3223 lines
148 KiB
C
3223 lines
148 KiB
C
|
|
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipmip6ha_mh.c,v $ $Revision: 1.45 $
|
|
* $Source: /home/interpeak/CVSRoot/ipmip6ha/src/ipmip6ha_mh.c,v $
|
|
* $Author: jhorteli $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Joel Hortelius <joel.hortelis@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
#include "ipmip6ha_config.h"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_os.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_sock6.h>
|
|
#include <ipcom_inet.h>
|
|
#include <ipcom_list.h>
|
|
#include <ipcom_syslog.h>
|
|
|
|
#include <ipmip.h>
|
|
#include <private/ipmip_buffer.h>
|
|
|
|
#include <ipmip6.h>
|
|
#include <private/ipmip6_mh.h>
|
|
#include <private/ipmip6_mh_pkt.h>
|
|
#include <private/ipmip6_context.h>
|
|
#include <private/ipmip6_ntop.h>
|
|
#include <private/ipmip6_util.h>
|
|
#include <private/ipmip6_if.h>
|
|
|
|
#include "ipmip6ha_h.h"
|
|
#include "ipmip6ha_pktbuf.h"
|
|
#include "ipmip6ha_mh.h"
|
|
#include "ipmip6ha_mag.h"
|
|
#include "ipmip6ha_if.h"
|
|
#include "ipmip6ha_node.h"
|
|
#include "ipmip6ha_route.h"
|
|
#include "ipmip6ha_nemo.h"
|
|
#include "ipmip6ha_nemo_dsmipv6.h"
|
|
#include "ipmip6ha_attr_h.h"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
IP_STATIC int ipmip6ha_mh_unknown_handoff_completion(Ipmip6ha_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6ha_node_t *node,
|
|
Ipmip6ha_br_t *bri,
|
|
Ip_bool timeout,
|
|
Ip_u8 status);
|
|
IP_STATIC int ipmip6ha_mh_process_binding_update_P_delayed(int event,
|
|
Ipmip6ha_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6ha_pktbuf_t *pbuf,
|
|
Ipmip6ha_node_P_t *cnode);
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_STATIC void
|
|
ipmip6ha_mh_transport_type(Ipmip6ha_node_t *node,
|
|
Ipmip6ha_pktbuf_t *pbuf)
|
|
{
|
|
if (IPMIP6_MH_BU_IS_P(node->regflags))
|
|
{
|
|
#ifdef IPMIP_USE_INET
|
|
if (pbuf->pktinfo.mip6BindingCOAType == IPMIP6_MIB_InetAddressType_ipv4)
|
|
{
|
|
if (pbuf->iff_transport.priv->ifenabled_P[IPMIP_TUNNELTYPE_GRE] == IP_FALSE && pbuf->iff_transport.priv->ifenabled_P[IPMIP_TUNNELTYPE_IPIP] == IP_FALSE)
|
|
{
|
|
/* IMPOSSIBLE COMBINATION */
|
|
ipmip_pktbuf_error(&pbuf->ipmip6.ipmip, 0,
|
|
"Neither GRE nor IPIP are enabled in the v4 CoA domain");
|
|
return;
|
|
}
|
|
|
|
if (pbuf->ipmip6.mo.gre_key == IP_NULL)
|
|
{
|
|
if (pbuf->iff_transport.priv->ifenabled_P[IPMIP_TUNNELTYPE_IPIP] == IP_FALSE)
|
|
{
|
|
/* Require GRE key */
|
|
ipmip_pktbuf_error(&pbuf->ipmip6.ipmip, IPMIP6_MH_BA_STATUS_ERROR_GRE_KEY_OPTION_REQUIRED,
|
|
"GRE required, since IPIP is disabled by configuration in the v4 CoA domain");
|
|
return;
|
|
}
|
|
|
|
/* Kill UDP TLV Encap */
|
|
if (IP_BIT_ISSET(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_T) && pbuf->iff_transport.priv->AcceptUDPTLVEncapsulation == IP_FALSE)
|
|
pbuf->pktinfo.haMobilityBindingFlags = (Ip_u16) (pbuf->pktinfo.haMobilityBindingFlags & ~IPMIP6_MH_BU_T);
|
|
|
|
pbuf->pktinfo.haMobilityBindingTunnelingType = IPMIP_TUNNELTYPE_IPIP;
|
|
}
|
|
else
|
|
{
|
|
/* GRE's not enabled? */
|
|
if (pbuf->iff_transport.priv->ifenabled_P[IPMIP_TUNNELTYPE_GRE] == IP_FALSE)
|
|
{
|
|
/* */
|
|
pbuf->pktinfo.haMobilityBindingSuccessCode = IPMIP6_MH_BA_STATUS_SUCCESS_GRE_KEY_OPTION_NOT_REQUIRED;
|
|
pbuf->pktinfo.haMobilityBindingTunnelingType = IPMIP_TUNNELTYPE_IPIP;
|
|
pbuf->ipmip6.mo.gre_key = IP_NULL;
|
|
}
|
|
else if (pbuf->pktinfo.mip6BindingUDPEncapsulate)
|
|
{
|
|
if (IP_BIT_ISFALSE(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_T) || pbuf->iff_transport.priv->AcceptUDPTLVEncapsulation == IP_FALSE)
|
|
{
|
|
/* Now what */
|
|
pbuf->pktinfo.haMobilityBindingSuccessCode = IPMIP6_MH_BA_STATUS_SUCCESS_GRE_TUNNELING_BUT_TLV_NOT_SUPPORTED;
|
|
pbuf->pktinfo.mip6BindingUDPEncapsulate = IP_FALSE;
|
|
}
|
|
|
|
/**/
|
|
pbuf->pktinfo.haMobilityBindingTunnelingType = IPMIP_TUNNELTYPE_GRE;
|
|
}
|
|
else
|
|
{
|
|
/**/
|
|
pbuf->pktinfo.haMobilityBindingTunnelingType = IPMIP_TUNNELTYPE_GRE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (pbuf->iff_transport.priv->ifenabled_P[IPMIP_TUNNELTYPE_GRE6] == IP_FALSE && pbuf->iff_transport.priv->ifenabled_P[IPMIP_TUNNELTYPE_IP6IP6] == IP_FALSE)
|
|
{
|
|
/* IMPOSSIBLE COMBINATION */
|
|
ipmip_pktbuf_error(&pbuf->ipmip6.ipmip, 0,
|
|
"Neither GRE6 nor IP6IP6 are enabled in the v6 CoA domain");
|
|
return;
|
|
}
|
|
|
|
if (pbuf->ipmip6.mo.gre_key == IP_NULL)
|
|
{
|
|
if (pbuf->iff_transport.priv->ifenabled_P[IPMIP_TUNNELTYPE_IP6IP6] == IP_FALSE)
|
|
{
|
|
/* Require GRE key */
|
|
ipmip_pktbuf_error(&pbuf->ipmip6.ipmip, IPMIP6_MH_BA_STATUS_ERROR_GRE_KEY_OPTION_REQUIRED,
|
|
"GRE6 required, since IP6IP6 is disabled by configuration in the v6 CoA domain");
|
|
return;
|
|
}
|
|
|
|
/* Kill UDP TLV Encap */
|
|
if (IP_BIT_ISSET(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_T) && pbuf->iff_transport.priv->AcceptUDPTLVEncapsulation == IP_FALSE)
|
|
pbuf->pktinfo.haMobilityBindingFlags = (Ip_u16)(pbuf->pktinfo.haMobilityBindingFlags & ~IPMIP6_MH_BU_T);
|
|
|
|
pbuf->pktinfo.haMobilityBindingTunnelingType = IPMIP_TUNNELTYPE_IP6IP6;
|
|
}
|
|
else
|
|
{
|
|
/* GRE's not enabled? */
|
|
if (pbuf->iff_transport.priv->ifenabled_P[IPMIP_TUNNELTYPE_GRE6] == IP_FALSE)
|
|
{
|
|
/* */
|
|
pbuf->pktinfo.haMobilityBindingSuccessCode = IPMIP6_MH_BA_STATUS_SUCCESS_GRE_KEY_OPTION_NOT_REQUIRED;
|
|
pbuf->pktinfo.haMobilityBindingTunnelingType = IPMIP_TUNNELTYPE_IP6IP6;
|
|
pbuf->ipmip6.mo.gre_key = IP_NULL;
|
|
}
|
|
else if (pbuf->pktinfo.mip6BindingUDPEncapsulate)
|
|
{
|
|
if (IP_BIT_ISFALSE(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_T) || pbuf->iff_transport.priv->AcceptUDPTLVEncapsulation == IP_FALSE)
|
|
{
|
|
/* Now what */
|
|
pbuf->pktinfo.haMobilityBindingSuccessCode = IPMIP6_MH_BA_STATUS_SUCCESS_GRE_TUNNELING_BUT_TLV_NOT_SUPPORTED;
|
|
pbuf->pktinfo.mip6BindingUDPEncapsulate = IP_FALSE;
|
|
}
|
|
|
|
/**/
|
|
pbuf->pktinfo.haMobilityBindingTunnelingType = IPMIP_TUNNELTYPE_GRE6;
|
|
}
|
|
else
|
|
{
|
|
/**/
|
|
pbuf->pktinfo.haMobilityBindingTunnelingType = IPMIP_TUNNELTYPE_GRE6;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef IPMIP_USE_INET
|
|
if (pbuf->pktinfo.mip6BindingCOAType == IPMIP6_MIB_InetAddressType_ipv4)
|
|
{
|
|
if (pbuf->iff_transport.priv->ifenabled_H[IPMIP_TUNNELTYPE_IPIP] == IP_FALSE)
|
|
{
|
|
/* IMPOSSIBLE COMBINATION */
|
|
ipmip_pktbuf_error(&pbuf->ipmip6.ipmip, 0,
|
|
"IPIP is enabled in the v4 CoA domain");
|
|
return;
|
|
}
|
|
|
|
pbuf->pktinfo.haMobilityBindingTunnelingType = IPMIP_TUNNELTYPE_IPIP;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (pbuf->iff_transport.priv->ifenabled_H[IPMIP_TUNNELTYPE_IP6IP6] == IP_FALSE)
|
|
{
|
|
/* IMPOSSIBLE COMBINATION */
|
|
ipmip_pktbuf_error(&pbuf->ipmip6.ipmip, 0,
|
|
"IP6IP6 is not enabled in the v6 CoA domain");
|
|
return;
|
|
}
|
|
|
|
pbuf->pktinfo.haMobilityBindingTunnelingType = IPMIP_TUNNELTYPE_IP6IP6;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6ha_mh_send_binding_acknowledgement
|
|
*===========================================================================
|
|
* Description: Send a binding acknowledgement in response to a binding
|
|
* update.
|
|
* Parameters: dst - The destination address.
|
|
* home - IP_NULL or home address.
|
|
* status - The status code to return.
|
|
* seq - The sequence number.
|
|
* lifetime - Lifetime value.
|
|
* refresh - Refresh interval, or 0 if to be omitted.
|
|
* Returns: IP_TRUE if successful, IP_FALSE otherwise.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipmip6ha_mh_send_binding_acknowledgement(Ipmip6ha_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ip_u8 status,
|
|
Ipmip6ha_pktbuf_t *pbpkt,
|
|
Ipmip6ha_node_t *pbnode)
|
|
{
|
|
Ipmip6_pktbuf_t *pkt;
|
|
Ipmip6_mh_binding_acknowledgement_t *ba;
|
|
int err;
|
|
Ip_u8 otype;
|
|
void *optr;
|
|
Ip_socklen_t olen;
|
|
int offset = 0;
|
|
unsigned si, mask;
|
|
struct Ip_in6_addr *dst;
|
|
|
|
|
|
/* Grab a packet */
|
|
pkt = (Ipmip6_pktbuf_t *) ipmip6_mh_pkt_alloc(&ctx->ipmip6,
|
|
proc,
|
|
sizeof(Ipmip6_mh_binding_acknowledgement_t),
|
|
IPMIP6_MH_BINDING_ACKNOWLEDGEMENT);
|
|
|
|
/* Out of memory? */
|
|
if (pkt == IP_NULL)
|
|
return IP_FALSE;
|
|
|
|
|
|
/* Do a temporary route update */
|
|
if (ipmip6ha_route_update_temporary(ctx, proc, pbpkt, IPMIP6_MH_BU_IS_P(pbpkt->pktinfo.haMobilityBindingFlags)))
|
|
ipmip6_pkt_set_noroute(pkt);
|
|
|
|
/* Get the MH header */
|
|
ba = (Ipmip6_mh_binding_acknowledgement_t *) IPMIP_BUFFER_PTR(&pkt->ipmip.packet);
|
|
|
|
/* Set values */
|
|
ba->ba_status = status;
|
|
ba->ba_flags = 0; /* IPMIP6_MH_BA_K; */ /* XXX: TODO */
|
|
if (IP_BIT_ISSET(pbpkt->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_P))
|
|
ba->ba_flags = IPMIP6_MH_BA_P;
|
|
else if (IP_BIT_ISSET(pbpkt->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_R))
|
|
ba->ba_flags = IPMIP6_MH_BA_R;
|
|
|
|
ba->ba_seq = ip_htons(pbpkt->pktinfo.haMobilityBindingSequenceNumber);
|
|
|
|
if (!pbpkt->pktinfo.haMobilityDeregistration && status < IPMIP6_MH_BA_STATUS_ERROR_UNKNOWN)
|
|
{
|
|
Ip_u16 lifetime = pbpkt->pktinfo.haMobilityBindingTimeGranted;
|
|
Ip_u16 refresh = (Ip_u16) (((Ip_u32)pbpkt->iff_home.priv->refresh * (Ip_u32)lifetime) / 100);
|
|
|
|
lifetime >>= 2;
|
|
ba->ba_lifetime = ip_htons(lifetime);
|
|
|
|
if (refresh)
|
|
{
|
|
/* Set value, add option to packet */
|
|
refresh = ip_htons(refresh);
|
|
ipmip6_mh_pkt_opt_add(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
IPMIP6_MH_MO_BINDING_REFRESH_ADVICE,
|
|
sizeof(refresh),
|
|
&refresh);
|
|
}
|
|
|
|
/* Loop until we're unable to locate any more options */
|
|
while ((offset = ipmip6_mh_opt_next(IPMIP_BUFFER_PTR(&pbpkt->mo_reply), (Ip_socklen_t)IPMIP_BUFFER_SIZE(&pbpkt->mo_reply), offset, &otype, &olen, &optr)) != -1)
|
|
ipmip6_mh_pkt_opt_add(&ctx->ipmip6, proc, pkt, otype, (Ip_u8)olen, optr);
|
|
}
|
|
else
|
|
{
|
|
ba->ba_lifetime = 0;
|
|
}
|
|
|
|
/* Need to reply to it */
|
|
if (pbnode != IP_NULL && pbpkt->ipmip6.mo.gre_key != IP_NULL)
|
|
{
|
|
Ipmip6_mh_mo_gre_key_option_t gk;
|
|
|
|
gk.hdr.val = IPMIP6_MH_MO_GRE_KEY_OPTION;
|
|
gk.reserved = 0;
|
|
|
|
/* Got both keys? */
|
|
if (pbpkt->ipmip6.mo.gre_key->hdr.len == 6
|
|
&& pbnode->gre_keys[IPMIP_LINK_UP]
|
|
&& pbnode->gre_keys[IPMIP_LINK_DOWN])
|
|
{
|
|
/* Must request UPLINK key */
|
|
gk.hdr.len = 6;
|
|
gk.gre_key = ipcom_htonl(*pbnode->gre_keys[IPMIP_LINK_UP]);
|
|
}
|
|
else
|
|
{
|
|
gk.hdr.len = 2;
|
|
}
|
|
|
|
/**/
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&gk.hdr);
|
|
|
|
}
|
|
|
|
/* Add refresh option if specified */
|
|
|
|
#ifdef IPMIP_USE_INET
|
|
if (pbpkt->pktinfo.mip6BindingCOAType == IPMIP6_MIB_InetAddressType_ipv4)
|
|
{
|
|
if (pbpkt->pktinfo.mip6BindingUDPEncapsulate)
|
|
{
|
|
/* ADD NAT DETECT */
|
|
Ipmip6_mh_mo_dsmipv6_ipv4_nat_detect_t ndetect;
|
|
|
|
ndetect.hdr.val = IPMIP6_MH_MO_DSMIPV6_IPV4_NAT_DETECT;
|
|
ndetect.hdr.len = 6;
|
|
|
|
if (pbpkt->iff_transport.priv->ForceIPv4UDPEncapsulation)
|
|
ndetect.flags = ip_htons(IPMIP6_MH_MO_DSMIPV6_IPV4_NAT_DETECT_F);
|
|
|
|
/**/
|
|
IP_SET_HTONL(&ndetect.refresh, pbpkt->iff_transport.priv->IPv4UDPEncapsulationNATTimeout);
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&ndetect.hdr);
|
|
|
|
if (IP_BIT_ISSET(pbpkt->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_T))
|
|
{
|
|
ba = (Ipmip6_mh_binding_acknowledgement_t *) IPMIP_BUFFER_PTR(&pkt->ipmip.packet);
|
|
ba->ba_flags |= IPMIP6_MH_BA_T;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* If there's a NAI, it must be added */
|
|
if (pbpkt->ipmip6.mo.mn_naip != IP_NULL)
|
|
{
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&pbpkt->ipmip6.mo.mn_naip->hdr);
|
|
}
|
|
|
|
/* Do some proxy mobility processing */
|
|
if (IP_BIT_ISSET(pbpkt->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_P))
|
|
{
|
|
/* Proxy Mobility Request we're responding to */
|
|
Ipmip6ha_node_P_t *proxy = (Ipmip6ha_node_P_t *)pbnode;
|
|
|
|
dst = &pbpkt->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr;
|
|
|
|
/*
|
|
o The local mobility anchor, when sending the Proxy Binding
|
|
Acknowledgement message to the mobile access gateway, MUST
|
|
construct the message as specified below.
|
|
|
|
IPv6 header (src=LMAA, dst=Proxy-CoA)
|
|
Mobility header
|
|
- BA
|
|
Mobility Options
|
|
- Mobile Node Identifier option (mandatory)
|
|
- Home Network Prefix option(s) (mandatory)
|
|
- Handoff Indicator option (mandatory)
|
|
- Access Technology Type option (mandatory)
|
|
- Timestamp option (optional)
|
|
- Mobile Node Link-layer Identifier option (optional)
|
|
- Link-local Address option (optional)
|
|
|
|
Figure 6: Proxy Binding Acknowledgement Message Format
|
|
|
|
|
|
o The Source Address field in the IPv6 header of the message MUST be
|
|
set to the destination address of the received Proxy Binding
|
|
Update message.
|
|
|
|
o The Destination Address field in the IPv6 header of the message
|
|
MUST be set to the source address of the received Proxy Binding
|
|
Update message. When there is no Alternate Care-of Address option
|
|
present in the request, the destination address is the same as the
|
|
Proxy-CoA; otherwise, the address may not be the same as the
|
|
Proxy-CoA.
|
|
|
|
o The Mobile Node Identifier option [RFC4283] MUST be present. The
|
|
identifier field in the option MUST be copied from the Mobile Node
|
|
Identifier option in the received Proxy Binding Update message.
|
|
If the option was not present in the request, the identifier in
|
|
the option MUST be set to a zero length identifier.
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
o At least one Home Network Prefix option MUST be present.
|
|
*/
|
|
if (status >= IPMIP6_MH_BA_STATUS_ERROR_UNKNOWN)
|
|
{
|
|
unsigned i;
|
|
/*
|
|
* If the Status field is set to a value greater than or equal to
|
|
128, i.e., if the Proxy Binding Update is rejected, all the
|
|
Home Network Prefix options that were present in the request
|
|
(along with their prefix values) MUST be present in the reply.
|
|
But, if there was no Home Network Prefix option present in the
|
|
request, then there MUST be only one Home Network Prefix option
|
|
with the value in the option set to ALL_ZERO.
|
|
*/
|
|
|
|
if (pbpkt->ipmip6.mo.pmipv6_home_prefix.numelem == 0)
|
|
{
|
|
Ipmip6_mh_mo_pmipv6_home_prefix_t hp;
|
|
|
|
ipcom_memset(&hp, 0, sizeof(hp));
|
|
hp.hdr.val = IPMIP6_MH_MO_PMIPV6_HOME_NETWORK_PREFIX_OPTION;
|
|
hp.hdr.len = 18;
|
|
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&hp.hdr);
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < pbpkt->ipmip6.mo.pmipv6_home_prefix.numelem; i++)
|
|
{
|
|
Ipmip6_mh_mo_pmipv6_home_prefix_t *hp = (Ipmip6_mh_mo_pmipv6_home_prefix_t *)pbpkt->ipmip6.mo.pmipv6_home_prefix.hdrs[i];
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&hp->hdr);
|
|
}
|
|
}
|
|
|
|
if (pbpkt->ipmip6.mo.pmipv6_link_local_address)
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&pbpkt->ipmip6.mo.pmipv6_link_local_address->hdr);
|
|
}
|
|
else if (pbnode != IP_NULL)
|
|
{
|
|
/*
|
|
* For all other cases, there MUST be a Home Network Prefix option
|
|
for each of the assigned home network prefixes (for that
|
|
mobility session), and with the prefix value in the option set
|
|
to the allocated prefix value.
|
|
*/
|
|
|
|
Ipmip6ha_nemo_t *nemo;
|
|
Ipmip6_mh_mo_pmipv6_home_prefix_t hp;
|
|
|
|
hp.hdr.val = IPMIP6_MH_MO_PMIPV6_HOME_NETWORK_PREFIX_OPTION;
|
|
hp.hdr.len = 18;
|
|
hp.reserved = 0;
|
|
|
|
for (nemo = IPCOM_LIST_FIRST(&pbnode->nemo_v6); nemo != IP_NULL; nemo = IPCOM_LIST_NEXT(&nemo->next))
|
|
{
|
|
ipcom_memcpy(&hp.prefix, &nemo->prefix, sizeof(nemo->prefix));
|
|
hp.prefix_length = nemo->len;
|
|
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&hp.hdr);
|
|
}
|
|
}
|
|
|
|
#ifdef IPMIP_USE_INET
|
|
if (pbnode != IP_NULL && pbnode->nemo_hoa_v4.size > 0)
|
|
{
|
|
Ipmip6_mh_mo_pdsmipv6_ipv4_default_router_address_t gw;
|
|
|
|
gw.hdr.val = IPMIP6_MH_MO_PDSMIPV6_IPV4_DEFAULT_ROUTER_ADDRESS;
|
|
gw.hdr.len = 6;
|
|
gw.reserved = 0;
|
|
gw.gw = proxy->router;
|
|
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&gw.hdr);
|
|
}
|
|
#endif
|
|
/*
|
|
o The Handoff Indicator option MUST be present. The handoff
|
|
indicator field in the option MUST be copied from the Handoff
|
|
Indicator option in the received Proxy Binding Update message. If
|
|
the option was not present in the request, the value in the option
|
|
MUST be set to zero.
|
|
*/
|
|
if (pbpkt->ipmip6.mo.pmipv6_handoff_indicator != IP_NULL)
|
|
{
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&pbpkt->ipmip6.mo.pmipv6_handoff_indicator->hdr);
|
|
}
|
|
else
|
|
{
|
|
Ipmip6_mh_mo_pmipv6_handoff_indicator_t hi;
|
|
hi.hdr.val = IPMIP6_MH_MO_PMIPV6_HANDOFF_INDICATOR_OPTION;
|
|
hi.hdr.len = 2;
|
|
hi.hi = 0;
|
|
hi.reserved = 0;
|
|
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&hi.hdr);
|
|
}
|
|
|
|
|
|
/*
|
|
o The Access Technology Type option MUST be present. The access
|
|
technology type field in the option MUST be copied from the Access
|
|
Technology Type option in the received Proxy Binding Update
|
|
message. If the option was not present in the request, the value
|
|
in the option MUST be set to zero.
|
|
*/
|
|
if (pbpkt->ipmip6.mo.pmipv6_access_technology != IP_NULL)
|
|
{
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&pbpkt->ipmip6.mo.pmipv6_access_technology->hdr);
|
|
}
|
|
else
|
|
{
|
|
Ipmip6_mh_mo_pmipv6_access_technology_type_t at;
|
|
at.hdr.val = IPMIP6_MH_MO_PMIPV6_ACCESS_TECHNOLOGY_TYPE_OPTION;
|
|
at.hdr.len = 2;
|
|
at.att = 0;
|
|
at.reserved = 0;
|
|
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&at.hdr);
|
|
}
|
|
|
|
|
|
/*
|
|
o The Timestamp option MUST be present only if the same option was
|
|
present in the received Proxy Binding Update message and MUST NOT
|
|
be present otherwise. Considerations from Section 5.5 must be
|
|
applied for constructing the Timestamp option.
|
|
*/
|
|
if (pbpkt->ipmip6.mo.pmipv6_timestamp != IP_NULL)
|
|
{
|
|
if (status != IPMIP6_MH_BA_STATUS_ERROR_TIMESTAMP_MISMATCH)
|
|
{
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&pbpkt->ipmip6.mo.pmipv6_timestamp->hdr);
|
|
}
|
|
else
|
|
{
|
|
Ipmip6_mh_mo_pmipv6_timstamp_t timestamp;
|
|
|
|
ipmip6_util_timestamp(×tamp.timestamp, 0);
|
|
timestamp.hdr.len = 8;
|
|
timestamp.hdr.val = IPMIP6_MH_MO_PMIPV6_TIMESTAMP_OPTION;
|
|
|
|
/**/
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
×tamp.hdr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
o The Mobile Node Link-layer Identifier option MUST be present only
|
|
if the same option was present in the received Proxy Binding
|
|
Update message and MUST NOT be present otherwise. The link-layer
|
|
identifier value MUST be copied from the Mobile Node Link-layer
|
|
Identifier option present in the received Proxy Binding Update
|
|
message.
|
|
*/
|
|
if (pbpkt->ipmip6.mo.pmipv6_mn_link_layer_id != IP_NULL)
|
|
{
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&pbpkt->ipmip6.mo.pmipv6_mn_link_layer_id->hdr);
|
|
}
|
|
|
|
/*
|
|
o The Link-local Address option MUST be present only if the same
|
|
option was present in the received Proxy Binding Update message
|
|
and MUST NOT be present otherwise. If the Status field in the
|
|
reply is set to a value greater than or equal to 128, i.e., if the
|
|
Proxy Binding Update is rejected, then the link-local address from
|
|
the request MUST be copied to the Link-local Address option in the
|
|
reply, otherwise the following considerations apply.
|
|
|
|
* If the received Proxy Binding Update message has the Link-local
|
|
Address option with ALL_ZERO value and if there is an existing
|
|
Binding Cache entry associated with this request, then the
|
|
link-local address from the Binding Cache entry MUST be copied
|
|
to the Link-local Address option in the reply.
|
|
|
|
* If the received Proxy Binding Update message has the Link-local
|
|
Address option with ALL_ZERO value and if there is no existing
|
|
Binding Cache entry associated with this request, then the
|
|
local mobility anchor MUST generate the link-local address that
|
|
the mobile access gateway can use on the point-to-point link
|
|
shared with the mobile node. This generated address MUST be
|
|
copied to the Link-local Address option in the reply. The same
|
|
address MUST also be copied to the link-local address field of
|
|
Binding Cache entry created for this mobility session.
|
|
|
|
* If the received Proxy Binding Update message has the Link-local
|
|
Address option with NON_ZERO value, then the link-local address
|
|
from the request MUST be copied to the Link-local Address
|
|
option in the reply. The same address MUST also be copied to
|
|
the link-local address field of the Binding Cache entry
|
|
associated with this request (after creating the Binding Cache
|
|
entry, if one does not exist).
|
|
*/
|
|
|
|
if (pbpkt->ipmip6.mo.pmipv6_link_local_address)
|
|
{
|
|
/* The above mentioned rules are dealt with in ipmip6ha_node_update */
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
proxy? &proxy->pmipv6_link_local_address.hdr : &pbpkt->ipmip6.mo.pmipv6_link_local_address->hdr);
|
|
}
|
|
|
|
/*
|
|
o If IPsec is used for protecting the signaling messages, the
|
|
message MUST be protected using the security association existing
|
|
between the local mobility anchor and the mobile access gateway.
|
|
|
|
o Unlike in Mobile IPv6 [RFC3775], the Type 2 Routing header MUST
|
|
NOT be present in the IPv6 header of the packet.
|
|
*/
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
/* H - NODE */
|
|
dst = &pbpkt->pktinfo.mip6BindingHomeAddress.ipv6;
|
|
}
|
|
|
|
/* Resolve mask */
|
|
if (pbnode)
|
|
mask = pbnode->service_supported;
|
|
else
|
|
mask = IP_BIT_ISSET(pbpkt->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_P)? ctx->extensions_P_mask : ctx->extensions_H_mask;
|
|
|
|
for (si = 0; si < ctx->extensions_num; si++)
|
|
{
|
|
if ((mask & (1 << si)) == 0)
|
|
continue;
|
|
|
|
if (ctx->extensions[si]->pkt_rep_apply != IP_NULL)
|
|
(*ctx->extensions[si]->pkt_rep_apply) (ctx, proc, ctx->extdata[si], pbpkt->iff_home.priv->extdata[si], pbnode? pbnode->extdata[si] : IP_NULL, pbnode, (Ipmip6ha_pktbuf_t *)pkt, pbpkt);
|
|
}
|
|
|
|
/* Force it to go out using the CoA */
|
|
ipmip6_pkt_pktinfo_src_opt_add(pkt, &pbpkt->iff_transport.priv->ifaddr.prefix);
|
|
|
|
/* Send */
|
|
if ((err = ipmip6_pkt_send(&ctx->ipmip6, proc, ctx->ipmip6.pkt->fd_mh, pkt, dst)) != 0)
|
|
{
|
|
IPCOM_LOG2(INFO,
|
|
"%s: Error sending MH Acknowledgement = %x",
|
|
ctx->ipmip6.ipmip.name,
|
|
err);
|
|
return IP_FALSE;
|
|
}
|
|
|
|
return IP_FALSE;
|
|
}
|
|
|
|
enum
|
|
{
|
|
IPMIP6HA_MH_BU_DAD = 0,
|
|
IPMIP6HA_MH_BU_SEQUENCE,
|
|
IPMIP6HA_MH_BU_PROCESS,
|
|
IPMIP6HA_MH_BU_TUNNEL_TYPE,
|
|
IPMIP6HA_MH_BU_NEMO,
|
|
IPMIP6HA_MH_BU_DSMIPV6,
|
|
IPMIP6HA_MH_BU_SERVICE_UPDATE,
|
|
IPMIP6HA_MH_BU_TERMINATE,
|
|
IPMIP6HA_MH_BU_ACCEPT,
|
|
IPMIP6HA_MH_BU_NODE_UPDATE,
|
|
IPMIP6HA_MH_BU_SERVICE_ENABLE,
|
|
IPMIP6HA_MH_BU_ACK
|
|
};
|
|
|
|
IP_STATIC int
|
|
ipmip6ha_mh_process_binding_update_bc(int event,
|
|
Ipmip6ha_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6ha_pktbuf_t *pbuf,
|
|
Ipmip6ha_node_t *node)
|
|
{
|
|
int ret = IPMIP_TIMER_RET_DONE;
|
|
int errcode;
|
|
|
|
if (event == IPMIP_TIMER_EVENT_RUN)
|
|
{
|
|
/* Prepare the timer */
|
|
ipmip_timer_prepare(&ctx->ipmip6.ipmip, proc, &node->service, node->lock, (Ipmip_timer_func_t)ipmip6ha_mh_process_binding_update_bc, pbuf, node);
|
|
|
|
switch (pbuf->state)
|
|
{
|
|
case IPMIP6HA_MH_BU_DAD:
|
|
if ((node->state & IPMIP6HA_NODE_STATE_DAD_FAILED) != 0)
|
|
{
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_DUPLICATE_ADDRESS_DETECTION,
|
|
pbuf,
|
|
node);
|
|
/* There should be a pending timeout for this node; which will hit as soon as we release the lock */
|
|
break;
|
|
}
|
|
|
|
/* FALL THROUGH */
|
|
case IPMIP6HA_MH_BU_SEQUENCE:
|
|
/* Setup init values */
|
|
pbuf->pktinfo.haMobilityRefreshRequest = ipmip6ha_node_refresh_request(node, pbuf);
|
|
|
|
/* Sequence number check */
|
|
if (pbuf->ipmip6.mo.pmipv6_timestamp == IP_NULL)
|
|
{
|
|
if (node->updated != 0)
|
|
{
|
|
Ip_u16 out_index = (Ip_u16) (pbuf->pktinfo.haMobilityBindingSequenceNumber - node->sequence);
|
|
|
|
/* Verify sequence number */
|
|
if (out_index >= 32768)
|
|
{
|
|
IPMIP6_EVENT_IF(INFO, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Erronous sequence %u number in mobile node %s binding update (prev=%u, index=%u)",
|
|
pbuf->pktinfo.haMobilityBindingSequenceNumber,
|
|
ipmip6ha_node_describe(proc, node),
|
|
node->sequence,
|
|
out_index));
|
|
|
|
/* Set to correct */
|
|
pbuf->pktinfo.haMobilityBindingSequenceNumber = node->sequence;
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_SEQUENCE_NUMBER,
|
|
pbuf,
|
|
node);
|
|
goto error_out;
|
|
}
|
|
|
|
IPMIP6_EVENT_IF(DEBUG, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Processing sequence %u number in mobile node %s binding update (prev=%u, index=%u)",
|
|
pbuf->pktinfo.haMobilityBindingSequenceNumber,
|
|
ipmip6ha_node_describe(proc, node),
|
|
node->sequence,
|
|
out_index));
|
|
}
|
|
|
|
/* Invalidate this sequence number */
|
|
node->sequence = (Ip_u16) (pbuf->pktinfo.haMobilityBindingSequenceNumber + 1);
|
|
}
|
|
else if (IPMIP6_MH_BU_IS_P(node->regflags))
|
|
{
|
|
/* Verify the timestamp */
|
|
struct Ip_timeval timestamp;
|
|
|
|
ipmip6_util_timestamp_to_tv(&pbuf->ipmip6.mo.pmipv6_timestamp->timestamp, ×tamp);
|
|
if (pbuf->iff_home.priv->MobileNodeGeneratedTimestampInUse == IP_FALSE)
|
|
{
|
|
Ipmip6_util_timediff_t ndiff;
|
|
|
|
ipmip6_util_timestamp_diff_tv(IP_NULL, ×tamp, &ndiff);
|
|
|
|
/* Are there more seconds passed than allowed by the Validity Window? */
|
|
if (ip_timeval_cmp(&ndiff.diff, &pbuf->iff_home.priv->TimestampValidityWindow_tv, >))
|
|
{
|
|
IPMIP6_EVENT_IF(INFO, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Timestamp out of bounds for %s (%ds %dus vs %ds %dus - is not within Validity Window",
|
|
ipmip6ha_node_describe(proc, node),
|
|
ndiff.diff.tv_sec, ndiff.diff.tv_usec, pbuf->iff_home.priv->TimestampValidityWindow_tv.tv_sec, pbuf->iff_home.priv->TimestampValidityWindow_tv.tv_usec));
|
|
|
|
/* Send time out of sync */
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_TIMESTAMP_MISMATCH,
|
|
pbuf,
|
|
node);
|
|
goto error_out;
|
|
}
|
|
}
|
|
|
|
if (node->updated != 0)
|
|
{
|
|
/* Check that we have a larger timestamp */
|
|
if (ip_timeval_cmp(×tamp, &((Ipmip6ha_node_P_t *)node)->timestamp, <))
|
|
{
|
|
IPMIP6_EVENT_IF(INFO, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Timestamp too small for %s - must be larger than previously accepted",
|
|
ipmip6ha_node_describe(proc, node)));
|
|
|
|
/* Send time out of sync */
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_TIMESTAMP_LOWER_THAN_PREV_ACCEPTED,
|
|
pbuf,
|
|
node);
|
|
goto error_out;
|
|
}
|
|
}
|
|
/* We're through the timestamp verification */
|
|
}
|
|
else
|
|
{
|
|
IPMIP6_EVENT_IF(INFO, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Timestamp provided on non-Proxy mobility entity",
|
|
ipmip6ha_node_describe(proc, node)));
|
|
|
|
/* Send time out of sync */
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_UNKNOWN,
|
|
pbuf,
|
|
node);
|
|
goto error_out;
|
|
}
|
|
|
|
case IPMIP6HA_MH_BU_PROCESS:
|
|
pbuf->state = IPMIP6HA_MH_BU_PROCESS;
|
|
if (ipmip6ha_node_extension_pkt_req_validate(ctx, proc, node, pbuf))
|
|
return IPMIP_TIMER_RET_PENDING;
|
|
|
|
if (pbuf->ipmip6.ipmip.message != IP_NULL)
|
|
{
|
|
IPMIP6_EVENT_IF(WARNING, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"%s discarded during extension processing with error message %s(%d)",
|
|
ipmip6ha_node_describe(proc, node),
|
|
pbuf->ipmip6.ipmip.message,
|
|
pbuf->ipmip6.ipmip.error_code));
|
|
|
|
if (pbuf->ipmip6.ipmip.error_code)
|
|
{
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
pbuf->ipmip6.ipmip.error_code,
|
|
pbuf,
|
|
node);
|
|
}
|
|
|
|
goto error_out;
|
|
}
|
|
|
|
case IPMIP6HA_MH_BU_TUNNEL_TYPE:
|
|
/* Sanity */
|
|
ipmip6ha_mh_transport_type(node, pbuf);
|
|
if (pbuf->ipmip6.ipmip.message != IP_NULL)
|
|
{
|
|
IPMIP6_EVENT_IF(WARNING, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"%s discarded during transport type processing with error message %s(%d)",
|
|
ipmip6ha_node_describe(proc, node),
|
|
pbuf->ipmip6.ipmip.message,
|
|
pbuf->ipmip6.ipmip.error_code));
|
|
|
|
if (pbuf->ipmip6.ipmip.error_code)
|
|
{
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
pbuf->ipmip6.ipmip.error_code,
|
|
pbuf,
|
|
node);
|
|
}
|
|
|
|
goto error_out;
|
|
}
|
|
|
|
/* FALL THROUGH */
|
|
case IPMIP6HA_MH_BU_NEMO:
|
|
if (!pbuf->pktinfo.haMobilityDeregistration)
|
|
{
|
|
if (IP_BIT_ISSET(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_R|IPMIP6_MH_BU_P))
|
|
{
|
|
if (IP_BIT_ISTRUE(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_R))
|
|
{
|
|
errcode = ipmip6ha_nemo_verify(ctx,
|
|
proc,
|
|
node,
|
|
&pbuf->ipmip6.mo.mobile_prefix,
|
|
&pbuf->update_services);
|
|
}
|
|
else
|
|
{
|
|
errcode = ipmip6ha_nemo_pmipv6_verify(ctx,
|
|
proc,
|
|
node,
|
|
&pbuf->ipmip6.mo.pmipv6_home_prefix);
|
|
}
|
|
|
|
if (errcode)
|
|
{
|
|
/* This node should be deleted; */
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
(Ip_u8)errcode,
|
|
pbuf,
|
|
node);
|
|
goto error_out;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Deregistration */
|
|
#ifdef IPMIP_USE_INET
|
|
if (IPMIP6_MH_BU_IS_P(node->regflags))
|
|
{
|
|
if (node->nemo_v6.size > 0 && pbuf->ipmip6.mo.pmipv6_home_prefix.numelem > 0)
|
|
{
|
|
if ((node->nemo_hoa_v4.size + node->nemo_prefix_v4.size) > 0 && pbuf->ipmip6.mo.dsmipv6_prefix_request.numelem == 0)
|
|
{
|
|
/* Selective de-registration of PMIPv6 V6 prefixes */
|
|
Ipmip6ha_nemo_t *nemo;
|
|
|
|
for (nemo = IPCOM_LIST_FIRST(&node->nemo_v6); nemo != IP_NULL; nemo = IPCOM_LIST_NEXT(&nemo->next))
|
|
nemo->flags |= IPMIP6HA_NEMO_ACL_DELETED;
|
|
|
|
pbuf->update_services = IP_TRUE;
|
|
pbuf->pktinfo.haMobilitySelectiveDeregistration = IP_TRUE;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
/* FALL THROUGH */
|
|
#ifdef IPMIP_USE_INET
|
|
case IPMIP6HA_MH_BU_DSMIPV6:
|
|
if (!pbuf->pktinfo.haMobilityDeregistration)
|
|
{
|
|
errcode = ipmip6ha_nemo_dsmipv6_verify(ctx, proc, node, pbuf, &pbuf->update_services);
|
|
if (errcode)
|
|
{
|
|
/* This node should be deleted; */
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
(Ip_u8)errcode,
|
|
pbuf,
|
|
node);
|
|
goto error_out;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (IPMIP6_MH_BU_IS_P(node->regflags))
|
|
{
|
|
if ((node->nemo_hoa_v4.size + node->nemo_prefix_v4.size) > 0 && pbuf->ipmip6.mo.dsmipv6_prefix_request.numelem > 0)
|
|
{
|
|
if (node->nemo_v6.size > 0 && pbuf->ipmip6.mo.pmipv6_home_prefix.numelem == 0)
|
|
{
|
|
/* Selective de-registration of PMIPv6 V4 prefixes */
|
|
Ipmip6ha_nemo_dsmipv6_t *nemo;
|
|
for (nemo = IPCOM_LIST_FIRST(&node->nemo_hoa_v4); nemo != IP_NULL; nemo = IPCOM_LIST_NEXT(&nemo->next))
|
|
nemo->flags |= IPMIP6HA_NEMO_ACL_DELETED;
|
|
for (nemo = IPCOM_LIST_FIRST(&node->nemo_prefix_v4); nemo != IP_NULL; nemo = IPCOM_LIST_NEXT(&nemo->next))
|
|
nemo->flags |= IPMIP6HA_NEMO_ACL_DELETED;
|
|
|
|
pbuf->update_services = IP_TRUE;
|
|
pbuf->pktinfo.haMobilitySelectiveDeregistration = IP_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* FALL THROUGH */
|
|
#endif
|
|
|
|
case IPMIP6HA_MH_BU_SERVICE_UPDATE:
|
|
if (pbuf->update_services)
|
|
{
|
|
pbuf->state = IPMIP6HA_MH_BU_SERVICE_UPDATE;
|
|
if (ipmip6ha_node_update_service(ctx, proc, node))
|
|
return IPMIP_TIMER_RET_PENDING;
|
|
}
|
|
|
|
/* FALL THROUGH */
|
|
|
|
case IPMIP6HA_MH_BU_TERMINATE:
|
|
if (pbuf->pktinfo.haMobilityDeregistration && !pbuf->pktinfo.haMobilitySelectiveDeregistration)
|
|
{
|
|
/* Send ACK */
|
|
if (IP_BIT_ISSET(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_A))
|
|
{
|
|
/* This node should be deleted; */
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_SUCCESS_ACCEPTED,
|
|
pbuf,
|
|
node);
|
|
}
|
|
|
|
|
|
if (IPMIP6_MH_BU_IS_P(node->regflags))
|
|
{
|
|
Ipmip6ha_node_P_t *pnode = (Ipmip6ha_node_P_t *)node;
|
|
|
|
/* Delayed update? */
|
|
if (pnode->delayed_BU != IP_NULL)
|
|
{
|
|
ipmip6ha_node_bri_cancel(ctx, proc, &pnode->node, ipmip6ha_mh_unknown_handoff_completion);
|
|
|
|
ip_timeval_clear(&pnode->delayed_start);
|
|
|
|
ipmip_pktbuf_release(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip);
|
|
ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &pnode->delayed_BU->ipmip6.ipmip.timeout);
|
|
pbuf = pnode->delayed_BU;
|
|
pnode->delayed_BU = IP_NULL;
|
|
return ipmip6ha_mh_process_binding_update_bc(event, ctx, proc, pbuf, node);
|
|
}
|
|
}
|
|
|
|
ret = ipmip6ha_node_terminate(ctx, proc, node, "deregistration", IP_FALSE);
|
|
break;
|
|
}
|
|
|
|
/* FALL THROUGH */
|
|
case IPMIP6HA_MH_BU_ACCEPT:
|
|
pbuf->state = IPMIP6HA_MH_BU_ACCEPT;
|
|
if (ipmip6ha_node_extension_pkt_req_accept(ctx, proc, node, pbuf))
|
|
return IPMIP_TIMER_RET_PENDING;
|
|
/* FALL THROUGH */
|
|
|
|
case IPMIP6HA_MH_BU_NODE_UPDATE:
|
|
ipmip6ha_node_update(ctx, proc, node, pbuf);
|
|
/* FALL THROUGH */
|
|
|
|
case IPMIP6HA_MH_BU_SERVICE_ENABLE:
|
|
pbuf->state = IPMIP6HA_MH_BU_SERVICE_ENABLE;
|
|
if (ipmip6ha_node_enable_service(ctx, proc, node))
|
|
return IPMIP_TIMER_RET_PENDING;
|
|
|
|
/* FALL THROUGH */
|
|
|
|
case IPMIP6HA_MH_BU_ACK:
|
|
/* Send ACK */
|
|
if (IP_BIT_ISSET(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_A))
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_SUCCESS_ACCEPTED,
|
|
pbuf,
|
|
node);
|
|
|
|
if (IPMIP6_MH_BU_IS_P(node->regflags))
|
|
{
|
|
Ipmip6ha_node_P_t *pnode = (Ipmip6ha_node_P_t *)node;
|
|
if (pnode->delayed_BU != IP_NULL)
|
|
{
|
|
ipmip6ha_node_bri_cancel(ctx, proc, &pnode->node, ipmip6ha_mh_unknown_handoff_completion);
|
|
|
|
ip_timeval_clear(&pnode->delayed_start);
|
|
|
|
ipmip_pktbuf_release(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip);
|
|
ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &pnode->delayed_BU->ipmip6.ipmip.timeout);
|
|
pbuf = pnode->delayed_BU;
|
|
pnode->delayed_BU = IP_NULL;
|
|
ret = ipmip6ha_mh_process_binding_update_P_delayed(event, ctx, proc, pnode->delayed_BU, pnode);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
}
|
|
error_out:
|
|
ipmip_pktbuf_release(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6mn_mh_process_binding_acknowledgement
|
|
*===========================================================================
|
|
* Description: Process a binding acknowledgement. This is generic preprocessing
|
|
* prior to this packet being sent to its correct subsystem.
|
|
* Parameters: mn - The context
|
|
* home - The home address.
|
|
* src - The correspondent address.
|
|
* hdr - The packet.
|
|
* len - Length of the hdr.
|
|
* salist - The sa list.
|
|
* Returns: IP_TRUE if successful.
|
|
*
|
|
*/
|
|
|
|
IP_STATIC int
|
|
ipmip6ha_mh_process_binding_update_H(int event,
|
|
Ipmip6ha_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6ha_pktbuf_t *pbuf,
|
|
Ipmip6ha_node_t *head)
|
|
{
|
|
if (event == IPMIP_TIMER_EVENT_RUN)
|
|
{
|
|
Ipmip6ha_node_t *node;
|
|
|
|
for (node = head; node != IP_NULL; node = node->next)
|
|
{
|
|
if (!IPMIP6_MH_BU_IS_H(node->regflags))
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
/* RFC 3775, 9.5.1.
|
|
* This is a deletion request either if 1) the home address and the care
|
|
* of addresses are equal, or 2) the binding lifetime is zero.
|
|
* 1) The home address and the care of address are equal
|
|
* 2) The binding lifetime is zero.
|
|
*/
|
|
if (pbuf->pktinfo.haMobilityDeregistration)
|
|
{
|
|
if (node != IP_NULL)
|
|
{
|
|
pbuf->state = IPMIP6HA_MH_BU_SEQUENCE;
|
|
return ipmip6ha_mh_process_binding_update_bc(event, ctx, proc, pbuf, node);
|
|
}
|
|
|
|
/* Deletion; but we're lacking the binding */
|
|
/* RFC 3775, 10.3.2
|
|
* SHOULD send a binding acknowledgement with value 133.*/
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_NOT_HOME_AGENT,
|
|
pbuf,
|
|
node);
|
|
|
|
}
|
|
else
|
|
{
|
|
Ip_time_t tmp;
|
|
|
|
tmp = pbuf->pktinfo.haMobilityPrefixValid - (pbuf->pktinfo.haBindingLastUpdated - pbuf->pktinfo.haMobilityPrefixTimestamp);
|
|
|
|
pbuf->pktinfo.haMobilityBindingTimeGranted = (Ip_u16)IP_MIN(tmp, pbuf->pktinfo.haMobilityBindingTimeGranted);
|
|
|
|
if (node != IP_NULL)
|
|
{
|
|
pbuf->state = IPMIP6HA_MH_BU_SEQUENCE;
|
|
return ipmip6ha_mh_process_binding_update_bc(event, ctx, proc, pbuf, node);
|
|
}
|
|
else
|
|
{
|
|
|
|
/* 2. Allocate a new Node */
|
|
node = ipmip6ha_node_create_H(ctx, proc, head, pbuf->iff_home.priv, pbuf->iff_transport.priv, &pbuf->pktinfo.mip6BindingHomeAddress.ipv6, pbuf->ipmip6.mo.mn_naip, pbuf->pktinfo.haMobilityBindingFlags);
|
|
if (node != IP_NULL)
|
|
{
|
|
/* 3. Do DAD
|
|
* 3.1 Lock the Node lock, and indicate a timer.
|
|
* 3.2 Init DAD
|
|
*/
|
|
ipmip_timer_prepare(&ctx->ipmip6.ipmip, proc, &node->service, node->lock, (Ipmip_timer_func_t)ipmip6ha_mh_process_binding_update_bc, pbuf, node);
|
|
|
|
node->state |= IPMIP6HA_NODE_STATE_NDP_HOA;
|
|
if (IP_BIT_ISSET(node->regflags, IPMIP6_MH_BU_L))
|
|
node->state |= IPMIP6HA_NODE_STATE_NDP_LLOCAL;
|
|
|
|
ipmip_timer_lock_acquire(&ctx->ipmip6.ipmip, proc, node->ifp_home->ipmip6.iff_lock);
|
|
|
|
ipmip6_nd_nud_query_single_iflocked(&ctx->ipmip6, proc, &node->ifp_home->ipmip6, &((Ipmip6ha_node_H_t*)node)->nud);
|
|
if (IP_BIT_ISSET(node->regflags, IPMIP6_MH_BU_L))
|
|
ipmip6_nd_nud_query_single_iflocked(&ctx->ipmip6, proc, &node->ifp_home->ipmip6, &((Ipmip6ha_node_H_t*)node)->link_local_nud);
|
|
|
|
ipmip_timer_lock_release(&ctx->ipmip6.ipmip, proc, node->ifp_home->ipmip6.iff_lock);
|
|
|
|
/* Pend if we've gotten a head; otherwise return */
|
|
return head? IPMIP_TIMER_RET_PENDING : IPMIP_TIMER_RET_DONE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ipmip_pktbuf_release(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip);
|
|
return IPMIP_TIMER_RET_DONE;
|
|
}
|
|
|
|
|
|
IP_STATIC int
|
|
ipmip6ha_mh_process_binding_update_P_delayed(int event,
|
|
Ipmip6ha_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6ha_pktbuf_t *pbuf,
|
|
Ipmip6ha_node_P_t *cnode)
|
|
{
|
|
if (event == IPMIP_TIMER_EVENT_RUN)
|
|
{
|
|
Ipmip6ha_node_t *node = &cnode->node;
|
|
|
|
/* Clear the delay */
|
|
cnode->delayed_BU = IP_NULL;
|
|
ip_timeval_clear(&cnode->delayed_start);
|
|
|
|
/* Find the HEAD */
|
|
while (node->prev != IP_NULL)
|
|
node = node->prev;
|
|
|
|
/* 2. Allocate a new Node */
|
|
cnode = (Ipmip6ha_node_P_t *)ipmip6ha_node_create_P(ctx, proc, node, pbuf->iff_home.priv, pbuf->iff_transport.priv, &pbuf->ipmip6.mo, pbuf->pktinfo.haMobilityBindingFlags);
|
|
if (cnode != IP_NULL)
|
|
{
|
|
if (node != IP_NULL)
|
|
return ipmip6ha_mh_process_binding_update_bc(IPMIP_TIMER_EVENT_RUN, ctx, proc, pbuf, &cnode->node);
|
|
|
|
ipmip_timer_call(&ctx->ipmip6.ipmip, proc, cnode->node.lock, (Ipmip_timer_func_t)ipmip6ha_mh_process_binding_update_bc, pbuf, &cnode->node);
|
|
return IPMIP_TIMER_RET_DONE;
|
|
}
|
|
else
|
|
{
|
|
/* Failed. Free buffer */
|
|
ipmip_pktbuf_release(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip);
|
|
}
|
|
}
|
|
|
|
return IPMIP_TIMER_RET_DONE;
|
|
}
|
|
|
|
IP_STATIC int
|
|
ipmip6ha_mh_unknown_handoff_completion(Ipmip6ha_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6ha_node_t *node,
|
|
Ipmip6ha_br_t *bri,
|
|
Ip_bool timeout,
|
|
Ip_u8 status)
|
|
{
|
|
Ipmip6ha_node_P_t *cnode = (Ipmip6ha_node_P_t*) node;
|
|
Ipmip6ha_pktbuf_t *pbuf = cnode->delayed_BU;
|
|
|
|
ipmip6ha_node_bri_free(ctx, proc, node, bri);
|
|
|
|
if (pbuf != IP_NULL)
|
|
{
|
|
cnode->delayed_BU = IP_NULL;
|
|
if (!timeout && IPMIP6_MH_BRA_STATUS_OK(status))
|
|
{
|
|
#ifdef IPTESTENGINE
|
|
IPMIP6_EVENT_IF(INFO, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Mobile node %s handoff indicator 'unknown handoff' revoked previous registration, updating current binding",
|
|
ipmip6ha_node_describe(proc, node)));
|
|
#else
|
|
IPMIP6_EVENT_IF(DEBUG2, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Mobile node %s handoff indicator 'unknown handoff' revoked previous registration, updating current binding",
|
|
ipmip6ha_node_describe(proc, node)));
|
|
#endif
|
|
/* Node revoked; do registration */
|
|
return ipmip6ha_mh_process_binding_update_bc(IPMIP_TIMER_EVENT_RUN, ctx, proc, pbuf, &cnode->node);
|
|
}
|
|
else
|
|
{
|
|
IPMIP6_EVENT_IF(INFO, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Mobile node %s handoff indicator 'unknown handoff' unable to revoke, due to error %s, creating new binding",
|
|
ipmip6ha_node_describe(proc, node),
|
|
timeout? "no acknowledgement received" : ipmip6_mh_bra_error_str(status)));
|
|
|
|
return ipmip6ha_mh_process_binding_update_P_delayed(IPMIP_TIMER_EVENT_RUN, ctx, proc, pbuf, cnode);
|
|
}
|
|
}
|
|
|
|
return IPMIP_TIMER_RET_DONE;
|
|
}
|
|
|
|
|
|
|
|
IP_STATIC Ip_bool
|
|
ipmip6ha_mh_process_binding_update_P_schedule_delayed(Ipmip6ha_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6ha_pktbuf_t *pbuf,
|
|
Ipmip6ha_node_P_t *cnode)
|
|
{
|
|
struct Ip_timeval tv;
|
|
|
|
ipcom_gettimeofday(&tv, IP_NULL);
|
|
|
|
if (cnode->delayed_BU)
|
|
{
|
|
ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &cnode->delayed_BU->ipmip6.ipmip.timeout);
|
|
ipmip_pktbuf_release(&ctx->ipmip6.ipmip, proc, &cnode->delayed_BU->ipmip6.ipmip);
|
|
cnode->delayed_BU = IP_NULL;
|
|
}
|
|
|
|
if (pbuf->iff_home.priv->BindingRevocationEnabled)
|
|
{
|
|
if (!ip_timeval_isset(&cnode->delayed_start))
|
|
{
|
|
/* Try revoke this node */
|
|
cnode->delayed_BU = pbuf;
|
|
ipmip6ha_node_revoke(ctx, proc, &cnode->node, IP_FALSE, IPMIP6_MH_BRI_TRIGGER_INTER_MAG_UNKNOWN_HANDOFF, ipmip6ha_mh_unknown_handoff_completion);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Need to delay until MaxDelayBeforeNewBCEAssign */
|
|
if (ip_timeval_isset(&cnode->delayed_start))
|
|
{
|
|
ip_timeval_sub(&tv, &cnode->delayed_start, &tv);
|
|
if ((unsigned) ((tv.tv_sec * 1000 + tv.tv_usec / 1000)) >= pbuf->iff_home.priv->MaxDelayBeforeNewBCEAssign)
|
|
{
|
|
/* We've just timed out, create new */
|
|
ip_timeval_clear(&cnode->delayed_start);
|
|
return IP_FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Set to current */
|
|
ip_timeval_add(&cnode->delayed_start, &tv, &cnode->delayed_start);
|
|
tv.tv_sec = (long) (pbuf->iff_home.priv->MaxDelayBeforeNewBCEAssign / 1000);
|
|
tv.tv_usec = (long) ((pbuf->iff_home.priv->MaxDelayBeforeNewBCEAssign % 1000) * 1000);
|
|
}
|
|
|
|
/* Store new BU */
|
|
cnode->delayed_BU = pbuf;
|
|
|
|
/* Request timeout */
|
|
ipmip_timer_request_timeval(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
&pbuf->ipmip6.ipmip.timeout,
|
|
cnode->node.lock,
|
|
(Ipmip_timer_func_t)ipmip6ha_mh_process_binding_update_P_delayed,
|
|
pbuf,
|
|
IP_NULL,
|
|
&tv);
|
|
|
|
}
|
|
/* Just wait ... */
|
|
return IP_TRUE;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6mn_mh_process_binding_acknowledgement
|
|
*===========================================================================
|
|
* Description: Process a binding acknowledgement. This is generic preprocessing
|
|
* prior to this packet being sent to its correct subsystem.
|
|
* Parameters: mn - The context
|
|
* home - The home address.
|
|
* src - The correspondent address.
|
|
* hdr - The packet.
|
|
* len - Length of the hdr.
|
|
* salist - The sa list.
|
|
* Returns: IP_TRUE if successful.
|
|
*
|
|
*/
|
|
|
|
IP_STATIC int
|
|
ipmip6ha_mh_process_binding_update_P(int event,
|
|
Ipmip6ha_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6ha_pktbuf_t *pbuf,
|
|
Ipmip6ha_node_t *node)
|
|
{
|
|
if (event == IPMIP_TIMER_EVENT_RUN)
|
|
{
|
|
Ipmip6ha_node_P_t *cnode = IP_NULL;
|
|
|
|
pbuf->state = IPMIP6HA_MH_BU_SEQUENCE;
|
|
|
|
/* Need to verify that we've got the right node */
|
|
if (node != IP_NULL)
|
|
{
|
|
|
|
/* RFC5213; 5.4.1.1 Non-Zero HNP present
|
|
|
|
If there is at least one Home Network Prefix option present in the
|
|
request with a NON_ZERO prefix value and irrespective of the presence
|
|
of the Mobile Node Link-layer Identifier option in the request, the
|
|
following considerations MUST be applied. If there is more than one
|
|
instance of the Home Network Prefix option, any one of the Home
|
|
Network Prefix options present in the request (with NON_ZERO prefix
|
|
value) can be used for locating the Binding Cache entry.
|
|
*/
|
|
|
|
/* if (pbuf->ipmip6.mo.pmipv6_home_prefix.numelem > 0) */
|
|
{
|
|
|
|
unsigned int i;
|
|
Ipmip6_mh_mo_pmipv6_home_prefix_t *prefix = IP_NULL;
|
|
#ifdef IPMIP_USE_INET
|
|
Ipmip6_mh_mo_dsmipv6_ipv4_home_address_t *dsprefix = IP_NULL;
|
|
#endif
|
|
|
|
for (i = 0; i < pbuf->ipmip6.mo.pmipv6_home_prefix.numelem; i++)
|
|
{
|
|
|
|
/* Is it specified? */
|
|
if (!IP_IN6_IS_ADDR_UNSPECIFIED(&((Ipmip6_mh_mo_pmipv6_home_prefix_t*)pbuf->ipmip6.mo.pmipv6_home_prefix.hdrs[i])->prefix))
|
|
{
|
|
prefix = (Ipmip6_mh_mo_pmipv6_home_prefix_t*)pbuf->ipmip6.mo.pmipv6_home_prefix.hdrs[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef IPMIP_USE_INET
|
|
for (i = 0; i < pbuf->ipmip6.mo.dsmipv6_prefix_request.numelem; i++, dsprefix = IP_NULL)
|
|
{
|
|
dsprefix = (Ipmip6_mh_mo_dsmipv6_ipv4_home_address_t*)pbuf->ipmip6.mo.dsmipv6_prefix_request.hdrs[i];
|
|
|
|
if (IP_GET_32ON8(&dsprefix->home_address) != 0)
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if (prefix
|
|
#ifdef IPMIP_USE_INET
|
|
|| dsprefix
|
|
#endif
|
|
)
|
|
{
|
|
Ip_size_t tot_num = 0;
|
|
/*
|
|
1. The local mobility anchor MUST verify if there is an existing
|
|
Binding Cache entry with one of its home network prefixes
|
|
matching the prefix value in one of the Home Network Prefix
|
|
options of the received Proxy Binding Update message.
|
|
*/
|
|
for (cnode = (Ipmip6ha_node_P_t *)node; cnode != IP_NULL; cnode = (Ipmip6ha_node_P_t *)cnode->node.next)
|
|
{
|
|
if (!IPMIP6_MH_BU_IS_P(cnode->node.regflags))
|
|
continue;
|
|
|
|
if (prefix)
|
|
{
|
|
Ipmip6ha_nemo_t *nemo = IP_NULL;
|
|
for (nemo = IPCOM_LIST_FIRST(&cnode->node.nemo_v6); nemo != IP_NULL; nemo = IPCOM_LIST_NEXT(&nemo->next))
|
|
{
|
|
if (IP_IN6_ARE_ADDR_EQUAL(&nemo->prefix, &prefix->prefix))
|
|
break;
|
|
}
|
|
|
|
if (nemo)
|
|
break;
|
|
}
|
|
|
|
#ifdef IPMIP_USE_INET
|
|
if (dsprefix)
|
|
{
|
|
Ipmip6ha_nemo_dsmipv6_t *dsmipv6;
|
|
|
|
for (dsmipv6 = IPCOM_LIST_FIRST(&cnode->node.nemo_hoa_v4); dsmipv6 != IP_NULL; dsmipv6 = IPCOM_LIST_NEXT(&dsmipv6->next))
|
|
if (IP_GET_32ON8(&dsprefix->home_address) == dsmipv6->prefix)
|
|
break;
|
|
|
|
if (dsmipv6)
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (!cnode)
|
|
{
|
|
/*
|
|
2. If a Binding Cache entry does not exist (with one of its home
|
|
network prefixes in the Binding Cache entry matching the prefix
|
|
value in one of the Home Network Prefix options of the received
|
|
Proxy Binding Update message), the request MUST be considered as
|
|
a request for creating a new mobility session.
|
|
*/
|
|
goto match_out;
|
|
}
|
|
|
|
/** THE BELLOW RULE IS DEALT WITH DURING LATER PROCESSING **/
|
|
/*
|
|
3. If there exists a Binding Cache entry (with one of its home
|
|
network prefixes in the Binding Cache entry matching the prefix
|
|
value in one of the Home Network Prefix options of the received
|
|
Proxy Binding Update message), but if the mobile node identifier
|
|
in the entry does not match the mobile node identifier in the
|
|
Mobile Node Identifier option of the received Proxy Binding
|
|
Update message, the local mobility anchor MUST reject the request
|
|
with the Status field value set to
|
|
NOT_AUTHORIZED_FOR_HOME_NETWORK_PREFIX (mobile node is not
|
|
authorized for one or more of the requesting home network
|
|
prefixes).
|
|
*/
|
|
|
|
/* Only do this if its not a dereg or they've specified the home prefixes */
|
|
if (!pbuf->pktinfo.haMobilityDeregistration || pbuf->ipmip6.mo.pmipv6_home_prefix.numelem > 0)
|
|
{
|
|
/* They must all match */
|
|
for (i = 0; i < pbuf->ipmip6.mo.pmipv6_home_prefix.numelem; i++)
|
|
{
|
|
Ipmip6ha_nemo_t *nemo;
|
|
prefix = (Ipmip6_mh_mo_pmipv6_home_prefix_t*)pbuf->ipmip6.mo.pmipv6_home_prefix.hdrs[i];
|
|
|
|
/* Is it specified? */
|
|
if (IP_IN6_IS_ADDR_UNSPECIFIED(&prefix->prefix))
|
|
continue;
|
|
|
|
tot_num++;
|
|
for (nemo = IPCOM_LIST_FIRST(&cnode->node.nemo_v6); nemo != IP_NULL; nemo = IPCOM_LIST_NEXT(&nemo->next))
|
|
{
|
|
if (IP_IN6_ARE_ADDR_EQUAL(&nemo->prefix, &prefix->prefix))
|
|
break;
|
|
}
|
|
|
|
if (!nemo)
|
|
tot_num = 0;
|
|
}
|
|
|
|
/*
|
|
4. If there exists a Binding Cache entry (matching MN-Identifier and
|
|
one of its home network prefixes in the Binding Cache entry
|
|
matching the prefix value in one of the Home Network Prefix
|
|
options of the received Proxy Binding Update message), but if all
|
|
the prefixes in the request do not match all the prefixes in the
|
|
Binding Cache entry, or if they do not match in count, then the
|
|
local mobility anchor MUST reject the request with the Status
|
|
field value set to BCE_PBU_PREFIX_SET_DO_NOT_MATCH (all the home
|
|
network prefixes listed in the BCE do not match all the prefixes
|
|
in the received PBU).
|
|
*/
|
|
if (tot_num != cnode->node.nemo_v6.size)
|
|
{
|
|
/* Not the same number of prefixes */
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_BCE_PBU_PREFIX_SET_DO_NOT_MATCH,
|
|
pbuf,
|
|
IP_NULL);
|
|
goto done_out;
|
|
}
|
|
}
|
|
|
|
|
|
/* We've got a match */
|
|
/*
|
|
5. If there exists a Binding Cache entry (matching MN-Identifier and
|
|
all the home network prefixes in the Binding Cache entry matching
|
|
all the home network prefixes in the received Proxy Binding
|
|
Update message) and if any one or more of these below stated
|
|
conditions are true, the request MUST be considered as a request
|
|
for updating that Binding Cache entry.
|
|
*/
|
|
|
|
|
|
/* 5. CONT
|
|
* If the Handoff Indicator field in the Handoff Indicator option
|
|
present in the request is set to a value of 2 (Handoff between
|
|
two different interfaces of the mobile node).
|
|
*/
|
|
if (pbuf->ipmip6.mo.pmipv6_handoff_indicator->hi == IPMIP6_MH_MO_PMIPV6_HANDOFF_INDICATOR_DIFFERENT_INTERFACE)
|
|
goto match_out;
|
|
|
|
if (cnode->pmipv6_access_technology.att == pbuf->ipmip6.mo.pmipv6_access_technology->att)
|
|
{
|
|
/* 5. CONT
|
|
* If the Proxy-CoA in the Binding Cache entry matches the source
|
|
address of the request (or the address in the Alternate
|
|
Care-of Address option, if the option is present) and if the
|
|
access technology type field in the Access Technology Type
|
|
option present in the request matches the access technology
|
|
type in the Binding Cache entry.
|
|
*/
|
|
if (ipmip6_util_InetAddress_cmp(pbuf->pktinfo.mip6BindingCOAType, &pbuf->pktinfo.mip6BindingCOA, cnode->node.registered.mip6BindingCOAType, &cnode->node.registered.mip6BindingCOA))
|
|
goto match_out;
|
|
|
|
if (pbuf->ipmip6.mo.pmipv6_mn_link_layer_id != IP_NULL)
|
|
{
|
|
/* 5. CONT
|
|
* If there is a Mobile Node Link-layer Identifier option present
|
|
in the request and if the link-layer identifier in the option
|
|
matches the link-layer identifier of the Binding Cache entry
|
|
and the access technology type in the Access Technology Type
|
|
option present in the request matches the access technology
|
|
type in the Binding Cache entry.
|
|
*/
|
|
|
|
if (cnode->pmipv6_mn_link_layer_id == IP_NULL ||
|
|
pbuf->ipmip6.mo.pmipv6_mn_link_layer_id->hdr.len != cnode->pmipv6_mn_link_layer_id->hdr.len ||
|
|
ipcom_memcmp(&pbuf->ipmip6.mo.pmipv6_mn_link_layer_id->lli[0], &cnode->pmipv6_mn_link_layer_id->lli[0], (Ip_size_t) (cnode->pmipv6_mn_link_layer_id->hdr.len - 2)) != 0)
|
|
{
|
|
cnode = IP_NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* 5. CONT
|
|
* If there is no Mobile Node Link-layer Identifier option
|
|
present in the request, the link-layer identifier value in the
|
|
Binding Cache entry is set to ALL_ZERO, the access technology
|
|
type field in the Access Technology Type option present in the
|
|
request matches the access technology type in the Binding
|
|
Cache entry, and if the Handoff Indicator field in the Handoff
|
|
Indicator option present in the request is set to a value of 3
|
|
(Handoff between mobile access gateways for the same
|
|
interface).
|
|
*/
|
|
if (cnode->pmipv6_mn_link_layer_id != IP_NULL ||
|
|
pbuf->ipmip6.mo.pmipv6_handoff_indicator->hi != IPMIP6_MH_MO_PMIPV6_HANDOFF_INDICATOR_SAME_INTERFACE_NEW_MAG)
|
|
{
|
|
cnode = IP_NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cnode = IP_NULL;
|
|
}
|
|
|
|
/*
|
|
6. For all other cases, the message MUST be considered as a request
|
|
for creating a new mobility session. However, if the received
|
|
Proxy Binding Update message has the lifetime value of zero and
|
|
if the request cannot be associated with any existing mobility
|
|
session, the message MUST be silently ignored.
|
|
*/
|
|
goto match_out;
|
|
}
|
|
}
|
|
|
|
/* RFC5213; 5.4.1.2. Mobile Node Link-layer Identifier Option Present in the BU
|
|
|
|
If there is no Home Network Prefix option present in the request with
|
|
a NON_ZERO prefix value, but if there is a Mobile Node Link-layer
|
|
Identifier option present in the request, then the following
|
|
considerations MUST be applied for locating the Binding Cache entry.
|
|
*/
|
|
if (pbuf->ipmip6.mo.pmipv6_mn_link_layer_id != IP_NULL)
|
|
{
|
|
|
|
for (cnode = (Ipmip6ha_node_P_t *)node; cnode != IP_NULL; cnode = (Ipmip6ha_node_P_t *)cnode->node.next)
|
|
{
|
|
if (IPMIP6_MH_BU_IS_P(cnode->node.regflags))
|
|
continue;
|
|
|
|
/*
|
|
1. The local mobility anchor MUST verify if there is an existing
|
|
Binding Cache entry, with the mobile node identifier matching the
|
|
identifier in the received Mobile Node Identifier option, access
|
|
technology type matching the value in the received Access
|
|
Technology Type option, and the link-layer identifier value
|
|
matching the identifier in the received Mobile Node Link-layer
|
|
Identifier option.
|
|
*/
|
|
|
|
if (cnode->pmipv6_access_technology.att != pbuf->ipmip6.mo.pmipv6_access_technology->att ||
|
|
cnode->pmipv6_mn_link_layer_id == IP_NULL)
|
|
continue;
|
|
|
|
if (pbuf->ipmip6.mo.pmipv6_mn_link_layer_id->hdr.len != cnode->pmipv6_mn_link_layer_id->hdr.len ||
|
|
ipcom_memcmp(&pbuf->ipmip6.mo.pmipv6_mn_link_layer_id->lli[0], &cnode->pmipv6_mn_link_layer_id->lli[0], (Ip_size_t) (cnode->pmipv6_mn_link_layer_id->hdr.len - 2)) != 0)
|
|
continue;
|
|
|
|
/*
|
|
2. If there exists a Binding Cache entry (matching MN-Identifier,
|
|
Access Technology Type (ATT), and MN-LL-Identifier), the request
|
|
MUST be considered as a request for updating that Binding Cache
|
|
entry.
|
|
*/
|
|
break;
|
|
|
|
}
|
|
|
|
if (!cnode)
|
|
{
|
|
/*
|
|
3. If there does not exist a Binding Cache entry (matching MN-
|
|
Identifier, ATT, and MN-LL-Identifier) and the Handoff Indicator
|
|
field in the Handoff Indicator option present in the request is
|
|
set to a value of 2 (Handoff between two different interfaces of
|
|
the mobile node). The local mobility anchor MUST apply the
|
|
following additional considerations.
|
|
|
|
* The local mobility anchor MUST verify if there exists one and
|
|
only one Binding Cache entry with the mobile node identifier
|
|
matching the identifier in the Mobile Node Identifier option
|
|
present in the request and for any link-layer identifier
|
|
value. If there exists only one such entry (matching the MN-
|
|
Identifier), the request MUST be considered as a request for
|
|
updating that Binding Cache entry.
|
|
*/
|
|
if (pbuf->ipmip6.mo.pmipv6_handoff_indicator->hi == IPMIP6_MH_MO_PMIPV6_HANDOFF_INDICATOR_DIFFERENT_INTERFACE)
|
|
{
|
|
Ipmip6ha_node_t *tnode = node;
|
|
Ipmip6ha_node_t *pnode = IP_NULL;
|
|
Ip_size_t num_P = 0;
|
|
|
|
for (; tnode != IP_NULL; tnode = tnode->next )
|
|
{
|
|
if (IPMIP6_MH_BU_IS_P(tnode->regflags))
|
|
{
|
|
num_P++;
|
|
pnode = tnode;
|
|
}
|
|
}
|
|
|
|
if (num_P == 1)
|
|
{
|
|
cnode = (Ipmip6ha_node_P_t *)pnode;
|
|
goto match_out;
|
|
}
|
|
}
|
|
else if (pbuf->ipmip6.mo.pmipv6_handoff_indicator->hi == IPMIP6_MH_MO_PMIPV6_HANDOFF_INDICATOR_UNKNOWN)
|
|
{
|
|
/*
|
|
4. If there does not exist a Binding Cache entry (matching MN-
|
|
Identifier, ATT, and MN-LL-Identifier) and if the Handoff
|
|
Indicator field in the Handoff Indicator option present in the
|
|
request is set to a value of 4 (Handoff state unknown), the local
|
|
mobility anchor MUST apply the following additional
|
|
considerations.
|
|
|
|
* The local mobility anchor MUST verify if there exists one and
|
|
only one Binding Cache entry with the mobile node identifier
|
|
matching the identifier in the Mobile Node Identifier option
|
|
present in the request and for any link-layer identifier
|
|
value. If there exists only one such entry (matching the MN-
|
|
Identifier), the local mobility anchor SHOULD wait until the
|
|
existing Binding Cache entry is de-registered by the
|
|
previously serving mobile access gateway, before the request
|
|
can be considered as a request for updating that Binding Cache
|
|
entry. However, if there is no de-registration message that
|
|
is received within MaxDelayBeforeNewBCEAssign amount of time,
|
|
the local mobility anchor, upon accepting the request, MUST
|
|
consider the request as a request for creating a new mobility
|
|
session. The local mobility anchor MAY also choose to create
|
|
a new mobility session without waiting for a de-registration
|
|
message, and this should be configurable on the local mobility
|
|
anchor.
|
|
*/
|
|
|
|
if (!pbuf->pktinfo.haMobilityDeregistration)
|
|
{
|
|
Ipmip6ha_node_t *tnode = node;
|
|
Ipmip6ha_node_t *pnode = IP_NULL;
|
|
Ip_size_t num_P = 0;
|
|
|
|
for (; tnode != IP_NULL; tnode = tnode->next )
|
|
{
|
|
if (IPMIP6_MH_BU_IS_P(tnode->regflags))
|
|
{
|
|
num_P++;
|
|
pnode = tnode;
|
|
}
|
|
}
|
|
|
|
if (num_P == 1)
|
|
{
|
|
if (pbuf->iff_home.priv->MaxDelayBeforeNewBCEAssign != 0)
|
|
{
|
|
if (ipmip6ha_mh_process_binding_update_P_schedule_delayed(ctx, proc, pbuf, (Ipmip6ha_node_P_t *)pnode))
|
|
return IPMIP_TIMER_RET_DONE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
5. For all other cases, the message MUST be considered as a request
|
|
for creating a new mobility session. However, if the received
|
|
Proxy Binding Update message has the lifetime value of zero and
|
|
if the request cannot be associated with any existing mobility
|
|
session, the message MUST be silently ignored.
|
|
*/
|
|
|
|
}
|
|
|
|
goto match_out;
|
|
}
|
|
/* 5.4.1.3. Mobile Node Link-layer Identifier Option Not Present in the Request
|
|
|
|
If there is no Home Network Prefix option present in the request with
|
|
a NON_ZERO prefix value and if there is also no Mobile Node Link-
|
|
layer Identifier option present in the request, then the following
|
|
considerations MUST be applied for locating the Binding Cache entry.
|
|
*/
|
|
else
|
|
{
|
|
Ipmip6ha_node_t *tnode = node;
|
|
Ipmip6ha_node_t *pnode = IP_NULL;
|
|
Ip_size_t num_P = 0;
|
|
|
|
for (; tnode != IP_NULL; tnode = tnode->next )
|
|
{
|
|
if (IPMIP6_MH_BU_IS_P(tnode->regflags))
|
|
{
|
|
num_P++;
|
|
pnode = tnode;
|
|
}
|
|
}
|
|
|
|
/*
|
|
1. The local mobility anchor MUST verify if there exists one and
|
|
only one Binding Cache entry with the mobile node identifier
|
|
matching the identifier in the Mobile Node Identifier option
|
|
present in the request.
|
|
*/
|
|
if (num_P == 1)
|
|
{
|
|
/*
|
|
2. If there exists only one such entry (matching the MN-Identifier)
|
|
and the Handoff Indicator field in the Handoff Indicator option
|
|
present in the request is set to a value of 2 (Handoff between
|
|
two different interfaces of the mobile node) or set to a value of
|
|
3 (Handoff between mobile access gateways for the same
|
|
interface), then the request MUST be considered as a request for
|
|
updating that Binding Cache entry.
|
|
*/
|
|
if (pbuf->ipmip6.mo.pmipv6_handoff_indicator->hi == IPMIP6_MH_MO_PMIPV6_HANDOFF_INDICATOR_DIFFERENT_INTERFACE ||
|
|
pbuf->ipmip6.mo.pmipv6_handoff_indicator->hi == IPMIP6_MH_MO_PMIPV6_HANDOFF_INDICATOR_SAME_INTERFACE_NEW_MAG)
|
|
{
|
|
cnode = (Ipmip6ha_node_P_t *)pnode;
|
|
goto match_out;
|
|
}
|
|
|
|
|
|
/*
|
|
3. If there exists only one such entry (matching the MN-Identifier)
|
|
and the Handoff Indicator field in the Handoff Indicator option
|
|
present in the request is set to a value of 4 (Handoff state
|
|
unknown), the local mobility anchor SHOULD wait until the
|
|
existing Binding Cache entry is de-registered by the previously
|
|
serving mobile access gateway before the request can be
|
|
considered as a request for updating that Binding Cache entry.
|
|
However, if there is no de-registration message that is received
|
|
within MaxDelayBeforeNewBCEAssign amount of time, the local
|
|
mobility anchor, upon accepting the request, MUST consider the
|
|
request as a request for creating a new mobility session. The
|
|
local mobility anchor MAY also choose to create a new mobility
|
|
session without waiting for a de-registration message, and this
|
|
should be configurable on the local mobility anchor.
|
|
*/
|
|
if (pbuf->ipmip6.mo.pmipv6_handoff_indicator->hi == IPMIP6_MH_MO_PMIPV6_HANDOFF_INDICATOR_UNKNOWN)
|
|
{
|
|
if (!pbuf->pktinfo.haMobilityDeregistration)
|
|
{
|
|
if (pbuf->iff_home.priv->MaxDelayBeforeNewBCEAssign != 0)
|
|
{
|
|
if (ipmip6ha_mh_process_binding_update_P_schedule_delayed(ctx, proc, pbuf, (Ipmip6ha_node_P_t *)pnode))
|
|
return IPMIP_TIMER_RET_DONE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
4. For all other cases, the message MUST be considered as a request
|
|
for creating a new mobility session. However, if the received
|
|
Proxy Binding Update message has the lifetime value of zero and
|
|
if the request cannot be associated with any existing mobility
|
|
session, the message MUST be silently ignored.
|
|
*/
|
|
goto match_out;
|
|
}
|
|
}
|
|
|
|
match_out:
|
|
if (cnode != IP_NULL)
|
|
{
|
|
/*
|
|
5.3.5. Binding De-Registration
|
|
|
|
|
|
1. If the received Proxy Binding Update message with the lifetime
|
|
value of zero, has a Source Address in the IPv6 header (or the
|
|
address in the Alternate Care-of Address option, if the option is
|
|
present) different from what is present in the Proxy-CoA field in
|
|
the Binding Cache entry, the local mobility anchor MUST ignore
|
|
the request.
|
|
*/
|
|
if (pbuf->pktinfo.haMobilityDeregistration && !ipmip6_util_InetAddress_cmp(pbuf->pktinfo.mip6BindingCOAType, &pbuf->pktinfo.mip6BindingCOA, cnode->node.registered.mip6BindingCOAType, &cnode->node.registered.mip6BindingCOA))
|
|
{
|
|
IPMIP6_EVENT_IF(DEBUG2, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Deregistration request for %s received from obsoleted MAG(%s)",
|
|
ipmip6ha_node_describe(proc, &cnode->node),
|
|
ipmip6_ntop_InetAddress(proc, pbuf->pktinfo.mip6BindingCOAType, &pbuf->pktinfo.mip6BindingCOA)));
|
|
goto done_out;
|
|
}
|
|
|
|
return ipmip6ha_mh_process_binding_update_bc(event, ctx, proc, pbuf, &cnode->node);
|
|
}
|
|
else
|
|
{
|
|
if (!pbuf->pktinfo.haMobilityDeregistration)
|
|
{
|
|
/* 2. Allocate a new Node */
|
|
cnode = (Ipmip6ha_node_P_t *)ipmip6ha_node_create_P(ctx, proc, node, pbuf->iff_home.priv, pbuf->iff_transport.priv, &pbuf->ipmip6.mo, pbuf->pktinfo.haMobilityBindingFlags);
|
|
if (cnode != IP_NULL)
|
|
{
|
|
if (node != IP_NULL)
|
|
return ipmip6ha_mh_process_binding_update_bc(IPMIP_TIMER_EVENT_RUN, ctx, proc, pbuf, &cnode->node);
|
|
|
|
ipmip_timer_call(&ctx->ipmip6.ipmip, proc, cnode->node.lock, (Ipmip_timer_func_t)ipmip6ha_mh_process_binding_update_bc, pbuf, &cnode->node);
|
|
return IPMIP_TIMER_RET_DONE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
done_out:
|
|
ipmip_pktbuf_release(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip);
|
|
return IPMIP_TIMER_RET_DONE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6mn_mh_process_binding_acknowledgement
|
|
*===========================================================================
|
|
* Description: Process a binding acknowledgement. This is generic preprocessing
|
|
* prior to this packet being sent to its correct subsystem.
|
|
* Parameters: mn - The context
|
|
* home - The home address.
|
|
* src - The correspondent address.
|
|
* hdr - The packet.
|
|
* len - Length of the hdr.
|
|
* salist - The sa list.
|
|
* Returns: IP_TRUE if successful.
|
|
*
|
|
*/
|
|
|
|
IP_STATIC int
|
|
ipmip6ha_mh_process_binding_revocation_P(int event,
|
|
Ipmip6ha_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6ha_pktbuf_t *pbuf,
|
|
Ipmip6ha_node_t *node)
|
|
{
|
|
if (event == IPMIP_TIMER_EVENT_RUN)
|
|
{
|
|
Ip_u32 found = 0;
|
|
unsigned i;
|
|
Ipmip6ha_node_P_t *cnode;
|
|
|
|
if (node == IP_NULL)
|
|
{
|
|
ipmip6ha_mh_bra_send(ctx, proc, pbuf, IPMIP6_MH_BRA_STATUS_BINDING_DOES_NOT_EXIST);
|
|
goto done_out;
|
|
}
|
|
|
|
/* We've got a node head*/
|
|
|
|
#ifdef IPMIP_USE_INET
|
|
if (IP_BIT_ISSET(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BRI_FLAG_V))
|
|
{
|
|
if (pbuf->ipmip6.mo.dsmipv6_prefix_request.numelem > 0)
|
|
{
|
|
Ipmip6ha_nemo_dsmipv6_t *nemo;
|
|
Ipmip6_mh_mo_dsmipv6_ipv4_home_address_t *mp;
|
|
|
|
|
|
ipmip_context_id_lock(ctx->idents);
|
|
|
|
for (cnode = (Ipmip6ha_node_P_t *)node; cnode != IP_NULL; cnode = (Ipmip6ha_node_P_t *)cnode->node.next)
|
|
{
|
|
for (nemo = IPCOM_LIST_FIRST(&cnode->node.nemo_hoa_v4); nemo != IP_NULL; nemo = IPCOM_LIST_NEXT(&nemo->next))
|
|
{
|
|
for (i = 0; i < pbuf->ipmip6.mo.dsmipv6_prefix_request.numelem; i++)
|
|
{
|
|
mp = (Ipmip6_mh_mo_dsmipv6_ipv4_home_address_t *)pbuf->ipmip6.mo.dsmipv6_prefix_request.hdrs[i];
|
|
|
|
if (nemo->prefix == IP_GET_32ON8(&mp->home_address))
|
|
{
|
|
/* To be revoked */
|
|
/* Found one -- We're supposed to revoke this one */
|
|
ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &cnode->node.revocation);
|
|
ipmip_timer_request(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
&cnode->node.revocation,
|
|
cnode->node.lock,
|
|
(Ipmip_timer_func_t)ipmip6ha_node_bri_inet_process,
|
|
cnode,
|
|
"inet revocation",
|
|
0);
|
|
found++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ipmip_context_id_unlock(ctx->idents);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Must pin it down during revocation */
|
|
ipmip_context_id_lock(ctx->idents);
|
|
if (pbuf->ipmip6.mo.pmipv6_home_prefix.numelem > 0)
|
|
{
|
|
Ipmip6_mh_mo_pmipv6_home_prefix_t *prefix = IP_NULL;
|
|
|
|
|
|
for (i = 0; i < pbuf->ipmip6.mo.pmipv6_home_prefix.numelem; i++)
|
|
{
|
|
|
|
/* Is it specified? */
|
|
if (!IP_IN6_IS_ADDR_UNSPECIFIED(&((Ipmip6_mh_mo_pmipv6_home_prefix_t*)pbuf->ipmip6.mo.pmipv6_home_prefix.hdrs[i])->prefix))
|
|
{
|
|
prefix = (Ipmip6_mh_mo_pmipv6_home_prefix_t*)pbuf->ipmip6.mo.pmipv6_home_prefix.hdrs[i];
|
|
/* Need to revoke a couple of prefixes */
|
|
for (cnode = (Ipmip6ha_node_P_t *)node; cnode != IP_NULL; cnode = (Ipmip6ha_node_P_t *)cnode->node.next)
|
|
{
|
|
Ipmip6ha_nemo_t *nemo;
|
|
|
|
if (!IPMIP6_MH_BU_IS_P(cnode->node.regflags))
|
|
continue;
|
|
|
|
for (nemo = IPCOM_LIST_FIRST(&cnode->node.nemo_v6); nemo != IP_NULL; nemo = IPCOM_LIST_NEXT(&nemo->next))
|
|
{
|
|
if (IP_IN6_ARE_ADDR_EQUAL(&nemo->prefix, &prefix->prefix))
|
|
{
|
|
/* Found one -- We're supposed to revoke this one */
|
|
ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &cnode->node.revocation);
|
|
ipmip_timer_request(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
&cnode->node.revocation,
|
|
cnode->node.lock,
|
|
(Ipmip_timer_func_t)ipmip6ha_node_timeout,
|
|
cnode,
|
|
"revocation",
|
|
0);
|
|
found++;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Revoke ALL nodes with the NAI */
|
|
/* Need to revoke a couple of prefixes */
|
|
for (cnode = (Ipmip6ha_node_P_t *)node; cnode != IP_NULL; cnode = (Ipmip6ha_node_P_t *)cnode->node.next)
|
|
{
|
|
if (!IPMIP6_MH_BU_IS_P(cnode->node.regflags))
|
|
continue;
|
|
|
|
/* Found one -- We're supposed to revoke this one */
|
|
ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &cnode->node.revocation);
|
|
ipmip_timer_request(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
&cnode->node.revocation,
|
|
cnode->node.lock,
|
|
(Ipmip_timer_func_t)ipmip6ha_node_timeout,
|
|
cnode,
|
|
"revocation",
|
|
0);
|
|
found++;
|
|
}
|
|
}
|
|
ipmip_context_id_unlock(ctx->idents);
|
|
}
|
|
/* Reply */
|
|
ipmip6ha_mh_bra_send(ctx, proc, pbuf, (Ip_u8) (found? IPMIP6_MH_BRA_STATUS_SUCCESS : IPMIP6_MH_BRA_STATUS_BINDING_DOES_NOT_EXIST));
|
|
}
|
|
|
|
done_out:
|
|
ipmip_pktbuf_release(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip);
|
|
return IPMIP_TIMER_RET_DONE;
|
|
}
|
|
|
|
IP_STATIC Ip_bool
|
|
ipmip6ha_mh_resolve_coa(Ipmip6ha_pktbuf_t *pbuf)
|
|
{
|
|
#ifdef IPMIP_USE_INET
|
|
/* */
|
|
if (pbuf->ipmip6.recvinfo.encapsulation && pbuf->ipmip6.recvinfo.encapsulation->sa.sa_family == IP_AF_INET)
|
|
{
|
|
pbuf->pktinfo.mip6BindingCOAType = IPMIP6_MIB_InetAddressType_ipv4;
|
|
pbuf->pktinfo.mip6BindingUDPEncapPort = pbuf->ipmip6.recvinfo.encapsulation->sin.sin_port;
|
|
pbuf->pktinfo.mip6BindingCOA.ipv4.s_addr = pbuf->ipmip6.recvinfo.encapsulation->sin.sin_addr.s_addr;
|
|
}
|
|
else
|
|
#endif
|
|
/* */
|
|
if (pbuf->ipmip6.recvinfo.encapsulation && pbuf->ipmip6.recvinfo.encapsulation->sa.sa_family != IP_AF_INET6)
|
|
return IP_FALSE;
|
|
else
|
|
{
|
|
|
|
/* RFC 3775. 9.5.1
|
|
* Care Of is Alternate Care-of address if specified.
|
|
* Otherwise Source Address as indicated by the packet */
|
|
if (pbuf->ipmip6.recvinfo.encapsulation)
|
|
ipcom_memcpy(&pbuf->pktinfo.mip6BindingCOA.ipv6, &pbuf->ipmip6.recvinfo.encapsulation->sin6.sin6_addr, sizeof(struct Ip_in6_addr));
|
|
else
|
|
ipcom_memcpy(&pbuf->pktinfo.mip6BindingCOA.ipv6, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, sizeof(struct Ip_in6_addr));
|
|
|
|
pbuf->pktinfo.mip6BindingCOAType = IPMIP6_MIB_InetAddressType_ipv6;
|
|
|
|
/* UDP tunneled v6 frame */
|
|
if (pbuf->ipmip6.recvinfo.encapsulation && pbuf->ipmip6.recvinfo.encapsulation->sin6.sin6_port != 0)
|
|
pbuf->pktinfo.mip6BindingUDPEncapPort = pbuf->ipmip6.recvinfo.encapsulation->sin6.sin6_port;
|
|
}
|
|
return IP_TRUE;
|
|
}
|
|
|
|
IP_STATIC void
|
|
ipmip6ha_mh_global_binding_revocation(Ipmip6ha_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6ha_node_t *node,
|
|
Ipmip6ha_mag_t *mag,
|
|
void *unused)
|
|
{
|
|
IPMIP_UNUSED_PARAMETER(unused);
|
|
|
|
if (IPMIP6_MH_BU_IS_P(node->regflags))
|
|
{
|
|
Ipmip6ha_node_P_t *pnode = (Ipmip6ha_node_P_t *)node;
|
|
|
|
if (pnode->mag == mag)
|
|
{
|
|
/* REVOKE */
|
|
ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &node->revocation);
|
|
ipmip_timer_request(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
&node->revocation,
|
|
node->lock,
|
|
(Ipmip_timer_func_t)ipmip6ha_node_timeout,
|
|
node,
|
|
"per-peer global revocation acknowledged",
|
|
0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip6ha_mh_dispatch
|
|
*===========================================================================
|
|
* Description: Process a received MH packet.
|
|
* Parameters: context - The context.
|
|
* pktinfo - The packet info
|
|
* src - The sender.
|
|
* home - Home address
|
|
* salist - The list of SA's processed.
|
|
* hdr - The MH header
|
|
* mlen - Length of the MH frame.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_bool
|
|
ipmip6ha_mh_dispatch(Ipmip6ha_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6ha_pktbuf_t *pbuf)
|
|
{
|
|
Ipmip6_mobility_header_t *hdr = IPMIP_BUFFER_PTR(&pbuf->ipmip6.ipmip.packet);
|
|
unsigned mlen = (unsigned) ((hdr->mh_len << 3) + 8);
|
|
|
|
IPCOM_LOG1(DEBUG2,
|
|
"%s: Incoming MH packet",
|
|
ctx->ipmip6.ipmip.name);
|
|
|
|
if ((pbuf->iff_transport.priv = ipmip6ha_if_findbypktinfo(ctx, proc, pbuf->ipmip6.ipmip.srcinfo.dst)) == IP_NULL)
|
|
goto done_out;
|
|
|
|
pbuf->iff_home.priv = pbuf->iff_transport.priv;
|
|
|
|
/* Process the Mobility Header */
|
|
switch (hdr->mh_type)
|
|
{
|
|
|
|
case IPMIP6_MH_BINDING_UPDATE:
|
|
if (sizeof(Ipmip6_mh_binding_update_t) > mlen)
|
|
break;
|
|
else
|
|
{
|
|
Ipmip6_mh_binding_update_t *bu = (Ipmip6_mh_binding_update_t *) hdr;
|
|
|
|
/* Store some values */
|
|
pbuf->pktinfo.haMobilityBindingFlags = ip_ntohs(bu->bu_flags);
|
|
pbuf->pktinfo.haMobilityBindingTimeGranted = (Ip_u16) ((ip_ntohs(bu->bu_lifetime)) << 2);
|
|
pbuf->pktinfo.haMobilityBindingSequenceNumber = ip_ntohs(bu->bu_seq);
|
|
pbuf->pktinfo.haBindingLastUpdated = ipmip_util_time();
|
|
|
|
pbuf->pktinfo.mip6BindingHomeAddressType = IPMIP6_MIB_InetAddressType_ipv6;
|
|
|
|
/* Home registration? */
|
|
if ((pbuf->pktinfo.haMobilityBindingFlags & (IPMIP6_MH_BU_H)) == 0)
|
|
break;
|
|
|
|
/* RFC 3775, 9.5.4
|
|
* Source address must be unicast routable.
|
|
*/
|
|
if (!ipmip6_address_unicast_routable(&pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr))
|
|
break;
|
|
|
|
/* Set Home Address to Source Address
|
|
* RFC 3775, 9.5.1 */
|
|
if ((pbuf->pktinfo.haMobilityBindingFlags & IPMIP6_MH_BU_P) == 0)
|
|
{
|
|
if (pbuf->ipmip6.recvinfo.home)
|
|
ipcom_memcpy(&pbuf->pktinfo.mip6BindingHomeAddress.ipv6, pbuf->ipmip6.recvinfo.home, sizeof(struct Ip_in6_addr));
|
|
else
|
|
ipcom_memcpy(&pbuf->pktinfo.mip6BindingHomeAddress.ipv6, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, sizeof(struct Ip_in6_addr));
|
|
|
|
/* RFC 3775, 9.5.1
|
|
* Home address must be unicast routable.
|
|
*/
|
|
if (!ipmip6_address_unicast_routable(&pbuf->pktinfo.mip6BindingHomeAddress.ipv6))
|
|
break;
|
|
}
|
|
|
|
/* Process any mobile options */
|
|
if (ipmip6_mh_pkt_process_options(&ctx->ipmip6, proc, &pbuf->ipmip6.mo, IPMIP_BUFFER_PTR(&pbuf->ipmip6.ipmip.packet), sizeof(Ipmip6_mh_binding_update_t), (int)IPMIP_BUFFER_SIZE(&pbuf->ipmip6.ipmip.packet)) == IP_FALSE)
|
|
{
|
|
/* Failure */
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Failed to process mobile options"));
|
|
break;
|
|
}
|
|
|
|
#ifdef IPMIP_USE_INET
|
|
if (pbuf->ipmip6.mo.dsmipv6_coap != IP_NULL)
|
|
{
|
|
/* DSMIPv6 */
|
|
pbuf->pktinfo.mip6BindingCOA.ipv4.s_addr = pbuf->ipmip6.mo.dsmipv6_coap->care_of_address;
|
|
pbuf->pktinfo.mip6BindingCOAType = IPMIP6_MIB_InetAddressType_ipv4;
|
|
|
|
/* */
|
|
if (pbuf->ipmip6.recvinfo.encapsulation && pbuf->ipmip6.recvinfo.encapsulation->sa.sa_family == IP_AF_INET)
|
|
{
|
|
pbuf->pktinfo.mip6BindingUDPEncapPort = pbuf->ipmip6.recvinfo.encapsulation->sin.sin_port;
|
|
if (pbuf->ipmip6.recvinfo.encapsulation->sin.sin_addr.s_addr != pbuf->pktinfo.mip6BindingCOA.ipv4.s_addr && pbuf->pktinfo.mip6BindingUDPEncapPort != 0)
|
|
{
|
|
/* NAT in effect */
|
|
pbuf->pktinfo.mip6BindingCOA.ipv4.s_addr = pbuf->ipmip6.recvinfo.encapsulation->sin.sin_addr.s_addr;
|
|
pbuf->pktinfo.mip6BindingUDPEncapsulate = IP_TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Failure */
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"No IPv4 encapsulation encountered"));
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
/* */
|
|
if (pbuf->ipmip6.recvinfo.encapsulation && pbuf->ipmip6.recvinfo.encapsulation->sa.sa_family != IP_AF_INET6)
|
|
{
|
|
/* Failure */
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"IPv4 encapsulation encountered, but no DSMIPv6 CoA option found"));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
|
|
/* RFC 3775. 9.5.1
|
|
* Care Of is Alternate Care-of address if specified.
|
|
* Otherwise Source Address as indicated by the packet */
|
|
if (pbuf->ipmip6.mo.acoap)
|
|
ipcom_memcpy(&pbuf->pktinfo.mip6BindingCOA.ipv6, &pbuf->ipmip6.mo.acoap->address, sizeof(struct Ip_in6_addr));
|
|
else if (pbuf->ipmip6.recvinfo.encapsulation)
|
|
ipcom_memcpy(&pbuf->pktinfo.mip6BindingCOA.ipv6, &pbuf->ipmip6.recvinfo.encapsulation->sin6.sin6_addr, sizeof(struct Ip_in6_addr));
|
|
else
|
|
ipcom_memcpy(&pbuf->pktinfo.mip6BindingCOA.ipv6, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, sizeof(struct Ip_in6_addr));
|
|
|
|
pbuf->pktinfo.mip6BindingCOAType = IPMIP6_MIB_InetAddressType_ipv6;
|
|
|
|
/* UDP tunneled v6 frame */
|
|
if (pbuf->ipmip6.recvinfo.encapsulation && pbuf->ipmip6.recvinfo.encapsulation->sin6.sin6_port != 0)
|
|
{
|
|
pbuf->pktinfo.mip6BindingUDPEncapsulate = IP_TRUE;
|
|
pbuf->pktinfo.mip6BindingUDPEncapPort = pbuf->ipmip6.recvinfo.encapsulation->sin6.sin6_port;
|
|
}
|
|
}
|
|
|
|
#ifdef IPMIP_USE_INET
|
|
if (pbuf->pktinfo.mip6BindingCOAType == IPMIP6_MIB_InetAddressType_ipv4)
|
|
{
|
|
if (IP_BIT_ISSET(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BU_F))
|
|
{
|
|
if (pbuf->pktinfo.mip6BindingUDPEncapsulate == IP_FALSE && pbuf->iff_transport.priv->AcceptForcedIPv4UDPEncapsulationRequest == IP_FALSE)
|
|
{
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_CANNOT_FORCE_UDP_ENCAPSULATION,
|
|
pbuf,
|
|
IP_NULL);
|
|
|
|
IPMIP6_EVENT_IF(WARNING, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"UDP encapsulation force attempt, but disabled by configuration"));
|
|
|
|
goto done_out;
|
|
}
|
|
|
|
/* Set encapsulation */
|
|
pbuf->pktinfo.mip6BindingUDPEncapsulate = IP_TRUE;
|
|
}
|
|
|
|
/* Do encapsulate */
|
|
if (pbuf->pktinfo.mip6BindingUDPEncapPort != 0 && pbuf->iff_transport.priv->ForceIPv4UDPEncapsulation)
|
|
pbuf->pktinfo.mip6BindingUDPEncapsulate = IP_TRUE;
|
|
}
|
|
|
|
#endif
|
|
if ((pbuf->pktinfo.haMobilityBindingFlags & IPMIP6_MH_BU_P) == 0)
|
|
{
|
|
Ipmip6ha_if_route_entry_t *se;
|
|
|
|
/**/
|
|
if (ipmip6_util_InetAddress_cmp(pbuf->pktinfo.mip6BindingCOAType, &pbuf->pktinfo.mip6BindingCOA, pbuf->pktinfo.mip6BindingHomeAddressType, &pbuf->pktinfo.mip6BindingHomeAddress) || pbuf->pktinfo.haMobilityBindingTimeGranted == 0)
|
|
{
|
|
IPCOM_LOG2(INFO,
|
|
"%s: Deregistration MH packet %s",
|
|
ctx->ipmip6.ipmip.name,
|
|
ipmip6_ntop_InetAddress(proc, pbuf->pktinfo.mip6BindingCOAType, &pbuf->pktinfo.mip6BindingCOA));
|
|
pbuf->pktinfo.haMobilityDeregistration = IP_TRUE;
|
|
}
|
|
|
|
|
|
ipcom_mutex_lock(ctx->prefixes_lock);
|
|
if (ipmip_route_lookup(ctx->prefixes, &pbuf->pktinfo.mip6BindingHomeAddress.ipv6, (Ipmip_route_t **)&se) == IPMIP_ROUTE_LOOKUP_MISS ||
|
|
(!pbuf->iff_home.priv->RelayBindings && pbuf->iff_home.priv != se->ifp))
|
|
{
|
|
ipcom_mutex_unlock(ctx->prefixes_lock);
|
|
|
|
IPMIP6_EVENT_IF(INFO, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Invalid home address %s requested on interface %s",
|
|
ipmip_ntop_inet6(proc, &pbuf->pktinfo.mip6BindingHomeAddress.ipv6, IP_NULL),
|
|
pbuf->iff_home.pub->iff.ifname));
|
|
|
|
/* 1. Check that the home address is OK */
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_NOT_HOME_SUBNET,
|
|
pbuf,
|
|
IP_NULL);
|
|
goto done_out;
|
|
}
|
|
else
|
|
{
|
|
Ipmip6ha_if_t *ifp_home = se->ifp;
|
|
|
|
pbuf->pktinfo.haMobilityPrefixTimestamp = se->timestamp;
|
|
pbuf->pktinfo.haMobilityPrefixValid = se->valid;
|
|
|
|
ipcom_mutex_unlock(ctx->prefixes_lock);
|
|
|
|
/**/
|
|
if (ifp_home != pbuf->iff_home.priv)
|
|
{
|
|
IPMIP6_EVENT_IF(DEBUG2, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Switching HoA %s to home interface %s from %s",
|
|
ipmip_ntop_inet6(proc, &pbuf->pktinfo.mip6BindingHomeAddress.ipv6, IP_NULL),
|
|
se->ifp->ipmip6.iff.ifname,
|
|
pbuf->iff_home.pub->iff.ifname));
|
|
pbuf->iff_home.priv = ifp_home;
|
|
}
|
|
|
|
IPMIP6_EVENT_IF(DEBUG2, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"MH Prefix Values %s, %x, %d, %d",
|
|
ipmip_ntop_tm(proc, se->timestamp),
|
|
se->timestamp,
|
|
se->valid,
|
|
se->valid - (pbuf->pktinfo.haBindingLastUpdated - se->timestamp)));
|
|
|
|
if (pbuf->iff_home.priv->lifetime != 0)
|
|
pbuf->pktinfo.haMobilityBindingTimeGranted = IP_MIN(pbuf->iff_home.priv->lifetime, pbuf->pktinfo.haMobilityBindingTimeGranted);
|
|
|
|
/* Lookup by NAI or HOA */
|
|
ipmip6ha_node_lookup(ctx,
|
|
proc,
|
|
pbuf->iff_home.priv,
|
|
&pbuf->pktinfo.mip6BindingHomeAddress.ipv6,
|
|
pbuf->ipmip6.mo.mn_naip,
|
|
&pbuf->ipmip6.ipmip.timeout,
|
|
(Ipmip_timer_func_t)ipmip6ha_mh_process_binding_update_H,
|
|
pbuf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pbuf->pktinfo.haMobilityBindingTimeGranted == 0)
|
|
{
|
|
IPCOM_LOG2(INFO,
|
|
"%s: Deregistration Proxy MH packet %s",
|
|
ctx->ipmip6.ipmip.name,
|
|
ipmip6_ntop_InetAddress(proc, pbuf->pktinfo.mip6BindingCOAType, &pbuf->pktinfo.mip6BindingCOA));
|
|
pbuf->pktinfo.haMobilityDeregistration = IP_TRUE;
|
|
}
|
|
|
|
/* */
|
|
if (pbuf->ipmip6.mo.mn_naip == IP_NULL)
|
|
{
|
|
/* RFC5213
|
|
5.3.1. Processing Proxy Binding Updates
|
|
4. The local mobility anchor MUST identify the mobile node from the
|
|
identifier present in the Mobile Node Identifier option
|
|
[RFC4283] of the Proxy Binding Update message. If the Mobile
|
|
Node Identifier option is not present in the Proxy Binding
|
|
Update message, the local mobility anchor MUST reject the
|
|
request and send a Proxy Binding Acknowledgement message with
|
|
Status field set to MISSING_MN_IDENTIFIER_OPTION (Missing Mobile
|
|
Node Identifier option) and the identifier in the Mobile Node
|
|
Identifier option carried in the message MUST be set to a zero
|
|
length identifier.
|
|
*/
|
|
|
|
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_MISSING_MN_IDENTIFIER_OPTION,
|
|
pbuf,
|
|
IP_NULL);
|
|
goto done_out;
|
|
}
|
|
|
|
if (pbuf->ipmip6.mo.pmipv6_home_prefix.numelem == 0
|
|
#ifdef IPMIP_USE_INET
|
|
&& pbuf->ipmip6.mo.dsmipv6_prefix_request.numelem == 0
|
|
#endif
|
|
)
|
|
{
|
|
/* RFC5213
|
|
5.3.1. Processing Proxy Binding Updates
|
|
10. If there is no Home Network Prefix option(s) (with any value)
|
|
present in the Proxy Binding Update message, the local mobility
|
|
anchor MUST reject the request and send a Proxy Binding
|
|
Acknowledgement message with the Status field set to
|
|
MISSING_HOME_NETWORK_PREFIX_OPTION (Missing Home Network Prefix
|
|
option).
|
|
*/
|
|
|
|
/* draft-ietf-netlmm-pmip6-ipv4-support-XX
|
|
3.1.2.1. Processing Proxy Binding Updates
|
|
o If there is an IPv4 Home Address option [ID-DSMIP6] present in the
|
|
received Proxy Binding Update message, but if there is no Home
|
|
Network Prefix option [RFC-5213] present in the request, the local
|
|
mobility anchor MUST NOT reject the request as specified in
|
|
Section 5.3.1 of [RFC-5213]. At least one instance of any of
|
|
these two options MUST be present. If there is not a single
|
|
instance of any of these two options present in the request, the
|
|
local mobility anchor MUST reject the request and send a Proxy
|
|
Binding Acknowledgement message with Status field set to
|
|
MISSING_HOME_NETWORK_PREFIX_OPTION (Missing mobile node's home
|
|
network prefix option) [RFC-5213].
|
|
*/
|
|
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_MISSING_HOME_NETWORK_PREFIX_OPTION,
|
|
pbuf,
|
|
IP_NULL);
|
|
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"No INET or INET6 prefix requests found in binding update"));
|
|
|
|
goto done_out;
|
|
}
|
|
|
|
|
|
if (pbuf->ipmip6.mo.pmipv6_handoff_indicator == IP_NULL)
|
|
{
|
|
/* RFC 5213; 5.3.1
|
|
11. If the Handoff Indicator option is not present in the Proxy
|
|
Binding Update message, the local mobility anchor MUST reject
|
|
the request and send a Proxy Binding Acknowledgement message
|
|
with the Status field set to MISSING_HANDOFF_INDICATOR_OPTION
|
|
(Missing Handoff Indicator option).
|
|
*/
|
|
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_MISSING_HANDOFF_INDICATOR_OPTION,
|
|
pbuf,
|
|
IP_NULL);
|
|
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Missing handoff indicator option"));
|
|
|
|
goto done_out;
|
|
}
|
|
|
|
if (pbuf->ipmip6.mo.pmipv6_handoff_indicator == IP_NULL)
|
|
{
|
|
/* RFC 5213; 5.3.1
|
|
12. If the Access Technology Type option is not present in the Proxy
|
|
Binding Update message, the local mobility anchor MUST reject
|
|
the request and send a Proxy Binding Acknowledgement message
|
|
with the Status field set to MISSING_ACCESS_TECH_TYPE_OPTION
|
|
(Missing Access Technology Type option).
|
|
*/
|
|
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_MISSING_ACCESS_TECH_TYPE_OPTION,
|
|
pbuf,
|
|
IP_NULL);
|
|
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Missing access type option"));
|
|
|
|
goto done_out;
|
|
}
|
|
|
|
#ifdef IPMIP_USE_INET
|
|
if (pbuf->ipmip6.mo.dsmipv6_prefix_request.numelem > 1)
|
|
{
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_MULTIPLE_IPV4_HOME_ADDRESS_ASSIGNMENT_NOT_SUPPORTED,
|
|
pbuf,
|
|
IP_NULL);
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Too many INET home address requests found in binding update (%d)", pbuf->ipmip6.mo.dsmipv6_prefix_request.numelem));
|
|
goto done_out;
|
|
}
|
|
else if (pbuf->ipmip6.mo.dsmipv6_prefix_request.numelem == 1)
|
|
{
|
|
Ipmip6_mh_mo_dsmipv6_ipv4_home_address_t *mp = (Ipmip6_mh_mo_dsmipv6_ipv4_home_address_t *)pbuf->ipmip6.mo.dsmipv6_prefix_request.hdrs[0];
|
|
Ip_u16 pflags = IP_GET_NTOHS(&mp->pref_flags);
|
|
|
|
|
|
if ((pflags & IPMIP6_MH_MO_DSMIPV6_IPV4_HOME_ADDRESS_P) != 0)
|
|
{
|
|
ipmip6ha_mh_send_binding_acknowledgement(ctx,
|
|
proc,
|
|
IPMIP6_MH_BA_STATUS_ERROR_IPV4_PREFIX_ASSIGNMENT_NOT_SUPPORTED,
|
|
pbuf,
|
|
IP_NULL);
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"INET prefix requested by PMIP, which is disallowed"));
|
|
goto done_out;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* */
|
|
if (pbuf->iff_home.priv->RelayBindings)
|
|
{
|
|
/* Do we need to relay? */
|
|
Ipmip6ha_relay_key_t key;
|
|
Ipmip6ha_relay_key_t *rkey;
|
|
Ipmip6_mh_mo_nai_t *nai = (Ipmip6_mh_mo_nai_t *)pbuf->ipmip6.mo.mn_naip;
|
|
|
|
key.nai = (char *)&nai->nai[0];
|
|
key.nai_len = (Ip_size_t) (nai->hdr.len - 1);
|
|
|
|
/* Discard trailing characters */
|
|
while (key.nai_len > 0 && key.nai[key.nai_len - 1] == '\0')
|
|
key.nai_len--;
|
|
|
|
rkey = ipcom_hash_get(ctx->relay_map, &key);
|
|
if (rkey == IP_NULL)
|
|
{
|
|
/* Find domain */
|
|
for (; key.nai_len > 0 && key.nai[0] != '@'; key.nai_len--, key.nai++)
|
|
;
|
|
|
|
if (key.nai_len)
|
|
rkey = ipcom_hash_get(ctx->relay_map, &key);
|
|
}
|
|
|
|
if (rkey != IP_NULL && rkey->ifp != pbuf->iff_home.priv)
|
|
{
|
|
IPMIP6_EVENT_IF(DEBUG2, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Switching NAI %s to home interface %s from %s",
|
|
rkey->nai,
|
|
rkey->ifp->ipmip6.iff.ifname,
|
|
pbuf->iff_home.pub->iff.ifname));
|
|
pbuf->iff_home.priv = rkey->ifp;
|
|
}
|
|
}
|
|
|
|
if (pbuf->iff_home.priv->lifetime != 0)
|
|
pbuf->pktinfo.haMobilityBindingTimeGranted = IP_MIN(pbuf->iff_home.priv->lifetime, pbuf->pktinfo.haMobilityBindingTimeGranted);
|
|
|
|
/* Lookup by NAI or HOA */
|
|
ipmip6ha_node_lookup(ctx,
|
|
proc,
|
|
pbuf->iff_home.priv,
|
|
IP_NULL,
|
|
pbuf->ipmip6.mo.mn_naip,
|
|
&pbuf->ipmip6.ipmip.timeout,
|
|
(Ipmip_timer_func_t)ipmip6ha_mh_process_binding_update_P,
|
|
pbuf);
|
|
}
|
|
|
|
|
|
}
|
|
return IP_TRUE;
|
|
case IPMIP6_MH_HEARTBEAT_MESSAGE:
|
|
if (sizeof(Ipmip6_mh_heartbeat_message_t) > mlen)
|
|
break;
|
|
|
|
/* Process any mobile options */
|
|
if (ipmip6_mh_pkt_process_options(&ctx->ipmip6, proc, &pbuf->ipmip6.mo, IPMIP_BUFFER_PTR(&pbuf->ipmip6.ipmip.packet), sizeof(Ipmip6_mh_heartbeat_message_t), (int)IPMIP_BUFFER_SIZE(&pbuf->ipmip6.ipmip.packet)) == IP_FALSE)
|
|
{
|
|
/* Failure */
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Failed to process mobile options"));
|
|
break;
|
|
}
|
|
|
|
ipmip6ha_mh_resolve_coa(pbuf);
|
|
return ipmip6ha_mag_heartbeat_input(ctx, proc, pbuf);
|
|
|
|
case IPMIP6_MH_BINDING_ERROR:
|
|
if (sizeof(Ipmip6_mh_binding_error_t) > mlen)
|
|
break;
|
|
else
|
|
{
|
|
Ipmip6_mh_binding_error_t *be = IPMIP_BUFFER_PTR(&pbuf->ipmip6.ipmip.packet);
|
|
if (be->be_status == IPMIP6_MH_BE_STATUS_ERROR_UNKNOWN_MH_TYPE)
|
|
{
|
|
Ipmip6ha_mag_t *mag = ipmip6ha_mag_get(ctx, proc, pbuf->iff_transport.priv, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, 0);
|
|
if (mag)
|
|
{
|
|
ipmip6ha_mag_binding_error_input(ctx, proc, pbuf, mag);
|
|
ipmip6ha_mag_release(ctx, proc, mag);
|
|
return IP_TRUE;
|
|
}
|
|
|
|
/* Must have been a H node sending a binding error; Try locate it */
|
|
}
|
|
}
|
|
|
|
/**/
|
|
ipmip_pktbuf_free(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip);
|
|
return IP_TRUE;
|
|
|
|
case IPMIP6_MH_BINDING_REVOCATION_MESSAGE:
|
|
|
|
if (sizeof(Ipmip6_mh_br_message_t) > mlen)
|
|
goto done_out;
|
|
else
|
|
{
|
|
Ipmip6_mh_br_message_t *br = (Ipmip6_mh_br_message_t *)IPMIP_BUFFER_PTR(&pbuf->ipmip6.ipmip.packet);
|
|
|
|
switch (br->br_type)
|
|
{
|
|
case IPMIP6_MH_BR_INDICATION:
|
|
{
|
|
Ipmip6_mh_bri_message_t *bri = (Ipmip6_mh_bri_message_t *)IPMIP_BUFFER_PTR(&pbuf->ipmip6.ipmip.packet);
|
|
|
|
/* Process any mobile options */
|
|
if (ipmip6_mh_pkt_process_options(&ctx->ipmip6, proc, &pbuf->ipmip6.mo, IPMIP_BUFFER_PTR(&pbuf->ipmip6.ipmip.packet), sizeof(Ipmip6_mh_bri_message_t), (int)IPMIP_BUFFER_SIZE(&pbuf->ipmip6.ipmip.packet)) == IP_FALSE)
|
|
{
|
|
/* Failure */
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Failed to process mobile options"));
|
|
break;
|
|
}
|
|
|
|
pbuf->pktinfo.haMobilityBindingFlags = IP_GET_NTOHS(&bri->bri_flags);
|
|
pbuf->pktinfo.haMobilityBindingSequenceNumber = IP_GET_NTOHS(&bri->bri_seq);
|
|
pbuf->pktinfo.haMobilityReason = bri->bri_trigger;
|
|
|
|
if (IP_BIT_ISFALSE(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BRI_FLAG_P))
|
|
{
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"P flag not set in LMA revocation"));
|
|
break;
|
|
}
|
|
|
|
ipmip6ha_mh_resolve_coa(pbuf);
|
|
|
|
if (pbuf->ipmip6.recvinfo.home)
|
|
ipcom_memcpy(&pbuf->pktinfo.mip6BindingHomeAddress.ipv6, pbuf->ipmip6.recvinfo.home, sizeof(struct Ip_in6_addr));
|
|
else
|
|
ipcom_memcpy(&pbuf->pktinfo.mip6BindingHomeAddress.ipv6, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, sizeof(struct Ip_in6_addr));
|
|
|
|
/**/
|
|
if (IP_BIT_ISSET(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BRI_FLAG_G))
|
|
{
|
|
switch (pbuf->pktinfo.haMobilityReason)
|
|
{
|
|
case IPMIP6_MH_BRI_TRIGGER_PER_PEER_POLICY:
|
|
{
|
|
Ipmip6ha_mag_t *mag;
|
|
|
|
/* Go through the interface context */
|
|
if (pbuf->ipmip6.mo.acoap != IP_NULL)
|
|
mag = ipmip6ha_mag_get(ctx, proc, pbuf->iff_transport.priv, &pbuf->ipmip6.mo.acoap->address, 0);
|
|
else
|
|
mag = ipmip6ha_mag_get(ctx, proc, pbuf->iff_transport.priv, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, 0);
|
|
|
|
if (mag == IP_NULL)
|
|
{
|
|
IPMIP6_EVENT_IF(INFO, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Binding Revocation Indication from MAG '%s' discarded since MAG not found in database",
|
|
ipmip_ntop_inet6(proc, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, IP_NULL)));
|
|
ipmip6ha_mh_bra_send(ctx, proc, pbuf, IPMIP6_MH_BRA_STATUS_GLOBAL_REVOCATION_NOT_AUTHORIZED);
|
|
}
|
|
else
|
|
{
|
|
/* Time to kill of all nodes with this MAG */
|
|
ipmip_context_id_lock(ctx->idents);
|
|
ipmip_context_id_foreach(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
ctx->idents,
|
|
(Ipmip_util_hash_foreach_t)ipmip6ha_mh_global_binding_revocation,
|
|
mag,
|
|
IP_NULL);
|
|
|
|
ipmip6ha_mh_bra_send(ctx, proc, pbuf, IPMIP6_MH_BRA_STATUS_SUCCESS);
|
|
|
|
ipmip_context_id_unlock(ctx->idents);
|
|
ipmip6ha_mag_release(ctx, proc, mag);
|
|
}
|
|
}
|
|
|
|
break;
|
|
default:
|
|
IPMIP6_EVENT_IF(INFO, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Binding Revocation Indication from MAG '%s' discarded trigger was unknown (%d)",
|
|
ipmip_ntop_inet6(proc, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, IP_NULL),
|
|
pbuf->pktinfo.haMobilityReason));
|
|
ipmip6ha_mh_bra_send(ctx, proc, pbuf, IPMIP6_MH_BRA_STATUS_REVOCATION_TRIGGER_NOT_SUPPORTED);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pbuf->ipmip6.mo.mn_naip == IP_NULL)
|
|
{
|
|
IPMIP6_EVENT_IF(ERR, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Network Access Identifier missing in Acknowledgement"));
|
|
break;
|
|
}
|
|
|
|
if (pbuf->iff_home.priv->RelayBindings)
|
|
{
|
|
/* Do we need to relay? */
|
|
Ipmip6ha_relay_key_t key;
|
|
Ipmip6ha_relay_key_t *rkey;
|
|
Ipmip6_mh_mo_nai_t *nai = (Ipmip6_mh_mo_nai_t *)pbuf->ipmip6.mo.mn_naip;
|
|
|
|
key.nai = (char *)&nai->nai[0];
|
|
key.nai_len = (Ip_size_t) (nai->hdr.len - 1);
|
|
|
|
/* Discard trailing characters */
|
|
while (key.nai_len > 0 && key.nai[key.nai_len - 1] == '\0')
|
|
key.nai_len--;
|
|
|
|
rkey = ipcom_hash_get(ctx->relay_map, &key);
|
|
if (rkey == IP_NULL)
|
|
{
|
|
/* Find domain */
|
|
for (; key.nai_len > 0 && key.nai[0] != '@'; key.nai_len--, key.nai++)
|
|
;
|
|
|
|
if (key.nai_len)
|
|
rkey = ipcom_hash_get(ctx->relay_map, &key);
|
|
}
|
|
|
|
if (rkey != IP_NULL && rkey->ifp != pbuf->iff_home.priv)
|
|
{
|
|
IPMIP6_EVENT_IF(DEBUG2, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Switching NAI %s to home interface %s from %s",
|
|
rkey->nai,
|
|
rkey->ifp->ipmip6.iff.ifname,
|
|
pbuf->iff_home.pub->iff.ifname));
|
|
pbuf->iff_home.priv = rkey->ifp;
|
|
}
|
|
}
|
|
|
|
/* Lookup by NAI */
|
|
ipmip6ha_node_lookup(ctx,
|
|
proc,
|
|
pbuf->iff_home.priv,
|
|
IP_NULL,
|
|
pbuf->ipmip6.mo.mn_naip,
|
|
&pbuf->ipmip6.ipmip.timeout,
|
|
(Ipmip_timer_func_t)ipmip6ha_mh_process_binding_revocation_P,
|
|
pbuf);
|
|
return IP_TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case IPMIP6_MH_BR_ACKNOWLEDGEMENT:
|
|
/**/
|
|
{
|
|
Ipmip6_mh_bra_message_t *bra = (Ipmip6_mh_bra_message_t *)br;
|
|
Ipmip6ha_br_t *bri;
|
|
|
|
|
|
ipmip_context_id_lock(ctx->bri);
|
|
|
|
bri = ipmip_context_id_get(ctx->bri, IP_GET_HTONS(&bra->bra_seq));
|
|
if (bri != IP_NULL)
|
|
{
|
|
ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &bri->retransmit);
|
|
ipmip_timer_request(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
&bri->retransmit,
|
|
bri->lock,
|
|
bri->reply,
|
|
bri,
|
|
pbuf,
|
|
0);
|
|
}
|
|
ipmip_context_id_unlock(ctx->bri);
|
|
|
|
/* Found? */
|
|
if (bri == IP_NULL)
|
|
{
|
|
IPMIP6_EVENT_IF(DEBUG2, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"Unmatched Binding Revocation Acknowledgement received"));
|
|
ipmip_pktbuf_free(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip);
|
|
}
|
|
|
|
return IP_TRUE;
|
|
}
|
|
default:
|
|
goto unknown_mh;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
unknown_mh:
|
|
ipmip6ha_mh_resolve_coa(pbuf);
|
|
ipmip6ha_route_update_temporary(ctx, proc, pbuf, IP_TRUE);
|
|
return IP_FALSE;
|
|
|
|
}
|
|
|
|
done_out:
|
|
IPCOM_LOG1(INFO,
|
|
"%s: Discarding MH packet",
|
|
ctx->ipmip6.ipmip.name);
|
|
/* Unknown type; free it */
|
|
ipmip_pktbuf_free(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip);
|
|
return IP_TRUE;
|
|
}
|
|
|
|
|
|
IP_GLOBAL Ip_bool
|
|
ipmip6ha_mh_bri_send(struct Ipmip6ha_context *ctx,
|
|
struct Ipmip_proc *proc,
|
|
struct Ipmip6ha_node *node,
|
|
struct Ipmip6ha_br *br)
|
|
{
|
|
Ipmip6_mh_bri_message_t *bri;
|
|
int err = 0;
|
|
Ipmip6_pktbuf_t *pkt;
|
|
|
|
|
|
/* Grab a packet */
|
|
pkt = (Ipmip6_pktbuf_t *) ipmip6_mh_pkt_alloc(&ctx->ipmip6,
|
|
proc,
|
|
sizeof(Ipmip6_mh_bri_message_t),
|
|
IPMIP6_MH_BINDING_REVOCATION_MESSAGE);
|
|
|
|
/* Out of memory? */
|
|
if (pkt == IP_NULL)
|
|
return IP_FALSE;
|
|
|
|
bri = (Ipmip6_mh_bri_message_t *) IPMIP_BUFFER_PTR(&pkt->ipmip.packet);
|
|
|
|
bri->bri_type = IPMIP6_MH_BR_INDICATION;
|
|
bri->bri_trigger = br->reason;
|
|
IP_SET_HTONS(&bri->bri_flags, br->flags);
|
|
IP_SET_HTONS(&bri->bri_seq, br->sequence);
|
|
|
|
if (node != IP_NULL)
|
|
{
|
|
#ifdef IPMIP_USE_INET
|
|
if (br->inet == IP_TRUE)
|
|
{
|
|
Ipmip6_mh_mo_dsmipv6_ipv4_home_address_t hoa;
|
|
Ipmip6ha_nemo_dsmipv6_t *nemo;
|
|
|
|
/* Init header values */
|
|
hoa.hdr.val = IPMIP6_MH_MO_DSMIPV6_IPV4_HOME_ADDRESS;
|
|
hoa.hdr.len = 6;
|
|
|
|
hoa.pref_flags = ip_htons((32 << IPMIP6_MH_MO_DSMIPV6_IPV4_HOME_ADDRESS_PREF));
|
|
|
|
for (nemo = IPCOM_LIST_FIRST(&node->nemo_hoa_v4); nemo != IP_NULL; nemo = IPCOM_LIST_NEXT(&nemo->next))
|
|
{
|
|
hoa.home_address = nemo->prefix;
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&hoa.hdr);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Add the NAI */
|
|
if (node->nai_mn)
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
node->nai_mn);
|
|
|
|
|
|
if (IPMIP6_MH_BU_IS_P(node->regflags))
|
|
{
|
|
if (br->mag != IP_NULL)
|
|
{
|
|
/**/
|
|
Ipmip6ha_nemo_t *nemo;
|
|
nemo = IPCOM_LIST_FIRST(&node->nemo_v6);
|
|
|
|
if (nemo != IP_NULL)
|
|
{
|
|
Ipmip6_mh_mo_pmipv6_home_prefix_t hp;
|
|
|
|
hp.hdr.val = IPMIP6_MH_MO_PMIPV6_HOME_NETWORK_PREFIX_OPTION;
|
|
hp.hdr.len = 18;
|
|
hp.reserved = 0;
|
|
|
|
ipcom_memcpy(&hp.prefix, &nemo->prefix, sizeof(nemo->prefix));
|
|
hp.prefix_length = nemo->len;
|
|
|
|
ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
&hp.hdr);
|
|
}
|
|
|
|
ipmip6ha_route_direct_temporary(ctx,
|
|
proc,
|
|
br->mag->ifp,
|
|
&br->mag->endpoint,
|
|
&br->mag->tunnel_endpoint,
|
|
br->mag->tunnel_endpoint_type,
|
|
br->mag->port);
|
|
|
|
/* Force it to go out using the CoA */
|
|
ipmip6_pkt_pktinfo_src_opt_add(pkt, &node->ifp_transport->ifaddr.prefix);
|
|
|
|
/* Send */
|
|
err = ipmip6_pkt_send(&ctx->ipmip6, proc, ctx->ipmip6.pkt->fd_mh, pkt, &br->mag->endpoint);
|
|
}
|
|
else
|
|
{
|
|
/**/
|
|
err = IP_ERRNO_ENOTCONN;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ipmip6ha_route_rthdr_temporary(ctx,
|
|
proc,
|
|
node->ifp_transport,
|
|
&node->mip6BindingCacheEntry.mip6BindingHomeAddress.ipv6,
|
|
&node->registered.mip6BindingCOA,
|
|
node->registered.mip6BindingCOAType,
|
|
(Ip_u16) (node->registered.mip6BindingUDPEncapType & 0xFFFF));
|
|
|
|
/* Force it to go out using the CoA */
|
|
ipmip6_pkt_pktinfo_src_opt_add(pkt, &node->ifp_transport->ifaddr.prefix);
|
|
|
|
/* Send */
|
|
err = ipmip6_pkt_send(&ctx->ipmip6, proc, ctx->ipmip6.pkt->fd_mh, pkt, &node->mip6BindingCacheEntry.mip6BindingHomeAddress.ipv6);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Global revocation */
|
|
if (br->match)
|
|
{
|
|
/* Add NAI */
|
|
ipmip6_mh_pkt_opt_add_nai(&ctx->ipmip6,
|
|
proc,
|
|
pkt,
|
|
br->match,
|
|
br->match_len);
|
|
}
|
|
|
|
ipmip6ha_route_direct_temporary(ctx,
|
|
proc,
|
|
br->mag->ifp,
|
|
&br->mag->endpoint,
|
|
&br->mag->tunnel_endpoint,
|
|
br->mag->tunnel_endpoint_type,
|
|
br->mag->port);
|
|
|
|
|
|
/* Force it to go out using the CoA */
|
|
ipmip6_pkt_pktinfo_src_opt_add(pkt, &br->mag->ifp->ifaddr.prefix);
|
|
|
|
/* Send */
|
|
err = ipmip6_pkt_send(&ctx->ipmip6, proc, ctx->ipmip6.pkt->fd_mh, pkt, &br->mag->endpoint);
|
|
}
|
|
|
|
if (err != 0)
|
|
{
|
|
IPCOM_LOG2(INFO,
|
|
"%s: Error sending MH Binding Revocation Indication = %x",
|
|
ctx->ipmip6.ipmip.name,
|
|
err);
|
|
return IP_FALSE;
|
|
}
|
|
|
|
return IP_TRUE;
|
|
}
|
|
|
|
IP_GLOBAL Ip_bool
|
|
ipmip6ha_mh_bra_send(Ipmip6ha_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip6ha_pktbuf_t *pbuf,
|
|
Ip_u8 status)
|
|
{
|
|
Ipmip6_mh_bra_message_t *bra;
|
|
int err;
|
|
Ipmip6_pktbuf_t *pkt;
|
|
Ip_u16 flags = 0;
|
|
struct Ip_in6_addr *dst;
|
|
|
|
|
|
/* Grab a packet */
|
|
pkt = (Ipmip6_pktbuf_t *) ipmip6_mh_pkt_alloc(&ctx->ipmip6,
|
|
proc,
|
|
sizeof(Ipmip6_mh_bra_message_t),
|
|
IPMIP6_MH_BINDING_REVOCATION_MESSAGE);
|
|
|
|
/* Out of memory? */
|
|
if (pkt == IP_NULL)
|
|
return IP_FALSE;
|
|
|
|
bra = (Ipmip6_mh_bra_message_t *) IPMIP_BUFFER_PTR(&pkt->ipmip.packet);
|
|
|
|
bra->bra_type = IPMIP6_MH_BR_ACKNOWLEDGEMENT;
|
|
bra->bra_status = status;
|
|
IP_SET_HTONS(&bra->bra_seq, pbuf->pktinfo.haMobilityBindingSequenceNumber);
|
|
|
|
if (IP_BIT_ISSET(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BRI_FLAG_V))
|
|
flags |= IPMIP6_MH_BRA_FLAG_V;
|
|
if (IP_BIT_ISSET(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BRI_FLAG_G))
|
|
flags |= IPMIP6_MH_BRA_FLAG_G;
|
|
if (IP_BIT_ISSET(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BRI_FLAG_P))
|
|
flags |= IPMIP6_MH_BRA_FLAG_P;
|
|
|
|
IP_SET_HTONS(&bra->bra_flags, flags);
|
|
|
|
/**/
|
|
ipmip6_pkt_pktinfo_src_opt_add(pkt, &pbuf->iff_transport.priv->ifaddr.prefix);
|
|
|
|
if (IP_BIT_ISSET(pbuf->pktinfo.haMobilityBindingFlags, IPMIP6_MH_BRI_FLAG_P))
|
|
{
|
|
ipmip6ha_route_update_temporary(ctx, proc, pbuf, IP_TRUE);
|
|
dst = &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr;
|
|
}
|
|
else
|
|
{
|
|
ipmip6ha_route_update_temporary(ctx, proc, pbuf, IP_FALSE);
|
|
dst = &pbuf->pktinfo.mip6BindingHomeAddress.ipv6;
|
|
}
|
|
|
|
/* Route */
|
|
if ((err = ipmip6_pkt_send(&ctx->ipmip6, proc, ctx->ipmip6.pkt->fd_mh, pkt, dst)) != 0)
|
|
{
|
|
IPMIP6_EVENT_IF(INFO, proc,
|
|
ipmip_log_event_add(&ctx->ipmip6.ipmip,
|
|
proc,
|
|
"%Failed to transmit binding revocation acknowledgement = %d",
|
|
err));
|
|
return IP_FALSE;
|
|
}
|
|
return IP_TRUE;
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|