[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[win-pv-devel] [PATCH] Use multicast control if the backend supports it.



This required a bit of re-work in the transmitter code since using multicast
control necessitates sending dummy transmit requests with special extra
info fragments. As a knock-on I also re-worked the way the IP address table
is maintained, removing bits that were in the transmitter code into the
general frontend code.

Re-work was also required in the mac code since it is also necessary to
track addition and removal of individual multicast addresses, rather than
just handling a table update.

Signed-off-by: Paul Durrant <paul.durrant@xxxxxxxxxx>
---
 src/xenvif/frontend.c    |  317 ++++++++++---
 src/xenvif/frontend.h    |   12 +
 src/xenvif/mac.c         |  343 ++++++++++++--
 src/xenvif/mac.h         |   19 +-
 src/xenvif/transmitter.c | 1126 +++++++++++++++++++++++++++++++++-------------
 src/xenvif/transmitter.h |   27 +-
 src/xenvif/vif.c         |   19 +-
 7 files changed, 1435 insertions(+), 428 deletions(-)

diff --git a/src/xenvif/frontend.c b/src/xenvif/frontend.c
index b389b1c..69aed73 100644
--- a/src/xenvif/frontend.c
+++ b/src/xenvif/frontend.c
@@ -63,7 +63,6 @@ struct _XENVIF_FRONTEND {
     XENVIF_FRONTEND_STATE       State;
     BOOLEAN                     Online;
     KSPIN_LOCK                  Lock;
-    PXENVIF_THREAD              MibThread;
     PXENVIF_THREAD              EjectThread;
     KEVENT                      EjectEvent;
 
@@ -86,6 +85,10 @@ struct _XENVIF_FRONTEND {
 
     PXENVIF_FRONTEND_STATISTICS Statistics;
     ULONG                       StatisticsCount;
+
+    PXENVIF_THREAD              MibThread;
+    PSOCKADDR_INET              AddressTable;
+    ULONG                       AddressCount;
 };
 
 static const PCHAR
@@ -229,7 +232,7 @@ FrontendSetMaxQueues(
     if (NT_SUCCESS(status) && FrontendMaxQueues < Frontend->MaxQueues)
         Frontend->MaxQueues = FrontendMaxQueues;
 
-    Info("%u\n", Frontend->MaxQueues);
+    Info("%s: %u\n", __FrontendGetPath(Frontend), Frontend->MaxQueues);
 }
 
 static FORCEINLINE ULONG
@@ -503,50 +506,49 @@ found:
 
 static NTSTATUS
 FrontendInsertAddress(
-    IN OUT  PSOCKADDR_INET      *AddressTable,
-    IN      const SOCKADDR_INET *Address,
-    IN OUT  PULONG              AddressCount
+    IN  PXENVIF_FRONTEND    Frontend,
+    IN  const SOCKADDR_INET *Address
     )
 {
-    ULONG                       Index;
-    PSOCKADDR_INET              Table;
-    NTSTATUS                    status;
+    ULONG                   Index;
+    PSOCKADDR_INET          Table;
+    NTSTATUS                status;
 
     Trace("====>\n");
 
-    for (Index = 0; Index < *AddressCount; Index++) {
-        if ((*AddressTable)[Index].si_family != Address->si_family)
+    for (Index = 0; Index < Frontend->AddressCount; Index++) {
+        if (Frontend->AddressTable[Index].si_family != Address->si_family)
             continue;
 
         if (Address->si_family == AF_INET) {
-            if (RtlCompareMemory(&Address->Ipv4.sin_addr.s_addr,
-                                 &(*AddressTable)[Index].Ipv4.sin_addr.s_addr,
-                                 IPV4_ADDRESS_LENGTH) == IPV4_ADDRESS_LENGTH)
+            if (RtlEqualMemory(&Address->Ipv4.sin_addr.s_addr,
+                               
&Frontend->AddressTable[Index].Ipv4.sin_addr.s_addr,
+                                 IPV4_ADDRESS_LENGTH))
                 goto done;
         } else {
             ASSERT3U(Address->si_family, ==, AF_INET6);
 
-            if (RtlCompareMemory(&Address->Ipv6.sin6_addr.s6_addr,
-                                 
&(*AddressTable)[Index].Ipv6.sin6_addr.s6_addr,
-                                 IPV6_ADDRESS_LENGTH) == IPV6_ADDRESS_LENGTH)
+            if (RtlEqualMemory(&Address->Ipv6.sin6_addr.s6_addr,
+                               
&Frontend->AddressTable[Index].Ipv6.sin6_addr.s6_addr,
+                               IPV6_ADDRESS_LENGTH))
                 goto done;
         }
     }
 
     // We have an address we've not seen before so grow the table
-    Table = __FrontendAllocate(sizeof (SOCKADDR_INET) * (*AddressCount + 1));
+    Table = __FrontendAllocate(sizeof (SOCKADDR_INET) * 
(Frontend->AddressCount + 1));
 
     status = STATUS_NO_MEMORY;
     if (Table == NULL)
         goto fail1;
 
-    RtlCopyMemory(Table, *AddressTable, sizeof (SOCKADDR_INET) * 
*AddressCount);
-    Table[(*AddressCount)++] = *Address;
+    RtlCopyMemory(Table, Frontend->AddressTable, sizeof (SOCKADDR_INET) * 
Frontend->AddressCount);
 
-    if (*AddressTable != NULL)
-        __FrontendFree(*AddressTable);
+    if (Frontend->AddressCount != 0)
+        __FrontendFree(Frontend->AddressTable);
 
-    *AddressTable = Table;
+    Table[Frontend->AddressCount++] = *Address;
+    Frontend->AddressTable = Table;
 
 done:
     Trace("<====\n");
@@ -563,9 +565,7 @@ static NTSTATUS
 FrontendProcessAddressTable(
     IN  PXENVIF_FRONTEND            Frontend,
     IN  PMIB_UNICASTIPADDRESS_TABLE Table,
-    IN  NET_IFINDEX                 InterfaceIndex,
-    OUT PSOCKADDR_INET              *AddressTable,
-    OUT PULONG                      AddressCount
+    IN  NET_IFINDEX                 InterfaceIndex
     )
 {
     ULONG                           Index;
@@ -573,8 +573,12 @@ FrontendProcessAddressTable(
 
     UNREFERENCED_PARAMETER(Frontend);
 
-    *AddressTable = NULL;
-    *AddressCount = 0;
+    if (Frontend->AddressCount != 0) {
+        __FrontendFree(Frontend->AddressTable);
+
+        Frontend->AddressTable = NULL;
+        Frontend->AddressCount = 0;
+    }
 
     for (Index = 0; Index < Table->NumEntries; Index++) {
         PMIB_UNICASTIPADDRESS_ROW   Row = &Table->Table[Index];
@@ -586,9 +590,7 @@ FrontendProcessAddressTable(
             Row->Address.si_family != AF_INET6)
             continue;
 
-        status = FrontendInsertAddress(AddressTable,
-                                       &Row->Address,
-                                       AddressCount);
+        status = FrontendInsertAddress(Frontend, &Row->Address);
         if (!NT_SUCCESS(status))
             goto fail1;
     }
@@ -598,17 +600,12 @@ FrontendProcessAddressTable(
 fail1:
     Error("fail1 (%08x)\n", status);
 
-    if (*AddressTable != NULL)
-        __FrontendFree(*AddressTable);
-
     return status;
 }
 
 static NTSTATUS
 FrontendDumpAddressTable(
-    IN  PXENVIF_FRONTEND        Frontend,
-    IN  PSOCKADDR_INET          AddressTable,
-    IN  ULONG                   AddressCount
+    IN  PXENVIF_FRONTEND        Frontend
     )
 {
     PXENBUS_STORE_TRANSACTION   Transaction;
@@ -646,19 +643,19 @@ FrontendDumpAddressTable(
     IpVersion4Count = 0;
     IpVersion6Count = 0;
 
-    for (Index = 0; Index < AddressCount; Index++) {
-        switch (AddressTable[Index].si_family) {
+    for (Index = 0; Index < Frontend->AddressCount; Index++) {
+        switch (Frontend->AddressTable[Index].si_family) {
         case AF_INET: {
             IPV4_ADDRESS    Address;
-            CHAR            Node[sizeof ("ipv4/XXXXXXXX/addr")];
+            CHAR            Node[sizeof ("ipv4/address/XXXXXXXX")];
 
             RtlCopyMemory(Address.Byte,
-                          &AddressTable[Index].Ipv4.sin_addr.s_addr,
+                          &Frontend->AddressTable[Index].Ipv4.sin_addr.s_addr,
                           IPV4_ADDRESS_LENGTH);
 
             status = RtlStringCbPrintfA(Node,
                                         sizeof (Node),
-                                        "ipv4/%u/addr",
+                                        "ipv4/address/%u",
                                         IpVersion4Count);
             ASSERT(NT_SUCCESS(status));
 
@@ -687,15 +684,15 @@ FrontendDumpAddressTable(
         }
         case AF_INET6: {
             IPV6_ADDRESS    Address;
-            CHAR            Node[sizeof ("ipv6/XXXXXXXX/addr")];
+            CHAR            Node[sizeof ("ipv6/address/XXXXXXXX")];
 
             RtlCopyMemory(Address.Byte,
-                          &AddressTable[Index].Ipv6.sin6_addr.s6_addr,
+                          
&Frontend->AddressTable[Index].Ipv6.sin6_addr.s6_addr,
                           IPV6_ADDRESS_LENGTH);
 
             status = RtlStringCbPrintfA(Node,
                                         sizeof (Node),
-                                        "ipv6/%u/addr",
+                                        "ipv6/address/%u",
                                         IpVersion6Count);
             ASSERT(NT_SUCCESS(status));
 
@@ -848,8 +845,6 @@ FrontendMib(
         NET_IFINDEX                 InterfaceIndex;
         PMIB_UNICASTIPADDRESS_TABLE UnicastIpAddressTable;
         KIRQL                       Irql;
-        PSOCKADDR_INET              AddressTable;
-        ULONG                       AddressCount;
 
         Trace("waiting...\n");
 
@@ -892,22 +887,11 @@ FrontendMib(
 
         status = FrontendProcessAddressTable(Frontend,
                                              UnicastIpAddressTable,
-                                             InterfaceIndex,
-                                             &AddressTable,
-                                             &AddressCount);
+                                             InterfaceIndex);
         if (!NT_SUCCESS(status))
             goto unlock;
 
-        TransmitterUpdateAddressTable(__FrontendGetTransmitter(Frontend),
-                                      AddressTable,
-                                      AddressCount);
-
-        (VOID) FrontendDumpAddressTable(Frontend,
-                                        AddressTable,
-                                        AddressCount);
-
-        if (AddressCount != 0)
-            __FrontendFree(AddressTable);
+        (VOID) FrontendDumpAddressTable(Frontend);
 
 unlock:
         KeReleaseSpinLock(&Frontend->Lock, Irql);
@@ -920,6 +904,13 @@ loop:
             __FreeMibTable(IfTable);
     }
 
+    if (Frontend->AddressCount != 0) {
+        __FrontendFree(Frontend->AddressTable);
+
+        Frontend->AddressTable = NULL;
+        Frontend->AddressCount = 0;
+    }
+
     status = __CancelMibChangeNotify2(Handle);
     ASSERT(NT_SUCCESS(status));
 
@@ -948,6 +939,208 @@ fail1:
     return status;
 }
 
+NTSTATUS
+FrontendSetMulticastAddresses(
+    IN  PXENVIF_FRONTEND    Frontend,
+    IN  PETHERNET_ADDRESS   Address,
+    IN  ULONG               Count
+    )
+{
+    PXENVIF_TRANSMITTER     Transmitter;
+    PXENVIF_MAC             Mac;
+    KIRQL                   Irql;
+    PETHERNET_ADDRESS       MulticastAddress;
+    ULONG                   MulticastCount;
+    ULONG                   MulticastIndex;
+    ULONG                   Index;
+    NTSTATUS                status;
+
+    Transmitter = FrontendGetTransmitter(Frontend);
+    Mac = FrontendGetMac(Frontend);
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    status = MacQueryMulticastAddresses(Mac, NULL, &MulticastCount);
+    ASSERT3U(status, ==, STATUS_BUFFER_OVERFLOW);
+
+    if (MulticastCount != 0) {
+        MulticastAddress = __FrontendAllocate(sizeof (ETHERNET_ADDRESS) *
+                                              MulticastCount);
+
+        status = STATUS_NO_MEMORY;
+        if (MulticastAddress == NULL)
+            goto fail1;
+
+        status = MacQueryMulticastAddresses(Mac,
+                                            MulticastAddress,
+                                            &MulticastCount);
+        if (!NT_SUCCESS(status))
+            goto fail2;
+    } else
+        MulticastAddress = NULL;
+
+    for (Index = 0; Index < Count; Index++) {
+        BOOLEAN Found;
+
+        ASSERT(Address[Index].Byte[0] & 0x01);
+
+        Found = FALSE;
+
+        // If the multicast address has already been added and it
+        // appears in the updated list then we don't want to remove it.
+        for (MulticastIndex = 0;
+             MulticastIndex < MulticastCount;
+             MulticastIndex++) {
+            if (RtlEqualMemory(&Address[Index],
+                               &MulticastAddress[MulticastIndex],
+                               ETHERNET_ADDRESS_LENGTH)) {
+                Found = TRUE;
+                RtlZeroMemory(&MulticastAddress[MulticastIndex],
+                              ETHERNET_ADDRESS_LENGTH);
+                break;
+            }
+        }
+
+        if (!Found) {
+            (VOID) MacAddMulticastAddress(Mac, &Address[Index]);
+            (VOID) TransmitterQueueMulticastControl(Transmitter,
+                                                    &Address[Index],
+                                                    TRUE);
+        }
+    }
+
+    // Walk the multicast list removing any addresses not in the
+    // updated list
+    for (MulticastIndex = 0;
+         MulticastIndex < MulticastCount;
+         MulticastIndex++) {
+        if (!(MulticastAddress[MulticastIndex].Byte[0] & 0x01))
+            continue;
+
+        (VOID) TransmitterQueueMulticastControl(Transmitter,
+                                                
&MulticastAddress[MulticastIndex],
+                                                FALSE);
+        (VOID) MacRemoveMulticastAddress(Mac,
+                                         &MulticastAddress[MulticastIndex]);
+    }
+
+    if (MulticastAddress != NULL)
+        __FrontendFree(MulticastAddress);
+
+    KeLowerIrql(Irql);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    __FrontendFree(MulticastAddress);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    KeLowerIrql(Irql);
+
+    return status;
+}
+
+static NTSTATUS
+FrontendNotifyMulticastAddresses(
+    IN  PXENVIF_FRONTEND    Frontend,
+    IN  BOOLEAN             Add
+    )
+{
+    PXENVIF_TRANSMITTER     Transmitter;
+    PXENVIF_MAC             Mac;
+    PETHERNET_ADDRESS       Address;
+    ULONG                   Count;
+    ULONG                   Index;
+    NTSTATUS                status;
+
+    Transmitter = FrontendGetTransmitter(Frontend);
+    Mac = FrontendGetMac(Frontend);
+
+    status = MacQueryMulticastAddresses(Mac, NULL, &Count);
+    ASSERT3U(status, ==, STATUS_BUFFER_OVERFLOW);
+
+    if (Count != 0) {
+        Address = __FrontendAllocate(sizeof (ETHERNET_ADDRESS) *
+                                     Count);
+
+        status = STATUS_NO_MEMORY;
+        if (Address == NULL)
+            goto fail1;
+
+        status = MacQueryMulticastAddresses(Mac, Address, &Count);
+        if (!NT_SUCCESS(status))
+            goto fail2;
+    } else
+        Address = NULL;
+
+    for (Index = 0; Index < Count; Index++)
+        (VOID) TransmitterQueueMulticastControl(Transmitter,
+                                                &Address[Index],
+                                                Add);
+
+    if (Address != NULL)
+        __FrontendFree(Address);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    __FrontendFree(Address);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+FrontendAdvertiseIpAddresses(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    PXENVIF_TRANSMITTER     Transmitter;
+    KIRQL                   Irql;
+    ULONG                   Index;
+
+    Transmitter = FrontendGetTransmitter(Frontend);
+
+    KeAcquireSpinLock(&Frontend->Lock, &Irql);
+
+    for (Index = 0; Index < Frontend->AddressCount; Index++) {
+        switch (Frontend->AddressTable[Index].si_family) {
+        case AF_INET: {
+            IPV4_ADDRESS    Address;
+
+            RtlCopyMemory(Address.Byte,
+                          &Frontend->AddressTable[Index].Ipv4.sin_addr.s_addr,
+                          IPV4_ADDRESS_LENGTH);
+
+            TransmitterQueueArp(Transmitter, &Address);
+            break;
+        }
+        case AF_INET6: {
+            IPV6_ADDRESS    Address;
+
+            RtlCopyMemory(Address.Byte,
+                          
&Frontend->AddressTable[Index].Ipv6.sin6_addr.s6_addr,
+                          IPV6_ADDRESS_LENGTH);
+
+            TransmitterQueueNeighbourAdvertisement(Transmitter, &Address);
+            break;
+        }
+        default:
+            ASSERT(FALSE);
+        }
+    }
+
+    KeReleaseSpinLock(&Frontend->Lock, Irql);
+}
+
 static VOID
 FrontendSetOnline(
     IN  PXENVIF_FRONTEND    Frontend
@@ -1461,7 +1654,7 @@ FrontendSetNumQueues(
 
     Frontend->NumQueues = __min(Frontend->MaxQueues, BackendMaxQueues);
 
-    Info("%u\n", Frontend->NumQueues);
+    Info("%s: %u\n", __FrontendGetPath(Frontend), Frontend->NumQueues);
 }
 
 static FORCEINLINE ULONG
@@ -1713,6 +1906,8 @@ FrontendEnable(
     if (!NT_SUCCESS(status))
         goto fail3;
 
+    FrontendNotifyMulticastAddresses(Frontend, TRUE);
+
     Trace("<====\n");
     return STATUS_SUCCESS;
 
@@ -1739,6 +1934,8 @@ FrontendDisable(
 {
     Trace("====>\n");
 
+    FrontendNotifyMulticastAddresses(Frontend, FALSE);
+
     TransmitterDisable(__FrontendGetTransmitter(Frontend));
     ReceiverDisable(__FrontendGetReceiver(Frontend));
     MacDisable(__FrontendGetMac(Frontend));
diff --git a/src/xenvif/frontend.h b/src/xenvif/frontend.h
index 374e9b4..bd39767 100644
--- a/src/xenvif/frontend.h
+++ b/src/xenvif/frontend.h
@@ -169,4 +169,16 @@ FrontendIncrementStatistic(
     IN  ULONGLONG               Delta
     );
 
+extern NTSTATUS
+FrontendSetMulticastAddresses(
+    IN  PXENVIF_FRONTEND    Frontend,
+    IN  PETHERNET_ADDRESS   Address,
+    IN  ULONG               Count
+    );
+
+extern VOID
+FrontendAdvertiseIpAddresses(
+    IN  PXENVIF_FRONTEND        Frontend
+    );
+
 #endif  // _XENVIF_FRONTEND_H
diff --git a/src/xenvif/mac.c b/src/xenvif/mac.c
index b255d58..91467d6 100644
--- a/src/xenvif/mac.c
+++ b/src/xenvif/mac.c
@@ -42,6 +42,11 @@
 #include "assert.h"
 #include "util.h"
 
+typedef struct _XENVIF_MAC_MULTICAST {
+    LIST_ENTRY          ListEntry;
+    ETHERNET_ADDRESS    Address;
+} XENVIF_MAC_MULTICAST, *PXENVIF_MAC_MULTICAST;
+
 struct _XENVIF_MAC {
     PXENVIF_FRONTEND        Frontend;
     KSPIN_LOCK              Lock;
@@ -51,8 +56,9 @@ struct _XENVIF_MAC {
     ETHERNET_ADDRESS        PermanentAddress;
     ETHERNET_ADDRESS        CurrentAddress;
     ETHERNET_ADDRESS        BroadcastAddress;
-    PETHERNET_ADDRESS       MulticastAddress;
-    ULONG                   MulticastAddressCount;
+    LIST_ENTRY              MulticastList;
+    ULONG                   MulticastCount;
+    BOOLEAN                 MulticastControl;
     XENVIF_MAC_FILTER_LEVEL FilterLevel[ETHERNET_ADDRESS_TYPE_COUNT];
     XENBUS_DEBUG_INTERFACE  DebugInterface;
     PXENBUS_DEBUG_CALLBACK  DebugCallback;
@@ -108,6 +114,15 @@ __MacSetPermanentAddress(
                         Mac->PermanentAddress.Byte[4],
                         Mac->PermanentAddress.Byte[5]);
 
+    Info("%s: %02X:%02X:%02X:%02X:%02X:%02X\n",
+         FrontendGetPrefix(Frontend),
+         Mac->PermanentAddress.Byte[0],
+         Mac->PermanentAddress.Byte[1],
+         Mac->PermanentAddress.Byte[2],
+         Mac->PermanentAddress.Byte[3],
+         Mac->PermanentAddress.Byte[4],
+         Mac->PermanentAddress.Byte[5]);
+
     return STATUS_SUCCESS;
 
 fail1:
@@ -153,6 +168,15 @@ __MacSetCurrentAddress(
                         Mac->CurrentAddress.Byte[4],
                         Mac->CurrentAddress.Byte[5]);
 
+    Info("%s: %02X:%02X:%02X:%02X:%02X:%02X\n",
+         FrontendGetPrefix(Frontend),
+         Mac->CurrentAddress.Byte[0],
+         Mac->CurrentAddress.Byte[1],
+         Mac->CurrentAddress.Byte[2],
+         Mac->CurrentAddress.Byte[3],
+         Mac->CurrentAddress.Byte[4],
+         Mac->CurrentAddress.Byte[5]);
+
     return STATUS_SUCCESS;
 
 fail1:
@@ -218,6 +242,7 @@ MacInitialize(
         goto fail1;
 
     KeInitializeSpinLock(&(*Mac)->Lock);
+    InitializeListHead(&(*Mac)->MulticastList);
 
     FdoGetDebugInterface(PdoGetFdo(FrontendGetPdo(Frontend)),
                          &(*Mac)->DebugInterface);
@@ -235,6 +260,113 @@ fail1:
     return status;
 }
 
+static NTSTATUS
+MacDumpMulticastList(
+    IN  PXENVIF_MAC     Mac
+    )
+{
+    PXENVIF_FRONTEND    Frontend;
+    PETHERNET_ADDRESS   Address;
+    ULONG               Count;
+    PLIST_ENTRY         ListEntry;
+    ULONG               Index;
+    KIRQL               Irql;
+    NTSTATUS            status;
+
+    Trace("====>\n");
+
+    Frontend = Mac->Frontend;
+
+    KeAcquireSpinLock(&Mac->Lock, &Irql);
+
+    status  = STATUS_UNSUCCESSFUL;
+    if (!Mac->Connected)
+        goto fail1;
+
+    if (Mac->MulticastCount != 0) {
+        Address = __MacAllocate(sizeof (ETHERNET_ADDRESS) *
+                                Mac->MulticastCount);
+
+        status = STATUS_NO_MEMORY;
+        if (Address == NULL)
+            goto fail2;
+
+        Count = 0;
+        for (ListEntry = Mac->MulticastList.Flink;
+             ListEntry != &Mac->MulticastList;
+             ListEntry = ListEntry->Flink) {
+            PXENVIF_MAC_MULTICAST   Multicast;
+
+            Multicast = CONTAINING_RECORD(ListEntry,
+                                          XENVIF_MAC_MULTICAST,
+                                          ListEntry);
+
+            Address[Count++] = Multicast->Address;
+        }
+        ASSERT3U(Count, ==, Mac->MulticastCount);
+    } else {
+        Address = NULL;
+        Count = 0;
+    }
+
+    KeReleaseSpinLock(&Mac->Lock, Irql);
+
+    (VOID) XENBUS_STORE(Remove,
+                        &Mac->StoreInterface,
+                        NULL,
+                        FrontendGetPrefix(Frontend),
+                        "mac/multicast");
+
+    for (Index = 0; Index < Count; Index++) {
+        CHAR    Node[sizeof ("mac/multicast/XX")];
+
+        status = RtlStringCbPrintfA(Node,
+                                    sizeof (Node),
+                                    "mac/multicast/%u",
+                                    Index);
+        ASSERT(NT_SUCCESS(status));
+
+        (VOID) XENBUS_STORE(Printf,
+                            &Mac->StoreInterface,
+                            NULL,
+                            FrontendGetPrefix(Frontend),
+                            Node,
+                            "%02x:%02x:%02x:%02x:%02x:%02x",
+                            Address[Index].Byte[0],
+                            Address[Index].Byte[1],
+                            Address[Index].Byte[2],
+                            Address[Index].Byte[3],
+                            Address[Index].Byte[4],
+                            Address[Index].Byte[5]);
+
+        Trace("%s: %02x:%02x:%02x:%02x:%02x:%02x\n",
+              FrontendGetPrefix(Frontend),
+              Address[Index].Byte[0],
+              Address[Index].Byte[1],
+              Address[Index].Byte[2],
+              Address[Index].Byte[3],
+              Address[Index].Byte[4],
+              Address[Index].Byte[5]);
+    }
+
+    if (Address != NULL)
+        __MacFree(Address);
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    KeReleaseSpinLock(&Mac->Lock, Irql);
+
+    return status;
+}
+
 NTSTATUS
 MacConnect(
     IN  PXENVIF_MAC     Mac
@@ -292,6 +424,22 @@ MacConnect(
 
     Mac->MaximumFrameSize = (ULONG)Mtu + sizeof (ETHERNET_TAGGED_HEADER);
 
+    status = XENBUS_STORE(Read,
+                          &Mac->StoreInterface,
+                          NULL,
+                          FrontendGetBackendPath(Frontend),
+                          "feature-multicast-control",
+                          &Buffer);
+    if (!NT_SUCCESS(status)) {
+        Mac->MulticastControl = FALSE;
+    } else {
+        Mac->MulticastControl = (BOOLEAN)strtol(Buffer, NULL, 2);
+
+        XENBUS_STORE(Free,
+                     &Mac->StoreInterface,
+                     Buffer);
+    }
+
     status = XENBUS_DEBUG(Register,
                           &Mac->DebugInterface,
                           __MODULE__ "|MAC",
@@ -301,8 +449,12 @@ MacConnect(
     if (!NT_SUCCESS(status))
         goto fail5;
 
+    KeAcquireSpinLockAtDpcLevel(&Mac->Lock);
     ASSERT(!Mac->Connected);
     Mac->Connected = TRUE;
+    KeReleaseSpinLockFromDpcLevel(&Mac->Lock);
+
+    (VOID) MacDumpMulticastList(Mac);
 
     return STATUS_SUCCESS;
 
@@ -419,14 +571,18 @@ MacDisconnect(
 
     Frontend = Mac->Frontend;
 
+    KeAcquireSpinLockAtDpcLevel(&Mac->Lock);
     ASSERT(Mac->Connected);
     Mac->Connected = FALSE;
+    KeReleaseSpinLockFromDpcLevel(&Mac->Lock);
 
     XENBUS_DEBUG(Deregister,
                  &Mac->DebugInterface,
                  Mac->DebugCallback);
     Mac->DebugCallback = NULL;
 
+    Mac->MulticastControl = FALSE;
+
     Mac->MaximumFrameSize = 0;
 
     RtlZeroMemory(&Mac->BroadcastAddress, sizeof (ETHERNET_ADDRESS));
@@ -449,11 +605,25 @@ MacTeardown(
     IN  PXENVIF_MAC Mac
     )
 {
-    if (Mac->MulticastAddressCount != 0) {
-        __MacFree(Mac->MulticastAddress);
-        Mac->MulticastAddress = NULL;
-        Mac->MulticastAddressCount = 0;
+    while (!IsListEmpty(&Mac->MulticastList)) {
+        PLIST_ENTRY             ListEntry;
+        PXENVIF_MAC_MULTICAST   Multicast;
+
+        ListEntry = RemoveHeadList(&Mac->MulticastList);
+        ASSERT3P(ListEntry, !=, &Mac->MulticastList);
+
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+        Multicast = CONTAINING_RECORD(ListEntry,
+                                      XENVIF_MAC_MULTICAST,
+                                      ListEntry);
+        __MacFree(Multicast);
+
+        --Mac->MulticastCount;
     }
+    ASSERT3U(Mac->MulticastCount, ==, 0);
+
+    RtlZeroMemory(&Mac->MulticastList, sizeof (LIST_ENTRY));
 
     RtlZeroMemory(&Mac->FilterLevel,
                   ETHERNET_ADDRESS_TYPE_COUNT * sizeof 
(XENVIF_MAC_FILTER_LEVEL));
@@ -570,50 +740,105 @@ MacQueryMaximumFrameSize(
 }
 
 NTSTATUS
-MacSetMulticastAddresses(
-    IN  PXENVIF_MAC         Mac,
-    IN  ETHERNET_ADDRESS    Address[],
-    IN  ULONG               Count
+MacAddMulticastAddress(
+    IN      PXENVIF_MAC         Mac,
+    IN      PETHERNET_ADDRESS   Address
     )
 {
-    KIRQL                   Irql;
-    PETHERNET_ADDRESS       MulticastAddress;
-    ULONG                   Index;
-    NTSTATUS                status;
+    PXENVIF_FRONTEND            Frontend;
+    PXENVIF_MAC_MULTICAST       Multicast;
+    KIRQL                       Irql;
+    NTSTATUS                    status;
+
+    Frontend = Mac->Frontend;
+
+    ASSERT(Address->Byte[0] & 0x01);
+
+    Multicast = __MacAllocate(sizeof (XENVIF_MAC_MULTICAST));
+
+    status = STATUS_NO_MEMORY;
+    if (Multicast == NULL)
+        goto fail1;
+
+    Multicast->Address = *Address;
 
     KeAcquireSpinLock(&Mac->Lock, &Irql);
+    InsertTailList(&Mac->MulticastList, &Multicast->ListEntry);
+    Mac->MulticastCount++;
+    KeReleaseSpinLock(&Mac->Lock, Irql);
 
-    status = STATUS_INVALID_PARAMETER;
-    for (Index = 0; Index < Count; Index++) {
-        if (!(Address[Index].Byte[0] & 0x01))
-            goto fail1;
-    }
+    (VOID) MacDumpMulticastList(Mac);
 
-    if (Count != 0) {
-        MulticastAddress = __MacAllocate(sizeof (ETHERNET_ADDRESS) * Count);
+    Trace("%s: %02X:%02X:%02X:%02X:%02X:%02X\n",
+          FrontendGetPrefix(Frontend),
+          Address->Byte[0],
+          Address->Byte[1],
+          Address->Byte[2],
+          Address->Byte[3],
+          Address->Byte[4],
+          Address->Byte[5]);
 
-        status = STATUS_NO_MEMORY;
-        if (MulticastAddress == NULL)
-            goto fail2;
+    return STATUS_SUCCESS;
 
-        for (Index = 0; Index < Count; Index++)
-            MulticastAddress[Index] = Address[Index];
-    } else {
-        MulticastAddress = NULL;
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+NTSTATUS
+MacRemoveMulticastAddress(
+    IN      PXENVIF_MAC         Mac,
+    IN      PETHERNET_ADDRESS   Address
+    )
+{
+    PXENVIF_FRONTEND            Frontend;
+    PLIST_ENTRY                 ListEntry;
+    PXENVIF_MAC_MULTICAST       Multicast;
+    KIRQL                       Irql;
+    NTSTATUS                    status;
+
+    Frontend = Mac->Frontend;
+
+    KeAcquireSpinLock(&Mac->Lock, &Irql);
+
+    for (ListEntry = Mac->MulticastList.Flink;
+         ListEntry != &Mac->MulticastList;
+         ListEntry = ListEntry->Flink) {
+        Multicast = CONTAINING_RECORD(ListEntry,
+                                      XENVIF_MAC_MULTICAST,
+                                      ListEntry);
+
+        if (RtlEqualMemory(&Multicast->Address,
+                           Address,
+                           ETHERNET_ADDRESS_LENGTH))
+            goto found;
     }
 
-    if (Mac->MulticastAddressCount != 0)
-        __MacFree(Mac->MulticastAddress);
+    status = STATUS_OBJECT_NAME_NOT_FOUND;
+    goto fail1;
+
+found:
+    ASSERT(Mac->MulticastCount != 0);
+    --Mac->MulticastCount;
 
-    Mac->MulticastAddress = MulticastAddress;
-    Mac->MulticastAddressCount = Count;
+    RemoveEntryList(&Multicast->ListEntry);
+    __MacFree(Multicast);
 
     KeReleaseSpinLock(&Mac->Lock, Irql);
 
-    return STATUS_SUCCESS;
+    (VOID) MacDumpMulticastList(Mac);
 
-fail2:
-    Error("fail2\n");
+    Trace("%s: %02X:%02X:%02X:%02X:%02X:%02X\n",
+          FrontendGetPrefix(Frontend),
+          Address->Byte[0],
+          Address->Byte[1],
+          Address->Byte[2],
+          Address->Byte[3],
+          Address->Byte[4],
+          Address->Byte[5]);
+
+    return STATUS_SUCCESS;
 
 fail1:
     Error("fail1 (%08x)\n", status);
@@ -626,31 +851,40 @@ fail1:
 NTSTATUS
 MacQueryMulticastAddresses(
     IN      PXENVIF_MAC         Mac,
-    IN      PETHERNET_ADDRESS   Address,
+    IN      PETHERNET_ADDRESS   Address OPTIONAL,
     IN OUT  PULONG              Count
     )
 {
+    PLIST_ENTRY                 ListEntry;
     KIRQL                       Irql;
-    ULONG                       Index;
     NTSTATUS                    status;
 
     KeAcquireSpinLock(&Mac->Lock, &Irql);
 
     status = STATUS_BUFFER_OVERFLOW;
-    if (*Count < Mac->MulticastAddressCount)
+    if (Address == NULL || *Count < Mac->MulticastCount)
         goto fail1;
 
-    for (Index = 0; Index < Mac->MulticastAddressCount; Index++)
-        Address[Index] = Mac->MulticastAddress[Index];
+    *Count = 0;
+    for (ListEntry = Mac->MulticastList.Flink;
+         ListEntry != &Mac->MulticastList;
+         ListEntry = ListEntry->Flink) {
+        PXENVIF_MAC_MULTICAST   Multicast;
+
+        Multicast = CONTAINING_RECORD(ListEntry,
+                                      XENVIF_MAC_MULTICAST,
+                                      ListEntry);
 
-    *Count = Mac->MulticastAddressCount;
+        Address[(*Count)++] = Multicast->Address;
+    }
+    ASSERT3U(*Count, ==, Mac->MulticastCount);
 
     KeReleaseSpinLock(&Mac->Lock, Irql);
 
     return STATUS_SUCCESS;
 
 fail1:
-    *Count = Mac->MulticastAddressCount;
+    *Count = Mac->MulticastCount;
 
     KeReleaseSpinLock(&Mac->Lock, Irql);
 
@@ -770,17 +1004,32 @@ MacApplyFilters(
             break;
 
         case XENVIF_MAC_FILTER_MATCHING: {
-            ULONG Index;
+            PLIST_ENTRY ListEntry;
+
+            if (Mac->MulticastControl) {
+                Allow = TRUE;
+                break;
+            }
 
-            for (Index = 0; Index < Mac->MulticastAddressCount; Index++) {
-                if (RtlEqualMemory(&Mac->MulticastAddress[Index],
+            for (ListEntry = Mac->MulticastList.Flink;
+                 ListEntry != &Mac->MulticastList;
+                 ListEntry = ListEntry->Flink) {
+                PXENVIF_MAC_MULTICAST   Multicast;
+
+                Multicast = CONTAINING_RECORD(ListEntry,
+                                              XENVIF_MAC_MULTICAST,
+                                              ListEntry);
+
+                if (RtlEqualMemory(&Multicast->Address,
                                    DestinationAddress,
-                                   ETHERNET_ADDRESS_LENGTH))
+                                   ETHERNET_ADDRESS_LENGTH)) {
                     Allow = TRUE;
+                    break;
+                }
             }
+
             break;
         }
-
         case XENVIF_MAC_FILTER_ALL:
             Allow = TRUE;
             break;
diff --git a/src/xenvif/mac.h b/src/xenvif/mac.h
index d39c523..83ce5b8 100644
--- a/src/xenvif/mac.h
+++ b/src/xenvif/mac.h
@@ -96,17 +96,22 @@ MacQueryBroadcastAddress(
     );
 
 extern NTSTATUS
-MacQueryMulticastAddresses(
+MacAddMulticastAddress(
     IN      PXENVIF_MAC         Mac,
-    OUT     PETHERNET_ADDRESS   Address OPTIONAL,
-    IN OUT  PULONG              Count
+    OUT     PETHERNET_ADDRESS   Address
     );
 
 extern NTSTATUS
-MacSetMulticastAddresses(
-    IN  PXENVIF_MAC         Mac,
-    IN  PETHERNET_ADDRESS   Address OPTIONAL,
-    IN  ULONG               Count
+MacRemoveMulticastAddress(
+    IN      PXENVIF_MAC         Mac,
+    OUT     PETHERNET_ADDRESS   Address
+    );
+
+extern NTSTATUS
+MacQueryMulticastAddresses(
+    IN      PXENVIF_MAC         Mac,
+    OUT     PETHERNET_ADDRESS   Address OPTIONAL,
+    IN OUT  PULONG              Count
     );
 
 extern NTSTATUS
diff --git a/src/xenvif/transmitter.c b/src/xenvif/transmitter.c
index 4cf21db..2d8f613 100644
--- a/src/xenvif/transmitter.c
+++ b/src/xenvif/transmitter.c
@@ -10,7 +10,7 @@
  *     following disclaimer.
  * *   Redistributions in binary form must reproduce the above 
  *     copyright notice, this list of conditions and the 
- *     following disclaimer in the documentation and/or other 
+ *     following disclaimer in the documetation and/or other
  *     materials provided with the distribution.
  * 
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
@@ -64,16 +64,64 @@
 
 #define MAXNAMELEN  128
 
+typedef struct _XENVIF_TRANSMITTER_REQUEST_ARP_PARAMETERS {
+    IPV4_ADDRESS    Address;
+} XENVIF_TRANSMITTER_REQUEST_ARP_PARAMETERS, 
*PXENVIF_TRANSMITTER_REQUEST_ARP_PARAMETERS;
+
+typedef struct _XENVIF_TRANSMITTER_REQUEST_NEIGHBOUR_ADVERTISEMENT_PARAMETERS {
+    IPV6_ADDRESS    Address;
+} XENVIF_TRANSMITTER_REQUEST_NEIGHBOUR_ADVERTISEMENT_PARAMETERS, 
*PXENVIF_TRANSMITTER_REQUEST_NEIGHBOUR_ADVERTISEMENT_PARAMETERS;
+
+typedef struct _XENVIF_TRANSMITTER_REQUEST_MULTICAST_CONTROL_PARAMETERS {
+    ETHERNET_ADDRESS    Address;
+    BOOLEAN             Add;
+} XENVIF_TRANSMITTER_REQUEST_MULTICAST_CONTROL_PARAMETERS, 
*PXENVIF_TRANSMITTER_REQUEST_MULTICAST_CONTROL_PARAMETERS;
+
+typedef enum _XENVIF_TRANSMITTER_REQUEST_TYPE {
+    XENVIF_TRANSMITTER_REQUEST_TYPE_INVALID = 0,
+    XENVIF_TRANSMITTER_REQUEST_TYPE_ARP,
+    XENVIF_TRANSMITTER_REQUEST_TYPE_NEIGHBOUR_ADVERTISEMENT,
+    XENVIF_TRANSMITTER_REQUEST_TYPE_MULTICAST_CONTROL
+} XENVIF_TRANSMITTER_REQUEST_TYPE, *PXENVIF_TRANSMITTER_REQUEST_TYPE;
+
+#pragma warning(push)
+#pragma warning(disable:4201)   // nonstandard extension used : nameless 
struct/union
+
+typedef struct _XENVIF_TRANSMITTER_REQUEST {
+    LIST_ENTRY                      ListEntry;
+    XENVIF_TRANSMITTER_REQUEST_TYPE Type;
+    union {
+        XENVIF_TRANSMITTER_REQUEST_ARP_PARAMETERS                       Arp;
+        XENVIF_TRANSMITTER_REQUEST_NEIGHBOUR_ADVERTISEMENT_PARAMETERS   
NeighbourAdvertisement;
+        XENVIF_TRANSMITTER_REQUEST_MULTICAST_CONTROL_PARAMETERS         
MulticastControl;
+    };
+} XENVIF_TRANSMITTER_REQUEST, *PXENVIF_TRANSMITTER_REQUEST;
+
+#pragma warning(pop)
+
 typedef struct _XENVIF_TRANSMITTER_BUFFER {
     PMDL        Mdl;
     PVOID       Context;
     ULONG       Reference;
 } XENVIF_TRANSMITTER_BUFFER, *PXENVIF_TRANSMITTER_BUFFER;
 
+typedef enum _XENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE {
+    XENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE_INVALID = 0,
+    XENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE_ADD,
+    XENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE_REMOVE
+} XENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE, 
*PXENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE;
+
+typedef struct _XENVIF_TRANSMITTER_MULTICAST_CONTROL {
+    XENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE   Type;
+    ETHERNET_ADDRESS                            Address;
+    ULONG                                       Reference;
+} XENVIF_TRANSMITTER_MULTICAST_CONTROL, *PXENVIF_TRANSMITTER_MULTICAST_CONTROL;
+
 typedef enum _XENVIF_TRANSMITTER_FRAGMENT_TYPE {
     XENVIF_TRANSMITTER_FRAGMENT_TYPE_INVALID = 0,
     XENVIF_TRANSMITTER_FRAGMENT_TYPE_PACKET,
-    XENVIF_TRANSMITTER_FRAGMENT_TYPE_BUFFER
+    XENVIF_TRANSMITTER_FRAGMENT_TYPE_BUFFER,
+    XENVIF_TRANSMITTER_FRAGMENT_TYPE_MULTICAST_CONTROL
 } XENVIF_TRANSMITTER_FRAGMENT_TYPE, *PXENVIF_TRANSMITTER_FRAGMENT_TYPE;
 
 typedef struct _XENVIF_TRANSMITTER_FRAGMENT {
@@ -106,9 +154,11 @@ typedef struct _XENVIF_TRANSMITTER_RING {
     ULONG                           Index;
     PCHAR                           Path;
     PXENBUS_CACHE                   BufferCache;
+    PXENBUS_CACHE                   MulticastControlCache;
     PXENBUS_CACHE                   FragmentCache;
     PXENBUS_GNTTAB_CACHE            GnttabCache;
     PXENBUS_RANGE_SET               RangeSet;
+    PXENBUS_CACHE                   RequestCache;
     PMDL                            Mdl;
     netif_tx_front_ring_t           Front;
     netif_tx_sring_t                *Shared;
@@ -122,7 +172,8 @@ typedef struct _XENVIF_TRANSMITTER_RING {
     BOOLEAN                         Stopped;
     PVOID                           Lock;
     PKTHREAD                        LockThread;
-    LIST_ENTRY                      Queued;
+    LIST_ENTRY                      PacketQueue;
+    LIST_ENTRY                      RequestQueue;
     XENVIF_TRANSMITTER_STATE        State;
     ULONG                           PacketsQueued;
     ULONG                           PacketsGranted;
@@ -135,11 +186,8 @@ typedef struct _XENVIF_TRANSMITTER_RING {
     ULONG                           RequestsPushed;
     ULONG                           ResponsesProcessed;
     ULONG                           PacketsSent;
-    LIST_ENTRY                      Completed;
+    LIST_ENTRY                      PacketComplete;
     ULONG                           PacketsCompleted;
-    PSOCKADDR_INET                  AddressTable;
-    ULONG                           AddressCount;
-    ULONG                           AddressIndex;
     PXENBUS_DEBUG_CALLBACK          DebugCallback;
     PXENVIF_THREAD                  WatchdogThread;
 } XENVIF_TRANSMITTER_RING, *PXENVIF_TRANSMITTER_RING;
@@ -155,6 +203,7 @@ struct _XENVIF_TRANSMITTER {
     LONG                        NumQueues;
     LONG_PTR                    Offset[XENVIF_TRANSMITTER_PACKET_OFFSET_COUNT];
     BOOLEAN                     Split;
+    BOOLEAN                     MulticastControl;
     ULONG                       DisableIpVersion4Gso;
     ULONG                       DisableIpVersion6Gso;
     ULONG                       AlwaysCopy;
@@ -196,7 +245,7 @@ TransmitterPacketAcquireLock(
 
 static VOID
 TransmitterPacketReleaseLock(
-    IN  PVOID                       Argument
+    IN  PVOID           Argument
     )
 {
     PXENVIF_TRANSMITTER Transmitter = Argument;
@@ -207,8 +256,8 @@ TransmitterPacketReleaseLock(
 
 static NTSTATUS
 TransmitterPacketCtor(
-    IN  PVOID                       Argument,
-    IN  PVOID                       Object
+    IN  PVOID   Argument,
+    IN  PVOID   Object
     )
 {
     UNREFERENCED_PARAMETER(Argument);
@@ -219,8 +268,8 @@ TransmitterPacketCtor(
 
 static VOID
 TransmitterPacketDtor(
-    IN  PVOID                       Argument,
-    IN  PVOID                       Object
+    IN  PVOID   Argument,
+    IN  PVOID   Object
     )
 {
     UNREFERENCED_PARAMETER(Argument);
@@ -360,6 +409,69 @@ __TransmitterPutBuffer(
 }
 
 static NTSTATUS
+TransmitterMulticastControlCtor(
+    IN  PVOID   Argument,
+    IN  PVOID   Object
+    )
+{
+    UNREFERENCED_PARAMETER(Argument);
+    UNREFERENCED_PARAMETER(Object);
+
+    return STATUS_SUCCESS;
+}
+
+static VOID
+TransmitterMulticastControlDtor(
+    IN  PVOID   Argument,
+    IN  PVOID   Object
+    )
+{
+    UNREFERENCED_PARAMETER(Argument);
+    UNREFERENCED_PARAMETER(Object);
+}
+
+static FORCEINLINE PXENVIF_TRANSMITTER_MULTICAST_CONTROL
+__TransmitterGetMulticastControl(
+    IN  PXENVIF_TRANSMITTER_RING            Ring
+    )
+{
+    PXENVIF_TRANSMITTER                     Transmitter;
+    PXENVIF_FRONTEND                        Frontend;
+    PXENVIF_TRANSMITTER_MULTICAST_CONTROL   Control;
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+
+    Control = XENBUS_CACHE(Get,
+                           &Transmitter->CacheInterface,
+                           Ring->MulticastControlCache,
+                           TRUE);
+
+    return Control;
+}
+
+static FORCEINLINE VOID
+__TransmitterPutMulticastControl(
+    IN  PXENVIF_TRANSMITTER_RING                Ring,
+    IN  PXENVIF_TRANSMITTER_MULTICAST_CONTROL   Control
+    )
+{
+    PXENVIF_TRANSMITTER                         Transmitter;
+    PXENVIF_FRONTEND                            Frontend;
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+
+    ASSERT3U(Control->Reference, ==, 0);
+
+    XENBUS_CACHE(Put,
+                 &Transmitter->CacheInterface,
+                 Ring->MulticastControlCache,
+                 Control,
+                 TRUE);
+}
+
+static NTSTATUS
 TransmitterFragmentCtor(
     IN  PVOID                       Argument,
     IN  PVOID                       Object
@@ -457,6 +569,7 @@ __TransmitterPutFragment(
     ASSERT3U(Fragment->Offset, ==, 0);
     ASSERT3U(Fragment->Type, ==, XENVIF_TRANSMITTER_FRAGMENT_TYPE_INVALID);
     ASSERT3P(Fragment->Context, ==, NULL);
+    ASSERT3P(Fragment->Entry, ==, NULL);
     ASSERT(!Fragment->Extra);
 
     XENBUS_CACHE(Put,
@@ -466,6 +579,65 @@ __TransmitterPutFragment(
                  TRUE);
 }
 
+static NTSTATUS
+TransmitterRequestCtor(
+    IN  PVOID   Argument,
+    IN  PVOID   Object
+    )
+{
+    UNREFERENCED_PARAMETER(Argument);
+    UNREFERENCED_PARAMETER(Object);
+
+    return STATUS_SUCCESS;
+}
+
+static VOID
+TransmitterRequestDtor(
+    IN  PVOID   Argument,
+    IN  PVOID   Object
+    )
+{
+    UNREFERENCED_PARAMETER(Argument);
+    UNREFERENCED_PARAMETER(Object);
+}
+
+static FORCEINLINE PXENVIF_TRANSMITTER_REQUEST
+__TransmitterGetRequest(
+    IN  PXENVIF_TRANSMITTER_RING    Ring
+    )
+{
+    PXENVIF_TRANSMITTER             Transmitter;
+    PXENVIF_TRANSMITTER_REQUEST     Request;
+
+    Transmitter = Ring->Transmitter;
+
+    Request = XENBUS_CACHE(Get,
+                           &Transmitter->CacheInterface,
+                           Ring->RequestCache,
+                           TRUE);
+
+    return Request;
+}
+
+static FORCEINLINE VOID
+__TransmitterPutRequest(
+    IN  PXENVIF_TRANSMITTER_RING    Ring,
+    IN  PXENVIF_TRANSMITTER_REQUEST Request
+    )
+{
+    PXENVIF_TRANSMITTER             Transmitter;
+
+    Transmitter = Ring->Transmitter;
+
+    ASSERT3U(Request->Type, ==, XENVIF_TRANSMITTER_REQUEST_TYPE_INVALID);
+
+    XENBUS_CACHE(Put,
+                 &Transmitter->CacheInterface,
+                 Ring->RequestCache,
+                 Request,
+                 TRUE);
+}
+
 static VOID
 TransmitterRingDebugCallback(
     IN  PVOID                   Argument,
@@ -1188,7 +1360,7 @@ fail1:
     return status;
 }
 
-static FORCEINLINE VOID
+static FORCEINLINE PXENVIF_TRANSMITTER_PACKET
 __TransmitterRingUnprepareFragments(
     IN  PXENVIF_TRANSMITTER_RING    Ring
     )
@@ -1196,18 +1368,20 @@ __TransmitterRingUnprepareFragments(
     PXENVIF_TRANSMITTER             Transmitter;
     PXENVIF_FRONTEND                Frontend;
     PXENVIF_TRANSMITTER_STATE       State;
+    ULONG                           Count;
+    PXENVIF_TRANSMITTER_PACKET      Packet;
 
     Transmitter = Ring->Transmitter;
     Frontend = Transmitter->Frontend;
 
     State = &Ring->State;
+    Count = State->Count;
 
-    while (State->Count != 0) {
+    while (Count != 0) {
         PLIST_ENTRY                     ListEntry;
         PXENVIF_TRANSMITTER_FRAGMENT    Fragment;
-        PXENVIF_TRANSMITTER_PACKET      Packet;
 
-        --State->Count;
+        --Count;
 
         ListEntry = RemoveTailList(&State->List);
         ASSERT3P(ListEntry, !=, &State->List);
@@ -1238,8 +1412,8 @@ __TransmitterRingUnprepareFragments(
             Buffer->Context = NULL;
 
             ASSERT(Buffer->Reference != 0);
-            if (--Buffer->Reference == 0)
-                __TransmitterPutBuffer(Ring, Buffer);
+            --Buffer->Reference;
+            __TransmitterPutBuffer(Ring, Buffer);
 
             break;
         }
@@ -1250,16 +1424,64 @@ __TransmitterRingUnprepareFragments(
 
             break;
 
-        default:
+        case XENVIF_TRANSMITTER_FRAGMENT_TYPE_MULTICAST_CONTROL: {
+            PXENVIF_TRANSMITTER_MULTICAST_CONTROL Control;
+
+            Control = Fragment->Context;
+            Fragment->Context = NULL;
+            Fragment->Type = XENVIF_TRANSMITTER_FRAGMENT_TYPE_INVALID;
+
+            switch (Control->Type) {
+            case XENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE_ADD:
+            case XENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE_REMOVE:
+                break;
+            default:
+                ASSERT(FALSE);
+                break;
+            }
+
+            ASSERT(Control->Reference != 0);
+            --Control->Reference;
+            __TransmitterPutMulticastControl(Ring, Control);
+
             Packet = NULL;
+            break;
+            }
+        default:
             ASSERT(FALSE);
+            Packet = NULL;
+            break;
         }
 
-        __TransmitterPutFragment(Ring, Fragment);
-
         if (Packet != NULL)
             Packet->Value--;
+
+        __TransmitterPutFragment(Ring, Fragment);
+    }
+
+    if (State->Count != 0) {
+        ASSERT(IsListEmpty(&State->List));
+        RtlZeroMemory(&State->List, sizeof (LIST_ENTRY));
+
+        State->Count = 0;
+    }
+
+    Packet = State->Packet;
+
+    if (Packet != NULL) {
+        Ring->PacketsUnprepared++;
+
+        RtlZeroMemory(&State->Payload, sizeof (XENVIF_PACKET_PAYLOAD));
+
+        Packet->Send = State->Send;
+        RtlZeroMemory(&State->Send, sizeof 
(XENVIF_TRANSMITTER_PACKET_SEND_INFO));
+
+        State->Packet = NULL;
     }
+
+    ASSERT(IsZeroMemory(&Ring->State, sizeof (XENVIF_TRANSMITTER_STATE)));
+
+    return Packet;
 }
 
 static FORCEINLINE NTSTATUS
@@ -1395,44 +1617,6 @@ fail1:
     return status;
 }
 
-static FORCEINLINE PXENVIF_TRANSMITTER_PACKET
-__TransmitterRingUnpreparePacket(
-    IN  PXENVIF_TRANSMITTER_RING    Ring
-    )
-{
-    PXENVIF_TRANSMITTER_STATE       State;
-    PXENVIF_TRANSMITTER_PACKET      Packet;
-
-    State = &Ring->State;
-    Packet = State->Packet;
-
-    // This has the side effect of freeing up resources associated with a 
pending
-    // gratuitous ARP, which is why the call is not conditional on Packet being
-    // non-NULL
-    __TransmitterRingUnprepareFragments(Ring);
-    RtlZeroMemory(&State->Info, sizeof (XENVIF_PACKET_INFO));
-
-    if (Packet == NULL)
-        goto done;
-
-    Ring->PacketsUnprepared++;
-
-    ASSERT(IsListEmpty(&State->List));
-    RtlZeroMemory(&State->List, sizeof (LIST_ENTRY));
-
-    RtlZeroMemory(&State->Payload, sizeof (XENVIF_PACKET_PAYLOAD));
-
-    Packet->Send = State->Send;
-    RtlZeroMemory(&State->Send, sizeof (XENVIF_TRANSMITTER_PACKET_SEND_INFO));
-
-    State->Packet = NULL;
-
-    ASSERT(IsZeroMemory(&Ring->State, sizeof (XENVIF_TRANSMITTER_STATE)));
-
-done:
-    return Packet;
-}
-
 static FORCEINLINE NTSTATUS
 __TransmitterRingPrepareArp(
     IN  PXENVIF_TRANSMITTER_RING    Ring,
@@ -1458,12 +1642,6 @@ __TransmitterRingPrepareArp(
 
     ASSERT(IsZeroMemory(&Ring->State, sizeof (XENVIF_TRANSMITTER_STATE)));
 
-    Info("%u.%u.%u.%u\n",
-         Address->Byte[0],
-         Address->Byte[1],
-         Address->Byte[2],
-         Address->Byte[3]);
-
     Transmitter = Ring->Transmitter;
     Frontend = Transmitter->Frontend;
     Mac = FrontendGetMac(Frontend);
@@ -1606,16 +1784,6 @@ __TransmitterRingPrepareNeighbourAdvertisement(
 
     ASSERT(IsZeroMemory(&Ring->State, sizeof (XENVIF_TRANSMITTER_STATE)));
 
-    Info("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
-         HTONS(Address->Word[0]),
-         HTONS(Address->Word[1]),
-         HTONS(Address->Word[2]),
-         HTONS(Address->Word[3]),
-         HTONS(Address->Word[4]),
-         HTONS(Address->Word[5]),
-         HTONS(Address->Word[6]),
-         HTONS(Address->Word[7]));
-
     Transmitter = Ring->Transmitter;
     Frontend = Transmitter->Frontend;
     Mac = FrontendGetMac(Frontend);
@@ -1758,13 +1926,71 @@ fail1:
 }
 
 static FORCEINLINE NTSTATUS
-__TransmitterRingPostFragments(
-    IN  PXENVIF_TRANSMITTER_RING    Ring
+__TransmitterRingPrepareMulticastControl(
+    IN  PXENVIF_TRANSMITTER_RING            Ring,
+    IN  PETHERNET_ADDRESS                   Address,
+    IN  BOOLEAN                             Add
     )
 {
+    PXENVIF_TRANSMITTER_STATE               State;
+    PXENVIF_TRANSMITTER_FRAGMENT            Fragment;
+    PXENVIF_TRANSMITTER_MULTICAST_CONTROL   Control;
+    NTSTATUS                                status;
+
+    ASSERT(IsZeroMemory(&Ring->State, sizeof (XENVIF_TRANSMITTER_STATE)));
+
+    State = &Ring->State;
+
+    Control = __TransmitterGetMulticastControl(Ring);
+
+    status = STATUS_NO_MEMORY;
+    if (Control == NULL)
+        goto fail1;
+
+    Control->Type = (Add) ?
+                    XENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE_ADD :
+                    XENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE_REMOVE;
+    Control->Address = *Address;
+
+    Fragment = __TransmitterGetFragment(Ring);
+
+    status = STATUS_NO_MEMORY;
+    if (Fragment == NULL)
+        goto fail2;
+
+    Fragment->Context = Control;
+    Fragment->Type = XENVIF_TRANSMITTER_FRAGMENT_TYPE_MULTICAST_CONTROL;
+    Control->Reference++;
+
+    InitializeListHead(&State->List);
+
+    ASSERT(IsZeroMemory(&Fragment->ListEntry, sizeof (LIST_ENTRY)));
+    InsertTailList(&State->List, &Fragment->ListEntry);
+    State->Count++;
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    __TransmitterPutMulticastControl(Ring, Control);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    ASSERT(IsZeroMemory(&Ring->State, sizeof (XENVIF_TRANSMITTER_STATE)));
+
+    return status;
+}
+
 #define RING_SLOTS_AVAILABLE(_Front, _req_prod, _rsp_cons)   \
         (RING_SIZE(_Front) - ((_req_prod) - (_rsp_cons)))
 
+static FORCEINLINE NTSTATUS
+__TransmitterRingPostFragments(
+    IN  PXENVIF_TRANSMITTER_RING    Ring
+    )
+{
     PXENVIF_TRANSMITTER             Transmitter;
     PXENVIF_FRONTEND                Frontend;
     PXENVIF_TRANSMITTER_STATE       State;
@@ -1775,6 +2001,8 @@ __TransmitterRingPostFragments(
     ULONG                           Extra;
     ULONG                           PacketLength;
     BOOLEAN                         FirstRequest;
+    PLIST_ENTRY                     ListEntry;
+    PXENVIF_TRANSMITTER_FRAGMENT    Fragment;
     netif_tx_request_t              *req;
     NTSTATUS                        status;
 
@@ -1793,8 +2021,14 @@ __TransmitterRingPostFragments(
     req_prod = Ring->Front.req_prod_pvt;
     rsp_cons = Ring->Front.rsp_cons;
 
+    ListEntry = State->List.Flink;
+    Fragment = CONTAINING_RECORD(ListEntry,
+                                 XENVIF_TRANSMITTER_FRAGMENT,
+                                 ListEntry);
+
     Extra = (State->Send.OffloadOptions.OffloadIpVersion4LargePacket ||
-             State->Send.OffloadOptions.OffloadIpVersion6LargePacket) ?
+             State->Send.OffloadOptions.OffloadIpVersion6LargePacket ||
+             Fragment->Type == 
XENVIF_TRANSMITTER_FRAGMENT_TYPE_MULTICAST_CONTROL) ?
             1 :
             0;
 
@@ -1809,9 +2043,6 @@ __TransmitterRingPostFragments(
     FirstRequest = TRUE;
     PacketLength = 0;
     while (State->Count != 0) {
-        PLIST_ENTRY                     ListEntry;
-        PXENVIF_TRANSMITTER_FRAGMENT    Fragment;
-
         --State->Count;
 
         ListEntry = RemoveHeadList(&State->List);
@@ -1819,16 +2050,20 @@ __TransmitterRingPostFragments(
 
         RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
 
-        Fragment = CONTAINING_RECORD(ListEntry, XENVIF_TRANSMITTER_FRAGMENT, 
ListEntry);
+        Fragment = CONTAINING_RECORD(ListEntry,
+                                     XENVIF_TRANSMITTER_FRAGMENT,
+                                     ListEntry);
 
         req = RING_GET_REQUEST(&Ring->Front, req_prod);
         req_prod++;
         Ring->RequestsPosted++;
 
         req->id = Fragment->Id;
-        req->gref = XENBUS_GNTTAB(GetReference,
+        req->gref = (Fragment->Entry != NULL) ?
+                    XENBUS_GNTTAB(GetReference,
                                   &Transmitter->GnttabInterface,
-                                  Fragment->Entry);
+                                  Fragment->Entry) :
+                    0;
         req->offset = (USHORT)Fragment->Offset;
         req->size = (USHORT)Fragment->Length;
         req->flags = NETTXF_more_data;
@@ -1843,37 +2078,49 @@ __TransmitterRingPostFragments(
                 req->flags |= NETTXF_csum_blank | NETTXF_data_validated;
 
             if (State->Send.OffloadOptions.OffloadIpVersion4LargePacket ||
-                State->Send.OffloadOptions.OffloadIpVersion6LargePacket) {
-                uint8_t                 type;
-                uint16_t                size;
+                State->Send.OffloadOptions.OffloadIpVersion6LargePacket ||
+                Fragment->Type == 
XENVIF_TRANSMITTER_FRAGMENT_TYPE_MULTICAST_CONTROL) {
                 struct netif_extra_info *extra;
 
                 ASSERT(Extra != 0);
                 Fragment->Extra = TRUE;
 
-                
ASSERT(!(State->Send.OffloadOptions.OffloadIpVersion4LargePacket &&
-                         
State->Send.OffloadOptions.OffloadIpVersion6LargePacket));
-                type = 
(State->Send.OffloadOptions.OffloadIpVersion4LargePacket) ?
-                       XEN_NETIF_GSO_TYPE_TCPV4 :
-                       XEN_NETIF_GSO_TYPE_TCPV6;
-
-                ASSERT(State->Send.MaximumSegmentSize != 0);
-                size = State->Send.MaximumSegmentSize;
-
-                ASSERT(req->flags & (NETTXF_csum_blank | 
NETTXF_data_validated));
-                req->flags |= NETTXF_extra_info;
-
                 extra = (struct netif_extra_info 
*)RING_GET_REQUEST(&Ring->Front, req_prod);
                 req_prod++;
                 Ring->RequestsPosted++;
 
-                extra->type = XEN_NETIF_EXTRA_TYPE_GSO;
-                extra->flags = 0;
+                if (State->Send.OffloadOptions.OffloadIpVersion4LargePacket ||
+                    State->Send.OffloadOptions.OffloadIpVersion6LargePacket) {
+                    ASSERT(State->Send.MaximumSegmentSize != 0);
+
+                    extra->type = XEN_NETIF_EXTRA_TYPE_GSO;
+                    extra->flags = 0;
+
+                    extra->u.gso.type = 
(State->Send.OffloadOptions.OffloadIpVersion4LargePacket) ?
+                                        XEN_NETIF_GSO_TYPE_TCPV4 :
+                                        XEN_NETIF_GSO_TYPE_TCPV6;;
+                    extra->u.gso.size = State->Send.MaximumSegmentSize;
+                    extra->u.gso.pad = 0;
+                    extra->u.gso.features = 0;
+
+                    ASSERT(req->flags & (NETTXF_csum_blank | 
NETTXF_data_validated));
+                } else {
+                    PXENVIF_TRANSMITTER_MULTICAST_CONTROL   Control;
+
+                    ASSERT(Fragment->Type == 
XENVIF_TRANSMITTER_FRAGMENT_TYPE_MULTICAST_CONTROL);
+                    Control = Fragment->Context;
+
+                    extra->type = (Control->Type == 
XENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE_ADD) ?
+                        XEN_NETIF_EXTRA_TYPE_MCAST_ADD :
+                        XEN_NETIF_EXTRA_TYPE_MCAST_DEL;
+                    extra->flags = 0;
 
-                extra->u.gso.size = size;
-                extra->u.gso.type = type;
-                extra->u.gso.pad = 0;
-                extra->u.gso.features = 0;
+                    RtlCopyMemory(&extra->u.mcast.addr,
+                                  &Control->Address.Byte[0],
+                                  ETHERNET_ADDRESS_LENGTH);
+                }
+
+                req->flags |= NETTXF_extra_info;
             }
 
             // The first fragment length is the length of the entire packet
@@ -1886,7 +2133,6 @@ __TransmitterRingPostFragments(
         Ring->Pending[req->id] = Fragment;
     }
     ASSERT(!FirstRequest);
-    ASSERT(PacketLength != 0);
 
     ASSERT(req != NULL);
     req->flags &= ~NETTXF_more_data;
@@ -1902,6 +2148,8 @@ __TransmitterRingPostFragments(
         PXENVIF_PACKET_INFO Info;
         PETHERNET_HEADER    Header;
 
+        ASSERT(PacketLength != 0);
+
         StartVa = State->StartVa;
         Info = &State->Info;
 
@@ -1930,9 +2178,9 @@ __TransmitterRingPostFragments(
 
 fail1:
     return status;
+}
 
 #undef  RING_SLOTS_AVAILABLE
-}
 
 static FORCEINLINE VOID
 __TransmitterRingFakeResponses(
@@ -1989,8 +2237,16 @@ __TransmitterRingFakeResponses(
 
     ASSERT3U(Ring->Shared->rsp_prod, ==, Ring->Front.req_prod_pvt);
 
-    if (Count != 0)
-        Info("Faked %lu responses\n", Count);
+    if (Count != 0) {
+        PXENVIF_TRANSMITTER Transmitter;
+        PXENVIF_FRONTEND    Frontend;
+
+        Transmitter = Ring->Transmitter;
+        Frontend = Transmitter->Frontend;
+
+        Info("%s: faked %lu responses\n",
+             FrontendGetPath(Frontend), Count);
+    }
 }
 
 static FORCEINLINE VOID
@@ -2055,7 +2311,7 @@ __TransmitterRingCompletePacket(
         }
     }
 
-    InsertTailList(&Ring->Completed, &Packet->ListEntry);
+    InsertTailList(&Ring->PacketComplete, &Packet->ListEntry);
     Ring->PacketsCompleted++;
 }
 
@@ -2123,8 +2379,8 @@ TransmitterRingPoll(
                 Buffer->Context = NULL;
 
                 ASSERT(Buffer->Reference != 0);
-                if (--Buffer->Reference == 0)
-                    __TransmitterPutBuffer(Ring, Buffer);
+                --Buffer->Reference;
+                __TransmitterPutBuffer(Ring, Buffer);
 
                 break;
             }
@@ -2135,20 +2391,46 @@ TransmitterRingPoll(
 
                 break;
 
-            default:
-                Packet = NULL;
-                ASSERT(FALSE);
+            case XENVIF_TRANSMITTER_FRAGMENT_TYPE_MULTICAST_CONTROL: {
+                PXENVIF_TRANSMITTER_MULTICAST_CONTROL   Control;
+
+                Control = Fragment->Context;
+                Fragment->Context = NULL;
+                Fragment->Type = XENVIF_TRANSMITTER_FRAGMENT_TYPE_INVALID;
+
+                switch (Control->Type) {
+                case XENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE_ADD:
+                case XENVIF_TRANSMITTER_MULTICAST_CONTROL_TYPE_REMOVE:
+                    break;
+                default:
+                    ASSERT(FALSE);
+                    break;
+                }
+
+                ASSERT(Control->Reference != 0);
+                --Control->Reference;
+                __TransmitterPutMulticastControl(Ring, Control);
+
+                Packet = NULL;
+                break;
+            }
+            default:
+                ASSERT(FALSE);
+                Packet = NULL;
+                break;
             }
 
             Fragment->Length = 0;
             Fragment->Offset = 0;
 
-            (VOID) XENBUS_GNTTAB(RevokeForeignAccess,
-                                 &Transmitter->GnttabInterface,
-                                 Ring->GnttabCache,
-                                 TRUE,
-                                 Fragment->Entry);
-            Fragment->Entry = NULL;
+            if (Fragment->Entry != NULL) {
+                (VOID) XENBUS_GNTTAB(RevokeForeignAccess,
+                                     &Transmitter->GnttabInterface,
+                                     Ring->GnttabCache,
+                                     TRUE,
+                                     Fragment->Entry);
+                Fragment->Entry = NULL;
+            }
 
             Fragment->Extra = FALSE;
             __TransmitterPutFragment(Ring, Fragment);
@@ -2331,7 +2613,8 @@ TransmitterRingSwizzle(
     ListEntry = List.Flink;
     if (!IsListEmpty(&List)) {
         RemoveEntryList(&List);
-        AppendTailList(&Ring->Queued, ListEntry);
+        InitializeListHead(&List);
+        AppendTailList(&Ring->PacketQueue, ListEntry);
         Ring->PacketsQueued += Count;
     }
 }
@@ -2349,9 +2632,7 @@ TransmitterRingSchedule(
     State = &Ring->State;
 
     for (;;) {
-        PLIST_ENTRY                 ListEntry;
-        PXENVIF_TRANSMITTER_PACKET  Packet;
-        NTSTATUS                    status;
+        NTSTATUS    status;
 
         if (State->Count != 0) {
             status = __TransmitterRingPostFragments(Ring);
@@ -2367,72 +2648,85 @@ TransmitterRingSchedule(
 
         ASSERT3U(State->Count, ==, 0);
 
-        if (Ring->AddressIndex != 0) {
-            ULONG   Index = (--Ring->AddressIndex) % Ring->AddressCount;
-
-            switch (Ring->AddressTable[Index].si_family) {
-            case AF_INET: {
-                IPV4_ADDRESS    Address;
+        if (!IsListEmpty(&Ring->RequestQueue)) {
+            PLIST_ENTRY                 ListEntry;
+            PXENVIF_TRANSMITTER_REQUEST Request;
 
-                RtlCopyMemory(Address.Byte,
-                              &Ring->AddressTable[Index].Ipv4.sin_addr.s_addr,
-                              IPV4_ADDRESS_LENGTH);
+            ListEntry = RemoveHeadList(&Ring->RequestQueue);
+            RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
 
-                (VOID) __TransmitterRingPrepareArp(Ring, &Address);
+            Request = CONTAINING_RECORD(ListEntry,
+                                        XENVIF_TRANSMITTER_REQUEST,
+                                        ListEntry);
 
+            switch (Request->Type) {
+            case XENVIF_TRANSMITTER_REQUEST_TYPE_ARP:
+                (VOID) __TransmitterRingPrepareArp(Ring,
+                                                   &Request->Arp.Address);
                 break;
-            }
-            case AF_INET6: {
-                IPV6_ADDRESS    Address;
-
-                RtlCopyMemory(Address.Byte,
-                              
&Ring->AddressTable[Index].Ipv6.sin6_addr.s6_addr,
-                              IPV6_ADDRESS_LENGTH);
 
-                (VOID) __TransmitterRingPrepareNeighbourAdvertisement(Ring, 
&Address);
+            case XENVIF_TRANSMITTER_REQUEST_TYPE_NEIGHBOUR_ADVERTISEMENT:
+                (VOID) __TransmitterRingPrepareNeighbourAdvertisement(Ring,
+                                                                      
&Request->NeighbourAdvertisement.Address);
+                break;
 
+            case XENVIF_TRANSMITTER_REQUEST_TYPE_MULTICAST_CONTROL:
+                (VOID) __TransmitterRingPrepareMulticastControl(Ring,
+                                                                
&Request->MulticastControl.Address,
+                                                                
Request->MulticastControl.Add);
                 break;
-            }
+
             default:
-                ASSERT(FALSE);
+                break;
             }
 
+            Request->Type = XENVIF_TRANSMITTER_REQUEST_TYPE_INVALID;
+            __TransmitterPutRequest(Ring, Request);
             continue;
         }
 
-        ListEntry = RemoveHeadList(&Ring->Queued);
-        if (ListEntry == &Ring->Queued)
-            break;
+        if (!IsListEmpty(&Ring->PacketQueue)) {
+            PLIST_ENTRY                 ListEntry;
+            PXENVIF_TRANSMITTER_PACKET  Packet;
 
-        Packet = CONTAINING_RECORD(ListEntry, XENVIF_TRANSMITTER_PACKET, 
ListEntry);
-        Packet->ListEntry.Flink = Packet->ListEntry.Blink = NULL;
-        Packet->Value = 0;
+            ListEntry = RemoveHeadList(&Ring->PacketQueue);
+            RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
 
-        status = __TransmitterRingPreparePacket(Ring, Packet);
-        if (!NT_SUCCESS(status)) {
-            PXENVIF_TRANSMITTER Transmitter;
-            PXENVIF_FRONTEND    Frontend;
+            Packet = CONTAINING_RECORD(ListEntry,
+                                       XENVIF_TRANSMITTER_PACKET,
+                                       ListEntry);
 
-            Transmitter = Ring->Transmitter;
-            Frontend = Transmitter->Frontend;
+            Packet->Value = 0;
 
-            ASSERT(status != STATUS_BUFFER_OVERFLOW);
+            status = __TransmitterRingPreparePacket(Ring, Packet);
+            if (!NT_SUCCESS(status)) {
+                PXENVIF_TRANSMITTER Transmitter;
+                PXENVIF_FRONTEND    Frontend;
 
-            // Fake that we prapared and sent this packet
-            Ring->PacketsPrepared++;
-            Ring->PacketsSent++;
-            Ring->PacketsFaked++;
+                Transmitter = Ring->Transmitter;
+                Frontend = Transmitter->Frontend;
 
-            Packet->Completion.Status = XENVIF_TRANSMITTER_PACKET_DROPPED;
+                ASSERT(status != STATUS_BUFFER_OVERFLOW);
 
-            FrontendIncrementStatistic(Frontend,
-                                       XENVIF_TRANSMITTER_FRONTEND_ERRORS,
-                                       1);
+                // Fake that we prapared and sent this packet
+                Ring->PacketsPrepared++;
+                Ring->PacketsSent++;
+                Ring->PacketsFaked++;
 
-            __TransmitterRingCompletePacket(Ring, Packet);
+                Packet->Completion.Status = XENVIF_TRANSMITTER_PACKET_DROPPED;
+
+                FrontendIncrementStatistic(Frontend,
+                                           XENVIF_TRANSMITTER_FRONTEND_ERRORS,
+                                           1);
+
+                __TransmitterRingCompletePacket(Ring, Packet);
+            }
+
+            ASSERT3U(Ring->PacketsPrepared, ==, Ring->PacketsCopied + 
Ring->PacketsGranted + Ring->PacketsFaked);
+            continue;
         }
 
-        ASSERT3U(Ring->PacketsPrepared, ==, Ring->PacketsCopied + 
Ring->PacketsGranted + Ring->PacketsFaked);
+        break;
     }
 
     __TransmitterRingPushRequests(Ring);
@@ -2607,10 +2901,10 @@ __TransmitterRingReleaseLock(
         TransmitterRingSwizzle(Ring);
         TransmitterRingSchedule(Ring);
 
-        ListEntry = Ring->Completed.Flink;
-        if (!IsListEmpty(&Ring->Completed)) {
-            RemoveEntryList(&Ring->Completed);
-            InitializeListHead(&Ring->Completed);
+        ListEntry = Ring->PacketComplete.Flink;
+        if (!IsListEmpty(&Ring->PacketComplete)) {
+            RemoveEntryList(&Ring->PacketComplete);
+            InitializeListHead(&Ring->PacketComplete);
             AppendTailList(&List, ListEntry);
         }
     } while (!__TransmitterRingTryReleaseLock(Ring));
@@ -2787,62 +3081,6 @@ TransmitterRingWatchdog(
     return STATUS_SUCCESS;
 }
 
-static FORCEINLINE VOID
-__TransmitterRingUpdateAddressTable(
-    IN  PXENVIF_TRANSMITTER_RING    Ring,
-    IN  PSOCKADDR_INET              Table,
-    IN  ULONG                       Count
-    )
-{
-    NTSTATUS                        status;
-
-    __TransmitterRingAcquireLock(Ring);
-
-    if (Ring->AddressCount != 0) {
-        Ring->AddressCount = 0;
-
-        ASSERT(Ring->AddressTable != NULL);
-        __TransmitterFree(Ring->AddressTable);
-        Ring->AddressTable = NULL;
-    }
-
-    if (Count == 0)
-        goto done;
-
-    Ring->AddressTable = __TransmitterAllocate(sizeof (SOCKADDR_INET) * Count);
-
-    status = STATUS_NO_MEMORY;
-    if (Ring->AddressTable == NULL)
-        goto fail1;
-
-    RtlCopyMemory(Ring->AddressTable, Table, sizeof (SOCKADDR_INET) * Count);
-    Ring->AddressCount = Count;
-
-    // Re-advertize if we were part way through
-    if (Ring->AddressIndex != 0)
-        Ring->AddressIndex = Ring->AddressCount * 3;
-
-done:
-    __TransmitterRingReleaseLock(Ring);
-
-    return;
-
-fail1:
-    Error("fail1 (%08x)\n", status);
-
-    __TransmitterRingReleaseLock(Ring);
-}
-
-static FORCEINLINE VOID
-__TransmitterRingAdvertiseAddresses(
-    IN  PXENVIF_TRANSMITTER_RING    Ring
-    )
-{
-    __TransmitterRingAcquireLock(Ring);
-    Ring->AddressIndex = Ring->AddressCount * 3;
-    __TransmitterRingReleaseLock(Ring);
-}
-
 static FORCEINLINE NTSTATUS
 __TransmitterRingInitialize(
     IN  PXENVIF_TRANSMITTER         Transmitter,
@@ -2869,8 +3107,9 @@ __TransmitterRingInitialize(
     if ((*Ring)->Path == NULL)
         goto fail2;
 
-    InitializeListHead(&(*Ring)->Queued);
-    InitializeListHead(&(*Ring)->Completed);
+    InitializeListHead(&(*Ring)->PacketQueue);
+    InitializeListHead(&(*Ring)->RequestQueue);
+    InitializeListHead(&(*Ring)->PacketComplete);
 
     KeInitializeDpc(&(*Ring)->Dpc, TransmitterRingDpc, *Ring);
 
@@ -2901,7 +3140,7 @@ __TransmitterRingInitialize(
 
     status = RtlStringCbPrintfA(Name,
                                 sizeof (Name),
-                                "%s_transmitter_req_id",
+                                "%s_transmitter_multicast_control",
                                 (*Ring)->Path);
     if (!NT_SUCCESS(status))
         goto fail5;
@@ -2910,12 +3149,37 @@ __TransmitterRingInitialize(
         if (Name[Index] == '/')
             Name[Index] = '_';
 
+    status = XENBUS_CACHE(Create,
+                          &Transmitter->CacheInterface,
+                          Name,
+                          sizeof (XENVIF_TRANSMITTER_MULTICAST_CONTROL),
+                          0,
+                          TransmitterMulticastControlCtor,
+                          TransmitterMulticastControlDtor,
+                          TransmitterRingAcquireLock,
+                          TransmitterRingReleaseLock,
+                          *Ring,
+                          &(*Ring)->MulticastControlCache);
+    if (!NT_SUCCESS(status))
+        goto fail6;
+
+    status = RtlStringCbPrintfA(Name,
+                                sizeof (Name),
+                                "%s_transmitter_req_id",
+                                (*Ring)->Path);
+    if (!NT_SUCCESS(status))
+        goto fail7;
+
+    for (Index = 0; Name[Index] != '\0'; Index++)
+        if (Name[Index] == '/')
+            Name[Index] = '_';
+
     status = XENBUS_RANGE_SET(Create,
                               &Transmitter->RangeSetInterface,
                               Name,
                               &(*Ring)->RangeSet);
     if (!NT_SUCCESS(status))
-        goto fail6;
+        goto fail8;
 
     status = XENBUS_RANGE_SET(Put,
                               &Transmitter->RangeSetInterface,
@@ -2923,14 +3187,14 @@ __TransmitterRingInitialize(
                               1,
                               XENVIF_TRANSMITTER_MAXIMUM_FRAGMENT_ID);
     if (!NT_SUCCESS(status))
-        goto fail7;
+        goto fail9;
 
     status = RtlStringCbPrintfA(Name,
                                 sizeof (Name),
                                 "%s_transmitter_fragment",
                                 (*Ring)->Path);
     if (!NT_SUCCESS(status))
-        goto fail8;
+        goto fail10;
 
     for (Index = 0; Name[Index] != '\0'; Index++)
         if (Name[Index] == '/')
@@ -2948,29 +3212,65 @@ __TransmitterRingInitialize(
                           *Ring,
                           &(*Ring)->FragmentCache);
     if (!NT_SUCCESS(status))
-        goto fail9;
+        goto fail11;
+
+    status = RtlStringCbPrintfA(Name,
+                                sizeof (Name),
+                                "%s_transmitter_request",
+                                (*Ring)->Path);
+    if (!NT_SUCCESS(status))
+        goto fail12;
+
+    for (Index = 0; Name[Index] != '\0'; Index++)
+        if (Name[Index] == '/')
+            Name[Index] = '_';
+
+    status = XENBUS_CACHE(Create,
+                          &Transmitter->CacheInterface,
+                          Name,
+                          sizeof (XENVIF_TRANSMITTER_REQUEST),
+                          0,
+                          TransmitterRequestCtor,
+                          TransmitterRequestDtor,
+                          TransmitterRingAcquireLock,
+                          TransmitterRingReleaseLock,
+                          *Ring,
+                          &(*Ring)->RequestCache);
+    if (!NT_SUCCESS(status))
+        goto fail13;
 
     status = ThreadCreate(TransmitterRingWatchdog,
                           *Ring,
                           &(*Ring)->WatchdogThread);
     if (!NT_SUCCESS(status))
-        goto fail10;
+        goto fail14;
 
     return STATUS_SUCCESS;
 
-fail10:
-    Error("fail10\n");
+fail14:
+    Error("fail14\n");
+
+    XENBUS_CACHE(Destroy,
+                 &Transmitter->CacheInterface,
+                 (*Ring)->RequestCache);
+    (*Ring)->RequestCache = NULL;
+
+fail13:
+    Error("fail13\n");
+
+fail12:
+    Error("fail12\n");
 
     XENBUS_CACHE(Destroy,
                  &Transmitter->CacheInterface,
                  (*Ring)->FragmentCache);
     (*Ring)->FragmentCache = NULL;
 
-fail9:
-    Error("fail9\n");
+fail11:
+    Error("fail11\n");
 
-fail8:
-    Error("fail8\n");
+fail10:
+    Error("fail10\n");
 
     (VOID) XENBUS_RANGE_SET(Get,
                             &Transmitter->RangeSetInterface,
@@ -2978,14 +3278,25 @@ fail8:
                             1,
                             XENVIF_TRANSMITTER_MAXIMUM_FRAGMENT_ID);
 
-fail7:
-    Error("fail7\n");
+fail9:
+    Error("fail9\n");
 
     XENBUS_RANGE_SET(Destroy,
                      &Transmitter->RangeSetInterface,
                      (*Ring)->RangeSet);
     (*Ring)->RangeSet = NULL;
 
+fail8:
+    Error("fail8\n");
+
+fail7:
+    Error("fail7\n");
+
+    XENBUS_CACHE(Destroy,
+                 &Transmitter->CacheInterface,
+                 (*Ring)->MulticastControlCache);
+    (*Ring)->MulticastControlCache = NULL;
+
 fail6:
     Error("fail6\n");
 
@@ -3005,8 +3316,9 @@ fail3:
 
     RtlZeroMemory(&(*Ring)->Dpc, sizeof (KDPC));
 
-    RtlZeroMemory(&(*Ring)->Queued, sizeof (LIST_ENTRY));
-    RtlZeroMemory(&(*Ring)->Completed, sizeof (LIST_ENTRY));
+    RtlZeroMemory(&(*Ring)->PacketComplete, sizeof (LIST_ENTRY));
+    RtlZeroMemory(&(*Ring)->RequestQueue, sizeof (LIST_ENTRY));
+    RtlZeroMemory(&(*Ring)->PacketQueue, sizeof (LIST_ENTRY));
 
     FrontendFreePath(Frontend, (*Ring)->Path);
     (*Ring)->Path = NULL;
@@ -3300,13 +3612,27 @@ __TransmitterRingDisable(
     Ring->Enabled = FALSE;
 
     // Release any fragments associated with a pending packet
-    Packet = __TransmitterRingUnpreparePacket(Ring);
+    Packet = __TransmitterRingUnprepareFragments(Ring);
 
     // Put any packet back on the head of the queue
     if (Packet != NULL)
-        InsertHeadList(&Ring->Queued, &Packet->ListEntry);
+        InsertHeadList(&Ring->PacketQueue, &Packet->ListEntry);
+
+    // Discard any pending requests
+    while (!IsListEmpty(&Ring->RequestQueue)) {
+        PLIST_ENTRY                 ListEntry;
+        PXENVIF_TRANSMITTER_REQUEST Request;
 
-    Ring->AddressIndex = 0;
+        ListEntry = RemoveHeadList(&Ring->RequestQueue);
+        ASSERT3P(ListEntry, !=, &Ring->RequestQueue);
+
+        Request = CONTAINING_RECORD(ListEntry,
+                                    XENVIF_TRANSMITTER_REQUEST,
+                                    ListEntry);
+
+        Request->Type = XENVIF_TRANSMITTER_REQUEST_TYPE_INVALID;
+        __TransmitterPutRequest(Ring, Request);
+    }
 
     status = XENBUS_STORE(Read,
                           &Transmitter->StoreInterface,
@@ -3433,20 +3759,17 @@ __TransmitterRingTeardown(
     Ring->PacketsPrepared = 0;
     Ring->PacketsQueued = 0;
 
-    if (Ring->AddressCount != 0) {
-        ASSERT(Ring->AddressTable != NULL);
-        __TransmitterFree(Ring->AddressTable);
-    }
-
-    Ring->AddressTable = NULL;
-    Ring->AddressCount = 0;
-
     ThreadAlert(Ring->WatchdogThread);
     ThreadJoin(Ring->WatchdogThread);
     Ring->WatchdogThread = NULL;
 
     XENBUS_CACHE(Destroy,
                  &Transmitter->CacheInterface,
+                 Ring->RequestCache);
+    Ring->RequestCache = NULL;
+
+    XENBUS_CACHE(Destroy,
+                 &Transmitter->CacheInterface,
                  Ring->FragmentCache);
     Ring->FragmentCache = NULL;
 
@@ -3463,14 +3786,22 @@ __TransmitterRingTeardown(
 
     XENBUS_CACHE(Destroy,
                  &Transmitter->CacheInterface,
+                 Ring->MulticastControlCache);
+    Ring->MulticastControlCache = NULL;
+
+    XENBUS_CACHE(Destroy,
+                 &Transmitter->CacheInterface,
                  Ring->BufferCache);
     Ring->BufferCache = NULL;
 
-    ASSERT(IsListEmpty(&Ring->Queued));
-    RtlZeroMemory(&Ring->Queued, sizeof (LIST_ENTRY));
+    ASSERT(IsListEmpty(&Ring->PacketComplete));
+    RtlZeroMemory(&Ring->PacketComplete, sizeof (LIST_ENTRY));
+
+    ASSERT(IsListEmpty(&Ring->RequestQueue));
+    RtlZeroMemory(&Ring->RequestQueue, sizeof (LIST_ENTRY));
 
-    ASSERT(IsListEmpty(&Ring->Completed));
-    RtlZeroMemory(&Ring->Completed, sizeof (LIST_ENTRY));
+    ASSERT(IsListEmpty(&Ring->PacketQueue));
+    RtlZeroMemory(&Ring->PacketQueue, sizeof (LIST_ENTRY));
 
     FrontendFreePath(Frontend, Ring->Path);
     Ring->Path = NULL;
@@ -3520,12 +3851,12 @@ __TransmitterRingAbortPackets(
 
     TransmitterRingSwizzle(Ring);
 
-    while (!IsListEmpty(&Ring->Queued)) {
+    while (!IsListEmpty(&Ring->PacketQueue)) {
         PLIST_ENTRY                 ListEntry;
         PXENVIF_TRANSMITTER_PACKET  Packet;
         
-        ListEntry = RemoveHeadList(&Ring->Queued);
-        ASSERT3P(ListEntry, !=, &Ring->Queued);
+        ListEntry = RemoveHeadList(&Ring->PacketQueue);
+        ASSERT3P(ListEntry, !=, &Ring->PacketQueue);
 
         Packet = CONTAINING_RECORD(ListEntry, XENVIF_TRANSMITTER_PACKET, 
ListEntry);
         Packet->ListEntry.Flink = Packet->ListEntry.Blink = NULL;
@@ -3548,6 +3879,168 @@ __TransmitterRingAbortPackets(
     __TransmitterRingReleaseLock(Ring);
 }
 
+static FORCEINLINE NTSTATUS
+__TransmitterRingQueueArp(
+    IN  PXENVIF_TRANSMITTER_RING    Ring,
+    IN  PIPV4_ADDRESS               Address
+    )
+{
+    PXENVIF_TRANSMITTER             Transmitter;
+    PXENVIF_FRONTEND                Frontend;
+    PXENVIF_TRANSMITTER_REQUEST     Request;
+    NTSTATUS                        status;
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+
+    __TransmitterRingAcquireLock(Ring);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (!Ring->Enabled)
+        goto fail1;
+
+    Request = __TransmitterGetRequest(Ring);
+
+    status = STATUS_NO_MEMORY;
+    if (Request == NULL)
+        goto fail2;
+
+    Request->Type = XENVIF_TRANSMITTER_REQUEST_TYPE_ARP;
+    Request->Arp.Address = *Address;
+
+    InsertTailList(&Ring->RequestQueue, &Request->ListEntry);
+
+    __TransmitterRingReleaseLock(Ring);
+
+    Info("%s: %u.%u.%u.%u\n",
+         FrontendGetPath(Frontend),
+         Address->Byte[0],
+         Address->Byte[1],
+         Address->Byte[2],
+         Address->Byte[3]);
+
+    return STATUS_SUCCESS;
+
+fail2:
+fail1:
+    __TransmitterRingReleaseLock(Ring);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__TransmitterRingQueueNeighbourAdvertisement(
+    IN  PXENVIF_TRANSMITTER_RING    Ring,
+    IN  PIPV6_ADDRESS               Address
+    )
+{
+    PXENVIF_TRANSMITTER             Transmitter;
+    PXENVIF_FRONTEND                Frontend;
+    PXENVIF_TRANSMITTER_REQUEST     Request;
+    NTSTATUS                        status;
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+
+    __TransmitterRingAcquireLock(Ring);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (!Ring->Enabled)
+        goto fail1;
+
+    Request = __TransmitterGetRequest(Ring);
+
+    status = STATUS_NO_MEMORY;
+    if (Request == NULL)
+        goto fail2;
+
+    Request->Type = XENVIF_TRANSMITTER_REQUEST_TYPE_NEIGHBOUR_ADVERTISEMENT;
+    Request->NeighbourAdvertisement.Address = *Address;
+
+    InsertTailList(&Ring->RequestQueue, &Request->ListEntry);
+
+    __TransmitterRingReleaseLock(Ring);
+
+    Info("%s: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+         FrontendGetPath(Frontend),
+         HTONS(Address->Word[0]),
+         HTONS(Address->Word[1]),
+         HTONS(Address->Word[2]),
+         HTONS(Address->Word[3]),
+         HTONS(Address->Word[4]),
+         HTONS(Address->Word[5]),
+         HTONS(Address->Word[6]),
+         HTONS(Address->Word[7]));
+
+    return STATUS_SUCCESS;
+
+fail2:
+fail1:
+    __TransmitterRingReleaseLock(Ring);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__TransmitterRingQueueMulticastControl(
+    IN  PXENVIF_TRANSMITTER_RING    Ring,
+    IN  PETHERNET_ADDRESS           Address,
+    IN  BOOLEAN                     Add
+    )
+{
+    PXENVIF_TRANSMITTER             Transmitter;
+    PXENVIF_FRONTEND                Frontend;
+    PXENVIF_TRANSMITTER_REQUEST     Request;
+    NTSTATUS                        status;
+
+    Transmitter = Ring->Transmitter;
+
+    status = STATUS_NOT_SUPPORTED;
+    if (!Transmitter->MulticastControl)
+        goto fail1;
+
+    Frontend = Transmitter->Frontend;
+
+    __TransmitterRingAcquireLock(Ring);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (!Ring->Enabled)
+        goto fail2;
+
+    Request = __TransmitterGetRequest(Ring);
+
+    status = STATUS_NO_MEMORY;
+    if (Request == NULL)
+        goto fail3;
+
+    Request->Type = XENVIF_TRANSMITTER_REQUEST_TYPE_MULTICAST_CONTROL;
+    Request->MulticastControl.Address = *Address;
+    Request->MulticastControl.Add = Add;
+
+    InsertTailList(&Ring->RequestQueue, &Request->ListEntry);
+
+    __TransmitterRingReleaseLock(Ring);
+
+    Info("%s: %s %02X:%02X:%02X:%02X:%02X:%02X\n",
+         FrontendGetPath(Frontend),
+         (Add) ? "ADD" : "REMOVE",
+         Address->Byte[0],
+         Address->Byte[1],
+         Address->Byte[2],
+         Address->Byte[3],
+         Address->Byte[4],
+         Address->Byte[5]);
+
+    return STATUS_SUCCESS;
+
+fail3:
+fail2:
+    __TransmitterRingReleaseLock(Ring);
+
+fail1:
+    return status;
+}
+
 static VOID
 TransmitterDebugCallback(
     IN  PVOID           Argument,
@@ -3796,6 +4289,22 @@ TransmitterConnect(
                      Buffer);
     }
 
+    status = XENBUS_STORE(Read,
+                          &Transmitter->StoreInterface,
+                          NULL,
+                          FrontendGetBackendPath(Frontend),
+                          "feature-multicast-control",
+                          &Buffer);
+    if (!NT_SUCCESS(status)) {
+        Transmitter->MulticastControl = FALSE;
+    } else {
+        Transmitter->MulticastControl = (BOOLEAN)strtol(Buffer, NULL, 2);
+
+        XENBUS_STORE(Free,
+                     &Transmitter->StoreInterface,
+                     Buffer);
+    }
+
     Transmitter->NumQueues = FrontendGetNumQueues(Frontend);
     ASSERT3U(Transmitter->NumQueues, <=, Transmitter->MaxQueues);
 
@@ -3880,22 +4389,38 @@ TransmitterStoreWrite(
     IN  PXENBUS_STORE_TRANSACTION   Transaction
     )
 {
+    PXENVIF_FRONTEND                Frontend;
     NTSTATUS                        status;
     LONG                            Index;
 
+    Frontend = Transmitter->Frontend;
+
+    status = XENBUS_STORE(Printf,
+                          &Transmitter->StoreInterface,
+                          Transaction,
+                          FrontendGetPath(Frontend),
+                          "request-multicast-control",
+                          "%u",
+                          TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
     Index = 0;
     while (Index < Transmitter->NumQueues) {
         PXENVIF_TRANSMITTER_RING    Ring = Transmitter->Ring[Index];
 
         status = __TransmitterRingStoreWrite(Ring, Transaction);
         if (!NT_SUCCESS(status))
-            goto fail1;
+            goto fail2;
 
         Index++;
     }    
 
     return STATUS_SUCCESS;
 
+fail2:
+    Error("fail2\n");
+
 fail1:
     Error("fail1 (%08x)\n", status);
 
@@ -3954,6 +4479,7 @@ TransmitterDisconnect(
 
     Frontend = Transmitter->Frontend;
 
+    Transmitter->MulticastControl = FALSE;
     Transmitter->Split = FALSE;
 
     XENBUS_DEBUG(Deregister,
@@ -4046,42 +4572,6 @@ TransmitterTeardown(
     __TransmitterFree(Transmitter);
 }
 
-VOID
-TransmitterUpdateAddressTable(
-    IN  PXENVIF_TRANSMITTER     Transmitter,
-    IN  SOCKADDR_INET           Table[],
-    IN  ULONG                   Count
-    )
-{
-    KIRQL                       Irql;
-    PXENVIF_TRANSMITTER_RING    Ring;
-
-    // Make sure we don't suspend
-    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
-
-    // Use the first ring for address advertisment
-    Ring = Transmitter->Ring[0];
-    ASSERT3U(Ring, !=, NULL);
-
-    __TransmitterRingUpdateAddressTable(Ring, Table, Count);
-
-    KeLowerIrql(Irql);
-}
-
-VOID
-TransmitterAdvertiseAddresses(
-    IN  PXENVIF_TRANSMITTER     Transmitter
-    )
-{
-    PXENVIF_TRANSMITTER_RING    Ring;
-
-    // Use the first ring for address advertisment
-    Ring = Transmitter->Ring[0];
-    ASSERT3U(Ring, !=, NULL);
-
-    __TransmitterRingAdvertiseAddresses(Ring);
-}
-
 NTSTATUS
 TransmitterSetPacketOffset(
     IN  PXENVIF_TRANSMITTER                 Transmitter,
@@ -4346,6 +4836,40 @@ TransmitterAbortPackets(
 }
 
 VOID
+TransmitterQueueArp(
+    IN  PXENVIF_TRANSMITTER     Transmitter,
+    IN  PIPV4_ADDRESS           Address
+    )
+{
+    PXENVIF_TRANSMITTER_RING    Ring = Transmitter->Ring[0];
+
+    (VOID) __TransmitterRingQueueArp(Ring, Address);
+}
+
+VOID
+TransmitterQueueNeighbourAdvertisement(
+    IN  PXENVIF_TRANSMITTER     Transmitter,
+    IN  PIPV6_ADDRESS           Address
+    )
+{
+    PXENVIF_TRANSMITTER_RING    Ring = Transmitter->Ring[0];
+
+    (VOID) __TransmitterRingQueueNeighbourAdvertisement(Ring, Address);
+}
+
+VOID
+TransmitterQueueMulticastControl(
+    IN  PXENVIF_TRANSMITTER     Transmitter,
+    IN  PETHERNET_ADDRESS       Address,
+    IN  BOOLEAN                 Add
+    )
+{
+    PXENVIF_TRANSMITTER_RING    Ring = Transmitter->Ring[0];
+
+    (VOID) __TransmitterRingQueueMulticastControl(Ring, Address, Add);
+}
+
+VOID
 TransmitterQueryRingSize(
     IN  PXENVIF_TRANSMITTER Transmitter,
     OUT PULONG              Size
diff --git a/src/xenvif/transmitter.h b/src/xenvif/transmitter.h
index fad0762..eddc51b 100644
--- a/src/xenvif/transmitter.h
+++ b/src/xenvif/transmitter.h
@@ -34,7 +34,9 @@
 
 #include <ntddk.h>
 #include <netioapi.h>
+
 #include <vif_interface.h>
+#include <tcpip.h>
 
 #include "frontend.h"
 
@@ -89,21 +91,28 @@ TransmitterAbortPackets(
     );
 
 extern VOID
-TransmitterQueryRingSize(
-    IN  PXENVIF_TRANSMITTER Transmitter,
-    OUT PULONG              Size
+TransmitterQueueArp(
+    IN  PXENVIF_TRANSMITTER     Transmitter,
+    IN  PIPV4_ADDRESS           Address
     );
 
 extern VOID
-TransmitterUpdateAddressTable(
-    IN  PXENVIF_TRANSMITTER Transmitter,
-    IN  PSOCKADDR_INET      Table,
-    IN  ULONG               Count
+TransmitterQueueNeighbourAdvertisement(
+    IN  PXENVIF_TRANSMITTER     Transmitter,
+    IN  PIPV6_ADDRESS           Address
     );
 
 extern VOID
-TransmitterAdvertiseAddresses(
-    IN  PXENVIF_TRANSMITTER Transmitter
+TransmitterQueueMulticastControl(
+    IN  PXENVIF_TRANSMITTER     Transmitter,
+    IN  PETHERNET_ADDRESS       Address,
+    IN  BOOLEAN                 Add
+    );
+
+extern VOID
+TransmitterQueryRingSize(
+    IN  PXENVIF_TRANSMITTER Transmitter,
+    OUT PULONG              Size
     );
 
 extern VOID
diff --git a/src/xenvif/vif.c b/src/xenvif/vif.c
index 35af384..b2bbf05 100644
--- a/src/xenvif/vif.c
+++ b/src/xenvif/vif.c
@@ -501,16 +501,24 @@ VifMacSetMulticastAddresses(
     )
 {
     PXENVIF_VIF_CONTEXT     Context = Interface->Context;
+    ULONG                   Index;
     NTSTATUS                status;
 
+    status = STATUS_INVALID_PARAMETER;
+    for (Index = 0; Index < Count; Index++) {
+        if (!(Address[Index].Byte[0] & 0x01))
+            goto done;
+    }
+
     AcquireMrswLockShared(&Context->Lock);
 
-    status = MacSetMulticastAddresses(FrontendGetMac(Context->Frontend),
-                                      Address,
-                                      Count);
+    status = FrontendSetMulticastAddresses(Context->Frontend,
+                                           Address,
+                                           Count);
 
     ReleaseMrswLockShared(&Context->Lock);
 
+done:
     return status;
 }
 
@@ -598,7 +606,10 @@ VifSuspendCallbackLate(
     status = FrontendSetState(Context->Frontend, FRONTEND_ENABLED);
     ASSERT(NT_SUCCESS(status));
 
-    TransmitterAdvertiseAddresses(FrontendGetTransmitter(Context->Frontend));
+    // We do this three times to make sure switches take note
+    FrontendAdvertiseIpAddresses(Context->Frontend);
+    FrontendAdvertiseIpAddresses(Context->Frontend);
+    FrontendAdvertiseIpAddresses(Context->Frontend);
 }
 
 static NTSTATUS
-- 
2.1.1


_______________________________________________
win-pv-devel mailing list
win-pv-devel@xxxxxxxxxxxxxxxxxxxx
http://lists.xenproject.org/cgi-bin/mailman/listinfo/win-pv-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.