/*++ Copyright (c) 2015 Minoca Corp. All Rights Reserved Module Name: generic.c Abstract: This module implements the generic netlink socket protocol. Author: Chris Stevens 9-Feb-2016 Environment: Kernel --*/ // // ------------------------------------------------------------------- Includes // // // Protocol drivers are supposed to be able to stand on their own (ie be able to // be implemented outside the core net library). For the builtin ones, avoid // including netcore.h, but still redefine those functions that would otherwise // generate imports. // #define NET_API __DLLEXPORT #include #include #include #include "generic.h" // // ---------------------------------------------------------------- Definitions // // // Define the maximum supported packet size of the generic netlink protocol, // including the message headers. // #define NETLINK_GENERIC_MAX_PACKET_SIZE \ (NETLINK_MAX_PACKET_SIZE - sizeof(NETLINK_HEADER)) // // Define the default size of the generic netlink's receive data buffer, in // bytes. // #define NETLINK_GENERIC_DEFAULT_RECEIVE_BUFFER_SIZE (256 * _1KB) // // Define the minimum receive buffer size. // #define NETLINK_GENERIC_MIN_RECEIVE_BUFFER_SIZE _2KB // // Define the default minimum number of bytes necessary for the generic netlink // socket to become readable. // #define NETLINK_GENERIC_DEFAULT_RECEIVE_MINIMUM 1 // // Define the minmum number of bytes necessary for generic netlink sockets to // become writable. There is no minimum and bytes are immediately sent on the // wire. // #define NETLINK_GENERIC_SEND_MINIMUM 1 // // ------------------------------------------------------ Data Type Definitions // /*++ Structure Description: This structure defines a generic netlink socket. Members: NetlinkSocket - Stores the common netlink socket information. ReceivedPacketList - Stores the list of packets ready to be read by the user. ReceiveLock - Stores the lock that protects the received packets list, dropped packet count, and various receive buffer parameters. This lock must always be acquired at low level. ReceiveBufferTotalSize - Stores the total size of the receive buffer, in bytes. Packets that are received but will not fit in the buffer are discarded. ReceiveBufferFreeSize - Stores the receive buffer's free space, in bytes. Packets that are received but do not fit in the free space are discarded. ReceiveTimeout - Stores the maximum amount of time, in milliseconds, that the socket will wait when receiving data. ReceiveMinimum - Stores the minimum amount of bytes that must be available before the socket is made readable. This is ignored. DroppedPacketCount - Stores the number of packets that have been dropped because the receive queue was full. MaxPacketSize - Stores the maximum size of UDP datagrams. --*/ typedef struct _NETLINK_GENERIC_SOCKET { NETLINK_SOCKET NetlinkSocket; LIST_ENTRY ReceivedPacketList; PQUEUED_LOCK ReceiveLock; ULONG ReceiveBufferTotalSize; ULONG ReceiveBufferFreeSize; ULONG ReceiveTimeout; ULONG ReceiveMinimum; ULONG DroppedPacketCount; UINTN MaxPacketSize; } NETLINK_GENERIC_SOCKET, *PNETLINK_GENERIC_SOCKET; /*++ Structure Description: This structure defines a generic netlink received message. Members: ListEntry - Stores pointers to the next and previous packets. Address - Stores the network address where this data came from. NetPacket - Stores a pointer to the network packet buffer holding the data. --*/ typedef struct _NETLINK_GENERIC_RECEIVED_PACKET { LIST_ENTRY ListEntry; NETWORK_ADDRESS Address; PNET_PACKET_BUFFER NetPacket; } NETLINK_GENERIC_RECEIVED_PACKET, *PNETLINK_GENERIC_RECEIVED_PACKET; /*++ Structure Description: This structure defines a netlink generic socket option. Members: InformationType - Stores the information type for the socket option. Option - Stores the type-specific option identifier. Size - Stores the size of the option value, in bytes. SetAllowed - Stores a boolean indicating whether or not the option is allowed to be set. --*/ typedef struct _NETLINK_GENERIC_SOCKET_OPTION { SOCKET_INFORMATION_TYPE InformationType; UINTN Option; UINTN Size; BOOL SetAllowed; } NETLINK_GENERIC_SOCKET_OPTION, *PNETLINK_GENERIC_SOCKET_OPTION; // // ----------------------------------------------- Internal Function Prototypes // KSTATUS NetlinkpGenericCreateSocket ( PNET_PROTOCOL_ENTRY ProtocolEntry, PNET_NETWORK_ENTRY NetworkEntry, ULONG NetworkProtocol, PNET_SOCKET *NewSocket ); VOID NetlinkpGenericDestroySocket ( PNET_SOCKET Socket ); KSTATUS NetlinkpGenericBindToAddress ( PNET_SOCKET Socket, PNET_LINK Link, PNETWORK_ADDRESS Address ); KSTATUS NetlinkpGenericListen ( PNET_SOCKET Socket ); KSTATUS NetlinkpGenericAccept ( PNET_SOCKET Socket, PIO_HANDLE *NewConnectionSocket, PNETWORK_ADDRESS RemoteAddress ); KSTATUS NetlinkpGenericConnect ( PNET_SOCKET Socket, PNETWORK_ADDRESS Address ); KSTATUS NetlinkpGenericClose ( PNET_SOCKET Socket ); KSTATUS NetlinkpGenericShutdown ( PNET_SOCKET Socket, ULONG ShutdownType ); KSTATUS NetlinkpGenericSend ( BOOL FromKernelMode, PNET_SOCKET Socket, PSOCKET_IO_PARAMETERS Parameters, PIO_BUFFER IoBuffer ); VOID NetlinkpGenericProcessReceivedData ( PNET_LINK Link, PNET_PACKET_BUFFER Packet, PNETWORK_ADDRESS SourceAddress, PNETWORK_ADDRESS DestinationAddress, PNET_PROTOCOL_ENTRY ProtocolEntry ); KSTATUS NetlinkpGenericProcessReceivedSocketData ( PNET_LINK Link, PNET_SOCKET Socket, PNET_PACKET_BUFFER Packet, PNETWORK_ADDRESS SourceAddress, PNETWORK_ADDRESS DestinationAddress ); KSTATUS NetlinkpGenericReceive ( BOOL FromKernelMode, PNET_SOCKET Socket, PSOCKET_IO_PARAMETERS Parameters, PIO_BUFFER IoBuffer ); KSTATUS NetlinkpGenericGetSetInformation ( PNET_SOCKET Socket, SOCKET_INFORMATION_TYPE InformationType, UINTN SocketOption, PVOID Data, PUINTN DataSize, BOOL Set ); KSTATUS NetlinkpGenericUserControl ( PNET_SOCKET Socket, ULONG CodeNumber, BOOL FromKernelMode, PVOID ContextBuffer, UINTN ContextBufferSize ); KSTATUS NetlinkpGenericJoinMulticastGroup ( PNET_SOCKET Socket, ULONG GroupId ); VOID NetlinkpGenericInsertReceivedPacket ( PNETLINK_GENERIC_SOCKET Socket, PNETLINK_GENERIC_RECEIVED_PACKET Packet ); KSTATUS NetlinkpGenericProcessReceivedKernelData ( PNET_LINK Link, PNET_SOCKET Socket, PNET_PACKET_BUFFER Packet, PNETWORK_ADDRESS SourceAddress, PNETWORK_ADDRESS DestinationAddress ); PNETLINK_GENERIC_FAMILY NetlinkpGenericLookupFamilyById ( ULONG MessageType ); COMPARISON_RESULT NetlinkpGenericCompareFamilies ( PRED_BLACK_TREE Tree, PRED_BLACK_TREE_NODE FirstNode, PRED_BLACK_TREE_NODE SecondNode ); VOID NetlinkpGenericDestroyFamily ( PNETLINK_GENERIC_FAMILY Family ); KSTATUS NetlinkpGenericAllocateFamilyId ( PULONG FamilyId ); KSTATUS NetlinkpGenericAllocateMulticastGroups ( PNETLINK_GENERIC_FAMILY Family ); VOID NetlinkpGenericFreeMulticastGroups ( PNETLINK_GENERIC_FAMILY Family ); KSTATUS NetlinkpGenericValidateMulticastGroup ( ULONG GroupId, BOOL LockHeld ); // // -------------------------------------------------------------------- Globals // NET_PROTOCOL_ENTRY NetlinkGenericProtocol = { {NULL, NULL}, NetSocketDatagram, SOCKET_INTERNET_PROTOCOL_NETLINK_GENERIC, NULL, NULL, {{0}, {0}, {0}}, { NetlinkpGenericCreateSocket, NetlinkpGenericDestroySocket, NetlinkpGenericBindToAddress, NetlinkpGenericListen, NetlinkpGenericAccept, NetlinkpGenericConnect, NetlinkpGenericClose, NetlinkpGenericShutdown, NetlinkpGenericSend, NetlinkpGenericProcessReceivedData, NetlinkpGenericProcessReceivedSocketData, NetlinkpGenericReceive, NetlinkpGenericGetSetInformation, NetlinkpGenericUserControl } }; NETLINK_GENERIC_SOCKET_OPTION NetlinkGenericSocketOptions[] = { { SocketInformationBasic, SocketBasicOptionSendBufferSize, sizeof(ULONG), TRUE }, { SocketInformationBasic, SocketBasicOptionSendMinimum, sizeof(ULONG), FALSE }, { SocketInformationBasic, SocketBasicOptionReceiveBufferSize, sizeof(ULONG), TRUE }, { SocketInformationBasic, SocketBasicOptionReceiveMinimum, sizeof(ULONG), TRUE }, { SocketInformationBasic, SocketBasicOptionReceiveTimeout, sizeof(SOCKET_TIME), TRUE }, }; NETLINK_PROTOCOL_INTERFACE NetlinkGenericProtocolInterface = { NetlinkpGenericJoinMulticastGroup }; PIO_HANDLE NetlinkGenericSocketHandle; PNET_SOCKET NetlinkGenericSocket; // // Store the lock and tree for storing the generic netlink families. // PSHARED_EXCLUSIVE_LOCK NetlinkGenericFamilyLock; RED_BLACK_TREE NetlinkGenericFamilyTree; // // Store the next generic family message type to allocate. // ULONG NetlinkGenericFamilyNextId = NETLINK_MESSAGE_TYPE_PROTOCOL_MINIMUM; // // Store a pointer to the multicast group bitmap and its size, in bytes. // PULONG NetlinkGenericMulticastBitmap = NULL; ULONG NetlinkGenericMulticastBitmapSize = 0; // // ------------------------------------------------------------------ Functions // NETLINK_API KSTATUS NetlinkGenericRegisterFamily ( PNETLINK_GENERIC_FAMILY_PROPERTIES Properties, PNETLINK_GENERIC_FAMILY *Family ) /*++ Routine Description: This routine registers a generic netlink family with the generic netlink core. The core will route messages with a message type equal to the family's ID to the provided interface. Arguments: Properties - Supplies a pointer to the family properties. The netlink library will not reference this memory after the function returns, a copy will be made. Family - Supplies an optional pointer that receives a pointer to the registered family. Return Value: Status code. --*/ { ULONG AllocationSize; UCHAR Command; ULONG CommandSize; PNETLINK_GENERIC_FAMILY FoundFamily; PRED_BLACK_TREE_NODE FoundNode; PNETLINK_GENERIC_MULTICAST_GROUP Group; ULONG Index; BOOL LockHeld; BOOL Match; ULONG MulticastSize; PNETLINK_GENERIC_FAMILY NewFamily; ULONG NewId; KSTATUS Status; LockHeld = FALSE; NewFamily = NULL; if (Properties->Version < NETLINK_GENERIC_FAMILY_PROPERTIES_VERSION) { Status = STATUS_VERSION_MISMATCH; goto RegisterFamilyEnd; } if ((Properties->CommandCount == 0) || (Properties->Commands == NULL)) { Status = STATUS_INVALID_PARAMETER; goto RegisterFamilyEnd; } if ((Properties->NameLength == 1) || (Properties->NameLength > NETLINK_GENERIC_MAX_FAMILY_NAME_LENGTH)) { Status = STATUS_INVALID_PARAMETER; goto RegisterFamilyEnd; } if ((Properties->Id < NETLINK_MESSAGE_TYPE_PROTOCOL_MINIMUM) && (Properties->Id != 0)) { Status = STATUS_INVALID_PARAMETER; goto RegisterFamilyEnd; } // // Allocate and initialize the new generic netlink family. // CommandSize = Properties->CommandCount * sizeof(NETLINK_GENERIC_COMMAND); MulticastSize = Properties->MulticastGroupCount * sizeof(NETLINK_GENERIC_MULTICAST_GROUP); AllocationSize = sizeof(NETLINK_GENERIC_FAMILY) + CommandSize + MulticastSize; NewFamily = MmAllocatePagedPool(AllocationSize, NETLINK_GENERIC_ALLOCATION_TAG); if (NewFamily == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto RegisterFamilyEnd; } RtlZeroMemory(NewFamily, sizeof(NETLINK_GENERIC_FAMILY)); NewFamily->ReferenceCount = 1; RtlCopyMemory(&(NewFamily->Properties), Properties, sizeof(NETLINK_GENERIC_FAMILY_PROPERTIES)); NewFamily->Properties.Commands = (PNETLINK_GENERIC_COMMAND)(NewFamily + 1); RtlCopyMemory(NewFamily->Properties.Commands, Properties->Commands, CommandSize); NewFamily->Properties.MulticastGroups = (PVOID)(NewFamily + 1) + CommandSize; RtlCopyMemory(NewFamily->Properties.MulticastGroups, Properties->MulticastGroups, MulticastSize); // // Acquire the family tree lock and attempt to insert this new family. // KeAcquireSharedExclusiveLockExclusive(NetlinkGenericFamilyLock); LockHeld = TRUE; // // Check to make sure the name is not a duplicate. // FoundNode = RtlRedBlackTreeGetLowestNode(&NetlinkGenericFamilyTree); while (FoundNode != NULL) { FoundFamily = RED_BLACK_TREE_VALUE(FoundNode, NETLINK_GENERIC_FAMILY, TreeNode); Match = RtlAreStringsEqual(FoundFamily->Properties.Name, NewFamily->Properties.Name, NETLINK_GENERIC_MAX_FAMILY_NAME_LENGTH); if (Match != FALSE) { Status = STATUS_DUPLICATE_ENTRY; goto RegisterFamilyEnd; } FoundNode = RtlRedBlackTreeGetNextNode(&NetlinkGenericFamilyTree, FALSE, FoundNode); } // // If the message type is zero, then one needs to be dynamically allocated. // if (NewFamily->Properties.Id == 0) { Status = NetlinkpGenericAllocateFamilyId(&NewId); if (!KSUCCESS(Status)) { goto RegisterFamilyEnd; } NewFamily->Properties.Id = NewId; // // Otherwise make sure the provided message type is not already in use. // } else { FoundNode = RtlRedBlackTreeSearch(&NetlinkGenericFamilyTree, &(NewFamily->TreeNode)); if (FoundNode != NULL) { Status = STATUS_DUPLICATE_ENTRY; goto RegisterFamilyEnd; } } // // If the family has multicast groups, allocate a region. // if (NewFamily->Properties.MulticastGroupCount != 0) { Status = NetlinkpGenericAllocateMulticastGroups(NewFamily); if (!KSUCCESS(Status)) { goto RegisterFamilyEnd; } } // // Insert the new family into the tree. // RtlRedBlackTreeInsert(&NetlinkGenericFamilyTree, &(NewFamily->TreeNode)); KeReleaseSharedExclusiveLockExclusive(NetlinkGenericFamilyLock); LockHeld = FALSE; Status = STATUS_SUCCESS; // // Blast out some notifications. // Command = NETLINK_CONTROL_COMMAND_NEW_FAMILY; NetlinkpGenericControlSendNotification(NewFamily, Command, NULL); for (Index = 0; Index < NewFamily->Properties.MulticastGroupCount; Index += 1) { Command = NETLINK_CONTROL_COMMAND_NEW_MULTICAST_GROUP; Group = &(NewFamily->Properties.MulticastGroups[Index]); NetlinkpGenericControlSendNotification(NewFamily, Command, Group); } RegisterFamilyEnd: if (LockHeld != FALSE) { KeReleaseSharedExclusiveLockExclusive(NetlinkGenericFamilyLock); } if (!KSUCCESS(Status)) { if (NewFamily != INVALID_HANDLE) { NetlinkpGenericFamilyReleaseReference(NewFamily); NewFamily = INVALID_HANDLE; } } if (Family != NULL) { *Family = NewFamily; } return Status; } NETLINK_API VOID NetlinkGenericUnregisterFamily ( PNETLINK_GENERIC_FAMILY Family ) /*++ Routine Description: This routine unregisters the given generic netlink family. Arguments: Family - Supplies a pointer to the generic netlink family to unregister. Return Value: None. --*/ { UCHAR Command; PNETLINK_GENERIC_FAMILY FoundFamily; PRED_BLACK_TREE_NODE FoundNode; BOOL LockHeld; KeAcquireSharedExclusiveLockExclusive(NetlinkGenericFamilyLock); LockHeld = TRUE; FoundNode = RtlRedBlackTreeSearch(&NetlinkGenericFamilyTree, &(Family->TreeNode)); if (FoundNode == NULL) { goto UnregisterFamilyEnd; } FoundFamily = RED_BLACK_TREE_VALUE(FoundNode, NETLINK_GENERIC_FAMILY, TreeNode); if (FoundFamily != Family) { goto UnregisterFamilyEnd; } RtlRedBlackTreeRemove(&NetlinkGenericFamilyTree, &(Family->TreeNode)); // // If the family had allocated multicast groups, then release them now. // if ((Family->Properties.MulticastGroupCount != 0) && (Family->MulticastGroupOffset != 0)) { NetlinkpGenericFreeMulticastGroups(Family); } KeReleaseSharedExclusiveLockExclusive(NetlinkGenericFamilyLock); LockHeld = FALSE; // // Before releasing the last reference, make sure the family is not in the // middle of receiving a packet. If it is, netcore could be able to call // into the driver that is unregistering the family. It would be bad for // that driver to disappear. // while (Family->ReferenceCount > 1) { KeYield(); } Command = NETLINK_CONTROL_COMMAND_DELETE_FAMILY; NetlinkpGenericControlSendNotification(Family, Command, NULL); NetlinkpGenericFamilyReleaseReference(Family); UnregisterFamilyEnd: if (LockHeld != FALSE) { KeReleaseSharedExclusiveLockExclusive(NetlinkGenericFamilyLock); } return; } NETLINK_API KSTATUS NetlinkGenericSendCommand ( PNETLINK_GENERIC_FAMILY Family, PNET_PACKET_BUFFER Packet, PNETWORK_ADDRESS DestinationAddress ) /*++ Routine Description: This routine sends a generic netlink command. The generic header should already be filled out. Arguments: Family - Supplies a pointer to the generic netlink family sending the command. Packet - Supplies a pointer to the network packet to be sent. DestinationAddress - Supplies a pointer to the destination address to which the command will be sent. Return Value: Status code. --*/ { KSTATUS Status; Status = NetlinkSendMessage(NetlinkGenericSocket, Packet, DestinationAddress); return Status; } NETLINK_API KSTATUS NetlinkGenericSendMulticastCommand ( PNETLINK_GENERIC_FAMILY Family, PNET_PACKET_BUFFER Packet, ULONG GroupId ) /*++ Routine Description: This routine multicasts the given command packet to the specified group. The packet should already store the completed command, ready to send. Arguments: Family - Supplies a pointer to the generic netlink family sending the multicast command. Packet - Supplies a pointer to the network packet to be sent. GroupId - Supplies the family's multicast group ID over which to send the command. Return Value: Status code. --*/ { PNETWORK_ADDRESS Destination; NETLINK_ADDRESS NetlinkDestination; // // The destination is based on the group ID, which must be adjusted by the // offset allocated when the family as registered. The caller does not know // about this offset. // NetlinkDestination.Domain = NetDomainNetlink; NetlinkDestination.Port = 0; NetlinkDestination.Group = Family->MulticastGroupOffset + GroupId; Destination = (PNETWORK_ADDRESS)&NetlinkDestination; return NetlinkGenericSendCommand(Family, Packet, Destination); } NETLINK_API KSTATUS NetlinkGenericAppendHeaders ( PNETLINK_GENERIC_FAMILY Family, PNET_PACKET_BUFFER Packet, ULONG Length, ULONG SequenceNumber, USHORT Flags, UCHAR Command, UCHAR Version ) /*++ Routine Description: This routine appends the base and generic netlink headers to the given packet, validating that there is enough space remaining in the buffer and moving the data offset forward to the first byte after the headers once they have been added. Arguments: Family - Supplies a pointer to the netlink generic family to which the packet belongs. Packet - Supplies a pointer to the network packet to which the headers will be appended. Length - Supplies the length of the generic command payload, not including any headers. SequenceNumber - Supplies the desired sequence number for the netlink message. Flags - Supplies a bitmask of netlink message flags to be set. See NETLINK_HEADER_FLAG_* for definitions. Command - Supplies the generic netlink command to bet set in the header. Version - Supplies the version number of the command. Return Value: Status code. --*/ { PNETLINK_GENERIC_HEADER Header; ULONG PacketLength; KSTATUS Status; Length += NETLINK_GENERIC_HEADER_LENGTH; Status = NetlinkAppendHeader(NetlinkGenericSocket, Packet, Length, SequenceNumber, Family->Properties.Id, Flags); if (!KSUCCESS(Status)) { goto AppendHeadersEnd; } PacketLength = Packet->FooterOffset - Packet->DataOffset; if (PacketLength < Length) { Status = STATUS_BUFFER_TOO_SMALL; goto AppendHeadersEnd; } Header = Packet->Buffer + Packet->DataOffset; Header->Command = Command; Header->Version = Version; Packet->DataOffset += NETLINK_GENERIC_HEADER_LENGTH; Header->Reserved = 0; Status = STATUS_SUCCESS; AppendHeadersEnd: return Status; } VOID NetpNetlinkGenericInitialize ( ULONG Phase ) /*++ Routine Description: This routine initializes support for generic netlink sockets. Arguments: Phase - Supplies the phase of the initialization. Phase 0 happens before the networking core registers with the kernel, meaning sockets cannot be created. Phase 1 happens after the networking core has registered with the kernel allowing socket creation. Return Value: None. --*/ { NETLINK_ADDRESS Address; KSTATUS Status; // // In phase 0, register the generic netlink socket handlers with the core // networking library so that it is ready to go when netcore registers with // the kernel. // if (Phase == 0) { NetlinkGenericFamilyLock = KeCreateSharedExclusiveLock(); if (NetlinkGenericFamilyLock == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeEnd; } RtlRedBlackTreeInitialize(&NetlinkGenericFamilyTree, 0, NetlinkpGenericCompareFamilies); Status = NetRegisterProtocol(&NetlinkGenericProtocol, NULL); if (!KSUCCESS(Status)) { goto InitializeEnd; } // // In phase 1, create netcore's kernel-side generic netlink socket. // } else { ASSERT(Phase == 1); Status = IoSocketCreate(NetDomainNetlink, NetSocketDatagram, SOCKET_INTERNET_PROTOCOL_NETLINK_GENERIC, 0, &NetlinkGenericSocketHandle); if (!KSUCCESS(Status)) { goto InitializeEnd; } Status = IoGetSocketFromHandle(NetlinkGenericSocketHandle, (PVOID)&NetlinkGenericSocket); if (!KSUCCESS(Status)) { goto InitializeEnd; } // // Add the kernel flag and bind it to port 0. // RtlAtomicOr32(&(NetlinkGenericSocket->Flags), NET_SOCKET_FLAG_KERNEL); RtlZeroMemory(&Address, sizeof(NETLINK_ADDRESS)); Address.Domain = NetDomainNetlink; Status = IoSocketBindToAddress(TRUE, NetlinkGenericSocketHandle, NULL, &(Address.NetworkAddress), NULL, 0); if (!KSUCCESS(Status)) { goto InitializeEnd; } NetlinkpGenericControlInitialize(); } InitializeEnd: ASSERT(KSUCCESS(Status)); return; } KSTATUS NetlinkpGenericCreateSocket ( PNET_PROTOCOL_ENTRY ProtocolEntry, PNET_NETWORK_ENTRY NetworkEntry, ULONG NetworkProtocol, PNET_SOCKET *NewSocket ) /*++ Routine Description: This routine allocates resources associated with a new socket. The protocol driver is responsible for allocating the structure (with additional length for any of its context). The core networking library will fill in the common header when this routine returns. Arguments: ProtocolEntry - Supplies a pointer to the protocol information. NetworkEntry - Supplies a pointer to the network information. NetworkProtocol - Supplies the raw protocol value for this socket used on the network. This value is network specific. NewSocket - Supplies a pointer where a pointer to a newly allocated socket structure will be returned. The caller is responsible for allocating the socket (and potentially a larger structure for its own context). The core network library will fill in the standard socket structure after this routine returns. Return Value: Status code. --*/ { PNETLINK_GENERIC_SOCKET GenericSocket; PNET_NETWORK_INITIALIZE_SOCKET InitializeSocket; ULONG MaxPacketSize; PNET_SOCKET NetSocket; PNET_PACKET_SIZE_INFORMATION PacketSizeInformation; KSTATUS Status; ASSERT(ProtocolEntry->Type == NetSocketDatagram); ASSERT(NetworkProtocol == ProtocolEntry->ParentProtocolNumber); ASSERT(NetworkProtocol == SOCKET_INTERNET_PROTOCOL_NETLINK_GENERIC); NetSocket = NULL; GenericSocket = MmAllocatePagedPool(sizeof(NETLINK_GENERIC_SOCKET), NETLINK_GENERIC_ALLOCATION_TAG); if (GenericSocket == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto GenericCreateSocketEnd; } RtlZeroMemory(GenericSocket, sizeof(NETLINK_GENERIC_SOCKET)); NetSocket = &(GenericSocket->NetlinkSocket.NetSocket); NetSocket->KernelSocket.Protocol = NetworkProtocol; NetSocket->KernelSocket.ReferenceCount = 1; RtlCopyMemory(&(GenericSocket->NetlinkSocket.ProtocolInterface), &NetlinkGenericProtocolInterface, sizeof(NETLINK_PROTOCOL_INTERFACE)); INITIALIZE_LIST_HEAD(&(GenericSocket->ReceivedPacketList)); GenericSocket->ReceiveTimeout = WAIT_TIME_INDEFINITE; GenericSocket->ReceiveBufferTotalSize = NETLINK_GENERIC_DEFAULT_RECEIVE_BUFFER_SIZE; GenericSocket->ReceiveBufferFreeSize = GenericSocket->ReceiveBufferTotalSize; GenericSocket->ReceiveMinimum = NETLINK_GENERIC_DEFAULT_RECEIVE_MINIMUM; GenericSocket->MaxPacketSize = NETLINK_GENERIC_MAX_PACKET_SIZE; GenericSocket->ReceiveLock = KeCreateQueuedLock(); if (GenericSocket->ReceiveLock == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto GenericCreateSocketEnd; } // // Give the lower layers a chance to initialize. Start the maximum packet // size at the largest possible value. // PacketSizeInformation = &(NetSocket->PacketSizeInformation); PacketSizeInformation->MaxPacketSize = MAX_ULONG; InitializeSocket = NetworkEntry->Interface.InitializeSocket; Status = InitializeSocket(ProtocolEntry, NetworkEntry, NetworkProtocol, NetSocket); if (!KSUCCESS(Status)) { goto GenericCreateSocketEnd; } // // If the max packet size is greater than what is allowed for a generic // netlink packet plus all the previous headers and footers, then truncate // the max packet size. Note that there is no additional header for generic // netlink packets. // MaxPacketSize = PacketSizeInformation->HeaderSize + NETLINK_GENERIC_MAX_PACKET_SIZE + PacketSizeInformation->FooterSize; if (PacketSizeInformation->MaxPacketSize > MaxPacketSize) { PacketSizeInformation->MaxPacketSize = MaxPacketSize; } PacketSizeInformation->HeaderSize += NETLINK_GENERIC_HEADER_LENGTH; Status = STATUS_SUCCESS; GenericCreateSocketEnd: if (!KSUCCESS(Status)) { if (GenericSocket != NULL) { if (GenericSocket->ReceiveLock != NULL) { KeDestroyQueuedLock(GenericSocket->ReceiveLock); } MmFreePagedPool(GenericSocket); GenericSocket = NULL; NetSocket = NULL; } } *NewSocket = NetSocket; return Status; } VOID NetlinkpGenericDestroySocket ( PNET_SOCKET Socket ) /*++ Routine Description: This routine destroys resources associated with an open socket, officially marking the end of the kernel and core networking library's knowledge of this structure. Arguments: Socket - Supplies a pointer to the socket to destroy. The core networking library will have already destroyed any resources inside the common header, the protocol should not reach through any pointers inside the socket header except the protocol and network entries. Return Value: None. This routine is responsible for freeing the memory associated with the socket structure itself. --*/ { PNETLINK_GENERIC_SOCKET GenericSocket; PNETLINK_GENERIC_RECEIVED_PACKET Packet; GenericSocket = (PNETLINK_GENERIC_SOCKET)Socket; // // Loop through and free any leftover packets. // KeAcquireQueuedLock(GenericSocket->ReceiveLock); while (!LIST_EMPTY(&(GenericSocket->ReceivedPacketList))) { Packet = LIST_VALUE(GenericSocket->ReceivedPacketList.Next, NETLINK_GENERIC_RECEIVED_PACKET, ListEntry); LIST_REMOVE(&(Packet->ListEntry)); GenericSocket->ReceiveBufferFreeSize += Packet->NetPacket->DataSize; NetFreeBuffer(Packet->NetPacket); MmFreePagedPool(Packet); } ASSERT(GenericSocket->ReceiveBufferFreeSize == GenericSocket->ReceiveBufferTotalSize); KeReleaseQueuedLock(GenericSocket->ReceiveLock); KeDestroyQueuedLock(GenericSocket->ReceiveLock); MmFreePagedPool(GenericSocket); return; } KSTATUS NetlinkpGenericBindToAddress ( PNET_SOCKET Socket, PNET_LINK Link, PNETWORK_ADDRESS Address ) /*++ Routine Description: This routine binds the given socket to the specified network address. Usually this is a no-op for the protocol, it's simply responsible for passing the request down to the network layer. Arguments: Socket - Supplies a pointer to the socket to bind. Link - Supplies an optional pointer to a link to bind to. Address - Supplies a pointer to the address to bind the socket to. Return Value: Status code. --*/ { BOOL LockHeld; KSTATUS Status; LockHeld = FALSE; // // Netlink sockets are allowed to be rebound to different multicast groups. // if ((Socket->LocalAddress.Domain != NetDomainInvalid) && (Socket->LocalAddress.Port != Address->Port)) { Status = STATUS_INVALID_PARAMETER; goto GenericBindToAddressEnd; } // // Only netlink addresses are supported. // if (Address->Domain != NetDomainNetlink) { Status = STATUS_NOT_SUPPORTED; goto GenericBindToAddressEnd; } // // Pass the request down to the network layer. // Status = Socket->Network->Interface.BindToAddress(Socket, Link, Address); if (!KSUCCESS(Status)) { goto GenericBindToAddressEnd; } // // Begin listening immediately, as there is no explicit listen step for // generic netlink sockets. // Status = Socket->Network->Interface.Listen(Socket); if (!KSUCCESS(Status)) { goto GenericBindToAddressEnd; } IoSetIoObjectState(Socket->KernelSocket.IoState, POLL_EVENT_OUT, TRUE); GenericBindToAddressEnd: if (LockHeld != FALSE) { KeReleaseSharedExclusiveLockShared(NetlinkGenericFamilyLock); } return Status; } KSTATUS NetlinkpGenericListen ( PNET_SOCKET Socket ) /*++ Routine Description: This routine adds a bound socket to the list of listening sockets, officially allowing clients to attempt to connect to it. Arguments: Socket - Supplies a pointer to the socket to mark as listning. Return Value: Status code. --*/ { return STATUS_NOT_SUPPORTED; } KSTATUS NetlinkpGenericAccept ( PNET_SOCKET Socket, PIO_HANDLE *NewConnectionSocket, PNETWORK_ADDRESS RemoteAddress ) /*++ Routine Description: This routine accepts an incoming connection on a listening connection-based socket. Arguments: Socket - Supplies a pointer to the socket to accept a connection from. NewConnectionSocket - Supplies a pointer where a new socket will be returned that represents the accepted connection with the remote host. RemoteAddress - Supplies a pointer where the address of the connected remote host will be returned. Return Value: Status code. --*/ { return STATUS_NOT_SUPPORTED; } KSTATUS NetlinkpGenericConnect ( PNET_SOCKET Socket, PNETWORK_ADDRESS Address ) /*++ Routine Description: This routine attempts to make an outgoing connection to a server. Arguments: Socket - Supplies a pointer to the socket to use for the connection. Address - Supplies a pointer to the address to connect to. Return Value: Status code. --*/ { ULONG GroupId; PNETLINK_ADDRESS NetlinkAddress; KSTATUS Status; // // If there is a request to connect to a multicast group, then validate it. // NetlinkAddress = (PNETLINK_ADDRESS)Address; if (NetlinkAddress->Group != 0) { GroupId = NetlinkAddress->Group; Status = NetlinkpGenericValidateMulticastGroup(GroupId, FALSE); if (!KSUCCESS(Status)) { goto GenericConnectEnd; } } // // Pass the request down to the network layer. // Status = Socket->Network->Interface.Connect(Socket, Address); if (!KSUCCESS(Status)) { goto GenericConnectEnd; } IoSetIoObjectState(Socket->KernelSocket.IoState, POLL_EVENT_OUT, TRUE); GenericConnectEnd: return Status; } KSTATUS NetlinkpGenericClose ( PNET_SOCKET Socket ) /*++ Routine Description: This routine closes a socket connection. Arguments: Socket - Supplies a pointer to the socket to shut down. Return Value: Status code. --*/ { KSTATUS Status; // // Close it at the lower level and then release the reference taken on // create if the close was successful. // Status = Socket->Network->Interface.Close(Socket); if (!KSUCCESS(Status)) { goto GenericCloseEnd; } IoSocketReleaseReference(&(Socket->KernelSocket)); GenericCloseEnd: return Status; } KSTATUS NetlinkpGenericShutdown ( PNET_SOCKET Socket, ULONG ShutdownType ) /*++ Routine Description: This routine shuts down communication with a given socket. Arguments: Socket - Supplies a pointer to the socket. ShutdownType - Supplies the shutdown type to perform. See the SOCKET_SHUTDOWN_* definitions. Return Value: Status code. --*/ { return STATUS_NOT_SUPPORTED; } KSTATUS NetlinkpGenericSend ( BOOL FromKernelMode, PNET_SOCKET Socket, PSOCKET_IO_PARAMETERS Parameters, PIO_BUFFER IoBuffer ) /*++ Routine Description: This routine sends the given data buffer through the network using a specific protocol. Arguments: FromKernelMode - Supplies a boolean indicating whether the request is coming from kernel mode (TRUE) or user mode (FALSE). Socket - Supplies a pointer to the socket to send the data to. Parameters - Supplies a pointer to the socket I/O parameters. This will always be a kernel mode pointer. IoBuffer - Supplies a pointer to the I/O buffer containing the data to send. Return Value: Status code. --*/ { UINTN BytesComplete; PNETWORK_ADDRESS Destination; NETWORK_ADDRESS DestinationLocal; PNETLINK_GENERIC_SOCKET GenericSocket; NETWORK_ADDRESS LocalAddress; PNET_PACKET_BUFFER Packet; NET_PACKET_LIST PacketList; UINTN Size; KSTATUS Status; ASSERT(Socket->PacketSizeInformation.MaxPacketSize > sizeof(NETLINK_HEADER)); BytesComplete = 0; NET_INITIALIZE_PACKET_LIST(&PacketList); Size = Parameters->Size; GenericSocket = (PNETLINK_GENERIC_SOCKET)Socket; Parameters->SocketIoFlags = 0; Destination = Parameters->NetworkAddress; if ((Destination != NULL) && (FromKernelMode == FALSE)) { Status = MmCopyFromUserMode(&DestinationLocal, Destination, sizeof(NETWORK_ADDRESS)); Destination = &DestinationLocal; if (!KSUCCESS(Status)) { goto GenericSendEnd; } } if ((Destination == NULL) || (Destination->Domain == NetDomainInvalid)) { if (Socket->BindingType != SocketFullyBound) { Status = STATUS_NOT_CONFIGURED; goto GenericSendEnd; } Destination = &(Socket->RemoteAddress); } // // Fail if there's ancillary data. // if (Parameters->ControlDataSize != 0) { Status = STATUS_NOT_SUPPORTED; goto GenericSendEnd; } // // If the size is greater than the generic netlink socket's maximum packet // size, fail. // if (Size > GenericSocket->MaxPacketSize) { Status = STATUS_MESSAGE_TOO_LONG; goto GenericSendEnd; } // // If the socket is not yet bound, then at least try to bind it to a local // port. This bind attempt may race with another bind attempt, but leave it // to the socket owner to synchronize bind and send. // if (Socket->BindingType == SocketBindingInvalid) { RtlZeroMemory(&LocalAddress, sizeof(NETWORK_ADDRESS)); LocalAddress.Domain = Socket->Network->Domain; Status = NetlinkpGenericBindToAddress(Socket, NULL, &LocalAddress); if (!KSUCCESS(Status)) { goto GenericSendEnd; } } // // Allocate a buffer for the packet. // Status = NetAllocateBuffer(0, Size, 0, NULL, 0, &Packet); if (!KSUCCESS(Status)) { goto GenericSendEnd; } NET_ADD_PACKET_TO_LIST(Packet, &PacketList); // // Copy the data to the packet's buffer. // Status = MmCopyIoBufferData(IoBuffer, Packet->Buffer + Packet->DataOffset, BytesComplete, Size - BytesComplete, FALSE); if (!KSUCCESS(Status)) { goto GenericSendEnd; } // // Send the packet down to the network layer. // Status = Socket->Network->Interface.Send(Socket, Destination, NULL, &PacketList); if (!KSUCCESS(Status)) { goto GenericSendEnd; } Packet = NULL; BytesComplete = Size; GenericSendEnd: Parameters->Size = BytesComplete; if (!KSUCCESS(Status)) { NetDestroyBufferList(&PacketList); } return Status; } VOID NetlinkpGenericProcessReceivedData ( PNET_LINK Link, PNET_PACKET_BUFFER Packet, PNETWORK_ADDRESS SourceAddress, PNETWORK_ADDRESS DestinationAddress, PNET_PROTOCOL_ENTRY ProtocolEntry ) /*++ Routine Description: This routine is called to process a received packet. Arguments: Link - Supplies a pointer to the link that received the packet. Packet - Supplies a pointer to a structure describing the incoming packet. This structure may be used as a scratch space while this routine executes and the packet travels up the stack, but will not be accessed after this routine returns. SourceAddress - Supplies a pointer to the source (remote) address that the packet originated from. This memory will not be referenced once the function returns, it can be stack allocated. DestinationAddress - Supplies a pointer to the destination (local) address that the packet is heading to. This memory will not be referenced once the function returns, it can be stack allocated. ProtocolEntry - Supplies a pointer to this protocol's protocol entry. Return Value: None. --*/ { ASSERT(FALSE); return; } KSTATUS NetlinkpGenericProcessReceivedSocketData ( PNET_LINK Link, PNET_SOCKET Socket, PNET_PACKET_BUFFER Packet, PNETWORK_ADDRESS SourceAddress, PNETWORK_ADDRESS DestinationAddress ) /*++ Routine Description: This routine is called for a particular socket to process a received packet that was sent to it. Arguments: Link - Supplies a pointer to the network link that received the packet. Socket - Supplies a pointer to the socket that received the packet. Packet - Supplies a pointer to a structure describing the incoming packet. Use of this structure depends on its packet flags and the socket type. If it is a multicast packet or it was sent to a kernel socket, then it must not be modified. Otherwise this routine is responsible for the destruction of the packet. SourceAddress - Supplies a pointer to the source (remote) address that the packet originated from. This memory will not be referenced once the function returns, it can be stack allocated. DestinationAddress - Supplies a pointer to the destination (local) address that the packet is heading to. This memory will not be referenced once the function returns, it can be stack allocated. Return Value: Status code. --*/ { ULONG AllocationSize; PNETLINK_GENERIC_SOCKET GenericSocket; PNET_PACKET_BUFFER PacketCopy; ULONG PacketLength; PNETLINK_GENERIC_RECEIVED_PACKET ReceivePacket; KSTATUS Status; ASSERT(KeGetRunLevel() == RunLevelLow); PacketCopy = NULL; GenericSocket = (PNETLINK_GENERIC_SOCKET)Socket; // // If this is a kernel socket is on the receiving end, then route the // packet directly to the kernel component. // if ((Socket->Flags & NET_SOCKET_FLAG_KERNEL) != 0) { Status = NetlinkpGenericProcessReceivedKernelData(Link, Socket, Packet, SourceAddress, DestinationAddress); return Status; } // // Create a received packet entry for this data. This routine is invoked by // the network core on multicast packets. Create a copy of the network // packet as it may need to be sent to multiple sockets, no single socket // can own it. // AllocationSize = sizeof(NETLINK_GENERIC_RECEIVED_PACKET); ReceivePacket = MmAllocatePagedPool(AllocationSize, NETLINK_GENERIC_ALLOCATION_TAG); if (ReceivePacket == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ProcessReceivedSocketDataEnd; } RtlCopyMemory(&(ReceivePacket->Address), SourceAddress, sizeof(NETWORK_ADDRESS)); // // If the original packet is a multicast packet, then its services are // needed again by another socket. Make a copy and save that. // if ((Packet->Flags & NET_PACKET_FLAG_MULTICAST) != 0) { PacketLength = Packet->FooterOffset - Packet->DataOffset; Status = NetAllocateBuffer(0, PacketLength, 0, NULL, 0, &PacketCopy); if (!KSUCCESS(Status)) { goto ProcessReceivedSocketDataEnd; } RtlCopyMemory(PacketCopy->Buffer + PacketCopy->DataOffset, Packet->Buffer + Packet->DataOffset, PacketLength); ReceivePacket->NetPacket = PacketCopy; // // Otherwise set the packet directly in the generic receive packet. // } else { ReceivePacket->NetPacket = Packet; } // // Work to insert the packet on the list of received packets. // NetlinkpGenericInsertReceivedPacket(GenericSocket, ReceivePacket); ProcessReceivedSocketDataEnd: if (!KSUCCESS(Status)) { if (ReceivePacket != NULL) { MmFreePagedPool(ReceivePacket); } if (PacketCopy != NULL) { NetFreeBuffer(PacketCopy); } if ((Packet->Flags & NET_PACKET_FLAG_MULTICAST) == 0) { NetFreeBuffer(Packet); } } return Status; } KSTATUS NetlinkpGenericReceive ( BOOL FromKernelMode, PNET_SOCKET Socket, PSOCKET_IO_PARAMETERS Parameters, PIO_BUFFER IoBuffer ) /*++ Routine Description: This routine is called by the user to receive data from the socket on a particular protocol. Arguments: FromKernelMode - Supplies a boolean indicating whether the request is coming from kernel mode (TRUE) or user mode (FALSE). Socket - Supplies a pointer to the socket to receive data from. Parameters - Supplies a pointer to the socket I/O parameters. IoBuffer - Supplies a pointer to the I/O buffer where the received data will be returned. Return Value: STATUS_SUCCESS if any bytes were read. STATUS_TIMEOUT if the request timed out. STATUS_BUFFER_TOO_SMALL if the incoming datagram was too large for the buffer. The remainder of the datagram is discarded in this case. Other error codes on other failures. --*/ { PVOID Buffer; UINTN BytesComplete; ULONG CopySize; ULONGLONG CurrentTime; ULONGLONG EndTime; ULONG Flags; PNETLINK_GENERIC_SOCKET GenericSocket; BOOL LockHeld; PNETLINK_GENERIC_RECEIVED_PACKET Packet; PLIST_ENTRY PacketEntry; ULONG PacketSize; ULONG ReturnedEvents; ULONG ReturnSize; UINTN Size; KSTATUS Status; ULONGLONG TimeCounterFrequency; ULONG Timeout; ULONG WaitTime; ASSERT(KeGetRunLevel() == RunLevelLow); BytesComplete = 0; EndTime = 0; LockHeld = FALSE; Flags = Parameters->SocketIoFlags; Parameters->SocketIoFlags = 0; if ((Flags & SOCKET_IO_OUT_OF_BAND) != 0) { Status = STATUS_NOT_SUPPORTED; goto GenericReceiveEnd; } // // Fail if there's ancillary data. // if (Parameters->ControlDataSize != 0) { Status = STATUS_NOT_SUPPORTED; goto GenericReceiveEnd; } Packet = NULL; Size = Parameters->Size; TimeCounterFrequency = 0; Timeout = Parameters->TimeoutInMilliseconds; GenericSocket = (PNETLINK_GENERIC_SOCKET)Socket; // // Set a timeout timer to give up on. The socket stores the maximum timeout. // if (Timeout > GenericSocket->ReceiveTimeout) { Timeout = GenericSocket->ReceiveTimeout; } if ((Timeout != 0) && (Timeout != WAIT_TIME_INDEFINITE)) { EndTime = KeGetRecentTimeCounter(); EndTime += KeConvertMicrosecondsToTimeTicks( Timeout * MICROSECONDS_PER_MILLISECOND); TimeCounterFrequency = HlQueryTimeCounterFrequency(); } // // Loop trying to get some data. This loop exits once one packet is read. // while (TRUE) { // // Wait for a packet to become available. Start by computing the wait // time. // if (Timeout == 0) { WaitTime = 0; } else if (Timeout != WAIT_TIME_INDEFINITE) { CurrentTime = KeGetRecentTimeCounter(); WaitTime = (EndTime - CurrentTime) * MILLISECONDS_PER_SECOND / TimeCounterFrequency; } else { WaitTime = WAIT_TIME_INDEFINITE; } // // Wait for something to maybe become available. If the wait fails due // to a timeout, interruption, or something else, then fail out. // Otherwise when the read event is signalled, there is at least one // packet to receive. // Status = IoWaitForIoObjectState(Socket->KernelSocket.IoState, POLL_EVENT_IN, TRUE, WaitTime, &ReturnedEvents); if (!KSUCCESS(Status)) { goto GenericReceiveEnd; } if ((ReturnedEvents & POLL_ERROR_EVENTS) != 0) { if ((ReturnedEvents & POLL_EVENT_DISCONNECTED) != 0) { Status = STATUS_NO_NETWORK_CONNECTION; } else { Status = NET_SOCKET_GET_LAST_ERROR(Socket); if (KSUCCESS(Status)) { Status = STATUS_DEVICE_IO_ERROR; } } goto GenericReceiveEnd; } KeAcquireQueuedLock(GenericSocket->ReceiveLock); LockHeld = TRUE; // // If another thread beat this one to the punch, try again. // if (LIST_EMPTY(&(GenericSocket->ReceivedPacketList)) != FALSE) { KeReleaseQueuedLock(GenericSocket->ReceiveLock); LockHeld = FALSE; continue; } // // This should be the first packet being read. // ASSERT(BytesComplete == 0); PacketEntry = GenericSocket->ReceivedPacketList.Next; Packet = LIST_VALUE(PacketEntry, NETLINK_GENERIC_RECEIVED_PACKET, ListEntry); PacketSize = Packet->NetPacket->FooterOffset - Packet->NetPacket->DataOffset; ReturnSize = PacketSize; CopySize = ReturnSize; if (CopySize > Size) { Parameters->SocketIoFlags |= SOCKET_IO_DATA_TRUNCATED; CopySize = Size; // // The real packet size is only returned to the user on truncation // if the truncated flag was supplied to this routine. Default to // returning the truncated size. // if ((Flags & SOCKET_IO_DATA_TRUNCATED) == 0) { ReturnSize = CopySize; } } Buffer = Packet->NetPacket->Buffer + Packet->NetPacket->DataOffset; Status = MmCopyIoBufferData(IoBuffer, Buffer, 0, CopySize, TRUE); if (!KSUCCESS(Status)) { goto GenericReceiveEnd; } // // Copy the packet address out to the caller if requested. // if (Parameters->NetworkAddress != NULL) { if (FromKernelMode != FALSE) { RtlCopyMemory(Parameters->NetworkAddress, &(Packet->Address), sizeof(NETWORK_ADDRESS)); } else { Status = MmCopyToUserMode(Parameters->NetworkAddress, &(Packet->Address), sizeof(NETWORK_ADDRESS)); if (!KSUCCESS(Status)) { goto GenericReceiveEnd; } } } BytesComplete = ReturnSize; // // Remove the packet if not peeking. // if ((Flags & SOCKET_IO_PEEK) == 0) { LIST_REMOVE(&(Packet->ListEntry)); GenericSocket->ReceiveBufferFreeSize += PacketSize; // // The total receive buffer size may have been decreased. Don't // increment the free size above the total. // if (GenericSocket->ReceiveBufferFreeSize > GenericSocket->ReceiveBufferTotalSize) { GenericSocket->ReceiveBufferFreeSize = GenericSocket->ReceiveBufferTotalSize; } MmFreePagedPool(Packet); // // Unsignal the IN event if there are no more packets. // if (LIST_EMPTY(&(GenericSocket->ReceivedPacketList)) != FALSE) { IoSetIoObjectState(Socket->KernelSocket.IoState, POLL_EVENT_IN, FALSE); } } // // Wait-all does not apply to netlink sockets. Break out. // Status = STATUS_SUCCESS; break; } GenericReceiveEnd: if (LockHeld != FALSE) { KeReleaseQueuedLock(GenericSocket->ReceiveLock); } Parameters->Size = BytesComplete; return Status; } KSTATUS NetlinkpGenericGetSetInformation ( PNET_SOCKET Socket, SOCKET_INFORMATION_TYPE InformationType, UINTN Option, PVOID Data, PUINTN DataSize, BOOL Set ) /*++ Routine Description: This routine gets or sets properties of the given socket. Arguments: Socket - Supplies a pointer to the socket to get or set information for. InformationType - Supplies the socket information type category to which specified option belongs. Option - Supplies the option to get or set, which is specific to the information type. The type of this value is generally SOCKET__OPTION. Data - Supplies a pointer to the data buffer where the data is either returned for a get operation or given for a set operation. DataSize - Supplies a pointer that on input constains the size of the data buffer. On output, this contains the required size of the data buffer. Set - Supplies a boolean indicating if this is a get operation (FALSE) or a set operation (TRUE). Return Value: STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if the information type is incorrect. STATUS_BUFFER_TOO_SMALL if the data buffer is too small to receive the requested option. STATUS_NOT_SUPPORTED_BY_PROTOCOL if the socket option is not supported by the socket. STATUS_NOT_HANDLED if the protocol does not override the default behavior for a basic socket option. --*/ { ULONG Count; PNETLINK_GENERIC_SOCKET_OPTION GenericOption; PNETLINK_GENERIC_SOCKET GenericSocket; ULONG Index; LONGLONG Milliseconds; PNET_PACKET_SIZE_INFORMATION SizeInformation; ULONG SizeOption; PSOCKET_TIME SocketTime; SOCKET_TIME SocketTimeBuffer; PVOID Source; KSTATUS Status; GenericSocket = (PNETLINK_GENERIC_SOCKET)Socket; if ((InformationType != SocketInformationBasic) && (InformationType != SocketInformationNetlinkGeneric)) { Status = STATUS_NOT_SUPPORTED; goto GenericGetSetInformationEnd; } // // Search to see if the socket option is supported by the netlink generic // protocol. // Count = sizeof(NetlinkGenericSocketOptions) / sizeof(NetlinkGenericSocketOptions[0]); for (Index = 0; Index < Count; Index += 1) { GenericOption = &(NetlinkGenericSocketOptions[Index]); if ((GenericOption->InformationType == InformationType) && (GenericOption->Option == Option)) { break; } } if (Index == Count) { if (InformationType == SocketInformationBasic) { Status = STATUS_NOT_HANDLED; } else { Status = STATUS_NOT_SUPPORTED_BY_PROTOCOL; } goto GenericGetSetInformationEnd; } // // Handle failure cases common to all options. // if (Set != FALSE) { if (GenericOption->SetAllowed == FALSE) { Status = STATUS_NOT_SUPPORTED_BY_PROTOCOL; goto GenericGetSetInformationEnd; } if (*DataSize < GenericOption->Size) { *DataSize = GenericOption->Size; Status = STATUS_BUFFER_TOO_SMALL; goto GenericGetSetInformationEnd; } } // // There are currently no netlink generic protocol options. // ASSERT(InformationType != SocketInformationNetlinkGeneric); // // Parse the basic socket option, getting the information from the generic // socket or setting the new state in the generic socket. // Source = NULL; Status = STATUS_SUCCESS; switch ((SOCKET_BASIC_OPTION)Option) { case SocketBasicOptionSendBufferSize: if (Set != FALSE) { SizeOption = *((PULONG)Data); if (SizeOption > SOCKET_OPTION_MAX_ULONG) { SizeOption = SOCKET_OPTION_MAX_ULONG; } SizeInformation = &(Socket->PacketSizeInformation); if (SizeOption > NETLINK_GENERIC_MAX_PACKET_SIZE) { SizeOption = NETLINK_GENERIC_MAX_PACKET_SIZE; } else if (SizeOption < SizeInformation->MaxPacketSize) { SizeOption = SizeInformation->MaxPacketSize; } GenericSocket->MaxPacketSize = SizeOption; } else { Source = &SizeOption; SizeOption = GenericSocket->MaxPacketSize; } break; case SocketBasicOptionSendMinimum: ASSERT(Set == FALSE); SizeOption = NETLINK_GENERIC_SEND_MINIMUM; Source = &SizeOption; break; case SocketBasicOptionReceiveBufferSize: if (Set != FALSE) { SizeOption = *((PULONG)Data); if (SizeOption < NETLINK_GENERIC_MIN_RECEIVE_BUFFER_SIZE) { SizeOption = NETLINK_GENERIC_MIN_RECEIVE_BUFFER_SIZE; } else if (SizeOption > SOCKET_OPTION_MAX_ULONG) { SizeOption = SOCKET_OPTION_MAX_ULONG; } // // Set the receive buffer size and truncate the available free // space if necessary. Do not remove any packets that have already // been received. This is not meant to be a truncate call. // KeAcquireQueuedLock(GenericSocket->ReceiveLock); GenericSocket->ReceiveBufferTotalSize = SizeOption; if (GenericSocket->ReceiveBufferFreeSize > SizeOption) { GenericSocket->ReceiveBufferFreeSize = SizeOption; } KeReleaseQueuedLock(GenericSocket->ReceiveLock); } else { SizeOption = GenericSocket->ReceiveBufferTotalSize; Source = &SizeOption; } break; case SocketBasicOptionReceiveMinimum: if (Set != FALSE) { SizeOption = *((PULONG)Data); if (SizeOption > SOCKET_OPTION_MAX_ULONG) { SizeOption = SOCKET_OPTION_MAX_ULONG; } GenericSocket->ReceiveMinimum = SizeOption; } else { SizeOption = GenericSocket->ReceiveMinimum; Source = &SizeOption; } break; case SocketBasicOptionReceiveTimeout: if (Set != FALSE) { SocketTime = (PSOCKET_TIME)Data; if (SocketTime->Seconds < 0) { Status = STATUS_DOMAIN_ERROR; break; } Milliseconds = SocketTime->Seconds * MILLISECONDS_PER_SECOND; if (Milliseconds < SocketTime->Seconds) { Status = STATUS_DOMAIN_ERROR; break; } Milliseconds += SocketTime->Microseconds / MICROSECONDS_PER_MILLISECOND; if ((Milliseconds < 0) || (Milliseconds > MAX_LONG)) { Status = STATUS_DOMAIN_ERROR; break; } GenericSocket->ReceiveTimeout = (ULONG)(LONG)Milliseconds; } else { Source = &SocketTimeBuffer; if (GenericSocket->ReceiveTimeout == WAIT_TIME_INDEFINITE) { SocketTimeBuffer.Seconds = 0; SocketTimeBuffer.Microseconds = 0; } else { SocketTimeBuffer.Seconds = GenericSocket->ReceiveTimeout / MILLISECONDS_PER_SECOND; SocketTimeBuffer.Microseconds = (GenericSocket->ReceiveTimeout % MILLISECONDS_PER_SECOND) * MICROSECONDS_PER_MILLISECOND; } } break; default: ASSERT(FALSE); Status = STATUS_NOT_HANDLED; break; } if (!KSUCCESS(Status)) { goto GenericGetSetInformationEnd; } // // Truncate all copies for get requests down to the required size and only // return the required size on set requests. // if (*DataSize > GenericOption->Size) { *DataSize = GenericOption->Size; } // // For get requests, copy the gathered information to the supplied data // buffer. // if (Set == FALSE) { ASSERT(Source != NULL); RtlCopyMemory(Data, Source, *DataSize); // // If the copy truncated the data, report that the given buffer was too // small. The caller can choose to ignore this if the truncated data is // enough. // if (*DataSize < GenericOption->Size) { *DataSize = GenericOption->Size; Status = STATUS_BUFFER_TOO_SMALL; goto GenericGetSetInformationEnd; } } GenericGetSetInformationEnd: return Status; } KSTATUS NetlinkpGenericUserControl ( PNET_SOCKET Socket, ULONG CodeNumber, BOOL FromKernelMode, PVOID ContextBuffer, UINTN ContextBufferSize ) /*++ Routine Description: This routine handles user control requests destined for a socket. Arguments: Socket - Supplies a pointer to the socket. CodeNumber - Supplies the minor code of the request. FromKernelMode - Supplies a boolean indicating whether or not this request (and the buffer associated with it) originates from user mode (FALSE) or kernel mode (TRUE). ContextBuffer - Supplies a pointer to the context buffer allocated by the caller for the request. ContextBufferSize - Supplies the size of the supplied context buffer. Return Value: Status code. --*/ { return STATUS_NOT_SUPPORTED; } KSTATUS NetlinkpGenericJoinMulticastGroup ( PNET_SOCKET Socket, ULONG GroupId ) /*++ Routine Description: This routine attempts to join the given multicast group by validating the group ID for the protocol and then joining the multicast group. Arguments: Socket - Supplies a pointer to the network socket requesting to join a multicast group. GroupId - Supplies the ID of the multicast group to join. Return Value: Status code. --*/ { KSTATUS Status; // // With the family lock held to prevent family's from unregistering, // validate the multicast group and then attempt to join it. This must be // done with the lock held or else a family could leave after validation // and then the socket would miss being removed from the multicast group it // is about to join. // KeAcquireSharedExclusiveLockShared(NetlinkGenericFamilyLock); Status = NetlinkpGenericValidateMulticastGroup(GroupId, TRUE); if (!KSUCCESS(Status)) { goto JoinMulticastGroupEnd; } Status = NetlinkJoinMulticastGroup(Socket, GroupId); if (!KSUCCESS(Status)) { goto JoinMulticastGroupEnd; } JoinMulticastGroupEnd: KeReleaseSharedExclusiveLockShared(NetlinkGenericFamilyLock); return Status; } PNETLINK_GENERIC_FAMILY NetlinkpGenericLookupFamilyById ( ULONG FamilyId ) /*++ Routine Description: This routine searches the database of registered generic netlink families for one with the given ID. If successful, the family is returned with an added reference which the caller must release. Arguments: FamilyId - Supplies the ID of the desired family. Return Value: Returns a pointer to the generic netlink family on success or NULL on failure. --*/ { PNETLINK_GENERIC_FAMILY FoundFamily; PRED_BLACK_TREE_NODE FoundNode; NETLINK_GENERIC_FAMILY SearchFamily; FoundFamily = NULL; SearchFamily.Properties.Id = FamilyId; KeAcquireSharedExclusiveLockShared(NetlinkGenericFamilyLock); FoundNode = RtlRedBlackTreeSearch(&NetlinkGenericFamilyTree, &(SearchFamily.TreeNode)); if (FoundNode != NULL) { FoundFamily = RED_BLACK_TREE_VALUE(FoundNode, NETLINK_GENERIC_FAMILY, TreeNode); NetlinkpGenericFamilyAddReference(FoundFamily); } KeReleaseSharedExclusiveLockShared(NetlinkGenericFamilyLock); return FoundFamily; } PNETLINK_GENERIC_FAMILY NetlinkpGenericLookupFamilyByName ( PSTR FamilyName ) /*++ Routine Description: This routine searches the database of registered generic netlink families for one with the given name. If successful, the family is returned with an added reference which the caller must release. Arguments: FamilyName - Supplies the name of the desired family. Return Value: Returns a pointer to the generic netlink family on success or NULL on failure. --*/ { PNETLINK_GENERIC_FAMILY Family; PNETLINK_GENERIC_FAMILY FoundFamily; BOOL Match; PRED_BLACK_TREE_NODE Node; FoundFamily = NULL; KeAcquireSharedExclusiveLockShared(NetlinkGenericFamilyLock); Node = RtlRedBlackTreeGetLowestNode(&NetlinkGenericFamilyTree); while (Node != NULL) { Family = RED_BLACK_TREE_VALUE(Node, NETLINK_GENERIC_FAMILY, TreeNode); Match = RtlAreStringsEqual(Family->Properties.Name, FamilyName, NETLINK_GENERIC_MAX_FAMILY_NAME_LENGTH); if (Match != FALSE) { FoundFamily = Family; NetlinkpGenericFamilyAddReference(FoundFamily); break; } Node = RtlRedBlackTreeGetNextNode(&NetlinkGenericFamilyTree, FALSE, Node); } KeReleaseSharedExclusiveLockShared(NetlinkGenericFamilyLock); return FoundFamily; } VOID NetlinkpGenericFamilyAddReference ( PNETLINK_GENERIC_FAMILY Family ) /*++ Routine Description: This routine increments the reference count of a generic netlink family. Arguments: Family - Supplies a pointer to a generic netlink family. Return Value: None. --*/ { ULONG OldReferenceCount; OldReferenceCount = RtlAtomicAdd32(&(Family->ReferenceCount), 1); ASSERT((OldReferenceCount != 0) && (OldReferenceCount < 0x10000000)); return; } VOID NetlinkpGenericFamilyReleaseReference ( PNETLINK_GENERIC_FAMILY Family ) /*++ Routine Description: This routine decrements the reference count of a generic netlink family, releasing all of its resources if the reference count drops to zero. Arguments: Family - Supplies a pointer to a generic netlink family. Return Value: None. --*/ { ULONG OldReferenceCount; OldReferenceCount = RtlAtomicAdd32(&(Family->ReferenceCount), -1); ASSERT((OldReferenceCount != 0) && (OldReferenceCount < 0x10000000)); if (OldReferenceCount == 1) { NetlinkpGenericDestroyFamily(Family); } return; } // // --------------------------------------------------------- Internal Functions // VOID NetlinkpGenericInsertReceivedPacket ( PNETLINK_GENERIC_SOCKET Socket, PNETLINK_GENERIC_RECEIVED_PACKET Packet ) /*++ Routine Description: This routine attempts to insert the given packet into the socket's list of received packets. Arguments: Socket - Supplies a pointer to the generic netlink socket that received the packet. Packet - Supplies a pointer to the generic netlink packet that was received. Return Value: None. --*/ { ULONG PacketLength; PacketLength = Packet->NetPacket->FooterOffset - Packet->NetPacket->DataOffset; KeAcquireQueuedLock(Socket->ReceiveLock); if (PacketLength <= Socket->ReceiveBufferFreeSize) { INSERT_BEFORE(&(Packet->ListEntry), &(Socket->ReceivedPacketList)); Socket->ReceiveBufferFreeSize -= PacketLength; ASSERT(Socket->ReceiveBufferFreeSize < Socket->ReceiveBufferTotalSize); // // One packet is always enough to notify a waiting receiver. // IoSetIoObjectState(Socket->NetlinkSocket.NetSocket.KernelSocket.IoState, POLL_EVENT_IN, TRUE); Packet = NULL; } else { Socket->DroppedPacketCount += 1; } KeReleaseQueuedLock(Socket->ReceiveLock); // // If the packet wasn't nulled out, that's an indication it wasn't added to // the list, so free it up. // if (Packet != NULL) { NetFreeBuffer(Packet->NetPacket); MmFreePagedPool(Packet); } return; } KSTATUS NetlinkpGenericProcessReceivedKernelData ( PNET_LINK Link, PNET_SOCKET Socket, PNET_PACKET_BUFFER Packet, PNETWORK_ADDRESS SourceAddress, PNETWORK_ADDRESS DestinationAddress ) /*++ Routine Description: This routine is called to process a packet received by a kernel socket. Arguments: Link - Supplies a pointer to the link that received the packet. Socket - Supplies a pointer to the network socket that received the packet. Packet - Supplies a pointer to a structure describing the incoming packet. This structure may not be used as a scratch space and must not be modified by this routine. SourceAddress - Supplies a pointer to the source (remote) address that the packet originated from. This memory will not be referenced once the function returns, it can be stack allocated. DestinationAddress - Supplies a pointer to the destination (local) address that the packet is heading to. This memory will not be referenced once the function returns, it can be stack allocated. Return Value: Status code. --*/ { PNETLINK_GENERIC_COMMAND Command; NETLINK_GENERIC_COMMAND_INFORMATION CommandInformation; PNETLINK_GENERIC_FAMILY Family; BOOL FoundCommand; PNETLINK_GENERIC_HEADER GenericHeader; PNETLINK_HEADER Header; ULONG Index; NET_PACKET_BUFFER LocalPacket; ULONG PacketLength; USHORT RequiredFlags; KSTATUS Status; Family = NULL; // // Make a local copy of the network packet buffer so that the offsets can // be modified. The original is not allowed to be modified. // RtlCopyMemory(&LocalPacket, Packet, sizeof(NET_PACKET_BUFFER)); PacketLength = LocalPacket.FooterOffset - LocalPacket.DataOffset; Header = LocalPacket.Buffer + LocalPacket.DataOffset; ASSERT(PacketLength >= NETLINK_HEADER_LENGTH); ASSERT((Header->Flags & NETLINK_HEADER_FLAG_REQUEST) != 0); ASSERT(Header->Type >= NETLINK_MESSAGE_TYPE_PROTOCOL_MINIMUM); ASSERT(Header->Length <= PacketLength); LocalPacket.DataOffset += NETLINK_HEADER_LENGTH; PacketLength -= NETLINK_HEADER_LENGTH; // // The generic header must be present. // GenericHeader = NETLINK_DATA(Header); if (PacketLength < NETLINK_GENERIC_HEADER_LENGTH) { RtlDebugPrint("NETLINK: packet does not have space for generic " "header. %d expected, has %d.\n", NETLINK_GENERIC_HEADER_LENGTH, PacketLength); Status = STATUS_BUFFER_TOO_SMALL; goto ProcessReceivedKernelDataEnd; } LocalPacket.DataOffset += NETLINK_GENERIC_HEADER_LENGTH; PacketLength -= NETLINK_GENERIC_HEADER_LENGTH; // // Find the generic netlink family and call the command's callback. // Family = NetlinkpGenericLookupFamilyById(Header->Type); if (Family == NULL) { Status = STATUS_NOT_SUPPORTED; goto ProcessReceivedKernelDataEnd; } FoundCommand = FALSE; for (Index = 0; Index < Family->Properties.CommandCount; Index += 1) { Command = &(Family->Properties.Commands[Index]); if (Command->CommandId == GenericHeader->Command) { FoundCommand = TRUE; break; } } if (FoundCommand == FALSE) { Status = STATUS_NOT_SUPPORTED; goto ProcessReceivedKernelDataEnd; } RequiredFlags = Command->RequiredFlags; if ((Header->Flags & RequiredFlags) != RequiredFlags) { Status = STATUS_NOT_SUPPORTED; goto ProcessReceivedKernelDataEnd; } CommandInformation.Message.SourceAddress = SourceAddress; CommandInformation.Message.DestinationAddress = DestinationAddress; CommandInformation.Message.SequenceNumber = Header->SequenceNumber; CommandInformation.Message.Type = Header->Type; CommandInformation.Command = GenericHeader->Command; CommandInformation.Version = GenericHeader->Version; Status = Command->ProcessCommand(Socket, &LocalPacket, &CommandInformation); if (!KSUCCESS(Status)) { goto ProcessReceivedKernelDataEnd; } ProcessReceivedKernelDataEnd: if (Family != NULL) { NetlinkpGenericFamilyReleaseReference(Family); } return Status; } COMPARISON_RESULT NetlinkpGenericCompareFamilies ( PRED_BLACK_TREE Tree, PRED_BLACK_TREE_NODE FirstNode, PRED_BLACK_TREE_NODE SecondNode ) /*++ Routine Description: This routine compares two netlink generic family Red-Black tree nodes. Arguments: Tree - Supplies a pointer to the Red-Black tree that owns both nodes. FirstNode - Supplies a pointer to the left side of the comparison. SecondNode - Supplies a pointer to the second side of the comparison. Return Value: Same if the two nodes have the same value. Ascending if the first node is less than the second node. Descending if the second node is less than the first node. --*/ { PNETLINK_GENERIC_FAMILY FirstFamily; PNETLINK_GENERIC_FAMILY SecondFamily; FirstFamily = RED_BLACK_TREE_VALUE(FirstNode, NETLINK_GENERIC_FAMILY, TreeNode); SecondFamily = RED_BLACK_TREE_VALUE(SecondNode, NETLINK_GENERIC_FAMILY, TreeNode); if (FirstFamily->Properties.Id < SecondFamily->Properties.Id) { return ComparisonResultAscending; } if (FirstFamily->Properties.Id > SecondFamily->Properties.Id) { return ComparisonResultDescending; } return ComparisonResultSame; } VOID NetlinkpGenericDestroyFamily ( PNETLINK_GENERIC_FAMILY Family ) /*++ Routine Description: This routine destroys a generic netlink family and all of its resources. It should already be unregistered and removed from the global tree. Arguments: Family - Supplies a pointer to a generic netlink family. Return Value: None. --*/ { MmFreePagedPool(Family); return; } KSTATUS NetlinkpGenericAllocateFamilyId ( PULONG FamilyId ) /*++ Routine Description: This routine attempts to allocate a free generic netlink family ID. It assumes the netlink generic family lock is held exclusively. Arguments: FamilyId - Supplies a pointer that receives the newly allocated ID. Return Value: Status code. --*/ { PRED_BLACK_TREE_NODE FoundNode; ULONG NextId; NETLINK_GENERIC_FAMILY SearchFamily; KSTATUS Status; ASSERT(KeIsSharedExclusiveLockHeldExclusive(NetlinkGenericFamilyLock)); Status = STATUS_INSUFFICIENT_RESOURCES; NextId = NetlinkGenericFamilyNextId; // // Iterate until all the possible message types have been tried. // do { SearchFamily.Properties.Id = NextId; NextId += 1; if (NextId < NETLINK_MESSAGE_TYPE_PROTOCOL_MINIMUM) { NextId = NETLINK_MESSAGE_TYPE_PROTOCOL_MINIMUM; } FoundNode = RtlRedBlackTreeSearch(&NetlinkGenericFamilyTree, &(SearchFamily.TreeNode)); if (FoundNode == NULL) { *FamilyId = SearchFamily.Properties.Id; Status = STATUS_SUCCESS; break; } } while (NextId != NetlinkGenericFamilyNextId); // // Update the global to make the next search start where this left off. // NetlinkGenericFamilyNextId = NextId; return Status; } KSTATUS NetlinkpGenericAllocateMulticastGroups ( PNETLINK_GENERIC_FAMILY Family ) /*++ Routine Description: This routine allocates a region of the multicast group ID namespace for the given family. It assumes the netlink generic family lock is held exclusively. Arguments: Family - Supplies a pointer to the netlink generic family in need of multicast group allocation. Return Value: Status code. --*/ { ULONG BitmapIndex; ULONG Count; PNETLINK_GENERIC_MULTICAST_GROUP Group; ULONG Index; ULONG Mask; PULONG NewBitmap; ULONG NewBitmapSize; ULONG NewGroups; ULONG Offset; ULONG Run; KSTATUS Status; ULONG Value; ASSERT(KeIsSharedExclusiveLockHeldExclusive(NetlinkGenericFamilyLock)); Count = Family->Properties.MulticastGroupCount; // // Search through the existing bitmap for a run that can accomodate the // new multicast groups. // Offset = 0; Run = 0; for (BitmapIndex = 0; BitmapIndex < (NetlinkGenericMulticastBitmapSize / sizeof(ULONG)); BitmapIndex += 1) { Value = NetlinkGenericMulticastBitmap[BitmapIndex]; for (Index = 0; Index < (sizeof(ULONG) * BITS_PER_BYTE); Index += 1) { if ((Value & 0x1) != 0) { Offset += Run + 1; Run = 0; } else { Run += 1; if (Run == Count) { break; } } Value >>= 1; } if (Run == Count) { break; } // // A multicast group ID of 0 should never be assigned. // ASSERT(Offset != 0); } // // If there is not enough space, allocate a bigger array. If the run // is not zero, that means that there was some space at the end of the // last ULONG of the bitmap. Account for that when added more ULONGs. // if (Run != Count) { NewGroups = Count - Run; // // Avoid allocating group ID 0. It is invalid. // if (Offset == 0) { NewGroups += 1; Offset = 1; } NewGroups = ALIGN_RANGE_UP(NewGroups, sizeof(ULONG) * BITS_PER_BYTE); NewBitmapSize = NetlinkGenericMulticastBitmapSize + (NewGroups / BITS_PER_BYTE); NewBitmap = MmAllocatePagedPool(NewBitmapSize, NETLINK_GENERIC_ALLOCATION_TAG); if (NewBitmap == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto AllocateMulticastGroupOffsetEnd; } RtlZeroMemory((PVOID)NewBitmap + NetlinkGenericMulticastBitmapSize, (NewGroups / BITS_PER_BYTE)); // // If there is an existing bitmap, copy it to the new bitmap and // then release it. // if (NetlinkGenericMulticastBitmap != NULL) { RtlCopyMemory(NewBitmap, NetlinkGenericMulticastBitmap, NetlinkGenericMulticastBitmapSize); MmFreePagedPool(NetlinkGenericMulticastBitmap); // // Otherwise, this is the first allocation. Reserve group ID 0. // } else { Index = NETLINK_SOCKET_BITMAP_INDEX(0); Mask = NETLINK_SOCKET_BITMAP_MASK(0); NewBitmap[Index] |= Mask; } NetlinkGenericMulticastBitmap = NewBitmap; NetlinkGenericMulticastBitmapSize = NewBitmapSize; } // // Set the newly allocated groups as reserved in the bitmap. // for (Index = 0; Index < Count; Index += 1) { Group = &(Family->Properties.MulticastGroups[Index]); Group->Id += Offset; BitmapIndex = NETLINK_SOCKET_BITMAP_INDEX(Group->Id); Mask = NETLINK_SOCKET_BITMAP_MASK(Group->Id); NetlinkGenericMulticastBitmap[BitmapIndex] |= Mask; } Family->MulticastGroupOffset = Offset; Status = STATUS_SUCCESS; AllocateMulticastGroupOffsetEnd: return Status; } VOID NetlinkpGenericFreeMulticastGroups ( PNETLINK_GENERIC_FAMILY Family ) /*++ Routine Description: This routine releases the region of multicast group IDs allocated for the given netlink generic family. It assumes the netlink generic family lock is held exclusively. Arguments: Family - Supplies a pointer to the netlink generic family whose multicast groups should be released. Return Value: None. --*/ { ULONG BitmapIndex; UCHAR Command; ULONG Count; PNETLINK_GENERIC_MULTICAST_GROUP Group; ULONG Index; ULONG Mask; ULONG Offset; ULONG Protocol; ASSERT(KeIsSharedExclusiveLockHeldExclusive(NetlinkGenericFamilyLock)); Count = Family->Properties.MulticastGroupCount; Offset = Family->MulticastGroupOffset; // // Remove all sockets from these multicast groups. // Protocol = SOCKET_INTERNET_PROTOCOL_NETLINK_GENERIC; NetlinkRemoveSocketsFromMulticastGroups(Protocol, Offset, Count); // // Free the groups from the generic netlink bitmap. // for (Index = 0; Index < Count; Index += 1) { Group = &(Family->Properties.MulticastGroups[Index]); BitmapIndex = NETLINK_SOCKET_BITMAP_INDEX(Group->Id); Mask = NETLINK_SOCKET_BITMAP_MASK(Group->Id); NetlinkGenericMulticastBitmap[BitmapIndex] &= ~Mask; // // Announce the deletion of the multicast group. // Command = NETLINK_CONTROL_COMMAND_DELETE_MULTICAST_GROUP; NetlinkpGenericControlSendNotification(Family, Command, Group); } return; } KSTATUS NetlinkpGenericValidateMulticastGroup ( ULONG GroupId, BOOL LockHeld ) /*++ Routine Description: This routine validates that the group ID is valid for the netlink generic families. Arguments: GroupId - Supplies the group ID that is to be validated. LockHeld - Supplies a boolean indicating whether or not the global generic family lock is held. Return Value: Status code. --*/ { ULONG Index; ULONG Mask; KSTATUS Status; ASSERT(GroupId != 0); if (LockHeld == FALSE) { KeAcquireSharedExclusiveLockShared(NetlinkGenericFamilyLock); } Index = NETLINK_SOCKET_BITMAP_INDEX(GroupId); Mask = NETLINK_SOCKET_BITMAP_MASK(GroupId); if ((Index >= (NetlinkGenericMulticastBitmapSize / sizeof(ULONG))) || ((NetlinkGenericMulticastBitmap[Index] & Mask) == 0)) { Status = STATUS_INVALID_PARAMETER; } else { Status = STATUS_SUCCESS; } if (LockHeld == FALSE) { KeReleaseSharedExclusiveLockShared(NetlinkGenericFamilyLock); } return Status; }