]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-netdev.c
networkd: handle SIGINT and SIGTERM
[thirdparty/systemd.git] / src / network / networkd-netdev.c
CommitLineData
02b59d57
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 "networkd.h"
23#include "net-util.h"
24#include "path-util.h"
25#include "conf-files.h"
26#include "conf-parser.h"
27#include "list.h"
28
672682a6
TG
29#define VLANID_MAX 4094
30
52433f6b
TG
31static const char* const netdev_kind_table[] = {
32 [NETDEV_KIND_BRIDGE] = "bridge",
54abf461
TG
33 [NETDEV_KIND_BOND] = "bond",
34 [NETDEV_KIND_VLAN] = "vlan",
52433f6b 35};
02b59d57 36
1a436809
TG
37DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
38DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
52433f6b 39
1a436809 40void netdev_free(NetDev *netdev) {
52433f6b
TG
41 netdev_enslave_callback *callback;
42
43 if (!netdev)
02b59d57
TG
44 return;
45
52433f6b
TG
46 while ((callback = netdev->callbacks)) {
47 LIST_REMOVE(callbacks, netdev->callbacks, callback);
02b59d57
TG
48 free(callback);
49 }
50
52433f6b
TG
51 if (netdev->name)
52 hashmap_remove(netdev->manager->netdevs, netdev->name);
02b59d57 53
52433f6b 54 free(netdev->filename);
02b59d57 55
52433f6b
TG
56 free(netdev->description);
57 free(netdev->name);
02b59d57 58
52433f6b 59 free(netdev);
02b59d57
TG
60}
61
1a436809
TG
62int netdev_get(Manager *manager, const char *name, NetDev **ret) {
63 NetDev *netdev;
02b59d57
TG
64
65 assert(manager);
66 assert(name);
67 assert(ret);
68
52433f6b
TG
69 netdev = hashmap_get(manager->netdevs, name);
70 if (!netdev) {
02b59d57
TG
71 *ret = NULL;
72 return -ENOENT;
73 }
74
52433f6b 75 *ret = netdev;
02b59d57
TG
76
77 return 0;
78}
79
1a436809 80static int netdev_enter_failed(NetDev *netdev) {
52433f6b 81 netdev->state = NETDEV_STATE_FAILED;
02b59d57
TG
82
83 return 0;
84}
85
1a436809 86static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
cf6a8911 87 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
02b59d57
TG
88 int r;
89
52433f6b
TG
90 assert(netdev);
91 assert(netdev->state == NETDEV_STATE_READY);
4fb7242c
TG
92 assert(netdev->manager);
93 assert(netdev->manager->rtnl);
02b59d57
TG
94 assert(link);
95 assert(callback);
96
151b9b96
LP
97 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
98 RTM_SETLINK, link->ifindex);
02b59d57 99 if (r < 0) {
52433f6b 100 log_error_netdev(netdev,
3333d748
ZJS
101 "Could not allocate RTM_SETLINK message: %s",
102 strerror(-r));
02b59d57
TG
103 return r;
104 }
105
50add290 106 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
02b59d57 107 if (r < 0) {
52433f6b 108 log_error_netdev(netdev,
3333d748
ZJS
109 "Could not append IFLA_MASTER attribute: %s",
110 strerror(-r));
02b59d57
TG
111 return r;
112 }
113
52433f6b 114 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
02b59d57 115 if (r < 0) {
52433f6b 116 log_error_netdev(netdev,
3333d748
ZJS
117 "Could not send rtnetlink message: %s",
118 strerror(-r));
02b59d57
TG
119 return r;
120 }
121
52433f6b 122 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
ab47d620 123
02b59d57
TG
124 return 0;
125}
126
1a436809 127static int netdev_enter_ready(NetDev *netdev) {
52433f6b 128 netdev_enslave_callback *callback;
02b59d57 129
52433f6b
TG
130 assert(netdev);
131 assert(netdev->name);
924fe430 132
52433f6b 133 netdev->state = NETDEV_STATE_READY;
02b59d57 134
52433f6b 135 log_info_netdev(netdev, "netdev ready");
02b59d57 136
52433f6b
TG
137 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
138 /* enslave the links that were attempted to be enslaved befor the
02b59d57 139 * link was ready */
52433f6b 140 netdev_enslave_ready(netdev, callback->link, callback->callback);
02b59d57
TG
141 }
142
143 return 0;
144}
145
52433f6b 146static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
1a436809 147 NetDev *netdev = userdata;
172f6635 148 int r;
02b59d57 149
52433f6b 150 assert(netdev->state != _NETDEV_STATE_INVALID);
02b59d57
TG
151
152 r = sd_rtnl_message_get_errno(m);
153 if (r < 0) {
52433f6b
TG
154 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
155 netdev_enter_failed(netdev);
dd3efc09
TG
156
157 return 1;
02b59d57
TG
158 }
159
dd3efc09 160 return 1;
02b59d57
TG
161}
162
1a436809 163static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
cf6a8911 164 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
52433f6b 165 const char *kind;
02b59d57
TG
166 int r;
167
52433f6b 168 assert(netdev);
4fb7242c
TG
169 assert(!(netdev->kind == NETDEV_KIND_VLAN) ||
170 (link && callback && netdev->vlanid <= VLANID_MAX));
52433f6b
TG
171 assert(netdev->name);
172 assert(netdev->manager);
173 assert(netdev->manager->rtnl);
02b59d57 174
151b9b96 175 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
02b59d57 176 if (r < 0) {
52433f6b 177 log_error_netdev(netdev,
3333d748
ZJS
178 "Could not allocate RTM_NEWLINK message: %s",
179 strerror(-r));
02b59d57
TG
180 return r;
181 }
182
54abf461
TG
183 if (link) {
184 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
185 if (r < 0) {
186 log_error_netdev(netdev,
187 "Could not append IFLA_LINK attribute: %s",
188 strerror(-r));
189 return r;
190 }
191 }
192
52433f6b 193 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
02b59d57 194 if (r < 0) {
52433f6b 195 log_error_netdev(netdev,
3333d748
ZJS
196 "Could not append IFLA_IFNAME attribute: %s",
197 strerror(-r));
02b59d57
TG
198 return r;
199 }
200
ee3a6a51 201 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
02b59d57 202 if (r < 0) {
52433f6b 203 log_error_netdev(netdev,
3333d748
ZJS
204 "Could not open IFLA_LINKINFO container: %s",
205 strerror(-r));
02b59d57
TG
206 return r;
207 }
208
52433f6b
TG
209 kind = netdev_kind_to_string(netdev->kind);
210 if (!kind) {
211 log_error_netdev(netdev, "Invalid kind");
212 return -EINVAL;
213 }
214
215 r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind);
02b59d57 216 if (r < 0) {
52433f6b 217 log_error_netdev(netdev,
3333d748
ZJS
218 "Could not append IFLA_INFO_KIND attribute: %s",
219 strerror(-r));
02b59d57
TG
220 return r;
221 }
222
672682a6 223 if (netdev->vlanid <= VLANID_MAX) {
ee3a6a51 224 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA);
54abf461
TG
225 if (r < 0) {
226 log_error_netdev(netdev,
227 "Could not open IFLA_INFO_DATA container: %s",
228 strerror(-r));
229 return r;
230 }
231
232 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
233 if (r < 0) {
234 log_error_netdev(netdev,
235 "Could not append IFLA_VLAN_ID attribute: %s",
236 strerror(-r));
237 return r;
238 }
239
240 r = sd_rtnl_message_close_container(req);
241 if (r < 0) {
242 log_error_netdev(netdev,
243 "Could not close IFLA_INFO_DATA container %s",
244 strerror(-r));
245 return r;
246 }
247 }
248
02b59d57
TG
249 r = sd_rtnl_message_close_container(req);
250 if (r < 0) {
52433f6b 251 log_error_netdev(netdev,
3333d748
ZJS
252 "Could not close IFLA_LINKINFO container %s",
253 strerror(-r));
02b59d57
TG
254 return r;
255 }
256
54abf461
TG
257 if (link)
258 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
259 else
260 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
02b59d57 261 if (r < 0) {
52433f6b 262 log_error_netdev(netdev,
3333d748 263 "Could not send rtnetlink message: %s", strerror(-r));
02b59d57
TG
264 return r;
265 }
266
52433f6b 267 log_debug_netdev(netdev, "creating netdev");
02b59d57 268
52433f6b 269 netdev->state = NETDEV_STATE_CREATING;
02b59d57
TG
270
271 return 0;
272}
273
1a436809 274int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
54abf461
TG
275 if (netdev->kind == NETDEV_KIND_VLAN)
276 return netdev_create(netdev, link, callback);
277
52433f6b
TG
278 if (netdev->state == NETDEV_STATE_READY) {
279 netdev_enslave_ready(netdev, link, callback);
02b59d57 280 } else {
52433f6b
TG
281 /* the netdev is not yet read, save this request for when it is*/
282 netdev_enslave_callback *cb;
02b59d57 283
52433f6b 284 cb = new0(netdev_enslave_callback, 1);
02b59d57
TG
285 if (!cb)
286 return log_oom();
287
288 cb->callback = callback;
289 cb->link = link;
290
52433f6b 291 LIST_PREPEND(callbacks, netdev->callbacks, cb);
02b59d57
TG
292 }
293
294 return 0;
295}
296
1a436809 297int netdev_set_ifindex(NetDev *netdev, int ifindex) {
50add290
TG
298 assert(netdev);
299 assert(ifindex > 0);
02b59d57 300
50add290
TG
301 if (netdev->ifindex > 0) {
302 if (netdev->ifindex == ifindex)
303 return 0;
304 else
305 return -EEXIST;
306 }
02b59d57 307
50add290 308 netdev->ifindex = ifindex;
52433f6b
TG
309
310 netdev_enter_ready(netdev);
02b59d57
TG
311
312 return 0;
313}
314
52433f6b 315static int netdev_load_one(Manager *manager, const char *filename) {
1a436809 316 _cleanup_netdev_free_ NetDev *netdev = NULL;
02b59d57
TG
317 _cleanup_fclose_ FILE *file = NULL;
318 int r;
319
bf1bc670
TA
320 assert(manager);
321 assert(filename);
322
02b59d57
TG
323 file = fopen(filename, "re");
324 if (!file) {
325 if (errno == ENOENT)
326 return 0;
327 else
328 return errno;
329 }
330
1a436809 331 netdev = new0(NetDev, 1);
52433f6b 332 if (!netdev)
02b59d57
TG
333 return log_oom();
334
52433f6b
TG
335 netdev->manager = manager;
336 netdev->state = _NETDEV_STATE_INVALID;
337 netdev->kind = _NETDEV_KIND_INVALID;
672682a6 338 netdev->vlanid = VLANID_MAX + 1;
02b59d57 339
c0dda186
TG
340 r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0", config_item_perf_lookup,
341 (void*) network_netdev_gperf_lookup, false, false, netdev);
02b59d57
TG
342 if (r < 0) {
343 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
344 return r;
449f7554 345 }
02b59d57 346
52433f6b 347 if (netdev->kind == _NETDEV_KIND_INVALID) {
1a436809 348 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
52433f6b
TG
349 return 0;
350 }
351
352 if (!netdev->name) {
1a436809 353 log_warning("NetDev without Name configured in %s. Ignoring", filename);
02b59d57
TG
354 return 0;
355 }
356
672682a6
TG
357 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
358 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
54abf461
TG
359 return 0;
360 }
361
52433f6b
TG
362 netdev->filename = strdup(filename);
363 if (!netdev->filename)
02b59d57
TG
364 return log_oom();
365
c0dda186 366 if (net_match_config(NULL, NULL, NULL, NULL, NULL,
edbb03e9
TG
367 netdev->match_host, netdev->match_virt,
368 netdev->match_kernel, netdev->match_arch,
bf175aaf 369 NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
c0dda186
TG
370 return 0;
371
52433f6b 372 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
02b59d57
TG
373 if (r < 0)
374 return r;
375
52433f6b 376 LIST_HEAD_INIT(netdev->callbacks);
02b59d57 377
54abf461
TG
378 if (netdev->kind != NETDEV_KIND_VLAN) {
379 r = netdev_create(netdev, NULL, NULL);
380 if (r < 0)
381 return r;
382 }
02b59d57 383
52433f6b 384 netdev = NULL;
02b59d57
TG
385
386 return 0;
387}
388
52433f6b 389int netdev_load(Manager *manager) {
1a436809 390 NetDev *netdev;
02b59d57
TG
391 char **files, **f;
392 int r;
393
394 assert(manager);
395
52433f6b
TG
396 while ((netdev = hashmap_first(manager->netdevs)))
397 netdev_free(netdev);
02b59d57 398
2ad8416d 399 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
02b59d57
TG
400 if (r < 0) {
401 log_error("Failed to enumerate netdev files: %s", strerror(-r));
402 return r;
403 }
404
405 STRV_FOREACH_BACKWARDS(f, files) {
52433f6b 406 r = netdev_load_one(manager, *f);
02b59d57
TG
407 if (r < 0)
408 return r;
409 }
410
411 strv_free(files);
412
413 return 0;
414}