mtucalc.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. /*
  2. * OpenConnect (SSL + DTLS) VPN client
  3. *
  4. * Copyright © 2020 Daniel Lenski
  5. *
  6. * Authors: Daniel Lenski <dlenski@gmail.com>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public License
  10. * version 2.1, as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. */
  17. #include <config.h>
  18. #include "openconnect-internal.h"
  19. #if defined(__linux__)
  20. /* For TCP_INFO */
  21. # include <linux/tcp.h>
  22. #endif
  23. #define ESP_HEADER_SIZE (4 /* SPI */ + 4 /* sequence number */)
  24. #define ESP_FOOTER_SIZE (1 /* pad length */ + 1 /* next header */)
  25. #define UDP_HEADER_SIZE 8
  26. #define TCP_HEADER_SIZE 20 /* with no options */
  27. #define IPV4_HEADER_SIZE 20
  28. #define IPV6_HEADER_SIZE 40
  29. /* Calculate MTU of a tunnel.
  30. *
  31. * is_udp: 1 if outer packet is UDP, 0 if TCP
  32. * unpadded_overhead: overhead that does not get padded (headers or footers)
  33. * padded_overhead: overhead that gets before padding the payload (typically footers)
  34. * block_size: block size that payload will be padded to AFTER adding padded overhead
  35. */
  36. int calculate_mtu(struct openconnect_info *vpninfo, int is_udp,
  37. int unpadded_overhead, int padded_overhead, int block_size)
  38. {
  39. int mtu = vpninfo->reqmtu, base_mtu = vpninfo->basemtu;
  40. int mss = 0;
  41. /* Try to figure out base_mtu (from TCP PMTU), and save TCP MSS for later */
  42. #if defined(__linux__) && defined(TCP_INFO)
  43. if (!mtu) {
  44. struct tcp_info ti;
  45. socklen_t ti_size = sizeof(ti);
  46. if (!getsockopt(vpninfo->ssl_fd, IPPROTO_TCP, TCP_INFO,
  47. &ti, &ti_size)) {
  48. vpn_progress(vpninfo, PRG_DEBUG,
  49. _("TCP_INFO rcv mss %d, snd mss %d, adv mss %d, pmtu %d\n"),
  50. ti.tcpi_rcv_mss, ti.tcpi_snd_mss, ti.tcpi_advmss, ti.tcpi_pmtu);
  51. /* If base_mtu unknown, use TCP PMTU */
  52. if (!base_mtu) {
  53. base_mtu = ti.tcpi_pmtu;
  54. }
  55. /* Use largest MSS */
  56. mss = MAX(ti.tcpi_rcv_mss, ti.tcpi_snd_mss);
  57. mss = MAX(mss, ti.tcpi_advmss);
  58. }
  59. }
  60. #endif
  61. #ifdef TCP_MAXSEG
  62. if (!mtu && !mss) {
  63. socklen_t mss_size = sizeof(mss);
  64. if (!getsockopt(vpninfo->ssl_fd, IPPROTO_TCP, TCP_MAXSEG,
  65. &mss, &mss_size)) {
  66. vpn_progress(vpninfo, PRG_DEBUG, _("TCP_MAXSEG %d\n"), mss);
  67. }
  68. }
  69. #endif
  70. /* Default base_mtu needs to be big enough for IPv6 (1280 minimum) */
  71. if (!base_mtu) {
  72. /* Default */
  73. base_mtu = 1406;
  74. }
  75. if (base_mtu < 1280)
  76. base_mtu = 1280;
  77. vpn_progress(vpninfo, PRG_TRACE, _("Using base_mtu of %d\n"), base_mtu);
  78. /* base_mtu is now (we hope) the PMTU between our external network interface
  79. * and the VPN gateway */
  80. if (!mtu) {
  81. if (!is_udp && mss)
  82. /* MSS already has IP, TCP header size removed */
  83. mtu = mss;
  84. else {
  85. /* remove TCP/UDP, IP headers from base (wire) MTU */
  86. mtu = base_mtu - (is_udp ? UDP_HEADER_SIZE : TCP_HEADER_SIZE);
  87. mtu -= (vpninfo->peer_addr->sa_family == AF_INET6) ? IPV6_HEADER_SIZE : IPV4_HEADER_SIZE;
  88. }
  89. }
  90. vpn_progress(vpninfo, PRG_TRACE, _("After removing %s/IPv%d headers, MTU of %d\n"),
  91. (is_udp ? "UDP" : "TCP"), vpninfo->peer_addr->sa_family == AF_INET6 ? 6 : 4, mtu);
  92. /* MTU is now (we hope) the number of payload bytes that can fit in a UDP or
  93. * TCP packet exchanged with the VPN gateway. */
  94. mtu -= unpadded_overhead; /* remove protocol-specific overhead that isn't affected by padding */
  95. mtu -= mtu % block_size; /* round down to a multiple of blocksize */
  96. mtu -= padded_overhead; /* remove protocol-specific overhead that contributes to payload padding */
  97. vpn_progress(vpninfo, PRG_TRACE, _("After removing protocol specific overhead (%d unpadded, %d padded, %d blocksize), MTU of %d\n"),
  98. unpadded_overhead, padded_overhead, block_size, mtu);
  99. return mtu;
  100. }