/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_rtnetlink_rule.c,v $ $Revision: 1.15.10.1 $ * $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_rtnetlink_rule.c,v $ * $Author: svc-cmnet $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation Jonas Rendel ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * Contains netlink handlers for managing virtual routers, tables and * interface rules. */ /* **************************************************************************** * 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" #include "ipnet_netlink_h.h" #include "ipnet_loopback.h" #include "ipcom_sock.h" #ifdef IPCOM_USE_INET6 #include "ipcom_sock6.h" #include "ipnet_ip6.h" #endif #include "ipnet_eth.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ #define IPNET_RTNETLINK_RULE_RTPRIO_TO_PRIO(prio) (Ip_s16)IP_MIN(IPNET_PR_PRIO_MAX, (Ip_s32) ((IPNET_PR_PRIO_MIN + (prio)))) #define IPNET_RTNETLINK_RULE_PRIO_TO_RTPRIO(prio) (Ip_u32)(-IPNET_PR_PRIO_MIN + (prio)) /* **************************************************************************** * 5 TYPES **************************************************************************** */ typedef struct Ipnet_rtnetlink_rule { struct { Ip_ptrdiff_t *vr; Ip_ptrdiff_t *done; Ip_ptrdiff_t *rule; Ip_ptrdiff_t aborted; Ip_ptrdiff_t cont; } state; /* Requested */ struct Ip_nlmsghdr *nlmsg; Ip_u8 family; /* Buffering */ Ipnet_netlink_mem_t *mem; } Ipnet_rtnetlink_rule_t; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ IP_STATIC int ipnet_rtnetlink_rule_rtrule_to_rule(struct Ip_nlmsghdr *nlmsg, Ip_u16 *vr, Ip_u8 family, struct Ip_rtattr **rta, struct Ipnet_policy_routing_rule *mpr) { struct Ip_fib_rule_hdr *fib = IP_NLMSG_DATA(nlmsg); Ip_size_t addrlen; #ifdef IPCOM_USE_INET if (family == IP_AF_INET) addrlen = sizeof(struct Ip_in_addr); else #endif #ifdef IPCOM_USE_INET6 if (family == IP_AF_INET6) addrlen = sizeof(struct Ip_in6_addr); else #endif { return -IP_ERRNO_EINVAL; } ipcom_memset(mpr, 0, sizeof(*mpr)); mpr->af = family; mpr->daddr_prefixlen = fib->dst_len; mpr->saddr_prefixlen = fib->src_len; if (*vr == IPCOM_VR_ANY) return -IP_ERRNO_EINVAL; *vr = ipnet_rtnetlink_vr(rta[IP_FRA_VR - 1], *vr); if (rta[IP_FRA_IFNAME - 1]) { Ipnet_netif *netif = ipnet_if_nametonetif(*vr, IP_RTA_DATA(rta[IP_FRA_IFNAME - 1])); if (!netif) return -IP_ERRNO_EINVAL; mpr->ifindex = netif->ipcom.ifindex; IP_BIT_SET(mpr->mask, IPNET_PR_RULE_IFINDEX); } if (rta[IP_FRA_PRIORITY - 1]) { Ip_s32 tmp = (Ip_s32)IP_GET_32ON8(IP_RTA_DATA(rta[IP_FRA_PRIORITY - 1])); mpr->prio = IPNET_RTNETLINK_RULE_RTPRIO_TO_PRIO(tmp); } if (rta[IP_FRA_SRC - 1]) { ipcom_memcpy(&mpr->saddr, IP_RTA_DATA(rta[IP_FRA_SRC - 1]), addrlen); IP_BIT_SET(mpr->mask, IPNET_PR_RULE_SADDR); } if (rta[IP_FRA_DST - 1]) { ipcom_memcpy(&mpr->daddr, IP_RTA_DATA(rta[IP_FRA_DST - 1]), addrlen); IP_BIT_SET(mpr->mask, IPNET_PR_RULE_DADDR); } #ifdef IPCOM_USE_INET6 if (rta[IP_FRA_SCOPE - 1]) { mpr->scope = ipnet_rtnetlink_ip6_rtscope_to_scope(IP_GET_32ON8(IP_RTA_DATA(rta[IP_FRA_SCOPE - 1]))); IP_BIT_SET(mpr->mask, IPNET_PR_RULE_SCOPE); } #endif if (fib->tos) { mpr->ds = fib->tos; IP_BIT_SET(mpr->mask, IPNET_PR_RULE_DS); } /**/ if (rta[IP_FRA_ID - 1]) mpr->id = IP_GET_32ON8(IP_RTA_DATA(rta[IP_FRA_ID - 1])); if (IP_BIT_ISSET(fib->flags, IP_FIB_RULE_FIND_SADDR)) IP_BIT_SET(mpr->mask, IPNET_PR_RULE_RTKEYSADDR); if (IP_BIT_ISSET(fib->flags, IP_FIB_RULE_INVERT)) IP_BIT_SET(mpr->mask, IPNET_PR_RULE_INVERTED); if (rta[IP_FRA_TABLE - 1]) mpr->table = IP_GET_32ON8(IP_RTA_DATA(rta[IP_FRA_TABLE - 1])); else mpr->table = fib->table; mpr->table = IPCOM_ROUTE_TABLE_GET(mpr->table); mpr->action = fib->action; switch (mpr->action) { case IP_FR_ACT_TO_TBL: case IP_FR_ACT_PROHIBIT: case IP_FR_ACT_UNREACHABLE: case IP_FR_ACT_BLACKHOLE: return 0; default: return -IP_ERRNO_EINVAL; } } /* *=========================================================================== * ipnet_rtnetlink_ip4_rule_fill_info *=========================================================================== * Description: Fill a NETLINK message with info about a route. * Parameters: data - Message buffer. * rt - IPNET route entry. * pid - NETLINK caller pid. * len - Available buffer length * * Returns: 0 - Success * <0 - Failure * */ IP_STATIC int ipnet_rtnetlink_rule_fill_info(Ipnet_netlink_mem_t *mem, Ip_u16 vr, Ip_u8 family, struct Ipnet_policy_routing_rule *rule, Ip_u32 seq, int event, Ip_u16 eflags) { struct Ip_nlmsghdr *nlh; Ip_u32 tmp32; Ip_size_t addrlen; struct Ip_fib_rule_hdr *fib; nlh = ipnet_netlink_nlmsg_sz_begin(mem, seq, event, eflags); if (!nlh) return -1; fib = ipnet_netlink_sz_put(mem, IP_NULL, sizeof (*fib)); if (!fib) return -1; #ifdef IPCOM_USE_INET if (family == IP_AF_INET) addrlen = sizeof(struct Ip_in_addr); else #endif #ifdef IPCOM_USE_INET6 if (family == IP_AF_INET6) addrlen = sizeof(struct Ip_in6_addr); else #endif { return 0; } ipcom_memset(fib, 0, sizeof(*fib)); /* Add route message header */ fib->family = family; /* Only have unicast right now */ fib->action = rule->action; /* Convert to 0 ... MAX */ { tmp32 = (Ip_u32)(-IPNET_PR_PRIO_MIN + rule->prio); if (!ipnet_rtnetlink_rta_sz_put(mem, IP_FRA_PRIORITY, sizeof(tmp32), &tmp32)) return -1; } tmp32 = vr; if (!ipnet_rtnetlink_rta_sz_put(mem, IP_FRA_VR, sizeof(tmp32), &tmp32)) return -1; if (!ipnet_rtnetlink_rta_sz_put(mem, IP_FRA_ID, sizeof(rule->id), &rule->id)) return -1; /* Store the source & destination */ if (IP_BIT_ISSET(rule->mask, IPNET_PR_RULE_DS)) fib->tos = rule->ds; /* Store the source & destination */ if (IP_BIT_ISSET(rule->mask, IPNET_PR_RULE_DADDR)) { if (!ipnet_rtnetlink_rta_sz_put(mem, IP_FRA_DST, addrlen, &rule->daddr)) return -1; fib->dst_len = rule->daddr_prefixlen; } if (IP_BIT_ISSET(rule->mask, IPNET_PR_RULE_SADDR)) { if (!ipnet_rtnetlink_rta_sz_put(mem, IP_FRA_SRC, addrlen, &rule->saddr)) return -1; fib->src_len = rule->saddr_prefixlen; } if (fib->action == IP_FR_ACT_TO_TBL) { fib->table = (Ip_u8) rule->table; /* Store table */ if (!ipnet_rtnetlink_rta_sz_put(mem, IP_FRA_TABLE, sizeof(rule->table), &rule->table)) return -1; } if (IP_BIT_ISSET(rule->mask, IPNET_PR_RULE_IFINDEX)) { /* Put destination address */ Ipnet_netif *netif = ipnet_if_indextonetif(vr, rule->ifindex); if (netif) if (!ipnet_rtnetlink_rta_sz_put(mem, IP_FRA_IFNAME, ipcom_strlen(netif->ipcom.name) + 1, netif->ipcom.name)) return -1; } #ifdef IPCOM_USE_INET6 if (family == IP_AF_INET6 && IP_BIT_ISSET(rule->mask, IPNET_PR_RULE_SCOPE)) { tmp32 = ipnet_rtnetlink_ip6_scope_to_rtscope(rule->scope); if (!ipnet_rtnetlink_rta_sz_put(mem, IP_FRA_SCOPE, sizeof(tmp32), &tmp32)) return -1; } #endif if (IP_BIT_ISSET(rule->mask, IPNET_PR_RULE_RTKEYSADDR)) IP_BIT_SET(fib->flags, IP_FIB_RULE_FIND_SADDR); if (IP_BIT_ISSET(rule->mask, IPNET_PR_RULE_INVERTED)) IP_BIT_SET(fib->flags, IP_FIB_RULE_INVERT); /* Done */ return ipnet_netlink_nlmsg_sz_end(mem, nlh); } /* *=========================================================================== * ipnet_rtnetlink_route_foreach_vr *=========================================================================== * Description: Hash table foreach callback. Called once for each virtual * router that exists in the system. * Parameters: rtab - route table * param - callback parameters. * Returns: - * */ IP_STATIC void ipnet_rtnetlink_rule_foreach_vr(Ipcom_route *rtab, Ipnet_rtnetlink_rule_t *param) { Ip_u16 vr = IPNET_ROUTE_GET_VR(rtab); Ipnet_route_policy_rule *r = IP_NULL; /* This traversal is already aborted */ if (param->state.aborted) return; /* Only handle rtabs of selected address family */ if (IPNET_ROUTE_GET_FAMILY(rtab) != param->family) return; /* Go for the defaults to get current VR's */ if (IPNET_ROUTE_GET_TABLE(rtab) != IPCOM_ROUTE_TABLE_DEFAULT) return; /* * If this is a trailer message buffer, make sure it * starts dumping where the last message ended */ if (param->state.cont) { Ipnet_route_policy_rule *rtmp = (Ipnet_route_policy_rule*)(*param->state.rule); /* Verify current state */ if (*param->state.vr != vr) return; r = ipnet_route_policy_get_rule_chain(param->family, vr); /**/ if (r) { while (r != IP_NULL && r != rtmp) r = IPCOM_LIST_NEXT(&r->list); } } else { r = ipnet_route_policy_get_rule_chain(param->family, vr); } /* Reset the continue variable; we're on the move again */ param->state.cont = 0; if (r == IP_NULL) return; do { /* Time to start dump info */ if (ipnet_rtnetlink_rule_fill_info(param->mem, vr, param->family, &r->rule, param->nlmsg->nlmsg_seq, IP_RTM_NEWRULE, IP_NLM_F_MULTI) < 0) goto aborted; } while ((r = IPCOM_LIST_NEXT(&r->list)) != IP_NULL); return; aborted: param->state.aborted = 1; *param->state.rule = (Ip_ptrdiff_t)r; *param->state.vr = (Ip_ptrdiff_t)vr; } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipnet_rtnetlink_rule_event *=========================================================================== * Description: Status of a network rule has changed, create a rule * event on NETLNK.. * Parameters: netif - The interface. * Returns: - * */ IP_GLOBAL void ipnet_rtnetlink_rule_event(struct Ipnet_policy_routing_rule *mpr, Ip_u16 vr, Ip_pid_t pid, int event) { Ipcom_pkt *pkt = IP_NULL; Ipnet_netlink_mem_t mem; int ret = -1; int group = 0; if (!ipnet_netlink_pkt_alloc(&pkt, IP_NULL, IP_TRUE, &mem, 512)) return; /* Must explicitly switch to the correct VR here */ pkt->vr_index = mem.vr = vr; mem.pid = pid; ret = ipnet_rtnetlink_rule_fill_info(&mem, vr, mpr->af, mpr, 0, event, 0); #ifdef IPCOM_USE_INET if (mpr->af == IP_AF_INET) group = IP_RTNLGRP_IPV4_RULE; #endif #ifdef IPCOM_USE_INET6 if (mpr->af == IP_AF_INET6) group = IP_RTNLGRP_IPV6_RULE; #endif if (ret < 0) ipcom_pkt_free(pkt); else { pkt->end += ret; /* We're done; do report this pkt */ ipnet_rtnetlink_broadcast(pkt, group); } } /* *========================================================================== * ipnet_rtnetlink_rule_dump *=========================================================================== * 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_rule_dump_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, int family, Ip_ptrdiff_t *args) { Ip_ptrdiff_t *done = &args[0]; Ip_ptrdiff_t *s_vr = &args[1]; Ip_ptrdiff_t *s_rule = &args[2]; Ip_ptrdiff_t *s_continue = &args[3]; Ipnet_rtnetlink_rule_t param; param.nlmsg = nlmsg; param.mem = mem; if (*done) goto exit_out; param.family = (Ip_u8) family; param.state.aborted = 0; param.state.done = done; param.state.vr = s_vr; param.state.rule = s_rule; param.state.cont = *s_continue; /* */ ipcom_hash_for_each(ipnet->rtabs, (Ipcom_hash_foreach_cb_func)ipnet_rtnetlink_rule_foreach_vr, ¶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_rule_newrule_family *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL int ipnet_rtnetlink_rule_newrule_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta, int family) { struct Ipnet_policy_routing_rule mpr; Ip_u16 vr; int ret; vr = mem->vr; /**/ ret = ipnet_rtnetlink_rule_rtrule_to_rule(nlmsg, &vr, (Ip_u8) family, rta, &mpr); if (ret < 0) return ret; /* Add this rule */ return ipnet_route_policy_ioctl(mem->pid, vr, IPNET_SIOCSPRRULE, &mpr); } /* *=========================================================================== * ipnet_rtnetlink_rule_newrule_family *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL int ipnet_rtnetlink_rule_delrule_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta, int family) { struct Ipnet_policy_routing_rule mpr; Ip_u16 vr; int ret; vr = mem->vr; /**/ ret = ipnet_rtnetlink_rule_rtrule_to_rule(nlmsg, &vr, (Ip_u8) family, rta, &mpr); if (ret < 0) return ret; if (mpr.id == IPNET_PR_ID_NONE) { Ipnet_route_policy_rule *r; /* Try find this rule */ r = ipnet_route_policy_get_rule_chain(family, vr); for (; r != IP_NULL; r = IPCOM_LIST_NEXT(&r->list)) { mpr.id = r->rule.id; /* Not the same table */ if (mpr.table != r->rule.table) continue; /* Do item for item compare */ if (IP_BIT_ISSET(mpr.mask, IPNET_PR_RULE_DS)) { if (IP_BIT_ISFALSE(r->rule.mask, IPNET_PR_RULE_DS) || r->rule.ds != mpr.ds) continue; } /* Do item for item compare */ if (IP_BIT_ISSET(mpr.mask, IPNET_PR_RULE_SADDR)) { if (IP_BIT_ISFALSE(r->rule.mask, IPNET_PR_RULE_SADDR) || r->rule.saddr_prefixlen != mpr.saddr_prefixlen || !ipnet_route_key_cmp(&r->rule.saddr, &mpr.saddr, r->rule.saddr_prefixlen)) continue; } /* Do item for item compare */ if (IP_BIT_ISSET(mpr.mask, IPNET_PR_RULE_DADDR)) { if (IP_BIT_ISFALSE(r->rule.mask, IPNET_PR_RULE_DADDR) || r->rule.daddr_prefixlen != mpr.daddr_prefixlen || !ipnet_route_key_cmp(&r->rule.daddr, &mpr.daddr, r->rule.daddr_prefixlen)) continue; } /* Do item for item compare */ if (IP_BIT_ISSET(mpr.mask, IPNET_PR_RULE_RTKEYSADDR)) { if (IP_BIT_ISFALSE(r->rule.mask, IPNET_PR_RULE_RTKEYSADDR)) continue; } /* Do item for item compare */ if (IP_BIT_ISSET(mpr.mask, IPNET_PR_RULE_IFINDEX)) { if (IP_BIT_ISFALSE(r->rule.mask, IPNET_PR_RULE_IFINDEX) || r->rule.ifindex != mpr.ifindex) continue; } /* Do item for item compare */ if (IP_BIT_ISSET(mpr.mask, IPNET_PR_RULE_SCOPE)) { if (IP_BIT_ISFALSE(r->rule.mask, IPNET_PR_RULE_SCOPE) || r->rule.scope != mpr.scope) continue; } /* Passed all */ break; } if (!r) return -IP_ERRNO_ENOENT; } /* Add this rule */ return ipnet_route_policy_ioctl(mem->pid, vr, IPNET_SIOCDPRRULE, &mpr); } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ #endif /* IPNET_USE_NETLINK */ /* **************************************************************************** * END OF FILE **************************************************************************** */