/* Add/Delete IP route to/from a specific interface */ static int netlink_route(ip_route_t *iproute, int cmd) { int status = 1; struct { struct nlmsghdr n; struct rtmsg r; char buf[RTM_SIZE]; } req; char buf[RTA_SIZE]; struct rtattr *rta = (void*)buf; memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); if (cmd == IPROUTE_DEL) { req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_DELROUTE; } else { req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; if (cmd == IPROUTE_REPLACE) req.n.nlmsg_flags |= NLM_F_REPLACE; req.n.nlmsg_type = RTM_NEWROUTE; } rta->rta_type = RTA_METRICS; rta->rta_len = RTA_LENGTH(0); req.r.rtm_family = iproute->family; if (iproute->table < 256) req.r.rtm_table = iproute->table; else { req.r.rtm_table = RT_TABLE_UNSPEC; addattr32(&req.n, sizeof(req), RTA_TABLE, iproute->table); } if (cmd == IPROUTE_DEL) { req.r.rtm_scope = RT_SCOPE_NOWHERE; if (iproute->mask & IPROUTE_BIT_TYPE) req.r.rtm_type = iproute->type; } else { req.r.rtm_protocol = RTPROT_BOOT; req.r.rtm_scope = RT_SCOPE_UNIVERSE; req.r.rtm_type = iproute->type; } if (iproute->mask & IPROUTE_BIT_PROTOCOL) req.r.rtm_protocol = iproute->protocol; if (iproute->mask & IPROUTE_BIT_SCOPE) req.r.rtm_scope = iproute->scope; if (iproute->dst) { req.r.rtm_dst_len = iproute->dst->ifa.ifa_prefixlen; add_addr2req(&req.n, sizeof(req), RTA_DST, iproute->dst); } if (iproute->src) { req.r.rtm_src_len = iproute->src->ifa.ifa_prefixlen; add_addr2req(&req.n, sizeof(req), RTA_SRC, iproute->src); } if (iproute->pref_src) add_addr2req(&req.n, sizeof(req), RTA_PREFSRC, iproute->pref_src); //#ifdef _HAVE_RTA_NEWDST_ // if (iproute->as_to) // add_addr2req(&req.n, sizeof(req), RTA_NEWDST, iproute->as_to); //#endif if (iproute->via) { if (iproute->via->ifa.ifa_family == iproute->family) add_addr2req(&req.n, sizeof(req), RTA_GATEWAY, iproute->via); #ifdef _HAVE_RTA_VIA_ else add_addr_fam2req(&req.n, sizeof(req), RTA_VIA, iproute->via); #endif } #ifdef _HAVE_RTA_ENCAP_ if (iproute->encap.type != LWTUNNEL_ENCAP_NONE) { char encap_buf[ENCAP_RTA_SIZE]; struct rtattr *encap_rta = (void *)encap_buf; encap_rta->rta_type = RTA_ENCAP; encap_rta->rta_len = RTA_LENGTH(0); add_encap(encap_rta, sizeof(encap_buf), &iproute->encap); if (encap_rta->rta_len > RTA_LENGTH(0)) addraw_l(&req.n, sizeof(encap_buf), RTA_DATA(encap_rta), RTA_PAYLOAD(encap_rta)); } #endif if (iproute->mask & IPROUTE_BIT_DSFIELD) req.r.rtm_tos = iproute->tos; if (iproute->oif) addattr32(&req.n, sizeof(req), RTA_OIF, iproute->oif->ifindex); if (iproute->mask & IPROUTE_BIT_METRIC) addattr32(&req.n, sizeof(req), RTA_PRIORITY, iproute->metric); req.r.rtm_flags = iproute->flags; if (iproute->realms) addattr32(&req.n, sizeof(req), RTA_FLOW, iproute->realms); #ifdef _HAVE_RTA_EXPIRES_ if (iproute->mask & IPROUTE_BIT_EXPIRES) addattr32(&req.n, sizeof(req), RTA_EXPIRES, iproute->expires); #endif #ifdef RTAX_CC_ALGO if (iproute->congctl) rta_addattr_l(rta, sizeof(buf), RTAX_CC_ALGO, iproute->congctl, strlen(iproute->congctl)); #endif if (iproute->mask & IPROUTE_BIT_RTT) rta_addattr32(rta, sizeof(buf), RTAX_RTT, iproute->rtt); if (iproute->mask & IPROUTE_BIT_RTTVAR) rta_addattr32(rta, sizeof(buf), RTAX_RTTVAR, iproute->rttvar); if (iproute->mask & IPROUTE_BIT_RTO_MIN) rta_addattr32(rta, sizeof(buf), RTAX_RTO_MIN, iproute->rto_min); #ifdef RTAX_FEATURES if (iproute->features) rta_addattr32(rta, sizeof(buf), RTAX_FEATURES, iproute->features); #endif if (iproute->mask & IPROUTE_BIT_MTU) rta_addattr32(rta, sizeof(buf), RTAX_MTU, iproute->mtu); if (iproute->mask & IPROUTE_BIT_WINDOW) rta_addattr32(rta, sizeof(buf), RTAX_WINDOW, iproute->window); if (iproute->mask & IPROUTE_BIT_SSTHRESH) rta_addattr32(rta, sizeof(buf), RTAX_SSTHRESH, iproute->ssthresh); if (iproute->mask & IPROUTE_BIT_CWND) rta_addattr32(rta, sizeof(buf), RTAX_CWND, iproute->cwnd); if (iproute->mask & IPROUTE_BIT_ADVMSS) rta_addattr32(rta, sizeof(buf), RTAX_ADVMSS, iproute->advmss); if (iproute->mask & IPROUTE_BIT_REORDERING) rta_addattr32(rta, sizeof(buf), RTAX_REORDERING, iproute->reordering); if (iproute->mask & IPROUTE_BIT_HOPLIMIT) rta_addattr32(rta, sizeof(buf), RTAX_HOPLIMIT, iproute->hoplimit); if (iproute->mask & IPROUTE_BIT_INITCWND) rta_addattr32(rta, sizeof(buf), RTAX_INITCWND, iproute->initcwnd); #ifdef RTAX_INITRWND if (iproute->mask & IPROUTE_BIT_INITRWND) rta_addattr32(rta, sizeof(buf), RTAX_INITRWND, iproute->initrwnd); #endif #ifdef RTAX_QUICKACK if (iproute->mask & IPROUTE_BIT_QUICKACK) rta_addattr32(rta, sizeof(buf), RTAX_QUICKACK, iproute->quickack); #endif #ifdef _HAVE_RTA_PREF_ if (iproute->mask & IPROUTE_BIT_PREF) addattr8(&req.n, sizeof(req), RTA_PREF, iproute->pref); #endif if (rta->rta_len > RTA_LENGTH(0)) { if (iproute->lock) rta_addattr32(rta, sizeof(buf), RTAX_LOCK, iproute->lock); addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(rta), RTA_PAYLOAD(rta)); } if (!LIST_ISEMPTY(iproute->nhs)) add_nexthops(iproute, &req.n, &req.r); #ifdef DEBUG_NETLINK_MSG size_t i, j; uint8_t *p; char lbuf[3072]; char *op = lbuf; log_message(LOG_INFO, "rtmsg buffer used %lu, rtattr buffer used %d", req.n.nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg)), rta->rta_len); op += snprintf(op, sizeof(lbuf) - (op - lbuf), "nlmsghdr %p(%u):", &req.n, req.n.nlmsg_len); for (i = 0, p = (uint8_t*)&req.n; i < sizeof(struct nlmsghdr); i++) op += snprintf(op, sizeof(lbuf) - (op - lbuf), " %2.2hhx", *(p++)); log_message(LOG_INFO, "%s\n", lbuf); op = lbuf; op += snprintf(op, sizeof(lbuf) - (op - lbuf), "rtmsg %p(%lu):", &req.r, req.n.nlmsg_len - sizeof(struct nlmsghdr)); for (i = 0, p = (uint8_t*)&req.r; i < + req.n.nlmsg_len - sizeof(struct nlmsghdr); i++) op += snprintf(op, sizeof(lbuf) - (op - lbuf), " %2.2hhx", *(p++)); for (j = 0; lbuf + j < op; j+= MAX_LOG_MSG) log_message(LOG_INFO, "%.*\n", MAX_LOG_MSG, lbuf+j); #endif /* This returns ESRCH if the address of via address doesn't exist */ /* ENETDOWN if dev p33p1.40 for example is down */ if (netlink_talk(&nl_cmd, &req.n) < 0) { #ifdef _HAVE_RTA_EXPIRES_ /* If an expiry was set on the route, it may have disappeared already */ if (cmd != IPADDRESS_DEL || !(iproute->mask & IPROUTE_BIT_EXPIRES)) #endif status = -1; } return status; }
/* * route_exec: replaces, adds or deletes a route from the routing table. * `to' and nhops->gw must be addresses given in network order */ int route_exec(int route_cmd, int route_type, int route_scope, unsigned flags, inet_prefix * src, inet_prefix * to, struct nexthop *nhops, char *dev, u_char table) { struct rt_request req; struct rtnl_handle rth; setzero(&req, sizeof(req)); if (!table) table = RT_TABLE_MAIN; req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.nh.nlmsg_flags = NLM_F_REQUEST | flags; req.nh.nlmsg_type = route_cmd; req.rt.rtm_family = AF_UNSPEC; req.rt.rtm_table = table; req.rt.rtm_protocol = RTPROT_NETSUKUKU; req.rt.rtm_scope = RT_SCOPE_NOWHERE; req.rt.rtm_type = RTN_UNSPEC; /* kernel protocol layer */ if (table == RT_TABLE_LOCAL) req.rt.rtm_protocol = RTPROT_KERNEL; if (route_cmd != RTM_DELROUTE) { req.rt.rtm_scope = RT_SCOPE_UNIVERSE; req.rt.rtm_type = RTN_UNICAST; } if (route_type) req.rt.rtm_type = route_type; if (route_scope) req.rt.rtm_scope = route_scope; else if (req.rt.rtm_type == RTN_LOCAL) req.rt.rtm_scope = RT_SCOPE_HOST; if (rtnl_open(&rth, 0) < 0) return -1; if (dev || nhops) ll_init_map(&rth); #ifdef LINUX_2_6_14 uint32_t mp_alg = NTK_MULTIPATH_ALGO; addattr_l(&req.n, sizeof(req), RTA_MP_ALGO, &mp_alg, sizeof(mp_alg)); #endif if (dev) { int idx; if ((idx = ll_name_to_index(dev)) == 0) { error("%s:%d, Device \"%s\" doesn't really exist\n", ERROR_POS, dev); return -1; } addattr32(&req.nh, sizeof(req), RTA_OIF, idx); } if (to) { req.rt.rtm_family = to->family; req.rt.rtm_dst_len = to->bits; if (!to->data[0] && !to->data[1] && !to->data[2] && !to->data[3]) { /* Modify the default gw */ if (route_cmd == RTM_DELROUTE) req.rt.rtm_protocol = 0; } if (to->len) addattr_l(&req.nh, sizeof(req), RTA_DST, &to->data, to->len); } if (src) { if (req.rt.rtm_family == AF_UNSPEC) req.rt.rtm_family = src->family; addattr_l(&req.nh, sizeof(req), RTA_PREFSRC, &src->data, src->len); } if (nhops) add_nexthops(&req.nh, &req.rt, nhops); if (req.rt.rtm_family == AF_UNSPEC) req.rt.rtm_family = AF_INET; /*Finaly stage: <<Hey krnl, r u there?>> */ if (rtnl_talk(&rth, &req.nh, 0, 0, NULL, NULL, NULL) < 0) return -1; rtnl_close(&rth); return 0; }