/**
 * D header file for POSIX.
 *
 * Copyright: Copyright Sean Kelly 2005 - 2009.
 * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
 * Authors:   Sean Kelly
 * Standards: The Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition
 */

/*          Copyright Sean Kelly 2005 - 2009.
 * Distributed under the Boost Software License, Version 1.0.
 *    (See accompanying file LICENSE or copy at
 *          http://www.boost.org/LICENSE_1_0.txt)
 */
module core.sys.posix.netinet.in_;

private import core.sys.posix.config;
public import core.stdc.inttypes; // for uint32_t, uint16_t, uint8_t
public import core.sys.posix.arpa.inet;
public import core.sys.posix.sys.socket; // for sa_family_t

version (OSX)
    version = Darwin;
else version (iOS)
    version = Darwin;
else version (TVOS)
    version = Darwin;
else version (WatchOS)
    version = Darwin;

version (Posix):
extern (C) nothrow @nogc:

//
// Required
//
/*
NOTE: The following must must be defined in core.sys.posix.arpa.inet to break
      a circular import: in_port_t, in_addr_t, struct in_addr, INET_ADDRSTRLEN.

in_port_t
in_addr_t

sa_family_t // from core.sys.posix.sys.socket
uint8_t     // from core.stdc.inttypes
uint32_t    // from core.stdc.inttypes

struct in_addr
{
    in_addr_t   s_addr;
}

struct sockaddr_in
{
    sa_family_t sin_family;
    in_port_t   sin_port;
    in_addr     sin_addr;
}

IPPROTO_IP
IPPROTO_ICMP
IPPROTO_TCP
IPPROTO_UDP

INADDR_ANY
INADDR_BROADCAST

INET_ADDRSTRLEN

htonl() // from core.sys.posix.arpa.inet
htons() // from core.sys.posix.arpa.inet
ntohl() // from core.sys.posix.arpa.inet
ntohs() // from core.sys.posix.arpa.inet
*/

