|
|
// -*- 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; uint MobileIPv6Mode;
//* 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; 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) { 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; }
|