Ver código fonte

Move multicast group accounting into netcore.

Before this change, IPv4 kept track of which multicast groups a socket
joined. It did this by allocating its own private IPv4 specific socket
structure. This information, however, will also be needed by IPv6 for
multicast support so it makes sense to centralize it into netcore.

This change also centralizes a link's accounting of which multicast
groups it belongs to. This is necessary as a complete list of IPv4 and
IPv6 multicast groups needs to be passed to the hardware in order to
update the multicast filters. It is not good enough to make add/remove
requests to the hardware due to how NICs implement multicast filters.
Chris Stevens 6 anos atrás
pai
commit
21183e16e1

+ 1 - 0
drivers/net/netcore/Makefile

@@ -36,6 +36,7 @@ BINPLACE = bin
 OBJS = addr.o            \
        buf.o             \
        ethernet.o        \
+       mcast.o           \
        netcore.o         \
        raw.o             \
        tcp.o             \

+ 2 - 0
drivers/net/netcore/addr.c

@@ -327,6 +327,8 @@ Return Value:
                               0,
                               NetpCompareAddressTranslationEntries);
 
+    INITIALIZE_LIST_HEAD(&(Link->MulticastGroupList));
+
     //
     // Find the appropriate data link layer and initialize it for this link.
     //

+ 1 - 0
drivers/net/netcore/build.ck

