/*++

Copyright (c) Microsoft. All rights reserved.

Module Name:

    netlink.c

Abstract:

    Linux socket test for the AF_NETLINK family.

--*/

#include "lxtcommon.h"
#include "unittests.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <linux/icmp.h>
#include <netinet/icmp6.h>
#include <net/if.h>
#include <netdb.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>
#include <poll.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/prctl.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include "common.h"

#define LXT_NAME "Netlink"

#define ATTRIBUTE_DUMP_BUFFER_SIZE 60

#define min(a, b) (((a) < (b)) ? (a) : (b))

#define SOCKET_LOOPBACK_IF_NAME "lo"

#define NLMSG_TAIL(nmsg) ((struct rtattr*)(((void*)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))

const int MessageTypes[] = {
    RTM_DELADDR, RTM_DELLINK, RTM_DELROUTE, RTM_GETADDR, RTM_GETLINK, RTM_GETNSID, RTM_GETROUTE, RTM_NEWADDR, RTM_NEWLINK, RTM_NEWROUTE, RTM_SETLINK};

const int SupportedFamily[] = {NETLINK_ROUTE};
const int SupportedType[] = {SOCK_DGRAM, SOCK_RAW};

typedef struct _NetlinkRecvmmsgBlockedReaderParams
{
    int Socket;
    int Option;
} NetlinkRecvmmsgBlockedReaderParams;

typedef struct _BindChildThreadReturn
{
    pid_t nl_pid;
} BindChildThreadReturn;

LXT_VARIATION_HANDLER SocketNetlinkOpenClose;
LXT_VARIATION_HANDLER SocketNetlinkBasic;
LXT_VARIATION_HANDLER SocketNetlinkBind;
LXT_VARIATION_HANDLER SocketNetlinkBindThread;
LXT_VARIATION_HANDLER SocketNetlinkSendReceive;
LXT_VARIATION_HANDLER SocketNetlinkSendReceiveOverflow;
LXT_VARIATION_HANDLER SocketNetlinkBlockedReader;
LXT_VARIATION_HANDLER SocketNetlinkEpoll;
LXT_VARIATION_HANDLER SocketNetlinkRecvmmsg;
LXT_VARIATION_HANDLER SocketNetlinkSendBadMessage;
LXT_VARIATION_HANDLER SocketNetlinkRouteGetAddr;
LXT_VARIATION_HANDLER SocketNetlinkRouteGetLink;
LXT_VARIATION_HANDLER SocketNetlinkRouteGetRouteBestRoute;
LXT_VARIATION_HANDLER SocketNetlinkRouteGetRouteDump;
LXT_VARIATION_HANDLER SocketNetlinkRouteNewDelAddress;
LXT_VARIATION_HANDLER SocketNetlinkRouteNewDelRoute;
LXT_VARIATION_HANDLER SocketNetlinkSoPasscred;

void* SocketNetlinkBindThreadChild(void* Arg);

bool SocketNetlinkIsIpv6Configured();

void* SocketNetlinkRecvmmsgBlockedReaderThread(void* Arg);

void SocketNetlinkRouteDumpAttributeData(char* Buffer, int BufferSize, struct rtattr* Attribute);

int SocketNetlinkRouteGetLinkCheckResponse(int Socket, int OnlyOneLoopback);

int SocketNetlinkRouteGetRouteBestRouteCheckResponse(int Socket, int CheckGateway);

int SocketNetlinkGetLoopbackIndex();

int SocketNetlinkCheckResponseError(void* ReceiveBuffer, int ReceiveResult, int Error);

int SocketNetlinkRouteAddAttribute(struct nlmsghdr* Msghdr, int MessageSize, int AttributeType, void* AttributeData, int AttributeSize);

void SocketNetlinkRouteAddAddressAttributes(struct nlmsghdr* Msghdr, int MessageSize, struct in_addr* AddressIpv4);

void SocketNetlinkRouteAddRouteAttributes(
    struct nlmsghdr* Msghdr, int MessageSize, struct in_addr* DestinationIpv4, struct in_addr* GatewayIpv4, int InterfaceIndex);

int SocketNetlinkSendAndWaitForExpectedError(int Socket, void* Request, int RequestSize, int ExpectedError);

int SocketNetlinkSetAndVerifySocketOptionTimeout(int Socket, int SocketOption, int Usec);

static const LXT_VARIATION g_LxtVariations[] = {
    {"Open, close", SocketNetlinkOpenClose},
    {"Basic netlink operations", SocketNetlinkBasic},
    {"bind, getsockname (with fork)", SocketNetlinkBind},
    {"bind, getsockname (with pthread_create)", SocketNetlinkBindThread},
    {"Send and receive basic (sending to kernel)", SocketNetlinkSendReceive},
    {"Send and receive where the receive buffer overflows (sending to kernel)", SocketNetlinkSendReceiveOverflow},
    {"Blocked reader thread", SocketNetlinkBlockedReader},
    {"epoll", SocketNetlinkEpoll},
    {"Recvmmsg syscall", SocketNetlinkRecvmmsg},
    {"Sending bad Netlink messages", SocketNetlinkSendBadMessage},
    {"NETLINK_ROUTE RTM_GETADDR message", SocketNetlinkRouteGetAddr},
    {"NETLINK_ROUTE RTM_GETLINK message", SocketNetlinkRouteGetLink},
    {"NETLINK_ROUTE RTM_GETROUTE message - get best route", SocketNetlinkRouteGetRouteBestRoute},
    {"NETLINK_ROUTE RTM_GETROUTE message - dump all routing entries", SocketNetlinkRouteGetRouteDump},
    {"NETLINK_ROUTE RTM_NEWADDR and RTM_DELADDR message", SocketNetlinkRouteNewDelAddress},
    {"NETLINK_ROUTE RTM_NEWROUTE and RTM_DELROUTE message", SocketNetlinkRouteNewDelRoute},
    {"SO_PASSCRED", SocketNetlinkSoPasscred},
};

//
// Function definitions.
//

int NetlinkTestEntry(int Argc, char* Argv[])

/*++

Routine Description:

    This routine main entry point for the test for get*id,set*id system call.

Arguments:

    Argc - Supplies the number of command line arguments.

    Argv - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    LXT_ARGS Args;
    int Result;

    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));

    //
    // Run test cases.
    //

    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));

ErrorExit:
    LxtUninitialize();
    return !LXT_SUCCESS(Result);
}

bool SocketNetlinkIsIpv6Configured()

/*++

Routine Description:

    This routine checks if an IPv6 address has been configured.

Arguments:

    None.

Return Value:

    Returns true if an IPv6 address is available.

--*/

{

    socklen_t AddressLength;
    struct rtattr* Attribute;
    char AttributeDump[ATTRIBUTE_DUMP_BUFFER_SIZE];
    struct sockaddr_nl BindAddress;
    int FoundDone;
    struct nlmsghdr* Header;
    struct ifaddrmsg* IfAddrMsg;
    bool IpV6AddressValid;
    int LoopbackIndex;
    char ReceiveBuffer[5000];
    int ReceiveResult;
    int RemainingLength;
    int Result;
    int Socket;

    struct
    {
        struct nlmsghdr nlh;
        struct ifinfomsg ifm;
        struct rtattr ext_req __attribute__((aligned(NLMSG_ALIGNTO)));
        __u32 ext_filter_mask;
    } Request;

    IpV6AddressValid = false;

    LxtCheckErrnoZeroSuccess(SocketNetlinkGetLoopbackIndex(&LoopbackIndex));

    //
    // Create and bind socket. Create a RTM_GETADDR request.
    //

    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = sizeof(Request);
    Request.nlh.nlmsg_type = RTM_GETADDR;
    Request.nlh.nlmsg_seq = 0x4563;
    Request.ifm.ifi_family = AF_NETLINK;
    Request.ext_req.rta_type = IFLA_EXT_MASK;
    Request.ext_req.rta_len = RTA_LENGTH(sizeof(__u32));
    Request.ext_filter_mask = RTEXT_FILTER_VF;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    FoundDone = 0;
    for (;;)
    {
        LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0));

        Header = (struct nlmsghdr*)ReceiveBuffer;
        while (!IpV6AddressValid && NLMSG_OK(Header, ReceiveResult))
        {
            if (Header->nlmsg_type == NLMSG_DONE)
            {
                FoundDone = 1;
                break;
            }

            IfAddrMsg = (struct ifaddrmsg*)NLMSG_DATA(Header);
            if ((IfAddrMsg->ifa_index != LoopbackIndex) && (IfAddrMsg->ifa_family == AF_INET6))
            {

                Attribute = (struct rtattr*)((char*)IfAddrMsg + sizeof(struct ifaddrmsg));

                RemainingLength = Header->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));

                while (RTA_OK(Attribute, RemainingLength))
                {
                    SocketNetlinkRouteDumpAttributeData(AttributeDump, ATTRIBUTE_DUMP_BUFFER_SIZE, Attribute);

                    //
                    // If the address is IPv6 and does not start with the link
                    // local prefix, assume it is a valid address.
                    //

                    if ((Attribute->rta_type == IFA_ADDRESS) && strncmp(AttributeDump, "fe 80", 5) != 0)
                    {

                        LxtLogInfo("IpV6 address found: %s", AttributeDump);
                        IpV6AddressValid = true;
                        break;
                    }

                    Attribute = RTA_NEXT(Attribute, RemainingLength);
                }
            }

            Header = NLMSG_NEXT(Header, ReceiveResult);
        }

        if (IpV6AddressValid || (FoundDone == 1))
        {
            break;
        }
    }

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return IpV6AddressValid;
}

int SocketNetlinkOpenClose(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests the socket() API.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    int FamilyIterator;
    int Result;
    int Socket;
    int TypeIterator;

    Result = LXT_RESULT_FAILURE;
    Socket = -1;

    for (TypeIterator = 0; TypeIterator < sizeof(SupportedType) / sizeof(int); TypeIterator++)
    {

        for (FamilyIterator = 0; FamilyIterator < sizeof(SupportedFamily) / sizeof(int); FamilyIterator++)
        {

            LxtLogInfo("testing type: %d, netlink family: %d", SupportedType[TypeIterator], SupportedFamily[FamilyIterator]);

            //
            // Success cases.
            //

            LxtCheckErrno((Socket = socket(AF_NETLINK, SupportedType[TypeIterator], SupportedFamily[FamilyIterator])));

            LxtClose(Socket);

            //
            // Test case: Overloading 'type' field for netlink sockets.
            //

            LxtCheckErrno((Socket = socket(AF_NETLINK, SupportedType[TypeIterator] | O_NONBLOCK, SupportedFamily[FamilyIterator])));

            LxtClose(Socket);
            LxtCheckErrno((Socket = socket(AF_NETLINK, SupportedType[TypeIterator] | O_CLOEXEC, SupportedFamily[FamilyIterator])));

            LxtClose(Socket);
            LxtCheckErrno((Socket = socket(AF_NETLINK, SupportedType[TypeIterator] | O_NONBLOCK | O_CLOEXEC, SupportedFamily[FamilyIterator])));

            LxtClose(Socket);
        }
    }

    //
    // Test case: Failure cases for socket(), unsupported type, family.
    //

    LxtCheckErrnoFailure(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE + 100), EPROTONOSUPPORT);

    LxtCheckErrnoFailure(socket(AF_NETLINK, SOCK_STREAM, NETLINK_ROUTE), ESOCKTNOSUPPORT);

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

int SocketNetlinkBasic(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests basic operations on netlink sockets.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    struct sockaddr_nl BindAddress;
    int BufferSize;
    int FamilyIterator;
    socklen_t OptionLength;
    int OptionInt;
    char ReceiveBuffer[500];
    int Result;
    int Socket;
    int SocketError;
    struct timeval Timeout;
    int TypeIterator;

    Result = LXT_RESULT_FAILURE;
    Socket = -1;

    for (TypeIterator = 0; TypeIterator < sizeof(SupportedType) / sizeof(int); TypeIterator++)
    {

        for (FamilyIterator = 0; FamilyIterator < sizeof(SupportedFamily) / sizeof(int); FamilyIterator++)
        {

            LxtCheckErrno(Socket = socket(AF_NETLINK, SupportedType[TypeIterator], SupportedFamily[FamilyIterator]));

            memset(&BindAddress, 0, sizeof(BindAddress));
            BindAddress.nl_family = AF_NETLINK;
            AddressLength = sizeof(BindAddress);
            LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));
            OptionLength = sizeof(BufferSize);
            BufferSize = 2345;
            LxtCheckErrno(setsockopt(Socket, SOL_SOCKET, SO_SNDBUF, &BufferSize, OptionLength));

            LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_SNDBUF, &BufferSize, &OptionLength));

            LxtCheckEqual(OptionLength, sizeof(BufferSize), "%d");
            LxtCheckEqual(BufferSize, 2345 * 2, "%d");

            OptionLength = sizeof(BufferSize);
            BufferSize = 6345;
            LxtCheckErrno(setsockopt(Socket, SOL_SOCKET, SO_RCVBUF, &BufferSize, OptionLength));

            LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_RCVBUF, &BufferSize, &OptionLength));

            LxtCheckEqual(OptionLength, sizeof(BufferSize), "%d");
            LxtCheckEqual(BufferSize, 6345 * 2, "%d");

            //
            // Set a timeout value of 8 milliseconds for the send.
            //

            LxtCheckResult(SocketNetlinkSetAndVerifySocketOptionTimeout(Socket, SO_SNDTIMEO, 8000));

            //
            // Set a timeout value of 8 milliseconds for the receive.
            //

            LxtCheckResult(SocketNetlinkSetAndVerifySocketOptionTimeout(Socket, SO_RCVTIMEO, 8000));

            //
            // Check that the blocking read will timeout with EAGAIN
            // after 8 milliseconds.
            //

            LxtCheckErrnoFailure(read(Socket, ReceiveBuffer, sizeof(ReceiveBuffer)), EAGAIN);

            //
            // Check that the value of SO_ERROR is 0 (no error).
            //

            SocketError = 1234;
            OptionLength = sizeof(SocketError);
            LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_ERROR, &SocketError, &OptionLength));

            LxtCheckEqual(OptionLength, sizeof(SocketError), "%d");
            LxtCheckEqual(SocketError, 0, "%d");

            OptionLength = sizeof(OptionInt);
            LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_TYPE, &OptionInt, &OptionLength));

            LxtCheckEqual(OptionLength, sizeof(OptionInt), "%d");
            LxtCheckEqual(OptionInt, SupportedType[TypeIterator], "%d");

            OptionLength = sizeof(OptionInt);
            LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_PROTOCOL, &OptionInt, &OptionLength));

            LxtCheckEqual(OptionLength, sizeof(OptionInt), "%d");
            LxtCheckEqual(OptionInt, SupportedFamily[FamilyIterator], "%d");

            OptionLength = sizeof(OptionInt);
            LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_DOMAIN, &OptionInt, &OptionLength));

            LxtCheckEqual(OptionLength, sizeof(OptionInt), "%d");
            LxtCheckEqual(OptionInt, AF_NETLINK, "%d");
            LxtClose(Socket);
        }
    }

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

