]> git.ipfire.org Git - people/ms/network.git/blob - src/networkd/port.c
test: Run networkd as root in its own namespace
[people/ms/network.git] / src / networkd / port.c
1 /*#############################################################################
2 # #
3 # IPFire.org - A linux based firewall #
4 # Copyright (C) 2023 IPFire Network Development Team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <limits.h>
22 #include <net/if.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25
26 #include <systemd/sd-bus.h>
27
28 #include "address.h"
29 #include "config.h"
30 #include "json.h"
31 #include "link.h"
32 #include "logging.h"
33 #include "port.h"
34 #include "port-bonding.h"
35 #include "port-dummy.h"
36 #include "port-vlan.h"
37 #include "stats-collector.h"
38 #include "string.h"
39
40 static const nw_string_table_t nw_port_type_id[] = {
41 { NW_PORT_BONDING, "bonding" },
42 { NW_PORT_DUMMY, "dummy" },
43 { NW_PORT_VLAN, "vlan" },
44 { -1, NULL },
45 };
46
47 NW_STRING_TABLE_LOOKUP(nw_port_type_id_t, nw_port_type_id)
48
49 static void nw_port_free(nw_port* port) {
50 if (port->link)
51 nw_link_unref(port->link);
52 if (port->config)
53 nw_config_unref(port->config);
54 if (port->daemon)
55 nw_daemon_unref(port->daemon);
56
57 free(port);
58 }
59
60 static int nw_port_set_link(nw_port* port, nw_link* link) {
61 // Do nothing if the same link is being re-assigned
62 if (port->link == link)
63 return 0;
64
65 // Dereference the former link if set
66 if (port->link)
67 nw_link_unref(port->link);
68
69 // Store the new link
70 if (link) {
71 port->link = nw_link_ref(link);
72
73 DEBUG("Port %s: Assigned link %d\n", port->name, nw_link_ifindex(port->link));
74
75 // Or clear the pointer if no link has been provided
76 } else {
77 port->link = NULL;
78
79 DEBUG("Port %s: Removed link\n", port->name);
80 }
81
82 return 0;
83 }
84
85 static int nw_port_setup(nw_port* port) {
86 nw_link* link = NULL;
87 int r;
88
89 // Find the link
90 link = nw_daemon_get_link_by_name(port->daemon, port->name);
91 if (link) {
92 r = nw_port_set_link(port, link);
93 if (r)
94 goto ERROR;
95 }
96
97 // Generate a random Ethernet address
98 r = nw_address_generate(&port->address);
99 if (r < 0) {
100 ERROR("Could not generate an Ethernet address: %s\n", strerror(-r));
101 goto ERROR;
102 }
103
104 // Setup options
105 r = NW_CONFIG_OPTION_ADDRESS(port->config, "ADDRESS", &port->address);
106 if (r < 0)
107 goto ERROR;
108
109 // Call any custom initialization
110 if (NW_PORT_TYPE(port)->setup) {
111 r = NW_PORT_TYPE(port)->setup(port);
112 if (r < 0)
113 goto ERROR;
114 }
115
116 // Parse the configuration
117 r = nw_config_options_read(port->config);
118 if (r < 0) {
119 ERROR("Could not read configuration for port %s: %s\n", port->name, strerror(-r));
120 goto ERROR;
121 }
122
123 ERROR:
124 if (link)
125 nw_link_unref(link);
126
127 return r;
128 }
129
130 static int nw_port_validate(nw_port* port) {
131 int r = 0;
132
133 // Validate the port configuration
134 if (NW_PORT_TYPE(port)->validate) {
135 r = NW_PORT_TYPE(port)->validate(port);
136 if (r < 0)
137 ERROR("Could not check configuration for %s: %s\n", port->name, strerror(-r));
138 }
139
140 return r;
141 }
142
143 int nw_port_create(nw_port** port, nw_daemon* daemon,
144 const nw_port_type_id_t type, const char* name, nw_config* config) {
145 int r;
146
147 // Allocate a new object
148 nw_port* p = calloc(1, sizeof(*p));
149 if (!p)
150 return 1;
151
152 // Store a reference to the daemon
153 p->daemon = nw_daemon_ref(daemon);
154
155 // Initialize reference counter
156 p->nrefs = 1;
157
158 // Set operations
159 switch (type) {
160 case NW_PORT_BONDING:
161 p->type = &nw_port_type_bonding;
162 break;
163
164 case NW_PORT_DUMMY:
165 p->type = &nw_port_type_dummy;
166 break;
167
168 case NW_PORT_VLAN:
169 p->type = &nw_port_type_vlan;
170 break;
171 }
172
173 // Store the name
174 r = nw_string_set(p->name, name);
175 if (r)
176 goto ERROR;
177
178 // Copy the configuration
179 r = nw_config_copy(config, &p->config);
180 if (r)
181 goto ERROR;
182
183 // Setup the port
184 r = nw_port_setup(p);
185 if (r)
186 goto ERROR;
187
188 // Validate the configuration
189 r = nw_port_validate(p);
190 switch (r) {
191 // Configuration is valid
192 case 0:
193 break;
194
195 // Configuration is invalid
196 case 1:
197 ERROR("%s: Invalid configuration\n", p->name);
198 goto ERROR;
199
200 // Error
201 default:
202 goto ERROR;
203 }
204
205 *port = p;
206 return 0;
207
208 ERROR:
209 nw_port_free(p);
210 return r;
211 }
212
213 int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
214 const char* name, const char* path) {
215 nw_config* config = NULL;
216 int r;
217
218 // Initialize the configuration
219 r = nw_config_create(&config, path);
220 if (r)
221 goto ERROR;
222
223 // Fetch the type
224 const char* type = nw_config_get(config, "TYPE");
225 if (!type) {
226 ERROR("Port configuration %s has no TYPE\n", path);
227 r = 1;
228 goto ERROR;
229 }
230
231 // Create a new port
232 r = nw_port_create(port, daemon, nw_port_type_id_from_string(type), name, config);
233 if (r)
234 goto ERROR;
235
236 ERROR:
237 if (config)
238 nw_config_unref(config);
239
240 return r;
241 }
242
243 nw_port* nw_port_ref(nw_port* port) {
244 port->nrefs++;
245
246 return port;
247 }
248
249 nw_port* nw_port_unref(nw_port* port) {
250 if (--port->nrefs > 0)
251 return port;
252
253 nw_port_free(port);
254 return NULL;
255 }
256
257 /*
258 This is a helper function for when we pass a reference to the event loop
259 it will have to dereference the port instance later.
260 */
261 static void __nw_port_unref(void* data) {
262 nw_port* port = (nw_port*)data;
263
264 nw_port_unref(port);
265 }
266
267 int nw_port_destroy(nw_port* port) {
268 int r;
269
270 DEBUG("Destroying port %s\n", port->name);
271
272 // Destroy the physical link (if exists)
273 if (port->link) {
274 r = nw_link_destroy(port->link);
275 if (r)
276 return r;
277 }
278
279 // Dereference the port from other ports
280 r = nw_daemon_ports_walk(port->daemon, __nw_port_drop_port, port);
281 if (r)
282 return r;
283
284 // Dereference the port from other zones
285 r = nw_daemon_zones_walk(port->daemon, __nw_zone_drop_port, port);
286 if (r)
287 return r;
288
289 // Destroy the configuration
290 r = nw_config_destroy(port->config);
291 if (r)
292 return r;
293
294 return 0;
295 }
296
297 int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
298 nw_port* dropped_port = (nw_port*)data;
299 int r;
300
301 switch (port->type->id) {
302 case NW_PORT_VLAN:
303 if (port->vlan.parent == dropped_port) {
304 r = nw_port_set_vlan_parent(port, NULL);
305 if (r)
306 return r;
307 }
308 break;
309
310 case NW_PORT_BONDING:
311 case NW_PORT_DUMMY:
312 break;
313 }
314
315 return 0;
316 }
317
318 int nw_port_save(nw_port* port) {
319 int r;
320
321 // Write out the configuration
322 r = nw_config_options_write(port->config);
323 if (r < 0)
324 goto ERROR;
325
326 // Write the configuration
327 r = nw_config_write(port->config);
328 if (r)
329 return r;
330
331 return 0;
332
333 ERROR:
334 ERROR("Could not save configuration for port %s: %s\n", port->name, strerror(-r));
335
336 return 1;
337 }
338
339 const char* nw_port_name(nw_port* port) {
340 return port->name;
341 }
342
343 char* nw_port_bus_path(nw_port* port) {
344 char* p = NULL;
345 int r;
346
347 // Encode the bus path
348 r = sd_bus_path_encode("/org/ipfire/network1/port", port->name, &p);
349 if (r < 0)
350 return NULL;
351
352 return p;
353 }
354
355 int __nw_port_set_link(nw_daemon* daemon, nw_port* port, void* data) {
356 nw_link* link = (nw_link*)data;
357
358 // Fetch the link name
359 const char* ifname = nw_link_ifname(link);
360 if (!ifname) {
361 ERROR("Link does not have a name set\n");
362 return 1;
363 }
364
365 // Set link if the name matches
366 if (strcmp(port->name, ifname) == 0)
367 return nw_port_set_link(port, link);
368
369 // If we have the link set but the name did not match, we will remove it
370 else if (port->link == link)
371 return nw_port_set_link(port, NULL);
372
373 return 0;
374 }
375
376 int __nw_port_drop_link(nw_daemon* daemon, nw_port* port, void* data) {
377 nw_link* link = (nw_link*)data;
378
379 // Drop the link if it matches
380 if (port->link == link)
381 return nw_port_set_link(port, NULL);
382
383 return 0;
384 }
385
386 static nw_link* nw_port_get_link(nw_port* port) {
387 // Fetch the link if not set
388 if (!port->link)
389 port->link = nw_daemon_get_link_by_name(port->daemon, port->name);
390
391 if (!port->link)
392 return NULL;
393
394 return nw_link_ref(port->link);
395 }
396
397 const nw_address_t* nw_port_get_address(nw_port* port) {
398 return &port->address;
399 }
400
401 static int nw_port_is_disabled(nw_port* port) {
402 return nw_config_get_bool(port->config, "DISABLED");
403 }
404
405 nw_port* nw_port_get_parent_port(nw_port* port) {
406 if (!NW_PORT_TYPE(port)->get_parent_port)
407 return NULL;
408
409 return NW_PORT_TYPE(port)->get_parent_port(port);
410 }
411
412 static nw_link* nw_port_get_parent_link(nw_port* port) {
413 nw_port* parent = NULL;
414 nw_link* link = NULL;
415
416 // Fetch the parent
417 parent = nw_port_get_parent_port(port);
418 if (!parent)
419 return NULL;
420
421 // Fetch the link
422 link = nw_port_get_link(parent);
423
424 // Cleanup
425 if (parent)
426 nw_port_unref(parent);
427
428 return link;
429 }
430
431 static int __nw_port_create_link(sd_netlink* rtnl, sd_netlink_message* m, void* data) {
432 nw_port* port = (nw_port*)data;
433 int r;
434
435 // Check if the operation was successful
436 r = sd_netlink_message_get_errno(m);
437 if (r < 0) {
438 ERROR("Could not create port %s: %s\n", port->name, strerror(-r));
439 // XXX We should extract the error message
440
441 return 0;
442 }
443
444 DEBUG("Successfully created %s\n", port->name);
445
446 return 0;
447 }
448
449 static int nw_port_create_link(nw_port* port) {
450 sd_netlink_message* m = NULL;
451 nw_link* link = NULL;
452 int r;
453
454 sd_netlink* rtnl = nw_daemon_get_rtnl(port->daemon);
455
456 DEBUG("Creating port %s...\n", port->name);
457
458 // Check the kind
459 if (!NW_PORT_TYPE(port)->kind) {
460 ERROR("Port type has no kind\n");
461 r = -ENOTSUP;
462 goto ERROR;
463 }
464
465 // Create a new link
466 r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
467 if (r < 0) {
468 ERROR("Could not create netlink message: %m\n");
469 goto ERROR;
470 }
471
472 // Set the name
473 r = sd_netlink_message_append_string(m, IFLA_IFNAME, port->name);
474 if (r < 0) {
475 ERROR("Could not set port name: %s\n", strerror(-r));
476 goto ERROR;
477 }
478
479 // XXX Set common things like MTU, etc.
480
481 // Set Ethernet address
482 r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &port->address);
483 if (r < 0) {
484 ERROR("Could not set MAC address: %s\n", strerror(-r));
485 goto ERROR;
486 }
487
488 // Fetch the parent link
489 link = nw_port_get_parent_link(port);
490 if (link) {
491 r = sd_netlink_message_append_u32(m, IFLA_LINK, nw_link_ifindex(link));
492 if (r < 0)
493 goto ERROR;
494 }
495
496 // Open an IFLA_LINKINFO container
497 r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
498 if (r < 0)
499 goto ERROR;
500
501 // Run the custom setup
502 if (NW_PORT_TYPE(port)->create_link) {
503 r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, NW_PORT_TYPE(port)->kind);
504 if (r < 0) {
505 ERROR("Could not open IFLA_INFO_DATA container: %s\n", strerror(-r));
506 goto ERROR;
507 }
508
509 r = NW_PORT_TYPE(port)->create_link(port, m);
510 if (r) {
511 ERROR("Could not create port %s: %m\n", port->name);
512 goto ERROR;
513 }
514
515 // Close the container
516 r = sd_netlink_message_close_container(m);
517 if (r < 0)
518 goto ERROR;
519
520 // Just set IFLA_INFO_KIND if there is no custom function
521 } else {
522 r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, NW_PORT_TYPE(port)->kind);
523 if (r < 0)
524 goto ERROR;
525 }
526
527 // Close the container
528 r = sd_netlink_message_close_container(m);
529 if (r < 0)
530 goto ERROR;
531
532 // Send the message
533 r = sd_netlink_call_async(rtnl, NULL, m, __nw_port_create_link,
534 __nw_port_unref, nw_port_ref(port), -1, NULL);
535 if (r < 0) {
536 ERROR("Could not send netlink message: %s\n", strerror(-r));
537 goto ERROR;
538 }
539
540 r = 0;
541
542 ERROR:
543 if (m)
544 sd_netlink_message_unref(m);
545 if (link)
546 nw_link_unref(link);
547
548 return r;
549 }
550
551 int nw_port_reconfigure(nw_port* port) {
552 int r;
553
554 // If the port is disabled, we will try to destroy it
555 if (nw_port_is_disabled(port)) {
556 if (port->link) {
557 r = nw_link_destroy(port->link);
558 if (r)
559 return r;
560 }
561
562 return 0;
563 }
564
565 // If there is no link, we will try to create it
566 if (!port->link)
567 return nw_port_create_link(port);
568
569 // XXX TODO
570
571 return 0;
572 }
573
574 int nw_port_has_carrier(nw_port* port) {
575 if (!port->link)
576 return 0;
577
578 return nw_link_has_carrier(port->link);
579 }
580
581 /*
582 Stats
583 */
584
585 const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port) {
586 if (!port->link)
587 return NULL;
588
589 return nw_link_get_stats64(port->link);
590 }
591
592 int __nw_port_update_stats(nw_daemon* daemon, nw_port* port, void* data) {
593 nw_link* link = (nw_link*)data;
594
595 // Emit stats if link matches
596 if (port->link == link)
597 return nw_stats_collector_emit_port_stats(daemon, port);
598
599 return 0;
600 }
601
602 int nw_port_update_stats(nw_port* port) {
603 if (port->link)
604 return nw_link_update_stats(port->link);
605
606 return 0;
607 }
608
609 int nw_port_check_type(nw_port* port, const nw_port_type_id_t type) {
610 if (port->type->id == type)
611 return 0;
612
613 errno = ENOTSUP;
614 return -errno;
615 }
616
617 /*
618 JSON
619 */
620 int nw_port_to_json(nw_port* port, struct json_object** object) {
621 char* address = NULL;
622 int r;
623
624 // Create a new JSON object
625 struct json_object* o = json_object_new_object();
626 if (!o) {
627 r = -errno;
628 goto ERROR;
629 }
630
631 // Add name
632 r = json_object_add_string(o, "Name", port->name);
633 if (r < 0)
634 goto ERROR;
635
636 // Add Type
637 r = json_object_add_string(o, "Type", nw_port_type_id_to_string(port->type->id));
638 if (r < 0)
639 goto ERROR;
640
641 // Add address
642 address = nw_address_to_string(&port->address);
643 if (address) {
644 r = json_object_add_string(o, "Address", address);
645 if (r < 0)
646 goto ERROR;
647 }
648
649 // Call custom stuff
650 if (NW_PORT_TYPE(port)->to_json) {
651 r = NW_PORT_TYPE(port)->to_json(port, o);
652 if (r < 0)
653 goto ERROR;
654 }
655
656 // Success
657 r = 0;
658
659 // Return a reference to the created object
660 *object = json_object_ref(o);
661
662 ERROR:
663 if (address)
664 free(address);
665 if (o)
666 json_object_unref(o);
667
668 return r;
669 }