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