blob: 60d58b4cf841aa0b426a16f179fd785495e89999 [file] [log] [blame]
James Kuszmaul871d0712021-01-17 11:30:43 -08001/**
2 * @file pcp/request.c PCP request
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <string.h>
7#include <re_types.h>
8#include <re_fmt.h>
9#include <re_mem.h>
10#include <re_mbuf.h>
11#include <re_list.h>
12#include <re_sys.h>
13#include <re_sa.h>
14#include <re_tmr.h>
15#include <re_udp.h>
16#include <re_pcp.h>
17#include "pcp.h"
18
19
20/*
21 * Defines a PCP client request
22 *
23 * the application must keep a reference to this object and the
24 * object must be deleted by the application. the response handler
25 * might be called multiple times.
26 */
27struct pcp_request {
28 struct pcp_conf conf;
29 struct sa srv;
30 struct udp_sock *us;
31 struct mbuf *mb;
32 struct tmr tmr;
33 struct tmr tmr_dur;
34 struct tmr tmr_refresh;
35 enum pcp_opcode opcode;
36 union pcp_payload payload;
37 uint32_t lifetime;
38 bool granted;
39 unsigned txc;
40 double RT;
41 pcp_resp_h *resph;
42 void *arg;
43};
44
45
46/*
47 * RT: Retransmission timeout
48 * IRT: Initial retransmission time, SHOULD be 3 seconds
49 * MRC: Maximum retransmission count, SHOULD be 0 (no maximum)
50 * MRT: Maximum retransmission time, SHOULD be 1024 seconds
51 * MRD: Maximum retransmission duration, SHOULD be 0 (no maximum)
52 * RAND: Randomization factor
53 */
54static const struct pcp_conf default_conf = {
55 3,
56 0,
57 1024,
58 0
59};
60
61
62static int start_sending(struct pcp_request *req);
63
64
65/* random number between -0.1 and +0.1 */
66static inline double RAND(void)
67{
68 return (1.0 * rand_u16() / 32768 - 1.0) / 10.0;
69}
70
71static double RT_init(const struct pcp_conf *conf)
72{
73 return (1.0 + RAND()) * conf->irt;
74}
75
76static double RT_next(const struct pcp_conf *conf, double RTprev)
77{
78 return (1.0 + RAND()) * min (2 * RTprev, conf->mrt);
79}
80
81
82static void destructor(void *arg)
83{
84 struct pcp_request *req = arg;
85
86 /* Destroy the mapping if it was granted */
87 if (req->granted && req->lifetime && req->mb) {
88
89 /* set the lifetime to zero */
90 req->mb->pos = 4;
91 mbuf_write_u32(req->mb, 0);
92
93 req->mb->pos = 0;
94 (void)udp_send(req->us, &req->srv, req->mb);
95 }
96
97 tmr_cancel(&req->tmr);
98 tmr_cancel(&req->tmr_dur);
99 tmr_cancel(&req->tmr_refresh);
100 mem_deref(req->us);
101 mem_deref(req->mb);
102}
103
104
105static void completed(struct pcp_request *req, int err, struct pcp_msg *msg)
106{
107 pcp_resp_h *resph = req->resph;
108 void *arg = req->arg;
109
110 tmr_cancel(&req->tmr);
111 tmr_cancel(&req->tmr_dur);
112
113 /* if the request failed, we only called the
114 response handler once and never again */
115 if (err || msg->hdr.result != PCP_SUCCESS ) {
116 req->resph = NULL;
117 }
118
119 if (resph)
120 resph(err, msg, arg);
121}
122
123
124static void refresh_timeout(void *arg)
125{
126 struct pcp_request *req = arg;
127
128 /* todo: update request with new EXT-ADDR from server */
129 (void)start_sending(req);
130}
131
132
133static void timeout(void *arg)
134{
135 struct pcp_request *req = arg;
136 int err;
137
138 req->txc++;
139
140 if (req->conf.mrc > 0 && req->txc > req->conf.mrc) {
141 completed(req, ETIMEDOUT, NULL);
142 return;
143 }
144
145 req->mb->pos = 0;
146 err = udp_send(req->us, &req->srv, req->mb);
147 if (err) {
148 completed(req, err, NULL);
149 return;
150 }
151
152 req->RT = RT_next(&req->conf, req->RT);
153 tmr_start(&req->tmr, req->RT * 1000, timeout, req);
154}
155
156
157static void timeout_duration(void *arg)
158{
159 struct pcp_request *req = arg;
160
161 completed(req, ETIMEDOUT, NULL);
162}
163
164
165static void udp_recv(const struct sa *src, struct mbuf *mb, void *arg)
166{
167 struct pcp_request *req = arg;
168 struct pcp_msg *msg;
169 int err;
170
171 if (!sa_cmp(src, &req->srv, SA_ALL))
172 return;
173
174 err = pcp_msg_decode(&msg, mb);
175 if (err)
176 return;
177
178 if (!msg->hdr.resp) {
179 (void)re_fprintf(stderr, "pcp: ignoring PCP request\n");
180 goto out;
181 }
182
183 if (msg->hdr.opcode != req->opcode)
184 goto out;
185
186 /* compare opcode-specific data */
187
188 switch (msg->hdr.opcode) {
189
190 case PCP_MAP:
191 case PCP_PEER:
192 if (0 != memcmp(msg->pld.map.nonce, req->payload.map.nonce,
193 PCP_NONCE_SZ)) {
194 (void)re_fprintf(stderr, "ignoring unknown nonce\n");
195 goto out;
196 }
197 req->payload.map.ext_addr = msg->pld.map.ext_addr;
198 break;
199
200 default:
201 break;
202 }
203
204 req->lifetime = msg->hdr.lifetime;
205 req->granted = (msg->hdr.result == PCP_SUCCESS);
206
207 /* todo:
208 *
209 * Once a PCP client has successfully received a response from a PCP
210 * server on that interface, it resets RT to a value randomly selected
211 * in the range 1/2 to 5/8 of the mapping lifetime, as described in
212 * Section 11.2.1, "Renewing a Mapping", and sends subsequent PCP
213 * requests for that mapping to that same server.
214 */
215 if (req->granted && req->lifetime) {
216
217 uint32_t v = req->lifetime * 3/4;
218
219 tmr_start(&req->tmr_refresh, v * 1000, refresh_timeout, req);
220 }
221
222 completed(req, 0, msg);
223
224 out:
225 mem_deref(msg);
226}
227
228
229static int start_sending(struct pcp_request *req)
230{
231 int err;
232
233 req->txc = 1;
234
235 req->mb->pos = 0;
236 err = udp_send(req->us, &req->srv, req->mb);
237 if (err)
238 return err;
239
240 req->RT = RT_init(&req->conf);
241 tmr_start(&req->tmr, req->RT * 1000, timeout, req);
242
243 if (req->conf.mrd) {
244 tmr_start(&req->tmr_dur, req->conf.mrd * 1000,
245 timeout_duration, req);
246 }
247
248 return err;
249}
250
251
252static int pcp_vrequest(struct pcp_request **reqp, const struct pcp_conf *conf,
253 const struct sa *srv, enum pcp_opcode opcode,
254 uint32_t lifetime, const void *payload,
255 pcp_resp_h *resph, void *arg,
256 uint32_t optionc, va_list ap)
257{
258 const union pcp_payload *up = payload;
259 struct pcp_request *req;
260 struct sa laddr;
261 int err;
262
263 if (!reqp || !srv)
264 return EINVAL;
265
266 sa_init(&laddr, sa_af(srv));
267
268 req = mem_zalloc(sizeof(*req), destructor);
269 if (!req)
270 return ENOMEM;
271
272 req->conf = conf ? *conf : default_conf;
273 req->opcode = opcode;
274 req->srv = *srv;
275 req->resph = resph;
276 req->arg = arg;
277
278 req->lifetime = lifetime;
279
280 if (up)
281 req->payload = *up;
282
283 err = udp_listen(&req->us, &laddr, udp_recv, req);
284 if (err)
285 goto out;
286
287 /*
288 * see RFC 6887 section 16.4
289 */
290 err = udp_connect(req->us, srv);
291 if (err)
292 goto out;
293 err = udp_local_get(req->us, &laddr);
294 if (err)
295 goto out;
296
297 req->mb = mbuf_alloc(128);
298 if (!req->mb) {
299 err = ENOMEM;
300 goto out;
301 }
302
303 err = pcp_msg_req_vencode(req->mb, opcode, lifetime,
304 &laddr, up, optionc, ap);
305 if (err)
306 goto out;
307
308 err = start_sending(req);
309
310 out:
311 if (err)
312 mem_deref(req);
313 else
314 *reqp = req;
315
316 return err;
317}
318
319
320int pcp_request(struct pcp_request **reqp, const struct pcp_conf *conf,
321 const struct sa *srv, enum pcp_opcode opcode,
322 uint32_t lifetime, const void *payload,
323 pcp_resp_h *resph, void *arg, uint32_t optionc, ...)
324{
325 va_list ap;
326 int err;
327
328 va_start(ap, optionc);
329 err = pcp_vrequest(reqp, conf, srv, opcode, lifetime, payload,
330 resph, arg, optionc, ap);
331 va_end(ap);
332
333 return err;
334}
335
336
337void pcp_force_refresh(struct pcp_request *req)
338{
339 if (!req)
340 return;
341
342 tmr_cancel(&req->tmr);
343 tmr_cancel(&req->tmr_dur);
344
345 tmr_start(&req->tmr_refresh, rand_u16() % 2000, refresh_timeout, req);
346}