]> git.proxmox.com Git - mirror_ovs.git/blob - utilities/ovs-discover.c
Import from old repository commit 61ef2b42a9c4ba8e1600f15bb0236765edc2ad45.
[mirror_ovs.git] / utilities / ovs-discover.c
1 /*
2 * Copyright (c) 2008, 2009 Nicira Networks.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
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
37 #include "vlog.h"
38 #define THIS_MODULE VLM_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 = ".*";
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 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 set_program_name(argv[0]);
79 time_init();
80 vlog_init();
81 parse_options(argc, argv);
82
83 argc -= optind;
84 argv += optind;
85 if (argc < 1) {
86 ovs_fatal(0, "need at least one non-option argument; "
87 "use --help for usage");
88 }
89
90 ifaces = xmalloc(argc * sizeof *ifaces);
91 n_ifaces = 0;
92 for (i = 0; i < argc; i++) {
93 if (iface_init(&ifaces[n_ifaces], argv[i])) {
94 n_ifaces++;
95 }
96 }
97 if (!n_ifaces) {
98 ovs_fatal(0, "failed to initialize any DHCP clients");
99 }
100
101 for (i = 0; i < n_ifaces; i++) {
102 struct iface *iface = &ifaces[i];
103 dhclient_init(iface->dhcp, 0);
104 }
105 fatal_signal_add_hook(release_ifaces, NULL, true);
106
107 retval = regcomp(&accept_controller_regex, accept_controller_re,
108 REG_NOSUB | REG_EXTENDED);
109 if (retval) {
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);
114 }
115
116 retval = unixctl_server_create(NULL, &unixctl);
117 if (retval) {
118 ovs_fatal(retval, "Could not listen for unixctl connections");
119 }
120
121 die_if_already_running();
122
123 signal(SIGPIPE, SIG_IGN);
124 for (;;) {
125 fatal_signal_block();
126 for (i = 0; i < n_ifaces; i++) {
127 struct iface *iface = &ifaces[i];
128 dhclient_run(iface->dhcp);
129 if (dhclient_changed(iface->dhcp)) {
130 bool is_bound = dhclient_is_bound(iface->dhcp);
131 int j;
132
133 /* Configure network device. */
134 if (!exit_without_bind) {
135 dhclient_configure_netdev(iface->dhcp);
136 dhclient_update_resolv_conf(iface->dhcp);
137 }
138
139 if (is_bound) {
140 static bool detached = false;
141 struct ds ds;
142
143 /* Disable timeout, since discovery was successful. */
144 time_alarm(0);
145
146 /* Print discovered parameters. */
147 ds_init(&ds);
148 dhcp_msg_to_string(dhclient_get_config(iface->dhcp),
149 true, &ds);
150 fputs(ds_cstr(&ds), stdout);
151 putchar('\n');
152 fflush(stdout);
153 ds_destroy(&ds);
154
155 /* Exit if the user requested it. */
156 if (exit_without_bind) {
157 VLOG_DBG("exiting because of successful binding on %s "
158 "and --exit-without-bind specified",
159 iface->name);
160 exit(0);
161 }
162 if (exit_after_bind) {
163 VLOG_DBG("exiting because of successful binding on %s "
164 "and --exit-after-bind specified",
165 iface->name);
166 exit(0);
167 }
168
169 /* Detach into background, if we haven't already. */
170 if (!detached) {
171 detached = true;
172 daemonize();
173 }
174 }
175
176 /* We only want an address on a single one of our interfaces.
177 * So: if we have an address on this interface, stop looking
178 * for one on the others; if we don't have an address on this
179 * interface, start looking everywhere. */
180 for (j = 0; j < n_ifaces; j++) {
181 struct iface *if2 = &ifaces[j];
182 if (iface != if2) {
183 if (is_bound) {
184 dhclient_release(if2->dhcp);
185 } else {
186 dhclient_init(if2->dhcp, 0);
187 }
188 }
189 }
190 }
191 }
192 unixctl_server_run(unixctl);
193 for (i = 0; i < n_ifaces; i++) {
194 struct iface *iface = &ifaces[i];
195 dhclient_wait(iface->dhcp);
196 }
197 unixctl_server_wait(unixctl);
198 fatal_signal_unblock();
199 poll_block();
200 }
201
202 return 0;
203 }
204
205 static bool
206 iface_init(struct iface *iface, const char *netdev_name)
207 {
208 int retval;
209
210 iface->name = netdev_name;
211 iface->dhcp = NULL;
212
213 if (exit_after_bind) {
214 /* Bring this interface up permanently, so that the bound address
215 * persists past program termination. */
216 struct netdev *netdev;
217
218 retval = netdev_open(iface->name, NETDEV_ETH_TYPE_NONE, &netdev);
219 if (retval) {
220 ovs_error(retval, "Could not open %s device", iface->name);
221 return false;
222 }
223 retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
224 if (retval) {
225 ovs_error(retval, "Could not bring %s device up", iface->name);
226 return false;
227 }
228 netdev_close(netdev);
229 }
230
231 retval = dhclient_create(iface->name, modify_dhcp_request,
232 validate_dhcp_offer, NULL, &iface->dhcp);
233 if (retval) {
234 ovs_error(retval, "%s: failed to initialize DHCP client", iface->name);
235 return false;
236 }
237
238 return true;
239 }
240
241 static void
242 release_ifaces(void *aux UNUSED)
243 {
244 int i;
245
246 for (i = 0; i < n_ifaces; i++) {
247 struct dhclient *dhcp = ifaces[i].dhcp;
248 dhclient_release(dhcp);
249 if (dhclient_changed(dhcp)) {
250 dhclient_configure_netdev(dhcp);
251 }
252 }
253 }
254
255 static void
256 modify_dhcp_request(struct dhcp_msg *msg, void *aux UNUSED)
257 {
258 dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
259 }
260
261 static bool
262 validate_dhcp_offer(const struct dhcp_msg *msg, void *aux UNUSED)
263 {
264 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
265 char *vconn_name;
266 bool accept;
267
268 vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
269 if (!vconn_name) {
270 VLOG_WARN_RL(&rl, "rejecting DHCP offer missing controller vconn");
271 return false;
272 }
273 accept = !regexec(&accept_controller_regex, vconn_name, 0, NULL, 0);
274 free(vconn_name);
275 return accept;
276 }
277
278 static void
279 parse_options(int argc, char *argv[])
280 {
281 enum {
282 OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
283 OPT_EXIT_WITHOUT_BIND,
284 OPT_EXIT_AFTER_BIND,
285 OPT_NO_DETACH,
286 };
287 static struct option long_options[] = {
288 {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
289 {"exit-without-bind", no_argument, 0, OPT_EXIT_WITHOUT_BIND},
290 {"exit-after-bind", no_argument, 0, OPT_EXIT_AFTER_BIND},
291 {"no-detach", no_argument, 0, OPT_NO_DETACH},
292 {"timeout", required_argument, 0, 't'},
293 {"pidfile", optional_argument, 0, 'P'},
294 {"force", no_argument, 0, 'f'},
295 {"verbose", optional_argument, 0, 'v'},
296 {"help", no_argument, 0, 'h'},
297 {"version", no_argument, 0, 'V'},
298 {0, 0, 0, 0},
299 };
300 char *short_options = long_options_to_short_options(long_options);
301 bool detach_after_bind = true;
302
303 for (;;) {
304 unsigned long int timeout;
305 int c;
306
307 c = getopt_long(argc, argv, short_options, long_options, NULL);
308 if (c == -1) {
309 break;
310 }
311
312 switch (c) {
313 case OPT_ACCEPT_VCONN:
314 accept_controller_re = (optarg[0] == '^'
315 ? optarg
316 : xasprintf("^%s", optarg));
317 break;
318
319 case OPT_EXIT_WITHOUT_BIND:
320 exit_without_bind = true;
321 break;
322
323 case OPT_EXIT_AFTER_BIND:
324 exit_after_bind = true;
325 break;
326
327 case OPT_NO_DETACH:
328 detach_after_bind = false;
329 break;
330
331 case 'P':
332 set_pidfile(optarg);
333 break;
334
335 case 'f':
336 ignore_existing_pidfile();
337 break;
338
339 case 't':
340 timeout = strtoul(optarg, NULL, 10);
341 if (timeout <= 0) {
342 ovs_fatal(0, "value %s on -t or --timeout is not at least 1",
343 optarg);
344 } else {
345 time_alarm(timeout);
346 }
347 signal(SIGALRM, SIG_DFL);
348 break;
349
350 case 'h':
351 usage();
352
353 case 'V':
354 OVS_PRINT_VERSION(0, 0);
355 exit(EXIT_SUCCESS);
356
357 case 'v':
358 vlog_set_verbosity(optarg);
359 break;
360
361 case '?':
362 exit(EXIT_FAILURE);
363
364 default:
365 abort();
366 }
367 }
368 free(short_options);
369
370 if ((exit_without_bind + exit_after_bind + !detach_after_bind) > 1) {
371 ovs_fatal(0, "--exit-without-bind, --exit-after-bind, and --no-detach "
372 "are mutually exclusive");
373 }
374 if (detach_after_bind) {
375 set_detach();
376 }
377 }
378
379 static void
380 usage(void)
381 {
382 printf("%s: a tool for discovering OpenFlow controllers.\n"
383 "usage: %s [OPTIONS] NETDEV [NETDEV...]\n"
384 "where each NETDEV is a network device on which to perform\n"
385 "controller discovery.\n"
386 "\nOrdinarily, ovs-discover runs in the foreground until it\n"
387 "obtains an IP address and discovers an OpenFlow controller via\n"
388 "DHCP, then it prints information about the controller to stdout\n"
389 "and detaches to the background to maintain the IP address lease.\n"
390 "\nNetworking options:\n"
391 " --accept-vconn=REGEX accept matching discovered controllers\n"
392 " --exit-without-bind exit after discovery, without binding\n"
393 " --exit-after-bind exit after discovery, after binding\n"
394 " --no-detach do not detach after discovery\n",
395 program_name, program_name);
396 vlog_usage();
397 printf("\nOther options:\n"
398 " -t, --timeout=SECS give up discovery after SECS seconds\n"
399 " -P, --pidfile[=FILE] create pidfile (default: %s/%s.pid)\n"
400 " -f, --force with -P, start even if already running\n"
401 " -h, --help display this help message\n"
402 " -V, --version display version information\n",
403 ovs_rundir, program_name);
404 exit(EXIT_SUCCESS);
405 }