345 lines
11 KiB
C
345 lines
11 KiB
C
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
|
|
//
|
|
// Copyright (c) 1985-2000 Microsoft Corporation
|
|
//
|
|
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
|
|
// You should have received a copy of the Microsoft End-User License Agreement
|
|
// for this software along with this release; see the file "license.txt".
|
|
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
|
|
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Mobility routines for Internet Protocol Version 6.
|
|
//
|
|
|
|
#include "oscfg.h"
|
|
#include "ndis.h"
|
|
#include "ip6imp.h"
|
|
#include "ip6def.h"
|
|
#include "mobile.h"
|
|
#include "route.h"
|
|
#include "security.h"
|
|
#include "ipsec.h"
|
|
|
|
int MobilitySecurity;
|
|
|
|
|
|
//* IPv6SendBindingAck
|
|
//
|
|
// Sends a Binding Acknowledgement using an explicit routing header.
|
|
//
|
|
void
|
|
IPv6SendBindingAck(
|
|
const IPv6Addr *DestAddr,
|
|
NetTableEntryOrInterface *NTEorIF,
|
|
const IPv6Addr *HomeAddr,
|
|
BindingUpdateDisposition StatusCode,
|
|
ushort SeqNumber, // Network byte order.
|
|
uint Lifetime) // Network byte order, seconds.
|
|
{
|
|
NDIS_STATUS Status;
|
|
PNDIS_PACKET Packet;
|
|
PNDIS_BUFFER Buffer;
|
|
uint Offset, PayloadLength;
|
|
uchar *Mem;
|
|
uint MemLen;
|
|
IPv6Header UNALIGNED *IP;
|
|
MobileAcknowledgementOption UNALIGNED *MA;
|
|
IPv6RoutingHeader UNALIGNED *Routing;
|
|
IP_STATUS IPStatus;
|
|
RouteCacheEntry *RCE;
|
|
|
|
IPStatus = RouteToDestination(DestAddr, 0, NTEorIF,
|
|
RTD_FLAG_NORMAL, &RCE);
|
|
if (IPStatus != IP_SUCCESS) {
|
|
//
|
|
// No route - drop the packet.
|
|
//
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
|
|
"IPv6SendBindingNack - no route: %x\n", IPStatus));
|
|
return;
|
|
}
|
|
|
|
// Determine size of memory buffer needed.
|
|
Offset = RCE->NCE->IF->LinkHeaderSize;
|
|
PayloadLength = sizeof(IPv6RoutingHeader) + sizeof(IPv6Addr) +
|
|
sizeof(MobileAcknowledgementOption);
|
|
MemLen = Offset + sizeof(IPv6Header) + PayloadLength;
|
|
|
|
// Allocate the packet.
|
|
Status = IPv6AllocatePacket(MemLen, &Packet, &Mem);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
|
"IPv6SendBindingNack: Couldn't allocate packet header!?!\n"));
|
|
return;
|
|
}
|
|
|
|
// Prepare IP header of reply packet.
|
|
IP = (IPv6Header UNALIGNED *)(Mem + Offset);
|
|
IP->VersClassFlow = IP_VERSION;
|
|
IP->NextHeader = IP_PROTOCOL_ROUTING;
|
|
IP->HopLimit = (uchar)RCE->NCE->IF->CurHopLimit;
|
|
IP->Dest = *DestAddr;
|
|
IP->Source = (IsNTE(NTEorIF) ? CastToNTE(NTEorIF) : RCE->NTE)->Address;
|
|
|
|
// Prepare the routing header.
|
|
Routing = (IPv6RoutingHeader UNALIGNED *)(IP + 1);
|
|
Routing->NextHeader = IP_PROTOCOL_DEST_OPTS;
|
|
Routing->HeaderExtLength = 2;
|
|
Routing->RoutingType = 0;
|
|
RtlZeroMemory(&Routing->Reserved, sizeof Routing->Reserved);
|
|
Routing->SegmentsLeft = 1;
|
|
RtlCopyMemory(Routing + 1, HomeAddr, sizeof(IPv6Addr));
|
|
|
|
// Prepare the binding acknowledgement option.
|
|
MA = (MobileAcknowledgementOption UNALIGNED *)((uchar *)(Routing + 1) +
|
|
sizeof(IPv6Addr));
|
|
MA->Header.NextHeader = IP_PROTOCOL_NONE;
|
|
MA->Header.HeaderExtLength = 1;
|
|
MA->Pad1 = 0;
|
|
MA->Option.Type = OPT6_BINDING_ACK;
|
|
MA->Option.Length = 11;
|
|
MA->Option.Status = StatusCode;
|
|
MA->Option.SeqNumber = SeqNumber;
|
|
MA->Option.Lifetime = Lifetime;
|
|
MA->Option.Refresh = Lifetime;
|
|
|
|
IPv6Send(Packet, Offset, IP, PayloadLength, RCE,
|
|
SEND_FLAG_BYPASS_BINDING_CACHE, 0, 0, 0);
|
|
|
|
//
|
|
// Release the route.
|
|
//
|
|
ReleaseRCE(RCE);
|
|
}
|
|
|
|
|
|
//* ParseSubOptions - Routine for mobile ip sub-option parsing.
|
|
//
|
|
// Mobile IPv6 destination options may themselves have options, see
|
|
// section 5.5 of the draft. This routine parses these sub-options.
|
|
//
|
|
// We do not return any values to our caller;
|
|
// we merely check that the sub-options are well-formed.
|
|
//
|
|
// Returns TRUE if the sub-options were successfully parsed.
|
|
// Returns FALSE if the packet should be discarded.
|
|
//
|
|
int
|
|
ParseSubOptions(
|
|
uchar *SubOptPtr, // Start of the sub-option data.
|
|
uint SubOptSizeLeft) // Length remaining in the parent option.
|
|
{
|
|
SubOptionHeader UNALIGNED *SubOptHdr;
|
|
uint SubOptLen;
|
|
|
|
while (SubOptSizeLeft != 0) {
|
|
//
|
|
// First we check the option length and ensure that it fits.
|
|
// We move OptPtr past this option while leaving OptHdr
|
|
// for use by the option processing code below.
|
|
//
|
|
|
|
SubOptHdr = (SubOptionHeader UNALIGNED *) SubOptPtr;
|
|
|
|
if ((sizeof *SubOptHdr > SubOptSizeLeft) ||
|
|
((SubOptLen = sizeof *SubOptHdr + SubOptHdr->DataLength) >
|
|
SubOptSizeLeft)) {
|
|
//
|
|
// Bad length. REVIEW: Should we discard the packet or continue
|
|
// processing it? For now, discard it.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
SubOptPtr += SubOptLen;
|
|
SubOptSizeLeft -= SubOptLen;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//* IPv6RecvBindingUpdate - handle an incoming binding update.
|
|
//
|
|
// Process the receipt of a binding update destination option
|
|
// from a mobile node.
|
|
//
|
|
int
|
|
IPv6RecvBindingUpdate(
|
|
IPv6Packet *Packet, // Packet received.
|
|
IPv6BindingUpdateOption UNALIGNED *BindingUpdate)
|
|
{
|
|
const IPv6Addr *CareOfAddr;
|
|
const IPv6Addr *HomeAddr;
|
|
BindingUpdateDisposition Status;
|
|
uint OptBytesLeft;
|
|
|
|
//
|
|
// If a home address option is not also present
|
|
// then we MUST silently drop this packet.
|
|
//
|
|
if ((Packet->Flags & PACKET_SAW_HA_OPT) == 0)
|
|
return 1; // Drop packet.
|
|
|
|
HomeAddr = Packet->SrcAddr;
|
|
|
|
//
|
|
// Check to make sure we have a reasonable home address.
|
|
// Not required by spec but seems like a good idea.
|
|
// Most of what we want to protect against has already been checked
|
|
// by the time we get here, we ASSERT this is checked builds.
|
|
// REVIEW: Final spec may allow/disallow a different set of addresses.
|
|
//
|
|
ASSERT(!IsInvalidSourceAddress(HomeAddr));
|
|
ASSERT(!IsUnspecified(HomeAddr));
|
|
ASSERT(!IsLoopback(HomeAddr));
|
|
if (IsLinkLocal(HomeAddr) ||
|
|
IsSiteLocal(HomeAddr)) {
|
|
|
|
//
|
|
// Since the home address is suspect, do not send binding ack.
|
|
//
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// While the mobility spec requires that packets containing binding
|
|
// update options be authenticated, we currently allow this to be
|
|
// turned off for interoperability testing with mobility implementations
|
|
// that don't support IPSec yet.
|
|
//
|
|
if (MobilitySecurity) {
|
|
//
|
|
// Check if the packet went through some security.
|
|
// If the security check fails we MUST silently drop the packet.
|
|
//
|
|
// REVIEW: This doesn't check that use of this security association
|
|
// REVIEW: actually falls within a security policy.
|
|
//
|
|
if (Packet->SAPerformed == NULL) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR,
|
|
"IPv6RecvBindingUpdate: IPSec required "
|
|
"for binding update\n"));
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
CareOfAddr = AlignAddr(&Packet->IP->Source);
|
|
ASSERT(!IsInvalidSourceAddress(CareOfAddr));
|
|
|
|
//
|
|
// Sub-options may follow the fixed portion of the header.
|
|
//
|
|
OptBytesLeft = sizeof(OptionHeader) + BindingUpdate->Length
|
|
- sizeof(IPv6BindingUpdateOption);
|
|
if (OptBytesLeft != 0) {
|
|
//
|
|
// Sub-options are present. Parse them.
|
|
//
|
|
if (!ParseSubOptions((uchar *) (BindingUpdate + 1), OptBytesLeft)) {
|
|
//
|
|
// Sub-options are malformed. Spec doesn't explicitly say
|
|
// what to do, but the implication is to silently drop.
|
|
//
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check to make sure we have a reasonable care-of address.
|
|
// Not required by spec but seems like a good idea.
|
|
// REVIEW: Aren't link-local addresses o.k. as care-of addresses?
|
|
//
|
|
if (IsUnspecified(CareOfAddr) ||
|
|
IsLoopback(CareOfAddr) ||
|
|
IsLinkLocal(CareOfAddr)) {
|
|
|
|
//
|
|
// Since the care-of address is suspect, do not send binding ack.
|
|
//
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// We don't support home agent functionality (yet).
|
|
// The spec says we SHOULD send a rejecting acknowledgement in this case.
|
|
//
|
|
if (BindingUpdate->Flags & IPV6_BINDING_HOME_REG) {
|
|
IPv6SendBindingAck(CareOfAddr, Packet->NTEorIF, HomeAddr,
|
|
IPV6_BINDING_HOME_REG_NOT_SUPPORTED,
|
|
BindingUpdate->SeqNumber, 0);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Update our binding cache to reflect this binding update.
|
|
//
|
|
Status = CacheBindingUpdate(BindingUpdate, CareOfAddr, Packet->NTEorIF,
|
|
HomeAddr);
|
|
if (Status != IPV6_BINDING_ACCEPTED) {
|
|
//
|
|
// Failed to update our binding cache. If this failure was due to
|
|
// an old sequence number being present in the packet, we MUST
|
|
// silently ignore it. Otherwise we send a rejecting acknowledgement.
|
|
//
|
|
if (Status != IPV6_BINDING_SEQ_NO_TOO_SMALL)
|
|
IPv6SendBindingAck(CareOfAddr, Packet->NTEorIF, HomeAddr,
|
|
Status, BindingUpdate->SeqNumber, 0);
|
|
return 1;
|
|
}
|
|
|
|
if (BindingUpdate->Flags & IPV6_BINDING_ACK) {
|
|
//
|
|
// The mobile node has requested an acknowledgement. In some cases
|
|
// this could be delayed until the next packet is sent
|
|
// to the mobile node, but for now we always send one immediately.
|
|
// We MUST always use a routing header for binding acks.
|
|
// If we deleted a binding, we ack with a zero lifetime.
|
|
//
|
|
IPv6SendBindingAck(CareOfAddr, Packet->NTEorIF, HomeAddr,
|
|
Status, BindingUpdate->SeqNumber,
|
|
(IP6_ADDR_EQUAL(HomeAddr, CareOfAddr) ?
|
|
0 : BindingUpdate->Lifetime));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//* IPv6RecvHomeAddress - handle an incoming home address option.
|
|
//
|
|
// Process the receipt of a Home Address destination option.
|
|
//
|
|
int
|
|
IPv6RecvHomeAddress(
|
|
IPv6Packet *Packet, // Packet received.
|
|
IPv6HomeAddressOption UNALIGNED *HomeAddress)
|
|
{
|
|
IP_STATUS Status;
|
|
uint OptBytesLeft, OptsLen;
|
|
|
|
//
|
|
// If any mobile sub-options exist, then find out which ones.
|
|
// For now we don't do anything with them.
|
|
//
|
|
OptsLen = HomeAddress->Length + sizeof(OptionHeader);
|
|
OptBytesLeft = OptsLen - sizeof(IPv6HomeAddressOption);
|
|
|
|
if (OptBytesLeft != 0) {
|
|
if (!ParseSubOptions((uchar *) HomeAddress + OptsLen - OptBytesLeft,
|
|
OptBytesLeft))
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Save the home address for use by upper layers.
|
|
//
|
|
Packet->SrcAddr = AlignAddr(&HomeAddress->HomeAddress);
|
|
Packet->Flags |= PACKET_SAW_HA_OPT;
|
|
|
|
// Done.
|
|
return 0;
|
|
}
|