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

2417 lines
88 KiB
C

/*
******************************************************************************
* INTERPEAK SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipmipmn_if.c,v $ $Revision: 1.33.10.2 $
* $Source: /home/interpeak/CVSRoot/ipmipmn/src/ipmipmn_if.c,v $
* $Author: jsheng $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Joel Hortelius <joel.hortelis@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#include "ipmipmn_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_clib.h>
#include <ipcom_err.h>
#include <ipcom_os.h>
#include <ipcom_sock.h>
#include <ipcom_inet.h>
#include <ipcom_syslog.h>
#include <ipcom_ipd.h>
#include <ipcom_sock.h>
#include <ipcom_sock2.h>
#include <ipcom_list.h>
#include <ipcom_netif.h> /* for IP_IFT_ETHER */
#include <ipcom_shell.h>
#include <ipnet_routesock.h>
#include <ipnet_eth.h>
#ifdef IPIPSEC2
#include <ipipsec.h>
#include <ipipsec_pfkeyv2.h>
#endif
#ifdef IPIKE
#include <ipike.h>
#endif
#include <ipmip.h>
#include <private/ipmip_timer.h>
#include <private/ipmip_sysvar.h>
#include <ipmip4.h>
#include <private/ipmip4_context.h>
#include <private/ipmip4_auth.h>
#include <private/ipmip4_lla.h>
#include <private/ipmip4_util.h>
#include <private/ipmip4_sysext.h>
#include <private/ipmip4_if.h>
#include <private/ipmip4_pkt.h>
#include <private/ipmip4_usr.h>
#include <private/ipmip4_netlink.h>
#include "ipmipmn.h"
#include "ipmipmn_h.h"
#include "ipmipmn_if.h"
#include "ipmipmn_pktbuf.h"
#include "ipmipmn_route.h"
#include "ipmipmn_node.h"
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
typedef struct Ipmipmn_if_sainfo
{
Ip_bool tunnel;
Ip_u32 srcaddr;
} Ipmipmn_if_sainfo_t;
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipmipmn_if_coa_reference
*===========================================================================
* Description: Reference the CoA
* Parameters: data - Context
* proc - Process
* coa - CoA
* Returns: The CoA
*
*/
IP_STATIC void
ipmipmn_if_coa_reference_iflocked(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_api_coa_t *coa,
Ip_bool sysref,
const char *reason)
{
IPMIP_UNUSED_PARAMETER(data);
IPMIP_UNUSED_PARAMETER(proc);
if (!sysref || !coa->sysref)
{
if (sysref)
coa->sysref = IP_TRUE;
coa->refcnt++;
IPMIP4_EVENT_IF(DEBUG2, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA=%s upped refcount (%s) to %d (%s)",
coa->name,
sysref? "system" : "normal",
coa->refcnt,
reason));
}
}
/*
*===========================================================================
* ipmipmn_if_coa_dereference_iflocked
*===========================================================================
* Description: Reference the CoA
* Parameters: data - Context
* proc - Process
* coa - CoA
* Returns: The CoA
*
*/
IP_STATIC Ip_bool
ipmipmn_if_coa_dereference_iflocked(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_api_coa_t *coa,
Ip_bool sysref,
const char *reason)
{
IPMIP_UNUSED_PARAMETER(data);
IPMIP_UNUSED_PARAMETER(proc);
if (!sysref || coa->sysref)
{
if (sysref)
coa->sysref = IP_FALSE;
coa->refcnt--;
IPMIP4_EVENT_IF(DEBUG2, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA=%s downed refcount (%s) to %d (%s)",
coa->name,
sysref? "system" : "normal",
coa->refcnt,
reason));
return coa->refcnt == 0;
}
return IP_FALSE;
}
/*
*===========================================================================
* ipmipmn_if_notify_iflocked
*===========================================================================
* Description: Schedule callbacks.
* Parameters: data - Context
* proc - Process
* ifp - Interface
* Returns:
*
*/
IP_STATIC void
ipmipmn_if_notify_iflocked(Ipmipmn_context_t *data,
Ipmip_proc_t *proc,
Ipmipmn_iff_t *ifp)
{
Ipmipmn_iff_notify_t *notifier;
for (ifp = IPCOM_LIST_FIRST(&data->ifhead); ifp != IP_NULL; ifp = IPCOM_LIST_NEXT(&ifp->iff.ipmip.next))
{
for (notifier = IPCOM_LIST_FIRST(&ifp->notifiers); notifier != IP_NULL; notifier = IPCOM_LIST_NEXT(&notifier->if_next))
{
ipmip_timer_cancel(&data->api.ipmip4.ipmip, proc, &notifier->timer);
ipmip_timer_request(&data->api.ipmip4.ipmip,
proc,
&notifier->timer,
notifier->lock,
notifier->func,
notifier->obj,
ifp,
0);
}
}
}
/*
*===========================================================================
* ipmipmn_if_ifstate_iflocked
*===========================================================================
* Description: Verify interface state
* Parameters: data - Context
* proc - Process
* ifp - The interface
* Returns:
*
*/
IP_STATIC void
ipmipmn_if_ifstate_iflocked(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_iff_t *ifp)
{
Ip_bool down = ifp->admin_down || ifp->link_down;
if (down != ifp->down)
{
IPMIP4_EVENT_IF(INFO, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Interface %s is administratively brought %s",
ifp->iff.ipmip.ifname, down? "down" : "up"));
ifp->down = down;
if (ifp->down)
ifp->sync_count++;
ipmipmn_if_notify_iflocked(data, proc, ifp);
}
}
/*
*===========================================================================
* ipmipmn_route_destroy_tunnel
*===========================================================================
* Description: Destroy a particular tunnel.
* Parameters: tunnel - The tunnel to destroy.
* Returns:
*/
IP_STATIC void
ipmipmn_if_destroy(Ipmipmn_context_t *data,
Ipmip_proc_t *proc,
Ipmipmn_iff_t *iff)
{
/* No tunnel, don't bother */
if (iff->iff.ipmip.ifindex == 0)
return;
IPMIP_UNUSED_PARAMETER(data);
IPMIP_UNUSED_PARAMETER(proc);
}
/*
*===========================================================================
* ipmipmn_if_get_iflocked
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ipmipmn_iff_t *
ipmipmn_if_get_iflocked(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
const char *name)
{
/* Go through the interface list */
Ipmipmn_iff_t *ifp = (Ipmipmn_iff_t *)ipmip_if_findbyname(&data->ifhead, (char *)name);
IPMIP_UNUSED_PARAMETER(proc);
if (ifp != IP_NULL)
ifp->references++;
return ifp;
}
IP_STATIC Ipmipmn_iff_t *
ipmipmn_if_getbyindex_iflocked(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
int ifindex)
{
Ipmipmn_iff_t *ifp;
IPMIP_UNUSED_PARAMETER(proc);
/* Go through the interface list */
ifp = (Ipmipmn_iff_t *)ipmip_if_findbyindex(&data->ifhead, ifindex);
if (ifp != IP_NULL)
ifp->references++;
return ifp;
}
/*
*===========================================================================
* ipmipmn_if_coa_put_iflocked
*===========================================================================
* Description: Return a previously retrieved CoA
* Parameters: data - Context
* proc - Process
* ifp - Interface
* coa - CoA
* Returns: IP_TRUE if the CoA was deleted.
*
*/
IP_STATIC Ip_bool
ipmipmn_if_coa_put_iflocked(Ipmipmn_context_t *data,
Ipmip_proc_t *proc,
Ipmipmn_iff_t *ifp,
Ipmipmn_api_coa_t *coa,
Ip_bool hide,
Ip_bool system,
const char *reason)
{
Ip_bool dele = IP_FALSE;
if (ipmipmn_if_coa_dereference_iflocked(data, proc, coa, system, reason))
{
dele = IP_TRUE;
if (ipmipmn_if_coa_valid(coa))
ipcom_list_remove(&coa->next);
ifp->allocated--;
IPMIP4_EVENT_IF(DEBUG, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA '%s' removed from interface '%s' (%d)",
coa->name,
ifp->iff.ipmip.ifname,
ifp->allocated));
switch (coa->type)
{
case IPMIPMN_ATTR_COA_CCOA:
/* FALL THROUGH */
case IPMIPMN_ATTR_COA_SCOA_TIA:
case IPMIPMN_ATTR_COA_SCOA_PROTECTED:
break;
case IPMIPMN_ATTR_COA_FA:
case IPMIPMN_ATTR_COA_FA_LLH:
ipmip_timer_cancel(&data->api.ipmip4.ipmip, proc, &coa->params.fa.timer);
if (coa->params.fa.lladdr)
ipcom_free(coa->params.fa.lladdr);
if (coa->params.fa.lladdr_llh)
ipcom_free(coa->params.fa.lladdr_llh);
if (coa->params.fa.challenge)
ipcom_free(coa->params.fa.challenge);
break;
case IPMIPMN_ATTR_COA_HA:
ipmip_timer_cancel(&data->api.ipmip4.ipmip, proc, &coa->params.ha.timer);
if (coa->params.ha.lladdr)
ipcom_free(coa->params.ha.lladdr);
break;
}
ipcom_free(coa->name);
ipcom_free(coa);
}
else if (hide)
{
if (ipmipmn_if_coa_valid(coa))
ipcom_list_remove(&coa->next);
}
return dele;
}
IP_STATIC int
ipmipmn_if_agent_timeout_iflocked(int event,
Ipmipmn_context_t *data,
Ipmip_proc_t *proc,
Ipmipmn_api_coa_t *agent,
Ipmipmn_iff_t *ifp)
{
/**/
if (event == IPMIP_TIMER_EVENT_RUN)
{
IPMIP4_EVENT_IF(INFO, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA '%s' timed out",
agent->name));
ipmipmn_if_coa_put_iflocked(data, proc, ifp, agent, IP_TRUE, IP_TRUE, "CoA Timed Out");
ipmipmn_if_notify_iflocked(data, proc, ifp);
}
return IPMIP_TIMER_RET_DONE;
}
IP_STATIC Ip_bool
ipmipmn_if_put_iflocked(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_iff_t *ifp)
{
if (--ifp->references == 0)
{
Ipmipmn_api_coa_t *coa;
for (coa = IPCOM_LIST_FIRST(&ifp->coa); coa != IP_NULL;)
{
Ipmipmn_api_coa_t *ncoa = IPCOM_LIST_NEXT(&coa->next);
ipmipmn_if_coa_put_iflocked(data, proc, ifp, coa, IP_FALSE, IP_TRUE, "Interface removed");
coa = ncoa;
}
/* MUST BE EMPTY, OR WE'LL HAVE DANGLING REFERENCES */
ip_assert(ifp->coa.size == 0);
ip_assert(ifp->allocated == 0);
data->ifalloc--;
IPMIP4_EVENT_IF(DEBUG, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Interface '%s' removed (%d)",
ifp->iff.ipmip.
ifname, data->ifalloc));
/* Free the interface */
ipmip_if_free(&data->api.ipmip4.ipmip, proc, &ifp->iff.ipmip);
return IP_TRUE;
}
return IP_FALSE;
}
#ifdef IPIPSEC2
IP_STATIC Ip_bool
ipmipmn_if_get_sainfo(Ipipsec_sa_handle *sa,
Ipmipmn_if_sainfo_t *sainfo)
{
Ip_tag tags[7];
int retval;
/* Get additional information. */
tags[0] = IPIPSEC_SACTRLT_GET_Encapsulation;
tags[1] = 0;
tags[2] = IPIPSEC_SACTRLT_GET_SrcAddr;
tags[3] = 0;
tags[4] = IP_TAG_END;
retval = ipipsec_sactrl(sa, tags);
if (retval != IPCOM_SUCCESS)
{
ipcom_printf("ipsecctrl: ipipsec_sactrl(info) failed, error = %d"IP_LF, retval);
return IP_FALSE;
}
sainfo->tunnel = tags[1] == 1;
sainfo->srcaddr = tags[3];
return IP_TRUE;
}
IP_STATIC Ip_bool
ipmipmn_if_secure_verify_spd(Ipmipmn_context_t *data,
Ipmip_proc_t *proc,
Ipipsec_ctrl_selector *sel,
int dir)
{
const char *dstr = dir == IPIPSEC_SADIR_INPUT? "inbound" : "outbound";
/* Only interested in INET */
if (sel->domain != IP_AF_INET)
return IP_FALSE;
/* Only parse inbound to start with */
if (sel->direction != dir)
return IP_FALSE;
/* Require a SA */
if (sel->sa.domain != IP_AF_INET)
return IP_FALSE;
/* We got a selector; MUST BE WILDCARDED - NO PORTS or PROTOCOLS */
if (sel->LocalPort || sel->RemotePort || sel->Protocol)
{
IPMIP4_EVENT_IF(DEBUG, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Discarding %s selector due to specified protocol or ports",
dstr));
return IP_FALSE;
}
return IP_TRUE;
}
IP_STATIC Ip_bool
ipmipmn_if_secure_isbound(Ipmip_buffer_t *reply,
Ip_u32 addr)
{
struct Ip_nlmsghdr *nlh = IPMIP_BUFFER_PTR(reply);
Ip_size_t nlh_size = IPMIP_BUFFER_SIZE(reply);
/* Verify that the address exists */
while (IP_NLMSG_OK(nlh, nlh_size))
{
/**/
if (nlh->nlmsg_type == IP_RTM_NEWADDR)
{
struct Ip_ifaddrmsg *ifa = IP_NLMSG_DATA(nlh);
struct Ip_rtattr *ifap[IP_IFA_MAX + 1];
/* Resolve this entry; I want the prefix length and the address */
ipmip_netlink_nlmsghdr_rta_parse(IP_IFA_MAX + 1,
IP_IFA_RTA(ifa),
IP_IFA_PAYLOAD(nlh),
ifap);
/* Found one */
if (ifap[IP_IFA_LOCAL] && addr == IP_GET_32ON8(IP_RTA_DATA(ifap[IP_IFA_LOCAL])))
return IP_TRUE;
}
/**/
nlh = IP_NLMSG_NEXT(nlh, nlh_size);
}
return IP_FALSE;
}
#ifdef IPIKE
IP_STATIC void
ipmipmn_if_ike_move(Ipmipmn_context_t *data,
Ipmip_proc_t *proc,
Ipmipmn_iff_t *ifp,
Ipmipmn_api_coa_t *coa,
Ip_u32 addr)
{
IPMIP4_EVENT_IF(DEBUG, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Trying to move IKE endpoint %s -> %s",
ipmip_ntop_inet(proc, addr),
ipmip_ntop_inet(proc, coa->addr)));
IPMIP_UNUSED_PARAMETER(data);
IPMIP_UNUSED_PARAMETER(ifp);
/* Is it started? */
if (ipcom_ipd_send("ipike", IPCOM_IPD_MSGTYPE_PING) == IPCOM_SUCCESS)
{
Ipmip_buffer_t buf;
Ipcom_ipd_msg hdr;
char *argbuf;
ipmip_buffer_init(&buf);
ipcom_memset(&hdr, 0, sizeof(hdr));
hdr.msgtype = IPIKE_CMD_MOBIKE;
ipmip_buffer_append(&buf, &hdr, sizeof(hdr));
argbuf = ipmip_util_strdup("%s %s", ipmip_ntop_inet(proc, addr), ipmip_ntop_inet(proc, coa->addr));
if (argbuf)
{
ipmip_buffer_append(&buf, argbuf, ipcom_strlen(argbuf) + 1);
if (ipcom_ipd_sendmsg("ipike", IPMIP_BUFFER_PTR(&buf), IPMIP_BUFFER_SIZE(&buf)) == IPCOM_SUCCESS)
{
IPMIP4_EVENT_IF(DEBUG, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Trying to move IKE endpoint %s -- success",
argbuf));
}
else
{
IPMIP4_EVENT_IF(ERR, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Trying to move IKE endpoint %s -- failed",
argbuf));
}
ipcom_free(argbuf);
}
ipmip_buffer_release(&buf);
}
else
{
IPMIP4_EVENT_IF(ERR, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"IKE not started"));
}
}
#endif /* IPIKE */
#endif
IP_STATIC int
ipmipmn_if_coa_type(Ipmipmn_context_t *data,
Ipmip_proc_t *proc,
Ipmip_buffer_t *reply,
Ip_u32 addr,
Ipmipmn_api_coa_params_t *cparam)
{
#ifdef IPIPSEC2
Ip_err retval;
Ipipsec_ctrl_selector isel;
Ipipsec_ctrl_selector osel;
Ipmipmn_if_sainfo_t sainfo;
isel.Index = 0; /* get first selector. */
for(;;)
{
Ip_bool tia = IP_TRUE;
/* Get first/next Selector */
retval = ipmip_util_ioctl(&data->api.ipmip4.ipmip, proc, IP_SIOCXIPSEC_SELECTOR, &isel);
if(retval != IPCOM_SUCCESS)
{
IPMIP4_EVENT_IF(ERR, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA Type/IPIPSEC: Selector print aborted: error = %d", retval));
goto bail_out;
}
/* */
if(isel.Index == 0)
break;
/* Do verify the SPD */
if (!ipmipmn_if_secure_verify_spd(data, proc, &isel, IPIPSEC_SADIR_INPUT))
continue;
if (addr != isel.RemoteId.in.s_addr)
{
IPMIP4_EVENT_IF(DEBUG2, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA Type/IPIPSEC: Non-TIA address due to mismatching addresses (%s != %s)",
ipmip_ntop_inet(proc, addr),
ipmip_ntop_inet(proc, isel.RemoteId.in.s_addr)));
tia = IP_FALSE;
}
if (tia && isel.remote_mask.in.s_addr != 0xffffffff)
{
IPMIP4_EVENT_IF(DEBUG2, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA Type/IPIPSEC: Non-TIA to non-explicit host mask (%s)",
ipmip_ntop_inet(proc, isel.remote_mask.in.s_addr)));
tia = IP_FALSE;
}
/* Now; We got an inbound selector */
/* Must be protected by a different address */
if (tia && isel.RemoteId.in.s_addr == isel.sa.dst.in.s_addr)
{
IPMIP4_EVENT_IF(DEBUG2, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA Type/IPIPSEC: Non-TIA due to sa destination and remote destination are equal (%s)",
ipmip_ntop_inet(proc, isel.sa.dst.in.s_addr)));
tia = IP_FALSE;
}
if (!tia)
{
/* Match against mask */
if ((addr & isel.remote_mask.in.s_addr) != (isel.RemoteId.in.s_addr & isel.remote_mask.in.s_addr))
{
IPMIP4_EVENT_IF(DEBUG2, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA Type/IPIPSEC: Discarding inbound selector (%s/%s) due to SPD not properly covering the address(%s)",
ipmip_ntop_inet(proc, isel.RemoteId.in.s_addr),
ipmip_ntop_inet(proc, isel.remote_mask.in.s_addr),
ipmip_ntop_inet(proc, addr)));
continue;
}
}
else
{
/* Verify the scoa */
if (!ipmipmn_if_secure_isbound(reply, isel.RemoteId.in.s_addr))
{
IPMIP4_EVENT_IF(DEBUG2, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA Type/IPIPSEC: Discarding inbound selector due to remote destination not bound"));
continue;
}
}
/* Verify the end-point */
if (!ipmipmn_if_secure_isbound(reply, isel.sa.dst.in.s_addr))
{
IPMIP4_EVENT_IF(DEBUG2, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA Type/IPIPSEC: Discarding inbound selector due to SA destination not bound"));
/**/
/* Current anchor address */
continue;
}
/* OK; Verify that we've gotten an outbound flow */
osel.Index = 0; /* get first selector. */
for(;;)
{
/* Get first/next Selector */
retval = ipmip_util_ioctl(&data->api.ipmip4.ipmip, proc, IP_SIOCXIPSEC_SELECTOR, &osel);
if(retval != IPCOM_SUCCESS)
{
IPMIP4_EVENT_IF(ERR, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA Type/IPIPSEC: Selector print aborted: error = %d", retval));
goto bail_out;
}
/* */
if(osel.Index == 0)
break;
/* Do verify the SPD */
if (!ipmipmn_if_secure_verify_spd(data, proc, &osel, IPIPSEC_SADIR_OUTPUT))
continue;
/* Local origin must be the same as remote destination */
if (osel.LocalId.in.s_addr != isel.RemoteId.in.s_addr ||
osel.local_mask.in.s_addr != isel.remote_mask.in.s_addr)
{
IPMIP4_EVENT_IF(DEBUG2, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA Type/IPIPSEC: Discarding outbound selector due inbound(%s)/outbound(%s) mismatch",
ipmip_ntop_inet(proc, isel.RemoteId.in.s_addr),
ipmip_ntop_inet(proc, osel.LocalId.in.s_addr)));
continue;
}
if (!ipmipmn_if_get_sainfo(&osel.sa, &sainfo))
continue;
/* Verify the end-point */
if (!ipmipmn_if_secure_isbound(reply, sainfo.srcaddr))
{
IPMIP4_EVENT_IF(DEBUG2, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA Type/IPIPSEC: Discarding outbound selector due to SA destination not bound"));
/**/
/* Current anchor address */
continue;
}
if (tia && sainfo.tunnel == IP_FALSE)
{
IPMIP4_EVENT_IF(DEBUG2, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA Type/IPIPSEC: Non-TIA due to sa does not tunnel"));
tia = IP_FALSE;
}
/* Store TIA information */
if (tia)
{
cparam->tia.ike_address = osel.sa.dst.in.s_addr;
cparam->tia.local_address = sainfo.srcaddr;
}
/* We're successful; check this particular address */
if (tia)
return IPMIPMN_ATTR_COA_SCOA_TIA;
else
return IPMIPMN_ATTR_COA_SCOA_PROTECTED;
}
}
bail_out:
#else
IPMIP_UNUSED_PARAMETER(proc);
IPMIP_UNUSED_PARAMETER(reply);
IPMIP_UNUSED_PARAMETER(addr);
IPMIP_UNUSED_PARAMETER(cparam);
#endif
return IPMIPMN_ATTR_COA_CCOA;
}
/*
*===========================================================================
* ipmipmn_if_coa_relink_iflocked
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipmipmn_if_coa_relink_iflocked(Ipmipmn_context_t *data,
Ipmip_proc_t *proc,
Ipmipmn_iff_t *ifp,
Ipmipmn_api_coa_t *coa,
Ip_bool first)
{
IPMIP_UNUSED_PARAMETER(data);
IPMIP_UNUSED_PARAMETER(proc);
if (ipmipmn_if_coa_valid(coa))
{
ipcom_list_remove(&coa->next);
if (first)
ipcom_list_insert_first(&ifp->coa, &coa->next);
else
ipcom_list_insert_last(&ifp->coa, &coa->next);
}
}
IP_STATIC int
ipmipmn_if_coa_output_send(Ipmipmn_context_t *data,
Ipmip_proc_t *proc,
Ipmipmn_node_t *node,
Ipmipmn_pktbuf_t *pbuf,
struct Ipmipmn_api_coa *coa,
Ip_bool deregister,
Ip_u32 src,
Ip_u32 dst,
void *exdata)
{
IPMIP_UNUSED_PARAMETER(deregister);
IPMIP_UNUSED_PARAMETER(coa);
IPMIP_UNUSED_PARAMETER(exdata);
if (node->api.params.mnRegCoA->peer == IPMIPMN_ATTR_PEER_FOREIGN_AGENT)
{
if (!ipmip4_pkt_udp_write(&data->api.ipmip4,
proc,
&pbuf->api.ipmip4.ipmip.packet,
src,
data->api.ipmip4.pkt->s_udp_out.sin_port,
dst,
ip_htons(IPMIP4_PORT),
node->api.params.pubIf,
node->api.params.mnRegCoA->params.fa.lladdr))
{
IPMIP4_EVENT_IF(WARNING, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"%s unable to write request errno=%d",
IPMIPMN_API_NODE_NAME(&data->api, proc, &node->api),
ipcom_errno));
return -1;
}
}
else if (node->api.params.mnRegCoA->type == IPMIPMN_ATTR_COA_HA)
{
if (!ipmip4_pkt_udp_write(&data->api.ipmip4,
proc,
&pbuf->api.ipmip4.ipmip.packet,
src,
data->api.ipmip4.pkt->s_udp_out.sin_port,
dst,
ip_htons(IPMIP4_PORT),
node->api.params.pubIf,
node->api.params.mnRegCoA->params.ha.lladdr))
{
IPMIP4_EVENT_IF(WARNING, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"%s unable to send de-registration request errno=%d",
IPMIPMN_API_NODE_NAME(&data->api, proc, &node->api),
ipcom_errno));
return -1;
}
}
else
{
if (ipmip4_pkt_udp_sendmsg(&data->api.ipmip4,
proc,
&pbuf->api.ipmip4.ipmip.packet,
src,
data->api.ipmip4.pkt->s_udp_out.sin_port,
dst,
ip_htons(IPMIP4_PORT),
node->api.params.pubIf) == IP_FALSE)
{
IPMIP4_EVENT_IF(WARNING, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"%s unable to send request errno=%d",
IPMIPMN_API_NODE_NAME(&data->api, proc, &node->api),
ipcom_errno));
return -1;
}
}
return 0;
}
/*
*===========================================================================
* ipmipmn_if_coa_new_iflocked
*===========================================================================
* Description: Create a CoA of a specific type.
* Parameters: data - Context
* proc - Process
* ifp - Interface
* addr - Address
* type - Type of CoA
* Returns:
*
*/
IP_STATIC Ipmipmn_api_coa_t *
ipmipmn_if_coa_new_iflocked(Ipmipmn_context_t *data,
Ipmip_proc_t *proc,
Ipmipmn_iff_t *ifp,
Ip_u32 addr,
Ip_u32 type,
Ip_bool first,
Ipmipmn_api_coa_params_t *param)
{
Ipmipmn_api_coa_t *coa = ipcom_calloc(1, sizeof(*coa));
if (coa == IP_NULL)
return IP_NULL;
coa->type = type;
coa->addr = addr;
switch (type)
{
case IPMIPMN_ATTR_COA_CCOA:
coa->name = ipmip_util_strdup("CCoA(%s)", ipmip_ntop_inet(proc, addr));
coa->peer = IPMIPMN_ATTR_PEER_HOME_AGENT;
coa->route_ro = IPMIPMN_ROUTE_TYPE_SKIP;
coa->route_reverse = IPMIPMN_ROUTE_TYPE_TUNNEL;
/* Remove it from the obsolete list in case its there */
break;
case IPMIPMN_ATTR_COA_SCOA_TIA:
coa->params.tia = param->tia;
coa->name = ipmip_util_strdup("CCoA-TIA(%s/%s/%s)",
ipmip_ntop_inet(proc, addr),
ipmip_ntop_inet(proc, param->tia.ike_address),
ipmip_ntop_inet(proc, param->tia.local_address));
coa->peer = IPMIPMN_ATTR_PEER_HOME_AGENT;
coa->route_ro = IPMIPMN_ROUTE_TYPE_SKIP;
coa->route_reverse = IPMIPMN_ROUTE_TYPE_TUNNEL;
break;
case IPMIPMN_ATTR_COA_SCOA_PROTECTED:
coa->name = ipmip_util_strdup("CCoA-Prot(%s)", ipmip_ntop_inet(proc, addr));
coa->peer = IPMIPMN_ATTR_PEER_HOME_AGENT;
coa->route_ro = IPMIPMN_ROUTE_TYPE_SKIP;
coa->route_reverse = IPMIPMN_ROUTE_TYPE_TUNNEL;
break;
case IPMIPMN_ATTR_COA_SCOA_DEAD:
coa->params.tia = param->tia;
coa->name = ipmip_util_strdup("CCoA-Dead(%s/%s/%s)",
ipmip_ntop_inet(proc, addr),
ipmip_ntop_inet(proc, param->tia.ike_address),
ipmip_ntop_inet(proc, param->tia.local_address));
coa->peer = IPMIPMN_ATTR_PEER_HOME_AGENT;
coa->route_ro = IPMIPMN_ROUTE_TYPE_SKIP;
coa->route_reverse = IPMIPMN_ROUTE_TYPE_TUNNEL;
break;
case IPMIPMN_ATTR_COA_FA:
coa->name = ipmip_util_strdup("FA(%s)", ipmip_ntop_inet(proc, addr));
coa->peer = IPMIPMN_ATTR_PEER_FOREIGN_AGENT;
coa->route_ro = IPMIPMN_ROUTE_TYPE_FA;
coa->route_reverse = IPMIPMN_ROUTE_TYPE_FA;
IPMIP_TIMER_INIT(&coa->params.fa.timer);
break;
case IPMIPMN_ATTR_COA_FA_LLH:
coa->name = ipmip_util_strdup("FA-LLH(%s)", ipmip_ntop_inet(proc, addr));
coa->peer = IPMIPMN_ATTR_PEER_FOREIGN_AGENT;
coa->route_ro = IPMIPMN_ROUTE_TYPE_FA;
coa->route_reverse = IPMIPMN_ROUTE_TYPE_FA;
IPMIP_TIMER_INIT(&coa->params.fa.timer);
break;
case IPMIPMN_ATTR_COA_HA:
coa->name = ipmip_util_strdup("HA(%s)", ipmip_ntop_inet(proc, addr));
coa->peer = IPMIPMN_ATTR_PEER_HOME_AGENT;
coa->route_ro = IPMIPMN_ROUTE_TYPE_HOME;
coa->route_reverse = IPMIPMN_ROUTE_TYPE_HOME;
IPMIP_TIMER_INIT(&coa->params.ha.timer);
break;
default:
coa->name = ipmip_util_strdup("unknown(%s)", ipmip_ntop_inet(proc, addr));
coa->peer = IPMIPMN_ATTR_PEER_HOME_AGENT;
coa->route_ro = IPMIPMN_ROUTE_TYPE_BLACKHOLE;
coa->route_reverse = IPMIPMN_ROUTE_TYPE_BLACKHOLE;
break;
}
coa->output = (Ipmipmn_api_coa_output_t)ipmipmn_if_coa_output_send;
ifp->allocated++;
IPMIP4_EVENT_IF(INFO, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"CoA '%s' added to interface '%s' (%d)",
coa->name,
ifp->iff.ipmip.ifname,
ifp->allocated));
ipmipmn_if_coa_reference_iflocked(data, proc, coa, IP_TRUE, "CoA Creation");
if (first)
ipcom_list_insert_first(&ifp->coa, &coa->next);
else
ipcom_list_insert_last(&ifp->coa, &coa->next);
return coa;
}
/*
*===========================================================================
* ipmipmn_if_coa_get_iflocked
*===========================================================================
* Description: Retrieve a CoA of a specific type.
* Parameters: data - Context
* proc - Process
* ifp - Interface
* addr - Address (may be 0)
* type - Type of CoA
* Returns:
*
*/
IP_STATIC Ipmipmn_api_coa_t *
ipmipmn_if_coa_get_iflocked(Ipmipmn_context_t *data,
Ipmip_proc_t *proc,
Ipmipmn_iff_t *ifp,
Ip_u32 addr,
Ip_u32 type,
Ip_bool down)
{
Ipmipmn_api_coa_t *coa;
IPMIP_UNUSED_PARAMETER(data);
IPMIP_UNUSED_PARAMETER(proc);
for (coa = IPCOM_LIST_FIRST(&ifp->coa); coa != IP_NULL; coa = IPCOM_LIST_NEXT(&coa->next))
{
if (coa->down && !down)
continue;
if (coa->type == type && (addr == 0 || coa->addr == addr))
break;
}
return coa;
}
/*
*===========================================================================
* ipmipmn_if_attached
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipmipmn_if_attached(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_iff_t *ifp)
{
/* */
if (ifp->iff.ipmip.ifindex != 0)
{
(void)ipmip_if_resolve_lla(&data->api.ipmip4.ipmip, proc, &ifp->iff.ipmip);
/** JOIN **/
(void)ipmip4_pkt_icmp_join(&data->api.ipmip4, proc, ip_htonl(IP_INADDR_ALLHOSTS_GROUP), ifp->iff.ipmip.ifindex);
(void)ipmip4_pkt_icmp_join(&data->api.ipmip4, proc, ip_htonl(IP_INADDR_ALLRTRS_GROUP), ifp->iff.ipmip.ifindex);
}
}
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipmipmn_if_acquire
*===========================================================================
* Description: Acquire an instantiation of a interface
* Parameters: data - Node context
* proc - Current process
* name - The name of the interface
* Returns: The pointer to the interface structure on success.
*
*/
IP_GLOBAL Ipmipmn_iff_t *
ipmipmn_if_acquire(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
const char *name,
Ipmipmn_iff_notify_t *notifier)
{
Ipmipmn_iff_t *ifp;
Ip_bool update = IP_FALSE;
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
ifp = ipmipmn_if_get_iflocked(data, proc, name);
/* */
if (ifp == IP_NULL)
{
/* Create the interface */
ifp = (Ipmipmn_iff_t*)ipmip4_if_alloc(&data->api.ipmip4,
proc,
sizeof(*ifp),
name,
0,
IPMIP_IF_TYPE_GENERIC,
IPMIPMN_IFF_IDENTITY_IFF,
(Ipmip_if_cleanup_t )ipmipmn_if_destroy);
/* Add */
if (ifp != IP_NULL)
{
Ip_u16 flags;
ifp->references++;
data->ifalloc++;
ipcom_list_init(&ifp->notifiers);
ipcom_list_init(&ifp->coa);
ipcom_list_insert_last(&data->ifhead, &ifp->iff.ipmip.next);
ifp->iff.ipmip.ifindex = ipcom_if_nametoindex(ifp->iff.ipmip.ifname);
if (ifp->iff.ipmip.ifindex)
ipmipmn_if_attached(data, proc, ifp);
if (!ifp->iff.ipmip.ifindex ||
!ipmip_if_get_flags(&data->api.ipmip4.ipmip, proc, &ifp->iff.ipmip, &flags) ||
!IP_BIT_ISSET(flags, IP_IFF_UP) ||
!IP_BIT_ISSET(flags, IP_IFF_RUNNING))
{
/* Not found; not running; not up */
ifp->link_down = IP_TRUE;
ifp->down = IP_TRUE;
}
IPMIP4_EVENT_IF(INFO, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Interface '%s' added (%d)", ifp->iff.ipmip.ifname, data->ifalloc));
/* Populate interface */
update = IP_TRUE;
}
}
/* */
if (ifp != IP_NULL && notifier != IP_NULL)
ipcom_list_insert_last(&ifp->notifiers, &notifier->if_next);
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
/* */
if (update)
ipmipmn_if_update_coa(data, proc);
return ifp;
}
/*
*===========================================================================
* ipmipmn_if_reference
*===========================================================================
* Description: Reference an interface pointer
* Parameters: data - Node context
* proc - Current process
* ifp - The interface
* Returns: The pointer to the interface structure on success.
*
*/
IP_GLOBAL Ipmipmn_iff_t *
ipmipmn_if_reference(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_iff_t *ifp)
{
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
ifp->references++;
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
return ifp;
}
/*
*===========================================================================
* ipmipmn_if_release
*===========================================================================
* Description: Release a previously acquired interface reference.
* Parameters: data - Context
* proc - The process
* iff - Interface pointer.
* Returns: IP_TRUE if the interface was destroyed; IP_FALSE otherwise.
*
*/
IP_GLOBAL Ip_bool
ipmipmn_if_release(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_iff_t *iff,
Ipmipmn_iff_notify_t *notifier)
{
Ip_bool ret;
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
if (notifier)
{
ipcom_list_remove(&notifier->if_next);
ipmip_timer_cancel(&data->api.ipmip4.ipmip, proc, &notifier->timer);
}
ret = ipmipmn_if_put_iflocked(data, proc, iff);
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
return ret;
}
/*
*===========================================================================
* ipmipmn_if_update_coa
*===========================================================================
* Description: Retrieve a secure address
* Parameters: mn - Mobile node data
* proc - Process
* ifname - The interface name
* addr - The address
* netmask - The netmask
* Returns: 0
*
*/
IP_GLOBAL int
ipmipmn_if_update_coa(Ipmipmn_context_t *data,
Ipmip_proc_t *proc)
{
/**/
Ip_u32 timestamp;
Ipmipmn_iff_t *ifp;
Ipmipmn_api_coa_t *coa;
Ipmipmn_api_coa_params_t cparam;
Ipmip_buffer_t reply;
int ret;
ipcom_memset(&cparam, 0, sizeof(cparam));
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
/**/
timestamp = data->ifstamp++;
for (ifp = IPCOM_LIST_FIRST(&data->ifhead); ifp != IP_NULL; ifp = IPCOM_LIST_NEXT(&ifp->iff.ipmip.next))
{
Ip_u16 flags;
Ip_u32 ifindex;
if ((ifindex = ipcom_if_nametoindex(ifp->iff.ipmip.ifname)) == 0 ||
!ipmip_if_get_flags(&data->api.ipmip4.ipmip, proc, &ifp->iff.ipmip, &flags) ||
!IP_BIT_ISSET(flags, IP_IFF_UP) ||
!IP_BIT_ISSET(flags, IP_IFF_RUNNING))
{
if (!ifp->link_down)
{
ifp->link_down = IP_TRUE;
if (!ifp->down)
{
ifp->sync_count++;
ifp->down = IP_TRUE;
ifp->timestamp = timestamp;
IPMIP4_EVENT_IF(INFO, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Interface %s is brought down",
ifp->iff.ipmip.ifname));
}
}
}
else
{
if (ifp->link_down)
{
ifp->link_down = IP_FALSE;
if (ifp->down && !ifp->admin_down)
{
ifp->down = IP_FALSE;
ifp->timestamp = timestamp;
IPMIP4_EVENT_IF(INFO, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Interface %s is brought up",
ifp->iff.ipmip.ifname));
}
}
}
/* Update IFINDEX */
if (ifindex != ifp->iff.ipmip.ifindex)
{
ifp->timestamp = timestamp;
ifp->iff.ipmip.ifindex = ifindex;
}
if (!ifp->link_down)
ipmipmn_if_attached(data, proc, ifp);
}
ipmip_buffer_init(&reply);
if ((ret = ipmip_netlink_inX_addr_dump(&data->api.ipmip4.ipmip, proc, IP_AF_INET, &reply)) < 0)
{
IPMIP4_EVENT_IF(ERR, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Unable to dump INET addresses error value %d", -ret));
}
else
{
struct Ip_nlmsghdr *nlh = IPMIP_BUFFER_PTR(&reply);
Ip_size_t nlh_size = IPMIP_BUFFER_SIZE(&reply);
/* */
while (IP_NLMSG_OK(nlh, nlh_size))
{
/**/
if (nlh->nlmsg_type == IP_RTM_NEWADDR)
{
struct Ip_ifaddrmsg *ifa = IP_NLMSG_DATA(nlh);
struct Ip_rtattr *ifap[IP_IFA_MAX + 1];
ifp = ipmipmn_if_getbyindex_iflocked(data, proc, ifa->ifa_index);
/* Don't have */
if (ifp != IP_NULL)
{
/* Parse */
ipmip_netlink_nlmsghdr_rta_parse(IP_IFA_MAX + 1,
IP_IFA_RTA(ifa),
IP_IFA_PAYLOAD(nlh),
ifap);
if (ifap[IP_IFA_LOCAL])
{
Ip_u32 addr = IP_GET_32ON8(IP_RTA_DATA(ifap[IP_IFA_LOCAL]));
int type = ipmipmn_if_coa_type(data, proc, &reply, addr, &cparam);
switch (type)
{
case IPMIPMN_ATTR_COA_SCOA_PROTECTED:
case IPMIPMN_ATTR_COA_SCOA_TIA:
case IPMIPMN_ATTR_COA_SCOA_DEAD:
case IPMIPMN_ATTR_COA_CCOA:
coa = ipmipmn_if_coa_get_iflocked(data, proc, ifp, addr, type, IP_TRUE);
if (coa)
{
/* Timestamp it */
coa->timestamp = timestamp;
/* Back in buisiness */
if (coa->down)
{
coa->down = IP_FALSE;
ifp->timestamp = timestamp;
/* Return the sysref */
ipmipmn_if_coa_reference_iflocked(data, proc, coa, IP_TRUE, "CoA Reappeared");
}
ipmipmn_if_coa_relink_iflocked(data, proc, ifp, coa, IP_FALSE);
}
else
{
if (type == IPMIPMN_ATTR_COA_CCOA)
{
Ipmipmn_api_coa_t *scoa;
/* Detached TIA */
if ((scoa = ipmipmn_if_coa_get_iflocked(data, proc, ifp, addr, IPMIPMN_ATTR_COA_SCOA_DEAD, IP_FALSE)) != IP_NULL)
{
scoa->timestamp = timestamp;
break;
}
else if ((scoa = ipmipmn_if_coa_get_iflocked(data, proc, ifp, addr, IPMIPMN_ATTR_COA_SCOA_TIA, IP_FALSE)) != IP_NULL)
{
coa = ipmipmn_if_coa_new_iflocked(data, proc, ifp, addr, IPMIPMN_ATTR_COA_SCOA_DEAD, IP_FALSE, &scoa->params);
coa->timestamp = timestamp;
coa->params.tia.mobiked = IP_FALSE;
ifp->timestamp = timestamp;
ipmipmn_if_coa_put_iflocked(data, proc, ifp, scoa, IP_TRUE, IP_TRUE, "Address type changed");
break;
}
}
else if (type == IPMIPMN_ATTR_COA_SCOA_TIA)
{
Ipmipmn_api_coa_t *scoa;
if ((scoa = ipmipmn_if_coa_get_iflocked(data, proc, ifp, addr, IPMIPMN_ATTR_COA_SCOA_DEAD, IP_FALSE)) != IP_NULL)
{
if (scoa->params.tia.mobiked)
cparam.tia.mobiked = IP_TRUE;
ipmipmn_if_coa_put_iflocked(data, proc, ifp, scoa, IP_TRUE, IP_TRUE, "CCoA-TIA reappered");
}
}
/* Add it */
coa = ipmipmn_if_coa_new_iflocked(data, proc, ifp, addr, type, IP_FALSE, &cparam);
coa->timestamp = timestamp;
ifp->timestamp = timestamp;
}
break;
}
}
ipmipmn_if_put_iflocked(data, proc, ifp);
}
}
/**/
nlh = IP_NLMSG_NEXT(nlh, nlh_size);
}
}
ipmip_buffer_release(&reply);
/* Remove any COA's not holding the correct timestamp */
/* Go through the interface list */
for (ifp = IPCOM_LIST_FIRST(&data->ifhead); ifp != IP_NULL; ifp = IPCOM_LIST_NEXT(&ifp->iff.ipmip.next))
{
for (coa = IPCOM_LIST_FIRST(&ifp->coa); coa != IP_NULL;)
{
Ipmipmn_api_coa_t *ncoa = IPCOM_LIST_NEXT(&coa->next);
/* Dont touch external CoA's */
if (coa->peer == IPMIPMN_ATTR_PEER_FOREIGN_AGENT)
{
if (ifp->down && !coa->down)
{
/**/
coa->down = IP_TRUE;
/* Sysref? */
ipmipmn_if_coa_put_iflocked(data, proc, ifp, coa, IP_FALSE, IP_TRUE, "Interface down");
/* Flag the interface as being timestamped */
ifp->timestamp = timestamp;
}
}
else if (coa->type != IPMIPMN_ATTR_COA_HA)
{
/* No longer around? */
if (coa->timestamp != timestamp)
{
coa->down = IP_TRUE;
ipmipmn_if_coa_put_iflocked(data, proc, ifp, coa, IP_TRUE, IP_TRUE, "CoA Disappeared");
/* Flag the interface as being timestamped */
ifp->timestamp = timestamp;
}
}
else
{
if (ifp->down && !coa->down)
{
coa->down = IP_TRUE;
ipmipmn_if_coa_put_iflocked(data, proc, ifp, coa, IP_TRUE, IP_TRUE, "Interface down");
/* Flag the interface as being timestamped */
ifp->timestamp = timestamp;
}
else if (!ifp->down && ifp->timestamp == timestamp)
{
/* Something on this interface has changed */
if (coa->params.ha.verify == IPMIPMN_IFF_COA_HA_VERIFY_NONE)
coa->params.ha.verify = IPMIPMN_IFF_COA_HA_VERIFY_REQUEST;
}
}
coa = ncoa;
}
}
/* Notify on all timestamped interfaces */
for (ifp = IPCOM_LIST_FIRST(&data->ifhead); ifp != IP_NULL; ifp = IPCOM_LIST_NEXT(&ifp->iff.ipmip.next))
{
if (ifp->timestamp == timestamp)
ipmipmn_if_notify_iflocked(data, proc, ifp);
}
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
return 0;
}
/*
*===========================================================================
* ipmipmn_if_coa_get
*===========================================================================
* Description: Retrieve a CoA of a specific type.
* Parameters: data - Context
* proc - Process
* ifp - Interface
* addr - Address (may be 0)
* type - Type of CoA
* Returns:
*
*/
IP_GLOBAL Ipmipmn_api_coa_t *
ipmipmn_if_coa_get(Ipmipmn_context_t *data,
Ipmip_proc_t *proc,
Ipmipmn_iff_t *ifp,
Ip_u32 addr,
Ip_u32 type,
const char *reason)
{
Ipmipmn_api_coa_t *coa;
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
coa = ipmipmn_if_coa_get_iflocked(data, proc, ifp, addr, type, IP_FALSE);
if (coa)
ipmipmn_if_coa_reference_iflocked(data, proc, coa, IP_FALSE, reason);
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
return coa;
}
/*
*===========================================================================
* ipmipmn_if_coa_put
*===========================================================================
* Description: Return a previously retrieved CoA
* Parameters: data - Context
* proc - Process
* ifp - Interface
* coa - CoA
* Returns: IP_TRUE if the CoA was deleted.
*
*/
IP_GLOBAL Ip_bool
ipmipmn_if_coa_put(Ipmipmn_context_t *data,
Ipmip_proc_t *proc,
Ipmipmn_iff_t *ifp,
Ipmipmn_api_coa_t *coa,
const char *reason)
{
Ip_bool dele;
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
dele = ipmipmn_if_coa_put_iflocked(data, proc, ifp, coa, IP_FALSE, IP_FALSE, reason);
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
return dele;
}
/*
*===========================================================================
* ipmipmn_if_findbyindex
*===========================================================================
* Description: Find an existing interface by locating its index.
* Parameters: data - Context
* proc - Process
* ifindex - Interface index.
* Returns: A referenced handle to the interface. Must be returned by
* release.
*
*/
IP_GLOBAL Ipmipmn_iff_t *
ipmipmn_if_findbyindex(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
int ifindex)
{
Ipmipmn_iff_t *ifp;
IPMIP_UNUSED_PARAMETER(proc);
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
/* Go through the interface list */
ifp = ipmipmn_if_getbyindex_iflocked(data, proc, ifindex);
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
return ifp;
}
/*
*===========================================================================
* ipmipmn_if_fa_adv
*===========================================================================
* Description: Process a foreign agent advertisement and update any
* foreign agent values.
* Parameters: data - Context
* proc - Process
* pbuf - Packet buffer.
* Returns:
*
*/
IP_GLOBAL void
ipmipmn_if_fa_adv(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_pktbuf_t *pbuf)
{
Ipmipmn_api_coa_t *fa;
Ip_bool change = IP_FALSE;
Ip_bool unreliable = IP_FALSE;
Ip_bool relinked = IP_FALSE;
int type = pbuf->api.ipmip4.ext.icmp.lla.found? IPMIPMN_ATTR_COA_FA_LLH : IPMIPMN_ATTR_COA_FA;
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
/* Find it it? */
fa = ipmipmn_if_coa_get_iflocked(data, proc, pbuf->api.iff.priv, pbuf->api.advinfo.coa, type, IP_TRUE);
if (fa != IP_NULL)
{
/* Check if FA rebooted */
if (pbuf->api.advinfo.adv_seq < fa->params.fa.adv_seq && pbuf->api.advinfo.adv_seq < 256)
{
IPMIP4_EVENT_IF(INFO, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Foreign Agent '%s' rebooted (seq=%d, oldseq=%d)",
ipmip_ntop_inet(proc, fa->addr),
(int)pbuf->api.advinfo.adv_seq,
fa->params.fa.adv_seq));
fa->params.fa.revoked = IP_TRUE;
change = IP_TRUE;
ipmipmn_if_coa_put_iflocked(data, proc, pbuf->api.iff.priv, fa, IP_TRUE, IP_TRUE, "FA Rebooted");
fa = IP_NULL;
goto create;
}
if (IP_BIT_ISSET(pbuf->api.advinfo.flags, IPMIP4_ICMP_ADVEXT_FLAG_BUSY))
{
IPMIP4_EVENT_IF(INFO, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Foreign Agent '%s' reported as busy",
ipmip_ntop_inet(proc, fa->addr)));
change = IP_TRUE;
fa->params.fa.revoked = IP_TRUE;
ipmipmn_if_coa_put_iflocked(data, proc, pbuf->api.iff.priv, fa, IP_TRUE, IP_TRUE, "FA Busy");
fa = IP_NULL;
goto done_out;
}
/* Seen again? */
if (fa->down)
{
fa->down = IP_FALSE;
fa->unreliable = IP_FALSE;
change = IP_TRUE;
relinked = IP_TRUE;
ipmipmn_if_coa_reference_iflocked(data, proc, fa, IP_TRUE, "Previously 'down' FA was advertised");
/* Relink first */
ipmipmn_if_coa_relink_iflocked(data, proc, pbuf->api.iff.priv, fa, IP_TRUE);
}
/* Detached? */
if (fa->unreliable)
{
fa->unreliable = IP_FALSE;
unreliable = IP_TRUE;
}
}
else
{
create:
if (IP_BIT_ISSET(pbuf->api.advinfo.flags, IPMIP4_ICMP_ADVEXT_FLAG_BUSY))
goto done_out;
relinked = IP_TRUE;
fa = ipmipmn_if_coa_new_iflocked(data, proc, pbuf->api.iff.priv, pbuf->api.advinfo.coa, type, IP_TRUE, IP_NULL);
}
if (fa != IP_NULL)
{
/* Store the flags */
fa->params.fa.adv_flags = pbuf->api.advinfo.flags;
/* Update params */
if (ipmip_lladdr_cmp(fa->params.fa.lladdr, pbuf->api.ipmip4.ipmip.srcinfo.lladdr) == IP_FALSE)
{
change = IP_TRUE;
/* We're using this one instead */
ipmip_util_dup(&fa->params.fa.lladdr,
pbuf->api.ipmip4.ipmip.srcinfo.lladdr,
pbuf->api.ipmip4.ipmip.srcinfo.lladdr->len);
}
/* LLH CoA */
if (pbuf->api.ipmip4.ext.icmp.lla.found > 0)
{
Ipmip_lladdr_t *lladdr;
/* Relink first */
ipmipmn_if_coa_relink_iflocked(data, proc, pbuf->api.iff.priv, fa, IP_TRUE);
change = IP_TRUE;
relinked = IP_TRUE;
lladdr = ipmip4_lla_llh_convert_lla_to_lladdr(IP_NULL, (Ipmip4_icmp_lla_ext_t *)pbuf->api.ipmip4.ext.icmp.lla.ext[0]);
if (ipmip_lladdr_cmp(fa->params.fa.lladdr_llh, lladdr) == IP_FALSE)
{
/* We're using this one instead */
ipcom_free(fa->params.fa.lladdr_llh);
fa->params.fa.lladdr_llh = lladdr;
}
else
{
ipcom_free(lladdr);
}
}
/* Look for FA challenge extension */
if (fa->params.fa.challenge != IP_NULL)
{
ipcom_free(fa->params.fa.challenge);
fa->params.fa.challenge = IP_NULL;
}
if (pbuf->api.ipmip4.ext.icmp.challenge)
{
/* Found challenge extension */
if (IPMIP4_MSGEXT_SHDR_EXT_SIZE(pbuf->api.ipmip4.ext.icmp.challenge) <= IPMIPMN_MAX_CHALLENGE_SIZE)
{
/* Save FA challenge */
fa->params.fa.challenge = ipmip4_msgext_shdr_dup(pbuf->api.ipmip4.ext.icmp.challenge);
if(IP_NULL == fa->params.fa.challenge)
goto done_out;
fa->params.fa.challenge->shdr.type = IPMIP4_EXT_TYPE_AGENT_ADV_CHALLENGE;
change = IP_TRUE;
}
else
{
IPMIP4_EVENT_IF(ERR, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Foreign Agent '%s' included challenge with a longer than supporter maximum size",
ipmip_ntop_inet(proc, fa->addr)));
}
}
/* Move us to be the FIRST foreign agent */
if (!relinked)
{
Ipmipmn_api_coa_t *coa;
Ipmipmn_api_coa_t *ffa = IP_NULL;
for (coa = IPCOM_LIST_PREV(&fa->next); coa != IP_NULL; coa = IPCOM_LIST_PREV(&coa->next))
{
/**/
if (coa->type == IPMIPMN_ATTR_COA_FA)
ffa = coa;
}
/* Found a previous entry? */
if (ffa != IP_NULL)
{
ipcom_list_remove(&fa->next);
ipcom_list_insert_before(&ffa->next, &fa->next);
change = IP_TRUE;
}
}
/* Update */
fa->params.fa.adv_seq = pbuf->api.advinfo.adv_seq;
if ((pbuf->api.advinfo.lifetime == 0 || fa->params.fa.lifetime == 0) && pbuf->api.advinfo.lifetime != fa->params.fa.lifetime)
change = IP_TRUE;
fa->params.fa.lifetime = pbuf->api.advinfo.lifetime;
ipmip_timer_cancel(&data->api.ipmip4.ipmip, proc, &fa->params.fa.timer);
ipmip_timer_request(&data->api.ipmip4.ipmip,
proc,
&fa->params.fa.timer,
data->iflock,
(Ipmip_timer_func_t)ipmipmn_if_agent_timeout_iflocked,
fa,
pbuf->api.iff.priv,
fa->params.fa.lifetime * 1000);
}
done_out:
#if 1
if (unreliable && fa != IP_NULL)
{
Ipmipmn_api_coa_t *coa;
/* Go through and locate and Home Agents located _AFTER_ the foreign agent, have we done
* a 'silent' move? */
for (coa = IPCOM_LIST_PREV(&fa->next); coa != IP_NULL; coa = IPCOM_LIST_PREV(&coa->next))
{
if (coa->down)
continue;
/**/
if (coa->type == IPMIPMN_ATTR_COA_HA && coa->params.ha.verify == IPMIPMN_IFF_COA_HA_VERIFY_NONE)
{
/* Need to do a directed solicitation on this HA; verify that
* it's still available */
coa->params.ha.verify = IPMIPMN_IFF_COA_HA_VERIFY_REQUEST;
change = IP_TRUE;
}
}
}
#endif
/**/
if (change)
{
#if 1
if (relinked && fa != IP_NULL)
{
Ipmipmn_api_coa_t *coa;
/* Go through and locate and Home Agents located _AFTER_ the foreign agent, have we done
* a 'silent' move? */
for (coa = IPCOM_LIST_NEXT(&fa->next); coa != IP_NULL; coa = IPCOM_LIST_NEXT(&coa->next))
{
if (coa->down)
continue;
/**/
if (coa->type == IPMIPMN_ATTR_COA_HA && coa->params.ha.verify == IPMIPMN_IFF_COA_HA_VERIFY_NONE)
{
/* Need to do a directed solicitation on this HA; verify that
* it's still available */
coa->params.ha.verify = IPMIPMN_IFF_COA_HA_VERIFY_REQUEST;
}
}
}
#endif
ipmipmn_if_notify_iflocked(data, proc, pbuf->api.iff.priv);
}
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
}
/*
*===========================================================================
* ipmipmn_if_fa_arp
*===========================================================================
* Description: Process an ARP reply and update any
* foreign agent values.
* Parameters: data - Context
* proc - Process
* pbuf - Packet buffer.
* Returns:
*
*/
IP_GLOBAL void
ipmipmn_if_fa_arp(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_pktbuf_t *pbuf)
{
Ipmipmn_api_coa_t *fa;
Ip_bool change = IP_FALSE;
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
/* Find it it? */
fa = ipmipmn_if_coa_get_iflocked(data, proc, pbuf->api.iff.priv, pbuf->api.advinfo.coa, IPMIPMN_ATTR_COA_FA, IP_TRUE);
if (fa != IP_NULL)
{
/* Update params */
if (ipmip_lladdr_cmp(fa->params.fa.lladdr, pbuf->api.ipmip4.ipmip.srcinfo.lladdr) == IP_FALSE)
{
change = IP_TRUE;
/* We're using this one instead */
ipmip_util_dup(&fa->params.fa.lladdr,
pbuf->api.ipmip4.ipmip.srcinfo.lladdr,
pbuf->api.ipmip4.ipmip.srcinfo.lladdr->len);
IPMIP4_EVENT_IF(INFO, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Foreign Agent '%s' changed LLA to '%s', Low Latency in effect?",
ipmip_ntop_inet(proc, fa->addr),
ipmip_ntop_lladdr(proc, fa->params.fa.lladdr)));
}
}
/**/
if (change)
ipmipmn_if_notify_iflocked(data, proc, pbuf->api.iff.priv);
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
}
/*
*===========================================================================
* ipmipmn_if_ha_adv_verify
*===========================================================================
* Description: Do home agent verification; if there's been a 'silent' roam
* we need to discover it.
* Parameters: data - Context
* proc - Process
* ifp - The interface
* ha - The home agent.
* Returns:
*
*/
IP_GLOBAL Ip_bool
ipmipmn_if_ha_adv_verify(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_iff_t *ifp,
Ipmipmn_api_coa_t *ha)
{
Ip_bool verify;
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
verify = ha->params.ha.verify == IPMIPMN_IFF_COA_HA_VERIFY_REQUEST;
if (verify)
{
IPMIP4_EVENT_IF(INFO, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Verifying Home Agent '%s' on interface %s due to foreign agent advertisement",
ha->name,
ifp->iff.ipmip.ifname));
/* Reschedule timeout */
ipmip_timer_cancel(&data->api.ipmip4.ipmip, proc, &ha->params.ha.timer);
ipmip_timer_request(&data->api.ipmip4.ipmip,
proc,
&ha->params.ha.timer,
data->iflock,
(Ipmip_timer_func_t)ipmipmn_if_agent_timeout_iflocked,
ha,
ifp,
2000);
ha->params.ha.verify = IPMIPMN_IFF_COA_HA_VERIFY_PENDING;
}
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
return verify;
}
/*
*===========================================================================
* ipmipmn_if_ha_adv
*===========================================================================
* Description: Process a home agent advertisement and update any
* home agent values.
* Parameters: data - Context
* proc - Process
* pbuf - Packet buffer.
* Returns:
*
*/
IP_GLOBAL void
ipmipmn_if_ha_adv(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_pktbuf_t *pbuf)
{
Ipmipmn_api_coa_t *ha;
Ip_bool change = IP_FALSE;
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
/* Find it it? */
ha = ipmipmn_if_coa_get_iflocked(data, proc, pbuf->api.iff.priv, pbuf->api.ipmip4.ipmip.srcinfo.src->sin.sin_addr.s_addr, IPMIPMN_ATTR_COA_HA, IP_FALSE);
if (ha == IP_NULL)
{
ha = ipmipmn_if_coa_new_iflocked(data, proc, pbuf->api.iff.priv, pbuf->api.ipmip4.ipmip.srcinfo.src->sin.sin_addr.s_addr, IPMIPMN_ATTR_COA_HA, IP_TRUE, IP_NULL);
if (ha != IP_NULL)
change = IP_TRUE;
}
/**/
if (ha != IP_NULL)
{
if (change)
{
Ipmipmn_api_coa_t *coa;
/* Go through and locate and Home Agents located _AFTER_ the foreign agent, have we done
* a 'silent' move? */
for (coa = IPCOM_LIST_NEXT(&ha->next); coa != IP_NULL; coa = IPCOM_LIST_NEXT(&coa->next))
{
if (coa->down)
continue;
/**/
if (coa->type == IPMIPMN_ATTR_COA_FA)
{
/* Need to do a directed solicitation on this HA; verify that
* it's still available */
coa->unreliable = IP_TRUE;
}
}
}
/* Update params */
if (ipmip_lladdr_cmp(ha->params.ha.lladdr, pbuf->api.ipmip4.ipmip.srcinfo.lladdr) == IP_FALSE)
{
change = IP_TRUE;
/* We're using this one instead */
ipmip_util_dup(&ha->params.ha.lladdr,
pbuf->api.ipmip4.ipmip.srcinfo.lladdr,
pbuf->api.ipmip4.ipmip.srcinfo.lladdr->len);
}
/* Verified */
ha->params.ha.verify = IPMIPMN_IFF_COA_HA_VERIFY_NONE;
/* Always relink to first in list when seen */
ipmipmn_if_coa_relink_iflocked(data, proc, pbuf->api.iff.priv, ha, IP_TRUE);
/* Update lifetime */
ha->params.ha.lifetime = pbuf->api.advinfo.lifetime;
/* Reschedule timeout */
ipmip_timer_cancel(&data->api.ipmip4.ipmip, proc, &ha->params.ha.timer);
ipmip_timer_request(&data->api.ipmip4.ipmip,
proc,
&ha->params.ha.timer,
data->iflock,
(Ipmip_timer_func_t)ipmipmn_if_agent_timeout_iflocked,
ha,
pbuf->api.iff.priv,
ha->params.ha.lifetime * 1000);
}
/**/
if (change)
ipmipmn_if_notify_iflocked(data, proc, pbuf->api.iff.priv);
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
}
/*
*===========================================================================
* ipmipmn_if_coa_fa_llh
*===========================================================================
* Description: Turn a LLH coa into a usable directly connected one.
* Parameters: data - Context
* proc - Process
* ifp - The interface
* llh_coa - LLH CoA
* Returns: A new freshly allocated direct CoA
*
*/
IP_GLOBAL Ipmipmn_api_coa_t *
ipmipmn_if_coa_fa_llh(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_iff_t *ifp,
Ipmipmn_api_coa_t *llh_coa)
{
Ipmipmn_api_coa_t *fa;
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
/* Find it it? */
fa = ipmipmn_if_coa_get_iflocked(data, proc, ifp, llh_coa->addr, IPMIPMN_ATTR_COA_FA, IP_TRUE);
if (fa == IP_NULL || fa->down)
{
if (fa == IP_NULL)
fa = ipmipmn_if_coa_new_iflocked(data, proc, ifp, llh_coa->addr, IPMIPMN_ATTR_COA_FA, IP_TRUE, IP_NULL);
if (fa != IP_NULL)
{
/* Update */
fa->params.fa.adv_seq = llh_coa->params.fa.adv_seq;
fa->params.fa.adv_flags = llh_coa->params.fa.adv_flags;
fa->params.fa.lifetime = llh_coa->params.fa.lifetime;
/* We're using this one instead */
ipmip_util_dup(&fa->params.fa.lladdr,
llh_coa->params.fa.lladdr_llh,
llh_coa->params.fa.lladdr_llh->len);
ipmip_timer_cancel(&data->api.ipmip4.ipmip, proc, &fa->params.fa.timer);
ipmip_timer_request(&data->api.ipmip4.ipmip,
proc,
&fa->params.fa.timer,
data->iflock,
(Ipmip_timer_func_t)ipmipmn_if_agent_timeout_iflocked,
fa,
ifp,
fa->params.fa.lifetime * 1000);
if (fa->down)
{
fa->down = IP_FALSE;
ipmipmn_if_coa_reference_iflocked(data, proc, fa, IP_TRUE, "LLH Completed");
ipmipmn_if_coa_relink_iflocked(data, proc, ifp, fa, IP_TRUE);
}
}
}
if (fa)
{
ipmipmn_if_coa_reference_iflocked(data, proc, fa, IP_FALSE, "LLH Done");
ipmipmn_if_coa_put_iflocked(data, proc, ifp, llh_coa, IP_FALSE, IP_FALSE, "LLH Done");
}
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
return fa;
}
/*
*===========================================================================
* ipmipmn_if_admin_down
*===========================================================================
* Description: Bring an interface administratively down.
* Parameters: data - Context
* proc - Process
* ifp - The interface
* Returns:
*
*/
IP_GLOBAL void
ipmipmn_if_admin_down(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_iff_t *ifp)
{
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
if (!ifp->admin_down)
{
ifp->admin_down = IP_TRUE;
ifp->references++;
ipmipmn_if_ifstate_iflocked(data, proc, ifp);
}
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
}
/*
*===========================================================================
* ipmipmn_if_admin_up
*===========================================================================
* Description: Bring an interface administratively up
* Parameters: data - Context
* proc - Process
* ifp - The interface
* Returns:
*
*/
IP_GLOBAL void
ipmipmn_if_admin_up(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_iff_t *ifp)
{
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
if (ifp->admin_down)
{
ifp->admin_down = IP_FALSE;
ipmipmn_if_put_iflocked(data, proc, ifp);
ipmipmn_if_ifstate_iflocked(data, proc, ifp);
}
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
}
/*
*===========================================================================
* ipmipmn_if_coa_reference
*===========================================================================
* Description: Reference the CoA
* Parameters: data - Context
* proc - Process
* coa - CoA
* Returns: The CoA
*
*/
IP_GLOBAL Ipmipmn_api_coa_t *
ipmipmn_if_coa_reference(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_api_coa_t *coa,
const char *reason)
{
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
ipmipmn_if_coa_reference_iflocked(data, proc, coa, IP_FALSE, reason);
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
return coa;
}
/*
*===========================================================================
* ipmipmn_if_coa_ike_reconfigure
*===========================================================================
* Description: Request a re-configuration of IKE if required.
* Parameters: data - The context
* proc - Process
* ifp - Interface
* coa - Care of Address.
* Returns:
*
*/
IP_GLOBAL Ip_bool
ipmipmn_if_coa_ike_reconfigure(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_iff_t *ifp,
Ipmipmn_api_coa_t *coa)
{
#ifdef IPIPSEC2
Ip_bool reconfigure = IP_FALSE;
IPMIP_UNUSED_PARAMETER(ifp);
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
if (coa->type == IPMIPMN_ATTR_COA_CCOA)
{
if (coa->params.ccoa.ike_reconfigure == IP_FALSE)
{
reconfigure = IP_TRUE;
coa->params.ccoa.ike_reconfigure = IP_TRUE;
}
}
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
/* */
if (reconfigure)
{
IPMIP4_EVENT_IF(INFO, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Reconfigure IKE due to new primary CoA"));
ipcom_ipd_send("ipike", IPCOM_IPD_MSGTYPE_RECONFIGURE);
return IP_TRUE;
}
#else
IPMIP_UNUSED_PARAMETER(ifp);
IPMIP_UNUSED_PARAMETER(data);
IPMIP_UNUSED_PARAMETER(proc);
IPMIP_UNUSED_PARAMETER(coa);
#endif
return IP_FALSE;
}
/*
*===========================================================================
* ipmipmn_if_coa_ike_mobility
*===========================================================================
* Description: Request an IKE mobility movement
* Parameters: data - The context
* proc - Process
* ifp - Interface
* coa - Care of Address.
* Returns:
*
*/
IP_GLOBAL Ip_bool
ipmipmn_if_coa_ike_mobility(struct Ipmipmn_context *data,
struct Ipmip_proc *proc,
Ipmipmn_iff_t *ifp,
Ipmipmn_api_coa_t *coa)
{
#ifdef IPIKE
Ip_u32 endpoints[4];
Ip_u32 endindex = 0;
unsigned int i;
unsigned int p;
Ip_bool pending = IP_FALSE;
IPMIP_UNUSED_PARAMETER(ifp);
ipmip_timer_lock_acquire(&data->api.ipmip4.ipmip, proc, data->iflock);
if (coa->type == IPMIPMN_ATTR_COA_CCOA)
{
struct Ip_ifaddrs *ifaddrs = IP_NULL;
Ipmipmn_api_coa_t *scoa;
/* Get interface addresses */
ipcom_getifaddrs(&ifaddrs);
/**/
for (scoa = IPCOM_LIST_FIRST(&ifp->coa); endindex < 4 && scoa != IP_NULL; scoa = IPCOM_LIST_NEXT(&scoa->next))
{
Ipmip_buffer_t reply;
if (scoa->type != IPMIPMN_ATTR_COA_SCOA_DEAD)
continue;
pending = IP_TRUE;
if (scoa->params.tia.mobiked)
continue;
ipmip_buffer_init(&reply);
if (ipmip4_netlink_route_validate(&data->api.ipmip4, proc, ifp->iff.ipmip.ifindex, scoa->params.tia.ike_address, &reply) < 0)
{
IPMIP4_EVENT_IF(INFO, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Postponing mobike request, since %s is not yet routable",
ipmip_ntop_inet(proc, scoa->params.tia.ike_address)));
continue;
}
ipmip_buffer_release(&reply);
#if 0
if (!ipmip4_usr_route_validate(&data->api.ipmip4, proc, &ifp->iff, 0, scoa->params.tia.ike_address))
{
IPMIP4_EVENT_IF(INFO, proc,
ipmip_log_event_add(&data->api.ipmip4.ipmip,
proc,
"Postponing mobike request, since %s is not yet routable",
ipmip_ntop_inet(proc, scoa->params.tia.ike_address)));
continue;
}
#endif
for (p = 0; p < endindex; p++)
{
if (endpoints[p] == scoa->params.tia.local_address)
break;
}
/**/
scoa->params.tia.mobiked = IP_TRUE;
if (p == endindex)
endpoints[endindex++] = scoa->params.tia.local_address;
}
/* Free the interface addresses */
ipcom_freeifaddrs(ifaddrs);
}
ipmip_timer_lock_release(&data->api.ipmip4.ipmip, proc, data->iflock);
/* Move the IKE endpoints */
for (i = 0; i < endindex; i++)
ipmipmn_if_ike_move(data, proc, ifp, coa, endpoints[i]);
return pending || endindex > 0;
#else
IPMIP_UNUSED_PARAMETER(ifp);
IPMIP_UNUSED_PARAMETER(data);
IPMIP_UNUSED_PARAMETER(proc);
IPMIP_UNUSED_PARAMETER(coa);
return IP_FALSE;
#endif
}
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/