]> git.proxmox.com Git - systemd.git/blame - src/libsystemd-network/test-dhcp-option.c
New upstream version 249~rc1
[systemd.git] / src / libsystemd-network / test-dhcp-option.c
CommitLineData
a032b68d 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
a10f5d05 2
60f067b4 3#include <errno.h>
a10f5d05 4#include <net/if_arp.h>
db2df898
MP
5#include <stdbool.h>
6#include <stdio.h>
60f067b4 7#include <string.h>
60f067b4 8
db2df898 9#include "alloc-util.h"
60f067b4 10#include "dhcp-internal.h"
db2df898
MP
11#include "dhcp-protocol.h"
12#include "macro.h"
bb4f798a 13#include "memory-util.h"
60f067b4
JS
14
15struct 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
28static bool verbose = false;
29
30static 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,
4c89c718 33 SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
60f067b4
JS
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, },
4c89c718 41 { {}, 0, {}, 0, { SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
60f067b4
JS
42 42, 3, 0, 0, 0 }, 8, true, },
43 { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
44
45 { {}, 0,
4c89c718
MP
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, },
60f067b4 48
4c89c718 49 { { 1, 4, 1, 2, 3, 4, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
60f067b4 50 { 222, 3, 1, 2, 3 }, 5,
4c89c718 51 { SD_DHCP_OPTION_OVERLOAD, 1,
60f067b4
JS
52 DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
53};
54
e3bff60a 55static const char *dhcp_type(int type) {
60f067b4
JS
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
e3bff60a 76static void test_invalid_buffer_length(void) {
60f067b4
JS
77 DHCPMessage message;
78
4c89c718
MP
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);
60f067b4
JS
81}
82
e3bff60a 83static void test_message_init(void) {
60f067b4
JS
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,
5eef597e 92 DHCP_DISCOVER, ARPHRD_ETHER, optlen, &optoffset) >= 0);
60f067b4
JS
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);
5eef597e
MP
100 assert_se(magic[1] == 130);
101 assert_se(magic[2] == 83);
102 assert_se(magic[3] == 99);
60f067b4 103
4c89c718 104 assert_se(dhcp_option_parse(message, len, NULL, NULL, NULL) >= 0);
60f067b4
JS
105}
106
107static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
108 uint8_t *file, uint8_t filelen,
e3bff60a 109 uint8_t *sname, uint8_t snamelen) {
60f067b4
JS
110 DHCPMessage *message;
111 size_t len = sizeof(DHCPMessage) + optlen;
112
113 message = malloc0(len);
5eef597e 114 assert_se(message);
60f067b4 115
aa27b158
MP
116 memcpy_safe(&message->options, options, optlen);
117 memcpy_safe(&message->file, file, filelen);
118 memcpy_safe(&message->sname, sname, snamelen);
60f067b4
JS
119
120 return message;
121}
122
e3bff60a
MP
123static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) {
124 assert(*descpos >= 0);
125
60f067b4
JS
126 while (*descpos < *desclen) {
127 switch(descoption[*descpos]) {
4c89c718 128 case SD_DHCP_OPTION_PAD:
60f067b4
JS
129 *descpos += 1;
130 break;
131
4c89c718
MP
132 case SD_DHCP_OPTION_MESSAGE_TYPE:
133 case SD_DHCP_OPTION_OVERLOAD:
60f067b4
JS
134 *descpos += 3;
135 break;
136
137 default:
138 return;
139 }
140 }
141}
142
d9dfd233
MP
143static int test_options_cb(uint8_t code, uint8_t len, const void *option, void *userdata) {
144 struct option_desc *desc = userdata;
60f067b4
JS
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
4c89c718
MP
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);
60f067b4
JS
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)
d9dfd233 205 printf("0x%02x(0x%02x) ", ((uint8_t*) option)[i],
60f067b4
JS
206 descoption[*descpos + 2 + i]);
207
d9dfd233 208 assert_se(((uint8_t*) option)[i] == descoption[*descpos + 2 + i]);
60f067b4
JS
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
e3bff60a 230static void test_options(struct option_desc *desc) {
60f067b4
JS
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) {
4c89c718 262 assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, NULL, NULL)) == -ENOMSG);
60f067b4 263 } else if (desc->success) {
4c89c718
MP
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);
60f067b4 266 } else
4c89c718 267 assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) < 0);
60f067b4
JS
268
269 if (verbose)
270 printf("DHCP type %s\n", dhcp_type(res));
271}
272
8b3d4ff0
MB
273static void test_option_removal(struct option_desc *desc) {
274 _cleanup_free_ DHCPMessage *message = create_message(&desc->options[0], desc->len, NULL, 0, NULL, 0);
275
276 assert_se(dhcp_option_parse(message, sizeof(DHCPMessage) + desc->len, NULL, NULL, NULL) >= 0);
277 assert_se((desc->len = dhcp_option_remove_option(message->options, desc->len, SD_DHCP_OPTION_MESSAGE_TYPE)) >= 0);
278 assert_se(dhcp_option_parse(message, sizeof(DHCPMessage) + desc->len, NULL, NULL, NULL) < 0);
279}
280
60f067b4
JS
281static uint8_t options[64] = {
282 'A', 'B', 'C', 'D',
283 160, 2, 0x11, 0x12,
284 0,
285 31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
286 0,
287 55, 3, 0x51, 0x52, 0x53,
288 17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
289 255
290};
291
e3bff60a 292static void test_option_set(void) {
60f067b4
JS
293 _cleanup_free_ DHCPMessage *result = NULL;
294 size_t offset = 0, len, pos;
295 unsigned i;
296
297 result = malloc0(sizeof(DHCPMessage) + 11);
298 assert_se(result);
299
300 result->options[0] = 'A';
301 result->options[1] = 'B';
302 result->options[2] = 'C';
303 result->options[3] = 'D';
304
4c89c718 305 assert_se(dhcp_option_append(result, 0, &offset, 0, SD_DHCP_OPTION_PAD,
60f067b4
JS
306 0, NULL) == -ENOBUFS);
307 assert_se(offset == 0);
308
309 offset = 4;
4c89c718 310 assert_se(dhcp_option_append(result, 5, &offset, 0, SD_DHCP_OPTION_PAD,
60f067b4
JS
311 0, NULL) == -ENOBUFS);
312 assert_se(offset == 4);
4c89c718 313 assert_se(dhcp_option_append(result, 6, &offset, 0, SD_DHCP_OPTION_PAD,
60f067b4
JS
314 0, NULL) >= 0);
315 assert_se(offset == 5);
316
317 offset = pos = 4;
318 len = 11;
4c89c718 319 while (pos < len && options[pos] != SD_DHCP_OPTION_END) {
60f067b4
JS
320 assert_se(dhcp_option_append(result, len, &offset, DHCP_OVERLOAD_SNAME,
321 options[pos],
322 options[pos + 1],
323 &options[pos + 2]) >= 0);
324
4c89c718 325 if (options[pos] == SD_DHCP_OPTION_PAD)
60f067b4
JS
326 pos++;
327 else
328 pos += 2 + options[pos + 1];
329
330 if (pos < len)
331 assert_se(offset == pos);
332 }
333
334 for (i = 0; i < 9; i++) {
335 if (verbose)
e735f4d4 336 printf("%2u: 0x%02x(0x%02x) (options)\n", i, result->options[i],
60f067b4
JS
337 options[i]);
338 assert_se(result->options[i] == options[i]);
339 }
340
341 if (verbose)
342 printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9],
4c89c718 343 SD_DHCP_OPTION_END);
60f067b4 344
4c89c718 345 assert_se(result->options[9] == SD_DHCP_OPTION_END);
60f067b4
JS
346
347 if (verbose)
348 printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result->options[10],
4c89c718 349 SD_DHCP_OPTION_PAD);
60f067b4 350
4c89c718 351 assert_se(result->options[10] == SD_DHCP_OPTION_PAD);
60f067b4
JS
352
353 for (i = 0; i < pos - 8; i++) {
354 if (verbose)
e735f4d4 355 printf("%2u: 0x%02x(0x%02x) (sname)\n", i, result->sname[i],
60f067b4
JS
356 options[i + 9]);
357 assert_se(result->sname[i] == options[i + 9]);
358 }
359
360 if (verbose)
361 printf ("\n");
362}
363
e3bff60a
MP
364int main(int argc, char *argv[]) {
365 unsigned i;
60f067b4
JS
366
367 test_invalid_buffer_length();
368 test_message_init();
369
370 test_options(NULL);
371
372 for (i = 0; i < ELEMENTSOF(option_tests); i++)
373 test_options(&option_tests[i]);
374
375 test_option_set();
376
8b3d4ff0
MB
377 for (i = 0; i < ELEMENTSOF(option_tests); i++) {
378 struct option_desc *desc = &option_tests[i];
379 if (!desc->success || desc->snamelen > 0 || desc->filelen > 0)
380 continue;
381 test_option_removal(desc);
382 }
383
60f067b4
JS
384 return 0;
385}