]>
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-monitor.h" | |
19 | #include "byte-order.h" | |
20 | #include "nx-match.h" | |
21 | #include "ovs-atomic.h" | |
22 | #include "openvswitch/ofp-actions.h" | |
23 | #include "openvswitch/ofp-errors.h" | |
24 | #include "openvswitch/ofp-group.h" | |
25 | #include "openvswitch/ofp-match.h" | |
26 | #include "openvswitch/ofp-meter.h" | |
27 | #include "openvswitch/ofp-msgs.h" | |
28 | #include "openvswitch/ofp-parse.h" | |
dfc77282 | 29 | #include "openvswitch/ofp-print.h" |
0d71302e BP |
30 | #include "openvswitch/ofp-table.h" |
31 | #include "openvswitch/vlog.h" | |
c7b02b80 | 32 | #include "ox-stat.h" |
0d71302e BP |
33 | |
34 | VLOG_DEFINE_THIS_MODULE(ofp_monitor); | |
35 | ||
36 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
37 | ||
dfc77282 BP |
38 | /* Returns a string form of 'reason'. The return value is either a statically |
39 | * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. | |
40 | * 'bufsize' should be at least OFP_FLOW_REMOVED_REASON_BUFSIZE. */ | |
dfc77282 BP |
41 | const char * |
42 | ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason, | |
43 | char *reasonbuf, size_t bufsize) | |
44 | { | |
45 | switch (reason) { | |
46 | case OFPRR_IDLE_TIMEOUT: | |
47 | return "idle"; | |
48 | case OFPRR_HARD_TIMEOUT: | |
49 | return "hard"; | |
50 | case OFPRR_DELETE: | |
51 | return "delete"; | |
52 | case OFPRR_GROUP_DELETE: | |
53 | return "group_delete"; | |
54 | case OFPRR_EVICTION: | |
55 | return "eviction"; | |
56 | case OFPRR_METER_DELETE: | |
57 | return "meter_delete"; | |
58 | case OVS_OFPRR_NONE: | |
59 | default: | |
60 | snprintf(reasonbuf, bufsize, "%d", (int) reason); | |
61 | return reasonbuf; | |
62 | } | |
63 | } | |
64 | ||
0d71302e BP |
65 | /* Converts an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message 'oh' into an |
66 | * abstract ofputil_flow_removed in 'fr'. Returns 0 if successful, otherwise | |
67 | * an OpenFlow error code. */ | |
68 | enum ofperr | |
69 | ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, | |
70 | const struct ofp_header *oh) | |
71 | { | |
72 | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); | |
73 | enum ofpraw raw = ofpraw_pull_assert(&b); | |
c7b02b80 S |
74 | if (raw == OFPRAW_OFPT15_FLOW_REMOVED) { |
75 | const struct ofp15_flow_removed *ofr; | |
76 | enum ofperr error; | |
77 | ||
78 | ofr = ofpbuf_pull(&b, sizeof *ofr); | |
79 | ||
80 | error = ofputil_pull_ofp11_match(&b, NULL, NULL, &fr->match, NULL); | |
81 | if (error) { | |
82 | return error; | |
83 | } | |
84 | ||
85 | struct oxs_stats stats; | |
86 | uint16_t statlen; | |
87 | uint8_t oxs_field_set; | |
88 | error = oxs_pull_stat(&b, &stats, &statlen, &oxs_field_set); | |
89 | if (error) { | |
90 | return error; | |
91 | } | |
92 | ||
93 | fr->cookie = ofr->cookie; | |
94 | fr->priority = ntohs(ofr->priority); | |
95 | fr->reason = ofr->reason; | |
96 | fr->table_id = ofr->table_id; | |
97 | fr->duration_sec = stats.duration_sec; | |
98 | fr->duration_nsec = stats.duration_nsec; | |
99 | fr->idle_timeout = ntohs(ofr->idle_timeout); | |
100 | fr->hard_timeout = ntohs(ofr->hard_timeout); | |
101 | fr->packet_count = stats.packet_count; | |
102 | fr->byte_count = stats.byte_count; | |
103 | } else if (raw == OFPRAW_OFPT11_FLOW_REMOVED) { | |
0d71302e BP |
104 | const struct ofp12_flow_removed *ofr; |
105 | enum ofperr error; | |
106 | ||
107 | ofr = ofpbuf_pull(&b, sizeof *ofr); | |
108 | ||
109 | error = ofputil_pull_ofp11_match(&b, NULL, NULL, &fr->match, NULL); | |
110 | if (error) { | |
111 | return error; | |
112 | } | |
113 | ||
114 | fr->priority = ntohs(ofr->priority); | |
115 | fr->cookie = ofr->cookie; | |
116 | fr->reason = ofr->reason; | |
117 | fr->table_id = ofr->table_id; | |
118 | fr->duration_sec = ntohl(ofr->duration_sec); | |
119 | fr->duration_nsec = ntohl(ofr->duration_nsec); | |
120 | fr->idle_timeout = ntohs(ofr->idle_timeout); | |
121 | fr->hard_timeout = ntohs(ofr->hard_timeout); | |
122 | fr->packet_count = ntohll(ofr->packet_count); | |
123 | fr->byte_count = ntohll(ofr->byte_count); | |
124 | } else if (raw == OFPRAW_OFPT10_FLOW_REMOVED) { | |
125 | const struct ofp10_flow_removed *ofr; | |
126 | ||
127 | ofr = ofpbuf_pull(&b, sizeof *ofr); | |
128 | ||
129 | ofputil_match_from_ofp10_match(&ofr->match, &fr->match); | |
130 | fr->priority = ntohs(ofr->priority); | |
131 | fr->cookie = ofr->cookie; | |
132 | fr->reason = ofr->reason; | |
133 | fr->table_id = 255; | |
134 | fr->duration_sec = ntohl(ofr->duration_sec); | |
135 | fr->duration_nsec = ntohl(ofr->duration_nsec); | |
136 | fr->idle_timeout = ntohs(ofr->idle_timeout); | |
137 | fr->hard_timeout = 0; | |
138 | fr->packet_count = ntohll(ofr->packet_count); | |
139 | fr->byte_count = ntohll(ofr->byte_count); | |
140 | } else if (raw == OFPRAW_NXT_FLOW_REMOVED) { | |
141 | struct nx_flow_removed *nfr; | |
142 | enum ofperr error; | |
143 | ||
144 | nfr = ofpbuf_pull(&b, sizeof *nfr); | |
145 | error = nx_pull_match(&b, ntohs(nfr->match_len), &fr->match, NULL, | |
146 | NULL, false, NULL, NULL); | |
147 | if (error) { | |
148 | return error; | |
149 | } | |
150 | if (b.size) { | |
151 | return OFPERR_OFPBRC_BAD_LEN; | |
152 | } | |
153 | ||
154 | fr->priority = ntohs(nfr->priority); | |
155 | fr->cookie = nfr->cookie; | |
156 | fr->reason = nfr->reason; | |
157 | fr->table_id = nfr->table_id ? nfr->table_id - 1 : 255; | |
158 | fr->duration_sec = ntohl(nfr->duration_sec); | |
159 | fr->duration_nsec = ntohl(nfr->duration_nsec); | |
160 | fr->idle_timeout = ntohs(nfr->idle_timeout); | |
161 | fr->hard_timeout = 0; | |
162 | fr->packet_count = ntohll(nfr->packet_count); | |
163 | fr->byte_count = ntohll(nfr->byte_count); | |
164 | } else { | |
165 | OVS_NOT_REACHED(); | |
166 | } | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
171 | /* Returns 'count' unchanged except that UINT64_MAX becomes 0. | |
172 | * | |
173 | * We use this in situations where OVS internally uses UINT64_MAX to mean | |
174 | * "value unknown" but OpenFlow 1.0 does not define any unknown value. */ | |
175 | static uint64_t | |
176 | unknown_to_zero(uint64_t count) | |
177 | { | |
178 | return count != UINT64_MAX ? count : 0; | |
179 | } | |
180 | ||
181 | /* Converts abstract ofputil_flow_removed 'fr' into an OFPT_FLOW_REMOVED or | |
182 | * NXT_FLOW_REMOVED message 'oh' according to 'protocol', and returns the | |
183 | * message. */ | |
184 | struct ofpbuf * | |
185 | ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, | |
186 | enum ofputil_protocol protocol) | |
187 | { | |
188 | struct ofpbuf *msg; | |
189 | enum ofp_flow_removed_reason reason = fr->reason; | |
190 | ||
191 | if (reason == OFPRR_METER_DELETE && !(protocol & OFPUTIL_P_OF14_UP)) { | |
192 | reason = OFPRR_DELETE; | |
193 | } | |
194 | ||
195 | switch (protocol) { | |
196 | case OFPUTIL_P_OF11_STD: | |
197 | case OFPUTIL_P_OF12_OXM: | |
198 | case OFPUTIL_P_OF13_OXM: | |
c7b02b80 | 199 | case OFPUTIL_P_OF14_OXM: { |
0d71302e BP |
200 | struct ofp12_flow_removed *ofr; |
201 | ||
202 | msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED, | |
203 | ofputil_protocol_to_ofp_version(protocol), | |
204 | htonl(0), | |
205 | ofputil_match_typical_len(protocol)); | |
206 | ofr = ofpbuf_put_zeros(msg, sizeof *ofr); | |
207 | ofr->cookie = fr->cookie; | |
208 | ofr->priority = htons(fr->priority); | |
209 | ofr->reason = reason; | |
210 | ofr->table_id = fr->table_id; | |
211 | ofr->duration_sec = htonl(fr->duration_sec); | |
212 | ofr->duration_nsec = htonl(fr->duration_nsec); | |
213 | ofr->idle_timeout = htons(fr->idle_timeout); | |
214 | ofr->hard_timeout = htons(fr->hard_timeout); | |
215 | ofr->packet_count = htonll(fr->packet_count); | |
216 | ofr->byte_count = htonll(fr->byte_count); | |
217 | ofputil_put_ofp11_match(msg, &fr->match, protocol); | |
218 | break; | |
219 | } | |
29718ad4 | 220 | case OFPUTIL_P_OF15_OXM: { |
c7b02b80 S |
221 | struct ofp15_flow_removed *ofr; |
222 | ||
223 | msg = ofpraw_alloc_xid(OFPRAW_OFPT15_FLOW_REMOVED, | |
224 | ofputil_protocol_to_ofp_version(protocol), | |
225 | htonl(0), | |
226 | ofputil_match_typical_len(protocol)); | |
227 | ofr = ofpbuf_put_zeros(msg, sizeof *ofr); | |
228 | ofr->cookie = fr->cookie; | |
229 | ofr->priority = htons(fr->priority); | |
230 | ofr->reason = reason; | |
231 | ofr->table_id = fr->table_id; | |
232 | ofr->idle_timeout = htons(fr->idle_timeout); | |
233 | ofr->hard_timeout = htons(fr->hard_timeout); | |
234 | ofputil_put_ofp11_match(msg, &fr->match, protocol); | |
0d71302e | 235 | |
c7b02b80 S |
236 | const struct oxs_stats oxs = { |
237 | .duration_sec = fr->duration_sec, | |
238 | .duration_nsec = fr->duration_nsec, | |
239 | .idle_age = UINT32_MAX, | |
240 | .packet_count = fr->packet_count, | |
241 | .byte_count = fr->byte_count, | |
242 | .flow_count = UINT32_MAX, | |
243 | }; | |
244 | oxs_put_stats(msg, &oxs); | |
245 | break; | |
246 | } | |
0d71302e BP |
247 | case OFPUTIL_P_OF10_STD: |
248 | case OFPUTIL_P_OF10_STD_TID: { | |
249 | struct ofp10_flow_removed *ofr; | |
250 | ||
251 | msg = ofpraw_alloc_xid(OFPRAW_OFPT10_FLOW_REMOVED, OFP10_VERSION, | |
252 | htonl(0), 0); | |
253 | ofr = ofpbuf_put_zeros(msg, sizeof *ofr); | |
254 | ofputil_match_to_ofp10_match(&fr->match, &ofr->match); | |
255 | ofr->cookie = fr->cookie; | |
256 | ofr->priority = htons(fr->priority); | |
257 | ofr->reason = reason; | |
258 | ofr->duration_sec = htonl(fr->duration_sec); | |
259 | ofr->duration_nsec = htonl(fr->duration_nsec); | |
260 | ofr->idle_timeout = htons(fr->idle_timeout); | |
261 | ofr->packet_count = htonll(unknown_to_zero(fr->packet_count)); | |
262 | ofr->byte_count = htonll(unknown_to_zero(fr->byte_count)); | |
263 | break; | |
264 | } | |
265 | ||
266 | case OFPUTIL_P_OF10_NXM: | |
267 | case OFPUTIL_P_OF10_NXM_TID: { | |
268 | struct nx_flow_removed *nfr; | |
269 | int match_len; | |
270 | ||
271 | msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_REMOVED, OFP10_VERSION, | |
272 | htonl(0), NXM_TYPICAL_LEN); | |
273 | ofpbuf_put_zeros(msg, sizeof *nfr); | |
274 | match_len = nx_put_match(msg, &fr->match, 0, 0); | |
275 | ||
276 | nfr = msg->msg; | |
277 | nfr->cookie = fr->cookie; | |
278 | nfr->priority = htons(fr->priority); | |
279 | nfr->reason = reason; | |
280 | nfr->table_id = fr->table_id + 1; | |
281 | nfr->duration_sec = htonl(fr->duration_sec); | |
282 | nfr->duration_nsec = htonl(fr->duration_nsec); | |
283 | nfr->idle_timeout = htons(fr->idle_timeout); | |
284 | nfr->match_len = htons(match_len); | |
285 | nfr->packet_count = htonll(fr->packet_count); | |
286 | nfr->byte_count = htonll(fr->byte_count); | |
287 | break; | |
288 | } | |
289 | ||
290 | default: | |
291 | OVS_NOT_REACHED(); | |
292 | } | |
293 | ||
294 | return msg; | |
295 | } | |
dfc77282 BP |
296 | |
297 | void | |
298 | ofputil_flow_removed_format(struct ds *s, | |
299 | const struct ofputil_flow_removed *fr, | |
300 | const struct ofputil_port_map *port_map, | |
301 | const struct ofputil_table_map *table_map) | |
302 | { | |
303 | char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE]; | |
304 | ||
305 | ds_put_char(s, ' '); | |
306 | match_format(&fr->match, port_map, s, fr->priority); | |
307 | ||
308 | ds_put_format(s, " reason=%s", | |
309 | ofp_flow_removed_reason_to_string(fr->reason, reasonbuf, | |
310 | sizeof reasonbuf)); | |
311 | ||
312 | if (fr->table_id != 255) { | |
313 | ds_put_format(s, " table_id="); | |
314 | ofputil_format_table(fr->table_id, table_map, s); | |
315 | } | |
316 | ||
317 | if (fr->cookie != htonll(0)) { | |
318 | ds_put_format(s, " cookie:0x%"PRIx64, ntohll(fr->cookie)); | |
319 | } | |
320 | ds_put_cstr(s, " duration"); | |
321 | ofp_print_duration(s, fr->duration_sec, fr->duration_nsec); | |
322 | ds_put_format(s, " idle%"PRIu16, fr->idle_timeout); | |
323 | if (fr->hard_timeout) { | |
324 | /* The hard timeout was only added in OF1.2, so only print it if it is | |
325 | * actually in use to avoid gratuitous change to the formatting. */ | |
326 | ds_put_format(s, " hard%"PRIu16, fr->hard_timeout); | |
327 | } | |
328 | ds_put_format(s, " pkts%"PRIu64" bytes%"PRIu64"\n", | |
329 | fr->packet_count, fr->byte_count); | |
330 | } | |
0d71302e BP |
331 | \f |
332 | /* ofputil_flow_monitor_request */ | |
333 | ||
334 | /* Converts an NXST_FLOW_MONITOR request in 'msg' into an abstract | |
335 | * ofputil_flow_monitor_request in 'rq'. | |
336 | * | |
337 | * Multiple NXST_FLOW_MONITOR requests can be packed into a single OpenFlow | |
338 | * message. Calling this function multiple times for a single 'msg' iterates | |
339 | * through the requests. The caller must initially leave 'msg''s layer | |
340 | * pointers null and not modify them between calls. | |
341 | * | |
342 | * Returns 0 if successful, EOF if no requests were left in this 'msg', | |
343 | * otherwise an OFPERR_* value. */ | |
344 | int | |
345 | ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *rq, | |
346 | struct ofpbuf *msg) | |
347 | { | |
348 | struct nx_flow_monitor_request *nfmr; | |
349 | uint16_t flags; | |
350 | ||
351 | if (!msg->header) { | |
352 | ofpraw_pull_assert(msg); | |
353 | } | |
354 | ||
355 | if (!msg->size) { | |
356 | return EOF; | |
357 | } | |
358 | ||
359 | nfmr = ofpbuf_try_pull(msg, sizeof *nfmr); | |
360 | if (!nfmr) { | |
361 | VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR request has %"PRIu32" " | |
362 | "leftover bytes at end", msg->size); | |
363 | return OFPERR_OFPBRC_BAD_LEN; | |
364 | } | |
365 | ||
366 | flags = ntohs(nfmr->flags); | |
367 | if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY)) | |
368 | || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | |
369 | | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) { | |
370 | VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16, flags); | |
371 | return OFPERR_OFPMOFC_BAD_FLAGS; | |
372 | } | |
373 | ||
374 | if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) { | |
375 | return OFPERR_NXBRC_MUST_BE_ZERO; | |
376 | } | |
377 | ||
378 | rq->id = ntohl(nfmr->id); | |
379 | rq->flags = flags; | |
380 | rq->out_port = u16_to_ofp(ntohs(nfmr->out_port)); | |
381 | rq->table_id = nfmr->table_id; | |
382 | ||
383 | return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL, | |
384 | NULL, false, NULL, NULL); | |
385 | } | |
386 | ||
387 | void | |
388 | ofputil_append_flow_monitor_request( | |
389 | const struct ofputil_flow_monitor_request *rq, struct ofpbuf *msg) | |
390 | { | |
391 | struct nx_flow_monitor_request *nfmr; | |
392 | size_t start_ofs; | |
393 | int match_len; | |
394 | ||
395 | if (!msg->size) { | |
396 | ofpraw_put(OFPRAW_NXST_FLOW_MONITOR_REQUEST, OFP10_VERSION, msg); | |
397 | } | |
398 | ||
399 | start_ofs = msg->size; | |
400 | ofpbuf_put_zeros(msg, sizeof *nfmr); | |
401 | match_len = nx_put_match(msg, &rq->match, htonll(0), htonll(0)); | |
402 | ||
403 | nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr); | |
404 | nfmr->id = htonl(rq->id); | |
405 | nfmr->flags = htons(rq->flags); | |
406 | nfmr->out_port = htons(ofp_to_u16(rq->out_port)); | |
407 | nfmr->match_len = htons(match_len); | |
408 | nfmr->table_id = rq->table_id; | |
409 | } | |
410 | ||
dfc77282 BP |
411 | static const char * |
412 | nx_flow_monitor_flags_to_name(uint32_t bit) | |
413 | { | |
414 | enum nx_flow_monitor_flags fmf = bit; | |
415 | ||
416 | switch (fmf) { | |
417 | case NXFMF_INITIAL: return "initial"; | |
418 | case NXFMF_ADD: return "add"; | |
419 | case NXFMF_DELETE: return "delete"; | |
420 | case NXFMF_MODIFY: return "modify"; | |
421 | case NXFMF_ACTIONS: return "actions"; | |
422 | case NXFMF_OWN: return "own"; | |
423 | } | |
424 | ||
425 | return NULL; | |
426 | } | |
427 | ||
428 | void | |
429 | ofputil_flow_monitor_request_format( | |
430 | struct ds *s, const struct ofputil_flow_monitor_request *request, | |
431 | const struct ofputil_port_map *port_map, | |
432 | const struct ofputil_table_map *table_map) | |
433 | { | |
434 | ds_put_format(s, "\n id=%"PRIu32" flags=", request->id); | |
435 | ofp_print_bit_names(s, request->flags, nx_flow_monitor_flags_to_name, ','); | |
436 | ||
437 | if (request->out_port != OFPP_NONE) { | |
438 | ds_put_cstr(s, " out_port="); | |
439 | ofputil_format_port(request->out_port, port_map, s); | |
440 | } | |
441 | ||
442 | if (request->table_id != 0xff) { | |
443 | ds_put_format(s, " table="); | |
444 | ofputil_format_table(request->table_id, table_map, s); | |
445 | } | |
446 | ||
447 | ds_put_char(s, ' '); | |
448 | match_format(&request->match, port_map, s, OFP_DEFAULT_PRIORITY); | |
449 | ds_chomp(s, ' '); | |
450 | } | |
451 | ||
0d71302e BP |
452 | static char * OVS_WARN_UNUSED_RESULT |
453 | parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr, | |
454 | const char *str_, | |
455 | const struct ofputil_port_map *port_map, | |
456 | const struct ofputil_table_map *table_map, | |
457 | char *string, | |
458 | enum ofputil_protocol *usable_protocols) | |
459 | { | |
460 | static atomic_count id = ATOMIC_COUNT_INIT(0); | |
461 | char *name, *value; | |
462 | ||
463 | fmr->id = atomic_count_inc(&id); | |
464 | ||
465 | fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY | |
466 | | NXFMF_OWN | NXFMF_ACTIONS); | |
467 | fmr->out_port = OFPP_NONE; | |
468 | fmr->table_id = 0xff; | |
469 | match_init_catchall(&fmr->match); | |
470 | ||
3e613cd8 | 471 | *usable_protocols = OFPUTIL_P_ANY; |
0d71302e BP |
472 | while (ofputil_parse_key_value(&string, &name, &value)) { |
473 | const struct ofp_protocol *p; | |
474 | char *error = NULL; | |
475 | ||
476 | if (!strcmp(name, "!initial")) { | |
477 | fmr->flags &= ~NXFMF_INITIAL; | |
478 | } else if (!strcmp(name, "!add")) { | |
479 | fmr->flags &= ~NXFMF_ADD; | |
480 | } else if (!strcmp(name, "!delete")) { | |
481 | fmr->flags &= ~NXFMF_DELETE; | |
482 | } else if (!strcmp(name, "!modify")) { | |
483 | fmr->flags &= ~NXFMF_MODIFY; | |
484 | } else if (!strcmp(name, "!actions")) { | |
485 | fmr->flags &= ~NXFMF_ACTIONS; | |
486 | } else if (!strcmp(name, "!own")) { | |
487 | fmr->flags &= ~NXFMF_OWN; | |
488 | } else if (ofp_parse_protocol(name, &p)) { | |
489 | match_set_dl_type(&fmr->match, htons(p->dl_type)); | |
490 | if (p->nw_proto) { | |
491 | match_set_nw_proto(&fmr->match, p->nw_proto); | |
492 | } | |
493 | } else if (mf_from_name(name)) { | |
494 | error = ofp_parse_field(mf_from_name(name), value, port_map, | |
495 | &fmr->match, usable_protocols); | |
3e613cd8 AV |
496 | if (!error && !(*usable_protocols & OFPUTIL_P_OF10_ANY)) { |
497 | return xasprintf("%s: match field is not supported for " | |
498 | "flow monitor", name); | |
499 | } | |
0d71302e BP |
500 | } else { |
501 | if (!*value) { | |
502 | return xasprintf("%s: field %s missing value", str_, name); | |
503 | } | |
504 | ||
505 | if (!strcmp(name, "table")) { | |
506 | if (!ofputil_table_from_string(value, table_map, | |
507 | &fmr->table_id)) { | |
508 | error = xasprintf("unknown table \"%s\"", value); | |
509 | } | |
510 | } else if (!strcmp(name, "out_port")) { | |
511 | fmr->out_port = u16_to_ofp(atoi(value)); | |
512 | } else { | |
513 | return xasprintf("%s: unknown keyword %s", str_, name); | |
514 | } | |
515 | } | |
516 | ||
517 | if (error) { | |
518 | return error; | |
519 | } | |
3e613cd8 AV |
520 | /* Flow Monitor is supported in OpenFlow 1.0 or can be further reduced |
521 | * to a few 1.0 flavors by a match field. */ | |
522 | *usable_protocols &= OFPUTIL_P_OF10_ANY; | |
0d71302e BP |
523 | } |
524 | return NULL; | |
525 | } | |
526 | ||
527 | /* Convert 'str_' (as described in the documentation for the "monitor" command | |
528 | * in the ovs-ofctl man page) into 'fmr'. | |
529 | * | |
530 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
531 | * error. The caller is responsible for freeing the returned string. */ | |
532 | char * OVS_WARN_UNUSED_RESULT | |
533 | parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr, | |
534 | const char *str_, | |
535 | const struct ofputil_port_map *port_map, | |
536 | const struct ofputil_table_map *table_map, | |
537 | enum ofputil_protocol *usable_protocols) | |
538 | { | |
539 | char *string = xstrdup(str_); | |
540 | char *error = parse_flow_monitor_request__(fmr, str_, port_map, table_map, | |
541 | string, usable_protocols); | |
542 | free(string); | |
543 | return error; | |
544 | } | |
545 | ||
546 | /* Converts an NXST_FLOW_MONITOR reply (also known as a flow update) in 'msg' | |
547 | * into an abstract ofputil_flow_update in 'update'. The caller must have | |
548 | * initialized update->match to point to space allocated for a match. | |
549 | * | |
550 | * Uses 'ofpacts' to store the abstract OFPACT_* version of the update's | |
551 | * actions (except for NXFME_ABBREV, which never includes actions). The caller | |
552 | * must initialize 'ofpacts' and retains ownership of it. 'update->ofpacts' | |
553 | * will point into the 'ofpacts' buffer. | |
554 | * | |
555 | * Multiple flow updates can be packed into a single OpenFlow message. Calling | |
556 | * this function multiple times for a single 'msg' iterates through the | |
557 | * updates. The caller must initially leave 'msg''s layer pointers null and | |
558 | * not modify them between calls. | |
559 | * | |
560 | * Returns 0 if successful, EOF if no updates were left in this 'msg', | |
561 | * otherwise an OFPERR_* value. */ | |
562 | int | |
563 | ofputil_decode_flow_update(struct ofputil_flow_update *update, | |
564 | struct ofpbuf *msg, struct ofpbuf *ofpacts) | |
565 | { | |
566 | struct nx_flow_update_header *nfuh; | |
567 | unsigned int length; | |
568 | struct ofp_header *oh; | |
569 | ||
570 | if (!msg->header) { | |
571 | ofpraw_pull_assert(msg); | |
572 | } | |
573 | ||
574 | ofpbuf_clear(ofpacts); | |
575 | if (!msg->size) { | |
576 | return EOF; | |
577 | } | |
578 | ||
579 | if (msg->size < sizeof(struct nx_flow_update_header)) { | |
580 | goto bad_len; | |
581 | } | |
582 | ||
583 | oh = msg->header; | |
584 | ||
585 | nfuh = msg->data; | |
586 | update->event = ntohs(nfuh->event); | |
587 | length = ntohs(nfuh->length); | |
588 | if (length > msg->size || length % 8) { | |
589 | goto bad_len; | |
590 | } | |
591 | ||
592 | if (update->event == NXFME_ABBREV) { | |
593 | struct nx_flow_update_abbrev *nfua; | |
594 | ||
595 | if (length != sizeof *nfua) { | |
596 | goto bad_len; | |
597 | } | |
598 | ||
599 | nfua = ofpbuf_pull(msg, sizeof *nfua); | |
600 | update->xid = nfua->xid; | |
601 | return 0; | |
602 | } else if (update->event == NXFME_ADDED | |
603 | || update->event == NXFME_DELETED | |
604 | || update->event == NXFME_MODIFIED) { | |
605 | struct nx_flow_update_full *nfuf; | |
606 | unsigned int actions_len; | |
607 | unsigned int match_len; | |
608 | enum ofperr error; | |
609 | ||
610 | if (length < sizeof *nfuf) { | |
611 | goto bad_len; | |
612 | } | |
613 | ||
614 | nfuf = ofpbuf_pull(msg, sizeof *nfuf); | |
615 | match_len = ntohs(nfuf->match_len); | |
616 | if (sizeof *nfuf + match_len > length) { | |
617 | goto bad_len; | |
618 | } | |
619 | ||
620 | update->reason = ntohs(nfuf->reason); | |
621 | update->idle_timeout = ntohs(nfuf->idle_timeout); | |
622 | update->hard_timeout = ntohs(nfuf->hard_timeout); | |
623 | update->table_id = nfuf->table_id; | |
624 | update->cookie = nfuf->cookie; | |
625 | update->priority = ntohs(nfuf->priority); | |
626 | ||
627 | error = nx_pull_match(msg, match_len, &update->match, NULL, NULL, | |
628 | false, NULL, NULL); | |
629 | if (error) { | |
630 | return error; | |
631 | } | |
632 | ||
633 | actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8); | |
634 | error = ofpacts_pull_openflow_actions(msg, actions_len, oh->version, | |
635 | NULL, NULL, ofpacts); | |
636 | if (error) { | |
637 | return error; | |
638 | } | |
639 | ||
640 | update->ofpacts = ofpacts->data; | |
641 | update->ofpacts_len = ofpacts->size; | |
642 | return 0; | |
643 | } else { | |
644 | VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has bad event %"PRIu16, | |
645 | ntohs(nfuh->event)); | |
646 | return OFPERR_NXBRC_FM_BAD_EVENT; | |
647 | } | |
648 | ||
649 | bad_len: | |
650 | VLOG_WARN_RL(&rl, "NXST_FLOW_MONITOR reply has %"PRIu32" " | |
651 | "leftover bytes at end", msg->size); | |
652 | return OFPERR_OFPBRC_BAD_LEN; | |
653 | } | |
654 | ||
655 | uint32_t | |
656 | ofputil_decode_flow_monitor_cancel(const struct ofp_header *oh) | |
657 | { | |
658 | const struct nx_flow_monitor_cancel *cancel = ofpmsg_body(oh); | |
659 | ||
660 | return ntohl(cancel->id); | |
661 | } | |
662 | ||
663 | struct ofpbuf * | |
664 | ofputil_encode_flow_monitor_cancel(uint32_t id) | |
665 | { | |
666 | struct nx_flow_monitor_cancel *nfmc; | |
667 | struct ofpbuf *msg; | |
668 | ||
669 | msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MONITOR_CANCEL, OFP10_VERSION, 0); | |
670 | nfmc = ofpbuf_put_uninit(msg, sizeof *nfmc); | |
671 | nfmc->id = htonl(id); | |
672 | return msg; | |
673 | } | |
674 | ||
675 | void | |
676 | ofputil_start_flow_update(struct ovs_list *replies) | |
677 | { | |
678 | struct ofpbuf *msg; | |
679 | ||
680 | msg = ofpraw_alloc_xid(OFPRAW_NXST_FLOW_MONITOR_REPLY, OFP10_VERSION, | |
681 | htonl(0), 1024); | |
682 | ||
683 | ovs_list_init(replies); | |
684 | ovs_list_push_back(replies, &msg->list_node); | |
685 | } | |
686 | ||
687 | void | |
688 | ofputil_append_flow_update(const struct ofputil_flow_update *update, | |
689 | struct ovs_list *replies, | |
690 | const struct tun_table *tun_table) | |
691 | { | |
692 | struct ofputil_flow_update *update_ = | |
693 | CONST_CAST(struct ofputil_flow_update *, update); | |
694 | const struct tun_table *orig_tun_table; | |
695 | enum ofp_version version = ofpmp_version(replies); | |
696 | struct nx_flow_update_header *nfuh; | |
697 | struct ofpbuf *msg; | |
698 | size_t start_ofs; | |
699 | ||
700 | orig_tun_table = update->match.flow.tunnel.metadata.tab; | |
701 | update_->match.flow.tunnel.metadata.tab = tun_table; | |
702 | ||
703 | msg = ofpbuf_from_list(ovs_list_back(replies)); | |
704 | start_ofs = msg->size; | |
705 | ||
706 | if (update->event == NXFME_ABBREV) { | |
707 | struct nx_flow_update_abbrev *nfua; | |
708 | ||
709 | nfua = ofpbuf_put_zeros(msg, sizeof *nfua); | |
710 | nfua->xid = update->xid; | |
711 | } else { | |
712 | struct nx_flow_update_full *nfuf; | |
713 | int match_len; | |
714 | ||
715 | ofpbuf_put_zeros(msg, sizeof *nfuf); | |
716 | match_len = nx_put_match(msg, &update->match, htonll(0), htonll(0)); | |
717 | ofpacts_put_openflow_actions(update->ofpacts, update->ofpacts_len, msg, | |
718 | version); | |
719 | nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf); | |
720 | nfuf->reason = htons(update->reason); | |
721 | nfuf->priority = htons(update->priority); | |
722 | nfuf->idle_timeout = htons(update->idle_timeout); | |
723 | nfuf->hard_timeout = htons(update->hard_timeout); | |
724 | nfuf->match_len = htons(match_len); | |
725 | nfuf->table_id = update->table_id; | |
726 | nfuf->cookie = update->cookie; | |
727 | } | |
728 | ||
729 | nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh); | |
730 | nfuh->length = htons(msg->size - start_ofs); | |
731 | nfuh->event = htons(update->event); | |
732 | ||
733 | ofpmp_postappend(replies, start_ofs); | |
734 | update_->match.flow.tunnel.metadata.tab = orig_tun_table; | |
735 | } | |
dfc77282 BP |
736 | |
737 | void | |
738 | ofputil_flow_update_format(struct ds *s, | |
739 | const struct ofputil_flow_update *update, | |
740 | const struct ofputil_port_map *port_map, | |
741 | const struct ofputil_table_map *table_map) | |
742 | { | |
743 | char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE]; | |
744 | ||
745 | ds_put_cstr(s, "\n event="); | |
746 | switch (update->event) { | |
747 | case NXFME_ADDED: | |
748 | ds_put_cstr(s, "ADDED"); | |
749 | break; | |
750 | ||
751 | case NXFME_DELETED: | |
752 | ds_put_format(s, "DELETED reason=%s", | |
753 | ofp_flow_removed_reason_to_string(update->reason, | |
754 | reasonbuf, | |
755 | sizeof reasonbuf)); | |
756 | break; | |
757 | ||
758 | case NXFME_MODIFIED: | |
759 | ds_put_cstr(s, "MODIFIED"); | |
760 | break; | |
761 | ||
762 | case NXFME_ABBREV: | |
763 | ds_put_format(s, "ABBREV xid=0x%"PRIx32, ntohl(update->xid)); | |
764 | return; | |
765 | } | |
766 | ||
767 | ds_put_format(s, " table="); | |
768 | ofputil_format_table(update->table_id, table_map, s); | |
769 | if (update->idle_timeout != OFP_FLOW_PERMANENT) { | |
770 | ds_put_format(s, " idle_timeout=%"PRIu16, update->idle_timeout); | |
771 | } | |
772 | if (update->hard_timeout != OFP_FLOW_PERMANENT) { | |
773 | ds_put_format(s, " hard_timeout=%"PRIu16, update->hard_timeout); | |
774 | } | |
775 | ds_put_format(s, " cookie=%#"PRIx64, ntohll(update->cookie)); | |
776 | ||
777 | ds_put_char(s, ' '); | |
778 | match_format(&update->match, port_map, s, OFP_DEFAULT_PRIORITY); | |
779 | ||
780 | if (update->ofpacts_len) { | |
781 | if (s->string[s->length - 1] != ' ') { | |
782 | ds_put_char(s, ' '); | |
783 | } | |
784 | ds_put_cstr(s, "actions="); | |
785 | struct ofpact_format_params fp = { | |
786 | .port_map = port_map, | |
787 | .table_map = table_map, | |
788 | .s = s, | |
789 | }; | |
790 | ofpacts_format(update->ofpacts, update->ofpacts_len, &fp); | |
791 | } | |
792 | } | |
0d71302e | 793 | \f |
98a9272b | 794 | /* Encodes 'rf' according to 'protocol', and returns the encoded message. */ |
0d71302e BP |
795 | struct ofpbuf * |
796 | ofputil_encode_requestforward(const struct ofputil_requestforward *rf, | |
797 | enum ofputil_protocol protocol) | |
798 | { | |
799 | enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol); | |
98a9272b | 800 | enum ofpraw raw_msg_type; |
0d71302e BP |
801 | struct ofpbuf *inner; |
802 | ||
803 | switch (rf->reason) { | |
804 | case OFPRFR_GROUP_MOD: | |
f836888d BP |
805 | inner = ofputil_encode_group_mod(ofp_version, rf->group_mod, |
806 | rf->new_buckets, rf->group_existed); | |
0d71302e BP |
807 | break; |
808 | ||
809 | case OFPRFR_METER_MOD: | |
810 | inner = ofputil_encode_meter_mod(ofp_version, rf->meter_mod); | |
811 | break; | |
812 | ||
813 | case OFPRFR_N_REASONS: | |
814 | default: | |
815 | OVS_NOT_REACHED(); | |
816 | } | |
817 | ||
818 | struct ofp_header *inner_oh = inner->data; | |
819 | inner_oh->xid = rf->xid; | |
820 | inner_oh->length = htons(inner->size); | |
821 | ||
98a9272b ZW |
822 | if (ofp_version < OFP13_VERSION) { |
823 | raw_msg_type = OFPRAW_NXT_REQUESTFORWARD; | |
824 | } else if (ofp_version == OFP13_VERSION) { | |
825 | raw_msg_type = OFPRAW_ONFT13_REQUESTFORWARD; | |
826 | } else { | |
827 | raw_msg_type = OFPRAW_OFPT14_REQUESTFORWARD; | |
828 | } | |
829 | ||
830 | struct ofpbuf *outer = ofpraw_alloc_xid(raw_msg_type, ofp_version, | |
831 | htonl(0), inner->size); | |
0d71302e BP |
832 | ofpbuf_put(outer, inner->data, inner->size); |
833 | ofpbuf_delete(inner); | |
834 | ||
835 | return outer; | |
836 | } | |
837 | ||
838 | /* Decodes OFPT_REQUESTFORWARD message 'outer'. On success, puts the decoded | |
839 | * form into '*rf' and returns 0, and the caller is later responsible for | |
840 | * freeing the content of 'rf', with ofputil_destroy_requestforward(rf). On | |
841 | * failure, returns an ofperr and '*rf' is indeterminate. */ | |
842 | enum ofperr | |
843 | ofputil_decode_requestforward(const struct ofp_header *outer, | |
844 | struct ofputil_requestforward *rf) | |
845 | { | |
f836888d BP |
846 | rf->new_buckets = NULL; |
847 | rf->group_existed = -1; | |
848 | ||
0d71302e BP |
849 | struct ofpbuf b = ofpbuf_const_initializer(outer, ntohs(outer->length)); |
850 | ||
851 | /* Skip past outer message. */ | |
98a9272b ZW |
852 | enum ofpraw raw_msg_type = ofpraw_pull_assert(&b); |
853 | ovs_assert(raw_msg_type == OFPRAW_OFPT14_REQUESTFORWARD || | |
854 | raw_msg_type == OFPRAW_ONFT13_REQUESTFORWARD || | |
855 | raw_msg_type == OFPRAW_NXT_REQUESTFORWARD); | |
0d71302e BP |
856 | |
857 | /* Validate inner message. */ | |
858 | if (b.size < sizeof(struct ofp_header)) { | |
859 | return OFPERR_OFPBFC_MSG_BAD_LEN; | |
860 | } | |
861 | const struct ofp_header *inner = b.data; | |
862 | unsigned int inner_len = ntohs(inner->length); | |
863 | if (inner_len < sizeof(struct ofp_header) || inner_len > b.size) { | |
864 | return OFPERR_OFPBFC_MSG_BAD_LEN; | |
865 | } | |
866 | if (inner->version != outer->version) { | |
867 | return OFPERR_OFPBRC_BAD_VERSION; | |
868 | } | |
869 | ||
870 | /* Parse inner message. */ | |
871 | enum ofptype type; | |
872 | enum ofperr error = ofptype_decode(&type, inner); | |
873 | if (error) { | |
874 | return error; | |
875 | } | |
876 | ||
877 | rf->xid = inner->xid; | |
878 | if (type == OFPTYPE_GROUP_MOD) { | |
879 | rf->reason = OFPRFR_GROUP_MOD; | |
880 | rf->group_mod = xmalloc(sizeof *rf->group_mod); | |
881 | error = ofputil_decode_group_mod(inner, rf->group_mod); | |
882 | if (error) { | |
883 | free(rf->group_mod); | |
884 | return error; | |
885 | } | |
886 | } else if (type == OFPTYPE_METER_MOD) { | |
887 | rf->reason = OFPRFR_METER_MOD; | |
888 | rf->meter_mod = xmalloc(sizeof *rf->meter_mod); | |
889 | ofpbuf_init(&rf->bands, 64); | |
890 | error = ofputil_decode_meter_mod(inner, rf->meter_mod, &rf->bands); | |
891 | if (error) { | |
892 | free(rf->meter_mod); | |
893 | ofpbuf_uninit(&rf->bands); | |
894 | return error; | |
895 | } | |
896 | } else { | |
897 | return OFPERR_OFPBFC_MSG_UNSUP; | |
898 | } | |
899 | ||
900 | return 0; | |
901 | } | |
902 | ||
fe2c69f4 BP |
903 | void |
904 | ofputil_format_requestforward(struct ds *string, | |
905 | enum ofp_version ofp_version, | |
906 | const struct ofputil_requestforward *rf, | |
907 | const struct ofputil_port_map *port_map, | |
908 | const struct ofputil_table_map *table_map) | |
909 | { | |
910 | ds_put_cstr(string, " reason="); | |
911 | ||
912 | switch (rf->reason) { | |
913 | case OFPRFR_GROUP_MOD: | |
914 | ds_put_cstr(string, "group_mod"); | |
915 | ofputil_group_mod_format__(string, ofp_version, rf->group_mod, | |
916 | port_map, table_map); | |
917 | break; | |
918 | ||
919 | case OFPRFR_METER_MOD: | |
920 | ds_put_cstr(string, "meter_mod"); | |
921 | ofputil_format_meter_mod(string, rf->meter_mod); | |
922 | break; | |
923 | ||
924 | case OFPRFR_N_REASONS: | |
925 | OVS_NOT_REACHED(); | |
926 | } | |
927 | } | |
928 | ||
929 | ||
0d71302e BP |
930 | /* Frees the content of 'rf', which should have been initialized through a |
931 | * successful call to ofputil_decode_requestforward(). */ | |
932 | void | |
933 | ofputil_destroy_requestforward(struct ofputil_requestforward *rf) | |
934 | { | |
935 | if (!rf) { | |
936 | return; | |
937 | } | |
938 | ||
939 | switch (rf->reason) { | |
940 | case OFPRFR_GROUP_MOD: | |
941 | ofputil_uninit_group_mod(rf->group_mod); | |
942 | free(rf->group_mod); | |
f836888d | 943 | /* 'rf' does not own rf->new_buckets. */ |
0d71302e BP |
944 | break; |
945 | ||
946 | case OFPRFR_METER_MOD: | |
947 | ofpbuf_uninit(&rf->bands); | |
948 | free(rf->meter_mod); | |
949 | break; | |
950 | ||
951 | case OFPRFR_N_REASONS: | |
952 | OVS_NOT_REACHED(); | |
953 | } | |
954 | } |