diff options
Diffstat (limited to 'net/netfilter/xt_TCPMSS.c')
| -rw-r--r-- | net/netfilter/xt_TCPMSS.c | 138 | 
1 files changed, 84 insertions, 54 deletions
diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index eb81c380da1..e762de5ee89 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -2,6 +2,7 @@   * This is a module which is used for setting the MSS option in TCP packets.   *   * Copyright (C) 2000 Marc Boucher <marc@mbsi.ca> + * Copyright (C) 2007 Patrick McHardy <kaber@trash.net>   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License version 2 as @@ -42,40 +43,82 @@ optlen(const u_int8_t *opt, unsigned int offset)  		return opt[offset+1];  } +static u_int32_t tcpmss_reverse_mtu(struct net *net, +				    const struct sk_buff *skb, +				    unsigned int family) +{ +	struct flowi fl; +	const struct nf_afinfo *ai; +	struct rtable *rt = NULL; +	u_int32_t mtu     = ~0U; + +	if (family == PF_INET) { +		struct flowi4 *fl4 = &fl.u.ip4; +		memset(fl4, 0, sizeof(*fl4)); +		fl4->daddr = ip_hdr(skb)->saddr; +	} else { +		struct flowi6 *fl6 = &fl.u.ip6; + +		memset(fl6, 0, sizeof(*fl6)); +		fl6->daddr = ipv6_hdr(skb)->saddr; +	} +	rcu_read_lock(); +	ai = nf_get_afinfo(family); +	if (ai != NULL) +		ai->route(net, (struct dst_entry **)&rt, &fl, false); +	rcu_read_unlock(); + +	if (rt != NULL) { +		mtu = dst_mtu(&rt->dst); +		dst_release(&rt->dst); +	} +	return mtu; +} +  static int  tcpmss_mangle_packet(struct sk_buff *skb, -		     const struct xt_tcpmss_info *info, -		     unsigned int in_mtu, +		     const struct xt_action_param *par, +		     unsigned int family,  		     unsigned int tcphoff,  		     unsigned int minlen)  { +	const struct xt_tcpmss_info *info = par->targinfo;  	struct tcphdr *tcph; -	unsigned int tcplen, i; +	int len, tcp_hdrlen; +	unsigned int i;  	__be16 oldval;  	u16 newmss;  	u8 *opt; +	/* This is a fragment, no TCP header is available */ +	if (par->fragoff != 0) +		return 0; +  	if (!skb_make_writable(skb, skb->len))  		return -1; -	tcplen = skb->len - tcphoff; +	len = skb->len - tcphoff; +	if (len < (int)sizeof(struct tcphdr)) +		return -1; +  	tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); +	tcp_hdrlen = tcph->doff * 4; -	/* Header cannot be larger than the packet */ -	if (tcplen < tcph->doff*4) +	if (len < tcp_hdrlen)  		return -1;  	if (info->mss == XT_TCPMSS_CLAMP_PMTU) { +		struct net *net = dev_net(par->in ? par->in : par->out); +		unsigned int in_mtu = tcpmss_reverse_mtu(net, skb, family); +  		if (dst_mtu(skb_dst(skb)) <= minlen) { -			if (net_ratelimit()) -				pr_err("unknown or invalid path-MTU (%u)\n", -				       dst_mtu(skb_dst(skb))); +			net_err_ratelimited("unknown or invalid path-MTU (%u)\n", +					    dst_mtu(skb_dst(skb)));  			return -1;  		}  		if (in_mtu <= minlen) { -			if (net_ratelimit()) -				pr_err("unknown or invalid path-MTU (%u)\n", -				       in_mtu); +			net_err_ratelimited("unknown or invalid path-MTU (%u)\n", +					    in_mtu);  			return -1;  		}  		newmss = min(dst_mtu(skb_dst(skb)), in_mtu) - minlen; @@ -83,9 +126,8 @@ tcpmss_mangle_packet(struct sk_buff *skb,  		newmss = info->mss;  	opt = (u_int8_t *)tcph; -	for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) { -		if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS && -		    opt[i+1] == TCPOLEN_MSS) { +	for (i = sizeof(struct tcphdr); i <= tcp_hdrlen - TCPOLEN_MSS; i += optlen(opt, i)) { +		if (opt[i] == TCPOPT_MSS && opt[i+1] == TCPOLEN_MSS) {  			u_int16_t oldmss;  			oldmss = (opt[i+2] << 8) | opt[i+3]; @@ -108,9 +150,10 @@ tcpmss_mangle_packet(struct sk_buff *skb,  	}  	/* There is data after the header so the option can't be added -	   without moving it, and doing so may make the SYN packet -	   itself too large. Accept the packet unmodified instead. */ -	if (tcplen > tcph->doff*4) +	 * without moving it, and doing so may make the SYN packet +	 * itself too large. Accept the packet unmodified instead. +	 */ +	if (len > tcp_hdrlen)  		return 0;  	/* @@ -126,11 +169,23 @@ tcpmss_mangle_packet(struct sk_buff *skb,  	skb_put(skb, TCPOLEN_MSS); +	/* +	 * IPv4: RFC 1122 states "If an MSS option is not received at +	 * connection setup, TCP MUST assume a default send MSS of 536". +	 * IPv6: RFC 2460 states IPv6 has a minimum MTU of 1280 and a minimum +	 * length IPv6 header of 60, ergo the default MSS value is 1220 +	 * Since no MSS was provided, we must use the default values +	 */ +	if (par->family == NFPROTO_IPV4) +		newmss = min(newmss, (u16)536); +	else +		newmss = min(newmss, (u16)1220); +  	opt = (u_int8_t *)tcph + sizeof(struct tcphdr); -	memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr)); +	memmove(opt + TCPOLEN_MSS, opt, len - sizeof(struct tcphdr));  	inet_proto_csum_replace2(&tcph->check, skb, -				 htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1); +				 htons(len), htons(len + TCPOLEN_MSS), 1);  	opt[0] = TCPOPT_MSS;  	opt[1] = TCPOLEN_MSS;  	opt[2] = (newmss & 0xff00) >> 8; @@ -145,32 +200,6 @@ tcpmss_mangle_packet(struct sk_buff *skb,  	return TCPOLEN_MSS;  } -static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb, -				    unsigned int family) -{ -	struct flowi fl = {}; -	const struct nf_afinfo *ai; -	struct rtable *rt = NULL; -	u_int32_t mtu     = ~0U; - -	if (family == PF_INET) -		fl.fl4_dst = ip_hdr(skb)->saddr; -	else -		fl.fl6_dst = ipv6_hdr(skb)->saddr; - -	rcu_read_lock(); -	ai = nf_get_afinfo(family); -	if (ai != NULL) -		ai->route((struct dst_entry **)&rt, &fl); -	rcu_read_unlock(); - -	if (rt != NULL) { -		mtu = dst_mtu(&rt->dst); -		dst_release(&rt->dst); -	} -	return mtu; -} -  static unsigned int  tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)  { @@ -178,8 +207,8 @@ tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)  	__be16 newlen;  	int ret; -	ret = tcpmss_mangle_packet(skb, par->targinfo, -				   tcpmss_reverse_mtu(skb, PF_INET), +	ret = tcpmss_mangle_packet(skb, par, +				   PF_INET,  				   iph->ihl * 4,  				   sizeof(*iph) + sizeof(struct tcphdr));  	if (ret < 0) @@ -193,21 +222,22 @@ tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)  	return XT_CONTINUE;  } -#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)  static unsigned int  tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par)  {  	struct ipv6hdr *ipv6h = ipv6_hdr(skb);  	u8 nexthdr; +	__be16 frag_off;  	int tcphoff;  	int ret;  	nexthdr = ipv6h->nexthdr; -	tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr); +	tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off);  	if (tcphoff < 0)  		return NF_DROP; -	ret = tcpmss_mangle_packet(skb, par->targinfo, -				   tcpmss_reverse_mtu(skb, PF_INET6), +	ret = tcpmss_mangle_packet(skb, par, +				   PF_INET6,  				   tcphoff,  				   sizeof(*ipv6h) + sizeof(struct tcphdr));  	if (ret < 0) @@ -254,7 +284,7 @@ static int tcpmss_tg4_check(const struct xt_tgchk_param *par)  	return -EINVAL;  } -#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)  static int tcpmss_tg6_check(const struct xt_tgchk_param *par)  {  	const struct xt_tcpmss_info *info = par->targinfo; @@ -287,7 +317,7 @@ static struct xt_target tcpmss_tg_reg[] __read_mostly = {  		.proto		= IPPROTO_TCP,  		.me		= THIS_MODULE,  	}, -#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)  	{  		.family		= NFPROTO_IPV6,  		.name		= "TCPMSS",  | 
