Clean up secchan code.
[sliver-openvswitch.git] / secchan / secchan.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 <assert.h>
36 #include <errno.h>
37 #include <getopt.h>
38 #include <inttypes.h>
39 #include <netinet/in.h>
40 #include <poll.h>
41 #include <regex.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <time.h>
45 #include <unistd.h>
46
47 #include "buffer.h"
48 #include "command-line.h"
49 #include "compiler.h"
50 #include "daemon.h"
51 #include "dhcp.h"
52 #include "dhcp-client.h"
53 #include "dynamic-string.h"
54 #include "fault.h"
55 #include "flow.h"
56 #include "learning-switch.h"
57 #include "list.h"
58 #include "mac-learning.h"
59 #include "netdev.h"
60 #include "openflow.h"
61 #include "packets.h"
62 #include "poll-loop.h"
63 #include "rconn.h"
64 #include "util.h"
65 #include "vconn-ssl.h"
66 #include "vconn.h"
67 #include "vlog-socket.h"
68
69 #include "vlog.h"
70 #define THIS_MODULE VLM_secchan
71
72 /* Behavior when the connection to the controller fails. */
73 enum fail_mode {
74     FAIL_OPEN,                  /* Act as learning switch. */
75     FAIL_CLOSED                 /* Drop all packets. */
76 };
77
78 /* Settings that may be configured by the user. */
79 struct settings {
80     /* Overall mode of operation. */
81     bool discovery;           /* Discover the controller automatically? */
82     bool in_band;             /* Connect to controller in-band? */
83
84     /* Related vconns and network devices. */
85     const char *nl_name;        /* Local datapath (must be "nl:" vconn). */
86     char *of_name;              /* ofX network device name. */
87     const char *controller_name; /* Controller (if not discovery mode). */
88     const char *listen_vconn_name; /* Listens for mgmt connections. */
89
90     /* Failure behavior. */
91     enum fail_mode fail_mode; /* Act as learning switch if no controller? */
92     int max_idle;             /* Idle time for flows in fail-open mode. */
93     int probe_interval;       /* # seconds idle before sending echo request. */
94     int max_backoff;          /* Max # seconds between connection attempts. */
95
96     /* Discovery behavior. */
97     regex_t accept_controller_regex;  /* Controller vconns to accept. */
98     const char *accept_controller_re; /* String version of regex. */
99     bool update_resolv_conf;          /* Update /etc/resolv.conf? */
100 };
101
102 struct half {
103     struct rconn *rconn;
104     struct buffer *rxbuf;
105 };
106
107 struct relay {
108     struct list node;
109
110 #define HALF_LOCAL 0
111 #define HALF_REMOTE 1
112     struct half halves[2];
113
114     bool is_mgmt_conn;
115 };
116
117 struct hook {
118     bool (*packet_cb)(struct relay *, int half, void *aux);
119     void (*periodic_cb)(void *aux);
120     void *aux;
121 };
122
123 static void parse_options(int argc, char *argv[], struct settings *);
124 static void usage(void) NO_RETURN;
125
126 static struct relay *relay_create(struct rconn *local, struct rconn *remote,
127                                   bool is_mgmt_conn);
128 static struct relay *relay_accept(const struct settings *, struct vconn *);
129 static void relay_run(struct relay *, const struct hook[], size_t n_hooks);
130 static void relay_wait(struct relay *);
131 static void relay_destroy(struct relay *);
132
133 static struct hook make_hook(bool (*packet_cb)(struct relay *, int, void *),
134                              void (*periodic_cb)(void *),
135                              void *aux);
136
137 static struct discovery *discovery_init(const struct settings *);
138 static void discovery_question_connectivity(struct discovery *);
139 static bool discovery_run(struct discovery *, char **controller_name);
140 static void discovery_wait(struct discovery *);
141
142 static struct hook in_band_hook_create(const struct settings *);
143 static struct hook fail_open_hook_create(const struct settings *,
144                                          struct rconn *local,
145                                          struct rconn *remote);
146
147 static void modify_dhcp_request(struct dhcp_msg *, void *aux);
148 static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux);
149
150 int
151 main(int argc, char *argv[])
152 {
153     struct settings s;
154
155     struct list relays = LIST_INITIALIZER(&relays);
156
157     struct hook hooks[3];
158     size_t n_hooks;
159
160     struct rconn *local_rconn, *remote_rconn;
161     struct vconn *listen_vconn;
162     struct relay *controller_relay;
163     struct discovery *discovery;
164     int retval;
165
166     set_program_name(argv[0]);
167     register_fault_handlers();
168     vlog_init();
169     parse_options(argc, argv, &s);
170
171     /* Start listening for management connections. */
172     if (s.listen_vconn_name) {
173         retval = vconn_open(s.listen_vconn_name, &listen_vconn);
174         if (retval && retval != EAGAIN) {
175             fatal(retval, "opening %s", s.listen_vconn_name);
176         }
177         if (!vconn_is_passive(listen_vconn)) {
178             fatal(0, "%s is not a passive vconn", s.listen_vconn_name);
179         }
180     } else {
181         listen_vconn = NULL;
182     }
183
184     /* Start controller discovery. */
185     discovery = s.discovery ? discovery_init(&s) : NULL;
186
187     /* Start listening for vlogconf requests. */
188     retval = vlog_server_listen(NULL, NULL);
189     if (retval) {
190         fatal(retval, "Could not listen for vlog connections");
191     }
192
193     daemonize();
194
195     /* Connect to datapath. */
196     local_rconn = rconn_create(1, 0, s.max_backoff);
197     rconn_connect(local_rconn, s.nl_name);
198
199     /* Connect to controller. */
200     remote_rconn = rconn_create(1, s.probe_interval, s.max_backoff);
201     if (s.controller_name) {
202         retval = rconn_connect(remote_rconn, s.controller_name);
203         if (retval == EAFNOSUPPORT) {
204             fatal(0, "No support for %s vconn", s.controller_name);
205         }
206     }
207
208     /* Start relaying. */
209     controller_relay = relay_create(local_rconn, remote_rconn, false);
210     list_push_back(&relays, &controller_relay->node);
211
212     /* Set up hooks. */
213     n_hooks = 0;
214     if (s.in_band) {
215         hooks[n_hooks++] = in_band_hook_create(&s);
216     }
217     if (s.fail_mode == FAIL_OPEN) {
218         hooks[n_hooks++] = fail_open_hook_create(&s,
219                                                  local_rconn, remote_rconn);
220     }
221     assert(n_hooks <= ARRAY_SIZE(hooks));
222
223     for (;;) {
224         struct relay *r, *n;
225         size_t i;
226
227         /* Do work. */
228         LIST_FOR_EACH_SAFE (r, n, struct relay, node, &relays) {
229             relay_run(r, hooks, n_hooks);
230         }
231         if (listen_vconn) {
232             for (;;) {
233                 struct relay *r = relay_accept(&s, listen_vconn);
234                 if (!r) {
235                     break;
236                 }
237                 list_push_back(&relays, &r->node);
238             }
239         }
240         for (i = 0; i < n_hooks; i++) {
241             if (hooks[i].periodic_cb) {
242                 hooks[i].periodic_cb(hooks[i].aux);
243             }
244         }
245         if (s.discovery) {
246             char *controller_name;
247             if (rconn_is_connectivity_questionable(remote_rconn)) {
248                 discovery_question_connectivity(discovery);
249             }
250             if (discovery_run(discovery, &controller_name)) {
251                 if (controller_name) {
252                     rconn_connect(remote_rconn, controller_name);
253                 } else {
254                     rconn_disconnect(remote_rconn);
255                 }
256             }
257         }
258
259         /* Wait for something to happen. */
260         LIST_FOR_EACH (r, struct relay, node, &relays) {
261             relay_wait(r);
262         }
263         if (listen_vconn) {
264             vconn_accept_wait(listen_vconn);
265         }
266         if (discovery) {
267             discovery_wait(discovery);
268         }
269         poll_block();
270     }
271
272     return 0;
273 }
274
275 static struct hook
276 make_hook(bool (*packet_cb)(struct relay *, int half, void *aux),
277           void (*periodic_cb)(void *aux),
278           void *aux)
279 {
280     struct hook h;
281     h.packet_cb = packet_cb;
282     h.periodic_cb = periodic_cb;
283     h.aux = aux;
284     return h;
285 }
286 \f
287 /* OpenFlow message relaying. */
288
289 static struct relay *
290 relay_accept(const struct settings *s, struct vconn *listen_vconn)
291 {
292     struct vconn *new_remote, *new_local;
293     char *nl_name_without_subscription;
294     struct rconn *r1, *r2;
295     int retval;
296
297     retval = vconn_accept(listen_vconn, &new_remote);
298     if (retval) {
299         if (retval != EAGAIN) {
300             VLOG_WARN("accept failed (%s)", strerror(retval));
301         }
302         return NULL;
303     }
304
305     /* nl:123 or nl:123:1 opens a netlink connection to local datapath 123.  We
306      * only accept the former syntax in main().
307      *
308      * nl:123:0 opens a netlink connection to local datapath 123 without
309      * obtaining a subscription for ofp_packet_in or ofp_flow_expired
310      * messages.*/
311     nl_name_without_subscription = xasprintf("%s:0", s->nl_name);
312     retval = vconn_open(nl_name_without_subscription, &new_local);
313     if (retval) {
314         VLOG_ERR("could not connect to %s (%s)",
315                  nl_name_without_subscription, strerror(retval));
316         vconn_close(new_remote);
317         free(nl_name_without_subscription);
318         return NULL;
319     }
320
321     /* Create and return relay. */
322     r1 = rconn_create(1, 0, 0);
323     rconn_connect_unreliably(r1, nl_name_without_subscription, new_local);
324     free(nl_name_without_subscription);
325
326     r2 = rconn_create(1, 0, 0);
327     rconn_connect_unreliably(r2, "passive", new_remote);
328
329     return relay_create(r1, r2, true);
330 }
331
332 static struct relay *
333 relay_create(struct rconn *local, struct rconn *remote, bool is_mgmt_conn)
334 {
335     struct relay *r;
336     int i;
337
338     r = xmalloc(sizeof *r);
339     r->halves[HALF_LOCAL].rconn = local;
340     r->halves[HALF_REMOTE].rconn = remote;
341     for (i = 0; i < 2; i++) {
342         r->halves[i].rxbuf = NULL;
343     }
344     r->is_mgmt_conn = is_mgmt_conn;
345     return r;
346 }
347
348 static void
349 relay_run(struct relay *r, const struct hook hooks[], size_t n_hooks)
350 {
351     int iteration;
352     int i;
353
354     for (i = 0; i < 2; i++) {
355         rconn_run(r->halves[i].rconn);
356     }
357
358     /* Limit the number of iterations to prevent other tasks from starving. */
359     for (iteration = 0; iteration < 50; iteration++) {
360         bool progress = false;
361         for (i = 0; i < 2; i++) {
362             struct half *this = &r->halves[i];
363             struct half *peer = &r->halves[!i];
364
365             if (!this->rxbuf) {
366                 this->rxbuf = rconn_recv(this->rconn);
367                 if (this->rxbuf) {
368                     const struct hook *h;
369                     for (h = hooks; h < &hooks[n_hooks]; h++) {
370                         if (h->packet_cb(r, i, h->aux)) {
371                             buffer_delete(this->rxbuf);
372                             this->rxbuf = NULL;
373                             progress = true;
374                             break;
375                         }
376                     }
377                 }
378             }
379
380             if (this->rxbuf) {
381                 int retval = rconn_send(peer->rconn, this->rxbuf);
382                 if (retval != EAGAIN) {
383                     if (!retval) {
384                         progress = true;
385                     } else {
386                         buffer_delete(this->rxbuf);
387                     }
388                     this->rxbuf = NULL;
389                 }
390             }
391         }
392         if (!progress) {
393             break;
394         }
395     }
396
397     if (r->is_mgmt_conn) {
398         for (i = 0; i < 2; i++) {
399             struct half *this = &r->halves[i];
400             if (!rconn_is_alive(this->rconn)) {
401                 relay_destroy(r);
402                 return;
403             }
404         }
405     }
406 }
407
408 static void
409 relay_wait(struct relay *r)
410 {
411     int i;
412
413     for (i = 0; i < 2; i++) {
414         struct half *this = &r->halves[i];
415
416         rconn_run_wait(this->rconn);
417         if (!this->rxbuf) {
418             rconn_recv_wait(this->rconn);
419         }
420     }
421 }
422
423 static void
424 relay_destroy(struct relay *r)
425 {
426     int i;
427
428     list_remove(&r->node);
429     for (i = 0; i < 2; i++) {
430         struct half *this = &r->halves[i];
431         rconn_destroy(this->rconn);
432         buffer_delete(this->rxbuf);
433     }
434     free(r);
435 }
436 \f
437 /* In-band control. */
438
439 struct in_band_data {
440     const struct settings *s;
441     struct mac_learning *ml;
442     struct netdev *of_device;
443     uint8_t mac[ETH_ADDR_LEN];
444 };
445
446 static void
447 queue_tx(struct rconn *rc, struct buffer *b)
448 {
449     if (rconn_force_send(rc, b)) {
450         buffer_delete(b);
451     }
452 }
453
454 static bool
455 is_controller_mac(const uint8_t dl_addr[ETH_ADDR_LEN], struct netdev *netdev,
456                   struct rconn *controller)
457 {
458     static uint32_t ip, last_nonzero_ip;
459     static uint8_t mac[ETH_ADDR_LEN], last_nonzero_mac[ETH_ADDR_LEN];
460     static time_t next_refresh = 0;
461
462     uint32_t last_ip = ip;
463
464     time_t now = time(0);
465
466     ip = rconn_get_ip(controller);
467     if (last_ip != ip || !next_refresh || now >= next_refresh) {
468         bool have_mac;
469
470         /* Look up MAC address. */
471         memset(mac, 0, sizeof mac);
472         if (ip) {
473             int retval = netdev_arp_lookup(netdev, ip, mac);
474             if (retval) {
475                 VLOG_DBG("cannot look up controller hw address ("IP_FMT"): %s",
476                          IP_ARGS(&ip), strerror(retval));
477             }
478         }
479         have_mac = !eth_addr_is_zero(mac);
480
481         /* Log changes in IP, MAC addresses. */
482         if (ip && ip != last_nonzero_ip) {
483             VLOG_DBG("controller IP address changed from "IP_FMT
484                      " to "IP_FMT, IP_ARGS(&last_nonzero_ip), IP_ARGS(&ip));
485             last_nonzero_ip = ip;
486         }
487         if (have_mac && memcmp(last_nonzero_mac, mac, ETH_ADDR_LEN)) {
488             VLOG_DBG("controller MAC address changed from "ETH_ADDR_FMT" to "
489                      ETH_ADDR_FMT,
490                      ETH_ADDR_ARGS(last_nonzero_mac), ETH_ADDR_ARGS(mac));
491             memcpy(last_nonzero_mac, mac, ETH_ADDR_LEN);
492         }
493
494         /* Schedule next refresh.
495          *
496          * If we have an IP address but not a MAC address, then refresh
497          * quickly, since we probably will get a MAC address soon (via ARP).
498          * Otherwise, we can afford to wait a little while. */
499         next_refresh = now + (!ip || have_mac ? 10 : 1);
500     }
501     return !eth_addr_is_zero(mac) && eth_addr_equals(mac, dl_addr);
502 }
503
504 static bool
505 in_band_packet_cb(struct relay *r, int half, void *in_band_)
506 {
507     struct in_band_data *in_band = in_band_;
508     struct rconn *rc = r->halves[HALF_LOCAL].rconn;
509     struct buffer *msg = r->halves[HALF_LOCAL].rxbuf;
510     struct ofp_packet_in *opi;
511     struct ofp_header *oh;
512     size_t pkt_ofs, pkt_len;
513     struct buffer pkt;
514     struct flow flow;
515     uint16_t in_port, out_port;
516
517     if (half != HALF_LOCAL || r->is_mgmt_conn) {
518         return false;
519     }
520
521     oh = msg->data;
522     if (oh->type != OFPT_PACKET_IN) {
523         return false;
524     }
525     if (msg->size < offsetof (struct ofp_packet_in, data)) {
526         VLOG_WARN("packet too short (%zu bytes) for packet_in", msg->size);
527         return false;
528     }
529
530     /* Extract flow data from 'opi' into 'flow'. */
531     opi = msg->data;
532     in_port = ntohs(opi->in_port);
533     pkt_ofs = offsetof(struct ofp_packet_in, data);
534     pkt_len = ntohs(opi->header.length) - pkt_ofs;
535     pkt.data = opi->data;
536     pkt.size = pkt_len;
537     flow_extract(&pkt, in_port, &flow);
538
539     /* Deal with local stuff. */
540     if (in_port == OFPP_LOCAL) {
541         out_port = mac_learning_lookup(in_band->ml, flow.dl_dst);
542     } else if (eth_addr_equals(flow.dl_dst, in_band->mac)) {
543         out_port = OFPP_LOCAL;
544         if (mac_learning_learn(in_band->ml, flow.dl_src, in_port)) {
545             VLOG_DBG("learned that "ETH_ADDR_FMT" is on port %"PRIu16,
546                      ETH_ADDR_ARGS(flow.dl_src), in_port);
547         }
548     } else if (flow.dl_type == htons(ETH_TYPE_ARP)
549                && eth_addr_is_broadcast(flow.dl_dst)
550                && is_controller_mac(flow.dl_src, in_band->of_device,
551                                     r->halves[HALF_REMOTE].rconn)) {
552         out_port = OFPP_FLOOD;
553     } else {
554         return false;
555     }
556
557     if (out_port != OFPP_FLOOD) {
558         /* The output port is known, so add a new flow. */
559         queue_tx(rc, make_add_simple_flow(&flow, ntohl(opi->buffer_id),
560                                           out_port, in_band->s->max_idle));
561
562         /* If the switch didn't buffer the packet, we need to send a copy. */
563         if (ntohl(opi->buffer_id) == UINT32_MAX) {
564             queue_tx(rc, make_unbuffered_packet_out(&pkt, in_port, out_port));
565         }
566     } else {
567         /* We don't know that MAC.  Send along the packet without setting up a
568          * flow. */
569         struct buffer *b;
570         if (ntohl(opi->buffer_id) == UINT32_MAX) {
571             b = make_unbuffered_packet_out(&pkt, in_port, out_port);
572         } else {
573             b = make_buffered_packet_out(ntohl(opi->buffer_id),
574                                          in_port, out_port);
575         }
576         queue_tx(rc, b);
577     }
578     return true;
579 }
580
581 static struct hook
582 in_band_hook_create(const struct settings *s)
583 {
584     struct in_band_data *in_band;
585     int retval;
586
587     in_band = xmalloc(sizeof *in_band);
588     in_band->s = s;
589     in_band->ml = mac_learning_create();
590     retval = netdev_open(s->of_name, NETDEV_ETH_TYPE_NONE,
591                          &in_band->of_device);
592     if (retval) {
593         fatal(retval, "Could not open %s device", s->of_name);
594     }
595     memcpy(in_band->mac, netdev_get_etheraddr(in_band->of_device),
596            ETH_ADDR_LEN);
597
598     return make_hook(in_band_packet_cb, NULL, in_band);
599 }
600 \f
601 /* Fail open support. */
602
603 struct fail_open_data {
604     const struct settings *s;
605     struct rconn *local_rconn;
606     struct rconn *remote_rconn;
607     struct lswitch *lswitch;
608     int last_disconn_secs;
609 };
610
611 /* Causes 'r' to enter or leave fail-open mode, if appropriate. */
612 static void
613 fail_open_periodic_cb(void *fail_open_)
614 {
615     struct fail_open_data *fail_open = fail_open_;
616     int disconn_secs;
617     bool open;
618
619     disconn_secs = rconn_disconnected_duration(fail_open->remote_rconn);
620     open = disconn_secs >= fail_open->s->probe_interval * 3;
621     if (open != (fail_open->lswitch != NULL)) {
622         if (!open) {
623             VLOG_WARN("No longer in fail-open mode");
624             lswitch_destroy(fail_open->lswitch);
625             fail_open->lswitch = NULL;
626         } else {
627             VLOG_WARN("Could not connect to controller for %d seconds, "
628                       "failing open", disconn_secs);
629             fail_open->lswitch = lswitch_create(fail_open->local_rconn, true,
630                                                 fail_open->s->max_idle);
631             fail_open->last_disconn_secs = disconn_secs;
632         }
633     } else if (open && disconn_secs > fail_open->last_disconn_secs + 60) {
634         VLOG_WARN("Still in fail-open mode after %d seconds disconnected "
635                   "from controller", disconn_secs);
636         fail_open->last_disconn_secs = disconn_secs;
637     }
638 }
639
640 static bool
641 fail_open_packet_cb(struct relay *r, int half, void *fail_open_)
642 {
643     struct fail_open_data *fail_open = fail_open_;
644     if (half != HALF_LOCAL || r->is_mgmt_conn || !fail_open->lswitch) {
645         return false;
646     } else {
647         lswitch_process_packet(fail_open->lswitch, fail_open->local_rconn,
648                                r->halves[HALF_LOCAL].rxbuf);
649         rconn_run(fail_open->local_rconn);
650         return true;
651     }
652 }
653
654 static struct hook
655 fail_open_hook_create(const struct settings *s, struct rconn *local_rconn,
656                       struct rconn *remote_rconn)
657 {
658     struct fail_open_data *fail_open = xmalloc(sizeof *fail_open);
659     fail_open->s = s;
660     fail_open->local_rconn = local_rconn;
661     fail_open->remote_rconn = remote_rconn;
662     fail_open->lswitch = NULL;
663     return make_hook(fail_open_packet_cb, fail_open_periodic_cb, fail_open);
664 }
665 \f
666 /* Controller discovery. */
667
668 struct discovery
669 {
670     const struct settings *s;
671     struct dhclient *dhcp;
672     bool ever_successful;
673 };
674
675 static struct discovery *
676 discovery_init(const struct settings *s)
677 {
678     struct netdev *netdev;
679     struct discovery *d;
680     struct dhclient *dhcp;
681     int retval;
682
683     /* Bring ofX network device up. */
684     retval = netdev_open(s->of_name, NETDEV_ETH_TYPE_NONE, &netdev);
685     if (retval) {
686         fatal(retval, "Could not open %s device", s->of_name);
687     }
688     retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
689     if (retval) {
690         fatal(retval, "Could not bring %s device up", s->of_name);
691     }
692     netdev_close(netdev);
693
694     /* Initialize DHCP client. */
695     retval = dhclient_create(s->of_name, modify_dhcp_request,
696                              validate_dhcp_offer, (void *) s, &dhcp);
697     if (retval) {
698         fatal(retval, "Failed to initialize DHCP client");
699     }
700     dhclient_init(dhcp, 0);
701
702     d = xmalloc(sizeof *d);
703     d->s = s;
704     d->dhcp = dhcp;
705     d->ever_successful = false;
706     return d;
707 }
708
709 static void
710 discovery_question_connectivity(struct discovery *d)
711 {
712     dhclient_force_renew(d->dhcp, 15);
713 }
714
715 static bool
716 discovery_run(struct discovery *d, char **controller_name)
717 {
718     dhclient_run(d->dhcp);
719     if (!dhclient_changed(d->dhcp)) {
720         return false;
721     }
722
723     dhclient_configure_netdev(d->dhcp);
724     if (d->s->update_resolv_conf) {
725         dhclient_update_resolv_conf(d->dhcp);
726     }
727
728     if (dhclient_is_bound(d->dhcp)) {
729         *controller_name = dhcp_msg_get_string(dhclient_get_config(d->dhcp),
730                                                DHCP_CODE_OFP_CONTROLLER_VCONN);
731         VLOG_WARN("%s: discovered controller", *controller_name);
732         d->ever_successful = true;
733     } else if (controller_name) {
734         *controller_name = NULL;
735         if (d->ever_successful) {
736             VLOG_WARN("discovered controller no longer available");
737         }
738     }
739     return true;
740 }
741
742 static void
743 discovery_wait(struct discovery *d)
744 {
745     dhclient_wait(d->dhcp);
746 }
747
748 static void
749 modify_dhcp_request(struct dhcp_msg *msg, void *aux)
750 {
751     dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
752 }
753
754 static bool
755 validate_dhcp_offer(const struct dhcp_msg *msg, void *s_)
756 {
757     const struct settings *s = s_;
758     char *vconn_name;
759     bool accept;
760
761     vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
762     if (!vconn_name) {
763         VLOG_WARN("rejecting DHCP offer missing controller vconn");
764         return false;
765     }
766     accept = !regexec(&s->accept_controller_regex, vconn_name, 0, NULL, 0);
767     if (!accept) {
768         VLOG_WARN("rejecting controller vconn that fails to match %s",
769                   s->accept_controller_re);
770     }
771     free(vconn_name);
772     return accept;
773 }
774 \f
775 /* User interface. */
776
777 static void
778 parse_options(int argc, char *argv[], struct settings *s)
779 {
780     enum {
781         OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
782         OPT_NO_RESOLV_CONF,
783         OPT_INACTIVITY_PROBE,
784         OPT_MAX_IDLE,
785         OPT_MAX_BACKOFF
786     };
787     static struct option long_options[] = {
788         {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
789         {"no-resolv-conf", no_argument, 0, OPT_NO_RESOLV_CONF},
790         {"fail",        required_argument, 0, 'f'},
791         {"inactivity-probe", required_argument, 0, OPT_INACTIVITY_PROBE},
792         {"max-idle",    required_argument, 0, OPT_MAX_IDLE},
793         {"max-backoff", required_argument, 0, OPT_MAX_BACKOFF},
794         {"listen",      required_argument, 0, 'l'},
795         {"detach",      no_argument, 0, 'D'},
796         {"pidfile",     optional_argument, 0, 'P'},
797         {"verbose",     optional_argument, 0, 'v'},
798         {"help",        no_argument, 0, 'h'},
799         {"version",     no_argument, 0, 'V'},
800         VCONN_SSL_LONG_OPTIONS
801         {0, 0, 0, 0},
802     };
803     char *short_options = long_options_to_short_options(long_options);
804     char *accept_re = NULL;
805     int retval;
806
807     /* Set defaults that we can figure out before parsing options. */
808     s->listen_vconn_name = NULL;
809     s->fail_mode = FAIL_OPEN;
810     s->max_idle = 15;
811     s->probe_interval = 15;
812     s->max_backoff = 15;
813     s->update_resolv_conf = true;
814     for (;;) {
815         int c;
816
817         c = getopt_long(argc, argv, short_options, long_options, NULL);
818         if (c == -1) {
819             break;
820         }
821
822         switch (c) {
823         case OPT_ACCEPT_VCONN:
824             accept_re = optarg[0] == '^' ? optarg : xasprintf("^%s", optarg);
825             break;
826
827         case OPT_NO_RESOLV_CONF:
828             s->update_resolv_conf = false;
829             break;
830
831         case 'f':
832             if (!strcmp(optarg, "open")) {
833                 s->fail_mode = FAIL_OPEN;
834             } else if (!strcmp(optarg, "closed")) {
835                 s->fail_mode = FAIL_CLOSED;
836             } else {
837                 fatal(0,
838                       "-f or --fail argument must be \"open\" or \"closed\"");
839             }
840             break;
841
842         case OPT_INACTIVITY_PROBE:
843             s->probe_interval = atoi(optarg);
844             if (s->probe_interval < 5) {
845                 fatal(0, "--inactivity-probe argument must be at least 5");
846             }
847             break;
848
849         case OPT_MAX_IDLE:
850             if (!strcmp(optarg, "permanent")) {
851                 s->max_idle = OFP_FLOW_PERMANENT;
852             } else {
853                 s->max_idle = atoi(optarg);
854                 if (s->max_idle < 1 || s->max_idle > 65535) {
855                     fatal(0, "--max-idle argument must be between 1 and "
856                           "65535 or the word 'permanent'");
857                 }
858             }
859             break;
860
861         case OPT_MAX_BACKOFF:
862             s->max_backoff = atoi(optarg);
863             if (s->max_backoff < 1) {
864                 fatal(0, "--max-backoff argument must be at least 1");
865             } else if (s->max_backoff > 3600) {
866                 s->max_backoff = 3600;
867             }
868             break;
869
870         case 'D':
871             set_detach();
872             break;
873
874         case 'P':
875             set_pidfile(optarg);
876             break;
877
878         case 'l':
879             if (s->listen_vconn_name) {
880                 fatal(0, "-l or --listen may be only specified once");
881             }
882             s->listen_vconn_name = optarg;
883             break;
884
885         case 'h':
886             usage();
887
888         case 'V':
889             printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
890             exit(EXIT_SUCCESS);
891
892         case 'v':
893             vlog_set_verbosity(optarg);
894             break;
895
896         VCONN_SSL_OPTION_HANDLERS
897
898         case '?':
899             exit(EXIT_FAILURE);
900
901         default:
902             abort();
903         }
904     }
905     free(short_options);
906
907     argc -= optind;
908     argv += optind;
909     if (argc < 1 || argc > 2) {
910         fatal(0, "need one or two non-option arguments; use --help for usage");
911     }
912
913     /* Local and remote vconns. */
914     s->nl_name = argv[0];
915     if (strncmp(s->nl_name, "nl:", 3)
916         || strlen(s->nl_name) < 4
917         || s->nl_name[strspn(s->nl_name + 3, "0123456789") + 3]) {
918         fatal(0, "%s: argument is not of the form \"nl:DP_IDX\"", s->nl_name);
919     }
920     s->of_name = xasprintf("of%s", s->nl_name + 3);
921     s->controller_name = argc > 1 ? xstrdup(argv[1]) : NULL;
922
923     /* Set accept_controller_regex. */
924     if (!accept_re) {
925         accept_re = vconn_ssl_is_configured() ? "^ssl:.*" : ".*";
926     }
927     retval = regcomp(&s->accept_controller_regex, accept_re,
928                      REG_NOSUB | REG_EXTENDED);
929     if (retval) {
930         size_t length = regerror(retval, &s->accept_controller_regex, NULL, 0);
931         char *buffer = xmalloc(length);
932         regerror(retval, &s->accept_controller_regex, buffer, length);
933         fatal(0, "%s: %s", accept_re, buffer);
934     }
935     s->accept_controller_re = accept_re;
936
937     /* Mode of operation. */
938     s->discovery = s->controller_name == NULL;
939     if (s->discovery) {
940         s->in_band = true;
941     } else {
942         enum netdev_flags flags;
943         struct netdev *netdev;
944
945         retval = netdev_open(s->of_name, NETDEV_ETH_TYPE_NONE, &netdev);
946         if (retval) {
947             fatal(retval, "Could not open %s device", s->of_name);
948         }
949
950         retval = netdev_get_flags(netdev, &flags);
951         if (retval) {
952             fatal(retval, "Could not get flags for %s device", s->of_name);
953         }
954
955         s->in_band = (flags & NETDEV_UP) != 0;
956         if (s->in_band && netdev_get_in6(netdev, NULL)) {
957             VLOG_WARN("Ignoring IPv6 address on %s device: IPv6 not supported",
958                       s->of_name);
959         }
960
961         netdev_close(netdev);
962     }
963 }
964
965 static void
966 usage(void)
967 {
968     printf("%s: secure channel, a relay for OpenFlow messages.\n"
969            "usage: %s [OPTIONS] nl:DP_IDX [CONTROLLER]\n"
970            "where nl:DP_IDX is a datapath that has been added with dpctl.\n"
971            "CONTROLLER is an active OpenFlow connection method; if it is\n"
972            "omitted, then secchan performs controller discovery.\n",
973            program_name, program_name);
974     vconn_usage(true, true);
975     printf("\nController discovery options:\n"
976            "  --accept-vconn=REGEX    accept matching discovered controllers\n"
977            "  --no-resolv-conf        do not update /etc/resolv.conf\n"
978            "\nNetworking options:\n"
979            "  -f, --fail=open|closed  when controller connection fails:\n"
980            "                            closed: drop all packets\n"
981            "                            open (default): act as learning switch\n"
982            "  --inactivity-probe=SECS time between inactivity probes\n"
983            "  --max-idle=SECS         max idle for flows set up by secchan\n"
984            "  --max-backoff=SECS      max time between controller connection\n"
985            "                          attempts (default: 15 seconds)\n"
986            "  -l, --listen=METHOD     allow management connections on METHOD\n"
987            "                          (a passive OpenFlow connection method)\n"
988            "\nOther options:\n"
989            "  -D, --detach            run in background as daemon\n"
990            "  -P, --pidfile[=FILE]    create pidfile (default: %s/secchan.pid)\n"
991            "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
992            "  -v, --verbose           set maximum verbosity level\n"
993            "  -h, --help              display this help message\n"
994            "  -V, --version           display version information\n",
995            RUNDIR);
996     exit(EXIT_SUCCESS);
997 }