Path: blob/main/sys/netlink/netlink_message_parser.c
39475 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2022 Alexander V. Chernikov <[email protected]>4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/2627#include <sys/cdefs.h>28#include "opt_inet.h"29#include "opt_inet6.h"30#include <sys/types.h>31#include <sys/malloc.h>32#include <sys/rmlock.h>33#include <sys/socket.h>34#include <sys/stdarg.h>3536#include <net/if.h>37#include <net/route.h>38#include <net/route/nhop.h>3940#include <net/route/route_ctl.h>41#include <netinet/in.h>42#include <netlink/netlink.h>43#include <netlink/netlink_ctl.h>44#include <netlink/netlink_var.h>45#include <netlink/netlink_route.h>4647#define DEBUG_MOD_NAME nl_parser48#define DEBUG_MAX_LEVEL LOG_DEBUG349#include <netlink/netlink_debug.h>50_DECLARE_DEBUG(LOG_INFO);5152bool53nlmsg_report_err_msg(struct nl_pstate *npt, const char *fmt, ...)54{55va_list ap;5657if (npt->err_msg != NULL)58return (false);59char *buf = npt_alloc(npt, NL_MAX_ERROR_BUF);60if (buf == NULL)61return (false);62va_start(ap, fmt);63vsnprintf(buf, NL_MAX_ERROR_BUF, fmt, ap);64va_end(ap);6566npt->err_msg = buf;67return (true);68}6970bool71nlmsg_report_err_offset(struct nl_pstate *npt, uint32_t off)72{73if (npt->err_off != 0)74return (false);75npt->err_off = off;76return (true);77}7879void80nlmsg_report_cookie(struct nl_pstate *npt, struct nlattr *nla)81{82MPASS(nla->nla_type == NLMSGERR_ATTR_COOKIE);83MPASS(nla->nla_len >= sizeof(struct nlattr));84npt->cookie = nla;85}8687void88nlmsg_report_cookie_u32(struct nl_pstate *npt, uint32_t val)89{90struct nlattr *nla = npt_alloc(npt, sizeof(*nla) + sizeof(uint32_t));9192nla->nla_type = NLMSGERR_ATTR_COOKIE;93nla->nla_len = sizeof(*nla) + sizeof(uint32_t);94memcpy(nla + 1, &val, sizeof(uint32_t));95nlmsg_report_cookie(npt, nla);96}9798static const struct nlattr_parser *99search_states(const struct nlattr_parser *ps, u_int pslen, int key)100{101int left_i = 0, right_i = pslen - 1;102103if (key < ps[0].type || key > ps[pslen - 1].type)104return (NULL);105106while (left_i + 1 < right_i) {107int mid_i = (left_i + right_i) / 2;108if (key < ps[mid_i].type)109right_i = mid_i;110else if (key > ps[mid_i].type)111left_i = mid_i + 1;112else113return (&ps[mid_i]);114}115if (ps[left_i].type == key)116return (&ps[left_i]);117else if (ps[right_i].type == key)118return (&ps[right_i]);119return (NULL);120}121122int123nl_parse_attrs_raw(struct nlattr *nla_head, uint16_t len,124const struct nlattr_parser *ps, u_int pslen, struct nl_pstate *npt,125void *target)126{127const struct nlattr_parser *s;128struct nlattr *nla;129uint16_t orig_len, off;130int error = 0;131132NL_LOG(LOG_DEBUG3, "parse %p remaining_len %d", nla_head, len);133orig_len = len;134NLA_FOREACH(nla, nla_head, len) {135NL_LOG(LOG_DEBUG3, ">> parsing %p attr_type %u len %u (rem %u)",136nla, nla->nla_type, nla->nla_len, len);137if (nla->nla_len < sizeof(struct nlattr)) {138NLMSG_REPORT_ERR_MSG(npt,139"Invalid attr %p type %u len: %u",140nla, nla->nla_type, nla->nla_len);141off = (char *)nla - (char *)npt->hdr;142nlmsg_report_err_offset(npt, off);143return (EINVAL);144}145146s = search_states(ps, pslen, nla->nla_type & NLA_TYPE_MASK);147if (s != NULL) {148void *ptr;149150ptr = (void *)((char *)target + s->off);151error = s->cb(nla, npt, s->arg, ptr);152if (error != 0) {153off = (char *)nla - (char *)npt->hdr;154nlmsg_report_err_offset(npt, off);155NL_LOG(LOG_DEBUG3,156"parse failed at offset %u", off);157return (error);158}159} else {160/* Ignore non-specified attributes */161NL_LOG(LOG_DEBUG3, "ignoring attr %u", nla->nla_type);162}163}164if (len >= sizeof(struct nlattr)) {165nla = (struct nlattr *)((char *)nla_head + (orig_len - len));166NL_LOG(LOG_DEBUG3, " >>> end %p attr_type %u len %u", nla,167nla->nla_type, nla->nla_len);168}169NL_LOG(LOG_DEBUG3, "end parse: %p remaining_len %u", nla, len);170171return (0);172}173174void175nl_get_attrs_bmask_raw(struct nlattr *nla_head, uint32_t len,176struct nlattr_bmask *bm)177{178struct nlattr *nla = NULL;179uint16_t nla_type;180181BIT_ZERO(NL_ATTR_BMASK_SIZE, bm);182183NLA_FOREACH(nla, nla_head, len) {184if (nla->nla_len < sizeof(struct nlattr))185return;186nla_type = nla->nla_type & NLA_TYPE_MASK;187if (nla_type < NL_ATTR_BMASK_SIZE)188BIT_SET(NL_ATTR_BMASK_SIZE, nla_type, bm);189else190NL_LOG(LOG_DEBUG2,191"Skipping type %u in the mask: too short",192nla_type);193}194}195196bool197nl_has_attr(const struct nlattr_bmask *bm, uint16_t nla_type)198{199MPASS(nla_type < NL_ATTR_BMASK_SIZE);200201return (BIT_ISSET(NL_ATTR_BMASK_SIZE, nla_type, bm));202}203204int205nlattr_get_flag(struct nlattr *nla, struct nl_pstate *npt, const void *arg,206void *target)207{208if (__predict_false(NLA_DATA_LEN(nla) != 0)) {209NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not a flag",210nla->nla_type, NLA_DATA_LEN(nla));211return (EINVAL);212}213214*((uint8_t *)target) = 1;215return (0);216}217218static struct sockaddr *219parse_rta_ip4(void *rta_data, struct nl_pstate *npt, int *perror)220{221struct sockaddr_in *sin;222223sin = (struct sockaddr_in *)npt_alloc_sockaddr(npt,224sizeof(struct sockaddr_in));225if (__predict_false(sin == NULL)) {226*perror = ENOBUFS;227return (NULL);228}229sin->sin_len = sizeof(struct sockaddr_in);230sin->sin_family = AF_INET;231memcpy(&sin->sin_addr, rta_data, sizeof(struct in_addr));232return ((struct sockaddr *)sin);233}234235static struct sockaddr *236parse_rta_ip6(void *rta_data, struct nl_pstate *npt, int *perror)237{238struct sockaddr_in6 *sin6;239240sin6 = (struct sockaddr_in6 *)npt_alloc_sockaddr(npt,241sizeof(struct sockaddr_in6));242if (__predict_false(sin6 == NULL)) {243*perror = ENOBUFS;244return (NULL);245}246sin6->sin6_len = sizeof(struct sockaddr_in6);247sin6->sin6_family = AF_INET6;248memcpy(&sin6->sin6_addr, rta_data, sizeof(struct in6_addr));249return ((struct sockaddr *)sin6);250}251252static struct sockaddr *253parse_rta_ip(struct rtattr *rta, struct nl_pstate *npt, int *perror)254{255void *rta_data = NL_RTA_DATA(rta);256int rta_len = NL_RTA_DATA_LEN(rta);257258if (rta_len == sizeof(struct in_addr)) {259return (parse_rta_ip4(rta_data, npt, perror));260} else if (rta_len == sizeof(struct in6_addr)) {261return (parse_rta_ip6(rta_data, npt, perror));262} else {263NLMSG_REPORT_ERR_MSG(npt, "unknown IP len: %d for rta type %d",264rta_len, rta->rta_type);265*perror = ENOTSUP;266return (NULL);267}268return (NULL);269}270271int272nlattr_get_ip(struct nlattr *nla, struct nl_pstate *npt, const void *arg,273void *target)274{275int error = 0;276277struct sockaddr *sa = parse_rta_ip((struct rtattr *)nla, npt, &error);278279*((struct sockaddr **)target) = sa;280return (error);281}282283static struct sockaddr *284parse_rta_via(struct rtattr *rta, struct nl_pstate *npt, int *perror)285{286struct rtvia *via = NL_RTA_DATA(rta);287int data_len = NL_RTA_DATA_LEN(rta);288289if (__predict_false(data_len) < sizeof(struct rtvia)) {290NLMSG_REPORT_ERR_MSG(npt, "undersized RTA_VIA(%d) attr: len %d",291rta->rta_type, data_len);292*perror = EINVAL;293return (NULL);294}295data_len -= offsetof(struct rtvia, rtvia_addr);296297switch (via->rtvia_family) {298case AF_INET:299if (__predict_false(data_len < sizeof(struct in_addr))) {300*perror = EINVAL;301return (NULL);302}303return (parse_rta_ip4(via->rtvia_addr, npt, perror));304case AF_INET6:305if (__predict_false(data_len < sizeof(struct in6_addr))) {306*perror = EINVAL;307return (NULL);308}309return (parse_rta_ip6(via->rtvia_addr, npt, perror));310default:311*perror = ENOTSUP;312return (NULL);313}314}315316int317nlattr_get_ipvia(struct nlattr *nla, struct nl_pstate *npt, const void *arg,318void *target)319{320int error = 0;321322struct sockaddr *sa = parse_rta_via((struct rtattr *)nla, npt, &error);323324*((struct sockaddr **)target) = sa;325return (error);326}327328int329nlattr_get_bool(struct nlattr *nla, struct nl_pstate *npt, const void *arg,330void *target)331{332if (__predict_false(NLA_DATA_LEN(nla) != sizeof(bool))) {333NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not bool",334nla->nla_type, NLA_DATA_LEN(nla));335return (EINVAL);336}337*((bool *)target) = *((const bool *)NL_RTA_DATA_CONST(nla));338return (0);339}340341int342nlattr_get_uint8(struct nlattr *nla, struct nl_pstate *npt, const void *arg,343void *target)344{345if (__predict_false(NLA_DATA_LEN(nla) != sizeof(uint8_t))) {346NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not uint8",347nla->nla_type, NLA_DATA_LEN(nla));348return (EINVAL);349}350*((uint8_t *)target) = *((const uint8_t *)NL_RTA_DATA_CONST(nla));351return (0);352}353354int355nlattr_get_uint16(struct nlattr *nla, struct nl_pstate *npt, const void *arg,356void *target)357{358if (__predict_false(NLA_DATA_LEN(nla) != sizeof(uint16_t))) {359NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not uint16",360nla->nla_type, NLA_DATA_LEN(nla));361return (EINVAL);362}363*((uint16_t *)target) = *((const uint16_t *)NL_RTA_DATA_CONST(nla));364return (0);365}366367int368nlattr_get_uint32(struct nlattr *nla, struct nl_pstate *npt, const void *arg,369void *target)370{371if (__predict_false(NLA_DATA_LEN(nla) != sizeof(uint32_t))) {372NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not uint32",373nla->nla_type, NLA_DATA_LEN(nla));374return (EINVAL);375}376*((uint32_t *)target) = *((const uint32_t *)NL_RTA_DATA_CONST(nla));377return (0);378}379380int381nlattr_get_uint64(struct nlattr *nla, struct nl_pstate *npt, const void *arg,382void *target)383{384if (__predict_false(NLA_DATA_LEN(nla) != sizeof(uint64_t))) {385NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not uint64",386nla->nla_type, NLA_DATA_LEN(nla));387return (EINVAL);388}389memcpy(target, NL_RTA_DATA_CONST(nla), sizeof(uint64_t));390return (0);391}392393int394nlattr_get_in_addr(struct nlattr *nla, struct nl_pstate *npt, const void *arg,395void *target)396{397if (__predict_false(NLA_DATA_LEN(nla) != sizeof(in_addr_t))) {398NLMSG_REPORT_ERR_MSG(npt,399"nla type %d size(%u) is not in_addr_t",400nla->nla_type, NLA_DATA_LEN(nla));401return (EINVAL);402}403memcpy(target, NLA_DATA_CONST(nla), sizeof(in_addr_t));404return (0);405}406407int408nlattr_get_in6_addr(struct nlattr *nla, struct nl_pstate *npt, const void *arg,409void *target)410{411if (__predict_false(NLA_DATA_LEN(nla) != sizeof(struct in6_addr))) {412NLMSG_REPORT_ERR_MSG(npt,413"nla type %d size(%u) is not struct in6_addr",414nla->nla_type, NLA_DATA_LEN(nla));415return (EINVAL);416}417memcpy(target, NLA_DATA_CONST(nla), sizeof(struct in6_addr));418return (0);419}420421static int422nlattr_get_ifp_internal(struct nlattr *nla, struct nl_pstate *npt,423void *target, bool zero_ok)424{425struct ifnet *ifp;426u_int ifindex;427428if (__predict_false(NLA_DATA_LEN(nla) != sizeof(uint32_t))) {429NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not uint32",430nla->nla_type, NLA_DATA_LEN(nla));431return (EINVAL);432}433ifindex = *((const u_int *)NLA_DATA_CONST(nla));434435if (ifindex == 0 && zero_ok) {436*((struct ifnet **)target) = NULL;437return (0);438}439440NET_EPOCH_ASSERT();441442ifp = ifnet_byindex(ifindex);443if (__predict_false(ifp == NULL)) {444NLMSG_REPORT_ERR_MSG(npt, "nla type %d: ifindex %u invalid",445nla->nla_type, ifindex);446return (ENOENT);447}448*((struct ifnet **)target) = ifp;449NL_LOG(LOG_DEBUG3, "nla type %d: ifindex %u -> %s", nla->nla_type,450ifindex, if_name(ifp));451452return (0);453}454455int456nlattr_get_ifp(struct nlattr *nla, struct nl_pstate *npt, const void *arg,457void *target)458{459return (nlattr_get_ifp_internal(nla, npt, target, false));460}461462int463nlattr_get_ifpz(struct nlattr *nla, struct nl_pstate *npt, const void *arg,464void *target)465{466return (nlattr_get_ifp_internal(nla, npt, target, true));467}468469int470nlattr_get_chara(struct nlattr *nla, struct nl_pstate *npt, const void *arg,471void *target)472{473int maxlen = NLA_DATA_LEN(nla);474int target_size = (size_t)arg;475int len = strnlen((char *)NLA_DATA(nla), maxlen);476477if (__predict_false(len >= maxlen) ||478__predict_false(len >= target_size)) {479NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not "480"NULL-terminated or longer than %u",481nla->nla_type, maxlen, target_size);482return (EINVAL);483}484485strncpy((char *)target, (char *)NLA_DATA(nla), target_size);486return (0);487}488489int490nlattr_get_string(struct nlattr *nla, struct nl_pstate *npt, const void *arg,491void *target)492{493int maxlen = NLA_DATA_LEN(nla);494495if (__predict_false(strnlen((char *)NLA_DATA(nla), maxlen) >= maxlen)) {496NLMSG_REPORT_ERR_MSG(npt,497"nla type %d size(%u) is not NULL-terminated",498nla->nla_type, maxlen);499return (EINVAL);500}501502*((char **)target) = (char *)NLA_DATA(nla);503return (0);504}505506int507nlattr_get_stringn(struct nlattr *nla, struct nl_pstate *npt, const void *arg,508void *target)509{510int maxlen = NLA_DATA_LEN(nla);511512char *buf = npt_alloc(npt, maxlen + 1);513if (buf == NULL)514return (ENOMEM);515buf[maxlen] = '\0';516memcpy(buf, NLA_DATA(nla), maxlen);517518*((char **)target) = buf;519return (0);520}521522int523nlattr_get_bytes(struct nlattr *nla, struct nl_pstate *npt, const void *arg,524void *target)525{526size_t size = (size_t)arg;527528if (NLA_DATA_LEN(nla) != size)529return (EINVAL);530531memcpy(target, NLA_DATA(nla), size);532533return (0);534}535536int537nlattr_get_nla(struct nlattr *nla, struct nl_pstate *npt, const void *arg,538void *target)539{540NL_LOG(LOG_DEBUG3, "STORING %p len %d", nla, nla->nla_len);541*((struct nlattr **)target) = nla;542return (0);543}544545int546nlattr_get_nested(struct nlattr *nla, struct nl_pstate *npt, const void *arg,547void *target)548{549const struct nlhdr_parser *p = (const struct nlhdr_parser *)arg;550551/* Assumes target points to the beginning of the structure. */552return (nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), p, npt,553target));554}555556int557nlattr_get_nested_ptr(struct nlattr *nla, struct nl_pstate *npt,558const void *arg, void *target)559{560const struct nlhdr_parser *p = (const struct nlhdr_parser *)arg;561562/* Assumes target points to the beginning of the structure. */563return (nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), p, npt,564*(void **)target));565}566567int568nlf_get_ifp(void *src, struct nl_pstate *npt, void *target)569{570struct ifnet *ifp;571u_int ifindex;572573NET_EPOCH_ASSERT();574575ifindex = *((const u_int *)src);576ifp = ifnet_byindex(ifindex);577if (ifp == NULL) {578NL_LOG(LOG_DEBUG, "ifindex %u invalid", ifindex);579return (ENOENT);580}581*((struct ifnet **)target) = ifp;582583return (0);584}585586int587nlf_get_ifpz(void *src, struct nl_pstate *npt, void *target)588{589struct ifnet *ifp;590u_int ifindex;591592NET_EPOCH_ASSERT();593594ifindex = *((const u_int *)src);595ifp = ifnet_byindex(ifindex);596if (ifindex != 0 && ifp == NULL) {597NL_LOG(LOG_DEBUG, "ifindex %u invalid", ifindex);598return (ENOENT);599}600*((struct ifnet **)target) = ifp;601602return (0);603}604605int606nlf_get_u8(void *src, struct nl_pstate *npt, void *target)607{608uint8_t val = *((const uint8_t *)src);609610*((uint8_t *)target) = val;611612return (0);613}614615int616nlf_get_u8_u32(void *src, struct nl_pstate *npt, void *target)617{618*((uint32_t *)target) = *((const uint8_t *)src);619return (0);620}621622int623nlf_get_u16(void *src, struct nl_pstate *npt, void *target)624{625*((uint16_t *)target) = *((const uint16_t *)src);626return (0);627}628629int630nlf_get_u32(void *src, struct nl_pstate *npt, void *target)631{632*((uint32_t *)target) = *((const uint32_t *)src);633return (0);634}635636637