]>
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" |
16936e4d | 23 | #include "ovn-l7.h" |
467085fd | 24 | #include "hash.h" |
52ed5fcc | 25 | #include "lib/packets.h" |
667e2b0b | 26 | #include "logical-fields.h" |
42814145 | 27 | #include "nx-match.h" |
b598f214 | 28 | #include "openvswitch/dynamic-string.h" |
d5a76da4 BP |
29 | #include "openvswitch/hmap.h" |
30 | #include "openvswitch/json.h" | |
b598f214 | 31 | #include "openvswitch/ofp-actions.h" |
64c96779 | 32 | #include "openvswitch/ofpbuf.h" |
d5a76da4 | 33 | #include "openvswitch/vlog.h" |
8b2ed684 AR |
34 | #include "ovn/actions.h" |
35 | #include "ovn/expr.h" | |
36 | #include "ovn/lex.h" | |
d383eed5 | 37 | #include "ovn/lib/acl-log.h" |
ad35c0c5 | 38 | #include "ovn/lib/extend-table.h" |
42814145 | 39 | #include "packets.h" |
ee89ea7b | 40 | #include "openvswitch/shash.h" |
78aab811 | 41 | #include "simap.h" |
3b7cb7e1 | 42 | |
d5a76da4 BP |
43 | VLOG_DEFINE_THIS_MODULE(actions); |
44 | \f | |
45 | /* Prototypes for functions to be defined by each action. */ | |
46 | #define OVNACT(ENUM, STRUCT) \ | |
47 | static void format_##ENUM(const struct STRUCT *, struct ds *); \ | |
48 | static void encode_##ENUM(const struct STRUCT *, \ | |
49 | const struct ovnact_encode_params *, \ | |
50 | struct ofpbuf *ofpacts); \ | |
80b6743d | 51 | static void STRUCT##_free(struct STRUCT *a); |
d5a76da4 BP |
52 | OVNACTS |
53 | #undef OVNACT | |
54 | \f | |
55 | /* Helpers. */ | |
56 | ||
57 | /* Implementation of ovnact_put_<ENUM>(). */ | |
58 | void * | |
59 | ovnact_put(struct ofpbuf *ovnacts, enum ovnact_type type, size_t len) | |
60 | { | |
8a41ad8e | 61 | ovs_assert(len == OVNACT_ALIGN(len)); |
d5a76da4 BP |
62 | |
63 | ovnacts->header = ofpbuf_put_uninit(ovnacts, len); | |
8a41ad8e | 64 | struct ovnact *ovnact = ovnacts->header; |
d5a76da4 BP |
65 | ovnact_init(ovnact, type, len); |
66 | return ovnact; | |
67 | } | |
68 | ||
69 | /* Implementation of ovnact_init_<ENUM>(). */ | |
70 | void | |
71 | ovnact_init(struct ovnact *ovnact, enum ovnact_type type, size_t len) | |
72 | { | |
8a41ad8e | 73 | ovs_assert(len == OVNACT_ALIGN(len)); |
d5a76da4 BP |
74 | memset(ovnact, 0, len); |
75 | ovnact->type = type; | |
76 | ovnact->len = len; | |
77 | } | |
78 | ||
79 | static size_t | |
80 | encode_start_controller_op(enum action_opcode opcode, bool pause, | |
81 | struct ofpbuf *ofpacts) | |
82 | { | |
83 | size_t ofs = ofpacts->size; | |
84 | ||
85 | struct ofpact_controller *oc = ofpact_put_CONTROLLER(ofpacts); | |
86 | oc->max_len = UINT16_MAX; | |
87 | oc->reason = OFPR_ACTION; | |
88 | oc->pause = pause; | |
89 | ||
90 | struct action_header ah = { .opcode = htonl(opcode) }; | |
91 | ofpbuf_put(ofpacts, &ah, sizeof ah); | |
92 | ||
93 | return ofs; | |
94 | } | |
95 | ||
96 | static void | |
97 | encode_finish_controller_op(size_t ofs, struct ofpbuf *ofpacts) | |
98 | { | |
99 | struct ofpact_controller *oc = ofpbuf_at_assert(ofpacts, ofs, sizeof *oc); | |
100 | ofpacts->header = oc; | |
101 | oc->userdata_len = ofpacts->size - (ofs + sizeof *oc); | |
102 | ofpact_finish_CONTROLLER(ofpacts, &oc); | |
103 | } | |
104 | ||
105 | static void | |
106 | encode_controller_op(enum action_opcode opcode, struct ofpbuf *ofpacts) | |
107 | { | |
108 | size_t ofs = encode_start_controller_op(opcode, false, ofpacts); | |
109 | encode_finish_controller_op(ofs, ofpacts); | |
110 | } | |
111 | ||
112 | static void | |
113 | init_stack(struct ofpact_stack *stack, enum mf_field_id field) | |
114 | { | |
115 | stack->subfield.field = mf_from_id(field); | |
116 | stack->subfield.ofs = 0; | |
117 | stack->subfield.n_bits = stack->subfield.field->n_bits; | |
118 | } | |
119 | ||
120 | struct arg { | |
121 | const struct mf_subfield src; | |
122 | enum mf_field_id dst; | |
123 | }; | |
124 | ||
125 | static void | |
126 | encode_setup_args(const struct arg args[], size_t n_args, | |
127 | struct ofpbuf *ofpacts) | |
128 | { | |
129 | /* 1. Save all of the destinations that will be modified. */ | |
130 | for (const struct arg *a = args; a < &args[n_args]; a++) { | |
131 | ovs_assert(a->src.n_bits == mf_from_id(a->dst)->n_bits); | |
132 | if (a->src.field->id != a->dst) { | |
133 | init_stack(ofpact_put_STACK_PUSH(ofpacts), a->dst); | |
134 | } | |
135 | } | |
136 | ||
137 | /* 2. Push the sources, in reverse order. */ | |
138 | for (size_t i = n_args - 1; i < n_args; i--) { | |
139 | const struct arg *a = &args[i]; | |
140 | if (a->src.field->id != a->dst) { | |
141 | ofpact_put_STACK_PUSH(ofpacts)->subfield = a->src; | |
142 | } | |
143 | } | |
144 | ||
145 | /* 3. Pop the sources into the destinations. */ | |
146 | for (const struct arg *a = args; a < &args[n_args]; a++) { | |
147 | if (a->src.field->id != a->dst) { | |
148 | init_stack(ofpact_put_STACK_POP(ofpacts), a->dst); | |
149 | } | |
150 | } | |
151 | } | |
152 | ||
153 | static void | |
154 | encode_restore_args(const struct arg args[], size_t n_args, | |
155 | struct ofpbuf *ofpacts) | |
156 | { | |
157 | for (size_t i = n_args - 1; i < n_args; i--) { | |
158 | const struct arg *a = &args[i]; | |
159 | if (a->src.field->id != a->dst) { | |
160 | init_stack(ofpact_put_STACK_POP(ofpacts), a->dst); | |
161 | } | |
162 | } | |
163 | } | |
164 | ||
165 | static void | |
166 | put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits, | |
167 | struct ofpbuf *ofpacts) | |
168 | { | |
128684a6 JR |
169 | struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts, |
170 | mf_from_id(dst), NULL, | |
171 | NULL); | |
d5a76da4 | 172 | ovs_be64 n_value = htonll(value); |
128684a6 JR |
173 | bitwise_copy(&n_value, 8, 0, sf->value, sf->field->n_bytes, ofs, n_bits); |
174 | bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs, n_bits); | |
d5a76da4 | 175 | } |
4c99cb18 BP |
176 | |
177 | static uint8_t | |
178 | first_ptable(const struct ovnact_encode_params *ep, | |
179 | enum ovnact_pipeline pipeline) | |
180 | { | |
181 | return (pipeline == OVNACT_P_INGRESS | |
182 | ? ep->ingress_ptable | |
183 | : ep->egress_ptable); | |
184 | } | |
d5a76da4 BP |
185 | \f |
186 | /* Context maintained during ovnacts_parse(). */ | |
3b7cb7e1 | 187 | struct action_context { |
d5a76da4 | 188 | const struct ovnact_parse_params *pp; /* Parameters. */ |
3b7cb7e1 | 189 | struct lexer *lexer; /* Lexer for pulling more tokens. */ |
d5a76da4 | 190 | struct ofpbuf *ovnacts; /* Actions. */ |
3b7cb7e1 BP |
191 | struct expr *prereqs; /* Prerequisites to apply to match. */ |
192 | }; | |
193 | ||
bac29564 | 194 | static void parse_actions(struct action_context *, enum lex_type sentinel); |
d8681a83 | 195 | |
3b7cb7e1 | 196 | static bool |
9aef3c1b BP |
197 | action_parse_field(struct action_context *ctx, |
198 | int n_bits, bool rw, struct expr_field *f) | |
3b7cb7e1 | 199 | { |
9aef3c1b | 200 | if (!expr_field_parse(ctx->lexer, ctx->pp->symtab, f, &ctx->prereqs)) { |
3b7cb7e1 BP |
201 | return false; |
202 | } | |
d5a76da4 | 203 | |
9aef3c1b BP |
204 | char *error = expr_type_check(f, n_bits, rw); |
205 | if (error) { | |
206 | lexer_error(ctx->lexer, "%s", error); | |
207 | free(error); | |
d5a76da4 | 208 | return false; |
3b7cb7e1 | 209 | } |
3b7cb7e1 | 210 | |
9aef3c1b | 211 | return true; |
558ec83d BP |
212 | } |
213 | ||
d5a76da4 BP |
214 | static bool |
215 | action_parse_port(struct action_context *ctx, uint16_t *port) | |
216 | { | |
217 | if (lexer_is_int(ctx->lexer)) { | |
218 | int value = ntohll(ctx->lexer->token.value.integer); | |
219 | if (value <= UINT16_MAX) { | |
220 | *port = value; | |
221 | lexer_get(ctx->lexer); | |
222 | return true; | |
223 | } | |
224 | } | |
9aef3c1b | 225 | lexer_syntax_error(ctx->lexer, "expecting port number"); |
d5a76da4 BP |
226 | return false; |
227 | } | |
228 | ||
229 | /* Parses 'prerequisite' as an expression in the context of 'ctx', then adds it | |
230 | * as a conjunction with the existing 'ctx->prereqs'. */ | |
231 | static void | |
232 | add_prerequisite(struct action_context *ctx, const char *prerequisite) | |
233 | { | |
234 | struct expr *expr; | |
235 | char *error; | |
236 | ||
237 | expr = expr_parse_string(prerequisite, ctx->pp->symtab, NULL, &error); | |
238 | ovs_assert(!error); | |
239 | ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr); | |
240 | } | |
80b6743d BP |
241 | |
242 | static void | |
243 | ovnact_null_free(struct ovnact_null *a OVS_UNUSED) | |
244 | { | |
245 | } | |
d5a76da4 BP |
246 | \f |
247 | static void | |
248 | format_OUTPUT(const struct ovnact_null *a OVS_UNUSED, struct ds *s) | |
249 | { | |
250 | ds_put_cstr(s, "output;"); | |
251 | } | |
252 | ||
253 | static void | |
254 | emit_resubmit(struct ofpbuf *ofpacts, uint8_t ptable) | |
255 | { | |
256 | struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(ofpacts); | |
257 | resubmit->in_port = OFPP_IN_PORT; | |
258 | resubmit->table_id = ptable; | |
259 | } | |
260 | ||
261 | static void | |
262 | encode_OUTPUT(const struct ovnact_null *a OVS_UNUSED, | |
263 | const struct ovnact_encode_params *ep, | |
264 | struct ofpbuf *ofpacts) | |
265 | { | |
266 | emit_resubmit(ofpacts, ep->output_ptable); | |
267 | } | |
d5a76da4 | 268 | \f |
558ec83d | 269 | static void |
d5a76da4 | 270 | parse_NEXT(struct action_context *ctx) |
558ec83d | 271 | { |
d5a76da4 | 272 | if (!ctx->pp->n_tables) { |
9aef3c1b | 273 | lexer_error(ctx->lexer, "\"next\" action not allowed here."); |
8f5de083 BP |
274 | return; |
275 | } | |
558ec83d | 276 | |
4c99cb18 | 277 | int pipeline = ctx->pp->pipeline; |
8f5de083 | 278 | int table = ctx->pp->cur_ltable + 1; |
4c99cb18 BP |
279 | if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { |
280 | if (lexer_is_int(ctx->lexer)) { | |
281 | lexer_get_int(ctx->lexer, &table); | |
282 | } else { | |
283 | do { | |
284 | if (lexer_match_id(ctx->lexer, "pipeline")) { | |
285 | if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { | |
286 | return; | |
287 | } | |
288 | if (lexer_match_id(ctx->lexer, "ingress")) { | |
289 | pipeline = OVNACT_P_INGRESS; | |
290 | } else if (lexer_match_id(ctx->lexer, "egress")) { | |
291 | pipeline = OVNACT_P_EGRESS; | |
292 | } else { | |
293 | lexer_syntax_error( | |
294 | ctx->lexer, "expecting \"ingress\" or \"egress\""); | |
295 | return; | |
296 | } | |
297 | } else if (lexer_match_id(ctx->lexer, "table")) { | |
298 | if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS) || | |
299 | !lexer_force_int(ctx->lexer, &table)) { | |
300 | return; | |
301 | } | |
302 | } else { | |
303 | lexer_syntax_error(ctx->lexer, | |
304 | "expecting \"pipeline\" or \"table\""); | |
305 | return; | |
306 | } | |
307 | } while (lexer_match(ctx->lexer, LEX_T_COMMA)); | |
308 | } | |
309 | if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) { | |
310 | return; | |
311 | } | |
8f5de083 | 312 | } |
558ec83d | 313 | |
4c99cb18 BP |
314 | if (pipeline == OVNACT_P_EGRESS && ctx->pp->pipeline == OVNACT_P_INGRESS) { |
315 | lexer_error(ctx->lexer, | |
316 | "\"next\" action cannot advance from ingress to egress " | |
317 | "pipeline (use \"output\" action instead)"); | |
318 | } else if (table >= ctx->pp->n_tables) { | |
8f5de083 BP |
319 | lexer_error(ctx->lexer, |
320 | "\"next\" action cannot advance beyond table %d.", | |
321 | ctx->pp->n_tables - 1); | |
322 | return; | |
558ec83d | 323 | } |
8f5de083 BP |
324 | |
325 | struct ovnact_next *next = ovnact_put_NEXT(ctx->ovnacts); | |
4c99cb18 | 326 | next->pipeline = pipeline; |
8f5de083 | 327 | next->ltable = table; |
4c99cb18 | 328 | next->src_pipeline = ctx->pp->pipeline; |
8f5de083 | 329 | next->src_ltable = ctx->pp->cur_ltable; |
558ec83d BP |
330 | } |
331 | ||
5b84185b | 332 | static void |
d5a76da4 | 333 | format_NEXT(const struct ovnact_next *next, struct ds *s) |
5b84185b | 334 | { |
4c99cb18 BP |
335 | if (next->pipeline != next->src_pipeline) { |
336 | ds_put_format(s, "next(pipeline=%s, table=%d);", | |
337 | (next->pipeline == OVNACT_P_INGRESS | |
338 | ? "ingress" : "egress"), | |
339 | next->ltable); | |
340 | } else if (next->ltable != next->src_ltable + 1) { | |
8f5de083 BP |
341 | ds_put_format(s, "next(%d);", next->ltable); |
342 | } else { | |
343 | ds_put_cstr(s, "next;"); | |
344 | } | |
d5a76da4 | 345 | } |
5b84185b | 346 | |
d5a76da4 BP |
347 | static void |
348 | encode_NEXT(const struct ovnact_next *next, | |
349 | const struct ovnact_encode_params *ep, | |
350 | struct ofpbuf *ofpacts) | |
351 | { | |
4c99cb18 | 352 | emit_resubmit(ofpacts, first_ptable(ep, next->pipeline) + next->ltable); |
5b84185b BP |
353 | } |
354 | ||
d5a76da4 | 355 | static void |
80b6743d | 356 | ovnact_next_free(struct ovnact_next *a OVS_UNUSED) |
0bac7164 | 357 | { |
d5a76da4 BP |
358 | } |
359 | \f | |
360 | static void | |
361 | parse_LOAD(struct action_context *ctx, const struct expr_field *lhs) | |
362 | { | |
363 | size_t ofs = ctx->ovnacts->size; | |
364 | struct ovnact_load *load = ovnact_put_LOAD(ctx->ovnacts); | |
365 | load->dst = *lhs; | |
9aef3c1b | 366 | |
d5a76da4 | 367 | char *error = expr_type_check(lhs, lhs->n_bits, true); |
d5a76da4 BP |
368 | if (error) { |
369 | ctx->ovnacts->size = ofs; | |
9aef3c1b | 370 | lexer_error(ctx->lexer, "%s", error); |
d5a76da4 | 371 | free(error); |
9aef3c1b BP |
372 | return; |
373 | } | |
374 | if (!expr_constant_parse(ctx->lexer, lhs, &load->imm)) { | |
375 | ctx->ovnacts->size = ofs; | |
376 | return; | |
d5a76da4 BP |
377 | } |
378 | } | |
0bac7164 | 379 | |
d5a76da4 BP |
380 | static enum expr_constant_type |
381 | load_type(const struct ovnact_load *load) | |
382 | { | |
383 | return load->dst.symbol->width > 0 ? EXPR_C_INTEGER : EXPR_C_STRING; | |
384 | } | |
0bac7164 | 385 | |
d5a76da4 BP |
386 | static void |
387 | format_LOAD(const struct ovnact_load *load, struct ds *s) | |
388 | { | |
389 | expr_field_format(&load->dst, s); | |
390 | ds_put_cstr(s, " = "); | |
391 | expr_constant_format(&load->imm, load_type(load), s); | |
392 | ds_put_char(s, ';'); | |
393 | } | |
0bac7164 | 394 | |
128684a6 JR |
395 | static void |
396 | encode_LOAD(const struct ovnact_load *load, | |
397 | const struct ovnact_encode_params *ep, | |
398 | struct ofpbuf *ofpacts) | |
d5a76da4 BP |
399 | { |
400 | const union expr_constant *c = &load->imm; | |
401 | struct mf_subfield dst = expr_resolve_field(&load->dst); | |
128684a6 JR |
402 | struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts, dst.field, |
403 | NULL, NULL); | |
d5a76da4 BP |
404 | |
405 | if (load->dst.symbol->width) { | |
406 | bitwise_copy(&c->value, sizeof c->value, 0, | |
128684a6 | 407 | sf->value, dst.field->n_bytes, dst.ofs, |
d5a76da4 BP |
408 | dst.n_bits); |
409 | if (c->masked) { | |
410 | bitwise_copy(&c->mask, sizeof c->mask, 0, | |
128684a6 JR |
411 | ofpact_set_field_mask(sf), dst.field->n_bytes, |
412 | dst.ofs, dst.n_bits); | |
d5a76da4 | 413 | } else { |
128684a6 | 414 | bitwise_one(ofpact_set_field_mask(sf), dst.field->n_bytes, |
d5a76da4 BP |
415 | dst.ofs, dst.n_bits); |
416 | } | |
417 | } else { | |
418 | uint32_t port; | |
128684a6 | 419 | if (!ep->lookup_port(ep->aux, load->imm.string, &port)) { |
d5a76da4 BP |
420 | port = 0; |
421 | } | |
128684a6 | 422 | bitwise_put(port, sf->value, |
d5a76da4 | 423 | sf->field->n_bytes, 0, sf->field->n_bits); |
128684a6 JR |
424 | bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, 0, |
425 | sf->field->n_bits); | |
d5a76da4 | 426 | } |
0bac7164 BP |
427 | } |
428 | ||
429 | static void | |
80b6743d | 430 | ovnact_load_free(struct ovnact_load *load) |
0bac7164 | 431 | { |
d5a76da4 BP |
432 | expr_constant_destroy(&load->imm, load_type(load)); |
433 | } | |
434 | \f | |
435 | static void | |
436 | format_assignment(const struct ovnact_move *move, const char *operator, | |
437 | struct ds *s) | |
438 | { | |
439 | expr_field_format(&move->lhs, s); | |
440 | ds_put_format(s, " %s ", operator); | |
441 | expr_field_format(&move->rhs, s); | |
442 | ds_put_char(s, ';'); | |
0bac7164 BP |
443 | } |
444 | ||
445 | static void | |
d5a76da4 | 446 | format_MOVE(const struct ovnact_move *move, struct ds *s) |
0bac7164 | 447 | { |
d5a76da4 | 448 | format_assignment(move, "=", s); |
0bac7164 BP |
449 | } |
450 | ||
6335d074 | 451 | static void |
d5a76da4 BP |
452 | format_EXCHANGE(const struct ovnact_move *move, struct ds *s) |
453 | { | |
454 | format_assignment(move, "<->", s); | |
455 | } | |
456 | ||
457 | static void | |
458 | parse_assignment_action(struct action_context *ctx, bool exchange, | |
459 | const struct expr_field *lhs) | |
6335d074 | 460 | { |
d5a76da4 | 461 | struct expr_field rhs; |
9aef3c1b | 462 | if (!expr_field_parse(ctx->lexer, ctx->pp->symtab, &rhs, &ctx->prereqs)) { |
6335d074 BP |
463 | return; |
464 | } | |
465 | ||
d5a76da4 BP |
466 | const struct expr_symbol *ls = lhs->symbol; |
467 | const struct expr_symbol *rs = rhs.symbol; | |
468 | if ((ls->width != 0) != (rs->width != 0)) { | |
469 | if (exchange) { | |
9aef3c1b BP |
470 | lexer_error(ctx->lexer, |
471 | "Can't exchange %s field (%s) with %s field (%s).", | |
472 | ls->width ? "integer" : "string", | |
473 | ls->name, | |
474 | rs->width ? "integer" : "string", | |
475 | rs->name); | |
d5a76da4 | 476 | } else { |
9aef3c1b BP |
477 | lexer_error(ctx->lexer, |
478 | "Can't assign %s field (%s) to %s field (%s).", | |
479 | rs->width ? "integer" : "string", | |
480 | rs->name, | |
481 | ls->width ? "integer" : "string", | |
482 | ls->name); | |
d5a76da4 BP |
483 | } |
484 | return; | |
485 | } | |
6335d074 | 486 | |
d5a76da4 BP |
487 | if (lhs->n_bits != rhs.n_bits) { |
488 | if (exchange) { | |
9aef3c1b BP |
489 | lexer_error(ctx->lexer, |
490 | "Can't exchange %d-bit field with %d-bit field.", | |
491 | lhs->n_bits, rhs.n_bits); | |
d5a76da4 | 492 | } else { |
9aef3c1b BP |
493 | lexer_error(ctx->lexer, |
494 | "Can't assign %d-bit value to %d-bit destination.", | |
495 | rhs.n_bits, lhs->n_bits); | |
6335d074 | 496 | } |
d5a76da4 BP |
497 | return; |
498 | } else if (!lhs->n_bits && | |
499 | ls->field->n_bits != rs->field->n_bits) { | |
9aef3c1b BP |
500 | lexer_error(ctx->lexer, "String fields %s and %s are incompatible for " |
501 | "%s.", ls->name, rs->name, | |
502 | exchange ? "exchange" : "assignment"); | |
d5a76da4 | 503 | return; |
6335d074 BP |
504 | } |
505 | ||
9aef3c1b | 506 | char *error = expr_type_check(lhs, lhs->n_bits, true); |
d5a76da4 BP |
507 | if (!error) { |
508 | error = expr_type_check(&rhs, rhs.n_bits, true); | |
509 | } | |
510 | if (error) { | |
9aef3c1b | 511 | lexer_error(ctx->lexer, "%s", error); |
d5a76da4 BP |
512 | free(error); |
513 | return; | |
514 | } | |
6335d074 | 515 | |
d5a76da4 BP |
516 | struct ovnact_move *move; |
517 | move = (exchange | |
518 | ? ovnact_put_EXCHANGE(ctx->ovnacts) | |
519 | : ovnact_put_MOVE(ctx->ovnacts)); | |
520 | move->lhs = *lhs; | |
521 | move->rhs = rhs; | |
522 | } | |
6335d074 | 523 | |
d5a76da4 BP |
524 | static void |
525 | encode_MOVE(const struct ovnact_move *move, | |
526 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
527 | struct ofpbuf *ofpacts) | |
528 | { | |
529 | struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts); | |
530 | orm->src = expr_resolve_field(&move->rhs); | |
531 | orm->dst = expr_resolve_field(&move->lhs); | |
6335d074 BP |
532 | } |
533 | ||
d5a76da4 BP |
534 | static void |
535 | encode_EXCHANGE(const struct ovnact_move *xchg, | |
536 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
537 | struct ofpbuf *ofpacts) | |
0bac7164 | 538 | { |
d5a76da4 BP |
539 | ofpact_put_STACK_PUSH(ofpacts)->subfield = expr_resolve_field(&xchg->rhs); |
540 | ofpact_put_STACK_PUSH(ofpacts)->subfield = expr_resolve_field(&xchg->lhs); | |
541 | ofpact_put_STACK_POP(ofpacts)->subfield = expr_resolve_field(&xchg->rhs); | |
542 | ofpact_put_STACK_POP(ofpacts)->subfield = expr_resolve_field(&xchg->lhs); | |
543 | } | |
0bac7164 | 544 | |
d5a76da4 | 545 | static void |
80b6743d | 546 | ovnact_move_free(struct ovnact_move *move OVS_UNUSED) |
d5a76da4 BP |
547 | { |
548 | } | |
549 | \f | |
550 | static void | |
551 | parse_DEC_TTL(struct action_context *ctx) | |
552 | { | |
9aef3c1b | 553 | lexer_force_match(ctx->lexer, LEX_T_DECREMENT); |
d5a76da4 BP |
554 | ovnact_put_DEC_TTL(ctx->ovnacts); |
555 | add_prerequisite(ctx, "ip"); | |
556 | } | |
0bac7164 | 557 | |
d5a76da4 BP |
558 | static void |
559 | format_DEC_TTL(const struct ovnact_null *null OVS_UNUSED, struct ds *s) | |
560 | { | |
561 | ds_put_cstr(s, "ip.ttl--;"); | |
0bac7164 BP |
562 | } |
563 | ||
d5a76da4 BP |
564 | static void |
565 | encode_DEC_TTL(const struct ovnact_null *null OVS_UNUSED, | |
566 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
567 | struct ofpbuf *ofpacts) | |
0bac7164 | 568 | { |
d5a76da4 BP |
569 | ofpact_put_DEC_TTL(ofpacts); |
570 | } | |
d5a76da4 BP |
571 | \f |
572 | static void | |
573 | parse_CT_NEXT(struct action_context *ctx) | |
574 | { | |
575 | if (ctx->pp->cur_ltable >= ctx->pp->n_tables) { | |
9aef3c1b BP |
576 | lexer_error(ctx->lexer, |
577 | "\"ct_next\" action not allowed in last table."); | |
d5a76da4 | 578 | return; |
0bac7164 BP |
579 | } |
580 | ||
d5a76da4 BP |
581 | add_prerequisite(ctx, "ip"); |
582 | ovnact_put_CT_NEXT(ctx->ovnacts)->ltable = ctx->pp->cur_ltable + 1; | |
0bac7164 BP |
583 | } |
584 | ||
585 | static void | |
ebb467ff | 586 | format_CT_NEXT(const struct ovnact_ct_next *ct_next OVS_UNUSED, struct ds *s) |
0bac7164 | 587 | { |
d5a76da4 | 588 | ds_put_cstr(s, "ct_next;"); |
0bac7164 BP |
589 | } |
590 | ||
d5a76da4 | 591 | static void |
ebb467ff | 592 | encode_CT_NEXT(const struct ovnact_ct_next *ct_next, |
d5a76da4 BP |
593 | const struct ovnact_encode_params *ep, |
594 | struct ofpbuf *ofpacts) | |
595 | { | |
596 | struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts); | |
4c99cb18 | 597 | ct->recirc_table = first_ptable(ep, ep->pipeline) + ct_next->ltable; |
d7039b9a GS |
598 | ct->zone_src.field = ep->is_switch ? mf_from_id(MFF_LOG_CT_ZONE) |
599 | : mf_from_id(MFF_LOG_DNAT_ZONE); | |
d5a76da4 BP |
600 | ct->zone_src.ofs = 0; |
601 | ct->zone_src.n_bits = 16; | |
602 | ofpact_finish(ofpacts, &ct->ofpact); | |
603 | } | |
ebb467ff BP |
604 | |
605 | static void | |
606 | ovnact_ct_next_free(struct ovnact_ct_next *a OVS_UNUSED) | |
607 | { | |
608 | } | |
d5a76da4 BP |
609 | \f |
610 | static void | |
611 | parse_ct_commit_arg(struct action_context *ctx, | |
612 | struct ovnact_ct_commit *cc) | |
613 | { | |
614 | if (lexer_match_id(ctx->lexer, "ct_mark")) { | |
9aef3c1b | 615 | if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { |
d5a76da4 | 616 | return; |
0bac7164 | 617 | } |
d5a76da4 BP |
618 | if (ctx->lexer->token.type == LEX_T_INTEGER) { |
619 | cc->ct_mark = ntohll(ctx->lexer->token.value.integer); | |
620 | cc->ct_mark_mask = UINT32_MAX; | |
621 | } else if (ctx->lexer->token.type == LEX_T_MASKED_INTEGER) { | |
622 | cc->ct_mark = ntohll(ctx->lexer->token.value.integer); | |
623 | cc->ct_mark_mask = ntohll(ctx->lexer->token.mask.integer); | |
624 | } else { | |
9aef3c1b | 625 | lexer_syntax_error(ctx->lexer, "expecting integer"); |
d5a76da4 | 626 | return; |
0bac7164 | 627 | } |
d5a76da4 BP |
628 | lexer_get(ctx->lexer); |
629 | } else if (lexer_match_id(ctx->lexer, "ct_label")) { | |
9aef3c1b | 630 | if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { |
d5a76da4 | 631 | return; |
0bac7164 | 632 | } |
d5a76da4 BP |
633 | if (ctx->lexer->token.type == LEX_T_INTEGER) { |
634 | cc->ct_label = ctx->lexer->token.value.be128_int; | |
635 | cc->ct_label_mask = OVS_BE128_MAX; | |
636 | } else if (ctx->lexer->token.type == LEX_T_MASKED_INTEGER) { | |
637 | cc->ct_label = ctx->lexer->token.value.be128_int; | |
638 | cc->ct_label_mask = ctx->lexer->token.mask.be128_int; | |
639 | } else { | |
9aef3c1b | 640 | lexer_syntax_error(ctx->lexer, "expecting integer"); |
d5a76da4 BP |
641 | return; |
642 | } | |
643 | lexer_get(ctx->lexer); | |
644 | } else { | |
9aef3c1b | 645 | lexer_syntax_error(ctx->lexer, NULL); |
0bac7164 BP |
646 | } |
647 | } | |
648 | ||
649 | static void | |
d5a76da4 | 650 | parse_CT_COMMIT(struct action_context *ctx) |
0bac7164 | 651 | { |
d5a76da4 BP |
652 | add_prerequisite(ctx, "ip"); |
653 | ||
654 | struct ovnact_ct_commit *ct_commit = ovnact_put_CT_COMMIT(ctx->ovnacts); | |
655 | if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { | |
656 | while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { | |
657 | parse_ct_commit_arg(ctx, ct_commit); | |
9aef3c1b | 658 | if (ctx->lexer->error) { |
d5a76da4 BP |
659 | return; |
660 | } | |
661 | lexer_match(ctx->lexer, LEX_T_COMMA); | |
0bac7164 BP |
662 | } |
663 | } | |
664 | } | |
665 | ||
666 | static void | |
d5a76da4 | 667 | format_CT_COMMIT(const struct ovnact_ct_commit *cc, struct ds *s) |
0bac7164 | 668 | { |
d5a76da4 BP |
669 | ds_put_cstr(s, "ct_commit("); |
670 | if (cc->ct_mark_mask) { | |
671 | ds_put_format(s, "ct_mark=%#"PRIx32, cc->ct_mark); | |
672 | if (cc->ct_mark_mask != UINT32_MAX) { | |
673 | ds_put_format(s, "/%#"PRIx32, cc->ct_mark_mask); | |
674 | } | |
675 | } | |
676 | if (!ovs_be128_is_zero(cc->ct_label_mask)) { | |
677 | if (ds_last(s) != '(') { | |
678 | ds_put_cstr(s, ", "); | |
679 | } | |
0bac7164 | 680 | |
d5a76da4 BP |
681 | ds_put_format(s, "ct_label="); |
682 | ds_put_hex(s, &cc->ct_label, sizeof cc->ct_label); | |
683 | if (!ovs_be128_equals(cc->ct_label_mask, OVS_BE128_MAX)) { | |
684 | ds_put_char(s, '/'); | |
685 | ds_put_hex(s, &cc->ct_label_mask, sizeof cc->ct_label_mask); | |
686 | } | |
687 | } | |
688 | if (!ds_chomp(s, '(')) { | |
689 | ds_put_char(s, ')'); | |
690 | } | |
691 | ds_put_char(s, ';'); | |
0bac7164 BP |
692 | } |
693 | ||
694 | static void | |
d5a76da4 BP |
695 | encode_CT_COMMIT(const struct ovnact_ct_commit *cc, |
696 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
697 | struct ofpbuf *ofpacts) | |
0bac7164 | 698 | { |
d5a76da4 BP |
699 | struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts); |
700 | ct->flags = NX_CT_F_COMMIT; | |
701 | ct->recirc_table = NX_CT_RECIRC_NONE; | |
702 | ct->zone_src.field = mf_from_id(MFF_LOG_CT_ZONE); | |
703 | ct->zone_src.ofs = 0; | |
704 | ct->zone_src.n_bits = 16; | |
0bac7164 | 705 | |
d5a76da4 BP |
706 | size_t set_field_offset = ofpacts->size; |
707 | ofpbuf_pull(ofpacts, set_field_offset); | |
0bac7164 | 708 | |
d5a76da4 | 709 | if (cc->ct_mark_mask) { |
128684a6 JR |
710 | const ovs_be32 value = htonl(cc->ct_mark); |
711 | const ovs_be32 mask = htonl(cc->ct_mark_mask); | |
712 | ofpact_put_set_field(ofpacts, mf_from_id(MFF_CT_MARK), &value, &mask); | |
d5a76da4 | 713 | } |
0bac7164 | 714 | |
d5a76da4 | 715 | if (!ovs_be128_is_zero(cc->ct_label_mask)) { |
128684a6 JR |
716 | ofpact_put_set_field(ofpacts, mf_from_id(MFF_CT_LABEL), &cc->ct_label, |
717 | &cc->ct_label_mask); | |
d5a76da4 | 718 | } |
0bac7164 | 719 | |
d5a76da4 BP |
720 | ofpacts->header = ofpbuf_push_uninit(ofpacts, set_field_offset); |
721 | ct = ofpacts->header; | |
722 | ofpact_finish(ofpacts, &ct->ofpact); | |
0bac7164 BP |
723 | } |
724 | ||
725 | static void | |
80b6743d | 726 | ovnact_ct_commit_free(struct ovnact_ct_commit *cc OVS_UNUSED) |
0bac7164 | 727 | { |
0bac7164 | 728 | } |
d5a76da4 | 729 | \f |
42814145 | 730 | static void |
d5a76da4 BP |
731 | parse_ct_nat(struct action_context *ctx, const char *name, |
732 | struct ovnact_ct_nat *cn) | |
42814145 | 733 | { |
d5a76da4 | 734 | add_prerequisite(ctx, "ip"); |
42814145 | 735 | |
d5a76da4 | 736 | if (ctx->pp->cur_ltable >= ctx->pp->n_tables) { |
9aef3c1b BP |
737 | lexer_error(ctx->lexer, |
738 | "\"%s\" action not allowed in last table.", name); | |
42814145 NS |
739 | return; |
740 | } | |
d5a76da4 | 741 | cn->ltable = ctx->pp->cur_ltable + 1; |
42814145 | 742 | |
d5a76da4 BP |
743 | if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { |
744 | if (ctx->lexer->token.type != LEX_T_INTEGER | |
745 | || ctx->lexer->token.format != LEX_F_IPV4) { | |
9aef3c1b | 746 | lexer_syntax_error(ctx->lexer, "expecting IPv4 address"); |
42814145 NS |
747 | return; |
748 | } | |
d5a76da4 BP |
749 | cn->ip = ctx->lexer->token.value.ipv4; |
750 | lexer_get(ctx->lexer); | |
751 | ||
9aef3c1b | 752 | if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) { |
42814145 NS |
753 | return; |
754 | } | |
755 | } | |
d5a76da4 | 756 | } |
42814145 | 757 | |
d5a76da4 BP |
758 | static void |
759 | parse_CT_DNAT(struct action_context *ctx) | |
760 | { | |
761 | parse_ct_nat(ctx, "ct_dnat", ovnact_put_CT_DNAT(ctx->ovnacts)); | |
762 | } | |
42814145 | 763 | |
d5a76da4 BP |
764 | static void |
765 | parse_CT_SNAT(struct action_context *ctx) | |
766 | { | |
767 | parse_ct_nat(ctx, "ct_snat", ovnact_put_CT_SNAT(ctx->ovnacts)); | |
768 | } | |
42814145 | 769 | |
d5a76da4 BP |
770 | static void |
771 | format_ct_nat(const struct ovnact_ct_nat *cn, const char *name, struct ds *s) | |
772 | { | |
773 | ds_put_cstr(s, name); | |
774 | if (cn->ip) { | |
775 | ds_put_format(s, "("IP_FMT")", IP_ARGS(cn->ip)); | |
42814145 | 776 | } |
d5a76da4 BP |
777 | ds_put_char(s, ';'); |
778 | } | |
42814145 | 779 | |
d5a76da4 BP |
780 | static void |
781 | format_CT_DNAT(const struct ovnact_ct_nat *cn, struct ds *s) | |
782 | { | |
783 | format_ct_nat(cn, "ct_dnat", s); | |
784 | } | |
42814145 | 785 | |
d5a76da4 BP |
786 | static void |
787 | format_CT_SNAT(const struct ovnact_ct_nat *cn, struct ds *s) | |
788 | { | |
789 | format_ct_nat(cn, "ct_snat", s); | |
790 | } | |
42814145 | 791 | |
d5a76da4 BP |
792 | static void |
793 | encode_ct_nat(const struct ovnact_ct_nat *cn, | |
794 | const struct ovnact_encode_params *ep, | |
795 | bool snat, struct ofpbuf *ofpacts) | |
796 | { | |
797 | const size_t ct_offset = ofpacts->size; | |
798 | ofpbuf_pull(ofpacts, ct_offset); | |
42814145 | 799 | |
d5a76da4 | 800 | struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts); |
4c99cb18 | 801 | ct->recirc_table = cn->ltable + first_ptable(ep, ep->pipeline); |
d5a76da4 BP |
802 | if (snat) { |
803 | ct->zone_src.field = mf_from_id(MFF_LOG_SNAT_ZONE); | |
804 | } else { | |
805 | ct->zone_src.field = mf_from_id(MFF_LOG_DNAT_ZONE); | |
806 | } | |
807 | ct->zone_src.ofs = 0; | |
808 | ct->zone_src.n_bits = 16; | |
809 | ct->flags = 0; | |
810 | ct->alg = 0; | |
42814145 | 811 | |
d5a76da4 BP |
812 | struct ofpact_nat *nat; |
813 | size_t nat_offset; | |
814 | nat_offset = ofpacts->size; | |
815 | ofpbuf_pull(ofpacts, nat_offset); | |
816 | ||
817 | nat = ofpact_put_NAT(ofpacts); | |
818 | nat->flags = 0; | |
819 | nat->range_af = AF_UNSPEC; | |
820 | ||
821 | if (cn->ip) { | |
822 | nat->range_af = AF_INET; | |
823 | nat->range.addr.ipv4.min = cn->ip; | |
824 | if (snat) { | |
825 | nat->flags |= NX_NAT_F_SRC; | |
826 | } else { | |
827 | nat->flags |= NX_NAT_F_DST; | |
42814145 | 828 | } |
42814145 NS |
829 | } |
830 | ||
d5a76da4 BP |
831 | ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset); |
832 | ct = ofpacts->header; | |
833 | if (cn->ip) { | |
834 | ct->flags |= NX_CT_F_COMMIT; | |
1b441300 MS |
835 | } else if (snat && ep->is_gateway_router) { |
836 | /* For performance reasons, we try to prevent additional | |
837 | * recirculations. ct_snat which is used in a gateway router | |
838 | * does not need a recirculation. ct_snat(IP) does need a | |
839 | * recirculation. ct_snat in a distributed router needs | |
840 | * recirculation regardless of whether an IP address is | |
841 | * specified. | |
842 | * XXX Should we consider a method to let the actions specify | |
843 | * whether an action needs recirculation if there are more use | |
d5a76da4 BP |
844 | * cases?. */ |
845 | ct->recirc_table = NX_CT_RECIRC_NONE; | |
846 | } | |
847 | ofpact_finish(ofpacts, &ct->ofpact); | |
848 | ofpbuf_push_uninit(ofpacts, ct_offset); | |
42814145 NS |
849 | } |
850 | ||
42814145 | 851 | static void |
d5a76da4 BP |
852 | encode_CT_DNAT(const struct ovnact_ct_nat *cn, |
853 | const struct ovnact_encode_params *ep, | |
854 | struct ofpbuf *ofpacts) | |
42814145 | 855 | { |
d5a76da4 | 856 | encode_ct_nat(cn, ep, false, ofpacts); |
42814145 NS |
857 | } |
858 | ||
01cfdb2f | 859 | static void |
d5a76da4 BP |
860 | encode_CT_SNAT(const struct ovnact_ct_nat *cn, |
861 | const struct ovnact_encode_params *ep, | |
862 | struct ofpbuf *ofpacts) | |
01cfdb2f | 863 | { |
d5a76da4 BP |
864 | encode_ct_nat(cn, ep, true, ofpacts); |
865 | } | |
01cfdb2f | 866 | |
d5a76da4 | 867 | static void |
80b6743d | 868 | ovnact_ct_nat_free(struct ovnact_ct_nat *ct_nat OVS_UNUSED) |
d5a76da4 BP |
869 | { |
870 | } | |
871 | \f | |
872 | static void | |
873 | parse_ct_lb_action(struct action_context *ctx) | |
874 | { | |
875 | if (ctx->pp->cur_ltable >= ctx->pp->n_tables) { | |
9aef3c1b | 876 | lexer_error(ctx->lexer, "\"ct_lb\" action not allowed in last table."); |
01cfdb2f NS |
877 | return; |
878 | } | |
879 | ||
d5a76da4 | 880 | add_prerequisite(ctx, "ip"); |
01cfdb2f | 881 | |
d5a76da4 BP |
882 | struct ovnact_ct_lb_dst *dsts = NULL; |
883 | size_t allocated_dsts = 0; | |
884 | size_t n_dsts = 0; | |
01cfdb2f | 885 | |
d5a76da4 BP |
886 | if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { |
887 | while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { | |
9d236afa MM |
888 | struct ovnact_ct_lb_dst dst; |
889 | if (lexer_match(ctx->lexer, LEX_T_LSQUARE)) { | |
890 | /* IPv6 address and port */ | |
891 | if (ctx->lexer->token.type != LEX_T_INTEGER | |
892 | || ctx->lexer->token.format != LEX_F_IPV6) { | |
893 | free(dsts); | |
894 | lexer_syntax_error(ctx->lexer, "expecting IPv6 address"); | |
895 | return; | |
896 | } | |
897 | dst.family = AF_INET6; | |
898 | dst.ipv6 = ctx->lexer->token.value.ipv6; | |
899 | ||
900 | lexer_get(ctx->lexer); | |
901 | if (!lexer_match(ctx->lexer, LEX_T_RSQUARE)) { | |
902 | free(dsts); | |
903 | lexer_syntax_error(ctx->lexer, "no closing square " | |
904 | "bracket"); | |
905 | return; | |
906 | } | |
907 | dst.port = 0; | |
908 | if (lexer_match(ctx->lexer, LEX_T_COLON) | |
909 | && !action_parse_port(ctx, &dst.port)) { | |
910 | free(dsts); | |
911 | return; | |
912 | } | |
913 | } else { | |
914 | if (ctx->lexer->token.type != LEX_T_INTEGER | |
915 | || (ctx->lexer->token.format != LEX_F_IPV4 | |
916 | && ctx->lexer->token.format != LEX_F_IPV6)) { | |
917 | free(dsts); | |
918 | lexer_syntax_error(ctx->lexer, "expecting IP address"); | |
919 | return; | |
920 | } | |
01cfdb2f | 921 | |
9d236afa MM |
922 | /* Parse IP. */ |
923 | if (ctx->lexer->token.format == LEX_F_IPV4) { | |
924 | dst.family = AF_INET; | |
925 | dst.ipv4 = ctx->lexer->token.value.ipv4; | |
926 | } else { | |
927 | dst.family = AF_INET6; | |
928 | dst.ipv6 = ctx->lexer->token.value.ipv6; | |
929 | } | |
01cfdb2f | 930 | |
9d236afa MM |
931 | lexer_get(ctx->lexer); |
932 | dst.port = 0; | |
933 | if (lexer_match(ctx->lexer, LEX_T_COLON)) { | |
934 | if (dst.family == AF_INET6) { | |
935 | free(dsts); | |
936 | lexer_syntax_error(ctx->lexer, "IPv6 address needs " | |
937 | "square brackets if port is included"); | |
938 | return; | |
939 | } else if (!action_parse_port(ctx, &dst.port)) { | |
940 | free(dsts); | |
941 | return; | |
942 | } | |
943 | } | |
d5a76da4 BP |
944 | } |
945 | lexer_match(ctx->lexer, LEX_T_COMMA); | |
01cfdb2f | 946 | |
d5a76da4 BP |
947 | /* Append to dsts. */ |
948 | if (n_dsts >= allocated_dsts) { | |
949 | dsts = x2nrealloc(dsts, &allocated_dsts, sizeof *dsts); | |
950 | } | |
9d236afa | 951 | dsts[n_dsts++] = dst; |
01cfdb2f | 952 | } |
01cfdb2f NS |
953 | } |
954 | ||
d5a76da4 BP |
955 | struct ovnact_ct_lb *cl = ovnact_put_CT_LB(ctx->ovnacts); |
956 | cl->ltable = ctx->pp->cur_ltable + 1; | |
957 | cl->dsts = dsts; | |
958 | cl->n_dsts = n_dsts; | |
01cfdb2f NS |
959 | } |
960 | ||
01cfdb2f | 961 | static void |
d5a76da4 | 962 | format_CT_LB(const struct ovnact_ct_lb *cl, struct ds *s) |
01cfdb2f | 963 | { |
d5a76da4 BP |
964 | ds_put_cstr(s, "ct_lb"); |
965 | if (cl->n_dsts) { | |
966 | ds_put_char(s, '('); | |
967 | for (size_t i = 0; i < cl->n_dsts; i++) { | |
968 | if (i) { | |
969 | ds_put_cstr(s, ", "); | |
970 | } | |
01cfdb2f | 971 | |
d5a76da4 | 972 | const struct ovnact_ct_lb_dst *dst = &cl->dsts[i]; |
9d236afa MM |
973 | if (dst->family == AF_INET) { |
974 | ds_put_format(s, IP_FMT, IP_ARGS(dst->ipv4)); | |
975 | if (dst->port) { | |
976 | ds_put_format(s, ":%"PRIu16, dst->port); | |
977 | } | |
978 | } else { | |
979 | if (dst->port) { | |
980 | ds_put_char(s, '['); | |
981 | } | |
982 | ipv6_format_addr(&dst->ipv6, s); | |
983 | if (dst->port) { | |
984 | ds_put_format(s, "]:%"PRIu16, dst->port); | |
985 | } | |
d5a76da4 | 986 | } |
467085fd | 987 | } |
d5a76da4 | 988 | ds_put_char(s, ')'); |
467085fd | 989 | } |
d5a76da4 | 990 | ds_put_char(s, ';'); |
467085fd GS |
991 | } |
992 | ||
993 | static void | |
d5a76da4 BP |
994 | encode_CT_LB(const struct ovnact_ct_lb *cl, |
995 | const struct ovnact_encode_params *ep, | |
996 | struct ofpbuf *ofpacts) | |
467085fd | 997 | { |
4c99cb18 | 998 | uint8_t recirc_table = cl->ltable + first_ptable(ep, ep->pipeline); |
d5a76da4 BP |
999 | if (!cl->n_dsts) { |
1000 | /* ct_lb without any destinations means that this is an established | |
467085fd | 1001 | * connection and we just need to do a NAT. */ |
d5a76da4 BP |
1002 | const size_t ct_offset = ofpacts->size; |
1003 | ofpbuf_pull(ofpacts, ct_offset); | |
467085fd | 1004 | |
d5a76da4 | 1005 | struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts); |
467085fd GS |
1006 | struct ofpact_nat *nat; |
1007 | size_t nat_offset; | |
c2e954a1 GS |
1008 | ct->zone_src.field = ep->is_switch ? mf_from_id(MFF_LOG_CT_ZONE) |
1009 | : mf_from_id(MFF_LOG_DNAT_ZONE); | |
467085fd GS |
1010 | ct->zone_src.ofs = 0; |
1011 | ct->zone_src.n_bits = 16; | |
1012 | ct->flags = 0; | |
1013 | ct->recirc_table = recirc_table; | |
1014 | ct->alg = 0; | |
1015 | ||
d5a76da4 BP |
1016 | nat_offset = ofpacts->size; |
1017 | ofpbuf_pull(ofpacts, nat_offset); | |
467085fd | 1018 | |
d5a76da4 | 1019 | nat = ofpact_put_NAT(ofpacts); |
467085fd GS |
1020 | nat->flags = 0; |
1021 | nat->range_af = AF_UNSPEC; | |
1022 | ||
d5a76da4 BP |
1023 | ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset); |
1024 | ct = ofpacts->header; | |
1025 | ofpact_finish(ofpacts, &ct->ofpact); | |
1026 | ofpbuf_push_uninit(ofpacts, ct_offset); | |
467085fd GS |
1027 | return; |
1028 | } | |
1029 | ||
ad35c0c5 | 1030 | uint32_t table_id = 0; |
467085fd | 1031 | struct ofpact_group *og; |
c2e954a1 GS |
1032 | uint32_t zone_reg = ep->is_switch ? MFF_LOG_CT_ZONE - MFF_REG0 |
1033 | : MFF_LOG_DNAT_ZONE - MFF_REG0; | |
467085fd GS |
1034 | |
1035 | struct ds ds = DS_EMPTY_INITIALIZER; | |
eacd48ec | 1036 | ds_put_format(&ds, "type=select,selection_method=dp_hash"); |
467085fd GS |
1037 | |
1038 | BUILD_ASSERT(MFF_LOG_CT_ZONE >= MFF_REG0); | |
1039 | BUILD_ASSERT(MFF_LOG_CT_ZONE < MFF_REG0 + FLOW_N_REGS); | |
c2e954a1 GS |
1040 | BUILD_ASSERT(MFF_LOG_DNAT_ZONE >= MFF_REG0); |
1041 | BUILD_ASSERT(MFF_LOG_DNAT_ZONE < MFF_REG0 + FLOW_N_REGS); | |
d5a76da4 BP |
1042 | for (size_t bucket_id = 0; bucket_id < cl->n_dsts; bucket_id++) { |
1043 | const struct ovnact_ct_lb_dst *dst = &cl->dsts[bucket_id]; | |
9d236afa MM |
1044 | char ip_addr[INET6_ADDRSTRLEN]; |
1045 | if (dst->family == AF_INET) { | |
1046 | inet_ntop(AF_INET, &dst->ipv4, ip_addr, sizeof ip_addr); | |
1047 | } else { | |
1048 | inet_ntop(AF_INET6, &dst->ipv6, ip_addr, sizeof ip_addr); | |
1049 | } | |
d5a76da4 | 1050 | ds_put_format(&ds, ",bucket=bucket_id=%"PRIuSIZE",weight:100,actions=" |
9d236afa MM |
1051 | "ct(nat(dst=%s%s%s", bucket_id, |
1052 | dst->family == AF_INET6 && dst->port ? "[" : "", | |
1053 | ip_addr, | |
1054 | dst->family == AF_INET6 && dst->port ? "]" : ""); | |
d5a76da4 BP |
1055 | if (dst->port) { |
1056 | ds_put_format(&ds, ":%"PRIu16, dst->port); | |
467085fd GS |
1057 | } |
1058 | ds_put_format(&ds, "),commit,table=%d,zone=NXM_NX_REG%d[0..15])", | |
c2e954a1 | 1059 | recirc_table, zone_reg); |
d5a76da4 | 1060 | } |
467085fd | 1061 | |
ad35c0c5 GL |
1062 | table_id = ovn_extend_table_assign_id(ep->group_table, &ds); |
1063 | ds_destroy(&ds); | |
1064 | if (table_id == EXT_TABLE_ID_INVALID) { | |
1065 | return; | |
467085fd GS |
1066 | } |
1067 | ||
d5a76da4 BP |
1068 | /* Create an action to set the group. */ |
1069 | og = ofpact_put_GROUP(ofpacts); | |
ad35c0c5 | 1070 | og->group_id = table_id; |
d5a76da4 BP |
1071 | } |
1072 | ||
1073 | static void | |
80b6743d | 1074 | ovnact_ct_lb_free(struct ovnact_ct_lb *ct_lb) |
d5a76da4 BP |
1075 | { |
1076 | free(ct_lb->dsts); | |
1077 | } | |
1078 | \f | |
db0e819b BP |
1079 | static void |
1080 | format_CT_CLEAR(const struct ovnact_null *null OVS_UNUSED, struct ds *s) | |
1081 | { | |
1082 | ds_put_cstr(s, "ct_clear;"); | |
1083 | } | |
1084 | ||
1085 | static void | |
1086 | encode_CT_CLEAR(const struct ovnact_null *null OVS_UNUSED, | |
1087 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
1088 | struct ofpbuf *ofpacts) | |
1089 | { | |
1090 | ofpact_put_CT_CLEAR(ofpacts); | |
1091 | } | |
1092 | \f | |
b3bd2c33 BP |
1093 | /* Implements the "arp", "nd_na", and "clone" actions, which execute nested |
1094 | * actions on a packet derived from the one being processed. */ | |
d5a76da4 BP |
1095 | static void |
1096 | parse_nested_action(struct action_context *ctx, enum ovnact_type type, | |
1097 | const char *prereq) | |
1098 | { | |
9aef3c1b | 1099 | if (!lexer_force_match(ctx->lexer, LEX_T_LCURLY)) { |
d5a76da4 BP |
1100 | return; |
1101 | } | |
1102 | ||
1103 | uint64_t stub[1024 / 8]; | |
1104 | struct ofpbuf nested = OFPBUF_STUB_INITIALIZER(stub); | |
1105 | ||
1106 | struct action_context inner_ctx = { | |
1107 | .pp = ctx->pp, | |
1108 | .lexer = ctx->lexer, | |
d5a76da4 | 1109 | .ovnacts = &nested, |
b3bd2c33 | 1110 | .prereqs = NULL, |
d5a76da4 | 1111 | }; |
bac29564 | 1112 | parse_actions(&inner_ctx, LEX_T_RCURLY); |
d5a76da4 | 1113 | |
b3bd2c33 BP |
1114 | if (prereq) { |
1115 | /* XXX Not really sure what we should do with prerequisites for "arp" | |
1116 | * and "nd_na" actions. */ | |
1117 | expr_destroy(inner_ctx.prereqs); | |
1118 | add_prerequisite(ctx, prereq); | |
1119 | } else { | |
1120 | /* For "clone", the inner prerequisites should just add to the outer | |
1121 | * ones. */ | |
1122 | ctx->prereqs = expr_combine(EXPR_T_AND, | |
1123 | inner_ctx.prereqs, ctx->prereqs); | |
1124 | } | |
d5a76da4 | 1125 | |
9aef3c1b | 1126 | if (inner_ctx.lexer->error) { |
d5a76da4 BP |
1127 | ovnacts_free(nested.data, nested.size); |
1128 | ofpbuf_uninit(&nested); | |
1129 | return; | |
1130 | } | |
1131 | ||
8a41ad8e BP |
1132 | struct ovnact_nest *on = ovnact_put(ctx->ovnacts, type, |
1133 | OVNACT_ALIGN(sizeof *on)); | |
d5a76da4 BP |
1134 | on->nested_len = nested.size; |
1135 | on->nested = ofpbuf_steal_data(&nested); | |
1136 | } | |
1137 | ||
1138 | static void | |
1139 | parse_ARP(struct action_context *ctx) | |
1140 | { | |
1141 | parse_nested_action(ctx, OVNACT_ARP, "ip4"); | |
1142 | } | |
1143 | ||
bc3d6a63 LB |
1144 | static void |
1145 | parse_ICMP4(struct action_context *ctx) | |
1146 | { | |
1147 | parse_nested_action(ctx, OVNACT_ICMP4, "ip4"); | |
1148 | } | |
1149 | ||
3e7fa1e3 LB |
1150 | static void |
1151 | parse_ICMP6(struct action_context *ctx) | |
1152 | { | |
1153 | parse_nested_action(ctx, OVNACT_ICMP6, "ip6"); | |
1154 | } | |
1155 | ||
22b65e4d LB |
1156 | static void |
1157 | parse_TCP_RESET(struct action_context *ctx) | |
1158 | { | |
1159 | parse_nested_action(ctx, OVNACT_TCP_RESET, "tcp"); | |
1160 | } | |
1161 | ||
d5a76da4 BP |
1162 | static void |
1163 | parse_ND_NA(struct action_context *ctx) | |
1164 | { | |
1165 | parse_nested_action(ctx, OVNACT_ND_NA, "nd_ns"); | |
1166 | } | |
1167 | ||
b1a3a6a4 NS |
1168 | static void |
1169 | parse_ND_NS(struct action_context *ctx) | |
1170 | { | |
1171 | parse_nested_action(ctx, OVNACT_ND_NS, "ip6"); | |
1172 | } | |
1173 | ||
b3bd2c33 BP |
1174 | static void |
1175 | parse_CLONE(struct action_context *ctx) | |
1176 | { | |
1177 | parse_nested_action(ctx, OVNACT_CLONE, NULL); | |
1178 | } | |
1179 | ||
d5a76da4 BP |
1180 | static void |
1181 | format_nested_action(const struct ovnact_nest *on, const char *name, | |
1182 | struct ds *s) | |
1183 | { | |
1184 | ds_put_format(s, "%s { ", name); | |
1185 | ovnacts_format(on->nested, on->nested_len, s); | |
1186 | ds_put_format(s, " };"); | |
1187 | } | |
1188 | ||
1189 | static void | |
1190 | format_ARP(const struct ovnact_nest *nest, struct ds *s) | |
1191 | { | |
1192 | format_nested_action(nest, "arp", s); | |
1193 | } | |
1194 | ||
bc3d6a63 LB |
1195 | static void |
1196 | format_ICMP4(const struct ovnact_nest *nest, struct ds *s) | |
1197 | { | |
1198 | format_nested_action(nest, "icmp4", s); | |
1199 | } | |
1200 | ||
3e7fa1e3 LB |
1201 | static void |
1202 | format_ICMP6(const struct ovnact_nest *nest, struct ds *s) | |
1203 | { | |
1204 | format_nested_action(nest, "icmp6", s); | |
1205 | } | |
1206 | ||
22b65e4d LB |
1207 | static void |
1208 | format_TCP_RESET(const struct ovnact_nest *nest, struct ds *s) | |
1209 | { | |
1210 | format_nested_action(nest, "tcp_reset", s); | |
1211 | } | |
1212 | ||
d5a76da4 BP |
1213 | static void |
1214 | format_ND_NA(const struct ovnact_nest *nest, struct ds *s) | |
1215 | { | |
1216 | format_nested_action(nest, "nd_na", s); | |
1217 | } | |
1218 | ||
b1a3a6a4 NS |
1219 | static void |
1220 | format_ND_NS(const struct ovnact_nest *nest, struct ds *s) | |
1221 | { | |
1222 | format_nested_action(nest, "nd_ns", s); | |
1223 | } | |
1224 | ||
d5a76da4 | 1225 | static void |
b3bd2c33 BP |
1226 | format_CLONE(const struct ovnact_nest *nest, struct ds *s) |
1227 | { | |
1228 | format_nested_action(nest, "clone", s); | |
1229 | } | |
1230 | ||
1231 | static void | |
358fa138 LB |
1232 | encode_nested_actions(const struct ovnact_nest *on, |
1233 | const struct ovnact_encode_params *ep, | |
1234 | enum action_opcode opcode, | |
1235 | struct ofpbuf *ofpacts) | |
d5a76da4 BP |
1236 | { |
1237 | /* Convert nested actions into ofpacts. */ | |
1238 | uint64_t inner_ofpacts_stub[1024 / 8]; | |
1239 | struct ofpbuf inner_ofpacts = OFPBUF_STUB_INITIALIZER(inner_ofpacts_stub); | |
1240 | ovnacts_encode(on->nested, on->nested_len, ep, &inner_ofpacts); | |
1241 | ||
1242 | /* Add a "controller" action with the actions nested inside "{...}", | |
1243 | * converted to OpenFlow, as its userdata. ovn-controller will convert the | |
1244 | * packet to ARP or NA and then send the packet and actions back to the | |
1245 | * switch inside an OFPT_PACKET_OUT message. */ | |
1246 | size_t oc_offset = encode_start_controller_op(opcode, false, ofpacts); | |
1247 | ofpacts_put_openflow_actions(inner_ofpacts.data, inner_ofpacts.size, | |
1248 | ofpacts, OFP13_VERSION); | |
1249 | encode_finish_controller_op(oc_offset, ofpacts); | |
1250 | ||
1251 | /* Free memory. */ | |
1252 | ofpbuf_uninit(&inner_ofpacts); | |
1253 | } | |
1254 | ||
1255 | static void | |
1256 | encode_ARP(const struct ovnact_nest *on, | |
1257 | const struct ovnact_encode_params *ep, | |
1258 | struct ofpbuf *ofpacts) | |
1259 | { | |
358fa138 | 1260 | encode_nested_actions(on, ep, ACTION_OPCODE_ARP, ofpacts); |
d5a76da4 BP |
1261 | } |
1262 | ||
bc3d6a63 LB |
1263 | static void |
1264 | encode_ICMP4(const struct ovnact_nest *on, | |
1265 | const struct ovnact_encode_params *ep, | |
1266 | struct ofpbuf *ofpacts) | |
1267 | { | |
3e7fa1e3 LB |
1268 | encode_nested_actions(on, ep, ACTION_OPCODE_ICMP, ofpacts); |
1269 | } | |
1270 | ||
1271 | static void | |
1272 | encode_ICMP6(const struct ovnact_nest *on, | |
1273 | const struct ovnact_encode_params *ep, | |
1274 | struct ofpbuf *ofpacts) | |
1275 | { | |
1276 | encode_nested_actions(on, ep, ACTION_OPCODE_ICMP, ofpacts); | |
bc3d6a63 LB |
1277 | } |
1278 | ||
22b65e4d LB |
1279 | static void |
1280 | encode_TCP_RESET(const struct ovnact_nest *on, | |
1281 | const struct ovnact_encode_params *ep, | |
1282 | struct ofpbuf *ofpacts) | |
1283 | { | |
1284 | encode_nested_actions(on, ep, ACTION_OPCODE_TCP_RESET, ofpacts); | |
1285 | } | |
1286 | ||
d5a76da4 BP |
1287 | static void |
1288 | encode_ND_NA(const struct ovnact_nest *on, | |
1289 | const struct ovnact_encode_params *ep, | |
1290 | struct ofpbuf *ofpacts) | |
1291 | { | |
358fa138 | 1292 | encode_nested_actions(on, ep, ACTION_OPCODE_ND_NA, ofpacts); |
d5a76da4 BP |
1293 | } |
1294 | ||
b1a3a6a4 NS |
1295 | static void |
1296 | encode_ND_NS(const struct ovnact_nest *on, | |
1297 | const struct ovnact_encode_params *ep, | |
1298 | struct ofpbuf *ofpacts) | |
1299 | { | |
358fa138 | 1300 | encode_nested_actions(on, ep, ACTION_OPCODE_ND_NS, ofpacts); |
b1a3a6a4 NS |
1301 | } |
1302 | ||
b3bd2c33 BP |
1303 | static void |
1304 | encode_CLONE(const struct ovnact_nest *on, | |
1305 | const struct ovnact_encode_params *ep, | |
1306 | struct ofpbuf *ofpacts) | |
1307 | { | |
1308 | size_t ofs = ofpacts->size; | |
1309 | ofpact_put_CLONE(ofpacts); | |
1310 | ovnacts_encode(on->nested, on->nested_len, ep, ofpacts); | |
1311 | ||
1312 | struct ofpact_nest *clone = ofpbuf_at_assert(ofpacts, ofs, sizeof *clone); | |
1313 | ofpacts->header = clone; | |
1314 | ofpact_finish_CLONE(ofpacts, &clone); | |
1315 | } | |
80b6743d | 1316 | |
d5a76da4 | 1317 | static void |
80b6743d | 1318 | ovnact_nest_free(struct ovnact_nest *on) |
d5a76da4 BP |
1319 | { |
1320 | ovnacts_free(on->nested, on->nested_len); | |
1321 | free(on->nested); | |
1322 | } | |
d5a76da4 BP |
1323 | \f |
1324 | static void | |
1325 | parse_get_mac_bind(struct action_context *ctx, int width, | |
1326 | struct ovnact_get_mac_bind *get_mac) | |
1327 | { | |
9aef3c1b | 1328 | lexer_force_match(ctx->lexer, LEX_T_LPAREN); |
d5a76da4 | 1329 | action_parse_field(ctx, 0, false, &get_mac->port); |
9aef3c1b | 1330 | lexer_force_match(ctx->lexer, LEX_T_COMMA); |
d5a76da4 | 1331 | action_parse_field(ctx, width, false, &get_mac->ip); |
9aef3c1b | 1332 | lexer_force_match(ctx->lexer, LEX_T_RPAREN); |
d5a76da4 BP |
1333 | } |
1334 | ||
1335 | static void | |
1336 | format_get_mac_bind(const struct ovnact_get_mac_bind *get_mac, | |
1337 | const char *name, struct ds *s) | |
1338 | { | |
1339 | ds_put_format(s, "%s(", name); | |
1340 | expr_field_format(&get_mac->port, s); | |
1341 | ds_put_cstr(s, ", "); | |
1342 | expr_field_format(&get_mac->ip, s); | |
1343 | ds_put_cstr(s, ");"); | |
1344 | } | |
1345 | ||
1346 | static void | |
1347 | format_GET_ARP(const struct ovnact_get_mac_bind *get_mac, struct ds *s) | |
1348 | { | |
1349 | format_get_mac_bind(get_mac, "get_arp", s); | |
1350 | } | |
1351 | ||
1352 | static void | |
1353 | format_GET_ND(const struct ovnact_get_mac_bind *get_mac, struct ds *s) | |
1354 | { | |
1355 | format_get_mac_bind(get_mac, "get_nd", s); | |
1356 | } | |
1357 | ||
1358 | static void | |
1359 | encode_get_mac(const struct ovnact_get_mac_bind *get_mac, | |
1360 | enum mf_field_id ip_field, | |
1361 | const struct ovnact_encode_params *ep, | |
1362 | struct ofpbuf *ofpacts) | |
1363 | { | |
1364 | const struct arg args[] = { | |
1365 | { expr_resolve_field(&get_mac->port), MFF_LOG_OUTPORT }, | |
1366 | { expr_resolve_field(&get_mac->ip), ip_field }, | |
1367 | }; | |
1368 | encode_setup_args(args, ARRAY_SIZE(args), ofpacts); | |
1369 | ||
1370 | put_load(0, MFF_ETH_DST, 0, 48, ofpacts); | |
1371 | emit_resubmit(ofpacts, ep->mac_bind_ptable); | |
1372 | ||
1373 | encode_restore_args(args, ARRAY_SIZE(args), ofpacts); | |
1374 | } | |
1375 | ||
1376 | static void | |
1377 | encode_GET_ARP(const struct ovnact_get_mac_bind *get_mac, | |
1378 | const struct ovnact_encode_params *ep, | |
1379 | struct ofpbuf *ofpacts) | |
1380 | { | |
1381 | encode_get_mac(get_mac, MFF_REG0, ep, ofpacts); | |
1382 | } | |
1383 | ||
1384 | static void | |
1385 | encode_GET_ND(const struct ovnact_get_mac_bind *get_mac, | |
1386 | const struct ovnact_encode_params *ep, | |
1387 | struct ofpbuf *ofpacts) | |
1388 | { | |
1389 | encode_get_mac(get_mac, MFF_XXREG0, ep, ofpacts); | |
1390 | } | |
1391 | ||
1392 | static void | |
80b6743d | 1393 | ovnact_get_mac_bind_free(struct ovnact_get_mac_bind *get_mac OVS_UNUSED) |
d5a76da4 BP |
1394 | { |
1395 | } | |
1396 | \f | |
1397 | static void | |
1398 | parse_put_mac_bind(struct action_context *ctx, int width, | |
1399 | struct ovnact_put_mac_bind *put_mac) | |
1400 | { | |
9aef3c1b | 1401 | lexer_force_match(ctx->lexer, LEX_T_LPAREN); |
d5a76da4 | 1402 | action_parse_field(ctx, 0, false, &put_mac->port); |
9aef3c1b | 1403 | lexer_force_match(ctx->lexer, LEX_T_COMMA); |
d5a76da4 | 1404 | action_parse_field(ctx, width, false, &put_mac->ip); |
9aef3c1b | 1405 | lexer_force_match(ctx->lexer, LEX_T_COMMA); |
d5a76da4 | 1406 | action_parse_field(ctx, 48, false, &put_mac->mac); |
9aef3c1b | 1407 | lexer_force_match(ctx->lexer, LEX_T_RPAREN); |
d5a76da4 BP |
1408 | } |
1409 | ||
1410 | static void | |
1411 | format_put_mac_bind(const struct ovnact_put_mac_bind *put_mac, | |
1412 | const char *name, struct ds *s) | |
1413 | { | |
1414 | ds_put_format(s, "%s(", name); | |
1415 | expr_field_format(&put_mac->port, s); | |
1416 | ds_put_cstr(s, ", "); | |
1417 | expr_field_format(&put_mac->ip, s); | |
1418 | ds_put_cstr(s, ", "); | |
1419 | expr_field_format(&put_mac->mac, s); | |
1420 | ds_put_cstr(s, ");"); | |
1421 | } | |
1422 | ||
1423 | static void | |
1424 | format_PUT_ARP(const struct ovnact_put_mac_bind *put_mac, struct ds *s) | |
1425 | { | |
1426 | format_put_mac_bind(put_mac, "put_arp", s); | |
1427 | } | |
1428 | ||
1429 | static void | |
1430 | format_PUT_ND(const struct ovnact_put_mac_bind *put_mac, struct ds *s) | |
1431 | { | |
1432 | format_put_mac_bind(put_mac, "put_nd", s); | |
1433 | } | |
1434 | ||
1435 | static void | |
1436 | encode_put_mac(const struct ovnact_put_mac_bind *put_mac, | |
1437 | enum mf_field_id ip_field, enum action_opcode opcode, | |
1438 | struct ofpbuf *ofpacts) | |
1439 | { | |
1440 | const struct arg args[] = { | |
1441 | { expr_resolve_field(&put_mac->port), MFF_LOG_INPORT }, | |
1442 | { expr_resolve_field(&put_mac->ip), ip_field }, | |
1443 | { expr_resolve_field(&put_mac->mac), MFF_ETH_SRC } | |
1444 | }; | |
1445 | encode_setup_args(args, ARRAY_SIZE(args), ofpacts); | |
1446 | encode_controller_op(opcode, ofpacts); | |
1447 | encode_restore_args(args, ARRAY_SIZE(args), ofpacts); | |
1448 | } | |
1449 | ||
1450 | static void | |
1451 | encode_PUT_ARP(const struct ovnact_put_mac_bind *put_mac, | |
1452 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
1453 | struct ofpbuf *ofpacts) | |
1454 | { | |
1455 | encode_put_mac(put_mac, MFF_REG0, ACTION_OPCODE_PUT_ARP, ofpacts); | |
1456 | } | |
1457 | ||
1458 | static void | |
1459 | encode_PUT_ND(const struct ovnact_put_mac_bind *put_mac, | |
1460 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
1461 | struct ofpbuf *ofpacts) | |
1462 | { | |
1463 | encode_put_mac(put_mac, MFF_XXREG0, ACTION_OPCODE_PUT_ND, ofpacts); | |
467085fd GS |
1464 | } |
1465 | ||
c34a87b6 | 1466 | static void |
80b6743d | 1467 | ovnact_put_mac_bind_free(struct ovnact_put_mac_bind *put_mac OVS_UNUSED) |
d5a76da4 BP |
1468 | { |
1469 | } | |
1470 | \f | |
1471 | static void | |
16936e4d NS |
1472 | parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o, |
1473 | const struct hmap *gen_opts, const char *opts_type) | |
d5a76da4 BP |
1474 | { |
1475 | if (ctx->lexer->token.type != LEX_T_ID) { | |
9aef3c1b | 1476 | lexer_syntax_error(ctx->lexer, NULL); |
c34a87b6 JP |
1477 | return; |
1478 | } | |
1479 | ||
16936e4d | 1480 | o->option = gen_opts ? gen_opts_find(gen_opts, ctx->lexer->token.s) : NULL; |
d5a76da4 | 1481 | if (!o->option) { |
16936e4d | 1482 | lexer_syntax_error(ctx->lexer, "expecting %s option name", opts_type); |
d5a76da4 BP |
1483 | return; |
1484 | } | |
1485 | lexer_get(ctx->lexer); | |
1486 | ||
9aef3c1b | 1487 | if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { |
d5a76da4 BP |
1488 | return; |
1489 | } | |
1490 | ||
9aef3c1b | 1491 | if (!expr_constant_set_parse(ctx->lexer, &o->value)) { |
d5a76da4 | 1492 | memset(&o->value, 0, sizeof o->value); |
d5a76da4 BP |
1493 | return; |
1494 | } | |
c34a87b6 | 1495 | |
d5a76da4 BP |
1496 | if (!strcmp(o->option->type, "str")) { |
1497 | if (o->value.type != EXPR_C_STRING) { | |
9aef3c1b | 1498 | lexer_error(ctx->lexer, "%s option %s requires string value.", |
16936e4d | 1499 | opts_type, o->option->name); |
d5a76da4 BP |
1500 | return; |
1501 | } | |
1502 | } else { | |
1503 | if (o->value.type != EXPR_C_INTEGER) { | |
9aef3c1b | 1504 | lexer_error(ctx->lexer, "%s option %s requires numeric value.", |
16936e4d | 1505 | opts_type, o->option->name); |
d5a76da4 BP |
1506 | return; |
1507 | } | |
1508 | } | |
1509 | } | |
c34a87b6 | 1510 | |
16936e4d NS |
1511 | static const struct ovnact_gen_option * |
1512 | find_offerip(const struct ovnact_gen_option *options, size_t n) | |
d5a76da4 | 1513 | { |
16936e4d | 1514 | for (const struct ovnact_gen_option *o = options; o < &options[n]; o++) { |
d5a76da4 BP |
1515 | if (o->option->code == 0) { |
1516 | return o; | |
1517 | } | |
1518 | } | |
1519 | return NULL; | |
c34a87b6 JP |
1520 | } |
1521 | ||
1522 | static void | |
16936e4d | 1523 | free_gen_options(struct ovnact_gen_option *options, size_t n) |
c34a87b6 | 1524 | { |
16936e4d | 1525 | for (struct ovnact_gen_option *o = options; o < &options[n]; o++) { |
d5a76da4 | 1526 | expr_constant_set_destroy(&o->value); |
c34a87b6 | 1527 | } |
d5a76da4 | 1528 | free(options); |
c34a87b6 JP |
1529 | } |
1530 | ||
78aab811 | 1531 | static void |
16936e4d NS |
1532 | parse_put_opts(struct action_context *ctx, const struct expr_field *dst, |
1533 | struct ovnact_put_opts *po, const struct hmap *gen_opts, | |
1534 | const char *opts_type) | |
78aab811 | 1535 | { |
52ed5fcc | 1536 | lexer_get(ctx->lexer); /* Skip put_dhcp[v6]_opts / put_nd_ra_opts. */ |
d5a76da4 | 1537 | lexer_get(ctx->lexer); /* Skip '('. */ |
78aab811 | 1538 | |
d5a76da4 BP |
1539 | /* Validate that the destination is a 1-bit, modifiable field. */ |
1540 | char *error = expr_type_check(dst, 1, true); | |
1541 | if (error) { | |
9aef3c1b | 1542 | lexer_error(ctx->lexer, "%s", error); |
d5a76da4 BP |
1543 | free(error); |
1544 | return; | |
78aab811 | 1545 | } |
16936e4d | 1546 | po->dst = *dst; |
78aab811 | 1547 | |
d5a76da4 BP |
1548 | size_t allocated_options = 0; |
1549 | while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { | |
16936e4d NS |
1550 | if (po->n_options >= allocated_options) { |
1551 | po->options = x2nrealloc(po->options, &allocated_options, | |
1552 | sizeof *po->options); | |
d5a76da4 | 1553 | } |
78aab811 | 1554 | |
16936e4d | 1555 | struct ovnact_gen_option *o = &po->options[po->n_options++]; |
d5a76da4 | 1556 | memset(o, 0, sizeof *o); |
16936e4d | 1557 | parse_gen_opt(ctx, o, gen_opts, opts_type); |
9aef3c1b | 1558 | if (ctx->lexer->error) { |
d5a76da4 BP |
1559 | return; |
1560 | } | |
a9e1b66f | 1561 | |
d5a76da4 BP |
1562 | lexer_match(ctx->lexer, LEX_T_COMMA); |
1563 | } | |
16936e4d NS |
1564 | } |
1565 | ||
1566 | /* Parses the "put_dhcp_opts" and "put_dhcpv6_opts" actions. | |
1567 | * | |
1568 | * The caller has already consumed "<dst> =", so this just parses the rest. */ | |
1569 | static void | |
1570 | parse_put_dhcp_opts(struct action_context *ctx, | |
1571 | const struct expr_field *dst, | |
1572 | struct ovnact_put_opts *po) | |
1573 | { | |
1574 | const struct hmap *dhcp_opts = | |
1575 | (po->ovnact.type == OVNACT_PUT_DHCPV6_OPTS) ? | |
1576 | ctx->pp->dhcpv6_opts : ctx->pp->dhcp_opts; | |
1577 | const char *opts_type = | |
1578 | (po->ovnact.type == OVNACT_PUT_DHCPV6_OPTS) ? "DHCPv6" : "DHCPv4"; | |
1579 | ||
1580 | parse_put_opts(ctx, dst, po, dhcp_opts, opts_type); | |
a9e1b66f | 1581 | |
16936e4d NS |
1582 | if (!ctx->lexer->error && po->ovnact.type == OVNACT_PUT_DHCPV4_OPTS |
1583 | && !find_offerip(po->options, po->n_options)) { | |
9aef3c1b BP |
1584 | lexer_error(ctx->lexer, |
1585 | "put_dhcp_opts requires offerip to be specified."); | |
d5a76da4 | 1586 | return; |
a9e1b66f | 1587 | } |
d5a76da4 | 1588 | } |
a9e1b66f | 1589 | |
d5a76da4 | 1590 | static void |
16936e4d NS |
1591 | format_put_opts(const char *name, const struct ovnact_put_opts *pdo, |
1592 | struct ds *s) | |
d5a76da4 BP |
1593 | { |
1594 | expr_field_format(&pdo->dst, s); | |
1595 | ds_put_format(s, " = %s(", name); | |
16936e4d | 1596 | for (const struct ovnact_gen_option *o = pdo->options; |
d5a76da4 BP |
1597 | o < &pdo->options[pdo->n_options]; o++) { |
1598 | if (o != pdo->options) { | |
1599 | ds_put_cstr(s, ", "); | |
1600 | } | |
1601 | ds_put_format(s, "%s = ", o->option->name); | |
1602 | expr_constant_set_format(&o->value, s); | |
a9e1b66f | 1603 | } |
d5a76da4 BP |
1604 | ds_put_cstr(s, ");"); |
1605 | } | |
a9e1b66f | 1606 | |
d5a76da4 | 1607 | static void |
16936e4d | 1608 | format_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo, struct ds *s) |
d5a76da4 | 1609 | { |
16936e4d | 1610 | format_put_opts("put_dhcp_opts", pdo, s); |
a9e1b66f RB |
1611 | } |
1612 | ||
d5a76da4 | 1613 | static void |
16936e4d | 1614 | format_PUT_DHCPV6_OPTS(const struct ovnact_put_opts *pdo, struct ds *s) |
a9e1b66f | 1615 | { |
16936e4d | 1616 | format_put_opts("put_dhcpv6_opts", pdo, s); |
d5a76da4 BP |
1617 | } |
1618 | ||
1619 | static void | |
16936e4d | 1620 | encode_put_dhcpv4_option(const struct ovnact_gen_option *o, |
d5a76da4 BP |
1621 | struct ofpbuf *ofpacts) |
1622 | { | |
1623 | uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2); | |
1624 | opt_header[0] = o->option->code; | |
1625 | ||
1626 | const union expr_constant *c = o->value.values; | |
1627 | size_t n_values = o->value.n_values; | |
1628 | if (!strcmp(o->option->type, "bool") || | |
1629 | !strcmp(o->option->type, "uint8")) { | |
1630 | opt_header[1] = 1; | |
1631 | ofpbuf_put(ofpacts, &c->value.u8_val, 1); | |
1632 | } else if (!strcmp(o->option->type, "uint16")) { | |
1633 | opt_header[1] = 2; | |
1634 | ofpbuf_put(ofpacts, &c->value.be16_int, 2); | |
1635 | } else if (!strcmp(o->option->type, "uint32")) { | |
1636 | opt_header[1] = 4; | |
1637 | ofpbuf_put(ofpacts, &c->value.be32_int, 4); | |
1638 | } else if (!strcmp(o->option->type, "ipv4")) { | |
1639 | opt_header[1] = n_values * sizeof(ovs_be32); | |
1640 | for (size_t i = 0; i < n_values; i++) { | |
1641 | ofpbuf_put(ofpacts, &c[i].value.ipv4, sizeof(ovs_be32)); | |
a9e1b66f | 1642 | } |
d5a76da4 BP |
1643 | } else if (!strcmp(o->option->type, "static_routes")) { |
1644 | size_t no_of_routes = n_values; | |
1645 | if (no_of_routes % 2) { | |
1646 | no_of_routes -= 1; | |
a9e1b66f | 1647 | } |
d5a76da4 | 1648 | opt_header[1] = 0; |
354b8f27 | 1649 | |
d5a76da4 BP |
1650 | /* Calculating the length of this option first because when |
1651 | * we call ofpbuf_put, it might reallocate the buffer if the | |
1652 | * tail room is short making "opt_header" pointer invalid. | |
1653 | * So running the for loop twice. | |
1654 | */ | |
1655 | for (size_t i = 0; i < no_of_routes; i += 2) { | |
1656 | uint8_t plen = 32; | |
1657 | if (c[i].masked) { | |
1658 | plen = (uint8_t) ip_count_cidr_bits(c[i].mask.ipv4); | |
1659 | } | |
1660 | opt_header[1] += (1 + (plen / 8) + sizeof(ovs_be32)) ; | |
a9e1b66f | 1661 | } |
a9e1b66f | 1662 | |
d5a76da4 BP |
1663 | /* Copied from RFC 3442. Please refer to this RFC for the format of |
1664 | * the classless static route option. | |
1665 | * | |
1666 | * The following table contains some examples of how various subnet | |
1667 | * number/mask combinations can be encoded: | |
1668 | * | |
1669 | * Subnet number Subnet mask Destination descriptor | |
1670 | * 0 0 0 | |
1671 | * 10.0.0.0 255.0.0.0 8.10 | |
1672 | * 10.0.0.0 255.255.255.0 24.10.0.0 | |
1673 | * 10.17.0.0 255.255.0.0 16.10.17 | |
1674 | * 10.27.129.0 255.255.255.0 24.10.27.129 | |
1675 | * 10.229.0.128 255.255.255.128 25.10.229.0.128 | |
1676 | * 10.198.122.47 255.255.255.255 32.10.198.122.47 | |
1677 | */ | |
1678 | ||
1679 | for (size_t i = 0; i < no_of_routes; i += 2) { | |
1680 | uint8_t plen = 32; | |
1681 | if (c[i].masked) { | |
1682 | plen = ip_count_cidr_bits(c[i].mask.ipv4); | |
1683 | } | |
1684 | ofpbuf_put(ofpacts, &plen, 1); | |
1685 | ofpbuf_put(ofpacts, &c[i].value.ipv4, plen / 8); | |
1686 | ofpbuf_put(ofpacts, &c[i + 1].value.ipv4, | |
1687 | sizeof(ovs_be32)); | |
a9e1b66f | 1688 | } |
d5a76da4 BP |
1689 | } else if (!strcmp(o->option->type, "str")) { |
1690 | opt_header[1] = strlen(c->string); | |
1691 | ofpbuf_put(ofpacts, c->string, opt_header[1]); | |
a9e1b66f | 1692 | } |
a9e1b66f RB |
1693 | } |
1694 | ||
1695 | static void | |
16936e4d | 1696 | encode_put_dhcpv6_option(const struct ovnact_gen_option *o, |
d5a76da4 | 1697 | struct ofpbuf *ofpacts) |
a9e1b66f | 1698 | { |
d5a76da4 | 1699 | struct dhcp_opt6_header *opt = ofpbuf_put_uninit(ofpacts, sizeof *opt); |
d5a76da4 BP |
1700 | const union expr_constant *c = o->value.values; |
1701 | size_t n_values = o->value.n_values; | |
a55dacac DDP |
1702 | size_t size; |
1703 | ||
1704 | opt->opt_code = htons(o->option->code); | |
1705 | ||
d5a76da4 | 1706 | if (!strcmp(o->option->type, "ipv6")) { |
a55dacac DDP |
1707 | size = n_values * sizeof(struct in6_addr); |
1708 | opt->size = htons(size); | |
d5a76da4 BP |
1709 | for (size_t i = 0; i < n_values; i++) { |
1710 | ofpbuf_put(ofpacts, &c[i].value.ipv6, sizeof(struct in6_addr)); | |
1711 | } | |
1712 | } else if (!strcmp(o->option->type, "mac")) { | |
a55dacac DDP |
1713 | size = sizeof(struct eth_addr); |
1714 | opt->size = htons(size); | |
1715 | ofpbuf_put(ofpacts, &c->value.mac, size); | |
d5a76da4 | 1716 | } else if (!strcmp(o->option->type, "str")) { |
a55dacac DDP |
1717 | size = strlen(c->string); |
1718 | opt->size = htons(size); | |
1719 | ofpbuf_put(ofpacts, c->string, size); | |
a9e1b66f | 1720 | } |
d5a76da4 | 1721 | } |
a9e1b66f | 1722 | |
d5a76da4 | 1723 | static void |
16936e4d | 1724 | encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo, |
d5a76da4 BP |
1725 | const struct ovnact_encode_params *ep OVS_UNUSED, |
1726 | struct ofpbuf *ofpacts) | |
1727 | { | |
1728 | struct mf_subfield dst = expr_resolve_field(&pdo->dst); | |
1729 | ||
1730 | size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_PUT_DHCP_OPTS, | |
1731 | true, ofpacts); | |
1732 | nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false); | |
1733 | ovs_be32 ofs = htonl(dst.ofs); | |
1734 | ofpbuf_put(ofpacts, &ofs, sizeof ofs); | |
1735 | ||
1736 | /* Encode the offerip option first, because it's a special case and needs | |
1737 | * to be first in the actual DHCP response, and then encode the rest | |
1738 | * (skipping offerip the second time around). */ | |
16936e4d | 1739 | const struct ovnact_gen_option *offerip_opt = find_offerip( |
d5a76da4 BP |
1740 | pdo->options, pdo->n_options); |
1741 | ovs_be32 offerip = offerip_opt->value.values[0].value.ipv4; | |
1742 | ofpbuf_put(ofpacts, &offerip, sizeof offerip); | |
1743 | ||
16936e4d | 1744 | for (const struct ovnact_gen_option *o = pdo->options; |
d5a76da4 BP |
1745 | o < &pdo->options[pdo->n_options]; o++) { |
1746 | if (o != offerip_opt) { | |
1747 | encode_put_dhcpv4_option(o, ofpacts); | |
a9e1b66f RB |
1748 | } |
1749 | } | |
1750 | ||
d5a76da4 | 1751 | encode_finish_controller_op(oc_offset, ofpacts); |
78aab811 JP |
1752 | } |
1753 | ||
de297547 | 1754 | static void |
16936e4d | 1755 | encode_PUT_DHCPV6_OPTS(const struct ovnact_put_opts *pdo, |
d5a76da4 BP |
1756 | const struct ovnact_encode_params *ep OVS_UNUSED, |
1757 | struct ofpbuf *ofpacts) | |
de297547 | 1758 | { |
d5a76da4 | 1759 | struct mf_subfield dst = expr_resolve_field(&pdo->dst); |
de297547 | 1760 | |
d5a76da4 BP |
1761 | size_t oc_offset = encode_start_controller_op( |
1762 | ACTION_OPCODE_PUT_DHCPV6_OPTS, true, ofpacts); | |
1763 | nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false); | |
1764 | ovs_be32 ofs = htonl(dst.ofs); | |
1765 | ofpbuf_put(ofpacts, &ofs, sizeof ofs); | |
de297547 | 1766 | |
16936e4d | 1767 | for (const struct ovnact_gen_option *o = pdo->options; |
d5a76da4 BP |
1768 | o < &pdo->options[pdo->n_options]; o++) { |
1769 | encode_put_dhcpv6_option(o, ofpacts); | |
de297547 | 1770 | } |
de297547 | 1771 | |
d5a76da4 BP |
1772 | encode_finish_controller_op(oc_offset, ofpacts); |
1773 | } | |
de297547 | 1774 | |
d5a76da4 | 1775 | static void |
16936e4d | 1776 | ovnact_put_opts_free(struct ovnact_put_opts *pdo) |
d5a76da4 | 1777 | { |
16936e4d | 1778 | free_gen_options(pdo->options, pdo->n_options); |
d5a76da4 | 1779 | } |
de297547 | 1780 | |
a6095f81 BS |
1781 | static void |
1782 | parse_SET_QUEUE(struct action_context *ctx) | |
1783 | { | |
1784 | int queue_id; | |
1785 | ||
1786 | if (!lexer_force_match(ctx->lexer, LEX_T_LPAREN) | |
1787 | || !lexer_get_int(ctx->lexer, &queue_id) | |
1788 | || !lexer_force_match(ctx->lexer, LEX_T_RPAREN)) { | |
1789 | return; | |
1790 | } | |
1791 | ||
1792 | if (queue_id < QDISC_MIN_QUEUE_ID || queue_id > QDISC_MAX_QUEUE_ID) { | |
1793 | lexer_error(ctx->lexer, "Queue ID %d for set_queue is " | |
1794 | "not in valid range %d to %d.", | |
1795 | queue_id, QDISC_MIN_QUEUE_ID, QDISC_MAX_QUEUE_ID); | |
1796 | return; | |
1797 | } | |
1798 | ||
1799 | ovnact_put_SET_QUEUE(ctx->ovnacts)->queue_id = queue_id; | |
1800 | } | |
1801 | ||
1802 | static void | |
1803 | format_SET_QUEUE(const struct ovnact_set_queue *set_queue, struct ds *s) | |
1804 | { | |
1805 | ds_put_format(s, "set_queue(%d);", set_queue->queue_id); | |
1806 | } | |
1807 | ||
1808 | static void | |
1809 | encode_SET_QUEUE(const struct ovnact_set_queue *set_queue, | |
1810 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
1811 | struct ofpbuf *ofpacts) | |
1812 | { | |
1813 | ofpact_put_SET_QUEUE(ofpacts)->queue_id = set_queue->queue_id; | |
1814 | } | |
1815 | ||
1816 | static void | |
80b6743d | 1817 | ovnact_set_queue_free(struct ovnact_set_queue *a OVS_UNUSED) |
a6095f81 BS |
1818 | { |
1819 | } | |
ea991ad2 NS |
1820 | |
1821 | static void | |
1822 | parse_dns_lookup(struct action_context *ctx, const struct expr_field *dst, | |
1823 | struct ovnact_dns_lookup *dl) | |
1824 | { | |
1825 | lexer_get(ctx->lexer); /* Skip dns_lookup. */ | |
1826 | lexer_get(ctx->lexer); /* Skip '('. */ | |
1827 | if (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { | |
1828 | lexer_error(ctx->lexer, "dns_lookup doesn't take any parameters"); | |
1829 | return; | |
1830 | } | |
1831 | /* Validate that the destination is a 1-bit, modifiable field. */ | |
1832 | char *error = expr_type_check(dst, 1, true); | |
1833 | if (error) { | |
1834 | lexer_error(ctx->lexer, "%s", error); | |
1835 | free(error); | |
1836 | return; | |
1837 | } | |
1838 | dl->dst = *dst; | |
1839 | add_prerequisite(ctx, "udp"); | |
1840 | } | |
1841 | ||
1842 | static void | |
1843 | format_DNS_LOOKUP(const struct ovnact_dns_lookup *dl, struct ds *s) | |
1844 | { | |
1845 | expr_field_format(&dl->dst, s); | |
1846 | ds_put_cstr(s, " = dns_lookup();"); | |
1847 | } | |
1848 | ||
1849 | static void | |
1850 | encode_DNS_LOOKUP(const struct ovnact_dns_lookup *dl, | |
1851 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
1852 | struct ofpbuf *ofpacts) | |
1853 | { | |
1854 | struct mf_subfield dst = expr_resolve_field(&dl->dst); | |
1855 | ||
1856 | size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_DNS_LOOKUP, | |
1857 | true, ofpacts); | |
1858 | nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false); | |
1859 | ovs_be32 ofs = htonl(dst.ofs); | |
1860 | ofpbuf_put(ofpacts, &ofs, sizeof ofs); | |
1861 | encode_finish_controller_op(oc_offset, ofpacts); | |
1862 | } | |
1863 | ||
1864 | ||
1865 | static void | |
1866 | ovnact_dns_lookup_free(struct ovnact_dns_lookup *dl OVS_UNUSED) | |
1867 | { | |
1868 | } | |
52ed5fcc NS |
1869 | |
1870 | /* Parses the "put_nd_ra_opts" action. | |
1871 | * The caller has already consumed "<dst> =", so this just parses the rest. */ | |
1872 | static void | |
1873 | parse_put_nd_ra_opts(struct action_context *ctx, const struct expr_field *dst, | |
1874 | struct ovnact_put_opts *po) | |
1875 | { | |
1876 | parse_put_opts(ctx, dst, po, ctx->pp->nd_ra_opts, "IPv6 ND RA"); | |
1877 | ||
1878 | if (ctx->lexer->error) { | |
1879 | return; | |
1880 | } | |
1881 | ||
1882 | bool addr_mode_stateful = false; | |
1883 | bool prefix_set = false; | |
1884 | bool slla_present = false; | |
1885 | /* Let's validate the options. */ | |
1886 | for (struct ovnact_gen_option *o = po->options; | |
1887 | o < &po->options[po->n_options]; o++) { | |
1888 | const union expr_constant *c = o->value.values; | |
1889 | if (o->value.n_values > 1) { | |
1890 | lexer_error(ctx->lexer, "Invalid value for \"%s\" option", | |
1891 | o->option->name); | |
1892 | return; | |
1893 | } | |
1894 | ||
1895 | bool ok = true; | |
1896 | switch (o->option->code) { | |
1897 | case ND_RA_FLAG_ADDR_MODE: | |
1898 | ok = (c->string && (!strcmp(c->string, "slaac") || | |
1899 | !strcmp(c->string, "dhcpv6_stateful") || | |
1900 | !strcmp(c->string, "dhcpv6_stateless"))); | |
1901 | if (ok && !strcmp(c->string, "dhcpv6_stateful")) { | |
1902 | addr_mode_stateful = true; | |
1903 | } | |
1904 | break; | |
1905 | ||
1906 | case ND_OPT_SOURCE_LINKADDR: | |
1907 | ok = c->format == LEX_F_ETHERNET; | |
1908 | slla_present = true; | |
1909 | break; | |
1910 | ||
1911 | case ND_OPT_PREFIX_INFORMATION: | |
1912 | ok = c->format == LEX_F_IPV6 && c->masked; | |
1913 | prefix_set = true; | |
1914 | break; | |
1915 | ||
1916 | case ND_OPT_MTU: | |
1917 | ok = c->format == LEX_F_DECIMAL; | |
1918 | break; | |
1919 | } | |
1920 | ||
1921 | if (!ok) { | |
1922 | lexer_error(ctx->lexer, "Invalid value for \"%s\" option", | |
1923 | o->option->name); | |
1924 | return; | |
1925 | } | |
1926 | } | |
1927 | ||
1928 | if (!slla_present) { | |
1929 | lexer_error(ctx->lexer, "slla option not present"); | |
1930 | return; | |
1931 | } | |
1932 | ||
1933 | if (addr_mode_stateful && prefix_set) { | |
1934 | lexer_error(ctx->lexer, "prefix option can't be" | |
1935 | " set when address mode is dhcpv6_stateful."); | |
1936 | return; | |
1937 | } | |
1938 | ||
1939 | if (!addr_mode_stateful && !prefix_set) { | |
1940 | lexer_error(ctx->lexer, "prefix option needs " | |
1941 | "to be set when address mode is slaac/dhcpv6_stateless."); | |
1942 | return; | |
1943 | } | |
1944 | ||
1945 | add_prerequisite(ctx, "ip6"); | |
1946 | } | |
1947 | ||
1948 | static void | |
1949 | format_PUT_ND_RA_OPTS(const struct ovnact_put_opts *po, | |
1950 | struct ds *s) | |
1951 | { | |
1952 | format_put_opts("put_nd_ra_opts", po, s); | |
1953 | } | |
1954 | ||
1955 | static void | |
1956 | encode_put_nd_ra_option(const struct ovnact_gen_option *o, | |
1957 | struct ofpbuf *ofpacts, struct ovs_ra_msg *ra) | |
1958 | { | |
1959 | const union expr_constant *c = o->value.values; | |
1960 | ||
1961 | switch (o->option->code) { | |
1962 | case ND_RA_FLAG_ADDR_MODE: | |
1963 | if (!strcmp(c->string, "dhcpv6_stateful")) { | |
1964 | ra->mo_flags = IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG; | |
1965 | } else if (!strcmp(c->string, "dhcpv6_stateless")) { | |
1966 | ra->mo_flags = IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG; | |
1967 | } | |
1968 | break; | |
1969 | ||
1970 | case ND_OPT_SOURCE_LINKADDR: | |
1971 | { | |
1972 | struct ovs_nd_lla_opt *lla_opt = | |
1973 | ofpbuf_put_uninit(ofpacts, sizeof *lla_opt); | |
1974 | lla_opt->type = ND_OPT_SOURCE_LINKADDR; | |
1975 | lla_opt->len = 1; | |
1976 | lla_opt->mac = c->value.mac; | |
1977 | break; | |
1978 | } | |
1979 | ||
1980 | case ND_OPT_MTU: | |
1981 | { | |
1982 | struct ovs_nd_mtu_opt *mtu_opt = | |
1983 | ofpbuf_put_uninit(ofpacts, sizeof *mtu_opt); | |
1984 | mtu_opt->type = ND_OPT_MTU; | |
1985 | mtu_opt->len = 1; | |
1986 | mtu_opt->reserved = 0; | |
1987 | put_16aligned_be32(&mtu_opt->mtu, c->value.be32_int); | |
1988 | break; | |
1989 | } | |
1990 | ||
1991 | case ND_OPT_PREFIX_INFORMATION: | |
1992 | { | |
1993 | struct ovs_nd_prefix_opt *prefix_opt = | |
1994 | ofpbuf_put_uninit(ofpacts, sizeof *prefix_opt); | |
1995 | uint8_t prefix_len = ipv6_count_cidr_bits(&c->mask.ipv6); | |
1996 | prefix_opt->type = ND_OPT_PREFIX_INFORMATION; | |
1997 | prefix_opt->len = 4; | |
1998 | prefix_opt->prefix_len = prefix_len; | |
1999 | prefix_opt->la_flags = IPV6_ND_RA_OPT_PREFIX_FLAGS; | |
2000 | put_16aligned_be32(&prefix_opt->valid_lifetime, | |
2001 | htonl(IPV6_ND_RA_OPT_PREFIX_VALID_LIFETIME)); | |
2002 | put_16aligned_be32(&prefix_opt->preferred_lifetime, | |
2003 | htonl(IPV6_ND_RA_OPT_PREFIX_PREFERRED_LIFETIME)); | |
2004 | put_16aligned_be32(&prefix_opt->reserved, 0); | |
2005 | memcpy(prefix_opt->prefix.be32, &c->value.be128[7].be32, | |
2006 | sizeof(ovs_be32[4])); | |
2007 | break; | |
2008 | } | |
2009 | } | |
2010 | } | |
2011 | ||
2012 | static void | |
2013 | encode_PUT_ND_RA_OPTS(const struct ovnact_put_opts *po, | |
2014 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
2015 | struct ofpbuf *ofpacts) | |
2016 | { | |
2017 | struct mf_subfield dst = expr_resolve_field(&po->dst); | |
2018 | ||
2019 | size_t oc_offset = encode_start_controller_op( | |
2020 | ACTION_OPCODE_PUT_ND_RA_OPTS, true, ofpacts); | |
2021 | nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false); | |
2022 | ovs_be32 ofs = htonl(dst.ofs); | |
2023 | ofpbuf_put(ofpacts, &ofs, sizeof ofs); | |
2024 | ||
2025 | /* Frame the complete ICMPv6 Router Advertisement data encoding | |
2026 | * the ND RA options in it, in the userdata field, so that when | |
2027 | * pinctrl module receives the ICMPv6 Router Solicitation packet | |
2028 | * it can copy the userdata field AS IS and resume the packet. | |
2029 | */ | |
2030 | struct ovs_ra_msg *ra = ofpbuf_put_zeros(ofpacts, sizeof *ra); | |
2031 | ra->icmph.icmp6_type = ND_ROUTER_ADVERT; | |
2032 | ra->cur_hop_limit = IPV6_ND_RA_CUR_HOP_LIMIT; | |
2033 | ra->mo_flags = 0; | |
2034 | ra->router_lifetime = htons(IPV6_ND_RA_LIFETIME); | |
2035 | ||
2036 | for (const struct ovnact_gen_option *o = po->options; | |
2037 | o < &po->options[po->n_options]; o++) { | |
2038 | encode_put_nd_ra_option(o, ofpacts, ra); | |
2039 | } | |
2040 | ||
2041 | encode_finish_controller_op(oc_offset, ofpacts); | |
2042 | } | |
2043 | ||
d5a76da4 | 2044 | \f |
d383eed5 JP |
2045 | static void |
2046 | parse_log_arg(struct action_context *ctx, struct ovnact_log *log) | |
2047 | { | |
2048 | if (lexer_match_id(ctx->lexer, "verdict")) { | |
2049 | if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { | |
2050 | return; | |
2051 | } | |
2052 | if (lexer_match_id(ctx->lexer, "drop")) { | |
2053 | log->verdict = LOG_VERDICT_DROP; | |
2054 | } else if (lexer_match_id(ctx->lexer, "reject")) { | |
2055 | log->verdict = LOG_VERDICT_REJECT; | |
2056 | } else if (lexer_match_id(ctx->lexer, "allow")) { | |
2057 | log->verdict = LOG_VERDICT_ALLOW; | |
2058 | } else { | |
2059 | lexer_syntax_error(ctx->lexer, "unknown acl verdict"); | |
2060 | } | |
2061 | } else if (lexer_match_id(ctx->lexer, "name")) { | |
2062 | if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { | |
2063 | return; | |
2064 | } | |
2065 | /* If multiple names are given, use the most recent. */ | |
2066 | if (ctx->lexer->token.type == LEX_T_STRING) { | |
2067 | /* Arbitrarily limit the name length to 64 bytes, since | |
2068 | * these will be encoded in datapath actions. */ | |
2069 | if (strlen(ctx->lexer->token.s) >= 64) { | |
2070 | lexer_syntax_error(ctx->lexer, "name must be shorter " | |
2071 | "than 64 characters"); | |
2072 | return; | |
2073 | } | |
2074 | free(log->name); | |
2075 | log->name = xstrdup(ctx->lexer->token.s); | |
2076 | } else { | |
2077 | lexer_syntax_error(ctx->lexer, "expecting string"); | |
2078 | return; | |
2079 | } | |
2080 | lexer_get(ctx->lexer); | |
2081 | } else if (lexer_match_id(ctx->lexer, "severity")) { | |
2082 | if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { | |
2083 | return; | |
2084 | } | |
2085 | if (ctx->lexer->token.type == LEX_T_ID) { | |
2086 | uint8_t severity = log_severity_from_string(ctx->lexer->token.s); | |
2087 | if (severity != UINT8_MAX) { | |
2088 | log->severity = severity; | |
2089 | lexer_get(ctx->lexer); | |
2090 | return; | |
2091 | } | |
2092 | } | |
2093 | lexer_syntax_error(ctx->lexer, "expecting severity"); | |
2094 | } else { | |
2095 | lexer_syntax_error(ctx->lexer, NULL); | |
2096 | } | |
2097 | } | |
2098 | ||
2099 | static void | |
2100 | parse_LOG(struct action_context *ctx) | |
2101 | { | |
2102 | struct ovnact_log *log = ovnact_put_LOG(ctx->ovnacts); | |
2103 | ||
2104 | /* Provide default values. */ | |
2105 | log->severity = LOG_SEVERITY_INFO; | |
2106 | log->verdict = LOG_VERDICT_UNKNOWN; | |
2107 | ||
2108 | if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { | |
2109 | while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { | |
2110 | parse_log_arg(ctx, log); | |
2111 | if (ctx->lexer->error) { | |
2112 | return; | |
2113 | } | |
2114 | lexer_match(ctx->lexer, LEX_T_COMMA); | |
2115 | } | |
2116 | } | |
2117 | } | |
2118 | ||
2119 | static void | |
2120 | format_LOG(const struct ovnact_log *log, struct ds *s) | |
2121 | { | |
2122 | ds_put_cstr(s, "log("); | |
2123 | ||
2124 | if (log->name) { | |
2125 | ds_put_format(s, "name=\"%s\", ", log->name); | |
2126 | } | |
2127 | ||
2128 | ds_put_format(s, "verdict=%s, ", log_verdict_to_string(log->verdict)); | |
2129 | ds_put_format(s, "severity=%s);", log_severity_to_string(log->severity)); | |
2130 | } | |
2131 | ||
2132 | static void | |
2133 | encode_LOG(const struct ovnact_log *log, | |
2134 | const struct ovnact_encode_params *ep OVS_UNUSED, | |
2135 | struct ofpbuf *ofpacts) | |
2136 | { | |
2137 | size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_LOG, false, | |
2138 | ofpacts); | |
2139 | ||
2140 | struct log_pin_header *lph = ofpbuf_put_uninit(ofpacts, sizeof *lph); | |
2141 | lph->verdict = log->verdict; | |
2142 | lph->severity = log->severity; | |
2143 | ||
2144 | if (log->name) { | |
2145 | int name_len = strlen(log->name); | |
2146 | ofpbuf_put(ofpacts, log->name, name_len); | |
2147 | } | |
2148 | ||
2149 | encode_finish_controller_op(oc_offset, ofpacts); | |
2150 | } | |
2151 | ||
2152 | static void | |
2153 | ovnact_log_free(struct ovnact_log *log) | |
2154 | { | |
2155 | free(log->name); | |
2156 | } | |
2157 | ||
66d89287 GL |
2158 | static void |
2159 | parse_set_meter_action(struct action_context *ctx) | |
2160 | { | |
2161 | uint64_t rate = 0; | |
2162 | uint64_t burst = 0; | |
2163 | ||
2164 | lexer_force_match(ctx->lexer, LEX_T_LPAREN); /* Skip '('. */ | |
2165 | if (ctx->lexer->token.type == LEX_T_INTEGER | |
2166 | && ctx->lexer->token.format == LEX_F_DECIMAL) { | |
2167 | rate = ntohll(ctx->lexer->token.value.integer); | |
2168 | } | |
2169 | lexer_get(ctx->lexer); | |
2170 | if (lexer_match(ctx->lexer, LEX_T_COMMA)) { /* Skip ','. */ | |
2171 | if (ctx->lexer->token.type == LEX_T_INTEGER | |
2172 | && ctx->lexer->token.format == LEX_F_DECIMAL) { | |
2173 | burst = ntohll(ctx->lexer->token.value.integer); | |
2174 | } | |
2175 | lexer_get(ctx->lexer); | |
2176 | } | |
2177 | lexer_force_match(ctx->lexer, LEX_T_RPAREN); /* Skip ')'. */ | |
2178 | ||
2179 | if (!rate) { | |
2180 | lexer_error(ctx->lexer, | |
2181 | "Rate %"PRId64" for set_meter is not in valid.", | |
2182 | rate); | |
2183 | return; | |
2184 | } | |
2185 | ||
2186 | struct ovnact_set_meter *cl = ovnact_put_SET_METER(ctx->ovnacts); | |
2187 | cl->rate = rate; | |
2188 | cl->burst = burst; | |
2189 | } | |
2190 | ||
2191 | static void | |
2192 | format_SET_METER(const struct ovnact_set_meter *cl, struct ds *s) | |
2193 | { | |
2194 | if (cl->burst) { | |
2195 | ds_put_format(s, "set_meter(%"PRId64", %"PRId64");", | |
2196 | cl->rate, cl->burst); | |
2197 | } else { | |
2198 | ds_put_format(s, "set_meter(%"PRId64");", cl->rate); | |
2199 | } | |
2200 | } | |
2201 | ||
2202 | static void | |
2203 | encode_SET_METER(const struct ovnact_set_meter *cl, | |
2204 | const struct ovnact_encode_params *ep, | |
2205 | struct ofpbuf *ofpacts) | |
2206 | { | |
2207 | uint32_t table_id; | |
2208 | struct ofpact_meter *om; | |
2209 | ||
2210 | struct ds ds = DS_EMPTY_INITIALIZER; | |
2211 | if (cl->burst) { | |
2212 | ds_put_format(&ds, | |
2213 | "kbps burst stats bands=type=drop rate=%"PRId64" " | |
2214 | "burst_size=%"PRId64"", | |
2215 | cl->rate, cl->burst); | |
2216 | } else { | |
2217 | ds_put_format(&ds, "kbps stats bands=type=drop rate=%"PRId64"", | |
2218 | cl->rate); | |
2219 | } | |
2220 | ||
2221 | table_id = ovn_extend_table_assign_id(ep->meter_table, &ds); | |
2222 | if (table_id == EXT_TABLE_ID_INVALID) { | |
2223 | ds_destroy(&ds); | |
2224 | return; | |
2225 | } | |
2226 | ||
2227 | ds_destroy(&ds); | |
2228 | ||
2229 | /* Create an action to set the meter. */ | |
2230 | om = ofpact_put_METER(ofpacts); | |
2231 | om->meter_id = table_id; | |
2232 | } | |
2233 | ||
2234 | static void | |
2235 | ovnact_set_meter_free(struct ovnact_set_meter *ct OVS_UNUSED) | |
2236 | { | |
2237 | } | |
2238 | ||
d5a76da4 BP |
2239 | /* Parses an assignment or exchange or put_dhcp_opts action. */ |
2240 | static void | |
2241 | parse_set_action(struct action_context *ctx) | |
2242 | { | |
d5a76da4 | 2243 | struct expr_field lhs; |
9aef3c1b BP |
2244 | if (!expr_field_parse(ctx->lexer, ctx->pp->symtab, &lhs, &ctx->prereqs)) { |
2245 | return; | |
2246 | } | |
de297547 | 2247 | |
9aef3c1b BP |
2248 | if (lexer_match(ctx->lexer, LEX_T_EXCHANGE)) { |
2249 | parse_assignment_action(ctx, true, &lhs); | |
2250 | } else if (lexer_match(ctx->lexer, LEX_T_EQUALS)) { | |
2251 | if (ctx->lexer->token.type != LEX_T_ID) { | |
2252 | parse_LOAD(ctx, &lhs); | |
2253 | } else if (!strcmp(ctx->lexer->token.s, "put_dhcp_opts") | |
2254 | && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { | |
2255 | parse_put_dhcp_opts(ctx, &lhs, ovnact_put_PUT_DHCPV4_OPTS( | |
2256 | ctx->ovnacts)); | |
2257 | } else if (!strcmp(ctx->lexer->token.s, "put_dhcpv6_opts") | |
2258 | && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { | |
2259 | parse_put_dhcp_opts(ctx, &lhs, ovnact_put_PUT_DHCPV6_OPTS( | |
2260 | ctx->ovnacts)); | |
ea991ad2 NS |
2261 | } else if (!strcmp(ctx->lexer->token.s, "dns_lookup") |
2262 | && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { | |
2263 | parse_dns_lookup(ctx, &lhs, ovnact_put_DNS_LOOKUP(ctx->ovnacts)); | |
52ed5fcc NS |
2264 | } else if (!strcmp(ctx->lexer->token.s, "put_nd_ra_opts") |
2265 | && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { | |
2266 | parse_put_nd_ra_opts(ctx, &lhs, | |
2267 | ovnact_put_PUT_ND_RA_OPTS(ctx->ovnacts)); | |
de297547 | 2268 | } else { |
9aef3c1b | 2269 | parse_assignment_action(ctx, false, &lhs); |
de297547 | 2270 | } |
9aef3c1b BP |
2271 | } else { |
2272 | lexer_syntax_error(ctx->lexer, "expecting `=' or `<->'"); | |
de297547 | 2273 | } |
de297547 GS |
2274 | } |
2275 | ||
d8681a83 BP |
2276 | static bool |
2277 | parse_action(struct action_context *ctx) | |
2278 | { | |
2279 | if (ctx->lexer->token.type != LEX_T_ID) { | |
9aef3c1b | 2280 | lexer_syntax_error(ctx->lexer, NULL); |
d8681a83 BP |
2281 | return false; |
2282 | } | |
2283 | ||
2284 | enum lex_type lookahead = lexer_lookahead(ctx->lexer); | |
2285 | if (lookahead == LEX_T_EQUALS || lookahead == LEX_T_EXCHANGE | |
2286 | || lookahead == LEX_T_LSQUARE) { | |
2287 | parse_set_action(ctx); | |
2288 | } else if (lexer_match_id(ctx->lexer, "next")) { | |
d5a76da4 | 2289 | parse_NEXT(ctx); |
d8681a83 | 2290 | } else if (lexer_match_id(ctx->lexer, "output")) { |
d5a76da4 | 2291 | ovnact_put_OUTPUT(ctx->ovnacts); |
d8681a83 | 2292 | } else if (lexer_match_id(ctx->lexer, "ip.ttl")) { |
d5a76da4 | 2293 | parse_DEC_TTL(ctx); |
d8681a83 | 2294 | } else if (lexer_match_id(ctx->lexer, "ct_next")) { |
d5a76da4 | 2295 | parse_CT_NEXT(ctx); |
d8681a83 | 2296 | } else if (lexer_match_id(ctx->lexer, "ct_commit")) { |
d5a76da4 | 2297 | parse_CT_COMMIT(ctx); |
de297547 | 2298 | } else if (lexer_match_id(ctx->lexer, "ct_dnat")) { |
d5a76da4 | 2299 | parse_CT_DNAT(ctx); |
de297547 | 2300 | } else if (lexer_match_id(ctx->lexer, "ct_snat")) { |
d5a76da4 | 2301 | parse_CT_SNAT(ctx); |
467085fd GS |
2302 | } else if (lexer_match_id(ctx->lexer, "ct_lb")) { |
2303 | parse_ct_lb_action(ctx); | |
db0e819b BP |
2304 | } else if (lexer_match_id(ctx->lexer, "ct_clear")) { |
2305 | ovnact_put_CT_CLEAR(ctx->ovnacts); | |
b3bd2c33 BP |
2306 | } else if (lexer_match_id(ctx->lexer, "clone")) { |
2307 | parse_CLONE(ctx); | |
6335d074 | 2308 | } else if (lexer_match_id(ctx->lexer, "arp")) { |
d5a76da4 | 2309 | parse_ARP(ctx); |
bc3d6a63 LB |
2310 | } else if (lexer_match_id(ctx->lexer, "icmp4")) { |
2311 | parse_ICMP4(ctx); | |
3e7fa1e3 LB |
2312 | } else if (lexer_match_id(ctx->lexer, "icmp6")) { |
2313 | parse_ICMP6(ctx); | |
22b65e4d LB |
2314 | } else if (lexer_match_id(ctx->lexer, "tcp_reset")) { |
2315 | parse_TCP_RESET(ctx); | |
d5a76da4 BP |
2316 | } else if (lexer_match_id(ctx->lexer, "nd_na")) { |
2317 | parse_ND_NA(ctx); | |
b1a3a6a4 NS |
2318 | } else if (lexer_match_id(ctx->lexer, "nd_ns")) { |
2319 | parse_ND_NS(ctx); | |
0bac7164 | 2320 | } else if (lexer_match_id(ctx->lexer, "get_arp")) { |
d5a76da4 | 2321 | parse_get_mac_bind(ctx, 32, ovnact_put_GET_ARP(ctx->ovnacts)); |
0bac7164 | 2322 | } else if (lexer_match_id(ctx->lexer, "put_arp")) { |
d5a76da4 | 2323 | parse_put_mac_bind(ctx, 32, ovnact_put_PUT_ARP(ctx->ovnacts)); |
c34a87b6 | 2324 | } else if (lexer_match_id(ctx->lexer, "get_nd")) { |
d5a76da4 | 2325 | parse_get_mac_bind(ctx, 128, ovnact_put_GET_ND(ctx->ovnacts)); |
c34a87b6 | 2326 | } else if (lexer_match_id(ctx->lexer, "put_nd")) { |
d5a76da4 | 2327 | parse_put_mac_bind(ctx, 128, ovnact_put_PUT_ND(ctx->ovnacts)); |
a6095f81 BS |
2328 | } else if (lexer_match_id(ctx->lexer, "set_queue")) { |
2329 | parse_SET_QUEUE(ctx); | |
d383eed5 JP |
2330 | } else if (lexer_match_id(ctx->lexer, "log")) { |
2331 | parse_LOG(ctx); | |
66d89287 GL |
2332 | } else if (lexer_match_id(ctx->lexer, "set_meter")) { |
2333 | parse_set_meter_action(ctx); | |
d8681a83 | 2334 | } else { |
9aef3c1b | 2335 | lexer_syntax_error(ctx->lexer, "expecting action"); |
d8681a83 | 2336 | } |
9aef3c1b BP |
2337 | lexer_force_match(ctx->lexer, LEX_T_SEMICOLON); |
2338 | return !ctx->lexer->error; | |
d8681a83 BP |
2339 | } |
2340 | ||
3b7cb7e1 | 2341 | static void |
bac29564 | 2342 | parse_actions(struct action_context *ctx, enum lex_type sentinel) |
3b7cb7e1 BP |
2343 | { |
2344 | /* "drop;" by itself is a valid (empty) set of actions, but it can't be | |
2345 | * combined with other actions because that doesn't make sense. */ | |
2346 | if (ctx->lexer->token.type == LEX_T_ID | |
2347 | && !strcmp(ctx->lexer->token.s, "drop") | |
2348 | && lexer_lookahead(ctx->lexer) == LEX_T_SEMICOLON) { | |
2349 | lexer_get(ctx->lexer); /* Skip "drop". */ | |
2350 | lexer_get(ctx->lexer); /* Skip ";". */ | |
bac29564 | 2351 | lexer_force_match(ctx->lexer, sentinel); |
3b7cb7e1 BP |
2352 | return; |
2353 | } | |
2354 | ||
bac29564 | 2355 | while (!lexer_match(ctx->lexer, sentinel)) { |
d8681a83 | 2356 | if (!parse_action(ctx)) { |
3b7cb7e1 BP |
2357 | return; |
2358 | } | |
2359 | } | |
2360 | } | |
2361 | ||
2362 | /* Parses OVN actions, in the format described for the "actions" column in the | |
48605550 | 2363 | * Logical_Flow table in ovn-sb(5), and appends the parsed versions of the |
d5a76da4 BP |
2364 | * actions to 'ovnacts' as "struct ovnact"s. The caller must eventually free |
2365 | * the parsed ovnacts with ovnacts_free(). | |
3b7cb7e1 | 2366 | * |
d5a76da4 | 2367 | * 'pp' provides most of the parameters for translation. |
3646e396 | 2368 | * |
3b7cb7e1 BP |
2369 | * Some actions add extra requirements (prerequisites) to the flow's match. If |
2370 | * so, this function sets '*prereqsp' to the actions' prerequisites; otherwise, | |
2371 | * it sets '*prereqsp' to NULL. The caller owns '*prereqsp' and must | |
2372 | * eventually free it. | |
2373 | * | |
9aef3c1b BP |
2374 | * Returns true if successful, false if an error occurred. Upon return, |
2375 | * returns true if and only if lexer->error is NULL. | |
d5a76da4 | 2376 | */ |
9aef3c1b | 2377 | bool |
d5a76da4 BP |
2378 | ovnacts_parse(struct lexer *lexer, const struct ovnact_parse_params *pp, |
2379 | struct ofpbuf *ovnacts, struct expr **prereqsp) | |
3b7cb7e1 | 2380 | { |
d5a76da4 | 2381 | size_t ovnacts_start = ovnacts->size; |
3b7cb7e1 | 2382 | |
1d7b2ece | 2383 | struct action_context ctx = { |
d5a76da4 | 2384 | .pp = pp, |
1d7b2ece | 2385 | .lexer = lexer, |
d5a76da4 | 2386 | .ovnacts = ovnacts, |
1d7b2ece BP |
2387 | .prereqs = NULL, |
2388 | }; | |
9aef3c1b | 2389 | if (!lexer->error) { |
bac29564 | 2390 | parse_actions(&ctx, LEX_T_END); |
9aef3c1b | 2391 | } |
3b7cb7e1 | 2392 | |
9aef3c1b | 2393 | if (!lexer->error) { |
3b7cb7e1 | 2394 | *prereqsp = ctx.prereqs; |
9aef3c1b | 2395 | return true; |
3b7cb7e1 | 2396 | } else { |
d5a76da4 BP |
2397 | ofpbuf_pull(ovnacts, ovnacts_start); |
2398 | ovnacts_free(ovnacts->data, ovnacts->size); | |
2399 | ofpbuf_push_uninit(ovnacts, ovnacts_start); | |
2400 | ||
2401 | ovnacts->size = ovnacts_start; | |
3b7cb7e1 BP |
2402 | expr_destroy(ctx.prereqs); |
2403 | *prereqsp = NULL; | |
9aef3c1b | 2404 | return false; |
3b7cb7e1 BP |
2405 | } |
2406 | } | |
2407 | ||
d5a76da4 | 2408 | /* Like ovnacts_parse(), but the actions are taken from 's'. */ |
3b7cb7e1 | 2409 | char * OVS_WARN_UNUSED_RESULT |
d5a76da4 | 2410 | ovnacts_parse_string(const char *s, const struct ovnact_parse_params *pp, |
1d7b2ece | 2411 | struct ofpbuf *ofpacts, struct expr **prereqsp) |
3b7cb7e1 BP |
2412 | { |
2413 | struct lexer lexer; | |
3b7cb7e1 BP |
2414 | |
2415 | lexer_init(&lexer, s); | |
2416 | lexer_get(&lexer); | |
9aef3c1b BP |
2417 | ovnacts_parse(&lexer, pp, ofpacts, prereqsp); |
2418 | char *error = lexer_steal_error(&lexer); | |
3b7cb7e1 BP |
2419 | lexer_destroy(&lexer); |
2420 | ||
2421 | return error; | |
2422 | } | |
d5a76da4 BP |
2423 | \f |
2424 | /* Formatting ovnacts. */ | |
2425 | ||
2426 | static void | |
2427 | ovnact_format(const struct ovnact *a, struct ds *s) | |
2428 | { | |
2429 | switch (a->type) { | |
2430 | #define OVNACT(ENUM, STRUCT) \ | |
2431 | case OVNACT_##ENUM: \ | |
2432 | format_##ENUM(ALIGNED_CAST(const struct STRUCT *, a), s); \ | |
2433 | break; | |
2434 | OVNACTS | |
2435 | #undef OVNACT | |
2436 | default: | |
2437 | OVS_NOT_REACHED(); | |
2438 | } | |
2439 | } | |
2440 | ||
2441 | /* Appends a string representing the 'ovnacts_len' bytes of ovnacts in | |
2442 | * 'ovnacts' to 'string'. */ | |
2443 | void | |
2444 | ovnacts_format(const struct ovnact *ovnacts, size_t ovnacts_len, | |
2445 | struct ds *string) | |
2446 | { | |
2447 | if (!ovnacts_len) { | |
2448 | ds_put_cstr(string, "drop;"); | |
2449 | } else { | |
2450 | const struct ovnact *a; | |
2451 | ||
2452 | OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) { | |
2453 | if (a != ovnacts) { | |
2454 | ds_put_char(string, ' '); | |
2455 | } | |
2456 | ovnact_format(a, string); | |
2457 | } | |
2458 | } | |
2459 | } | |
2460 | \f | |
2461 | /* Encoding ovnacts to OpenFlow. */ | |
2462 | ||
2463 | static void | |
2464 | ovnact_encode(const struct ovnact *a, const struct ovnact_encode_params *ep, | |
2465 | struct ofpbuf *ofpacts) | |
2466 | { | |
2467 | switch (a->type) { | |
2468 | #define OVNACT(ENUM, STRUCT) \ | |
2469 | case OVNACT_##ENUM: \ | |
2470 | encode_##ENUM(ALIGNED_CAST(const struct STRUCT *, a), \ | |
2471 | ep, ofpacts); \ | |
2472 | break; | |
2473 | OVNACTS | |
2474 | #undef OVNACT | |
2475 | default: | |
2476 | OVS_NOT_REACHED(); | |
2477 | } | |
2478 | } | |
2479 | ||
2480 | /* Appends ofpacts to 'ofpacts' that represent the actions in the 'ovnacts_len' | |
2481 | * bytes of actions starting at 'ovnacts'. */ | |
2482 | void | |
2483 | ovnacts_encode(const struct ovnact *ovnacts, size_t ovnacts_len, | |
2484 | const struct ovnact_encode_params *ep, | |
2485 | struct ofpbuf *ofpacts) | |
2486 | { | |
2487 | if (ovnacts) { | |
2488 | const struct ovnact *a; | |
2489 | ||
2490 | OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) { | |
2491 | ovnact_encode(a, ep, ofpacts); | |
2492 | } | |
2493 | } | |
2494 | } | |
2495 | \f | |
2496 | /* Freeing ovnacts. */ | |
2497 | ||
2498 | static void | |
2499 | ovnact_free(struct ovnact *a) | |
2500 | { | |
2501 | switch (a->type) { | |
2502 | #define OVNACT(ENUM, STRUCT) \ | |
2503 | case OVNACT_##ENUM: \ | |
80b6743d | 2504 | STRUCT##_free(ALIGNED_CAST(struct STRUCT *, a)); \ |
d5a76da4 BP |
2505 | break; |
2506 | OVNACTS | |
2507 | #undef OVNACT | |
2508 | default: | |
2509 | OVS_NOT_REACHED(); | |
2510 | } | |
2511 | } | |
2512 | ||
2513 | /* Frees each of the actions in the 'ovnacts_len' bytes of actions starting at | |
2514 | * 'ovnacts'. | |
2515 | * | |
2516 | * Does not call free(ovnacts); the caller must do so if desirable. */ | |
2517 | void | |
2518 | ovnacts_free(struct ovnact *ovnacts, size_t ovnacts_len) | |
2519 | { | |
2520 | if (ovnacts) { | |
2521 | struct ovnact *a; | |
2522 | ||
2523 | OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) { | |
2524 | ovnact_free(a); | |
2525 | } | |
2526 | } | |
2527 | } |