chiark / gitweb /
sd-icmp6-nd: Add function to stop ongoing ICMPv6 discovery
[elogind.git] / src / libsystemd-network / test-dhcp-option.c
1
2 #include <stdio.h>
3 #include <stdbool.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <assert.h>
7
8 #include "util.h"
9 #include "macro.h"
10
11 #include "dhcp-protocol.h"
12 #include "dhcp-internal.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 {
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 {
78         DHCPMessage message;
79
80         assert_se(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
81         assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL)
82                == -EINVAL);
83 }
84
85 static void test_message_init(void)
86 {
87         _cleanup_free_ DHCPMessage *message = NULL;
88         size_t optlen = 4, optoffset;
89         size_t len = sizeof(DHCPMessage) + optlen;
90         uint8_t *magic;
91
92         message = malloc0(len);
93
94         assert_se(dhcp_message_init(message, BOOTREQUEST, 0x12345678,
95                   DHCP_DISCOVER, optlen, &optoffset) >= 0);
96
97         assert_se(message->xid == htobe32(0x12345678));
98         assert_se(message->op == BOOTREQUEST);
99
100         magic = (uint8_t*)&message->magic;
101
102         assert_se(magic[0] == 99);
103         assert_se(magic[1] = 130);
104         assert_se(magic[2] = 83);
105         assert_se(magic[3] = 99);
106
107         assert_se(dhcp_option_parse(message, len, NULL, NULL) >= 0);
108 }
109
110 static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
111                 uint8_t *file, uint8_t filelen,
112                 uint8_t *sname, uint8_t snamelen)
113 {
114         DHCPMessage *message;
115         size_t len = sizeof(DHCPMessage) + optlen;
116
117         message = malloc0(len);
118
119         if (options && optlen)
120                 memcpy(&message->options, options, optlen);
121
122         if (file && filelen <= 128)
123                 memcpy(&message->file, file, filelen);
124
125         if (sname && snamelen <= 64)
126                 memcpy(&message->sname, sname, snamelen);
127
128         return message;
129 }
130
131 static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen)
132 {
133         while (*descpos < *desclen) {
134                 switch(descoption[*descpos]) {
135                 case DHCP_OPTION_PAD:
136                         *descpos += 1;
137                         break;
138
139                 case DHCP_OPTION_MESSAGE_TYPE:
140                 case DHCP_OPTION_OVERLOAD:
141                         *descpos += 3;
142                         break;
143
144                 default:
145                         return;
146                 }
147         }
148 }
149
150 static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option,
151                            void *user_data)
152 {
153         struct option_desc *desc = user_data;
154         uint8_t *descoption = NULL;
155         int *desclen = NULL, *descpos = NULL;
156         uint8_t optcode = 0;
157         uint8_t optlen = 0;
158         uint8_t i;
159
160         assert_se((!desc && !code && !len) || desc);
161
162         if (!desc)
163                 return -EINVAL;
164
165         assert_se(code != DHCP_OPTION_PAD);
166         assert_se(code != DHCP_OPTION_END);
167         assert_se(code != DHCP_OPTION_MESSAGE_TYPE);
168         assert_se(code != DHCP_OPTION_OVERLOAD);
169
170         while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) {
171
172                 if (desc->pos >= 0) {
173                         descoption = &desc->options[0];
174                         desclen = &desc->len;
175                         descpos = &desc->pos;
176                 } else if (desc->filepos >= 0) {
177                         descoption = &desc->file[0];
178                         desclen = &desc->filelen;
179                         descpos = &desc->filepos;
180                 } else if (desc->snamepos >= 0) {
181                         descoption = &desc->sname[0];
182                         desclen = &desc->snamelen;
183                         descpos = &desc->snamepos;
184                 }
185
186                 assert_se(descoption && desclen && descpos);
187
188                 if (*desclen)
189                         test_ignore_opts(descoption, descpos, desclen);
190
191                 if (*descpos < *desclen)
192                         break;
193
194                 if (*descpos == *desclen)
195                         *descpos = -1;
196         }
197
198         assert_se(descpos);
199         assert_se(*descpos != -1);
200
201         optcode = descoption[*descpos];
202         optlen = descoption[*descpos + 1];
203
204         if (verbose)
205                 printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode,
206                                 len, optlen);
207
208         assert_se(code == optcode);
209         assert_se(len == optlen);
210
211         for (i = 0; i < len; i++) {
212
213                 if (verbose)
214                         printf("0x%02x(0x%02x) ", option[i],
215                                         descoption[*descpos + 2 + i]);
216
217                 assert_se(option[i] == descoption[*descpos + 2 + i]);
218         }
219
220         if (verbose)
221                 printf("\n");
222
223         *descpos += optlen + 2;
224
225         test_ignore_opts(descoption, descpos, desclen);
226
227         if (desc->pos != -1 && desc->pos == desc->len)
228                 desc->pos = -1;
229
230         if (desc->filepos != -1 && desc->filepos == desc->filelen)
231                 desc->filepos = -1;
232
233         if (desc->snamepos != -1 && desc->snamepos == desc->snamelen)
234                 desc->snamepos = -1;
235
236         return 0;
237 }
238
239 static void test_options(struct option_desc *desc)
240 {
241         uint8_t *options = NULL;
242         uint8_t *file = NULL;
243         uint8_t *sname = NULL;
244         int optlen = 0;
245         int filelen = 0;
246         int snamelen = 0;
247         int buflen = 0;
248         _cleanup_free_ DHCPMessage *message = NULL;
249         int res;
250
251         if (desc) {
252                 file = &desc->file[0];
253                 filelen = desc->filelen;
254                 if (!filelen)
255                         desc->filepos = -1;
256
257                 sname = &desc->sname[0];
258                 snamelen = desc->snamelen;
259                 if (!snamelen)
260                         desc->snamepos = -1;
261
262                 options = &desc->options[0];
263                 optlen = desc->len;
264                 desc->pos = 0;
265         }
266         message = create_message(options, optlen, file, filelen,
267                                  sname, snamelen);
268
269         buflen = sizeof(DHCPMessage) + optlen;
270
271         if (!desc) {
272                 assert_se((res = dhcp_option_parse(message, buflen,
273                                                 test_options_cb,
274                                                 NULL)) == -ENOMSG);
275         } else if (desc->success) {
276                 assert_se((res = dhcp_option_parse(message, buflen,
277                                                 test_options_cb,
278                                                 desc)) >= 0);
279                 assert_se(desc->pos == -1 && desc->filepos == -1 &&
280                                 desc->snamepos == -1);
281         } else
282                 assert_se((res = dhcp_option_parse(message, buflen,
283                                                 test_options_cb,
284                                                 desc)) < 0);
285
286         if (verbose)
287                 printf("DHCP type %s\n", dhcp_type(res));
288 }
289
290 static uint8_t options[64] = {
291         'A', 'B', 'C', 'D',
292         160, 2, 0x11, 0x12,
293         0,
294         31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
295         0,
296         55, 3, 0x51, 0x52, 0x53,
297         17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
298         255
299 };
300
301 static void test_option_set(void)
302 {
303         _cleanup_free_ DHCPMessage *result = NULL;
304         size_t offset = 0, len, pos;
305         unsigned i;
306
307         result = malloc0(sizeof(DHCPMessage) + 11);
308         assert_se(result);
309
310         result->options[0] = 'A';
311         result->options[1] = 'B';
312         result->options[2] = 'C';
313         result->options[3] = 'D';
314
315         assert_se(dhcp_option_append(result, 0, &offset, 0, DHCP_OPTION_PAD,
316                                      0, NULL) == -ENOBUFS);
317         assert_se(offset == 0);
318
319         offset = 4;
320         assert_se(dhcp_option_append(result, 5, &offset, 0, DHCP_OPTION_PAD,
321                                      0, NULL) == -ENOBUFS);
322         assert_se(offset == 4);
323         assert_se(dhcp_option_append(result, 6, &offset, 0, DHCP_OPTION_PAD,
324                                      0, NULL) >= 0);
325         assert_se(offset == 5);
326
327         offset = pos = 4;
328         len = 11;
329         while (pos < len && options[pos] != DHCP_OPTION_END) {
330                 assert_se(dhcp_option_append(result, len, &offset, DHCP_OVERLOAD_SNAME,
331                                              options[pos],
332                                              options[pos + 1],
333                                              &options[pos + 2]) >= 0);
334
335                 if (options[pos] == DHCP_OPTION_PAD)
336                         pos++;
337                 else
338                         pos += 2 + options[pos + 1];
339
340                 if (pos < len)
341                         assert_se(offset == pos);
342         }
343
344         for (i = 0; i < 9; i++) {
345                 if (verbose)
346                         printf("%2d: 0x%02x(0x%02x) (options)\n", i, result->options[i],
347                                options[i]);
348                 assert_se(result->options[i] == options[i]);
349         }
350
351         if (verbose)
352                 printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9],
353                        DHCP_OPTION_END);
354
355         assert_se(result->options[9] == DHCP_OPTION_END);
356
357         if (verbose)
358                 printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result->options[10],
359                        DHCP_OPTION_PAD);
360
361         assert_se(result->options[10] == DHCP_OPTION_PAD);
362
363         for (i = 0; i < pos - 8; i++) {
364                 if (verbose)
365                         printf("%2d: 0x%02x(0x%02x) (sname)\n", i, result->sname[i],
366                                options[i + 9]);
367                 assert_se(result->sname[i] == options[i + 9]);
368         }
369
370         if (verbose)
371                 printf ("\n");
372 }
373
374 int main(int argc, char *argv[])
375 {
376         unsigned int i;
377
378         test_invalid_buffer_length();
379         test_message_init();
380
381         test_options(NULL);
382
383         for (i = 0; i < ELEMENTSOF(option_tests); i++)
384                 test_options(&option_tests[i]);
385
386         test_option_set();
387
388         return 0;
389 }