blob: 327f25442e0371ed54d5488df305c307c47edb78 [file] [log] [blame]
James Kuszmaul871d0712021-01-17 11:30:43 -08001/**
2 * @file pcp/msg.c PCP messages
3 *
4 * Copyright (C) 2010 - 2016 Creytiv.com
5 */
6#include <re_types.h>
7#include <re_fmt.h>
8#include <re_mem.h>
9#include <re_mbuf.h>
10#include <re_list.h>
11#include <re_sa.h>
12#include <re_pcp.h>
13#include "pcp.h"
14
15
16static int pcp_map_decode(struct pcp_map *map, struct mbuf *mb)
17{
18 uint16_t port;
19 int err;
20
21 if (!map || !mb)
22 return EINVAL;
23
24 if (mbuf_get_left(mb) < PCP_MAP_SZ)
25 return EBADMSG;
26
27 (void)mbuf_read_mem(mb, map->nonce, sizeof(map->nonce));
28 map->proto = mbuf_read_u8(mb); mbuf_advance(mb, 3);
29 map->int_port = ntohs(mbuf_read_u16(mb));
30
31 port = ntohs(mbuf_read_u16(mb));
32 err = pcp_ipaddr_decode(mb, &map->ext_addr);
33 sa_set_port(&map->ext_addr, port);
34
35 return err;
36}
37
38
39static int pcp_peer_decode(struct pcp_peer *peer, struct mbuf *mb)
40{
41 uint16_t port;
42 int err = 0;
43
44 if (!peer || !mb)
45 return EINVAL;
46
47 if (mbuf_get_left(mb) < PCP_PEER_SZ)
48 return EBADMSG;
49
50 /* note: the MAP and PEER opcodes are quite similar */
51 err = pcp_map_decode(&peer->map, mb);
52 if (err)
53 return err;
54
55 port = ntohs(mbuf_read_u16(mb)); mbuf_advance(mb, 2);
56 err |= pcp_ipaddr_decode(mb, &peer->remote_addr);
57 sa_set_port(&peer->remote_addr, port);
58
59 return err;
60}
61
62
63static void destructor(void *arg)
64{
65 struct pcp_msg *msg = arg;
66
67 list_flush(&msg->optionl);
68}
69
70
71static int pcp_header_encode_request(struct mbuf *mb, enum pcp_opcode opcode,
72 uint32_t req_lifetime, const struct sa *int_addr)
73{
74 int err = 0;
75
76 if (!mb || !int_addr)
77 return EINVAL;
78
79 err |= mbuf_write_u8(mb, PCP_VERSION);
80 err |= mbuf_write_u8(mb, opcode);
81 err |= mbuf_write_u16(mb, 0x0000);
82 err |= mbuf_write_u32(mb, htonl(req_lifetime));
83 err |= pcp_ipaddr_encode(mb, int_addr);
84
85 return err;
86}
87
88
89static int pcp_header_decode(struct pcp_hdr *hdr, struct mbuf *mb)
90{
91 uint8_t b;
92
93 if (!hdr || !mb)
94 return EINVAL;
95
96 if (mbuf_get_left(mb) < PCP_HDR_SZ)
97 return EBADMSG;
98
99 hdr->version = mbuf_read_u8(mb);
100
101 if (hdr->version != PCP_VERSION) {
102 (void)re_fprintf(stderr, "pcp: unknown version %u\n",
103 hdr->version);
104 return EPROTO;
105 }
106
107 b = mbuf_read_u8(mb);
108 hdr->resp = b>>7;
109 hdr->opcode = b & 0x7f;
110
111 (void)mbuf_read_u8(mb);
112 b = mbuf_read_u8(mb);
113
114 if (hdr->resp)
115 hdr->result = b;
116
117 hdr->lifetime = ntohl(mbuf_read_u32(mb));
118
119 if (hdr->resp) {
120 hdr->epoch = ntohl(mbuf_read_u32(mb));
121 mbuf_advance(mb, 12);
122 }
123 else { /* Request */
124 (void)pcp_ipaddr_decode(mb, &hdr->cli_addr);
125 }
126
127 return 0;
128}
129
130
131int pcp_msg_req_vencode(struct mbuf *mb, enum pcp_opcode opcode,
132 uint32_t lifetime, const struct sa *cli_addr,
133 const void *payload, uint32_t optionc, va_list ap)
134{
135 uint32_t i;
136 int err;
137
138 if (!mb || !cli_addr)
139 return EINVAL;
140
141 err = pcp_header_encode_request(mb, opcode, lifetime, cli_addr);
142 if (err)
143 return err;
144
145 if (payload) {
146 err = pcp_payload_encode(mb, opcode, payload);
147 if (err)
148 return err;
149 }
150
151 /* encode options */
152 for (i=0; i<optionc; i++) {
153
154 enum pcp_option_code code = va_arg(ap, int);
155 const void *v = va_arg(ap, const void *);
156
157 if (!v)
158 continue;
159
160 err |= pcp_option_encode(mb, code, v);
161 }
162
163 return err;
164}
165
166
167int pcp_msg_req_encode(struct mbuf *mb, enum pcp_opcode opcode,
168 uint32_t lifetime, const struct sa *cli_addr,
169 const void *payload, uint32_t optionc, ...)
170{
171 va_list ap;
172 int err;
173
174 va_start(ap, optionc);
175 err = pcp_msg_req_vencode(mb, opcode, lifetime, cli_addr,
176 payload, optionc, ap);
177 va_end(ap);
178
179 return err;
180}
181
182
183int pcp_msg_decode(struct pcp_msg **msgp, struct mbuf *mb)
184{
185 struct pcp_msg *msg;
186 size_t len, pos;
187 int err;
188
189 if (!msgp || !mb)
190 return EINVAL;
191
192 len = mbuf_get_left(mb);
193 if (len < PCP_MIN_PACKET || len > PCP_MAX_PACKET || len&3)
194 return EBADMSG;
195
196 msg = mem_zalloc(sizeof(*msg), destructor);
197 if (!msg)
198 return ENOMEM;
199
200 pos = mb->pos;
201 err = pcp_header_decode(&msg->hdr, mb);
202 if (err)
203 goto out;
204
205 switch (msg->hdr.opcode) {
206
207 case PCP_MAP:
208 err = pcp_map_decode(&msg->pld.map, mb);
209 break;
210
211 case PCP_PEER:
212 err = pcp_peer_decode(&msg->pld.peer, mb);
213 break;
214
215 default:
216 break;
217 }
218 if (err)
219 goto out;
220
221 /* Decode PCP Options */
222 while (mbuf_get_left(mb) >= 4) {
223
224 struct pcp_option *opt;
225
226 err = pcp_option_decode(&opt, mb);
227 if (err)
228 goto out;
229
230 list_append(&msg->optionl, &opt->le, opt);
231 }
232
233 out:
234 if (err) {
235 mb->pos = pos;
236 mem_deref(msg);
237 }
238 else
239 *msgp = msg;
240
241 return err;
242}
243
244
245struct pcp_option *pcp_msg_option(const struct pcp_msg *msg,
246 enum pcp_option_code code)
247{
248 struct le *le = msg ? list_head(&msg->optionl) : NULL;
249
250 while (le) {
251 struct pcp_option *opt = le->data;
252
253 le = le->next;
254
255 if (opt->code == code)
256 return opt;
257 }
258
259 return NULL;
260}
261
262
263struct pcp_option *pcp_msg_option_apply(const struct pcp_msg *msg,
264 pcp_option_h *h, void *arg)
265{
266 struct le *le = msg ? list_head(&msg->optionl) : NULL;
267
268 while (le) {
269 struct pcp_option *opt = le->data;
270
271 le = le->next;
272
273 if (h && h(opt, arg))
274 return opt;
275 }
276
277 return NULL;
278}
279
280
281static bool option_print(const struct pcp_option *opt, void *arg)
282{
283 return 0 != pcp_option_print(arg, opt);
284}
285
286
287int pcp_msg_printhdr(struct re_printf *pf, const struct pcp_msg *msg)
288{
289 int err;
290
291 if (!msg)
292 return 0;
293
294 err = re_hprintf(pf, "%s %s %usec",
295 msg->hdr.resp ? "Response" : "Request",
296 pcp_opcode_name(msg->hdr.opcode),
297 msg->hdr.lifetime);
298
299 if (msg->hdr.resp) {
300 err |= re_hprintf(pf, " result=%s, epoch_time=%u sec",
301 pcp_result_name(msg->hdr.result),
302 msg->hdr.epoch);
303 }
304 else {
305 err |= re_hprintf(pf, " client_addr=%j", &msg->hdr.cli_addr);
306 }
307
308 return err;
309}
310
311
312static int pcp_map_print(struct re_printf *pf, const struct pcp_map *map)
313{
314 if (!map)
315 return 0;
316
317 return re_hprintf(pf,
318 " nonce = %w\n protocol = %s\n"
319 " int_port = %u\n ext_addr = %J\n",
320 map->nonce, sizeof(map->nonce),
321 pcp_proto_name(map->proto),
322 map->int_port,
323 &map->ext_addr);
324}
325
326
327int pcp_msg_print(struct re_printf *pf, const struct pcp_msg *msg)
328{
329 int err;
330
331 if (!msg)
332 return 0;
333
334 err = pcp_msg_printhdr(pf, msg);
335 err |= re_hprintf(pf, "\n");
336
337 switch (msg->hdr.opcode) {
338
339 case PCP_MAP:
340 err |= pcp_map_print(pf, &msg->pld.map);
341 break;
342
343 case PCP_PEER:
344 err |= pcp_map_print(pf, &msg->pld.peer.map);
345 err |= re_hprintf(pf, " remote_peer = %J\n",
346 &msg->pld.peer.remote_addr);
347 break;
348 }
349
350 if (err)
351 return err;
352
353 if (pcp_msg_option_apply(msg, option_print, pf))
354 return ENOMEM;
355
356 return 0;
357}
358
359
360/**
361 * Get the payload from a PCP message
362 *
363 * @param msg PCP message
364 *
365 * @return either "struct pcp_map" or "struct pcp_peer"
366 */
367const void *pcp_msg_payload(const struct pcp_msg *msg)
368{
369 if (!msg)
370 return NULL;
371
372 switch (msg->hdr.opcode) {
373
374 case PCP_MAP: return &msg->pld.map;
375 case PCP_PEER: return &msg->pld.peer;
376 default: return NULL;
377 }
378}