Browse code

Implement IPv6 in TUN mode for Windows TAP driver.

* install-win32/settings.in: bump version to 9.7, TAP_RELDATE to "07/03/2010".

* tap-win32/proto.h: add data types and definitions needed for IPv6

* tap-win32/types.h: add m_UserToTap_IPv6 ethernet header for IPv6 packets

* tap-win32/tapdrvr.c: implement support for IPv6 in TUN mode:
- IPv6 packets User->OS need correct ether type
- IPv6 packets OS->User get correctly forwarded
- IPv6 neighbour discovery packets for "fe80::8" (magic address
installed as route-nexthop by OpenVPN.exe) get answered locally
(cherry picked from commit 175e17a5abd5969f6803a9cc9587b7959e1100ae)

Signed-off-by: Gert Doering <gert@greenie.muc.de>
Signed-off-by: David Sommerseth <dazo@users.sourceforge.net>

Gert Doering authored on 2010/03/08 03:28:55
Showing 4 changed files
... ...
@@ -42,8 +42,8 @@
42 42
 !define PRODUCT_TAP_DEVICE_DESCRIPTION  "TAP-Win32 Adapter V9"
43 43
 !define PRODUCT_TAP_PROVIDER            "TAP-Win32 Provider V9"
44 44
 !define PRODUCT_TAP_MAJOR_VER           9
45
-!define PRODUCT_TAP_MINOR_VER           6
46
-!define PRODUCT_TAP_RELDATE             "06/22/2009"
45
+!define PRODUCT_TAP_MINOR_VER           7
46
+!define PRODUCT_TAP_RELDATE             "07/03/2010"
47 47
 
48 48
 # TAP adapter icon -- visible=0x81 or hidden=0x89
49 49
 !define PRODUCT_TAP_CHARACTERISTICS     0x81
... ...
@@ -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()
... ...
@@ -1429,6 +1429,158 @@ NDIS_STATUS AdapterModify
1429 1429
   return l_Status;
1430 1430
 }
1431 1431
 
