Initial import
[openvswitch] / 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 <unistd.h>
28
29 #include "buffer.h"
30 #include "command-line.h"
31 #include "compiler.h"
32 #include "fault.h"
33 #include "util.h"
34 #include "vconn.h"
35 #include "vlog-socket.h"
36 #include "openflow.h"
37
38 #include "vlog.h"
39 #define THIS_MODULE VLM_secchan
40
41 static void parse_options(int argc, char *argv[]);
42 static void usage(void) NO_RETURN;
43
44 static bool reliable = true;
45
46 struct half {
47     const char *name;
48     struct vconn *vconn;
49     struct pollfd *pollfd;
50     struct buffer *rxbuf;
51 };
52
53 static void reconnect(struct half *);
54
55 int
56 main(int argc, char *argv[])
57 {
58     struct half halves[2];
59     struct pollfd pollfds[2 + 1];
60     struct vlog_server *vlog_server;
61     int retval;
62     int i;
63
64     set_program_name(argv[0]);
65     register_fault_handlers();
66     vlog_init();
67     parse_options(argc, argv);
68
69     if (argc - optind != 2) {
70         fatal(0, "exactly two peer arguments required; use --help for usage");
71     }
72
73     retval = vlog_server_listen(NULL, &vlog_server);
74     if (retval) {
75         fatal(retval, "Could not listen for vlog connections");
76     }
77
78     for (i = 0; i < 2; i++) {
79         halves[i].name = argv[optind + i];
80         halves[i].vconn = NULL;
81         halves[i].pollfd = &pollfds[i];
82         halves[i].rxbuf = NULL;
83         reconnect(&halves[i]);
84     }
85     for (;;) {
86         /* Wait until there's something to do. */
87         for (i = 0; i < 2; i++) {
88             struct half *this = &halves[i];
89             struct half *peer = &halves[!i];
90             int want = 0;
91             if (peer->rxbuf) {
92                 want |= WANT_SEND;
93             }
94             if (!this->rxbuf) {
95                 want |= WANT_RECV;
96             }
97             this->pollfd->fd = -1;
98             this->pollfd->events = 0;
99             vconn_prepoll(this->vconn, want, this->pollfd);
100         }
101         if (vlog_server) {
102             pollfds[2].fd = vlog_server_get_fd(vlog_server);
103             pollfds[2].events = POLLIN;
104         }
105         do {
106             retval = poll(pollfds, 2 + (vlog_server != NULL), -1);
107         } while (retval < 0 && errno == EINTR);
108         if (retval <= 0) {
109             fatal(retval < 0 ? errno : 0, "poll");
110         }
111
112         /* Let each connection deal with any pending operations. */
113         for (i = 0; i < 2; i++) {
114             struct half *this = &halves[i];
115             vconn_postpoll(this->vconn, &this->pollfd->revents);
116             if (this->pollfd->revents & POLLERR) {
117                 this->pollfd->revents |= POLLIN | POLLOUT;
118             }
119         }
120         if (vlog_server && pollfds[2].revents) {
121             vlog_server_poll(vlog_server);
122         }
123
124         /* Do as much work as we can without waiting. */
125         for (i = 0; i < 2; i++) {
126             struct half *this = &halves[i];
127             struct half *peer = &halves[!i];
128
129             if (this->pollfd->revents & POLLIN && !this->rxbuf) {
130                 retval = vconn_recv(this->vconn, &this->rxbuf);
131                 if (retval && retval != EAGAIN) {
132                     VLOG_DBG("%s: recv: closing connection: %s",
133                              this->name, strerror(retval));
134                     reconnect(this);
135                     break;
136                 }
137             }
138
139             if (peer->pollfd->revents & POLLOUT && this->rxbuf) {
140                 retval = vconn_send(peer->vconn, this->rxbuf);
141                 if (!retval) {
142                     this->rxbuf = NULL;
143                 } else if (retval != EAGAIN) {
144                     VLOG_DBG("%s: send: closing connection: %s",
145                              peer->name, strerror(retval));
146                     reconnect(peer); 
147                     break;
148                 }
149             } 
150         }
151     }
152
153     return 0;
154 }
155
156 static void
157 reconnect(struct half *this) 
158 {
159     int backoff;
160     
161     if (this->vconn != NULL) {
162         if (!reliable) {
163             fatal(0, "%s: connection dropped", this->name);
164         }
165
166         VLOG_WARN("%s: connection dropped, reconnecting", this->name);
167         vconn_close(this->vconn);
168         this->vconn = NULL;
169         buffer_delete(this->rxbuf);
170         this->rxbuf = NULL;
171     }
172     this->pollfd->revents = POLLIN | POLLOUT;
173
174     for (backoff = 1; ; backoff = MIN(backoff * 2, 60)) {
175         int retval = vconn_open(this->name, &this->vconn);
176         if (!retval) {
177             VLOG_WARN("%s: connected", this->name);
178             if (vconn_is_passive(this->vconn)) {
179                 fatal(0, "%s: passive vconn not supported in control path",
180                       this->name);
181             }
182             return;
183         }
184
185         if (!reliable) {
186             fatal(0, "%s: connection failed", this->name);
187         }
188         VLOG_WARN("%s: connection failed (%s), reconnecting",
189                   this->name, strerror(errno));
190         sleep(backoff);
191     }
192 }
193
194 static void
195 parse_options(int argc, char *argv[]) 
196 {
197     static struct option long_options[] = {
198         {"unreliable",  no_argument, 0, 'u'},
199         {"verbose",     optional_argument, 0, 'v'},
200         {"help",        no_argument, 0, 'h'},
201         {"version",     no_argument, 0, 'V'},
202         {0, 0, 0, 0},
203     };
204     char *short_options = long_options_to_short_options(long_options);
205     
206     for (;;) {
207         int indexptr;
208         int c;
209
210         c = getopt_long(argc, argv, short_options, long_options, &indexptr);
211         if (c == -1) {
212             break;
213         }
214
215         switch (c) {
216         case 'u':
217             reliable = false;
218             break;
219
220         case 'h':
221             usage();
222
223         case 'V':
224             printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
225             exit(EXIT_SUCCESS);
226
227         case 'v':
228             vlog_set_verbosity(optarg);
229             break;
230
231         case '?':
232             exit(EXIT_FAILURE);
233
234         default:
235             abort();
236         }
237     }
238     free(short_options);
239 }
240
241 static void
242 usage(void)
243 {
244     printf("%s: Secure Channel\n"
245            "usage: %s [OPTIONS] nl:DP_ID tcp:HOST:[PORT]\n"
246            "\nConnects to local datapath DP_ID via Netlink and \n"
247            "controller on HOST via TCP to PORT (default: %d).\n"
248            "\nNetworking options:\n"
249            "  -u, --unreliable        do not reconnect after connections drop\n"
250            "\nOther options:\n"
251            "  -v, --verbose           set maximum verbosity level\n"
252            "  -h, --help              display this help message\n"
253            "  -V, --version           display version information\n",
254            program_name, program_name, OFP_TCP_PORT);
255     exit(EXIT_SUCCESS);
256 }