Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4b340ae2 authored by Brian Haley's avatar Brian Haley Committed by David S. Miller
Browse files

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: default avatarBrian Haley <brian.haley@hp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 13b52cd4
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -257,6 +257,7 @@ struct inet6_skb_parm {
};

#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)
{
@@ -366,6 +367,7 @@ struct ipv6_pinfo {

	struct ipv6_txoptions	*opt;
	struct sk_buff		*pktoptions;
	struct sk_buff		*rxpmtu;
	struct {
		struct ipv6_txoptions *opt;
		u8 hop_limit;
+2 −0
Original line number Diff line number Diff line
@@ -578,9 +578,11 @@ extern int ip6_datagram_connect(struct sock *sk,
						     struct sockaddr *addr, int addr_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,
						u32 info, u8 *payload);
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_bind(struct socket *sock, struct sockaddr *uaddr, 
+3 −0
Original line number Diff line number Diff line
@@ -417,6 +417,9 @@ void inet6_destroy_sock(struct sock *sk)
	if ((skb = xchg(&np->pktoptions, NULL)) != NULL)
		kfree_skb(skb);

	if ((skb = xchg(&np->rxpmtu, NULL)) != NULL)
		kfree_skb(skb);

	/* Free flowlabels */
	fl6_free_socklist(sk);

+87 −0
Original line number Diff line number Diff line
@@ -278,6 +278,45 @@ void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info)
		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
 */
@@ -381,6 +420,54 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
	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)
+16 −8
Original line number Diff line number Diff line
@@ -1219,16 +1219,24 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
	 */

	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)) {

		err = ip6_ufo_append_data(sk, getfrag, from, length, hh_len,
					  fragheaderlen, transhdrlen, mtu,
					  flags);
			err = ip6_ufo_append_data(sk, getfrag, from, length,
						  hh_len, fragheaderlen,
						  transhdrlen, mtu, flags);
			if (err)
				goto error;
			return 0;
		}
	}

	if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)
		goto alloc_new_skb;
Loading