static void test_packet_output_gre_no_nexthop(void) { odp_packet_t pkt = ODP_PACKET_INVALID; odp_event_t ev; int res; if (create_odp_packet_ip4(&pkt, test_frame, sizeof(test_frame), tun_p2p + 1)) { CU_FAIL("Fail to create packet"); return; } /* * Packet's destination is GRE tunnel's p2p address, no next hop * is found for tunnel destination address, packet is dropped. */ res = ofp_ip_output(pkt, NULL); CU_ASSERT_EQUAL(res, OFP_PKT_DROP); res = ofp_send_pending_pkt(); CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED); ev = odp_queue_deq(dev->outq_def); CU_ASSERT_EQUAL_FATAL(ev, ODP_EVENT_INVALID); }
/* * Tests */ static void test_packet_output_gre(void) { odp_packet_t pkt = ODP_PACKET_INVALID; odp_event_t ev; int res; struct ofp_ether_header *eth; struct ofp_ip *ip; struct ofp_ip *ip_orig; struct ofp_greip *greip; if (create_odp_packet_ip4(&pkt, test_frame, sizeof(test_frame), tun_p2p)) { CU_FAIL("Fail to create packet"); return; } /* * Packet's destination is GRE tunnel's p2p address, next hop is GRE * interface. GRE+IP header is prepended. Packet's new destination is * link local. Packet is put into output queue. */ res = ofp_ip_output(pkt, NULL); CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED); res = ofp_send_pending_pkt(); CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED); ev = odp_queue_deq(dev->outq_def); CU_ASSERT_NOT_EQUAL_FATAL(ev, ODP_EVENT_INVALID); pkt = odp_packet_from_event(ev); CU_ASSERT_EQUAL_FATAL(odp_packet_len(pkt), sizeof(test_frame) + 20 + 4); eth = odp_packet_l2_ptr(pkt, NULL); if (memcmp(eth->ether_dhost, tun_rem_mac, OFP_ETHER_ADDR_LEN)) CU_FAIL("Bad destination mac address."); if (memcmp(eth->ether_shost, dev->mac, OFP_ETHER_ADDR_LEN)) CU_FAIL("Bad source mac address."); ip = odp_packet_l3_ptr(pkt, NULL); CU_ASSERT_EQUAL(ip->ip_src.s_addr, dev_ip); CU_ASSERT_EQUAL(ip->ip_dst.s_addr, tun_rem_ip); CU_ASSERT_EQUAL(ip->ip_p, OFP_IPPROTO_GRE); greip = (struct ofp_greip *)ip; CU_ASSERT_EQUAL(greip->gi_g.flags, 0); CU_ASSERT_EQUAL(greip->gi_g.ptype, odp_cpu_to_be_16(OFP_ETHERTYPE_IP)); /* inner ip */ ip = (struct ofp_ip *)(greip + 1); ip_orig = (struct ofp_ip *)(&orig_pkt_data[OFP_ETHER_HDR_LEN]); if (memcmp(ip, ip_orig, odp_be_to_cpu_16(ip_orig->ip_len))) CU_FAIL("Inner IP packet error."); }
static void test_hook_out_ipv4(void) { int res; odp_packet_t pkt = ODP_PACKET_INVALID; if (create_odp_packet_ip4(&pkt, test_frame, sizeof(test_frame), tun_p2p)) { CU_FAIL("Fail to create packet"); return; } my_test_val = TEST_HOOK_OUT_IPv4; res = ofp_ip_output(pkt, NULL); my_test_val = 0; CU_ASSERT_EQUAL(res, TEST_HOOK_OUT_IPv4_VALUE); CU_PASS("test_hook_out_ipv4"); }
static void test_packet_size_is_less_then_mtu(void) { odp_packet_t pkt_orig, pkt_sent; odp_event_t ev; int res; struct ofp_ether_header *eth; if (create_odp_packet_ip4(&pkt_orig, pkt1_frag1, sizeof(pkt1_frag1), 0)) { CU_FAIL("Fail to create packet"); return; } res = ofp_ip_output(pkt_orig, &nexthop); CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED); res = ofp_send_pending_pkt(); CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED); ev = odp_queue_deq(dev->outq_def); CU_ASSERT_NOT_EQUAL_FATAL(ev, ODP_EVENT_INVALID); CU_ASSERT_EQUAL_FATAL(odp_queue_deq(dev->outq_def), ODP_EVENT_INVALID); pkt_sent = odp_packet_from_event(ev); CU_ASSERT_EQUAL_FATAL(odp_packet_len(pkt_sent), sizeof(pkt1_frag1)); eth = odp_packet_l2_ptr(pkt_sent, NULL); if (memcmp(eth->ether_dhost, dst_mac, OFP_ETHER_ADDR_LEN)) CU_FAIL("Bad destination mac address."); if (memcmp(eth->ether_shost, dev->mac, OFP_ETHER_ADDR_LEN)) CU_FAIL("Bad source mac address."); if (memcmp(odp_packet_l3_ptr(pkt_sent, NULL), &orig_pkt_data[OFP_ETHER_HDR_LEN], sizeof(pkt1_frag1) - OFP_ETHER_HDR_LEN)) CU_FAIL("corrupt l3 + data forwarded"); CU_PASS("Correct packet"); odp_packet_free(pkt_sent); }
static void test_dont_fragment_set_pkt_dropped(void) { odp_packet_t pkt; odp_event_t ev; int res; struct ofp_ip *ip; if (create_odp_packet_ip4(&pkt, pkt1_full, sizeof(pkt1_full), 0)) { CU_FAIL("Fail to create packet"); return; } ip = odp_packet_l3_ptr(pkt, NULL); ip->ip_off |= odp_cpu_to_be_16(OFP_IP_DF); res = ofp_ip_output(pkt, &nexthop); CU_ASSERT_EQUAL(res, OFP_PKT_DROP); ev = odp_queue_deq(dev->outq_def); CU_ASSERT_EQUAL(ev, ODP_EVENT_INVALID); odp_packet_free(pkt); }
static void test_fragment_fragmented_to_two(void) { odp_packet_t pkt_orig, pkt_sent; odp_event_t ev; int res; struct ofp_ether_header *eth; struct ofp_ip *ip; struct ofp_ip *ip_orig; uint16_t pl_pos, pl_len, orig_pl_len, pktlen, start_offset; dev->if_mtu = 620; if (create_odp_packet_ip4(&pkt_orig, pkt1_frag2, sizeof(pkt1_frag2), 0)) { CU_FAIL("Fail to create packet"); return; } res = ofp_ip_output(pkt_orig, &nexthop); CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED); res = ofp_send_pending_pkt(); CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED); /* ASSERT 1st fragment */ ev = odp_queue_deq(dev->outq_def); CU_ASSERT_NOT_EQUAL_FATAL(ev, ODP_EVENT_INVALID); pkt_sent = odp_packet_from_event(ev); CU_ASSERT_EQUAL_FATAL(odp_packet_len(pkt_sent), dev->if_mtu + OFP_ETHER_HDR_LEN); eth = odp_packet_l2_ptr(pkt_sent, NULL); if (memcmp(eth->ether_dhost, dst_mac, OFP_ETHER_ADDR_LEN)) CU_FAIL("Bad destination mac address."); if (memcmp(eth->ether_shost, dev->mac, OFP_ETHER_ADDR_LEN)) CU_FAIL("Bad source mac address."); ip = odp_packet_l3_ptr(pkt_sent, NULL); ip_orig = (struct ofp_ip *)(&orig_pkt_data[OFP_ETHER_HDR_LEN]); orig_pl_len = odp_be_to_cpu_16(ip_orig->ip_len) - (ip_orig->ip_hl<<2); start_offset = odp_be_to_cpu_16(ip_orig->ip_off) & OFP_IP_OFFMASK; assert_ip_header(ip, ip_orig, dev->if_mtu, 1, start_offset); pl_len = dev->if_mtu - (ip->ip_hl<<2); if (memcmp((uint8_t *)ip + (ip->ip_hl<<2), (uint8_t *)ip_orig + (ip_orig->ip_hl<<2), pl_len)) CU_FAIL("corrupt l3 + data forwarded"); pl_pos = pl_len; CU_PASS("Correct packet"); odp_packet_free(pkt_sent); /* ASSERT 2nd fragment */ ev = odp_queue_deq(dev->outq_def); CU_ASSERT_NOT_EQUAL_FATAL(ev, ODP_EVENT_INVALID); pkt_sent = odp_packet_from_event(ev); pl_len = orig_pl_len - pl_pos; pktlen = pl_len + OFP_ETHER_HDR_LEN + sizeof(struct ofp_ip); CU_ASSERT_EQUAL_FATAL(odp_packet_len(pkt_sent), pktlen); eth = odp_packet_l2_ptr(pkt_sent, NULL); if (memcmp(eth->ether_dhost, dst_mac, OFP_ETHER_ADDR_LEN)) CU_FAIL("Bad destination mac address."); if (memcmp(eth->ether_shost, dev->mac, OFP_ETHER_ADDR_LEN)) CU_FAIL("Bad source mac address."); ip = odp_packet_l3_ptr(pkt_sent, NULL); assert_ip_header(ip, ip_orig, pl_len + sizeof(struct ofp_ip), 0, start_offset + pl_pos/8); if (memcmp((uint8_t *)ip + (ip->ip_hl<<2), (uint8_t *)ip_orig + (ip_orig->ip_hl<<2) + pl_pos, pl_len)) CU_FAIL("corrupt l3 + data forwarded"); CU_PASS("Correct packet"); odp_packet_free(pkt_sent); /* no more fragments */ ev = odp_queue_deq(dev->outq_def); CU_ASSERT_EQUAL(ev, ODP_EVENT_INVALID); dev->if_mtu = def_mtu; }
enum ofp_return_code ofp_ipv4_processing(odp_packet_t pkt) { int res; int protocol = IS_IPV4; uint32_t flags; struct ofp_ip *ip; struct ofp_nh_entry *nh; struct ofp_ifnet *dev = odp_packet_user_ptr(pkt); uint32_t is_ours; ip = (struct ofp_ip *)odp_packet_l3_ptr(pkt, NULL); if (odp_unlikely(ip == NULL)) { OFP_DBG("ip is NULL"); return OFP_PKT_DROP; } if (odp_unlikely(!PHYS_PORT(dev->port))) { switch (dev->port) { case GRE_PORTS: /* Doesn't happen. */ break; case VXLAN_PORTS: { /* Look for the correct device. */ struct vxlan_user_data *saved = odp_packet_user_area(pkt); dev = ofp_get_ifnet(VXLAN_PORTS, saved->vni); if (!dev) return OFP_PKT_DROP; break; } } } #ifndef OFP_PERFORMANCE if (odp_unlikely(ip->ip_v != 4)) return OFP_PKT_DROP; if (odp_unlikely(ofp_cksum_buffer((uint16_t *) ip, ip->ip_hl<<2))) return OFP_PKT_DROP; /* TODO: handle broadcast */ if (dev->bcast_addr == ip->ip_dst.s_addr) return OFP_PKT_DROP; #endif OFP_DBG("Device IP: %s, Packet Dest IP: %s", ofp_print_ip_addr(dev->ip_addr), ofp_print_ip_addr(ip->ip_dst.s_addr)); is_ours = dev->ip_addr == ip->ip_dst.s_addr || OFP_IN_MULTICAST(odp_be_to_cpu_32(ip->ip_dst.s_addr)); if (!is_ours) { /* This may be for some other local interface. */ nh = ofp_get_next_hop(dev->vrf, ip->ip_dst.s_addr, &flags); if (nh) is_ours = nh->flags & OFP_RTF_LOCAL; } if (is_ours) { if (odp_be_to_cpu_16(ip->ip_off) & 0x3fff) { OFP_UPDATE_PACKET_STAT(rx_ip_frag, 1); pkt = ofp_ip_reass(pkt); if (pkt == ODP_PACKET_INVALID) return OFP_PKT_ON_HOLD; OFP_UPDATE_PACKET_STAT(rx_ip_reass, 1); ip = (struct ofp_ip *)odp_packet_l3_ptr(pkt, NULL); } OFP_HOOK(OFP_HOOK_LOCAL, pkt, &protocol, &res); if (res != OFP_PKT_CONTINUE) { OFP_DBG("OFP_HOOK_LOCAL returned %d", res); return res; } OFP_HOOK(OFP_HOOK_LOCAL_IPv4, pkt, NULL, &res); if (res != OFP_PKT_CONTINUE) { OFP_DBG("OFP_HOOK_LOCAL_IPv4 returned %d", res); return res; } return ipv4_transport_classifier(pkt, ip->ip_p); } OFP_HOOK(OFP_HOOK_FWD_IPv4, pkt, nh, &res); if (res != OFP_PKT_CONTINUE) { OFP_DBG("OFP_HOOK_FWD_IPv4 returned %d", res); return res; } if (nh == NULL) { OFP_DBG("nh is NULL, vrf=%d dest=%x", dev->vrf, ip->ip_dst.s_addr); return OFP_PKT_CONTINUE; } if (ip->ip_ttl <= 1) { OFP_DBG("OFP_ICMP_TIMXCEED"); ofp_icmp_error(pkt, OFP_ICMP_TIMXCEED, OFP_ICMP_TIMXCEED_INTRANS, 0, 0); return OFP_PKT_DROP; } /* * Decrement TTL and incrementally change the IP header checksum. */ ip->ip_ttl--; uint16_t a = ~odp_cpu_to_be_16(1 << 8); if (ip->ip_sum >= a) ip->ip_sum -= a; else ip->ip_sum += odp_cpu_to_be_16(1 << 8); #ifdef OFP_SEND_ICMP_REDIRECT /* 1. The interface on which the packet comes into the router is the * same interface on which the packet gets routed out. * 2. The subnet or network of the source IP address is on the same * subnet or network of the next-hop IP address of the routed packet. * 3. Stack configured to send redirects. */ #define INET_SUBNET_PREFIX(addr) \ (odp_be_to_cpu_32(addr) & ((~0) << (32 - dev->masklen))) if (nh->port == dev->port && (INET_SUBNET_PREFIX(ip->ip_src.s_addr) == INET_SUBNET_PREFIX(nh->gw))) { OFP_DBG("send OFP_ICMP_REDIRECT"); ofp_icmp_error(pkt, OFP_ICMP_REDIRECT, OFP_ICMP_REDIRECT_HOST, nh->gw, 0); } #endif return ofp_ip_output(pkt, nh); }