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