| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Linux IPv6 multicast routing support for BSD pim6sd |
|---|
| 3 | 4 | * Based on net/ipv4/ipmr.c. |
|---|
| .. | .. |
|---|
| 8 | 9 | * 6WIND, Paris, France |
|---|
| 9 | 10 | * Copyright (C)2007,2008 USAGI/WIDE Project |
|---|
| 10 | 11 | * YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is free software; you can redistribute it and/or |
|---|
| 13 | | - * modify it under the terms of the GNU General Public License |
|---|
| 14 | | - * as published by the Free Software Foundation; either version |
|---|
| 15 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 16 | | - * |
|---|
| 17 | 12 | */ |
|---|
| 18 | 13 | |
|---|
| 19 | 14 | #include <linux/uaccess.h> |
|---|
| .. | .. |
|---|
| 88 | 83 | static void ip6mr_free_table(struct mr_table *mrt); |
|---|
| 89 | 84 | |
|---|
| 90 | 85 | static void ip6_mr_forward(struct net *net, struct mr_table *mrt, |
|---|
| 91 | | - struct sk_buff *skb, struct mfc6_cache *cache); |
|---|
| 86 | + struct net_device *dev, struct sk_buff *skb, |
|---|
| 87 | + struct mfc6_cache *cache); |
|---|
| 92 | 88 | static int ip6mr_cache_report(struct mr_table *mrt, struct sk_buff *pkt, |
|---|
| 93 | 89 | mifi_t mifi, int assert); |
|---|
| 94 | 90 | static void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc, |
|---|
| .. | .. |
|---|
| 96 | 92 | static void mrt6msg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt); |
|---|
| 97 | 93 | static int ip6mr_rtm_dumproute(struct sk_buff *skb, |
|---|
| 98 | 94 | struct netlink_callback *cb); |
|---|
| 99 | | -static void mroute_clean_tables(struct mr_table *mrt, bool all); |
|---|
| 95 | +static void mroute_clean_tables(struct mr_table *mrt, int flags); |
|---|
| 100 | 96 | static void ipmr_expire_process(struct timer_list *t); |
|---|
| 101 | 97 | |
|---|
| 102 | 98 | #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES |
|---|
| 103 | 99 | #define ip6mr_for_each_table(mrt, net) \ |
|---|
| 104 | | - list_for_each_entry_rcu(mrt, &net->ipv6.mr6_tables, list) |
|---|
| 100 | + list_for_each_entry_rcu(mrt, &net->ipv6.mr6_tables, list, \ |
|---|
| 101 | + lockdep_rtnl_is_held() || \ |
|---|
| 102 | + list_empty(&net->ipv6.mr6_tables)) |
|---|
| 105 | 103 | |
|---|
| 106 | 104 | static struct mr_table *ip6mr_mr_table_iter(struct net *net, |
|---|
| 107 | 105 | struct mr_table *mrt) |
|---|
| .. | .. |
|---|
| 141 | 139 | .flags = FIB_LOOKUP_NOREF, |
|---|
| 142 | 140 | }; |
|---|
| 143 | 141 | |
|---|
| 142 | + /* update flow if oif or iif point to device enslaved to l3mdev */ |
|---|
| 143 | + l3mdev_update_flow(net, flowi6_to_flowi(flp6)); |
|---|
| 144 | + |
|---|
| 144 | 145 | err = fib_rules_lookup(net->ipv6.mr6_rules_ops, |
|---|
| 145 | 146 | flowi6_to_flowi(flp6), 0, &arg); |
|---|
| 146 | 147 | if (err < 0) |
|---|
| .. | .. |
|---|
| 167 | 168 | return -EINVAL; |
|---|
| 168 | 169 | } |
|---|
| 169 | 170 | |
|---|
| 170 | | - mrt = ip6mr_get_table(rule->fr_net, rule->table); |
|---|
| 171 | + arg->table = fib_rule_get_table(rule, arg); |
|---|
| 172 | + |
|---|
| 173 | + mrt = ip6mr_get_table(rule->fr_net, arg->table); |
|---|
| 171 | 174 | if (!mrt) |
|---|
| 172 | 175 | return -EAGAIN; |
|---|
| 173 | 176 | res->mrt = mrt; |
|---|
| .. | .. |
|---|
| 266 | 269 | rtnl_unlock(); |
|---|
| 267 | 270 | } |
|---|
| 268 | 271 | |
|---|
| 269 | | -static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb) |
|---|
| 272 | +static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb, |
|---|
| 273 | + struct netlink_ext_ack *extack) |
|---|
| 270 | 274 | { |
|---|
| 271 | | - return fib_rules_dump(net, nb, RTNL_FAMILY_IP6MR); |
|---|
| 275 | + return fib_rules_dump(net, nb, RTNL_FAMILY_IP6MR, extack); |
|---|
| 272 | 276 | } |
|---|
| 273 | 277 | |
|---|
| 274 | 278 | static unsigned int ip6mr_rules_seq_read(struct net *net) |
|---|
| .. | .. |
|---|
| 325 | 329 | rtnl_unlock(); |
|---|
| 326 | 330 | } |
|---|
| 327 | 331 | |
|---|
| 328 | | -static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb) |
|---|
| 332 | +static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb, |
|---|
| 333 | + struct netlink_ext_ack *extack) |
|---|
| 329 | 334 | { |
|---|
| 330 | 335 | return 0; |
|---|
| 331 | 336 | } |
|---|
| .. | .. |
|---|
| 351 | 356 | .key_offset = offsetof(struct mfc6_cache, cmparg), |
|---|
| 352 | 357 | .key_len = sizeof(struct mfc6_cache_cmp_arg), |
|---|
| 353 | 358 | .nelem_hint = 3, |
|---|
| 354 | | - .locks_mul = 1, |
|---|
| 355 | 359 | .obj_cmpfn = ip6mr_hash_cmp, |
|---|
| 356 | 360 | .automatic_shrinking = true, |
|---|
| 357 | 361 | }; |
|---|
| .. | .. |
|---|
| 389 | 393 | static void ip6mr_free_table(struct mr_table *mrt) |
|---|
| 390 | 394 | { |
|---|
| 391 | 395 | del_timer_sync(&mrt->ipmr_expire_timer); |
|---|
| 392 | | - mroute_clean_tables(mrt, true); |
|---|
| 396 | + mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC | |
|---|
| 397 | + MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC); |
|---|
| 393 | 398 | rhltable_destroy(&mrt->mfc_hash); |
|---|
| 394 | 399 | kfree(mrt); |
|---|
| 395 | 400 | } |
|---|
| .. | .. |
|---|
| 658 | 663 | return NULL; |
|---|
| 659 | 664 | } |
|---|
| 660 | 665 | |
|---|
| 661 | | - if (dev_open(dev)) |
|---|
| 666 | + if (dev_open(dev, NULL)) |
|---|
| 662 | 667 | goto failure; |
|---|
| 663 | 668 | |
|---|
| 664 | 669 | dev_hold(dev); |
|---|
| .. | .. |
|---|
| 735 | 740 | |
|---|
| 736 | 741 | in6_dev = __in6_dev_get(dev); |
|---|
| 737 | 742 | if (in6_dev) { |
|---|
| 738 | | - in6_dev->cnf.mc_forwarding--; |
|---|
| 743 | + atomic_dec(&in6_dev->cnf.mc_forwarding); |
|---|
| 739 | 744 | inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, |
|---|
| 740 | 745 | NETCONFA_MC_FORWARDING, |
|---|
| 741 | 746 | dev->ifindex, &in6_dev->cnf); |
|---|
| .. | .. |
|---|
| 903 | 908 | |
|---|
| 904 | 909 | in6_dev = __in6_dev_get(dev); |
|---|
| 905 | 910 | if (in6_dev) { |
|---|
| 906 | | - in6_dev->cnf.mc_forwarding++; |
|---|
| 911 | + atomic_inc(&in6_dev->cnf.mc_forwarding); |
|---|
| 907 | 912 | inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, |
|---|
| 908 | 913 | NETCONFA_MC_FORWARDING, |
|---|
| 909 | 914 | dev->ifindex, &in6_dev->cnf); |
|---|
| .. | .. |
|---|
| 1023 | 1028 | } |
|---|
| 1024 | 1029 | rtnl_unicast(skb, net, NETLINK_CB(skb).portid); |
|---|
| 1025 | 1030 | } else |
|---|
| 1026 | | - ip6_mr_forward(net, mrt, skb, c); |
|---|
| 1031 | + ip6_mr_forward(net, mrt, skb->dev, skb, c); |
|---|
| 1027 | 1032 | } |
|---|
| 1028 | 1033 | } |
|---|
| 1029 | 1034 | |
|---|
| .. | .. |
|---|
| 1129 | 1134 | |
|---|
| 1130 | 1135 | /* Queue a packet for resolution. It gets locked cache entry! */ |
|---|
| 1131 | 1136 | static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi, |
|---|
| 1132 | | - struct sk_buff *skb) |
|---|
| 1137 | + struct sk_buff *skb, struct net_device *dev) |
|---|
| 1133 | 1138 | { |
|---|
| 1134 | 1139 | struct mfc6_cache *c; |
|---|
| 1135 | 1140 | bool found = false; |
|---|
| .. | .. |
|---|
| 1149 | 1154 | * Create a new entry if allowable |
|---|
| 1150 | 1155 | */ |
|---|
| 1151 | 1156 | |
|---|
| 1152 | | - if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 || |
|---|
| 1153 | | - (c = ip6mr_cache_alloc_unres()) == NULL) { |
|---|
| 1157 | + c = ip6mr_cache_alloc_unres(); |
|---|
| 1158 | + if (!c) { |
|---|
| 1154 | 1159 | spin_unlock_bh(&mfc_unres_lock); |
|---|
| 1155 | 1160 | |
|---|
| 1156 | 1161 | kfree_skb(skb); |
|---|
| .. | .. |
|---|
| 1189 | 1194 | kfree_skb(skb); |
|---|
| 1190 | 1195 | err = -ENOBUFS; |
|---|
| 1191 | 1196 | } else { |
|---|
| 1197 | + if (dev) { |
|---|
| 1198 | + skb->dev = dev; |
|---|
| 1199 | + skb->skb_iif = dev->ifindex; |
|---|
| 1200 | + } |
|---|
| 1192 | 1201 | skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb); |
|---|
| 1193 | 1202 | err = 0; |
|---|
| 1194 | 1203 | } |
|---|
| .. | .. |
|---|
| 1253 | 1262 | return net->ipv6.ipmr_seq + ip6mr_rules_seq_read(net); |
|---|
| 1254 | 1263 | } |
|---|
| 1255 | 1264 | |
|---|
| 1256 | | -static int ip6mr_dump(struct net *net, struct notifier_block *nb) |
|---|
| 1265 | +static int ip6mr_dump(struct net *net, struct notifier_block *nb, |
|---|
| 1266 | + struct netlink_ext_ack *extack) |
|---|
| 1257 | 1267 | { |
|---|
| 1258 | 1268 | return mr_dump(net, nb, RTNL_FAMILY_IP6MR, ip6mr_rules_dump, |
|---|
| 1259 | | - ip6mr_mr_table_iter, &mrt_lock); |
|---|
| 1269 | + ip6mr_mr_table_iter, &mrt_lock, extack); |
|---|
| 1260 | 1270 | } |
|---|
| 1261 | 1271 | |
|---|
| 1262 | 1272 | static struct notifier_block ip6_mr_notifier = { |
|---|
| .. | .. |
|---|
| 1488 | 1498 | * Close the multicast socket, and clear the vif tables etc |
|---|
| 1489 | 1499 | */ |
|---|
| 1490 | 1500 | |
|---|
| 1491 | | -static void mroute_clean_tables(struct mr_table *mrt, bool all) |
|---|
| 1501 | +static void mroute_clean_tables(struct mr_table *mrt, int flags) |
|---|
| 1492 | 1502 | { |
|---|
| 1493 | 1503 | struct mr_mfc *c, *tmp; |
|---|
| 1494 | 1504 | LIST_HEAD(list); |
|---|
| 1495 | 1505 | int i; |
|---|
| 1496 | 1506 | |
|---|
| 1497 | 1507 | /* Shut down all active vif entries */ |
|---|
| 1498 | | - for (i = 0; i < mrt->maxvif; i++) { |
|---|
| 1499 | | - if (!all && (mrt->vif_table[i].flags & VIFF_STATIC)) |
|---|
| 1500 | | - continue; |
|---|
| 1501 | | - mif6_delete(mrt, i, 0, &list); |
|---|
| 1508 | + if (flags & (MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC)) { |
|---|
| 1509 | + for (i = 0; i < mrt->maxvif; i++) { |
|---|
| 1510 | + if (((mrt->vif_table[i].flags & VIFF_STATIC) && |
|---|
| 1511 | + !(flags & MRT6_FLUSH_MIFS_STATIC)) || |
|---|
| 1512 | + (!(mrt->vif_table[i].flags & VIFF_STATIC) && !(flags & MRT6_FLUSH_MIFS))) |
|---|
| 1513 | + continue; |
|---|
| 1514 | + mif6_delete(mrt, i, 0, &list); |
|---|
| 1515 | + } |
|---|
| 1516 | + unregister_netdevice_many(&list); |
|---|
| 1502 | 1517 | } |
|---|
| 1503 | | - unregister_netdevice_many(&list); |
|---|
| 1504 | 1518 | |
|---|
| 1505 | 1519 | /* Wipe the cache */ |
|---|
| 1506 | | - list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) { |
|---|
| 1507 | | - if (!all && (c->mfc_flags & MFC_STATIC)) |
|---|
| 1508 | | - continue; |
|---|
| 1509 | | - rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params); |
|---|
| 1510 | | - list_del_rcu(&c->list); |
|---|
| 1511 | | - call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net), |
|---|
| 1512 | | - FIB_EVENT_ENTRY_DEL, |
|---|
| 1513 | | - (struct mfc6_cache *)c, mrt->id); |
|---|
| 1514 | | - mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE); |
|---|
| 1515 | | - mr_cache_put(c); |
|---|
| 1520 | + if (flags & (MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC)) { |
|---|
| 1521 | + list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) { |
|---|
| 1522 | + if (((c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC_STATIC)) || |
|---|
| 1523 | + (!(c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC))) |
|---|
| 1524 | + continue; |
|---|
| 1525 | + rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params); |
|---|
| 1526 | + list_del_rcu(&c->list); |
|---|
| 1527 | + call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net), |
|---|
| 1528 | + FIB_EVENT_ENTRY_DEL, |
|---|
| 1529 | + (struct mfc6_cache *)c, mrt->id); |
|---|
| 1530 | + mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE); |
|---|
| 1531 | + mr_cache_put(c); |
|---|
| 1532 | + } |
|---|
| 1516 | 1533 | } |
|---|
| 1517 | 1534 | |
|---|
| 1518 | | - if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { |
|---|
| 1519 | | - spin_lock_bh(&mfc_unres_lock); |
|---|
| 1520 | | - list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) { |
|---|
| 1521 | | - list_del(&c->list); |
|---|
| 1522 | | - mr6_netlink_event(mrt, (struct mfc6_cache *)c, |
|---|
| 1523 | | - RTM_DELROUTE); |
|---|
| 1524 | | - ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c); |
|---|
| 1535 | + if (flags & MRT6_FLUSH_MFC) { |
|---|
| 1536 | + if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { |
|---|
| 1537 | + spin_lock_bh(&mfc_unres_lock); |
|---|
| 1538 | + list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) { |
|---|
| 1539 | + list_del(&c->list); |
|---|
| 1540 | + mr6_netlink_event(mrt, (struct mfc6_cache *)c, |
|---|
| 1541 | + RTM_DELROUTE); |
|---|
| 1542 | + ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c); |
|---|
| 1543 | + } |
|---|
| 1544 | + spin_unlock_bh(&mfc_unres_lock); |
|---|
| 1525 | 1545 | } |
|---|
| 1526 | | - spin_unlock_bh(&mfc_unres_lock); |
|---|
| 1527 | 1546 | } |
|---|
| 1528 | 1547 | } |
|---|
| 1529 | 1548 | |
|---|
| .. | .. |
|---|
| 1539 | 1558 | } else { |
|---|
| 1540 | 1559 | rcu_assign_pointer(mrt->mroute_sk, sk); |
|---|
| 1541 | 1560 | sock_set_flag(sk, SOCK_RCU_FREE); |
|---|
| 1542 | | - net->ipv6.devconf_all->mc_forwarding++; |
|---|
| 1561 | + atomic_inc(&net->ipv6.devconf_all->mc_forwarding); |
|---|
| 1543 | 1562 | } |
|---|
| 1544 | 1563 | write_unlock_bh(&mrt_lock); |
|---|
| 1545 | 1564 | |
|---|
| .. | .. |
|---|
| 1572 | 1591 | * so the RCU grace period before sk freeing |
|---|
| 1573 | 1592 | * is guaranteed by sk_destruct() |
|---|
| 1574 | 1593 | */ |
|---|
| 1575 | | - net->ipv6.devconf_all->mc_forwarding--; |
|---|
| 1594 | + atomic_dec(&net->ipv6.devconf_all->mc_forwarding); |
|---|
| 1576 | 1595 | write_unlock_bh(&mrt_lock); |
|---|
| 1577 | 1596 | inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, |
|---|
| 1578 | 1597 | NETCONFA_MC_FORWARDING, |
|---|
| 1579 | 1598 | NETCONFA_IFINDEX_ALL, |
|---|
| 1580 | 1599 | net->ipv6.devconf_all); |
|---|
| 1581 | 1600 | |
|---|
| 1582 | | - mroute_clean_tables(mrt, false); |
|---|
| 1601 | + mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MFC); |
|---|
| 1583 | 1602 | err = 0; |
|---|
| 1584 | 1603 | break; |
|---|
| 1585 | 1604 | } |
|---|
| .. | .. |
|---|
| 1612 | 1631 | * MOSPF/PIM router set up we can clean this up. |
|---|
| 1613 | 1632 | */ |
|---|
| 1614 | 1633 | |
|---|
| 1615 | | -int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen) |
|---|
| 1634 | +int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, |
|---|
| 1635 | + unsigned int optlen) |
|---|
| 1616 | 1636 | { |
|---|
| 1617 | 1637 | int ret, parent = 0; |
|---|
| 1618 | 1638 | struct mif6ctl vif; |
|---|
| .. | .. |
|---|
| 1648 | 1668 | case MRT6_ADD_MIF: |
|---|
| 1649 | 1669 | if (optlen < sizeof(vif)) |
|---|
| 1650 | 1670 | return -EINVAL; |
|---|
| 1651 | | - if (copy_from_user(&vif, optval, sizeof(vif))) |
|---|
| 1671 | + if (copy_from_sockptr(&vif, optval, sizeof(vif))) |
|---|
| 1652 | 1672 | return -EFAULT; |
|---|
| 1653 | 1673 | if (vif.mif6c_mifi >= MAXMIFS) |
|---|
| 1654 | 1674 | return -ENFILE; |
|---|
| .. | .. |
|---|
| 1661 | 1681 | case MRT6_DEL_MIF: |
|---|
| 1662 | 1682 | if (optlen < sizeof(mifi_t)) |
|---|
| 1663 | 1683 | return -EINVAL; |
|---|
| 1664 | | - if (copy_from_user(&mifi, optval, sizeof(mifi_t))) |
|---|
| 1684 | + if (copy_from_sockptr(&mifi, optval, sizeof(mifi_t))) |
|---|
| 1665 | 1685 | return -EFAULT; |
|---|
| 1666 | 1686 | rtnl_lock(); |
|---|
| 1667 | 1687 | ret = mif6_delete(mrt, mifi, 0, NULL); |
|---|
| .. | .. |
|---|
| 1675 | 1695 | case MRT6_ADD_MFC: |
|---|
| 1676 | 1696 | case MRT6_DEL_MFC: |
|---|
| 1677 | 1697 | parent = -1; |
|---|
| 1678 | | - /* fall through */ |
|---|
| 1698 | + fallthrough; |
|---|
| 1679 | 1699 | case MRT6_ADD_MFC_PROXY: |
|---|
| 1680 | 1700 | case MRT6_DEL_MFC_PROXY: |
|---|
| 1681 | 1701 | if (optlen < sizeof(mfc)) |
|---|
| 1682 | 1702 | return -EINVAL; |
|---|
| 1683 | | - if (copy_from_user(&mfc, optval, sizeof(mfc))) |
|---|
| 1703 | + if (copy_from_sockptr(&mfc, optval, sizeof(mfc))) |
|---|
| 1684 | 1704 | return -EFAULT; |
|---|
| 1685 | 1705 | if (parent == 0) |
|---|
| 1686 | 1706 | parent = mfc.mf6cc_parent; |
|---|
| .. | .. |
|---|
| 1695 | 1715 | rtnl_unlock(); |
|---|
| 1696 | 1716 | return ret; |
|---|
| 1697 | 1717 | |
|---|
| 1718 | + case MRT6_FLUSH: |
|---|
| 1719 | + { |
|---|
| 1720 | + int flags; |
|---|
| 1721 | + |
|---|
| 1722 | + if (optlen != sizeof(flags)) |
|---|
| 1723 | + return -EINVAL; |
|---|
| 1724 | + if (copy_from_sockptr(&flags, optval, sizeof(flags))) |
|---|
| 1725 | + return -EFAULT; |
|---|
| 1726 | + rtnl_lock(); |
|---|
| 1727 | + mroute_clean_tables(mrt, flags); |
|---|
| 1728 | + rtnl_unlock(); |
|---|
| 1729 | + return 0; |
|---|
| 1730 | + } |
|---|
| 1731 | + |
|---|
| 1698 | 1732 | /* |
|---|
| 1699 | 1733 | * Control PIM assert (to activate pim will activate assert) |
|---|
| 1700 | 1734 | */ |
|---|
| .. | .. |
|---|
| 1704 | 1738 | |
|---|
| 1705 | 1739 | if (optlen != sizeof(v)) |
|---|
| 1706 | 1740 | return -EINVAL; |
|---|
| 1707 | | - if (get_user(v, (int __user *)optval)) |
|---|
| 1741 | + if (copy_from_sockptr(&v, optval, sizeof(v))) |
|---|
| 1708 | 1742 | return -EFAULT; |
|---|
| 1709 | 1743 | mrt->mroute_do_assert = v; |
|---|
| 1710 | 1744 | return 0; |
|---|
| .. | .. |
|---|
| 1717 | 1751 | |
|---|
| 1718 | 1752 | if (optlen != sizeof(v)) |
|---|
| 1719 | 1753 | return -EINVAL; |
|---|
| 1720 | | - if (get_user(v, (int __user *)optval)) |
|---|
| 1754 | + if (copy_from_sockptr(&v, optval, sizeof(v))) |
|---|
| 1721 | 1755 | return -EFAULT; |
|---|
| 1722 | 1756 | v = !!v; |
|---|
| 1723 | 1757 | rtnl_lock(); |
|---|
| .. | .. |
|---|
| 1738 | 1772 | |
|---|
| 1739 | 1773 | if (optlen != sizeof(u32)) |
|---|
| 1740 | 1774 | return -EINVAL; |
|---|
| 1741 | | - if (get_user(v, (u32 __user *)optval)) |
|---|
| 1775 | + if (copy_from_sockptr(&v, optval, sizeof(v))) |
|---|
| 1742 | 1776 | return -EFAULT; |
|---|
| 1743 | 1777 | /* "pim6reg%u" should not exceed 16 bytes (IFNAMSIZ) */ |
|---|
| 1744 | 1778 | if (v != RT_TABLE_DEFAULT && v >= 100000000) |
|---|
| .. | .. |
|---|
| 1968 | 2002 | */ |
|---|
| 1969 | 2003 | |
|---|
| 1970 | 2004 | static int ip6mr_forward2(struct net *net, struct mr_table *mrt, |
|---|
| 1971 | | - struct sk_buff *skb, struct mfc6_cache *c, int vifi) |
|---|
| 2005 | + struct sk_buff *skb, int vifi) |
|---|
| 1972 | 2006 | { |
|---|
| 1973 | 2007 | struct ipv6hdr *ipv6h; |
|---|
| 1974 | 2008 | struct vif_device *vif = &mrt->vif_table[vifi]; |
|---|
| .. | .. |
|---|
| 2053 | 2087 | } |
|---|
| 2054 | 2088 | |
|---|
| 2055 | 2089 | static void ip6_mr_forward(struct net *net, struct mr_table *mrt, |
|---|
| 2056 | | - struct sk_buff *skb, struct mfc6_cache *c) |
|---|
| 2090 | + struct net_device *dev, struct sk_buff *skb, |
|---|
| 2091 | + struct mfc6_cache *c) |
|---|
| 2057 | 2092 | { |
|---|
| 2058 | 2093 | int psend = -1; |
|---|
| 2059 | 2094 | int vif, ct; |
|---|
| 2060 | | - int true_vifi = ip6mr_find_vif(mrt, skb->dev); |
|---|
| 2095 | + int true_vifi = ip6mr_find_vif(mrt, dev); |
|---|
| 2061 | 2096 | |
|---|
| 2062 | 2097 | vif = c->_c.mfc_parent; |
|---|
| 2063 | 2098 | c->_c.mfc_un.res.pkt++; |
|---|
| .. | .. |
|---|
| 2083 | 2118 | /* |
|---|
| 2084 | 2119 | * Wrong interface: drop packet and (maybe) send PIM assert. |
|---|
| 2085 | 2120 | */ |
|---|
| 2086 | | - if (mrt->vif_table[vif].dev != skb->dev) { |
|---|
| 2121 | + if (mrt->vif_table[vif].dev != dev) { |
|---|
| 2087 | 2122 | c->_c.mfc_un.res.wrong_if++; |
|---|
| 2088 | 2123 | |
|---|
| 2089 | 2124 | if (true_vifi >= 0 && mrt->mroute_do_assert && |
|---|
| .. | .. |
|---|
| 2133 | 2168 | if (psend != -1) { |
|---|
| 2134 | 2169 | struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); |
|---|
| 2135 | 2170 | if (skb2) |
|---|
| 2136 | | - ip6mr_forward2(net, mrt, skb2, |
|---|
| 2137 | | - c, psend); |
|---|
| 2171 | + ip6mr_forward2(net, mrt, skb2, psend); |
|---|
| 2138 | 2172 | } |
|---|
| 2139 | 2173 | psend = ct; |
|---|
| 2140 | 2174 | } |
|---|
| 2141 | 2175 | } |
|---|
| 2142 | 2176 | last_forward: |
|---|
| 2143 | 2177 | if (psend != -1) { |
|---|
| 2144 | | - ip6mr_forward2(net, mrt, skb, c, psend); |
|---|
| 2178 | + ip6mr_forward2(net, mrt, skb, psend); |
|---|
| 2145 | 2179 | return; |
|---|
| 2146 | 2180 | } |
|---|
| 2147 | 2181 | |
|---|
| .. | .. |
|---|
| 2164 | 2198 | .flowi6_mark = skb->mark, |
|---|
| 2165 | 2199 | }; |
|---|
| 2166 | 2200 | int err; |
|---|
| 2201 | + struct net_device *dev; |
|---|
| 2202 | + |
|---|
| 2203 | + /* skb->dev passed in is the master dev for vrfs. |
|---|
| 2204 | + * Get the proper interface that does have a vif associated with it. |
|---|
| 2205 | + */ |
|---|
| 2206 | + dev = skb->dev; |
|---|
| 2207 | + if (netif_is_l3_master(skb->dev)) { |
|---|
| 2208 | + dev = dev_get_by_index_rcu(net, IPCB(skb)->iif); |
|---|
| 2209 | + if (!dev) { |
|---|
| 2210 | + kfree_skb(skb); |
|---|
| 2211 | + return -ENODEV; |
|---|
| 2212 | + } |
|---|
| 2213 | + } |
|---|
| 2167 | 2214 | |
|---|
| 2168 | 2215 | err = ip6mr_fib_lookup(net, &fl6, &mrt); |
|---|
| 2169 | 2216 | if (err < 0) { |
|---|
| .. | .. |
|---|
| 2175 | 2222 | cache = ip6mr_cache_find(mrt, |
|---|
| 2176 | 2223 | &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr); |
|---|
| 2177 | 2224 | if (!cache) { |
|---|
| 2178 | | - int vif = ip6mr_find_vif(mrt, skb->dev); |
|---|
| 2225 | + int vif = ip6mr_find_vif(mrt, dev); |
|---|
| 2179 | 2226 | |
|---|
| 2180 | 2227 | if (vif >= 0) |
|---|
| 2181 | 2228 | cache = ip6mr_cache_find_any(mrt, |
|---|
| .. | .. |
|---|
| 2189 | 2236 | if (!cache) { |
|---|
| 2190 | 2237 | int vif; |
|---|
| 2191 | 2238 | |
|---|
| 2192 | | - vif = ip6mr_find_vif(mrt, skb->dev); |
|---|
| 2239 | + vif = ip6mr_find_vif(mrt, dev); |
|---|
| 2193 | 2240 | if (vif >= 0) { |
|---|
| 2194 | | - int err = ip6mr_cache_unresolved(mrt, vif, skb); |
|---|
| 2241 | + int err = ip6mr_cache_unresolved(mrt, vif, skb, dev); |
|---|
| 2195 | 2242 | read_unlock(&mrt_lock); |
|---|
| 2196 | 2243 | |
|---|
| 2197 | 2244 | return err; |
|---|
| .. | .. |
|---|
| 2201 | 2248 | return -ENODEV; |
|---|
| 2202 | 2249 | } |
|---|
| 2203 | 2250 | |
|---|
| 2204 | | - ip6_mr_forward(net, mrt, skb, cache); |
|---|
| 2251 | + ip6_mr_forward(net, mrt, dev, skb, cache); |
|---|
| 2205 | 2252 | |
|---|
| 2206 | 2253 | read_unlock(&mrt_lock); |
|---|
| 2207 | 2254 | |
|---|
| .. | .. |
|---|
| 2267 | 2314 | iph->saddr = rt->rt6i_src.addr; |
|---|
| 2268 | 2315 | iph->daddr = rt->rt6i_dst.addr; |
|---|
| 2269 | 2316 | |
|---|
| 2270 | | - err = ip6mr_cache_unresolved(mrt, vif, skb2); |
|---|
| 2317 | + err = ip6mr_cache_unresolved(mrt, vif, skb2, dev); |
|---|
| 2271 | 2318 | read_unlock(&mrt_lock); |
|---|
| 2272 | 2319 | |
|---|
| 2273 | 2320 | return err; |
|---|
| .. | .. |
|---|
| 2443 | 2490 | |
|---|
| 2444 | 2491 | static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) |
|---|
| 2445 | 2492 | { |
|---|
| 2493 | + const struct nlmsghdr *nlh = cb->nlh; |
|---|
| 2494 | + struct fib_dump_filter filter = {}; |
|---|
| 2495 | + int err; |
|---|
| 2496 | + |
|---|
| 2497 | + if (cb->strict_check) { |
|---|
| 2498 | + err = ip_valid_fib_dump_req(sock_net(skb->sk), nlh, |
|---|
| 2499 | + &filter, cb); |
|---|
| 2500 | + if (err < 0) |
|---|
| 2501 | + return err; |
|---|
| 2502 | + } |
|---|
| 2503 | + |
|---|
| 2504 | + if (filter.table_id) { |
|---|
| 2505 | + struct mr_table *mrt; |
|---|
| 2506 | + |
|---|
| 2507 | + mrt = ip6mr_get_table(sock_net(skb->sk), filter.table_id); |
|---|
| 2508 | + if (!mrt) { |
|---|
| 2509 | + if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR) |
|---|
| 2510 | + return skb->len; |
|---|
| 2511 | + |
|---|
| 2512 | + NL_SET_ERR_MSG_MOD(cb->extack, "MR table does not exist"); |
|---|
| 2513 | + return -ENOENT; |
|---|
| 2514 | + } |
|---|
| 2515 | + err = mr_table_dump(mrt, skb, cb, _ip6mr_fill_mroute, |
|---|
| 2516 | + &mfc_unres_lock, &filter); |
|---|
| 2517 | + return skb->len ? : err; |
|---|
| 2518 | + } |
|---|
| 2519 | + |
|---|
| 2446 | 2520 | return mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter, |
|---|
| 2447 | | - _ip6mr_fill_mroute, &mfc_unres_lock); |
|---|
| 2521 | + _ip6mr_fill_mroute, &mfc_unres_lock, &filter); |
|---|
| 2448 | 2522 | } |
|---|