For SNAT, don't store the pre-fragment L2 header before actions are applied.
[sliver-openvswitch.git] / utilities / ofp-discover.c
1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
2  * Junior University
3  *
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:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
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
27  * SOFTWARE.
28  *
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.
32  */
33
34 #include <config.h>
35 #include <getopt.h>
36 #include <limits.h>
37 #include <regex.h>
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include "command-line.h"
42 #include "daemon.h"
43 #include "dhcp-client.h"
44 #include "dhcp.h"
45 #include "dirs.h"
46 #include "dynamic-string.h"
47 #include "fatal-signal.h"
48 #include "netdev.h"
49 #include "poll-loop.h"
50 #include "timeval.h"
51 #include "util.h"
52 #include "vlog-socket.h"
53
54 #include "vlog.h"
55 #define THIS_MODULE VLM_ofp_discover
56
57 struct iface {
58     const char *name;
59     struct dhclient *dhcp;
60 };
61
62 /* The interfaces that we serve. */
63 static struct iface *ifaces;
64 static int n_ifaces;
65
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;
70
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;
74
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;
78
79 static bool iface_init(struct iface *, const char *netdev_name);
80 static void release_ifaces(void *aux UNUSED);
81
82 static void parse_options(int argc, char *argv[]);
83 static void usage(void) NO_RETURN;
84
85 static void modify_dhcp_request(struct dhcp_msg *, void *aux);
86 static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux);
87
88 int
89 main(int argc, char *argv[])
90 {
91     int retval;
92     int i;
93
94     set_program_name(argv[0]);
95     time_init();
96     vlog_init();
97     parse_options(argc, argv);
98
99     argc -= optind;
100     argv += optind;
101     if (argc < 1) {
102         ofp_fatal(0, "need at least one non-option argument; "
103                   "use --help for usage");
104     }
105
106     ifaces = xmalloc(argc * sizeof *ifaces);
107     n_ifaces = 0;
108     for (i = 0; i < argc; i++) {
109         if (iface_init(&ifaces[n_ifaces], argv[i])) {
110             n_ifaces++;
111         }
112     }
113     if (!n_ifaces) {
114         ofp_fatal(0, "failed to initialize any DHCP clients");
115     }
116
117     for (i = 0; i < n_ifaces; i++) {
118         struct iface *iface = &ifaces[i];
119         dhclient_init(iface->dhcp, 0);
120     }
121     fatal_signal_add_hook(release_ifaces, NULL, true);
122
123     retval = regcomp(&accept_controller_regex, accept_controller_re,
124                      REG_NOSUB | REG_EXTENDED);
125     if (retval) {
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);
130     }
131
132     retval = vlog_server_listen(NULL, NULL);
133     if (retval) {
134         ofp_fatal(retval, "Could not listen for vlog connections");
135     }
136
137     die_if_already_running();
138
139     signal(SIGPIPE, SIG_IGN);
140     for (;;) {
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);
147                 int j;
148
149                 /* Configure network device. */
150                 if (!exit_without_bind) {
151                     dhclient_configure_netdev(iface->dhcp);
152                     dhclient_update_resolv_conf(iface->dhcp);
153                 }
154
155                 if (is_bound) {
156                     static bool detached = false;
157                     struct ds ds;
158
159                     /* Disable timeout, since discovery was successful. */
160                     time_alarm(0);
161
162                     /* Print discovered parameters. */
163                     ds_init(&ds);
164                     dhcp_msg_to_string(dhclient_get_config(iface->dhcp),
165                                        true, &ds);
166                     fputs(ds_cstr(&ds), stdout);
167                     putchar('\n');
168                     fflush(stdout);
169                     ds_destroy(&ds);
170
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",
175                                  iface->name);
176                         exit(0);
177                     }
178                     if (exit_after_bind) {
179                         VLOG_DBG("exiting because of successful binding on %s "
180                                  "and --exit-after-bind specified",
181                                  iface->name);
182                         exit(0);
183                     }
184
185                     /* Detach into background, if we haven't already. */
186                     if (!detached) {
187                         detached = true;
188                         daemonize();
189                     }
190                 }
191
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];
198                     if (iface != if2) {
199                         if (is_bound) {
200                             dhclient_release(if2->dhcp);
201                         } else {
202                             dhclient_init(if2->dhcp, 0);
203                         }
204                     }
205                 }
206             }
207         }
208         for (i = 0; i < n_ifaces; i++) {
209             struct iface *iface = &ifaces[i];
210             dhclient_wait(iface->dhcp);
211         }
212         fatal_signal_unblock();
213         poll_block();
214     }
215
216     return 0;
217 }
218
219 static bool
220 iface_init(struct iface *iface, const char *netdev_name)
221 {
222     int retval;
223
224     iface->name = netdev_name;
225     iface->dhcp = NULL;
226
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;
231
232         retval = netdev_open(iface->name, NETDEV_ETH_TYPE_NONE, &netdev);
233         if (retval) {
234             ofp_error(retval, "Could not open %s device", iface->name);
235             return false;
236         }
237         retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
238         if (retval) {
239             ofp_error(retval, "Could not bring %s device up", iface->name);
240             return false;
241         }
242         netdev_close(netdev);
243     }
244
245     retval = dhclient_create(iface->name, modify_dhcp_request,
246                              validate_dhcp_offer, NULL, &iface->dhcp);
247     if (retval) {
248         ofp_error(retval, "%s: failed to initialize DHCP client", iface->name);
249         return false;
250     }
251
252     return true;
253 }
254
255 static void
256 release_ifaces(void *aux UNUSED)
257 {
258     int i;
259
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);
265         }
266     }
267 }
268
269 static void
270 modify_dhcp_request(struct dhcp_msg *msg, void *aux)
271 {
272     dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
273 }
274
275 static bool
276 validate_dhcp_offer(const struct dhcp_msg *msg, void *aux)
277 {
278     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
279     char *vconn_name;
280     bool accept;
281
282     vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
283     if (!vconn_name) {
284         VLOG_WARN_RL(&rl, "rejecting DHCP offer missing controller vconn");
285         return false;
286     }
287     accept = !regexec(&accept_controller_regex, vconn_name, 0, NULL, 0);
288     free(vconn_name);
289     return accept;
290 }
291
292 static void
293 parse_options(int argc, char *argv[])
294 {
295     enum {
296         OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
297         OPT_EXIT_WITHOUT_BIND,
298         OPT_EXIT_AFTER_BIND,
299         OPT_NO_DETACH,
300     };
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'},
312         {0, 0, 0, 0},
313     };
314     char *short_options = long_options_to_short_options(long_options);
315     bool detach_after_bind = true;
316
317     for (;;) {
318         unsigned long int timeout;
319         int c;
320
321         c = getopt_long(argc, argv, short_options, long_options, NULL);
322         if (c == -1) {
323             break;
324         }
325
326         switch (c) {
327         case OPT_ACCEPT_VCONN:
328             accept_controller_re = (optarg[0] == '^'
329                                     ? optarg
330                                     : xasprintf("^%s", optarg));
331             break;
332
333         case OPT_EXIT_WITHOUT_BIND:
334             exit_without_bind = true;
335             break;
336
337         case OPT_EXIT_AFTER_BIND:
338             exit_after_bind = true;
339             break;
340
341         case OPT_NO_DETACH:
342             detach_after_bind = false;
343             break;
344
345         case 'P':
346             set_pidfile(optarg);
347             break;
348
349         case 'f':
350             ignore_existing_pidfile();
351             break;
352
353         case 't':
354             timeout = strtoul(optarg, NULL, 10);
355             if (timeout <= 0) {
356                 ofp_fatal(0, "value %s on -t or --timeout is not at least 1",
357                           optarg);
358             } else {
359                 time_alarm(timeout);
360             }
361             signal(SIGALRM, SIG_DFL);
362             break;
363
364         case 'h':
365             usage();
366
367         case 'V':
368             printf("%s %s compiled "__DATE__" "__TIME__"\n",
369                    program_name, VERSION BUILDNR);
370             exit(EXIT_SUCCESS);
371
372         case 'v':
373             vlog_set_verbosity(optarg);
374             break;
375
376         case '?':
377             exit(EXIT_FAILURE);
378
379         default:
380             abort();
381         }
382     }
383     free(short_options);
384
385     if ((exit_without_bind + exit_after_bind + !detach_after_bind) > 1) {
386         ofp_fatal(0, "--exit-without-bind, --exit-after-bind, and --no-detach "
387                   "are mutually exclusive");
388     }
389     if (detach_after_bind) {
390         set_detach();
391     }
392 }
393
394 static void
395 usage(void)
396 {
397     printf("%s: a tool for discovering OpenFlow controllers.\n"
398            "usage: %s [OPTIONS] NETDEV [NETDEV...]\n"
399            "where each NETDEV is a network device on which to perform\n"
400            "controller discovery.\n"
401            "\nOrdinarily, ofp-discover runs in the foreground until it\n"
402            "obtains an IP address and discovers an OpenFlow controller via\n"
403            "DHCP, then it prints information about the controller to stdout\n"
404            "and detaches to the background to maintain the IP address lease.\n"
405            "\nNetworking options:\n"
406            "  --accept-vconn=REGEX    accept matching discovered controllers\n"
407            "  --exit-without-bind     exit after discovery, without binding\n"
408            "  --exit-after-bind       exit after discovery, after binding\n"
409            "  --no-detach             do not detach after discovery\n",
410            program_name, program_name);
411     vlog_usage();
412     printf("\nOther options:\n"
413            "  -t, --timeout=SECS      give up discovery after SECS seconds\n"
414            "  -P, --pidfile[=FILE]    create pidfile (default: %s/%s.pid)\n"
415            "  -f, --force             with -P, start even if already running\n"
416            "  -h, --help              display this help message\n"
417            "  -V, --version           display version information\n",
418            ofp_rundir, program_name);
419     exit(EXIT_SUCCESS);
420 }