int SocketNetlinkBind(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests the bind() API.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    char AddressBuffer[150];
    socklen_t AddressLength;
    int ArrayIterator;
    int ArrayIterator2;
    const int ArraySize = 10;
    struct sockaddr_nl BindAddress;
    struct sockaddr_nl* BindAddressP;
    int ChildPid;
    char DataBuffer[10];
    int Family;
    int FamilyIterator;
    int Result;
    uint32_t SavedPid;
    int Socket;
    int Socket2;
    int Socket3;
    int Socket4;
    int SocketA[ArraySize];
    int SocketChildA[ArraySize];
    int SocketChildPid[ArraySize];
    int SocketPid[ArraySize];
    int Type;
    int TypeIterator;

    Result = LXT_RESULT_FAILURE;
    Socket = -1;
    Socket2 = -1;
    Socket3 = -1;
    Socket4 = -1;
    ChildPid = -1;
    for (TypeIterator = 0; TypeIterator < sizeof(SupportedType) / sizeof(int); TypeIterator++)
    {

        Type = SupportedType[TypeIterator];
        for (FamilyIterator = 0; FamilyIterator < sizeof(SupportedFamily) / sizeof(int); FamilyIterator++)
        {

            //
            // First thing; initialize the various array.
            //

            for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)
            {
                SocketA[ArrayIterator] = 0;
                SocketPid[ArrayIterator] = 0;
                SocketChildPid[ArrayIterator] = 0;
            }

            Family = SupportedFamily[FamilyIterator];
            LxtLogInfo("testing type: %d, netlink family: %d", SupportedType[TypeIterator], SupportedFamily[FamilyIterator]);

            //
            // Test case: bind with invalid family value in the address.
            //

            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));
            memset(&BindAddress, 0, sizeof(BindAddress));
            BindAddress.nl_family = AF_INET;
            AddressLength = sizeof(BindAddress);
            LxtCheckErrnoFailure(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength), EINVAL);

            LxtClose(Socket);

            //
            // Test case: bind with invalid address length.
            //

            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));
            memset(&BindAddress, 0, sizeof(BindAddress));
            BindAddress.nl_family = AF_NETLINK;
            AddressLength = sizeof(BindAddress.nl_family);
            LxtCheckErrnoFailure(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength), EINVAL);

            LxtClose(Socket);

            //
            // Test case: address length > sizeof(sockaddr_nl) is also invalid.
            //

            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));
            BindAddressP = (struct sockaddr_nl*)AddressBuffer;
            memset(AddressBuffer, 0, sizeof(AddressBuffer));
            BindAddressP->nl_family = AF_NETLINK;
            AddressLength = sizeof(AddressBuffer);
            LxtCheckErrnoFailure(bind(Socket, (struct sockaddr*)BindAddressP, AddressLength), EINVAL);

            LxtClose(Socket);

            //
            // Test case: getsockname on unbound socket returns nl_pid = 0.
            //

            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));
            memset(&BindAddress, 0, sizeof(BindAddress));
            AddressLength = sizeof(BindAddress);
            LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));

            LxtCheckEqual(BindAddress.nl_pid, 0, "%d");
            LxtCheckEqual(AddressLength, sizeof(BindAddress), "%d");
            LxtClose(Socket);

            //
            // Test case: calling sendto() on unbound socket automatically
            //            binds the socket (even if the sendto() fails).
            //

            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));
            memset(DataBuffer, 0, sizeof(DataBuffer));
            memset(&BindAddress, 0, sizeof(BindAddress));
            BindAddress.nl_family = AF_NETLINK;
            AddressLength = sizeof(BindAddress);
            sendto(Socket, DataBuffer, sizeof(DataBuffer), 0, (struct sockaddr*)&BindAddress, AddressLength);

            memset(&BindAddress, 0, sizeof(BindAddress));
            LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));

            LxtCheckEqual(BindAddress.nl_pid, getpid(), "%d");
            LxtCheckEqual(AddressLength, sizeof(BindAddress), "%d");
            LxtClose(Socket);

            //
            // Test case: Bind with nl_pid = 0, kernel should assign a unique
            //            ID. The first netlink socket of the process is
            //            assigned the process ID as the nl_pid. It also confirms
            //            the pad value is ignored.
            //

            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));
            memset(&BindAddress, 0, sizeof(BindAddress));
            BindAddress.nl_family = AF_NETLINK;
            BindAddress.nl_pad = 1;
            AddressLength = sizeof(BindAddress);
            LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

            memset(&BindAddress, 0, sizeof(BindAddress));
            LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));

            LxtCheckEqual(BindAddress.nl_pid, getpid(), "%d");
            LxtCheckEqual(AddressLength, sizeof(BindAddress), "%d");

            //
            // Validate that a socket that is already bound, cannot be bound again.
            //

            memset(&BindAddress, 0, sizeof(BindAddress));
            BindAddress.nl_family = AF_NETLINK;
            AddressLength = sizeof(BindAddress);
            LxtCheckErrnoFailure(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength), EINVAL);

            //
            // Test case: The user specifies a negative nl_pid (which is what the
            //            kernel assigns to the non-first sockets of the process).
            //            Test that the kernel will skip any negative nl_pid's that
            //            the user already specified. For example, Socket2 gets
            //            an auto-assigned nl_pid of -5. Socket3 is bound with
            //            the user specifying nl_pid of -4. Socket4 gets an
            //            auto-assigned nl_pid of -3, since -4 was already taken
            //            by the user.
            //

            LxtCheckErrno(Socket2 = socket(AF_NETLINK, Type, Family));
            memset(&BindAddress, 0, sizeof(BindAddress));
            BindAddress.nl_family = AF_NETLINK;
            AddressLength = sizeof(BindAddress);
            LxtCheckErrno(bind(Socket2, (struct sockaddr*)&BindAddress, AddressLength));

            memset(&BindAddress, 0, sizeof(BindAddress));
            LxtCheckErrno(getsockname(Socket2, (struct sockaddr*)&BindAddress, &AddressLength));

            LxtCheckTrue((int)BindAddress.nl_pid < 0);
            LxtCheckEqual(AddressLength, sizeof(BindAddress), "%d");

            LxtCheckErrno(Socket3 = socket(AF_NETLINK, Type, Family));
            BindAddress.nl_pid += 1;
            AddressLength = sizeof(BindAddress);
            LxtCheckErrno(bind(Socket3, (struct sockaddr*)&BindAddress, AddressLength));

            memset(&BindAddress, 0, sizeof(BindAddress));
            LxtCheckErrno(getsockname(Socket3, (struct sockaddr*)&BindAddress, &AddressLength));

            LxtCheckTrue((int)BindAddress.nl_pid < 0);
            LxtCheckEqual(AddressLength, sizeof(BindAddress), "%d");

            LxtCheckErrno(Socket4 = socket(AF_NETLINK, Type, Family));
            memset(&BindAddress, 0, sizeof(BindAddress));
            BindAddress.nl_family = AF_NETLINK;
            AddressLength = sizeof(BindAddress);
            LxtCheckErrno(bind(Socket4, (struct sockaddr*)&BindAddress, AddressLength));

            memset(&BindAddress, 0, sizeof(BindAddress));
            LxtCheckErrno(getsockname(Socket4, (struct sockaddr*)&BindAddress, &AddressLength));

            LxtCheckTrue((int)BindAddress.nl_pid < 0);
            LxtCheckEqual(AddressLength, sizeof(BindAddress), "%d");
            LxtClose(Socket3);
            LxtClose(Socket4);

            //
            // Test case: The kernel assigned a negative custom PID for Socket2.
            //            Save Socket2's custom PID, close Socket2, then bind a
            //            new socket with the saved PID, and verify success.
            //

            AddressLength = sizeof(BindAddress);
            memset(&BindAddress, 0, sizeof(BindAddress));
            LxtCheckErrno(getsockname(Socket2, (struct sockaddr*)&BindAddress, &AddressLength));

            LxtCheckTrue((int)BindAddress.nl_pid < 0);
            LxtCheckEqual(AddressLength, sizeof(BindAddress), "%d");
            SavedPid = BindAddress.nl_pid;
            LxtClose(Socket2);
            LxtCheckErrno(Socket2 = socket(AF_NETLINK, Type, Family));
            AddressLength = sizeof(BindAddress);
            LxtCheckErrno(bind(Socket2, (struct sockaddr*)&BindAddress, AddressLength));

            memset(&BindAddress, 0, sizeof(BindAddress));
            LxtCheckErrno(getsockname(Socket2, (struct sockaddr*)&BindAddress, &AddressLength));

            LxtCheckEqual((int)BindAddress.nl_pid, SavedPid, "%d");
            LxtCheckEqual(AddressLength, sizeof(BindAddress), "%d");
            LxtClose(Socket2);

            //
            // Test case: close the first socket of the process and open a new
            //            one. Being the first netlink socket of the process,
            //            it should get the process ID as the 'nl_pid'.
            //

            LxtClose(Socket);
            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));
            memset(&BindAddress, 0, sizeof(BindAddress));
            BindAddress.nl_family = AF_NETLINK;
            AddressLength = sizeof(BindAddress);
            LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

            memset(&BindAddress, 0, sizeof(BindAddress));
            LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));

            LxtCheckEqual(BindAddress.nl_pid, getpid(), "%d");
            LxtCheckEqual(AddressLength, sizeof(BindAddress), "%d");

            //
            // Test case: Kernel should assign a negative unique ID to any subsequent
            //            (after the first one) netlink socket that the process
            //            subsequently creates.
            //

            for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)
            {
                LxtCheckErrno(SocketA[ArrayIterator] = socket(AF_NETLINK, Type, Family));

                memset(&BindAddress, 0, sizeof(BindAddress));
                BindAddress.nl_family = AF_NETLINK;
                AddressLength = sizeof(BindAddress);
                LxtCheckErrno(bind(SocketA[ArrayIterator], (struct sockaddr*)&BindAddress, AddressLength));

                memset(&BindAddress, 0, sizeof(BindAddress));
                LxtCheckErrno(getsockname(SocketA[ArrayIterator], (struct sockaddr*)&BindAddress, &AddressLength));

                SocketPid[ArrayIterator] = BindAddress.nl_pid;
                LxtCheckTrue(SocketPid[ArrayIterator] < 0);
            }

            //
            // Validate that every socket PID is unique.
            //

            for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)
            {
                LxtLogInfo("parent, socket: %d, pid: %d", ArrayIterator + 1, SocketPid[ArrayIterator]);

                for (ArrayIterator2 = ArrayIterator + 1; ArrayIterator2 < ArraySize; ArrayIterator2++)
                {

                    LxtCheckNotEqual(SocketPid[ArrayIterator], SocketPid[ArrayIterator2], "%d");
                }
            }

            //
            // Test case: validate that the child process gets its own set of ID's.
            //

            ChildPid = fork();
            if (ChildPid == 0)
            {
                LxtLogInfo("child, pid: %d", getpid());
                LxtClose(Socket);
                LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));
                memset(&BindAddress, 0, sizeof(BindAddress));
                BindAddress.nl_family = AF_NETLINK;
                AddressLength = sizeof(BindAddress);
                LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

                memset(&BindAddress, 0, sizeof(BindAddress));
                LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));

                LxtCheckEqual(BindAddress.nl_pid, getpid(), "%d");
                for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)
                {
                    LxtCheckErrno(SocketChildA[ArrayIterator] = socket(AF_NETLINK, Type, Family));

                    memset(&BindAddress, 0, sizeof(BindAddress));
                    BindAddress.nl_family = AF_NETLINK;
                    AddressLength = sizeof(BindAddress);
                    LxtCheckErrno(bind(SocketChildA[ArrayIterator], (struct sockaddr*)&BindAddress, AddressLength));

                    memset(&BindAddress, 0, sizeof(BindAddress));
                    LxtCheckErrno(getsockname(SocketChildA[ArrayIterator], (struct sockaddr*)&BindAddress, &AddressLength));

                    SocketChildPid[ArrayIterator] = BindAddress.nl_pid;
                    LxtCheckTrue(SocketChildPid[ArrayIterator] < 0);
                }

                //
                // Validate that every socket PID is unique, locally as well
                // as globally.
                //

                for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)
                {
                    LxtLogInfo("child, socket: %d, pid: %d", ArrayIterator + 1, SocketChildPid[ArrayIterator]);

                    for (ArrayIterator2 = ArrayIterator + 1; ArrayIterator2 < ArraySize; ArrayIterator2++)
                    {

                        LxtCheckNotEqual(SocketChildPid[ArrayIterator], SocketChildPid[ArrayIterator2], "%d");

                        LxtCheckNotEqual(SocketPid[ArrayIterator], SocketChildPid[ArrayIterator2], "%d");
                    }
                }

                for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)
                {
                    LxtClose(SocketChildA[ArrayIterator]);
                }

                LxtClose(Socket);
                Result = LXT_RESULT_SUCCESS;
                goto ErrorExit;
            }

            LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
            for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)
            {
                LxtClose(SocketA[ArrayIterator]);
            }

            //
            // Test case: validate that if the app provides a PID for the socket,
            //            then it should be unique.
            //

            LxtCheckErrno(SocketA[0] = socket(AF_NETLINK, Type, Family));
            memset(&BindAddress, 0, sizeof(BindAddress));
            BindAddress.nl_family = AF_NETLINK;

            //
            // Since the very first socket of this process is still open,
            // getpid() as the socket PID is already in use.
            //

            BindAddress.nl_pid = getpid();
            AddressLength = sizeof(BindAddress);
            LxtCheckErrnoFailure(bind(SocketA[0], (struct sockaddr*)&BindAddress, AddressLength), EADDRINUSE);

            LxtClose(SocketA[0]);
            LxtClose(Socket);

            //
            // Test case: validate that when there are no netlink sockets opened
            //            by the process, the app should be able to provide the
            //            PID of the process as the unique ID for the socket.
            //

            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));
            memset(&BindAddress, 0, sizeof(BindAddress));
            BindAddress.nl_family = AF_NETLINK;
            BindAddress.nl_pid = getpid();
            AddressLength = sizeof(BindAddress);
            LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

            LxtClose(Socket);
        }
    }

    Result = LXT_RESULT_SUCCESS;

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    if (Socket2 > 0)
    {
        close(Socket2);
    }

    if (Socket3 > 0)
    {
        close(Socket3);
    }

    if (Socket4 > 0)
    {
        close(Socket4);
    }

    for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)
    {
        if (SocketA[ArrayIterator] != 0)
        {
            close(SocketA[ArrayIterator]);
        }

        if (SocketChildA[ArrayIterator] != 0)
        {
            close(SocketChildA[ArrayIterator]);
        }
    }

    //
    // If child, exit.
    //

    if (ChildPid == 0)
    {
        _exit(Result);
    }

    return Result;
}

int SocketNetlinkBindThread(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests the bind() API with new threads
    created by pthread_create.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    int ArrayIterator;
    struct sockaddr_nl BindAddress;
    pthread_t ChildThread;
    pid_t ChildThreadNetlinkPid;
    BindChildThreadReturn* ChildThreadReturn;
    int Result;
    int Socket;

    Result = LXT_RESULT_FAILURE;
    Socket = -1;
    for (ArrayIterator = 0; ArrayIterator < 5; ArrayIterator++)
    {

        //
        // Open a netlink socket. This is the first netlink socket of the
        // threadgroup, so it should have a nl_pid equal to getpid() (which
        // is the tgid).
        //

        LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
        memset(&BindAddress, 0, sizeof(BindAddress));
        BindAddress.nl_family = AF_NETLINK;
        AddressLength = sizeof(BindAddress);
        LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

        memset(&BindAddress, 0, sizeof(BindAddress));
        LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));

        LxtLogInfo("nl_pid 1: %d pid: %d", BindAddress.nl_pid, getpid());
        LxtCheckTrue(BindAddress.nl_pid > 0);
        LxtCheckEqual(BindAddress.nl_pid, getpid(), "%d");

        //
        // Create a new thread, which opens and closes a netlink socket in the
        // new thread. This is not the first netlink socket of the threadgroup,
        // so it should have a negative nl_pid.
        //

        LxtCheckErrnoZeroSuccess(pthread_create(&ChildThread, NULL, SocketNetlinkBindThreadChild, NULL));

        LxtCheckErrnoZeroSuccess(pthread_join(ChildThread, (void*)&ChildThreadReturn));
        LxtCheckTrue(ChildThreadReturn != NULL);
        ChildThreadNetlinkPid = ChildThreadReturn->nl_pid;
        free(ChildThreadReturn);
        LxtLogInfo("nl_pid 2: %d pid: %d", ChildThreadNetlinkPid, getpid());
        LxtCheckTrue(ChildThreadNetlinkPid < 0);

        //
        // Close the first netlink socket (leaving no open netlink sockets).
        // Create a new thread, which opens and closes a netlink socket in the
        // new thread. This is the first netlink socket of the threadgroup.
        //

        LxtClose(Socket);
        LxtCheckErrnoZeroSuccess(pthread_create(&ChildThread, NULL, SocketNetlinkBindThreadChild, NULL));

        LxtCheckErrnoZeroSuccess(pthread_join(ChildThread, (void*)&ChildThreadReturn));
        LxtCheckTrue(ChildThreadReturn != NULL);
        ChildThreadNetlinkPid = ChildThreadReturn->nl_pid;
        free(ChildThreadReturn);
        LxtLogInfo("nl_pid 3: %d pid: %d", ChildThreadNetlinkPid, getpid());
        LxtCheckTrue(ChildThreadNetlinkPid > 0);
        LxtCheckEqual(ChildThreadNetlinkPid, getpid(), "%d");
    }

    Result = LXT_RESULT_SUCCESS;

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

void* SocketNetlinkBindThreadChild(void* Arg)

/*++

Routine Description:

    This routine runs in a child thread and creates a netlink socket.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    On success, returns a struct containing the nl_pid of the netlink socket
    created by this thread. On failure, returns NULL.

--*/

{

    socklen_t AddressLength;
    struct sockaddr_nl BindAddress;
    int Result;
    int Socket;
    BindChildThreadReturn* ThreadReturn;

    Socket = -1;
    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    memset(&BindAddress, 0, sizeof(BindAddress));
    LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));

    LxtLogInfo("Child thread: nl_pid: %d, getpid(): %d", BindAddress.nl_pid, getpid());

    LxtClose(Socket);
    ThreadReturn = malloc(sizeof(*ThreadReturn));
    LxtCheckTrue(ThreadReturn != NULL);
    ThreadReturn->nl_pid = BindAddress.nl_pid;

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    if (Result < 0)
    {
        return NULL;
    }
    else
    {
        return ThreadReturn;
    }
}

