chiark / gitweb /
sd-dhcp: refactor dhcp_option_append
[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 = 3, 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, message->options, 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 result[64] = {
291         'A', 'B', 'C', 'D',
292 };
293
294 static uint8_t options[64] = {
295         'A', 'B', 'C', 'D',
296         160, 2, 0x11, 0x12,
297         0,
298         31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
299         0,
300         55, 3, 0x51, 0x52, 0x53,
301         17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
302         255
303 };
304
305 static void test_option_set(void)
306 {
307         size_t offset = 0, len, pos;
308         unsigned i;
309
310         assert_se(dhcp_option_append(result, 0, &offset, DHCP_OPTION_PAD,
311                                   0, NULL) == -ENOBUFS);
312         assert_se(offset == 0);
313
314         offset = 4;
315         assert_se(dhcp_option_append(result, 1, &offset, DHCP_OPTION_PAD,
316                                     0, NULL) >= 0);
317         assert_se(offset == 5);
318
319         offset = pos = 4;
320         len = 60;
321         while (pos < 64 && options[pos] != DHCP_OPTION_END) {
322                 offset = pos;
323
324                 assert_se(dhcp_option_append(result, len, &offset,
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                 assert_se(offset == pos);
335         }
336
337         for (i = 0; i < pos; i++) {
338                 if (verbose)
339                         printf("%2d: 0x%02x(0x%02x)\n", i, result[i],
340                                options[i]);
341                 assert_se(result[i] == options[i]);
342         }
343
344         if (verbose)
345                 printf ("\n");
346 }
347
348 int main(int argc, char *argv[])
349 {
350         unsigned int i;
351
352         test_invalid_buffer_length();
353         test_message_init();
354
355         test_options(NULL);
356
357         for (i = 0; i < ELEMENTSOF(option_tests); i++)
358                 test_options(&option_tests[i]);
359
360         test_option_set();
361
362         return 0;
363 }