@@ -44,6 +44,7 @@ function build() {
         "ipv4/igmp.c",
         "ipv4/ip4.c",
         "ipv6/ip6.c",
+        "mcast.c",
         "netcore.c",
         "netlink/netlink.c",
         "netlink/genctrl.c",

+ 35 - 110
drivers/net/netcore/ipv4/igmp.c

@@ -64,14 +64,14 @@ Environment:
 //
 
 //
-// Define the allocation tag used by the UDP socket protocol.
+// Define the allocation tag used by IGMP.
 //
 
-#define IGMP_PROTOCOL_ALLOCATION_TAG 0x706d6749 // 'pmgI'
+#define IGMP_ALLOCATION_TAG 0x706d6749 // 'pmgI'
 
 //
 // Define the size of an IGMP IPv4 header. Each packet should include the
-// router alter option.
+// router alert option.
 //
 
 #define IGMP_IP4_HEADER_SIZE (sizeof(IP4_HEADER) + sizeof(ULONG))
@@ -104,8 +104,7 @@ Environment:
 #define IGMP_MAX_GROUP_RECORD_COUNT MAX_USHORT
 
 //
-// Define the source IPv4 address for all IGMP general query messages -
-// 224.0.0.1.
+// Define the IPv4 address to which all IGMP general query messages are sent.
 //
 
 #define IGMP_ALL_SYSTEMS_ADDRESS CPU_TO_NETWORK32(0xE0000001)
@@ -288,8 +287,8 @@ Members:
 
     Type - Stores the group record type.
 
-    DataLength - Stores the length of auxiliary data that starts at the end of
-        the source address array.
+    DataLength - Stores the length of auxiliary data, in 32-bit words, that
+        starts at the end of the source address array.
 
     SourceAddressCount - Stores the number of source address entries in the
         array that starts at the end of this structure.
@@ -560,12 +559,12 @@ NetpIgmpUserControl (
 
 KSTATUS
 NetpIgmpJoinMulticastGroup (
-    PSOCKET_IGMP_MULTICAST_REQUEST Request
+    PNET_NETWORK_MULTICAST_REQUEST Request
     );
 
 KSTATUS
 NetpIgmpLeaveMulticastGroup (
-    PSOCKET_IGMP_MULTICAST_REQUEST Request
+    PNET_NETWORK_MULTICAST_REQUEST Request
     );
 
 VOID
@@ -720,12 +719,6 @@ NetpIgmpDestroyTimer (
     PIGMP_TIMER Timer
     );
 
-USHORT
-NetpIgmpChecksumData (
-    PVOID Data,
-    ULONG Length
-    );
-
 //
 // -------------------------------------------------------------------- Globals
 //
@@ -1182,7 +1175,7 @@ Return Value:
     // Validate the IGMP checksum.
     //
 
-    ComputedChecksum = NetpIgmpChecksumData((PVOID)Header, Length);
+    ComputedChecksum = NetChecksumData((PVOID)Header, Length);
     if (ComputedChecksum != 0) {
         RtlDebugPrint("IGMP: Invalid checksum. Computed checksum: 0x%04x, "
                       "should have been zero.\n",
@@ -1371,7 +1364,8 @@ Return Value:
 {
 
     SOCKET_IGMP_OPTION IgmpOption;
-    PSOCKET_IGMP_MULTICAST_REQUEST MulticastRequest;
+    PIP4_ADDRESS MulticastAddress;
+    PNET_NETWORK_MULTICAST_REQUEST MulticastRequest;
     UINTN RequiredSize;
     PVOID Source;
     KSTATUS Status;
@@ -1393,15 +1387,18 @@ Return Value:
             break;
         }
 
-        RequiredSize = sizeof(SOCKET_IGMP_MULTICAST_REQUEST);
+        RequiredSize = sizeof(NET_NETWORK_MULTICAST_REQUEST);
         if (*DataSize < RequiredSize) {
             *DataSize = RequiredSize;
             Status = STATUS_BUFFER_TOO_SMALL;
             break;
         }
 
-        MulticastRequest = (PSOCKET_IGMP_MULTICAST_REQUEST)Data;
-        if (!IP4_IS_MULTICAST_ADDRESS(MulticastRequest->MulticastAddress)) {
+        MulticastRequest = (PNET_NETWORK_MULTICAST_REQUEST)Data;
+        MulticastAddress = (PIP4_ADDRESS)MulticastRequest->MulticastAddress;
+        if ((MulticastAddress->Domain != NetDomainIp4) ||
+            (!IP4_IS_MULTICAST_ADDRESS(MulticastAddress->Address))) {
+
             Status = STATUS_INVALID_PARAMETER;
             break;
         }
@@ -1508,7 +1505,7 @@ Return Value:
 
 KSTATUS
 NetpIgmpJoinMulticastGroup (
-    PSOCKET_IGMP_MULTICAST_REQUEST Request
+    PNET_NETWORK_MULTICAST_REQUEST Request
     )
 
 /*++
@@ -1539,10 +1536,12 @@ Return Value:
     PIGMP_MULTICAST_GROUP Group;
     PIGMP_LINK IgmpLink;
     BOOL LinkLockHeld;
+    PIP4_ADDRESS MulticastAddress;
     PIGMP_MULTICAST_GROUP NewGroup;
     KSTATUS Status;
 
     LinkLockHeld = FALSE;
+    MulticastAddress = (PIP4_ADDRESS)Request->MulticastAddress;
     NewGroup = NULL;
 
     //
@@ -1575,7 +1574,7 @@ Return Value:
         CurrentEntry = IgmpLink->MulticastGroupList.Next;
         while (CurrentEntry != &(IgmpLink->MulticastGroupList)) {
             Group = LIST_VALUE(CurrentEntry, IGMP_MULTICAST_GROUP, ListEntry);
-            if (Group->Address == Request->MulticastAddress) {
+            if (Group->Address == MulticastAddress->Address) {
                 Status = STATUS_SUCCESS;
                 break;
             }
@@ -1588,7 +1587,7 @@ Return Value:
                 KeReleaseQueuedLock(IgmpLink->Lock);
                 LinkLockHeld = FALSE;
                 NewGroup = NetpIgmpCreateGroup(IgmpLink,
-                                               Request->MulticastAddress);
+                                               MulticastAddress->Address);
 
                 if (NewGroup == NULL) {
                     Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -1599,7 +1598,7 @@ Return Value:
             }
 
             //
-            // Add the newly allocate group to the link's list.
+            // Add the newly allocated group to the link's list.
             //
 
             INSERT_BEFORE(&(NewGroup->ListEntry),
@@ -1672,7 +1671,7 @@ JoinMulticastGroupEnd:
 
 KSTATUS
 NetpIgmpLeaveMulticastGroup (
-    PSOCKET_IGMP_MULTICAST_REQUEST Request
+    PNET_NETWORK_MULTICAST_REQUEST Request
     )
 
 /*++
@@ -1703,10 +1702,12 @@ Return Value:
     PIGMP_LINK IgmpLink;
     BOOL LinkLockHeld;
     BOOL LinkUp;
+    PIP4_ADDRESS MulticastAddress;
     KSTATUS Status;
 
     LinkLockHeld = FALSE;
     IgmpLink = NULL;
+    MulticastAddress = (PIP4_ADDRESS)Request->MulticastAddress;
 
     //
     // Now see if there is an IGMP link for the given network link.
@@ -1729,7 +1730,7 @@ Return Value:
     CurrentEntry = IgmpLink->MulticastGroupList.Next;
     while (CurrentEntry != &(IgmpLink->MulticastGroupList)) {
         Group = LIST_VALUE(CurrentEntry, IGMP_MULTICAST_GROUP, ListEntry);
-        if (Group->Address == Request->MulticastAddress) {
+        if (Group->Address == MulticastAddress->Address) {
             Status = STATUS_SUCCESS;
             break;
         }
@@ -2669,7 +2670,7 @@ Return Value:
     Header->Type = Type;
     Header->MaxResponseCode = 0;
     Header->Checksum = 0;
-    Header->Checksum = NetpIgmpChecksumData((PVOID)Header, BufferSize);
+    Header->Checksum = NetChecksumData((PVOID)Header, BufferSize);
     NET_INITIALIZE_PACKET_LIST(&NetPacketList);
     NET_ADD_PACKET_TO_LIST(Packet, &NetPacketList);
     NetpIgmpSendPackets(Group->IgmpLink,
@@ -2822,7 +2823,7 @@ Return Value:
     Header->Type = Type;
     Header->MaxResponseCode = 0;
     Header->Checksum = 0;
-    Header->Checksum = NetpIgmpChecksumData((PVOID)Header, BufferSize);
+    Header->Checksum = NetChecksumData((PVOID)Header, BufferSize);
     NET_INITIALIZE_PACKET_LIST(&NetPacketList);
     NET_ADD_PACKET_TO_LIST(Packet, &NetPacketList);
     NetpIgmpSendPackets(Group->IgmpLink,
@@ -2987,7 +2988,7 @@ Return Value:
                       (PIGMP_GROUP_RECORD_V3)((PUCHAR)GroupRecord + GroupSize);
         }
 
-        Header->Checksum = NetpIgmpChecksumData((PVOID)Header, BufferSize);
+        Header->Checksum = NetChecksumData((PVOID)Header, BufferSize);
         NET_ADD_PACKET_TO_LIST(Packet, &NetPacketList);
     }
 
@@ -3103,9 +3104,7 @@ Return Value:
         if ((Link->Properties.Capabilities &
              NET_LINK_CAPABILITY_TRANSMIT_IP_CHECKSUM_OFFLOAD) == 0) {
 
-             Checksum = NetpIgmpChecksumData((PVOID)Header,
-                                             IGMP_IP4_HEADER_SIZE);
-
+             Checksum = NetChecksumData((PVOID)Header, IGMP_IP4_HEADER_SIZE);
              Header->HeaderChecksum = Checksum;
 
         } else {
@@ -3263,7 +3262,7 @@ Return Value:
     }
 
     NewIgmpLink = MmAllocatePagedPool(sizeof(IGMP_LINK),
-                                      IGMP_PROTOCOL_ALLOCATION_TAG);
+                                      IGMP_ALLOCATION_TAG);
 
     if (NewIgmpLink == NULL) {
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -3768,7 +3767,7 @@ Return Value:
     KSTATUS Status;
 
     Group = MmAllocatePagedPool(sizeof(IGMP_MULTICAST_GROUP),
-                                IGMP_PROTOCOL_ALLOCATION_TAG);
+                                IGMP_ALLOCATION_TAG);
 
     if (Group == NULL) {
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -3932,7 +3931,7 @@ Return Value:
 
     KSTATUS Status;
 
-    Timer->Timer = KeCreateTimer(IGMP_PROTOCOL_ALLOCATION_TAG);
+    Timer->Timer = KeCreateTimer(IGMP_ALLOCATION_TAG);
     if (Timer->Timer == NULL) {
         Status = STATUS_INSUFFICIENT_RESOURCES;
         goto InitializeTimerEnd;
@@ -3948,7 +3947,7 @@ Return Value:
                                        WorkPriorityNormal,
                                        WorkRoutine,
                                        WorkParameter,
-                                       IGMP_PROTOCOL_ALLOCATION_TAG);
+                                       IGMP_ALLOCATION_TAG);
 
     if (Timer->WorkItem == NULL) {
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -4005,77 +4004,3 @@ Return Value:
     return;
 }
 
-USHORT
-NetpIgmpChecksumData (
-    PVOID Data,
-    ULONG Length
-    )
-
-/*++
-
-Routine Description:
-
-    This routine checksums a section of data for IGMP processing.
-
-Arguments:
-
-    Data - Supplies a pointer to the data to checksum.
-
-    Length - Supplies the number of bytes to checksum. This must be an even
-        number.
-
-Return Value:
-
-    Returns the checksum of the data.
-
---*/
-
-{
-
-    PUCHAR BytePointer;
-    PULONG LongPointer;
-    ULONG NextValue;
-    USHORT ShortOne;
-    PUSHORT ShortPointer;
-    USHORT ShortTwo;
-    ULONG Sum;
-
-    ASSERT((Length & 0x1) == 0);
-
-    Sum = 0;
-    LongPointer = (PULONG)Data;
-    while (Length >= sizeof(ULONG)) {
-        NextValue = *LongPointer;
-        LongPointer += 1;
-        Sum += NextValue;
-        if (Sum < NextValue) {
-            Sum += 1;
-        }
-
-        Length -= sizeof(ULONG);
-    }
-
-    BytePointer = (PUCHAR)LongPointer;
-    if (Length == sizeof(USHORT)) {
-        ShortPointer = (PUSHORT)BytePointer;
-        NextValue = (USHORT)*ShortPointer;
-        Sum += NextValue;
-        if (Sum < NextValue) {
-            Sum += 1;
-        }
-    }
-
-    //
-    // Fold the 32-bit value down to 16-bits.
-    //
-
-    ShortOne = (USHORT)Sum;
-    ShortTwo = (USHORT)(Sum >> 16);
-    ShortTwo += ShortOne;
-    if (ShortTwo < ShortOne) {
-        ShortTwo += 1;
-    }
-
-    return (USHORT)~ShortTwo;
-}
-

+ 135 - 871
drivers/net/netcore/ipv4/ip4.c

@@ -74,12 +74,6 @@ Environment:
 
 #define IP4_MAX_FRAGMENT_COUNT 1000
 
-//
-// Define IPv4 the socket information flags.
-//
-
-#define IP4_SOCKET_FLAG_MULTICAST_LOOPBACK 0x00000001
-
 //
 // --------------------------------------------------------------------- Macros
 //
@@ -162,77 +156,6 @@ typedef struct _IP4_FRAGMENT_ENTRY {
     BOOL LastFragment;
 } IP4_FRAGMENT_ENTRY, *PIP4_FRAGMENT_ENTRY;
 
-/*++
-
-Structure Description:
-
-    This structure defines a multicast group for an IPv4 socket.
-
-Members:
-
-    ListEntry - Stores pointers to the previous and next multicast groups in
-        the socket's list.
-
-    Link - Supplies a pointer to the network link to which the multicast group
-        is attached.
-
-    LinkAddress - Supplies a pointer to the link address entry with which the
-        multicast group is associated.
-
-    Address - Stores the IPv4 multicast address of the group.
-
---*/
-
-typedef struct _IP4_MULTICAST_GROUP {
-    LIST_ENTRY ListEntry;
-    PNET_LINK Link;
-    PNET_LINK_ADDRESS_ENTRY LinkAddress;
-    ULONG MulticastAddress;
-} IP4_MULTICAST_GROUP, *PIP4_MULTICAST_GROUP;
-
-/*++
-
-Structure Description:
-
-    This structure defines the IP4 socket option information.
-
-Members:
-
-    Flags - Stores a bitmask of IPv4 socket information flags. See
-        IP4_SOCKET_FLAG_* for definitions.
-
-    TimeToLive - Stores the time-to-live that is to be set in the IPv4 header
-        for every packet sent by this socket.
-
-    DifferentiatedServicesCodePoint - Stores the differentiated services code
-        point that is to be set in the IPv4 header for every packet sent by
-        this socket.
-
-    MulticastTimeToLive - Stores the time-to-live that is to be set in the IPv4
-        header for every multicast packet sent by this socket.
-
-    MulticastInterface - Stores the interface over which to send all multicast
-        packets. If this is not initialized, then a default interface is chosen
-        just as it would be for unicast packets.
-
-    MulticastLock - Supplies a pointer to the queued lock that protects access
-        to the multicast information.
-
-    MulticastGroupList - Stores the head of the list of multicast groups to
-        which the socket belongs.
-
---*/
-
-typedef struct _IP4_SOCKET_INFORMATION {
-    volatile ULONG Flags;
-    UCHAR TimeToLive;
-    UCHAR DifferentiatedServicesCodePoint;
-    UCHAR MulticastTimeToLive;
-    NET_SOCKET_LINK_OVERRIDE MulticastInterface;
-    volatile PQUEUED_LOCK MulticastLock;
-    LIST_ENTRY MulticastGroupList;
-} IP4_SOCKET_INFORMATION, *PIP4_SOCKET_INFORMATION;
-
 //
 // ----------------------------------------------- Internal Function Prototypes
 //
@@ -319,12 +242,6 @@ NetpIp4GetSetInformation (
     BOOL Set
     );
 
-KSTATUS
-NetpIp4CopyInformation (
-    PNET_SOCKET DestinationSocket,
-    PNET_SOCKET SourceSocket
-    );
-
 NET_ADDRESS_TYPE
 NetpIp4GetAddressType (
     PNET_LINK Link,
@@ -347,6 +264,12 @@ NetpIp4ConfigureLinkAddress (
     BOOL Configure
     );
 
+KSTATUS
+NetpIp4JoinLeaveMulticastGroup (
+    PNET_NETWORK_MULTICAST_REQUEST Request,
+    BOOL Join
+    );
+
 KSTATUS
 NetpIp4TranslateNetworkAddress (
     PNET_SOCKET Socket,
@@ -384,40 +307,6 @@ NetpIp4DestroyFragmentedPacketNode (
     PIP4_FRAGMENTED_PACKET_NODE PacketNode
     );
 
-KSTATUS
-NetpIp4JoinMulticastGroup (
-    PNET_SOCKET Socket,
-    PSOCKET_IP4_MULTICAST_REQUEST Request
-    );
-
-KSTATUS
-NetpIp4LeaveMulticastGroup (
-    PNET_SOCKET Socket,
-    PSOCKET_IP4_MULTICAST_REQUEST Request
-    );
-
-VOID
-NetpIp4DestroyMulticastGroups (
-    PNET_SOCKET Socket
-    );
-
-KSTATUS
-NetpIp4FindLinkForMulticastRequest (
-    PNET_NETWORK_ENTRY Network,
-    PSOCKET_IP4_MULTICAST_REQUEST Request,
-    PNET_LINK_LOCAL_ADDRESS LinkResult
-    );
-
-KSTATUS
-NetpIp4AcquireMulticastLock (
-    PIP4_SOCKET_INFORMATION SocketInformation
-    );
-
-VOID
-NetpIp4ReleaseMulticastLock (
-    PIP4_SOCKET_INFORMATION SocketInformation
-    );
-
 //
 // -------------------------------------------------------------------- Globals
 //
@@ -450,11 +339,11 @@ NET_NETWORK_ENTRY NetIp4Network = {
         NetpIp4ProcessReceivedData,
         NetpIp4PrintAddress,
         NetpIp4GetSetInformation,
-        NetpIp4CopyInformation,
         NetpIp4GetAddressType,
         NULL,
         NetpIp4ChecksumPseudoHeader,
-        NetpIp4ConfigureLinkAddress
+        NetpIp4ConfigureLinkAddress,
+        NetpIp4JoinLeaveMulticastGroup,
     }
 };
 
@@ -660,7 +549,6 @@ Return Value:
 {
 
     ULONG MaxPacketSize;
-    PIP4_SOCKET_INFORMATION SocketInformation;
 
     //
     // If this is coming from the raw protocol and the network protocol is the
@@ -704,25 +592,18 @@ Return Value:
     }
 
     //
-    // Allocate and initialize a socket information structure for this socket.
+    // Set IPv4 specific socket setting defaults.
     //
 
-    SocketInformation = MmAllocatePagedPool(sizeof(IP4_SOCKET_INFORMATION),
-                                            IP4_ALLOCATION_TAG);
+    NewSocket->HopLimit = IP4_INITIAL_TIME_TO_LIVE;
+    NewSocket->DifferentiatedServicesCodePoint = 0;
+    NewSocket->MulticastHopLimit = IP4_INITIAL_MULTICAST_TIME_TO_LIVE;
 
-    if (SocketInformation == NULL) {
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
+    //
+    // Initialize the socket's multicast fields.
+    //
 
-    RtlZeroMemory(SocketInformation, sizeof(IP4_SOCKET_INFORMATION));
-    SocketInformation->Flags = IP4_SOCKET_FLAG_MULTICAST_LOOPBACK;
-    SocketInformation->TimeToLive = IP4_INITIAL_TIME_TO_LIVE;
-    SocketInformation->MulticastTimeToLive = IP4_INITIAL_MULTICAST_TIME_TO_LIVE;
-    SocketInformation->DifferentiatedServicesCodePoint = 0;
-    SocketInformation->MulticastLock = NULL;
-    INITIALIZE_LIST_HEAD(&(SocketInformation->MulticastGroupList));
-    NewSocket->NetworkSocketInformation = SocketInformation;
-    return STATUS_SUCCESS;
+    return NetInitializeMulticastSocket(NewSocket);
 }
 
 VOID
@@ -749,12 +630,7 @@ Return Value:
 
 {
 
-    if (Socket->NetworkSocketInformation != NULL) {
-        NetpIp4DestroyMulticastGroups(Socket);
-        MmFreePagedPool(Socket->NetworkSocketInformation);
-        Socket->NetworkSocketInformation = NULL;
-    }
-
+    NetDestroyMulticastSocket(Socket);
     return;
 }
 
@@ -1143,7 +1019,6 @@ Return Value:
     NET_RECEIVE_CONTEXT ReceiveContext;
     PIP4_ADDRESS RemoteAddress;
     PNET_DATA_LINK_SEND Send;
-    PIP4_SOCKET_INFORMATION SocketInformation;
     PNETWORK_ADDRESS Source;
     KSTATUS Status;
     ULONG TimeToLive;
@@ -1154,26 +1029,22 @@ Return Value:
            (Socket->KernelSocket.Protocol ==
             Socket->Protocol->ParentProtocolNumber));
 
-    SocketInformation = Socket->NetworkSocketInformation;
-
-    ASSERT(SocketInformation != NULL);
-
     //
     // Multicast packets must use the multicast time-to-live, which defaults to
     // 1 (rather than 63) as multicast packets aren't typically meant to go
     // beyond the local network.
     //
 
-    TimeToLive = SocketInformation->TimeToLive;
+    TimeToLive = Socket->HopLimit;
     RemoteAddress = (PIP4_ADDRESS)Destination;
     if (IP4_IS_MULTICAST_ADDRESS(RemoteAddress->Address) != FALSE) {
-        TimeToLive = SocketInformation->MulticastTimeToLive;
+        TimeToLive = Socket->MulticastHopLimit;
 
         //
         // Also use the multicast interface information if it is present.
         //
 
-        MulticastInterface = &(SocketInformation->MulticastInterface);
+        MulticastInterface = &(Socket->MulticastInterface);
         if (MulticastInterface->LinkInformation.Link != NULL) {
             LinkOverride = MulticastInterface;
         }
@@ -1333,9 +1204,7 @@ Return Value:
                      IP4_VERSION | (UCHAR)(sizeof(IP4_HEADER) / sizeof(ULONG));
 
                 Header->Type = 0;
-                Header->Type |=
-                            SocketInformation->DifferentiatedServicesCodePoint;
-
+                Header->Type |= Socket->DifferentiatedServicesCodePoint;
                 TotalLength = Fragment->FooterOffset - Fragment->DataOffset;
                 Header->TotalLength = CPU_TO_NETWORK16(TotalLength);
                 Header->Identification =
@@ -1418,7 +1287,7 @@ Return Value:
                                                      sizeof(ULONG));
 
             Header->Type = 0;
-            Header->Type |= SocketInformation->DifferentiatedServicesCodePoint;
+            Header->Type |= Socket->DifferentiatedServicesCodePoint;
             TotalLength = Packet->FooterOffset - Packet->DataOffset;
             Header->TotalLength = CPU_TO_NETWORK16(TotalLength);
             Header->Identification = CPU_TO_NETWORK16(Socket->SendPacketCount);
@@ -1490,8 +1359,7 @@ Return Value:
     //
 
     if ((IP4_IS_MULTICAST_ADDRESS(RemoteAddress->Address) != FALSE) &&
-        ((SocketInformation->Flags &
-          IP4_SOCKET_FLAG_MULTICAST_LOOPBACK) != 0)) {
+        ((Socket->Flags & NET_SOCKET_FLAG_MULTICAST_LOOPBACK) != 0)) {
 
         RtlZeroMemory(&ReceiveContext, sizeof(NET_RECEIVE_CONTEXT));
         ReceiveContext.Link = Link;
@@ -1926,18 +1794,14 @@ Return Value:
     UCHAR ByteOption;
     ULONG Flags;
     ULONG IntegerOption;
-    SOCKET_IP4_MULTICAST_REQUEST InterfaceRequest;
-    PIP4_ADDRESS Ip4Address;
+    PIP4_ADDRESS InterfaceAddress;
+    PSOCKET_IP4_MULTICAST_REQUEST InterfaceRequest;
+    PSOCKET_IP4_MULTICAST_REQUEST Ip4MulticastRequest;
     SOCKET_IP4_OPTION Ip4Option;
-    PNET_LINK_LOCAL_ADDRESS LinkInformation;
-    NET_LINK_LOCAL_ADDRESS LinkResult;
-    PNET_SOCKET_LINK_OVERRIDE MulticastInterface;
-    PSOCKET_IP4_MULTICAST_REQUEST MulticastRequest;
-    NET_SOCKET_LINK_OVERRIDE NewInterface;
-    PNET_LINK OldInterfaceLink;
+    PIP4_ADDRESS MulticastAddress;
+    NET_SOCKET_MULTICAST_REQUEST MulticastRequest;
     PNET_PROTOCOL_ENTRY Protocol;
     UINTN RequiredSize;
-    PIP4_SOCKET_INFORMATION SocketInformation;
     PVOID Source;
     KSTATUS Status;
 
@@ -1946,12 +1810,6 @@ Return Value:
         goto Ip4GetSetInformationEnd;
     }
 
-    SocketInformation = Socket->NetworkSocketInformation;
-    if (SocketInformation == NULL) {
-        Status = STATUS_NOT_INITIALIZED;
-        goto Ip4GetSetInformationEnd;
-    }
-
     RequiredSize = 0;
     Source = NULL;
     Status = STATUS_SUCCESS;
@@ -2017,11 +1875,11 @@ Return Value:
                 break;
             }
 
-            SocketInformation->TimeToLive = (UCHAR)IntegerOption;
+            Socket->HopLimit = (UCHAR)IntegerOption;
 
         } else {
             Source = &IntegerOption;
-            IntegerOption = SocketInformation->TimeToLive;
+            IntegerOption = Socket->HopLimit;
         }
 
         break;
@@ -2042,12 +1900,11 @@ Return Value:
             }
 
             IntegerOption &= IP4_TYPE_DSCP_MASK;
-            SocketInformation->DifferentiatedServicesCodePoint =
-                                                          (UCHAR)IntegerOption;
+            Socket->DifferentiatedServicesCodePoint = (UCHAR)IntegerOption;
 
         } else {
             Source = &IntegerOption;
-            IntegerOption = SocketInformation->DifferentiatedServicesCodePoint;
+            IntegerOption = Socket->DifferentiatedServicesCodePoint;
         }
 
         break;
@@ -2075,17 +1932,24 @@ Return Value:
             break;
         }
 
