platform: add dump/update function for MPTCP addresses

Since the generic netlink API does (currently) not support notifications
about changes of the MPTCP addresses, we won't get notifications when
they change, and it seems wrong to put such things in the NMPlatform
cache.

We can just get the list of endpoints by polling, so add a function
nm_platform_mptcp_addrs_dump() for that.

Also, add nm_platform_mptcp_addr_update() which can add/remove/update
MPTCP addresses.
This commit is contained in:
Thomas Haller 2022-07-14 22:19:47 +02:00
parent ec64559caa
commit ce635c4339
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
5 changed files with 366 additions and 19 deletions

View file

@ -9,6 +9,7 @@
#include <syslog.h>
#include "libnm-platform/nm-linux-platform.h"
#include "libnm-platform/nmp-object.h"
#include "nm-test-utils-core.h"
@ -50,6 +51,18 @@ read_argv(int *argc, char ***argv)
return TRUE;
}
/*****************************************************************************/
static void
mptcp_addr_dump(NMPlatform *platform)
{
gs_unref_ptrarray GPtrArray *addrs = NULL;
addrs = nm_platform_mptcp_addrs_dump(platform);
}
/*****************************************************************************/
int
main(int argc, char **argv)
{
@ -69,6 +82,8 @@ main(int argc, char **argv)
nm_linux_platform_setup();
mptcp_addr_dump(NM_PLATFORM_GET);
if (global_opt.persist)
g_main_loop_run(loop);

View file

@ -9334,6 +9334,9 @@ object_delete(NMPlatform *platform, const NMPObject *obj)
case NMP_OBJECT_TYPE_TFILTER:
nlmsg = _nl_msg_new_tfilter(RTM_DELTFILTER, 0, NMP_OBJECT_CAST_TFILTER(obj));
break;
case NMP_OBJECT_TYPE_MPTCP_ADDR:
return (nm_platform_mptcp_addr_update(platform, FALSE, NMP_OBJECT_CAST_MPTCP_ADDR(obj))
>= 0);
default:
break;
}
@ -10101,6 +10104,301 @@ out:
/*****************************************************************************/
static const NMPObject *
_mptcp_addrs_dump_parse_addr(struct nlattr *attr)
{
static const struct nla_policy policy[] = {
[MPTCP_PM_ADDR_ATTR_FAMILY] = {.type = NLA_U16},
[MPTCP_PM_ADDR_ATTR_ID] = {.type = NLA_U8},
[MPTCP_PM_ADDR_ATTR_ADDR4] = {.minlen = sizeof(in_addr_t)},
[MPTCP_PM_ADDR_ATTR_ADDR6] = {.minlen = sizeof(struct in6_addr)},
[MPTCP_PM_ADDR_ATTR_PORT] = {.type = NLA_U16},
[MPTCP_PM_ADDR_ATTR_FLAGS] = {.type = NLA_U32},
[MPTCP_PM_ADDR_ATTR_IF_IDX] = {.type = NLA_S32},
};
struct nlattr *tb[G_N_ELEMENTS(policy)];
nm_auto_nmpobj NMPObject *obj = NULL;
NMPlatformMptcpAddr *mptcp_addr;
int addr_family;
int addr_attr;
if (nla_parse_nested_arr(tb, attr, policy) < 0)
return NULL;
if (!tb[MPTCP_PM_ADDR_ATTR_ID])
return NULL;
obj = nmp_object_new(NMP_OBJECT_TYPE_MPTCP_ADDR, NULL);
mptcp_addr = &obj->mptcp_addr;
mptcp_addr->id = nla_get_u8(tb[MPTCP_PM_ADDR_ATTR_ID]);
if (!tb[MPTCP_PM_ADDR_ATTR_FAMILY]) {
/* If we don't have the family. Only create a stub object containing
* the ID. */
goto out;
}
addr_family = nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_FAMILY]);
if (addr_family == AF_INET)
addr_attr = MPTCP_PM_ADDR_ATTR_ADDR4;
else if (addr_family == AF_INET6)
addr_attr = MPTCP_PM_ADDR_ATTR_ADDR6;
else
goto out;
if (!tb[addr_attr])
goto out;
mptcp_addr->addr_family = addr_family;
memcpy(&mptcp_addr->addr, nla_data(tb[addr_attr]), nm_utils_addr_family_to_size(addr_family));
if (tb[MPTCP_PM_ADDR_ATTR_PORT])
mptcp_addr->port = nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_PORT]);
if (tb[MPTCP_PM_ADDR_ATTR_IF_IDX])
mptcp_addr->ifindex = nla_get_s32(tb[MPTCP_PM_ADDR_ATTR_IF_IDX]);
if (tb[MPTCP_PM_ADDR_ATTR_FLAGS])
mptcp_addr->flags = nla_get_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]);
out:
return g_steal_pointer(&obj);
}
typedef struct {
GPtrArray *addrs;
} FetchMptcpAddrParseData;
static int
_mptcp_addrs_dump_parse_cb(const struct nl_msg *msg, void *arg)
{
static const struct nla_policy policy[] = {
[MPTCP_PM_ATTR_ADDR] = {.type = NLA_NESTED},
};
struct nlattr *tb[G_N_ELEMENTS(policy)];
FetchMptcpAddrParseData *parse_data = arg;
if (genlmsg_parse_arr(nlmsg_hdr(msg), 0, tb, policy) < 0)
return NL_SKIP;
if (tb[MPTCP_PM_ATTR_ADDR]) {
const NMPObject *obj;
obj = _mptcp_addrs_dump_parse_addr(tb[MPTCP_PM_ATTR_ADDR]);
if (obj)
g_ptr_array_add(parse_data->addrs, (gpointer) obj);
}
return NL_OK;
}
#define EXTACK_MSG_BUFSIZE 200
static int
_mptcp_addr_update_err_cb(const struct sockaddr_nl *nla, const struct nlmsgerr *nlerr, void *arg)
{
char *out_extack_msg = arg;
const char *extack_msg;
int errsv;
errsv = nlmsg_parse_error(nlmsg_undata(nlerr), &extack_msg);
if (extack_msg)
g_strlcpy(out_extack_msg, extack_msg, EXTACK_MSG_BUFSIZE);
return errsv;
}
static int
mptcp_addr_update(NMPlatform *platform, NMOptionBool add, const NMPlatformMptcpAddr *addr)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
struct nlattr *nla_nest;
char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
guint16 genl_family_id;
guint8 cmd_num;
const char *cmd_str;
int nle;
char extack_msg[EXTACK_MSG_BUFSIZE];
const struct nl_cb cb = {
.err_cb = _mptcp_addr_update_err_cb,
.err_arg = extack_msg,
};
extack_msg[0] = '\0';
if (add == NM_OPTION_BOOL_DEFAULT) {
cmd_num = MPTCP_PM_CMD_SET_FLAGS;
cmd_str = "update";
} else if (add) {
cmd_num = MPTCP_PM_CMD_ADD_ADDR;
cmd_str = "add";
} else {
cmd_num = MPTCP_PM_CMD_DEL_ADDR;
cmd_str = "delete";
}
nm_assert_addr_family_or_unspec(addr->addr_family);
nm_assert(cmd_num != MPTCP_PM_CMD_ADD_ADDR
|| (addr->port == 0 && addr->addr_family != AF_UNSPEC));
nm_assert(cmd_num != MPTCP_PM_CMD_SET_FLAGS || (addr->id != 0 && addr->port == 0));
nm_assert(cmd_num != MPTCP_PM_CMD_DEL_ADDR || addr->id != 0);
genl_family_id = nm_platform_genl_get_family_id(platform, NMP_GENL_FAMILY_TYPE_MPTCP_PM);
if (genl_family_id == 0) {
_LOGT("mptcp: %s address %s fails because %s generic netlink family is unknown",
cmd_str,
nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf)),
nmp_genl_family_infos[NMP_GENL_FAMILY_TYPE_MPTCP_PM].name);
return -ENOSYS;
}
_LOGT("mptcp: %s address %s",
cmd_str,
nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf)));
nlmsg = nlmsg_alloc_size(nlmsg_total_size(GENL_HDRLEN) + 200);
if (!genlmsg_put(nlmsg,
NL_AUTO_PORT,
NL_AUTO_SEQ,
genl_family_id,
0,
NLM_F_REQUEST,
cmd_num,
MPTCP_PM_VER))
goto nla_put_failure;
nla_nest = nla_nest_start(nlmsg, MPTCP_PM_ATTR_ADDR | NLA_F_NESTED);
if (!nla_nest)
goto nla_put_failure;
if (addr->id != 0)
NLA_PUT_U8(nlmsg, MPTCP_PM_ADDR_ATTR_ID, addr->id);
if (addr->flags != 0)
NLA_PUT_U32(nlmsg, MPTCP_PM_ADDR_ATTR_FLAGS, addr->flags);
if (addr->ifindex != 0)
NLA_PUT_S32(nlmsg, MPTCP_PM_ADDR_ATTR_IF_IDX, addr->ifindex);
if (addr->port != 0)
NLA_PUT_U16(nlmsg, MPTCP_PM_ADDR_ATTR_PORT, addr->port);
if (addr->addr_family != AF_UNSPEC) {
int addr_attr;
NLA_PUT_U16(nlmsg, MPTCP_PM_ADDR_ATTR_FAMILY, addr->addr_family);
if (addr->addr_family == AF_INET)
addr_attr = MPTCP_PM_ADDR_ATTR_ADDR4;
else if (addr->addr_family == AF_INET6)
addr_attr = MPTCP_PM_ADDR_ATTR_ADDR6;
else
g_return_val_if_reached(-NME_BUG);
NLA_PUT(nlmsg, addr_attr, nm_utils_addr_family_to_size(addr->addr_family), &addr->addr);
}
NLA_NEST_END(nlmsg, nla_nest);
nle = nl_send_auto(priv->sk_genl_sync, nlmsg);
if (nle < 0) {
_LOGT("mptcp: %s address %s: failed sending request: %s (%d)",
cmd_str,
nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf)),
nm_strerror(nle),
nle);
return nle;
}
do {
nle = nl_recvmsgs(priv->sk_genl_sync, &cb);
} while (nle == -EAGAIN);
if (nle < 0) {
_LOGT("mptcp: %s address %s: failed: %s (%d)%s%s%s",
cmd_str,
nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf)),
nm_strerror(nle),
nle,
NM_PRINT_FMT_QUOTED(extack_msg[0] != '\0', " \"", extack_msg, "\"", ""));
return nle;
}
_LOGT("mptcp: %s address %s: success",
cmd_str,
nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf)));
return 0;
nla_put_failure:
g_return_val_if_reached(-NME_BUG);
}
static GPtrArray *
mptcp_addrs_dump(NMPlatform *platform)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
gs_unref_ptrarray GPtrArray *addrs = NULL;
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
FetchMptcpAddrParseData parse_data;
guint16 genl_family_id;
int r;
guint i;
genl_family_id = nm_platform_genl_get_family_id(platform, NMP_GENL_FAMILY_TYPE_MPTCP_PM);
if (genl_family_id == 0) {
_LOGT("mptcp: dump addresses fails because %s generic netlink family is unknown",
nmp_genl_family_infos[NMP_GENL_FAMILY_TYPE_MPTCP_PM].name);
return NULL;
}
nlmsg = nlmsg_alloc_size(nlmsg_total_size(GENL_HDRLEN));
if (!genlmsg_put(nlmsg,
NL_AUTO_PORT,
NL_AUTO_SEQ,
genl_family_id,
0,
NLM_F_REQUEST | NLM_F_DUMP,
MPTCP_PM_CMD_GET_ADDR,
MPTCP_PM_VER))
g_return_val_if_reached(NULL);
r = nl_send_auto(priv->sk_genl_sync, nlmsg);
if (r < 0) {
_LOGT("mptcp: dump addresses failed to send dump request: %s", nm_strerror(r));
return NULL;
}
addrs = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref);
parse_data = (FetchMptcpAddrParseData){
.addrs = addrs,
};
nl_recvmsgs(priv->sk_genl_sync,
&((const struct nl_cb){
.valid_cb = _mptcp_addrs_dump_parse_cb,
.valid_arg = (gpointer) &parse_data,
}));
if (_LOGT_ENABLED()) {
_LOGT("mptcp: %u addresses dumped", addrs->len);
for (i = 0; i < addrs->len; i++) {
char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
_LOGT("mptcp: address[%04d]: %s",
i,
nmp_object_to_string(addrs->pdata[i],
NMP_OBJECT_TO_STRING_PUBLIC,
sbuf,
sizeof(sbuf)));
}
}
return g_steal_pointer(&addrs);
}
/*****************************************************************************/
static void
cache_update_link_udev(NMPlatform *platform, int ifindex, struct udev_device *udevice)
{
@ -10589,4 +10887,6 @@ nm_linux_platform_class_init(NMLinuxPlatformClass *klass)
platform_class->process_events = process_events;
platform_class->genl_get_family_id = genl_get_family_id;
platform_class->mptcp_addr_update = mptcp_addr_update;
platform_class->mptcp_addrs_dump = mptcp_addrs_dump;
}

