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