-        MulticastRequest = (PSOCKET_IP4_MULTICAST_REQUEST)Data;
-        if (!IP4_IS_MULTICAST_ADDRESS(MulticastRequest->Address)) {
+        Ip4MulticastRequest = (PSOCKET_IP4_MULTICAST_REQUEST)Data;
+        if (!IP4_IS_MULTICAST_ADDRESS(Ip4MulticastRequest->Address)) {
             Status = STATUS_INVALID_PARAMETER;
             break;
         }
 
+        RtlZeroMemory(&MulticastRequest, sizeof(NET_SOCKET_MULTICAST_REQUEST));
+        MulticastAddress = (PIP4_ADDRESS)&(MulticastRequest.MulticastAddress);
+        MulticastAddress->Domain = NetDomainIp4;
+        MulticastAddress->Address = Ip4MulticastRequest->Address;
+        InterfaceAddress = (PIP4_ADDRESS)&(MulticastRequest.InterfaceAddress);
+        InterfaceAddress->Domain = NetDomainIp4;
+        InterfaceAddress->Address = Ip4MulticastRequest->Interface;
         if (Ip4Option == SocketIp4OptionJoinMulticastGroup) {
-            Status = NetpIp4JoinMulticastGroup(Socket, MulticastRequest);
+            Status = NetJoinSocketMulticastGroup(Socket, &MulticastRequest);
 
         } else {
-            Status = NetpIp4LeaveMulticastGroup(Socket, MulticastRequest);
+            Status = NetLeaveSocketMulticastGroup(Socket, &MulticastRequest);
         }
 
         goto Ip4GetSetInformationEnd;
@@ -2100,11 +1964,11 @@ Return Value:
             }
 
             ByteOption = *((PUCHAR)Data);
-            SocketInformation->MulticastTimeToLive = ByteOption;
+            Socket->MulticastHopLimit = ByteOption;
 
         } else {
             Source = &ByteOption;
-            ByteOption = SocketInformation->MulticastTimeToLive;
+            ByteOption = Socket->MulticastHopLimit;
         }
 
         break;
@@ -2122,8 +1986,14 @@ Return Value:
         // to determine which one was supplied.
         //
 
-        MulticastInterface = &(SocketInformation->MulticastInterface);
+        MulticastAddress = (PIP4_ADDRESS)&(MulticastRequest.MulticastAddress);
+        InterfaceAddress = (PIP4_ADDRESS)&(MulticastRequest.InterfaceAddress);
         if (Set != FALSE) {
+            RtlZeroMemory(&MulticastRequest,
+                          sizeof(NET_SOCKET_MULTICAST_REQUEST));
+
+            MulticastAddress->Domain = NetDomainIp4;
+            InterfaceAddress->Domain = NetDomainIp4;
 
             //
             // The size accounts for one IPv4 address, but not two. Interpret
@@ -2131,86 +2001,36 @@ Return Value:
             //
 
             if (*DataSize < sizeof(SOCKET_IP4_MULTICAST_REQUEST)) {
-                RtlZeroMemory(&InterfaceRequest, sizeof(InterfaceRequest));
-                InterfaceRequest.Interface = *((PULONG)Data);
+                InterfaceAddress->Address = *((PULONG)Data);
 
             //
             // Otherwise interpret it as a multicast request structure.
             //
 
             } else {
-
-                ASSERT(*DataSize >= sizeof(SOCKET_IP4_MULTICAST_REQUEST));
-
                 RequiredSize = sizeof(SOCKET_IP4_MULTICAST_REQUEST);
-                RtlCopyMemory(&InterfaceRequest, Data, RequiredSize);
-            }
-
-            //
-            // The any address resets the multicast interface to the default.
-            //
-
-            if (InterfaceRequest.Interface == 0) {
-                RtlZeroMemory(&NewInterface, sizeof(NET_SOCKET_LINK_OVERRIDE));
-
-            //
-            // Otherwise the net link for the supplied interface is found.
-            //
-
-            } else {
-                Status = NetpIp4FindLinkForMulticastRequest(Socket->Network,
-                                                            &InterfaceRequest,
-                                                            &LinkResult);
-
-                if (!KSUCCESS(Status)) {
-                    break;
-                }
-
-                NetInitializeSocketLinkOverride(Socket,
-                                                &LinkResult,
-                                                &NewInterface);
+                InterfaceRequest = Data;
+                MulticastAddress->Address = InterfaceRequest->Address;
+                InterfaceAddress->Address = InterfaceRequest->Interface;
             }
 
-            //
-            // Acquire the multicast lock and smash in the new interface.
-            //
-
-            Status = NetpIp4AcquireMulticastLock(SocketInformation);
+            Status = NetSetSocketMulticastInterface(Socket, &MulticastRequest);
             if (!KSUCCESS(Status)) {
-                if (NewInterface.LinkInformation.Link != NULL) {
-                    NetLinkReleaseReference(NewInterface.LinkInformation.Link);
-                }
-
                 break;
             }
 
-            OldInterfaceLink = MulticastInterface->LinkInformation.Link;
-            RtlCopyMemory(MulticastInterface,
-                          &NewInterface,
-                          sizeof(NET_SOCKET_LINK_OVERRIDE));
-
-            NetpIp4ReleaseMulticastLock(SocketInformation);
-            if (OldInterfaceLink != NULL) {
-                NetLinkReleaseReference(OldInterfaceLink);
-            }
-
         //
         // A get request only ever returns the IPv4 address of the interface.
-        // This must acquire the lock as the set call copies the address into
-        // place byte by byte. Avoid a torn read.
         //
 
         } else {
-            LinkInformation = &(MulticastInterface->LinkInformation);
-            Ip4Address = (PIP4_ADDRESS)&(LinkInformation->SendAddress);
-            Source = &IntegerOption;
-            Status = NetpIp4AcquireMulticastLock(SocketInformation);
+            Status = NetGetSocketMulticastInterface(Socket, &MulticastRequest);
             if (!KSUCCESS(Status)) {
                 break;
             }
 
-            IntegerOption = Ip4Address->Address;
-            NetpIp4ReleaseMulticastLock(SocketInformation);
+            IntegerOption = InterfaceAddress->Address;
+            Source = &IntegerOption;
         }
 
         break;
@@ -2226,19 +2046,18 @@ Return Value:
         if (Set != FALSE) {
             ByteOption = *((PUCHAR)Data);
             if (ByteOption != FALSE) {
-                RtlAtomicOr32(&(SocketInformation->Flags),
-                              IP4_SOCKET_FLAG_MULTICAST_LOOPBACK);
+                RtlAtomicOr32(&(Socket->Flags),
+                              NET_SOCKET_FLAG_MULTICAST_LOOPBACK);
 
             } else {
-                RtlAtomicAnd32(&(SocketInformation->Flags),
-                               ~IP4_SOCKET_FLAG_MULTICAST_LOOPBACK);
+                RtlAtomicAnd32(&(Socket->Flags),
+                               ~NET_SOCKET_FLAG_MULTICAST_LOOPBACK);
             }
 
         } else {
             Source = &ByteOption;
             ByteOption = FALSE;
-            Flags = SocketInformation->Flags;
-            if ((Flags & IP4_SOCKET_FLAG_MULTICAST_LOOPBACK) != 0) {
+            if ((Socket->Flags & NET_SOCKET_FLAG_MULTICAST_LOOPBACK) != 0) {
                 ByteOption = TRUE;
             }
         }
@@ -2291,66 +2110,6 @@ Ip4GetSetInformationEnd:
     return Status;
 }
 
