1 /* Copyright (c) 2008, 2009 Nicira Networks, Inc.
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 * In addition, as a special exception, Nicira Networks gives permission
17 * to link the code of its release of vswitchd with the OpenSSL project's
18 * "OpenSSL" library (or with modified versions of it that use the same
19 * license as the "OpenSSL" library), and distribute the linked
20 * executables. You must obey the GNU General Public License in all
21 * respects for all of the code used other than "OpenSSL". If you modify
22 * this file, you may extend this exception to your version of the file,
23 * but you are not obligated to do so. If you do not wish to do so,
24 * delete this exception statement from your version.
29 #include <arpa/inet.h>
44 #include "command-line.h"
46 #include "dynamic-string.h"
48 #include "fatal-signal.h"
51 #include "openflow/nicira-ext.h"
52 #include "openflow/openflow.h"
54 #include "poll-loop.h"
58 #include "socket-util.h"
65 #define THIS_MODULE VLM_switchui
68 static void parse_options(int argc, char *argv[]);
69 static void usage(void);
71 static void initialize_terminal(void);
72 static void restore_terminal(void *aux);
83 static void emit(struct message **, enum priority, const char *, ...)
85 static void emit_function(struct message **, enum priority,
86 void (*function)(void *aux), void *aux);
87 static int shown(struct message **);
88 static void clear_messages(void);
89 static bool empty_message(const struct message *);
90 static struct message *best_message(void);
91 static struct message *next_message(struct message *);
92 static struct message *prev_message(struct message *);
93 static void put_message(const struct message *);
94 static void message_shown(struct message *);
95 static void age_messages(void);
107 static void dict_init(struct dict *);
108 static void dict_add(struct dict *, const char *name, const char *value);
109 static void dict_add_nocopy(struct dict *, char *name, char *value);
110 static void dict_delete(struct dict *, const char *name);
111 static void dict_parse(struct dict *, const char *data, size_t nbytes);
112 static void dict_free(struct dict *);
113 static bool dict_lookup(const struct dict *,
114 const char *name, const char **value);
115 static int dict_get_int(const struct dict *, const char *name, int def);
116 static bool dict_get_bool(const struct dict *, const char *name, bool def);
117 static const char *dict_get_string(const struct dict *,
118 const char *name, const char *def);
119 static uint32_t dict_get_ip(const struct dict *, const char *name);
121 static void addf(const char *format, ...) PRINTF_FORMAT(1, 2);
123 static void fetch_status(struct rconn *, struct dict *, long long int timeout);
124 static bool parse_reply(void *, struct dict *, uint32_t xid);
125 static void compose_messages(const struct dict *, struct rconn *rconn);
127 static void show_flows(struct rconn *);
128 static void show_dpid_ip(struct rconn *, const struct dict *);
129 static void show_secchan_state(const struct dict *);
130 static void show_fail_open_state(const struct dict *);
131 static void show_discovery_state(const struct dict *);
132 static void show_remote_state(const struct dict *);
133 static void show_data_rates(struct rconn *, const struct dict *);
135 static void init_reboot_notifier(void);
136 static bool show_reboot_state(void);
138 static void show_string(const char *string);
139 static void block_until(long long timeout);
140 static void menu(const struct dict *);
141 static void drain_keyboard_buffer(void);
143 static const char *progress(void);
146 main(int argc, char *argv[])
154 /* Tracking keystroke repeat counts. */
156 long long int last_key_time = 0;
157 int repeat_count = 0;
159 set_program_name(argv[0]);
162 parse_options(argc, argv);
163 signal(SIGPIPE, SIG_IGN);
164 vlog_set_levels(VLM_ANY_MODULE, VLF_CONSOLE, VLL_EMER);
165 init_reboot_notifier();
170 ovs_fatal(0, "exactly one non-option argument required; "
171 "use --help for help");
174 rconn = rconn_new(argv[0], 5, 5);
176 die_if_already_running();
179 initialize_terminal();
180 fatal_signal_add_hook(restore_terminal, NULL, true);
184 user_selected = false;
188 long long timeout = time_msec() + 1000;
193 fetch_status(rconn, &dict, timeout);
194 dict_add(&dict, "debug", debug_mode ? "true" : "false");
195 compose_messages(&dict, rconn);
198 if (!empty_message(msg)) {
201 msg = user_selected ? next_message(msg) : best_message();
205 msg = best_message();
207 user_selected = false;
209 if (!user_selected) {
220 if (c != last_key || time_msec() > last_key_time + 250) {
224 last_key_time = time_msec();
227 if (c == KEY_DOWN || c == KEY_UP) {
228 msg = (c == KEY_DOWN ? next_message(msg)
229 : prev_message(msg));
231 user_selected = true;
232 } else if (c == '\r' || c == '\n') {
234 user_selected = true;
235 if (repeat_count >= 20) {
236 debug_mode = !debug_mode;
237 show_string(debug_mode
238 ? "Debug Mode\nEnabled"
239 : "Debug Mode\nDisabled");
241 } else if (c == '\b' || c == '\x7f' ||
242 c == '\x1b' || c == KEY_BACKSPACE || c == KEY_DC) {
244 drain_keyboard_buffer();
255 poll_fd_wait(STDIN_FILENO, POLLIN);
256 poll_timer_wait(timeout - time_msec());
258 } while (time_msec() < timeout);
267 compose_messages(const struct dict *dict, struct rconn *rconn)
269 if (!show_reboot_state()) {
271 show_dpid_ip(rconn, dict);
272 show_secchan_state(dict);
273 show_fail_open_state(dict);
274 show_discovery_state(dict);
275 show_remote_state(dict);
276 show_data_rates(rconn, dict);
280 struct put_flows_data {
288 parse_flow_reply(void *data, struct put_flows_data *pfd)
290 struct ofp_header *oh;
291 struct ofp_stats_reply *rpy;
292 struct ofp_aggregate_stats_reply *asr;
293 const size_t min_size = sizeof *rpy + sizeof *asr;
296 if (ntohs(oh->length) < min_size) {
297 VLOG_WARN("reply is too short (%"PRIu16")", ntohs(oh->length));
300 if (oh->xid != pfd->xid) {
301 VLOG_WARN("xid 0x%08"PRIx32" != expected 0x%08"PRIx32,
305 if (oh->type != OFPT_STATS_REPLY) {
306 VLOG_WARN("reply is wrong type %"PRIu8, oh->type);
311 if (rpy->type != htons(OFPST_AGGREGATE)) {
312 VLOG_WARN("reply has wrong stat type ID %08"PRIx16, rpy->type);
316 asr = (struct ofp_aggregate_stats_reply *) rpy->body;
317 pfd->flow_count = ntohl(asr->flow_count);
318 pfd->got_reply = true;
324 const char *dico = tigetstr("dico");
325 return dico && dico != (const char *) -1;
329 set_icon(int num, int r0, int r1, int r2, int r3, int r4, int r5, int r6,
333 putp(tparm(tigetstr("dico"), num, r0, r1, r2, r3, r4, r5, r6, r7));
338 set_repeated_icon(int num, int row)
340 set_icon(num, row, row, row, row, row, row, row, row);
345 set_brick_icon(int num, int n_solid)
347 const static int rows[6] = {_____, X____, XX___, XXX__, XXXX_, XXXXX};
348 set_repeated_icon(num, rows[n_solid < 0 ? 0
355 icon_char(int num, int alternate)
357 return have_icons() ? 0x80 | num | A_ALTCHARSET : alternate;
361 put_icon(int num, char alternate)
363 addch(icon_char(num, alternate));
368 bar_graph(int n_chars, int n_pixels)
374 } else if (n_pixels > n_chars * 5) {
375 n_pixels = n_chars * 5;
379 set_brick_icon(0, 5);
380 for (i = 0; i < n_pixels / 5; i++) {
385 set_brick_icon(1, n_pixels % 5);
392 put_flows(void *pfd_)
394 struct put_flows_data *pfd = pfd_;
395 static struct rconn_packet_counter *counter;
399 counter = rconn_packet_counter_create();
403 struct ofp_stats_request *rq;
404 struct ofp_aggregate_stats_request *asr;
407 pfd->xid = random_uint32();
408 rq = make_openflow_xid(sizeof *rq, OFPT_STATS_REQUEST,
410 rq->type = htons(OFPST_AGGREGATE);
411 rq->flags = htons(0);
412 asr = ofpbuf_put_uninit(b, sizeof *asr);
413 memset(asr, 0, sizeof *asr);
414 asr->match.wildcards = htonl(OFPFW_ALL);
415 asr->table_id = 0xff;
416 asr->out_port = htons(OFPP_NONE);
417 update_openflow_length(b);
418 rconn_send_with_limit(pfd->rconn, b, counter, 10);
421 if (!pfd->got_reply) {
424 rconn_run(pfd->rconn);
425 for (i = 0; i < 50; i++) {
428 b = rconn_recv(pfd->rconn);
433 parse_flow_reply(b->data, pfd);
435 if (pfd->got_reply) {
441 gethostname(host, sizeof host);
442 host[sizeof host - 1] = '\0';
443 if (strlen(host) + 6 <= 16) {
444 addf("Host: %s\n", host);
448 if (pfd->got_reply) {
449 addf("Flows: %"PRIu32, pfd->flow_count);
452 if (!pfd->got_reply) {
453 rconn_run_wait(pfd->rconn);
454 rconn_recv_wait(pfd->rconn);
459 show_flows(struct rconn *rconn)
461 static struct message *m;
462 static struct put_flows_data pfd;
464 memset(&pfd, 0, sizeof pfd);
466 emit_function(&m, P_STATUS, put_flows, &pfd);
470 struct put_dpid_ip_data {
479 parse_dp_reply(void *data, struct put_dpid_ip_data *pdid)
481 struct ofp_switch_features *osf;
482 struct ofp_header *oh;
485 if (ntohs(oh->length) < sizeof *osf) {
486 VLOG_WARN("reply is too short (%"PRIu16")", ntohs(oh->length));
489 if (oh->xid != pdid->xid) {
490 VLOG_WARN("xid 0x%08"PRIx32" != expected 0x%08"PRIx32,
494 if (oh->type != OFPT_FEATURES_REPLY) {
495 VLOG_WARN("reply is wrong type %"PRIu8, oh->type);
500 pdid->dpid = ntohll(osf->datapath_id);
501 pdid->got_reply = true;
505 put_dpid_id(void *pdid_)
507 struct put_dpid_ip_data *pdid = pdid_;
508 static struct rconn_packet_counter *counter;
511 counter = rconn_packet_counter_create();
515 struct ofp_header *oh;
518 pdid->xid = random_uint32();
519 oh = make_openflow_xid(sizeof *oh, OFPT_FEATURES_REQUEST,
521 rconn_send_with_limit(pdid->rconn, b, counter, 10);
524 if (!pdid->got_reply) {
527 rconn_run(pdid->rconn);
528 for (i = 0; i < 50; i++) {
531 b = rconn_recv(pdid->rconn);
536 parse_dp_reply(b->data, pdid);
538 if (pdid->got_reply) {
545 if (pdid->got_reply) {
546 addf("%012"PRIx64, pdid->dpid);
548 addf("\nIP: %s", pdid->ip);
550 if (!pdid->got_reply) {
551 rconn_run_wait(pdid->rconn);
552 rconn_recv_wait(pdid->rconn);
557 show_dpid_ip(struct rconn *rconn, const struct dict *dict)
559 static struct message *m;
560 static struct put_dpid_ip_data pdid;
561 const char *is_connected, *local_ip;
563 dict_lookup(dict, "local.is-connected", &is_connected);
564 dict_lookup(dict, "in-band.local-ip", &local_ip);
565 if (!is_connected && !local_ip) {
566 /* If we're not connected to the datapath and don't have a local IP,
567 * then we won't have anything useful to show anyhow. */
571 memset(&pdid, 0, sizeof pdid);
573 ovs_strlcpy(pdid.ip, local_ip ? local_ip : "", sizeof pdid.ip);
574 emit_function(&m, P_STATUS, put_dpid_id, &pdid);
578 dict_find(const struct dict *dict, const char *name)
582 for (i = 0; i < dict->n; i++) {
583 const struct pair *p = &dict->pairs[i];
584 if (!strcmp(p->name, name)) {
593 dict_lookup(const struct dict *dict, const char *name, const char **value)
595 size_t idx = dict_find(dict, name);
596 if (idx != SIZE_MAX) {
597 *value = dict->pairs[idx].value;
606 dict_get(const struct dict *dict, const char *name)
609 return dict_lookup(dict, name, &value) ? value : NULL;
613 dict_get_int(const struct dict *dict, const char *name, int def)
616 return dict_lookup(dict, name, &value) ? atoi(value) : def;
620 dict_get_bool(const struct dict *dict, const char *name, bool def)
623 if (dict_lookup(dict, name, &value)) {
624 if (!strcmp(value, "true")) {
627 if (!strcmp(value, "false")) {
635 dict_get_string(const struct dict *dict, const char *name, const char *def)
638 return dict_lookup(dict, name, &value) ? value : def;
642 dict_get_ip(const struct dict *dict, const char *name)
645 return (inet_aton(dict_get_string(dict, name, ""), &in) ? in.s_addr
650 addf(const char *format, ...)
655 va_start(args, format);
656 vsnprintf(buf, sizeof buf, format, args);
663 show_secchan_state(const struct dict *dict)
665 static struct message *msg;
666 const char *is_connected;
668 if (!dict_lookup(dict, "remote.is-connected", &is_connected)) {
669 /* Secchan not running or not responding. */
670 emit(&msg, P_ERROR, "Switch disabled");
675 discovery_state_label(const char *name)
677 static struct dict *states;
679 states = xmalloc(sizeof *states);
681 dict_add(states, "INIT", "Init");
682 dict_add(states, "INIT_REBOOT", "Init");
683 dict_add(states, "REBOOTING", "Init");
684 dict_add(states, "SELECTING", "Searching");
685 dict_add(states, "REQUESTING", "Requesting");
686 dict_add(states, "BOUND", "Got");
687 dict_add(states, "RENEWING", "Renewing");
688 dict_add(states, "REBINDING", "Rebinding");
689 dict_add(states, "RELEASED", "Released");
691 return dict_get_string(states, name, "Error");
695 show_discovery_state(const struct dict *dict)
697 static struct message *m_bound, *m_other;
699 const char *state, *ip;
700 enum priority priority;
703 state = dict_get_string(dict, "discovery.state", NULL);
707 ip = dict_get_string(dict, "discovery.ip", NULL);
708 state_elapsed = dict_get_int(dict, "discovery.state-elapsed", 0);
710 if (!strcmp(state, "BOUND")) {
715 priority = P_PROGRESS;
717 emit(m, priority, "Discovery %s\n%s",
718 progress(), discovery_state_label(state));
720 emit(m, priority, " %s", ip);
725 human_time(int seconds, char *buf, size_t size)
727 const char *sign = "";
730 seconds = seconds == INT_MIN ? INT_MAX : -seconds;
734 snprintf(buf, size, "%s%d s", sign, seconds);
735 } else if (seconds <= 60 * 60) {
736 snprintf(buf, size, "%s%d min", sign, seconds / 60);
737 } else if (seconds <= 60 * 60 * 24 * 2) {
738 snprintf(buf, size, "%s%d h", sign, seconds / 60 / 60);
740 snprintf(buf, size, "%s%d days", sign, seconds / 60 / 60 / 24);
745 show_fail_open_state(const struct dict *dict)
747 static struct message *m;
748 int cur_duration, trigger_duration;
750 if (!dict_get_bool(dict, "fail-open.triggered", false)) {
753 trigger_duration = dict_get_int(dict, "fail-open.trigger-duration", 0);
754 cur_duration = dict_get_int(dict, "fail-open.current-duration", 0);
756 emit(&m, P_WARNING, "Failed open %s\nafter %d secs",
757 progress(), trigger_duration);
760 human_time(cur_duration - trigger_duration, buf, sizeof buf);
761 emit(&m, P_WARNING, "In fail open for\n%s now %s", buf, progress());
768 return "..." + (3 - (unsigned int) time_now() % 4);
772 show_remote_state(const struct dict *dict)
774 bool debug_mode = dict_get_bool(dict, "debug", false);
775 const char *state, *is_connected;
777 state = dict_get_string(dict, "remote.state", NULL);
781 is_connected = dict_get_string(dict, "remote.is-connected", "false");
782 if (!strcmp(is_connected, "true")) {
784 static struct message *m_connected;
786 human_time(dict_get_int(dict, "remote.last-connection", 0),
788 emit(&m_connected, P_STATUS,
789 "Connected for\nlast %s %s", buf, progress());
792 if (!strcmp(state, "IDLE")) {
793 static struct message *m_idle;
794 emit(&m_idle, P_PROGRESS, "Sent idle probe");
798 const char *name = dict_get_string(dict, "remote.name", NULL);
800 static struct message *m_name;
801 emit(&m_name, P_STATUS, "Connected to\n%s", name);
805 int elapsed, backoff;
806 const char *name, *error;
808 elapsed = dict_get_int(dict, "remote.state-elapsed", 0);
809 backoff = dict_get_int(dict, "remote.backoff", 0);
810 name = dict_get_string(dict, "remote.name", "unknown");
811 state = dict_get_string(dict, "remote.state", "VOID");
812 error = dict_get_string(dict, "remote.last-connect-error", NULL);
813 if (!strcmp(state, "VOID")) {
814 static struct message *m;
815 emit(&m, P_PROGRESS, "Controller not\nfound");
816 } else if (!strcmp(state, "BACKOFF")) {
817 static struct message *m[3];
821 emit(&m[0], P_PROGRESS, "Connect failed:\n%s", error);
823 emit(&m[2], P_STATUS, "Last connected\n%s ago", buf);
824 emit(&m[1], P_PROGRESS,
825 "Disconnected\nReconnect in %d", backoff - elapsed);
826 human_time(dict_get_int(dict, "remote.last-connection", 0),
828 } else if (!strcmp(state, "CONNECTING")) {
829 static struct message *m;
830 emit(&m, P_PROGRESS, "Connecting %s\n%s", progress(), name);
836 fetch_status(struct rconn *rconn, struct dict *dict, long long timeout)
838 static struct rconn_packet_counter *counter;
840 struct nicira_header *rq;
845 counter = rconn_packet_counter_create();
848 xid = random_uint32();
851 rq = make_openflow_xid(sizeof *rq, OFPT_VENDOR, ++xid, &b);
852 rq->vendor = htonl(NX_VENDOR_ID);
853 rq->subtype = htonl(NXT_STATUS_REQUEST);
854 retval = rconn_send_with_limit(rconn, b, counter, 10);
856 /* continue into the loop so that we pause for a while */
859 while (time_msec() < timeout) {
864 for (i = 0; i < 50; i++) {
868 b = rconn_recv(rconn);
873 got_reply = parse_reply(b->data, dict, xid);
880 rconn_run_wait(rconn);
881 rconn_recv_wait(rconn);
882 poll_timer_wait(timeout - time_msec());
888 parse_reply(void *data, struct dict *dict, uint32_t xid)
890 struct ofp_header *oh;
891 struct nicira_header *rpy;
894 if (ntohs(oh->length) < sizeof *rpy) {
895 VLOG_WARN("reply is too short (%"PRIu16")", ntohs(oh->length));
898 if (oh->xid != xid) {
899 VLOG_WARN("xid 0x%08"PRIx32" != expected 0x%08"PRIx32, oh->xid, xid);
902 if (oh->type != OFPT_VENDOR) {
903 VLOG_WARN("reply is wrong type %"PRIu8, oh->type);
908 if (rpy->vendor != htonl(NX_VENDOR_ID)) {
909 VLOG_WARN("reply has wrong vendor ID %08"PRIx32, rpy->vendor);
912 if (rpy->subtype != htonl(NXT_STATUS_REPLY)) {
913 VLOG_WARN("reply has wrong subtype %08"PRIx32, rpy->subtype);
917 dict_parse(dict, (const char *) (rpy + 1),
918 ntohs(oh->length) - sizeof *rpy);
923 dict_parse(struct dict *dict, const char *data, size_t nbytes)
925 char *save_ptr = NULL;
928 copy = xmemdup0(data, nbytes);
929 for (name = strtok_r(copy, "=", &save_ptr); name;
930 name = strtok_r(NULL, "=", &save_ptr))
932 char *value = strtok_r(NULL, "\n", &save_ptr);
936 dict_add(dict, name, value);
942 dict_init(struct dict *dict)
946 dict->pairs = xmalloc(sizeof *dict->pairs * dict->max);
950 dict_add(struct dict *dict, const char *name, const char *value)
952 dict_add_nocopy(dict, xstrdup(name), xstrdup(value));
956 dict_add_nocopy(struct dict *dict, char *name, char *value)
960 if (dict->n >= dict->max) {
962 dict->pairs = xrealloc(dict->pairs, sizeof *dict->pairs * dict->max);
964 p = &dict->pairs[dict->n++];
970 dict_delete(struct dict *dict, const char *name)
973 while ((idx = dict_find(dict, name)) != SIZE_MAX) {
974 struct pair *pair = &dict->pairs[idx];
977 dict->pairs[idx] = dict->pairs[--dict->n];
982 dict_free(struct dict *dict)
987 for (i = 0; i < dict->n; i++) {
988 free(dict->pairs[i].name);
989 free(dict->pairs[i].value);
996 initialize_terminal(void)
1002 intrflush(stdscr, FALSE);
1003 keypad(stdscr, TRUE);
1004 nodelay(stdscr, TRUE);
1006 scrollok(stdscr, TRUE);
1010 restore_terminal(void *aux UNUSED)
1020 struct show_rates_data {
1021 struct rconn *rconn;
1023 struct byte_count prev, now;
1028 parse_port_reply(void *data, struct show_rates_data *rates)
1030 struct ofp_header *oh;
1031 struct ofp_stats_reply *rpy;
1032 struct ofp_port_stats *ops;
1037 if (ntohs(oh->length) < sizeof *rpy) {
1038 VLOG_WARN("reply is too short (%"PRIu16")", ntohs(oh->length));
1041 if (oh->xid != rates->xid) {
1042 VLOG_WARN("xid 0x%08"PRIx32" != expected 0x%08"PRIx32,
1043 oh->xid, rates->xid);
1046 if (oh->type != OFPT_STATS_REPLY) {
1047 VLOG_WARN("reply is wrong type %"PRIu8, oh->type);
1052 if (rpy->type != htons(OFPST_PORT)) {
1053 VLOG_WARN("reply has wrong stat type ID %08"PRIx16, rpy->type);
1057 n_ports = ((ntohs(oh->length) - offsetof(struct ofp_stats_reply, body))
1059 ops = (struct ofp_port_stats *) rpy->body;
1060 rates->prev = rates->now;
1061 rates->now.when = time_msec();
1062 rates->now.tx_bytes = UINT64_MAX;
1063 for (i = 0; i < n_ports; i++, ops++) {
1064 if (ops->tx_bytes != htonll(UINT64_MAX)) {
1065 if (rates->now.tx_bytes == UINT64_MAX) {
1066 rates->now.tx_bytes = 0;
1068 rates->now.tx_bytes += ntohll(ops->tx_bytes);
1071 rates->got_reply = true;
1075 dump_graph(const bool graph[80])
1077 signed char icons[32];
1081 memset(icons, -1, sizeof icons);
1082 for (i = 0; i < 16; i++) {
1087 for (j = 0; j < 5; j++) {
1088 row = (row << 1) | graph[i * 5 + j];
1095 if (icons[row] < 0) {
1100 set_repeated_icon(n_icons, row);
1101 icons[row] = n_icons++;
1103 put_icon(icons[row], row == 0x1f ? '#' : ' ');
1108 do_show_data_rates(void *rates_)
1110 struct show_rates_data *rates = rates_;
1111 static struct rconn_packet_counter *counter;
1115 counter = rconn_packet_counter_create();
1118 struct ofp_stats_request *rq;
1121 rates->xid = random_uint32();
1122 rq = make_openflow_xid(sizeof *rq, OFPT_STATS_REQUEST,
1124 rq->type = htons(OFPST_PORT);
1125 rq->flags = htons(0);
1126 rconn_send_with_limit(rates->rconn, b, counter, 10);
1129 if (!rates->got_reply) {
1132 rconn_run(rates->rconn);
1133 for (i = 0; i < 50; i++) {
1136 b = rconn_recv(rates->rconn);
1141 parse_port_reply(b->data, rates);
1143 if (rates->got_reply) {
1177 memset(graph, 0, sizeof graph);
1190 if (rates->now.tx_bytes != UINT64_MAX
1191 && rates->prev.tx_bytes != UINT64_MAX
1192 && rates->now.when - rates->prev.when > 500
1193 && time_msec() - rates->now.when < 2000)
1195 uint64_t bits = (rates->now.tx_bytes - rates->prev.tx_bytes) * 8;
1196 uint64_t msecs = rates->now.when - rates->prev.when;
1197 double bps = (double) bits * 1000.0 / msecs;
1198 int pixels = bps > 0 ? log(bps) / log(10.0) * 8 + .5 : 0;
1201 } else if (pixels > 80) {
1204 memset(graph, 1, pixels);
1209 if (!rates->got_reply) {
1210 rconn_run_wait(rates->rconn);
1211 rconn_recv_wait(rates->rconn);
1216 show_data_rates(struct rconn *rconn, const struct dict *dict)
1218 static struct message *m;
1219 static struct show_rates_data rates;
1220 const char *is_connected, *local_ip;
1221 static bool inited = false;
1223 dict_lookup(dict, "local.is-connected", &is_connected);
1224 dict_lookup(dict, "in-band.local-ip", &local_ip);
1225 if (!is_connected && !local_ip) {
1226 /* If we're not connected to the datapath and don't have a local IP,
1227 * then we won't have anything useful to show anyhow. */
1231 rates.rconn = rconn;
1233 rates.got_reply = false;
1235 rates.now.tx_bytes = UINT64_MAX;
1236 rates.prev.tx_bytes = UINT64_MAX;
1239 emit_function(&m, P_STATUS, do_show_data_rates, &rates);
1244 void (*function)(void *aux);
1249 enum priority priority;
1254 static struct message **messages;
1255 static size_t n_messages, allocated_messages;
1257 static struct message *
1258 allocate_message(struct message **msgp)
1261 /* Allocate and initialize message. */
1262 *msgp = xcalloc(1, sizeof **msgp);
1263 (*msgp)->index = n_messages;
1265 /* Add to list of messages. */
1266 if (n_messages >= allocated_messages) {
1267 allocated_messages = 2 * allocated_messages + 1;
1268 messages = xrealloc(messages,
1269 sizeof *messages * allocated_messages);
1271 messages[n_messages++] = *msgp;
1277 emit(struct message **msgp, enum priority priority, const char *format, ...)
1279 struct message *msg = allocate_message(msgp);
1283 msg->priority = priority;
1285 va_start(args, format);
1286 length = strlen(msg->string);
1287 vsnprintf(msg->string + length, sizeof msg->string - length, format, args);
1292 emit_function(struct message **msgp, enum priority priority,
1293 void (*function)(void *aux), void *aux)
1295 struct message *msg = allocate_message(msgp);
1296 msg->priority = priority;
1297 msg->function = function;
1302 shown(struct message **msgp)
1304 struct message *msg = allocate_message(msgp);
1309 clear_messages(void)
1313 for (i = 0; i < n_messages; i++) {
1314 struct message *msg = messages[i];
1315 msg->string[0] = '\0';
1316 msg->function = NULL;
1320 static struct message *
1323 struct message *best_msg;
1327 best_score = INT_MIN;
1329 for (i = 0; i < n_messages; i++) {
1330 struct message *msg = messages[i];
1333 if (empty_message(msg)) {
1337 score = msg->priority;
1341 score -= msg->shown;
1343 if (score > best_score) {
1352 message_shown(struct message *msg)
1354 if (msg && msg->shown++ > 3600) {
1360 empty_message(const struct message *msg)
1362 return !msg || (!msg->string[0] && !msg->function);
1365 static struct message *get_message(size_t index)
1367 assert(index <= n_messages || index == SIZE_MAX);
1368 return (index < n_messages ? messages[index]
1369 : index == SIZE_MAX ? messages[n_messages - 1]
1373 static struct message *
1374 next_message(struct message *msg)
1378 for (p = get_message(msg->index + 1); p != msg;
1379 p = get_message(p->index + 1)) {
1380 if (!empty_message(p)) {
1387 static struct message *
1388 prev_message(struct message *msg)
1392 for (p = get_message(msg->index - 1); p != msg;
1393 p = get_message(p->index - 1)) {
1394 if (!empty_message(p)) {
1402 put_message(const struct message *m)
1406 } else if (m->function) {
1407 m->function(m->aux);
1418 for (i = 0; i < n_messages; i++) {
1419 struct message *msg = messages[i];
1420 if (!empty_message(msg)) {
1425 for (i = 0; i < n_messages; i++) {
1426 struct message *msg = messages[i];
1427 if (empty_message(msg)) {
1428 msg->age = msg->shown = 0;
1430 if (msg->age && msg->age % 60 == 0) {
1431 msg->shown -= MAX(0, 5 - (load + 6) / 12);
1432 if (msg->shown < 0) {
1436 if (msg->age++ > 3600) {
1443 /* Set by SIGUSR1 handler. */
1444 static volatile sig_atomic_t sigusr1_triggered;
1446 /* The time after which we stop indicating that the switch is rebooting.
1447 * (This is just in case the reboot fails.) */
1448 static time_t reboot_deadline = TIME_MIN;
1450 static void sigusr1_handler(int);
1453 init_reboot_notifier(void)
1455 signal(SIGUSR1, sigusr1_handler);
1459 sigusr1_handler(int signr UNUSED)
1461 sigusr1_triggered = true;
1465 show_reboot_state(void)
1467 if (sigusr1_triggered) {
1468 reboot_deadline = time_now() + 30;
1469 sigusr1_triggered = false;
1471 if (time_now() < reboot_deadline) {
1472 static struct message *msg;
1473 emit(&msg, P_FATAL, "Rebooting");
1481 void (*f)(const struct dict *);
1488 struct menu_item **items;
1489 size_t n_items, allocated_items;
1492 static void menu_init(struct menu *);
1493 static void menu_free(struct menu *);
1494 static struct menu_item *menu_add_item(struct menu *, const char *text, ...)
1495 PRINTF_FORMAT(2, 3);
1496 static int menu_show(const struct menu *, int start, bool select);
1498 static void cmd_shell(const struct dict *);
1499 static void cmd_show_version(const struct dict *);
1500 static void cmd_configure(const struct dict *);
1501 static void cmd_setup_pki(const struct dict *);
1502 static void cmd_browse_status(const struct dict *);
1503 static void cmd_show_motto(const struct dict *);
1506 menu_init(struct menu *menu)
1508 memset(menu, 0, sizeof *menu);
1512 menu_free(struct menu *menu)
1516 for (i = 0; i < menu->n_items; i++) {
1517 struct menu_item *item = menu->items[i];
1524 static struct menu_item *
1525 menu_add_item(struct menu *menu, const char *text, ...)
1527 struct menu_item *item;
1530 if (menu->n_items >= menu->allocated_items) {
1531 menu->allocated_items = 2 * menu->allocated_items + 1;
1532 menu->items = xrealloc(menu->items,
1533 sizeof *menu->items * menu->allocated_items);
1535 item = menu->items[menu->n_items++] = xmalloc(sizeof *item);
1536 va_start(args, text);
1537 item->text = xvasprintf(text, args);
1541 item->enabled = true;
1547 menu(const struct dict *dict)
1549 bool debug_mode = dict_get_bool(dict, "debug", false);
1554 menu_add_item(&menu, "Exit");
1555 menu_add_item(&menu, "Show Version")->f = cmd_show_version;
1556 menu_add_item(&menu, "Configure")->f = cmd_configure;
1557 menu_add_item(&menu, "Setup PKI")->f = cmd_setup_pki;
1559 menu_add_item(&menu, "Browse Status")->f = cmd_browse_status;
1560 menu_add_item(&menu, "Shell")->f = cmd_shell;
1561 menu_add_item(&menu, "Show Motto")->f = cmd_show_motto;
1564 choice = menu_show(&menu, 0, true);
1566 void (*f)(const struct dict *) = menu.items[choice]->f;
1576 menu_show(const struct menu *menu, int start, bool select)
1578 long long int adjust = LLONG_MAX;
1579 int min = 0, max = MAX(menu->n_items - 2, 0);
1608 if (menu->n_items) {
1609 pos = MIN(menu->n_items - 1, MAX(0, start));
1618 while ((key = getch()) != ERR) {
1621 if (select && selection > 0) {
1623 if (selection >= pos) {
1633 if (select && selection < menu->n_items - 1) {
1635 if (selection <= pos + 1) {
1644 case '\r': case '\n':
1645 if (select && selection >= 0 && selection < menu->n_items) {
1646 struct menu_item *item = menu->items[selection];
1647 if (!item->enabled) {
1648 show_string("Item disabled");
1650 } else if (item->toggle >= 0) {
1651 item->toggle = !item->toggle;
1657 case '\b': case '\x7f': case '\x1b':
1658 case KEY_BACKSPACE: case KEY_DC:
1661 adjust = time_msec() + 1000;
1663 if (time_msec() >= adjust && menu->n_items > 1) {
1666 } else if (pos > max) {
1674 if (!menu->n_items) {
1678 for (idx = pos; idx < pos + 2; idx++) {
1683 if (selection == idx) {
1692 } else if (idx >= menu->n_items) {
1695 const struct menu_item *item = menu->items[idx];
1696 size_t length = strlen(item->text);
1697 if (!item->enabled) {
1701 if (item->toggle >= 0) {
1703 addch(icon_char(item->toggle ? 2 : 1, 0));
1706 addstr(item->toggle ? "[X]" : "[ ]");
1710 addnstr(item->text, MIN(width, length));
1711 if (!item->enabled) {
1722 if (pos < min || pos > max) {
1723 poll_timer_wait(adjust - time_msec());
1725 poll_fd_wait(STDIN_FILENO, POLLIN);
1731 menu_show2(const struct menu *menu, int start, bool select)
1734 if (menu->n_items) {
1735 pos = MIN(menu->n_items - 1, MAX(0, start));
1760 while ((key = getch()) != ERR) {
1769 if (menu->n_items > 0 && pos < menu->n_items - 1) {
1774 case '\r': case '\n':
1775 if (select && !menu->items[pos]->enabled) {
1776 show_string("Item disabled");
1781 case '\b': case '\x7f': case '\x1b':
1782 case KEY_BACKSPACE: case KEY_DC:
1793 const struct menu_item *item = menu->items[pos];
1794 const char *line1 = item->text;
1795 size_t len1 = strcspn(line1, "\n");
1796 const char *line2 = line1[len1] ? &line1[len1 + 1] : "";
1797 size_t len2 = strcspn(line2, "\n");
1798 size_t width = 39 - 2 * !item->enabled;
1801 addch(pos > 0 ? icon_char(0, '^') : ' ');
1802 if (!item->enabled && len1) {
1805 addnstr(line1, MIN(len1, width));
1806 if (!item->enabled && len1) {
1812 addch(pos < menu->n_items - 1 ? icon_char(1, 'V') : ' ');
1813 if (!item->enabled && len2) {
1816 addnstr(line2, MIN(len2, width));
1817 if (!item->enabled && len2) {
1823 poll_fd_wait(STDIN_FILENO, POLLIN);
1829 yesno(const char *title, bool def)
1846 while ((key = getch()) != ERR) {
1863 case '\r': case '\n':
1874 addch(answer ? icon_char(0, '>') : ' ');
1878 addch(!answer ? icon_char(0, '>') : ' ');
1883 poll_fd_wait(STDIN_FILENO, POLLIN);
1889 cmd_show_version(const struct dict *dict UNUSED)
1891 show_string(VERSION BUILDNR);
1895 cmd_browse_status(const struct dict *dict)
1901 for (i = 0; i < dict->n; i++) {
1902 const struct pair *p = &dict->pairs[i];
1903 menu_add_item(&menu, "%s = %s", p->name, p->value);
1905 menu_show(&menu, 0, false);
1910 cmd_shell(const struct dict *dict UNUSED)
1918 printf("Type ^D to exit\n");
1925 home = getenv("HOME");
1930 initialize_terminal();
1934 cmd_show_motto(const struct dict *dict UNUSED)
1936 show_string("\"Just Add Ice\"");
1940 show_string(const char *string)
1942 VLOG_INFO("%s", string);
1948 block_until(time_msec() + 5000);
1952 block_until(long long timeout)
1954 while (timeout > time_msec()) {
1955 poll_timer_wait(timeout - time_msec());
1958 drain_keyboard_buffer();
1962 drain_keyboard_buffer(void)
1964 while (getch() != ERR) {
1970 read_vars(const char *cmd, struct dict *dict)
1976 stream = popen(cmd, "r");
1978 VLOG_ERR("popen(\"%s\") failed: %s", cmd, strerror(errno));
1984 while (!ds_get_line(&ds, stream)) {
1985 const char *s = ds_cstr(&ds);
1986 const char *equals = strchr(s, '=');
1988 dict_add_nocopy(dict,
1989 xmemdup0(s, equals - s), xstrdup(equals + 1));
1992 status = pclose(stream);
1994 char *msg = process_status_msg(status);
1995 VLOG_ERR("pclose(\"%s\") reported subprocess failure: %s",
2005 run_and_report_failure(char **argv, const char *title)
2007 int null_fds[3] = {0, 1, 2};
2012 s = process_escape_args(argv);
2013 VLOG_INFO("starting subprocess: %s", s);
2016 retval = process_run(argv, NULL, 0, null_fds, 3, &status);
2018 char *s = xasprintf("%s:\n%s", title, strerror(retval));
2022 } else if (status) {
2023 char *msg = process_status_msg(status);
2024 char *s = xasprintf("%s:\n%s", title, msg);
2030 VLOG_INFO("subprocess exited with status 0");
2036 do_load_config(const char *file_name, struct dict *dict)
2038 struct dict auto_vars;
2043 /* Get the list of the variables that the shell sets automatically. */
2044 retval = read_vars("set -a && env", &auto_vars);
2049 /* Get the variables from 'file_name'. */
2050 cmd = xasprintf("set -a && . '%s' && env", file_name);
2051 retval = read_vars(cmd, dict);
2054 dict_free(&auto_vars);
2059 for (i = 0; i < auto_vars.n; i++) {
2060 dict_delete(dict, auto_vars.pairs[i].name);
2062 dict_free(&auto_vars);
2067 load_config(struct dict *dict)
2069 static const char default_file[] = "/etc/default/openflow-switch";
2070 int retval = do_load_config(default_file, dict);
2074 char *s = xasprintf("Cfg load failed:\n%s", strerror(retval));
2082 save_config(const struct svec *settings)
2088 VLOG_INFO("Saving configuration:");
2089 for (i = 0; i < settings->n; i++) {
2090 VLOG_INFO("%s", settings->names[i]);
2094 svec_add(&argv, "/usr/share/openvswitch/commands/reconfigure");
2095 svec_append(&argv, settings);
2096 svec_terminate(&argv);
2097 ok = run_and_report_failure(argv.names, "Save failed");
2099 long long int timeout = time_msec() + 5000;
2104 addstr("Saved.\nRestarting...");
2108 svec_add(&argv, "/bin/sh");
2109 svec_add(&argv, "-c");
2111 "/etc/init.d/openflow-switch restart >/dev/null 2>&1");
2112 svec_terminate(&argv);
2114 ok = run_and_report_failure(argv.names, "Restart failed");
2116 block_until(timeout);
2119 svec_destroy(&argv);
2122 VLOG_INFO("Save completed successfully");
2124 VLOG_WARN("Save failed");
2130 match(pcre *re, const char *string, int length)
2135 retval = pcre_exec(re, NULL, string, length, 0, PCRE_PARTIAL,
2136 ovec, ARRAY_SIZE(ovec));
2138 if (ovec[0] >= 0 && ovec[1] >= length) {
2139 /* 're' matched all of 'string'. */
2142 /* 're' matched the initial part of 'string' but not all of it. */
2143 return PCRE_ERROR_NOMATCH;
2151 figure_choices(pcre *re, struct ds *s, int pos, struct ds *choices)
2159 /* See whether the current string is a complete match. */
2160 if (!match(re, s->string, pos)) {
2161 ds_put_char(choices, '\n');
2164 /* Then try all the other possibilities. */
2166 ds_put_buffer(&tmp, s->string, pos);
2167 for (c = 0x20; c < 0x7f; c++) {
2168 ds_put_char(&tmp, c);
2169 retval = match(re, tmp.string, pos + 1);
2170 if (retval == PCRE_ERROR_PARTIAL || !retval) {
2171 ds_put_char(choices, c);
2177 if (!choices->length) {
2178 ds_put_char(choices, '\n');
2183 figure_completion(pcre *re, struct ds *s)
2189 /* See whether the current string is a complete match. */
2190 if (!match(re, s->string, s->length)) {
2193 for (c = 0x20; c < 0x7f; c++) {
2197 retval = match(re, s->string, s->length);
2200 if (retval == PCRE_ERROR_PARTIAL || !retval) {
2210 ds_put_char(s, found);
2214 #define OCTET_RE "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"
2215 #define IP_RE "("OCTET_RE"\\."OCTET_RE"\\."OCTET_RE"\\."OCTET_RE")"
2219 "[1-9][0-9][0-9]|" \
2220 "[1-9][0-9][0-9][0-9]|" \
2221 "[1-5][0-9][0-9][0-9][0-9]|" \
2222 "6[1-4][0-9][0-9][0-9]|" \
2223 "65[1-4][0-9][0-9]|" \
2226 #define XOCTET_RE "[0-9A-F][0-9A-F]"
2228 XOCTET_RE":"XOCTET_RE":"XOCTET_RE":"\
2229 XOCTET_RE":"XOCTET_RE":"XOCTET_RE
2230 #define NUM100_TO_99999_RE \
2231 "([1-9][0-9][0-9]|" \
2232 "[1-9][0-9][0-9][0-9]|" \
2233 "[1-9][0-9][0-9][0-9][0-9])"
2234 #define NUM5_TO_99999_RE \
2237 "[1-9][0-9][0-9]|" \
2238 "[1-9][0-9][0-9][0-9]|" \
2239 "[1-9][0-9][0-9][0-9][0-9])"
2240 #define NUM1_TO_99999_RE \
2243 "[1-9][0-9][0-9]|" \
2244 "[1-9][0-9][0-9][0-9]|" \
2245 "[1-9][0-9][0-9][0-9][0-9])"
2248 prompt(const char *prompt, const char *initial, const char *pattern)
2270 re = pcre_compile(pattern, PCRE_ANCHORED, &error, &erroffset, NULL);
2272 VLOG_ERR("PCRE error for pattern \"%s\" at offset %d: %s",
2273 pattern, erroffset, error);
2274 return xstrdup(initial);
2277 retval = pcre_fullinfo(re, NULL, PCRE_INFO_OKPARTIAL, &okpartial);
2283 ds_put_cstr(&ds, initial);
2285 figure_choices(re, &ds, pos, &choices);
2286 p = memchr(choices.string, initial[0], choices.length);
2287 chidx = p ? p - choices.string : 0;
2291 while ((key = getch()) != ERR) {
2294 if (choices.length > 1) {
2295 if (++chidx >= choices.length) {
2298 ds.string[pos] = choices.string[chidx];
2299 ds_truncate(&ds, pos + 1);
2300 figure_completion(re, &ds);
2305 if (choices.length > 1) {
2307 chidx = choices.length - 1;
2309 ds.string[pos] = choices.string[chidx];
2310 ds_truncate(&ds, pos + 1);
2311 figure_completion(re, &ds);
2315 case '\r': case '\n':
2316 if (choices.string[chidx] == '\n') {
2317 ds_truncate(&ds, pos);
2318 return ds_cstr(&ds);
2320 if (pos >= ds.length) {
2322 ds_put_char(&ds, choices.string[chidx]);
2323 figure_choices(re, &ds, pos, &choices);
2325 figure_completion(re, &ds);
2328 figure_choices(re, &ds, pos, &choices);
2330 figure_completion(re, &ds);
2336 ds_truncate(&ds, pos + 1);
2337 figure_choices(re, &ds, pos, &choices);
2341 case '\b': case '\x7f': case '\x1b':
2342 case KEY_BACKSPACE: case KEY_DC:
2346 return xstrdup(initial);
2348 figure_choices(re, &ds, pos, &choices);
2350 if (pos < ds.length) {
2351 p = memchr(choices.string, ds.string[pos],
2354 chidx = p - choices.string;
2360 if (key >= 0x20 && key < 0x7f) {
2361 /* Check whether 'key' is valid and toggle case if
2363 if (!memchr(choices.string, key, choices.length)) {
2364 if (memchr(choices.string, toupper(key),
2367 } else if (memchr(choices.string, tolower(key),
2375 /* Insert 'key' and advance the position. */
2376 if (pos >= ds.length) {
2377 ds_put_char(&ds, key);
2379 ds.string[pos] = key;
2383 if (choices.string[chidx] != key) {
2384 ds_truncate(&ds, pos);
2386 figure_choices(re, &ds, pos, &choices);
2388 if (pos < ds.length) {
2389 p = memchr(choices.string, ds.string[pos],
2392 chidx = p - choices.string;
2395 figure_completion(re, &ds);
2403 addnstr(prompt, MIN(40, strlen(prompt)));
2405 c = choices.string[chidx];
2407 addstr(ds_cstr(&ds));
2417 poll_fd_wait(STDIN_FILENO, POLLIN);
2423 prompt_ip(const char *title, uint32_t *ip)
2425 char *in = xasprintf(IP_FMT, IP_ARGS(ip));
2426 char *out = prompt(title, in, "^"IP_RE"$");
2427 *ip = inet_addr(out);
2433 abbreviate_netdevs(const struct svec *netdevs, struct ds *abbrev)
2438 for (i = 0; i < netdevs->n; ) {
2439 size_t i_len = strlen(netdevs->names[i]);
2442 for (j = i + 1; j < netdevs->n; j++) {
2443 size_t j_len = strlen(netdevs->names[j]);
2444 if (!i_len || !j_len || i_len != j_len
2445 || memcmp(netdevs->names[i], netdevs->names[j], i_len - 1)) {
2450 if (abbrev->length) {
2451 ds_put_char(abbrev, ' ');
2454 ds_put_cstr(abbrev, netdevs->names[i]);
2458 ds_put_buffer(abbrev, netdevs->names[i], i_len - 1);
2459 ds_put_char(abbrev, '[');
2460 for (k = i; k < j; k++) {
2461 ds_put_char(abbrev, netdevs->names[k][i_len - 1]);
2463 ds_put_char(abbrev, ']');
2470 choose_netdevs(struct svec *choices)
2472 struct svec netdevs;
2476 netdev_enumerate(&netdevs);
2477 svec_sort(&netdevs);
2480 menu_add_item(&menu, "Exit");
2481 for (i = 0; i < netdevs.n; i++) {
2482 const char *name = netdevs.names[i];
2483 struct menu_item *item;
2484 struct netdev *netdev;
2487 if (!strncmp(name, "wmaster", strlen("wmaster"))
2488 || !strncmp(name, "of", strlen("of"))
2489 || !strcmp(name, "lo")) {
2493 retval = netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev);
2495 bool exclude = netdev_get_in4(netdev, NULL);
2496 netdev_close(netdev);
2502 item = menu_add_item(&menu, "%s", name);
2503 item->toggle = svec_contains(choices, name);
2505 if (menu.n_items > 1) {
2506 menu_show(&menu, 0, true);
2508 show_string("No available\nbridge ports");
2511 svec_clear(choices);
2512 for (i = 0; i < menu.n_items; i++) {
2513 struct menu_item *item = menu.items[i];
2514 if (item->toggle > 0) {
2515 svec_add(choices, item->text);
2523 is_datapath_id_in_dmi(void)
2529 dmidecode = popen("dmidecode -s system-uuid", "r");
2533 is_in_dmi = fgets(line, sizeof line, dmidecode) && strstr(line, "-002320");
2538 struct switch_config {
2539 struct svec netdevs;
2540 enum { DISCOVERY, IN_BAND } mode;
2542 uint32_t switch_mask;
2544 enum { FAIL_DROP, FAIL_SWITCH } disconnected;
2547 int inactivity_probe;
2549 char *controller_vconn;
2554 disconnected_string(int value)
2556 #define FAIL_SWITCH_STRING "Switch packets"
2557 #define FAIL_DROP_STRING "Drop packets"
2558 return value == FAIL_SWITCH ? FAIL_SWITCH_STRING : FAIL_DROP_STRING;
2562 cmd_configure(const struct dict *dict UNUSED)
2564 bool debug_mode = dict_get_bool(dict, "debug", false);
2565 struct dict config_dict;
2566 struct switch_config config;
2569 if (!load_config(&config_dict)) {
2572 svec_init(&config.netdevs);
2573 svec_parse_words(&config.netdevs,
2574 dict_get_string(&config_dict, "NETDEVS", ""));
2575 config.mode = (!strcmp(dict_get_string(&config_dict, "MODE", "discovery"),
2576 "in-band") ? IN_BAND : DISCOVERY);
2577 config.switch_ip = dict_get_ip(&config_dict, "SWITCH_IP");
2578 config.switch_mask = dict_get_ip(&config_dict, "SWITCH_NETMASK");
2579 config.switch_gw = dict_get_ip(&config_dict, "SWITCH_GATEWAY");
2580 config.controller_vconn = xstrdup(dict_get_string(&config_dict,
2582 config.disconnected = (!strcmp(dict_get_string(&config_dict,
2583 "DISCONNECTED_MODE", ""),
2585 ? FAIL_SWITCH : FAIL_DROP);
2586 config.stp = !strcmp(dict_get_string(&config_dict, "stp", ""), "yes");
2587 config.rate_limit = dict_get_int(&config_dict, "RATE_LIMIT", -1);
2588 config.inactivity_probe = dict_get_int(&config_dict, "INACTIVITY_PROBE",
2590 config.max_backoff = dict_get_int(&config_dict, "MAX_BACKOFF", -1);
2591 if (is_datapath_id_in_dmi()) {
2592 config.datapath_id = xstrdup("DMI");
2594 const char *dpid = dict_get(&config_dict, "DATAPATH_ID");
2596 struct ds ds = DS_EMPTY_INITIALIZER;
2598 for (cp = dpid; *cp != '\0'; cp++) {
2600 ds_put_char(&ds, toupper((unsigned char) *cp));
2603 config.datapath_id = ds_cstr(&ds);
2605 config.datapath_id = xstrdup("Random");
2608 dict_free(&config_dict);
2611 while (start != -1) {
2620 MENU_DISCONNECTED_MODE,
2624 MENU_INACTIVITY_PROBE,
2629 struct menu_item *item;
2637 item = menu_add_item(&menu, "Exit");
2638 item->id = MENU_EXIT;
2641 abbreviate_netdevs(&config.netdevs, &ports);
2642 item = menu_add_item(&menu, "Bridge Ports:\n%s", ds_cstr(&ports));
2643 item->id = MENU_NETDEVS;
2647 item = menu_add_item(&menu, "Mode:\n%s",
2648 (config.mode == DISCOVERY
2649 ? "Discovery" : "In-Band"));
2650 item->id = MENU_MODE;
2653 if (config.switch_ip == htonl(0)) {
2654 item = menu_add_item(&menu, "Switch IP Addr:\nDHCP");
2656 item = menu_add_item(&menu, "Switch IP Addr:\n"IP_FMT,
2657 IP_ARGS(&config.switch_ip));
2660 item->enabled = config.mode == IN_BAND;
2663 item = menu_add_item(&menu, "Switch Netmask:\n"IP_FMT,
2664 IP_ARGS(&config.switch_mask));
2665 item->id = MENU_NETMASK;
2666 item->enabled = config.mode == IN_BAND && config.switch_ip != htonl(0);
2669 item = menu_add_item(&menu, "Switch Gateway:\n"IP_FMT,
2670 IP_ARGS(&config.switch_gw));
2671 item->id = MENU_GATEWAY;
2672 item->enabled = config.mode == IN_BAND && config.switch_ip != htonl(0);
2675 item = menu_add_item(&menu, "Controller:\n%s",
2676 config.controller_vconn);
2677 item->id = MENU_CONTROLLER;
2678 item->enabled = config.mode == IN_BAND;
2680 /* Disconnected mode. */
2681 item = menu_add_item(&menu, "If disconnected:\n%s\n",
2682 disconnected_string(config.disconnected));
2683 item->id = MENU_DISCONNECTED_MODE;
2686 item = menu_add_item(&menu, "Datapath ID:\n%s", config.datapath_id);
2687 item->id = MENU_DATAPATH_ID;
2688 item->enabled = strcmp(config.datapath_id, "DMI");
2690 /* Spanning tree protocol. */
2692 item = menu_add_item(&menu, "802.1D-1998 STP:\n%s",
2693 config.stp ? "Enabled" : "Disabled");
2694 item->id = MENU_STP;
2697 /* Rate-limiting. */
2699 if (config.rate_limit < 0) {
2700 item = menu_add_item(&menu, "Ctlr rate limit:\nDisabled");
2702 item = menu_add_item(&menu, "Ctlr rate limit:\n%d/s",
2705 item->id = MENU_RATE_LIMIT;
2708 /* Inactivity probe. */
2710 if (config.inactivity_probe < 0) {
2711 item = menu_add_item(&menu, "Activity probe:\nDefault");
2713 item = menu_add_item(&menu, "Activity probe:\n%d s",
2714 config.inactivity_probe);
2716 item->id = MENU_INACTIVITY_PROBE;
2721 if (config.max_backoff < 0) {
2722 item = menu_add_item(&menu, "Max backoff:\nDefault");
2724 item = menu_add_item(&menu, "Max backoff:\n%d s",
2725 config.max_backoff);
2727 item->id = MENU_MAX_BACKOFF;
2730 start = menu_show2(&menu, start, true);
2740 choose_netdevs(&config.netdevs);
2744 out = prompt("Mode:",
2745 config.mode == DISCOVERY ? "Discovery" : "In-Band",
2746 "^(Discovery|In-Band)$");
2747 config.mode = !strcmp(out, "Discovery") ? DISCOVERY : IN_BAND;
2752 in = (config.switch_ip == htonl(0) ? xstrdup("DHCP")
2753 : xasprintf(IP_FMT, IP_ARGS(&config.switch_ip)));
2754 out = prompt("Switch IP:", in, "^(DHCP|"IP_RE")$");
2755 ip = strcmp(out, "DHCP") ? inet_addr(out) : htonl(0);
2758 if (ip != config.switch_ip) {
2759 config.switch_ip = ip;
2760 if (ip != htonl(0)) {
2761 uint32_t mask = guess_netmask(ip);
2763 config.switch_mask = mask;
2764 config.switch_gw = (ip & mask) | htonl(1);
2771 prompt_ip("Switch Netmask:", &config.switch_mask);
2775 prompt_ip("Switch Gateway:", &config.switch_gw);
2778 case MENU_CONTROLLER:
2779 out = prompt("Controller:", config.controller_vconn,
2780 "^(tcp|ssl):"IP_RE"(:"PORT_RE")?$");
2781 free(config.controller_vconn);
2782 config.controller_vconn = out;
2785 case MENU_DISCONNECTED_MODE:
2786 out = prompt("If disconnected",
2787 disconnected_string(config.disconnected),
2788 "^("FAIL_DROP_STRING"|"FAIL_SWITCH_STRING")$");
2789 config.disconnected = (!strcmp(out, FAIL_DROP_STRING)
2790 ? FAIL_DROP : FAIL_SWITCH);
2794 case MENU_DATAPATH_ID:
2795 out = prompt("Datapath ID:", config.datapath_id,
2796 "^Random|"MAC_RE"$");
2797 free(config.datapath_id);
2798 config.datapath_id = out;
2802 out = prompt("802.1D-1998 STP:",
2803 config.stp ? "Enabled" : "Disabled",
2804 "^(Enabled|Disabled)$");
2805 config.stp = !strcmp(out, "Enabled");
2809 case MENU_RATE_LIMIT:
2810 in = (config.rate_limit < 0
2811 ? xstrdup("Disabled")
2812 : xasprintf("%d/s", config.rate_limit));
2813 out = prompt("Ctlr rate limit:", in,
2814 "^(Disabled|("NUM100_TO_99999_RE")/s)$");
2816 config.rate_limit = isdigit(out[0]) ? atoi(out) : -1;
2820 case MENU_INACTIVITY_PROBE:
2821 in = (config.inactivity_probe < 0
2822 ? xstrdup("Default")
2823 : xasprintf("%d s", config.inactivity_probe));
2824 out = prompt("Activity probe:", in,
2825 "^(Default|("NUM5_TO_99999_RE") s)$");
2827 config.inactivity_probe = isdigit(out[0]) ? atoi(out) : -1;
2831 case MENU_MAX_BACKOFF:
2832 in = (config.max_backoff < 0
2833 ? xstrdup("Default")
2834 : xasprintf("%d s", config.max_backoff));
2835 out = prompt("Max backoff:", in,
2836 "^(Default|("NUM1_TO_99999_RE") s)$");
2838 config.max_backoff = isdigit(out[0]) ? atoi(out) : -1;
2844 if (yesno("Save\nChanges?", false)) {
2849 netdevs = svec_join(&config.netdevs, " ", "");
2850 svec_add_nocopy(&set, xasprintf("NETDEVS=%s", netdevs));
2853 config.mode == IN_BAND ? "MODE=in-band" : "MODE=discovery");
2854 if (config.mode == IN_BAND) {
2855 if (config.switch_ip == htonl(0)) {
2856 svec_add(&set, "SWITCH_IP=dhcp");
2858 svec_add_nocopy(&set, xasprintf("SWITCH_IP="IP_FMT,
2859 IP_ARGS(&config.switch_ip)));
2860 svec_add_nocopy(&set,
2861 xasprintf("SWITCH_NETMASK="IP_FMT,
2862 IP_ARGS(&config.switch_mask)));
2863 svec_add_nocopy(&set, xasprintf("SWITCH_GATEWAY="IP_FMT,
2864 IP_ARGS(&config.switch_gw)));
2865 svec_add_nocopy(&set, xasprintf("CONTROLLER=%s",
2866 config.controller_vconn));
2869 svec_add(&set, (config.disconnected == FAIL_DROP
2870 ? "DISCONNECTED_MODE=drop"
2871 : "DISCONNECTED_MODE=switch"));
2872 svec_add_nocopy(&set, xasprintf("STP=%s", config.stp ? "yes" : "no"));
2873 if (config.rate_limit < 0) {
2874 svec_add(&set, "RATE_LIMIT=");
2876 svec_add_nocopy(&set,
2877 xasprintf("RATE_LIMIT=%d", config.rate_limit));
2879 if (config.inactivity_probe < 0) {
2880 svec_add(&set, "INACTIVITY_PROBE=");
2882 svec_add_nocopy(&set, xasprintf("INACTIVITY_PROBE=%d",
2883 config.inactivity_probe));
2885 if (config.max_backoff < 0) {
2886 svec_add(&set, "MAX_BACKOFF=");
2888 svec_add_nocopy(&set, xasprintf("MAX_BACKOFF=%d",
2889 config.max_backoff));
2895 svec_destroy(&config.netdevs);
2896 free(config.controller_vconn);
2897 free(config.datapath_id);
2901 cmd_setup_pki(const struct dict *dict UNUSED)
2903 static const char def_privkey_file[]
2904 = "/etc/openflow-switch/of0-privkey.pem";
2905 static const char def_cert_file[] = "/etc/openflow-switch/of0-cert.pem";
2906 static const char def_cacert_file[] = "/etc/openflow-switch/cacert.pem";
2907 struct dict config_dict;
2908 const char *privkey_file, *cert_file, *cacert_file;
2914 if (!load_config(&config_dict)) {
2917 privkey_file = dict_get_string(&config_dict, "PRIVKEY", def_privkey_file);
2918 cert_file = dict_get_string(&config_dict, "CERT", def_cert_file);
2919 cacert_file = dict_get_string(&config_dict, "CACERT", def_cacert_file);
2920 bootstrap = !strcmp(dict_get_string(&config_dict, "CACERT_MODE", "secure"),
2923 has_keys = !stat(privkey_file, &s) && !stat(cert_file, &s);
2925 ? yesno("Generate\nkeys?", true)
2926 : yesno("Generate\nnew keys?", false)) {
2930 privkey_file = def_privkey_file;
2931 cert_file = def_cert_file;
2934 svec_parse_words(&argv, "sh -c 'cd /etc/openflow-switch "
2935 "&& ovs-pki --force req of0"
2936 "&& ovs-pki --force self-sign of0'");
2937 svec_terminate(&argv);
2938 ok = run_and_report_failure(argv.names, "Key gen failed");
2939 svec_destroy(&argv);
2949 if (stat(cacert_file, &s) && errno == ENOENT) {
2950 bootstrap = yesno("Bootstrap\nCA cert?", bootstrap);
2951 } else if (yesno("Replace\nCA cert?", false)) {
2952 unlink(cacert_file);
2957 svec_add_nocopy(&set, xasprintf("PRIVKEY=%s", privkey_file));
2958 svec_add_nocopy(&set, xasprintf("CERT=%s", cert_file));
2959 svec_add_nocopy(&set, xasprintf("CACERT=%s", cacert_file));
2960 svec_add_nocopy(&set, xasprintf("CACERT_MODE=%s",
2961 bootstrap ? "bootstrap" : "secure"));
2967 parse_options(int argc, char *argv[])
2970 OPT_DUMMY = UCHAR_MAX + 1,
2973 static struct option long_options[] = {
2974 {"verbose", optional_argument, 0, 'v'},
2975 {"help", no_argument, 0, 'h'},
2976 {"version", no_argument, 0, 'V'},
2977 DAEMON_LONG_OPTIONS,
2981 char *short_options = long_options_to_short_options(long_options);
2986 c = getopt_long(argc, argv, short_options, long_options, NULL);
2996 OVS_PRINT_VERSION(OFP_VERSION, OFP_VERSION);
2999 VLOG_OPTION_HANDLERS
3000 DAEMON_OPTION_HANDLERS
3009 free(short_options);
3015 printf("%s: OpenFlow switch monitoring user interface\n"
3016 "usage: %s [OPTIONS] SWITCH\n"
3017 "where SWITCH is an active OpenFlow connection method.\n",
3018 program_name, program_name);
3019 vconn_usage(true, false, false);
3020 printf("\nOptions:\n"
3021 " -v, --verbose=MODULE:FACILITY:LEVEL configure logging levels\n"
3022 " -v, --verbose set maximum verbosity level\n"
3023 " -h, --help display this help message\n"
3024 " -V, --version display version information\n");