From craig@tobuj.gank.org  Fri Nov 11 01:08:54 2005
Return-Path: <craig@tobuj.gank.org>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id F126616A41F
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 11 Nov 2005 01:08:54 +0000 (GMT)
	(envelope-from craig@tobuj.gank.org)
Received: from ion.gank.org (ion.gank.org [69.55.238.164])
	by mx1.FreeBSD.org (Postfix) with ESMTP id A56E743D48
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 11 Nov 2005 01:08:54 +0000 (GMT)
	(envelope-from craig@tobuj.gank.org)
Received: by ion.gank.org (mail, from userid 1001)
	id 1E7032D2D3; Thu, 10 Nov 2005 19:08:11 -0600 (CST)
Message-Id: <20051111010811.1E7032D2D3@ion.gank.org>
Date: Thu, 10 Nov 2005 19:08:11 -0600 (CST)
From: Craig Boston <craig@tobuj.gank.org>
Reply-To: Craig Boston <craig@tobuj.gank.org>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: [PATCH] IPv6 support for ggate
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         88821
>Category:       bin
>Synopsis:       [patch] IPv6 support for ggated(8)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    pjd
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Fri Nov 11 01:10:16 GMT 2005
>Closed-Date:    
>Last-Modified:  Thu Mar 26 07:00:11 UTC 2009
>Originator:     Craig Boston
>Release:        FreeBSD 6.0-STABLE i386
>Organization:
I wish I had some
>Environment:
System: FreeBSD hostname.gank.org 6.0-STABLE FreeBSD 6.0-STABLE #0: Sat Nov 5 20:59:05 CST 2005 root@hostname.gank.org:/compile/obj/compile/src/sys/HOSTNAME i386

>Description:
	Adds full IPv6 support to ggated and complete address family
	independence to ggatec.

>How-To-Repeat:
	n/a

>Fix:

--- ggate_ipv6_2.patch begins here ---
diff -ruN ggate.orig/ggatec/ggatec.c ggate/ggatec/ggatec.c
--- ggate.orig/ggatec/ggatec.c	Thu Nov 10 18:53:21 2005
+++ ggate/ggatec/ggatec.c	Thu Nov 10 18:53:25 2005
@@ -50,6 +50,7 @@
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
+#include <netdb.h>
 
 #include <geom/gate/g_gate.h>
 #include "ggate.h"
@@ -63,7 +64,7 @@
 static unsigned flags = 0;
 static int force = 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;
@@ -244,37 +245,55 @@
 	struct g_gate_version ver;
 	struct g_gate_cinit cinit;
 	struct g_gate_sinit sinit;
-	struct sockaddr_in serv;
+	struct addrinfo hints;
+	struct addrinfo *res, *p;
 	int sfd;
 
 	/*
 	 * 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);
+	bzero(&hints, sizeof(hints));
+	hints.ai_family = PF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	sfd = -1;
+	if (getaddrinfo(host, port, &hints, &res) != 0) {
+		g_gate_log(LOG_DEBUG, "Invalid IP/host name or port: %s (%s).",
+		    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, 0);
+			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.
@@ -454,7 +473,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)
@@ -535,9 +554,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)
diff -ruN ggate.orig/ggated/ggated.8 ggate/ggated/ggated.8
--- ggate.orig/ggated/ggated.8	Thu Nov 10 18:53:21 2005
+++ ggate/ggated/ggated.8	Thu Nov 10 18:53:25 2005
@@ -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.
diff -ruN ggate.orig/ggated/ggated.c ggate/ggated/ggated.c
--- ggate.orig/ggated/ggated.c	Thu Nov 10 18:53:21 2005
+++ ggate/ggated/ggated.c	Thu Nov 10 18:53:25 2005
@@ -44,6 +44,7 @@
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
+#include <netdb.h>
 #include <signal.h>
 #include <assert.h>
 #include <err.h>
@@ -68,7 +69,7 @@
 	time_t		 c_birthtime;
 	char		*c_path;
 	uint64_t	 c_token;
-	in_addr_t	 c_srcip;
+	struct sockaddr_storage	 c_srcaddr;
 	LIST_ENTRY(ggd_connection) c_next;
 };
 
@@ -83,16 +84,22 @@
 #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 TAILQ_HEAD(, ggd_request) inqueue = TAILQ_HEAD_INITIALIZER(inqueue);
 static TAILQ_HEAD(, ggd_request) outqueue = TAILQ_HEAD_INITIALIZER(outqueue);
@@ -115,71 +122,88 @@
 	exit(EXIT_FAILURE);
 }
 
-static char *
-ip2str(in_addr_t ip)
+static const char *
+ip2str(struct sockaddr *addr)
 {
-	static char sip[16];
+	static char sip[64];
+
+	if (getnameinfo(addr, addr->sa_len, sip, sizeof(sip),
+	    NULL, 0, NI_NUMERICHOST) == 0)
+		return (sip);
 
-	snprintf(sip, sizeof(sip), "%u.%u.%u.%u",
-	    ((ip >> 24) & 0xff),
-	    ((ip >> 16) & 0xff),
-	    ((ip >> 8) & 0xff),
-	    (ip & 0xff));
-	return (sip);
+	return ("Unknown");
 }
 
-static in_addr_t
-countmask(unsigned m)
+static struct sockaddr_storage
+countmask(struct sockaddr* addr, int mask)	/* also normalizes addr */
 {
-	in_addr_t mask;
+	struct sockaddr_storage ss;
+	int i, alen;
+	unsigned char *mp, *ap;
 
-	if (m == 0) {
-		mask = 0x0;
-	} else {
-		mask = 1 << (32 - m);
-		mask--;
-		mask = ~mask;
+	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");
+	}
+
+	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;
 	}
