.. | .. |
---|
| 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; |
---|