]>
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; |
4249b547 JB |
189 | } else if (!strcasecmp(fields, "symmetric_l3l4")) { |
190 | bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L3L4; | |
191 | } else if (!strcasecmp(fields, "symmetric_l3l4+udp")) { | |
192 | bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP; | |
daff3353 | 193 | } else { |
bdda5aca | 194 | return xasprintf("%s: unknown fields `%s'", s, fields); |
daff3353 EJ |
195 | } |
196 | ||
197 | if (!strcasecmp(algorithm, "active_backup")) { | |
f25d0cf3 | 198 | bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP; |
daff3353 | 199 | } else if (!strcasecmp(algorithm, "hrw")) { |
f25d0cf3 | 200 | bundle->algorithm = NX_BD_ALG_HRW; |
daff3353 | 201 | } else { |
bdda5aca | 202 | return xasprintf("%s: unknown algorithm `%s'", s, algorithm); |
daff3353 EJ |
203 | } |
204 | ||
f25d0cf3 | 205 | if (strcasecmp(slave_type, "ofport")) { |
bdda5aca | 206 | return xasprintf("%s: unknown slave_type `%s'", s, slave_type); |
daff3353 EJ |
207 | } |
208 | ||
f25d0cf3 | 209 | if (dst) { |
bdda5aca BP |
210 | char *error = mf_parse_subfield(&bundle->dst, dst); |
211 | if (error) { | |
212 | return error; | |
213 | } | |
bad8a439 BP |
214 | |
215 | if (!mf_nxm_header(bundle->dst.field->id)) { | |
216 | return xasprintf("%s: experimenter OXM field '%s' not supported", | |
217 | s, dst); | |
218 | } | |
a368bb53 | 219 | } |
bdda5aca BP |
220 | |
221 | return NULL; | |
a368bb53 EJ |
222 | } |
223 | ||
224 | /* Converts a bundle action string contained in 's' to an nx_action_bundle and | |
bdda5aca BP |
225 | * stores it in 'b'. Sets 'b''s l2 pointer to NULL. |
226 | * | |
227 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
228 | * error. The caller is responsible for freeing the returned string. */ | |
cab50449 | 229 | char * OVS_WARN_UNUSED_RESULT |
f25d0cf3 | 230 | bundle_parse(const char *s, struct ofpbuf *ofpacts) |
a368bb53 EJ |
231 | { |
232 | char *fields, *basis, *algorithm, *slave_type, *slave_delim; | |
233 | char *tokstr, *save_ptr; | |
bdda5aca | 234 | char *error; |
a368bb53 EJ |
235 | |
236 | save_ptr = NULL; | |
237 | tokstr = xstrdup(s); | |
238 | fields = strtok_r(tokstr, ", ", &save_ptr); | |
239 | basis = strtok_r(NULL, ", ", &save_ptr); | |
240 | algorithm = strtok_r(NULL, ", ", &save_ptr); | |
241 | slave_type = strtok_r(NULL, ", ", &save_ptr); | |
242 | slave_delim = strtok_r(NULL, ": ", &save_ptr); | |
243 | ||
bdda5aca BP |
244 | error = bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, |
245 | NULL, slave_delim, ofpacts); | |
a368bb53 | 246 | free(tokstr); |
bdda5aca BP |
247 | |
248 | return error; | |
a368bb53 EJ |
249 | } |
250 | ||
251 | /* Converts a bundle_load action string contained in 's' to an nx_action_bundle | |
bdda5aca BP |
252 | * and stores it in 'b'. Sets 'b''s l2 pointer to NULL. |
253 | * | |
254 | * Returns NULL if successful, otherwise a malloc()'d string describing the | |
255 | * error. The caller is responsible for freeing the returned string.*/ | |
cab50449 | 256 | char * OVS_WARN_UNUSED_RESULT |
f25d0cf3 | 257 | bundle_parse_load(const char *s, struct ofpbuf *ofpacts) |
a368bb53 EJ |
258 | { |
259 | char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim; | |
260 | char *tokstr, *save_ptr; | |
bdda5aca | 261 | char *error; |
a368bb53 EJ |
262 | |
263 | save_ptr = NULL; | |
264 | tokstr = xstrdup(s); | |
265 | fields = strtok_r(tokstr, ", ", &save_ptr); | |
266 | basis = strtok_r(NULL, ", ", &save_ptr); | |
267 | algorithm = strtok_r(NULL, ", ", &save_ptr); | |
268 | slave_type = strtok_r(NULL, ", ", &save_ptr); | |
269 | dst = strtok_r(NULL, ", ", &save_ptr); | |
270 | slave_delim = strtok_r(NULL, ": ", &save_ptr); | |
271 | ||
bdda5aca BP |
272 | error = bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, |
273 | dst, slave_delim, ofpacts); | |
a368bb53 | 274 | |
daff3353 | 275 | free(tokstr); |
bdda5aca BP |
276 | |
277 | return error; | |
daff3353 EJ |
278 | } |
279 | ||
280 | /* Appends a human-readable representation of 'nab' to 's'. */ | |
281 | void | |
f25d0cf3 | 282 | bundle_format(const struct ofpact_bundle *bundle, struct ds *s) |
daff3353 | 283 | { |
f25d0cf3 | 284 | const char *action, *fields, *algorithm; |
daff3353 EJ |
285 | size_t i; |
286 | ||
f25d0cf3 | 287 | fields = flow_hash_fields_to_str(bundle->fields); |
daff3353 | 288 | |
f25d0cf3 | 289 | switch (bundle->algorithm) { |
daff3353 EJ |
290 | case NX_BD_ALG_HRW: |
291 | algorithm = "hrw"; | |
292 | break; | |
293 | case NX_BD_ALG_ACTIVE_BACKUP: | |
294 | algorithm = "active_backup"; | |
295 | break; | |
296 | default: | |
297 | algorithm = "<unknown>"; | |
298 | } | |
299 | ||
f25d0cf3 | 300 | action = bundle->dst.field ? "bundle_load" : "bundle"; |
a368bb53 EJ |
301 | |
302 | ds_put_format(s, "%s(%s,%"PRIu16",%s,%s,", action, fields, | |
f25d0cf3 | 303 | bundle->basis, algorithm, "ofport"); |
816fd533 | 304 | |
f25d0cf3 BP |
305 | if (bundle->dst.field) { |
306 | mf_format_subfield(&bundle->dst, s); | |
a368bb53 EJ |
307 | ds_put_cstr(s, ","); |
308 | } | |
309 | ||
310 | ds_put_cstr(s, "slaves:"); | |
f25d0cf3 | 311 | for (i = 0; i < bundle->n_slaves; i++) { |
daff3353 EJ |
312 | if (i) { |
313 | ds_put_cstr(s, ","); | |
314 | } | |
315 | ||
c6100d92 | 316 | ofputil_format_port(bundle->slaves[i], s); |
daff3353 EJ |
317 | } |
318 | ||
319 | ds_put_cstr(s, ")"); | |
320 | } |