int SocketNetlinkSendReceive(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests the send and receive family of APIs.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    struct sockaddr_nl BindAddress;
    struct
    {
        struct nlmsghdr nlh1;
        char dummy[4];
        struct nlmsghdr nlh2;
    } DoubleRequest;
    struct nlmsgerr* Error;
    int ExpectedReceiveLength;
    struct iovec IoVec[10];
    struct msghdr MessageHeader;
    struct sockaddr_nl ReceiveAddress;
    char ReceiveBuffer[500];
    struct nlmsghdr* ReceiveHeader;
    struct nlmsghdr* ReceiveHeader2;
    int ReceiveResult;
    struct nlmsghdr Request;
    int Result;
    struct sockaddr_nl SendAddress;
    uint32_t Sequence;
    int Socket;

    Result = LXT_RESULT_FAILURE;
    Socket = -1;
    Sequence = 0x7435;

    //
    // Create and bind socket. Create a Netlink request with the ACK flag.
    // Netlink should echo the same request back to us.
    //
    // TODO_LX: Test whether invalid messages should be ACK'd back.
    //

    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    memset(&Request, 0, sizeof(Request));
    Request.nlmsg_len = NLMSG_LENGTH(0);
    Request.nlmsg_type = NLMSG_NOOP;
    Request.nlmsg_flags = NLM_F_ACK;
    Request.nlmsg_seq = Sequence;

    //
    // Test sendto() with invalid send addresses.
    //

    AddressLength = sizeof(SendAddress);
    memset(&SendAddress, 0, sizeof(SendAddress));
    SendAddress.nl_family = AF_NETLINK;
    SendAddress.nl_pid = 0xFFFF1234;
    LxtCheckErrnoFailure(sendto(Socket, &Request, sizeof(Request), 0, (struct sockaddr*)&SendAddress, AddressLength), ECONNREFUSED);

    memset(&SendAddress, 0, sizeof(SendAddress));
    SendAddress.nl_family = 0xFFFF;
    LxtCheckErrnoFailure(sendto(Socket, &Request, sizeof(Request), 0, (struct sockaddr*)&SendAddress, AddressLength), EINVAL);

    //
    // Test sendto() with 0 nl_pid in send address. This should go to the kernel.
    // This is message 0.
    //

    memset(&SendAddress, 0, sizeof(SendAddress));
    Request.nlmsg_seq = Sequence;
    SendAddress.nl_family = AF_NETLINK;
    SendAddress.nl_pid = 0;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, (struct sockaddr*)&SendAddress, AddressLength));

    //
    // Test sendto() with 0 buffer length. The return value should be 0
    // and no response should be generated.
    //

    LxtCheckErrnoZeroSuccess(sendto(Socket, &Request, 0, 0, (struct sockaddr*)&SendAddress, AddressLength));

    //
    // Test sendto() with NULL send address. This should go to the kernel
    // by default. This is message 1.
    //

    Request.nlmsg_seq = Sequence + 1;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));

    //
    // Test send(). This should go to the kernel by default. This is message 2.
    //

    Request.nlmsg_seq = Sequence + 2;
    LxtCheckErrno(send(Socket, &Request, sizeof(Request), 0));

    //
    // Test write(). This should go to the kernel by default. This is message 3.
    //

    Request.nlmsg_seq = Sequence + 3;
    LxtCheckErrno(write(Socket, &Request, sizeof(Request)));

    //
    // Test sendmsg() with NULL send address. This is message 4.
    // A NULL send address means the name size should be ignored, so also add
    // an invalid name size to verify this.
    //

    memset(&IoVec, 0, sizeof(IoVec));
    memset(&MessageHeader, 0, sizeof(MessageHeader));
    IoVec[0].iov_base = &Request;
    IoVec[0].iov_len = sizeof(Request);
    MessageHeader.msg_iov = IoVec;
    MessageHeader.msg_iovlen = 1;
    MessageHeader.msg_namelen = -1;
    Request.nlmsg_seq = Sequence + 4;
    LxtCheckErrno(sendmsg(Socket, &MessageHeader, 0));

    //
    // Test sendmsg() with buffer split across vectors. This is message 5.
    //

    memset(&IoVec, 0, sizeof(IoVec));
    memset(&MessageHeader, 0, sizeof(MessageHeader));
    memset(&DoubleRequest, 0, sizeof(DoubleRequest));
    IoVec[0].iov_base = &DoubleRequest.nlh1;
    IoVec[0].iov_len = __builtin_offsetof(struct nlmsghdr, nlmsg_flags);
    IoVec[1].iov_base = &DoubleRequest.nlh2.nlmsg_flags;
    IoVec[1].iov_len = sizeof(struct nlmsghdr) - __builtin_offsetof(struct nlmsghdr, nlmsg_flags);

    DoubleRequest.nlh1.nlmsg_len = NLMSG_LENGTH(0);
    DoubleRequest.nlh1.nlmsg_type = NLMSG_NOOP;
    DoubleRequest.nlh2.nlmsg_flags = NLM_F_ACK;
    DoubleRequest.nlh2.nlmsg_seq = Sequence + 5;
    MessageHeader.msg_iov = IoVec;
    MessageHeader.msg_iovlen = 2;
    LxtCheckErrno(sendmsg(Socket, &MessageHeader, 0));

    //
    // Test sendmsg() with MSG_DONTWAIT flag.
    //

    memset(&IoVec, 0, sizeof(IoVec));
    memset(&MessageHeader, 0, sizeof(MessageHeader));
    IoVec[0].iov_base = &Request;
    IoVec[0].iov_len = sizeof(Request);
    MessageHeader.msg_iov = IoVec;
    MessageHeader.msg_iovlen = 1;
    Request.nlmsg_seq = Sequence + 6;
    LxtCheckErrno(sendmsg(Socket, &MessageHeader, MSG_DONTWAIT));

    //
    // Test sendmsg() with invalid send address.
    //

    AddressLength = sizeof(SendAddress);
    memset(&SendAddress, 0, sizeof(SendAddress));
    SendAddress.nl_family = AF_NETLINK;
    SendAddress.nl_pid = 0xFFFF1234;
    MessageHeader.msg_name = &SendAddress;
    MessageHeader.msg_namelen = AddressLength;
    LxtCheckErrnoFailure(sendmsg(Socket, &MessageHeader, 0), ECONNREFUSED);

    //
    // Test sendmsg() with negative send address length.
    //

    AddressLength = sizeof(SendAddress);
    memset(&SendAddress, 0, sizeof(SendAddress));
    SendAddress.nl_family = AF_NETLINK;
    SendAddress.nl_pid = 0xFFFF1234;
    MessageHeader.msg_name = &SendAddress;
    MessageHeader.msg_namelen = -1;
    LxtCheckErrnoFailure(sendmsg(Socket, &MessageHeader, 0), EINVAL);

    //
    // Test sendmsg() with 0 nl_pid in send address.
    //

    AddressLength = sizeof(SendAddress);
    memset(&SendAddress, 0, sizeof(SendAddress));
    SendAddress.nl_family = AF_NETLINK;
    SendAddress.nl_pid = 0;
    MessageHeader.msg_name = &SendAddress;
    MessageHeader.msg_namelen = AddressLength;
    Request.nlmsg_seq = Sequence + 7;
    LxtCheckErrno(sendmsg(Socket, &MessageHeader, 0));

    //
    // Test read(). Verify the received contents. The response should be message 0.
    //

    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
    LxtCheckErrno(ReceiveResult = read(Socket, ReceiveBuffer, sizeof(ReceiveBuffer)));
    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));
    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, "%d");
    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);
    LxtCheckEqual(Error->error, 0, "%d");

    //
    // Test recv(). Verify the received contents. The response should be message 1.
    //

    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));
    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, "%d");
    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence + 1, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);
    LxtCheckEqual(Error->error, 0, "%d");

    //
    // Test recvfrom() with NULL receive address. Use MSG_PEEK flag, which
    // should not remove the data from the socket. Use MSG_TRUNC flag, which
    // should have no effect since the passed in buffer is larger than the
    // response. Use MSG_WAITALL flag, which has no effect on datagram sockets.
    // Verify the received contents. The response should be message 2.
    //

    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
    LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_PEEK | MSG_TRUNC | MSG_WAITALL, NULL, 0));

    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));
    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, "%d");
    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence + 2, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);
    LxtCheckEqual(Error->error, 0, "%d");

    //
    // Test recvfrom(). Use MSG_TRUNC flag. Even though the passed in
    // receive buffer is too small, the return value should be the full length
    // of the response. The response should still be message 2, since the previous
    // call was a MSK_PEEK.
    //

    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
    LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, 2 * sizeof(uint32_t), MSG_TRUNC, NULL, 0));

    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));
    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, "%d");
    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_seq, 0, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_pid, 0, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);
    LxtCheckEqual(Error->error, 0, "%d");

    //
    // Test recvfrom() with 0 buffer length. This should still advance the
    // socket's internal receive buffer (making the next response message 4).
    // This response should be message 3.
    //

    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
    LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, 0, 0, NULL, 0));

    ExpectedReceiveLength = 0;
    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, "%d");
    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;

    //
    // Test recvfrom() with valid receive address. Verify the received contents.
    // The response should be message 4.
    //

    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
    memset(&ReceiveAddress, 0, sizeof(ReceiveAddress));
    AddressLength = sizeof(struct sockaddr_nl);
    LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, (struct sockaddr*)&ReceiveAddress, &AddressLength));

    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));
    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, "%d");
    LxtCheckEqual(AddressLength, sizeof(struct sockaddr_nl), "%d");
    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence + 4, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), "%d");
    LxtCheckEqual(ReceiveAddress.nl_family, AF_NETLINK, "%d");
    LxtCheckEqual(ReceiveAddress.nl_pid, 0, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);
    LxtCheckEqual(Error->error, 0, "%d");
    Request.nlmsg_seq = Sequence + 4;
    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, "%d");

    //
    // Test recvmsg() with valid receive address. Verify the received contents.
    //

    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
    memset(&ReceiveAddress, 0, sizeof(ReceiveAddress));
    memset(&IoVec, 0, sizeof(IoVec));
    memset(&MessageHeader, 0, sizeof(MessageHeader));
    IoVec[0].iov_base = &ReceiveBuffer;
    IoVec[0].iov_len = sizeof(ReceiveBuffer);
    MessageHeader.msg_iov = IoVec;
    MessageHeader.msg_iovlen = 1;
    MessageHeader.msg_name = &ReceiveAddress;
    MessageHeader.msg_namelen = AddressLength;

    //
    // Set some random value as the control length and check whether it gets
    // properly (re)set by the kernel.
    //

    MessageHeader.msg_controllen = 100;
    LxtCheckErrno(ReceiveResult = recvmsg(Socket, &MessageHeader, 0));
    LxtCheckEqual(MessageHeader.msg_controllen, 0, "%d");
    LxtCheckEqual(MessageHeader.msg_flags, 0, "%d");
    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));
    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, "%d");
    LxtCheckEqual(MessageHeader.msg_namelen, AddressLength, "%d");
    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence + 5, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), "%d");
    LxtCheckEqual(ReceiveAddress.nl_family, AF_NETLINK, "%d");
    LxtCheckEqual(ReceiveAddress.nl_pid, 0, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);
    LxtCheckEqual(Error->error, 0, "%d");
    Request.nlmsg_seq = Sequence + 5;
    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, "%d");

    //
    // Test recvmsg() with passing in a small receive buffer.
    // The response should be truncated.
    //

    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
    memset(&IoVec, 0, sizeof(IoVec));
    memset(&MessageHeader, 0, sizeof(MessageHeader));
    IoVec[0].iov_base = &ReceiveBuffer;
    IoVec[0].iov_len = 2 * sizeof(uint32_t);
    MessageHeader.msg_iov = IoVec;
    MessageHeader.msg_iovlen = 1;
    LxtCheckErrno(ReceiveResult = recvmsg(Socket, &MessageHeader, 0));
    ExpectedReceiveLength = IoVec[0].iov_len;
    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_seq, 0, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_pid, 0, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);
    LxtCheckEqual(Error->error, 0, "%d");
    memset(&Request, 0, sizeof(Request));
    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, "%d");

    //
    // Test recvmsg() split across vectors.
    //

    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
    memset(&IoVec, 0, sizeof(IoVec));
    memset(&MessageHeader, 0, sizeof(MessageHeader));
    ReceiveHeader2 =
        (struct nlmsghdr*)&ReceiveBuffer[NLMSG_LENGTH(sizeof(struct nlmsgerr)) + __builtin_offsetof(struct nlmsghdr, nlmsg_flags)];

    IoVec[0].iov_len = __builtin_offsetof(struct nlmsghdr, nlmsg_flags);
    IoVec[0].iov_base = ReceiveHeader2;
    IoVec[1].iov_base = ReceiveBuffer + __builtin_offsetof(struct nlmsghdr, nlmsg_flags);
    IoVec[1].iov_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
    MessageHeader.msg_iov = IoVec;
    MessageHeader.msg_iovlen = 2;
    LxtCheckErrno(ReceiveResult = recvmsg(Socket, &MessageHeader, 0));
    LxtCheckEqual(MessageHeader.msg_controllen, 0, "%d");
    LxtCheckEqual(MessageHeader.msg_flags, 0, "%d");
    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));
    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, "%d");
    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckEqual(ReceiveHeader->nlmsg_len, 0, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_type, 0, "%d");
    ReceiveHeader->nlmsg_len = ReceiveHeader2->nlmsg_len;
    ReceiveHeader->nlmsg_type = ReceiveHeader2->nlmsg_type;
    LxtCheckEqual(ReceiveHeader->nlmsg_len, ExpectedReceiveLength, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence + 7, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), "%d");
    LxtCheckEqual(ReceiveAddress.nl_family, AF_NETLINK, "%d");
    LxtCheckEqual(ReceiveAddress.nl_pid, 0, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);
    LxtCheckEqual(Error->error, 0, "%d");
    Request.nlmsg_len = NLMSG_LENGTH(0);
    Request.nlmsg_type = NLMSG_NOOP;
    Request.nlmsg_flags = NLM_F_ACK;
    Request.nlmsg_seq = Sequence + 7;
    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, "%d");

    //
    // Test recvfrom() when using the MSG_DONTWAIT flag. The socket has no data,
    // so it should return EAGAIN immediately instead of blocking forever.
    //

    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
    LxtCheckErrnoFailure(recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT, NULL, 0), EAGAIN);

    //
    // Test specifying that the input buffer is much larger than it actually is.
    //

    memset(&DoubleRequest, 0, sizeof(DoubleRequest));
    memset(&IoVec, 0, sizeof(IoVec));
    memset(&MessageHeader, 0, sizeof(MessageHeader));
    DoubleRequest.nlh1.nlmsg_len = 0x30000;
    DoubleRequest.nlh1.nlmsg_type = NLMSG_NOOP;
    DoubleRequest.nlh1.nlmsg_flags = NLM_F_ACK;
    DoubleRequest.nlh1.nlmsg_seq = Sequence;
    IoVec[0].iov_base = &DoubleRequest;
    IoVec[0].iov_len = 0x30000;
    MessageHeader.msg_iov = IoVec;
    MessageHeader.msg_iovlen = 1;
    Request.nlmsg_seq = Sequence + 4;
    LxtCheckErrnoFailure(sendmsg(Socket, &MessageHeader, 0), EFAULT);

    //
    // Test passing in garbage length values in the message headers.
    //

    DoubleRequest.nlh1.nlmsg_len = NLMSG_LENGTH(0) + 1;
    DoubleRequest.nlh2.nlmsg_len = 0x3000;
    IoVec[0].iov_len = sizeof(DoubleRequest.nlh1) + 1;
    LxtCheckErrno(sendmsg(Socket, &MessageHeader, 0));

    //
    // Again.
    //

    DoubleRequest.nlh1.nlmsg_len = 0x3000;
    DoubleRequest.nlh2.nlmsg_len = 0x3000;
    IoVec[0].iov_len = sizeof(DoubleRequest.nlh1) + 1;
    LxtCheckErrno(sendmsg(Socket, &MessageHeader, 0));

    //
    // Again.
    //

    DoubleRequest.nlh1.nlmsg_len = 0x3000;
    DoubleRequest.nlh2.nlmsg_len = NLMSG_LENGTH(0) + 1;
    IoVec[0].iov_len = sizeof(DoubleRequest.nlh1) + 1;
    LxtCheckErrno(sendmsg(Socket, &MessageHeader, 0));

    //
    // TODO: Test multi-message send with different requests
    //       bundled in the same send.
    //

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

