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