For SNAT, don't store the pre-fragment L2 header before actions are applied.
[sliver-openvswitch.git] / secchan / fail-open.c
1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
2  * Junior University
3  *
4  * We are making the OpenFlow specification and associated documentation
5  * (Software) available for public use and benefit with the expectation
6  * that others will use, modify and enhance the Software and contribute
7  * those enhancements back to the community. However, since we would
8  * like to make the Software available for broadest use, with as few
9  * restrictions as possible permission is hereby granted, free of
10  * charge, to any person obtaining a copy of this Software to deal in
11  * the Software under the copyrights without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE.
28  *
29  * The name and trademarks of copyright holder(s) may NOT be used in
30  * advertising or publicity pertaining to the Software or any
31  * derivatives without specific, written prior permission.
32  */
33
34 #include <config.h>
35 #include "fail-open.h"
36 #include <arpa/inet.h>
37 #include <stddef.h>
38 #include <string.h>
39 #include "learning-switch.h"
40 #include "netdev.h"
41 #include "packets.h"
42 #include "port-watcher.h"
43 #include "rconn.h"
44 #include "secchan.h"
45 #include "status.h"
46 #include "stp-secchan.h"
47 #include "timeval.h"
48
49 #define THIS_MODULE VLM_fail_open
50 #include "vlog.h"
51
52 struct fail_open_data {
53     const struct settings *s;
54     struct rconn *local_rconn;
55     struct rconn *remote_rconn;
56     struct lswitch *lswitch;
57     int last_disconn_secs;
58     time_t boot_deadline;
59 };
60
61 /* Causes 'r' to enter or leave fail-open mode, if appropriate. */
62 static void
63 fail_open_periodic_cb(void *fail_open_)
64 {
65     struct fail_open_data *fail_open = fail_open_;
66     int disconn_secs;
67     bool open;
68
69     if (time_now() < fail_open->boot_deadline) {
70         return;
71     }
72     disconn_secs = rconn_failure_duration(fail_open->remote_rconn);
73     open = disconn_secs >= fail_open->s->probe_interval * 3;
74     if (open != (fail_open->lswitch != NULL)) {
75         if (!open) {
76             VLOG_WARN("No longer in fail-open mode");
77             lswitch_destroy(fail_open->lswitch);
78             fail_open->lswitch = NULL;
79         } else {
80             VLOG_WARN("Could not connect to controller for %d seconds, "
81                       "failing open", disconn_secs);
82             fail_open->lswitch = lswitch_create(fail_open->local_rconn, true,
83                                                 fail_open->s->max_idle);
84             fail_open->last_disconn_secs = disconn_secs;
85         }
86     } else if (open && disconn_secs > fail_open->last_disconn_secs + 60) {
87         VLOG_WARN("Still in fail-open mode after %d seconds disconnected "
88                   "from controller", disconn_secs);
89         fail_open->last_disconn_secs = disconn_secs;
90     }
91     if (fail_open->lswitch) {
92         lswitch_run(fail_open->lswitch, fail_open->local_rconn);
93     }
94 }
95
96 static void
97 fail_open_wait_cb(void *fail_open_)
98 {
99     struct fail_open_data *fail_open = fail_open_;
100     if (fail_open->lswitch) {
101         lswitch_wait(fail_open->lswitch);
102     }
103 }
104
105 static bool
106 fail_open_local_packet_cb(struct relay *r, void *fail_open_)
107 {
108     struct fail_open_data *fail_open = fail_open_;
109     if (rconn_is_connected(fail_open->remote_rconn) || !fail_open->lswitch) {
110         return false;
111     } else {
112         lswitch_process_packet(fail_open->lswitch, fail_open->local_rconn,
113                                r->halves[HALF_LOCAL].rxbuf);
114         rconn_run(fail_open->local_rconn);
115         return true;
116     }
117 }
118
119 static void
120 fail_open_status_cb(struct status_reply *sr, void *fail_open_)
121 {
122     struct fail_open_data *fail_open = fail_open_;
123     const struct settings *s = fail_open->s;
124     int trigger_duration = s->probe_interval * 3;
125     int cur_duration = rconn_failure_duration(fail_open->remote_rconn);
126
127     status_reply_put(sr, "trigger-duration=%d", trigger_duration);
128     status_reply_put(sr, "current-duration=%d", cur_duration);
129     status_reply_put(sr, "triggered=%s",
130                      cur_duration >= trigger_duration ? "true" : "false");
131     status_reply_put(sr, "max-idle=%d", s->max_idle);
132 }
133
134 static struct hook_class fail_open_hook_class = {
135     fail_open_local_packet_cb,  /* local_packet_cb */
136     NULL,                       /* remote_packet_cb */
137     fail_open_periodic_cb,      /* periodic_cb */
138     fail_open_wait_cb,          /* wait_cb */
139     NULL,                       /* closing_cb */
140 };
141
142 void
143 fail_open_start(struct secchan *secchan, const struct settings *s,
144                 struct switch_status *ss,
145                 struct rconn *local_rconn, struct rconn *remote_rconn)
146 {
147     struct fail_open_data *fail_open = xmalloc(sizeof *fail_open);
148     fail_open->s = s;
149     fail_open->local_rconn = local_rconn;
150     fail_open->remote_rconn = remote_rconn;
151     fail_open->lswitch = NULL;
152     fail_open->boot_deadline = time_now() + s->probe_interval * 3;
153     if (s->enable_stp) {
154         fail_open->boot_deadline += STP_EXTRA_BOOT_TIME;
155     }
156     switch_status_register_category(ss, "fail-open",
157                                     fail_open_status_cb, fail_open);
158     add_hook(secchan, &fail_open_hook_class, fail_open);
159 }