2 * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
24 #include "command-line.h"
26 #include "dhcp-client.h"
29 #include "dynamic-string.h"
30 #include "fatal-signal.h"
32 #include "poll-loop.h"
38 VLOG_DEFINE_THIS_MODULE(ovs_discover);
42 struct dhclient *dhcp;
45 /* The interfaces that we serve. */
46 static struct iface *ifaces;
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;
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;
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;
62 static bool iface_init(struct iface *, const char *netdev_name);
63 static void release_ifaces(void *aux OVS_UNUSED);
65 static void parse_options(int argc, char *argv[]);
66 static void usage(void) NO_RETURN;
68 static void modify_dhcp_request(struct dhcp_msg *, void *aux);
69 static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux);
72 main(int argc, char *argv[])
74 struct unixctl_server *unixctl;
78 proctitle_init(argc, argv);
79 set_program_name(argv[0]);
80 parse_options(argc, argv);
85 ovs_fatal(0, "need at least one non-option argument; "
86 "use --help for usage");
89 ifaces = xmalloc(argc * sizeof *ifaces);
91 for (i = 0; i < argc; i++) {
92 if (iface_init(&ifaces[n_ifaces], argv[i])) {
97 ovs_fatal(0, "failed to initialize any DHCP clients");
100 for (i = 0; i < n_ifaces; i++) {
101 struct iface *iface = &ifaces[i];
102 dhclient_init(iface->dhcp, 0);
104 fatal_signal_add_hook(release_ifaces, NULL, NULL, true);
106 retval = regcomp(&accept_controller_regex, accept_controller_re,
107 REG_NOSUB | REG_EXTENDED);
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);
115 retval = unixctl_server_create(NULL, &unixctl);
120 die_if_already_running();
122 signal(SIGPIPE, SIG_IGN);
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);
131 /* Configure network device. */
132 if (!exit_without_bind) {
133 dhclient_configure_netdev(iface->dhcp);
134 dhclient_update_resolv_conf(iface->dhcp);
138 static bool detached = false;
141 /* Disable timeout, since discovery was successful. */
144 /* Print discovered parameters. */
146 dhcp_msg_to_string(dhclient_get_config(iface->dhcp),
148 fputs(ds_cstr(&ds), stdout);
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",
160 if (exit_after_bind) {
161 VLOG_DBG("exiting because of successful binding on %s "
162 "and --exit-after-bind specified",
167 /* Detach into background, if we haven't already. */
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];
182 dhclient_release(if2->dhcp);
184 dhclient_init(if2->dhcp, 0);
190 unixctl_server_run(unixctl);
191 for (i = 0; i < n_ifaces; i++) {
192 struct iface *iface = &ifaces[i];
193 dhclient_wait(iface->dhcp);
195 unixctl_server_wait(unixctl);
203 iface_init(struct iface *iface, const char *netdev_name)
207 iface->name = netdev_name;
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;
215 retval = netdev_open_default(iface->name, &netdev);
217 ovs_error(retval, "Could not open %s device", iface->name);
220 retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
222 ovs_error(retval, "Could not bring %s device up", iface->name);
225 netdev_close(netdev);
228 retval = dhclient_create(iface->name, modify_dhcp_request,
229 validate_dhcp_offer, NULL, &iface->dhcp);
231 ovs_error(retval, "%s: failed to initialize DHCP client", iface->name);
239 release_ifaces(void *aux OVS_UNUSED)
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);
253 modify_dhcp_request(struct dhcp_msg *msg, void *aux OVS_UNUSED)
255 dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
259 validate_dhcp_offer(const struct dhcp_msg *msg, void *aux OVS_UNUSED)
261 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
265 vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
267 VLOG_WARN_RL(&rl, "rejecting DHCP offer missing controller vconn");
270 accept = !regexec(&accept_controller_regex, vconn_name, 0, NULL, 0);
276 parse_options(int argc, char *argv[])
279 OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
280 OPT_EXIT_WITHOUT_BIND,
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'},
299 char *short_options = long_options_to_short_options(long_options);
300 bool detach_after_bind = true;
303 unsigned long int timeout;
306 c = getopt_long(argc, argv, short_options, long_options, NULL);
312 case OPT_ACCEPT_VCONN:
313 accept_controller_re = (optarg[0] == '^'
315 : xasprintf("^%s", optarg));
318 case OPT_EXIT_WITHOUT_BIND:
319 exit_without_bind = true;
322 case OPT_EXIT_AFTER_BIND:
323 exit_after_bind = true;
327 detach_after_bind = false;
334 case OPT_OVERWRITE_PIDFILE:
335 ignore_existing_pidfile();
339 timeout = strtoul(optarg, NULL, 10);
341 ovs_fatal(0, "value %s on -t or --timeout is not at least 1",
346 signal(SIGALRM, SIG_DFL);
353 OVS_PRINT_VERSION(0, 0);
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");
371 if (detach_after_bind) {
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);
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 "
399 " -h, --help display this help message\n"
400 " -V, --version display version information\n",
401 ovs_rundir(), program_name);