Skip to content

Commit 96e8f5a

Browse files
pmachatakuba-moo
authored andcommitted
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 <[email protected]> Link: https://patch.msgid.link/3bcc034a3ab4d3c291072fff38f78d7fbbeef4e6.1750113335.git.petrm@nvidia.com Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 1b02f44 commit 96e8f5a

File tree

4 files changed

+127
-0
lines changed

4 files changed

+127
-0
lines changed

include/linux/ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ struct inet6_skb_parm {
156156
#define IP6SKB_SEG6 256
157157
#define IP6SKB_FAKEJUMBO 512
158158
#define IP6SKB_MULTIPATH 1024
159+
#define IP6SKB_MCROUTE 2048
159160
};
160161

161162
#if defined(CONFIG_NET_L3_MASTER_DEV)

include/linux/mroute6.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ extern int ip6_mroute_getsockopt(struct sock *, int, sockptr_t, sockptr_t);
3131
extern int ip6_mr_input(struct sk_buff *skb);
3232
extern int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
3333
extern int ip6_mr_init(void);
34+
extern int ip6_mr_output(struct net *net, struct sock *sk, struct sk_buff *skb);
3435
extern void ip6_mr_cleanup(void);
3536
int ip6mr_ioctl(struct sock *sk, int cmd, void *arg);
3637
#else
@@ -58,6 +59,12 @@ static inline int ip6_mr_init(void)
5859
return 0;
5960
}
6061

