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