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