Skip to content

Commit 4b340ae

Browse files
Brian Haleydavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 13b52cd commit 4b340ae

File tree

7 files changed

+116
-8
lines changed

7 files changed

+116
-8
lines changed

include/linux/ipv6.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ struct inet6_skb_parm {
257257
};
258258

259259
#define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb))
260+
#define IP6CBMTU(skb) ((struct ip6_mtuinfo *)((skb)->cb))
260261

261262
static inline int inet6_iif(const struct sk_buff *skb)
262263
{
@@ -366,6 +367,7 @@ struct ipv6_pinfo {
366367

367368
struct ipv6_txoptions *opt;
368369
struct sk_buff *pktoptions;
370+
struct sk_buff *rxpmtu;
369371
struct {
370372
struct ipv6_txoptions *opt;
371373
u8 hop_limit;

include/net/ipv6.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,9 +578,11 @@ extern int ip6_datagram_connect(struct sock *sk,
578578
struct sockaddr *addr, int addr_len);
579579

580580
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);
581582
extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port,
582583
u32 info, u8 *payload);
583584
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);
584586

585587
extern int inet6_release(struct socket *sock);
586588
extern int inet6_bind(struct socket *sock, struct sockaddr *uaddr,

net/ipv6/af_inet6.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,9 @@ void inet6_destroy_sock(struct sock *sk)
417417
if ((skb = xchg(&np->pktoptions, NULL)) != NULL)
418418
kfree_skb(skb);
419419

420+
if ((skb = xchg(&np->rxpmtu, NULL)) != NULL)
421+
kfree_skb(skb);
422+
420423
/* Free flowlabels */
421424
fl6_free_socklist(sk);
422425

net/ipv6/datagram.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,45 @@ void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info)
278278
kfree_skb(skb);
279279
}
280280

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+
281320
/*
282321
* Handle MSG_ERRQUEUE
283322
*/
@@ -381,6 +420,54 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
381420
return err;
382421
}
383422

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+
}
384471

385472

386473
int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)

net/ipv6/ip6_output.c

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,15 +1219,23 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
12191219
*/
12201220

12211221
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+
}
12241228

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+
}
12311239
}
12321240

12331241
if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)

net/ipv6/raw.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,9 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk,
461461
if (flags & MSG_ERRQUEUE)
462462
return ipv6_recv_error(sk, msg, len);
463463

464+
if (np->rxpmtu && np->rxopt.bits.rxpmtu)
465+
return ipv6_recv_rxpmtu(sk, msg, len);
466+
464467
skb = skb_recv_datagram(sk, flags, noblock, &err);
465468
if (!skb)
466469
goto out;

net/ipv6/udp.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,9 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
335335
if (flags & MSG_ERRQUEUE)
336336
return ipv6_recv_error(sk, msg, len);
337337

338+
if (np->rxpmtu && np->rxopt.bits.rxpmtu)
339+
return ipv6_recv_rxpmtu(sk, msg, len);
340+
338341
try_again:
339342
skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
340343
&peeked, &err);

0 commit comments

Comments
 (0)