.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * IP multicast routing support for mrouted 3.6/3.8 |
---|
3 | 4 | * |
---|
4 | 5 | * (c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk> |
---|
5 | 6 | * Linux Consultancy and Custom Driver Development |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or |
---|
8 | | - * modify it under the terms of the GNU General Public License |
---|
9 | | - * as published by the Free Software Foundation; either version |
---|
10 | | - * 2 of the License, or (at your option) any later version. |
---|
11 | 7 | * |
---|
12 | 8 | * Fixes: |
---|
13 | 9 | * Michael Chastain : Incorrect size of copying. |
---|
.. | .. |
---|
23 | 19 | * Carlos Picoto : PIMv1 Support |
---|
24 | 20 | * Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header |
---|
25 | 21 | * Relax this requirement to work with older peers. |
---|
26 | | - * |
---|
27 | 22 | */ |
---|
28 | 23 | |
---|
29 | 24 | #include <linux/uaccess.h> |
---|
.. | .. |
---|
66 | 61 | #include <net/netlink.h> |
---|
67 | 62 | #include <net/fib_rules.h> |
---|
68 | 63 | #include <linux/netconf.h> |
---|
69 | | -#include <net/nexthop.h> |
---|
70 | | -#include <net/switchdev.h> |
---|
| 64 | +#include <net/rtnh.h> |
---|
71 | 65 | |
---|
72 | 66 | #include <linux/nospec.h> |
---|
73 | 67 | |
---|
.. | .. |
---|
111 | 105 | static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, |
---|
112 | 106 | int cmd); |
---|
113 | 107 | static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt); |
---|
114 | | -static void mroute_clean_tables(struct mr_table *mrt, bool all); |
---|
| 108 | +static void mroute_clean_tables(struct mr_table *mrt, int flags); |
---|
115 | 109 | static void ipmr_expire_process(struct timer_list *t); |
---|
116 | 110 | |
---|
117 | 111 | #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES |
---|
118 | | -#define ipmr_for_each_table(mrt, net) \ |
---|
119 | | - list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list) |
---|
| 112 | +#define ipmr_for_each_table(mrt, net) \ |
---|
| 113 | + list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list, \ |
---|
| 114 | + lockdep_rtnl_is_held() || \ |
---|
| 115 | + list_empty(&net->ipv4.mr_tables)) |
---|
120 | 116 | |
---|
121 | 117 | static struct mr_table *ipmr_mr_table_iter(struct net *net, |
---|
122 | 118 | struct mr_table *mrt) |
---|
.. | .. |
---|
286 | 282 | rtnl_unlock(); |
---|
287 | 283 | } |
---|
288 | 284 | |
---|
289 | | -static int ipmr_rules_dump(struct net *net, struct notifier_block *nb) |
---|
| 285 | +static int ipmr_rules_dump(struct net *net, struct notifier_block *nb, |
---|
| 286 | + struct netlink_ext_ack *extack) |
---|
290 | 287 | { |
---|
291 | | - return fib_rules_dump(net, nb, RTNL_FAMILY_IPMR); |
---|
| 288 | + return fib_rules_dump(net, nb, RTNL_FAMILY_IPMR, extack); |
---|
292 | 289 | } |
---|
293 | 290 | |
---|
294 | 291 | static unsigned int ipmr_rules_seq_read(struct net *net) |
---|
.. | .. |
---|
344 | 341 | rtnl_unlock(); |
---|
345 | 342 | } |
---|
346 | 343 | |
---|
347 | | -static int ipmr_rules_dump(struct net *net, struct notifier_block *nb) |
---|
| 344 | +static int ipmr_rules_dump(struct net *net, struct notifier_block *nb, |
---|
| 345 | + struct netlink_ext_ack *extack) |
---|
348 | 346 | { |
---|
349 | 347 | return 0; |
---|
350 | 348 | } |
---|
.. | .. |
---|
376 | 374 | .key_offset = offsetof(struct mfc_cache, cmparg), |
---|
377 | 375 | .key_len = sizeof(struct mfc_cache_cmp_arg), |
---|
378 | 376 | .nelem_hint = 3, |
---|
379 | | - .locks_mul = 1, |
---|
380 | 377 | .obj_cmpfn = ipmr_hash_cmp, |
---|
381 | 378 | .automatic_shrinking = true, |
---|
382 | 379 | }; |
---|
.. | .. |
---|
418 | 415 | static void ipmr_free_table(struct mr_table *mrt) |
---|
419 | 416 | { |
---|
420 | 417 | del_timer_sync(&mrt->ipmr_expire_timer); |
---|
421 | | - mroute_clean_tables(mrt, true); |
---|
| 418 | + mroute_clean_tables(mrt, MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC | |
---|
| 419 | + MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC); |
---|
422 | 420 | rhltable_destroy(&mrt->mfc_hash); |
---|
423 | 421 | kfree(mrt); |
---|
424 | 422 | } |
---|
425 | 423 | |
---|
426 | 424 | /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */ |
---|
427 | | - |
---|
428 | | -static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v) |
---|
429 | | -{ |
---|
430 | | - struct net *net = dev_net(dev); |
---|
431 | | - |
---|
432 | | - dev_close(dev); |
---|
433 | | - |
---|
434 | | - dev = __dev_get_by_name(net, "tunl0"); |
---|
435 | | - if (dev) { |
---|
436 | | - const struct net_device_ops *ops = dev->netdev_ops; |
---|
437 | | - struct ifreq ifr; |
---|
438 | | - struct ip_tunnel_parm p; |
---|
439 | | - |
---|
440 | | - memset(&p, 0, sizeof(p)); |
---|
441 | | - p.iph.daddr = v->vifc_rmt_addr.s_addr; |
---|
442 | | - p.iph.saddr = v->vifc_lcl_addr.s_addr; |
---|
443 | | - p.iph.version = 4; |
---|
444 | | - p.iph.ihl = 5; |
---|
445 | | - p.iph.protocol = IPPROTO_IPIP; |
---|
446 | | - sprintf(p.name, "dvmrp%d", v->vifc_vifi); |
---|
447 | | - ifr.ifr_ifru.ifru_data = (__force void __user *)&p; |
---|
448 | | - |
---|
449 | | - if (ops->ndo_do_ioctl) { |
---|
450 | | - mm_segment_t oldfs = get_fs(); |
---|
451 | | - |
---|
452 | | - set_fs(KERNEL_DS); |
---|
453 | | - ops->ndo_do_ioctl(dev, &ifr, SIOCDELTUNNEL); |
---|
454 | | - set_fs(oldfs); |
---|
455 | | - } |
---|
456 | | - } |
---|
457 | | -} |
---|
458 | 425 | |
---|
459 | 426 | /* Initialize ipmr pimreg/tunnel in_device */ |
---|
460 | 427 | static bool ipmr_init_vif_indev(const struct net_device *dev) |
---|
.. | .. |
---|
475 | 442 | |
---|
476 | 443 | static struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v) |
---|
477 | 444 | { |
---|
478 | | - struct net_device *dev; |
---|
| 445 | + struct net_device *tunnel_dev, *new_dev; |
---|
| 446 | + struct ip_tunnel_parm p = { }; |
---|
| 447 | + int err; |
---|
479 | 448 | |
---|
480 | | - dev = __dev_get_by_name(net, "tunl0"); |
---|
| 449 | + tunnel_dev = __dev_get_by_name(net, "tunl0"); |
---|
| 450 | + if (!tunnel_dev) |
---|
| 451 | + goto out; |
---|
481 | 452 | |
---|
482 | | - if (dev) { |
---|
483 | | - const struct net_device_ops *ops = dev->netdev_ops; |
---|
484 | | - int err; |
---|
485 | | - struct ifreq ifr; |
---|
486 | | - struct ip_tunnel_parm p; |
---|
| 453 | + p.iph.daddr = v->vifc_rmt_addr.s_addr; |
---|
| 454 | + p.iph.saddr = v->vifc_lcl_addr.s_addr; |
---|
| 455 | + p.iph.version = 4; |
---|
| 456 | + p.iph.ihl = 5; |
---|
| 457 | + p.iph.protocol = IPPROTO_IPIP; |
---|
| 458 | + sprintf(p.name, "dvmrp%d", v->vifc_vifi); |
---|
487 | 459 | |
---|
488 | | - memset(&p, 0, sizeof(p)); |
---|
489 | | - p.iph.daddr = v->vifc_rmt_addr.s_addr; |
---|
490 | | - p.iph.saddr = v->vifc_lcl_addr.s_addr; |
---|
491 | | - p.iph.version = 4; |
---|
492 | | - p.iph.ihl = 5; |
---|
493 | | - p.iph.protocol = IPPROTO_IPIP; |
---|
494 | | - sprintf(p.name, "dvmrp%d", v->vifc_vifi); |
---|
495 | | - ifr.ifr_ifru.ifru_data = (__force void __user *)&p; |
---|
| 460 | + if (!tunnel_dev->netdev_ops->ndo_tunnel_ctl) |
---|
| 461 | + goto out; |
---|
| 462 | + err = tunnel_dev->netdev_ops->ndo_tunnel_ctl(tunnel_dev, &p, |
---|
| 463 | + SIOCADDTUNNEL); |
---|
| 464 | + if (err) |
---|
| 465 | + goto out; |
---|
496 | 466 | |
---|
497 | | - if (ops->ndo_do_ioctl) { |
---|
498 | | - mm_segment_t oldfs = get_fs(); |
---|
| 467 | + new_dev = __dev_get_by_name(net, p.name); |
---|
| 468 | + if (!new_dev) |
---|
| 469 | + goto out; |
---|
499 | 470 | |
---|
500 | | - set_fs(KERNEL_DS); |
---|
501 | | - err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL); |
---|
502 | | - set_fs(oldfs); |
---|
503 | | - } else { |
---|
504 | | - err = -EOPNOTSUPP; |
---|
505 | | - } |
---|
506 | | - dev = NULL; |
---|
507 | | - |
---|
508 | | - if (err == 0 && |
---|
509 | | - (dev = __dev_get_by_name(net, p.name)) != NULL) { |
---|
510 | | - dev->flags |= IFF_MULTICAST; |
---|
511 | | - if (!ipmr_init_vif_indev(dev)) |
---|
512 | | - goto failure; |
---|
513 | | - if (dev_open(dev)) |
---|
514 | | - goto failure; |
---|
515 | | - dev_hold(dev); |
---|
516 | | - } |
---|
| 471 | + new_dev->flags |= IFF_MULTICAST; |
---|
| 472 | + if (!ipmr_init_vif_indev(new_dev)) |
---|
| 473 | + goto out_unregister; |
---|
| 474 | + if (dev_open(new_dev, NULL)) |
---|
| 475 | + goto out_unregister; |
---|
| 476 | + dev_hold(new_dev); |
---|
| 477 | + err = dev_set_allmulti(new_dev, 1); |
---|
| 478 | + if (err) { |
---|
| 479 | + dev_close(new_dev); |
---|
| 480 | + tunnel_dev->netdev_ops->ndo_tunnel_ctl(tunnel_dev, &p, |
---|
| 481 | + SIOCDELTUNNEL); |
---|
| 482 | + dev_put(new_dev); |
---|
| 483 | + new_dev = ERR_PTR(err); |
---|
517 | 484 | } |
---|
518 | | - return dev; |
---|
| 485 | + return new_dev; |
---|
519 | 486 | |
---|
520 | | -failure: |
---|
521 | | - unregister_netdevice(dev); |
---|
522 | | - return NULL; |
---|
| 487 | +out_unregister: |
---|
| 488 | + unregister_netdevice(new_dev); |
---|
| 489 | +out: |
---|
| 490 | + return ERR_PTR(-ENOBUFS); |
---|
523 | 491 | } |
---|
524 | 492 | |
---|
525 | 493 | #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2) |
---|
.. | .. |
---|
593 | 561 | |
---|
594 | 562 | if (!ipmr_init_vif_indev(dev)) |
---|
595 | 563 | goto failure; |
---|
596 | | - if (dev_open(dev)) |
---|
| 564 | + if (dev_open(dev, NULL)) |
---|
597 | 565 | goto failure; |
---|
598 | 566 | |
---|
599 | 567 | dev_hold(dev); |
---|
.. | .. |
---|
670 | 638 | |
---|
671 | 639 | /** |
---|
672 | 640 | * vif_delete - Delete a VIF entry |
---|
| 641 | + * @mrt: Table to delete from |
---|
| 642 | + * @vifi: VIF identifier to delete |
---|
673 | 643 | * @notify: Set to 1, if the caller is a notifier_call |
---|
| 644 | + * @head: if unregistering the VIF, place it on this queue |
---|
674 | 645 | */ |
---|
675 | 646 | static int vif_delete(struct mr_table *mrt, int vifi, int notify, |
---|
676 | 647 | struct list_head *head) |
---|
.. | .. |
---|
839 | 810 | static int vif_add(struct net *net, struct mr_table *mrt, |
---|
840 | 811 | struct vifctl *vifc, int mrtsock) |
---|
841 | 812 | { |
---|
| 813 | + struct netdev_phys_item_id ppid = { }; |
---|
842 | 814 | int vifi = vifc->vifc_vifi; |
---|
843 | | - struct switchdev_attr attr = { |
---|
844 | | - .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID, |
---|
845 | | - }; |
---|
846 | 815 | struct vif_device *v = &mrt->vif_table[vifi]; |
---|
847 | 816 | struct net_device *dev; |
---|
848 | 817 | struct in_device *in_dev; |
---|
.. | .. |
---|
873 | 842 | break; |
---|
874 | 843 | case VIFF_TUNNEL: |
---|
875 | 844 | dev = ipmr_new_tunnel(net, vifc); |
---|
876 | | - if (!dev) |
---|
877 | | - return -ENOBUFS; |
---|
878 | | - err = dev_set_allmulti(dev, 1); |
---|
879 | | - if (err) { |
---|
880 | | - ipmr_del_tunnel(dev, vifc); |
---|
881 | | - dev_put(dev); |
---|
882 | | - return err; |
---|
883 | | - } |
---|
| 845 | + if (IS_ERR(dev)) |
---|
| 846 | + return PTR_ERR(dev); |
---|
884 | 847 | break; |
---|
885 | 848 | case VIFF_USE_IFINDEX: |
---|
886 | 849 | case 0: |
---|
.. | .. |
---|
921 | 884 | vifc->vifc_flags | (!mrtsock ? VIFF_STATIC : 0), |
---|
922 | 885 | (VIFF_TUNNEL | VIFF_REGISTER)); |
---|
923 | 886 | |
---|
924 | | - attr.orig_dev = dev; |
---|
925 | | - if (!switchdev_port_attr_get(dev, &attr)) { |
---|
926 | | - memcpy(v->dev_parent_id.id, attr.u.ppid.id, attr.u.ppid.id_len); |
---|
927 | | - v->dev_parent_id.id_len = attr.u.ppid.id_len; |
---|
| 887 | + err = dev_get_port_parent_id(dev, &ppid, true); |
---|
| 888 | + if (err == 0) { |
---|
| 889 | + memcpy(v->dev_parent_id.id, ppid.id, ppid.id_len); |
---|
| 890 | + v->dev_parent_id.id_len = ppid.id_len; |
---|
928 | 891 | } else { |
---|
929 | 892 | v->dev_parent_id.id_len = 0; |
---|
930 | 893 | } |
---|
.. | .. |
---|
1077 | 1040 | memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr)); |
---|
1078 | 1041 | msg->im_msgtype = assert; |
---|
1079 | 1042 | msg->im_mbz = 0; |
---|
1080 | | - if (assert == IGMPMSG_WRVIFWHOLE) |
---|
| 1043 | + if (assert == IGMPMSG_WRVIFWHOLE) { |
---|
1081 | 1044 | msg->im_vif = vifi; |
---|
1082 | | - else |
---|
| 1045 | + msg->im_vif_hi = vifi >> 8; |
---|
| 1046 | + } else { |
---|
1083 | 1047 | msg->im_vif = mrt->mroute_reg_vif_num; |
---|
| 1048 | + msg->im_vif_hi = mrt->mroute_reg_vif_num >> 8; |
---|
| 1049 | + } |
---|
1084 | 1050 | ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2; |
---|
1085 | 1051 | ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) + |
---|
1086 | 1052 | sizeof(struct iphdr)); |
---|
.. | .. |
---|
1093 | 1059 | ip_hdr(skb)->protocol = 0; |
---|
1094 | 1060 | msg = (struct igmpmsg *)skb_network_header(skb); |
---|
1095 | 1061 | msg->im_vif = vifi; |
---|
| 1062 | + msg->im_vif_hi = vifi >> 8; |
---|
1096 | 1063 | skb_dst_set(skb, dst_clone(skb_dst(pkt))); |
---|
1097 | 1064 | /* Add our header */ |
---|
1098 | 1065 | igmp = skb_put(skb, sizeof(struct igmphdr)); |
---|
.. | .. |
---|
1144 | 1111 | |
---|
1145 | 1112 | if (!found) { |
---|
1146 | 1113 | /* Create a new entry if allowable */ |
---|
1147 | | - if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 || |
---|
1148 | | - (c = ipmr_cache_alloc_unres()) == NULL) { |
---|
| 1114 | + c = ipmr_cache_alloc_unres(); |
---|
| 1115 | + if (!c) { |
---|
1149 | 1116 | spin_unlock_bh(&mfc_unres_lock); |
---|
1150 | 1117 | |
---|
1151 | 1118 | kfree_skb(skb); |
---|
.. | .. |
---|
1301 | 1268 | } |
---|
1302 | 1269 | |
---|
1303 | 1270 | /* Close the multicast socket, and clear the vif tables etc */ |
---|
1304 | | -static void mroute_clean_tables(struct mr_table *mrt, bool all) |
---|
| 1271 | +static void mroute_clean_tables(struct mr_table *mrt, int flags) |
---|
1305 | 1272 | { |
---|
1306 | 1273 | struct net *net = read_pnet(&mrt->net); |
---|
1307 | 1274 | struct mr_mfc *c, *tmp; |
---|
.. | .. |
---|
1310 | 1277 | int i; |
---|
1311 | 1278 | |
---|
1312 | 1279 | /* Shut down all active vif entries */ |
---|
1313 | | - for (i = 0; i < mrt->maxvif; i++) { |
---|
1314 | | - if (!all && (mrt->vif_table[i].flags & VIFF_STATIC)) |
---|
1315 | | - continue; |
---|
1316 | | - vif_delete(mrt, i, 0, &list); |
---|
| 1280 | + if (flags & (MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC)) { |
---|
| 1281 | + for (i = 0; i < mrt->maxvif; i++) { |
---|
| 1282 | + if (((mrt->vif_table[i].flags & VIFF_STATIC) && |
---|
| 1283 | + !(flags & MRT_FLUSH_VIFS_STATIC)) || |
---|
| 1284 | + (!(mrt->vif_table[i].flags & VIFF_STATIC) && !(flags & MRT_FLUSH_VIFS))) |
---|
| 1285 | + continue; |
---|
| 1286 | + vif_delete(mrt, i, 0, &list); |
---|
| 1287 | + } |
---|
| 1288 | + unregister_netdevice_many(&list); |
---|
1317 | 1289 | } |
---|
1318 | | - unregister_netdevice_many(&list); |
---|
1319 | 1290 | |
---|
1320 | 1291 | /* Wipe the cache */ |
---|
1321 | | - list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) { |
---|
1322 | | - if (!all && (c->mfc_flags & MFC_STATIC)) |
---|
1323 | | - continue; |
---|
1324 | | - rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params); |
---|
1325 | | - list_del_rcu(&c->list); |
---|
1326 | | - cache = (struct mfc_cache *)c; |
---|
1327 | | - call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, cache, |
---|
1328 | | - mrt->id); |
---|
1329 | | - mroute_netlink_event(mrt, cache, RTM_DELROUTE); |
---|
1330 | | - mr_cache_put(c); |
---|
| 1292 | + if (flags & (MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC)) { |
---|
| 1293 | + list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) { |
---|
| 1294 | + if (((c->mfc_flags & MFC_STATIC) && !(flags & MRT_FLUSH_MFC_STATIC)) || |
---|
| 1295 | + (!(c->mfc_flags & MFC_STATIC) && !(flags & MRT_FLUSH_MFC))) |
---|
| 1296 | + continue; |
---|
| 1297 | + rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params); |
---|
| 1298 | + list_del_rcu(&c->list); |
---|
| 1299 | + cache = (struct mfc_cache *)c; |
---|
| 1300 | + call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, cache, |
---|
| 1301 | + mrt->id); |
---|
| 1302 | + mroute_netlink_event(mrt, cache, RTM_DELROUTE); |
---|
| 1303 | + mr_cache_put(c); |
---|
| 1304 | + } |
---|
1331 | 1305 | } |
---|
1332 | 1306 | |
---|
1333 | | - if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { |
---|
1334 | | - spin_lock_bh(&mfc_unres_lock); |
---|
1335 | | - list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) { |
---|
1336 | | - list_del(&c->list); |
---|
1337 | | - cache = (struct mfc_cache *)c; |
---|
1338 | | - mroute_netlink_event(mrt, cache, RTM_DELROUTE); |
---|
1339 | | - ipmr_destroy_unres(mrt, cache); |
---|
| 1307 | + if (flags & MRT_FLUSH_MFC) { |
---|
| 1308 | + if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { |
---|
| 1309 | + spin_lock_bh(&mfc_unres_lock); |
---|
| 1310 | + list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) { |
---|
| 1311 | + list_del(&c->list); |
---|
| 1312 | + cache = (struct mfc_cache *)c; |
---|
| 1313 | + mroute_netlink_event(mrt, cache, RTM_DELROUTE); |
---|
| 1314 | + ipmr_destroy_unres(mrt, cache); |
---|
| 1315 | + } |
---|
| 1316 | + spin_unlock_bh(&mfc_unres_lock); |
---|
1340 | 1317 | } |
---|
1341 | | - spin_unlock_bh(&mfc_unres_lock); |
---|
1342 | 1318 | } |
---|
1343 | 1319 | } |
---|
1344 | 1320 | |
---|
.. | .. |
---|
1359 | 1335 | NETCONFA_IFINDEX_ALL, |
---|
1360 | 1336 | net->ipv4.devconf_all); |
---|
1361 | 1337 | RCU_INIT_POINTER(mrt->mroute_sk, NULL); |
---|
1362 | | - mroute_clean_tables(mrt, false); |
---|
| 1338 | + mroute_clean_tables(mrt, MRT_FLUSH_VIFS | MRT_FLUSH_MFC); |
---|
1363 | 1339 | } |
---|
1364 | 1340 | } |
---|
1365 | 1341 | rtnl_unlock(); |
---|
.. | .. |
---|
1371 | 1347 | * MOSPF/PIM router set up we can clean this up. |
---|
1372 | 1348 | */ |
---|
1373 | 1349 | |
---|
1374 | | -int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, |
---|
| 1350 | +int ip_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, |
---|
1375 | 1351 | unsigned int optlen) |
---|
1376 | 1352 | { |
---|
1377 | 1353 | struct net *net = sock_net(sk); |
---|
.. | .. |
---|
1443 | 1419 | ret = -EINVAL; |
---|
1444 | 1420 | break; |
---|
1445 | 1421 | } |
---|
1446 | | - if (copy_from_user(&vif, optval, sizeof(vif))) { |
---|
| 1422 | + if (copy_from_sockptr(&vif, optval, sizeof(vif))) { |
---|
1447 | 1423 | ret = -EFAULT; |
---|
1448 | 1424 | break; |
---|
1449 | 1425 | } |
---|
.. | .. |
---|
1464 | 1440 | case MRT_ADD_MFC: |
---|
1465 | 1441 | case MRT_DEL_MFC: |
---|
1466 | 1442 | parent = -1; |
---|
1467 | | - /* fall through */ |
---|
| 1443 | + fallthrough; |
---|
1468 | 1444 | case MRT_ADD_MFC_PROXY: |
---|
1469 | 1445 | case MRT_DEL_MFC_PROXY: |
---|
1470 | 1446 | if (optlen != sizeof(mfc)) { |
---|
1471 | 1447 | ret = -EINVAL; |
---|
1472 | 1448 | break; |
---|
1473 | 1449 | } |
---|
1474 | | - if (copy_from_user(&mfc, optval, sizeof(mfc))) { |
---|
| 1450 | + if (copy_from_sockptr(&mfc, optval, sizeof(mfc))) { |
---|
1475 | 1451 | ret = -EFAULT; |
---|
1476 | 1452 | break; |
---|
1477 | 1453 | } |
---|
.. | .. |
---|
1484 | 1460 | sk == rtnl_dereference(mrt->mroute_sk), |
---|
1485 | 1461 | parent); |
---|
1486 | 1462 | break; |
---|
| 1463 | + case MRT_FLUSH: |
---|
| 1464 | + if (optlen != sizeof(val)) { |
---|
| 1465 | + ret = -EINVAL; |
---|
| 1466 | + break; |
---|
| 1467 | + } |
---|
| 1468 | + if (copy_from_sockptr(&val, optval, sizeof(val))) { |
---|
| 1469 | + ret = -EFAULT; |
---|
| 1470 | + break; |
---|
| 1471 | + } |
---|
| 1472 | + mroute_clean_tables(mrt, val); |
---|
| 1473 | + break; |
---|
1487 | 1474 | /* Control PIM assert. */ |
---|
1488 | 1475 | case MRT_ASSERT: |
---|
1489 | 1476 | if (optlen != sizeof(val)) { |
---|
1490 | 1477 | ret = -EINVAL; |
---|
1491 | 1478 | break; |
---|
1492 | 1479 | } |
---|
1493 | | - if (get_user(val, (int __user *)optval)) { |
---|
| 1480 | + if (copy_from_sockptr(&val, optval, sizeof(val))) { |
---|
1494 | 1481 | ret = -EFAULT; |
---|
1495 | 1482 | break; |
---|
1496 | 1483 | } |
---|
.. | .. |
---|
1505 | 1492 | ret = -EINVAL; |
---|
1506 | 1493 | break; |
---|
1507 | 1494 | } |
---|
1508 | | - if (get_user(val, (int __user *)optval)) { |
---|
| 1495 | + if (copy_from_sockptr(&val, optval, sizeof(val))) { |
---|
1509 | 1496 | ret = -EFAULT; |
---|
1510 | 1497 | break; |
---|
1511 | 1498 | } |
---|
.. | .. |
---|
1527 | 1514 | ret = -EINVAL; |
---|
1528 | 1515 | break; |
---|
1529 | 1516 | } |
---|
1530 | | - if (get_user(uval, (u32 __user *)optval)) { |
---|
| 1517 | + if (copy_from_sockptr(&uval, optval, sizeof(uval))) { |
---|
1531 | 1518 | ret = -EFAULT; |
---|
1532 | 1519 | break; |
---|
1533 | 1520 | } |
---|
.. | .. |
---|
1784 | 1771 | ip_send_check(iph); |
---|
1785 | 1772 | |
---|
1786 | 1773 | memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); |
---|
1787 | | - nf_reset(skb); |
---|
| 1774 | + nf_reset_ct(skb); |
---|
1788 | 1775 | } |
---|
1789 | 1776 | |
---|
1790 | 1777 | static inline int ipmr_forward_finish(struct net *net, struct sock *sk, |
---|
.. | .. |
---|
1808 | 1795 | struct vif_device *out_vif = &mrt->vif_table[out_vifi]; |
---|
1809 | 1796 | struct vif_device *in_vif = &mrt->vif_table[in_vifi]; |
---|
1810 | 1797 | |
---|
1811 | | - if (!skb->offload_mr_fwd_mark) |
---|
| 1798 | + if (!skb->offload_l3_fwd_mark) |
---|
1812 | 1799 | return false; |
---|
1813 | 1800 | if (!out_vif->dev_parent_id.id_len || !in_vif->dev_parent_id.id_len) |
---|
1814 | 1801 | return false; |
---|
.. | .. |
---|
1826 | 1813 | /* Processing handlers for ipmr_forward */ |
---|
1827 | 1814 | |
---|
1828 | 1815 | static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, |
---|
1829 | | - int in_vifi, struct sk_buff *skb, |
---|
1830 | | - struct mfc_cache *c, int vifi) |
---|
| 1816 | + int in_vifi, struct sk_buff *skb, int vifi) |
---|
1831 | 1817 | { |
---|
1832 | 1818 | const struct iphdr *iph = ip_hdr(skb); |
---|
1833 | 1819 | struct vif_device *vif = &mrt->vif_table[vifi]; |
---|
.. | .. |
---|
2033 | 2019 | |
---|
2034 | 2020 | if (skb2) |
---|
2035 | 2021 | ipmr_queue_xmit(net, mrt, true_vifi, |
---|
2036 | | - skb2, c, psend); |
---|
| 2022 | + skb2, psend); |
---|
2037 | 2023 | } |
---|
2038 | 2024 | psend = ct; |
---|
2039 | 2025 | } |
---|
.. | .. |
---|
2045 | 2031 | |
---|
2046 | 2032 | if (skb2) |
---|
2047 | 2033 | ipmr_queue_xmit(net, mrt, true_vifi, skb2, |
---|
2048 | | - c, psend); |
---|
| 2034 | + psend); |
---|
2049 | 2035 | } else { |
---|
2050 | | - ipmr_queue_xmit(net, mrt, true_vifi, skb, c, psend); |
---|
| 2036 | + ipmr_queue_xmit(net, mrt, true_vifi, skb, psend); |
---|
2051 | 2037 | return; |
---|
2052 | 2038 | } |
---|
2053 | 2039 | } |
---|
.. | .. |
---|
2131 | 2117 | |
---|
2132 | 2118 | mroute_sk = rcu_dereference(mrt->mroute_sk); |
---|
2133 | 2119 | if (mroute_sk) { |
---|
2134 | | - nf_reset(skb); |
---|
| 2120 | + nf_reset_ct(skb); |
---|
2135 | 2121 | raw_rcv(mroute_sk, skb); |
---|
2136 | 2122 | return 0; |
---|
2137 | 2123 | } |
---|
.. | .. |
---|
2416 | 2402 | + nla_total_size(4) /* IPMRA_CREPORT_VIF_ID */ |
---|
2417 | 2403 | + nla_total_size(4) /* IPMRA_CREPORT_SRC_ADDR */ |
---|
2418 | 2404 | + nla_total_size(4) /* IPMRA_CREPORT_DST_ADDR */ |
---|
| 2405 | + + nla_total_size(4) /* IPMRA_CREPORT_TABLE */ |
---|
2419 | 2406 | /* IPMRA_CREPORT_PKT */ |
---|
2420 | 2407 | + nla_total_size(payloadlen) |
---|
2421 | 2408 | ; |
---|
.. | .. |
---|
2447 | 2434 | rtgenm = nlmsg_data(nlh); |
---|
2448 | 2435 | rtgenm->rtgen_family = RTNL_FAMILY_IPMR; |
---|
2449 | 2436 | if (nla_put_u8(skb, IPMRA_CREPORT_MSGTYPE, msg->im_msgtype) || |
---|
2450 | | - nla_put_u32(skb, IPMRA_CREPORT_VIF_ID, msg->im_vif) || |
---|
| 2437 | + nla_put_u32(skb, IPMRA_CREPORT_VIF_ID, msg->im_vif | (msg->im_vif_hi << 8)) || |
---|
2451 | 2438 | nla_put_in_addr(skb, IPMRA_CREPORT_SRC_ADDR, |
---|
2452 | 2439 | msg->im_src.s_addr) || |
---|
2453 | 2440 | nla_put_in_addr(skb, IPMRA_CREPORT_DST_ADDR, |
---|
2454 | | - msg->im_dst.s_addr)) |
---|
| 2441 | + msg->im_dst.s_addr) || |
---|
| 2442 | + nla_put_u32(skb, IPMRA_CREPORT_TABLE, mrt->id)) |
---|
2455 | 2443 | goto nla_put_failure; |
---|
2456 | 2444 | |
---|
2457 | 2445 | nla = nla_reserve(skb, IPMRA_CREPORT_PKT, payloadlen); |
---|
.. | .. |
---|
2471 | 2459 | rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE_R, -ENOBUFS); |
---|
2472 | 2460 | } |
---|
2473 | 2461 | |
---|
| 2462 | +static int ipmr_rtm_valid_getroute_req(struct sk_buff *skb, |
---|
| 2463 | + const struct nlmsghdr *nlh, |
---|
| 2464 | + struct nlattr **tb, |
---|
| 2465 | + struct netlink_ext_ack *extack) |
---|
| 2466 | +{ |
---|
| 2467 | + struct rtmsg *rtm; |
---|
| 2468 | + int i, err; |
---|
| 2469 | + |
---|
| 2470 | + if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) { |
---|
| 2471 | + NL_SET_ERR_MSG(extack, "ipv4: Invalid header for multicast route get request"); |
---|
| 2472 | + return -EINVAL; |
---|
| 2473 | + } |
---|
| 2474 | + |
---|
| 2475 | + if (!netlink_strict_get_check(skb)) |
---|
| 2476 | + return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX, |
---|
| 2477 | + rtm_ipv4_policy, extack); |
---|
| 2478 | + |
---|
| 2479 | + rtm = nlmsg_data(nlh); |
---|
| 2480 | + if ((rtm->rtm_src_len && rtm->rtm_src_len != 32) || |
---|
| 2481 | + (rtm->rtm_dst_len && rtm->rtm_dst_len != 32) || |
---|
| 2482 | + rtm->rtm_tos || rtm->rtm_table || rtm->rtm_protocol || |
---|
| 2483 | + rtm->rtm_scope || rtm->rtm_type || rtm->rtm_flags) { |
---|
| 2484 | + NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for multicast route get request"); |
---|
| 2485 | + return -EINVAL; |
---|
| 2486 | + } |
---|
| 2487 | + |
---|
| 2488 | + err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX, |
---|
| 2489 | + rtm_ipv4_policy, extack); |
---|
| 2490 | + if (err) |
---|
| 2491 | + return err; |
---|
| 2492 | + |
---|
| 2493 | + if ((tb[RTA_SRC] && !rtm->rtm_src_len) || |
---|
| 2494 | + (tb[RTA_DST] && !rtm->rtm_dst_len)) { |
---|
| 2495 | + NL_SET_ERR_MSG(extack, "ipv4: rtm_src_len and rtm_dst_len must be 32 for IPv4"); |
---|
| 2496 | + return -EINVAL; |
---|
| 2497 | + } |
---|
| 2498 | + |
---|
| 2499 | + for (i = 0; i <= RTA_MAX; i++) { |
---|
| 2500 | + if (!tb[i]) |
---|
| 2501 | + continue; |
---|
| 2502 | + |
---|
| 2503 | + switch (i) { |
---|
| 2504 | + case RTA_SRC: |
---|
| 2505 | + case RTA_DST: |
---|
| 2506 | + case RTA_TABLE: |
---|
| 2507 | + break; |
---|
| 2508 | + default: |
---|
| 2509 | + NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in multicast route get request"); |
---|
| 2510 | + return -EINVAL; |
---|
| 2511 | + } |
---|
| 2512 | + } |
---|
| 2513 | + |
---|
| 2514 | + return 0; |
---|
| 2515 | +} |
---|
| 2516 | + |
---|
2474 | 2517 | static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, |
---|
2475 | 2518 | struct netlink_ext_ack *extack) |
---|
2476 | 2519 | { |
---|
.. | .. |
---|
2479 | 2522 | struct sk_buff *skb = NULL; |
---|
2480 | 2523 | struct mfc_cache *cache; |
---|
2481 | 2524 | struct mr_table *mrt; |
---|
2482 | | - struct rtmsg *rtm; |
---|
2483 | 2525 | __be32 src, grp; |
---|
2484 | 2526 | u32 tableid; |
---|
2485 | 2527 | int err; |
---|
2486 | 2528 | |
---|
2487 | | - err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, |
---|
2488 | | - rtm_ipv4_policy, extack); |
---|
| 2529 | + err = ipmr_rtm_valid_getroute_req(in_skb, nlh, tb, extack); |
---|
2489 | 2530 | if (err < 0) |
---|
2490 | 2531 | goto errout; |
---|
2491 | | - |
---|
2492 | | - rtm = nlmsg_data(nlh); |
---|
2493 | 2532 | |
---|
2494 | 2533 | src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0; |
---|
2495 | 2534 | grp = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0; |
---|
.. | .. |
---|
2534 | 2573 | |
---|
2535 | 2574 | static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) |
---|
2536 | 2575 | { |
---|
| 2576 | + struct fib_dump_filter filter = {}; |
---|
| 2577 | + int err; |
---|
| 2578 | + |
---|
| 2579 | + if (cb->strict_check) { |
---|
| 2580 | + err = ip_valid_fib_dump_req(sock_net(skb->sk), cb->nlh, |
---|
| 2581 | + &filter, cb); |
---|
| 2582 | + if (err < 0) |
---|
| 2583 | + return err; |
---|
| 2584 | + } |
---|
| 2585 | + |
---|
| 2586 | + if (filter.table_id) { |
---|
| 2587 | + struct mr_table *mrt; |
---|
| 2588 | + |
---|
| 2589 | + mrt = ipmr_get_table(sock_net(skb->sk), filter.table_id); |
---|
| 2590 | + if (!mrt) { |
---|
| 2591 | + if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IPMR) |
---|
| 2592 | + return skb->len; |
---|
| 2593 | + |
---|
| 2594 | + NL_SET_ERR_MSG(cb->extack, "ipv4: MR table does not exist"); |
---|
| 2595 | + return -ENOENT; |
---|
| 2596 | + } |
---|
| 2597 | + err = mr_table_dump(mrt, skb, cb, _ipmr_fill_mroute, |
---|
| 2598 | + &mfc_unres_lock, &filter); |
---|
| 2599 | + return skb->len ? : err; |
---|
| 2600 | + } |
---|
| 2601 | + |
---|
2537 | 2602 | return mr_rtm_dumproute(skb, cb, ipmr_mr_table_iter, |
---|
2538 | | - _ipmr_fill_mroute, &mfc_unres_lock); |
---|
| 2603 | + _ipmr_fill_mroute, &mfc_unres_lock, &filter); |
---|
2539 | 2604 | } |
---|
2540 | 2605 | |
---|
2541 | 2606 | static const struct nla_policy rtm_ipmr_policy[RTA_MAX + 1] = { |
---|
.. | .. |
---|
2584 | 2649 | struct rtmsg *rtm; |
---|
2585 | 2650 | int ret, rem; |
---|
2586 | 2651 | |
---|
2587 | | - ret = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipmr_policy, |
---|
2588 | | - extack); |
---|
| 2652 | + ret = nlmsg_validate_deprecated(nlh, sizeof(*rtm), RTA_MAX, |
---|
| 2653 | + rtm_ipmr_policy, extack); |
---|
2589 | 2654 | if (ret < 0) |
---|
2590 | 2655 | goto out; |
---|
2591 | 2656 | rtm = nlmsg_data(nlh); |
---|
.. | .. |
---|
2693 | 2758 | return true; |
---|
2694 | 2759 | |
---|
2695 | 2760 | vif = &mrt->vif_table[vifid]; |
---|
2696 | | - vif_nest = nla_nest_start(skb, IPMRA_VIF); |
---|
| 2761 | + vif_nest = nla_nest_start_noflag(skb, IPMRA_VIF); |
---|
2697 | 2762 | if (!vif_nest) |
---|
2698 | 2763 | return false; |
---|
2699 | 2764 | if (nla_put_u32(skb, IPMRA_VIFA_IFINDEX, vif->dev->ifindex) || |
---|
.. | .. |
---|
2717 | 2782 | return true; |
---|
2718 | 2783 | } |
---|
2719 | 2784 | |
---|
| 2785 | +static int ipmr_valid_dumplink(const struct nlmsghdr *nlh, |
---|
| 2786 | + struct netlink_ext_ack *extack) |
---|
| 2787 | +{ |
---|
| 2788 | + struct ifinfomsg *ifm; |
---|
| 2789 | + |
---|
| 2790 | + if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { |
---|
| 2791 | + NL_SET_ERR_MSG(extack, "ipv4: Invalid header for ipmr link dump"); |
---|
| 2792 | + return -EINVAL; |
---|
| 2793 | + } |
---|
| 2794 | + |
---|
| 2795 | + if (nlmsg_attrlen(nlh, sizeof(*ifm))) { |
---|
| 2796 | + NL_SET_ERR_MSG(extack, "Invalid data after header in ipmr link dump"); |
---|
| 2797 | + return -EINVAL; |
---|
| 2798 | + } |
---|
| 2799 | + |
---|
| 2800 | + ifm = nlmsg_data(nlh); |
---|
| 2801 | + if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags || |
---|
| 2802 | + ifm->ifi_change || ifm->ifi_index) { |
---|
| 2803 | + NL_SET_ERR_MSG(extack, "Invalid values in header for ipmr link dump request"); |
---|
| 2804 | + return -EINVAL; |
---|
| 2805 | + } |
---|
| 2806 | + |
---|
| 2807 | + return 0; |
---|
| 2808 | +} |
---|
| 2809 | + |
---|
2720 | 2810 | static int ipmr_rtm_dumplink(struct sk_buff *skb, struct netlink_callback *cb) |
---|
2721 | 2811 | { |
---|
2722 | 2812 | struct net *net = sock_net(skb->sk); |
---|
.. | .. |
---|
2724 | 2814 | unsigned int t = 0, s_t; |
---|
2725 | 2815 | unsigned int e = 0, s_e; |
---|
2726 | 2816 | struct mr_table *mrt; |
---|
| 2817 | + |
---|
| 2818 | + if (cb->strict_check) { |
---|
| 2819 | + int err = ipmr_valid_dumplink(cb->nlh, cb->extack); |
---|
| 2820 | + |
---|
| 2821 | + if (err < 0) |
---|
| 2822 | + return err; |
---|
| 2823 | + } |
---|
2727 | 2824 | |
---|
2728 | 2825 | s_t = cb->args[0]; |
---|
2729 | 2826 | s_e = cb->args[1]; |
---|
.. | .. |
---|
2745 | 2842 | memset(hdr, 0, sizeof(*hdr)); |
---|
2746 | 2843 | hdr->ifi_family = RTNL_FAMILY_IPMR; |
---|
2747 | 2844 | |
---|
2748 | | - af = nla_nest_start(skb, IFLA_AF_SPEC); |
---|
| 2845 | + af = nla_nest_start_noflag(skb, IFLA_AF_SPEC); |
---|
2749 | 2846 | if (!af) { |
---|
2750 | 2847 | nlmsg_cancel(skb, nlh); |
---|
2751 | 2848 | goto out; |
---|
.. | .. |
---|
2756 | 2853 | goto out; |
---|
2757 | 2854 | } |
---|
2758 | 2855 | |
---|
2759 | | - vifs = nla_nest_start(skb, IPMRA_TABLE_VIFS); |
---|
| 2856 | + vifs = nla_nest_start_noflag(skb, IPMRA_TABLE_VIFS); |
---|
2760 | 2857 | if (!vifs) { |
---|
2761 | 2858 | nla_nest_end(skb, af); |
---|
2762 | 2859 | nlmsg_end(skb, nlh); |
---|
.. | .. |
---|
2923 | 3020 | return net->ipv4.ipmr_seq + ipmr_rules_seq_read(net); |
---|
2924 | 3021 | } |
---|
2925 | 3022 | |
---|
2926 | | -static int ipmr_dump(struct net *net, struct notifier_block *nb) |
---|
| 3023 | +static int ipmr_dump(struct net *net, struct notifier_block *nb, |
---|
| 3024 | + struct netlink_ext_ack *extack) |
---|
2927 | 3025 | { |
---|
2928 | 3026 | return mr_dump(net, nb, RTNL_FAMILY_IPMR, ipmr_rules_dump, |
---|
2929 | | - ipmr_mr_table_iter, &mrt_lock); |
---|
| 3027 | + ipmr_mr_table_iter, &mrt_lock, extack); |
---|
2930 | 3028 | } |
---|
2931 | 3029 | |
---|
2932 | 3030 | static const struct fib_notifier_ops ipmr_notifier_ops_template = { |
---|