diff options
author | Brian Haley <brian.haley@hp.com> | 2010-04-23 07:26:09 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-04-24 02:35:29 -0400 |
commit | 4b340ae20d0e2366792abe70f46629e576adaf5e (patch) | |
tree | b0c413a0348e722fbc23d45508224076b6e60f92 | |
parent | 13b52cd44670e3359055e9918d0e766d89836425 (diff) |
IPv6: Complete IPV6_DONTFRAG support
Finally add support to detect a local IPV6_DONTFRAG event
and return the relevant data to the user if they've enabled
IPV6_RECVPATHMTU on the socket. The next recvmsg() will
return no data, but have an IPV6_PATHMTU as ancillary data.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/ipv6.h | 2 | ||||
-rw-r--r-- | include/net/ipv6.h | 2 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 3 | ||||
-rw-r--r-- | net/ipv6/datagram.c | 87 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 24 | ||||
-rw-r--r-- | net/ipv6/raw.c | 3 | ||||
-rw-r--r-- | net/ipv6/udp.c | 3 |
7 files changed, 116 insertions, 8 deletions
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 1976942cf6f9..2ab5509f6d49 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h | |||
@@ -257,6 +257,7 @@ struct inet6_skb_parm { | |||
257 | }; | 257 | }; |
258 | 258 | ||
259 | #define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb)) | 259 | #define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb)) |
260 | #define IP6CBMTU(skb) ((struct ip6_mtuinfo *)((skb)->cb)) | ||
260 | 261 | ||
261 | static inline int inet6_iif(const struct sk_buff *skb) | 262 | static inline int inet6_iif(const struct sk_buff *skb) |
262 | { | 263 | { |
@@ -366,6 +367,7 @@ struct ipv6_pinfo { | |||
366 | 367 | ||
367 | struct ipv6_txoptions *opt; | 368 | struct ipv6_txoptions *opt; |
368 | struct sk_buff *pktoptions; | 369 | struct sk_buff *pktoptions; |
370 | struct sk_buff *rxpmtu; | ||
369 | struct { | 371 | struct { |
370 | struct ipv6_txoptions *opt; | 372 | struct ipv6_txoptions *opt; |
371 | u8 hop_limit; | 373 | u8 hop_limit; |
diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 7ab6323e631e..eba5cc00325a 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h | |||
@@ -578,9 +578,11 @@ extern int ip6_datagram_connect(struct sock *sk, | |||
578 | struct sockaddr *addr, int addr_len); | 578 | struct sockaddr *addr, int addr_len); |
579 | 579 | ||
580 | extern int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len); | 580 | extern int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len); |
581 | extern int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len); | ||
581 | extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port, | 582 | extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port, |
582 | u32 info, u8 *payload); | 583 | u32 info, u8 *payload); |
583 | extern void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info); | 584 | extern void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info); |
585 | extern void ipv6_local_rxpmtu(struct sock *sk, struct flowi *fl, u32 mtu); | ||
584 | 586 | ||
585 | extern int inet6_release(struct socket *sock); | 587 | extern int inet6_release(struct socket *sock); |
586 | extern int inet6_bind(struct socket *sock, struct sockaddr *uaddr, | 588 | extern int inet6_bind(struct socket *sock, struct sockaddr *uaddr, |
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 3192aa02ba5d..d2df3144429b 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c | |||
@@ -417,6 +417,9 @@ void inet6_destroy_sock(struct sock *sk) | |||
417 | if ((skb = xchg(&np->pktoptions, NULL)) != NULL) | 417 | if ((skb = xchg(&np->pktoptions, NULL)) != NULL) |
418 | kfree_skb(skb); | 418 | kfree_skb(skb); |
419 | 419 | ||
420 | if ((skb = xchg(&np->rxpmtu, NULL)) != NULL) | ||
421 | kfree_skb(skb); | ||
422 | |||
420 | /* Free flowlabels */ | 423 | /* Free flowlabels */ |
421 | fl6_free_socklist(sk); | 424 | fl6_free_socklist(sk); |
422 | 425 | ||
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index f5076d349b18..5959230bc6c1 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c | |||
@@ -278,6 +278,45 @@ void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info) | |||
278 | kfree_skb(skb); | 278 | kfree_skb(skb); |
279 | } | 279 | } |
280 | 280 | ||
281 | void ipv6_local_rxpmtu(struct sock *sk, struct flowi *fl, u32 mtu) | ||
282 | { | ||
283 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
284 | struct ipv6hdr *iph; | ||
285 | struct sk_buff *skb; | ||
286 | struct ip6_mtuinfo *mtu_info; | ||
287 | |||
288 | if (!np->rxopt.bits.rxpmtu) | ||
289 | return; | ||
290 | |||
291 | skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC); | ||
292 | if (!skb) | ||
293 | return; | ||
294 | |||
295 | skb_put(skb, sizeof(struct ipv6hdr)); | ||
296 | skb_reset_network_header(skb); | ||
297 | iph = ipv6_hdr(skb); | ||
298 | ipv6_addr_copy(&iph->daddr, &fl->fl6_dst); | ||
299 | |||
300 | mtu_info = IP6CBMTU(skb); | ||
301 | if (!mtu_info) { | ||
302 | kfree_skb(skb); | ||
303 | return; | ||
304 | } | ||
305 | |||
306 | mtu_info->ip6m_mtu = mtu; | ||
307 | mtu_info->ip6m_addr.sin6_family = AF_INET6; | ||
308 | mtu_info->ip6m_addr.sin6_port = 0; | ||
309 | mtu_info->ip6m_addr.sin6_flowinfo = 0; | ||
310 | mtu_info->ip6m_addr.sin6_scope_id = fl->oif; | ||
311 | ipv6_addr_copy(&mtu_info->ip6m_addr.sin6_addr, &ipv6_hdr(skb)->daddr); | ||
312 | |||
313 | __skb_pull(skb, skb_tail_pointer(skb) - skb->data); | ||
314 | skb_reset_transport_header(skb); | ||
315 | |||
316 | skb = xchg(&np->rxpmtu, skb); | ||
317 | kfree_skb(skb); | ||
318 | } | ||
319 | |||
281 | /* | 320 | /* |
282 | * Handle MSG_ERRQUEUE | 321 | * Handle MSG_ERRQUEUE |
283 | */ | 322 | */ |
@@ -381,6 +420,54 @@ out: | |||
381 | return err; | 420 | return err; |
382 | } | 421 | } |
383 | 422 | ||
423 | /* | ||
424 | * Handle IPV6_RECVPATHMTU | ||
425 | */ | ||
426 | int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len) | ||
427 | { | ||
428 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
429 | struct sk_buff *skb; | ||
430 | struct sockaddr_in6 *sin; | ||
431 | struct ip6_mtuinfo mtu_info; | ||
432 | int err; | ||
433 | int copied; | ||
434 | |||
435 | err = -EAGAIN; | ||
436 | skb = xchg(&np->rxpmtu, NULL); | ||
437 | if (skb == NULL) | ||
438 | goto out; | ||
439 | |||
440 | copied = skb->len; | ||
441 | if (copied > len) { | ||
442 | msg->msg_flags |= MSG_TRUNC; | ||
443 | copied = len; | ||
444 | } | ||
445 | err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); | ||
446 | if (err) | ||
447 | goto out_free_skb; | ||
448 | |||
449 | sock_recv_timestamp(msg, sk, skb); | ||
450 | |||
451 | memcpy(&mtu_info, IP6CBMTU(skb), sizeof(mtu_info)); | ||
452 | |||
453 | sin = (struct sockaddr_in6 *)msg->msg_name; | ||
454 | if (sin) { | ||
455 | sin->sin6_family = AF_INET6; | ||
456 | sin->sin6_flowinfo = 0; | ||
457 | sin->sin6_port = 0; | ||
458 | sin->sin6_scope_id = mtu_info.ip6m_addr.sin6_scope_id; | ||
459 | ipv6_addr_copy(&sin->sin6_addr, &mtu_info.ip6m_addr.sin6_addr); | ||
460 | } | ||
461 | |||
462 | put_cmsg(msg, SOL_IPV6, IPV6_PATHMTU, sizeof(mtu_info), &mtu_info); | ||
463 | |||
464 | err = copied; | ||
465 | |||
466 | out_free_skb: | ||
467 | kfree_skb(skb); | ||
468 | out: | ||
469 | return err; | ||
470 | } | ||
384 | 471 | ||
385 | 472 | ||
386 | int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) | 473 | int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) |
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 54d43dd1f085..61e2bef56090 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -1219,15 +1219,23 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, | |||
1219 | */ | 1219 | */ |
1220 | 1220 | ||
1221 | inet->cork.length += length; | 1221 | inet->cork.length += length; |
1222 | if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) && | 1222 | if (length > mtu) { |
1223 | (rt->u.dst.dev->features & NETIF_F_UFO)) { | 1223 | int proto = sk->sk_protocol; |
1224 | if (dontfrag && (proto == IPPROTO_UDP || proto == IPPROTO_RAW)){ | ||
1225 | ipv6_local_rxpmtu(sk, fl, mtu-exthdrlen); | ||
1226 | return -EMSGSIZE; | ||
1227 | } | ||
1224 | 1228 | ||
1225 | err = ip6_ufo_append_data(sk, getfrag, from, length, hh_len, | 1229 | if (proto == IPPROTO_UDP && |
1226 | fragheaderlen, transhdrlen, mtu, | 1230 | (rt->u.dst.dev->features & NETIF_F_UFO)) { |
1227 | flags); | 1231 | |
1228 | if (err) | 1232 | err = ip6_ufo_append_data(sk, getfrag, from, length, |
1229 | goto error; | 1233 | hh_len, fragheaderlen, |
1230 | return 0; | 1234 | transhdrlen, mtu, flags); |
1235 | if (err) | ||
1236 | goto error; | ||
1237 | return 0; | ||
1238 | } | ||
1231 | } | 1239 | } |
1232 | 1240 | ||
1233 | if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) | 1241 | if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) |
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 44a84ea9b3e8..85627386cb02 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c | |||
@@ -461,6 +461,9 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk, | |||
461 | if (flags & MSG_ERRQUEUE) | 461 | if (flags & MSG_ERRQUEUE) |
462 | return ipv6_recv_error(sk, msg, len); | 462 | return ipv6_recv_error(sk, msg, len); |
463 | 463 | ||
464 | if (np->rxpmtu && np->rxopt.bits.rxpmtu) | ||
465 | return ipv6_recv_rxpmtu(sk, msg, len); | ||
466 | |||
464 | skb = skb_recv_datagram(sk, flags, noblock, &err); | 467 | skb = skb_recv_datagram(sk, flags, noblock, &err); |
465 | if (!skb) | 468 | if (!skb) |
466 | goto out; | 469 | goto out; |
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 39e3665d9460..2850e35cee3d 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -335,6 +335,9 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, | |||
335 | if (flags & MSG_ERRQUEUE) | 335 | if (flags & MSG_ERRQUEUE) |
336 | return ipv6_recv_error(sk, msg, len); | 336 | return ipv6_recv_error(sk, msg, len); |
337 | 337 | ||
338 | if (np->rxpmtu && np->rxopt.bits.rxpmtu) | ||
339 | return ipv6_recv_rxpmtu(sk, msg, len); | ||
340 | |||
338 | try_again: | 341 | try_again: |
339 | skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), | 342 | skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), |
340 | &peeked, &err); | 343 | &peeked, &err); |