Skip to content

Commit

Permalink
net: vrf: Flip IPv4 output path from FIB lookup hook to out hook
Browse files Browse the repository at this point in the history
Flip the IPv4 output path to use the l3mdev tx out hook. The VRF dst
is not returned on the first FIB lookup. Instead, the dst on the
skb is switched at the beginning of the IPv4 output processing to
send the packet to the VRF driver on xmit.

Signed-off-by: David Ahern <dsa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David Ahern authored and David S. Miller committed Sep 11, 2016
1 parent 5f02ce2 commit ebfc102
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 5 deletions.
64 changes: 63 additions & 1 deletion drivers/net/vrf.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,20 @@ static netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb,
}
#endif

/* based on ip_local_out; can't use it b/c the dst is switched pointing to us */
static int vrf_ip_local_out(struct net *net, struct sock *sk,
struct sk_buff *skb)
{
int err;

err = nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, net, sk,
skb, NULL, skb_dst(skb)->dev, dst_output);
if (likely(err == 1))
err = dst_output(net, sk, skb);

return err;
}

static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb,
struct net_device *vrf_dev)
{
Expand Down Expand Up @@ -292,7 +306,7 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb,
RT_SCOPE_LINK);
}

ret = ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb);
ret = vrf_ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb);
if (unlikely(net_xmit_eval(ret)))
vrf_dev->stats.tx_errors++;
else
Expand Down Expand Up @@ -531,6 +545,53 @@ static int vrf_output(struct net *net, struct sock *sk, struct sk_buff *skb)
!(IPCB(skb)->flags & IPSKB_REROUTED));
}

/* set dst on skb to send packet to us via dev_xmit path. Allows
* packet to go through device based features such as qdisc, netfilter
* hooks and packet sockets with skb->dev set to vrf device.
*/
static struct sk_buff *vrf_ip_out(struct net_device *vrf_dev,
struct sock *sk,
struct sk_buff *skb)
{
struct net_vrf *vrf = netdev_priv(vrf_dev);
struct dst_entry *dst = NULL;
struct rtable *rth;

rcu_read_lock();

rth = rcu_dereference(vrf->rth);
if (likely(rth)) {
dst = &rth->dst;
dst_hold(dst);
}

rcu_read_unlock();

if (unlikely(!dst)) {
vrf_tx_error(vrf_dev, skb);
return NULL;
}

skb_dst_drop(skb);
skb_dst_set(skb, dst);

return skb;
}

/* called with rcu lock held */
static struct sk_buff *vrf_l3_out(struct net_device *vrf_dev,
struct sock *sk,
struct sk_buff *skb,
u16 proto)
{
switch (proto) {
case AF_INET:
return vrf_ip_out(vrf_dev, sk, skb);
}

return skb;
}

/* holding rtnl */
static void vrf_rtable_release(struct net_device *dev, struct net_vrf *vrf)
{
Expand Down Expand Up @@ -1067,6 +1128,7 @@ static const struct l3mdev_ops vrf_l3mdev_ops = {
.l3mdev_get_rtable = vrf_get_rtable,
.l3mdev_get_saddr = vrf_get_saddr,
.l3mdev_l3_rcv = vrf_l3_rcv,
.l3mdev_l3_out = vrf_l3_out,
#if IS_ENABLED(CONFIG_IPV6)
.l3mdev_get_rt6_dst = vrf_get_rt6_dst,
.l3mdev_get_saddr6 = vrf_get_saddr6,
Expand Down
4 changes: 0 additions & 4 deletions net/ipv4/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -2246,10 +2246,6 @@ struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *fl4,
fl4->saddr = inet_select_addr(dev_out, 0,
RT_SCOPE_HOST);
}

rth = l3mdev_get_rtable(dev_out, fl4);
if (rth)
goto out;
}

if (!fl4->daddr) {
Expand Down

0 comments on commit ebfc102

Please sign in to comment.