/* *=========================================================================== * 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; }
/* *=========================================================================== * ipnet_rtnetlink_neigh_newneigh_family *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL int ipnet_rtnetlink_neigh_newneigh_family(Ipnet_netlink_mem_t *mem, struct Ip_nlmsghdr *nlmsg, struct Ip_rtattr **rta, int family) { Ipnet_netif *netif; Ipnet_route_entry *rt; int nd_state; int ret = -IP_ERRNO_EAFNOSUPPORT; Ip_u16 vr; Ip_u32 table = IPCOM_ROUTE_TABLE_DEFAULT; struct Ip_sockaddr_dl dl; struct Ip_ndmsg *ndm = IP_NLMSG_DATA(nlmsg); void *link_addr = IP_NULL; Ipnet_rtnetlink_key_t ukey; /* Destination address required */ if (rta[IP_NDA_DST-1] == IP_NULL) return -IP_ERRNO_EINVAL; #ifdef IPNET_USE_ROUTE_TABLE_NAMES if (rta[IP_NDA_TABLE_NAME -1]) { /* Extract router and table from name */ if (ipnet_route_vr_and_table_from_name(IP_RTA_DATA(rta[IP_NDA_TABLE_NAME - 1]), &vr, &table) < 0) return -IP_ERRNO_ESRCH; } else #endif /* IPNET_USE_ROUTE_TABLE_NAMES */ { if (mem->vr == IPCOM_VR_ANY) return -IP_ERRNO_EINVAL; vr = ipnet_rtnetlink_vr(rta[IP_NDA_VR - 1], mem->vr); if (rta[IP_NDA_TABLE - 1]) table = IP_GET_32ON8(IP_RTA_DATA(rta[IP_NDA_TABLE - 1])); } netif = ipnet_if_indextonetif(vr, ndm->ndm_ifindex); if (netif && (netif->vr_index != vr)) return -IP_ERRNO_EINVAL; if ((ret = ipnet_rtnetlink_route_key_setup(family, &ukey, netif? netif->ipcom.ifindex : 0, IP_NULL, -1, rta[IP_NDA_DST-1])) < 0) return ret; if (rta[IP_NDA_LLADDR-1]) /* Neighbors link layer address */ link_addr = IP_RTA_DATA(rta[IP_NDA_LLADDR-1]); else if (IP_BIT_ISSET(ndm->ndm_flags, IP_NTF_PROXY)) { /* Proxy ARP entry. Use Link layer address of local netif */ if (netif == IP_NULL) return -IP_ERRNO_EINVAL; link_addr = netif->ipcom.link_addr; } /* Check if neighbor route exists */ ret = ipnet_route_lookup(family, vr, table, IPNET_RTL_FLAG_LINK_LOCAL | IPNET_RTL_FLAG_DONTCLONE, &ukey, netif ? netif->ipcom.ifindex : 0, netif ? netif->ipcom.ifindex : 0, &rt); if ((ret == IPNET_ROUTE_PERFECT_MATCH) && (rt->netif == netif) && IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_HOST) && IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_LLINFO)) { if (rt->data == IP_NULL) return -IP_ERRNO_EINVAL; /* Get the neighbour state */ nd_state = *(Ipnet_nd_state_t *)rt->data; if (((ndm->ndm_state == IP_NUD_INCOMPLETE) && (nd_state != IPNET_ND_UNINITIALIZED) && (nd_state != IPNET_ND_INCOMPLETE)) || ((ndm->ndm_state == IP_NUD_DELAY) && (nd_state != IPNET_ND_STALE))) { return -IP_ERRNO_EINVAL; } } else { struct Ipnet_rt_metrics metrics; struct Ipnet_route_add_param param; /* Create new neighbor entry */ if (ndm->ndm_state == IP_NUD_DELAY) ndm->ndm_state = IP_NUD_INCOMPLETE; ipcom_memset(¶m, 0, sizeof(struct Ipnet_route_add_param)); ipcom_memset(&dl, 0, sizeof(struct Ip_sockaddr_dl)); param.domain = family; param.vr = vr; param.table = table; param.key = &ukey; param.netif = netif; param.flags = IPNET_RTF_UP | IPNET_RTF_HOST | IPNET_RTF_LLINFO | IPNET_RTF_DONE; param.gateway = (struct Ip_sockaddr*) &dl; if (ndm->ndm_state != IP_NUD_PERMANENT) { ipcom_memset(&metrics, 0, sizeof(metrics)); /* The correct timeout will be set when the ND state is set, but must be != infinite for now */ metrics.rmx_expire = 1; param.metrics = &metrics; } IPCOM_SA_LEN_SET(&dl, sizeof(struct Ip_sockaddr_dl)); dl.sdl_family = IP_AF_LINK; dl.sdl_index = (Ip_u16)(netif ? netif->ipcom.ifindex : 0); dl.sdl_alen = 6; dl.sdl_type = IP_IFT_ETHER; if (link_addr) ipcom_memcpy(&dl.sdl_data[0], link_addr,dl.sdl_alen); if (netif) IPNET_IF_LOCK(netif); /* Add new neighbor */ ret = ipnet_route_add(¶m); if (netif) IPNET_IF_UNLOCK(netif); if (ret < 0) return ret; /* Get the new neighbor route entry */ ret = ipnet_route_lookup(family, vr, table, IPNET_RTL_FLAG_LINK_LOCAL | IPNET_RTL_FLAG_DONTCLONE, &ukey, netif ? netif->ipcom.ifindex : 0, netif ? netif->ipcom.ifindex : 0, &rt); if (ret < 0) return ret; } #ifdef IPNET_COMPENSATE_UNRELIABLE_NETLINK /* Set neighbor acknowledge bit */ IP_BIT_SET(rt->hdr.flags, IPNET_RTF_X_NEIGH_ACK); #endif if (IP_BIT_ISSET(ndm->ndm_flags,IP_NTF_PROXY)) /* Proxy ARP entry */ IP_BIT_SET(rt->hdr.flags, IPNET_RTF_PROTO2); if (IP_BIT_ISSET(ndm->ndm_state, IP_NUD_PERMANENT)) ipnet_route_set_lifetime(rt, IPCOM_ADDR_INFINITE); nd_state = ipnet_rtnetlink_neigh_nud2nc(ndm->ndm_state); #ifdef IPCOM_USE_INET if (family == IP_AF_INET) ret = ipnet_rtnetlink_ip4_neigh_setup(&ukey, ndm, rt, nd_state, link_addr, rta); else #endif #ifdef IPCOM_USE_INET6 if (family == IP_AF_INET6) ret = ipnet_rtnetlink_ip6_neigh_setup(&ukey, ndm, rt, nd_state, link_addr, rta); else #endif return -IP_ERRNO_EAFNOSUPPORT; return ret; }