/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipmip6ha_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 **************************************************************************** */