/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipmip6pm_lma.c,v $ $Revision: 1.15 $ * $Source: /home/interpeak/CVSRoot/ipmip6pm/src/ipmip6pm_lma.c,v $ * $Author: jhorteli $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Joel Hortelius ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipmip6pm_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #include #include #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipmip6pm.h" #include "ipmip6pm_h.h" #include "ipmip6pm_node.h" #include "ipmip6pm_attr_h.h" #include "ipmip6pm_cli.h" #include "ipmip6pm_ifevent.h" #include "ipmip6pm_mh.h" #include "ipmip6pm_if.h" #include "ipmip6pm_route.h" #include "ipmip6pm_pktbuf.h" #include "ipmip6pm_lma.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ struct Ipmip6pm_lma_cb { Ipmip6pm_context_t *ctx; Ipmip6pm_if_t *ife; }; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ IP_STATIC unsigned ipmip6pm_lma_key_hash_nai(struct Ip_in6_addr *endpoint) { return ipcom_hash_update(endpoint, sizeof(*endpoint), 0); } IP_STATIC unsigned ipmip6pm_lma_obj_hash_nai(Ipmip6pm_lma_t *lma) { return ipmip6pm_lma_key_hash_nai(&lma->endpoint); } IP_STATIC Ip_bool ipmip6pm_lma_cmp_nai(Ipmip6pm_lma_t *lma, struct Ip_in6_addr *endpoint) { return IP_IN6_ARE_ADDR_EQUAL(&lma->endpoint, endpoint); } IP_STATIC void ipmip6pm_lma_free_bri(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_br_t *bri) { if (bri->next.head != IP_NULL) { ipcom_list_remove(&bri->next); /* Already have something outstanding? */ ipmip_context_id_lock(ctx->bri); ipmip_context_id_release(ctx->bri, bri); ipmip_context_id_unlock(ctx->bri); /**/ ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &bri->retransmit); /**/ ipcom_free(bri); } } IP_STATIC void ipmip6pm_lma_delete_locked(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma) { Ipmip6pm_br_t *bri; IPMIP6_EVENT_IF(DEBUG, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "Deleted LMA %s", ipmip_ntop_inet6(proc, &lma->endpoint, &lma->ifp->ipmip6.iff))); while ((bri = IPCOM_LIST_FIRST(&lma->bri)) != IP_NULL) ipmip6pm_lma_free_bri(ctx, proc, bri); /* Clear lma */ ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &lma->timer); ipcom_hash_remove(lma->ifp->lma_table, lma); ipcom_free(lma); } IP_STATIC Ipmip6pm_lma_t * ipmip6pm_lma_new_locked(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, struct Ipmip6pm_if *ifp, struct Ip_in6_addr *addr, Ip_u32 inet_endpoint, Ip_bool permanent) { Ipmip6pm_lma_t *lma = ipcom_calloc(1, sizeof(*lma)); ipcom_memcpy(&lma->endpoint, addr, sizeof(*addr)); lma->ifp = ifp; lma->permanent = permanent; lma->inet_endpoint = inet_endpoint; ipcom_list_init(&lma->bri); /* Clear lma */ ipcom_hash_add(ifp->lma_table, lma); /* */ IPMIP6_EVENT_IF(DEBUG, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "Added LMA %s", ipmip_ntop_inet6(proc, &lma->endpoint, &ifp->ipmip6.iff))); return lma; } IP_STATIC void ipmip6pm_lma_release_locked(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma) { /**/ if (--lma->references == 0) { ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &lma->timer); if (!lma->permanent) ipmip6pm_lma_delete_locked(ctx, proc, lma); } } IP_STATIC void ipmip6pm_lma_delete_cb(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma, void *arg1, void *arg2) { IPMIP_UNUSED_PARAMETER(arg1); IPMIP_UNUSED_PARAMETER(arg2); /* Delete */ ipmip6pm_lma_delete_locked(ctx, proc, lma); } IP_STATIC Ip_bool ipmip6pm_lma_heartbeat_response(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_if_t *ifp, struct Ip_in6_addr *dst, Ip_u32 seq, Ip_u16 flags) { /**/ Ipmip6_pktbuf_t *pkt; Ipmip6_mh_heartbeat_message_t *bh; Ipmip6_mh_mo_heartbeat_restart_counter_option_t rc; int err; /* Grab a packet */ pkt = (Ipmip6_pktbuf_t *) ipmip6_mh_pkt_alloc(&ctx->ipmip6, proc, sizeof(Ipmip6_mh_heartbeat_message_t), IPMIP6_MH_HEARTBEAT_MESSAGE); /* Out of memory? */ if (pkt == IP_NULL) return IP_FALSE; /* Get the MH header */ bh = (Ipmip6_mh_heartbeat_message_t *) IPMIP_BUFFER_PTR(&pkt->ipmip.packet); IP_SET_HTONS(&bh->bh_flags, flags | IPMIP6_MH_HEARTBEAT_FLAG_R); IP_SET_HTONL(&bh->bh_seq, seq); rc.hdr.val = IPMIP6_MH_MO_HEARTBEAT_RESTART_COUNTER; rc.hdr.len = 4; IP_SET_HTONL(&rc.restart_counter, ctx->ipmip6.restart_counter); /**/ ipmip6_mh_pkt_opt_add_mo(&ctx->ipmip6, proc, pkt, &rc.hdr); /**/ /* Force it to go out using the CoA */ if (ifp->UseIPv4UDPEncapForSignalingMessages) { /* Ipv4 transport */ ipmip6_pkt_pktinfo_opt_add(pkt, &ifp->ifaddr.prefix, &ifp->iftunnels[IPMIP_TUNNELTYPE_IPIP]->ipmip6); } else ipmip6_pkt_pktinfo_src_opt_add(pkt, &ifp->ifaddr.prefix); if ((err = ipmip6_pkt_send(&ctx->ipmip6, proc, ctx->ipmip6.pkt->fd_mh, pkt, dst)) != 0) { IPCOM_LOG2(INFO, "%s: Error sending MH HeartBeat Response = %x", ctx->ipmip6.ipmip.name, err); return IP_FALSE; } return IP_TRUE; } IP_STATIC Ip_bool ipmip6pm_lma_send_heartbeat_request_locked(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma) { /**/ Ipmip6_pktbuf_t *pkt; Ipmip6_mh_heartbeat_message_t *bh; int err; /* Grab a packet */ pkt = (Ipmip6_pktbuf_t *) ipmip6_mh_pkt_alloc(&ctx->ipmip6, proc, sizeof(Ipmip6_mh_heartbeat_message_t), IPMIP6_MH_HEARTBEAT_MESSAGE); /* Out of memory? */ if (pkt == IP_NULL) return IP_FALSE; /* Get the MH header */ bh = (Ipmip6_mh_heartbeat_message_t *) IPMIP_BUFFER_PTR(&pkt->ipmip.packet); IP_SET_HTONS(&bh->bh_flags, 0); lma->sequence++; lma->unacked++; IP_SET_HTONL(&bh->bh_seq, lma->sequence); /**/ ipmip6pm_lma_route_mh(ctx, proc, lma->ifp, &lma->endpoint, lma->inet_endpoint); /**/ /* Force it to go out using the CoA */ if (lma->ifp->UseIPv4UDPEncapForSignalingMessages) { /* Ipv4 transport */ ipmip6_pkt_pktinfo_opt_add(pkt, &lma->ifp->ifaddr.prefix, &lma->ifp->iftunnels[IPMIP_TUNNELTYPE_IPIP]->ipmip6); } else ipmip6_pkt_pktinfo_src_opt_add(pkt, &lma->ifp->ifaddr.prefix); /* Send */ if ((err = ipmip6_pkt_send(&ctx->ipmip6, proc, ctx->ipmip6.pkt->fd_mh, pkt, &lma->endpoint)) != 0) { IPCOM_LOG2(INFO, "%s: Error sending MH HeartBeat = %x", ctx->ipmip6.ipmip.name, err); return IP_FALSE; } return IP_TRUE; } IP_STATIC int ipmip6pm_lma_send_heartbeat_unsolicited_locked(int event, Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma, void *unused) { IPMIP_UNUSED_PARAMETER(unused); if (event == IPMIP_TIMER_EVENT_RUN) { ipmip6pm_lma_route(ctx, proc, lma); ipmip6pm_lma_heartbeat_response(ctx, proc, lma->ifp, &lma->endpoint, 0, IPMIP6_MH_HEARTBEAT_FLAG_U); } return IPMIP_TIMER_RET_DONE; /* do not reschedule the timeout */ } IP_STATIC int ipmip6pm_lma_send_heartbeat_timeout_locked(int event, Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma, void *unused) { IPMIP_UNUSED_PARAMETER(unused); if (event == IPMIP_TIMER_EVENT_RUN) { if (lma->unacked > lma->ifp->HeartBeatMissAllowed) { IPMIP6_EVENT_IF(WARNING, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "LMA %s seems DEAD - no HeartBeat response", ipmip_ntop_inet6(proc, &lma->endpoint, IP_NULL))); } /**/ ipmip6pm_lma_send_heartbeat_request_locked(ctx, proc, lma); ipmip_timer_request(&ctx->ipmip6.ipmip, proc, &lma->timer, lma->ifp->ipmip6.iff_lock, (Ipmip_timer_func_t)ipmip6pm_lma_send_heartbeat_timeout_locked, lma, IP_NULL, lma->ifp->HeartBeatInterval * 1000); } return IPMIP_TIMER_RET_DONE; /* do not reschedule the timeout */ } IP_STATIC Ip_u32 ipmip6pm_lma_reference_locked(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma) { IPMIP_UNUSED_PARAMETER(ctx); IPMIP_UNUSED_PARAMETER(proc); return ++lma->references; } IP_STATIC Ipmip6pm_lma_t * ipmip6pm_lma_get_locked(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, struct Ipmip6pm_if *ifp, struct Ip_in6_addr *endpoint, Ip_u32 inet_endpoint, unsigned flags) { Ipmip6pm_lma_t *lma; lma = ipcom_hash_get(ifp->lma_table, endpoint); if (lma == IP_NULL && IP_BIT_ISSET(flags, IPMIP6PM_LMA_CREATE)) lma = ipmip6pm_lma_new_locked(ctx, proc, ifp, endpoint, inet_endpoint, IP_BIT_ISTRUE(flags, IPMIP6PM_LMA_PERMANENT)); /**/ if (lma) { if (ipmip6pm_lma_reference_locked(ctx, proc, lma) == 1 && lma->ifp->HeartBeatEnabled && !lma->disabled) ipmip_timer_request(&ctx->ipmip6.ipmip, proc, &lma->timer, lma->ifp->ipmip6.iff_lock, (Ipmip_timer_func_t)ipmip6pm_lma_send_heartbeat_timeout_locked, lma, IP_NULL, 0); } return lma; } IP_STATIC void ipmip6pm_lma_configure_foreach(struct Ipmip_proc *proc, char *name, char *subkey, const char *value, struct Ipmip6pm_lma_cb *cb) { struct Ip_in6_addr endpoint; Ip_u32 inet_endpoint = 0; Ipmip6pm_lma_t *lma; #ifdef IPMIP_USE_INET const char *needle; #endif IPMIP_UNUSED_PARAMETER(name); ipmip_util_inet6(subkey, &endpoint, IP_NULL); #ifdef IPMIP_USE_INET if ((needle = ipcom_strchr(subkey, '/')) != IP_NULL) ipmip_util_inet(needle + 1, &inet_endpoint, IP_NULL); #endif lma = ipmip6pm_lma_get_locked(cb->ctx, proc, cb->ife, &endpoint, inet_endpoint, IPMIP6PM_LMA_CREATE|IPMIP6PM_LMA_PERMANENT); if (lma) { int enabled = ipmip_sysvar_cmd_str_to_key(ipmip_sysvar_bool_ops, value); lma->disabled = (enabled == 0); /**/ if (lma->disabled == IP_FALSE) { ipmip_timer_cancel(&cb->ctx->ipmip6.ipmip, proc, &lma->timer); ipmip_timer_request(&cb->ctx->ipmip6.ipmip, proc, &lma->timer, cb->ife->ipmip6.iff_lock, (Ipmip_timer_func_t)ipmip6pm_lma_send_heartbeat_unsolicited_locked, lma, IP_NULL, 0); } --lma->references; } } IP_STATIC void ipmip6pm_lma_configure_dump_foreach(Ipmip6_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma, Ipmip_buffer_t *buffer, Ipmip6pm_if_t *ife) { IPMIP_UNUSED_PARAMETER(ctx); /**/ if (lma->permanent) { #ifdef IPMIP_USE_INET if (lma->inet_endpoint) ipmip_cli_config_printf(buffer, "ipmip6pm.interface.%s.LMA.%s/%s=%s", ife->ipmip6.iff.ifname, ipmip_ntop_inet6(proc, &lma->endpoint, IP_NULL), ipmip_ntop_inet(proc, lma->inet_endpoint), lma->disabled? "disabled" : "enabled"); else #endif ipmip_cli_config_printf(buffer, "ipmip6pm.interface.%s.LMA.%s=%s", ife->ipmip6.iff.ifname, ipmip_ntop_inet6(proc, &lma->endpoint, IP_NULL), lma->disabled? "disabled" : "enabled"); } } IP_STATIC void ipmip6pm_lma_restart_counter_foreach(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_node_t *node, Ipmip6pm_lma_t *lma, void *unused) { IPMIP_UNUSED_PARAMETER(unused); if (node->lma == lma) { ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &node->expire); ipmip_timer_request(&ctx->ipmip6.ipmip,proc, &node->expire, node->lock, (Ipmip_timer_func_t)ipmip6pm_node_registration_timeout, node, "timeout due to LMA restart", 0); } } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ IP_GLOBAL void ipmip6pm_lma_route_mh(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_if_t *ifp, struct Ip_in6_addr *endpoint, Ip_u32 inet_address) { #ifdef IPMIP_USE_INET Ipmip_netlink_kern_compare_t scmp; if (inet_address) { /* Create inbound tunnel route */ ipmip6_netlink_route_mip_do(&ctx->ipmip6, proc, IP_RTM_NEWMIP, IP_WRMOB6_NEW_LINK_TX, IP_WRMOB6_NEW_TYPE_TEMPORARY, ctx->iftable_pm_inet, ifp->iftunnels[IPMIP_TUNNELTYPE_IPIP]->ipmip6.iff.ifindex, endpoint, sizeof(struct Ip_in6_addr), 128, &inet_address, sizeof(struct Ip_in_addr), ifp->UseIPv4UDPEncapForSignalingMessages? ip_htons(IPMIP6_DSMIP_UDP_PORT) : 0, ipmip_netlink_route_src_add_protocol(ipmip_netlink_route_src_egress_ifindex(&scmp, ifp->iftunnels[IPMIP_TUNNELTYPE_IPIP]->ipmip6.iff.ifindex), IP_IPPROTO_MH), IP_NULL, IPMIP_LINK_SEND, IP_NULL, IPNET_RTF_TUNNELEND); } #else IPMIP_UNUSED_PARAMETER(ctx); IPMIP_UNUSED_PARAMETER(proc); IPMIP_UNUSED_PARAMETER(ifp); IPMIP_UNUSED_PARAMETER(endpoint); IPMIP_UNUSED_PARAMETER(inet_address); #endif } IP_GLOBAL void ipmip6pm_lma_route(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma) { ipmip6pm_lma_route_mh(ctx, proc, lma->ifp, &lma->endpoint, lma->inet_endpoint); } IP_GLOBAL void ipmip6pm_lma_route_pbuf(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_pktbuf_t *pbuf) { #ifdef IPMIP_USE_INET if (pbuf->ipmip6.recvinfo.encapsulation != IP_NULL && pbuf->ipmip6.recvinfo.encapsulation->sa.sa_family == IP_AF_INET) ipmip6pm_lma_route_mh(ctx, proc, pbuf->iff.priv, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, pbuf->ipmip6.recvinfo.encapsulation->sin.sin_addr.s_addr); #else IPMIP_UNUSED_PARAMETER(ctx); IPMIP_UNUSED_PARAMETER(proc); IPMIP_UNUSED_PARAMETER(pbuf); #endif } IP_GLOBAL Ipmip6pm_lma_t * ipmip6pm_lma_get(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, struct Ipmip6pm_if *ifp, struct Ip_in6_addr *endpoint, Ip_u32 inet_endpoint, unsigned flags) { Ipmip6pm_lma_t *lma; ipmip_timer_lock_acquire(&ctx->ipmip6.ipmip, proc, ifp->ipmip6.iff_lock); lma = ipmip6pm_lma_get_locked(ctx, proc, ifp, endpoint, inet_endpoint, flags); ipmip_timer_lock_release(&ctx->ipmip6.ipmip, proc, ifp->ipmip6.iff_lock); return lma; } IP_GLOBAL void ipmip6pm_lma_release(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma) { if (lma != IP_NULL) { Ipmip_timer_lock_t *lock = ipmip_timer_lock_acquire(&ctx->ipmip6.ipmip, proc, lma->ifp->ipmip6.iff_lock); ipmip6pm_lma_release_locked(ctx, proc, lma); ipmip_timer_lock_release(&ctx->ipmip6.ipmip, proc, lock); } } IP_GLOBAL void ipmip6pm_lma_reference(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma) { if (lma != IP_NULL) { Ipmip_timer_lock_t *lock = ipmip_timer_lock_acquire(&ctx->ipmip6.ipmip, proc, lma->ifp->ipmip6.iff_lock); ipmip6pm_lma_reference_locked(ctx, proc, lma); ipmip_timer_lock_release(&ctx->ipmip6.ipmip, proc, lock); } } IP_GLOBAL void ipmip6pm_lma_configure(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, struct Ipmip6pm_if *ife) { struct Ipmip6pm_lma_cb cb; ife->lma_table= ipcom_hash_new((Ipcom_hash_obj_func) ipmip6pm_lma_obj_hash_nai, (Ipcom_hash_key_func) ipmip6pm_lma_key_hash_nai, (Ipcom_hash_cmp_func) ipmip6pm_lma_cmp_nai); ife->HeartBeatInterval = ipmip_sysvar_ulong(ipmip_sysvar_ulong(60, "ipmip6pm.HeartBeatInterval"), "ipmip6pm.interface.%s.HeartBeatInterval", ife->ipmip6.iff.ifname); ife->HeartBeatMissAllowed = ipmip_sysvar_ulong(ipmip_sysvar_ulong(3, "ipmip6pm.HeartBeatMissAllowed"), "ipmip6pm.interface.%s.HeartBeatMissAllowed", ife->ipmip6.iff.ifname); ife->HeartBeatEnabled = ipmip_sysvar_bool(ipmip_sysvar_bool(IP_FALSE, "ipmip6pm.HeartBeatEnabled"), "ipmip6pm.interface.%s.HeartBeatEnabled", ife->ipmip6.iff.ifname); cb.ctx = ctx; cb.ife = ife; /* Read The permanent LMA's */ ipmip_sysvar_foreach(proc, (Ipmip_sysvar_foreach_t) ipmip6pm_lma_configure_foreach, &cb, "ipmip6pm.LMA.*"); ipmip_sysvar_foreach(proc, (Ipmip_sysvar_foreach_t) ipmip6pm_lma_configure_foreach, &cb, "ipmip6pm.interface.%s.LMA.*", ife->ipmip6.iff.ifname); } IP_GLOBAL void ipmip6pm_lma_unconfigure(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, struct Ipmip6pm_if *ifp) { if (ifp->lma_table) { ipmip_util_hash_foreach(&ctx->ipmip6.ipmip, proc, ifp->lma_table, (Ipmip_util_hash_foreach_t)ipmip6pm_lma_delete_cb, IP_NULL, IP_NULL); ipcom_hash_delete(ifp->lma_table); ifp->lma_table = IP_NULL; } } IP_GLOBAL void ipmip6pm_lma_configure_dump(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_if_t *ife, Ipmip_buffer_t *buffer) { ipmip_cli_config_printf(buffer, "ipmip6pm.interface.%s.HeartBeatInterval=%d", ife->ipmip6.iff.ifname, ife->HeartBeatInterval); ipmip_cli_config_printf(buffer, "ipmip6pm.interface.%s.HeartBeatMissAllowed=%d", ife->ipmip6.iff.ifname, ife->HeartBeatMissAllowed); ipmip_cli_config_printf(buffer, "ipmip6pm.interface.%s.HeartBeatEnabled=%s", ife->ipmip6.iff.ifname, ife->HeartBeatEnabled? "enabled" : "disabled"); /* Dump permanent rules */ ipmip_util_hash_foreach(&ctx->ipmip6.ipmip, proc, ife->lma_table, (Ipmip_util_hash_foreach_t)ipmip6pm_lma_configure_dump_foreach, buffer, ife); } IP_STATIC Ip_bool ipmip6pm_lma_heartbeat_restart_counter_update(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma, Ipmip6_mh_mo_heartbeat_restart_counter_option_t *mo) { IPMIP_UNUSED_PARAMETER(ctx); IPMIP_UNUSED_PARAMETER(proc); if (mo != IP_NULL) { Ip_u32 restart_counter = IP_GET_NTOHL(&mo->restart_counter); Ip_bool changed = (lma->have_restart_counter && restart_counter != lma->restart_counter); lma->restart_counter = restart_counter; lma->have_restart_counter = IP_TRUE; return changed; } return IP_FALSE; } IP_STATIC Ip_bool ipmip6pm_lma_heartbeat_restart_counter(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma, Ip_bool unsolicited, Ipmip6_mh_mo_heartbeat_restart_counter_option_t *mo) { if (ipmip6pm_lma_heartbeat_restart_counter_update(ctx, proc, lma, mo) || unsolicited) { if (lma->references > 1) { /* There are nodes */ ipmip_context_id_lock(ctx->idents); ipmip_context_id_foreach(&ctx->ipmip6.ipmip, proc, ctx->idents, (Ipmip_util_hash_foreach_t)ipmip6pm_lma_restart_counter_foreach, lma, IP_NULL); ipmip_context_id_unlock(ctx->idents); } return IP_TRUE; } return IP_FALSE; } IP_GLOBAL Ip_bool ipmip6pm_lma_heartbeat_input(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_pktbuf_t *pbuf) { if (pbuf->iff.priv->HeartBeatEnabled) { Ipmip6_mh_heartbeat_message_t *hb = IPMIP_BUFFER_PTR(&pbuf->ipmip6.ipmip.packet); Ip_u16 flags = IP_GET_NTOHS(&hb->bh_flags); Ip_u32 seq = IP_GET_NTOHL(&hb->bh_seq); if (IP_BIT_ISFALSE(flags, IPMIP6_MH_HEARTBEAT_FLAG_R)) { IPMIP6_EVENT_IF(DEBUG2, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "HeartBeat Request from LMA %s -- replying with %x", ipmip_ntop_inet6(proc, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, IP_NULL), ctx->ipmip6.restart_counter)); /* */ /**/ ipmip6pm_lma_route_pbuf(ctx, proc, pbuf); ipmip6pm_lma_heartbeat_response(ctx, proc, pbuf->iff.priv, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, seq, 0); } else { /* Reply */ Ipmip6pm_lma_t *lma = ipmip6pm_lma_get(ctx, proc, pbuf->iff.priv, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, 0, 0); if (lma != IP_NULL) { if (IP_BIT_ISSET(flags, IPMIP6_MH_HEARTBEAT_FLAG_U)) { IPMIP6_EVENT_IF(WARNING, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "UNSOLICITED HeartBeat Response from LMA %s", ipmip_ntop_inet6(proc, &lma->endpoint, IP_NULL))); /* Deal with unsolicited - and if it does not contain a restart counter */ ipmip6pm_lma_heartbeat_restart_counter(ctx, proc, lma, IP_TRUE, pbuf->ipmip6.mo.restart_option); } else { if (seq == lma->sequence) { lma->unacked = 0; if (ipmip6pm_lma_heartbeat_restart_counter(ctx, proc, lma, IP_FALSE, pbuf->ipmip6.mo.restart_option)) { IPMIP6_EVENT_IF(WARNING, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "SOLICITED IN-SEQUENCE HeartBeat Response with changed Restart Counter (%x) from LMA %s", lma->restart_counter, ipmip_ntop_inet6(proc, &lma->endpoint, IP_NULL))); } else { #ifdef IPTESTENGINE IPMIP6_EVENT_IF(INFO, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "SOLICITED IN-SEQUENCE HeartBeat Response with unchanged Restart Counter (%x) from LMA %s", lma->restart_counter, ipmip_ntop_inet6(proc, &lma->endpoint, IP_NULL))); #else IPMIP6_EVENT_IF(DEBUG2, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "SOLICITED IN-SEQUENCE HeartBeat Response with unchanged Restart Counter (%x) from LMA %s", lma->restart_counter, ipmip_ntop_inet6(proc, &lma->endpoint, IP_NULL))); #endif } } else { IPMIP6_EVENT_IF(DEBUG2, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "SOLICITED OUT-OF-SEQUENCE HeartBeat Response from LMA %s", ipmip_ntop_inet6(proc, &lma->endpoint, IP_NULL))); } } ipmip6pm_lma_release(ctx, proc, lma); } else { IPMIP6_EVENT_IF(INFO, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "SOLICITED HeartBeat Response from unknown LMA %s", ipmip_ntop_inet6(proc, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, IP_NULL))); } } } else { ipmip6pm_lma_route_pbuf(ctx, proc, pbuf); return IP_FALSE; } /* */ ipmip_pktbuf_free(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip); return IP_TRUE; } IP_GLOBAL Ip_bool ipmip6pm_lma_heartbeat_binding_error_input(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_pktbuf_t *pbuf) { 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) { Ipmip6pm_lma_t *lma = ipmip6pm_lma_get(ctx, proc, pbuf->iff.priv, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, 0, 0); if (lma != IP_NULL) { /* */ if (lma->unacked != 0 && lma->disabled == IP_FALSE) { IPMIP6_EVENT_IF(WARNING, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "Binding Error received from LMA %s, HeartBeat disabled", ipmip_ntop_inet6(proc, &lma->endpoint, IP_NULL))); lma->disabled = IP_TRUE; ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &lma->timer); /* */ ipmip6pm_lma_release(ctx, proc, lma); } } } /* */ ipmip_pktbuf_free(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip); return IP_TRUE; } IP_STATIC void ipmip6pm_lma_attr_get_foreach(Ipmip6_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma, Ipmip_buffer_t *buffer, Ipmip6pm_if_t *ife) { Ip_size_t rta = ipmip_attr_begin(buffer, IPMIP6PM_CLI_ATTR_LMA); IPMIP_UNUSED_PARAMETER(ctx); IPMIP_UNUSED_PARAMETER(proc); /* */ ipmip_attr_put_inet6(buffer, IPMIP6PM_CLI_ATTR_BC_P_HOME_AGENT, &lma->endpoint); #ifdef IPMIP_USE_INET ipmip_attr_put_inet(buffer, IPMIP6PM_CLI_ATTR_BC_P_HOME_AGENT_INET, lma->inet_endpoint); #endif ipmip_attr_put_str(buffer, IPMIP6PM_CLI_ATTR_BC_P_IFNAME, ife->ipmip6.iff.ifname); ipmip_attr_end(buffer, rta); } IP_GLOBAL void ipmip6pm_lma_attr_get(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_if_t *ife, Ipmip_buffer_t *buffer) { /* Dump permanent rules */ ipmip_util_hash_foreach(&ctx->ipmip6.ipmip, proc, ife->lma_table, (Ipmip_util_hash_foreach_t)ipmip6pm_lma_attr_get_foreach, buffer, ife); } IP_STATIC void ipmip6pm_lma_revoke_node(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_node_t *node, Ipmip6pm_br_t *bri, void *unused) { IPMIP_UNUSED_PARAMETER(unused); if (node->lma == bri->lma) { /* REVOKE */ ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &node->revoke); ipmip_timer_request(&ctx->ipmip6.ipmip, proc, &node->revoke, node->lock, (Ipmip_timer_func_t)ipmip6pm_node_timeout, node, "global revocation acknowledged", 0); } } IP_STATIC int ipmip6pm_lma_bri_timeout(int event, Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_br_t *bri, void *extarg) { IPMIP_UNUSED_PARAMETER(extarg); if (event == IPMIP_TIMER_EVENT_RUN) { if (++bri->xmits > bri->lma->ifp->BRIMaxRetriesNumber) { ipmip_context_id_lock(ctx->idents); /* Terminate any matching this MAG */ ipmip_context_id_foreach(&ctx->ipmip6.ipmip, proc, ctx->idents, (Ipmip_util_hash_foreach_t)ipmip6pm_lma_revoke_node, bri, IP_NULL); ipmip_context_id_unlock(ctx->idents); /**/ ipmip6pm_lma_free_bri(ctx, proc, bri); } else { /* Retransmit */ ipmip6pm_mh_bri_send(ctx, proc, IP_NULL, bri); return IPMIP_TIMER_RET_REINSERT; } } return IPMIP_TIMER_RET_DONE; } IP_STATIC int ipmip6pm_lma_bri_reply_locked(int event, Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_br_t *bri, Ipmip6pm_pktbuf_t *pbuf) { if (event == IPMIP_TIMER_EVENT_RUN) { /* Wipeout the nodes associated with this BRI */ Ipmip6_mh_bra_message_t *bra = (Ipmip6_mh_bra_message_t *)IPMIP_BUFFER_PTR(&pbuf->ipmip6.ipmip.packet); IPMIP6_EVENT_IF(DEBUG2, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "Global Binding Revocation Acknowledgement received with status code %d", bra->bra_status)); /* Process */ if (bra->bra_status <= IPMIP6_MH_BRA_STATUS_PARTIAL_SUCCESS) { /* Accept */ ipmip_context_id_lock(ctx->idents); /* Terminate any matching this MAG */ ipmip_context_id_foreach(&ctx->ipmip6.ipmip, proc, ctx->idents, (Ipmip_util_hash_foreach_t)ipmip6pm_lma_revoke_node, bri, IP_NULL); ipmip_context_id_unlock(ctx->idents); } else { IPMIP6_EVENT_IF(INFO, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "LMA %s unable to revoke, due to error %s", ipmip_ntop_inet6(proc, &bri->lma->endpoint, IP_NULL), ipmip6_mh_bra_error_str(bra->bra_status))); } } /**/ ipmip6pm_lma_free_bri(ctx, proc, bri); if (pbuf != IP_NULL) ipmip_pktbuf_free(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip); return IPMIP_TIMER_RET_DONE; /* do not reschedule the timeout */ } IP_GLOBAL void ipmip6pm_lma_revoke(Ipmip6pm_context_t *ctx, Ipmip_proc_t *proc, Ipmip6pm_lma_t *lma, Ip_u8 reason) { Ipmip6pm_br_t *bri = ipcom_calloc(1, sizeof(*bri)); ipmip_context_id_lock(ctx->bri); ipmip_context_id_allocate(ctx->bri, bri); ipmip_context_id_unlock(ctx->bri); bri->reason = reason? reason : IPMIP6_MH_BRI_TRIGGER_PER_PEER_POLICY; bri->xmits = 0; bri->lock = lma->ifp->ipmip6.iff_lock; bri->opaque = IP_NULL; bri->flags = IPMIP6_MH_BRI_FLAG_G; bri->inet = IP_FALSE; bri->reply = (Ipmip_timer_func_t)ipmip6pm_lma_bri_reply_locked; bri->lma = lma; bri->flags |= IPMIP6_MH_BRI_FLAG_P; ipcom_list_insert_last(&lma->bri, &bri->next); ipmip6pm_mh_bri_send(ctx, proc, IP_NULL, bri); /* Need the ACK before we do the processing */ ipmip_timer_request(&ctx->ipmip6.ipmip, proc, &bri->retransmit, lma->ifp->ipmip6.iff_lock, (Ipmip_timer_func_t)ipmip6pm_lma_bri_timeout, bri, IP_NULL, lma->ifp->MINDelayBRIs); } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* **************************************************************************** * END OF FILE **************************************************************************** */