aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2013-01-21 13:50:10 -0500
committerDavid S. Miller <davem@davemloft.net>2013-01-21 13:50:10 -0500
commit9acefd17cba1a018163d00b5b5e463db8e3761ab (patch)
treea9b0846968a4df192ec7cb890b5a929c67b61a9d
parent7373470202a3c98e1a338bf1acf51247cd100868 (diff)
parent4d5c152e8694b57094e320060be4621dee5779dd (diff)
Merge branch 'ipv6_ndisc'
YOSHIFUJI Hideaki says: ==================== This series of changes basically clean up NDISC logic, especially on sender side. We originally do For NS/NA/RS: 1) build temporary ICMPv6 header 2) ndisc_build_skb() with temporary ICMPv6 header and rather criptic arguments. - Calculate total length and allocate sk_buff - Build IPv6 header. - copy ICMPv6 header, additional data and ND options. - Fill-in ICMPv6 checksum. Here, structures defined for message format was not used at all, it is difficult to understand what is being sent, and it was not generic. 3) __ndisc_send() - Allocate temporary dst. - Send it. Several issues: - We could not defer decision if we should/can send some ND option. - It is hard to see the packet format at a glance. - ICMPv6 header was built as temporary variable, and then copied to the buffer. - Some code path for Redirect was not shared. With these patches, we do: 1) Calculate (or estimate) message length and option length. 2) Allocate skb (via new ndisc_skb_alloc()). 3) Fill-in ICMPv6 message directly using compound literals. 4) Fill-in ICMPv6 checksum 5) Build IPv6 header (including length) 6) Send the packet (via ndisc_send_skb()). - allocate temporary dst and send it. - We can defer calculating real length of the packet. For example, we can give up filling some option at when filling in. - Message is built directly without temporary buffer. - Structures defined for message format is easier to understand what is being built. - NS/NA/RS/Redirect share same logic. - Reduced code/data size: text data bss dec hex filename 265407 14133 3488 283028 45194 old/net/ipv6/ipv6.o 264955 14109 3488 282552 44fb8 new/net/ipv6/ipv6.o ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/ipv6.h7
-rw-r--r--include/net/ndisc.h8
-rw-r--r--net/ipv6/ip6_output.c33
-rw-r--r--net/ipv6/mcast.c29
-rw-r--r--net/ipv6/ndisc.c331
5 files changed, 203 insertions, 205 deletions
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 464c6f70eca1..c1878f7049c8 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -661,13 +661,6 @@ extern int ip6_xmit(struct sock *sk,
661 struct ipv6_txoptions *opt, 661 struct ipv6_txoptions *opt,
662 int tclass); 662 int tclass);
663 663
664extern int ip6_nd_hdr(struct sock *sk,
665 struct sk_buff *skb,
666 struct net_device *dev,
667 const struct in6_addr *saddr,
668 const struct in6_addr *daddr,
669 int proto, int len);
670
671extern int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr); 664extern int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr);
672 665
673extern int ip6_append_data(struct sock *sk, 666extern int ip6_append_data(struct sock *sk,
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index ec48f42db5ed..745bf741e029 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -127,13 +127,19 @@ static int ndisc_addr_option_pad(unsigned short type)
127 } 127 }
128} 128}
129 129
130static inline int ndisc_opt_addr_space(struct net_device *dev)
131{
132 return NDISC_OPT_SPACE(dev->addr_len +
133 ndisc_addr_option_pad(dev->type));
134}
135
130static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p, 136static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
131 struct net_device *dev) 137 struct net_device *dev)
132{ 138{
133 u8 *lladdr = (u8 *)(p + 1); 139 u8 *lladdr = (u8 *)(p + 1);
134 int lladdrlen = p->nd_opt_len << 3; 140 int lladdrlen = p->nd_opt_len << 3;
135 int prepad = ndisc_addr_option_pad(dev->type); 141 int prepad = ndisc_addr_option_pad(dev->type);
136 if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len + prepad)) 142 if (lladdrlen != ndisc_opt_addr_space(dev))
137 return NULL; 143 return NULL;
138 return lladdr + prepad; 144 return lladdr + prepad;
139} 145}
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index b0895f5d5fc6..7eee94c27f8d 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -254,39 +254,6 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
254 254
255EXPORT_SYMBOL(ip6_xmit); 255EXPORT_SYMBOL(ip6_xmit);
256 256
257/*
258 * To avoid extra problems ND packets are send through this
259 * routine. It's code duplication but I really want to avoid
260 * extra checks since ipv6_build_header is used by TCP (which
261 * is for us performance critical)
262 */
263
264int ip6_nd_hdr(struct sock *sk, struct sk_buff *skb, struct net_device *dev,
265 const struct in6_addr *saddr, const struct in6_addr *daddr,
266 int proto, int len)
267{
268 struct ipv6_pinfo *np = inet6_sk(sk);
269 struct ipv6hdr *hdr;
270
271 skb->protocol = htons(ETH_P_IPV6);
272 skb->dev = dev;
273
274 skb_reset_network_header(skb);
275 skb_put(skb, sizeof(struct ipv6hdr));
276 hdr = ipv6_hdr(skb);
277
278 ip6_flow_hdr(hdr, 0, 0);
279
280 hdr->payload_len = htons(len);
281 hdr->nexthdr = proto;
282 hdr->hop_limit = np->hop_limit;
283
284 hdr->saddr = *saddr;
285 hdr->daddr = *daddr;
286
287 return 0;
288}
289
290static int ip6_call_ra_chain(struct sk_buff *skb, int sel) 257static int ip6_call_ra_chain(struct sk_buff *skb, int sel)
291{ 258{
292 struct ip6_ra_chain *ra; 259 struct ip6_ra_chain *ra;
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 587a84530a57..f25002aaf624 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1313,6 +1313,31 @@ mld_scount(struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted)
1313 return scount; 1313 return scount;
1314} 1314}
1315 1315
1316static void ip6_mc_hdr(struct sock *sk, struct sk_buff *skb,
1317 struct net_device *dev,
1318 const struct in6_addr *saddr,
1319 const struct in6_addr *daddr,
1320 int proto, int len)
1321{
1322 struct ipv6hdr *hdr;
1323
1324 skb->protocol = htons(ETH_P_IPV6);
1325 skb->dev = dev;
1326
1327 skb_reset_network_header(skb);
1328 skb_put(skb, sizeof(struct ipv6hdr));
1329 hdr = ipv6_hdr(skb);
1330
1331 ip6_flow_hdr(hdr, 0, 0);
1332
1333 hdr->payload_len = htons(len);
1334 hdr->nexthdr = proto;
1335 hdr->hop_limit = inet6_sk(sk)->hop_limit;
1336
1337 hdr->saddr = *saddr;
1338 hdr->daddr = *daddr;
1339}
1340
1316static struct sk_buff *mld_newpack(struct net_device *dev, int size) 1341static struct sk_buff *mld_newpack(struct net_device *dev, int size)
1317{ 1342{
1318 struct net *net = dev_net(dev); 1343 struct net *net = dev_net(dev);
@@ -1348,7 +1373,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
1348 } else 1373 } else
1349 saddr = &addr_buf; 1374 saddr = &addr_buf;
1350 1375
1351 ip6_nd_hdr(sk, skb, dev, saddr, &mld2_all_mcr, NEXTHDR_HOP, 0); 1376 ip6_mc_hdr(sk, skb, dev, saddr, &mld2_all_mcr, NEXTHDR_HOP, 0);
1352 1377
1353 memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra)); 1378 memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
1354 1379
@@ -1740,7 +1765,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
1740 } else 1765 } else
1741 saddr = &addr_buf; 1766 saddr = &addr_buf;
1742 1767
1743 ip6_nd_hdr(sk, skb, dev, saddr, snd_addr, NEXTHDR_HOP, payload_len); 1768 ip6_mc_hdr(sk, skb, dev, saddr, snd_addr, NEXTHDR_HOP, payload_len);
1744 1769
1745 memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra)); 1770 memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
1746 1771
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 067a0d2eb557..76ef4353d518 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -143,16 +143,12 @@ struct neigh_table nd_tbl = {
143 .gc_thresh3 = 1024, 143 .gc_thresh3 = 1024,
144}; 144};
145 145
146static inline int ndisc_opt_addr_space(struct net_device *dev) 146static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
147{ 147{
148 return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type)); 148 int pad = ndisc_addr_option_pad(skb->dev->type);
149} 149 int data_len = skb->dev->addr_len;
150 150 int space = ndisc_opt_addr_space(skb->dev);
151static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len, 151 u8 *opt = skb_put(skb, space);
152 unsigned short addr_type)
153{
154 int pad = ndisc_addr_option_pad(addr_type);
155 int space = NDISC_OPT_SPACE(data_len + pad);
156 152
157 opt[0] = type; 153 opt[0] = type;
158 opt[1] = space>>3; 154 opt[1] = space>>3;
@@ -166,7 +162,6 @@ static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len,
166 opt += data_len; 162 opt += data_len;
167 if ((space -= data_len) > 0) 163 if ((space -= data_len) > 0)
168 memset(opt, 0, space); 164 memset(opt, 0, space);
169 return opt + space;
170} 165}
171 166
172static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, 167static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
@@ -370,90 +365,88 @@ static void pndisc_destructor(struct pneigh_entry *n)
370 ipv6_dev_mc_dec(dev, &maddr); 365 ipv6_dev_mc_dec(dev, &maddr);
371} 366}
372 367
373static struct sk_buff *ndisc_build_skb(struct net_device *dev, 368static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
374 const struct in6_addr *daddr, 369 int len)
375 const struct in6_addr *saddr,
376 struct icmp6hdr *icmp6h,
377 const struct in6_addr *target,
378 int llinfo)
379{ 370{
380 struct net *net = dev_net(dev);
381 struct sock *sk = net->ipv6.ndisc_sk;
382 struct sk_buff *skb;
383 struct icmp6hdr *hdr;
384 int hlen = LL_RESERVED_SPACE(dev); 371 int hlen = LL_RESERVED_SPACE(dev);
385 int tlen = dev->needed_tailroom; 372 int tlen = dev->needed_tailroom;
386 int len; 373 struct sock *sk = dev_net(dev)->ipv6.ndisc_sk;
374 struct sk_buff *skb;
387 int err; 375 int err;
388 u8 *opt;
389
390 if (!dev->addr_len)
391 llinfo = 0;
392
393 len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
394 if (llinfo)
395 len += ndisc_opt_addr_space(dev);
396 376
397 skb = sock_alloc_send_skb(sk, 377 skb = sock_alloc_send_skb(sk,
398 (sizeof(struct ipv6hdr) + 378 hlen + sizeof(struct ipv6hdr) + len + tlen,
399 len + hlen + tlen),
400 1, &err); 379 1, &err);
401 if (!skb) { 380 if (!skb) {
402 ND_PRINTK(0, err, "ND: %s failed to allocate an skb, err=%d\n", 381 ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb, err=%d\n",
403 __func__, err); 382 __func__, err);
404 return NULL; 383 return NULL;
405 } 384 }
406 385
407 skb_reserve(skb, hlen); 386 skb->protocol = htons(ETH_P_IPV6);
408 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len); 387 skb->dev = dev;
409 388
410 skb->transport_header = skb->tail; 389 skb_reserve(skb, hlen + sizeof(struct ipv6hdr));
411 skb_put(skb, len); 390 skb_reset_transport_header(skb);
412 391
413 hdr = (struct icmp6hdr *)skb_transport_header(skb); 392 return skb;
414 memcpy(hdr, icmp6h, sizeof(*hdr)); 393}
415 394
416 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr); 395static void ip6_nd_hdr(struct sk_buff *skb,
417 if (target) { 396 const struct in6_addr *saddr,
418 *(struct in6_addr *)opt = *target; 397 const struct in6_addr *daddr,
419 opt += sizeof(*target); 398 int hop_limit, int len)
420 } 399{
400 struct ipv6hdr *hdr;
421 401
422 if (llinfo) 402 skb_push(skb, sizeof(*hdr));
423 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr, 403 skb_reset_network_header(skb);
424 dev->addr_len, dev->type); 404 hdr = ipv6_hdr(skb);
425 405
426 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len, 406 ip6_flow_hdr(hdr, 0, 0);
427 IPPROTO_ICMPV6,
428 csum_partial(hdr,
429 len, 0));
430 407
431 return skb; 408 hdr->payload_len = htons(len);
409 hdr->nexthdr = IPPROTO_ICMPV6;
410 hdr->hop_limit = hop_limit;
411
412 hdr->saddr = *saddr;
413 hdr->daddr = *daddr;
432} 414}
433 415
434static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev, 416static void ndisc_send_skb(struct sk_buff *skb,
435 const struct in6_addr *daddr, 417 const struct in6_addr *daddr,
436 const struct in6_addr *saddr, 418 const struct in6_addr *saddr)
437 struct icmp6hdr *icmp6h)
438{ 419{
439 struct flowi6 fl6; 420 struct dst_entry *dst = skb_dst(skb);
440 struct dst_entry *dst; 421 struct net *net = dev_net(skb->dev);
441 struct net *net = dev_net(dev);
442 struct sock *sk = net->ipv6.ndisc_sk; 422 struct sock *sk = net->ipv6.ndisc_sk;
443 struct inet6_dev *idev; 423 struct inet6_dev *idev;
444 int err; 424 int err;
425 struct icmp6hdr *icmp6h = icmp6_hdr(skb);
445 u8 type; 426 u8 type;
446 427
447 type = icmp6h->icmp6_type; 428 type = icmp6h->icmp6_type;
448 429
449 icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex); 430 if (!dst) {
450 dst = icmp6_dst_alloc(dev, &fl6); 431 struct sock *sk = net->ipv6.ndisc_sk;
451 if (IS_ERR(dst)) { 432 struct flowi6 fl6;
452 kfree_skb(skb); 433
453 return; 434 icmpv6_flow_init(sk, &fl6, type, saddr, daddr, skb->dev->ifindex);
435 dst = icmp6_dst_alloc(skb->dev, &fl6);
436 if (IS_ERR(dst)) {
437 kfree_skb(skb);
438 return;
439 }
440
441 skb_dst_set(skb, dst);
454 } 442 }
455 443
456 skb_dst_set(skb, dst); 444 icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len,
445 IPPROTO_ICMPV6,
446 csum_partial(icmp6h,
447 skb->len, 0));
448
449 ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len);
457 450
458 rcu_read_lock(); 451 rcu_read_lock();
459 idev = __in6_dev_get(dst->dev); 452 idev = __in6_dev_get(dst->dev);
@@ -469,35 +462,17 @@ static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev,
469 rcu_read_unlock(); 462 rcu_read_unlock();
470} 463}
471 464
472/*
473 * Send a Neighbour Discover packet
474 */
475static void __ndisc_send(struct net_device *dev,
476 const struct in6_addr *daddr,
477 const struct in6_addr *saddr,
478 struct icmp6hdr *icmp6h, const struct in6_addr *target,
479 int llinfo)
480{
481 struct sk_buff *skb;
482
483 skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
484 if (!skb)
485 return;
486
487 ndisc_send_skb(skb, dev, daddr, saddr, icmp6h);
488}
489
490static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, 465static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
491 const struct in6_addr *daddr, 466 const struct in6_addr *daddr,
492 const struct in6_addr *solicited_addr, 467 const struct in6_addr *solicited_addr,
493 bool router, bool solicited, bool override, bool inc_opt) 468 bool router, bool solicited, bool override, bool inc_opt)
494{ 469{
470 struct sk_buff *skb;
495 struct in6_addr tmpaddr; 471 struct in6_addr tmpaddr;
496 struct inet6_ifaddr *ifp; 472 struct inet6_ifaddr *ifp;
497 const struct in6_addr *src_addr; 473 const struct in6_addr *src_addr;
498 struct icmp6hdr icmp6h = { 474 struct nd_msg *msg;
499 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT, 475 int optlen = 0;
500 };
501 476
502 /* for anycast or proxy, solicited_addr != src_addr */ 477 /* for anycast or proxy, solicited_addr != src_addr */
503 ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1); 478 ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
@@ -515,12 +490,32 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
515 src_addr = &tmpaddr; 490 src_addr = &tmpaddr;
516 } 491 }
517 492
518 icmp6h.icmp6_router = router; 493 if (!dev->addr_len)
519 icmp6h.icmp6_solicited = solicited; 494 inc_opt = 0;
520 icmp6h.icmp6_override = override; 495 if (inc_opt)
496 optlen += ndisc_opt_addr_space(dev);
497
498 skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
499 if (!skb)
500 return;
501
502 msg = (struct nd_msg *)skb_put(skb, sizeof(*msg));
503 *msg = (struct nd_msg) {
504 .icmph = {
505 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
506 .icmp6_router = router,
507 .icmp6_solicited = solicited,
508 .icmp6_override = override,
509 },
510 .target = *solicited_addr,
511 };
521 512
522 __ndisc_send(dev, daddr, src_addr, &icmp6h, solicited_addr, 513 if (inc_opt)
523 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0); 514 ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
515 dev->dev_addr);
516
517
518 ndisc_send_skb(skb, daddr, src_addr);
524} 519}
525 520
526static void ndisc_send_unsol_na(struct net_device *dev) 521static void ndisc_send_unsol_na(struct net_device *dev)
@@ -548,10 +543,11 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
548 const struct in6_addr *solicit, 543 const struct in6_addr *solicit,
549 const struct in6_addr *daddr, const struct in6_addr *saddr) 544 const struct in6_addr *daddr, const struct in6_addr *saddr)
550{ 545{
546 struct sk_buff *skb;
551 struct in6_addr addr_buf; 547 struct in6_addr addr_buf;
552 struct icmp6hdr icmp6h = { 548 int inc_opt = dev->addr_len;
553 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION, 549 int optlen = 0;
554 }; 550 struct nd_msg *msg;
555 551
556 if (saddr == NULL) { 552 if (saddr == NULL) {
557 if (ipv6_get_lladdr(dev, &addr_buf, 553 if (ipv6_get_lladdr(dev, &addr_buf,
@@ -560,17 +556,37 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
560 saddr = &addr_buf; 556 saddr = &addr_buf;
561 } 557 }
562 558
563 __ndisc_send(dev, daddr, saddr, &icmp6h, solicit, 559 if (ipv6_addr_any(saddr))
564 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0); 560 inc_opt = 0;
561 if (inc_opt)
562 optlen += ndisc_opt_addr_space(dev);
563
564 skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
565 if (!skb)
566 return;
567
568 msg = (struct nd_msg *)skb_put(skb, sizeof(*msg));
569 *msg = (struct nd_msg) {
570 .icmph = {
571 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
572 },
573 .target = *solicit,
574 };
575
576 if (inc_opt)
577 ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
578 dev->dev_addr);
579
580 ndisc_send_skb(skb, daddr, saddr);
565} 581}
566 582
567void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, 583void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
568 const struct in6_addr *daddr) 584 const struct in6_addr *daddr)
569{ 585{
570 struct icmp6hdr icmp6h = { 586 struct sk_buff *skb;
571 .icmp6_type = NDISC_ROUTER_SOLICITATION, 587 struct rs_msg *msg;
572 };
573 int send_sllao = dev->addr_len; 588 int send_sllao = dev->addr_len;
589 int optlen = 0;
574 590
575#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 591#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
576 /* 592 /*
@@ -594,8 +610,27 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
594 } 610 }
595 } 611 }
596#endif 612#endif
597 __ndisc_send(dev, daddr, saddr, &icmp6h, NULL, 613 if (!dev->addr_len)
598 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0); 614 send_sllao = 0;
615 if (send_sllao)
616 optlen += ndisc_opt_addr_space(dev);
617
618 skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
619 if (!skb)
620 return;
621
622 msg = (struct rs_msg *)skb_put(skb, sizeof(*msg));
623 *msg = (struct rs_msg) {
624 .icmph = {
625 .icmp6_type = NDISC_ROUTER_SOLICITATION,
626 },
627 };
628
629 if (send_sllao)
630 ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
631 dev->dev_addr);
632
633 ndisc_send_skb(skb, daddr, saddr);
599} 634}
600 635
601 636
@@ -1346,24 +1381,34 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
1346 icmpv6_notify(skb, NDISC_REDIRECT, 0, 0); 1381 icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
1347} 1382}
1348 1383
1384static void ndisc_fill_redirect_hdr_option(struct sk_buff *skb,
1385 struct sk_buff *orig_skb,
1386 int rd_len)
1387{
1388 u8 *opt = skb_put(skb, rd_len);
1389
1390 memset(opt, 0, 8);
1391 *(opt++) = ND_OPT_REDIRECT_HDR;
1392 *(opt++) = (rd_len >> 3);
1393 opt += 6;
1394
1395 memcpy(opt, ipv6_hdr(orig_skb), rd_len - 8);
1396}
1397
1349void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) 1398void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
1350{ 1399{
1351 struct net_device *dev = skb->dev; 1400 struct net_device *dev = skb->dev;
1352 struct net *net = dev_net(dev); 1401 struct net *net = dev_net(dev);
1353 struct sock *sk = net->ipv6.ndisc_sk; 1402 struct sock *sk = net->ipv6.ndisc_sk;
1354 int len = sizeof(struct rd_msg); 1403 int optlen = 0;
1355 struct inet_peer *peer; 1404 struct inet_peer *peer;
1356 struct sk_buff *buff; 1405 struct sk_buff *buff;
1357 struct rd_msg *msg; 1406 struct rd_msg *msg;
1358 struct in6_addr saddr_buf; 1407 struct in6_addr saddr_buf;
1359 struct rt6_info *rt; 1408 struct rt6_info *rt;
1360 struct dst_entry *dst; 1409 struct dst_entry *dst;
1361 struct inet6_dev *idev;
1362 struct flowi6 fl6; 1410 struct flowi6 fl6;
1363 u8 *opt;
1364 int hlen, tlen;
1365 int rd_len; 1411 int rd_len;
1366 int err;
1367 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL; 1412 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
1368 bool ret; 1413 bool ret;
1369 1414
@@ -1419,7 +1464,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
1419 memcpy(ha_buf, neigh->ha, dev->addr_len); 1464 memcpy(ha_buf, neigh->ha, dev->addr_len);
1420 read_unlock_bh(&neigh->lock); 1465 read_unlock_bh(&neigh->lock);
1421 ha = ha_buf; 1466 ha = ha_buf;
1422 len += ndisc_opt_addr_space(dev); 1467 optlen += ndisc_opt_addr_space(dev);
1423 } else 1468 } else
1424 read_unlock_bh(&neigh->lock); 1469 read_unlock_bh(&neigh->lock);
1425 1470
@@ -1427,78 +1472,40 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
1427 } 1472 }
1428 1473
1429 rd_len = min_t(unsigned int, 1474 rd_len = min_t(unsigned int,
1430 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8); 1475 IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(*msg) - optlen,
1476 skb->len + 8);
1431 rd_len &= ~0x7; 1477 rd_len &= ~0x7;
1432 len += rd_len; 1478 optlen += rd_len;
1433
1434 hlen = LL_RESERVED_SPACE(dev);
1435 tlen = dev->needed_tailroom;
1436 buff = sock_alloc_send_skb(sk,
1437 (sizeof(struct ipv6hdr) +
1438 len + hlen + tlen),
1439 1, &err);
1440 if (buff == NULL) {
1441 ND_PRINTK(0, err,
1442 "Redirect: %s failed to allocate an skb, err=%d\n",
1443 __func__, err);
1444 goto release;
1445 }
1446
1447 skb_reserve(buff, hlen);
1448 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
1449 IPPROTO_ICMPV6, len);
1450 1479
1451 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data); 1480 buff = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
1452 skb_put(buff, len); 1481 if (!buff)
1453 msg = (struct rd_msg *)icmp6_hdr(buff); 1482 goto release;
1454
1455 memset(&msg->icmph, 0, sizeof(struct icmp6hdr));
1456 msg->icmph.icmp6_type = NDISC_REDIRECT;
1457
1458 /*
1459 * copy target and destination addresses
1460 */
1461
1462 msg->target = *target;
1463 msg->dest = ipv6_hdr(skb)->daddr;
1464 1483
1465 opt = msg->opt; 1484 msg = (struct rd_msg *)skb_put(buff, sizeof(*msg));
1485 *msg = (struct rd_msg) {
1486 .icmph = {
1487 .icmp6_type = NDISC_REDIRECT,
1488 },
1489 .target = *target,
1490 .dest = ipv6_hdr(skb)->daddr,
1491 };
1466 1492
1467 /* 1493 /*
1468 * include target_address option 1494 * include target_address option
1469 */ 1495 */
1470 1496
1471 if (ha) 1497 if (ha)
1472 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha, 1498 ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, ha);
1473 dev->addr_len, dev->type);
1474 1499
1475 /* 1500 /*
1476 * build redirect option and copy skb over to the new packet. 1501 * build redirect option and copy skb over to the new packet.
1477 */ 1502 */
1478 1503
1479 memset(opt, 0, 8); 1504 if (rd_len)
1480 *(opt++) = ND_OPT_REDIRECT_HDR; 1505 ndisc_fill_redirect_hdr_option(buff, skb, rd_len);
1481 *(opt++) = (rd_len >> 3);
1482 opt += 6;
1483
1484 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
1485
1486 msg->icmph.icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
1487 len, IPPROTO_ICMPV6,
1488 csum_partial(msg, len, 0));
1489 1506
1490 skb_dst_set(buff, dst); 1507 skb_dst_set(buff, dst);
1491 rcu_read_lock(); 1508 ndisc_send_skb(buff, &ipv6_hdr(skb)->saddr, &saddr_buf);
1492 idev = __in6_dev_get(dst->dev);
1493 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
1494 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
1495 dst_output);
1496 if (!err) {
1497 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
1498 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
1499 }
1500
1501 rcu_read_unlock();
1502 return; 1509 return;
1503 1510
1504release: 1511release:
@@ -1515,7 +1522,7 @@ int ndisc_rcv(struct sk_buff *skb)
1515{ 1522{
1516 struct nd_msg *msg; 1523 struct nd_msg *msg;
1517 1524
1518 if (!pskb_may_pull(skb, skb->len)) 1525 if (skb_linearize(skb))
1519 return 0; 1526 return 0;
1520 1527
1521 msg = (struct nd_msg *)skb_transport_header(skb); 1528 msg = (struct nd_msg *)skb_transport_header(skb);