]>
Commit | Line | Data |
---|---|---|
c2d936a4 | 1 | /* Copyright (c) 2011, 2012, 2013, 2014 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" | |
e6211adc | 32 | #include "openvswitch/vlog.h" |
daff3353 | 33 | |
daff3353 EJ |
34 | VLOG_DEFINE_THIS_MODULE(bundle); |
35 | ||
4e022ec0 | 36 | static ofp_port_t |
f25d0cf3 | 37 | execute_ab(const struct ofpact_bundle *bundle, |
4e022ec0 | 38 | bool (*slave_enabled)(ofp_port_t ofp_port, void *aux), void *aux) |
04a85497 EJ |
39 | { |
40 | size_t i; | |
41 | ||
f25d0cf3 | 42 | for (i = 0; i < bundle->n_slaves; i++) { |
4e022ec0 | 43 | ofp_port_t slave = bundle->slaves[i]; |
04a85497 EJ |
44 | if (slave_enabled(slave, aux)) { |
45 | return slave; | |
46 | } | |
47 | } | |
48 | ||
49 | return OFPP_NONE; | |
50 | } | |
51 | ||
4e022ec0 | 52 | static ofp_port_t |
bcd2633a JP |
53 | execute_hrw(const struct ofpact_bundle *bundle, |
54 | const struct flow *flow, struct flow_wildcards *wc, | |
4e022ec0 | 55 | bool (*slave_enabled)(ofp_port_t ofp_port, void *aux), void *aux) |
daff3353 EJ |
56 | { |
57 | uint32_t flow_hash, best_hash; | |
58 | int best, i; | |
59 | ||
bcd2633a | 60 | if (bundle->n_slaves > 1) { |
6cdd5145 | 61 | flow_mask_hash_fields(flow, wc, bundle->fields); |
bcd2633a JP |
62 | } |
63 | ||
f25d0cf3 | 64 | flow_hash = flow_hash_fields(flow, bundle->fields, bundle->basis); |
daff3353 | 65 | best = -1; |
481db488 | 66 | best_hash = 0; |
daff3353 | 67 | |
f25d0cf3 BP |
68 | for (i = 0; i < bundle->n_slaves; i++) { |
69 | if (slave_enabled(bundle->slaves[i], aux)) { | |
daff3353 EJ |
70 | uint32_t hash = hash_2words(i, flow_hash); |
71 | ||
72 | if (best < 0 || hash > best_hash) { | |
73 | best_hash = hash; | |
74 | best = i; | |
75 | } | |
76 | } | |
77 | } | |
78 | ||
f25d0cf3 | 79 | return best >= 0 ? bundle->slaves[best] : OFPP_NONE; |
daff3353 EJ |
80 | } |
81 | ||
bcd2633a JP |
82 | /* Executes 'bundle' on 'flow'. Sets fields in 'wc' that were used to |
83 | * calculate the result. Uses 'slave_enabled' to determine if the slave | |
84 | * designated by 'ofp_port' is up. Returns the chosen slave, or | |
85 | * OFPP_NONE if none of the slaves are acceptable. */ | |
4e022ec0 | 86 | ofp_port_t |
bcd2633a JP |
87 | bundle_execute(const struct ofpact_bundle *bundle, |
88 | const struct flow *flow, struct flow_wildcards *wc, | |
4e022ec0 AW |
89 | bool (*slave_enabled)(ofp_port_t ofp_port, void *aux), |
90 | void *aux) | |
04a85497 | 91 | { |
f25d0cf3 BP |
92 | switch (bundle->algorithm) { |
93 | case NX_BD_ALG_HRW: | |
bcd2633a | 94 | return execute_hrw(bundle, flow, wc, slave_enabled, aux); |
04a85497 | 95 | |
f25d0cf3 BP |
96 | case NX_BD_ALG_ACTIVE_BACKUP: |
97 | return execute_ab(bundle, slave_enabled, aux); | |
816fd533 | 98 | |
f25d0cf3 | 99 | default: |
428b2edd | 100 | OVS_NOT_REACHED(); |
f25d0cf3 | 101 | } |
a368bb53 EJ |
102 | } |
103 | ||
f25d0cf3 | 104 | enum ofperr |
4e022ec0 | 105 | bundle_check(const struct ofpact_bundle *bundle, ofp_port_t max_ports, |
f25d0cf3 BP |
106 | const struct flow *flow) |
107 | { | |
108 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
109 | size_t i; | |
110 | ||
111 | if (bundle->dst.field) { | |
112 | enum ofperr error = mf_check_dst(&bundle->dst, flow); | |
113 | if (error) { | |
114 | return error; | |
115 | } | |
116 | } | |
117 | ||
118 | for (i = 0; i < bundle->n_slaves; i++) { | |
4e022ec0 | 119 | ofp_port_t ofp_port = bundle->slaves[i]; |
f25d0cf3 | 120 | enum ofperr error; |
daff3353 | 121 | |
57ad4e9e | 122 | error = ofpact_check_output_port(ofp_port, max_ports); |
f25d0cf3 | 123 | if (error) { |
daff3353 | 124 | VLOG_WARN_RL(&rl, "invalid slave %"PRIu16, ofp_port); |
f25d0cf3 | 125 | return error; |
daff3353 EJ |
126 | } |
127 | ||
128 | /* Controller slaves are unsupported due to the lack of a max_len | |
129 | * argument. This may or may not change in the future. There doesn't | |
130 | * seem to be a real-world use-case for supporting it. */ | |
131 | if (ofp_port == OFPP_CONTROLLER) { | |
132 | VLOG_WARN_RL(&rl, "unsupported controller slave"); | |
f25d0cf3 | 133 | return OFPERR_OFPBAC_BAD_OUT_PORT; |
daff3353 EJ |
134 | } |
135 | } | |
136 | ||
f25d0cf3 BP |
137 | return 0; |
138 | } | |
139 | ||
daff3353 | 140 | |
bdda5aca BP |
141 | /* Helper for bundle_parse and bundle_parse_load. |
142 | * | |
143 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
144 | * error. The caller is responsible for freeing the returned string.*/ | |
cab50449 | 145 | static char * OVS_WARN_UNUSED_RESULT |
f25d0cf3 | 146 | bundle_parse__(const char *s, char **save_ptr, |
a368bb53 | 147 | const char *fields, const char *basis, const char *algorithm, |
f25d0cf3 BP |
148 | const char *slave_type, const char *dst, |
149 | const char *slave_delim, struct ofpbuf *ofpacts) | |
daff3353 | 150 | { |
f25d0cf3 | 151 | struct ofpact_bundle *bundle; |
daff3353 | 152 | |
daff3353 | 153 | if (!slave_delim) { |
bdda5aca | 154 | return xasprintf("%s: not enough arguments to bundle action", s); |
daff3353 EJ |
155 | } |
156 | ||
157 | if (strcasecmp(slave_delim, "slaves")) { | |
bdda5aca BP |
158 | return xasprintf("%s: missing slave delimiter, expected `slaves' " |
159 | "got `%s'", s, slave_delim); | |
daff3353 EJ |
160 | } |
161 | ||
f25d0cf3 | 162 | bundle = ofpact_put_BUNDLE(ofpacts); |
daff3353 | 163 | |
daff3353 | 164 | for (;;) { |
4e022ec0 | 165 | ofp_port_t slave_port; |
daff3353 EJ |
166 | char *slave; |
167 | ||
c6100d92 | 168 | slave = strtok_r(NULL, ", []", save_ptr); |
f25d0cf3 | 169 | if (!slave || bundle->n_slaves >= BUNDLE_MAX_SLAVES) { |
daff3353 EJ |
170 | break; |
171 | } | |
172 | ||
8010100b | 173 | if (!ofputil_port_from_string(slave, &slave_port)) { |
bdda5aca | 174 | return xasprintf("%s: bad port number", slave); |
c6100d92 | 175 | } |
f25d0cf3 | 176 | ofpbuf_put(ofpacts, &slave_port, sizeof slave_port); |
daff3353 | 177 | |
6fd6ed71 | 178 | bundle = ofpacts->header; |
f25d0cf3 | 179 | bundle->n_slaves++; |
daff3353 | 180 | } |
f25d0cf3 | 181 | ofpact_update_len(ofpacts, &bundle->ofpact); |
daff3353 | 182 | |
f25d0cf3 | 183 | bundle->basis = atoi(basis); |
daff3353 EJ |
184 | |
185 | if (!strcasecmp(fields, "eth_src")) { | |
f25d0cf3 | 186 | bundle->fields = NX_HASH_FIELDS_ETH_SRC; |
daff3353 | 187 | } else if (!strcasecmp(fields, "symmetric_l4")) { |
f25d0cf3 | 188 | bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L4; |
daff3353 | 189 | } else { |
bdda5aca | 190 | return xasprintf("%s: unknown fields `%s'", s, fields); |
daff3353 EJ |
191 | } |
192 | ||
193 | if (!strcasecmp(algorithm, "active_backup")) { | |
f25d0cf3 | 194 | bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP; |
daff3353 | 195 | } else if (!strcasecmp(algorithm, "hrw")) { |
f25d0cf3 | 196 | bundle->algorithm = NX_BD_ALG_HRW; |
daff3353 | 197 | } else { |
bdda5aca | 198 | return xasprintf("%s: unknown algorithm `%s'", s, algorithm); |
daff3353 EJ |
199 | } |
200 | ||
f25d0cf3 | 201 | if (strcasecmp(slave_type, "ofport")) { |
bdda5aca | 202 | return xasprintf("%s: unknown slave_type `%s'", s, slave_type); |
daff3353 EJ |
203 | } |
204 | ||
f25d0cf3 | 205 | if (dst) { |
bdda5aca BP |
206 | char *error = mf_parse_subfield(&bundle->dst, dst); |
207 | if (error) { | |
208 | return error; | |
209 | } | |
bad8a439 BP |
210 | |
211 | if (!mf_nxm_header(bundle->dst.field->id)) { | |
212 | return xasprintf("%s: experimenter OXM field '%s' not supported", | |
213 | s, dst); | |
214 | } | |
a368bb53 | 215 | } |
bdda5aca BP |
216 | |
217 | return NULL; | |
a368bb53 EJ |
218 | } |
219 | ||
220 | /* Converts a bundle action string contained in 's' to an nx_action_bundle and | |
bdda5aca BP |
221 | * stores it in 'b'. Sets 'b''s l2 pointer to NULL. |
222 | * | |
223 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
224 | * error. The caller is responsible for freeing the returned string. */ | |
cab50449 | 225 | char * OVS_WARN_UNUSED_RESULT |
f25d0cf3 | 226 | bundle_parse(const char *s, struct ofpbuf *ofpacts) |
a368bb53 EJ |
227 | { |
228 | char *fields, *basis, *algorithm, *slave_type, *slave_delim; | |
229 | char *tokstr, *save_ptr; | |
bdda5aca | 230 | char *error; |
a368bb53 EJ |
231 | |
232 | save_ptr = NULL; | |
233 | tokstr = xstrdup(s); | |
234 | fields = strtok_r(tokstr, ", ", &save_ptr); | |
235 | basis = strtok_r(NULL, ", ", &save_ptr); | |
236 | algorithm = strtok_r(NULL, ", ", &save_ptr); | |
237 | slave_type = strtok_r(NULL, ", ", &save_ptr); | |
238 | slave_delim = strtok_r(NULL, ": ", &save_ptr); | |
239 | ||
bdda5aca BP |
240 | error = bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, |
241 | NULL, slave_delim, ofpacts); | |
a368bb53 | 242 | free(tokstr); |
bdda5aca BP |
243 | |
244 | return error; | |
a368bb53 EJ |
245 | } |
246 | ||
247 | /* Converts a bundle_load action string contained in 's' to an nx_action_bundle | |
bdda5aca BP |
248 | * and stores it in 'b'. Sets 'b''s l2 pointer to NULL. |
249 | * | |
250 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
251 | * error. The caller is responsible for freeing the returned string.*/ | |
cab50449 | 252 | char * OVS_WARN_UNUSED_RESULT |
f25d0cf3 | 253 | bundle_parse_load(const char *s, struct ofpbuf *ofpacts) |
a368bb53 EJ |
254 | { |
255 | char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim; | |
256 | char *tokstr, *save_ptr; | |
bdda5aca | 257 | char *error; |
a368bb53 EJ |
258 | |
259 | save_ptr = NULL; | |
260 | tokstr = xstrdup(s); | |
261 | fields = strtok_r(tokstr, ", ", &save_ptr); | |
262 | basis = strtok_r(NULL, ", ", &save_ptr); | |
263 | algorithm = strtok_r(NULL, ", ", &save_ptr); | |
264 | slave_type = strtok_r(NULL, ", ", &save_ptr); | |
265 | dst = strtok_r(NULL, ", ", &save_ptr); | |
266 | slave_delim = strtok_r(NULL, ": ", &save_ptr); | |
267 | ||
bdda5aca BP |
268 | error = bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, |
269 | dst, slave_delim, ofpacts); | |
a368bb53 | 270 | |
daff3353 | 271 | free(tokstr); |
bdda5aca BP |
272 | |
273 | return error; | |
daff3353 EJ |
274 | } |
275 | ||
276 | /* Appends a human-readable representation of 'nab' to 's'. */ | |
277 | void | |
f25d0cf3 | 278 | bundle_format(const struct ofpact_bundle *bundle, struct ds *s) |
daff3353 | 279 | { |
f25d0cf3 | 280 | const char *action, *fields, *algorithm; |
daff3353 EJ |
281 | size_t i; |
282 | ||
f25d0cf3 | 283 | fields = flow_hash_fields_to_str(bundle->fields); |
daff3353 | 284 | |
f25d0cf3 | 285 | switch (bundle->algorithm) { |
daff3353 EJ |
286 | case NX_BD_ALG_HRW: |
287 | algorithm = "hrw"; | |
288 | break; | |
289 | case NX_BD_ALG_ACTIVE_BACKUP: | |
290 | algorithm = "active_backup"; | |
291 | break; | |
292 | default: | |
293 | algorithm = "<unknown>"; | |
294 | } | |
295 | ||
f25d0cf3 | 296 | action = bundle->dst.field ? "bundle_load" : "bundle"; |
a368bb53 EJ |
297 | |
298 | ds_put_format(s, "%s(%s,%"PRIu16",%s,%s,", action, fields, | |
f25d0cf3 | 299 | bundle->basis, algorithm, "ofport"); |
816fd533 | 300 | |
f25d0cf3 BP |
301 | if (bundle->dst.field) { |
302 | mf_format_subfield(&bundle->dst, s); | |
a368bb53 EJ |
303 | ds_put_cstr(s, ","); |
304 | } | |
305 | ||
306 | ds_put_cstr(s, "slaves:"); | |
f25d0cf3 | 307 | for (i = 0; i < bundle->n_slaves; i++) { |
daff3353 EJ |
308 | if (i) { |
309 | ds_put_cstr(s, ","); | |
310 | } | |
311 | ||
c6100d92 | 312 | ofputil_format_port(bundle->slaves[i], s); |
daff3353 EJ |
313 | } |
314 | ||
315 | ds_put_cstr(s, ")"); | |
316 | } |