Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: Plumb support for filtering ipv4 and ipv6 multicast route dumps

Implement kernel side filtering of routes by egress device index and
table id. If the table id is given in the filter, lookup table and
call mr_table_dump directly for it.

Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

David Ahern and committed by
David S. Miller
cb167893 e1cedae1

+74 -12
+4 -3
include/linux/mroute_base.h
··· 7 7 #include <net/net_namespace.h> 8 8 #include <net/sock.h> 9 9 #include <net/fib_notifier.h> 10 + #include <net/ip_fib.h> 10 11 11 12 /** 12 13 * struct vif_device - interface representor for multicast routing ··· 289 288 int (*fill)(struct mr_table *mrt, struct sk_buff *skb, 290 289 u32 portid, u32 seq, struct mr_mfc *c, 291 290 int cmd, int flags), 292 - spinlock_t *lock); 291 + spinlock_t *lock, struct fib_dump_filter *filter); 293 292 int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb, 294 293 struct mr_table *(*iter)(struct net *net, 295 294 struct mr_table *mrt), ··· 297 296 struct sk_buff *skb, 298 297 u32 portid, u32 seq, struct mr_mfc *c, 299 298 int cmd, int flags), 300 - spinlock_t *lock); 299 + spinlock_t *lock, struct fib_dump_filter *filter); 301 300 302 301 int mr_dump(struct net *net, struct notifier_block *nb, unsigned short family, 303 302 int (*rules_dump)(struct net *net, ··· 347 346 struct sk_buff *skb, 348 347 u32 portid, u32 seq, struct mr_mfc *c, 349 348 int cmd, int flags), 350 - spinlock_t *lock) 349 + spinlock_t *lock, struct fib_dump_filter *filter) 351 350 { 352 351 return -EINVAL; 353 352 }
+15 -3
net/ipv4/ipmr.c
··· 2528 2528 static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) 2529 2529 { 2530 2530 struct fib_dump_filter filter = {}; 2531 + int err; 2531 2532 2532 2533 if (cb->strict_check) { 2533 - int err; 2534 - 2535 2534 err = ip_valid_fib_dump_req(sock_net(skb->sk), cb->nlh, 2536 2535 &filter, cb->extack); 2537 2536 if (err < 0) 2538 2537 return err; 2539 2538 } 2540 2539 2540 + if (filter.table_id) { 2541 + struct mr_table *mrt; 2542 + 2543 + mrt = ipmr_get_table(sock_net(skb->sk), filter.table_id); 2544 + if (!mrt) { 2545 + NL_SET_ERR_MSG(cb->extack, "ipv4: MR table does not exist"); 2546 + return -ENOENT; 2547 + } 2548 + err = mr_table_dump(mrt, skb, cb, _ipmr_fill_mroute, 2549 + &mfc_unres_lock, &filter); 2550 + return skb->len ? : err; 2551 + } 2552 + 2541 2553 return mr_rtm_dumproute(skb, cb, ipmr_mr_table_iter, 2542 - _ipmr_fill_mroute, &mfc_unres_lock); 2554 + _ipmr_fill_mroute, &mfc_unres_lock, &filter); 2543 2555 } 2544 2556 2545 2557 static const struct nla_policy rtm_ipmr_policy[RTA_MAX + 1] = {
+40 -3
net/ipv4/ipmr_base.c
··· 268 268 } 269 269 EXPORT_SYMBOL(mr_fill_mroute); 270 270 271 + static bool mr_mfc_uses_dev(const struct mr_table *mrt, 272 + const struct mr_mfc *c, 273 + const struct net_device *dev) 274 + { 275 + int ct; 276 + 277 + for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) { 278 + if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) { 279 + const struct vif_device *vif; 280 + 281 + vif = &mrt->vif_table[ct]; 282 + if (vif->dev == dev) 283 + return true; 284 + } 285 + } 286 + return false; 287 + } 288 + 271 289 int mr_table_dump(struct mr_table *mrt, struct sk_buff *skb, 272 290 struct netlink_callback *cb, 273 291 int (*fill)(struct mr_table *mrt, struct sk_buff *skb, 274 292 u32 portid, u32 seq, struct mr_mfc *c, 275 293 int cmd, int flags), 276 - spinlock_t *lock) 294 + spinlock_t *lock, struct fib_dump_filter *filter) 277 295 { 278 296 unsigned int e = 0, s_e = cb->args[1]; 279 297 unsigned int flags = NLM_F_MULTI; 280 298 struct mr_mfc *mfc; 281 299 int err; 282 300 301 + if (filter->filter_set) 302 + flags |= NLM_F_DUMP_FILTERED; 303 + 283 304 list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list) { 284 305 if (e < s_e) 306 + goto next_entry; 307 + if (filter->dev && 308 + !mr_mfc_uses_dev(mrt, mfc, filter->dev)) 285 309 goto next_entry; 286 310 287 311 err = fill(mrt, skb, NETLINK_CB(cb->skb).portid, ··· 321 297 spin_lock_bh(lock); 322 298 list_for_each_entry(mfc, &mrt->mfc_unres_queue, list) { 323 299 if (e < s_e) 300 + goto next_entry2; 301 + if (filter->dev && 302 + !mr_mfc_uses_dev(mrt, mfc, filter->dev)) 324 303 goto next_entry2; 325 304 326 305 err = fill(mrt, skb, NETLINK_CB(cb->skb).portid, ··· 343 316 cb->args[1] = e; 344 317 return err; 345 318 } 319 + EXPORT_SYMBOL(mr_table_dump); 346 320 347 321 int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb, 348 322 struct mr_table *(*iter)(struct net *net, ··· 352 324 struct sk_buff *skb, 353 325 u32 portid, u32 seq, struct mr_mfc *c, 354 326 int cmd, int flags), 355 - spinlock_t *lock) 327 + spinlock_t *lock, struct fib_dump_filter *filter) 356 328 { 357 329 unsigned int t = 0, s_t = cb->args[0]; 358 330 struct net *net = sock_net(skb->sk); 359 331 struct mr_table *mrt; 360 332 int err; 361 333 334 + /* multicast does not track protocol or have route type other 335 + * than RTN_MULTICAST 336 + */ 337 + if (filter->filter_set) { 338 + if (filter->protocol || filter->flags || 339 + (filter->rt_type && filter->rt_type != RTN_MULTICAST)) 340 + return skb->len; 341 + } 342 + 362 343 rcu_read_lock(); 363 344 for (mrt = iter(net, NULL); mrt; mrt = iter(net, mrt)) { 364 345 if (t < s_t) 365 346 goto next_table; 366 347 367 - err = mr_table_dump(mrt, skb, cb, fill, lock); 348 + err = mr_table_dump(mrt, skb, cb, fill, lock, filter); 368 349 if (err < 0) 369 350 break; 370 351 next_table:
+15 -3
net/ipv6/ip6mr.c
··· 2459 2459 { 2460 2460 const struct nlmsghdr *nlh = cb->nlh; 2461 2461 struct fib_dump_filter filter = {}; 2462 + int err; 2462 2463 2463 2464 if (cb->strict_check) { 2464 - int err; 2465 - 2466 2465 err = ip_valid_fib_dump_req(sock_net(skb->sk), nlh, 2467 2466 &filter, cb->extack); 2468 2467 if (err < 0) 2469 2468 return err; 2470 2469 } 2471 2470 2471 + if (filter.table_id) { 2472 + struct mr_table *mrt; 2473 + 2474 + mrt = ip6mr_get_table(sock_net(skb->sk), filter.table_id); 2475 + if (!mrt) { 2476 + NL_SET_ERR_MSG_MOD(cb->extack, "MR table does not exist"); 2477 + return -ENOENT; 2478 + } 2479 + err = mr_table_dump(mrt, skb, cb, _ip6mr_fill_mroute, 2480 + &mfc_unres_lock, &filter); 2481 + return skb->len ? : err; 2482 + } 2483 + 2472 2484 return mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter, 2473 - _ip6mr_fill_mroute, &mfc_unres_lock); 2485 + _ip6mr_fill_mroute, &mfc_unres_lock, &filter); 2474 2486 }