|
@@ -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));
|
|
|
}
|
|
|
|