xt_TCPMSS has local logic for scanning TCP options and lowering an existing MSS option without increasing it.
Move that scan-and-clamp logic into a TCP helper so other networking code can reuse it without duplicating TCP option parsing. Keep xt_TCPMSS responsible for the policy-specific behavior of adding a missing MSS option. The helper returns 0 when an existing MSS option was handled, whether or not it had to be lowered. It returns -ENOENT when no MSS option is present and -EINVAL when option parsing finds a malformed option block. xt_TCPMSS treats both non-zero cases like the previous local scan failing to find a clampable MSS option, so the target keeps its existing fallback behavior for adding a missing MSS option or accepting the packet unchanged. The helper expects the caller to ensure that the TCP header and options are linear and writable. Signed-off-by: Ralf Lici <[email protected]> --- include/net/tcp.h | 2 ++ net/ipv4/tcp.c | 60 +++++++++++++++++++++++++++++++++++++++ net/netfilter/xt_TCPMSS.c | 36 ++--------------------- 3 files changed, 64 insertions(+), 34 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 3c4e6adb0dbd..e722c7d936bf 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -538,6 +538,8 @@ void tcp_parse_options(const struct net *net, const struct sk_buff *skb, struct tcp_options_received *opt_rx, int estab, struct tcp_fastopen_cookie *foc); +int tcp_clamp_mss_option(struct sk_buff *skb, struct tcphdr *th, u16 maxmss); + /* * BPF SKB-less helpers */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 21ece4c71612..ef10e530a1ad 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -721,6 +721,66 @@ static inline void tcp_mark_urg(struct tcp_sock *tp, int flags) tp->snd_up = tp->write_seq; } +/** + * tcp_clamp_mss_option - clamp an existing TCP MSS option + * @skb: skb containing the TCP segment + * @th: TCP header in @skb + * @maxmss: upper bound for the TCP MSS option value + * + * Parse the TCP option block and lower an existing MSS option to @maxmss. + * The MSS value is never increased. If the MSS value is changed, the TCP + * checksum in @th is updated. + * + * The caller must ensure that @th and the complete TCP option block are + * present in the linear data area and writable. + * + * Return: 0 on success or when no update is needed, -ENOENT when no MSS + * option is present, or -EINVAL when the TCP option block is malformed. + */ +int tcp_clamp_mss_option(struct sk_buff *skb, struct tcphdr *th, u16 maxmss) +{ + int length = th->doff * 4 - sizeof(*th); + u8 *ptr = (u8 *)(th + 1); + u16 oldmss; + + while (length > 0) { + int opcode = *ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return -ENOENT; + case TCPOPT_NOP: + length--; + continue; + default: + if (length < 2) + return -EINVAL; + + opsize = *ptr++; + if (opsize < 2 || opsize > length) + return -EINVAL; + + if (opcode == TCPOPT_MSS && opsize == TCPOLEN_MSS) { + oldmss = get_unaligned_be16(ptr); + if (!oldmss || oldmss <= maxmss) + return 0; + + put_unaligned_be16(maxmss, ptr); + inet_proto_csum_replace2(&th->check, skb, + htons(oldmss), + htons(maxmss), false); + return 0; + } + + ptr += opsize - 2; + length -= opsize; + } + } + return -ENOENT; +} +EXPORT_SYMBOL_GPL(tcp_clamp_mss_option); + /* If a not yet filled skb is pushed, do not send it if * we have data packets in Qdisc or NIC queues : * Because TX completion will happen shortly, it gives a chance diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index 80e1634bc51f..70983b757229 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -30,16 +30,6 @@ MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment"); MODULE_ALIAS("ipt_TCPMSS"); MODULE_ALIAS("ip6t_TCPMSS"); -static inline unsigned int -optlen(const u_int8_t *opt, unsigned int offset) -{ - /* Beware zero-length options: make finite progress */ - if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0) - return 1; - else - return opt[offset+1]; -} - static u_int32_t tcpmss_reverse_mtu(struct net *net, const struct sk_buff *skb, unsigned int family) @@ -77,7 +67,6 @@ tcpmss_mangle_packet(struct sk_buff *skb, const struct xt_tcpmss_info *info = par->targinfo; struct tcphdr *tcph; int len, tcp_hdrlen; - unsigned int i; __be16 oldval; u16 newmss; u8 *opt; @@ -113,29 +102,8 @@ tcpmss_mangle_packet(struct sk_buff *skb, } else newmss = info->mss; - opt = (u_int8_t *)tcph; - 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]; - - /* Never increase MSS, even when setting it, as - * doing so results in problems for hosts that rely - * on MSS being set correctly. - */ - if (oldmss <= newmss) - return 0; - - opt[i+2] = (newmss & 0xff00) >> 8; - opt[i+3] = newmss & 0x00ff; - - inet_proto_csum_replace2(&tcph->check, skb, - htons(oldmss), htons(newmss), - false); - return 0; - } - } + if (tcp_clamp_mss_option(skb, tcph, newmss) == 0) + return 0; /* There is data after the header so the option can't be added * without moving it, and doing so may make the SYN packet -- 2.54.0 _______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel
