/src/openvswitch/lib/netlink.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at: |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #include <config.h> |
18 | | #include "netlink.h" |
19 | | #include <errno.h> |
20 | | #include <inttypes.h> |
21 | | #include <sys/types.h> |
22 | | #include <unistd.h> |
23 | | #include "coverage.h" |
24 | | #include "flow.h" |
25 | | #include "netlink-protocol.h" |
26 | | #include "openvswitch/ofpbuf.h" |
27 | | #include "timeval.h" |
28 | | #include "unaligned.h" |
29 | | #include "openvswitch/vlog.h" |
30 | | #include "util.h" |
31 | | |
32 | | #ifdef HAVE_NETLINK |
33 | | #include <linux/rtnetlink.h> |
34 | | #else |
35 | | /* RTA_VIA */ |
36 | | struct rtvia { |
37 | | sa_family_t rtvia_family; |
38 | | uint8_t rtvia_addr[]; |
39 | | }; |
40 | | #endif |
41 | | |
42 | | VLOG_DEFINE_THIS_MODULE(netlink); |
43 | | |
44 | | /* A single (bad) Netlink message can in theory dump out many, many log |
45 | | * messages, so the burst size is set quite high here to avoid missing useful |
46 | | * information. Also, at high logging levels we log *all* Netlink messages. */ |
47 | | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 600); |
48 | | |
49 | | /* Returns the nlmsghdr at the head of 'msg'. |
50 | | * |
51 | | * 'msg' must be at least as large as a nlmsghdr. */ |
52 | | struct nlmsghdr * |
53 | | nl_msg_nlmsghdr(const struct ofpbuf *msg) |
54 | 0 | { |
55 | 0 | return ofpbuf_at_assert(msg, 0, NLMSG_HDRLEN); |
56 | 0 | } |
57 | | |
58 | | /* Returns the genlmsghdr just past 'msg''s nlmsghdr. |
59 | | * |
60 | | * Returns a null pointer if 'msg' is not large enough to contain an nlmsghdr |
61 | | * and a genlmsghdr. */ |
62 | | struct genlmsghdr * |
63 | | nl_msg_genlmsghdr(const struct ofpbuf *msg) |
64 | 0 | { |
65 | 0 | return ofpbuf_at(msg, NLMSG_HDRLEN, GENL_HDRLEN); |
66 | 0 | } |
67 | | |
68 | | /* Parses the ext ack netlink attributes and, if successful, a pointer |
69 | | * to the error message, if included, is stored in '*errmsg'. */ |
70 | | static void |
71 | | nl_parse_ext_ack(const struct ofpbuf *msg, size_t offset, const char **errmsg) |
72 | 0 | { |
73 | 0 | static const struct nl_policy policy[] = { |
74 | 0 | [NLMSGERR_ATTR_MSG] = { .type = NL_A_STRING, .optional = true }, |
75 | 0 | }; |
76 | 0 | struct nlattr *attrs[ARRAY_SIZE(policy)]; |
77 | |
|
78 | 0 | if (!nl_policy_parse(msg, offset, policy, attrs, ARRAY_SIZE(policy))) { |
79 | 0 | VLOG_ERR_RL(&rl, "Failed to parse extended ack data"); |
80 | 0 | return; |
81 | 0 | } |
82 | | |
83 | 0 | if (attrs[NLMSGERR_ATTR_MSG]) { |
84 | 0 | *errmsg = nl_attr_get_string(attrs[NLMSGERR_ATTR_MSG]); |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | | /* If 'buffer' is a NLMSG_ERROR message, stores 0 in '*errorp' if it is an ACK |
89 | | * message, otherwise a positive errno value, and returns true. If 'buffer' is |
90 | | * not an NLMSG_ERROR message, returns false. |
91 | | * |
92 | | * 'msg' must be at least as large as a nlmsghdr. */ |
93 | | bool |
94 | | nl_msg_nlmsgerr(const struct ofpbuf *msg, int *errorp, const char **attr_msg) |
95 | 0 | { |
96 | 0 | struct nlmsghdr *nlh = nl_msg_nlmsghdr(msg); |
97 | |
|
98 | 0 | if (nlh->nlmsg_type == NLMSG_ERROR) { |
99 | 0 | struct nlmsgerr *err = ofpbuf_at(msg, NLMSG_HDRLEN, sizeof *err); |
100 | 0 | int code = EPROTO; |
101 | 0 | if (!err) { |
102 | 0 | VLOG_ERR_RL(&rl, "received invalid nlmsgerr (%"PRIu32" bytes < %"PRIuSIZE")", |
103 | 0 | msg->size, NLMSG_HDRLEN + sizeof *err); |
104 | 0 | } else if (err->error <= 0 && err->error > INT_MIN) { |
105 | 0 | code = -err->error; |
106 | 0 | if (attr_msg && err->error != 0 && |
107 | 0 | (nlh->nlmsg_flags & NLM_F_ACK_TLVS)) { |
108 | 0 | size_t offt = NLMSG_HDRLEN + sizeof *err; |
109 | |
|
110 | 0 | if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) { |
111 | 0 | offt += (err->msg.nlmsg_len - NLMSG_HDRLEN); |
112 | 0 | } |
113 | 0 | nl_parse_ext_ack(msg, offt, attr_msg); |
114 | 0 | } |
115 | 0 | } |
116 | 0 | if (errorp) { |
117 | 0 | *errorp = code; |
118 | 0 | } |
119 | 0 | return true; |
120 | 0 | } else { |
121 | 0 | return false; |
122 | 0 | } |
123 | 0 | } |
124 | | |
125 | | /* Ensures that 'b' has room for at least 'size' bytes plus netlink padding at |
126 | | * its tail end, reallocating and copying its data if necessary. */ |
127 | | void |
128 | | nl_msg_reserve(struct ofpbuf *msg, size_t size) |
129 | 0 | { |
130 | 0 | ofpbuf_prealloc_tailroom(msg, NLMSG_ALIGN(size)); |
131 | 0 | } |
132 | | |
133 | | /* Puts a nlmsghdr at the beginning of 'msg', which must be initially empty. |
134 | | * Uses the given 'type' and 'flags'. 'expected_payload' should be |
135 | | * an estimate of the number of payload bytes to be supplied; if the size of |
136 | | * the payload is unknown a value of 0 is acceptable. |
137 | | * |
138 | | * 'type' is ordinarily an enumerated value specific to the Netlink protocol |
139 | | * (e.g. RTM_NEWLINK, for NETLINK_ROUTE protocol). For Generic Netlink, 'type' |
140 | | * is the family number obtained via nl_lookup_genl_family(). |
141 | | * |
142 | | * 'flags' is a bit-mask that indicates what kind of request is being made. It |
143 | | * is often NLM_F_REQUEST indicating that a request is being made, commonly |
144 | | * or'd with NLM_F_ACK to request an acknowledgement. |
145 | | * |
146 | | * Sets the new nlmsghdr's nlmsg_len, nlmsg_seq, and nlmsg_pid fields to 0 for |
147 | | * now. Functions that send Netlink messages will fill these in just before |
148 | | * sending the message. |
149 | | * |
150 | | * nl_msg_put_genlmsghdr() is more convenient for composing a Generic Netlink |
151 | | * message. */ |
152 | | void |
153 | | nl_msg_put_nlmsghdr(struct ofpbuf *msg, |
154 | | size_t expected_payload, uint32_t type, uint32_t flags) |
155 | 0 | { |
156 | 0 | struct nlmsghdr *nlmsghdr; |
157 | |
|
158 | 0 | ovs_assert(msg->size == 0); |
159 | |
|
160 | 0 | nl_msg_reserve(msg, NLMSG_HDRLEN + expected_payload); |
161 | 0 | nlmsghdr = nl_msg_put_uninit(msg, NLMSG_HDRLEN); |
162 | 0 | nlmsghdr->nlmsg_len = 0; |
163 | 0 | nlmsghdr->nlmsg_type = type; |
164 | 0 | nlmsghdr->nlmsg_flags = flags; |
165 | 0 | nlmsghdr->nlmsg_seq = 0; |
166 | 0 | nlmsghdr->nlmsg_pid = 0; |
167 | 0 | } |
168 | | |
169 | | /* Puts a nlmsghdr and genlmsghdr at the beginning of 'msg', which must be |
170 | | * initially empty. 'expected_payload' should be an estimate of the number of |
171 | | * payload bytes to be supplied; if the size of the payload is unknown a value |
172 | | * of 0 is acceptable. |
173 | | * |
174 | | * 'family' is the family number obtained via nl_lookup_genl_family(). |
175 | | * |
176 | | * 'flags' is a bit-mask that indicates what kind of request is being made. It |
177 | | * is often NLM_F_REQUEST indicating that a request is being made, commonly |
178 | | * or'd with NLM_F_ACK to request an acknowledgement. |
179 | | * |
180 | | * 'cmd' is an enumerated value specific to the Generic Netlink family |
181 | | * (e.g. CTRL_CMD_NEWFAMILY for the GENL_ID_CTRL family). |
182 | | * |
183 | | * 'version' is a version number specific to the family and command (often 1). |
184 | | * |
185 | | * Sets the new nlmsghdr's nlmsg_pid field to 0 for now. nl_sock_send() will |
186 | | * fill it in just before sending the message. |
187 | | * |
188 | | * nl_msg_put_nlmsghdr() should be used to compose Netlink messages that are |
189 | | * not Generic Netlink messages. */ |
190 | | void |
191 | | nl_msg_put_genlmsghdr(struct ofpbuf *msg, size_t expected_payload, |
192 | | int family, uint32_t flags, uint8_t cmd, uint8_t version) |
193 | 0 | { |
194 | 0 | struct genlmsghdr *genlmsghdr; |
195 | |
|
196 | 0 | nl_msg_put_nlmsghdr(msg, GENL_HDRLEN + expected_payload, family, flags); |
197 | 0 | ovs_assert(msg->size == NLMSG_HDRLEN); |
198 | 0 | genlmsghdr = nl_msg_put_uninit(msg, GENL_HDRLEN); |
199 | 0 | genlmsghdr->cmd = cmd; |
200 | 0 | genlmsghdr->version = version; |
201 | 0 | genlmsghdr->reserved = 0; |
202 | 0 | } |
203 | | |
204 | | /* Appends the 'size' bytes of data in 'p', plus Netlink padding if needed, to |
205 | | * the tail end of 'msg'. Data in 'msg' is reallocated and copied if |
206 | | * necessary. */ |
207 | | void |
208 | | nl_msg_put(struct ofpbuf *msg, const void *data, size_t size) |
209 | 0 | { |
210 | 0 | memcpy(nl_msg_put_uninit(msg, size), data, size); |
211 | 0 | } |
212 | | |
213 | | /* Appends 'size' bytes of data, plus Netlink padding if needed, to the tail |
214 | | * end of 'msg', reallocating and copying its data if necessary. Returns a |
215 | | * pointer to the first byte of the new data, which is left uninitialized. */ |
216 | | void * |
217 | | nl_msg_put_uninit(struct ofpbuf *msg, size_t size) |
218 | 0 | { |
219 | 0 | size_t pad = PAD_SIZE(size, NLMSG_ALIGNTO); |
220 | 0 | char *p = ofpbuf_put_uninit(msg, size + pad); |
221 | 0 | if (pad) { |
222 | 0 | memset(p + size, 0, pad); |
223 | 0 | } |
224 | 0 | return p; |
225 | 0 | } |
226 | | |
227 | | /* Prepends the 'size' bytes of data in 'p', plus Netlink padding if needed, to |
228 | | * the head end of 'msg'. Data in 'msg' is reallocated and copied if |
229 | | * necessary. */ |
230 | | void |
231 | | nl_msg_push(struct ofpbuf *msg, const void *data, size_t size) |
232 | 0 | { |
233 | 0 | memcpy(nl_msg_push_uninit(msg, size), data, size); |
234 | 0 | } |
235 | | |
236 | | /* Prepends 'size' bytes of data, plus Netlink padding if needed, to the head |
237 | | * end of 'msg', reallocating and copying its data if necessary. Returns a |
238 | | * pointer to the first byte of the new data, which is left uninitialized. */ |
239 | | void * |
240 | | nl_msg_push_uninit(struct ofpbuf *msg, size_t size) |
241 | 0 | { |
242 | 0 | size_t pad = PAD_SIZE(size, NLMSG_ALIGNTO); |
243 | 0 | char *p = ofpbuf_push_uninit(msg, size + pad); |
244 | 0 | if (pad) { |
245 | 0 | memset(p + size, 0, pad); |
246 | 0 | } |
247 | 0 | return p; |
248 | 0 | } |
249 | | |
250 | | /* Appends a Netlink attribute of the given 'type' and room for 'size' bytes of |
251 | | * data as its payload, plus Netlink padding if needed, to the tail end of |
252 | | * 'msg', reallocating and copying its data if necessary. Returns a pointer to |
253 | | * the first byte of data in the attribute, which is left uninitialized. */ |
254 | | void * |
255 | | nl_msg_put_unspec_uninit(struct ofpbuf *msg, uint16_t type, size_t size) |
256 | 0 | { |
257 | 0 | size_t total_size = NLA_HDRLEN + size; |
258 | 0 | struct nlattr* nla = nl_msg_put_uninit(msg, total_size); |
259 | 0 | ovs_assert(!nl_attr_oversized(size)); |
260 | 0 | nla->nla_len = total_size; |
261 | 0 | nla->nla_type = type; |
262 | 0 | return nla + 1; |
263 | 0 | } |
264 | | |
265 | | /* Appends a Netlink attribute of the given 'type' and room for 'size' bytes of |
266 | | * data as its payload, plus Netlink padding if needed, to the tail end of |
267 | | * 'msg', reallocating and copying its data if necessary. Returns a pointer to |
268 | | * the first byte of data in the attribute, which is zeroed. */ |
269 | | void * |
270 | | nl_msg_put_unspec_zero(struct ofpbuf *msg, uint16_t type, size_t size) |
271 | 0 | { |
272 | 0 | void *data = nl_msg_put_unspec_uninit(msg, type, size); |
273 | 0 | memset(data, 0, size); |
274 | 0 | return data; |
275 | 0 | } |
276 | | |
277 | | /* Appends a Netlink attribute of the given 'type' and the 'size' bytes of |
278 | | * 'data' as its payload, to the tail end of 'msg', reallocating and copying |
279 | | * its data if necessary. */ |
280 | | void |
281 | | nl_msg_put_unspec(struct ofpbuf *msg, uint16_t type, |
282 | | const void *data, size_t size) |
283 | 0 | { |
284 | 0 | void *ptr; |
285 | |
|
286 | 0 | ptr = nl_msg_put_unspec_uninit(msg, type, size); |
287 | 0 | nullable_memcpy(ptr, data, size); |
288 | 0 | } |
289 | | |
290 | | /* Appends a Netlink attribute of the given 'type' and no payload to 'msg'. |
291 | | * (Some Netlink protocols use the presence or absence of an attribute as a |
292 | | * Boolean flag.) */ |
293 | | void |
294 | | nl_msg_put_flag(struct ofpbuf *msg, uint16_t type) |
295 | 0 | { |
296 | 0 | nl_msg_put_unspec(msg, type, NULL, 0); |
297 | 0 | } |
298 | | |
299 | | /* Appends a Netlink attribute of the given 'type' and the given 8-bit 'value' |
300 | | * to 'msg'. */ |
301 | | void |
302 | | nl_msg_put_u8(struct ofpbuf *msg, uint16_t type, uint8_t value) |
303 | 0 | { |
304 | 0 | nl_msg_put_unspec(msg, type, &value, sizeof value); |
305 | 0 | } |
306 | | |
307 | | /* Appends a Netlink attribute of the given 'type' and the given 16-bit host |
308 | | * byte order 'value' to 'msg'. */ |
309 | | void |
310 | | nl_msg_put_u16(struct ofpbuf *msg, uint16_t type, uint16_t value) |
311 | 0 | { |
312 | 0 | nl_msg_put_unspec(msg, type, &value, sizeof value); |
313 | 0 | } |
314 | | |
315 | | /* Appends a Netlink attribute of the given 'type' and the given 32-bit host |
316 | | * byte order 'value' to 'msg'. */ |
317 | | void |
318 | | nl_msg_put_u32(struct ofpbuf *msg, uint16_t type, uint32_t value) |
319 | 0 | { |
320 | 0 | nl_msg_put_unspec(msg, type, &value, sizeof value); |
321 | 0 | } |
322 | | |
323 | | /* Appends a Netlink attribute of the given 'type' and the given 64-bit host |
324 | | * byte order 'value' to 'msg'. */ |
325 | | void |
326 | | nl_msg_put_u64(struct ofpbuf *msg, uint16_t type, uint64_t value) |
327 | 0 | { |
328 | 0 | nl_msg_put_unspec(msg, type, &value, sizeof value); |
329 | 0 | } |
330 | | |
331 | | /* Appends a Netlink attribute of the given 'type' and the given 128-bit host |
332 | | * byte order 'value' to 'msg'. */ |
333 | | void |
334 | | nl_msg_put_u128(struct ofpbuf *msg, uint16_t type, ovs_u128 value) |
335 | 0 | { |
336 | 0 | nl_msg_put_unspec(msg, type, &value, sizeof value); |
337 | 0 | } |
338 | | |
339 | | /* Appends a Netlink attribute of the given 'type' and the given 16-bit network |
340 | | * byte order 'value' to 'msg'. */ |
341 | | void |
342 | | nl_msg_put_be16(struct ofpbuf *msg, uint16_t type, ovs_be16 value) |
343 | 0 | { |
344 | 0 | nl_msg_put_unspec(msg, type, &value, sizeof value); |
345 | 0 | } |
346 | | |
347 | | /* Appends a Netlink attribute of the given 'type' and the given 32-bit network |
348 | | * byte order 'value' to 'msg'. */ |
349 | | void |
350 | | nl_msg_put_be32(struct ofpbuf *msg, uint16_t type, ovs_be32 value) |
351 | 0 | { |
352 | 0 | nl_msg_put_unspec(msg, type, &value, sizeof value); |
353 | 0 | } |
354 | | |
355 | | /* Appends a Netlink attribute of the given 'type' and the given 64-bit network |
356 | | * byte order 'value' to 'msg'. */ |
357 | | void |
358 | | nl_msg_put_be64(struct ofpbuf *msg, uint16_t type, ovs_be64 value) |
359 | 0 | { |
360 | 0 | nl_msg_put_unspec(msg, type, &value, sizeof value); |
361 | 0 | } |
362 | | |
363 | | /* Appends a Netlink attribute of the given 'type' and the given 128-bit |
364 | | * network byte order 'value' to 'msg'. */ |
365 | | void |
366 | | nl_msg_put_be128(struct ofpbuf *msg, uint16_t type, ovs_be128 value) |
367 | 0 | { |
368 | 0 | nl_msg_put_unspec(msg, type, &value, sizeof value); |
369 | 0 | } |
370 | | |
371 | | /* Appends a Netlink attribute of the given 'type' and the given IPv6 |
372 | | * address order 'value' to 'msg'. */ |
373 | | void |
374 | | nl_msg_put_in6_addr(struct ofpbuf *msg, uint16_t type, |
375 | | const struct in6_addr *value) |
376 | 0 | { |
377 | 0 | nl_msg_put_unspec(msg, type, value, sizeof *value); |
378 | 0 | } |
379 | | |
380 | | /* Appends a Netlink attribute of the given 'type' and the given odp_port_t |
381 | | * 'value' to 'msg'. */ |
382 | | void |
383 | | nl_msg_put_odp_port(struct ofpbuf *msg, uint16_t type, odp_port_t value) |
384 | 0 | { |
385 | 0 | nl_msg_put_u32(msg, type, odp_to_u32(value)); |
386 | 0 | } |
387 | | |
388 | | /* Appends a Netlink attribute of the given 'type' with the 'len' characters |
389 | | * of 'value', followed by the null byte to 'msg'. */ |
390 | | void |
391 | | nl_msg_put_string__(struct ofpbuf *msg, uint16_t type, const char *value, |
392 | | size_t len) |
393 | 0 | { |
394 | 0 | char *data = nl_msg_put_unspec_uninit(msg, type, len + 1); |
395 | |
|
396 | 0 | memcpy(data, value, len); |
397 | 0 | data[len] = '\0'; |
398 | 0 | } |
399 | | |
400 | | /* Appends a Netlink attribute of the given 'type' and the given |
401 | | * null-terminated string 'value' to 'msg'. */ |
402 | | void |
403 | | nl_msg_put_string(struct ofpbuf *msg, uint16_t type, const char *value) |
404 | 0 | { |
405 | 0 | nl_msg_put_unspec(msg, type, value, strlen(value) + 1); |
406 | 0 | } |
407 | | |
408 | | /* Prepends a Netlink attribute of the given 'type' and room for 'size' bytes |
409 | | * of data as its payload, plus Netlink padding if needed, to the head end of |
410 | | * 'msg', reallocating and copying its data if necessary. Returns a pointer to |
411 | | * the first byte of data in the attribute, which is left uninitialized. */ |
412 | | void * |
413 | | nl_msg_push_unspec_uninit(struct ofpbuf *msg, uint16_t type, size_t size) |
414 | 0 | { |
415 | 0 | size_t total_size = NLA_HDRLEN + size; |
416 | 0 | struct nlattr* nla = nl_msg_push_uninit(msg, total_size); |
417 | 0 | ovs_assert(!nl_attr_oversized(size)); |
418 | 0 | nla->nla_len = total_size; |
419 | 0 | nla->nla_type = type; |
420 | 0 | return nla + 1; |
421 | 0 | } |
422 | | |
423 | | /* Prepends a Netlink attribute of the given 'type' and the 'size' bytes of |
424 | | * 'data' as its payload, to the head end of 'msg', reallocating and copying |
425 | | * its data if necessary. Returns a pointer to the first byte of data in the |
426 | | * attribute, which is left uninitialized. */ |
427 | | void |
428 | | nl_msg_push_unspec(struct ofpbuf *msg, uint16_t type, |
429 | | const void *data, size_t size) |
430 | 0 | { |
431 | 0 | memcpy(nl_msg_push_unspec_uninit(msg, type, size), data, size); |
432 | 0 | } |
433 | | |
434 | | /* Prepends a Netlink attribute of the given 'type' and no payload to 'msg'. |
435 | | * (Some Netlink protocols use the presence or absence of an attribute as a |
436 | | * Boolean flag.) */ |
437 | | void |
438 | | nl_msg_push_flag(struct ofpbuf *msg, uint16_t type) |
439 | 0 | { |
440 | 0 | nl_msg_push_unspec_uninit(msg, type, 0); |
441 | 0 | } |
442 | | |
443 | | /* Prepends a Netlink attribute of the given 'type' and the given 8-bit 'value' |
444 | | * to 'msg'. */ |
445 | | void |
446 | | nl_msg_push_u8(struct ofpbuf *msg, uint16_t type, uint8_t value) |
447 | 0 | { |
448 | 0 | nl_msg_push_unspec(msg, type, &value, sizeof value); |
449 | 0 | } |
450 | | |
451 | | /* Prepends a Netlink attribute of the given 'type' and the given 16-bit host |
452 | | * byte order 'value' to 'msg'. */ |
453 | | void |
454 | | nl_msg_push_u16(struct ofpbuf *msg, uint16_t type, uint16_t value) |
455 | 0 | { |
456 | 0 | nl_msg_push_unspec(msg, type, &value, sizeof value); |
457 | 0 | } |
458 | | |
459 | | /* Prepends a Netlink attribute of the given 'type' and the given 32-bit host |
460 | | * byte order 'value' to 'msg'. */ |
461 | | void |
462 | | nl_msg_push_u32(struct ofpbuf *msg, uint16_t type, uint32_t value) |
463 | 0 | { |
464 | 0 | nl_msg_push_unspec(msg, type, &value, sizeof value); |
465 | 0 | } |
466 | | |
467 | | /* Prepends a Netlink attribute of the given 'type' and the given 64-bit host |
468 | | * byte order 'value' to 'msg'. */ |
469 | | void |
470 | | nl_msg_push_u64(struct ofpbuf *msg, uint16_t type, uint64_t value) |
471 | 0 | { |
472 | 0 | nl_msg_push_unspec(msg, type, &value, sizeof value); |
473 | 0 | } |
474 | | |
475 | | /* Prepends a Netlink attribute of the given 'type' and the given 128-bit host |
476 | | * byte order 'value' to 'msg'. */ |
477 | | void |
478 | | nl_msg_push_u128(struct ofpbuf *msg, uint16_t type, ovs_u128 value) |
479 | 0 | { |
480 | 0 | nl_msg_push_unspec(msg, type, &value, sizeof value); |
481 | 0 | } |
482 | | |
483 | | /* Prepends a Netlink attribute of the given 'type' and the given 16-bit |
484 | | * network byte order 'value' to 'msg'. */ |
485 | | void |
486 | | nl_msg_push_be16(struct ofpbuf *msg, uint16_t type, ovs_be16 value) |
487 | 0 | { |
488 | 0 | nl_msg_push_unspec(msg, type, &value, sizeof value); |
489 | 0 | } |
490 | | |
491 | | /* Prepends a Netlink attribute of the given 'type' and the given 32-bit |
492 | | * network byte order 'value' to 'msg'. */ |
493 | | void |
494 | | nl_msg_push_be32(struct ofpbuf *msg, uint16_t type, ovs_be32 value) |
495 | 0 | { |
496 | 0 | nl_msg_push_unspec(msg, type, &value, sizeof value); |
497 | 0 | } |
498 | | |
499 | | /* Prepends a Netlink attribute of the given 'type' and the given 64-bit |
500 | | * network byte order 'value' to 'msg'. */ |
501 | | void |
502 | | nl_msg_push_be64(struct ofpbuf *msg, uint16_t type, ovs_be64 value) |
503 | 0 | { |
504 | 0 | nl_msg_push_unspec(msg, type, &value, sizeof value); |
505 | 0 | } |
506 | | |
507 | | /* Prepends a Netlink attribute of the given 'type' and the given 128-bit |
508 | | * network byte order 'value' to 'msg'. */ |
509 | | void |
510 | | nl_msg_push_be128(struct ofpbuf *msg, uint16_t type, ovs_be128 value) |
511 | 0 | { |
512 | 0 | nl_msg_push_unspec(msg, type, &value, sizeof value); |
513 | 0 | } |
514 | | |
515 | | /* Prepends a Netlink attribute of the given 'type' and the given |
516 | | * null-terminated string 'value' to 'msg'. */ |
517 | | void |
518 | | nl_msg_push_string(struct ofpbuf *msg, uint16_t type, const char *value) |
519 | 0 | { |
520 | 0 | nl_msg_push_unspec(msg, type, value, strlen(value) + 1); |
521 | 0 | } |
522 | | |
523 | | /* Adds the header for nested Netlink attributes to 'msg', with the specified |
524 | | * 'type', and returns the header's offset within 'msg'. The caller should add |
525 | | * the content for the nested Netlink attribute to 'msg' (e.g. using the other |
526 | | * nl_msg_*() functions), and then pass the returned offset to |
527 | | * nl_msg_end_nested() to finish up the nested attributes. */ |
528 | | size_t |
529 | | nl_msg_start_nested(struct ofpbuf *msg, uint16_t type) |
530 | 0 | { |
531 | 0 | size_t offset = msg->size; |
532 | 0 | nl_msg_put_unspec_uninit(msg, type, 0); |
533 | 0 | return offset; |
534 | 0 | } |
535 | | |
536 | | /* Adds the header for nested Netlink attributes to 'msg', with the specified |
537 | | * 'type', and returns the header's offset within 'msg'. It's similar to |
538 | | * nl_msg_start_nested() and uses NLA_F_NESTED flag mandatorily. */ |
539 | | size_t |
540 | | nl_msg_start_nested_with_flag(struct ofpbuf *msg, uint16_t type) |
541 | 0 | { |
542 | 0 | return nl_msg_start_nested(msg, type | NLA_F_NESTED); |
543 | 0 | } |
544 | | |
545 | | /* Finalizes a nested Netlink attribute in 'msg'. 'offset' should be the value |
546 | | * returned by nl_msg_start_nested(). */ |
547 | | void |
548 | | nl_msg_end_nested(struct ofpbuf *msg, size_t offset) |
549 | 0 | { |
550 | 0 | struct nlattr *attr = ofpbuf_at_assert(msg, offset, sizeof *attr); |
551 | 0 | ovs_assert(!nl_attr_oversized(msg->size - offset - NLA_HDRLEN)); |
552 | 0 | attr->nla_len = msg->size - offset; |
553 | 0 | } |
554 | | |
555 | | /* Cancel a nested Netlink attribute in 'msg'. 'offset' should be the value |
556 | | * returned by nl_msg_start_nested(). */ |
557 | | void |
558 | | nl_msg_cancel_nested(struct ofpbuf *msg, size_t offset) |
559 | 0 | { |
560 | 0 | msg->size = offset; |
561 | 0 | } |
562 | | |
563 | | /* Same as nls_msg_end_nested() when the nested Netlink contains non empty |
564 | | * message. Otherwise, drop the nested message header from 'msg'. |
565 | | * |
566 | | * Return true if the nested message has been dropped. */ |
567 | | bool |
568 | | nl_msg_end_non_empty_nested(struct ofpbuf *msg, size_t offset) |
569 | 0 | { |
570 | 0 | nl_msg_end_nested(msg, offset); |
571 | |
|
572 | 0 | struct nlattr *attr = ofpbuf_at_assert(msg, offset, sizeof *attr); |
573 | 0 | if (!nl_attr_get_size(attr)) { |
574 | 0 | nl_msg_cancel_nested(msg, offset); |
575 | 0 | return true; |
576 | 0 | } else { |
577 | 0 | return false; |
578 | 0 | } |
579 | 0 | } |
580 | | |
581 | | /* Appends a nested Netlink attribute of the given 'type', with the 'size' |
582 | | * bytes of content starting at 'data', to 'msg'. */ |
583 | | void |
584 | | nl_msg_put_nested(struct ofpbuf *msg, |
585 | | uint16_t type, const void *data, size_t size) |
586 | 0 | { |
587 | 0 | size_t offset = nl_msg_start_nested(msg, type); |
588 | 0 | nl_msg_put(msg, data, size); |
589 | 0 | nl_msg_end_nested(msg, offset); |
590 | 0 | } |
591 | | |
592 | | /* Reset message size to offset. */ |
593 | | void |
594 | | nl_msg_reset_size(struct ofpbuf *msg, size_t offset) |
595 | 0 | { |
596 | 0 | msg->size = offset; |
597 | 0 | } |
598 | | |
599 | | /* If 'buffer' begins with a valid "struct nlmsghdr", pulls the header and its |
600 | | * payload off 'buffer', stores header and payload in 'msg->data' and |
601 | | * 'msg->size', and returns a pointer to the header. |
602 | | * |
603 | | * If 'buffer' does not begin with a "struct nlmsghdr" or begins with one that |
604 | | * is invalid, returns NULL and clears 'buffer' and 'msg'. */ |
605 | | struct nlmsghdr * |
606 | | nl_msg_next(struct ofpbuf *buffer, struct ofpbuf *msg) |
607 | 0 | { |
608 | 0 | if (buffer->size >= sizeof(struct nlmsghdr)) { |
609 | 0 | struct nlmsghdr *nlmsghdr = nl_msg_nlmsghdr(buffer); |
610 | 0 | size_t len = nlmsghdr->nlmsg_len; |
611 | 0 | if (len >= sizeof *nlmsghdr && len <= buffer->size) { |
612 | 0 | ofpbuf_use_const(msg, nlmsghdr, len); |
613 | 0 | ofpbuf_pull(buffer, len); |
614 | 0 | return nlmsghdr; |
615 | 0 | } |
616 | 0 | } |
617 | | |
618 | 0 | ofpbuf_clear(buffer); |
619 | 0 | msg->data = NULL; |
620 | 0 | msg->size = 0; |
621 | 0 | return NULL; |
622 | 0 | } |
623 | | |
624 | | /* Returns true if a Netlink attribute with a payload that is 'payload_size' |
625 | | * bytes long would be oversized, that is, if it's not possible to create an |
626 | | * nlattr of that size because its size wouldn't fit in the 16-bit nla_len |
627 | | * field. */ |
628 | | bool |
629 | | nl_attr_oversized(size_t payload_size) |
630 | 0 | { |
631 | 0 | return payload_size > UINT16_MAX - NLA_HDRLEN; |
632 | 0 | } |
633 | | |
634 | | /* Attributes. */ |
635 | | |
636 | | /* Returns the bits of 'nla->nla_type' that are significant for determining its |
637 | | * type. */ |
638 | | int |
639 | | nl_attr_type(const struct nlattr *nla) |
640 | 0 | { |
641 | 0 | return nla->nla_type & NLA_TYPE_MASK; |
642 | 0 | } |
643 | | |
644 | | /* Returns the first byte in the payload of attribute 'nla'. */ |
645 | | const void * |
646 | | nl_attr_get(const struct nlattr *nla) |
647 | 0 | { |
648 | 0 | ovs_assert(nla->nla_len >= NLA_HDRLEN); |
649 | 0 | return nla + 1; |
650 | 0 | } |
651 | | |
652 | | /* Returns the number of bytes in the payload of attribute 'nla'. */ |
653 | | size_t |
654 | | nl_attr_get_size(const struct nlattr *nla) |
655 | 0 | { |
656 | 0 | ovs_assert(nla->nla_len >= NLA_HDRLEN); |
657 | 0 | return nla->nla_len - NLA_HDRLEN; |
658 | 0 | } |
659 | | |
660 | | /* Asserts that 'nla''s payload is at least 'size' bytes long, and returns the |
661 | | * first byte of the payload. */ |
662 | | const void * |
663 | | nl_attr_get_unspec(const struct nlattr *nla, size_t size) |
664 | 0 | { |
665 | 0 | ovs_assert(nla->nla_len >= NLA_HDRLEN + size); |
666 | 0 | return nla + 1; |
667 | 0 | } |
668 | | |
669 | | /* Returns true if 'nla' is nonnull. (Some Netlink protocols use the presence |
670 | | * or absence of an attribute as a Boolean flag.) */ |
671 | | bool |
672 | | nl_attr_get_flag(const struct nlattr *nla) |
673 | 0 | { |
674 | 0 | return nla != NULL; |
675 | 0 | } |
676 | | |
677 | | #define NL_ATTR_GET_AS(NLA, TYPE) \ |
678 | 0 | (*(TYPE*) nl_attr_get_unspec(nla, sizeof(TYPE))) |
679 | | |
680 | | /* Returns the 8-bit value in 'nla''s payload. |
681 | | * |
682 | | * Asserts that 'nla''s payload is at least 1 byte long. */ |
683 | | uint8_t |
684 | | nl_attr_get_u8(const struct nlattr *nla) |
685 | 0 | { |
686 | 0 | return NL_ATTR_GET_AS(nla, uint8_t); |
687 | 0 | } |
688 | | |
689 | | /* Returns the 16-bit host byte order value in 'nla''s payload. |
690 | | * |
691 | | * Asserts that 'nla''s payload is at least 2 bytes long. */ |
692 | | uint16_t |
693 | | nl_attr_get_u16(const struct nlattr *nla) |
694 | 0 | { |
695 | 0 | return NL_ATTR_GET_AS(nla, uint16_t); |
696 | 0 | } |
697 | | |
698 | | /* Returns the 32-bit host byte order value in 'nla''s payload. |
699 | | * |
700 | | * Asserts that 'nla''s payload is at least 4 bytes long. */ |
701 | | uint32_t |
702 | | nl_attr_get_u32(const struct nlattr *nla) |
703 | 0 | { |
704 | 0 | return NL_ATTR_GET_AS(nla, uint32_t); |
705 | 0 | } |
706 | | |
707 | | /* Returns the 64-bit host byte order value in 'nla''s payload. |
708 | | * |
709 | | * Asserts that 'nla''s payload is at least 8 bytes long. */ |
710 | | uint64_t |
711 | | nl_attr_get_u64(const struct nlattr *nla) |
712 | 0 | { |
713 | 0 | const ovs_32aligned_u64 *x = nl_attr_get_unspec(nla, sizeof *x); |
714 | 0 | return get_32aligned_u64(x); |
715 | 0 | } |
716 | | |
717 | | /* Returns the 128-bit host byte order value in 'nla''s payload. |
718 | | * |
719 | | * Asserts that 'nla''s payload is at least 16 bytes long. */ |
720 | | ovs_u128 |
721 | | nl_attr_get_u128(const struct nlattr *nla) |
722 | 0 | { |
723 | 0 | const ovs_32aligned_u128 *x = nl_attr_get_unspec(nla, sizeof *x); |
724 | 0 | return get_32aligned_u128(x); |
725 | 0 | } |
726 | | |
727 | | /* Returns the 16-bit network byte order value in 'nla''s payload. |
728 | | * |
729 | | * Asserts that 'nla''s payload is at least 2 bytes long. */ |
730 | | ovs_be16 |
731 | | nl_attr_get_be16(const struct nlattr *nla) |
732 | 0 | { |
733 | 0 | return NL_ATTR_GET_AS(nla, ovs_be16); |
734 | 0 | } |
735 | | |
736 | | /* Returns the 32-bit network byte order value in 'nla''s payload. |
737 | | * |
738 | | * Asserts that 'nla''s payload is at least 4 bytes long. */ |
739 | | ovs_be32 |
740 | | nl_attr_get_be32(const struct nlattr *nla) |
741 | 0 | { |
742 | 0 | return NL_ATTR_GET_AS(nla, ovs_be32); |
743 | 0 | } |
744 | | |
745 | | /* Returns the 64-bit network byte order value in 'nla''s payload. |
746 | | * |
747 | | * Asserts that 'nla''s payload is at least 8 bytes long. */ |
748 | | ovs_be64 |
749 | | nl_attr_get_be64(const struct nlattr *nla) |
750 | 0 | { |
751 | 0 | const ovs_32aligned_be64 *x = nl_attr_get_unspec(nla, sizeof *x); |
752 | 0 | return get_32aligned_be64(x); |
753 | 0 | } |
754 | | |
755 | | /* Returns the 128-bit network byte order value in 'nla''s payload. |
756 | | * |
757 | | * Asserts that 'nla''s payload is at least 16 bytes long. */ |
758 | | ovs_be128 |
759 | | nl_attr_get_be128(const struct nlattr *nla) |
760 | 0 | { |
761 | 0 | const ovs_32aligned_be128 *x = nl_attr_get_unspec(nla, sizeof *x); |
762 | 0 | return get_32aligned_be128(x); |
763 | 0 | } |
764 | | |
765 | | /* Returns the IPv6 address value in 'nla''s payload. |
766 | | * |
767 | | * Asserts that 'nla''s payload is at least 16 bytes long. */ |
768 | | struct in6_addr |
769 | | nl_attr_get_in6_addr(const struct nlattr *nla) |
770 | 0 | { |
771 | 0 | return NL_ATTR_GET_AS(nla, struct in6_addr); |
772 | 0 | } |
773 | | |
774 | | /* Returns the 32-bit odp_port_t value in 'nla''s payload. |
775 | | * |
776 | | * Asserts that 'nla''s payload is at least 4 bytes long. */ |
777 | | odp_port_t |
778 | | nl_attr_get_odp_port(const struct nlattr *nla) |
779 | 0 | { |
780 | 0 | return u32_to_odp(nl_attr_get_u32(nla)); |
781 | 0 | } |
782 | | |
783 | | /* Returns the null-terminated string value in 'nla''s payload. |
784 | | * |
785 | | * Asserts that 'nla''s payload contains a null-terminated string. */ |
786 | | const char * |
787 | | nl_attr_get_string(const struct nlattr *nla) |
788 | 0 | { |
789 | 0 | ovs_assert(nla->nla_len > NLA_HDRLEN); |
790 | 0 | ovs_assert(memchr(nl_attr_get(nla), '\0', nla->nla_len - NLA_HDRLEN)); |
791 | 0 | return nl_attr_get(nla); |
792 | 0 | } |
793 | | |
794 | | /* Initializes 'nested' to the payload of 'nla'. */ |
795 | | void |
796 | | nl_attr_get_nested(const struct nlattr *nla, struct ofpbuf *nested) |
797 | 0 | { |
798 | 0 | ofpbuf_use_const(nested, nl_attr_get(nla), nl_attr_get_size(nla)); |
799 | 0 | } |
800 | | |
801 | | /* Returns the Ethernet Address value in 'nla''s payload. */ |
802 | | struct eth_addr |
803 | | nl_attr_get_eth_addr(const struct nlattr *nla) |
804 | 0 | { |
805 | 0 | return NL_ATTR_GET_AS(nla, struct eth_addr); |
806 | 0 | } |
807 | | |
808 | | /* Returns the Infiniband LL Address value in 'nla''s payload. */ |
809 | | struct ib_addr |
810 | | nl_attr_get_ib_addr(const struct nlattr *nla) |
811 | 0 | { |
812 | 0 | return NL_ATTR_GET_AS(nla, struct ib_addr); |
813 | 0 | } |
814 | | |
815 | | /* Default minimum payload size for each type of attribute. */ |
816 | | static size_t |
817 | | min_attr_len(enum nl_attr_type type) |
818 | 0 | { |
819 | 0 | switch (type) { |
820 | 0 | case NL_A_NO_ATTR: return 0; |
821 | 0 | case NL_A_UNSPEC: return 0; |
822 | 0 | case NL_A_U8: return 1; |
823 | 0 | case NL_A_U16: return 2; |
824 | 0 | case NL_A_U32: return 4; |
825 | 0 | case NL_A_U64: return 8; |
826 | 0 | case NL_A_U128: return 16; |
827 | 0 | case NL_A_STRING: return 1; |
828 | 0 | case NL_A_FLAG: return 0; |
829 | 0 | case NL_A_IPV6: return 16; |
830 | 0 | case NL_A_NESTED: return 0; |
831 | 0 | case NL_A_LL_ADDR: return 6; /* ETH_ALEN */ |
832 | 0 | case NL_A_RTA_VIA: return sizeof(struct rtvia) + sizeof(struct in_addr); |
833 | 0 | case N_NL_ATTR_TYPES: default: OVS_NOT_REACHED(); |
834 | 0 | } |
835 | 0 | } |
836 | | |
837 | | /* Default maximum payload size for each type of attribute. */ |
838 | | static size_t |
839 | | max_attr_len(enum nl_attr_type type) |
840 | 0 | { |
841 | 0 | switch (type) { |
842 | 0 | case NL_A_NO_ATTR: return SIZE_MAX; |
843 | 0 | case NL_A_UNSPEC: return SIZE_MAX; |
844 | 0 | case NL_A_U8: return 1; |
845 | 0 | case NL_A_U16: return 2; |
846 | 0 | case NL_A_U32: return 4; |
847 | 0 | case NL_A_U64: return 8; |
848 | 0 | case NL_A_U128: return 16; |
849 | 0 | case NL_A_STRING: return SIZE_MAX; |
850 | 0 | case NL_A_FLAG: return SIZE_MAX; |
851 | 0 | case NL_A_IPV6: return 16; |
852 | 0 | case NL_A_NESTED: return SIZE_MAX; |
853 | 0 | case NL_A_LL_ADDR: return 20; /* INFINIBAND_ALEN */ |
854 | 0 | case NL_A_RTA_VIA: return sizeof(struct rtvia) + sizeof(struct in6_addr); |
855 | 0 | case N_NL_ATTR_TYPES: default: OVS_NOT_REACHED(); |
856 | 0 | } |
857 | 0 | } |
858 | | |
859 | | bool |
860 | | nl_attr_validate(const struct nlattr *nla, const struct nl_policy *policy) |
861 | 0 | { |
862 | 0 | uint16_t type = nl_attr_type(nla); |
863 | 0 | size_t min_len; |
864 | 0 | size_t max_len; |
865 | 0 | size_t len; |
866 | |
|
867 | 0 | if (policy->type == NL_A_NO_ATTR) { |
868 | 0 | return true; |
869 | 0 | } |
870 | | |
871 | | /* Figure out min and max length. */ |
872 | 0 | min_len = policy->min_len; |
873 | 0 | if (!min_len) { |
874 | 0 | min_len = min_attr_len(policy->type); |
875 | 0 | } |
876 | 0 | max_len = policy->max_len; |
877 | 0 | if (!max_len) { |
878 | 0 | max_len = max_attr_len(policy->type); |
879 | 0 | } |
880 | | |
881 | | /* Verify length. */ |
882 | 0 | len = nl_attr_get_size(nla); |
883 | 0 | if (len < min_len || len > max_len) { |
884 | 0 | VLOG_DBG_RL(&rl, "attr %"PRIu16" length %"PRIuSIZE" not in " |
885 | 0 | "allowed range %"PRIuSIZE"...%"PRIuSIZE, type, len, min_len, max_len); |
886 | 0 | return false; |
887 | 0 | } |
888 | | |
889 | | /* Strings must be null terminated and must not have embedded nulls. */ |
890 | 0 | if (policy->type == NL_A_STRING) { |
891 | 0 | if (((char *) nla)[nla->nla_len - 1]) { |
892 | 0 | VLOG_DBG_RL(&rl, "attr %"PRIu16" lacks null at end", type); |
893 | 0 | return false; |
894 | 0 | } |
895 | 0 | if (memchr(nla + 1, '\0', len - 1) != NULL) { |
896 | 0 | VLOG_DBG_RL(&rl, "attr %"PRIu16" has bad length", type); |
897 | 0 | return false; |
898 | 0 | } |
899 | 0 | } |
900 | | |
901 | 0 | return true; |
902 | 0 | } |
903 | | |
904 | | /* Parses the 'msg' starting at the given 'nla_offset' as a sequence of Netlink |
905 | | * attributes. 'policy[i]', for 0 <= i < n_attrs, specifies how the attribute |
906 | | * with nla_type == i is parsed; a pointer to attribute i is stored in |
907 | | * attrs[i]. Returns true if successful, false on failure. |
908 | | * |
909 | | * If the Netlink attributes in 'msg' follow a Netlink header and a Generic |
910 | | * Netlink header, then 'nla_offset' should be NLMSG_HDRLEN + GENL_HDRLEN. */ |
911 | | bool |
912 | | nl_policy_parse(const struct ofpbuf *msg, size_t nla_offset, |
913 | | const struct nl_policy policy[], |
914 | | struct nlattr *attrs[], size_t n_attrs) |
915 | 0 | { |
916 | 0 | struct nlattr *nla; |
917 | 0 | size_t left; |
918 | 0 | size_t i; |
919 | |
|
920 | 0 | memset(attrs, 0, n_attrs * sizeof *attrs); |
921 | |
|
922 | 0 | if (msg->size < nla_offset) { |
923 | 0 | VLOG_DBG_RL(&rl, "missing headers in nl_policy_parse"); |
924 | 0 | return false; |
925 | 0 | } |
926 | | |
927 | 0 | NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, nla_offset, 0), |
928 | 0 | msg->size - nla_offset) |
929 | 0 | { |
930 | 0 | uint16_t type = nl_attr_type(nla); |
931 | 0 | if (type < n_attrs && policy[type].type != NL_A_NO_ATTR) { |
932 | 0 | const struct nl_policy *e = &policy[type]; |
933 | 0 | if (!nl_attr_validate(nla, e)) { |
934 | 0 | return false; |
935 | 0 | } |
936 | 0 | if (attrs[type]) { |
937 | 0 | VLOG_DBG_RL(&rl, "duplicate attr %"PRIu16, type); |
938 | 0 | } |
939 | 0 | attrs[type] = nla; |
940 | 0 | } |
941 | 0 | } |
942 | 0 | if (left) { |
943 | 0 | VLOG_DBG_RL(&rl, "attributes followed by garbage"); |
944 | 0 | return false; |
945 | 0 | } |
946 | | |
947 | 0 | for (i = 0; i < n_attrs; i++) { |
948 | 0 | const struct nl_policy *e = &policy[i]; |
949 | 0 | if (!e->optional && e->type != NL_A_NO_ATTR && !attrs[i]) { |
950 | 0 | VLOG_DBG_RL(&rl, "required attr %"PRIuSIZE" missing", i); |
951 | 0 | return false; |
952 | 0 | } |
953 | 0 | } |
954 | 0 | return true; |
955 | 0 | } |
956 | | |
957 | | /* Parses the Netlink attributes within 'nla'. 'policy[i]', for 0 <= i < |
958 | | * n_attrs, specifies how the attribute with nla_type == i is parsed; a pointer |
959 | | * to attribute i is stored in attrs[i]. Returns true if successful, false on |
960 | | * failure. */ |
961 | | bool |
962 | | nl_parse_nested(const struct nlattr *nla, const struct nl_policy policy[], |
963 | | struct nlattr *attrs[], size_t n_attrs) |
964 | 0 | { |
965 | 0 | struct ofpbuf buf; |
966 | |
|
967 | 0 | nl_attr_get_nested(nla, &buf); |
968 | 0 | return nl_policy_parse(&buf, 0, policy, attrs, n_attrs); |
969 | 0 | } |
970 | | |
971 | | const struct nlattr * |
972 | | nl_attr_find__(const struct nlattr *attrs, size_t size, uint16_t type) |
973 | 0 | { |
974 | 0 | const struct nlattr *nla; |
975 | 0 | size_t left; |
976 | |
|
977 | 0 | NL_ATTR_FOR_EACH (nla, left, attrs, size) { |
978 | 0 | if (nl_attr_type(nla) == type) { |
979 | 0 | return nla; |
980 | 0 | } |
981 | 0 | } |
982 | 0 | return NULL; |
983 | 0 | } |
984 | | |
985 | | /* Returns the first Netlink attribute within 'buf' with the specified 'type', |
986 | | * skipping a header of 'hdr_len' bytes at the beginning of 'buf'. |
987 | | * |
988 | | * This function does not validate the attribute's length. */ |
989 | | const struct nlattr * |
990 | | nl_attr_find(const struct ofpbuf *buf, size_t hdr_len, uint16_t type) |
991 | 0 | { |
992 | 0 | return nl_attr_find__(ofpbuf_at(buf, hdr_len, 0), buf->size - hdr_len, |
993 | 0 | type); |
994 | 0 | } |
995 | | |
996 | | /* Returns the first Netlink attribute within 'nla' with the specified |
997 | | * 'type'. |
998 | | * |
999 | | * This function does not validate the attribute's length. */ |
1000 | | const struct nlattr * |
1001 | | nl_attr_find_nested(const struct nlattr *nla, uint16_t type) |
1002 | 0 | { |
1003 | 0 | return nl_attr_find__(nl_attr_get(nla), nl_attr_get_size(nla), type); |
1004 | 0 | } |