From ae3f2361b671bbf878fb63ff223ff5b34f2c2326 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Fri, 4 Apr 2008 16:20:28 -0700 Subject: [PATCH] Allow secchan to listen for management connections also. This is needed in the userspace switch code also. That change is implemented in later change "Send replies to OpenFlow requests only to the sender". Really that part of the change should be moved into this one, but intervening changes are intertwined enough with the same code to make that difficult. --- lib/vconn-netlink.c | 8 +- secchan/Makefile.am | 4 + secchan/secchan.c | 234 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 198 insertions(+), 48 deletions(-) diff --git a/lib/vconn-netlink.c b/lib/vconn-netlink.c index 05c7ab07..ca1f93d5 100644 --- a/lib/vconn-netlink.c +++ b/lib/vconn-netlink.c @@ -72,16 +72,18 @@ netlink_open(const char *name, char *suffix, struct vconn **vconnp) { struct netlink_vconn *netlink; int dp_idx; + int subscribe; int retval; - if (sscanf(suffix, "%d", &dp_idx) != 1) { - fatal(0, "%s: bad peer name format", name); + subscribe = 1; + if (sscanf(suffix, "%d:%d", &dp_idx, &subscribe) < 1) { + fatal(0, "%s: syntax error", name); } netlink = xmalloc(sizeof *netlink); netlink->vconn.class = &netlink_vconn_class; netlink->vconn.connect_status = 0; - retval = dpif_open(dp_idx, true, &netlink->dp); + retval = dpif_open(dp_idx, subscribe, &netlink->dp); if (retval) { free(netlink); *vconnp = NULL; diff --git a/secchan/Makefile.am b/secchan/Makefile.am index 13ddb044..882c98e7 100644 --- a/secchan/Makefile.am +++ b/secchan/Makefile.am @@ -1,6 +1,10 @@ include ../Make.vars +if HAVE_NETLINK bin_PROGRAMS = secchan +else +bin_PROGRAMS = +endif secchan_SOURCES = secchan.c secchan_LDADD = ../lib/libopenflow.la -ldl diff --git a/secchan/secchan.c b/secchan/secchan.c index e2486936..72cbd257 100644 --- a/secchan/secchan.c +++ b/secchan/secchan.c @@ -43,12 +43,14 @@ #include "command-line.h" #include "compiler.h" #include "fault.h" +#include "list.h" #include "util.h" #include "rconn.h" #include "vconn-ssl.h" #include "vlog-socket.h" #include "openflow.h" #include "poll-loop.h" +#include "vconn.h" #include "vlog.h" #define THIS_MODULE VLM_secchan @@ -56,19 +58,31 @@ static void parse_options(int argc, char *argv[]); static void usage(void) NO_RETURN; -static bool reliable = true; +static struct vconn *listen_vconn = NULL; + +struct half { + struct rconn *rconn; + struct buffer *rxbuf; +}; + +struct relay { + struct list node; + struct half halves[2]; +}; + +static struct list relays = LIST_INITIALIZER(&relays); + +static void new_management_connection(const char *nl_name, struct vconn *new_remote); +static void relay_create(struct rconn *, struct rconn *); +static void relay_run(struct relay *); +static void relay_wait(struct relay *); +static void relay_destroy(struct relay *); int main(int argc, char *argv[]) { - struct half { - struct rconn *rconn; - struct buffer *rxbuf; - }; - - struct half halves[2]; + const char *nl_name; int retval; - int i; set_program_name(argv[0]); register_fault_handlers(); @@ -76,7 +90,14 @@ main(int argc, char *argv[]) parse_options(argc, argv); if (argc - optind != 2) { - fatal(0, "exactly two peer arguments required; use --help for usage"); + fatal(0, + "need exactly two non-option arguments; use --help for usage"); + } + nl_name = argv[optind]; + if (strncmp(nl_name, "nl:", 3) + || strlen(nl_name) < 4 + || nl_name[strspn(nl_name + 3, "0123456789") + 3]) { + fatal(0, "%s: argument is not of the form \"nl:DP_ID\"", nl_name); } retval = vlog_server_listen(NULL, NULL); @@ -84,63 +105,165 @@ main(int argc, char *argv[]) fatal(retval, "Could not listen for vlog connections"); } - for (i = 0; i < 2; i++) { - halves[i].rconn = rconn_new(argv[optind + i], 1); - halves[i].rxbuf = NULL; - } + relay_create(rconn_new(argv[optind], 1), rconn_new(argv[optind + 1], 1)); for (;;) { - /* Do some work. Limit the number of iterations so that callbacks - * registered with the poll loop don't starve. */ - int iteration; + struct relay *r, *n; - for (i = 0; i < 2; i++) { - rconn_run(halves[i].rconn); + /* Do work. */ + LIST_FOR_EACH_SAFE (r, n, struct relay, node, &relays) { + relay_run(r); } - - for (iteration = 0; iteration < 50; iteration++) { - bool progress = false; - for (i = 0; i < 2; i++) { - struct half *this = &halves[i]; - struct half *peer = &halves[!i]; - - if (!this->rxbuf) { - this->rxbuf = rconn_recv(this->rconn); - } - - if (this->rxbuf) { - retval = rconn_send(peer->rconn, this->rxbuf); + if (listen_vconn) { + struct vconn *new_remote; + for (;;) { + retval = vconn_accept(listen_vconn, &new_remote); + if (retval) { if (retval != EAGAIN) { - this->rxbuf = NULL; - if (!retval) { - progress = true; - } + VLOG_WARN("accept failed (%s)", strerror(retval)); } + break; } - } - if (!progress) { - break; + + new_management_connection(nl_name, new_remote); } } /* Wait for something to happen. */ + LIST_FOR_EACH (r, struct relay, node, &relays) { + relay_wait(r); + } + if (listen_vconn) { + vconn_accept_wait(listen_vconn); + } + poll_block(); + } + + return 0; +} + +static void +new_management_connection(const char *nl_name, struct vconn *new_remote) +{ + char *nl_name_without_subscription; + struct vconn *new_local; + struct rconn *r1, *r2; + int retval; + + /* nl:123 or nl:123:1 opens a netlink connection to local datapath 123. We + * only accept the former syntax in main(). + * + * nl:123:0 opens a netlink connection to local datapath 123 without + * obtaining a subscription for ofp_packet_in or ofp_flow_expired + * messages.*/ + nl_name_without_subscription = xasprintf("%s:0", nl_name); + retval = vconn_open(nl_name_without_subscription, &new_local); + if (retval) { + VLOG_ERR("could not connect to %s (%s)", + nl_name_without_subscription, strerror(retval)); + vconn_close(new_remote); + return; + } + free(nl_name_without_subscription); + + /* Add it to the relay list. */ + r1 = rconn_new_from_vconn(nl_name_without_subscription, 1, new_local); + r2 = rconn_new_from_vconn("passive", 1, new_remote); + relay_create(r1, r2); +} + +static void +relay_create(struct rconn *a, struct rconn *b) +{ + struct relay *r; + int i; + + r = xmalloc(sizeof *r); + for (i = 0; i < 2; i++) { + r->halves[i].rconn = i ? b : a; + r->halves[i].rxbuf = NULL; + } + list_push_back(&relays, &r->node); +} + +static void +relay_run(struct relay *r) +{ + int iteration; + int i; + + for (i = 0; i < 2; i++) { + rconn_run(r->halves[i].rconn); + } + + /* Limit the number of iterations to prevent other tasks from starving. */ + for (iteration = 0; iteration < 50; iteration++) { + bool progress = false; for (i = 0; i < 2; i++) { - struct half *this = &halves[i]; + struct half *this = &r->halves[i]; + struct half *peer = &r->halves[!i]; - rconn_run_wait(this->rconn); if (!this->rxbuf) { - rconn_recv_wait(this->rconn); + this->rxbuf = rconn_recv(this->rconn); + } + + if (this->rxbuf) { + int retval = rconn_send(peer->rconn, this->rxbuf); + if (retval != EAGAIN) { + this->rxbuf = NULL; + if (!retval) { + progress = true; + } + } } } - poll_block(); + if (!progress) { + break; + } } - return 0; + for (i = 0; i < 2; i++) { + struct half *this = &r->halves[i]; + if (!rconn_is_alive(this->rconn)) { + relay_destroy(r); + return; + } + } +} + +static void +relay_wait(struct relay *r) +{ + int i; + + for (i = 0; i < 2; i++) { + struct half *this = &r->halves[i]; + + rconn_run_wait(this->rconn); + if (!this->rxbuf) { + rconn_recv_wait(this->rconn); + } + } +} + +static void +relay_destroy(struct relay *r) +{ + int i; + + list_remove(&r->node); + for (i = 0; i < 2; i++) { + struct half *this = &r->halves[i]; + rconn_destroy(this->rconn); + buffer_delete(this->rxbuf); + } + free(r); } static void parse_options(int argc, char *argv[]) { static struct option long_options[] = { + {"listen", required_argument, 0, 'l'}, {"verbose", optional_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, @@ -154,15 +277,28 @@ parse_options(int argc, char *argv[]) char *short_options = long_options_to_short_options(long_options); for (;;) { - int indexptr; + int retval; int c; - c = getopt_long(argc, argv, short_options, long_options, &indexptr); + c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { + case 'l': + if (listen_vconn) { + fatal(0, "-l or --listen may be only specified once"); + } + retval = vconn_open(optarg, &listen_vconn); + if (retval && retval != EAGAIN) { + fatal(retval, "opening %s", optarg); + } + if (!vconn_is_passive(listen_vconn)) { + fatal(0, "%s is not a passive vconn", optarg); + } + break; + case 'h': usage(); @@ -217,6 +353,14 @@ usage(void) " -c, --certificate=FILE file with certificate for private key\n" " -C, --ca-cert=FILE file with peer CA certificate\n", OFP_SSL_PORT); +#endif + printf("\nNetworking options:\n" + " -l, --listen=VCONN allow management connections on VCONN:\n" + " ptcp:[PORT] TCP PORT (default: %d)\n", + OFP_TCP_PORT); +#ifdef HAVE_OPENSSL + printf(" pssl:[PORT] SSL PORT (default: %d)\n", + OFP_SSL_PORT); #endif printf("\nOther options:\n" " -v, --verbose set maximum verbosity level\n" -- 2.30.2