]>
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-meter.h" | |
19 | #include "byte-order.h" | |
20 | #include "nx-match.h" | |
21 | #include "openvswitch/ofp-errors.h" | |
22 | #include "openvswitch/ofp-msgs.h" | |
23 | #include "openvswitch/ofp-parse.h" | |
fe2c69f4 | 24 | #include "openvswitch/ofp-print.h" |
0d71302e BP |
25 | #include "openvswitch/ofpbuf.h" |
26 | #include "openvswitch/vlog.h" | |
27 | ||
28 | VLOG_DEFINE_THIS_MODULE(ofp_meter); | |
29 | ||
30 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
31 | ||
fe2c69f4 BP |
32 | void |
33 | ofputil_format_meter_id(struct ds *s, uint32_t meter_id, char separator) | |
34 | { | |
35 | if (meter_id <= OFPM13_MAX) { | |
36 | ds_put_format(s, "meter%c%"PRIu32, separator, meter_id); | |
37 | } else { | |
38 | const char *name; | |
39 | switch (meter_id) { | |
40 | case OFPM13_SLOWPATH: | |
41 | name = "slowpath"; | |
42 | break; | |
43 | case OFPM13_CONTROLLER: | |
44 | name = "controller"; | |
45 | break; | |
46 | case OFPM13_ALL: | |
47 | name = "all"; | |
48 | break; | |
49 | default: | |
50 | name = "unknown"; | |
51 | } | |
52 | ds_put_format(s, "meter%c%s", separator, name); | |
53 | } | |
54 | } | |
55 | ||
56 | void | |
57 | ofputil_format_meter_band(struct ds *s, enum ofp13_meter_flags flags, | |
58 | const struct ofputil_meter_band *mb) | |
59 | { | |
60 | ds_put_cstr(s, "\ntype="); | |
61 | switch (mb->type) { | |
62 | case OFPMBT13_DROP: | |
63 | ds_put_cstr(s, "drop"); | |
64 | break; | |
65 | case OFPMBT13_DSCP_REMARK: | |
66 | ds_put_cstr(s, "dscp_remark"); | |
67 | break; | |
68 | default: | |
69 | ds_put_format(s, "%u", mb->type); | |
70 | } | |
71 | ||
72 | ds_put_format(s, " rate=%"PRIu32, mb->rate); | |
73 | ||
74 | if (flags & OFPMF13_BURST) { | |
75 | ds_put_format(s, " burst_size=%"PRIu32, mb->burst_size); | |
76 | } | |
77 | if (mb->type == OFPMBT13_DSCP_REMARK) { | |
78 | ds_put_format(s, " prec_level=%"PRIu8, mb->prec_level); | |
79 | } | |
80 | } | |
81 | ||
0d71302e BP |
82 | static enum ofperr |
83 | ofputil_pull_bands(struct ofpbuf *msg, size_t len, uint16_t *n_bands, | |
84 | struct ofpbuf *bands) | |
85 | { | |
86 | const struct ofp13_meter_band_header *ombh; | |
87 | struct ofputil_meter_band *mb; | |
88 | uint16_t n = 0; | |
89 | ||
90 | ombh = ofpbuf_try_pull(msg, len); | |
91 | if (!ombh) { | |
92 | return OFPERR_OFPBRC_BAD_LEN; | |
93 | } | |
94 | ||
95 | while (len >= sizeof (struct ofp13_meter_band_drop)) { | |
96 | size_t ombh_len = ntohs(ombh->len); | |
97 | /* All supported band types have the same length. */ | |
98 | if (ombh_len != sizeof (struct ofp13_meter_band_drop)) { | |
99 | return OFPERR_OFPBRC_BAD_LEN; | |
100 | } | |
101 | mb = ofpbuf_put_uninit(bands, sizeof *mb); | |
102 | mb->type = ntohs(ombh->type); | |
103 | if (mb->type != OFPMBT13_DROP && mb->type != OFPMBT13_DSCP_REMARK) { | |
104 | return OFPERR_OFPMMFC_BAD_BAND; | |
105 | } | |
106 | mb->rate = ntohl(ombh->rate); | |
107 | mb->burst_size = ntohl(ombh->burst_size); | |
108 | mb->prec_level = (mb->type == OFPMBT13_DSCP_REMARK) ? | |
109 | ((struct ofp13_meter_band_dscp_remark *)ombh)->prec_level : 0; | |
110 | n++; | |
111 | len -= ombh_len; | |
112 | ombh = ALIGNED_CAST(struct ofp13_meter_band_header *, | |
113 | (char *) ombh + ombh_len); | |
114 | } | |
115 | if (len) { | |
116 | return OFPERR_OFPBRC_BAD_LEN; | |
117 | } | |
118 | *n_bands = n; | |
119 | return 0; | |
120 | } | |
121 | ||
122 | enum ofperr | |
123 | ofputil_decode_meter_mod(const struct ofp_header *oh, | |
124 | struct ofputil_meter_mod *mm, | |
125 | struct ofpbuf *bands) | |
126 | { | |
127 | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); | |
128 | ofpraw_pull_assert(&b); | |
129 | const struct ofp13_meter_mod *omm = ofpbuf_pull(&b, sizeof *omm); | |
130 | ||
131 | /* Translate the message. */ | |
132 | mm->command = ntohs(omm->command); | |
133 | if (mm->command != OFPMC13_ADD && | |
134 | mm->command != OFPMC13_MODIFY && | |
135 | mm->command != OFPMC13_DELETE) { | |
136 | return OFPERR_OFPMMFC_BAD_COMMAND; | |
137 | } | |
138 | mm->meter.meter_id = ntohl(omm->meter_id); | |
139 | ||
140 | if (mm->command == OFPMC13_DELETE) { | |
141 | mm->meter.flags = 0; | |
142 | mm->meter.n_bands = 0; | |
143 | mm->meter.bands = NULL; | |
144 | } else { | |
145 | enum ofperr error; | |
146 | ||
147 | mm->meter.flags = ntohs(omm->flags); | |
148 | if (mm->meter.flags & OFPMF13_KBPS && | |
149 | mm->meter.flags & OFPMF13_PKTPS) { | |
150 | return OFPERR_OFPMMFC_BAD_FLAGS; | |
151 | } | |
0d71302e BP |
152 | |
153 | error = ofputil_pull_bands(&b, b.size, &mm->meter.n_bands, bands); | |
154 | if (error) { | |
155 | return error; | |
156 | } | |
8b70d824 | 157 | mm->meter.bands = bands->data; |
0d71302e BP |
158 | } |
159 | return 0; | |
160 | } | |
161 | ||
162 | void | |
163 | ofputil_decode_meter_request(const struct ofp_header *oh, uint32_t *meter_id) | |
164 | { | |
165 | const struct ofp13_meter_multipart_request *omr = ofpmsg_body(oh); | |
166 | *meter_id = ntohl(omr->meter_id); | |
167 | } | |
168 | ||
169 | struct ofpbuf * | |
170 | ofputil_encode_meter_request(enum ofp_version ofp_version, | |
171 | enum ofputil_meter_request_type type, | |
172 | uint32_t meter_id) | |
173 | { | |
174 | struct ofpbuf *msg; | |
175 | ||
176 | enum ofpraw raw; | |
177 | ||
178 | switch (type) { | |
179 | case OFPUTIL_METER_CONFIG: | |
180 | raw = OFPRAW_OFPST13_METER_CONFIG_REQUEST; | |
181 | break; | |
182 | case OFPUTIL_METER_STATS: | |
183 | raw = OFPRAW_OFPST13_METER_REQUEST; | |
184 | break; | |
185 | default: | |
186 | case OFPUTIL_METER_FEATURES: | |
187 | raw = OFPRAW_OFPST13_METER_FEATURES_REQUEST; | |
188 | break; | |
189 | } | |
190 | ||
191 | msg = ofpraw_alloc(raw, ofp_version, 0); | |
192 | ||
193 | if (type != OFPUTIL_METER_FEATURES) { | |
194 | struct ofp13_meter_multipart_request *omr; | |
195 | omr = ofpbuf_put_zeros(msg, sizeof *omr); | |
196 | omr->meter_id = htonl(meter_id); | |
197 | } | |
198 | return msg; | |
199 | } | |
200 | ||
201 | static void | |
202 | ofputil_put_bands(uint16_t n_bands, const struct ofputil_meter_band *mb, | |
203 | struct ofpbuf *msg) | |
204 | { | |
205 | uint16_t n = 0; | |
206 | ||
207 | for (n = 0; n < n_bands; ++n) { | |
208 | /* Currently all band types have same size. */ | |
209 | struct ofp13_meter_band_dscp_remark *ombh; | |
210 | size_t ombh_len = sizeof *ombh; | |
211 | ||
212 | ombh = ofpbuf_put_zeros(msg, ombh_len); | |
213 | ||
214 | ombh->type = htons(mb->type); | |
215 | ombh->len = htons(ombh_len); | |
216 | ombh->rate = htonl(mb->rate); | |
217 | ombh->burst_size = htonl(mb->burst_size); | |
218 | ombh->prec_level = mb->prec_level; | |
219 | ||
220 | mb++; | |
221 | } | |
222 | } | |
223 | ||
224 | /* Encode a meter stat for 'mc' and append it to 'replies'. */ | |
225 | void | |
226 | ofputil_append_meter_config(struct ovs_list *replies, | |
227 | const struct ofputil_meter_config *mc) | |
228 | { | |
229 | struct ofpbuf *msg = ofpbuf_from_list(ovs_list_back(replies)); | |
230 | size_t start_ofs = msg->size; | |
231 | struct ofp13_meter_config *reply; | |
232 | ||
233 | ofpbuf_put_uninit(msg, sizeof *reply); | |
234 | ofputil_put_bands(mc->n_bands, mc->bands, msg); | |
235 | ||
236 | reply = ofpbuf_at_assert(msg, start_ofs, sizeof *reply); | |
237 | reply->flags = htons(mc->flags); | |
238 | reply->meter_id = htonl(mc->meter_id); | |
239 | reply->length = htons(msg->size - start_ofs); | |
240 | ||
241 | ofpmp_postappend(replies, start_ofs); | |
242 | } | |
243 | ||
244 | /* Encode a meter stat for 'ms' and append it to 'replies'. */ | |
245 | void | |
246 | ofputil_append_meter_stats(struct ovs_list *replies, | |
247 | const struct ofputil_meter_stats *ms) | |
248 | { | |
249 | struct ofp13_meter_stats *reply; | |
250 | uint16_t n = 0; | |
251 | uint16_t len; | |
252 | ||
253 | len = sizeof *reply + ms->n_bands * sizeof(struct ofp13_meter_band_stats); | |
254 | reply = ofpmp_append(replies, len); | |
255 | ||
256 | reply->meter_id = htonl(ms->meter_id); | |
257 | reply->len = htons(len); | |
258 | memset(reply->pad, 0, sizeof reply->pad); | |
259 | reply->flow_count = htonl(ms->flow_count); | |
260 | reply->packet_in_count = htonll(ms->packet_in_count); | |
261 | reply->byte_in_count = htonll(ms->byte_in_count); | |
262 | reply->duration_sec = htonl(ms->duration_sec); | |
263 | reply->duration_nsec = htonl(ms->duration_nsec); | |
264 | ||
265 | for (n = 0; n < ms->n_bands; ++n) { | |
266 | const struct ofputil_meter_band_stats *src = &ms->bands[n]; | |
267 | struct ofp13_meter_band_stats *dst = &reply->band_stats[n]; | |
268 | ||
269 | dst->packet_band_count = htonll(src->packet_count); | |
270 | dst->byte_band_count = htonll(src->byte_count); | |
271 | } | |
272 | } | |
273 | ||
274 | /* Converts an OFPMP_METER_CONFIG reply in 'msg' into an abstract | |
275 | * ofputil_meter_config in 'mc', with mc->bands pointing to bands decoded into | |
276 | * 'bands'. The caller must have initialized 'bands' and retains ownership of | |
277 | * it across the call. | |
278 | * | |
279 | * Multiple OFPST13_METER_CONFIG replies can be packed into a single OpenFlow | |
280 | * message. Calling this function multiple times for a single 'msg' iterates | |
281 | * through the replies. 'bands' is cleared for each reply. | |
282 | * | |
283 | * Returns 0 if successful, EOF if no replies were left in this 'msg', | |
284 | * otherwise a positive errno value. */ | |
285 | int | |
286 | ofputil_decode_meter_config(struct ofpbuf *msg, | |
287 | struct ofputil_meter_config *mc, | |
288 | struct ofpbuf *bands) | |
289 | { | |
290 | const struct ofp13_meter_config *omc; | |
291 | enum ofperr err; | |
292 | ||
293 | /* Pull OpenFlow headers for the first call. */ | |
294 | if (!msg->header) { | |
295 | ofpraw_pull_assert(msg); | |
296 | } | |
297 | ||
298 | if (!msg->size) { | |
299 | return EOF; | |
300 | } | |
301 | ||
302 | omc = ofpbuf_try_pull(msg, sizeof *omc); | |
303 | if (!omc) { | |
304 | VLOG_WARN_RL(&rl, "OFPMP_METER_CONFIG reply has %"PRIu32" leftover " | |
305 | "bytes at end", msg->size); | |
306 | return OFPERR_OFPBRC_BAD_LEN; | |
307 | } | |
308 | ||
309 | ofpbuf_clear(bands); | |
310 | err = ofputil_pull_bands(msg, ntohs(omc->length) - sizeof *omc, | |
311 | &mc->n_bands, bands); | |
312 | if (err) { | |
313 | return err; | |
314 | } | |
315 | mc->meter_id = ntohl(omc->meter_id); | |
316 | mc->flags = ntohs(omc->flags); | |
317 | mc->bands = bands->data; | |
318 | ||
319 | return 0; | |
320 | } | |
321 | ||
fe2c69f4 BP |
322 | static void |
323 | ofp_print_meter_flags(struct ds *s, enum ofp13_meter_flags flags) | |
324 | { | |
325 | if (flags & OFPMF13_KBPS) { | |
326 | ds_put_cstr(s, "kbps "); | |
327 | } | |
328 | if (flags & OFPMF13_PKTPS) { | |
329 | ds_put_cstr(s, "pktps "); | |
330 | } | |
331 | if (flags & OFPMF13_BURST) { | |
332 | ds_put_cstr(s, "burst "); | |
333 | } | |
334 | if (flags & OFPMF13_STATS) { | |
335 | ds_put_cstr(s, "stats "); | |
336 | } | |
337 | ||
338 | flags &= ~(OFPMF13_KBPS | OFPMF13_PKTPS | OFPMF13_BURST | OFPMF13_STATS); | |
339 | if (flags) { | |
e4614813 | 340 | ds_put_format(s, "flags:0x%x ", (unsigned)flags); |
fe2c69f4 BP |
341 | } |
342 | } | |
343 | ||
344 | void | |
345 | ofputil_format_meter_config(struct ds *s, | |
346 | const struct ofputil_meter_config *mc) | |
347 | { | |
348 | uint16_t i; | |
349 | ||
350 | ofputil_format_meter_id(s, mc->meter_id, '='); | |
351 | ds_put_char(s, ' '); | |
352 | ||
353 | ofp_print_meter_flags(s, mc->flags); | |
354 | ||
355 | ds_put_cstr(s, "bands="); | |
356 | for (i = 0; i < mc->n_bands; ++i) { | |
357 | ofputil_format_meter_band(s, mc->flags, &mc->bands[i]); | |
358 | } | |
359 | ds_put_char(s, '\n'); | |
360 | } | |
361 | ||
0d71302e BP |
362 | static enum ofperr |
363 | ofputil_pull_band_stats(struct ofpbuf *msg, size_t len, uint16_t *n_bands, | |
364 | struct ofpbuf *bands) | |
365 | { | |
366 | const struct ofp13_meter_band_stats *ombs; | |
367 | struct ofputil_meter_band_stats *mbs; | |
368 | uint16_t n, i; | |
369 | ||
370 | ombs = ofpbuf_try_pull(msg, len); | |
371 | if (!ombs) { | |
372 | return OFPERR_OFPBRC_BAD_LEN; | |
373 | } | |
374 | ||
375 | n = len / sizeof *ombs; | |
376 | if (len != n * sizeof *ombs) { | |
377 | return OFPERR_OFPBRC_BAD_LEN; | |
378 | } | |
379 | ||
380 | mbs = ofpbuf_put_uninit(bands, len); | |
381 | ||
382 | for (i = 0; i < n; ++i) { | |
383 | mbs[i].packet_count = ntohll(ombs[i].packet_band_count); | |
384 | mbs[i].byte_count = ntohll(ombs[i].byte_band_count); | |
385 | } | |
386 | *n_bands = n; | |
387 | return 0; | |
388 | } | |
389 | ||
390 | /* Converts an OFPMP_METER reply in 'msg' into an abstract | |
391 | * ofputil_meter_stats in 'ms', with ms->bands pointing to band stats | |
392 | * decoded into 'bands'. | |
393 | * | |
394 | * Multiple OFPMP_METER replies can be packed into a single OpenFlow | |
395 | * message. Calling this function multiple times for a single 'msg' iterates | |
396 | * through the replies. 'bands' is cleared for each reply. | |
397 | * | |
398 | * Returns 0 if successful, EOF if no replies were left in this 'msg', | |
399 | * otherwise a positive errno value. */ | |
400 | int | |
401 | ofputil_decode_meter_stats(struct ofpbuf *msg, | |
402 | struct ofputil_meter_stats *ms, | |
403 | struct ofpbuf *bands) | |
404 | { | |
405 | const struct ofp13_meter_stats *oms; | |
406 | enum ofperr err; | |
407 | ||
408 | /* Pull OpenFlow headers for the first call. */ | |
409 | if (!msg->header) { | |
410 | ofpraw_pull_assert(msg); | |
411 | } | |
412 | ||
413 | if (!msg->size) { | |
414 | return EOF; | |
415 | } | |
416 | ||
417 | oms = ofpbuf_try_pull(msg, sizeof *oms); | |
418 | if (!oms) { | |
419 | VLOG_WARN_RL(&rl, "OFPMP_METER reply has %"PRIu32" leftover bytes " | |
420 | "at end", msg->size); | |
421 | return OFPERR_OFPBRC_BAD_LEN; | |
422 | } | |
423 | ||
424 | ofpbuf_clear(bands); | |
425 | err = ofputil_pull_band_stats(msg, ntohs(oms->len) - sizeof *oms, | |
426 | &ms->n_bands, bands); | |
427 | if (err) { | |
428 | return err; | |
429 | } | |
430 | ms->meter_id = ntohl(oms->meter_id); | |
431 | ms->flow_count = ntohl(oms->flow_count); | |
432 | ms->packet_in_count = ntohll(oms->packet_in_count); | |
433 | ms->byte_in_count = ntohll(oms->byte_in_count); | |
434 | ms->duration_sec = ntohl(oms->duration_sec); | |
435 | ms->duration_nsec = ntohl(oms->duration_nsec); | |
436 | ms->bands = bands->data; | |
437 | ||
438 | return 0; | |
439 | } | |
440 | ||
fe2c69f4 BP |
441 | void |
442 | ofputil_format_meter_stats(struct ds *s, const struct ofputil_meter_stats *ms) | |
443 | { | |
444 | uint16_t i; | |
445 | ||
446 | ofputil_format_meter_id(s, ms->meter_id, ':'); | |
447 | ds_put_char(s, ' '); | |
448 | ds_put_format(s, "flow_count:%"PRIu32" ", ms->flow_count); | |
449 | ds_put_format(s, "packet_in_count:%"PRIu64" ", ms->packet_in_count); | |
450 | ds_put_format(s, "byte_in_count:%"PRIu64" ", ms->byte_in_count); | |
451 | ds_put_cstr(s, "duration:"); | |
452 | ofp_print_duration(s, ms->duration_sec, ms->duration_nsec); | |
453 | ds_put_char(s, ' '); | |
454 | ||
455 | ds_put_cstr(s, "bands:\n"); | |
456 | for (i = 0; i < ms->n_bands; ++i) { | |
457 | ds_put_format(s, "%d: ", i); | |
458 | ds_put_format(s, "packet_count:%"PRIu64" ", ms->bands[i].packet_count); | |
459 | ds_put_format(s, "byte_count:%"PRIu64"\n", ms->bands[i].byte_count); | |
460 | } | |
461 | } | |
462 | ||
0d71302e BP |
463 | void |
464 | ofputil_decode_meter_features(const struct ofp_header *oh, | |
465 | struct ofputil_meter_features *mf) | |
466 | { | |
467 | const struct ofp13_meter_features *omf = ofpmsg_body(oh); | |
468 | ||
469 | mf->max_meters = ntohl(omf->max_meter); | |
470 | mf->band_types = ntohl(omf->band_types); | |
471 | mf->capabilities = ntohl(omf->capabilities); | |
472 | mf->max_bands = omf->max_bands; | |
473 | mf->max_color = omf->max_color; | |
474 | } | |
475 | ||
476 | struct ofpbuf * | |
477 | ofputil_encode_meter_features_reply(const struct ofputil_meter_features *mf, | |
478 | const struct ofp_header *request) | |
479 | { | |
480 | struct ofpbuf *reply; | |
481 | struct ofp13_meter_features *omf; | |
482 | ||
483 | reply = ofpraw_alloc_stats_reply(request, 0); | |
484 | omf = ofpbuf_put_zeros(reply, sizeof *omf); | |
485 | ||
486 | omf->max_meter = htonl(mf->max_meters); | |
487 | omf->band_types = htonl(mf->band_types); | |
488 | omf->capabilities = htonl(mf->capabilities); | |
489 | omf->max_bands = mf->max_bands; | |
490 | omf->max_color = mf->max_color; | |
491 | ||
492 | return reply; | |
493 | } | |
494 | ||
fe2c69f4 BP |
495 | static const char * |
496 | ofputil_meter_band_types_to_name(uint32_t bit) | |
497 | { | |
498 | switch (bit) { | |
499 | case 1 << OFPMBT13_DROP: return "drop"; | |
500 | case 1 << OFPMBT13_DSCP_REMARK: return "dscp_remark"; | |
501 | } | |
502 | ||
503 | return NULL; | |
504 | } | |
505 | ||
506 | static const char * | |
507 | ofputil_meter_capabilities_to_name(uint32_t bit) | |
508 | { | |
509 | enum ofp13_meter_flags flag = bit; | |
510 | ||
511 | switch (flag) { | |
512 | case OFPMF13_KBPS: return "kbps"; | |
513 | case OFPMF13_PKTPS: return "pktps"; | |
514 | case OFPMF13_BURST: return "burst"; | |
515 | case OFPMF13_STATS: return "stats"; | |
516 | } | |
517 | ||
518 | return NULL; | |
519 | } | |
520 | ||
521 | void | |
522 | ofputil_format_meter_features(struct ds *s, | |
523 | const struct ofputil_meter_features *mf) | |
524 | { | |
525 | ds_put_format(s, "\nmax_meter:%"PRIu32, mf->max_meters); | |
526 | ds_put_format(s, " max_bands:%"PRIu8, mf->max_bands); | |
527 | ds_put_format(s, " max_color:%"PRIu8"\n", mf->max_color); | |
528 | ||
529 | ds_put_cstr(s, "band_types: "); | |
530 | ofp_print_bit_names(s, mf->band_types, | |
531 | ofputil_meter_band_types_to_name, ' '); | |
532 | ds_put_char(s, '\n'); | |
533 | ||
534 | ds_put_cstr(s, "capabilities: "); | |
535 | ofp_print_bit_names(s, mf->capabilities, | |
536 | ofputil_meter_capabilities_to_name, ' '); | |
537 | ds_put_char(s, '\n'); | |
538 | } | |
539 | ||
0d71302e BP |
540 | struct ofpbuf * |
541 | ofputil_encode_meter_mod(enum ofp_version ofp_version, | |
542 | const struct ofputil_meter_mod *mm) | |
543 | { | |
544 | struct ofpbuf *msg; | |
545 | ||
546 | struct ofp13_meter_mod *omm; | |
547 | ||
548 | msg = ofpraw_alloc(OFPRAW_OFPT13_METER_MOD, ofp_version, | |
549 | NXM_TYPICAL_LEN + mm->meter.n_bands * 16); | |
550 | omm = ofpbuf_put_zeros(msg, sizeof *omm); | |
551 | omm->command = htons(mm->command); | |
552 | if (mm->command != OFPMC13_DELETE) { | |
553 | omm->flags = htons(mm->meter.flags); | |
554 | } | |
555 | omm->meter_id = htonl(mm->meter.meter_id); | |
556 | ||
557 | ofputil_put_bands(mm->meter.n_bands, mm->meter.bands, msg); | |
558 | ||
559 | ofpmsg_update_length(msg); | |
560 | return msg; | |
561 | } | |
562 | ||
563 | /* Parse a string representation of a meter modification message to '*mm'. | |
564 | * If successful, 'mm->meter.bands' must be free()d by the caller. */ | |
565 | static char * OVS_WARN_UNUSED_RESULT | |
566 | parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string, | |
567 | struct ofpbuf *bands, int command, | |
568 | enum ofputil_protocol *usable_protocols) | |
569 | { | |
570 | enum { | |
571 | F_METER = 1 << 0, | |
572 | F_FLAGS = 1 << 1, | |
573 | F_BANDS = 1 << 2, | |
574 | } fields; | |
575 | char *save_ptr = NULL; | |
576 | char *band_str = NULL; | |
577 | char *name; | |
578 | ||
579 | /* Meters require at least OF 1.3. */ | |
580 | *usable_protocols = OFPUTIL_P_OF13_UP; | |
581 | ||
582 | switch (command) { | |
583 | case -1: | |
584 | fields = F_METER; | |
585 | break; | |
586 | ||
587 | case OFPMC13_ADD: | |
588 | fields = F_METER | F_FLAGS | F_BANDS; | |
589 | break; | |
590 | ||
591 | case OFPMC13_DELETE: | |
592 | fields = F_METER; | |
593 | break; | |
594 | ||
595 | case OFPMC13_MODIFY: | |
596 | fields = F_METER | F_FLAGS | F_BANDS; | |
597 | break; | |
598 | ||
599 | default: | |
600 | OVS_NOT_REACHED(); | |
601 | } | |
602 | ||
603 | mm->command = command; | |
604 | mm->meter.meter_id = 0; | |
605 | mm->meter.flags = 0; | |
606 | mm->meter.n_bands = 0; | |
607 | mm->meter.bands = NULL; | |
608 | ||
609 | if (fields & F_BANDS) { | |
610 | band_str = strstr(string, "band"); | |
611 | if (!band_str) { | |
612 | return xstrdup("must specify bands"); | |
613 | } | |
614 | *band_str = '\0'; | |
615 | ||
616 | band_str = strchr(band_str + 1, '='); | |
617 | if (!band_str) { | |
618 | return xstrdup("must specify bands"); | |
619 | } | |
620 | ||
621 | band_str++; | |
622 | } | |
623 | for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; | |
624 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
625 | ||
626 | if (fields & F_FLAGS && !strcmp(name, "kbps")) { | |
627 | mm->meter.flags |= OFPMF13_KBPS; | |
628 | } else if (fields & F_FLAGS && !strcmp(name, "pktps")) { | |
629 | mm->meter.flags |= OFPMF13_PKTPS; | |
630 | } else if (fields & F_FLAGS && !strcmp(name, "burst")) { | |
631 | mm->meter.flags |= OFPMF13_BURST; | |
632 | } else if (fields & F_FLAGS && !strcmp(name, "stats")) { | |
633 | mm->meter.flags |= OFPMF13_STATS; | |
634 | } else { | |
635 | char *value; | |
636 | ||
637 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
638 | if (!value) { | |
639 | return xasprintf("field %s missing value", name); | |
640 | } | |
641 | ||
642 | if (!strcmp(name, "meter")) { | |
643 | if (!strcmp(value, "all")) { | |
644 | mm->meter.meter_id = OFPM13_ALL; | |
645 | } else if (!strcmp(value, "controller")) { | |
646 | mm->meter.meter_id = OFPM13_CONTROLLER; | |
647 | } else if (!strcmp(value, "slowpath")) { | |
648 | mm->meter.meter_id = OFPM13_SLOWPATH; | |
649 | } else { | |
650 | char *error = str_to_u32(value, &mm->meter.meter_id); | |
651 | if (error) { | |
652 | return error; | |
653 | } | |
654 | if (mm->meter.meter_id > OFPM13_MAX | |
655 | || !mm->meter.meter_id) { | |
656 | return xasprintf("invalid value for %s", name); | |
657 | } | |
658 | } | |
659 | } else { | |
660 | return xasprintf("unknown keyword %s", name); | |
661 | } | |
662 | } | |
663 | } | |
664 | if (fields & F_METER && !mm->meter.meter_id) { | |
665 | return xstrdup("must specify 'meter'"); | |
666 | } | |
667 | if (fields & F_FLAGS && !mm->meter.flags) { | |
668 | return xstrdup("meter must specify either 'kbps' or 'pktps'"); | |
669 | } | |
670 | ||
671 | if (fields & F_BANDS) { | |
672 | uint16_t n_bands = 0; | |
673 | struct ofputil_meter_band *band = NULL; | |
674 | int i; | |
675 | ||
676 | for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name; | |
677 | name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { | |
678 | ||
679 | char *value; | |
680 | ||
681 | value = strtok_r(NULL, ", \t\r\n", &save_ptr); | |
682 | if (!value) { | |
683 | return xasprintf("field %s missing value", name); | |
684 | } | |
685 | ||
686 | if (!strcmp(name, "type")) { | |
687 | /* Start a new band */ | |
688 | band = ofpbuf_put_zeros(bands, sizeof *band); | |
689 | n_bands++; | |
690 | ||
691 | if (!strcmp(value, "drop")) { | |
692 | band->type = OFPMBT13_DROP; | |
693 | } else if (!strcmp(value, "dscp_remark")) { | |
694 | band->type = OFPMBT13_DSCP_REMARK; | |
695 | } else { | |
696 | return xasprintf("field %s unknown value %s", name, value); | |
697 | } | |
698 | } else if (!band || !band->type) { | |
699 | return xstrdup("band must start with the 'type' keyword"); | |
700 | } else if (!strcmp(name, "rate")) { | |
701 | char *error = str_to_u32(value, &band->rate); | |
702 | if (error) { | |
703 | return error; | |
704 | } | |
705 | } else if (!strcmp(name, "burst_size")) { | |
706 | char *error = str_to_u32(value, &band->burst_size); | |
707 | if (error) { | |
708 | return error; | |
709 | } | |
710 | } else if (!strcmp(name, "prec_level")) { | |
711 | char *error = str_to_u8(value, name, &band->prec_level); | |
712 | if (error) { | |
713 | return error; | |
714 | } | |
715 | } else { | |
716 | return xasprintf("unknown keyword %s", name); | |
717 | } | |
718 | } | |
719 | /* validate bands */ | |
720 | if (!n_bands) { | |
721 | return xstrdup("meter must have bands"); | |
722 | } | |
723 | ||
724 | mm->meter.n_bands = n_bands; | |
725 | mm->meter.bands = ofpbuf_steal_data(bands); | |
726 | ||
727 | for (i = 0; i < n_bands; ++i) { | |
728 | band = &mm->meter.bands[i]; | |
729 | ||
730 | if (!band->type) { | |
731 | return xstrdup("band must have 'type'"); | |
732 | } | |
733 | if (band->type == OFPMBT13_DSCP_REMARK) { | |
734 | if (!band->prec_level) { | |
735 | return xstrdup("'dscp_remark' band must have" | |
736 | " 'prec_level'"); | |
737 | } | |
738 | } else { | |
739 | if (band->prec_level) { | |
740 | return xstrdup("Only 'dscp_remark' band may have" | |
741 | " 'prec_level'"); | |
742 | } | |
743 | } | |
744 | if (!band->rate) { | |
745 | return xstrdup("band must have 'rate'"); | |
746 | } | |
747 | if (mm->meter.flags & OFPMF13_BURST) { | |
748 | if (!band->burst_size) { | |
749 | return xstrdup("band must have 'burst_size' " | |
750 | "when 'burst' flag is set"); | |
751 | } | |
752 | } else { | |
753 | if (band->burst_size) { | |
754 | return xstrdup("band may have 'burst_size' only " | |
755 | "when 'burst' flag is set"); | |
756 | } | |
757 | } | |
758 | } | |
759 | } | |
760 | ||
761 | return NULL; | |
762 | } | |
763 | ||
5d2988f7 JP |
764 | /* Convert 'str_' (as described in the Meter Syntax section of the |
765 | * ovs-ofctl man page) into 'mm' for sending the specified meter_mod | |
766 | * 'command' to a switch. | |
0d71302e BP |
767 | * |
768 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
769 | * error. The caller is responsible for freeing the returned string. | |
5d2988f7 | 770 | * If successful, 'mm->meter.bands' must be free()'d by the caller. */ |
0d71302e BP |
771 | char * OVS_WARN_UNUSED_RESULT |
772 | parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_, | |
773 | int command, enum ofputil_protocol *usable_protocols) | |
774 | { | |
775 | struct ofpbuf bands; | |
776 | char *string; | |
777 | char *error; | |
778 | ||
779 | ofpbuf_init(&bands, 64); | |
780 | string = xstrdup(str_); | |
781 | ||
782 | error = parse_ofp_meter_mod_str__(mm, string, &bands, command, | |
783 | usable_protocols); | |
784 | ||
785 | free(string); | |
786 | ofpbuf_uninit(&bands); | |
787 | ||
788 | return error; | |
789 | } | |
fe2c69f4 BP |
790 | |
791 | void | |
792 | ofputil_format_meter_mod(struct ds *s, const struct ofputil_meter_mod *mm) | |
793 | { | |
794 | switch (mm->command) { | |
795 | case OFPMC13_ADD: | |
796 | ds_put_cstr(s, " ADD "); | |
797 | break; | |
798 | case OFPMC13_MODIFY: | |
799 | ds_put_cstr(s, " MOD "); | |
800 | break; | |
801 | case OFPMC13_DELETE: | |
802 | ds_put_cstr(s, " DEL "); | |
803 | break; | |
804 | default: | |
805 | ds_put_format(s, " cmd:%d ", mm->command); | |
806 | } | |
807 | ||
808 | ofputil_format_meter_config(s, &mm->meter); | |
809 | } |