chiark / gitweb /
libsystemd-dhcp: Fix checksum computation for buffer with odd size
[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 static int test_fd[2];
42
43 static void test_request_basic(sd_event *e)
44 {
45         sd_dhcp_client *client;
46
47         client = sd_dhcp_client_new(e);
48
49         assert(client);
50
51         assert(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL);
52         assert(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL);
53         assert(sd_dhcp_client_set_index(NULL, 0) == -EINVAL);
54
55         assert(sd_dhcp_client_set_index(client, 15) == 0);
56         assert(sd_dhcp_client_set_index(client, -42) == -EINVAL);
57         assert(sd_dhcp_client_set_index(client, -1) == 0);
58
59         assert(sd_dhcp_client_set_request_option(client,
60                                         DHCP_OPTION_SUBNET_MASK) == -EEXIST);
61         assert(sd_dhcp_client_set_request_option(client,
62                                         DHCP_OPTION_ROUTER) == -EEXIST);
63         assert(sd_dhcp_client_set_request_option(client,
64                                         DHCP_OPTION_HOST_NAME) == -EEXIST);
65         assert(sd_dhcp_client_set_request_option(client,
66                                         DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
67         assert(sd_dhcp_client_set_request_option(client,
68                                         DHCP_OPTION_DOMAIN_NAME_SERVER)
69                         == -EEXIST);
70         assert(sd_dhcp_client_set_request_option(client,
71                                         DHCP_OPTION_NTP_SERVER) == -EEXIST);
72
73         assert(sd_dhcp_client_set_request_option(client,
74                                         DHCP_OPTION_PAD) == -EINVAL);
75         assert(sd_dhcp_client_set_request_option(client,
76                                         DHCP_OPTION_END) == -EINVAL);
77         assert(sd_dhcp_client_set_request_option(client,
78                                         DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
79         assert(sd_dhcp_client_set_request_option(client,
80                                         DHCP_OPTION_OVERLOAD) == -EINVAL);
81         assert(sd_dhcp_client_set_request_option(client,
82                                         DHCP_OPTION_PARAMETER_REQUEST_LIST)
83                         == -EINVAL);
84
85         assert(sd_dhcp_client_set_request_option(client, 33) == 0);
86         assert(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
87         assert(sd_dhcp_client_set_request_option(client, 44) == 0);
88 }
89
90 static uint16_t client_checksum(void *buf, int len)
91 {
92         uint32_t sum;
93         uint16_t *check;
94         int i;
95         uint8_t *odd;
96
97         sum = 0;
98         check = buf;
99
100         for (i = 0; i < len / 2 ; i++)
101                 sum += check[i];
102
103         if (len & 0x01) {
104                 odd = buf;
105                 sum += odd[len - 1];
106         }
107
108         while (sum >> 16)
109                 sum = (sum & 0xffff) + (sum >> 16);
110
111         return ~sum;
112 }
113
114 static void test_checksum(void)
115 {
116         uint8_t buf[20] = {
117                 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
118                 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119                 0xff, 0xff, 0xff, 0xff
120         };
121
122         uint8_t check[2] = {
123                 0x78, 0xae
124         };
125
126         uint16_t *val = (uint16_t *)check;
127
128         assert(client_checksum(&buf, 20) == *val);
129 }
130
131 static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
132                 void *user_data)
133 {
134         return 0;
135 }
136
137 int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
138                                  const void *packet, size_t len)
139 {
140         size_t size;
141         _cleanup_free_ DHCPPacket *discover;
142         uint16_t ip_check, udp_check;
143         int res;
144
145         assert(s >= 0);
146         assert(packet);
147
148         size = sizeof(DHCPPacket) + 4;
149         assert(len > size);
150
151         discover = memdup(packet, len);
152
153         assert(memcmp(discover->dhcp.chaddr,
154                       &mac_addr.ether_addr_octet, 6) == 0);
155         assert(discover->ip.ttl == IPDEFTTL);
156         assert(discover->ip.protocol == IPPROTO_UDP);
157         assert(discover->ip.saddr == INADDR_ANY);
158         assert(discover->ip.daddr == INADDR_BROADCAST);
159         assert(discover->udp.source == be16toh(DHCP_PORT_CLIENT));
160         assert(discover->udp.dest == be16toh(DHCP_PORT_SERVER));
161
162         ip_check = discover->ip.check;
163
164         discover->ip.ttl = 0;
165         discover->ip.check = discover->udp.len;
166
167         udp_check = ~client_checksum(&discover->ip.ttl, len - 8);
168         assert(udp_check == 0xffff);
169
170         discover->ip.ttl = IPDEFTTL;
171         discover->ip.check = ip_check;
172
173         ip_check = ~client_checksum(&discover->ip, sizeof(discover->ip));
174         assert(ip_check == 0xffff);
175
176         size = len - sizeof(struct iphdr) - sizeof(struct udphdr);
177
178         res = dhcp_option_parse(&discover->dhcp, size, check_options, NULL);
179         if (res < 0)
180                 return res;
181
182         return 575;
183 }
184
185 int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
186 {
187         if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
188                 return -errno;
189
190         return test_fd[0];
191 }
192
193 static void test_discover_message(sd_event *e)
194 {
195         sd_dhcp_client *client;
196         int res;
197
198         client = sd_dhcp_client_new(e);
199         assert(client);
200
201         assert(sd_dhcp_client_set_index(client, 42) >= 0);
202         assert(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
203
204         assert(sd_dhcp_client_set_request_option(client, 248) >= 0);
205
206         res = sd_dhcp_client_start(client);
207
208         assert(res == 0 || res == -EINPROGRESS);
209
210         close(test_fd[0]);
211         close(test_fd[1]);
212 }
213
214 int main(int argc, char *argv[])
215 {
216         sd_event *e;
217
218         assert(sd_event_new(&e) >= 0);
219
220         test_request_basic(e);
221         test_checksum();
222
223         test_discover_message(e);
224         sd_event_run(e, (uint64_t) -1);
225
226         return 0;
227 }