version( CRuntime_Glibc )
{
    // Some networking constants are subtly different for glibc, linux kernel
    // constants are also provided below.

    //alias uint16_t in_port_t;
    //alias uint32_t in_addr_t;

    //struct in_addr
    //{
    //    in_addr_t s_addr;
    //}

    private enum __SOCK_SIZE__ = 16;

    struct sockaddr_in
    {
        sa_family_t sin_family;
        in_port_t   sin_port;
        in_addr     sin_addr;

        /* Pad to size of `struct sockaddr'. */
        ubyte[__SOCK_SIZE__ - sa_family_t.sizeof -
              in_port_t.sizeof - in_addr.sizeof] __pad;
    }

    enum
    {
        IPPROTO_IP   = 0,
        IPPROTO_ICMP = 1,
        IPPROTO_IGMP = 2,
        IPPROTO_GGP  = 3,
        IPPROTO_TCP  = 6,
        IPPROTO_PUP  = 12,
        IPPROTO_UDP  = 17,
        IPPROTO_IDP  = 22,
        IPPROTO_ND   = 77,
        IPPROTO_MAX  = 256
    }

    enum : uint
    {
        INADDR_ANY       = 0x00000000,
        INADDR_BROADCAST = 0xffffffff,
        INADDR_LOOPBACK  = 0x7F000001,
        INADDR_NONE      = 0xFFFFFFFF
    }

    //enum INET_ADDRSTRLEN       = 16;
}
else version( Darwin )
{
    //alias uint16_t in_port_t;
    //alias uint32_t in_addr_t;

    //struct in_addr
    //{
    //    in_addr_t s_addr;
    //}

    private enum __SOCK_SIZE__ = 16;

    struct sockaddr_in
    {
        ubyte       sin_len;
        sa_family_t sin_family;
        in_port_t   sin_port;
        in_addr     sin_addr;
        ubyte[8]    sin_zero;
    }

    enum
    {
        IPPROTO_IP   = 0,
        IPPROTO_ICMP = 1,
        IPPROTO_IGMP = 2,
        IPPROTO_GGP  = 3,
        IPPROTO_TCP  = 6,
        IPPROTO_PUP  = 12,
        IPPROTO_UDP  = 17,
        IPPROTO_IDP  = 22,
        IPPROTO_ND   = 77,
        IPPROTO_MAX  = 256
    }

    enum : uint
    {
        INADDR_ANY       = 0x00000000,
        INADDR_BROADCAST = 0xffffffff,
        INADDR_LOOPBACK  = 0x7F000001,
        INADDR_NONE      = 0xFFFFFFFF
    }

    //enum INET_ADDRSTRLEN       = 16;
}
else version( FreeBSD )
{
    //alias uint16_t in_port_t;
    //alias uint32_t in_addr_t;

    //struct in_addr
    //{
    //    in_addr_t s_addr;
    //}

    struct sockaddr_in
    {
        ubyte       sin_len;
        sa_family_t sin_family;
        in_port_t   sin_port;
        in_addr     sin_addr;
        ubyte[8]    sin_zero;
    }

    enum
    {
        IPPROTO_IP   = 0,
        IPPROTO_ICMP = 1,
        IPPROTO_IGMP = 2,
        IPPROTO_GGP  = 3,
        IPPROTO_TCP  = 6,
        IPPROTO_PUP  = 12,
        IPPROTO_UDP  = 17,
        IPPROTO_IDP  = 22,
        IPPROTO_ND   = 77,
        IPPROTO_MAX  = 256
    }

    enum : uint
    {
        INADDR_ANY       = 0x00000000,
        INADDR_BROADCAST = 0xffffffff,
        INADDR_LOOPBACK  = 0x7f000001,
        INADDR_NONE      = 0xffffffff
    }

    //enum INET_ADDRSTRLEN       = 16;
}
else version(NetBSD)
{
    struct sockaddr_in
    {
        ubyte       sin_len;
        sa_family_t sin_family;
        in_port_t   sin_port;
        in_addr     sin_addr;
        ubyte[8]    sin_zero;
    }

    enum
    {
        IPPROTO_IP   = 0,
        IPPROTO_ICMP = 1,
        IPPROTO_IGMP = 2,
        IPPROTO_GGP  = 3,
        IPPROTO_TCP  = 6,
        IPPROTO_PUP  = 12,
        IPPROTO_UDP  = 17,
        IPPROTO_IDP  = 22,
        IPPROTO_ND   = 77,
        IPPROTO_MAX  = 256
    }

    enum : uint
    {
        INADDR_ANY       = 0x00000000,
        INADDR_BROADCAST = 0xffffffff,
        INADDR_LOOPBACK  = 0x7f000001,
        INADDR_NONE      = 0xffffffff
    }

    //enum INET_ADDRSTRLEN       = 16;
}
else version( DragonFlyBSD )
{
    //alias uint16_t in_port_t;
    //alias uint32_t in_addr_t;

    //struct in_addr
    //{
    //    in_addr_t s_addr;
    //}

    struct sockaddr_in
    {
        ubyte       sin_len;
        sa_family_t sin_family;
        in_port_t   sin_port;
        in_addr     sin_addr;
        ubyte[8]    sin_zero;
    }

    enum
    {
        IPPROTO_IP   = 0,
        IPPROTO_ICMP = 1,
        IPPROTO_IGMP = 2,
        IPPROTO_GGP  = 3,
        IPPROTO_TCP  = 6,
        IPPROTO_PUP  = 12,
        IPPROTO_UDP  = 17,
        IPPROTO_IDP  = 22,
        IPPROTO_ND   = 77,
        IPPROTO_MAX  = 256
    }

    enum : uint
    {
        INADDR_ANY       = 0x00000000,
        INADDR_LOOPBACK  = 0x7f000001,
        INADDR_BROADCAST = 0xffffffff,
        INADDR_NONE      = 0xffffffff,
    }

    //enum INET_ADDRSTRLEN       = 16;
}
else version( CRuntime_UClibc )
{
    private enum __SOCK_SIZE__ = 16;

    struct sockaddr_in
    {
        sa_family_t sin_family;
        in_port_t   sin_port;
        in_addr     sin_addr;

        ubyte[__SOCK_SIZE__ - short.sizeof -
              ushort.sizeof - in_addr.sizeof] __pad;
    }

    enum
    {
        IPPROTO_IP      = 0,
        IPPROTO_ICMP    = 1,
        IPPROTO_IGMP    = 2,
        IPPROTO_GGP     = 3,
        IPPROTO_IPIP    = 4,
        IPPROTO_TCP     = 6,
        IPPROTO_EGP     = 8,
        IPPROTO_PUP     = 12,
        IPPROTO_UDP     = 17,
        IPPROTO_IDP     = 22,
        IPPROTO_TP      = 29,
        IPPROTO_DCCP    = 33,
        IPPROTO_RSVP    = 46,
        IPPROTO_GRE     = 47,
        IPPROTO_ESP     = 50,
        IPPROTO_AH      = 51,
        IPPROTO_MTP     = 92,
        IPPROTO_BEETPH  = 94,
        IPPROTO_ENCAP   = 98,
        IPPROTO_PIM     = 103,
        IPPROTO_COMP    = 108,
        IPPROTO_SCTP    = 132,
        IPPROTO_UDPLITE = 136,
        IPPROTO_MPLS    = 137,
        IPPROTO_MAX     = 256
    }

    enum : uint
    {
        INADDR_ANY       = 0x00000000,
        INADDR_BROADCAST = 0xffffffff,
        IN_LOOPBACKNET   = 127,
        INADDR_LOOPBACK  = 0x7F000001,
        INADDR_NONE      = 0xFFFFFFFF
    }
}
else version( Solaris )
{
    struct sockaddr_in
    {
        sa_family_t sin_family;
        in_port_t   sin_port;
        in_addr     sin_addr;
        ubyte[8]    sin_zero;
    }

    enum
    {
        IPPROTO_IP   = 0,
        IPPROTO_ICMP = 1,
        IPPROTO_IGMP = 2,
        IPPROTO_GGP  = 3,
        IPPROTO_TCP  = 6,
        IPPROTO_PUP  = 12,
        IPPROTO_UDP  = 17,
        IPPROTO_IDP  = 22,
        IPPROTO_ND   = 77,
        IPPROTO_MAX  = 256
    }

    enum : uint
    {
        INADDR_ANY       = 0x00000000,
        INADDR_BROADCAST = 0xffffffff,
        INADDR_LOOPBACK  = 0x7f000001,
        INADDR_NONE      = 0xffffffff
    }
}
else version( linux )
{
    private enum __SOCK_SIZE__ = 16;

    struct sockaddr_in
    {
        sa_family_t sin_family;
        ushort      sin_port;
        in_addr     sin_addr;

        /* Pad to size of `struct sockaddr'. */
        ubyte[__SOCK_SIZE__ - sa_family_t.sizeof -
              ushort.sizeof - in_addr.sizeof] __pad;
    }

    enum
    {
        IPPROTO_IP   = 0,
        IPPROTO_ICMP = 1,
        IPPROTO_IGMP = 2,
        IPPROTO_GGP  = 3,
        IPPROTO_TCP  = 6,
        IPPROTO_PUP  = 12,
        IPPROTO_UDP  = 17,
        IPPROTO_IDP  = 22,
        IPPROTO_IPV6 = 41
    }

    enum : c_ulong
    {
        INADDR_ANY       = 0x00000000,
        INADDR_BROADCAST = 0xffffffff,
        INADDR_LOOPBACK  = 0x7f000001,
        INADDR_NONE      = 0xFFFFFFFF
    }
}


