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