1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 Intel Corporation. All rights reserved.
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.
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.
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/>.
26 #include "udev-util.h"
28 #include "siphash24.h"
32 #include "network-internal.h"
33 #include "sd-dhcp6-client.h"
34 #include "dhcp6-protocol.h"
35 #include "dhcp6-internal.h"
37 #define SYSTEMD_PEN 43793
38 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
40 struct sd_dhcp6_client {
43 enum DHCP6State state;
47 struct ether_addr mac_addr;
49 sd_dhcp6_client_cb_t cb;
53 uint16_t type; /* DHCP6_DUID_EN */
59 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
60 sd_dhcp6_client_cb_t cb, void *userdata)
62 assert_return(client, -EINVAL);
65 client->userdata = userdata;
70 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
72 assert_return(client, -EINVAL);
73 assert_return(interface_index >= -1, -EINVAL);
75 client->index = interface_index;
80 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
81 const struct ether_addr *mac_addr)
83 assert_return(client, -EINVAL);
86 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
88 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
93 static sd_dhcp6_client *client_notify(sd_dhcp6_client *client, int event) {
95 client = sd_dhcp6_client_ref(client);
96 client->cb(client, event, client->userdata);
97 client = sd_dhcp6_client_unref(client);
103 static int client_initialize(sd_dhcp6_client *client)
105 assert_return(client, -EINVAL);
107 client->ia_na.timeout_t1 =
108 sd_event_source_unref(client->ia_na.timeout_t1);
109 client->ia_na.timeout_t2 =
110 sd_event_source_unref(client->ia_na.timeout_t2);
112 client->state = DHCP6_STATE_STOPPED;
117 static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
118 assert_return(client, NULL);
120 client = client_notify(client, error);
122 client_initialize(client);
127 static int client_ensure_iaid(sd_dhcp6_client *client) {
128 const char *name = NULL;
133 if (client->ia_na.id)
136 if (detect_container(NULL) <= 0) {
137 /* not in a container, udev will be around */
138 _cleanup_udev_unref_ struct udev *udev;
139 _cleanup_udev_device_unref_ struct udev_device *device;
140 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
146 sprintf(ifindex_str, "n%d", client->index);
147 device = udev_device_new_from_device_id(udev, ifindex_str);
151 if (udev_device_get_is_initialized(device) <= 0)
155 name = net_get_name(device);
159 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
161 /* fall back to mac address if no predictable name available */
162 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
165 /* fold into 32 bits */
166 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
171 static int client_start(sd_dhcp6_client *client)
175 assert_return(client, -EINVAL);
176 assert_return(client->event, -EINVAL);
177 assert_return(client->index > 0, -EINVAL);
179 r = client_ensure_iaid(client);
186 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
188 client_stop(client, DHCP6_EVENT_STOP);
193 int sd_dhcp6_client_start(sd_dhcp6_client *client)
197 assert_return(client, -EINVAL);
198 assert_return(client->event, -EINVAL);
199 assert_return(client->index > 0, -EINVAL);
201 r = client_initialize(client);
205 return client_start(client);
208 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
213 assert_return(client, -EINVAL);
214 assert_return(!client->event, -EBUSY);
217 client->event = sd_event_ref(event);
219 r = sd_event_default(&client->event);
224 client->event_priority = priority;
229 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
230 assert_return(client, -EINVAL);
232 client->event = sd_event_unref(client->event);
237 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
241 return client->event;
244 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
246 assert_se(REFCNT_INC(client->n_ref) >= 2);
251 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
252 if (client && REFCNT_DEC(client->n_ref) <= 0) {
253 client_initialize(client);
255 sd_dhcp6_client_detach_event(client);
265 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
266 #define _cleanup_dhcp6_client_free_ _cleanup_(sd_dhcp6_client_unrefp)
268 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
270 _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL;
271 sd_id128_t machine_id;
274 assert_return(ret, -EINVAL);
276 client = new0(sd_dhcp6_client, 1);
280 client->n_ref = REFCNT_INIT;
282 client->ia_na.type = DHCP6_OPTION_IA_NA;
286 /* initialize DUID */
287 client->duid.type = htobe16(DHCP6_DUID_EN);
288 client->duid.pen = htobe32(SYSTEMD_PEN);
290 r = sd_id128_get_machine(&machine_id);
294 /* a bit of snake-oil perhaps, but no need to expose the machine-id
296 siphash24(client->duid.id, &machine_id, sizeof(machine_id),