chiark / gitweb /
8659fd502556d888316838dfe0c07b2a9c536fb3
[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), 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_se(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_se(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_se((!desc && !code && !len) || desc);
164
165         if (!desc)
166                 return -EINVAL;
167
168         assert_se(code != DHCP_OPTION_PAD);
169         assert_se(code != DHCP_OPTION_END);
170         assert_se(code != DHCP_OPTION_MESSAGE_TYPE);
171         assert_se(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_se(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_se(descpos);
202         assert_se(*descpos != -1);
203
204         optcode = descoption[*descpos];
205         optlen = descoption[*descpos + 1];
206
207         if (verbose)
208                 printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode,
209                                 len, optlen);
210
211         assert_se(code == optcode);
212         assert_se(len == optlen);
213
214         for (i = 0; i < len; i++) {
215
216                 if (verbose)
217                         printf("0x%02x(0x%02x) ", option[i],
218                                         descoption[*descpos + 2 + i]);
219
220                 assert_se(option[i] == descoption[*descpos + 2 + i]);
221         }
222
223         if (verbose)
224                 printf("\n");
225
226         *descpos += optlen + 2;
227
228         test_ignore_opts(descoption, descpos, desclen);
229
230         if (desc->pos != -1 && desc->pos == desc->len)
231                 desc->pos = -1;
232
233         if (desc->filepos != -1 && desc->filepos == desc->filelen)
234                 desc->filepos = -1;
235
236         if (desc->snamepos != -1 && desc->snamepos == desc->snamelen)
237                 desc->snamepos = -1;
238
239         return 0;
240 }
241
242 static void test_options(struct option_desc *desc)
243 {
244         uint8_t *options = NULL;
245         uint8_t *file = NULL;
246         uint8_t *sname = NULL;
247         int optlen = 0;
248         int filelen = 0;
249         int snamelen = 0;
250         int buflen = 0;
251         _cleanup_free_ DHCPMessage *message;
252         int res;
253
254         if (desc) {
255                 file = &desc->file[0];
256                 filelen = desc->filelen;
257                 if (!filelen)
258                         desc->filepos = -1;
259
260                 sname = &desc->sname[0];
261                 snamelen = desc->snamelen;
262                 if (!snamelen)
263                         desc->snamepos = -1;
264
265                 options = &desc->options[0];
266                 optlen = desc->len;
267                 desc->pos = 0;
268         }
269         message = create_message(options, optlen, file, filelen,
270                         sname, snamelen);
271
272         buflen = sizeof(DHCPMessage) + 4 + optlen;
273
274         if (!desc) {
275                 assert_se((res = dhcp_option_parse(message, buflen,
276                                                 test_options_cb,
277                                                 NULL)) == -ENOMSG);
278         } else if (desc->success) {
279                 assert_se((res = dhcp_option_parse(message, buflen,
280                                                 test_options_cb,
281                                                 desc)) >= 0);
282                 assert_se(desc->pos == -1 && desc->filepos == -1 &&
283                                 desc->snamepos == -1);
284         } else
285                 assert_se((res = dhcp_option_parse(message, buflen,
286                                                 test_options_cb,
287                                                 desc)) < 0);
288
289         if (verbose)
290                 printf("DHCP type %s\n", dhcp_type(res));
291 }
292
293 static uint8_t result[64] = {
294         'A', 'B', 'C', 'D',
295 };
296
297 static uint8_t options[64] = {
298         'A', 'B', 'C', 'D',
299         160, 2, 0x11, 0x12,
300         0,
301         31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
302         0,
303         55, 3, 0x51, 0x52, 0x53,
304         17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
305         255
306 };
307
308 static void test_option_set(void)
309 {
310         size_t len, oldlen;
311         int pos, i;
312         uint8_t *opt;
313
314         assert_se(dhcp_option_append(NULL, NULL, 0, 0, NULL) == -EINVAL);
315
316         len = 0;
317         opt = &result[0];
318         assert_se(dhcp_option_append(&opt, NULL, 0, 0, NULL) == -EINVAL);
319         assert_se(opt == &result[0] && len == 0);
320
321         assert_se(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
322                                   0, NULL) == -ENOBUFS);
323         assert_se(opt == &result[0] && len == 0);
324
325         opt = &result[4];
326         len = 1;
327         assert_se(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
328                                     0, NULL) >= 0);
329         assert_se(opt == &result[5] && len == 0);
330
331         pos = 4;
332         len = 60;
333         while (pos < 64 && options[pos] != DHCP_OPTION_END) {
334                 opt = &result[pos];
335                 oldlen = len;
336
337                 assert_se(dhcp_option_append(&opt, &len, options[pos],
338                                           options[pos + 1],
339                                           &options[pos + 2]) >= 0);
340
341                 if (options[pos] == DHCP_OPTION_PAD) {
342                         assert_se(opt == &result[pos + 1]);
343                         assert_se(len == oldlen - 1);
344                         pos++;
345                 } else {
346                         assert_se(opt == &result[pos + 2 + options[pos + 1]]);
347                         assert_se(len == oldlen - 2 - options[pos + 1]);
348                         pos += 2 + options[pos + 1];
349                 }
350         }
351
352         for (i = 0; i < pos; i++) {
353                 if (verbose)
354                         printf("%2d: 0x%02x(0x%02x)\n", i, result[i],
355                                options[i]);
356                 assert_se(result[i] == options[i]);
357         }
358
359         if (verbose)
360                 printf ("\n");
361 }
362
363 int main(int argc, char *argv[])
364 {
365         unsigned int i;
366
367         test_invalid_buffer_length();
368         test_cookie();
369
370         test_options(NULL);
371
372         for (i = 0; i < ELEMENTSOF(option_tests); i++)
373                 test_options(&option_tests[i]);
374
375         test_option_set();
376
377         return 0;
378 }