]> git.proxmox.com Git - ovs.git/blame - ovn/lib/actions.c
actions: Bundle action parsing parameters into a structure.
[ovs.git] / ovn / lib / actions.c
CommitLineData
3b7cb7e1
BP
1/*
2 * Copyright (c) 2015 Nicira, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <config.h>
18#include "actions.h"
19#include <stdarg.h>
20#include <stdbool.h>
21#include "compiler.h"
22#include "dynamic-string.h"
23#include "expr.h"
24#include "lex.h"
667e2b0b 25#include "logical-fields.h"
3b7cb7e1
BP
26#include "ofp-actions.h"
27#include "ofpbuf.h"
78aab811 28#include "simap.h"
3b7cb7e1
BP
29
30/* Context maintained during actions_parse(). */
31struct action_context {
1d7b2ece 32 const struct action_params *ap; /* Parameters. */
3b7cb7e1 33 struct lexer *lexer; /* Lexer for pulling more tokens. */
3b7cb7e1 34 char *error; /* Error, if any, otherwise NULL. */
3b7cb7e1
BP
35 struct ofpbuf *ofpacts; /* Actions. */
36 struct expr *prereqs; /* Prerequisites to apply to match. */
37};
38
d8681a83
BP
39static bool parse_action(struct action_context *);
40
3b7cb7e1
BP
41static bool
42action_error_handle_common(struct action_context *ctx)
43{
44 if (ctx->error) {
45 /* Already have an error, suppress this one since the cascade seems
46 * unlikely to be useful. */
47 return true;
48 } else if (ctx->lexer->token.type == LEX_T_ERROR) {
49 /* The lexer signaled an error. Nothing at the action level
50 * accepts an error token, so we'll inevitably end up here with some
51 * meaningless parse error. Report the lexical error instead. */
52 ctx->error = xstrdup(ctx->lexer->token.s);
53 return true;
54 } else {
55 return false;
56 }
57}
58
59static void OVS_PRINTF_FORMAT(2, 3)
60action_error(struct action_context *ctx, const char *message, ...)
61{
62 if (action_error_handle_common(ctx)) {
63 return;
64 }
65
66 va_list args;
67 va_start(args, message);
68 ctx->error = xvasprintf(message, args);
69 va_end(args);
70}
71
72static void OVS_PRINTF_FORMAT(2, 3)
73action_syntax_error(struct action_context *ctx, const char *message, ...)
74{
75 if (action_error_handle_common(ctx)) {
76 return;
77 }
78
79 struct ds s;
80
81 ds_init(&s);
82 ds_put_cstr(&s, "Syntax error");
83 if (ctx->lexer->token.type == LEX_T_END) {
84 ds_put_cstr(&s, " at end of input");
85 } else if (ctx->lexer->start) {
86 ds_put_format(&s, " at `%.*s'",
87 (int) (ctx->lexer->input - ctx->lexer->start),
88 ctx->lexer->start);
89 }
90
91 if (message) {
92 ds_put_char(&s, ' ');
93
94 va_list args;
95 va_start(args, message);
96 ds_put_format_valist(&s, message, args);
97 va_end(args);
98 }
99 ds_put_char(&s, '.');
100
101 ctx->error = ds_steal_cstr(&s);
102}
103
a20c96c6 104/* Parses an assignment or exchange action. */
3b7cb7e1
BP
105static void
106parse_set_action(struct action_context *ctx)
107{
108 struct expr *prereqs;
109 char *error;
110
1d7b2ece 111 error = expr_parse_assignment(ctx->lexer, ctx->ap->symtab, ctx->ap->ports,
3b7cb7e1
BP
112 ctx->ofpacts, &prereqs);
113 if (error) {
114 action_error(ctx, "%s", error);
115 free(error);
116 return;
117 }
118
119 ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, prereqs);
120}
121
122static void
123emit_resubmit(struct action_context *ctx, uint8_t table_id)
124{
125 struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(ctx->ofpacts);
126 resubmit->in_port = OFPP_IN_PORT;
127 resubmit->table_id = table_id;
128}
129
558ec83d
BP
130static bool
131action_get_int(struct action_context *ctx, int *value)
132{
133 bool ok = lexer_get_int(ctx->lexer, value);
134 if (!ok) {
135 action_syntax_error(ctx, "expecting small integer");
136 }
137 return ok;
138}
139
140static void
141parse_next_action(struct action_context *ctx)
142{
1d7b2ece 143 if (!ctx->ap->n_tables) {
558ec83d
BP
144 action_error(ctx, "\"next\" action not allowed here.");
145 } else if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
146 int ltable;
147
148 if (!action_get_int(ctx, &ltable)) {
149 return;
150 }
151 if (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
152 action_syntax_error(ctx, "expecting `)'");
153 return;
154 }
155
1d7b2ece 156 if (ltable >= ctx->ap->n_tables) {
558ec83d 157 action_error(ctx, "\"next\" argument must be in range 0 to %d.",
1d7b2ece 158 ctx->ap->n_tables - 1);
558ec83d
BP
159 return;
160 }
161
1d7b2ece 162 emit_resubmit(ctx, ctx->ap->first_ptable + ltable);
558ec83d 163 } else {
1d7b2ece
BP
164 if (ctx->ap->cur_ltable < ctx->ap->n_tables) {
165 emit_resubmit(ctx,
166 ctx->ap->first_ptable + ctx->ap->cur_ltable + 1);
558ec83d
BP
167 } else {
168 action_error(ctx, "\"next\" action not allowed in last table.");
169 }
170 }
171}
172
5b84185b
BP
173/* Parses 'prerequisite' as an expression in the context of 'ctx', then adds it
174 * as a conjunction with the existing 'ctx->prereqs'. */
175static void
176add_prerequisite(struct action_context *ctx, const char *prerequisite)
177{
178 struct expr *expr;
179 char *error;
180
1d7b2ece 181 expr = expr_parse_string(prerequisite, ctx->ap->symtab, &error);
5b84185b
BP
182 ovs_assert(!error);
183 ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr);
184}
185
78aab811
JP
186static void
187emit_ct(struct action_context *ctx, bool recirc_next, bool commit)
188{
189 struct ofpact_conntrack *ct = ofpact_put_CT(ctx->ofpacts);
190 ct->flags |= commit ? NX_CT_F_COMMIT : 0;
191
192 /* If "recirc" is set, we automatically go to the next table. */
193 if (recirc_next) {
1d7b2ece
BP
194 if (ctx->ap->cur_ltable < ctx->ap->n_tables) {
195 ct->recirc_table = ctx->ap->first_ptable + ctx->ap->cur_ltable + 1;
78aab811
JP
196 } else {
197 action_error(ctx, "\"ct_next\" action not allowed in last table.");
198 return;
199 }
200 } else {
201 ct->recirc_table = NX_CT_RECIRC_NONE;
202 }
203
667e2b0b 204 ct->zone_src.field = mf_from_id(MFF_LOG_CT_ZONE);
78aab811
JP
205 ct->zone_src.ofs = 0;
206 ct->zone_src.n_bits = 16;
207
208 /* We do not support ALGs yet. */
209 ct->alg = 0;
210
211 /* CT only works with IP, so set up a prerequisite. */
5b84185b 212 add_prerequisite(ctx, "ip");
78aab811
JP
213}
214
d8681a83
BP
215static bool
216parse_action(struct action_context *ctx)
217{
218 if (ctx->lexer->token.type != LEX_T_ID) {
219 action_syntax_error(ctx, NULL);
220 return false;
221 }
222
223 enum lex_type lookahead = lexer_lookahead(ctx->lexer);
224 if (lookahead == LEX_T_EQUALS || lookahead == LEX_T_EXCHANGE
225 || lookahead == LEX_T_LSQUARE) {
226 parse_set_action(ctx);
227 } else if (lexer_match_id(ctx->lexer, "next")) {
228 parse_next_action(ctx);
229 } else if (lexer_match_id(ctx->lexer, "output")) {
1d7b2ece 230 emit_resubmit(ctx, ctx->ap->output_ptable);
d8681a83
BP
231 } else if (lexer_match_id(ctx->lexer, "ip.ttl")) {
232 if (lexer_match(ctx->lexer, LEX_T_DECREMENT)) {
233 add_prerequisite(ctx, "ip");
234 ofpact_put_DEC_TTL(ctx->ofpacts);
235 } else {
236 action_syntax_error(ctx, "expecting `--'");
237 }
238 } else if (lexer_match_id(ctx->lexer, "ct_next")) {
239 emit_ct(ctx, true, false);
240 } else if (lexer_match_id(ctx->lexer, "ct_commit")) {
241 emit_ct(ctx, false, true);
242 } else {
243 action_syntax_error(ctx, "expecting action");
244 }
245 if (!lexer_match(ctx->lexer, LEX_T_SEMICOLON)) {
246 action_syntax_error(ctx, "expecting ';'");
247 }
248 return !ctx->error;
249}
250
3b7cb7e1
BP
251static void
252parse_actions(struct action_context *ctx)
253{
254 /* "drop;" by itself is a valid (empty) set of actions, but it can't be
255 * combined with other actions because that doesn't make sense. */
256 if (ctx->lexer->token.type == LEX_T_ID
257 && !strcmp(ctx->lexer->token.s, "drop")
258 && lexer_lookahead(ctx->lexer) == LEX_T_SEMICOLON) {
259 lexer_get(ctx->lexer); /* Skip "drop". */
260 lexer_get(ctx->lexer); /* Skip ";". */
261 if (ctx->lexer->token.type != LEX_T_END) {
262 action_syntax_error(ctx, "expecting end of input");
263 }
264 return;
265 }
266
267 while (ctx->lexer->token.type != LEX_T_END) {
d8681a83 268 if (!parse_action(ctx)) {
3b7cb7e1
BP
269 return;
270 }
271 }
272}
273
274/* Parses OVN actions, in the format described for the "actions" column in the
48605550
BP
275 * Logical_Flow table in ovn-sb(5), and appends the parsed versions of the
276 * actions to 'ofpacts' as "struct ofpact"s.
3b7cb7e1 277 *
1d7b2ece 278 * 'ap' provides most of the parameters for translation.
3646e396 279 *
3b7cb7e1
BP
280 * Some actions add extra requirements (prerequisites) to the flow's match. If
281 * so, this function sets '*prereqsp' to the actions' prerequisites; otherwise,
282 * it sets '*prereqsp' to NULL. The caller owns '*prereqsp' and must
283 * eventually free it.
284 *
285 * Returns NULL on success, otherwise a malloc()'d error message that the
286 * caller must free. On failure, 'ofpacts' has the same contents and
287 * '*prereqsp' is set to NULL, but some tokens may have been consumed from
288 * 'lexer'.
289 */
290char * OVS_WARN_UNUSED_RESULT
1d7b2ece
BP
291actions_parse(struct lexer *lexer, const struct action_params *ap,
292 struct ofpbuf *ofpacts, struct expr **prereqsp)
3b7cb7e1
BP
293{
294 size_t ofpacts_start = ofpacts->size;
295
1d7b2ece
BP
296 struct action_context ctx = {
297 .ap = ap,
298 .lexer = lexer,
299 .error = NULL,
300 .ofpacts = ofpacts,
301 .prereqs = NULL,
302 };
3b7cb7e1
BP
303 parse_actions(&ctx);
304
305 if (!ctx.error) {
306 *prereqsp = ctx.prereqs;
307 return NULL;
308 } else {
309 ofpacts->size = ofpacts_start;
310 expr_destroy(ctx.prereqs);
311 *prereqsp = NULL;
312 return ctx.error;
313 }
314}
315
316/* Like actions_parse(), but the actions are taken from 's'. */
317char * OVS_WARN_UNUSED_RESULT
1d7b2ece
BP
318actions_parse_string(const char *s, const struct action_params *ap,
319 struct ofpbuf *ofpacts, struct expr **prereqsp)
3b7cb7e1
BP
320{
321 struct lexer lexer;
322 char *error;
323
324 lexer_init(&lexer, s);
325 lexer_get(&lexer);
1d7b2ece 326 error = actions_parse(&lexer, ap, ofpacts, prereqsp);
3b7cb7e1
BP
327 lexer_destroy(&lexer);
328
329 return error;
330}