chiark / gitweb /
networkd: add link-sense and simplify state-machine a bit
[elogind.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 }