]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-link.c
networkd: distinguish between static and dynamic addresses/routes
[thirdparty/systemd.git] / src / network / networkd-link.c
CommitLineData
f579559b
TG
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
29int link_new(Manager *manager, struct udev_device *device, Link **ret) {
30 _cleanup_link_free_ Link *link = NULL;
8cd11a0f 31 const char *mac;
602cc437 32 struct ether_addr *mac_addr;
c166a070 33 const char *ifname;
f579559b
TG
34 int r;
35
36 assert(device);
37 assert(ret);
38
39 link = new0(Link, 1);
40 if (!link)
41 return -ENOMEM;
42
5a3eb5a7
TG
43 link->manager = manager;
44 link->state = _LINK_STATE_INVALID;
45
0617ffab
TG
46 link->ifindex = udev_device_get_ifindex(device);
47 if (link->ifindex <= 0)
f579559b
TG
48 return -EINVAL;
49
8cd11a0f 50 mac = udev_device_get_sysattr_value(device, "address");
5a3eb5a7
TG
51 if (mac) {
52 mac_addr = ether_aton(mac);
53 if (mac_addr)
54 memcpy(&link->mac, mac_addr, sizeof(struct ether_addr));
55 }
f579559b 56
c166a070
TG
57 ifname = udev_device_get_sysname(device);
58 link->ifname = strdup(ifname);
59
0617ffab 60 r = hashmap_put(manager->links, &link->ifindex, link);
f579559b
TG
61 if (r < 0)
62 return r;
63
64 *ret = link;
65 link = NULL;
66
67 return 0;
68}
69
70void link_free(Link *link) {
71 if (!link)
72 return;
73
0617ffab 74 assert(link->manager);
f579559b 75
0617ffab 76 hashmap_remove(link->manager->links, &link->ifindex);
f579559b 77
c166a070
TG
78 free(link->ifname);
79
f579559b
TG
80 free(link);
81}
82
83int link_add(Manager *m, struct udev_device *device) {
84 Link *link;
85 Network *network;
86 int r;
87 uint64_t ifindex;
02b59d57 88 const char *devtype;
f579559b
TG
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) {
c166a070 100 log_error("Could not create link: %s", strerror(-r));
f579559b
TG
101 return r;
102 }
103
02b59d57
TG
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
f579559b
TG
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
f882c247 122static int link_enter_configured(Link *link) {
ef1ba606
TG
123 assert(link);
124 assert(link->state == LINK_STATE_SETTING_ROUTES);
125
c166a070 126 log_info("Link '%s' configured", link->ifname);
f882c247
TG
127
128 link->state = LINK_STATE_CONFIGURED;
129
130 return 0;
131}
132
ef1ba606
TG
133static void link_enter_failed(Link *link) {
134 assert(link);
f882c247 135
ef1ba606 136 link->state = LINK_STATE_FAILED;
f882c247
TG
137}
138
f882c247
TG
139static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
140 Link *link = userdata;
141 int r;
142
143 assert(link->rtnl_messages > 0);
ef1ba606 144 assert(link->state == LINK_STATE_SETTING_ROUTES || link->state == LINK_STATE_FAILED);
f882c247
TG
145
146 link->rtnl_messages --;
147
148 if (link->state == LINK_STATE_FAILED)
149 return 1;
150
151 r = sd_rtnl_message_get_errno(m);
c166a070
TG
152 if (r < 0 && r != -EEXIST)
153 log_warning("Could not set route on interface '%s': %s",
154 link->ifname, strerror(-r));
f882c247 155
dd3efc09
TG
156 if (link->rtnl_messages == 0) {
157 log_info("Routes set for link '%s'", link->ifname);
158 link_enter_configured(link);
159 }
f882c247
TG
160
161 return 1;
162}
163
164static int link_enter_set_routes(Link *link) {
165 Route *route;
166 int r;
167
168 assert(link);
169 assert(link->network);
170 assert(link->rtnl_messages == 0);
ef1ba606 171 assert(link->state == LINK_STATE_SETTING_ADDRESSES);
f882c247 172
ef1ba606 173 link->state = LINK_STATE_SETTING_ROUTES;
f882c247 174
f048a16b 175 if (!link->network->static_routes)
dd3efc09 176 return link_enter_configured(link);
f882c247 177
f048a16b 178 LIST_FOREACH(static_routes, route, link->network->static_routes) {
f882c247 179 r = route_configure(route, link, &route_handler);
dd3efc09
TG
180 if (r < 0) {
181 log_warning("Could not set routes for link '%s'", link->ifname);
ef1ba606
TG
182 link_enter_failed(link);
183 return r;
dd3efc09 184 }
c166a070
TG
185
186 link->rtnl_messages ++;
f882c247
TG
187 }
188
189 return 0;
190}
191
f882c247
TG
192static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
193 Link *link = userdata;
194 int r;
195
196 assert(link->rtnl_messages > 0);
ef1ba606 197 assert(link->state == LINK_STATE_SETTING_ADDRESSES || link->state == LINK_STATE_FAILED);
f882c247
TG
198
199 link->rtnl_messages --;
200
201 if (link->state == LINK_STATE_FAILED)
202 return 1;
203
204 r = sd_rtnl_message_get_errno(m);
c166a070
TG
205 if (r < 0 && r != -EEXIST)
206 log_warning("Could not set address on interface '%s': %s",
207 link->ifname, strerror(-r));
f882c247 208
dd3efc09
TG
209 if (link->rtnl_messages == 0) {
210 log_info("Addresses set for link '%s'", link->ifname);
ef1ba606 211 link_enter_set_routes(link);
dd3efc09 212 }
f882c247
TG
213
214 return 1;
215}
216
217static int link_enter_set_addresses(Link *link) {
218 Address *address;
219 int r;
220
221 assert(link);
222 assert(link->network);
ef1ba606 223 assert(link->state == LINK_STATE_JOINING_BRIDGE);
f882c247
TG
224 assert(link->rtnl_messages == 0);
225
ef1ba606 226 link->state = LINK_STATE_SETTING_ADDRESSES;
f882c247 227
f048a16b 228 if (!link->network->static_addresses)
ef1ba606 229 return link_enter_set_routes(link);
f882c247 230
f048a16b 231 LIST_FOREACH(static_addresses, address, link->network->static_addresses) {
f882c247 232 r = address_configure(address, link, &address_handler);
dd3efc09
TG
233 if (r < 0) {
234 log_warning("Could not set addresses for link '%s'", link->ifname);
ef1ba606
TG
235 link_enter_failed(link);
236 return r;
dd3efc09 237 }
c166a070
TG
238
239 link->rtnl_messages ++;
f882c247
TG
240 }
241
242 return 0;
243}
244
dd3efc09
TG
245static int link_up_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 }
f882c247
TG
255
256 return 1;
257}
258
259static int link_up(Link *link) {
f579559b
TG
260 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
261 int r;
262
f882c247
TG
263 assert(link);
264 assert(link->manager);
265 assert(link->manager->rtnl);
266
0f49a5f7 267 r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
f579559b 268 if (r < 0) {
0f49a5f7 269 log_error("Could not allocate RTM_SETLINK message");
f579559b
TG
270 return r;
271 }
272
fc25d7f8
TG
273 r = sd_rtnl_message_link_set_flags(req, IFF_UP);
274 if (r < 0) {
275 log_error("Could not set link flags");
276 return r;
277 }
278
dd3efc09 279 r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
f579559b 280 if (r < 0) {
f882c247 281 log_error("Could not send rtnetlink message: %s", strerror(-r));
f579559b
TG
282 return r;
283 }
284
f882c247
TG
285 return 0;
286}
287
ef1ba606 288static int link_bridge_joined(Link *link) {
f882c247
TG
289 int r;
290
ef1ba606
TG
291 assert(link);
292 assert(link->state == LINK_STATE_JOINING_BRIDGE);
dd3efc09 293
f882c247 294 r = link_up(link);
ef1ba606
TG
295 if (r < 0) {
296 link_enter_failed(link);
297 return r;
298 }
f882c247 299
ef1ba606
TG
300 r = link_enter_set_addresses(link);
301 if (r < 0) {
302 link_enter_failed(link);
303 return r;
304 }
305
306 return 0;
02b59d57
TG
307}
308
309static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
310 Link *link = userdata;
311 int r;
312
ef1ba606
TG
313 assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED);
314 assert(link->network);
02b59d57
TG
315
316 if (link->state == LINK_STATE_FAILED)
317 return 1;
318
319 r = sd_rtnl_message_get_errno(m);
ef1ba606 320 if (r < 0) {
dd3efc09
TG
321 log_warning("Could not join interface '%s' to bridge '%s': %s",
322 link->ifname, link->network->bridge->name, strerror(-r));
ef1ba606
TG
323 link_enter_failed(link);
324 return 1;
325 } else
dd3efc09
TG
326 log_info("Join interface '%s' to bridge: %s",
327 link->ifname, link->network->bridge->name);
02b59d57 328
ef1ba606 329 link_bridge_joined(link);
02b59d57
TG
330
331 return 1;
332}
333
334static int link_enter_join_bridge(Link *link) {
335 int r;
336
337 assert(link);
338 assert(link->network);
ef1ba606 339 assert(link->state == _LINK_STATE_INVALID);
02b59d57 340
ef1ba606 341 link->state = LINK_STATE_JOINING_BRIDGE;
02b59d57 342
ef1ba606
TG
343 if (!link->network->bridge)
344 return link_bridge_joined(link);
02b59d57
TG
345
346 r = bridge_join(link->network->bridge, link, &bridge_handler);
dd3efc09
TG
347 if (r < 0) {
348 log_warning("Could not join link '%s' to bridge '%s'", link->ifname,
349 link->network->bridge->name);
ef1ba606
TG
350 link_enter_failed(link);
351 return r;
352 }
353
354 return 0;
355}
356
357static int link_get_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
358 Link *link = userdata;
359 int r;
360
361 r = sd_rtnl_message_get_errno(m);
362 if (r < 0) {
363 log_warning("Could not get state of interface '%s': %s",
364 link->ifname, strerror(-r));
365 link_enter_failed(link);
366 }
367
368 return 1;
369}
370
371static int link_get(Link *link) {
372 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
373 int r;
374
375 assert(link);
376 assert(link->manager);
377 assert(link->manager->rtnl);
378
379 r = sd_rtnl_message_link_new(RTM_GETLINK, link->ifindex, &req);
380 if (r < 0) {
381 log_error("Could not allocate RTM_GETLINK message");
382 return r;
383 }
384
385 r = sd_rtnl_call_async(link->manager->rtnl, req, link_get_handler, link, 0, NULL);
386 if (r < 0) {
387 log_error("Could not send rtnetlink message: %s", strerror(-r));
388 return r;
dd3efc09 389 }
02b59d57
TG
390
391 return 0;
392}
393
394int link_configure(Link *link) {
395 int r;
396
ef1ba606
TG
397 assert(link);
398 assert(link->network);
399 assert(link->state == _LINK_STATE_INVALID);
400
dd3efc09 401 r = link_get(link);
ef1ba606
TG
402 if (r < 0) {
403 link_enter_failed(link);
404 return r;
405 }
dd3efc09 406
02b59d57 407 r = link_enter_join_bridge(link);
f882c247 408 if (r < 0)
ef1ba606 409 return r;
f579559b
TG
410
411 return 0;
412}
dd3efc09 413
22936833
TG
414int link_update(Link *link, sd_rtnl_message *m) {
415 unsigned flags;
416 int r;
417
dd3efc09 418 assert(link);
22936833
TG
419 assert(m);
420
421 r = sd_rtnl_message_link_get_flags(m, &flags);
422 if (r < 0) {
423 log_warning("Could not get link flags of '%s'", link->ifname);
424 return r;
425 }
dd3efc09
TG
426
427 if (link->flags & IFF_UP && !(flags & IFF_UP))
428 log_info("Interface '%s' is down", link->ifname);
429 else if (!(link->flags & IFF_UP) && flags & IFF_UP)
430 log_info("Interface '%s' is up", link->ifname);
431
432 if (link->flags & IFF_LOWER_UP && !(flags & IFF_LOWER_UP))
433 log_info("Interface '%s' is disconnected", link->ifname);
434 else if (!(link->flags & IFF_LOWER_UP) && flags & IFF_LOWER_UP)
435 log_info("Interface '%s' is connected", link->ifname);
436
437 link->flags = flags;
438
439 return 0;
440}