mirror of
https://github.com/microsoft/WSL.git
synced 2025-07-03 23:33:21 +00:00
5395 lines
185 KiB
C
5395 lines
185 KiB
C
![]() |
/*++
|
||
|
|
||
|
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 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, can not 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 binded 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 unqiue 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;
|
||
|
|
||
|
//
|
||
|
// Setup 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. Setup 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 retured %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 gauranteed.
|
||
|
//
|
||
|
|
||
|
LxtCheckErrno(SocketGetSetBooleanSocketOption(Socket, SOL_SOCKET, SO_PASSCRED, false));
|
||
|
|
||
|
ErrorExit:
|
||
|
if (Socket > 0)
|
||
|
{
|
||
|
close(Socket);
|
||
|
}
|
||
|
|
||
|
return Result;
|
||
|
}
|