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