📄 ipv6.c
字号:
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
//
// (c) Microsoft Corporation. All rights reserved.
//
// This file is part of the Microsoft Virtual Ring Routing distribution.
// You should have received a copy of the Microsoft Research Shared Source
// license agreement (MSR-SSLA) for this software; see the file "license.txt".
// If not, please see http://research.microsoft.com/vrr/license.htm,
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
//
//
// This file contains procedures for supporting IPv6 over VRR.
//
#include "headers.h"
#include <packon.h>
extern void
MiniportTransmitPacket(
MiniportAdapter *VA,
NDIS_PACKET *Packet);
extern void
MiniportFreePacket(
ProtocolAdapter *PA,
NDIS_PACKET *Packet);
extern void
MiniportReceiveLocally(
MiniportAdapter *VA,
SRPacket *srp,
void (*Complete)(MiniportAdapter *VA, SRPacket *srp, NDIS_STATUS Status));
extern void
MiniportReceiveComplete(
MiniportAdapter *VA,
SRPacket *srp,
NDIS_STATUS Status);
// Forward declaration.
boolint
IPv6FakeDHTtxNA(
MiniportAdapter *VA,
IPv6Addr SolicitSource);
//
// Magic addresses used to support DHT functionality on VRR.
// These addresses are real addresses taken from host msrc-gregos05.
// Therefore nobody else should ever legitimately conflict with them.
//
IPv6Addr UnspecifiedAddr = { 0 };
IPv6Addr DHTtxAddr = {0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x0d, 0x56, 0xff, 0xfe, 0x6d, 0xf0, 0x2c};
IPv6Addr DHTrxAddr = {0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x04, 0x23, 0xff, 0xfe, 0xa3, 0x21, 0x13};
PhysicalAddress DHTMagicL2 = {0x00, 0x0d, 0x56, 0x6d, 0xf0, 0x2c};
#define ETYPE_IPv6 0x86dd
// gregos: these so we can snip types from ipv6 header files.
typedef unsigned char u_char;
typedef unsigned short u_short;
typedef unsigned int u_int;
typedef unsigned long u_long;
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef struct in6_addr in6_addr;
//* EthAddrIsIPv6ND
__inline boolint
EthAddrIsIPv6ND(const PhysicalAddress A)
{
//
// The MS IPv6 stacks emit ND frames with 802.3 prefix 0x3333ff/24.
// The low order 32-bits are treated by eth as a multicast groups address.
//
return ((A[0] == 0x33) && (A[1] == 0x33) && (A[2] == 0xff));
}
__inline boolint
IPv6AddrIsLinkLocal(in6_addr *IPv6Addr)
{
uchar *A = (uchar *)IPv6Addr;
return ((A[0] == 0xfe) && ((A[1] & 0xc0) == 0x80));
}
//
// For comparing IPv6 addresseses.
//
__inline int
IP6_ADDR_EQUAL(const IPv6Addr *x, const IPv6Addr *y)
{
__int64 UNALIGNED *a;
__int64 UNALIGNED *b;
a = (__int64 UNALIGNED *)x;
b = (__int64 UNALIGNED *)y;
return (a[1] == b[1]) && (a[0] == b[0]);
}
//
// Following taken from msripv6\inc\ip6.h
// IPv6 Header Format.
// See RFC 1883, page 5 (and subsequent draft updates to same).
//
typedef struct IPv6Header {
ulong VersClassFlow; // 4 bits Version, 8 Traffic Class, 20 Flow Label.
ushort PayloadLength; // Zero indicates Jumbo Payload hop-by-hop option.
uchar NextHeader; // Values are superset of IPv4's Protocol field.
uchar HopLimit;
struct in6_addr Source;
struct in6_addr Dest;
} IPv6Header;
//
// Useful constants for working with various fields in the IPv6 header.
//
// NOTE: We keep the Version, Traffic Class and Flow Label fields as a single
// NOTE: 32 bit value (VersClassFlow) in network byte order (big-endian).
// NOTE: Since NT is little-endian, this means all loads/stores to/from this
// NOTE: field need to be byte swapped.
//
#define IP_VER_MASK 0x000000F0 // Version is high 4 bits of VersClassFlow.
#define IP_VERSION6 0x00000060 // This is 6 << 28 (after byte swap).
#define IP_PROTOCOL_ICMPv6 58 // IPv6 Internet Control Message Protocol.
//
// Following taken from msripv6\inc\icmp.h
// ICMPv6 Header.
// The actual message body follows this header and is type-specific.
//
typedef struct ICMPv6Header {
uchar Type; // Type of message (high bit zero for error messages).
uchar Code; // Type-specific differentiater.
ushort Checksum; // Calculated over ICMPv6 message and IPv6 psuedo-header.
} ICMPv6Header;
#define ICMPv6_NEIGHBOR_SOLICIT 135
#define ICMPv6_NEIGHBOR_ADVERT 136
//
// IPv6 ND headers.
//
typedef struct IPv6NS {
ICMPv6Header ICMPHeader;
unsigned int reserved;
in6_addr TargetAddress;
} IPv6NS;
typedef struct IPv6NA {
ICMPv6Header ICMPHeader;
unsigned int Flags;
in6_addr TargetAddress;
uchar TLLAType;
uchar TLLALength;
PhysicalAddress TLLAddress;
} IPv6NA;
#define ND_NA_FLAG_SOLICITED 0x40000000
#define ND_NA_FLAG_OVERRIDE 0x20000000
//
// Following struct taken from msripv6\tcpip6\udp\udp.h
// Structure of a UDP header.
//
#define IP_PROTOCOL_UDP 17
typedef struct UDPHeader {
ushort Source; // Source port.
ushort Dest; // Destination port.
ushort Length; // Length.
ushort Checksum; // Checksum.
} UDPHeader;
//* DHTTrapPacketMaxSize
//
// Returns max payload size supported via DHT_TRAP_PACKET API.
//
ULONG
DHTTrapPacketMaxSize()
{
return PROTOCOL_MIN_FRAME_SIZE
- sizeof(EtherHeader)
- sizeof(IPv6Header)
- sizeof(UDPHeader)
- sizeof(DHTHeader);
}
//* IPv6AddrToVirtualAddr
//
// Extracts MAC address bits from an IPv6 address
// into the correspondng VRR Address.
//
IPv6AddrToVirtualAddr(
in6_addr *IPv6Addr,
VirtualAddress VRRAddr)
{
uchar *From = (uchar *)IPv6Addr;
uchar *To = (uchar *)VRRAddr;
To[0] = From[8];
To[0] &= 0xfd;
To[1] = From[9];
To[2] = From[10];
To[3] = From[13];
To[4] = From[14];
To[5] = From[15];
}
//* AllocFlatBuffer
//
// Helper for IPv6 optimizations that facilitates parsing of headers.
//
// Allocates and returns a flat buffer containing leading n bytes from
// an NDIS packet supplied by the caller.
//
// The caller is responsible for releasing the memory returned.
//
char *
AllocFlatBuffer(
NDIS_PACKET *Packet,
uint BytesRequired)
{
uint PacketLength;
NDIS_BUFFER *Buffer;
uint BufferLength;
uint FlatBytes = 0;
uchar *FlatBuffer;
uchar *Flat;
if (BytesRequired == 0)
return NULL;
NdisQueryPacketLength(Packet, &PacketLength);
if (PacketLength < BytesRequired)
return NULL;
FlatBuffer=ExAllocatePool(NonPagedPool, BytesRequired);
if (FlatBuffer == NULL)
return NULL;
Flat = FlatBuffer;
Buffer = NdisFirstBuffer(Packet);
while (FlatBytes < BytesRequired) {
uchar *Data;
uint Transfer;
NdisQueryBuffer(Buffer, &Data, &BufferLength);
Transfer = min(BufferLength, BytesRequired - FlatBytes);
RtlCopyMemory(Flat, Data, Transfer);
FlatBytes += Transfer;
Flat += Transfer;
NdisGetNextBuffer(Buffer, &Buffer);
}
return FlatBuffer;
}
//* Cksum
//
// Helper for ChecksumPacket. See RFC793.
//
unsigned short
Cksum(
unsigned char *c,
unsigned int Length)
{
unsigned int cks = 0;
unsigned short *ps = (unsigned short *)c;
unsigned short *pSentinel = ps + (Length >> 1);
while (ps < pSentinel)
cks += *ps++; // Next ushort.
if (Length & 1)
cks += *(c + Length - 1); // Final char.
cks = (cks >> 16) + (cks & 0xFFFF);
cks = (cks >> 16) + (cks & 0xFFFF);
return (unsigned short)cks;
}
//* IPv6TrapOutboundNS
//
// We trap outbound IPv6 Neighbor Solicitations, thus:
// - Duplicate Addresses detection packets are dropped;
// - solicitations other than link-local drop through;
// - link-local solicitations are handled as follows.
//
// Link-local solicitations are handled by rewriting and
// redirecting them towards the VRR node with the closest
// address to the address from which the Interface Id of
// the target address in the solicitation was derived.
// This allows applications to use VRR to perform DHT-type
// operations of routing to the node with the closest key
// to some given key where the closest key is a VRR address
// and the given key is encoded in the interface Id of the
// IPv6 target address.
//
// Returns true if an IPv6 Neighbor Solicitation was found
// and handled, otherwise returns false.
//
boolint
IPv6TrapOutboundNS(
MiniportAdapter *VA,
NDIS_PACKET *Packet,
EtherHeader *eth)
{
void *Address;
uint BufferLength;
uint PacketLength;
NDIS_BUFFER *Buffer;
const uint MinBuffSize = sizeof(EtherHeader)
+ sizeof(IPv6Header)
+ sizeof(IPv6NS);
IPv6Header UNALIGNED *IPv6;
ICMPv6Header UNALIGNED *ICMPv6;
IPv6NS UNALIGNED *NS;
uchar *Tgt;
VirtualAddress VAddr;
//
// We are only interested in IPv6 packets that look
// like they are destined to eth multicast address.
//
if (RtlUshortByteSwap(eth->Type) != ETYPE_IPv6)
return FALSE;
if (EthAddrIsIPv6ND(eth->Dest) == FALSE &&
RtlEqualMemory(eth->Dest,DHTMagicL2,sizeof(PhysicalAddress)) == FALSE)
return FALSE;
//
// Ethernet header looks like that of an IPv6 Neighbor Solicitation.
// Check that packet is large enough to be an NS.
//
Buffer = NdisFirstBuffer(Packet);
NdisQueryBuffer(Buffer, &Address, &BufferLength);
NdisQueryPacketLength(Packet, &PacketLength);
if (BufferLength < MinBuffSize)
return FALSE;
//
// The packet is big enough to be an IPv6 Neighbor
// solicitation. Now check the IPv6 and ICMPv6 headers.
//
IPv6 = (IPv6Header UNALIGNED *)(eth + 1);
ICMPv6 = (ICMPv6Header UNALIGNED *)(IPv6 + 1);
NS = (IPv6NS UNALIGNED *)ICMPv6;
Tgt = (uchar *)&NS->TargetAddress;
if ((IPv6->VersClassFlow & IP_VERSION6) != IP_VERSION6 ||
ICMPv6->Type != ICMPv6_NEIGHBOR_SOLICIT ||
ICMPv6->Code != 0) {
KdPrint(("VrrKdPrint: (%u) IPv6OutNS: not ICMPv6 NS, ignored\n", TIMESTAMP));
return FALSE;
}
//
// This is indeed an IPv6 Neighbor Solicitation.
//
if (IP6_ADDR_EQUAL(&NS->TargetAddress,&DHTrxAddr)) {
//
// Drop NS to DHT rx address. This prevents DAD from detecting
// duplicate instances of this special address in VRR rings.
//
return TRUE;
}
else if (IP6_ADDR_EQUAL(&NS->TargetAddress,&DHTtxAddr)) {
//
// Trap NS for the DHT tx address and send a fake NA
// up to our IPv6 protocol stack. This quietens DAD
// for the DHT tx address and associates it with
// DHTMagicL2 in the local IPv6 Neighbor Cache.
//
IPv6FakeDHTtxNA(VA, IPv6->Source);
return TRUE;
}
else if (IPv6AddrIsLinkLocal(&NS->TargetAddress)) {
//
// Determine the VRR address from which the interface
// id of the target IPv6 address would have been
// derived by IPv6 stateless address autoconfiguration.
//
IPv6AddrToVirtualAddr(&NS->TargetAddress,VAddr);
//
// What we do next has implications for IPv6 Duplicate
// Address Detection or IPv6 Neighbor Discovery.
//
if (VirtualAddressEqual(VA->Address,VAddr)) {
//
// NS to self is dropped, therefore IPv6 depends upon
// VRR to keep duplicate addresses out of the network.
//
KdPrint(("VrrKdPrint: (%u) IPv6OutNS: target=self, drop packet, note DAD skipped.)\n",TIMESTAMP));
}
else {
//
// Otherwise we route IPv6 Neighbor Solicitations towards
// the VRR node with the closest matching VRR address.
//
RtlCopyMemory(eth->Dest,VAddr,sizeof(VirtualAddress));
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -