def test_snat(): """ Title: Tests SNAT on ping messages. Scenario: When: a VM sends ICMP echo request with ping command to a different subnet, Then: the router performs SNAT on the message according to the rule chain set to the router, And: the receiver VM should receive the ICMP echo packet, with src address NATted, And: the ping command succeeds. """ sender = BM.get_iface_for_port('bridge-000-001', 2) receiver = BM.get_iface_for_port('bridge-000-002', 2) # Reset in-/out-bound filters. unset_filters('router-000-001') feed_receiver_mac(receiver) # No SNAT configured. Should not receive SNATed messages. f2 = async_assert_that(receiver, should_NOT_receive('src host 172.16.1.100 and icmp', within_sec(5))) f1 = sender.ping4(receiver) wait_on_futures([f1, f2]) # Set SNAT rule chains to the router set_filters('router-000-001', 'pre_filter_002', 'post_filter_002') # The receiver should receive SNATed messages. f2 = async_assert_that(receiver, receives('src host 172.16.1.100 and icmp', within_sec(5))) f3 = async_assert_that(sender, receives('dst host 172.16.1.1 and icmp', within_sec(5))) f1 = sender.ping4(receiver) wait_on_futures([f1, f2, f3])
def test_dnat(): """ Title: Tests DNAT on ping messages. Scenario 1: When: a VM sends ICMP echo request with ping command to an unassigned IP address. Then: the router performs DNAT on the message according to the rule chain set to the router, And: the receiver VM should receive the ICMP echo packet, And: the ping command succeeds """ sender = BM.get_iface_for_port('bridge-000-001', 2) receiver = BM.get_iface_for_port('bridge-000-002', 2) # Reset in-/out-bound filters. unset_filters('router-000-001') feed_receiver_mac(receiver) f2 = async_assert_that(receiver, should_NOT_receive('dst host 172.16.2.1 and icmp', within_sec(5))) f1 = sender.ping_ipv4_addr('100.100.100.100') wait_on_futures([f1, f2]) # Set DNAT rule chains to the router set_filters('router-000-001', 'pre_filter_001', 'post_filter_001') f2 = async_assert_that(receiver, receives('dst host 172.16.2.1 and icmp', within_sec(5))) f3 = async_assert_that(sender, receives('src host 100.100.100.100 and icmp', within_sec(5))) f1 = sender.ping_ipv4_addr('100.100.100.100') wait_on_futures([f1, f2, f3])
def ping_to_vtep(bridge_name, port_index, dest_ip, interval=0.5, count=4, retries=1): try: time.sleep(2) sender = BM.get_iface_for_port(bridge_name, port_index) f1 = sender.ping_ipv4_addr(dest_ip, interval=interval, count=count) wait_on_futures([f1]) output_stream, exec_id = f1.result() exit_status = sender.compute_host.check_exit_status(exec_id, output_stream, timeout=60) assert_that(exit_status, equal_to(0), "Ping to from {0}.{1} to {2} failed.".format(bridge_name, port_index, dest_ip)) except Exception: if retries == 0: assert_that(-1, equal_to(0), "Ping to from {0}.{1} to {2} failed.".format(bridge_name, port_index, dest_ip)) ping_to_vtep(bridge_name, port_index, dest_ip, count, interval, retries - 1)
def ping(src, dst, expected_failure=False, retries=3): try: # src and dst could be the vm object # or the port name where the vm is bound LOG.info("VPNaaS: pinging from %s to %s" % (src, dst)) src_vm = src if not isinstance(src, str) \ else BM.get_interface_on_vport(src) dst_vm = dst if not isinstance(dst, str) \ else BM.get_interface_on_vport(dst) f1 = src_vm.ping_ipv4_addr(dst_vm.get_ip(update=True), interval=1, count=5) wait_on_futures([f1]) output_stream, exec_id = f1.result() exit_status = src_vm.compute_host.check_exit_status(exec_id, output_stream, timeout=10) assert_that(exit_status, equal_to(0), "Ping did not return any data") except AssertionError: if retries == 0: if expected_failure: return raise AssertionError("Ping failed after max retries. Giving up.") LOG.debug("VPNaaS: failed ping from %s to %s... (%d retries left)" % (src, dst, retries)) ping(src, dst, expected_failure, retries=retries - 1)
def test_fragmented_packets(): """ Title: L3 connectivity over bridge and router (MN-L3-ICMP-2) with large packets that will be fragmented Scenario 1: When: a VM sends a ICMP echo request with size >MTU uwing ping command to a different subnet Then: the receiver VM should receive the ICMP echo packet. And: the ping command succeeds """ sender = BM.get_iface_for_port('bridge-000-001', 2) receiver = BM.get_iface_for_port('bridge-000-002', 2) # The receiver VM needs to send some frames so the MN Router learns # the VM's mac address. Otherwise this test would fail with binding2 # because the MidoNet Router forwards the ICMP with the previous mac # found in bindings1 in ethernet headers. # Issue: https://midobugs.atlassian.net/browse/MN-79 receiver.ping4(sender, 0.5, 3, True, 2000, do_arp=True) f1 = async_assert_that(receiver, receives('dst host 172.16.2.1 and icmp', within_sec(5))) f2 = sender.ping4(receiver, 0.5, 3, False, 2000) wait_on_futures([f1, f2])
def _ping_from_mn(midoVmIface, exHostIface, count=3, do_arp=False): f1 = async_assert_that(exHostIface, receives('dst host 172.16.0.224 and icmp', within_sec(20))) f2 = midoVmIface.ping4(exHostIface, count=count, do_arp=do_arp) wait_on_futures([f1, f2])
def _ping_to_mn(midoVmIface, exHostIface, count=3, do_arp=False): exHostIface.clear_arp() f1 = async_assert_that(midoVmIface, receives('dst host 172.16.0.1 and icmp', within_sec(5))) f2 = exHostIface.ping4(midoVmIface, count=count, do_arp=do_arp) wait_on_futures([f1, f2])
def check_return_flow(src_vm, dst_vm, snat_ip, snat_port, dst_port, src_port): # And expect: Internal VM receives return traffic recv_filter = 'udp and port %d and ip dst %s' % (dst_port, dst_vm.get_ip()) f = async_assert_that(dst_vm, receives(recv_filter, within_sec(10))) # When: sending return flows src_vm.execute('hping3 -c 1 -q -2 -s %s -p %s %s' % (src_port, snat_port, snat_ip)) wait_on_futures([f])
def check_forward_flow(src_port_no): dst_mac = mac_for(downlink_port()) fs = expect_forward() f = downlink_iface().send_udp(dst_mac, '21.42.84.168', 41, src_port=src_port_no, dst_port=1080) wait_on_futures([f, fs])
def test_icmp_autobind_vm_in_binding(): vm1 = BM2.get_interface_on_vport('port1') vm2 = BM2.get_interface_on_vport('port2') # Test ping works f1 = async_assert_that(vm2, receives('dst host %s and icmp' % vm2.get_ip(), within_sec(10))) f2 = vm1.ping4(vm2) wait_on_futures([f1, f2])
def feed_receiver_mac(receiver): """Feeds the receiver's mac address to the MidoNet router.""" try: router_port = VTM.get_router('router-000-001').get_port(2) router_ip = router_port.get_mn_resource().get_port_address() receiver_ip = receiver.get_ip() f1 = async_assert_that(receiver, receives('dst host %s' % receiver_ip, within_sec(10))) receiver.send_arp_request(router_ip) wait_on_futures([f1]) except Exception: LOG.warn('Oops, sending ARP from the receiver VM failed.')
def send_udp(sender, receiver, hw_dst, dst_p, src_p, mirror=None): sender.get_mac_addr() sender.get_ip() ip_dst = receiver.get_ip() udp_filter = "dst host %s and dst port %d" % (ip_dst, dst_p) futures = [] futures.append(async_assert_that(receiver, receives(udp_filter, within_sec(15)))) if mirror is not None: futures.append(async_assert_that(mirror, receives(udp_filter, within_sec(15)))) sender.send_udp(hw_dst, ip_dst, src_port=src_p, dst_port=dst_p) wait_on_futures(futures)
def test_mac_learning(): """ Title: Bridge mac learning Scenario 1: When: the destination ethernet address has never been seen before. Then: the bridge should flood the ethernet unicast Scenario 2: When: the destination ethernet address has been seen before. Then: the bridge should not flood the ethernet frame, instaed it should forward to only the port that is connected to the interface with the mac address. """ sender = BM.get_iface_for_port('bridge-000-001', 1) iface_with_the_hw_addr = BM.get_iface_for_port('bridge-000-001', 2) iface_x = BM.get_iface_for_port('bridge-000-001', 3) hw_addr = iface_with_the_hw_addr.get_mac_addr() match_on_the_hw_addr = 'ether dst ' + hw_addr ethernet_unicast_to_the_hw_addr = '%s-7e:1f:ff:ff:ff:ff-aa:bb' % (hw_addr) # Scenario 1: # Both interfaces should get the frname as the bridge should flood it. f1 = async_assert_that(iface_with_the_hw_addr, receives(match_on_the_hw_addr, within_sec(5))) f2 = async_assert_that(iface_x, receives(match_on_the_hw_addr, within_sec(5))) time.sleep(1) sender.send_ether(ethernet_unicast_to_the_hw_addr, count=3) wait_on_futures([f1, f2]) # Scenario 2: # Get the bridge to learn the mac address iface_with_the_hw_addr.ping4(sender, sync=True) time.sleep(1) # only iface_with_the_hw_addr should receives the ehternet unicast f1 = async_assert_that(iface_with_the_hw_addr, receives(match_on_the_hw_addr, within_sec(5))) f2 = async_assert_that(iface_x, should_NOT_receive(match_on_the_hw_addr, within_sec(5))) sender.send_ether(ethernet_unicast_to_the_hw_addr, count=1) wait_on_futures([f1, f2])
def _send_icmp(sender, receiver, payload, target_ipv4, expect, expect_icmp_frag_needed=False): f1 = async_assert_that(receiver, expect('dst host %s and icmp' % target_ipv4, within_sec(5))) f2 = _async_assert_receives_icmp_frag_needed(sender, expect_icmp_frag_needed) time.sleep(1) f3 = sender.ping4(receiver, 0.5, 3, False, payload) wait_on_futures([f1, f2, f3])
def test_src_mac_masking(): """ Title: Test source MAC masking in chain rules Scenario 1: When: There's a rule dropping any traffic with an even source MAC Then: Traffic from if2 to if1 is blocked because if2's MAC ends with 2 And: Traffic from if1 to if2 goes through because if1's MAC ends with 1 FIXME: moving to the new bindings mechanisms should allow removing this restriction. Only running this with the one-host binding, because: 1. The multi-host binding breaks the assumptions that if1 will have an odd MAC address and if2 an even one. 2. This is basically just a sanity test to make sure dl_src_mask is wired up. Unit tests and test_dst_mac_masking provide enough coverage of the other aspects. 3. These tests are slow enough as it is. """ bridge = VTM.get_bridge('bridge-000-001') if1 = BM.get_iface_for_port('bridge-000-001', 1) if2 = BM.get_iface_for_port('bridge-000-001', 2) if1_hw_addr = if1.interface['hw_addr'] if2_hw_addr = if2.interface['hw_addr'] if1_ip_addr = if1.get_ip() if2_ip_addr = if2.get_ip() if1_rcv_filter = 'udp and ether dst %s' % if1_hw_addr if2_rcv_filter = 'udp and ether dst %s' % if2_hw_addr bridge.set_inbound_filter(VTM.get_chain('drop_even_src_mac')) # If2 has an even MAC (ends with 2), so traffic from if2 to if1 # should be dropped. f1 = async_assert_that(if1, should_NOT_receive(if1_rcv_filter, within_sec(5))) time.sleep(1) f2 = if2.send_udp(if1_hw_addr, if1_ip_addr, 41) wait_on_futures([f1, f2]) # If1 has an odd MAC (ends with 1), so traffic from if1 to if2 # should go through. f1 = async_assert_that(if2, receives(if2_rcv_filter, within_sec(5))) time.sleep(1) f2 = if1.send_udp(if2_hw_addr, if2_ip_addr, 41) wait_on_futures([f1, f2])
def send_udp(sender, receiver, hw_dst, dst_p, src_p, mirror=None): sender.get_mac_addr() sender.get_ip() ip_dst = receiver.get_ip() udp_filter = "dst host %s and dst port %d" % (ip_dst, dst_p) futures = [] futures.append( async_assert_that(receiver, receives(udp_filter, within_sec(15)))) if mirror is not None: futures.append( async_assert_that(mirror, receives(udp_filter, within_sec(15)))) sender.send_udp(hw_dst, ip_dst, src_port=src_p, dst_port=dst_p) wait_on_futures(futures)
def _send_udp(sender, receiver, target_hw, target_ipv4, parms, payload, expect, expect_icmp_frag_needed=False): f1 = async_assert_that(receiver, expect('dst host %s and udp' % target_ipv4, within_sec(5))) f2 = _async_assert_receives_icmp_frag_needed(sender, expect_icmp_frag_needed) time.sleep(1) f3 = sender.send_packet(target_hw, target_ipv4, 'udp', parms, payload, 1, 3, False) wait_on_futures([f1, f2, f3])
def ping_to_inet(count=5, interval=1, port=2, retries=3): try: sender = BM.get_iface_for_port('bridge-000-001', port) f1 = sender.ping_ipv4_addr('1.1.1.1', interval=interval, count=count) wait_on_futures([f1]) output_stream, exec_id = f1.result() exit_status = sender.compute_host.check_exit_status(exec_id, output_stream, timeout=20) assert_that(exit_status, equal_to(0), "Ping did not return any data") except Exception: if retries == 0: raise RuntimeError("Ping did not return any data and returned -1") LOG.debug("BGP: failed ping to inet... (%d retries left)" % retries) ping_to_inet(count, interval, port, retries - 1)
def _send_icmp(sender, receiver, payload, target_ipv4, expect, expect_icmp_frag_needed=False): f1 = async_assert_that( receiver, expect('dst host %s and icmp' % target_ipv4, within_sec(5))) f2 = _async_assert_receives_icmp_frag_needed(sender, expect_icmp_frag_needed) time.sleep(1) f3 = sender.ping4(receiver, 0.5, 3, False, payload) wait_on_futures([f1, f2, f3])
def check_forward_flow(src_vm, dst_vm, fip, src_port, dst_port): # Expect: Both vms (with fip) receive the packet recv_filter = 'udp and port %d and ip dst %s' % (dst_port, dst_vm.get_ip()) f = async_assert_that(dst_vm, receives(recv_filter, within_sec(10))) # When: Sending udp packet # src_vm (internal) -> dst_vm (fip) src_vm.execute('hping3 -c 1 -q -2 -s %s -p %s %s' % (src_port, dst_port, fip)) wait_on_futures([f]) # tcpdump format: # date net_proto src_ip.src_port > dst_ip.dst_port: transp_proto [...] output = dst_vm.get_last_tcpdump_output() snat_ip = output.split(' ')[2].rsplit('.', 1)[0] snat_port = output.split(' ')[2].rsplit('.', 1)[1] return {'ip': snat_ip, 'port': snat_port}
def test_two_isolated_bridges(): """ Title: Two isolated bridges All traffic between two VMs in different and unconnected bridges should be independent, so receiver shouldn't get any packets """ sender = BM.get_iface_for_port('bridge-000-001', 1) receiver = BM.get_iface_for_port('bridge-000-002', 1) f2 = async_assert_that(receiver, should_NOT_receive('icmp', within_sec(5))) f1 = sender.ping4(receiver, 0.5, 3, False, 100) wait_on_futures([f1, f2])
def check_forward_flow(src_vm, dst_vm, fip, src_port, dst_port): # Expect: VM with fip to receive the packet recv_filter = 'udp and port %d and ip dst %s' % (dst_port, dst_vm.get_ip()) f = async_assert_that(dst_vm, receives(recv_filter, within_sec(10))) # When: Sending udp packet # src_vm (internal) -> dst_vm (fip) src_vm.execute('hping3 -c 1 -q -2 -s %s -p %s %s' % (src_port, dst_port, fip)) wait_on_futures([f]) # tcpdump format: # date net_proto src_ip.src_port > dst_ip.dst_port: transp_proto [...] output = dst_vm.get_last_tcpdump_output() snat_ip = output.split(' ')[2].rsplit('.', 1)[0] snat_port = output.split(' ')[2].rsplit('.', 1)[1] return {'ip': snat_ip, 'port': snat_port}
def test_icmp(): """ Title: ICMP reachability over bridge Scenario 1: When: a VM sends ICMP echo request with ping command Then: the receiver VM should receive the ICMP echo packet. And: the ping command succeeds """ sender = BM.get_iface_for_port('bridge-000-001', 1) receiver = BM.get_iface_for_port('bridge-000-001', 3) f1 = async_assert_that(receiver, receives('dst host %s and icmp' % receiver.get_ip(), within_sec(5))) f2 = sender.ping4(receiver) wait_on_futures([f1, f2])
def test_two_isolated_bridges(): """ Title: Two isolated bridges All traffic between two VMs in different and unconnected bridges should be independent, so receiver shouldn't get any packets """ sender = BM.get_iface_for_port('bridge-000-001', 1) receiver = BM.get_iface_for_port('bridge-000-002', 1) f2 = async_assert_that(receiver, should_NOT_receive('icmp', within_sec(5))) f1 = sender.ping4(receiver, 0.5, 3, False, 100) wait_on_futures([f1, f2])
def test_icmp(): """ Title: ICMP reachability over bridge Scenario 1: When: a VM sends ICMP echo request with ping command Then: the receiver VM should receive the ICMP echo packet. And: the ping command succeeds """ sender = BM.get_iface_for_port('bridge-000-001', 1) receiver = BM.get_iface_for_port('bridge-000-001', 3) f1 = async_assert_that( receiver, receives('dst host %s and icmp' % receiver.get_ip(), within_sec(5))) f2 = sender.ping4(receiver) wait_on_futures([f1, f2])
def test_fragmented_packets(): """ Title: Fragmented IP packets through a bridge Scenario 1: When: a VM sends large PING packets (> MTU) Then: the receiver should receive the ICMP echo packet And: the ping command succeeds """ sender = BM.get_iface_for_port('bridge-000-001', 1) receiver = BM.get_iface_for_port('bridge-000-001', 3) f2 = async_assert_that( receiver, receives('dst host 172.16.1.3 and icmp', within_sec(5))) f1 = sender.ping4(receiver, 0.5, 3, False, 2000) wait_on_futures([f1, f2])
def ping_to_inet(count=5, interval=1, port=2, retries=3): try: sender = BM.get_iface_for_port('bridge-000-001', port) f1 = sender.ping_ipv4_addr('1.1.1.1', interval=interval, count=count) wait_on_futures([f1]) output_stream, exec_id = f1.result() exit_status = sender.compute_host.check_exit_status(exec_id, output_stream, timeout=20) assert_that(exit_status, equal_to(0), "Ping did not return any data") except Exception: if retries == 0: raise RuntimeError("Ping did not return any data and returned -1") LOG.debug("BGP: failed ping to inet... (%d retries left)" % retries) ping_to_inet(count, interval, port, retries - 1)
def test_snat_for_udp(): """ Title: Tests SNAT on UDP packets. Scenario: When: a VM sends UDP packets to an unassigned IP address. Then: the router performs SNAT on the message according to the rule chain set to the router, And: the UDP packets reach the receiver VM, with src address NATted, And: because the UDP port is not open, the receiver VM returns ICMP error responses. """ sender = BM.get_iface_for_port('bridge-000-001', 2) receiver = BM.get_iface_for_port('bridge-000-002', 2) # Reset in-/out-bound filters. unset_filters('router-000-001') feed_receiver_mac(receiver) # Target hardware is a router's incoming port. router_port = VTM.get_router('router-000-001').get_port(1) router_mac = router_port.get_mn_resource().get_port_mac() # No SNAT configured. Should not receive SNATed messages. f2 = async_assert_that(receiver, should_NOT_receive('src host 172.16.1.100 and udp', within_sec(5))) f1 = sender.send_udp(router_mac, '172.16.2.1', 29, src_port=9, dst_port=65000) wait_on_futures([f1, f2]) # Set SNAT rule chains to the router set_filters('router-000-001', 'pre_filter_002', 'post_filter_002') # The receiver should receive SNATed messages. f2 = async_assert_that(receiver, receives('src host 172.16.1.100 and udp', within_sec(5))) # Sender should receive ICMP unreachable as the receiver port is not open. f3 = async_assert_that(sender, receives_icmp_unreachable_for_udp( '172.16.1.1', '172.16.2.1', udp_src_port=9, udp_dst_port=65000, timeout=within_sec(5))) f1 = sender.send_udp(router_mac, '172.16.2.1', 29, src_port=9, dst_port=65000) wait_on_futures([f1, f2, f3])
def test_icmp_topology_out_test_two_computes(): # Query resources created on the managers vm1 = PTM2.get_resource('vm1_host1') vm2 = PTM2.get_resource('vm2_host2') port1 = VTM2.get_resource('port1') port2 = VTM2.get_resource('port2') # Bind virtual and physical topology vm1.compute_host.bind_port(vm1, port1['port']['id']) vm2.compute_host.bind_port(vm2, port2['port']['id']) # Test ping works f1 = async_assert_that(vm2, receives('dst host %s and icmp' % vm2.get_ip(), within_sec(10))) f2 = vm1.ping4(vm2) wait_on_futures([f1, f2])
def test_dst_mac_masking(): """ Title: Test destination MAC masking in chain rules Scenario 1: When: There's a rule dropping any traffic with the multicast bit on Then: Multicast traffic is blocked and unicast traffic goes through Scenario 2: When: There's a rule dropping any traffic with the multicast bit off Then: Multicast traffic goes through and unicast traffic is blocked """ bridge = VTM.get_bridge('bridge-000-001') if1 = BM.get_iface_for_port('bridge-000-001', 1) if2 = BM.get_iface_for_port('bridge-000-001', 2) if1_hw_addr = if1.get_mac_addr() # interface['hw_addr'] if2_hw_addr = if2.get_mac_addr() # interface['hw_addr'] if2_ip_addr = if2.get_ip() rcv_filter = 'udp and ether src %s' % if1_hw_addr bridge.set_inbound_filter(VTM.get_chain('drop_multicast')) # Send a frame to an arbitrary multicast address. Bridge doesn't # recognize it and will try to flood it to the other port, but the # masked MAC rule should drop it since it has the multicast bit set. f1 = async_assert_that(if2, should_NOT_receive(rcv_filter, within_sec(10))) f2 = if1.send_udp("01:23:45:67:89:ab", if2_ip_addr) wait_on_futures([f1, f2]) # If2's actual MAC address should work, since it doesn't have the bit set. f1 = async_assert_that(if2, receives(rcv_filter, within_sec(10))) f2 = if1.send_udp(if2_hw_addr, if2_ip_addr) wait_on_futures([f1, f2]) # Change to the chain that allows only multicast addresses. bridge.set_inbound_filter(VTM.get_chain('allow_only_multicast')) # Send another frame to the multicast address. Bridge doesn't # recognize it and will try to flood it to the other port. This # time the rule should allow it through. f1 = async_assert_that(if2, receives(rcv_filter, within_sec(10))) f2 = if1.send_udp("01:23:45:67:89:ab", if2_ip_addr) wait_on_futures([f1, f2]) # If2's actual MAC address should be blocked, since it doesn't # have the multicast bit set. f1 = async_assert_that(if2, should_NOT_receive(rcv_filter, within_sec(10))) f2 = if1.send_udp(if2_hw_addr, if2_ip_addr) wait_on_futures([f1, f2])
def test_fragmented_packets(): """ Title: Fragmented IP packets through a bridge Scenario 1: When: a VM sends large PING packets (> MTU) Then: the receiver should receive the ICMP echo packet And: the ping command succeeds """ sender = BM.get_iface_for_port('bridge-000-001', 1) receiver = BM.get_iface_for_port('bridge-000-001', 3) f2 = async_assert_that(receiver, receives('dst host 172.16.1.3 and icmp', within_sec(5))) f1 = sender.ping4(receiver, 0.5, 3, False, 2000) wait_on_futures([f1, f2])
def test_filtering_by_network_address(): ''' Title: Tests packets filtering based on network address Scenario: When: A VM sends UDP packets to another host on the same bridge. Then: The UDP packets reaches the receiver. Then: Filtering rule chains based on network address (IP address) are set on the bridge port that the receiver host is connected to. And: The UDP packets from the same sender do NOT reach the receiver. ''' sender = BM.get_iface_for_port('bridge-000-001', 2) receiver = BM.get_iface_for_port('bridge-000-001', 3) # Reset in/out-bound filters. unset_bridge_port_filters('bridge-000-001', 3) port_num = get_random_port_num() # FIXME: do not use harcoded values! f1 = async_assert_that( receiver, receives('dst host 172.16.1.2 and udp', within_sec(5)), 'No filtering: receives UDP packets from sender.') f2 = sender.send_udp('aa:bb:cc:00:01:02', '172.16.1.2', 41, src_port=port_num, dst_port=port_num) wait_on_futures([f1, f2]) # Set a filtering rule based on network address. set_bridge_port_filters('bridge-000-001', 3, 'connection_tracking_nw_in', 'connection_tracking_nw_out') f1 = async_assert_that( receiver, should_NOT_receive('dst host 172.16.1.2 and udp', within_sec(5)), 'Packets are filtered based on IP address.') f2 = sender.send_udp('aa:bb:cc:00:01:02', '172.16.1.2', 41, src_port=port_num, dst_port=port_num) wait_on_futures([f1, f2])
def test_dst_mac_masking(): """ Title: Test destination MAC masking in chain rules Scenario 1: When: There's a rule dropping any traffic with the multicast bit on Then: Multicast traffic is blocked and unicast traffic goes through Scenario 2: When: There's a rule dropping any traffic with the multicast bit off Then: Multicast traffic goes through and unicast traffic is blocked """ bridge = VTM.get_bridge('bridge-000-001') if1 = BM.get_iface_for_port('bridge-000-001', 1) if2 = BM.get_iface_for_port('bridge-000-001', 2) if1_hw_addr = if1.get_mac_addr() # interface['hw_addr'] if2_hw_addr = if2.get_mac_addr() # interface['hw_addr'] if2_ip_addr = if2.get_ip() rcv_filter = 'udp and ether src %s' % if1_hw_addr bridge.set_inbound_filter(VTM.get_chain('drop_multicast')) # Send a frame to an arbitrary multicast address. Bridge doesn't # recognize it and will try to flood it to the other port, but the # masked MAC rule should drop it since it has the multicast bit set. f1 = async_assert_that(if2, should_NOT_receive(rcv_filter, within_sec(10))) f2 = if1.send_udp("01:23:45:67:89:ab", if2_ip_addr) wait_on_futures([f1, f2]) # If2's actual MAC address should work, since it doesn't have the bit set. f1 = async_assert_that(if2, receives(rcv_filter, within_sec(10))) f2 = if1.send_udp(if2_hw_addr, if2_ip_addr) wait_on_futures([f1, f2]) # Change to the chain that allows only multicast addresses. bridge.set_inbound_filter(VTM.get_chain('allow_only_multicast')) # Send another frame to the multicast address. Bridge doesn't # recognize it and will try to flood it to the other port. This # time the rule should allow it through. f1 = async_assert_that(if2, receives(rcv_filter, within_sec(10))) f2 = if1.send_udp("01:23:45:67:89:ab", if2_ip_addr) wait_on_futures([f1, f2]) # If2's actual MAC address should be blocked, since it doesn't # have the multicast bit set. f1 = async_assert_that(if2, should_NOT_receive(rcv_filter, within_sec(10))) f2 = if1.send_udp(if2_hw_addr, if2_ip_addr) wait_on_futures([f1, f2])
def test_tracing_egress_matching_over_nat(): """ Title: Tracing egress matching over nat Scenario 1: When: a VM sends ICMP echo request over nat wait for echo response Then: Trace data appears for the ingress and the egress host, but only as part of 2 flow traces (one for forward, one for return) """ unset_filters('router-000-001') tracerequest = VTM.get_tracerequest('ping-trace-request') try: set_filters('router-000-001', 'pre_filter_001', 'post_filter_001') tracerequest.set_enabled(True) time.sleep(5) flowtraces = get_flow_traces(tracerequest.get_id()) assert (len(flowtraces) == 0) sender = BM.get_iface_for_port('bridge-000-001', 2) receiver = BM.get_iface_for_port('bridge-000-002', 2) feed_receiver_mac(receiver) f2 = async_assert_that( receiver, receives('dst host 172.16.2.1 and icmp', within_sec(10))) f3 = async_assert_that( sender, receives('src host 100.100.100.100 and icmp', within_sec(10))) f1 = sender.ping_ipv4_addr('100.100.100.100') wait_on_futures([f1, f2, f3]) time.sleep(5) flowtraces = get_flow_traces(tracerequest.get_id()) assert (len(flowtraces) == 2) # ensure both packets were traced on both hosts assert (len(get_hosts(tracerequest.get_id(), flowtraces[0])) == 2) assert (len(get_hosts(tracerequest.get_id(), flowtraces[1])) == 2) finally: tracerequest.set_enabled(False) unset_filters('router-000-001')
def test_connection_tracking_by_network_addres(): ''' Title: Tests NW address based connection tracking. Scenario: When: A VM, supposedly inside a FW, sends UDP packets to another host, supposedly outside the FS, on the same bridge. And: The host outside the FW receives the UDP packets. Then: A connection-tracking-based peep hole is established. And: The outside host now can send UDP packets to the inside host. ''' outside = BM.get_iface_for_port('bridge-000-001', 2) inside = BM.get_iface_for_port('bridge-000-001', 3) # Set a filtering rule based on ip address. set_bridge_port_filters('bridge-000-001', 3, 'connection_tracking_nw_in', 'connection_tracking_nw_out') # Send forward packets to set up a connection-tracking based peep hole in # the filter. port_num = get_random_port_num() f1 = async_assert_that( outside, receives('dst host 172.16.1.1 and udp', within_sec(5)), 'Outside host receives forward packets from inside.') f2 = inside.send_udp('aa:bb:cc:00:01:01', '172.16.1.1', 41, src_port=port_num, dst_port=port_num) wait_on_futures([f1, f2]) # Verify the peep hole. f1 = async_assert_that( inside, receives('dst host 172.16.1.2 and udp', within_sec(5)), 'Outside host can send packets to inside ' 'via a peep hole.') f2 = outside.send_udp('aa:bb:cc:00:01:02', '172.16.1.2', 41, src_port=port_num, dst_port=port_num) wait_on_futures([f1, f2])
def test_connection_tracking_with_drop_by_dl(): ''' Title: Tests dl-based connection tracking. Scenario: When: A VM inside a FW sends UDP packets to a VM outside. And: The outside receives the UDP packets. Then: A connection-tracking-based peep hole is established. And: The outside now can send UDP packets to the inside. ''' outside = BM.get_iface_for_port('bridge-000-001', 2) inside = BM.get_iface_for_port('bridge-000-001', 3) # Set a filtering rule based on mac addresses set_bridge_port_filters('bridge-000-001', 3, 'connection_tracking_dl_in', 'connection_tracking_dl_out') # Send forward packets to set up a connection-tracking based peep hole in # the filter. port_num = get_random_port_num() f1 = async_assert_that( outside, receives('dst host 172.16.1.1 and udp', within_sec(5)), 'The outside host receives forward packets ' 'from the inside.') f2 = inside.send_udp('aa:bb:cc:00:01:01', '172.16.1.1', 41, src_port=port_num, dst_port=port_num) wait_on_futures([f1, f2]) # Verify the peep hole. f1 = async_assert_that( inside, receives('dst host 172.16.1.2 and udp', within_sec(5)), 'The outside host can now send packets to the inside' 'via a peep hole.') f2 = outside.send_udp('aa:bb:cc:00:01:02', '172.16.1.2', 41, src_port=port_num, dst_port=port_num) wait_on_futures([f1, f2])
def _send_udp(sender, receiver, target_hw, target_ipv4, parms, payload, expect, expect_icmp_frag_needed=False): f1 = async_assert_that( receiver, expect('dst host %s and udp' % target_ipv4, within_sec(5))) f2 = _async_assert_receives_icmp_frag_needed(sender, expect_icmp_frag_needed) time.sleep(1) f3 = sender.send_packet(target_hw, target_ipv4, 'udp', parms, payload, 1, 3, False) wait_on_futures([f1, f2, f3])
def test_filtering_by_dl(): ''' Title: Tests dl-based packet filtering. Scenario: When: A VM sends UDP packets to another host on the same bridge. Then: The UDP packets reach the receiver without filtering rule chains. Then: A filtering rule chain based on mac address is set on the bridge. And: UDP packets from the same host do NOT reach the same destination host. ''' outside = BM.get_iface_for_port('bridge-000-001', 2) inside = BM.get_iface_for_port('bridge-000-001', 3) # Reset an in-bound filter. unset_bridge_port_filters('bridge-000-001', 3) port_num = get_random_port_num() f1 = async_assert_that( inside, receives('dst host 172.16.1.2 and udp', within_sec(5)), 'No filtering: inside receives UDP packets from outside.') f2 = outside.send_udp('aa:bb:cc:00:01:02', '172.16.1.2', 41, src_port=port_num, dst_port=port_num) wait_on_futures([f1, f2]) # Set a filtering rule based on mac addresses set_bridge_port_filters('bridge-000-001', 3, 'connection_tracking_dl_in', 'connection_tracking_dl_out') f1 = async_assert_that( inside, should_NOT_receive('dst host 172.16.1.2 and udp', within_sec(5)), 'Packets are filtered based on mac address.') f2 = outside.send_udp('aa:bb:cc:00:01:02', '172.16.1.2', 41, src_port=port_num, dst_port=port_num) wait_on_futures([f1, f2])
def test_flow_invalidation_on_mac_update(): """ Title: Flow invalidation, learning MACs The bridge learns the MACs from the traffic flowing by its ports. When the bridge learns a MAC that has 'moved' to another port, it should send traffic only to that port. """ sender = BM.get_iface_for_port('bridge-000-001', 1) receiver = BM.get_iface_for_port('bridge-000-001', 2) intruder = BM.get_iface_for_port('bridge-000-001', 3) receiver_MAC = receiver.get_mac_addr() frame = '%s-%s-aa:bb' % (receiver_MAC, receiver_MAC) capture = 'icmp and src host %s' % (sender.get_ip()) # Populate ARP table sender.execute('arp -s %s %s' % (receiver.get_ip(), receiver_MAC)) receiver.execute('arp -s %s %s' % (sender.get_ip(), sender.get_mac_addr())) # Trigger receiver MAC learning receiver.send_ether(frame) # First: packets go from sender to receiver f1 = async_assert_that(receiver, receives(capture, within_sec(5))) f2 = async_assert_that(intruder, should_NOT_receive(capture, within_sec(5))) f3 = sender.ping4(receiver) wait_on_futures([f1, f2, f3]) # Second: intruder claims to be receiver intruder.send_ether(frame) # Third: packets go from sender to intruder f1 = async_assert_that(receiver, should_NOT_receive(capture, within_sec(5))) f2 = async_assert_that(intruder, receives(capture, within_sec(5))) f3 = sender.ping4(receiver) wait_on_futures([f1, f2, f3])
def check_return_flow(port, iface, dst_port_no, dropped=False, retries=0): dst_mac = mac_for(port) if dropped: fs = expect_return_dropped(dst_port_no) else: fs = expect_return(dst_port_no) f = iface.send_udp(dst_mac, '192.168.0.1', 41, src_port=80, dst_port=dst_port_no, src_ipv4='172.16.42.1') try: wait_on_futures([f, fs]) except Exception: if retries > 0: time.sleep(5) check_return_flow(port, iface, dst_port_no, dropped, retries - 1) else: raise
def test_filter_ipv6(): """ Title: Filter IPv6 packets out on Bridge Scenario 1: When: there is no filter settings Then: IPv6 packets go through the bridge Scenario 2: When: the bridge has a chain in which there is a drop rule for IPv6 Then: IPv6 packets should not go through the bridge Scenario 3: When: the chain is removed from the bridge Then: IPv6 packets should go through again. """ iface1 = BM.get_iface_for_port('bridge-000-001', 1) iface2 = BM.get_iface_for_port('bridge-000-001', 2) iface1_hw_addr = iface1.interface['hw_addr'] iface2_hw_addr = iface2.interface['hw_addr'] ipv6_proto = "86:dd" ipv6_icmp = ("60:00:00:00:00:20:3a:ff:fe:80:00:00:00:00:00:00:1a:03:73:ff:" "fe:29:a9:b1:ff:02:00:00:00:00:00:00:00:00:00:01:ff:29:a9:b2:" "87:00:32:26:00:00:00:00:fe:80:00:00:00:00:00:00:1a:03:73:ff:" "fe:29:a9:b2:01:01:18:03:73:29:a9:b1") packet = '%s-%s-%s-%s' % (iface2_hw_addr, iface1_hw_addr, ipv6_proto, ipv6_icmp) rcv_filter = 'ether dst %s' % iface2_hw_addr # Sceneario 1: f1 = async_assert_that(iface2, receives(rcv_filter, within_sec(10))) # async_assert_that expects only 1 packet. Send only one, because the next # tcpdump might capture it (and fail the test) in case it takes some time # to arrive. # FIXME: make the tcpdump listener configurable f2 = iface1.send_ether(packet, count=1) wait_on_futures([f1, f2]) # Scenario 2: # setting chain and make sure it's dropped chain = VTM.get_chain('drop_ipv6') VTM.get_bridge('bridge-000-001').set_inbound_filter(chain) f1 = async_assert_that(iface2, should_NOT_receive( rcv_filter, within_sec(10))) f2 = iface1.send_ether(packet, count=1) wait_on_futures([f1, f2]) # Remove the filter and verify that packets go through again. VTM.get_bridge('bridge-000-001').set_inbound_filter(None) time.sleep(1) f1 = async_assert_that(iface2, receives(rcv_filter, within_sec(10))) f2 = iface1.send_ether(packet, count=1) wait_on_futures([f1, f2])
def test_ping_delete_port(): """ Title: L3 connectivity over bridge and router, then deletes a port and verifies that there is no connectivity. Implemented to cover the old DeletePortTest. Scenario 1: When: a VM sends ICMP echo request with ping command to a different subnet Then: the receiver VM should receive the ICMP echo packet. And: the ping command succeeds When: the destination port on the router is deleted Then: the receiver VM should NOT receive the ICMP echo packet """ sender = BM.get_iface_for_port('bridge-000-001', 2) receiver = BM.get_iface_for_port('bridge-000-002', 2) # The receiver VM needs to send some frames so the MN Router learns # the VM's mac address. Otherwise this test would fail with binding2 # because the MidoNet Router forwards the ICMP with the previous mac # found in bindings1 in ethernet headers. # Issue: https://midobugs.atlassian.net/browse/MN-79 receiver.ping4(sender) f1 = async_assert_that(receiver, receives('dst host 172.16.2.1 and icmp', within_sec(5))) f2 = sender.ping4(receiver) wait_on_futures([f1, f2]) port = VTM.get_device_port('router-000-001', 2) port.destroy() f1 = async_assert_that(receiver, should_NOT_receive('dst host 172.16.2.1 and icmp', within_sec(5))) f2 = sender.ping4(receiver) wait_on_futures([f1, f2])
def test_filter_ipv6(): """ Title: Filter IPv6 packets out on Bridge Scenario 1: When: there is no filter settings Then: IPv6 packets go through the bridge Scenario 2: When: the bridge has a chain in which there is a drop rule for IPv6 Then: IPv6 packets should not go through the bridge Scenario 3: When: the chain is removed from the bridge Then: IPv6 packets should go through again. """ iface1 = BM.get_iface_for_port('bridge-000-001', 1) iface2 = BM.get_iface_for_port('bridge-000-001', 2) iface1_hw_addr = iface1.interface['hw_addr'] iface2_hw_addr = iface2.interface['hw_addr'] ipv6_proto = "86:dd" ipv6_icmp = ("60:00:00:00:00:20:3a:ff:fe:80:00:00:00:00:00:00:1a:03:73:ff:" "fe:29:a9:b1:ff:02:00:00:00:00:00:00:00:00:00:01:ff:29:a9:b2:" "87:00:32:26:00:00:00:00:fe:80:00:00:00:00:00:00:1a:03:73:ff:" "fe:29:a9:b2:01:01:18:03:73:29:a9:b1") packet = '%s-%s-%s-%s' % (iface2_hw_addr, iface1_hw_addr, ipv6_proto, ipv6_icmp) rcv_filter = 'ether dst %s' % iface2_hw_addr # Sceneario 1: f1 = async_assert_that(iface2, receives(rcv_filter, within_sec(10))) # async_assert_that expects only 1 packet. Send only one, because the next # tcpdump might capture it (and fail the test) in case it takes some time # to arrive. # FIXME: make the tcpdump listener configurable f2 = iface1.send_ether(packet, count=1) wait_on_futures([f1, f2]) # Scenario 2: # setting chain and make sure it's dropped chain = VTM.get_chain('drop_ipv6') VTM.get_bridge('bridge-000-001').set_inbound_filter(chain) f1 = async_assert_that(iface2, should_NOT_receive(rcv_filter, within_sec(10))) f2 = iface1.send_ether(packet, count=1) wait_on_futures([f1, f2]) # Remove the filter and verify that packets go through again. VTM.get_bridge('bridge-000-001').set_inbound_filter(None) time.sleep(1) f1 = async_assert_that(iface2, receives(rcv_filter, within_sec(10))) f2 = iface1.send_ether(packet, count=1) wait_on_futures([f1, f2])
def test_flow_invalidation_on_mac_update(): """ Title: Flow invalidation, learning MACs The bridge learns the MACs from the traffic flowing by its ports. When the bridge learns a MAC that has 'moved' to another port, it should send traffic only to that port. """ sender = BM.get_iface_for_port('bridge-000-001', 1) receiver = BM.get_iface_for_port('bridge-000-001', 2) intruder = BM.get_iface_for_port('bridge-000-001', 3) receiver_MAC = receiver.get_mac_addr() frame = '%s-%s-aa:bb' % (receiver_MAC, receiver_MAC) capture = 'icmp and src host %s' % (sender.get_ip()) # Populate ARP table sender.execute('arp -s %s %s' % (receiver.get_ip(), receiver_MAC)) receiver.execute('arp -s %s %s' % (sender.get_ip(), sender.get_mac_addr())) # Trigger receiver MAC learning receiver.send_ether(frame) # First: packets go from sender to receiver f1 = async_assert_that(receiver, receives(capture, within_sec(5))) f2 = async_assert_that(intruder, should_NOT_receive(capture, within_sec(5))) f3 = sender.ping4(receiver) wait_on_futures([f1, f2, f3]) # Second: intruder claims to be receiver intruder.send_ether(frame) # Third: packets go from sender to intruder f1 = async_assert_that(receiver, should_NOT_receive(capture, within_sec(5))) f2 = async_assert_that(intruder, receives(capture, within_sec(5))) f3 = sender.ping4(receiver) wait_on_futures([f1, f2, f3])
def test_ping_delete_port(): """ Title: L3 connectivity over bridge and router, then deletes a port and verifies that there is no connectivity. Implemented to cover the old DeletePortTest. Scenario 1: When: a VM sends ICMP echo request with ping command to a different subnet Then: the receiver VM should receive the ICMP echo packet. And: the ping command succeeds When: the destination port on the router is deleted Then: the receiver VM should NOT receive the ICMP echo packet """ sender = BM.get_iface_for_port('bridge-000-001', 2) receiver = BM.get_iface_for_port('bridge-000-002', 2) # The receiver VM needs to send some frames so the MN Router learns # the VM's mac address. Otherwise this test would fail with binding2 # because the MidoNet Router forwards the ICMP with the previous mac # found in bindings1 in ethernet headers. # Issue: https://midobugs.atlassian.net/browse/MN-79 receiver.ping4(sender) f1 = async_assert_that( receiver, receives('dst host 172.16.2.1 and icmp', within_sec(5))) f2 = sender.ping4(receiver) wait_on_futures([f1, f2]) port = VTM.get_device_port('router-000-001', 2) port.destroy() f1 = async_assert_that( receiver, should_NOT_receive('dst host 172.16.2.1 and icmp', within_sec(5))) f2 = sender.ping4(receiver) wait_on_futures([f1, f2])
def test_filtering_by_network_address(): ''' Title: Tests packets filtering based on network address Scenario: When: A VM sends UDP packets to another host on the same bridge. Then: The UDP packets reaches the receiver. Then: Filtering rule chains based on network address (IP address) are set on the bridge port that the receiver host is connected to. And: The UDP packets from the same sender do NOT reach the receiver. ''' sender = BM.get_iface_for_port('bridge-000-001', 2) receiver = BM.get_iface_for_port('bridge-000-001', 3) # Reset in/out-bound filters. unset_bridge_port_filters('bridge-000-001', 3) port_num = get_random_port_num() # FIXME: do not use harcoded values! f1 = async_assert_that(receiver, receives('dst host 172.16.1.2 and udp', within_sec(5)), 'No filtering: receives UDP packets from sender.') f2 = sender.send_udp('aa:bb:cc:00:01:02', '172.16.1.2', 41, src_port=port_num, dst_port=port_num) wait_on_futures([f1, f2]) # Set a filtering rule based on network address. set_bridge_port_filters('bridge-000-001', 3, 'connection_tracking_nw_in', 'connection_tracking_nw_out') f1 = async_assert_that(receiver, should_NOT_receive( 'dst host 172.16.1.2 and udp', within_sec(5)), 'Packets are filtered based on IP address.') f2 = sender.send_udp('aa:bb:cc:00:01:02', '172.16.1.2', 41, src_port=port_num, dst_port=port_num) wait_on_futures([f1, f2])
def test_floating_ip(): """ Title: Tests a floating IP. Scenario 1: When: a VM sends an ICMP echo request to a floating IP address (100.100.100.100). Then: the router performs DNAT on the message according to the rule chain set to the router, And: the receiver VM should receive the ICMP echo packet, And: the receiver sends back an ICMP reply with its original IP address as a source address. And: the router applies SNAT to the reply packet. And: the sender receives the reply with src address NATed to the floating IP address. """ sender = BM.get_iface_for_port('bridge-000-001', 2) receiver = BM.get_iface_for_port('bridge-000-002', 2) # Reset in-/out-bound filters. unset_filters('router-000-001') feed_receiver_mac(receiver) f1 = async_assert_that( receiver, should_NOT_receive('dst host 172.16.2.1 and icmp', within_sec(10))) sender.ping_ipv4_addr('100.100.100.100') wait_on_futures([f1]) # Configure floating IP address with the router set_filters('router-000-001', 'pre_filter_floating_ip', 'post_filter_floating_ip') f1 = async_assert_that( receiver, receives('dst host 172.16.2.1 and icmp', within_sec(10))) f2 = async_assert_that( sender, receives('src host 100.100.100.100 and icmp', within_sec(10))) sender.ping_ipv4_addr('100.100.100.100') wait_on_futures([f1, f2])
def test_connection_tracking_with_drop_by_dl(): ''' Title: Tests dl-based connection tracking. Scenario: When: A VM inside a FW sends UDP packets to a VM outside. And: The outside receives the UDP packets. Then: A connection-tracking-based peep hole is established. And: The outside now can send UDP packets to the inside. ''' outside = BM.get_iface_for_port('bridge-000-001', 2) inside = BM.get_iface_for_port('bridge-000-001', 3) # Set a filtering rule based on mac addresses set_bridge_port_filters('bridge-000-001', 3, 'connection_tracking_dl_in', 'connection_tracking_dl_out') # Send forward packets to set up a connection-tracking based peep hole in # the filter. port_num = get_random_port_num() f1 = async_assert_that(outside, receives('dst host 172.16.1.1 and udp', within_sec(5)), 'The outside host receives forward packets ' 'from the inside.') f2 = inside.send_udp('aa:bb:cc:00:01:01', '172.16.1.1', 41, src_port=port_num, dst_port=port_num) wait_on_futures([f1, f2]) # Verify the peep hole. f1 = async_assert_that(inside, receives('dst host 172.16.1.2 and udp', within_sec(5)), 'The outside host can now send packets to the inside' 'via a peep hole.') f2 = outside.send_udp('aa:bb:cc:00:01:02', '172.16.1.2', 41, src_port=port_num, dst_port=port_num) wait_on_futures([f1, f2])
def test_snat(): """ Title: Tests SNAT on ping messages. Scenario: When: a VM sends ICMP echo request with ping command to a different subnet, Then: the router performs SNAT on the message according to the rule chain set to the router, And: the receiver VM should receive the ICMP echo packet, with src address NATted, And: the ping command succeeds. """ sender = BM.get_iface_for_port('bridge-000-001', 2) receiver = BM.get_iface_for_port('bridge-000-002', 2) # Reset in-/out-bound filters. unset_filters('router-000-001') feed_receiver_mac(receiver) # No SNAT configured. Should not receive SNATed messages. f2 = async_assert_that( receiver, should_NOT_receive('src host 172.16.1.100 and icmp', within_sec(5))) f1 = sender.ping4(receiver) wait_on_futures([f1, f2]) # Set SNAT rule chains to the router set_filters('router-000-001', 'pre_filter_002', 'post_filter_002') # The receiver should receive SNATed messages. f2 = async_assert_that( receiver, receives('src host 172.16.1.100 and icmp', within_sec(5))) f3 = async_assert_that( sender, receives('dst host 172.16.1.1 and icmp', within_sec(5))) f1 = sender.ping4(receiver) wait_on_futures([f1, f2, f3])
def run_garp_scenario(BM, sender_port, target_ip, enable_vip, disable_vip): vip1 = BM.get_interface_on_vport('port_int1') vip2 = BM.get_interface_on_vport('port_int2') sender = BM.get_interface_on_vport(sender_port) # allow sender to accept gratutious arps (only makes sense if on same network) sender.execute('bash -c "echo 1 > /proc/sys/net/ipv4/conf/%s/arp_accept"' % sender.get_ifname()) rcv_filter = 'icmp and ip src %s' % (sender.get_ip()) # noone responds initially f1 = async_assert_that(vip1, should_NOT_receive(rcv_filter, within_sec(10))) f2 = async_assert_that(vip2, should_NOT_receive(rcv_filter, within_sec(10))) f3 = sender.ping_ipv4_addr(target_ip, count=5) wait_on_futures([f1, f2, f3]) # enable for vip1 enable_vip(vip1) disable_vip(vip2) f1 = async_assert_that(vip1, receives(rcv_filter, within_sec(10))) f2 = async_assert_that(vip2, should_NOT_receive(rcv_filter, within_sec(10))) f3 = sender.ping_ipv4_addr(target_ip, count=5) wait_on_futures([f1, f2, f3]) # enable for vip2 enable_vip(vip2) disable_vip(vip1) f1 = async_assert_that(vip1, should_NOT_receive(rcv_filter, within_sec(10))) f2 = async_assert_that(vip2, receives(rcv_filter, within_sec(10))) f3 = sender.ping_ipv4_addr(target_ip, count=5) wait_on_futures([f1, f2, f3]) # enable for vip1 enable_vip(vip1) disable_vip(vip2) f1 = async_assert_that(vip1, receives(rcv_filter, within_sec(10))) f2 = async_assert_that(vip2, should_NOT_receive(rcv_filter, within_sec(10))) f3 = sender.ping_ipv4_addr(target_ip, count=5) wait_on_futures([f1, f2, f3])
def test_icmp_after_interface_recovery(): """ Title: ICMP reachability over bridge before and after interfaces go down Scenario 1: When: a VM sends ICMP echo request with ping command Then: the receiver VM should receive the ICMP echo packet. And: the ping command succeeds Then: the receiver VM's tap goes down And: the ping command fails Then: the receiver VM's tap goes back up And: the ping command succeeds """ sender = BM.get_iface_for_port('bridge-000-001', 1) receiver = BM.get_iface_for_port('bridge-000-001', 3) f1 = async_assert_that( receiver, receives('dst host 172.16.1.3 and icmp', within_sec(5))) f2 = sender.ping4(receiver) wait_on_futures([f1, f2]) receiver.set_down() f1 = async_assert_that( receiver, should_NOT_receive('icmp', within_sec(5), on_host_interface(True))) f2 = sender.ping4(receiver) wait_on_futures([f1, f2]) receiver.set_up() f1 = async_assert_that( receiver, receives('dst host 172.16.1.3 and icmp', within_sec(5))) f2 = sender.ping4(receiver) wait_on_futures([f1, f2])
def test_rule_changes(): """ Title: ICMP reachability over bridge before and after adding rule to drop IPv4 traffic. Scenario 1: When: A VM sends ICMP echo request with ping command Then: The receiver VM should receive the ICMP echo packet. And: The ping command succeeds Then: The receiver adds a rule blocking IPv4 traffic. And: The ping command fails Then: the receiver removes the rule And: The ping succeeds again. """ sender = BM.get_iface_for_port('bridge-000-001', 1) receiver = BM.get_iface_for_port('bridge-000-001', 2) # There are no filters, so the first ping should succeed. f1 = async_assert_that(receiver, receives('icmp', within_sec(5))) f2 = sender.ping4(receiver, do_arp=True) wait_on_futures([f1, f2]) # Add a filter dropping all IPv4 traffic to port 2. chain = VTM.get_chain('drop_ipv4') VTM.get_device_port('bridge-000-001', 2).set_outbound_filter(chain) # The second ping should not reach port 2. f1 = async_assert_that(receiver, should_NOT_receive('icmp', within_sec(5))) f2 = sender.ping4(receiver, do_arp=True) wait_on_futures([f1, f2]) # After removing the filter, ping should succeed again. VTM.get_device_port('bridge-000-001', 2).set_outbound_filter(None) f1 = async_assert_that(receiver, receives('icmp', within_sec(5))) f2 = sender.ping4(receiver, do_arp=True) wait_on_futures([f1, f2])
def test_l2insertion_both_ends_protected(): """ Title: Test insertions with both ends protected 2 L2 insertions on 2 ports, each insertion with a different bad pattern. The bad packets matching the different patterns should be blocked at different insertions depending on the direction of the packet. Verify that packets are blocked in the same order regardless of the direction. """ api = get_midonet_api() service_port1 = BM.get_iface_for_port('bridge-000-001', 2) service_port2 = BM.get_iface_for_port('bridge-000-001', 3) fakesnort1 = FakeSnort(service_port1, "deadbeef") fakesnort2 = FakeSnort(service_port2, "cafef00d") try: fakesnort1.run() fakesnort2.run() insertion_port1 = BM.get_iface_for_port('bridge-000-001', 1) insertion_port2 = BM.get_iface_for_port('bridge-000-001', 4) insertion1 = api.add_l2insertion() insertion1.srv_port(service_port1.vport_id) insertion1.port(insertion_port1.vport_id) insertion1.position(1) insertion1.fail_open(False) insertion1.vlan(1) insertion1.mac(insertion_port1.get_mac_addr()) insertion1.create() insertion2 = api.add_l2insertion() insertion2.srv_port(service_port2.vport_id) insertion2.port(insertion_port1.vport_id) insertion2.position(2) insertion2.fail_open(False) insertion2.vlan(2) insertion2.mac(insertion_port1.get_mac_addr()) insertion2.create() insertion3 = api.add_l2insertion() insertion3.srv_port(service_port1.vport_id) insertion3.port(insertion_port2.vport_id) insertion3.position(1) insertion3.fail_open(False) insertion3.vlan(3) insertion3.mac(insertion_port2.get_mac_addr()) insertion3.create() insertion4 = api.add_l2insertion() insertion4.srv_port(service_port2.vport_id) insertion4.port(insertion_port2.vport_id) insertion4.position(2) insertion4.fail_open(False) insertion4.vlan(4) insertion4.mac(insertion_port2.get_mac_addr()) insertion4.create() time.sleep(5) rcv_filter = 'ip dst %s and icmp[icmptype] == 8' % insertion_port2.get_ip() LOG.info("Sending good packet") f1 = async_assert_that(service_port1, receives(rcv_filter, within_sec(10), count=2)) f2 = async_assert_that(service_port2, receives(rcv_filter, within_sec(10), count=2)) f3 = async_assert_that(insertion_port2, receives(rcv_filter, within_sec(10))) ping_port(insertion_port1, insertion_port2, data="f00b4c") wait_on_futures([f1, f2, f3]) LOG.info("Sending dead beef") f1 = async_assert_that(service_port1, receives(rcv_filter, within_sec(10))) f2 = async_assert_that(service_port2, should_NOT_receive(rcv_filter, within_sec(10))) f3 = async_assert_that(insertion_port2, should_NOT_receive(rcv_filter, within_sec(10))) ping_port(insertion_port1, insertion_port2, data="deadbeef", should_succeed=False) wait_on_futures([f1, f2, f3]) LOG.info("Sending cafe food") f1 = async_assert_that(service_port1, receives(rcv_filter, within_sec(10))) f2 = async_assert_that(service_port2, receives(rcv_filter, within_sec(10))) f3 = async_assert_that(insertion_port2, should_NOT_receive(rcv_filter, within_sec(10))) ping_port(insertion_port1, insertion_port2, data="cafef00d", should_succeed=False) wait_on_futures([f1, f2, f3]) finally: fakesnort2.kill() fakesnort1.kill()
def test_l2insertion_with_flowstate(): """ Title: Test insertions with flow state 2 L2 insertions on a port, each with a different bad pattern. Packets are sent over DNAT so that flow state is generated and necessary to return the packet. The bad packets matching the different patterns should be blocked at different points. Verify that packets are blocked in the same order regardless of the direction. """ api = get_midonet_api() router = VTM.get_router('router-000-001') conntrack_filter = VTM.get_chain('conntrack_filter_001') router.set_inbound_filter(conntrack_filter) # Sleep here to make sure that the settings have been propagated. time.sleep(5) service_port1 = BM.get_iface_for_port('bridge-000-001', 2) service_port2 = BM.get_iface_for_port('bridge-000-001', 3) fakesnort1 = FakeSnort(service_port1, "deadbeef") fakesnort2 = FakeSnort(service_port2, "cafef00d") try: fakesnort1.run() fakesnort2.run() insertion_port = BM.get_iface_for_port('bridge-000-001', 1) other_port = BM.get_iface_for_port('bridge-000-002', 1) insertion1 = api.add_l2insertion() insertion1.srv_port(service_port1.vport_id) insertion1.port(insertion_port.vport_id) insertion1.position(1) insertion1.fail_open(False) insertion1.vlan(1) insertion1.mac(insertion_port.get_mac_addr()) insertion1.create() insertion2 = api.add_l2insertion() insertion2.srv_port(service_port2.vport_id) insertion2.port(insertion_port.vport_id) insertion2.position(2) insertion2.fail_open(False) insertion2.vlan(2) insertion2.mac(insertion_port.get_mac_addr()) insertion2.create() time.sleep(5) rcv_filter = 'ip dst %s and icmp[icmptype] == 8' % insertion_port.get_ip() LOG.info("Sending good packet") f1 = async_assert_that(service_port1, receives(rcv_filter, within_sec(10))) f2 = async_assert_that(service_port2, receives(rcv_filter, within_sec(10))) f3 = async_assert_that(insertion_port, receives(rcv_filter, within_sec(10))) ping_port(other_port, insertion_port, data="f00b4c") wait_on_futures([f1, f2, f3]) LOG.info("Sending dead beef") f1 = async_assert_that(service_port1, receives(rcv_filter, within_sec(10))) f2 = async_assert_that(service_port2, should_NOT_receive(rcv_filter, within_sec(10))) f3 = async_assert_that(insertion_port, should_NOT_receive(rcv_filter, within_sec(10))) ping_port(other_port, insertion_port, data="deadbeef", should_succeed=False) wait_on_futures([f1, f2, f3]) LOG.info("Sending cafe food") f1 = async_assert_that(service_port1, receives(rcv_filter, within_sec(10))) f2 = async_assert_that(service_port2, receives(rcv_filter, within_sec(10))) f3 = async_assert_that(insertion_port, should_NOT_receive(rcv_filter, within_sec(10))) ping_port(other_port, insertion_port, data="cafef00d", should_succeed=False) wait_on_futures([f1, f2, f3]) LOG.info("Sending another good packet") f1 = async_assert_that(service_port1, receives(rcv_filter, within_sec(10))) f2 = async_assert_that(service_port2, receives(rcv_filter, within_sec(10))) f3 = async_assert_that(insertion_port, receives(rcv_filter, within_sec(10))) ping_port(other_port, insertion_port, data="beeff00d") wait_on_futures([f1, f2, f3]) finally: fakesnort1.kill() fakesnort2.kill()
def test_l2insertion_fail_open(): """ Title: Test insertions with fail open 2 L2 insertions on a port, each insertion with a different bad pattern. One of the insertions is set to fail open. Test both bad patterns can't get through. Take down the fail open insertion. Verify that that bad pattern can now get through. Take down the non fail open insertion. Verify that no traffic can get through. Also verify both directions """ api = get_midonet_api() service_port1 = BM.get_iface_for_port('bridge-000-001', 2) service_port2 = BM.get_iface_for_port('bridge-000-001', 3) fakesnort1 = FakeSnort(service_port1, "deadbeef") fakesnort2 = FakeSnort(service_port2, "cafef00d") try: fakesnort1.run() fakesnort2.run() insertion_port = BM.get_iface_for_port('bridge-000-001', 1) other_port = BM.get_iface_for_port('bridge-000-001', 4) insertion1 = api.add_l2insertion() insertion1.srv_port(service_port1.vport_id) insertion1.port(insertion_port.vport_id) insertion1.position(1) insertion1.fail_open(True) insertion1.vlan(1) insertion1.mac(insertion_port.get_mac_addr()) insertion1.create() insertion2 = api.add_l2insertion() insertion2.srv_port(service_port2.vport_id) insertion2.port(insertion_port.vport_id) insertion2.position(2) insertion2.fail_open(False) insertion2.vlan(2) insertion2.mac(insertion_port.get_mac_addr()) insertion2.create() time.sleep(5) rcv_filter = 'ip dst %s and icmp[icmptype] == 8' % insertion_port.get_ip() rcv_filter_ret = 'ip src %s and icmp[icmptype] == 8' % insertion_port.get_ip() LOG.info("Sending good packet") f1 = async_assert_that(service_port1, receives(rcv_filter, within_sec(10))) f2 = async_assert_that(service_port2, receives(rcv_filter, within_sec(10))) f3 = async_assert_that(insertion_port, receives(rcv_filter, within_sec(10))) ping_port(other_port, insertion_port, data="f00b4c") wait_on_futures([f1, f2, f3]) LOG.info("Sending dead beef") f1 = async_assert_that(service_port1, receives(rcv_filter, within_sec(10))) f2 = async_assert_that(service_port2, should_NOT_receive(rcv_filter, within_sec(10))) f3 = async_assert_that(insertion_port, should_NOT_receive(rcv_filter, within_sec(10))) ping_port(other_port, insertion_port, data="deadbeef", should_succeed=False) wait_on_futures([f1, f2, f3]) LOG.info("Sending cafe food") f1 = async_assert_that(service_port1, receives(rcv_filter, within_sec(10))) f2 = async_assert_that(service_port2, receives(rcv_filter, within_sec(10))) f3 = async_assert_that(insertion_port, should_NOT_receive(rcv_filter, within_sec(10))) ping_port(other_port, insertion_port, data="cafef00d", should_succeed=False) wait_on_futures([f1, f2, f3]) LOG.info("Sending dead beef the other way") f1 = async_assert_that(service_port1, receives(rcv_filter_ret, within_sec(10))) f2 = async_assert_that(service_port2, should_NOT_receive(rcv_filter_ret, within_sec(10))) f3 = async_assert_that(other_port, should_NOT_receive(rcv_filter_ret, within_sec(10))) ping_port(insertion_port, other_port, data="deadbeef", should_succeed=False) wait_on_futures([f1, f2, f3]) set_interface_admin_down(service_port1) time.sleep(5) # give midolman a chance to see it LOG.info("Sending dead beef again") f1 = async_assert_that(service_port2, receives(rcv_filter, within_sec(10))) f2 = async_assert_that(insertion_port, receives(rcv_filter, within_sec(10))) ping_port(other_port, insertion_port, data="deadbeef") wait_on_futures([f1, f2]) LOG.info("Sending dead beef the other way again") f1 = async_assert_that(service_port2, receives(rcv_filter_ret, within_sec(10))) f2 = async_assert_that(other_port, receives(rcv_filter_ret, within_sec(10))) ping_port(insertion_port, other_port, data="deadbeef") wait_on_futures([f1, f2]) LOG.info("Sending cafe food again") f1 = async_assert_that(service_port2, receives(rcv_filter, within_sec(10))) f2 = async_assert_that(insertion_port, should_NOT_receive(rcv_filter, within_sec(10))) ping_port(other_port, insertion_port, data="cafef00d", should_succeed=False) wait_on_futures([f1, f2]) set_interface_admin_down(service_port2) time.sleep(5) # nothing should get through LOG.info("Sending good packet") f1 = async_assert_that(insertion_port, should_NOT_receive(rcv_filter, within_sec(10))) ping_port(other_port, insertion_port, data="f00b4c", should_succeed=False) wait_on_futures([f1]) LOG.info("Sending good packet the other way") f1 = async_assert_that(other_port, should_NOT_receive(rcv_filter_ret, within_sec(10))) ping_port(insertion_port, other_port, data="f00b4c", should_succeed=False) wait_on_futures([f1]) finally: fakesnort2.kill() fakesnort1.kill()
def test_tracing_with_limit(): """ Title: Tracing with a limit Scenario 1: When: a VM sends 20 ICMP echo requests over a trace request with limit 10 Then: Trace data appears for the ingress and the egress host, but only for the first 10. Then: when disabled and reenabled, new trace data shows up """ tracerequest = VTM.get_tracerequest('ping-trace-request-limited') try: set_filters('router-000-001', 'pre_filter_001', 'post_filter_001') tracerequest.set_enabled(True) time.sleep(5) flowtraces = get_flow_traces(tracerequest.get_id()) assert (len(flowtraces) == 0) sender = BM.get_iface_for_port('bridge-000-001', 2) receiver = BM.get_iface_for_port('bridge-000-002', 2) feed_receiver_mac(receiver) for i in range(0, 20): f2 = async_assert_that( receiver, receives('dst host 172.16.2.1 and icmp', within_sec(10))) f3 = async_assert_that( sender, receives('src host 172.16.2.1 and icmp', within_sec(10))) f1 = sender.ping_ipv4_addr('172.16.2.1') wait_on_futures([f1, f2, f3]) time.sleep(5) flowtraces = get_flow_traces(tracerequest.get_id()) assert (len(flowtraces) == 10) # ensure both packets were traced on both hosts for i in range(0, 10): assert (len(get_hosts(tracerequest.get_id(), flowtraces[i])) == 2) tracerequest.set_enabled(False) tracerequest.set_enabled(True) time.sleep(5) f2 = async_assert_that( receiver, receives('dst host 172.16.2.1 and icmp', within_sec(10))) f3 = async_assert_that( sender, receives('src host 172.16.2.1 and icmp', within_sec(10))) f1 = sender.ping_ipv4_addr('172.16.2.1') wait_on_futures([f1, f2, f3]) time.sleep(5) flowtraces = get_flow_traces(tracerequest.get_id()) assert (len(flowtraces) == 11) finally: unset_filters('router-000-001') tracerequest.set_enabled(False)
def test_compat_fip(): """ Title: Tests that FIP creation and deletions are backwards compatible We add a new FIP and test that a ping to it works. Afterwards we upgrade, delete the FIP, and test that the ping fails. Finally we add a new FIP, and test that the ping works again. """ public_vm1 = BM.get_interface_on_vport('public_1') private_vm2 = BM.get_interface_on_vport('private_2') fip = VTM.get_resource('public_1_fip')['floatingip'] fip_address = fip['floating_ip_address'] agent1 = service.get_container_by_hostname('midolman1') agent2 = service.get_container_by_hostname('midolman2') cluster = service.get_container_by_hostname('cluster1') # Restart the cluster. Cluster should always be upgraded before agents. cluster.stop(wait=False) cluster.start(wait=True) # Pinging the FIP works check_forward_flow(private_vm2, public_vm1, fip_address, 50000, 80) # Restart the agents public_vm1_id = VTM.get_resource('public_1')['port']['id'] private_vm2_id = VTM.get_resource('private_2')['port']['id'] agent1.stop(wait=True) agent2.stop(wait=True) await_port_active(public_vm1_id, active=False) await_port_active(private_vm2_id, active=False) agent1.start(wait=True) agent2.start(wait=True) await_port_active(public_vm1_id, active=True) await_port_active(private_vm2_id, active=True) # Pinging the FIP after the update works check_forward_flow(private_vm2, public_vm1, fip_address, 50000, 80) # Disassociate FIP fip['port_id'] = None VTM.update_floating_ip(fip) # Ping does not reach FIP f = async_assert_that( public_vm1, should_NOT_receive("udp and src host %s" % private_vm2.get_ip(), 10)) private_vm2.execute('hping3 -c 1 -q -2 -s 50000 -p 80 %s' % fip_address) wait_on_futures([f]) # Before re-associating the FIP, we need to disable and enable SNAT on the # router so that the old router creation works with the new MN version # github.com/midonet/midonet/commit/4d5cb885decbb3bfacfece69639ccae2f570c5b3 router = VTM.get_resource('router_1')['router'] public_net = VTM.get_resource('public')['network'] VTM.set_router_gateway(router, public_net, enable_snat=False) VTM.set_router_gateway(router, public_net, enable_snat=True) # Associate FIP again fip['port_id'] = public_vm1_id VTM.update_floating_ip(fip) # Afterwards ping works again check_forward_flow(private_vm2, public_vm1, fip_address, 50000, 80)
def test_router_service(): """ Title: TCP/UDP services on router Scenario 1: When: A client tries to access a TCP or UDP service on a router. Then: the router should redirect the traffic to a namespace. And: the namespace should respond correctly. """ router = VTM.get_router('router-000-001') pre_filter = VTM.get_chain('pre_filter_001') router.set_local_redirect_chain(pre_filter) sender1 = BM.get_iface_for_port('bridge-000-001', 2) service1 = BM.get_iface_for_port('router-000-001', 2) service2 = BM.get_iface_for_port('router-000-001', 3) # setup extra parameters in namespaces service1.execute("ip a add 172.16.1.254/32 dev lo") service1.execute("arp -s 169.254.1.1 aa:bb:cc:00:11:33") service2.execute("ip a add 172.16.1.254/32 dev lo") service2.execute("arp -s 169.254.2.1 aa:bb:cc:00:11:33") # assert udp traffic goes to correct node f1 = async_assert_that( service1, receives('dst host 172.16.1.254 and udp port 500', within_sec(5))) f2 = async_assert_that( service2, should_NOT_receive('dst host 172.16.1.2 and udp port 500', within_sec(5))) f3 = sender1.send_udp('aa:bb:cc:00:00:22', '172.16.1.254', 41, src_port=500, dst_port=500) wait_on_futures([f1, f2, f3]) # assert udp traffic is returned f1 = async_assert_that( sender1, receives('dst host 172.16.1.1 and udp port 500', within_sec(5))) f2 = service1.send_udp('aa:bb:cc:00:11:22', '172.16.1.1', 41, src_port=500, dst_port=500, src_hw='aa:bb:cc:00:00:22', src_ipv4='172.16.1.254') wait_on_futures([f1, f2]) # assert tcp traffic goes both ways try: service2.execute("sh -c \"echo foobar | nc -l 1500\"") f1 = async_assert_that( service2, receives('dst host 172.16.1.254 and tcp port 1500', within_sec(5))) f2 = async_assert_that( sender1, receives('dst host 172.16.1.1 and tcp port 1500', within_sec(5))) f3 = async_assert_that( service1, should_NOT_receive('dst host 172.16.1.254', within_sec(5))) sender1.execute("sh -c \"echo foobar | nc 172.16.1.254 1500\"") wait_on_futures([f1, f2, f3]) finally: service2.execute("pkill nc")