diff options
author | David S. Miller <davem@davemloft.net> | 2013-01-21 13:50:10 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-01-21 13:50:10 -0500 |
commit | 9acefd17cba1a018163d00b5b5e463db8e3761ab (patch) | |
tree | a9b0846968a4df192ec7cb890b5a929c67b61a9d | |
parent | 7373470202a3c98e1a338bf1acf51247cd100868 (diff) | |
parent | 4d5c152e8694b57094e320060be4621dee5779dd (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.h | 7 | ||||
-rw-r--r-- | include/net/ndisc.h | 8 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 33 | ||||
-rw-r--r-- | net/ipv6/mcast.c | 29 | ||||
-rw-r--r-- | net/ipv6/ndisc.c | 331 |
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 | ||
664 | extern 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 | |||
671 | extern int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr); | 664 | extern int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr); |
672 | 665 | ||
673 | extern int ip6_append_data(struct sock *sk, | 666 | extern 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 | ||
130 | static 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 | |||
130 | static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p, | 136 | static 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 | ||
255 | EXPORT_SYMBOL(ip6_xmit); | 255 | EXPORT_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 | |||
264 | int 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 | |||
290 | static int ip6_call_ra_chain(struct sk_buff *skb, int sel) | 257 | static 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 | ||
1316 | static 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 | |||
1316 | static struct sk_buff *mld_newpack(struct net_device *dev, int size) | 1341 | static 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 | ||
146 | static inline int ndisc_opt_addr_space(struct net_device *dev) | 146 | static 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); | |
151 | static 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 | ||
172 | static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, | 167 | static 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 | ||
373 | static struct sk_buff *ndisc_build_skb(struct net_device *dev, | 368 | static 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); | 395 | static 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 | ||
434 | static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev, | 416 | static 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 | */ | ||
475 | static 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 | |||
490 | static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, | 465 | static 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 | ||
526 | static void ndisc_send_unsol_na(struct net_device *dev) | 521 | static 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 | ||
567 | void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, | 583 | void 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 | ||
1384 | static 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 | |||
1349 | void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) | 1398 | void 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 | ||
1504 | release: | 1511 | release: |
@@ -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); |