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