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