1432
+// checksum code for ICMPv6 packet, taken from dhcp.c / udp_checksum
1433
+// see RFC 4443, 2.3, and RFC 2460, 8.1
1434
+USHORT
1435
+icmpv6_checksum (const UCHAR *buf,
1436
+	         const int len_icmpv6,
1437
+	         const UCHAR *saddr6,
1438
+	         const UCHAR *daddr6)
1439
+{
1440
+  USHORT word16;
1441
+  ULONG sum = 0;
1442
+  int i;
1443
+
1444
+  // make 16 bit words out of every two adjacent 8 bit words and
1445
+  // calculate the sum of all 16 bit words
1446
+  for (i = 0; i < len_icmpv6; i += 2){
1447
+    word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_icmpv6) ? (buf[i+1] & 0xFF) : 0);
1448
+    sum += word16;
1449
+  }
1450
+
1451
+  // add the IPv6 pseudo header which contains the IP source and destination addresses
1452
+  for (i = 0; i < 16; i += 2){
1453
+    word16 =((saddr6[i] << 8) & 0xFF00) + (saddr6[i+1] & 0xFF);
1454
+    sum += word16;
1455
+  }
1456
+  for (i = 0; i < 16; i += 2){
1457
+    word16 =((daddr6[i] << 8) & 0xFF00) + (daddr6[i+1] & 0xFF);
1458
+    sum += word16;
1459
+  }
1460
+
1461
+  // the next-header number and the length of the ICMPv6 packet
1462
+  sum += (USHORT) IPPROTO_ICMPV6 + (USHORT) len_icmpv6;
1463
+
1464
+  // keep only the last 16 bits of the 32 bit calculated sum and add the carries
1465
+  while (sum >> 16)
1466
+    sum = (sum & 0xFFFF) + (sum >> 16);
1467
+
1468
+  // Take the one's complement of sum
1469
+  return ((USHORT) ~sum);
1470
+}
1471
+
1472
+// check IPv6 packet for "is this an IPv6 Neighbor Solicitation that
1473
+// the tap driver needs to answer?"
1474
+// see RFC 4861 4.3 for the different cases
1475
+static IPV6ADDR IPV6_NS_TARGET_MCAST =
1476
+	{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1477
+          0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x08 };
1478
+static IPV6ADDR IPV6_NS_TARGET_UNICAST =
1479
+	{ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1480
+          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 };
1481
+
1482
+BOOLEAN
1483
+HandleIPv6NeighborDiscovery( TapAdapterPointer p_Adapter, UCHAR * m_Data )
1484
+{
1485
+    const ETH_HEADER * e = (ETH_HEADER *) m_Data;
1486
+    const IPV6HDR *ipv6 = (IPV6HDR *) (m_Data + sizeof (ETH_HEADER));
1487
+    const ICMPV6_NS * icmpv6_ns = (ICMPV6_NS *) (m_Data + sizeof (ETH_HEADER) + sizeof (IPV6HDR));
1488
+    ICMPV6_NA_PKT *na;
1489
+    USHORT icmpv6_len, icmpv6_csum;
1490
+
1491
+    // we don't really care about the destination MAC address here
1492
+    // - it's either a multicast MAC, or the userland destination MAC
1493
+    // but since the TAP driver is point-to-point, all packets are "for us"
1494
+
1495
+    // IPv6 target address must be ff02::1::ff00:8 (multicast for
1496
+    // initial NS) or fe80::1 (unicast for recurrent NUD)
1497
+    if ( memcmp( ipv6->daddr, IPV6_NS_TARGET_MCAST,
1498
+		 sizeof(IPV6ADDR) ) != 0 &&
1499
+         memcmp( ipv6->daddr, IPV6_NS_TARGET_UNICAST,
1500
+		 sizeof(IPV6ADDR) ) != 0 )
1501
+    {
1502
+	return FALSE;				// wrong target address
1503
+    }
1504
+
1505
+    // IPv6 Next-Header must be ICMPv6
1506
+    if ( ipv6->nexthdr != IPPROTO_ICMPV6 )
1507
+    {
1508
+	return FALSE;				// wrong next-header
1509
+    }
1510
+
1511
+    // ICMPv6 type+code must be 135/0 for NS
1512
+    if ( icmpv6_ns->type != ICMPV6_TYPE_NS ||
1513
+	 icmpv6_ns->code != ICMPV6_CODE_0 )
1514
+    {
1515
+	return FALSE;				// wrong ICMPv6 type
1516
+    }
1517
+
1518
+    // ICMPv6 target address must be fe80::8 (magic)
1519
+    if ( memcmp( icmpv6_ns->target_addr, IPV6_NS_TARGET_UNICAST,
1520
+	         sizeof(IPV6ADDR) ) != 0 )
1521
+    {
1522
+	return FALSE;				// not for us
1523
+    }
1524
+
1525
+    // packet identified, build magic response packet
1526
+
1527
+    na = (ICMPV6_NA_PKT *) MemAlloc (sizeof (ICMPV6_NA_PKT), TRUE);
1528
+    if ( !na ) return FALSE;
1529
+
1530
+    //------------------------------------------------
1531
+    // Initialize Neighbour Advertisement reply packet
1532
+    //------------------------------------------------
1533
+
1534
+    // ethernet header
1535
+    na->eth.proto = htons(ETH_P_IPV6);
1536
+    COPY_MAC(na->eth.dest, p_Adapter->m_MAC);
1537
+    COPY_MAC(na->eth.src, p_Adapter->m_TapToUser.dest);
1538
+
1539
+    // IPv6 header
1540
+    na->ipv6.version_prio = ipv6->version_prio;
1541
+    NdisMoveMemory( na->ipv6.flow_lbl, ipv6->flow_lbl,
1542
+		    sizeof(na->ipv6.flow_lbl) );
1543
+    icmpv6_len = sizeof(ICMPV6_NA_PKT) - sizeof(ETH_HEADER) - sizeof(IPV6HDR);
1544
+    na->ipv6.payload_len = htons(icmpv6_len);
1545
+    na->ipv6.nexthdr = IPPROTO_ICMPV6;
1546
+    na->ipv6.hop_limit = 255;
1547
+    NdisMoveMemory( na->ipv6.saddr, IPV6_NS_TARGET_UNICAST,
1548
+		    sizeof(IPV6ADDR) );
1549
+    NdisMoveMemory( na->ipv6.daddr, ipv6->saddr,
1550
+		    sizeof(IPV6ADDR) );
1551
+
1552
+    // ICMPv6
1553
+    na->icmpv6.type = ICMPV6_TYPE_NA;
1554
+    na->icmpv6.code = ICMPV6_CODE_0;
1555
+    na->icmpv6.checksum = 0;
1556
+    na->icmpv6.rso_bits = 0x60;		// Solicited + Override
1557
+    NdisZeroMemory( na->icmpv6.reserved, sizeof(na->icmpv6.reserved) );
1558
+    NdisMoveMemory( na->icmpv6.target_addr, IPV6_NS_TARGET_UNICAST,
1559
+		    sizeof(IPV6ADDR) );
1560
+
1561
+    // ICMPv6 option "Target Link Layer Address"
1562
+    na->icmpv6.opt_type = ICMPV6_OPTION_TLLA;
1563
+    na->icmpv6.opt_length = ICMPV6_LENGTH_TLLA;
1564
+    COPY_MAC( na->icmpv6.target_macaddr, p_Adapter->m_TapToUser.dest );
1565
+
1566
+    // calculate and set checksum
1567
+    icmpv6_csum = icmpv6_checksum ( (UCHAR*) &(na->icmpv6),
1568
+				    icmpv6_len,
1569
+				    na->ipv6.saddr,
1570
+				    na->ipv6.daddr );
1571
+    na->icmpv6.checksum = htons( icmpv6_csum );
1572
+
1573
+    DUMP_PACKET ("HandleIPv6NeighborDiscovery",
1574
+		 (unsigned char *) na,
1575
+		 sizeof (ICMPV6_NA_PKT));
1576
+
1577
+    InjectPacketDeferred (p_Adapter, (UCHAR *) na, sizeof (ICMPV6_NA_PKT));
1578
+
1579
+    MemFree (na, sizeof (ICMPV6_NA_PKT));
1580
+
1581
+    return TRUE;				// all fine
1582
+}
1583
+
1432 1584
 //====================================================================
