]>
Commit | Line | Data |
---|---|---|
0d71302e BP |
1 | /* |
2 | * Copyright (c) 2008-2017 Nicira, Inc. | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <config.h> | |
18 | #include "openvswitch/ofp-connection.h" | |
19 | #include "byte-order.h" | |
20 | #include "openflow/nicira-ext.h" | |
21 | #include "openvswitch/ofp-errors.h" | |
fe2c69f4 | 22 | #include "openvswitch/ofp-monitor.h" |
0d71302e | 23 | #include "openvswitch/ofp-msgs.h" |
fe2c69f4 | 24 | #include "openvswitch/ofp-packet.h" |
0d71302e | 25 | #include "openvswitch/ofp-prop.h" |
fe2c69f4 | 26 | #include "openvswitch/ofp-table.h" |
0d71302e | 27 | #include "openvswitch/ofpbuf.h" |
fe2c69f4 | 28 | #include "openvswitch/type-props.h" |
0d71302e BP |
29 | #include "openvswitch/vlog.h" |
30 | #include "util.h" | |
31 | ||
32 | VLOG_DEFINE_THIS_MODULE(ofp_connection); | |
33 | ||
34 | /* ofputil_role_request */ | |
35 | ||
36 | /* Decodes the OpenFlow "role request" or "role reply" message in '*oh' into | |
37 | * an abstract form in '*rr'. Returns 0 if successful, otherwise an | |
38 | * OFPERR_* value. */ | |
39 | enum ofperr | |
40 | ofputil_decode_role_message(const struct ofp_header *oh, | |
41 | struct ofputil_role_request *rr) | |
42 | { | |
43 | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); | |
44 | enum ofpraw raw = ofpraw_pull_assert(&b); | |
45 | if (raw == OFPRAW_OFPT12_ROLE_REQUEST || | |
46 | raw == OFPRAW_OFPT12_ROLE_REPLY) { | |
47 | const struct ofp12_role_request *orr = b.msg; | |
48 | ||
49 | if (orr->role != htonl(OFPCR12_ROLE_NOCHANGE) && | |
50 | orr->role != htonl(OFPCR12_ROLE_EQUAL) && | |
807152a4 BP |
51 | orr->role != htonl(OFPCR12_ROLE_PRIMARY) && |
52 | orr->role != htonl(OFPCR12_ROLE_SECONDARY)) { | |
0d71302e BP |
53 | return OFPERR_OFPRRFC_BAD_ROLE; |
54 | } | |
55 | ||
56 | rr->role = ntohl(orr->role); | |
57 | if (raw == OFPRAW_OFPT12_ROLE_REQUEST | |
58 | ? orr->role == htonl(OFPCR12_ROLE_NOCHANGE) | |
59 | : orr->generation_id == OVS_BE64_MAX) { | |
60 | rr->have_generation_id = false; | |
61 | rr->generation_id = 0; | |
62 | } else { | |
63 | rr->have_generation_id = true; | |
64 | rr->generation_id = ntohll(orr->generation_id); | |
65 | } | |
66 | } else if (raw == OFPRAW_NXT_ROLE_REQUEST || | |
67 | raw == OFPRAW_NXT_ROLE_REPLY) { | |
68 | const struct nx_role_request *nrr = b.msg; | |
69 | ||
70 | BUILD_ASSERT(NX_ROLE_OTHER + 1 == OFPCR12_ROLE_EQUAL); | |
807152a4 BP |
71 | BUILD_ASSERT(NX_ROLE_PRIMARY + 1 == OFPCR12_ROLE_PRIMARY); |
72 | BUILD_ASSERT(NX_ROLE_SECONDARY + 1 == OFPCR12_ROLE_SECONDARY); | |
0d71302e BP |
73 | |
74 | if (nrr->role != htonl(NX_ROLE_OTHER) && | |
807152a4 BP |
75 | nrr->role != htonl(NX_ROLE_PRIMARY) && |
76 | nrr->role != htonl(NX_ROLE_SECONDARY)) { | |
0d71302e BP |
77 | return OFPERR_OFPRRFC_BAD_ROLE; |
78 | } | |
79 | ||
80 | rr->role = ntohl(nrr->role) + 1; | |
81 | rr->have_generation_id = false; | |
82 | rr->generation_id = 0; | |
83 | } else { | |
84 | OVS_NOT_REACHED(); | |
85 | } | |
86 | ||
87 | return 0; | |
88 | } | |
89 | ||
fe2c69f4 BP |
90 | static void |
91 | format_role_generic(struct ds *string, enum ofp12_controller_role role, | |
92 | uint64_t generation_id) | |
93 | { | |
94 | ds_put_cstr(string, " role="); | |
95 | ||
96 | switch (role) { | |
97 | case OFPCR12_ROLE_NOCHANGE: | |
98 | ds_put_cstr(string, "nochange"); | |
99 | break; | |
100 | case OFPCR12_ROLE_EQUAL: | |
101 | ds_put_cstr(string, "equal"); /* OF 1.2 wording */ | |
102 | break; | |
807152a4 BP |
103 | case OFPCR12_ROLE_PRIMARY: |
104 | ds_put_cstr(string, "primary"); | |
fe2c69f4 | 105 | break; |
807152a4 BP |
106 | case OFPCR12_ROLE_SECONDARY: |
107 | ds_put_cstr(string, "secondary"); | |
fe2c69f4 BP |
108 | break; |
109 | default: | |
110 | OVS_NOT_REACHED(); | |
111 | } | |
112 | ||
113 | if (generation_id != UINT64_MAX) { | |
114 | ds_put_format(string, " generation_id=%"PRIu64, generation_id); | |
115 | } | |
116 | } | |
117 | ||
118 | void | |
119 | ofputil_format_role_message(struct ds *string, | |
120 | const struct ofputil_role_request *rr) | |
121 | { | |
122 | format_role_generic(string, rr->role, (rr->have_generation_id | |
123 | ? rr->generation_id | |
124 | : UINT64_MAX)); | |
125 | } | |
126 | ||
0d71302e BP |
127 | /* Returns an encoded form of a role reply suitable for the "request" in a |
128 | * buffer owned by the caller. */ | |
129 | struct ofpbuf * | |
130 | ofputil_encode_role_reply(const struct ofp_header *request, | |
131 | const struct ofputil_role_request *rr) | |
132 | { | |
133 | struct ofpbuf *buf; | |
134 | enum ofpraw raw; | |
135 | ||
136 | raw = ofpraw_decode_assert(request); | |
137 | if (raw == OFPRAW_OFPT12_ROLE_REQUEST) { | |
138 | struct ofp12_role_request *orr; | |
139 | ||
140 | buf = ofpraw_alloc_reply(OFPRAW_OFPT12_ROLE_REPLY, request, 0); | |
141 | orr = ofpbuf_put_zeros(buf, sizeof *orr); | |
142 | ||
143 | orr->role = htonl(rr->role); | |
144 | orr->generation_id = htonll(rr->have_generation_id | |
145 | ? rr->generation_id | |
146 | : UINT64_MAX); | |
147 | } else if (raw == OFPRAW_NXT_ROLE_REQUEST) { | |
148 | struct nx_role_request *nrr; | |
149 | ||
150 | BUILD_ASSERT(NX_ROLE_OTHER == OFPCR12_ROLE_EQUAL - 1); | |
807152a4 BP |
151 | BUILD_ASSERT(NX_ROLE_PRIMARY == OFPCR12_ROLE_PRIMARY - 1); |
152 | BUILD_ASSERT(NX_ROLE_SECONDARY == OFPCR12_ROLE_SECONDARY - 1); | |
0d71302e BP |
153 | |
154 | buf = ofpraw_alloc_reply(OFPRAW_NXT_ROLE_REPLY, request, 0); | |
155 | nrr = ofpbuf_put_zeros(buf, sizeof *nrr); | |
156 | nrr->role = htonl(rr->role - 1); | |
157 | } else { | |
158 | OVS_NOT_REACHED(); | |
159 | } | |
160 | ||
161 | return buf; | |
162 | } | |
163 | \f | |
164 | /* Encodes "role status" message 'status' for sending in the given | |
165 | * 'protocol'. Returns the role status message, if 'protocol' supports them, | |
166 | * otherwise a null pointer. */ | |
167 | struct ofpbuf * | |
168 | ofputil_encode_role_status(const struct ofputil_role_status *status, | |
169 | enum ofputil_protocol protocol) | |
170 | { | |
b0e07d50 BP |
171 | enum ofp_version version = ofputil_protocol_to_ofp_version(protocol); |
172 | if (version < OFP13_VERSION) { | |
0d71302e BP |
173 | return NULL; |
174 | } | |
b0e07d50 BP |
175 | |
176 | enum ofpraw raw = (version >= OFP14_VERSION | |
177 | ? OFPRAW_OFPT14_ROLE_STATUS | |
178 | : OFPRAW_ONFT13_ROLE_STATUS); | |
179 | struct ofpbuf *buf = ofpraw_alloc_xid(raw, version, htonl(0), 0); | |
180 | struct ofp14_role_status *rstatus = ofpbuf_put_zeros(buf, sizeof *rstatus); | |
181 | rstatus->role = htonl(status->role); | |
182 | rstatus->reason = status->reason; | |
183 | rstatus->generation_id = htonll(status->generation_id); | |
184 | ||
185 | return buf; | |
0d71302e BP |
186 | } |
187 | ||
188 | enum ofperr | |
189 | ofputil_decode_role_status(const struct ofp_header *oh, | |
190 | struct ofputil_role_status *rs) | |
191 | { | |
192 | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); | |
b0e07d50 BP |
193 | enum ofpraw raw = ofpraw_pull_assert(&b); |
194 | ovs_assert(raw == OFPRAW_OFPT14_ROLE_STATUS || | |
195 | raw == OFPRAW_ONFT13_ROLE_STATUS); | |
0d71302e BP |
196 | |
197 | const struct ofp14_role_status *r = b.msg; | |
198 | if (r->role != htonl(OFPCR12_ROLE_NOCHANGE) && | |
199 | r->role != htonl(OFPCR12_ROLE_EQUAL) && | |
807152a4 BP |
200 | r->role != htonl(OFPCR12_ROLE_PRIMARY) && |
201 | r->role != htonl(OFPCR12_ROLE_SECONDARY)) { | |
0d71302e BP |
202 | return OFPERR_OFPRRFC_BAD_ROLE; |
203 | } | |
204 | ||
205 | rs->role = ntohl(r->role); | |
206 | rs->generation_id = ntohll(r->generation_id); | |
207 | rs->reason = r->reason; | |
208 | ||
209 | return 0; | |
210 | } | |
fe2c69f4 BP |
211 | |
212 | void | |
213 | ofputil_format_role_status(struct ds *string, | |
214 | const struct ofputil_role_status *rs) | |
215 | { | |
216 | format_role_generic(string, rs->role, rs->generation_id); | |
217 | ||
218 | ds_put_cstr(string, " reason="); | |
219 | ||
220 | switch (rs->reason) { | |
807152a4 BP |
221 | case OFPCRR_PRIMARY_REQUEST: |
222 | ds_put_cstr(string, "primary_request"); | |
fe2c69f4 BP |
223 | break; |
224 | case OFPCRR_CONFIG: | |
225 | ds_put_cstr(string, "configuration_changed"); | |
226 | break; | |
227 | case OFPCRR_EXPERIMENTER: | |
228 | ds_put_cstr(string, "experimenter_data_changed"); | |
229 | break; | |
230 | case OFPCRR_N_REASONS: | |
231 | default: | |
232 | ds_put_cstr(string, "(unknown)"); | |
233 | break; | |
234 | } | |
235 | } | |
0d71302e BP |
236 | \f |
237 | const char * | |
238 | ofputil_async_msg_type_to_string(enum ofputil_async_msg_type type) | |
239 | { | |
240 | switch (type) { | |
241 | case OAM_PACKET_IN: return "PACKET_IN"; | |
242 | case OAM_PORT_STATUS: return "PORT_STATUS"; | |
243 | case OAM_FLOW_REMOVED: return "FLOW_REMOVED"; | |
244 | case OAM_ROLE_STATUS: return "ROLE_STATUS"; | |
245 | case OAM_TABLE_STATUS: return "TABLE_STATUS"; | |
246 | case OAM_REQUESTFORWARD: return "REQUESTFORWARD"; | |
247 | ||
248 | case OAM_N_TYPES: | |
249 | default: | |
250 | OVS_NOT_REACHED(); | |
251 | } | |
252 | } | |
253 | ||
254 | struct ofp14_async_prop { | |
255 | uint64_t prop_type; | |
256 | enum ofputil_async_msg_type oam; | |
807152a4 | 257 | bool primary; |
0d71302e BP |
258 | uint32_t allowed10, allowed14; |
259 | }; | |
260 | ||
807152a4 BP |
261 | #define AP_PAIR(SECONDARY_PROP_TYPE, OAM, A10, A14) \ |
262 | { SECONDARY_PROP_TYPE, OAM, false, A10, (A14) ? (A14) : (A10) }, \ | |
263 | { (SECONDARY_PROP_TYPE + 1), OAM, true, A10, (A14) ? (A14) : (A10) } | |
0d71302e BP |
264 | |
265 | static const struct ofp14_async_prop async_props[] = { | |
266 | AP_PAIR( 0, OAM_PACKET_IN, OFPR10_BITS, OFPR14_BITS), | |
267 | AP_PAIR( 2, OAM_PORT_STATUS, (1 << OFPPR_N_REASONS) - 1, 0), | |
268 | AP_PAIR( 4, OAM_FLOW_REMOVED, (1 << OVS_OFPRR_NONE) - 1, 0), | |
269 | AP_PAIR( 6, OAM_ROLE_STATUS, (1 << OFPCRR_N_REASONS) - 1, 0), | |
270 | AP_PAIR( 8, OAM_TABLE_STATUS, OFPTR_BITS, 0), | |
271 | AP_PAIR(10, OAM_REQUESTFORWARD, (1 << OFPRFR_N_REASONS) - 1, 0), | |
272 | }; | |
273 | ||
274 | #define FOR_EACH_ASYNC_PROP(VAR) \ | |
275 | for (const struct ofp14_async_prop *VAR = async_props; \ | |
276 | VAR < &async_props[ARRAY_SIZE(async_props)]; VAR++) | |
277 | ||
278 | static const struct ofp14_async_prop * | |
279 | get_ofp14_async_config_prop_by_prop_type(uint64_t prop_type) | |
280 | { | |
281 | FOR_EACH_ASYNC_PROP (ap) { | |
282 | if (prop_type == ap->prop_type) { | |
283 | return ap; | |
284 | } | |
285 | } | |
286 | return NULL; | |
287 | } | |
288 | ||
289 | static const struct ofp14_async_prop * | |
290 | get_ofp14_async_config_prop_by_oam(enum ofputil_async_msg_type oam, | |
807152a4 | 291 | bool primary) |
0d71302e BP |
292 | { |
293 | FOR_EACH_ASYNC_PROP (ap) { | |
807152a4 | 294 | if (ap->oam == oam && ap->primary == primary) { |
0d71302e BP |
295 | return ap; |
296 | } | |
297 | } | |
298 | return NULL; | |
299 | } | |
300 | ||
301 | static uint32_t | |
302 | ofp14_async_prop_allowed(const struct ofp14_async_prop *prop, | |
303 | enum ofp_version version) | |
304 | { | |
305 | return version >= OFP14_VERSION ? prop->allowed14 : prop->allowed10; | |
306 | } | |
307 | ||
308 | static ovs_be32 | |
309 | encode_async_mask(const struct ofputil_async_cfg *src, | |
310 | const struct ofp14_async_prop *ap, | |
311 | enum ofp_version version) | |
312 | { | |
807152a4 BP |
313 | uint32_t mask = (ap->primary |
314 | ? src->primary[ap->oam] | |
315 | : src->secondary[ap->oam]); | |
0d71302e BP |
316 | return htonl(mask & ofp14_async_prop_allowed(ap, version)); |
317 | } | |
318 | ||
319 | static enum ofperr | |
320 | decode_async_mask(ovs_be32 src, | |
321 | const struct ofp14_async_prop *ap, enum ofp_version version, | |
322 | bool loose, struct ofputil_async_cfg *dst) | |
323 | { | |
324 | uint32_t mask = ntohl(src); | |
325 | uint32_t allowed = ofp14_async_prop_allowed(ap, version); | |
326 | if (mask & ~allowed) { | |
327 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
328 | OFPPROP_LOG(&rl, loose, | |
329 | "bad value %#x for %s (allowed mask %#x)", | |
330 | mask, ofputil_async_msg_type_to_string(ap->oam), | |
331 | allowed); | |
332 | mask &= allowed; | |
333 | if (!loose) { | |
334 | return OFPERR_OFPACFC_INVALID; | |
335 | } | |
336 | } | |
337 | ||
338 | if (ap->oam == OAM_PACKET_IN) { | |
339 | if (mask & (1u << OFPR_NO_MATCH)) { | |
340 | mask |= 1u << OFPR_EXPLICIT_MISS; | |
341 | if (version < OFP13_VERSION) { | |
342 | mask |= 1u << OFPR_IMPLICIT_MISS; | |
343 | } | |
344 | } | |
345 | } | |
346 | ||
807152a4 | 347 | uint32_t *array = ap->primary ? dst->primary : dst->secondary; |
0d71302e BP |
348 | array[ap->oam] = mask; |
349 | return 0; | |
350 | } | |
351 | ||
352 | static enum ofperr | |
353 | parse_async_tlv(const struct ofpbuf *property, | |
354 | const struct ofp14_async_prop *ap, | |
355 | struct ofputil_async_cfg *ac, | |
356 | enum ofp_version version, bool loose) | |
357 | { | |
358 | enum ofperr error; | |
359 | ovs_be32 mask; | |
360 | ||
361 | error = ofpprop_parse_be32(property, &mask); | |
362 | if (error) { | |
363 | return error; | |
364 | } | |
365 | ||
366 | if (ofpprop_is_experimenter(ap->prop_type)) { | |
807152a4 BP |
367 | /* For experimenter properties, whether a property is for the primary or |
368 | * secondary role is indicated by both 'type' and 'exp_type' in struct | |
0d71302e BP |
369 | * ofp_prop_experimenter. Check that these are consistent. */ |
370 | const struct ofp_prop_experimenter *ope = property->data; | |
807152a4 BP |
371 | bool should_be_primary = ope->type == htons(0xffff); |
372 | if (should_be_primary != ap->primary) { | |
0d71302e BP |
373 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); |
374 | VLOG_WARN_RL(&rl, "async property type %#"PRIx16" " | |
375 | "indicates %s role but exp_type %"PRIu32" indicates " | |
376 | "%s role", | |
377 | ntohs(ope->type), | |
807152a4 | 378 | should_be_primary ? "primary" : "secondary", |
0d71302e | 379 | ntohl(ope->exp_type), |
807152a4 | 380 | ap->primary ? "primary" : "secondary"); |
0d71302e BP |
381 | return OFPERR_OFPBPC_BAD_EXP_TYPE; |
382 | } | |
383 | } | |
384 | ||
385 | return decode_async_mask(mask, ap, version, loose, ac); | |
386 | } | |
387 | ||
388 | static void | |
389 | decode_legacy_async_masks(const ovs_be32 masks[2], | |
390 | enum ofputil_async_msg_type oam, | |
391 | enum ofp_version version, | |
392 | struct ofputil_async_cfg *dst) | |
393 | { | |
394 | for (int i = 0; i < 2; i++) { | |
807152a4 | 395 | bool primary = i == 0; |
0d71302e | 396 | const struct ofp14_async_prop *ap |
807152a4 | 397 | = get_ofp14_async_config_prop_by_oam(oam, primary); |
0d71302e BP |
398 | decode_async_mask(masks[i], ap, version, true, dst); |
399 | } | |
400 | } | |
401 | ||
402 | /* Decodes the OpenFlow "set async config" request and "get async config | |
403 | * reply" message in '*oh' into an abstract form in 'ac'. | |
404 | * | |
405 | * Some versions of the "set async config" request change only some of the | |
406 | * settings and leave the others alone. This function uses 'basis' as the | |
407 | * initial state for decoding these. Other versions of the request change all | |
408 | * the settings; this function ignores 'basis' when decoding these. | |
409 | * | |
410 | * If 'loose' is true, this function ignores properties and values that it does | |
411 | * not understand, as a controller would want to do when interpreting | |
412 | * capabilities provided by a switch. If 'loose' is false, this function | |
413 | * treats unknown properties and values as an error, as a switch would want to | |
414 | * do when interpreting a configuration request made by a controller. | |
415 | * | |
416 | * Returns 0 if successful, otherwise an OFPERR_* value. | |
417 | * | |
418 | * Returns error code OFPERR_OFPACFC_INVALID if the value of mask is not in | |
419 | * the valid range of mask. | |
420 | * | |
421 | * Returns error code OFPERR_OFPACFC_UNSUPPORTED if the configuration is not | |
422 | * supported.*/ | |
423 | enum ofperr | |
424 | ofputil_decode_set_async_config(const struct ofp_header *oh, bool loose, | |
425 | const struct ofputil_async_cfg *basis, | |
426 | struct ofputil_async_cfg *ac) | |
427 | { | |
428 | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); | |
429 | enum ofpraw raw = ofpraw_pull_assert(&b); | |
430 | ||
431 | if (raw == OFPRAW_OFPT13_SET_ASYNC || | |
432 | raw == OFPRAW_NXT_SET_ASYNC_CONFIG || | |
433 | raw == OFPRAW_OFPT13_GET_ASYNC_REPLY) { | |
434 | const struct nx_async_config *msg = ofpmsg_body(oh); | |
435 | ||
436 | *ac = OFPUTIL_ASYNC_CFG_INIT; | |
437 | decode_legacy_async_masks(msg->packet_in_mask, OAM_PACKET_IN, | |
438 | oh->version, ac); | |
439 | decode_legacy_async_masks(msg->port_status_mask, OAM_PORT_STATUS, | |
440 | oh->version, ac); | |
441 | decode_legacy_async_masks(msg->flow_removed_mask, OAM_FLOW_REMOVED, | |
442 | oh->version, ac); | |
443 | } else if (raw == OFPRAW_OFPT14_SET_ASYNC || | |
444 | raw == OFPRAW_OFPT14_GET_ASYNC_REPLY || | |
445 | raw == OFPRAW_NXT_SET_ASYNC_CONFIG2) { | |
446 | *ac = *basis; | |
447 | while (b.size > 0) { | |
448 | struct ofpbuf property; | |
449 | enum ofperr error; | |
450 | uint64_t type; | |
451 | ||
452 | error = ofpprop_pull__(&b, &property, 8, 0xfffe, &type); | |
453 | if (error) { | |
454 | return error; | |
455 | } | |
456 | ||
457 | const struct ofp14_async_prop *ap | |
458 | = get_ofp14_async_config_prop_by_prop_type(type); | |
459 | error = (ap | |
460 | ? parse_async_tlv(&property, ap, ac, oh->version, loose) | |
461 | : OFPPROP_UNKNOWN(loose, "async config", type)); | |
462 | if (error) { | |
463 | /* Most messages use OFPBPC_BAD_TYPE but async has its own (who | |
464 | * knows why, it's OpenFlow. */ | |
465 | if (error == OFPERR_OFPBPC_BAD_TYPE) { | |
466 | error = OFPERR_OFPACFC_UNSUPPORTED; | |
467 | } | |
468 | return error; | |
469 | } | |
470 | } | |
471 | } else { | |
472 | return OFPERR_OFPBRC_BAD_VERSION; | |
473 | } | |
474 | return 0; | |
475 | } | |
476 | ||
477 | static void | |
478 | encode_legacy_async_masks(const struct ofputil_async_cfg *ac, | |
479 | enum ofputil_async_msg_type oam, | |
480 | enum ofp_version version, | |
481 | ovs_be32 masks[2]) | |
482 | { | |
483 | for (int i = 0; i < 2; i++) { | |
807152a4 | 484 | bool primary = i == 0; |
0d71302e | 485 | const struct ofp14_async_prop *ap |
807152a4 | 486 | = get_ofp14_async_config_prop_by_oam(oam, primary); |
0d71302e BP |
487 | masks[i] = encode_async_mask(ac, ap, version); |
488 | } | |
489 | } | |
490 | ||
491 | static void | |
492 | ofputil_put_async_config__(const struct ofputil_async_cfg *ac, | |
493 | struct ofpbuf *buf, bool tlv, | |
494 | enum ofp_version version, uint32_t oams) | |
495 | { | |
496 | if (!tlv) { | |
497 | struct nx_async_config *msg = ofpbuf_put_zeros(buf, sizeof *msg); | |
498 | encode_legacy_async_masks(ac, OAM_PACKET_IN, version, | |
499 | msg->packet_in_mask); | |
500 | encode_legacy_async_masks(ac, OAM_PORT_STATUS, version, | |
501 | msg->port_status_mask); | |
502 | encode_legacy_async_masks(ac, OAM_FLOW_REMOVED, version, | |
503 | msg->flow_removed_mask); | |
504 | } else { | |
505 | FOR_EACH_ASYNC_PROP (ap) { | |
506 | if (oams & (1u << ap->oam)) { | |
507 | size_t ofs = buf->size; | |
508 | ofpprop_put_be32(buf, ap->prop_type, | |
509 | encode_async_mask(ac, ap, version)); | |
510 | ||
511 | /* For experimenter properties, we need to use type 0xfffe for | |
807152a4 | 512 | * primary and 0xffff for secondaries. */ |
0d71302e BP |
513 | if (ofpprop_is_experimenter(ap->prop_type)) { |
514 | struct ofp_prop_experimenter *ope | |
515 | = ofpbuf_at_assert(buf, ofs, sizeof *ope); | |
807152a4 | 516 | ope->type = ap->primary ? htons(0xffff) : htons(0xfffe); |
0d71302e BP |
517 | } |
518 | } | |
519 | } | |
520 | } | |
521 | } | |
522 | ||
523 | /* Encodes and returns a reply to the OFPT_GET_ASYNC_REQUEST in 'oh' that | |
524 | * states that the asynchronous message configuration is 'ac'. */ | |
525 | struct ofpbuf * | |
526 | ofputil_encode_get_async_reply(const struct ofp_header *oh, | |
527 | const struct ofputil_async_cfg *ac) | |
528 | { | |
529 | enum ofpraw raw = (oh->version < OFP14_VERSION | |
530 | ? OFPRAW_OFPT13_GET_ASYNC_REPLY | |
531 | : OFPRAW_OFPT14_GET_ASYNC_REPLY); | |
532 | struct ofpbuf *reply = ofpraw_alloc_reply(raw, oh, 0); | |
533 | ofputil_put_async_config__(ac, reply, | |
534 | raw == OFPRAW_OFPT14_GET_ASYNC_REPLY, | |
535 | oh->version, UINT32_MAX); | |
536 | return reply; | |
537 | } | |
538 | ||
539 | /* Encodes and returns a message, in a format appropriate for OpenFlow version | |
540 | * 'ofp_version', that sets the asynchronous message configuration to 'ac'. | |
541 | * | |
542 | * Specify 'oams' as a bitmap of OAM_* that indicate the asynchronous messages | |
543 | * to configure. OF1.0 through OF1.3 can't natively configure a subset of | |
544 | * messages, so more messages than requested may be configured. OF1.0 through | |
545 | * OF1.3 also can't configure OVS extension OAM_* values, so if 'oam' includes | |
546 | * any extensions then this function encodes an Open vSwitch extension message | |
547 | * that does support configuring OVS extension OAM_*. */ | |
548 | struct ofpbuf * | |
549 | ofputil_encode_set_async_config(const struct ofputil_async_cfg *ac, | |
550 | uint32_t oams, enum ofp_version ofp_version) | |
551 | { | |
552 | enum ofpraw raw = (ofp_version >= OFP14_VERSION ? OFPRAW_OFPT14_SET_ASYNC | |
553 | : oams & OAM_EXTENSIONS ? OFPRAW_NXT_SET_ASYNC_CONFIG2 | |
554 | : ofp_version >= OFP13_VERSION ? OFPRAW_OFPT13_SET_ASYNC | |
555 | : OFPRAW_NXT_SET_ASYNC_CONFIG); | |
556 | struct ofpbuf *request = ofpraw_alloc(raw, ofp_version, 0); | |
557 | ofputil_put_async_config__(ac, request, | |
558 | (raw == OFPRAW_OFPT14_SET_ASYNC || | |
559 | raw == OFPRAW_NXT_SET_ASYNC_CONFIG2), | |
560 | ofp_version, oams); | |
561 | return request; | |
562 | } | |
563 | ||
fe2c69f4 BP |
564 | /* Returns a string form of 'reason'. The return value is either a statically |
565 | * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. | |
566 | * 'bufsize' should be at least OFP_PORT_REASON_BUFSIZE. */ | |
567 | #define OFP_PORT_REASON_BUFSIZE (INT_STRLEN(int) + 1) | |
568 | static const char * | |
569 | ofp_port_reason_to_string(enum ofp_port_reason reason, | |
570 | char *reasonbuf, size_t bufsize) | |
571 | { | |
572 | switch (reason) { | |
573 | case OFPPR_ADD: | |
574 | return "add"; | |
575 | ||
576 | case OFPPR_DELETE: | |
577 | return "delete"; | |
578 | ||
579 | case OFPPR_MODIFY: | |
580 | return "modify"; | |
581 | ||
582 | case OFPPR_N_REASONS: | |
583 | default: | |
584 | snprintf(reasonbuf, bufsize, "%d", (int) reason); | |
585 | return reasonbuf; | |
586 | } | |
587 | } | |
588 | ||
589 | /* Returns a string form of 'reason'. The return value is either a statically | |
590 | * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. | |
591 | * 'bufsize' should be at least OFP_ASYNC_CONFIG_REASON_BUFSIZE. */ | |
592 | static const char* | |
593 | ofp_role_reason_to_string(enum ofp14_controller_role_reason reason, | |
594 | char *reasonbuf, size_t bufsize) | |
595 | { | |
596 | switch (reason) { | |
807152a4 BP |
597 | case OFPCRR_PRIMARY_REQUEST: |
598 | return "primary_request"; | |
fe2c69f4 BP |
599 | |
600 | case OFPCRR_CONFIG: | |
601 | return "configuration_changed"; | |
602 | ||
603 | case OFPCRR_EXPERIMENTER: | |
604 | return "experimenter_data_changed"; | |
605 | ||
606 | case OFPCRR_N_REASONS: | |
607 | default: | |
608 | snprintf(reasonbuf, bufsize, "%d", (int) reason); | |
609 | return reasonbuf; | |
610 | } | |
611 | } | |
612 | ||
613 | /* Returns a string form of 'reason'. The return value is either a statically | |
614 | * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. | |
615 | * 'bufsize' should be at least OFP_ASYNC_CONFIG_REASON_BUFSIZE. */ | |
616 | static const char* | |
617 | ofp_requestforward_reason_to_string(enum ofp14_requestforward_reason reason, | |
618 | char *reasonbuf, size_t bufsize) | |
619 | { | |
620 | switch (reason) { | |
621 | case OFPRFR_GROUP_MOD: | |
622 | return "group_mod_request"; | |
623 | ||
624 | case OFPRFR_METER_MOD: | |
625 | return "meter_mod_request"; | |
626 | ||
627 | case OFPRFR_N_REASONS: | |
628 | default: | |
629 | snprintf(reasonbuf, bufsize, "%d", (int) reason); | |
630 | return reasonbuf; | |
631 | } | |
632 | } | |
633 | ||
634 | static const char * | |
635 | ofp_async_config_reason_to_string(uint32_t reason, | |
636 | enum ofputil_async_msg_type type, | |
637 | char *reasonbuf, size_t bufsize) | |
638 | { | |
639 | switch (type) { | |
640 | case OAM_PACKET_IN: | |
641 | return ofputil_packet_in_reason_to_string(reason, reasonbuf, bufsize); | |
642 | ||
643 | case OAM_PORT_STATUS: | |
644 | return ofp_port_reason_to_string(reason, reasonbuf, bufsize); | |
645 | ||
646 | case OAM_FLOW_REMOVED: | |
647 | return ofp_flow_removed_reason_to_string(reason, reasonbuf, bufsize); | |
648 | ||
649 | case OAM_ROLE_STATUS: | |
650 | return ofp_role_reason_to_string(reason, reasonbuf, bufsize); | |
651 | ||
652 | case OAM_TABLE_STATUS: | |
653 | return ofp_table_reason_to_string(reason, reasonbuf, bufsize); | |
654 | ||
655 | case OAM_REQUESTFORWARD: | |
656 | return ofp_requestforward_reason_to_string(reason, reasonbuf, bufsize); | |
657 | ||
658 | case OAM_N_TYPES: | |
659 | default: | |
660 | return "Unknown asynchronous configuration message type"; | |
661 | } | |
662 | } | |
663 | ||
664 | void | |
665 | ofputil_format_set_async_config(struct ds *string, | |
666 | const struct ofputil_async_cfg *ac) | |
667 | { | |
668 | for (int i = 0; i < 2; i++) { | |
807152a4 | 669 | ds_put_format(string, "\n %s:\n", i == 0 ? "primary" : "secondary"); |
fe2c69f4 BP |
670 | for (uint32_t type = 0; type < OAM_N_TYPES; type++) { |
671 | ds_put_format(string, "%16s:", | |
672 | ofputil_async_msg_type_to_string(type)); | |
673 | ||
807152a4 | 674 | uint32_t role = i == 0 ? ac->primary[type] : ac->secondary[type]; |
fe2c69f4 BP |
675 | for (int j = 0; j < 32; j++) { |
676 | if (role & (1u << j)) { | |
677 | char reasonbuf[INT_STRLEN(int) + 1]; | |
678 | const char *reason; | |
679 | ||
680 | reason = ofp_async_config_reason_to_string( | |
681 | j, type, reasonbuf, sizeof reasonbuf); | |
682 | if (reason[0]) { | |
683 | ds_put_format(string, " %s", reason); | |
684 | } | |
685 | } | |
686 | } | |
687 | if (!role) { | |
688 | ds_put_cstr(string, " (off)"); | |
689 | } | |
690 | ds_put_char(string, '\n'); | |
691 | } | |
692 | } | |
693 | } | |
694 | ||
0d71302e BP |
695 | struct ofputil_async_cfg |
696 | ofputil_async_cfg_default(enum ofp_version version) | |
697 | { | |
698 | /* We enable all of the OF1.4 reasons regardless of 'version' because the | |
699 | * reasons added in OF1.4 just are just refinements of the OFPR_ACTION | |
700 | * introduced in OF1.0, breaking it into more specific categories. When we | |
701 | * encode these for earlier OpenFlow versions, we translate them into | |
702 | * OFPR_ACTION. */ | |
703 | uint32_t pin = OFPR14_BITS & ~(1u << OFPR_INVALID_TTL); | |
704 | pin |= 1u << OFPR_EXPLICIT_MISS; | |
705 | if (version <= OFP12_VERSION) { | |
706 | pin |= 1u << OFPR_IMPLICIT_MISS; | |
707 | } | |
708 | ||
709 | struct ofputil_async_cfg oac = { | |
807152a4 BP |
710 | .primary[OAM_PACKET_IN] = pin, |
711 | .primary[OAM_PORT_STATUS] = OFPPR_BITS, | |
712 | .secondary[OAM_PORT_STATUS] = OFPPR_BITS | |
0d71302e BP |
713 | }; |
714 | ||
715 | if (version >= OFP14_VERSION) { | |
807152a4 | 716 | oac.primary[OAM_FLOW_REMOVED] = OFPRR14_BITS; |
0d71302e | 717 | } else if (version == OFP13_VERSION) { |
807152a4 | 718 | oac.primary[OAM_FLOW_REMOVED] = OFPRR13_BITS; |
0d71302e | 719 | } else { |
807152a4 | 720 | oac.primary[OAM_FLOW_REMOVED] = OFPRR10_BITS; |
0d71302e BP |
721 | } |
722 | ||
723 | return oac; | |
724 | } |