| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Neighbour Discovery for IPv6 |
|---|
| 3 | 4 | * Linux INET6 implementation |
|---|
| .. | .. |
|---|
| 5 | 6 | * Authors: |
|---|
| 6 | 7 | * Pedro Roque <roque@di.fc.ul.pt> |
|---|
| 7 | 8 | * Mike Shaver <shaver@ingenia.com> |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is free software; you can redistribute it and/or |
|---|
| 10 | | - * modify it under the terms of the GNU General Public License |
|---|
| 11 | | - * as published by the Free Software Foundation; either version |
|---|
| 12 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 13 | 9 | */ |
|---|
| 14 | 10 | |
|---|
| 15 | 11 | /* |
|---|
| .. | .. |
|---|
| 77 | 73 | const struct net_device *dev, |
|---|
| 78 | 74 | __u32 *hash_rnd); |
|---|
| 79 | 75 | static bool ndisc_key_eq(const struct neighbour *neigh, const void *pkey); |
|---|
| 76 | +static bool ndisc_allow_add(const struct net_device *dev, |
|---|
| 77 | + struct netlink_ext_ack *extack); |
|---|
| 80 | 78 | static int ndisc_constructor(struct neighbour *neigh); |
|---|
| 81 | 79 | static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb); |
|---|
| 82 | 80 | static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb); |
|---|
| 83 | 81 | static int pndisc_constructor(struct pneigh_entry *n); |
|---|
| 84 | 82 | static void pndisc_destructor(struct pneigh_entry *n); |
|---|
| 85 | 83 | static void pndisc_redo(struct sk_buff *skb); |
|---|
| 84 | +static int ndisc_is_multicast(const void *pkey); |
|---|
| 86 | 85 | |
|---|
| 87 | 86 | static const struct neigh_ops ndisc_generic_ops = { |
|---|
| 88 | 87 | .family = AF_INET6, |
|---|
| .. | .. |
|---|
| 117 | 116 | .pconstructor = pndisc_constructor, |
|---|
| 118 | 117 | .pdestructor = pndisc_destructor, |
|---|
| 119 | 118 | .proxy_redo = pndisc_redo, |
|---|
| 119 | + .is_multicast = ndisc_is_multicast, |
|---|
| 120 | + .allow_add = ndisc_allow_add, |
|---|
| 120 | 121 | .id = "ndisc_cache", |
|---|
| 121 | 122 | .parms = { |
|---|
| 122 | 123 | .tbl = &nd_tbl, |
|---|
| .. | .. |
|---|
| 195 | 196 | static inline int ndisc_is_useropt(const struct net_device *dev, |
|---|
| 196 | 197 | struct nd_opt_hdr *opt) |
|---|
| 197 | 198 | { |
|---|
| 198 | | - return opt->nd_opt_type == ND_OPT_RDNSS || |
|---|
| 199 | + return opt->nd_opt_type == ND_OPT_PREFIX_INFO || |
|---|
| 200 | + opt->nd_opt_type == ND_OPT_RDNSS || |
|---|
| 199 | 201 | opt->nd_opt_type == ND_OPT_DNSSL || |
|---|
| 200 | 202 | opt->nd_opt_type == ND_OPT_CAPTIVE_PORTAL || |
|---|
| 201 | 203 | opt->nd_opt_type == ND_OPT_PREF64 || |
|---|
| .. | .. |
|---|
| 392 | 394 | return; |
|---|
| 393 | 395 | addrconf_addr_solict_mult(addr, &maddr); |
|---|
| 394 | 396 | ipv6_dev_mc_dec(dev, &maddr); |
|---|
| 397 | +} |
|---|
| 398 | + |
|---|
| 399 | +/* called with rtnl held */ |
|---|
| 400 | +static bool ndisc_allow_add(const struct net_device *dev, |
|---|
| 401 | + struct netlink_ext_ack *extack) |
|---|
| 402 | +{ |
|---|
| 403 | + struct inet6_dev *idev = __in6_dev_get(dev); |
|---|
| 404 | + |
|---|
| 405 | + if (!idev || idev->cnf.disable_ipv6) { |
|---|
| 406 | + NL_SET_ERR_MSG(extack, "IPv6 is disabled on this device"); |
|---|
| 407 | + return false; |
|---|
| 408 | + } |
|---|
| 409 | + |
|---|
| 410 | + return true; |
|---|
| 395 | 411 | } |
|---|
| 396 | 412 | |
|---|
| 397 | 413 | static struct sk_buff *ndisc_alloc_skb(struct net_device *dev, |
|---|
| .. | .. |
|---|
| 1274 | 1290 | !in6_dev->cnf.accept_ra_rtr_pref) |
|---|
| 1275 | 1291 | pref = ICMPV6_ROUTER_PREF_MEDIUM; |
|---|
| 1276 | 1292 | #endif |
|---|
| 1277 | | - |
|---|
| 1293 | + /* routes added from RAs do not use nexthop objects */ |
|---|
| 1278 | 1294 | rt = rt6_get_dflt_router(net, &ipv6_hdr(skb)->saddr, skb->dev); |
|---|
| 1279 | | - |
|---|
| 1280 | 1295 | if (rt) { |
|---|
| 1281 | | - neigh = ip6_neigh_lookup(&rt->fib6_nh.nh_gw, |
|---|
| 1282 | | - rt->fib6_nh.nh_dev, NULL, |
|---|
| 1296 | + neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6, |
|---|
| 1297 | + rt->fib6_nh->fib_nh_dev, NULL, |
|---|
| 1283 | 1298 | &ipv6_hdr(skb)->saddr); |
|---|
| 1284 | 1299 | if (!neigh) { |
|---|
| 1285 | 1300 | ND_PRINTK(0, err, |
|---|
| .. | .. |
|---|
| 1290 | 1305 | } |
|---|
| 1291 | 1306 | } |
|---|
| 1292 | 1307 | if (rt && lifetime == 0) { |
|---|
| 1293 | | - ip6_del_rt(net, rt); |
|---|
| 1308 | + ip6_del_rt(net, rt, false); |
|---|
| 1294 | 1309 | rt = NULL; |
|---|
| 1295 | 1310 | } |
|---|
| 1296 | 1311 | |
|---|
| .. | .. |
|---|
| 1308 | 1323 | return; |
|---|
| 1309 | 1324 | } |
|---|
| 1310 | 1325 | |
|---|
| 1311 | | - neigh = ip6_neigh_lookup(&rt->fib6_nh.nh_gw, |
|---|
| 1312 | | - rt->fib6_nh.nh_dev, NULL, |
|---|
| 1326 | + neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6, |
|---|
| 1327 | + rt->fib6_nh->fib_nh_dev, NULL, |
|---|
| 1313 | 1328 | &ipv6_hdr(skb)->saddr); |
|---|
| 1314 | 1329 | if (!neigh) { |
|---|
| 1315 | 1330 | ND_PRINTK(0, err, |
|---|
| .. | .. |
|---|
| 1347 | 1362 | |
|---|
| 1348 | 1363 | if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) { |
|---|
| 1349 | 1364 | rtime = (rtime*HZ)/1000; |
|---|
| 1350 | | - if (rtime < HZ/10) |
|---|
| 1351 | | - rtime = HZ/10; |
|---|
| 1365 | + if (rtime < HZ/100) |
|---|
| 1366 | + rtime = HZ/100; |
|---|
| 1352 | 1367 | NEIGH_VAR_SET(in6_dev->nd_parms, RETRANS_TIME, rtime); |
|---|
| 1353 | 1368 | in6_dev->tstamp = jiffies; |
|---|
| 1354 | 1369 | send_ifinfo_notify = true; |
|---|
| .. | .. |
|---|
| 1535 | 1550 | |
|---|
| 1536 | 1551 | if (!ndopts.nd_opts_rh) { |
|---|
| 1537 | 1552 | ip6_redirect_no_header(skb, dev_net(skb->dev), |
|---|
| 1538 | | - skb->dev->ifindex, 0); |
|---|
| 1553 | + skb->dev->ifindex); |
|---|
| 1539 | 1554 | return; |
|---|
| 1540 | 1555 | } |
|---|
| 1541 | 1556 | |
|---|
| .. | .. |
|---|
| 1694 | 1709 | kfree_skb(skb); |
|---|
| 1695 | 1710 | } |
|---|
| 1696 | 1711 | |
|---|
| 1712 | +static int ndisc_is_multicast(const void *pkey) |
|---|
| 1713 | +{ |
|---|
| 1714 | + return ipv6_addr_is_multicast((struct in6_addr *)pkey); |
|---|
| 1715 | +} |
|---|
| 1716 | + |
|---|
| 1697 | 1717 | static bool ndisc_suppress_frag_ndisc(struct sk_buff *skb) |
|---|
| 1698 | 1718 | { |
|---|
| 1699 | 1719 | struct inet6_dev *idev = __in6_dev_get(skb->dev); |
|---|
| .. | .. |
|---|
| 1771 | 1791 | case NETDEV_CHANGEADDR: |
|---|
| 1772 | 1792 | neigh_changeaddr(&nd_tbl, dev); |
|---|
| 1773 | 1793 | fib6_run_gc(0, net, false); |
|---|
| 1774 | | - /* fallthrough */ |
|---|
| 1794 | + fallthrough; |
|---|
| 1775 | 1795 | case NETDEV_UP: |
|---|
| 1776 | 1796 | idev = in6_dev_get(dev); |
|---|
| 1777 | 1797 | if (!idev) |
|---|
| .. | .. |
|---|
| 1785 | 1805 | change_info = ptr; |
|---|
| 1786 | 1806 | if (change_info->flags_changed & IFF_NOARP) |
|---|
| 1787 | 1807 | neigh_changeaddr(&nd_tbl, dev); |
|---|
| 1808 | + if (!netif_carrier_ok(dev)) |
|---|
| 1809 | + neigh_carrier_down(&nd_tbl, dev); |
|---|
| 1788 | 1810 | break; |
|---|
| 1789 | 1811 | case NETDEV_DOWN: |
|---|
| 1790 | 1812 | neigh_ifdown(&nd_tbl, dev); |
|---|
| .. | .. |
|---|
| 1821 | 1843 | } |
|---|
| 1822 | 1844 | } |
|---|
| 1823 | 1845 | |
|---|
| 1824 | | -int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) |
|---|
| 1846 | +int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void *buffer, |
|---|
| 1847 | + size_t *lenp, loff_t *ppos) |
|---|
| 1825 | 1848 | { |
|---|
| 1826 | 1849 | struct net_device *dev = ctl->extra1; |
|---|
| 1827 | 1850 | struct inet6_dev *idev; |
|---|