enum ofp_return_code ofp_ipv6_processing(odp_packet_t *pkt) { int res; int protocol = IS_IPV6; uint32_t flags; struct ofp_ip6_hdr *ipv6; struct ofp_nh6_entry *nh; struct ofp_ifnet *dev = odp_packet_user_ptr(*pkt); int is_ours = 0; ipv6 = (struct ofp_ip6_hdr *)odp_packet_l3_ptr(*pkt, NULL); if (odp_unlikely(ipv6 == NULL)) return OFP_PKT_DROP; /* is ipv6->dst_addr one of my IPv6 addresses from this interface*/ if (ofp_ip6_equal(dev->ip6_addr, ipv6->ip6_dst.ofp_s6_addr) || OFP_IN6_IS_SOLICITED_NODE_MC(ipv6->ip6_dst, dev->ip6_addr) || (memcmp((const void *)((uintptr_t)dev->link_local + 8), (const void *)((uintptr_t)ipv6->ip6_dst.ofp_s6_addr + 8), 2 * sizeof(uint32_t)) == 0)) { is_ours = 1; } /* check if it's ours for another ipv6 address */ if (!is_ours) { nh = ofp_get_next_hop6(dev->vrf, ipv6->ip6_dst.ofp_s6_addr, &flags); if (nh && (nh->flags & OFP_RTF_LOCAL)) is_ours = 1; } if (is_ours) { 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_IPv6, *pkt, NULL, &res); if (res != OFP_PKT_CONTINUE) { OFP_DBG("OFP_HOOK_LOCAL_IPv6 returned %d", res); return res; } return ipv6_transport_classifier(pkt, ipv6->ofp_ip6_nxt); } OFP_HOOK(OFP_HOOK_FWD_IPv6, *pkt, NULL, &res); if (res != OFP_PKT_CONTINUE) { OFP_DBG("OFP_HOOK_FWD_IPv6 returned %d", res); return res; } nh = ofp_get_next_hop6(dev->vrf, ipv6->ip6_dst.ofp_s6_addr, &flags); if (nh == NULL) return OFP_PKT_CONTINUE; return ofp_ip6_output(*pkt, nh); }
void ofp_nd6_ns_input(odp_packet_t m, int off, int icmp6len) { struct ofp_ether_header *eth; struct ofp_ip6_hdr *ip6; struct ofp_icmp6_hdr *icmp6; struct ofp_ifnet *ifp; (void)icmp6len; ifp = odp_packet_user_ptr(m); eth = (struct ofp_ether_header *) odp_packet_l2_ptr(m, NULL); ip6 = (struct ofp_ip6_hdr *)odp_packet_l3_ptr(m, NULL); icmp6 = (struct ofp_icmp6_hdr *)((uint8_t *)ip6 + off); if (icmp6->ofp_icmp6_data8[20] == OFP_ND_OPT_SOURCE_LINKADDR && !OFP_IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && !OFP_IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) { ofp_set_route6_params(OFP_ROUTE6_ADD, 0 /*vrf*/, ifp->vlan, ifp->port, ip6->ip6_src.ofp_s6_addr, 128 /*masklen*/, ofp_in6addr_any.ofp_s6_addr, OFP_RTF_HOST); ofp_add_mac6(ifp, &ip6->ip6_src.ofp_s6_addr[0], (uint8_t *)ð->ether_shost); } }
void ofp_nd6_na_input(odp_packet_t m, int off, int icmp6len) { struct ofp_ether_header *eth; struct ofp_ip6_hdr *ip6; struct ofp_icmp6_hdr *icmp6; struct ofp_ifnet *ifp; (void)icmp6len; ifp = odp_packet_user_ptr(m); eth = (struct ofp_ether_header *) odp_packet_l2_ptr(m, NULL); ip6 = (struct ofp_ip6_hdr *)odp_packet_l3_ptr(m, NULL); icmp6 = (struct ofp_icmp6_hdr *)((uint8_t *)ip6 + off); if (icmp6->ofp_icmp6_data8[20] == OFP_ND_OPT_TARGET_LINKADDR) { ofp_set_route6_params(OFP_ROUTE6_ADD, 0 /*vrf*/, ifp->vlan, ifp->port, &icmp6->ofp_icmp6_data8[4], 128 /*masklen*/, ofp_in6addr_any.ofp_s6_addr, OFP_RTF_HOST); ofp_add_mac6(ifp, &icmp6->ofp_icmp6_data8[4], (uint8_t *)ð->ether_shost); } }
enum ofp_return_code ofp_eth_vlan_processing(odp_packet_t *pkt) { uint16_t vlan = 0, ethtype; struct ofp_ether_header *eth; struct ofp_ifnet *ifnet = odp_packet_user_ptr(*pkt); eth = (struct ofp_ether_header *)odp_packet_l2_ptr(*pkt, NULL); if (odp_unlikely(eth == NULL)) { OFP_DBG("eth is NULL"); return OFP_PKT_DROP; } ethtype = odp_be_to_cpu_16(eth->ether_type); if (ethtype == OFP_ETHERTYPE_VLAN) { struct ofp_ether_vlan_header *vlan_hdr; vlan_hdr = (struct ofp_ether_vlan_header *)eth; vlan = OFP_EVL_VLANOFTAG(odp_be_to_cpu_16(vlan_hdr->evl_tag)); ethtype = odp_be_to_cpu_16(vlan_hdr->evl_proto); ifnet = ofp_get_ifnet(ifnet->port, vlan); if (!ifnet) return OFP_PKT_DROP; if (odp_likely(ofp_if_type(ifnet) != OFP_IFT_VXLAN)) odp_packet_user_ptr_set(*pkt, ifnet); } OFP_DBG("ETH TYPE = %04x", ethtype); /* network layer classifier */ switch (ethtype) { /* STUB: except for ARP, just terminate all traffic to slowpath. * FIXME: test/implement other cases */ #ifdef INET case OFP_ETHERTYPE_IP: return ofp_ipv4_processing(pkt); #endif /* INET */ #ifdef INET6 case OFP_ETHERTYPE_IPV6: return ofp_ipv6_processing(pkt); #endif /* INET6 */ #if 0 case OFP_ETHERTYPE_MPLS: return OFP_PKT_DROP; #endif case OFP_ETHERTYPE_ARP: return ofp_arp_processing(pkt); default: return OFP_PKT_CONTINUE; } }
/** * Get per packet processing context from packet buffer * * @param pkt Packet * * @return pointer to context area */ static pkt_ctx_t *get_pkt_ctx_from_pkt(odp_packet_t pkt) { return (pkt_ctx_t *)odp_packet_user_ptr(pkt); }
static enum ofp_return_code ofp_fragment_pkt(odp_packet_t pkt, struct ip_out *odata) { struct ofp_ip *ip, *ip_new; int pl_len, seg_len, pl_pos, flen, hwlen; uint16_t frag, frag_new; uint8_t *payload_new; uint32_t payload_offset; odp_packet_t pkt_new; int ret = OFP_PKT_PROCESSED; ip = (struct ofp_ip *)odp_packet_l3_ptr(pkt, NULL); /* * Copy fragment IP options into a separate buffer, which is * copied into each fragment, except the first one. */ int ip_hlen = ip->ip_hl<<2; int iopts_len = ip_hlen - sizeof(struct ofp_ip); uint8_t fopts[(iopts_len+3)&0xfffc]; uint8_t *iopts = (uint8_t *)(ip + 1); int iopts_pos = 0, fopts_len = 0; while (iopts_pos < iopts_len) { int opt_len = 1; switch (OFP_IPOPT_NUMBER(iopts[iopts_pos])) { case OFP_IPOPT_EOL: case OFP_IPOPT_NOP: break; default: opt_len = iopts[iopts_pos+1]; if (opt_len > iopts_len - iopts_pos) opt_len = iopts_len - iopts_pos; if (OFP_IPOPT_COPIED(iopts[iopts_pos])) { memcpy(fopts + fopts_len, iopts + iopts_pos, opt_len); fopts_len += opt_len; } } iopts_pos += opt_len; } while (fopts_len & 3) fopts[fopts_len++] = 0; pl_len = odp_be_to_cpu_16(ip->ip_len) - ip_hlen; pl_pos = 0; frag = odp_be_to_cpu_16(ip->ip_off); payload_offset = odp_packet_l3_offset(pkt) + ip_hlen; OFP_UPDATE_PACKET_STAT(tx_eth_frag, 1); int first = 1; while (pl_pos < pl_len) { int f_ip_hl = ip->ip_hl; if (!first) f_ip_hl = (sizeof(struct ofp_ip) + fopts_len) >> 2; int f_ip_hlen = f_ip_hl<<2; seg_len = (odata->dev_out->if_mtu - f_ip_hlen) & 0xfff8; flen = (pl_len - pl_pos) > seg_len ? seg_len : (pl_len - pl_pos); hwlen = flen + f_ip_hlen; pkt_new = ofp_packet_alloc(hwlen); if (pkt_new == ODP_PACKET_INVALID) { OFP_ERR("ofp_packet_alloc failed"); return OFP_PKT_DROP; } odp_packet_user_ptr_set(pkt_new, odp_packet_user_ptr(pkt)); *ofp_packet_user_area(pkt_new) = *ofp_packet_user_area(pkt); odp_packet_l2_offset_set(pkt_new, 0); odp_packet_l3_offset_set(pkt_new, 0); ip_new = odp_packet_l3_ptr(pkt_new, NULL); *ip_new = *ip; if (first) memcpy(ip_new + 1, ip + 1, ip_hlen - sizeof(struct ofp_ip)); else memcpy(ip_new + 1, fopts, fopts_len); ip_new->ip_hl = f_ip_hl; payload_new = (uint8_t *)ip_new + f_ip_hlen; if (odp_packet_copy_to_mem(pkt, payload_offset + pl_pos, flen, payload_new) < 0) { OFP_ERR("odp_packet_copy_to_mem failed"); odp_packet_free(pkt_new); return OFP_PKT_DROP; }; ip_new->ip_len = odp_cpu_to_be_16(flen + f_ip_hlen); 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); odata->ip = ip_new; odata->insert_checksum = 1; ret = ofp_ip_output_continue(pkt_new, odata); if (ret == OFP_PKT_DROP) { odp_packet_free(pkt_new); return OFP_PKT_DROP; } first = 0; } odp_packet_free(pkt); return OFP_PKT_PROCESSED; }
enum ofp_return_code ofp_arp_processing(odp_packet_t *pkt) { struct ofp_arphdr *arp; struct ofp_ifnet *dev = odp_packet_user_ptr(*pkt); struct ofp_ifnet *outdev = dev; uint16_t vlan = dev->vlan; uint8_t inner_from_mac[OFP_ETHER_ADDR_LEN]; uint32_t is_ours; arp = (struct ofp_arphdr *)odp_packet_l3_ptr(*pkt, NULL); if (odp_unlikely(arp == NULL)) { OFP_DBG("arp is NULL"); return OFP_PKT_DROP; } /* save the received arp info */ if (odp_be_to_cpu_16(arp->op) == OFP_ARPOP_REPLY) ofp_add_mac(dev, arp->ip_src, arp->eth_src); OFP_DBG("Device IP: %s, Packet Dest IP: %s", ofp_print_ip_addr(dev->ip_addr), ofp_print_ip_addr(arp->ip_dst)); /* Check for VXLAN interface */ if (odp_unlikely(ofp_if_type(dev) == OFP_IFT_VXLAN)) { ofp_vxlan_update_devices(*pkt, arp, &vlan, &dev, &outdev, inner_from_mac); } is_ours = dev->ip_addr && dev->ip_addr == (ofp_in_addr_t)(arp->ip_dst); if (!is_ours && !global_param->arp.check_interface) { /* This may be for some other local interface. */ uint32_t flags; struct ofp_nh_entry *nh; nh = ofp_get_next_hop(dev->vrf, arp->ip_dst, &flags); if (nh) is_ours = nh->flags & OFP_RTF_LOCAL; } /* on our interface an ARP request */ if (is_ours && odp_be_to_cpu_16(arp->op) == OFP_ARPOP_REQUEST) { struct ofp_arphdr tmp; struct ofp_ether_header tmp_eth; struct ofp_ether_vlan_header tmp_eth_vlan; void *l2_addr = odp_packet_l2_ptr(*pkt, NULL); struct ofp_ether_header *eth = (struct ofp_ether_header *)l2_addr; struct ofp_ether_vlan_header *eth_vlan = (struct ofp_ether_vlan_header *)l2_addr; ofp_add_mac(dev, arp->ip_src, arp->eth_src); if (vlan) tmp_eth_vlan = *eth_vlan; else tmp_eth = *eth; OFP_DBG("Reply to ARPOP_REQ from ip %s" #ifdef SP "on IF %d" #endif " mac %s ip %s", ofp_print_ip_addr(arp->ip_src), #ifdef SP dev->linux_index, #endif ofp_print_mac(dev->mac), ofp_print_ip_addr(arp->ip_dst)); tmp = *arp; tmp.ip_dst = arp->ip_src; tmp.ip_src = arp->ip_dst; memcpy(&tmp.eth_dst, &arp->eth_src, OFP_ETHER_ADDR_LEN); memcpy(&tmp.eth_src, dev->mac, OFP_ETHER_ADDR_LEN); tmp.op = odp_cpu_to_be_16(OFP_ARPOP_REPLY); *arp = tmp; if (vlan) { memcpy(tmp_eth_vlan.evl_dhost, &arp->eth_dst, OFP_ETHER_ADDR_LEN); memcpy(tmp_eth_vlan.evl_shost, &arp->eth_src, OFP_ETHER_ADDR_LEN); *eth_vlan = tmp_eth_vlan; } else { memcpy(tmp_eth.ether_dhost, &arp->eth_dst, OFP_ETHER_ADDR_LEN); memcpy(tmp_eth.ether_shost, &arp->eth_src, OFP_ETHER_ADDR_LEN); *eth = tmp_eth; } if (odp_unlikely(ofp_if_type(dev) == OFP_IFT_VXLAN)) { /* Restore the original vxlan header and update the addresses */ ofp_vxlan_restore_and_update_header (*pkt, outdev, inner_from_mac); } return send_pkt_out(outdev, *pkt); } return OFP_PKT_CONTINUE; }
enum ofp_return_code ofp_ipv4_processing(odp_packet_t *pkt) { int frag_res = 0, 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(ofp_if_type(dev) == OFP_IFT_VXLAN)) { struct ofp_packet_user_area *ua; /* Look for the correct device. */ ua = ofp_packet_user_area(*pkt); dev = ofp_get_ifnet(VXLAN_PORTS, ua->vxlan.vni); if (!dev) return OFP_PKT_DROP; } if (odp_unlikely(ip->ip_v != OFP_IPVERSION)) return OFP_PKT_DROP; if (ofp_packet_user_area(*pkt)->chksum_flags & OFP_L3_CHKSUM_STATUS_VALID) { switch (odp_packet_l3_chksum_status(*pkt)) { case ODP_PACKET_CHKSUM_OK: break; case ODP_PACKET_CHKSUM_UNKNOWN: /* Checksum was not validated by HW */ if (odp_unlikely(ofp_cksum_iph(ip, ip->ip_hl))) return OFP_PKT_DROP; break; case ODP_PACKET_CHKSUM_BAD: return OFP_PKT_DROP; break; } ofp_packet_user_area(*pkt)->chksum_flags &= ~OFP_L3_CHKSUM_STATUS_VALID; } else if (odp_unlikely(ofp_cksum_iph(ip, ip->ip_hl))) return OFP_PKT_DROP; /* TODO: handle broadcast */ if (dev->bcast_addr == ip->ip_dst.s_addr) return OFP_PKT_DROP; 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) { frag_res = pkt_reassembly(pkt); if (frag_res != OFP_PKT_CONTINUE) return frag_res; 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_common_inline(*pkt, nh, 0); }
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); }
enum ofp_return_code ofp_arp_processing(odp_packet_t pkt) { struct ofp_arphdr *arp; struct ofp_ifnet *dev = odp_packet_user_ptr(pkt); struct ofp_ifnet *outdev = dev; uint16_t vlan = dev->vlan; uint8_t inner_from_mac[OFP_ETHER_ADDR_LEN]; arp = (struct ofp_arphdr *)odp_packet_l3_ptr(pkt, NULL); if (odp_unlikely(arp == NULL)) { OFP_DBG("arp is NULL"); return OFP_PKT_DROP; } /* save the received arp info */ if (odp_be_to_cpu_16(arp->op) == OFP_ARPOP_REPLY) ofp_add_mac(dev, arp->ip_src, arp->eth_src); OFP_DBG("Device IP: %s, Packet Dest IP: %s", ofp_print_ip_addr(dev->ip_addr), ofp_print_ip_addr(arp->ip_dst)); /* Check for VXLAN interface */ if (odp_unlikely(!PHYS_PORT(dev->port))) { switch (dev->port) { case GRE_PORTS: /* Never happens. */ break; case VXLAN_PORTS: { ofp_vxlan_update_devices(arp, &vlan, &dev, &outdev, inner_from_mac); break; } } /* switch */ } /* on our interface an ARP request */ if ((dev->ip_addr) && dev->ip_addr == (ofp_in_addr_t)(arp->ip_dst) && odp_be_to_cpu_16(arp->op) == OFP_ARPOP_REQUEST) { struct ofp_arphdr tmp; struct ofp_ether_header tmp_eth; struct ofp_ether_vlan_header tmp_eth_vlan; void *l2_addr = odp_packet_l2_ptr(pkt, NULL); struct ofp_ether_header *eth = (struct ofp_ether_header *)l2_addr; struct ofp_ether_vlan_header *eth_vlan = (struct ofp_ether_vlan_header *)l2_addr; ofp_add_mac(dev, arp->ip_src, arp->eth_src); if (vlan) tmp_eth_vlan = *eth_vlan; else tmp_eth = *eth; OFP_DBG("Reply to ARPOP_REQ from ip %s" #ifdef SP "on IF %d" #endif " mac %s ip %s", ofp_print_ip_addr(arp->ip_src), #ifdef SP dev->linux_index, #endif ofp_print_mac(dev->mac), ofp_print_ip_addr(arp->ip_dst)); tmp = *arp; tmp.ip_dst = arp->ip_src; tmp.ip_src = arp->ip_dst; memcpy(&tmp.eth_dst, &arp->eth_src, OFP_ETHER_ADDR_LEN); memcpy(&tmp.eth_src, dev->mac, OFP_ETHER_ADDR_LEN); tmp.op = odp_cpu_to_be_16(OFP_ARPOP_REPLY); *arp = tmp; if (vlan) { memcpy(tmp_eth_vlan.evl_dhost, &arp->eth_dst, OFP_ETHER_ADDR_LEN); memcpy(tmp_eth_vlan.evl_shost, &arp->eth_src, OFP_ETHER_ADDR_LEN); *eth_vlan = tmp_eth_vlan; } else { memcpy(tmp_eth.ether_dhost, &arp->eth_dst, OFP_ETHER_ADDR_LEN); memcpy(tmp_eth.ether_shost, &arp->eth_src, OFP_ETHER_ADDR_LEN); *eth = tmp_eth; } if (odp_unlikely(!PHYS_PORT(dev->port))) { switch (dev->port) { case GRE_PORTS: /* Never happens. */ break; case VXLAN_PORTS: { /* Restore the original vxlan header and update the addresses */ ofp_vxlan_restore_and_update_header (pkt, outdev, inner_from_mac); break; } } /* switch */ } /* not phys port */ return send_pkt_out(outdev, pkt); } return OFP_PKT_CONTINUE; }