/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipmip6ha_mag.c,v $ $Revision: 1.14 $ * $Source: /home/interpeak/CVSRoot/ipmip6ha/src/ipmip6ha_mag.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 **************************************************************************** */ #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 "ipmip6ha.h" #include "ipmip6ha_h.h" #include "ipmip6ha_node.h" #include "ipmip6ha_nemo.h" #include "ipmip6ha_attr_h.h" #include "ipmip6ha_cli.h" #include "ipmip6ha_ifevent.h" #include "ipmip6ha_mh.h" #include "ipmip6ha_icmp.h" #include "ipmip6ha_if.h" #include "ipmip6ha_route.h" #include "ipmip6ha_pktbuf.h" #include "ipmip6ha_mag.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ struct Ipmip6ha_mag_cb { Ipmip6ha_context_t *ctx; Ipmip6ha_if_t *ife; }; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ IP_STATIC unsigned ipmip6ha_mag_key_hash_nai(struct Ip_in6_addr *endpoint) { return ipcom_hash_update(endpoint, sizeof(*endpoint), 0); } IP_STATIC unsigned ipmip6ha_mag_obj_hash_nai(Ipmip6ha_mag_t *mag) { return ipmip6ha_mag_key_hash_nai(&mag->endpoint); } IP_STATIC Ip_bool ipmip6ha_mag_cmp_nai(Ipmip6ha_mag_t *mag, struct Ip_in6_addr *endpoint) { return IP_IN6_ARE_ADDR_EQUAL(&mag->endpoint, endpoint); } IP_STATIC void ipmip6ha_mag_free_bri(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_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); if (bri->match) ipcom_free(bri->match); /**/ ipcom_free(bri); } } IP_STATIC void ipmip6ha_mag_delete_locked(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag) { Ipmip6ha_br_t *bri; IPMIP6_EVENT_IF(DEBUG, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "Deleted MAG %s", ipmip_ntop_inet6(proc, &mag->endpoint, &mag->ifp->ipmip6.iff))); /* Clear MAG */ ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &mag->timer); ipcom_hash_remove(mag->ifp->mag_table, mag); while ((bri = IPCOM_LIST_FIRST(&mag->bri)) != IP_NULL) ipmip6ha_mag_free_bri(ctx, proc, bri); ipcom_free(mag); } IP_STATIC Ip_bool ipmip6ha_mag_send_heartbeat_response(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, struct Ip_in6_addr *endpoint, Ipmip6ha_if_t *ifp, 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 */ ipmip6_pkt_pktinfo_src_opt_add(pkt, &ifp->ifaddr.prefix); /* Send */ if ((err = ipmip6_pkt_send(&ctx->ipmip6, proc, ctx->ipmip6.pkt->fd_mh, pkt, endpoint)) != 0) { IPCOM_LOG2(INFO, "%s: Error sending MH HeartBeat = %x", ctx->ipmip6.ipmip.name, err); return IP_FALSE; } return IP_TRUE; } IP_STATIC Ip_bool ipmip6ha_mag_send_heartbeat_request_locked(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag) { /**/ 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); mag->sequence++; mag->unacked++; IP_SET_HTONL(&bh->bh_seq, mag->sequence); /**/ ipmip6ha_route_direct_temporary(ctx, proc, mag->ifp, &mag->endpoint, &mag->tunnel_endpoint, mag->tunnel_endpoint_type, mag->port); /**/ /* Force it to go out using the CoA */ ipmip6_pkt_pktinfo_src_opt_add(pkt, &mag->ifp->ifaddr.prefix); /* Send */ if ((err = ipmip6_pkt_send(&ctx->ipmip6, proc, ctx->ipmip6.pkt->fd_mh, pkt, &mag->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 ipmip6ha_mag_send_heartbeat_timeout_locked(int event, Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag, void *unused) { IPMIP_UNUSED_PARAMETER(unused); if (event == IPMIP_TIMER_EVENT_RUN) { if (mag->unacked > mag->ifp->HeartBeatMissAllowed) { IPMIP6_EVENT_IF(WARNING, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "MAG %s seems DEAD - no HeartBeat response", ipmip_ntop_inet6(proc, &mag->endpoint, IP_NULL))); } IPMIP6_EVENT_IF(DEBUG2, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "Sending HeartBeat request to %s", ipmip_ntop_inet6(proc, &mag->endpoint, IP_NULL))); /**/ ipmip6ha_mag_send_heartbeat_request_locked(ctx, proc, mag); ipmip_timer_request(&ctx->ipmip6.ipmip, proc, &mag->timer, mag->ifp->ipmip6.iff_lock, (Ipmip_timer_func_t)ipmip6ha_mag_send_heartbeat_timeout_locked, mag, IP_NULL, mag->ifp->HeartBeatInterval * 1000); } return IPMIP_TIMER_RET_DONE; /* do not reschedule the timeout */ } IP_STATIC int ipmip6ha_mag_send_heartbeat_unsolicited_locked(int event, Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag, void *unused) { IPMIP_UNUSED_PARAMETER(unused); if (event == IPMIP_TIMER_EVENT_RUN) { ipmip6ha_route_direct_temporary(ctx, proc, mag->ifp, &mag->endpoint, &mag->tunnel_endpoint, mag->tunnel_endpoint_type, mag->port); ipmip6ha_mag_send_heartbeat_response(ctx, proc, &mag->endpoint, mag->ifp, 0, IPMIP6_MH_HEARTBEAT_FLAG_U); } return IPMIP_TIMER_RET_DONE; /* do not reschedule the timeout */ } IP_STATIC Ipmip6ha_mag_t * ipmip6ha_mag_new_locked(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, struct Ipmip6ha_if *ifp, struct Ip_in6_addr *addr, Ip_bool permanent) { Ipmip6ha_mag_t *mag = ipcom_calloc(1, sizeof(*mag)); ipcom_memcpy(&mag->endpoint, addr, sizeof(*addr)); mag->ifp = ifp; mag->permanent = permanent; ipcom_list_init(&mag->bri); /* Clear MAG */ ipcom_hash_add(ifp->mag_table, mag); /* */ IPMIP6_EVENT_IF(DEBUG, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "Added MAG %s", ipmip_ntop_inet6(proc, &mag->endpoint, &ifp->ipmip6.iff))); return mag; } IP_STATIC void ipmip6ha_mag_release_locked(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag) { /**/ if (--mag->references == 0) { ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &mag->timer); if (!mag->permanent) ipmip6ha_mag_delete_locked(ctx, proc, mag); } } IP_STATIC void ipmip6ha_mag_delete_cb(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag, void *arg1, void *arg2) { IPMIP_UNUSED_PARAMETER(arg1); IPMIP_UNUSED_PARAMETER(arg2); /* Delete */ ipmip6ha_mag_delete_locked(ctx, proc, mag); } IP_STATIC void ipmip6ha_mag_reference_locked(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag) { /**/ if (mag) { if (++mag->references == 1 && mag->ifp->HeartBeatEnabled && IP_BIT_ISFALSE(mag->flags, IPMIP6HA_MAG_HEARTBEAT_DISABLED)) ipmip_timer_request(&ctx->ipmip6.ipmip, proc, &mag->timer, mag->ifp->ipmip6.iff_lock, (Ipmip_timer_func_t)ipmip6ha_mag_send_heartbeat_timeout_locked, mag, IP_NULL, 0); } } IP_STATIC Ipmip6ha_mag_t * ipmip6ha_mag_get_locked(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, struct Ipmip6ha_if *ifp, struct Ip_in6_addr *endpoint, unsigned flags) { Ipmip6ha_mag_t *mag; mag = ipcom_hash_get(ifp->mag_table, endpoint); if (mag == IP_NULL && IP_BIT_ISSET(flags, IPMIP6HA_MAG_CREATE)) mag = ipmip6ha_mag_new_locked(ctx, proc, ifp, endpoint, IP_BIT_ISTRUE(flags, IPMIP6HA_MAG_PERMANENT)); /**/ ipmip6ha_mag_reference_locked(ctx, proc, mag); return mag; } IP_STATIC void ipmip6ha_mag_configure_foreach(struct Ipmip_proc *proc, char *name, char *subkey, const char *value, struct Ipmip6ha_mag_cb *cb) { struct Ip_in6_addr endpoint; Ipmip6ha_mag_t *mag; Ip_u32 inet_endpoint = 0; #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 mag = ipmip6ha_mag_get_locked(cb->ctx, proc, cb->ife, &endpoint, IPMIP6HA_MAG_CREATE|IPMIP6HA_MAG_PERMANENT); if (mag) { int enabled = ipmip_sysvar_cmd_str_to_key(ipmip_sysvar_bool_ops, value); if (inet_endpoint != 0) { mag->tunnel_endpoint.ipv4.s_addr = inet_endpoint; mag->tunnel_endpoint_type = IPMIP6_MIB_InetAddressType_ipv4; } if (enabled == 0) IP_BIT_SET(mag->flags, IPMIP6HA_MAG_HEARTBEAT_DISABLED); if (IP_BIT_ISFALSE(mag->flags, IPMIP6HA_MAG_HEARTBEAT_DISABLED)) { IPMIP6_EVENT_IF(DEBUG, proc, ipmip_log_event_add(&cb->ctx->ipmip6.ipmip, proc, "Scheduling Unsolicited Heartbeat for MAG %s", ipmip_ntop_inet6(proc, &mag->endpoint, &cb->ife->ipmip6.iff))); ipmip_timer_cancel(&cb->ctx->ipmip6.ipmip, proc, &mag->timer); ipmip_timer_request(&cb->ctx->ipmip6.ipmip, proc, &mag->timer, cb->ife->ipmip6.iff_lock, (Ipmip_timer_func_t)ipmip6ha_mag_send_heartbeat_unsolicited_locked, mag, IP_NULL, 0); } /* Decrease this reference */ --mag->references; } } IP_STATIC void ipmip6ha_mag_configure_dump_foreach(Ipmip6_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag, Ipmip_buffer_t *buffer, Ipmip6ha_if_t *ife) { IPMIP_UNUSED_PARAMETER(ctx); /**/ if (mag->permanent) { #ifdef IPMIP_USE_INET if (mag->tunnel_endpoint_type == IPMIP6_MIB_InetAddressType_ipv4) { ipmip_cli_config_printf(buffer, "ipmip6ha.interface.%s.LMA.MAG.%s/%s=%s", ife->ipmip6.iff.ifname, ipmip_ntop_inet6(proc, &mag->endpoint, IP_NULL), ipmip_ntop_inet(proc, mag->tunnel_endpoint.ipv4.s_addr), IP_BIT_ISSET(mag->flags, IPMIP6HA_MAG_HEARTBEAT_DISABLED)? "disabled" : "enabled"); } else #endif ipmip_cli_config_printf(buffer, "ipmip6ha.interface.%s.LMA.MAG.%s=%s", ife->ipmip6.iff.ifname, ipmip_ntop_inet6(proc, &mag->endpoint, IP_NULL), IP_BIT_ISSET(mag->flags, IPMIP6HA_MAG_HEARTBEAT_DISABLED)? "disabled" : "enabled"); } } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ IP_GLOBAL Ipmip6ha_mag_t * ipmip6ha_mag_get(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, struct Ipmip6ha_if *ifp, struct Ip_in6_addr *endpoint, unsigned flags) { Ipmip6ha_mag_t *mag; ipmip_timer_lock_acquire(&ctx->ipmip6.ipmip, proc, ifp->ipmip6.iff_lock); mag = ipmip6ha_mag_get_locked(ctx, proc, ifp, endpoint, flags); ipmip_timer_lock_release(&ctx->ipmip6.ipmip, proc, ifp->ipmip6.iff_lock); return mag; } IP_GLOBAL void ipmip6ha_mag_release(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag) { if (mag != IP_NULL) { Ipmip_timer_lock_t *lock = ipmip_timer_lock_acquire(&ctx->ipmip6.ipmip, proc, mag->ifp->ipmip6.iff_lock); ipmip6ha_mag_release_locked(ctx, proc, mag); ipmip_timer_lock_release(&ctx->ipmip6.ipmip, proc, lock); } } IP_GLOBAL void ipmip6ha_mag_reference(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag) { if (mag != IP_NULL) { Ipmip_timer_lock_t *lock = ipmip_timer_lock_acquire(&ctx->ipmip6.ipmip, proc, mag->ifp->ipmip6.iff_lock); ipmip6ha_mag_reference_locked(ctx, proc, mag); ipmip_timer_lock_release(&ctx->ipmip6.ipmip, proc, lock); } } IP_GLOBAL void ipmip6ha_mag_set_tunnel(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag, union Ipmip6_mib_InetAddress *tunnel_endpoint, Ip_u32 tunnel_endpoint_type, Ip_u16 port) { /* If we have a pointer; the mag cant change endpoint under us due to reference */ if (!ipmip6_util_InetAddress_cmp(mag->tunnel_endpoint_type, &mag->tunnel_endpoint, tunnel_endpoint_type, tunnel_endpoint) || port != mag->port) { ipmip_timer_lock_acquire(&ctx->ipmip6.ipmip, proc, mag->ifp->ipmip6.iff_lock); ipcom_memcpy(&mag->tunnel_endpoint, tunnel_endpoint, sizeof(*tunnel_endpoint)); mag->tunnel_endpoint_type = tunnel_endpoint_type; mag->port = port; ipmip_timer_lock_release(&ctx->ipmip6.ipmip, proc, mag->ifp->ipmip6.iff_lock); } } IP_GLOBAL void ipmip6ha_mag_configure(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, struct Ipmip6ha_if *ife) { struct Ipmip6ha_mag_cb cb; ife->HeartBeatInterval = ipmip_sysvar_u32(ipmip_sysvar_u32(60, "ipmip6ha.LMA.HeartBeatInterval"), "ipmip6ha.interface.%s.LMA.HeartBeatInterval", ife->ipmip6.iff.ifname); ife->HeartBeatMissAllowed = ipmip_sysvar_u32(ipmip_sysvar_u32(3, "ipmip6ha.LMA.MissingHeartBeatsAllowed"), "ipmip6ha.interface.%s.LMA.MissingHeartBeatsAllowed", ife->ipmip6.iff.ifname); ife->HeartBeatEnabled = ipmip_sysvar_bool(ipmip_sysvar_bool(IP_FALSE, "ipmip6ha.LMA.HeartBeatEnabled"), "ipmip6ha.interface.%s.LMA.HeartBeatEnabled", ife->ipmip6.iff.ifname); ife->mag_table= ipcom_hash_new((Ipcom_hash_obj_func) ipmip6ha_mag_obj_hash_nai, (Ipcom_hash_key_func) ipmip6ha_mag_key_hash_nai, (Ipcom_hash_cmp_func) ipmip6ha_mag_cmp_nai); cb.ctx = ctx; cb.ife = ife; /* Read The permanent MAG's */ ipmip_sysvar_foreach(proc, (Ipmip_sysvar_foreach_t) ipmip6ha_mag_configure_foreach, &cb, "ipmip6ha.LMA.MAG.*"); ipmip_sysvar_foreach(proc, (Ipmip_sysvar_foreach_t) ipmip6ha_mag_configure_foreach, &cb, "ipmip6ha.interface.%s.LMA.MAG.*", ife->ipmip6.iff.ifname); } IP_GLOBAL void ipmip6ha_mag_unconfigure(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, struct Ipmip6ha_if *ifp) { if (ifp->mag_table) { ipmip_util_hash_foreach(&ctx->ipmip6.ipmip, proc, ifp->mag_table, (Ipmip_util_hash_foreach_t)ipmip6ha_mag_delete_cb, IP_NULL, IP_NULL); ipcom_hash_delete(ifp->mag_table); ifp->mag_table = IP_NULL;; } } IP_GLOBAL void ipmip6ha_mag_configure_dump(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_if_t *ife, Ipmip_buffer_t *buffer) { ipmip_cli_config_printf(buffer, "ipmip6ha.interface.%s.LMA.HeartBeatInterval=%d", ife->ipmip6.iff.ifname, ife->HeartBeatInterval); ipmip_cli_config_printf(buffer, "ipmip6ha.interface.%s.LMA.MissingHeartBeatsAllowed=%d", ife->ipmip6.iff.ifname, ife->HeartBeatMissAllowed); ipmip_cli_config_printf(buffer, "ipmip6ha.interface.%s.LMA.HeartBeatEnabled=%s", ife->ipmip6.iff.ifname, ife->HeartBeatEnabled? "enabled" : "disabled"); /* Dump permanent rules */ ipmip_util_hash_foreach(&ctx->ipmip6.ipmip, proc, ife->mag_table, (Ipmip_util_hash_foreach_t)ipmip6ha_mag_configure_dump_foreach, buffer, ife); } IP_STATIC void ipmip6ha_mag_attr_get_foreach(Ipmip6_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag, Ipmip_buffer_t *buffer, Ipmip6ha_if_t *ife) { Ip_size_t rta = ipmip_attr_begin(buffer, IPMIP6HA_CLI_ATTR_MAG); IPMIP_UNUSED_PARAMETER(ctx); IPMIP_UNUSED_PARAMETER(proc); /* */ ipmip_attr_put_inet6(buffer, IPMIP6HA_CLI_ATTR_MAG_ADDR_V6, &mag->endpoint); #ifdef IPMIP_USE_INET ipmip_attr_put_inet(buffer, IPMIP6HA_CLI_ATTR_MAG_ADDR_V4, mag->tunnel_endpoint.ipv4.s_addr); #endif ipmip_attr_put_str(buffer, IPMIP6HA_CLI_ATTR_MAG_IFNAME, ife->ipmip6.iff.ifname); ipmip_attr_put_u32(buffer, IPMIP6HA_CLI_ATTR_MAG_REFERENCES, mag->references); ipmip_attr_put_u32(buffer, IPMIP6HA_CLI_ATTR_MAG_HEARTBEAT, (Ip_u32)IP_BIT_ISFALSE(mag->flags, IPMIP6HA_MAG_HEARTBEAT_DISABLED)); ipmip_attr_end(buffer, rta); } IP_GLOBAL void ipmip6ha_mag_attr_get(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_if_t *ife, Ipmip_buffer_t *buffer) { /* Dump permanent rules */ ipmip_util_hash_foreach(&ctx->ipmip6.ipmip, proc, ife->mag_table, (Ipmip_util_hash_foreach_t)ipmip6ha_mag_attr_get_foreach, buffer, ife); } IP_STATIC Ip_bool ipmip6ha_mag_heartbeat_restart_counter_update(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag, 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 = (mag->have_restart_counter && restart_counter != mag->restart_counter); mag->restart_counter = restart_counter; mag->have_restart_counter = IP_TRUE; return changed; } return IP_FALSE; } IP_STATIC Ip_bool ipmip6ha_mag_heartbeat_restart_counter(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag, Ip_bool unsolicited, Ipmip6_mh_mo_heartbeat_restart_counter_option_t *mo) { if (ipmip6ha_mag_heartbeat_restart_counter_update(ctx, proc, mag, mo) || unsolicited) { /* Should there be any sort of action? */ return IP_TRUE; } return IP_FALSE; } IP_GLOBAL Ip_bool ipmip6ha_mag_heartbeat_input(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_pktbuf_t *pbuf) { if (pbuf->iff_transport.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 MAG %s -- replying with %x", ipmip_ntop_inet6(proc, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, IP_NULL), ctx->ipmip6.restart_counter)); /* */ ipmip6ha_route_update_temporary(ctx, proc, pbuf, IP_TRUE); ipmip6ha_mag_send_heartbeat_response(ctx, proc, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, pbuf->iff_transport.priv, seq, 0); } else { /* Reply */ Ipmip6ha_mag_t *mag = ipmip6ha_mag_get(ctx, proc, pbuf->iff_transport.priv, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, 0); if (mag != 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 from MAG %s", ipmip_ntop_inet6(proc, &mag->endpoint, IP_NULL))); ipmip6ha_mag_heartbeat_restart_counter(ctx, proc, mag, IP_TRUE, pbuf->ipmip6.mo.restart_option); } else { if (seq == mag->sequence) { mag->unacked = 0; if (ipmip6ha_mag_heartbeat_restart_counter(ctx, proc, mag, 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 MAG %s", mag->restart_counter, ipmip_ntop_inet6(proc, &mag->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 MAG %s", mag->restart_counter, ipmip_ntop_inet6(proc, &mag->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 MAG %s", mag->restart_counter, ipmip_ntop_inet6(proc, &mag->endpoint, IP_NULL))); #endif } } else { IPMIP6_EVENT_IF(INFO, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "SOLICITED OUT-OF-SEQUENCE HeartBeat Response from MAG %s", ipmip_ntop_inet6(proc, &mag->endpoint, IP_NULL))); } } ipmip6ha_mag_release(ctx, proc, mag); } else { IPMIP6_EVENT_IF(INFO, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "SOLICITED HeartBeat Response from unknown MAG %s", ipmip_ntop_inet6(proc, &pbuf->ipmip6.ipmip.srcinfo.src->sin6.sin6_addr, IP_NULL))); } } } else { /**/ ipmip6ha_route_update_temporary(ctx, proc, pbuf, IP_TRUE); return IP_FALSE; } /* */ ipmip_pktbuf_free(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip); return IP_TRUE; } IP_GLOBAL Ip_bool ipmip6ha_mag_binding_error_input(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_pktbuf_t *pbuf, Ipmip6ha_mag_t *mag) { 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) { /* */ if ((mag->flags & (IPMIP6HA_MAG_HEARTBEAT_SEEN|IPMIP6HA_MAG_HEARTBEAT_DISABLED)) == 0 && mag->unacked != 0) { IPMIP6_EVENT_IF(WARNING, proc, ipmip_log_event_add(&ctx->ipmip6.ipmip, proc, "Binding Error received from MAG %s, HeartBeat disabled", ipmip_ntop_inet6(proc, &mag->endpoint, IP_NULL))); IP_BIT_SET(mag->flags, IPMIP6HA_MAG_HEARTBEAT_DISABLED); ipmip_timer_cancel(&ctx->ipmip6.ipmip, proc, &mag->timer); } } /* */ ipmip_pktbuf_free(&ctx->ipmip6.ipmip, proc, &pbuf->ipmip6.ipmip); return IP_TRUE; } IP_STATIC void ipmip6ha_mag_revoke_node(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_node_t *node, Ipmip6ha_br_t *bri, 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 == bri->mag) { Ipmip6_mh_mo_nai_t *nai = (Ipmip6_mh_mo_nai_t *)node->nai_mn; /* Does the NAI match? */ if (bri->match == IP_NULL || ipmip_util_strnnmatch(bri->match, bri->match_len, (char *)&nai->nai[0], (Ip_size_t) (nai->hdr.len - 1))) { /* 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, "global revocation acknowledged", 0); } } } } IP_STATIC int ipmip6ha_mag_bri_timeout(int event, Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_br_t *bri, void *extarg) { IPMIP_UNUSED_PARAMETER(extarg); if (event == IPMIP_TIMER_EVENT_RUN) { if (++bri->xmits > bri->mag->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)ipmip6ha_mag_revoke_node, bri, IP_NULL); ipmip_context_id_unlock(ctx->idents); /**/ ipmip6ha_mag_free_bri(ctx, proc, bri); } else { /* Retransmit */ ipmip6ha_mh_bri_send(ctx, proc, IP_NULL, bri); return IPMIP_TIMER_RET_REINSERT; } } return IPMIP_TIMER_RET_DONE; } IP_STATIC int ipmip6ha_mag_bri_reply_locked(int event, Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_br_t *bri, Ipmip6ha_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)ipmip6ha_mag_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, "MAG %s unable to revoke, due to error %s", ipmip_ntop_inet6(proc, &bri->mag->endpoint, IP_NULL), ipmip6_mh_bra_error_str(bra->bra_status))); } } /**/ ipmip6ha_mag_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 ipmip6ha_mag_revoke(Ipmip6ha_context_t *ctx, Ipmip_proc_t *proc, Ipmip6ha_mag_t *mag, Ip_u8 reason, const char *match) { Ipmip6ha_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 = (Ip_u8) (reason? reason : (match? IPMIP6_MH_BRI_TRIGGER_REVOKING_NODE_LOCAL_POLICY : IPMIP6_MH_BRI_TRIGGER_PER_PEER_POLICY)); bri->xmits = 0; bri->lock = mag->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)ipmip6ha_mag_bri_reply_locked; bri->mag = mag; bri->flags |= IPMIP6_MH_BRI_FLAG_P; /**/ if (match) { bri->match = ipcom_strdup(match); bri->match_len = ipcom_strlen(bri->match); } ipcom_list_insert_last(&mag->bri, &bri->next); ipmip6ha_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, mag->ifp->ipmip6.iff_lock, (Ipmip_timer_func_t)ipmip6ha_mag_bri_timeout, bri, IP_NULL, mag->ifp->MINDelayBRIs); } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* **************************************************************************** * END OF FILE **************************************************************************** */