View file

@ -7,6 +7,8 @@
#include "nm-platform.h"
#include "libnm-std-aux/nm-linux-compat.h"
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
@ -402,7 +404,7 @@ const NMPGenlFamilyInfo nmp_genl_family_infos[_NMP_GENL_FAMILY_TYPE_NUM] = {
},
[NMP_GENL_FAMILY_TYPE_MPTCP_PM] =
{
.name = "mptcp_pm",
.name = MPTCP_PM_NAME,
},
[NMP_GENL_FAMILY_TYPE_NL80211] =
{
@ -5320,23 +5322,26 @@ nm_platform_object_delete(NMPlatform *self, const NMPObject *obj)
_CHECK_SELF(self, klass, FALSE);
switch (NMP_OBJECT_GET_TYPE(obj)) {
case NMP_OBJECT_TYPE_ROUTING_RULE:
_LOGD("%s: delete %s",
NMP_OBJECT_GET_CLASS(obj)->obj_type_name,
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
break;
case NMP_OBJECT_TYPE_IP4_ROUTE:
case NMP_OBJECT_TYPE_IP6_ROUTE:
case NMP_OBJECT_TYPE_QDISC:
case NMP_OBJECT_TYPE_TFILTER:
ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj)->ifindex;
_LOG3D("%s: delete %s",
NMP_OBJECT_GET_CLASS(obj)->obj_type_name,
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
break;
default:
g_return_val_if_reached(FALSE);
if (_LOGD_ENABLED()) {
switch (NMP_OBJECT_GET_TYPE(obj)) {
case NMP_OBJECT_TYPE_ROUTING_RULE:
case NMP_OBJECT_TYPE_MPTCP_ADDR:
_LOGD("%s: delete %s",
NMP_OBJECT_GET_CLASS(obj)->obj_type_name,
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
break;
case NMP_OBJECT_TYPE_IP4_ROUTE:
case NMP_OBJECT_TYPE_IP6_ROUTE:
case NMP_OBJECT_TYPE_QDISC:
case NMP_OBJECT_TYPE_TFILTER:
ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj)->ifindex;
_LOG3D("%s: delete %s",
NMP_OBJECT_GET_CLASS(obj)->obj_type_name,
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
break;
default:
g_return_val_if_reached(FALSE);
}
}
return klass->object_delete(self, obj);
@ -9393,6 +9398,24 @@ nm_platform_genl_get_family_id(NMPlatform *self, NMPGenlFamilyType family_type)
/*****************************************************************************/
int
nm_platform_mptcp_addr_update(NMPlatform *self, NMOptionBool add, const NMPlatformMptcpAddr *addr)
{
_CHECK_SELF(self, klass, -NME_BUG);
return klass->mptcp_addr_update(self, add, addr);
}
GPtrArray *
nm_platform_mptcp_addrs_dump(NMPlatform *self)
{
_CHECK_SELF(self, klass, NULL);
return klass->mptcp_addrs_dump(self);
}
/*****************************************************************************/
GHashTable *
nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex)
{

View file

@ -1394,6 +1394,10 @@ typedef struct {
guint16 (*genl_get_family_id)(NMPlatform *platform, NMPGenlFamilyType family_type);
int (*mptcp_addr_update)(NMPlatform *self, NMOptionBool add, const NMPlatformMptcpAddr *addr);
GPtrArray *(*mptcp_addrs_dump)(NMPlatform *self);
} NMPlatformClass;
/* NMPlatform signals
@ -2668,4 +2672,9 @@ gboolean nm_platform_ip_address_match(int addr_family,
guint16 nm_platform_genl_get_family_id(NMPlatform *self, NMPGenlFamilyType family_type);
int
nm_platform_mptcp_addr_update(NMPlatform *self, NMOptionBool add, const NMPlatformMptcpAddr *addr);
GPtrArray *nm_platform_mptcp_addrs_dump(NMPlatform *self);
#endif /* __NETWORKMANAGER_PLATFORM_H__ */

View file

@ -1785,7 +1785,7 @@ _vt_cmd_obj_is_alive_tfilter(const NMPObject *obj)
static gboolean
_vt_cmd_obj_is_alive_mptcp_addr(const NMPObject *obj)
{
return NM_IN_SET(obj->mptcp_addr.addr_family, AF_INET, AF_INET6);
return NM_IN_SET(obj->mptcp_addr.addr_family, AF_INET, AF_INET6, AF_UNSPEC);
}
gboolean