chiark / gitweb /
dcc5cfe65a3a8813f2237d478af7405a9a8e9c74
[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         uint64_t ifindex;
32         const char *mac;
33         int r;
34
35         assert(device);
36         assert(ret);
37
38         link = new0(Link, 1);
39         if (!link)
40                 return -ENOMEM;
41
42         ifindex = udev_device_get_ifindex(device);
43         if (ifindex <= 0)
44                 return -EINVAL;
45
46         mac = udev_device_get_sysattr_value(device, "address");
47         if (!mac)
48                 return -EINVAL;
49
50         memcpy(&link->mac.ether_addr_octet[0], ether_aton(mac), ETH_ALEN);
51         link->ifindex = ifindex;
52         link->manager = manager;
53         link->state = _LINK_STATE_INVALID;
54
55         r = hashmap_put(manager->links, &ifindex, link);
56         if (r < 0)
57                 return r;
58
59         *ret = link;
60         link = NULL;
61
62         return 0;
63 }
64
65 void link_free(Link *link) {
66         if (!link)
67                 return;
68
69         network_free(link->network);
70
71         hashmap_remove(link->manager->links, link);
72
73         free(link);
74 }
75
76 int link_add(Manager *m, struct udev_device *device) {
77         Link *link;
78         Network *network;
79         int r;
80         uint64_t ifindex;
81
82         assert(m);
83         assert(device);
84
85         ifindex = udev_device_get_ifindex(device);
86         link = hashmap_get(m->links, &ifindex);
87         if (link)
88                 return 0;
89
90         r = link_new(m, device, &link);
91         if (r < 0) {
92                 log_error("could not create link: %s", strerror(-r));
93                 return r;
94         }
95
96         r = network_get(m, device, &network);
97         if (r < 0)
98                 return r == -ENOENT ? 0 : r;
99
100         r = network_apply(m, network, link);
101         if (r < 0)
102                 return r;
103
104         return 0;
105 }
106
107 static int link_enter_configured(Link *link) {
108         log_info("Link configured successfully.");
109
110         link->state = LINK_STATE_CONFIGURED;
111
112         return 0;
113 }
114
115 static int link_enter_failed(Link *link) {
116         log_warning("Could not configure link.");
117
118         link->state = LINK_STATE_FAILED;
119
120         return 0;
121 }
122
123 static bool link_is_up(Link *link) {
124         return link->flags & IFF_UP;
125 }
126
127 static int link_enter_routes_set(Link *link) {
128         log_info("Routes set for link %d", link->ifindex);
129
130         if (link_is_up(link))
131                 return link_enter_configured(link);
132
133         link->state = LINK_STATE_ROUTES_SET;
134
135         return 0;
136 }
137
138 static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
139         Link *link = userdata;
140         int r;
141
142         assert(link->rtnl_messages > 0);
143         assert(link->state == LINK_STATE_SET_ROUTES || link->state == LINK_STATE_FAILED);
144
145         link->rtnl_messages --;
146
147         if (link->state == LINK_STATE_FAILED)
148                 return 1;
149
150         r = sd_rtnl_message_get_errno(m);
151         if (r < 0 && r != -EEXIST) {
152                 log_warning("Could not set route on interface %d: %s",
153                             link->ifindex, strerror(-r));
154                 return link_enter_failed(link);
155         }
156
157         if (link->rtnl_messages == 0)
158                 return link_enter_routes_set(link);
159
160         return 1;
161 }
162
163 static int link_enter_set_routes(Link *link) {
164         Route *route;
165         int r;
166
167         assert(link);
168         assert(link->network);
169         assert(link->rtnl_messages == 0);
170         assert(link->state == LINK_STATE_ADDRESSES_SET);
171
172         link->state = LINK_STATE_SET_ROUTES;
173
174         if (!link->network->routes)
175                 return link_enter_routes_set(link);
176
177         LIST_FOREACH(routes, route, link->network->routes) {
178                 r = route_configure(route, link, &route_handler);
179                 if (r < 0)
180                         link_enter_failed(link);
181         }
182
183         return 0;
184 }
185
186 static int link_enter_addresses_set(Link *link) {
187         log_info("Addresses set for link %d", link->ifindex);
188
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 %d: %s",
209                             link->ifindex, strerror(-r));
210                 link_enter_failed(link);
211         }
212
213         if (link->rtnl_messages == 0)
214                 link_enter_addresses_set(link);
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                         link_enter_failed(link);
236         }
237
238         return 0;
239 }
240
241 static int link_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
242         Link *link = userdata;
243         int r;
244
245         r = sd_rtnl_message_get_errno(m);
246         if (r < 0) {
247                 log_warning("Could not bring up interface %d: %s",
248                             link->ifindex, strerror(-r));
249                 return link_enter_failed(link);
250         }
251
252         link->flags |= IFF_UP;
253
254         log_info("Link is UP.");
255
256         if (link->state == LINK_STATE_ROUTES_SET)
257                 return link_enter_configured(link);
258
259         return 1;
260 }
261
262 static int link_up(Link *link) {
263         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
264         int r;
265
266         assert(link);
267         assert(link->manager);
268         assert(link->manager->rtnl);
269
270         r = sd_rtnl_message_link_new(RTM_NEWLINK, link->ifindex, 0, IFF_UP, &req);
271         if (r < 0) {
272                 log_error("Could not allocate RTM_NEWLINK message");
273                 return r;
274         }
275
276         r = sd_rtnl_call_async(link->manager->rtnl, req, link_handler, link, 0, NULL);
277         if (r < 0) {
278                 log_error("Could not send rtnetlink message: %s", strerror(-r));
279                 return r;
280         }
281
282         return 0;
283 }
284
285 int link_configure(Link *link) {
286         int r;
287
288         r = link_up(link);
289         if (r < 0)
290                 return link_enter_failed(link);
291
292         r = link_enter_set_addresses(link);
293         if (r < 0)
294                 return link_enter_failed(link);
295
296         return 0;
297 }