]> git.ipfire.org Git - people/ms/network.git/blob - src/networkd/port.c
c14d439ec2ef79636d982142e525ffb3221e14a2
[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 int nw_port_create(nw_port** port, nw_daemon* daemon,
131 const nw_port_type_id_t type, const char* name, nw_config* config) {
132 int r;
133
134 // Allocate a new object
135 nw_port* p = calloc(1, sizeof(*p));
136 if (!p)
137 return 1;
138
139 // Store a reference to the daemon
140 p->daemon = nw_daemon_ref(daemon);
141
142 // Initialize reference counter
143 p->nrefs = 1;
144
145 // Set operations
146 switch (type) {
147 case NW_PORT_BONDING:
148 p->type = &nw_port_type_bonding;
149 break;
150
151 case NW_PORT_DUMMY:
152 p->type = &nw_port_type_dummy;
153 break;
154
155 case NW_PORT_VLAN:
156 p->type = &nw_port_type_vlan;
157 break;
158 }
159
160 // Store the name
161 r = nw_string_set(p->name, name);
162 if (r)
163 goto ERROR;
164
165 // Copy the configuration
166 r = nw_config_copy(config, &p->config);
167 if (r)
168 goto ERROR;
169
170 // Setup the port
171 r = nw_port_setup(p);
172 if (r)
173 goto ERROR;
174
175 *port = p;
176 return 0;
177
178 ERROR:
179 nw_port_free(p);
180 return r;
181 }
182
183 int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
184 const char* name, const char* path) {
185 nw_config* config = NULL;
186 int r;
187
188 // Initialize the configuration
189 r = nw_config_create(&config, path);
190 if (r)
191 goto ERROR;
192
193 // Fetch the type
194 const char* type = nw_config_get(config, "TYPE");
195 if (!type) {
196 ERROR("Port configuration %s has no TYPE\n", path);
197 r = 1;
198 goto ERROR;
199 }
200
201 // Create a new port
202 r = nw_port_create(port, daemon, nw_port_type_id_from_string(type), name, config);
203 if (r)
204 goto ERROR;
205
206 ERROR:
207 if (config)
208 nw_config_unref(config);
209
210 return r;
211 }
212
213 nw_port* nw_port_ref(nw_port* port) {
214 port->nrefs++;
215
216 return port;
217 }
218
219 nw_port* nw_port_unref(nw_port* port) {
220 if (--port->nrefs > 0)
221 return port;
222
223 nw_port_free(port);
224 return NULL;
225 }
226
227 /*
228 This is a helper function for when we pass a reference to the event loop
229 it will have to dereference the port instance later.
230 */
231 static void __nw_port_unref(void* data) {
232 nw_port* port = (nw_port*)data;
233
234 nw_port_unref(port);
235 }
236
237 int nw_port_destroy(nw_port* port) {
238 int r;
239
240 DEBUG("Destroying port %s\n", port->name);
241
242 // Destroy the physical link (if exists)
243 if (port->link) {
244 r = nw_link_destroy(port->link);
245 if (r)
246 return r;
247 }
248
249 // Dereference the port from other ports
250 r = nw_daemon_ports_walk(port->daemon, __nw_port_drop_port, port);
251 if (r)
252 return r;
253
254 // Dereference the port from other zones
255 r = nw_daemon_zones_walk(port->daemon, __nw_zone_drop_port, port);
256 if (r)
257 return r;
258
259 // Destroy the configuration
260 r = nw_config_destroy(port->config);
261 if (r)
262 return r;
263
264 return 0;
265 }
266
267 int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
268 nw_port* dropped_port = (nw_port*)data;
269 int r;
270
271 switch (port->type->id) {
272 case NW_PORT_VLAN:
273 if (port->vlan.parent == dropped_port) {
274 r = nw_port_set_vlan_parent(port, NULL);
275 if (r)
276 return r;
277 }
278 break;
279
280 case NW_PORT_BONDING:
281 case NW_PORT_DUMMY:
282 break;
283 }
284
285 return 0;
286 }
287
288 int nw_port_save(nw_port* port) {
289 int r;
290
291 // Write out the configuration
292 r = nw_config_options_write(port->config);
293 if (r < 0)
294 goto ERROR;
295
296 // Write the configuration
297 r = nw_config_write(port->config);
298 if (r)
299 return r;
300
301 return 0;
302
303 ERROR:
304 ERROR("Could not save configuration for port %s: %s\n", port->name, strerror(-r));
305
306 return 1;
307 }
308
309 const char* nw_port_name(nw_port* port) {
310 return port->name;
311 }
312
313 char* nw_port_bus_path(nw_port* port) {
314 char* p = NULL;
315 int r;
316
317 // Encode the bus path
318 r = sd_bus_path_encode("/org/ipfire/network1/port", port->name, &p);
319 if (r < 0)
320 return NULL;
321
322 return p;
323 }
324
325 int __nw_port_set_link(nw_daemon* daemon, nw_port* port, void* data) {
326 nw_link* link = (nw_link*)data;
327
328 // Fetch the link name
329 const char* ifname = nw_link_ifname(link);
330 if (!ifname) {
331 ERROR("Link does not have a name set\n");
332 return 1;
333 }
334
335 // Set link if the name matches
336 if (strcmp(port->name, ifname) == 0)
337 return nw_port_set_link(port, link);
338
339 // If we have the link set but the name did not match, we will remove it
340 else if (port->link == link)
341 return nw_port_set_link(port, NULL);
342
343 return 0;
344 }
345
346 int __nw_port_drop_link(nw_daemon* daemon, nw_port* port, void* data) {
347 nw_link* link = (nw_link*)data;
348
349 // Drop the link if it matches
350 if (port->link == link)
351 return nw_port_set_link(port, NULL);
352
353 return 0;
354 }
355
356 static nw_link* nw_port_get_link(nw_port* port) {
357 // Fetch the link if not set
358 if (!port->link)
359 port->link = nw_daemon_get_link_by_name(port->daemon, port->name);
360
361 if (!port->link)
362 return NULL;
363
364 return nw_link_ref(port->link);
365 }
366
367 const nw_address_t* nw_port_get_address(nw_port* port) {
368 return &port->address;
369 }
370
371 static int nw_port_is_disabled(nw_port* port) {
372 return nw_config_get_bool(port->config, "DISABLED");
373 }
374
375 nw_port* nw_port_get_parent_port(nw_port* port) {
376 if (!NW_PORT_TYPE(port)->get_parent_port)
377 return NULL;
378
379 return NW_PORT_TYPE(port)->get_parent_port(port);
380 }
381
382 static nw_link* nw_port_get_parent_link(nw_port* port) {
383 nw_port* parent = NULL;
384 nw_link* link = NULL;
385
386 // Fetch the parent
387 parent = nw_port_get_parent_port(port);
388 if (!parent)
389 return NULL;
390
391 // Fetch the link
392 link = nw_port_get_link(parent);
393
394 // Cleanup
395 if (parent)
396 nw_port_unref(parent);
397
398 return link;
399 }
400
401 static int __nw_port_create_link(sd_netlink* rtnl, sd_netlink_message* m, void* data) {
402 nw_port* port = (nw_port*)data;
403 int r;
404
405 // Check if the operation was successful
406 r = sd_netlink_message_get_errno(m);
407 if (r < 0) {
408 ERROR("Could not create port %s: %s\n", port->name, strerror(-r));
409 // XXX We should extract the error message
410
411 return 0;
412 }
413
414 DEBUG("Successfully created %s\n", port->name);
415
416 return 0;
417 }
418
419 static int nw_port_create_link(nw_port* port) {
420 sd_netlink_message* m = NULL;
421 nw_link* link = NULL;
422 int r;
423
424 sd_netlink* rtnl = nw_daemon_get_rtnl(port->daemon);
425
426 DEBUG("Creating port %s...\n", port->name);
427
428 // Check the kind
429 if (!NW_PORT_TYPE(port)->kind) {
430 ERROR("Port type has no kind\n");
431 r = -ENOTSUP;
432 goto ERROR;
433 }
434
435 // Create a new link
436 r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
437 if (r < 0) {
438 ERROR("Could not create netlink message: %m\n");
439 goto ERROR;
440 }
441
442 // Set the name
443 r = sd_netlink_message_append_string(m, IFLA_IFNAME, port->name);
444 if (r < 0) {
445 ERROR("Could not set port name: %s\n", strerror(-r));
446 goto ERROR;
447 }
448
449 // XXX Set common things like MTU, etc.
450
451 // Set Ethernet address
452 r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &port->address);
453 if (r < 0) {
454 ERROR("Could not set MAC address: %s\n", strerror(-r));
455 goto ERROR;
456 }
457
458 // Fetch the parent link
459 link = nw_port_get_parent_link(port);
460 if (link) {
461 r = sd_netlink_message_append_u32(m, IFLA_LINK, nw_link_ifindex(link));
462 if (r < 0)
463 goto ERROR;
464 }
465
466 // Open an IFLA_LINKINFO container
467 r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
468 if (r < 0)
469 goto ERROR;
470
471 // Run the custom setup
472 if (NW_PORT_TYPE(port)->create_link) {
473 r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, NW_PORT_TYPE(port)->kind);
474 if (r < 0) {
475 ERROR("Could not open IFLA_INFO_DATA container: %s\n", strerror(-r));
476 goto ERROR;
477 }
478
479 r = NW_PORT_TYPE(port)->create_link(port, m);
480 if (r) {
481 ERROR("Could not create port %s: %m\n", port->name);
482 goto ERROR;
483 }
484
485 // Close the container
486 r = sd_netlink_message_close_container(m);
487 if (r < 0)
488 goto ERROR;
489
490 // Just set IFLA_INFO_KIND if there is no custom function
491 } else {
492 r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, NW_PORT_TYPE(port)->kind);
493 if (r < 0)
494 goto ERROR;
495 }
496
497 // Close the container
498 r = sd_netlink_message_close_container(m);
499 if (r < 0)
500 goto ERROR;
501
502 // Send the message
503 r = sd_netlink_call_async(rtnl, NULL, m, __nw_port_create_link,
504 __nw_port_unref, nw_port_ref(port), -1, NULL);
505 if (r < 0) {
506 ERROR("Could not send netlink message: %s\n", strerror(-r));
507 goto ERROR;
508 }
509
510 r = 0;
511
512 ERROR:
513 if (m)
514 sd_netlink_message_unref(m);
515 if (link)
516 nw_link_unref(link);
517
518 return r;
519 }
520
521 int nw_port_reconfigure(nw_port* port) {
522 int r;
523
524 // If the port is disabled, we will try to destroy it
525 if (nw_port_is_disabled(port)) {
526 if (port->link) {
527 r = nw_link_destroy(port->link);
528 if (r)
529 return r;
530 }
531
532 return 0;
533 }
534
535 // If there is no link, we will try to create it
536 if (!port->link)
537 return nw_port_create_link(port);
538
539 // XXX TODO
540
541 return 0;
542 }
543
544 int nw_port_has_carrier(nw_port* port) {
545 if (!port->link)
546 return 0;
547
548 return nw_link_has_carrier(port->link);
549 }
550
551 /*
552 Stats
553 */
554
555 const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port) {
556 if (!port->link)
557 return NULL;
558
559 return nw_link_get_stats64(port->link);
560 }
561
562 int __nw_port_update_stats(nw_daemon* daemon, nw_port* port, void* data) {
563 nw_link* link = (nw_link*)data;
564
565 // Emit stats if link matches
566 if (port->link == link)
567 return nw_stats_collector_emit_port_stats(daemon, port);
568
569 return 0;
570 }
571
572 int nw_port_update_stats(nw_port* port) {
573 if (port->link)
574 return nw_link_update_stats(port->link);
575
576 return 0;
577 }
578
579 int nw_port_check_type(nw_port* port, const nw_port_type_id_t type) {
580 if (port->type->id == type)
581 return 0;
582
583 errno = ENOTSUP;
584 return -errno;
585 }
586
587 /*
588 JSON
589 */
590 int nw_port_to_json(nw_port* port, struct json_object** object) {
591 char* address = NULL;
592 int r;
593
594 // Create a new JSON object
595 struct json_object* o = json_object_new_object();
596 if (!o) {
597 r = -errno;
598 goto ERROR;
599 }
600
601 // Add name
602 r = json_object_add_string(o, "Name", port->name);
603 if (r < 0)
604 goto ERROR;
605
606 // Add Type
607 r = json_object_add_string(o, "Type", nw_port_type_id_to_string(port->type->id));
608 if (r < 0)
609 goto ERROR;
610
611 // Add address
612 address = nw_address_to_string(&port->address);
613 if (address) {
614 r = json_object_add_string(o, "Address", address);
615 if (r < 0)
616 goto ERROR;
617 }
618
619 // Call custom stuff
620 if (NW_PORT_TYPE(port)->to_json) {
621 r = NW_PORT_TYPE(port)->to_json(port, o);
622 if (r < 0)
623 goto ERROR;
624 }
625
626 // Success
627 r = 0;
628
629 // Return a reference to the created object
630 *object = json_object_ref(o);
631
632 ERROR:
633 if (address)
634 free(address);
635 if (o)
636 json_object_unref(o);
637
638 return r;
639 }