chiark / gitweb /
libsystemd-dhcp: Factor out test code for DHCP message verification
[elogind.git] / src / libsystemd-dhcp / test-dhcp-client.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright (C) 2013 Intel Corporation. All rights reserved.
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdlib.h>
23 #include <assert.h>
24 #include <errno.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <unistd.h>
29
30 #include "util.h"
31 #include "socket-util.h"
32
33 #include "dhcp-protocol.h"
34 #include "dhcp-internal.h"
35 #include "sd-dhcp-client.h"
36
37 static struct ether_addr mac_addr = {
38         .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
39 };
40
41 typedef int (*test_callback_recv_t)(size_t size, DHCPMessage *dhcp);
42
43 static bool verbose = false;
44 static int test_fd[2];
45 static test_callback_recv_t callback_recv;
46
47 static void test_request_basic(sd_event *e)
48 {
49         int r;
50
51         sd_dhcp_client *client;
52
53         if (verbose)
54                 printf("* %s\n", __FUNCTION__);
55
56         r = sd_dhcp_client_new(&client);
57
58         assert(r >= 0);
59         assert(client);
60
61         r = sd_dhcp_client_attach_event(client, e, 0);
62         assert(r >= 0);
63
64         assert(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL);
65         assert(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL);
66         assert(sd_dhcp_client_set_index(NULL, 0) == -EINVAL);
67
68         assert(sd_dhcp_client_set_index(client, 15) == 0);
69         assert(sd_dhcp_client_set_index(client, -42) == -EINVAL);
70         assert(sd_dhcp_client_set_index(client, -1) == 0);
71
72         assert(sd_dhcp_client_set_request_option(client,
73                                         DHCP_OPTION_SUBNET_MASK) == -EEXIST);
74         assert(sd_dhcp_client_set_request_option(client,
75                                         DHCP_OPTION_ROUTER) == -EEXIST);
76         assert(sd_dhcp_client_set_request_option(client,
77                                         DHCP_OPTION_HOST_NAME) == -EEXIST);
78         assert(sd_dhcp_client_set_request_option(client,
79                                         DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
80         assert(sd_dhcp_client_set_request_option(client,
81                                         DHCP_OPTION_DOMAIN_NAME_SERVER)
82                         == -EEXIST);
83         assert(sd_dhcp_client_set_request_option(client,
84                                         DHCP_OPTION_NTP_SERVER) == -EEXIST);
85
86         assert(sd_dhcp_client_set_request_option(client,
87                                         DHCP_OPTION_PAD) == -EINVAL);
88         assert(sd_dhcp_client_set_request_option(client,
89                                         DHCP_OPTION_END) == -EINVAL);
90         assert(sd_dhcp_client_set_request_option(client,
91                                         DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
92         assert(sd_dhcp_client_set_request_option(client,
93                                         DHCP_OPTION_OVERLOAD) == -EINVAL);
94         assert(sd_dhcp_client_set_request_option(client,
95                                         DHCP_OPTION_PARAMETER_REQUEST_LIST)
96                         == -EINVAL);
97
98         assert(sd_dhcp_client_set_request_option(client, 33) == 0);
99         assert(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
100         assert(sd_dhcp_client_set_request_option(client, 44) == 0);
101         assert(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
102 }
103
104 static uint16_t client_checksum(void *buf, int len)
105 {
106         uint32_t sum;
107         uint16_t *check;
108         int i;
109         uint8_t *odd;
110
111         sum = 0;
112         check = buf;
113
114         for (i = 0; i < len / 2 ; i++)
115                 sum += check[i];
116
117         if (len & 0x01) {
118                 odd = buf;
119                 sum += odd[len - 1];
120         }
121
122         while (sum >> 16)
123                 sum = (sum & 0xffff) + (sum >> 16);
124
125         return ~sum;
126 }
127
128 static void test_checksum(void)
129 {
130         uint8_t buf[20] = {
131                 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
132                 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133                 0xff, 0xff, 0xff, 0xff
134         };
135
136         if (verbose)
137                 printf("* %s\n", __FUNCTION__);
138
139         assert(client_checksum(&buf, 20) == be16toh(0x78ae));
140 }
141
142 static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
143                 void *user_data)
144 {
145         return 0;
146 }
147
148 int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
149                                  const void *packet, size_t len)
150 {
151         size_t size;
152         _cleanup_free_ DHCPPacket *discover;
153         uint16_t ip_check, udp_check;
154
155         assert(s >= 0);
156         assert(packet);
157
158         size = sizeof(DHCPPacket) + 4;
159         assert(len > size);
160
161         discover = memdup(packet, len);
162
163         assert(discover->ip.ttl == IPDEFTTL);
164         assert(discover->ip.protocol == IPPROTO_UDP);
165         assert(discover->ip.saddr == INADDR_ANY);
166         assert(discover->ip.daddr == INADDR_BROADCAST);
167         assert(discover->udp.source == be16toh(DHCP_PORT_CLIENT));
168         assert(discover->udp.dest == be16toh(DHCP_PORT_SERVER));
169
170         ip_check = discover->ip.check;
171
172         discover->ip.ttl = 0;
173         discover->ip.check = discover->udp.len;
174
175         udp_check = ~client_checksum(&discover->ip.ttl, len - 8);
176         assert(udp_check == 0xffff);
177
178         discover->ip.ttl = IPDEFTTL;
179         discover->ip.check = ip_check;
180
181         ip_check = ~client_checksum(&discover->ip, sizeof(discover->ip));
182         assert(ip_check == 0xffff);
183
184         assert(discover->dhcp.xid);
185         assert(memcmp(discover->dhcp.chaddr,
186                       &mac_addr.ether_addr_octet, 6) == 0);
187
188         size = len - sizeof(struct iphdr) - sizeof(struct udphdr);
189
190         assert(callback_recv);
191         callback_recv(size, &discover->dhcp);
192
193         return 575;
194 }
195
196 int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
197 {
198         if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
199                 return -errno;
200
201         return test_fd[0];
202 }
203
204 int dhcp_network_bind_udp_socket(int index, be32_t address, uint16_t port)
205 {
206         return 0;
207 }
208
209 int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
210                                  const void *packet, size_t len)
211 {
212         return 0;
213 }
214
215 static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp)
216 {
217         int res;
218
219         res = dhcp_option_parse(dhcp, size, check_options, NULL);
220         assert(res == DHCP_DISCOVER);
221
222         if (verbose)
223                 printf("  recv DHCP Discover 0x%08x\n", be32toh(dhcp->xid));
224
225         return 0;
226 }
227
228 static void test_discover_message(sd_event *e)
229 {
230         sd_dhcp_client *client;
231         int res, r;
232
233         if (verbose)
234                 printf("* %s\n", __FUNCTION__);
235
236         r = sd_dhcp_client_new(&client);
237         assert(r >= 0);
238         assert(client);
239
240         r = sd_dhcp_client_attach_event(client, e, 0);
241         assert(r >= 0);
242
243         assert(sd_dhcp_client_set_index(client, 42) >= 0);
244         assert(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
245
246         assert(sd_dhcp_client_set_request_option(client, 248) >= 0);
247
248         callback_recv = test_discover_message_verify;
249
250         res = sd_dhcp_client_start(client);
251
252         assert(res == 0 || res == -EINPROGRESS);
253
254         sd_event_run(e, (uint64_t) -1);
255
256         sd_dhcp_client_stop(client);
257         sd_dhcp_client_free(client);
258
259         close(test_fd[0]);
260         close(test_fd[1]);
261
262         callback_recv = NULL;
263 }
264
265 int main(int argc, char *argv[])
266 {
267         sd_event *e;
268
269         assert(sd_event_new(&e) >= 0);
270
271         test_request_basic(e);
272         test_checksum();
273
274         test_discover_message(e);
275
276         return 0;
277 }