From 3667bdfe80dde2c72fc0224e3995eed4cad4457b Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Sun, 5 Nov 2023 17:21:14 +0100 Subject: [PATCH] Also clamp MSS on IPv6 Some routers erratically drop "Packet too big" icmp messages, and PMTU discovery then doesn't work. We can however easily clamp MSS on IPv6 too. --- l2tpns.c | 38 ++++++++++++++++++++++++++++++-------- l2tpns.h | 4 +++- ppp.c | 10 +++++++++- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/l2tpns.c b/l2tpns.c index f756e74..03da55f 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -90,6 +90,7 @@ int guest_accounts_num = 0; // Number of guest users // calculated from config->l2tp_mtu uint16_t MRU = 0; // PPP MRU uint16_t MSS = 0; // TCP MSS +uint16_t MSS6 = 0; // TCPv6 MSS struct cli_session_actions *cli_session_actions = NULL; // Pending session changes requested by CLI struct cli_tunnel_actions *cli_tunnel_actions = NULL; // Pending tunnel changes required by CLI @@ -1322,7 +1323,7 @@ int tun_write(uint8_t * data, int size) } // adjust tcp mss to avoid fragmentation (called only for tcp packets with syn set) -void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *tcp) +static void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *tcp, in_addr_t src, in_addr_t dst, uint16_t mss_clamp) { int d = (tcp[12] >> 4) * 4; uint8_t *mss = 0; @@ -1359,23 +1360,34 @@ void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *t if (!mss) return; // not found orig = ntohs(*(uint16_t *) mss); - if (orig <= MSS) return; // mss OK + if (orig <= mss_clamp) return; // mss OK LOG(5, s, t, "TCP: %s:%u -> %s:%u SYN%s: adjusted mss from %u to %u\n", - fmtaddr(*(in_addr_t *) (buf + 12), 0), ntohs(*(uint16_t *) tcp), - fmtaddr(*(in_addr_t *) (buf + 16), 1), ntohs(*(uint16_t *) (tcp + 2)), - (tcp[13] & TCP_FLAG_ACK) ? ",ACK" : "", orig, MSS); + fmtaddr(src, 0), ntohs(*(uint16_t *) tcp), + fmtaddr(dst, 1), ntohs(*(uint16_t *) (tcp + 2)), + (tcp[13] & TCP_FLAG_ACK) ? ",ACK" : "", orig, mss_clamp); // set mss - *(int16_t *) mss = htons(MSS); + *(int16_t *) mss = htons(mss_clamp); // adjust checksum (see rfc1141) - sum = orig + (~MSS & 0xffff); + sum = orig + (~mss_clamp & 0xffff); sum += ntohs(*(uint16_t *) (tcp + 16)); sum = (sum & 0xffff) + (sum >> 16); *(uint16_t *) (tcp + 16) = htons(sum + (sum >> 16)); } +void adjust_tcp4_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *tcp) +{ + adjust_tcp_mss(s, t, buf, len, tcp, *(in_addr_t *) (buf + 12), *(in_addr_t *) (buf + 16), MSS); +} + +void adjust_tcp6_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *tcp) +{ + /* Showing the lower part of IPv6 addresses */ + adjust_tcp_mss(s, t, buf, len, tcp, *(in_addr_t *) (buf + 20), *(in_addr_t *) (buf + 36), MSS6); +} + void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t extra) { uint16_t proto; @@ -1565,7 +1577,7 @@ void processipout(uint8_t *buf, int len) { int ihl = (buf[0] & 0xf) * 4; // length of IP header if (len >= ihl + 20 && (buf[ihl + 13] & TCP_FLAG_SYN) && ((buf[ihl + 12] >> 4) > 5)) - adjust_tcp_mss(s, t, buf, len, buf + ihl); + adjust_tcp4_mss(s, t, buf, len, buf + ihl); } if (sp->tbf_out) @@ -1792,6 +1804,14 @@ static void processipv6out(uint8_t * buf, int len) // FIXME: add DoS prevention/filters? + // adjust MSS on SYN and SYN,ACK packets with options + if (buf[6] == IPPROTO_TCP) + { + int ihl = 40; // length of IPv6 header + if (len >= ihl + 20 && (buf[ihl + 13] & TCP_FLAG_SYN) && ((buf[ihl + 12] >> 4) > 5)) + adjust_tcp6_mss(s, t, buf, len, buf + ihl); + } + if (sp->tbf_out) { // Are we throttling this session? @@ -5391,6 +5411,7 @@ static void update_config() #define L2TP_HDRS (20+8+6+4) // L2TP data encaptulation: ip + udp + l2tp (data) + ppp (inc hdlc) #define TCP_HDRS (20+20) // TCP encapsulation: ip + tcp +#define TCP6_HDRS (40+20) // TCP encapsulation: ipv6 + tcp if (config->l2tp_mtu <= 0) config->l2tp_mtu = 1500; // ethernet default else if (config->l2tp_mtu < MINMTU) config->l2tp_mtu = MINMTU; @@ -5404,6 +5425,7 @@ static void update_config() MRU = PPPoE_MRU; MSS = MRU - TCP_HDRS; + MSS6 = MRU - TCP6_HDRS; // Update radius config->numradiusservers = 0; diff --git a/l2tpns.h b/l2tpns.h index fa6111f..3ec33da 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -969,7 +969,8 @@ void filter_session(sessionidt s, int filter_in, int filter_out); void send_garp(in_addr_t ip); void tunnelsend(uint8_t *buf, uint16_t l, tunnelidt t); int tun_write(uint8_t *data, int size); -void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *tcp); +void adjust_tcp4_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *tcp); +void adjust_tcp6_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *tcp); void sendipcp(sessionidt s, tunnelidt t); void sendipv6cp(sessionidt s, tunnelidt t); void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexudpfd); @@ -1054,6 +1055,7 @@ struct event_data { extern uint16_t MRU; extern uint16_t MSS; +extern uint16_t MSS6; // macros for handling help in cli commands #define CLI_HELP_REQUESTED (argc > 0 && argv[argc-1][strlen(argv[argc-1])-1] == '?') diff --git a/ppp.c b/ppp.c index c987bb7..aae7f86 100644 --- a/ppp.c +++ b/ppp.c @@ -1803,7 +1803,7 @@ void processipin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) { int ihl = (p[0] & 0xf) * 4; // length of IP header if (l >= ihl + 20 && (p[ihl + 13] & TCP_FLAG_SYN) && ((p[ihl + 12] >> 4) > 5)) - adjust_tcp_mss(s, t, p, l, p + ihl); + adjust_tcp4_mss(s, t, p, l, p + ihl); } // Add on the tun header @@ -2284,6 +2284,14 @@ void processipv6in(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) return; } + // adjust MSS on SYN and SYN,ACK packets with options + if (p[6] == IPPROTO_TCP) + { + int ihl = 40; // length of IPv6 header + if (l >= ihl + 20 && (p[ihl + 13] & TCP_FLAG_SYN) && ((p[ihl + 12] >> 4) > 5)) + adjust_tcp6_mss(s, t, p, l, p + ihl); + } + // Check if it's a Router Solicition message. if (*(p + 6) == 58 && *(p + 7) == 255 && *(p + 24) == 0xFF && *(p + 25) == 2 && *(uint32_t *)(p + 26) == 0 && *(uint32_t *)(p + 30) == 0 &&