static int create_odp_packet_ip4(odp_packet_t *opkt, uint8_t *pkt_data, int plen, uint32_t dst_addr) { odp_pool_t pool; uint8_t *buf; odp_packet_t pkt = ODP_PACKET_INVALID; struct ofp_ip *iphdr; memset(orig_pkt_data, 0x0, sizeof(orig_pkt_data)); pool = odp_pool_lookup("packet_pool"); if (pool == ODP_POOL_INVALID) { fail_with_odp("ODP packet_pool not found\n"); return -1; } pkt = odp_packet_alloc(pool, plen); if (pkt == ODP_PACKET_INVALID) { fail_with_odp("ODP packet alloc failed"); return -1; } buf = odp_packet_data(pkt); if (odp_packet_copy_from_mem(pkt, 0, plen, pkt_data) < 0) { fail_with_odp("Packet data copy failed\n"); return -1; }; iphdr = (struct ofp_ip *)&buf[OFP_ETHER_HDR_LEN]; /* changes to the default packet. Recalculate ip checksum */ if (dst_addr) { iphdr->ip_dst.s_addr = dst_addr; iphdr->ip_sum = 0; iphdr->ip_sum = ofp_cksum_buffer((uint16_t *)iphdr, iphdr->ip_hl<<2); } /* END OF changes to the default packet */ odp_packet_has_eth_set(pkt, 1); odp_packet_has_ipv4_set(pkt, 1); odp_packet_l2_offset_set(pkt, 0); odp_packet_l3_offset_set(pkt, OFP_ETHER_HDR_LEN); odp_packet_l4_offset_set(pkt, OFP_ETHER_HDR_LEN + (iphdr->ip_hl<<2)); *opkt = pkt; memcpy(orig_pkt_data, pkt_data, plen); return 0; }
enum ofp_return_code ofp_gre_processing(odp_packet_t pkt) { struct ofp_ip *ip = (struct ofp_ip *)odp_packet_l3_ptr(pkt, NULL); if (odp_unlikely(ofp_cksum_buffer((uint16_t *) ip, ip->ip_hl<<2))) return OFP_PKT_DROP; 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); } return ofp_inetsw[ofp_ip_protox_gre].pr_input(pkt, ip->ip_hl << 2); }
enum ofp_return_code ofp_nd6_ns_output(struct ofp_ifnet *dev, uint8_t *daddr6, uint8_t *taddr6) { size_t size = 0; size_t iter = 0; struct ofp_ether_header *e1; struct ofp_ether_vlan_header *e2; struct ofp_ip6_hdr *ip6hdr; struct ofp_icmp6_hdr *icmp; odp_packet_t pkt; if (dev->vlan) size = sizeof(struct ofp_ether_vlan_header); else size = sizeof(struct ofp_ether_header); size += sizeof(struct ofp_ip6_hdr) + sizeof(struct ofp_icmp6_hdr) + 16 /*target addr*/ + 8; /* option*/ pkt = ofp_packet_alloc(size); if (pkt == ODP_PACKET_INVALID) return OFP_PKT_DROP; odp_packet_has_eth_set(pkt, 1); odp_packet_l2_offset_set(pkt, iter); if (dev->vlan) { e2 = (struct ofp_ether_vlan_header *)odp_packet_l2_ptr(pkt, NULL); iter += sizeof(*e2); memset(e2->evl_dhost, 0xff, OFP_ETHER_ADDR_LEN); memcpy(e2->evl_shost, dev->mac, OFP_ETHER_ADDR_LEN); e2->evl_encap_proto = odp_cpu_to_be_16(OFP_ETHERTYPE_VLAN); e2->evl_tag = odp_cpu_to_be_16(dev->vlan); e2->evl_proto = odp_cpu_to_be_16(OFP_ETHERTYPE_IPV6); } else { e1 = (struct ofp_ether_header *)odp_packet_l2_ptr(pkt, NULL); iter += sizeof(*e1); memset(e1->ether_dhost, 0xff, OFP_ETHER_ADDR_LEN); memcpy(e1->ether_shost, dev->mac, OFP_ETHER_ADDR_LEN); e1->ether_type = odp_cpu_to_be_16(OFP_ETHERTYPE_IPV6); } odp_packet_l3_offset_set(pkt, iter); ip6hdr = (struct ofp_ip6_hdr *)odp_packet_l3_ptr(pkt, NULL); iter += sizeof(*ip6hdr); ip6hdr->ofp_ip6_flow = 0; ip6hdr->ofp_ip6_vfc = OFP_IPV6_VERSION; ip6hdr->ofp_ip6_plen = odp_cpu_to_be_16(32); /*sizeof(*icmp) + sizeof taddr + 8*/ /* for checksum calculation */ ip6hdr->ofp_ip6_nxt = 0; ip6hdr->ofp_ip6_hlim = OFP_IPPROTO_ICMPV6; /* XXX should be multicast address*/ memcpy(ip6hdr->ip6_src.ofp_s6_addr, dev->ip6_addr, 16); if (ofp_ip6_is_set(daddr6)) memcpy(ip6hdr->ip6_dst.ofp_s6_addr, daddr6, 16); else { /* Solicited-node multicast address */ ip6hdr->ip6_dst.ofp_s6_addr16[0] = OFP_IPV6_ADDR_INT16_MLL; ip6hdr->ip6_dst.ofp_s6_addr16[1] = 0; ip6hdr->ip6_dst.ofp_s6_addr32[1] = 0; ip6hdr->ip6_dst.ofp_s6_addr32[2] = OFP_IPV6_ADDR_INT32_ONE; ip6hdr->ip6_dst.ofp_s6_addr32[3] = *((uint32_t *)taddr6 + 3); ip6hdr->ip6_dst.ofp_s6_addr[12] = 0xff; } odp_packet_l4_offset_set(pkt, iter); icmp = (struct ofp_icmp6_hdr *)odp_packet_l4_ptr(pkt, NULL); iter += sizeof(*icmp) + 8 /* option */; icmp->icmp6_type = OFP_ND_NEIGHBOR_SOLICIT; icmp->icmp6_code = 0; icmp->icmp6_cksum = 0; icmp->ofp_icmp6_data32[0] = 0; /* Reserved */ memcpy(&icmp->ofp_icmp6_data8[4], taddr6, 16); /* Option: Source link-layer address */ icmp->ofp_icmp6_data8[20] = OFP_ND_OPT_SOURCE_LINKADDR; icmp->ofp_icmp6_data8[21] = 1; /* 8 octets */ memcpy(&icmp->ofp_icmp6_data8[22], dev->mac, 6); icmp->icmp6_cksum = ofp_cksum_buffer(&ip6hdr->ofp_ip6_plen, 68); ip6hdr->ofp_ip6_nxt = OFP_IPPROTO_ICMPV6; ip6hdr->ofp_ip6_hlim = 255; if (send_pkt_out(dev, pkt) == OFP_PKT_DROP) { OFP_ERR("Drop packet"); odp_packet_free(pkt); return OFP_PKT_DROP; } return OFP_PKT_PROCESSED; }
static enum ofp_return_code ofp_fragment_pkt(odp_packet_t pkt, struct ofp_ifnet *dev_out, uint8_t is_local_address) { struct ofp_ip *ip, *ip_new; uint16_t vlan = dev_out->vlan; int tot_len, pl_len, seg_len, pl_pos, flen, hwlen; uint16_t frag, frag_new; uint8_t *payload_new; uint32_t payload_offset; odp_pool_t pkt_pool; odp_packet_t pkt_new; struct ofp_ether_header *eth, *eth_new; struct ofp_ether_vlan_header *eth_vlan, *eth_new_vlan; int ret = OFP_PKT_PROCESSED; if (!vlan) eth = odp_packet_l2_ptr(pkt, NULL); else eth_vlan = odp_packet_l2_ptr(pkt, NULL); ip = (struct ofp_ip *)odp_packet_l3_ptr(pkt, NULL); pkt_pool = ofp_packet_pool; tot_len = odp_be_to_cpu_16(ip->ip_len); pl_len = tot_len - (ip->ip_hl<<2); seg_len = (dev_out->if_mtu - sizeof(struct ofp_ip)) & 0xfff8; pl_pos = 0; frag = odp_be_to_cpu_16(ip->ip_off); payload_offset = odp_packet_l3_offset(pkt) + (ip->ip_hl<<2); OFP_UPDATE_PACKET_STAT(tx_eth_frag, 1); while (pl_pos < pl_len) { flen = (pl_len - pl_pos) > seg_len ? seg_len : (pl_len - pl_pos); hwlen = flen + sizeof(struct ofp_ip) + (vlan ? sizeof(struct ofp_ether_vlan_header) : sizeof(struct ofp_ether_header)); pkt_new = odp_packet_alloc(pkt_pool, hwlen); if (pkt_new == ODP_PACKET_INVALID) { OFP_ERR("odp_packet_alloc failed"); return OFP_PKT_DROP; } odp_packet_user_ptr_set(pkt_new, odp_packet_user_ptr(pkt)); odp_packet_l2_offset_set(pkt_new, 0); if (vlan) { eth_new_vlan = odp_packet_l2_ptr(pkt_new, NULL); *eth_new_vlan = *eth_vlan; ip_new = (struct ofp_ip *)(eth_new_vlan + 1); odp_packet_l3_offset_set(pkt_new, OFP_ETHER_HDR_LEN + OFP_ETHER_VLAN_ENCAP_LEN); } else { eth_new = odp_packet_l2_ptr(pkt_new, NULL); *eth_new = *eth; ip_new = (struct ofp_ip *)(eth_new + 1); odp_packet_l3_offset_set(pkt_new, OFP_ETHER_HDR_LEN); } *ip_new = *ip; payload_new = (uint8_t *)(ip_new + 1); if (odp_packet_copydata_out(pkt, payload_offset + pl_pos, flen, payload_new) < 0) { OFP_ERR("odp_packet_copydata_out failed"); return OFP_PKT_DROP; }; ip_new->ip_len = odp_cpu_to_be_16(flen + sizeof(*ip_new)); frag_new = frag + pl_pos/8; pl_pos += flen; if (pl_pos < pl_len) frag_new |= OFP_IP_MF; ip_new->ip_off = odp_cpu_to_be_16(frag_new); ip_new->ip_sum = 0; ip_new->ip_sum = ofp_cksum_buffer((uint16_t *)ip_new, sizeof(*ip_new)); if (is_local_address) ret = send_pkt_loop(dev_out, pkt_new); else ret = send_pkt_out(dev_out, pkt_new); if (ret == OFP_PKT_DROP) { odp_packet_free(pkt_new); return OFP_PKT_DROP; } } odp_packet_free(pkt); return OFP_PKT_PROCESSED; }
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); }
static void test_ofp_packet_input_forwarding_to_output(void) { odp_packet_t pkt; odp_event_t ev; int res; /* Call ofp_packet_input using a pkt with destination ip * that does NOT match the local ip on ifnet and a route is found. * ARP is found for gateway IP. * Function returns OFP_PKT_PROCESSED and * packet is forwarded to ofp_ip_output.*/ unsigned char ll_addr[13] = "123456789012"; my_test_val = TEST_FORWARD_HOOK; CU_ASSERT_EQUAL( ofp_ipv4_lookup_mac(dst_ipaddr + 1, ll_addr, ifnet), -1); CU_ASSERT_EQUAL( ofp_arp_ipv4_insert(dst_ipaddr + 1, ll_addr, ifnet), 0); if (create_odp_packet_ip4(&pkt, test_frame, sizeof(test_frame), dst_ipaddr, 0)) { CU_FAIL("Fail to create packet"); return; } res = ofp_packet_input(pkt, interface_queue[port], ofp_eth_vlan_processing); CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED); CU_ASSERT_NOT_EQUAL(ev = odp_queue_deq(ifnet->outq_def), ODP_EVENT_INVALID); CU_ASSERT_EQUAL(odp_queue_deq(ifnet->outq_def), ODP_EVENT_INVALID); #ifdef SP CU_ASSERT_EQUAL(odp_queue_deq(ifnet->spq_def), ODP_EVENT_INVALID); #endif /* SP */ CU_ASSERT_EQUAL(odp_packet_len(pkt), sizeof(test_frame)); pkt = odp_packet_from_event(ev); struct ofp_ip *ip_in_pkt_data = (struct ofp_ip *)(in_pkt_data + OFP_ETHER_HDR_LEN); ip_in_pkt_data->ip_ttl--; #ifdef OFP_PERFORMANCE /*checksum is not filled on ip_output*/ ip_in_pkt_data->ip_sum = ((struct ofp_ip *)odp_packet_l3_ptr(pkt, NULL))->ip_sum; #else ip_in_pkt_data->ip_sum = 0; ip_in_pkt_data->ip_sum = ofp_cksum_buffer((uint16_t *)ip_in_pkt_data, ip_in_pkt_data->ip_hl<<2); #endif if (memcmp((uint8_t *)odp_packet_data(pkt) + odp_packet_l3_offset(pkt), in_pkt_data + OFP_ETHER_HDR_LEN, sizeof(test_frame) - OFP_ETHER_HDR_LEN)) CU_FAIL("corrupt l3 + data forwarded"); struct ofp_ether_header *eth = (struct ofp_ether_header *)odp_packet_l2_ptr(pkt, NULL); if (memcmp(eth->ether_dhost, ll_addr, OFP_ETHER_ADDR_LEN)) CU_FAIL("Bad destination mac address on the forwarded packet"); CU_ASSERT_EQUAL(eth->ether_type, odp_cpu_to_be_16(OFP_ETHERTYPE_IP)); CU_PASS("ofp_packet_input_forwarding_to_output"); }