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