int SocketNetlinkSendReceiveOverflow(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests the send and receive family of APIs
    in the context of the receive buffer overflowing.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    struct sockaddr_nl BindAddress;
    struct nlmsgerr* Error;
    int ExpectedReceiveLength;
    int Index;
    struct sockaddr_nl ReceiveAddress;
    char ReceiveBuffer[500];
    struct nlmsghdr* ReceiveHeader;
    int ReceiveResult;
    struct nlmsghdr Request;
    int Result;
    struct sockaddr_nl SendAddress;
    int SendResult;
    uint32_t Sequence;
    int Socket;
    int SocketError;
    socklen_t SocketErrorSize;
    struct timeval Timeout;

    Result = LXT_RESULT_FAILURE;
    Socket = -1;
    Sequence = 0x3468;

    //
    // Create and bind socket. Create a NOOP request with the ACK flag.
    // Netlink should echo the same request back to us.
    //

    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    memset(&Request, 0, sizeof(Request));
    Request.nlmsg_len = NLMSG_LENGTH(0);
    Request.nlmsg_type = NLMSG_NOOP;
    Request.nlmsg_flags = NLM_F_ACK;

    //
    // Overflow the receive buffer by sending 3000 events.
    // Each send should succeed with the return value being the full number of
    // bytes the user sent.
    //

    AddressLength = sizeof(SendAddress);
    memset(&SendAddress, 0, sizeof(SendAddress));
    SendAddress.nl_family = AF_NETLINK;
    SendAddress.nl_pid = 0;
    for (Index = 0; Index < 3000; Index++)
    {
        Request.nlmsg_seq = Sequence + Index;
        LxtCheckErrno(SendResult = sendto(Socket, &Request, sizeof(Request), 0, (struct sockaddr*)&SendAddress, AddressLength));

        LxtCheckEqual(SendResult, sizeof(Request), "%d");
    }

    //
    // The first call to recvfrom() after the overflow should return ENOBUFS.
    // Also verify that the receive and address buffers were not changed.
    //

    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
    memset(&ReceiveAddress, 0, sizeof(ReceiveAddress));
    ReceiveBuffer[1] = 0x56;
    ReceiveAddress.nl_groups = 0x3456789a;
    AddressLength = sizeof(struct sockaddr_nl);
    LxtCheckErrnoFailure(recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, (struct sockaddr*)&ReceiveAddress, &AddressLength), ENOBUFS);

    LxtCheckEqual(ReceiveBuffer[1], 0x56, "%d");
    LxtCheckEqual(ReceiveAddress.nl_groups, 0x3456789a, "%d");

    //
    // The subsequent calls to recvfrom() pull out the responses before the
    // overflow happened. Only call recvfrom() 3 times here, so that the receive
    // buffer is not yet fully drained.
    //

    for (Index = 0; Index < 3; Index++)
    {
        memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
        memset(&ReceiveAddress, 0, sizeof(ReceiveAddress));
        LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, (struct sockaddr*)&ReceiveAddress, &AddressLength));

        ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));
        LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, "%d");
        LxtCheckEqual(AddressLength, sizeof(struct sockaddr_nl), "%d");
        ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;
        LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, "%d");
        LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, "%d");
        LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence + Index, "%d");
        LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), "%d");
        LxtCheckEqual(ReceiveAddress.nl_family, AF_NETLINK, "%d");
        LxtCheckEqual(ReceiveAddress.nl_pid, 0, "%d");
    }

    //
    // Check the socket option SO_ERROR. This should be 0 (no error),
    // since the error was cleared when the first recvfrom() returned ENOBUFS.
    //

    SocketErrorSize = sizeof(SocketError);
    SocketError = 212;
    LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_ERROR, &SocketError, &SocketErrorSize));

    LxtCheckEqual(SocketErrorSize, sizeof(SocketError), "%d");
    LxtCheckEqual(SocketError, 0, "%d");

    //
    // Test that the receive buffer is still considered to be "overflown"
    // until the entire buffer is drained. This means that anything sent to
    // the socket now will be ignored and no response generated, even though
    // technically there is space in the receive buffer for the response.
    // The test sends a message with a unique sequence number. Later on, the
    // entire receive buffer will be drained and all responses checked to
    // verify that this unique sequence number is not in any of the responses.
    //

    Request.nlmsg_seq = 0x98765432;
    LxtCheckErrno(SendResult = sendto(Socket, &Request, sizeof(Request), 0, (struct sockaddr*)&SendAddress, AddressLength));

    LxtCheckEqual(SendResult, sizeof(Request), "%d");

    //
    // Drain the entire response buffer. Set the recvfrom() timeout to 1 millisecond
    // so that it does not block infinitely.
    //

    Timeout.tv_sec = 0;
    Timeout.tv_usec = 1000;
    LxtCheckErrno(setsockopt(Socket, SOL_SOCKET, SO_RCVTIMEO, &Timeout, sizeof(Timeout)));

    for (;;)
    {
        memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
        memset(&ReceiveAddress, 0, sizeof(ReceiveAddress));
        ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, (struct sockaddr*)&ReceiveAddress, &AddressLength);

        if (ReceiveResult == -1)
        {
            LxtCheckErrnoFailure(ReceiveResult, EAGAIN);
            break;
        }
        else
        {
            ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));
            LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, "%d");
            LxtCheckEqual(AddressLength, sizeof(struct sockaddr_nl), "%d");
            ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;
            LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, "%d");
            LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, "%d");
            LxtCheckNotEqual(ReceiveHeader->nlmsg_seq, 0, "%d");
            LxtCheckNotEqual(ReceiveHeader->nlmsg_seq, 0x98765432, "%d");
            LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), "%d");
            LxtCheckEqual(ReceiveAddress.nl_family, AF_NETLINK, "%d");
            LxtCheckEqual(ReceiveAddress.nl_pid, 0, "%d");
        }
    }

    //
    // Overflow the receive buffer again by sending 3000 events.
    // Each send should succeed with the return value being the full number of
    // bytes the user sent.
    //

    AddressLength = sizeof(SendAddress);
    memset(&SendAddress, 0, sizeof(SendAddress));
    SendAddress.nl_family = AF_NETLINK;
    SendAddress.nl_pid = 0;
    for (Index = 0; Index < 3000; Index++)
    {
        Request.nlmsg_seq = Sequence + Index;
        LxtCheckErrno(SendResult = sendto(Socket, &Request, sizeof(Request), 0, (struct sockaddr*)&SendAddress, AddressLength));

        LxtCheckEqual(SendResult, sizeof(Request), "%d");
    }

    //
    // Check the socket option SO_ERROR. This should be ENOBUFS,
    // since recvfrom() has not been called yet.
    //

    SocketErrorSize = sizeof(SocketError);
    SocketError = 212;
    LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_ERROR, &SocketError, &SocketErrorSize));

    LxtCheckEqual(SocketErrorSize, sizeof(SocketError), "%d");
    LxtCheckEqual(SocketError, ENOBUFS, "%d");

    //
    // The first recvfrom() should be successful, since the ENOBUFS error
    // was already cleared when socket option SO_ERROR was retrieved.
    //

    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
    memset(&ReceiveAddress, 0, sizeof(ReceiveAddress));
    LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, (struct sockaddr*)&ReceiveAddress, &AddressLength));

    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));
    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, "%d");
    LxtCheckEqual(AddressLength, sizeof(struct sockaddr_nl), "%d");
    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence, "%d");
    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), "%d");
    LxtCheckEqual(ReceiveAddress.nl_family, AF_NETLINK, "%d");
    LxtCheckEqual(ReceiveAddress.nl_pid, 0, "%d");

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

int SocketNetlinkBlockedReader(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests that blocked readers do not get unblocked when the socket
    is closed. Also verify that shutdown() is invalid on NETLINK sockets.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    struct sockaddr_nl BindAddress;
    void* PthreadResult;
    int Result;
    int Socket;
    pthread_t Thread;
    struct timespec Timeout = {0};

    Result = LXT_RESULT_FAILURE;
    Socket = -1;

    //
    // Create and bind socket.
    //

    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    //
    // Create a reader thread that will block on 'recv'.
    //

    LxtCheckResultError(pthread_create(&Thread, NULL, SocketBlockedReaderThread, &Socket));

    //
    // Wait for sometime to allow the reader thread to block on read. There
    // is no other elegant way of knowing whether the thread has blocked.
    //

    usleep(5000);

    //
    // Shutdown is NOT supported for NETLINK sockets.
    //

    LxtCheckErrnoFailure(shutdown(Socket, SHUT_RD), EOPNOTSUPP);
    LxtCheckErrnoFailure(shutdown(Socket, SHUT_WR), EOPNOTSUPP);
    LxtCheckErrnoFailure(shutdown(Socket, SHUT_RDWR), EOPNOTSUPP);

    //
    // Closing the socket does not unblock the reader. The pthread_timedjoin_np()
    // should timeout since the reader thread is still blocked.
    //

    LxtCheckErrnoZeroSuccess(close(Socket));
    usleep(5000);
    Timeout.tv_nsec = 5000;
    Result = pthread_timedjoin_np(Thread, &PthreadResult, &Timeout);
    if (Result != ETIMEDOUT)
    {
        LxtLogError(
            "Expecting pthread_tryjoin_np to return ETIMEDOUT(%d), "
            "but it returned with result: %d",
            ETIMEDOUT,
            Result);

        goto ErrorExit;
    }

    //
    // No other choice but to kill the reader thread.
    //

    LxtCheckErrnoZeroSuccess(pthread_cancel(Thread));
    LxtCheckErrnoZeroSuccess(pthread_join(Thread, &PthreadResult));
    LxtCheckEqual(PthreadResult, PTHREAD_CANCELED, "%p");

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

int SocketNetlinkEpoll(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests that the epoll state is correct.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    struct sockaddr_nl BindAddress;
    int EdRead;
    int EdWrite;
    struct epoll_event EpollControlEvent;
    struct epoll_event EpollWaitEvent[2];
    int Index;
    char ReceiveBuffer[500];
    int ReceiveResult;
    struct nlmsghdr Request;
    int Result;
    int SendResult;
    int Socket;

    Result = LXT_RESULT_FAILURE;
    Socket = -1;

    //
    // Create socket.
    //

    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, 0));

    //
    // Create epoll containers for read and write and
    // add the socket descriptor to them.
    //

    LxtCheckErrno(EdRead = epoll_create(1));
    EpollControlEvent.events = EPOLLIN;
    EpollControlEvent.data.fd = Socket;
    Result = epoll_ctl(EdRead, EPOLL_CTL_ADD, Socket, &EpollControlEvent);
    LxtCheckErrnoZeroSuccess(Result);

    LxtCheckErrno(EdWrite = epoll_create(1));
    EpollControlEvent.events = EPOLLOUT;
    EpollControlEvent.data.fd = Socket;
    Result = epoll_ctl(EdWrite, EPOLL_CTL_ADD, Socket, &EpollControlEvent);
    LxtCheckErrnoZeroSuccess(Result);

    //
    // Wait for data to be available with a timeout. This should timeout since
    // there is no data. Verify that write is available.
    //

    Result = epoll_wait(EdRead, EpollWaitEvent, 2, 50);
    LxtCheckEqual(Result, 0, "%d");
    Result = epoll_wait(EdWrite, EpollWaitEvent, 2, 50);
    LxtCheckEqual(Result, 1, "%d");

    //
    // Bind the socket.
    //

    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    //
    // Wait for data to be available with a timeout. This should timeout since
    // there is no data. Verify that write is available.
    //

    Result = epoll_wait(EdRead, EpollWaitEvent, 2, 50);
    LxtCheckEqual(Result, 0, "%d");
    Result = epoll_wait(EdWrite, EpollWaitEvent, 2, 50);
    LxtCheckEqual(Result, 1, "%d");

    //
    // Send 3000 messages. The receive buffer will overflow, but write is still
    // available. Read is available now.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlmsg_len = NLMSG_LENGTH(0);
    Request.nlmsg_type = NLMSG_NOOP;
    Request.nlmsg_flags = NLM_F_ACK;
    for (Index = 0; Index < 3000; Index++)
    {
        LxtCheckErrno(SendResult = sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));

        LxtCheckEqual(SendResult, sizeof(Request), "%d");
    }

    Result = epoll_wait(EdRead, EpollWaitEvent, 2, 50);
    LxtCheckEqual(Result, 1, "%d");
    Result = epoll_wait(EdWrite, EpollWaitEvent, 2, 50);
    LxtCheckEqual(Result, 1, "%d");

    //
    // Drain all the events. Write is still available, but read is not.
    //

    LxtCheckErrnoFailure(recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0), ENOBUFS);

    for (;;)
    {
        memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
        ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0);

        if (ReceiveResult == -1)
        {
            LxtCheckErrnoFailure(ReceiveResult, EAGAIN);
            break;
        }
    }

    Result = epoll_wait(EdRead, EpollWaitEvent, 2, 50);
    LxtCheckEqual(Result, 0, "%d");
    Result = epoll_wait(EdWrite, EpollWaitEvent, 2, 50);
    LxtCheckEqual(Result, 1, "%d");

    //
    // Close the socket. Both read and write are not available.
    //

    LxtCheckErrnoZeroSuccess(close(Socket));
    Result = epoll_wait(EdRead, EpollWaitEvent, 2, 50);
    LxtCheckEqual(Result, 0, "%d");
    Result = epoll_wait(EdWrite, EpollWaitEvent, 2, 50);
    LxtCheckEqual(Result, 0, "%d");

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    if (EdRead > 0)
    {
        close(EdRead);
    }

    if (EdWrite > 0)
    {
        close(EdWrite);
    }

    return Result;
}

