/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_RCPL8_2024030303 $ $RCSfile: ipnet_rtnetlink_neigh.c,v $ $Revision: 1.16.10.6 $ * $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_rtnetlink_neigh.c,v $ * $Author: lwang7 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation Jonas Rendel ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * Contains netlink handlers for managing Ipv4 and Ipv6 addresses. */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif #include "ipnet_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #ifdef IPNET_USE_NETLINK #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include "ipnet_h.h" #include "ipnet_ctrl.h" #include "ipnet_route.h" #include "ipnet_netlink_h.h" #include "ipnet_rtnetlink_h.h" #include "ipcom_sock.h" #ifdef IPCOM_USE_INET6 #include "ipcom_sock6.h" #include "ipnet_ip6.h" #endif #ifdef IP_PORT_LKM #include "ipcom_lkm.h" /* Needed for Linux PPP interface config */ #endif /* IP_PORT_LKM */ #include "ipnet_eth.h" #ifdef IPCOM_FORWARDER_NAE #include "ipcom_forwarder.h" #endif #ifdef IPNET_USE_VRRP #include "ipnet_vrrp.h" #endif /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ typedef struct Ipnet_rtnetlink_neigh_family { struct { Ip_ptrdiff_t *vr; Ip_ptrdiff_t *neigh; Ip_ptrdiff_t *done; Ip_ptrdiff_t aborted; Ip_ptrdiff_t cont; } state; /* Requested */ struct Ip_nlmsghdr *nlmsg; int family; /* Buffering */ Ipnet_netlink_mem_t *mem; } Ipnet_rtnetlink_neigh_family_t; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ #ifdef IPNET_USE_VRRP /* *=========================================================================== * ipnet_eth_vrrp_dtor *=========================================================================== * Description: Destructor for VRRP neighbors. * Parameters: neigh - neighbor entry that is used to respond to ARP * requests for the VRRP MAC. * Returns: * */ IP_STATIC void ipnet_eth_vrrp_dtor(const Ipnet_neigh *neigh) { Ipnet_netif *netif = neigh->netif; switch(neigh->domain) { case IP_AF_INET: #ifdef IPCOM_USE_INET if (ipnet_ip4_get_addr_type2(neigh->addr.in.s_addr, netif) == IPNET_ADDR_TYPE_UNICAST) /* * Owner address, inform link local nodes about the MAC * change */ ipnet_arp_request(netif, 0, neigh->addr.in.s_addr, IP_TRUE, IP_NULL, IP_NULL); return; #endif default: return; } } /* *=========================================================================== * ipnet_eth_vrrp_add_addr *=========================================================================== * Description: Checks if the neighbor should be used for VRRP and * adds the VRRP address in that case. * Parameters: neigh - a neighbor entry. * Returns: 0 = success * <0 = error code * */ IP_STATIC int ipnet_eth_vrrp_add_addr(Ipnet_neigh *neigh) { Ip_u8 vrid = neigh->lladdr[5]; if ((ipnet_eth_get_vrrp_mac_addr_type(neigh->lladdr) == IPNET_NOT_VRRP_MAC_ADDRESS) || vrid == 0 || ipnet_neigh_is_blackhole(neigh)) /* * Should not be added as a VRRP address. */ return 0; /* set VRRP address to be publish */ ipnet_neigh_set_publ(neigh, IP_TRUE); return ipnet_vrrp_add_addr(neigh, vrid); } /* *=========================================================================== * ipnet_eth_vrrp_del_addr *=========================================================================== * Description: Removes a VRRP address * Parameters: neigh - neighbor used to respond to ARP/ND request on the * VRRP address. * Returns: * */ IP_STATIC int ipnet_eth_vrrp_del_addr(Ipnet_neigh *neigh) { if (ipnet_eth_get_vrrp_mac_addr_type(neigh->lladdr) == IPNET_NOT_VRRP_MAC_ADDRESS) return -IP_ERRNO_EINVAL; return ipnet_vrrp_del_addr(neigh, neigh->lladdr[5], ipnet_eth_vrrp_dtor); } #endif /* *=========================================================================== * ipnet_rtnetlink_neigh_foreach *=========================================================================== * Description: Called for each neighbor entry during dumping of * neighbors via netlink. * Parameters: neigh - a neighbor entry * user_arg - pointer the the netlink dump parameters. * Returns: * */ IP_STATIC void ipnet_rtnetlink_neigh_foreach(Ipnet_neigh *neigh, void *user_arg) { Ipnet_rtnetlink_neigh_family_t *param = user_arg; Ip_u16 vr = neigh->netif->vr_index; int ret = 0; /* * This traversal is already aborted */ if (param->state.aborted) return; /* * Is this the right domain? AF_UNSPEC means we should dump all * domain */ if (param->family != IP_AF_UNSPEC && neigh->key_domain != param->family) return; /* * Check if specific VR shall be dumped. */ if (IP_BIT_ISFALSE(param->nlmsg->nlmsg_flags, IP_NLM_F_VR_UNSPEC) && (param->mem->vr != IPCOM_VR_ANY) && (param->mem->vr != vr)) return; #ifdef IPCOM_USE_INET6 if (neigh->domain == IP_AF_INET6 && ipnet_rtnetlink_ip6_neigh_not_local(neigh) == IP_FALSE) return; #endif /* * If this is a trailer message buffer, make sure it * starts dumping where the last message ended */ if (param->state.cont) { Ipnet_neigh *prev_neigh = (Ipnet_neigh*)(*param->state.neigh); /* * Verify current state */ if (*param->state.vr != vr) return; if (prev_neigh != neigh) /* * Skip all entries until we get back to the entry we * should continue from. */ return; } /* * Reset the continue variable; we're on the move again */ param->state.cont = 0; /* * Verify this particular NC entry */ #ifdef IPCOM_USE_INET if (neigh->domain == IP_AF_INET) ret = ipnet_rtnetlink_ip4_neigh_fill_info(param->mem, neigh, param->nlmsg->nlmsg_seq, IP_RTM_NEWNEIGH, IP_NLM_F_MULTI); #endif #ifdef IPCOM_USE_INET6 if (neigh->domain == IP_AF_INET6) ret = ipnet_rtnetlink_ip6_neigh_fill_info(param->mem, neigh, param->nlmsg->nlmsg_seq, IP_RTM_NEWNEIGH, IP_NLM_F_MULTI); #endif if (ret < 0) goto aborted; return; aborted: param->state.aborted = 1; *param->state.neigh = (Ip_ptrdiff_t)neigh; *param->state.vr = (Ip_ptrdiff_t)vr; } /* *=========================================================================== * ipnet_rtnetlink_neigh_event *=========================================================================== * Description: Status of a neighbor has changed create a netlink * event to inform any listeners about it * Parameters: neigh - neighbor entry that generated the event * event - RTM_NEWNEIGH or RTM_DELNEIGH * Returns: - * */ IP_STATIC void ipnet_rtnetlink_neigh_event(Ipnet_neigh *neigh, int event) { Ipcom_pkt *pkt = IP_NULL; Ipnet_netlink_mem_t mem; int ret = -1; if (!ipnet_netlink_pkt_alloc(&pkt, IP_NULL, IP_TRUE, &mem, 512)) return; /* * Set the VR-index */ pkt->vr_index = mem.vr = neigh->netif->vr_index; #ifdef IPCOM_USE_INET if (neigh->domain == IP_AF_INET) { ret = ipnet_rtnetlink_ip4_neigh_fill_info (&mem, neigh, 0, event, 0); } #endif #ifdef IPCOM_USE_INET6 if (neigh->domain == IP_AF_INET6) { if (ipnet_rtnetlink_ip6_neigh_not_local(neigh)) ret = ipnet_rtnetlink_ip6_neigh_fill_info (&mem, neigh, 0, event, 0); } #endif if (ret < 0) ipcom_pkt_free(pkt); else { pkt->end += ret; /* * We're done; do report this pkt */ ipnet_rtnetlink_broadcast(pkt, IP_RTNLGRP_NEIGH); } } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *========================================================================== * ipnet_rtnetlink_getroute_family *=========================================================================== * Description: Get interface info via NETLINK * Parameters: data - Message payload. * len - Length of message. * cb - NETLINK callback entry * * Returns: 0 - Success * <0 - Failure * */ IP_GLOBAL int ipnet_rtnetlink_neigh_get_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlh, struct Ip_rtattr **rta, int family) { struct Ip_ndmsg *ndm = IP_NLMSG_DATA(nlh); int ret = -IP_ERRNO_EAFNOSUPPORT; Ip_u16 vr; Ipnet_netif *netif; Ipnet_neigh *neigh; /* * Destination address required */ if (rta[IP_NDA_DST-1] == IP_NULL) return -IP_ERRNO_EINVAL; if (mem->vr == IPCOM_VR_ANY) return -IP_ERRNO_EINVAL; vr = ipnet_rtnetlink_vr(rta[IP_NDA_VR - 1], mem->vr); netif = ipnet_if_indextonetif(vr, (Ip_u32) ndm->ndm_ifindex); if (netif == IP_NULL) /* * No network interface with the specified index */ return -IP_ERRNO_ENXIO; neigh = ipnet_neigh_get(family, IP_RTA_DATA(rta[IP_NDA_DST-1]), netif, IPNET_NEIGH_NO_CREATE); if (IP_NULL == neigh) return -IP_ERRNO_ENOENT; switch (family) { #ifdef IPCOM_USE_INET case IP_AF_INET: ret = ipnet_rtnetlink_ip4_neigh_fill_info(mem, neigh, nlh->nlmsg_seq, IP_RTM_NEWNEIGH, 0); break; #endif #ifdef IPCOM_USE_INET6 case IP_AF_INET6: ret = ipnet_rtnetlink_ip6_neigh_fill_info(mem, neigh, nlh->nlmsg_seq, IP_RTM_NEWNEIGH, 0); break; #endif } ipnet_neigh_release(neigh); return ret; } /* *========================================================================== * ipnet_rtnetlink_neigh_dump_family *=========================================================================== * Description: Get interface info via NETLINK * Parameters: data - Message payload. * len - Length of message. * cb - NETLINK callback entry * * Returns: 0 - Success * <0 - Failure * */ IP_GLOBAL int ipnet_rtnetlink_neigh_dump_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, int family, Ip_ptrdiff_t *args) { Ipnet_rtnetlink_neigh_family_t param; Ip_ptrdiff_t *done = &args[0]; Ip_ptrdiff_t *s_vr = &args[1]; Ip_ptrdiff_t *s_neigh = &args[2]; Ip_ptrdiff_t *s_continue = &args[3]; param.nlmsg = nlmsg; param.family = family; param.mem = mem; if (*done) goto exit_out; param.state.aborted = 0; param.state.done = done; param.state.neigh = s_neigh; param.state.vr = s_vr; param.state.cont = *s_continue; ipnet_neigh_foreach(ipnet_rtnetlink_neigh_foreach, ¶m); /* * Are we aborted? */ *s_continue = param.state.aborted; /* * If not aborted; we're done */ if (!param.state.aborted) *done = 1; exit_out: return (int) param.mem->used; } /* *=========================================================================== * ipnet_rtnetlink_neigh_delneigh_family *=========================================================================== * Description: Marks a specific neighbor as dead. Dead neighbors will * be deleted as soon as their reference count reaches zero. * Parameters: * Returns: * */ IP_GLOBAL int ipnet_rtnetlink_neigh_delneigh_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta, int family) { Ip_u16 vr; Ipnet_netif *netif; struct Ip_ndmsg *ndm = IP_NLMSG_DATA(nlmsg); Ipnet_neigh *neigh; /* * Destination address required */ if (rta[IP_NDA_DST-1] == IP_NULL) return -IP_ERRNO_EINVAL; if (mem->vr == IPCOM_VR_ANY) return -IP_ERRNO_EINVAL; vr = ipnet_rtnetlink_vr(rta[IP_NDA_VR - 1], mem->vr); netif = ipnet_if_indextonetif(vr, (Ip_u32) ndm->ndm_ifindex); if (netif == IP_NULL) /* * No network interface with the specified index */ return -IP_ERRNO_ENXIO; if (netif->vr_index != vr) /* * Wrong VR */ return -IP_ERRNO_EINVAL; /* * Get the matching neighbor entry, create it if necessary. */ neigh = ipnet_neigh_get(family, IP_RTA_DATA(rta[IP_NDA_DST-1]), netif, IPNET_NEIGH_NO_CREATE); if (neigh == IP_NULL) /* * No matching neighbor present */ return -IP_ERRNO_ESRCH; #ifdef IPNET_USE_VRRP (void)ipnet_eth_vrrp_del_addr(neigh); #endif #ifdef IPCOM_FORWARDER_NAE if (neigh->state == IPNET_ND_REACHABLE && ipnet_neigh_is_external(neigh) && ipcom_nae_is_control()) { /* * Go STALE; were deleting an external REACHABLE neighbour on the mgmt plane. */ ipnet_neigh_set_external(neigh, IP_FALSE); if (!ipnet_neigh_nae_verify_use(neigh, IPNET_ND_DEAD)) { #ifdef IPCOM_FORWARDER_NAE ipcom_forwarder_fw_neigh_external_delete++; #endif ipnet_neigh_set_state(neigh, IPNET_ND_DEAD); } } else { #ifdef IPCOM_FORWARDER_NAE ipcom_forwarder_fw_neigh_external_delete++; #endif ipnet_neigh_set_state(neigh, IPNET_ND_DEAD); } #else ipnet_neigh_set_state(neigh, IPNET_ND_DEAD); #endif ipnet_neigh_release(neigh); return 0; } /* *=========================================================================== * ipnet_rtnetlink_neigh_newneigh_family *=========================================================================== * Description: Creates a new neighbor entry as a response to a * netlink RTM_NEWNEIGH message * * Parameters: * Returns: * */ IP_GLOBAL int ipnet_rtnetlink_neigh_newneigh_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta, int family) { Ipnet_netif *netif; Ipnet_neigh *neigh; int nd_state; int ret = 0; Ip_u16 vr; struct Ip_ndmsg *ndm = IP_NLMSG_DATA(nlmsg); void *link_addr = IP_NULL; /* * Destination address required */ if (rta[IP_NDA_DST-1] == IP_NULL) return -IP_ERRNO_EINVAL; if (mem->vr == IPCOM_VR_ANY) return -IP_ERRNO_EINVAL; vr = ipnet_rtnetlink_vr(rta[IP_NDA_VR - 1], mem->vr); netif = ipnet_if_indextonetif(vr, (Ip_u32) ndm->ndm_ifindex); if (netif == IP_NULL) /* * No network interface with the specified index */ return -IP_ERRNO_ENXIO; #ifndef IPCOM_FORWARDER_NAE neigh = ipnet_neigh_get(family, IP_RTA_DATA(rta[IP_NDA_DST-1]), netif, IPNET_NEIGH_NO_CREATE); if (IP_NULL == neigh) { if (IP_BIT_ISFALSE(nlmsg->nlmsg_flags, IP_NLM_F_CREATE)) return -IP_ERRNO_ENOENT; } else { if (IP_BIT_ISSET(nlmsg->nlmsg_flags, IP_NLM_F_EXCL)) { ipnet_neigh_release(neigh); return -IP_ERRNO_EEXIST; } if (IP_BIT_ISSET(nlmsg->nlmsg_flags, IP_NLM_F_REPLACE) && IP_BIT_ISFALSE(ndm->ndm_flags, IP_NTF_USE)) { ipnet_neigh_set_state(neigh, IPNET_ND_DEAD); ipnet_neigh_release(neigh); neigh = IP_NULL; } } /* * Get the matching neighbor entry, create it if necessary. */ if (IP_NULL == neigh) neigh = ipnet_neigh_get(family, IP_RTA_DATA(rta[IP_NDA_DST-1]), netif, IPNET_NEIGH_CAN_CREATE); #else neigh = ipnet_neigh_get(family, IP_RTA_DATA(rta[IP_NDA_DST-1]), netif, IPNET_NEIGH_CAN_CREATE); #endif if (neigh == IP_NULL) return -IP_ERRNO_ENOMEM; if (IP_BIT_ISFALSE(ndm->ndm_flags, IP_NTF_USE)) { if (rta[IP_NDA_LLADDR-1]) /* * Neighbors link layer address */ link_addr = IP_RTA_DATA(rta[IP_NDA_LLADDR-1]); else if (IP_BIT_ISSET(ndm->ndm_flags, IP_NTF_PROXY)) { /* * Proxy ARP entry. Use Link layer address of local netif */ link_addr = netif->ipcom.link_addr; ipnet_neigh_set_publ(neigh, IP_TRUE); } nd_state = ipnet_rtnetlink_neigh_nud2nc(ndm->ndm_state); if (nd_state == IPNET_ND_INCOMPLETE && ipnet_neigh_is_complete(neigh)) { #ifdef IPCOM_FORWARDER_NAE if (!ipcom_nae_is_control() || ipnet_neigh_is_external(neigh)) { /* * This state will end up being incomplete - which is * allowed under NAE, since we do not drive our own * states */ ipcom_forwarder_fw_invalid_incomplete++; ipnet_neigh_invalidate(neigh); } else #endif { /* * Don't allow the entry to be pushed back to invalid * state. */ ret = -IP_ERRNO_EINVAL; goto done; } } if (link_addr && ipcom_memcmp(neigh->lladdr, link_addr, neigh->netif->ipcom.link_addr_size) != 0) { #ifdef IPCOM_FORWARDER_NAE if (ipnet_neigh_is_complete(neigh)) { ipcom_forwarder_fw_invalid_lladdr++; ipnet_neigh_invalidate(neigh); } #endif /* Must update */ ipnet_neigh_set_lladdr(neigh, link_addr); } /* * No link layer address specified, the neighbour is incomplete, * and we're setting it to something that indicates reachability */ if (IP_NULL == link_addr && !ipnet_neigh_is_complete(neigh) && nd_state != IPNET_ND_INCOMPLETE) { ret = -IP_ERRNO_EINVAL; goto done; } if (IP_BIT_ISSET(ndm->ndm_flags, IP_NTF_EXTERNAL)) ipnet_neigh_set_external(neigh, IP_TRUE); if (IP_BIT_ISTRUE(ndm->ndm_state, IP_NUD_PERMANENT)) { ipnet_neigh_set_report(neigh); ipnet_neigh_set_perm(neigh, IP_TRUE); ipnet_neigh_set_state(neigh, IPNET_ND_REACHABLE); } else { ipnet_neigh_set_perm(neigh, IP_FALSE); /* Change State */ #ifdef IPCOM_FORWARDER_NAE if (ipcom_nae_is_control() && ipnet_neigh_is_external(neigh) && nd_state == IPNET_ND_STALE && neigh->state == IPNET_ND_REACHABLE) { if (!ipnet_neigh_nae_verify_use(neigh, IPNET_ND_STALE)) ipnet_neigh_set_state(neigh, IPNET_ND_STALE); } else #endif { if (nd_state != (int) neigh->state) { ipnet_neigh_set_report(neigh); ipnet_neigh_set_state(neigh, nd_state); } } } #ifndef IPCOM_FORWARDER_NAE if (link_addr != IP_NULL && IP_BIT_ISSET(ndm->ndm_flags, IP_NTF_GRAT)) /* * Gratuitous ARP/NDP was requested */ ipnet_neigh_dad(neigh); #endif #ifdef IPNET_USE_VRRP (void)ipnet_eth_vrrp_add_addr(neigh); #endif } else { /* Have this entry be 'updated' */ switch (neigh->state) { default: case IPNET_ND_REACHABLE: case IPNET_ND_PROBE: case IPNET_ND_INCOMPLETE: case IPNET_ND_DELAY: break; case IPNET_ND_STALE: ipnet_neigh_set_state(neigh, IPNET_ND_PROBE); break; case IPNET_ND_PENDING: ipnet_neigh_set_state(neigh, IPNET_ND_INCOMPLETE); break; } } done: ipnet_neigh_release(neigh); return ret; } /* *=========================================================================== * ipnet_rtnetlink_neigh_nc2nud *=========================================================================== * Description: Converts neighbor states from IPNET to NETLINK standard. * Parameters: neigh - a IPNET neighbor entry. * Returns: NETLINK neighbor state * */ IP_GLOBAL Ip_u16 ipnet_rtnetlink_neigh_nc2nud(Ipnet_neigh *neigh) { Ip_u16 flags = 0; if (ipnet_neigh_is_perm(neigh)) { flags |= IP_NUD_PERMANENT; return flags; } switch (neigh->state) { case IPNET_ND_PENDING: case IPNET_ND_INCOMPLETE: if (neigh->remaining_ucast_probes == 0 && neigh->remaining_mcast_probes == 0) flags |= IP_NUD_FAILED; else flags |= IP_NUD_INCOMPLETE; break; case IPNET_ND_REACHABLE: flags |= IP_NUD_REACHABLE; break; case IPNET_ND_STALE: flags |= IP_NUD_STALE; break; case IPNET_ND_DELAY: flags |= IP_NUD_DELAY; break; case IPNET_ND_PROBE: flags |= IP_NUD_PROBE; break; default: /* * Set all other states to the NUD_NONE dummy state. */ flags = IP_NUD_NONE; break; } return flags; } /* *=========================================================================== * ipnet_rtnetlink_neigh_nud2nc *=========================================================================== * Description: Converts neighbor states from NETLINK standard to IPNET * Parameters: ndm_state - NETLINK neighbor state * Returns: IPNET neighbor state. * */ IP_GLOBAL int ipnet_rtnetlink_neigh_nud2nc(Ip_u16 ndm_state) { if (IP_BIT_ISSET(ndm_state, IP_NUD_PERMANENT|IP_NUD_REACHABLE)) return IPNET_ND_REACHABLE; if (IP_BIT_ISSET(ndm_state, IP_NUD_STALE)) return IPNET_ND_STALE; if (IP_BIT_ISSET(ndm_state, IP_NUD_PROBE)) return IPNET_ND_PROBE; if (IP_BIT_ISSET(ndm_state, IP_NUD_DELAY)) return IPNET_ND_DELAY; return IPNET_ND_INCOMPLETE; } /* *=========================================================================== * ipnet_rtnetlink_neigh_change *=========================================================================== * Description: Called for any kind of change in the neighbor state machine. * There is no need for a specific neigh_add() function * anymore. This function is called at creation time with * state set to INCOMPLETE * Parameters: neigh - a neighbor entry * Returns: * */ IP_GLOBAL void ipnet_rtnetlink_neigh_change(Ipnet_neigh *neigh) { ipnet_rtnetlink_neigh_event(neigh, IP_RTM_NEWNEIGH); } /* *=========================================================================== * ipnet_rtnetlink_neigh_del *=========================================================================== * Description: Called when a neighbor is about to be removed from the stack. * Parameters: neigh - neighbor entry that is about to be deleted. * Returns: * */ IP_GLOBAL void ipnet_rtnetlink_neigh_del(Ipnet_neigh *neigh) { ipnet_rtnetlink_neigh_event(neigh, IP_RTM_DELNEIGH); } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ #endif /* IPNET_USE_NETLINK */ /* **************************************************************************** * END OF FILE **************************************************************************** */