Enable secchan, ofp-discover to update /etc/resolv.conf.
[sliver-openvswitch.git] / utilities / ofp-discover.c
1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
2  * Junior University
3  *
4  * We are making the OpenFlow specification and associated documentation
5  * (Software) available for public use and benefit with the expectation
6  * that others will use, modify and enhance the Software and contribute
7  * those enhancements back to the community. However, since we would
8  * like to make the Software available for broadest use, with as few
9  * restrictions as possible permission is hereby granted, free of
10  * charge, to any person obtaining a copy of this Software to deal in
11  * the Software under the copyrights without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE.
28  *
29  * The name and trademarks of copyright holder(s) may NOT be used in
30  * advertising or publicity pertaining to the Software or any
31  * derivatives without specific, written prior permission.
32  */
33
34 #include <config.h>
35 #include <getopt.h>
36 #include <limits.h>
37 #include <regex.h>
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include "command-line.h"
42 #include "daemon.h"
43 #include "dhcp-client.h"
44 #include "dhcp.h"
45 #include "dynamic-string.h"
46 #include "fatal-signal.h"
47 #include "netdev.h"
48 #include "poll-loop.h"
49 #include "util.h"
50 #include "vlog-socket.h"
51
52 #include "vlog.h"
53 #define THIS_MODULE VLM_ofp_discover
54
55 struct iface {
56     const char *name;
57     struct dhclient *dhcp;
58 };
59
60 /* The interfaces that we serve. */
61 static struct iface *ifaces;
62 static int n_ifaces;
63
64 /* --accept-vconn: Regular expression specifying the class of controller vconns
65  * that we will accept during autodiscovery. */
66 static const char *accept_controller_re = ".*";
67 static regex_t accept_controller_regex;
68
69 /* --exit-without-bind: Exit after discovering the controller, without binding
70  * the network device to an IP address? */
71 static bool exit_without_bind;
72
73 /* --exit-after-bind: Exit after discovering the controller, after binding the
74  * network device to an IP address? */
75 static bool exit_after_bind;
76
77 static bool iface_init(struct iface *, const char *netdev_name);
78 static void release_ifaces(void *aux UNUSED);
79
80 static void parse_options(int argc, char *argv[]);
81 static void usage(void) NO_RETURN;
82
83 static void modify_dhcp_request(struct dhcp_msg *, void *aux);
84 static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux);
85
86 int
87 main(int argc, char *argv[])
88 {
89     int retval;
90     int i;
91
92     set_program_name(argv[0]);
93     vlog_init();
94     parse_options(argc, argv);
95
96     argc -= optind;
97     argv += optind;
98     if (argc < 1) {
99         fatal(0, "need at least one non-option argument; "
100               "use --help for usage");
101     }
102
103     ifaces = xmalloc(argc * sizeof *ifaces);
104     n_ifaces = 0;
105     for (i = 0; i < argc; i++) {
106         if (iface_init(&ifaces[n_ifaces], argv[i])) {
107             n_ifaces++;
108         }
109     }
110     if (!n_ifaces) {
111         fatal(0, "failed to initialize any DHCP clients");
112     }
113
114     for (i = 0; i < n_ifaces; i++) {
115         struct iface *iface = &ifaces[i];
116         dhclient_init(iface->dhcp, 0);
117     }
118     fatal_signal_add_hook(release_ifaces, NULL);
119
120     retval = regcomp(&accept_controller_regex, accept_controller_re,
121                      REG_NOSUB | REG_EXTENDED);
122     if (retval) {
123         size_t length = regerror(retval, &accept_controller_regex, NULL, 0);
124         char *buffer = xmalloc(length);
125         regerror(retval, &accept_controller_regex, buffer, length);
126         fatal(0, "%s: %s", accept_controller_re, buffer);
127     }
128
129     retval = vlog_server_listen(NULL, NULL);
130     if (retval) {
131         fatal(retval, "Could not listen for vlog connections");
132     }
133
134     signal(SIGPIPE, SIG_IGN);
135     for (;;) {
136         fatal_signal_block();
137         for (i = 0; i < n_ifaces; i++) {
138             struct iface *iface = &ifaces[i];
139             dhclient_run(iface->dhcp);
140             if (dhclient_changed(iface->dhcp)) {
141                 bool is_bound = dhclient_is_bound(iface->dhcp);
142                 int j;
143
144                 /* Configure network device. */
145                 if (!exit_without_bind) {
146                     dhclient_configure_netdev(iface->dhcp);
147                     dhclient_update_resolv_conf(iface->dhcp);
148                 }
149
150                 if (is_bound) {
151                     static bool detached = false;
152                     struct ds ds;
153
154                     /* Disable timeout, since discovery was successful. */
155                     alarm(0);
156
157                     /* Print discovered parameters. */
158                     ds_init(&ds);
159                     dhcp_msg_to_string(dhclient_get_config(iface->dhcp),
160                                        true, &ds);
161                     fputs(ds_cstr(&ds), stdout);
162                     putchar('\n');
163                     fflush(stdout);
164                     ds_destroy(&ds);
165
166                     /* Exit if the user requested it. */
167                     if (exit_without_bind) {
168                         VLOG_DBG("exiting because of successful binding on %s "
169                                  "and --exit-without-bind specified",
170                                  iface->name);
171                         exit(0);
172                     }
173                     if (exit_after_bind) {
174                         VLOG_DBG("exiting because of successful binding on %s "
175                                  "and --exit-after-bind specified",
176                                  iface->name);
177                         exit(0);
178                     }
179
180                     /* Detach into background, if we haven't already. */
181                     if (!detached) {
182                         detached = true;
183                         daemonize();
184                     }
185                 }
186
187                 /* We only want an address on a single one of our interfaces.
188                  * So: if we have an address on this interface, stop looking
189                  * for one on the others; if we don't have an address on this
190                  * interface, start looking everywhere. */
191                 for (j = 0; j < n_ifaces; j++) {
192                     struct iface *if2 = &ifaces[j];
193                     if (iface != if2) {
194                         if (is_bound) {
195                             dhclient_release(if2->dhcp);
196                         } else {
197                             dhclient_init(if2->dhcp, 0);
198                         }
199                     }
200                 }
201             }
202         }
203         for (i = 0; i < n_ifaces; i++) {
204             struct iface *iface = &ifaces[i];
205             dhclient_wait(iface->dhcp);
206         }
207         fatal_signal_unblock();
208         poll_block();
209     }
210
211     return 0;
212 }
213
214 static bool
215 iface_init(struct iface *iface, const char *netdev_name)
216 {
217     int retval;
218
219     iface->name = netdev_name;
220     iface->dhcp = NULL;
221
222     if (exit_after_bind) {
223         /* Bring this interface up permanently, so that the bound address
224          * persists past program termination. */
225         struct netdev *netdev;
226
227         retval = netdev_open(iface->name, NETDEV_ETH_TYPE_NONE, &netdev);
228         if (retval) {
229             error(retval, "Could not open %s device", iface->name);
230             return false;
231         }
232         retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
233         if (retval) {
234             error(retval, "Could not bring %s device up", iface->name);
235             return false;
236         }
237         netdev_close(netdev);
238     }
239
240     retval = dhclient_create(iface->name, modify_dhcp_request,
241                              validate_dhcp_offer, NULL, &iface->dhcp);
242     if (retval) {
243         error(retval, "%s: failed to initialize DHCP client", iface->name);
244         return false;
245     }
246
247     return true;
248 }
249
250 static void
251 release_ifaces(void *aux UNUSED)
252 {
253     int i;
254
255     for (i = 0; i < n_ifaces; i++) {
256         struct dhclient *dhcp = ifaces[i].dhcp;
257         dhclient_release(dhcp);
258         if (dhclient_changed(dhcp)) {
259             dhclient_configure_netdev(dhcp);
260         }
261     }
262 }
263
264 static void
265 modify_dhcp_request(struct dhcp_msg *msg, void *aux)
266 {
267     dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
268 }
269
270 static bool
271 validate_dhcp_offer(const struct dhcp_msg *msg, void *aux)
272 {
273     char *vconn_name;
274     bool accept;
275
276     vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
277     if (!vconn_name) {
278         VLOG_WARN("rejecting DHCP offer missing controller vconn");
279         return false;
280     }
281     accept = !regexec(&accept_controller_regex, vconn_name, 0, NULL, 0);
282     free(vconn_name);
283     return accept;
284 }
285
286 static void
287 parse_options(int argc, char *argv[])
288 {
289     enum {
290         OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
291         OPT_EXIT_WITHOUT_BIND,
292         OPT_EXIT_AFTER_BIND,
293         OPT_NO_DETACH,
294     };
295     static struct option long_options[] = {
296         {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
297         {"exit-without-bind", no_argument, 0, OPT_EXIT_WITHOUT_BIND},
298         {"exit-after-bind", no_argument, 0, OPT_EXIT_AFTER_BIND},
299         {"no-detach",   no_argument, 0, OPT_NO_DETACH},
300         {"timeout",     required_argument, 0, 't'},
301         {"pidfile",     optional_argument, 0, 'P'},
302         {"verbose",     optional_argument, 0, 'v'},
303         {"help",        no_argument, 0, 'h'},
304         {"version",     no_argument, 0, 'V'},
305         {0, 0, 0, 0},
306     };
307     char *short_options = long_options_to_short_options(long_options);
308     bool detach_after_bind = true;
309
310     for (;;) {
311         unsigned long int timeout;
312         int c;
313
314         c = getopt_long(argc, argv, short_options, long_options, NULL);
315         if (c == -1) {
316             break;
317         }
318
319         switch (c) {
320         case OPT_ACCEPT_VCONN:
321             accept_controller_re = (optarg[0] == '^'
322                                     ? optarg
323                                     : xasprintf("^%s", optarg));
324             break;
325
326         case OPT_EXIT_WITHOUT_BIND:
327             exit_without_bind = true;
328             break;
329
330         case OPT_EXIT_AFTER_BIND:
331             exit_after_bind = true;
332             break;
333
334         case OPT_NO_DETACH:
335             detach_after_bind = false;
336             break;
337
338         case 'P':
339             set_pidfile(optarg);
340             break;
341
342         case 't':
343             timeout = strtoul(optarg, NULL, 10);
344             if (timeout <= 0) {
345                 fatal(0, "value %s on -t or --timeout is not at least 1",
346                       optarg);
347             } else if (timeout < UINT_MAX) {
348                 /* Add 1 because historical implementations allow an alarm to
349                  * occur up to a second early. */
350                 alarm(timeout + 1);
351             } else {
352                 alarm(UINT_MAX);
353             }
354             signal(SIGALRM, SIG_DFL);
355             break;
356
357         case 'h':
358             usage();
359
360         case 'V':
361             printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
362             exit(EXIT_SUCCESS);
363
364         case 'v':
365             vlog_set_verbosity(optarg);
366             break;
367
368         case '?':
369             exit(EXIT_FAILURE);
370
371         default:
372             abort();
373         }
374     }
375     free(short_options);
376
377     if ((exit_without_bind + exit_after_bind + !detach_after_bind) > 1) {
378         fatal(0, "--exit-without-bind, --exit-after-bind, and --no-detach "
379               "are mutually exclusive");
380     }
381     if (detach_after_bind) {
382         set_detach();
383     }
384 }
385
386 static void
387 usage(void)
388 {
389     printf("%s: a tool for discovering OpenFlow controllers.\n"
390            "usage: %s [OPTIONS] NETDEV [NETDEV...]\n"
391            "where each NETDEV is a network device on which to perform\n"
392            "controller discovery.\n"
393            "\nOrdinarily, ofp-discover runs in the foreground until it\n"
394            "obtains an IP address and discovers an OpenFlow controller via\n"
395            "DHCP, then it prints information about the controller to stdout\n"
396            "and detaches to the background to maintain the IP address lease.\n"
397            "\nNetworking options:\n"
398            "  --accept-vconn=REGEX    accept matching discovered controllers\n"
399            "  --exit-without-bind     exit after discovery, without binding\n"
400            "  --exit-after-bind       exit after discovery, after binding\n"
401            "  --no-detach             do not detach after discovery\n"
402            "\nOther options:\n"
403            "  -t, --timeout=SECS      give up discovery after SECS seconds\n"
404            "  -P, --pidfile[=FILE]    create pidfile (default: %s/%s.pid)\n"
405            "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
406            "  -v, --verbose           set maximum verbosity level\n"
407            "  -h, --help              display this help message\n"
408            "  -V, --version           display version information\n",
409            program_name, program_name, RUNDIR, program_name);
410     exit(EXIT_SUCCESS);
411 }