diff --git a/src/core/platform/tests/monitor.c b/src/core/platform/tests/monitor.c index 1a71e6335d..8d0e01074a 100644 --- a/src/core/platform/tests/monitor.c +++ b/src/core/platform/tests/monitor.c @@ -9,6 +9,7 @@ #include #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); diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index fc3ca60aeb..cd685fe20e 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -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; } diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index e6cba638b4..ab9664d2ee 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -7,6 +7,8 @@ #include "nm-platform.h" +#include "libnm-std-aux/nm-linux-compat.h" + #include #include #include @@ -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) { diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index a4aebee573..90ffbed02e 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -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__ */ diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c index 67f508ef14..d06aa9cdf3 100644 --- a/src/libnm-platform/nmp-object.c +++ b/src/libnm-platform/nmp-object.c @@ -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