1433 1585
 //                               Adapter Transmission
1434 1586
 //====================================================================
... ...
@@ -1565,7 +1717,10 @@ AdapterTransmit (IN NDIS_HANDLE p_AdapterContext,
1565 1565
 
1566 1566
     //===============================================
1567 1567
     // In Point-To-Point mode, check to see whether
1568
-    // packet is ARP or IPv4 (if neither, then drop).
1568
+    // packet is ARP (handled) or IPv4 (sent to app).
1569
+    // IPv6 packets are inspected for neighbour discovery
1570
+    // (to be handled locally), and the rest is forwarded
1571
+    // all other protocols are dropped
1569 1572
     //===============================================
1570 1573
     if (l_Adapter->m_tun)
1571 1574
       {
... ...
@@ -1610,6 +1765,27 @@ AdapterTransmit (IN NDIS_HANDLE p_AdapterContext,
1610 1610
 
1611 1611
 	    // Packet looks like IPv4, queue it.
1612 1612
 	    l_PacketBuffer->m_SizeFlags |= TP_TUN;
1613
+
1614
+	  case ETH_P_IPV6:
1615
+	    // make sure that packet is large
1616
+	    // enough to be IPv6
1617
+	    if (l_PacketLength
1618
+		< ETHERNET_HEADER_SIZE + IPV6_HEADER_SIZE)
1619
+	      goto no_queue;
1620
+
1621
+	    // broadcasts and multicasts are handled specially
1622
+	    // (to be implemented)
1623
+
1624
+	    // neighbor discovery packets to fe80::8 are special
1625
+	    // OpenVPN sets this next-hop to signal "handled by tapdrv"
1626
+	    if ( HandleIPv6NeighborDiscovery( l_Adapter,
1627
+					      l_PacketBuffer->m_Data ))
1628
+	      {
1629
+		goto no_queue;
1630
+	      }
1631
+
1632
+	    // Packet looks like IPv6, queue it :-)
1633
+	    l_PacketBuffer->m_SizeFlags |= TP_TUN;
1613 1634
 	  }
1614 1635
       }
1615 1636
 
... ...
@@ -1901,6 +2077,8 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP)
1901 1901
 		  COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC);
