diff options
Diffstat (limited to 'net/ipv6/ndisc.c')
| -rw-r--r-- | net/ipv6/ndisc.c | 129 |
1 files changed, 10 insertions, 119 deletions
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 54f62d3b8dd6..ff36194a71aa 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c | |||
| @@ -143,40 +143,6 @@ struct neigh_table nd_tbl = { | |||
| 143 | .gc_thresh3 = 1024, | 143 | .gc_thresh3 = 1024, |
| 144 | }; | 144 | }; |
| 145 | 145 | ||
| 146 | /* ND options */ | ||
| 147 | struct ndisc_options { | ||
| 148 | struct nd_opt_hdr *nd_opt_array[__ND_OPT_ARRAY_MAX]; | ||
| 149 | #ifdef CONFIG_IPV6_ROUTE_INFO | ||
| 150 | struct nd_opt_hdr *nd_opts_ri; | ||
| 151 | struct nd_opt_hdr *nd_opts_ri_end; | ||
| 152 | #endif | ||
| 153 | struct nd_opt_hdr *nd_useropts; | ||
| 154 | struct nd_opt_hdr *nd_useropts_end; | ||
| 155 | }; | ||
| 156 | |||
| 157 | #define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR] | ||
| 158 | #define nd_opts_tgt_lladdr nd_opt_array[ND_OPT_TARGET_LL_ADDR] | ||
| 159 | #define nd_opts_pi nd_opt_array[ND_OPT_PREFIX_INFO] | ||
| 160 | #define nd_opts_pi_end nd_opt_array[__ND_OPT_PREFIX_INFO_END] | ||
| 161 | #define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR] | ||
| 162 | #define nd_opts_mtu nd_opt_array[ND_OPT_MTU] | ||
| 163 | |||
| 164 | #define NDISC_OPT_SPACE(len) (((len)+2+7)&~7) | ||
| 165 | |||
| 166 | /* | ||
| 167 | * Return the padding between the option length and the start of the | ||
| 168 | * link addr. Currently only IP-over-InfiniBand needs this, although | ||
| 169 | * if RFC 3831 IPv6-over-Fibre Channel is ever implemented it may | ||
| 170 | * also need a pad of 2. | ||
| 171 | */ | ||
| 172 | static int ndisc_addr_option_pad(unsigned short type) | ||
| 173 | { | ||
| 174 | switch (type) { | ||
| 175 | case ARPHRD_INFINIBAND: return 2; | ||
| 176 | default: return 0; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | static inline int ndisc_opt_addr_space(struct net_device *dev) | 146 | static inline int ndisc_opt_addr_space(struct net_device *dev) |
| 181 | { | 147 | { |
| 182 | return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type)); | 148 | return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type)); |
| @@ -233,8 +199,8 @@ static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur, | |||
| 233 | return cur <= end && ndisc_is_useropt(cur) ? cur : NULL; | 199 | return cur <= end && ndisc_is_useropt(cur) ? cur : NULL; |
| 234 | } | 200 | } |
| 235 | 201 | ||
| 236 | static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, | 202 | struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, |
| 237 | struct ndisc_options *ndopts) | 203 | struct ndisc_options *ndopts) |
| 238 | { | 204 | { |
| 239 | struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt; | 205 | struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt; |
| 240 | 206 | ||
| @@ -297,17 +263,6 @@ static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, | |||
| 297 | return ndopts; | 263 | return ndopts; |
| 298 | } | 264 | } |
| 299 | 265 | ||
| 300 | static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p, | ||
| 301 | struct net_device *dev) | ||
| 302 | { | ||
| 303 | u8 *lladdr = (u8 *)(p + 1); | ||
| 304 | int lladdrlen = p->nd_opt_len << 3; | ||
| 305 | int prepad = ndisc_addr_option_pad(dev->type); | ||
| 306 | if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len + prepad)) | ||
| 307 | return NULL; | ||
| 308 | return lladdr + prepad; | ||
| 309 | } | ||
| 310 | |||
| 311 | int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir) | 266 | int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir) |
| 312 | { | 267 | { |
| 313 | switch (dev->type) { | 268 | switch (dev->type) { |
| @@ -1379,16 +1334,6 @@ out: | |||
| 1379 | 1334 | ||
| 1380 | static void ndisc_redirect_rcv(struct sk_buff *skb) | 1335 | static void ndisc_redirect_rcv(struct sk_buff *skb) |
| 1381 | { | 1336 | { |
| 1382 | struct inet6_dev *in6_dev; | ||
| 1383 | struct icmp6hdr *icmph; | ||
| 1384 | const struct in6_addr *dest; | ||
| 1385 | const struct in6_addr *target; /* new first hop to destination */ | ||
| 1386 | struct neighbour *neigh; | ||
| 1387 | int on_link = 0; | ||
| 1388 | struct ndisc_options ndopts; | ||
| 1389 | int optlen; | ||
| 1390 | u8 *lladdr = NULL; | ||
| 1391 | |||
| 1392 | #ifdef CONFIG_IPV6_NDISC_NODETYPE | 1337 | #ifdef CONFIG_IPV6_NDISC_NODETYPE |
| 1393 | switch (skb->ndisc_nodetype) { | 1338 | switch (skb->ndisc_nodetype) { |
| 1394 | case NDISC_NODETYPE_HOST: | 1339 | case NDISC_NODETYPE_HOST: |
| @@ -1405,65 +1350,7 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) | |||
| 1405 | return; | 1350 | return; |
| 1406 | } | 1351 | } |
| 1407 | 1352 | ||
| 1408 | optlen = skb->tail - skb->transport_header; | 1353 | icmpv6_notify(skb, NDISC_REDIRECT, 0, 0); |
| 1409 | optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); | ||
| 1410 | |||
| 1411 | if (optlen < 0) { | ||
| 1412 | ND_PRINTK(2, warn, "Redirect: packet too short\n"); | ||
| 1413 | return; | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | icmph = icmp6_hdr(skb); | ||
| 1417 | target = (const struct in6_addr *) (icmph + 1); | ||
| 1418 | dest = target + 1; | ||
| 1419 | |||
| 1420 | if (ipv6_addr_is_multicast(dest)) { | ||
| 1421 | ND_PRINTK(2, warn, | ||
| 1422 | "Redirect: destination address is multicast\n"); | ||
| 1423 | return; | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | if (ipv6_addr_equal(dest, target)) { | ||
| 1427 | on_link = 1; | ||
| 1428 | } else if (ipv6_addr_type(target) != | ||
| 1429 | (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { | ||
| 1430 | ND_PRINTK(2, warn, | ||
| 1431 | "Redirect: target address is not link-local unicast\n"); | ||
| 1432 | return; | ||
| 1433 | } | ||
| 1434 | |||
| 1435 | in6_dev = __in6_dev_get(skb->dev); | ||
| 1436 | if (!in6_dev) | ||
| 1437 | return; | ||
| 1438 | if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) | ||
| 1439 | return; | ||
| 1440 | |||
| 1441 | /* RFC2461 8.1: | ||
| 1442 | * The IP source address of the Redirect MUST be the same as the current | ||
| 1443 | * first-hop router for the specified ICMP Destination Address. | ||
| 1444 | */ | ||
| 1445 | |||
| 1446 | if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) { | ||
| 1447 | ND_PRINTK(2, warn, "Redirect: invalid ND options\n"); | ||
| 1448 | return; | ||
| 1449 | } | ||
| 1450 | if (ndopts.nd_opts_tgt_lladdr) { | ||
| 1451 | lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, | ||
| 1452 | skb->dev); | ||
| 1453 | if (!lladdr) { | ||
| 1454 | ND_PRINTK(2, warn, | ||
| 1455 | "Redirect: invalid link-layer address length\n"); | ||
| 1456 | return; | ||
| 1457 | } | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1); | ||
| 1461 | if (neigh) { | ||
| 1462 | rt6_redirect(dest, &ipv6_hdr(skb)->daddr, | ||
| 1463 | &ipv6_hdr(skb)->saddr, neigh, lladdr, | ||
| 1464 | on_link); | ||
| 1465 | neigh_release(neigh); | ||
| 1466 | } | ||
| 1467 | } | 1354 | } |
| 1468 | 1355 | ||
| 1469 | void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) | 1356 | void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) |
| @@ -1472,6 +1359,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) | |||
| 1472 | struct net *net = dev_net(dev); | 1359 | struct net *net = dev_net(dev); |
| 1473 | struct sock *sk = net->ipv6.ndisc_sk; | 1360 | struct sock *sk = net->ipv6.ndisc_sk; |
| 1474 | int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); | 1361 | int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); |
| 1362 | struct inet_peer *peer; | ||
| 1475 | struct sk_buff *buff; | 1363 | struct sk_buff *buff; |
| 1476 | struct icmp6hdr *icmph; | 1364 | struct icmp6hdr *icmph; |
| 1477 | struct in6_addr saddr_buf; | 1365 | struct in6_addr saddr_buf; |
| @@ -1485,6 +1373,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) | |||
| 1485 | int rd_len; | 1373 | int rd_len; |
| 1486 | int err; | 1374 | int err; |
| 1487 | u8 ha_buf[MAX_ADDR_LEN], *ha = NULL; | 1375 | u8 ha_buf[MAX_ADDR_LEN], *ha = NULL; |
| 1376 | bool ret; | ||
| 1488 | 1377 | ||
| 1489 | if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) { | 1378 | if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) { |
| 1490 | ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n", | 1379 | ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n", |
| @@ -1518,9 +1407,11 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) | |||
| 1518 | "Redirect: destination is not a neighbour\n"); | 1407 | "Redirect: destination is not a neighbour\n"); |
| 1519 | goto release; | 1408 | goto release; |
| 1520 | } | 1409 | } |
| 1521 | if (!rt->rt6i_peer) | 1410 | peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1); |
| 1522 | rt6_bind_peer(rt, 1); | 1411 | ret = inet_peer_xrlim_allow(peer, 1*HZ); |
| 1523 | if (!inet_peer_xrlim_allow(rt->rt6i_peer, 1*HZ)) | 1412 | if (peer) |
| 1413 | inet_putpeer(peer); | ||
| 1414 | if (!ret) | ||
| 1524 | goto release; | 1415 | goto release; |
| 1525 | 1416 | ||
| 1526 | if (dev->addr_len) { | 1417 | if (dev->addr_len) { |
