/* modelled after ip_finish_output2 */ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct rtable *rt = (struct rtable *)dst; struct net_device *dev = dst->dev; unsigned int hh_len = LL_RESERVED_SPACE(dev); struct neighbour *neigh; u32 nexthop; int ret = -EINVAL; nf_reset(skb); /* Be paranoid, rather than too clever. */ if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) { struct sk_buff *skb2; skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev)); if (!skb2) { ret = -ENOMEM; goto err; } if (skb->sk) skb_set_owner_w(skb2, skb->sk); consume_skb(skb); skb = skb2; } rcu_read_lock_bh(); nexthop = (__force u32)rt_nexthop(rt, ip_hdr(skb)->daddr); neigh = __ipv4_neigh_lookup_noref(dev, nexthop); if (unlikely(!neigh)) neigh = __neigh_create(&arp_tbl, &nexthop, dev, false); if (!IS_ERR(neigh)) { sock_confirm_neigh(skb, neigh); ret = neigh_output(neigh, skb); rcu_read_unlock_bh(); return ret; } rcu_read_unlock_bh(); err: vrf_tx_error(skb->dev, skb); return ret; }
static struct neighbour *hwaddr_neighbour(struct rtable *rt, struct hwaddr_entry *entry) { #if LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0) struct neighbour *neigh = rt->dst.neighbour; #else struct neighbour *neigh = NULL; __be32 next = 0; rcu_read_lock_bh(); next = rt_nexthop(rt, entry->h_remote); neigh = __ipv4_neigh_lookup_noref(rt->dst.dev, next); if (IS_ERR_OR_NULL(neigh)) neigh = __neigh_create(&arp_tbl, &next, rt->dst.dev, false); rcu_read_unlock_bh(); #endif if (IS_ERR(neigh)) return NULL; return neigh; }