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