ovs-vsctl: Remove stray \ from end of manpage.
[sliver-openvswitch.git] / utilities / ovs-discover.c
1 /*
2  * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <config.h>
18 #include <getopt.h>
19 #include <limits.h>
20 #include <regex.h>
21 #include <signal.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include "command-line.h"
25 #include "daemon.h"
26 #include "dhcp-client.h"
27 #include "dhcp.h"
28 #include "dirs.h"
29 #include "dynamic-string.h"
30 #include "fatal-signal.h"
31 #include "netdev.h"
32 #include "poll-loop.h"
33 #include "timeval.h"
34 #include "unixctl.h"
35 #include "util.h"
36 #include "vlog.h"
37
38 VLOG_DEFINE_THIS_MODULE(ovs_discover);
39
40 struct iface {
41     const char *name;
42     struct dhclient *dhcp;
43 };
44
45 /* The interfaces that we serve. */
46 static struct iface *ifaces;
47 static int n_ifaces;
48
49 /* --accept-vconn: Regular expression specifying the class of controller vconns
50  * that we will accept during autodiscovery. */
51 static const char *accept_controller_re = "tcp:.*";
52 static regex_t accept_controller_regex;
53
54 /* --exit-without-bind: Exit after discovering the controller, without binding
55  * the network device to an IP address? */
56 static bool exit_without_bind;
57
58 /* --exit-after-bind: Exit after discovering the controller, after binding the
59  * network device to an IP address? */
60 static bool exit_after_bind;
61
62 static bool iface_init(struct iface *, const char *netdev_name);
63 static void release_ifaces(void *aux OVS_UNUSED);
64
65 static void parse_options(int argc, char *argv[]);
66 static void usage(void) NO_RETURN;
67
68 static void modify_dhcp_request(struct dhcp_msg *, void *aux);
69 static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux);
70
71 int
72 main(int argc, char *argv[])
73 {
74     struct unixctl_server *unixctl;
75     int retval;
76     int i;
77
78     proctitle_init(argc, argv);
79     set_program_name(argv[0]);
80     parse_options(argc, argv);
81
82     argc -= optind;
83     argv += optind;
84     if (argc < 1) {
85         ovs_fatal(0, "need at least one non-option argument; "
86                   "use --help for usage");
87     }
88
89     ifaces = xmalloc(argc * sizeof *ifaces);
90     n_ifaces = 0;
91     for (i = 0; i < argc; i++) {
92         if (iface_init(&ifaces[n_ifaces], argv[i])) {
93             n_ifaces++;
94         }
95     }
96     if (!n_ifaces) {
97         ovs_fatal(0, "failed to initialize any DHCP clients");
98     }
99
100     for (i = 0; i < n_ifaces; i++) {
101         struct iface *iface = &ifaces[i];
102         dhclient_init(iface->dhcp, 0);
103     }
104     fatal_signal_add_hook(release_ifaces, NULL, NULL, true);
105
106     retval = regcomp(&accept_controller_regex, accept_controller_re,
107                      REG_NOSUB | REG_EXTENDED);
108     if (retval) {
109         size_t length = regerror(retval, &accept_controller_regex, NULL, 0);
110         char *buffer = xmalloc(length);
111         regerror(retval, &accept_controller_regex, buffer, length);
112         ovs_fatal(0, "%s: %s", accept_controller_re, buffer);
113     }
114
115     retval = unixctl_server_create(NULL, &unixctl);
116     if (retval) {
117         exit(EXIT_FAILURE);
118     }
119
120     die_if_already_running();
121
122     signal(SIGPIPE, SIG_IGN);
123     for (;;) {
124         for (i = 0; i < n_ifaces; i++) {
125             struct iface *iface = &ifaces[i];
126             dhclient_run(iface->dhcp);
127             if (dhclient_changed(iface->dhcp)) {
128                 bool is_bound = dhclient_is_bound(iface->dhcp);
129                 int j;
130
131                 /* Configure network device. */
132                 if (!exit_without_bind) {
133                     dhclient_configure_netdev(iface->dhcp);
134                     dhclient_update_resolv_conf(iface->dhcp);
135                 }
136
137                 if (is_bound) {
138                     static bool detached = false;
139                     struct ds ds;
140
141                     /* Disable timeout, since discovery was successful. */
142                     time_alarm(0);
143
144                     /* Print discovered parameters. */
145                     ds_init(&ds);
146                     dhcp_msg_to_string(dhclient_get_config(iface->dhcp),
147                                        true, &ds);
148                     fputs(ds_cstr(&ds), stdout);
149                     putchar('\n');
150                     fflush(stdout);
151                     ds_destroy(&ds);
152
153                     /* Exit if the user requested it. */
154                     if (exit_without_bind) {
155                         VLOG_DBG("exiting because of successful binding on %s "
156                                  "and --exit-without-bind specified",
157                                  iface->name);
158                         exit(0);
159                     }
160                     if (exit_after_bind) {
161                         VLOG_DBG("exiting because of successful binding on %s "
162                                  "and --exit-after-bind specified",
163                                  iface->name);
164                         exit(0);
165                     }
166
167                     /* Detach into background, if we haven't already. */
168                     if (!detached) {
169                         detached = true;
170                         daemonize();
171                     }
172                 }
173
174                 /* We only want an address on a single one of our interfaces.
175                  * So: if we have an address on this interface, stop looking
176                  * for one on the others; if we don't have an address on this
177                  * interface, start looking everywhere. */
178                 for (j = 0; j < n_ifaces; j++) {
179                     struct iface *if2 = &ifaces[j];
180                     if (iface != if2) {
181                         if (is_bound) {
182                             dhclient_release(if2->dhcp);
183                         } else {
184                             dhclient_init(if2->dhcp, 0);
185                         }
186                     }
187                 }
188             }
189         }
190         unixctl_server_run(unixctl);
191         for (i = 0; i < n_ifaces; i++) {
192             struct iface *iface = &ifaces[i];
193             dhclient_wait(iface->dhcp);
194         }
195         unixctl_server_wait(unixctl);
196         poll_block();
197     }
198
199     return 0;
200 }
201
202 static bool
203 iface_init(struct iface *iface, const char *netdev_name)
204 {
205     int retval;
206
207     iface->name = netdev_name;
208     iface->dhcp = NULL;
209
210     if (exit_after_bind) {
211         /* Bring this interface up permanently, so that the bound address
212          * persists past program termination. */
213         struct netdev *netdev;
214
215         retval = netdev_open_default(iface->name, &netdev);
216         if (retval) {
217             ovs_error(retval, "Could not open %s device", iface->name);
218             return false;
219         }
220         retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
221         if (retval) {
222             ovs_error(retval, "Could not bring %s device up", iface->name);
223             return false;
224         }
225         netdev_close(netdev);
226     }
227
228     retval = dhclient_create(iface->name, modify_dhcp_request,
229                              validate_dhcp_offer, NULL, &iface->dhcp);
230     if (retval) {
231         ovs_error(retval, "%s: failed to initialize DHCP client", iface->name);
232         return false;
233     }
234
235     return true;
236 }
237
238 static void
239 release_ifaces(void *aux OVS_UNUSED)
240 {
241     int i;
242
243     for (i = 0; i < n_ifaces; i++) {
244         struct dhclient *dhcp = ifaces[i].dhcp;
245         dhclient_release(dhcp);
246         if (dhclient_changed(dhcp)) {
247             dhclient_configure_netdev(dhcp);
248         }
249     }
250 }
251
252 static void
253 modify_dhcp_request(struct dhcp_msg *msg, void *aux OVS_UNUSED)
254 {
255     dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
256 }
257
258 static bool
259 validate_dhcp_offer(const struct dhcp_msg *msg, void *aux OVS_UNUSED)
260 {
261     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
262     char *vconn_name;
263     bool accept;
264
265     vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
266     if (!vconn_name) {
267         VLOG_WARN_RL(&rl, "rejecting DHCP offer missing controller vconn");
268         return false;
269     }
270     accept = !regexec(&accept_controller_regex, vconn_name, 0, NULL, 0);
271     free(vconn_name);
272     return accept;
273 }
274
275 static void
276 parse_options(int argc, char *argv[])
277 {
278     enum {
279         OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
280         OPT_EXIT_WITHOUT_BIND,
281         OPT_EXIT_AFTER_BIND,
282         OPT_NO_DETACH,
283         VLOG_OPTION_ENUMS,
284         DAEMON_OPTION_ENUMS
285     };
286     static struct option long_options[] = {
287         {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
288         {"exit-without-bind", no_argument, 0, OPT_EXIT_WITHOUT_BIND},
289         {"exit-after-bind", no_argument, 0, OPT_EXIT_AFTER_BIND},
290         {"no-detach",   no_argument, 0, OPT_NO_DETACH},
291         {"timeout",     required_argument, 0, 't'},
292         {"pidfile",     optional_argument, 0, OPT_PIDFILE},
293         {"overwrite-pidfile", no_argument, 0, OPT_OVERWRITE_PIDFILE},
294         {"help",        no_argument, 0, 'h'},
295         {"version",     no_argument, 0, 'V'},
296         VLOG_LONG_OPTIONS,
297         {0, 0, 0, 0},
298     };
299     char *short_options = long_options_to_short_options(long_options);
300     bool detach_after_bind = true;
301
302     for (;;) {
303         unsigned long int timeout;
304         int c;
305
306         c = getopt_long(argc, argv, short_options, long_options, NULL);
307         if (c == -1) {
308             break;
309         }
310
311         switch (c) {
312         case OPT_ACCEPT_VCONN:
313             accept_controller_re = (optarg[0] == '^'
314                                     ? optarg
315                                     : xasprintf("^%s", optarg));
316             break;
317
318         case OPT_EXIT_WITHOUT_BIND:
319             exit_without_bind = true;
320             break;
321
322         case OPT_EXIT_AFTER_BIND:
323             exit_after_bind = true;
324             break;
325
326         case OPT_NO_DETACH:
327             detach_after_bind = false;
328             break;
329
330         case OPT_PIDFILE:
331             set_pidfile(optarg);
332             break;
333
334         case OPT_OVERWRITE_PIDFILE:
335             ignore_existing_pidfile();
336             break;
337
338         case 't':
339             timeout = strtoul(optarg, NULL, 10);
340             if (timeout <= 0) {
341                 ovs_fatal(0, "value %s on -t or --timeout is not at least 1",
342                           optarg);
343             } else {
344                 time_alarm(timeout);
345             }
346             signal(SIGALRM, SIG_DFL);
347             break;
348
349         case 'h':
350             usage();
351
352         case 'V':
353             OVS_PRINT_VERSION(0, 0);
354             exit(EXIT_SUCCESS);
355
356         VLOG_OPTION_HANDLERS
357
358         case '?':
359             exit(EXIT_FAILURE);
360
361         default:
362             abort();
363         }
364     }
365     free(short_options);
366
367     if ((exit_without_bind + exit_after_bind + !detach_after_bind) > 1) {
368         ovs_fatal(0, "--exit-without-bind, --exit-after-bind, and --no-detach "
369                   "are mutually exclusive");
370     }
371     if (detach_after_bind) {
372         set_detach();
373     }
374 }
375
376 static void
377 usage(void)
378 {
379     printf("%s: a tool for discovering OpenFlow controllers.\n"
380            "usage: %s [OPTIONS] NETDEV [NETDEV...]\n"
381            "where each NETDEV is a network device on which to perform\n"
382            "controller discovery.\n"
383            "\nOrdinarily, ovs-discover runs in the foreground until it\n"
384            "obtains an IP address and discovers an OpenFlow controller via\n"
385            "DHCP, then it prints information about the controller to stdout\n"
386            "and detaches to the background to maintain the IP address lease.\n"
387            "\nNetworking options:\n"
388            "  --accept-vconn=REGEX    accept matching discovered controllers\n"
389            "  --exit-without-bind     exit after discovery, without binding\n"
390            "  --exit-after-bind       exit after discovery, after binding\n"
391            "  --no-detach             do not detach after discovery\n",
392            program_name, program_name);
393     vlog_usage();
394     printf("\nOther options:\n"
395            "  -t, --timeout=SECS      give up discovery after SECS seconds\n"
396            "  --pidfile[=FILE]        create pidfile (default: %s/%s.pid)\n"
397            "  --overwrite-pidfile     with --pidfile, start even if already "
398                                       "running\n"
399            "  -h, --help              display this help message\n"
400            "  -V, --version           display version information\n",
401            ovs_rundir(), program_name);
402     exit(EXIT_SUCCESS);
403 }