Skip to content

Commit

Permalink
net: l3mdev: Add hook to output path
Browse files Browse the repository at this point in the history
This patch adds the infrastructure to the output path to pass an skb
to an l3mdev device if it has a hook registered. This is the Tx parallel
to l3mdev_ip{6}_rcv in the receive path and is the basis for removing
the existing hook that returns the vrf dst on the fib lookup.

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 9ee0034 commit a8e3e1a
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 0 deletions.
48 changes: 48 additions & 0 deletions include/net/l3mdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@
#ifndef _NET_L3MDEV_H_
#define _NET_L3MDEV_H_

#include <net/dst.h>
#include <net/fib_rules.h>

/**
* struct l3mdev_ops - l3mdev operations
*
* @l3mdev_fib_table: Get FIB table id to use for lookups
*
* @l3mdev_l3_rcv: Hook in L3 receive path
*
* @l3mdev_l3_out: Hook in L3 output path
*
* @l3mdev_get_rtable: Get cached IPv4 rtable (dst_entry) for device
*
* @l3mdev_get_saddr: Get source address for a flow
Expand All @@ -29,6 +34,9 @@ struct l3mdev_ops {
u32 (*l3mdev_fib_table)(const struct net_device *dev);
struct sk_buff * (*l3mdev_l3_rcv)(struct net_device *dev,
struct sk_buff *skb, u16 proto);
struct sk_buff * (*l3mdev_l3_out)(struct net_device *dev,
struct sock *sk, struct sk_buff *skb,
u16 proto);

/* IPv4 ops */
struct rtable * (*l3mdev_get_rtable)(const struct net_device *dev,
Expand Down Expand Up @@ -201,6 +209,34 @@ struct sk_buff *l3mdev_ip6_rcv(struct sk_buff *skb)
return l3mdev_l3_rcv(skb, AF_INET6);
}

static inline
struct sk_buff *l3mdev_l3_out(struct sock *sk, struct sk_buff *skb, u16 proto)
{
struct net_device *dev = skb_dst(skb)->dev;

if (netif_is_l3_slave(dev)) {
struct net_device *master;

master = netdev_master_upper_dev_get_rcu(dev);
if (master && master->l3mdev_ops->l3mdev_l3_out)
skb = master->l3mdev_ops->l3mdev_l3_out(master, sk,
skb, proto);
}

return skb;
}

static inline
struct sk_buff *l3mdev_ip_out(struct sock *sk, struct sk_buff *skb)
{
return l3mdev_l3_out(sk, skb, AF_INET);
}

static inline
struct sk_buff *l3mdev_ip6_out(struct sock *sk, struct sk_buff *skb)
{
return l3mdev_l3_out(sk, skb, AF_INET6);
}
#else

static inline int l3mdev_master_ifindex_rcu(const struct net_device *dev)
Expand Down Expand Up @@ -286,6 +322,18 @@ struct sk_buff *l3mdev_ip6_rcv(struct sk_buff *skb)
return skb;
}

static inline
struct sk_buff *l3mdev_ip_out(struct sock *sk, struct sk_buff *skb)
{
return skb;
}

static inline
struct sk_buff *l3mdev_ip6_out(struct sock *sk, struct sk_buff *skb)
{
return skb;
}

static inline
int l3mdev_fib_rule_match(struct net *net, struct flowi *fl,
struct fib_lookup_arg *arg)
Expand Down
8 changes: 8 additions & 0 deletions net/ipv4/ip_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)

iph->tot_len = htons(skb->len);
ip_send_check(iph);

/* if egress device is enslaved to an L3 master device pass the
* skb to its handler for processing
*/
skb = l3mdev_ip_out(sk, skb);
if (unlikely(!skb))
return 0;

return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
net, sk, skb, NULL, skb_dst(skb)->dev,
dst_output);
Expand Down
8 changes: 8 additions & 0 deletions net/ipv6/ip6_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,14 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
if ((skb->len <= mtu) || skb->ignore_df || skb_is_gso(skb)) {
IP6_UPD_PO_STATS(net, ip6_dst_idev(skb_dst(skb)),
IPSTATS_MIB_OUT, skb->len);

/* if egress device is enslaved to an L3 master device pass the
* skb to its handler for processing
*/
skb = l3mdev_ip6_out((struct sock *)sk, skb);
if (unlikely(!skb))
return 0;

/* hooks should never assume socket lock is held.
* we promote our socket to non const
*/
Expand Down
7 changes: 7 additions & 0 deletions net/ipv6/output_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,13 @@ int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
ipv6_hdr(skb)->payload_len = htons(len);
IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);

/* if egress device is enslaved to an L3 master device pass the
* skb to its handler for processing
*/
skb = l3mdev_ip6_out(sk, skb);
if (unlikely(!skb))
return 0;

return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
net, sk, skb, NULL, skb_dst(skb)->dev,
dst_output);
Expand Down
7 changes: 7 additions & 0 deletions net/ipv6/raw.c
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,13 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
if (err)
goto error_fault;

/* if egress device is enslaved to an L3 master device pass the
* skb to its handler for processing
*/
skb = l3mdev_ip6_out(sk, skb);
if (unlikely(!skb))
return 0;

IP6_UPD_PO_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len);
err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb,
NULL, rt->dst.dev, dst_output);
Expand Down

0 comments on commit a8e3e1a

Please sign in to comment.