]>
Commit | Line | Data |
---|---|---|
0d71302e BP |
1 | /* |
2 | * Copyright (c) 2008-2017 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 "openvswitch/ofp-queue.h" | |
19 | #include "byte-order.h" | |
20 | #include "flow.h" | |
21 | #include "openvswitch/ofp-msgs.h" | |
22 | #include "openvswitch/ofp-port.h" | |
23 | #include "openvswitch/ofp-prop.h" | |
24 | #include "openvswitch/ofpbuf.h" | |
25 | #include "openvswitch/vlog.h" | |
26 | #include "util.h" | |
27 | ||
28 | VLOG_DEFINE_THIS_MODULE(ofp_queue); | |
29 | ||
30 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
31 | ||
32 | /* Constructs and returns an OFPT_QUEUE_GET_CONFIG request for the specified | |
33 | * 'port' and 'queue', suitable for OpenFlow version 'version'. | |
34 | * | |
35 | * 'queue' is honored only for OpenFlow 1.4 and later; older versions always | |
36 | * request all queues. */ | |
37 | struct ofpbuf * | |
38 | ofputil_encode_queue_get_config_request(enum ofp_version version, | |
39 | ofp_port_t port, | |
40 | uint32_t queue) | |
41 | { | |
42 | struct ofpbuf *request; | |
43 | ||
44 | if (version == OFP10_VERSION) { | |
45 | struct ofp10_queue_get_config_request *qgcr10; | |
46 | ||
47 | request = ofpraw_alloc(OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST, | |
48 | version, 0); | |
49 | qgcr10 = ofpbuf_put_zeros(request, sizeof *qgcr10); | |
50 | qgcr10->port = htons(ofp_to_u16(port)); | |
51 | } else if (version < OFP14_VERSION) { | |
52 | struct ofp11_queue_get_config_request *qgcr11; | |
53 | ||
54 | request = ofpraw_alloc(OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST, | |
55 | version, 0); | |
56 | qgcr11 = ofpbuf_put_zeros(request, sizeof *qgcr11); | |
57 | qgcr11->port = ofputil_port_to_ofp11(port); | |
58 | } else { | |
59 | struct ofp14_queue_desc_request *qdr14; | |
60 | ||
61 | request = ofpraw_alloc(OFPRAW_OFPST14_QUEUE_DESC_REQUEST, | |
62 | version, 0); | |
63 | qdr14 = ofpbuf_put_zeros(request, sizeof *qdr14); | |
64 | qdr14->port = ofputil_port_to_ofp11(port); | |
65 | qdr14->queue = htonl(queue); | |
66 | } | |
67 | ||
68 | return request; | |
69 | } | |
70 | ||
71 | /* Parses OFPT_QUEUE_GET_CONFIG request 'oh', storing the port specified by the | |
72 | * request into '*port'. Returns 0 if successful, otherwise an OpenFlow error | |
73 | * code. */ | |
74 | enum ofperr | |
75 | ofputil_decode_queue_get_config_request(const struct ofp_header *oh, | |
76 | ofp_port_t *port, uint32_t *queue) | |
77 | { | |
78 | const struct ofp10_queue_get_config_request *qgcr10; | |
79 | const struct ofp11_queue_get_config_request *qgcr11; | |
80 | const struct ofp14_queue_desc_request *qdr14; | |
81 | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); | |
82 | enum ofpraw raw = ofpraw_pull_assert(&b); | |
83 | ||
84 | switch ((int) raw) { | |
85 | case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST: | |
86 | qgcr10 = b.data; | |
87 | *port = u16_to_ofp(ntohs(qgcr10->port)); | |
88 | *queue = OFPQ_ALL; | |
89 | break; | |
90 | ||
91 | case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST: | |
92 | qgcr11 = b.data; | |
93 | *queue = OFPQ_ALL; | |
94 | enum ofperr error = ofputil_port_from_ofp11(qgcr11->port, port); | |
95 | if (error || *port == OFPP_ANY) { | |
96 | return error; | |
97 | } | |
98 | break; | |
99 | ||
100 | case OFPRAW_OFPST14_QUEUE_DESC_REQUEST: | |
101 | qdr14 = b.data; | |
102 | *queue = ntohl(qdr14->queue); | |
103 | return ofputil_port_from_ofp11(qdr14->port, port); | |
104 | ||
105 | default: | |
106 | OVS_NOT_REACHED(); | |
107 | } | |
108 | ||
109 | return (ofp_to_u16(*port) < ofp_to_u16(OFPP_MAX) | |
110 | ? 0 | |
111 | : OFPERR_OFPQOFC_BAD_PORT); | |
112 | } | |
113 | ||
114 | /* Constructs and returns the beginning of a reply to | |
115 | * OFPT_QUEUE_GET_CONFIG_REQUEST or OFPMP_QUEUE_DESC request 'oh'. The caller | |
116 | * may append information about individual queues with | |
117 | * ofputil_append_queue_get_config_reply(). */ | |
118 | void | |
119 | ofputil_start_queue_get_config_reply(const struct ofp_header *request, | |
120 | struct ovs_list *replies) | |
121 | { | |
122 | struct ofpbuf *reply; | |
123 | ofp_port_t port; | |
124 | uint32_t queue; | |
125 | ||
126 | ovs_assert(!ofputil_decode_queue_get_config_request(request, &port, | |
127 | &queue)); | |
128 | ||
129 | enum ofpraw raw = ofpraw_decode_assert(request); | |
130 | switch ((int) raw) { | |
131 | case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST: | |
132 | reply = ofpraw_alloc_reply(OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY, | |
133 | request, 0); | |
134 | struct ofp10_queue_get_config_reply *qgcr10 | |
135 | = ofpbuf_put_zeros(reply, sizeof *qgcr10); | |
136 | qgcr10->port = htons(ofp_to_u16(port)); | |
137 | break; | |
138 | ||
139 | case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST: | |
140 | reply = ofpraw_alloc_reply(OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY, | |
141 | request, 0); | |
142 | struct ofp11_queue_get_config_reply *qgcr11 | |
143 | = ofpbuf_put_zeros(reply, sizeof *qgcr11); | |
144 | qgcr11->port = ofputil_port_to_ofp11(port); | |
145 | break; | |
146 | ||
147 | case OFPRAW_OFPST14_QUEUE_DESC_REQUEST: | |
148 | reply = ofpraw_alloc_stats_reply(request, 0); | |
149 | break; | |
150 | ||
151 | default: | |
152 | OVS_NOT_REACHED(); | |
153 | } | |
154 | ||
155 | ovs_list_init(replies); | |
156 | ovs_list_push_back(replies, &reply->list_node); | |
157 | } | |
158 | ||
159 | static void | |
160 | put_ofp10_queue_rate(struct ofpbuf *reply, | |
161 | enum ofp10_queue_properties property, uint16_t rate) | |
162 | { | |
163 | if (rate != UINT16_MAX) { | |
164 | struct ofp10_queue_prop_rate *oqpr; | |
165 | ||
166 | oqpr = ofpbuf_put_zeros(reply, sizeof *oqpr); | |
167 | oqpr->prop_header.property = htons(property); | |
168 | oqpr->prop_header.len = htons(sizeof *oqpr); | |
169 | oqpr->rate = htons(rate); | |
170 | } | |
171 | } | |
172 | ||
173 | static void | |
174 | put_ofp14_queue_rate(struct ofpbuf *reply, | |
175 | enum ofp14_queue_desc_prop_type type, uint16_t rate) | |
176 | { | |
177 | if (rate != UINT16_MAX) { | |
178 | ofpprop_put_u16(reply, type, rate); | |
179 | } | |
180 | } | |
181 | ||
182 | void | |
183 | ofputil_append_queue_get_config_reply(const struct ofputil_queue_config *qc, | |
184 | struct ovs_list *replies) | |
185 | { | |
186 | enum ofp_version ofp_version = ofpmp_version(replies); | |
187 | struct ofpbuf *reply = ofpbuf_from_list(ovs_list_back(replies)); | |
188 | size_t start_ofs = reply->size; | |
189 | size_t len_ofs; | |
190 | ovs_be16 *len; | |
191 | ||
192 | if (ofp_version < OFP14_VERSION) { | |
193 | if (ofp_version < OFP12_VERSION) { | |
194 | struct ofp10_packet_queue *opq10; | |
195 | ||
196 | opq10 = ofpbuf_put_zeros(reply, sizeof *opq10); | |
197 | opq10->queue_id = htonl(qc->queue); | |
198 | len_ofs = (char *) &opq10->len - (char *) reply->data; | |
199 | } else { | |
200 | struct ofp12_packet_queue *opq12; | |
201 | ||
202 | opq12 = ofpbuf_put_zeros(reply, sizeof *opq12); | |
203 | opq12->port = ofputil_port_to_ofp11(qc->port); | |
204 | opq12->queue_id = htonl(qc->queue); | |
205 | len_ofs = (char *) &opq12->len - (char *) reply->data; | |
206 | } | |
207 | ||
208 | put_ofp10_queue_rate(reply, OFPQT10_MIN_RATE, qc->min_rate); | |
209 | put_ofp10_queue_rate(reply, OFPQT11_MAX_RATE, qc->max_rate); | |
210 | } else { | |
211 | struct ofp14_queue_desc *oqd = ofpbuf_put_zeros(reply, sizeof *oqd); | |
212 | oqd->port_no = ofputil_port_to_ofp11(qc->port); | |
213 | oqd->queue_id = htonl(qc->queue); | |
214 | len_ofs = (char *) &oqd->len - (char *) reply->data; | |
215 | put_ofp14_queue_rate(reply, OFPQDPT14_MIN_RATE, qc->min_rate); | |
216 | put_ofp14_queue_rate(reply, OFPQDPT14_MAX_RATE, qc->max_rate); | |
217 | } | |
218 | ||
219 | len = ofpbuf_at(reply, len_ofs, sizeof *len); | |
220 | *len = htons(reply->size - start_ofs); | |
221 | ||
222 | if (ofp_version >= OFP14_VERSION) { | |
223 | ofpmp_postappend(replies, start_ofs); | |
224 | } | |
225 | } | |
226 | ||
227 | static enum ofperr | |
228 | parse_ofp10_queue_rate(const struct ofp10_queue_prop_header *hdr, | |
229 | uint16_t *rate) | |
230 | { | |
231 | const struct ofp10_queue_prop_rate *oqpr; | |
232 | ||
233 | if (hdr->len == htons(sizeof *oqpr)) { | |
234 | oqpr = (const struct ofp10_queue_prop_rate *) hdr; | |
235 | *rate = ntohs(oqpr->rate); | |
236 | return 0; | |
237 | } else { | |
238 | return OFPERR_OFPBRC_BAD_LEN; | |
239 | } | |
240 | } | |
241 | ||
242 | static int | |
243 | ofputil_pull_queue_get_config_reply10(struct ofpbuf *msg, | |
244 | struct ofputil_queue_config *queue) | |
245 | { | |
246 | const struct ofp_header *oh = msg->header; | |
247 | unsigned int opq_len; /* Length of protocol-specific queue header. */ | |
248 | unsigned int len; /* Total length of queue + properties. */ | |
249 | ||
250 | /* Obtain the port number from the message header. */ | |
251 | if (oh->version == OFP10_VERSION) { | |
252 | const struct ofp10_queue_get_config_reply *oqgcr10 = msg->msg; | |
253 | queue->port = u16_to_ofp(ntohs(oqgcr10->port)); | |
254 | } else { | |
255 | const struct ofp11_queue_get_config_reply *oqgcr11 = msg->msg; | |
256 | enum ofperr error = ofputil_port_from_ofp11(oqgcr11->port, | |
257 | &queue->port); | |
258 | if (error) { | |
259 | return error; | |
260 | } | |
261 | } | |
262 | ||
263 | /* Pull off the queue header and get the queue number and length. */ | |
264 | if (oh->version < OFP12_VERSION) { | |
265 | const struct ofp10_packet_queue *opq10; | |
266 | opq10 = ofpbuf_try_pull(msg, sizeof *opq10); | |
267 | if (!opq10) { | |
268 | return OFPERR_OFPBRC_BAD_LEN; | |
269 | } | |
270 | queue->queue = ntohl(opq10->queue_id); | |
271 | len = ntohs(opq10->len); | |
272 | opq_len = sizeof *opq10; | |
273 | } else { | |
274 | const struct ofp12_packet_queue *opq12; | |
275 | opq12 = ofpbuf_try_pull(msg, sizeof *opq12); | |
276 | if (!opq12) { | |
277 | return OFPERR_OFPBRC_BAD_LEN; | |
278 | } | |
279 | queue->queue = ntohl(opq12->queue_id); | |
280 | len = ntohs(opq12->len); | |
281 | opq_len = sizeof *opq12; | |
282 | } | |
283 | ||
284 | /* Length check. */ | |
285 | if (len < opq_len || len > msg->size + opq_len || len % 8) { | |
286 | return OFPERR_OFPBRC_BAD_LEN; | |
287 | } | |
288 | len -= opq_len; | |
289 | ||
290 | /* Pull properties. The format of these properties differs from used in | |
291 | * OF1.4+ so we can't use the common property functions. */ | |
292 | while (len > 0) { | |
293 | const struct ofp10_queue_prop_header *hdr; | |
294 | unsigned int property; | |
295 | unsigned int prop_len; | |
296 | enum ofperr error = 0; | |
297 | ||
298 | hdr = ofpbuf_at_assert(msg, 0, sizeof *hdr); | |
299 | prop_len = ntohs(hdr->len); | |
300 | if (prop_len < sizeof *hdr || prop_len > len || prop_len % 8) { | |
301 | return OFPERR_OFPBRC_BAD_LEN; | |
302 | } | |
303 | ||
304 | property = ntohs(hdr->property); | |
305 | switch (property) { | |
306 | case OFPQT10_MIN_RATE: | |
307 | error = parse_ofp10_queue_rate(hdr, &queue->min_rate); | |
308 | break; | |
309 | ||
310 | case OFPQT11_MAX_RATE: | |
311 | error = parse_ofp10_queue_rate(hdr, &queue->max_rate); | |
312 | break; | |
313 | ||
314 | default: | |
315 | VLOG_INFO_RL(&rl, "unknown queue property %u", property); | |
316 | break; | |
317 | } | |
318 | if (error) { | |
319 | return error; | |
320 | } | |
321 | ||
322 | ofpbuf_pull(msg, prop_len); | |
323 | len -= prop_len; | |
324 | } | |
325 | return 0; | |
326 | } | |
327 | ||
328 | static int | |
329 | ofputil_pull_queue_get_config_reply14(struct ofpbuf *msg, | |
330 | struct ofputil_queue_config *queue) | |
331 | { | |
332 | struct ofp14_queue_desc *oqd14 = ofpbuf_try_pull(msg, sizeof *oqd14); | |
333 | if (!oqd14) { | |
334 | return OFPERR_OFPBRC_BAD_LEN; | |
335 | } | |
336 | enum ofperr error = ofputil_port_from_ofp11(oqd14->port_no, &queue->port); | |
337 | if (error) { | |
338 | return error; | |
339 | } | |
340 | queue->queue = ntohl(oqd14->queue_id); | |
341 | ||
342 | /* Length check. */ | |
343 | unsigned int len = ntohs(oqd14->len); | |
344 | if (len < sizeof *oqd14 || len > msg->size + sizeof *oqd14 || len % 8) { | |
345 | return OFPERR_OFPBRC_BAD_LEN; | |
346 | } | |
347 | len -= sizeof *oqd14; | |
348 | ||
349 | struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len), | |
350 | len); | |
351 | while (properties.size > 0) { | |
352 | struct ofpbuf payload; | |
353 | uint64_t type; | |
354 | ||
355 | error = ofpprop_pull(&properties, &payload, &type); | |
356 | if (error) { | |
357 | return error; | |
358 | } | |
359 | ||
360 | switch (type) { | |
361 | case OFPQDPT14_MIN_RATE: | |
362 | error = ofpprop_parse_u16(&payload, &queue->min_rate); | |
363 | break; | |
364 | ||
365 | case OFPQDPT14_MAX_RATE: | |
366 | error = ofpprop_parse_u16(&payload, &queue->max_rate); | |
367 | break; | |
368 | ||
369 | default: | |
370 | error = OFPPROP_UNKNOWN(true, "queue desc", type); | |
371 | break; | |
372 | } | |
373 | ||
374 | if (error) { | |
375 | return error; | |
376 | } | |
377 | } | |
378 | ||
379 | return 0; | |
380 | } | |
381 | ||
382 | /* Decodes information about a queue from the OFPT_QUEUE_GET_CONFIG_REPLY in | |
383 | * 'reply' and stores it in '*queue'. ofputil_decode_queue_get_config_reply() | |
384 | * must already have pulled off the main header. | |
385 | * | |
386 | * This function returns EOF if the last queue has already been decoded, 0 if a | |
387 | * queue was successfully decoded into '*queue', or an ofperr if there was a | |
388 | * problem decoding 'reply'. */ | |
389 | int | |
390 | ofputil_pull_queue_get_config_reply(struct ofpbuf *msg, | |
391 | struct ofputil_queue_config *queue) | |
392 | { | |
393 | enum ofpraw raw; | |
394 | if (!msg->header) { | |
395 | /* Pull OpenFlow header. */ | |
396 | raw = ofpraw_pull_assert(msg); | |
397 | ||
398 | /* Pull protocol-specific ofp_queue_get_config_reply header (OF1.4 | |
399 | * doesn't have one at all). */ | |
400 | if (raw == OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY) { | |
401 | ofpbuf_pull(msg, sizeof(struct ofp10_queue_get_config_reply)); | |
402 | } else if (raw == OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY) { | |
403 | ofpbuf_pull(msg, sizeof(struct ofp11_queue_get_config_reply)); | |
404 | } else { | |
405 | ovs_assert(raw == OFPRAW_OFPST14_QUEUE_DESC_REPLY); | |
406 | } | |
407 | } else { | |
408 | raw = ofpraw_decode_assert(msg->header); | |
409 | } | |
410 | ||
411 | queue->min_rate = UINT16_MAX; | |
412 | queue->max_rate = UINT16_MAX; | |
413 | ||
414 | if (!msg->size) { | |
415 | return EOF; | |
416 | } else if (raw == OFPRAW_OFPST14_QUEUE_DESC_REPLY) { | |
417 | return ofputil_pull_queue_get_config_reply14(msg, queue); | |
418 | } else { | |
419 | return ofputil_pull_queue_get_config_reply10(msg, queue); | |
420 | } | |
421 | } | |
422 | \f | |
423 | /* Parse a queue status request message into 'oqsr'. | |
424 | * Returns 0 if successful, otherwise an OFPERR_* number. */ | |
425 | enum ofperr | |
426 | ofputil_decode_queue_stats_request(const struct ofp_header *request, | |
427 | struct ofputil_queue_stats_request *oqsr) | |
428 | { | |
429 | switch ((enum ofp_version)request->version) { | |
430 | case OFP16_VERSION: | |
431 | case OFP15_VERSION: | |
432 | case OFP14_VERSION: | |
433 | case OFP13_VERSION: | |
434 | case OFP12_VERSION: | |
435 | case OFP11_VERSION: { | |
436 | const struct ofp11_queue_stats_request *qsr11 = ofpmsg_body(request); | |
437 | oqsr->queue_id = ntohl(qsr11->queue_id); | |
438 | return ofputil_port_from_ofp11(qsr11->port_no, &oqsr->port_no); | |
439 | } | |
440 | ||
441 | case OFP10_VERSION: { | |
442 | const struct ofp10_queue_stats_request *qsr10 = ofpmsg_body(request); | |
443 | oqsr->queue_id = ntohl(qsr10->queue_id); | |
444 | oqsr->port_no = u16_to_ofp(ntohs(qsr10->port_no)); | |
445 | /* OF 1.0 uses OFPP_ALL for OFPP_ANY */ | |
446 | if (oqsr->port_no == OFPP_ALL) { | |
447 | oqsr->port_no = OFPP_ANY; | |
448 | } | |
449 | return 0; | |
450 | } | |
451 | ||
452 | default: | |
453 | OVS_NOT_REACHED(); | |
454 | } | |
455 | } | |
456 | ||
457 | /* Encode a queue stats request for 'oqsr', the encoded message | |
458 | * will be for OpenFlow version 'ofp_version'. Returns message | |
459 | * as a struct ofpbuf. Returns encoded message on success, NULL on error. */ | |
460 | struct ofpbuf * | |
461 | ofputil_encode_queue_stats_request( | |
462 | enum ofp_version ofp_version, | |
463 | const struct ofputil_queue_stats_request *oqsr) | |
464 | { | |
465 | struct ofpbuf *request; | |
466 | ||
467 | switch (ofp_version) { | |
468 | case OFP11_VERSION: | |
469 | case OFP12_VERSION: | |
470 | case OFP13_VERSION: | |
471 | case OFP14_VERSION: | |
472 | case OFP15_VERSION: | |
473 | case OFP16_VERSION: { | |
474 | struct ofp11_queue_stats_request *req; | |
475 | request = ofpraw_alloc(OFPRAW_OFPST11_QUEUE_REQUEST, ofp_version, 0); | |
476 | req = ofpbuf_put_zeros(request, sizeof *req); | |
477 | req->port_no = ofputil_port_to_ofp11(oqsr->port_no); | |
478 | req->queue_id = htonl(oqsr->queue_id); | |
479 | break; | |
480 | } | |
481 | case OFP10_VERSION: { | |
482 | struct ofp10_queue_stats_request *req; | |
483 | request = ofpraw_alloc(OFPRAW_OFPST10_QUEUE_REQUEST, ofp_version, 0); | |
484 | req = ofpbuf_put_zeros(request, sizeof *req); | |
485 | /* OpenFlow 1.0 needs OFPP_ALL instead of OFPP_ANY */ | |
486 | req->port_no = htons(ofp_to_u16(oqsr->port_no == OFPP_ANY | |
487 | ? OFPP_ALL : oqsr->port_no)); | |
488 | req->queue_id = htonl(oqsr->queue_id); | |
489 | break; | |
490 | } | |
491 | default: | |
492 | OVS_NOT_REACHED(); | |
493 | } | |
494 | ||
495 | return request; | |
496 | } | |
497 | ||
498 | /* Returns the number of queue stats elements in OFPTYPE_QUEUE_STATS_REPLY | |
499 | * message 'oh'. */ | |
500 | size_t | |
501 | ofputil_count_queue_stats(const struct ofp_header *oh) | |
502 | { | |
503 | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); | |
504 | ofpraw_pull_assert(&b); | |
505 | ||
506 | for (size_t n = 0; ; n++) { | |
507 | struct ofputil_queue_stats qs; | |
508 | if (ofputil_decode_queue_stats(&qs, &b)) { | |
509 | return n; | |
510 | } | |
511 | } | |
512 | } | |
513 | ||
514 | static enum ofperr | |
515 | ofputil_queue_stats_from_ofp10(struct ofputil_queue_stats *oqs, | |
516 | const struct ofp10_queue_stats *qs10) | |
517 | { | |
518 | oqs->port_no = u16_to_ofp(ntohs(qs10->port_no)); | |
519 | oqs->queue_id = ntohl(qs10->queue_id); | |
520 | oqs->tx_bytes = ntohll(get_32aligned_be64(&qs10->tx_bytes)); | |
521 | oqs->tx_packets = ntohll(get_32aligned_be64(&qs10->tx_packets)); | |
522 | oqs->tx_errors = ntohll(get_32aligned_be64(&qs10->tx_errors)); | |
523 | oqs->duration_sec = oqs->duration_nsec = UINT32_MAX; | |
524 | ||
525 | return 0; | |
526 | } | |
527 | ||
528 | static enum ofperr | |
529 | ofputil_queue_stats_from_ofp11(struct ofputil_queue_stats *oqs, | |
530 | const struct ofp11_queue_stats *qs11) | |
531 | { | |
532 | enum ofperr error; | |
533 | ||
534 | error = ofputil_port_from_ofp11(qs11->port_no, &oqs->port_no); | |
535 | if (error) { | |
536 | return error; | |
537 | } | |
538 | ||
539 | oqs->queue_id = ntohl(qs11->queue_id); | |
540 | oqs->tx_bytes = ntohll(qs11->tx_bytes); | |
541 | oqs->tx_packets = ntohll(qs11->tx_packets); | |
542 | oqs->tx_errors = ntohll(qs11->tx_errors); | |
543 | oqs->duration_sec = oqs->duration_nsec = UINT32_MAX; | |
544 | ||
545 | return 0; | |
546 | } | |
547 | ||
548 | static enum ofperr | |
549 | ofputil_queue_stats_from_ofp13(struct ofputil_queue_stats *oqs, | |
550 | const struct ofp13_queue_stats *qs13) | |
551 | { | |
552 | enum ofperr error = ofputil_queue_stats_from_ofp11(oqs, &qs13->qs); | |
553 | if (!error) { | |
554 | oqs->duration_sec = ntohl(qs13->duration_sec); | |
555 | oqs->duration_nsec = ntohl(qs13->duration_nsec); | |
556 | } | |
557 | ||
558 | return error; | |
559 | } | |
560 | ||
561 | static enum ofperr | |
562 | ofputil_pull_ofp14_queue_stats(struct ofputil_queue_stats *oqs, | |
563 | struct ofpbuf *msg) | |
564 | { | |
565 | const struct ofp14_queue_stats *qs14; | |
566 | size_t len; | |
567 | ||
568 | qs14 = ofpbuf_try_pull(msg, sizeof *qs14); | |
569 | if (!qs14) { | |
570 | return OFPERR_OFPBRC_BAD_LEN; | |
571 | } | |
572 | ||
573 | len = ntohs(qs14->length); | |
574 | if (len < sizeof *qs14 || len - sizeof *qs14 > msg->size) { | |
575 | return OFPERR_OFPBRC_BAD_LEN; | |
576 | } | |
577 | ofpbuf_pull(msg, len - sizeof *qs14); | |
578 | ||
579 | /* No properties yet defined, so ignore them for now. */ | |
580 | ||
581 | return ofputil_queue_stats_from_ofp13(oqs, &qs14->qs); | |
582 | } | |
583 | ||
584 | /* Converts an OFPST_QUEUE_STATS reply in 'msg' into an abstract | |
585 | * ofputil_queue_stats in 'qs'. | |
586 | * | |
587 | * Multiple OFPST_QUEUE_STATS replies can be packed into a single OpenFlow | |
588 | * message. Calling this function multiple times for a single 'msg' iterates | |
589 | * through the replies. The caller must initially leave 'msg''s layer pointers | |
590 | * null and not modify them between calls. | |
591 | * | |
592 | * Returns 0 if successful, EOF if no replies were left in this 'msg', | |
593 | * otherwise a positive errno value. */ | |
594 | int | |
595 | ofputil_decode_queue_stats(struct ofputil_queue_stats *qs, struct ofpbuf *msg) | |
596 | { | |
597 | enum ofperr error; | |
598 | enum ofpraw raw; | |
599 | ||
600 | error = (msg->header ? ofpraw_decode(&raw, msg->header) | |
601 | : ofpraw_pull(&raw, msg)); | |
602 | if (error) { | |
603 | return error; | |
604 | } | |
605 | ||
606 | if (!msg->size) { | |
607 | return EOF; | |
608 | } else if (raw == OFPRAW_OFPST14_QUEUE_REPLY) { | |
609 | return ofputil_pull_ofp14_queue_stats(qs, msg); | |
610 | } else if (raw == OFPRAW_OFPST13_QUEUE_REPLY) { | |
611 | const struct ofp13_queue_stats *qs13; | |
612 | ||
613 | qs13 = ofpbuf_try_pull(msg, sizeof *qs13); | |
614 | if (!qs13) { | |
615 | goto bad_len; | |
616 | } | |
617 | return ofputil_queue_stats_from_ofp13(qs, qs13); | |
618 | } else if (raw == OFPRAW_OFPST11_QUEUE_REPLY) { | |
619 | const struct ofp11_queue_stats *qs11; | |
620 | ||
621 | qs11 = ofpbuf_try_pull(msg, sizeof *qs11); | |
622 | if (!qs11) { | |
623 | goto bad_len; | |
624 | } | |
625 | return ofputil_queue_stats_from_ofp11(qs, qs11); | |
626 | } else if (raw == OFPRAW_OFPST10_QUEUE_REPLY) { | |
627 | const struct ofp10_queue_stats *qs10; | |
628 | ||
629 | qs10 = ofpbuf_try_pull(msg, sizeof *qs10); | |
630 | if (!qs10) { | |
631 | goto bad_len; | |
632 | } | |
633 | return ofputil_queue_stats_from_ofp10(qs, qs10); | |
634 | } else { | |
635 | OVS_NOT_REACHED(); | |
636 | } | |
637 | ||
638 | bad_len: | |
639 | VLOG_WARN_RL(&rl, "OFPST_QUEUE reply has %"PRIu32" leftover " | |
640 | "bytes at end", msg->size); | |
641 | return OFPERR_OFPBRC_BAD_LEN; | |
642 | } | |
643 | ||
644 | static void | |
645 | ofputil_queue_stats_to_ofp10(const struct ofputil_queue_stats *oqs, | |
646 | struct ofp10_queue_stats *qs10) | |
647 | { | |
648 | qs10->port_no = htons(ofp_to_u16(oqs->port_no)); | |
649 | memset(qs10->pad, 0, sizeof qs10->pad); | |
650 | qs10->queue_id = htonl(oqs->queue_id); | |
651 | put_32aligned_be64(&qs10->tx_bytes, htonll(oqs->tx_bytes)); | |
652 | put_32aligned_be64(&qs10->tx_packets, htonll(oqs->tx_packets)); | |
653 | put_32aligned_be64(&qs10->tx_errors, htonll(oqs->tx_errors)); | |
654 | } | |
655 | ||
656 | static void | |
657 | ofputil_queue_stats_to_ofp11(const struct ofputil_queue_stats *oqs, | |
658 | struct ofp11_queue_stats *qs11) | |
659 | { | |
660 | qs11->port_no = ofputil_port_to_ofp11(oqs->port_no); | |
661 | qs11->queue_id = htonl(oqs->queue_id); | |
662 | qs11->tx_bytes = htonll(oqs->tx_bytes); | |
663 | qs11->tx_packets = htonll(oqs->tx_packets); | |
664 | qs11->tx_errors = htonll(oqs->tx_errors); | |
665 | } | |
666 | ||
667 | static void | |
668 | ofputil_queue_stats_to_ofp13(const struct ofputil_queue_stats *oqs, | |
669 | struct ofp13_queue_stats *qs13) | |
670 | { | |
671 | ofputil_queue_stats_to_ofp11(oqs, &qs13->qs); | |
672 | if (oqs->duration_sec != UINT32_MAX) { | |
673 | qs13->duration_sec = htonl(oqs->duration_sec); | |
674 | qs13->duration_nsec = htonl(oqs->duration_nsec); | |
675 | } else { | |
676 | qs13->duration_sec = OVS_BE32_MAX; | |
677 | qs13->duration_nsec = OVS_BE32_MAX; | |
678 | } | |
679 | } | |
680 | ||
681 | static void | |
682 | ofputil_queue_stats_to_ofp14(const struct ofputil_queue_stats *oqs, | |
683 | struct ofp14_queue_stats *qs14) | |
684 | { | |
685 | qs14->length = htons(sizeof *qs14); | |
686 | memset(qs14->pad, 0, sizeof qs14->pad); | |
687 | ofputil_queue_stats_to_ofp13(oqs, &qs14->qs); | |
688 | } | |
689 | ||
690 | ||
691 | /* Encode a queue stat for 'oqs' and append it to 'replies'. */ | |
692 | void | |
693 | ofputil_append_queue_stat(struct ovs_list *replies, | |
694 | const struct ofputil_queue_stats *oqs) | |
695 | { | |
696 | switch (ofpmp_version(replies)) { | |
697 | case OFP13_VERSION: { | |
698 | struct ofp13_queue_stats *reply = ofpmp_append(replies, sizeof *reply); | |
699 | ofputil_queue_stats_to_ofp13(oqs, reply); | |
700 | break; | |
701 | } | |
702 | ||
703 | case OFP12_VERSION: | |
704 | case OFP11_VERSION: { | |
705 | struct ofp11_queue_stats *reply = ofpmp_append(replies, sizeof *reply); | |
706 | ofputil_queue_stats_to_ofp11(oqs, reply); | |
707 | break; | |
708 | } | |
709 | ||
710 | case OFP10_VERSION: { | |
711 | struct ofp10_queue_stats *reply = ofpmp_append(replies, sizeof *reply); | |
712 | ofputil_queue_stats_to_ofp10(oqs, reply); | |
713 | break; | |
714 | } | |
715 | ||
716 | case OFP14_VERSION: | |
717 | case OFP15_VERSION: | |
718 | case OFP16_VERSION: { | |
719 | struct ofp14_queue_stats *reply = ofpmp_append(replies, sizeof *reply); | |
720 | ofputil_queue_stats_to_ofp14(oqs, reply); | |
721 | break; | |
722 | } | |
723 | ||
724 | default: | |
725 | OVS_NOT_REACHED(); | |
726 | } | |
727 | } | |
728 |