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>
This commit is contained in:
parent
13b52cd446
commit
4b340ae20d
7 changed files with 116 additions and 8 deletions
|
@ -257,6 +257,7 @@ struct inet6_skb_parm {
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb))
|
#define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb))
|
||||||
|
#define IP6CBMTU(skb) ((struct ip6_mtuinfo *)((skb)->cb))
|
||||||
|
|
||||||
static inline int inet6_iif(const struct sk_buff *skb)
|
static inline int inet6_iif(const struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
@ -366,6 +367,7 @@ struct ipv6_pinfo {
|
||||||
|
|
||||||
struct ipv6_txoptions *opt;
|
struct ipv6_txoptions *opt;
|
||||||
struct sk_buff *pktoptions;
|
struct sk_buff *pktoptions;
|
||||||
|
struct sk_buff *rxpmtu;
|
||||||
struct {
|
struct {
|
||||||
struct ipv6_txoptions *opt;
|
struct ipv6_txoptions *opt;
|
||||||
u8 hop_limit;
|
u8 hop_limit;
|
||||||
|
|
|
@ -578,9 +578,11 @@ extern int ip6_datagram_connect(struct sock *sk,
|
||||||
struct sockaddr *addr, int addr_len);
|
struct sockaddr *addr, int addr_len);
|
||||||
|
|
||||||
extern int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len);
|
extern int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len);
|
||||||
|
extern int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len);
|
||||||
extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port,
|
extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port,
|
||||||
u32 info, u8 *payload);
|
u32 info, u8 *payload);
|
||||||
extern void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info);
|
extern void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info);
|
||||||
|
extern void ipv6_local_rxpmtu(struct sock *sk, struct flowi *fl, u32 mtu);
|
||||||
|
|
||||||
extern int inet6_release(struct socket *sock);
|
extern int inet6_release(struct socket *sock);
|
||||||
extern int inet6_bind(struct socket *sock, struct sockaddr *uaddr,
|
extern int inet6_bind(struct socket *sock, struct sockaddr *uaddr,
|
||||||
|
|
|
@ -417,6 +417,9 @@ void inet6_destroy_sock(struct sock *sk)
|
||||||
if ((skb = xchg(&np->pktoptions, NULL)) != NULL)
|
if ((skb = xchg(&np->pktoptions, NULL)) != NULL)
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
if ((skb = xchg(&np->rxpmtu, NULL)) != NULL)
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
/* Free flowlabels */
|
/* Free flowlabels */
|
||||||
fl6_free_socklist(sk);
|
fl6_free_socklist(sk);
|
||||||
|
|
||||||
|
|
|
@ -278,6 +278,45 @@ void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info)
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ipv6_local_rxpmtu(struct sock *sk, struct flowi *fl, u32 mtu)
|
||||||
|
{
|
||||||
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||||
|
struct ipv6hdr *iph;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct ip6_mtuinfo *mtu_info;
|
||||||
|
|
||||||
|
if (!np->rxopt.bits.rxpmtu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC);
|
||||||
|
if (!skb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
skb_put(skb, sizeof(struct ipv6hdr));
|
||||||
|
skb_reset_network_header(skb);
|
||||||
|
iph = ipv6_hdr(skb);
|
||||||
|
ipv6_addr_copy(&iph->daddr, &fl->fl6_dst);
|
||||||
|
|
||||||
|
mtu_info = IP6CBMTU(skb);
|
||||||
|
if (!mtu_info) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtu_info->ip6m_mtu = mtu;
|
||||||
|
mtu_info->ip6m_addr.sin6_family = AF_INET6;
|
||||||
|
mtu_info->ip6m_addr.sin6_port = 0;
|
||||||
|
mtu_info->ip6m_addr.sin6_flowinfo = 0;
|
||||||
|
mtu_info->ip6m_addr.sin6_scope_id = fl->oif;
|
||||||
|
ipv6_addr_copy(&mtu_info->ip6m_addr.sin6_addr, &ipv6_hdr(skb)->daddr);
|
||||||
|
|
||||||
|
__skb_pull(skb, skb_tail_pointer(skb) - skb->data);
|
||||||
|
skb_reset_transport_header(skb);
|
||||||
|
|
||||||
|
skb = xchg(&np->rxpmtu, skb);
|
||||||
|
kfree_skb(skb);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle MSG_ERRQUEUE
|
* Handle MSG_ERRQUEUE
|
||||||
*/
|
*/
|
||||||
|
@ -381,6 +420,54 @@ out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle IPV6_RECVPATHMTU
|
||||||
|
*/
|
||||||
|
int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len)
|
||||||
|
{
|
||||||
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct sockaddr_in6 *sin;
|
||||||
|
struct ip6_mtuinfo mtu_info;
|
||||||
|
int err;
|
||||||
|
int copied;
|
||||||
|
|
||||||
|
err = -EAGAIN;
|
||||||
|
skb = xchg(&np->rxpmtu, NULL);
|
||||||
|
if (skb == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
copied = skb->len;
|
||||||
|
if (copied > len) {
|
||||||
|
msg->msg_flags |= MSG_TRUNC;
|
||||||
|
copied = len;
|
||||||
|
}
|
||||||
|
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
|
||||||
|
if (err)
|
||||||
|
goto out_free_skb;
|
||||||
|
|
||||||
|
sock_recv_timestamp(msg, sk, skb);
|
||||||
|
|
||||||
|
memcpy(&mtu_info, IP6CBMTU(skb), sizeof(mtu_info));
|
||||||
|
|
||||||
|
sin = (struct sockaddr_in6 *)msg->msg_name;
|
||||||
|
if (sin) {
|
||||||
|
sin->sin6_family = AF_INET6;
|
||||||
|
sin->sin6_flowinfo = 0;
|
||||||
|
sin->sin6_port = 0;
|
||||||
|
sin->sin6_scope_id = mtu_info.ip6m_addr.sin6_scope_id;
|
||||||
|
ipv6_addr_copy(&sin->sin6_addr, &mtu_info.ip6m_addr.sin6_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
put_cmsg(msg, SOL_IPV6, IPV6_PATHMTU, sizeof(mtu_info), &mtu_info);
|
||||||
|
|
||||||
|
err = copied;
|
||||||
|
|
||||||
|
out_free_skb:
|
||||||
|
kfree_skb(skb);
|
||||||
|
out:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
|
int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
|
||||||
|
|
|
@ -1219,16 +1219,24 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
inet->cork.length += length;
|
inet->cork.length += length;
|
||||||
if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) &&
|
if (length > mtu) {
|
||||||
|
int proto = sk->sk_protocol;
|
||||||
|
if (dontfrag && (proto == IPPROTO_UDP || proto == IPPROTO_RAW)){
|
||||||
|
ipv6_local_rxpmtu(sk, fl, mtu-exthdrlen);
|
||||||
|
return -EMSGSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proto == IPPROTO_UDP &&
|
||||||
(rt->u.dst.dev->features & NETIF_F_UFO)) {
|
(rt->u.dst.dev->features & NETIF_F_UFO)) {
|
||||||
|
|
||||||
err = ip6_ufo_append_data(sk, getfrag, from, length, hh_len,
|
err = ip6_ufo_append_data(sk, getfrag, from, length,
|
||||||
fragheaderlen, transhdrlen, mtu,
|
hh_len, fragheaderlen,
|
||||||
flags);
|
transhdrlen, mtu, flags);
|
||||||
if (err)
|
if (err)
|
||||||
goto error;
|
goto error;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)
|
if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)
|
||||||
goto alloc_new_skb;
|
goto alloc_new_skb;
|
||||||
|
|
|
@ -461,6 +461,9 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk,
|
||||||
if (flags & MSG_ERRQUEUE)
|
if (flags & MSG_ERRQUEUE)
|
||||||
return ipv6_recv_error(sk, msg, len);
|
return ipv6_recv_error(sk, msg, len);
|
||||||
|
|
||||||
|
if (np->rxpmtu && np->rxopt.bits.rxpmtu)
|
||||||
|
return ipv6_recv_rxpmtu(sk, msg, len);
|
||||||
|
|
||||||
skb = skb_recv_datagram(sk, flags, noblock, &err);
|
skb = skb_recv_datagram(sk, flags, noblock, &err);
|
||||||
if (!skb)
|
if (!skb)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -335,6 +335,9 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
|
||||||
if (flags & MSG_ERRQUEUE)
|
if (flags & MSG_ERRQUEUE)
|
||||||
return ipv6_recv_error(sk, msg, len);
|
return ipv6_recv_error(sk, msg, len);
|
||||||
|
|
||||||
|
if (np->rxpmtu && np->rxopt.bits.rxpmtu)
|
||||||
|
return ipv6_recv_rxpmtu(sk, msg, len);
|
||||||
|
|
||||||
try_again:
|
try_again:
|
||||||
skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
|
skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
|
||||||
&peeked, &err);
|
&peeked, &err);
|
||||||
|
|
Loading…
Add table
Reference in a new issue