Update copyright on all non-GPL files
[sliver-openvswitch.git] / secchan / secchan.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 <errno.h>
35 #include <getopt.h>
36 #include <poll.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40 #include <unistd.h>
41
42 #include "buffer.h"
43 #include "command-line.h"
44 #include "compiler.h"
45 #include "fault.h"
46 #include "util.h"
47 #include "vconn-ssl.h"
48 #include "vconn.h"
49 #include "vlog-socket.h"
50 #include "openflow.h"
51 #include "poll-loop.h"
52
53 #include "vlog.h"
54 #define THIS_MODULE VLM_secchan
55
56 static void parse_options(int argc, char *argv[]);
57 static void usage(void) NO_RETURN;
58
59 static bool reliable = true;
60
61 struct half {
62     const char *name;
63     struct vconn *vconn;
64     struct buffer *rxbuf;
65     time_t backoff_deadline;
66     int backoff;
67 };
68
69 static void reconnect(struct half *);
70
71 int
72 main(int argc, char *argv[])
73 {
74     struct half halves[2];
75     int retval;
76     int i;
77
78     set_program_name(argv[0]);
79     register_fault_handlers();
80     vlog_init();
81     parse_options(argc, argv);
82
83     if (argc - optind != 2) {
84         fatal(0, "exactly two peer arguments required; use --help for usage");
85     }
86
87     retval = vlog_server_listen(NULL, NULL);
88     if (retval) {
89         fatal(retval, "Could not listen for vlog connections");
90     }
91
92     for (i = 0; i < 2; i++) {
93         halves[i].name = argv[optind + i];
94         halves[i].vconn = NULL;
95         halves[i].rxbuf = NULL;
96         halves[i].backoff_deadline = 0;
97         halves[i].backoff = 1;
98         reconnect(&halves[i]);
99     }
100     for (;;) {
101         /* Do some work.  Limit the number of iterations so that callbacks
102          * registered with the poll loop don't starve. */
103         int iteration;
104         for (iteration = 0; iteration < 50; iteration++) {
105             bool progress = false;
106             for (i = 0; i < 2; i++) {
107                 struct half *this = &halves[i];
108                 struct half *peer = &halves[!i];
109
110                 if (!this->rxbuf) {
111                     retval = vconn_recv(this->vconn, &this->rxbuf);
112                     if (retval && retval != EAGAIN) {
113                         if (retval == EOF) {
114                             VLOG_DBG("%s: connection closed by remote host",
115                                      this->name); 
116                         } else {
117                             VLOG_DBG("%s: recv: closing connection: %s",
118                                      this->name, strerror(retval));
119                         }
120                         reconnect(this);
121                         break;
122                     }
123                 }
124
125                 if (this->rxbuf) {
126                     retval = vconn_send(peer->vconn, this->rxbuf);
127                     if (!retval) {
128                         this->rxbuf = NULL;
129                         progress = true;
130                     } else if (retval != EAGAIN) {
131                         VLOG_DBG("%s: send: closing connection: %s",
132                                  peer->name, strerror(retval));
133                         reconnect(peer);
134                         break;
135                     }
136                 }
137             }
138             if (!progress) {
139                 break;
140             }
141         }
142
143         /* Wait for something to happen. */
144         for (i = 0; i < 2; i++) {
145             struct half *this = &halves[i];
146             struct half *peer = &halves[!i];
147             if (!this->rxbuf) {
148                 vconn_recv_wait(this->vconn);
149             } else {
150                 vconn_send_wait(peer->vconn);
151             }
152         }
153         poll_block();
154     }
155
156     return 0;
157 }
158
159 static void
160 reconnect(struct half *this) 
161 {
162     if (this->vconn != NULL) {
163         if (!reliable) {
164             fatal(0, "%s: connection dropped", this->name);
165         }
166
167         VLOG_WARN("%s: connection dropped, reconnecting", this->name);
168         vconn_close(this->vconn);
169         this->vconn = NULL;
170         buffer_delete(this->rxbuf);
171         this->rxbuf = NULL;
172     }
173
174     for (;;) {
175         time_t now = time(0);
176         int retval;
177
178         if (now >= this->backoff_deadline) {
179             this->backoff = 1;
180         } else {
181             this->backoff *= 2;
182             if (this->backoff > 60) {
183                 this->backoff = 60;
184             }
185             VLOG_WARN("%s: waiting %d seconds before reconnect\n",
186                       this->name, (int) (this->backoff_deadline - now));
187             poll_timer_wait((this->backoff_deadline - now) * 1000);
188             poll_block();
189         }
190
191         retval = vconn_open_block(this->name, &this->vconn);
192         if (!retval) {
193             VLOG_WARN("%s: connected", this->name);
194             if (vconn_is_passive(this->vconn)) {
195                 fatal(0, "%s: passive vconn not supported in control path",
196                       this->name);
197             }
198             this->backoff_deadline = now + this->backoff;
199             return;
200         }
201
202         if (!reliable) {
203             fatal(0, "%s: connection failed", this->name);
204         }
205         VLOG_WARN("%s: connection failed (%s)", this->name, strerror(retval));
206         this->backoff_deadline = time(0) + this->backoff;
207     }
208 }
209
210 static void
211 parse_options(int argc, char *argv[]) 
212 {
213     static struct option long_options[] = {
214         {"unreliable",  no_argument, 0, 'u'},
215         {"verbose",     optional_argument, 0, 'v'},
216         {"help",        no_argument, 0, 'h'},
217         {"version",     no_argument, 0, 'V'},
218 #ifdef HAVE_OPENSSL
219         {"private-key", required_argument, 0, 'p'},
220         {"certificate", required_argument, 0, 'c'},
221         {"ca-cert",     required_argument, 0, 'C'},
222 #endif
223         {0, 0, 0, 0},
224     };
225     char *short_options = long_options_to_short_options(long_options);
226     
227     for (;;) {
228         int indexptr;
229         int c;
230
231         c = getopt_long(argc, argv, short_options, long_options, &indexptr);
232         if (c == -1) {
233             break;
234         }
235
236         switch (c) {
237         case 'u':
238             reliable = false;
239             break;
240
241         case 'h':
242             usage();
243
244         case 'V':
245             printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
246             exit(EXIT_SUCCESS);
247
248         case 'v':
249             vlog_set_verbosity(optarg);
250             break;
251
252 #ifdef HAVE_OPENSSL
253         case 'p':
254             vconn_ssl_set_private_key_file(optarg);
255             break;
256
257         case 'c':
258             vconn_ssl_set_certificate_file(optarg);
259             break;
260
261         case 'C':
262             vconn_ssl_set_ca_cert_file(optarg);
263             break;
264 #endif
265
266         case '?':
267             exit(EXIT_FAILURE);
268
269         default:
270             abort();
271         }
272     }
273     free(short_options);
274 }
275
276 static void
277 usage(void)
278 {
279     printf("%s: Secure Channel\n"
280            "usage: %s [OPTIONS] LOCAL REMOTE\n"
281            "\nRelays OpenFlow message between LOCAL and REMOTE datapaths.\n"
282            "LOCAL and REMOTE must each be one of the following:\n"
283            "  tcp:HOST[:PORT]         PORT (default: %d) on remote TCP HOST\n",
284            program_name, program_name, OFP_TCP_PORT);
285 #ifdef HAVE_NETLINK
286     printf("  nl:DP_IDX               local datapath DP_IDX\n");
287 #endif
288 #ifdef HAVE_OPENSSL
289     printf("  ssl:HOST[:PORT]         SSL PORT (default: %d) on remote HOST\n"
290            "\nPKI configuration (required to use SSL):\n"
291            "  -p, --private-key=FILE  file with private key\n"
292            "  -c, --certificate=FILE  file with certificate for private key\n"
293            "  -C, --ca-cert=FILE      file with peer CA certificate\n",
294            OFP_SSL_PORT);
295 #endif
296     printf("\nNetworking options:\n"
297            "  -u, --unreliable        do not reconnect after connections drop\n"
298            "\nOther options:\n"
299            "  -v, --verbose           set maximum verbosity level\n"
300            "  -h, --help              display this help message\n"
301            "  -V, --version           display version information\n");
302     exit(EXIT_SUCCESS);
303 }