X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=lib%2Fvconn-tcp.c;h=7fbec081ea9f80734742f9c22cb2bf8b81cff454;hb=f730dded18218f869c3c316b98912ff19d8fb71c;hp=f97761fe1c00187e44e0b3ad3b54741def380c21;hpb=cf6207b610f15e73984e94c6c84ee07730ec746b;p=sliver-openvswitch.git diff --git a/lib/vconn-tcp.c b/lib/vconn-tcp.c index f97761fe1..7fbec081e 100644 --- a/lib/vconn-tcp.c +++ b/lib/vconn-tcp.c @@ -1,27 +1,41 @@ -/* Copyright (C) 2007 Board of Trustees, Leland Stanford Jr. University. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. +/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford + * Junior University + * + * We are making the OpenFlow specification and associated documentation + * (Software) available for public use and benefit with the expectation + * that others will use, modify and enhance the Software and contribute + * those enhancements back to the community. However, since we would + * like to make the Software available for broadest use, with as few + * restrictions as possible permission is hereby granted, free of + * charge, to any person obtaining a copy of this Software to deal in + * the Software under the copyrights without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The name and trademarks of copyright holder(s) may NOT be used in + * advertising or publicity pertaining to the Software or any + * derivatives without specific, written prior permission. */ +#include #include "vconn.h" #include #include +#include #include #include #include @@ -35,6 +49,8 @@ #include "util.h" #include "openflow.h" #include "ofp-print.h" +#include "packets.h" +#include "poll-loop.h" #include "vlog.h" #define THIS_MODULE VLM_vconn_tcp @@ -47,22 +63,17 @@ struct tcp_vconn int fd; struct buffer *rxbuf; struct buffer *txbuf; + struct poll_waiter *tx_waiter; }; static int -new_tcp_vconn(const char *name, int fd, struct vconn **vconnp) +new_tcp_vconn(const char *name, int fd, int connect_status, + const struct sockaddr_in *sin, struct vconn **vconnp) { struct tcp_vconn *tcp; int on = 1; int retval; - retval = set_nonblocking(fd); - if (retval) { - VLOG_ERR("%s: set_nonblocking: %s", name, strerror(retval)); - close(fd); - return retval; - } - retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof on); if (retval) { VLOG_ERR("%s: setsockopt(TCP_NODELAY): %s", name, strerror(errno)); @@ -72,18 +83,21 @@ new_tcp_vconn(const char *name, int fd, struct vconn **vconnp) tcp = xmalloc(sizeof *tcp); tcp->vconn.class = &tcp_vconn_class; + tcp->vconn.connect_status = connect_status; + tcp->vconn.ip = sin->sin_addr.s_addr; tcp->fd = fd; tcp->txbuf = NULL; + tcp->tx_waiter = NULL; tcp->rxbuf = NULL; *vconnp = &tcp->vconn; return 0; } static struct tcp_vconn * -tcp_vconn_cast(struct vconn *vconn) +tcp_vconn_cast(struct vconn *vconn) { assert(vconn->class == &tcp_vconn_class); - return CONTAINER_OF(vconn, struct tcp_vconn, vconn); + return CONTAINER_OF(vconn, struct tcp_vconn, vconn); } @@ -104,7 +118,8 @@ tcp_open(const char *name, char *suffix, struct vconn **vconnp) host_name = strtok_r(suffix, "::", &save_ptr); port_string = strtok_r(NULL, "::", &save_ptr); if (!host_name) { - fatal(0, "%s: bad peer name format", name); + error(0, "%s: bad peer name format", name); + return EAFNOSUPPORT; } memset(&sin, 0, sizeof sin); @@ -120,61 +135,41 @@ tcp_open(const char *name, char *suffix, struct vconn **vconnp) return errno; } - retval = connect(fd, (struct sockaddr *) &sin, sizeof sin); - if (retval < 0) { - int error = errno; - VLOG_ERR("%s: connect: %s", name, strerror(error)); + retval = set_nonblocking(fd); + if (retval) { close(fd); - return error; + return retval; } - return new_tcp_vconn(name, fd, vconnp); + retval = connect(fd, (struct sockaddr *) &sin, sizeof sin); + if (retval < 0) { + if (errno == EINPROGRESS) { + return new_tcp_vconn(name, fd, EAGAIN, &sin, vconnp); + } else { + int error = errno; + VLOG_ERR("%s: connect: %s", name, strerror(error)); + close(fd); + return error; + } + } else { + return new_tcp_vconn(name, fd, 0, &sin, vconnp); + } } static void -tcp_close(struct vconn *vconn) +tcp_close(struct vconn *vconn) { struct tcp_vconn *tcp = tcp_vconn_cast(vconn); + poll_cancel(tcp->tx_waiter); close(tcp->fd); free(tcp); } -static bool -tcp_prepoll(struct vconn *vconn, int want, struct pollfd *pfd) -{ - struct tcp_vconn *tcp = tcp_vconn_cast(vconn); - pfd->fd = tcp->fd; - if (want & WANT_RECV) { - pfd->events |= POLLIN; - } - if (want & WANT_SEND || tcp->txbuf) { - pfd->events |= POLLOUT; - } - return false; -} - -static void -tcp_postpoll(struct vconn *vconn, short int *revents) +static int +tcp_connect(struct vconn *vconn) { struct tcp_vconn *tcp = tcp_vconn_cast(vconn); - if (*revents & POLLOUT && tcp->txbuf) { - ssize_t n = write(tcp->fd, tcp->txbuf->data, tcp->txbuf->size); - if (n < 0) { - if (errno != EAGAIN) { - VLOG_ERR("send: %s", strerror(errno)); - *revents |= POLLERR; - } - } else if (n > 0) { - buffer_pull(tcp->txbuf, n); - if (tcp->txbuf->size == 0) { - buffer_delete(tcp->txbuf); - tcp->txbuf = NULL; - } - } - if (tcp->txbuf) { - *revents &= ~POLLOUT; - } - } + return check_connection_completion(tcp->fd); } static int @@ -201,8 +196,13 @@ again: return EPROTO; } want_bytes = length - rx->size; + if (!want_bytes) { + *bufferp = rx; + tcp->rxbuf = NULL; + return 0; + } } - buffer_reserve_tailroom(rx, want_bytes); + buffer_prealloc_tailroom(rx, want_bytes); retval = read(tcp->fd, buffer_tail(rx), want_bytes); if (retval > 0) { @@ -218,14 +218,49 @@ again: } return EAGAIN; } else if (retval == 0) { - return rx->size ? EPROTO : EOF; + if (rx->size) { + VLOG_ERR("connection dropped mid-packet"); + return EPROTO; + } else { + return EOF; + } } else { return retval ? errno : EAGAIN; } } +static void +tcp_clear_txbuf(struct tcp_vconn *tcp) +{ + buffer_delete(tcp->txbuf); + tcp->txbuf = NULL; + tcp->tx_waiter = NULL; +} + +static void +tcp_do_tx(int fd UNUSED, short int revents UNUSED, void *vconn_) +{ + struct vconn *vconn = vconn_; + struct tcp_vconn *tcp = tcp_vconn_cast(vconn); + ssize_t n = write(tcp->fd, tcp->txbuf->data, tcp->txbuf->size); + if (n < 0) { + if (errno != EAGAIN) { + VLOG_ERR("send: %s", strerror(errno)); + tcp_clear_txbuf(tcp); + return; + } + } else if (n > 0) { + buffer_pull(tcp->txbuf, n); + if (!tcp->txbuf->size) { + tcp_clear_txbuf(tcp); + return; + } + } + tcp->tx_waiter = poll_fd_callback(tcp->fd, POLLOUT, tcp_do_tx, vconn); +} + static int -tcp_send(struct vconn *vconn, struct buffer *buffer) +tcp_send(struct vconn *vconn, struct buffer *buffer) { struct tcp_vconn *tcp = tcp_vconn_cast(vconn); ssize_t retval; @@ -243,20 +278,47 @@ tcp_send(struct vconn *vconn, struct buffer *buffer) if (retval > 0) { buffer_pull(buffer, retval); } + tcp->tx_waiter = poll_fd_callback(tcp->fd, POLLOUT, tcp_do_tx, vconn); return 0; } else { return errno; } } +static void +tcp_wait(struct vconn *vconn, enum vconn_wait_type wait) +{ + struct tcp_vconn *tcp = tcp_vconn_cast(vconn); + switch (wait) { + case WAIT_CONNECT: + poll_fd_wait(tcp->fd, POLLOUT); + break; + + case WAIT_SEND: + if (!tcp->txbuf) { + poll_fd_wait(tcp->fd, POLLOUT); + } else { + /* Nothing to do: need to drain txbuf first. */ + } + break; + + case WAIT_RECV: + poll_fd_wait(tcp->fd, POLLIN); + break; + + default: + NOT_REACHED(); + } +} + struct vconn_class tcp_vconn_class = { .name = "tcp", .open = tcp_open, .close = tcp_close, - .prepoll = tcp_prepoll, - .postpoll = tcp_postpoll, + .connect = tcp_connect, .recv = tcp_recv, .send = tcp_send, + .wait = tcp_wait, }; /* Passive TCP. */ @@ -268,10 +330,10 @@ struct ptcp_vconn }; static struct ptcp_vconn * -ptcp_vconn_cast(struct vconn *vconn) +ptcp_vconn_cast(struct vconn *vconn) { assert(vconn->class == &ptcp_vconn_class); - return CONTAINER_OF(vconn, struct ptcp_vconn, vconn); + return CONTAINER_OF(vconn, struct ptcp_vconn, vconn); } static int @@ -317,56 +379,71 @@ ptcp_open(const char *name, char *suffix, struct vconn **vconnp) retval = set_nonblocking(fd); if (retval) { - VLOG_ERR("%s: set_nonblocking: %s", name, strerror(retval)); close(fd); return retval; } ptcp = xmalloc(sizeof *ptcp); ptcp->vconn.class = &ptcp_vconn_class; + ptcp->vconn.connect_status = 0; ptcp->fd = fd; *vconnp = &ptcp->vconn; return 0; } static void -ptcp_close(struct vconn *vconn) +ptcp_close(struct vconn *vconn) { struct ptcp_vconn *ptcp = ptcp_vconn_cast(vconn); close(ptcp->fd); free(ptcp); } -static bool -ptcp_prepoll(struct vconn *vconn, int want, struct pollfd *pfd) -{ - struct ptcp_vconn *ptcp = ptcp_vconn_cast(vconn); - pfd->fd = ptcp->fd; - if (want & WANT_ACCEPT) { - pfd->events |= POLLIN; - } - return false; -} - static int -ptcp_accept(struct vconn *vconn, struct vconn **new_vconnp) +ptcp_accept(struct vconn *vconn, struct vconn **new_vconnp) { struct ptcp_vconn *ptcp = ptcp_vconn_cast(vconn); + struct sockaddr_in sin; + socklen_t sin_len = sizeof sin; + char name[128]; int new_fd; + int error; - new_fd = accept(ptcp->fd, NULL, NULL); + new_fd = accept(ptcp->fd, &sin, &sin_len); if (new_fd < 0) { - return errno; + int error = errno; + if (error != EAGAIN) { + VLOG_DBG("accept: %s", strerror(error)); + } + return error; } - return new_tcp_vconn("tcp" /* FIXME */, new_fd, new_vconnp); + error = set_nonblocking(new_fd); + if (error) { + close(new_fd); + return error; + } + + 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)); + } + return new_tcp_vconn(name, new_fd, 0, &sin, new_vconnp); +} + +static void +ptcp_wait(struct vconn *vconn, enum vconn_wait_type wait) +{ + struct ptcp_vconn *ptcp = ptcp_vconn_cast(vconn); + assert(wait == WAIT_ACCEPT); + poll_fd_wait(ptcp->fd, POLLIN); } struct vconn_class ptcp_vconn_class = { .name = "ptcp", .open = ptcp_open, .close = ptcp_close, - .prepoll = ptcp_prepoll, .accept = ptcp_accept, + .wait = ptcp_wait };