]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/test-dhcp-option.c
basic/include: replace _Static_assert() with static_assert()
[thirdparty/systemd.git] / src / libsystemd-network / test-dhcp-option.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
ddb82ec2 2
ddb82ec2 3#include <net/if_arp.h>
cf0fbc49 4#include <stdio.h>
a0ae95c9 5#include <string.h>
a0ae95c9 6
b5efdb8a 7#include "alloc-util.h"
8664ded7
YW
8#include "dhcp-option.h"
9#include "dhcp-packet.h"
0a970718 10#include "memory-util.h"
965040d8 11#include "tests.h"
a0ae95c9 12
a10c375e
PF
13struct 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
26static bool verbose = false;
27
28static 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,
22805d92 31 SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
a10c375e
PF
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, },
22805d92 39 { {}, 0, {}, 0, { SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
a10c375e
PF
40 42, 3, 0, 0, 0 }, 8, true, },
41 { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
42
43 { {}, 0,
22805d92
BG
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, },
a10c375e 46
22805d92 47 { { 1, 4, 1, 2, 3, 4, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
a10c375e 48 { 222, 3, 1, 2, 3 }, 5,
22805d92 49 { SD_DHCP_OPTION_OVERLOAD, 1,
a10c375e
PF
50 DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
51};
52
9116b406 53static const char *dhcp_type(int type) {
79893116 54 switch (type) {
a10c375e
PF
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
9116b406 74static void test_invalid_buffer_length(void) {
a0ae95c9
PF
75 DHCPMessage message;
76
f693e9b3
TG
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);
a0ae95c9
PF
79}
80
9116b406 81static void test_message_init(void) {
d47e1de4 82 _cleanup_free_ DHCPMessage *message = NULL;
2688ef60 83 size_t optlen = 4, optoffset;
d47e1de4 84 size_t len = sizeof(DHCPMessage) + optlen;
20b958bf 85 uint8_t *magic;
a0ae95c9
PF
86
87 message = malloc0(len);
88
d47e1de4 89 assert_se(dhcp_message_init(message, BOOTREQUEST, 0x12345678,
be40a31f 90 ARPHRD_ETHER, ETH_ALEN, (uint8_t[16]){}, DHCP_DISCOVER,
97fa338d 91 optlen, &optoffset) >= 0);
d47e1de4
TG
92
93 assert_se(message->xid == htobe32(0x12345678));
94 assert_se(message->op == BOOTREQUEST);
a0ae95c9 95
d47e1de4 96 magic = (uint8_t*)&message->magic;
a0ae95c9 97
d47e1de4 98 assert_se(magic[0] == 99);
86be3e1e
TA
99 assert_se(magic[1] == 130);
100 assert_se(magic[2] == 83);
101 assert_se(magic[3] == 99);
d47e1de4 102
f693e9b3 103 assert_se(dhcp_option_parse(message, len, NULL, NULL, NULL) >= 0);
a10c375e
PF
104}
105
106static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
107 uint8_t *file, uint8_t filelen,
9116b406 108 uint8_t *sname, uint8_t snamelen) {
a10c375e 109 DHCPMessage *message;
d47e1de4 110 size_t len = sizeof(DHCPMessage) + optlen;
a0ae95c9 111
a10c375e 112 message = malloc0(len);
6f428772 113 assert_se(message);
a10c375e 114
75f32f04
ZJS
115 memcpy_safe(&message->options, options, optlen);
116 memcpy_safe(&message->file, file, filelen);
117 memcpy_safe(&message->sname, sname, snamelen);
a10c375e
PF
118
119 return message;
120}
121
9116b406 122static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) {
f21b863e 123 assert_se(*descpos >= 0);
0915fda6 124
a10c375e 125 while (*descpos < *desclen) {
79893116 126 switch (descoption[*descpos]) {
22805d92 127 case SD_DHCP_OPTION_PAD:
a10c375e
PF
128 *descpos += 1;
129 break;
130
22805d92
BG
131 case SD_DHCP_OPTION_MESSAGE_TYPE:
132 case SD_DHCP_OPTION_OVERLOAD:
a10c375e
PF
133 *descpos += 3;
134 break;
135
136 default:
137 return;
138 }
139 }
140}
141
e4735228 142static int test_options_cb(uint8_t code, uint8_t len, const void *option, void *userdata) {
89ca10c6 143 struct option_desc *desc = userdata;
a10c375e
PF
144 uint8_t *descoption = NULL;
145 int *desclen = NULL, *descpos = NULL;
146 uint8_t optcode = 0;
147 uint8_t optlen = 0;
a10c375e 148
12e0f830 149 assert_se((!desc && !code && !len) || desc);
a10c375e
PF
150
151 if (!desc)
152 return -EINVAL;
153
22805d92
BG
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);
a10c375e
PF
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
12e0f830 175 assert_se(descoption && desclen && descpos);
a10c375e
PF
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
12e0f830
TG
187 assert_se(descpos);
188 assert_se(*descpos != -1);
a10c375e
PF
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
12e0f830
TG
197 assert_se(code == optcode);
198 assert_se(len == optlen);
a10c375e 199
874e525d 200 for (unsigned i = 0; i < len; i++) {
a10c375e 201 if (verbose)
874e525d
ZJS
202 printf("0x%02x(0x%02x) ",
203 ((uint8_t*) option)[i],
204 descoption[*descpos + 2 + i]);
a10c375e 205
e4735228 206 assert_se(((uint8_t*) option)[i] == descoption[*descpos + 2 + i]);
a10c375e
PF
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
9116b406 228static void test_options(struct option_desc *desc) {
a10c375e
PF
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;
d47e1de4 236 _cleanup_free_ DHCPMessage *message = NULL;
a10c375e
PF
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,
d47e1de4 255 sname, snamelen);
a10c375e 256
d47e1de4 257 buflen = sizeof(DHCPMessage) + optlen;
a10c375e
PF
258
259 if (!desc) {
f693e9b3 260 assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, NULL, NULL)) == -ENOMSG);
a10c375e 261 } else if (desc->success) {
f693e9b3
TG
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);
a10c375e 264 } else
f693e9b3 265 assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) < 0);
a10c375e
PF
266
267 if (verbose)
268 printf("DHCP type %s\n", dhcp_type(res));
a0ae95c9
PF
269}
270
11c38d3e
YA
271static 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
74e2e548 279static uint8_t the_options[64] = {
d8b61a1d
PF
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
9116b406 290static void test_option_set(void) {
4eb20caa 291 _cleanup_free_ DHCPMessage *result = NULL;
20b958bf 292 size_t offset = 0, len, pos;
d8b61a1d 293
04b28be1
TG
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
22805d92 302 assert_se(dhcp_option_append(result, 0, &offset, 0, SD_DHCP_OPTION_PAD,
04b28be1 303 0, NULL) == -ENOBUFS);
20b958bf 304 assert_se(offset == 0);
d8b61a1d 305
20b958bf 306 offset = 4;
22805d92 307 assert_se(dhcp_option_append(result, 5, &offset, 0, SD_DHCP_OPTION_PAD,
04b28be1
TG
308 0, NULL) == -ENOBUFS);
309 assert_se(offset == 4);
22805d92 310 assert_se(dhcp_option_append(result, 6, &offset, 0, SD_DHCP_OPTION_PAD,
04b28be1 311 0, NULL) >= 0);
20b958bf 312 assert_se(offset == 5);
d8b61a1d 313
20b958bf 314 offset = pos = 4;
04b28be1 315 len = 11;
74e2e548 316 while (pos < len && the_options[pos] != SD_DHCP_OPTION_END) {
04b28be1 317 assert_se(dhcp_option_append(result, len, &offset, DHCP_OVERLOAD_SNAME,
74e2e548
ZJS
318 the_options[pos],
319 the_options[pos + 1],
320 &the_options[pos + 2]) >= 0);
d8b61a1d 321
74e2e548 322 if (the_options[pos] == SD_DHCP_OPTION_PAD)
d8b61a1d 323 pos++;
20b958bf 324 else
74e2e548 325 pos += 2 + the_options[pos + 1];
20b958bf 326
04b28be1
TG
327 if (pos < len)
328 assert_se(offset == pos);
d8b61a1d
PF
329 }
330
874e525d 331 for (unsigned i = 0; i < 9; i++) {
d8b61a1d 332 if (verbose)
c4ef0548 333 printf("%2u: 0x%02x(0x%02x) (options)\n", i, result->options[i],
74e2e548
ZJS
334 the_options[i]);
335 assert_se(result->options[i] == the_options[i]);
04b28be1
TG
336 }
337
338 if (verbose)
339 printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9],
5570a097 340 (unsigned) SD_DHCP_OPTION_END);
04b28be1 341
22805d92 342 assert_se(result->options[9] == SD_DHCP_OPTION_END);
04b28be1
TG
343
344 if (verbose)
345 printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result->options[10],
5570a097 346 (unsigned) SD_DHCP_OPTION_PAD);
04b28be1 347
22805d92 348 assert_se(result->options[10] == SD_DHCP_OPTION_PAD);
04b28be1 349
874e525d 350 for (unsigned i = 0; i < pos - 8; i++) {
04b28be1 351 if (verbose)
c4ef0548 352 printf("%2u: 0x%02x(0x%02x) (sname)\n", i, result->sname[i],
74e2e548
ZJS
353 the_options[i + 9]);
354 assert_se(result->sname[i] == the_options[i + 9]);
d8b61a1d
PF
355 }
356
357 if (verbose)
358 printf ("\n");
359}
360
9116b406 361int main(int argc, char *argv[]) {
965040d8
YW
362 test_setup_logging(LOG_DEBUG);
363
a0ae95c9 364 test_invalid_buffer_length();
d47e1de4 365 test_message_init();
a0ae95c9 366
a10c375e
PF
367 test_options(NULL);
368
ddb8a639
I
369 FOREACH_ELEMENT(desc, option_tests)
370 test_options(desc);
a10c375e 371
d8b61a1d
PF
372 test_option_set();
373
ddb8a639 374 FOREACH_ELEMENT(desc, option_tests) {
11c38d3e
YA
375 if (!desc->success || desc->snamelen > 0 || desc->filelen > 0)
376 continue;
377 test_option_removal(desc);
378 }
379
a0ae95c9
PF
380 return 0;
381}