]>
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-table.h" | |
19 | #include "bitmap.h" | |
20 | #include "nx-match.h" | |
21 | #include "openvswitch/dynamic-string.h" | |
22 | #include "openvswitch/json.h" | |
23 | #include "openvswitch/ofp-actions.h" | |
24 | #include "openvswitch/ofp-msgs.h" | |
dfc77282 | 25 | #include "openvswitch/ofp-print.h" |
0d71302e BP |
26 | #include "openvswitch/ofp-prop.h" |
27 | #include "openvswitch/ofpbuf.h" | |
28 | #include "openvswitch/vlog.h" | |
29 | #include "util.h" | |
30 | ||
31 | VLOG_DEFINE_THIS_MODULE(ofp_table); | |
32 | ||
33 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
34 | ||
35 | static ovs_be32 ofputil_encode_table_config(enum ofputil_table_miss, | |
36 | enum ofputil_table_eviction, | |
37 | enum ofputil_table_vacancy, | |
38 | enum ofp_version); | |
39 | static enum ofputil_table_vacancy ofputil_decode_table_vacancy( | |
40 | ovs_be32 config, enum ofp_version); | |
41 | static enum ofputil_table_eviction ofputil_decode_table_eviction( | |
42 | ovs_be32 config, enum ofp_version); | |
43 | ||
dfc77282 BP |
44 | const char * |
45 | ofputil_table_miss_to_string(enum ofputil_table_miss miss) | |
46 | { | |
47 | switch (miss) { | |
48 | case OFPUTIL_TABLE_MISS_DEFAULT: return "default"; | |
49 | case OFPUTIL_TABLE_MISS_CONTROLLER: return "controller"; | |
50 | case OFPUTIL_TABLE_MISS_CONTINUE: return "continue"; | |
51 | case OFPUTIL_TABLE_MISS_DROP: return "drop"; | |
52 | default: return "***error***"; | |
53 | } | |
54 | } | |
55 | ||
56 | const char * | |
57 | ofputil_table_eviction_to_string(enum ofputil_table_eviction eviction) | |
58 | { | |
59 | switch (eviction) { | |
60 | case OFPUTIL_TABLE_EVICTION_DEFAULT: return "default"; | |
61 | case OFPUTIL_TABLE_EVICTION_ON: return "on"; | |
62 | case OFPUTIL_TABLE_EVICTION_OFF: return "off"; | |
63 | default: return "***error***"; | |
64 | } | |
65 | } | |
66 | ||
67 | const char * | |
68 | ofputil_table_vacancy_to_string(enum ofputil_table_vacancy vacancy) | |
69 | { | |
70 | switch (vacancy) { | |
71 | case OFPUTIL_TABLE_VACANCY_DEFAULT: return "default"; | |
72 | case OFPUTIL_TABLE_VACANCY_ON: return "on"; | |
73 | case OFPUTIL_TABLE_VACANCY_OFF: return "off"; | |
74 | default: return "***error***"; | |
75 | } | |
76 | } | |
77 | \f | |
0d71302e BP |
78 | /* ofputil_table_map. */ |
79 | ||
80 | void | |
81 | ofputil_table_map_init(struct ofputil_table_map *map) | |
82 | { | |
83 | namemap_init(&map->map); | |
84 | } | |
85 | ||
86 | void | |
87 | ofputil_table_map_put(struct ofputil_table_map *map, | |
88 | uint8_t table_id, const char *name) | |
89 | { | |
90 | namemap_put(&map->map, table_id, name); | |
91 | } | |
92 | ||
93 | const char * | |
94 | ofputil_table_map_get_name(const struct ofputil_table_map *map, | |
95 | uint8_t table_id) | |
96 | { | |
97 | struct namemap_node *node | |
98 | = map ? namemap_find_by_number(&map->map, table_id) : NULL; | |
99 | return node && !node->duplicate ? node->name : NULL; | |
100 | } | |
101 | ||
102 | uint8_t | |
103 | ofputil_table_map_get_number(const struct ofputil_table_map *map, | |
104 | const char *name) | |
105 | { | |
106 | struct namemap_node *node | |
107 | = map ? namemap_find_by_name(&map->map, name) : NULL; | |
108 | return node && !node->duplicate ? node->number : UINT8_MAX; | |
109 | } | |
110 | ||
111 | void | |
112 | ofputil_table_map_destroy(struct ofputil_table_map *map) | |
113 | { | |
114 | namemap_destroy(&map->map); | |
115 | } | |
116 | \f | |
117 | /* Table numbers. */ | |
118 | ||
119 | /* Stores the table number represented by 's' into '*tablep'. 's' may be an | |
120 | * integer or, if 'table_map' is nonnull, a name (quoted or unquoted). | |
121 | * | |
122 | * Returns true if successful, false if 's' is not a valid OpenFlow table | |
123 | * number or name. The caller should issue an error message in this case, | |
124 | * because this function usually does not. (This gives the caller an | |
125 | * opportunity to look up the table name another way, e.g. by contacting the | |
126 | * switch and listing the names of all its tables). */ | |
127 | bool | |
128 | ofputil_table_from_string(const char *s, | |
129 | const struct ofputil_table_map *table_map, | |
130 | uint8_t *tablep) | |
131 | { | |
132 | *tablep = 0; | |
133 | if (*s == '-') { | |
134 | VLOG_WARN("Negative value %s is not a valid table number.", s); | |
135 | return false; | |
136 | } | |
137 | ||
138 | unsigned int table; | |
139 | if (str_to_uint(s, 10, &table)) { | |
140 | if (table > 255) { | |
141 | VLOG_WARN("table %u is outside the supported range 0 through 255", | |
142 | table); | |
143 | return false; | |
144 | } | |
145 | *tablep = table; | |
146 | return true; | |
147 | } else { | |
148 | if (s[0] != '"') { | |
149 | table = ofputil_table_map_get_number(table_map, s); | |
150 | } else { | |
151 | size_t length = strlen(s); | |
152 | char *name = NULL; | |
153 | if (length > 1 | |
154 | && s[length - 1] == '"' | |
155 | && json_string_unescape(s + 1, length - 2, &name)) { | |
156 | table = ofputil_table_map_get_number(table_map, name); | |
157 | } | |
158 | free(name); | |
159 | } | |
160 | if (table != UINT8_MAX) { | |
161 | *tablep = table; | |
162 | return true; | |
163 | } | |
164 | ||
165 | return false; | |
166 | } | |
167 | } | |
168 | ||
169 | /* Appends to 's' a string representation of the OpenFlow table number 'table', | |
170 | * either the table number or a name drawn from 'table_map'. */ | |
171 | void | |
172 | ofputil_format_table(uint8_t table, const struct ofputil_table_map *table_map, | |
173 | struct ds *s) | |
174 | { | |
175 | const char *table_name = ofputil_table_map_get_name(table_map, table); | |
176 | if (table_name) { | |
177 | namemap_put_name(table_name, s); | |
178 | } else { | |
179 | ds_put_format(s, "%"PRIu8, table); | |
180 | } | |
181 | } | |
182 | ||
183 | /* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string | |
184 | * representation of OpenFlow table number 'table', either the table's number | |
185 | * or a name drawn from 'table_map'. */ | |
186 | void | |
187 | ofputil_table_to_string(uint8_t table, | |
188 | const struct ofputil_table_map *table_map, | |
189 | char *namebuf, size_t bufsize) | |
190 | { | |
191 | const char *table_name = ofputil_table_map_get_name(table_map, table); | |
192 | if (table_name) { | |
193 | struct ds s = DS_EMPTY_INITIALIZER; | |
194 | namemap_put_name(table_name, &s); | |
195 | ovs_strlcpy(namebuf, ds_cstr(&s), bufsize); | |
196 | ds_destroy(&s); | |
197 | return; | |
198 | } | |
199 | ||
200 | snprintf(namebuf, bufsize, "%"PRIu8, table); | |
201 | } | |
202 | \f | |
203 | /* Table features. */ | |
204 | ||
205 | static enum ofperr | |
206 | pull_table_feature_property(struct ofpbuf *msg, struct ofpbuf *payload, | |
207 | uint64_t *typep) | |
208 | { | |
209 | enum ofperr error; | |
210 | ||
211 | error = ofpprop_pull(msg, payload, typep); | |
212 | if (payload && !error) { | |
213 | ofpbuf_pull(payload, (char *)payload->msg - (char *)payload->header); | |
214 | } | |
215 | return error; | |
216 | } | |
217 | ||
218 | static enum ofperr | |
219 | parse_action_bitmap(struct ofpbuf *payload, enum ofp_version ofp_version, | |
220 | uint64_t *ofpacts) | |
221 | { | |
222 | uint32_t types = 0; | |
223 | ||
224 | while (payload->size > 0) { | |
225 | enum ofperr error; | |
226 | uint64_t type; | |
227 | ||
228 | error = ofpprop_pull__(payload, NULL, 1, 0x10000, &type); | |
229 | if (error) { | |
230 | return error; | |
231 | } | |
232 | if (type < CHAR_BIT * sizeof types) { | |
233 | types |= 1u << type; | |
234 | } | |
235 | } | |
236 | ||
237 | *ofpacts = ofpact_bitmap_from_openflow(htonl(types), ofp_version); | |
238 | return 0; | |
239 | } | |
240 | ||
241 | static enum ofperr | |
242 | parse_instruction_ids(struct ofpbuf *payload, bool loose, uint32_t *insts) | |
243 | { | |
244 | *insts = 0; | |
245 | while (payload->size > 0) { | |
246 | enum ovs_instruction_type inst; | |
247 | enum ofperr error; | |
248 | uint64_t ofpit; | |
249 | ||
250 | /* OF1.3 and OF1.4 aren't clear about padding in the instruction IDs. | |
251 | * It seems clear that they aren't padded to 8 bytes, though, because | |
252 | * both standards say that "non-experimenter instructions are 4 bytes" | |
253 | * and do not mention any padding before the first instruction ID. | |
254 | * (There wouldn't be any point in padding to 8 bytes if the IDs were | |
255 | * aligned on an odd 4-byte boundary.) | |
256 | * | |
257 | * Anyway, we just assume they're all glommed together on byte | |
258 | * boundaries. */ | |
259 | error = ofpprop_pull__(payload, NULL, 1, 0x10000, &ofpit); | |
260 | if (error) { | |
261 | return error; | |
262 | } | |
263 | ||
264 | error = ovs_instruction_type_from_inst_type(&inst, ofpit); | |
265 | if (!error) { | |
266 | *insts |= 1u << inst; | |
267 | } else if (!loose) { | |
268 | return error; | |
269 | } | |
270 | } | |
271 | return 0; | |
272 | } | |
273 | ||
274 | static enum ofperr | |
275 | parse_table_features_next_table(struct ofpbuf *payload, | |
276 | unsigned long int *next_tables) | |
277 | { | |
278 | size_t i; | |
279 | ||
280 | memset(next_tables, 0, bitmap_n_bytes(255)); | |
281 | for (i = 0; i < payload->size; i++) { | |
282 | uint8_t id = ((const uint8_t *) payload->data)[i]; | |
283 | if (id >= 255) { | |
284 | return OFPERR_OFPBPC_BAD_VALUE; | |
285 | } | |
286 | bitmap_set1(next_tables, id); | |
287 | } | |
288 | return 0; | |
289 | } | |
290 | ||
291 | static enum ofperr | |
292 | parse_oxms(struct ofpbuf *payload, bool loose, | |
293 | struct mf_bitmap *exactp, struct mf_bitmap *maskedp) | |
294 | { | |
295 | struct mf_bitmap exact = MF_BITMAP_INITIALIZER; | |
296 | struct mf_bitmap masked = MF_BITMAP_INITIALIZER; | |
297 | ||
298 | while (payload->size > 0) { | |
299 | const struct mf_field *field; | |
300 | enum ofperr error; | |
301 | bool hasmask; | |
302 | ||
303 | error = nx_pull_header(payload, NULL, &field, &hasmask); | |
304 | if (!error) { | |
305 | bitmap_set1(hasmask ? masked.bm : exact.bm, field->id); | |
306 | } else if (error != OFPERR_OFPBMC_BAD_FIELD || !loose) { | |
307 | return error; | |
308 | } | |
309 | } | |
310 | if (exactp) { | |
311 | *exactp = exact; | |
312 | } else if (!bitmap_is_all_zeros(exact.bm, MFF_N_IDS)) { | |
313 | return OFPERR_OFPBMC_BAD_MASK; | |
314 | } | |
315 | if (maskedp) { | |
316 | *maskedp = masked; | |
317 | } else if (!bitmap_is_all_zeros(masked.bm, MFF_N_IDS)) { | |
318 | return OFPERR_OFPBMC_BAD_MASK; | |
319 | } | |
320 | return 0; | |
321 | } | |
322 | ||
323 | /* Converts an OFPMP_TABLE_FEATURES request or reply in 'msg' into an abstract | |
324 | * ofputil_table_features in 'tf'. | |
325 | * | |
326 | * If 'loose' is true, this function ignores properties and values that it does | |
327 | * not understand, as a controller would want to do when interpreting | |
328 | * capabilities provided by a switch. If 'loose' is false, this function | |
329 | * treats unknown properties and values as an error, as a switch would want to | |
330 | * do when interpreting a configuration request made by a controller. | |
331 | * | |
332 | * A single OpenFlow message can specify features for multiple tables. Calling | |
333 | * this function multiple times for a single 'msg' iterates through the tables | |
334 | * in the message. The caller must initially leave 'msg''s layer pointers null | |
335 | * and not modify them between calls. | |
336 | * | |
337 | * Returns 0 if successful, EOF if no tables were left in this 'msg', otherwise | |
338 | * a positive "enum ofperr" value. */ | |
339 | int | |
340 | ofputil_decode_table_features(struct ofpbuf *msg, | |
341 | struct ofputil_table_features *tf, bool loose) | |
342 | { | |
343 | memset(tf, 0, sizeof *tf); | |
344 | ||
345 | if (!msg->header) { | |
346 | ofpraw_pull_assert(msg); | |
347 | } | |
348 | ||
349 | if (!msg->size) { | |
350 | return EOF; | |
351 | } | |
352 | ||
353 | const struct ofp_header *oh = msg->header; | |
354 | struct ofp13_table_features *otf = msg->data; | |
355 | if (msg->size < sizeof *otf) { | |
356 | return OFPERR_OFPBPC_BAD_LEN; | |
357 | } | |
358 | ||
359 | unsigned int len = ntohs(otf->length); | |
360 | if (len < sizeof *otf || len % 8 || len > msg->size) { | |
361 | return OFPERR_OFPBPC_BAD_LEN; | |
362 | } | |
363 | ||
364 | tf->table_id = otf->table_id; | |
365 | if (tf->table_id == OFPTT_ALL) { | |
366 | return OFPERR_OFPTFFC_BAD_TABLE; | |
367 | } | |
368 | ||
369 | ovs_strlcpy_arrays(tf->name, otf->name); | |
370 | tf->metadata_match = otf->metadata_match; | |
371 | tf->metadata_write = otf->metadata_write; | |
372 | tf->miss_config = OFPUTIL_TABLE_MISS_DEFAULT; | |
373 | if (oh->version >= OFP14_VERSION) { | |
374 | uint32_t caps = ntohl(otf->capabilities); | |
375 | tf->supports_eviction = (caps & OFPTC14_EVICTION) != 0; | |
376 | tf->supports_vacancy_events = (caps & OFPTC14_VACANCY_EVENTS) != 0; | |
377 | } else { | |
378 | tf->supports_eviction = -1; | |
379 | tf->supports_vacancy_events = -1; | |
380 | } | |
381 | tf->max_entries = ntohl(otf->max_entries); | |
382 | ||
383 | struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len), | |
384 | len); | |
385 | ofpbuf_pull(&properties, sizeof *otf); | |
386 | while (properties.size > 0) { | |
387 | struct ofpbuf payload; | |
388 | enum ofperr error; | |
389 | uint64_t type; | |
390 | ||
391 | error = pull_table_feature_property(&properties, &payload, &type); | |
392 | if (error) { | |
393 | return error; | |
394 | } | |
395 | ||
396 | switch ((enum ofp13_table_feature_prop_type) type) { | |
397 | case OFPTFPT13_INSTRUCTIONS: | |
398 | error = parse_instruction_ids(&payload, loose, | |
399 | &tf->nonmiss.instructions); | |
400 | break; | |
401 | case OFPTFPT13_INSTRUCTIONS_MISS: | |
402 | error = parse_instruction_ids(&payload, loose, | |
403 | &tf->miss.instructions); | |
404 | break; | |
405 | ||
406 | case OFPTFPT13_NEXT_TABLES: | |
407 | error = parse_table_features_next_table(&payload, | |
408 | tf->nonmiss.next); | |
409 | break; | |
410 | case OFPTFPT13_NEXT_TABLES_MISS: | |
411 | error = parse_table_features_next_table(&payload, tf->miss.next); | |
412 | break; | |
413 | ||
414 | case OFPTFPT13_WRITE_ACTIONS: | |
415 | error = parse_action_bitmap(&payload, oh->version, | |
416 | &tf->nonmiss.write.ofpacts); | |
417 | break; | |
418 | case OFPTFPT13_WRITE_ACTIONS_MISS: | |
419 | error = parse_action_bitmap(&payload, oh->version, | |
420 | &tf->miss.write.ofpacts); | |
421 | break; | |
422 | ||
423 | case OFPTFPT13_APPLY_ACTIONS: | |
424 | error = parse_action_bitmap(&payload, oh->version, | |
425 | &tf->nonmiss.apply.ofpacts); | |
426 | break; | |
427 | case OFPTFPT13_APPLY_ACTIONS_MISS: | |
428 | error = parse_action_bitmap(&payload, oh->version, | |
429 | &tf->miss.apply.ofpacts); | |
430 | break; | |
431 | ||
432 | case OFPTFPT13_MATCH: | |
433 | error = parse_oxms(&payload, loose, &tf->match, &tf->mask); | |
434 | break; | |
435 | case OFPTFPT13_WILDCARDS: | |
436 | error = parse_oxms(&payload, loose, &tf->wildcard, NULL); | |
437 | break; | |
438 | ||
439 | case OFPTFPT13_WRITE_SETFIELD: | |
440 | error = parse_oxms(&payload, loose, | |
441 | &tf->nonmiss.write.set_fields, NULL); | |
442 | break; | |
443 | case OFPTFPT13_WRITE_SETFIELD_MISS: | |
444 | error = parse_oxms(&payload, loose, | |
445 | &tf->miss.write.set_fields, NULL); | |
446 | break; | |
447 | case OFPTFPT13_APPLY_SETFIELD: | |
448 | error = parse_oxms(&payload, loose, | |
449 | &tf->nonmiss.apply.set_fields, NULL); | |
450 | break; | |
451 | case OFPTFPT13_APPLY_SETFIELD_MISS: | |
452 | error = parse_oxms(&payload, loose, | |
453 | &tf->miss.apply.set_fields, NULL); | |
454 | break; | |
455 | ||
456 | case OFPTFPT13_EXPERIMENTER: | |
457 | case OFPTFPT13_EXPERIMENTER_MISS: | |
458 | default: | |
459 | error = OFPPROP_UNKNOWN(loose, "table features", type); | |
460 | break; | |
461 | } | |
462 | if (error) { | |
463 | return error; | |
464 | } | |
465 | } | |
466 | ||
467 | /* Fix inconsistencies: | |
468 | * | |
469 | * - Turn on 'match' bits that are set in 'mask', because maskable | |
470 | * fields are matchable. | |
471 | * | |
472 | * - Turn on 'wildcard' bits that are set in 'mask', because a field | |
473 | * that is arbitrarily maskable can be wildcarded entirely. | |
474 | * | |
475 | * - Turn off 'wildcard' bits that are not in 'match', because a field | |
476 | * must be matchable for it to be meaningfully wildcarded. */ | |
477 | bitmap_or(tf->match.bm, tf->mask.bm, MFF_N_IDS); | |
478 | bitmap_or(tf->wildcard.bm, tf->mask.bm, MFF_N_IDS); | |
479 | bitmap_and(tf->wildcard.bm, tf->match.bm, MFF_N_IDS); | |
480 | ||
481 | return 0; | |
482 | } | |
483 | ||
484 | /* Encodes and returns a request to obtain the table features of a switch. | |
485 | * The message is encoded for OpenFlow version 'ofp_version'. */ | |
486 | struct ofpbuf * | |
487 | ofputil_encode_table_features_request(enum ofp_version ofp_version) | |
488 | { | |
489 | struct ofpbuf *request = NULL; | |
490 | ||
491 | switch (ofp_version) { | |
492 | case OFP10_VERSION: | |
493 | case OFP11_VERSION: | |
494 | case OFP12_VERSION: | |
495 | ovs_fatal(0, "dump-table-features needs OpenFlow 1.3 or later " | |
496 | "(\'-O OpenFlow13\')"); | |
497 | case OFP13_VERSION: | |
498 | case OFP14_VERSION: | |
499 | case OFP15_VERSION: | |
500 | case OFP16_VERSION: | |
501 | request = ofpraw_alloc(OFPRAW_OFPST13_TABLE_FEATURES_REQUEST, | |
502 | ofp_version, 0); | |
503 | break; | |
504 | default: | |
505 | OVS_NOT_REACHED(); | |
506 | } | |
507 | ||
508 | return request; | |
509 | } | |
510 | ||
511 | static void | |
512 | put_fields_property(struct ofpbuf *reply, | |
513 | const struct mf_bitmap *fields, | |
514 | const struct mf_bitmap *masks, | |
515 | enum ofp13_table_feature_prop_type property, | |
516 | enum ofp_version version) | |
517 | { | |
518 | size_t start_ofs; | |
519 | int field; | |
520 | ||
521 | start_ofs = ofpprop_start(reply, property); | |
522 | BITMAP_FOR_EACH_1 (field, MFF_N_IDS, fields->bm) { | |
523 | nx_put_header(reply, field, version, | |
524 | masks && bitmap_is_set(masks->bm, field)); | |
525 | } | |
526 | ofpprop_end(reply, start_ofs); | |
527 | } | |
528 | ||
529 | static void | |
530 | put_table_action_features(struct ofpbuf *reply, | |
531 | const struct ofputil_table_action_features *taf, | |
532 | enum ofp13_table_feature_prop_type actions_type, | |
533 | enum ofp13_table_feature_prop_type set_fields_type, | |
534 | int miss_offset, enum ofp_version version) | |
535 | { | |
536 | ofpprop_put_bitmap(reply, actions_type + miss_offset, | |
537 | ntohl(ofpact_bitmap_to_openflow(taf->ofpacts, | |
538 | version))); | |
539 | put_fields_property(reply, &taf->set_fields, NULL, | |
540 | set_fields_type + miss_offset, version); | |
541 | } | |
542 | ||
543 | static void | |
544 | put_table_instruction_features( | |
545 | struct ofpbuf *reply, const struct ofputil_table_instruction_features *tif, | |
546 | int miss_offset, enum ofp_version version) | |
547 | { | |
548 | size_t start_ofs; | |
549 | uint8_t table_id; | |
550 | ||
551 | ofpprop_put_bitmap(reply, OFPTFPT13_INSTRUCTIONS + miss_offset, | |
552 | ntohl(ovsinst_bitmap_to_openflow(tif->instructions, | |
553 | version))); | |
554 | ||
555 | start_ofs = ofpprop_start(reply, OFPTFPT13_NEXT_TABLES + miss_offset); | |
556 | BITMAP_FOR_EACH_1 (table_id, 255, tif->next) { | |
557 | ofpbuf_put(reply, &table_id, 1); | |
558 | } | |
559 | ofpprop_end(reply, start_ofs); | |
560 | ||
561 | put_table_action_features(reply, &tif->write, | |
562 | OFPTFPT13_WRITE_ACTIONS, | |
563 | OFPTFPT13_WRITE_SETFIELD, miss_offset, version); | |
564 | put_table_action_features(reply, &tif->apply, | |
565 | OFPTFPT13_APPLY_ACTIONS, | |
566 | OFPTFPT13_APPLY_SETFIELD, miss_offset, version); | |
567 | } | |
568 | ||
569 | void | |
570 | ofputil_append_table_features_reply(const struct ofputil_table_features *tf, | |
571 | struct ovs_list *replies) | |
572 | { | |
573 | struct ofpbuf *reply = ofpbuf_from_list(ovs_list_back(replies)); | |
574 | enum ofp_version version = ofpmp_version(replies); | |
575 | size_t start_ofs = reply->size; | |
576 | struct ofp13_table_features *otf; | |
577 | ||
578 | otf = ofpbuf_put_zeros(reply, sizeof *otf); | |
579 | otf->table_id = tf->table_id; | |
580 | ovs_strlcpy_arrays(otf->name, tf->name); | |
581 | otf->metadata_match = tf->metadata_match; | |
582 | otf->metadata_write = tf->metadata_write; | |
583 | if (version >= OFP14_VERSION) { | |
584 | if (tf->supports_eviction) { | |
585 | otf->capabilities |= htonl(OFPTC14_EVICTION); | |
586 | } | |
587 | if (tf->supports_vacancy_events) { | |
588 | otf->capabilities |= htonl(OFPTC14_VACANCY_EVENTS); | |
589 | } | |
590 | } | |
591 | otf->max_entries = htonl(tf->max_entries); | |
592 | ||
593 | put_table_instruction_features(reply, &tf->nonmiss, 0, version); | |
594 | put_table_instruction_features(reply, &tf->miss, 1, version); | |
595 | ||
596 | put_fields_property(reply, &tf->match, &tf->mask, | |
597 | OFPTFPT13_MATCH, version); | |
598 | put_fields_property(reply, &tf->wildcard, NULL, | |
599 | OFPTFPT13_WILDCARDS, version); | |
600 | ||
601 | otf = ofpbuf_at_assert(reply, start_ofs, sizeof *otf); | |
602 | otf->length = htons(reply->size - start_ofs); | |
603 | ofpmp_postappend(replies, start_ofs); | |
604 | } | |
605 | ||
606 | static enum ofperr | |
607 | parse_table_desc_vacancy_property(struct ofpbuf *property, | |
608 | struct ofputil_table_desc *td) | |
609 | { | |
610 | struct ofp14_table_mod_prop_vacancy *otv = property->data; | |
611 | ||
612 | if (property->size != sizeof *otv) { | |
613 | return OFPERR_OFPBPC_BAD_LEN; | |
614 | } | |
615 | ||
616 | td->table_vacancy.vacancy_down = otv->vacancy_down; | |
617 | td->table_vacancy.vacancy_up = otv->vacancy_up; | |
618 | td->table_vacancy.vacancy = otv->vacancy; | |
619 | return 0; | |
620 | } | |
621 | ||
622 | /* Decodes the next OpenFlow "table desc" message (of possibly several) from | |
623 | * 'msg' into an abstract form in '*td'. Returns 0 if successful, EOF if the | |
624 | * last "table desc" in 'msg' was already decoded, otherwise an OFPERR_* | |
625 | * value. */ | |
626 | int | |
627 | ofputil_decode_table_desc(struct ofpbuf *msg, | |
628 | struct ofputil_table_desc *td, | |
629 | enum ofp_version version) | |
630 | { | |
631 | memset(td, 0, sizeof *td); | |
632 | ||
633 | if (!msg->header) { | |
634 | ofpraw_pull_assert(msg); | |
635 | } | |
636 | ||
637 | if (!msg->size) { | |
638 | return EOF; | |
639 | } | |
640 | ||
641 | struct ofp14_table_desc *otd = ofpbuf_try_pull(msg, sizeof *otd); | |
642 | if (!otd) { | |
643 | VLOG_WARN_RL(&rl, "OFP14_TABLE_DESC reply has %"PRIu32" " | |
644 | "leftover bytes at end", msg->size); | |
645 | return OFPERR_OFPBRC_BAD_LEN; | |
646 | } | |
647 | ||
648 | td->table_id = otd->table_id; | |
649 | size_t length = ntohs(otd->length); | |
650 | if (length < sizeof *otd || length - sizeof *otd > msg->size) { | |
651 | VLOG_WARN_RL(&rl, "OFP14_TABLE_DESC reply claims invalid " | |
652 | "length %"PRIuSIZE, length); | |
653 | return OFPERR_OFPBRC_BAD_LEN; | |
654 | } | |
655 | length -= sizeof *otd; | |
656 | ||
657 | td->eviction = ofputil_decode_table_eviction(otd->config, version); | |
658 | td->vacancy = ofputil_decode_table_vacancy(otd->config, version); | |
659 | td->eviction_flags = UINT32_MAX; | |
660 | ||
661 | struct ofpbuf properties = ofpbuf_const_initializer( | |
662 | ofpbuf_pull(msg, length), length); | |
663 | while (properties.size > 0) { | |
664 | struct ofpbuf payload; | |
665 | enum ofperr error; | |
666 | uint64_t type; | |
667 | ||
668 | error = ofpprop_pull(&properties, &payload, &type); | |
669 | if (error) { | |
670 | return error; | |
671 | } | |
672 | ||
673 | switch (type) { | |
674 | case OFPTMPT14_EVICTION: | |
675 | error = ofpprop_parse_u32(&payload, &td->eviction_flags); | |
676 | break; | |
677 | ||
678 | case OFPTMPT14_VACANCY: | |
679 | error = parse_table_desc_vacancy_property(&payload, td); | |
680 | break; | |
681 | ||
682 | default: | |
683 | error = OFPPROP_UNKNOWN(true, "table_desc", type); | |
684 | break; | |
685 | } | |
686 | ||
687 | if (error) { | |
688 | return error; | |
689 | } | |
690 | } | |
691 | ||
692 | return 0; | |
693 | } | |
694 | ||
695 | /* Encodes and returns a request to obtain description of tables of a switch. | |
696 | * The message is encoded for OpenFlow version 'ofp_version'. */ | |
697 | struct ofpbuf * | |
698 | ofputil_encode_table_desc_request(enum ofp_version ofp_version) | |
699 | { | |
700 | struct ofpbuf *request = NULL; | |
701 | ||
702 | if (ofp_version >= OFP14_VERSION) { | |
703 | request = ofpraw_alloc(OFPRAW_OFPST14_TABLE_DESC_REQUEST, | |
704 | ofp_version, 0); | |
705 | } else { | |
706 | ovs_fatal(0, "dump-table-desc needs OpenFlow 1.4 or later " | |
707 | "(\'-O OpenFlow14\')"); | |
708 | } | |
709 | ||
710 | return request; | |
711 | } | |
712 | ||
713 | /* Function to append Table desc information in a reply list. */ | |
714 | void | |
715 | ofputil_append_table_desc_reply(const struct ofputil_table_desc *td, | |
716 | struct ovs_list *replies, | |
717 | enum ofp_version version) | |
718 | { | |
719 | struct ofpbuf *reply = ofpbuf_from_list(ovs_list_back(replies)); | |
720 | size_t start_otd; | |
721 | struct ofp14_table_desc *otd; | |
722 | ||
723 | start_otd = reply->size; | |
724 | ofpbuf_put_zeros(reply, sizeof *otd); | |
725 | if (td->eviction_flags != UINT32_MAX) { | |
726 | ofpprop_put_u32(reply, OFPTMPT14_EVICTION, td->eviction_flags); | |
727 | } | |
728 | if (td->vacancy == OFPUTIL_TABLE_VACANCY_ON) { | |
729 | struct ofp14_table_mod_prop_vacancy *otv; | |
730 | ||
731 | otv = ofpprop_put_zeros(reply, OFPTMPT14_VACANCY, sizeof *otv); | |
732 | otv->vacancy_down = td->table_vacancy.vacancy_down; | |
733 | otv->vacancy_up = td->table_vacancy.vacancy_up; | |
734 | otv->vacancy = td->table_vacancy.vacancy; | |
735 | } | |
736 | ||
737 | otd = ofpbuf_at_assert(reply, start_otd, sizeof *otd); | |
738 | otd->length = htons(reply->size - start_otd); | |
739 | otd->table_id = td->table_id; | |
740 | otd->config = ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT, | |
741 | td->eviction, td->vacancy, | |
742 | version); | |
743 | ofpmp_postappend(replies, start_otd); | |
744 | } | |
745 | ||
dfc77282 BP |
746 | static const char * |
747 | ofputil_eviction_flag_to_string(uint32_t bit) | |
748 | { | |
749 | enum ofp14_table_mod_prop_eviction_flag eviction_flag = bit; | |
750 | ||
751 | switch (eviction_flag) { | |
752 | case OFPTMPEF14_OTHER: return "OTHER"; | |
753 | case OFPTMPEF14_IMPORTANCE: return "IMPORTANCE"; | |
754 | case OFPTMPEF14_LIFETIME: return "LIFETIME"; | |
755 | } | |
756 | ||
757 | return NULL; | |
758 | } | |
759 | ||
760 | /* Appends to 'string' a description of the bitmap of OFPTMPEF14_* values in | |
761 | * 'eviction_flags'. */ | |
762 | static void | |
763 | ofputil_put_eviction_flags(struct ds *string, uint32_t eviction_flags) | |
764 | { | |
765 | if (eviction_flags != UINT32_MAX) { | |
766 | ofp_print_bit_names(string, eviction_flags, | |
767 | ofputil_eviction_flag_to_string, '|'); | |
768 | } else { | |
769 | ds_put_cstr(string, "(default)"); | |
770 | } | |
771 | } | |
772 | ||
773 | void | |
774 | ofputil_table_desc_format(struct ds *s, const struct ofputil_table_desc *td, | |
775 | const struct ofputil_table_map *table_map) | |
776 | { | |
777 | ds_put_format(s, "\n table "); | |
778 | ofputil_format_table(td->table_id, table_map, s); | |
779 | ds_put_cstr(s, ":\n"); | |
780 | ds_put_format(s, " eviction=%s eviction_flags=", | |
781 | ofputil_table_eviction_to_string(td->eviction)); | |
782 | ofputil_put_eviction_flags(s, td->eviction_flags); | |
783 | ds_put_char(s, '\n'); | |
784 | ds_put_format(s, " vacancy=%s", | |
785 | ofputil_table_vacancy_to_string(td->vacancy)); | |
786 | if (td->vacancy == OFPUTIL_TABLE_VACANCY_ON) { | |
787 | ds_put_format(s, " vacancy_down=%"PRIu8"%%", | |
788 | td->table_vacancy.vacancy_down); | |
789 | ds_put_format(s, " vacancy_up=%"PRIu8"%%", | |
790 | td->table_vacancy.vacancy_up); | |
791 | ds_put_format(s, " vacancy=%"PRIu8"%%", | |
792 | td->table_vacancy.vacancy); | |
793 | } | |
794 | ds_put_char(s, '\n'); | |
795 | } | |
796 | ||
0d71302e BP |
797 | /* This function parses Vacancy property, and decodes the |
798 | * ofp14_table_mod_prop_vacancy in ofputil_table_mod. | |
799 | * Returns OFPERR_OFPBPC_BAD_VALUE error code when vacancy_down is | |
800 | * greater than vacancy_up and also when current vacancy has non-zero | |
801 | * value. Returns 0 on success. */ | |
802 | static enum ofperr | |
803 | parse_table_mod_vacancy_property(struct ofpbuf *property, | |
804 | struct ofputil_table_mod *tm) | |
805 | { | |
806 | struct ofp14_table_mod_prop_vacancy *otv = property->data; | |
807 | ||
808 | if (property->size != sizeof *otv) { | |
809 | return OFPERR_OFPBPC_BAD_LEN; | |
810 | } | |
811 | tm->table_vacancy.vacancy_down = otv->vacancy_down; | |
812 | tm->table_vacancy.vacancy_up = otv->vacancy_up; | |
813 | if (tm->table_vacancy.vacancy_down > tm->table_vacancy.vacancy_up) { | |
814 | OFPPROP_LOG(&rl, false, | |
815 | "Value of vacancy_down is greater than vacancy_up"); | |
816 | return OFPERR_OFPBPC_BAD_VALUE; | |
817 | } | |
818 | if (tm->table_vacancy.vacancy_down > 100 || | |
819 | tm->table_vacancy.vacancy_up > 100) { | |
820 | OFPPROP_LOG(&rl, false, "Vacancy threshold percentage " | |
821 | "should not be greater than 100"); | |
822 | return OFPERR_OFPBPC_BAD_VALUE; | |
823 | } | |
824 | tm->table_vacancy.vacancy = otv->vacancy; | |
825 | if (tm->table_vacancy.vacancy) { | |
826 | OFPPROP_LOG(&rl, false, | |
827 | "Vacancy value should be zero for table-mod messages"); | |
828 | return OFPERR_OFPBPC_BAD_VALUE; | |
829 | } | |
830 | return 0; | |
831 | } | |
832 | ||
833 | /* Given 'config', taken from an OpenFlow 'version' message that specifies | |
834 | * table configuration (a table mod, table stats, or table features message), | |
835 | * returns the table vacancy configuration that it specifies. | |
836 | * | |
837 | * Only OpenFlow 1.4 and later specify table vacancy configuration this way, | |
838 | * so for other 'version' this function always returns | |
839 | * OFPUTIL_TABLE_VACANCY_DEFAULT. */ | |
840 | static enum ofputil_table_vacancy | |
841 | ofputil_decode_table_vacancy(ovs_be32 config, enum ofp_version version) | |
842 | { | |
843 | return (version < OFP14_VERSION ? OFPUTIL_TABLE_VACANCY_DEFAULT | |
844 | : config & htonl(OFPTC14_VACANCY_EVENTS) ? OFPUTIL_TABLE_VACANCY_ON | |
845 | : OFPUTIL_TABLE_VACANCY_OFF); | |
846 | } | |
847 | ||
848 | /* Given 'config', taken from an OpenFlow 'version' message that specifies | |
849 | * table configuration (a table mod, table stats, or table features message), | |
850 | * returns the table eviction configuration that it specifies. | |
851 | * | |
852 | * Only OpenFlow 1.4 and later specify table eviction configuration this way, | |
853 | * so for other 'version' values this function always returns | |
854 | * OFPUTIL_TABLE_EVICTION_DEFAULT. */ | |
855 | static enum ofputil_table_eviction | |
856 | ofputil_decode_table_eviction(ovs_be32 config, enum ofp_version version) | |
857 | { | |
858 | return (version < OFP14_VERSION ? OFPUTIL_TABLE_EVICTION_DEFAULT | |
859 | : config & htonl(OFPTC14_EVICTION) ? OFPUTIL_TABLE_EVICTION_ON | |
860 | : OFPUTIL_TABLE_EVICTION_OFF); | |
861 | } | |
862 | ||
863 | /* Returns a bitmap of OFPTC* values suitable for 'config' fields in various | |
864 | * OpenFlow messages of the given 'version', based on the provided 'miss' and | |
865 | * 'eviction' values. */ | |
866 | static ovs_be32 | |
867 | ofputil_encode_table_config(enum ofputil_table_miss miss, | |
868 | enum ofputil_table_eviction eviction, | |
869 | enum ofputil_table_vacancy vacancy, | |
870 | enum ofp_version version) | |
871 | { | |
872 | uint32_t config = 0; | |
873 | /* Search for "OFPTC_* Table Configuration" in the documentation for more | |
874 | * information on the crazy evolution of this field. */ | |
875 | switch (version) { | |
876 | case OFP10_VERSION: | |
877 | /* OpenFlow 1.0 didn't have such a field, any value ought to do. */ | |
878 | return htonl(0); | |
879 | ||
880 | case OFP11_VERSION: | |
881 | case OFP12_VERSION: | |
882 | /* OpenFlow 1.1 and 1.2 define only OFPTC11_TABLE_MISS_*. */ | |
883 | switch (miss) { | |
884 | case OFPUTIL_TABLE_MISS_DEFAULT: | |
885 | /* Really this shouldn't be used for encoding (the caller should | |
886 | * provide a specific value) but I can't imagine that defaulting to | |
887 | * the fall-through case here will hurt. */ | |
888 | case OFPUTIL_TABLE_MISS_CONTROLLER: | |
889 | default: | |
890 | return htonl(OFPTC11_TABLE_MISS_CONTROLLER); | |
891 | case OFPUTIL_TABLE_MISS_CONTINUE: | |
892 | return htonl(OFPTC11_TABLE_MISS_CONTINUE); | |
893 | case OFPUTIL_TABLE_MISS_DROP: | |
894 | return htonl(OFPTC11_TABLE_MISS_DROP); | |
895 | } | |
896 | OVS_NOT_REACHED(); | |
897 | ||
898 | case OFP13_VERSION: | |
899 | /* OpenFlow 1.3 removed OFPTC11_TABLE_MISS_* and didn't define any new | |
900 | * flags, so this is correct. */ | |
901 | return htonl(0); | |
902 | ||
903 | case OFP14_VERSION: | |
904 | case OFP15_VERSION: | |
905 | case OFP16_VERSION: | |
906 | /* OpenFlow 1.4 introduced OFPTC14_EVICTION and | |
907 | * OFPTC14_VACANCY_EVENTS. */ | |
908 | if (eviction == OFPUTIL_TABLE_EVICTION_ON) { | |
909 | config |= OFPTC14_EVICTION; | |
910 | } | |
911 | if (vacancy == OFPUTIL_TABLE_VACANCY_ON) { | |
912 | config |= OFPTC14_VACANCY_EVENTS; | |
913 | } | |
914 | return htonl(config); | |
915 | } | |
916 | ||
917 | OVS_NOT_REACHED(); | |
918 | } | |
919 | ||
920 | /* Given 'config', taken from an OpenFlow 'version' message that specifies | |
921 | * table configuration (a table mod, table stats, or table features message), | |
922 | * returns the table miss configuration that it specifies. | |
923 | * | |
924 | * Only OpenFlow 1.1 and 1.2 specify table miss configurations this way, so for | |
925 | * other 'version' values this function always returns | |
926 | * OFPUTIL_TABLE_MISS_DEFAULT. */ | |
927 | static enum ofputil_table_miss | |
928 | ofputil_decode_table_miss(ovs_be32 config_, enum ofp_version version) | |
929 | { | |
930 | uint32_t config = ntohl(config_); | |
931 | ||
932 | if (version == OFP11_VERSION || version == OFP12_VERSION) { | |
933 | switch (config & OFPTC11_TABLE_MISS_MASK) { | |
934 | case OFPTC11_TABLE_MISS_CONTROLLER: | |
935 | return OFPUTIL_TABLE_MISS_CONTROLLER; | |
936 | ||
937 | case OFPTC11_TABLE_MISS_CONTINUE: | |
938 | return OFPUTIL_TABLE_MISS_CONTINUE; | |
939 | ||
940 | case OFPTC11_TABLE_MISS_DROP: | |
941 | return OFPUTIL_TABLE_MISS_DROP; | |
942 | ||
943 | default: | |
944 | VLOG_WARN_RL(&rl, "bad table miss config %d", config); | |
945 | return OFPUTIL_TABLE_MISS_CONTROLLER; | |
946 | } | |
947 | } else { | |
948 | return OFPUTIL_TABLE_MISS_DEFAULT; | |
949 | } | |
950 | } | |
951 | ||
952 | /* Decodes the OpenFlow "table mod" message in '*oh' into an abstract form in | |
953 | * '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */ | |
954 | enum ofperr | |
955 | ofputil_decode_table_mod(const struct ofp_header *oh, | |
956 | struct ofputil_table_mod *pm) | |
957 | { | |
958 | memset(pm, 0, sizeof *pm); | |
959 | pm->miss = OFPUTIL_TABLE_MISS_DEFAULT; | |
960 | pm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT; | |
961 | pm->eviction_flags = UINT32_MAX; | |
962 | pm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT; | |
963 | ||
964 | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); | |
965 | enum ofpraw raw = ofpraw_pull_assert(&b); | |
966 | if (raw == OFPRAW_OFPT11_TABLE_MOD) { | |
967 | const struct ofp11_table_mod *otm = b.data; | |
968 | ||
969 | pm->table_id = otm->table_id; | |
970 | pm->miss = ofputil_decode_table_miss(otm->config, oh->version); | |
971 | } else if (raw == OFPRAW_OFPT14_TABLE_MOD) { | |
972 | const struct ofp14_table_mod *otm = ofpbuf_pull(&b, sizeof *otm); | |
973 | ||
974 | pm->table_id = otm->table_id; | |
975 | pm->miss = ofputil_decode_table_miss(otm->config, oh->version); | |
976 | pm->eviction = ofputil_decode_table_eviction(otm->config, oh->version); | |
977 | pm->vacancy = ofputil_decode_table_vacancy(otm->config, oh->version); | |
978 | while (b.size > 0) { | |
979 | struct ofpbuf property; | |
980 | enum ofperr error; | |
981 | uint64_t type; | |
982 | ||
983 | error = ofpprop_pull(&b, &property, &type); | |
984 | if (error) { | |
985 | return error; | |
986 | } | |
987 | ||
988 | switch (type) { | |
989 | case OFPTMPT14_EVICTION: | |
990 | error = ofpprop_parse_u32(&property, &pm->eviction); | |
991 | break; | |
992 | ||
993 | case OFPTMPT14_VACANCY: | |
994 | error = parse_table_mod_vacancy_property(&property, pm); | |
995 | break; | |
996 | ||
997 | default: | |
998 | error = OFPERR_OFPBRC_BAD_TYPE; | |
999 | break; | |
1000 | } | |
1001 | ||
1002 | if (error) { | |
1003 | return error; | |
1004 | } | |
1005 | } | |
1006 | } else { | |
1007 | return OFPERR_OFPBRC_BAD_TYPE; | |
1008 | } | |
1009 | ||
1010 | return 0; | |
1011 | } | |
1012 | ||
1013 | /* Converts the abstract form of a "table mod" message in '*tm' into an | |
1014 | * OpenFlow message suitable for 'protocol', and returns that encoded form in a | |
1015 | * buffer owned by the caller. */ | |
1016 | struct ofpbuf * | |
1017 | ofputil_encode_table_mod(const struct ofputil_table_mod *tm, | |
1018 | enum ofputil_protocol protocol) | |
1019 | { | |
1020 | enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol); | |
1021 | struct ofpbuf *b; | |
1022 | ||
1023 | switch (ofp_version) { | |
1024 | case OFP10_VERSION: { | |
1025 | ovs_fatal(0, "table mod needs OpenFlow 1.1 or later " | |
1026 | "(\'-O OpenFlow11\')"); | |
1027 | break; | |
1028 | } | |
1029 | case OFP11_VERSION: | |
1030 | case OFP12_VERSION: | |
1031 | case OFP13_VERSION: { | |
1032 | struct ofp11_table_mod *otm; | |
1033 | ||
1034 | b = ofpraw_alloc(OFPRAW_OFPT11_TABLE_MOD, ofp_version, 0); | |
1035 | otm = ofpbuf_put_zeros(b, sizeof *otm); | |
1036 | otm->table_id = tm->table_id; | |
1037 | otm->config = ofputil_encode_table_config(tm->miss, tm->eviction, | |
1038 | tm->vacancy, ofp_version); | |
1039 | break; | |
1040 | } | |
1041 | case OFP14_VERSION: | |
1042 | case OFP15_VERSION: | |
1043 | case OFP16_VERSION: { | |
1044 | struct ofp14_table_mod *otm; | |
1045 | ||
1046 | b = ofpraw_alloc(OFPRAW_OFPT14_TABLE_MOD, ofp_version, 0); | |
1047 | otm = ofpbuf_put_zeros(b, sizeof *otm); | |
1048 | otm->table_id = tm->table_id; | |
1049 | otm->config = ofputil_encode_table_config(tm->miss, tm->eviction, | |
1050 | tm->vacancy, ofp_version); | |
1051 | ||
1052 | if (tm->eviction_flags != UINT32_MAX) { | |
1053 | ofpprop_put_u32(b, OFPTMPT14_EVICTION, tm->eviction_flags); | |
1054 | } | |
1055 | if (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON) { | |
1056 | struct ofp14_table_mod_prop_vacancy *otv; | |
1057 | ||
1058 | otv = ofpprop_put_zeros(b, OFPTMPT14_VACANCY, sizeof *otv); | |
1059 | otv->vacancy_down = tm->table_vacancy.vacancy_down; | |
1060 | otv->vacancy_up = tm->table_vacancy.vacancy_up; | |
1061 | } | |
1062 | break; | |
1063 | } | |
1064 | default: | |
1065 | OVS_NOT_REACHED(); | |
1066 | } | |
1067 | ||
1068 | return b; | |
1069 | } | |
1070 | ||
dfc77282 BP |
1071 | void |
1072 | ofputil_table_mod_format(struct ds *s, const struct ofputil_table_mod *tm, | |
1073 | const struct ofputil_table_map *table_map) | |
1074 | { | |
1075 | if (tm->table_id == 0xff) { | |
1076 | ds_put_cstr(s, " table_id: ALL_TABLES"); | |
1077 | } else { | |
1078 | ds_put_format(s, " table_id="); | |
1079 | ofputil_format_table(tm->table_id, table_map, s); | |
1080 | } | |
1081 | ||
1082 | if (tm->miss != OFPUTIL_TABLE_MISS_DEFAULT) { | |
1083 | ds_put_format(s, ", flow_miss_config=%s", | |
1084 | ofputil_table_miss_to_string(tm->miss)); | |
1085 | } | |
1086 | if (tm->eviction != OFPUTIL_TABLE_EVICTION_DEFAULT) { | |
1087 | ds_put_format(s, ", eviction=%s", | |
1088 | ofputil_table_eviction_to_string(tm->eviction)); | |
1089 | } | |
1090 | if (tm->eviction_flags != UINT32_MAX) { | |
1091 | ds_put_cstr(s, "eviction_flags="); | |
1092 | ofputil_put_eviction_flags(s, tm->eviction_flags); | |
1093 | } | |
1094 | if (tm->vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) { | |
1095 | ds_put_format(s, ", vacancy=%s", | |
1096 | ofputil_table_vacancy_to_string(tm->vacancy)); | |
1097 | if (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON) { | |
1098 | ds_put_format(s, " vacancy:%"PRIu8"" | |
1099 | ",%"PRIu8"", tm->table_vacancy.vacancy_down, | |
1100 | tm->table_vacancy.vacancy_up); | |
1101 | } | |
1102 | } | |
1103 | } | |
1104 | ||
0d71302e BP |
1105 | /* Convert 'setting' (as described for the "mod-table" command |
1106 | * in ovs-ofctl man page) into 'tm->table_vacancy->vacancy_up' and | |
1107 | * 'tm->table_vacancy->vacancy_down' threshold values. | |
1108 | * For the two threshold values, value of vacancy_up is always greater | |
1109 | * than value of vacancy_down. | |
1110 | * | |
1111 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
1112 | * error. The caller is responsible for freeing the returned string. */ | |
1113 | static char * OVS_WARN_UNUSED_RESULT | |
1114 | parse_ofp_table_vacancy(struct ofputil_table_mod *tm, const char *setting) | |
1115 | { | |
1116 | char *save_ptr = NULL; | |
1117 | char *vac_up, *vac_down; | |
1118 | char *value = xstrdup(setting); | |
1119 | char *ret_msg; | |
1120 | int vacancy_up, vacancy_down; | |
1121 | ||
1122 | strtok_r(value, ":", &save_ptr); | |
1123 | vac_down = strtok_r(NULL, ",", &save_ptr); | |
1124 | if (!vac_down) { | |
1125 | ret_msg = xasprintf("Vacancy down value missing"); | |
1126 | goto exit; | |
1127 | } | |
1128 | if (!str_to_int(vac_down, 0, &vacancy_down) || | |
1129 | vacancy_down < 0 || vacancy_down > 100) { | |
1130 | ret_msg = xasprintf("Invalid vacancy down value \"%s\"", vac_down); | |
1131 | goto exit; | |
1132 | } | |
1133 | vac_up = strtok_r(NULL, ",", &save_ptr); | |
1134 | if (!vac_up) { | |
1135 | ret_msg = xasprintf("Vacancy up value missing"); | |
1136 | goto exit; | |
1137 | } | |
1138 | if (!str_to_int(vac_up, 0, &vacancy_up) || | |
1139 | vacancy_up < 0 || vacancy_up > 100) { | |
1140 | ret_msg = xasprintf("Invalid vacancy up value \"%s\"", vac_up); | |
1141 | goto exit; | |
1142 | } | |
1143 | if (vacancy_down > vacancy_up) { | |
1144 | ret_msg = xasprintf("Invalid vacancy range, vacancy up should be " | |
1145 | "greater than vacancy down (%s)", | |
1146 | ofperr_to_string(OFPERR_OFPBPC_BAD_VALUE)); | |
1147 | goto exit; | |
1148 | } | |
1149 | ||
1150 | free(value); | |
1151 | tm->table_vacancy.vacancy_down = vacancy_down; | |
1152 | tm->table_vacancy.vacancy_up = vacancy_up; | |
1153 | return NULL; | |
1154 | ||
1155 | exit: | |
1156 | free(value); | |
1157 | return ret_msg; | |
1158 | } | |
1159 | ||
1160 | /* Convert 'table_id' and 'setting' (as described for the "mod-table" command | |
1161 | * in the ovs-ofctl man page) into 'tm' for sending a table_mod command to a | |
1162 | * switch. | |
1163 | * | |
1164 | * Stores a bitmap of the OpenFlow versions that are usable for 'tm' into | |
1165 | * '*usable_versions'. | |
1166 | * | |
1167 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
1168 | * error. The caller is responsible for freeing the returned string. */ | |
1169 | char * OVS_WARN_UNUSED_RESULT | |
1170 | parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id, | |
1171 | const char *setting, | |
1172 | const struct ofputil_table_map *table_map, | |
1173 | uint32_t *usable_versions) | |
1174 | { | |
1175 | *usable_versions = 0; | |
1176 | if (!strcasecmp(table_id, "all")) { | |
1177 | tm->table_id = OFPTT_ALL; | |
1178 | } else if (!ofputil_table_from_string(table_id, table_map, | |
1179 | &tm->table_id)) { | |
1180 | return xasprintf("unknown table \"%s\"", table_id); | |
1181 | } | |
1182 | ||
1183 | tm->miss = OFPUTIL_TABLE_MISS_DEFAULT; | |
1184 | tm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT; | |
1185 | tm->eviction_flags = UINT32_MAX; | |
1186 | tm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT; | |
1187 | tm->table_vacancy.vacancy_down = 0; | |
1188 | tm->table_vacancy.vacancy_up = 0; | |
1189 | tm->table_vacancy.vacancy = 0; | |
1190 | /* Only OpenFlow 1.1 and 1.2 can configure table-miss via table_mod. | |
1191 | * Only OpenFlow 1.4+ can configure eviction and vacancy events | |
1192 | * via table_mod. | |
1193 | */ | |
1194 | if (!strcmp(setting, "controller")) { | |
1195 | tm->miss = OFPUTIL_TABLE_MISS_CONTROLLER; | |
1196 | *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION); | |
1197 | } else if (!strcmp(setting, "continue")) { | |
1198 | tm->miss = OFPUTIL_TABLE_MISS_CONTINUE; | |
1199 | *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION); | |
1200 | } else if (!strcmp(setting, "drop")) { | |
1201 | tm->miss = OFPUTIL_TABLE_MISS_DROP; | |
1202 | *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION); | |
1203 | } else if (!strcmp(setting, "evict")) { | |
1204 | tm->eviction = OFPUTIL_TABLE_EVICTION_ON; | |
1205 | *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); | |
1206 | } else if (!strcmp(setting, "noevict")) { | |
1207 | tm->eviction = OFPUTIL_TABLE_EVICTION_OFF; | |
1208 | *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); | |
1209 | } else if (!strncmp(setting, "vacancy", strcspn(setting, ":"))) { | |
1210 | tm->vacancy = OFPUTIL_TABLE_VACANCY_ON; | |
1211 | *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); | |
1212 | char *error = parse_ofp_table_vacancy(tm, setting); | |
1213 | if (error) { | |
1214 | return error; | |
1215 | } | |
1216 | } else if (!strcmp(setting, "novacancy")) { | |
1217 | tm->vacancy = OFPUTIL_TABLE_VACANCY_OFF; | |
1218 | *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); | |
1219 | } else { | |
1220 | return xasprintf("invalid table_mod setting %s", setting); | |
1221 | } | |
1222 | ||
1223 | if (tm->table_id == 0xfe | |
1224 | && tm->miss == OFPUTIL_TABLE_MISS_CONTINUE) { | |
1225 | return xstrdup("last table's flow miss handling can not be continue"); | |
1226 | } | |
1227 | ||
1228 | return NULL; | |
1229 | } | |
dfc77282 BP |
1230 | |
1231 | static void | |
1232 | print_table_action_features(struct ds *s, | |
1233 | const struct ofputil_table_action_features *taf) | |
1234 | { | |
1235 | if (taf->ofpacts) { | |
1236 | ds_put_cstr(s, " actions: "); | |
1237 | ofpact_bitmap_format(taf->ofpacts, s); | |
1238 | ds_put_char(s, '\n'); | |
1239 | } | |
1240 | ||
1241 | if (!bitmap_is_all_zeros(taf->set_fields.bm, MFF_N_IDS)) { | |
1242 | int i; | |
1243 | ||
1244 | ds_put_cstr(s, " supported on Set-Field:"); | |
1245 | BITMAP_FOR_EACH_1 (i, MFF_N_IDS, taf->set_fields.bm) { | |
1246 | ds_put_format(s, " %s", mf_from_id(i)->name); | |
1247 | } | |
1248 | ds_put_char(s, '\n'); | |
1249 | } | |
1250 | } | |
1251 | ||
1252 | static bool | |
1253 | table_action_features_equal(const struct ofputil_table_action_features *a, | |
1254 | const struct ofputil_table_action_features *b) | |
1255 | { | |
1256 | return (a->ofpacts == b->ofpacts | |
1257 | && bitmap_equal(a->set_fields.bm, b->set_fields.bm, MFF_N_IDS)); | |
1258 | } | |
1259 | ||
1260 | static bool | |
1261 | table_action_features_empty(const struct ofputil_table_action_features *taf) | |
1262 | { | |
1263 | return !taf->ofpacts && bitmap_is_all_zeros(taf->set_fields.bm, MFF_N_IDS); | |
1264 | } | |
1265 | ||
1266 | static void | |
1267 | print_table_instruction_features( | |
1268 | struct ds *s, | |
1269 | const struct ofputil_table_instruction_features *tif, | |
1270 | const struct ofputil_table_instruction_features *prev_tif) | |
1271 | { | |
1272 | int start, end; | |
1273 | ||
1274 | if (!bitmap_is_all_zeros(tif->next, 255)) { | |
1275 | ds_put_cstr(s, " next tables: "); | |
1276 | for (start = bitmap_scan(tif->next, 1, 0, 255); start < 255; | |
1277 | start = bitmap_scan(tif->next, 1, end, 255)) { | |
1278 | end = bitmap_scan(tif->next, 0, start + 1, 255); | |
1279 | if (end == start + 1) { | |
1280 | ds_put_format(s, "%d,", start); | |
1281 | } else { | |
1282 | ds_put_format(s, "%d-%d,", start, end - 1); | |
1283 | } | |
1284 | } | |
1285 | ds_chomp(s, ','); | |
1286 | if (ds_last(s) == ' ') { | |
1287 | ds_put_cstr(s, "none"); | |
1288 | } | |
1289 | ds_put_char(s, '\n'); | |
1290 | } | |
1291 | ||
1292 | if (tif->instructions) { | |
1293 | if (prev_tif && tif->instructions == prev_tif->instructions) { | |
1294 | ds_put_cstr(s, " (same instructions)\n"); | |
1295 | } else { | |
1296 | ds_put_cstr(s, " instructions: "); | |
1297 | int i; | |
1298 | ||
1299 | for (i = 0; i < 32; i++) { | |
1300 | if (tif->instructions & (1u << i)) { | |
1301 | const char *name = ovs_instruction_name_from_type(i); | |
1302 | if (name) { | |
1303 | ds_put_cstr(s, name); | |
1304 | } else { | |
1305 | ds_put_format(s, "%d", i); | |
1306 | } | |
1307 | ds_put_char(s, ','); | |
1308 | } | |
1309 | } | |
1310 | ds_chomp(s, ','); | |
1311 | ds_put_char(s, '\n'); | |
1312 | } | |
1313 | } | |
1314 | ||
1315 | if (prev_tif | |
1316 | && table_action_features_equal(&tif->write, &prev_tif->write) | |
1317 | && table_action_features_equal(&tif->apply, &prev_tif->apply) | |
1318 | && !bitmap_is_all_zeros(tif->write.set_fields.bm, MFF_N_IDS)) { | |
1319 | ds_put_cstr(s, " (same actions)\n"); | |
1320 | } else if (!table_action_features_equal(&tif->write, &tif->apply)) { | |
1321 | ds_put_cstr(s, " Write-Actions features:\n"); | |
1322 | print_table_action_features(s, &tif->write); | |
1323 | ds_put_cstr(s, " Apply-Actions features:\n"); | |
1324 | print_table_action_features(s, &tif->apply); | |
1325 | } else if (tif->write.ofpacts | |
1326 | || !bitmap_is_all_zeros(tif->write.set_fields.bm, MFF_N_IDS)) { | |
1327 | ds_put_cstr(s, " Write-Actions and Apply-Actions features:\n"); | |
1328 | print_table_action_features(s, &tif->write); | |
1329 | } | |
1330 | } | |
1331 | ||
1332 | static bool | |
1333 | table_instruction_features_equal( | |
1334 | const struct ofputil_table_instruction_features *a, | |
1335 | const struct ofputil_table_instruction_features *b) | |
1336 | { | |
1337 | return (bitmap_equal(a->next, b->next, 255) | |
1338 | && a->instructions == b->instructions | |
1339 | && table_action_features_equal(&a->write, &b->write) | |
1340 | && table_action_features_equal(&a->apply, &b->apply)); | |
1341 | } | |
1342 | ||
1343 | static bool | |
1344 | table_instruction_features_empty( | |
1345 | const struct ofputil_table_instruction_features *tif) | |
1346 | { | |
1347 | return (bitmap_is_all_zeros(tif->next, 255) | |
1348 | && !tif->instructions | |
1349 | && table_action_features_empty(&tif->write) | |
1350 | && table_action_features_empty(&tif->apply)); | |
1351 | } | |
1352 | ||
1353 | static bool | |
1354 | table_features_equal(const struct ofputil_table_features *a, | |
1355 | const struct ofputil_table_features *b) | |
1356 | { | |
1357 | return (a->metadata_match == b->metadata_match | |
1358 | && a->metadata_write == b->metadata_write | |
1359 | && a->miss_config == b->miss_config | |
1360 | && a->supports_eviction == b->supports_eviction | |
1361 | && a->supports_vacancy_events == b->supports_vacancy_events | |
1362 | && a->max_entries == b->max_entries | |
1363 | && table_instruction_features_equal(&a->nonmiss, &b->nonmiss) | |
1364 | && table_instruction_features_equal(&a->miss, &b->miss) | |
1365 | && bitmap_equal(a->match.bm, b->match.bm, MFF_N_IDS)); | |
1366 | } | |
1367 | ||
1368 | static bool | |
1369 | table_features_empty(const struct ofputil_table_features *tf) | |
1370 | { | |
1371 | return (!tf->metadata_match | |
1372 | && !tf->metadata_write | |
1373 | && tf->miss_config == OFPUTIL_TABLE_MISS_DEFAULT | |
1374 | && tf->supports_eviction < 0 | |
1375 | && tf->supports_vacancy_events < 0 | |
1376 | && !tf->max_entries | |
1377 | && table_instruction_features_empty(&tf->nonmiss) | |
1378 | && table_instruction_features_empty(&tf->miss) | |
1379 | && bitmap_is_all_zeros(tf->match.bm, MFF_N_IDS)); | |
1380 | } | |
1381 | ||
1382 | static bool | |
1383 | table_stats_equal(const struct ofputil_table_stats *a, | |
1384 | const struct ofputil_table_stats *b) | |
1385 | { | |
1386 | return (a->active_count == b->active_count | |
1387 | && a->lookup_count == b->lookup_count | |
1388 | && a->matched_count == b->matched_count); | |
1389 | } | |
1390 | ||
1391 | void | |
1392 | ofputil_table_features_format( | |
1393 | struct ds *s, | |
1394 | const struct ofputil_table_features *features, | |
1395 | const struct ofputil_table_features *prev_features, | |
1396 | const struct ofputil_table_stats *stats, | |
1397 | const struct ofputil_table_stats *prev_stats, | |
1398 | const struct ofputil_table_map *table_map) | |
1399 | { | |
1400 | int i; | |
1401 | ||
1402 | ds_put_format(s, " table "); | |
1403 | ofputil_format_table(features->table_id, table_map, s); | |
1404 | if (features->name[0]) { | |
1405 | ds_put_format(s, " (\"%s\")", features->name); | |
1406 | } | |
1407 | ds_put_char(s, ':'); | |
1408 | ||
1409 | bool same_stats = prev_stats && table_stats_equal(stats, prev_stats); | |
1410 | bool same_features = prev_features && table_features_equal(features, | |
1411 | prev_features); | |
1412 | if ((!stats || same_stats) && same_features) { | |
1413 | ds_put_cstr(s, " ditto"); | |
1414 | return; | |
1415 | } | |
1416 | ds_put_char(s, '\n'); | |
1417 | if (stats) { | |
1418 | ds_put_format(s, " active=%"PRIu32", ", stats->active_count); | |
1419 | ds_put_format(s, "lookup=%"PRIu64", ", stats->lookup_count); | |
1420 | ds_put_format(s, "matched=%"PRIu64"\n", stats->matched_count); | |
1421 | } | |
1422 | if (same_features) { | |
1423 | if (!table_features_empty(features)) { | |
1424 | ds_put_cstr(s, " (same features)\n"); | |
1425 | } | |
1426 | return; | |
1427 | } | |
1428 | if (features->metadata_match || features->metadata_write) { | |
1429 | ds_put_format(s, " metadata: match=%#"PRIx64" write=%#"PRIx64"\n", | |
1430 | ntohll(features->metadata_match), | |
1431 | ntohll(features->metadata_write)); | |
1432 | } | |
1433 | ||
1434 | if (features->miss_config != OFPUTIL_TABLE_MISS_DEFAULT) { | |
1435 | ds_put_format(s, " config=%s\n", | |
1436 | ofputil_table_miss_to_string(features->miss_config)); | |
1437 | } | |
1438 | ||
1439 | if (features->supports_eviction >= 0) { | |
1440 | ds_put_format(s, " eviction: %ssupported\n", | |
1441 | features->supports_eviction ? "" : "not "); | |
1442 | ||
1443 | } | |
1444 | if (features->supports_vacancy_events >= 0) { | |
1445 | ds_put_format(s, " vacancy events: %ssupported\n", | |
1446 | features->supports_vacancy_events ? "" : "not "); | |
1447 | ||
1448 | } | |
1449 | ||
1450 | if (features->max_entries) { | |
1451 | ds_put_format(s, " max_entries=%"PRIu32"\n", features->max_entries); | |
1452 | } | |
1453 | ||
1454 | const struct ofputil_table_instruction_features *prev_nonmiss | |
1455 | = prev_features ? &prev_features->nonmiss : NULL; | |
1456 | const struct ofputil_table_instruction_features *prev_miss | |
1457 | = prev_features ? &prev_features->miss : NULL; | |
1458 | if (prev_features | |
1459 | && table_instruction_features_equal(&features->nonmiss, prev_nonmiss) | |
1460 | && table_instruction_features_equal(&features->miss, prev_miss)) { | |
1461 | if (!table_instruction_features_empty(&features->nonmiss)) { | |
1462 | ds_put_cstr(s, " (same instructions)\n"); | |
1463 | } | |
1464 | } else if (!table_instruction_features_equal(&features->nonmiss, | |
1465 | &features->miss)) { | |
1466 | ds_put_cstr(s, " instructions (other than table miss):\n"); | |
1467 | print_table_instruction_features(s, &features->nonmiss, prev_nonmiss); | |
1468 | ds_put_cstr(s, " instructions (table miss):\n"); | |
1469 | print_table_instruction_features(s, &features->miss, prev_miss); | |
1470 | } else if (!table_instruction_features_empty(&features->nonmiss)) { | |
1471 | ds_put_cstr(s, " instructions (table miss and others):\n"); | |
1472 | print_table_instruction_features(s, &features->nonmiss, prev_nonmiss); | |
1473 | } | |
1474 | ||
1475 | if (!bitmap_is_all_zeros(features->match.bm, MFF_N_IDS)) { | |
1476 | if (prev_features | |
1477 | && bitmap_equal(features->match.bm, prev_features->match.bm, | |
1478 | MFF_N_IDS)) { | |
1479 | ds_put_cstr(s, " (same matching)\n"); | |
1480 | } else { | |
1481 | ds_put_cstr(s, " matching:\n"); | |
1482 | BITMAP_FOR_EACH_1 (i, MFF_N_IDS, features->match.bm) { | |
1483 | const struct mf_field *f = mf_from_id(i); | |
1484 | bool mask = bitmap_is_set(features->mask.bm, i); | |
1485 | bool wildcard = bitmap_is_set(features->wildcard.bm, i); | |
1486 | ||
1487 | ds_put_format(s, " %s: %s\n", | |
1488 | f->name, | |
1489 | (mask ? "arbitrary mask" | |
1490 | : wildcard ? "exact match or wildcard" | |
1491 | : "must exact match")); | |
1492 | } | |
1493 | } | |
1494 | } | |
1495 | } | |
0d71302e BP |
1496 | \f |
1497 | /* Table stats. */ | |
1498 | ||
1499 | /* OpenFlow 1.0 and 1.1 don't distinguish between a field that cannot be | |
1500 | * matched and a field that must be wildcarded. This function returns a bitmap | |
1501 | * that contains both kinds of fields. */ | |
1502 | static struct mf_bitmap | |
1503 | wild_or_nonmatchable_fields(const struct ofputil_table_features *features) | |
1504 | { | |
1505 | struct mf_bitmap wc = features->match; | |
1506 | bitmap_not(wc.bm, MFF_N_IDS); | |
1507 | bitmap_or(wc.bm, features->wildcard.bm, MFF_N_IDS); | |
1508 | return wc; | |
1509 | } | |
1510 | ||
1511 | struct ofp10_wc_map { | |
1512 | enum ofp10_flow_wildcards wc10; | |
1513 | enum mf_field_id mf; | |
1514 | }; | |
1515 | ||
1516 | static const struct ofp10_wc_map ofp10_wc_map[] = { | |
1517 | { OFPFW10_IN_PORT, MFF_IN_PORT }, | |
1518 | { OFPFW10_DL_VLAN, MFF_VLAN_VID }, | |
1519 | { OFPFW10_DL_SRC, MFF_ETH_SRC }, | |
1520 | { OFPFW10_DL_DST, MFF_ETH_DST}, | |
1521 | { OFPFW10_DL_TYPE, MFF_ETH_TYPE }, | |
1522 | { OFPFW10_NW_PROTO, MFF_IP_PROTO }, | |
1523 | { OFPFW10_TP_SRC, MFF_TCP_SRC }, | |
1524 | { OFPFW10_TP_DST, MFF_TCP_DST }, | |
1525 | { OFPFW10_NW_SRC_MASK, MFF_IPV4_SRC }, | |
1526 | { OFPFW10_NW_DST_MASK, MFF_IPV4_DST }, | |
1527 | { OFPFW10_DL_VLAN_PCP, MFF_VLAN_PCP }, | |
1528 | { OFPFW10_NW_TOS, MFF_IP_DSCP }, | |
1529 | }; | |
1530 | ||
1531 | static ovs_be32 | |
1532 | mf_bitmap_to_of10(const struct mf_bitmap *fields) | |
1533 | { | |
1534 | const struct ofp10_wc_map *p; | |
1535 | uint32_t wc10 = 0; | |
1536 | ||
1537 | for (p = ofp10_wc_map; p < &ofp10_wc_map[ARRAY_SIZE(ofp10_wc_map)]; p++) { | |
1538 | if (bitmap_is_set(fields->bm, p->mf)) { | |
1539 | wc10 |= p->wc10; | |
1540 | } | |
1541 | } | |
1542 | return htonl(wc10); | |
1543 | } | |
1544 | ||
1545 | static struct mf_bitmap | |
1546 | mf_bitmap_from_of10(ovs_be32 wc10_) | |
1547 | { | |
1548 | struct mf_bitmap fields = MF_BITMAP_INITIALIZER; | |
1549 | const struct ofp10_wc_map *p; | |
1550 | uint32_t wc10 = ntohl(wc10_); | |
1551 | ||
1552 | for (p = ofp10_wc_map; p < &ofp10_wc_map[ARRAY_SIZE(ofp10_wc_map)]; p++) { | |
1553 | if (wc10 & p->wc10) { | |
1554 | bitmap_set1(fields.bm, p->mf); | |
1555 | } | |
1556 | } | |
1557 | return fields; | |
1558 | } | |
1559 | ||
1560 | static void | |
1561 | ofputil_put_ofp10_table_stats(const struct ofputil_table_stats *stats, | |
1562 | const struct ofputil_table_features *features, | |
1563 | struct ofpbuf *buf) | |
1564 | { | |
1565 | struct mf_bitmap wc = wild_or_nonmatchable_fields(features); | |
1566 | struct ofp10_table_stats *out; | |
1567 | ||
1568 | out = ofpbuf_put_zeros(buf, sizeof *out); | |
1569 | out->table_id = features->table_id; | |
1570 | ovs_strlcpy_arrays(out->name, features->name); | |
1571 | out->wildcards = mf_bitmap_to_of10(&wc); | |
1572 | out->max_entries = htonl(features->max_entries); | |
1573 | out->active_count = htonl(stats->active_count); | |
1574 | put_32aligned_be64(&out->lookup_count, htonll(stats->lookup_count)); | |
1575 | put_32aligned_be64(&out->matched_count, htonll(stats->matched_count)); | |
1576 | } | |
1577 | ||
1578 | struct ofp11_wc_map { | |
1579 | enum ofp11_flow_match_fields wc11; | |
1580 | enum mf_field_id mf; | |
1581 | }; | |
1582 | ||
1583 | static const struct ofp11_wc_map ofp11_wc_map[] = { | |
1584 | { OFPFMF11_IN_PORT, MFF_IN_PORT }, | |
1585 | { OFPFMF11_DL_VLAN, MFF_VLAN_VID }, | |
1586 | { OFPFMF11_DL_VLAN_PCP, MFF_VLAN_PCP }, | |
1587 | { OFPFMF11_DL_TYPE, MFF_ETH_TYPE }, | |
1588 | { OFPFMF11_NW_TOS, MFF_IP_DSCP }, | |
1589 | { OFPFMF11_NW_PROTO, MFF_IP_PROTO }, | |
1590 | { OFPFMF11_TP_SRC, MFF_TCP_SRC }, | |
1591 | { OFPFMF11_TP_DST, MFF_TCP_DST }, | |
1592 | { OFPFMF11_MPLS_LABEL, MFF_MPLS_LABEL }, | |
1593 | { OFPFMF11_MPLS_TC, MFF_MPLS_TC }, | |
1594 | /* I don't know what OFPFMF11_TYPE means. */ | |
1595 | { OFPFMF11_DL_SRC, MFF_ETH_SRC }, | |
1596 | { OFPFMF11_DL_DST, MFF_ETH_DST }, | |
1597 | { OFPFMF11_NW_SRC, MFF_IPV4_SRC }, | |
1598 | { OFPFMF11_NW_DST, MFF_IPV4_DST }, | |
1599 | { OFPFMF11_METADATA, MFF_METADATA }, | |
1600 | }; | |
1601 | ||
1602 | static ovs_be32 | |
1603 | mf_bitmap_to_of11(const struct mf_bitmap *fields) | |
1604 | { | |
1605 | const struct ofp11_wc_map *p; | |
1606 | uint32_t wc11 = 0; | |
1607 | ||
1608 | for (p = ofp11_wc_map; p < &ofp11_wc_map[ARRAY_SIZE(ofp11_wc_map)]; p++) { | |
1609 | if (bitmap_is_set(fields->bm, p->mf)) { | |
1610 | wc11 |= p->wc11; | |
1611 | } | |
1612 | } | |
1613 | return htonl(wc11); | |
1614 | } | |
1615 | ||
1616 | static struct mf_bitmap | |
1617 | mf_bitmap_from_of11(ovs_be32 wc11_) | |
1618 | { | |
1619 | struct mf_bitmap fields = MF_BITMAP_INITIALIZER; | |
1620 | const struct ofp11_wc_map *p; | |
1621 | uint32_t wc11 = ntohl(wc11_); | |
1622 | ||
1623 | for (p = ofp11_wc_map; p < &ofp11_wc_map[ARRAY_SIZE(ofp11_wc_map)]; p++) { | |
1624 | if (wc11 & p->wc11) { | |
1625 | bitmap_set1(fields.bm, p->mf); | |
1626 | } | |
1627 | } | |
1628 | return fields; | |
1629 | } | |
1630 | ||
1631 | static void | |
1632 | ofputil_put_ofp11_table_stats(const struct ofputil_table_stats *stats, | |
1633 | const struct ofputil_table_features *features, | |
1634 | struct ofpbuf *buf) | |
1635 | { | |
1636 | struct mf_bitmap wc = wild_or_nonmatchable_fields(features); | |
1637 | struct ofp11_table_stats *out; | |
1638 | ||
1639 | out = ofpbuf_put_zeros(buf, sizeof *out); | |
1640 | out->table_id = features->table_id; | |
1641 | ovs_strlcpy_arrays(out->name, features->name); | |
1642 | out->wildcards = mf_bitmap_to_of11(&wc); | |
1643 | out->match = mf_bitmap_to_of11(&features->match); | |
1644 | out->instructions = ovsinst_bitmap_to_openflow( | |
1645 | features->nonmiss.instructions, OFP11_VERSION); | |
1646 | out->write_actions = ofpact_bitmap_to_openflow( | |
1647 | features->nonmiss.write.ofpacts, OFP11_VERSION); | |
1648 | out->apply_actions = ofpact_bitmap_to_openflow( | |
1649 | features->nonmiss.apply.ofpacts, OFP11_VERSION); | |
1650 | out->config = htonl(features->miss_config); | |
1651 | out->max_entries = htonl(features->max_entries); | |
1652 | out->active_count = htonl(stats->active_count); | |
1653 | out->lookup_count = htonll(stats->lookup_count); | |
1654 | out->matched_count = htonll(stats->matched_count); | |
1655 | } | |
1656 | ||
1657 | static void | |
1658 | ofputil_put_ofp12_table_stats(const struct ofputil_table_stats *stats, | |
1659 | const struct ofputil_table_features *features, | |
1660 | struct ofpbuf *buf) | |
1661 | { | |
1662 | struct ofp12_table_stats *out; | |
1663 | ||
1664 | out = ofpbuf_put_zeros(buf, sizeof *out); | |
1665 | out->table_id = features->table_id; | |
1666 | ovs_strlcpy_arrays(out->name, features->name); | |
1667 | out->match = oxm_bitmap_from_mf_bitmap(&features->match, OFP12_VERSION); | |
1668 | out->wildcards = oxm_bitmap_from_mf_bitmap(&features->wildcard, | |
1669 | OFP12_VERSION); | |
1670 | out->write_actions = ofpact_bitmap_to_openflow( | |
1671 | features->nonmiss.write.ofpacts, OFP12_VERSION); | |
1672 | out->apply_actions = ofpact_bitmap_to_openflow( | |
1673 | features->nonmiss.apply.ofpacts, OFP12_VERSION); | |
1674 | out->write_setfields = oxm_bitmap_from_mf_bitmap( | |
1675 | &features->nonmiss.write.set_fields, OFP12_VERSION); | |
1676 | out->apply_setfields = oxm_bitmap_from_mf_bitmap( | |
1677 | &features->nonmiss.apply.set_fields, OFP12_VERSION); | |
1678 | out->metadata_match = features->metadata_match; | |
1679 | out->metadata_write = features->metadata_write; | |
1680 | out->instructions = ovsinst_bitmap_to_openflow( | |
1681 | features->nonmiss.instructions, OFP12_VERSION); | |
1682 | out->config = ofputil_encode_table_config(features->miss_config, | |
1683 | OFPUTIL_TABLE_EVICTION_DEFAULT, | |
1684 | OFPUTIL_TABLE_VACANCY_DEFAULT, | |
1685 | OFP12_VERSION); | |
1686 | out->max_entries = htonl(features->max_entries); | |
1687 | out->active_count = htonl(stats->active_count); | |
1688 | out->lookup_count = htonll(stats->lookup_count); | |
1689 | out->matched_count = htonll(stats->matched_count); | |
1690 | } | |
1691 | ||
1692 | static void | |
1693 | ofputil_put_ofp13_table_stats(const struct ofputil_table_stats *stats, | |
1694 | struct ofpbuf *buf) | |
1695 | { | |
1696 | struct ofp13_table_stats *out; | |
1697 | ||
1698 | out = ofpbuf_put_zeros(buf, sizeof *out); | |
1699 | out->table_id = stats->table_id; | |
1700 | out->active_count = htonl(stats->active_count); | |
1701 | out->lookup_count = htonll(stats->lookup_count); | |
1702 | out->matched_count = htonll(stats->matched_count); | |
1703 | } | |
1704 | ||
1705 | struct ofpbuf * | |
1706 | ofputil_encode_table_stats_reply(const struct ofp_header *request) | |
1707 | { | |
1708 | return ofpraw_alloc_stats_reply(request, 0); | |
1709 | } | |
1710 | ||
1711 | void | |
1712 | ofputil_append_table_stats_reply(struct ofpbuf *reply, | |
1713 | const struct ofputil_table_stats *stats, | |
1714 | const struct ofputil_table_features *features) | |
1715 | { | |
1716 | struct ofp_header *oh = reply->header; | |
1717 | ||
1718 | ovs_assert(stats->table_id == features->table_id); | |
1719 | ||
1720 | switch ((enum ofp_version) oh->version) { | |
1721 | case OFP10_VERSION: | |
1722 | ofputil_put_ofp10_table_stats(stats, features, reply); | |
1723 | break; | |
1724 | ||
1725 | case OFP11_VERSION: | |
1726 | ofputil_put_ofp11_table_stats(stats, features, reply); | |
1727 | break; | |
1728 | ||
1729 | case OFP12_VERSION: | |
1730 | ofputil_put_ofp12_table_stats(stats, features, reply); | |
1731 | break; | |
1732 | ||
1733 | case OFP13_VERSION: | |
1734 | case OFP14_VERSION: | |
1735 | case OFP15_VERSION: | |
1736 | case OFP16_VERSION: | |
1737 | ofputil_put_ofp13_table_stats(stats, reply); | |
1738 | break; | |
1739 | ||
1740 | default: | |
1741 | OVS_NOT_REACHED(); | |
1742 | } | |
1743 | } | |
1744 | ||
1745 | static int | |
1746 | ofputil_decode_ofp10_table_stats(struct ofpbuf *msg, | |
1747 | struct ofputil_table_stats *stats, | |
1748 | struct ofputil_table_features *features) | |
1749 | { | |
1750 | struct ofp10_table_stats *ots; | |
1751 | ||
1752 | ots = ofpbuf_try_pull(msg, sizeof *ots); | |
1753 | if (!ots) { | |
1754 | return OFPERR_OFPBRC_BAD_LEN; | |
1755 | } | |
1756 | ||
1757 | features->table_id = ots->table_id; | |
1758 | ovs_strlcpy_arrays(features->name, ots->name); | |
1759 | features->max_entries = ntohl(ots->max_entries); | |
1760 | features->match = features->wildcard = mf_bitmap_from_of10(ots->wildcards); | |
1761 | ||
1762 | stats->table_id = ots->table_id; | |
1763 | stats->active_count = ntohl(ots->active_count); | |
1764 | stats->lookup_count = ntohll(get_32aligned_be64(&ots->lookup_count)); | |
1765 | stats->matched_count = ntohll(get_32aligned_be64(&ots->matched_count)); | |
1766 | ||
1767 | return 0; | |
1768 | } | |
1769 | ||
1770 | static int | |
1771 | ofputil_decode_ofp11_table_stats(struct ofpbuf *msg, | |
1772 | struct ofputil_table_stats *stats, | |
1773 | struct ofputil_table_features *features) | |
1774 | { | |
1775 | struct ofp11_table_stats *ots; | |
1776 | ||
1777 | ots = ofpbuf_try_pull(msg, sizeof *ots); | |
1778 | if (!ots) { | |
1779 | return OFPERR_OFPBRC_BAD_LEN; | |
1780 | } | |
1781 | ||
1782 | features->table_id = ots->table_id; | |
1783 | ovs_strlcpy_arrays(features->name, ots->name); | |
1784 | features->max_entries = ntohl(ots->max_entries); | |
1785 | features->nonmiss.instructions = ovsinst_bitmap_from_openflow( | |
1786 | ots->instructions, OFP11_VERSION); | |
1787 | features->nonmiss.write.ofpacts = ofpact_bitmap_from_openflow( | |
1788 | ots->write_actions, OFP11_VERSION); | |
1789 | features->nonmiss.apply.ofpacts = ofpact_bitmap_from_openflow( | |
1790 | ots->write_actions, OFP11_VERSION); | |
1791 | features->miss = features->nonmiss; | |
1792 | features->miss_config = ofputil_decode_table_miss(ots->config, | |
1793 | OFP11_VERSION); | |
1794 | features->match = mf_bitmap_from_of11(ots->match); | |
1795 | features->wildcard = mf_bitmap_from_of11(ots->wildcards); | |
1796 | bitmap_or(features->match.bm, features->wildcard.bm, MFF_N_IDS); | |
1797 | ||
1798 | stats->table_id = ots->table_id; | |
1799 | stats->active_count = ntohl(ots->active_count); | |
1800 | stats->lookup_count = ntohll(ots->lookup_count); | |
1801 | stats->matched_count = ntohll(ots->matched_count); | |
1802 | ||
1803 | return 0; | |
1804 | } | |
1805 | ||
1806 | static int | |
1807 | ofputil_decode_ofp12_table_stats(struct ofpbuf *msg, | |
1808 | struct ofputil_table_stats *stats, | |
1809 | struct ofputil_table_features *features) | |
1810 | { | |
1811 | struct ofp12_table_stats *ots; | |
1812 | ||
1813 | ots = ofpbuf_try_pull(msg, sizeof *ots); | |
1814 | if (!ots) { | |
1815 | return OFPERR_OFPBRC_BAD_LEN; | |
1816 | } | |
1817 | ||
1818 | features->table_id = ots->table_id; | |
1819 | ovs_strlcpy_arrays(features->name, ots->name); | |
1820 | features->metadata_match = ots->metadata_match; | |
1821 | features->metadata_write = ots->metadata_write; | |
1822 | features->miss_config = ofputil_decode_table_miss(ots->config, | |
1823 | OFP12_VERSION); | |
1824 | features->max_entries = ntohl(ots->max_entries); | |
1825 | ||
1826 | features->nonmiss.instructions = ovsinst_bitmap_from_openflow( | |
1827 | ots->instructions, OFP12_VERSION); | |
1828 | features->nonmiss.write.ofpacts = ofpact_bitmap_from_openflow( | |
1829 | ots->write_actions, OFP12_VERSION); | |
1830 | features->nonmiss.apply.ofpacts = ofpact_bitmap_from_openflow( | |
1831 | ots->apply_actions, OFP12_VERSION); | |
1832 | features->nonmiss.write.set_fields = oxm_bitmap_to_mf_bitmap( | |
1833 | ots->write_setfields, OFP12_VERSION); | |
1834 | features->nonmiss.apply.set_fields = oxm_bitmap_to_mf_bitmap( | |
1835 | ots->apply_setfields, OFP12_VERSION); | |
1836 | features->miss = features->nonmiss; | |
1837 | ||
1838 | features->match = oxm_bitmap_to_mf_bitmap(ots->match, OFP12_VERSION); | |
1839 | features->wildcard = oxm_bitmap_to_mf_bitmap(ots->wildcards, | |
1840 | OFP12_VERSION); | |
1841 | bitmap_or(features->match.bm, features->wildcard.bm, MFF_N_IDS); | |
1842 | ||
1843 | stats->table_id = ots->table_id; | |
1844 | stats->active_count = ntohl(ots->active_count); | |
1845 | stats->lookup_count = ntohll(ots->lookup_count); | |
1846 | stats->matched_count = ntohll(ots->matched_count); | |
1847 | ||
1848 | return 0; | |
1849 | } | |
1850 | ||
1851 | static int | |
1852 | ofputil_decode_ofp13_table_stats(struct ofpbuf *msg, | |
1853 | struct ofputil_table_stats *stats, | |
1854 | struct ofputil_table_features *features) | |
1855 | { | |
1856 | struct ofp13_table_stats *ots; | |
1857 | ||
1858 | ots = ofpbuf_try_pull(msg, sizeof *ots); | |
1859 | if (!ots) { | |
1860 | return OFPERR_OFPBRC_BAD_LEN; | |
1861 | } | |
1862 | ||
1863 | features->table_id = ots->table_id; | |
1864 | ||
1865 | stats->table_id = ots->table_id; | |
1866 | stats->active_count = ntohl(ots->active_count); | |
1867 | stats->lookup_count = ntohll(ots->lookup_count); | |
1868 | stats->matched_count = ntohll(ots->matched_count); | |
1869 | ||
1870 | return 0; | |
1871 | } | |
1872 | ||
1873 | int | |
1874 | ofputil_decode_table_stats_reply(struct ofpbuf *msg, | |
1875 | struct ofputil_table_stats *stats, | |
1876 | struct ofputil_table_features *features) | |
1877 | { | |
1878 | const struct ofp_header *oh; | |
1879 | ||
1880 | if (!msg->header) { | |
1881 | ofpraw_pull_assert(msg); | |
1882 | } | |
1883 | oh = msg->header; | |
1884 | ||
1885 | if (!msg->size) { | |
1886 | return EOF; | |
1887 | } | |
1888 | ||
1889 | memset(stats, 0, sizeof *stats); | |
1890 | memset(features, 0, sizeof *features); | |
1891 | features->supports_eviction = -1; | |
1892 | features->supports_vacancy_events = -1; | |
1893 | ||
1894 | switch ((enum ofp_version) oh->version) { | |
1895 | case OFP10_VERSION: | |
1896 | return ofputil_decode_ofp10_table_stats(msg, stats, features); | |
1897 | ||
1898 | case OFP11_VERSION: | |
1899 | return ofputil_decode_ofp11_table_stats(msg, stats, features); | |
1900 | ||
1901 | case OFP12_VERSION: | |
1902 | return ofputil_decode_ofp12_table_stats(msg, stats, features); | |
1903 | ||
1904 | case OFP13_VERSION: | |
1905 | case OFP14_VERSION: | |
1906 | case OFP15_VERSION: | |
1907 | case OFP16_VERSION: | |
1908 | return ofputil_decode_ofp13_table_stats(msg, stats, features); | |
1909 | ||
1910 | default: | |
1911 | OVS_NOT_REACHED(); | |
1912 | } | |
1913 | } | |
1914 | \f | |
1915 | static void | |
1916 | ofputil_put_ofp14_table_desc(const struct ofputil_table_desc *td, | |
1917 | struct ofpbuf *b, enum ofp_version version) | |
1918 | { | |
1919 | struct ofp14_table_desc *otd; | |
1920 | struct ofp14_table_mod_prop_vacancy *otv; | |
1921 | size_t start_otd; | |
1922 | ||
1923 | start_otd = b->size; | |
1924 | ofpbuf_put_zeros(b, sizeof *otd); | |
1925 | ||
1926 | ofpprop_put_u32(b, OFPTMPT14_EVICTION, td->eviction_flags); | |
1927 | ||
1928 | otv = ofpbuf_put_zeros(b, sizeof *otv); | |
1929 | otv->type = htons(OFPTMPT14_VACANCY); | |
1930 | otv->length = htons(sizeof *otv); | |
1931 | otv->vacancy_down = td->table_vacancy.vacancy_down; | |
1932 | otv->vacancy_up = td->table_vacancy.vacancy_up; | |
1933 | otv->vacancy = td->table_vacancy.vacancy; | |
1934 | ||
1935 | otd = ofpbuf_at_assert(b, start_otd, sizeof *otd); | |
1936 | otd->length = htons(b->size - start_otd); | |
1937 | otd->table_id = td->table_id; | |
1938 | otd->config = ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT, | |
1939 | td->eviction, td->vacancy, | |
1940 | version); | |
1941 | } | |
1942 | ||
1943 | /* Converts the abstract form of a "table status" message in '*ts' into an | |
1944 | * OpenFlow message suitable for 'protocol', and returns that encoded form in | |
1945 | * a buffer owned by the caller. */ | |
1946 | struct ofpbuf * | |
1947 | ofputil_encode_table_status(const struct ofputil_table_status *ts, | |
1948 | enum ofputil_protocol protocol) | |
1949 | { | |
1950 | enum ofp_version version; | |
1951 | struct ofpbuf *b; | |
1952 | ||
1953 | version = ofputil_protocol_to_ofp_version(protocol); | |
1954 | if (version >= OFP14_VERSION) { | |
1955 | enum ofpraw raw; | |
1956 | struct ofp14_table_status *ots; | |
1957 | ||
1958 | raw = OFPRAW_OFPT14_TABLE_STATUS; | |
1959 | b = ofpraw_alloc_xid(raw, version, htonl(0), 0); | |
1960 | ots = ofpbuf_put_zeros(b, sizeof *ots); | |
1961 | ots->reason = ts->reason; | |
1962 | ofputil_put_ofp14_table_desc(&ts->desc, b, version); | |
1963 | ofpmsg_update_length(b); | |
1964 | return b; | |
1965 | } else { | |
1966 | return NULL; | |
1967 | } | |
1968 | } | |
1969 | ||
1970 | /* Decodes the OpenFlow "table status" message in '*ots' into an abstract form | |
1971 | * in '*ts'. Returns 0 if successful, otherwise an OFPERR_* value. */ | |
1972 | enum ofperr | |
1973 | ofputil_decode_table_status(const struct ofp_header *oh, | |
1974 | struct ofputil_table_status *ts) | |
1975 | { | |
1976 | const struct ofp14_table_status *ots; | |
1977 | struct ofpbuf b; | |
1978 | enum ofperr error; | |
1979 | enum ofpraw raw; | |
1980 | ||
1981 | ofpbuf_use_const(&b, oh, ntohs(oh->length)); | |
1982 | raw = ofpraw_pull_assert(&b); | |
1983 | ots = ofpbuf_pull(&b, sizeof *ots); | |
1984 | ||
1985 | if (raw == OFPRAW_OFPT14_TABLE_STATUS) { | |
1986 | if (ots->reason != OFPTR_VACANCY_DOWN | |
1987 | && ots->reason != OFPTR_VACANCY_UP) { | |
1988 | return OFPERR_OFPBPC_BAD_VALUE; | |
1989 | } | |
1990 | ts->reason = ots->reason; | |
1991 | ||
1992 | error = ofputil_decode_table_desc(&b, &ts->desc, oh->version); | |
1993 | return error; | |
1994 | } else { | |
1995 | return OFPERR_OFPBRC_BAD_VERSION; | |
1996 | } | |
1997 | ||
1998 | return 0; | |
1999 | } |