1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
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:
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
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
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.
41 #include "command-line.h"
43 #include "dhcp-client.h"
46 #include "dynamic-string.h"
47 #include "fatal-signal.h"
49 #include "poll-loop.h"
52 #include "vlog-socket.h"
55 #define THIS_MODULE VLM_ofp_discover
59 struct dhclient *dhcp;
62 /* The interfaces that we serve. */
63 static struct iface *ifaces;
66 /* --accept-vconn: Regular expression specifying the class of controller vconns
67 * that we will accept during autodiscovery. */
68 static const char *accept_controller_re = ".*";
69 static regex_t accept_controller_regex;
71 /* --exit-without-bind: Exit after discovering the controller, without binding
72 * the network device to an IP address? */
73 static bool exit_without_bind;
75 /* --exit-after-bind: Exit after discovering the controller, after binding the
76 * network device to an IP address? */
77 static bool exit_after_bind;
79 static bool iface_init(struct iface *, const char *netdev_name);
80 static void release_ifaces(void *aux UNUSED);
82 static void parse_options(int argc, char *argv[]);
83 static void usage(void) NO_RETURN;
85 static void modify_dhcp_request(struct dhcp_msg *, void *aux);
86 static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux);
89 main(int argc, char *argv[])
94 set_program_name(argv[0]);
97 parse_options(argc, argv);
102 ofp_fatal(0, "need at least one non-option argument; "
103 "use --help for usage");
106 ifaces = xmalloc(argc * sizeof *ifaces);
108 for (i = 0; i < argc; i++) {
109 if (iface_init(&ifaces[n_ifaces], argv[i])) {
114 ofp_fatal(0, "failed to initialize any DHCP clients");
117 for (i = 0; i < n_ifaces; i++) {
118 struct iface *iface = &ifaces[i];
119 dhclient_init(iface->dhcp, 0);
121 fatal_signal_add_hook(release_ifaces, NULL, true);
123 retval = regcomp(&accept_controller_regex, accept_controller_re,
124 REG_NOSUB | REG_EXTENDED);
126 size_t length = regerror(retval, &accept_controller_regex, NULL, 0);
127 char *buffer = xmalloc(length);
128 regerror(retval, &accept_controller_regex, buffer, length);
129 ofp_fatal(0, "%s: %s", accept_controller_re, buffer);
132 retval = vlog_server_listen(NULL, NULL);
134 ofp_fatal(retval, "Could not listen for vlog connections");
137 die_if_already_running();
139 signal(SIGPIPE, SIG_IGN);
141 fatal_signal_block();
142 for (i = 0; i < n_ifaces; i++) {
143 struct iface *iface = &ifaces[i];
144 dhclient_run(iface->dhcp);
145 if (dhclient_changed(iface->dhcp)) {
146 bool is_bound = dhclient_is_bound(iface->dhcp);
149 /* Configure network device. */
150 if (!exit_without_bind) {
151 dhclient_configure_netdev(iface->dhcp);
152 dhclient_update_resolv_conf(iface->dhcp);
156 static bool detached = false;
159 /* Disable timeout, since discovery was successful. */
162 /* Print discovered parameters. */
164 dhcp_msg_to_string(dhclient_get_config(iface->dhcp),
166 fputs(ds_cstr(&ds), stdout);
171 /* Exit if the user requested it. */
172 if (exit_without_bind) {
173 VLOG_DBG("exiting because of successful binding on %s "
174 "and --exit-without-bind specified",
178 if (exit_after_bind) {
179 VLOG_DBG("exiting because of successful binding on %s "
180 "and --exit-after-bind specified",
185 /* Detach into background, if we haven't already. */
192 /* We only want an address on a single one of our interfaces.
193 * So: if we have an address on this interface, stop looking
194 * for one on the others; if we don't have an address on this
195 * interface, start looking everywhere. */
196 for (j = 0; j < n_ifaces; j++) {
197 struct iface *if2 = &ifaces[j];
200 dhclient_release(if2->dhcp);
202 dhclient_init(if2->dhcp, 0);
208 for (i = 0; i < n_ifaces; i++) {
209 struct iface *iface = &ifaces[i];
210 dhclient_wait(iface->dhcp);
212 fatal_signal_unblock();
220 iface_init(struct iface *iface, const char *netdev_name)
224 iface->name = netdev_name;
227 if (exit_after_bind) {
228 /* Bring this interface up permanently, so that the bound address
229 * persists past program termination. */
230 struct netdev *netdev;
232 retval = netdev_open(iface->name, NETDEV_ETH_TYPE_NONE, &netdev);
234 ofp_error(retval, "Could not open %s device", iface->name);
237 retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
239 ofp_error(retval, "Could not bring %s device up", iface->name);
242 netdev_close(netdev);
245 retval = dhclient_create(iface->name, modify_dhcp_request,
246 validate_dhcp_offer, NULL, &iface->dhcp);
248 ofp_error(retval, "%s: failed to initialize DHCP client", iface->name);
256 release_ifaces(void *aux UNUSED)
260 for (i = 0; i < n_ifaces; i++) {
261 struct dhclient *dhcp = ifaces[i].dhcp;
262 dhclient_release(dhcp);
263 if (dhclient_changed(dhcp)) {
264 dhclient_configure_netdev(dhcp);
270 modify_dhcp_request(struct dhcp_msg *msg, void *aux)
272 dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
276 validate_dhcp_offer(const struct dhcp_msg *msg, void *aux)
278 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
282 vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
284 VLOG_WARN_RL(&rl, "rejecting DHCP offer missing controller vconn");
287 accept = !regexec(&accept_controller_regex, vconn_name, 0, NULL, 0);
293 parse_options(int argc, char *argv[])
296 OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
297 OPT_EXIT_WITHOUT_BIND,
301 static struct option long_options[] = {
302 {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
303 {"exit-without-bind", no_argument, 0, OPT_EXIT_WITHOUT_BIND},
304 {"exit-after-bind", no_argument, 0, OPT_EXIT_AFTER_BIND},
305 {"no-detach", no_argument, 0, OPT_NO_DETACH},
306 {"timeout", required_argument, 0, 't'},
307 {"pidfile", optional_argument, 0, 'P'},
308 {"force", no_argument, 0, 'f'},
309 {"verbose", optional_argument, 0, 'v'},
310 {"help", no_argument, 0, 'h'},
311 {"version", no_argument, 0, 'V'},
314 char *short_options = long_options_to_short_options(long_options);
315 bool detach_after_bind = true;
318 unsigned long int timeout;
321 c = getopt_long(argc, argv, short_options, long_options, NULL);
327 case OPT_ACCEPT_VCONN:
328 accept_controller_re = (optarg[0] == '^'
330 : xasprintf("^%s", optarg));
333 case OPT_EXIT_WITHOUT_BIND:
334 exit_without_bind = true;
337 case OPT_EXIT_AFTER_BIND:
338 exit_after_bind = true;
342 detach_after_bind = false;
350 ignore_existing_pidfile();
354 timeout = strtoul(optarg, NULL, 10);
356 ofp_fatal(0, "value %s on -t or --timeout is not at least 1",
361 signal(SIGALRM, SIG_DFL);
368 printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
372 vlog_set_verbosity(optarg);
384 if ((exit_without_bind + exit_after_bind + !detach_after_bind) > 1) {
385 ofp_fatal(0, "--exit-without-bind, --exit-after-bind, and --no-detach "
386 "are mutually exclusive");
388 if (detach_after_bind) {
396 printf("%s: a tool for discovering OpenFlow controllers.\n"
397 "usage: %s [OPTIONS] NETDEV [NETDEV...]\n"
398 "where each NETDEV is a network device on which to perform\n"
399 "controller discovery.\n"
400 "\nOrdinarily, ofp-discover runs in the foreground until it\n"
401 "obtains an IP address and discovers an OpenFlow controller via\n"
402 "DHCP, then it prints information about the controller to stdout\n"
403 "and detaches to the background to maintain the IP address lease.\n"
404 "\nNetworking options:\n"
405 " --accept-vconn=REGEX accept matching discovered controllers\n"
406 " --exit-without-bind exit after discovery, without binding\n"
407 " --exit-after-bind exit after discovery, after binding\n"
408 " --no-detach do not detach after discovery\n",
409 program_name, program_name);
411 printf("\nOther options:\n"
412 " -t, --timeout=SECS give up discovery after SECS seconds\n"
413 " -P, --pidfile[=FILE] create pidfile (default: %s/%s.pid)\n"
414 " -f, --force with -P, start even if already running\n"
415 " -h, --help display this help message\n"
416 " -V, --version display version information\n",
417 ofp_rundir, program_name);