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