]> git.proxmox.com Git - ovs.git/blame - utilities/ovs-discover.c
ovs-vsctl: Update --help message.
[ovs.git] / utilities / ovs-discover.c
CommitLineData
064af421 1/*
149f577a 2 * Copyright (c) 2008, 2009, 2010 Nicira Networks.
064af421 3 *
a14bc59f
BP
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:
064af421 7 *
a14bc59f
BP
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
064af421
BP
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
40struct iface {
41 const char *name;
42 struct dhclient *dhcp;
43};
44
45/* The interfaces that we serve. */
46static struct iface *ifaces;
47static int n_ifaces;
48
49/* --accept-vconn: Regular expression specifying the class of controller vconns
50 * that we will accept during autodiscovery. */
12fb742b 51static const char *accept_controller_re = "tcp:.*";
064af421
BP
52static 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? */
56static bool exit_without_bind;
57
58/* --exit-after-bind: Exit after discovering the controller, after binding the
59 * network device to an IP address? */
60static bool exit_after_bind;
61
62static bool iface_init(struct iface *, const char *netdev_name);
63static void release_ifaces(void *aux UNUSED);
64
65static void parse_options(int argc, char *argv[]);
66static void usage(void) NO_RETURN;
67
68static void modify_dhcp_request(struct dhcp_msg *, void *aux);
69static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux);
70
71int
72main(int argc, char *argv[])
73{
74 struct unixctl_server *unixctl;
75 int retval;
76 int i;
77
40f0707c 78 proctitle_init(argc, argv);
064af421
BP
79 set_program_name(argv[0]);
80 time_init();
81 vlog_init();
82 parse_options(argc, argv);
83
84 argc -= optind;
85 argv += optind;
86 if (argc < 1) {
87 ovs_fatal(0, "need at least one non-option argument; "
88 "use --help for usage");
89 }
90
91 ifaces = xmalloc(argc * sizeof *ifaces);
92 n_ifaces = 0;
93 for (i = 0; i < argc; i++) {
94 if (iface_init(&ifaces[n_ifaces], argv[i])) {
95 n_ifaces++;
96 }
97 }
98 if (!n_ifaces) {
99 ovs_fatal(0, "failed to initialize any DHCP clients");
100 }
101
102 for (i = 0; i < n_ifaces; i++) {
103 struct iface *iface = &ifaces[i];
104 dhclient_init(iface->dhcp, 0);
105 }
e3830e90 106 fatal_signal_add_hook(release_ifaces, NULL, NULL, true);
064af421
BP
107
108 retval = regcomp(&accept_controller_regex, accept_controller_re,
109 REG_NOSUB | REG_EXTENDED);
110 if (retval) {
111 size_t length = regerror(retval, &accept_controller_regex, NULL, 0);
112 char *buffer = xmalloc(length);
113 regerror(retval, &accept_controller_regex, buffer, length);
114 ovs_fatal(0, "%s: %s", accept_controller_re, buffer);
115 }
116
117 retval = unixctl_server_create(NULL, &unixctl);
118 if (retval) {
4d12270a 119 exit(EXIT_FAILURE);
064af421
BP
120 }
121
122 die_if_already_running();
123
124 signal(SIGPIPE, SIG_IGN);
125 for (;;) {
064af421
BP
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);
064af421
BP
198 poll_block();
199 }
200
201 return 0;
202}
203
204static bool
205iface_init(struct iface *iface, const char *netdev_name)
206{
207 int retval;
208
209 iface->name = netdev_name;
210 iface->dhcp = NULL;
211
212 if (exit_after_bind) {
213 /* Bring this interface up permanently, so that the bound address
214 * persists past program termination. */
215 struct netdev *netdev;
216
149f577a 217 retval = netdev_open_default(iface->name, &netdev);
064af421
BP
218 if (retval) {
219 ovs_error(retval, "Could not open %s device", iface->name);
220 return false;
221 }
222 retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
223 if (retval) {
224 ovs_error(retval, "Could not bring %s device up", iface->name);
225 return false;
226 }
227 netdev_close(netdev);
228 }
229
230 retval = dhclient_create(iface->name, modify_dhcp_request,
231 validate_dhcp_offer, NULL, &iface->dhcp);
232 if (retval) {
233 ovs_error(retval, "%s: failed to initialize DHCP client", iface->name);
234 return false;
235 }
236
237 return true;
238}
239
240static void
241release_ifaces(void *aux UNUSED)
242{
243 int i;
244
245 for (i = 0; i < n_ifaces; i++) {
246 struct dhclient *dhcp = ifaces[i].dhcp;
247 dhclient_release(dhcp);
248 if (dhclient_changed(dhcp)) {
249 dhclient_configure_netdev(dhcp);
250 }
251 }
252}
253
254static void
255modify_dhcp_request(struct dhcp_msg *msg, void *aux UNUSED)
256{
257 dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
258}
259
260static bool
261validate_dhcp_offer(const struct dhcp_msg *msg, void *aux UNUSED)
262{
263 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
264 char *vconn_name;
265 bool accept;
266
267 vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
268 if (!vconn_name) {
269 VLOG_WARN_RL(&rl, "rejecting DHCP offer missing controller vconn");
270 return false;
271 }
272 accept = !regexec(&accept_controller_regex, vconn_name, 0, NULL, 0);
273 free(vconn_name);
274 return accept;
275}
276
277static void
278parse_options(int argc, char *argv[])
279{
280 enum {
281 OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
282 OPT_EXIT_WITHOUT_BIND,
283 OPT_EXIT_AFTER_BIND,
e7bd7d78 284 OPT_NO_DETACH
064af421
BP
285 };
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'},
e7bd7d78
JP
292 {"pidfile", optional_argument, 0, OPT_PIDFILE},
293 {"overwrite-pidfile", no_argument, 0, OPT_OVERWRITE_PIDFILE},
064af421
BP
294 {"verbose", optional_argument, 0, 'v'},
295 {"help", no_argument, 0, 'h'},
296 {"version", no_argument, 0, 'V'},
297 {0, 0, 0, 0},
298 };
299 char *short_options = long_options_to_short_options(long_options);
300 bool detach_after_bind = true;
301
302 for (;;) {
303 unsigned long int timeout;
304 int c;
305
306 c = getopt_long(argc, argv, short_options, long_options, NULL);
307 if (c == -1) {
308 break;
309 }
310
311 switch (c) {
312 case OPT_ACCEPT_VCONN:
313 accept_controller_re = (optarg[0] == '^'
314 ? optarg
315 : xasprintf("^%s", optarg));
316 break;
317
318 case OPT_EXIT_WITHOUT_BIND:
319 exit_without_bind = true;
320 break;
321
322 case OPT_EXIT_AFTER_BIND:
323 exit_after_bind = true;
324 break;
325
326 case OPT_NO_DETACH:
327 detach_after_bind = false;
328 break;
329
e7bd7d78 330 case OPT_PIDFILE:
064af421
BP
331 set_pidfile(optarg);
332 break;
333
e7bd7d78 334 case OPT_OVERWRITE_PIDFILE:
064af421
BP
335 ignore_existing_pidfile();
336 break;
337
338 case 't':
339 timeout = strtoul(optarg, NULL, 10);
340 if (timeout <= 0) {
341 ovs_fatal(0, "value %s on -t or --timeout is not at least 1",
342 optarg);
343 } else {
344 time_alarm(timeout);
345 }
346 signal(SIGALRM, SIG_DFL);
347 break;
348
349 case 'h':
350 usage();
351
352 case 'V':
353 OVS_PRINT_VERSION(0, 0);
354 exit(EXIT_SUCCESS);
355
356 case 'v':
357 vlog_set_verbosity(optarg);
358 break;
359
360 case '?':
361 exit(EXIT_FAILURE);
362
363 default:
364 abort();
365 }
366 }
367 free(short_options);
368
369 if ((exit_without_bind + exit_after_bind + !detach_after_bind) > 1) {
370 ovs_fatal(0, "--exit-without-bind, --exit-after-bind, and --no-detach "
371 "are mutually exclusive");
372 }
373 if (detach_after_bind) {
374 set_detach();
375 }
376}
377
378static void
379usage(void)
380{
381 printf("%s: a tool for discovering OpenFlow controllers.\n"
382 "usage: %s [OPTIONS] NETDEV [NETDEV...]\n"
383 "where each NETDEV is a network device on which to perform\n"
384 "controller discovery.\n"
385 "\nOrdinarily, ovs-discover runs in the foreground until it\n"
386 "obtains an IP address and discovers an OpenFlow controller via\n"
387 "DHCP, then it prints information about the controller to stdout\n"
388 "and detaches to the background to maintain the IP address lease.\n"
389 "\nNetworking options:\n"
390 " --accept-vconn=REGEX accept matching discovered controllers\n"
391 " --exit-without-bind exit after discovery, without binding\n"
392 " --exit-after-bind exit after discovery, after binding\n"
393 " --no-detach do not detach after discovery\n",
394 program_name, program_name);
395 vlog_usage();
396 printf("\nOther options:\n"
397 " -t, --timeout=SECS give up discovery after SECS seconds\n"
e7bd7d78
JP
398 " --pidfile[=FILE] create pidfile (default: %s/%s.pid)\n"
399 " --overwrite-pidfile with --pidfile, start even if already "
400 "running\n"
064af421
BP
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}