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