mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
net: ipv6: Add ip6_mr_output()
Multicast routing is today handled in the input path. Locally generated MC packets don't hit the IPMR code today. Thus if a VXLAN remote address is multicast, the driver needs to set an OIF during route lookup. Thus MC routing configuration needs to be kept in sync with the VXLAN FDB and MDB. Ideally, the VXLAN packets would be routed by the MC routing code instead. To that end, this patch adds support to route locally generated multicast packets. The newly-added routines do largely what ip6_mr_input() and ip6_mr_forward() do: make an MR cache lookup to find where to send the packets, and use ip6_output() to send each of them. When no cache entry is found, the packet is punted to the daemon for resolution. Similarly to the IPv4 case in a previous patch, the new logic is contingent on a newly-added IP6CB flag being set. Signed-off-by: Petr Machata <petrm@nvidia.com> Link: https://patch.msgid.link/3bcc034a3ab4d3c291072fff38f78d7fbbeef4e6.1750113335.git.petrm@nvidia.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
1b02f4475d
commit
96e8f5a9fe
4 changed files with 127 additions and 0 deletions
|
@ -156,6 +156,7 @@ struct inet6_skb_parm {
|
||||||
#define IP6SKB_SEG6 256
|
#define IP6SKB_SEG6 256
|
||||||
#define IP6SKB_FAKEJUMBO 512
|
#define IP6SKB_FAKEJUMBO 512
|
||||||
#define IP6SKB_MULTIPATH 1024
|
#define IP6SKB_MULTIPATH 1024
|
||||||
|
#define IP6SKB_MCROUTE 2048
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(CONFIG_NET_L3_MASTER_DEV)
|
#if defined(CONFIG_NET_L3_MASTER_DEV)
|
||||||
|
|
|
@ -31,6 +31,7 @@ extern int ip6_mroute_getsockopt(struct sock *, int, sockptr_t, sockptr_t);
|
||||||
extern int ip6_mr_input(struct sk_buff *skb);
|
extern int ip6_mr_input(struct sk_buff *skb);
|
||||||
extern int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
|
extern int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
|
||||||
extern int ip6_mr_init(void);
|
extern int ip6_mr_init(void);
|
||||||
|
extern int ip6_mr_output(struct net *net, struct sock *sk, struct sk_buff *skb);
|
||||||
extern void ip6_mr_cleanup(void);
|
extern void ip6_mr_cleanup(void);
|
||||||
int ip6mr_ioctl(struct sock *sk, int cmd, void *arg);
|
int ip6mr_ioctl(struct sock *sk, int cmd, void *arg);
|
||||||
#else
|
#else
|
||||||
|
@ -58,6 +59,12 @@ static inline int ip6_mr_init(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
ip6_mr_output(struct net *net, struct sock *sk, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
return ip6_output(net, sk, skb);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void ip6_mr_cleanup(void)
|
static inline void ip6_mr_cleanup(void)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
118
net/ipv6/ip6mr.c
118
net/ipv6/ip6mr.c
|
@ -2119,6 +2119,19 @@ out_free:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ip6mr_output2(struct net *net, struct mr_table *mrt,
|
||||||
|
struct sk_buff *skb, int vifi)
|
||||||
|
{
|
||||||
|
if (ip6mr_prepare_xmit(net, mrt, skb, vifi))
|
||||||
|
goto out_free;
|
||||||
|
|
||||||
|
ip6_output(net, NULL, skb);
|
||||||
|
return;
|
||||||
|
|
||||||
|
out_free:
|
||||||
|
kfree_skb(skb);
|
||||||
|
}
|
||||||
|
|
||||||
/* Called with rcu_read_lock() */
|
/* Called with rcu_read_lock() */
|
||||||
static int ip6mr_find_vif(struct mr_table *mrt, struct net_device *dev)
|
static int ip6mr_find_vif(struct mr_table *mrt, struct net_device *dev)
|
||||||
{
|
{
|
||||||
|
@ -2231,6 +2244,56 @@ dont_forward:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called under rcu_read_lock() */
|
||||||
|
static void ip6_mr_output_finish(struct net *net, struct mr_table *mrt,
|
||||||
|
struct net_device *dev, struct sk_buff *skb,
|
||||||
|
struct mfc6_cache *c)
|
||||||
|
{
|
||||||
|
int psend = -1;
|
||||||
|
int ct;
|
||||||
|
|
||||||
|
WARN_ON_ONCE(!rcu_read_lock_held());
|
||||||
|
|
||||||
|
atomic_long_inc(&c->_c.mfc_un.res.pkt);
|
||||||
|
atomic_long_add(skb->len, &c->_c.mfc_un.res.bytes);
|
||||||
|
WRITE_ONCE(c->_c.mfc_un.res.lastuse, jiffies);
|
||||||
|
|
||||||
|
/* Forward the frame */
|
||||||
|
if (ipv6_addr_any(&c->mf6c_origin) &&
|
||||||
|
ipv6_addr_any(&c->mf6c_mcastgrp)) {
|
||||||
|
if (ipv6_hdr(skb)->hop_limit >
|
||||||
|
c->_c.mfc_un.res.ttls[c->_c.mfc_parent]) {
|
||||||
|
/* It's an (*,*) entry and the packet is not coming from
|
||||||
|
* the upstream: forward the packet to the upstream
|
||||||
|
* only.
|
||||||
|
*/
|
||||||
|
psend = c->_c.mfc_parent;
|
||||||
|
goto last_forward;
|
||||||
|
}
|
||||||
|
goto dont_forward;
|
||||||
|
}
|
||||||
|
for (ct = c->_c.mfc_un.res.maxvif - 1;
|
||||||
|
ct >= c->_c.mfc_un.res.minvif; ct--) {
|
||||||
|
if (ipv6_hdr(skb)->hop_limit > c->_c.mfc_un.res.ttls[ct]) {
|
||||||
|
if (psend != -1) {
|
||||||
|
struct sk_buff *skb2;
|
||||||
|
|
||||||
|
skb2 = skb_clone(skb, GFP_ATOMIC);
|
||||||
|
if (skb2)
|
||||||
|
ip6mr_output2(net, mrt, skb2, psend);
|
||||||
|
}
|
||||||
|
psend = ct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_forward:
|
||||||
|
if (psend != -1) {
|
||||||
|
ip6mr_output2(net, mrt, skb, psend);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dont_forward:
|
||||||
|
kfree_skb(skb);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Multicast packets for forwarding arrive here
|
* Multicast packets for forwarding arrive here
|
||||||
|
@ -2298,6 +2361,61 @@ int ip6_mr_input(struct sk_buff *skb)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ip6_mr_output(struct net *net, struct sock *sk, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct net_device *dev = skb_dst(skb)->dev;
|
||||||
|
struct flowi6 fl6 = (struct flowi6) {
|
||||||
|
.flowi6_iif = LOOPBACK_IFINDEX,
|
||||||
|
.flowi6_mark = skb->mark,
|
||||||
|
};
|
||||||
|
struct mfc6_cache *cache;
|
||||||
|
struct mr_table *mrt;
|
||||||
|
int err;
|
||||||
|
int vif;
|
||||||
|
|
||||||
|
WARN_ON_ONCE(!rcu_read_lock_held());
|
||||||
|
|
||||||
|
if (IP6CB(skb)->flags & IP6SKB_FORWARDED)
|
||||||
|
goto ip6_output;
|
||||||
|
if (!(IP6CB(skb)->flags & IP6SKB_MCROUTE))
|
||||||
|
goto ip6_output;
|
||||||
|
|
||||||
|
err = ip6mr_fib_lookup(net, &fl6, &mrt);
|
||||||
|
if (err < 0) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache = ip6mr_cache_find(mrt,
|
||||||
|
&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr);
|
||||||
|
if (!cache) {
|
||||||
|
vif = ip6mr_find_vif(mrt, dev);
|
||||||
|
if (vif >= 0)
|
||||||
|
cache = ip6mr_cache_find_any(mrt,
|
||||||
|
&ipv6_hdr(skb)->daddr,
|
||||||
|
vif);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No usable cache entry */
|
||||||
|
if (!cache) {
|
||||||
|
vif = ip6mr_find_vif(mrt, dev);
|
||||||
|
if (vif >= 0)
|
||||||
|
return ip6mr_cache_unresolved(mrt, vif, skb, dev);
|
||||||
|
goto ip6_output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wrong interface */
|
||||||
|
vif = cache->_c.mfc_parent;
|
||||||
|
if (rcu_access_pointer(mrt->vif_table[vif].dev) != dev)
|
||||||
|
goto ip6_output;
|
||||||
|
|
||||||
|
ip6_mr_output_finish(net, mrt, dev, skb, cache);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ip6_output:
|
||||||
|
return ip6_output(net, sk, skb);
|
||||||
|
}
|
||||||
|
|
||||||
int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,
|
int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,
|
||||||
u32 portid)
|
u32 portid)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1145,6 +1145,7 @@ static void ip6_rt_init_dst(struct rt6_info *rt, const struct fib6_result *res)
|
||||||
rt->dst.input = ip6_input;
|
rt->dst.input = ip6_input;
|
||||||
} else if (ipv6_addr_type(&f6i->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
|
} else if (ipv6_addr_type(&f6i->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
|
||||||
rt->dst.input = ip6_mc_input;
|
rt->dst.input = ip6_mc_input;
|
||||||
|
rt->dst.output = ip6_mr_output;
|
||||||
} else {
|
} else {
|
||||||
rt->dst.input = ip6_forward;
|
rt->dst.input = ip6_forward;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue