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