aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ndisc.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ndisc.c')
-rw-r--r--net/ipv6/ndisc.c129
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 */
147struct 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 */
172static 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
180static inline int ndisc_opt_addr_space(struct net_device *dev) 146static 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
236static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, 202struct 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
300static 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
311int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir) 266int 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
1380static void ndisc_redirect_rcv(struct sk_buff *skb) 1335static 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
1469void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) 1356void 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) {