On Tue, Mar 08, 2011 at 02:11:31AM +0100, Alexander Bluhm wrote:
> Hmm, perhaps too small.  There is a session timeout and relayctl
> show sessions idle time.  Relayd does not realize when the kernel
> is transferring data automatically.  It should check the splice
> data length with getsockopt() periodically or when the timeout
> fires.

This diff fixes the timeout issues.  If a timeout occured and
splicing is active, check wether the splice length has changed.  If
so, reset the timeout and idle counter.  Thay may make timeout
detection longer than without splicing.

Socket splicing works for plain TCP conections now.  I have tested
that HTTP keep-alive and SSL are not affected.

As requested by Theo splicing cannot be turned off.

ok?

bluhm


Index: usr.sbin/relayd/relay.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/relayd/relay.c,v
retrieving revision 1.130
diff -u -p -r1.130 relay.c
--- usr.sbin/relayd/relay.c     12 Mar 2011 21:06:40 -0000      1.130
+++ usr.sbin/relayd/relay.c     16 Mar 2011 01:04:38 -0000
@@ -77,6 +77,7 @@ u_int32_t      relay_hash_addr(struct sockad
 
 void            relay_write(struct bufferevent *, void *);
 void            relay_read(struct bufferevent *, void *);
+int             relay_splicelen(struct ctl_relay_event *);
 void            relay_error(struct bufferevent *, short, void *);
 void            relay_dump(struct ctl_relay_event *, const void *, size_t);
 
@@ -1938,15 +1939,47 @@ relay_close_http(struct rsession *con, u
        }
 }
 
+int
+relay_splicelen(struct ctl_relay_event *cre)
+{
+       struct rsession *con = (struct rsession *)cre->con;
+       off_t len;
+       socklen_t optlen;
+
+       optlen = sizeof(len);
+       if (getsockopt(cre->s, SOL_SOCKET, SO_SPLICE, &len, &optlen) == -1) {
+               relay_close(con, strerror(errno));
+               return (0);
+       }
+       if (len > cre->splicelen) {
+               cre->splicelen = len;
+               return (1);
+       }
+       return (0);
+}
+
 void
 relay_error(struct bufferevent *bev, short error, void *arg)
 {
        struct ctl_relay_event *cre = (struct ctl_relay_event *)arg;
        struct rsession *con = (struct rsession *)cre->con;
        struct evbuffer *dst;
+       struct timeval tv, tv_now;
 
        if (error & EVBUFFER_TIMEOUT) {
-               relay_close(con, "buffer event timeout");
+               if (gettimeofday(&tv_now, NULL) == -1) {
+                       relay_close(con, strerror(errno));
+                       return;
+               }
+               if (cre->splicelen >= 0 && relay_splicelen(cre))
+                       con->se_tv_last = tv_now;
+               if (cre->dst->splicelen >= 0 && relay_splicelen(cre->dst))
+                       con->se_tv_last = tv_now;
+               timersub(&tv_now, &con->se_tv_last, &tv);
+               if (timercmp(&tv, &con->se_relay->rl_conf.timeout, >=))
+                       relay_close(con, "buffer event timeout");
+               else
+                       bufferevent_enable(cre->dst->bev, EV_READ);
                return;
        }
        if (error & (EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF)) {
@@ -2000,6 +2033,8 @@ relay_accept(int fd, short sig, void *ar
        con->se_out.dst = &con->se_in;
        con->se_in.con = con;
        con->se_out.con = con;
+       con->se_in.splicelen = -1;
+       con->se_out.splicelen = -1;
        con->se_relay = rlay;
        con->se_id = ++relay_conid;
        con->se_relayid = rlay->rl_conf.id;
@@ -2327,6 +2362,24 @@ relay_connect(struct rsession *con)
                log_debug("relay_connect: session %d: forward failed: %s",
                    con->se_id, strerror(errno));
                return (-1);
+       }
+
+       if (rlay->rl_proto->type == RELAY_PROTO_TCP &&
+           (rlay->rl_conf.flags & (F_SSL|F_SSLCLIENT)) == 0) {
+               if (setsockopt(con->se_in.s, SOL_SOCKET, SO_SPLICE,
+                   &con->se_out.s, sizeof(int)) == -1) {
+                       log_debug("relay_connect: session %d: splice forward "
+                           "failed: %s", con->se_id, strerror(errno));
+                       return (-1);
+               }
+               con->se_in.splicelen = 0;
+               if (setsockopt(con->se_out.s, SOL_SOCKET, SO_SPLICE,
+                   &con->se_in.s, sizeof(int)) == -1) {
+                       log_debug("relay_connect: session %d: splice backward "
+                           "failed: %s", con->se_id, strerror(errno));
+                       return (-1);
+               }
+               con->se_out.splicelen = 0;
        }
 
        if (errno == EINPROGRESS)
Index: usr.sbin/relayd/relayd.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.140
diff -u -p -r1.140 relayd.h
--- usr.sbin/relayd/relayd.h    31 Dec 2010 21:22:42 -0000      1.140
+++ usr.sbin/relayd/relayd.h    12 Mar 2011 19:54:53 -0000
@@ -162,6 +162,7 @@ struct ctl_relay_event {
        char                    *args;
        char                    *version;
 
+       off_t                    splicelen;
        int                      line;
        size_t                   toread;
        int                      chunked;

Reply via email to