int SocketNetlinkRecvmmsg(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests the recvmmsg() syscall.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    struct sockaddr_nl BindAddress;
    NetlinkRecvmmsgBlockedReaderParams BlockedReaderParams;
    struct nlmsgerr* Error;
    struct nlmsghdr* Header;
    int Index;
    socklen_t OptionLength;
    void* PthreadResult;
    char ReceiveBuffers[10][1000];
    struct iovec ReceiveIovecs[10];
    struct mmsghdr ReceiveMessages[10];
    int ReceiveResult;
    int Result;
    int SendResult;
    int Socket;
    pthread_t Thread;
    struct timespec Timeout;
    struct timespec TimeoutPthread;

    struct
    {
        struct nlmsghdr nlh;
        struct ifinfomsg ifm;
        struct rtattr ext_req __attribute__((aligned(NLMSG_ALIGNTO)));
        __u32 ext_filter_mask;
    } Request;

    //
    // Create and bind socket. Create a RTM_GETLINK request. Note that the
    // request is not sent yet at this point.
    //

    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
    Request.nlh.nlmsg_len = sizeof(Request);
    Request.nlh.nlmsg_type = RTM_GETLINK;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.ifm.ifi_family = AF_NETLINK;
    Request.ext_req.rta_type = IFLA_EXT_MASK;
    Request.ext_req.rta_len = RTA_LENGTH(sizeof(__u32));
    Request.ext_filter_mask = RTEXT_FILTER_VF;

    //
    // The timeout value passed to recvmmsg() is ignored - there is a bug in the
    // Linux kernel where the timeout does not work at all. In lxcore we ignore
    // this parameter.
    //

    Timeout.tv_sec = 800;
    Timeout.tv_nsec = 0;

    //
    // Set up the receive buffers.
    //

    memset(ReceiveMessages, 0, sizeof(ReceiveMessages));
    for (Index = 0; Index < 10; Index++)
    {
        ReceiveIovecs[Index].iov_base = ReceiveBuffers[Index];
        ReceiveIovecs[Index].iov_len = 1000;
        ReceiveMessages[Index].msg_hdr.msg_iov = &ReceiveIovecs[Index];
        ReceiveMessages[Index].msg_hdr.msg_iovlen = 1;
    }

    //
    // There is nothing to receive at this point. Test that MSG_DONTWAIT trumps
    // MSG_WAITFORONE and MSG_WAITALL, therefore EAGAIN is returned due to not waiting.
    //

    LxtCheckErrnoFailure(Result = recvmmsg(Socket, ReceiveMessages, 1000, MSG_DONTWAIT | MSG_WAITFORONE | MSG_WAITALL, &Timeout), EAGAIN);

    LxtCheckErrnoFailure(Result = recvmmsg(Socket, ReceiveMessages, 1000, MSG_DONTWAIT | MSG_WAITFORONE, &Timeout), EAGAIN);

    LxtCheckErrnoFailure(Result = recvmmsg(Socket, ReceiveMessages, 1000, MSG_DONTWAIT | MSG_WAITALL, &Timeout), EAGAIN);

    LxtCheckErrnoFailure(Result = recvmmsg(Socket, ReceiveMessages, 1000, MSG_DONTWAIT, &Timeout), EAGAIN);

    //
    // Set a SO_RCVTIMEO timeout value of 8 milliseconds for the receive. This timeout
    // applies to each individual recvmsg() call and actually works. Both calls to
    // recvmmsg below should time out from the SO_RCVTIMEO.
    //

    LxtCheckResult(SocketNetlinkSetAndVerifySocketOptionTimeout(Socket, SO_RCVTIMEO, 8000));

    LxtCheckErrnoFailure(Result = recvmmsg(Socket, ReceiveMessages, 1000, MSG_WAITFORONE, &Timeout), EAGAIN);

    LxtCheckErrnoFailure(Result = recvmmsg(Socket, ReceiveMessages, 1000, 0, &Timeout), EAGAIN);

    //
    // Restore the SO_RCVTIMEO timeout to 0 (never times out).
    //

    LxtCheckResult(SocketNetlinkSetAndVerifySocketOptionTimeout(Socket, SO_RCVTIMEO, 0));

    //
    // Test blocking behavior when zero flags (option 0) and when MSG_WAITFORONE
    // (option 1) is passed to recvmmsg. Create a new thread that blocks forever,
    // then cancel the thread after waiting for some time.
    //

    for (Index = 0; Index < 2; Index++)
    {

        //
        // Create a reader thread that will block on 'recvmmsg'.
        //

        BlockedReaderParams.Socket = Socket;
        BlockedReaderParams.Option = Index;
        LxtCheckResultError(pthread_create(&Thread, NULL, SocketNetlinkRecvmmsgBlockedReaderThread, &BlockedReaderParams));

        //
        // Wait for sometime to allow the reader thread to block on read. There
        // is no other elegant way of knowing whether the thread has blocked.
        //

        usleep(5000);

        //
        // The pthread_timedjoin_np() should timeout since the reader thread is
        // still blocked.
        //

        TimeoutPthread.tv_sec = 0;
        TimeoutPthread.tv_nsec = 5000;
        Result = pthread_timedjoin_np(Thread, &PthreadResult, &TimeoutPthread);
        if (Result != ETIMEDOUT)
        {
            LxtLogError(
                "Expecting pthread_tryjoin_np to return ETIMEDOUT(%d), "
                "but it returned with result: %d for index: %d",
                ETIMEDOUT,
                Result,
                Index);

            goto ErrorExit;
        }

        //
        // No other choice but to kill the reader thread.
        //

        LxtCheckErrnoZeroSuccess(pthread_cancel(Thread));
        LxtCheckErrnoZeroSuccess(pthread_join(Thread, &PthreadResult));
        LxtCheckEqual(PthreadResult, PTHREAD_CANCELED, "%p");
    }

    //
    // Now generate a lot of Netlink responses waiting to be read.
    //

    for (Index = 0; Index < 10; Index++)
    {
        LxtCheckErrno(SendResult = send(Socket, &Request, sizeof(Request), 0));
        LxtCheckEqual(SendResult, sizeof(Request), "%d");
    }

    //
    // Test various combinations of input parameters and verify the output.
    //

    LxtCheckErrno(Result = recvmmsg(Socket, ReceiveMessages, 3, MSG_DONTWAIT | MSG_WAITFORONE | MSG_WAITALL | MSG_PEEK | MSG_TRUNC, &Timeout));

    LxtCheckEqual(Result, 3, "%d");
    LxtCheckNotEqual(ReceiveMessages[0].msg_len, 0, "%d");
    LxtCheckNotEqual(ReceiveMessages[1].msg_len, 0, "%d");
    LxtCheckNotEqual(ReceiveMessages[2].msg_len, 0, "%d");
    LxtCheckEqual(ReceiveMessages[3].msg_len, 0, "%d");
    LxtCheckErrno(Result = recvmmsg(Socket, ReceiveMessages, 2, MSG_DONTWAIT, &Timeout));
    LxtCheckEqual(Result, 2, "%d");
    LxtCheckNotEqual(ReceiveMessages[0].msg_len, 0, "%d");
    LxtCheckNotEqual(ReceiveMessages[1].msg_len, 0, "%d");
    LxtCheckNotEqual(ReceiveMessages[2].msg_len, 0, "%d");
    LxtCheckEqual(ReceiveMessages[3].msg_len, 0, "%d");
    ReceiveMessages[0].msg_len = 0;
    ReceiveMessages[1].msg_len = 0;
    ReceiveMessages[2].msg_len = 0;
    LxtCheckErrno(Result = recvmmsg(Socket, ReceiveMessages, 1, 0, &Timeout));
    LxtCheckEqual(Result, 1, "%d");
    LxtCheckNotEqual(ReceiveMessages[0].msg_len, 0, "%d");
    LxtCheckEqual(ReceiveMessages[1].msg_len, 0, "%d");
    LxtCheckEqual(ReceiveMessages[2].msg_len, 0, "%d");
    LxtCheckEqual(ReceiveMessages[3].msg_len, 0, "%d");
    LxtCheckErrno(Result = recvmmsg(Socket, ReceiveMessages, 1, MSG_WAITFORONE, &Timeout));
    LxtCheckEqual(Result, 1, "%d");
    LxtCheckNotEqual(ReceiveMessages[0].msg_len, 0, "%d");
    LxtCheckEqual(ReceiveMessages[1].msg_len, 0, "%d");
    LxtCheckEqual(ReceiveMessages[2].msg_len, 0, "%d");
    LxtCheckEqual(ReceiveMessages[3].msg_len, 0, "%d");
    Result = LXT_RESULT_SUCCESS;

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

void* SocketNetlinkRecvmmsgBlockedReaderThread(void* Arg)

/*++

Routine Description:

    This routine will call recvmmsg on the given socket fd and block.

Arguments:

    Arg - Supplies the socket fd to read and the options.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    NetlinkRecvmmsgBlockedReaderParams* BlockedReaderParams;
    int Flags;
    int Index;
    int MessagesRead;
    char ReceiveBuffers[20][1000];
    struct iovec ReceiveIovecs[20];
    struct mmsghdr ReceiveMessages[20];
    int Result = LXT_RESULT_FAILURE;
    int Socket;
    struct timespec Timeout;

    //
    // The timeout value passed to recvmmsg() is ignored. Set up the receive
    // buffers.
    //

    BlockedReaderParams = (NetlinkRecvmmsgBlockedReaderParams*)Arg;
    Socket = BlockedReaderParams->Socket;
    Timeout.tv_sec = 800;
    Timeout.tv_nsec = 0;
    memset(ReceiveMessages, 0, sizeof(ReceiveMessages));
    for (Index = 0; Index < 20; Index++)
    {
        ReceiveIovecs[Index].iov_base = ReceiveBuffers[Index];
        ReceiveIovecs[Index].iov_len = 1000;
        ReceiveMessages[Index].msg_hdr.msg_iov = &ReceiveIovecs[Index];
        ReceiveMessages[Index].msg_hdr.msg_iovlen = 1;
    }

    //
    // The flags passed to recvmmsg() depend on the option value passed into
    // this function.
    //

    switch (BlockedReaderParams->Option)
    {
    case 0:
        Flags = 0;
        break;

    case 1:
        Flags = MSG_WAITFORONE;
        break;

    default:
        LxtLogError("Incorrect option: %d", BlockedReaderParams->Option);
        goto ErrorExit;
    }

    LxtCheckErrno(MessagesRead = recvmmsg(Socket, ReceiveMessages, 20, Flags, NULL));
    if (MessagesRead != 0)
    {
        LxtLogError(
            "recvmmsg should return 0 messages read, "
            "but it returned %d messages for flags %x",
            MessagesRead,
            Flags);

        goto ErrorExit;
    }

    LxtLogInfo("recvmmsg unblocked, flags %x", Flags);
    Result = LXT_RESULT_SUCCESS;

ErrorExit:
    pthread_exit((void*)(ssize_t)Result);
}

int SocketNetlinkSendBadMessage(PLXT_ARGS Args)

/*++

Routine Description:

    This routine sends bad Netlink messages, to test that the system does not crash.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    struct sockaddr_nl BindAddress;
    char* Buffer;
    struct nlmsghdr* Header;
    char ReceiveBuffer;
    int Result;
    int Socket;
    int TypeIterator;

    Buffer = malloc(PAGE_SIZE * 2);
    LxtCheckTrue(Buffer != NULL);
    Header = (struct nlmsghdr*)(((uint64_t)(Buffer + PAGE_SIZE) & ~(PAGE_SIZE - 1)) - sizeof(struct nlmsghdr));
    LxtLogInfo("Malloc end of page: %p", Header);
    for (TypeIterator = 0; TypeIterator < sizeof(MessageTypes) / sizeof(int); TypeIterator++)
    {

        LxtLogInfo("Checking message type: %d", MessageTypes[TypeIterator]);
        LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
        memset(&BindAddress, 0, sizeof(BindAddress));
        BindAddress.nl_family = AF_NETLINK;
        AddressLength = sizeof(BindAddress);
        LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

        Header->nlmsg_flags = NLM_F_REQUEST;
        Header->nlmsg_len = sizeof(struct nlmsghdr);
        Header->nlmsg_type = MessageTypes[TypeIterator];
        Header->nlmsg_seq = 0x4567;
        sendto(Socket, Header, sizeof(struct nlmsghdr), 0, NULL, 0);
    }

    Result = LXT_RESULT_SUCCESS;

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    if (Buffer != NULL)
    {
        free(Buffer);
    }

    return Result;
}

void SocketNetlinkRouteDumpAttributeData(char* Buffer, int BufferSize, struct rtattr* Attribute)

/*++

Routine Description:

    This routine dumps the data in the NETLINK_ROUTE protocol's RT attribute.

Arguments:

    Buffer - Supplies a pointer to the buffer to write the dumped data to.

    Attribute - Supplies a pointer to the RT attribute to dump.

Return Value:

    None.

--*/

{

    unsigned char* AttributeCurrent;
    char* BufferCurrent;
    int Index;

    AttributeCurrent = (unsigned char*)RTA_DATA(Attribute);
    BufferCurrent = Buffer;
    memset(Buffer, 0, BufferSize);

    //
    // Each char takes 3 bytes in the dump buffer.
    //

    for (Index = 0; Index < min((BufferSize - 1) / 3, RTA_PAYLOAD(Attribute)); Index++)
    {
        sprintf(BufferCurrent, "%02x ", *AttributeCurrent);
        AttributeCurrent += 1;
        BufferCurrent += 3;
    }

    return;
}

int SocketNetlinkRouteGetAddr(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests the NETLINK_ROUTE protocol's RTM_GETADDR message.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    struct rtattr* Attribute;
    char AttributeDump[ATTRIBUTE_DUMP_BUFFER_SIZE];
    int AttributeSeenAddress;
    int AttributeSeenCacheInfo;
    struct sockaddr_nl BindAddress;
    struct nlmsgerr* Error;
    int FoundDone;
    struct nlmsghdr* Header;
    struct ifaddrmsg* IfAddrMsg;
    char ReceiveBuffer[5000];
    int ReceiveResult;
    int RemainingLength;
    int Result;
    int Socket;

    struct
    {
        struct nlmsghdr nlh;
        struct ifinfomsg ifm;
        struct rtattr ext_req __attribute__((aligned(NLMSG_ALIGNTO)));
        __u32 ext_filter_mask;
    } Request;

    AttributeSeenAddress = 0;
    AttributeSeenCacheInfo = 0;

    //
    // Create and bind socket. Create a RTM_GETADDR request.
    //

    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = sizeof(Request);
    Request.nlh.nlmsg_type = RTM_GETADDR;
    Request.nlh.nlmsg_seq = 0x4563;
    Request.ifm.ifi_family = AF_NETLINK;
    Request.ext_req.rta_type = IFLA_EXT_MASK;
    Request.ext_req.rta_len = RTA_LENGTH(sizeof(__u32));
    Request.ext_filter_mask = RTEXT_FILTER_VF;

    //
    // Test flags. Only passing the NLM_F_REQUEST flag returns an Error.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));

    Header = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));
    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(Header);
    LxtCheckEqual(Error->error, -EOPNOTSUPP, "%d");
    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, "%d");

    //
    // Test flags. Only passing the NLM_F_REQUEST flag returns an Error.
    // Verify that adding a NLM_F_ACK flag still returns an Error.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));

    Header = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));
    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(Header);
    LxtCheckEqual(Error->error, -EOPNOTSUPP, "%d");
    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, "%d");

    //
    // Test flags. Only passing the NLM_F_ROOT and/or NLM_F_MATCH flag(s)
    // result in no response.
    //

    Request.nlh.nlmsg_flags = NLM_F_ROOT;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = NLM_F_MATCH;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Test flags. Passing 0 flags or invalid flags result in no response.
    //

    Request.nlh.nlmsg_flags = 0;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = 0x40;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Test flags. NLM_F_REQUEST and at least one of NLM_F_ROOT/NLM_F_MATCH
    // results in the correct response.
    // For the response, verify the presence of several attributes.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    FoundDone = 0;
    for (;;)
    {
        LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0));

        Header = (struct nlmsghdr*)ReceiveBuffer;
        while (NLMSG_OK(Header, ReceiveResult))
        {
            if (Header->nlmsg_type == NLMSG_DONE)
            {
                FoundDone = 1;
                break;
            }

            IfAddrMsg = (struct ifaddrmsg*)NLMSG_DATA(Header);
            LxtLogInfo(
                "ifaddrmsg: ifa_family %d ifa_prefixlen %d "
                "ifa_flags %d ifa_scope %d ifa_index %d",
                IfAddrMsg->ifa_family,
                IfAddrMsg->ifa_prefixlen,
                IfAddrMsg->ifa_flags,
                IfAddrMsg->ifa_scope,
                IfAddrMsg->ifa_index);

            Attribute = (struct rtattr*)((char*)IfAddrMsg + sizeof(struct ifaddrmsg));

            RemainingLength = Header->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));

            while (RTA_OK(Attribute, RemainingLength))
            {
                SocketNetlinkRouteDumpAttributeData(AttributeDump, ATTRIBUTE_DUMP_BUFFER_SIZE, Attribute);

                LxtLogInfo("RTATTR type: %2d len: %3d data: %s", Attribute->rta_type, Attribute->rta_len, AttributeDump);

                if (Attribute->rta_type == IFA_ADDRESS)
                {
                    AttributeSeenAddress += 1;
                }
                else if (Attribute->rta_type == IFA_CACHEINFO)
                {
                    LxtCheckEqual(Attribute->rta_len, RTA_LENGTH(sizeof(struct ifa_cacheinfo)), "%d");

                    AttributeSeenCacheInfo += 1;
                }

                Attribute = RTA_NEXT(Attribute, RemainingLength);
            }

            Header = NLMSG_NEXT(Header, ReceiveResult);
        }

        if (FoundDone == 1)
        {
            break;
        }
    }

    LxtCheckTrue(AttributeSeenAddress > 0);
    LxtCheckTrue(AttributeSeenCacheInfo > 0);

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

int SocketNetlinkRouteGetLink(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests the NETLINK_ROUTE protocol's RTM_GETLINK message.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    struct sockaddr_nl BindAddress;
    pid_t ChildPid;
    int ChildSocket;
    struct nlmsgerr* Error;
    struct nlmsghdr* Header;
    int Index;
    char InterfaceName[32];
    int LoopbackIndex;
    char ReceiveBuffer[5000];
    int ReceiveResult;
    int Result;
    int SendResult;
    int Socket;

    struct
    {
        struct nlmsghdr nlh;
        struct ifinfomsg ifm;
        char Attributes[200];
    } Request;

    //
    // Create and bind socket. Create a RTM_GETLINK request.
    //

    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = sizeof(Request);
    Request.nlh.nlmsg_type = RTM_GETLINK;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.ifm.ifi_family = AF_NETLINK;

    //
    // Test flags. Only passing the NLM_F_REQUEST flag with no network interface
    // specified returns an Error.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));

    Header = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));
    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(Header);
    LxtCheckEqual(Error->error, -EINVAL, "%d");
    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, "%d");

    //
    // Test flags. Only passing the NLM_F_REQUEST flag with no network interface
    // returns an Error. Verify that adding a NLM_F_ACK flag still returns an Error.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));

    Header = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));
    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(Header);
    LxtCheckEqual(Error->error, -EINVAL, "%d");
    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, "%d");

    //
    // Test flags. Only passing the NLM_F_ROOT and/or NLM_F_MATCH flag(s)
    // result in no response.
    //

    Request.nlh.nlmsg_flags = NLM_F_ROOT;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = NLM_F_MATCH;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Test flags. Passing 0 flags or invalid flags result in no response.
    //

    Request.nlh.nlmsg_flags = 0;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = 0x40;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Test filter mode.
    // When passing only the NLM_F_REQUEST flag, "filter" mode is used.
    // In this mode, either ifinfomsg.ifi_index must be valid, or IFLA_IFNAME
    // must be present to filter the response to one network interface.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST;

    //
    // Passing a zero ifi_index returns an Error.
    //

    Request.ifm.ifi_index = 0;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));

    Header = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));
    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(Header);
    LxtCheckEqual(Error->error, -EINVAL, "%d");

    //
    // Passing a negative ifi_index returns an Error.
    //

    Request.ifm.ifi_index = -1;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));

    Header = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));
    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(Header);
    LxtCheckEqual(Error->error, -EINVAL, "%d");

    //
    // Passing a bad ifi_index and the correct interface name returns an Error.
    //

    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
    Request.ifm.ifi_index = 100;
    strcpy(InterfaceName, "lo");
    LxtCheckErrnoZeroSuccess(
        SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), IFLA_IFNAME, InterfaceName, strlen(InterfaceName) + 1));

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));

    Header = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));
    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(Header);
    LxtCheckEqual(Error->error, -ENODEV, "%d");

    //
    // Passing a negative ifi_index and the correct interface name results in the
    // correct response.
    //

    Request.ifm.ifi_index = -1;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckResult(SocketNetlinkRouteGetLinkCheckResponse(Socket, TRUE));

    //
    // Passing a zero ifi_index and the correct interface name results in the
    // correct response.
    //

    Request.ifm.ifi_index = 0;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckResult(SocketNetlinkRouteGetLinkCheckResponse(Socket, TRUE));

    //
    // Passing a bad ifi_index and a bad interface name returns an Error.
    //

    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
    Request.ifm.ifi_index = 100;
    strcpy(InterfaceName, "loooo");
    LxtCheckErrnoZeroSuccess(
        SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), IFLA_IFNAME, InterfaceName, strlen(InterfaceName) + 1));

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));

    Header = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));
    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(Header);
    LxtCheckEqual(Error->error, -ENODEV, "%d");

    //
    // Passing a zero ifi_index and a bad interface name returns an Error.
    //

    Request.ifm.ifi_index = 0;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));

    Header = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));
    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(Header);
    LxtCheckEqual(Error->error, -ENODEV, "%d");

    //
    // Passing a negative ifi_index and a bad interface name returns an Error.
    //

    Request.ifm.ifi_index = -1;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));

    Header = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));
    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, "%d");
    Error = (struct nlmsgerr*)NLMSG_DATA(Header);
    LxtCheckEqual(Error->error, -ENODEV, "%d");

    //
    // Get the interface index of the loopback adapter.
    //

    LxtCheckErrnoZeroSuccess(SocketNetlinkGetLoopbackIndex(&LoopbackIndex));
    LxtCheckTrue(LoopbackIndex > 0);

    //
    // Passing a correct ifi_index and a bad interface name results in the
    // correct response.
    //

    Request.ifm.ifi_index = LoopbackIndex;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckResult(SocketNetlinkRouteGetLinkCheckResponse(Socket, TRUE));

    //
    // Passing a correct ifi_index and no interface name results in the
    // correct response.
    //

    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckResult(SocketNetlinkRouteGetLinkCheckResponse(Socket, TRUE));

    //
    // Test dump mode.
    // NLM_F_REQUEST and at least one of NLM_F_ROOT/NLM_F_MATCH
    // results in the correct response.
    // For the response, verify that at least one interface is present (loopback),
    // and also verify the presence of several attributes.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckResult(SocketNetlinkRouteGetLinkCheckResponse(Socket, FALSE));

    //
    // Create a child process and switch it to a new network namespace.
    // RTM_GETLINK should only return one network interface - loopback.
    //

    LxtCheckErrno(ChildPid = fork());
    if (ChildPid == 0)
    {
        LxtCheckErrnoZeroSuccess(unshare(CLONE_NEWNET));

        //
        // N.B. The sleep is because it can take some time for the lxcore cache to get
        //      the new network interface notification.
        //

        usleep(1000 * 100);
        LxtLogInfo("Now testing child socket");
        LxtCheckErrno(ChildSocket = socket(AF_NETLINK, SOCK_RAW, 0));
        LxtCheckErrno(sendto(ChildSocket, &Request, sizeof(Request), 0, NULL, 0));
        LxtCheckResult(SocketNetlinkRouteGetLinkCheckResponse(ChildSocket, TRUE));
        LxtClose(ChildSocket);
        exit(0);
    }

    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));

    //
    // Check that sending is still successful even if the
    // receive buffer has overflown.
    //

    for (Index = 0; Index < 1000; Index++)
    {
        LxtCheckErrno(SendResult = send(Socket, &Request, sizeof(Request), 0));
        LxtCheckEqual(SendResult, sizeof(Request), "%d");
    }

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

