From: Ben Pfaff Date: Wed, 6 Jan 2010 22:35:20 +0000 (-0800) Subject: vconn: Reimplement in terms of the "stream" abstraction. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fe55ad159d8fd396a9e4914a03eea93d096d03b1;p=openvswitch vconn: Reimplement in terms of the "stream" abstraction. This reduces the amount of redundancy in the source tree, by making all of the current implementations of a vconn simply delegate to the "stream" abstraction. --- diff --git a/lib/automake.mk b/lib/automake.mk index c8b07c96..0a6beae9 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -144,11 +144,7 @@ lib_libopenvswitch_a_SOURCES = \ lib/uuid.h \ lib/valgrind.h \ lib/vconn-provider.h \ - lib/vconn-ssl.h \ lib/vconn-stream.c \ - lib/vconn-stream.h \ - lib/vconn-tcp.c \ - lib/vconn-unix.c \ lib/vconn.c \ lib/vconn.h \ lib/vlog-modules.def \ @@ -168,9 +164,7 @@ lib_libopenvswitch_a_SOURCES += \ endif if HAVE_OPENSSL -lib_libopenvswitch_a_SOURCES += \ - lib/stream-ssl.c \ - lib/vconn-ssl.c +lib_libopenvswitch_a_SOURCES += lib/stream-ssl.c nodist_lib_libopenvswitch_a_SOURCES += lib/dhparams.c lib/dhparams.c: lib/dh1024.pem lib/dh2048.pem lib/dh4096.pem (echo '#include "lib/dhparams.h"' && \ diff --git a/lib/vconn-ssl.c b/lib/vconn-ssl.c deleted file mode 100644 index 773090de..00000000 --- a/lib/vconn-ssl.c +++ /dev/null @@ -1,1121 +0,0 @@ -/* - * Copyright (c) 2008, 2009 Nicira Networks. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "vconn-ssl.h" -#include "dhparams.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "dynamic-string.h" -#include "leak-checker.h" -#include "ofpbuf.h" -#include "openflow/openflow.h" -#include "packets.h" -#include "poll-loop.h" -#include "socket-util.h" -#include "socket-util.h" -#include "util.h" -#include "vconn-provider.h" -#include "vconn.h" - -#include "vlog.h" -#define THIS_MODULE VLM_vconn_ssl - -/* Active SSL. */ - -enum ssl_state { - STATE_TCP_CONNECTING, - STATE_SSL_CONNECTING -}; - -enum session_type { - CLIENT, - SERVER -}; - -struct ssl_vconn -{ - struct vconn vconn; - enum ssl_state state; - int connect_error; - enum session_type type; - int fd; - SSL *ssl; - struct ofpbuf *rxbuf; - struct ofpbuf *txbuf; - - /* rx_want and tx_want record the result of the last call to SSL_read() - * and SSL_write(), respectively: - * - * - If the call reported that data needed to be read from the file - * descriptor, the corresponding member is set to SSL_READING. - * - * - If the call reported that data needed to be written to the file - * descriptor, the corresponding member is set to SSL_WRITING. - * - * - Otherwise, the member is set to SSL_NOTHING, indicating that the - * call completed successfully (or with an error) and that there is no - * need to block. - * - * These are needed because there is no way to ask OpenSSL what a data read - * or write would require without giving it a buffer to receive into or - * data to send, respectively. (Note that the SSL_want() status is - * overwritten by each SSL_read() or SSL_write() call, so we can't rely on - * its value.) - * - * A single call to SSL_read() or SSL_write() can perform both reading - * and writing and thus invalidate not one of these values but actually - * both. Consider this situation, for example: - * - * - SSL_write() blocks on a read, so tx_want gets SSL_READING. - * - * - SSL_read() laters succeeds reading from 'fd' and clears out the - * whole receive buffer, so rx_want gets SSL_READING. - * - * - Client calls vconn_wait(WAIT_RECV) and vconn_wait(WAIT_SEND) and - * blocks. - * - * - Now we're stuck blocking until the peer sends us data, even though - * SSL_write() could now succeed, which could easily be a deadlock - * condition. - * - * On the other hand, we can't reset both tx_want and rx_want on every call - * to SSL_read() or SSL_write(), because that would produce livelock, - * e.g. in this situation: - * - * - SSL_write() blocks, so tx_want gets SSL_READING or SSL_WRITING. - * - * - SSL_read() blocks, so rx_want gets SSL_READING or SSL_WRITING, - * but tx_want gets reset to SSL_NOTHING. - * - * - Client calls vconn_wait(WAIT_RECV) and vconn_wait(WAIT_SEND) and - * blocks. - * - * - Client wakes up immediately since SSL_NOTHING in tx_want indicates - * that no blocking is necessary. - * - * The solution we adopt here is to set tx_want to SSL_NOTHING after - * calling SSL_read() only if the SSL state of the connection changed, - * which indicates that an SSL-level renegotiation made some progress, and - * similarly for rx_want and SSL_write(). This prevents both the - * deadlock and livelock situations above. - */ - int rx_want, tx_want; -}; - -/* SSL context created by ssl_init(). */ -static SSL_CTX *ctx; - -/* Required configuration. */ -static bool has_private_key, has_certificate, has_ca_cert; - -/* Ordinarily, we require a CA certificate for the peer to be locally - * available. 'has_ca_cert' is true when this is the case, and neither of the - * following variables matter. - * - * We can, however, bootstrap the CA certificate from the peer at the beginning - * of our first connection then use that certificate on all subsequent - * connections, saving it to a file for use in future runs also. In this case, - * 'has_ca_cert' is false, 'bootstrap_ca_cert' is true, and 'ca_cert_file' - * names the file to be saved. */ -static bool bootstrap_ca_cert; -static char *ca_cert_file; - -/* Who knows what can trigger various SSL errors, so let's throttle them down - * quite a bit. */ -static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25); - -static int ssl_init(void); -static int do_ssl_init(void); -static bool ssl_wants_io(int ssl_error); -static void ssl_close(struct vconn *); -static void ssl_clear_txbuf(struct ssl_vconn *); -static int interpret_ssl_error(const char *function, int ret, int error, - int *want); -static DH *tmp_dh_callback(SSL *ssl, int is_export UNUSED, int keylength); -static void log_ca_cert(const char *file_name, X509 *cert); - -static short int -want_to_poll_events(int want) -{ - switch (want) { - case SSL_NOTHING: - NOT_REACHED(); - - case SSL_READING: - return POLLIN; - - case SSL_WRITING: - return POLLOUT; - - default: - NOT_REACHED(); - } -} - -static int -new_ssl_vconn(const char *name, int fd, enum session_type type, - enum ssl_state state, const struct sockaddr_in *remote, - struct vconn **vconnp) -{ - struct sockaddr_in local; - socklen_t local_len = sizeof local; - struct ssl_vconn *sslv; - SSL *ssl = NULL; - int on = 1; - int retval; - - /* Check for all the needful configuration. */ - retval = 0; - if (!has_private_key) { - VLOG_ERR("Private key must be configured to use SSL"); - retval = ENOPROTOOPT; - } - if (!has_certificate) { - VLOG_ERR("Certificate must be configured to use SSL"); - retval = ENOPROTOOPT; - } - if (!has_ca_cert && !bootstrap_ca_cert) { - VLOG_ERR("CA certificate must be configured to use SSL"); - retval = ENOPROTOOPT; - } - if (!SSL_CTX_check_private_key(ctx)) { - VLOG_ERR("Private key does not match certificate public key: %s", - ERR_error_string(ERR_get_error(), NULL)); - retval = ENOPROTOOPT; - } - if (retval) { - goto error; - } - - /* Get the local IP and port information */ - retval = getsockname(fd, (struct sockaddr *) &local, &local_len); - if (retval) { - memset(&local, 0, sizeof local); - } - - /* Disable Nagle. */ - retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof on); - if (retval) { - VLOG_ERR("%s: setsockopt(TCP_NODELAY): %s", name, strerror(errno)); - retval = errno; - goto error; - } - - /* Create and configure OpenSSL stream. */ - ssl = SSL_new(ctx); - if (ssl == NULL) { - VLOG_ERR("SSL_new: %s", ERR_error_string(ERR_get_error(), NULL)); - retval = ENOPROTOOPT; - goto error; - } - if (SSL_set_fd(ssl, fd) == 0) { - VLOG_ERR("SSL_set_fd: %s", ERR_error_string(ERR_get_error(), NULL)); - retval = ENOPROTOOPT; - goto error; - } - if (bootstrap_ca_cert && type == CLIENT) { - SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); - } - - /* Create and return the ssl_vconn. */ - sslv = xmalloc(sizeof *sslv); - vconn_init(&sslv->vconn, &ssl_vconn_class, EAGAIN, name); - vconn_set_remote_ip(&sslv->vconn, remote->sin_addr.s_addr); - vconn_set_remote_port(&sslv->vconn, remote->sin_port); - vconn_set_local_ip(&sslv->vconn, local.sin_addr.s_addr); - vconn_set_local_port(&sslv->vconn, local.sin_port); - sslv->state = state; - sslv->type = type; - sslv->fd = fd; - sslv->ssl = ssl; - sslv->rxbuf = NULL; - sslv->txbuf = NULL; - sslv->rx_want = sslv->tx_want = SSL_NOTHING; - *vconnp = &sslv->vconn; - return 0; - -error: - if (ssl) { - SSL_free(ssl); - } - close(fd); - return retval; -} - -static struct ssl_vconn * -ssl_vconn_cast(struct vconn *vconn) -{ - vconn_assert_class(vconn, &ssl_vconn_class); - return CONTAINER_OF(vconn, struct ssl_vconn, vconn); -} - -static int -ssl_open(const char *name, char *suffix, struct vconn **vconnp) -{ - struct sockaddr_in sin; - int error, fd; - - error = ssl_init(); - if (error) { - return error; - } - - error = inet_open_active(SOCK_STREAM, suffix, OFP_SSL_PORT, &sin, &fd); - if (fd >= 0) { - int state = error ? STATE_TCP_CONNECTING : STATE_SSL_CONNECTING; - return new_ssl_vconn(name, fd, CLIENT, state, &sin, vconnp); - } else { - VLOG_ERR("%s: connect: %s", name, strerror(error)); - return error; - } -} - -static int -do_ca_cert_bootstrap(struct vconn *vconn) -{ - struct ssl_vconn *sslv = ssl_vconn_cast(vconn); - STACK_OF(X509) *chain; - X509 *ca_cert; - FILE *file; - int error; - int fd; - - chain = SSL_get_peer_cert_chain(sslv->ssl); - if (!chain || !sk_X509_num(chain)) { - VLOG_ERR("could not bootstrap CA cert: no certificate presented by " - "peer"); - return EPROTO; - } - ca_cert = sk_X509_value(chain, sk_X509_num(chain) - 1); - - /* Check that 'ca_cert' is self-signed. Otherwise it is not a CA - * certificate and we should not attempt to use it as one. */ - error = X509_check_issued(ca_cert, ca_cert); - if (error) { - VLOG_ERR("could not bootstrap CA cert: obtained certificate is " - "not self-signed (%s)", - X509_verify_cert_error_string(error)); - if (sk_X509_num(chain) < 2) { - VLOG_ERR("only one certificate was received, so probably the peer " - "is not configured to send its CA certificate"); - } - return EPROTO; - } - - fd = open(ca_cert_file, O_CREAT | O_EXCL | O_WRONLY, 0444); - if (fd < 0) { - VLOG_ERR("could not bootstrap CA cert: creating %s failed: %s", - ca_cert_file, strerror(errno)); - return errno; - } - - file = fdopen(fd, "w"); - if (!file) { - int error = errno; - VLOG_ERR("could not bootstrap CA cert: fdopen failed: %s", - strerror(error)); - unlink(ca_cert_file); - return error; - } - - if (!PEM_write_X509(file, ca_cert)) { - VLOG_ERR("could not bootstrap CA cert: PEM_write_X509 to %s failed: " - "%s", ca_cert_file, ERR_error_string(ERR_get_error(), NULL)); - fclose(file); - unlink(ca_cert_file); - return EIO; - } - - if (fclose(file)) { - int error = errno; - VLOG_ERR("could not bootstrap CA cert: writing %s failed: %s", - ca_cert_file, strerror(error)); - unlink(ca_cert_file); - return error; - } - - VLOG_INFO("successfully bootstrapped CA cert to %s", ca_cert_file); - log_ca_cert(ca_cert_file, ca_cert); - bootstrap_ca_cert = false; - has_ca_cert = true; - - /* SSL_CTX_add_client_CA makes a copy of ca_cert's relevant data. */ - SSL_CTX_add_client_CA(ctx, ca_cert); - - /* SSL_CTX_use_certificate() takes ownership of the certificate passed in. - * 'ca_cert' is owned by sslv->ssl, so we need to duplicate it. */ - ca_cert = X509_dup(ca_cert); - if (!ca_cert) { - out_of_memory(); - } - if (SSL_CTX_load_verify_locations(ctx, ca_cert_file, NULL) != 1) { - VLOG_ERR("SSL_CTX_load_verify_locations: %s", - ERR_error_string(ERR_get_error(), NULL)); - return EPROTO; - } - VLOG_INFO("killing successful connection to retry using CA cert"); - return EPROTO; -} - -static int -ssl_connect(struct vconn *vconn) -{ - struct ssl_vconn *sslv = ssl_vconn_cast(vconn); - int retval; - - switch (sslv->state) { - case STATE_TCP_CONNECTING: - retval = check_connection_completion(sslv->fd); - if (retval) { - return retval; - } - sslv->state = STATE_SSL_CONNECTING; - /* Fall through. */ - - case STATE_SSL_CONNECTING: - retval = (sslv->type == CLIENT - ? SSL_connect(sslv->ssl) : SSL_accept(sslv->ssl)); - if (retval != 1) { - int error = SSL_get_error(sslv->ssl, retval); - if (retval < 0 && ssl_wants_io(error)) { - return EAGAIN; - } else { - int unused; - interpret_ssl_error((sslv->type == CLIENT ? "SSL_connect" - : "SSL_accept"), retval, error, &unused); - shutdown(sslv->fd, SHUT_RDWR); - return EPROTO; - } - } else if (bootstrap_ca_cert) { - return do_ca_cert_bootstrap(vconn); - } else if ((SSL_get_verify_mode(sslv->ssl) - & (SSL_VERIFY_NONE | SSL_VERIFY_PEER)) - != SSL_VERIFY_PEER) { - /* Two or more SSL connections completed at the same time while we - * were in bootstrap mode. Only one of these can finish the - * bootstrap successfully. The other one(s) must be rejected - * because they were not verified against the bootstrapped CA - * certificate. (Alternatively we could verify them against the CA - * certificate, but that's more trouble than it's worth. These - * connections will succeed the next time they retry, assuming that - * they have a certificate against the correct CA.) */ - VLOG_ERR("rejecting SSL connection during bootstrap race window"); - return EPROTO; - } else { - return 0; - } - } - - NOT_REACHED(); -} - -static void -ssl_close(struct vconn *vconn) -{ - struct ssl_vconn *sslv = ssl_vconn_cast(vconn); - ssl_clear_txbuf(sslv); - ofpbuf_delete(sslv->rxbuf); - SSL_free(sslv->ssl); - close(sslv->fd); - free(sslv); -} - -static int -interpret_ssl_error(const char *function, int ret, int error, - int *want) -{ - *want = SSL_NOTHING; - - switch (error) { - case SSL_ERROR_NONE: - VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_NONE", function); - break; - - case SSL_ERROR_ZERO_RETURN: - VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_ZERO_RETURN", function); - break; - - case SSL_ERROR_WANT_READ: - *want = SSL_READING; - return EAGAIN; - - case SSL_ERROR_WANT_WRITE: - *want = SSL_WRITING; - return EAGAIN; - - case SSL_ERROR_WANT_CONNECT: - VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_WANT_CONNECT", function); - break; - - case SSL_ERROR_WANT_ACCEPT: - VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_WANT_ACCEPT", function); - break; - - case SSL_ERROR_WANT_X509_LOOKUP: - VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_WANT_X509_LOOKUP", - function); - break; - - case SSL_ERROR_SYSCALL: { - int queued_error = ERR_get_error(); - if (queued_error == 0) { - if (ret < 0) { - int status = errno; - VLOG_WARN_RL(&rl, "%s: system error (%s)", - function, strerror(status)); - return status; - } else { - VLOG_WARN_RL(&rl, "%s: unexpected SSL connection close", - function); - return EPROTO; - } - } else { - VLOG_WARN_RL(&rl, "%s: %s", - function, ERR_error_string(queued_error, NULL)); - break; - } - } - - case SSL_ERROR_SSL: { - int queued_error = ERR_get_error(); - if (queued_error != 0) { - VLOG_WARN_RL(&rl, "%s: %s", - function, ERR_error_string(queued_error, NULL)); - } else { - VLOG_ERR_RL(&rl, "%s: SSL_ERROR_SSL without queued error", - function); - } - break; - } - - default: - VLOG_ERR_RL(&rl, "%s: bad SSL error code %d", function, error); - break; - } - return EIO; -} - -static int -ssl_recv(struct vconn *vconn, struct ofpbuf **bufferp) -{ - struct ssl_vconn *sslv = ssl_vconn_cast(vconn); - struct ofpbuf *rx; - size_t want_bytes; - int old_state; - ssize_t ret; - - if (sslv->rxbuf == NULL) { - sslv->rxbuf = ofpbuf_new(1564); - } - rx = sslv->rxbuf; - -again: - if (sizeof(struct ofp_header) > rx->size) { - want_bytes = sizeof(struct ofp_header) - rx->size; - } else { - struct ofp_header *oh = rx->data; - size_t length = ntohs(oh->length); - if (length < sizeof(struct ofp_header)) { - VLOG_ERR_RL(&rl, "received too-short ofp_header (%zu bytes)", - length); - return EPROTO; - } - want_bytes = length - rx->size; - if (!want_bytes) { - *bufferp = rx; - sslv->rxbuf = NULL; - return 0; - } - } - ofpbuf_prealloc_tailroom(rx, want_bytes); - - /* Behavior of zero-byte SSL_read is poorly defined. */ - assert(want_bytes > 0); - - old_state = SSL_get_state(sslv->ssl); - ret = SSL_read(sslv->ssl, ofpbuf_tail(rx), want_bytes); - if (old_state != SSL_get_state(sslv->ssl)) { - sslv->tx_want = SSL_NOTHING; - } - sslv->rx_want = SSL_NOTHING; - - if (ret > 0) { - rx->size += ret; - if (ret == want_bytes) { - if (rx->size > sizeof(struct ofp_header)) { - *bufferp = rx; - sslv->rxbuf = NULL; - return 0; - } else { - goto again; - } - } - return EAGAIN; - } else { - int error = SSL_get_error(sslv->ssl, ret); - if (error == SSL_ERROR_ZERO_RETURN) { - /* Connection closed (EOF). */ - if (rx->size) { - VLOG_WARN_RL(&rl, "SSL_read: unexpected connection close"); - return EPROTO; - } else { - return EOF; - } - } else { - return interpret_ssl_error("SSL_read", ret, error, &sslv->rx_want); - } - } -} - -static void -ssl_clear_txbuf(struct ssl_vconn *sslv) -{ - ofpbuf_delete(sslv->txbuf); - sslv->txbuf = NULL; -} - -static int -ssl_do_tx(struct vconn *vconn) -{ - struct ssl_vconn *sslv = ssl_vconn_cast(vconn); - - for (;;) { - int old_state = SSL_get_state(sslv->ssl); - int ret = SSL_write(sslv->ssl, sslv->txbuf->data, sslv->txbuf->size); - if (old_state != SSL_get_state(sslv->ssl)) { - sslv->rx_want = SSL_NOTHING; - } - sslv->tx_want = SSL_NOTHING; - if (ret > 0) { - ofpbuf_pull(sslv->txbuf, ret); - if (sslv->txbuf->size == 0) { - return 0; - } - } else { - int ssl_error = SSL_get_error(sslv->ssl, ret); - if (ssl_error == SSL_ERROR_ZERO_RETURN) { - VLOG_WARN_RL(&rl, "SSL_write: connection closed"); - return EPIPE; - } else { - return interpret_ssl_error("SSL_write", ret, ssl_error, - &sslv->tx_want); - } - } - } -} - -static int -ssl_send(struct vconn *vconn, struct ofpbuf *buffer) -{ - struct ssl_vconn *sslv = ssl_vconn_cast(vconn); - - if (sslv->txbuf) { - return EAGAIN; - } else { - int error; - - sslv->txbuf = buffer; - error = ssl_do_tx(vconn); - switch (error) { - case 0: - ssl_clear_txbuf(sslv); - return 0; - case EAGAIN: - leak_checker_claim(buffer); - return 0; - default: - sslv->txbuf = NULL; - return error; - } - } -} - -static void -ssl_run(struct vconn *vconn) -{ - struct ssl_vconn *sslv = ssl_vconn_cast(vconn); - - if (sslv->txbuf && ssl_do_tx(vconn) != EAGAIN) { - ssl_clear_txbuf(sslv); - } -} - -static void -ssl_run_wait(struct vconn *vconn) -{ - struct ssl_vconn *sslv = ssl_vconn_cast(vconn); - - if (sslv->tx_want != SSL_NOTHING) { - poll_fd_wait(sslv->fd, want_to_poll_events(sslv->tx_want)); - } -} - -static void -ssl_wait(struct vconn *vconn, enum vconn_wait_type wait) -{ - struct ssl_vconn *sslv = ssl_vconn_cast(vconn); - - switch (wait) { - case WAIT_CONNECT: - if (vconn_connect(vconn) != EAGAIN) { - poll_immediate_wake(); - } else { - switch (sslv->state) { - case STATE_TCP_CONNECTING: - poll_fd_wait(sslv->fd, POLLOUT); - break; - - case STATE_SSL_CONNECTING: - /* ssl_connect() called SSL_accept() or SSL_connect(), which - * set up the status that we test here. */ - poll_fd_wait(sslv->fd, - want_to_poll_events(SSL_want(sslv->ssl))); - break; - - default: - NOT_REACHED(); - } - } - break; - - case WAIT_RECV: - if (sslv->rx_want != SSL_NOTHING) { - poll_fd_wait(sslv->fd, want_to_poll_events(sslv->rx_want)); - } else { - poll_immediate_wake(); - } - break; - - case WAIT_SEND: - if (!sslv->txbuf) { - /* We have room in our tx queue. */ - poll_immediate_wake(); - } else { - /* vconn_run_wait() will do the right thing; don't bother with - * redundancy. */ - } - break; - - default: - NOT_REACHED(); - } -} - -struct vconn_class ssl_vconn_class = { - "ssl", /* name */ - ssl_open, /* open */ - ssl_close, /* close */ - ssl_connect, /* connect */ - ssl_recv, /* recv */ - ssl_send, /* send */ - ssl_run, /* run */ - ssl_run_wait, /* run_wait */ - ssl_wait, /* wait */ -}; - -/* Passive SSL. */ - -struct pssl_pvconn -{ - struct pvconn pvconn; - int fd; -}; - -struct pvconn_class pssl_pvconn_class; - -static struct pssl_pvconn * -pssl_pvconn_cast(struct pvconn *pvconn) -{ - pvconn_assert_class(pvconn, &pssl_pvconn_class); - return CONTAINER_OF(pvconn, struct pssl_pvconn, pvconn); -} - -static int -pssl_open(const char *name, char *suffix, struct pvconn **pvconnp) -{ - struct pssl_pvconn *pssl; - int retval; - int fd; - - retval = ssl_init(); - if (retval) { - return retval; - } - - fd = inet_open_passive(SOCK_STREAM, suffix, OFP_SSL_PORT); - if (fd < 0) { - return -fd; - } - - pssl = xmalloc(sizeof *pssl); - pvconn_init(&pssl->pvconn, &pssl_pvconn_class, name); - pssl->fd = fd; - *pvconnp = &pssl->pvconn; - return 0; -} - -static void -pssl_close(struct pvconn *pvconn) -{ - struct pssl_pvconn *pssl = pssl_pvconn_cast(pvconn); - close(pssl->fd); - free(pssl); -} - -static int -pssl_accept(struct pvconn *pvconn, struct vconn **new_vconnp) -{ - struct pssl_pvconn *pssl = pssl_pvconn_cast(pvconn); - struct sockaddr_in sin; - socklen_t sin_len = sizeof sin; - char name[128]; - int new_fd; - int error; - - new_fd = accept(pssl->fd, &sin, &sin_len); - if (new_fd < 0) { - int error = errno; - if (error != EAGAIN) { - VLOG_DBG_RL(&rl, "accept: %s", strerror(error)); - } - return error; - } - - error = set_nonblocking(new_fd); - if (error) { - close(new_fd); - return error; - } - - sprintf(name, "ssl:"IP_FMT, IP_ARGS(&sin.sin_addr)); - if (sin.sin_port != htons(OFP_SSL_PORT)) { - sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin.sin_port)); - } - return new_ssl_vconn(name, new_fd, SERVER, STATE_SSL_CONNECTING, &sin, - new_vconnp); -} - -static void -pssl_wait(struct pvconn *pvconn) -{ - struct pssl_pvconn *pssl = pssl_pvconn_cast(pvconn); - poll_fd_wait(pssl->fd, POLLIN); -} - -struct pvconn_class pssl_pvconn_class = { - "pssl", - pssl_open, - pssl_close, - pssl_accept, - pssl_wait, -}; - -/* - * Returns true if OpenSSL error is WANT_READ or WANT_WRITE, indicating that - * OpenSSL is requesting that we call it back when the socket is ready for read - * or writing, respectively. - */ -static bool -ssl_wants_io(int ssl_error) -{ - return (ssl_error == SSL_ERROR_WANT_WRITE - || ssl_error == SSL_ERROR_WANT_READ); -} - -static int -ssl_init(void) -{ - static int init_status = -1; - if (init_status < 0) { - init_status = do_ssl_init(); - assert(init_status >= 0); - } - return init_status; -} - -static int -do_ssl_init(void) -{ - SSL_METHOD *method; - - SSL_library_init(); - SSL_load_error_strings(); - - method = TLSv1_method(); - if (method == NULL) { - VLOG_ERR("TLSv1_method: %s", ERR_error_string(ERR_get_error(), NULL)); - return ENOPROTOOPT; - } - - ctx = SSL_CTX_new(method); - if (ctx == NULL) { - VLOG_ERR("SSL_CTX_new: %s", ERR_error_string(ERR_get_error(), NULL)); - return ENOPROTOOPT; - } - SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); - SSL_CTX_set_tmp_dh_callback(ctx, tmp_dh_callback); - SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); - SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - NULL); - - return 0; -} - -static DH * -tmp_dh_callback(SSL *ssl UNUSED, int is_export UNUSED, int keylength) -{ - struct dh { - int keylength; - DH *dh; - DH *(*constructor)(void); - }; - - static struct dh dh_table[] = { - {1024, NULL, get_dh1024}, - {2048, NULL, get_dh2048}, - {4096, NULL, get_dh4096}, - }; - - struct dh *dh; - - for (dh = dh_table; dh < &dh_table[ARRAY_SIZE(dh_table)]; dh++) { - if (dh->keylength == keylength) { - if (!dh->dh) { - dh->dh = dh->constructor(); - if (!dh->dh) { - ovs_fatal(ENOMEM, "out of memory constructing " - "Diffie-Hellman parameters"); - } - } - return dh->dh; - } - } - VLOG_ERR_RL(&rl, "no Diffie-Hellman parameters for key length %d", - keylength); - return NULL; -} - -/* Returns true if SSL is at least partially configured. */ -bool -vconn_ssl_is_configured(void) -{ - return has_private_key || has_certificate || has_ca_cert; -} - -void -vconn_ssl_set_private_key_file(const char *file_name) -{ - if (ssl_init()) { - return; - } - if (SSL_CTX_use_PrivateKey_file(ctx, file_name, SSL_FILETYPE_PEM) != 1) { - VLOG_ERR("SSL_use_PrivateKey_file: %s", - ERR_error_string(ERR_get_error(), NULL)); - return; - } - has_private_key = true; -} - -void -vconn_ssl_set_certificate_file(const char *file_name) -{ - if (ssl_init()) { - return; - } - if (SSL_CTX_use_certificate_chain_file(ctx, file_name) != 1) { - VLOG_ERR("SSL_use_certificate_file: %s", - ERR_error_string(ERR_get_error(), NULL)); - return; - } - has_certificate = true; -} - -/* Reads the X509 certificate or certificates in file 'file_name'. On success, - * stores the address of the first element in an array of pointers to - * certificates in '*certs' and the number of certificates in the array in - * '*n_certs', and returns 0. On failure, stores a null pointer in '*certs', 0 - * in '*n_certs', and returns a positive errno value. - * - * The caller is responsible for freeing '*certs'. */ -static int -read_cert_file(const char *file_name, X509 ***certs, size_t *n_certs) -{ - FILE *file; - size_t allocated_certs = 0; - - *certs = NULL; - *n_certs = 0; - - file = fopen(file_name, "r"); - if (!file) { - VLOG_ERR("failed to open %s for reading: %s", - file_name, strerror(errno)); - return errno; - } - - for (;;) { - X509 *certificate; - int c; - - /* Read certificate from file. */ - certificate = PEM_read_X509(file, NULL, NULL, NULL); - if (!certificate) { - size_t i; - - VLOG_ERR("PEM_read_X509 failed reading %s: %s", - file_name, ERR_error_string(ERR_get_error(), NULL)); - for (i = 0; i < *n_certs; i++) { - X509_free((*certs)[i]); - } - free(*certs); - *certs = NULL; - *n_certs = 0; - return EIO; - } - - /* Add certificate to array. */ - if (*n_certs >= allocated_certs) { - *certs = x2nrealloc(*certs, &allocated_certs, sizeof **certs); - } - (*certs)[(*n_certs)++] = certificate; - - /* Are there additional certificates in the file? */ - do { - c = getc(file); - } while (isspace(c)); - if (c == EOF) { - break; - } - ungetc(c, file); - } - fclose(file); - return 0; -} - - -/* Sets 'file_name' as the name of a file containing one or more X509 - * certificates to send to the peer. Typical use in OpenFlow is to send the CA - * certificate to the peer, which enables a switch to pick up the controller's - * CA certificate on its first connection. */ -void -vconn_ssl_set_peer_ca_cert_file(const char *file_name) -{ - X509 **certs; - size_t n_certs; - size_t i; - - if (ssl_init()) { - return; - } - - if (!read_cert_file(file_name, &certs, &n_certs)) { - for (i = 0; i < n_certs; i++) { - if (SSL_CTX_add_extra_chain_cert(ctx, certs[i]) != 1) { - VLOG_ERR("SSL_CTX_add_extra_chain_cert: %s", - ERR_error_string(ERR_get_error(), NULL)); - } - } - free(certs); - } -} - -/* Logs fingerprint of CA certificate 'cert' obtained from 'file_name'. */ -static void -log_ca_cert(const char *file_name, X509 *cert) -{ - unsigned char digest[EVP_MAX_MD_SIZE]; - unsigned int n_bytes; - struct ds fp; - char *subject; - - ds_init(&fp); - if (!X509_digest(cert, EVP_sha1(), digest, &n_bytes)) { - ds_put_cstr(&fp, ""); - } else { - unsigned int i; - for (i = 0; i < n_bytes; i++) { - if (i) { - ds_put_char(&fp, ':'); - } - ds_put_format(&fp, "%02hhx", digest[i]); - } - } - subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); - VLOG_INFO("Trusting CA cert from %s (%s) (fingerprint %s)", file_name, - subject ? subject : "", ds_cstr(&fp)); - free(subject); - ds_destroy(&fp); -} - -/* Sets 'file_name' as the name of the file from which to read the CA - * certificate used to verify the peer within SSL connections. If 'bootstrap' - * is false, the file must exist. If 'bootstrap' is false, then the file is - * read if it is exists; if it does not, then it will be created from the CA - * certificate received from the peer on the first SSL connection. */ -void -vconn_ssl_set_ca_cert_file(const char *file_name, bool bootstrap) -{ - X509 **certs; - size_t n_certs; - struct stat s; - - if (ssl_init()) { - return; - } - - if (bootstrap && stat(file_name, &s) && errno == ENOENT) { - bootstrap_ca_cert = true; - ca_cert_file = xstrdup(file_name); - } else if (!read_cert_file(file_name, &certs, &n_certs)) { - size_t i; - - /* Set up list of CAs that the server will accept from the client. */ - for (i = 0; i < n_certs; i++) { - /* SSL_CTX_add_client_CA makes a copy of the relevant data. */ - if (SSL_CTX_add_client_CA(ctx, certs[i]) != 1) { - VLOG_ERR("failed to add client certificate %d from %s: %s", - i, file_name, - ERR_error_string(ERR_get_error(), NULL)); - } else { - log_ca_cert(file_name, certs[i]); - } - X509_free(certs[i]); - } - - /* Set up CAs for OpenSSL to trust in verifying the peer's - * certificate. */ - if (SSL_CTX_load_verify_locations(ctx, file_name, NULL) != 1) { - VLOG_ERR("SSL_CTX_load_verify_locations: %s", - ERR_error_string(ERR_get_error(), NULL)); - return; - } - - has_ca_cert = true; - } -} diff --git a/lib/vconn-ssl.h b/lib/vconn-ssl.h deleted file mode 100644 index 551e2ebc..00000000 --- a/lib/vconn-ssl.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2008 Nicira Networks. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef VCONN_SSL_H -#define VCONN_SSL_H 1 - -#include - -#ifdef HAVE_OPENSSL -bool vconn_ssl_is_configured(void); -void vconn_ssl_set_private_key_file(const char *file_name); -void vconn_ssl_set_certificate_file(const char *file_name); -void vconn_ssl_set_ca_cert_file(const char *file_name, bool bootstrap); -void vconn_ssl_set_peer_ca_cert_file(const char *file_name); - -#define VCONN_SSL_LONG_OPTIONS \ - {"private-key", required_argument, 0, 'p'}, \ - {"certificate", required_argument, 0, 'c'}, \ - {"ca-cert", required_argument, 0, 'C'}, - -#define VCONN_SSL_OPTION_HANDLERS \ - case 'p': \ - vconn_ssl_set_private_key_file(optarg); \ - break; \ - \ - case 'c': \ - vconn_ssl_set_certificate_file(optarg); \ - break; \ - \ - case 'C': \ - vconn_ssl_set_ca_cert_file(optarg, false); \ - break; -#else /* !HAVE_OPENSSL */ -static inline bool vconn_ssl_is_configured(void) -{ - return false; -} -#define VCONN_SSL_LONG_OPTIONS -#define VCONN_SSL_OPTION_HANDLERS -#endif /* !HAVE_OPENSSL */ - -#endif /* vconn-ssl.h */ diff --git a/lib/vconn-stream.c b/lib/vconn-stream.c index f19f3ebf..594eded5 100644 --- a/lib/vconn-stream.c +++ b/lib/vconn-stream.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 2010 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ #include -#include "vconn-stream.h" #include #include #include @@ -29,6 +28,7 @@ #include "openflow/openflow.h" #include "poll-loop.h" #include "socket-util.h" +#include "stream.h" #include "util.h" #include "vconn-provider.h" #include "vconn.h" @@ -38,76 +38,92 @@ /* Active stream socket vconn. */ -struct stream_vconn +struct vconn_stream { struct vconn vconn; - int fd; + struct stream *stream; struct ofpbuf *rxbuf; struct ofpbuf *txbuf; - char *unlink_path; }; static struct vconn_class stream_vconn_class; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25); -static void stream_clear_txbuf(struct stream_vconn *); -static void maybe_unlink_and_free(char *path); +static void vconn_stream_clear_txbuf(struct vconn_stream *); +static int count_fields(const char *); -/* Creates a new vconn named 'name' that will send and receive data on 'fd' and - * stores a pointer to the vconn in '*vconnp'. Initial connection status - * 'connect_status' is interpreted as described for vconn_init(). - * - * When '*vconnp' is closed, then 'unlink_path' (if nonnull) will be passed to - * fatal_signal_unlink_file_now() and then freed with free(). - * - * Returns 0 if successful, otherwise a positive errno value. (The current - * implementation never fails.) */ -int -new_stream_vconn(const char *name, int fd, int connect_status, - char *unlink_path, struct vconn **vconnp) +static struct vconn * +vconn_stream_new(struct stream *stream, int connect_status) { - struct stream_vconn *s; + struct vconn_stream *s; s = xmalloc(sizeof *s); - vconn_init(&s->vconn, &stream_vconn_class, connect_status, name); - s->fd = fd; + vconn_init(&s->vconn, &stream_vconn_class, connect_status, + stream_get_name(stream)); + s->stream = stream; s->txbuf = NULL; s->rxbuf = NULL; - s->unlink_path = unlink_path; - *vconnp = &s->vconn; + return &s->vconn; +} + +/* Creates a new vconn that will send and receive data on a stream named 'name' + * and stores a pointer to the vconn in '*vconnp'. + * + * Returns 0 if successful, otherwise a positive errno value. */ +static int +vconn_stream_open(const char *name_, char *suffix UNUSED, + struct vconn **vconnp) +{ + struct stream *stream; + char *name; + int error; + + if (!strncmp(name_, "tcp:", 4) && count_fields(name_) < 3) { + name = xasprintf("%s:%d", name_, OFP_TCP_PORT); + } else if (!strncmp(name_, "ssl:", 4) && count_fields(name_) < 3) { + name = xasprintf("%s:%d", name_, OFP_SSL_PORT); + } else { + name = xstrdup(name_); + } + error = stream_open(name, &stream); + free(name); + + if (error && error != EAGAIN) { + return error; + } + + *vconnp = vconn_stream_new(stream, error); return 0; } -static struct stream_vconn * -stream_vconn_cast(struct vconn *vconn) +static struct vconn_stream * +vconn_stream_cast(struct vconn *vconn) { - vconn_assert_class(vconn, &stream_vconn_class); - return CONTAINER_OF(vconn, struct stream_vconn, vconn); + return CONTAINER_OF(vconn, struct vconn_stream, vconn); } static void -stream_close(struct vconn *vconn) +vconn_stream_close(struct vconn *vconn) { - struct stream_vconn *s = stream_vconn_cast(vconn); - stream_clear_txbuf(s); + struct vconn_stream *s = vconn_stream_cast(vconn); + stream_close(s->stream); + vconn_stream_clear_txbuf(s); ofpbuf_delete(s->rxbuf); - close(s->fd); - maybe_unlink_and_free(s->unlink_path); free(s); } static int -stream_connect(struct vconn *vconn) +vconn_stream_connect(struct vconn *vconn) { - struct stream_vconn *s = stream_vconn_cast(vconn); - return check_connection_completion(s->fd); + struct vconn_stream *s = vconn_stream_cast(vconn); + return stream_connect(s->stream); } static int -stream_recv(struct vconn *vconn, struct ofpbuf **bufferp) +vconn_stream_recv(struct vconn *vconn, struct ofpbuf **bufferp) { - struct stream_vconn *s = stream_vconn_cast(vconn); + struct vconn_stream *s = vconn_stream_cast(vconn); struct ofpbuf *rx; size_t want_bytes; ssize_t retval; @@ -137,7 +153,7 @@ again: } ofpbuf_prealloc_tailroom(rx, want_bytes); - retval = read(s->fd, ofpbuf_tail(rx), want_bytes); + retval = stream_recv(s->stream, ofpbuf_tail(rx), want_bytes); if (retval > 0) { rx->size += retval; if (retval == want_bytes) { @@ -158,32 +174,32 @@ again: return EOF; } } else { - return errno; + return -retval; } } static void -stream_clear_txbuf(struct stream_vconn *s) +vconn_stream_clear_txbuf(struct vconn_stream *s) { ofpbuf_delete(s->txbuf); s->txbuf = NULL; } static int -stream_send(struct vconn *vconn, struct ofpbuf *buffer) +vconn_stream_send(struct vconn *vconn, struct ofpbuf *buffer) { - struct stream_vconn *s = stream_vconn_cast(vconn); + struct vconn_stream *s = vconn_stream_cast(vconn); ssize_t retval; if (s->txbuf) { return EAGAIN; } - retval = write(s->fd, buffer->data, buffer->size); + retval = stream_send(s->stream, buffer->data, buffer->size); if (retval == buffer->size) { ofpbuf_delete(buffer); return 0; - } else if (retval >= 0 || errno == EAGAIN) { + } else if (retval >= 0 || retval == -EAGAIN) { leak_checker_claim(buffer); s->txbuf = buffer; if (retval > 0) { @@ -191,193 +207,211 @@ stream_send(struct vconn *vconn, struct ofpbuf *buffer) } return 0; } else { - return errno; + return -retval; } } static void -stream_run(struct vconn *vconn) +vconn_stream_run(struct vconn *vconn) { - struct stream_vconn *s = stream_vconn_cast(vconn); - ssize_t n; + struct vconn_stream *s = vconn_stream_cast(vconn); + ssize_t retval; if (!s->txbuf) { return; } - n = write(s->fd, s->txbuf->data, s->txbuf->size); - if (n < 0) { - if (errno != EAGAIN) { - VLOG_ERR_RL(&rl, "send: %s", strerror(errno)); - stream_clear_txbuf(s); + retval = stream_send(s->stream, s->txbuf->data, s->txbuf->size); + if (retval < 0) { + if (retval != -EAGAIN) { + VLOG_ERR_RL(&rl, "send: %s", strerror(-retval)); + vconn_stream_clear_txbuf(s); return; } - } else if (n > 0) { - ofpbuf_pull(s->txbuf, n); + } else if (retval > 0) { + ofpbuf_pull(s->txbuf, retval); if (!s->txbuf->size) { - stream_clear_txbuf(s); + vconn_stream_clear_txbuf(s); return; } } } static void -stream_run_wait(struct vconn *vconn) +vconn_stream_run_wait(struct vconn *vconn) { - struct stream_vconn *s = stream_vconn_cast(vconn); + struct vconn_stream *s = vconn_stream_cast(vconn); if (s->txbuf) { - poll_fd_wait(s->fd, POLLOUT); + stream_send_wait(s->stream); } } static void -stream_wait(struct vconn *vconn, enum vconn_wait_type wait) +vconn_stream_wait(struct vconn *vconn, enum vconn_wait_type wait) { - struct stream_vconn *s = stream_vconn_cast(vconn); + struct vconn_stream *s = vconn_stream_cast(vconn); switch (wait) { case WAIT_CONNECT: - poll_fd_wait(s->fd, POLLOUT); + stream_connect_wait(s->stream); break; case WAIT_SEND: if (!s->txbuf) { - poll_fd_wait(s->fd, POLLOUT); + stream_send_wait(s->stream); } else { - /* Nothing to do: need to drain txbuf first. stream_run_wait() - * will arrange to wake up when there room to send data, so there's - * no point in calling poll_fd_wait() redundantly here. */ + /* Nothing to do: need to drain txbuf first. + * vconn_stream_run_wait() will arrange to wake up when there room + * to send data, so there's no point in calling poll_fd_wait() + * redundantly here. */ } break; case WAIT_RECV: - poll_fd_wait(s->fd, POLLIN); + stream_recv_wait(s->stream); break; default: NOT_REACHED(); } } - -static struct vconn_class stream_vconn_class = { - "stream", /* name */ - NULL, /* open */ - stream_close, /* close */ - stream_connect, /* connect */ - stream_recv, /* recv */ - stream_send, /* send */ - stream_run, /* run */ - stream_run_wait, /* run_wait */ - stream_wait, /* wait */ -}; /* Passive stream socket vconn. */ -struct pstream_pvconn +struct pvconn_pstream { struct pvconn pvconn; - int fd; - int (*accept_cb)(int fd, const struct sockaddr *, size_t sa_len, - struct vconn **); - char *unlink_path; + struct pstream *pstream; }; static struct pvconn_class pstream_pvconn_class; -static struct pstream_pvconn * -pstream_pvconn_cast(struct pvconn *pvconn) +static struct pvconn_pstream * +pvconn_pstream_cast(struct pvconn *pvconn) { - pvconn_assert_class(pvconn, &pstream_pvconn_class); - return CONTAINER_OF(pvconn, struct pstream_pvconn, pvconn); + return CONTAINER_OF(pvconn, struct pvconn_pstream, pvconn); } -/* Creates a new pvconn named 'name' that will accept new socket connections on - * 'fd' and stores a pointer to the vconn in '*pvconnp'. - * - * When a connection has been accepted, 'accept_cb' will be called with the new - * socket fd 'fd' and the remote address of the connection 'sa' and 'sa_len'. - * accept_cb must return 0 if the connection is successful, in which case it - * must initialize '*vconnp' to the new vconn, or a positive errno value on - * error. In either case accept_cb takes ownership of the 'fd' passed in. - * - * When '*pvconnp' is closed, then 'unlink_path' (if nonnull) will be passed to - * fatal_signal_unlink_file_now() and freed with free(). +/* Creates a new pvconn named 'name' that will accept new connections using + * pstream_accept() and stores a pointer to the pvconn in '*pvconnp'. * * Returns 0 if successful, otherwise a positive errno value. (The current * implementation never fails.) */ -int -new_pstream_pvconn(const char *name, int fd, - int (*accept_cb)(int fd, const struct sockaddr *sa, - size_t sa_len, struct vconn **vconnp), - char *unlink_path, struct pvconn **pvconnp) +static int +pvconn_pstream_listen(const char *name_, char *suffix UNUSED, + struct pvconn **pvconnp) { - struct pstream_pvconn *ps = xmalloc(sizeof *ps); - pvconn_init(&ps->pvconn, &pstream_pvconn_class, name); - ps->fd = fd; - ps->accept_cb = accept_cb; - ps->unlink_path = unlink_path; + struct pvconn_pstream *ps; + struct pstream *pstream; + char *name; + int error; + + if (!strncmp(name_, "ptcp:", 5) && count_fields(name_) < 2) { + name = xasprintf("%s:%d", name_, OFP_TCP_PORT); + } else if (!strncmp(name_, "pssl:", 5) && count_fields(name_) < 2) { + name = xasprintf("%s:%d", name_, OFP_SSL_PORT); + } else { + name = xstrdup(name_); + } + error = pstream_open(name, &pstream); + free(name); + if (error) { + return error; + } + + ps = xmalloc(sizeof *ps); + pvconn_init(&ps->pvconn, &pstream_pvconn_class, name_); + ps->pstream = pstream; *pvconnp = &ps->pvconn; return 0; } static void -pstream_close(struct pvconn *pvconn) +pvconn_pstream_close(struct pvconn *pvconn) { - struct pstream_pvconn *ps = pstream_pvconn_cast(pvconn); - close(ps->fd); - maybe_unlink_and_free(ps->unlink_path); + struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn); + pstream_close(ps->pstream); free(ps); } static int -pstream_accept(struct pvconn *pvconn, struct vconn **new_vconnp) +pvconn_pstream_accept(struct pvconn *pvconn, struct vconn **new_vconnp) { - struct pstream_pvconn *ps = pstream_pvconn_cast(pvconn); - struct sockaddr_storage ss; - socklen_t ss_len = sizeof ss; - int new_fd; - int retval; - - new_fd = accept(ps->fd, (struct sockaddr *) &ss, &ss_len); - if (new_fd < 0) { - int retval = errno; - if (retval != EAGAIN) { - VLOG_DBG_RL(&rl, "accept: %s", strerror(retval)); + struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn); + struct stream *stream; + int error; + + error = pstream_accept(ps->pstream, &stream); + if (error) { + if (error != EAGAIN) { + VLOG_DBG_RL(&rl, "%s: accept: %s", + pstream_get_name(ps->pstream), strerror(error)); } - return retval; - } - - retval = set_nonblocking(new_fd); - if (retval) { - close(new_fd); - return retval; + return error; } - return ps->accept_cb(new_fd, (const struct sockaddr *) &ss, ss_len, - new_vconnp); + *new_vconnp = vconn_stream_new(stream, 0); + return 0; } static void -pstream_wait(struct pvconn *pvconn) +pvconn_pstream_wait(struct pvconn *pvconn) { - struct pstream_pvconn *ps = pstream_pvconn_cast(pvconn); - poll_fd_wait(ps->fd, POLLIN); + struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn); + pstream_wait(ps->pstream); } - -static struct pvconn_class pstream_pvconn_class = { - "pstream", - NULL, - pstream_close, - pstream_accept, - pstream_wait -}; -/* Helper functions. */ -static void -maybe_unlink_and_free(char *path) +static int +count_fields(const char *s_) { - if (path) { - fatal_signal_unlink_file_now(path); - free(path); + char *s, *field, *save_ptr; + int n = 0; + + save_ptr = NULL; + s = xstrdup(s_); + for (field = strtok_r(s, ":", &save_ptr); field != NULL; + field = strtok_r(NULL, ":", &save_ptr)) { + n++; } + free(s); + + return n; } + +/* Stream-based vconns and pvconns. */ + +#define DEFINE_VCONN_STREAM_CLASS(NAME) \ + struct vconn_class NAME##_vconn_class = { \ + #NAME, \ + vconn_stream_open, \ + vconn_stream_close, \ + vconn_stream_connect, \ + vconn_stream_recv, \ + vconn_stream_send, \ + vconn_stream_run, \ + vconn_stream_run_wait, \ + vconn_stream_wait, \ + }; + +#define DEFINE_PVCONN_STREAM_CLASS(NAME) \ + struct pvconn_class NAME##_pvconn_class = { \ + #NAME, \ + pvconn_pstream_listen, \ + pvconn_pstream_close, \ + pvconn_pstream_accept, \ + pvconn_pstream_wait \ + }; + +static DEFINE_VCONN_STREAM_CLASS(stream); +static DEFINE_PVCONN_STREAM_CLASS(pstream); + +DEFINE_VCONN_STREAM_CLASS(tcp); +DEFINE_PVCONN_STREAM_CLASS(ptcp); + +DEFINE_VCONN_STREAM_CLASS(unix); +DEFINE_PVCONN_STREAM_CLASS(punix); + +#ifdef HAVE_OPENSSL +DEFINE_VCONN_STREAM_CLASS(ssl); +DEFINE_PVCONN_STREAM_CLASS(pssl); +#endif diff --git a/lib/vconn-stream.h b/lib/vconn-stream.h deleted file mode 100644 index 91904fff..00000000 --- a/lib/vconn-stream.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2008, 2009 Nicira Networks. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef VCONN_STREAM_H -#define VCONN_STREAM_H 1 - -#include -#include -#include - -struct vconn; -struct pvconn; -struct sockaddr; - -int new_stream_vconn(const char *name, int fd, int connect_status, - char *unlink_path, struct vconn **vconnp); -int new_pstream_pvconn(const char *name, int fd, - int (*accept_cb)(int fd, const struct sockaddr *, - size_t sa_len, struct vconn **), - char *unlink_path, - struct pvconn **pvconnp); - -#endif /* vconn-stream.h */ diff --git a/lib/vconn-tcp.c b/lib/vconn-tcp.c deleted file mode 100644 index e3a24f58..00000000 --- a/lib/vconn-tcp.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2008, 2009 Nicira Networks. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "vconn.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "packets.h" -#include "socket-util.h" -#include "util.h" -#include "openflow/openflow.h" -#include "vconn-provider.h" -#include "vconn-stream.h" - -#include "vlog.h" -#define THIS_MODULE VLM_vconn_tcp - -/* Active TCP. */ - -static int -new_tcp_vconn(const char *name, int fd, int connect_status, - const struct sockaddr_in *remote, struct vconn **vconnp) -{ - struct sockaddr_in local; - socklen_t local_len = sizeof local; - int on = 1; - int retval; - - /* Get the local IP and port information */ - retval = getsockname(fd, (struct sockaddr *)&local, &local_len); - if (retval) { - memset(&local, 0, sizeof local); - } - - retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof on); - if (retval) { - VLOG_ERR("%s: setsockopt(TCP_NODELAY): %s", name, strerror(errno)); - close(fd); - return errno; - } - - retval = new_stream_vconn(name, fd, connect_status, NULL, vconnp); - if (!retval) { - struct vconn *vconn = *vconnp; - vconn_set_remote_ip(vconn, remote->sin_addr.s_addr); - vconn_set_remote_port(vconn, remote->sin_port); - vconn_set_local_ip(vconn, local.sin_addr.s_addr); - vconn_set_local_port(vconn, local.sin_port); - } - return retval; -} - -static int -tcp_open(const char *name, char *suffix, struct vconn **vconnp) -{ - struct sockaddr_in sin; - int fd, error; - - error = inet_open_active(SOCK_STREAM, suffix, OFP_TCP_PORT, &sin, &fd); - if (fd >= 0) { - return new_tcp_vconn(name, fd, error, &sin, vconnp); - } else { - VLOG_ERR("%s: connect: %s", name, strerror(error)); - return error; - } -} - -struct vconn_class tcp_vconn_class = { - "tcp", /* name */ - tcp_open, /* open */ - NULL, /* close */ - NULL, /* connect */ - NULL, /* recv */ - NULL, /* send */ - NULL, /* run */ - NULL, /* run_wait */ - NULL, /* wait */ -}; - -/* Passive TCP. */ - -static int ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len, - struct vconn **vconnp); - -static int -ptcp_open(const char *name UNUSED, char *suffix, struct pvconn **pvconnp) -{ - int fd; - - fd = inet_open_passive(SOCK_STREAM, suffix, OFP_TCP_PORT); - if (fd < 0) { - return -fd; - } else { - return new_pstream_pvconn("ptcp", fd, ptcp_accept, NULL, pvconnp); - } -} - -static int -ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len, - struct vconn **vconnp) -{ - const struct sockaddr_in *sin = (const struct sockaddr_in *) sa; - char name[128]; - - if (sa_len == sizeof(struct sockaddr_in) && sin->sin_family == AF_INET) { - sprintf(name, "tcp:"IP_FMT, IP_ARGS(&sin->sin_addr)); - if (sin->sin_port != htons(OFP_TCP_PORT)) { - sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin->sin_port)); - } - } else { - strcpy(name, "tcp"); - } - return new_tcp_vconn(name, fd, 0, sin, vconnp); -} - -struct pvconn_class ptcp_pvconn_class = { - "ptcp", - ptcp_open, - NULL, - NULL, - NULL -}; - diff --git a/lib/vconn-unix.c b/lib/vconn-unix.c deleted file mode 100644 index ff01022e..00000000 --- a/lib/vconn-unix.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2008, 2009 Nicira Networks. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "vconn.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ofpbuf.h" -#include "openflow/openflow.h" -#include "packets.h" -#include "poll-loop.h" -#include "socket-util.h" -#include "util.h" -#include "vconn-provider.h" -#include "vconn-stream.h" - -#include "vlog.h" -#define THIS_MODULE VLM_vconn_unix - -/* Active UNIX socket. */ - -/* Number of unix sockets created so far, to ensure binding path uniqueness. */ -static int n_unix_sockets; - -static int -unix_open(const char *name, char *suffix, struct vconn **vconnp) -{ - const char *connect_path = suffix; - char *bind_path; - int fd; - - bind_path = xasprintf("/tmp/vconn-unix.%ld.%d", - (long int) getpid(), n_unix_sockets++); - fd = make_unix_socket(SOCK_STREAM, true, false, bind_path, connect_path); - if (fd < 0) { - VLOG_ERR("%s: connection to %s failed: %s", - bind_path, connect_path, strerror(-fd)); - free(bind_path); - return -fd; - } - - return new_stream_vconn(name, fd, check_connection_completion(fd), - bind_path, vconnp); -} - -struct vconn_class unix_vconn_class = { - "unix", /* name */ - unix_open, /* open */ - NULL, /* close */ - NULL, /* connect */ - NULL, /* recv */ - NULL, /* send */ - NULL, /* run */ - NULL, /* run_wait */ - NULL, /* wait */ -}; - -/* Passive UNIX socket. */ - -static int punix_accept(int fd, const struct sockaddr *sa, size_t sa_len, - struct vconn **vconnp); - -static int -punix_open(const char *name UNUSED, char *suffix, struct pvconn **pvconnp) -{ - int fd, error; - - fd = make_unix_socket(SOCK_STREAM, true, true, suffix, NULL); - if (fd < 0) { - VLOG_ERR("%s: binding failed: %s", suffix, strerror(errno)); - return errno; - } - - if (listen(fd, 10) < 0) { - error = errno; - VLOG_ERR("%s: listen: %s", name, strerror(error)); - close(fd); - return error; - } - - return new_pstream_pvconn("punix", fd, punix_accept, - xstrdup(suffix), pvconnp); -} - -static int -punix_accept(int fd, const struct sockaddr *sa, size_t sa_len, - struct vconn **vconnp) -{ - const struct sockaddr_un *sun = (const struct sockaddr_un *) sa; - int name_len = get_unix_name_len(sa_len); - char name[128]; - - if (name_len > 0) { - snprintf(name, sizeof name, "unix:%.*s", name_len, sun->sun_path); - } else { - strcpy(name, "unix"); - } - return new_stream_vconn(name, fd, 0, NULL, vconnp); -} - -struct pvconn_class punix_pvconn_class = { - "punix", - punix_open, - NULL, - NULL, - NULL -}; - diff --git a/ofproto/discovery.c b/ofproto/discovery.c index c7625066..ce3e7ce5 100644 --- a/ofproto/discovery.c +++ b/ofproto/discovery.c @@ -29,7 +29,7 @@ #include "openflow/openflow.h" #include "packets.h" #include "status.h" -#include "vconn-ssl.h" +#include "stream-ssl.h" #define THIS_MODULE VLM_discovery #include "vlog.h" @@ -169,7 +169,7 @@ discovery_set_accept_controller_re(struct discovery *d, const char *re_) int error; char *re; - re = (!re_ ? xstrdup(vconn_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*") + re = (!re_ ? xstrdup(stream_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*") : re_[0] == '^' ? xstrdup(re_) : xasprintf("^%s", re_)); regex = xmalloc(sizeof *regex); error = regcomp(regex, re, REG_NOSUB | REG_EXTENDED); diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 32757b57..3ab9cc99 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -48,12 +48,12 @@ #include "shash.h" #include "status.h" #include "stp.h" +#include "stream-ssl.h" #include "svec.h" #include "tag.h" #include "timeval.h" #include "unixctl.h" #include "vconn.h" -#include "vconn-ssl.h" #include "xtoxll.h" #define THIS_MODULE VLM_ofproto diff --git a/utilities/ovs-controller.c b/utilities/ovs-controller.c index e2816cfc..0e453f48 100644 --- a/utilities/ovs-controller.c +++ b/utilities/ovs-controller.c @@ -32,10 +32,10 @@ #include "openflow/openflow.h" #include "poll-loop.h" #include "rconn.h" +#include "stream-ssl.h" #include "timeval.h" #include "unixctl.h" #include "util.h" -#include "vconn-ssl.h" #include "vconn.h" #include "vlog.h" @@ -257,7 +257,7 @@ parse_options(int argc, char *argv[]) DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, #ifdef HAVE_OPENSSL - VCONN_SSL_LONG_OPTIONS + STREAM_SSL_LONG_OPTIONS {"peer-ca-cert", required_argument, 0, OPT_PEER_CA_CERT}, #endif {0, 0, 0, 0}, @@ -317,10 +317,10 @@ parse_options(int argc, char *argv[]) DAEMON_OPTION_HANDLERS #ifdef HAVE_OPENSSL - VCONN_SSL_OPTION_HANDLERS + STREAM_SSL_OPTION_HANDLERS case OPT_PEER_CA_CERT: - vconn_ssl_set_peer_ca_cert_file(optarg); + stream_ssl_set_peer_ca_cert_file(optarg); break; #endif diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index ab4e9b55..61ca23e3 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -44,9 +44,9 @@ #include "packets.h" #include "random.h" #include "socket-util.h" +#include "stream-ssl.h" #include "timeval.h" #include "util.h" -#include "vconn-ssl.h" #include "vconn.h" #include "vlog.h" @@ -91,7 +91,7 @@ parse_options(int argc, char *argv[]) {"strict", no_argument, 0, OPT_STRICT}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, - VCONN_SSL_LONG_OPTIONS + STREAM_SSL_LONG_OPTIONS {0, 0, 0, 0}, }; char *short_options = long_options_to_short_options(long_options); @@ -131,7 +131,7 @@ parse_options(int argc, char *argv[]) strict = true; break; - VCONN_SSL_OPTION_HANDLERS + STREAM_SSL_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); diff --git a/utilities/ovs-openflowd.c b/utilities/ovs-openflowd.c index 7c61dbea..8307891a 100644 --- a/utilities/ovs-openflowd.c +++ b/utilities/ovs-openflowd.c @@ -39,11 +39,11 @@ #include "packets.h" #include "poll-loop.h" #include "rconn.h" +#include "stream-ssl.h" #include "svec.h" #include "timeval.h" #include "unixctl.h" #include "util.h" -#include "vconn-ssl.h" #include "vconn.h" #include "vlog.h" @@ -294,7 +294,7 @@ parse_options(int argc, char *argv[], struct ofsettings *s) VLOG_LONG_OPTIONS, LEAK_CHECKER_LONG_OPTIONS, #ifdef HAVE_OPENSSL - VCONN_SSL_LONG_OPTIONS + STREAM_SSL_LONG_OPTIONS {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT}, #endif {0, 0, 0, 0}, @@ -476,10 +476,10 @@ parse_options(int argc, char *argv[], struct ofsettings *s) LEAK_CHECKER_OPTION_HANDLERS #ifdef HAVE_OPENSSL - VCONN_SSL_OPTION_HANDLERS + STREAM_SSL_OPTION_HANDLERS case OPT_BOOTSTRAP_CA_CERT: - vconn_ssl_set_ca_cert_file(optarg, true); + stream_ssl_set_ca_cert_file(optarg, true); break; #endif @@ -506,7 +506,7 @@ parse_options(int argc, char *argv[], struct ofsettings *s) /* Set accept_controller_regex. */ if (!s->accept_controller_re) { s->accept_controller_re - = vconn_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*"; + = stream_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*"; } /* Mode of operation. */ diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 4cf1b86f..ea8161ff 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -52,12 +52,12 @@ #include "sha1.h" #include "shash.h" #include "socket-util.h" +#include "stream-ssl.h" #include "svec.h" #include "timeval.h" #include "util.h" #include "unixctl.h" #include "vconn.h" -#include "vconn-ssl.h" #include "vswitchd/vswitch-idl.h" #include "xenserver.h" #include "xtoxll.h" @@ -366,11 +366,11 @@ bridge_configure_ssl(const struct ovsrec_ssl *ssl) } if (config_string_change(ssl->private_key, &private_key_file)) { - vconn_ssl_set_private_key_file(private_key_file); + stream_ssl_set_private_key_file(private_key_file); } if (config_string_change(ssl->certificate, &certificate_file)) { - vconn_ssl_set_certificate_file(certificate_file); + stream_ssl_set_certificate_file(certificate_file); } /* We assume that even if the filename hasn't changed, if the CA cert @@ -380,7 +380,7 @@ bridge_configure_ssl(const struct ovsrec_ssl *ssl) * restarted. We may want to address this in vconn's SSL library. */ if (config_string_change(ssl->ca_cert, &cacert_file) || (cacert_file && stat(cacert_file, &s) && errno == ENOENT)) { - vconn_ssl_set_ca_cert_file(cacert_file, ssl->bootstrap_ca_cert); + stream_ssl_set_ca_cert_file(cacert_file, ssl->bootstrap_ca_cert); } } #endif diff --git a/vswitchd/ovs-vswitchd.c b/vswitchd/ovs-vswitchd.c index 606285df..1d1fa291 100644 --- a/vswitchd/ovs-vswitchd.c +++ b/vswitchd/ovs-vswitchd.c @@ -36,12 +36,12 @@ #include "proc-net-compat.h" #include "process.h" #include "signals.h" +#include "stream-ssl.h" #include "stream.h" #include "svec.h" #include "timeval.h" #include "unixctl.h" #include "util.h" -#include "vconn-ssl.h" #include "vconn.h" #include "vswitchd/vswitch-idl.h" @@ -148,7 +148,7 @@ parse_options(int argc, char *argv[]) VLOG_LONG_OPTIONS, LEAK_CHECKER_LONG_OPTIONS, #ifdef HAVE_OPENSSL - VCONN_SSL_LONG_OPTIONS + STREAM_SSL_LONG_OPTIONS {"peer-ca-cert", required_argument, 0, OPT_PEER_CA_CERT}, #endif {0, 0, 0, 0}, @@ -183,12 +183,13 @@ parse_options(int argc, char *argv[]) VLOG_OPTION_HANDLERS DAEMON_OPTION_HANDLERS - VCONN_SSL_OPTION_HANDLERS LEAK_CHECKER_OPTION_HANDLERS #ifdef HAVE_OPENSSL + STREAM_SSL_OPTION_HANDLERS + case OPT_PEER_CA_CERT: - vconn_ssl_set_peer_ca_cert_file(optarg); + stream_ssl_set_peer_ca_cert_file(optarg); break; #endif