The branch main has been updated by glebius: URL: https://cgit.FreeBSD.org/src/commit/?id=ee507b70f150855cfff0ef1080c38a3c70c1e7cd
commit ee507b70f150855cfff0ef1080c38a3c70c1e7cd Author: Gleb Smirnoff <gleb...@freebsd.org> AuthorDate: 2025-02-05 18:09:06 +0000 Commit: Gleb Smirnoff <gleb...@freebsd.org> CommitDate: 2025-02-05 18:09:06 +0000 netlink: refactor KPI for generic Netlink modules Now that the family and group are completely private to netlink_generic.c, provide a simple and robust KPI, that would require very simple guarantees from both KPI and the module: * Strings are used only for family and group registration, that return ID: uint16_t genl_register_family(const char *name, ... uint32_t genl_register_group(uint16_t family, const char *name, ... * Once created families and groups are guaranteed to not disappear and be addressable by their ID. * All subsequent calls, including deregistration shall use ID. Reviewed by: kp Differential Revision: https://reviews.freebsd.org/D48845 --- sys/netinet/ip_carp.c | 11 +- sys/netlink/netlink_ctl.h | 15 +-- sys/netlink/netlink_generic.c | 254 ++++++++++++++++------------------------- sys/netlink/netlink_sysevent.c | 7 +- sys/netlink/netlink_var.h | 4 - sys/netpfil/pf/pf_nl.c | 6 +- sys/netpfil/pf/pflow.c | 10 +- sys/rpc/clnt_nl.c | 5 +- sys/tests/ktest.c | 7 +- 9 files changed, 124 insertions(+), 195 deletions(-) diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index 871638adb048..5bec8fce3fcb 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -2969,26 +2969,25 @@ static const struct genl_cmd carp_cmds[] = { }, }; +static uint16_t carp_family_id; static void carp_nl_register(void) { bool ret __diagused; - int family_id __diagused; NL_VERIFY_PARSERS(all_parsers); - family_id = genl_register_family(CARP_NL_FAMILY_NAME, 0, 2, + carp_family_id = genl_register_family(CARP_NL_FAMILY_NAME, 0, 2, CARP_NL_CMD_MAX); - MPASS(family_id != 0); + MPASS(carp_family_id != 0); - ret = genl_register_cmds(CARP_NL_FAMILY_NAME, carp_cmds, - nitems(carp_cmds)); + ret = genl_register_cmds(carp_family_id, carp_cmds, nitems(carp_cmds)); MPASS(ret); } static void carp_nl_unregister(void) { - genl_unregister_family(CARP_NL_FAMILY_NAME); + genl_unregister_family(carp_family_id); } static void diff --git a/sys/netlink/netlink_ctl.h b/sys/netlink/netlink_ctl.h index 895f70322a29..e7566552ea32 100644 --- a/sys/netlink/netlink_ctl.h +++ b/sys/netlink/netlink_ctl.h @@ -93,16 +93,13 @@ struct genl_cmd { uint16_t genl_register_family(const char *family_name, size_t hdrsize, uint16_t family_version, uint16_t max_attr_idx); -bool genl_unregister_family(const char *family_name); -bool genl_register_cmds(const char *family_name, const struct genl_cmd *cmds, - int count); -uint32_t genl_register_group(const char *family_name, const char *group_name); +void genl_unregister_family(uint16_t family); +bool genl_register_cmds(uint16_t family, const struct genl_cmd *cmds, + u_int count); +uint32_t genl_register_group(uint16_t family, const char *group_name); -struct genl_family; -const char *genl_get_family_name(const struct genl_family *gf); -uint16_t genl_get_family_id(const struct genl_family *gf); - -typedef void (*genl_family_event_handler_t)(void *arg, const struct genl_family *gf, int action); +typedef void (*genl_family_event_handler_t)(void *arg, const char *family_name, + uint16_t family_id, u_int action); EVENTHANDLER_DECLARE(genl_family_event, genl_family_event_handler_t); struct thread; diff --git a/sys/netlink/netlink_generic.c b/sys/netlink/netlink_generic.c index 30c73133134b..0714f22382cb 100644 --- a/sys/netlink/netlink_generic.c +++ b/sys/netlink/netlink_generic.c @@ -89,6 +89,24 @@ static struct genl_group { }, }; +static inline struct genl_family * +genl_family(uint16_t family_id) +{ + struct genl_family *gf; + + gf = &families[family_id - GENL_MIN_ID]; + KASSERT(family_id - GENL_MIN_ID < MAX_FAMILIES && + gf->family_name != NULL, ("family %u does not exist", family_id)); + return (gf); +} + +static inline uint16_t +genl_family_id(const struct genl_family *gf) +{ + MPASS(gf >= &families[0] && gf < &families[MAX_FAMILIES]); + return ((uint16_t)(gf - &families[0]) + GENL_MIN_ID); +} + /* * Handler called by netlink subsystem when matching netlink message is received */ @@ -96,22 +114,26 @@ static int genl_handle_message(struct nlmsghdr *hdr, struct nl_pstate *npt) { struct nlpcb *nlp = npt->nlp; - struct genl_family *gf = NULL; + struct genl_family *gf; + uint16_t family_id; int error = 0; - int family_id = (int)hdr->nlmsg_type - GENL_MIN_ID; - - if (__predict_false(family_id < 0 || (gf = genl_get_family(family_id)) == NULL)) { - NLP_LOG(LOG_DEBUG, nlp, "invalid message type: %d", hdr->nlmsg_type); - return (ENOTSUP); - } - if (__predict_false(hdr->nlmsg_len < sizeof(struct nlmsghdr) + GENL_HDRLEN)) { - NLP_LOG(LOG_DEBUG, nlp, "invalid message size: %d", hdr->nlmsg_len); + NLP_LOG(LOG_DEBUG, nlp, "invalid message size: %d", + hdr->nlmsg_len); return (EINVAL); } + family_id = hdr->nlmsg_type - GENL_MIN_ID; + gf = &families[family_id]; + if (__predict_false(family_id >= MAX_FAMILIES || + gf->family_name == NULL)) { + NLP_LOG(LOG_DEBUG, nlp, "invalid message type: %d", + hdr->nlmsg_type); + return (ENOTSUP); + } + struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1); if (ghdr->cmd >= gf->family_cmd_size || gf->family_cmds[ghdr->cmd].cmd_cb == NULL) { @@ -158,7 +180,7 @@ dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr, ghdr_new->reserved = 0; nlattr_add_string(nw, CTRL_ATTR_FAMILY_NAME, gf->family_name); - nlattr_add_u16(nw, CTRL_ATTR_FAMILY_ID, genl_get_family_id(gf)); + nlattr_add_u16(nw, CTRL_ATTR_FAMILY_ID, genl_family_id(gf)); nlattr_add_u32(nw, CTRL_ATTR_VERSION, gf->family_version); nlattr_add_u32(nw, CTRL_ATTR_HDRSIZE, gf->family_hdrsize); nlattr_add_u32(nw, CTRL_ATTR_MAXATTR, gf->family_attr_max); @@ -185,9 +207,10 @@ dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr, int off = nlattr_add_nested(nw, CTRL_ATTR_MCAST_GROUPS); if (off == 0) goto enomem; - for (int i = 0, cnt = 0; i < MAX_GROUPS; i++) { - struct genl_group *gg = genl_get_group(i); - if (gg == NULL || gg->group_family != gf) + for (u_int i = 0, cnt = 0; i < MAX_GROUPS; i++) { + struct genl_group *gg = &groups[i]; + + if (gg->group_family != gf) continue; int cmd_off = nlattr_add_nested(nw, ++cnt); @@ -207,14 +230,9 @@ enomem: return (ENOMEM); } - -/* Declare ourself as a user */ -static void nlctrl_notify(void *arg, const struct genl_family *gf, int action); -static eventhandler_tag family_event_tag; - struct nl_parsed_family { - uint32_t family_id; char *family_name; + uint16_t family_id; uint8_t version; }; @@ -232,18 +250,6 @@ static struct nlattr_parser nla_p_generic[] = { #undef _OUT NL_DECLARE_PARSER(genl_parser, struct genlmsghdr, nlf_p_generic, nla_p_generic); -static bool -match_family(const struct genl_family *gf, const struct nl_parsed_family *attrs) -{ - if (gf->family_name == NULL) - return (false); - if (attrs->family_id != 0 && attrs->family_id != genl_get_family_id(gf)) - return (false); - if (attrs->family_name != NULL && strcmp(attrs->family_name, gf->family_name)) - return (false); - return (true); -} - static int nlctrl_handle_getfamily(struct nlmsghdr *hdr, struct nl_pstate *npt) { @@ -259,21 +265,27 @@ nlctrl_handle_getfamily(struct nlmsghdr *hdr, struct nl_pstate *npt) }; if (attrs.family_id != 0 || attrs.family_name != NULL) { - /* Resolve request */ - for (int i = 0; i < MAX_FAMILIES; i++) { - struct genl_family *gf = genl_get_family(i); - if (gf != NULL && match_family(gf, &attrs)) { - error = dump_family(hdr, &ghdr, gf, npt->nw); - return (error); - } + for (u_int i = 0; i < MAX_FAMILIES; i++) { + struct genl_family *gf = &families[i]; + + if (gf->family_name == NULL) + continue; + if (attrs.family_id != 0 && + attrs.family_id != genl_family_id(gf)) + continue; + if (attrs.family_name != NULL && + strcmp(attrs.family_name, gf->family_name) != 0) + continue; + return (dump_family(hdr, &ghdr, gf, npt->nw)); } return (ENOENT); } hdr->nlmsg_flags = hdr->nlmsg_flags | NLM_F_MULTI; - for (int i = 0; i < MAX_FAMILIES; i++) { - struct genl_family *gf = genl_get_family(i); - if (gf != NULL && match_family(gf, &attrs)) { + for (u_int i = 0; i < MAX_FAMILIES; i++) { + struct genl_family *gf = &families[i]; + + if (gf->family_name != NULL) { error = dump_family(hdr, &ghdr, gf, npt->nw); if (error != 0) break; @@ -289,12 +301,15 @@ nlctrl_handle_getfamily(struct nlmsghdr *hdr, struct nl_pstate *npt) } static void -nlctrl_notify(void *arg __unused, const struct genl_family *gf, int cmd) +nlctrl_notify(void *arg __unused, const char *family_name __unused, + uint16_t family_id, u_int cmd) { struct nlmsghdr hdr = {.nlmsg_type = NETLINK_GENERIC }; struct genlmsghdr ghdr = { .cmd = cmd }; + struct genl_family *gf; struct nl_writer nw; + gf = genl_family(family_id); if (!nl_writer_group(&nw, NLMSG_SMALL, NETLINK_GENERIC, CTRL_GROUP_ID, 0, false)) { NL_LOG(LOG_DEBUG, "error allocating group writer"); @@ -306,6 +321,7 @@ nlctrl_notify(void *arg __unused, const struct genl_family *gf, int cmd) } static const struct nlhdr_parser *all_parsers[] = { &genl_parser }; +static eventhandler_tag family_event_tag; static void genl_load_all(void *u __unused) @@ -338,30 +354,6 @@ SX_SYSINIT(genl_lock, &sx_lock, "genetlink lock"); #define GENL_ASSERT_LOCKED() sx_assert(&sx_lock, SA_LOCKED) #define GENL_ASSERT_XLOCKED() sx_assert(&sx_lock, SA_XLOCKED) -static struct genl_family * -find_family(const char *family_name) -{ - GENL_ASSERT_LOCKED(); - for (u_int i = 0; i < MAX_FAMILIES; i++) - if (families[i].family_name != NULL && - strcmp(families[i].family_name, family_name) == 0) - return (&families[i]); - - return (NULL); -} - -static struct genl_family * -find_empty_family_id(const char *family_name) -{ - GENL_ASSERT_LOCKED(); - /* Microoptimization: index 0 is reserved for the control family */ - for (u_int i = 1; i < MAX_FAMILIES; i++) - if (families[i].family_name == NULL) - return (&families[i]); - - return (NULL); -} - uint16_t genl_register_family(const char *family_name, size_t hdrsize, uint16_t family_version, uint16_t max_attr_idx) @@ -369,13 +361,21 @@ genl_register_family(const char *family_name, size_t hdrsize, struct genl_family *gf; uint16_t family_id; + MPASS(family_name != NULL); + GENL_LOCK(); - if (find_family(family_name) != NULL) { - GENL_UNLOCK(); - return (0); - } + for (u_int i = 0; i < MAX_FAMILIES; i++) + if (families[i].family_name != NULL && + strcmp(families[i].family_name, family_name) == 0) + return (0); - gf = find_empty_family_id(family_name); + /* Microoptimization: index 0 is reserved for the control family. */ + gf = NULL; + for (u_int i = 1; i < MAX_FAMILIES; i++) + if (families[i].family_name == NULL) { + gf = &families[i]; + break; + } KASSERT(gf, ("%s: maximum of %u generic netlink families allocated", __func__, MAX_FAMILIES)); @@ -385,30 +385,27 @@ genl_register_family(const char *family_name, size_t hdrsize, .family_hdrsize = hdrsize, .family_attr_max = max_attr_idx, }; - family_id = genl_get_family_id(gf); + family_id = genl_family_id(gf); GENL_UNLOCK(); NL_LOG(LOG_DEBUG2, "Registered family %s id %d", gf->family_name, family_id); - EVENTHANDLER_INVOKE(genl_family_event, gf, CTRL_CMD_NEWFAMILY); + EVENTHANDLER_INVOKE(genl_family_event, gf->family_name, family_id, + CTRL_CMD_NEWFAMILY); return (family_id); } -static void -free_family(struct genl_family *gf) +void +genl_unregister_family(uint16_t family_id) { - if (gf->family_cmds != NULL) - free(gf->family_cmds, M_NETLINK); -} + struct genl_family *gf; -/* - * unregister groups of a given family - */ -static void -unregister_groups(const struct genl_family *gf) -{ + GENL_LOCK(); + gf = genl_family(family_id); + EVENTHANDLER_INVOKE(genl_family_event, gf->family_name, + family_id, CTRL_CMD_DELFAMILY); for (u_int i = 0; i < MAX_GROUPS; i++) { struct genl_group *gg = &groups[i]; if (gg->group_family == gf && gg->group_name != NULL) { @@ -416,44 +413,21 @@ unregister_groups(const struct genl_family *gf) gg->group_name = NULL; } } -} - -/* - * Can sleep, I guess - */ -bool -genl_unregister_family(const char *family_name) -{ - bool found = false; - - GENL_LOCK(); - struct genl_family *gf = find_family(family_name); - - if (gf != NULL) { - EVENTHANDLER_INVOKE(genl_family_event, gf, CTRL_CMD_DELFAMILY); - found = true; - unregister_groups(gf); - /* TODO: zero pointer first */ - free_family(gf); - bzero(gf, sizeof(*gf)); - } + if (gf->family_cmds != NULL) + free(gf->family_cmds, M_NETLINK); + bzero(gf, sizeof(*gf)); GENL_UNLOCK(); - - return (found); } bool -genl_register_cmds(const char *family_name, const struct genl_cmd *cmds, - int count) +genl_register_cmds(uint16_t family_id, const struct genl_cmd *cmds, + u_int count) { struct genl_family *gf; uint16_t cmd_size; GENL_LOCK(); - if ((gf = find_family(family_name)) == NULL) { - GENL_UNLOCK(); - return (false); - } + gf = genl_family(family_id); cmd_size = gf->family_cmd_size; @@ -490,33 +464,23 @@ genl_register_cmds(const char *family_name, const struct genl_cmd *cmds, return (true); } -static struct genl_group * -find_group(const struct genl_family *gf, const char *group_name) -{ - for (u_int i = 0; i < MAX_GROUPS; i++) { - struct genl_group *gg = &groups[i]; - if (gg->group_family == gf && - !strcmp(gg->group_name, group_name)) - return (gg); - } - return (NULL); -} - uint32_t -genl_register_group(const char *family_name, const char *group_name) +genl_register_group(uint16_t family_id, const char *group_name) { struct genl_family *gf; uint32_t group_id = 0; - MPASS(family_name != NULL); MPASS(group_name != NULL); GENL_LOCK(); - if ((gf = find_family(family_name)) == NULL || - find_group(gf, group_name) != NULL) { - GENL_UNLOCK(); - return (0); - } + gf = genl_family(family_id); + + for (u_int i = 0; i < MAX_GROUPS; i++) + if (groups[i].group_family == gf && + strcmp(groups[i].group_name, group_name) == 0) { + GENL_UNLOCK(); + return (0); + } /* Microoptimization: index 0 is reserved for the control family */ for (u_int i = 1; i < MAX_GROUPS; i++) { @@ -533,29 +497,3 @@ genl_register_group(const char *family_name, const char *group_name) return (group_id); } - -/* accessors */ -struct genl_family * -genl_get_family(uint16_t family_id) -{ - return ((family_id < MAX_FAMILIES) ? &families[family_id] : NULL); -} - -const char * -genl_get_family_name(const struct genl_family *gf) -{ - return (gf->family_name); -} - -uint16_t -genl_get_family_id(const struct genl_family *gf) -{ - MPASS(gf >= &families[0] && gf < &families[MAX_FAMILIES]); - return ((uint16_t)(gf - &families[0]) + GENL_MIN_ID); -} - -struct genl_group * -genl_get_group(uint32_t group_id) -{ - return ((group_id < MAX_GROUPS) ? &groups[group_id] : NULL); -} diff --git a/sys/netlink/netlink_sysevent.c b/sys/netlink/netlink_sysevent.c index c955ce2e8b45..09e7e50a7409 100644 --- a/sys/netlink/netlink_sysevent.c +++ b/sys/netlink/netlink_sysevent.c @@ -45,7 +45,7 @@ _DECLARE_DEBUG(LOG_INFO); MALLOC_DEFINE(M_NLSE, "nlsysevent", "Memory used for Netlink sysevent"); #define NLSE_FAMILY_NAME "nlsysevent" -static uint32_t ctrl_family_id; +static uint16_t ctrl_family_id; #define MAX_SYSEVENT_GROUPS 64 static struct sysevent_group { @@ -118,7 +118,8 @@ sysevent_new_group(size_t index, const char *name) return; } sysevent_groups[index].name = strdup(name, M_NLSE); - sysevent_groups[index].id = genl_register_group(NLSE_FAMILY_NAME, sysevent_groups[index].name); + sysevent_groups[index].id = genl_register_group(ctrl_family_id, + sysevent_groups[index].name); } static struct sysevent_group * @@ -171,7 +172,7 @@ static void nlsysevent_unload(void) { devctl_unset_notify_hook(); - genl_unregister_family(NLSE_FAMILY_NAME); + genl_unregister_family(ctrl_family_id); for (size_t i = 0; i < MAX_SYSEVENT_GROUPS; i++) { if (sysevent_groups[i].name == NULL) break; diff --git a/sys/netlink/netlink_var.h b/sys/netlink/netlink_var.h index ce10a303f9f7..a59cb2efecd0 100644 --- a/sys/netlink/netlink_var.h +++ b/sys/netlink/netlink_var.h @@ -138,10 +138,6 @@ void nl_set_source_metadata(struct mbuf *m, int num_messages); struct nl_buf *nl_buf_alloc(size_t len, int mflag); void nl_buf_free(struct nl_buf *nb); -/* netlink_generic.c */ -struct genl_family *genl_get_family(uint16_t family_id); -struct genl_group *genl_get_group(uint32_t group_id); - #define MAX_FAMILIES 20 #define MAX_GROUPS 64 diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c index c0f722b1fd18..737e9cf8cab8 100644 --- a/sys/netpfil/pf/pf_nl.c +++ b/sys/netpfil/pf/pf_nl.c @@ -1858,7 +1858,7 @@ static const struct nlhdr_parser *all_parsers[] = { &table_parser, }; -static int family_id; +static uint16_t family_id; static const struct genl_cmd pf_cmds[] = { { @@ -2051,11 +2051,11 @@ pf_nl_register(void) NL_VERIFY_PARSERS(all_parsers); family_id = genl_register_family(PFNL_FAMILY_NAME, 0, 2, PFNL_CMD_MAX); - genl_register_cmds(PFNL_FAMILY_NAME, pf_cmds, nitems(pf_cmds)); + genl_register_cmds(family_id, pf_cmds, nitems(pf_cmds)); } void pf_nl_unregister(void) { - genl_unregister_family(PFNL_FAMILY_NAME); + genl_unregister_family(family_id); } diff --git a/sys/netpfil/pf/pflow.c b/sys/netpfil/pf/pflow.c index 8741d55b622c..ae9d162bb6bf 100644 --- a/sys/netpfil/pf/pflow.c +++ b/sys/netpfil/pf/pflow.c @@ -1783,11 +1783,11 @@ static const struct nlhdr_parser *all_parsers[] = { static unsigned pflow_do_osd_jail_slot; +static uint16_t family_id; static int pflow_init(void) { bool ret; - int family_id __diagused; NL_VERIFY_PARSERS(all_parsers); @@ -1796,10 +1796,10 @@ pflow_init(void) }; pflow_do_osd_jail_slot = osd_jail_register(NULL, methods); - family_id = genl_register_family(PFLOWNL_FAMILY_NAME, 0, 2, PFLOWNL_CMD_MAX); + family_id = genl_register_family(PFLOWNL_FAMILY_NAME, 0, 2, + PFLOWNL_CMD_MAX); MPASS(family_id != 0); - ret = genl_register_cmds(PFLOWNL_FAMILY_NAME, pflow_cmds, - nitems(pflow_cmds)); + ret = genl_register_cmds(family_id, pflow_cmds, nitems(pflow_cmds)); return (ret ? 0 : ENODEV); } @@ -1808,7 +1808,7 @@ static void pflow_uninit(void) { osd_jail_deregister(pflow_do_osd_jail_slot); - genl_unregister_family(PFLOWNL_FAMILY_NAME); + genl_unregister_family(family_id); } static int diff --git a/sys/rpc/clnt_nl.c b/sys/rpc/clnt_nl.c index 177566232cb5..90d159907d3c 100644 --- a/sys/rpc/clnt_nl.c +++ b/sys/rpc/clnt_nl.c @@ -167,8 +167,7 @@ rpcnl_init(void) rpcnl_family_id = genl_register_family(rpcnl_family_name, 0, 1, 1); MPASS(rpcnl_family_id != 0); - rv = genl_register_cmds(rpcnl_family_name, clnt_cmds, - nitems(clnt_cmds)); + rv = genl_register_cmds(rpcnl_family_id, clnt_cmds, nitems(clnt_cmds)); MPASS(rv); rw_init(&rpcnl_global_lock, rpcnl_family_name); } @@ -185,7 +184,7 @@ client_nl_create(const char *name, const rpcprog_t program, uint32_t group; bool rv __diagused; - if ((group = genl_register_group(rpcnl_family_name, name)) == 0) + if ((group = genl_register_group(rpcnl_family_id, name)) == 0) return (NULL); nl = malloc(sizeof(*nl), M_RPC, M_WAITOK); diff --git a/sys/tests/ktest.c b/sys/tests/ktest.c index 694e1f4229b5..640710f2b89e 100644 --- a/sys/tests/ktest.c +++ b/sys/tests/ktest.c @@ -360,18 +360,17 @@ static const struct genl_cmd ktest_cmds[] = { }, }; +static int family_id; static void ktest_nl_register(void) { bool ret __diagused; - int family_id __diagused; NL_VERIFY_PARSERS(all_parsers); family_id = genl_register_family(KTEST_FAMILY_NAME, 0, 1, KTEST_CMD_MAX); MPASS(family_id != 0); - ret = genl_register_cmds(KTEST_FAMILY_NAME, ktest_cmds, - nitems(ktest_cmds)); + ret = genl_register_cmds(family_id, ktest_cmds, nitems(ktest_cmds)); MPASS(ret); } @@ -380,7 +379,7 @@ ktest_nl_unregister(void) { MPASS(TAILQ_EMPTY(&module_list)); - genl_unregister_family(KTEST_FAMILY_NAME); + genl_unregister_family(family_id); } static int