int SocketNetlinkRouteGetLinkCheckResponse(int Socket, int OnlyOneInterface)

/*++

Routine Description:

    This routine checks the response of the NETLINK_ROUTE protocol's
    RTM_GETLINK message.

Arguments:

    Socket - Supplies the socket to read the response from.

    OnlyOneInterface - Supplies a boolean indicating whether there should only
        be one network interface in the response.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    struct rtattr* Attribute;
    char AttributeDump[ATTRIBUTE_DUMP_BUFFER_SIZE];
    int AttributeSeenAddress;
    int AttributeSeenMtu;
    int AttributeSeenName;
    int FoundDone;
    struct nlmsghdr* Header;
    struct ifinfomsg* IfInfoMsg;
    int InterfaceCount;
    char ReceiveBuffer[5000];
    int ReceiveResult;
    int RemainingLength;
    int Result;
    int SeenLoopback;

    AttributeSeenAddress = 0;
    AttributeSeenMtu = 0;
    AttributeSeenName = 0;
    SeenLoopback = 0;
    FoundDone = 0;
    InterfaceCount = 0;
    for (;;)
    {
        LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0));

        Header = (struct nlmsghdr*)ReceiveBuffer;
        while (NLMSG_OK(Header, ReceiveResult))
        {
            if (Header->nlmsg_type == NLMSG_DONE)
            {
                FoundDone = 1;
                break;
            }

            IfInfoMsg = (struct ifinfomsg*)NLMSG_DATA(Header);
            LxtLogInfo(
                "ifinfomsg: ifi_family %d ifi_type %d "
                "ifi_index %d ifi_flags %d ifi_change %d",
                IfInfoMsg->ifi_family,
                IfInfoMsg->ifi_type,
                IfInfoMsg->ifi_index,
                IfInfoMsg->ifi_flags,
                IfInfoMsg->ifi_change);

            InterfaceCount += 1;
            if ((IfInfoMsg->ifi_flags & IFF_LOOPBACK) != 0)
            {
                SeenLoopback += 1;
            }

            Attribute = (struct rtattr*)((char*)IfInfoMsg + sizeof(struct ifinfomsg));

            RemainingLength = Header->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg));

            while (RTA_OK(Attribute, RemainingLength))
            {
                SocketNetlinkRouteDumpAttributeData(AttributeDump, ATTRIBUTE_DUMP_BUFFER_SIZE, Attribute);

                LxtLogInfo("RTATTR type: %2d len: %3d data: %s", Attribute->rta_type, Attribute->rta_len, AttributeDump);

                if (Attribute->rta_type == IFLA_ADDRESS)
                {
                    AttributeSeenAddress += 1;
                }
                else if (Attribute->rta_type == IFLA_MTU)
                {
                    AttributeSeenMtu += 1;
                }
                else if (Attribute->rta_type == IFLA_IFNAME)
                {
                    AttributeSeenName += 1;
                }

                Attribute = RTA_NEXT(Attribute, RemainingLength);
            }

            if ((Header->nlmsg_flags & NLM_F_MULTI) == 0)
            {
                goto LoopDone;
            }

            Header = NLMSG_NEXT(Header, ReceiveResult);
        }

        if (FoundDone == 1)
        {
            break;
        }
    }

LoopDone:
    LxtCheckTrue(AttributeSeenAddress > 0);
    LxtCheckTrue(AttributeSeenMtu > 0);
    LxtCheckTrue(AttributeSeenName > 0);
    LxtCheckEqual(SeenLoopback, 1, "%d");
    LxtLogInfo("Found %d interfaces total", InterfaceCount);
    if (OnlyOneInterface != FALSE)
    {
        LxtCheckEqual(InterfaceCount, 1, "%d");
    }

    Result = LXT_RESULT_SUCCESS;

ErrorExit:
    return Result;
}

int SocketNetlinkRouteGetRouteBestRoute(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests the NETLINK_ROUTE protocol's RTM_GETROUTE message's
    get best route request.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    struct sockaddr_nl BindAddress;
    struct in_addr DestinationIpv4;
    struct in6_addr DestinationIpv6;
    struct in_addr GatewayIpv4;
    struct in6_addr GatewayIpv6;
    struct nlmsgerr* Error;
    struct nlmsghdr* Header;
    int Index;
    bool IsIpV6Configured;
    int LoopbackIndex;
    char ReceiveBuffer[5000];
    int ReceiveResult;
    int Result;
    int SendResult;
    int Socket;

    struct
    {
        struct nlmsghdr nlh;
        struct rtmsg msg;
        char attr[200];
    } Request;

    //
    // Get the interface index of the loopback adapter.
    //

    LxtCheckErrnoZeroSuccess(SocketNetlinkGetLoopbackIndex(&LoopbackIndex));
    LxtCheckTrue(LoopbackIndex > 0);

    //
    // Create and bind socket. Create a RTM_GETROUTE request.
    //

    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_GETROUTE;
    Request.nlh.nlmsg_seq = 0x4567;

    //
    // NLM_F_REQUEST with no NLM_F_DUMP means "get best route" request.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST;

    //
    // Test specifying an invalid address family.
    //

    Request.msg.rtm_family = AF_UNSPEC;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EOPNOTSUPP));

    //
    // Test specifying AF_INET6 while really giving an IPv4 destination address.
    //

    Request.msg.rtm_family = AF_INET6;
    inet_aton("1.1.1.1", &DestinationIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, -1);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EINVAL));

    IsIpV6Configured = SocketNetlinkIsIpv6Configured();

    //
    // Test not specifying the destination (both Ipv4 and Ipv6).
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_GETROUTE;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST;
    Request.msg.rtm_family = AF_INET;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, FALSE));
    Request.msg.rtm_family = AF_INET6;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    if (IsIpV6Configured)
    {
        LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, FALSE));
    }
    else
    {
        LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));

        LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENETUNREACH));
    }

    //
    // Test specifying the destination (Ipv4).
    //

    inet_aton("1.1.1.1", &DestinationIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, -1);

    Request.msg.rtm_family = AF_INET;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, TRUE));

    //
    // Test specifying the destination (Ipv6).
    //
    if (IsIpV6Configured)
    {
        memset(&Request, 0, sizeof(Request));
        Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
        Request.nlh.nlmsg_type = RTM_GETROUTE;
        Request.nlh.nlmsg_seq = 0x4567;
        Request.nlh.nlmsg_flags = NLM_F_REQUEST;
        Request.msg.rtm_family = AF_INET6;
        inet_pton(AF_INET6, "12::", &GatewayIpv6);
        LxtCheckErrnoZeroSuccess(
            SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));

        LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
        LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, TRUE));
    }

    //
    // Test specifying the destination and gateway (Ipv4).
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_GETROUTE;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST;
    Request.msg.rtm_family = AF_INET;
    inet_aton("1.1.1.1", &DestinationIpv4);
    inet_aton("1.1.1.2", &GatewayIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, -1);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, TRUE));

    //
    // Test specifying the destination and gateway (Ipv6).
    //

    if (IsIpV6Configured)
    {
        memset(&Request, 0, sizeof(Request));
        Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
        Request.nlh.nlmsg_type = RTM_GETROUTE;
        Request.nlh.nlmsg_seq = 0x4567;
        Request.nlh.nlmsg_flags = NLM_F_REQUEST;
        Request.msg.rtm_family = AF_INET6;
        inet_pton(AF_INET6, "12::", &DestinationIpv6);
        inet_pton(AF_INET6, "13::", &GatewayIpv6);
        LxtCheckErrnoZeroSuccess(
            SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));

        LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_GATEWAY, &GatewayIpv6, sizeof(GatewayIpv6)));

        LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
        LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, TRUE));
    }

    //
    // Test specifying the destination, gateway and interface index (Ipv4).
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_GETROUTE;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST;
    Request.msg.rtm_family = AF_INET;
    inet_aton("127.0.0.1", &DestinationIpv4);
    inet_aton("1.1.1.2", &GatewayIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, FALSE));

    //
    // Test specifying the destination, gateway and interface index (Ipv6).
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_GETROUTE;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST;
    Request.msg.rtm_family = AF_INET6;
    inet_pton(AF_INET6, "::1", &DestinationIpv6);
    inet_pton(AF_INET6, "13::", &GatewayIpv6);
    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));

    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_GATEWAY, &GatewayIpv6, sizeof(GatewayIpv6)));

    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, FALSE));

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

int SocketNetlinkRouteGetRouteBestRouteCheckResponse(int Socket, int CheckGateway)

/*++

Routine Description:

    This routine tests the response to the NETLINK_ROUTE protocol's
    RTM_GETROUTE message's get best route request.

Arguments:

    Socket - Supplies the socket to read the response from.

    CheckGateway - Supplies a boolean indicating whether to check for the
        presence of the RTA_GATEWAY attribute.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    struct rtattr* Attribute;
    char AttributeDump[ATTRIBUTE_DUMP_BUFFER_SIZE];
    int AttributeSeenDst;
    int AttributeSeenGateway;
    int AttributeSeenOif;
    int FoundDone;
    struct nlmsghdr* Header;
    char ReceiveBuffer[5000];
    int ReceiveResult;
    int RemainingLength;
    int Result;
    struct rtmsg* RtMsg;

    AttributeSeenDst = 0;
    AttributeSeenGateway = 0;
    AttributeSeenOif = 0;
    FoundDone = 0;
    LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0));

    Header = (struct nlmsghdr*)ReceiveBuffer;
    RtMsg = (struct rtmsg*)NLMSG_DATA(Header);
    LxtLogInfo(
        "rtmsg: rtm_family %d rtm_dst_len %d rtm_src_len %d "
        "rtm_tos %d rtm_table %d rtm_protocol %d "
        "rtm_scope %d rtm_type %d rtm_flags %d",
        RtMsg->rtm_family,
        RtMsg->rtm_dst_len,
        RtMsg->rtm_src_len,
        RtMsg->rtm_tos,
        RtMsg->rtm_table,
        RtMsg->rtm_protocol,
        RtMsg->rtm_scope,
        RtMsg->rtm_type,
        RtMsg->rtm_flags);

    Attribute = (struct rtattr*)((char*)RtMsg + sizeof(struct rtmsg));
    RemainingLength = Header->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg));
    while (RTA_OK(Attribute, RemainingLength))
    {
        SocketNetlinkRouteDumpAttributeData(AttributeDump, ATTRIBUTE_DUMP_BUFFER_SIZE, Attribute);

        LxtLogInfo("RTATTR type: %2d len: %3d data: %s", Attribute->rta_type, Attribute->rta_len, AttributeDump);

        if (Attribute->rta_type == RTA_DST)
        {
            AttributeSeenDst += 1;
        }
        else if (Attribute->rta_type == RTA_GATEWAY)
        {
            AttributeSeenGateway += 1;
        }
        else if (Attribute->rta_type == RTA_OIF)
        {
            AttributeSeenOif += 1;
        }

        Attribute = RTA_NEXT(Attribute, RemainingLength);
    }

    LxtCheckTrue(AttributeSeenDst > 0);
    if (CheckGateway != FALSE)
    {
        LxtCheckTrue(AttributeSeenGateway > 0);
    }

    LxtCheckTrue(AttributeSeenOif > 0);
    Result = LXT_RESULT_SUCCESS;

ErrorExit:
    return Result;
}

int SocketNetlinkRouteGetRouteDump(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests the NETLINK_ROUTE protocol's RTM_GETROUTE message's
    dump request.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    struct rtattr* Attribute;
    char AttributeDump[ATTRIBUTE_DUMP_BUFFER_SIZE];
    int AttributeSeenDst;
    int AttributeSeenGateway;
    int AttributeSeenOif;
    int AttributeSeenPriority;
    struct sockaddr_nl BindAddress;
    struct nlmsgerr* Error;
    int FoundDone;
    struct nlmsghdr* Header;
    int Index;
    char ReceiveBuffer[5000];
    int ReceiveResult;
    int RemainingLength;
    int Result;
    struct rtmsg* RtMsg;
    int SendResult;
    int Socket;

    struct
    {
        struct nlmsghdr nlh;
        struct rtmsg msg;
    } Request;

    AttributeSeenDst = 0;
    AttributeSeenGateway = 0;
    AttributeSeenOif = 0;
    AttributeSeenPriority = 0;

    //
    // Create and bind socket. Create a RTM_GETROUTE request.
    //

    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = sizeof(Request);
    Request.nlh.nlmsg_type = RTM_GETROUTE;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_UNSPEC;

    //
    // Test flags. Only passing the NLM_F_ROOT and/or NLM_F_MATCH flag(s)
    // result in no response.
    //

    Request.nlh.nlmsg_flags = NLM_F_ROOT;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = NLM_F_MATCH;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Test flags. Passing 0 flags or invalid flags result in no response.
    //

    Request.nlh.nlmsg_flags = 0;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = 0x40;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Test flags. NLM_F_REQUEST and at least one of NLM_F_ROOT/NLM_F_MATCH
    // results in the correct response.
    // For the response, verify that at least one interface is present (loopback),
    // and also verify the presence of several attributes.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    FoundDone = 0;
    for (;;)
    {
        LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0));

        Header = (struct nlmsghdr*)ReceiveBuffer;
        while (NLMSG_OK(Header, ReceiveResult))
        {
            if (Header->nlmsg_type == NLMSG_DONE)
            {
                FoundDone = 1;
                break;
            }

            RtMsg = (struct rtmsg*)NLMSG_DATA(Header);
            LxtLogInfo(
                "rtmsg: rtm_family %d rtm_dst_len %d rtm_src_len %d "
                "rtm_tos %d rtm_table %d rtm_protocol %d "
                "rtm_scope %d rtm_type %d rtm_flags %d",
                RtMsg->rtm_family,
                RtMsg->rtm_dst_len,
                RtMsg->rtm_src_len,
                RtMsg->rtm_tos,
                RtMsg->rtm_table,
                RtMsg->rtm_protocol,
                RtMsg->rtm_scope,
                RtMsg->rtm_type,
                RtMsg->rtm_flags);

            Attribute = (struct rtattr*)((char*)RtMsg + sizeof(struct rtmsg));

            RemainingLength = Header->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg));

            while (RTA_OK(Attribute, RemainingLength))
            {
                SocketNetlinkRouteDumpAttributeData(AttributeDump, ATTRIBUTE_DUMP_BUFFER_SIZE, Attribute);

                LxtLogInfo("RTATTR type: %2d len: %3d data: %s", Attribute->rta_type, Attribute->rta_len, AttributeDump);

                if (Attribute->rta_type == RTA_DST)
                {
                    AttributeSeenDst += 1;
                }
                else if (Attribute->rta_type == RTA_GATEWAY)
                {
                    AttributeSeenGateway += 1;
                }
                else if (Attribute->rta_type == RTA_OIF)
                {
                    AttributeSeenOif += 1;
                }
                else if (Attribute->rta_type == RTA_PRIORITY)
                {
                    AttributeSeenPriority += 1;
                }

                Attribute = RTA_NEXT(Attribute, RemainingLength);
            }

            Header = NLMSG_NEXT(Header, ReceiveResult);
        }

        if (FoundDone == 1)
        {
            break;
        }
    }

    LxtCheckTrue(AttributeSeenDst > 0);
    LxtCheckTrue(AttributeSeenGateway > 0);
    LxtCheckTrue(AttributeSeenOif > 0);
    LxtCheckTrue(AttributeSeenPriority > 0);

    //
    // Check that sending is still successful even if the
    // receive buffer has overflown.
    //

    for (Index = 0; Index < 1000; Index++)
    {
        LxtCheckErrno(SendResult = send(Socket, &Request, sizeof(Request), 0));
        LxtCheckEqual(SendResult, sizeof(Request), "%d");
    }

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

int SocketNetlinkGetLoopbackIndex(int* LoopbackIndex)

/*++

Routine Description:

    This routine obtains the interface index of the loopback adapter.

Arguments:

    LoopbackIndex - Supplies a pointer to an integer that receives the
        interface index of the loopback adapter.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    struct ifreq InterfaceRequest;
    int Result;
    int Socket;

    LxtCheckErrno(Socket = socket(AF_INET, SOCK_STREAM, 0));
    memset(&InterfaceRequest, 0, sizeof(InterfaceRequest));
    strncpy(InterfaceRequest.ifr_name, SOCKET_LOOPBACK_IF_NAME, sizeof(InterfaceRequest.ifr_name) - 1);

    LxtCheckErrnoZeroSuccess(ioctl(Socket, SIOCGIFINDEX, &InterfaceRequest));
    *LoopbackIndex = InterfaceRequest.ifr_ifindex;

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

int SocketNetlinkCheckResponseError(void* ReceiveBuffer, int ReceiveResult, int Error)

/*++

Routine Description:

    This routine checks the Netlink error message response.

Arguments:

    ReceiveBuffer - Supplies a pointer to the error message.

    ReceiveResult - Supplies the total size of the error message.

    Error - Supplies the error number to check for in the error message.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    struct nlmsgerr* ErrorMessage;
    struct nlmsghdr* ReceiveHeader;
    int Result;

    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;
    LxtCheckTrue(NLMSG_OK(ReceiveHeader, ReceiveResult));
    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, "%d");
    ErrorMessage = (struct nlmsgerr*)NLMSG_DATA(ReceiveHeader);
    LxtCheckEqual(ErrorMessage->error, Error, "%d");
    Result = 0;

ErrorExit:
    return Result;
}

int SocketNetlinkRouteAddAttribute(struct nlmsghdr* Msghdr, int MessageSize, int AttributeType, void* AttributeData, int AttributeSize)

/*++

Routine Description:

    This routine adds a RT attribute to the Netlink message.

Arguments:

    Msghdr - Supplies the message header.

    MessageSize - Supplies the total possible size of the message.

    AttributeType - Supplies the RT attribute type.

    AttributeData - Supplies the RT attribute data.

    AttributeSize - Supplies the RT attribute size.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    int Length;
    struct rtattr* Attribute;

    Length = RTA_LENGTH(AttributeSize);
    if (NLMSG_ALIGN(Msghdr->nlmsg_len) + RTA_ALIGN(Length) > MessageSize)
    {
        LxtLogError("Adding RT attribute, message size overflowed: %d %d %d", Msghdr->nlmsg_len, Length, MessageSize);

        return -1;
    }

    Attribute = NLMSG_TAIL(Msghdr);
    Attribute->rta_type = AttributeType;
    Attribute->rta_len = Length;
    memcpy(RTA_DATA(Attribute), AttributeData, AttributeSize);
    Msghdr->nlmsg_len = NLMSG_ALIGN(Msghdr->nlmsg_len) + RTA_ALIGN(Length);
    return 0;
}

void SocketNetlinkRouteAddAddressAttributes(struct nlmsghdr* Msghdr, int MessageSize, struct in_addr* AddressIpv4)

/*++

Routine Description:

    This routine appends the RTM_*ADDR message RT attributes to the message.

Arguments:

    Msghdr - Supplies the message header.

    MessageSize - Supplies the total possible size of the message.

    AddressIpv4 - Supplies a pointer to the IPv4 address RT attribute.

Return Value:

    None.

--*/

