Dear all, having recently pushed on my responsibility two error corrections concerning 'syslogd', I am now able to offer for discussion a change set that implements full IPv6 capability in 'syslogd'. As the changes are now more intrusive, I would like comments before pushing any changes at all. Suffice it to say that the present test script verifies dual stacked functionality. (Just remember to have a remote system listening to your forwarded messages!)
Let me mention the changes and techniques used:
1. New command line options for INET transport:
-4/--ipv4 (default), -6/--ipv6, --ipany
Using '--ipany' corresponds to AF_UNSPEC,
allowing the resolver to use A and AAAA
records.
The default (--ipv4) is still set in order not to break
present day installations. In itself is depends on a
single assignment, but a change might upset the flow
for a remote loghost that resolves for IPv4 and IPv6,
but only listens for syslog/IPv4.
2. A new option '-b/--bind=ADDR' allowing at most
a dual stacked listening pair. Either a single
address is given, or a host name is resolved.
The latter results in a single address with
either of '--ipv4/--ipv6', but one address
for each address family, should the given host
name possess an A as well as an AAAA record.
3. The present code uses a single file destriptor
'int finet' to hold the AF_INET descriptor.
This is now replaced with a pair 'int finet[2]',
one for each address family, and each socket
being single stacked (for better portability).
4. The option '-r/--inet' is now independent of
the implicit option '--forward', since without
the former switch being active, the server will
produce on-demand sockets in order to forward
messages to remote loghosts. Unless, that is,
the local service was started using '--no-forward'!
This last change was implemented in rev 13af03e2 (present HEAD),
with the present changes in mind.
The code changes are surprisingly small to accomplish these
alterations, but the multitude of setups needed to test
all possible use cases calls for great care during evaluation.
Let me state once more, that I have with intention chosen
to code the server into using two single stacked sockets,
one each for AF_INET and AF_INET6, in order to achieve
complete predictability regarding the option IPV6_V6ONLY,
be it active or not on the running system. Remember that
its value can not be changed on a running OpenBSD system,
and that it is almost always set in OpenBSD!
Take your time when reading this draft. The attached patch
can be tested, except that 'unused-parameter.h' needs to
be manually copied from GNUlib to our 'lib/'.
Best regards
Mats
diff --git a/src/syslogd.c b/src/syslogd.c
index a09586c..73bc3d3 100644
--- a/src/syslogd.c
+++ b/src/syslogd.c
@@ -263,16 +263,20 @@ static void dbg_printf (const char *, ...);
void trigger_restart (int);
static void add_funix (const char *path);
static int create_unix_socket (const char *path);
-static int create_inet_socket (int af);
+static void create_inet_socket (int af, int fd46[2]);
char *LocalHostName; /* Our hostname. */
char *LocalDomain; /* Our local domain name. */
+char *BindAddress = NULL; /* Binding address for INET listeners.
+ * The default produces a wildcard. */
char addrstr[INET6_ADDRSTRLEN]; /* Common address presentation. */
char addrname[NI_MAXHOST]; /* Common name lookup. */
int usefamily = AF_INET; /* Address family for INET services.
* Each of the values `AF_INET' and `AF_INET6'
* produces a single-stacked server. */
-int finet = -1; /* Internet datagram socket fd. */
+int finet[2] = {-1, -1}; /* Internet datagram socket fd. */
+#define IU_FD_IP4 0 /* Indices for the address families. */
+#define IU_FD_IP6 1
int fklog = -1; /* Kernel log device fd. */
char *LogPortText = "syslog"; /* Service/port for INET connections. */
int Initialized; /* True when we are initialized. */
@@ -301,7 +305,8 @@ enum {
OPT_NO_FORWARD = 256,
OPT_NO_KLOG,
OPT_NO_UNIXAF,
- OPT_PIDFILE
+ OPT_PIDFILE,
+ OPT_IPANY
};
static struct argp_option argp_options[] = {
@@ -316,6 +321,10 @@ static struct argp_option argp_options[] = {
{"hop", 'h', NULL, 0, "forward messages from remote hosts", GRP+1},
{"inet", 'r', NULL, 0, "receive remote messages via internet domain socket",
GRP+1},
+ {"ipv4", '4', NULL, 0, "restrict to IPv4 transport (default)", GRP+1},
+ {"ipv6", '6', NULL, 0, "restrict to IPv6 transport", GRP+1},
+ {"ipany", OPT_IPANY, NULL, 0, "allow transport with IPv4 and IPv6", GRP+1},
+ {"bind", 'b', "ADDR", 0, "bind listener to this address/name", GRP+1},
{"mark", 'm', "INTVL", 0, "specify timestamp interval in logs (0 for no "
"timestamps)", GRP+1},
{"no-detach", 'n', NULL, 0, "do not enter daemon mode", GRP+1},
@@ -372,6 +381,22 @@ parse_opt (int key, char *arg, struct argp_state *state)
AcceptRemote = 1;
break;
+ case '4':
+ usefamily = AF_INET;
+ break;
+
+ case '6':
+ usefamily = AF_INET6;
+ break;
+
+ case OPT_IPANY:
+ usefamily = AF_UNSPEC;
+ break;
+
+ case 'b':
+ BindAddress = arg;
+ break;
+
case 'm':
v = strtol (arg, &endptr, 10);
if (*endptr)
@@ -515,8 +540,8 @@ main (int argc, char *argv[])
signal (SIGUSR1, Debug ? dbg_toggle : SIG_IGN);
alarm (TIMERINTVL);
- /* We add 2 = 1(klog) + 1(inet), even if they may be not use. */
- fdarray = (struct pollfd *) malloc ((nfunix + 2) * sizeof (*fdarray));
+ /* We add 3 = 1(klog) + 2(inet,inet6), even if they may stay unused. */
+ fdarray = (struct pollfd *) malloc ((nfunix + 3) * sizeof (*fdarray));
if (fdarray == NULL)
error (EXIT_FAILURE, errno, "can't allocate fd table");
@@ -562,15 +587,24 @@ main (int argc, char *argv[])
/* Initialize inet socket and add it to the list. */
if (AcceptRemote)
{
- finet = create_inet_socket (usefamily);
- if (finet >= 0)
+ create_inet_socket (usefamily, finet);
+ if (finet[IU_FD_IP4] >= 0)
{
- fdarray[nfds].fd = finet;
+ /* IPv4 socket is present. */
+ fdarray[nfds].fd = finet[IU_FD_IP4];
fdarray[nfds].events = POLLIN | POLLPRI;
nfds++;
- dbg_printf ("Opened syslog UDP port.\n");
+ dbg_printf ("Opened syslog UDP/IPv4 port.\n");
}
- else
+ if (finet[IU_FD_IP6] >= 0)
+ {
+ /* IPv6 socket is present. */
+ fdarray[nfds].fd = finet[IU_FD_IP6];
+ fdarray[nfds].events = POLLIN | POLLPRI;
+ nfds++;
+ dbg_printf ("Opened syslog UDP/IPv6 port.\n");
+ }
+ if (finet[IU_FD_IP4] < 0 && finet[IU_FD_IP6] < 0)
dbg_printf ("Can't open UDP port: %s\n", strerror (errno));
}
@@ -707,7 +741,8 @@ main (int argc, char *argv[])
}
}
}
- else if (fdarray[i].fd == finet)
+ else if (fdarray[i].fd == finet[IU_FD_IP4]
+ || fdarray[i].fd == finet[IU_FD_IP6])
{
struct sockaddr_storage frominet;
/*dbg_printf ("inet message\n"); */
@@ -794,22 +829,25 @@ create_unix_socket (const char *path)
return fd;
}
-static int
-create_inet_socket (int af)
+static void
+create_inet_socket (int af, int fd46[2])
{
int err, fd = -1;
struct addrinfo hints, *rp, *ai;
+ /* Invalidate old descriptors. */
+ fd46[IU_FD_IP4] = fd46[IU_FD_IP6] = -1;
+
memset (&hints, 0, sizeof (hints));
hints.ai_family = af;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
- err = getaddrinfo (NULL, LogPortText, &hints, &rp);
+ err = getaddrinfo (BindAddress, LogPortText, &hints, &rp);
if (err)
{
logerror ("lookup error, suspending inet service");
- return fd;
+ return;
}
for (ai = rp; ai; ai = ai->ai_next)
@@ -829,17 +867,20 @@ create_inet_socket (int af)
fd = -1;
continue;
}
- /* Success. Only one socket at this time. */
- break;
+ /* Register any success. */
+ if (ai->ai_family == AF_INET && fd46[IU_FD_IP4] < 0)
+ fd46[IU_FD_IP4] = fd;
+ else if (ai->ai_family == AF_INET6 && fd46[IU_FD_IP6] < 0)
+ fd46[IU_FD_IP6] = fd;
}
freeaddrinfo (rp);
- if (ai == NULL)
+ if (fd46[IU_FD_IP4] < 0 && fd46[IU_FD_IP6] < 0)
{
logerror ("inet service, failed lookup.");
- return fd;
+ return;
}
- return fd;
+ return;
}
char **
@@ -1287,7 +1328,14 @@ fprintlog (struct filed *f, const char *from, int flags, const char *msg)
dbg_printf ("Not forwarding because forwarding is disabled.\n");
else
{
- int temp_finet = finet;
+ int temp_finet, *pfinet; /* PFINET points to active fd. */
+
+ if (f->f_un.f_forw.f_addr.ss_family == AF_INET)
+ pfinet = &finet[IU_FD_IP4];
+ else /* AF_INET6 */
+ pfinet = &finet[IU_FD_IP6];
+
+ temp_finet = *pfinet;
if (temp_finet < 0)
{
@@ -1345,7 +1393,7 @@ fprintlog (struct filed *f, const char *from, int flags, const char *msg)
logerror ("sendto");
}
- if (finet < 0)
+ if (*pfinet < 0)
close (temp_finet); /* Only temporary socket may be closed. */
}
break;
@@ -1631,8 +1679,10 @@ die (int signo)
unlink (funix[i].name);
}
- if (finet >= 0)
- close (finet);
+ if (finet[0] >= 0)
+ close (finet[0]);
+ if (finet[1] >= 0)
+ close (finet[1]);
exit (EXIT_SUCCESS);
}
diff --git a/tests/syslogd.sh b/tests/syslogd.sh
index 05848b7..3d3f7b6 100755
--- a/tests/syslogd.sh
+++ b/tests/syslogd.sh
@@ -94,7 +94,7 @@ fi
IU_OPTIONS="--rcfile=$CONF --pidfile=$PID --socket=$SOCKET"
## Enable INET service when running as root.
if [ "$USER" = "root" ]; then
- IU_OPTIONS="$IU_OPTIONS --inet --hop"
+ IU_OPTIONS="$IU_OPTIONS --ipany --inet --hop"
fi
## Bring in additional options from command line.
## Disable kernel messages otherwise.
@@ -125,11 +125,12 @@ EXITCODE=1
# Send messages on two sockets: IPv4 and UNIX.
#
TESTCASES=$((TESTCASES + 1))
-$IU_LOGGER -h $SOCKET -p user -t $TAG "Sending BSD message."
+$IU_LOGGER -h $SOCKET -p user.info -t $TAG "Sending BSD message. (pid $$)"
if [ "$USER" = "root" ]; then
- TESTCASES=$((TESTCASES + 1))
- $IU_LOGGER -4 -h localhost -p user -t $TAG "Sending IPv4 message."
+ TESTCASES=$((TESTCASES + 2))
+ $IU_LOGGER -4 -h localhost -p user.info -t $TAG "Sending IPv4 message. (pid $$)"
+ $IU_LOGGER -6 -h localhost -p user.info -t $TAG "Sending IPv6 message. (pid $$)"
fi
# Detection of registered messages.
signature.asc
Description: Digital signature
