2 * Copyright (c) 2008, 2009 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 #define THIS_MODULE VLM_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 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 set_program_name(argv[0]);
81 parse_options(argc, argv);
86 ovs_fatal(0, "need at least one non-option argument; "
87 "use --help for usage");
90 ifaces = xmalloc(argc * sizeof *ifaces);
92 for (i = 0; i < argc; i++) {
93 if (iface_init(&ifaces[n_ifaces], argv[i])) {
98 ovs_fatal(0, "failed to initialize any DHCP clients");
101 for (i = 0; i < n_ifaces; i++) {
102 struct iface *iface = &ifaces[i];
103 dhclient_init(iface->dhcp, 0);
105 fatal_signal_add_hook(release_ifaces, NULL, true);
107 retval = regcomp(&accept_controller_regex, accept_controller_re,
108 REG_NOSUB | REG_EXTENDED);
110 size_t length = regerror(retval, &accept_controller_regex, NULL, 0);
111 char *buffer = xmalloc(length);
112 regerror(retval, &accept_controller_regex, buffer, length);
113 ovs_fatal(0, "%s: %s", accept_controller_re, buffer);
116 retval = unixctl_server_create(NULL, &unixctl);
118 ovs_fatal(retval, "Could not listen for unixctl connections");
121 die_if_already_running();
123 signal(SIGPIPE, SIG_IGN);
125 for (i = 0; i < n_ifaces; i++) {
126 struct iface *iface = &ifaces[i];
127 dhclient_run(iface->dhcp);
128 if (dhclient_changed(iface->dhcp)) {
129 bool is_bound = dhclient_is_bound(iface->dhcp);
132 /* Configure network device. */
133 if (!exit_without_bind) {
134 dhclient_configure_netdev(iface->dhcp);
135 dhclient_update_resolv_conf(iface->dhcp);
139 static bool detached = false;
142 /* Disable timeout, since discovery was successful. */
145 /* Print discovered parameters. */
147 dhcp_msg_to_string(dhclient_get_config(iface->dhcp),
149 fputs(ds_cstr(&ds), stdout);
154 /* Exit if the user requested it. */
155 if (exit_without_bind) {
156 VLOG_DBG("exiting because of successful binding on %s "
157 "and --exit-without-bind specified",
161 if (exit_after_bind) {
162 VLOG_DBG("exiting because of successful binding on %s "
163 "and --exit-after-bind specified",
168 /* Detach into background, if we haven't already. */
175 /* We only want an address on a single one of our interfaces.
176 * So: if we have an address on this interface, stop looking
177 * for one on the others; if we don't have an address on this
178 * interface, start looking everywhere. */
179 for (j = 0; j < n_ifaces; j++) {
180 struct iface *if2 = &ifaces[j];
183 dhclient_release(if2->dhcp);
185 dhclient_init(if2->dhcp, 0);
191 unixctl_server_run(unixctl);
192 for (i = 0; i < n_ifaces; i++) {
193 struct iface *iface = &ifaces[i];
194 dhclient_wait(iface->dhcp);
196 unixctl_server_wait(unixctl);
204 iface_init(struct iface *iface, const char *netdev_name)
208 iface->name = netdev_name;
211 if (exit_after_bind) {
212 /* Bring this interface up permanently, so that the bound address
213 * persists past program termination. */
214 struct netdev *netdev;
216 retval = netdev_open(iface->name, NETDEV_ETH_TYPE_NONE, &netdev);
218 ovs_error(retval, "Could not open %s device", iface->name);
221 retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
223 ovs_error(retval, "Could not bring %s device up", iface->name);
226 netdev_close(netdev);
229 retval = dhclient_create(iface->name, modify_dhcp_request,
230 validate_dhcp_offer, NULL, &iface->dhcp);
232 ovs_error(retval, "%s: failed to initialize DHCP client", iface->name);
240 release_ifaces(void *aux UNUSED)
244 for (i = 0; i < n_ifaces; i++) {
245 struct dhclient *dhcp = ifaces[i].dhcp;
246 dhclient_release(dhcp);
247 if (dhclient_changed(dhcp)) {
248 dhclient_configure_netdev(dhcp);
254 modify_dhcp_request(struct dhcp_msg *msg, void *aux UNUSED)
256 dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
260 validate_dhcp_offer(const struct dhcp_msg *msg, void *aux UNUSED)
262 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
266 vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
268 VLOG_WARN_RL(&rl, "rejecting DHCP offer missing controller vconn");
271 accept = !regexec(&accept_controller_regex, vconn_name, 0, NULL, 0);
277 parse_options(int argc, char *argv[])
280 OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
281 OPT_EXIT_WITHOUT_BIND,
285 static struct option long_options[] = {
286 {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
287 {"exit-without-bind", no_argument, 0, OPT_EXIT_WITHOUT_BIND},
288 {"exit-after-bind", no_argument, 0, OPT_EXIT_AFTER_BIND},
289 {"no-detach", no_argument, 0, OPT_NO_DETACH},
290 {"timeout", required_argument, 0, 't'},
291 {"pidfile", optional_argument, 0, OPT_PIDFILE},
292 {"overwrite-pidfile", no_argument, 0, OPT_OVERWRITE_PIDFILE},
293 {"verbose", optional_argument, 0, 'v'},
294 {"help", no_argument, 0, 'h'},
295 {"version", no_argument, 0, 'V'},
298 char *short_options = long_options_to_short_options(long_options);
299 bool detach_after_bind = true;
302 unsigned long int timeout;
305 c = getopt_long(argc, argv, short_options, long_options, NULL);
311 case OPT_ACCEPT_VCONN:
312 accept_controller_re = (optarg[0] == '^'
314 : xasprintf("^%s", optarg));
317 case OPT_EXIT_WITHOUT_BIND:
318 exit_without_bind = true;
321 case OPT_EXIT_AFTER_BIND:
322 exit_after_bind = true;
326 detach_after_bind = false;
333 case OPT_OVERWRITE_PIDFILE:
334 ignore_existing_pidfile();
338 timeout = strtoul(optarg, NULL, 10);
340 ovs_fatal(0, "value %s on -t or --timeout is not at least 1",
345 signal(SIGALRM, SIG_DFL);
352 OVS_PRINT_VERSION(0, 0);
356 vlog_set_verbosity(optarg);
368 if ((exit_without_bind + exit_after_bind + !detach_after_bind) > 1) {
369 ovs_fatal(0, "--exit-without-bind, --exit-after-bind, and --no-detach "
370 "are mutually exclusive");
372 if (detach_after_bind) {
380 printf("%s: a tool for discovering OpenFlow controllers.\n"
381 "usage: %s [OPTIONS] NETDEV [NETDEV...]\n"
382 "where each NETDEV is a network device on which to perform\n"
383 "controller discovery.\n"
384 "\nOrdinarily, ovs-discover runs in the foreground until it\n"
385 "obtains an IP address and discovers an OpenFlow controller via\n"
386 "DHCP, then it prints information about the controller to stdout\n"
387 "and detaches to the background to maintain the IP address lease.\n"
388 "\nNetworking options:\n"
389 " --accept-vconn=REGEX accept matching discovered controllers\n"
390 " --exit-without-bind exit after discovery, without binding\n"
391 " --exit-after-bind exit after discovery, after binding\n"
392 " --no-detach do not detach after discovery\n",
393 program_name, program_name);
395 printf("\nOther options:\n"
396 " -t, --timeout=SECS give up discovery after SECS seconds\n"
397 " --pidfile[=FILE] create pidfile (default: %s/%s.pid)\n"
398 " --overwrite-pidfile with --pidfile, start even if already "
400 " -h, --help display this help message\n"
401 " -V, --version display version information\n",
402 ovs_rundir, program_name);