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