def runTest(self): # Get test parameters self.exp_iface = self.test_params['exp_iface'] self.timeout = self.test_params['timeout'] self.packet_timing = self.test_params['packet_timing'] self.ether_type = self.test_params['ether_type'] self.interval_count = int(self.test_params['interval_count']) if self.interval_count < 1: self.interval_count = 3 # Make sure the interval count is odd, so that we only look at one median interval if self.interval_count % 2 == 0: self.interval_count += 1 # Generate a packet. exp_pkt = simple_eth_packet(eth_type=self.ether_type) exp_pkt = exp_pkt / ("0" * 64) # Ignore fields with value unknown masked_exp_pkt = Mask(exp_pkt) masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "dst") masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "src") masked_exp_pkt.set_do_not_care(14 * 8, 110 * 8) # Flush packets in dataplane self.dataplane.flush() # Check that packet timing matches the expected value. current_pkt_timing = self.getMedianInterval(masked_exp_pkt) self.assertTrue( abs(current_pkt_timing - float(self.packet_timing)) < 0.1, "Bad packet timing: %.2f seconds while expected timing is %d seconds from port %s out of %d intervals" % (current_pkt_timing, self.packet_timing, self.exp_iface, self.interval_count))
def runIntTransitTest(self, pkt, tagged1, tagged2, ignore_csum=False, switch_id=1, mpls=False): if IP not in pkt: self.fail("Packet is not IP") if UDP not in pkt and TCP not in pkt: self.fail("Packet must be UDP or TCP for INT tests") if INT_META_HDR not in pkt: self.fail("Packet must have INT_META_HDR") if INT_L45_HEAD not in pkt: self.fail("Packet must have INT_L45_HEAD") proto = UDP if UDP in pkt else TCP # will use runIPv4UnicastTest dst_mac = HOST2_MAC ig_port = self.port1 eg_port = self.port2 self.setup_transit(switch_id) instructions = self.get_ins_from_mask(pkt[INT_META_HDR].inst_mask) ins_cnt = len(instructions) assert ins_cnt == pkt[INT_META_HDR].ins_cnt # Forge expected packet based on pkt's ins_mask new_metadata, masked_ins_cnt = self.get_int_metadata( instructions=instructions, switch_id=switch_id, ig_port=ig_port, eg_port=eg_port) exp_pkt = pkt.copy() exp_pkt[INT_L45_HEAD].length += pkt[INT_META_HDR].ins_cnt exp_pkt[INT_META_HDR].total_hop_cnt += 1 exp_pkt[INT_META_HDR].payload = new_metadata + str(exp_pkt[INT_META_HDR].payload) exp_pkt[Ether].src = exp_pkt[Ether].dst exp_pkt[Ether].dst = dst_mac if not mpls: exp_pkt[IP].ttl = exp_pkt[IP].ttl - 1 else: exp_pkt = pkt_add_mpls(exp_pkt, MPLS_LABEL_2, DEFAULT_MPLS_TTL) if tagged2: # VLAN if tagged1 will be added by runIPv4UnicastTest exp_pkt = pkt_add_vlan(exp_pkt, VLAN_ID_2) if ignore_csum or masked_ins_cnt > 0: mask_pkt = Mask(exp_pkt) if ignore_csum: csum_offset = len(exp_pkt) - len(exp_pkt[IP].payload) \ + (6 if proto is UDP else 16) mask_pkt.set_do_not_care(csum_offset * 8, 2 * 8) if masked_ins_cnt > 0: offset_metadata = len(exp_pkt) - len(exp_pkt[proto].payload) \ + len(INT_L45_HEAD()) + len(INT_META_HDR()) \ + (ins_cnt - masked_ins_cnt) * 4 mask_pkt.set_do_not_care(offset_metadata * 8, masked_ins_cnt * 4 * 8) exp_pkt = mask_pkt self.runIPv4UnicastTest(pkt=pkt, next_hop_mac=HOST2_MAC, tagged1=tagged1, tagged2=tagged2, mpls=mpls, prefix_len=32, exp_pkt=exp_pkt)
def checkMirroredFlow(self): """ @summary: Send traffic & check how many mirrored packets are received @return: count: number of mirrored packets received Note: Mellanox crafts the GRE packets with extra information: That is: 22 bytes extra information after the GRE header """ payload = self.base_pkt if self.asic_type in ["mellanox"]: import binascii payload = binascii.unhexlify("0" * 44) + str( payload) # Add the padding exp_pkt = testutils.simple_gre_packet( eth_src=self.router_mac, ip_src=self.session_src_ip, ip_dst=self.session_dst_ip, ip_dscp=self.session_dscp, ip_id=0, #ip_flags = 0x10, # need to upgrade ptf version to support it ip_ttl=self.session_ttl, inner_frame=payload) if self.asic_type in ["mellanox"]: exp_pkt['GRE'].proto = 0x8949 # Mellanox specific else: exp_pkt['GRE'].proto = 0x88be masked_exp_pkt = Mask(exp_pkt) masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "dst") masked_exp_pkt.set_do_not_care_scapy(scapy.IP, "flags") masked_exp_pkt.set_do_not_care_scapy(scapy.IP, "chksum") if self.asic_type in ["mellanox"]: masked_exp_pkt.set_do_not_care( 304, 176) # Mask the Mellanox specific inner header self.dataplane.flush() count = 0 for i in range(0, self.NUM_OF_TOTAL_PACKETS): testutils.send_packet(self, self.src_port, self.base_pkt) (rcv_device, rcv_port, rcv_pkt, pkt_time) = testutils.dp_poll(self, timeout=0.1, exp_pkt=masked_exp_pkt) if rcv_pkt is not None: count += 1 elif count == 0: print "The first mirrored packet is not recieved" assert False # Fast failure without waiting for full iteration print "Received " + str( count) + " mirrored packets after rate limiting" return count
def verify_ack_received(self): dhcp_ack = self.create_dhcp_ack_packet() # Mask out lease time, ip checksum, udp checksum (explanation above) masked_ack = Mask(dhcp_ack) masked_ack.set_do_not_care_scapy(scapy.Ether, "dst") masked_ack.set_do_not_care_scapy(scapy.UDP, "chksum") masked_ack.set_do_not_care_scapy(scapy.IP, "chksum") # Also mask out lease time (see comment in verify_offer_received() above) masked_ack.set_do_not_care((self.DHCP_LEASE_TIME_OFFSET * 8), (self.DHCP_LEASE_TIME_LEN * 8)) # NOTE: verify_packet() will fail for us via an assert, so no nedd to check a return value here testutils.verify_packet(self, masked_ack, self.client_port_index)
def verify_offer_received(self): dhcp_offer = self.create_dhcp_offer_packet() masked_offer = Mask(dhcp_offer) masked_offer.set_do_not_care_scapy(scapy.Ether, "dst") masked_offer.set_do_not_care_scapy(scapy.UDP, "chksum") masked_offer.set_do_not_care_scapy(scapy.IP, "chksum") # Mask out lease time since it changes depending on when the server recieves the request # Lease time in ack can be slightly different than in offer, since lease time varies slightly # We also want to ignore the checksums since they will vary a bit depending on the timestamp # Offset is byte 292, 6 byte field, set_do_not_care() expects values in bits masked_offer.set_do_not_care((self.DHCP_LEASE_TIME_OFFSET * 8), (self.DHCP_LEASE_TIME_LEN * 8)) # NOTE: verify_packet() will fail for us via an assert, so no nedd to check a return value here testutils.verify_packet(self, masked_offer, self.client_port_index)
def runTest(self): # Get test parameters self.exp_iface = self.test_params['exp_iface'] self.timeout = self.test_params['timeout'] self.packet_timing = self.test_params['packet_timing'] self.ether_type = self.test_params['ether_type'] # Generate a packet. exp_pkt = simple_eth_packet(eth_type=self.ether_type) exp_pkt = exp_pkt / ("0" * 64) # Ignore fields with value unknown masked_exp_pkt = Mask(exp_pkt) masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "dst") masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "src") masked_exp_pkt.set_do_not_care(14 * 8, 110 * 8) # Flush packets in dataplane self.dataplane.flush() # Verify two LACP packets. (rcv_device, rcv_port, rcv_pkt, first_pkt_time) = self.dataplane.poll(port_number=self.exp_iface, timeout=self.timeout, exp_pkt=masked_exp_pkt) (rcv_device, rcv_port, rcv_pkt, last_pkt_time) = self.dataplane.poll(port_number=self.exp_iface, timeout=self.timeout, exp_pkt=masked_exp_pkt) # Check the packet received. self.assertTrue(rcv_pkt != None, "Failed to receive LACP packet\n") # Get current packet timing first_pkt_time = round(float(first_pkt_time), 2) last_pkt_time = round(float(last_pkt_time), 2) current_pkt_timing = last_pkt_time - first_pkt_time # Check that packet timing matches the expected value. self.assertTrue( abs(current_pkt_timing - float(self.packet_timing)) < 0.1, "Bad packet timing: %.2f seconds while expected timing is %d seconds from %s" % (current_pkt_timing, self.packet_timing, self.exp_iface))
def get_expected_mirror_packet(mirror_session, setup, duthost, mirror_packet, check_ttl): payload = mirror_packet.copy() # Add vendor specific padding to the packet if duthost.facts["asic_type"] in ["mellanox"]: payload = binascii.unhexlify("0" * 44) + str(payload) if duthost.facts["asic_type"] in ["barefoot", "cisco-8000"]: payload = binascii.unhexlify("0" * 24) + str(payload) expected_packet = testutils.simple_gre_packet( eth_src=setup["router_mac"], ip_src=mirror_session["session_src_ip"], ip_dst=mirror_session["session_dst_ip"], ip_dscp=int(mirror_session["session_dscp"]), ip_id=0, ip_ttl=int(mirror_session["session_ttl"]), inner_frame=payload) expected_packet["GRE"].proto = mirror_session["session_gre"] expected_packet = Mask(expected_packet) expected_packet.set_do_not_care_scapy(packet.Ether, "dst") expected_packet.set_do_not_care_scapy(packet.IP, "ihl") expected_packet.set_do_not_care_scapy(packet.IP, "len") expected_packet.set_do_not_care_scapy(packet.IP, "flags") expected_packet.set_do_not_care_scapy(packet.IP, "chksum") if duthost.facts["asic_type"] in ["cisco-8000"]: expected_packet.set_do_not_care_scapy(packet.GRE, "seqnum_present") if not check_ttl: expected_packet.set_do_not_care_scapy(packet.IP, "ttl") # The fanout switch may modify this value en route to the PTF so we should ignore it, even # though the session does have a DSCP specified. expected_packet.set_do_not_care_scapy(packet.IP, "tos") # Mask off the payload (we check it later) expected_packet.set_do_not_care(OUTER_HEADER_SIZE * 8, len(payload) * 8) return expected_packet
def checkMirroredFlow(self): """ @summary: Send traffic & check how many mirrored packets are received @return: count: number of mirrored packets received Note: Mellanox crafts the GRE packets with extra information: That is: 22 bytes extra information after the GRE header """ payload = self.base_pkt.copy() payload_mask = Mask(payload) if self.mirror_stage == "egress": payload['Ethernet'].src = self.router_mac payload['IP'].ttl -= 1 payload_mask.set_do_not_care_scapy(scapy.Ether, "dst") payload_mask.set_do_not_care_scapy(scapy.IP, "chksum") if self.asic_type in ["mellanox"]: import binascii payload = binascii.unhexlify("0" * 44) + str( payload) # Add the padding exp_pkt = testutils.simple_gre_packet( eth_src=self.router_mac, ip_src=self.session_src_ip, ip_dst=self.session_dst_ip, ip_dscp=self.session_dscp, ip_id=0, #ip_flags = 0x10, # need to upgrade ptf version to support it ip_ttl=self.session_ttl, inner_frame=payload) if self.asic_type in ["mellanox"]: exp_pkt['GRE'].proto = 0x8949 # Mellanox specific else: exp_pkt['GRE'].proto = 0x88be masked_exp_pkt = Mask(exp_pkt) masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "dst") masked_exp_pkt.set_do_not_care_scapy(scapy.IP, "flags") masked_exp_pkt.set_do_not_care_scapy(scapy.IP, "chksum") masked_exp_pkt.set_do_not_care( 38 * 8, len(payload) * 8 ) # don't match payload, payload will be matched by match_payload(pkt) def match_payload(pkt): pkt = scapy.Ether(pkt).load if self.asic_type in ["mellanox"]: pkt = pkt[22:] # Mask the Mellanox specific inner header pkt = scapy.Ether(pkt) return dataplane.match_exp_pkt(payload_mask, pkt) self.dataplane.flush() count = 0 testutils.send_packet(self, self.src_port, self.base_pkt, count=self.NUM_OF_TOTAL_PACKETS) for i in range(0, self.NUM_OF_TOTAL_PACKETS): (rcv_device, rcv_port, rcv_pkt, pkt_time) = testutils.dp_poll(self, timeout=0.1, exp_pkt=masked_exp_pkt) if rcv_pkt is not None and match_payload(rcv_pkt): count += 1 elif count == 0: assert_str = "The first mirrored packet is not recieved" assert count > 0, assert_str # Fast failure without waiting for full iteration else: break # No more packets available logger.info( "Received {} mirrored packets after rate limiting".format(count)) return count
def build_ttl0_pkts(version, dst_mac, dst_ip, vm_mac, vm_ip, dut_lb): """ Builds ttl0 packet to send and ICMP TTL exceeded packet to expect back. Args: version: 4 or 6 for dst_mac: Destination MAC, of DUT port. dst_ip: Destination IP, a farend VM interface. vm_mac: Source MAC, of VM port that packets are sent out. vm_ip: Source IP, of the VM port. dut_lb: Loopback of DUT, source of the ICMP packets returned to the VM. Returns: 3 packets, one with ttl0 to send, one as the ICMP expected packet, and one to check for TTL wrapping. """ if version == 4: send_pkt = simple_udp_packet( eth_dst=dst_mac, # mac address of dut eth_src=vm_mac, # mac address of vm1 ip_src=str(vm_ip), ip_dst=str(dst_ip), ip_ttl=0, pktlen=100) exp_pkt255 = simple_udp_packet( eth_dst=dst_mac, # mac address of dut eth_src=vm_mac, # mac address of vm1 ip_src=str(vm_ip), ip_dst=str(dst_ip), ip_ttl=255, pktlen=100) v4_pktsz = 128 exp_pkt = simple_icmp_packet( eth_dst=vm_mac, # mac address of vm1 eth_src=dst_mac, # mac address of dut ip_src=dut_lb, ip_dst=vm_ip, ip_ttl=64, icmp_code=0, icmp_type=11, pktlen=v4_pktsz, ) masked_pkt = Mask(exp_pkt) masked_pkt.set_do_not_care_scapy(scapy.IP, "tos") masked_pkt.set_do_not_care_scapy(scapy.IP, "len") masked_pkt.set_do_not_care_scapy(scapy.IP, "id") masked_pkt.set_do_not_care_scapy(scapy.IP, "chksum") masked_pkt.set_do_not_care_scapy(scapy.ICMP, "chksum") masked_pkt.set_do_not_care(304, v4_pktsz * 8 - 304) # ignore icmp data else: send_pkt = simple_udpv6_packet( eth_dst=dst_mac, # mac address of dut eth_src=vm_mac, # mac address of vm1 ipv6_src=str(vm_ip), ipv6_dst=str(dst_ip), ipv6_hlim=0, pktlen=100) exp_pkt255 = simple_udpv6_packet( eth_dst=dst_mac, # mac address of dut eth_src=vm_mac, # mac address of vm1 ipv6_src=str(vm_ip), ipv6_dst=str(dst_ip), ipv6_hlim=255, pktlen=100) v6_pktsz = 148 exp_pkt = simple_icmpv6_packet( eth_dst=vm_mac, # mac address of vm1 eth_src=dst_mac, # mac address of dut ipv6_src=str(dut_lb), ipv6_dst=str(vm_ip), ipv6_hlim=64, icmp_code=0, icmp_type=3, pktlen=v6_pktsz, ) # masked_pkt = Mask(exp_pkt) masked_pkt.set_do_not_care_scapy(scapy.IPv6, "tc") masked_pkt.set_do_not_care_scapy(scapy.IPv6, "fl") masked_pkt.set_do_not_care_scapy(scapy.IPv6, "plen") masked_pkt.set_do_not_care_scapy(scapy.ICMPv6Unknown, "cksum") masked_pkt.set_do_not_care(456, v6_pktsz * 8 - 456) # ignore icmp data return send_pkt, masked_pkt, exp_pkt255
def runIntSourceTest(self, pkt, tagged1, tagged2, instructions, with_transit=True, ignore_csum=False, switch_id=1, max_hop=5, mpls=False): if IP not in pkt: self.fail("Packet is not IP") if UDP not in pkt and TCP not in pkt: self.fail("Packet must be UDP or TCP for INT tests") proto = UDP if UDP in pkt else TCP # will use runIPv4UnicastTest dst_mac = HOST2_MAC ig_port = self.port1 eg_port = self.port2 ipv4_src = pkt[IP].src ipv4_dst = pkt[IP].dst sport = pkt[proto].sport dport = pkt[proto].dport instructions = set(instructions) ins_cnt = len(instructions) self.setup_source_port(ig_port) self.setup_source_flow(ipv4_src=ipv4_src, ipv4_dst=ipv4_dst, sport=sport, dport=dport, instructions=instructions, max_hop=max_hop) if with_transit: self.setup_transit(switch_id) if with_transit: int_metadata, masked_ins_cnt = self.get_int_metadata( instructions=instructions, switch_id=switch_id, ig_port=ig_port, eg_port=eg_port) else: int_metadata, masked_ins_cnt = "", ins_cnt exp_pkt = self.get_int_pkt(pkt=pkt, instructions=instructions, max_hop=max_hop, transit_hops=1 if with_transit else 0, hop_metadata=int_metadata) exp_pkt[Ether].src = exp_pkt[Ether].dst exp_pkt[Ether].dst = dst_mac if not mpls: exp_pkt[IP].ttl = exp_pkt[IP].ttl - 1 else: exp_pkt = pkt_add_mpls(exp_pkt, MPLS_LABEL_2, DEFAULT_MPLS_TTL) if tagged2: # VLAN if tagged1 will be added by runIPv4UnicastTest exp_pkt = pkt_add_vlan(exp_pkt, VLAN_ID_2) if with_transit or ignore_csum: mask_pkt = Mask(exp_pkt) if with_transit: offset_metadata = len(exp_pkt) - len(exp_pkt[proto].payload) \ + len(INT_L45_HEAD()) + len(INT_META_HDR()) \ + (ins_cnt - masked_ins_cnt) * 4 mask_pkt.set_do_not_care(offset_metadata * 8, masked_ins_cnt * 4 * 8) if ignore_csum: csum_offset = len(exp_pkt) - len(exp_pkt[IP].payload) \ + (6 if proto is UDP else 16) mask_pkt.set_do_not_care(csum_offset * 8, 2 * 8) exp_pkt = mask_pkt self.runIPv4UnicastTest(pkt=pkt, dst_mac=HOST2_MAC, prefix_len=32, exp_pkt=exp_pkt, bidirectional=False, tagged1=tagged1, tagged2=tagged2, mpls=mpls)
def checkMirroredFlow(self): """ @summary: Send traffic & check how many mirrored packets are received @return: count: number of mirrored packets received Note: Mellanox crafts the GRE packets with extra information: That is: 22 bytes extra information after the GRE header """ payload = self.base_pkt.copy() payload_mask = Mask(payload) if self.mirror_stage == "egress": payload['Ethernet'].src = self.router_mac payload['IP'].ttl -= 1 payload_mask.set_do_not_care_scapy(scapy.Ether, "dst") payload_mask.set_do_not_care_scapy(scapy.IP, "chksum") if self.asic_type in ["mellanox"]: import binascii payload = binascii.unhexlify("0"*44) + str(payload) # Add the padding if self.asic_type in ["barefoot"]: import binascii payload = binascii.unhexlify("0"*24) + str(payload) # Add the padding exp_pkt = testutils.simple_gre_packet( eth_src = self.router_mac, ip_src = self.session_src_ip, ip_dst = self.session_dst_ip, ip_dscp = self.session_dscp, ip_id = 0, #ip_flags = 0x10, # need to upgrade ptf version to support it ip_ttl = self.session_ttl, inner_frame = payload) if self.asic_type in ["mellanox"]: exp_pkt['GRE'].proto = 0x8949 # Mellanox specific elif self.asic_type in ["barefoot"]: exp_pkt['GRE'].proto = 0x22eb # Barefoot specific else: exp_pkt['GRE'].proto = 0x88be masked_exp_pkt = Mask(exp_pkt) masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "dst") masked_exp_pkt.set_do_not_care_scapy(scapy.IP, "flags") masked_exp_pkt.set_do_not_care_scapy(scapy.IP, "chksum") masked_exp_pkt.set_do_not_care(38*8, len(payload)*8) # don't match payload, payload will be matched by match_payload(pkt) if self.check_ttl == 'False': masked_exp_pkt.set_do_not_care_scapy(scapy.IP, "ttl") def match_payload(pkt): if self.asic_type in ["mellanox"]: pkt = scapy.Ether(pkt).load pkt = pkt[22:] # Mask the Mellanox specific inner header pkt = scapy.Ether(pkt) else: pkt = scapy.Ether(pkt)[scapy.GRE].payload return dataplane.match_exp_pkt(payload_mask, pkt) # send some amount to absorb CBS capacity testutils.send_packet(self, self.src_port, str(self.base_pkt), count=self.NUM_OF_TOTAL_PACKETS) self.dataplane.flush() end_time = datetime.datetime.now() + datetime.timedelta(seconds=self.send_time) tx_pkts = 0 while datetime.datetime.now() < end_time: testutils.send_packet(self, self.src_port, str(self.base_pkt)) tx_pkts += 1 rx_pkts = 0 while True: (rcv_device, rcv_port, rcv_pkt, pkt_time) = testutils.dp_poll(self, timeout=0.1, exp_pkt=masked_exp_pkt) if rcv_pkt is not None and match_payload(rcv_pkt): rx_pkts += 1 else: break # No more packets available tx_pps = tx_pkts / self.send_time rx_pps = rx_pkts / self.send_time logger.info("Sent {} packets".format(tx_pkts)) logger.info("Received {} mirrored packets after rate limiting".format(rx_pkts)) logger.info("TX PPS {}".format(tx_pps)) logger.info("RX PPS {}".format(rx_pps)) return rx_pkts, tx_pps, rx_pps
def test_mask__set_do_not_care(self): expected_packet = "\x01\x02\x03\x04\x05\x06" packet = "\x01\x00\x00\x04\x05\x06\x07\x08" mask = Mask(expected_packet.encode(), ignore_extra_bytes=True) mask.set_do_not_care(8, 16) assert mask.pkt_match(packet.encode())