ovn: Implement action to exchange two fields.
[cascardo/ovs.git] / ovn / lib / actions.c
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"
25 #include "ofp-actions.h"
26 #include "ofpbuf.h"
27
28 /* Context maintained during actions_parse(). */
29 struct action_context {
30     /* Input. */
31     struct lexer *lexer;        /* Lexer for pulling more tokens. */
32     const struct shash *symtab; /* Symbol table. */
33     uint8_t next_table_id;      /* OpenFlow table for 'next' to resubmit. */
34     uint8_t output_table_id;    /* OpenFlow table for 'output' to resubmit. */
35     const struct simap *ports;  /* Map from port name to number. */
36
37     /* State. */
38     char *error;                /* Error, if any, otherwise NULL. */
39
40     /* Output. */
41     struct ofpbuf *ofpacts;     /* Actions. */
42     struct expr *prereqs;       /* Prerequisites to apply to match. */
43 };
44
45 static bool
46 action_error_handle_common(struct action_context *ctx)
47 {
48     if (ctx->error) {
49         /* Already have an error, suppress this one since the cascade seems
50          * unlikely to be useful. */
51         return true;
52     } else if (ctx->lexer->token.type == LEX_T_ERROR) {
53         /* The lexer signaled an error.  Nothing at the action level
54          * accepts an error token, so we'll inevitably end up here with some
55          * meaningless parse error.  Report the lexical error instead. */
56         ctx->error = xstrdup(ctx->lexer->token.s);
57         return true;
58     } else {
59         return false;
60     }
61 }
62
63 static void OVS_PRINTF_FORMAT(2, 3)
64 action_error(struct action_context *ctx, const char *message, ...)
65 {
66     if (action_error_handle_common(ctx)) {
67         return;
68     }
69
70     va_list args;
71     va_start(args, message);
72     ctx->error = xvasprintf(message, args);
73     va_end(args);
74 }
75
76 static void OVS_PRINTF_FORMAT(2, 3)
77 action_syntax_error(struct action_context *ctx, const char *message, ...)
78 {
79     if (action_error_handle_common(ctx)) {
80         return;
81     }
82
83     struct ds s;
84
85     ds_init(&s);
86     ds_put_cstr(&s, "Syntax error");
87     if (ctx->lexer->token.type == LEX_T_END) {
88         ds_put_cstr(&s, " at end of input");
89     } else if (ctx->lexer->start) {
90         ds_put_format(&s, " at `%.*s'",
91                       (int) (ctx->lexer->input - ctx->lexer->start),
92                       ctx->lexer->start);
93     }
94
95     if (message) {
96         ds_put_char(&s, ' ');
97
98         va_list args;
99         va_start(args, message);
100         ds_put_format_valist(&s, message, args);
101         va_end(args);
102     }
103     ds_put_char(&s, '.');
104
105     ctx->error = ds_steal_cstr(&s);
106 }
107
108 /* Parses an assignment or exchange action. */
109 static void
110 parse_set_action(struct action_context *ctx)
111 {
112     struct expr *prereqs;
113     char *error;
114
115     error = expr_parse_assignment(ctx->lexer, ctx->symtab, ctx->ports,
116                                   ctx->ofpacts, &prereqs);
117     if (error) {
118         action_error(ctx, "%s", error);
119         free(error);
120         return;
121     }
122
123     ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, prereqs);
124 }
125
126 static void
127 emit_resubmit(struct action_context *ctx, uint8_t table_id)
128 {
129     struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(ctx->ofpacts);
130     resubmit->in_port = OFPP_IN_PORT;
131     resubmit->table_id = table_id;
132 }
133
134 static void
135 parse_actions(struct action_context *ctx)
136 {
137     /* "drop;" by itself is a valid (empty) set of actions, but it can't be
138      * combined with other actions because that doesn't make sense. */
139     if (ctx->lexer->token.type == LEX_T_ID
140         && !strcmp(ctx->lexer->token.s, "drop")
141         && lexer_lookahead(ctx->lexer) == LEX_T_SEMICOLON) {
142         lexer_get(ctx->lexer);  /* Skip "drop". */
143         lexer_get(ctx->lexer);  /* Skip ";". */
144         if (ctx->lexer->token.type != LEX_T_END) {
145             action_syntax_error(ctx, "expecting end of input");
146         }
147         return;
148     }
149
150     while (ctx->lexer->token.type != LEX_T_END) {
151         if (ctx->lexer->token.type != LEX_T_ID) {
152             action_syntax_error(ctx, NULL);
153             break;
154         }
155
156         enum lex_type lookahead = lexer_lookahead(ctx->lexer);
157         if (lookahead == LEX_T_EQUALS || lookahead == LEX_T_EXCHANGE
158             || lookahead == LEX_T_LSQUARE) {
159             parse_set_action(ctx);
160         } else if (lexer_match_id(ctx->lexer, "next")) {
161             if (ctx->next_table_id) {
162                 emit_resubmit(ctx, ctx->next_table_id);
163             } else {
164                 action_error(ctx, "\"next\" action not allowed here.");
165             }
166         } else if (lexer_match_id(ctx->lexer, "output")) {
167             emit_resubmit(ctx, ctx->output_table_id);
168         } else {
169             action_syntax_error(ctx, "expecting action");
170         }
171         if (!lexer_match(ctx->lexer, LEX_T_SEMICOLON)) {
172             action_syntax_error(ctx, "expecting ';'");
173         }
174         if (ctx->error) {
175             return;
176         }
177     }
178 }
179
180 /* Parses OVN actions, in the format described for the "actions" column in the
181  * Logical_Flow table in ovn-sb(5), and appends the parsed versions of the
182  * actions to 'ofpacts' as "struct ofpact"s.
183  *
184  * 'symtab' provides a table of "struct expr_symbol"s to support (as one would
185  * provide to expr_parse()).
186  *
187  * 'ports' must be a map from strings (presumably names of ports) to integers
188  * (as one would provide to expr_to_matches()).  Strings used in the actions
189  * that are not in 'ports' are translated to zero.
190  *
191  * 'next_table_id' should be the OpenFlow table to which the "next" action will
192  * resubmit, or 0 to disable "next".
193  *
194  * 'output_table_id' should be the OpenFlow table to which the "output" action
195  * will resubmit
196  *
197  * Some actions add extra requirements (prerequisites) to the flow's match.  If
198  * so, this function sets '*prereqsp' to the actions' prerequisites; otherwise,
199  * it sets '*prereqsp' to NULL.  The caller owns '*prereqsp' and must
200  * eventually free it.
201  *
202  * Returns NULL on success, otherwise a malloc()'d error message that the
203  * caller must free.  On failure, 'ofpacts' has the same contents and
204  * '*prereqsp' is set to NULL, but some tokens may have been consumed from
205  * 'lexer'.
206   */
207 char * OVS_WARN_UNUSED_RESULT
208 actions_parse(struct lexer *lexer, const struct shash *symtab,
209               const struct simap *ports, uint8_t next_table_id,
210               uint8_t output_table_id, struct ofpbuf *ofpacts,
211               struct expr **prereqsp)
212 {
213     size_t ofpacts_start = ofpacts->size;
214
215     struct action_context ctx;
216     ctx.lexer = lexer;
217     ctx.symtab = symtab;
218     ctx.ports = ports;
219     ctx.next_table_id = next_table_id;
220     ctx.output_table_id = output_table_id;
221     ctx.error = NULL;
222     ctx.ofpacts = ofpacts;
223     ctx.prereqs = NULL;
224
225     parse_actions(&ctx);
226
227     if (!ctx.error) {
228         *prereqsp = ctx.prereqs;
229         return NULL;
230     } else {
231         ofpacts->size = ofpacts_start;
232         expr_destroy(ctx.prereqs);
233         *prereqsp = NULL;
234         return ctx.error;
235     }
236 }
237
238 /* Like actions_parse(), but the actions are taken from 's'. */
239 char * OVS_WARN_UNUSED_RESULT
240 actions_parse_string(const char *s, const struct shash *symtab,
241                      const struct simap *ports, uint8_t next_table_id,
242                      uint8_t output_table_id, struct ofpbuf *ofpacts,
243                      struct expr **prereqsp)
244 {
245     struct lexer lexer;
246     char *error;
247
248     lexer_init(&lexer, s);
249     lexer_get(&lexer);
250     error = actions_parse(&lexer, symtab, ports, next_table_id,
251                           output_table_id, ofpacts, prereqsp);
252     lexer_destroy(&lexer);
253
254     return error;
255 }