]>
Commit | Line | Data |
---|---|---|
3b7cb7e1 | 1 | /* |
80b6743d | 2 | * Copyright (c) 2015, 2016, 2017 Nicira, Inc. |
3b7cb7e1 BP |
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> | |
3b7cb7e1 BP |
18 | #include <stdarg.h> |
19 | #include <stdbool.h> | |
467085fd | 20 | #include "bitmap.h" |
f4248336 | 21 | #include "byte-order.h" |
3b7cb7e1 | 22 | #include "compiler.h" |
42814145 | 23 | #include "ovn-dhcp.h" |
467085fd | 24 | #include "hash.h" |
667e2b0b | 25 | #include "logical-fields.h" |
42814145 | 26 | #include "nx-match.h" |
b598f214 | 27 | #include "openvswitch/dynamic-string.h" |
d5a76da4 BP |
28 | #include "openvswitch/hmap.h" |
29 | #include "openvswitch/json.h" | |
b598f214 | 30 | #include "openvswitch/ofp-actions.h" |
64c96779 | 31 | #include "openvswitch/ofpbuf.h" |
d5a76da4 | 32 | #include "openvswitch/vlog.h" |
8b2ed684 AR |
33 | #include "ovn/actions.h" |
34 | #include "ovn/expr.h" | |
35 | #include "ovn/lex.h" | |
42814145 | 36 | #include "packets.h" |
ee89ea7b | 37 | #include "openvswitch/shash.h" |
78aab811 | 38 | #include "simap.h" |
3b7cb7e1 | 39 | |
d5a76da4 BP |
40 | VLOG_DEFINE_THIS_MODULE(actions); |
41 | \f | |
42 | /* Prototypes for functions to be defined by each action. */ | |
43 | #define OVNACT(ENUM, STRUCT) \ | |
44 | static void format_##ENUM(const struct STRUCT *, struct ds *); \ | |
45 | static void encode_##ENUM(const struct STRUCT *, \ | |
46 | const struct ovnact_encode_params *, \ | |
47 | struct ofpbuf *ofpacts); \ | |
80b6743d | 48 | static void STRUCT##_free(struct STRUCT *a); |
d5a76da4 BP |
49 | OVNACTS |
50 | #undef OVNACT | |
51 | \f | |
52 | /* Helpers. */ | |
53 | ||
54 | /* Implementation of ovnact_put_<ENUM>(). */ | |
55 | void * | |
56 | ovnact_put(struct ofpbuf *ovnacts, enum ovnact_type type, size_t len) | |
57 | { | |
8a41ad8e | 58 | ovs_assert(len == OVNACT_ALIGN(len)); |
d5a76da4 BP |
59 | |
60 | ovnacts->header = ofpbuf_put_uninit(ovnacts, len); | |
8a41ad8e | 61 | struct ovnact *ovnact = ovnacts->header; |
d5a76da4 BP |
62 | ovnact_init(ovnact, type, len); |
63 | return ovnact; | |
64 | } | |
65 | ||
66 | /* Implementation of ovnact_init_<ENUM>(). */ | |
67 | void | |
68 | ovnact_init(struct ovnact *ovnact, enum ovnact_type type, size_t len) | |
69 | { | |
8a41ad8e | 70 | ovs_assert(len == OVNACT_ALIGN(len)); |
d5a76da4 BP |
71 | memset(ovnact, 0, len); |
72 | ovnact->type = type; | |
73 | ovnact->len = len; | |
74 | } | |
75 | ||
76 | static size_t | |
77 | encode_start_controller_op(enum action_opcode opcode, bool pause, | |
78 | struct ofpbuf *ofpacts) | |
79 | { | |
80 | size_t ofs = ofpacts->size; | |
81 | ||
82 | struct ofpact_controller *oc = ofpact_put_CONTROLLER(ofpacts); | |
83 | oc->max_len = UINT16_MAX; | |
84 | oc->reason = OFPR_ACTION; | |
85 | oc->pause = pause; | |
86 | ||
87 | struct action_header ah = { .opcode = htonl(opcode) }; | |
88 | ofpbuf_put(ofpacts, &ah, sizeof ah); | |
89 | ||
90 | return ofs; | |
91 | } | |
92 | ||
93 | static void | |
94 | encode_finish_controller_op(size_t ofs, struct ofpbuf *ofpacts) | |
95 | { | |
96 | struct ofpact_controller *oc = ofpbuf_at_assert(ofpacts, ofs, sizeof *oc); | |
97 | ofpacts->header = oc; | |
98 | oc->userdata_len = ofpacts->size - (ofs + sizeof *oc); | |
99 | ofpact_finish_CONTROLLER(ofpacts, &oc); | |
100 | } | |
101 | ||
102 | static void | |
103 | encode_controller_op(enum action_opcode opcode, struct ofpbuf *ofpacts) | |
104 | { | |
105 | size_t ofs = encode_start_controller_op(opcode, false, ofpacts); | |
106 | encode_finish_controller_op(ofs, ofpacts); | |
107 | } | |
108 | ||
109 | static void | |
110 | init_stack(struct ofpact_stack *stack, enum mf_field_id field) | |
111 | { | |
112 | stack->subfield.field = mf_from_id(field); | |
113 | stack->subfield.ofs = 0; | |
114 | stack->subfield.n_bits = stack->subfield.field->n_bits; | |
115 | } | |
116 | ||
117 | struct arg { | |
118 | const struct mf_subfield src; | |
119 | enum mf_field_id dst; | |
120 | }; | |
121 | ||
122 | static void | |
123 | encode_setup_args(const struct arg args[], size_t n_args, | |
124 | struct ofpbuf *ofpacts) | |
125 | { | |
126 | /* 1. Save all of the destinations that will be modified. */ | |
127 | for (const struct arg *a = args; a < &args[n_args]; a++) { | |
128 | ovs_assert(a->src.n_bits == mf_from_id(a->dst)->n_bits); | |
129 | if (a->src.field->id != a->dst) { | |
130 | init_stack(ofpact_put_STACK_PUSH(ofpacts), a->dst); | |
131 | } | |
132 | } | |
133 | ||
134 | /* 2. Push the sources, in reverse order. */ | |
135 | for (size_t i = n_args - 1; i < n_args; i--) { | |
136 | const struct arg *a = &args[i]; | |
137 | if (a->src.field->id != a->dst) { | |
138 | ofpact_put_STACK_PUSH(ofpacts)->subfield = a->src; | |
139 | } | |
140 | } | |
141 | ||
142 | /* 3. Pop the sources into the destinations. */ | |
143 | for (const struct arg *a = args; a < &args[n_args]; a++) { | |
144 | if (a->src.field->id != a->dst) { | |
145 | init_stack(ofpact_put_STACK_POP(ofpacts), a->dst); | |
146 | } | |
147 | } | |
148 | } | |
149 | ||
150 | static void | |
151 | encode_restore_args(const struct arg args[], size_t n_args, | |
152 | struct ofpbuf *ofpacts) | |
153 | { | |
154 | for (size_t i = n_args - 1; i < n_args; i--) { | |
155 | const struct arg *a = &args[i]; | |
156 | if (a->src.field->id != a->dst) { | |
157 | init_stack(ofpact_put_STACK_POP(ofpacts), a->dst); | |
158 | } | |
159 | } | |
160 | } | |
161 | ||
162 | static void | |
163 | put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits, | |
164 | struct ofpbuf *ofpacts) | |
165 | { | |
128684a6 JR |
166 | struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts, |
167 | mf_from_id(dst), NULL, | |
168 | NULL); | |
d5a76da4 | 169 | ovs_be64 n_value = htonll(value); |
128684a6 JR |
170 | bitwise_copy(&n_value, 8, 0, sf->value, sf->field->n_bytes, ofs, n_bits); |
171 | bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs, n_bits); | |
d5a76da4 BP |
172 | } |
173 | \f | |
174 | /* Context maintained during ovnacts_parse(). */ | |
3b7cb7e1 | 175 | struct action_context { |
d5a76da4 | 176 | const struct ovnact_parse_params *pp; /* Parameters. */ |
3b7cb7e1 | 177 | struct lexer *lexer; /* Lexer for pulling more tokens. */ |
d5a76da4 | 178 | struct ofpbuf *ovnacts; /* Actions. */ |
3b7cb7e1 BP |
179 | struct expr *prereqs; /* Prerequisites to apply to match. */ |
180 | }; | |
181 | ||
bac29564 | 182 | static void parse_actions(struct action_context *, enum lex_type sentinel); |
d8681a83 | 183 | |
3b7cb7e1 | 184 | static bool |
9aef3c1b BP |
185 | action_parse_field(struct action_context *ctx, |
186 | int n_bits, bool rw, struct expr_field *f) | |
3b7cb7e1 | 187 | { |
9aef3c1b | 188 | if (!expr_field_parse(ctx->lexer, ctx->pp->symtab, f, &ctx->prereqs)) { |
3b7cb7e1 BP |
189 | return false; |
190 | } | |
d5a76da4 | 191 | |
9aef3c1b BP |
192 | char *error = expr_type_check(f, n_bits, rw); |
193 | if (error) { | |
194 | lexer_error(ctx->lexer, "%s", error); | |
195 | free(error); | |
d5a76da4 | 196 | return false; |
3b7cb7e1 | 197 | } |
3b7cb7e1 | 198 | |
9aef3c1b | 199 | return true; |
558ec83d BP |
200 | } |
201 | ||
d5a76da4 BP |
202 | static bool |
203 | action_parse_port(struct action_context *ctx, uint16_t *port) | |
204 | { | |
205 | if (lexer_is_int(ctx->lexer)) { | |
206 | int value = ntohll(ctx->lexer->token.value.integer); | |
207 | if (value <= UINT16_MAX) { | |
208 | *port = value; | |
209 | lexer_get(ctx->lexer); | |
210 | return true; | |
211 | } | |
212 | } | |
9aef3c1b | 213 | lexer_syntax_error(ctx->lexer, "expecting port number"); |
d5a76da4 BP |
214 | return false; |
215 | } | |
216 | ||
217 | /* Parses 'prerequisite' as an expression in the context of 'ctx', then adds it | |
218 | * as a conjunction with the existing 'ctx->prereqs'. */ | |
219 | static void | |
220 | add_prerequisite(struct action_context *ctx, const char *prerequisite) | |
221 | { | |
222 | struct expr *expr; | |
223 | char *error; | |
224 | ||
225 | expr = expr_parse_string(prerequisite, ctx->pp->symtab, NULL, &error); | |
226 | ovs_assert(!error); | |
227 | ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr); | |
228 | } | |
80b6743d BP |
229 | |
230 | static void | |
231 | ovnact_null_free(struct ovnact_null *a OVS_UNUSED) | |
232 | { | |
233 | } | |
d5a76da4 BP |
234 | \f |
235 | static void | |
236 | format_OUTPUT(const struct ovnact_null *a OVS_UNUSED, struct ds *s) | |
237 | { | |
238 | ds_put_cstr(s, "output;"); | |
239 | } | |
240 | ||
241 | static void | |
242 | emit_resubmit(struct ofpbuf *ofpacts, uint8_t ptable) | |
243 | { | |
244 | struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(ofpacts); | |
245 | resubmit->in_port = OFPP_IN_PORT; | |
246 | resubmit->table_id = ptable; | |
247 | } | |
248 | ||
249 | static void | |
250 | encode_OUTPUT(const struct ovnact_null *a OVS_UNUSED, | |
251 | const struct ovnact_encode_params *ep, | |
252 | struct ofpbuf *ofpacts) | |
253 | { | |
254 | emit_resubmit(ofpacts, ep->output_ptable); | |
255 | } | |
d5a76da4 | 256 | \f |
558ec83d | 257 | static void |
d5a76da4 | 258 | parse_NEXT(struct action_context *ctx) |
558ec83d | 259 | { |
d5a76da4 | 260 | if (!ctx->pp->n_tables) { |
9aef3c1b | 261 | lexer_error(ctx->lexer, "\"next\" action not allowed here."); |
558ec83d BP |
262 | } else if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { |
263 | int ltable; | |
264 | ||
9aef3c1b BP |
265 | if (!lexer_force_int(ctx->lexer, <able) || |
266 | !lexer_force_match(ctx->lexer, LEX_T_RPAREN)) { | |
558ec83d BP |
267 | return; |
268 | } | |
269 | ||
d5a76da4 | 270 | if (ltable >= ctx->pp->n_tables) { |
9aef3c1b BP |
271 | lexer_error(ctx->lexer, |
272 | "\"next\" argument must be in range 0 to %d.", | |
d5a76da4 | 273 | ctx->pp->n_tables - 1); |
558ec83d BP |
274 | return; |
275 | } | |
276 | ||
d5a76da4 | 277 | ovnact_put_NEXT(ctx->ovnacts)->ltable = ltable; |
558ec83d | 278 | } else { |
d5a76da4 BP |
279 | if (ctx->pp->cur_ltable < ctx->pp->n_tables) { |
280 | ovnact_put_NEXT(ctx->ovnacts)->ltable = ctx->pp->cur_ltable + 1; | |
558ec83d | 281 | } else { |
9aef3c1b BP |
282 | lexer_error(ctx->lexer, |
283 | "\"next\" action not allowed in last table."); | |
558ec83d BP |
284 | } |
285 | } | |
286 | } | |
287 | ||
5b84185b | 288 | static void |
d5a76da4 | 289 | format_NEXT(const struct ovnact_next *next, struct ds *s) |
5b84185b | 290 | { |
d5a76da4 BP |
291 | ds_put_format(s, "next(%d);", next->ltable); |
292 | } | |
5b84185b | 293 | |
d5a76da4 BP |
294 | static void |
295 | encode_NEXT(const struct ovnact_next *next, | |
296 | const struct ovnact_encode_params *ep, | |
297 | struct ofpbuf *ofpacts) | |
298 | { | |
299 | emit_resubmit(ofpacts, ep->first_ptable + next->ltable); | |
5b84185b BP |
300 | } |
301 | ||
d5a76da4 | 302 | static void |
80b6743d | 303 | ovnact_next_free(struct ovnact_next *a OVS_UNUSED) |
0bac7164 | 304 | { |
d5a76da4 BP |
305 | } |
306 | \f | |
307 | static void | |
308 | parse_LOAD(struct action_context *ctx, const struct expr_field *lhs) | |
309 | { | |
310 | size_t ofs = ctx->ovnacts->size; | |
311 | struct ovnact_load *load = ovnact_put_LOAD(ctx->ovnacts); | |
312 | load->dst = *lhs; | |
9aef3c1b | 313 | |
d5a76da4 | 314 | char *error = expr_type_check(lhs, lhs->n_bits, true); |
d5a76da4 BP |
315 | if (error) { |
316 | ctx->ovnacts->size = ofs; | |
9aef3c1b | 317 | lexer_error(ctx->lexer, "%s", error); |
d5a76da4 | 318 | free(error); |
9aef3c1b BP |
319 | return; |
320 | } | |
321 | if (!expr_constant_parse(ctx->lexer, lhs, &load->imm)) { | |
322 | ctx->ovnacts->size = ofs; | |
323 | return; | |
d5a76da4 BP |
324 | } |
325 | } | |
0bac7164 | 326 | |
d5a76da4 BP |
327 | static enum expr_constant_type |
328 | load_type(const struct ovnact_load *load) | |
329 | { | |
330 | return load->dst.symbol->width > 0 ? EXPR_C_INTEGER : EXPR_C_STRING; | |
331 | } | |
0bac7164 | 332 | |
d5a76da4 BP |
333 | static void |
334 | format_LOAD(const struct ovnact_load *load, struct ds *s) | |
335 | { | |
336 | expr_field_format(&load->dst, s); | |
337 | ds_put_cstr(s, " = "); | |
338 | expr_constant_format(&load->imm, load_type(load), s); | |
339 | ds_put_char(s, ';'); | |
340 | } | |
0bac7164 | 341 | |
128684a6 JR |
342 | static void |
343 | encode_LOAD(const struct ovnact_load *load, | |
344 | const struct ovnact_encode_params *ep, | |
345 | struct ofpbuf *ofpacts) | |
d5a76da4 BP |
346 | { |
347 | const union expr_constant *c = &load->imm; | |
348 | struct mf_subfield dst = expr_resolve_field(&load->dst); | |
128684a6 JR |
349 | struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts, dst.field, |
350 | NULL, NULL); | |
d5a76da4 BP |
351 | |
352 | if (load->dst.symbol->width) { | |
353 | bitwise_copy(&c->value, sizeof c->value, 0, | |
128684a6 | 354 | sf->value, dst.field->n_bytes, dst.ofs, |
d5a76da4 BP |
355 | dst.n_bits); |
356 | if (c->masked) { | |
357 | bitwise_copy(&c->mask, sizeof c->mask, 0, | |
128684a6 JR |
358 | ofpact_set_field_mask(sf), dst.field->n_bytes, |
359 | dst.ofs, dst.n_bits); | |
d5a76da4 | 360 | } else { |
128684a6 | 361 | bitwise_one(ofpact_set_field_mask(sf), dst.field->n_bytes, |
d5a76da4 BP |
362 | dst.ofs, dst.n_bits); |
363 | } | |
364 | } else { | |
365 | uint32_t port; | |
128684a6 | 366 | if (!ep->lookup_port(ep->aux, load->imm.string, &port)) { |
d5a76da4 BP |
367 | port = 0; |
368 | } | |
128684a6 | 369 | bitwise_put(port, sf->value, |
d5a76da4 | 370 | sf->field->n_bytes, 0, sf->field->n_bits); |
128684a6 JR |
371 | bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, 0, |
372 | sf->field->n_bits); | |
d5a76da4 | 373 | } |
0bac7164 BP |
374 | } |
375 | ||
376 | static void | |
80b6743d | 377 | ovnact_load_free(struct ovnact_load *load) |
0bac7164 | 378 | { |
d5a76da4 BP |
379 | expr_constant_destroy(&load->imm, load_type(load)); |
380 | } | |
381 | \f | |
382 | static void | |
383 | format_assignment(const struct ovnact_move *move, const char *operator, | |
384 | struct ds *s) | |
385 | { | |
386 | expr_field_format(&move->lhs, s); | |
387 | ds_put_format(s, " %s ", operator); | |
388 | expr_field_format(&move->rhs, s); | |
389 | ds_put_char(s, ';'); | |
0bac7164 BP |
390 | } |
391 | ||
392 | static void | |
d5a76da4 | 393 | format_MOVE(const struct ovnact_move *move, struct ds *s) |
0bac7164 | 394 | { |
d5a76da4 | 395 | format_assignment(move, "=", s); |
0bac7164 BP |
396 | } |
397 | ||
6335d074 | 398 | static void |
d5a76da4 BP |
399 | format_EXCHANGE(const struct ovnact_move *move, struct ds *s) |
400 | { | |
401 | format_assignment(move, "<->", s); | |
402 | } | |
403 | ||
404 | static void | |
405 | parse_assignment_action(struct action_context *ctx, bool exchange, | |
406 | const struct expr_field *lhs) | |
6335d074 | 407 | { |
d5a76da4 | 408 | struct expr_field rhs; |
9aef3c1b | 409 | if (!expr_field_parse(ctx->lexer, ctx->pp->symtab, &rhs, &ctx->prereqs)) { |
6335d074 BP |
410 | return; |
411 | } | |
412 | ||
d5a76da4 BP |
413 | const struct expr_symbol *ls = lhs->symbol; |
414 | const struct expr_symbol *rs = rhs.symbol; | |
415 | if ((ls->width != 0) != (rs->width != 0)) { | |
416 | if (exchange) { | |
9aef3c1b BP |
417 | lexer_error(ctx->lexer, |
418 | "Can't exchange %s field (%s) with %s field (%s).", | |
419 | ls->width ? "integer" : "string", | |
420 | ls->name, | |
421 | rs->width ? "integer" : "string", | |
422 | rs->name); | |
d5a76da4 | 423 | } else { |
9aef3c1b BP |
424 | lexer_error(ctx->lexer, |
425 | "Can't assign %s field (%s) to %s field (%s).", | |
426 | rs->width ? "integer" : "string", | |
427 | rs->name, | |
428 | ls->width ? "integer" : "string", | |
429 | ls->name); | |
d5a76da4 BP |
430 | } |
431 | return; | |
432 | } | |
6335d074 | 433 | |
d5a76da4 BP |
434 | if (lhs->n_bits != rhs.n_bits) { |
435 | if (exchange) { | |
9aef3c1b BP |
436 | lexer_error(ctx->lexer, |
437 | "Can't exchange %d-bit field with %d-bit field.", | |
438 | lhs->n_bits, rhs.n_bits); | |
d5a76da4 | 439 | } else { |
9aef3c1b BP |
440 | lexer_error(ctx->lexer, |
441 | "Can't assign %d-bit value to %d-bit destination.", | |
442 | rhs.n_bits, lhs->n_bits); | |
6335d074 | 443 | } |
d5a76da4 BP |
444 | return; |
445 | } else if (!lhs->n_bits && | |
446 | ls->field->n_bits != rs->field->n_bits) { | |
9aef3c1b BP |
447 | lexer_error(ctx->lexer, "String fields %s and %s are incompatible for " |
448 | "%s.", ls->name, rs->name, | |
449 | exchange ? "exchange" : "assignment"); | |
d5a76da4 | 450 | return; |
6335d074 BP |
451 | } |
452 | ||
9aef3c1b | 453 | char *error = expr_type_check(lhs, lhs->n_bits, true); |
d5a76da4 BP |
454 | if (!error) { |
455 | error = expr_type_check(&rhs, rhs.n_bits, true); | |
456 | } | |
457 | if (error) { | |
9aef3c1b | 458 | lexer_error(ctx->lexer, "%s", error); |
d5a76da4 BP |
459 | free(error); |
460 | return; | |
461 | } | |
6335d074 | 462 | |
d5a76da4 BP |
463 | struct ovnact_move *move; |
464 | move = (exchange | |
465 | ? ovnact_put_EXCHANGE(ctx->ovnacts) | |
466 | : ovnact_put_MOVE(ctx->ovnacts)); | |
467 | move->lhs = *lhs; | |
468 | move->rhs = rhs; | |
469 | } | |
6335d074 | 470 | |
d5a76da4 BP |
471 | static void |
472 | encode_MOVE(const struct ovnact_move *move, | |
473 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
474 | struct ofpbuf *ofpacts) | |
475 | { | |
476 | struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts); | |
477 | orm->src = expr_resolve_field(&move->rhs); | |
478 | orm->dst = expr_resolve_field(&move->lhs); | |
6335d074 BP |
479 | } |
480 | ||
d5a76da4 BP |
481 | static void |
482 | encode_EXCHANGE(const struct ovnact_move *xchg, | |
483 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
484 | struct ofpbuf *ofpacts) | |
0bac7164 | 485 | { |
d5a76da4 BP |
486 | ofpact_put_STACK_PUSH(ofpacts)->subfield = expr_resolve_field(&xchg->rhs); |
487 | ofpact_put_STACK_PUSH(ofpacts)->subfield = expr_resolve_field(&xchg->lhs); | |
488 | ofpact_put_STACK_POP(ofpacts)->subfield = expr_resolve_field(&xchg->rhs); | |
489 | ofpact_put_STACK_POP(ofpacts)->subfield = expr_resolve_field(&xchg->lhs); | |
490 | } | |
0bac7164 | 491 | |
d5a76da4 | 492 | static void |
80b6743d | 493 | ovnact_move_free(struct ovnact_move *move OVS_UNUSED) |
d5a76da4 BP |
494 | { |
495 | } | |
496 | \f | |
497 | static void | |
498 | parse_DEC_TTL(struct action_context *ctx) | |
499 | { | |
9aef3c1b | 500 | lexer_force_match(ctx->lexer, LEX_T_DECREMENT); |
d5a76da4 BP |
501 | ovnact_put_DEC_TTL(ctx->ovnacts); |
502 | add_prerequisite(ctx, "ip"); | |
503 | } | |
0bac7164 | 504 | |
d5a76da4 BP |
505 | static void |
506 | format_DEC_TTL(const struct ovnact_null *null OVS_UNUSED, struct ds *s) | |
507 | { | |
508 | ds_put_cstr(s, "ip.ttl--;"); | |
0bac7164 BP |
509 | } |
510 | ||
d5a76da4 BP |
511 | static void |
512 | encode_DEC_TTL(const struct ovnact_null *null OVS_UNUSED, | |
513 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
514 | struct ofpbuf *ofpacts) | |
0bac7164 | 515 | { |
d5a76da4 BP |
516 | ofpact_put_DEC_TTL(ofpacts); |
517 | } | |
d5a76da4 BP |
518 | \f |
519 | static void | |
520 | parse_CT_NEXT(struct action_context *ctx) | |
521 | { | |
522 | if (ctx->pp->cur_ltable >= ctx->pp->n_tables) { | |
9aef3c1b BP |
523 | lexer_error(ctx->lexer, |
524 | "\"ct_next\" action not allowed in last table."); | |
d5a76da4 | 525 | return; |
0bac7164 BP |
526 | } |
527 | ||
d5a76da4 BP |
528 | add_prerequisite(ctx, "ip"); |
529 | ovnact_put_CT_NEXT(ctx->ovnacts)->ltable = ctx->pp->cur_ltable + 1; | |
0bac7164 BP |
530 | } |
531 | ||
532 | static void | |
d5a76da4 | 533 | format_CT_NEXT(const struct ovnact_next *next OVS_UNUSED, struct ds *s) |
0bac7164 | 534 | { |
d5a76da4 | 535 | ds_put_cstr(s, "ct_next;"); |
0bac7164 BP |
536 | } |
537 | ||
d5a76da4 BP |
538 | static void |
539 | encode_CT_NEXT(const struct ovnact_next *next, | |
540 | const struct ovnact_encode_params *ep, | |
541 | struct ofpbuf *ofpacts) | |
542 | { | |
543 | struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts); | |
544 | ct->recirc_table = ep->first_ptable + next->ltable; | |
d7039b9a GS |
545 | ct->zone_src.field = ep->is_switch ? mf_from_id(MFF_LOG_CT_ZONE) |
546 | : mf_from_id(MFF_LOG_DNAT_ZONE); | |
d5a76da4 BP |
547 | ct->zone_src.ofs = 0; |
548 | ct->zone_src.n_bits = 16; | |
549 | ofpact_finish(ofpacts, &ct->ofpact); | |
550 | } | |
d5a76da4 BP |
551 | \f |
552 | static void | |
553 | parse_ct_commit_arg(struct action_context *ctx, | |
554 | struct ovnact_ct_commit *cc) | |
555 | { | |
556 | if (lexer_match_id(ctx->lexer, "ct_mark")) { | |
9aef3c1b | 557 | if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { |
d5a76da4 | 558 | return; |
0bac7164 | 559 | } |
d5a76da4 BP |
560 | if (ctx->lexer->token.type == LEX_T_INTEGER) { |
561 | cc->ct_mark = ntohll(ctx->lexer->token.value.integer); | |
562 | cc->ct_mark_mask = UINT32_MAX; | |
563 | } else if (ctx->lexer->token.type == LEX_T_MASKED_INTEGER) { | |
564 | cc->ct_mark = ntohll(ctx->lexer->token.value.integer); | |
565 | cc->ct_mark_mask = ntohll(ctx->lexer->token.mask.integer); | |
566 | } else { | |
9aef3c1b | 567 | lexer_syntax_error(ctx->lexer, "expecting integer"); |
d5a76da4 | 568 | return; |
0bac7164 | 569 | } |
d5a76da4 BP |
570 | lexer_get(ctx->lexer); |
571 | } else if (lexer_match_id(ctx->lexer, "ct_label")) { | |
9aef3c1b | 572 | if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { |
d5a76da4 | 573 | return; |
0bac7164 | 574 | } |
d5a76da4 BP |
575 | if (ctx->lexer->token.type == LEX_T_INTEGER) { |
576 | cc->ct_label = ctx->lexer->token.value.be128_int; | |
577 | cc->ct_label_mask = OVS_BE128_MAX; | |
578 | } else if (ctx->lexer->token.type == LEX_T_MASKED_INTEGER) { | |
579 | cc->ct_label = ctx->lexer->token.value.be128_int; | |
580 | cc->ct_label_mask = ctx->lexer->token.mask.be128_int; | |
581 | } else { | |
9aef3c1b | 582 | lexer_syntax_error(ctx->lexer, "expecting integer"); |
d5a76da4 BP |
583 | return; |
584 | } | |
585 | lexer_get(ctx->lexer); | |
586 | } else { | |
9aef3c1b | 587 | lexer_syntax_error(ctx->lexer, NULL); |
0bac7164 BP |
588 | } |
589 | } | |
590 | ||
591 | static void | |
d5a76da4 | 592 | parse_CT_COMMIT(struct action_context *ctx) |
0bac7164 | 593 | { |
d5a76da4 BP |
594 | add_prerequisite(ctx, "ip"); |
595 | ||
596 | struct ovnact_ct_commit *ct_commit = ovnact_put_CT_COMMIT(ctx->ovnacts); | |
597 | if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { | |
598 | while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { | |
599 | parse_ct_commit_arg(ctx, ct_commit); | |
9aef3c1b | 600 | if (ctx->lexer->error) { |
d5a76da4 BP |
601 | return; |
602 | } | |
603 | lexer_match(ctx->lexer, LEX_T_COMMA); | |
0bac7164 BP |
604 | } |
605 | } | |
606 | } | |
607 | ||
608 | static void | |
d5a76da4 | 609 | format_CT_COMMIT(const struct ovnact_ct_commit *cc, struct ds *s) |
0bac7164 | 610 | { |
d5a76da4 BP |
611 | ds_put_cstr(s, "ct_commit("); |
612 | if (cc->ct_mark_mask) { | |
613 | ds_put_format(s, "ct_mark=%#"PRIx32, cc->ct_mark); | |
614 | if (cc->ct_mark_mask != UINT32_MAX) { | |
615 | ds_put_format(s, "/%#"PRIx32, cc->ct_mark_mask); | |
616 | } | |
617 | } | |
618 | if (!ovs_be128_is_zero(cc->ct_label_mask)) { | |
619 | if (ds_last(s) != '(') { | |
620 | ds_put_cstr(s, ", "); | |
621 | } | |
0bac7164 | 622 | |
d5a76da4 BP |
623 | ds_put_format(s, "ct_label="); |
624 | ds_put_hex(s, &cc->ct_label, sizeof cc->ct_label); | |
625 | if (!ovs_be128_equals(cc->ct_label_mask, OVS_BE128_MAX)) { | |
626 | ds_put_char(s, '/'); | |
627 | ds_put_hex(s, &cc->ct_label_mask, sizeof cc->ct_label_mask); | |
628 | } | |
629 | } | |
630 | if (!ds_chomp(s, '(')) { | |
631 | ds_put_char(s, ')'); | |
632 | } | |
633 | ds_put_char(s, ';'); | |
0bac7164 BP |
634 | } |
635 | ||
636 | static void | |
d5a76da4 BP |
637 | encode_CT_COMMIT(const struct ovnact_ct_commit *cc, |
638 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
639 | struct ofpbuf *ofpacts) | |
0bac7164 | 640 | { |
d5a76da4 BP |
641 | struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts); |
642 | ct->flags = NX_CT_F_COMMIT; | |
643 | ct->recirc_table = NX_CT_RECIRC_NONE; | |
644 | ct->zone_src.field = mf_from_id(MFF_LOG_CT_ZONE); | |
645 | ct->zone_src.ofs = 0; | |
646 | ct->zone_src.n_bits = 16; | |
0bac7164 | 647 | |
d5a76da4 BP |
648 | size_t set_field_offset = ofpacts->size; |
649 | ofpbuf_pull(ofpacts, set_field_offset); | |
0bac7164 | 650 | |
d5a76da4 | 651 | if (cc->ct_mark_mask) { |
128684a6 JR |
652 | const ovs_be32 value = htonl(cc->ct_mark); |
653 | const ovs_be32 mask = htonl(cc->ct_mark_mask); | |
654 | ofpact_put_set_field(ofpacts, mf_from_id(MFF_CT_MARK), &value, &mask); | |
d5a76da4 | 655 | } |
0bac7164 | 656 | |
d5a76da4 | 657 | if (!ovs_be128_is_zero(cc->ct_label_mask)) { |
128684a6 JR |
658 | ofpact_put_set_field(ofpacts, mf_from_id(MFF_CT_LABEL), &cc->ct_label, |
659 | &cc->ct_label_mask); | |
d5a76da4 | 660 | } |
0bac7164 | 661 | |
d5a76da4 BP |
662 | ofpacts->header = ofpbuf_push_uninit(ofpacts, set_field_offset); |
663 | ct = ofpacts->header; | |
664 | ofpact_finish(ofpacts, &ct->ofpact); | |
0bac7164 BP |
665 | } |
666 | ||
667 | static void | |
80b6743d | 668 | ovnact_ct_commit_free(struct ovnact_ct_commit *cc OVS_UNUSED) |
0bac7164 | 669 | { |
0bac7164 | 670 | } |
d5a76da4 | 671 | \f |
42814145 | 672 | static void |
d5a76da4 BP |
673 | parse_ct_nat(struct action_context *ctx, const char *name, |
674 | struct ovnact_ct_nat *cn) | |
42814145 | 675 | { |
d5a76da4 | 676 | add_prerequisite(ctx, "ip"); |
42814145 | 677 | |
d5a76da4 | 678 | if (ctx->pp->cur_ltable >= ctx->pp->n_tables) { |
9aef3c1b BP |
679 | lexer_error(ctx->lexer, |
680 | "\"%s\" action not allowed in last table.", name); | |
42814145 NS |
681 | return; |
682 | } | |
d5a76da4 | 683 | cn->ltable = ctx->pp->cur_ltable + 1; |
42814145 | 684 | |
d5a76da4 BP |
685 | if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { |
686 | if (ctx->lexer->token.type != LEX_T_INTEGER | |
687 | || ctx->lexer->token.format != LEX_F_IPV4) { | |
9aef3c1b | 688 | lexer_syntax_error(ctx->lexer, "expecting IPv4 address"); |
42814145 NS |
689 | return; |
690 | } | |
d5a76da4 BP |
691 | cn->ip = ctx->lexer->token.value.ipv4; |
692 | lexer_get(ctx->lexer); | |
693 | ||
9aef3c1b | 694 | if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) { |
42814145 NS |
695 | return; |
696 | } | |
697 | } | |
d5a76da4 | 698 | } |
42814145 | 699 | |
d5a76da4 BP |
700 | static void |
701 | parse_CT_DNAT(struct action_context *ctx) | |
702 | { | |
703 | parse_ct_nat(ctx, "ct_dnat", ovnact_put_CT_DNAT(ctx->ovnacts)); | |
704 | } | |
42814145 | 705 | |
d5a76da4 BP |
706 | static void |
707 | parse_CT_SNAT(struct action_context *ctx) | |
708 | { | |
709 | parse_ct_nat(ctx, "ct_snat", ovnact_put_CT_SNAT(ctx->ovnacts)); | |
710 | } | |
42814145 | 711 | |
d5a76da4 BP |
712 | static void |
713 | format_ct_nat(const struct ovnact_ct_nat *cn, const char *name, struct ds *s) | |
714 | { | |
715 | ds_put_cstr(s, name); | |
716 | if (cn->ip) { | |
717 | ds_put_format(s, "("IP_FMT")", IP_ARGS(cn->ip)); | |
42814145 | 718 | } |
d5a76da4 BP |
719 | ds_put_char(s, ';'); |
720 | } | |
42814145 | 721 | |
d5a76da4 BP |
722 | static void |
723 | format_CT_DNAT(const struct ovnact_ct_nat *cn, struct ds *s) | |
724 | { | |
725 | format_ct_nat(cn, "ct_dnat", s); | |
726 | } | |
42814145 | 727 | |
d5a76da4 BP |
728 | static void |
729 | format_CT_SNAT(const struct ovnact_ct_nat *cn, struct ds *s) | |
730 | { | |
731 | format_ct_nat(cn, "ct_snat", s); | |
732 | } | |
42814145 | 733 | |
d5a76da4 BP |
734 | static void |
735 | encode_ct_nat(const struct ovnact_ct_nat *cn, | |
736 | const struct ovnact_encode_params *ep, | |
737 | bool snat, struct ofpbuf *ofpacts) | |
738 | { | |
739 | const size_t ct_offset = ofpacts->size; | |
740 | ofpbuf_pull(ofpacts, ct_offset); | |
42814145 | 741 | |
d5a76da4 BP |
742 | struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts); |
743 | ct->recirc_table = cn->ltable + ep->first_ptable; | |
744 | if (snat) { | |
745 | ct->zone_src.field = mf_from_id(MFF_LOG_SNAT_ZONE); | |
746 | } else { | |
747 | ct->zone_src.field = mf_from_id(MFF_LOG_DNAT_ZONE); | |
748 | } | |
749 | ct->zone_src.ofs = 0; | |
750 | ct->zone_src.n_bits = 16; | |
751 | ct->flags = 0; | |
752 | ct->alg = 0; | |
42814145 | 753 | |
d5a76da4 BP |
754 | struct ofpact_nat *nat; |
755 | size_t nat_offset; | |
756 | nat_offset = ofpacts->size; | |
757 | ofpbuf_pull(ofpacts, nat_offset); | |
758 | ||
759 | nat = ofpact_put_NAT(ofpacts); | |
760 | nat->flags = 0; | |
761 | nat->range_af = AF_UNSPEC; | |
762 | ||
763 | if (cn->ip) { | |
764 | nat->range_af = AF_INET; | |
765 | nat->range.addr.ipv4.min = cn->ip; | |
766 | if (snat) { | |
767 | nat->flags |= NX_NAT_F_SRC; | |
768 | } else { | |
769 | nat->flags |= NX_NAT_F_DST; | |
42814145 | 770 | } |
42814145 NS |
771 | } |
772 | ||
d5a76da4 BP |
773 | ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset); |
774 | ct = ofpacts->header; | |
775 | if (cn->ip) { | |
776 | ct->flags |= NX_CT_F_COMMIT; | |
777 | } else if (snat) { | |
778 | /* XXX: For performance reasons, we try to prevent additional | |
779 | * recirculations. So far, ct_snat which is used in a gateway router | |
780 | * does not need a recirculation. ct_snat(IP) does need a | |
781 | * recirculation. Should we consider a method to let the actions | |
782 | * specify whether an action needs recirculation if there more use | |
783 | * cases?. */ | |
784 | ct->recirc_table = NX_CT_RECIRC_NONE; | |
785 | } | |
786 | ofpact_finish(ofpacts, &ct->ofpact); | |
787 | ofpbuf_push_uninit(ofpacts, ct_offset); | |
42814145 NS |
788 | } |
789 | ||
42814145 | 790 | static void |
d5a76da4 BP |
791 | encode_CT_DNAT(const struct ovnact_ct_nat *cn, |
792 | const struct ovnact_encode_params *ep, | |
793 | struct ofpbuf *ofpacts) | |
42814145 | 794 | { |
d5a76da4 | 795 | encode_ct_nat(cn, ep, false, ofpacts); |
42814145 NS |
796 | } |
797 | ||
01cfdb2f | 798 | static void |
d5a76da4 BP |
799 | encode_CT_SNAT(const struct ovnact_ct_nat *cn, |
800 | const struct ovnact_encode_params *ep, | |
801 | struct ofpbuf *ofpacts) | |
01cfdb2f | 802 | { |
d5a76da4 BP |
803 | encode_ct_nat(cn, ep, true, ofpacts); |
804 | } | |
01cfdb2f | 805 | |
d5a76da4 | 806 | static void |
80b6743d | 807 | ovnact_ct_nat_free(struct ovnact_ct_nat *ct_nat OVS_UNUSED) |
d5a76da4 BP |
808 | { |
809 | } | |
810 | \f | |
811 | static void | |
812 | parse_ct_lb_action(struct action_context *ctx) | |
813 | { | |
814 | if (ctx->pp->cur_ltable >= ctx->pp->n_tables) { | |
9aef3c1b | 815 | lexer_error(ctx->lexer, "\"ct_lb\" action not allowed in last table."); |
01cfdb2f NS |
816 | return; |
817 | } | |
818 | ||
d5a76da4 | 819 | add_prerequisite(ctx, "ip"); |
01cfdb2f | 820 | |
d5a76da4 BP |
821 | struct ovnact_ct_lb_dst *dsts = NULL; |
822 | size_t allocated_dsts = 0; | |
823 | size_t n_dsts = 0; | |
01cfdb2f | 824 | |
d5a76da4 BP |
825 | if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { |
826 | while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { | |
827 | if (ctx->lexer->token.type != LEX_T_INTEGER | |
828 | || mf_subvalue_width(&ctx->lexer->token.value) > 32) { | |
9aef3c1b | 829 | lexer_syntax_error(ctx->lexer, "expecting IPv4 address"); |
d5a76da4 BP |
830 | return; |
831 | } | |
01cfdb2f | 832 | |
d5a76da4 BP |
833 | /* Parse IP. */ |
834 | ovs_be32 ip = ctx->lexer->token.value.ipv4; | |
835 | lexer_get(ctx->lexer); | |
01cfdb2f | 836 | |
d5a76da4 BP |
837 | /* Parse optional port. */ |
838 | uint16_t port = 0; | |
839 | if (lexer_match(ctx->lexer, LEX_T_COLON) | |
840 | && !action_parse_port(ctx, &port)) { | |
841 | free(dsts); | |
842 | return; | |
843 | } | |
844 | lexer_match(ctx->lexer, LEX_T_COMMA); | |
01cfdb2f | 845 | |
d5a76da4 BP |
846 | /* Append to dsts. */ |
847 | if (n_dsts >= allocated_dsts) { | |
848 | dsts = x2nrealloc(dsts, &allocated_dsts, sizeof *dsts); | |
849 | } | |
850 | dsts[n_dsts++] = (struct ovnact_ct_lb_dst) { ip, port }; | |
01cfdb2f | 851 | } |
01cfdb2f NS |
852 | } |
853 | ||
d5a76da4 BP |
854 | struct ovnact_ct_lb *cl = ovnact_put_CT_LB(ctx->ovnacts); |
855 | cl->ltable = ctx->pp->cur_ltable + 1; | |
856 | cl->dsts = dsts; | |
857 | cl->n_dsts = n_dsts; | |
01cfdb2f NS |
858 | } |
859 | ||
01cfdb2f | 860 | static void |
d5a76da4 | 861 | format_CT_LB(const struct ovnact_ct_lb *cl, struct ds *s) |
01cfdb2f | 862 | { |
d5a76da4 BP |
863 | ds_put_cstr(s, "ct_lb"); |
864 | if (cl->n_dsts) { | |
865 | ds_put_char(s, '('); | |
866 | for (size_t i = 0; i < cl->n_dsts; i++) { | |
867 | if (i) { | |
868 | ds_put_cstr(s, ", "); | |
869 | } | |
01cfdb2f | 870 | |
d5a76da4 BP |
871 | const struct ovnact_ct_lb_dst *dst = &cl->dsts[i]; |
872 | ds_put_format(s, IP_FMT, IP_ARGS(dst->ip)); | |
873 | if (dst->port) { | |
874 | ds_put_format(s, ":%"PRIu16, dst->port); | |
875 | } | |
467085fd | 876 | } |
d5a76da4 | 877 | ds_put_char(s, ')'); |
467085fd | 878 | } |
d5a76da4 | 879 | ds_put_char(s, ';'); |
467085fd GS |
880 | } |
881 | ||
882 | static void | |
d5a76da4 BP |
883 | encode_CT_LB(const struct ovnact_ct_lb *cl, |
884 | const struct ovnact_encode_params *ep, | |
885 | struct ofpbuf *ofpacts) | |
467085fd | 886 | { |
d5a76da4 BP |
887 | uint8_t recirc_table = cl->ltable + ep->first_ptable; |
888 | if (!cl->n_dsts) { | |
889 | /* ct_lb without any destinations means that this is an established | |
467085fd | 890 | * connection and we just need to do a NAT. */ |
d5a76da4 BP |
891 | const size_t ct_offset = ofpacts->size; |
892 | ofpbuf_pull(ofpacts, ct_offset); | |
467085fd | 893 | |
d5a76da4 | 894 | struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts); |
467085fd GS |
895 | struct ofpact_nat *nat; |
896 | size_t nat_offset; | |
c2e954a1 GS |
897 | ct->zone_src.field = ep->is_switch ? mf_from_id(MFF_LOG_CT_ZONE) |
898 | : mf_from_id(MFF_LOG_DNAT_ZONE); | |
467085fd GS |
899 | ct->zone_src.ofs = 0; |
900 | ct->zone_src.n_bits = 16; | |
901 | ct->flags = 0; | |
902 | ct->recirc_table = recirc_table; | |
903 | ct->alg = 0; | |
904 | ||
d5a76da4 BP |
905 | nat_offset = ofpacts->size; |
906 | ofpbuf_pull(ofpacts, nat_offset); | |
467085fd | 907 | |
d5a76da4 | 908 | nat = ofpact_put_NAT(ofpacts); |
467085fd GS |
909 | nat->flags = 0; |
910 | nat->range_af = AF_UNSPEC; | |
911 | ||
d5a76da4 BP |
912 | ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset); |
913 | ct = ofpacts->header; | |
914 | ofpact_finish(ofpacts, &ct->ofpact); | |
915 | ofpbuf_push_uninit(ofpacts, ct_offset); | |
467085fd GS |
916 | return; |
917 | } | |
918 | ||
d5a76da4 | 919 | uint32_t group_id = 0, hash; |
467085fd GS |
920 | struct group_info *group_info; |
921 | struct ofpact_group *og; | |
c2e954a1 GS |
922 | uint32_t zone_reg = ep->is_switch ? MFF_LOG_CT_ZONE - MFF_REG0 |
923 | : MFF_LOG_DNAT_ZONE - MFF_REG0; | |
467085fd GS |
924 | |
925 | struct ds ds = DS_EMPTY_INITIALIZER; | |
926 | ds_put_format(&ds, "type=select"); | |
927 | ||
928 | BUILD_ASSERT(MFF_LOG_CT_ZONE >= MFF_REG0); | |
929 | BUILD_ASSERT(MFF_LOG_CT_ZONE < MFF_REG0 + FLOW_N_REGS); | |
c2e954a1 GS |
930 | BUILD_ASSERT(MFF_LOG_DNAT_ZONE >= MFF_REG0); |
931 | BUILD_ASSERT(MFF_LOG_DNAT_ZONE < MFF_REG0 + FLOW_N_REGS); | |
d5a76da4 BP |
932 | for (size_t bucket_id = 0; bucket_id < cl->n_dsts; bucket_id++) { |
933 | const struct ovnact_ct_lb_dst *dst = &cl->dsts[bucket_id]; | |
934 | ds_put_format(&ds, ",bucket=bucket_id=%"PRIuSIZE",weight:100,actions=" | |
935 | "ct(nat(dst="IP_FMT, bucket_id, IP_ARGS(dst->ip)); | |
936 | if (dst->port) { | |
937 | ds_put_format(&ds, ":%"PRIu16, dst->port); | |
467085fd GS |
938 | } |
939 | ds_put_format(&ds, "),commit,table=%d,zone=NXM_NX_REG%d[0..15])", | |
c2e954a1 | 940 | recirc_table, zone_reg); |
d5a76da4 | 941 | } |
467085fd GS |
942 | |
943 | hash = hash_string(ds_cstr(&ds), 0); | |
944 | ||
945 | /* Check whether we have non installed but allocated group_id. */ | |
946 | HMAP_FOR_EACH_WITH_HASH (group_info, hmap_node, hash, | |
d5a76da4 | 947 | &ep->group_table->desired_groups) { |
467085fd GS |
948 | if (!strcmp(ds_cstr(&group_info->group), ds_cstr(&ds))) { |
949 | group_id = group_info->group_id; | |
950 | break; | |
951 | } | |
952 | } | |
953 | ||
954 | if (!group_id) { | |
955 | /* Check whether we already have an installed entry for this | |
956 | * combination. */ | |
957 | HMAP_FOR_EACH_WITH_HASH (group_info, hmap_node, hash, | |
d5a76da4 | 958 | &ep->group_table->existing_groups) { |
467085fd GS |
959 | if (!strcmp(ds_cstr(&group_info->group), ds_cstr(&ds))) { |
960 | group_id = group_info->group_id; | |
961 | } | |
962 | } | |
963 | ||
e4f7cf9c | 964 | bool new_group_id = false; |
467085fd GS |
965 | if (!group_id) { |
966 | /* Reserve a new group_id. */ | |
d5a76da4 | 967 | group_id = bitmap_scan(ep->group_table->group_ids, 0, 1, |
467085fd | 968 | MAX_OVN_GROUPS + 1); |
e4f7cf9c | 969 | new_group_id = true; |
467085fd GS |
970 | } |
971 | ||
972 | if (group_id == MAX_OVN_GROUPS + 1) { | |
d5a76da4 BP |
973 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); |
974 | VLOG_ERR_RL(&rl, "out of group ids"); | |
975 | ||
467085fd | 976 | ds_destroy(&ds); |
467085fd GS |
977 | return; |
978 | } | |
d5a76da4 | 979 | bitmap_set1(ep->group_table->group_ids, group_id); |
467085fd GS |
980 | |
981 | group_info = xmalloc(sizeof *group_info); | |
982 | group_info->group = ds; | |
983 | group_info->group_id = group_id; | |
984 | group_info->hmap_node.hash = hash; | |
e4f7cf9c | 985 | group_info->new_group_id = new_group_id; |
467085fd | 986 | |
d5a76da4 | 987 | hmap_insert(&ep->group_table->desired_groups, |
467085fd GS |
988 | &group_info->hmap_node, group_info->hmap_node.hash); |
989 | } else { | |
990 | ds_destroy(&ds); | |
991 | } | |
992 | ||
d5a76da4 BP |
993 | /* Create an action to set the group. */ |
994 | og = ofpact_put_GROUP(ofpacts); | |
995 | og->group_id = group_id; | |
996 | } | |
997 | ||
998 | static void | |
80b6743d | 999 | ovnact_ct_lb_free(struct ovnact_ct_lb *ct_lb) |
d5a76da4 BP |
1000 | { |
1001 | free(ct_lb->dsts); | |
1002 | } | |
1003 | \f | |
b3bd2c33 BP |
1004 | /* Implements the "arp", "nd_na", and "clone" actions, which execute nested |
1005 | * actions on a packet derived from the one being processed. */ | |
d5a76da4 BP |
1006 | static void |
1007 | parse_nested_action(struct action_context *ctx, enum ovnact_type type, | |
1008 | const char *prereq) | |
1009 | { | |
9aef3c1b | 1010 | if (!lexer_force_match(ctx->lexer, LEX_T_LCURLY)) { |
d5a76da4 BP |
1011 | return; |
1012 | } | |
1013 | ||
1014 | uint64_t stub[1024 / 8]; | |
1015 | struct ofpbuf nested = OFPBUF_STUB_INITIALIZER(stub); | |
1016 | ||
1017 | struct action_context inner_ctx = { | |
1018 | .pp = ctx->pp, | |
1019 | .lexer = ctx->lexer, | |
d5a76da4 | 1020 | .ovnacts = &nested, |
b3bd2c33 | 1021 | .prereqs = NULL, |
d5a76da4 | 1022 | }; |
bac29564 | 1023 | parse_actions(&inner_ctx, LEX_T_RCURLY); |
d5a76da4 | 1024 | |
b3bd2c33 BP |
1025 | if (prereq) { |
1026 | /* XXX Not really sure what we should do with prerequisites for "arp" | |
1027 | * and "nd_na" actions. */ | |
1028 | expr_destroy(inner_ctx.prereqs); | |
1029 | add_prerequisite(ctx, prereq); | |
1030 | } else { | |
1031 | /* For "clone", the inner prerequisites should just add to the outer | |
1032 | * ones. */ | |
1033 | ctx->prereqs = expr_combine(EXPR_T_AND, | |
1034 | inner_ctx.prereqs, ctx->prereqs); | |
1035 | } | |
d5a76da4 | 1036 | |
9aef3c1b | 1037 | if (inner_ctx.lexer->error) { |
d5a76da4 BP |
1038 | ovnacts_free(nested.data, nested.size); |
1039 | ofpbuf_uninit(&nested); | |
1040 | return; | |
1041 | } | |
1042 | ||
8a41ad8e BP |
1043 | struct ovnact_nest *on = ovnact_put(ctx->ovnacts, type, |
1044 | OVNACT_ALIGN(sizeof *on)); | |
d5a76da4 BP |
1045 | on->nested_len = nested.size; |
1046 | on->nested = ofpbuf_steal_data(&nested); | |
1047 | } | |
1048 | ||
1049 | static void | |
1050 | parse_ARP(struct action_context *ctx) | |
1051 | { | |
1052 | parse_nested_action(ctx, OVNACT_ARP, "ip4"); | |
1053 | } | |
1054 | ||
1055 | static void | |
1056 | parse_ND_NA(struct action_context *ctx) | |
1057 | { | |
1058 | parse_nested_action(ctx, OVNACT_ND_NA, "nd_ns"); | |
1059 | } | |
1060 | ||
b3bd2c33 BP |
1061 | static void |
1062 | parse_CLONE(struct action_context *ctx) | |
1063 | { | |
1064 | parse_nested_action(ctx, OVNACT_CLONE, NULL); | |
1065 | } | |
1066 | ||
d5a76da4 BP |
1067 | static void |
1068 | format_nested_action(const struct ovnact_nest *on, const char *name, | |
1069 | struct ds *s) | |
1070 | { | |
1071 | ds_put_format(s, "%s { ", name); | |
1072 | ovnacts_format(on->nested, on->nested_len, s); | |
1073 | ds_put_format(s, " };"); | |
1074 | } | |
1075 | ||
1076 | static void | |
1077 | format_ARP(const struct ovnact_nest *nest, struct ds *s) | |
1078 | { | |
1079 | format_nested_action(nest, "arp", s); | |
1080 | } | |
1081 | ||
1082 | static void | |
1083 | format_ND_NA(const struct ovnact_nest *nest, struct ds *s) | |
1084 | { | |
1085 | format_nested_action(nest, "nd_na", s); | |
1086 | } | |
1087 | ||
1088 | static void | |
b3bd2c33 BP |
1089 | format_CLONE(const struct ovnact_nest *nest, struct ds *s) |
1090 | { | |
1091 | format_nested_action(nest, "clone", s); | |
1092 | } | |
1093 | ||
1094 | static void | |
1095 | encode_nested_neighbor_actions(const struct ovnact_nest *on, | |
1096 | const struct ovnact_encode_params *ep, | |
1097 | enum action_opcode opcode, | |
1098 | struct ofpbuf *ofpacts) | |
d5a76da4 BP |
1099 | { |
1100 | /* Convert nested actions into ofpacts. */ | |
1101 | uint64_t inner_ofpacts_stub[1024 / 8]; | |
1102 | struct ofpbuf inner_ofpacts = OFPBUF_STUB_INITIALIZER(inner_ofpacts_stub); | |
1103 | ovnacts_encode(on->nested, on->nested_len, ep, &inner_ofpacts); | |
1104 | ||
1105 | /* Add a "controller" action with the actions nested inside "{...}", | |
1106 | * converted to OpenFlow, as its userdata. ovn-controller will convert the | |
1107 | * packet to ARP or NA and then send the packet and actions back to the | |
1108 | * switch inside an OFPT_PACKET_OUT message. */ | |
1109 | size_t oc_offset = encode_start_controller_op(opcode, false, ofpacts); | |
1110 | ofpacts_put_openflow_actions(inner_ofpacts.data, inner_ofpacts.size, | |
1111 | ofpacts, OFP13_VERSION); | |
1112 | encode_finish_controller_op(oc_offset, ofpacts); | |
1113 | ||
1114 | /* Free memory. */ | |
1115 | ofpbuf_uninit(&inner_ofpacts); | |
1116 | } | |
1117 | ||
1118 | static void | |
1119 | encode_ARP(const struct ovnact_nest *on, | |
1120 | const struct ovnact_encode_params *ep, | |
1121 | struct ofpbuf *ofpacts) | |
1122 | { | |
b3bd2c33 | 1123 | encode_nested_neighbor_actions(on, ep, ACTION_OPCODE_ARP, ofpacts); |
d5a76da4 BP |
1124 | } |
1125 | ||
1126 | static void | |
1127 | encode_ND_NA(const struct ovnact_nest *on, | |
1128 | const struct ovnact_encode_params *ep, | |
1129 | struct ofpbuf *ofpacts) | |
1130 | { | |
b3bd2c33 | 1131 | encode_nested_neighbor_actions(on, ep, ACTION_OPCODE_ND_NA, ofpacts); |
d5a76da4 BP |
1132 | } |
1133 | ||
b3bd2c33 BP |
1134 | static void |
1135 | encode_CLONE(const struct ovnact_nest *on, | |
1136 | const struct ovnact_encode_params *ep, | |
1137 | struct ofpbuf *ofpacts) | |
1138 | { | |
1139 | size_t ofs = ofpacts->size; | |
1140 | ofpact_put_CLONE(ofpacts); | |
1141 | ovnacts_encode(on->nested, on->nested_len, ep, ofpacts); | |
1142 | ||
1143 | struct ofpact_nest *clone = ofpbuf_at_assert(ofpacts, ofs, sizeof *clone); | |
1144 | ofpacts->header = clone; | |
1145 | ofpact_finish_CLONE(ofpacts, &clone); | |
1146 | } | |
80b6743d | 1147 | |
d5a76da4 | 1148 | static void |
80b6743d | 1149 | ovnact_nest_free(struct ovnact_nest *on) |
d5a76da4 BP |
1150 | { |
1151 | ovnacts_free(on->nested, on->nested_len); | |
1152 | free(on->nested); | |
1153 | } | |
d5a76da4 BP |
1154 | \f |
1155 | static void | |
1156 | parse_get_mac_bind(struct action_context *ctx, int width, | |
1157 | struct ovnact_get_mac_bind *get_mac) | |
1158 | { | |
9aef3c1b | 1159 | lexer_force_match(ctx->lexer, LEX_T_LPAREN); |
d5a76da4 | 1160 | action_parse_field(ctx, 0, false, &get_mac->port); |
9aef3c1b | 1161 | lexer_force_match(ctx->lexer, LEX_T_COMMA); |
d5a76da4 | 1162 | action_parse_field(ctx, width, false, &get_mac->ip); |
9aef3c1b | 1163 | lexer_force_match(ctx->lexer, LEX_T_RPAREN); |
d5a76da4 BP |
1164 | } |
1165 | ||
1166 | static void | |
1167 | format_get_mac_bind(const struct ovnact_get_mac_bind *get_mac, | |
1168 | const char *name, struct ds *s) | |
1169 | { | |
1170 | ds_put_format(s, "%s(", name); | |
1171 | expr_field_format(&get_mac->port, s); | |
1172 | ds_put_cstr(s, ", "); | |
1173 | expr_field_format(&get_mac->ip, s); | |
1174 | ds_put_cstr(s, ");"); | |
1175 | } | |
1176 | ||
1177 | static void | |
1178 | format_GET_ARP(const struct ovnact_get_mac_bind *get_mac, struct ds *s) | |
1179 | { | |
1180 | format_get_mac_bind(get_mac, "get_arp", s); | |
1181 | } | |
1182 | ||
1183 | static void | |
1184 | format_GET_ND(const struct ovnact_get_mac_bind *get_mac, struct ds *s) | |
1185 | { | |
1186 | format_get_mac_bind(get_mac, "get_nd", s); | |
1187 | } | |
1188 | ||
1189 | static void | |
1190 | encode_get_mac(const struct ovnact_get_mac_bind *get_mac, | |
1191 | enum mf_field_id ip_field, | |
1192 | const struct ovnact_encode_params *ep, | |
1193 | struct ofpbuf *ofpacts) | |
1194 | { | |
1195 | const struct arg args[] = { | |
1196 | { expr_resolve_field(&get_mac->port), MFF_LOG_OUTPORT }, | |
1197 | { expr_resolve_field(&get_mac->ip), ip_field }, | |
1198 | }; | |
1199 | encode_setup_args(args, ARRAY_SIZE(args), ofpacts); | |
1200 | ||
1201 | put_load(0, MFF_ETH_DST, 0, 48, ofpacts); | |
1202 | emit_resubmit(ofpacts, ep->mac_bind_ptable); | |
1203 | ||
1204 | encode_restore_args(args, ARRAY_SIZE(args), ofpacts); | |
1205 | } | |
1206 | ||
1207 | static void | |
1208 | encode_GET_ARP(const struct ovnact_get_mac_bind *get_mac, | |
1209 | const struct ovnact_encode_params *ep, | |
1210 | struct ofpbuf *ofpacts) | |
1211 | { | |
1212 | encode_get_mac(get_mac, MFF_REG0, ep, ofpacts); | |
1213 | } | |
1214 | ||
1215 | static void | |
1216 | encode_GET_ND(const struct ovnact_get_mac_bind *get_mac, | |
1217 | const struct ovnact_encode_params *ep, | |
1218 | struct ofpbuf *ofpacts) | |
1219 | { | |
1220 | encode_get_mac(get_mac, MFF_XXREG0, ep, ofpacts); | |
1221 | } | |
1222 | ||
1223 | static void | |
80b6743d | 1224 | ovnact_get_mac_bind_free(struct ovnact_get_mac_bind *get_mac OVS_UNUSED) |
d5a76da4 BP |
1225 | { |
1226 | } | |
1227 | \f | |
1228 | static void | |
1229 | parse_put_mac_bind(struct action_context *ctx, int width, | |
1230 | struct ovnact_put_mac_bind *put_mac) | |
1231 | { | |
9aef3c1b | 1232 | lexer_force_match(ctx->lexer, LEX_T_LPAREN); |
d5a76da4 | 1233 | action_parse_field(ctx, 0, false, &put_mac->port); |
9aef3c1b | 1234 | lexer_force_match(ctx->lexer, LEX_T_COMMA); |
d5a76da4 | 1235 | action_parse_field(ctx, width, false, &put_mac->ip); |
9aef3c1b | 1236 | lexer_force_match(ctx->lexer, LEX_T_COMMA); |
d5a76da4 | 1237 | action_parse_field(ctx, 48, false, &put_mac->mac); |
9aef3c1b | 1238 | lexer_force_match(ctx->lexer, LEX_T_RPAREN); |
d5a76da4 BP |
1239 | } |
1240 | ||
1241 | static void | |
1242 | format_put_mac_bind(const struct ovnact_put_mac_bind *put_mac, | |
1243 | const char *name, struct ds *s) | |
1244 | { | |
1245 | ds_put_format(s, "%s(", name); | |
1246 | expr_field_format(&put_mac->port, s); | |
1247 | ds_put_cstr(s, ", "); | |
1248 | expr_field_format(&put_mac->ip, s); | |
1249 | ds_put_cstr(s, ", "); | |
1250 | expr_field_format(&put_mac->mac, s); | |
1251 | ds_put_cstr(s, ");"); | |
1252 | } | |
1253 | ||
1254 | static void | |
1255 | format_PUT_ARP(const struct ovnact_put_mac_bind *put_mac, struct ds *s) | |
1256 | { | |
1257 | format_put_mac_bind(put_mac, "put_arp", s); | |
1258 | } | |
1259 | ||
1260 | static void | |
1261 | format_PUT_ND(const struct ovnact_put_mac_bind *put_mac, struct ds *s) | |
1262 | { | |
1263 | format_put_mac_bind(put_mac, "put_nd", s); | |
1264 | } | |
1265 | ||
1266 | static void | |
1267 | encode_put_mac(const struct ovnact_put_mac_bind *put_mac, | |
1268 | enum mf_field_id ip_field, enum action_opcode opcode, | |
1269 | struct ofpbuf *ofpacts) | |
1270 | { | |
1271 | const struct arg args[] = { | |
1272 | { expr_resolve_field(&put_mac->port), MFF_LOG_INPORT }, | |
1273 | { expr_resolve_field(&put_mac->ip), ip_field }, | |
1274 | { expr_resolve_field(&put_mac->mac), MFF_ETH_SRC } | |
1275 | }; | |
1276 | encode_setup_args(args, ARRAY_SIZE(args), ofpacts); | |
1277 | encode_controller_op(opcode, ofpacts); | |
1278 | encode_restore_args(args, ARRAY_SIZE(args), ofpacts); | |
1279 | } | |
1280 | ||
1281 | static void | |
1282 | encode_PUT_ARP(const struct ovnact_put_mac_bind *put_mac, | |
1283 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
1284 | struct ofpbuf *ofpacts) | |
1285 | { | |
1286 | encode_put_mac(put_mac, MFF_REG0, ACTION_OPCODE_PUT_ARP, ofpacts); | |
1287 | } | |
1288 | ||
1289 | static void | |
1290 | encode_PUT_ND(const struct ovnact_put_mac_bind *put_mac, | |
1291 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
1292 | struct ofpbuf *ofpacts) | |
1293 | { | |
1294 | encode_put_mac(put_mac, MFF_XXREG0, ACTION_OPCODE_PUT_ND, ofpacts); | |
467085fd GS |
1295 | } |
1296 | ||
c34a87b6 | 1297 | static void |
80b6743d | 1298 | ovnact_put_mac_bind_free(struct ovnact_put_mac_bind *put_mac OVS_UNUSED) |
d5a76da4 BP |
1299 | { |
1300 | } | |
1301 | \f | |
1302 | static void | |
1303 | parse_dhcp_opt(struct action_context *ctx, struct ovnact_dhcp_option *o, | |
1304 | bool v6) | |
1305 | { | |
1306 | if (ctx->lexer->token.type != LEX_T_ID) { | |
9aef3c1b | 1307 | lexer_syntax_error(ctx->lexer, NULL); |
c34a87b6 JP |
1308 | return; |
1309 | } | |
1310 | ||
d5a76da4 BP |
1311 | const char *name = v6 ? "DHCPv6" : "DHCPv4"; |
1312 | const struct hmap *map = v6 ? ctx->pp->dhcpv6_opts : ctx->pp->dhcp_opts; | |
ebf8381b | 1313 | o->option = map ? dhcp_opts_find(map, ctx->lexer->token.s) : NULL; |
d5a76da4 | 1314 | if (!o->option) { |
9aef3c1b | 1315 | lexer_syntax_error(ctx->lexer, "expecting %s option name", name); |
d5a76da4 BP |
1316 | return; |
1317 | } | |
1318 | lexer_get(ctx->lexer); | |
1319 | ||
9aef3c1b | 1320 | if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { |
d5a76da4 BP |
1321 | return; |
1322 | } | |
1323 | ||
9aef3c1b | 1324 | if (!expr_constant_set_parse(ctx->lexer, &o->value)) { |
d5a76da4 | 1325 | memset(&o->value, 0, sizeof o->value); |
d5a76da4 BP |
1326 | return; |
1327 | } | |
c34a87b6 | 1328 | |
d5a76da4 BP |
1329 | if (!strcmp(o->option->type, "str")) { |
1330 | if (o->value.type != EXPR_C_STRING) { | |
9aef3c1b BP |
1331 | lexer_error(ctx->lexer, "%s option %s requires string value.", |
1332 | name, o->option->name); | |
d5a76da4 BP |
1333 | return; |
1334 | } | |
1335 | } else { | |
1336 | if (o->value.type != EXPR_C_INTEGER) { | |
9aef3c1b BP |
1337 | lexer_error(ctx->lexer, "%s option %s requires numeric value.", |
1338 | name, o->option->name); | |
d5a76da4 BP |
1339 | return; |
1340 | } | |
1341 | } | |
1342 | } | |
c34a87b6 | 1343 | |
d5a76da4 BP |
1344 | static const struct ovnact_dhcp_option * |
1345 | find_offerip(const struct ovnact_dhcp_option *options, size_t n) | |
1346 | { | |
1347 | for (const struct ovnact_dhcp_option *o = options; o < &options[n]; o++) { | |
1348 | if (o->option->code == 0) { | |
1349 | return o; | |
1350 | } | |
1351 | } | |
1352 | return NULL; | |
c34a87b6 JP |
1353 | } |
1354 | ||
1355 | static void | |
d5a76da4 | 1356 | free_dhcp_options(struct ovnact_dhcp_option *options, size_t n) |
c34a87b6 | 1357 | { |
d5a76da4 BP |
1358 | for (struct ovnact_dhcp_option *o = options; o < &options[n]; o++) { |
1359 | expr_constant_set_destroy(&o->value); | |
c34a87b6 | 1360 | } |
d5a76da4 | 1361 | free(options); |
c34a87b6 JP |
1362 | } |
1363 | ||
d5a76da4 BP |
1364 | /* Parses the "put_dhcp_opts" and "put_dhcpv6_opts" actions. |
1365 | * | |
1366 | * The caller has already consumed "<dst> =", so this just parses the rest. */ | |
78aab811 | 1367 | static void |
d5a76da4 BP |
1368 | parse_put_dhcp_opts(struct action_context *ctx, |
1369 | const struct expr_field *dst, | |
1370 | struct ovnact_put_dhcp_opts *pdo) | |
78aab811 | 1371 | { |
d5a76da4 BP |
1372 | lexer_get(ctx->lexer); /* Skip put_dhcp[v6]_opts. */ |
1373 | lexer_get(ctx->lexer); /* Skip '('. */ | |
78aab811 | 1374 | |
d5a76da4 BP |
1375 | /* Validate that the destination is a 1-bit, modifiable field. */ |
1376 | char *error = expr_type_check(dst, 1, true); | |
1377 | if (error) { | |
9aef3c1b | 1378 | lexer_error(ctx->lexer, "%s", error); |
d5a76da4 BP |
1379 | free(error); |
1380 | return; | |
78aab811 | 1381 | } |
d5a76da4 | 1382 | pdo->dst = *dst; |
78aab811 | 1383 | |
d5a76da4 BP |
1384 | size_t allocated_options = 0; |
1385 | while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { | |
1386 | if (pdo->n_options >= allocated_options) { | |
1387 | pdo->options = x2nrealloc(pdo->options, &allocated_options, | |
1388 | sizeof *pdo->options); | |
1389 | } | |
78aab811 | 1390 | |
d5a76da4 BP |
1391 | struct ovnact_dhcp_option *o = &pdo->options[pdo->n_options++]; |
1392 | memset(o, 0, sizeof *o); | |
1393 | parse_dhcp_opt(ctx, o, pdo->ovnact.type == OVNACT_PUT_DHCPV6_OPTS); | |
9aef3c1b | 1394 | if (ctx->lexer->error) { |
d5a76da4 BP |
1395 | return; |
1396 | } | |
a9e1b66f | 1397 | |
d5a76da4 BP |
1398 | lexer_match(ctx->lexer, LEX_T_COMMA); |
1399 | } | |
a9e1b66f | 1400 | |
d5a76da4 BP |
1401 | if (pdo->ovnact.type == OVNACT_PUT_DHCPV4_OPTS |
1402 | && !find_offerip(pdo->options, pdo->n_options)) { | |
9aef3c1b BP |
1403 | lexer_error(ctx->lexer, |
1404 | "put_dhcp_opts requires offerip to be specified."); | |
d5a76da4 | 1405 | return; |
a9e1b66f | 1406 | } |
d5a76da4 | 1407 | } |
a9e1b66f | 1408 | |
d5a76da4 BP |
1409 | static void |
1410 | format_put_dhcp_opts(const char *name, | |
1411 | const struct ovnact_put_dhcp_opts *pdo, struct ds *s) | |
1412 | { | |
1413 | expr_field_format(&pdo->dst, s); | |
1414 | ds_put_format(s, " = %s(", name); | |
1415 | for (const struct ovnact_dhcp_option *o = pdo->options; | |
1416 | o < &pdo->options[pdo->n_options]; o++) { | |
1417 | if (o != pdo->options) { | |
1418 | ds_put_cstr(s, ", "); | |
1419 | } | |
1420 | ds_put_format(s, "%s = ", o->option->name); | |
1421 | expr_constant_set_format(&o->value, s); | |
a9e1b66f | 1422 | } |
d5a76da4 BP |
1423 | ds_put_cstr(s, ");"); |
1424 | } | |
a9e1b66f | 1425 | |
d5a76da4 BP |
1426 | static void |
1427 | format_PUT_DHCPV4_OPTS(const struct ovnact_put_dhcp_opts *pdo, struct ds *s) | |
1428 | { | |
1429 | format_put_dhcp_opts("put_dhcp_opts", pdo, s); | |
a9e1b66f RB |
1430 | } |
1431 | ||
d5a76da4 BP |
1432 | static void |
1433 | format_PUT_DHCPV6_OPTS(const struct ovnact_put_dhcp_opts *pdo, struct ds *s) | |
a9e1b66f | 1434 | { |
d5a76da4 BP |
1435 | format_put_dhcp_opts("put_dhcpv6_opts", pdo, s); |
1436 | } | |
1437 | ||
1438 | static void | |
1439 | encode_put_dhcpv4_option(const struct ovnact_dhcp_option *o, | |
1440 | struct ofpbuf *ofpacts) | |
1441 | { | |
1442 | uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2); | |
1443 | opt_header[0] = o->option->code; | |
1444 | ||
1445 | const union expr_constant *c = o->value.values; | |
1446 | size_t n_values = o->value.n_values; | |
1447 | if (!strcmp(o->option->type, "bool") || | |
1448 | !strcmp(o->option->type, "uint8")) { | |
1449 | opt_header[1] = 1; | |
1450 | ofpbuf_put(ofpacts, &c->value.u8_val, 1); | |
1451 | } else if (!strcmp(o->option->type, "uint16")) { | |
1452 | opt_header[1] = 2; | |
1453 | ofpbuf_put(ofpacts, &c->value.be16_int, 2); | |
1454 | } else if (!strcmp(o->option->type, "uint32")) { | |
1455 | opt_header[1] = 4; | |
1456 | ofpbuf_put(ofpacts, &c->value.be32_int, 4); | |
1457 | } else if (!strcmp(o->option->type, "ipv4")) { | |
1458 | opt_header[1] = n_values * sizeof(ovs_be32); | |
1459 | for (size_t i = 0; i < n_values; i++) { | |
1460 | ofpbuf_put(ofpacts, &c[i].value.ipv4, sizeof(ovs_be32)); | |
a9e1b66f | 1461 | } |
d5a76da4 BP |
1462 | } else if (!strcmp(o->option->type, "static_routes")) { |
1463 | size_t no_of_routes = n_values; | |
1464 | if (no_of_routes % 2) { | |
1465 | no_of_routes -= 1; | |
a9e1b66f | 1466 | } |
d5a76da4 | 1467 | opt_header[1] = 0; |
354b8f27 | 1468 | |
d5a76da4 BP |
1469 | /* Calculating the length of this option first because when |
1470 | * we call ofpbuf_put, it might reallocate the buffer if the | |
1471 | * tail room is short making "opt_header" pointer invalid. | |
1472 | * So running the for loop twice. | |
1473 | */ | |
1474 | for (size_t i = 0; i < no_of_routes; i += 2) { | |
1475 | uint8_t plen = 32; | |
1476 | if (c[i].masked) { | |
1477 | plen = (uint8_t) ip_count_cidr_bits(c[i].mask.ipv4); | |
1478 | } | |
1479 | opt_header[1] += (1 + (plen / 8) + sizeof(ovs_be32)) ; | |
a9e1b66f | 1480 | } |
a9e1b66f | 1481 | |
d5a76da4 BP |
1482 | /* Copied from RFC 3442. Please refer to this RFC for the format of |
1483 | * the classless static route option. | |
1484 | * | |
1485 | * The following table contains some examples of how various subnet | |
1486 | * number/mask combinations can be encoded: | |
1487 | * | |
1488 | * Subnet number Subnet mask Destination descriptor | |
1489 | * 0 0 0 | |
1490 | * 10.0.0.0 255.0.0.0 8.10 | |
1491 | * 10.0.0.0 255.255.255.0 24.10.0.0 | |
1492 | * 10.17.0.0 255.255.0.0 16.10.17 | |
1493 | * 10.27.129.0 255.255.255.0 24.10.27.129 | |
1494 | * 10.229.0.128 255.255.255.128 25.10.229.0.128 | |
1495 | * 10.198.122.47 255.255.255.255 32.10.198.122.47 | |
1496 | */ | |
1497 | ||
1498 | for (size_t i = 0; i < no_of_routes; i += 2) { | |
1499 | uint8_t plen = 32; | |
1500 | if (c[i].masked) { | |
1501 | plen = ip_count_cidr_bits(c[i].mask.ipv4); | |
1502 | } | |
1503 | ofpbuf_put(ofpacts, &plen, 1); | |
1504 | ofpbuf_put(ofpacts, &c[i].value.ipv4, plen / 8); | |
1505 | ofpbuf_put(ofpacts, &c[i + 1].value.ipv4, | |
1506 | sizeof(ovs_be32)); | |
a9e1b66f | 1507 | } |
d5a76da4 BP |
1508 | } else if (!strcmp(o->option->type, "str")) { |
1509 | opt_header[1] = strlen(c->string); | |
1510 | ofpbuf_put(ofpacts, c->string, opt_header[1]); | |
a9e1b66f | 1511 | } |
a9e1b66f RB |
1512 | } |
1513 | ||
1514 | static void | |
d5a76da4 BP |
1515 | encode_put_dhcpv6_option(const struct ovnact_dhcp_option *o, |
1516 | struct ofpbuf *ofpacts) | |
a9e1b66f | 1517 | { |
d5a76da4 | 1518 | struct dhcp_opt6_header *opt = ofpbuf_put_uninit(ofpacts, sizeof *opt); |
d5a76da4 BP |
1519 | const union expr_constant *c = o->value.values; |
1520 | size_t n_values = o->value.n_values; | |
a55dacac DDP |
1521 | size_t size; |
1522 | ||
1523 | opt->opt_code = htons(o->option->code); | |
1524 | ||
d5a76da4 | 1525 | if (!strcmp(o->option->type, "ipv6")) { |
a55dacac DDP |
1526 | size = n_values * sizeof(struct in6_addr); |
1527 | opt->size = htons(size); | |
d5a76da4 BP |
1528 | for (size_t i = 0; i < n_values; i++) { |
1529 | ofpbuf_put(ofpacts, &c[i].value.ipv6, sizeof(struct in6_addr)); | |
1530 | } | |
1531 | } else if (!strcmp(o->option->type, "mac")) { | |
a55dacac DDP |
1532 | size = sizeof(struct eth_addr); |
1533 | opt->size = htons(size); | |
1534 | ofpbuf_put(ofpacts, &c->value.mac, size); | |
d5a76da4 | 1535 | } else if (!strcmp(o->option->type, "str")) { |
a55dacac DDP |
1536 | size = strlen(c->string); |
1537 | opt->size = htons(size); | |
1538 | ofpbuf_put(ofpacts, c->string, size); | |
a9e1b66f | 1539 | } |
d5a76da4 | 1540 | } |
a9e1b66f | 1541 | |
d5a76da4 BP |
1542 | static void |
1543 | encode_PUT_DHCPV4_OPTS(const struct ovnact_put_dhcp_opts *pdo, | |
1544 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
1545 | struct ofpbuf *ofpacts) | |
1546 | { | |
1547 | struct mf_subfield dst = expr_resolve_field(&pdo->dst); | |
1548 | ||
1549 | size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_PUT_DHCP_OPTS, | |
1550 | true, ofpacts); | |
1551 | nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false); | |
1552 | ovs_be32 ofs = htonl(dst.ofs); | |
1553 | ofpbuf_put(ofpacts, &ofs, sizeof ofs); | |
1554 | ||
1555 | /* Encode the offerip option first, because it's a special case and needs | |
1556 | * to be first in the actual DHCP response, and then encode the rest | |
1557 | * (skipping offerip the second time around). */ | |
1558 | const struct ovnact_dhcp_option *offerip_opt = find_offerip( | |
1559 | pdo->options, pdo->n_options); | |
1560 | ovs_be32 offerip = offerip_opt->value.values[0].value.ipv4; | |
1561 | ofpbuf_put(ofpacts, &offerip, sizeof offerip); | |
1562 | ||
1563 | for (const struct ovnact_dhcp_option *o = pdo->options; | |
1564 | o < &pdo->options[pdo->n_options]; o++) { | |
1565 | if (o != offerip_opt) { | |
1566 | encode_put_dhcpv4_option(o, ofpacts); | |
a9e1b66f RB |
1567 | } |
1568 | } | |
1569 | ||
d5a76da4 | 1570 | encode_finish_controller_op(oc_offset, ofpacts); |
78aab811 JP |
1571 | } |
1572 | ||
de297547 | 1573 | static void |
d5a76da4 BP |
1574 | encode_PUT_DHCPV6_OPTS(const struct ovnact_put_dhcp_opts *pdo, |
1575 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
1576 | struct ofpbuf *ofpacts) | |
de297547 | 1577 | { |
d5a76da4 | 1578 | struct mf_subfield dst = expr_resolve_field(&pdo->dst); |
de297547 | 1579 | |
d5a76da4 BP |
1580 | size_t oc_offset = encode_start_controller_op( |
1581 | ACTION_OPCODE_PUT_DHCPV6_OPTS, true, ofpacts); | |
1582 | nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false); | |
1583 | ovs_be32 ofs = htonl(dst.ofs); | |
1584 | ofpbuf_put(ofpacts, &ofs, sizeof ofs); | |
de297547 | 1585 | |
d5a76da4 BP |
1586 | for (const struct ovnact_dhcp_option *o = pdo->options; |
1587 | o < &pdo->options[pdo->n_options]; o++) { | |
1588 | encode_put_dhcpv6_option(o, ofpacts); | |
de297547 | 1589 | } |
de297547 | 1590 | |
d5a76da4 BP |
1591 | encode_finish_controller_op(oc_offset, ofpacts); |
1592 | } | |
de297547 | 1593 | |
d5a76da4 | 1594 | static void |
80b6743d | 1595 | ovnact_put_dhcp_opts_free(struct ovnact_put_dhcp_opts *pdo) |
d5a76da4 BP |
1596 | { |
1597 | free_dhcp_options(pdo->options, pdo->n_options); | |
1598 | } | |
de297547 | 1599 | |
a6095f81 BS |
1600 | static void |
1601 | parse_SET_QUEUE(struct action_context *ctx) | |
1602 | { | |
1603 | int queue_id; | |
1604 | ||
1605 | if (!lexer_force_match(ctx->lexer, LEX_T_LPAREN) | |
1606 | || !lexer_get_int(ctx->lexer, &queue_id) | |
1607 | || !lexer_force_match(ctx->lexer, LEX_T_RPAREN)) { | |
1608 | return; | |
1609 | } | |
1610 | ||
1611 | if (queue_id < QDISC_MIN_QUEUE_ID || queue_id > QDISC_MAX_QUEUE_ID) { | |
1612 | lexer_error(ctx->lexer, "Queue ID %d for set_queue is " | |
1613 | "not in valid range %d to %d.", | |
1614 | queue_id, QDISC_MIN_QUEUE_ID, QDISC_MAX_QUEUE_ID); | |
1615 | return; | |
1616 | } | |
1617 | ||
1618 | ovnact_put_SET_QUEUE(ctx->ovnacts)->queue_id = queue_id; | |
1619 | } | |
1620 | ||
1621 | static void | |
1622 | format_SET_QUEUE(const struct ovnact_set_queue *set_queue, struct ds *s) | |
1623 | { | |
1624 | ds_put_format(s, "set_queue(%d);", set_queue->queue_id); | |
1625 | } | |
1626 | ||
1627 | static void | |
1628 | encode_SET_QUEUE(const struct ovnact_set_queue *set_queue, | |
1629 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
1630 | struct ofpbuf *ofpacts) | |
1631 | { | |
1632 | ofpact_put_SET_QUEUE(ofpacts)->queue_id = set_queue->queue_id; | |
1633 | } | |
1634 | ||
1635 | static void | |
80b6743d | 1636 | ovnact_set_queue_free(struct ovnact_set_queue *a OVS_UNUSED) |
a6095f81 BS |
1637 | { |
1638 | } | |
d5a76da4 BP |
1639 | \f |
1640 | /* Parses an assignment or exchange or put_dhcp_opts action. */ | |
1641 | static void | |
1642 | parse_set_action(struct action_context *ctx) | |
1643 | { | |
d5a76da4 | 1644 | struct expr_field lhs; |
9aef3c1b BP |
1645 | if (!expr_field_parse(ctx->lexer, ctx->pp->symtab, &lhs, &ctx->prereqs)) { |
1646 | return; | |
1647 | } | |
de297547 | 1648 | |
9aef3c1b BP |
1649 | if (lexer_match(ctx->lexer, LEX_T_EXCHANGE)) { |
1650 | parse_assignment_action(ctx, true, &lhs); | |
1651 | } else if (lexer_match(ctx->lexer, LEX_T_EQUALS)) { | |
1652 | if (ctx->lexer->token.type != LEX_T_ID) { | |
1653 | parse_LOAD(ctx, &lhs); | |
1654 | } else if (!strcmp(ctx->lexer->token.s, "put_dhcp_opts") | |
1655 | && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { | |
1656 | parse_put_dhcp_opts(ctx, &lhs, ovnact_put_PUT_DHCPV4_OPTS( | |
1657 | ctx->ovnacts)); | |
1658 | } else if (!strcmp(ctx->lexer->token.s, "put_dhcpv6_opts") | |
1659 | && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { | |
1660 | parse_put_dhcp_opts(ctx, &lhs, ovnact_put_PUT_DHCPV6_OPTS( | |
1661 | ctx->ovnacts)); | |
de297547 | 1662 | } else { |
9aef3c1b | 1663 | parse_assignment_action(ctx, false, &lhs); |
de297547 | 1664 | } |
9aef3c1b BP |
1665 | } else { |
1666 | lexer_syntax_error(ctx->lexer, "expecting `=' or `<->'"); | |
de297547 | 1667 | } |
de297547 GS |
1668 | } |
1669 | ||
d8681a83 BP |
1670 | static bool |
1671 | parse_action(struct action_context *ctx) | |
1672 | { | |
1673 | if (ctx->lexer->token.type != LEX_T_ID) { | |
9aef3c1b | 1674 | lexer_syntax_error(ctx->lexer, NULL); |
d8681a83 BP |
1675 | return false; |
1676 | } | |
1677 | ||
1678 | enum lex_type lookahead = lexer_lookahead(ctx->lexer); | |
1679 | if (lookahead == LEX_T_EQUALS || lookahead == LEX_T_EXCHANGE | |
1680 | || lookahead == LEX_T_LSQUARE) { | |
1681 | parse_set_action(ctx); | |
1682 | } else if (lexer_match_id(ctx->lexer, "next")) { | |
d5a76da4 | 1683 | parse_NEXT(ctx); |
d8681a83 | 1684 | } else if (lexer_match_id(ctx->lexer, "output")) { |
d5a76da4 | 1685 | ovnact_put_OUTPUT(ctx->ovnacts); |
d8681a83 | 1686 | } else if (lexer_match_id(ctx->lexer, "ip.ttl")) { |
d5a76da4 | 1687 | parse_DEC_TTL(ctx); |
d8681a83 | 1688 | } else if (lexer_match_id(ctx->lexer, "ct_next")) { |
d5a76da4 | 1689 | parse_CT_NEXT(ctx); |
d8681a83 | 1690 | } else if (lexer_match_id(ctx->lexer, "ct_commit")) { |
d5a76da4 | 1691 | parse_CT_COMMIT(ctx); |
de297547 | 1692 | } else if (lexer_match_id(ctx->lexer, "ct_dnat")) { |
d5a76da4 | 1693 | parse_CT_DNAT(ctx); |
de297547 | 1694 | } else if (lexer_match_id(ctx->lexer, "ct_snat")) { |
d5a76da4 | 1695 | parse_CT_SNAT(ctx); |
467085fd GS |
1696 | } else if (lexer_match_id(ctx->lexer, "ct_lb")) { |
1697 | parse_ct_lb_action(ctx); | |
b3bd2c33 BP |
1698 | } else if (lexer_match_id(ctx->lexer, "clone")) { |
1699 | parse_CLONE(ctx); | |
6335d074 | 1700 | } else if (lexer_match_id(ctx->lexer, "arp")) { |
d5a76da4 BP |
1701 | parse_ARP(ctx); |
1702 | } else if (lexer_match_id(ctx->lexer, "nd_na")) { | |
1703 | parse_ND_NA(ctx); | |
0bac7164 | 1704 | } else if (lexer_match_id(ctx->lexer, "get_arp")) { |
d5a76da4 | 1705 | parse_get_mac_bind(ctx, 32, ovnact_put_GET_ARP(ctx->ovnacts)); |
0bac7164 | 1706 | } else if (lexer_match_id(ctx->lexer, "put_arp")) { |
d5a76da4 | 1707 | parse_put_mac_bind(ctx, 32, ovnact_put_PUT_ARP(ctx->ovnacts)); |
c34a87b6 | 1708 | } else if (lexer_match_id(ctx->lexer, "get_nd")) { |
d5a76da4 | 1709 | parse_get_mac_bind(ctx, 128, ovnact_put_GET_ND(ctx->ovnacts)); |
c34a87b6 | 1710 | } else if (lexer_match_id(ctx->lexer, "put_nd")) { |
d5a76da4 | 1711 | parse_put_mac_bind(ctx, 128, ovnact_put_PUT_ND(ctx->ovnacts)); |
a6095f81 BS |
1712 | } else if (lexer_match_id(ctx->lexer, "set_queue")) { |
1713 | parse_SET_QUEUE(ctx); | |
d8681a83 | 1714 | } else { |
9aef3c1b | 1715 | lexer_syntax_error(ctx->lexer, "expecting action"); |
d8681a83 | 1716 | } |
9aef3c1b BP |
1717 | lexer_force_match(ctx->lexer, LEX_T_SEMICOLON); |
1718 | return !ctx->lexer->error; | |
d8681a83 BP |
1719 | } |
1720 | ||
3b7cb7e1 | 1721 | static void |
bac29564 | 1722 | parse_actions(struct action_context *ctx, enum lex_type sentinel) |
3b7cb7e1 BP |
1723 | { |
1724 | /* "drop;" by itself is a valid (empty) set of actions, but it can't be | |
1725 | * combined with other actions because that doesn't make sense. */ | |
1726 | if (ctx->lexer->token.type == LEX_T_ID | |
1727 | && !strcmp(ctx->lexer->token.s, "drop") | |
1728 | && lexer_lookahead(ctx->lexer) == LEX_T_SEMICOLON) { | |
1729 | lexer_get(ctx->lexer); /* Skip "drop". */ | |
1730 | lexer_get(ctx->lexer); /* Skip ";". */ | |
bac29564 | 1731 | lexer_force_match(ctx->lexer, sentinel); |
3b7cb7e1 BP |
1732 | return; |
1733 | } | |
1734 | ||
bac29564 | 1735 | while (!lexer_match(ctx->lexer, sentinel)) { |
d8681a83 | 1736 | if (!parse_action(ctx)) { |
3b7cb7e1 BP |
1737 | return; |
1738 | } | |
1739 | } | |
1740 | } | |
1741 | ||
1742 | /* Parses OVN actions, in the format described for the "actions" column in the | |
48605550 | 1743 | * Logical_Flow table in ovn-sb(5), and appends the parsed versions of the |
d5a76da4 BP |
1744 | * actions to 'ovnacts' as "struct ovnact"s. The caller must eventually free |
1745 | * the parsed ovnacts with ovnacts_free(). | |
3b7cb7e1 | 1746 | * |
d5a76da4 | 1747 | * 'pp' provides most of the parameters for translation. |
3646e396 | 1748 | * |
3b7cb7e1 BP |
1749 | * Some actions add extra requirements (prerequisites) to the flow's match. If |
1750 | * so, this function sets '*prereqsp' to the actions' prerequisites; otherwise, | |
1751 | * it sets '*prereqsp' to NULL. The caller owns '*prereqsp' and must | |
1752 | * eventually free it. | |
1753 | * | |
9aef3c1b BP |
1754 | * Returns true if successful, false if an error occurred. Upon return, |
1755 | * returns true if and only if lexer->error is NULL. | |
d5a76da4 | 1756 | */ |
9aef3c1b | 1757 | bool |
d5a76da4 BP |
1758 | ovnacts_parse(struct lexer *lexer, const struct ovnact_parse_params *pp, |
1759 | struct ofpbuf *ovnacts, struct expr **prereqsp) | |
3b7cb7e1 | 1760 | { |
d5a76da4 | 1761 | size_t ovnacts_start = ovnacts->size; |
3b7cb7e1 | 1762 | |
1d7b2ece | 1763 | struct action_context ctx = { |
d5a76da4 | 1764 | .pp = pp, |
1d7b2ece | 1765 | .lexer = lexer, |
d5a76da4 | 1766 | .ovnacts = ovnacts, |
1d7b2ece BP |
1767 | .prereqs = NULL, |
1768 | }; | |
9aef3c1b | 1769 | if (!lexer->error) { |
bac29564 | 1770 | parse_actions(&ctx, LEX_T_END); |
9aef3c1b | 1771 | } |
3b7cb7e1 | 1772 | |
9aef3c1b | 1773 | if (!lexer->error) { |
3b7cb7e1 | 1774 | *prereqsp = ctx.prereqs; |
9aef3c1b | 1775 | return true; |
3b7cb7e1 | 1776 | } else { |
d5a76da4 BP |
1777 | ofpbuf_pull(ovnacts, ovnacts_start); |
1778 | ovnacts_free(ovnacts->data, ovnacts->size); | |
1779 | ofpbuf_push_uninit(ovnacts, ovnacts_start); | |
1780 | ||
1781 | ovnacts->size = ovnacts_start; | |
3b7cb7e1 BP |
1782 | expr_destroy(ctx.prereqs); |
1783 | *prereqsp = NULL; | |
9aef3c1b | 1784 | return false; |
3b7cb7e1 BP |
1785 | } |
1786 | } | |
1787 | ||
d5a76da4 | 1788 | /* Like ovnacts_parse(), but the actions are taken from 's'. */ |
3b7cb7e1 | 1789 | char * OVS_WARN_UNUSED_RESULT |
d5a76da4 | 1790 | ovnacts_parse_string(const char *s, const struct ovnact_parse_params *pp, |
1d7b2ece | 1791 | struct ofpbuf *ofpacts, struct expr **prereqsp) |
3b7cb7e1 BP |
1792 | { |
1793 | struct lexer lexer; | |
3b7cb7e1 BP |
1794 | |
1795 | lexer_init(&lexer, s); | |
1796 | lexer_get(&lexer); | |
9aef3c1b BP |
1797 | ovnacts_parse(&lexer, pp, ofpacts, prereqsp); |
1798 | char *error = lexer_steal_error(&lexer); | |
3b7cb7e1 BP |
1799 | lexer_destroy(&lexer); |
1800 | ||
1801 | return error; | |
1802 | } | |
d5a76da4 BP |
1803 | \f |
1804 | /* Formatting ovnacts. */ | |
1805 | ||
1806 | static void | |
1807 | ovnact_format(const struct ovnact *a, struct ds *s) | |
1808 | { | |
1809 | switch (a->type) { | |
1810 | #define OVNACT(ENUM, STRUCT) \ | |
1811 | case OVNACT_##ENUM: \ | |
1812 | format_##ENUM(ALIGNED_CAST(const struct STRUCT *, a), s); \ | |
1813 | break; | |
1814 | OVNACTS | |
1815 | #undef OVNACT | |
1816 | default: | |
1817 | OVS_NOT_REACHED(); | |
1818 | } | |
1819 | } | |
1820 | ||
1821 | /* Appends a string representing the 'ovnacts_len' bytes of ovnacts in | |
1822 | * 'ovnacts' to 'string'. */ | |
1823 | void | |
1824 | ovnacts_format(const struct ovnact *ovnacts, size_t ovnacts_len, | |
1825 | struct ds *string) | |
1826 | { | |
1827 | if (!ovnacts_len) { | |
1828 | ds_put_cstr(string, "drop;"); | |
1829 | } else { | |
1830 | const struct ovnact *a; | |
1831 | ||
1832 | OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) { | |
1833 | if (a != ovnacts) { | |
1834 | ds_put_char(string, ' '); | |
1835 | } | |
1836 | ovnact_format(a, string); | |
1837 | } | |
1838 | } | |
1839 | } | |
1840 | \f | |
1841 | /* Encoding ovnacts to OpenFlow. */ | |
1842 | ||
1843 | static void | |
1844 | ovnact_encode(const struct ovnact *a, const struct ovnact_encode_params *ep, | |
1845 | struct ofpbuf *ofpacts) | |
1846 | { | |
1847 | switch (a->type) { | |
1848 | #define OVNACT(ENUM, STRUCT) \ | |
1849 | case OVNACT_##ENUM: \ | |
1850 | encode_##ENUM(ALIGNED_CAST(const struct STRUCT *, a), \ | |
1851 | ep, ofpacts); \ | |
1852 | break; | |
1853 | OVNACTS | |
1854 | #undef OVNACT | |
1855 | default: | |
1856 | OVS_NOT_REACHED(); | |
1857 | } | |
1858 | } | |
1859 | ||
1860 | /* Appends ofpacts to 'ofpacts' that represent the actions in the 'ovnacts_len' | |
1861 | * bytes of actions starting at 'ovnacts'. */ | |
1862 | void | |
1863 | ovnacts_encode(const struct ovnact *ovnacts, size_t ovnacts_len, | |
1864 | const struct ovnact_encode_params *ep, | |
1865 | struct ofpbuf *ofpacts) | |
1866 | { | |
1867 | if (ovnacts) { | |
1868 | const struct ovnact *a; | |
1869 | ||
1870 | OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) { | |
1871 | ovnact_encode(a, ep, ofpacts); | |
1872 | } | |
1873 | } | |
1874 | } | |
1875 | \f | |
1876 | /* Freeing ovnacts. */ | |
1877 | ||
1878 | static void | |
1879 | ovnact_free(struct ovnact *a) | |
1880 | { | |
1881 | switch (a->type) { | |
1882 | #define OVNACT(ENUM, STRUCT) \ | |
1883 | case OVNACT_##ENUM: \ | |
80b6743d | 1884 | STRUCT##_free(ALIGNED_CAST(struct STRUCT *, a)); \ |
d5a76da4 BP |
1885 | break; |
1886 | OVNACTS | |
1887 | #undef OVNACT | |
1888 | default: | |
1889 | OVS_NOT_REACHED(); | |
1890 | } | |
1891 | } | |
1892 | ||
1893 | /* Frees each of the actions in the 'ovnacts_len' bytes of actions starting at | |
1894 | * 'ovnacts'. | |
1895 | * | |
1896 | * Does not call free(ovnacts); the caller must do so if desirable. */ | |
1897 | void | |
1898 | ovnacts_free(struct ovnact *ovnacts, size_t ovnacts_len) | |
1899 | { | |
1900 | if (ovnacts) { | |
1901 | struct ovnact *a; | |
1902 | ||
1903 | OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) { | |
1904 | ovnact_free(a); | |
1905 | } | |
1906 | } | |
1907 | } |