/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipcom_netlink.c,v $ $Revision: 1.2.46.2 $ * $Source: /home/interpeak/CVSRoot/ipcom/src/ipcom_netlink.c,v $ * $Author: svc-cmnet $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Joel Hortelius ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif #include "ipcom_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include "ipcom_type.h" #include "ipcom_cstyle.h" #include "ipcom_clib.h" #include "ipcom_clib2.h" #include "ipcom_sock.h" #include "ipcom_netlink.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /*** LINUX COMPAT MODE FLAGS ***/ #define IFF_LINUX_UP 0x1 /* IP_IFF_UP */ #define IFF_LINUX_BROADCAST 0x2 /* IP_IFF_BROADCAST */ #define IFF_LINUX_DEBUG 0x4 /* IP_IFF_DEBUG */ #define IFF_LINUX_LOOPBACK 0x8 /* IP_IFF_LOOPBACK */ #define IFF_LINUX_POINTOPOINT 0x10 /* IP_IFF_POINTTOPOINT */ #define IFF_LINUX_NOTRAILERS 0x20 /* NO MATCH */ #define IFF_LINUX_RUNNING 0x40 /* IP_IFF_RUNNING */ #define IFF_LINUX_NOARP 0x80 /* IP_IFF_NOARP */ #define IFF_LINUX_PROMISC 0x100 /* IP_IFF_PROMISC*/ #define IFF_LINUX_ALLMULTI 0x200 /* IP_IFF_ALLMULT */ #define IFF_LINUX_MASTER 0x400 /* IP_IFF_MASTER */ #define IFF_LINUX_SLAVE 0x800 /* IP_IFF_SLAVE */ #define IFF_LINUX_MULTICAST 0x1000 /* IP_IFF_MULTICAST */ #define IFF_LINUX_PORTSEL 0x2000 #define IFF_LINUX_DYNAMIC 0x8000 #define IFF_LINUX_LOWER_UP 0x10000 #define IFF_LINUX_DORMANT 0x20000 #define IFF_LINUX_ECHO 0x40000 #define IFF_LINUX_AUTOMEDIA 0x4000 #define IP_AF_INET6_LINUX 10 /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* **************************************************************************** * INTERFACE TRANSLATION FUNCTIONS **************************************************************************** */ IP_STATIC Ip_u32 ipcom_netlink_rtnl_translate_ifflags(int from, int to, Ip_u32 flags) { Ip_u32 nflags = flags; int b; if (from == IP_NETLINK_TRANSLATE_IPNET) { /* Switch from IPNET */ switch (to) { case IP_NETLINK_TRANSLATE_LINUX: nflags = 0; for (b = 0; b < 32; b++) { if (IP_BIT_ISSET(flags, IP_BIT_VAL(b))) { switch (IP_BIT_VAL(b)) { case IP_IFF_UP: nflags |= IFF_LINUX_UP; break; case IP_IFF_BROADCAST: nflags |= IFF_LINUX_BROADCAST; break; case IP_IFF_DEBUG: nflags |= IFF_LINUX_DEBUG; break; case IP_IFF_LOOPBACK: nflags |= IFF_LINUX_LOOPBACK; break; case IP_IFF_POINTOPOINT: nflags |= IFF_LINUX_POINTOPOINT; break; case IP_IFF_RUNNING: /* We do not have the linux lower up; map it to running */ nflags |= IFF_LINUX_RUNNING|IFF_LINUX_LOWER_UP; break; case IP_IFF_NOARP: nflags |= IFF_LINUX_NOARP; break; case IP_IFF_PROMISC: nflags |= IFF_LINUX_PROMISC; break; case IP_IFF_ALLMULTI: nflags |= IFF_LINUX_ALLMULTI; break; case IP_IFF_MULTICAST: nflags |= IFF_LINUX_MULTICAST; break; case IP_IFF_SLAVE: nflags |= IFF_LINUX_SLAVE; break; case IP_IFF_MASTER: nflags |= IFF_LINUX_MASTER; break; default: break; } } } break; } } else { /* Switch to IPNET */ switch (from) { case IP_NETLINK_TRANSLATE_LINUX: nflags = 0; for (b = 0; b < 32; b++) { if (IP_BIT_ISSET(flags, IP_BIT_VAL(b))) { switch (IP_BIT_VAL(b)) { case IFF_LINUX_UP: nflags |= IP_IFF_UP; break; case IFF_LINUX_BROADCAST: nflags |= IP_IFF_BROADCAST; break; case IFF_LINUX_DEBUG: nflags |= IP_IFF_DEBUG; break; case IFF_LINUX_LOOPBACK: nflags |= IP_IFF_LOOPBACK; break; case IFF_LINUX_POINTOPOINT: nflags |= IP_IFF_POINTOPOINT; break; case IFF_LINUX_RUNNING: nflags |= IP_IFF_RUNNING; break; case IFF_LINUX_NOARP: nflags |= IP_IFF_NOARP; break; case IFF_LINUX_PROMISC: nflags |= IP_IFF_PROMISC; break; case IFF_LINUX_ALLMULTI: nflags |= IP_IFF_ALLMULTI; break; case IFF_LINUX_MULTICAST: nflags |= IP_IFF_MULTICAST; break; case IFF_LINUX_SLAVE: nflags |= IP_IFF_SLAVE; break; case IFF_LINUX_MASTER: nflags |= IP_IFF_MASTER; break; case IFF_LINUX_LOWER_UP: nflags |= IP_IFF_RUNNING; break; default: break; } } } break; } } return nflags; } IP_STATIC void ipcom_netlink_rtnl_translate_family(int from, int to, struct Ip_nlmsghdr *nlh) { struct Ip_rtgenmsg *rtmsg = (struct Ip_rtgenmsg *)IP_NLMSG_DATA(nlh); if (from == IP_NETLINK_TRANSLATE_IPNET) { /* Switch from IPNET */ switch (to) { case IP_NETLINK_TRANSLATE_LINUX: if (rtmsg->rtgen_family == IP_AF_INET6) rtmsg->rtgen_family = IP_AF_INET6_LINUX; break; } } else { /* Switch to IPNET */ switch (from) { case IP_NETLINK_TRANSLATE_LINUX: if (rtmsg->rtgen_family == IP_AF_INET6_LINUX) rtmsg->rtgen_family = IP_AF_INET6; break; } } } IP_STATIC void ipcom_netlink_rtnl_translate_route(int from, int to, struct Ip_nlmsghdr *nlh) { struct Ip_rtmsg *rtm = (struct Ip_rtmsg *)IP_NLMSG_DATA(nlh); if (IP_NLMSG_PAYLOAD(nlh, 0) < sizeof(*rtm)) return; if (from == IP_NETLINK_TRANSLATE_IPNET) { /* Switch from IPNET */ switch (to) { case IP_NETLINK_TRANSLATE_LINUX: if (rtm->rtm_type == IP_RTN_CLONE || rtm->rtm_type == IP_RTN_PROXY) { if (rtm->rtm_type == IP_RTN_CLONE) IP_BIT_SET(rtm->rtm_flags, IP_RTM_F_CLONED); rtm->rtm_type = IP_RTN_UNICAST; } break; } } else { /* Switch to IPNET */ switch (from) { case IP_NETLINK_TRANSLATE_LINUX: if (IP_BIT_ISSET(rtm->rtm_flags, IP_RTM_F_CLONED)) { IP_BIT_CLR(rtm->rtm_flags, IP_RTM_F_CLONED); rtm->rtm_type = IP_RTN_CLONE; } break; } } } IP_STATIC void ipcom_netlink_rtnl_translate_link(int from, int to, struct Ip_nlmsghdr *nlh) { struct Ip_ifinfomsg *ifm = (struct Ip_ifinfomsg *)IP_NLMSG_DATA(nlh); if (IP_NLMSG_PAYLOAD(nlh, 0) < sizeof(*ifm)) return; ifm->ifi_change = ipcom_netlink_rtnl_translate_ifflags(from, to, ifm->ifi_change); ifm->ifi_flags = ipcom_netlink_rtnl_translate_ifflags(from, to, ifm->ifi_flags); if (from == IP_NETLINK_TRANSLATE_IPNET) { /* Switch from IPNET */ switch (to) { case IP_NETLINK_TRANSLATE_LINUX: break; } } else { /* Switch to IPNET */ switch (from) { case IP_NETLINK_TRANSLATE_LINUX: break; } } } IP_STATIC void ipcom_netlink_rtnl_translate_neigh(int from, int to, struct Ip_nlmsghdr *nlh) { struct Ip_ndmsg *ndm = (struct Ip_ndmsg *)IP_NLMSG_DATA(nlh); if (IP_NLMSG_PAYLOAD(nlh, 0) < sizeof(*ndm)) return; if (from == IP_NETLINK_TRANSLATE_IPNET) { /* Switch from IPNET */ switch (to) { case IP_NETLINK_TRANSLATE_LINUX: /* If they are set, they mean completely different things */ IP_BIT_CLR(ndm->ndm_flags, IP_NTF_GRAT|IP_NTF_EXTERNAL); break; } } else { /* Switch to IPNET */ switch (from) { case IP_NETLINK_TRANSLATE_LINUX: /* If they are set, they mean completely different things */ IP_BIT_CLR(ndm->ndm_flags, IP_NTF_GRAT|IP_NTF_EXTERNAL); break; } } } /* **************************************************************************** * TRANSLATION FUNCTIONS **************************************************************************** */ IP_GLOBAL void ipcom_netlink_rtnl_translate(int to, int from, struct Ip_nlmsghdr *nlh) { if (to == from) return; /* We cant deal with two unknowns, move one to IPNET */ if (IP_NETLINK_TRANSLATE_IPNET != from && to != IP_NETLINK_TRANSLATE_IPNET) { /* Translate one of them to IPNET - Its our base */ ipcom_netlink_rtnl_translate(from, IP_NETLINK_TRANSLATE_IPNET, nlh); from = IP_NETLINK_TRANSLATE_IPNET; } ipcom_netlink_rtnl_translate_family(to, from, nlh); if (IP_RTM_FAM(nlh->nlmsg_type) == IP_RTM_FAM(IP_RTM_NEWROUTE)) ipcom_netlink_rtnl_translate_route(to, from, nlh); else if (IP_RTM_FAM(nlh->nlmsg_type) == IP_RTM_FAM(IP_RTM_NEWLINK)) ipcom_netlink_rtnl_translate_link(to, from, nlh); else if (IP_RTM_FAM(nlh->nlmsg_type) == IP_RTM_FAM(IP_RTM_NEWNEIGH)) ipcom_netlink_rtnl_translate_neigh(to, from, nlh); } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ IP_GLOBAL struct Ip_rtattr * ipcom_netlink_addattr_l(struct Ip_nlmsghdr *n, Ip_size_t max_size, int type, const void *rta_data, Ip_size_t rta_len) { int rta_space = IP_RTA_LENGTH(rta_len); struct Ip_rtattr *rta; /* Have enough space? */ if (IP_NLMSG_ALIGN(n->nlmsg_len) + IP_RTA_ALIGN(rta_space) > (unsigned)max_size) return IP_NULL; /* Store the data */ rta = (struct Ip_rtattr *) ((char *)n + IP_NLMSG_ALIGN(n->nlmsg_len)); rta->rta_len = rta_space; rta->rta_type = type; if (IP_NULL != rta_data) ipcom_memcpy(IP_RTA_DATA(rta), rta_data, rta_len); /* Move the length indicator */ n->nlmsg_len = IP_NLMSG_ALIGN(n->nlmsg_len) + IP_RTA_ALIGN(rta_space); return rta; } IP_GLOBAL struct Ip_rtattr * ipcom_netlink_rta_addattr_l(struct Ip_rtattr *rta_nest, Ip_size_t max_size, int type, const void *rta_data, Ip_size_t rta_len) { struct Ip_rtattr *rta; int rta_space = IP_RTA_LENGTH(rta_len); if (IP_RTA_ALIGN(rta_nest->rta_len) + IP_RTA_ALIGN(rta_space) > max_size) return IP_NULL; rta = (struct Ip_rtattr*)(((char*)rta_nest) + IP_RTA_ALIGN(rta_nest->rta_len)); rta->rta_type = type; rta->rta_len = rta_space; if (IP_NULL != rta_data) ipcom_memcpy(IP_RTA_DATA(rta), rta_data, rta_len); rta_nest->rta_len = IP_NLMSG_ALIGN(rta_nest->rta_len) + IP_RTA_ALIGN(rta_space); return rta; } IP_GLOBAL struct Ip_rtattr * ipcom_netlink_addattr_l_begin(struct Ip_nlmsghdr *n, int max_size, int type) { int rta_space = IP_RTA_LENGTH(0); struct Ip_rtattr *rta; /* Have enough space? */ if (IP_NLMSG_ALIGN(n->nlmsg_len) + IP_RTA_ALIGN(rta_space) > (unsigned)max_size) return IP_NULL; /* Store the data */ rta = (struct Ip_rtattr *) ((char *)n + IP_NLMSG_ALIGN(n->nlmsg_len)); rta->rta_len = rta_space; rta->rta_type = type; return rta; } IP_GLOBAL void ipcom_netlink_addattr_l_end(struct Ip_nlmsghdr *n, struct Ip_rtattr *rta) { n->nlmsg_len = IP_NLMSG_ALIGN(n->nlmsg_len) + IP_RTA_ALIGN(rta->rta_len); } IP_GLOBAL struct Ip_nlmsghdr * ipcom_netlink_nlmsghdr_init(void *buf, int type, int flags, int hdrsize) { struct Ip_nlmsghdr *nlh = (struct Ip_nlmsghdr *)buf; ipcom_memset(nlh, 0, sizeof(*nlh)); nlh->nlmsg_len = IP_NLMSG_LENGTH(hdrsize); nlh->nlmsg_type = type; nlh->nlmsg_flags = flags; return nlh; } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* **************************************************************************** * END OF FILE **************************************************************************** */