chiark / gitweb /
networkd: minor fixes
[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
89         assert(m);
90         assert(device);
91
92         ifindex = udev_device_get_ifindex(device);
93         link = hashmap_get(m->links, &ifindex);
94         if (link)
95                 return 0;
96
97         r = link_new(m, device, &link);
98         if (r < 0) {
99                 log_error("Could not create link: %s", strerror(-r));
100                 return r;
101         }
102
103         r = network_get(m, device, &network);
104         if (r < 0)
105                 return r == -ENOENT ? 0 : r;
106
107         r = network_apply(m, network, link);
108         if (r < 0)
109                 return r;
110
111         return 0;
112 }
113
114 static int link_enter_configured(Link *link) {
115         log_info("Link '%s' configured", link->ifname);
116
117         link->state = LINK_STATE_CONFIGURED;
118
119         return 0;
120 }
121
122 static int link_enter_failed(Link *link) {
123         log_warning("Could not configure link '%s'", link->ifname);
124
125         link->state = LINK_STATE_FAILED;
126
127         return 0;
128 }
129
130 static bool link_is_up(Link *link) {
131         return link->flags & IFF_UP;
132 }
133
134 static int link_enter_routes_set(Link *link) {
135         log_info("Routes set for link '%s'", link->ifname);
136
137         if (link_is_up(link))
138                 return link_enter_configured(link);
139
140         link->state = LINK_STATE_ROUTES_SET;
141
142         return 0;
143 }
144
145 static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
146         Link *link = userdata;
147         int r;
148
149         assert(link->rtnl_messages > 0);
150         assert(link->state == LINK_STATE_SET_ROUTES || link->state == LINK_STATE_FAILED);
151
152         link->rtnl_messages --;
153
154         if (link->state == LINK_STATE_FAILED)
155                 return 1;
156
157         r = sd_rtnl_message_get_errno(m);
158         if (r < 0 && r != -EEXIST)
159                 log_warning("Could not set route on interface '%s': %s",
160                             link->ifname, strerror(-r));
161
162         if (link->rtnl_messages == 0)
163                 return link_enter_routes_set(link);
164
165         return 1;
166 }
167
168 static int link_enter_set_routes(Link *link) {
169         Route *route;
170         int r;
171
172         assert(link);
173         assert(link->network);
174         assert(link->rtnl_messages == 0);
175         assert(link->state == LINK_STATE_ADDRESSES_SET);
176
177         link->state = LINK_STATE_SET_ROUTES;
178
179         if (!link->network->routes)
180                 return link_enter_routes_set(link);
181
182         LIST_FOREACH(routes, route, link->network->routes) {
183                 r = route_configure(route, link, &route_handler);
184                 if (r < 0)
185                         return link_enter_failed(link);
186
187                 link->rtnl_messages ++;
188         }
189
190         return 0;
191 }
192
193 static int link_enter_addresses_set(Link *link) {
194         log_info("Addresses set for link '%s'", link->ifname);
195
196         link->state = LINK_STATE_ADDRESSES_SET;
197
198         return link_enter_set_routes(link);
199 }
200
201 static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
202         Link *link = userdata;
203         int r;
204
205         assert(link->rtnl_messages > 0);
206         assert(link->state == LINK_STATE_SET_ADDRESSES || link->state == LINK_STATE_FAILED);
207
208         link->rtnl_messages --;
209
210         if (link->state == LINK_STATE_FAILED)
211                 return 1;
212
213         r = sd_rtnl_message_get_errno(m);
214         if (r < 0 && r != -EEXIST)
215                 log_warning("Could not set address on interface '%s': %s",
216                             link->ifname, strerror(-r));
217
218         if (link->rtnl_messages == 0)
219                 link_enter_addresses_set(link);
220
221         return 1;
222 }
223
224 static int link_enter_set_addresses(Link *link) {
225         Address *address;
226         int r;
227
228         assert(link);
229         assert(link->network);
230         assert(link->rtnl_messages == 0);
231
232         if (!link->network->addresses)
233                 return link_enter_addresses_set(link);
234
235         link->state = LINK_STATE_SET_ADDRESSES;
236
237         LIST_FOREACH(addresses, address, link->network->addresses) {
238                 r = address_configure(address, link, &address_handler);
239                 if (r < 0)
240                         return link_enter_failed(link);
241
242                 link->rtnl_messages ++;
243         }
244
245         return 0;
246 }
247
248 static int link_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
249         Link *link = userdata;
250         int r;
251
252         r = sd_rtnl_message_get_errno(m);
253         if (r < 0)
254                 log_warning("Could not bring up interface '%s': %s",
255                             link->ifname, strerror(-r));
256
257         link->flags |= IFF_UP;
258
259         log_info("Link '%s' is up", link->ifname);
260
261         if (link->state == LINK_STATE_ROUTES_SET)
262                 return link_enter_configured(link);
263
264         return 1;
265 }
266
267 static int link_up(Link *link) {
268         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
269         int r;
270
271         assert(link);
272         assert(link->manager);
273         assert(link->manager->rtnl);
274
275         r = sd_rtnl_message_link_new(RTM_NEWLINK, link->ifindex, 0, IFF_UP, &req);
276         if (r < 0) {
277                 log_error("Could not allocate RTM_NEWLINK message");
278                 return r;
279         }
280
281         r = sd_rtnl_call_async(link->manager->rtnl, req, link_handler, link, 0, NULL);
282         if (r < 0) {
283                 log_error("Could not send rtnetlink message: %s", strerror(-r));
284                 return r;
285         }
286
287         return 0;
288 }
289
290 int link_configure(Link *link) {
291         int r;
292
293         r = link_up(link);
294         if (r < 0)
295                 return link_enter_failed(link);
296
297         r = link_enter_set_addresses(link);
298         if (r < 0)
299                 return link_enter_failed(link);
300
301         return 0;
302 }