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