-KSTATUS
-NetpIp4CopyInformation (
-    PNET_SOCKET DestinationSocket,
-    PNET_SOCKET SourceSocket
-    )
-
-/*++
-
-Routine Description:
-
-    This routine copies socket information properties from the source socket to
-    the destination socket.
-
-Arguments:
-
-    DestinationSocket - Supplies a pointer to the socket whose information will
-        be overwritten with the source socket's information.
-
-    SourceSocket - Supplies a pointer to the socket whose information will
-        be copied to the destination socket.
-
-Return Value:
-
-    Status code.
-
---*/
-
-{
-
-    PIP4_SOCKET_INFORMATION DestinationInformation;
-
-    if ((DestinationSocket->NetworkSocketInformation == NULL) ||
-        (SourceSocket->NetworkSocketInformation == NULL)) {
-
-        return STATUS_NOT_INITIALIZED;
-    }
-
-    //
-    // Copy all of the socket information. This routine is invoked when a
-    // connection is accepted and the listening socket is forked.
-    //
-
-    RtlCopyMemory(DestinationSocket->NetworkSocketInformation,
-                  SourceSocket->NetworkSocketInformation,
-                  sizeof(IP4_SOCKET_INFORMATION));
-
-    //
-    // Reset the multicast information. The new socket should not inherit that
-    // information.
-    //
-
-    DestinationInformation = DestinationSocket->NetworkSocketInformation;
-    DestinationInformation->MulticastLock = NULL;
-    RtlZeroMemory(&(DestinationInformation->MulticastInterface),
-                  sizeof(NET_SOCKET_LINK_OVERRIDE));
-
-    INITIALIZE_LIST_HEAD(&(DestinationInformation->MulticastGroupList));
-    return STATUS_SUCCESS;
-}
-
 NET_ADDRESS_TYPE
 NetpIp4GetAddressType (
     PNET_LINK Link,
@@ -2367,7 +2126,8 @@ Routine Description:
 
 Arguments:
 
-    Link - Supplies a pointer to the network link to which the address is bound.
+    Link - Supplies an optional pointer to the network link to which the
+        address is bound.
 
     LinkAddressEntry - Supplies an optional pointer to a network link address
         entry to use while classifying the address.
@@ -2415,6 +2175,10 @@ Return Value:
     //
 
     if (LinkAddressEntry == NULL) {
+        if (Link == NULL) {
+            return NetAddressUnknown;
+        }
+
         LinkAddressList = &(Link->LinkAddressArray[NetDomainIp4]);
 
         ASSERT(LIST_EMPTY(LinkAddressList) == FALSE);
@@ -2551,6 +2315,70 @@ Return Value:
     return Status;
 }
 
+KSTATUS
+NetpIp4JoinLeaveMulticastGroup (
+    PNET_NETWORK_MULTICAST_REQUEST Request,
+    BOOL Join
+    )
+
+/*++
+
+Routine Description:
+
+    This routine joins or leaves a multicast group using a network-specific
+    protocol.
+
+Arguments:
+
+    Request - Supplies a pointer to the multicast group join/leave request.
+
+    Join - Supplies a boolean indicating whether to join (TRUE) or leave
+        (FALSE) the multicast group.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+{
+
+    UINTN Option;
+    PNET_PROTOCOL_ENTRY Protocol;
+    UINTN RequestSize;
+    KSTATUS Status;
+
+    //
+    // This isn't going to get very far without IGMP support.
+    //
+
+    Protocol = NetGetProtocolEntry(SOCKET_INTERNET_PROTOCOL_IGMP);
+    if (Protocol == NULL) {
+        return STATUS_NOT_SUPPORTED_BY_PROTOCOL;
+    }
+
+    //
+    // IGMP actually doesn't depend on a socket to join/leave a multicast
+    // group. Don't bother passing one around.
+    //
+
+    Option = SocketIgmpOptionLeaveMulticastGroup;
+    if (Join != FALSE) {
+        Option = SocketIgmpOptionJoinMulticastGroup;
+    }
+
+    RequestSize = sizeof(NET_NETWORK_MULTICAST_REQUEST);
+    Status = Protocol->Interface.GetSetInformation(
+                                           NULL,
+                                           SocketInformationIgmp,
+                                           Option,
+                                           Request,
+                                           &RequestSize,
+                                           TRUE);
+
+    return Status;
+}
+
 //
 // --------------------------------------------------------- Internal Functions
 //
@@ -3418,567 +3246,3 @@ Return Value:
     return;
 }
 
-KSTATUS
-NetpIp4JoinMulticastGroup (
-    PNET_SOCKET Socket,
-    PSOCKET_IP4_MULTICAST_REQUEST Request
-    )
-
-/*++
-
-Routine Description:
-
-    This routine adds the given socket to a multicast group.
-
-Arguments:
-
-    Socket - Supplies a pointer to a socket.
-
-    Request - Supplies a pointer to the multicast join request. This stores
-        the multicast group address to join and the address of the interface on
-        which to join it.
-
-Return Value:
-
-    Status code.
-
---*/
-
-{
-
-    PLIST_ENTRY CurrentEntry;
-    PIP4_MULTICAST_GROUP Group;
-    SOCKET_IGMP_MULTICAST_REQUEST IgmpRequest;
-    NET_LINK_LOCAL_ADDRESS LinkResult;
-    BOOL LockHeld;
-    PIP4_MULTICAST_GROUP NewGroup;
-    PNET_PROTOCOL_ENTRY Protocol;
-    UINTN RequestSize;
-    PIP4_SOCKET_INFORMATION SocketInformation;
-    KSTATUS Status;
-
-    LinkResult.Link = NULL;
-    LockHeld = FALSE;
-    NewGroup = NULL;
-    SocketInformation = Socket->NetworkSocketInformation;
-
-    //
-    // This isn't going to get very far without IGMP. Fail immediately if it's
-    // not present.
-    //
-
-    Protocol = NetGetProtocolEntry(SOCKET_INTERNET_PROTOCOL_IGMP);
-    if (Protocol == NULL) {
-        Status = STATUS_NOT_SUPPORTED_BY_PROTOCOL;
-        goto JoinMulticastGroupEnd;
-    }
-
-    //
-    // Attempt to find a network link that can reach the multicast address, or
-    // find the one specified by the request.
-    //
-
-    Status = NetpIp4FindLinkForMulticastRequest(Socket->Network,
-                                                Request,
-                                                &LinkResult);
-
-    if (!KSUCCESS(Status)) {
-        Status = STATUS_NO_SUCH_DEVICE;
-        goto JoinMulticastGroupEnd;
-    }
-
-    Status = NetpIp4AcquireMulticastLock(SocketInformation);
-    if (!KSUCCESS(Status)) {
-        goto JoinMulticastGroupEnd;
-    }
-
-    LockHeld = TRUE;
-
-    //
-    // Check to see if this socket already joined the group.
-    //
-
-    CurrentEntry = SocketInformation->MulticastGroupList.Next;
-    while (CurrentEntry != &(SocketInformation->MulticastGroupList)) {
-        Group = LIST_VALUE(CurrentEntry, IP4_MULTICAST_GROUP, ListEntry);
-        if ((Group->MulticastAddress == Request->Address) &&
-            (Group->Link == LinkResult.Link) &&
-            (Group->LinkAddress == LinkResult.LinkAddress)) {
-
-            Status = STATUS_ADDRESS_IN_USE;
-            goto JoinMulticastGroupEnd;
-        }
-
-        CurrentEntry = CurrentEntry->Next;
-    }
-
-    //
-    // Prepare for success and allocate a new IPv4 multicast group.
-    //
-
-    NewGroup = MmAllocatePagedPool(sizeof(IP4_MULTICAST_GROUP),
-                                   IP4_ALLOCATION_TAG);
-
-    if (NewGroup == NULL) {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto JoinMulticastGroupEnd;
-    }
-
-    //
-    // Ask IGMP to join the multicast group. A multithreaded request for this
-    // socket to join or leave a muliticast group is highly unlikely, so don't
-    // fret too much about holding the lock.
-    //
-
-    IgmpRequest.Link = LinkResult.Link;
-    IgmpRequest.LinkAddress = LinkResult.LinkAddress;
-    IgmpRequest.MulticastAddress = Request->Address;
-    RequestSize = sizeof(SOCKET_IGMP_MULTICAST_REQUEST);
-    Status = Protocol->Interface.GetSetInformation(
-                                            Socket,
-                                            SocketInformationIgmp,
-                                            SocketIgmpOptionJoinMulticastGroup,
-                                            &IgmpRequest,
-                                            &RequestSize,
-                                            TRUE);
-
-    if (!KSUCCESS(Status)) {
-        goto JoinMulticastGroupEnd;
-    }
-
-    NewGroup->MulticastAddress = Request->Address;
-    NewGroup->Link = LinkResult.Link;
-    NewGroup->LinkAddress = LinkResult.LinkAddress;
-    INSERT_BEFORE(&(NewGroup->ListEntry),
-                  &(SocketInformation->MulticastGroupList));
-
-    //
-    // Make sure to take the link's reference from the link result.
-    //
-
-    LinkResult.Link = NULL;
-
-JoinMulticastGroupEnd:
-    if (LockHeld != FALSE) {
-        NetpIp4ReleaseMulticastLock(SocketInformation);
-    }
-
-    if (LinkResult.Link != NULL) {
-        NetLinkReleaseReference(LinkResult.Link);
-    }
-
-    if (!KSUCCESS(Status)) {
-        if (NewGroup != NULL) {
-            MmFreePagedPool(NewGroup);
-        }
-    }
-
-    return Status;
-}
-
-KSTATUS
-NetpIp4LeaveMulticastGroup (
-    PNET_SOCKET Socket,
-    PSOCKET_IP4_MULTICAST_REQUEST Request
-    )
-
-/*++
-
-Routine Description:
-
-    This routine removes the given socket from a multicast group.
-
-Arguments:
-
-    Socket - Supplies a pointer to a socket.
-
-    Request - Supplies a pointer to the multicast leave request. This stores
-        the multicast group address to leave and the address of the interface
-        on which the socket joined the group.
-
-Return Value:
-
-    Status code.
-
---*/
-
-{
-
-    PLIST_ENTRY CurrentEntry;
-    PIP4_MULTICAST_GROUP DestroyGroup;
-    PIP4_MULTICAST_GROUP Group;
-    SOCKET_IGMP_MULTICAST_REQUEST IgmpRequest;
-    NET_LINK_LOCAL_ADDRESS LinkResult;
-    BOOL LockHeld;
-    PNET_PROTOCOL_ENTRY Protocol;
-    UINTN RequestSize;
-    PIP4_SOCKET_INFORMATION SocketInformation;
-    KSTATUS Status;
-
-    DestroyGroup = NULL;
-    LinkResult.Link = NULL;
-    LockHeld = FALSE;
-
-    //
-    // If the multicast lock is not allocated or the list is empty, then this
-    // socket never joined any multicast groups.
-    //
-
-    SocketInformation = Socket->NetworkSocketInformation;
-    if (LIST_EMPTY(&(SocketInformation->MulticastGroupList)) != FALSE) {
-        Status = STATUS_INVALID_ADDRESS;
-        goto LeaveMulticastGroupEnd;
-    }
-
-    ASSERT(SocketInformation->MulticastLock != NULL);
-
-    //
-    // Attempt to find a network link that can reach the multicast address, or
-    // find the one specified by the request.
-    //
-
-    Status = NetpIp4FindLinkForMulticastRequest(Socket->Network,
-                                                Request,
-                                                &LinkResult);
-
-    if (!KSUCCESS(Status)) {
-        Status = STATUS_NO_SUCH_DEVICE;
-        goto LeaveMulticastGroupEnd;
-    }
-
-    //
-    // Search through the multicast groups for a matching entry.
-    //
-
-    Status = NetpIp4AcquireMulticastLock(SocketInformation);
-    if (!KSUCCESS(Status)) {
-        goto LeaveMulticastGroupEnd;
-    }
-
-    LockHeld = TRUE;
-    Status = STATUS_INVALID_ADDRESS;
-    CurrentEntry = SocketInformation->MulticastGroupList.Next;
-    while (CurrentEntry != &(SocketInformation->MulticastGroupList)) {
-        Group = LIST_VALUE(CurrentEntry, IP4_MULTICAST_GROUP, ListEntry);
-        if ((Group->MulticastAddress == Request->Address) &&
-            (Group->Link == LinkResult.Link) &&
-            (Group->LinkAddress == LinkResult.LinkAddress)) {
-
-            Status = STATUS_SUCCESS;
-            break;
-        }
-
-        CurrentEntry = CurrentEntry->Next;
-    }
-
-    if (!KSUCCESS(Status)) {
-        goto LeaveMulticastGroupEnd;
-    }
-
-    //
-    // Remove the group from the list and mark the group for destruction.
-    //
-
-    LIST_REMOVE(&(Group->ListEntry));
-    NetpIp4ReleaseMulticastLock(SocketInformation);
-    LockHeld = FALSE;
-    DestroyGroup = Group;
-
-    //
-    // Now notify IGMP that this socket has left the group. IGMP should not be
-    // failing at this point.
-    //
-
-    Protocol = NetGetProtocolEntry(SOCKET_INTERNET_PROTOCOL_IGMP);
-    if (Protocol == NULL) {
-        Status = STATUS_NOT_SUPPORTED_BY_PROTOCOL;
-        goto LeaveMulticastGroupEnd;
-    }
-
-    IgmpRequest.Link = Group->Link;
-    IgmpRequest.LinkAddress = Group->LinkAddress;
-    IgmpRequest.MulticastAddress = Group->MulticastAddress;
-    RequestSize = sizeof(SOCKET_IGMP_MULTICAST_REQUEST);
-    Status = Protocol->Interface.GetSetInformation(
-                                           Socket,
-                                           SocketInformationIgmp,
-                                           SocketIgmpOptionLeaveMulticastGroup,
-                                           &IgmpRequest,
-                                           &RequestSize,
-                                           TRUE);
-
-    if (!KSUCCESS(Status)) {
-        goto LeaveMulticastGroupEnd;
-    }
-
-LeaveMulticastGroupEnd:
-    if (LockHeld != FALSE) {
-        NetpIp4ReleaseMulticastLock(SocketInformation);
-    }
-
-    if (LinkResult.Link != NULL) {
-        NetLinkReleaseReference(LinkResult.Link);
-    }
-
-    if (DestroyGroup != NULL) {
-        NetLinkReleaseReference(Group->Link);
-        MmFreePagedPool(Group);
-    }
-
-    return Status;
-}
-
-VOID
-NetpIp4DestroyMulticastGroups (
-    PNET_SOCKET Socket
-    )
-
-/*++
-
-Routine Description:
-
-    This routine destroys all multicast groups that the given socket joined. It
-    notifies IGMP that the socket is leaving the group and then destroys the
-    group structure.
-
-Arguments:
-
-    Socket - Supplies a pointer to the socket meant to destroy its multicast
-        groups.
-
-Return Value:
-
-    None.
-
---*/
-
-{
-
-    PIP4_MULTICAST_GROUP Group;
-    PLIST_ENTRY MulticastGroupList;
-    PNET_PROTOCOL_ENTRY Protocol;
-    SOCKET_IGMP_MULTICAST_REQUEST Request;
-    UINTN RequestSize;
-    PIP4_SOCKET_INFORMATION SocketInformation;
-
-    ASSERT(Socket->NetworkSocketInformation != NULL);
-
-    SocketInformation = Socket->NetworkSocketInformation;
-    MulticastGroupList = &(SocketInformation->MulticastGroupList);
-    if (LIST_EMPTY(MulticastGroupList) != FALSE) {
-        goto DestroyMulticastGroupsEnd;
-    }
-
-    ASSERT(SocketInformation->MulticastLock != NULL);
-
-    Protocol = NetGetProtocolEntry(SOCKET_INTERNET_PROTOCOL_IGMP);
-    if (Protocol == NULL) {
-        goto DestroyMulticastGroupsEnd;
-    }
-
-    //
-    // Run through the local list, leave each multicast group and destroy the
-    // group structures.
-    //
-
-    while (LIST_EMPTY(MulticastGroupList) == FALSE) {
-        Group = LIST_VALUE(MulticastGroupList->Next,
-                           IP4_MULTICAST_GROUP,
-                           ListEntry);
-
-        LIST_REMOVE(&(Group->ListEntry));
-        Request.Link = Group->Link;
-        Request.LinkAddress = Group->LinkAddress;
-        Request.MulticastAddress = Group->MulticastAddress;
-        RequestSize = sizeof(SOCKET_IGMP_MULTICAST_REQUEST);
-        Protocol->Interface.GetSetInformation(
-                                           Socket,
-                                           SocketInformationIgmp,
-                                           SocketIgmpOptionLeaveMulticastGroup,
-                                           &Request,
-                                           &RequestSize,
-                                           TRUE);
-
-        NetLinkReleaseReference(Group->Link);
-        MmFreePagedPool(Group);
-    }
-
-DestroyMulticastGroupsEnd:
-    if (SocketInformation->MulticastLock != NULL) {
-        KeDestroyQueuedLock(SocketInformation->MulticastLock);
-    }
-
-    return;
-}
-
-KSTATUS
-NetpIp4FindLinkForMulticastRequest (
-    PNET_NETWORK_ENTRY Network,
-    PSOCKET_IP4_MULTICAST_REQUEST Request,
-    PNET_LINK_LOCAL_ADDRESS LinkResult
-    )
-
-/*++
-
-Routine Description:
-
-    This routine searches for a network link that matches the given multicast
-    request. If the any address is supplied, then the multicast address will be
-    used to find a link that can reach it. A reference is taken on the returned
-    network link. The caller is responsible for releasing the reference.
-
-Arguments:
-
-    Network - Supplies a pointer to the network to which the address belongs.
-        This should be a pointer to the IPv4 network entry.
-
-    Request - Supplies the multicast request for which a link needs to be
-        found.
-
-    LinkResult - Supplies a pointer that receives the link information,
-        including the link, link address entry, and associated local address.
-
-Return Value:
-
-    Status code.
-
---*/
-
-{
-
-    IP4_ADDRESS LocalAddress;
-    IP4_ADDRESS RemoteAddress;
-    KSTATUS Status;
-
-    //
-    // If the any address is supplied, find a link that can reach the multicast
-    // address.
-    //
-
-    if (Request->Interface == 0) {
-        RtlZeroMemory(&RemoteAddress, sizeof(IP4_ADDRESS));
-        RemoteAddress.Domain = NetDomainIp4;
-        RemoteAddress.Address = Request->Address;
-        Status = NetFindLinkForRemoteAddress((PNETWORK_ADDRESS)&RemoteAddress,
-                                             LinkResult);
-
-        if (KSUCCESS(Status)) {
-            goto FindLinkForMulticastRequest;
-        }
-    }
-
-    //
-    // Otherwise a link that matches the given IPv4 address must be found.
-    //
-
-    RtlZeroMemory(&LocalAddress, sizeof(IP4_ADDRESS));
-    LocalAddress.Domain = NetDomainIp4;
-    LocalAddress.Address = Request->Interface;
-    Status = NetFindLinkForLocalAddress((PNETWORK_ADDRESS)&LocalAddress,
-                                        NULL,
-                                        LinkResult);
-
-    if (!KSUCCESS(Status)) {
-        goto FindLinkForMulticastRequest;
-    }
-
-FindLinkForMulticastRequest:
-    return Status;
-}
-
-KSTATUS
-NetpIp4AcquireMulticastLock (
-    PIP4_SOCKET_INFORMATION SocketInformation
-    )
-
-/*++
-
-Routine Description:
-
-    This routine acquires the given socket information's multicast lock,
-    allocating it on the fly if it does not already exist. This is done so most
-    sockets don't have to allocate the lock, as most sockets don't perform
-    multicast actions.
-
-Arguments:
-
-    SocketInformation - Supplies a pointer to the IPv4 socket information whose
-        multicast lock is to be acquired.
-
-Return Value:
-
-    Status code.
-
---*/
-
-{
-
-    PQUEUED_LOCK NewLock;
-    PQUEUED_LOCK OldLock;
-    KSTATUS Status;
-
-    //
-    // If there is no multicast lock. Create one before going any further. This
-    // is done on the fly so that most sockets don't need to allocate the lock
-    // resource.
-    //
-
-    if (SocketInformation->MulticastLock == NULL) {
-        NewLock = KeCreateQueuedLock();
-        if (NewLock == NULL) {
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto AcquireMulticastLockEnd;
-        }
-
-        //
-        // Try to exchange the lock into place.
-        //
-
-        OldLock = (PQUEUED_LOCK)RtlAtomicCompareExchange(
-                                   (PUINTN)&(SocketInformation->MulticastLock),
-                                   (UINTN)NewLock,
-                                   (UINTN)NULL);
-
-        if (OldLock != NULL) {
-            KeDestroyQueuedLock(NewLock);
-        }
-    }
-
-    ASSERT(SocketInformation->MulticastLock != NULL);
-
-    KeAcquireQueuedLock(SocketInformation->MulticastLock);
-    Status = STATUS_SUCCESS;
-
-AcquireMulticastLockEnd:
-    return Status;
-}
-
-VOID
-NetpIp4ReleaseMulticastLock (
-    PIP4_SOCKET_INFORMATION SocketInformation
-    )
-
-/*++
-
-Routine Description:
-
-    This routine releases the multicast lock for the given socket information.
-
-Arguments:
-
-    SocketInformation - Supplies a pointer to an IPv4 sockets information.
-
-Return Value:
-
-    None.
-
---*/
-
-{
-
-    ASSERT(SocketInformation->MulticastLock != NULL);
-
-    KeReleaseQueuedLock(SocketInformation->MulticastLock);
-    return;
-}
-

+ 50 - 11
drivers/net/netcore/ipv6/ip6.c

@@ -172,10 +172,10 @@ NetpIp6ConfigureLinkAddress (
     BOOL Configure
     );
 
-USHORT
-NetpIp6ChecksumData (
-    PSHORT Data,
-    ULONG Length
+KSTATUS
+NetpIp6JoinLeaveMulticastGroup (
+    PNET_NETWORK_MULTICAST_REQUEST Request,
+    BOOL Join
     );
 
 KSTATUS
@@ -211,11 +211,11 @@ NET_NETWORK_ENTRY NetIp6Network = {
         NetpIp6ProcessReceivedData,
         NetpIp6PrintAddress,
         NetpIp6GetSetInformation,
-        NULL,
         NetpIp6GetAddressType,
         NetpIp6SendTranslationRequest,
         NetpIp6ChecksumPseudoHeader,
-        NetpIp6ConfigureLinkAddress
+        NetpIp6ConfigureLinkAddress,
+        NetpIp6JoinLeaveMulticastGroup
     }
 };
 
@@ -300,9 +300,9 @@ Return Value:
     PhysicalAddress = &(Link->Properties.PhysicalAddress);
 
     //
-    // This current only supports creating an EUI-64 based interface identifier
-    // from 48-bit MAC addresses. If a different data link layer is added, this
-    // work probably needs to be farmed out to each data link layer.
+    // This currently only supports creating an EUI-64 based interface
+    // identifier from 48-bit MAC addresses. If a different data link layer is
+    // added, this work probably needs to be farmed out to each data link layer.
     //
 
     ASSERT((PhysicalAddress->Domain == NetDomainEthernet) ||
@@ -451,7 +451,7 @@ Return Value:
         NewSocket->PacketSizeInformation.HeaderSize += sizeof(IP6_HEADER);
     }
 
-    return STATUS_SUCCESS;
+    return NetInitializeMulticastSocket(NewSocket);
 }
 
 VOID
