Merge branch 'socket' into transsock
[doldaconnect.git] / daemon / net.c
index d64e8c8..86bc552 100644 (file)
@@ -66,8 +66,6 @@ static struct configvar myvars[] =
      * and net.visibleipv4 are unspecified the address of the hub
      * connection is used. */
     {CONF_VAR_STRING, "publicif", {.str = L""}},
-    /* Diffserv should be supported on IPv4, too, but I don't know the
-     * API to do that. */
     /** The Diffserv value to use on IPv6 connections when the
      * minimize cost TOS value is used (see the TOS VALUES
      * section). */
@@ -84,6 +82,12 @@ static struct configvar myvars[] =
      * minimize delay TOS value is used (see the TOS VALUES
      * section). */
     {CONF_VAR_INT, "diffserv-mindelay", {.num = 0}},
+    /** If enabled, the IP TOS interface will be used to set Diffserv
+     * codepoints on IPv4 sockets, by shifting the DSCP value two bits
+     * to the left (remember, the DSCP field in the IPv4 header is
+     * defined as the 6 uppermost bits of the TOS field, the lower two
+     * being left for ECN). This may only work on Linux. */
+    {CONF_VAR_BOOL, "dscp-tos", {.num = 0}},
     {CONF_VAR_END}
 };
 
@@ -234,6 +238,15 @@ static void sksetstate(struct socket *sk, int state)
     sk->back->state = state;
 }
 
+struct socket *netsockpipe(void)
+{
+    struct socket *sk;
+    
+    sk = sockpair(0);
+    sksetstate(sk, SOCK_EST);
+    return(sk);
+}
+
 static void closeufd(struct ufd *ufd)
 {
     if(ufd->fd != -1)
@@ -349,10 +362,41 @@ void getsock(struct socket *sk)
     sk->refcount++;
 }
 
+static void sockdebug(int level, struct socket *sk, char *format, ...)
+{
+    va_list args;
+    char *tb;
+    
+    if((sk->dbgnm == NULL) || (level > sk->dbglvl))
+       return;
+    va_start(args, format);
+    tb = vsprintf2(format, args);
+    va_end(args);
+    fprintf(stderr, "%s: %s\n", sk->dbgnm, tb);
+    free(tb);
+}
+
+void socksetdebug(struct socket *sk, int level, char *nm, ...)
+{
+    va_list args;
+    char *tb;
+    
+    va_start(args, nm);
+    tb = vsprintf2(nm, args);
+    va_end(args);
+    sk->dbgnm = sprintf2("%s (f)", tb);
+    sk->back->dbgnm = sprintf2("%s (b)", tb);
+    free(tb);
+    sk->dbglvl = level;
+    sk->back->dbglvl = level;
+    sockdebug(1, sk, "enabled debugging");
+}
+
 static void freesock(struct socket *sk)
 {
     struct dgrambuf *buf;
     
+    sockdebug(1, sk, "freeing socket");
     if(sk->dgram) {
        while((buf = sk->buf.d.f) != NULL) {
            sk->buf.d.f = buf->next;
@@ -362,6 +406,8 @@ static void freesock(struct socket *sk)
        if(sk->buf.s.buf != NULL)
            free(sk->buf.s.buf);
     }
+    if(sk->dbgnm != NULL)
+       free(sk->dbgnm);
     free(sk);
     numsocks--;
 }
@@ -381,6 +427,14 @@ void putsock(struct socket *sk)
     }
 }
 
+void quitsock(struct socket *sk)
+{
+    sk->readcb = NULL;
+    sk->writecb = NULL;
+    sk->errcb = NULL;
+    putsock(sk);
+}
+
 static void linksock(struct scons **list, struct socket *sk)
 {
     struct scons *sc;
@@ -459,6 +513,7 @@ void *sockgetinbuf(struct socket *sk, size_t *size)
        if((sk->buf.s.buf == NULL) || (sk->buf.s.datasize == 0))
        {
            *size = 0;
+           sockdebug(2, sk, "read 0 bytes", *size);
            return(NULL);
        }
        buf = sk->buf.s.buf;
@@ -467,6 +522,7 @@ void *sockgetinbuf(struct socket *sk, size_t *size)
        sk->buf.s.bufsize = sk->buf.s.datasize = 0;
        sockread(sk);
     }
