diff --git a/sys/netlink/netlink_generic.c b/sys/netlink/netlink_generic.c index d4c84a34b850..30c73133134b 100644 --- a/sys/netlink/netlink_generic.c +++ b/sys/netlink/netlink_generic.c @@ -1,551 +1,561 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Alexander V. Chernikov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG_MOD_NAME nl_generic #define DEBUG_MAX_LEVEL LOG_DEBUG3 #include _DECLARE_DEBUG(LOG_INFO); -static int dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr, - const struct genl_family *gf, struct nl_writer *nw); +static int nlctrl_handle_getfamily(struct nlmsghdr *, struct nl_pstate *); + +static struct genl_cmd nlctrl_cmds[] = { + [CTRL_CMD_GETFAMILY] = { + .cmd_num = CTRL_CMD_GETFAMILY, + .cmd_name = "GETFAMILY", + .cmd_cb = nlctrl_handle_getfamily, + .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | + GENL_CMD_CAP_HASPOL, + }, +}; + +static struct genl_family { + const char *family_name; + uint16_t family_hdrsize; + uint16_t family_version; + uint16_t family_attr_max; + uint16_t family_cmd_size; + uint16_t family_num_groups; + struct genl_cmd *family_cmds; +} families[MAX_FAMILIES] = { + [CTRL_FAMILY_ID] = { + .family_name = CTRL_FAMILY_NAME, + .family_hdrsize = 0, + .family_version = 2, + .family_attr_max = CTRL_ATTR_MAX, + .family_cmd_size = CTRL_CMD_GETFAMILY + 1, + .family_cmds = nlctrl_cmds, + .family_num_groups = 1, + }, +}; + +static struct genl_group { + struct genl_family *group_family; + const char *group_name; +} groups[MAX_GROUPS] = { + [CTRL_GROUP_ID] = { + .group_family = &families[CTRL_FAMILY_ID], + .group_name = CTRL_GROUP_NAME, + }, +}; /* * Handler called by netlink subsystem when matching netlink message is received */ static int genl_handle_message(struct nlmsghdr *hdr, struct nl_pstate *npt) { struct nlpcb *nlp = npt->nlp; struct genl_family *gf = NULL; 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); return (EINVAL); } struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1); if (ghdr->cmd >= gf->family_cmd_size || gf->family_cmds[ghdr->cmd].cmd_cb == NULL) { NLP_LOG(LOG_DEBUG, nlp, "family %s: invalid cmd %d", gf->family_name, ghdr->cmd); return (ENOTSUP); } struct genl_cmd *cmd = &gf->family_cmds[ghdr->cmd]; if (cmd->cmd_priv != 0 && !nlp_has_priv(nlp, cmd->cmd_priv)) { NLP_LOG(LOG_DEBUG, nlp, "family %s: cmd %d priv_check() failed", gf->family_name, ghdr->cmd); return (EPERM); } NLP_LOG(LOG_DEBUG2, nlp, "received family %s cmd %s(%d) len %d", gf->family_name, cmd->cmd_name, ghdr->cmd, hdr->nlmsg_len); error = cmd->cmd_cb(hdr, npt); return (error); } static uint32_t get_cmd_flags(const struct genl_cmd *cmd) { uint32_t flags = cmd->cmd_flags; if (cmd->cmd_priv != 0) flags |= GENL_ADMIN_PERM; return (flags); } static int dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr, const struct genl_family *gf, struct nl_writer *nw) { if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) goto enomem; struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); ghdr_new->cmd = ghdr->cmd; ghdr_new->version = gf->family_version; 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_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); if (gf->family_cmd_size > 0) { int off = nlattr_add_nested(nw, CTRL_ATTR_OPS); if (off == 0) goto enomem; for (int i = 0, cnt=0; i < gf->family_cmd_size; i++) { struct genl_cmd *cmd = &gf->family_cmds[i]; if (cmd->cmd_cb == NULL) continue; int cmd_off = nlattr_add_nested(nw, ++cnt); if (cmd_off == 0) goto enomem; nlattr_add_u32(nw, CTRL_ATTR_OP_ID, cmd->cmd_num); nlattr_add_u32(nw, CTRL_ATTR_OP_FLAGS, get_cmd_flags(cmd)); nlattr_set_len(nw, cmd_off); } nlattr_set_len(nw, off); } if (gf->family_num_groups > 0) { 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) continue; int cmd_off = nlattr_add_nested(nw, ++cnt); if (cmd_off == 0) goto enomem; nlattr_add_u32(nw, CTRL_ATTR_MCAST_GRP_ID, i + MIN_GROUP_NUM); nlattr_add_string(nw, CTRL_ATTR_MCAST_GRP_NAME, gg->group_name); nlattr_set_len(nw, cmd_off); } nlattr_set_len(nw, off); } if (nlmsg_end(nw)) return (0); enomem: NL_LOG(LOG_DEBUG, "unable to dump family %s state (ENOMEM)", gf->family_name); nlmsg_abort(nw); 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; uint8_t version; }; #define _IN(_field) offsetof(struct genlmsghdr, _field) #define _OUT(_field) offsetof(struct nl_parsed_family, _field) static const struct nlfield_parser nlf_p_generic[] = { { .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 }, }; static struct nlattr_parser nla_p_generic[] = { { .type = CTRL_ATTR_FAMILY_ID , .off = _OUT(family_id), .cb = nlattr_get_uint16 }, { .type = CTRL_ATTR_FAMILY_NAME , .off = _OUT(family_name), .cb = nlattr_get_string }, }; #undef _IN #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) { int error = 0; struct nl_parsed_family attrs = {}; error = nl_parse_nlmsg(hdr, &genl_parser, npt, &attrs); if (error != 0) return (error); struct genlmsghdr ghdr = { .cmd = CTRL_CMD_NEWFAMILY, }; 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); } } 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)) { error = dump_family(hdr, &ghdr, gf, npt->nw); if (error != 0) break; } } if (!nlmsg_end_dump(npt->nw, error, hdr)) { NL_LOG(LOG_DEBUG, "Unable to finalize the dump"); return (ENOMEM); } return (error); } static void nlctrl_notify(void *arg __unused, const struct genl_family *gf, int cmd) { struct nlmsghdr hdr = {.nlmsg_type = NETLINK_GENERIC }; struct genlmsghdr ghdr = { .cmd = cmd }; struct nl_writer nw; if (!nl_writer_group(&nw, NLMSG_SMALL, NETLINK_GENERIC, CTRL_GROUP_ID, 0, false)) { NL_LOG(LOG_DEBUG, "error allocating group writer"); return; } dump_family(&hdr, &ghdr, gf, &nw); nlmsg_flush(&nw); } static const struct nlhdr_parser *all_parsers[] = { &genl_parser }; static void genl_load_all(void *u __unused) { NL_VERIFY_PARSERS(all_parsers); family_event_tag = EVENTHANDLER_REGISTER(genl_family_event, nlctrl_notify, NULL, EVENTHANDLER_PRI_ANY); netlink_register_proto(NETLINK_GENERIC, "NETLINK_GENERIC", genl_handle_message); } SYSINIT(genl_load_all, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_load_all, NULL); static void genl_unload(void *u __unused) { netlink_unregister_proto(NETLINK_GENERIC); EVENTHANDLER_DEREGISTER(genl_family_event, family_event_tag); NET_EPOCH_WAIT(); } SYSUNINIT(genl_unload, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_unload, NULL); /* * Public KPI for NETLINK_GENERIC families/groups registration logic below. */ static struct sx sx_lock; SX_SYSINIT(genl_lock, &sx_lock, "genetlink lock"); #define GENL_LOCK() sx_xlock(&sx_lock) #define GENL_UNLOCK() sx_xunlock(&sx_lock) #define GENL_ASSERT_LOCKED() sx_assert(&sx_lock, SA_LOCKED) #define GENL_ASSERT_XLOCKED() sx_assert(&sx_lock, SA_XLOCKED) -static struct genl_cmd nlctrl_cmds[] = { - [CTRL_CMD_GETFAMILY] = { - .cmd_num = CTRL_CMD_GETFAMILY, - .cmd_name = "GETFAMILY", - .cmd_cb = nlctrl_handle_getfamily, - .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | - GENL_CMD_CAP_HASPOL, - }, -}; - -static struct genl_family families[MAX_FAMILIES] = { - [CTRL_FAMILY_ID] = { - .family_name = CTRL_FAMILY_NAME, - .family_hdrsize = 0, - .family_version = 2, - .family_attr_max = CTRL_ATTR_MAX, - .family_cmd_size = CTRL_CMD_GETFAMILY + 1, - .family_cmds = nlctrl_cmds, - .family_num_groups = 1, - }, -} -; -static struct genl_group groups[MAX_GROUPS] = { - [CTRL_GROUP_ID] = { - .group_family = &families[CTRL_FAMILY_ID], - .group_name = CTRL_GROUP_NAME, - }, -}; - 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) { struct genl_family *gf; uint16_t family_id; GENL_LOCK(); if (find_family(family_name) != NULL) { GENL_UNLOCK(); return (0); } gf = find_empty_family_id(family_name); KASSERT(gf, ("%s: maximum of %u generic netlink families allocated", __func__, MAX_FAMILIES)); *gf = (struct genl_family) { .family_name = family_name, .family_version = family_version, .family_hdrsize = hdrsize, .family_attr_max = max_attr_idx, }; family_id = genl_get_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); return (family_id); } static void free_family(struct genl_family *gf) { if (gf->family_cmds != NULL) free(gf->family_cmds, M_NETLINK); } /* * unregister groups of a given family */ static void unregister_groups(const struct genl_family *gf) { for (u_int i = 0; i < MAX_GROUPS; i++) { struct genl_group *gg = &groups[i]; if (gg->group_family == gf && gg->group_name != NULL) { gg->group_family = NULL; 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)); } GENL_UNLOCK(); return (found); } bool genl_register_cmds(const char *family_name, const struct genl_cmd *cmds, int count) { struct genl_family *gf; uint16_t cmd_size; GENL_LOCK(); if ((gf = find_family(family_name)) == NULL) { GENL_UNLOCK(); return (false); } cmd_size = gf->family_cmd_size; for (u_int i = 0; i < count; i++) { MPASS(cmds[i].cmd_cb != NULL); if (cmds[i].cmd_num >= cmd_size) cmd_size = cmds[i].cmd_num + 1; } if (cmd_size > gf->family_cmd_size) { void *old_data; /* need to realloc */ size_t sz = cmd_size * sizeof(struct genl_cmd); void *data = malloc(sz, M_NETLINK, M_WAITOK | M_ZERO); memcpy(data, gf->family_cmds, gf->family_cmd_size * sizeof(struct genl_cmd)); old_data = gf->family_cmds; gf->family_cmds = data; gf->family_cmd_size = cmd_size; free(old_data, M_NETLINK); } for (u_int i = 0; i < count; i++) { const struct genl_cmd *cmd = &cmds[i]; MPASS(gf->family_cmds[cmd->cmd_num].cmd_cb == NULL); gf->family_cmds[cmd->cmd_num] = cmds[i]; NL_LOG(LOG_DEBUG2, "Adding cmd %s(%d) to family %s", cmd->cmd_name, cmd->cmd_num, gf->family_name); } GENL_UNLOCK(); 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) { 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); } /* Microoptimization: index 0 is reserved for the control family */ for (u_int i = 1; i < MAX_GROUPS; i++) { struct genl_group *gg = &groups[i]; if (gg->group_family == NULL) { gf->family_num_groups++; gg->group_family = gf; gg->group_name = group_name; group_id = i + MIN_GROUP_NUM; break; } } GENL_UNLOCK(); 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_var.h b/sys/netlink/netlink_var.h index 87b9f5aaaecd..c341abf7ca55 100644 --- a/sys/netlink/netlink_var.h +++ b/sys/netlink/netlink_var.h @@ -1,204 +1,189 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2021 Ng Peng Nam Sean * Copyright (c) 2022 Alexander V. Chernikov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _NETLINK_NETLINK_VAR_H_ #define _NETLINK_NETLINK_VAR_H_ #ifdef _KERNEL #include #include #include #include #include #define NLSNDQ 65536 /* Default socket sendspace */ #define NLRCVQ 65536 /* Default socket recvspace */ #define NLMBUFSIZE 2048 /* External storage size for Netlink mbufs */ struct ucred; struct nl_buf { TAILQ_ENTRY(nl_buf) tailq; u_int buflen; u_int datalen; u_int offset; char data[]; }; #define NLP_MAX_GROUPS 128 BITSET_DEFINE(nl_groups, NLP_MAX_GROUPS); struct nlpcb { struct socket *nl_socket; struct nl_groups nl_groups; uint32_t nl_port; uint32_t nl_flags; uint32_t nl_process_id; int nl_proto; bool nl_bound; bool nl_task_pending; bool nl_tx_blocked; /* No new requests accepted */ bool nl_linux; /* true if running under compat */ bool nl_unconstrained_vnet; /* true if running under VNET jail (or without jail) */ bool nl_need_thread_setup; struct taskqueue *nl_taskqueue; struct task nl_task; struct ucred *nl_cred; /* Copy of nl_socket->so_cred */ uint64_t nl_dropped_bytes; uint64_t nl_dropped_messages; CK_LIST_ENTRY(nlpcb) nl_next; CK_LIST_ENTRY(nlpcb) nl_port_next; volatile u_int nl_refcount; struct mtx nl_lock; struct epoch_context nl_epoch_ctx; }; #define sotonlpcb(so) ((struct nlpcb *)(so)->so_pcb) #define NLP_LOCK_INIT(_nlp) mtx_init(&((_nlp)->nl_lock), "nlp mtx", NULL, MTX_DEF) #define NLP_LOCK_DESTROY(_nlp) mtx_destroy(&((_nlp)->nl_lock)) #define NLP_LOCK(_nlp) mtx_lock(&((_nlp)->nl_lock)) #define NLP_UNLOCK(_nlp) mtx_unlock(&((_nlp)->nl_lock)) #define ALIGNED_NL_SZ(_data) roundup2((((struct nlmsghdr *)(_data))->nlmsg_len), 16) /* nl_flags */ #define NLF_CAP_ACK 0x01 /* Do not send message body with errmsg */ #define NLF_EXT_ACK 0x02 /* Allow including extended TLVs in ack */ #define NLF_STRICT 0x04 /* Perform strict header checks */ #define NLF_MSG_INFO 0x08 /* Send caller info along with the notifications */ SYSCTL_DECL(_net_netlink); SYSCTL_DECL(_net_netlink_debug); struct nl_control { CK_LIST_HEAD(nl_pid_head, nlpcb) ctl_port_head; CK_LIST_HEAD(nlpcb_head, nlpcb) ctl_pcb_head; CK_LIST_ENTRY(nl_control) ctl_next; struct rmlock ctl_lock; }; VNET_DECLARE(struct nl_control *, nl_ctl); #define V_nl_ctl VNET(nl_ctl) struct sockaddr_nl; struct sockaddr; struct nlmsghdr; /* netlink_module.c */ struct nl_control *vnet_nl_ctl_init(void); int nl_verify_proto(int proto); const char *nl_get_proto_name(int proto); extern int netlink_unloading; struct nl_proto_handler { nl_handler_f cb; const char *proto_name; }; extern struct nl_proto_handler *nl_handlers; /* netlink_domain.c */ bool nl_send_group(struct nl_writer *); void nl_osd_register(void); void nl_osd_unregister(void); void nl_set_thread_nlp(struct thread *td, struct nlpcb *nlp); /* netlink_io.c */ bool nl_send(struct nl_writer *, struct nlpcb *); void nlmsg_ack(struct nlpcb *nlp, int error, struct nlmsghdr *nlmsg, struct nl_pstate *npt); void nl_on_transmit(struct nlpcb *nlp); void nl_taskqueue_handler(void *_arg, int pending); void nl_schedule_taskqueue(struct nlpcb *nlp); void nl_process_receive_locked(struct nlpcb *nlp); 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 { - const char *family_name; - uint16_t family_hdrsize; - uint16_t family_version; - uint16_t family_attr_max; - uint16_t family_cmd_size; - uint16_t family_num_groups; - struct genl_cmd *family_cmds; -}; - -struct genl_group { - struct genl_family *group_family; - const char *group_name; -}; - 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 #define MIN_GROUP_NUM 48 #define CTRL_FAMILY_ID 0 #define CTRL_FAMILY_NAME "nlctrl" #define CTRL_GROUP_ID 0 #define CTRL_GROUP_NAME "notify" struct ifnet; struct nl_parsed_link; struct nlattr_bmask; struct nl_pstate; /* Function map */ struct nl_function_wrapper { bool (*nlmsg_add)(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type, uint16_t flags, uint32_t len); bool (*nlmsg_refill_buffer)(struct nl_writer *nw, size_t required_len); bool (*nlmsg_flush)(struct nl_writer *nw); bool (*nlmsg_end)(struct nl_writer *nw); void (*nlmsg_abort)(struct nl_writer *nw); void (*nlmsg_ignore_limit)(struct nl_writer *nw); bool (*nl_writer_unicast)(struct nl_writer *nw, size_t size, struct nlpcb *nlp, bool waitok); bool (*nl_writer_group)(struct nl_writer *nw, size_t size, uint16_t protocol, uint16_t group_id, int priv, bool waitok); bool (*nlmsg_end_dump)(struct nl_writer *nw, int error, struct nlmsghdr *hdr); int (*nl_modify_ifp_generic)(struct ifnet *ifp, struct nl_parsed_link *lattrs, const struct nlattr_bmask *bm, struct nl_pstate *npt); void (*nl_store_ifp_cookie)(struct nl_pstate *npt, struct ifnet *ifp); struct nlpcb * (*nl_get_thread_nlp)(struct thread *td); }; void nl_set_functions(const struct nl_function_wrapper *nl); #endif #endif