socket-proxyd: rework to support multiple sockets and splice()-based zero-copy network IO

This also drops --ignore-env, which can't really work anymore if we
allow multiple fds. Also adds support for pretty printing of peer
identities for debug purposes, and abstract namespace UNIX sockets. Also
ensures that we never take more connections than a certain limit.
This commit is contained in:
Lennart Poettering 2013-11-06 22:40:54 +01:00
parent 175a3d25d0
commit 8569a77629
4 changed files with 546 additions and 455 deletions

View file

@ -39,19 +39,17 @@
</refmeta> </refmeta>
<refnamediv> <refnamediv>
<refname>systemd-socket-proxyd</refname> <refname>systemd-socket-proxyd</refname>
<refpurpose>Inherit a socket. Bidirectionally <refpurpose>Bidirectionally proxy local sockets to another (possibly remote) socket.</refpurpose>
proxy.</refpurpose>
</refnamediv> </refnamediv>
<refsynopsisdiv> <refsynopsisdiv>
<cmdsynopsis> <cmdsynopsis>
<command>systemd-socket-proxyd</command> <command>systemd-socket-proxyd</command>
<arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat"><replaceable>OPTIONS</replaceable></arg>
<arg choice="plain"><replaceable>HOSTNAME-OR-IPADDR</replaceable></arg> <arg choice="plain"><replaceable>HOST</replaceable>:<replaceable>PORT</replaceable></arg>
<arg choice="plain"><replaceable>PORT-OR-SERVICE</replaceable></arg>
</cmdsynopsis> </cmdsynopsis>
<cmdsynopsis> <cmdsynopsis>
<command>systemd-socket-proxyd</command> <command>systemd-socket-proxyd</command>
<arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat"><replaceable>OPTIONS</replaceable></arg>
<arg choice="plain"><replaceable>UNIX-DOMAIN-SOCKET-PATH</replaceable> <arg choice="plain"><replaceable>UNIX-DOMAIN-SOCKET-PATH</replaceable>
</arg> </arg>
</cmdsynopsis> </cmdsynopsis>
@ -59,13 +57,20 @@
<refsect1> <refsect1>
<title>Description</title> <title>Description</title>
<para> <para>
<command>systemd-socket-proxyd</command> provides a proxy <command>systemd-socket-proxyd</command> is a generic
to socket-activate services that do not yet support socket-activated network socket forwarder proxy daemon
native socket activation. On behalf of the daemon, for IPV4, IPv6 and UNIX stream sockets. It may be used
the proxy inherits the socket from systemd, accepts to bi-directionally forward traffic from a local listening socket to a
each client connection, opens a connection to the server local or remote destination socket.</para>
for each client, and then bidirectionally forwards
data between the two.</para> <para>One use of this tool is to provide
socket-activation support for services that do not
natively support socket activation. On behalf of the
service to activate, the proxy inherits the socket
from systemd, accepts each client connection, opens a
connection to a configured server for each client, and
then bidirectionally forwards data between the
two.</para>
<para>This utility's behavior is similar to <para>This utility's behavior is similar to
<citerefentry><refentrytitle>socat</refentrytitle><manvolnum>1</manvolnum></citerefentry>. <citerefentry><refentrytitle>socat</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
The main differences for <command>systemd-socket-proxyd</command> The main differences for <command>systemd-socket-proxyd</command>
@ -93,19 +98,6 @@
string and exits.</para> string and exits.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--ignore-env</option></term>
<listitem>
<para>Skips verification of
the expected PID and file
descriptor numbers. Use this if
invoked indirectly, for
example, with a shell script
rather than with
<option>ExecStart=/usr/lib/systemd/systemd-socket-proxyd</option>
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>
<refsect1> <refsect1>
@ -205,7 +197,7 @@ while [ ! -f /tmp/nginx.pid ]
do do
/usr/bin/inotifywait /tmp/nginx.pid /usr/bin/inotifywait /tmp/nginx.pid
done done
/usr/bin/systemd-socket-proxyd --ignore-env localhost 8080]]> exec /usr/bin/systemd-socket-proxyd localhost 8080]]>
</programlisting> </programlisting>
</example> </example>
<example label="nginx configuration"> <example label="nginx configuration">
@ -232,23 +224,11 @@ $ curl http://localhost:80/]]>
<refsect1> <refsect1>
<title>See Also</title> <title>See Also</title>
<para> <para>
<citerefentry> <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<refentrytitle> <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
systemd.service</refentrytitle> <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<manvolnum>5</manvolnum> <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
</citerefentry>, <citerefentry><refentrytitle>socat</refentrytitle><manvolnum>1</manvolnum></citerefentry>
<citerefentry> </para>
<refentrytitle>
systemd.socket</refentrytitle>
<manvolnum>5</manvolnum>
</citerefentry>,
<citerefentry>
<refentrytitle>systemctl</refentrytitle>
<manvolnum>1</manvolnum>
</citerefentry>,
<citerefentry>
<refentrytitle>socat</refentrytitle>
<manvolnum>1</manvolnum>
</citerefentry></para>
</refsect1> </refsect1>
</refentry> </refentry>

View file

@ -568,6 +568,89 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) {
return false; return false;
} }
int getpeername_pretty(int fd, char **ret) {
union {
struct sockaddr sa;
struct sockaddr_un un;
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr_storage storage;
} sa;
socklen_t salen;
char *p;
assert(fd >= 0);
assert(ret);
salen = sizeof(sa);
if (getpeername(fd, &sa.sa, &salen) < 0)
return -errno;
switch (sa.sa.sa_family) {
case AF_INET: {
uint32_t a;
a = ntohl(sa.in.sin_addr.s_addr);
if (asprintf(&p,
"%u.%u.%u.%u:%u",
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
ntohs(sa.in.sin_port)) < 0)
return -ENOMEM;
break;
}
case AF_INET6: {
static const unsigned char ipv4_prefix[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
};
if (memcmp(&sa.in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
const uint8_t *a = sa.in6.sin6_addr.s6_addr+12;
if (asprintf(&p,
"%u.%u.%u.%u:%u",
a[0], a[1], a[2], a[3],
ntohs(sa.in6.sin6_port)) < 0)
return -ENOMEM;
} else {
char a[INET6_ADDRSTRLEN];
if (asprintf(&p,
"%s:%u",
inet_ntop(AF_INET6, &sa.in6.sin6_addr, a, sizeof(a)),
ntohs(sa.in6.sin6_port)) < 0)
return -ENOMEM;
}
break;
}
case AF_UNIX: {
struct ucred ucred;
salen = sizeof(ucred);
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &salen) < 0)
return -errno;
if (asprintf(&p, "PID %lu/UID %lu", (unsigned long) ucred.pid, (unsigned long) ucred.pid) < 0)
return -ENOMEM;
break;
}
default:
return -ENOTSUP;
}
*ret = p;
return 0;
}
static const char* const netlink_family_table[] = { static const char* const netlink_family_table[] = {
[NETLINK_ROUTE] = "route", [NETLINK_ROUTE] = "route",
[NETLINK_FIREWALL] = "firewall", [NETLINK_FIREWALL] = "firewall",

View file

@ -99,3 +99,5 @@ int netlink_family_to_string_alloc(int b, char **s);
int netlink_family_from_string(const char *s); int netlink_family_from_string(const char *s);
bool socket_ipv6_is_supported(void); bool socket_ipv6_is_supported(void);
int getpeername_pretty(int fd, char **ret);

File diff suppressed because it is too large Load diff