+    sockdebug(2, sk, "read %zi bytes", *size);
     return(buf);
 }
 
@@ -476,6 +532,7 @@ void sockqueue(struct socket *sk, void *data, size_t size)
     struct sockaddr *remote;
     socklen_t remotelen;
     
+    sockdebug(2, sk, "queued %zi bytes", size);
     if(size == 0)
        return;
     if(sk->state == SOCK_STL)
@@ -736,6 +793,7 @@ static int sockflush(struct ufd *ufd)
 
 void closesock(struct socket *sk)
 {
+    sockdebug(1, sk, "closed");
     sksetstate(sk, SOCK_STL);
     if(sk->back->eos == 0)
        sk->back->eos = 1;
@@ -762,6 +820,18 @@ size_t sockgetdatalen(struct socket *sk)
 /*     return(sockgetdatalen(sk->back)); */
 /* } */
 
+size_t socktqueuesize(struct socket *sk)
+{
+    size_t ret;
+    
+    ret = 0;
+    while(1) {
+       ret += sockgetdatalen(sk->back);
+       if((sk = sk->back->pnext) == NULL)
+           return(ret);
+    }
+}
+
 ssize_t sockqueueleft(struct socket *sk)
 {
     return(sk->back->maxbuf - sockgetdatalen(sk->back));
@@ -937,6 +1007,8 @@ struct socket *netcsconn(struct sockaddr *addr, socklen_t addrlen, void (*func)(
        memcpy(sk->ufd->d.s.remote = smalloc(addrlen), addr, sk->ufd->d.s.remotelen = addrlen);
        sk->back->conncb = func;
        sk->back->data = data;
+       getsock(sk->back);
+       putsock(sk);
        if(!connect(sk->ufd->fd, addr, addrlen))
        {
            sksetstate(sk, SOCK_EST);
@@ -1008,10 +1080,18 @@ static void runbatches(void)
 static void cleansocks(void)
 {
     struct ufd *ufd, *next;
+    int dead;
     
     for(ufd = ufds; ufd != NULL; ufd = next) {
        next = ufd->next;
-       if(ufd->sk && ((ufd->fd < 0) || (sockgetdatalen(ufd->sk) == 0))) {
+       if(ufd->sk) {
+           dead = (ufd->fd < 0);
+           if(ufd->sk->state == SOCK_STL)
+               dead = 1;
+           if((ufd->sk->state == SOCK_EST) && (sockgetdatalen(ufd->sk) == 0))
+               dead = 1;
+           if(!dead)
+               continue;
            if(ufd->sk->eos == 1) {
                ufd->sk->eos = 2;
                closeufd(ufd);
@@ -1155,6 +1235,7 @@ int socksettos(struct socket *sk, int tos)
 {
     int buf;
     struct ufd *ufd;
+    int dscp2tos;
     
     ufd = getskufd(sk);
     if(ufd->type != UFD_SOCK) {
@@ -1165,22 +1246,35 @@ int socksettos(struct socket *sk, int tos)
        return(0); /* Unix sockets are always perfect. :) */
     if(ufd->d.s.family == AF_INET)
     {
+       dscp2tos = confgetint("net", "dscp-tos");
        switch(tos)
        {
        case 0:
            buf = 0;
            break;
        case SOCK_TOS_MINCOST:
-           buf = 0x02;
+           if(dscp2tos)
+               buf = confgetint("net", "diffserv-mincost") << 2;
+           else
+               buf = 0x02;
            break;
        case SOCK_TOS_MAXREL:
-           buf = 0x04;
+           if(dscp2tos)
+               buf = confgetint("net", "diffserv-maxrel") << 2;
+           else
+               buf = 0x04;
            break;
        case SOCK_TOS_MAXTP:
-           buf = 0x08;
+           if(dscp2tos)
+               buf = confgetint("net", "diffserv-maxtp") << 2;
+           else
+               buf = 0x08;
            break;
        case SOCK_TOS_MINDELAY:
-           buf = 0x10;
+           if(dscp2tos)
+               buf = confgetint("net", "diffserv-mindelay") << 2;
+           else
+               buf = 0x10;
            break;
        default:
            flog(LOG_WARNING, "attempted to set unknown TOS value %i to IPv4 sock", tos);