int route_configure(Route *route, Link *link, sd_rtnl_message_handler_t callback) { _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL; int r; assert(link); assert(link->manager); assert(link->manager->rtnl); assert(link->ifindex > 0); assert(route->family == AF_INET || route->family == AF_INET6); r = sd_rtnl_message_new_route(link->manager->rtnl, RTM_NEWROUTE, route->family, &req); if (r < 0) { log_error("Could not create RTM_NEWROUTE message: %s", strerror(-r)); return r; } if (route->family == AF_INET) r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in); else if (route->family == AF_INET6) r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6); if (r < 0) { log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r)); return r; } if (route->dst_prefixlen) { if (route->family == AF_INET) r = sd_rtnl_message_append_in_addr(req, RTA_DST, &route->dst_addr.in); else if (route->family == AF_INET6) r = sd_rtnl_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6); if (r < 0) { log_error("Could not append RTA_DST attribute: %s", strerror(-r)); return r; } r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); if (r < 0) { log_error("Could not set destination prefix length: %s", strerror(-r)); return r; } } r = sd_rtnl_message_append_u32(req, RTA_OIF, link->ifindex); if (r < 0) { log_error("Could not append RTA_OIF attribute: %s", strerror(-r)); return r; } r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL); if (r < 0) { log_error("Could not send rtnetlink message: %s", strerror(-r)); return r; } return 0; }
static void test_route(void) { _cleanup_rtnl_message_unref_ sd_rtnl_message *req; struct in_addr addr; uint32_t index = 2; uint16_t type; void *data; uint32_t u32_data; int r; struct rtmsg *rtm; r = sd_rtnl_message_new_route(NULL, &req, RTM_NEWROUTE, AF_INET); if (r < 0) { log_error("Could not create RTM_NEWROUTE message: %s", strerror(-r)); return; } addr.s_addr = htonl(INADDR_LOOPBACK); r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &addr); if (r < 0) { log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r)); return; } r = sd_rtnl_message_append_u32(req, RTA_OIF, index); if (r < 0) { log_error("Could not append RTA_OIF attribute: %s", strerror(-r)); return; } assert_se(rtnl_message_seal(NULL, req) >= 0); assert_se(sd_rtnl_message_read(req, &type, &data) > 0); assert_se(type == RTA_GATEWAY); assert_se(((struct in_addr *)data)->s_addr == addr.s_addr); assert_se(sd_rtnl_message_read(req, &type, &data) > 0); assert_se(type == RTA_OIF); assert_se(*(uint32_t *) data == index); rtm = NLMSG_DATA(req->hdr); r = rtnl_message_parse(req, &req->rta_offset_tb, &req->rta_tb_size, RTA_MAX, RTM_RTA(rtm), RTM_PAYLOAD(req->hdr)); assert_se(sd_rtnl_message_read_u32(req, RTA_GATEWAY, &u32_data) == 0); assert_se(sd_rtnl_message_read_u32(req, RTA_OIF, &u32_data) == 0); assert_se((req = sd_rtnl_message_unref(req)) == NULL); }
static void test_route(void) { _cleanup_rtnl_message_unref_ sd_rtnl_message *req; struct in_addr addr, addr_data; uint32_t index = 2, u32_data; int r; r = sd_rtnl_message_new_route(NULL, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC); if (r < 0) { log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); return; } addr.s_addr = htonl(INADDR_LOOPBACK); r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &addr); if (r < 0) { log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); return; } r = sd_rtnl_message_append_u32(req, RTA_OIF, index); if (r < 0) { log_error_errno(r, "Could not append RTA_OIF attribute: %m"); return; } assert_se(sd_rtnl_message_rewind(req) >= 0); assert_se(sd_rtnl_message_read_in_addr(req, RTA_GATEWAY, &addr_data) >= 0); assert_se(addr_data.s_addr == addr.s_addr); assert_se(sd_rtnl_message_read_u32(req, RTA_OIF, &u32_data) >= 0); assert_se(u32_data == index); assert_se((req = sd_rtnl_message_unref(req)) == NULL); }
int route_drop(Route *route, Link *link, sd_netlink_message_handler_t callback) { _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL; int r; assert(link); assert(link->manager); assert(link->manager->rtnl); assert(link->ifindex > 0); assert(route->family == AF_INET || route->family == AF_INET6); r = sd_rtnl_message_new_route(link->manager->rtnl, &req, RTM_DELROUTE, route->family, route->protocol); if (r < 0) return log_error_errno(r, "Could not create RTM_DELROUTE message: %m"); if (!in_addr_is_null(route->family, &route->in_addr)) { if (route->family == AF_INET) r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in); else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); } if (route->dst_prefixlen) { if (route->family == AF_INET) r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in); else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_DST attribute: %m"); r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); if (r < 0) return log_error_errno(r, "Could not set destination prefix length: %m"); } if (route->src_prefixlen) { if (route->family == AF_INET) r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in); else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_DST attribute: %m"); r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); if (r < 0) return log_error_errno(r, "Could not set source prefix length: %m"); } if (!in_addr_is_null(route->family, &route->prefsrc_addr)) { if (route->family == AF_INET) r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in); else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m"); } r = sd_rtnl_message_route_set_scope(req, route->scope); if (r < 0) return log_error_errno(r, "Could not set scope: %m"); r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics); if (r < 0) return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); if (r < 0) return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL); if (r < 0) return log_error_errno(r, "Could not send rtnetlink message: %m"); link_ref(link); return 0; }
int local_gateways(sd_netlink *context, int ifindex, int af, struct local_address **ret) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; _cleanup_free_ struct local_address *list = NULL; sd_netlink_message *m = NULL; size_t n_list = 0, n_allocated = 0; int r; assert(ret); if (context) rtnl = sd_netlink_ref(context); else { r = sd_netlink_open(&rtnl); if (r < 0) return r; } r = sd_rtnl_message_new_route(rtnl, &req, RTM_GETROUTE, af, RTPROT_UNSPEC); if (r < 0) return r; r = sd_netlink_message_request_dump(req, true); if (r < 0) return r; r = sd_netlink_call(rtnl, req, 0, &reply); if (r < 0) return r; for (m = reply; m; m = sd_netlink_message_next(m)) { struct local_address *a; uint16_t type; unsigned char dst_len, src_len; uint32_t ifi; int family; r = sd_netlink_message_get_errno(m); if (r < 0) return r; r = sd_netlink_message_get_type(m, &type); if (r < 0) return r; if (type != RTM_NEWROUTE) continue; /* We only care for default routes */ r = sd_rtnl_message_route_get_dst_prefixlen(m, &dst_len); if (r < 0) return r; if (dst_len != 0) continue; r = sd_rtnl_message_route_get_src_prefixlen(m, &src_len); if (r < 0) return r; if (src_len != 0) continue; r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi); if (r == -ENODATA) /* Not all routes have an RTA_OIF attribute (for example nexthop ones) */ continue; if (r < 0) return r; if (ifindex > 0 && (int) ifi != ifindex) continue; r = sd_rtnl_message_route_get_family(m, &family); if (r < 0) return r; if (af != AF_UNSPEC && af != family) continue; if (!GREEDY_REALLOC0(list, n_allocated, n_list + 1)) return -ENOMEM; a = list + n_list; switch (family) { case AF_INET: r = sd_netlink_message_read_in_addr(m, RTA_GATEWAY, &a->address.in); if (r < 0) continue; break; case AF_INET6: r = sd_netlink_message_read_in6_addr(m, RTA_GATEWAY, &a->address.in6); if (r < 0) continue; break; default: continue; } sd_netlink_message_read_u32(m, RTA_PRIORITY, &a->metric); a->ifindex = ifi; a->family = family; n_list++; } if (n_list > 0) qsort(list, n_list, sizeof(struct local_address), address_compare); *ret = TAKE_PTR(list); return (int) n_list; }
int route_configure( Route *route, Link *link, sd_netlink_message_handler_t callback) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL; usec_t lifetime; int r; assert(link); assert(link->manager); assert(link->manager->rtnl); assert(link->ifindex > 0); assert(route->family == AF_INET || route->family == AF_INET6); if (route_get(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL) <= 0 && set_size(link->routes) >= ROUTES_PER_LINK_MAX) return -E2BIG; r = sd_rtnl_message_new_route(link->manager->rtnl, &req, RTM_NEWROUTE, route->family, route->protocol); if (r < 0) return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); if (!in_addr_is_null(route->family, &route->gw)) { if (route->family == AF_INET) r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in); else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); r = sd_rtnl_message_route_set_family(req, route->family); if (r < 0) return log_error_errno(r, "Could not set route family: %m"); } if (route->dst_prefixlen) { if (route->family == AF_INET) r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in); else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_DST attribute: %m"); r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); if (r < 0) return log_error_errno(r, "Could not set destination prefix length: %m"); } if (route->src_prefixlen) { if (route->family == AF_INET) r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in); else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_SRC attribute: %m"); r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); if (r < 0) return log_error_errno(r, "Could not set source prefix length: %m"); } if (!in_addr_is_null(route->family, &route->prefsrc)) { if (route->family == AF_INET) r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in); else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m"); } r = sd_rtnl_message_route_set_scope(req, route->scope); if (r < 0) return log_error_errno(r, "Could not set scope: %m"); r = sd_rtnl_message_route_set_flags(req, route->flags); if (r < 0) return log_error_errno(r, "Could not set flags: %m"); if (route->table != RT_TABLE_DEFAULT) { if (route->table < 256) { r = sd_rtnl_message_route_set_table(req, route->table); if (r < 0) return log_error_errno(r, "Could not set route table: %m"); } else { r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC); if (r < 0) return log_error_errno(r, "Could not set route table: %m"); /* Table attribute to allow more than 256. */ r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table)); if (r < 0) return log_error_errno(r, "Could not append RTA_TABLE attribute: %m"); } } r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority); if (r < 0) return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref); if (r < 0) return log_error_errno(r, "Could not append RTA_PREF attribute: %m"); r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); if (r < 0) return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL); if (r < 0) return log_error_errno(r, "Could not send rtnetlink message: %m"); link_ref(link); lifetime = route->lifetime; r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, &route); if (r < 0) return log_error_errno(r, "Could not add route: %m"); /* TODO: drop expiration handling once it can be pushed into the kernel */ route->lifetime = lifetime; if (route->lifetime != USEC_INFINITY) { r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(), route->lifetime, 0, route_expire_handler, route); if (r < 0) return log_error_errno(r, "Could not arm expiration timer: %m"); } sd_event_source_unref(route->expire); route->expire = expire; expire = NULL; return 0; }
int route_remove(Route *route, Link *link, sd_netlink_message_handler_t callback) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; assert(link); assert(link->manager); assert(link->manager->rtnl); assert(link->ifindex > 0); assert(IN_SET(route->family, AF_INET, AF_INET6)); r = sd_rtnl_message_new_route(link->manager->rtnl, &req, RTM_DELROUTE, route->family, route->protocol); if (r < 0) return log_error_errno(r, "Could not create RTM_DELROUTE message: %m"); if (!in_addr_is_null(route->family, &route->gw)) { if (route->family == AF_INET) r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in); else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); } if (route->dst_prefixlen) { if (route->family == AF_INET) r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in); else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_DST attribute: %m"); r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); if (r < 0) return log_error_errno(r, "Could not set destination prefix length: %m"); } if (route->src_prefixlen) { if (route->family == AF_INET) r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in); else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_SRC attribute: %m"); r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); if (r < 0) return log_error_errno(r, "Could not set source prefix length: %m"); } if (!in_addr_is_null(route->family, &route->prefsrc)) { if (route->family == AF_INET) r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in); else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m"); } r = sd_rtnl_message_route_set_scope(req, route->scope); if (r < 0) return log_error_errno(r, "Could not set scope: %m"); r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority); if (r < 0) return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE)) { r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); if (r < 0) return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); } r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL); if (r < 0) return log_error_errno(r, "Could not send rtnetlink message: %m"); link_ref(link); return 0; }