about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2013-04-14 00:50:09 +0000
committerEric Wong <normalperson@yhbt.net>2013-04-17 03:35:14 +0000
commit7b097d6129a7971197430d817682163adb8e2e8a (patch)
treeb2bd80fc3663be72c22732938190607ce090c481
parent449b85daa42cae1b9542a26e6dd52a1db38cce93 (diff)
downloadcmogstored-7b097d6129a7971197430d817682163adb8e2e8a.tar.gz
getpeername() does not work on unconnected sockets.  For error-handling,
unconnected sockets is a fairly common occurrence, so we want to get
the address early on when we know the address is still valid.

For IPv4 addresses, this does not increase memory overhead at all.  IPv6
addresses[1] does require an additional heap allocation, but it does not
need to be aligned since it is infrequently accessed.  If IPv6 becomes
common, we may need to expand our per-client storage to 192 bytes (from
128) on 64-bit (or see if we may pack data more carefully).

[1] IPv6 addresses are rare with MogileFS, as MogileFS does not
    currently support them.
-rw-r--r--Makefile.am2
-rw-r--r--accept.c2
-rw-r--r--accept_loop.c9
-rw-r--r--cmogstored.h37
-rw-r--r--http.c9
-rw-r--r--http_put.c17
-rw-r--r--inherit.c15
-rw-r--r--mgmt.c3
-rw-r--r--nameinfo.c3
-rw-r--r--packaddr.c21
-rw-r--r--packaddr.h49
11 files changed, 120 insertions, 47 deletions
diff --git a/Makefile.am b/Makefile.am
index dc556c5..3ce8cf0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -70,6 +70,8 @@ mog_src += nameinfo.c
 mog_src += nostd/setproctitle.h
 mog_src += notify.c
 mog_src += notify.h
+mog_src += packaddr.c
+mog_src += packaddr.h
 mog_src += pidfile.c
 mog_src += process.c
 mog_src += queue_common.c
diff --git a/accept.c b/accept.c
index 76c53db..6148279 100644
--- a/accept.c
+++ b/accept.c
@@ -5,7 +5,7 @@
 #include "cmogstored.h"
 
 struct mog_accept *
