diff options
-rw-r--r-- | net/ipv6/ip6_output.c | 13 |
1 files changed, 9 insertions, 4 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 1b6637fb99cb..d68a0c72cc93 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1151,7 +1151,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, struct ipv6_pinfo *np = inet6_sk(sk); struct inet_cork *cork; struct sk_buff *skb, *skb_prev = NULL; - unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu; + unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu, pmtu; int exthdrlen; int dst_exthdrlen; int hh_len; @@ -1259,6 +1259,12 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, else maxnonfragsize = mtu; + /* as per RFC 7112 section 5, the entire IPv6 Header Chain must fit + * the first fragment + */ + if (headersize + transhdrlen > mtu) + goto emsgsize; + /* dontfrag active */ if ((cork->length + length > mtu - headersize) && dontfrag && (sk->sk_protocol == IPPROTO_UDP || @@ -1270,9 +1276,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, if (cork->length + length > maxnonfragsize - headersize) { emsgsize: - ipv6_local_error(sk, EMSGSIZE, fl6, - mtu - headersize + - sizeof(struct ipv6hdr)); + pmtu = max_t(int, mtu - headersize + sizeof(struct ipv6hdr), 0); + ipv6_local_error(sk, EMSGSIZE, fl6, pmtu); return -EMSGSIZE; } } |