@@ -478,6 +478,7 @@ Return Value:
 
 {
 
+    NetDestroyMulticastSocket(Socket);
     return;
 }
 
@@ -1502,7 +1503,8 @@ Routine Description:
 
 Arguments:
 
-    Link - Supplies a pointer to the network link to which the address is bound.
+    Link - Supplies an optional pointer to the network link to which the
+        address is bound.
 
     LinkAddressEntry - Supplies an optional pointer to a network link address
         entry to use while classifying the address.
@@ -1694,6 +1696,43 @@ Return Value:
     return STATUS_NOT_IMPLEMENTED;
 }
 
+KSTATUS
+NetpIp6JoinLeaveMulticastGroup (
+    PNET_NETWORK_MULTICAST_REQUEST Request,
+    BOOL Join
+    )
+
+/*++
+
+Routine Description:
+
+    This routine joins or leaves a multicast group using a network-specific
+    protocol.
+
+Arguments:
+
+    Request - Supplies a pointer to the multicast group join/leave request.
+
+    Join - Supplies a boolean indicating whether to join (TRUE) or leave
+        (FALSE) the multicast group.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+{
+
+    //
+    // TODO: Implement MLD.
+    //
+
+    ASSERT(FALSE);
+
+    return STATUS_NOT_IMPLEMENTED;
+}
+
 //
 // --------------------------------------------------------- Internal Functions
 //

+ 1294 - 0
drivers/net/netcore/mcast.c

@@ -0,0 +1,1294 @@
+/*++
+
+Copyright (c) 2017 Minoca Corp.
+
+    This file is licensed under the terms of the GNU General Public License
+    version 3. Alternative licensing terms are available. Contact
+    info@minocacorp.com for details. See the LICENSE file at the root of this
+    project for complete licensing information.
+
+Module Name:
+
+    mcast.c
+
+Abstract:
+
+    This module implements generic multicast support for sockets and links.
+
+Author:
+
+    Chris Stevens 25-Aug-2017
+
+Environment:
+
+    Kernel
+
+--*/
+
+//
+// ------------------------------------------------------------------- Includes
+//
+
+#include <minoca/kernel/driver.h>
+#include "netcore.h"
+
+//
+// --------------------------------------------------------------------- Macros
+//
+
+//
+// ---------------------------------------------------------------- Definitions
+//
+
+//
+// ------------------------------------------------------ Data Type Definitions
+//
+
+//
+// ----------------------------------------------- Internal Function Prototypes
+//
+
+KSTATUS
+NetpFindLinkForMulticastRequest (
+    PNET_NETWORK_ENTRY Network,
+    PNET_SOCKET_MULTICAST_REQUEST Request,
+    PNET_LINK_LOCAL_ADDRESS LinkResult
+    );
+
+KSTATUS
+NetpUpdateMulticastAddressFilters (
+    PNET_LINK Link
+    );
+
+PNET_SOCKET_MULTICAST_GROUP
+NetpFindSocketMulticastGroup (
+    PNET_SOCKET Socket,
+    PNET_LINK Link,
+    PNET_LINK_ADDRESS_ENTRY LinkAddress,
+    PNETWORK_ADDRESS MulticastAddress
+    );
+
+PNET_SOCKET_MULTICAST_GROUP
+NetpCreateSocketMulticastGroup (
+    PNET_LINK Link,
+    PNET_LINK_ADDRESS_ENTRY LinkAddress,
+    PNETWORK_ADDRESS MulticastAddress
+    );
+
+VOID
+NetpDestroySocketMulticastGroup (
+    PNET_SOCKET_MULTICAST_GROUP Group
+    );
+
+KSTATUS
+NetpAcquireSocketMulticastLock (
+    PNET_SOCKET Socket
+    );
+
+VOID
+NetpReleaseSocketMulticastLock (
+    PNET_SOCKET Socket
+    );
+
+PNET_LINK_MULTICAST_GROUP
+NetpFindLinkMulticastGroup (
+    PNET_LINK Link,
+    PNET_LINK_ADDRESS_ENTRY LinkAddress,
+    PNETWORK_ADDRESS MulticastAddress
+    );
+
+//
+// -------------------------------------------------------------------- Globals
+//
+
+//
+// ------------------------------------------------------------------ Functions
+//
+
+NET_API
+KSTATUS
+NetInitializeMulticastSocket (
+    PNET_SOCKET Socket
+    )
+
+/*++
+
+Routine Description:
+
+    This routine initializes a network socket's multicast information.
+
+Arguments:
+
+    Socket - Supplies a pointer to the network socket to initialize.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+{
+
+    ASSERT(Socket->MulticastLock == NULL);
+    ASSERT(Socket->MulticastInterface.LinkInformation.Link == NULL);
+
+    RtlAtomicOr32(&(Socket->Flags), NET_SOCKET_FLAG_MULTICAST_LOOPBACK);
+    INITIALIZE_LIST_HEAD(&(Socket->MulticastGroupList));
+    return STATUS_SUCCESS;
+}
+
+NET_API
+VOID
+NetDestroyMulticastSocket (
+    PNET_SOCKET Socket
+    )
+
+/*++
+
+Routine Description:
+
+    This routine destroys all the multicast state associated with the given
+    socket.
+
+Arguments:
+
+    Socket - Supplies a pointer to the socket whose multicast state is to be
+        destroyed.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    PNET_SOCKET_MULTICAST_GROUP Group;
+
+    if (LIST_EMPTY(&(Socket->MulticastGroupList)) != FALSE) {
+        goto DestroyMulticastSocket;
+    }
+
+    ASSERT(Socket->MulticastLock != NULL);
+
+    //
+    // Run through the local list, leave each multicast group and destroy the
+    // group structures.
+    //
+
+    while (LIST_EMPTY(&(Socket->MulticastGroupList)) == FALSE) {
+        Group = LIST_VALUE(Socket->MulticastGroupList.Next,
+                           NET_SOCKET_MULTICAST_GROUP,
+                           ListEntry);
+
+        LIST_REMOVE(&(Group->ListEntry));
+        NetpLeaveLinkMulticastGroup(Group->Link,
+                                    Group->LinkAddress,
+                                    &(Group->MulticastAddress));
+
+        NetpDestroySocketMulticastGroup(Group);
+    }
+
+DestroyMulticastSocket:
+    if (Socket->MulticastLock != NULL) {
+        KeDestroyQueuedLock(Socket->MulticastLock);
+    }
+
+    return;
+}
+
+NET_API
+KSTATUS
+NetJoinSocketMulticastGroup (
+    PNET_SOCKET Socket,
+    PNET_SOCKET_MULTICAST_REQUEST Request
+    )
+
+/*++
+
+Routine Description:
+
+    This routine joins the given socket to a multicast group.
+
+Arguments:
+
+    Socket - Supplies a pointer to a socket.
+
+    Request - Supplies a pointer to the multicast join request. This stores
+        the address of the multicast group to join along with interface
+        information to indicate which link should join the group.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+{
+
+    PNET_SOCKET_MULTICAST_GROUP Group;
+    NET_LINK_LOCAL_ADDRESS LinkResult;
+    BOOL LockHeld;
+    PNET_NETWORK_ENTRY Network;
+    PNET_SOCKET_MULTICAST_GROUP NewGroup;
+    KSTATUS Status;
+
+    LinkResult.Link = NULL;
+    LockHeld = FALSE;
+    Network = Socket->Network;
+    NewGroup = NULL;
+
+    //
+    // Attempt to find a network link that can reach the multicast address, or
+    // find the one specified by the request.
+    //
+
+    Status = NetpFindLinkForMulticastRequest(Network, Request, &LinkResult);
+    if (!KSUCCESS(Status)) {
+        Status = STATUS_NO_SUCH_DEVICE;
+        goto SocketJoinMulticastGroupEnd;
+    }
+
+    Status = NetpAcquireSocketMulticastLock(Socket);
+    if (!KSUCCESS(Status)) {
+        goto SocketJoinMulticastGroupEnd;
+    }
+
+    LockHeld = TRUE;
+
+    //
+    // Check to see if this socket already joined the group.
+    //
+
+    Group = NetpFindSocketMulticastGroup(Socket,
+                                         LinkResult.Link,
+                                         LinkResult.LinkAddress,
+                                         &(Request->MulticastAddress));
+
+    if (Group != NULL) {
+        Status = STATUS_ADDRESS_IN_USE;
+        goto SocketJoinMulticastGroupEnd;
+    }
+
+    //
+    // Prepare for success and allocate a new socket multicast group.
+    //
+
+    NewGroup = NetpCreateSocketMulticastGroup(LinkResult.Link,
+                                              LinkResult.LinkAddress,
+                                              &(Request->MulticastAddress));
+
+    if (NewGroup == NULL) {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto SocketJoinMulticastGroupEnd;
+    }
+
+    //
+    // Before officially adding the multicast group to the socket, make sure
+    // the link joins the multicast group as well. This requires updating the
+    // hardware filters and sending network-specific messages to alert routers
+    // that this node is joining the multicast group. This all must happen with
+    // the socket's multicast link lock held to serialze with other join and
+    // leave requests.
+    //
+
+    Status = NetpJoinLinkMulticastGroup(LinkResult.Link,
+                                        LinkResult.LinkAddress,
+                                        &(Request->MulticastAddress));
+
+    if (!KSUCCESS(Status)) {
+        goto SocketJoinMulticastGroupEnd;
+    }
+
+    INSERT_BEFORE(&(NewGroup->ListEntry),
+                  &(Socket->MulticastGroupList));
+
+SocketJoinMulticastGroupEnd:
+    if (LockHeld != FALSE) {
+        NetpReleaseSocketMulticastLock(Socket);
+    }
+
+    if (LinkResult.Link != NULL) {
+        NetLinkReleaseReference(LinkResult.Link);
+    }
+
+    if (!KSUCCESS(Status)) {
+        if (NewGroup != NULL) {
+            NetpDestroySocketMulticastGroup(NewGroup);
+        }
+    }
+
+    return Status;
+}
+
+NET_API
+KSTATUS
+NetLeaveSocketMulticastGroup (
+    PNET_SOCKET Socket,
+    PNET_SOCKET_MULTICAST_REQUEST Request
+    )
+
+/*++
+
+Routine Description:
+
+    This routine removes the given socket from a multicast group.
+
+Arguments:
+
+    Socket - Supplies a pointer to a socket.
+
+    Request - Supplies a pointer to the multicast leave request. This stores
+        the multicast group address to leave and the address of the interface
+        on which the socket joined the group.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+{
+
+    PNET_SOCKET_MULTICAST_GROUP Group;
+    PNET_NETWORK_ENTRY Network;
+    NET_LINK_LOCAL_ADDRESS LinkResult;
+    BOOL LockHeld;
+    KSTATUS Status;
+
+    Group = NULL;
+    LinkResult.Link = NULL;
+    LockHeld = FALSE;
+    Network = Socket->Network;
+
+    //
+    // If the multicast group list is empty, then this socket never joined any
+    // multicast groups.
+    //
+
+    if (LIST_EMPTY(&(Socket->MulticastGroupList)) != FALSE) {
+        Status = STATUS_INVALID_ADDRESS;
+        goto SocketLeaveMulticastGroupEnd;
+    }
+
+    ASSERT(Socket->MulticastLock != NULL);
+
+    //
+    // Attempt to find a network link that can reach the multicast address, or
+    // find the one specified by the request.
+    //
+
+    Status = NetpFindLinkForMulticastRequest(Network, Request, &LinkResult);
+    if (!KSUCCESS(Status)) {
+        Status = STATUS_NO_SUCH_DEVICE;
+        goto SocketLeaveMulticastGroupEnd;
+    }
+
+    //
+    // Search through the multicast groups for a matching entry.
+    //
+
+    Status = NetpAcquireSocketMulticastLock(Socket);
+    if (!KSUCCESS(Status)) {
+        goto SocketLeaveMulticastGroupEnd;
+    }
+
+    LockHeld = TRUE;
+    Group = NetpFindSocketMulticastGroup(Socket,
+                                         LinkResult.Link,
+                                         LinkResult.LinkAddress,
+                                         &(Request->MulticastAddress));
+
+    if (Group == NULL) {
+        Status = STATUS_INVALID_ADDRESS;
+        goto SocketLeaveMulticastGroupEnd;
+    }
+
+    //
+    // Notify the link that this socket is leaving the group. This will trigger
+    // any network-specific protocol actions. The socket's multicast lock is
+    // held over this operation, but there shouldn't be high contention on an
+    // individual socket's lock.
+    //
+
+    Status = NetpLeaveLinkMulticastGroup(Group->Link,
+                                         Group->LinkAddress,
+                                         &(Group->MulticastAddress));
+
+    if (!KSUCCESS(Status)) {
+        goto SocketLeaveMulticastGroupEnd;
+    }
+
+    //
+    // Remove the group from the list.
+    //
+
+    LIST_REMOVE(&(Group->ListEntry));
+    NetpDestroySocketMulticastGroup(Group);
+
+SocketLeaveMulticastGroupEnd:
+    if (LockHeld != FALSE) {
+        NetpReleaseSocketMulticastLock(Socket);
+    }
+
+    if (LinkResult.Link != NULL) {
+        NetLinkReleaseReference(LinkResult.Link);
+    }
+
+    return Status;
+}
+
+NET_API
+KSTATUS
+NetSetSocketMulticastInterface (
+    PNET_SOCKET Socket,
+    PNET_SOCKET_MULTICAST_REQUEST Request
+    )
+
+/*++
+
+Routine Description:
+
+    This routine sets a socket's default multicast interface.
+
+Arguments:
+
+    Socket - Supplies a pointer a socket.
+
+    Request - Supplies a pointer to the request which dictates the default
+        interface.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+{
+
+    NET_ADDRESS_TYPE AddressType;
+    NET_LINK_LOCAL_ADDRESS LinkResult;
+    NET_SOCKET_LINK_OVERRIDE NewInterface;
+    PNET_LINK OldInterfaceLink;
+    KSTATUS Status;
+
+    NewInterface.LinkInformation.Link = NULL;
+    LinkResult.Link = NULL;
+
+    //
+    // Find the appropriate link and link address entry for the specified
+    // interface. If no interface is specified (an ID of zero and the
+    // unspecified interface address), then reset the multicast interface.
+    //
+
+    AddressType = NetAddressUnicast;
+    if (Request->InterfaceId == 0) {
+        AddressType = Socket->Network->Interface.GetAddressType(
+                                                 NULL,
+                                                 NULL,
+                                                 &(Request->InterfaceAddress));
+    }
+
+    if (AddressType == NetAddressAny) {
+        RtlZeroMemory(&NewInterface, sizeof(NET_SOCKET_LINK_OVERRIDE));
+
+    } else {
+        Status = NetpFindLinkForMulticastRequest(Socket->Network,
+                                                 Request,
+                                                 &LinkResult);
+
+        if (!KSUCCESS(Status)) {
+            goto SetSocketMulticastInterface;
+        }
+
+        NetInitializeSocketLinkOverride(Socket, &LinkResult, &NewInterface);
+    }
+
+    Status = NetpAcquireSocketMulticastLock(Socket);
+    if (!KSUCCESS(Status)) {
+        goto SetSocketMulticastInterface;
+    }
+
+    OldInterfaceLink = Socket->MulticastInterface.LinkInformation.Link;
+    RtlCopyMemory(&(Socket->MulticastInterface),
+                  &NewInterface,
+                  sizeof(NET_SOCKET_LINK_OVERRIDE));
+
+    NetpReleaseSocketMulticastLock(Socket);
+    if (OldInterfaceLink != NULL) {
+        NetLinkReleaseReference(OldInterfaceLink);
+    }
+
+    //
+    // The link reference was passed to the socket's multicast interface.
+    //
+
+    NewInterface.LinkInformation.Link = NULL;
+
+SetSocketMulticastInterface:
+    if (NewInterface.LinkInformation.Link != NULL) {
+        NetLinkReleaseReference(NewInterface.LinkInformation.Link);
+    }
+
+    if (LinkResult.Link != NULL) {
+        NetLinkReleaseReference(LinkResult.Link);
+    }
+
+    return Status;
+}
+
+NET_API
+KSTATUS
+NetGetSocketMulticastInterface (
+    PNET_SOCKET Socket,
+    PNET_SOCKET_MULTICAST_REQUEST Request
+    )
+
+/*++
+
+Routine Description:
+
+    This routine gets a socket's default multicast interface.
+
+Arguments:
+
+    Socket - Supplies a pointer a socket.
+
+    Request - Supplies a pointer that receives the current interface.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+{
+
+    PNET_LINK Link;
+    KSTATUS Status;
+
+    Status = NetpAcquireSocketMulticastLock(Socket);
+    if (!KSUCCESS(Status)) {
+        goto GetSocketMulticastInterface;
+    }
+
+    RtlZeroMemory(&(Request->MulticastAddress), sizeof(NETWORK_ADDRESS));
+    RtlCopyMemory(&(Request->InterfaceAddress),
+                  &(Socket->MulticastInterface.LinkInformation.SendAddress),
+                  sizeof(NETWORK_ADDRESS));
+
+    Link = Socket->MulticastInterface.LinkInformation.Link;
+    Request->InterfaceId = IoGetDeviceNumericId(Link->Properties.Device);
+    NetpReleaseSocketMulticastLock(Socket);
+
+GetSocketMulticastInterface:
+    return Status;
+}
+
+KSTATUS
+NetpJoinLinkMulticastGroup (
+    PNET_LINK Link,
+    PNET_LINK_ADDRESS_ENTRY LinkAddress,
+    PNETWORK_ADDRESS MulticastAddress
+    )
+
+/*++
+
+Routine Description:
+
+    This routine joins the multicast group on a link. If this is the first
+    request to join the supplied multicast group on the link, then the hardware
+    is reprogrammed to include messages to the multicast group's physical layer
+    address and the network is invoked to announce the join via a
+    network-specific protocol.
+
+Arguments:
+
+    Link - Supplies a pointer to the network link joining the multicast group.
+
+    LinkAddress - Supplies a pointer to the link address entry via which the
+        link will join the group.
+
+    MulticastAddress - Supplies a pointer to the multicast address of the group
+        to join.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+{
+
+    PNET_LINK_MULTICAST_GROUP Group;
+    BOOL LockHeld;
+    PNET_NETWORK_ENTRY Network;
+    PNET_LINK_MULTICAST_GROUP NewGroup;
+    NET_NETWORK_MULTICAST_REQUEST Request;
+    KSTATUS Status;
+
+    LockHeld = FALSE;
+    Network = LinkAddress->Network;
+    NewGroup = NULL;
+
+    //
+    // This isn't going to get very far without network multicast support.
+    //
+
+    if (Network->Interface.JoinLeaveMulticastGroup == NULL) {
+        Status = STATUS_NOT_SUPPORTED_BY_PROTOCOL;
+        goto LinkJoinMulticastGroupEnd;
+    }
+
+    //
+    // Search the link for the multicast group. If a matching group is found,
+    // add to the count for this join request. A previous join already updated
+    // the hardware filters and kicked off the network-specific join protocol.
+    //
+
+    while (TRUE) {
+        KeAcquireQueuedLock(Link->QueuedLock);
+        LockHeld = TRUE;
+        Group = NetpFindLinkMulticastGroup(Link, LinkAddress, MulticastAddress);
+        if (Group != NULL) {
+            Group->JoinCount += 1;
+            Status = STATUS_SUCCESS;
+            goto LinkJoinMulticastGroupEnd;
+        }
+
+        //
+        // If a group is not found the first time, release the lock and create
+        // one before looping to search again.
+        //
+
+        if (NewGroup == NULL) {
+            KeReleaseQueuedLock(Link->QueuedLock);
+            LockHeld = FALSE;
+            NewGroup = MmAllocatePagedPool(sizeof(NET_LINK_MULTICAST_GROUP),
+                                           NET_CORE_ALLOCATION_TAG);
+
+            if (NewGroup == NULL) {
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto LinkJoinMulticastGroupEnd;
+            }
+
+            RtlZeroMemory(NewGroup, sizeof(NET_LINK_MULTICAST_GROUP));
+            NewGroup->LinkAddress = LinkAddress;
+            NewGroup->JoinCount = 1;
+            RtlCopyMemory(&(NewGroup->Address),
+                          MulticastAddress,
+                          sizeof(NETWORK_ADDRESS));
+
+            continue;
+        }
+
+        //
+        // No group was found a second time. Add the newly allocated group to
+        // the link's list.
+        //
+
+        INSERT_BEFORE(&(NewGroup->ListEntry), &(Link->MulticastGroupList));
+        break;
+    }
+
+    //
+    // The hardware filters needs to be updated. The filters are updated with
+    // the lock held as every group's address needs to be sent to the hardware.
+    // It would also be bad to have a second join call run through before the
+    // hardware is initialized.
+    //
+
+    Status = NetpUpdateMulticastAddressFilters(Link);
+    if (!KSUCCESS(Status)) {
+        LIST_REMOVE(&(NewGroup->ListEntry));
+        goto LinkJoinMulticastGroupEnd;
+    }
+
+    NewGroup = NULL;
+    KeReleaseQueuedLock(Link->QueuedLock);
+    LockHeld = FALSE;
+
+    //
+    // Invoke the network layer to communicate that this link has joined the
+    // multicast group. If this fails, make an attempt to leave the group.
+    //
+
+    Request.Link = Link;
+    Request.LinkAddress = LinkAddress;
+    Request.MulticastAddress = MulticastAddress;
+    Status = Network->Interface.JoinLeaveMulticastGroup(&Request, TRUE);
+    if (!KSUCCESS(Status)) {
+        NetpLeaveLinkMulticastGroup(Link, LinkAddress, MulticastAddress);
+        goto LinkJoinMulticastGroupEnd;
+    }
+
+LinkJoinMulticastGroupEnd:
+    if (LockHeld != FALSE) {
+        KeReleaseQueuedLock(Link->QueuedLock);
+    }
+
+    if (NewGroup != NULL) {
+        MmFreePagedPool(NewGroup);
+    }
+
+    return Status;
+}
+
+KSTATUS
+NetpLeaveLinkMulticastGroup (
+    PNET_LINK Link,
+    PNET_LINK_ADDRESS_ENTRY LinkAddress,
+    PNETWORK_ADDRESS MulticastAddress
+    )
+
+/*++
+
+Routine Description:
+
+    This routine removes a link from a multicast. If this is the last request
+    to leave a multicast group on the link, then the hardware is reprogrammed
+    to filter out messages to the multicast group and a network-specific
+    protocol is invoked to announce the link is leaving the group.
+
+Arguments:
+
+    Link - Supplies a pointer to the network link leaving the multicast group.
+
+    LinkAddress - Supplies a pointer to the link address entry via which the
+        link will leave the group.
+
+    MulticastAddress - Supplies a pointer to the multicast address of the group
+        to leave.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+{
+
+    PNET_LINK_MULTICAST_GROUP Group;
+    BOOL LockHeld;
+    PNET_NETWORK_ENTRY Network;
+    NET_NETWORK_MULTICAST_REQUEST Request;
+    KSTATUS Status;
+
+    LockHeld = FALSE;
+    Network = LinkAddress->Network;
+
+    //
+    // Search the link for the multicast group. If a matching group is not
+    // found then the request fails.
+    //
+
+    KeAcquireQueuedLock(Link->QueuedLock);
+    LockHeld = TRUE;
+    Group = NetpFindLinkMulticastGroup(Link, LinkAddress, MulticastAddress);
+    if (Group == NULL) {
+        Status = STATUS_INVALID_ADDRESS;
+        goto LinkLeaveMulticastGroupEnd;
+    }
+
+    //
+    // If this is not the last reference on the group, the call is successful,
+    // but takes no further action. The link as whole remains joined to the
+    // multicast group.
+    //
+
+    Group->JoinCount -= 1;
+    if (Group->JoinCount != 0) {
+        Status = STATUS_SUCCESS;
+        goto LinkLeaveMulticastGroupEnd;
+    }
+
+    //
+    // Otherwise it's time for the group to go.
+    //
+
+    LIST_REMOVE(&(Group->ListEntry));
+
+    //
+    // Now that the group is out of the list, update the filters.
+    //
+
+    Status = NetpUpdateMulticastAddressFilters(Link);
+    if (!KSUCCESS(Status)) {
+        Group->JoinCount = 1;
+        INSERT_BEFORE(&(Group->ListEntry), &(Link->MulticastGroupList));
+        goto LinkLeaveMulticastGroupEnd;
+    }
+
+    //
+    // Release the lock and trigger the network-specific work to announce that
+    // this link has left the group.
+    //
+
+    KeReleaseQueuedLock(Link->QueuedLock);
+    LockHeld = FALSE;
+    Request.Link = Link;
+    Request.LinkAddress = LinkAddress;
+    Request.MulticastAddress = MulticastAddress;
+    Network->Interface.JoinLeaveMulticastGroup(&Request, FALSE);
+    MmFreePagedPool(Group);
+
+LinkLeaveMulticastGroupEnd:
+    if (LockHeld != FALSE) {
+        KeReleaseQueuedLock(Link->QueuedLock);
+    }
+
+    return Status;
+}
+
+//
+// --------------------------------------------------------- Internal Functions
+//
+
+KSTATUS
+NetpFindLinkForMulticastRequest (
+    PNET_NETWORK_ENTRY Network,
+    PNET_SOCKET_MULTICAST_REQUEST Request,
+    PNET_LINK_LOCAL_ADDRESS LinkResult
+    )
+
+/*++
+
+Routine Description:
+
+    This routine searches for a network link that matches the given multicast
+    request. If the any address is supplied, then the multicast address will be
+    used to find a link that can reach it. A reference is taken on the returned
+    network link. The caller is responsible for releasing the reference.
+
+Arguments:
+
+    Network - Supplies a pointer to the network to which the addresses belong.
+
+    Request - Supplies the multicast request for which a link needs to be
+        found.
+
+    LinkResult - Supplies a pointer that receives the link information,
+        including the link, link address entry, and associated local address.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+{
+
+    NET_ADDRESS_TYPE AddressType;
+    PDEVICE Device;
+    PNET_LINK Link;
+    KSTATUS Status;
+
+    //
+    // The interface ID can be used to find the desired link to use for the
+    // multicast request.
+    //
+
+    Link = NULL;
+    if (Request->InterfaceId != 0) {
+        Device = IoGetDeviceByNumericId(Request->InterfaceId);
+        if (Device == NULL) {
+            Status = STATUS_NO_SUCH_DEVICE;
+            goto FindLinkForMulticastRequest;
+        }
+
+        Status = NetLookupLinkByDevice(Device, &Link);
+        if (!KSUCCESS(Status)) {
+            goto FindLinkForMulticastRequest;
+        }
+
+    } else if (Network->Interface.GetAddressType != NULL) {
+        AddressType = Network->Interface.GetAddressType(
+                                                 NULL,
+                                                 NULL,
+                                                 &(Request->InterfaceAddress));
+
+        //
+        // If the any address is supplied for the interface, find a link that
+        // can reach the multicast address.
+        //
+
+        if (AddressType == NetAddressAny) {
+            Status = NetFindLinkForRemoteAddress(&(Request->MulticastAddress),
+                                                 LinkResult);
+
+            if (KSUCCESS(Status)) {
+                goto FindLinkForMulticastRequest;
+            }
+        }
+    }
+
+    //
+    // Otherwise a link result that matches the given address must be found.
+    //
+
+    Status = NetFindLinkForLocalAddress(&(Request->InterfaceAddress),
+                                        Link,
+                                        LinkResult);
+
+    if (!KSUCCESS(Status)) {
+        goto FindLinkForMulticastRequest;
+    }
+
+FindLinkForMulticastRequest:
+    if (Link != NULL) {
+        NetLinkReleaseReference(Link);
+    }
+
+    return Status;
+}
+
+KSTATUS
+NetpUpdateMulticastAddressFilters (
+    PNET_LINK Link
+    )
+
+/*++
+
+Routine Description:
+
+    This routine updates the given link's address filtering based on the
+    multicast groups to which the link currently belongs. It will gather a list
+    of all the physical layer addresses that need to be enabled and pass them
+    to the hardware for it to update its filters. It falls back to enabling
+    promiscuous mode if the link does not support multicast address filtering.
+
+Arguments:
+
+    Link - Supplies a pointer to the link whose filters are to be updated.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+{
+
+    PNET_DEVICE_LINK_GET_SET_INFORMATION GetSetInformation;
+    ULONG PromiscuousMode;
+    UINTN PromiscuousModeSize;
+    KSTATUS Status;
+
+    ASSERT(KeIsQueuedLockHeld(Link->QueuedLock) != FALSE);
+
+    GetSetInformation = Link->Properties.Interface.GetSetInformation;
+
+    //
+    // Set the link into promiscuous mode if there are any groups. Otherwise
+    // turn it off. Promiscuous must be supported for the link to have made it
+    // this far.
+    //
+    // TODO: Implement real multicast address filtering.
+    //
+
+    ASSERT((Link->Properties.Capabilities &
+            NET_LINK_CAPABILITY_PROMISCUOUS_MODE) != 0);
+
+    PromiscuousMode = FALSE;
+    if (LIST_EMPTY(&(Link->MulticastGroupList)) != FALSE) {
+        PromiscuousMode = TRUE;
+    }
+
+    PromiscuousModeSize = sizeof(ULONG);
+    Status = GetSetInformation(Link->Properties.DeviceContext,
+                               NetLinkInformationPromiscuousMode,
+                               &PromiscuousMode,
+                               &PromiscuousModeSize,
+                               TRUE);
+
+    return Status;
+}
+
+PNET_SOCKET_MULTICAST_GROUP
+NetpFindSocketMulticastGroup (
+    PNET_SOCKET Socket,
+    PNET_LINK Link,
+    PNET_LINK_ADDRESS_ENTRY LinkAddress,
+    PNETWORK_ADDRESS MulticastAddress
+    )
+
+/*++
+
+Routine Description:
+
+    This routine finds a multicast group in a socket's list of multicast groups.
+
+Arguments:
+
+    Socket - Supplies a pointer to the socket to search.
+
+    Link - Supplies a pointer to the link associated with the multicast group.
+
+    LinkAddress - Supplies a pointer to the link address entry associated with
+        the multicast group.
+
+    MulticastAddress - Supplies a pointer to the multicast address of the group.
+
+Return Value:
+
+    Returns a pointer to a socket multicast group on success or NULL on failure.
+
+--*/
+
+{
+
+    PLIST_ENTRY CurrentEntry;
+    PNET_SOCKET_MULTICAST_GROUP Group;
+    COMPARISON_RESULT Result;
+
+    ASSERT(KeIsQueuedLockHeld(Socket->MulticastLock) != FALSE);
+
+    CurrentEntry = Socket->MulticastGroupList.Next;
+    while (CurrentEntry != &(Socket->MulticastGroupList)) {
+        Group = LIST_VALUE(CurrentEntry, NET_SOCKET_MULTICAST_GROUP, ListEntry);
+        if ((Group->Link == Link) && (Group->LinkAddress == LinkAddress)) {
+            Result = NetCompareNetworkAddresses(&(Group->MulticastAddress),
+                                                MulticastAddress);
+
+            if (Result == ComparisonResultSame) {
+                return Group;
+            }
+        }
+
+        CurrentEntry = CurrentEntry->Next;
+    }
+
+    return NULL;
+}
+
+PNET_SOCKET_MULTICAST_GROUP
+NetpCreateSocketMulticastGroup (
+    PNET_LINK Link,
+    PNET_LINK_ADDRESS_ENTRY LinkAddress,
+    PNETWORK_ADDRESS MulticastAddress
+    )
+
+/*++
+
+Routine Description:
+
+    This routine creates a socket multicast group.
+
+Arguments:
+
+    Link - Supplies a pointer to the link on which the socket joined the group.
+
+    LinkAddress - Supplies a pointer to the link address on which the socket
+        joined the group.
+
+    MulticastAddress - Supplies a pointer to the multicast group address.
+
+Return Value:
+
+    Returns a pointer to the new group on success or NULL on failure.
+
+--*/
+
+{
+
+    PNET_SOCKET_MULTICAST_GROUP NewGroup;
+
+    //
+    // Prepare for success and allocate a new socket multicast group.
+    //
+
+    NewGroup = MmAllocatePagedPool(sizeof(NET_SOCKET_MULTICAST_GROUP),
+                                   NET_CORE_ALLOCATION_TAG);
+
+    if (NewGroup == NULL) {
+        goto CreateSocketMulticastGroupEnd;
+    }
+
+    NetLinkAddReference(Link);
+    NewGroup->Link = Link;
+    NewGroup->LinkAddress = LinkAddress;
+    RtlCopyMemory(&(NewGroup->MulticastAddress),
+                  MulticastAddress,
+                  sizeof(NETWORK_ADDRESS));
+
+CreateSocketMulticastGroupEnd:
+    return NewGroup;
+}
+
+VOID
+NetpDestroySocketMulticastGroup (
+    PNET_SOCKET_MULTICAST_GROUP Group
+    )
+
+/*++
+
+Routine Description:
+
+    This routine destroys the given socket multicast group.
+
+Arguments:
+
+    Group - Supplies a pointer to the group to destroy.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    NetLinkReleaseReference(Group->Link);
+    MmFreePagedPool(Group);
+    return;
+}
+
+KSTATUS
+NetpAcquireSocketMulticastLock (
+    PNET_SOCKET Socket
+    )
+
+/*++
+
+Routine Description:
+
+    This routine acquires the given socket's multicast lock, allocating it on
+    the fly if it does not already exist. This is done so most sockets don't
+    have to allocate the lock, as most sockets don't perform multicast actions.
+
+Arguments:
+
+    Socket - Supplies a pointer to the network socket whose multicast lock is
+        to be acquired.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+{
+
+    PQUEUED_LOCK NewLock;
+    PQUEUED_LOCK OldLock;
+    KSTATUS Status;
+
+    //
+    // If there is no multicast lock. Create one before going any further. This
+    // is done on the fly so that most sockets don't need to allocate the lock
+    // resource.
+    //
+
+    if (Socket->MulticastLock == NULL) {
+        NewLock = KeCreateQueuedLock();
+        if (NewLock == NULL) {
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto AcquireMulticastLockEnd;
+        }
+
+        //
+        // Try to exchange the lock into place.
+        //
+
+        OldLock = (PQUEUED_LOCK)RtlAtomicCompareExchange(
+                                              (PUINTN)&(Socket->MulticastLock),
+                                              (UINTN)NewLock,
+                                              (UINTN)NULL);
+
+        if (OldLock != NULL) {
+            KeDestroyQueuedLock(NewLock);
+        }
+    }
+
+    ASSERT(Socket->MulticastLock != NULL);
+
+    KeAcquireQueuedLock(Socket->MulticastLock);
+    Status = STATUS_SUCCESS;
+
+AcquireMulticastLockEnd:
+    return Status;
+}
+
+VOID
+NetpReleaseSocketMulticastLock (
+    PNET_SOCKET Socket
+    )
+
+/*++
+
+Routine Description:
+
+    This routine releases the multicast lock for the given socket.
+
+Arguments:
+
+    Socket - Supplies a pointer to a network socket.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    ASSERT(Socket->MulticastLock != NULL);
+
+    KeReleaseQueuedLock(Socket->MulticastLock);
+    return;
+}
+
+PNET_LINK_MULTICAST_GROUP
+NetpFindLinkMulticastGroup (
+    PNET_LINK Link,
+    PNET_LINK_ADDRESS_ENTRY LinkAddress,
+    PNETWORK_ADDRESS MulticastAddress
+    )
+
+/*++
+
+Routine Description:
+
+    This routine finds a multicast group in a link's list of multicast groups.
+
+Arguments:
+
+    Link - Supplies a pointer to the link to search.
+
+    LinkAddress - Supplies a pointer to the link address entry associated with
+        the group.
+
+    MulticastAddress - Supplies a pointer to the multicast address of the group.
+
+Return Value:
+
+    Returns a pointer to a link multicast group on success or NULL on failure.
+
+--*/
+
+{
+
+    PLIST_ENTRY CurrentEntry;
+    PNET_LINK_MULTICAST_GROUP Group;
+    COMPARISON_RESULT Result;
+
+    ASSERT(KeIsQueuedLockHeld(Link->QueuedLock) != FALSE);
+
+    CurrentEntry = Link->MulticastGroupList.Next;
+    while (CurrentEntry != &(Link->MulticastGroupList)) {
+        Group = LIST_VALUE(CurrentEntry, NET_LINK_MULTICAST_GROUP, ListEntry);
+        if (Group->LinkAddress == LinkAddress) {
+            Result = NetCompareNetworkAddresses(&(Group->Address),
+                                                MulticastAddress);
+
+            if (Result == ComparisonResultSame) {
+                return Group;
+            }
+        }
+
+        CurrentEntry = CurrentEntry->Next;
+    }
+
+    return NULL;
+}
+