-mog_accept_init(int fd, struct mog_svc *svc, post_accept_fn fn)
+mog_accept_init(int fd, struct mog_svc *svc, mog_post_accept_fn fn)
 {
         struct mog_fd *mfd = mog_fd_get(fd);
         struct mog_accept *ac = &mfd->as.accept;
diff --git a/accept_loop.c b/accept_loop.c
index d5eb840..7d62e17 100644
--- a/accept_loop.c
+++ b/accept_loop.c
@@ -90,16 +90,21 @@ void *mog_accept_loop(void *arg)
 {
         struct mog_accept *ac = arg;
         int accept_fd = mog_fd_of(ac)->fd;
+        union mog_sockaddr msa;
 
         pthread_cleanup_push(accept_loop_cleanup, NULL);
 
         for (;;) {
+                struct sockaddr *sa = mog_sockaddr_sa(&msa);
+                socklen_t salen = (socklen_t)sizeof(msa);
+                int client_fd;
+
                 /* pthread cancellation point */
-                int client_fd = mog_accept_fn(accept_fd, NULL, NULL);
+                client_fd = mog_accept_fn(accept_fd, sa, &salen);
 
                 if (client_fd >= 0) {
                         mog_cancel_disable();
-                        ac->post_accept_fn(client_fd, ac->svc);
+                        ac->post_accept_fn(client_fd, ac->svc, sa, salen);
                         mog_cancel_enable();
                 } else {
                         accept_error_check(ac);
diff --git a/cmogstored.h b/cmogstored.h
index 170b840..adb8cac 100644
--- a/cmogstored.h
+++ b/cmogstored.h
@@ -70,6 +70,7 @@
 #include "defaults.h"
 #include "iostat.h"
 #include "mnt.h"
+#include "packaddr.h"
 
 #define MOG_WR_ERROR ((void *)-1)
 #define MOG_IOSTAT (MAP_FAILED)
@@ -170,7 +171,7 @@ enum mog_chunk_state {
 struct mog_http {
         int cs;
         struct {
-                enum mog_http_method http_method:8;
+                enum mog_http_method http_method:4;
                 unsigned persistent:1;
                 unsigned chunked:1;
                 unsigned has_trailer_md5:1;
@@ -193,7 +194,8 @@ struct mog_http {
         struct mog_wbuf *wbuf; /* uncommonly needed */
         struct mog_svc *svc;
         uint8_t expect_md5[16];
-};
+        struct mog_packaddr mpa;
+} __attribute__((packed));
 
 struct mog_thrpool {
         pthread_mutex_t lock;
@@ -217,13 +219,15 @@ struct mog_queue {
 };
 
 /* accept.c */
-typedef void (*post_accept_fn)(int fd, struct mog_svc *);
+typedef void (*mog_post_accept_fn)(int fd, struct mog_svc *,
+                                struct sockaddr *, socklen_t);
 struct mog_accept {
         struct mog_svc *svc;
-        post_accept_fn post_accept_fn;
+        mog_post_accept_fn post_accept_fn;
         struct mog_thrpool thrpool;
 };
-struct mog_accept * mog_accept_init(int fd, struct mog_svc *, post_accept_fn);
+struct mog_accept *mog_accept_init(int fd, struct mog_svc *,
+                                        mog_post_accept_fn);
 void * mog_accept_loop(void *ac);
 
 struct mog_digest {
@@ -385,7 +389,8 @@ void mog_thrpool_process_queue(void);
 
 /* mgmt.c */
 void mog_mgmt_writev(struct mog_mgmt *, struct iovec *, int iovcnt);
-void mog_mgmt_post_accept(int fd, struct mog_svc *);
+void mog_mgmt_post_accept(int fd, struct mog_svc *,
+                                struct sockaddr *, socklen_t);
 enum mog_next mog_mgmt_queue_step(struct mog_fd *) MOG_CHECK;
 void mog_mgmt_quit_step(struct mog_fd *);
 
@@ -439,8 +444,10 @@ void mog_http_get_open(struct mog_http *, char *buf);
 enum mog_next mog_http_get_in_progress(struct mog_fd *);
 
 /* http.c */
-void mog_http_post_accept(int fd, struct mog_svc *);
-void mog_httpget_post_accept(int fd, struct mog_svc *);
+void mog_http_post_accept(int fd, struct mog_svc *,
+                                struct sockaddr *, socklen_t);
+void mog_httpget_post_accept(int fd, struct mog_svc *,
+                                struct sockaddr *, socklen_t);
 enum mog_next mog_http_queue_step(struct mog_fd *) MOG_CHECK;
 void mog_http_quit_step(struct mog_fd *);
 char *mog_http_path(struct mog_http *, char *buf);
@@ -556,17 +563,5 @@ struct mog_ni {
         char ni_serv[sizeof(":65536")];
 };
 
-/* avoid sockaddr_storage since that bigger than we need */
-union mog_sockaddr {
-        struct sockaddr_in in;
-        struct sockaddr_in6 in6;
-        uint8_t bytes[sizeof(struct sockaddr_in6)];
-};
-
 /* nameinfo.c */
-int mog_nameinfo(union mog_sockaddr *, socklen_t, struct mog_ni *);
-
-static inline struct sockaddr *mog_sockaddr_sa(union mog_sockaddr *msa)
-{
-        return (struct sockaddr *)msa;
-}
+int mog_nameinfo(struct sockaddr *, socklen_t, struct mog_ni *);
diff --git a/http.c b/http.c
index 4519498..f2a66e0 100644
--- a/http.c
+++ b/http.c
@@ -87,6 +87,7 @@ MOG_NOINLINE static void http_close(struct mog_fd *mfd)
          * their connection to save ourselves bandwidth/cycles
          */
         tcp_push(mfd, false);
+        mog_packaddr_free(&http->mpa);
 
         mog_fd_put(mfd);
 }
@@ -264,22 +265,26 @@ void mog_http_quit_step(struct mog_fd *mfd)
 }
 
 /* called immediately after accept(), this initializes the mfd (once) */
-void mog_http_post_accept(int fd, struct mog_svc *svc)
+void mog_http_post_accept(int fd, struct mog_svc *svc,
+                        struct sockaddr *sa, socklen_t salen)
 {
         struct mog_fd *mfd = mog_fd_init(fd, MOG_FD_TYPE_HTTP);
         struct mog_http *http = &mfd->as.http;
 
         mog_http_init(http, svc);
+        mog_packaddr_init(&http->mpa, sa, salen);
         mog_idleq_add(svc->queue, mfd, MOG_QEV_RD);
 }
 
 /* called immediately after accept(), this initializes the mfd (once) */
-void mog_httpget_post_accept(int fd, struct mog_svc *svc)
+void mog_httpget_post_accept(int fd, struct mog_svc *svc,
+                        struct sockaddr *sa, socklen_t salen)
 {
         struct mog_fd *mfd = mog_fd_init(fd, MOG_FD_TYPE_HTTPGET);
         struct mog_http *http = &mfd->as.http;
 
         mog_http_init(http, svc);
+        mog_packaddr_init(&http->mpa, sa, salen);
         mog_idleq_add(svc->queue, mfd, MOG_QEV_RD);
 }
 
diff --git a/http_put.c b/http_put.c
index a27d389..8035148 100644
--- a/http_put.c
+++ b/http_put.c
@@ -440,25 +440,20 @@ static unsigned last_data_recv(int fd)
 
 MOG_NOINLINE static void read_err_dbg(struct mog_fd *mfd, ssize_t r)
 {
-        static const char no_ip[] = "(unconnected)";
         int save_errno = errno;
-        union mog_sockaddr any;
         struct mog_ni ni;
+        struct sockaddr *sa;
         const char *addr;
         const char *path = "(unknown)";
         long long bytes = -1;
         const char *errfmt;
-        socklen_t len = (socklen_t)sizeof(any);
+        socklen_t len;
         unsigned last = last_data_recv(mfd->fd);
-        int rc = getpeername(mfd->fd, mog_sockaddr_sa(&any), &len);
+        int rc;
 
-        if (rc == 0) {
-                rc = mog_nameinfo(&any, len, &ni);
-                addr = rc == 0 ? ni.ni_host : gai_strerror(rc);
-        } else {
-                syslog(LOG_ERR, "getpeername() failed for fd=%d: %m", mfd->fd);
-                addr = no_ip;
-        }
+        mog_packaddr_read(&mfd->as.http.mpa, &sa, &len);
+        rc = mog_nameinfo(sa, len, &ni);
+        addr = rc == 0 ? ni.ni_host : gai_strerror(rc);
 
         if (mfd->as.http.forward) {
                 path = mfd->as.http.forward->as.file.path;
diff --git a/inherit.c b/inherit.c
index 7110067..da3c852 100644
--- a/inherit.c
+++ b/inherit.c
@@ -6,7 +6,7 @@
 static Hash_table *listeners; /* yes, we'll scale to 10K listen sockets, L10K! */
 
 struct listener {
-        union mog_sockaddr as;
+        union mog_sockaddr msa;
         socklen_t len;
         int fd;
 };
@@ -17,7 +17,7 @@ static bool listener_cmp(const void *a, const void *b)
         const struct listener *lb = b;
 
         return (la->len == lb->len) &&
-               (memcmp(&la->as, &lb->as, lb->len) == 0);
+               (memcmp(&la->msa, &lb->msa, lb->len) == 0);
 }
 
 static size_t listener_hash(const void *x, size_t tablesize)
@@ -27,7 +27,7 @@ static size_t listener_hash(const void *x, size_t tablesize)
         socklen_t i;
 
         for (i = 0; i < l->len; i++)
-                value = (value * 31 + l->as.bytes[i]) % tablesize;
+                value = (value * 31 + l->msa.bytes[i]) % tablesize;
 
         return value;
 }
@@ -38,12 +38,13 @@ static void register_listen_fd(int fd)
         struct listener *ins;
         struct mog_ni ni;
         int rc;
+        struct sockaddr *sa = mog_sockaddr_sa(&tmp.msa);
 
-        tmp.len = (socklen_t)sizeof(tmp.as);
-        if (getsockname(fd, mog_sockaddr_sa(&tmp.as), &tmp.len) != 0)
+        tmp.len = (socklen_t)sizeof(tmp.msa);
+        if (getsockname(fd, sa, &tmp.len) != 0)
                 die_errno("getsockname(fd=%d) failed", fd);
 
-        rc = mog_nameinfo(&tmp.as, tmp.len, &ni);
+        rc = mog_nameinfo(sa, tmp.len, &ni);
         if (rc != 0)
                 die("getnameinfo failed: %s (fd=%d)", gai_strerror(rc), fd);
 
@@ -105,7 +106,7 @@ int mog_inherit_get(struct sockaddr *addr, socklen_t len)
                 return fd;
 
         l.len = len;
-        memcpy(mog_sockaddr_sa(&l.as), addr, len);
+        memcpy(&l.msa.bytes, addr, len);
 
         in = hash_delete(listeners, &l);
         if (in) {
diff --git a/mgmt.c b/mgmt.c
index b6b2aa5..2fbef74 100644
--- a/mgmt.c
+++ b/mgmt.c
@@ -269,7 +269,8 @@ void mog_mgmt_quit_step(struct mog_fd *mfd)
 }
 
 /* called immediately after accept(), this initializes the mfd (once) */
-void mog_mgmt_post_accept(int fd, struct mog_svc *svc)
+void mog_mgmt_post_accept(int fd, struct mog_svc *svc,
+                        struct sockaddr *sa, socklen_t salen)
 {
         struct mog_fd *mfd = mog_fd_init(fd, MOG_FD_TYPE_MGMT);
         struct mog_mgmt *mgmt = &mfd->as.mgmt;
diff --git a/nameinfo.c b/nameinfo.c
index fcd2865..5d9122f 100644
--- a/nameinfo.c
+++ b/nameinfo.c
@@ -11,7 +11,7 @@
  *
  * returns the return value of getnameinfo(3)
  */
-int mog_nameinfo(union mog_sockaddr *any, socklen_t len, struct mog_ni *ni)
+int mog_nameinfo(struct sockaddr *sa, socklen_t len, struct mog_ni *ni)
 {
         char *hostptr = ni->ni_host;
         size_t hostlen = sizeof(ni->ni_host) - (sizeof("[]") - 1);
@@ -19,7 +19,6 @@ int mog_nameinfo(union mog_sockaddr *any, socklen_t len, struct mog_ni *ni)
         char *servptr = ni->ni_serv + 1; /* offset for ':' */
         size_t servlen = sizeof(ni->ni_serv) - 1; /* offset for ':' */
 
-        struct sockaddr *sa = mog_sockaddr_sa(any);
         int flags = NI_NUMERICHOST | NI_NUMERICSERV;
         int rc;
 
diff --git a/packaddr.c b/packaddr.c
new file mode 100644
index 0000000..75450d2
--- /dev/null
+++ b/packaddr.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012-2013, Eric Wong <normalperson@yhbt.net>
+ * License: GPLv3 or later (see COPYING for details)
+ */
+#include "cmogstored.h"
+
+void mog_packaddr_read(struct mog_packaddr *mpa,
+                        struct sockaddr **sa, socklen_t *salen)
+{
+        switch (mpa->as.sa_family) {
+        case AF_INET:
+                *salen = (socklen_t)sizeof(struct sockaddr_in);
+                *sa = (struct sockaddr *)&mpa->as.in4;
+                return;
+        case AF_INET6:
+                *salen = (socklen_t)sizeof(struct sockaddr_in6);
+                *sa = mog_sockaddr_sa(mpa->as.msa.ptr);
+                return;
+        }
+        assert(0 && "unrecognized sa_family" && mpa->as.sa_family);
+}
diff --git a/packaddr.h b/packaddr.h
new file mode 100644
index 0000000..4c9d880
--- /dev/null
+++ b/packaddr.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012-2013, Eric Wong <normalperson@yhbt.net>
+ * License: GPLv3 or later (see COPYING for details)
+ */
+
+/* avoid sockaddr_storage since that bigger than we need */
+union mog_sockaddr {
+        sa_family_t sa_family;
+        struct sockaddr_in in;
+        struct sockaddr_in6 in6;
+        uint8_t bytes[sizeof(struct sockaddr_in6)];
+};
+
+static inline struct sockaddr *mog_sockaddr_sa(union mog_sockaddr *msa)
+{
+        assert((void *)msa == (void *)&msa->bytes);
+        return (struct sockaddr *)msa;
+}
+
+struct mog_packaddr {
+        union {
+                sa_family_t sa_family;
+                struct sockaddr_in in4;
+                struct {
+                        sa_family_t sa_family; /* padding */
+                        union mog_sockaddr *ptr;
+                } msa;
+        } as;
+} __attribute__((packed));
+
+static inline void mog_packaddr_free(struct mog_packaddr *mpa)
+{
+        if (mpa->as.sa_family != AF_INET)
+                free(mpa->as.msa.ptr);
+}
+
+static inline void mog_packaddr_init(struct mog_packaddr *mpa,
+                                struct sockaddr *sa, socklen_t salen)
+{
+        if (sa->sa_family == AF_INET) {
+                memcpy(&mpa->as.in4, sa, (size_t)salen);
+        } else {
+                mpa->as.sa_family = sa->sa_family;
+                mpa->as.msa.ptr = xmalloc(sizeof(union mog_sockaddr));
+                memcpy(mpa->as.msa.ptr, sa, (size_t)salen);
+        }
+}
+
+void mog_packaddr_read(struct mog_packaddr *, struct sockaddr **, socklen_t *);