]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-dhcp/dhcp-client.c
dhcp: Add test for discover DHCP packet creation
[thirdparty/systemd.git] / src / libsystemd-dhcp / dhcp-client.c
CommitLineData
011feef8
PF
1/***
2 This file is part of systemd.
3
4 Copyright (C) 2013 Intel Corporation. All rights reserved.
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
20#include <stdlib.h>
21#include <errno.h>
22#include <string.h>
23#include <stdio.h>
46a66b79 24#include <net/ethernet.h>
011feef8
PF
25
26#include "util.h"
27#include "list.h"
28
29#include "dhcp-protocol.h"
46a66b79 30#include "dhcp-internal.h"
011feef8
PF
31#include "sd-dhcp-client.h"
32
46a66b79
PF
33#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
34
011feef8
PF
35struct sd_dhcp_client {
36 DHCPState state;
37 int index;
38 uint8_t *req_opts;
39 size_t req_opts_size;
40 uint32_t last_addr;
46a66b79
PF
41 struct ether_addr mac_addr;
42 uint32_t xid;
011feef8
PF
43};
44
45static const uint8_t default_req_opts[] = {
46 DHCP_OPTION_SUBNET_MASK,
47 DHCP_OPTION_ROUTER,
48 DHCP_OPTION_HOST_NAME,
49 DHCP_OPTION_DOMAIN_NAME,
50 DHCP_OPTION_DOMAIN_NAME_SERVER,
51 DHCP_OPTION_NTP_SERVER,
52};
53
54int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option)
55{
56 size_t i;
57
58 assert_return(client, -EINVAL);
59 assert_return (client->state == DHCP_STATE_INIT, -EBUSY);
60
61 switch(option) {
62 case DHCP_OPTION_PAD:
63 case DHCP_OPTION_OVERLOAD:
64 case DHCP_OPTION_MESSAGE_TYPE:
65 case DHCP_OPTION_PARAMETER_REQUEST_LIST:
66 case DHCP_OPTION_END:
67 return -EINVAL;
68
69 default:
70 break;
71 }
72
73 for (i = 0; i < client->req_opts_size; i++)
74 if (client->req_opts[i] == option)
75 return -EEXIST;
76
77 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_size,
78 client->req_opts_size + 1))
79 return -ENOMEM;
80
81 client->req_opts[client->req_opts_size - 1] = option;
82
83 return 0;
84}
85
86int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
87 const struct in_addr *last_addr)
88{
89 assert_return(client, -EINVAL);
90 assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
91
92 if (last_addr)
93 client->last_addr = last_addr->s_addr;
94 else
95 client->last_addr = INADDR_ANY;
96
97 return 0;
98}
99
100int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index)
101{
102 assert_return(client, -EINVAL);
103 assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
104 assert_return(interface_index >= -1, -EINVAL);
105
106 client->index = interface_index;
107
108 return 0;
109}
110
46a66b79
PF
111int sd_dhcp_client_set_mac(sd_dhcp_client *client,
112 const struct ether_addr *addr)
113{
114 assert_return(client, -EINVAL);
115 assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
116
117 memcpy(&client->mac_addr, addr, ETH_ALEN);
118
119 return 0;
120}
121
122static int client_packet_init(sd_dhcp_client *client, uint8_t type,
123 DHCPMessage *message, uint8_t **opt,
124 size_t *optlen)
125{
126 int err;
127
128 *opt = (uint8_t *)(message + 1);
129
130 if (*optlen < 4)
131 return -ENOBUFS;
132 *optlen -= 4;
133
134 message->op = BOOTREQUEST;
135 message->htype = 1;
136 message->hlen = ETHER_ADDR_LEN;
137 message->xid = htobe32(client->xid);
138
139 memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
140 (*opt)[0] = 0x63;
141 (*opt)[1] = 0x82;
142 (*opt)[2] = 0x53;
143 (*opt)[3] = 0x63;
144
145 *opt += 4;
146
147 err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1,
148 &type);
149 if (err < 0)
150 return err;
151
152 /* Some DHCP servers will refuse to issue an DHCP lease if the Cliient
153 Identifier option is not set */
154 err = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
155 ETH_ALEN, &client->mac_addr);
156 if (err < 0)
157 return err;
158
159 if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
160 err = dhcp_option_append(opt, optlen,
161 DHCP_OPTION_PARAMETER_REQUEST_LIST,
162 client->req_opts_size,
163 client->req_opts);
164 if (err < 0)
165 return err;
166 }
167
168 return 0;
169}
170
171static uint16_t client_checksum(void *buf, int len)
172{
173 uint32_t sum;
174 uint16_t *check;
175 int i;
176 uint8_t *odd;
177
178 sum = 0;
179 check = buf;
180
181 for (i = 0; i < len / 2 ; i++)
182 sum += check[i];
183
184 if (len & 0x01) {
185 odd = buf;
186 sum += odd[len];
187 }
188
189 return ~((sum & 0xffff) + (sum >> 16));
190}
191
192static int client_send_discover(sd_dhcp_client *client)
193{
194 int err = 0;
195 _cleanup_free_ DHCPPacket *discover;
196 size_t optlen, len;
197 uint8_t *opt;
198
199 optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE;
200 len = sizeof(DHCPPacket) + optlen;
201
202 discover = malloc0(len);
203
204 if (!discover)
205 return -ENOMEM;
206
207 err = client_packet_init(client, DHCP_DISCOVER, &discover->dhcp,
208 &opt, &optlen);
209 if (err < 0)
210 return err;
211
212 if (client->last_addr != INADDR_ANY) {
213 err = dhcp_option_append(&opt, &optlen,
214 DHCP_OPTION_REQUESTED_IP_ADDRESS,
215 4, &client->last_addr);
216 if (err < 0)
217 return err;
218 }
219
220 err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
221 if (err < 0)
222 return err;
223
224 discover->ip.version = IPVERSION;
225 discover->ip.ihl = sizeof(discover->ip) >> 2;
226 discover->ip.tot_len = htobe16(len);
227
228 discover->ip.protocol = IPPROTO_UDP;
229 discover->ip.saddr = INADDR_ANY;
230 discover->ip.daddr = INADDR_BROADCAST;
231
232 discover->udp.source = htobe16(DHCP_PORT_CLIENT);
233 discover->udp.dest = htobe16(DHCP_PORT_SERVER);
234 discover->udp.len = htobe16(len - sizeof(discover->ip));
235
236 discover->ip.check = discover->udp.len;
237 discover->udp.check = client_checksum(&discover->ip.ttl,
238 len - 8);
239
240 discover->ip.ttl = IPDEFTTL;
241 discover->ip.check = 0;
242 discover->ip.check = client_checksum(&discover->ip,
243 sizeof(discover->ip));
244
245 err = dhcp_network_send_raw_packet(client->index, discover, len);
246
247 return 0;
248}
249
250int sd_dhcp_client_start(sd_dhcp_client *client)
251{
252 assert_return(client, -EINVAL);
253 assert_return(client->index >= 0, -EINVAL);
254 assert_return(client->state == DHCP_STATE_INIT ||
255 client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
256
257 client->xid = random_u();
258
259 return client_send_discover(client);
260}
261
011feef8
PF
262sd_dhcp_client *sd_dhcp_client_new(void)
263{
264 sd_dhcp_client *client;
265
266 client = new0(sd_dhcp_client, 1);
267 if (!client)
268 return NULL;
269
270 client->state = DHCP_STATE_INIT;
271 client->index = -1;
272
273 client->req_opts_size = ELEMENTSOF(default_req_opts);
274
275 client->req_opts = memdup(default_req_opts, client->req_opts_size);
276 if (!client->req_opts) {
277 free(client);
278 return NULL;
279 }
280
281 return client;
282}