+ 65 - 0
drivers/net/netcore/netcore.h

@@ -268,6 +268,71 @@ Return Value:
 
 --*/
 
+KSTATUS
+NetpJoinLinkMulticastGroup (
+    PNET_LINK Link,
+    PNET_LINK_ADDRESS_ENTRY LinkAddress,
+    PNETWORK_ADDRESS MulticastAddress
+    );
+
+/*++
+
+Routine Description:
+
+    This routine joins the multicast group on a link. If this is the first
+    request to join the supplied multicast group on the link, then the hardware
+    is reprogrammed to include messages to the multicast group's physical layer
+    address and the network is invoked to announce the join via a
+    network-specific protocol.
+
+Arguments:
+
+    Link - Supplies a pointer to the network link joining the multicast group.
+
+    LinkAddress - Supplies a pointer to the link address entry via which the
+        link will join the group.
+
+    MulticastAddress - Supplies a pointer to the multicast address of the group
+        to join.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+KSTATUS
+NetpLeaveLinkMulticastGroup (
+    PNET_LINK Link,
+    PNET_LINK_ADDRESS_ENTRY LinkAddress,
+    PNETWORK_ADDRESS MulticastAddress
+    );
+
+/*++
+
+Routine Description:
+
+    This routine removes a link from a multicast. If this is the last request
+    to leave a multicast group on the link, then the hardware is reprogrammed
+    to filter out messages to the multicast group and a network-specific
+    protocol is invoked to announce the link is leaving the group.
+
+Arguments:
+
+    Link - Supplies a pointer to the network link leaving the multicast group.
+
+    LinkAddress - Supplies a pointer to the link address entry via which the
+        link will leave the group.
+
+    MulticastAddress - Supplies a pointer to the multicast address of the group
+        to leave.
+
+Return Value:
+
+    Status code.
+
+--*/
+
 //
 // Prototypes to the entry points for built in protocols.
 //

