Index: ggatec/ggatec.c =================================================================== RCS file: /home/ncvs/src/sbin/ggate/ggatec/ggatec.c,v --- ggatec/ggatec.c 25 Nov 2008 02:59:29 -0000 1.7.6.1 +++ ggatec/ggatec.c 26 Mar 2009 06:35:28 -0000 @@ -49,7 +49,9 @@ #include #include #include +#include #include +#include #include #include "ggate.h" @@ -61,9 +63,11 @@ static const char *host = NULL; static int unit = -1; static unsigned flags = 0; +/* destroy -f => force, create/rescue -f => foreground */ static int force = 0; +static int foreground = 0; static unsigned queue_size = G_GATE_QUEUE_SIZE; -static unsigned port = G_GATE_PORT; +static const char *port = G_GATE_PORT_STR; static off_t mediasize; static unsigned sectorsize = 0; static unsigned timeout = G_GATE_TIMEOUT; @@ -71,21 +75,51 @@ static uint32_t token; static pthread_t sendtd, recvtd; static int reconnect; +static struct addrinfo hints; +static int ai_protocol = 0; /* 0 for TCP and IPPROTO_SCTP for SCTP */ + +static uint64_t send_seq; +static uint64_t recv_seq; static void usage(void) { - fprintf(stderr, "usage: %s create [-nv] [-o ] [-p port] " + fprintf(stderr, "usage: %s create [-fnv] [-o ] [-p port] " "[-q queue_size] [-R rcvbuf] [-S sndbuf] [-s sectorsize] " - "[-t timeout] [-u unit] \n", getprogname()); - fprintf(stderr, " %s rescue [-nv] [-o ] [-p port] " - "[-R rcvbuf] [-S sndbuf] <-u unit> \n", getprogname()); + "[-t timeout] [-u unit] [-F <0|4|6>] [-P protocol] \n", + getprogname()); + fprintf(stderr, " %s rescue [-fnv] [-o ] [-p port] " + "[-R rcvbuf] [-S sndbuf] [-F <0|4|6>] [-P protocol] <-u unit> \n", + getprogname()); fprintf(stderr, " %s destroy [-f] <-u unit>\n", getprogname()); fprintf(stderr, " %s list [-v] [-u unit]\n", getprogname()); exit(EXIT_FAILURE); } +static void +summary(void) +{ + char buf[1024]; + + /* Use snprintf(3) so that we don't reenter stdio(3). */ + snprintf(buf, sizeof(buf), + "last send seq %llu\n" + "last recv seq %llu\n" + "", + send_seq, recv_seq); + write(STDERR_FILENO, buf, strlen(buf)); +} + +static void +summaryx(int notused __unused) +{ + int save_errno = errno; + + summary(); + errno = save_errno; +} + static void * send_thread(void *arg __unused) { @@ -150,8 +184,10 @@ g_gate_swap2n_hdr(&hdr); data = g_gate_send(sendfd, &hdr, sizeof(hdr), MSG_NOSIGNAL); - g_gate_log(LOG_DEBUG, "Sent hdr packet."); g_gate_swap2h_hdr(&hdr); + g_gate_log(LOG_DEBUG, "Sent hdr packet (seq=%llu).", hdr.gh_seq); + send_seq = hdr.gh_seq; + if (reconnect) break; if (data != sizeof(hdr)) { @@ -172,8 +208,8 @@ pthread_kill(recvtd, SIGUSR1); break; } - g_gate_log(LOG_DEBUG, "Sent %zd bytes (offset=%llu, " - "size=%u).", data, hdr.gh_offset, hdr.gh_length); + g_gate_log(LOG_DEBUG, "Sent %zd bytes (seq=%llu, offset=%llu, " + "size=%u).", data, hdr.gh_seq, hdr.gh_offset, hdr.gh_length); } } g_gate_log(LOG_DEBUG, "%s: Died.", __func__); @@ -207,7 +243,8 @@ pthread_kill(sendtd, SIGUSR1); break; } - g_gate_log(LOG_DEBUG, "Received hdr packet."); + g_gate_log(LOG_DEBUG, "Received hdr packet (seq=%llu).", hdr.gh_seq); + recv_seq = hdr.gh_seq; ggio.gctl_seq = hdr.gh_seq; ggio.gctl_cmd = hdr.gh_cmd; @@ -216,6 +253,7 @@ ggio.gctl_error = hdr.gh_error; if (ggio.gctl_error == 0 && ggio.gctl_cmd == GGATE_CMD_READ) { +g_gate_log(LOG_DEBUG, "Received READ packet."); data = g_gate_recv(recvfd, ggio.gctl_data, ggio.gctl_length, MSG_WAITALL); if (reconnect) @@ -244,37 +282,52 @@ struct g_gate_version ver; struct g_gate_cinit cinit; struct g_gate_sinit sinit; - struct sockaddr_in serv; + struct addrinfo *res, *p; int sfd; + int error = 0; /* * Do the network stuff. */ - bzero(&serv, sizeof(serv)); - serv.sin_family = AF_INET; - serv.sin_addr.s_addr = g_gate_str2ip(host); - if (serv.sin_addr.s_addr == INADDR_NONE) { - g_gate_log(LOG_DEBUG, "Invalid IP/host name: %s.", host); + sfd = -1; + if ((error = getaddrinfo(host, port, &hints, &res)) != 0) { + g_gate_log(LOG_DEBUG, "%s: %s (%s).", gai_strerror(error), + host, port); return (-1); - } - serv.sin_port = htons(port); - sfd = socket(AF_INET, SOCK_STREAM, 0); - if (sfd == -1) { - g_gate_log(LOG_DEBUG, "Cannot open socket: %s.", - strerror(errno)); - return (-1); - } + } else { + p = res; + while (p) { + sfd = socket(p->ai_family, p->ai_socktype, ai_protocol); + if (sfd == -1) { + g_gate_log(LOG_DEBUG, "Cannot open socket: %s.", + strerror(errno)); + freeaddrinfo(res); + return (-1); + } - g_gate_socket_settings(sfd); + g_gate_socket_settings(sfd); - if (connect(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1) { - g_gate_log(LOG_DEBUG, "Cannot connect to server: %s.", - strerror(errno)); - close(sfd); + if (connect(sfd, p->ai_addr, p->ai_addrlen) == 0) + break; + + /* Non-critical error, try next address */ + g_gate_log(LOG_DEBUG, "Cannot connect to server: %s.", + strerror(errno)); + close(sfd); + sfd = -1; + + p = p->ai_next; + } + } + freeaddrinfo(res); + + if (sfd == -1) { + g_gate_log(LOG_DEBUG, "Invalid IP/host name or port: %s (%s).", + host, port); return (-1); } - g_gate_log(LOG_INFO, "Connected to the server: %s:%d.", host, port); + g_gate_log(LOG_INFO, "Connected to the server: %s (%s).", host, port); /* * Create and send version packet. @@ -356,8 +409,7 @@ static void mydaemon(void) { - - if (g_gate_verbose > 0) + if (foreground || g_gate_verbose > 0) return; if (daemon(0, 0) == 0) return; @@ -420,6 +472,7 @@ struct g_gate_ctl_cancel ggioc; signal(SIGUSR1, signop); + signal(SIGINFO, summaryx); for (;;) { g_gatec_start(); g_gate_log(LOG_NOTICE, "Disconnected [%s %s]. Connecting...", @@ -454,7 +507,7 @@ ggioc.gctl_maxcount = queue_size; ggioc.gctl_timeout = timeout; ggioc.gctl_unit = unit; - snprintf(ggioc.gctl_info, sizeof(ggioc.gctl_info), "%s:%u %s", host, + snprintf(ggioc.gctl_info, sizeof(ggioc.gctl_info), "%s (%s) %s", host, port, path); g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc); if (unit == -1) { @@ -487,6 +540,9 @@ int main(int argc, char *argv[]) { + struct protoent *pent; + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; if (argc < 2) usage(); @@ -505,13 +561,19 @@ for (;;) { int ch; - ch = getopt(argc, argv, "fno:p:q:R:S:s:t:u:v"); + ch = getopt(argc, argv, "fno:p:q:R:S:s:t:u:vF:P:"); if (ch == -1) break; switch (ch) { case 'f': +#if 0 if (action != DESTROY) usage(); +#else + if (action != DESTROY) + foreground = 1; + else +#endif force = 1; break; case 'n': @@ -519,6 +581,43 @@ usage(); nagle = 0; break; + case 'F': + switch (*optarg) { + case '4': + hints.ai_family = PF_INET; + break; + case '6': + hints.ai_family = PF_INET6; + break; + case '0': + hints.ai_family = PF_UNSPEC; + break; + default: + usage(); + } + break; + case 'P': + if (optarg && (pent = getprotobyname(optarg))) + ai_protocol = pent->p_proto; + else { + ai_protocol = 0; + /* + errx(EXIT_FAILURE, + "protocol %s does not exist.", optarg); + */ + } + break; + case '4': + hints.ai_family = PF_INET; + break; + case '6': + hints.ai_family = PF_INET6; + break; + case 'x': + nagle = 0; + hints.ai_family = PF_INET; + ai_protocol = IPPROTO_SCTP; + break; case 'o': if (action != CREATE && action != RESCUE) usage(); @@ -537,9 +636,7 @@ if (action != CREATE && action != RESCUE) usage(); errno = 0; - port = strtoul(optarg, NULL, 10); - if (port == 0 && errno != 0) - errx(EXIT_FAILURE, "Invalid port."); + port = optarg; break; case 'q': if (action != CREATE) Index: ggated/ggated.8 =================================================================== RCS file: /home/ncvs/src/sbin/ggate/ggated/ggated.8,v --- ggated/ggated.8 25 Nov 2008 02:59:29 -0000 1.4.18.1 +++ ggated/ggated.8 26 Mar 2009 06:35:28 -0000 @@ -55,7 +55,10 @@ Available options: .Bl -tag -width ".Ar exports\ file" .It Fl a Ar address -Specifies an IP address to bind to. +Specifies an IP address to bind to. To bind to multiple addresses, +specify each address with a separate +.Fl a +option. .It Fl h Print available options. .It Fl n @@ -74,7 +77,7 @@ Size of send buffer to use. Default is 131072 (128kB). .It Fl v -Do not fork, run in foreground and print debug informations on standard +Do not fork, run in foreground and print debug information on standard output. .It Ar "exports file" An alternate location for the exports file. @@ -82,9 +85,10 @@ .Pp The format of an exports file is as follows: .Bd -literal -offset indent -1.2.3.4 RO /dev/acd0 -1.2.3.0/24 RW /tmp/test.img -hostname WO /tmp/image +1.2.3.4 RO /dev/acd0 +1.2.3.0/24 RW /tmp/test.img +2001:DB8:17C0::/64 RW /tmp/foo +hostname WO /tmp/image .Ed .Sh EXIT STATUS Exit status is 0 on success, or 1 if the command fails. Index: ggated/ggated.c =================================================================== RCS file: /home/ncvs/src/sbin/ggate/ggated/ggated.c,v --- ggated/ggated.c 25 Nov 2008 02:59:29 -0000 1.9.6.1 +++ ggated/ggated.c 26 Mar 2009 06:35:28 -0000 @@ -43,7 +43,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -68,7 +70,9 @@ time_t c_birthtime; char *c_path; uint64_t c_token; - in_addr_t c_srcip; + struct sockaddr_storage c_srcaddr; + uint64_t c_send_seq; + uint64_t c_recv_seq; LIST_ENTRY(ggd_connection) c_next; }; @@ -80,19 +84,28 @@ #define r_cmd r_hdr.gh_cmd #define r_offset r_hdr.gh_offset #define r_length r_hdr.gh_length +#define r_seq r_hdr.gh_seq #define r_error r_hdr.gh_error struct ggd_export { - char *e_path; /* path to device/file */ - in_addr_t e_ip; /* remote IP address */ - in_addr_t e_mask; /* IP mask */ - unsigned e_flags; /* flags (RO/RW) */ - SLIST_ENTRY(ggd_export) e_next; + char *e_path; /* path to device/file */ + struct sockaddr_storage e_addr; /* remote IP address */ + struct sockaddr_storage e_mask; /* IP mask */ + unsigned e_flags; /* flags (RO/RW) */ + SLIST_ENTRY(ggd_export) e_next; +}; + +struct ggd_listen { + const char *l_name; /* host name / address */ + struct sockaddr_storage l_addr; /* bind address & port */ + int l_fd; /* socket */ + SLIST_ENTRY(ggd_listen) l_next; }; static const char *exports_file = GGATED_EXPORT_FILE; static int got_sighup = 0; -in_addr_t bindaddr; +static int ai_protocol = 0; +struct addrinfo hints; static TAILQ_HEAD(, ggd_request) inqueue = TAILQ_HEAD_INITIALIZER(inqueue); static TAILQ_HEAD(, ggd_request) outqueue = TAILQ_HEAD_INITIALIZER(outqueue); @@ -110,76 +123,123 @@ usage(void) { - fprintf(stderr, "usage: %s [-nv] [-a address] [-p port] [-R rcvbuf] " + fprintf(stderr, "usage: %s [-fnv] [-a address] [-p port] [-R rcvbuf] " "[-S sndbuf] [exports file]\n", getprogname()); exit(EXIT_FAILURE); } -static char * -ip2str(in_addr_t ip) +static void +summary(void) +{ + struct ggd_connection *conn, *tconn; + + char buf[1024]; + + /* Use snprintf(3) so that we don't reenter stdio(3). */ + LIST_FOREACH_SAFE(conn, &connections, c_next, tconn) { + snprintf(buf, sizeof(buf), + "path %s\n" + "send_seq %llu\n" + "recv_seq %llu\n" + "", + conn->c_path, + conn->c_send_seq, conn->c_recv_seq); + write(STDERR_FILENO, buf, strlen(buf)); + } +} + +static void +summaryx(int notused __unused) { - static char sip[16]; + int save_errno = errno; - snprintf(sip, sizeof(sip), "%u.%u.%u.%u", - ((ip >> 24) & 0xff), - ((ip >> 16) & 0xff), - ((ip >> 8) & 0xff), - (ip & 0xff)); - return (sip); + summary(); + errno = save_errno; } -static in_addr_t -countmask(unsigned m) +static const char * +ip2str(struct sockaddr *addr) { - in_addr_t mask; + static char sip[64]; - if (m == 0) { - mask = 0x0; - } else { - mask = 1 << (32 - m); - mask--; - mask = ~mask; + if (getnameinfo(addr, addr->sa_len, sip, sizeof(sip), + NULL, 0, NI_NUMERICHOST) == 0) + return (sip); + + return ("Unknown"); +} + +static struct sockaddr_storage +countmask(struct sockaddr* addr, int mask) /* also normalizes addr */ +{ + struct sockaddr_storage ss; + int i, alen; + unsigned char *mp, *ap; + + bzero(&ss, sizeof(ss)); + ss.ss_family = addr->sa_family; + ss.ss_len = addr->sa_len; + + switch (addr->sa_family) { + case AF_INET: + alen = 4; /* 32 bits */ + ap = (unsigned char*)&((struct sockaddr_in*)addr)->sin_addr.s_addr; + mp = (unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr; + break; + case AF_INET6: + alen = 16; /* 128 bits */ + ap = (unsigned char*)&((struct sockaddr_in6*)addr)->sin6_addr.s6_addr; + mp = (unsigned char*)&((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr; + break; + default: + g_gate_xlog("Unknown address family in countmask"); + return ss; + } + + i = 0; + while (mask > 0 && i < alen) { + if (mask < 8) { + mp[i] = ~(0xff >> mask); + ap[i] &= mp[i]; + } else + mp[i] = 0xff; + i++; + mask -= 8; + } + while (i < alen) { /* zero out remaining bits of addr */ + ap[i] = 0; + i++; } - return (mask); + + return (ss); } static void line_parse(char *line, unsigned lineno) { struct ggd_export *ex; - char *word, *path, *sflags; - unsigned flags, i, vmask; - in_addr_t ip, mask; + char *pmask, *word, *path, *sflags; + unsigned flags, i; + int vmask; + struct addrinfo *res, *p; - ip = mask = flags = vmask = 0; + flags = vmask = 0; path = NULL; sflags = NULL; + pmask = NULL; for (i = 0, word = strtok(line, " \t"); word != NULL; i++, word = strtok(NULL, " \t")) { switch (i) { case 0: /* IP address or host name */ - ip = g_gate_str2ip(strsep(&word, "/")); - if (ip == INADDR_NONE) { + // bzero(&hints, sizeof(hints)); + // hints.ai_family = PF_UNSPEC; + // hints.ai_socktype = SOCKET_STREAM; + if (getaddrinfo(strsep(&word, "/"), NULL, &hints, + &res) != 0) g_gate_xlog("Invalid IP/host name at line %u.", lineno); - } - ip = ntohl(ip); - if (word == NULL) - vmask = 32; - else { - errno = 0; - vmask = strtoul(word, NULL, 10); - if (vmask == 0 && errno != 0) { - g_gate_xlog("Invalid IP mask value at " - "line %u.", lineno); - } - if ((unsigned)vmask > 32) { - g_gate_xlog("Invalid IP mask value at line %u.", - lineno); - } - } - mask = countmask(vmask); + pmask = word; break; case 1: /* flags */ if (strcasecmp("rd", word) == 0 || @@ -209,22 +269,46 @@ if (i != 3) g_gate_xlog("Too few arguments at line %u.", lineno); - ex = malloc(sizeof(*ex)); - if (ex == NULL) - g_gate_xlog("No enough memory."); - ex->e_path = strdup(path); - if (ex->e_path == NULL) - g_gate_xlog("No enough memory."); - - /* Made 'and' here. */ - ex->e_ip = (ip & mask); - ex->e_mask = mask; - ex->e_flags = flags; + p = res; + while (p) { + ex = malloc(sizeof(*ex)); + if (ex == NULL) + g_gate_xlog("Not enough memory."); + ex->e_path = strdup(path); + if (ex->e_path == NULL) + g_gate_xlog("Not enough memory."); + + if (pmask == NULL && p->ai_family == AF_INET6) + vmask = 128; + else if (pmask == NULL) + vmask = 32; + else { + errno = 0; + vmask = strtoul(pmask, NULL, 10); + if (vmask == 0 && errno != 0) { + g_gate_xlog("Invalid IP mask value at " + "line %u.", lineno); + } + } + + if ((vmask > 32 && p->ai_family == AF_INET) || + (vmask > 128 && p->ai_family == AF_INET6)) + g_gate_xlog("Invalid IP mask value at line %u", + lineno); + + memcpy(&ex->e_addr, p->ai_addr, p->ai_addrlen); + ex->e_mask = countmask((struct sockaddr*)&ex->e_addr, vmask); + ex->e_flags = flags; - SLIST_INSERT_HEAD(&exports, ex, e_next); + SLIST_INSERT_HEAD(&exports, ex, e_next); + + g_gate_log(LOG_DEBUG, "Added %s/%u %s %s to exports list.", + ip2str((struct sockaddr*)&ex->e_addr), vmask, path, sflags); + + p = p->ai_next; + } - g_gate_log(LOG_DEBUG, "Added %s/%u %s %s to exports list.", - ip2str(ex->e_ip), vmask, path, sflags); + freeaddrinfo(res); } static void @@ -302,12 +386,12 @@ exports_check(struct ggd_export *ex, struct g_gate_cinit *cinit, struct ggd_connection *conn) { - char ipmask[32]; /* 32 == strlen("xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx")+1 */ + char ipmask[80]; /* 80 == strlen("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx")+1 */ int error = 0, flags; - strlcpy(ipmask, ip2str(ex->e_ip), sizeof(ipmask)); + strlcpy(ipmask, ip2str((struct sockaddr*)&ex->e_addr), sizeof(ipmask)); strlcat(ipmask, "/", sizeof(ipmask)); - strlcat(ipmask, ip2str(ex->e_mask), sizeof(ipmask)); + strlcat(ipmask, ip2str((struct sockaddr*)&ex->e_mask), sizeof(ipmask)); if ((cinit->gc_flags & GGATE_FLAG_RDONLY) != 0) { if (ex->e_flags == O_WRONLY) { g_gate_log(LOG_WARNING, "Read-only access requested, " @@ -355,17 +439,53 @@ return (0); } +static int +mask_compare(struct sockaddr *a, struct sockaddr *b, struct sockaddr *m) { + unsigned char *ap, *bp, *mp; + int alen; + if (a->sa_family != m->sa_family || b->sa_family != m->sa_family) + return (0); + + switch (m->sa_family) { + case AF_INET: + alen = 4; + ap = (unsigned char*)&((struct sockaddr_in*)a)->sin_addr.s_addr; + bp = (unsigned char*)&((struct sockaddr_in*)b)->sin_addr.s_addr; + mp = (unsigned char*)&((struct sockaddr_in*)m)->sin_addr.s_addr; + break; + case AF_INET6: + alen = 16; + ap = (unsigned char*)&((struct sockaddr_in6*)a)->sin6_addr.s6_addr; + bp = (unsigned char*)&((struct sockaddr_in6*)b)->sin6_addr.s6_addr; + mp = (unsigned char*)&((struct sockaddr_in6*)m)->sin6_addr.s6_addr; + break; + default: + return (0); + } + + while (alen > 0) { + if ((*ap & *mp) != (*bp & *mp)) + return (0); + + ap++; + bp++; + mp++; + alen--; + } + + return (1); +} + static struct ggd_export * exports_find(struct sockaddr *s, struct g_gate_cinit *cinit, struct ggd_connection *conn) { struct ggd_export *ex; - in_addr_t ip; int error; - ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); SLIST_FOREACH(ex, &exports, e_next) { - if ((ip & ex->e_mask) != ex->e_ip) { + if (!mask_compare(s, (struct sockaddr*)&ex->e_addr, + (struct sockaddr*)&ex->e_mask)) { g_gate_log(LOG_DEBUG, "exports[%s]: IP mismatch.", ex->e_path); continue; @@ -384,7 +504,7 @@ } } g_gate_log(LOG_WARNING, "Unauthorized connection from: %s.", - ip2str(ip)); + ip2str(s)); errno = EPERM; return (NULL); } @@ -404,7 +524,8 @@ LIST_REMOVE(conn, c_next); g_gate_log(LOG_NOTICE, "Connection from %s [%s] removed.", - ip2str(conn->c_srcip), conn->c_path); + ip2str((struct sockaddr*)&conn->c_srcaddr), + conn->c_path); close(conn->c_diskfd); close(conn->c_sendfd); close(conn->c_recvfd); @@ -430,7 +551,6 @@ connection_new(struct g_gate_cinit *cinit, struct sockaddr *s, int sfd) { struct ggd_connection *conn; - in_addr_t ip; /* * First, look for old connections. @@ -449,8 +569,7 @@ return (NULL); } conn->c_token = cinit->gc_token; - ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); - conn->c_srcip = ip; + memcpy(&conn->c_srcaddr, s, s->sa_len); conn->c_sendfd = conn->c_recvfd = -1; if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) conn->c_sendfd = sfd; @@ -461,7 +580,7 @@ time(&conn->c_birthtime); conn->c_flags = cinit->gc_flags; LIST_INSERT_HEAD(&connections, conn, c_next); - g_gate_log(LOG_DEBUG, "Connection created [%s, %s].", ip2str(ip), + g_gate_log(LOG_DEBUG, "Connection created [%s, %s].", ip2str(s), conn->c_path); return (conn); } @@ -470,13 +589,10 @@ connection_add(struct ggd_connection *conn, struct g_gate_cinit *cinit, struct sockaddr *s, int sfd) { - in_addr_t ip; - - ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) { if (conn->c_sendfd != -1) { g_gate_log(LOG_WARNING, - "Send socket already exists [%s, %s].", ip2str(ip), + "Send socket already exists [%s, %s].", ip2str(s), conn->c_path); return (EEXIST); } @@ -485,12 +601,12 @@ if (conn->c_recvfd != -1) { g_gate_log(LOG_WARNING, "Receive socket already exists [%s, %s].", - ip2str(ip), conn->c_path); + ip2str(s), conn->c_path); return (EEXIST); } conn->c_recvfd = sfd; } - g_gate_log(LOG_DEBUG, "Connection added [%s, %s].", ip2str(ip), + g_gate_log(LOG_DEBUG, "Connection added [%s, %s].", ip2str(s), conn->c_path); return (0); } @@ -505,11 +621,12 @@ LIST_REMOVE(conn, c_next); g_gate_log(LOG_DEBUG, "Connection removed [%s %s].", - ip2str(conn->c_srcip), conn->c_path); + ip2str((struct sockaddr*)&conn->c_srcaddr), conn->c_path); if (conn->c_sendfd != -1) close(conn->c_sendfd); if (conn->c_recvfd != -1) close(conn->c_recvfd); + close(conn->c_diskfd); free(conn->c_path); free(conn); } @@ -642,11 +759,12 @@ } else if (data != sizeof(req->r_hdr)) { g_gate_xlog("Malformed hdr packet received."); } - g_gate_log(LOG_DEBUG, "Received hdr packet."); g_gate_swap2h_hdr(&req->r_hdr); + g_gate_log(LOG_DEBUG, "Received hdr packet (seq=%llu).", req->r_seq); + conn->c_recv_seq = req->r_seq; - g_gate_log(LOG_DEBUG, "%s: offset=%jd length=%u", __func__, - (intmax_t)req->r_offset, (unsigned)req->r_length); + g_gate_log(LOG_DEBUG, "%s: (offset=%jd length=%u", __func__, + req->r_seq, (intmax_t)req->r_offset, (unsigned)req->r_length); /* * Allocate memory for data. @@ -657,8 +775,8 @@ * Receive data to write for WRITE request. */ if (req->r_cmd == GGATE_CMD_WRITE) { - g_gate_log(LOG_DEBUG, "Waiting for %u bytes of data...", - req->r_length); + g_gate_log(LOG_DEBUG, "Waiting for %u bytes of data (seq=%llu).", + req->r_length, req->r_seq); data = g_gate_recv(fd, req->r_data, req->r_length, MSG_WAITALL); if (data == -1) { @@ -795,8 +913,9 @@ g_gate_xlog("Error while sending hdr packet: %s.", strerror(errno)); } - g_gate_log(LOG_DEBUG, "Sent hdr packet."); g_gate_swap2h_hdr(&req->r_hdr); + g_gate_log(LOG_DEBUG, "Sent hdr packet (seq=%llu).", req->r_hdr.gh_seq); + conn->c_send_seq = req->r_seq; if (req->r_data != NULL) { data = g_gate_send(fd, req->r_data, req->r_length, 0); if (data != (ssize_t)req->r_length) { @@ -815,10 +934,7 @@ static void log_connection(struct sockaddr *from) { - in_addr_t ip; - - ip = htonl(((struct sockaddr_in *)(void *)from)->sin_addr.s_addr); - g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(ip)); + g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(from)); } static int @@ -940,36 +1056,82 @@ int main(int argc, char *argv[]) { - struct sockaddr_in serv; - struct sockaddr from; + SLIST_HEAD(, ggd_listen) listens = SLIST_HEAD_INITIALIZER(&listens); + struct ggd_listen *cl, *nl; + struct addrinfo *res, *p; + struct sockaddr_storage from; + struct protoent *pent; socklen_t fromlen; - int sfd, tmpsfd; - unsigned port; + int maxfd, tmpsfd; + fd_set listenfds; + const char *port; + int foreground = 0; + + bzero(&hints, sizeof(hints)); + hints.ai_family = PF_UNSPEC; /* Bind to all address families */ + hints.ai_flags = AI_PASSIVE; + hints.ai_socktype = SOCK_STREAM; - bindaddr = htonl(INADDR_ANY); - port = G_GATE_PORT; + port = G_GATE_PORT_STR; for (;;) { int ch; - ch = getopt(argc, argv, "a:hnp:R:S:v"); + ch = getopt(argc, argv, "a:fhnp:R:S:vF:P:"); if (ch == -1) break; switch (ch) { case 'a': - bindaddr = g_gate_str2ip(optarg); - if (bindaddr == INADDR_NONE) { + nl = malloc(sizeof(*nl)); + bzero(nl, sizeof(*nl)); + nl->l_name = optarg; + /* delay resolution until we know port number */ + SLIST_INSERT_HEAD(&listens, nl, l_next); + break; + case 'n': + nagle = 0; + break; + case 'F': + switch (*optarg) { + case '4': + hints.ai_family = PF_INET; + break; + case '6': + hints.ai_family = PF_INET6; + break; + case '0': + hints.ai_family = PF_UNSPEC; + break; + default: + usage(); + } + break; + case 'P': + if (optarg && (pent = getprotobyname(optarg))) + ai_protocol = pent->p_proto; + else { + ai_protocol = 0; + /* errx(EXIT_FAILURE, - "Invalid IP/host name to bind to."); + "protocol %s does not exist.", optarg); + */ } break; - case 'n': + case '4': + hints.ai_family = PF_INET; + break; + case '6': + hints.ai_family = PF_INET6; + break; + case 'x': + /* "man netstat" shows AF_INET has sctp. */ + hints.ai_family = PF_INET; + hints.ai_family = PF_INET6; /* test... */ + /* hints.ai_protocol = IPPROTO_SCTP; */ + ai_protocol = IPPROTO_SCTP; nagle = 0; break; case 'p': - errno = 0; - port = strtoul(optarg, NULL, 10); - if (port == 0 && errno != 0) - errx(EXIT_FAILURE, "Invalid port."); + port = optarg; break; case 'R': errno = 0; @@ -983,8 +1145,12 @@ if (sndbuf == 0 && errno != 0) errx(EXIT_FAILURE, "Invalid sndbuf."); break; + case 'f': + foreground = 1; + break; case 'v': g_gate_verbose++; + foreground = 1; break; case 'h': default: @@ -998,47 +1164,120 @@ exports_file = argv[0]; exports_get(); - if (!g_gate_verbose) { + if (SLIST_EMPTY(&listens)) { + if (getaddrinfo(NULL, port, &hints, &res)) + g_gate_xlog("Cannot get passive address: %s", + strerror(errno)); + + p = res; + while (p) { + nl = malloc(sizeof(*nl)); + bzero(nl, sizeof(*nl)); + memcpy(&nl->l_addr, p->ai_addr, p->ai_addrlen); + SLIST_INSERT_HEAD(&listens, nl, l_next); + + p = p->ai_next; + } + freeaddrinfo(res); + } else { + /* Bind to some specific addresses */ + SLIST_FOREACH(cl, &listens, l_next) { + if (getaddrinfo(cl->l_name, port, &hints, &res)) + g_gate_xlog("Invalid IP/host name to bind to: " + "%s", cl->l_name); + + /* Re-use current list entry for first match, add + * new ones after that */ + p = res; + nl = cl; + while (p) { + if (p != res) { + nl = malloc(sizeof(*nl)); + bzero(nl, sizeof(*nl)); + } + + memcpy(&nl->l_addr, p->ai_addr, p->ai_addrlen); + + if (p != res) { + SLIST_INSERT_HEAD(&listens, nl, l_next); + } + p = p->ai_next; + } + freeaddrinfo(res); + } + } + + /* Actually create sockets and bind to them */ + maxfd = 0; + SLIST_FOREACH(cl, &listens, l_next) { + cl->l_fd = socket(cl->l_addr.ss_family, + hints.ai_socktype, ai_protocol); + if (cl->l_fd == -1) + g_gate_xlog("Cannot open stream socket: %s.", + strerror(errno)); + g_gate_socket_settings(cl->l_fd); + + if (bind(cl->l_fd, (struct sockaddr *)&cl->l_addr, + cl->l_addr.ss_len) == -1) { + g_gate_log(LOG_WARNING, "bind(): %s.", strerror(errno)); + continue; + } + if (listen(cl->l_fd, 5) == -1) + g_gate_xlog("listen(): %s.", strerror(errno)); + + if (maxfd <= cl->l_fd) + maxfd = cl->l_fd + 1; + + g_gate_log(LOG_INFO, "Listen on address: %s (%s).", + ip2str((struct sockaddr *)&cl->l_addr), port); + } + + signal(SIGINFO, summaryx); + if (!foreground) { /* Run in daemon mode. */ if (daemon(0, 0) == -1) g_gate_xlog("Cannot daemonize: %s", strerror(errno)); } signal(SIGCHLD, SIG_IGN); - - sfd = socket(AF_INET, SOCK_STREAM, 0); - if (sfd == -1) - g_gate_xlog("Cannot open stream socket: %s.", strerror(errno)); - bzero(&serv, sizeof(serv)); - serv.sin_family = AF_INET; - serv.sin_addr.s_addr = bindaddr; - serv.sin_port = htons(port); - - g_gate_socket_settings(sfd); - - if (bind(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1) - g_gate_xlog("bind(): %s.", strerror(errno)); - if (listen(sfd, 5) == -1) - g_gate_xlog("listen(): %s.", strerror(errno)); - - g_gate_log(LOG_INFO, "Listen on port: %d.", port); - signal(SIGHUP, huphandler); for (;;) { - fromlen = sizeof(from); - tmpsfd = accept(sfd, &from, &fromlen); - if (tmpsfd == -1) - g_gate_xlog("accept(): %s.", strerror(errno)); + FD_ZERO(&listenfds); + SLIST_FOREACH(cl, &listens, l_next) { + FD_SET(cl->l_fd, &listenfds); + } + + select(maxfd, &listenfds, NULL, NULL, NULL); if (got_sighup) { got_sighup = 0; exports_get(); } - if (!handshake(&from, tmpsfd)) - close(tmpsfd); + SLIST_FOREACH(cl, &listens, l_next) { + if (!FD_ISSET(cl->l_fd, &listenfds)) + continue; + + fromlen = sizeof(from); + tmpsfd = accept(cl->l_fd, (struct sockaddr*)&from, + &fromlen); + + if (tmpsfd == -1) { + g_gate_log(LOG_WARNING, "accept(): %s.", + strerror(errno)); + continue; + } + + if (!handshake((struct sockaddr*)&from, tmpsfd)) + close(tmpsfd); + } + } + while (!SLIST_EMPTY(&listens)) { + cl = SLIST_FIRST(&listens); + close(cl->l_fd); + SLIST_REMOVE_HEAD(&listens, l_next); + free(cl); } - close(sfd); exit(EXIT_SUCCESS); } Index: shared/ggate.c =================================================================== RCS file: /home/ncvs/src/sbin/ggate/shared/ggate.c,v --- shared/ggate.c 25 Nov 2008 02:59:29 -0000 1.9.6.1 +++ shared/ggate.c 26 Mar 2009 06:35:28 -0000 @@ -389,21 +389,3 @@ exit(EXIT_SUCCESS); } #endif /* LIBGEOM */ - -in_addr_t -g_gate_str2ip(const char *str) -{ - struct hostent *hp; - in_addr_t ip; - - ip = inet_addr(str); - if (ip != INADDR_NONE) { - /* It is a valid IP address. */ - return (ip); - } - /* Check if it is a valid host name. */ - hp = gethostbyname(str); - if (hp == NULL) - return (INADDR_NONE); - return (((struct in_addr *)(void *)hp->h_addr)->s_addr); -} Index: shared/ggate.h =================================================================== RCS file: /home/ncvs/src/sbin/ggate/shared/ggate.h,v --- shared/ggate.h 25 Nov 2008 02:59:29 -0000 1.5.6.1 +++ shared/ggate.h 26 Mar 2009 06:35:28 -0000 @@ -33,6 +33,7 @@ #include #define G_GATE_PORT 3080 +#define G_GATE_PORT_STR "3080" #define G_GATE_RCVBUF 131072 #define G_GATE_SNDBUF 131072 @@ -110,7 +111,6 @@ #ifdef LIBGEOM void g_gate_list(int unit, int verbose); #endif -in_addr_t g_gate_str2ip(const char *str); /* * g_gate_swap2h_* - functions swap bytes to host byte order (from big endian).