Skip to content

Commit adbc8f3

Browse files
tomasmatusmvollmer
authored andcommitted
networking: stop using deprecated addresses endpoint, improve validation
replace following properties on IP and IPv6 objects addresses -> addressData + gateway routes -> route-data dns -> dns-data dns-data is available since NM 1.42.0 so also keep support for older dns prop as it is required on ubuntu-2204. ipaddr.js is used to validate addresses so cockpit does not only rely on network manager to handle this.
1 parent b181de9 commit adbc8f3

File tree

10 files changed

+368
-206
lines changed

10 files changed

+368
-206
lines changed

node_modules

Submodule node_modules updated 62 files

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"@patternfly/react-table": "6.3.1",
1212
"@patternfly/react-tokens": "6.3.1",
1313
"dequal": "2.0.3",
14+
"ipaddr.js": "2.2.0",
1415
"json-stable-stringify-without-jsonify": "1.0.1",
1516
"prop-types": "15.8.1",
1617
"react": "18.3.1",

pkg/networkmanager/interfaces.js

Lines changed: 120 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,34 @@ export function NetworkManagerModel() {
165165
const client = cockpit.dbus("org.freedesktop.NetworkManager", { superuser: "try" });
166166
self.client = client;
167167

168+
// set to false by default as newer API is backwards compatible
169+
// and supports both "dns" and "dns-data" properties
170+
// dns-data property was added in version 1.42.0
171+
self.supports_dns_data = false;
172+
173+
function check_version_dns_data_support(version) {
174+
if (version.length < 2) {
175+
return false;
176+
}
177+
178+
if (version[0] > 1) {
179+
return true;
180+
} else if (version[0] === 1 && version[1] >= 42) {
181+
return true;
182+
}
183+
184+
return false;
185+
}
186+
168187
/* resolved once first stage of initialization is done */
169188
self.preinit = new Promise((resolve, reject) => {
170189
client.call("/org/freedesktop/NetworkManager",
171190
"org.freedesktop.DBus.Properties", "Get",
172-
["org.freedesktop.NetworkManager", "State"], { flags: "" })
191+
["org.freedesktop.NetworkManager", "Version"], { flags: "" })
173192
.then((reply, options) => {
193+
const nm_version = reply[0].v.split(".").map(n => Number.parseInt(n));
194+
self.supports_dns_data = check_version_dns_data_support(nm_version);
195+
174196
if (options.flags) {
175197
if (options.flags.indexOf(">") !== -1)
176198
utils.set_byteorder("be");
@@ -421,63 +443,52 @@ export function NetworkManagerModel() {
421443
/* NetworkManager specific data conversions and utility functions.
422444
*/
423445

424-
function ip4_address_from_nm(addr) {
425-
return [utils.ip4_to_text(addr[0]),
426-
utils.ip_prefix_to_text(addr[1]),
427-
utils.ip4_to_text(addr[2], true)
428-
];
446+
function ip_address_from_nm(addr) {
447+
return {
448+
address: addr.address.v,
449+
prefix: utils.ip_prefix_to_text(addr.prefix.v)
450+
};
429451
}
430452

431-
function ip4_address_to_nm(addr) {
432-
return [utils.ip4_from_text(addr[0]),
433-
utils.ip4_prefix_from_text(addr[1]),
434-
utils.ip4_from_text(addr[2], true)
435-
];
436-
}
453+
function ip_address_to_nm(addr, ipv) {
454+
const prefix = ipv === "ipv4" ? utils.ip4_prefix_from_text(addr.prefix) : utils.ip_prefix_from_text(addr.prefix);
437455

438-
function ip4_route_from_nm(addr) {
439-
return [utils.ip4_to_text(addr[0]),
440-
utils.ip_prefix_to_text(addr[1]),
441-
utils.ip4_to_text(addr[2], true),
442-
utils.ip_metric_to_text(addr[3])
443-
];
444-
}
456+
if (!utils.validate_ip(addr.address)) {
457+
throw cockpit.format(_("Invalid IP address: $0"), addr.address);
458+
}
445459

446-
function ip4_route_to_nm(addr) {
447-
return [utils.ip4_from_text(addr[0]),
448-
utils.ip4_prefix_from_text(addr[1]),
449-
utils.ip4_from_text(addr[2], true),
450-
utils.ip_metric_from_text(addr[3])
451-
];
452-
}
453-
function ip6_address_from_nm(addr) {
454-
return [utils.ip6_to_text(addr[0]),
455-
utils.ip_prefix_to_text(addr[1]),
456-
utils.ip6_to_text(addr[2], true)
457-
];
460+
return {
461+
address: { t: "s", v: addr.address },
462+
prefix: { t: "u", v: prefix },
463+
};
458464
}
459465

460-
function ip6_address_to_nm(addr) {
461-
return [utils.ip6_from_text(addr[0]),
462-
parseInt(addr[1], 10) || 64,
463-
utils.ip6_from_text(addr[2], true)
464-
];
466+
function route_from_nm(route) {
467+
return {
468+
dest: route.dest.v,
469+
prefix: utils.ip_prefix_to_text(route.prefix.v),
470+
next_hop: route["next-hop"].v,
471+
metric: utils.ip_metric_to_text(route.metric.v),
472+
};
465473
}
466474

467-
function ip6_route_from_nm(addr) {
468-
return [utils.ip6_to_text(addr[0]),
469-
utils.ip_prefix_to_text(addr[1]),
470-
utils.ip6_to_text(addr[2], true),
471-
utils.ip_metric_to_text(addr[3]),
472-
];
473-
}
475+
function route_to_nm(route, ipv) {
476+
const prefix = ipv === "ipv4" ? utils.ip4_prefix_from_text(route.prefix) : utils.ip_prefix_from_text(route.prefix);
477+
478+
if (!utils.validate_ip(route.dest)) {
479+
throw cockpit.format(_("Invalid destination address: $0"), route.dest);
480+
}
481+
482+
if (!utils.validate_ip(route.next_hop)) {
483+
throw cockpit.format(_("Invalid gateway address: $0"), route.next_hop);
484+
}
474485

475-
function ip6_route_to_nm(addr) {
476-
return [utils.ip6_from_text(addr[0]),
477-
utils.ip_prefix_from_text(addr[1]),
478-
utils.ip6_from_text(addr[2], true),
479-
utils.ip_metric_from_text(addr[3])
480-
];
486+
return {
487+
dest: { t: "s", v: route.dest },
488+
prefix: { t: "u", v: prefix },
489+
"next-hop": { t: "s", v: route.next_hop },
490+
metric: { t: "u", v: utils.ip_metric_from_text(route.metric) },
491+
};
481492
}
482493

483494
function settings_from_nm(settings) {
@@ -488,15 +499,20 @@ export function NetworkManagerModel() {
488499
return def;
489500
}
490501

491-
function get_ip(first, addr_from_nm, route_from_nm, ip_to_text) {
502+
function get_ip(first, ip_to_text) {
503+
const dns_data = self.supports_dns_data
504+
? get(first, "dns-data", [])
505+
: get(first, "dns", []).map(ip_to_text);
506+
492507
return {
493508
method: get(first, "method", "auto"),
494509
ignore_auto_dns: get(first, "ignore-auto-dns", false),
495510
ignore_auto_routes: get(first, "ignore-auto-routes", false),
496-
addresses: get(first, "addresses", []).map(addr_from_nm),
497-
dns: get(first, "dns", []).map(ip_to_text),
511+
address_data: get(first, "address-data", []).map(ip_address_from_nm),
512+
gateway: get(first, "gateway", ""),
513+
dns_data,
498514
dns_search: get(first, "dns-search", []),
499-
routes: get(first, "routes", []).map(route_from_nm)
515+
route_data: get(first, "route-data", []).map(route_from_nm),
500516
};
501517
}
502518

@@ -516,8 +532,8 @@ export function NetworkManagerModel() {
516532
};
517533

518534
if (!settings.connection.master) {
519-
result.ipv4 = get_ip("ipv4", ip4_address_from_nm, ip4_route_from_nm, utils.ip4_to_text);
520-
result.ipv6 = get_ip("ipv6", ip6_address_from_nm, ip6_route_from_nm, utils.ip6_to_text);
535+
result.ipv4 = get_ip("ipv4", utils.ip4_to_text);
536+
result.ipv6 = get_ip("ipv6", utils.ip6_to_text);
521537
}
522538

523539
if (settings["802-3-ethernet"]) {
@@ -612,31 +628,61 @@ export function NetworkManagerModel() {
612628
delete result[first][second];
613629
}
614630

615-
function set_ip(first, addrs_sig, addr_to_nm, routes_sig, route_to_nm, ips_sig, ip_from_text) {
631+
function set_ip(first, dns_ip_sig, ip_from_text) {
616632
set(first, "method", 's', settings[first].method);
617633
set(first, "ignore-auto-dns", 'b', settings[first].ignore_auto_dns);
618634
set(first, "ignore-auto-routes", 'b', settings[first].ignore_auto_routes);
619635
set(first, "addr-gen-mode", 'i', settings[first].addr_gen_mode);
620636

621-
const addresses = settings[first].addresses;
637+
const addresses = settings[first].address_data;
622638
if (addresses)
623-
set(first, "addresses", addrs_sig, addresses.map(addr_to_nm));
639+
set(first, "address-data", "aa{sv}", addresses.map(addr => ip_address_to_nm(addr, first)));
640+
641+
const gateway = settings[first].gateway;
642+
if (gateway && addresses.length > 0) {
643+
if (!utils.validate_ip(gateway)) {
644+
throw cockpit.format(_("Invalid gateway address: $0"), gateway);
645+
}
646+
set(first, "gateway", "s", gateway);
647+
} else {
648+
// gateway cannot be set if there are no addresses
649+
delete result[first].gateway;
650+
}
651+
652+
const dns = settings[first].dns_data;
653+
if (dns) {
654+
const invalid = dns.find(addr => !utils.validate_ip(addr));
655+
if (invalid) {
656+
throw cockpit.format(_("Invalid DNS address: $0"), invalid);
657+
}
658+
659+
if (self.supports_dns_data) {
660+
set(first, "dns-data", "as", dns);
661+
} else {
662+
set(first, "dns", dns_ip_sig, dns.map(addr => ip_from_text(addr)));
663+
}
664+
}
624665

625-
const dns = settings[first].dns;
626-
if (dns)
627-
set(first, "dns", ips_sig, dns.map(ip_from_text));
628666
set(first, "dns-search", 'as', settings[first].dns_search);
629667

630-
const routes = settings[first].routes;
668+
const routes = settings[first].route_data;
631669
if (routes)
632-
set(first, "routes", routes_sig, routes.map(route_to_nm));
670+
set(first, "route-data", "aa{sv}", routes.map(route => route_to_nm(route, first)));
633671

634672
// Never pass "address-labels" back to NetworkManager. It
635673
// is documented as "internal only", but needs to somehow
636674
// stay in sync with "addresses". By not passing it back
637675
// we don't have to worry about that.
638676
//
639677
delete result[first]["address-labels"];
678+
679+
// Never pass "addresses", instead use "address-data" + "gateway"
680+
delete result[first].addresses;
681+
// Never pass "routes", instead use "route-data"
682+
delete result[first].routes;
683+
// Never pass "dns" if "dns-data" is supported
684+
if (self.supports_dns_data)
685+
delete result[first].dns;
640686
}
641687

642688
set("connection", "id", 's', settings.connection.id);
@@ -649,12 +695,12 @@ export function NetworkManagerModel() {
649695
set("connection", "master", 's', settings.connection.group);
650696

651697
if (settings.ipv4)
652-
set_ip("ipv4", 'aau', ip4_address_to_nm, 'aau', ip4_route_to_nm, 'au', utils.ip4_from_text);
698+
set_ip("ipv4", 'au', utils.ip4_from_text);
653699
else
654700
delete result.ipv4;
655701

656702
if (settings.ipv6)
657-
set_ip("ipv6", 'a(ayuay)', ip6_address_to_nm, 'a(ayuayu)', ip6_route_to_nm, 'aay', utils.ip6_from_text);
703+
set_ip("ipv6", 'aay', utils.ip6_from_text);
658704
else
659705
delete result.ipv6;
660706

@@ -881,7 +927,7 @@ export function NetworkManagerModel() {
881927
],
882928

883929
props: {
884-
Addresses: { conv: conv_Array(ip4_address_from_nm), def: [] }
930+
AddressData: { conv: conv_Array(ip_address_from_nm), def: [] }
885931
}
886932
};
887933

@@ -891,7 +937,7 @@ export function NetworkManagerModel() {
891937
],
892938

893939
props: {
894-
Addresses: { conv: conv_Array(ip6_address_from_nm), def: [] }
940+
AddressData: { conv: conv_Array(ip_address_from_nm), def: [] }
895941
}
896942
};
897943

@@ -1356,8 +1402,8 @@ export function device_state_text(dev) {
13561402
return _("No carrier");
13571403
if (!is_managed(dev)) {
13581404
if (!dev.ActiveConnection &&
1359-
(!dev.Ip4Config || dev.Ip4Config.Addresses.length === 0) &&
1360-
(!dev.Ip6Config || dev.Ip6Config.Addresses.length === 0))
1405+
(!dev.Ip4Config || dev.Ip4Config.AddressData.length === 0) &&
1406+
(!dev.Ip6Config || dev.Ip6Config.AddressData.length === 0))
13611407
return _("Inactive");
13621408
}
13631409
return dev.StateText;
@@ -1387,8 +1433,8 @@ export function render_active_connection(dev, with_link, hide_link_local) {
13871433

13881434
const ip4config = con ? con.Ip4Config : dev.Ip4Config;
13891435
if (ip4config) {
1390-
ip4config.Addresses.forEach(function (a) {
1391-
parts.push(a[0] + "/" + a[1]);
1436+
ip4config.AddressData.forEach(function (a) {
1437+
parts.push(a.address + "/" + a.prefix);
13921438
});
13931439
}
13941440

@@ -1401,9 +1447,9 @@ export function render_active_connection(dev, with_link, hide_link_local) {
14011447

14021448
const ip6config = con ? con.Ip6Config : dev.Ip6Config;
14031449
if (ip6config) {
1404-
ip6config.Addresses.forEach(function (a) {
1405-
if (!(hide_link_local && is_ipv6_link_local(a[0])))
1406-
parts.push(a[0] + "/" + a[1]);
1450+
ip6config.AddressData.forEach(function (a) {
1451+
if (!(hide_link_local && is_ipv6_link_local(a.address)))
1452+
parts.push(a.address + "/" + a.prefix);
14071453
});
14081454
}
14091455

0 commit comments

Comments
 (0)