chiark / gitweb /
8718324c339a0153cab80f86f9d251b74058af83
[elogind.git] / src / libsystemd-network / sd-dhcp6-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) 2014 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 <errno.h>
23 #include <string.h>
24
25 #include "siphash24.h"
26 #include "util.h"
27 #include "refcnt.h"
28
29 #include "sd-dhcp6-client.h"
30 #include "dhcp6-protocol.h"
31
32 #define SYSTEMD_PEN 43793
33 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
34
35 struct sd_dhcp6_client {
36         RefCount n_ref;
37
38         enum DHCP6State state;
39         sd_event *event;
40         int event_priority;
41         int index;
42         struct ether_addr mac_addr;
43         sd_dhcp6_client_cb_t cb;
44         void *userdata;
45
46         struct duid_en {
47                 uint16_t type; /* DHCP6_DUID_EN */
48                 uint32_t pen;
49                 uint8_t id[8];
50         } _packed_ duid;
51 };
52
53 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
54                                  sd_dhcp6_client_cb_t cb, void *userdata)
55 {
56         assert_return(client, -EINVAL);
57
58         client->cb = cb;
59         client->userdata = userdata;
60
61         return 0;
62 }
63
64 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
65 {
66         assert_return(client, -EINVAL);
67         assert_return(interface_index >= -1, -EINVAL);
68
69         client->index = interface_index;
70
71         return 0;
72 }
73
74 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
75                             const struct ether_addr *mac_addr)
76 {
77         assert_return(client, -EINVAL);
78
79         if (mac_addr)
80                 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
81         else
82                 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
83
84         return 0;
85 }
86
87 static sd_dhcp6_client *client_notify(sd_dhcp6_client *client, int event) {
88         if (client->cb) {
89                 client = sd_dhcp6_client_ref(client);
90                 client->cb(client, event, client->userdata);
91                 client = sd_dhcp6_client_unref(client);
92         }
93
94         return client;
95 }
96
97 static int client_initialize(sd_dhcp6_client *client)
98 {
99         assert_return(client, -EINVAL);
100
101         client->state = DHCP6_STATE_STOPPED;
102
103         return 0;
104 }
105
106 static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
107         assert_return(client, NULL);
108
109         client = client_notify(client, error);
110         if (client)
111                 client_initialize(client);
112
113         return client;
114 }
115
116 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
117 {
118         client_stop(client, DHCP6_EVENT_STOP);
119
120         return 0;
121 }
122
123 int sd_dhcp6_client_start(sd_dhcp6_client *client)
124 {
125         int r = 0;
126
127         assert_return(client, -EINVAL);
128         assert_return(client->event, -EINVAL);
129         assert_return(client->index > 0, -EINVAL);
130
131         return r;
132 }
133
134 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
135                                  int priority)
136 {
137         int r;
138
139         assert_return(client, -EINVAL);
140         assert_return(!client->event, -EBUSY);
141
142         if (event)
143                 client->event = sd_event_ref(event);
144         else {
145                 r = sd_event_default(&client->event);
146                 if (r < 0)
147                         return 0;
148         }
149
150         client->event_priority = priority;
151
152         return 0;
153 }
154
155 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
156         assert_return(client, -EINVAL);
157
158         client->event = sd_event_unref(client->event);
159
160         return 0;
161 }
162
163 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
164         if (!client)
165                 return NULL;
166
167         return client->event;
168 }
169
170 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
171         if (client)
172                 assert_se(REFCNT_INC(client->n_ref) >= 2);
173
174         return client;
175 }
176
177 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
178         if (client && REFCNT_DEC(client->n_ref) <= 0) {
179
180                 client_initialize(client);
181
182                 sd_dhcp6_client_detach_event(client);
183
184                 free(client);
185
186                 return NULL;
187         }
188
189         return client;
190 }
191
192 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
193 #define _cleanup_dhcp6_client_free_ _cleanup_(sd_dhcp6_client_unrefp)
194
195 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
196 {
197         _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL;
198         sd_id128_t machine_id;
199         int r;
200
201         assert_return(ret, -EINVAL);
202
203         client = new0(sd_dhcp6_client, 1);
204         if (!client)
205                 return -ENOMEM;
206
207         client->n_ref = REFCNT_INIT;
208
209         client->index = -1;
210
211         /* initialize DUID */
212         client->duid.type = htobe16(DHCP6_DUID_EN);
213         client->duid.pen = htobe32(SYSTEMD_PEN);
214
215         r = sd_id128_get_machine(&machine_id);
216         if (r < 0)
217                 return r;
218
219         /* a bit of snake-oil perhaps, but no need to expose the machine-id
220            directly */
221         siphash24(client->duid.id, &machine_id, sizeof(machine_id),
222                   HASH_KEY.bytes);
223
224         *ret = client;
225         client = NULL;
226
227         return 0;
228 }