Commit 4b340ae20d0e2366792abe70f46629e576adaf5e
Committed by
David S. Miller
1 parent
13b52cd446
Exists in
master
and in
39 other branches
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; |
include/net/ipv6.h
... | ... | @@ -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, |
net/ipv6/af_inet6.c
net/ipv6/datagram.c
... | ... | @@ -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) |
net/ipv6/raw.c
... | ... | @@ -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; |
net/ipv6/udp.c
... | ... | @@ -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); |