aboutsummaryrefslogtreecommitdiff
path: root/sys/netlink/netlink_message_parser.h
diff options
context:
space:
mode:
authorAlexander V. Chernikov <melifaro@FreeBSD.org>2022-01-20 21:39:21 +0000
committerAlexander V. Chernikov <melifaro@FreeBSD.org>2022-10-01 14:15:35 +0000
commit7e5bf68495cc0a8c9793a338a8a02009a7f6dbb6 (patch)
tree9ed5b89fbb30fccbebc050062a943288f63e043c /sys/netlink/netlink_message_parser.h
parent35d60ac2e5bdb63ea8c6e08caca699dede8674e6 (diff)
downloadsrc-7e5bf68495cc0a8c9793a338a8a02009a7f6dbb6.tar.gz
src-7e5bf68495cc0a8c9793a338a8a02009a7f6dbb6.zip
netlink: add netlink support
Netlinks is a communication protocol currently used in Linux kernel to modify, read and subscribe for nearly all networking state. Interfaces, addresses, routes, firewall, fibs, vnets, etc are controlled via netlink. It is async, TLV-based protocol, providing 1-1 and 1-many communications. The current implementation supports the subset of NETLINK_ROUTE family. To be more specific, the following is supported: * Dumps: - routes - nexthops / nexthop groups - interfaces - interface addresses - neighbors (arp/ndp) * Notifications: - interface arrival/departure - interface address arrival/departure - route addition/deletion * Modifications: - adding/deleting routes - adding/deleting nexthops/nexthops groups - adding/deleting neghbors - adding/deleting interfaces (basic support only) * Rtsock interaction - route events are bridged both ways The implementation also supports the NETLINK_GENERIC family framework. Implementation notes: Netlink is implemented via loadable/unloadable kernel module, not touching many kernel parts. Each netlink socket uses dedicated taskqueue to support async operations that can sleep, such as interface creation. All message processing is performed within these taskqueues. Compatibility: Most of the Netlink data models specified above maps to FreeBSD concepts nicely. Unmodified ip(8) binary correctly works with interfaces, addresses, routes, nexthops and nexthop groups. Some software such as net/bird require header-only modifications to compile and work with FreeBSD netlink. Reviewed by: imp Differential Revision: https://reviews.freebsd.org/D36002 MFC after: 2 months
Diffstat (limited to 'sys/netlink/netlink_message_parser.h')
-rw-r--r--sys/netlink/netlink_message_parser.h270
1 files changed, 270 insertions, 0 deletions
diff --git a/sys/netlink/netlink_message_parser.h b/sys/netlink/netlink_message_parser.h
new file mode 100644
index 000000000000..06a6788b7de5
--- /dev/null
+++ b/sys/netlink/netlink_message_parser.h
@@ -0,0 +1,270 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
+ *
+ * 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_MESSAGE_PARSER_H_
+#define _NETLINK_NETLINK_MESSAGE_PARSER_H_
+
+/*
+ * It is not meant to be included directly
+ */
+
+/* Parsing state */
+struct linear_buffer {
+ char *base; /* Base allocated memory pointer */
+ uint32_t offset; /* Currently used offset */
+ uint32_t size; /* Total buffer size */
+};
+
+static inline void *
+lb_alloc(struct linear_buffer *lb, int len)
+{
+ len = roundup2(len, sizeof(uint64_t));
+ if (lb->offset + len > lb->size)
+ return (NULL);
+ void *data = (void *)(lb->base + lb->offset);
+ lb->offset += len;
+ return (data);
+}
+
+static inline void
+lb_clear(struct linear_buffer *lb)
+{
+ memset(lb->base, 0, lb->size);
+ lb->offset = 0;
+}
+
+#define NL_MAX_ERROR_BUF 128
+#define SCRATCH_BUFFER_SIZE (1024 + NL_MAX_ERROR_BUF)
+struct nl_pstate {
+ struct linear_buffer lb; /* Per-message scratch buffer */
+ struct nlpcb *nlp; /* Originator socket */
+ struct nl_writer *nw; /* Message writer to use */
+ struct nlmsghdr *hdr; /* Current parsed message header */
+ uint32_t err_off; /* error offset from hdr start */
+ int error; /* last operation error */
+ char *err_msg; /* Description of last error */
+ bool strict; /* Strict parsing required */
+};
+
+static inline void *
+npt_alloc(struct nl_pstate *npt, int len)
+{
+ return (lb_alloc(&npt->lb, len));
+}
+#define npt_alloc_sockaddr(_npt, _len) ((struct sockaddr *)(npt_alloc(_npt, _len)))
+
+typedef int parse_field_f(void *hdr, struct nl_pstate *npt,
+ void *target);
+struct nlfield_parser {
+ uint16_t off_in;
+ uint16_t off_out;
+ parse_field_f *cb;
+};
+static const struct nlfield_parser nlf_p_empty[] = {};
+
+int nlf_get_ifp(void *src, struct nl_pstate *npt, void *target);
+int nlf_get_ifpz(void *src, struct nl_pstate *npt, void *target);
+int nlf_get_u8(void *src, struct nl_pstate *npt, void *target);
+int nlf_get_u16(void *src, struct nl_pstate *npt, void *target);
+int nlf_get_u32(void *src, struct nl_pstate *npt, void *target);
+int nlf_get_u8_u32(void *src, struct nl_pstate *npt, void *target);
+
+
+struct nlattr_parser;
+typedef int parse_attr_f(struct nlattr *attr, struct nl_pstate *npt,
+ const void *arg, void *target);
+struct nlattr_parser {
+ uint16_t type; /* Attribute type */
+ uint16_t off; /* field offset in the target structure */
+ parse_attr_f *cb; /* parser function to call */
+ const void *arg;
+};
+
+typedef bool strict_parser_f(void *hdr, struct nl_pstate *npt);
+
+struct nlhdr_parser {
+ int nl_hdr_off; /* aligned netlink header size */
+ int out_hdr_off; /* target header size */
+ int fp_size;
+ int np_size;
+ const struct nlfield_parser *fp; /* array of header field parsers */
+ const struct nlattr_parser *np; /* array of attribute parsers */
+ strict_parser_f *sp; /* Parser function */
+};
+
+#define NL_DECLARE_PARSER(_name, _t, _fp, _np) \
+static const struct nlhdr_parser _name = { \
+ .nl_hdr_off = sizeof(_t), \
+ .fp = &((_fp)[0]), \
+ .np = &((_np)[0]), \
+ .fp_size = NL_ARRAY_LEN(_fp), \
+ .np_size = NL_ARRAY_LEN(_np), \
+}
+
+#define NL_DECLARE_STRICT_PARSER(_name, _t, _sp, _fp, _np)\
+static const struct nlhdr_parser _name = { \
+ .nl_hdr_off = sizeof(_t), \
+ .fp = &((_fp)[0]), \
+ .np = &((_np)[0]), \
+ .fp_size = NL_ARRAY_LEN(_fp), \
+ .np_size = NL_ARRAY_LEN(_np), \
+ .sp = _sp, \
+}
+
+#define NL_DECLARE_ARR_PARSER(_name, _t, _o, _fp, _np) \
+static const struct nlhdr_parser _name = { \
+ .nl_hdr_off = sizeof(_t), \
+ .out_hdr_off = sizeof(_o), \
+ .fp = &((_fp)[0]), \
+ .np = &((_np)[0]), \
+ .fp_size = NL_ARRAY_LEN(_fp), \
+ .np_size = NL_ARRAY_LEN(_np), \
+}
+
+#define NL_DECLARE_ATTR_PARSER(_name, _np) \
+static const struct nlhdr_parser _name = { \
+ .np = &((_np)[0]), \
+ .np_size = NL_ARRAY_LEN(_np), \
+}
+
+struct nlarr_hdr {
+ int num_items;
+ int max_items;
+};
+
+int nl_parse_attrs_raw(struct nlattr *nla_head, int len, const struct nlattr_parser *ps,
+ int pslen, struct nl_pstate *npt, void *target);
+int nl_parse_attrs(struct nlmsghdr *hdr, int hdrlen, struct nlattr_parser *ps,
+ int pslen, struct nl_pstate *npt, void *target);
+
+int nlattr_get_flag(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
+int nlattr_get_ip(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
+int nlattr_get_uint16(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
+int nlattr_get_uint32(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
+int nlattr_get_uint64(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
+int nlattr_get_ifp(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
+int nlattr_get_ifpz(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
+int nlattr_get_ipvia(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
+int nlattr_get_string(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
+int nlattr_get_stringn(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
+int nlattr_get_nla(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
+int nlattr_get_nested(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
+
+bool nlmsg_report_err_msg(struct nl_pstate *npt, const char *fmt, ...);
+
+#define NLMSG_REPORT_ERR_MSG(_npt, _fmt, ...) { \
+ nlmsg_report_err_msg(_npt, _fmt, ## __VA_ARGS__); \
+ NLP_LOG(LOG_DEBUG, (_npt)->nlp, _fmt, ## __VA_ARGS__); \
+}
+
+bool nlmsg_report_err_offset(struct nl_pstate *npt, uint32_t off);
+
+/*
+ * Have it inline so compiler can optimize field accesses into
+ * the list of direct function calls without iteration.
+ */
+static inline int
+nl_parse_header(void *hdr, int len, const struct nlhdr_parser *parser,
+ struct nl_pstate *npt, void *target)
+{
+ int error;
+
+ if (__predict_false(len < parser->nl_hdr_off)) {
+ nlmsg_report_err_msg(npt, "header too short: expected %d, got %d",
+ parser->nl_hdr_off, len);
+ return (EINVAL);
+ }
+
+ if (npt->strict && parser->sp != NULL && !parser->sp(hdr, npt))
+ return (EINVAL);
+
+ /* Extract fields first */
+ for (int i = 0; i < parser->fp_size; i++) {
+ const struct nlfield_parser *fp = &parser->fp[i];
+ void *src = (char *)hdr + fp->off_in;
+ void *dst = (char *)target + fp->off_out;
+
+ error = fp->cb(src, npt, dst);
+ if (error != 0)
+ return (error);
+ }
+
+ struct nlattr *nla_head = (struct nlattr *)((char *)hdr + parser->nl_hdr_off);
+ error = nl_parse_attrs_raw(nla_head, len - parser->nl_hdr_off, parser->np,
+ parser->np_size, npt, target);
+
+ return (error);
+}
+
+static inline int
+nl_parse_nested(struct nlattr *nla, const struct nlhdr_parser *parser,
+ struct nl_pstate *npt, void *target)
+{
+ struct nlattr *nla_head = (struct nlattr *)NLA_DATA(nla);
+
+ return (nl_parse_attrs_raw(nla_head, NLA_DATA_LEN(nla), parser->np,
+ parser->np_size, npt, target));
+}
+
+/*
+ * Checks that attributes are sorted by attribute type.
+ */
+static inline void
+nl_verify_parsers(const struct nlhdr_parser **parser, int count)
+{
+ for (int i = 0; i < count; i++) {
+ const struct nlhdr_parser *p = parser[i];
+ int attr_type = 0;
+ for (int j = 0; j < p->np_size; j++) {
+ MPASS(p->np[j].type > attr_type);
+ attr_type = p->np[j].type;
+ }
+ }
+}
+void nl_verify_parsers(const struct nlhdr_parser **parser, int count);
+#define NL_VERIFY_PARSERS(_p) nl_verify_parsers((_p), NL_ARRAY_LEN(_p))
+
+static inline int
+nl_parse_nlmsg(struct nlmsghdr *hdr, const struct nlhdr_parser *parser,
+ struct nl_pstate *npt, void *target)
+{
+ return (nl_parse_header(hdr + 1, hdr->nlmsg_len - sizeof(*hdr), parser, npt, target));
+}
+
+#endif