/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipmipfa_agent.c,v $ $Revision: 1.24 $ * $Source: /home/interpeak/CVSRoot/ipmipfa/src/ipmipfa_agent.c,v $ * $Author: jhorteli $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Joel Hortelius ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipmipfa_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipmipfa.h" #include "ipmipfa_h.h" #include "ipmipfa_if.h" #include "ipmipfa_agent.h" #include "ipmipfa_reg.h" #include "ipmipfa_node.h" #include "ipmipfa_llh.h" #include "ipmipfa_pktbuf.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipmipfa_agent_advert_input *=========================================================================== * Description: ICMP advertisement input. * Parameters: data - The context * proc - Current process * pbuf - The packet buffer * Returns: * */ IP_STATIC void ipmipfa_agent_advert_input(Ipmipfa_data_t *data, Ipmip_proc_t *proc, Ipmipfa_pktbuf_t *pbuf) { Ipmip4_icmp_advert_t *advpkt; int adv_len; Ipmip4_icmp_ext_advert_t *ext = IP_NULL; Ip_u8 *ext_start; int ext_len; int i; if (IPMIP_BUFFER_SIZE(&pbuf->ipmip4.ipmip.packet) < 8) { IPCOM_LOG2(WARNING, "ipcom_recvmsg(RtAdv) failed from '%s', short packet [length = %d]", ipmip_ntop_sa(proc, pbuf->ipmip4.ipmip.srcinfo.src), IPMIP_BUFFER_SIZE(&pbuf->ipmip4.ipmip.packet)); return; } /* Cache lengths and pointers */ advpkt = IPMIP_BUFFER_PTR(&pbuf->ipmip4.ipmip.packet); adv_len = sizeof(Ipmip4_icmp_advert_t) + (advpkt->adv_num_addrs * advpkt->adv_addr_entry_size * 4); /* Payload length check */ if (IPMIP_BUFFER_SIZE(&pbuf->ipmip4.ipmip.packet) < (unsigned)adv_len) { IPCOM_LOG3(WARNING, "ipcom_recvmsg(RtAdv) failed from '%s', short packet [length = %d/%d]", ipmip_ntop_sa(proc, pbuf->ipmip4.ipmip.srcinfo.src), IPMIP_BUFFER_SIZE(&pbuf->ipmip4.ipmip.packet), adv_len); return; } ext_start = ipmip_buffer_offset(&pbuf->ipmip4.ipmip.packet, adv_len); ext_len = IPMIP_BUFFER_SIZE(&pbuf->ipmip4.ipmip.packet) - adv_len; i = ipmip4_msgext_icmp_parse(proc, pbuf->iff.priv->iff.ipmip.ifname, pbuf->ipmip4.ipmip.srcinfo.src->sin.sin_addr.s_addr, &pbuf->ipmip4.ext.icmp, ext_start, ext_len); if (i != IPMIP4_REG_EXT_SUCCESS) return; if (pbuf->ipmip4.ext.icmp.agent == IP_NULL) return; /* Cast to something usable */ ext = (Ipmip4_icmp_ext_advert_t *) pbuf->ipmip4.ext.icmp.agent; if (IP_BIT_ISFALSE(IP_GET_NTOHS(&ext->flags), IPMIP4_ICMP_ADVEXT_FLAG_FOREIGN_AGENT)) return; if (pbuf->ipmip4.ext.icmp.lla.found) { /* Input the advertisement to the LLH system */ ipmipfa_llh_advert_input(data, proc, pbuf); } } /* *=========================================================================== * ipmipfa_agent_solicitation_process *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipmipfa_agent_solicitation_process(int event, Ipmipfa_data_t *data, Ipmip_proc_t *proc, Ipmipfa_pktbuf_t *pbuf, Ipmipfa_node_t *node) { if (event == IPMIP_TIMER_EVENT_RUN) { if (pbuf->ipmip4.ext.icmp.lla.found) { /* Feed this solicitation to the LLH system */ ipmipfa_llh_solicit_input(data, proc, node, pbuf); } else { IPCOM_LOG4(DEBUG2, "----> Received RtSol ", pbuf->iff.pub->ipmip.ifname, ipmip_ntop_sa(proc, pbuf->ipmip4.ipmip.srcinfo.src), ipmip_ntop_sa(proc, &pbuf->ipmip4.ipmip.srcinfo.dst->dst), ipmip_ntop_lladdr(proc, pbuf->ipmip4.ipmip.srcinfo.lladdr)); if (pbuf->ipmip4.ipmip.srcinfo.ttl != 1) { IPCOM_LOG2(WARNING, "ipcom_recvmsg(RtSol) failed from '%s', ttl != 1 (%d)", ipmip_ntop_sa(proc, pbuf->ipmip4.ipmip.srcinfo.src), pbuf->ipmip4.ipmip.srcinfo.ttl); } else { /* Received local RtSol, may be L2-LU */ if (node != IP_NULL) { /* This is a LU trigger for ST */ ipmipfa_llh_L2_LU(data, proc, node, pbuf->ipmip4.ipmip.srcinfo.src->sin.sin_addr.s_addr, pbuf->iff.priv, pbuf->ipmip4.ipmip.srcinfo.lladdr); } /* Write a directed output */ ipmipfa_agent_advert_output(data, proc, pbuf->iff.priv, node, &pbuf->ipmip4.ipmip.srcinfo.src->sin.sin_addr.s_addr, pbuf->ipmip4.ipmip.srcinfo.lladdr, IP_FALSE); } } } else { /* Aborted or cancelled */ } ipmip_pktbuf_release(&data->ipmip4.ipmip, proc, &pbuf->ipmip4.ipmip); return IPMIP_TIMER_RET_DONE; } /* *=========================================================================== * ipmipfa_agent_solicitation_input *=========================================================================== * Description: Process received ICMP Agent solicitations. * Parameters: pbuf - The packet buffer * Returns: * */ IP_STATIC Ip_bool ipmipfa_agent_solicitation_input(Ipmipfa_data_t *data, Ipmip_proc_t *proc, Ipmipfa_pktbuf_t *pbuf) { Ip_u8 *ext_start; int ext_len; int i; Ipmip4_icmp_solicit_t *solpkt; int sol_len; /* Solicitation packet */ solpkt = IPMIP_BUFFER_PTR(&pbuf->ipmip4.ipmip.packet); sol_len = sizeof(Ipmip4_icmp_solicit_t); if (IPMIP_BUFFER_SIZE(&pbuf->ipmip4.ipmip.packet) < (unsigned)sol_len) { IPCOM_LOG3(WARNING, "ipcom_recvmsg(RtSol) failed from '%s', short packet [length = %d/%d]", ipmip_ntop_sa(proc, pbuf->ipmip4.ipmip.srcinfo.src), IPMIP_BUFFER_SIZE(&pbuf->ipmip4.ipmip.packet), sol_len); return IP_FALSE; } /* Extension part */ ext_start = ipmip_buffer_offset(&pbuf->ipmip4.ipmip.packet, sol_len); ext_len = IPMIP_BUFFER_SIZE(&pbuf->ipmip4.ipmip.packet) - sol_len; i = ipmip4_msgext_icmp_parse(proc, pbuf->iff.pub->ipmip.ifname, pbuf->ipmip4.ipmip.srcinfo.src->sin.sin_addr.s_addr, &pbuf->ipmip4.ext.icmp, ext_start, ext_len); if (i != IPMIP4_REG_EXT_SUCCESS) return IP_FALSE; /* Found any extensions? */ if (pbuf->ipmip4.ext.icmp.lla.found) { /* Verify that it's unicast */ if (pbuf->ipmip4.ipmip.srcinfo.dst->dst.sin.sin_addr.s_addr != pbuf->iff.pub->ifaddr) { IPCOM_LOG4(DEBUG, "----> Received PrRtSol not unicast, discarding ", pbuf->iff.pub->ipmip.ifname, ipmip_ntop_sa(proc, pbuf->ipmip4.ipmip.srcinfo.src), ipmip_ntop_sa(proc, &pbuf->ipmip4.ipmip.srcinfo.dst->dst), ipmip_ntop_lladdr(proc, pbuf->ipmip4.ipmip.srcinfo.lladdr)); return IP_FALSE; } } /* Invoke through the node manager */ ipmipfa_node_findbykey(data, proc, pbuf->iff.priv, pbuf->ipmip4.ipmip.srcinfo.src->sin.sin_addr.s_addr, 0, IP_NULL, pbuf->ipmip4.ipmip.srcinfo.lladdr, &pbuf->ipmip4.ipmip.timeout, (Ipmip_timer_func_t)ipmipfa_agent_solicitation_process, pbuf); return IP_TRUE; } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipmipfa_agent_solicitation_build *=========================================================================== * Description: Build solicitation * Parameters: adv - The buffer to build the solicitation in. * lla - Optional extension * Returns: IP_TRUE on success * */ IP_GLOBAL Ip_bool ipmipfa_agent_solicitation_build(Ipmip_buffer_t *adv, Ipmip_lladdr_t *lladdr) { Ipmip4_icmp_solicit_t solpkt; Ipmip4_icmp_solicit_t *solpktp; ipcom_memset(&solpkt, 0, sizeof(solpkt)); solpkt.icmp_type = IPMIP4_ICMP_ROUTER_SOLICITATION; ipmip_buffer_append(adv, &solpkt, sizeof(solpkt)); /* Copy extension in case of PrRtSol */ if (lladdr != IP_NULL) ipmip4_lla_llh_lladdr_buffer_icmp_append(adv, lladdr); solpktp = IPMIP_BUFFER_PTR(adv); solpktp->icmp_checksum = ipcom_in_checksum(IPMIP_BUFFER_DATA(adv), IPMIP_BUFFER_SIZE(adv)); return IP_TRUE; } /* *=========================================================================== * ipmipfa_agent_challenge_build *=========================================================================== * Description: Create a challenge extension. * Parameters: iff - The interface to build the challenge for. * node - The node if it's directed to it. * buf - The extension we're building. * icmp - Is it an ICMP or UDP extension. * Returns: * */ IP_GLOBAL void ipmipfa_agent_challenge_build(Ipmipfa_data_t *data, Ipmip_proc_t *proc, Ipmipfa_iff_t *iff, Ipmipfa_node_t *node, Ipmip_buffer_t *buf, Ip_bool icmp) { Ip_u8 *challenge; int off; /* Unicast variety? */ if (node) { /* Generate a new UNICAST challenge if required */ if (IP_BIT_ISFALSE(node->flags, IPMIPFA_NODE_CHALLENGE_UNICAST_VALID)) { /* Generate a unicast challenge */ ipmipfa_if_challenge_generate(data, proc, iff, node->unicast_challenge); IP_BIT_SET(node->flags, IPMIPFA_NODE_CHALLENGE_UNICAST_VALID); } /* Store */ challenge = node->unicast_challenge; } else { /* Get the greatest & latest */ int i = iff->current_challenge % IPMIPFA_CONF_CHALLENGE_WINDOW; challenge = iff->challenge_window[i]; } /**/ off = ipmip_buffer_append(buf, IP_NULL, IPMIPFA_CONF_CHALLENGE_EXTSIZE); if (off != -1) { Ipmip4_msgext_uhdr_t *cha_ext = ipmip_buffer_offset(buf, off); cha_ext->shdr.type = icmp? IPMIP4_ICMP_EXT_TYPE_AGENT_ADV_CHALLENGE : IPMIP4_EXT_TYPE_AGENT_ADV_CHALLENGE; cha_ext->shdr.length = IPMIPFA_CONF_CHALLENGE_LEN; /* Copy the challenge */ ipcom_memcpy(IPMIP4_MSGEXT_SHDR_EXT_DATA(cha_ext), challenge, IPMIPFA_CONF_CHALLENGE_LEN); } } /* *=========================================================================== * ipmipfa_agent_advert_build *=========================================================================== * Description: Create an advertisement. * Parameters: data - Context * proc - Process * adv - The buffer to create the advertisement in. * iff - The interface for which we create the buffer. * lla_ext - Append the interfaces LLA extension * revoke - Revocation request. * Returns: IP_TRUE on success. * */ IP_GLOBAL Ip_bool ipmipfa_agent_advert_build(Ipmipfa_data_t *data, Ipmip_proc_t *proc, Ipmip_buffer_t *adv, Ipmipfa_iff_t *iff, Ipmipfa_node_t *node, Ip_bool lla_ext, Ip_bool revoke) { Ipmip4_icmp_advert_t advpkt; Ipmip4_icmp_advert_t *advpktp; Ipmip4_icmp_ext_advert_t adv_ext; Ip_u16 adv_seq; ipcom_memset(&advpkt, 0, sizeof(advpkt)); ipcom_memset(&adv_ext, 0, sizeof(adv_ext)); /* ICMP advertisement header */ advpkt.icmp_type = IPMIP4_ICMP_AGENT_ADVERTISEMENT; advpkt.icmp_code = data->ipnet_is_router ? IPMIP4_ICMP_CODE_GENERIC_ROUTER : IPMIP4_ICMP_CODE_AGENT_ONLY; advpkt.icmp_checksum = 0; advpkt.adv_num_addrs = 0; advpkt.adv_addr_entry_size = 2; advpkt.adv_lifetime = ip_htons(iff->adv_lifetime); ipmip_buffer_append(adv, &advpkt, sizeof(advpkt)); /* Store */ if (!revoke) { adv_seq = iff->adv_seq; if (++iff->adv_seq == 0) /* Roll-over prevention, so it does not look like we have rebooted */ iff->adv_seq = 256; } else { adv_seq = 0; } /* Add mobility agent advertisement extension */ adv_ext.type = IPMIP4_ICMP_EXT_TYPE_MOBILITY_AGENT_ADV; adv_ext.length = 6 + 4; /* 4=size of a careof address, possibly more of those here. +4n */ adv_ext.seq = ip_htons(adv_seq); adv_ext.reg_lifetime = ip_htons(iff->lifetime); adv_ext.flags = ip_ntohs(IPMIP4_ICMP_ADVEXT_FLAG_REG_REQUIRED | IPMIP4_ICMP_ADVEXT_FLAG_FOREIGN_AGENT); /**/ if (IP_BIT_ISTRUE(data->shutdown, IPMIPFA_SHUTDOWN_ADV)) adv_ext.flags |= ip_ntohs(IPMIP4_ICMP_ADVEXT_FLAG_BUSY); /* Set flags depending on configuration */ if (iff->tunnel.enabled[IPMIP_TUNNELTYPE_GRE]) adv_ext.flags |= ip_ntohs(IPMIP4_ICMP_ADVEXT_FLAG_GRE_ENCAP); if (iff->tunnel.enabled[IPMIP_TUNNELTYPE_MINENC]) adv_ext.flags |= ip_ntohs(IPMIP4_ICMP_ADVEXT_FLAG_MINIMAL_ENCAP); if (iff->tunnel.reverse != IPMIP_SYSVAR_MODE_DISABLED) adv_ext.flags |= ip_ntohs(IPMIP4_ICMP_ADVEXT_FLAG_REV_TUNNEL); if (iff->nat_t != 0) adv_ext.flags |= ip_ntohs(IPMIP4_ICMP_ADVEXT_FLAG_UDP_TUNNELING); if (iff->ext.revocation) adv_ext.flags |= ip_ntohs(IPMIP4_ICMP_ADVEXT_FLAG_REVOCATION); adv_ext.coa[0] = iff->iff.ifaddr; ipmip_buffer_append(adv, &adv_ext, sizeof(adv_ext)); if (iff->ext.challenge) ipmipfa_agent_challenge_build(data, proc, iff, node,adv, IP_TRUE); /* Append extension */ if (lla_ext == IP_TRUE) { /**/ ipmip4_lla_llh_lladdr_buffer_icmp_append(adv, iff->iff.ipmip.lladdr); } advpktp = IPMIP_BUFFER_PTR(adv); advpktp->icmp_checksum = ipcom_in_checksum(IPMIP_BUFFER_DATA(adv), IPMIP_BUFFER_SIZE(adv)); IPCOM_LOG5(DEBUG2, "<--- Creating %s #%d on %s ", lla_ext? "PrRtAdv" : "RtAdv", adv_seq, iff->iff.ipmip.ifname, ipmip_ntop_inet(proc, iff->iff.ifaddr), iff->adv_lifetime); /* Return advlen */ return IP_TRUE; } /* *=========================================================================== * ipmipfa_agent_advert_output *=========================================================================== * Description: Write an ordinary multicast/unicast advertisement * Parameters: data - Context * proc - Process * iff - The interface to send the advertisment from. * node - Unicast destination node. * dst - If specified, unicast dst address * lla - If specified, unicast LLA address * Returns: * */ IP_GLOBAL void ipmipfa_agent_advert_output(Ipmipfa_data_t *data, Ipmip_proc_t *proc, Ipmipfa_iff_t *iff, Ipmipfa_node_t *node, Ip_addr4 *dst, Ipmip_lladdr_t *lladdr, Ip_bool revoke) { Ipmip_buffer_t adv; ipmip_buffer_init(&adv); /* Create the advertisement */ if (ipmipfa_agent_advert_build(data, proc, &adv, iff, node, IP_FALSE, revoke)) { /* If dst is zero, specify it as the advertisement address */ if (dst == IP_NULL) dst = &iff->adv_address; /* Write the request */ if (ipmip4_pkt_icmp_write(&data->ipmip4, proc, &adv, iff->iff.ifaddr, *dst, &iff->iff, lladdr) != IP_TRUE) { IPCOM_LOG2(ERR, "----> Failed to send %s local RtAdv ", (dst == &iff->adv_address)? "unicast" : "multicast", iff->iff.ipmip.ifname); } ipmip_buffer_release(&adv); } } /* *=========================================================================== * ipmipfa_adv_recv *=========================================================================== * Description: Handles incoming advertisements or solicitations * Parameters: data - Context * proc - Current process. * pbuf - Packet buffer * Returns: * */ IP_GLOBAL void ipmipfa_adv_recv(struct Ipmipfa_data *data, struct Ipmip_proc *proc, Ipmipfa_pktbuf_t *pbuf) { if (data->conf == IP_NULL) return; if (IP_BIT_ISTRUE(data->shutdown, IPMIPFA_SHUTDOWN_ADV)) return; if (pbuf->ipmip4.ipmip.srcinfo.lladdr != IP_NULL) { Ipcom_icmp_header *icmph = IPMIP_BUFFER_PTR(&pbuf->ipmip4.ipmip.packet); /* Truncated packet? */ if (IPMIP_BUFFER_SIZE(&pbuf->ipmip4.ipmip.packet) < IPCOM_ICMP_HEADER_LENGTH) goto done_out; if (pbuf->ipmip4.ipmip.srcinfo.dst->dst.sin.sin_addr.s_addr == ip_htonl(IP_INADDR_ALLHOSTS_GROUP) || pbuf->ipmip4.ipmip.srcinfo.dst->dst.sin.sin_addr.s_addr == ip_htonl(IP_INADDR_ALLRTRS_GROUP) || pbuf->ipmip4.ipmip.srcinfo.dst->dst.sin.sin_addr.s_addr == ip_htonl(IP_INADDR_ALLMA_GROUP) || pbuf->ipmip4.ipmip.srcinfo.dst->dst.sin.sin_addr.s_addr == 0xffffffff) { /* Must find the interface */ if ((pbuf->iff.pub = (Ipmip4_if_t *)ipmip_if_findbyindex(&data->conf->if_head, pbuf->ipmip4.ipmip.srcinfo.dst->ifindex)) == IP_NULL) goto done_out; } else if ((pbuf->iff.pub = ipmip4_if_findbyaddr(&data->conf->if_head, pbuf->ipmip4.ipmip.srcinfo.dst->dst.sin.sin_addr.s_addr)) == IP_NULL) goto done_out; ipmip_lladdr_sanitize(pbuf->iff.pub->ipmip.lladdr, pbuf->ipmip4.ipmip.srcinfo.lladdr); switch (icmph->type) { case IPMIP4_ICMP_ROUTER_SOLICITATION: if (IP_BIT_ISFALSE(data->shutdown, IPMIPFA_SHUTDOWN_MUTE)) if (ipmipfa_agent_solicitation_input(data, proc, pbuf)) return; case IPMIP4_ICMP_AGENT_ADVERTISEMENT: ipmipfa_agent_advert_input(data, proc, pbuf); break; } } else { /* Failure */ IPCOM_LOG0(ERR, "NO MAC"); } done_out: /* Free the packet buffer */ ipmip_pktbuf_release(&data->ipmip4.ipmip, proc, &pbuf->ipmip4.ipmip); } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* **************************************************************************** * END OF FILE **************************************************************************** */