Exemple #1
0
static void
test_sinlge_port_basic(void)
{
	int port = 0;
	uint16_t vlan = 0;
	uint16_t vrf = 1;
	uint32_t ifaddr = 0x650AA8C0; /* C0.A8.0A.65 = 192.168.10.101 */
	int masklen = 24;
	uint32_t bcast = ifaddr | odp_cpu_to_be_32(0xFF);
	struct ofp_ifnet *dev;
	struct ofp_nh_entry *nh;
	const char *res;

	res = ofp_config_interface_up_v4(port, vlan, vrf, ifaddr, masklen);
	CU_ASSERT_PTR_NULL_FATAL(res);

	dev = ofp_get_ifnet(port, vlan);
	assert_dev(dev, port, vlan, vrf, ifaddr, ifmtu, masklen, bcast,
		   link_local);
	nh = ofp_get_next_hop(vrf, ifaddr, NULL);
	assert_next_hop(nh, 0, port, vlan);


	res = ofp_config_interface_down(port, vlan);
	CU_ASSERT_PTR_NULL_FATAL(res);

	dev = ofp_get_ifnet(port, vlan);
	assert_dev(dev, port, vlan, vrf, 0, ifmtu, masklen, bcast, link_local);
	nh = ofp_get_next_hop(vrf, ifaddr, NULL);
	CU_ASSERT_PTR_NULL(nh);
}
Exemple #2
0
static void
test_two_ports_vlan(void)
{
	int port = 0;
	uint16_t vlan = 0, vlan1 = 100;
	uint16_t vrf = 1, vrf1 = 2;
	uint32_t ifaddr = 0x650AA8C0; /* C0.A8.0A.65 = 192.168.10.101 */
	uint32_t ifaddr1 = 0x650AA8C1;
	int masklen = 24, masklen1 = 20;
	uint32_t bcast = ifaddr | odp_cpu_to_be_32(0xFF);
	uint32_t bcast1 = ifaddr1 | odp_cpu_to_be_32(0xFFF);
	struct ofp_ifnet *dev;
	struct ofp_nh_entry *nh;
	const char *res;

	res = ofp_config_interface_up_v4(port, vlan, vrf, ifaddr, masklen);
	CU_ASSERT_PTR_NULL_FATAL(res);
	res = ofp_config_interface_up_v4(port, vlan1, vrf1, ifaddr1, masklen1);
	CU_ASSERT_PTR_NULL_FATAL(res);

	dev = ofp_get_ifnet(port, vlan);
	CU_ASSERT_PTR_NOT_NULL_FATAL(dev);
	assert_dev(dev, port, vlan, vrf, ifaddr, ifmtu, masklen, bcast,
		   link_local);
	nh = ofp_get_next_hop(vrf, ifaddr, NULL);
	assert_next_hop(nh, 0, port, vlan);

	dev = ofp_get_ifnet(port, vlan1);
	assert_dev(dev, port, vlan1, vrf1, ifaddr1, ifmtu, masklen1, bcast1,
		   link_local);
	nh = ofp_get_next_hop(vrf1, ifaddr1, NULL);
	assert_next_hop(nh, 0, port, vlan1);

	res = ofp_config_interface_down(port, vlan);
	CU_ASSERT_PTR_NULL_FATAL(res);
	res = ofp_config_interface_down(port, vlan1);
	CU_ASSERT_PTR_NULL_FATAL(res);

	dev = ofp_get_ifnet(port, vlan);
	assert_dev(dev, port, vlan, vrf, 0, ifmtu, masklen, bcast, link_local);
	nh = ofp_get_next_hop(vrf, ifaddr, NULL);
	CU_ASSERT_PTR_NULL(nh);

	dev = ofp_get_ifnet(port, vlan1);
	CU_ASSERT_PTR_NULL_FATAL(dev);
	nh = ofp_get_next_hop(vrf1, ifaddr1, NULL);
	CU_ASSERT_PTR_NULL(nh);
}
Exemple #3
0
static enum ofp_return_code ofp_output_ipv4_to_gre(
	odp_packet_t pkt, struct ofp_ifnet *dev_gre,
	uint16_t vrfid,	struct ofp_nh_entry **nh_new)
{
	struct ofp_ip	*ip;
	struct ofp_greip *greip;
	uint32_t flags;
	uint8_t	l2_size = 0;
	int32_t	offset;

	*nh_new = ofp_get_next_hop(vrfid, dev_gre->ip_remote, &flags);

	if (*nh_new == NULL)
		return OFP_PKT_DROP;

	ip = odp_packet_l3_ptr(pkt, NULL);

	/* Remove eth header, prepend gre + ip */
	if (odp_packet_has_l2(pkt))
		l2_size = odp_packet_l3_offset(pkt) - odp_packet_l2_offset(pkt);

	offset = sizeof(*greip) - l2_size;
	if (offset >= 0)
		greip = odp_packet_push_head(pkt, offset);
	else
		greip = odp_packet_pull_head(pkt, -offset);

	odp_packet_has_l2_set(pkt, 0);
	odp_packet_l3_offset_set(pkt, 0);

	if (!greip)
		return OFP_PKT_DROP;

	greip->gi_flags = 0;
	greip->gi_ptype = odp_cpu_to_be_16(OFP_GREPROTO_IP);

	greip->gi_i.ip_hl = 5;
	greip->gi_i.ip_v = 4;
	greip->gi_i.ip_tos = ip->ip_tos;
	greip->gi_i.ip_len =
		odp_cpu_to_be_16(odp_be_to_cpu_16(ip->ip_len) +
				 sizeof(*greip));
	greip->gi_i.ip_id = ip->ip_id;
	greip->gi_i.ip_off = 0;
	greip->gi_i.ip_ttl = ip->ip_ttl;
	greip->gi_i.ip_p = OFP_IPPROTO_GRE;
	greip->gi_i.ip_sum = 0;
	greip->gi_i.ip_src.s_addr = dev_gre->ip_local;
	greip->gi_i.ip_dst.s_addr = dev_gre->ip_remote;

	return OFP_PKT_CONTINUE;
}
Exemple #4
0
static void
test_gre_port(void)
{
	int port = 0;
	uint16_t vlan = 10;
	uint16_t vrf = 1;
	uint32_t ifaddr = 0x650AA8C0; /* C0.A8.0A.65 = 192.168.10.101 */
	int masklen = 24, gre_ml = 32;
	uint16_t greid = 100;
	uint32_t greaddr = 0x010A0A0A;
	uint32_t grep2p = 0x020A0A0A;
	struct ofp_ifnet *dev;
	struct ofp_nh_entry *nh;
	const char *res;

	res = ofp_config_interface_up_v4(port, vlan, vrf, ifaddr, masklen);
	CU_ASSERT_PTR_NULL_FATAL(res);

	/* Non-existent endpoint in vrf */
	res = ofp_config_interface_up_tun(GRE_PORTS, greid, vrf + 1, ifaddr,
					    ifaddr + 1, greaddr, grep2p,
					    gre_ml);
	CU_ASSERT_PTR_NOT_NULL_FATAL(res);
	dev = ofp_get_ifnet(GRE_PORTS, greid);
	CU_ASSERT_PTR_NULL_FATAL(dev);

	/* Successful test */
	res = ofp_config_interface_up_tun(GRE_PORTS, greid, vrf, ifaddr,
					    ifaddr + 1, grep2p, greaddr,
					    gre_ml);
	CU_ASSERT_PTR_NULL_FATAL(res);
	dev = ofp_get_ifnet(GRE_PORTS, greid);
	CU_ASSERT_PTR_NOT_NULL_FATAL(dev);
	CU_ASSERT_EQUAL(dev->ip_local, ifaddr);
	CU_ASSERT_EQUAL(dev->ip_remote, ifaddr + 1);
	CU_ASSERT_EQUAL(dev->ip_addr, greaddr);
	CU_ASSERT_EQUAL(dev->ip_p2p, grep2p);
	CU_ASSERT_EQUAL(dev->masklen, gre_ml);
	CU_ASSERT_EQUAL(dev->if_mtu, ifmtu - 24);

	nh = ofp_get_next_hop(vrf, grep2p, NULL);
	assert_next_hop(nh, 0, GRE_PORTS, greid);

	res = ofp_config_interface_down(port, vlan);
	CU_ASSERT_PTR_NULL_FATAL(res);
	res = ofp_config_interface_down(GRE_PORTS, greid);
	CU_ASSERT_PTR_NULL_FATAL(res);
	dev = ofp_get_ifnet(GRE_PORTS, greid);
	CU_ASSERT_PTR_NULL_FATAL(dev);
}
static void
test_ofp_add_route(uint32_t port, uint32_t vrf, uint32_t vlan,
                   uint32_t destination, uint32_t mask_len,
                   uint32_t rt_dst_len, uint32_t gw)
{
    /* add/test only IPv4 routes and not IPv6 or DEFAULT routes(rt_dst=0)*/
    CU_ASSERT_EQUAL(rt_dst_len, 4);
    if (rt_dst_len == 4) {
        ofp_set_route_params(OFP_ROUTE_ADD, vrf, vlan, port,
                             destination, mask_len, gw);
    }


    uint32_t flags;
    struct ofp_nh_entry *node =
        ofp_get_next_hop(vrf, destination, &flags);

    CU_ASSERT_EQUAL(node->gw, gw);
    CU_ASSERT_EQUAL(node->port, port);
    CU_ASSERT_EQUAL(node->vlan, vlan);
}
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);
}
Exemple #8
0
int ofp_ioctl(int sockfd, int request, ...)
{
	va_list ap;
	void *data;
	struct ofp_ifnet *iface = NULL;
	struct socket  *so = ofp_get_sock_by_fd(sockfd);
	if (!so) {
		ofp_errno = OFP_EBADF;
		return -1;
	}

	va_start(ap, request);
	data = va_arg(ap, void *);
	va_end(ap);

	if (request == (int)(OFP_SIOCGIFCONF)) {
		ofp_errno = ((*so->so_proto->pr_usrreqs->pru_control)
			       (so, request, data, NULL, NULL));
	} else if (OFP_IOCGROUP(request) == 'i') {
		/* All the interface requests start with interface name */
		int port, vlan = 0;
		char *name = data;

		if (get_port_vlan_by_name(name, &port, &vlan) < 0) {
			ofp_errno = OFP_EBADF;
			return -1;
		}

		if (request == (int)(OFP_SIOCSIFTUN)) {
			struct ofp_in_tunreq *treq = data;
			const char *retstr =
				ofp_config_interface_up_tun
				(port, vlan, treq->iftun_vrf,
				 treq->iftun_local_addr.sin_addr.s_addr,
				 treq->iftun_remote_addr.sin_addr.s_addr,
				 treq->iftun_p2p_addr.sin_addr.s_addr,
				 treq->iftun_addr.sin_addr.s_addr, 30);
			if (!retstr)
				ofp_errno = 0;
			else
				ofp_errno = OFP_EBADMSG;
		} else {
			iface = ofp_get_ifnet(port, vlan);

			if (so->so_proto->pr_usrreqs->pru_control)
				ofp_errno = ((*so->so_proto->pr_usrreqs->pru_control)
					       (so, request, data, iface, NULL));
			else
				ofp_errno = OFP_EOPNOTSUPP;
		}
	} else if (OFP_IOCGROUP(request) == 'r') {
		int port = 0, vlan = 0;
		struct ofp_rtentry *rt = data;
		uint32_t dst  = ((struct ofp_sockaddr_in *)&rt->rt_dst)->sin_addr.s_addr;
		uint32_t mask = ((struct ofp_sockaddr_in *)&rt->rt_genmask)->sin_addr.s_addr;
		uint32_t gw   = ((struct ofp_sockaddr_in *)&rt->rt_gateway)->sin_addr.s_addr;
		uint32_t maskcpu = odp_be_to_cpu_32(mask);
		uint32_t mlen = 0;

		if (request != (int)OFP_SIOCADDRT &&
		    request != (int)OFP_SIOCDELRT) {
			ofp_errno = OFP_EBADF;
			return -1;
		}

		if (request == (int)OFP_SIOCADDRT) {
			if (rt->rt_dev) {
				if (get_port_vlan_by_name(rt->rt_dev, &port, &vlan) < 0) {
					ofp_errno = OFP_EBADF;
					return -1;
				}
			} else {
				uint32_t flags;
				struct ofp_nh_entry *nh =
					ofp_get_next_hop(rt->rt_vrf, gw, &flags);
				if (!nh) {
					ofp_errno = OFP_EBADF;
					return -1;
				}
				port = nh->port;
				vlan = nh->vlan;
			}
		}

		while (maskcpu) {
			mlen++;
			maskcpu <<= 1;
		}

		ofp_set_route_params((request == (int) OFP_SIOCADDRT) ? OFP_ROUTE_ADD : OFP_ROUTE_DEL,
				     rt->rt_vrf, vlan, port,
				     dst, mlen, gw,
				     (request == (int) OFP_SIOCADDRT) ?
				     (gw ? OFP_RTF_GATEWAY : OFP_RTF_NET) : 0);
	} else {
		ofp_errno = ofp_soo_ioctl(so, request, data, NULL, NULL);
	}

	if (ofp_errno)
		return -1;

	return 0;
}
Exemple #9
0
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);
}