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