blob: 5ca9e0b9653e70a8a2bf18b7e50a3a6658cc68e4 [file] [log] [blame]
James Kuszmaul871d0712021-01-17 11:30:43 -08001/**
2 * @file option.c PCP options
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 void destructor(void *arg)
17{
18 struct pcp_option *opt = arg;
19
20 list_unlink(&opt->le);
21
22 switch (opt->code) {
23
24 case PCP_OPTION_DESCRIPTION:
25 mem_deref(opt->u.description);
26 break;
27
28 default:
29 break;
30 }
31}
32
33
34int pcp_option_encode(struct mbuf *mb, enum pcp_option_code code,
35 const void *v)
36{
37 const struct sa *sa = v;
38 const struct pcp_option_filter *filt = v;
39 size_t start, len;
40 int err = 0;
41
42 if (!mb)
43 return EINVAL;
44
45 mb->pos += 4;
46 start = mb->pos;
47
48 switch (code) {
49
50 case PCP_OPTION_THIRD_PARTY:
51 if (!sa)
52 return EINVAL;
53 err |= pcp_ipaddr_encode(mb, sa);
54 break;
55
56 case PCP_OPTION_PREFER_FAILURE:
57 /* no payload */
58 break;
59
60 case PCP_OPTION_FILTER:
61 if (!filt)
62 return EINVAL;
63 err |= mbuf_write_u8(mb, 0x00);
64 err |= mbuf_write_u8(mb, filt->prefix_length);
65 err |= mbuf_write_u16(mb, htons(sa_port(&filt->remote_peer)));
66 err |= pcp_ipaddr_encode(mb, &filt->remote_peer);
67 break;
68
69 case PCP_OPTION_DESCRIPTION:
70 if (!v)
71 return EINVAL;
72 err |= mbuf_write_str(mb, v);
73 break;
74
75 default:
76 (void)re_fprintf(stderr,
77 "pcp: unsupported option %d\n", code);
78 return EINVAL;
79 }
80
81 /* header */
82 len = mb->pos - start;
83
84 mb->pos = start - 4;
85 err |= mbuf_write_u8(mb, code);
86 err |= mbuf_write_u8(mb, 0x00);
87 err |= mbuf_write_u16(mb, htons(len));
88 mb->pos += len;
89
90 /* padding */
91 while ((mb->pos - start) & 0x03)
92 err |= mbuf_write_u8(mb, 0x00);
93
94 return err;
95}
96
97
98int pcp_option_decode(struct pcp_option **optp, struct mbuf *mb)
99{
100 struct pcp_option *opt;
101 size_t start, len;
102 uint16_t port;
103 int err = 0;
104
105 if (!optp || !mb)
106 return EINVAL;
107
108 if (mbuf_get_left(mb) < 4)
109 return EBADMSG;
110
111 opt = mem_zalloc(sizeof(*opt), destructor);
112 if (!opt)
113 return ENOMEM;
114
115 opt->code = mbuf_read_u8(mb);
116 (void)mbuf_read_u8(mb);
117 len = ntohs(mbuf_read_u16(mb));
118
119 if (mbuf_get_left(mb) < len)
120 goto badmsg;
121
122 start = mb->pos;
123
124 switch (opt->code) {
125
126 case PCP_OPTION_THIRD_PARTY:
127 if (len < 16)
128 goto badmsg;
129 err = pcp_ipaddr_decode(mb, &opt->u.third_party);
130 break;
131
132 case PCP_OPTION_PREFER_FAILURE:
133 /* no payload */
134 break;
135
136 case PCP_OPTION_FILTER:
137 if (len < 20)
138 goto badmsg;
139 (void)mbuf_read_u8(mb);
140 opt->u.filter.prefix_length = mbuf_read_u8(mb);
141 port = ntohs(mbuf_read_u16(mb));
142 err = pcp_ipaddr_decode(mb, &opt->u.filter.remote_peer);
143 sa_set_port(&opt->u.filter.remote_peer, port);
144 break;
145
146 case PCP_OPTION_DESCRIPTION:
147 err = mbuf_strdup(mb, &opt->u.description, len);
148 break;
149
150 default:
151 mb->pos += len;
152
153 (void)re_printf("pcp: ignore option code %d (len=%zu)\n",
154 opt->code, len);
155 break;
156 }
157
158 if (err)
159 goto error;
160
161 /* padding */
162 while (((mb->pos - start) & 0x03) && mbuf_get_left(mb))
163 ++mb->pos;
164
165 *optp = opt;
166
167 return 0;
168
169 badmsg:
170 err = EBADMSG;
171 error:
172 mem_deref(opt);
173
174 return err;
175}
176
177
178static const char *pcp_option_name(enum pcp_option_code code)
179{
180 switch (code) {
181
182 case PCP_OPTION_THIRD_PARTY: return "THIRD_PARTY";
183 case PCP_OPTION_PREFER_FAILURE: return "PREFER_FAILURE";
184 case PCP_OPTION_FILTER: return "FILTER";
185 case PCP_OPTION_DESCRIPTION: return "DESCRIPTION";
186 default: return "?";
187 }
188}
189
190
191int pcp_option_print(struct re_printf *pf, const struct pcp_option *opt)
192{
193 int err;
194
195 if (!opt)
196 return 0;
197
198 err = re_hprintf(pf, " %-25s", pcp_option_name(opt->code));
199
200 switch (opt->code) {
201
202 case PCP_OPTION_THIRD_PARTY:
203 err |= re_hprintf(pf, "address=%j",
204 &opt->u.third_party);
205 break;
206
207 case PCP_OPTION_PREFER_FAILURE:
208 break;
209
210 case PCP_OPTION_FILTER:
211 err |= re_hprintf(pf, "prefix_length=%u, remote_peer=%J",
212 opt->u.filter.prefix_length,
213 &opt->u.filter.remote_peer);
214 break;
215
216 case PCP_OPTION_DESCRIPTION:
217 err |= re_hprintf(pf, "'%s'", opt->u.description);
218 break;
219
220 default:
221 err |= re_hprintf(pf, "???");
222 break;
223 }
224
225 err |= re_hprintf(pf, "\n");
226
227 return err;
228}