/* *=========================================================================== * ipnet_rtnetlink_rta_sz_nested_begin *=========================================================================== * Description: Begin a nested NETLINK route attribute to a NETLINK message. * Parameters: nlh - Current NLMSG * sz - Total size * attrtype - Attribute type * * Returns: * */ IP_GLOBAL void * ipnet_rtnetlink_rta_sz_nested_begin(Ipnet_netlink_mem_t *mem, int attrtype) { struct Ip_rtattr *rta = ipnet_netlink_sz_put(mem, IP_NULL, IP_RTA_LENGTH(0)); if (!rta) return IP_NULL; rta->rta_type = (Ip_u16) attrtype; rta->rta_len = (Ip_u16) IP_RTA_LENGTH (0); return rta; }
/* *=========================================================================== * ipnet_rtnetlink_rta_sz_put *=========================================================================== * Description: Add a NETLINK route attribute to a NETLINK message. * Parameters: nlh - Current NLMSG * sz - Total size * attrtype - Attribute type * attrlen - Lenght of attribute * data - Attribute data * * Returns: * */ IP_GLOBAL void * ipnet_rtnetlink_rta_sz_put(Ipnet_netlink_mem_t *mem, int attrtype, int attrlen, IP_CONST void *data) { struct Ip_rtattr *rta = ipnet_netlink_sz_put(mem, IP_NULL, IP_RTA_LENGTH(attrlen)); if (!rta) return IP_NULL; rta->rta_type = (Ip_u16) attrtype; rta->rta_len = (Ip_u16) IP_RTA_LENGTH (attrlen); if (data) ipcom_memcpy(IP_RTA_DATA(rta), data, attrlen); return rta; }
/* *=========================================================================== * ipnet_rtnetlink_route_delroute_family *=========================================================================== * Description: Delete a route via NETLINK. * Parameters: data - Message payload * len - Length of message. * nlh - NETLINK message header * arg - Reference to route attributes. * * Returns: 0 - Success * <0 - Failure * */ IP_GLOBAL int ipnet_rtnetlink_route_delroute_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlh, struct Ip_rtattr **rta, int family) { int ret = -1; Ip_u16 vr; Ip_u32 table; Ip_u32 ifindex = 0; Ip_bool is_multipath_route = IP_FALSE; Ip_size_t mpa_size = 0; struct Ip_rtnexthop *nh = IP_NULL; struct Ip_sockaddr *gw = IP_NULL; void *mask = IP_NULL; struct Ip_rtmsg *rtm = IP_NLMSG_DATA(nlh); union Ip_sockaddr_union ugw; Ipnet_rtnetlink_key_t ukey; #ifdef IPMPLS struct Ip_sockaddr_mpls gw_mpls; #endif /* IPMPLS */ table = IPCOM_ROUTE_TABLE_GET(rtm->rtm_table); #ifdef IPNET_USE_ROUTE_TABLE_NAMES if (rta[IP_RTA_TABLE_NAME -1]) { if (ipnet_route_vr_and_table_from_name(IP_RTA_DATA(rta[IP_RTA_TABLE_NAME - 1]), &vr, &table) < 0) return -IP_ERRNO_ESRCH; } else #endif { if (mem->vr == IPCOM_VR_ANY) return -IP_ERRNO_EINVAL; vr = ipnet_rtnetlink_vr(rta[IP_RTA_VR - 1], mem->vr); if (rta[IP_RTA_TABLE - 1]) table = IP_GET_32ON8(IP_RTA_DATA(rta[IP_RTA_TABLE - 1])); } if (rta[IP_RTA_OIF -1]) { if (rta[IP_RTA_OIF-1]->rta_len != IP_RTA_LENGTH(sizeof(Ip_u32))) return -IP_ERRNO_EINVAL; ifindex = IP_GET_32ON8(IP_RTA_DATA(rta[IP_RTA_OIF-1])); } if (rta[IP_RTA_MULTIPATH -1]) { is_multipath_route = IP_TRUE; nh = IP_RTA_DATA(rta[IP_RTA_MULTIPATH-1]); mpa_size = IP_RTA_PAYLOAD(rta[IP_RTA_MULTIPATH-1]); if ((nh == IP_NULL) || (mpa_size == 0)) return -IP_ERRNO_EINVAL; } if ((ret = ipnet_rtnetlink_route_key_setup(family, &ukey, ifindex, &mask, rtm->rtm_dst_len, rta[IP_RTA_DST-1])) < 0) return ret; if ((ret = ipnet_rtnetlink_route_gw_setup(family, ifindex, &ugw, &gw, rta[IP_RTA_GATEWAY-1])) < 0) return ret; if (rta[IP_RTA_GATEWAY-1]) gw = &ugw.sa; #ifdef IPMPLS if (rta[IP_RTA_NH_PROTO-1]) { Ip_u32 nh_type; /* IPNET MPLS pseudo-wire route */ if (rta[IP_RTA_NH_PROTO-1]->rta_len != IP_RTA_LENGTH(sizeof(Ip_u32))) return -IP_ERRNO_EINVAL; if (rta[IP_RTA_NH_PROTO_DATA-1]->rta_len != IP_RTA_LENGTH(sizeof(Ip_u32))) return -IP_ERRNO_EINVAL; ipcom_memcpy(&nh_type, IP_RTA_DATA(rta[IP_RTA_NH_PROTO-1]), sizeof(Ip_u32)); if (nh_type != IPNET_ETH_P_MPLS_UNICAST) return -IP_ERRNO_EINVAL; ipcom_memset(&gw_mpls, 0, sizeof(gw_mpls)); gw_mpls.smpls_family = IP_AF_MPLS; IPCOM_SA_LEN_SET(&gw_mpls, sizeof (struct Ip_sockaddr_mpls)); ipcom_memcpy(&gw_mpls.smpls_key, IP_RTA_DATA(rta[IP_RTA_NH_PROTO_DATA-1]), sizeof(Ip_u32)); gw = (struct Ip_sockaddr *)&gw_mpls; } #endif while(1) { if (is_multipath_route) { ifindex = nh->rtnh_ifindex; if (nh->rtnh_len > sizeof(struct Ip_rtnexthop)) { /* Multihop route has gateway */ if ((ret = ipnet_rtnetlink_route_gw_setup(family, ifindex, &ugw, &gw, IP_RTNH_DATA(nh))) < 0) return ret; } } /* Delete the route */ ret = ipnet_route_delete2(family, vr, table, &ukey, mask, gw, ifindex, 0, 0, 0); if (is_multipath_route) { mpa_size -= IP_RTNH_ALIGN(nh->rtnh_len); gw = IP_NULL; if (mpa_size > 0) nh = IP_RTNH_NEXT(nh); else break; } else break; } return is_multipath_route ? 0 : ret; }
/* *=========================================================================== * ipnet_rtnetlink_route_newroute_family *=========================================================================== * Description: Add a new rotue via NETLINK. * Parameters: data - Message payload * len - Length of message. * nlh - NETLINK message header * arg - Reference to route attributes. * * Returns: 0 - Success * <0 - Failure * */ IP_GLOBAL int ipnet_rtnetlink_route_newroute_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlh, struct Ip_rtattr **rta, int family) { Ipnet_netif *netif = IP_NULL; Ipnet_ppp_peer *p = IP_NULL; int ret = -1; Ip_u16 vr; Ip_u32 table; Ip_bool is_multipath_route = IP_FALSE; Ip_ssize_t mpa_size = 0; struct Ip_rtnexthop *nh = IP_NULL; struct Ipnet_rt_metrics metrics; struct Ip_sockaddr *gw = IP_NULL; void *mask = IP_NULL; struct Ip_sockaddr_dl gw_dl; struct Ipnet_route_add_param param; struct Ip_rtmsg *rtm = IP_NLMSG_DATA(nlh); union Ip_sockaddr_union ugw; Ipnet_rtnetlink_key_t ukey; #ifdef IPMPLS struct Ip_sockaddr_mpls gw_mpls; #endif /* IPMPLS */ #ifdef IPNET_USE_ROUTE_COOKIES Ipnet_rt_cookie cookie; #endif /* IPNET_USE_ROUTE_COOKIES */ table = IPCOM_ROUTE_TABLE_GET(rtm->rtm_table); /* Prepare route parameters */ ipcom_memset(¶m, 0, sizeof(param)); ipcom_memset(&metrics,0,sizeof(metrics)); param.flags = IPNET_RTF_STATIC | IPNET_RTF_UP | IPNET_RTF_DONE; #ifdef IPNET_USE_ROUTE_TABLE_NAMES if (rta[IP_RTA_TABLE_NAME -1]) { if (ipnet_route_vr_and_table_from_name(IP_RTA_DATA(rta[IP_RTA_TABLE_NAME - 1]), &vr, &table) < 0) return -IP_ERRNO_ESRCH; } else #endif { if (mem->vr == IPCOM_VR_ANY) return -IP_ERRNO_EINVAL; vr = ipnet_rtnetlink_vr(rta[IP_RTA_VR - 1], mem->vr); if (rta[IP_RTA_TABLE - 1]) table = IP_GET_32ON8(IP_RTA_DATA(rta[IP_RTA_TABLE - 1])); } if (rta[IP_RTA_OIF -1]) { int ifindex = 0; if (rta[IP_RTA_OIF-1]->rta_len != IP_RTA_LENGTH(sizeof(int))) return -IP_ERRNO_EINVAL; ifindex = IP_GET_32ON8(IP_RTA_DATA(rta[IP_RTA_OIF-1])); netif = ipnet_if_indextonetif(vr, ifindex); if (netif) p = netif->private_data; } if (rta[IP_RTA_MULTIPATH -1]) { is_multipath_route = IP_TRUE; nh = IP_RTA_DATA(rta[IP_RTA_MULTIPATH-1]); mpa_size = IP_RTA_PAYLOAD(rta[IP_RTA_MULTIPATH-1]); if ((nh == IP_NULL) || (mpa_size == 0)) return -IP_ERRNO_EINVAL; } if ((ret = ipnet_rtnetlink_route_key_setup(family, &ukey, netif? netif->ipcom.ifindex : 0, &mask, rtm->rtm_dst_len, rta[IP_RTA_DST-1])) < 0) return ret; if ((ret = ipnet_rtnetlink_route_gw_setup(family, netif? netif->ipcom.ifindex : 0, &ugw, &gw, rta[IP_RTA_GATEWAY-1])) < 0) return ret; #ifdef IPCOM_USE_INET if (family == IP_AF_INET) { if (gw != IP_NULL) { /* Check if it is a pure gateway or not */ if (p == IP_NULL || p->peer4.s_addr != ugw.sin.sin_addr.s_addr) param.flags |= IPNET_RTF_GATEWAY; } } else #endif #ifdef IPCOM_USE_INET6 if (family == IP_AF_INET6) { if (gw != IP_NULL) { /* Check if it is a pure gateway or not */ if (p == IP_NULL || !IP_IN6_ARE_ADDR_EQUAL(&ugw.sin6.sin6_addr, &p->peer6)) param.flags |= IPNET_RTF_GATEWAY; } } else #endif { return -IP_ERRNO_EAFNOSUPPORT; } if (!mask) param.flags |= IPNET_RTF_HOST; if (rtm->rtm_type == IP_RTN_PROXY) { /* This is a proxy arp network route */ IP_BIT_CLR(param.flags, IPNET_RTF_CLONING); IP_BIT_SET(param.flags, IPNET_RTF_PROTO2); if (rta[IP_RTA_PROXY_ARP_LLADDR - 1]) { /* This is a network proxy arp with specific lladdr */ if (netif == IP_NULL) return -IP_ERRNO_EINVAL; ipcom_memset(&gw_dl, 0, sizeof(struct Ip_sockaddr_dl)); IPCOM_SA_LEN_SET(&gw_dl, sizeof(struct Ip_sockaddr_dl)); gw_dl.sdl_family = IP_AF_LINK; gw_dl.sdl_index = (Ip_u16)netif->ipcom.ifindex; gw_dl.sdl_alen = (Ip_u8) netif->ipcom.link_addr_size; gw_dl.sdl_type = (Ip_u8) netif->ipcom.type; ipcom_memcpy(IP_SOCKADDR_DL_LLADDR(&gw_dl), IP_RTA_DATA(rta[IP_RTA_PROXY_ARP_LLADDR - 1]), IPNET_ETH_ADDR_SIZE); gw = (struct Ip_sockaddr *) &gw_dl; IP_BIT_SET(param.flags, IPNET_RTF_LLINFO); } } else if (rtm->rtm_type == IP_RTN_PROHIBIT) IP_BIT_SET(param.flags, IPNET_RTF_REJECT); else if (rtm->rtm_type == IP_RTN_THROW) IP_BIT_SET(param.flags, IPNET_RTF_SKIP); else if (rtm->rtm_type == IP_RTN_UNREACHABLE) { if (netif == IP_NULL) netif = ipnet_loopback_get_netif(vr); IP_BIT_CLR(param.flags, IPNET_RTF_UP); } else if (rtm->rtm_type == IP_RTN_CLONE) IP_BIT_SET(param.flags, IPNET_RTF_CLONING); else if (rtm->rtm_type == IP_RTN_BLACKHOLE) IP_BIT_SET(param.flags, IPNET_RTF_BLACKHOLE); #ifdef IPMPLS /* Check for IPNET MPLS pseudo-wire route */ if (rta[IP_RTA_NH_PROTO-1]) { Ip_u32 nh_type; if (rta[IP_RTA_NH_PROTO-1]->rta_len != IP_RTA_LENGTH(sizeof(Ip_u32))) return -IP_ERRNO_EINVAL; if (rta[IP_RTA_NH_PROTO_DATA-1]->rta_len != IP_RTA_LENGTH(sizeof(Ip_u32))) return -IP_ERRNO_EINVAL; ipcom_memcpy(&nh_type, IP_RTA_DATA(rta[IP_RTA_NH_PROTO-1]), sizeof(Ip_u32)); if (nh_type != IPNET_ETH_P_MPLS_UNICAST) return -IP_ERRNO_EINVAL; ipcom_memset(&gw_mpls, 0, sizeof(gw_mpls)); gw_mpls.smpls_family = IP_AF_MPLS; IPCOM_SA_LEN_SET(&gw_mpls, sizeof (struct Ip_sockaddr_mpls)); ipcom_memcpy(&gw_mpls.smpls_key, IP_RTA_DATA(rta[IP_RTA_NH_PROTO_DATA-1]), sizeof(Ip_u32)); IP_BIT_CLR(param.flags,IPNET_RTF_GATEWAY); gw = (struct Ip_sockaddr *)&gw_mpls; } else { /* Check if cloning flag shall be set */ if (IP_BIT_ISFALSE(param.flags, IPNET_RTF_HOST | IPNET_RTF_GATEWAY | IPNET_RTF_REJECT | IPNET_RTF_BLACKHOLE)) IP_BIT_SET(param.flags, IPNET_RTF_CLONING); } #endif if (rta[IP_RTA_METRICS-1]) { int rlen = (int)IP_RTA_PAYLOAD(rta[IP_RTA_METRICS-1]); Ip_u32 dummy = 0; struct Ip_rtattr *rtax = (struct Ip_rtattr*)IP_RTA_DATA(rta[IP_RTA_METRICS-1]); metrics.rmx_expire = IPCOM_ADDR_INFINITE; param.metrics = &metrics; for(;rlen > 0; rlen -= (int)rtax->rta_len,rtax = IP_RTA_NEXT(rtax,dummy)) { switch (rtax->rta_type) { case IP_RTAX_MTU: ipcom_memcpy(&metrics.rmx_mtu, IP_RTA_DATA(rtax), sizeof(Ip_u32)); break; case IP_RTAX_RTT: ipcom_memcpy(&metrics.rmx_rtt, IP_RTA_DATA(rtax), sizeof(Ip_u32)); break; case IP_RTAX_RTTVAR: ipcom_memcpy(&metrics.rmx_rttvar, IP_RTA_DATA(rtax), sizeof(Ip_u32)); break; } } } #ifdef IPNET_USE_ROUTE_COOKIES if (rta[IP_RTA_COOKIE-1]) { if (IP_RTA_PAYLOAD(rta[IP_RTA_COOKIE-1]) > sizeof(cookie)) return -IP_ERRNO_EINVAL; ipcom_memset(&cookie, 0, sizeof(cookie)); ipcom_memcpy(&cookie, IP_RTA_DATA(rta[IP_RTA_COOKIE-1]), IP_RTA_PAYLOAD(rta[IP_RTA_COOKIE-1])); param.cookie = &cookie; } #endif /* IPNET_USE_ROUTE_COOKIES */ while (1) { if (is_multipath_route && (gw == IP_NULL)) { netif = ipnet_if_indextonetif(vr, nh->rtnh_ifindex); metrics.rmx_hopcount = nh->rtnh_hops; metrics.rmx_expire = IPCOM_ADDR_INFINITE; param.metrics = &metrics; if (nh->rtnh_len > sizeof(struct Ip_rtnexthop)) { /* Multihop route has gateway */ IP_BIT_CLR(param.flags, IPNET_RTF_CLONING); IP_BIT_SET(param.flags, IPNET_RTF_GATEWAY); if ((ret = ipnet_rtnetlink_route_gw_setup(family, netif? netif->ipcom.ifindex : 0, &ugw, &gw, IP_RTNH_DATA(nh))) < 0) return ret; } mpa_size -= IP_RTNH_ALIGN(nh->rtnh_len); if (mpa_size > 0) nh = IP_RTNH_NEXT(nh); } param.domain = family; param.vr = vr; param.table = table; param.netif = netif; param.key = &ukey; param.netmask = mask; param.gateway = gw; param.pid = mem->pid; param.seq = nlh->nlmsg_seq; param.no_ref_count = IP_TRUE; /* Try to add the route */ if (netif) IPNET_IF_LOCK(netif); ret = ipnet_route_add(¶m); if (netif != IP_NULL) IPNET_IF_UNLOCK(netif); if (is_multipath_route) { gw = IP_NULL; if (ret < 0 && ret != -IP_ERRNO_EEXIST) goto rollback; if (mpa_size == 0) { ret = 0; break; } else if (mpa_size < 0) { /* Malformed packet */ ret = -IP_ERRNO_EINVAL; goto rollback; } } else break; } return ret; rollback: (void)ipnet_rtnetlink_route_delroute_family(mem, nlh, rta, family); return ret; }