On Aug 1, 2024, at 6:27 AM, Denis Ovsienko <de...@ovsienko.info> wrote:

> pcap-rpcap.c defines 8 OS-specific values for AF_INET6
> (xxxxx_AF_INET6), would it be correct also to include and use a value
> for Haiku (5)?

It shouldn't be necessary.

The original WinPcap code for the rpcap daemon sent, in response to a 
RPCAP_MSG_FINDALLIF_REQ message, interface information in which:

        1) IPv6 addresses had the host's native AF_ value;

        2) all addresses were put out a format that matched the layout of the 
sender's native socket address structures.

This rarely caused a problem, because, on non-Windows platforms, users would 
have to construct and build their own rpcapified versions of libpcap and 
rpcapd, or find a version that somebody else did, to do remote captures, so the 
vast majority of rpcap traffic was between Windows clients and servers.

When I was doing the integration of that code into the libpcap code base, I did 
a bunch of tests with servers on {Linux, *BSD, Solaris, Windows} VMs and the 
host's macOS client, and with clients on those platforms and the host's macOS 
server (as well as macOS <-> macOS tests), and, other than the obvious issue 
caused by 1), two issues caused by 2) also appeared, one of which occurred with 
*BSD (including CupertinoBSD, a/k/a Darwin) servers, and another of which 
occurred only between Solaris and non-Solaris platforms.

The fix for these issues was

    commit 6ee13e223c9709e79ac112f8c37ff0a7272a4f5f
    Author: Guy Harris <ghar...@sonic.net>
    Date:   Sun Mar 12 13:59:03 2017 -0700

        Standardize the format of network addresses sent over the wire.

        We *CANNOT* use struct sockaddr_storage, as the layout and size are
        platform-dependent, and that would mean that a client wouldn't be
        guaranteed to work with a server running a different OS.

        We also *CANNOT* send the OS's AF_ values over the wire, as AF_INET6 has
        different values on different OSes.

        So we choose a format that resembles the Windows struct
        sockaddr_storage, and we choose the Windows AF_ value for IPv6.

        We try, as best we can, to handle replies from older servers, although
        that won't work with servers sending a struct sockaddr_storage that's
        not 128 bytes long.  (We could do more, I guess, by checking whether the
        total message size, and count of interfaces, are consistent with a
        128-byte or 256-byte struct sockaddr_storage.)

The Windows AF_INET6 value and structure format were chosen because WinPcap 
came with pre-built support for rpcap in the WinPcap library and a pre-built 
rpcapd for Windows; all other rpcap servers would have to have been built by 
somebody from source, so recap-capable {lib,Win,N}pcap clients and rpcap 
servers are probably going to be running on Windows.

The new format for the RPCAP_MSG_FINDALLIF_REPLY message works between all 
platforms using the new format; the client code also handles packets from 
servers on all platforms that *haven't* been changed, *except* for servers on 
Solaris with non-Solaris clients and Solaris clients with non-Solaris servers, 
where there's no way to determine the difference and work around it.  
Pre-6ee13e223c9709e79ac112f8c37ff0a7272a4f5f servers on Solaris won't work with 
any non-solaris clients, and won't work with Solaris clients that have 
6ee13e223c9709e79ac112f8c37ff0a7272a4f5f.

At the time, neither Haiku nor Hurd were supported by our libpcap; unless 
pre-6ee13e223c9709e79ac112f8c37ff0a7272a4f5f libpcaps and rpcapds were in use 
on Haiku or Hurd, it's unlikely that any servers on Haiku or Hurd would have 
sent out old-style RPCAP_MSG_FINDALLIF_REPLY messages.

So I think it's unlikely that the client needs to test for the Haiku or Hurd 
AF_INET6 value in order to handle pre-6ee13e223c9709e79ac112f8c37ff0a7272a4f5f 
servers.

> Also the current values of AF_INET6 are just 24 on NetBSD and OpenBSD,
> 28 on FreeBSD and 30 on macOS, is it still correct to bitwise-OR the
> value with SOCKADDR_IN6_LEN << 8?

That's done because sending addresses in the native socket address structure 
format over the wire causes a problem between systems that have the modified 
socket address structure formats introduced by the OSI(!) stack support for BSD.

The serializing code from WinPcap is... not serializing in the most obvious 
fashion.  It does:

        memset(sockaddrout, 0, sizeof(struct sockaddr_storage) );

to zero out the serialized structure, and then does

        struct sockaddr_in *sockaddr;

        sockaddr= (struct sockaddr_in *) sockaddrin;
        sockaddr->sin_family= htons(sockaddr->sin_family);
        sockaddr->sin_port= htons(sockaddr->sin_port);
        memcpy(sockaddrout, sockaddr, sizeof(struct sockaddr_in) );

for IPv4 addresses and

        struct sockaddr_in6 *sockaddr;
        
        sockaddr= (struct sockaddr_in6 *) sockaddrin;
        sockaddr->sin6_family= htons(sockaddr->sin6_family);
        sockaddr->sin6_port= htons(sockaddr->sin6_port);
        sockaddr->sin6_flowinfo= htonl(sockaddr->sin6_flowinfo);
        sockaddr->sin6_scope_id= htonl(sockaddr->sin6_scope_id);
        memcpy(sockaddrout, sockaddr, sizeof(struct sockaddr_in6) );

for IPv6 addresses (really, for *all* other addresses, but I fixed it to check 
serialize only IPv4 and IPv6 addresses in our code).

