chiark / gitweb /
bd448ff2b20dc868056d8c0378f9f146a5a1adef
[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
7 #include "util.h"
8 #include "macro.h"
9
10 #include "dhcp-protocol.h"
11 #include "dhcp-internal.h"
12
13 struct option_desc {
14         uint8_t sname[64];
15         int snamelen;
16         uint8_t file[128];
17         int filelen;
18         uint8_t options[128];
19         int len;
20         bool success;
21         int filepos;
22         int snamepos;
23         int pos;
24 };
25
26 static bool verbose = false;
27
28 static struct option_desc option_tests[] = {
29         { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
30         { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
31                           DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
32         { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
33         { {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
34                           0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
35                           0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0,
36                           0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
37                           0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
38           40, true, },
39         { {}, 0, {}, 0, { DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
40                           42, 3, 0, 0, 0 }, 8, true, },
41         { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
42
43         { {}, 0,
44           { 222, 3, 1, 2, 3, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
45           { DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
46
47         { { 1, 4, 1, 2, 3, 4, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
48           { 222, 3, 1, 2, 3 }, 5,
49           { DHCP_OPTION_OVERLOAD, 1,
50             DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
51 };
52
53 static const char *dhcp_type(int type)
54 {
55         switch(type) {
56         case DHCP_DISCOVER:
57                 return "DHCPDISCOVER";
58         case DHCP_OFFER:
59                 return "DHCPOFFER";
60         case DHCP_REQUEST:
61                 return "DHCPREQUEST";
62         case DHCP_DECLINE:
63                 return "DHCPDECLINE";
64         case DHCP_ACK:
65                 return "DHCPACK";
66         case DHCP_NAK:
67                 return "DHCPNAK";
68         case DHCP_RELEASE:
69                 return "DHCPRELEASE";
70         default:
71                 return "unknown";
72         }
73 }
74
75 static void test_invalid_buffer_length(void)
76 {
77         DHCPMessage message;
78
79         assert_se(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
80         assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL)
81                == -EINVAL);
82 }
83
84 static void test_message_init(void)
85 {
86         _cleanup_free_ DHCPMessage *message = NULL;
87         size_t optlen = 4, optoffset;
88         size_t len = sizeof(DHCPMessage) + optlen;
89         uint8_t *magic;
90
91         message = malloc0(len);
92
93         assert_se(dhcp_message_init(message, BOOTREQUEST, 0x12345678,
94                   DHCP_DISCOVER, ARPHRD_ETHER, optlen, &optoffset) >= 0);
95
96         assert_se(message->xid == htobe32(0x12345678));
97         assert_se(message->op == BOOTREQUEST);
98
99         magic = (uint8_t*)&message->magic;
100
101         assert_se(magic[0] == 99);
102         assert_se(magic[1] == 130);
103         assert_se(magic[2] == 83);
104         assert_se(magic[3] == 99);
105
106         assert_se(dhcp_option_parse(message, len, NULL, NULL) >= 0);
107 }
108
109 static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
110                 uint8_t *file, uint8_t filelen,
111                 uint8_t *sname, uint8_t snamelen)
112 {
113         DHCPMessage *message;
114         size_t len = sizeof(DHCPMessage) + optlen;
115
116         message = malloc0(len);
117         assert_se(message);
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         assert(*descpos >= 0);
134
135         while (*descpos < *desclen) {
136                 switch(descoption[*descpos]) {
137                 case DHCP_OPTION_PAD:
138                         *descpos += 1;
139                         break;
140
141                 case DHCP_OPTION_MESSAGE_TYPE:
142                 case DHCP_OPTION_OVERLOAD:
143                         *descpos += 3;
144                         break;
145
146                 default:
147                         return;
148                 }
149         }
150 }
151
152 static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option,
153                            void *user_data)
154 {
155         struct option_desc *desc = user_data;
156         uint8_t *descoption = NULL;
157         int *desclen = NULL, *descpos = NULL;
158         uint8_t optcode = 0;
159         uint8_t optlen = 0;
160         uint8_t i;
161
162         assert_se((!desc && !code && !len) || desc);
163
164         if (!desc)
165                 return -EINVAL;
166
167         assert_se(code != DHCP_OPTION_PAD);
168         assert_se(code != DHCP_OPTION_END);
169         assert_se(code != DHCP_OPTION_MESSAGE_TYPE);
170         assert_se(code != DHCP_OPTION_OVERLOAD);
171
172         while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) {
173
174                 if (desc->pos >= 0) {
175                         descoption = &desc->options[0];
176                         desclen = &desc->len;
177                         descpos = &desc->pos;
178                 } else if (desc->filepos >= 0) {
179                         descoption = &desc->file[0];
180                         desclen = &desc->filelen;
181                         descpos = &desc->filepos;
182                 } else if (desc->snamepos >= 0) {
183                         descoption = &desc->sname[0];
184                         desclen = &desc->snamelen;
185                         descpos = &desc->snamepos;
186                 }
187
188                 assert_se(descoption && desclen && descpos);
189
190                 if (*desclen)
191                         test_ignore_opts(descoption, descpos, desclen);
192
193                 if (*descpos < *desclen)
194                         break;
195
196                 if (*descpos == *desclen)
197                         *descpos = -1;
198         }
199
200         assert_se(descpos);
201         assert_se(*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_se(code == optcode);
211         assert_se(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_se(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 = NULL;
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) + optlen;
272
273         if (!desc) {
274                 assert_se((res = dhcp_option_parse(message, buflen,
275                                                 test_options_cb,
276                                                 NULL)) == -ENOMSG);
277         } else if (desc->success) {
278                 assert_se((res = dhcp_option_parse(message, buflen,
279                                                 test_options_cb,
280                                                 desc)) >= 0);
281                 assert_se(desc->pos == -1 && desc->filepos == -1 &&
282                                 desc->snamepos == -1);
283         } else
284                 assert_se((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 options[64] = {
293         'A', 'B', 'C', 'D',
294         160, 2, 0x11, 0x12,
295         0,
296         31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
297         0,
298         55, 3, 0x51, 0x52, 0x53,
299         17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
300         255
301 };
302
303 static void test_option_set(void)
304 {
305         _cleanup_free_ DHCPMessage *result = NULL;
306         size_t offset = 0, len, pos;
307         unsigned i;
308
309         result = malloc0(sizeof(DHCPMessage) + 11);
310         assert_se(result);
311
312         result->options[0] = 'A';
313         result->options[1] = 'B';
314         result->options[2] = 'C';
315         result->options[3] = 'D';
316
317         assert_se(dhcp_option_append(result, 0, &offset, 0, DHCP_OPTION_PAD,
318                                      0, NULL) == -ENOBUFS);
319         assert_se(offset == 0);
320
321         offset = 4;
322         assert_se(dhcp_option_append(result, 5, &offset, 0, DHCP_OPTION_PAD,
323                                      0, NULL) == -ENOBUFS);
324         assert_se(offset == 4);
325         assert_se(dhcp_option_append(result, 6, &offset, 0, DHCP_OPTION_PAD,
326                                      0, NULL) >= 0);
327         assert_se(offset == 5);
328
329         offset = pos = 4;
330         len = 11;
331         while (pos < len && options[pos] != DHCP_OPTION_END) {
332                 assert_se(dhcp_option_append(result, len, &offset, DHCP_OVERLOAD_SNAME,
333                                              options[pos],
334                                              options[pos + 1],
335                                              &options[pos + 2]) >= 0);
336
337                 if (options[pos] == DHCP_OPTION_PAD)
338                         pos++;
339                 else
340                         pos += 2 + options[pos + 1];
341
342                 if (pos < len)
343                         assert_se(offset == pos);
344         }
345
346         for (i = 0; i < 9; i++) {
347                 if (verbose)
348                         printf("%2u: 0x%02x(0x%02x) (options)\n", i, result->options[i],
349                                options[i]);
350                 assert_se(result->options[i] == options[i]);
351         }
352
353         if (verbose)
354                 printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9],
355                        DHCP_OPTION_END);
356
357         assert_se(result->options[9] == DHCP_OPTION_END);
358
359         if (verbose)
360                 printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result->options[10],
361                        DHCP_OPTION_PAD);
362
363         assert_se(result->options[10] == DHCP_OPTION_PAD);
364
365         for (i = 0; i < pos - 8; i++) {
366                 if (verbose)
367                         printf("%2u: 0x%02x(0x%02x) (sname)\n", i, result->sname[i],
368                                options[i + 9]);
369                 assert_se(result->sname[i] == options[i + 9]);
370         }
371
372         if (verbose)
373                 printf ("\n");
374 }
375
376 int main(int argc, char *argv[])
377 {
378         unsigned int i;
379
380         test_invalid_buffer_length();
381         test_message_init();
382
383         test_options(NULL);
384
385         for (i = 0; i < ELEMENTSOF(option_tests); i++)
386                 test_options(&option_tests[i]);
387
388         test_option_set();
389
390         return 0;
391 }