]>
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" |
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 | 40 | static 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 | 47 | NW_STRING_TABLE_LOOKUP(nw_port_type_id_t, nw_port_type_id) |
827435c8 | 48 | |
2361667e | 49 | static 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 |
60 | static 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 | 85 | static 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 |
123 | ERROR: |
124 | if (link) | |
125 | nw_link_unref(link); | |
126 | ||
127 | return r; | |
827435c8 MT |
128 | } |
129 | ||
ea7dc1bb MT |
130 | static int nw_port_validate(nw_port* port) { |
131 | int r = 0; | |
132 | ||
133 | // Validate the port configuration | |
134 | if (NW_PORT_TYPE(port)->validate) { | |
135 | r = NW_PORT_TYPE(port)->validate(port); | |
136 | if (r < 0) | |
137 | ERROR("Could not check configuration for %s: %s\n", port->name, strerror(-r)); | |
138 | } | |
139 | ||
140 | return r; | |
141 | } | |
142 | ||
082d81a3 | 143 | int nw_port_create(nw_port** port, nw_daemon* daemon, |
c464b5d1 | 144 | const nw_port_type_id_t type, const char* name, nw_config* config) { |
abeab069 MT |
145 | int r; |
146 | ||
147 | // Allocate a new object | |
2361667e | 148 | nw_port* p = calloc(1, sizeof(*p)); |
abeab069 | 149 | if (!p) |
8edf3da1 | 150 | return -errno; |
abeab069 | 151 | |
30d4ab67 MT |
152 | // Store a reference to the daemon |
153 | p->daemon = nw_daemon_ref(daemon); | |
154 | ||
abeab069 MT |
155 | // Initialize reference counter |
156 | p->nrefs = 1; | |
157 | ||
9e8af30e | 158 | // Set operations |
c464b5d1 | 159 | switch (type) { |
95c5dca2 | 160 | case NW_PORT_BONDING: |
c464b5d1 | 161 | p->type = &nw_port_type_bonding; |
95c5dca2 MT |
162 | break; |
163 | ||
9e8af30e | 164 | case NW_PORT_DUMMY: |
c464b5d1 | 165 | p->type = &nw_port_type_dummy; |
9e8af30e | 166 | break; |
ff886975 MT |
167 | |
168 | case NW_PORT_VLAN: | |
c464b5d1 | 169 | p->type = &nw_port_type_vlan; |
ff886975 | 170 | break; |
9e8af30e MT |
171 | } |
172 | ||
abeab069 MT |
173 | // Store the name |
174 | r = nw_string_set(p->name, name); | |
8edf3da1 | 175 | if (r < 0) |
abeab069 MT |
176 | goto ERROR; |
177 | ||
082d81a3 MT |
178 | // Copy the configuration |
179 | r = nw_config_copy(config, &p->config); | |
8edf3da1 | 180 | if (r < 0) |
082d81a3 MT |
181 | goto ERROR; |
182 | ||
827435c8 MT |
183 | // Setup the port |
184 | r = nw_port_setup(p); | |
8edf3da1 | 185 | if (r < 0) |
827435c8 MT |
186 | goto ERROR; |
187 | ||
ea7dc1bb MT |
188 | // Validate the configuration |
189 | r = nw_port_validate(p); | |
190 | switch (r) { | |
191 | // Configuration is valid | |
192 | case 0: | |
193 | break; | |
194 | ||
195 | // Configuration is invalid | |
196 | case 1: | |
197 | ERROR("%s: Invalid configuration\n", p->name); | |
198 | goto ERROR; | |
199 | ||
200 | // Error | |
201 | default: | |
202 | goto ERROR; | |
203 | } | |
204 | ||
abeab069 MT |
205 | *port = p; |
206 | return 0; | |
207 | ||
208 | ERROR: | |
209 | nw_port_free(p); | |
210 | return r; | |
211 | } | |
212 | ||
8edf3da1 | 213 | int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name) { |
06bc93d3 | 214 | nw_config* config = NULL; |
8edf3da1 MT |
215 | FILE* f = NULL; |
216 | char path[PATH_MAX]; | |
06bc93d3 MT |
217 | int r; |
218 | ||
8edf3da1 MT |
219 | // Make path |
220 | r = nw_string_format(path, "ports/%s", name); | |
221 | if (r < 0) | |
222 | goto ERROR; | |
223 | ||
224 | // Open the configuration file | |
225 | f = nw_daemon_config_fopen(daemon, path, "r"); | |
226 | if (!f) { | |
227 | r = -errno; | |
228 | goto ERROR; | |
229 | } | |
230 | ||
06bc93d3 | 231 | // Initialize the configuration |
8edf3da1 MT |
232 | r = nw_config_create(&config, f); |
233 | if (r < 0) | |
06bc93d3 MT |
234 | goto ERROR; |
235 | ||
236 | // Fetch the type | |
237 | const char* type = nw_config_get(config, "TYPE"); | |
238 | if (!type) { | |
239 | ERROR("Port configuration %s has no TYPE\n", path); | |
8edf3da1 | 240 | r = -ENOTSUP; |
06bc93d3 MT |
241 | goto ERROR; |
242 | } | |
243 | ||
244 | // Create a new port | |
c464b5d1 | 245 | r = nw_port_create(port, daemon, nw_port_type_id_from_string(type), name, config); |
8edf3da1 | 246 | if (r < 0) |
06bc93d3 MT |
247 | goto ERROR; |
248 | ||
249 | ERROR: | |
250 | if (config) | |
251 | nw_config_unref(config); | |
8edf3da1 MT |
252 | if (f) |
253 | fclose(f); | |
06bc93d3 MT |
254 | |
255 | return r; | |
256 | } | |
257 | ||
2361667e | 258 | nw_port* nw_port_ref(nw_port* port) { |
abeab069 MT |
259 | port->nrefs++; |
260 | ||
261 | return port; | |
262 | } | |
263 | ||
2361667e | 264 | nw_port* nw_port_unref(nw_port* port) { |
abeab069 MT |
265 | if (--port->nrefs > 0) |
266 | return port; | |
267 | ||
268 | nw_port_free(port); | |
269 | return NULL; | |
270 | } | |
271 | ||
240e331b MT |
272 | /* |
273 | This is a helper function for when we pass a reference to the event loop | |
274 | it will have to dereference the port instance later. | |
275 | */ | |
276 | static void __nw_port_unref(void* data) { | |
277 | nw_port* port = (nw_port*)data; | |
278 | ||
279 | nw_port_unref(port); | |
280 | } | |
281 | ||
c403eb4c MT |
282 | int nw_port_destroy(nw_port* port) { |
283 | int r; | |
284 | ||
285 | DEBUG("Destroying port %s\n", port->name); | |
286 | ||
287 | // Destroy the physical link (if exists) | |
288 | if (port->link) { | |
289 | r = nw_link_destroy(port->link); | |
290 | if (r) | |
291 | return r; | |
292 | } | |
293 | ||
294 | // Dereference the port from other ports | |
295 | r = nw_daemon_ports_walk(port->daemon, __nw_port_drop_port, port); | |
296 | if (r) | |
297 | return r; | |
298 | ||
299 | // Dereference the port from other zones | |
300 | r = nw_daemon_zones_walk(port->daemon, __nw_zone_drop_port, port); | |
301 | if (r) | |
302 | return r; | |
303 | ||
304 | // Destroy the configuration | |
305 | r = nw_config_destroy(port->config); | |
306 | if (r) | |
307 | return r; | |
308 | ||
c403eb4c MT |
309 | return 0; |
310 | } | |
311 | ||
312 | int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) { | |
313 | nw_port* dropped_port = (nw_port*)data; | |
314 | int r; | |
315 | ||
c464b5d1 | 316 | switch (port->type->id) { |
c403eb4c MT |
317 | case NW_PORT_VLAN: |
318 | if (port->vlan.parent == dropped_port) { | |
319 | r = nw_port_set_vlan_parent(port, NULL); | |
320 | if (r) | |
321 | return r; | |
322 | } | |
323 | break; | |
324 | ||
95c5dca2 | 325 | case NW_PORT_BONDING: |
c403eb4c | 326 | case NW_PORT_DUMMY: |
c403eb4c MT |
327 | break; |
328 | } | |
329 | ||
330 | return 0; | |
331 | } | |
332 | ||
605e975f | 333 | int nw_port_save(nw_port* port) { |
8edf3da1 MT |
334 | char path[PATH_MAX]; |
335 | FILE* f = NULL; | |
605e975f MT |
336 | int r; |
337 | ||
8edf3da1 MT |
338 | // Compose path |
339 | r = nw_string_format(path, "ports/%s", port->name); | |
340 | if (r < 0) | |
341 | return r; | |
342 | ||
343 | // Open file | |
344 | f = nw_daemon_config_fopen(port->daemon, path, "w"); | |
345 | if (!f) { | |
346 | r = -errno; | |
347 | goto ERROR; | |
348 | } | |
349 | ||
082d81a3 MT |
350 | // Write out the configuration |
351 | r = nw_config_options_write(port->config); | |
352 | if (r < 0) | |
353 | goto ERROR; | |
c7761af8 | 354 | |
ff886975 | 355 | // Write the configuration |
8edf3da1 MT |
356 | r = nw_config_write(port->config, f); |
357 | if (r < 0) | |
358 | goto ERROR; | |
c7761af8 MT |
359 | |
360 | ERROR: | |
8edf3da1 MT |
361 | if (f) |
362 | fclose(f); | |
363 | if (r) | |
364 | ERROR("Could not save configuration for port %s: %s\n", port->name, strerror(-r)); | |
c7761af8 | 365 | |
8edf3da1 | 366 | return r; |
605e975f MT |
367 | } |
368 | ||
2361667e | 369 | const char* nw_port_name(nw_port* port) { |
abeab069 MT |
370 | return port->name; |
371 | } | |
7297ba7f | 372 | |
2361667e | 373 | char* nw_port_bus_path(nw_port* port) { |
7297ba7f MT |
374 | char* p = NULL; |
375 | int r; | |
376 | ||
377 | // Encode the bus path | |
378 | r = sd_bus_path_encode("/org/ipfire/network1/port", port->name, &p); | |
379 | if (r < 0) | |
380 | return NULL; | |
381 | ||
382 | return p; | |
383 | } | |
4a383286 | 384 | |
611d4aca MT |
385 | int __nw_port_set_link(nw_daemon* daemon, nw_port* port, void* data) { |
386 | nw_link* link = (nw_link*)data; | |
387 | ||
388 | // Fetch the link name | |
389 | const char* ifname = nw_link_ifname(link); | |
390 | if (!ifname) { | |
391 | ERROR("Link does not have a name set\n"); | |
392 | return 1; | |
393 | } | |
394 | ||
395 | // Set link if the name matches | |
396 | if (strcmp(port->name, ifname) == 0) | |
397 | return nw_port_set_link(port, link); | |
398 | ||
399 | // If we have the link set but the name did not match, we will remove it | |
400 | else if (port->link == link) | |
401 | return nw_port_set_link(port, NULL); | |
402 | ||
403 | return 0; | |
404 | } | |
405 | ||
406 | int __nw_port_drop_link(nw_daemon* daemon, nw_port* port, void* data) { | |
407 | nw_link* link = (nw_link*)data; | |
02801f0d | 408 | |
611d4aca MT |
409 | // Drop the link if it matches |
410 | if (port->link == link) | |
411 | return nw_port_set_link(port, NULL); | |
412 | ||
413 | return 0; | |
30d4ab67 MT |
414 | } |
415 | ||
240e331b MT |
416 | static nw_link* nw_port_get_link(nw_port* port) { |
417 | // Fetch the link if not set | |
418 | if (!port->link) | |
419 | port->link = nw_daemon_get_link_by_name(port->daemon, port->name); | |
420 | ||
421 | if (!port->link) | |
422 | return NULL; | |
423 | ||
424 | return nw_link_ref(port->link); | |
425 | } | |
426 | ||
2361667e | 427 | const nw_address_t* nw_port_get_address(nw_port* port) { |
4a383286 MT |
428 | return &port->address; |
429 | } | |
20375a08 | 430 | |
371b836a MT |
431 | static int nw_port_is_disabled(nw_port* port) { |
432 | return nw_config_get_bool(port->config, "DISABLED"); | |
433 | } | |
434 | ||
5a5c346c | 435 | nw_port* nw_port_get_parent_port(nw_port* port) { |
c464b5d1 | 436 | if (!NW_PORT_TYPE(port)->get_parent_port) |
5a5c346c MT |
437 | return NULL; |
438 | ||
c464b5d1 | 439 | return NW_PORT_TYPE(port)->get_parent_port(port); |
5a5c346c MT |
440 | } |
441 | ||
240e331b MT |
442 | static nw_link* nw_port_get_parent_link(nw_port* port) { |
443 | nw_port* parent = NULL; | |
444 | nw_link* link = NULL; | |
445 | ||
240e331b | 446 | // Fetch the parent |
5a5c346c | 447 | parent = nw_port_get_parent_port(port); |
240e331b | 448 | if (!parent) |
5a5c346c | 449 | return NULL; |
240e331b MT |
450 | |
451 | // Fetch the link | |
452 | link = nw_port_get_link(parent); | |
453 | ||
5a5c346c | 454 | // Cleanup |
240e331b MT |
455 | if (parent) |
456 | nw_port_unref(parent); | |
457 | ||
458 | return link; | |
459 | } | |
460 | ||
461 | static int __nw_port_create_link(sd_netlink* rtnl, sd_netlink_message* m, void* data) { | |
462 | nw_port* port = (nw_port*)data; | |
463 | int r; | |
464 | ||
465 | // Check if the operation was successful | |
466 | r = sd_netlink_message_get_errno(m); | |
467 | if (r < 0) { | |
468 | ERROR("Could not create port %s: %s\n", port->name, strerror(-r)); | |
469 | // XXX We should extract the error message | |
470 | ||
471 | return 0; | |
472 | } | |
473 | ||
474 | DEBUG("Successfully created %s\n", port->name); | |
475 | ||
476 | return 0; | |
477 | } | |
478 | ||
96b1b84d | 479 | static int nw_port_create_link(nw_port* port) { |
240e331b MT |
480 | sd_netlink_message* m = NULL; |
481 | nw_link* link = NULL; | |
ff886975 MT |
482 | int r; |
483 | ||
240e331b MT |
484 | sd_netlink* rtnl = nw_daemon_get_rtnl(port->daemon); |
485 | ||
575d1a3f MT |
486 | DEBUG("Creating port %s...\n", port->name); |
487 | ||
c464b5d1 MT |
488 | // Check the kind |
489 | if (!NW_PORT_TYPE(port)->kind) { | |
490 | ERROR("Port type has no kind\n"); | |
491 | r = -ENOTSUP; | |
492 | goto ERROR; | |
493 | } | |
494 | ||
240e331b MT |
495 | // Create a new link |
496 | r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); | |
497 | if (r < 0) { | |
498 | ERROR("Could not create netlink message: %m\n"); | |
499 | goto ERROR; | |
500 | } | |
501 | ||
502 | // Set the name | |
503 | r = sd_netlink_message_append_string(m, IFLA_IFNAME, port->name); | |
504 | if (r < 0) { | |
505 | ERROR("Could not set port name: %s\n", strerror(-r)); | |
506 | goto ERROR; | |
ff886975 MT |
507 | } |
508 | ||
1574bc00 MT |
509 | // XXX Set common things like MTU, etc. |
510 | ||
d667fff4 | 511 | // Set Ethernet address |
1574bc00 MT |
512 | r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &port->address); |
513 | if (r < 0) { | |
514 | ERROR("Could not set MAC address: %s\n", strerror(-r)); | |
515 | goto ERROR; | |
516 | } | |
240e331b MT |
517 | |
518 | // Fetch the parent link | |
519 | link = nw_port_get_parent_link(port); | |
520 | if (link) { | |
521 | r = sd_netlink_message_append_u32(m, IFLA_LINK, nw_link_ifindex(link)); | |
522 | if (r < 0) | |
523 | goto ERROR; | |
524 | } | |
525 | ||
526 | // Open an IFLA_LINKINFO container | |
527 | r = sd_netlink_message_open_container(m, IFLA_LINKINFO); | |
528 | if (r < 0) | |
529 | goto ERROR; | |
530 | ||
531 | // Run the custom setup | |
c464b5d1 MT |
532 | if (NW_PORT_TYPE(port)->create_link) { |
533 | r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, NW_PORT_TYPE(port)->kind); | |
240e331b MT |
534 | if (r < 0) { |
535 | ERROR("Could not open IFLA_INFO_DATA container: %s\n", strerror(-r)); | |
536 | goto ERROR; | |
537 | } | |
538 | ||
c464b5d1 | 539 | r = NW_PORT_TYPE(port)->create_link(port, m); |
240e331b MT |
540 | if (r) { |
541 | ERROR("Could not create port %s: %m\n", port->name); | |
542 | goto ERROR; | |
543 | } | |
544 | ||
545 | // Close the container | |
546 | r = sd_netlink_message_close_container(m); | |
547 | if (r < 0) | |
548 | goto ERROR; | |
549 | ||
550 | // Just set IFLA_INFO_KIND if there is no custom function | |
551 | } else { | |
c464b5d1 | 552 | r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, NW_PORT_TYPE(port)->kind); |
240e331b MT |
553 | if (r < 0) |
554 | goto ERROR; | |
555 | } | |
556 | ||
557 | // Close the container | |
558 | r = sd_netlink_message_close_container(m); | |
559 | if (r < 0) | |
560 | goto ERROR; | |
561 | ||
562 | // Send the message | |
563 | r = sd_netlink_call_async(rtnl, NULL, m, __nw_port_create_link, | |
564 | __nw_port_unref, nw_port_ref(port), -1, NULL); | |
565 | if (r < 0) { | |
566 | ERROR("Could not send netlink message: %s\n", strerror(-r)); | |
567 | goto ERROR; | |
568 | } | |
569 | ||
570 | r = 0; | |
571 | ||
572 | ERROR: | |
573 | if (m) | |
574 | sd_netlink_message_unref(m); | |
575 | if (link) | |
576 | nw_link_unref(link); | |
ff886975 MT |
577 | |
578 | return r; | |
96b1b84d MT |
579 | } |
580 | ||
b9769b09 | 581 | int nw_port_reconfigure(nw_port* port) { |
371b836a MT |
582 | int r; |
583 | ||
584 | // If the port is disabled, we will try to destroy it | |
585 | if (nw_port_is_disabled(port)) { | |
586 | if (port->link) { | |
587 | r = nw_link_destroy(port->link); | |
588 | if (r) | |
589 | return r; | |
590 | } | |
591 | ||
592 | return 0; | |
593 | } | |
594 | ||
96b1b84d | 595 | // If there is no link, we will try to create it |
895f7134 MT |
596 | if (!port->link) |
597 | return nw_port_create_link(port); | |
96b1b84d | 598 | |
371b836a | 599 | // XXX TODO |
96b1b84d MT |
600 | |
601 | return 0; | |
b9769b09 MT |
602 | } |
603 | ||
20375a08 | 604 | int nw_port_has_carrier(nw_port* port) { |
02801f0d MT |
605 | if (!port->link) |
606 | return 0; | |
20375a08 | 607 | |
02801f0d | 608 | return nw_link_has_carrier(port->link); |
20375a08 | 609 | } |
15240e08 MT |
610 | |
611 | /* | |
612 | Stats | |
613 | */ | |
614 | ||
615 | const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port) { | |
616 | if (!port->link) | |
617 | return NULL; | |
618 | ||
619 | return nw_link_get_stats64(port->link); | |
620 | } | |
621 | ||
622 | int __nw_port_update_stats(nw_daemon* daemon, nw_port* port, void* data) { | |
623 | nw_link* link = (nw_link*)data; | |
624 | ||
625 | // Emit stats if link matches | |
626 | if (port->link == link) | |
627 | return nw_stats_collector_emit_port_stats(daemon, port); | |
628 | ||
629 | return 0; | |
630 | } | |
631 | ||
632 | int nw_port_update_stats(nw_port* port) { | |
633 | if (port->link) | |
634 | return nw_link_update_stats(port->link); | |
635 | ||
636 | return 0; | |
637 | } | |
c7761af8 | 638 | |
c464b5d1 MT |
639 | int nw_port_check_type(nw_port* port, const nw_port_type_id_t type) { |
640 | if (port->type->id == type) | |
c7761af8 MT |
641 | return 0; |
642 | ||
643 | errno = ENOTSUP; | |
644 | return -errno; | |
645 | } | |
e9b0614e MT |
646 | |
647 | /* | |
648 | JSON | |
649 | */ | |
650 | int nw_port_to_json(nw_port* port, struct json_object** object) { | |
651 | char* address = NULL; | |
652 | int r; | |
653 | ||
654 | // Create a new JSON object | |
655 | struct json_object* o = json_object_new_object(); | |
656 | if (!o) { | |
657 | r = -errno; | |
658 | goto ERROR; | |
659 | } | |
660 | ||
661 | // Add name | |
662 | r = json_object_add_string(o, "Name", port->name); | |
663 | if (r < 0) | |
664 | goto ERROR; | |
665 | ||
666 | // Add Type | |
c464b5d1 | 667 | r = json_object_add_string(o, "Type", nw_port_type_id_to_string(port->type->id)); |
e9b0614e MT |
668 | if (r < 0) |
669 | goto ERROR; | |
670 | ||
671 | // Add address | |
672 | address = nw_address_to_string(&port->address); | |
673 | if (address) { | |
674 | r = json_object_add_string(o, "Address", address); | |
675 | if (r < 0) | |
676 | goto ERROR; | |
677 | } | |
678 | ||
679 | // Call custom stuff | |
c464b5d1 MT |
680 | if (NW_PORT_TYPE(port)->to_json) { |
681 | r = NW_PORT_TYPE(port)->to_json(port, o); | |
e9b0614e MT |
682 | if (r < 0) |
683 | goto ERROR; | |
684 | } | |
685 | ||
686 | // Success | |
687 | r = 0; | |
688 | ||
689 | // Return a reference to the created object | |
690 | *object = json_object_ref(o); | |
691 | ||
692 | ERROR: | |
693 | if (address) | |
694 | free(address); | |
695 | if (o) | |
696 | json_object_unref(o); | |
697 | ||
698 | return r; | |
699 | } |