Drop controller-bound traffic that arrives on the controller's port.
[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 const uint8_t *
455 get_controller_mac(struct netdev *netdev, struct rconn *controller)
456 {
457     static uint32_t ip, last_nonzero_ip;
458     static uint8_t mac[ETH_ADDR_LEN], last_nonzero_mac[ETH_ADDR_LEN];
459     static time_t next_refresh = 0;
460
461     uint32_t last_ip = ip;
462
463     time_t now = time(0);
464
465     ip = rconn_get_ip(controller);
466     if (last_ip != ip || !next_refresh || now >= next_refresh) {
467         bool have_mac;
468
469         /* Look up MAC address. */
470         memset(mac, 0, sizeof mac);
471         if (ip) {
472             int retval = netdev_arp_lookup(netdev, ip, mac);
473             if (retval) {
474                 VLOG_DBG("cannot look up controller hw address ("IP_FMT"): %s",
475                          IP_ARGS(&ip), strerror(retval));
476             }
477         }
478         have_mac = !eth_addr_is_zero(mac);
479
480         /* Log changes in IP, MAC addresses. */
481         if (ip && ip != last_nonzero_ip) {
482             VLOG_DBG("controller IP address changed from "IP_FMT
483                      " to "IP_FMT, IP_ARGS(&last_nonzero_ip), IP_ARGS(&ip));
484             last_nonzero_ip = ip;
485         }
486         if (have_mac && memcmp(last_nonzero_mac, mac, ETH_ADDR_LEN)) {
487             VLOG_DBG("controller MAC address changed from "ETH_ADDR_FMT" to "
488                      ETH_ADDR_FMT,
489                      ETH_ADDR_ARGS(last_nonzero_mac), ETH_ADDR_ARGS(mac));
490             memcpy(last_nonzero_mac, mac, ETH_ADDR_LEN);
491         }
492
493         /* Schedule next refresh.
494          *
495          * If we have an IP address but not a MAC address, then refresh
496          * quickly, since we probably will get a MAC address soon (via ARP).
497          * Otherwise, we can afford to wait a little while. */
498         next_refresh = now + (!ip || have_mac ? 10 : 1);
499     }
500     return !eth_addr_is_zero(mac) ? mac : NULL;
501 }
502
503 static bool
504 is_controller_mac(const uint8_t mac[ETH_ADDR_LEN],
505                   const uint8_t *controller_mac)
506 {
507     return controller_mac && eth_addr_equals(mac, controller_mac);
508 }
509
510 static bool
511 in_band_packet_cb(struct relay *r, int half, void *in_band_)
512 {
513     struct in_band_data *in_band = in_band_;
514     struct rconn *rc = r->halves[HALF_LOCAL].rconn;
515     struct buffer *msg = r->halves[HALF_LOCAL].rxbuf;
516     struct ofp_packet_in *opi;
517     struct ofp_header *oh;
518     size_t pkt_ofs, pkt_len;
519     struct buffer pkt;
520     struct flow flow;
521     uint16_t in_port, out_port;
522     const uint8_t *controller_mac;
523
524     if (half != HALF_LOCAL || r->is_mgmt_conn) {
525         return false;
526     }
527
528     oh = msg->data;
529     if (oh->type != OFPT_PACKET_IN) {
530         return false;
531     }
532     if (msg->size < offsetof (struct ofp_packet_in, data)) {
533         VLOG_WARN("packet too short (%zu bytes) for packet_in", msg->size);
534         return false;
535     }
536
537     /* Extract flow data from 'opi' into 'flow'. */
538     opi = msg->data;
539     in_port = ntohs(opi->in_port);
540     pkt_ofs = offsetof(struct ofp_packet_in, data);
541     pkt_len = ntohs(opi->header.length) - pkt_ofs;
542     pkt.data = opi->data;
543     pkt.size = pkt_len;
544     flow_extract(&pkt, in_port, &flow);
545
546     /* Deal with local stuff. */
547     controller_mac = get_controller_mac(in_band->of_device,
548                                         r->halves[HALF_REMOTE].rconn);
549     if (in_port == OFPP_LOCAL) {
550         /* Sent by secure channel. */
551         out_port = mac_learning_lookup(in_band->ml, flow.dl_dst);
552     } else if (eth_addr_equals(flow.dl_dst, in_band->mac)) {
553         /* Sent to secure channel. */
554         out_port = OFPP_LOCAL;
555         if (mac_learning_learn(in_band->ml, flow.dl_src, in_port)) {
556             VLOG_DBG("learned that "ETH_ADDR_FMT" is on port %"PRIu16,
557                      ETH_ADDR_ARGS(flow.dl_src), in_port);
558         }
559     } else if (flow.dl_type == htons(ETH_TYPE_ARP)
560                && eth_addr_is_broadcast(flow.dl_dst)
561                && is_controller_mac(flow.dl_src, controller_mac)) {
562         /* ARP sent by controller. */
563         out_port = OFPP_FLOOD;
564     } else if (is_controller_mac(flow.dl_dst, controller_mac)
565                && in_port == mac_learning_lookup(in_band->ml,
566                                                  controller_mac)) {
567         /* Drop controller traffic that arrives on the controller port. */
568         queue_tx(rc, make_add_flow(&flow, ntohl(opi->buffer_id),
569                                    in_band->s->max_idle, 0));
570         return true;
571     } else {
572         return false;
573     }
574
575     if (out_port != OFPP_FLOOD) {
576         /* The output port is known, so add a new flow. */
577         queue_tx(rc, make_add_simple_flow(&flow, ntohl(opi->buffer_id),
578                                           out_port, in_band->s->max_idle));
579
580         /* If the switch didn't buffer the packet, we need to send a copy. */
581         if (ntohl(opi->buffer_id) == UINT32_MAX) {
582             queue_tx(rc, make_unbuffered_packet_out(&pkt, in_port, out_port));
583         }
584     } else {
585         /* We don't know that MAC.  Send along the packet without setting up a
586          * flow. */
587         struct buffer *b;
588         if (ntohl(opi->buffer_id) == UINT32_MAX) {
589             b = make_unbuffered_packet_out(&pkt, in_port, out_port);
590         } else {
591             b = make_buffered_packet_out(ntohl(opi->buffer_id),
592                                          in_port, out_port);
593         }
594         queue_tx(rc, b);
595     }
596     return true;
597 }
598
599 static struct hook
600 in_band_hook_create(const struct settings *s)
601 {
602     struct in_band_data *in_band;
603     int retval;
604
605     in_band = xmalloc(sizeof *in_band);
606     in_band->s = s;
607     in_band->ml = mac_learning_create();
608     retval = netdev_open(s->of_name, NETDEV_ETH_TYPE_NONE,
609                          &in_band->of_device);
610     if (retval) {
611         fatal(retval, "Could not open %s device", s->of_name);
612     }
613     memcpy(in_band->mac, netdev_get_etheraddr(in_band->of_device),
614            ETH_ADDR_LEN);
615
616     return make_hook(in_band_packet_cb, NULL, in_band);
617 }
618 \f
619 /* Fail open support. */
620
621 struct fail_open_data {
622     const struct settings *s;
623     struct rconn *local_rconn;
624     struct rconn *remote_rconn;
625     struct lswitch *lswitch;
626     int last_disconn_secs;
627 };
628
629 /* Causes 'r' to enter or leave fail-open mode, if appropriate. */
630 static void
631 fail_open_periodic_cb(void *fail_open_)
632 {
633     struct fail_open_data *fail_open = fail_open_;
634     int disconn_secs;
635     bool open;
636
637     disconn_secs = rconn_disconnected_duration(fail_open->remote_rconn);
638     open = disconn_secs >= fail_open->s->probe_interval * 3;
639     if (open != (fail_open->lswitch != NULL)) {
640         if (!open) {
641             VLOG_WARN("No longer in fail-open mode");
642             lswitch_destroy(fail_open->lswitch);
643             fail_open->lswitch = NULL;
644         } else {
645             VLOG_WARN("Could not connect to controller for %d seconds, "
646                       "failing open", disconn_secs);
647             fail_open->lswitch = lswitch_create(fail_open->local_rconn, true,
648                                                 fail_open->s->max_idle);
649             fail_open->last_disconn_secs = disconn_secs;
650         }
651     } else if (open && disconn_secs > fail_open->last_disconn_secs + 60) {
652         VLOG_WARN("Still in fail-open mode after %d seconds disconnected "
653                   "from controller", disconn_secs);
654         fail_open->last_disconn_secs = disconn_secs;
655     }
656 }
657
658 static bool
659 fail_open_packet_cb(struct relay *r, int half, void *fail_open_)
660 {
661     struct fail_open_data *fail_open = fail_open_;
662     if (half != HALF_LOCAL || r->is_mgmt_conn || !fail_open->lswitch) {
663         return false;
664     } else {
665         lswitch_process_packet(fail_open->lswitch, fail_open->local_rconn,
666                                r->halves[HALF_LOCAL].rxbuf);
667         rconn_run(fail_open->local_rconn);
668         return true;
669     }
670 }
671
672 static struct hook
673 fail_open_hook_create(const struct settings *s, struct rconn *local_rconn,
674                       struct rconn *remote_rconn)
675 {
676     struct fail_open_data *fail_open = xmalloc(sizeof *fail_open);
677     fail_open->s = s;
678     fail_open->local_rconn = local_rconn;
679     fail_open->remote_rconn = remote_rconn;
680     fail_open->lswitch = NULL;
681     return make_hook(fail_open_packet_cb, fail_open_periodic_cb, fail_open);
682 }
683 \f
684 /* Controller discovery. */
685
686 struct discovery
687 {
688     const struct settings *s;
689     struct dhclient *dhcp;
690     bool ever_successful;
691 };
692
693 static struct discovery *
694 discovery_init(const struct settings *s)
695 {
696     struct netdev *netdev;
697     struct discovery *d;
698     struct dhclient *dhcp;
699     int retval;
700
701     /* Bring ofX network device up. */
702     retval = netdev_open(s->of_name, NETDEV_ETH_TYPE_NONE, &netdev);
703     if (retval) {
704         fatal(retval, "Could not open %s device", s->of_name);
705     }
706     retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
707     if (retval) {
708         fatal(retval, "Could not bring %s device up", s->of_name);
709     }
710     netdev_close(netdev);
711
712     /* Initialize DHCP client. */
713     retval = dhclient_create(s->of_name, modify_dhcp_request,
714                              validate_dhcp_offer, (void *) s, &dhcp);
715     if (retval) {
716         fatal(retval, "Failed to initialize DHCP client");
717     }
718     dhclient_init(dhcp, 0);
719
720     d = xmalloc(sizeof *d);
721     d->s = s;
722     d->dhcp = dhcp;
723     d->ever_successful = false;
724     return d;
725 }
726
727 static void
728 discovery_question_connectivity(struct discovery *d)
729 {
730     dhclient_force_renew(d->dhcp, 15);
731 }
732
733 static bool
734 discovery_run(struct discovery *d, char **controller_name)
735 {
736     dhclient_run(d->dhcp);
737     if (!dhclient_changed(d->dhcp)) {
738         return false;
739     }
740
741     dhclient_configure_netdev(d->dhcp);
742     if (d->s->update_resolv_conf) {
743         dhclient_update_resolv_conf(d->dhcp);
744     }
745
746     if (dhclient_is_bound(d->dhcp)) {
747         *controller_name = dhcp_msg_get_string(dhclient_get_config(d->dhcp),
748                                                DHCP_CODE_OFP_CONTROLLER_VCONN);
749         VLOG_WARN("%s: discovered controller", *controller_name);
750         d->ever_successful = true;
751     } else if (controller_name) {
752         *controller_name = NULL;
753         if (d->ever_successful) {
754             VLOG_WARN("discovered controller no longer available");
755         }
756     }
757     return true;
758 }
759
760 static void
761 discovery_wait(struct discovery *d)
762 {
763     dhclient_wait(d->dhcp);
764 }
765
766 static void
767 modify_dhcp_request(struct dhcp_msg *msg, void *aux)
768 {
769     dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
770 }
771
772 static bool
773 validate_dhcp_offer(const struct dhcp_msg *msg, void *s_)
774 {
775     const struct settings *s = s_;
776     char *vconn_name;
777     bool accept;
778
779     vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
780     if (!vconn_name) {
781         VLOG_WARN("rejecting DHCP offer missing controller vconn");
782         return false;
783     }
784     accept = !regexec(&s->accept_controller_regex, vconn_name, 0, NULL, 0);
785     if (!accept) {
786         VLOG_WARN("rejecting controller vconn that fails to match %s",
787                   s->accept_controller_re);
788     }
789     free(vconn_name);
790     return accept;
791 }
792 \f
793 /* User interface. */
794
795 static void
796 parse_options(int argc, char *argv[], struct settings *s)
797 {
798     enum {
799         OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
800         OPT_NO_RESOLV_CONF,
801         OPT_INACTIVITY_PROBE,
802         OPT_MAX_IDLE,
803         OPT_MAX_BACKOFF
804     };
805     static struct option long_options[] = {
806         {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
807         {"no-resolv-conf", no_argument, 0, OPT_NO_RESOLV_CONF},
808         {"fail",        required_argument, 0, 'f'},
809         {"inactivity-probe", required_argument, 0, OPT_INACTIVITY_PROBE},
810         {"max-idle",    required_argument, 0, OPT_MAX_IDLE},
811         {"max-backoff", required_argument, 0, OPT_MAX_BACKOFF},
812         {"listen",      required_argument, 0, 'l'},
813         {"detach",      no_argument, 0, 'D'},
814         {"pidfile",     optional_argument, 0, 'P'},
815         {"verbose",     optional_argument, 0, 'v'},
816         {"help",        no_argument, 0, 'h'},
817         {"version",     no_argument, 0, 'V'},
818         VCONN_SSL_LONG_OPTIONS
819         {0, 0, 0, 0},
820     };
821     char *short_options = long_options_to_short_options(long_options);
822     char *accept_re = NULL;
823     int retval;
824
825     /* Set defaults that we can figure out before parsing options. */
826     s->listen_vconn_name = NULL;
827     s->fail_mode = FAIL_OPEN;
828     s->max_idle = 15;
829     s->probe_interval = 15;
830     s->max_backoff = 15;
831     s->update_resolv_conf = true;
832     for (;;) {
833         int c;
834
835         c = getopt_long(argc, argv, short_options, long_options, NULL);
836         if (c == -1) {
837             break;
838         }
839
840         switch (c) {
841         case OPT_ACCEPT_VCONN:
842             accept_re = optarg[0] == '^' ? optarg : xasprintf("^%s", optarg);
843             break;
844
845         case OPT_NO_RESOLV_CONF:
846             s->update_resolv_conf = false;
847             break;
848
849         case 'f':
850             if (!strcmp(optarg, "open")) {
851                 s->fail_mode = FAIL_OPEN;
852             } else if (!strcmp(optarg, "closed")) {
853                 s->fail_mode = FAIL_CLOSED;
854             } else {
855                 fatal(0,
856                       "-f or --fail argument must be \"open\" or \"closed\"");
857             }
858             break;
859
860         case OPT_INACTIVITY_PROBE:
861             s->probe_interval = atoi(optarg);
862             if (s->probe_interval < 5) {
863                 fatal(0, "--inactivity-probe argument must be at least 5");
864             }
865             break;
866
867         case OPT_MAX_IDLE:
868             if (!strcmp(optarg, "permanent")) {
869                 s->max_idle = OFP_FLOW_PERMANENT;
870             } else {
871                 s->max_idle = atoi(optarg);
872                 if (s->max_idle < 1 || s->max_idle > 65535) {
873                     fatal(0, "--max-idle argument must be between 1 and "
874                           "65535 or the word 'permanent'");
875                 }
876             }
877             break;
878
879         case OPT_MAX_BACKOFF:
880             s->max_backoff = atoi(optarg);
881             if (s->max_backoff < 1) {
882                 fatal(0, "--max-backoff argument must be at least 1");
883             } else if (s->max_backoff > 3600) {
884                 s->max_backoff = 3600;
885             }
886             break;
887
888         case 'D':
889             set_detach();
890             break;
891
892         case 'P':
893             set_pidfile(optarg);
894             break;
895
896         case 'l':
897             if (s->listen_vconn_name) {
898                 fatal(0, "-l or --listen may be only specified once");
899             }
900             s->listen_vconn_name = optarg;
901             break;
902
903         case 'h':
904             usage();
905
906         case 'V':
907             printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
908             exit(EXIT_SUCCESS);
909
910         case 'v':
911             vlog_set_verbosity(optarg);
912             break;
913
914         VCONN_SSL_OPTION_HANDLERS
915
916         case '?':
917             exit(EXIT_FAILURE);
918
919         default:
920             abort();
921         }
922     }
923     free(short_options);
924
925     argc -= optind;
926     argv += optind;
927     if (argc < 1 || argc > 2) {
928         fatal(0, "need one or two non-option arguments; use --help for usage");
929     }
930
931     /* Local and remote vconns. */
932     s->nl_name = argv[0];
933     if (strncmp(s->nl_name, "nl:", 3)
934         || strlen(s->nl_name) < 4
935         || s->nl_name[strspn(s->nl_name + 3, "0123456789") + 3]) {
936         fatal(0, "%s: argument is not of the form \"nl:DP_IDX\"", s->nl_name);
937     }
938     s->of_name = xasprintf("of%s", s->nl_name + 3);
939     s->controller_name = argc > 1 ? xstrdup(argv[1]) : NULL;
940
941     /* Set accept_controller_regex. */
942     if (!accept_re) {
943         accept_re = vconn_ssl_is_configured() ? "^ssl:.*" : ".*";
944     }
945     retval = regcomp(&s->accept_controller_regex, accept_re,
946                      REG_NOSUB | REG_EXTENDED);
947     if (retval) {
948         size_t length = regerror(retval, &s->accept_controller_regex, NULL, 0);
949         char *buffer = xmalloc(length);
950         regerror(retval, &s->accept_controller_regex, buffer, length);
951         fatal(0, "%s: %s", accept_re, buffer);
952     }
953     s->accept_controller_re = accept_re;
954
955     /* Mode of operation. */
956     s->discovery = s->controller_name == NULL;
957     if (s->discovery) {
958         s->in_band = true;
959     } else {
960         enum netdev_flags flags;
961         struct netdev *netdev;
962
963         retval = netdev_open(s->of_name, NETDEV_ETH_TYPE_NONE, &netdev);
964         if (retval) {
965             fatal(retval, "Could not open %s device", s->of_name);
966         }
967
968         retval = netdev_get_flags(netdev, &flags);
969         if (retval) {
970             fatal(retval, "Could not get flags for %s device", s->of_name);
971         }
972
973         s->in_band = (flags & NETDEV_UP) != 0;
974         if (s->in_band && netdev_get_in6(netdev, NULL)) {
975             VLOG_WARN("Ignoring IPv6 address on %s device: IPv6 not supported",
976                       s->of_name);
977         }
978
979         netdev_close(netdev);
980     }
981 }
982
983 static void
984 usage(void)
985 {
986     printf("%s: secure channel, a relay for OpenFlow messages.\n"
987            "usage: %s [OPTIONS] nl:DP_IDX [CONTROLLER]\n"
988            "where nl:DP_IDX is a datapath that has been added with dpctl.\n"
989            "CONTROLLER is an active OpenFlow connection method; if it is\n"
990            "omitted, then secchan performs controller discovery.\n",
991            program_name, program_name);
992     vconn_usage(true, true);
993     printf("\nController discovery options:\n"
994            "  --accept-vconn=REGEX    accept matching discovered controllers\n"
995            "  --no-resolv-conf        do not update /etc/resolv.conf\n"
996            "\nNetworking options:\n"
997            "  -f, --fail=open|closed  when controller connection fails:\n"
998            "                            closed: drop all packets\n"
999            "                            open (default): act as learning switch\n"
1000            "  --inactivity-probe=SECS time between inactivity probes\n"
1001            "  --max-idle=SECS         max idle for flows set up by secchan\n"
1002            "  --max-backoff=SECS      max time between controller connection\n"
1003            "                          attempts (default: 15 seconds)\n"
1004            "  -l, --listen=METHOD     allow management connections on METHOD\n"
1005            "                          (a passive OpenFlow connection method)\n"
1006            "\nOther options:\n"
1007            "  -D, --detach            run in background as daemon\n"
1008            "  -P, --pidfile[=FILE]    create pidfile (default: %s/secchan.pid)\n"
1009            "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
1010            "  -v, --verbose           set maximum verbosity level\n"
1011            "  -h, --help              display this help message\n"
1012            "  -V, --version           display version information\n",
1013            RUNDIR);
1014     exit(EXIT_SUCCESS);
1015 }