network: add ModemManager support
[Match] Name=wwan* [Network] LLDP=no LinkLocalAddressing=no IPv6AcceptRA=no [ModemManager] SimpleConnectPropertie]s=apn=internet ip-type=ipv4 allow-roaming=no pin=1111 operator-id=25503 RouteMetric=200 UseGateway=yes Co-authored-by: Yu Watanabe <watanabe.yu+github@gmail.com>
This commit is contained in:
committed by
Yu Watanabe
co-authored by
Yu Watanabe
parent
4ed1e2ea17
commit
f8a4c3d375
@@ -89,6 +89,8 @@ systemd_networkd_extract_sources = files(
|
||||
'networkd-util.c',
|
||||
'networkd-wifi.c',
|
||||
'networkd-wiphy.c',
|
||||
'networkd-wwan.c',
|
||||
'networkd-wwan-bus.c',
|
||||
'tc/cake.c',
|
||||
'tc/codel.c',
|
||||
'tc/drr.c',
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-setlink.h"
|
||||
#include "networkd-state-file.h"
|
||||
#include "networkd-wwan.h"
|
||||
#include "parse-util.h"
|
||||
#include "set.h"
|
||||
#include "socket-util.h"
|
||||
@@ -1770,6 +1771,9 @@ int dhcp4_start_full(Link *link, bool set_ipv6_connectivity) {
|
||||
if (!link->dhcp_client)
|
||||
return 0;
|
||||
|
||||
if (link_dhcp_enabled_by_bearer(link, AF_INET) == 0)
|
||||
return 0;
|
||||
|
||||
if (!link_has_carrier(link))
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "networkd-queue.h"
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-state-file.h"
|
||||
#include "networkd-wwan.h"
|
||||
#include "set.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
@@ -494,6 +495,9 @@ int dhcp6_start(Link *link) {
|
||||
if (!link_dhcp6_enabled(link))
|
||||
return 0;
|
||||
|
||||
if (link_dhcp_enabled_by_bearer(link, AF_INET6) == 0)
|
||||
return 0;
|
||||
|
||||
if (!link_has_carrier(link))
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-route-util.h"
|
||||
#include "networkd-routing-policy-rule.h"
|
||||
#include "networkd-wwan.h"
|
||||
#include "ordered-set.h"
|
||||
#include "set.h"
|
||||
#include "string-util.h"
|
||||
@@ -541,6 +542,15 @@ static int dns_append_json(Link *link, sd_json_variant **v) {
|
||||
return r;
|
||||
}
|
||||
|
||||
Bearer *b;
|
||||
|
||||
if (link_get_bearer(link, &b) >= 0)
|
||||
FOREACH_ARRAY(dns, b->dns, b->n_dns) {
|
||||
r = dns_append_json_one(link, *dns, NETWORK_CONFIG_SOURCE_MODEM_MANAGER, NULL, &array);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (link->dhcp_lease && link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP4)) {
|
||||
const struct in_addr *dns;
|
||||
union in_addr_union s;
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
#include "networkd-state-file.h"
|
||||
#include "networkd-sysctl.h"
|
||||
#include "networkd-wifi.h"
|
||||
#include "networkd-wwan-bus.h"
|
||||
#include "ordered-set.h"
|
||||
#include "parse-util.h"
|
||||
#include "set.h"
|
||||
@@ -529,6 +530,9 @@ void link_check_ready(Link *link) {
|
||||
if (!link->sr_iov_configured)
|
||||
return (void) log_link_debug(link, "%s(): SR-IOV is not configured.", __func__);
|
||||
|
||||
if (!link->bearer_configured)
|
||||
return (void) log_link_debug(link, "%s(): Bearer has not been applied.", __func__);
|
||||
|
||||
/* IPv6LL is assigned after the link gains its carrier. */
|
||||
if (!link->network->configure_without_carrier &&
|
||||
link_ipv6ll_enabled(link) &&
|
||||
@@ -538,7 +542,7 @@ void link_check_ready(Link *link) {
|
||||
/* All static addresses must be ready. */
|
||||
bool has_static_address = false;
|
||||
SET_FOREACH(a, link->addresses) {
|
||||
if (a->source != NETWORK_CONFIG_SOURCE_STATIC)
|
||||
if (!IN_SET(a->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_MODEM_MANAGER))
|
||||
continue;
|
||||
if (!address_is_ready(a))
|
||||
return (void) log_link_debug(link, "%s(): static address %s is not ready.", __func__,
|
||||
@@ -1293,6 +1297,10 @@ static int link_configure(Link *link) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = link_modem_reconfigure(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!link_has_carrier(link))
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@ typedef struct Link {
|
||||
unsigned set_link_messages;
|
||||
unsigned set_flags_messages;
|
||||
unsigned create_stacked_netdev_messages;
|
||||
unsigned bearer_messages;
|
||||
|
||||
Set *addresses;
|
||||
Set *neighbors;
|
||||
@@ -141,6 +142,7 @@ typedef struct Link {
|
||||
bool master_set:1;
|
||||
bool stacked_netdevs_created:1;
|
||||
bool bridge_vlan_set:1;
|
||||
bool bearer_configured:1;
|
||||
|
||||
sd_dhcp_server *dhcp_server;
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "networkd-state-file.h"
|
||||
#include "networkd-wifi.h"
|
||||
#include "networkd-wiphy.h"
|
||||
#include "networkd-wwan-bus.h"
|
||||
#include "ordered-set.h"
|
||||
#include "qdisc.h"
|
||||
#include "set.h"
|
||||
@@ -96,6 +97,8 @@ static int on_connected(sd_bus_message *message, void *userdata, sd_bus_error *r
|
||||
if (m->product_uuid_requested)
|
||||
(void) manager_request_product_uuid(m);
|
||||
|
||||
(void) manager_notify_mm_bus_connected(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -145,6 +148,8 @@ static int manager_connect_bus(Manager *m) {
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to request match for PrepareForSleep, ignoring: %m");
|
||||
|
||||
(void) manager_match_mm_signals(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -733,6 +738,9 @@ Manager* manager_free(Manager *m) {
|
||||
|
||||
set_free(m->rules);
|
||||
|
||||
sd_bus_slot_unref(m->slot_mm);
|
||||
hashmap_free(m->modems_by_path);
|
||||
|
||||
sd_netlink_unref(m->rtnl);
|
||||
sd_netlink_unref(m->genl);
|
||||
sd_netlink_unref(m->nfnl);
|
||||
|
||||
@@ -99,6 +99,10 @@ typedef struct Manager {
|
||||
Hashmap *wiphy_by_index;
|
||||
Hashmap *wiphy_by_name;
|
||||
|
||||
/* ModemManager support */
|
||||
sd_bus_slot *slot_mm;
|
||||
Hashmap *modems_by_path;
|
||||
|
||||
/* For link speed meter */
|
||||
bool use_speed_meter;
|
||||
sd_event_source *speed_meter_event_source;
|
||||
|
||||
@@ -40,6 +40,7 @@ _Pragma("GCC diagnostic ignored \"-Wzero-as-null-pointer-constant\"")
|
||||
#include "networkd-radv.h"
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-routing-policy-rule.h"
|
||||
#include "networkd-wwan.h"
|
||||
#include "qdisc.h"
|
||||
#include "socket-util.h"
|
||||
#include "tclass.h"
|
||||
@@ -492,6 +493,9 @@ CAN.ClassicDataLengthCode, config_parse_can_control_mode,
|
||||
CAN.Termination, config_parse_can_termination, 0, 0
|
||||
IPoIB.Mode, config_parse_ipoib_mode, 0, offsetof(Network, ipoib_mode)
|
||||
IPoIB.IgnoreUserspaceMulticastGroups, config_parse_tristate, 0, offsetof(Network, ipoib_umcast)
|
||||
ModemManager.SimpleConnectProperties, config_parse_strv, 0, offsetof(Network, mm_simple_connect_props)
|
||||
ModemManager.RouteMetric, config_parse_mm_route_metric, 0, 0
|
||||
ModemManager.UseGateway, config_parse_tristate, 0, offsetof(Network, mm_use_gateway)
|
||||
QDisc.Parent, config_parse_qdisc_parent, _QDISC_KIND_INVALID, 0
|
||||
QDisc.Handle, config_parse_qdisc_handle, _QDISC_KIND_INVALID, 0
|
||||
BFIFO.Parent, config_parse_qdisc_parent, QDISC_KIND_BFIFO, 0
|
||||
|
||||
@@ -512,6 +512,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
||||
|
||||
.ipoib_mode = _IP_OVER_INFINIBAND_MODE_INVALID,
|
||||
.ipoib_umcast = -1,
|
||||
|
||||
.mm_use_gateway = -1,
|
||||
};
|
||||
|
||||
r = config_parse_many_full(
|
||||
@@ -551,6 +553,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
||||
"LLDP\0"
|
||||
"TrafficControlQueueingDiscipline\0"
|
||||
"CAN\0"
|
||||
"ModemManager\0"
|
||||
"QDisc\0"
|
||||
"BFIFO\0"
|
||||
"CAKE\0"
|
||||
@@ -847,6 +850,9 @@ static Network *network_free(Network *network) {
|
||||
hashmap_free(network->qdiscs_by_section);
|
||||
hashmap_free(network->tclasses_by_section);
|
||||
|
||||
/* ModemManager */
|
||||
strv_free(network->mm_simple_connect_props);
|
||||
|
||||
return mfree(network);
|
||||
}
|
||||
|
||||
|
||||
@@ -414,6 +414,12 @@ typedef struct Network {
|
||||
|
||||
/* NTP */
|
||||
char **ntp;
|
||||
|
||||
/* ModemManager support */
|
||||
char **mm_simple_connect_props;
|
||||
int mm_use_gateway;
|
||||
uint32_t mm_route_metric;
|
||||
bool mm_route_metric_set;
|
||||
} Network;
|
||||
|
||||
DECLARE_TRIVIAL_REF_UNREF_FUNC(Network, network);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-ntp.h"
|
||||
#include "networkd-state-file.h"
|
||||
#include "networkd-wwan.h"
|
||||
#include "ordered-set.h"
|
||||
#include "set.h"
|
||||
#include "string-util.h"
|
||||
@@ -109,6 +110,14 @@ static int link_put_dns(Link *link, OrderedSet **s) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
Bearer *b;
|
||||
|
||||
if (link_get_bearer(link, &b) >= 0) {
|
||||
r = ordered_set_put_dns_servers(s, link->ifindex, b->dns, b->n_dns);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (link->dhcp_lease && link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP4)) {
|
||||
const struct in_addr *addresses;
|
||||
|
||||
@@ -801,6 +810,11 @@ static int link_save(Link *link) {
|
||||
space = false;
|
||||
link_save_dns(link, f, link->network->dns, link->network->n_dns, &space);
|
||||
|
||||
Bearer *b;
|
||||
|
||||
if (link_get_bearer(link, &b) >= 0)
|
||||
link_save_dns(link, f, b->dns, b->n_dns, &space);
|
||||
|
||||
/* DNR resolvers are not required to provide Do53 service, however resolved doesn't
|
||||
* know how to handle such a server so for now Do53 service is required, and
|
||||
* assumed. */
|
||||
|
||||
@@ -17,14 +17,15 @@
|
||||
|
||||
/* This is used in log messages, and never used in parsing settings. So, upper cases are OK. */
|
||||
static const char * const network_config_source_table[_NETWORK_CONFIG_SOURCE_MAX] = {
|
||||
[NETWORK_CONFIG_SOURCE_FOREIGN] = "foreign",
|
||||
[NETWORK_CONFIG_SOURCE_STATIC] = "static",
|
||||
[NETWORK_CONFIG_SOURCE_IPV4LL] = "IPv4LL",
|
||||
[NETWORK_CONFIG_SOURCE_DHCP4] = "DHCPv4",
|
||||
[NETWORK_CONFIG_SOURCE_DHCP6] = "DHCPv6",
|
||||
[NETWORK_CONFIG_SOURCE_DHCP_PD] = "DHCP-PD",
|
||||
[NETWORK_CONFIG_SOURCE_NDISC] = "NDisc",
|
||||
[NETWORK_CONFIG_SOURCE_RUNTIME] = "runtime",
|
||||
[NETWORK_CONFIG_SOURCE_FOREIGN] = "foreign",
|
||||
[NETWORK_CONFIG_SOURCE_STATIC] = "static",
|
||||
[NETWORK_CONFIG_SOURCE_IPV4LL] = "IPv4LL",
|
||||
[NETWORK_CONFIG_SOURCE_DHCP4] = "DHCPv4",
|
||||
[NETWORK_CONFIG_SOURCE_DHCP6] = "DHCPv6",
|
||||
[NETWORK_CONFIG_SOURCE_DHCP_PD] = "DHCP-PD",
|
||||
[NETWORK_CONFIG_SOURCE_NDISC] = "NDisc",
|
||||
[NETWORK_CONFIG_SOURCE_RUNTIME] = "runtime",
|
||||
[NETWORK_CONFIG_SOURCE_MODEM_MANAGER] = "ModemManager",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(network_config_source, NetworkConfigSource);
|
||||
|
||||
@@ -14,6 +14,7 @@ typedef enum NetworkConfigSource {
|
||||
NETWORK_CONFIG_SOURCE_DHCP_PD,
|
||||
NETWORK_CONFIG_SOURCE_NDISC,
|
||||
NETWORK_CONFIG_SOURCE_RUNTIME, /* through D-Bus method */
|
||||
NETWORK_CONFIG_SOURCE_MODEM_MANAGER,
|
||||
_NETWORK_CONFIG_SOURCE_MAX,
|
||||
_NETWORK_CONFIG_SOURCE_INVALID = -EINVAL,
|
||||
} NetworkConfigSource;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,88 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
typedef struct Manager Manager;
|
||||
typedef struct Link Link;
|
||||
|
||||
/* From ModemManager-enums.h */
|
||||
typedef enum {
|
||||
MM_BEARER_IP_FAMILY_NONE = 0,
|
||||
MM_BEARER_IP_FAMILY_IPV4 = 1 << 0,
|
||||
MM_BEARER_IP_FAMILY_IPV6 = 1 << 1,
|
||||
MM_BEARER_IP_FAMILY_IPV4V6 = 1 << 2,
|
||||
MM_BEARER_IP_FAMILY_ANY = 0xFFFFFFFF,
|
||||
} MMBearerIpFamily;
|
||||
|
||||
typedef enum {
|
||||
MM_BEARER_TYPE_UNKNOWN = 0,
|
||||
MM_BEARER_TYPE_DEFAULT = 1,
|
||||
MM_BEARER_TYPE_DEFAULT_ATTACH = 2,
|
||||
MM_BEARER_TYPE_DEDICATED = 3,
|
||||
} MMBearerType;
|
||||
|
||||
typedef enum {
|
||||
MM_MODEM_STATE_FAILED = -1,
|
||||
MM_MODEM_STATE_UNKNOWN = 0,
|
||||
MM_MODEM_STATE_INITIALIZING = 1,
|
||||
MM_MODEM_STATE_LOCKED = 2,
|
||||
MM_MODEM_STATE_DISABLED = 3,
|
||||
MM_MODEM_STATE_DISABLING = 4,
|
||||
MM_MODEM_STATE_ENABLING = 5,
|
||||
MM_MODEM_STATE_ENABLED = 6,
|
||||
MM_MODEM_STATE_SEARCHING = 7,
|
||||
MM_MODEM_STATE_REGISTERED = 8,
|
||||
MM_MODEM_STATE_DISCONNECTING = 9,
|
||||
MM_MODEM_STATE_CONNECTING = 10,
|
||||
MM_MODEM_STATE_CONNECTED = 11,
|
||||
} MMModemState;
|
||||
|
||||
typedef enum { /*< underscore_name=mm_modem_state_failed_reason >*/
|
||||
MM_MODEM_STATE_FAILED_REASON_NONE = 0,
|
||||
MM_MODEM_STATE_FAILED_REASON_UNKNOWN = 1,
|
||||
MM_MODEM_STATE_FAILED_REASON_SIM_MISSING = 2,
|
||||
MM_MODEM_STATE_FAILED_REASON_SIM_ERROR = 3,
|
||||
MM_MODEM_STATE_FAILED_REASON_UNKNOWN_CAPABILITIES = 4,
|
||||
MM_MODEM_STATE_FAILED_REASON_ESIM_WITHOUT_PROFILES = 5,
|
||||
__MM_MODEM_STATE_FAILED_REASON_MAX = 6,
|
||||
} MMModemStateFailedReason;
|
||||
|
||||
typedef enum {
|
||||
MM_BEARER_IP_METHOD_UNKNOWN = 0,
|
||||
MM_BEARER_IP_METHOD_PPP = 1,
|
||||
MM_BEARER_IP_METHOD_STATIC = 2,
|
||||
MM_BEARER_IP_METHOD_DHCP = 3,
|
||||
} MMBearerIpMethod;
|
||||
|
||||
typedef enum { /*< underscore_name=mm_modem_port_type >*/
|
||||
MM_MODEM_PORT_TYPE_UNKNOWN = 1,
|
||||
MM_MODEM_PORT_TYPE_NET = 2,
|
||||
MM_MODEM_PORT_TYPE_AT = 3,
|
||||
MM_MODEM_PORT_TYPE_QCDM = 4,
|
||||
MM_MODEM_PORT_TYPE_GPS = 5,
|
||||
MM_MODEM_PORT_TYPE_QMI = 6,
|
||||
MM_MODEM_PORT_TYPE_MBIM = 7,
|
||||
MM_MODEM_PORT_TYPE_AUDIO = 8,
|
||||
MM_MODEM_PORT_TYPE_IGNORED = 9,
|
||||
MM_MODEM_PORT_TYPE_XMMRPC = 10,
|
||||
} MMModemPortType;
|
||||
|
||||
typedef enum { /*< underscore_name=mm_bearer_allowed_auth >*/
|
||||
MM_BEARER_ALLOWED_AUTH_UNKNOWN = 0,
|
||||
/* bits 0..4 order match Ericsson device bitmap */
|
||||
MM_BEARER_ALLOWED_AUTH_NONE = 1 << 0,
|
||||
MM_BEARER_ALLOWED_AUTH_PAP = 1 << 1,
|
||||
MM_BEARER_ALLOWED_AUTH_CHAP = 1 << 2,
|
||||
MM_BEARER_ALLOWED_AUTH_MSCHAP = 1 << 3,
|
||||
MM_BEARER_ALLOWED_AUTH_MSCHAPV2 = 1 << 4,
|
||||
MM_BEARER_ALLOWED_AUTH_EAP = 1 << 5,
|
||||
} MMBearerAllowedAuth;
|
||||
|
||||
typedef enum {
|
||||
MODEM_RECONNECT_DONE, /* No reconnect is required, e.g. connected. */
|
||||
MODEM_RECONNECT_SCHEDULED, /* Reconnect is in progress. */
|
||||
MODEM_RECONNECT_WAITING, /* Waiting for modem to recover. */
|
||||
} ModemReconnectState;
|
||||
|
||||
int manager_notify_mm_bus_connected(Manager *manager);
|
||||
int manager_match_mm_signals(Manager *manager);
|
||||
int link_modem_reconfigure(Link *link);
|
||||
@@ -0,0 +1,641 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bus-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-dhcp4.h"
|
||||
#include "networkd-dhcp6.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-setlink.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-ndisc.h"
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-wwan.h"
|
||||
#include "parse-util.h"
|
||||
#include "sd-dhcp-client.h"
|
||||
#include "sd-dhcp6-client.h"
|
||||
#include "sd-ndisc.h"
|
||||
#include "set.h"
|
||||
#include "string-util.h"
|
||||
|
||||
Bearer* bearer_free(Bearer *b) {
|
||||
if (!b)
|
||||
return NULL;
|
||||
|
||||
if (b->modem) {
|
||||
if (b->path)
|
||||
hashmap_remove_value(b->modem->bearers_by_path, b->path, b);
|
||||
if (b->name)
|
||||
hashmap_remove_value(b->modem->bearers_by_name, b->name, b);
|
||||
}
|
||||
|
||||
sd_bus_slot_unref(b->slot_getall);
|
||||
|
||||
free(b->path);
|
||||
free(b->name);
|
||||
free(b->apn);
|
||||
|
||||
in_addr_full_array_free(b->dns, b->n_dns);
|
||||
|
||||
return mfree(b);
|
||||
}
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
bearer_hash_ops,
|
||||
char,
|
||||
string_hash_func,
|
||||
string_compare_func,
|
||||
Bearer,
|
||||
bearer_free);
|
||||
|
||||
int bearer_new(Modem *modem, const char *path, Bearer **ret) {
|
||||
_cleanup_(bearer_freep) Bearer *b = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
assert(modem);
|
||||
assert(path);
|
||||
|
||||
if (hashmap_contains(modem->bearers_by_path, path))
|
||||
return -EEXIST;
|
||||
|
||||
p = strdup(path);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
b = new(Bearer, 1);
|
||||
if (!b)
|
||||
return -ENOMEM;
|
||||
|
||||
*b = (Bearer) {
|
||||
.modem = modem,
|
||||
.path = TAKE_PTR(p),
|
||||
};
|
||||
|
||||
r = hashmap_ensure_put(&modem->bearers_by_path, &bearer_hash_ops, b->path, b);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = b;
|
||||
TAKE_PTR(b);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bearer_set_name(Bearer *b, const char *name) {
|
||||
Bearer *old;
|
||||
int r;
|
||||
|
||||
assert(b);
|
||||
assert(b->modem);
|
||||
assert(name);
|
||||
|
||||
if (streq_ptr(b->name, name))
|
||||
return 0;
|
||||
|
||||
if (b->name)
|
||||
hashmap_remove_value(b->modem->bearers_by_name, b->name, b);
|
||||
|
||||
if (isempty(name)) {
|
||||
b->name = mfree(b->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = free_and_strdup(&b->name, name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/*
|
||||
* FIXME: it is possible during reconnect that an interface is already
|
||||
* registered in the hash map: if simple connect options
|
||||
* are changed, e.g. externally modified .network file and then
|
||||
* reloaded with 'networkctl reload'. This may create a new bearer
|
||||
* attached to the same inerface name, e.g. "wwan0". The order in which
|
||||
* we parse the bearer properties is undetermined and it can be that we
|
||||
* need to raplce the old one with the new one now, so only one bearer
|
||||
* with the given interface name exists.
|
||||
*/
|
||||
old = hashmap_get(b->modem->bearers_by_name, name);
|
||||
if (old) {
|
||||
hashmap_remove_value(old->modem->bearers_by_name, name, old);
|
||||
old->name = mfree(old->name);
|
||||
}
|
||||
|
||||
return hashmap_ensure_put(&b->modem->bearers_by_name, &bearer_hash_ops, b->name, b);
|
||||
}
|
||||
|
||||
int bearer_get_by_path(Manager *manager, const char *path, Modem **ret_modem, Bearer **ret_bearer) {
|
||||
Modem *modem;
|
||||
Bearer *b;
|
||||
|
||||
assert(manager);
|
||||
assert(path);
|
||||
|
||||
HASHMAP_FOREACH(modem, manager->modems_by_path) {
|
||||
b = hashmap_get(modem->bearers_by_path, path);
|
||||
if (!b)
|
||||
continue;
|
||||
|
||||
if (ret_bearer)
|
||||
*ret_bearer = b;
|
||||
if (ret_modem)
|
||||
*ret_modem = modem;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
Modem* modem_free(Modem *modem) {
|
||||
if (!modem)
|
||||
return NULL;
|
||||
|
||||
if (modem->bearers_by_name)
|
||||
hashmap_free(modem->bearers_by_name);
|
||||
|
||||
if (modem->bearers_by_path)
|
||||
hashmap_free(modem->bearers_by_path);
|
||||
|
||||
if (modem->manager)
|
||||
hashmap_remove_value(modem->manager->modems_by_path, modem->path, modem);
|
||||
|
||||
sd_bus_slot_unref(modem->slot_propertieschanged);
|
||||
sd_bus_slot_unref(modem->slot_statechanged);
|
||||
sd_bus_slot_unref(modem->slot_connect);
|
||||
|
||||
free(modem->path);
|
||||
free(modem->manufacturer);
|
||||
free(modem->model);
|
||||
free(modem->port_name);
|
||||
|
||||
return mfree(modem);
|
||||
}
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
modems_hash_ops,
|
||||
char,
|
||||
string_hash_func,
|
||||
string_compare_func,
|
||||
Modem,
|
||||
modem_free);
|
||||
|
||||
int modem_new(Manager *m, const char *path, Modem **ret) {
|
||||
_cleanup_(modem_freep) Modem *modem = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(path);
|
||||
|
||||
if (hashmap_contains(m->modems_by_path, path))
|
||||
return -EEXIST;
|
||||
|
||||
p = strdup(path);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
modem = new(Modem, 1);
|
||||
if (!modem)
|
||||
return -ENOMEM;
|
||||
|
||||
*modem = (Modem) {
|
||||
.manager = m,
|
||||
.path = TAKE_PTR(p),
|
||||
};
|
||||
|
||||
r = hashmap_ensure_put(&m->modems_by_path, &modems_hash_ops, modem->path, modem);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = modem;
|
||||
|
||||
TAKE_PTR(modem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int modem_get_by_path(Manager *m, const char *path, Modem **ret) {
|
||||
Modem *modem;
|
||||
|
||||
assert(m);
|
||||
assert(path);
|
||||
|
||||
modem = hashmap_get(m->modems_by_path, path);
|
||||
if (!modem)
|
||||
return -ENOENT;
|
||||
|
||||
if (ret)
|
||||
*ret = modem;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int link_get_modem(Link *link, Modem **ret) {
|
||||
Modem *modem;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
assert(link->ifname);
|
||||
|
||||
HASHMAP_FOREACH(modem, link->manager->modems_by_path)
|
||||
if (modem->port_name && streq(modem->port_name, link->ifname)) {
|
||||
*ret = modem;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int link_get_bearer(Link *link, Bearer **ret) {
|
||||
Modem *modem;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
assert(link->ifname);
|
||||
|
||||
HASHMAP_FOREACH(modem, link->manager->modems_by_path) {
|
||||
Bearer *b;
|
||||
|
||||
b = hashmap_get(modem->bearers_by_name, link->ifname);
|
||||
if (!b)
|
||||
continue;
|
||||
|
||||
if (ret)
|
||||
*ret = b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int link_dhcp_enabled_by_bearer(Link *link, int family) {
|
||||
Bearer *b;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(IN_SET(family, AF_INET, AF_INET6));
|
||||
|
||||
r = link_get_bearer(link, &b);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!b->connected)
|
||||
return false;
|
||||
|
||||
if (!FLAGS_SET(b->ip_type, family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6))
|
||||
return false;
|
||||
|
||||
return (family == AF_INET ? b->ip4_method : b->ip6_method) == MM_BEARER_IP_METHOD_DHCP;
|
||||
}
|
||||
|
||||
static int bearer_address_handler(
|
||||
sd_netlink *rtnl,
|
||||
sd_netlink_message *m,
|
||||
Request *req,
|
||||
Link *link,
|
||||
Address *address) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
r = address_configure_handler_internal(m, link, address);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (link->bearer_messages == 0) {
|
||||
link->bearer_configured = true;
|
||||
link_check_ready(link);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int link_request_bearer_address(
|
||||
Link *link,
|
||||
int family,
|
||||
const union in_addr_union *addr,
|
||||
unsigned prefixlen) {
|
||||
|
||||
_cleanup_(address_unrefp) Address *address = NULL;
|
||||
Address *existing;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(IN_SET(family, AF_INET, AF_INET6));
|
||||
assert(addr);
|
||||
|
||||
if (!in_addr_is_set(family, addr))
|
||||
return 0;
|
||||
|
||||
r = address_new(&address);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
address->source = NETWORK_CONFIG_SOURCE_MODEM_MANAGER;
|
||||
address->family = family;
|
||||
address->in_addr = *addr;
|
||||
address->prefixlen = prefixlen;
|
||||
|
||||
if (address_get(link, address, &existing) < 0) /* The address is new. */
|
||||
link->bearer_configured = false;
|
||||
else
|
||||
address_unmark(existing);
|
||||
|
||||
r = link_request_address(link, address, &link->bearer_messages,
|
||||
bearer_address_handler, /* ret = */ NULL);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to request address provided by bearer: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bearer_route_handler(
|
||||
sd_netlink *rtnl,
|
||||
sd_netlink_message *m,
|
||||
Request *req,
|
||||
Link *link,
|
||||
Route *route) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
r = route_configure_handler_internal(m, req, route);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (link->bearer_messages == 0) {
|
||||
link->bearer_configured = true;
|
||||
link_check_ready(link);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int link_request_bearer_route(
|
||||
Link *link,
|
||||
int family,
|
||||
const union in_addr_union *gw,
|
||||
const union in_addr_union *prefsrc) {
|
||||
|
||||
_cleanup_(route_unrefp) Route *route = NULL;
|
||||
Route *existing;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(IN_SET(family, AF_INET, AF_INET6));
|
||||
assert(gw);
|
||||
assert(prefsrc);
|
||||
|
||||
if (!in_addr_is_set(family, gw))
|
||||
return 0;
|
||||
|
||||
if (link->network->mm_use_gateway == 0)
|
||||
return 0;
|
||||
|
||||
r = route_new(&route);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
route->source = NETWORK_CONFIG_SOURCE_MODEM_MANAGER;
|
||||
route->family = family;
|
||||
route->nexthop.family = family;
|
||||
route->nexthop.gw = *gw;
|
||||
if (link->network->mm_route_metric_set) {
|
||||
route->priority = link->network->mm_route_metric;
|
||||
route->priority_set = true;
|
||||
}
|
||||
|
||||
if (prefsrc)
|
||||
route->prefsrc = *prefsrc;
|
||||
|
||||
if (route_get(link->manager, route, &existing) < 0) /* This is a new route. */
|
||||
link->bearer_configured = false;
|
||||
else
|
||||
route_unmark(existing);
|
||||
|
||||
r = link_request_route(link, route, &link->bearer_messages, bearer_route_handler);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to request gateway provided by bearer: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int link_apply_bearer_impl(Link *link, Bearer *b) {
|
||||
Address *address;
|
||||
Route *route;
|
||||
int r, ret = 0;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
|
||||
return 0;
|
||||
|
||||
/* First, mark bearer configs. */
|
||||
SET_FOREACH(address, link->addresses) {
|
||||
if (address->source != NETWORK_CONFIG_SOURCE_MODEM_MANAGER)
|
||||
continue;
|
||||
|
||||
address_mark(address);
|
||||
}
|
||||
|
||||
SET_FOREACH(route, link->manager->routes) {
|
||||
if (route->source != NETWORK_CONFIG_SOURCE_MODEM_MANAGER)
|
||||
continue;
|
||||
|
||||
route_mark(route);
|
||||
}
|
||||
|
||||
if (b && FLAGS_SET(b->ip_type, ADDRESS_FAMILY_IPV4)) {
|
||||
if (b->connected && b->ip4_method == MM_BEARER_IP_METHOD_STATIC) {
|
||||
r = link_request_bearer_address(link, AF_INET, &b->ip4_address, b->ip4_prefixlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = link_request_bearer_route(link, AF_INET, &b->ip4_gateway, &b->ip4_address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (b->connected && b->ip4_method == MM_BEARER_IP_METHOD_DHCP) {
|
||||
if (!link_dhcp4_enabled(link))
|
||||
log_link_notice(link, "The WWAN connection requested DHCPv4 client, but it is disabled.");
|
||||
|
||||
r = dhcp4_start(link);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to start DHCPv4 client: %m");
|
||||
} else {
|
||||
r = sd_dhcp_client_stop(link->dhcp_client);
|
||||
if (r < 0)
|
||||
ret = log_link_warning_errno(link, r, "Could not stop DHCPv4 client: %m");
|
||||
}
|
||||
}
|
||||
|
||||
if (b && FLAGS_SET(b->ip_type, ADDRESS_FAMILY_IPV6)) {
|
||||
if (b->connected && b->ip6_method == MM_BEARER_IP_METHOD_STATIC) {
|
||||
r = link_request_bearer_address(link, AF_INET6, &b->ip6_address, b->ip6_prefixlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = link_request_bearer_route(link, AF_INET6, &b->ip6_gateway, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (b->connected && b->ip6_method == MM_BEARER_IP_METHOD_DHCP) {
|
||||
if (!link_ndisc_enabled(link) && !link_dhcp6_enabled(link))
|
||||
log_link_notice(link,
|
||||
"The WWAN connection requested IPv6 dynamic address configuration,"
|
||||
"but both IPv6 Router Discovery and DHCPv6 client are disabled.");
|
||||
|
||||
r = ndisc_start(link);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
|
||||
|
||||
r = dhcp6_start(link);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to start DHCPv6 client: %m");
|
||||
} else {
|
||||
r = sd_dhcp6_client_stop(link->dhcp6_client);
|
||||
if (r < 0)
|
||||
ret = log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m");
|
||||
|
||||
r = sd_ndisc_stop(link->ndisc);
|
||||
if (r < 0)
|
||||
ret = log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m");
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally, remove all marked configs. */
|
||||
SET_FOREACH(address, link->addresses) {
|
||||
if (address->source != NETWORK_CONFIG_SOURCE_MODEM_MANAGER)
|
||||
continue;
|
||||
|
||||
if (!address_is_marked(address))
|
||||
continue;
|
||||
|
||||
r = address_remove(address, link);
|
||||
if (r < 0)
|
||||
ret = r;
|
||||
}
|
||||
|
||||
SET_FOREACH(route, link->manager->routes) {
|
||||
if (route->source != NETWORK_CONFIG_SOURCE_MODEM_MANAGER)
|
||||
continue;
|
||||
|
||||
if (!route_is_marked(route))
|
||||
continue;
|
||||
|
||||
r = route_remove(route, link->manager);
|
||||
if (ret)
|
||||
ret = r;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (link->bearer_messages == 0)
|
||||
link->bearer_configured = true;
|
||||
|
||||
if (!link->bearer_configured)
|
||||
link_set_state(link, LINK_STATE_CONFIGURING);
|
||||
|
||||
link_check_ready(link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int link_apply_bearer(Link *link) {
|
||||
Bearer *b = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
(void) link_get_bearer(link, &b);
|
||||
|
||||
r = link_apply_bearer_impl(link, b);
|
||||
if (r < 0)
|
||||
link_enter_failed(link);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int bearer_update_link(Bearer *b) {
|
||||
Link *link;
|
||||
int r;
|
||||
|
||||
assert(b);
|
||||
assert(b->modem);
|
||||
assert(b->modem->manager);
|
||||
|
||||
if (!b->name)
|
||||
return 0;
|
||||
|
||||
if (link_get_by_name(b->modem->manager, b->name, &link) < 0)
|
||||
return 0;
|
||||
|
||||
r = link_apply_bearer_impl(link, b);
|
||||
if (r < 0)
|
||||
link_enter_failed(link);
|
||||
|
||||
/*
|
||||
* Need to bring up the interface after the modem has connected.
|
||||
* This is because ModemManger does the following while connecting:
|
||||
* <msg> [1755871777.322239] [modem2] state changed (registered -> connecting)
|
||||
* <dbg> [1755871777.325012] [modem2/bearer5] launching connection with QMI port (cdc-wdm0) and data port (wwan0) (multiplex none)
|
||||
* <dbg> [1755871777.327665] [cdc-wdm0/qmi] bringing down data interface 'wwan0'
|
||||
* <dbg> [1755871777.330108] [modem2/wwan0/net] interface index: 9
|
||||
* <dbg> [1755871777.335265] [cdc-wdm0/qmi] deleting all links in data interface 'wwan0'
|
||||
*/
|
||||
|
||||
r = link_request_to_bring_up_or_down(link, b->connected);
|
||||
if (r < 0)
|
||||
link_enter_failed(link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bearer_drop(Bearer *b) {
|
||||
assert(b);
|
||||
|
||||
b->connected = false;
|
||||
b->apn = mfree(b->apn);
|
||||
|
||||
(void) bearer_update_link(b);
|
||||
|
||||
bearer_free(b);
|
||||
}
|
||||
|
||||
int config_parse_mm_route_metric(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Network *network = userdata;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
network->mm_route_metric_set = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = safe_atou32(rvalue, &network->mm_route_metric);
|
||||
if (r < 0)
|
||||
return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
|
||||
|
||||
network->mm_route_metric_set = true;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "conf-parser-forward.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "network-util.h"
|
||||
#include "networkd-wwan-bus.h"
|
||||
|
||||
typedef struct Modem Modem;
|
||||
|
||||
typedef struct Bearer {
|
||||
Modem *modem;
|
||||
|
||||
sd_bus_slot *slot_getall; /* for GetAll method */
|
||||
|
||||
char *path; /* DBus path e.g /org/freedesktop/ModemManager/Bearer/0 */
|
||||
char *name; /* Interface property, e.g. wwan0 */
|
||||
char *apn; /* "apn" field in Properties */
|
||||
AddressFamily ip_type; /* "ip-type" field in Properties */
|
||||
|
||||
/* Ip4Config or IP6Config property */
|
||||
MMBearerIpMethod ip4_method;
|
||||
MMBearerIpMethod ip6_method;
|
||||
unsigned ip4_prefixlen;
|
||||
unsigned ip6_prefixlen;
|
||||
union in_addr_union ip4_address;
|
||||
union in_addr_union ip6_address;
|
||||
union in_addr_union ip4_gateway;
|
||||
union in_addr_union ip6_gateway;
|
||||
struct in_addr_full **dns;
|
||||
size_t n_dns;
|
||||
uint32_t ip4_mtu;
|
||||
uint32_t ip6_mtu;
|
||||
|
||||
bool connected; /* Connected property */
|
||||
} Bearer;
|
||||
|
||||
typedef struct Modem {
|
||||
Manager *manager;
|
||||
|
||||
sd_bus_slot *slot_propertieschanged;
|
||||
sd_bus_slot *slot_statechanged;
|
||||
sd_bus_slot *slot_connect;
|
||||
|
||||
char *path; /* DBus path e.g /org/freedesktop/ModemManager/Modem/0 */
|
||||
char *manufacturer; /* The "Manufacturer" property */
|
||||
char *model; /* The "Model" property */
|
||||
char *port_name; /* MM_MODEM_PORT_TYPE_NET of Ports property */
|
||||
|
||||
Hashmap *bearers_by_path;
|
||||
Hashmap *bearers_by_name;
|
||||
|
||||
MMModemState state;
|
||||
MMModemStateFailedReason state_fail_reason;
|
||||
ModemReconnectState reconnect_state;
|
||||
} Modem;
|
||||
|
||||
int bearer_new(Modem *modem, const char *path, Bearer **ret);
|
||||
Bearer* bearer_free(Bearer *b);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Bearer*, bearer_free);
|
||||
|
||||
int bearer_set_name(Bearer *b, const char *name);
|
||||
|
||||
int bearer_get_by_path(Manager *manager, const char *path, Modem **ret_modem, Bearer **ret_bearer);
|
||||
int link_get_bearer(Link *link, Bearer **ret);
|
||||
|
||||
int link_dhcp_enabled_by_bearer(Link *link, int family);
|
||||
|
||||
int link_apply_bearer(Link *link);
|
||||
int bearer_update_link(Bearer *b);
|
||||
void bearer_drop(Bearer *b);
|
||||
|
||||
int modem_new(Manager *m, const char *path, Modem **ret);
|
||||
Modem* modem_free(Modem *modem);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Modem*, modem_free);
|
||||
|
||||
int modem_get_by_path(Manager *m, const char *path, Modem **ret);
|
||||
int link_get_modem(Link *link, Modem **ret);
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_mm_route_metric);
|
||||
Reference in New Issue
Block a user