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