//
// IPV6 (IP6)
//
/*
NOTE: The following must must be defined in core.sys.posix.arpa.inet to break
      a circular import: INET6_ADDRSTRLEN.

struct in6_addr
{
    uint8_t[16] s6_addr;
}

struct sockaddr_in6
{
    sa_family_t sin6_family;
    in_port_t   sin6_port;
    uint32_t    sin6_flowinfo;
    in6_addr    sin6_addr;
    uint32_t    sin6_scope_id;
}

extern in6_addr in6addr_any;
extern in6_addr in6addr_loopback;

struct ipv6_mreq
{
    in6_addr    ipv6mr_multiaddr;
    uint        ipv6mr_interface;
}

IPPROTO_IPV6

INET6_ADDRSTRLEN

IPV6_JOIN_GROUP
IPV6_LEAVE_GROUP
IPV6_MULTICAST_HOPS
IPV6_MULTICAST_IF
IPV6_MULTICAST_LOOP
IPV6_UNICAST_HOPS
IPV6_V6ONLY

// macros
int IN6_IS_ADDR_UNSPECIFIED(in6_addr*)
int IN6_IS_ADDR_LOOPBACK(in6_addr*)
int IN6_IS_ADDR_MULTICAST(in6_addr*)
int IN6_IS_ADDR_LINKLOCAL(in6_addr*)
int IN6_IS_ADDR_SITELOCAL(in6_addr*)
int IN6_IS_ADDR_V4MAPPED(in6_addr*)
int IN6_IS_ADDR_V4COMPAT(in6_addr*)
int IN6_IS_ADDR_MC_NODELOCAL(in6_addr*)
int IN6_IS_ADDR_MC_LINKLOCAL(in6_addr*)
int IN6_IS_ADDR_MC_SITELOCAL(in6_addr*)
int IN6_IS_ADDR_MC_ORGLOCAL(in6_addr*)
int IN6_IS_ADDR_MC_GLOBAL(in6_addr*)
*/

