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 // calculated from config->l2tp_mtu
uint16_t MRU = 0; // PPP MRU uint16_t MRU = 0; // PPP MRU
uint16_t MSS = 0; // TCP MSS 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_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 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) // 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; int d = (tcp[12] >> 4) * 4;
uint8_t *mss = 0; 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 if (!mss) return; // not found
orig = ntohs(*(uint16_t *) mss); 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", 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(src, 0), ntohs(*(uint16_t *) tcp),
fmtaddr(*(in_addr_t *) (buf + 16), 1), ntohs(*(uint16_t *) (tcp + 2)), fmtaddr(dst, 1), ntohs(*(uint16_t *) (tcp + 2)),
(tcp[13] & TCP_FLAG_ACK) ? ",ACK" : "", orig, MSS); (tcp[13] & TCP_FLAG_ACK) ? ",ACK" : "", orig, mss_clamp);
// set mss // set mss
*(int16_t *) mss = htons(MSS); *(int16_t *) mss = htons(mss_clamp);
// adjust checksum (see rfc1141) // adjust checksum (see rfc1141)
sum = orig + (~MSS & 0xffff); sum = orig + (~mss_clamp & 0xffff);
sum += ntohs(*(uint16_t *) (tcp + 16)); sum += ntohs(*(uint16_t *) (tcp + 16));
sum = (sum & 0xffff) + (sum >> 16); sum = (sum & 0xffff) + (sum >> 16);
*(uint16_t *) (tcp + 16) = htons(sum + (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) void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t extra)
{ {
uint16_t proto; uint16_t proto;
@ -1565,7 +1577,7 @@ void processipout(uint8_t *buf, int len)
{ {
int ihl = (buf[0] & 0xf) * 4; // length of IP header 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)) 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) if (sp->tbf_out)
@ -1792,6 +1804,14 @@ static void processipv6out(uint8_t * buf, int len)
// FIXME: add DoS prevention/filters? // 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) if (sp->tbf_out)
{ {
// Are we throttling this session? // 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 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 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 if (config->l2tp_mtu <= 0) config->l2tp_mtu = 1500; // ethernet default
else if (config->l2tp_mtu < MINMTU) config->l2tp_mtu = MINMTU; else if (config->l2tp_mtu < MINMTU) config->l2tp_mtu = MINMTU;
@ -5404,6 +5425,7 @@ static void update_config()
MRU = PPPoE_MRU; MRU = PPPoE_MRU;
MSS = MRU - TCP_HDRS; MSS = MRU - TCP_HDRS;
MSS6 = MRU - TCP6_HDRS;
// Update radius // Update radius
config->numradiusservers = 0; config->numradiusservers = 0;

View file

@ -969,7 +969,8 @@ void filter_session(sessionidt s, int filter_in, int filter_out);
void send_garp(in_addr_t ip); void send_garp(in_addr_t ip);
void tunnelsend(uint8_t *buf, uint16_t l, tunnelidt t); void tunnelsend(uint8_t *buf, uint16_t l, tunnelidt t);
int tun_write(uint8_t *data, int size); 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 sendipcp(sessionidt s, tunnelidt t);
void sendipv6cp(sessionidt s, tunnelidt t); void sendipv6cp(sessionidt s, tunnelidt t);
void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexudpfd); 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 MRU;
extern uint16_t MSS; extern uint16_t MSS;
extern uint16_t MSS6;
// macros for handling help in cli commands // macros for handling help in cli commands
#define CLI_HELP_REQUESTED (argc > 0 && argv[argc-1][strlen(argv[argc-1])-1] == '?') #define CLI_HELP_REQUESTED (argc > 0 && argv[argc-1][strlen(argv[argc-1])-1] == '?')

10
ppp.c
View file

@ -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 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)) 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 // Add on the tun header
@ -2284,6 +2284,14 @@ void processipv6in(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
return; 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. // Check if it's a Router Solicition message.
if (*(p + 6) == 58 && *(p + 7) == 255 && *(p + 24) == 0xFF && *(p + 25) == 2 && if (*(p + 6) == 58 && *(p + 7) == 255 && *(p + 24) == 0xFF && *(p + 25) == 2 &&
*(uint32_t *)(p + 26) == 0 && *(uint32_t *)(p + 30) == 0 && *(uint32_t *)(p + 26) == 0 && *(uint32_t *)(p + 30) == 0 &&