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