-	return (mask);
+	while (i < alen) {		/* zero out remaining bits of addr */
+		ap[i] = 0;
+		i++;
+	}
+
+	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 hints, *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 = SOCK_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 +233,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);
 
-	g_gate_log(LOG_DEBUG, "Added %s/%u %s %s to exports list.",
-	    ip2str(ex->e_ip), vmask, path, sflags);
+		p = p->ai_next;
+	}
+
+	freeaddrinfo(res);
 }
 
 static void
@@ -302,12 +350,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 +403,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 +468,7 @@
 		}
 	}
 	g_gate_log(LOG_WARNING, "Unauthorized connection from: %s.",
-	    ip2str(ip));
+	    ip2str(s));
 	errno = EPERM;
 	return (NULL);
 }
@@ -404,7 +488,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 +515,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 +533,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 +544,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 +553,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 +565,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,7 +585,7 @@
 
 	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)
@@ -815,10 +895,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,14 +1017,16 @@
 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 hints, *res, *p;
+	struct sockaddr_storage from;
 	socklen_t fromlen;
-	int sfd, tmpsfd;
-	unsigned port;
+	int maxfd, tmpsfd;
+	fd_set listenfds;
+	const char *port;
 
-	bindaddr = htonl(INADDR_ANY);
-	port = G_GATE_PORT;
+	port = G_GATE_PORT_STR;
 	for (;;) {
 		int ch;
 
@@ -956,20 +1035,17 @@
 			break;
 		switch (ch) {
 		case 'a':
-			bindaddr = g_gate_str2ip(optarg);
-			if (bindaddr == INADDR_NONE) {
-				errx(EXIT_FAILURE,
-				    "Invalid IP/host name to bind to.");
-			}
+			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 '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;
@@ -998,6 +1074,79 @@
 		exports_file = argv[0];
 	exports_get();
 
+	if (SLIST_EMPTY(&listens)) {
+		/* Bind to all address families */
+		bzero(&hints, sizeof(hints));
+		hints.ai_family = PF_UNSPEC;
+		hints.ai_flags = AI_PASSIVE;
+		hints.ai_socktype = SOCK_STREAM;
+		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) {
+			bzero(&hints, sizeof(hints));
+			hints.ai_family = PF_UNSPEC;
+			hints.ai_socktype = SOCK_STREAM;
+			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, SOCK_STREAM, 0);
+		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_xlog("bind(): %s.", strerror(errno));
+		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);
+	}
+
 	if (!g_gate_verbose) {
 		/* Run in daemon mode. */
 		if (daemon(0, 0) == -1)
@@ -1005,40 +1154,44 @@
 	}
 
 	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);
 }
diff -ruN ggate.orig/shared/ggate.c ggate/shared/ggate.c
--- ggate.orig/shared/ggate.c	Thu Nov 10 18:53:21 2005
+++ ggate/shared/ggate.c	Thu Nov 10 18:53:25 2005
@@ -375,21 +375,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);
-}
diff -ruN ggate.orig/shared/ggate.h ggate/shared/ggate.h
--- ggate.orig/shared/ggate.h	Thu Nov 10 18:53:21 2005
+++ ggate/shared/ggate.h	Thu Nov 10 18:53:25 2005
@@ -33,6 +33,7 @@
 #include <stdarg.h>
 
 #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).
--- ggate_ipv6_2.patch ends here ---


>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->pjd 
Responsible-Changed-By: pjd 
Responsible-Changed-When: Fri Nov 11 11:54:22 GMT 2005 
Responsible-Changed-Why:  
This one is for me. Thanks. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=88821 

