| ... | ... |
@@ -29,9 +29,11 @@ |
| 29 | 29 |
#pragma pack(1) |
| 30 | 30 |
|
| 31 | 31 |
#define IP_HEADER_SIZE 20 |
| 32 |
+#define IPV6_HEADER_SIZE 40 |
|
| 32 | 33 |
|
| 33 | 34 |
typedef unsigned char MACADDR [6]; |
| 34 | 35 |
typedef unsigned long IPADDR; |
| 36 |
+typedef unsigned char IPV6ADDR [16]; |
|
| 35 | 37 |
|
| 36 | 38 |
//----------------- |
| 37 | 39 |
// Ethernet address |
| ... | ... |
@@ -55,6 +57,7 @@ typedef struct |
| 55 | 55 |
MACADDR src; /* source ether addr */ |
| 56 | 56 |
|
| 57 | 57 |
# define ETH_P_IP 0x0800 /* IPv4 protocol */ |
| 58 |
+# define ETH_P_IPV6 0x86DD /* IPv6 protocol */ |
|
| 58 | 59 |
# define ETH_P_ARP 0x0806 /* ARP protocol */ |
| 59 | 60 |
USHORT proto; /* packet type ID field */ |
| 60 | 61 |
} ETH_HEADER, *PETH_HEADER; |
| ... | ... |
@@ -161,4 +164,61 @@ typedef struct {
|
| 161 | 161 |
#define TCPOPT_MAXSEG 2 |
| 162 | 162 |
#define TCPOLEN_MAXSEG 4 |
| 163 | 163 |
|
| 164 |
+//------------ |
|
| 165 |
+// IPv6 Header |
|
| 166 |
+//------------ |
|
| 167 |
+ |
|
| 168 |
+typedef struct {
|
|
| 169 |
+ UCHAR version_prio; |
|
| 170 |
+ UCHAR flow_lbl[3]; |
|
| 171 |
+ USHORT payload_len; |
|
| 172 |
+# define IPPROTO_ICMPV6 0x3a /* ICMP protocol v6 */ |
|
| 173 |
+ UCHAR nexthdr; |
|
| 174 |
+ UCHAR hop_limit; |
|
| 175 |
+ IPV6ADDR saddr; |
|
| 176 |
+ IPV6ADDR daddr; |
|
| 177 |
+} IPV6HDR; |
|
| 178 |
+ |
|
| 179 |
+//-------------------------------------------- |
|
| 180 |
+// IPCMPv6 NS/NA Packets (RFC4443 and RFC4861) |
|
| 181 |
+//-------------------------------------------- |
|
| 182 |
+ |
|
| 183 |
+// Neighbor Solictiation - RFC 4861, 4.3 |
|
| 184 |
+// (this is just the ICMPv6 part of the packet) |
|
| 185 |
+typedef struct {
|
|
| 186 |
+ UCHAR type; |
|
| 187 |
+# define ICMPV6_TYPE_NS 135 // neighbour solicitation |
|
| 188 |
+ UCHAR code; |
|
| 189 |
+# define ICMPV6_CODE_0 0 // no specific sub-code for NS/NA |
|
| 190 |
+ USHORT checksum; |
|
| 191 |
+ ULONG reserved; |
|
| 192 |
+ IPV6ADDR target_addr; |
|
| 193 |
+} ICMPV6_NS; |
|
| 194 |
+ |
|
| 195 |
+// Neighbor Advertisement - RFC 4861, 4.4 + 4.6/4.6.1 |
|
| 196 |
+// (this is just the ICMPv6 payload) |
|
| 197 |
+typedef struct {
|
|
| 198 |
+ UCHAR type; |
|
| 199 |
+# define ICMPV6_TYPE_NA 136 // neighbour advertisement |
|
| 200 |
+ UCHAR code; |
|
| 201 |
+# define ICMPV6_CODE_0 0 // no specific sub-code for NS/NA |
|
| 202 |
+ USHORT checksum; |
|
| 203 |
+ UCHAR rso_bits; // Router(0), Solicited(2), Ovrrd(4) |
|
| 204 |
+ UCHAR reserved[3]; |
|
| 205 |
+ IPV6ADDR target_addr; |
|
| 206 |
+// always include "Target Link-layer Address" option (RFC 4861 4.6.1) |
|
| 207 |
+ UCHAR opt_type; |
|
| 208 |
+#define ICMPV6_OPTION_TLLA 2 |
|
| 209 |
+ UCHAR opt_length; |
|
| 210 |
+#define ICMPV6_LENGTH_TLLA 1 // multiplied by 8 -> 1 = 8 bytes |
|
| 211 |
+ MACADDR target_macaddr; |
|
| 212 |
+} ICMPV6_NA; |
|
| 213 |
+ |
|
| 214 |
+// this is the complete packet with Ethernet and IPv6 headers |
|
| 215 |
+typedef struct {
|
|
| 216 |
+ ETH_HEADER eth; |
|
| 217 |
+ IPV6HDR ipv6; |
|
| 218 |
+ ICMPV6_NA icmpv6; |
|
| 219 |
+} ICMPV6_NA_PKT; |
|
| 220 |
+ |
|
| 164 | 221 |
#pragma pack() |
| ... | ... |
@@ -1430,6 +1430,158 @@ NDIS_STATUS AdapterModify |
| 1430 | 1430 |
return l_Status; |
| 1431 | 1431 |
} |
| 1432 | 1432 |
|
| 1433 |
+// checksum code for ICMPv6 packet, taken from dhcp.c / udp_checksum |
|
| 1434 |
+// see RFC 4443, 2.3, and RFC 2460, 8.1 |
|
| 1435 |
+USHORT |
|
| 1436 |
+icmpv6_checksum (const UCHAR *buf, |
|
| 1437 |
+ const int len_icmpv6, |
|
| 1438 |
+ const UCHAR *saddr6, |
|
| 1439 |
+ const UCHAR *daddr6) |
|
| 1440 |
+{
|
|
| 1441 |
+ USHORT word16; |
|
| 1442 |
+ ULONG sum = 0; |
|
| 1443 |
+ int i; |
|
| 1444 |
+ |
|
| 1445 |
+ // make 16 bit words out of every two adjacent 8 bit words and |
|
| 1446 |
+ // calculate the sum of all 16 bit words |
|
| 1447 |
+ for (i = 0; i < len_icmpv6; i += 2){
|
|
| 1448 |
+ word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_icmpv6) ? (buf[i+1] & 0xFF) : 0); |
|
| 1449 |
+ sum += word16; |
|
| 1450 |
+ } |
|
| 1451 |
+ |
|
| 1452 |
+ // add the IPv6 pseudo header which contains the IP source and destination addresses |
|
| 1453 |
+ for (i = 0; i < 16; i += 2){
|
|
| 1454 |
+ word16 =((saddr6[i] << 8) & 0xFF00) + (saddr6[i+1] & 0xFF); |
|
| 1455 |
+ sum += word16; |
|
| 1456 |
+ } |
|
| 1457 |
+ for (i = 0; i < 16; i += 2){
|
|
| 1458 |
+ word16 =((daddr6[i] << 8) & 0xFF00) + (daddr6[i+1] & 0xFF); |
|
| 1459 |
+ sum += word16; |
|
| 1460 |
+ } |
|
| 1461 |
+ |
|
| 1462 |
+ // the next-header number and the length of the ICMPv6 packet |
|
| 1463 |
+ sum += (USHORT) IPPROTO_ICMPV6 + (USHORT) len_icmpv6; |
|
| 1464 |
+ |
|
| 1465 |
+ // keep only the last 16 bits of the 32 bit calculated sum and add the carries |
|
| 1466 |
+ while (sum >> 16) |
|
| 1467 |
+ sum = (sum & 0xFFFF) + (sum >> 16); |
|
| 1468 |
+ |
|
| 1469 |
+ // Take the one's complement of sum |
|
| 1470 |
+ return ((USHORT) ~sum); |
|
| 1471 |
+} |
|
| 1472 |
+ |
|
| 1473 |
+// check IPv6 packet for "is this an IPv6 Neighbor Solicitation that |
|
| 1474 |
+// the tap driver needs to answer?" |
|
| 1475 |
+// see RFC 4861 4.3 for the different cases |
|
| 1476 |
+static IPV6ADDR IPV6_NS_TARGET_MCAST = |
|
| 1477 |
+ { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
| 1478 |
+ 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x08 }; |
|
| 1479 |
+static IPV6ADDR IPV6_NS_TARGET_UNICAST = |
|
| 1480 |
+ { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
| 1481 |
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 }; |
|
| 1482 |
+ |
|
| 1483 |
+BOOLEAN |
|
| 1484 |
+HandleIPv6NeighborDiscovery( TapAdapterPointer p_Adapter, UCHAR * m_Data ) |
|
| 1485 |
+{
|
|
| 1486 |
+ const ETH_HEADER * e = (ETH_HEADER *) m_Data; |
|
| 1487 |
+ const IPV6HDR *ipv6 = (IPV6HDR *) (m_Data + sizeof (ETH_HEADER)); |
|
| 1488 |
+ const ICMPV6_NS * icmpv6_ns = (ICMPV6_NS *) (m_Data + sizeof (ETH_HEADER) + sizeof (IPV6HDR)); |
|
| 1489 |
+ ICMPV6_NA_PKT *na; |
|
| 1490 |
+ USHORT icmpv6_len, icmpv6_csum; |
|
| 1491 |
+ |
|
| 1492 |
+ // we don't really care about the destination MAC address here |
|
| 1493 |
+ // - it's either a multicast MAC, or the userland destination MAC |
|
| 1494 |
+ // but since the TAP driver is point-to-point, all packets are "for us" |
|
| 1495 |
+ |
|
| 1496 |
+ // IPv6 target address must be ff02::1::ff00:8 (multicast for |
|
| 1497 |
+ // initial NS) or fe80::1 (unicast for recurrent NUD) |
|
| 1498 |
+ if ( memcmp( ipv6->daddr, IPV6_NS_TARGET_MCAST, |
|
| 1499 |
+ sizeof(IPV6ADDR) ) != 0 && |
|
| 1500 |
+ memcmp( ipv6->daddr, IPV6_NS_TARGET_UNICAST, |
|
| 1501 |
+ sizeof(IPV6ADDR) ) != 0 ) |
|
| 1502 |
+ {
|
|
| 1503 |
+ return FALSE; // wrong target address |
|
| 1504 |
+ } |
|
| 1505 |
+ |
|
| 1506 |
+ // IPv6 Next-Header must be ICMPv6 |
|
| 1507 |
+ if ( ipv6->nexthdr != IPPROTO_ICMPV6 ) |
|
| 1508 |
+ {
|
|
| 1509 |
+ return FALSE; // wrong next-header |
|
| 1510 |
+ } |
|
| 1511 |
+ |
|
| 1512 |
+ // ICMPv6 type+code must be 135/0 for NS |
|
| 1513 |
+ if ( icmpv6_ns->type != ICMPV6_TYPE_NS || |
|
| 1514 |
+ icmpv6_ns->code != ICMPV6_CODE_0 ) |
|
| 1515 |
+ {
|
|
| 1516 |
+ return FALSE; // wrong ICMPv6 type |
|
| 1517 |
+ } |
|
| 1518 |
+ |
|
| 1519 |
+ // ICMPv6 target address must be fe80::8 (magic) |
|
| 1520 |
+ if ( memcmp( icmpv6_ns->target_addr, IPV6_NS_TARGET_UNICAST, |
|
| 1521 |
+ sizeof(IPV6ADDR) ) != 0 ) |
|
| 1522 |
+ {
|
|
| 1523 |
+ return FALSE; // not for us |
|
| 1524 |
+ } |
|
| 1525 |
+ |
|
| 1526 |
+ // packet identified, build magic response packet |
|
| 1527 |
+ |
|
| 1528 |
+ na = (ICMPV6_NA_PKT *) MemAlloc (sizeof (ICMPV6_NA_PKT), TRUE); |
|
| 1529 |
+ if ( !na ) return FALSE; |
|
| 1530 |
+ |
|
| 1531 |
+ //------------------------------------------------ |
|
| 1532 |
+ // Initialize Neighbour Advertisement reply packet |
|
| 1533 |
+ //------------------------------------------------ |
|
| 1534 |
+ |
|
| 1535 |
+ // ethernet header |
|
| 1536 |
+ na->eth.proto = htons(ETH_P_IPV6); |
|
| 1537 |
+ COPY_MAC(na->eth.dest, p_Adapter->m_MAC); |
|
| 1538 |
+ COPY_MAC(na->eth.src, p_Adapter->m_TapToUser.dest); |
|
| 1539 |
+ |
|
| 1540 |
+ // IPv6 header |
|
| 1541 |
+ na->ipv6.version_prio = ipv6->version_prio; |
|
| 1542 |
+ NdisMoveMemory( na->ipv6.flow_lbl, ipv6->flow_lbl, |
|
| 1543 |
+ sizeof(na->ipv6.flow_lbl) ); |
|
| 1544 |
+ icmpv6_len = sizeof(ICMPV6_NA_PKT) - sizeof(ETH_HEADER) - sizeof(IPV6HDR); |
|
| 1545 |
+ na->ipv6.payload_len = htons(icmpv6_len); |
|
| 1546 |
+ na->ipv6.nexthdr = IPPROTO_ICMPV6; |
|
| 1547 |
+ na->ipv6.hop_limit = 255; |
|
| 1548 |
+ NdisMoveMemory( na->ipv6.saddr, IPV6_NS_TARGET_UNICAST, |
|
| 1549 |
+ sizeof(IPV6ADDR) ); |
|
| 1550 |
+ NdisMoveMemory( na->ipv6.daddr, ipv6->saddr, |
|
| 1551 |
+ sizeof(IPV6ADDR) ); |
|
| 1552 |
+ |
|
| 1553 |
+ // ICMPv6 |
|
| 1554 |
+ na->icmpv6.type = ICMPV6_TYPE_NA; |
|
| 1555 |
+ na->icmpv6.code = ICMPV6_CODE_0; |
|
| 1556 |
+ na->icmpv6.checksum = 0; |
|
| 1557 |
+ na->icmpv6.rso_bits = 0x60; // Solicited + Override |
|
| 1558 |
+ NdisZeroMemory( na->icmpv6.reserved, sizeof(na->icmpv6.reserved) ); |
|
| 1559 |
+ NdisMoveMemory( na->icmpv6.target_addr, IPV6_NS_TARGET_UNICAST, |
|
| 1560 |
+ sizeof(IPV6ADDR) ); |
|
| 1561 |
+ |
|
| 1562 |
+ // ICMPv6 option "Target Link Layer Address" |
|
| 1563 |
+ na->icmpv6.opt_type = ICMPV6_OPTION_TLLA; |
|
| 1564 |
+ na->icmpv6.opt_length = ICMPV6_LENGTH_TLLA; |
|
| 1565 |
+ COPY_MAC( na->icmpv6.target_macaddr, p_Adapter->m_TapToUser.dest ); |
|
| 1566 |
+ |
|
| 1567 |
+ // calculate and set checksum |
|
| 1568 |
+ icmpv6_csum = icmpv6_checksum ( (UCHAR*) &(na->icmpv6), |
|
| 1569 |
+ icmpv6_len, |
|
| 1570 |
+ na->ipv6.saddr, |
|
| 1571 |
+ na->ipv6.daddr ); |
|
| 1572 |
+ na->icmpv6.checksum = htons( icmpv6_csum ); |
|
| 1573 |
+ |
|
| 1574 |
+ DUMP_PACKET ("HandleIPv6NeighborDiscovery",
|
|
| 1575 |
+ (unsigned char *) na, |
|
| 1576 |
+ sizeof (ICMPV6_NA_PKT)); |
|
| 1577 |
+ |
|
| 1578 |
+ InjectPacketDeferred (p_Adapter, (UCHAR *) na, sizeof (ICMPV6_NA_PKT)); |
|
| 1579 |
+ |
|
| 1580 |
+ MemFree (na, sizeof (ICMPV6_NA_PKT)); |
|
| 1581 |
+ |
|
| 1582 |
+ return TRUE; // all fine |
|
| 1583 |
+} |
|
| 1584 |
+ |
|
| 1433 | 1585 |
//==================================================================== |
| 1434 | 1586 |
// Adapter Transmission |
| 1435 | 1587 |
//==================================================================== |
| ... | ... |
@@ -1566,7 +1718,10 @@ AdapterTransmit (IN NDIS_HANDLE p_AdapterContext, |
| 1566 | 1566 |
|
| 1567 | 1567 |
//=============================================== |
| 1568 | 1568 |
// In Point-To-Point mode, check to see whether |
| 1569 |
- // packet is ARP or IPv4 (if neither, then drop). |
|
| 1569 |
+ // packet is ARP (handled) or IPv4 (sent to app). |
|
| 1570 |
+ // IPv6 packets are inspected for neighbour discovery |
|
| 1571 |
+ // (to be handled locally), and the rest is forwarded |
|
| 1572 |
+ // all other protocols are dropped |
|
| 1570 | 1573 |
//=============================================== |
| 1571 | 1574 |
if (l_Adapter->m_tun) |
| 1572 | 1575 |
{
|
| ... | ... |
@@ -1611,6 +1766,27 @@ AdapterTransmit (IN NDIS_HANDLE p_AdapterContext, |
| 1611 | 1611 |
|
| 1612 | 1612 |
// Packet looks like IPv4, queue it. |
| 1613 | 1613 |
l_PacketBuffer->m_SizeFlags |= TP_TUN; |
| 1614 |
+ |
|
| 1615 |
+ case ETH_P_IPV6: |
|
| 1616 |
+ // make sure that packet is large |
|
| 1617 |
+ // enough to be IPv6 |
|
| 1618 |
+ if (l_PacketLength |
|
| 1619 |
+ < ETHERNET_HEADER_SIZE + IPV6_HEADER_SIZE) |
|
| 1620 |
+ goto no_queue; |
|
| 1621 |
+ |
|
| 1622 |
+ // broadcasts and multicasts are handled specially |
|
| 1623 |
+ // (to be implemented) |
|
| 1624 |
+ |
|
| 1625 |
+ // neighbor discovery packets to fe80::8 are special |
|
| 1626 |
+ // OpenVPN sets this next-hop to signal "handled by tapdrv" |
|
| 1627 |
+ if ( HandleIPv6NeighborDiscovery( l_Adapter, |
|
| 1628 |
+ l_PacketBuffer->m_Data )) |
|
| 1629 |
+ {
|
|
| 1630 |
+ goto no_queue; |
|
| 1631 |
+ } |
|
| 1632 |
+ |
|
| 1633 |
+ // Packet looks like IPv6, queue it :-) |
|
| 1634 |
+ l_PacketBuffer->m_SizeFlags |= TP_TUN; |
|
| 1614 | 1635 |
} |
| 1615 | 1636 |
} |
| 1616 | 1637 |
|
| ... | ... |
@@ -1902,6 +2078,8 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) |
| 1902 | 1902 |
COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC); |
| 1903 | 1903 |
|
| 1904 | 1904 |
l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP); |
| 1905 |
+ l_Adapter->m_UserToTap_IPv6 = l_Adapter->m_UserToTap; |
|
| 1906 |
+ l_Adapter->m_UserToTap_IPv6.proto = htons(ETH_P_IPV6); |
|
| 1905 | 1907 |
|
| 1906 | 1908 |
l_Adapter->m_tun = TRUE; |
| 1907 | 1909 |
|
| ... | ... |
@@ -1939,6 +2117,8 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) |
| 1939 | 1939 |
COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC); |
| 1940 | 1940 |
|
| 1941 | 1941 |
l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP); |
| 1942 |
+ l_Adapter->m_UserToTap_IPv6 = l_Adapter->m_UserToTap; |
|
| 1943 |
+ l_Adapter->m_UserToTap_IPv6.proto = htons(ETH_P_IPV6); |
|
| 1942 | 1944 |
|
| 1943 | 1945 |
l_Adapter->m_tun = TRUE; |
| 1944 | 1946 |
|
| ... | ... |
@@ -2236,10 +2416,18 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) |
| 2236 | 2236 |
{
|
| 2237 | 2237 |
__try |
| 2238 | 2238 |
{
|
| 2239 |
+ ETH_HEADER * p_UserToTap = &l_Adapter->m_UserToTap; |
|
| 2240 |
+ |
|
| 2241 |
+ // for IPv6, need to use ethernet header with IPv6 proto |
|
| 2242 |
+ if ( IPH_GET_VER( ((IPHDR*) p_IRP->AssociatedIrp.SystemBuffer)->version_len) == 6 ) |
|
| 2243 |
+ {
|
|
| 2244 |
+ p_UserToTap = &l_Adapter->m_UserToTap_IPv6; |
|
| 2245 |
+ } |
|
| 2246 |
+ |
|
| 2239 | 2247 |
p_IRP->IoStatus.Information = l_IrpSp->Parameters.Write.Length; |
| 2240 | 2248 |
|
| 2241 | 2249 |
DUMP_PACKET2 ("IRP_MJ_WRITE P2P",
|
| 2242 |
- &l_Adapter->m_UserToTap, |
|
| 2250 |
+ p_UserToTap, |
|
| 2243 | 2251 |
(unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, |
| 2244 | 2252 |
l_IrpSp->Parameters.Write.Length); |
| 2245 | 2253 |
|
| ... | ... |
@@ -2258,8 +2446,8 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) |
| 2258 | 2258 |
NdisMEthIndicateReceive |
| 2259 | 2259 |
(l_Adapter->m_MiniportAdapterHandle, |
| 2260 | 2260 |
(NDIS_HANDLE) l_Adapter, |
| 2261 |
- (unsigned char *) &l_Adapter->m_UserToTap, |
|
| 2262 |
- sizeof (l_Adapter->m_UserToTap), |
|
| 2261 |
+ (unsigned char *) p_UserToTap, |
|
| 2262 |
+ sizeof (ETH_HEADER), |
|
| 2263 | 2263 |
(unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, |
| 2264 | 2264 |
l_IrpSp->Parameters.Write.Length, |
| 2265 | 2265 |
l_IrpSp->Parameters.Write.Length); |
| ... | ... |
@@ -2820,6 +3008,7 @@ VOID ResetTapAdapterState (TapAdapterPointer p_Adapter) |
| 2820 | 2820 |
p_Adapter->m_remoteNetmask = 0; |
| 2821 | 2821 |
NdisZeroMemory (&p_Adapter->m_TapToUser, sizeof (p_Adapter->m_TapToUser)); |
| 2822 | 2822 |
NdisZeroMemory (&p_Adapter->m_UserToTap, sizeof (p_Adapter->m_UserToTap)); |
| 2823 |
+ NdisZeroMemory (&p_Adapter->m_UserToTap_IPv6, sizeof (p_Adapter->m_UserToTap_IPv6)); |
|
| 2823 | 2824 |
|
| 2824 | 2825 |
// DHCP Masq |
| 2825 | 2826 |
p_Adapter->m_dhcp_enabled = FALSE; |
| ... | ... |
@@ -143,6 +143,7 @@ typedef struct _TapAdapter |
| 143 | 143 |
IPADDR m_remoteNetmask; |
| 144 | 144 |
ETH_HEADER m_TapToUser; |
| 145 | 145 |
ETH_HEADER m_UserToTap; |
| 146 |
+ ETH_HEADER m_UserToTap_IPv6; // same as UserToTap but proto=ipv6 |
|
| 146 | 147 |
MACADDR m_MAC_Broadcast; |
| 147 | 148 |
|
| 148 | 149 |
// Used for DHCP server masquerade |