... | ... |
@@ -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 |