1029 lines
31 KiB
C
1029 lines
31 KiB
C
|
|
/*
|
|
******************************************************************************
|
|
* 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 <joel.hortelis@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 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 <ipcom_type.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_sysvar.h>
|
|
#include <ipcom_inet.h>
|
|
#include <ipcom_syslog.h>
|
|
|
|
#include "ipnet_h.h"
|
|
#include "ipnet.h"
|
|
|
|
#include <private/ipmip4_netlink_kern.h>
|
|
#include <ipmip.h>
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 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
|
|
****************************************************************************
|
|
*/
|