{

    int Result;

    if (AddressIpv4 != NULL)
    {
        LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(Msghdr, MessageSize, IFA_ADDRESS, AddressIpv4, sizeof(*AddressIpv4)));

        LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(Msghdr, MessageSize, IFA_LOCAL, AddressIpv4, sizeof(*AddressIpv4)));
    }

ErrorExit:
    return;
}

void SocketNetlinkRouteAddRouteAttributes(struct nlmsghdr* Msghdr, int MessageSize, struct in_addr* DestinationIpv4, struct in_addr* GatewayIpv4, int InterfaceIndex)

/*++

Routine Description:

    This routine appends the RTM_*ROUTE message RT attributes to the message.

Arguments:

    Msghdr - Supplies the message header.

    MessageSize - Supplies the total possible size of the message.

    DestinationIpv4 - Supplies a pointer to the destination IPv4 address RT attribute.

    GatewayIpv4 - Supplies a pointer to the gateway IPv4 address RT attribute.

    InterfaceIndex - Supplies the interface index RT attribute.

Return Value:

    None.

--*/

{

    int Result;

    if (DestinationIpv4 != NULL)
    {
        LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(Msghdr, MessageSize, RTA_DST, DestinationIpv4, sizeof(*DestinationIpv4)));
    }

    if (GatewayIpv4 != NULL)
    {
        LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(Msghdr, MessageSize, RTA_GATEWAY, GatewayIpv4, sizeof(*GatewayIpv4)));
    }

    if (InterfaceIndex > 0)
    {
        LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(Msghdr, MessageSize, RTA_OIF, &InterfaceIndex, sizeof(InterfaceIndex)));
    }

ErrorExit:
    return;
}

int SocketNetlinkRouteNewDelAddress(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests the NETLINK_ROUTE protocol's
    RTM_NEWADDR and RTM_DELADDR messages.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    struct in_addr AddressIpv4;
    struct in6_addr AddressIpv6;
    socklen_t AddressLength;
    struct sockaddr_nl BindAddress;
    struct nlmsgerr* Error;
    struct in_addr GatewayIpv4;
    struct in6_addr GatewayIpv6;
    int LoopbackIndex;
    char ReceiveBuffer[5000];
    struct nlmsghdr* ReceiveHeader;
    int ReceiveResult;
    struct
    {
        struct nlmsghdr nlh;
        struct ifaddrmsg msg;
        char attr[200];
    } Request;
    int Result;
    int Retries;
    int Socket;

    //
    // Create and bind socket. Create a RTM_GETROUTE request.
    //

    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
    Request.nlh.nlmsg_type = RTM_NEWADDR;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.ifa_family = AF_UNSPEC;

    //
    // Get the interface index of the loopback adapter. All the tests
    // below operate on the loopback adapter.
    //

    LxtCheckErrnoZeroSuccess(SocketNetlinkGetLoopbackIndex(&LoopbackIndex));
    LxtCheckTrue(LoopbackIndex > 0);

    //
    // Test flags. Passing in (invalid) flags for RTM_GET* results in no response.
    //

    Request.nlh.nlmsg_flags = NLM_F_ROOT;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = NLM_F_MATCH;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Test invalid flags with NLM_F_ACK.
    //

    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    //
    // Test flags. Passing 0 flags or invalid flags result in no response.
    //

    Request.nlh.nlmsg_flags = 0;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = 0x40;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Add an ip address 2.1.1.1/30.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
    Request.nlh.nlmsg_type = RTM_NEWADDR;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.ifa_prefixlen = 30;
    Request.msg.ifa_index = LoopbackIndex;
    inet_aton("2.1.1.1", &AddressIpv4);

    //
    // Test specifying an invalid address family.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST;
    Request.msg.ifa_family = AF_UNSPEC;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EOPNOTSUPP));

    //
    // Test not specifying the ip address, which should fail.
    //

    Request.msg.ifa_family = AF_INET;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_EXCL | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EINVAL));

    //
    // Basically any flag allows you to create.
    // Windows can take up to 10 seconds before sending the WNF notification that lxcore
    // uses to update its internal network interface and address cache. Before the cache
    // is updated, this create operation will fail, so wait for 20 seconds for the
    // operation to succeed.
    //

    SocketNetlinkRouteAddAddressAttributes(&Request.nlh, sizeof(Request), &AddressIpv4);

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_EXCL | NLM_F_ACK;
    LxtCheckResult(SocketNetlinkSendAndWaitForExpectedError(Socket, &Request, sizeof(Request), 0));

    //
    // Test NLM_F_REPLACE, which should succeed.
    // Windows can take up to 10 seconds before sending the WNF notification that lxcore
    // uses to update its internal network interface and address cache. Before the cache
    // is updated, this replace operation will fail, so wait for 20 seconds for the
    // operation to succeed.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK;
    LxtCheckResult(SocketNetlinkSendAndWaitForExpectedError(Socket, &Request, sizeof(Request), 0));

    //
    // Test NLM_F_EXCL, which should fail since it prevents existing addresses
    // from being changed.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_ACK;
    LxtCheckResult(SocketNetlinkSendAndWaitForExpectedError(Socket, &Request, sizeof(Request), -EEXIST));

    //
    // Even if NLM_F_REPLACE is added to NLM_F_EXCL, it still fails.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_EXCL | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));

    //
    // NLM_F_CREATE without NLM_F_REPLACE fails.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));

    //
    // NLM_F_CREATE with NLM_F_REPLACE succeeds.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    //
    // Test giving an invalid interface index, which should fail.
    //

    Request.msg.ifa_index = 90000;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENODEV));

    //
    // Add an ip address 2.1.1.2/32.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
    Request.nlh.nlmsg_type = RTM_NEWADDR;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.ifa_family = AF_INET;
    Request.msg.ifa_prefixlen = 32;
    Request.msg.ifa_index = LoopbackIndex;
    inet_aton("2.1.1.2", &AddressIpv4);
    SocketNetlinkRouteAddAddressAttributes(&Request.nlh, sizeof(Request), &AddressIpv4);

    //
    // Only setting the NLM_F_REQUEST flag allows creating a new ip address.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    //
    // Add an ip address 11::/31.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
    Request.nlh.nlmsg_type = RTM_NEWADDR;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.ifa_family = AF_INET6;
    Request.msg.ifa_prefixlen = 31;
    Request.msg.ifa_index = LoopbackIndex;
    inet_pton(AF_INET6, "11::", &AddressIpv6);
    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), IFA_ADDRESS, &AddressIpv6, sizeof(AddressIpv6)));

    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), IFA_LOCAL, &AddressIpv6, sizeof(AddressIpv6)));

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    //
    // Delete 2.1.1.1/30.
    //

    usleep(1000 * 40);
    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
    Request.nlh.nlmsg_type = RTM_DELADDR;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.ifa_family = AF_INET;
    Request.msg.ifa_prefixlen = 30;
    Request.msg.ifa_index = LoopbackIndex;
    inet_aton("2.1.1.1", &AddressIpv4);
    SocketNetlinkRouteAddAddressAttributes(&Request.nlh, sizeof(Request), &AddressIpv4);

    //
    // Passing 0 flags results in no response.
    //

    Request.nlh.nlmsg_flags = 0;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Need at least NLM_F_REQUEST.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    //
    // Try deleting again, which should fail.
    //

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EADDRNOTAVAIL));

    //
    // Delete 2.1.1.2/32.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
    Request.nlh.nlmsg_type = RTM_DELADDR;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.ifa_family = AF_INET;
    Request.msg.ifa_prefixlen = 32;
    Request.msg.ifa_index = LoopbackIndex;
    inet_aton("2.1.1.2", &AddressIpv4);
    SocketNetlinkRouteAddAddressAttributes(&Request.nlh, sizeof(Request), &AddressIpv4);

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_EXCL | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    //
    // Test giving an invalid interface index, which should fail.
    //

    Request.msg.ifa_index = 90000;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENODEV));

    //
    // Delete ip address 11::/31.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
    Request.nlh.nlmsg_type = RTM_DELADDR;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.ifa_family = AF_INET6;
    Request.msg.ifa_prefixlen = 31;
    Request.msg.ifa_index = LoopbackIndex;
    inet_pton(AF_INET6, "11::", &AddressIpv6);

    //
    // Test not specifying the ip address, which should fail.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EINVAL));

    //
    // Now, this should succeed.
    //

    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), IFA_ADDRESS, &AddressIpv6, sizeof(AddressIpv6)));

    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), IFA_LOCAL, &AddressIpv6, sizeof(AddressIpv6)));

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

