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"
45 #include "dynamic-string.h"
46 #include "fatal-signal.h"
48 #include "poll-loop.h"
50 #include "vlog-socket.h"
53 #define THIS_MODULE VLM_ofp_discover
57 struct dhclient *dhcp;
60 /* The interfaces that we serve. */
61 static struct iface *ifaces;
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;
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;
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;
77 static bool iface_init(struct iface *, const char *netdev_name);
78 static void release_ifaces(void *aux UNUSED);
80 static void parse_options(int argc, char *argv[]);
81 static void usage(void) NO_RETURN;
83 static void modify_dhcp_request(struct dhcp_msg *, void *aux);
84 static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux);
87 main(int argc, char *argv[])
92 set_program_name(argv[0]);
94 parse_options(argc, argv);
99 fatal(0, "need at least one non-option argument; "
100 "use --help for usage");
103 ifaces = xmalloc(argc * sizeof *ifaces);
105 for (i = 0; i < argc; i++) {
106 if (iface_init(&ifaces[n_ifaces], argv[i])) {
111 fatal(0, "failed to initialize any DHCP clients");
114 for (i = 0; i < n_ifaces; i++) {
115 struct iface *iface = &ifaces[i];
116 dhclient_init(iface->dhcp, 0);
118 fatal_signal_add_hook(release_ifaces, NULL);
120 retval = regcomp(&accept_controller_regex, accept_controller_re,
121 REG_NOSUB | REG_EXTENDED);
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);
129 retval = vlog_server_listen(NULL, NULL);
131 fatal(retval, "Could not listen for vlog connections");
134 signal(SIGPIPE, SIG_IGN);
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);
144 /* Configure network device. */
145 if (!exit_without_bind) {
146 dhclient_configure_netdev(iface->dhcp);
147 dhclient_update_resolv_conf(iface->dhcp);
151 static bool detached = false;
154 /* Disable timeout, since discovery was successful. */
157 /* Print discovered parameters. */
159 dhcp_msg_to_string(dhclient_get_config(iface->dhcp),
161 fputs(ds_cstr(&ds), stdout);
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",
173 if (exit_after_bind) {
174 VLOG_DBG("exiting because of successful binding on %s "
175 "and --exit-after-bind specified",
180 /* Detach into background, if we haven't already. */
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];
195 dhclient_release(if2->dhcp);
197 dhclient_init(if2->dhcp, 0);
203 for (i = 0; i < n_ifaces; i++) {
204 struct iface *iface = &ifaces[i];
205 dhclient_wait(iface->dhcp);
207 fatal_signal_unblock();
215 iface_init(struct iface *iface, const char *netdev_name)
219 iface->name = netdev_name;
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;
227 retval = netdev_open(iface->name, NETDEV_ETH_TYPE_NONE, &netdev);
229 error(retval, "Could not open %s device", iface->name);
232 retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
234 error(retval, "Could not bring %s device up", iface->name);
237 netdev_close(netdev);
240 retval = dhclient_create(iface->name, modify_dhcp_request,
241 validate_dhcp_offer, NULL, &iface->dhcp);
243 error(retval, "%s: failed to initialize DHCP client", iface->name);
251 release_ifaces(void *aux UNUSED)
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);
265 modify_dhcp_request(struct dhcp_msg *msg, void *aux)
267 dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
271 validate_dhcp_offer(const struct dhcp_msg *msg, void *aux)
276 vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
278 VLOG_WARN("rejecting DHCP offer missing controller vconn");
281 accept = !regexec(&accept_controller_regex, vconn_name, 0, NULL, 0);
287 parse_options(int argc, char *argv[])
290 OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
291 OPT_EXIT_WITHOUT_BIND,
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'},
307 char *short_options = long_options_to_short_options(long_options);
308 bool detach_after_bind = true;
311 unsigned long int timeout;
314 c = getopt_long(argc, argv, short_options, long_options, NULL);
320 case OPT_ACCEPT_VCONN:
321 accept_controller_re = (optarg[0] == '^'
323 : xasprintf("^%s", optarg));
326 case OPT_EXIT_WITHOUT_BIND:
327 exit_without_bind = true;
330 case OPT_EXIT_AFTER_BIND:
331 exit_after_bind = true;
335 detach_after_bind = false;
343 timeout = strtoul(optarg, NULL, 10);
345 fatal(0, "value %s on -t or --timeout is not at least 1",
347 } else if (timeout < UINT_MAX) {
348 /* Add 1 because historical implementations allow an alarm to
349 * occur up to a second early. */
354 signal(SIGALRM, SIG_DFL);
361 printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
365 vlog_set_verbosity(optarg);
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");
381 if (detach_after_bind) {
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"
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);