def test_enter(self, mock_cdll, mock_getpid): CLONE_NEWNET = 0x40000000 FAKE_NETNS = 'fake-netns' FAKE_PID = random.randrange(100000) current_netns_fd = random.randrange(100000) target_netns_fd = random.randrange(100000) mock_getpid.return_value = FAKE_PID mock_cdll_obj = mock.MagicMock() mock_cdll.return_value = mock_cdll_obj expected_current_netns = '/proc/{pid}/ns/net'.format(pid=FAKE_PID) expected_target_netns = '/var/run/netns/{netns}'.format( netns=FAKE_NETNS) netns = network_namespace.NetworkNamespace(FAKE_NETNS) current_mock_open = self.useFixture( test_utils.OpenFixture(expected_current_netns)).mock_open current_mock_open.return_value = current_netns_fd target_mock_open = self.useFixture( test_utils.OpenFixture(expected_target_netns)).mock_open handle = target_mock_open() handle.fileno.return_value = target_netns_fd netns.__enter__() self.assertEqual(current_netns_fd, netns.current_netns_fd) netns.set_netns.assert_called_once_with(target_netns_fd, CLONE_NEWNET)
def test_error_handler(self, mock_cdll, mock_get_errno): FAKE_NETNS = 'fake-netns' netns = network_namespace.NetworkNamespace(FAKE_NETNS) # Test result 0 netns._error_handler(0, None, None) mock_get_errno.assert_not_called() # Test result -1 mock_get_errno.reset_mock() self.assertRaises(OSError, netns._error_handler, -1, None, None) mock_get_errno.assert_called_once_with()
def test_init(self, mock_cdll, mock_getpid): FAKE_NETNS = 'fake-netns' FAKE_PID = random.randrange(100000) mock_cdll_obj = mock.MagicMock() mock_cdll.return_value = mock_cdll_obj mock_getpid.return_value = FAKE_PID expected_current_netns = '/proc/{pid}/ns/net'.format(pid=FAKE_PID) expected_target_netns = '/var/run/netns/{netns}'.format( netns=FAKE_NETNS) netns = network_namespace.NetworkNamespace(FAKE_NETNS) self.assertEqual(expected_current_netns, netns.current_netns) self.assertEqual(expected_target_netns, netns.target_netns) self.assertEqual(mock_cdll_obj.setns, netns.set_netns) self.assertEqual(netns.set_netns.errcheck, netns._error_handler)
def garp(interface, ip_address, net_ns=None): """Sends a gratuitous ARP for ip_address on the interface. :param interface: The interface name to send the GARP on. :param ip_address: The IP address to advertise in the GARP. :param net_ns: The network namespace to send the GARP from. :returns: None """ ARP_ETHERTYPE = 0x0806 BROADCAST_MAC = b'\xff\xff\xff\xff\xff\xff' # Get a socket, optionally inside a network namespace garp_socket = None if net_ns: with network_namespace.NetworkNamespace(net_ns): garp_socket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW) else: garp_socket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW) # Bind the socket with the ARP ethertype protocol garp_socket.bind((interface, ARP_ETHERTYPE)) # Get the MAC address of the interface source_mac = garp_socket.getsockname()[4] garp_msg = [ pack('!h', 1), # Hardware type ethernet pack('!h', 0x0800), # Protocol type IPv4 pack('!B', 6), # Hardware size pack('!B', 4), # Protocol size pack('!h', 1), # Opcode request source_mac, # Sender MAC address socket.inet_aton(ip_address), # Sender IP address BROADCAST_MAC, # Target MAC address socket.inet_aton(ip_address) ] # Target IP address garp_ethernet = [ BROADCAST_MAC, # Ethernet destination source_mac, # Ethernet source pack('!h', ARP_ETHERTYPE), # Ethernet type b''.join(garp_msg) ] # The GARP message garp_socket.send(b''.join(garp_ethernet)) garp_socket.close()
def test_exit(self, mock_cdll, mock_getpid): CLONE_NEWNET = 0x40000000 FAKE_NETNS = 'fake-netns' FAKE_PID = random.randrange(100000) current_netns_fileno = random.randrange(100000) mock_getpid.return_value = FAKE_PID mock_cdll_obj = mock.MagicMock() mock_cdll.return_value = mock_cdll_obj mock_current_netns_fd = mock.MagicMock() mock_current_netns_fd.fileno.return_value = current_netns_fileno netns = network_namespace.NetworkNamespace(FAKE_NETNS) netns.current_netns_fd = mock_current_netns_fd netns.__exit__() netns.set_netns.assert_called_once_with(current_netns_fileno, CLONE_NEWNET) mock_current_netns_fd.close.assert_called_once_with()
def neighbor_advertisement(interface, ip_address, net_ns=None): """Sends a unsolicited neighbor advertisement for an ip on the interface. :param interface: The interface name to send the GARP on. :param ip_address: The IP address to advertise in the GARP. :param net_ns: The network namespace to send the GARP from. :returns: None """ ALL_NODES_ADDR = 'ff02::1' SIOCGIFHWADDR = 0x8927 # Get a socket, optionally inside a network namespace na_socket = None if net_ns: with network_namespace.NetworkNamespace(net_ns): na_socket = socket.socket( socket.AF_INET6, socket.SOCK_RAW, socket.getprotobyname(constants.IPV6_ICMP)) else: na_socket = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.getprotobyname(constants.IPV6_ICMP)) # Per RFC 4861 section 4.4, the hop limit should be 255 na_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 255) # Bind the socket with the source address na_socket.bind((ip_address, 0)) # Get the byte representation of the MAC address of the interface # Note: You can't use getsockname() to get the MAC on this type of socket source_mac = fcntl.ioctl(na_socket.fileno(), SIOCGIFHWADDR, pack('256s', bytes(interface, 'utf-8')))[18:24] # Get the byte representation of the source IP address source_ip_bytes = socket.inet_pton(socket.AF_INET6, ip_address) icmpv6_na_msg_prefix = [ pack('!B', 136), # ICMP Type Neighbor Advertisement pack('!B', 0) ] # ICMP Code icmpv6_na_msg_postfix = [ pack('!I', 0xa0000000), # Flags (Router, Override) source_ip_bytes, # Target address pack('!B', 2), # ICMPv6 option type target link-layer address pack('!B', 1), # ICMPv6 option length source_mac ] # ICMPv6 option link-layer address # Calculate the ICMPv6 checksum icmpv6_pseudo_header = [ source_ip_bytes, # Source IP address socket.inet_pton(socket.AF_INET6, ALL_NODES_ADDR), # Destination IP pack('!I', 58), # IPv6 next header (ICMPv6) pack('!h', 32) ] # IPv6 payload length icmpv6_tmp_chksum = pack('!H', 0) # Checksum are zeros for calculation tmp_chksum_msg = b''.join(icmpv6_pseudo_header + icmpv6_na_msg_prefix + [icmpv6_tmp_chksum] + icmpv6_pseudo_header) checksum = pack('!H', calculate_icmpv6_checksum(tmp_chksum_msg)) # Build the ICMPv6 unsolicitated neighbor advertisement icmpv6_msg = b''.join(icmpv6_na_msg_prefix + [checksum] + icmpv6_na_msg_postfix) na_socket.sendto(icmpv6_msg, (ALL_NODES_ADDR, 0, 0, 0)) na_socket.close()