]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-link.c
networkd: add link-sense and simplify state-machine a bit
[thirdparty/systemd.git] / src / network / networkd-link.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
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 <netinet/ether.h>
23 #include <linux/if.h>
24
25 #include "networkd.h"
26 #include "libudev-private.h"
27 #include "util.h"
28
29 int link_new(Manager *manager, struct udev_device *device, Link **ret) {
30 _cleanup_link_free_ Link *link = NULL;
31 const char *mac;
32 struct ether_addr *mac_addr;
33 const char *ifname;
34 int r;
35
36 assert(device);
37 assert(ret);
38
39 link = new0(Link, 1);
40 if (!link)
41 return -ENOMEM;
42
43 link->manager = manager;
44 link->state = _LINK_STATE_INVALID;
45
46 link->ifindex = udev_device_get_ifindex(device);
47 if (link->ifindex <= 0)
48 return -EINVAL;
49
50 mac = udev_device_get_sysattr_value(device, "address");
51 if (mac) {
52 mac_addr = ether_aton(mac);
53 if (mac_addr)
54 memcpy(&link->mac, mac_addr, sizeof(struct ether_addr));
55 }
56
57 ifname = udev_device_get_sysname(device);
58 link->ifname = strdup(ifname);
59
60 r = hashmap_put(manager->links, &link->ifindex, link);
61 if (r < 0)
62 return r;
63
64 *ret = link;
65 link = NULL;
66
67 return 0;
68 }
69
70 void link_free(Link *link) {
71 if (!link)
72 return;
73
74 assert(link->manager);
75
76 hashmap_remove(link->manager->links, &link->ifindex);
77
78 free(link->ifname);
79
80 free(link);
81 }
82
83 int link_add(Manager *m, struct udev_device *device) {
84 Link *link;
85 Network *network;
86 int r;
87 uint64_t ifindex;
88 const char *devtype;
89
90 assert(m);
91 assert(device);
92
93 ifindex = udev_device_get_ifindex(device);
94 link = hashmap_get(m->links, &ifindex);
95 if (link)
96 return 0;
97
98 r = link_new(m, device, &link);
99 if (r < 0) {
100 log_error("Could not create link: %s", strerror(-r));
101 return r;
102 }
103
104 devtype = udev_device_get_devtype(device);
105 if (streq_ptr(devtype, "bridge")) {
106 r = bridge_set_link(m, link);
107 if (r < 0)
108 return r == -ENOENT ? 0 : r;
109 }
110
111 r = network_get(m, device, &network);
112 if (r < 0)
113 return r == -ENOENT ? 0 : r;
114
115 r = network_apply(m, network, link);
116 if (r < 0)
117 return r;
118
119 return 0;
120 }
121
122 static int link_enter_configured(Link *link) {
123 log_info("Link '%s' configured", link->ifname);
124
125 link->state = LINK_STATE_CONFIGURED;
126
127 return 0;
128 }
129
130 static int link_enter_failed(Link *link) {
131 link->state = LINK_STATE_FAILED;
132
133 return 0;
134 }
135
136 static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
137 Link *link = userdata;
138 int r;
139
140 assert(link->rtnl_messages > 0);
141 assert(link->state == LINK_STATE_SET_ROUTES || link->state == LINK_STATE_FAILED);
142
143 link->rtnl_messages --;
144
145 if (link->state == LINK_STATE_FAILED)
146 return 1;
147
148 r = sd_rtnl_message_get_errno(m);
149 if (r < 0 && r != -EEXIST)
150 log_warning("Could not set route on interface '%s': %s",
151 link->ifname, strerror(-r));
152
153 if (link->rtnl_messages == 0) {
154 log_info("Routes set for link '%s'", link->ifname);
155 link_enter_configured(link);
156 }
157
158 return 1;
159 }
160
161 static int link_enter_set_routes(Link *link) {
162 Route *route;
163 int r;
164
165 assert(link);
166 assert(link->network);
167 assert(link->rtnl_messages == 0);
168 assert(link->state == LINK_STATE_ADDRESSES_SET);
169
170 link->state = LINK_STATE_SET_ROUTES;
171
172 if (!link->network->routes)
173 return link_enter_configured(link);
174
175 LIST_FOREACH(routes, route, link->network->routes) {
176 r = route_configure(route, link, &route_handler);
177 if (r < 0) {
178 log_warning("Could not set routes for link '%s'", link->ifname);
179 return link_enter_failed(link);
180 }
181
182 link->rtnl_messages ++;
183 }
184
185 return 0;
186 }
187
188 static int link_enter_addresses_set(Link *link) {
189 link->state = LINK_STATE_ADDRESSES_SET;
190
191 return link_enter_set_routes(link);
192 }
193
194 static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
195 Link *link = userdata;
196 int r;
197
198 assert(link->rtnl_messages > 0);
199 assert(link->state == LINK_STATE_SET_ADDRESSES || link->state == LINK_STATE_FAILED);
200
201 link->rtnl_messages --;
202
203 if (link->state == LINK_STATE_FAILED)
204 return 1;
205
206 r = sd_rtnl_message_get_errno(m);
207 if (r < 0 && r != -EEXIST)
208 log_warning("Could not set address on interface '%s': %s",
209 link->ifname, strerror(-r));
210
211 if (link->rtnl_messages == 0) {
212 log_info("Addresses set for link '%s'", link->ifname);
213 link_enter_addresses_set(link);
214 }
215
216 return 1;
217 }
218
219 static int link_enter_set_addresses(Link *link) {
220 Address *address;
221 int r;
222
223 assert(link);
224 assert(link->network);
225 assert(link->rtnl_messages == 0);
226
227 if (!link->network->addresses)
228 return link_enter_addresses_set(link);
229
230 link->state = LINK_STATE_SET_ADDRESSES;
231
232 LIST_FOREACH(addresses, address, link->network->addresses) {
233 r = address_configure(address, link, &address_handler);
234 if (r < 0) {
235 log_warning("Could not set addresses for link '%s'", link->ifname);
236 return link_enter_failed(link);
237 }
238
239 link->rtnl_messages ++;
240 }
241
242 return 0;
243 }
244
245 static int link_get_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
246 Link *link = userdata;
247 int r;
248
249 r = sd_rtnl_message_get_errno(m);
250 if (r < 0) {
251 log_warning("Could not bring up interface '%s': %s",
252 link->ifname, strerror(-r));
253 link_enter_failed(link);
254 }
255
256 return 1;
257 }
258
259 static int link_get(Link *link) {
260 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
261 int r;
262
263 assert(link);
264 assert(link->manager);
265 assert(link->manager->rtnl);
266
267 r = sd_rtnl_message_link_new(RTM_GETLINK, link->ifindex, 0, 0, &req);
268 if (r < 0) {
269 log_error("Could not allocate RTM_GETLINK message");
270 return r;
271 }
272
273 r = sd_rtnl_call_async(link->manager->rtnl, req, link_get_handler, link, 0, NULL);
274 if (r < 0) {
275 log_error("Could not send rtnetlink message: %s", strerror(-r));
276 return r;
277 }
278
279 return 0;
280 }
281
282 static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
283 Link *link = userdata;
284 int r;
285
286 r = sd_rtnl_message_get_errno(m);
287 if (r < 0) {
288 log_warning("Could not bring up interface '%s': %s",
289 link->ifname, strerror(-r));
290 link_enter_failed(link);
291 }
292
293 return 1;
294 }
295
296 static int link_up(Link *link) {
297 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
298 int r;
299
300 assert(link);
301 assert(link->manager);
302 assert(link->manager->rtnl);
303
304 r = sd_rtnl_message_link_new(RTM_NEWLINK, link->ifindex, 0, IFF_UP, &req);
305 if (r < 0) {
306 log_error("Could not allocate RTM_NEWLINK message");
307 return r;
308 }
309
310 r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
311 if (r < 0) {
312 log_error("Could not send rtnetlink message: %s", strerror(-r));
313 return r;
314 }
315
316 return 0;
317 }
318
319 static int link_enter_bridge_joined(Link *link) {
320 int r;
321
322 link->state = LINK_STATE_BRIDGE_JOINED;
323
324 r = link_up(link);
325 if (r < 0)
326 return link_enter_failed(link);
327
328 return link_enter_set_addresses(link);
329 }
330
331 static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
332 Link *link = userdata;
333 int r;
334
335 assert(link->state == LINK_STATE_JOIN_BRIDGE || link->state == LINK_STATE_FAILED);
336
337 if (link->state == LINK_STATE_FAILED)
338 return 1;
339
340 r = sd_rtnl_message_get_errno(m);
341 if (r < 0)
342 log_warning("Could not join interface '%s' to bridge '%s': %s",
343 link->ifname, link->network->bridge->name, strerror(-r));
344 else
345 log_info("Join interface '%s' to bridge: %s",
346 link->ifname, link->network->bridge->name);
347
348 link_enter_bridge_joined(link);
349
350 return 1;
351 }
352
353 static int link_enter_join_bridge(Link *link) {
354 int r;
355
356 assert(link);
357 assert(link->network);
358
359 if (!link->network->bridge)
360 return link_enter_bridge_joined(link);
361
362 link->state = LINK_STATE_JOIN_BRIDGE;
363
364 r = bridge_join(link->network->bridge, link, &bridge_handler);
365 if (r < 0) {
366 log_warning("Could not join link '%s' to bridge '%s'", link->ifname,
367 link->network->bridge->name);
368 return link_enter_failed(link);
369 }
370
371 return 0;
372 }
373
374 int link_configure(Link *link) {
375 int r;
376
377 r = link_get(link);
378 if (r < 0)
379 return link_enter_failed(link);
380
381 r = link_enter_join_bridge(link);
382 if (r < 0)
383 return link_enter_failed(link);
384
385 return 0;
386 }
387
388 int link_update_flags(Link *link, unsigned flags) {
389 assert(link);
390
391 if (link->flags & IFF_UP && !(flags & IFF_UP))
392 log_info("Interface '%s' is down", link->ifname);
393 else if (!(link->flags & IFF_UP) && flags & IFF_UP)
394 log_info("Interface '%s' is up", link->ifname);
395
396 if (link->flags & IFF_LOWER_UP && !(flags & IFF_LOWER_UP))
397 log_info("Interface '%s' is disconnected", link->ifname);
398 else if (!(link->flags & IFF_LOWER_UP) && flags & IFF_LOWER_UP)
399 log_info("Interface '%s' is connected", link->ifname);
400
401 link->flags = flags;
402
403 return 0;
404 }