e9df46d2ca92988291aa4e5d0ef17e54e6800cb4
[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         /* Wait until there's something to do. */
92         for (i = 0; i < 2; i++) {
93             struct half *this = &halves[i];
94             struct half *peer = &halves[!i];
95             int want = 0;
96             if (peer->rxbuf) {
97                 want |= WANT_SEND;
98             }
99             if (!this->rxbuf) {
100                 want |= WANT_RECV;
101             }
102             this->pollfd->fd = -1;
103             this->pollfd->events = 0;
104             vconn_prepoll(this->vconn, want, this->pollfd);
105         }
106         if (vlog_server) {
107             pollfds[2].fd = vlog_server_get_fd(vlog_server);
108             pollfds[2].events = POLLIN;
109         }
110         do {
111             retval = poll(pollfds, 2 + (vlog_server != NULL), -1);
112         } while (retval < 0 && errno == EINTR);
113         if (retval <= 0) {
114             fatal(retval < 0 ? errno : 0, "poll");
115         }
116
117         /* Let each connection deal with any pending operations. */
118         for (i = 0; i < 2; i++) {
119             struct half *this = &halves[i];
120             vconn_postpoll(this->vconn, &this->pollfd->revents);
121             if (this->pollfd->revents & POLLERR) {
122                 this->pollfd->revents |= POLLIN | POLLOUT;
123             }
124         }
125         if (vlog_server && pollfds[2].revents) {
126             vlog_server_poll(vlog_server);
127         }
128
129         /* Do as much work as we can without waiting. */
130         for (i = 0; i < 2; i++) {
131             struct half *this = &halves[i];
132             struct half *peer = &halves[!i];
133
134             if (this->pollfd->revents & POLLIN && !this->rxbuf) {
135                 retval = vconn_recv(this->vconn, &this->rxbuf);
136                 if (retval && retval != EAGAIN) {
137                     VLOG_DBG("%s: recv: closing connection: %s",
138                              this->name, strerror(retval));
139                     reconnect(this);
140                     break;
141                 }
142             }
143
144             if (peer->pollfd->revents & POLLOUT && this->rxbuf) {
145                 retval = vconn_send(peer->vconn, this->rxbuf);
146                 if (!retval) {
147                     this->rxbuf = NULL;
148                 } else if (retval != EAGAIN) {
149                     VLOG_DBG("%s: send: closing connection: %s",
150                              peer->name, strerror(retval));
151                     reconnect(peer); 
152                     break;
153                 }
154             } 
155         }
156     }
157
158     return 0;
159 }
160
161 static void
162 reconnect(struct half *this) 
163 {
164     if (this->vconn != NULL) {
165         if (!reliable) {
166             fatal(0, "%s: connection dropped", this->name);
167         }
168
169         VLOG_WARN("%s: connection dropped, reconnecting", this->name);
170         vconn_close(this->vconn);
171         this->vconn = NULL;
172         buffer_delete(this->rxbuf);
173         this->rxbuf = NULL;
174     }
175     this->pollfd->revents = POLLIN | POLLOUT;
176
177     for (;;) {
178         time_t now = time(0);
179         int retval;
180
181         if (now >= this->backoff_deadline) {
182             this->backoff = 1;
183         } else {
184             this->backoff *= 2;
185             if (this->backoff > 60) {
186                 this->backoff = 60;
187             }
188             VLOG_WARN("%s: waiting %d seconds before reconnect\n",
189                       this->name, (int) (this->backoff_deadline - now));
190             sleep(this->backoff_deadline - now);
191         }
192
193         retval = vconn_open(this->name, &this->vconn);
194         if (!retval) {
195             VLOG_WARN("%s: connected", this->name);
196             if (vconn_is_passive(this->vconn)) {
197                 fatal(0, "%s: passive vconn not supported in control path",
198                       this->name);
199             }
200             this->backoff_deadline = now + this->backoff;
201             return;
202         }
203
204         if (!reliable) {
205             fatal(0, "%s: connection failed", this->name);
206         }
207         VLOG_WARN("%s: connection failed (%s)", this->name, strerror(errno));
208         this->backoff_deadline = time(0) + this->backoff;
209     }
210 }
211
212 static void
213 parse_options(int argc, char *argv[]) 
214 {
215     static struct option long_options[] = {
216         {"unreliable",  no_argument, 0, 'u'},
217         {"verbose",     optional_argument, 0, 'v'},
218         {"help",        no_argument, 0, 'h'},
219         {"version",     no_argument, 0, 'V'},
220         {0, 0, 0, 0},
221     };
222     char *short_options = long_options_to_short_options(long_options);
223     
224     for (;;) {
225         int indexptr;
226         int c;
227
228         c = getopt_long(argc, argv, short_options, long_options, &indexptr);
229         if (c == -1) {
230             break;
231         }
232
233         switch (c) {
234         case 'u':
235             reliable = false;
236             break;
237
238         case 'h':
239             usage();
240
241         case 'V':
242             printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
243             exit(EXIT_SUCCESS);
244
245         case 'v':
246             vlog_set_verbosity(optarg);
247             break;
248
249         case '?':
250             exit(EXIT_FAILURE);
251
252         default:
253             abort();
254         }
255     }
256     free(short_options);
257 }
258
259 static void
260 usage(void)
261 {
262     printf("%s: Secure Channel\n"
263            "usage: %s [OPTIONS] nl:DP_ID tcp:HOST:[PORT]\n"
264            "\nConnects to local datapath DP_ID via Netlink and \n"
265            "controller on HOST via TCP to PORT (default: %d).\n"
266            "\nNetworking options:\n"
267            "  -u, --unreliable        do not reconnect after connections drop\n"
268            "\nOther options:\n"
269            "  -v, --verbose           set maximum verbosity level\n"
270            "  -h, --help              display this help message\n"
271            "  -V, --version           display version information\n",
272            program_name, program_name, OFP_TCP_PORT);
273     exit(EXIT_SUCCESS);
274 }