/src/openvswitch/lib/ofp-connection.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-connection.h" |
19 | | #include "byte-order.h" |
20 | | #include "openflow/nicira-ext.h" |
21 | | #include "openvswitch/ofp-errors.h" |
22 | | #include "openvswitch/ofp-monitor.h" |
23 | | #include "openvswitch/ofp-msgs.h" |
24 | | #include "openvswitch/ofp-packet.h" |
25 | | #include "openvswitch/ofp-prop.h" |
26 | | #include "openvswitch/ofp-table.h" |
27 | | #include "openvswitch/ofpbuf.h" |
28 | | #include "openvswitch/type-props.h" |
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 | 13.2k | { |
43 | 13.2k | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); |
44 | 13.2k | enum ofpraw raw = ofpraw_pull_assert(&b); |
45 | 13.2k | if (raw == OFPRAW_OFPT12_ROLE_REQUEST || |
46 | 13.2k | raw == OFPRAW_OFPT12_ROLE_REPLY) { |
47 | 11.6k | const struct ofp12_role_request *orr = b.msg; |
48 | | |
49 | 11.6k | if (orr->role != htonl(OFPCR12_ROLE_NOCHANGE) && |
50 | 11.6k | orr->role != htonl(OFPCR12_ROLE_EQUAL) && |
51 | 11.6k | orr->role != htonl(OFPCR12_ROLE_PRIMARY) && |
52 | 11.6k | orr->role != htonl(OFPCR12_ROLE_SECONDARY)) { |
53 | 3.27k | return OFPERR_OFPRRFC_BAD_ROLE; |
54 | 3.27k | } |
55 | | |
56 | 8.38k | rr->role = ntohl(orr->role); |
57 | 8.38k | if (raw == OFPRAW_OFPT12_ROLE_REQUEST |
58 | 8.38k | ? orr->role == htonl(OFPCR12_ROLE_NOCHANGE) |
59 | 8.38k | : orr->generation_id == OVS_BE64_MAX) { |
60 | 124 | rr->have_generation_id = false; |
61 | 124 | rr->generation_id = 0; |
62 | 8.26k | } else { |
63 | 8.26k | rr->have_generation_id = true; |
64 | 8.26k | rr->generation_id = ntohll(orr->generation_id); |
65 | 8.26k | } |
66 | 8.38k | } else if (raw == OFPRAW_NXT_ROLE_REQUEST || |
67 | 1.56k | raw == OFPRAW_NXT_ROLE_REPLY) { |
68 | 1.56k | const struct nx_role_request *nrr = b.msg; |
69 | | |
70 | 1.56k | BUILD_ASSERT(NX_ROLE_OTHER + 1 == OFPCR12_ROLE_EQUAL); |
71 | 1.56k | BUILD_ASSERT(NX_ROLE_PRIMARY + 1 == OFPCR12_ROLE_PRIMARY); |
72 | 1.56k | BUILD_ASSERT(NX_ROLE_SECONDARY + 1 == OFPCR12_ROLE_SECONDARY); |
73 | | |
74 | 1.56k | if (nrr->role != htonl(NX_ROLE_OTHER) && |
75 | 1.56k | nrr->role != htonl(NX_ROLE_PRIMARY) && |
76 | 1.56k | nrr->role != htonl(NX_ROLE_SECONDARY)) { |
77 | 854 | return OFPERR_OFPRRFC_BAD_ROLE; |
78 | 854 | } |
79 | | |
80 | 707 | rr->role = ntohl(nrr->role) + 1; |
81 | 707 | rr->have_generation_id = false; |
82 | 707 | rr->generation_id = 0; |
83 | 707 | } else { |
84 | 0 | OVS_NOT_REACHED(); |
85 | 0 | } |
86 | | |
87 | 9.09k | return 0; |
88 | 13.2k | } |
89 | | |
90 | | static void |
91 | | format_role_generic(struct ds *string, enum ofp12_controller_role role, |
92 | | uint64_t generation_id) |
93 | 9.72k | { |
94 | 9.72k | ds_put_cstr(string, " role="); |
95 | | |
96 | 9.72k | switch (role) { |
97 | 292 | case OFPCR12_ROLE_NOCHANGE: |
98 | 292 | ds_put_cstr(string, "nochange"); |
99 | 292 | break; |
100 | 8.92k | case OFPCR12_ROLE_EQUAL: |
101 | 8.92k | ds_put_cstr(string, "equal"); /* OF 1.2 wording */ |
102 | 8.92k | break; |
103 | 226 | case OFPCR12_ROLE_PRIMARY: |
104 | 226 | ds_put_cstr(string, "primary"); |
105 | 226 | break; |
106 | 284 | case OFPCR12_ROLE_SECONDARY: |
107 | 284 | ds_put_cstr(string, "secondary"); |
108 | 284 | break; |
109 | 0 | default: |
110 | 0 | OVS_NOT_REACHED(); |
111 | 9.72k | } |
112 | | |
113 | 9.72k | if (generation_id != UINT64_MAX) { |
114 | 8.89k | ds_put_format(string, " generation_id=%"PRIu64, generation_id); |
115 | 8.89k | } |
116 | 9.72k | } |
117 | | |
118 | | void |
119 | | ofputil_format_role_message(struct ds *string, |
120 | | const struct ofputil_role_request *rr) |
121 | 9.09k | { |
122 | 9.09k | format_role_generic(string, rr->role, (rr->have_generation_id |
123 | 9.09k | ? rr->generation_id |
124 | 9.09k | : UINT64_MAX)); |
125 | 9.09k | } |
126 | | |
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 | 0 | { |
133 | 0 | struct ofpbuf *buf; |
134 | 0 | enum ofpraw raw; |
135 | |
|
136 | 0 | raw = ofpraw_decode_assert(request); |
137 | 0 | if (raw == OFPRAW_OFPT12_ROLE_REQUEST) { |
138 | 0 | struct ofp12_role_request *orr; |
139 | |
|
140 | 0 | buf = ofpraw_alloc_reply(OFPRAW_OFPT12_ROLE_REPLY, request, 0); |
141 | 0 | orr = ofpbuf_put_zeros(buf, sizeof *orr); |
142 | |
|
143 | 0 | orr->role = htonl(rr->role); |
144 | 0 | orr->generation_id = htonll(rr->have_generation_id |
145 | 0 | ? rr->generation_id |
146 | 0 | : UINT64_MAX); |
147 | 0 | } else if (raw == OFPRAW_NXT_ROLE_REQUEST) { |
148 | 0 | struct nx_role_request *nrr; |
149 | |
|
150 | 0 | BUILD_ASSERT(NX_ROLE_OTHER == OFPCR12_ROLE_EQUAL - 1); |
151 | 0 | BUILD_ASSERT(NX_ROLE_PRIMARY == OFPCR12_ROLE_PRIMARY - 1); |
152 | 0 | BUILD_ASSERT(NX_ROLE_SECONDARY == OFPCR12_ROLE_SECONDARY - 1); |
153 | |
|
154 | 0 | buf = ofpraw_alloc_reply(OFPRAW_NXT_ROLE_REPLY, request, 0); |
155 | 0 | nrr = ofpbuf_put_zeros(buf, sizeof *nrr); |
156 | 0 | nrr->role = htonl(rr->role - 1); |
157 | 0 | } else { |
158 | 0 | OVS_NOT_REACHED(); |
159 | 0 | } |
160 | | |
161 | 0 | return buf; |
162 | 0 | } |
163 | | |
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 | 0 | { |
171 | 0 | enum ofp_version version = ofputil_protocol_to_ofp_version(protocol); |
172 | 0 | if (version < OFP13_VERSION) { |
173 | 0 | return NULL; |
174 | 0 | } |
175 | | |
176 | 0 | enum ofpraw raw = (version >= OFP14_VERSION |
177 | 0 | ? OFPRAW_OFPT14_ROLE_STATUS |
178 | 0 | : OFPRAW_ONFT13_ROLE_STATUS); |
179 | 0 | struct ofpbuf *buf = ofpraw_alloc_xid(raw, version, htonl(0), 0); |
180 | 0 | struct ofp14_role_status *rstatus = ofpbuf_put_zeros(buf, sizeof *rstatus); |
181 | 0 | rstatus->role = htonl(status->role); |
182 | 0 | rstatus->reason = status->reason; |
183 | 0 | rstatus->generation_id = htonll(status->generation_id); |
184 | |
|
185 | 0 | return buf; |
186 | 0 | } |
187 | | |
188 | | enum ofperr |
189 | | ofputil_decode_role_status(const struct ofp_header *oh, |
190 | | struct ofputil_role_status *rs) |
191 | 1.22k | { |
192 | 1.22k | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); |
193 | 1.22k | enum ofpraw raw = ofpraw_pull_assert(&b); |
194 | 1.22k | ovs_assert(raw == OFPRAW_OFPT14_ROLE_STATUS || |
195 | 1.22k | raw == OFPRAW_ONFT13_ROLE_STATUS); |
196 | | |
197 | 1.22k | const struct ofp14_role_status *r = b.msg; |
198 | 1.22k | if (r->role != htonl(OFPCR12_ROLE_NOCHANGE) && |
199 | 1.22k | r->role != htonl(OFPCR12_ROLE_EQUAL) && |
200 | 1.22k | r->role != htonl(OFPCR12_ROLE_PRIMARY) && |
201 | 1.22k | r->role != htonl(OFPCR12_ROLE_SECONDARY)) { |
202 | 591 | return OFPERR_OFPRRFC_BAD_ROLE; |
203 | 591 | } |
204 | | |
205 | 636 | rs->role = ntohl(r->role); |
206 | 636 | rs->generation_id = ntohll(r->generation_id); |
207 | 636 | rs->reason = r->reason; |
208 | | |
209 | 636 | return 0; |
210 | 1.22k | } |
211 | | |
212 | | void |
213 | | ofputil_format_role_status(struct ds *string, |
214 | | const struct ofputil_role_status *rs) |
215 | 636 | { |
216 | 636 | format_role_generic(string, rs->role, rs->generation_id); |
217 | | |
218 | 636 | ds_put_cstr(string, " reason="); |
219 | | |
220 | 636 | switch (rs->reason) { |
221 | 213 | case OFPCRR_PRIMARY_REQUEST: |
222 | 213 | ds_put_cstr(string, "primary_request"); |
223 | 213 | break; |
224 | 1 | case OFPCRR_CONFIG: |
225 | 1 | ds_put_cstr(string, "configuration_changed"); |
226 | 1 | break; |
227 | 2 | case OFPCRR_EXPERIMENTER: |
228 | 2 | ds_put_cstr(string, "experimenter_data_changed"); |
229 | 2 | break; |
230 | 1 | case OFPCRR_N_REASONS: |
231 | 420 | default: |
232 | 420 | ds_put_cstr(string, "(unknown)"); |
233 | 420 | break; |
234 | 636 | } |
235 | 636 | } |
236 | | |
237 | | const char * |
238 | | ofputil_async_msg_type_to_string(enum ofputil_async_msg_type type) |
239 | 269k | { |
240 | 269k | switch (type) { |
241 | 44.9k | case OAM_PACKET_IN: return "PACKET_IN"; |
242 | 44.9k | case OAM_PORT_STATUS: return "PORT_STATUS"; |
243 | 44.9k | case OAM_FLOW_REMOVED: return "FLOW_REMOVED"; |
244 | 44.9k | case OAM_ROLE_STATUS: return "ROLE_STATUS"; |
245 | 44.9k | case OAM_TABLE_STATUS: return "TABLE_STATUS"; |
246 | 44.9k | case OAM_REQUESTFORWARD: return "REQUESTFORWARD"; |
247 | | |
248 | 0 | case OAM_N_TYPES: |
249 | 0 | default: |
250 | 0 | OVS_NOT_REACHED(); |
251 | 269k | } |
252 | 269k | } |
253 | | |
254 | | struct ofp14_async_prop { |
255 | | uint64_t prop_type; |
256 | | enum ofputil_async_msg_type oam; |
257 | | bool primary; |
258 | | uint32_t allowed10, allowed14; |
259 | | }; |
260 | | |
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) } |
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 | 37.1k | for (const struct ofp14_async_prop *VAR = async_props; \ |
276 | 257k | 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 | 26.1k | { |
281 | 211k | FOR_EACH_ASYNC_PROP (ap) { |
282 | 211k | if (prop_type == ap->prop_type) { |
283 | 18.7k | return ap; |
284 | 18.7k | } |
285 | 211k | } |
286 | 7.35k | return NULL; |
287 | 26.1k | } |
288 | | |
289 | | static const struct ofp14_async_prop * |
290 | | get_ofp14_async_config_prop_by_oam(enum ofputil_async_msg_type oam, |
291 | | bool primary) |
292 | 10.9k | { |
293 | 38.4k | FOR_EACH_ASYNC_PROP (ap) { |
294 | 38.4k | if (ap->oam == oam && ap->primary == primary) { |
295 | 10.9k | return ap; |
296 | 10.9k | } |
297 | 38.4k | } |
298 | 0 | return NULL; |
299 | 10.9k | } |
300 | | |
301 | | static uint32_t |
302 | | ofp14_async_prop_allowed(const struct ofp14_async_prop *prop, |
303 | | enum ofp_version version) |
304 | 28.3k | { |
305 | 28.3k | return version >= OFP14_VERSION ? prop->allowed14 : prop->allowed10; |
306 | 28.3k | } |
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 | 0 | { |
313 | 0 | uint32_t mask = (ap->primary |
314 | 0 | ? src->primary[ap->oam] |
315 | 0 | : src->secondary[ap->oam]); |
316 | 0 | return htonl(mask & ofp14_async_prop_allowed(ap, version)); |
317 | 0 | } |
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 | 28.3k | { |
324 | 28.3k | uint32_t mask = ntohl(src); |
325 | 28.3k | uint32_t allowed = ofp14_async_prop_allowed(ap, version); |
326 | 28.3k | if (mask & ~allowed) { |
327 | 21.4k | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); |
328 | 21.4k | OFPPROP_LOG(&rl, loose, |
329 | 21.4k | "bad value %#x for %s (allowed mask %#x)", |
330 | 21.4k | mask, ofputil_async_msg_type_to_string(ap->oam), |
331 | 21.4k | allowed); |
332 | 21.4k | mask &= allowed; |
333 | 21.4k | if (!loose) { |
334 | 1.53k | return OFPERR_OFPACFC_INVALID; |
335 | 1.53k | } |
336 | 21.4k | } |
337 | | |
338 | 26.7k | if (ap->oam == OAM_PACKET_IN) { |
339 | 7.64k | if (mask & (1u << OFPR_NO_MATCH)) { |
340 | 5.30k | mask |= 1u << OFPR_EXPLICIT_MISS; |
341 | 5.30k | if (version < OFP13_VERSION) { |
342 | 379 | mask |= 1u << OFPR_IMPLICIT_MISS; |
343 | 379 | } |
344 | 5.30k | } |
345 | 7.64k | } |
346 | | |
347 | 26.7k | uint32_t *array = ap->primary ? dst->primary : dst->secondary; |
348 | 26.7k | array[ap->oam] = mask; |
349 | 26.7k | return 0; |
350 | 28.3k | } |
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 | 18.7k | { |
358 | 18.7k | enum ofperr error; |
359 | 18.7k | ovs_be32 mask; |
360 | | |
361 | 18.7k | error = ofpprop_parse_be32(property, &mask); |
362 | 18.7k | if (error) { |
363 | 1.47k | return error; |
364 | 1.47k | } |
365 | | |
366 | 17.3k | if (ofpprop_is_experimenter(ap->prop_type)) { |
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 |
369 | | * ofp_prop_experimenter. Check that these are consistent. */ |
370 | 0 | const struct ofp_prop_experimenter *ope = property->data; |
371 | 0 | bool should_be_primary = ope->type == htons(0xffff); |
372 | 0 | if (should_be_primary != ap->primary) { |
373 | 0 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); |
374 | 0 | VLOG_WARN_RL(&rl, "async property type %#"PRIx16" " |
375 | 0 | "indicates %s role but exp_type %"PRIu32" indicates " |
376 | 0 | "%s role", |
377 | 0 | ntohs(ope->type), |
378 | 0 | should_be_primary ? "primary" : "secondary", |
379 | 0 | ntohl(ope->exp_type), |
380 | 0 | ap->primary ? "primary" : "secondary"); |
381 | 0 | return OFPERR_OFPBPC_BAD_EXP_TYPE; |
382 | 0 | } |
383 | 0 | } |
384 | | |
385 | 17.3k | return decode_async_mask(mask, ap, version, loose, ac); |
386 | 17.3k | } |
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 | 5.49k | { |
394 | 16.4k | for (int i = 0; i < 2; i++) { |
395 | 10.9k | bool primary = i == 0; |
396 | 10.9k | const struct ofp14_async_prop *ap |
397 | 10.9k | = get_ofp14_async_config_prop_by_oam(oam, primary); |
398 | 10.9k | decode_async_mask(masks[i], ap, version, true, dst); |
399 | 10.9k | } |
400 | 5.49k | } |
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 | 34.4k | { |
428 | 34.4k | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); |
429 | 34.4k | enum ofpraw raw = ofpraw_pull_assert(&b); |
430 | | |
431 | 34.4k | if (raw == OFPRAW_OFPT13_SET_ASYNC || |
432 | 34.4k | raw == OFPRAW_NXT_SET_ASYNC_CONFIG || |
433 | 34.4k | raw == OFPRAW_OFPT13_GET_ASYNC_REPLY) { |
434 | 1.83k | const struct nx_async_config *msg = ofpmsg_body(oh); |
435 | | |
436 | 1.83k | *ac = OFPUTIL_ASYNC_CFG_INIT; |
437 | 1.83k | decode_legacy_async_masks(msg->packet_in_mask, OAM_PACKET_IN, |
438 | 1.83k | oh->version, ac); |
439 | 1.83k | decode_legacy_async_masks(msg->port_status_mask, OAM_PORT_STATUS, |
440 | 1.83k | oh->version, ac); |
441 | 1.83k | decode_legacy_async_masks(msg->flow_removed_mask, OAM_FLOW_REMOVED, |
442 | 1.83k | oh->version, ac); |
443 | 32.6k | } else if (raw == OFPRAW_OFPT14_SET_ASYNC || |
444 | 32.6k | raw == OFPRAW_OFPT14_GET_ASYNC_REPLY || |
445 | 32.6k | raw == OFPRAW_NXT_SET_ASYNC_CONFIG2) { |
446 | 32.6k | *ac = *basis; |
447 | 54.1k | while (b.size > 0) { |
448 | 33.4k | struct ofpbuf property; |
449 | 33.4k | enum ofperr error; |
450 | 33.4k | uint64_t type; |
451 | | |
452 | 33.4k | error = ofpprop_pull__(&b, &property, 8, 0xfffe, &type); |
453 | 33.4k | if (error) { |
454 | 7.33k | return error; |
455 | 7.33k | } |
456 | | |
457 | 26.1k | const struct ofp14_async_prop *ap |
458 | 26.1k | = get_ofp14_async_config_prop_by_prop_type(type); |
459 | 26.1k | error = (ap |
460 | 26.1k | ? parse_async_tlv(&property, ap, ac, oh->version, loose) |
461 | 26.1k | : OFPPROP_UNKNOWN(loose, "async config", type)); |
462 | 26.1k | if (error) { |
463 | | /* Most messages use OFPBPC_BAD_TYPE but async has its own (who |
464 | | * knows why, it's OpenFlow. */ |
465 | 4.61k | if (error == OFPERR_OFPBPC_BAD_TYPE) { |
466 | 1.58k | error = OFPERR_OFPACFC_UNSUPPORTED; |
467 | 1.58k | } |
468 | 4.61k | return error; |
469 | 4.61k | } |
470 | 26.1k | } |
471 | 32.6k | } else { |
472 | 0 | return OFPERR_OFPBRC_BAD_VERSION; |
473 | 0 | } |
474 | 22.4k | return 0; |
475 | 34.4k | } |
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 | 0 | { |
483 | 0 | for (int i = 0; i < 2; i++) { |
484 | 0 | bool primary = i == 0; |
485 | 0 | const struct ofp14_async_prop *ap |
486 | 0 | = get_ofp14_async_config_prop_by_oam(oam, primary); |
487 | 0 | masks[i] = encode_async_mask(ac, ap, version); |
488 | 0 | } |
489 | 0 | } |
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 | 0 | { |
496 | 0 | if (!tlv) { |
497 | 0 | struct nx_async_config *msg = ofpbuf_put_zeros(buf, sizeof *msg); |
498 | 0 | encode_legacy_async_masks(ac, OAM_PACKET_IN, version, |
499 | 0 | msg->packet_in_mask); |
500 | 0 | encode_legacy_async_masks(ac, OAM_PORT_STATUS, version, |
501 | 0 | msg->port_status_mask); |
502 | 0 | encode_legacy_async_masks(ac, OAM_FLOW_REMOVED, version, |
503 | 0 | msg->flow_removed_mask); |
504 | 0 | } else { |
505 | 0 | FOR_EACH_ASYNC_PROP (ap) { |
506 | 0 | if (oams & (1u << ap->oam)) { |
507 | 0 | size_t ofs = buf->size; |
508 | 0 | ofpprop_put_be32(buf, ap->prop_type, |
509 | 0 | encode_async_mask(ac, ap, version)); |
510 | | |
511 | | /* For experimenter properties, we need to use type 0xfffe for |
512 | | * primary and 0xffff for secondaries. */ |
513 | 0 | if (ofpprop_is_experimenter(ap->prop_type)) { |
514 | 0 | struct ofp_prop_experimenter *ope |
515 | 0 | = ofpbuf_at_assert(buf, ofs, sizeof *ope); |
516 | 0 | ope->type = ap->primary ? htons(0xffff) : htons(0xfffe); |
517 | 0 | } |
518 | 0 | } |
519 | 0 | } |
520 | 0 | } |
521 | 0 | } |
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 | 0 | { |
529 | 0 | enum ofpraw raw = (oh->version < OFP14_VERSION |
530 | 0 | ? OFPRAW_OFPT13_GET_ASYNC_REPLY |
531 | 0 | : OFPRAW_OFPT14_GET_ASYNC_REPLY); |
532 | 0 | struct ofpbuf *reply = ofpraw_alloc_reply(raw, oh, 0); |
533 | 0 | ofputil_put_async_config__(ac, reply, |
534 | 0 | raw == OFPRAW_OFPT14_GET_ASYNC_REPLY, |
535 | 0 | oh->version, UINT32_MAX); |
536 | 0 | return reply; |
537 | 0 | } |
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 | 0 | { |
552 | 0 | enum ofpraw raw = (ofp_version >= OFP14_VERSION ? OFPRAW_OFPT14_SET_ASYNC |
553 | 0 | : oams & OAM_EXTENSIONS ? OFPRAW_NXT_SET_ASYNC_CONFIG2 |
554 | 0 | : ofp_version >= OFP13_VERSION ? OFPRAW_OFPT13_SET_ASYNC |
555 | 0 | : OFPRAW_NXT_SET_ASYNC_CONFIG); |
556 | 0 | struct ofpbuf *request = ofpraw_alloc(raw, ofp_version, 0); |
557 | 0 | ofputil_put_async_config__(ac, request, |
558 | 0 | (raw == OFPRAW_OFPT14_SET_ASYNC || |
559 | 0 | raw == OFPRAW_NXT_SET_ASYNC_CONFIG2), |
560 | 0 | ofp_version, oams); |
561 | 0 | return request; |
562 | 0 | } |
563 | | |
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 | 11.3k | { |
572 | 11.3k | switch (reason) { |
573 | 3.99k | case OFPPR_ADD: |
574 | 3.99k | return "add"; |
575 | | |
576 | 3.69k | case OFPPR_DELETE: |
577 | 3.69k | return "delete"; |
578 | | |
579 | 3.69k | case OFPPR_MODIFY: |
580 | 3.69k | return "modify"; |
581 | | |
582 | 0 | case OFPPR_N_REASONS: |
583 | 0 | default: |
584 | 0 | snprintf(reasonbuf, bufsize, "%d", (int) reason); |
585 | 0 | return reasonbuf; |
586 | 11.3k | } |
587 | 11.3k | } |
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 | 4.57k | { |
596 | 4.57k | switch (reason) { |
597 | 1.54k | case OFPCRR_PRIMARY_REQUEST: |
598 | 1.54k | return "primary_request"; |
599 | | |
600 | 1.49k | case OFPCRR_CONFIG: |
601 | 1.49k | return "configuration_changed"; |
602 | | |
603 | 1.53k | case OFPCRR_EXPERIMENTER: |
604 | 1.53k | return "experimenter_data_changed"; |
605 | | |
606 | 0 | case OFPCRR_N_REASONS: |
607 | 0 | default: |
608 | 0 | snprintf(reasonbuf, bufsize, "%d", (int) reason); |
609 | 0 | return reasonbuf; |
610 | 4.57k | } |
611 | 4.57k | } |
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 | 6.91k | { |
620 | 6.91k | switch (reason) { |
621 | 3.44k | case OFPRFR_GROUP_MOD: |
622 | 3.44k | return "group_mod_request"; |
623 | | |
624 | 3.46k | case OFPRFR_METER_MOD: |
625 | 3.46k | return "meter_mod_request"; |
626 | | |
627 | 0 | case OFPRFR_N_REASONS: |
628 | 0 | default: |
629 | 0 | snprintf(reasonbuf, bufsize, "%d", (int) reason); |
630 | 0 | return reasonbuf; |
631 | 6.91k | } |
632 | 6.91k | } |
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 | 70.1k | { |
639 | 70.1k | switch (type) { |
640 | 30.7k | case OAM_PACKET_IN: |
641 | 30.7k | return ofputil_packet_in_reason_to_string(reason, reasonbuf, bufsize); |
642 | | |
643 | 11.3k | case OAM_PORT_STATUS: |
644 | 11.3k | return ofp_port_reason_to_string(reason, reasonbuf, bufsize); |
645 | | |
646 | 10.7k | case OAM_FLOW_REMOVED: |
647 | 10.7k | return ofp_flow_removed_reason_to_string(reason, reasonbuf, bufsize); |
648 | | |
649 | 4.57k | case OAM_ROLE_STATUS: |
650 | 4.57k | return ofp_role_reason_to_string(reason, reasonbuf, bufsize); |
651 | | |
652 | 5.81k | case OAM_TABLE_STATUS: |
653 | 5.81k | return ofp_table_reason_to_string(reason, reasonbuf, bufsize); |
654 | | |
655 | 6.91k | case OAM_REQUESTFORWARD: |
656 | 6.91k | return ofp_requestforward_reason_to_string(reason, reasonbuf, bufsize); |
657 | | |
658 | 0 | case OAM_N_TYPES: |
659 | 0 | default: |
660 | 0 | return "Unknown asynchronous configuration message type"; |
661 | 70.1k | } |
662 | 70.1k | } |
663 | | |
664 | | void |
665 | | ofputil_format_set_async_config(struct ds *string, |
666 | | const struct ofputil_async_cfg *ac) |
667 | 22.4k | { |
668 | 67.4k | for (int i = 0; i < 2; i++) { |
669 | 44.9k | ds_put_format(string, "\n %s:\n", i == 0 ? "primary" : "secondary"); |
670 | 314k | for (uint32_t type = 0; type < OAM_N_TYPES; type++) { |
671 | 269k | ds_put_format(string, "%16s:", |
672 | 269k | ofputil_async_msg_type_to_string(type)); |
673 | | |
674 | 269k | uint32_t role = i == 0 ? ac->primary[type] : ac->secondary[type]; |
675 | 8.90M | for (int j = 0; j < 32; j++) { |
676 | 8.63M | if (role & (1u << j)) { |
677 | 70.1k | char reasonbuf[INT_STRLEN(int) + 1]; |
678 | 70.1k | const char *reason; |
679 | | |
680 | 70.1k | reason = ofp_async_config_reason_to_string( |
681 | 70.1k | j, type, reasonbuf, sizeof reasonbuf); |
682 | 70.1k | if (reason[0]) { |
683 | 64.7k | ds_put_format(string, " %s", reason); |
684 | 64.7k | } |
685 | 70.1k | } |
686 | 8.63M | } |
687 | 269k | if (!role) { |
688 | 249k | ds_put_cstr(string, " (off)"); |
689 | 249k | } |
690 | 269k | ds_put_char(string, '\n'); |
691 | 269k | } |
692 | 44.9k | } |
693 | 22.4k | } |
694 | | |
695 | | struct ofputil_async_cfg |
696 | | ofputil_async_cfg_default(enum ofp_version version) |
697 | 0 | { |
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 | 0 | uint32_t pin = OFPR14_BITS & ~(1u << OFPR_INVALID_TTL); |
704 | 0 | pin |= 1u << OFPR_EXPLICIT_MISS; |
705 | 0 | if (version <= OFP12_VERSION) { |
706 | 0 | pin |= 1u << OFPR_IMPLICIT_MISS; |
707 | 0 | } |
708 | |
|
709 | 0 | struct ofputil_async_cfg oac = { |
710 | 0 | .primary[OAM_PACKET_IN] = pin, |
711 | 0 | .primary[OAM_PORT_STATUS] = OFPPR_BITS, |
712 | 0 | .secondary[OAM_PORT_STATUS] = OFPPR_BITS |
713 | 0 | }; |
714 | |
|
715 | 0 | if (version >= OFP14_VERSION) { |
716 | 0 | oac.primary[OAM_FLOW_REMOVED] = OFPRR14_BITS; |
717 | 0 | } else if (version == OFP13_VERSION) { |
718 | 0 | oac.primary[OAM_FLOW_REMOVED] = OFPRR13_BITS; |
719 | 0 | } else { |
720 | 0 | oac.primary[OAM_FLOW_REMOVED] = OFPRR10_BITS; |
721 | 0 | } |
722 | |
|
723 | 0 | return oac; |
724 | 0 | } |