]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-netdev.c
rtnl: support adding VETH_INFO_PEER containers into rtnl messages
[thirdparty/systemd.git] / src / network / networkd-netdev.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 "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
29 #define VLANID_MAX 4094
30
31 static const char* const netdev_kind_table[] = {
32 [NETDEV_KIND_BRIDGE] = "bridge",
33 [NETDEV_KIND_BOND] = "bond",
34 [NETDEV_KIND_VLAN] = "vlan",
35 };
36
37 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
38 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
39
40 void netdev_free(NetDev *netdev) {
41 netdev_enslave_callback *callback;
42
43 if (!netdev)
44 return;
45
46 while ((callback = netdev->callbacks)) {
47 LIST_REMOVE(callbacks, netdev->callbacks, callback);
48 free(callback);
49 }
50
51 if (netdev->name)
52 hashmap_remove(netdev->manager->netdevs, netdev->name);
53
54 free(netdev->filename);
55
56 free(netdev->description);
57 free(netdev->name);
58
59 free(netdev);
60 }
61
62 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
63 NetDev *netdev;
64
65 assert(manager);
66 assert(name);
67 assert(ret);
68
69 netdev = hashmap_get(manager->netdevs, name);
70 if (!netdev) {
71 *ret = NULL;
72 return -ENOENT;
73 }
74
75 *ret = netdev;
76
77 return 0;
78 }
79
80 static int netdev_enter_failed(NetDev *netdev) {
81 netdev->state = NETDEV_STATE_FAILED;
82
83 return 0;
84 }
85
86 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
87 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
88 int r;
89
90 assert(netdev);
91 assert(netdev->state == NETDEV_STATE_READY);
92 assert(link);
93 assert(callback);
94
95 r = sd_rtnl_message_new_link(RTM_SETLINK, link->ifindex, &req);
96 if (r < 0) {
97 log_error_netdev(netdev,
98 "Could not allocate RTM_SETLINK message: %s",
99 strerror(-r));
100 return r;
101 }
102
103 r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
104 if (r < 0) {
105 log_error_netdev(netdev,
106 "Could not append IFLA_MASTER attribute: %s",
107 strerror(-r));
108 return r;
109 }
110
111 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
112 if (r < 0) {
113 log_error_netdev(netdev,
114 "Could not send rtnetlink message: %s",
115 strerror(-r));
116 return r;
117 }
118
119 log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
120
121 return 0;
122 }
123
124 static int netdev_enter_ready(NetDev *netdev) {
125 netdev_enslave_callback *callback;
126
127 assert(netdev);
128 assert(netdev->name);
129
130 netdev->state = NETDEV_STATE_READY;
131
132 log_info_netdev(netdev, "netdev ready");
133
134 LIST_FOREACH(callbacks, callback, netdev->callbacks) {
135 /* enslave the links that were attempted to be enslaved befor the
136 * link was ready */
137 netdev_enslave_ready(netdev, callback->link, callback->callback);
138 }
139
140 return 0;
141 }
142
143 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
144 NetDev *netdev = userdata;
145 int r;
146
147 assert(netdev->state != _NETDEV_STATE_INVALID);
148
149 r = sd_rtnl_message_get_errno(m);
150 if (r < 0) {
151 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
152 netdev_enter_failed(netdev);
153
154 return 1;
155 }
156
157 return 1;
158 }
159
160 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
161 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
162 const char *kind;
163 int r;
164
165 assert(netdev);
166 assert(!(netdev->kind == NETDEV_KIND_VLAN) || (link && callback && netdev->vlanid <= VLANID_MAX));
167 assert(netdev->name);
168 assert(netdev->manager);
169 assert(netdev->manager->rtnl);
170
171 r = sd_rtnl_message_new_link(RTM_NEWLINK, 0, &req);
172 if (r < 0) {
173 log_error_netdev(netdev,
174 "Could not allocate RTM_NEWLINK message: %s",
175 strerror(-r));
176 return r;
177 }
178
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
189 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
190 if (r < 0) {
191 log_error_netdev(netdev,
192 "Could not append IFLA_IFNAME attribute: %s",
193 strerror(-r));
194 return r;
195 }
196
197 r = sd_rtnl_message_open_container(req, IFLA_LINKINFO, 0);
198 if (r < 0) {
199 log_error_netdev(netdev,
200 "Could not open IFLA_LINKINFO container: %s",
201 strerror(-r));
202 return r;
203 }
204
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);
212 if (r < 0) {
213 log_error_netdev(netdev,
214 "Could not append IFLA_INFO_KIND attribute: %s",
215 strerror(-r));
216 return r;
217 }
218
219 if (netdev->vlanid <= VLANID_MAX) {
220 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA, 0);
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
245 r = sd_rtnl_message_close_container(req);
246 if (r < 0) {
247 log_error_netdev(netdev,
248 "Could not close IFLA_LINKINFO container %s",
249 strerror(-r));
250 return r;
251 }
252
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);
257 if (r < 0) {
258 log_error_netdev(netdev,
259 "Could not send rtnetlink message: %s", strerror(-r));
260 return r;
261 }
262
263 log_debug_netdev(netdev, "creating netdev");
264
265 netdev->state = NETDEV_STATE_CREATING;
266
267 return 0;
268 }
269
270 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
271 if (netdev->kind == NETDEV_KIND_VLAN)
272 return netdev_create(netdev, link, callback);
273
274 if (netdev->state == NETDEV_STATE_READY) {
275 netdev_enslave_ready(netdev, link, callback);
276 } else {
277 /* the netdev is not yet read, save this request for when it is*/
278 netdev_enslave_callback *cb;
279
280 cb = new0(netdev_enslave_callback, 1);
281 if (!cb)
282 return log_oom();
283
284 cb->callback = callback;
285 cb->link = link;
286
287 LIST_PREPEND(callbacks, netdev->callbacks, cb);
288 }
289
290 return 0;
291 }
292
293 int netdev_set_ifindex(NetDev *netdev, int ifindex) {
294 assert(netdev);
295 assert(ifindex > 0);
296
297 if (netdev->ifindex > 0) {
298 if (netdev->ifindex == ifindex)
299 return 0;
300 else
301 return -EEXIST;
302 }
303
304 netdev->ifindex = ifindex;
305
306 netdev_enter_ready(netdev);
307
308 return 0;
309 }
310
311 static int netdev_load_one(Manager *manager, const char *filename) {
312 _cleanup_netdev_free_ NetDev *netdev = NULL;
313 _cleanup_fclose_ FILE *file = NULL;
314 int r;
315
316 assert(manager);
317 assert(filename);
318
319 file = fopen(filename, "re");
320 if (!file) {
321 if (errno == ENOENT)
322 return 0;
323 else
324 return errno;
325 }
326
327 netdev = new0(NetDev, 1);
328 if (!netdev)
329 return log_oom();
330
331 netdev->manager = manager;
332 netdev->state = _NETDEV_STATE_INVALID;
333 netdev->kind = _NETDEV_KIND_INVALID;
334 netdev->vlanid = VLANID_MAX + 1;
335
336 r = config_parse(NULL, filename, file, "NetDev\0VLAN\0", config_item_perf_lookup,
337 (void*) network_gperf_lookup, false, false, netdev);
338 if (r < 0) {
339 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
340 return r;
341 }
342
343 if (netdev->kind == _NETDEV_KIND_INVALID) {
344 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
345 return 0;
346 }
347
348 if (!netdev->name) {
349 log_warning("NetDev without Name configured in %s. Ignoring", filename);
350 return 0;
351 }
352
353 if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
354 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
355 return 0;
356 }
357
358 netdev->filename = strdup(filename);
359 if (!netdev->filename)
360 return log_oom();
361
362 r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
363 if (r < 0)
364 return r;
365
366 LIST_HEAD_INIT(netdev->callbacks);
367
368 if (netdev->kind != NETDEV_KIND_VLAN) {
369 r = netdev_create(netdev, NULL, NULL);
370 if (r < 0)
371 return r;
372 }
373
374 netdev = NULL;
375
376 return 0;
377 }
378
379 int netdev_load(Manager *manager) {
380 NetDev *netdev;
381 char **files, **f;
382 int r;
383
384 assert(manager);
385
386 while ((netdev = hashmap_first(manager->netdevs)))
387 netdev_free(netdev);
388
389 r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
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) {
396 r = netdev_load_one(manager, *f);
397 if (r < 0)
398 return r;
399 }
400
401 strv_free(files);
402
403 return 0;
404 }