62+
static inline int
63+
ip6_mr_output(struct net *net, struct sock *sk, struct sk_buff *skb)
64+
{
65+
return ip6_output(net, sk, skb);
66+
}
67+
6168
static inline void ip6_mr_cleanup(void)
6269
{
6370
return;

net/ipv6/ip6mr.c

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,6 +2119,19 @@ static void ip6mr_forward2(struct net *net, struct mr_table *mrt,
21192119
kfree_skb(skb);
21202120
}
21212121

2122+
static void ip6mr_output2(struct net *net, struct mr_table *mrt,
2123+
struct sk_buff *skb, int vifi)
2124+
{
2125+
if (ip6mr_prepare_xmit(net, mrt, skb, vifi))
2126+
goto out_free;
2127+
2128+
ip6_output(net, NULL, skb);
2129+
return;
2130+
2131+
out_free:
2132+
kfree_skb(skb);
2133+
}
2134+
21222135
/* Called with rcu_read_lock() */
21232136
static int ip6mr_find_vif(struct mr_table *mrt, struct net_device *dev)
21242137
{
@@ -2231,6 +2244,56 @@ static void ip6_mr_forward(struct net *net, struct mr_table *mrt,
22312244
kfree_skb(skb);
22322245
}
22332246

2247+
/* Called under rcu_read_lock() */
2248+
static void ip6_mr_output_finish(struct net *net, struct mr_table *mrt,
2249+
struct net_device *dev, struct sk_buff *skb,
2250+
struct mfc6_cache *c)
2251+
{
2252+
int psend = -1;
2253+
int ct;
2254+
2255+
WARN_ON_ONCE(!rcu_read_lock_held());
2256+
2257+
atomic_long_inc(&c->_c.mfc_un.res.pkt);
2258+
atomic_long_add(skb->len, &c->_c.mfc_un.res.bytes);
2259+
WRITE_ONCE(c->_c.mfc_un.res.lastuse, jiffies);
2260+
2261+
/* Forward the frame */
2262+
if (ipv6_addr_any(&c->mf6c_origin) &&
2263+
ipv6_addr_any(&c->mf6c_mcastgrp)) {
2264+
if (ipv6_hdr(skb)->hop_limit >
2265+
c->_c.mfc_un.res.ttls[c->_c.mfc_parent]) {
2266+
/* It's an (*,*) entry and the packet is not coming from
2267+
* the upstream: forward the packet to the upstream
2268+
* only.
2269+
*/
2270+
psend = c->_c.mfc_parent;
2271+
goto last_forward;
2272+
}
2273+
goto dont_forward;
2274+
}
2275+
for (ct = c->_c.mfc_un.res.maxvif - 1;
2276+
ct >= c->_c.mfc_un.res.minvif; ct--) {
2277+
if (ipv6_hdr(skb)->hop_limit > c->_c.mfc_un.res.ttls[ct]) {
2278+
if (psend != -1) {
2279+
struct sk_buff *skb2;
2280+
2281+
skb2 = skb_clone(skb, GFP_ATOMIC);
2282+
if (skb2)
2283+
ip6mr_output2(net, mrt, skb2, psend);
2284+
}
2285+
psend = ct;
2286+
}
2287+
}
2288+
last_forward:
2289+
if (psend != -1) {
2290+
ip6mr_output2(net, mrt, skb, psend);
2291+
return;
2292+
}
2293+
2294+
dont_forward:
2295+
kfree_skb(skb);
2296+
}
22342297

22352298
/*
22362299
* Multicast packets for forwarding arrive here
@@ -2298,6 +2361,61 @@ int ip6_mr_input(struct sk_buff *skb)
22982361
return 0;
22992362
}
23002363

2364+
int ip6_mr_output(struct net *net, struct sock *sk, struct sk_buff *skb)
2365+
{
2366+
struct net_device *dev = skb_dst(skb)->dev;
2367+
struct flowi6 fl6 = (struct flowi6) {
2368+
.flowi6_iif = LOOPBACK_IFINDEX,
2369+
.flowi6_mark = skb->mark,
2370+
};
2371+
struct mfc6_cache *cache;
2372+
struct mr_table *mrt;
2373+
int err;
2374+
int vif;
2375+
2376+
WARN_ON_ONCE(!rcu_read_lock_held());
2377+
2378+
if (IP6CB(skb)->flags & IP6SKB_FORWARDED)
2379+
goto ip6_output;
2380+
if (!(IP6CB(skb)->flags & IP6SKB_MCROUTE))
2381+
goto ip6_output;
2382+
2383+
err = ip6mr_fib_lookup(net, &fl6, &mrt);
2384+
if (err < 0) {
2385+
kfree_skb(skb);
2386+
return err;
2387+
}
2388+
2389+
cache = ip6mr_cache_find(mrt,
2390+
&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr);
2391+
if (!cache) {
2392+
vif = ip6mr_find_vif(mrt, dev);
2393+
if (vif >= 0)
2394+
cache = ip6mr_cache_find_any(mrt,
2395+
&ipv6_hdr(skb)->daddr,
2396+
vif);
2397+
}
2398+
2399+
/* No usable cache entry */
2400+
if (!cache) {
2401+
vif = ip6mr_find_vif(mrt, dev);
2402+
if (vif >= 0)
2403+
return ip6mr_cache_unresolved(mrt, vif, skb, dev);
2404+
goto ip6_output;
2405+
}
2406+
2407+
/* Wrong interface */
2408+
vif = cache->_c.mfc_parent;
2409+
if (rcu_access_pointer(mrt->vif_table[vif].dev) != dev)
2410+
goto ip6_output;
2411+
2412+
ip6_mr_output_finish(net, mrt, dev, skb, cache);
2413+
return 0;
2414+
2415+
ip6_output:
2416+
return ip6_output(net, sk, skb);
2417+
}
2418+
23012419
int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,
23022420
u32 portid)
23032421
{

net/ipv6/route.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,7 @@ static void ip6_rt_init_dst(struct rt6_info *rt, const struct fib6_result *res)
11451145
rt->dst.input = ip6_input;
11461146
} else if (ipv6_addr_type(&f6i->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
11471147
rt->dst.input = ip6_mc_input;
1148+
rt->dst.output = ip6_mr_output;
11481149
} else {
11491150
rt->dst.input = ip6_forward;
11501151
}

0 commit comments

Comments
 (0)