int SocketNetlinkRouteNewDelRoute(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests the NETLINK_ROUTE protocol's
    RTM_NEWROUTE and RTM_DELROUTE messages.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    struct sockaddr_nl BindAddress;
    struct in_addr DestinationIpv4;
    struct in6_addr DestinationIpv6;
    struct in_addr GatewayIpv4;
    struct in6_addr GatewayIpv6;
    int LoopbackIndex;
    char ReceiveBuffer[5000];
    int ReceiveResult;
    struct
    {
        struct nlmsghdr nlh;
        struct rtmsg msg;
        char attr[200];
    } Request;
    int Result;
    int Socket;

    //
    // Create and bind socket. Create a RTM_GETROUTE request.
    //

    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_NEWROUTE;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_UNSPEC;

    //
    // Get the interface index of the loopback adapter. All the tests
    // below operate on the loopback adapter.
    //

    LxtCheckErrnoZeroSuccess(SocketNetlinkGetLoopbackIndex(&LoopbackIndex));
    LxtCheckTrue(LoopbackIndex > 0);

    //
    // Test flags. Passing in (invalid) flags for RTM_GET* results in no response.
    //

    Request.nlh.nlmsg_flags = NLM_F_ROOT;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = NLM_F_MATCH;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Test invalid flags with NLM_F_ACK.
    //

    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    //
    // Test flags. Passing 0 flags or invalid flags result in no response.
    //

    Request.nlh.nlmsg_flags = 0;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.nlh.nlmsg_flags = 0x40;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Add a routing entry to lo with destination 1.1.1.1 and on-link gateway.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_NEWROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_protocol = RTPROT_BOOT;
    Request.msg.rtm_scope = RT_SCOPE_LINK;
    Request.msg.rtm_type = RTN_UNICAST;
    inet_aton("1.1.1.1", &DestinationIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, -1);

    //
    // Test specifying an invalid address family.
    //

    Request.msg.rtm_family = AF_UNSPEC;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EOPNOTSUPP));

    //
    // Only send the request with the destination and no interface index.
    // The request should fail with ENODEV, because no interface index was specified.
    //

    Request.msg.rtm_family = AF_INET;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENODEV));

    //
    // Send the request with only NLM_F_REQUEST. The request should fail with ENOENT,
    // because destination 1.1.1.1 does not exist and NLM_F_CREATE was not specified.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST;
    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENOENT));

    //
    // Now add the NLM_F_REPLACE flag, and the request should still fail.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENOENT));

    //
    // Now add the NLM_F_CREATE flag, and the request should succeed.
    // Since the NLM_F_ACK flag was not specified, there should be no response,
    // even though the operation succeeded.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Sending again gets an error message EEXIST.
    //
    // N.B. The sleep is to wait for the lxcore internal cache to be updated
    //      with the latest routing info from NETIO.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
    LxtCheckResult(SocketNetlinkSendAndWaitForExpectedError(Socket, &Request, sizeof(Request), -EEXIST));

    //
    // Sending again with only NLM_F_REQUEST still gets an error message EEXIST.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));

    //
    // Sending again with NLM_F_REQUEST and NLM_F_EXCL still gets an error message EEXIST.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));

    //
    // Sending again after adding NLM_F_REPLACE still gets an error message EEXIST.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_REPLACE;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));

    //
    // Sending again with NLM_F_REPLACE is successful.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_APPEND | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    //
    // Add a routing entry to lo with destination 1.1.1.2 and on-link gateway.
    // Send the request with various flags. The request should succeed.
    //

    usleep(1000 * 60);
    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_NEWROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_protocol = RTPROT_BOOT;
    Request.msg.rtm_scope = RT_SCOPE_LINK;
    Request.msg.rtm_type = RTN_UNICAST;
    inet_aton("1.1.1.2", &DestinationIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Sending again gets an error message EEXIST.
    //

    usleep(1000 * 60);
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckEqual(ReceiveResult, Request.nlh.nlmsg_len + NLMSG_LENGTH(sizeof(int)), "%d");
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));

    //
    // Sending again with the following flags still gets an error message EEXIST.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_EXCL | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckEqual(ReceiveResult, Request.nlh.nlmsg_len + NLMSG_LENGTH(sizeof(int)), "%d");
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));

    //
    // Use NLM_F_REPLACE to change the gateway from on-link to 1.1.1.1.
    // Note that including the NLM_F_EXCL flag fails the operation.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL;
    inet_aton("1.1.1.1", &GatewayIpv4);
    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_GATEWAY, &GatewayIpv4, sizeof(GatewayIpv4)));

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckEqual(ReceiveResult, Request.nlh.nlmsg_len + NLMSG_LENGTH(sizeof(int)), "%d");
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));

    //
    // Now remove the NLM_F_EXCL flag, and the operation should succeed.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_ACK;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    //
    // Add a routing entry to lo with destination 1.1.1.3 and gateway 1.1.1.1.
    // Send the request with various flags. The request should succeed.
    //

    usleep(1000 * 60);
    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_NEWROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_protocol = RTPROT_BOOT;
    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
    Request.msg.rtm_type = RTN_UNICAST;
    inet_aton("1.1.1.3", &DestinationIpv4);
    inet_aton("1.1.1.1", &GatewayIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Change the gateway from 1.1.1.1 to on-link.
    // Note that invalid flags are silently dropped.
    //

    usleep(1000 * 60);
    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_NEWROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | 0xF800;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_protocol = RTPROT_BOOT;
    Request.msg.rtm_scope = RT_SCOPE_LINK;
    Request.msg.rtm_type = RTN_UNICAST;
    inet_aton("1.1.1.3", &DestinationIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Add two more routing entries: destination 1.1.2 and 1.3,
    // with 1.1.1.1 as their gateway.
    //

    usleep(1000 * 60);
    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_NEWROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_ACK;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_protocol = RTPROT_BOOT;
    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
    Request.msg.rtm_type = RTN_UNICAST;
    inet_aton("1.1.2", &DestinationIpv4);
    inet_aton("1.1.1.1", &GatewayIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    usleep(1000 * 60);
    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_NEWROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_protocol = RTPROT_BOOT;
    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
    Request.msg.rtm_type = RTN_UNICAST;
    inet_aton("1.3", &DestinationIpv4);
    inet_aton("1.1.1.1", &GatewayIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Add three more routing entries: destination 1.1.1.0,
    // with 1.1.1.1 as their gateway, and prefix lengths 30, 31 and 32.
    //

    usleep(1000 * 60);
    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_NEWROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 30;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_protocol = RTPROT_BOOT;
    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
    Request.msg.rtm_type = RTN_UNICAST;
    inet_aton("1.1.1.0", &DestinationIpv4);
    inet_aton("1.1.1.1", &GatewayIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    usleep(1000 * 60);
    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_NEWROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 31;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_protocol = RTPROT_BOOT;
    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
    Request.msg.rtm_type = RTN_UNICAST;
    inet_aton("1.1.1.0", &DestinationIpv4);
    inet_aton("1.1.1.1", &GatewayIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    usleep(1000 * 60);
    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_NEWROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_protocol = RTPROT_BOOT;
    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
    Request.msg.rtm_type = RTN_UNICAST;
    inet_aton("1.1.1.0", &DestinationIpv4);
    inet_aton("1.1.1.1", &GatewayIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    //
    // Test giving an invalid interface index, which should fail.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_NEWROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_protocol = RTPROT_BOOT;
    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
    Request.msg.rtm_type = RTN_UNICAST;
    inet_aton("1.1.1.10", &DestinationIpv4);
    inet_aton("1.1.1.1", &GatewayIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, 90000);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENETUNREACH));

    //
    // Add 2 Ipv6 entries.
    //

    usleep(1000 * 60);
    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_NEWROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET6;
    Request.msg.rtm_dst_len = 31;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_protocol = RTPROT_BOOT;
    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
    Request.msg.rtm_type = RTN_UNICAST;
    inet_pton(AF_INET6, "3ffa::", &DestinationIpv6);
    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));

    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    usleep(1000 * 60);
    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_NEWROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET6;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_protocol = RTPROT_BOOT;
    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
    Request.msg.rtm_type = RTN_UNICAST;
    inet_pton(AF_INET6, "3ffa::", &DestinationIpv6);
    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));

    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    //
    // Now test RTM_DELROUTE!
    // Now, we have the following routing entries for the loopback adapter:
    // Destination    Gateway
    // 1.1.1.0/30     1.1.1.1
    // 1.1.1.0/31     1.1.1.1
    // 1.1.1.0/32     1.1.1.1
    // 1.1.1.1        on-link (zero)
    // 1.1.1.2        1.1.1.1
    // 1.1.1.3        on-link (zero)
    // 1.1.2          1.1.1.1
    // 1.3            1.1.1.1
    // === IPv6 ===
    // 3ffa::/31      on-link
    // 3ffa::/32      on-link
    //

    //
    // Try to delete the routing entry with destination 1.1.1.1 and
    // gateway 1.2.3.4, which does not exist, returning ESRCH.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_DELROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;
    inet_aton("1.1.1.1", &DestinationIpv4);
    inet_aton("1.2.3.4", &GatewayIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ESRCH));

    //
    // Passing 0 flags results in no response.
    //

    Request.nlh.nlmsg_flags = 0;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Try to delete the routing entry with destination 1.1.1.1 and
    // gateway 0.0.0.0, which should succeed.
    // Note that as long as the NLM_F_REQUEST flag is present, it does
    // not matter what the rest of the flags are.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_DELROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;
    inet_aton("1.1.1.1", &DestinationIpv4);
    inet_aton("0.0.0.0", &GatewayIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Try to delete the routing entry with destination 1.1.1.2 and
    // gateway 1.2.3.4, which does not exist, returning ESRCH.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_DELROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;
    inet_aton("1.1.1.2", &DestinationIpv4);
    inet_aton("1.2.3.4", &GatewayIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ESRCH));

    //
    // Try to delete the routing entry with destination 1.1.1.2 and
    // gateway 0.0.0.0, which should succeed.
    // Note that even though the gateway is 1.1.1.1, specifying
    // 0.0.0.0 in the request specifies a wildcard gateway.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_DELROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;
    inet_aton("1.1.1.2", &DestinationIpv4);
    inet_aton("0.0.0.0", &GatewayIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Test giving an invalid interface index, which should fail.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_DELROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK | 0x4321;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;
    inet_aton("1.1.1.3", &DestinationIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, 90000);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ESRCH));

    //
    // Try to delete the routing entry with destination 1.1.1.3 and
    // no gateway specified, which should succeed.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_DELROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK | 0x4321;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;
    inet_aton("1.1.1.3", &DestinationIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    //
    // Try to delete the routing entry with destination 1.1.2 and
    // no gateway specified, which should succeed.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_DELROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK | 0x4321;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;
    inet_aton("1.1.2", &DestinationIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

    //
    // Try to delete the routing entry with destination 1.3 and
    // gateway 1.1.1.1 specified, which should succeed.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_DELROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;
    inet_aton("1.3", &DestinationIpv4);
    inet_aton("1.1.1.1", &GatewayIpv4);
    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_GATEWAY, &GatewayIpv4, sizeof(GatewayIpv4)));

    //
    // First try sending incomplete information (only sending the gateway
    // and nothing else), which should fail.
    //

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ESRCH));

    //
    // Now try the actual delete.
    //

    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv4, sizeof(DestinationIpv4)));

    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Try to delete the routing entries with destination 1.1.1.0 and prefix
    // lengths 29, 30, 31 and 32. Only 29 should fail and the rest should succeed.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_DELROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET;
    Request.msg.rtm_dst_len = 29;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;
    inet_aton("1.1.1.0", &DestinationIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, LoopbackIndex);

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ESRCH));

    Request.msg.rtm_dst_len = 30;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.msg.rtm_dst_len = 31;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    Request.msg.rtm_dst_len = 32;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Try to delete the routing entry with destination 3ffa:: and prefix length 31.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_DELROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET6;
    Request.msg.rtm_dst_len = 31;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;
    inet_pton(AF_INET6, "3ffa::", &DestinationIpv6);
    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));

    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);

    //
    // Try to delete the routing entry with destination 3ffa:: and prefix length 32.
    //

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_DELROUTE;
    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK | 0x4321;
    Request.nlh.nlmsg_seq = 0x4567;
    Request.msg.rtm_family = AF_INET6;
    Request.msg.rtm_dst_len = 32;
    Request.msg.rtm_table = RT_TABLE_MAIN;
    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;
    inet_pton(AF_INET6, "3ffa::", &DestinationIpv6);
    inet_pton(AF_INET6, "0::", &GatewayIpv6);
    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));

    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_GATEWAY, &GatewayIpv6, sizeof(GatewayIpv6)));

    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}

int SocketNetlinkSendAndWaitForExpectedError(int Socket, void* Request, int RequestSize, int ExpectedError)

/*++

Routine Description:

    This routine repeatedly sends a request and receives the Netlink error
    response until the Netlink error value matches the passed in expected value.

Arguments:

    Socket - Supplies the socket to send and receive the response from.

    Request -  Supplies the Netlink request to send.

    RequestSize - Supplies the size of the Netlink request.

    ExpectedError - Supplies the Netlink error to check for.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    struct nlmsgerr* Error;
    char ReceiveBuffer[5000];
    struct nlmsghdr* ReceiveHeader;
    int ReceiveResult;
    int Result;
    int Retries;

    Retries = 0;
    while (Retries < 20)
    {
        Retries += 1;
        LxtCheckErrno(sendto(Socket, Request, RequestSize, 0, NULL, 0));
        LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
        ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;
        LxtCheckTrue(NLMSG_OK(ReceiveHeader, ReceiveResult));
        LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, "%d");
        Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveHeader);
        if (Error->error == ExpectedError)
        {
            break;
        }
        else
        {
            LxtLogInfo("Error is: %d, waiting for it to become %d.", Error->error, ExpectedError);
        }

        usleep(1000 * 1000);
    }

    LxtCheckEqual(Error->error, ExpectedError, "%d");
    Result = LXT_RESULT_SUCCESS;

ErrorExit:
    return Result;
}

int SocketNetlinkSetAndVerifySocketOptionTimeout(int Socket, int SocketOption, int Usec)

/*++

Routine Description:

    This routine sets a timeout socket option and reads it back to verify.

Arguments:

    Socket - Supplies the socket to set the socket option on.

    SocketOption -  Supplies the timeout-related socket option to set.

    Usec - Supplies the number of microseconds for the timeout.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t OptionLength;
    int Result;
    struct timeval Timeout;

    OptionLength = sizeof(Timeout);
    Timeout.tv_sec = 0;
    Timeout.tv_usec = Usec;
    LxtCheckErrno(setsockopt(Socket, SOL_SOCKET, SocketOption, &Timeout, OptionLength));

    memset(&Timeout, 0, sizeof(Timeout));
    LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SocketOption, &Timeout, &OptionLength));

    LxtCheckEqual(OptionLength, sizeof(Timeout), "%d");
    LxtCheckEqual(Timeout.tv_sec, 0, "%d");
    LxtCheckEqual(Timeout.tv_usec, Usec, "%d");
    Result = LXT_RESULT_SUCCESS;

ErrorExit:
    return Result;
}

int SocketNetlinkSoPasscred(PLXT_ARGS Args)

/*++

Routine Description:

    This routine tests for the SO_PASSCRED socket option.

Arguments:

    Args - Supplies the command line arguments.

Return Value:

    Returns 0 on success, -1 on failure.

--*/

{

    socklen_t AddressLength;
    struct sockaddr_nl BindAddress;
    unsigned char ControlBuffer[CMSG_SPACE(sizeof(struct ucred))];
    struct cmsghdr* ControlMessage;
    struct in_addr DestinationIpv4;
    struct iovec IoVector;
    struct msghdr MessageHeader = {0};
    int PassCredentials;
    char ReceiveBuffer[5000];
    int Result;
    int Socket;

    struct
    {
        struct nlmsghdr nlh;
        struct rtmsg msg;
        char attr[200];
    } Request;

    Socket = -1;

    //
    // Create and bind socket. Create a RTM_GETROUTE request.
    //

    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));
    memset(&BindAddress, 0, sizeof(BindAddress));
    BindAddress.nl_family = AF_NETLINK;
    AddressLength = sizeof(BindAddress);
    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));

    //
    // Enable SO_PASSCRED on the socket.
    //

    PassCredentials = 1;
    LxtCheckErrno(setsockopt(Socket, SOL_SOCKET, SO_PASSCRED, &PassCredentials, sizeof(PassCredentials)));

    memset(&Request, 0, sizeof(Request));
    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    Request.nlh.nlmsg_type = RTM_GETROUTE;
    Request.nlh.nlmsg_seq = 0x4567;

    //
    // NLM_F_REQUEST with no NLM_F_DUMP means "get best route" request.
    //

    Request.nlh.nlmsg_flags = NLM_F_REQUEST;

    //
    // Specify the destination (Ipv4).
    //

    inet_aton("1.1.1.1", &DestinationIpv4);
    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, -1);

    Request.msg.rtm_family = AF_INET;
    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));

    //
    // Craft the message header to receive the data + ancillary data.
    //

    memset(&MessageHeader, 0, sizeof(MessageHeader));
    MessageHeader.msg_iov = &IoVector;
    MessageHeader.msg_iovlen = 1;
    MessageHeader.msg_control = ControlBuffer;
    MessageHeader.msg_controllen = sizeof(ControlBuffer);
    IoVector.iov_base = ReceiveBuffer;
    IoVector.iov_len = sizeof(ReceiveBuffer);
    memset(ControlBuffer, 0, sizeof(ControlBuffer));
    LxtCheckErrno(Result = recvmsg(Socket, &MessageHeader, 0));
    LxtCheckGreater(Result, 0, "%d");
    LxtCheckEqual(MessageHeader.msg_controllen, sizeof(ControlBuffer), "%d");
    LxtCheckEqual(MessageHeader.msg_flags, 0, "%d");

    //
    // Validate that SMC_CREDENTIALS control message is present and has valid
    // values.
    //

    ControlMessage = SocketGetControlMessage(&MessageHeader, NULL, SOL_SOCKET, SCM_CREDENTIALS);

    LxtCheckNotEqual(ControlMessage, NULL, "%p");
    LxtCheckEqual(ControlMessage->cmsg_len, CMSG_LEN(sizeof(struct ucred)), "%d");

    LxtCheckAncillaryCredentials(ControlMessage, 0, 0, 0);

    //
    // Pass NULL as the control buffer with invalid size.
    //

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    memset(&MessageHeader, 0, sizeof(MessageHeader));
    MessageHeader.msg_iov = &IoVector;
    MessageHeader.msg_iovlen = 1;
    MessageHeader.msg_control = NULL;
    MessageHeader.msg_controllen = sizeof(ControlBuffer);
    IoVector.iov_base = ReceiveBuffer;
    IoVector.iov_len = sizeof(ReceiveBuffer);
    LxtCheckErrno(Result = recvmsg(Socket, &MessageHeader, 0));
    LxtCheckGreater(Result, 0, "%d");
    LxtCheckEqual(MessageHeader.msg_controllen, 0, "%d");

    //
    // Since the control buffer was not big enough (NULL) to hold the control
    // message, proper (truncate) flags should be set.
    //

    LxtCheckEqual(MessageHeader.msg_flags, MSG_CTRUNC, "%d");

    //
    // Pass a control buffer smaller than the control message header.
    //

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    memset(&MessageHeader, 0, sizeof(MessageHeader));
    MessageHeader.msg_iov = &IoVector;
    MessageHeader.msg_iovlen = 1;
    MessageHeader.msg_control = NULL;
    MessageHeader.msg_controllen = sizeof(struct cmsghdr) - 1;
    IoVector.iov_base = ReceiveBuffer;
    IoVector.iov_len = sizeof(ReceiveBuffer);
    LxtCheckErrno(Result = recvmsg(Socket, &MessageHeader, 0));
    LxtCheckGreater(Result, 0, "%d");
    LxtCheckEqual(MessageHeader.msg_controllen, 0, "%d");

    //
    // Since the control buffer was not big enough (NULL) to hold the control
    // message, proper (truncate) flags should be set.
    //

    LxtCheckEqual(MessageHeader.msg_flags, MSG_CTRUNC, "%d");

    //
    // Pass a NULL message header.
    //

    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));
    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));
    LxtCheckErrno(Result = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));
    LxtCheckGreater(Result, 0, "%d");

    //
    // Check for general set/get of the SO_PASSCRED socket option.
    //
    // N.B After this routine, the state of the 'SO_PASSCRED' socket option in
    //     the socket is not guaranteed.
    //

    LxtCheckErrno(SocketGetSetBooleanSocketOption(Socket, SOL_SOCKET, SO_PASSCRED, false));

ErrorExit:
    if (Socket > 0)
    {
        close(Socket);
    }

    return Result;
}
