• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ngtcp2
3  *
4  * Copyright (c) 2022 ngtcp2 contributors
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #include "ngtcp2_pmtud.h"
26 
27 #include <assert.h>
28 
29 #include "ngtcp2_mem.h"
30 #include "ngtcp2_macro.h"
31 
32 /* NGTCP2_PMTUD_PROBE_NUM_MAX is the maximum number of packets sent
33    for each probe. */
34 #define NGTCP2_PMTUD_PROBE_NUM_MAX 3
35 
36 static size_t mtu_probes[] = {
37     1454 - 48, /* The well known MTU used by a domestic optic fiber
38                   service in Japan. */
39     1390 - 48, /* Typical Tunneled MTU */
40     1280 - 48, /* IPv6 minimum MTU */
41     1492 - 48, /* PPPoE */
42 };
43 
44 static size_t mtu_probeslen = sizeof(mtu_probes) / sizeof(mtu_probes[0]);
45 
ngtcp2_pmtud_new(ngtcp2_pmtud ** ppmtud,size_t max_udp_payload_size,size_t hard_max_udp_payload_size,int64_t tx_pkt_num,const ngtcp2_mem * mem)46 int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size,
47                      size_t hard_max_udp_payload_size, int64_t tx_pkt_num,
48                      const ngtcp2_mem *mem) {
49   ngtcp2_pmtud *pmtud = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pmtud));
50 
51   if (pmtud == NULL) {
52     return NGTCP2_ERR_NOMEM;
53   }
54 
55   pmtud->mem = mem;
56   pmtud->mtu_idx = 0;
57   pmtud->num_pkts_sent = 0;
58   pmtud->expiry = UINT64_MAX;
59   pmtud->tx_pkt_num = tx_pkt_num;
60   pmtud->max_udp_payload_size = max_udp_payload_size;
61   pmtud->hard_max_udp_payload_size = hard_max_udp_payload_size;
62   pmtud->min_fail_udp_payload_size = SIZE_MAX;
63 
64   for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) {
65     if (mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) {
66       continue;
67     }
68     if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) {
69       break;
70     }
71   }
72 
73   *ppmtud = pmtud;
74 
75   return 0;
76 }
77 
ngtcp2_pmtud_del(ngtcp2_pmtud * pmtud)78 void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud) {
79   if (!pmtud) {
80     return;
81   }
82 
83   ngtcp2_mem_free(pmtud->mem, pmtud);
84 }
85 
ngtcp2_pmtud_probelen(ngtcp2_pmtud * pmtud)86 size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud) {
87   assert(pmtud->mtu_idx < mtu_probeslen);
88 
89   return mtu_probes[pmtud->mtu_idx];
90 }
91 
ngtcp2_pmtud_probe_sent(ngtcp2_pmtud * pmtud,ngtcp2_duration pto,ngtcp2_tstamp ts)92 void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto,
93                              ngtcp2_tstamp ts) {
94   ngtcp2_tstamp timeout;
95 
96   if (++pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) {
97     timeout = pto;
98   } else {
99     timeout = 3 * pto;
100   }
101 
102   pmtud->expiry = ts + timeout;
103 }
104 
ngtcp2_pmtud_require_probe(ngtcp2_pmtud * pmtud)105 int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud) {
106   return pmtud->expiry == UINT64_MAX;
107 }
108 
pmtud_next_probe(ngtcp2_pmtud * pmtud)109 static void pmtud_next_probe(ngtcp2_pmtud *pmtud) {
110   assert(pmtud->mtu_idx < mtu_probeslen);
111 
112   ++pmtud->mtu_idx;
113   pmtud->num_pkts_sent = 0;
114   pmtud->expiry = UINT64_MAX;
115 
116   for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) {
117     if (mtu_probes[pmtud->mtu_idx] <= pmtud->max_udp_payload_size ||
118         mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) {
119       continue;
120     }
121 
122     if (mtu_probes[pmtud->mtu_idx] < pmtud->min_fail_udp_payload_size) {
123       break;
124     }
125   }
126 }
127 
ngtcp2_pmtud_probe_success(ngtcp2_pmtud * pmtud,size_t payloadlen)128 void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen) {
129   pmtud->max_udp_payload_size =
130       ngtcp2_max(pmtud->max_udp_payload_size, payloadlen);
131 
132   assert(pmtud->mtu_idx < mtu_probeslen);
133 
134   if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) {
135     return;
136   }
137 
138   pmtud_next_probe(pmtud);
139 }
140 
ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud * pmtud,ngtcp2_tstamp ts)141 void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts) {
142   if (ts < pmtud->expiry) {
143     return;
144   }
145 
146   pmtud->expiry = UINT64_MAX;
147 
148   if (pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) {
149     return;
150   }
151 
152   pmtud->min_fail_udp_payload_size =
153       ngtcp2_min(pmtud->min_fail_udp_payload_size, mtu_probes[pmtud->mtu_idx]);
154 
155   pmtud_next_probe(pmtud);
156 }
157 
ngtcp2_pmtud_finished(ngtcp2_pmtud * pmtud)158 int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud) {
159   return pmtud->mtu_idx >= mtu_probeslen;
160 }
161