version ( CRuntime_Glibc )
{
    struct in6_addr
    {
        union
        {
            uint8_t[16] s6_addr;
            uint16_t[8] s6_addr16;
            uint32_t[4] s6_addr32;
        }
    }

    struct sockaddr_in6
    {
        sa_family_t sin6_family;
        in_port_t   sin6_port;
        uint32_t    sin6_flowinfo;
        in6_addr    sin6_addr;
        uint32_t    sin6_scope_id;
    }

    extern __gshared immutable in6_addr in6addr_any;
    extern __gshared immutable in6_addr in6addr_loopback;

    struct ipv6_mreq
    {
        in6_addr    ipv6mr_multiaddr;
        uint        ipv6mr_interface;
    }

    enum : uint
    {
        IPPROTO_IPV6        = 41,

        //INET6_ADDRSTRLEN    = 46,

        IPV6_JOIN_GROUP     = 20,
        IPV6_LEAVE_GROUP    = 21,
        IPV6_MULTICAST_HOPS = 18,
        IPV6_MULTICAST_IF   = 17,
        IPV6_MULTICAST_LOOP = 19,
        IPV6_UNICAST_HOPS   = 16,
        IPV6_V6ONLY         = 26
    }

    // macros
    extern (D) int IN6_IS_ADDR_UNSPECIFIED( in6_addr* addr ) pure
    {
        return (cast(uint32_t*) addr)[0] == 0 &&
               (cast(uint32_t*) addr)[1] == 0 &&
               (cast(uint32_t*) addr)[2] == 0 &&
               (cast(uint32_t*) addr)[3] == 0;
    }

    extern (D) int IN6_IS_ADDR_LOOPBACK( in6_addr* addr ) pure
    {
        return (cast(uint32_t*) addr)[0] == 0  &&
               (cast(uint32_t*) addr)[1] == 0  &&
               (cast(uint32_t*) addr)[2] == 0  &&
               (cast(uint32_t*) addr)[3] == htonl( 1 );
    }

    extern (D) int IN6_IS_ADDR_MULTICAST( in6_addr* addr ) pure
    {
        return (cast(uint8_t*) addr)[0] == 0xff;
    }

    extern (D) int IN6_IS_ADDR_LINKLOCAL( in6_addr* addr ) pure
    {
        return ((cast(uint32_t*) addr)[0] & htonl( 0xffc00000 )) == htonl( 0xfe800000 );
    }

    extern (D) int IN6_IS_ADDR_SITELOCAL( in6_addr* addr ) pure
    {
        return ((cast(uint32_t*) addr)[0] & htonl( 0xffc00000 )) == htonl( 0xfec00000 );
    }

    extern (D) int IN6_IS_ADDR_V4MAPPED( in6_addr* addr ) pure
    {
        return (cast(uint32_t*) addr)[0] == 0 &&
               (cast(uint32_t*) addr)[1] == 0 &&
               (cast(uint32_t*) addr)[2] == htonl( 0xffff );
    }

    extern (D) int IN6_IS_ADDR_V4COMPAT( in6_addr* addr ) pure
    {
        return (cast(uint32_t*) addr)[0] == 0 &&
               (cast(uint32_t*) addr)[1] == 0 &&
               (cast(uint32_t*) addr)[2] == 0 &&
               ntohl( (cast(uint32_t*) addr)[3] ) > 1;
    }

    extern (D) int IN6_IS_ADDR_MC_NODELOCAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST( addr ) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0x1;
    }

    extern (D) int IN6_IS_ADDR_MC_LINKLOCAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST( addr ) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0x2;
    }

    extern (D) int IN6_IS_ADDR_MC_SITELOCAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST(addr) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0x5;
    }

    extern (D) int IN6_IS_ADDR_MC_ORGLOCAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST( addr) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0x8;
    }

    extern (D) int IN6_IS_ADDR_MC_GLOBAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST( addr ) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0xe;
    }
}
else version( Darwin )
{
    struct in6_addr
    {
        union
        {
            uint8_t[16] s6_addr;
            uint16_t[8] s6_addr16;
            uint32_t[4] s6_addr32;
        }
    }

    struct sockaddr_in6
    {
        uint8_t     sin6_len;
        sa_family_t sin6_family;
        in_port_t   sin6_port;
        uint32_t    sin6_flowinfo;
        in6_addr    sin6_addr;
        uint32_t    sin6_scope_id;
    }

    extern __gshared immutable in6_addr in6addr_any;
    extern __gshared immutable in6_addr in6addr_loopback;

    struct ipv6_mreq
    {
        in6_addr    ipv6mr_multiaddr;
        uint        ipv6mr_interface;
    }

    enum : uint
    {
        IPPROTO_IPV6        = 41,

        //INET6_ADDRSTRLEN    = 46,

        IPV6_JOIN_GROUP     = 12,
        IPV6_LEAVE_GROUP    = 13,
        IPV6_MULTICAST_HOPS = 10,
        IPV6_MULTICAST_IF   = 9,
        IPV6_MULTICAST_LOOP = 11,
        IPV6_UNICAST_HOPS   = 4,
        IPV6_V6ONLY         = 27
    }

    // macros
    extern (D) int IN6_IS_ADDR_UNSPECIFIED( in6_addr* addr ) pure
    {
        return (cast(uint32_t*) addr)[0] == 0 &&
               (cast(uint32_t*) addr)[1] == 0 &&
               (cast(uint32_t*) addr)[2] == 0 &&
               (cast(uint32_t*) addr)[3] == 0;
    }

    extern (D) int IN6_IS_ADDR_LOOPBACK( in6_addr* addr ) pure
    {
        return (cast(uint32_t*) addr)[0] == 0  &&
               (cast(uint32_t*) addr)[1] == 0  &&
               (cast(uint32_t*) addr)[2] == 0  &&
               (cast(uint32_t*) addr)[3] == ntohl( 1 );
    }

    extern (D) int IN6_IS_ADDR_MULTICAST( in6_addr* addr ) pure
    {
        return addr.s6_addr[0] == 0xff;
    }

    extern (D) int IN6_IS_ADDR_LINKLOCAL( in6_addr* addr ) pure
    {
        return addr.s6_addr[0] == 0xfe && (addr.s6_addr[1] & 0xc0) == 0x80;
    }

    extern (D) int IN6_IS_ADDR_SITELOCAL( in6_addr* addr ) pure
    {
        return addr.s6_addr[0] == 0xfe && (addr.s6_addr[1] & 0xc0) == 0xc0;
    }

    extern (D) int IN6_IS_ADDR_V4MAPPED( in6_addr* addr ) pure
    {
        return (cast(uint32_t*) addr)[0] == 0 &&
               (cast(uint32_t*) addr)[1] == 0 &&
               (cast(uint32_t*) addr)[2] == ntohl( 0x0000ffff );
    }

    extern (D) int IN6_IS_ADDR_V4COMPAT( in6_addr* addr ) pure
    {
        return (cast(uint32_t*) addr)[0] == 0 &&
               (cast(uint32_t*) addr)[1] == 0 &&
               (cast(uint32_t*) addr)[2] == 0 &&
               (cast(uint32_t*) addr)[3] != 0 &&
               (cast(uint32_t*) addr)[3] != ntohl( 1 );
    }

    extern (D) int IN6_IS_ADDR_MC_NODELOCAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST( addr ) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0x1;
    }

    extern (D) int IN6_IS_ADDR_MC_LINKLOCAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST( addr ) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0x2;
    }

    extern (D) int IN6_IS_ADDR_MC_SITELOCAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST(addr) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0x5;
    }

    extern (D) int IN6_IS_ADDR_MC_ORGLOCAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST( addr) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0x8;
    }

    extern (D) int IN6_IS_ADDR_MC_GLOBAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST( addr ) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0xe;
    }
}
else version( FreeBSD )
{
    struct in6_addr
    {
        union
        {
            uint8_t[16] s6_addr;
            uint16_t[8] s6_addr16;
            uint32_t[4] s6_addr32;
        }
    }

    struct sockaddr_in6
    {
        uint8_t     sin6_len;
        sa_family_t sin6_family;
        in_port_t   sin6_port;
        uint32_t    sin6_flowinfo;
        in6_addr    sin6_addr;
        uint32_t    sin6_scope_id;
    }

    extern __gshared immutable in6_addr in6addr_any;
    extern __gshared immutable in6_addr in6addr_loopback;

    struct ipv6_mreq
    {
        in6_addr    ipv6mr_multiaddr;
        uint        ipv6mr_interface;
    }

    enum : uint
    {
        IPPROTO_IPV6        = 41,

        //INET6_ADDRSTRLEN    = 46,

        IPV6_JOIN_GROUP     = 12,
        IPV6_LEAVE_GROUP    = 13,
        IPV6_MULTICAST_HOPS = 10,
        IPV6_MULTICAST_IF   = 9,
        IPV6_MULTICAST_LOOP = 11,
        IPV6_UNICAST_HOPS   = 4,
        IPV6_V6ONLY         = 27,
    }

    private enum
    {
        __IPV6_ADDR_SCOPE_NODELOCAL     = 0x01,
        __IPV6_ADDR_SCOPE_INTFACELOCAL  = 0x01,
        __IPV6_ADDR_SCOPE_LINKLOCAL     = 0x02,
        __IPV6_ADDR_SCOPE_SITELOCAL     = 0x05,
        __IPV6_ADDR_SCOPE_ORGLOCAL      = 0x08,
        __IPV6_ADDR_SCOPE_GLOBAL        = 0x0e,
    }

    // macros
    extern (D) int IN6_IS_ADDR_UNSPECIFIED( in in6_addr* a ) pure
    {
        return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) == 0);
    }

    extern (D) int IN6_IS_ADDR_LOOPBACK( in in6_addr* a ) pure
    {
        return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) == ntohl(1));
    }

    extern (D) int IN6_IS_ADDR_V4COMPAT( in in6_addr* a ) pure
    {
        return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) != 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) != ntohl(1));
    }

    extern (D) int IN6_IS_ADDR_V4MAPPED( in in6_addr* a ) pure
    {
        return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == ntohl(0x0000ffff));
    }

    extern (D) int IN6_IS_ADDR_LINKLOCAL( in in6_addr* a ) pure
    {
        return a.s6_addr[0] == 0xfe && (a.s6_addr[1] & 0xc0) == 0x80;
    }

    extern (D) int IN6_IS_ADDR_SITELOCAL( in in6_addr* a ) pure
    {
        return a.s6_addr[0] == 0xfe && (a.s6_addr[1] & 0xc0) == 0xc0;
    }

    extern (D) int IN6_IS_ADDR_MULTICAST( in in6_addr* a ) pure
    {
        return a.s6_addr[0] == 0xff;
    }

    extern (D) uint8_t __IPV6_ADDR_MC_SCOPE( in in6_addr* a ) pure
    {
        return a.s6_addr[1] & 0x0f;
    }

    extern (D) int IN6_IS_ADDR_MC_NODELOCAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_NODELOCAL;
    }

    extern (D) int IN6_IS_ADDR_MC_LINKLOCAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_LINKLOCAL;
    }

    extern (D) int IN6_IS_ADDR_MC_SITELOCAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_SITELOCAL;
    }

    extern (D) int IN6_IS_ADDR_MC_ORGLOCAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_ORGLOCAL;
    }

    extern (D) int IN6_IS_ADDR_MC_GLOBAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_GLOBAL;
    }
}
else version(NetBSD)
{
    struct in6_addr
    {
        union
        {
            uint8_t[16] s6_addr;
            uint16_t[8] s6_addr16;
            uint32_t[4] s6_addr32;
        }
    }

    struct sockaddr_in6
    {
        uint8_t     sin6_len;
        sa_family_t sin6_family;
        in_port_t   sin6_port;
        uint32_t    sin6_flowinfo;
        in6_addr    sin6_addr;
        uint32_t    sin6_scope_id;
    }

    extern __gshared immutable in6_addr in6addr_any;
    extern __gshared immutable in6_addr in6addr_loopback;

    struct ipv6_mreq
    {
        in6_addr    ipv6mr_multiaddr;
        uint        ipv6mr_interface;
    }

    enum : uint
    {
        IPPROTO_IPV6        = 41,

        //INET6_ADDRSTRLEN    = 46,

        IPV6_JOIN_GROUP     = 12,
        IPV6_LEAVE_GROUP    = 13,
        IPV6_MULTICAST_HOPS = 10,
        IPV6_MULTICAST_IF   = 9,
        IPV6_MULTICAST_LOOP = 11,
        IPV6_UNICAST_HOPS   = 4,
        IPV6_V6ONLY         = 27,
    }

    private enum
    {
        __IPV6_ADDR_SCOPE_NODELOCAL     = 0x01,
        __IPV6_ADDR_SCOPE_INTFACELOCAL  = 0x01,
        __IPV6_ADDR_SCOPE_LINKLOCAL     = 0x02,
        __IPV6_ADDR_SCOPE_SITELOCAL     = 0x05,
        __IPV6_ADDR_SCOPE_ORGLOCAL      = 0x08,
        __IPV6_ADDR_SCOPE_GLOBAL        = 0x0e,
    }

    // macros
    extern (D) int IN6_IS_ADDR_UNSPECIFIED( in in6_addr* a ) pure
    {
        return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) == 0);
    }

    extern (D) int IN6_IS_ADDR_LOOPBACK( in in6_addr* a ) pure
    {
        return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) == ntohl(1));
    }

    extern (D) int IN6_IS_ADDR_V4COMPAT( in in6_addr* a ) pure
    {
        return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) != 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) != ntohl(1));
    }

    extern (D) int IN6_IS_ADDR_V4MAPPED( in in6_addr* a ) pure
    {
        return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == ntohl(0x0000ffff));
    }

    extern (D) int IN6_IS_ADDR_LINKLOCAL( in in6_addr* a ) pure
    {
        return a.s6_addr[0] == 0xfe && (a.s6_addr[1] & 0xc0) == 0x80;
    }

    extern (D) int IN6_IS_ADDR_SITELOCAL( in in6_addr* a ) pure
    {
        return a.s6_addr[0] == 0xfe && (a.s6_addr[1] & 0xc0) == 0xc0;
    }

    extern (D) int IN6_IS_ADDR_MULTICAST( in in6_addr* a ) pure
    {
        return a.s6_addr[0] == 0xff;
    }

    extern (D) uint8_t __IPV6_ADDR_MC_SCOPE( in in6_addr* a ) pure
    {
        return a.s6_addr[1] & 0x0f;
    }

    extern (D) int IN6_IS_ADDR_MC_NODELOCAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_NODELOCAL;
    }

    extern (D) int IN6_IS_ADDR_MC_LINKLOCAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_LINKLOCAL;
    }

    extern (D) int IN6_IS_ADDR_MC_SITELOCAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_SITELOCAL;
    }

    extern (D) int IN6_IS_ADDR_MC_ORGLOCAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_ORGLOCAL;
    }

    extern (D) int IN6_IS_ADDR_MC_GLOBAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_GLOBAL;
    }
}
else version( DragonFlyBSD )
{
    struct in6_addr
    {
        union
        {
            uint8_t[16] s6_addr;
            uint16_t[8] s6_addr16;
            uint32_t[4] s6_addr32;
        }
    }

    struct sockaddr_in6
    {
        uint8_t     sin6_len;
        sa_family_t sin6_family;
        in_port_t   sin6_port;
        uint32_t    sin6_flowinfo;
        in6_addr    sin6_addr;
        uint32_t    sin6_scope_id;
    }

    extern __gshared immutable in6_addr in6addr_any;
    extern __gshared immutable in6_addr in6addr_loopback;

    struct ipv6_mreq
    {
        in6_addr    ipv6mr_multiaddr;
        uint        ipv6mr_interface;
    }

    enum : uint
    {
        IPPROTO_IPV6        = 41,

        //INET6_ADDRSTRLEN    = 46,

        IPV6_JOIN_GROUP     = 12,
        IPV6_LEAVE_GROUP    = 13,
        IPV6_MULTICAST_HOPS = 10,
        IPV6_MULTICAST_IF   = 9,
        IPV6_MULTICAST_LOOP = 11,
        IPV6_UNICAST_HOPS   = 4,
        IPV6_V6ONLY         = 27,
    }

    private enum
    {
        __IPV6_ADDR_SCOPE_NODELOCAL     = 0x01,
        __IPV6_ADDR_SCOPE_INTFACELOCAL  = 0x01,
        __IPV6_ADDR_SCOPE_LINKLOCAL     = 0x02,
        __IPV6_ADDR_SCOPE_SITELOCAL     = 0x05,
        __IPV6_ADDR_SCOPE_ORGLOCAL      = 0x08,
        __IPV6_ADDR_SCOPE_GLOBAL        = 0x0e,
    }

    // macros
    extern (D) int IN6_IS_ADDR_UNSPECIFIED( in in6_addr* a ) pure
    {
        return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) == 0);
    }

    extern (D) int IN6_IS_ADDR_LOOPBACK( in in6_addr* a ) pure
    {
        return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) == ntohl(1));
    }

    extern (D) int IN6_IS_ADDR_V4COMPAT( in in6_addr* a ) pure
    {
        return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) != 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) != ntohl(1));
    }

    extern (D) int IN6_IS_ADDR_V4MAPPED( in in6_addr* a ) pure
    {
        return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
               (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == ntohl(0x0000ffff));
    }

    extern (D) int IN6_IS_ADDR_LINKLOCAL( in in6_addr* a ) pure
    {
        return a.s6_addr[0] == 0xfe && (a.s6_addr[1] & 0xc0) == 0x80;
    }

    extern (D) int IN6_IS_ADDR_SITELOCAL( in in6_addr* a ) pure
    {
        return a.s6_addr[0] == 0xfe && (a.s6_addr[1] & 0xc0) == 0xc0;
    }

    extern (D) int IN6_IS_ADDR_MULTICAST( in in6_addr* a ) pure
    {
        return a.s6_addr[0] == 0xff;
    }

    extern (D) uint8_t __IPV6_ADDR_MC_SCOPE( in in6_addr* a ) pure
    {
        return a.s6_addr[1] & 0x0f;
    }

    extern (D) int IN6_IS_ADDR_MC_NODELOCAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_NODELOCAL;
    }

    extern (D) int IN6_IS_ADDR_MC_LINKLOCAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_LINKLOCAL;
    }

    extern (D) int IN6_IS_ADDR_MC_SITELOCAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_SITELOCAL;
    }

    extern (D) int IN6_IS_ADDR_MC_ORGLOCAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_ORGLOCAL;
    }

    extern (D) int IN6_IS_ADDR_MC_GLOBAL( in in6_addr* a ) pure
    {
        return IN6_IS_ADDR_MULTICAST(a) &&
               __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_GLOBAL;
    }
}
else version( Solaris )
{
    struct in6_addr
    {
        union
        {
            uint8_t[16] s6_addr;
            uint8_t[16] s6_addr8;
            uint32_t[4] s6_addr32;
        }
    }

    struct sockaddr_in6
    {
        sa_family_t sin6_family;
        in_port_t   sin6_port;
        uint32_t    sin6_flowinfo;
        in6_addr    sin6_addr;
        uint32_t    sin6_scope_id;
        uint32_t    __sin6_src_id;
    }

    extern __gshared immutable in6_addr in6addr_any;
    extern __gshared immutable in6_addr in6addr_loopback;

    struct ipv6_mreq
    {
        in6_addr    ipv6mr_multiaddr;
        uint        ipv6mr_interface;
    }

    enum : uint
    {
        IPPROTO_IPV6        = 41,

        //INET6_ADDRSTRLEN    = 46,

        IPV6_JOIN_GROUP     = 9,
        IPV6_LEAVE_GROUP    = 10,
        IPV6_MULTICAST_HOPS = 7,
        IPV6_MULTICAST_IF   = 6,
        IPV6_MULTICAST_LOOP = 8,
        IPV6_UNICAST_HOPS   = 5,
        IPV6_V6ONLY         = 39,
    }

    // macros
    extern (D) int IN6_IS_ADDR_UNSPECIFIED( in in6_addr* a ) pure
    {
        return (a.s6_addr32[0] == 0) && (a.s6_addr32[1] == 0) &&
               (a.s6_addr32[2] == 0) && (a.s6_addr32[3] == 0);
    }

    extern (D) int IN6_IS_ADDR_LOOPBACK( in in6_addr* a ) pure
    {
        return (a.s6_addr32[0] == 0) && (a.s6_addr32[1] == 0) &&
               (a.s6_addr32[2] == 0) && (a.s6_addr32[3] == ntohl(1));
    }

    extern (D) int IN6_IS_ADDR_V4COMPAT( in in6_addr* a ) pure
    {
        return (a.s6_addr32[0] == 0) && (a.s6_addr32[1] == 0) &&
               (a.s6_addr32[2] == 0) && (a.s6_addr32[3] != 0) &&
               (a.s6_addr32[3] != ntohl(1));
    }

    extern (D) int IN6_IS_ADDR_V4MAPPED( in in6_addr* a ) pure
    {
        return (a.s6_addr32[0] == 0) && (a.s6_addr32[1] == 0) &&
               (a.s6_addr32[2] == ntohl(0x0000ffff));
    }

    extern (D) int IN6_IS_ADDR_LINKLOCAL( in in6_addr* a ) pure
    {
        return a.s6_addr8[0] == 0xfe && (a.s6_addr8[1] & 0xc0) == 0x80;
    }

    extern (D) int IN6_IS_ADDR_SITELOCAL( in in6_addr* a ) pure
    {
        return a.s6_addr8[0] == 0xfe && (a.s6_addr8[1] & 0xc0) == 0xc0;
    }

    extern (D) int IN6_IS_ADDR_MULTICAST( in in6_addr* a ) pure
    {
        return a.s6_addr8[0] == 0xff;
    }

    extern (D) int IN6_IS_ADDR_MC_NODELOCAL( in in6_addr* a ) pure
    {
        return a.s6_addr8[0] == 0xff && (a.s6_addr8[1] & 0x0f) == 0x01;
    }

    extern (D) int IN6_IS_ADDR_MC_LINKLOCAL( in in6_addr* a ) pure
    {
        return a.s6_addr8[0] == 0xff && (a.s6_addr8[1] & 0x0f) == 0x02;
    }

    extern (D) int IN6_IS_ADDR_MC_SITELOCAL( in in6_addr* a ) pure
    {
        return a.s6_addr8[0] == 0xff && (a.s6_addr8[1] & 0x0f) == 0x05;
    }

    extern (D) int IN6_IS_ADDR_MC_ORGLOCAL( in in6_addr* a ) pure
    {
        return a.s6_addr8[0] == 0xff && (a.s6_addr8[1] & 0x0f) == 0x08;
    }

    extern (D) int IN6_IS_ADDR_MC_GLOBAL( in in6_addr* a ) pure
    {
        return a.s6_addr8[0] == 0xff && (a.s6_addr8[1] & 0x0f) == 0x0e;
    }
}
else version( CRuntime_Bionic )
{
    struct in6_addr
    {
        union
        {
            uint8_t[16] s6_addr;
            uint16_t[8] s6_addr16;
            uint32_t[4] s6_addr32;
        }
    }

    struct sockaddr_in6
    {
        ushort      sin6_family;
        uint16_t    sin6_port;
        uint32_t    sin6_flowinfo;
        in6_addr    sin6_addr;
        uint32_t    sin6_scope_id;
    }

    __gshared immutable in6_addr in6addr_any = {[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]};
    __gshared immutable in6_addr in6addr_loopback = {[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]};

    struct ipv6_mreq
    {
        in6_addr ipv6mr_multiaddr;
        int      ipv6mr_ifindex;
    }

    enum : uint
    {
        IPPROTO_IPV6        = 41,

        IPV6_JOIN_GROUP     = 20,
        IPV6_LEAVE_GROUP    = 21,
        IPV6_MULTICAST_HOPS = 18,
        IPV6_MULTICAST_IF   = 17,
        IPV6_MULTICAST_LOOP = 19,
        IPV6_UNICAST_HOPS   = 16,
        IPV6_V6ONLY         = 26
    }

    private enum
    {
        IPV6_ADDR_SCOPE_NODELOCAL     = 0x01,
        IPV6_ADDR_SCOPE_INTFACELOCAL  = 0x01,
        IPV6_ADDR_SCOPE_LINKLOCAL     = 0x02,
        IPV6_ADDR_SCOPE_SITELOCAL     = 0x05,
        IPV6_ADDR_SCOPE_ORGLOCAL      = 0x08,
        IPV6_ADDR_SCOPE_GLOBAL        = 0x0e,
    }

    extern (D) pure
    {
        bool IN6_IS_ADDR_UNSPECIFIED( in in6_addr* a )
        {
            return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
                   (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
                   (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) &&
                   (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) == 0);
        }

        bool IN6_IS_ADDR_LOOPBACK( in in6_addr* a )
        {
            return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
                   (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
                   (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) &&
                   (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) == ntohl(1));
        }

        bool IN6_IS_ADDR_V4COMPAT( in in6_addr* a )
        {
            return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
                   (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
                   (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) &&
                   (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) != 0) &&
                   (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) != ntohl(1));
        }

        bool IN6_IS_ADDR_V4MAPPED( in in6_addr* a )
        {
            return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) &&
                   (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) &&
                   (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == ntohl(0x0000ffff));
        }

        bool IN6_IS_ADDR_LINKLOCAL( in in6_addr* a )
        {
            return a.s6_addr[0] == 0xfe && (a.s6_addr[1] & 0xc0) == 0x80;
        }

        bool IN6_IS_ADDR_SITELOCAL( in in6_addr* a )
        {
            return a.s6_addr[0] == 0xfe && (a.s6_addr[1] & 0xc0) == 0xc0;
        }

        bool IN6_IS_ADDR_ULA( in in6_addr* a )
        {
            return (a.s6_addr[0] & 0xfe) == 0xfc;
        }

        bool IN6_IS_ADDR_MULTICAST( in in6_addr* a )
        {
            return a.s6_addr[0] == 0xff;
        }

        uint8_t IPV6_ADDR_MC_SCOPE( in in6_addr* a )
        {
            return a.s6_addr[1] & 0x0f;
        }

        bool IN6_IS_ADDR_MC_NODELOCAL( in in6_addr* a )
        {
            return IN6_IS_ADDR_MULTICAST(a) &&
                   IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_NODELOCAL;
        }

        bool IN6_IS_ADDR_MC_LINKLOCAL( in in6_addr* a )
        {
            return IN6_IS_ADDR_MULTICAST(a) &&
                   IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_LINKLOCAL;
        }

        bool IN6_IS_ADDR_MC_SITELOCAL( in in6_addr* a )
        {
            return IN6_IS_ADDR_MULTICAST(a) &&
                   IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_SITELOCAL;
        }

        bool IN6_IS_ADDR_MC_ORGLOCAL( in in6_addr* a )
        {
            return IN6_IS_ADDR_MULTICAST(a) &&
                   IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_ORGLOCAL;
        }

        bool IN6_IS_ADDR_MC_GLOBAL( in in6_addr* a )
        {
            return IN6_IS_ADDR_MULTICAST(a) &&
                   IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_GLOBAL;
        }
    }
}
else version( CRuntime_Musl )
{

    struct in6_addr {
        union {
            uint8_t[16] s6_addr;
            uint16_t[8] s6_addr16;
            uint32_t[4] s6_addr32;
        }
    }
    struct sockaddr_in6 {
        sa_family_t     sin6_family;
        in_port_t       sin6_port;
        uint32_t        sin6_flowinfo;
        in6_addr        sin6_addr;
        uint32_t        sin6_scope_id;
    }

    enum : uint
    {
        IPV6_UNICAST_HOPS   = 16,
        IPV6_MULTICAST_IF   = 17,
        IPV6_MULTICAST_HOPS = 18,
        IPV6_MULTICAST_LOOP = 19,
        IPV6_JOIN_GROUP     = 20,
        IPV6_LEAVE_GROUP    = 21,
        IPV6_V6ONLY         = 26
    }
    extern __gshared immutable in6_addr in6addr_any;
    extern __gshared immutable in6_addr in6addr_loopback;
}
else version ( CRuntime_UClibc )
{
    struct in6_addr
    {
        union
        {
            uint8_t[16] s6_addr;
            uint16_t[8] s6_addr16;
            uint32_t[4] s6_addr32;
        }
    }

    struct sockaddr_in6
    {
        sa_family_t sin6_family;
        in_port_t   sin6_port;
        uint32_t    sin6_flowinfo;
        in6_addr    sin6_addr;
        uint32_t    sin6_scope_id;
    }

    extern __gshared immutable in6_addr in6addr_any;
    extern __gshared immutable in6_addr in6addr_loopback;

    struct ipv6_mreq
    {
        in6_addr    ipv6mr_multiaddr;
        int         ipv6mr_ifindex;
    }

    enum : uint
    {
        IPPROTO_IPV6        = 41,
        IPV6_JOIN_GROUP     = 20,
        IPV6_LEAVE_GROUP    = 21,
        IPV6_MULTICAST_HOPS = 18,
        IPV6_MULTICAST_IF   = 17,
        IPV6_MULTICAST_LOOP = 19,
        IPV6_UNICAST_HOPS   = 16,
        IPV6_V6ONLY         = 26
    }

    // macros
    extern (D) int IN6_IS_ADDR_UNSPECIFIED( in6_addr* addr ) pure
    {
        return (cast(uint32_t*) addr)[0] == 0 &&
               (cast(uint32_t*) addr)[1] == 0 &&
               (cast(uint32_t*) addr)[2] == 0 &&
               (cast(uint32_t*) addr)[3] == 0;
    }

    extern (D) int IN6_IS_ADDR_LOOPBACK( in6_addr* addr ) pure
    {
        return (cast(uint32_t*) addr)[0] == 0  &&
               (cast(uint32_t*) addr)[1] == 0  &&
               (cast(uint32_t*) addr)[2] == 0  &&
               (cast(uint32_t*) addr)[3] == htonl( 1 );
    }

    extern (D) int IN6_IS_ADDR_MULTICAST( in6_addr* addr ) pure
    {
        return (cast(uint8_t*) addr)[0] == 0xff;
    }

    extern (D) int IN6_IS_ADDR_LINKLOCAL( in6_addr* addr ) pure
    {
        return ((cast(uint32_t*) addr)[0] & htonl( 0xffc00000 )) == htonl( 0xfe800000 );
    }

    extern (D) int IN6_IS_ADDR_SITELOCAL( in6_addr* addr ) pure
    {
        return ((cast(uint32_t*) addr)[0] & htonl( 0xffc00000 )) == htonl( 0xfec00000 );
    }

    extern (D) int IN6_IS_ADDR_V4MAPPED( in6_addr* addr ) pure
    {
        return (cast(uint32_t*) addr)[0] == 0 &&
               (cast(uint32_t*) addr)[1] == 0 &&
               (cast(uint32_t*) addr)[2] == htonl( 0xffff );
    }

    extern (D) int IN6_IS_ADDR_V4COMPAT( in6_addr* addr ) pure
    {
        return (cast(uint32_t*) addr)[0] == 0 &&
               (cast(uint32_t*) addr)[1] == 0 &&
               (cast(uint32_t*) addr)[2] == 0 &&
               ntohl( (cast(uint32_t*) addr)[3] ) > 1;
    }

    extern (D) int IN6_ARE_ADDR_EQUAL( in6_addr* a, in6_addr* b ) pure
    {
        return (cast(uint32_t*) a)[0] == (cast(uint32_t*) b)[0] &&
               (cast(uint32_t*) a)[1] == (cast(uint32_t*) b)[1] &&
               (cast(uint32_t*) a)[2] == (cast(uint32_t*) b)[2] &&
               (cast(uint32_t*) a)[3] == (cast(uint32_t*) b)[3];
    }

    extern (D) int IN6_IS_ADDR_MC_NODELOCAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST( addr ) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0x1;
    }

    extern (D) int IN6_IS_ADDR_MC_LINKLOCAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST( addr ) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0x2;
    }

    extern (D) int IN6_IS_ADDR_MC_SITELOCAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST(addr) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0x5;
    }

    extern (D) int IN6_IS_ADDR_MC_ORGLOCAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST( addr) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0x8;
    }

    extern (D) int IN6_IS_ADDR_MC_GLOBAL( in6_addr* addr ) pure
    {
        return IN6_IS_ADDR_MULTICAST( addr ) &&
               ((cast(uint8_t*) addr)[1] & 0xf) == 0xe;
    }
}


//
// Raw Sockets (RS)
//
/*
IPPROTO_RAW
*/

version( CRuntime_Glibc )
{
    enum uint IPPROTO_RAW = 255;
}
else version( Darwin )
{
    enum uint IPPROTO_RAW = 255;
}
else version( FreeBSD )
{
    enum uint IPPROTO_RAW = 255;
}
else version(NetBSD)
{
    enum uint IPPROTO_RAW = 255;
}
else version( DragonFlyBSD )
{
    enum uint IPPROTO_RAW = 255;
}
else version( Solaris )
{
    enum uint IPPROTO_RAW = 255;
}
else version( linux )
{
    enum uint IPPROTO_RAW = 255;
}
else version( CRuntime_UClibc )
{
    enum uint IPPROTO_RAW = 255;
}
