aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Haley <brian.haley@hp.com>2010-04-23 07:26:09 -0400
committerDavid S. Miller <davem@davemloft.net>2010-04-24 02:35:29 -0400
commit4b340ae20d0e2366792abe70f46629e576adaf5e (patch)
treeb0c413a0348e722fbc23d45508224076b6e60f92
parent13b52cd44670e3359055e9918d0e766d89836425 (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.h2
-rw-r--r--include/net/ipv6.h2
-rw-r--r--net/ipv6/af_inet6.c3
-rw-r--r--net/ipv6/datagram.c87
-rw-r--r--net/ipv6/ip6_output.c24
-rw-r--r--net/ipv6/raw.c3
-rw-r--r--net/ipv6/udp.c3
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
261static inline int inet6_iif(const struct sk_buff *skb) 262static 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
580extern int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len); 580extern int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len);
581extern int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len);
581extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port, 582extern 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);
583extern void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info); 584extern void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info);
585extern void ipv6_local_rxpmtu(struct sock *sk, struct flowi *fl, u32 mtu);
584 586
585extern int inet6_release(struct socket *sock); 587extern int inet6_release(struct socket *sock);
586extern int inet6_bind(struct socket *sock, struct sockaddr *uaddr, 588extern 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
281void 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 */
426int 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
466out_free_skb:
467 kfree_skb(skb);
468out:
469 return err;
470}
384 471
385 472
386int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) 473int 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
338try_again: 341try_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);