]>
Commit | Line | Data |
---|---|---|
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 | |
36 | struct 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 |
53 | static 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 | ||
61 | static 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 | 72 | static 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 | 83 | static 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 | ||
109 | ERROR: | |
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 | 120 | static 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 |
131 | static 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 | 156 | static 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 |
192 | ERROR: |
193 | if (link) | |
194 | nw_link_unref(link); | |
195 | ||
196 | return r; | |
827435c8 MT |
197 | } |
198 | ||
06bc93d3 | 199 | int 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 | ||
229 | ERROR: | |
230 | nw_port_free(p); | |
231 | return r; | |
232 | } | |
233 | ||
06bc93d3 MT |
234 | int 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 | ||
257 | ERROR: | |
258 | if (config) | |
259 | nw_config_unref(config); | |
260 | ||
261 | return r; | |
262 | } | |
263 | ||
2361667e | 264 | nw_port* nw_port_ref(nw_port* port) { |
abeab069 MT |
265 | port->nrefs++; |
266 | ||
267 | return port; | |
268 | } | |
269 | ||
2361667e | 270 | nw_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 |
278 | int 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 | 288 | const char* nw_port_name(nw_port* port) { |
abeab069 MT |
289 | return port->name; |
290 | } | |
7297ba7f | 291 | |
2361667e | 292 | char* 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 |
304 | int __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 | ||
325 | int __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 | 335 | const nw_address_t* nw_port_get_address(nw_port* port) { |
4a383286 MT |
336 | return &port->address; |
337 | } | |
20375a08 | 338 | |
371b836a MT |
339 | static int nw_port_is_disabled(nw_port* port) { |
340 | return nw_config_get_bool(port->config, "DISABLED"); | |
341 | } | |
342 | ||
96b1b84d MT |
343 | static int nw_port_create_link(nw_port* port) { |
344 | return 0; // XXX TODO | |
345 | } | |
346 | ||
b9769b09 | 347 | int 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 | 373 | int 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 | ||
384 | const 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 | ||
391 | int __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 | ||
401 | int nw_port_update_stats(nw_port* port) { | |
402 | if (port->link) | |
403 | return nw_link_update_stats(port->link); | |
404 | ||
405 | return 0; | |
406 | } |