diff options
Diffstat (limited to '')
-rw-r--r-- | drivers/net/vrf.c | 48 |
1 files changed, 45 insertions, 3 deletions
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index b3d9c5546c79..e7094fbd7568 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -27,6 +27,7 @@ #include <linux/hashtable.h> #include <linux/inetdevice.h> +#include <net/arp.h> #include <net/ip.h> #include <net/ip_fib.h> #include <net/ip6_route.h> @@ -219,6 +220,9 @@ err: static netdev_tx_t is_ip_tx_frame(struct sk_buff *skb, struct net_device *dev) { + /* strip the ethernet header added for pass through VRF device */ + __skb_pull(skb, skb_network_offset(skb)); + switch (skb->protocol) { case htons(ETH_P_IP): return vrf_process_v4_outbound(skb, dev); @@ -248,9 +252,47 @@ static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev) return ret; } -static netdev_tx_t vrf_finish(struct sock *sk, struct sk_buff *skb) +/* modelled after ip_finish_output2 */ +static int vrf_finish_output(struct sock *sk, struct sk_buff *skb) { - return dev_queue_xmit(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; + + /* 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)) + ret = dst_neigh_output(dst, neigh, skb); + + rcu_read_unlock_bh(); +err: + if (unlikely(ret < 0)) + vrf_tx_error(skb->dev, skb); + return ret; } static int vrf_output(struct sock *sk, struct sk_buff *skb) @@ -264,7 +306,7 @@ static int vrf_output(struct sock *sk, struct sk_buff *skb) return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb, NULL, dev, - vrf_finish, + vrf_finish_output, !(IPCB(skb)->flags & IPSKB_REROUTED)); } |