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.
This commit is contained in:
Samuel Thibault 2023-11-05 17:21:14 +01:00
parent 7514eecf68
commit 3667bdfe80
3 changed files with 42 additions and 10 deletions

View file

@ -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;