Re: How to get a useful peer address when doing accept(3, ...) on a systemd supplied listening socket

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Oct 27, 2022 at 1:51 PM Klaus Ebbe Grue <grue@xxxxxxxx> wrote:
Hi systemd-devel,

Sorry to bug you with another user question.

I have a socket activated daemon, call it mydaemon, and I have trouble finding out who connects to it.


mydaemon.socket contains:


  [Socket]
  ListenStream=9999

When I connect using IPv4 using

  nc -4 localhost 9999

then mydaemon does

  sockaddr_in6 peer;
  socklen_t peer_size=sizeof(peer);
  accept(3,(struct sockaddr *)&peer,sizeof(peer))

Afterwards, peer.sin6_family is AF_INET6 and peer.sin6_addr contains some gibberish like a00:e5ae::


If you specify nothing for the listen address, systemd will assume the IPv6 address [::] as the default, and will create an AF_INET6 socket bound to [::]:9999.

Due to Linux's default "bind both families" magic, it will actually be bound to both [::]:9999 *and* 0.0.0.0:9999, so it will accept IPv4 connections – but you'll receive them in the form of AF_INET6 sockets, so the peer address of your v4 client indeed has family AF_INET6 but contains a "v6-mapped" IPv4 address such as [::ffff:10.0.229.174] aka [::ffff:a00:e5ae].

The alternative would be to specify both ListenStream=[::]:9999 and ListenStream=0.0.0.0:9999 (as well as BindIPv6Only=ipv6-only), which would cause you to receive *two* socket FDs – one purely for IPv6 clients, the other for IPv4 – that you'd have to put into poll() or some other loop for accepting clients.

You can extract the IPv4 address by detecting the [::ffff:0:0/96] prefix and stripping away the first 12 bytes. (There's also a magic option for getsockopt() listed in ipv6(7) that can convert such a "v6-mapped" socket to a "real" AF_INET socket, but it's rarely needed.)
 


If I connect more than once, the gibberish changes from connection to connection.


I have a feeling it "changes" because you're trying to give the whole struct sockaddr to inet_pton() instead of giving just the .sin6_addr field, so your program is trying to interpret the *port number* (i.e. the .sin6_port which precedes .sin_addr) as part of the address...

But please show your entire code, otherwise this is all just guessing.

Here's a working example that I've just tested with `systemd-socket-activate --listen=9999`: https://gist.github.com/grawity/63369273742f23b596d764cb6d45feb7
 

If mydaemon creates the listening socket, I can easily get the peer address.

I suspect that when systemd creates the listening socket then accept(3,...) returns a socket which is connected to a local socket created by systemd.

QUESTION: Is that suspicion correct?

No, it isn't.


--
Mantas Mikulėnas

[Index of Archives]     [LARTC]     [Bugtraq]     [Yosemite Forum]     [Photo]

  Powered by Linux