/src/openvswitch/lib/ofp-packet.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-packet.h" |
19 | | #include <string.h> |
20 | | #include "dp-packet.h" |
21 | | #include "nx-match.h" |
22 | | #include "openvswitch/ofp-actions.h" |
23 | | #include "openvswitch/ofp-errors.h" |
24 | | #include "openvswitch/ofp-msgs.h" |
25 | | #include "openvswitch/ofp-parse.h" |
26 | | #include "openvswitch/ofp-print.h" |
27 | | #include "openvswitch/ofp-port.h" |
28 | | #include "openvswitch/ofp-prop.h" |
29 | | #include "openvswitch/ofp-table.h" |
30 | | #include "openvswitch/ofpbuf.h" |
31 | | #include "openvswitch/vlog.h" |
32 | | #include "util.h" |
33 | | #include "uuid.h" |
34 | | |
35 | | VLOG_DEFINE_THIS_MODULE(ofp_packet); |
36 | | |
37 | | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); |
38 | | |
39 | | const char * |
40 | | ofputil_packet_in_format_to_string(enum ofputil_packet_in_format format) |
41 | 0 | { |
42 | 0 | switch (format) { |
43 | 0 | case OFPUTIL_PACKET_IN_STD: |
44 | 0 | return "standard"; |
45 | 0 | case OFPUTIL_PACKET_IN_NXT: |
46 | 0 | return "nxt_packet_in"; |
47 | 0 | case OFPUTIL_PACKET_IN_NXT2: |
48 | 0 | return "nxt_packet_in2"; |
49 | 0 | default: |
50 | 0 | OVS_NOT_REACHED(); |
51 | 0 | } |
52 | 0 | } |
53 | | |
54 | | int |
55 | | ofputil_packet_in_format_from_string(const char *s) |
56 | 0 | { |
57 | 0 | return (!strcmp(s, "standard") || !strcmp(s, "openflow10") |
58 | 0 | ? OFPUTIL_PACKET_IN_STD |
59 | 0 | : !strcmp(s, "nxt_packet_in") || !strcmp(s, "nxm") |
60 | 0 | ? OFPUTIL_PACKET_IN_NXT |
61 | 0 | : !strcmp(s, "nxt_packet_in2") |
62 | 0 | ? OFPUTIL_PACKET_IN_NXT2 |
63 | 0 | : -1); |
64 | 0 | } |
65 | | |
66 | | struct ofpbuf * |
67 | | ofputil_encode_set_packet_in_format(enum ofp_version ofp_version, |
68 | | enum ofputil_packet_in_format format) |
69 | 0 | { |
70 | 0 | struct ofpbuf *msg = ofpraw_alloc(OFPRAW_NXT_SET_PACKET_IN_FORMAT, |
71 | 0 | ofp_version, 0); |
72 | 0 | ovs_be32 *spif = ofpbuf_put_uninit(msg, sizeof *spif); |
73 | 0 | *spif = htonl(format); |
74 | |
|
75 | 0 | return msg; |
76 | 0 | } |
77 | | |
78 | | enum ofperr |
79 | | ofputil_decode_set_packet_in_format(const struct ofp_header *oh, |
80 | | enum ofputil_packet_in_format *format) |
81 | 0 | { |
82 | 0 | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); |
83 | 0 | ovs_assert(ofpraw_pull_assert(&b) == OFPRAW_NXT_SET_PACKET_IN_FORMAT); |
84 | 0 | ovs_be32 *spifp = ofpbuf_pull(&b, sizeof *spifp); |
85 | 0 | uint32_t spif = ntohl(*spifp); |
86 | |
|
87 | 0 | switch (spif) { |
88 | 0 | case OFPUTIL_PACKET_IN_STD: |
89 | 0 | case OFPUTIL_PACKET_IN_NXT: |
90 | 0 | case OFPUTIL_PACKET_IN_NXT2: |
91 | 0 | *format = spif; |
92 | 0 | return 0; |
93 | | |
94 | 0 | default: |
95 | 0 | VLOG_WARN_RL(&rl, "NXT_SET_PACKET_IN_FORMAT message specified invalid " |
96 | 0 | "packet-in format %"PRIu32, spif); |
97 | 0 | return OFPERR_OFPBRC_EPERM; |
98 | 0 | } |
99 | 0 | } |
100 | | |
101 | | /* The caller has done basic initialization of '*pin'; the other output |
102 | | * arguments needs to be initialized. */ |
103 | | static enum ofperr |
104 | | decode_nx_packet_in2(const struct ofp_header *oh, bool loose, |
105 | | const struct tun_table *tun_table, |
106 | | const struct vl_mff_map *vl_mff_map, |
107 | | struct ofputil_packet_in *pin, |
108 | | size_t *total_len, uint32_t *buffer_id, |
109 | | struct ofpbuf *continuation) |
110 | 0 | { |
111 | 0 | *total_len = 0; |
112 | 0 | *buffer_id = UINT32_MAX; |
113 | |
|
114 | 0 | struct ofpbuf properties; |
115 | 0 | ofpbuf_use_const(&properties, oh, ntohs(oh->length)); |
116 | 0 | ofpraw_pull_assert(&properties); |
117 | |
|
118 | 0 | while (properties.size > 0) { |
119 | 0 | struct ofpbuf payload; |
120 | 0 | uint64_t type; |
121 | |
|
122 | 0 | enum ofperr error = ofpprop_pull(&properties, &payload, &type); |
123 | 0 | if (error) { |
124 | 0 | return error; |
125 | 0 | } |
126 | | |
127 | 0 | switch (type) { |
128 | 0 | case NXPINT_PACKET: |
129 | 0 | pin->packet = payload.msg; |
130 | 0 | pin->packet_len = ofpbuf_msgsize(&payload); |
131 | 0 | break; |
132 | | |
133 | 0 | case NXPINT_FULL_LEN: { |
134 | 0 | uint32_t u32; |
135 | 0 | error = ofpprop_parse_u32(&payload, &u32); |
136 | 0 | if (!error) { |
137 | 0 | *total_len = u32; |
138 | 0 | } |
139 | 0 | break; |
140 | 0 | } |
141 | | |
142 | 0 | case NXPINT_BUFFER_ID: |
143 | 0 | error = ofpprop_parse_u32(&payload, buffer_id); |
144 | 0 | break; |
145 | | |
146 | 0 | case NXPINT_TABLE_ID: |
147 | 0 | error = ofpprop_parse_u8(&payload, &pin->table_id); |
148 | 0 | break; |
149 | | |
150 | 0 | case NXPINT_COOKIE: |
151 | 0 | error = ofpprop_parse_be64(&payload, &pin->cookie); |
152 | 0 | break; |
153 | | |
154 | 0 | case NXPINT_REASON: { |
155 | 0 | uint8_t reason; |
156 | 0 | error = ofpprop_parse_u8(&payload, &reason); |
157 | 0 | if (!error) { |
158 | 0 | pin->reason = reason; |
159 | 0 | } |
160 | 0 | break; |
161 | 0 | } |
162 | | |
163 | 0 | case NXPINT_METADATA: |
164 | 0 | error = oxm_decode_match(payload.msg, ofpbuf_msgsize(&payload), |
165 | 0 | loose, tun_table, vl_mff_map, |
166 | 0 | &pin->flow_metadata); |
167 | 0 | pin->flow_metadata.flow.tunnel.metadata.tab = tun_table; |
168 | 0 | break; |
169 | | |
170 | 0 | case NXPINT_USERDATA: |
171 | 0 | pin->userdata = payload.msg; |
172 | 0 | pin->userdata_len = ofpbuf_msgsize(&payload); |
173 | 0 | break; |
174 | | |
175 | 0 | case NXPINT_CONTINUATION: |
176 | 0 | if (continuation) { |
177 | 0 | error = ofpprop_parse_nested(&payload, continuation); |
178 | 0 | } |
179 | 0 | break; |
180 | | |
181 | 0 | default: |
182 | 0 | error = OFPPROP_UNKNOWN(loose, "NX_PACKET_IN2", type); |
183 | 0 | break; |
184 | 0 | } |
185 | 0 | if (error) { |
186 | 0 | return error; |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | 0 | if (!pin->packet_len) { |
191 | 0 | VLOG_WARN_RL(&rl, "NXT_PACKET_IN2 lacks packet"); |
192 | 0 | return OFPERR_OFPBRC_BAD_LEN; |
193 | 0 | } else if (!*total_len) { |
194 | 0 | *total_len = pin->packet_len; |
195 | 0 | } else if (*total_len < pin->packet_len) { |
196 | 0 | VLOG_WARN_RL(&rl, "NXT_PACKET_IN2 claimed full_len < len"); |
197 | 0 | return OFPERR_OFPBRC_BAD_LEN; |
198 | 0 | } |
199 | | |
200 | 0 | return 0; |
201 | 0 | } |
202 | | |
203 | | /* Decodes the packet-in message starting at 'oh' into '*pin'. Populates |
204 | | * 'pin->packet' and 'pin->packet_len' with the part of the packet actually |
205 | | * included in the message. If 'total_lenp' is nonnull, populates |
206 | | * '*total_lenp' with the original length of the packet (which is larger than |
207 | | * 'packet->len' if only part of the packet was included). If 'buffer_idp' is |
208 | | * nonnull, stores the packet's buffer ID in '*buffer_idp' (UINT32_MAX if it |
209 | | * was not buffered). |
210 | | * |
211 | | * Populates 'continuation', if nonnull, with the continuation data from the |
212 | | * packet-in (an empty buffer, if 'oh' did not contain continuation data). The |
213 | | * format of this data is supposed to be opaque to anything other than |
214 | | * ovs-vswitchd, so that in any other process the only reasonable use of this |
215 | | * data is to be copied into an NXT_RESUME message via ofputil_encode_resume(). |
216 | | * |
217 | | * This function points 'pin->packet' into 'oh', so the caller should not free |
218 | | * it separately from the original OpenFlow message. This is also true for |
219 | | * 'pin->userdata' (which could also end up NULL if there is no userdata). |
220 | | * |
221 | | * 'vl_mff_map' is an optional parameter that is used to validate the length |
222 | | * of variable length mf_fields in 'match'. If it is not provided, the |
223 | | * default mf_fields with maximum length will be used. |
224 | | * |
225 | | * Returns 0 if successful, otherwise an OpenFlow error code. */ |
226 | | enum ofperr |
227 | | ofputil_decode_packet_in(const struct ofp_header *oh, bool loose, |
228 | | const struct tun_table *tun_table, |
229 | | const struct vl_mff_map *vl_mff_map, |
230 | | struct ofputil_packet_in *pin, |
231 | | size_t *total_lenp, uint32_t *buffer_idp, |
232 | | struct ofpbuf *continuation) |
233 | 0 | { |
234 | 0 | uint32_t buffer_id; |
235 | 0 | size_t total_len; |
236 | |
|
237 | 0 | memset(pin, 0, sizeof *pin); |
238 | 0 | pin->cookie = OVS_BE64_MAX; |
239 | 0 | if (continuation) { |
240 | 0 | ofpbuf_use_const(continuation, NULL, 0); |
241 | 0 | } |
242 | |
|
243 | 0 | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); |
244 | 0 | enum ofpraw raw = ofpraw_pull_assert(&b); |
245 | 0 | if (raw == OFPRAW_OFPT13_PACKET_IN || raw == OFPRAW_OFPT12_PACKET_IN) { |
246 | 0 | const struct ofp12_packet_in *opi = ofpbuf_pull(&b, sizeof *opi); |
247 | 0 | const ovs_be64 *cookie = (raw == OFPRAW_OFPT13_PACKET_IN |
248 | 0 | ? ofpbuf_pull(&b, sizeof *cookie) |
249 | 0 | : NULL); |
250 | 0 | enum ofperr error = oxm_pull_match_loose(&b, false, tun_table, |
251 | 0 | &pin->flow_metadata); |
252 | 0 | pin->flow_metadata.flow.tunnel.metadata.tab = tun_table; |
253 | 0 | if (error) { |
254 | 0 | return error; |
255 | 0 | } |
256 | | |
257 | 0 | if (!ofpbuf_try_pull(&b, 2)) { |
258 | 0 | return OFPERR_OFPBRC_BAD_LEN; |
259 | 0 | } |
260 | | |
261 | 0 | pin->reason = opi->reason; |
262 | 0 | pin->table_id = opi->table_id; |
263 | 0 | buffer_id = ntohl(opi->buffer_id); |
264 | 0 | total_len = ntohs(opi->total_len); |
265 | 0 | if (cookie) { |
266 | 0 | pin->cookie = *cookie; |
267 | 0 | } |
268 | |
|
269 | 0 | pin->packet = b.data; |
270 | 0 | pin->packet_len = b.size; |
271 | 0 | } else if (raw == OFPRAW_OFPT10_PACKET_IN) { |
272 | 0 | const struct ofp10_packet_in *opi; |
273 | |
|
274 | 0 | opi = ofpbuf_pull(&b, offsetof(struct ofp10_packet_in, data)); |
275 | |
|
276 | 0 | pin->packet = CONST_CAST(uint8_t *, opi->data); |
277 | 0 | pin->packet_len = b.size; |
278 | |
|
279 | 0 | match_init_catchall(&pin->flow_metadata); |
280 | 0 | match_set_in_port(&pin->flow_metadata, |
281 | 0 | u16_to_ofp(ntohs(opi->in_port))); |
282 | 0 | pin->reason = opi->reason; |
283 | 0 | buffer_id = ntohl(opi->buffer_id); |
284 | 0 | total_len = ntohs(opi->total_len); |
285 | 0 | } else if (raw == OFPRAW_OFPT11_PACKET_IN) { |
286 | 0 | const struct ofp11_packet_in *opi; |
287 | 0 | ofp_port_t in_port; |
288 | 0 | enum ofperr error; |
289 | |
|
290 | 0 | opi = ofpbuf_pull(&b, sizeof *opi); |
291 | |
|
292 | 0 | pin->packet = b.data; |
293 | 0 | pin->packet_len = b.size; |
294 | |
|
295 | 0 | buffer_id = ntohl(opi->buffer_id); |
296 | 0 | error = ofputil_port_from_ofp11(opi->in_port, &in_port); |
297 | 0 | if (error) { |
298 | 0 | return error; |
299 | 0 | } |
300 | 0 | match_init_catchall(&pin->flow_metadata); |
301 | 0 | match_set_in_port(&pin->flow_metadata, in_port); |
302 | 0 | total_len = ntohs(opi->total_len); |
303 | 0 | pin->reason = opi->reason; |
304 | 0 | pin->table_id = opi->table_id; |
305 | 0 | } else if (raw == OFPRAW_NXT_PACKET_IN) { |
306 | 0 | const struct nx_packet_in *npi; |
307 | 0 | int error; |
308 | |
|
309 | 0 | npi = ofpbuf_pull(&b, sizeof *npi); |
310 | 0 | error = nx_pull_match_loose(&b, ntohs(npi->match_len), |
311 | 0 | &pin->flow_metadata, NULL, NULL, false, |
312 | 0 | NULL); |
313 | 0 | if (error) { |
314 | 0 | return error; |
315 | 0 | } |
316 | | |
317 | 0 | if (!ofpbuf_try_pull(&b, 2)) { |
318 | 0 | return OFPERR_OFPBRC_BAD_LEN; |
319 | 0 | } |
320 | | |
321 | 0 | pin->reason = npi->reason; |
322 | 0 | pin->table_id = npi->table_id; |
323 | 0 | pin->cookie = npi->cookie; |
324 | |
|
325 | 0 | buffer_id = ntohl(npi->buffer_id); |
326 | 0 | total_len = ntohs(npi->total_len); |
327 | |
|
328 | 0 | pin->packet = b.data; |
329 | 0 | pin->packet_len = b.size; |
330 | 0 | } else if (raw == OFPRAW_NXT_PACKET_IN2 || raw == OFPRAW_NXT_RESUME) { |
331 | 0 | enum ofperr error = decode_nx_packet_in2(oh, loose, tun_table, |
332 | 0 | vl_mff_map, pin, &total_len, |
333 | 0 | &buffer_id, continuation); |
334 | 0 | if (error) { |
335 | 0 | return error; |
336 | 0 | } |
337 | 0 | } else { |
338 | 0 | OVS_NOT_REACHED(); |
339 | 0 | } |
340 | | |
341 | 0 | if (total_lenp) { |
342 | 0 | *total_lenp = total_len; |
343 | 0 | } |
344 | 0 | if (buffer_idp) { |
345 | 0 | *buffer_idp = buffer_id; |
346 | 0 | } |
347 | |
|
348 | 0 | return 0; |
349 | 0 | } |
350 | | |
351 | | static int |
352 | | encode_packet_in_reason(enum ofp_packet_in_reason reason, |
353 | | enum ofp_version version) |
354 | 0 | { |
355 | 0 | switch (reason) { |
356 | 0 | case OFPR_NO_MATCH: |
357 | 0 | case OFPR_ACTION: |
358 | 0 | case OFPR_INVALID_TTL: |
359 | 0 | return reason; |
360 | | |
361 | 0 | case OFPR_ACTION_SET: |
362 | 0 | case OFPR_GROUP: |
363 | 0 | case OFPR_PACKET_OUT: |
364 | 0 | return version < OFP14_VERSION ? OFPR_ACTION : reason; |
365 | | |
366 | 0 | case OFPR_EXPLICIT_MISS: |
367 | 0 | return version < OFP13_VERSION ? OFPR_ACTION : OFPR_NO_MATCH; |
368 | | |
369 | 0 | case OFPR_IMPLICIT_MISS: |
370 | 0 | return OFPR_NO_MATCH; |
371 | | |
372 | 0 | case OFPR_N_REASONS: |
373 | 0 | default: |
374 | 0 | OVS_NOT_REACHED(); |
375 | 0 | } |
376 | 0 | } |
377 | | |
378 | | /* Only NXT_PACKET_IN2 (not NXT_RESUME) should include NXCPT_USERDATA, so this |
379 | | * function omits it. The caller can add it itself if desired. */ |
380 | | static void |
381 | | ofputil_put_packet_in(const struct ofputil_packet_in *pin, |
382 | | enum ofp_version version, size_t include_bytes, |
383 | | struct ofpbuf *msg) |
384 | 0 | { |
385 | | /* Add packet properties. */ |
386 | 0 | ofpprop_put(msg, NXPINT_PACKET, pin->packet, include_bytes); |
387 | 0 | if (include_bytes != pin->packet_len) { |
388 | 0 | ofpprop_put_u32(msg, NXPINT_FULL_LEN, pin->packet_len); |
389 | 0 | } |
390 | | |
391 | | /* Add flow properties. */ |
392 | 0 | ofpprop_put_u8(msg, NXPINT_TABLE_ID, pin->table_id); |
393 | 0 | if (pin->cookie != OVS_BE64_MAX) { |
394 | 0 | ofpprop_put_be64(msg, NXPINT_COOKIE, pin->cookie); |
395 | 0 | } |
396 | | |
397 | | /* Add other properties. */ |
398 | 0 | ofpprop_put_u8(msg, NXPINT_REASON, |
399 | 0 | encode_packet_in_reason(pin->reason, version)); |
400 | |
|
401 | 0 | size_t start = ofpprop_start(msg, NXPINT_METADATA); |
402 | 0 | oxm_put_raw(msg, &pin->flow_metadata, version); |
403 | 0 | ofpprop_end(msg, start); |
404 | 0 | } |
405 | | |
406 | | static void |
407 | | put_actions_property(struct ofpbuf *msg, uint64_t prop_type, |
408 | | enum ofp_version version, |
409 | | const struct ofpact *actions, size_t actions_len) |
410 | 0 | { |
411 | 0 | if (actions_len) { |
412 | 0 | size_t start = ofpprop_start_nested(msg, prop_type); |
413 | 0 | ofpacts_put_openflow_actions(actions, actions_len, msg, version); |
414 | 0 | ofpprop_end(msg, start); |
415 | 0 | } |
416 | 0 | } |
417 | | |
418 | | enum nx_continuation_prop_type { |
419 | | NXCPT_BRIDGE = 0x8000, |
420 | | NXCPT_STACK, |
421 | | NXCPT_MIRRORS, |
422 | | NXCPT_CONNTRACKED, |
423 | | NXCPT_TABLE_ID, |
424 | | NXCPT_COOKIE, |
425 | | NXCPT_ACTIONS, |
426 | | NXCPT_ACTION_SET, |
427 | | NXCPT_ODP_PORT, |
428 | | }; |
429 | | |
430 | | /* Only NXT_PACKET_IN2 (not NXT_RESUME) should include NXCPT_USERDATA, so this |
431 | | * function omits it. The caller can add it itself if desired. */ |
432 | | static void |
433 | | ofputil_put_packet_in_private(const struct ofputil_packet_in_private *pin, |
434 | | enum ofp_version version, size_t include_bytes, |
435 | | struct ofpbuf *msg) |
436 | 0 | { |
437 | 0 | ofputil_put_packet_in(&pin->base, version, include_bytes, msg); |
438 | |
|
439 | 0 | size_t continuation_ofs = ofpprop_start_nested(msg, NXPINT_CONTINUATION); |
440 | 0 | size_t inner_ofs = msg->size; |
441 | |
|
442 | 0 | if (!uuid_is_zero(&pin->bridge)) { |
443 | 0 | ofpprop_put_uuid(msg, NXCPT_BRIDGE, &pin->bridge); |
444 | 0 | } |
445 | |
|
446 | 0 | struct ofpbuf pin_stack; |
447 | 0 | ofpbuf_use_const(&pin_stack, pin->stack, pin->stack_size); |
448 | |
|
449 | 0 | while (pin_stack.size) { |
450 | 0 | uint8_t len; |
451 | 0 | uint8_t *val = nx_stack_pop(&pin_stack, &len); |
452 | 0 | ofpprop_put(msg, NXCPT_STACK, val, len); |
453 | 0 | } |
454 | |
|
455 | 0 | if (pin->mirrors) { |
456 | 0 | ofpprop_put_u32(msg, NXCPT_MIRRORS, pin->mirrors); |
457 | 0 | } |
458 | |
|
459 | 0 | if (pin->conntracked) { |
460 | 0 | ofpprop_put_flag(msg, NXCPT_CONNTRACKED); |
461 | 0 | } |
462 | |
|
463 | 0 | if (pin->actions_len) { |
464 | | /* Divide 'pin->actions' into groups that begins with an |
465 | | * unroll_xlate action. For each group, emit a NXCPT_TABLE_ID and |
466 | | * NXCPT_COOKIE property (if either has changed; each is initially |
467 | | * assumed 0), then a NXCPT_ACTIONS property with the grouped |
468 | | * actions. |
469 | | * |
470 | | * The alternative is to make OFPACT_UNROLL_XLATE public. We can |
471 | | * always do that later, since this is a private property. */ |
472 | 0 | const struct ofpact *const end = ofpact_end(pin->actions, |
473 | 0 | pin->actions_len); |
474 | 0 | const struct ofpact_unroll_xlate *unroll = NULL; |
475 | 0 | uint8_t table_id = 0; |
476 | 0 | ovs_be64 cookie = 0; |
477 | |
|
478 | 0 | const struct ofpact *a; |
479 | 0 | for (a = pin->actions; ; a = ofpact_next(a)) { |
480 | 0 | if (a == end || a->type == OFPACT_UNROLL_XLATE) { |
481 | 0 | if (unroll) { |
482 | 0 | if (table_id != unroll->rule_table_id) { |
483 | 0 | ofpprop_put_u8(msg, NXCPT_TABLE_ID, |
484 | 0 | unroll->rule_table_id); |
485 | 0 | table_id = unroll->rule_table_id; |
486 | 0 | } |
487 | 0 | if (cookie != unroll->rule_cookie) { |
488 | 0 | ofpprop_put_be64(msg, NXCPT_COOKIE, |
489 | 0 | unroll->rule_cookie); |
490 | 0 | cookie = unroll->rule_cookie; |
491 | 0 | } |
492 | 0 | } |
493 | |
|
494 | 0 | const struct ofpact *start |
495 | 0 | = unroll ? ofpact_next(&unroll->ofpact) : pin->actions; |
496 | 0 | put_actions_property(msg, NXCPT_ACTIONS, version, |
497 | 0 | start, (a - start) * sizeof *a); |
498 | |
|
499 | 0 | if (a == end) { |
500 | 0 | break; |
501 | 0 | } |
502 | 0 | unroll = ofpact_get_UNROLL_XLATE(a); |
503 | 0 | } |
504 | 0 | } |
505 | 0 | } |
506 | |
|
507 | 0 | if (pin->action_set_len) { |
508 | 0 | size_t start = ofpprop_start_nested(msg, NXCPT_ACTION_SET); |
509 | 0 | ofpacts_put_openflow_actions(pin->action_set, |
510 | 0 | pin->action_set_len, msg, version); |
511 | 0 | ofpprop_end(msg, start); |
512 | 0 | } |
513 | |
|
514 | 0 | if (pin->odp_port) { |
515 | 0 | ofpprop_put_u32(msg, NXCPT_ODP_PORT, odp_to_u32(pin->odp_port)); |
516 | 0 | } |
517 | |
|
518 | 0 | if (msg->size > inner_ofs) { |
519 | 0 | ofpprop_end(msg, continuation_ofs); |
520 | 0 | } else { |
521 | 0 | msg->size = continuation_ofs; |
522 | 0 | } |
523 | 0 | } |
524 | | |
525 | | static struct ofpbuf * |
526 | | ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin) |
527 | 0 | { |
528 | 0 | struct ofp10_packet_in *opi; |
529 | 0 | struct ofpbuf *msg; |
530 | |
|
531 | 0 | msg = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION, |
532 | 0 | htonl(0), pin->packet_len); |
533 | 0 | opi = ofpbuf_put_zeros(msg, offsetof(struct ofp10_packet_in, data)); |
534 | 0 | opi->total_len = htons(pin->packet_len); |
535 | 0 | opi->in_port = htons(ofp_to_u16(pin->flow_metadata.flow.in_port.ofp_port)); |
536 | 0 | opi->reason = encode_packet_in_reason(pin->reason, OFP10_VERSION); |
537 | 0 | opi->buffer_id = htonl(UINT32_MAX); |
538 | |
|
539 | 0 | return msg; |
540 | 0 | } |
541 | | |
542 | | static struct ofpbuf * |
543 | | ofputil_encode_nx_packet_in(const struct ofputil_packet_in *pin, |
544 | | enum ofp_version version) |
545 | 0 | { |
546 | 0 | struct nx_packet_in *npi; |
547 | 0 | struct ofpbuf *msg; |
548 | 0 | size_t match_len; |
549 | | |
550 | | /* The final argument is just an estimate of the space required. */ |
551 | 0 | msg = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, version, |
552 | 0 | htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len); |
553 | 0 | ofpbuf_put_zeros(msg, sizeof *npi); |
554 | 0 | match_len = nx_put_match(msg, &pin->flow_metadata, 0, 0); |
555 | 0 | ofpbuf_put_zeros(msg, 2); |
556 | |
|
557 | 0 | npi = msg->msg; |
558 | 0 | npi->buffer_id = htonl(UINT32_MAX); |
559 | 0 | npi->total_len = htons(pin->packet_len); |
560 | 0 | npi->reason = encode_packet_in_reason(pin->reason, version); |
561 | 0 | npi->table_id = pin->table_id; |
562 | 0 | npi->cookie = pin->cookie; |
563 | 0 | npi->match_len = htons(match_len); |
564 | |
|
565 | 0 | return msg; |
566 | 0 | } |
567 | | |
568 | | static struct ofpbuf * |
569 | | ofputil_encode_nx_packet_in2(const struct ofputil_packet_in_private *pin, |
570 | | enum ofp_version version, size_t include_bytes) |
571 | 0 | { |
572 | | /* 'extra' is just an estimate of the space required. */ |
573 | 0 | size_t extra = (pin->base.packet_len |
574 | | + NXM_TYPICAL_LEN /* flow_metadata */ |
575 | 0 | + pin->stack_size * 4 |
576 | 0 | + pin->actions_len |
577 | 0 | + pin->action_set_len |
578 | 0 | + 256); /* fudge factor */ |
579 | 0 | struct ofpbuf *msg = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN2, version, |
580 | 0 | htonl(0), extra); |
581 | |
|
582 | 0 | ofputil_put_packet_in_private(pin, version, include_bytes, msg); |
583 | 0 | if (pin->base.userdata_len) { |
584 | 0 | ofpprop_put(msg, NXPINT_USERDATA, pin->base.userdata, |
585 | 0 | pin->base.userdata_len); |
586 | 0 | } |
587 | |
|
588 | 0 | ofpmsg_update_length(msg); |
589 | 0 | return msg; |
590 | 0 | } |
591 | | |
592 | | static struct ofpbuf * |
593 | | ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin) |
594 | 0 | { |
595 | 0 | struct ofp11_packet_in *opi; |
596 | 0 | struct ofpbuf *msg; |
597 | |
|
598 | 0 | msg = ofpraw_alloc_xid(OFPRAW_OFPT11_PACKET_IN, OFP11_VERSION, |
599 | 0 | htonl(0), pin->packet_len); |
600 | 0 | opi = ofpbuf_put_zeros(msg, sizeof *opi); |
601 | 0 | opi->buffer_id = htonl(UINT32_MAX); |
602 | 0 | opi->in_port = ofputil_port_to_ofp11( |
603 | 0 | pin->flow_metadata.flow.in_port.ofp_port); |
604 | 0 | opi->in_phy_port = opi->in_port; |
605 | 0 | opi->total_len = htons(pin->packet_len); |
606 | 0 | opi->reason = encode_packet_in_reason(pin->reason, OFP11_VERSION); |
607 | 0 | opi->table_id = pin->table_id; |
608 | |
|
609 | 0 | return msg; |
610 | 0 | } |
611 | | |
612 | | static struct ofpbuf * |
613 | | ofputil_encode_ofp12_packet_in(const struct ofputil_packet_in *pin, |
614 | | enum ofp_version version) |
615 | 0 | { |
616 | 0 | enum ofpraw raw = (version >= OFP13_VERSION |
617 | 0 | ? OFPRAW_OFPT13_PACKET_IN |
618 | 0 | : OFPRAW_OFPT12_PACKET_IN); |
619 | 0 | struct ofpbuf *msg; |
620 | | |
621 | | /* The final argument is just an estimate of the space required. */ |
622 | 0 | msg = ofpraw_alloc_xid(raw, version, |
623 | 0 | htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len); |
624 | |
|
625 | 0 | struct ofp12_packet_in *opi = ofpbuf_put_zeros(msg, sizeof *opi); |
626 | 0 | opi->buffer_id = htonl(UINT32_MAX); |
627 | 0 | opi->total_len = htons(pin->packet_len); |
628 | 0 | opi->reason = encode_packet_in_reason(pin->reason, version); |
629 | 0 | opi->table_id = pin->table_id; |
630 | |
|
631 | 0 | if (version >= OFP13_VERSION) { |
632 | 0 | ovs_be64 cookie = pin->cookie; |
633 | 0 | ofpbuf_put(msg, &cookie, sizeof cookie); |
634 | 0 | } |
635 | |
|
636 | 0 | oxm_put_match(msg, &pin->flow_metadata, version); |
637 | 0 | ofpbuf_put_zeros(msg, 2); |
638 | |
|
639 | 0 | return msg; |
640 | 0 | } |
641 | | |
642 | | /* Converts abstract ofputil_packet_in_private 'pin' into a PACKET_IN message |
643 | | * for 'protocol', using the packet-in format specified by 'format'. |
644 | | * |
645 | | * This function is really meant only for use by ovs-vswitchd. To any other |
646 | | * code, the "continuation" data, i.e. the data that is in struct |
647 | | * ofputil_packet_in_private but not in struct ofputil_packet_in, is supposed |
648 | | * to be opaque (and it might change from one OVS version to another). Thus, |
649 | | * if any other code wants to encode a packet-in, it should use a non-"private" |
650 | | * version of this function. (Such a version doesn't currently exist because |
651 | | * only ovs-vswitchd currently wants to encode packet-ins. If you need one, |
652 | | * write it...) */ |
653 | | struct ofpbuf * |
654 | | ofputil_encode_packet_in_private(const struct ofputil_packet_in_private *pin, |
655 | | enum ofputil_protocol protocol, |
656 | | enum ofputil_packet_in_format format) |
657 | 0 | { |
658 | 0 | enum ofp_version version = ofputil_protocol_to_ofp_version(protocol); |
659 | |
|
660 | 0 | struct ofpbuf *msg; |
661 | 0 | switch (format) { |
662 | 0 | case OFPUTIL_PACKET_IN_STD: |
663 | 0 | switch (protocol) { |
664 | 0 | case OFPUTIL_P_OF10_STD: |
665 | 0 | case OFPUTIL_P_OF10_STD_TID: |
666 | 0 | case OFPUTIL_P_OF10_NXM: |
667 | 0 | case OFPUTIL_P_OF10_NXM_TID: |
668 | 0 | msg = ofputil_encode_ofp10_packet_in(&pin->base); |
669 | 0 | break; |
670 | | |
671 | 0 | case OFPUTIL_P_OF11_STD: |
672 | 0 | msg = ofputil_encode_ofp11_packet_in(&pin->base); |
673 | 0 | break; |
674 | | |
675 | 0 | case OFPUTIL_P_OF12_OXM: |
676 | 0 | case OFPUTIL_P_OF13_OXM: |
677 | 0 | case OFPUTIL_P_OF14_OXM: |
678 | 0 | case OFPUTIL_P_OF15_OXM: |
679 | 0 | msg = ofputil_encode_ofp12_packet_in(&pin->base, version); |
680 | 0 | break; |
681 | | |
682 | 0 | default: |
683 | 0 | OVS_NOT_REACHED(); |
684 | 0 | } |
685 | 0 | break; |
686 | | |
687 | 0 | case OFPUTIL_PACKET_IN_NXT: |
688 | 0 | msg = ofputil_encode_nx_packet_in(&pin->base, version); |
689 | 0 | break; |
690 | | |
691 | 0 | case OFPUTIL_PACKET_IN_NXT2: |
692 | 0 | return ofputil_encode_nx_packet_in2(pin, version, |
693 | 0 | pin->base.packet_len); |
694 | | |
695 | 0 | default: |
696 | 0 | OVS_NOT_REACHED(); |
697 | 0 | } |
698 | | |
699 | 0 | ofpbuf_put(msg, pin->base.packet, pin->base.packet_len); |
700 | 0 | ofpmsg_update_length(msg); |
701 | 0 | return msg; |
702 | 0 | } |
703 | | |
704 | | /* Returns a string form of 'reason'. The return value is either a statically |
705 | | * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. |
706 | | * 'bufsize' should be at least OFPUTIL_PACKET_IN_REASON_BUFSIZE. */ |
707 | | const char * |
708 | | ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason reason, |
709 | | char *reasonbuf, size_t bufsize) |
710 | 0 | { |
711 | 0 | switch (reason) { |
712 | 0 | case OFPR_NO_MATCH: |
713 | 0 | return "no_match"; |
714 | 0 | case OFPR_ACTION: |
715 | 0 | return "action"; |
716 | 0 | case OFPR_INVALID_TTL: |
717 | 0 | return "invalid_ttl"; |
718 | 0 | case OFPR_ACTION_SET: |
719 | 0 | return "action_set"; |
720 | 0 | case OFPR_GROUP: |
721 | 0 | return "group"; |
722 | 0 | case OFPR_PACKET_OUT: |
723 | 0 | return "packet_out"; |
724 | 0 | case OFPR_EXPLICIT_MISS: |
725 | 0 | case OFPR_IMPLICIT_MISS: |
726 | 0 | return ""; |
727 | | |
728 | 0 | case OFPR_N_REASONS: |
729 | 0 | default: |
730 | 0 | snprintf(reasonbuf, bufsize, "%d", (int) reason); |
731 | 0 | return reasonbuf; |
732 | 0 | } |
733 | 0 | } |
734 | | |
735 | | bool |
736 | | ofputil_packet_in_reason_from_string(const char *s, |
737 | | enum ofp_packet_in_reason *reason) |
738 | 0 | { |
739 | 0 | int i; |
740 | |
|
741 | 0 | for (i = 0; i < OFPR_N_REASONS; i++) { |
742 | 0 | char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE]; |
743 | 0 | const char *reason_s; |
744 | |
|
745 | 0 | reason_s = ofputil_packet_in_reason_to_string(i, reasonbuf, |
746 | 0 | sizeof reasonbuf); |
747 | 0 | if (!strcasecmp(s, reason_s)) { |
748 | 0 | *reason = i; |
749 | 0 | return true; |
750 | 0 | } |
751 | 0 | } |
752 | 0 | return false; |
753 | 0 | } |
754 | | |
755 | | /* Returns a newly allocated NXT_RESUME message for 'pin', with the given |
756 | | * 'continuation', for 'protocol'. This message is suitable for resuming the |
757 | | * pipeline traveral of the packet represented by 'pin', if sent to the switch |
758 | | * from which 'pin' was received. */ |
759 | | struct ofpbuf * |
760 | | ofputil_encode_resume(const struct ofputil_packet_in *pin, |
761 | | const struct ofpbuf *continuation, |
762 | | enum ofputil_protocol protocol) |
763 | 0 | { |
764 | 0 | enum ofp_version version = ofputil_protocol_to_ofp_version(protocol); |
765 | 0 | size_t extra = pin->packet_len + NXM_TYPICAL_LEN + continuation->size; |
766 | 0 | struct ofpbuf *msg = ofpraw_alloc_xid(OFPRAW_NXT_RESUME, version, |
767 | 0 | 0, extra); |
768 | 0 | ofputil_put_packet_in(pin, version, pin->packet_len, msg); |
769 | 0 | ofpprop_put_nested(msg, NXPINT_CONTINUATION, continuation); |
770 | 0 | ofpmsg_update_length(msg); |
771 | 0 | return msg; |
772 | 0 | } |
773 | | |
774 | | static enum ofperr |
775 | | parse_stack_prop(const struct ofpbuf *property, struct ofpbuf *stack) |
776 | 0 | { |
777 | 0 | unsigned int len = ofpbuf_msgsize(property); |
778 | 0 | if (len > sizeof(union mf_subvalue)) { |
779 | 0 | VLOG_WARN_RL(&rl, "NXCPT_STACK property has bad length %u", |
780 | 0 | len); |
781 | 0 | return OFPERR_OFPBPC_BAD_LEN; |
782 | 0 | } |
783 | 0 | nx_stack_push_bottom(stack, property->msg, len); |
784 | 0 | return 0; |
785 | 0 | } |
786 | | |
787 | | static enum ofperr |
788 | | parse_actions_property(struct ofpbuf *property, enum ofp_version version, |
789 | | struct ofpbuf *ofpacts) |
790 | 0 | { |
791 | 0 | if (!ofpbuf_try_pull(property, ROUND_UP(ofpbuf_headersize(property), 8))) { |
792 | 0 | VLOG_WARN_RL(&rl, "actions property has bad length %"PRIu32, |
793 | 0 | property->size); |
794 | 0 | return OFPERR_OFPBPC_BAD_LEN; |
795 | 0 | } |
796 | | |
797 | 0 | return ofpacts_pull_openflow_actions(property, property->size, |
798 | 0 | version, NULL, NULL, ofpacts); |
799 | 0 | } |
800 | | |
801 | | /* This is like ofputil_decode_packet_in(), except that it decodes the |
802 | | * continuation data into 'pin'. The format of this data is supposed to be |
803 | | * opaque to any process other than ovs-vswitchd, so this function should not |
804 | | * be used outside ovs-vswitchd. |
805 | | * |
806 | | * 'vl_mff_map' is an optional parameter that is used to validate the length |
807 | | * of variable length mf_fields in 'match'. If it is not provided, the |
808 | | * default mf_fields with maximum length will be used. |
809 | | * |
810 | | * When successful, 'pin' contains some dynamically allocated data. Call |
811 | | * ofputil_packet_in_private_destroy() to free this data. */ |
812 | | enum ofperr |
813 | | ofputil_decode_packet_in_private(const struct ofp_header *oh, bool loose, |
814 | | const struct tun_table *tun_table, |
815 | | const struct vl_mff_map *vl_mff_map, |
816 | | struct ofputil_packet_in_private *pin, |
817 | | size_t *total_len, uint32_t *buffer_id) |
818 | 0 | { |
819 | 0 | memset(pin, 0, sizeof *pin); |
820 | |
|
821 | 0 | struct ofpbuf continuation; |
822 | 0 | enum ofperr error; |
823 | 0 | error = ofputil_decode_packet_in(oh, loose, tun_table, vl_mff_map, |
824 | 0 | &pin->base, total_len, buffer_id, |
825 | 0 | &continuation); |
826 | 0 | if (error) { |
827 | 0 | return error; |
828 | 0 | } |
829 | | |
830 | 0 | struct ofpbuf actions, action_set; |
831 | 0 | ofpbuf_init(&actions, 0); |
832 | 0 | ofpbuf_init(&action_set, 0); |
833 | |
|
834 | 0 | uint8_t table_id = 0; |
835 | 0 | ovs_be64 cookie = 0; |
836 | |
|
837 | 0 | struct ofpbuf stack; |
838 | 0 | ofpbuf_init(&stack, 0); |
839 | |
|
840 | 0 | while (continuation.size > 0) { |
841 | 0 | struct ofpbuf payload; |
842 | 0 | uint64_t type; |
843 | |
|
844 | 0 | error = ofpprop_pull(&continuation, &payload, &type); |
845 | 0 | if (error) { |
846 | 0 | break; |
847 | 0 | } |
848 | | |
849 | 0 | switch (type) { |
850 | 0 | case NXCPT_BRIDGE: |
851 | 0 | error = ofpprop_parse_uuid(&payload, &pin->bridge); |
852 | 0 | break; |
853 | | |
854 | 0 | case NXCPT_STACK: |
855 | 0 | error = parse_stack_prop(&payload, &stack); |
856 | 0 | break; |
857 | | |
858 | 0 | case NXCPT_MIRRORS: |
859 | 0 | error = ofpprop_parse_u32(&payload, &pin->mirrors); |
860 | 0 | break; |
861 | | |
862 | 0 | case NXCPT_CONNTRACKED: |
863 | 0 | pin->conntracked = true; |
864 | 0 | break; |
865 | | |
866 | 0 | case NXCPT_TABLE_ID: |
867 | 0 | error = ofpprop_parse_u8(&payload, &table_id); |
868 | 0 | break; |
869 | | |
870 | 0 | case NXCPT_COOKIE: |
871 | 0 | error = ofpprop_parse_be64(&payload, &cookie); |
872 | 0 | break; |
873 | | |
874 | 0 | case NXCPT_ACTIONS: { |
875 | 0 | struct ofpact_unroll_xlate *unroll |
876 | 0 | = ofpact_put_UNROLL_XLATE(&actions); |
877 | 0 | unroll->rule_table_id = table_id; |
878 | 0 | unroll->rule_cookie = cookie; |
879 | 0 | error = parse_actions_property(&payload, oh->version, &actions); |
880 | 0 | break; |
881 | 0 | } |
882 | | |
883 | 0 | case NXCPT_ACTION_SET: |
884 | 0 | error = parse_actions_property(&payload, oh->version, &action_set); |
885 | 0 | break; |
886 | | |
887 | 0 | case NXCPT_ODP_PORT: { |
888 | 0 | uint32_t value; |
889 | 0 | error = ofpprop_parse_u32(&payload, &value); |
890 | 0 | if (!error) { |
891 | 0 | pin->odp_port = u32_to_odp(value); |
892 | 0 | } |
893 | 0 | break; |
894 | 0 | } |
895 | | |
896 | 0 | default: |
897 | 0 | error = OFPPROP_UNKNOWN(loose, "continuation", type); |
898 | 0 | break; |
899 | 0 | } |
900 | 0 | if (error) { |
901 | 0 | break; |
902 | 0 | } |
903 | 0 | } |
904 | | |
905 | 0 | pin->actions_len = actions.size; |
906 | 0 | pin->actions = ofpbuf_steal_data(&actions); |
907 | 0 | pin->action_set_len = action_set.size; |
908 | 0 | pin->action_set = ofpbuf_steal_data(&action_set); |
909 | 0 | pin->stack_size = stack.size; |
910 | 0 | pin->stack = ofpbuf_steal_data(&stack); |
911 | |
|
912 | 0 | if (error) { |
913 | 0 | ofputil_packet_in_private_destroy(pin); |
914 | 0 | } |
915 | |
|
916 | 0 | return error; |
917 | 0 | } |
918 | | |
919 | | static void |
920 | | format_hex_arg(struct ds *s, const uint8_t *data, size_t len) |
921 | 0 | { |
922 | 0 | for (size_t i = 0; i < len; i++) { |
923 | 0 | if (i) { |
924 | 0 | ds_put_char(s, '.'); |
925 | 0 | } |
926 | 0 | ds_put_format(s, "%02"PRIx8, data[i]); |
927 | 0 | } |
928 | 0 | } |
929 | | |
930 | | void |
931 | | ofputil_packet_in_private_format(struct ds *s, |
932 | | const struct ofputil_packet_in_private *pin, |
933 | | size_t total_len, uint32_t buffer_id, |
934 | | const struct ofputil_port_map *port_map, |
935 | | const struct ofputil_table_map *table_map, |
936 | | int verbosity) |
937 | 0 | { |
938 | 0 | char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE]; |
939 | 0 | const struct ofputil_packet_in *public = &pin->base; |
940 | |
|
941 | 0 | if (public->table_id |
942 | 0 | || ofputil_table_map_get_name(table_map, public->table_id)) { |
943 | 0 | ds_put_format(s, " table_id="); |
944 | 0 | ofputil_format_table(public->table_id, table_map, s); |
945 | 0 | } |
946 | |
|
947 | 0 | if (public->cookie != OVS_BE64_MAX) { |
948 | 0 | ds_put_format(s, " cookie=0x%"PRIx64, ntohll(public->cookie)); |
949 | 0 | } |
950 | |
|
951 | 0 | ds_put_format(s, " total_len=%"PRIuSIZE" ", total_len); |
952 | |
|
953 | 0 | match_format(&public->flow_metadata, port_map, s, OFP_DEFAULT_PRIORITY); |
954 | |
|
955 | 0 | ds_put_format(s, " (via %s)", |
956 | 0 | ofputil_packet_in_reason_to_string(public->reason, |
957 | 0 | reasonbuf, |
958 | 0 | sizeof reasonbuf)); |
959 | |
|
960 | 0 | ds_put_format(s, " data_len=%"PRIuSIZE, public->packet_len); |
961 | 0 | if (buffer_id == UINT32_MAX) { |
962 | 0 | ds_put_format(s, " (unbuffered)"); |
963 | 0 | if (total_len != public->packet_len) { |
964 | 0 | ds_put_format(s, " (***total_len != data_len***)"); |
965 | 0 | } |
966 | 0 | } else { |
967 | 0 | ds_put_format(s, " buffer=0x%08"PRIx32, buffer_id); |
968 | 0 | if (total_len < public->packet_len) { |
969 | 0 | ds_put_format(s, " (***total_len < data_len***)"); |
970 | 0 | } |
971 | 0 | } |
972 | 0 | ds_put_char(s, '\n'); |
973 | |
|
974 | 0 | if (public->userdata_len) { |
975 | 0 | ds_put_cstr(s, " userdata="); |
976 | 0 | format_hex_arg(s, pin->base.userdata, pin->base.userdata_len); |
977 | 0 | ds_put_char(s, '\n'); |
978 | 0 | } |
979 | |
|
980 | 0 | if (!uuid_is_zero(&pin->bridge)) { |
981 | 0 | ds_put_format(s, " continuation.bridge="UUID_FMT"\n", |
982 | 0 | UUID_ARGS(&pin->bridge)); |
983 | 0 | } |
984 | |
|
985 | 0 | if (pin->stack_size) { |
986 | 0 | ds_put_cstr(s, " continuation.stack=(top)"); |
987 | |
|
988 | 0 | struct ofpbuf pin_stack; |
989 | 0 | ofpbuf_use_const(&pin_stack, pin->stack, pin->stack_size); |
990 | |
|
991 | 0 | while (pin_stack.size) { |
992 | 0 | uint8_t len; |
993 | 0 | uint8_t *val = nx_stack_pop(&pin_stack, &len); |
994 | 0 | union mf_subvalue value; |
995 | |
|
996 | 0 | ds_put_char(s, ' '); |
997 | 0 | memset(&value, 0, sizeof value - len); |
998 | 0 | memcpy(&value.u8[sizeof value - len], val, len); |
999 | 0 | mf_subvalue_format(&value, s); |
1000 | 0 | } |
1001 | 0 | ds_put_cstr(s, " (bottom)\n"); |
1002 | 0 | } |
1003 | |
|
1004 | 0 | if (pin->mirrors) { |
1005 | 0 | ds_put_format(s, " continuation.mirrors=0x%"PRIx32"\n", |
1006 | 0 | pin->mirrors); |
1007 | 0 | } |
1008 | |
|
1009 | 0 | if (pin->conntracked) { |
1010 | 0 | ds_put_cstr(s, " continuation.conntracked=true\n"); |
1011 | 0 | } |
1012 | |
|
1013 | 0 | struct ofpact_format_params fp = { |
1014 | 0 | .port_map = port_map, |
1015 | 0 | .table_map = table_map, |
1016 | 0 | .s = s, |
1017 | 0 | }; |
1018 | |
|
1019 | 0 | if (pin->actions_len) { |
1020 | 0 | ds_put_cstr(s, " continuation.actions="); |
1021 | 0 | ofpacts_format(pin->actions, pin->actions_len, &fp); |
1022 | 0 | ds_put_char(s, '\n'); |
1023 | 0 | } |
1024 | |
|
1025 | 0 | if (pin->action_set_len) { |
1026 | 0 | ds_put_cstr(s, " continuation.action_set="); |
1027 | 0 | ofpacts_format(pin->action_set, pin->action_set_len, &fp); |
1028 | 0 | ds_put_char(s, '\n'); |
1029 | 0 | } |
1030 | |
|
1031 | 0 | if (pin->odp_port) { |
1032 | 0 | ds_put_format(s, " continuation.odp_port=%"PRIu32, pin->odp_port); |
1033 | 0 | ds_put_char(s, '\n'); |
1034 | 0 | } |
1035 | |
|
1036 | 0 | if (verbosity > 0) { |
1037 | 0 | char *packet = ofp_packet_to_string( |
1038 | 0 | public->packet, public->packet_len, |
1039 | 0 | public->flow_metadata.flow.packet_type); |
1040 | 0 | ds_put_cstr(s, packet); |
1041 | 0 | free(packet); |
1042 | 0 | } |
1043 | 0 | if (verbosity > 2) { |
1044 | 0 | ds_put_hex_dump(s, public->packet, public->packet_len, 0, false); |
1045 | 0 | } |
1046 | 0 | } |
1047 | | |
1048 | | /* Frees data in 'pin' that is dynamically allocated by |
1049 | | * ofputil_decode_packet_in_private(). |
1050 | | * |
1051 | | * 'pin->base' contains some pointer members that |
1052 | | * ofputil_decode_packet_in_private() doesn't initialize to newly allocated |
1053 | | * data, so this function doesn't free those. */ |
1054 | | void |
1055 | | ofputil_packet_in_private_destroy(struct ofputil_packet_in_private *pin) |
1056 | 0 | { |
1057 | 0 | if (pin) { |
1058 | 0 | free(pin->stack); |
1059 | 0 | free(pin->actions); |
1060 | 0 | free(pin->action_set); |
1061 | 0 | } |
1062 | 0 | } |
1063 | | |
1064 | | /* Converts an OFPT_PACKET_OUT in 'opo' into an abstract ofputil_packet_out in |
1065 | | * 'po'. |
1066 | | * |
1067 | | * Uses 'ofpacts' to store the abstract OFPACT_* version of the packet out |
1068 | | * message's actions. The caller must initialize 'ofpacts' and retains |
1069 | | * ownership of it. 'po->ofpacts' will point into the 'ofpacts' buffer. |
1070 | | * |
1071 | | * 'po->packet' refers to the packet data in 'oh', so the buffer containing |
1072 | | * 'oh' must not be destroyed while 'po' is being used. |
1073 | | * |
1074 | | * Returns 0 if successful, otherwise an OFPERR_* value. */ |
1075 | | enum ofperr |
1076 | | ofputil_decode_packet_out(struct ofputil_packet_out *po, |
1077 | | const struct ofp_header *oh, |
1078 | | const struct tun_table *tun_table, |
1079 | | struct ofpbuf *ofpacts) |
1080 | 0 | { |
1081 | 0 | struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); |
1082 | 0 | enum ofpraw raw = ofpraw_pull_assert(&b); |
1083 | |
|
1084 | 0 | ofpbuf_clear(ofpacts); |
1085 | 0 | match_init_catchall(&po->flow_metadata); |
1086 | 0 | if (raw == OFPRAW_OFPT15_PACKET_OUT) { |
1087 | 0 | enum ofperr error; |
1088 | 0 | const struct ofp15_packet_out *opo = ofpbuf_pull(&b, sizeof *opo); |
1089 | |
|
1090 | 0 | po->buffer_id = ntohl(opo->buffer_id); |
1091 | 0 | error = oxm_pull_match_loose(&b, true, tun_table, &po->flow_metadata); |
1092 | 0 | if (error) { |
1093 | 0 | return error; |
1094 | 0 | } |
1095 | | |
1096 | 0 | if (!po->flow_metadata.wc.masks.in_port.ofp_port) { |
1097 | 0 | return OFPERR_OFPBRC_BAD_PORT; |
1098 | 0 | } |
1099 | | |
1100 | 0 | error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len), |
1101 | 0 | oh->version, NULL, NULL, |
1102 | 0 | ofpacts); |
1103 | 0 | if (error) { |
1104 | 0 | return error; |
1105 | 0 | } |
1106 | 0 | } else if (raw == OFPRAW_OFPT11_PACKET_OUT) { |
1107 | 0 | enum ofperr error; |
1108 | 0 | ofp_port_t in_port; |
1109 | 0 | const struct ofp11_packet_out *opo = ofpbuf_pull(&b, sizeof *opo); |
1110 | |
|
1111 | 0 | po->buffer_id = ntohl(opo->buffer_id); |
1112 | 0 | error = ofputil_port_from_ofp11(opo->in_port, &in_port); |
1113 | 0 | if (error) { |
1114 | 0 | return error; |
1115 | 0 | } |
1116 | 0 | match_set_packet_type(&po->flow_metadata, htonl(PT_ETH)); |
1117 | 0 | match_set_in_port(&po->flow_metadata, in_port); |
1118 | |
|
1119 | 0 | error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len), |
1120 | 0 | oh->version, NULL, NULL, |
1121 | 0 | ofpacts); |
1122 | 0 | if (error) { |
1123 | 0 | return error; |
1124 | 0 | } |
1125 | 0 | } else if (raw == OFPRAW_OFPT10_PACKET_OUT) { |
1126 | 0 | enum ofperr error; |
1127 | 0 | const struct ofp10_packet_out *opo = ofpbuf_pull(&b, sizeof *opo); |
1128 | |
|
1129 | 0 | po->buffer_id = ntohl(opo->buffer_id); |
1130 | 0 | match_set_packet_type(&po->flow_metadata, htonl(PT_ETH)); |
1131 | 0 | match_set_in_port(&po->flow_metadata, u16_to_ofp(ntohs(opo->in_port))); |
1132 | |
|
1133 | 0 | error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len), |
1134 | 0 | oh->version, NULL, NULL, |
1135 | 0 | ofpacts); |
1136 | 0 | if (error) { |
1137 | 0 | return error; |
1138 | 0 | } |
1139 | 0 | } else { |
1140 | 0 | OVS_NOT_REACHED(); |
1141 | 0 | } |
1142 | | |
1143 | 0 | ofp_port_t in_port = po->flow_metadata.flow.in_port.ofp_port; |
1144 | 0 | if (ofp_to_u16(in_port) >= ofp_to_u16(OFPP_MAX) |
1145 | 0 | && in_port != OFPP_LOCAL |
1146 | 0 | && in_port != OFPP_NONE |
1147 | 0 | && in_port != OFPP_CONTROLLER) { |
1148 | 0 | VLOG_WARN_RL(&rl, "packet-out has bad input port %#"PRIx32, |
1149 | 0 | po->flow_metadata.flow.in_port.ofp_port); |
1150 | 0 | return OFPERR_OFPBRC_BAD_PORT; |
1151 | 0 | } |
1152 | | |
1153 | 0 | po->ofpacts = ofpacts->data; |
1154 | 0 | po->ofpacts_len = ofpacts->size; |
1155 | |
|
1156 | 0 | if (po->buffer_id == UINT32_MAX) { |
1157 | 0 | po->packet = b.data; |
1158 | 0 | po->packet_len = b.size; |
1159 | 0 | } else { |
1160 | 0 | po->packet = NULL; |
1161 | 0 | po->packet_len = 0; |
1162 | 0 | } |
1163 | |
|
1164 | 0 | return 0; |
1165 | 0 | } |
1166 | | |
1167 | | struct ofpbuf * |
1168 | | ofputil_encode_packet_out(const struct ofputil_packet_out *po, |
1169 | | enum ofputil_protocol protocol) |
1170 | 0 | { |
1171 | 0 | enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol); |
1172 | 0 | struct ofpbuf *msg; |
1173 | 0 | size_t size; |
1174 | |
|
1175 | 0 | size = po->ofpacts_len; |
1176 | 0 | if (po->buffer_id == UINT32_MAX) { |
1177 | 0 | size += po->packet_len; |
1178 | 0 | } |
1179 | |
|
1180 | 0 | switch (ofp_version) { |
1181 | 0 | case OFP10_VERSION: { |
1182 | 0 | struct ofp10_packet_out *opo; |
1183 | 0 | size_t actions_ofs; |
1184 | |
|
1185 | 0 | msg = ofpraw_alloc(OFPRAW_OFPT10_PACKET_OUT, OFP10_VERSION, size); |
1186 | 0 | ofpbuf_put_zeros(msg, sizeof *opo); |
1187 | 0 | actions_ofs = msg->size; |
1188 | 0 | ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg, |
1189 | 0 | ofp_version); |
1190 | |
|
1191 | 0 | opo = msg->msg; |
1192 | 0 | opo->buffer_id = htonl(po->buffer_id); |
1193 | 0 | opo->in_port =htons(ofp_to_u16( |
1194 | 0 | po->flow_metadata.flow.in_port.ofp_port)); |
1195 | 0 | opo->actions_len = htons(msg->size - actions_ofs); |
1196 | 0 | break; |
1197 | 0 | } |
1198 | | |
1199 | 0 | case OFP11_VERSION: |
1200 | 0 | case OFP12_VERSION: |
1201 | 0 | case OFP13_VERSION: |
1202 | 0 | case OFP14_VERSION: { |
1203 | 0 | struct ofp11_packet_out *opo; |
1204 | 0 | size_t len; |
1205 | |
|
1206 | 0 | msg = ofpraw_alloc(OFPRAW_OFPT11_PACKET_OUT, ofp_version, size); |
1207 | 0 | ofpbuf_put_zeros(msg, sizeof *opo); |
1208 | 0 | len = ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg, |
1209 | 0 | ofp_version); |
1210 | 0 | opo = msg->msg; |
1211 | 0 | opo->buffer_id = htonl(po->buffer_id); |
1212 | 0 | opo->in_port = |
1213 | 0 | ofputil_port_to_ofp11(po->flow_metadata.flow.in_port.ofp_port); |
1214 | 0 | opo->actions_len = htons(len); |
1215 | 0 | break; |
1216 | 0 | } |
1217 | | |
1218 | 0 | case OFP15_VERSION: { |
1219 | 0 | struct ofp15_packet_out *opo; |
1220 | 0 | size_t len; |
1221 | | |
1222 | | /* The final argument is just an estimate of the space required. */ |
1223 | 0 | msg = ofpraw_alloc(OFPRAW_OFPT15_PACKET_OUT, ofp_version, |
1224 | 0 | size + NXM_TYPICAL_LEN); |
1225 | 0 | ofpbuf_put_zeros(msg, sizeof *opo); |
1226 | 0 | oxm_put_match(msg, &po->flow_metadata, ofp_version); |
1227 | 0 | len = ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg, |
1228 | 0 | ofp_version); |
1229 | 0 | opo = msg->msg; |
1230 | 0 | opo->buffer_id = htonl(po->buffer_id); |
1231 | 0 | opo->actions_len = htons(len); |
1232 | 0 | break; |
1233 | 0 | } |
1234 | | |
1235 | 0 | default: |
1236 | 0 | OVS_NOT_REACHED(); |
1237 | 0 | } |
1238 | | |
1239 | 0 | if (po->buffer_id == UINT32_MAX) { |
1240 | 0 | ofpbuf_put(msg, po->packet, po->packet_len); |
1241 | 0 | } |
1242 | |
|
1243 | 0 | ofpmsg_update_length(msg); |
1244 | |
|
1245 | 0 | return msg; |
1246 | 0 | } |
1247 | | |
1248 | | void |
1249 | | ofputil_packet_out_format(struct ds *s, const struct ofputil_packet_out *po, |
1250 | | const struct ofputil_port_map *port_map, |
1251 | | const struct ofputil_table_map *table_map, |
1252 | | int verbosity) |
1253 | 0 | { |
1254 | 0 | ds_put_char(s, ' '); |
1255 | 0 | match_format(&po->flow_metadata, port_map, s, OFP_DEFAULT_PRIORITY); |
1256 | |
|
1257 | 0 | ds_put_cstr(s, " actions="); |
1258 | 0 | struct ofpact_format_params fp = { |
1259 | 0 | .port_map = port_map, |
1260 | 0 | .table_map = table_map, |
1261 | 0 | .s = s, |
1262 | 0 | }; |
1263 | 0 | ofpacts_format(po->ofpacts, po->ofpacts_len, &fp); |
1264 | |
|
1265 | 0 | if (po->buffer_id == UINT32_MAX) { |
1266 | 0 | ds_put_format(s, " data_len=%"PRIuSIZE, po->packet_len); |
1267 | 0 | if (verbosity > 0 && po->packet_len > 0) { |
1268 | 0 | ovs_be32 po_packet_type = po->flow_metadata.flow.packet_type; |
1269 | 0 | char *packet = ofp_packet_to_string(po->packet, po->packet_len, |
1270 | 0 | po_packet_type); |
1271 | 0 | ds_put_char(s, '\n'); |
1272 | 0 | ds_put_cstr(s, packet); |
1273 | 0 | free(packet); |
1274 | 0 | } |
1275 | 0 | if (verbosity > 2) { |
1276 | 0 | ds_put_hex_dump(s, po->packet, po->packet_len, 0, false); |
1277 | 0 | } |
1278 | 0 | } else { |
1279 | 0 | ds_put_format(s, " buffer=0x%08"PRIx32, po->buffer_id); |
1280 | 0 | } |
1281 | 0 | } |
1282 | | |
1283 | | /* Parse a string representation of a OFPT_PACKET_OUT to '*po'. If successful, |
1284 | | * both 'po->ofpacts' and 'po->packet' must be free()d by the caller. */ |
1285 | | static char * OVS_WARN_UNUSED_RESULT |
1286 | | parse_ofp_packet_out_str__(struct ofputil_packet_out *po, char *string, |
1287 | | const struct ofputil_port_map *port_map, |
1288 | | const struct ofputil_table_map *table_map, |
1289 | | enum ofputil_protocol *usable_protocols) |
1290 | 0 | { |
1291 | 0 | enum ofputil_protocol action_usable_protocols; |
1292 | 0 | uint64_t stub[256 / 8]; |
1293 | 0 | struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub); |
1294 | 0 | struct dp_packet *packet = NULL; |
1295 | 0 | char *act_str = NULL; |
1296 | 0 | char *name, *value; |
1297 | 0 | char *error = NULL; |
1298 | |
|
1299 | 0 | *usable_protocols = OFPUTIL_P_ANY; |
1300 | |
|
1301 | 0 | *po = (struct ofputil_packet_out) { |
1302 | 0 | .buffer_id = UINT32_MAX, |
1303 | 0 | }; |
1304 | 0 | match_init_catchall(&po->flow_metadata); |
1305 | 0 | match_set_in_port(&po->flow_metadata, OFPP_CONTROLLER); |
1306 | |
|
1307 | 0 | act_str = ofp_extract_actions(string); |
1308 | |
|
1309 | 0 | while (ofputil_parse_key_value(&string, &name, &value)) { |
1310 | 0 | if (!*value) { |
1311 | 0 | error = xasprintf("field %s missing value", name); |
1312 | 0 | goto out; |
1313 | 0 | } |
1314 | | |
1315 | 0 | if (!strcmp(name, "in_port")) { |
1316 | 0 | ofp_port_t in_port; |
1317 | 0 | if (!ofputil_port_from_string(value, port_map, &in_port)) { |
1318 | 0 | error = xasprintf("%s is not a valid OpenFlow port", value); |
1319 | 0 | goto out; |
1320 | 0 | } |
1321 | 0 | if (ofp_to_u16(in_port) > ofp_to_u16(OFPP_MAX) |
1322 | 0 | && in_port != OFPP_LOCAL |
1323 | 0 | && in_port != OFPP_NONE |
1324 | 0 | && in_port != OFPP_CONTROLLER) { |
1325 | 0 | error = xasprintf( |
1326 | 0 | "%s is not a valid OpenFlow port for PACKET_OUT", |
1327 | 0 | value); |
1328 | 0 | goto out; |
1329 | 0 | } |
1330 | 0 | match_set_in_port(&po->flow_metadata, in_port); |
1331 | 0 | } else if (!strcmp(name, "packet_type")) { |
1332 | 0 | char *ns = value; |
1333 | 0 | char *ns_type = strstr(value, ","); |
1334 | 0 | if (ns_type) { |
1335 | 0 | ovs_be32 packet_type; |
1336 | 0 | *ns_type = '\0'; |
1337 | 0 | packet_type = PACKET_TYPE_BE(strtoul(ns, NULL, 0), |
1338 | 0 | strtoul(++ns_type, NULL, 0)); |
1339 | 0 | match_set_packet_type(&po->flow_metadata, packet_type); |
1340 | 0 | } else { |
1341 | 0 | error = xasprintf("%s(%s) can't be interpreted", name, value); |
1342 | 0 | goto out; |
1343 | 0 | } |
1344 | 0 | } else if (!strcmp(name, "packet")) { |
1345 | 0 | const char *error_msg = eth_from_hex(value, &packet); |
1346 | 0 | if (error_msg) { |
1347 | 0 | error = xasprintf("%s: %s", name, error_msg); |
1348 | 0 | goto out; |
1349 | 0 | } |
1350 | 0 | } else { |
1351 | 0 | const struct mf_field *mf = mf_from_name(name); |
1352 | 0 | if (!mf) { |
1353 | 0 | error = xasprintf("unknown keyword %s", name); |
1354 | 0 | goto out; |
1355 | 0 | } |
1356 | | |
1357 | 0 | error = ofp_parse_field(mf, value, port_map, &po->flow_metadata, |
1358 | 0 | usable_protocols); |
1359 | 0 | if (error) { |
1360 | 0 | goto out; |
1361 | 0 | } |
1362 | 0 | if (!mf_is_pipeline_field(mf)) { |
1363 | 0 | error = xasprintf("%s is not a valid pipeline field " |
1364 | 0 | "for PACKET_OUT", name); |
1365 | 0 | goto out; |
1366 | 0 | } |
1367 | 0 | } |
1368 | 0 | } |
1369 | | |
1370 | 0 | if (!packet || !dp_packet_size(packet)) { |
1371 | 0 | error = xstrdup("must specify packet"); |
1372 | 0 | goto out; |
1373 | 0 | } |
1374 | | |
1375 | 0 | if (act_str) { |
1376 | 0 | struct ofpact_parse_params pp = { |
1377 | 0 | .port_map = port_map, |
1378 | 0 | .table_map = table_map, |
1379 | 0 | .ofpacts = &ofpacts, |
1380 | 0 | .usable_protocols = &action_usable_protocols, |
1381 | 0 | }; |
1382 | 0 | error = ofpacts_parse_actions(act_str, &pp); |
1383 | 0 | *usable_protocols &= action_usable_protocols; |
1384 | 0 | if (error) { |
1385 | 0 | goto out; |
1386 | 0 | } |
1387 | 0 | } |
1388 | 0 | po->ofpacts_len = ofpacts.size; |
1389 | 0 | po->ofpacts = ofpbuf_steal_data(&ofpacts); |
1390 | |
|
1391 | 0 | po->packet_len = dp_packet_size(packet); |
1392 | 0 | po->packet = dp_packet_steal_data(packet); |
1393 | 0 | out: |
1394 | 0 | ofpbuf_uninit(&ofpacts); |
1395 | 0 | dp_packet_delete(packet); |
1396 | 0 | return error; |
1397 | 0 | } |
1398 | | |
1399 | | /* Convert 'str_' (as described in the Packet-Out Syntax section of the |
1400 | | * ovs-ofctl man page) into 'po' for sending a OFPT_PACKET_OUT message to a |
1401 | | * switch. Returns the set of usable protocols in '*usable_protocols'. |
1402 | | * |
1403 | | * Returns NULL if successful, otherwise a malloc()'d string describing the |
1404 | | * error. The caller is responsible for freeing the returned string. |
1405 | | * If successful, both 'po->ofpacts' and 'po->packet' must be free()d by |
1406 | | * the caller. */ |
1407 | | char * OVS_WARN_UNUSED_RESULT |
1408 | | parse_ofp_packet_out_str(struct ofputil_packet_out *po, const char *str_, |
1409 | | const struct ofputil_port_map *port_map, |
1410 | | const struct ofputil_table_map *table_map, |
1411 | | enum ofputil_protocol *usable_protocols) |
1412 | 0 | { |
1413 | 0 | char *string = xstrdup(str_); |
1414 | 0 | char *error; |
1415 | |
|
1416 | 0 | error = parse_ofp_packet_out_str__(po, string, port_map, table_map, |
1417 | 0 | usable_protocols); |
1418 | 0 | if (error) { |
1419 | 0 | po->ofpacts = NULL; |
1420 | 0 | po->ofpacts_len = 0; |
1421 | 0 | } |
1422 | |
|
1423 | 0 | free(string); |
1424 | 0 | return error; |
1425 | 0 | } |