Introduce ofpacts, an abstraction of OpenFlow actions.
[cascardo/ovs.git] / lib / bundle.c
1 /* Copyright (c) 2011, 2012 Nicira, Inc.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <config.h>
17
18 #include "bundle.h"
19
20 #include <arpa/inet.h>
21 #include <inttypes.h>
22
23 #include "dynamic-string.h"
24 #include "multipath.h"
25 #include "meta-flow.h"
26 #include "nx-match.h"
27 #include "ofpbuf.h"
28 #include "ofp-actions.h"
29 #include "ofp-errors.h"
30 #include "ofp-util.h"
31 #include "openflow/nicira-ext.h"
32 #include "vlog.h"
33
34 #define BUNDLE_MAX_SLAVES 2048
35
36 VLOG_DEFINE_THIS_MODULE(bundle);
37
38 static uint16_t
39 execute_ab(const struct ofpact_bundle *bundle,
40            bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
41 {
42     size_t i;
43
44     for (i = 0; i < bundle->n_slaves; i++) {
45         uint16_t slave = bundle->slaves[i];
46         if (slave_enabled(slave, aux)) {
47             return slave;
48         }
49     }
50
51     return OFPP_NONE;
52 }
53
54 static uint16_t
55 execute_hrw(const struct ofpact_bundle *bundle, const struct flow *flow,
56             bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
57 {
58     uint32_t flow_hash, best_hash;
59     int best, i;
60
61     flow_hash = flow_hash_fields(flow, bundle->fields, bundle->basis);
62     best = -1;
63     best_hash = 0;
64
65     for (i = 0; i < bundle->n_slaves; i++) {
66         if (slave_enabled(bundle->slaves[i], aux)) {
67             uint32_t hash = hash_2words(i, flow_hash);
68
69             if (best < 0 || hash > best_hash) {
70                 best_hash = hash;
71                 best = i;
72             }
73         }
74     }
75
76     return best >= 0 ? bundle->slaves[best] : OFPP_NONE;
77 }
78
79 /* Executes 'bundle' on 'flow'.  Uses 'slave_enabled' to determine if the slave
80  * designated by 'ofp_port' is up.  Returns the chosen slave, or OFPP_NONE if
81  * none of the slaves are acceptable. */
82 uint16_t
83 bundle_execute(const struct ofpact_bundle *bundle, const struct flow *flow,
84                bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
85 {
86     switch (bundle->algorithm) {
87     case NX_BD_ALG_HRW:
88         return execute_hrw(bundle, flow, slave_enabled, aux);
89
90     case NX_BD_ALG_ACTIVE_BACKUP:
91         return execute_ab(bundle, slave_enabled, aux);
92
93     default:
94         NOT_REACHED();
95     }
96 }
97
98 /* Checks that 'nab' specifies a bundle action which is supported by this
99  * bundle module.  Uses the 'max_ports' parameter to validate each port using
100  * ofputil_check_output_port().  Returns 0 if 'nab' is supported, otherwise an
101  * OFPERR_* error code. */
102 enum ofperr
103 bundle_from_openflow(const struct nx_action_bundle *nab,
104                      struct ofpbuf *ofpacts)
105 {
106     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
107     struct ofpact_bundle *bundle;
108     uint16_t subtype;
109     uint32_t slave_type;
110     size_t slaves_size, i;
111     enum ofperr error;
112
113     bundle = ofpact_put_BUNDLE(ofpacts);
114
115     subtype = ntohs(nab->subtype);
116     bundle->n_slaves = ntohs(nab->n_slaves);
117     bundle->basis = ntohs(nab->basis);
118     bundle->fields = ntohs(nab->fields);
119     bundle->algorithm = ntohs(nab->algorithm);
120     slave_type = ntohl(nab->slave_type);
121     slaves_size = ntohs(nab->len) - sizeof *nab;
122
123     error = OFPERR_OFPBAC_BAD_ARGUMENT;
124     if (!flow_hash_fields_valid(bundle->fields)) {
125         VLOG_WARN_RL(&rl, "unsupported fields %d", (int) bundle->fields);
126     } else if (bundle->n_slaves > BUNDLE_MAX_SLAVES) {
127         VLOG_WARN_RL(&rl, "too may slaves");
128     } else if (bundle->algorithm != NX_BD_ALG_HRW
129                && bundle->algorithm != NX_BD_ALG_ACTIVE_BACKUP) {
130         VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) bundle->algorithm);
131     } else if (slave_type != NXM_OF_IN_PORT) {
132         VLOG_WARN_RL(&rl, "unsupported slave type %"PRIu16, slave_type);
133     } else {
134         error = 0;
135     }
136
137     if (!is_all_zeros(nab->zero, sizeof nab->zero)) {
138         VLOG_WARN_RL(&rl, "reserved field is nonzero");
139         error = OFPERR_OFPBAC_BAD_ARGUMENT;
140     }
141
142     if (subtype == NXAST_BUNDLE && (nab->ofs_nbits || nab->dst)) {
143         VLOG_WARN_RL(&rl, "bundle action has nonzero reserved fields");
144         error = OFPERR_OFPBAC_BAD_ARGUMENT;
145     }
146
147     if (subtype == NXAST_BUNDLE_LOAD) {
148         bundle->dst.field = mf_from_nxm_header(ntohl(nab->dst));
149         bundle->dst.ofs = nxm_decode_ofs(nab->ofs_nbits);
150         bundle->dst.n_bits = nxm_decode_n_bits(nab->ofs_nbits);
151
152         if (bundle->dst.n_bits < 16) {
153             VLOG_WARN_RL(&rl, "bundle_load action requires at least 16 bit "
154                          "destination.");
155             error = OFPERR_OFPBAC_BAD_ARGUMENT;
156         }
157     }
158
159     if (slaves_size < bundle->n_slaves * sizeof(ovs_be16)) {
160         VLOG_WARN_RL(&rl, "Nicira action %"PRIu16" only has %zu bytes "
161                      "allocated for slaves.  %zu bytes are required for "
162                      "%"PRIu16" slaves.", subtype, slaves_size,
163                      bundle->n_slaves * sizeof(ovs_be16), bundle->n_slaves);
164         error = OFPERR_OFPBAC_BAD_LEN;
165     }
166
167     for (i = 0; i < bundle->n_slaves; i++) {
168         uint16_t ofp_port = ntohs(((ovs_be16 *)(nab + 1))[i]);
169         ofpbuf_put(ofpacts, &ofp_port, sizeof ofp_port);
170     }
171
172     bundle = ofpacts->l2;
173     ofpact_update_len(ofpacts, &bundle->ofpact);
174
175     if (!error) {
176         error = bundle_check(bundle, OFPP_MAX, NULL);
177     }
178     return error;
179 }
180
181 enum ofperr
182 bundle_check(const struct ofpact_bundle *bundle, int max_ports,
183              const struct flow *flow)
184 {
185     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
186     size_t i;
187
188     if (bundle->dst.field) {
189         enum ofperr error = mf_check_dst(&bundle->dst, flow);
190         if (error) {
191             return error;
192         }
193     }
194
195     for (i = 0; i < bundle->n_slaves; i++) {
196         uint16_t ofp_port = bundle->slaves[i];
197         enum ofperr error;
198
199         error = ofputil_check_output_port(ofp_port, max_ports);
200         if (error) {
201             VLOG_WARN_RL(&rl, "invalid slave %"PRIu16, ofp_port);
202             return error;
203         }
204
205         /* Controller slaves are unsupported due to the lack of a max_len
206          * argument. This may or may not change in the future.  There doesn't
207          * seem to be a real-world use-case for supporting it. */
208         if (ofp_port == OFPP_CONTROLLER) {
209             VLOG_WARN_RL(&rl, "unsupported controller slave");
210             return OFPERR_OFPBAC_BAD_OUT_PORT;
211         }
212     }
213
214     return 0;
215 }
216
217 void
218 bundle_to_nxast(const struct ofpact_bundle *bundle, struct ofpbuf *openflow)
219 {
220     int slaves_len = ROUND_UP(bundle->n_slaves, OFP_ACTION_ALIGN);
221     struct nx_action_bundle *nab;
222     ovs_be16 *slaves;
223     size_t i;
224
225     nab = (bundle->dst.field
226            ? ofputil_put_NXAST_BUNDLE_LOAD(openflow)
227            : ofputil_put_NXAST_BUNDLE(openflow));
228     nab->len = htons(ntohs(nab->len) + slaves_len);
229     nab->algorithm = htons(bundle->algorithm);
230     nab->fields = htons(bundle->fields);
231     nab->basis = htons(bundle->basis);
232     nab->slave_type = htonl(NXM_OF_IN_PORT);
233     nab->n_slaves = htons(bundle->n_slaves);
234     if (bundle->dst.field) {
235         nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs,
236                                               bundle->dst.n_bits);
237         nab->dst = htonl(bundle->dst.field->nxm_header);
238     }
239
240     slaves = ofpbuf_put_zeros(openflow, slaves_len);
241     for (i = 0; i < bundle->n_slaves; i++) {
242         slaves[i] = htons(bundle->slaves[i]);
243     }
244 }
245
246 /* Helper for bundle_parse and bundle_parse_load. */
247 static void
248 bundle_parse__(const char *s, char **save_ptr,
249                const char *fields, const char *basis, const char *algorithm,
250                const char *slave_type, const char *dst,
251                const char *slave_delim, struct ofpbuf *ofpacts)
252 {
253     struct ofpact_bundle *bundle;
254
255     if (!slave_delim) {
256         ovs_fatal(0, "%s: not enough arguments to bundle action", s);
257     }
258
259     if (strcasecmp(slave_delim, "slaves")) {
260         ovs_fatal(0, "%s: missing slave delimiter, expected `slaves' got `%s'",
261                    s, slave_delim);
262     }
263
264     bundle = ofpact_put_BUNDLE(ofpacts);
265
266     for (;;) {
267         uint16_t slave_port;
268         char *slave;
269
270         slave = strtok_r(NULL, ", [", save_ptr);
271         if (!slave || bundle->n_slaves >= BUNDLE_MAX_SLAVES) {
272             break;
273         }
274
275         slave_port = atoi(slave);
276         ofpbuf_put(ofpacts, &slave_port, sizeof slave_port);
277
278         bundle = ofpacts->l2;
279         bundle->n_slaves++;
280     }
281     ofpact_update_len(ofpacts, &bundle->ofpact);
282
283     bundle->basis = atoi(basis);
284
285     if (!strcasecmp(fields, "eth_src")) {
286         bundle->fields = NX_HASH_FIELDS_ETH_SRC;
287     } else if (!strcasecmp(fields, "symmetric_l4")) {
288         bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
289     } else {
290         ovs_fatal(0, "%s: unknown fields `%s'", s, fields);
291     }
292
293     if (!strcasecmp(algorithm, "active_backup")) {
294         bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP;
295     } else if (!strcasecmp(algorithm, "hrw")) {
296         bundle->algorithm = NX_BD_ALG_HRW;
297     } else {
298         ovs_fatal(0, "%s: unknown algorithm `%s'", s, algorithm);
299     }
300
301     if (strcasecmp(slave_type, "ofport")) {
302         ovs_fatal(0, "%s: unknown slave_type `%s'", s, slave_type);
303     }
304
305     if (dst) {
306         mf_parse_subfield(&bundle->dst, dst);
307     }
308 }
309
310 /* Converts a bundle action string contained in 's' to an nx_action_bundle and
311  * stores it in 'b'.  Sets 'b''s l2 pointer to NULL. */
312 void
313 bundle_parse(const char *s, struct ofpbuf *ofpacts)
314 {
315     char *fields, *basis, *algorithm, *slave_type, *slave_delim;
316     char *tokstr, *save_ptr;
317
318     save_ptr = NULL;
319     tokstr = xstrdup(s);
320     fields = strtok_r(tokstr, ", ", &save_ptr);
321     basis = strtok_r(NULL, ", ", &save_ptr);
322     algorithm = strtok_r(NULL, ", ", &save_ptr);
323     slave_type = strtok_r(NULL, ", ", &save_ptr);
324     slave_delim = strtok_r(NULL, ": ", &save_ptr);
325
326     bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, NULL,
327                    slave_delim, ofpacts);
328     free(tokstr);
329 }
330
331 /* Converts a bundle_load action string contained in 's' to an nx_action_bundle
332  * and stores it in 'b'.  Sets 'b''s l2 pointer to NULL. */
333 void
334 bundle_parse_load(const char *s, struct ofpbuf *ofpacts)
335 {
336     char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim;
337     char *tokstr, *save_ptr;
338
339     save_ptr = NULL;
340     tokstr = xstrdup(s);
341     fields = strtok_r(tokstr, ", ", &save_ptr);
342     basis = strtok_r(NULL, ", ", &save_ptr);
343     algorithm = strtok_r(NULL, ", ", &save_ptr);
344     slave_type = strtok_r(NULL, ", ", &save_ptr);
345     dst = strtok_r(NULL, ", ", &save_ptr);
346     slave_delim = strtok_r(NULL, ": ", &save_ptr);
347
348     bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, dst,
349                    slave_delim, ofpacts);
350
351     free(tokstr);
352 }
353
354 /* Appends a human-readable representation of 'nab' to 's'. */
355 void
356 bundle_format(const struct ofpact_bundle *bundle, struct ds *s)
357 {
358     const char *action, *fields, *algorithm;
359     size_t i;
360
361     fields = flow_hash_fields_to_str(bundle->fields);
362
363     switch (bundle->algorithm) {
364     case NX_BD_ALG_HRW:
365         algorithm = "hrw";
366         break;
367     case NX_BD_ALG_ACTIVE_BACKUP:
368         algorithm = "active_backup";
369         break;
370     default:
371         algorithm = "<unknown>";
372     }
373
374     action = bundle->dst.field ? "bundle_load" : "bundle";
375
376     ds_put_format(s, "%s(%s,%"PRIu16",%s,%s,", action, fields,
377                   bundle->basis, algorithm, "ofport");
378
379     if (bundle->dst.field) {
380         mf_format_subfield(&bundle->dst, s);
381         ds_put_cstr(s, ",");
382     }
383
384     ds_put_cstr(s, "slaves:");
385     for (i = 0; i < bundle->n_slaves; i++) {
386         if (i) {
387             ds_put_cstr(s, ",");
388         }
389
390         ds_put_format(s, "%"PRIu16, bundle->slaves[i]);
391     }
392
393     ds_put_cstr(s, ")");
394 }