1902 1902
 
1903 1903
 		  l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP);
1904
+		  l_Adapter->m_UserToTap_IPv6 = l_Adapter->m_UserToTap;
1905
+		  l_Adapter->m_UserToTap_IPv6.proto = htons(ETH_P_IPV6);
1904 1906
 
1905 1907
 		  l_Adapter->m_tun = TRUE;
1906 1908
 
... ...
@@ -1938,6 +2116,8 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP)
1938 1938
 		  COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC);
1939 1939
 
1940 1940
 		  l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP);
1941
+		  l_Adapter->m_UserToTap_IPv6 = l_Adapter->m_UserToTap;
1942
+		  l_Adapter->m_UserToTap_IPv6.proto = htons(ETH_P_IPV6);
1941 1943
 
1942 1944
 		  l_Adapter->m_tun = TRUE;
1943 1945
 
... ...
@@ -2235,10 +2415,18 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP)
2235 2235
 	  {
2236 2236
 	    __try
2237 2237
 	      {
2238
+		ETH_HEADER * p_UserToTap = &l_Adapter->m_UserToTap;
2239
+
2240
+		// for IPv6, need to use ethernet header with IPv6 proto
2241
+		if ( IPH_GET_VER( ((IPHDR*) p_IRP->AssociatedIrp.SystemBuffer)->version_len) == 6 )
2242
+		  {
2243
+		    p_UserToTap = &l_Adapter->m_UserToTap_IPv6;
2244
+		  }
2245
+
2238 2246
 		p_IRP->IoStatus.Information = l_IrpSp->Parameters.Write.Length;
2239 2247
 
2240 2248
 		DUMP_PACKET2 ("IRP_MJ_WRITE P2P",
2241
-			      &l_Adapter->m_UserToTap,
2249
+			      p_UserToTap,
2242 2250
 			      (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
2243 2251
 			      l_IrpSp->Parameters.Write.Length);
2244 2252
 
... ...
@@ -2257,8 +2445,8 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP)
2257 2257
 		NdisMEthIndicateReceive
2258 2258
 		  (l_Adapter->m_MiniportAdapterHandle,
2259 2259
 		   (NDIS_HANDLE) l_Adapter,
2260
-		   (unsigned char *) &l_Adapter->m_UserToTap,
2261
-		   sizeof (l_Adapter->m_UserToTap),
2260
+		   (unsigned char *) p_UserToTap,
2261
+		   sizeof (ETH_HEADER),
2262 2262
 		   (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
2263 2263
 		   l_IrpSp->Parameters.Write.Length,
2264 2264
 		   l_IrpSp->Parameters.Write.Length);
... ...
@@ -2819,6 +3007,7 @@ VOID ResetTapAdapterState (TapAdapterPointer p_Adapter)
2819 2819
   p_Adapter->m_remoteNetmask = 0;
2820 2820
   NdisZeroMemory (&p_Adapter->m_TapToUser, sizeof (p_Adapter->m_TapToUser));
2821 2821
   NdisZeroMemory (&p_Adapter->m_UserToTap, sizeof (p_Adapter->m_UserToTap));
2822
+  NdisZeroMemory (&p_Adapter->m_UserToTap_IPv6, sizeof (p_Adapter->m_UserToTap_IPv6));
2822 2823
 
2823 2824
   // DHCP Masq
2824 2825
   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