linux/tools/lib/bpf/nlattr.c
<<
>>
Prefs
   1// SPDX-License-Identifier: LGPL-2.1
   2
   3/*
   4 * NETLINK      Netlink attributes
   5 *
   6 *      This library is free software; you can redistribute it and/or
   7 *      modify it under the terms of the GNU Lesser General Public
   8 *      License as published by the Free Software Foundation version 2.1
   9 *      of the License.
  10 *
  11 * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
  12 */
  13
  14#include <errno.h>
  15#include "nlattr.h"
  16#include <linux/rtnetlink.h>
  17#include <string.h>
  18#include <stdio.h>
  19
  20static uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = {
  21        [NLA_U8]        = sizeof(uint8_t),
  22        [NLA_U16]       = sizeof(uint16_t),
  23        [NLA_U32]       = sizeof(uint32_t),
  24        [NLA_U64]       = sizeof(uint64_t),
  25        [NLA_STRING]    = 1,
  26        [NLA_FLAG]      = 0,
  27};
  28
  29static int nla_len(const struct nlattr *nla)
  30{
  31        return nla->nla_len - NLA_HDRLEN;
  32}
  33
  34static struct nlattr *nla_next(const struct nlattr *nla, int *remaining)
  35{
  36        int totlen = NLA_ALIGN(nla->nla_len);
  37
  38        *remaining -= totlen;
  39        return (struct nlattr *) ((char *) nla + totlen);
  40}
  41
  42static int nla_ok(const struct nlattr *nla, int remaining)
  43{
  44        return remaining >= sizeof(*nla) &&
  45               nla->nla_len >= sizeof(*nla) &&
  46               nla->nla_len <= remaining;
  47}
  48
  49static void *nla_data(const struct nlattr *nla)
  50{
  51        return (char *) nla + NLA_HDRLEN;
  52}
  53
  54static int nla_type(const struct nlattr *nla)
  55{
  56        return nla->nla_type & NLA_TYPE_MASK;
  57}
  58
  59static int validate_nla(struct nlattr *nla, int maxtype,
  60                        struct nla_policy *policy)
  61{
  62        struct nla_policy *pt;
  63        unsigned int minlen = 0;
  64        int type = nla_type(nla);
  65
  66        if (type < 0 || type > maxtype)
  67                return 0;
  68
  69        pt = &policy[type];
  70
  71        if (pt->type > NLA_TYPE_MAX)
  72                return 0;
  73
  74        if (pt->minlen)
  75                minlen = pt->minlen;
  76        else if (pt->type != NLA_UNSPEC)
  77                minlen = nla_attr_minlen[pt->type];
  78
  79        if (nla_len(nla) < minlen)
  80                return -1;
  81
  82        if (pt->maxlen && nla_len(nla) > pt->maxlen)
  83                return -1;
  84
  85        if (pt->type == NLA_STRING) {
  86                char *data = nla_data(nla);
  87                if (data[nla_len(nla) - 1] != '\0')
  88                        return -1;
  89        }
  90
  91        return 0;
  92}
  93
  94static inline int nlmsg_len(const struct nlmsghdr *nlh)
  95{
  96        return nlh->nlmsg_len - NLMSG_HDRLEN;
  97}
  98
  99/**
 100 * Create attribute index based on a stream of attributes.
 101 * @arg tb              Index array to be filled (maxtype+1 elements).
 102 * @arg maxtype         Maximum attribute type expected and accepted.
 103 * @arg head            Head of attribute stream.
 104 * @arg len             Length of attribute stream.
 105 * @arg policy          Attribute validation policy.
 106 *
 107 * Iterates over the stream of attributes and stores a pointer to each
 108 * attribute in the index array using the attribute type as index to
 109 * the array. Attribute with a type greater than the maximum type
 110 * specified will be silently ignored in order to maintain backwards
 111 * compatibility. If \a policy is not NULL, the attribute will be
 112 * validated using the specified policy.
 113 *
 114 * @see nla_validate
 115 * @return 0 on success or a negative error code.
 116 */
 117static int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
 118                     struct nla_policy *policy)
 119{
 120        struct nlattr *nla;
 121        int rem, err;
 122
 123        memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
 124
 125        nla_for_each_attr(nla, head, len, rem) {
 126                int type = nla_type(nla);
 127
 128                if (type > maxtype)
 129                        continue;
 130
 131                if (policy) {
 132                        err = validate_nla(nla, maxtype, policy);
 133                        if (err < 0)
 134                                goto errout;
 135                }
 136
 137                if (tb[type])
 138                        fprintf(stderr, "Attribute of type %#x found multiple times in message, "
 139                                  "previous attribute is being ignored.\n", type);
 140
 141                tb[type] = nla;
 142        }
 143
 144        err = 0;
 145errout:
 146        return err;
 147}
 148
 149/* dump netlink extended ack error message */
 150int nla_dump_errormsg(struct nlmsghdr *nlh)
 151{
 152        struct nla_policy extack_policy[NLMSGERR_ATTR_MAX + 1] = {
 153                [NLMSGERR_ATTR_MSG]     = { .type = NLA_STRING },
 154                [NLMSGERR_ATTR_OFFS]    = { .type = NLA_U32 },
 155        };
 156        struct nlattr *tb[NLMSGERR_ATTR_MAX + 1], *attr;
 157        struct nlmsgerr *err;
 158        char *errmsg = NULL;
 159        int hlen, alen;
 160
 161        /* no TLVs, nothing to do here */
 162        if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
 163                return 0;
 164
 165        err = (struct nlmsgerr *)NLMSG_DATA(nlh);
 166        hlen = sizeof(*err);
 167
 168        /* if NLM_F_CAPPED is set then the inner err msg was capped */
 169        if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
 170                hlen += nlmsg_len(&err->msg);
 171
 172        attr = (struct nlattr *) ((void *) err + hlen);
 173        alen = nlh->nlmsg_len - hlen;
 174
 175        if (nla_parse(tb, NLMSGERR_ATTR_MAX, attr, alen, extack_policy) != 0) {
 176                fprintf(stderr,
 177                        "Failed to parse extended error attributes\n");
 178                return 0;
 179        }
 180
 181        if (tb[NLMSGERR_ATTR_MSG])
 182                errmsg = (char *) nla_data(tb[NLMSGERR_ATTR_MSG]);
 183
 184        fprintf(stderr, "Kernel error message: %s\n", errmsg);
 185
 186        return 0;
 187}
 188