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