Add the ability to connect to a vconn asynchronously.
[sliver-openvswitch.git] / secchan / secchan.c
1 /* Copyright (C) 2007 Board of Trustees, Leland Stanford Jr. University.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21
22 #include <errno.h>
23 #include <getopt.h>
24 #include <poll.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
29
30 #include "buffer.h"
31 #include "command-line.h"
32 #include "compiler.h"
33 #include "fault.h"
34 #include "util.h"
35 #include "vconn-ssl.h"
36 #include "vconn.h"
37 #include "vlog-socket.h"
38 #include "openflow.h"
39 #include "poll-loop.h"
40
41 #include "vlog.h"
42 #define THIS_MODULE VLM_secchan
43
44 static void parse_options(int argc, char *argv[]);
45 static void usage(void) NO_RETURN;
46
47 static bool reliable = true;
48
49 struct half {
50     const char *name;
51     struct vconn *vconn;
52     struct buffer *rxbuf;
53     time_t backoff_deadline;
54     int backoff;
55 };
56
57 static void reconnect(struct half *);
58
59 int
60 main(int argc, char *argv[])
61 {
62     struct half halves[2];
63     int retval;
64     int i;
65
66     set_program_name(argv[0]);
67     register_fault_handlers();
68     vlog_init();
69     parse_options(argc, argv);
70
71     if (argc - optind != 2) {
72         fatal(0, "exactly two peer arguments required; use --help for usage");
73     }
74
75     retval = vlog_server_listen(NULL, NULL);
76     if (retval) {
77         fatal(retval, "Could not listen for vlog connections");
78     }
79
80     for (i = 0; i < 2; i++) {
81         halves[i].name = argv[optind + i];
82         halves[i].vconn = NULL;
83         halves[i].rxbuf = NULL;
84         halves[i].backoff_deadline = 0;
85         halves[i].backoff = 1;
86         reconnect(&halves[i]);
87     }
88     for (;;) {
89         /* Do some work.  Limit the number of iterations so that callbacks
90          * registered with the poll loop don't starve. */
91         int iteration;
92         for (iteration = 0; iteration < 50; iteration++) {
93             bool progress = false;
94             for (i = 0; i < 2; i++) {
95                 struct half *this = &halves[i];
96                 struct half *peer = &halves[!i];
97
98                 if (!this->rxbuf) {
99                     retval = vconn_recv(this->vconn, &this->rxbuf);
100                     if (retval && retval != EAGAIN) {
101                         if (retval == EOF) {
102                             VLOG_DBG("%s: connection closed by remote host",
103                                      this->name); 
104                         } else {
105                             VLOG_DBG("%s: recv: closing connection: %s",
106                                      this->name, strerror(retval));
107                         }
108                         reconnect(this);
109                         break;
110                     }
111                 }
112
113                 if (this->rxbuf) {
114                     retval = vconn_send(peer->vconn, this->rxbuf);
115                     if (!retval) {
116                         this->rxbuf = NULL;
117                         progress = true;
118                     } else if (retval != EAGAIN) {
119                         VLOG_DBG("%s: send: closing connection: %s",
120                                  peer->name, strerror(retval));
121                         reconnect(peer);
122                         break;
123                     }
124                 }
125             }
126             if (!progress) {
127                 break;
128             }
129         }
130
131         /* Wait for something to happen. */
132         for (i = 0; i < 2; i++) {
133             struct half *this = &halves[i];
134             struct half *peer = &halves[!i];
135             if (!this->rxbuf) {
136                 vconn_recv_wait(this->vconn);
137             } else {
138                 vconn_send_wait(peer->vconn);
139             }
140         }
141         poll_block();
142     }
143
144     return 0;
145 }
146
147 static void
148 reconnect(struct half *this) 
149 {
150     if (this->vconn != NULL) {
151         if (!reliable) {
152             fatal(0, "%s: connection dropped", this->name);
153         }
154
155         VLOG_WARN("%s: connection dropped, reconnecting", this->name);
156         vconn_close(this->vconn);
157         this->vconn = NULL;
158         buffer_delete(this->rxbuf);
159         this->rxbuf = NULL;
160     }
161
162     for (;;) {
163         time_t now = time(0);
164         int retval;
165
166         if (now >= this->backoff_deadline) {
167             this->backoff = 1;
168         } else {
169             this->backoff *= 2;
170             if (this->backoff > 60) {
171                 this->backoff = 60;
172             }
173             VLOG_WARN("%s: waiting %d seconds before reconnect\n",
174                       this->name, (int) (this->backoff_deadline - now));
175             poll_timer_wait((this->backoff_deadline - now) * 1000);
176             poll_block();
177         }
178
179         retval = vconn_open_block(this->name, &this->vconn);
180         if (!retval) {
181             VLOG_WARN("%s: connected", this->name);
182             if (vconn_is_passive(this->vconn)) {
183                 fatal(0, "%s: passive vconn not supported in control path",
184                       this->name);
185             }
186             this->backoff_deadline = now + this->backoff;
187             return;
188         }
189
190         if (!reliable) {
191             fatal(0, "%s: connection failed", this->name);
192         }
193         VLOG_WARN("%s: connection failed (%s)", this->name, strerror(retval));
194         this->backoff_deadline = time(0) + this->backoff;
195     }
196 }
197
198 static void
199 parse_options(int argc, char *argv[]) 
200 {
201     static struct option long_options[] = {
202         {"unreliable",  no_argument, 0, 'u'},
203         {"verbose",     optional_argument, 0, 'v'},
204         {"help",        no_argument, 0, 'h'},
205         {"version",     no_argument, 0, 'V'},
206 #ifdef HAVE_OPENSSL
207         {"private-key", required_argument, 0, 'p'},
208         {"certificate", required_argument, 0, 'c'},
209         {"ca-cert",     required_argument, 0, 'C'},
210 #endif
211         {0, 0, 0, 0},
212     };
213     char *short_options = long_options_to_short_options(long_options);
214     
215     for (;;) {
216         int indexptr;
217         int c;
218
219         c = getopt_long(argc, argv, short_options, long_options, &indexptr);
220         if (c == -1) {
221             break;
222         }
223
224         switch (c) {
225         case 'u':
226             reliable = false;
227             break;
228
229         case 'h':
230             usage();
231
232         case 'V':
233             printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
234             exit(EXIT_SUCCESS);
235
236         case 'v':
237             vlog_set_verbosity(optarg);
238             break;
239
240 #ifdef HAVE_OPENSSL
241         case 'p':
242             vconn_ssl_set_private_key_file(optarg);
243             break;
244
245         case 'c':
246             vconn_ssl_set_certificate_file(optarg);
247             break;
248
249         case 'C':
250             vconn_ssl_set_ca_cert_file(optarg);
251             break;
252 #endif
253
254         case '?':
255             exit(EXIT_FAILURE);
256
257         default:
258             abort();
259         }
260     }
261     free(short_options);
262 }
263
264 static void
265 usage(void)
266 {
267     printf("%s: Secure Channel\n"
268            "usage: %s [OPTIONS] LOCAL REMOTE\n"
269            "\nRelays OpenFlow message between LOCAL and REMOTE datapaths.\n"
270            "LOCAL and REMOTE must each be one of the following:\n"
271            "  tcp:HOST[:PORT]         PORT (default: %d) on remote TCP HOST\n",
272            program_name, program_name, OFP_TCP_PORT);
273 #ifdef HAVE_NETLINK
274     printf("  nl:DP_IDX               local datapath DP_IDX\n");
275 #endif
276 #ifdef HAVE_OPENSSL
277     printf("  ssl:HOST[:PORT]         SSL PORT (default: %d) on remote HOST\n"
278            "\nPKI configuration (required to use SSL):\n"
279            "  -p, --private-key=FILE  file with private key\n"
280            "  -c, --certificate=FILE  file with certificate for private key\n"
281            "  -C, --ca-cert=FILE      file with peer CA certificate\n",
282            OFP_SSL_PORT);
283 #endif
284     printf("\nNetworking options:\n"
285            "  -u, --unreliable        do not reconnect after connections drop\n"
286            "\nOther options:\n"
287            "  -v, --verbose           set maximum verbosity level\n"
288            "  -h, --help              display this help message\n"
289            "  -V, --version           display version information\n");
290     exit(EXIT_SUCCESS);
291 }