]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-netdev.c
rtnl: support adding VETH_INFO_PEER containers into rtnl messages
[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);
02b59d57
TG
92 assert(link);
93 assert(callback);
94
d595c5cc 95 r = sd_rtnl_message_new_link(RTM_SETLINK, link->ifindex, &req);
02b59d57 96 if (r < 0) {
52433f6b 97 log_error_netdev(netdev,
3333d748
ZJS
98 "Could not allocate RTM_SETLINK message: %s",
99 strerror(-r));
02b59d57
TG
100 return r;
101 }
102
50add290 103 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
02b59d57 104 if (r < 0) {
52433f6b 105 log_error_netdev(netdev,
3333d748
ZJS
106 "Could not append IFLA_MASTER attribute: %s",
107 strerror(-r));
02b59d57
TG
108 return r;
109 }
110
52433f6b 111 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
02b59d57 112 if (r < 0) {
52433f6b 113 log_error_netdev(netdev,
3333d748
ZJS
114 "Could not send rtnetlink message: %s",
115 strerror(-r));
02b59d57
TG
116 return r;
117 }
118
52433f6b 119 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
ab47d620 120
02b59d57
TG
121 return 0;
122}
123
1a436809 124static int netdev_enter_ready(NetDev *netdev) {
52433f6b 125 netdev_enslave_callback *callback;
02b59d57 126
52433f6b
TG
127 assert(netdev);
128 assert(netdev->name);
924fe430 129
52433f6b 130 netdev->state = NETDEV_STATE_READY;
02b59d57 131
52433f6b 132 log_info_netdev(netdev, "netdev ready");
02b59d57 133
52433f6b
TG
134 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
135 /* enslave the links that were attempted to be enslaved befor the
02b59d57 136 * link was ready */
52433f6b 137 netdev_enslave_ready(netdev, callback->link, callback->callback);
02b59d57
TG
138 }
139
140 return 0;
141}
142
52433f6b 143static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
1a436809 144 NetDev *netdev = userdata;
172f6635 145 int r;
02b59d57 146
52433f6b 147 assert(netdev->state != _NETDEV_STATE_INVALID);
02b59d57
TG
148
149 r = sd_rtnl_message_get_errno(m);
150 if (r < 0) {
52433f6b
TG
151 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
152 netdev_enter_failed(netdev);
dd3efc09
TG
153
154 return 1;
02b59d57
TG
155 }
156
dd3efc09 157 return 1;
02b59d57
TG
158}
159
1a436809 160static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
cf6a8911 161 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
52433f6b 162 const char *kind;
02b59d57
TG
163 int r;
164
52433f6b 165 assert(netdev);
672682a6 166 assert(!(netdev->kind == NETDEV_KIND_VLAN) || (link && callback && netdev->vlanid <= VLANID_MAX));
52433f6b
TG
167 assert(netdev->name);
168 assert(netdev->manager);
169 assert(netdev->manager->rtnl);
02b59d57 170
d595c5cc 171 r = sd_rtnl_message_new_link(RTM_NEWLINK, 0, &req);
02b59d57 172 if (r < 0) {
52433f6b 173 log_error_netdev(netdev,
3333d748
ZJS
174 "Could not allocate RTM_NEWLINK message: %s",
175 strerror(-r));
02b59d57
TG
176 return r;
177 }
178
54abf461
TG
179 if (link) {
180 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
181 if (r < 0) {
182 log_error_netdev(netdev,
183 "Could not append IFLA_LINK attribute: %s",
184 strerror(-r));
185 return r;
186 }
187 }
188
52433f6b 189 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
02b59d57 190 if (r < 0) {
52433f6b 191 log_error_netdev(netdev,
3333d748
ZJS
192 "Could not append IFLA_IFNAME attribute: %s",
193 strerror(-r));
02b59d57
TG
194 return r;
195 }
196
31a4e153 197 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO, 0);
02b59d57 198 if (r < 0) {
52433f6b 199 log_error_netdev(netdev,
3333d748
ZJS
200 "Could not open IFLA_LINKINFO container: %s",
201 strerror(-r));
02b59d57
TG
202 return r;
203 }
204
52433f6b
TG
205 kind = netdev_kind_to_string(netdev->kind);
206 if (!kind) {
207 log_error_netdev(netdev, "Invalid kind");
208 return -EINVAL;
209 }
210
211 r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind);
02b59d57 212 if (r < 0) {
52433f6b 213 log_error_netdev(netdev,
3333d748
ZJS
214 "Could not append IFLA_INFO_KIND attribute: %s",
215 strerror(-r));
02b59d57
TG
216 return r;
217 }
218
672682a6 219 if (netdev->vlanid <= VLANID_MAX) {
31a4e153 220 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA, 0);
54abf461
TG
221 if (r < 0) {
222 log_error_netdev(netdev,
223 "Could not open IFLA_INFO_DATA container: %s",
224 strerror(-r));
225 return r;
226 }
227
228 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
229 if (r < 0) {
230 log_error_netdev(netdev,
231 "Could not append IFLA_VLAN_ID attribute: %s",
232 strerror(-r));
233 return r;
234 }
235
236 r = sd_rtnl_message_close_container(req);
237 if (r < 0) {
238 log_error_netdev(netdev,
239 "Could not close IFLA_INFO_DATA container %s",
240 strerror(-r));
241 return r;
242 }
243 }
244
02b59d57
TG
245 r = sd_rtnl_message_close_container(req);
246 if (r < 0) {
52433f6b 247 log_error_netdev(netdev,
3333d748
ZJS
248 "Could not close IFLA_LINKINFO container %s",
249 strerror(-r));
02b59d57
TG
250 return r;
251 }
252
54abf461
TG
253 if (link)
254 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
255 else
256 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
02b59d57 257 if (r < 0) {
52433f6b 258 log_error_netdev(netdev,
3333d748 259 "Could not send rtnetlink message: %s", strerror(-r));
02b59d57
TG
260 return r;
261 }
262
52433f6b 263 log_debug_netdev(netdev, "creating netdev");
02b59d57 264
52433f6b 265 netdev->state = NETDEV_STATE_CREATING;
02b59d57
TG
266
267 return 0;
268}
269
1a436809 270int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
54abf461
TG
271 if (netdev->kind == NETDEV_KIND_VLAN)
272 return netdev_create(netdev, link, callback);
273
52433f6b
TG
274 if (netdev->state == NETDEV_STATE_READY) {
275 netdev_enslave_ready(netdev, link, callback);
02b59d57 276 } else {
52433f6b
TG
277 /* the netdev is not yet read, save this request for when it is*/
278 netdev_enslave_callback *cb;
02b59d57 279
52433f6b 280 cb = new0(netdev_enslave_callback, 1);
02b59d57
TG
281 if (!cb)
282 return log_oom();
283
284 cb->callback = callback;
285 cb->link = link;
286
52433f6b 287 LIST_PREPEND(callbacks, netdev->callbacks, cb);
02b59d57
TG
288 }
289
290 return 0;
291}
292
1a436809 293int netdev_set_ifindex(NetDev *netdev, int ifindex) {
50add290
TG
294 assert(netdev);
295 assert(ifindex > 0);
02b59d57 296
50add290
TG
297 if (netdev->ifindex > 0) {
298 if (netdev->ifindex == ifindex)
299 return 0;
300 else
301 return -EEXIST;
302 }
02b59d57 303
50add290 304 netdev->ifindex = ifindex;
52433f6b
TG
305
306 netdev_enter_ready(netdev);
02b59d57
TG
307
308 return 0;
309}
310
52433f6b 311static int netdev_load_one(Manager *manager, const char *filename) {
1a436809 312 _cleanup_netdev_free_ NetDev *netdev = NULL;
02b59d57
TG
313 _cleanup_fclose_ FILE *file = NULL;
314 int r;
315
bf1bc670
TA
316 assert(manager);
317 assert(filename);
318
02b59d57
TG
319 file = fopen(filename, "re");
320 if (!file) {
321 if (errno == ENOENT)
322 return 0;
323 else
324 return errno;
325 }
326
1a436809 327 netdev = new0(NetDev, 1);
52433f6b 328 if (!netdev)
02b59d57
TG
329 return log_oom();
330
52433f6b
TG
331 netdev->manager = manager;
332 netdev->state = _NETDEV_STATE_INVALID;
333 netdev->kind = _NETDEV_KIND_INVALID;
672682a6 334 netdev->vlanid = VLANID_MAX + 1;
02b59d57 335
1a436809 336 r = config_parse(NULL, filename, file, "NetDev\0VLAN\0", config_item_perf_lookup,
52433f6b 337 (void*) network_gperf_lookup, false, false, netdev);
02b59d57
TG
338 if (r < 0) {
339 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
340 return r;
449f7554 341 }
02b59d57 342
52433f6b 343 if (netdev->kind == _NETDEV_KIND_INVALID) {
1a436809 344 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
52433f6b
TG
345 return 0;
346 }
347
348 if (!netdev->name) {
1a436809 349 log_warning("NetDev without Name configured in %s. Ignoring", filename);
02b59d57
TG
350 return 0;
351 }
352
672682a6
TG
353 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
354 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
54abf461
TG
355 return 0;
356 }
357
52433f6b
TG
358 netdev->filename = strdup(filename);
359 if (!netdev->filename)
02b59d57
TG
360 return log_oom();
361
52433f6b 362 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
02b59d57
TG
363 if (r < 0)
364 return r;
365
52433f6b 366 LIST_HEAD_INIT(netdev->callbacks);
02b59d57 367
54abf461
TG
368 if (netdev->kind != NETDEV_KIND_VLAN) {
369 r = netdev_create(netdev, NULL, NULL);
370 if (r < 0)
371 return r;
372 }
02b59d57 373
52433f6b 374 netdev = NULL;
02b59d57
TG
375
376 return 0;
377}
378
52433f6b 379int netdev_load(Manager *manager) {
1a436809 380 NetDev *netdev;
02b59d57
TG
381 char **files, **f;
382 int r;
383
384 assert(manager);
385
52433f6b
TG
386 while ((netdev = hashmap_first(manager->netdevs)))
387 netdev_free(netdev);
02b59d57 388
2ad8416d 389 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
02b59d57
TG
390 if (r < 0) {
391 log_error("Failed to enumerate netdev files: %s", strerror(-r));
392 return r;
393 }
394
395 STRV_FOREACH_BACKWARDS(f, files) {
52433f6b 396 r = netdev_load_one(manager, *f);
02b59d57
TG
397 if (r < 0)
398 return r;
399 }
400
401 strv_free(files);
402
403 return 0;
404}