+ 3 - 14
drivers/net/netcore/tcp.c

@@ -7735,20 +7735,9 @@ Return Value:
     }
 
     NewTcpSocket->LingerTimeout = ListeningSocket->LingerTimeout;
-
-    //
-    // Copy any network specific socket options.
-    //
-
-    if (NewTcpSocket->NetSocket.Network->Interface.CopyInformation != NULL) {
-        Status = NewTcpSocket->NetSocket.Network->Interface.CopyInformation(
-                                                &(NewTcpSocket->NetSocket),
-                                                &(ListeningSocket->NetSocket));
-
-        if (!KSUCCESS(Status)) {
-            goto TcpHandleIncomingConnectionEnd;
-        }
-    }
+    NewTcpSocket->NetSocket.HopLimit = ListeningSocket->NetSocket.HopLimit;
+    NewTcpSocket->NetSocket.DifferentiatedServicesCodePoint =
+                    ListeningSocket->NetSocket.DifferentiatedServicesCodePoint;
 
     //
     // Re-parse any options coming from the SYN packet and set up the sequence

+ 2 - 27
include/minoca/net/igmp.h

@@ -49,11 +49,11 @@ Values:
     SocketIgmpOptionInvalid - Indicates an invalid IGMP socket option.
 
     SocketIgmpOptionJoinMulticastGroup - Indicates a request to join a
-        multicast group. This option takes a SOCKET_IGMP_MULTICAST_REQUEST
+        multicast group. This option takes a NET_NETWORK_MULTICAST_REQUEST
         structure.
 
     SocketIgmpOptionLeaveMulticastGroup - Indicates a request to leave a
-        multicast group. This option takes a SOCKET_IGMP_MULTICAST_REQUEST
+        multicast group. This option takes a NET_NETWORK_MULTICAST_REQUEST
         structure.
 
 --*/
@@ -64,31 +64,6 @@ typedef enum _SOCKET_IGMP_OPTION {
     SocketIgmpOptionLeaveMulticastGroup
 } SOCKET_IGMP_OPTION, *PSOCKET_IGMP_OPTION;
 
-/*++
-
-Structure Description:
-
-    This structure defines an IGMP request to join or leave a multicast group.
-
-Members:
-
-    Link - Supplies a pointer to the network link associated with the multicast
-        group. Requests will send IGMP notifications over this link and update
-        the address filters in this link's physical layer.
-
-    LinkAddress - Supplies a pointer to the link address entry with which the
-        multicast group is associated.
-
-    MulticastAddress - Supplies the IPv4 multicast group address.
-
---*/
-
-typedef struct _SOCKET_IGMP_MULTICAST_REQUEST {
-    PNET_LINK Link;
-    PNET_LINK_ADDRESS_ENTRY LinkAddress;
-    ULONG MulticastAddress;
-} SOCKET_IGMP_MULTICAST_REQUEST, *PSOCKET_IGMP_MULTICAST_REQUEST;
-
 //
 // -------------------------------------------------------------------- Globals
 //

+ 348 - 58
include/minoca/net/netdrv.h

@@ -191,13 +191,14 @@ Author:
 #define NET_SOCKET_FLAG_FORKED_LISTENER         0x00000080
 #define NET_SOCKET_FLAG_NETWORK_HEADER_INCLUDED 0x00000100
 #define NET_SOCKET_FLAG_KERNEL                  0x00000200
+#define NET_SOCKET_FLAG_MULTICAST_LOOPBACK      0x00000400
 
 //
 // Define the set of network socket flags that should be carried over to a
 // copied socket after a spawned connection.
 //
 
