]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
9b45d7f5 | 2 | * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks. |
064af421 | 3 | * |
a14bc59f BP |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
064af421 | 7 | * |
a14bc59f BP |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
064af421 BP |
15 | */ |
16 | ||
17 | #include <config.h> | |
18 | #include "fail-open.h" | |
19 | #include <inttypes.h> | |
20 | #include <stdlib.h> | |
cf3fad8a | 21 | #include "classifier.h" |
064af421 BP |
22 | #include "flow.h" |
23 | #include "mac-learning.h" | |
24 | #include "odp-util.h" | |
fa37b408 | 25 | #include "ofp-util.h" |
7778bd15 | 26 | #include "ofpbuf.h" |
064af421 | 27 | #include "ofproto.h" |
7778bd15 BP |
28 | #include "pktbuf.h" |
29 | #include "poll-loop.h" | |
064af421 | 30 | #include "rconn.h" |
064af421 | 31 | #include "timeval.h" |
7778bd15 | 32 | #include "vconn.h" |
064af421 BP |
33 | #include "vlog.h" |
34 | ||
d98e6007 | 35 | VLOG_DEFINE_THIS_MODULE(fail_open); |
5136ce49 | 36 | |
7778bd15 BP |
37 | /* |
38 | * Fail-open mode. | |
39 | * | |
40 | * In fail-open mode, the switch detects when the controller cannot be | |
41 | * contacted or when the controller is dropping switch connections because the | |
42 | * switch does not pass its admission control policy. In those situations the | |
43 | * switch sets up flows itself using the "normal" action. | |
44 | * | |
45 | * There is a little subtlety to implementation, to properly handle the case | |
46 | * where the controller allows switch connections but drops them a few seconds | |
47 | * later for admission control reasons. Because of this case, we don't want to | |
48 | * just stop setting up flows when we connect to the controller: if we did, | |
49 | * then new flow setup and existing flows would stop during the duration of | |
50 | * connection to the controller, and thus the whole network would go down for | |
51 | * that period of time. | |
52 | * | |
4a2026dd BP |
53 | * So, instead, we add some special cases when we are connected to a |
54 | * controller, but not yet sure that it has admitted us: | |
7778bd15 BP |
55 | * |
56 | * - We set up flows immediately ourselves, but simultaneously send out an | |
57 | * OFPT_PACKET_IN to the controller. We put a special bogus buffer-id in | |
58 | * these OFPT_PACKET_IN messages so that duplicate packets don't get sent | |
59 | * out to the network when the controller replies. | |
60 | * | |
61 | * - We also send out OFPT_PACKET_IN messages for totally bogus packets | |
62 | * every so often, in case no real new flows are arriving in the network. | |
63 | * | |
64 | * - We don't flush the flow table at the time we connect, because this | |
65 | * could cause network stuttering in a switch with lots of flows or very | |
66 | * high-bandwidth flows by suddenly throwing lots of packets down to | |
67 | * userspace. | |
68 | */ | |
69 | ||
064af421 BP |
70 | struct fail_open { |
71 | struct ofproto *ofproto; | |
76ce9432 BP |
72 | struct rconn **controllers; |
73 | size_t n_controllers; | |
064af421 | 74 | int last_disconn_secs; |
7778bd15 BP |
75 | long long int next_bogus_packet_in; |
76 | struct rconn_packet_counter *bogus_packet_counter; | |
064af421 BP |
77 | }; |
78 | ||
2f6d3445 BP |
79 | static void fail_open_recover(struct fail_open *); |
80 | ||
76ce9432 BP |
81 | /* Returns the number of seconds of disconnection after which fail-open mode |
82 | * should activate. */ | |
83 | static int | |
84 | trigger_duration(const struct fail_open *fo) | |
064af421 | 85 | { |
76ce9432 BP |
86 | if (!fo->n_controllers) { |
87 | /* Shouldn't ever arrive here, but if we do, never fail open. */ | |
88 | return INT_MAX; | |
89 | } else { | |
90 | /* Otherwise, every controller must have a chance to send an | |
91 | * inactivity probe and reconnect before we fail open, so take the | |
92 | * maximum probe interval and multiply by 3: | |
93 | * | |
94 | * - The first interval is the idle time before sending an inactivity | |
95 | * probe. | |
96 | * | |
97 | * - The second interval is the time allowed for a response to the | |
98 | * inactivity probe. | |
99 | * | |
100 | * - The third interval is the time allowed to reconnect after no | |
101 | * response is received. | |
102 | */ | |
103 | int max_probe_interval; | |
104 | size_t i; | |
105 | ||
106 | max_probe_interval = 0; | |
107 | for (i = 0; i < fo->n_controllers; i++) { | |
108 | int probe_interval = rconn_get_probe_interval(fo->controllers[i]); | |
109 | max_probe_interval = MAX(max_probe_interval, probe_interval); | |
110 | } | |
111 | return max_probe_interval * 3; | |
112 | } | |
113 | } | |
114 | ||
115 | /* Returns the number of seconds for which all controllers have been | |
116 | * disconnected. */ | |
117 | static int | |
118 | failure_duration(const struct fail_open *fo) | |
119 | { | |
120 | int min_failure_duration; | |
121 | size_t i; | |
122 | ||
123 | if (!fo->n_controllers) { | |
124 | return 0; | |
125 | } | |
126 | ||
127 | min_failure_duration = INT_MAX; | |
128 | for (i = 0; i < fo->n_controllers; i++) { | |
129 | int failure_duration = rconn_failure_duration(fo->controllers[i]); | |
130 | min_failure_duration = MIN(min_failure_duration, failure_duration); | |
131 | } | |
132 | return min_failure_duration; | |
7778bd15 BP |
133 | } |
134 | ||
135 | /* Returns true if 'fo' is currently in fail-open mode, otherwise false. */ | |
136 | bool | |
137 | fail_open_is_active(const struct fail_open *fo) | |
138 | { | |
139 | return fo->last_disconn_secs != 0; | |
140 | } | |
064af421 | 141 | |
76ce9432 BP |
142 | /* Returns true if at least one controller is connected (regardless of whether |
143 | * those controllers are believed to have authenticated and accepted this | |
144 | * switch), false if none of them are connected. */ | |
145 | static bool | |
146 | any_controller_is_connected(const struct fail_open *fo) | |
147 | { | |
148 | size_t i; | |
149 | ||
150 | for (i = 0; i < fo->n_controllers; i++) { | |
151 | if (rconn_is_connected(fo->controllers[i])) { | |
152 | return true; | |
153 | } | |
154 | } | |
155 | return false; | |
156 | } | |
157 | ||
158 | /* Returns true if at least one controller is believed to have authenticated | |
159 | * and accepted this switch, false otherwise. */ | |
160 | static bool | |
161 | any_controller_is_admitted(const struct fail_open *fo) | |
162 | { | |
163 | size_t i; | |
164 | ||
165 | for (i = 0; i < fo->n_controllers; i++) { | |
166 | if (rconn_is_admitted(fo->controllers[i])) { | |
167 | return true; | |
168 | } | |
169 | } | |
170 | return false; | |
171 | } | |
172 | ||
7778bd15 | 173 | static void |
76ce9432 | 174 | send_bogus_packet_in(struct fail_open *fo, struct rconn *rconn) |
7778bd15 BP |
175 | { |
176 | uint8_t mac[ETH_ADDR_LEN]; | |
177 | struct ofpbuf *opi; | |
178 | struct ofpbuf b; | |
064af421 | 179 | |
7778bd15 BP |
180 | /* Compose ofp_packet_in. */ |
181 | ofpbuf_init(&b, 128); | |
70150daf | 182 | eth_addr_nicira_random(mac); |
7778bd15 BP |
183 | compose_benign_packet(&b, "Open vSwitch Controller Probe", 0xa033, mac); |
184 | opi = make_packet_in(pktbuf_get_null(), OFPP_LOCAL, OFPR_NO_MATCH, &b, 64); | |
185 | ofpbuf_uninit(&b); | |
186 | ||
187 | /* Send. */ | |
76ce9432 BP |
188 | rconn_send_with_limit(rconn, opi, fo->bogus_packet_counter, 1); |
189 | } | |
190 | ||
191 | static void | |
192 | send_bogus_packet_ins(struct fail_open *fo) | |
193 | { | |
194 | size_t i; | |
195 | ||
196 | for (i = 0; i < fo->n_controllers; i++) { | |
197 | if (rconn_is_connected(fo->controllers[i])) { | |
198 | send_bogus_packet_in(fo, fo->controllers[i]); | |
199 | } | |
200 | } | |
7778bd15 BP |
201 | } |
202 | ||
76ce9432 | 203 | /* Enter fail-open mode if we should be in it. */ |
7778bd15 BP |
204 | void |
205 | fail_open_run(struct fail_open *fo) | |
206 | { | |
76ce9432 BP |
207 | int disconn_secs = failure_duration(fo); |
208 | ||
7778bd15 | 209 | /* Enter fail-open mode if 'fo' is not in it but should be. */ |
76ce9432 | 210 | if (disconn_secs >= trigger_duration(fo)) { |
7778bd15 | 211 | if (!fail_open_is_active(fo)) { |
5ec6690c JP |
212 | VLOG_WARN("Could not connect to controller (or switch failed " |
213 | "controller's post-connection admission control " | |
214 | "policy) for %d seconds, failing open", disconn_secs); | |
064af421 BP |
215 | fo->last_disconn_secs = disconn_secs; |
216 | ||
217 | /* Flush all OpenFlow and datapath flows. We will set up our | |
218 | * fail-open rule from fail_open_flushed() when | |
219 | * ofproto_flush_flows() calls back to us. */ | |
220 | ofproto_flush_flows(fo->ofproto); | |
7778bd15 BP |
221 | } else if (disconn_secs > fo->last_disconn_secs + 60) { |
222 | VLOG_INFO("Still in fail-open mode after %d seconds disconnected " | |
223 | "from controller", disconn_secs); | |
224 | fo->last_disconn_secs = disconn_secs; | |
064af421 | 225 | } |
064af421 | 226 | } |
7778bd15 BP |
227 | |
228 | /* Schedule a bogus packet-in if we're connected and in fail-open. */ | |
229 | if (fail_open_is_active(fo)) { | |
76ce9432 | 230 | if (any_controller_is_connected(fo)) { |
7778bd15 BP |
231 | bool expired = time_msec() >= fo->next_bogus_packet_in; |
232 | if (expired) { | |
76ce9432 | 233 | send_bogus_packet_ins(fo); |
7778bd15 BP |
234 | } |
235 | if (expired || fo->next_bogus_packet_in == LLONG_MAX) { | |
236 | fo->next_bogus_packet_in = time_msec() + 2000; | |
237 | } | |
238 | } else { | |
239 | fo->next_bogus_packet_in = LLONG_MAX; | |
240 | } | |
241 | } | |
242 | ||
064af421 BP |
243 | } |
244 | ||
7778bd15 BP |
245 | /* If 'fo' is currently in fail-open mode and its rconn has connected to the |
246 | * controller, exits fail open mode. */ | |
064af421 | 247 | void |
7778bd15 | 248 | fail_open_maybe_recover(struct fail_open *fo) |
064af421 | 249 | { |
76ce9432 | 250 | if (any_controller_is_admitted(fo)) { |
2f6d3445 BP |
251 | fail_open_recover(fo); |
252 | } | |
253 | } | |
254 | ||
255 | static void | |
256 | fail_open_recover(struct fail_open *fo) | |
257 | { | |
258 | if (fail_open_is_active(fo)) { | |
cf3fad8a | 259 | struct cls_rule rule; |
7778bd15 BP |
260 | |
261 | VLOG_WARN("No longer in fail-open mode"); | |
262 | fo->last_disconn_secs = 0; | |
263 | fo->next_bogus_packet_in = LLONG_MAX; | |
264 | ||
cf3fad8a BP |
265 | cls_rule_init_catchall(&rule, FAIL_OPEN_PRIORITY); |
266 | ofproto_delete_flow(fo->ofproto, &rule); | |
7778bd15 BP |
267 | } |
268 | } | |
269 | ||
270 | void | |
271 | fail_open_wait(struct fail_open *fo) | |
272 | { | |
273 | if (fo->next_bogus_packet_in != LLONG_MAX) { | |
7cf8b266 | 274 | poll_timer_wait_until(fo->next_bogus_packet_in); |
7778bd15 | 275 | } |
064af421 BP |
276 | } |
277 | ||
278 | void | |
279 | fail_open_flushed(struct fail_open *fo) | |
280 | { | |
76ce9432 BP |
281 | int disconn_secs = failure_duration(fo); |
282 | bool open = disconn_secs >= trigger_duration(fo); | |
064af421 BP |
283 | if (open) { |
284 | union ofp_action action; | |
cf3fad8a | 285 | struct cls_rule rule; |
064af421 BP |
286 | |
287 | /* Set up a flow that matches every packet and directs them to | |
288 | * OFPP_NORMAL. */ | |
289 | memset(&action, 0, sizeof action); | |
290 | action.type = htons(OFPAT_OUTPUT); | |
291 | action.output.len = htons(sizeof action); | |
292 | action.output.port = htons(OFPP_NORMAL); | |
cf3fad8a BP |
293 | |
294 | cls_rule_init_catchall(&rule, FAIL_OPEN_PRIORITY); | |
fa8b054f | 295 | ofproto_add_flow(fo->ofproto, &rule, &action, 1); |
064af421 BP |
296 | } |
297 | } | |
298 | ||
9b45d7f5 | 299 | /* Creates and returns a new struct fail_open for 'ofproto'. |
76ce9432 BP |
300 | * |
301 | * The caller should register its set of controllers with | |
302 | * fail_open_set_controllers(). (There should be at least one controller, | |
303 | * otherwise there isn't any point in having the struct fail_open around.) */ | |
064af421 | 304 | struct fail_open * |
9b45d7f5 | 305 | fail_open_create(struct ofproto *ofproto) |
064af421 BP |
306 | { |
307 | struct fail_open *fo = xmalloc(sizeof *fo); | |
308 | fo->ofproto = ofproto; | |
76ce9432 BP |
309 | fo->controllers = NULL; |
310 | fo->n_controllers = 0; | |
064af421 | 311 | fo->last_disconn_secs = 0; |
7778bd15 BP |
312 | fo->next_bogus_packet_in = LLONG_MAX; |
313 | fo->bogus_packet_counter = rconn_packet_counter_create(); | |
064af421 BP |
314 | return fo; |
315 | } | |
316 | ||
76ce9432 BP |
317 | /* Registers the 'n' rconns in 'rconns' as connections to the controller for |
318 | * 'fo'. The caller must ensure that all of the rconns remain valid until 'fo' | |
319 | * is destroyed or a new set is registered in a subsequent call. | |
320 | * | |
321 | * Takes ownership of the 'rconns' array, but not of the rconns that it points | |
322 | * to (of which the caller retains ownership). */ | |
064af421 | 323 | void |
76ce9432 BP |
324 | fail_open_set_controllers(struct fail_open *fo, |
325 | struct rconn **rconns, size_t n) | |
064af421 | 326 | { |
76ce9432 BP |
327 | free(fo->controllers); |
328 | fo->controllers = rconns; | |
329 | fo->n_controllers = n; | |
064af421 BP |
330 | } |
331 | ||
76ce9432 | 332 | /* Destroys 'fo'. */ |
064af421 BP |
333 | void |
334 | fail_open_destroy(struct fail_open *fo) | |
335 | { | |
336 | if (fo) { | |
2f6d3445 | 337 | fail_open_recover(fo); |
76ce9432 BP |
338 | free(fo->controllers); |
339 | /* We don't own the rconns behind fo->controllers. */ | |
7778bd15 | 340 | rconn_packet_counter_destroy(fo->bogus_packet_counter); |
064af421 BP |
341 | free(fo); |
342 | } | |
343 | } |