]> git.ipfire.org Git - people/ms/network.git/blame - src/networkd/port.c
ports: Drop UNKNOWN type
[people/ms/network.git] / src / networkd / port.c
CommitLineData
abeab069
MT
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
827435c8 21#include <limits.h>
abeab069 22#include <net/if.h>
2e7f4705 23#include <stdint.h>
abeab069
MT
24#include <stdlib.h>
25
7297ba7f
MT
26#include <systemd/sd-bus.h>
27
2e7f4705 28#include "address.h"
abeab069 29#include "config.h"
e9b0614e 30#include "json.h"
30d4ab67 31#include "link.h"
827435c8 32#include "logging.h"
abeab069 33#include "port.h"
95c5dca2 34#include "port-bonding.h"
9e8af30e 35#include "port-dummy.h"
ff886975 36#include "port-vlan.h"
15240e08
MT
37#include "stats-collector.h"
38#include "string.h"
abeab069 39
c464b5d1 40static const nw_string_table_t nw_port_type_id[] = {
95c5dca2 41 { NW_PORT_BONDING, "bonding" },
c7761af8
MT
42 { NW_PORT_DUMMY, "dummy" },
43 { NW_PORT_VLAN, "vlan" },
c464b5d1 44 { -1, NULL },
827435c8
MT
45};
46
c464b5d1 47NW_STRING_TABLE_LOOKUP(nw_port_type_id_t, nw_port_type_id)
827435c8 48
2361667e 49static void nw_port_free(nw_port* port) {
02801f0d
MT
50 if (port->link)
51 nw_link_unref(port->link);
abeab069
MT
52 if (port->config)
53 nw_config_unref(port->config);
30d4ab67
MT
54 if (port->daemon)
55 nw_daemon_unref(port->daemon);
abeab069
MT
56
57 free(port);
58}
59
611d4aca
MT
60static 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
2361667e 85static int nw_port_setup(nw_port* port) {
611d4aca 86 nw_link* link = NULL;
827435c8
MT
87 int r;
88
02801f0d 89 // Find the link
611d4aca
MT
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;
02801f0d
MT
95 }
96
082d81a3
MT
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));
611d4aca 101 goto ERROR;
082d81a3 102 }
827435c8 103
082d81a3
MT
104 // Setup options
105 r = NW_CONFIG_OPTION_ADDRESS(port->config, "ADDRESS", &port->address);
106 if (r < 0)
611d4aca 107 goto ERROR;
827435c8
MT
108
109 // Call any custom initialization
c464b5d1
MT
110 if (NW_PORT_TYPE(port)->setup) {
111 r = NW_PORT_TYPE(port)->setup(port);
082d81a3 112 if (r < 0)
ff886975 113 goto ERROR;
827435c8
MT
114 }
115
082d81a3
MT
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
611d4aca
MT
123ERROR:
124 if (link)
125 nw_link_unref(link);
126
127 return r;
827435c8
MT
128}
129
082d81a3 130int nw_port_create(nw_port** port, nw_daemon* daemon,
c464b5d1 131 const nw_port_type_id_t type, const char* name, nw_config* config) {
abeab069
MT
132 int r;
133
134 // Allocate a new object
2361667e 135 nw_port* p = calloc(1, sizeof(*p));
abeab069
MT
136 if (!p)
137 return 1;
138
30d4ab67
MT
139 // Store a reference to the daemon
140 p->daemon = nw_daemon_ref(daemon);
141
abeab069
MT
142 // Initialize reference counter
143 p->nrefs = 1;
144
9e8af30e 145 // Set operations
c464b5d1 146 switch (type) {
95c5dca2 147 case NW_PORT_BONDING:
c464b5d1 148 p->type = &nw_port_type_bonding;
95c5dca2
MT
149 break;
150
9e8af30e 151 case NW_PORT_DUMMY:
c464b5d1 152 p->type = &nw_port_type_dummy;
9e8af30e 153 break;
ff886975
MT
154
155 case NW_PORT_VLAN:
c464b5d1 156 p->type = &nw_port_type_vlan;
ff886975 157 break;
9e8af30e
MT
158 }
159
abeab069
MT
160 // Store the name
161 r = nw_string_set(p->name, name);
162 if (r)
163 goto ERROR;
164
082d81a3
MT
165 // Copy the configuration
166 r = nw_config_copy(config, &p->config);
167 if (r)
168 goto ERROR;
169
827435c8
MT
170 // Setup the port
171 r = nw_port_setup(p);
172 if (r)
173 goto ERROR;
174
abeab069
MT
175 *port = p;
176 return 0;
177
178ERROR:
179 nw_port_free(p);
180 return r;
181}
182
06bc93d3
MT
183int 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
c464b5d1 202 r = nw_port_create(port, daemon, nw_port_type_id_from_string(type), name, config);
06bc93d3
MT
203 if (r)
204 goto ERROR;
205
206ERROR:
207 if (config)
208 nw_config_unref(config);
209
210 return r;
211}
212
2361667e 213nw_port* nw_port_ref(nw_port* port) {
abeab069
MT
214 port->nrefs++;
215
216 return port;
217}
218
2361667e 219nw_port* nw_port_unref(nw_port* port) {
abeab069
MT
220 if (--port->nrefs > 0)
221 return port;
222
223 nw_port_free(port);
224 return NULL;
225}
226
240e331b
MT
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*/
231static void __nw_port_unref(void* data) {
232 nw_port* port = (nw_port*)data;
233
234 nw_port_unref(port);
235}
236
c403eb4c
MT
237int 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
c403eb4c
MT
264 return 0;
265}
266
267int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
268 nw_port* dropped_port = (nw_port*)data;
269 int r;
270
c464b5d1 271 switch (port->type->id) {
c403eb4c
MT
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
95c5dca2 280 case NW_PORT_BONDING:
c403eb4c 281 case NW_PORT_DUMMY:
c403eb4c
MT
282 break;
283 }
284
285 return 0;
286}
287
605e975f
MT
288int nw_port_save(nw_port* port) {
289 int r;
290
082d81a3
MT
291 // Write out the configuration
292 r = nw_config_options_write(port->config);
293 if (r < 0)
294 goto ERROR;
c7761af8 295
ff886975 296 // Write the configuration
605e975f
MT
297 r = nw_config_write(port->config);
298 if (r)
299 return r;
300
301 return 0;
c7761af8
MT
302
303ERROR:
082d81a3 304 ERROR("Could not save configuration for port %s: %s\n", port->name, strerror(-r));
c7761af8
MT
305
306 return 1;
605e975f
MT
307}
308
2361667e 309const char* nw_port_name(nw_port* port) {
abeab069
MT
310 return port->name;
311}
7297ba7f 312
2361667e 313char* nw_port_bus_path(nw_port* port) {
7297ba7f
MT
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}
4a383286 324
611d4aca
MT
325int __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
346int __nw_port_drop_link(nw_daemon* daemon, nw_port* port, void* data) {
347 nw_link* link = (nw_link*)data;
02801f0d 348
611d4aca
MT
349 // Drop the link if it matches
350 if (port->link == link)
351 return nw_port_set_link(port, NULL);
352
353 return 0;
30d4ab67
MT
354}
355
240e331b
MT
356static 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
2361667e 367const nw_address_t* nw_port_get_address(nw_port* port) {
4a383286
MT
368 return &port->address;
369}
20375a08 370
371b836a
MT
371static int nw_port_is_disabled(nw_port* port) {
372 return nw_config_get_bool(port->config, "DISABLED");
373}
374
5a5c346c 375nw_port* nw_port_get_parent_port(nw_port* port) {
c464b5d1 376 if (!NW_PORT_TYPE(port)->get_parent_port)
5a5c346c
MT
377 return NULL;
378
c464b5d1 379 return NW_PORT_TYPE(port)->get_parent_port(port);
5a5c346c
MT
380}
381
240e331b
MT
382static nw_link* nw_port_get_parent_link(nw_port* port) {
383 nw_port* parent = NULL;
384 nw_link* link = NULL;
385
240e331b 386 // Fetch the parent
5a5c346c 387 parent = nw_port_get_parent_port(port);
240e331b 388 if (!parent)
5a5c346c 389 return NULL;
240e331b
MT
390
391 // Fetch the link
392 link = nw_port_get_link(parent);
393
5a5c346c 394 // Cleanup
240e331b
MT
395 if (parent)
396 nw_port_unref(parent);
397
398 return link;
399}
400
401static 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
96b1b84d 419static int nw_port_create_link(nw_port* port) {
240e331b
MT
420 sd_netlink_message* m = NULL;
421 nw_link* link = NULL;
ff886975
MT
422 int r;
423
240e331b
MT
424 sd_netlink* rtnl = nw_daemon_get_rtnl(port->daemon);
425
575d1a3f
MT
426 DEBUG("Creating port %s...\n", port->name);
427
c464b5d1
MT
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
240e331b
MT
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;
ff886975
MT
447 }
448
1574bc00
MT
449 // XXX Set common things like MTU, etc.
450
d667fff4 451 // Set Ethernet address
1574bc00
MT
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 }
240e331b
MT
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
c464b5d1
MT
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);
240e331b
MT
474 if (r < 0) {
475 ERROR("Could not open IFLA_INFO_DATA container: %s\n", strerror(-r));
476 goto ERROR;
477 }
478
c464b5d1 479 r = NW_PORT_TYPE(port)->create_link(port, m);
240e331b
MT
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 {
c464b5d1 492 r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, NW_PORT_TYPE(port)->kind);
240e331b
MT
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
512ERROR:
513 if (m)
514 sd_netlink_message_unref(m);
515 if (link)
516 nw_link_unref(link);
ff886975
MT
517
518 return r;
96b1b84d
MT
519}
520
b9769b09 521int nw_port_reconfigure(nw_port* port) {
371b836a
MT
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
96b1b84d 535 // If there is no link, we will try to create it
895f7134
MT
536 if (!port->link)
537 return nw_port_create_link(port);
96b1b84d 538
371b836a 539 // XXX TODO
96b1b84d
MT
540
541 return 0;
b9769b09
MT
542}
543
20375a08 544int nw_port_has_carrier(nw_port* port) {
02801f0d
MT
545 if (!port->link)
546 return 0;
20375a08 547
02801f0d 548 return nw_link_has_carrier(port->link);
20375a08 549}
15240e08
MT
550
551/*
552 Stats
553*/
554
555const 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
562int __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
572int nw_port_update_stats(nw_port* port) {
573 if (port->link)
574 return nw_link_update_stats(port->link);
575
576 return 0;
577}
c7761af8 578
c464b5d1
MT
579int nw_port_check_type(nw_port* port, const nw_port_type_id_t type) {
580 if (port->type->id == type)
c7761af8
MT
581 return 0;
582
583 errno = ENOTSUP;
584 return -errno;
585}
e9b0614e
MT
586
587/*
588 JSON
589*/
590int 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
c464b5d1 607 r = json_object_add_string(o, "Type", nw_port_type_id_to_string(port->type->id));
e9b0614e
MT
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
c464b5d1
MT
620 if (NW_PORT_TYPE(port)->to_json) {
621 r = NW_PORT_TYPE(port)->to_json(port, o);
e9b0614e
MT
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
632ERROR:
633 if (address)
634 free(address);
635 if (o)
636 json_object_unref(o);
637
638 return r;
639}