-#define NET_SOCKET_FLAGS_INHERIT_MASK       0x0000000F
+#define NET_SOCKET_FLAGS_INHERIT_MASK           0x0000040F
 
 //
 // Define the network buffer allocation flags.
@@ -644,6 +645,33 @@ typedef struct _NET_LINK_PROPERTIES {
     NET_DEVICE_LINK_INTERFACE Interface;
 } NET_LINK_PROPERTIES, *PNET_LINK_PROPERTIES;
 
+/*++
+
+Structure Description:
+
+    This structure defines a multicast group for a link.
+
+Members:
+
+    ListEntry - Stores an entry into a link's list of multicast groups.
+
+    LinkAddress - Stores a pointer to the link address on which the link is
+        joined to the multicast group.
+
+    JoinCount - Stores the number of times a join request has been made for
+        this multicast group.
+
+    Address - Stores the multicast address of the group.
+
+--*/
+
+typedef struct _NET_LINK_MULTICAST_GROUP {
+    LIST_ENTRY ListEntry;
+    PNET_LINK_ADDRESS_ENTRY LinkAddress;
+    ULONG JoinCount;
+    NETWORK_ADDRESS Address;
+} NET_LINK_MULTICAST_GROUP, *PNET_LINK_MULTICAST_GROUP;
+
 typedef struct _NET_DATA_LINK_ENTRY NET_DATA_LINK_ENTRY, *PNET_DATA_LINK_ENTRY;
 
 /*++
@@ -689,6 +717,9 @@ Members:
     AddressTranslationTree - Stores the tree containing translations between
         network addresses and physical addresses, keyed by network address.
 
+    MulticastGroupList - Stores a list of the multicast groups to which this
+        link belongs.
+
 --*/
 
 typedef struct _NET_LINK {
@@ -703,6 +734,7 @@ typedef struct _NET_LINK {
     NET_LINK_PROPERTIES Properties;
     PKEVENT AddressTranslationEvent;
     RED_BLACK_TREE AddressTranslationTree;
+    LIST_ENTRY MulticastGroupList;
 } NET_LINK, *PNET_LINK;
 
 typedef
@@ -1015,6 +1047,83 @@ typedef struct _NET_LINK_LOCAL_ADDRESS {
 
 /*++
 
+Structure Description:
+
+    This structure defines a multicast group join/leave request.
+
+Members:
+
+    MulticastAddress - Stores the multicast address of the group to join or
+        leave.
+
+    InterfaceAddress - Stores the address of the link interface over which the
+        multicast join/leave is requested.
+
+    InterfaceId- Stores the ID of the link interface over which the multicast
+        join/leave is requested.
+
+--*/
+
+typedef struct _NET_SOCKET_MULTICAST_REQUEST {
+    NETWORK_ADDRESS MulticastAddress;
+    NETWORK_ADDRESS InterfaceAddress;
+    DEVICE_ID InterfaceId;
+} NET_SOCKET_MULTICAST_REQUEST, *PNET_SOCKET_MULTICAST_REQUEST;
+
+/*++
+
+Structure Description:
+
+    This structure defines a multicast group for a socket.
+
+Members:
+
+    ListEntry - Stores pointers to the previous and next multicast groups in
+        the socket's list.
+
+    Link - Supplies a pointer to the network link to which the multicast group
+        is attached.
+
+    LinkAddress - Supplies a pointer to the link address entry with which the
+        multicast group is associated.
+
+    MulitcastAddress - Stores the multicast address of the group.
+
+--*/
+
+typedef struct _NET_SOCKET_MULTICAST_GROUP {
+    LIST_ENTRY ListEntry;
+    PNET_LINK Link;
+    PNET_LINK_ADDRESS_ENTRY LinkAddress;
+    NETWORK_ADDRESS MulticastAddress;
+} NET_SOCKET_MULTICAST_GROUP, *PNET_SOCKET_MULTICAST_GROUP;
+
+/*++
+
+Structure Description:
+
+    This structure defines a core networking socket link override. This stores
+    all the socket and link specific information needed to send a packet. This
+    can be used to send data from a link on behalf of a socket if the socket
+    is not yet bound to a link.
+
+Members:
+
+    LinkInformation - Stores the local address and its associated link and link
+        address entry.
+
+    PacketSizeInformation - Stores the packet size information bound by the
+        protocol, network and link layers.
+
+--*/
+
+typedef struct _NET_SOCKET_LINK_OVERRIDE {
+    NET_LINK_LOCAL_ADDRESS LinkInformation;
+    NET_PACKET_SIZE_INFORMATION PacketSizeInformation;
+} NET_SOCKET_LINK_OVERRIDE, *PNET_SOCKET_LINK_OVERRIDE;
+
+/*++
+
 Structure Description:
 
     This structure defines a core networking library socket.
@@ -1073,8 +1182,25 @@ Members:
         accepted connections that are allowed to accumulate before connections
         are refused. In the sockets API this is known as the backlog count.
 
-    NetworkSocketInformation - Stores an optional pointer to the network
-        layer's socket information.
+    HopLimit - Stores the hop limit (a.k.a. time-to-live) that is to be set in
+        the IP headers of every packet sent by this socket.
+
+    DifferentiatedServicesCodePoint - Stores the differentiated services code
+        point that is to be set in the IP header of every packet sent by this
+        socket.
+
+    MulticastHopLimit - Stores the hop limit (a.k.a. time-to-live) that is to
+        be set in the IP header of every multicast packet sent by this socket.
+
+    MulticastInterface - Stores the interface over which to send all multicast
+        packets. If this is not initialized, then a default interface is chosen
+        just as it would be for unicast packets.
+
+    MulticastLock - Supplies a pointer to the queued lock that protects access
+        to the multicast information.
+
+    MulticastGroupList - Stores the head of the list of multicast groups to
+        which the socket belongs.
 
 --*/
 
@@ -1100,33 +1226,14 @@ typedef struct _NET_SOCKET {
     PNET_LINK_ADDRESS_ENTRY LinkAddress;
     ULONG SendPacketCount;
     ULONG MaxIncomingConnections;
-    PVOID NetworkSocketInformation;
+    UCHAR HopLimit;
+    UCHAR DifferentiatedServicesCodePoint;
+    UCHAR MulticastHopLimit;
+    NET_SOCKET_LINK_OVERRIDE MulticastInterface;
+    volatile PQUEUED_LOCK MulticastLock;
+    LIST_ENTRY MulticastGroupList;
 } NET_SOCKET, *PNET_SOCKET;
 
-/*++
-
-Structure Description:
-
-    This structure defines a core networking socket link override. This stores
-    all the socket and link specific information needed to send a packet. This
-    can be used to send data from a link on behalf of a socket if the socket
-    is not yet bound to a link.
-
-Members:
-
-    LinkInformation - Stores the local address and its associated link and link
-        address entry.
-
-    PacketSizeInformation - Stores the packet size information bound by the
-        protocol, network and link layers.
-
---*/
-
-typedef struct _NET_SOCKET_LINK_OVERRIDE {
-    NET_LINK_LOCAL_ADDRESS LinkInformation;
-    NET_PACKET_SIZE_INFORMATION PacketSizeInformation;
-} NET_SOCKET_LINK_OVERRIDE, *PNET_SOCKET_LINK_OVERRIDE;
-
 typedef
 KSTATUS
 (*PNET_PROTOCOL_CREATE_SOCKET) (
@@ -1688,6 +1795,34 @@ struct _NET_PROTOCOL_ENTRY {
     NET_PROTOCOL_INTERFACE Interface;
 };
 
+/*++
+
+Structure Description:
+
+    This structure defines a request to join or leave a multicast group. It is
+    supplies to the network layer, as most networks have a specific protocol
+    used to join or leave a multicast group.
+
+Members:
+
+    Link - Stores a pointer to the link on which to join the multicast group or
+        the link to leave the multicast group.
+
+    LinkAddress - Stores a pointer to the link address entry on which to join
+        the multicast group or the link address entry to leave the multicast
+        group.
+
+    MulticastAddress - Stores a pointer to the address of the multicast group
+        to join or leave.
+
+--*/
+
+typedef struct _NET_NETWORK_MULTICAST_REQUEST {
+    PNET_LINK Link;
+    PNET_LINK_ADDRESS_ENTRY LinkAddress;
+    PNETWORK_ADDRESS MulticastAddress;
+} NET_NETWORK_MULTICAST_REQUEST, *PNET_NETWORK_MULTICAST_REQUEST;
+
 typedef
 KSTATUS
 (*PNET_NETWORK_INITIALIZE_LINK) (
@@ -2065,34 +2200,6 @@ Return Value:
 
 --*/
 
-typedef
-KSTATUS
-(*PNET_NETWORK_COPY_INFORMATION) (
-    PNET_SOCKET DestinationSocket,
-    PNET_SOCKET SourceSocket
-    );
-
-/*++
-
-Routine Description:
-
-    This routine copies socket information properties from the source socket to
-    the destination socket.
-
-Arguments:
-
-    DestinationSocket - Supplies a pointer to the socket whose information will
-        be overwritten with the source socket's information.
-
-    SourceSocket - Supplies a pointer to the socket whose information will
-        be copied to the destination socket.
-
-Return Value:
-
-    Status code.
-
---*/
-
 typedef
 NET_ADDRESS_TYPE
 (*PNET_NETWORK_GET_ADDRESS_TYPE) (
@@ -2110,7 +2217,8 @@ Routine Description:
 
 Arguments:
 
-    Link - Supplies a pointer to the network link to which the address is bound.
+    Link - Supplies an optional pointer to the network link to which the
+        address is bound.
 
     LinkAddressEntry - Supplies an optional pointer to a network link address
         entry to use while classifying the address.
@@ -2221,6 +2329,33 @@ Return Value:
 
 --*/
 
+typedef
+KSTATUS
+(*PNET_NETWORK_JOIN_LEAVE_MULTICAST_GROUP) (
+    PNET_NETWORK_MULTICAST_REQUEST Request,
+    BOOL Join
+    );
+
+/*++
+
+Routine Description:
+
+    This routine joins or leaves a multicast group using a network-specific
+    protocol.
+
+Arguments:
+
+    Request - Supplies a pointer to the multicast group join/leave request.
+
+    Join - Supplies a boolean indicating whether to join (TRUE) or leave
+        (FALSE) the multicast group.
+
+Return Value:
+
+    Status code.
+
+--*/
+
 /*++
 
 Structure Description:
@@ -2288,6 +2423,10 @@ Members:
         network-specific protocol to validate or assign an address for the link
         address entry (e.g. DHCP, NDP, DHCPv6).
 
+    JoinLeaveMulticastGroup - Stores a pointer to a function used to join
+        or leave a multicast group using a network-specific protocol (e.g.
+        IGMP, MLD).
+
 --*/
 
 typedef struct _NET_NETWORK_INTERFACE {
@@ -2304,11 +2443,11 @@ typedef struct _NET_NETWORK_INTERFACE {
     PNET_NETWORK_PROCESS_RECEIVED_DATA ProcessReceivedData;
     PNET_NETWORK_PRINT_ADDRESS PrintAddress;
     PNET_NETWORK_GET_SET_INFORMATION GetSetInformation;
-    PNET_NETWORK_COPY_INFORMATION CopyInformation;
     PNET_NETWORK_GET_ADDRESS_TYPE GetAddressType;
     PNET_NETWORK_SEND_TRANSLATION_REQUEST SendTranslationRequest;
     PNET_NETWORK_CHECKSUM_PSEUDO_HEADER ChecksumPseudoHeader;
     PNET_NETWORK_CONFIGURE_LINK_ADDRESS ConfigureLinkAddress;
+    PNET_NETWORK_JOIN_LEAVE_MULTICAST_GROUP JoinLeaveMulticastGroup;
 } NET_NETWORK_INTERFACE, *PNET_NETWORK_INTERFACE;
 
 /*++
@@ -3575,6 +3714,157 @@ Return Value:
 
 --*/
 
+NET_API
+KSTATUS
+NetInitializeMulticastSocket (
+    PNET_SOCKET Socket
+    );
+
+/*++
+
+Routine Description:
+
+    This routine initializes a network socket's multicast information.
+
+Arguments:
+
+    Socket - Supplies a pointer to the network socket to initialize.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+NET_API
+VOID
+NetDestroyMulticastSocket (
+    PNET_SOCKET Socket
+    );
+
+/*++
+
+Routine Description:
+
+    This routine destroys all the multicast state associated with the given
+    socket.
+
+Arguments:
+
+    Socket - Supplies a pointer to the socket whose multicast state is to be
+        destroyed.
+
+Return Value:
+
+    None.
+
+--*/
+
+NET_API
+KSTATUS
+NetJoinSocketMulticastGroup (
+    PNET_SOCKET Socket,
+    PNET_SOCKET_MULTICAST_REQUEST Request
+    );
+
+/*++
+
+Routine Description:
+
+    This routine adds the given socket to a multicast group.
+
+Arguments:
+
+    Socket - Supplies a pointer to a socket.
+
+    Request - Supplies a pointer to the multicast join request. This stores
+        the address of the multicast group to join along with interface
+        information to indicate which link should join the group.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+NET_API
+KSTATUS
+NetLeaveSocketMulticastGroup (
+    PNET_SOCKET Socket,
+    PNET_SOCKET_MULTICAST_REQUEST Request
+    );
+
+/*++
+
+Routine Description:
+
+    This routine removes the given socket from a multicast group.
+
+Arguments:
+
+    Socket - Supplies a pointer to a socket.
+
+    Request - Supplies a pointer to the multicast leave request. This stores
+        the multicast group address to leave and the address of the interface
+        on which the socket joined the group.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+NET_API
+KSTATUS
+NetSetSocketMulticastInterface (
+    PNET_SOCKET Socket,
+    PNET_SOCKET_MULTICAST_REQUEST Request
+    );
+
+/*++
+
+Routine Description:
+
+    This routine sets a socket's default multicast interface.
+
+Arguments:
+
+    Socket - Supplies a pointer a socket.
+
+    Request - Supplies a pointer to the request which dictates the default
+        interface.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+NET_API
+KSTATUS
+NetGetSocketMulticastInterface (
+    PNET_SOCKET Socket,
+    PNET_SOCKET_MULTICAST_REQUEST Request
+    );
+
+/*++
+
+Routine Description:
+
+    This routine gets a socket's default multicast interface.
+
+Arguments:
+
+    Socket - Supplies a pointer a socket.
+
+    Request - Supplies a pointer that receives the current interface.
+
+Return Value:
+
+    Status code.
+
+--*/
+
 //
 // Link-specific definitions.
 //