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 20 static 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 29 static int nla_len(const struct nlattr *nla) 30 { 31 return nla->nla_len - NLA_HDRLEN; 32 } 33 34 static 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 42 static 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 49 static void *nla_data(const struct nlattr *nla) 50 { 51 return (char *) nla + NLA_HDRLEN; 52 } 53 54 static int nla_type(const struct nlattr *nla) 55 { 56 return nla->nla_type & NLA_TYPE_MASK; 57 } 58 59 static 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 94 static 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 */ 117 static 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; 145 errout: 146 return err; 147 } 148 149 /* dump netlink extended ack error message */ 150 int 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