Commit 4b340ae20d0e2366792abe70f46629e576adaf5e

Authored by Brian Haley
Committed by David S. Miller
1 parent 13b52cd446

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>

Showing 7 changed files with 116 additions and 8 deletions Side-by-side Diff

include/linux/ipv6.h
... ... @@ -257,6 +257,7 @@
257 257 };
258 258  
259 259 #define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb))
  260 +#define IP6CBMTU(skb) ((struct ip6_mtuinfo *)((skb)->cb))
260 261  
261 262 static inline int inet6_iif(const struct sk_buff *skb)
262 263 {
... ... @@ -366,6 +367,7 @@
366 367  
367 368 struct ipv6_txoptions *opt;
368 369 struct sk_buff *pktoptions;
  370 + struct sk_buff *rxpmtu;
369 371 struct {
370 372 struct ipv6_txoptions *opt;
371 373 u8 hop_limit;
... ... @@ -578,9 +578,11 @@
578 578 struct sockaddr *addr, int addr_len);
579 579  
580 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 582 extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port,
582 583 u32 info, u8 *payload);
583 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 587 extern int inet6_release(struct socket *sock);
586 588 extern int inet6_bind(struct socket *sock, struct sockaddr *uaddr,
... ... @@ -417,6 +417,9 @@
417 417 if ((skb = xchg(&np->pktoptions, NULL)) != NULL)
418 418 kfree_skb(skb);
419 419  
  420 + if ((skb = xchg(&np->rxpmtu, NULL)) != NULL)
  421 + kfree_skb(skb);
  422 +
420 423 /* Free flowlabels */
421 424 fl6_free_socklist(sk);
422 425  
... ... @@ -278,6 +278,45 @@
278 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 321 * Handle MSG_ERRQUEUE
283 322 */
... ... @@ -381,6 +420,54 @@
381 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 473 int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
net/ipv6/ip6_output.c
... ... @@ -1219,15 +1219,23 @@
1219 1219 */
1220 1220  
1221 1221 inet->cork.length += length;
1222   - if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) &&
1223   - (rt->u.dst.dev->features & NETIF_F_UFO)) {
  1222 + if (length > mtu) {
  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,
1226   - fragheaderlen, transhdrlen, mtu,
1227   - flags);
1228   - if (err)
1229   - goto error;
1230   - return 0;
  1229 + if (proto == IPPROTO_UDP &&
  1230 + (rt->u.dst.dev->features & NETIF_F_UFO)) {
  1231 +
  1232 + err = ip6_ufo_append_data(sk, getfrag, from, length,
  1233 + hh_len, fragheaderlen,
  1234 + transhdrlen, mtu, flags);
  1235 + if (err)
  1236 + goto error;
  1237 + return 0;
  1238 + }
1231 1239 }
1232 1240  
1233 1241 if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)
... ... @@ -461,6 +461,9 @@
461 461 if (flags & MSG_ERRQUEUE)
462 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 467 skb = skb_recv_datagram(sk, flags, noblock, &err);
465 468 if (!skb)
466 469 goto out;
... ... @@ -335,6 +335,9 @@
335 335 if (flags & MSG_ERRQUEUE)
336 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 341 try_again:
339 342 skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
340 343 &peeked, &err);