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