From: Craig Boston <craig@tobuj.gank.org>
To: bug-followup@freebsd.org
Cc:  
Subject: Re: bin/88821 : [PATCH] IPv6 support for ggate
Date: Thu, 17 Nov 2005 21:55:37 -0600

 Argh, I could have sworn I compiled this will full warnings enabled
 before submitting.
 
 In countmask(), this section:
 
 +	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");
 +	}
 
 There should be an extra line after the g_gate_xlog:
 
 +			return ss;
 
 Craig

From: Yoshihiro Ota <ota@j.email.ne.jp>
To: bug-followup@FreeBSD.org
Cc: craig@tobuj.gank.org, pjd@FreeBSD.org
Subject: Re: bin/88821: [patch] IPv6 support for ggated(8)
Date: Thu, 26 Mar 2009 02:50:38 -0400

 This is a multi-part message in MIME format.
 
 --Multipart=_Thu__26_Mar_2009_02_50_38_-0400_pmHen6O6P11Wj646
 Content-Type: text/plain; charset=US-ASCII
 Content-Transfer-Encoding: 7bit
 
 I also implemented SCTP on top of IPv6 support patch.
 Passing "-P sctp -n" to both ggated and ggated will enable it.
 
 -n option is added to disable NO_TCP_DELAY.
 -f option is added to ggated and ggatec create/rescue to run in foregraound
 -F option is added; -F4 to specify IPv4, -F6 for IPv6, and -F0 for both. 
 -P option is added to support SCTP; it uses /etc/protocols so that if
 protocol passed to the program is really applicable, other protocols may
 work as well.
 
 
 If the patch is to be commited, I can also send its man page updates.
 
 Thanks,
 Hiro
 
 --Multipart=_Thu__26_Mar_2009_02_50_38_-0400_pmHen6O6P11Wj646
 Content-Type: text/x-diff;
  name="ggate-ipv6-sctp-debug.diff"
 Content-Disposition: attachment;
  filename="ggate-ipv6-sctp-debug.diff"
 Content-Transfer-Encoding: 7bit
 
 ? .git
 ? .hg
 ? .hgignore
 ? .hgtags
 ? ggatec/ipv6-2.diff
 ? ggated/ggated-seq.diff
 ? ggated/ipv6-2.diff
 ? ggated/note.txt
 Index: ggatec/ggatec.c
 ===================================================================
 RCS file: /home/ncvs/src/sbin/ggate/ggatec/ggatec.c,v
 retrieving revision 1.7.6.1
 diff -u -u -r1.7.6.1 ggatec.c
 --- 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 <sys/bio.h>
  #include <netinet/in.h>
  #include <netinet/tcp.h>
 +#include <netinet/sctp.h>
  #include <arpa/inet.h>
 +#include <netdb.h>
  
  #include <geom/gate/g_gate.h>
  #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 <ro|wo|rw>] [-p port] "
 +	fprintf(stderr, "usage: %s create [-fnv] [-o <ro|wo|rw>] [-p port] "
  	    "[-q queue_size] [-R rcvbuf] [-S sndbuf] [-s sectorsize] "
 -	    "[-t timeout] [-u unit] <host> <path>\n", getprogname());
 -	fprintf(stderr, "       %s rescue [-nv] [-o <ro|wo|rw>] [-p port] "
 -	    "[-R rcvbuf] [-S sndbuf] <-u unit> <host> <path>\n", getprogname());
 +	    "[-t timeout] [-u unit] [-F <0|4|6>] [-P protocol] <host> <path>\n",
 +	    getprogname());
 +	fprintf(stderr, "       %s rescue [-fnv] [-o <ro|wo|rw>] [-p port] "
 +	    "[-R rcvbuf] [-S sndbuf] [-F <0|4|6>] [-P protocol] <-u unit> <host> <path>\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
 retrieving revision 1.4.18.1
 diff -u -u -r1.4.18.1 ggated.8
 --- 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
 retrieving revision 1.9.6.1
 diff -u -u -r1.9.6.1 ggated.c
 --- 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 <sys/bio.h>
  #include <netinet/in.h>
  #include <netinet/tcp.h>
 +#include <netinet/sctp.h>
  #include <arpa/inet.h>
 +#include <netdb.h>
  #include <signal.h>
  #include <assert.h>
  #include <err.h>
 @@ -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
 retrieving revision 1.9.6.1
 diff -u -u -r1.9.6.1 ggate.c
 --- 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
 retrieving revision 1.5.6.1
 diff -u -u -r1.5.6.1 ggate.h
 --- 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 <stdarg.h>
  
  #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).
 
 --Multipart=_Thu__26_Mar_2009_02_50_38_-0400_pmHen6O6P11Wj646--
>Unformatted:
