Files
m241Neelima/components/ip_net2-6.9/ipmip6ha/src/ipmip6ha_mh.c
T
2026-07-30 11:19:44 +05:30

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(&timestamp.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,
&timestamp.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, &timestamp);
if (pbuf->iff_home.priv->MobileNodeGeneratedTimestampInUse == IP_FALSE)
{
Ipmip6_util_timediff_t ndiff;
ipmip6_util_timestamp_diff_tv(IP_NULL, &timestamp, &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(&timestamp, &((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
****************************************************************************
*/