]> git.ipfire.org Git - people/ms/network.git/blame - src/networkd/port.c
ports: Require type to be set at all times
[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"
15240e08
MT
33#include "stats-collector.h"
34#include "string.h"
abeab069
MT
35
36struct nw_port {
2361667e 37 nw_daemon* daemon;
abeab069
MT
38 int nrefs;
39
02801f0d
MT
40 // Link
41 nw_link* link;
42
abeab069 43 char name[IF_NAMESIZE];
827435c8 44 nw_port_type_t type;
abeab069
MT
45
46 // Configuration
2361667e 47 nw_config *config;
2e7f4705
MT
48
49 // Common attributes
50 nw_address_t address;
abeab069
MT
51};
52
827435c8
MT
53static const struct nw_port_type_map {
54 nw_port_type_t type;
55 const char* name;
56} nw_port_type_map[] = {
57 { NW_PORT_DUMMY, "dummy" },
58 { NW_PORT_UNKNOWN, NULL },
59};
60
61static nw_port_type_t nw_port_type_from_string(const char* s) {
62 const struct nw_port_type_map* map = NULL;
63
64 for (map = nw_port_type_map; *map->name; map++) {
65 if (strcmp(map->name, s) == 0)
66 return map->type;
67 }
68
69 return NW_PORT_UNKNOWN;
70}
71
2361667e 72static void nw_port_free(nw_port* port) {
02801f0d
MT
73 if (port->link)
74 nw_link_unref(port->link);
abeab069
MT
75 if (port->config)
76 nw_config_unref(port->config);
30d4ab67
MT
77 if (port->daemon)
78 nw_daemon_unref(port->daemon);
abeab069
MT
79
80 free(port);
81}
82
2361667e 83static int nw_port_setup_address(nw_port* port) {
2e7f4705
MT
84 int r;
85
86 // Read ADDRESS from configuration
87 const char* s = nw_config_get(port->config, "ADDRESS");
88 if (!s) {
89 ERROR("Port %s: Address isn't set\n", port->name);
9a93da54 90 goto ERROR;
2e7f4705
MT
91 }
92
93 // Parse the address
94 r = nw_address_from_string(&port->address, s);
95 if (r) {
96 ERROR("Port %s: Could not parse address: %m\n", port->name);
9a93da54 97 goto ERROR;
2e7f4705
MT
98 }
99
d81d9cf5
MT
100 // Check if this address is usable
101 r = nw_address_is_multicast(&port->address);
102 if (r) {
103 DEBUG("Port %s: Multicast bit is set on Ethernet address\n", port->name);
104 goto ERROR;
105 }
2e7f4705 106
9a93da54
MT
107 return 0;
108
109ERROR:
110 // Generate a random Ethernet address
111 r = nw_address_generate(&port->address);
112 if (r) {
113 ERROR("Could not generate a random Ethernet address: %m\n");
114 return r;
115 }
116
2e7f4705
MT
117 return 0;
118}
119
2361667e 120static int nw_port_setup_common(nw_port* port) {
2e7f4705
MT
121 int r;
122
123 // Address
124 r = nw_port_setup_address(port);
125 if (r)
126 return r;
127
128 return 0;
827435c8
MT
129}
130
611d4aca
MT
131static int nw_port_set_link(nw_port* port, nw_link* link) {
132 // Do nothing if the same link is being re-assigned
133 if (port->link == link)
134 return 0;
135
136 // Dereference the former link if set
137 if (port->link)
138 nw_link_unref(port->link);
139
140 // Store the new link
141 if (link) {
142 port->link = nw_link_ref(link);
143
144 DEBUG("Port %s: Assigned link %d\n", port->name, nw_link_ifindex(port->link));
145
146 // Or clear the pointer if no link has been provided
147 } else {
148 port->link = NULL;
149
150 DEBUG("Port %s: Removed link\n", port->name);
151 }
152
153 return 0;
154}
155
2361667e 156static int nw_port_setup(nw_port* port) {
611d4aca 157 nw_link* link = NULL;
827435c8
MT
158 char path[PATH_MAX];
159 int r;
160
02801f0d 161 // Find the link
611d4aca
MT
162 link = nw_daemon_get_link_by_name(port->daemon, port->name);
163 if (link) {
164 r = nw_port_set_link(port, link);
165 if (r)
166 goto ERROR;
02801f0d
MT
167 }
168
827435c8
MT
169 // Compose the path to the main configuration file
170 r = nw_path_join(path, PORT_CONFIG_DIR, port->name);
171 if (r)
611d4aca 172 goto ERROR;
827435c8
MT
173
174 // Initialize the configuration
175 r = nw_config_create(&port->config, path);
176 if (r)
611d4aca 177 goto ERROR;
827435c8 178
827435c8
MT
179 // Perform some common initialization
180 r = nw_port_setup_common(port);
181 if (r)
611d4aca 182 goto ERROR;
827435c8
MT
183
184 // Call any custom initialization
185 switch (port->type) {
186 // These do not need any special initialization
187 case NW_PORT_DUMMY:
188 case NW_PORT_UNKNOWN:
189 break;
190 }
191
611d4aca
MT
192ERROR:
193 if (link)
194 nw_link_unref(link);
195
196 return r;
827435c8
MT
197}
198
06bc93d3 199int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const char* name) {
abeab069
MT
200 int r;
201
202 // Allocate a new object
2361667e 203 nw_port* p = calloc(1, sizeof(*p));
abeab069
MT
204 if (!p)
205 return 1;
206
30d4ab67
MT
207 // Store a reference to the daemon
208 p->daemon = nw_daemon_ref(daemon);
209
abeab069
MT
210 // Initialize reference counter
211 p->nrefs = 1;
212
06bc93d3
MT
213 // Store the type
214 p->type = type;
215
abeab069
MT
216 // Store the name
217 r = nw_string_set(p->name, name);
218 if (r)
219 goto ERROR;
220
827435c8
MT
221 // Setup the port
222 r = nw_port_setup(p);
223 if (r)
224 goto ERROR;
225
abeab069
MT
226 *port = p;
227 return 0;
228
229ERROR:
230 nw_port_free(p);
231 return r;
232}
233
06bc93d3
MT
234int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
235 const char* name, const char* path) {
236 nw_config* config = NULL;
237 int r;
238
239 // Initialize the configuration
240 r = nw_config_create(&config, path);
241 if (r)
242 goto ERROR;
243
244 // Fetch the type
245 const char* type = nw_config_get(config, "TYPE");
246 if (!type) {
247 ERROR("Port configuration %s has no TYPE\n", path);
248 r = 1;
249 goto ERROR;
250 }
251
252 // Create a new port
253 r = nw_port_create(port, daemon, nw_port_type_from_string(type), name);
254 if (r)
255 goto ERROR;
256
257ERROR:
258 if (config)
259 nw_config_unref(config);
260
261 return r;
262}
263
2361667e 264nw_port* nw_port_ref(nw_port* port) {
abeab069
MT
265 port->nrefs++;
266
267 return port;
268}
269
2361667e 270nw_port* nw_port_unref(nw_port* port) {
abeab069
MT
271 if (--port->nrefs > 0)
272 return port;
273
274 nw_port_free(port);
275 return NULL;
276}
277
605e975f
MT
278int nw_port_save(nw_port* port) {
279 int r;
280
281 r = nw_config_write(port->config);
282 if (r)
283 return r;
284
285 return 0;
286}
287
2361667e 288const char* nw_port_name(nw_port* port) {
abeab069
MT
289 return port->name;
290}
7297ba7f 291
2361667e 292char* nw_port_bus_path(nw_port* port) {
7297ba7f
MT
293 char* p = NULL;
294 int r;
295
296 // Encode the bus path
297 r = sd_bus_path_encode("/org/ipfire/network1/port", port->name, &p);
298 if (r < 0)
299 return NULL;
300
301 return p;
302}
4a383286 303
611d4aca
MT
304int __nw_port_set_link(nw_daemon* daemon, nw_port* port, void* data) {
305 nw_link* link = (nw_link*)data;
306
307 // Fetch the link name
308 const char* ifname = nw_link_ifname(link);
309 if (!ifname) {
310 ERROR("Link does not have a name set\n");
311 return 1;
312 }
313
314 // Set link if the name matches
315 if (strcmp(port->name, ifname) == 0)
316 return nw_port_set_link(port, link);
317
318 // If we have the link set but the name did not match, we will remove it
319 else if (port->link == link)
320 return nw_port_set_link(port, NULL);
321
322 return 0;
323}
324
325int __nw_port_drop_link(nw_daemon* daemon, nw_port* port, void* data) {
326 nw_link* link = (nw_link*)data;
02801f0d 327
611d4aca
MT
328 // Drop the link if it matches
329 if (port->link == link)
330 return nw_port_set_link(port, NULL);
331
332 return 0;
30d4ab67
MT
333}
334
2361667e 335const nw_address_t* nw_port_get_address(nw_port* port) {
4a383286
MT
336 return &port->address;
337}
20375a08 338
371b836a
MT
339static int nw_port_is_disabled(nw_port* port) {
340 return nw_config_get_bool(port->config, "DISABLED");
341}
342
96b1b84d
MT
343static int nw_port_create_link(nw_port* port) {
344 return 0; // XXX TODO
345}
346
b9769b09 347int nw_port_reconfigure(nw_port* port) {
371b836a
MT
348 int r;
349
350 // If the port is disabled, we will try to destroy it
351 if (nw_port_is_disabled(port)) {
352 if (port->link) {
353 r = nw_link_destroy(port->link);
354 if (r)
355 return r;
356 }
357
358 return 0;
359 }
360
96b1b84d
MT
361 // If there is no link, we will try to create it
362 if (!port->link) {
363 r = nw_port_create_link(port);
364 if (r)
365 return r;
366 }
367
371b836a 368 // XXX TODO
96b1b84d
MT
369
370 return 0;
b9769b09
MT
371}
372
20375a08 373int nw_port_has_carrier(nw_port* port) {
02801f0d
MT
374 if (!port->link)
375 return 0;
20375a08 376
02801f0d 377 return nw_link_has_carrier(port->link);
20375a08 378}
15240e08
MT
379
380/*
381 Stats
382*/
383
384const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port) {
385 if (!port->link)
386 return NULL;
387
388 return nw_link_get_stats64(port->link);
389}
390
391int __nw_port_update_stats(nw_daemon* daemon, nw_port* port, void* data) {
392 nw_link* link = (nw_link*)data;
393
394 // Emit stats if link matches
395 if (port->link == link)
396 return nw_stats_collector_emit_port_stats(daemon, port);
397
398 return 0;
399}
400
401int nw_port_update_stats(nw_port* port) {
402 if (port->link)
403 return nw_link_update_stats(port->link);
404
405 return 0;
406}