]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/test-dhcp-option.c
tree-wide: use proper unicode © instead of (C) where we can
[thirdparty/systemd.git] / src / libsystemd-network / test-dhcp-option.c
1 #include <errno.h>
2 #include <stdbool.h>
3 #include <stdio.h>
4 #include <string.h>
5
6 #include "alloc-util.h"
7 #include "dhcp-internal.h"
8 #include "dhcp-protocol.h"
9 #include "macro.h"
10 #include "util.h"
11
12 struct option_desc {
13 uint8_t sname[64];
14 int snamelen;
15 uint8_t file[128];
16 int filelen;
17 uint8_t options[128];
18 int len;
19 bool success;
20 int filepos;
21 int snamepos;
22 int pos;
23 };
24
25 static bool verbose = false;
26
27 static struct option_desc option_tests[] = {
28 { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
29 { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
30 SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
31 { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
32 { {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
33 0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
34 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0,
35 0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
36 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
37 40, true, },
38 { {}, 0, {}, 0, { SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
39 42, 3, 0, 0, 0 }, 8, true, },
40 { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
41
42 { {}, 0,
43 { 222, 3, 1, 2, 3, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
44 { SD_DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
45
46 { { 1, 4, 1, 2, 3, 4, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
47 { 222, 3, 1, 2, 3 }, 5,
48 { SD_DHCP_OPTION_OVERLOAD, 1,
49 DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
50 };
51
52 static const char *dhcp_type(int type) {
53 switch(type) {
54 case DHCP_DISCOVER:
55 return "DHCPDISCOVER";
56 case DHCP_OFFER:
57 return "DHCPOFFER";
58 case DHCP_REQUEST:
59 return "DHCPREQUEST";
60 case DHCP_DECLINE:
61 return "DHCPDECLINE";
62 case DHCP_ACK:
63 return "DHCPACK";
64 case DHCP_NAK:
65 return "DHCPNAK";
66 case DHCP_RELEASE:
67 return "DHCPRELEASE";
68 default:
69 return "unknown";
70 }
71 }
72
73 static void test_invalid_buffer_length(void) {
74 DHCPMessage message;
75
76 assert_se(dhcp_option_parse(&message, 0, NULL, NULL, NULL) == -EINVAL);
77 assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL, NULL) == -EINVAL);
78 }
79
80 static void test_message_init(void) {
81 _cleanup_free_ DHCPMessage *message = NULL;
82 size_t optlen = 4, optoffset;
83 size_t len = sizeof(DHCPMessage) + optlen;
84 uint8_t *magic;
85
86 message = malloc0(len);
87
88 assert_se(dhcp_message_init(message, BOOTREQUEST, 0x12345678,
89 DHCP_DISCOVER, ARPHRD_ETHER, optlen, &optoffset) >= 0);
90
91 assert_se(message->xid == htobe32(0x12345678));
92 assert_se(message->op == BOOTREQUEST);
93
94 magic = (uint8_t*)&message->magic;
95
96 assert_se(magic[0] == 99);
97 assert_se(magic[1] == 130);
98 assert_se(magic[2] == 83);
99 assert_se(magic[3] == 99);
100
101 assert_se(dhcp_option_parse(message, len, NULL, NULL, NULL) >= 0);
102 }
103
104 static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
105 uint8_t *file, uint8_t filelen,
106 uint8_t *sname, uint8_t snamelen) {
107 DHCPMessage *message;
108 size_t len = sizeof(DHCPMessage) + optlen;
109
110 message = malloc0(len);
111 assert_se(message);
112
113 memcpy_safe(&message->options, options, optlen);
114 memcpy_safe(&message->file, file, filelen);
115 memcpy_safe(&message->sname, sname, snamelen);
116
117 return message;
118 }
119
120 static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) {
121 assert(*descpos >= 0);
122
123 while (*descpos < *desclen) {
124 switch(descoption[*descpos]) {
125 case SD_DHCP_OPTION_PAD:
126 *descpos += 1;
127 break;
128
129 case SD_DHCP_OPTION_MESSAGE_TYPE:
130 case SD_DHCP_OPTION_OVERLOAD:
131 *descpos += 3;
132 break;
133
134 default:
135 return;
136 }
137 }
138 }
139
140 static int test_options_cb(uint8_t code, uint8_t len, const void *option, void *userdata) {
141 struct option_desc *desc = userdata;
142 uint8_t *descoption = NULL;
143 int *desclen = NULL, *descpos = NULL;
144 uint8_t optcode = 0;
145 uint8_t optlen = 0;
146 uint8_t i;
147
148 assert_se((!desc && !code && !len) || desc);
149
150 if (!desc)
151 return -EINVAL;
152
153 assert_se(code != SD_DHCP_OPTION_PAD);
154 assert_se(code != SD_DHCP_OPTION_END);
155 assert_se(code != SD_DHCP_OPTION_MESSAGE_TYPE);
156 assert_se(code != SD_DHCP_OPTION_OVERLOAD);
157
158 while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) {
159
160 if (desc->pos >= 0) {
161 descoption = &desc->options[0];
162 desclen = &desc->len;
163 descpos = &desc->pos;
164 } else if (desc->filepos >= 0) {
165 descoption = &desc->file[0];
166 desclen = &desc->filelen;
167 descpos = &desc->filepos;
168 } else if (desc->snamepos >= 0) {
169 descoption = &desc->sname[0];
170 desclen = &desc->snamelen;
171 descpos = &desc->snamepos;
172 }
173
174 assert_se(descoption && desclen && descpos);
175
176 if (*desclen)
177 test_ignore_opts(descoption, descpos, desclen);
178
179 if (*descpos < *desclen)
180 break;
181
182 if (*descpos == *desclen)
183 *descpos = -1;
184 }
185
186 assert_se(descpos);
187 assert_se(*descpos != -1);
188
189 optcode = descoption[*descpos];
190 optlen = descoption[*descpos + 1];
191
192 if (verbose)
193 printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode,
194 len, optlen);
195
196 assert_se(code == optcode);
197 assert_se(len == optlen);
198
199 for (i = 0; i < len; i++) {
200
201 if (verbose)
202 printf("0x%02x(0x%02x) ", ((uint8_t*) option)[i],
203 descoption[*descpos + 2 + i]);
204
205 assert_se(((uint8_t*) option)[i] == descoption[*descpos + 2 + i]);
206 }
207
208 if (verbose)
209 printf("\n");
210
211 *descpos += optlen + 2;
212
213 test_ignore_opts(descoption, descpos, desclen);
214
215 if (desc->pos != -1 && desc->pos == desc->len)
216 desc->pos = -1;
217
218 if (desc->filepos != -1 && desc->filepos == desc->filelen)
219 desc->filepos = -1;
220
221 if (desc->snamepos != -1 && desc->snamepos == desc->snamelen)
222 desc->snamepos = -1;
223
224 return 0;
225 }
226
227 static void test_options(struct option_desc *desc) {
228 uint8_t *options = NULL;
229 uint8_t *file = NULL;
230 uint8_t *sname = NULL;
231 int optlen = 0;
232 int filelen = 0;
233 int snamelen = 0;
234 int buflen = 0;
235 _cleanup_free_ DHCPMessage *message = NULL;
236 int res;
237
238 if (desc) {
239 file = &desc->file[0];
240 filelen = desc->filelen;
241 if (!filelen)
242 desc->filepos = -1;
243
244 sname = &desc->sname[0];
245 snamelen = desc->snamelen;
246 if (!snamelen)
247 desc->snamepos = -1;
248
249 options = &desc->options[0];
250 optlen = desc->len;
251 desc->pos = 0;
252 }
253 message = create_message(options, optlen, file, filelen,
254 sname, snamelen);
255
256 buflen = sizeof(DHCPMessage) + optlen;
257
258 if (!desc) {
259 assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, NULL, NULL)) == -ENOMSG);
260 } else if (desc->success) {
261 assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) >= 0);
262 assert_se(desc->pos == -1 && desc->filepos == -1 && desc->snamepos == -1);
263 } else
264 assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) < 0);
265
266 if (verbose)
267 printf("DHCP type %s\n", dhcp_type(res));
268 }
269
270 static uint8_t options[64] = {
271 'A', 'B', 'C', 'D',
272 160, 2, 0x11, 0x12,
273 0,
274 31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
275 0,
276 55, 3, 0x51, 0x52, 0x53,
277 17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
278 255
279 };
280
281 static void test_option_set(void) {
282 _cleanup_free_ DHCPMessage *result = NULL;
283 size_t offset = 0, len, pos;
284 unsigned i;
285
286 result = malloc0(sizeof(DHCPMessage) + 11);
287 assert_se(result);
288
289 result->options[0] = 'A';
290 result->options[1] = 'B';
291 result->options[2] = 'C';
292 result->options[3] = 'D';
293
294 assert_se(dhcp_option_append(result, 0, &offset, 0, SD_DHCP_OPTION_PAD,
295 0, NULL) == -ENOBUFS);
296 assert_se(offset == 0);
297
298 offset = 4;
299 assert_se(dhcp_option_append(result, 5, &offset, 0, SD_DHCP_OPTION_PAD,
300 0, NULL) == -ENOBUFS);
301 assert_se(offset == 4);
302 assert_se(dhcp_option_append(result, 6, &offset, 0, SD_DHCP_OPTION_PAD,
303 0, NULL) >= 0);
304 assert_se(offset == 5);
305
306 offset = pos = 4;
307 len = 11;
308 while (pos < len && options[pos] != SD_DHCP_OPTION_END) {
309 assert_se(dhcp_option_append(result, len, &offset, DHCP_OVERLOAD_SNAME,
310 options[pos],
311 options[pos + 1],
312 &options[pos + 2]) >= 0);
313
314 if (options[pos] == SD_DHCP_OPTION_PAD)
315 pos++;
316 else
317 pos += 2 + options[pos + 1];
318
319 if (pos < len)
320 assert_se(offset == pos);
321 }
322
323 for (i = 0; i < 9; i++) {
324 if (verbose)
325 printf("%2u: 0x%02x(0x%02x) (options)\n", i, result->options[i],
326 options[i]);
327 assert_se(result->options[i] == options[i]);
328 }
329
330 if (verbose)
331 printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9],
332 SD_DHCP_OPTION_END);
333
334 assert_se(result->options[9] == SD_DHCP_OPTION_END);
335
336 if (verbose)
337 printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result->options[10],
338 SD_DHCP_OPTION_PAD);
339
340 assert_se(result->options[10] == SD_DHCP_OPTION_PAD);
341
342 for (i = 0; i < pos - 8; i++) {
343 if (verbose)
344 printf("%2u: 0x%02x(0x%02x) (sname)\n", i, result->sname[i],
345 options[i + 9]);
346 assert_se(result->sname[i] == options[i + 9]);
347 }
348
349 if (verbose)
350 printf ("\n");
351 }
352
353 int main(int argc, char *argv[]) {
354 unsigned i;
355
356 test_invalid_buffer_length();
357 test_message_init();
358
359 test_options(NULL);
360
361 for (i = 0; i < ELEMENTSOF(option_tests); i++)
362 test_options(&option_tests[i]);
363
364 test_option_set();
365
366 return 0;
367 }