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