/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipmip4_netlink_kern.c,v $ $Revision: 1.4.10.3 $ * $Source: /home/interpeak/CVSRoot/ipmip4/src/ipmip4_netlink_kern.c,v $ * $Author: jsheng $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Joel Hortelius ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif #include "ipnet_config.h" #if defined(IPNET) /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include "ipnet_h.h" #include "ipnet.h" #include #include /* **************************************************************************** * 4 DEFINES **************************************************************************** */ #define IPMIP4_NETLINK_KERN_U32(rtap, val) ((rtap)[(val) - 1]? IP_GET_32ON8(IP_RTA_DATA((rtap)[(val) - 1])) : 0) /* **************************************************************************** * 5 TYPES **************************************************************************** */ typedef struct Ipmip4_netlink_kern_handle { Ipmip_netlink_kern_handle_t handle; Ipmip4_netlink_kern_statistics_t links[IPMIP_LINK_MAX]; } Ipmip4_netlink_kern_handle_t; typedef struct Ipmip4_ipnet_route_func_info { struct Ipnet_route_func_info info; /**/ Ipmip_netlink_route_flow_info_t flow; Ip_bool have_gre_key; Ip_u32 gre_key; Ip_u32 link; Ip_u32 flags; Ipmip4_netlink_kern_handle_t *handle; } Ipmip4_ipnet_route_func_info_t; /* **************************************************************************** * X STATIC FUNCTIONS **************************************************************************** */ IP_STATIC void * ipmip4_ipnet_route_func_base(struct Ipnet_dst_cache_struct *dst, struct Ipcom_pkt_struct *pkt) { Ipmip4_ipnet_route_func_info_t *rtinfo = (Ipmip4_ipnet_route_func_info_t*)dst->mip.flow_aux; if (rtinfo->handle != IP_NULL) { rtinfo->handle->links[rtinfo->link].mip_s_octets += pkt->end - pkt->start; rtinfo->handle->links[rtinfo->link].mip_s_pkts += 1; rtinfo->handle->links[rtinfo->link].mip_s_stamp_sec = ipnet->sec_now; rtinfo->handle->links[rtinfo->link].mip_s_stamp_msec = ipnet->msec_now - ipnet->msec_last; } return rtinfo; } IP_STATIC int ipmip4_ipnet_route_func_gw_link_TX(struct Ipnet_dst_cache_struct *dst, Ipcom_pkt *pkt) { Ipmip4_ipnet_route_func_info_t *rtinfo = ipmip4_ipnet_route_func_base(dst, pkt); int ret; /* Set GRE key */ if (rtinfo->have_gre_key) ipcom_pkt_set_info(pkt, IPNET_PKT_INFO_GRE_KEY_OUTPUT, sizeof(Ip_u32), &rtinfo->gre_key); ret = (*dst->mip.flow_tx) (dst, pkt); if (IP_UNLIKELY(ret < 0 && IP_BIT_ISSET(rtinfo->flags, IP_WRMOB4_NEW_TYPE_SILENT))) ret = 0; return ret; } IP_STATIC void ipmip4_ipnet_dst_cache_func_gw_link_TX_init(Ip_bool init, struct Ipnet_dst_cache_struct *dst) { if (init) { #if 0 dst->mip.flow_tx = dst->tx; /**/ dst->tx = (Ipnet_dst_cache_tx_func)ipmip4_ipnet_route_func_gw_link_TX; #else dst->mip.flow_tx = dst->real_tx; ipnet_dst_cache_set_tx_handler(dst, (Ipnet_dst_cache_tx_func)ipmip4_ipnet_route_func_gw_link_TX); #endif } else { /* Free the AUX data */ ipcom_free(dst->mip.flow_aux); } } IP_STATIC int ipmip4_ipnet_route_func_gw_link_RX(struct Ipnet_dst_cache_struct *dst, Ipcom_pkt *pkt) { Ipmip4_ipnet_route_func_info_t *rtinfo = ipmip4_ipnet_route_func_base(dst, pkt); /* Set GRE key */ if (rtinfo->have_gre_key) ipcom_pkt_set_info(pkt, IPNET_PKT_INFO_GRE_KEY_OUTPUT, sizeof(Ip_u32), &rtinfo->gre_key); return (*dst->mip.flow_rx) (dst, pkt); } IP_STATIC void ipmip4_ipnet_dst_cache_func_gw_link_RX_init(Ip_bool init, struct Ipnet_dst_cache_struct *dst) { if (init) { #if 0 dst->mip.flow_rx = dst->rx; dst->rx = (Ipnet_dst_cache_rx_func)ipmip4_ipnet_route_func_gw_link_RX; #else dst->mip.flow_rx = dst->real_rx; ipnet_dst_cache_set_rx_handler(dst, (Ipnet_dst_cache_rx_func)ipmip4_ipnet_route_func_gw_link_RX); #endif } else { /* Free the AUX data */ ipcom_free(dst->mip.flow_aux); } } IP_STATIC int ipmip4_netlink_kern_delmip_link_TX_RX(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta) { int ret; struct Ip_rtattr **mattr = IP_NULL; Ip_u32 domain; Ip_u32 gwdomain; Ipnet_rtnetlink_key_t ukey; void *mask = IP_NULL; struct Ip_sockaddr *gw = IP_NULL; union Ip_sockaddr_union ugw; (void)nlmsg; if (!rta[IP_MIP_ENCAP - 1]) return -IP_ERRNO_EINVAL; ret = ipnet_netlink_parse(IP_WRMOB4_ROUTE_MAX, IP_RTA_DATA(rta[IP_MIP_ENCAP - 1]), IP_RTA_PAYLOAD(rta[IP_MIP_ENCAP - 1]), &mattr); if (ret < 0) return ret; if (!mattr || !mattr[IP_WRMOB4_ROUTE_DST - 1]) { ret = -IP_ERRNO_EINVAL; goto done; } /* Create the mask */ if (IP_RTA_PAYLOAD(mattr[IP_WRMOB4_ROUTE_DST - 1]) == sizeof(struct Ip_in_addr)) domain = IP_AF_INET; else { #ifdef IPCOM_USE_INET6 domain = IP_AF_INET6; #else ret = -IP_ERRNO_EINVAL; goto done; #endif } /* Create the mask */ ret = ipnet_rtnetlink_route_key_setup(domain, &ukey, IPMIP4_NETLINK_KERN_U32(mattr, IP_WRMOB4_ROUTE_IFINDEX), &mask, IPMIP4_NETLINK_KERN_U32(mattr, IP_WRMOB4_ROUTE_DST_LEN), mattr[IP_WRMOB4_ROUTE_DST - 1]); if (ret < 0) { IPCOM_LOG0(ERR, "Unable to parse route, data"); goto done; } gwdomain = domain; if (mattr[IP_WRMOB4_ROUTE_GW - 1]) { if (IP_RTA_PAYLOAD(mattr[IP_WRMOB4_ROUTE_GW - 1]) == sizeof(struct Ip_in_addr)) gwdomain = IP_AF_INET; else { if (IP_RTA_PAYLOAD(mattr[IP_WRMOB4_ROUTE_GW - 1]) == sizeof(struct Ip_sockaddr_dl)) { gw = (struct Ip_sockaddr *)IP_RTA_DATA(mattr[IP_WRMOB4_ROUTE_GW - 1]); gwdomain = IP_AF_LINK; } else { #ifdef IPCOM_USE_INET6 gwdomain = IP_AF_INET6; #else ret = -IP_ERRNO_EINVAL; goto done; #endif } } } if (gwdomain != IP_AF_LINK) { ret = ipnet_rtnetlink_route_gw_setup(gwdomain, IPMIP4_NETLINK_KERN_U32(mattr, IP_WRMOB4_ROUTE_IFINDEX), &ugw, &gw, mattr[IP_WRMOB4_ROUTE_GW - 1]); if (ret < 0) { IPCOM_LOG0(ERR, "Unable to parse route data"); goto done; } } /* Do a referenced removal */ ret = ipnet_route_delete2(domain, mem->vr, IPCOM_ROUTE_TABLE_GET(IPMIP4_NETLINK_KERN_U32(mattr, IP_WRMOB4_ROUTE_TABLE)), &ukey, mask, gw, IPMIP4_NETLINK_KERN_U32(mattr, IP_WRMOB4_ROUTE_IFINDEX), 0, 0, IP_TRUE); done: if (mattr) ipcom_free(mattr); return ret; } IP_STATIC int ipmip4_netlink_kern_delmip_handle(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta) { int ret; struct Ip_rtattr **mattr = IP_NULL; (void)nlmsg; if (!rta[IP_MIP_ENCAP - 1]) return -IP_ERRNO_EINVAL; ret = ipnet_netlink_parse(IP_WRMOB4_ROUTE_MAX, IP_RTA_DATA(rta[IP_MIP_ENCAP - 1]), IP_RTA_PAYLOAD(rta[IP_MIP_ENCAP - 1]), &mattr); if (ret < 0) return ret; if (!mattr || !mattr[IP_WRMOB4_ROUTE_HANDLE - 1]) { ret = -IP_ERRNO_EINVAL; goto done; } /* Delete the handle */ ipmip_netlink_kern_handle_free(mem->vr, IP_RTA_DATA(mattr[IP_WRMOB4_ROUTE_HANDLE - 1])); done: if (mattr) ipcom_free(mattr); return ret; } IP_STATIC int ipmip4_netlink_kern_delmip_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta, int family) { struct Ip_mipmsg *msg = IP_NLMSG_DATA(nlmsg); (void)family; (void)mem; switch (msg->mip_type) { case IP_WRMOB4_DEL_LINK_TX: case IP_WRMOB4_DEL_LINK_RX: return ipmip4_netlink_kern_delmip_link_TX_RX(mem,nlmsg,rta); case IP_WRMOB4_DEL_HANDLE: return ipmip4_netlink_kern_delmip_handle(mem,nlmsg,rta); default: return -IP_ERRNO_EOPNOTSUPP; } } IP_STATIC int ipmip4_netlink_kern_setmip_address(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta, Ip_bool doset) { int ret = 0; struct Ip_rtattr **mattr = IP_NULL; Ipnet_netif *netif; (void)nlmsg; if (!rta[IP_MIP_ENCAP - 1]) return -IP_ERRNO_EINVAL; ret = ipnet_netlink_parse(IP_WRMOB4_HOA_MAX, IP_RTA_DATA(rta[IP_MIP_ENCAP - 1]), IP_RTA_PAYLOAD(rta[IP_MIP_ENCAP - 1]), &mattr); if (ret < 0) return ret; if (!mattr || !mattr[IP_WRMOB4_HOA_ADDRESS - 1] || !mattr[IP_WRMOB4_HOA_IFINDEX -1]) { ret = -IP_ERRNO_EINVAL; goto done; } /* Locate the interface */ netif = ipnet_if_indextonetif(mem->vr, IP_GET_32ON8(IP_RTA_DATA(mattr[IP_WRMOB4_HOA_IFINDEX - 1]))); if (netif == IP_NULL) { ret = -IP_ERRNO_ENXIO; goto done; } if (doset) ipmip_netlink_kern_hoa_activate(netif, mattr[IP_WRMOB4_HOA_ADDRESS - 1]); else ipmip_netlink_kern_hoa_deactivate(netif, mattr[IP_WRMOB4_HOA_ADDRESS - 1]); done: if (mattr) ipcom_free(mattr); return ret; } IP_STATIC int ipmip4_netlink_kern_setmip_no_global_src(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta, Ip_bool doset) { int ret = 0; struct Ip_rtattr **mattr = IP_NULL; Ipnet_netif *netif; (void)nlmsg; if (!rta[IP_MIP_ENCAP - 1]) return -IP_ERRNO_EINVAL; ret = ipnet_netlink_parse(IP_WRMOB4_HOA_MAX, IP_RTA_DATA(rta[IP_MIP_ENCAP - 1]), IP_RTA_PAYLOAD(rta[IP_MIP_ENCAP - 1]), &mattr); if (ret < 0) return ret; if (!mattr || !mattr[IP_WRMOB4_HOA_IFINDEX -1]) { ret = -IP_ERRNO_EINVAL; goto done; } /* Locate the interface */ netif = ipnet_if_indextonetif(mem->vr, IP_GET_32ON8(IP_RTA_DATA(mattr[IP_WRMOB4_HOA_IFINDEX - 1]))); if (netif == IP_NULL) { ret = -IP_ERRNO_ENXIO; goto done; } if (doset) ipmip_netlink_kern_if_exclude(netif); else ipmip_netlink_kern_if_include(netif); done: if (mattr) ipcom_free(mattr); return ret; } /* *=========================================================================== * ipnet_rtnetlink_link_setlink *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipmip4_netlink_kern_setmip_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta, int family) { struct Ip_mipmsg *msg = IP_NLMSG_DATA(nlmsg); (void)family; /* */ switch (msg->mip_type) { case IP_WRMOB4_SET_ADDRESS_ACTIVE: return ipmip4_netlink_kern_setmip_address(mem, nlmsg, rta, IP_BIT_ISFALSE(msg->mip_flags, IP_WRMOB4_SET_FLAG_CLEAR)); case IP_WRMOB4_SET_NO_GLOBAL_SRC: return ipmip4_netlink_kern_setmip_no_global_src(mem, nlmsg, rta, IP_BIT_ISFALSE(msg->mip_flags, IP_WRMOB4_SET_FLAG_CLEAR)); } return -IP_ERRNO_EOPNOTSUPP; } IP_STATIC int ipmip4_netlink_kern_getmip_statistics(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta) { int ret; struct Ip_mipmsg *msg; struct Ip_nlmsghdr *nlh; struct Ip_rtattr **mattr = IP_NULL; Ipmip4_netlink_kern_handle_t *handle; if (!rta[IP_MIP_ENCAP - 1]) return -IP_ERRNO_EINVAL; ret = ipnet_netlink_parse(IP_WRMOB4_ROUTE_MAX, IP_RTA_DATA(rta[IP_MIP_ENCAP - 1]), IP_RTA_PAYLOAD(rta[IP_MIP_ENCAP - 1]), &mattr); if (ret < 0) return ret; if (!mattr || !mattr[IP_WRMOB4_ROUTE_HANDLE - 1]) { ret = -IP_ERRNO_EINVAL; goto done; } /* Delete the handle */ handle = (Ipmip4_netlink_kern_handle_t *)ipmip_netlink_kern_handle_get(mem->vr, IP_RTA_DATA(mattr[IP_WRMOB4_ROUTE_HANDLE - 1])); if (handle == IP_NULL) { ret = -IP_ERRNO_ENOENT; goto done; } ret = -IP_ERRNO_ENOMEM; /* Create the response */ nlh = ipnet_netlink_nlmsg_sz_begin(mem, nlmsg->nlmsg_seq, IP_RTM_GETMIP, 0); if (!nlh) goto done; /**/ msg = ipnet_netlink_sz_put(mem, IP_NULL, sizeof(*msg)); if (!msg) goto done; /* Add route message header */ msg->mip_family = IP_AF_INET; msg->mip_flags = 0; msg->mip_type = IP_WRMOB4_GET_HANDLE_STAT; /* */ { struct Ip_rtattr *rep; rep = ipnet_rtnetlink_rta_sz_nested_begin(mem, IP_MIP_ENCAP); if (!rep) goto done; /* Put inbound statistics */ if (!ipnet_rtnetlink_rta_sz_put(mem, IP_WRMOB4_STATS_LINK_RECV, sizeof(handle->links[IPMIP_LINK_RECV]), &handle->links[IPMIP_LINK_RECV])) goto done; /* Put inbound statistics */ if (!ipnet_rtnetlink_rta_sz_put(mem, IP_WRMOB4_STATS_LINK_SEND, sizeof(handle->links[IPMIP_LINK_SEND]), &handle->links[IPMIP_LINK_SEND])) goto done; ipnet_rtnetlink_rta_sz_nested_end(mem, rep); } /* Done */ ret = ipnet_netlink_nlmsg_sz_end(mem, nlh); if (ret >= 0) { handle->links[IPMIP_LINK_RECV].mip_s_octets = 0; handle->links[IPMIP_LINK_RECV].mip_s_pkts = 0; handle->links[IPMIP_LINK_SEND].mip_s_octets = 0; handle->links[IPMIP_LINK_SEND].mip_s_pkts = 0; } done: if (mattr) ipcom_free(mattr); return ret; } IP_STATIC int ipmip4_netlink_kern_getmip_validate_route(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta) { int ret; Ipnet_route_entry *re; struct Ip_mipmsg *msg; struct Ip_nlmsghdr *nlh; struct Ip_rtattr **mattr = IP_NULL; if (!rta[IP_MIP_ENCAP - 1]) return -IP_ERRNO_EINVAL; ret = ipnet_netlink_parse(IP_WRMOB4_ROUTE_MAX, IP_RTA_DATA(rta[IP_MIP_ENCAP - 1]), IP_RTA_PAYLOAD(rta[IP_MIP_ENCAP - 1]), &mattr); if (ret < 0) return ret; if (!mattr || !mattr[IP_WRMOB4_ROUTE_DST - 1]) { ret = -IP_ERRNO_EINVAL; goto done; } /* Create the mask */ if (IP_RTA_PAYLOAD(mattr[IP_WRMOB4_ROUTE_DST - 1]) != sizeof(struct Ip_in_addr)) { ret = -IP_ERRNO_EINVAL; goto done; } ret = ipnet_route_raw_lookup(IP_AF_INET, mem->vr, IPCOM_ROUTE_TABLE_GET(IPMIP4_NETLINK_KERN_U32(mattr, IP_WRMOB4_ROUTE_TABLE)), 0, IP_RTA_DATA(mattr[IP_WRMOB4_ROUTE_DST - 1]), 0, IPMIP4_NETLINK_KERN_U32(mattr, IP_WRMOB4_ROUTE_IFINDEX), &re); if (ret < 0) goto done; /* Create the response */ nlh = ipnet_netlink_nlmsg_sz_begin(mem, nlmsg->nlmsg_seq, IP_RTM_GETMIP, 0); if (!nlh) goto done; /**/ msg = ipnet_netlink_sz_put(mem, IP_NULL, sizeof(*msg)); if (!msg) goto done; /* Add route message header */ msg->mip_family = IP_AF_INET; msg->mip_flags = 0; msg->mip_type = IP_WRMOB4_GET_VALIDATE_ROUTE; /* Done */ ret = ipnet_netlink_nlmsg_sz_end(mem, nlh); done: if (mattr) ipcom_free(mattr); return ret; } IP_STATIC int ipmip4_netlink_kern_getmip_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta, int family) { struct Ip_mipmsg *msg = IP_NLMSG_DATA(nlmsg); (void) family; switch (msg->mip_type) { case IP_WRMOB4_GET_HANDLE_STAT: return ipmip4_netlink_kern_getmip_statistics(mem, nlmsg, rta); case IP_WRMOB4_GET_VALIDATE_ROUTE: return ipmip4_netlink_kern_getmip_validate_route(mem, nlmsg, rta); } return -IP_ERRNO_EOPNOTSUPP; } IP_STATIC int ipmip4_netlink_kern_newmip_link_TX_RX(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta, Ip_u8 flags, Ipnet_dst_cache_mip_handle func) { int ret = 0; struct Ipnet_route_add_param params; Ipmip4_ipnet_route_func_info_t mipinfo; struct Ipnet_rt_metrics metric; Ipnet_rtnetlink_key_t ukey; void *mask = IP_NULL; struct Ip_rtattr **mattr = IP_NULL; struct Ip_sockaddr *gw = IP_NULL; union Ip_sockaddr_union ugw; Ip_u32 domain; (void)nlmsg; if (!rta[IP_MIP_ENCAP - 1]) { IPCOM_LOG0(ERR, "Required WRMOB4 route-gw parameter is missing"); return -IP_ERRNO_EINVAL; } ret = ipnet_netlink_parse(IP_WRMOB4_ROUTE_MAX, IP_RTA_DATA(rta[IP_MIP_ENCAP - 1]), IP_RTA_PAYLOAD(rta[IP_MIP_ENCAP - 1]), &mattr); if (ret < 0) { IPCOM_LOG0(ERR, "Unable to parse route-gw parameters"); return ret; } /* Required destination */ if (!mattr || !mattr[IP_WRMOB4_ROUTE_DST - 1]) { IPCOM_LOG0(ERR, "Required WRMOB4 route-gw parameters were missing"); ret = -IP_ERRNO_EINVAL; goto done; } ipcom_memset(¶ms, 0, sizeof(params)); ipcom_memset(&ukey, 0, sizeof(ukey)); ipcom_memset(&mipinfo, 0, sizeof(mipinfo)); ipcom_memset(&metric, 0, sizeof(metric)); /**/ metric.rmx_hopcount = 0; if (IP_BIT_ISSET(flags, IP_WRMOB4_NEW_TYPE_TEMPORARY)) metric.rmx_expire = 5; else metric.rmx_expire = IPCOM_ADDR_INFINITE; if (mattr[IP_WRMOB4_ROUTE_MTU - 1]) metric.rmx_mtu = IP_GET_32ON8(IP_RTA_DATA(mattr[IP_WRMOB4_ROUTE_MTU - 1])); params.metrics = &metric; params.key = &ukey; params.vr = mem->vr; params.table = IPCOM_ROUTE_TABLE_GET(IPMIP4_NETLINK_KERN_U32(mattr, IP_WRMOB4_ROUTE_TABLE)); if (mattr[IP_WRMOB4_ROUTE_HANDLE - 1]) { mipinfo.handle = (Ipmip4_netlink_kern_handle_t *)ipmip_netlink_kern_handle_get(mem->vr, IP_RTA_DATA(mattr[IP_WRMOB4_ROUTE_HANDLE - 1])); if (mipinfo.handle == IP_NULL) { ret = -IP_ERRNO_ENOENT; goto done; } } params.netif = ipnet_if_indextonetif(mem->vr, IPMIP4_NETLINK_KERN_U32(mattr, IP_WRMOB4_ROUTE_IFINDEX)); if (params.netif == IP_NULL) { IPCOM_LOG0(ERR, "Unable to locate route-gw interface"); ret = -IP_ERRNO_ENXIO; goto done; } /* Gre KEY? */ if (mattr[IP_WRMOB4_ROUTE_GRE_KEY - 1]) { mipinfo.have_gre_key = IP_TRUE; mipinfo.gre_key = IP_GET_32ON8(IP_RTA_DATA(mattr[IP_WRMOB4_ROUTE_GRE_KEY - 1])); } if (mattr[IP_WRMOB4_ROUTE_LINK - 1]) mipinfo.link = IP_GET_32ON8(IP_RTA_DATA(mattr[IP_WRMOB4_ROUTE_LINK - 1])); /* Create the mask */ if (IP_RTA_PAYLOAD(mattr[IP_WRMOB4_ROUTE_DST - 1]) == sizeof(struct Ip_in_addr)) params.domain = IP_AF_INET; else { #ifdef IPCOM_USE_INET6 params.domain = IP_AF_INET6; #else ret = -IP_ERRNO_EINVAL; goto done; #endif } ret = ipnet_rtnetlink_route_key_setup(params.domain, &ukey, params.netif->ipcom.ifindex, &mask, IPMIP4_NETLINK_KERN_U32(mattr, IP_WRMOB4_ROUTE_DST_LEN), mattr[IP_WRMOB4_ROUTE_DST - 1]); if (ret < 0) { IPCOM_LOG0(ERR, "Unable to parse route-gw data"); goto done; } domain = params.domain; if (mattr[IP_WRMOB4_ROUTE_GW - 1]) { if (IP_RTA_PAYLOAD(mattr[IP_WRMOB4_ROUTE_GW - 1]) == sizeof(struct Ip_in_addr)) domain = IP_AF_INET; else { if (IP_RTA_PAYLOAD(mattr[IP_WRMOB4_ROUTE_GW - 1]) == sizeof(struct Ip_sockaddr_dl)) { gw = (struct Ip_sockaddr *)IP_RTA_DATA(mattr[IP_WRMOB4_ROUTE_GW - 1]); domain = IP_AF_LINK; params.flags |= IPNET_RTF_LLINFO; } else { #ifdef IPCOM_USE_INET6 domain = IP_AF_INET6; #else ret = -IP_ERRNO_EINVAL; goto done; #endif } } } if (domain != IP_AF_LINK) { ret = ipnet_rtnetlink_route_gw_setup(domain, params.netif->ipcom.ifindex, &ugw, &gw, mattr[IP_WRMOB4_ROUTE_GW - 1]); if (ret < 0) { IPCOM_LOG0(ERR, "Unable to parse route-gw data"); goto done; } } if (mattr[IP_WRMOB4_ROUTE_NAT_PORT - 1]) { Ip_u32 nat_port = IP_GET_32ON8(IP_RTA_DATA(mattr[IP_WRMOB4_ROUTE_NAT_PORT - 1])); Ip_u32 nat_type = nat_port >> 16; nat_port &= 0xFFFF; if (nat_type == 0) nat_type = IP_UDP_NAT_T_DSMIPV6; /* */ if (gw != IP_NULL) { if (gw->sa_family == IP_AF_INET) { struct Ip_sockaddr_in *s_in = (struct Ip_sockaddr_in *)gw; /* Set the DSMIPv6 NAT type */ s_in->sin_port = (Ip_u16)nat_port; s_in->sin_zero[0] = (Ip_u8)nat_type; } } } params.netmask = mask; /* May need it for the compare */ mipinfo.info.rt_info_size = sizeof(mipinfo); mipinfo.flags = flags; params.rtinfo = &mipinfo.info; params.flags |= IPMIP4_NETLINK_KERN_U32(mattr, IP_WRMOB4_ROUTE_FLAGS); params.flags |= IPNET_RTF_UP | IPNET_RTF_DONE | IPNET_RTF_PREF; params.gateway = gw; if (!params.netmask) params.flags |= IPNET_RTF_HOST; /* Set the flow match/init function */ ipmip_netlink_kern_rt_flow_set(&mipinfo.info, mattr[IP_WRMOB4_ROUTE_SRCMAC - 1]? IP_RTA_DATA(mattr[IP_WRMOB4_ROUTE_SRCMAC - 1]) : IP_NULL, func); if (IP_BIT_ISSET(flags, IP_WRMOB4_NEW_TYPE_TEMPORARY)) { ret = ipnet_route_delete2(params.domain, params.vr, params.table, params.key, params.netmask, IP_NULL, 0, 0, 0, IP_FALSE); params.no_ref_count = IP_TRUE; } ret = ipnet_route_add(¶ms); done: if (mattr) ipcom_free(mattr); return ret; } IP_STATIC int ipmip4_netlink_kern_newmip_handle(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta) { int ret; Ipmip4_netlink_kern_handle_t *nhandle; Ipmip_netlink_kern_handle_t *handle; struct Ip_rtattr **mattr = IP_NULL; (void)nlmsg; if (!rta[IP_MIP_ENCAP - 1]) return -IP_ERRNO_EINVAL; ret = ipnet_netlink_parse(IP_WRMOB4_ROUTE_MAX, IP_RTA_DATA(rta[IP_MIP_ENCAP - 1]), IP_RTA_PAYLOAD(rta[IP_MIP_ENCAP - 1]), &mattr); if (ret < 0) return ret; if (!mattr || !mattr[IP_WRMOB4_ROUTE_HANDLE - 1]) { ret = -IP_ERRNO_EINVAL; goto done; } handle = (Ipmip_netlink_kern_handle_t *)IP_RTA_DATA(mattr[IP_WRMOB4_ROUTE_HANDLE - 1]); if (ipmip_netlink_kern_handle_get(mem->vr, handle)) { ret = -IP_ERRNO_EEXIST; goto done; } nhandle = ipcom_calloc(1, sizeof(*nhandle)); if (nhandle == IP_NULL) { ret = -IP_ERRNO_ENOMEM; goto done; } ipcom_memcpy(&nhandle->handle, handle, sizeof(*handle)); /* Delete the handle */ ipmip_netlink_kern_handle_add(mem->vr, &nhandle->handle); done: if (mattr) ipcom_free(mattr); return ret; } IP_STATIC int ipmip4_netlink_kern_newmip_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta, int family) { struct Ip_mipmsg *msg = IP_NLMSG_DATA(nlmsg); (void)family; switch (msg->mip_type) { case IP_WRMOB4_NEW_LINK_TX: return ipmip4_netlink_kern_newmip_link_TX_RX(mem, nlmsg, rta, msg->mip_flags, (Ipnet_dst_cache_mip_handle)ipmip4_ipnet_dst_cache_func_gw_link_TX_init); case IP_WRMOB4_NEW_LINK_RX: return ipmip4_netlink_kern_newmip_link_TX_RX(mem, nlmsg, rta, msg->mip_flags, (Ipnet_dst_cache_mip_handle)ipmip4_ipnet_dst_cache_func_gw_link_RX_init); case IP_WRMOB4_NEW_HANDLE: return ipmip4_netlink_kern_newmip_handle(mem, nlmsg, rta); } return -IP_ERRNO_EOPNOTSUPP; } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ IP_GLOBAL void ipmip4_rtnetlink_init(void) { /* AF_UNSPEC */ ipnet_rtnetlink_register_family_event(IP_AF_INET, IP_RTM_DELMIP, IP_MIP_MAX, sizeof(struct Ip_mipmsg), ipmip4_netlink_kern_delmip_family, IP_NULL); ipnet_rtnetlink_register_family_event(IP_AF_INET, IP_RTM_NEWMIP, IP_MIP_MAX, sizeof(struct Ip_mipmsg), ipmip4_netlink_kern_newmip_family, IP_NULL); ipnet_rtnetlink_register_family_event(IP_AF_INET, IP_RTM_SETMIP, IP_MIP_MAX, sizeof(struct Ip_mipmsg), ipmip4_netlink_kern_setmip_family, IP_NULL); ipnet_rtnetlink_register_family_event(IP_AF_INET, IP_RTM_GETMIP, IP_MIP_MAX, sizeof(struct Ip_mipmsg), ipmip4_netlink_kern_getmip_family, IP_NULL); } #else int ipmip4_netlink_kern_empty_file = 0; #endif /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* **************************************************************************** * END OF FILE **************************************************************************** */