I.e., it zeroes out the target "struct sockaddr_storage", changes some fields 
in the *source* "struct sockaddr_in" or "struct sockaddr_in6" in network byte 
order, and then copies the source structure to the target structure.

The original socket address structure format begins with a 16-bit family field, 
so that code just puts the family field in the source structure into host byte 
order.  That works fine.

The modified BSD socket address structure begins with an 8-bit address length 
field, followed by an 8-bit family field.

On a big-endian server, that code leaves the address family of the source 
structure alone, as byte-swapping that value does nothing, and copies the 
source structure, including the un-modified address length field and the 
in-effect-unmodified address family field, to the target source structure; that 
will go over the network as a 16-bit big-endian value with the address length 
field in the upper 8 bits and the address family in the lower 8 bits.

On a little-endian server, that code sets the address family of the source 
structure to 0, as byte-swapping that value as if it were a 16-bit value 
produces a 16-bit value with the address family in the upper 8 bits and zero in 
the lower 8 bits, as address family values are <= 255 (as they must fit in the 
8-bit address-family field), and then stores that 16-bit value in an 8-bit 
address-family field, which discards the upper 8 bits and stores the lower 8 
bits into the address-family field.  It then copies the source structure, 
including the un-modified address length field and the in-effect-unmodified 
address family field, to the target source structure; that will go over the 
network as a 16-bit big-endian value with the address length field in the upper 
8 bits and 0 in the lower 8 bits.

The new on-the-network format introduced in 
6ee13e223c9709e79ac112f8c37ff0a7272a4f5f sends a 16-bit AF_ value, in network 
byte order, at the beginning of each address, so the client code treats the 
first 16 bits of the structure as such, so that it's byte-swapped on 
little-endian clients.  That works with all servers using the new 
on-the-network format, regardless of the byte order of the client and 
regardless of the byte order of the server.  It also works with all non-BSD 
servers using the old on-the-network format.

We handle BSD servers using the old on-the-network by checking for the values 
as put on the network by those servers:

        for big-endian BSD servers using the old on-the-network format and 
clients using the new on-the-network format, the value sent over the wire will, 
after deserialization (which does not modify the value), have the address 
length field in the upper 8 bits and have the address family, using the 
*server's* AF_ values, in the lower 8 bits;

        for little-endian BSD servers using the old on-the-network format and 
clients using the new on-the-network format, the value sent over the wire will, 
after deserialization (which byte-swaps the value from big-endian to 
little-endian), have the address length field in the upper 8 bits and 0 in the 
lower 8 bits.

On all *BSDs (including CupertinoBSD, a/k/a Darwin), the address length of an 
IPv4 address is 16 (the size of "struct sockaddr_in"), and AF_INET is 2, so:

        a big-endian server using the old on-the-network format would send 
something that appears to a client using the new on-the-network format as (16 
<< 8 | 2), which is #defined in pcwp-rpcap.c as NEW_BSD_AF_INET_BE;

        a little-endian server using the old on-the-network format would send 
something that appears to a client using the new on-the-network format as (16 
<< 8 | 0), which is #defined in pcwp-rpcap.c as NEW_BSD_AF_INET_LE;

and:

        on NetBSD, OpenBSD, and BSD/OS, AF_INET6 is 24;

        on FreeBSD and DragonFly BSD, AF_INET6 is 28;

        on CupertinoBSD^WDarwin, AF_INET6 is 30;

and the address length an IPv6 address is 28 (the size of "struct 
sockaddr_in6") on all of those *BSDs, so:

        a big-endian NetBSD, OpenBSD, or BSD/OS server using the old 
on-the-network format would send something that appears to a client using the 
new on-the-network format as (28 << 8 | 24), which is #defined in pcwp-rpcap.c 
as NEW_BSD_AF_INET6_BSD_BE;

        a big-endian FreeBSD or DragonFly BSD server using the old 
on-the-network format would send something that appears to a client using the 
new on-the-network format as (28 << 8 | 28), which is #defined in pcwp-rpcap.c 
as NEW_BSD_AF_INET6_FREEBSD_BE;

        a big-endian Darwin server using the old on-the-network format would 
send something that appears to a client using the new on-the-network format as 
(28 << 8 | 30), which is #defined in pcwp-rpcap.c as NEW_BSD_AF_INET6_DARWIN_BE;

        a little-endian server - *regardless* of the AF_INET6 value, as the 
address family value is zeroed-out by the byte swapping - using the old 
on-the-network format would send something that appears to a client using the 
new on-the-network format as (28 << 8 | 0), which is #defined in pcwp-rpcap.c 
as NEW_BSD_AF_INET6_LE.

We also check for the AF_INET6 values for Linux, HP-UX, AIX, and Solaris; none 
of those use the new BSD sockaddr format, so we don't have to worry about the 
BSD issue.

I should check to make sure all those details are described in the comment in 
pcap-rpcap.c, following the "Function bodies" comment.
        
> Also GNU/Hurd uses the same value as SOLARIS_AF_INET6 and NetBSD &
> OpenBSD use the same value as AIX_AF_INET6, it could be useful to
> clarify that in the same go.

As noted, it's unlikely that anybody would run the old server code on GNU/Hurd.
_______________________________________________
tcpdump-workers mailing list -- tcpdump-workers@lists.tcpdump.org
To unsubscribe send an email to tcpdump-workers-le...@lists.tcpdump.org
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s

Reply via email to