diff --git a/src/frontends/android/app/src/main/jni/libandroidbridge/kernel/android_net.c b/src/frontends/android/app/src/main/jni/libandroidbridge/kernel/android_net.c index 3a312048a..cc6072244 100644 --- a/src/frontends/android/app/src/main/jni/libandroidbridge/kernel/android_net.c +++ b/src/frontends/android/app/src/main/jni/libandroidbridge/kernel/android_net.c @@ -256,14 +256,14 @@ METHOD(kernel_net_t, del_ip, status_t, METHOD(kernel_net_t, add_route, status_t, private_android_net_t *this, chunk_t dst_net, uint8_t prefixlen, - host_t *gateway, host_t *src_ip, char *if_name) + host_t *gateway, host_t *src_ip, char *if_name, bool pass) { return NOT_SUPPORTED; } METHOD(kernel_net_t, del_route, status_t, private_android_net_t *this, chunk_t dst_net, uint8_t prefixlen, - host_t *gateway, host_t *src_ip, char *if_name) + host_t *gateway, host_t *src_ip, char *if_name, bool pass) { return NOT_SUPPORTED; } diff --git a/src/libcharon/kernel/kernel_interface.c b/src/libcharon/kernel/kernel_interface.c index ced026dfc..300796f8e 100644 --- a/src/libcharon/kernel/kernel_interface.c +++ b/src/libcharon/kernel/kernel_interface.c @@ -614,26 +614,28 @@ METHOD(kernel_interface_t, del_ip, status_t, METHOD(kernel_interface_t, add_route, status_t, private_kernel_interface_t *this, chunk_t dst_net, - uint8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name) + uint8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name, + bool pass) { if (!this->net) { return NOT_SUPPORTED; } return this->net->add_route(this->net, dst_net, prefixlen, gateway, - src_ip, if_name); + src_ip, if_name, pass); } METHOD(kernel_interface_t, del_route, status_t, private_kernel_interface_t *this, chunk_t dst_net, - uint8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name) + uint8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name, + bool pass) { if (!this->net) { return NOT_SUPPORTED; } return this->net->del_route(this->net, dst_net, prefixlen, gateway, - src_ip, if_name); + src_ip, if_name, pass); } METHOD(kernel_interface_t, bypass_socket, bool, diff --git a/src/libcharon/kernel/kernel_interface.h b/src/libcharon/kernel/kernel_interface.h index 141198ac3..50c1cac53 100644 --- a/src/libcharon/kernel/kernel_interface.h +++ b/src/libcharon/kernel/kernel_interface.h @@ -375,12 +375,13 @@ struct kernel_interface_t { * @param gateway gateway for this route * @param src_ip source ip of the route * @param if_name name of the interface the route is bound to + * @param pass TRUE if route is installed for passthrough policy * @return SUCCESS if operation completed * ALREADY_DONE if the route already exists */ status_t (*add_route) (kernel_interface_t *this, chunk_t dst_net, uint8_t prefixlen, host_t *gateway, host_t *src_ip, - char *if_name); + char *if_name, bool pass); /** * Delete a route. @@ -390,11 +391,12 @@ struct kernel_interface_t { * @param gateway gateway for this route * @param src_ip source ip of the route * @param if_name name of the interface the route is bound to + * @param pass TRUE if route was installed for passthrough policy * @return SUCCESS if operation completed */ status_t (*del_route) (kernel_interface_t *this, chunk_t dst_net, uint8_t prefixlen, host_t *gateway, host_t *src_ip, - char *if_name); + char *if_name, bool pass); /** * Set up a bypass policy for a given socket. diff --git a/src/libcharon/kernel/kernel_net.h b/src/libcharon/kernel/kernel_net.h index 12475b123..11e197013 100644 --- a/src/libcharon/kernel/kernel_net.h +++ b/src/libcharon/kernel/kernel_net.h @@ -165,12 +165,13 @@ struct kernel_net_t { * @param gateway gateway for this route * @param src_ip source ip of the route * @param if_name name of the interface the route is bound to + * @param pass TRUE if route is installed for passthrough policy * @return SUCCESS if operation completed * ALREADY_DONE if the route already exists */ status_t (*add_route) (kernel_net_t *this, chunk_t dst_net, uint8_t prefixlen, host_t *gateway, host_t *src_ip, - char *if_name); + char *if_name, bool pass); /** * Delete a route. @@ -180,11 +181,12 @@ struct kernel_net_t { * @param gateway gateway for this route * @param src_ip source ip of the route * @param if_name name of the interface the route is bound to + * @param pass TRUE if route was installed for passthrough policy * @return SUCCESS if operation completed */ status_t (*del_route) (kernel_net_t *this, chunk_t dst_net, uint8_t prefixlen, host_t *gateway, host_t *src_ip, - char *if_name); + char *if_name, bool pass); /** * Destroy the implementation. diff --git a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c index 18a87b707..6b5d8249a 100644 --- a/src/libcharon/plugins/kernel_iph/kernel_iph_net.c +++ b/src/libcharon/plugins/kernel_iph/kernel_iph_net.c @@ -715,14 +715,14 @@ static status_t manage_route(private_kernel_iph_net_t *this, bool add, METHOD(kernel_net_t, add_route, status_t, private_kernel_iph_net_t *this, chunk_t dst, uint8_t prefixlen, - host_t *gateway, host_t *src, char *name) + host_t *gateway, host_t *src, char *name, bool pass) { return manage_route(this, TRUE, dst, prefixlen, gateway, name); } METHOD(kernel_net_t, del_route, status_t, private_kernel_iph_net_t *this, chunk_t dst, uint8_t prefixlen, - host_t *gateway, host_t *src, char *name) + host_t *gateway, host_t *src, char *name, bool pass) { return manage_route(this, FALSE, dst, prefixlen, gateway, name); } diff --git a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c index 3665d4557..4c2933a03 100644 --- a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c +++ b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c @@ -315,7 +315,7 @@ static void add_exclude_route(private_kernel_libipsec_ipsec_t *this, if (charon->kernel->get_interface(charon->kernel, src, &if_name) && charon->kernel->add_route(charon->kernel, dst->get_address(dst), dst->get_family(dst) == AF_INET ? 32 : 128, - gtw, src, if_name) == SUCCESS) + gtw, src, if_name, TRUE) == SUCCESS) { INIT(exclude, .dst = dst->clone(dst), @@ -363,7 +363,7 @@ static void remove_exclude_route(private_kernel_libipsec_ipsec_t *this, charon->kernel->del_route(charon->kernel, dst->get_address(dst), dst->get_family(dst) == AF_INET ? 32 : 128, route->exclude->gtw, route->exclude->src, - if_name) != SUCCESS) + if_name, TRUE) != SUCCESS) { DBG1(DBG_KNL, "uninstalling exclude route for %H failed", dst); } @@ -449,8 +449,8 @@ static bool install_route(private_kernel_libipsec_ipsec_t *this, } /* uninstall previously installed route */ if (charon->kernel->del_route(charon->kernel, old->dst_net, - old->prefixlen, old->gateway, - old->src_ip, old->if_name) != SUCCESS) + old->prefixlen, old->gateway, old->src_ip, + old->if_name, FALSE) != SUCCESS) { DBG1(DBG_KNL, "error uninstalling route installed with policy " "%R === %R %N", src_ts, dst_ts, policy_dir_names, @@ -481,7 +481,7 @@ static bool install_route(private_kernel_libipsec_ipsec_t *this, switch (charon->kernel->add_route(charon->kernel, route->dst_net, route->prefixlen, route->gateway, - route->src_ip, route->if_name)) + route->src_ip, route->if_name, FALSE)) { case ALREADY_DONE: /* route exists, do not uninstall */ @@ -586,8 +586,8 @@ METHOD(kernel_ipsec_t, del_policy, status_t, route_entry_t *route = policy->route; if (charon->kernel->del_route(charon->kernel, route->dst_net, - route->prefixlen, route->gateway, - route->src_ip, route->if_name) != SUCCESS) + route->prefixlen, route->gateway, route->src_ip, + route->if_name, FALSE) != SUCCESS) { DBG1(DBG_KNL, "error uninstalling route installed with " "policy %R === %R %N", id->src_ts, id->dst_ts, @@ -618,7 +618,7 @@ METHOD(kernel_ipsec_t, flush_policies, status_t, charon->kernel->del_route(charon->kernel, route->dst_net, route->prefixlen, route->gateway, - route->src_ip, route->if_name); + route->src_ip, route->if_name, FALSE); remove_exclude_route(this, route); } policy_entry_destroy(pol); diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 327854ff4..ef0d424bd 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -370,55 +370,6 @@ struct private_kernel_netlink_ipsec_t { kernel_ipsec_manage_policy_t *data); }; -typedef struct route_entry_t route_entry_t; - -/** - * Installed routing entry - */ -struct route_entry_t { - /** Name of the interface the route is bound to */ - char *if_name; - - /** Source ip of the route */ - host_t *src_ip; - - /** Gateway for this route */ - host_t *gateway; - - /** Destination net */ - chunk_t dst_net; - - /** Destination net prefixlen */ - uint8_t prefixlen; -}; - -/** - * Destroy a route_entry_t object - */ -static void route_entry_destroy(route_entry_t *this) -{ - free(this->if_name); - this->src_ip->destroy(this->src_ip); - DESTROY_IF(this->gateway); - chunk_free(&this->dst_net); - free(this); -} - -/** - * Compare two route_entry_t objects - */ -static bool route_entry_equals(route_entry_t *a, route_entry_t *b) -{ - if (a->if_name && b->if_name && streq(a->if_name, b->if_name) && - a->src_ip->ip_equals(a->src_ip, b->src_ip) && - chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen) - { - return (!a->gateway && !b->gateway) || (a->gateway && b->gateway && - a->gateway->ip_equals(a->gateway, b->gateway)); - } - return FALSE; -} - typedef struct ipsec_sa_t ipsec_sa_t; /** @@ -2614,89 +2565,97 @@ static void install_route(private_kernel_netlink_ipsec_t *this, INIT(route, .prefixlen = policy->sel.prefixlen_d, + .pass = mapping->type == POLICY_PASS, ); if (charon->kernel->get_address_by_ts(charon->kernel, out->src_ts, - &route->src_ip, NULL) == SUCCESS) + &route->src_ip, NULL) != SUCCESS) { - if (!ipsec->dst->is_anyaddr(ipsec->dst)) + if (!route->pass) { - route->gateway = charon->kernel->get_nexthop(charon->kernel, - ipsec->dst, -1, ipsec->src, - &route->if_name); + free(route); + return; } - else - { /* for shunt policies */ - iface = xfrm2host(policy->sel.family, &policy->sel.daddr, 0); - route->gateway = charon->kernel->get_nexthop(charon->kernel, - iface, policy->sel.prefixlen_d, - route->src_ip, &route->if_name); - iface->destroy(iface); - } - route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16); - memcpy(route->dst_net.ptr, &policy->sel.daddr, route->dst_net.len); + /* allow blank source IP for passthrough policies */ + route->src_ip = host_create_any(policy->sel.family); + } - /* get the interface to install the route for, if we haven't one yet. - * If we have a local address, use it. Otherwise (for shunt policies) - * use the route's source address. */ - if (!route->if_name) - { - iface = ipsec->src; - if (iface->is_anyaddr(iface)) - { - iface = route->src_ip; - } - if (!charon->kernel->get_interface(charon->kernel, iface, - &route->if_name)) - { - route_entry_destroy(route); - return; - } - } - if (policy->route) - { - route_entry_t *old = policy->route; - if (route_entry_equals(old, route)) - { - route_entry_destroy(route); - return; - } - /* uninstall previously installed route */ - if (charon->kernel->del_route(charon->kernel, old->dst_net, - old->prefixlen, old->gateway, - old->src_ip, old->if_name) != SUCCESS) - { - DBG1(DBG_KNL, "error uninstalling route installed with policy " - "%R === %R %N", out->src_ts, out->dst_ts, policy_dir_names, - policy->direction); - } - route_entry_destroy(old); - policy->route = NULL; - } - - DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", out->dst_ts, - route->gateway, route->src_ip, route->if_name); - switch (charon->kernel->add_route(charon->kernel, route->dst_net, - route->prefixlen, route->gateway, - route->src_ip, route->if_name)) - { - default: - DBG1(DBG_KNL, "unable to install source route for %H", - route->src_ip); - /* FALL */ - case ALREADY_DONE: - /* route exists, do not uninstall */ - route_entry_destroy(route); - break; - case SUCCESS: - /* cache the installed route */ - policy->route = route; - break; - } + if (!ipsec->dst->is_anyaddr(ipsec->dst)) + { + route->gateway = charon->kernel->get_nexthop(charon->kernel, + ipsec->dst, -1, ipsec->src, + &route->if_name); } else + { /* for shunt policies */ + iface = xfrm2host(policy->sel.family, &policy->sel.daddr, 0); + route->gateway = charon->kernel->get_nexthop(charon->kernel, + iface, policy->sel.prefixlen_d, + route->src_ip, &route->if_name); + iface->destroy(iface); + } + route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16); + memcpy(route->dst_net.ptr, &policy->sel.daddr, route->dst_net.len); + + /* get the interface to install the route for, if we haven't one yet. + * If we have a local address, use it. Otherwise (for shunt policies) + * use the route's source address. */ + if (!route->if_name) { - free(route); + iface = ipsec->src; + if (iface->is_anyaddr(iface)) + { + iface = route->src_ip; + } + if (!charon->kernel->get_interface(charon->kernel, iface, + &route->if_name) && + !route->pass) + { /* don't require an interface for passthrough policies */ + route_entry_destroy(route); + return; + } + } + if (policy->route) + { + route_entry_t *old = policy->route; + if (route_entry_equals(old, route)) + { + route_entry_destroy(route); + return; + } + /* uninstall previously installed route */ + if (charon->kernel->del_route(charon->kernel, old->dst_net, + old->prefixlen, old->gateway, + old->src_ip, old->if_name, + old->pass) != SUCCESS) + { + DBG1(DBG_KNL, "error uninstalling route installed with policy " + "%R === %R %N", out->src_ts, out->dst_ts, policy_dir_names, + policy->direction); + } + route_entry_destroy(old); + policy->route = NULL; + } + + DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", out->dst_ts, + route->gateway, route->src_ip, route->if_name); + switch (charon->kernel->add_route(charon->kernel, route->dst_net, + route->prefixlen, route->gateway, + route->src_ip, route->if_name, + route->pass)) + { + default: + DBG1(DBG_KNL, "unable to install source route for %H", + route->src_ip); + /* FALL */ + case ALREADY_DONE: + /* route exists, do not uninstall */ + route_entry_destroy(route); + break; + case SUCCESS: + /* cache the installed route */ + policy->route = route; + break; } } @@ -3207,7 +3166,8 @@ METHOD(kernel_ipsec_t, del_policy, status_t, route_entry_t *route = current->route; if (charon->kernel->del_route(charon->kernel, route->dst_net, route->prefixlen, route->gateway, - route->src_ip, route->if_name) != SUCCESS) + route->src_ip, route->if_name, + route->pass) != SUCCESS) { DBG1(DBG_KNL, "error uninstalling route installed with policy " "%R === %R %N%s", id->src_ts, id->dst_ts, policy_dir_names, diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c index f95ee3ce5..c667ff425 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c @@ -266,81 +266,6 @@ static bool addr_map_entry_match(addr_map_entry_t *a, addr_map_entry_t *b) return a->ip->ip_equals(a->ip, b->ip); } -typedef struct route_entry_t route_entry_t; - -/** - * Installed routing entry - */ -struct route_entry_t { - /** Name of the interface the route is bound to */ - char *if_name; - - /** Source ip of the route */ - host_t *src_ip; - - /** Gateway for this route */ - host_t *gateway; - - /** Destination net */ - chunk_t dst_net; - - /** Destination net prefixlen */ - uint8_t prefixlen; -}; - -/** - * Clone a route_entry_t object. - */ -static route_entry_t *route_entry_clone(route_entry_t *this) -{ - route_entry_t *route; - - INIT(route, - .if_name = strdup(this->if_name), - .src_ip = this->src_ip->clone(this->src_ip), - .gateway = this->gateway ? this->gateway->clone(this->gateway) : NULL, - .dst_net = chunk_clone(this->dst_net), - .prefixlen = this->prefixlen, - ); - return route; -} - -/** - * Destroy a route_entry_t object - */ -static void route_entry_destroy(route_entry_t *this) -{ - free(this->if_name); - DESTROY_IF(this->src_ip); - DESTROY_IF(this->gateway); - chunk_free(&this->dst_net); - free(this); -} - -/** - * Hash a route_entry_t object - */ -static u_int route_entry_hash(route_entry_t *this) -{ - return chunk_hash_inc(chunk_from_thing(this->prefixlen), - chunk_hash(this->dst_net)); -} - -/** - * Compare two route_entry_t objects - */ -static bool route_entry_equals(route_entry_t *a, route_entry_t *b) -{ - if (a->if_name && b->if_name && streq(a->if_name, b->if_name) && - a->src_ip->ip_equals(a->src_ip, b->src_ip) && - chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen) - { - return (!a->gateway && !b->gateway) || (a->gateway && b->gateway && - a->gateway->ip_equals(a->gateway, b->gateway)); - } - return FALSE; -} - typedef struct net_change_t net_change_t; /** @@ -544,7 +469,7 @@ struct private_kernel_netlink_net_t { static status_t manage_srcroute(private_kernel_netlink_net_t *this, int nlmsg_type, int flags, chunk_t dst_net, uint8_t prefixlen, host_t *gateway, - host_t *src_ip, char *if_name); + host_t *src_ip, char *if_name, bool pass); /** * Clear the queued network changes. @@ -580,6 +505,10 @@ static job_requeue_t reinstall_routes(private_kernel_netlink_net_t *this) net_change_t *change, lookup = { .if_name = route->if_name, }; + if (route->pass || !route->if_name) + { /* no need to reinstall these, they don't reference interfaces */ + continue; + } /* check if a change for the outgoing interface is queued */ change = this->net_changes->get(this->net_changes, &lookup); if (!change) @@ -598,7 +527,7 @@ static job_requeue_t reinstall_routes(private_kernel_netlink_net_t *this) { manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, route->dst_net, route->prefixlen, route->gateway, - route->src_ip, route->if_name); + route->src_ip, route->if_name, route->pass); } } enumerator->destroy(enumerator); @@ -2632,7 +2561,7 @@ METHOD(kernel_net_t, del_ip, status_t, static status_t manage_srcroute(private_kernel_netlink_net_t *this, int nlmsg_type, int flags, chunk_t dst_net, uint8_t prefixlen, host_t *gateway, - host_t *src_ip, char *if_name) + host_t *src_ip, char *if_name, bool pass) { netlink_buf_t request; struct nlmsghdr *hdr; @@ -2653,12 +2582,12 @@ static status_t manage_srcroute(private_kernel_netlink_net_t *this, half_net = chunk_alloca(dst_net.len); memset(half_net.ptr, 0, half_net.len); half_prefixlen = 1; - + /* no throw routes in the main table */ status = manage_srcroute(this, nlmsg_type, flags, half_net, - half_prefixlen, gateway, src_ip, if_name); + half_prefixlen, gateway, src_ip, if_name, FALSE); half_net.ptr[0] |= 0x80; status |= manage_srcroute(this, nlmsg_type, flags, half_net, - half_prefixlen, gateway, src_ip, if_name); + half_prefixlen, gateway, src_ip, if_name, FALSE); return status; } @@ -2670,10 +2599,10 @@ static status_t manage_srcroute(private_kernel_netlink_net_t *this, hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); msg = NLMSG_DATA(hdr); - msg->rtm_family = src_ip->get_family(src_ip); + msg->rtm_family = (dst_net.len == 4) ? AF_INET : AF_INET6; msg->rtm_dst_len = prefixlen; msg->rtm_protocol = RTPROT_STATIC; - msg->rtm_type = RTN_UNICAST; + msg->rtm_type = pass ? RTN_THROW : RTN_UNICAST; msg->rtm_scope = RT_SCOPE_UNIVERSE; if (this->routing_table < 256) @@ -2691,42 +2620,48 @@ static status_t manage_srcroute(private_kernel_netlink_net_t *this, #endif /* HAVE_RTA_TABLE */ } netlink_add_attribute(hdr, RTA_DST, dst_net, sizeof(request)); - chunk = src_ip->get_address(src_ip); - netlink_add_attribute(hdr, RTA_PREFSRC, chunk, sizeof(request)); - if (gateway && gateway->get_family(gateway) == src_ip->get_family(src_ip)) - { - chunk = gateway->get_address(gateway); - netlink_add_attribute(hdr, RTA_GATEWAY, chunk, sizeof(request)); - } - ifindex = get_interface_index(this, if_name); - chunk.ptr = (char*)&ifindex; - chunk.len = sizeof(ifindex); - netlink_add_attribute(hdr, RTA_OIF, chunk, sizeof(request)); - if (this->mtu || this->mss) + /* only when installing regular routes do we need all the parameters, + * deletes are done by destination net (except if metrics are used, which + * we don't support), for throw routes we don't need any of them either */ + if (nlmsg_type == RTM_NEWROUTE && !pass) { - chunk = chunk_alloca(RTA_LENGTH((sizeof(struct rtattr) + - sizeof(uint32_t)) * 2)); - chunk.len = 0; - rta = (struct rtattr*)chunk.ptr; - if (this->mtu) + chunk = src_ip->get_address(src_ip); + netlink_add_attribute(hdr, RTA_PREFSRC, chunk, sizeof(request)); + if (gateway && gateway->get_family(gateway) == src_ip->get_family(src_ip)) { - rta->rta_type = RTAX_MTU; - rta->rta_len = RTA_LENGTH(sizeof(uint32_t)); - memcpy(RTA_DATA(rta), &this->mtu, sizeof(uint32_t)); - chunk.len = rta->rta_len; + chunk = gateway->get_address(gateway); + netlink_add_attribute(hdr, RTA_GATEWAY, chunk, sizeof(request)); } - if (this->mss) - { - rta = (struct rtattr*)(chunk.ptr + RTA_ALIGN(chunk.len)); - rta->rta_type = RTAX_ADVMSS; - rta->rta_len = RTA_LENGTH(sizeof(uint32_t)); - memcpy(RTA_DATA(rta), &this->mss, sizeof(uint32_t)); - chunk.len = RTA_ALIGN(chunk.len) + rta->rta_len; - } - netlink_add_attribute(hdr, RTA_METRICS, chunk, sizeof(request)); - } + ifindex = get_interface_index(this, if_name); + chunk.ptr = (char*)&ifindex; + chunk.len = sizeof(ifindex); + netlink_add_attribute(hdr, RTA_OIF, chunk, sizeof(request)); + if (this->mtu || this->mss) + { + chunk = chunk_alloca(RTA_LENGTH((sizeof(struct rtattr) + + sizeof(uint32_t)) * 2)); + chunk.len = 0; + rta = (struct rtattr*)chunk.ptr; + if (this->mtu) + { + rta->rta_type = RTAX_MTU; + rta->rta_len = RTA_LENGTH(sizeof(uint32_t)); + memcpy(RTA_DATA(rta), &this->mtu, sizeof(uint32_t)); + chunk.len = rta->rta_len; + } + if (this->mss) + { + rta = (struct rtattr*)(chunk.ptr + RTA_ALIGN(chunk.len)); + rta->rta_type = RTAX_ADVMSS; + rta->rta_len = RTA_LENGTH(sizeof(uint32_t)); + memcpy(RTA_DATA(rta), &this->mss, sizeof(uint32_t)); + chunk.len = RTA_ALIGN(chunk.len) + rta->rta_len; + } + netlink_add_attribute(hdr, RTA_METRICS, chunk, sizeof(request)); + } + } return this->socket->send_ack(this->socket, hdr); } @@ -2769,7 +2704,7 @@ static bool route_with_dst(route_entry_lookup_t *a, route_entry_t *b) METHOD(kernel_net_t, add_route, status_t, private_kernel_netlink_net_t *this, chunk_t dst_net, uint8_t prefixlen, - host_t *gateway, host_t *src_ip, char *if_name) + host_t *gateway, host_t *src_ip, char *if_name, bool pass) { status_t status; route_entry_t *found; @@ -2780,10 +2715,16 @@ METHOD(kernel_net_t, add_route, status_t, .gateway = gateway, .src_ip = src_ip, .if_name = if_name, + .pass = pass, }, .this = this, }; + if (!this->routing_table) + { /* treat these as regular routes if installing in the main table */ + pass = lookup.route.pass = FALSE; + } + this->routes_lock->lock(this->routes_lock); found = this->routes->get(this->routes, &lookup.route); if (found) @@ -2808,7 +2749,8 @@ METHOD(kernel_net_t, add_route, status_t, else { status = manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE|NLM_F_REPLACE, - dst_net, prefixlen, gateway, src_ip, if_name); + dst_net, prefixlen, gateway, src_ip, if_name, + pass); } if (status == SUCCESS) { @@ -2821,7 +2763,7 @@ METHOD(kernel_net_t, add_route, status_t, METHOD(kernel_net_t, del_route, status_t, private_kernel_netlink_net_t *this, chunk_t dst_net, uint8_t prefixlen, - host_t *gateway, host_t *src_ip, char *if_name) + host_t *gateway, host_t *src_ip, char *if_name, bool pass) { status_t status; route_entry_t *found; @@ -2832,10 +2774,16 @@ METHOD(kernel_net_t, del_route, status_t, .gateway = gateway, .src_ip = src_ip, .if_name = if_name, + .pass = pass, }, .this = this, }; + if (!this->routing_table) + { /* treat these as regular routes if installing in the main table */ + pass = lookup.route.pass = FALSE; + } + this->routes_lock->lock(this->routes_lock); found = this->routes->remove(this->routes, &lookup.route); if (!found) @@ -2860,12 +2808,12 @@ METHOD(kernel_net_t, del_route, status_t, { status = manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE|NLM_F_REPLACE, found->dst_net, found->prefixlen, found->gateway, - found->src_ip, found->if_name); + found->src_ip, found->if_name, found->pass); } else { status = manage_srcroute(this, RTM_DELROUTE, 0, dst_net, prefixlen, - gateway, src_ip, if_name); + gateway, src_ip, if_name, pass); } this->routes_lock->unlock(this->routes_lock); return status; @@ -3111,7 +3059,8 @@ METHOD(kernel_net_t, destroy, void, while (enumerator->enumerate(enumerator, NULL, (void**)&route)) { manage_srcroute(this, RTM_DELROUTE, 0, route->dst_net, route->prefixlen, - route->gateway, route->src_ip, route->if_name); + route->gateway, route->src_ip, route->if_name, + route->pass); route_entry_destroy(route); } enumerator->destroy(enumerator); diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c index f1a9f7aaa..a87f1de88 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c @@ -2,7 +2,7 @@ * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * - * Copyright (C) 2008-2019 Tobias Brunner + * Copyright (C) 2008-2020 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -767,3 +767,63 @@ void *netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len) } return RTA_DATA(rta); } + +/* + * Described in header + */ +void route_entry_destroy(route_entry_t *this) +{ + free(this->if_name); + DESTROY_IF(this->src_ip); + DESTROY_IF(this->gateway); + chunk_free(&this->dst_net); + free(this); +} + +/* + * Described in header + */ +route_entry_t *route_entry_clone(const route_entry_t *this) +{ + route_entry_t *route; + + INIT(route, + .if_name = strdupnull(this->if_name), + .src_ip = this->src_ip ? this->src_ip->clone(this->src_ip) : NULL, + .gateway = this->gateway ? this->gateway->clone(this->gateway) : NULL, + .dst_net = chunk_clone(this->dst_net), + .prefixlen = this->prefixlen, + .pass = this->pass, + ); + return route; +} + +/* + * Described in header + */ +u_int route_entry_hash(const route_entry_t *this) +{ + return chunk_hash_inc(chunk_from_thing(this->prefixlen), + chunk_hash(this->dst_net)); +} + +/** + * Compare two IP addresses, also accept it if both are NULL + */ +static bool addrs_null_or_equal(host_t *a, host_t *b) +{ + return (!a && !b) || (a && b && a->ip_equals(a, b)); +} + +/* + * Described in header + */ +bool route_entry_equals(const route_entry_t *a, const route_entry_t *b) +{ + return streq(a->if_name, b->if_name) && + a->pass == b->pass && + a->prefixlen == b->prefixlen && + chunk_equals(a->dst_net, b->dst_net) && + addrs_null_or_equal(a->src_ip, b->src_ip) && + addrs_null_or_equal(a->gateway, b->gateway); +} diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h index d68a013de..1e154ad2b 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2019 Tobias Brunner + * Copyright (C) 2008-2020 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -130,4 +130,50 @@ void* netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len); */ u_int netlink_get_buflen(); +/** + * Information about an installed route. + */ +struct route_entry_t { + + /** Destination net */ + chunk_t dst_net; + + /** Destination net prefix length */ + uint8_t prefixlen; + + /** Name of the interface the route is bound to (optional) */ + char *if_name; + + /** Source IP of the route (virtual IP or %any) */ + host_t *src_ip; + + /** Gateway for this route (optional) */ + host_t *gateway; + + /** Whether the route was installed for a passthrough policy */ + bool pass; +}; + +typedef struct route_entry_t route_entry_t; + +/** + * Destroy a route entry. + */ +void route_entry_destroy(route_entry_t *this); + +/** + * Clone a route entry. + */ +route_entry_t *route_entry_clone(const route_entry_t *this); + +/** + * Hash a route entry (note that this only hashes the destination). + */ +u_int route_entry_hash(const route_entry_t *this); + +/** + * Compare two route entries. + */ +bool route_entry_equals(const route_entry_t *a, const route_entry_t *b); + #endif /* KERNEL_NETLINK_SHARED_H_ */ diff --git a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index a97daf4a9..3bd24d43e 100644 --- a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -2332,7 +2332,7 @@ static void add_exclude_route(private_kernel_pfkey_ipsec_t *this, charon->kernel->add_route(charon->kernel, dst->get_address(dst), dst->get_family(dst) == AF_INET ? 32 : 128, - gtw, src, if_name) == SUCCESS) + gtw, src, if_name, FALSE) == SUCCESS) { INIT(exclude, .dst = dst->clone(dst), @@ -2399,7 +2399,7 @@ static void remove_exclude_route(private_kernel_pfkey_ipsec_t *this, dst->get_address(dst), dst->get_family(dst) == AF_INET ? 32 : 128, route->exclude->gtw, route->exclude->src, - if_name) != SUCCESS) + if_name, FALSE) != SUCCESS) { DBG1(DBG_KNL, "uninstalling exclude route for %H failed", dst); } @@ -2479,8 +2479,8 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this, } /* uninstall previously installed route */ if (charon->kernel->del_route(charon->kernel, old->dst_net, - old->prefixlen, old->gateway, - old->src_ip, old->if_name) != SUCCESS) + old->prefixlen, old->gateway, + old->src_ip, old->if_name, FALSE) != SUCCESS) { DBG1(DBG_KNL, "error uninstalling route installed with policy " "%R === %R %N", out->src_ts, out->dst_ts, @@ -2512,7 +2512,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this, switch (charon->kernel->add_route(charon->kernel, route->dst_net, route->prefixlen, route->gateway, - route->src_ip, route->if_name)) + route->src_ip, route->if_name, FALSE)) { case ALREADY_DONE: /* route exists, do not uninstall */ @@ -3067,8 +3067,8 @@ METHOD(kernel_ipsec_t, del_policy, status_t, { route_entry_t *route = policy->route; if (charon->kernel->del_route(charon->kernel, route->dst_net, - route->prefixlen, route->gateway, - route->src_ip, route->if_name) != SUCCESS) + route->prefixlen, route->gateway, + route->src_ip, route->if_name, FALSE) != SUCCESS) { DBG1(DBG_KNL, "error uninstalling route installed with " "policy %R === %R %N", id->src_ts, id->dst_ts, diff --git a/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c index 0bbdb1bc3..d28a6aa1f 100644 --- a/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -1494,7 +1494,7 @@ static status_t manage_route(private_kernel_pfroute_net_t *this, int op, METHOD(kernel_net_t, add_route, status_t, private_kernel_pfroute_net_t *this, chunk_t dst_net, uint8_t prefixlen, - host_t *gateway, host_t *src_ip, char *if_name) + host_t *gateway, host_t *src_ip, char *if_name, bool pass) { status_t status; route_entry_t *found, route = { @@ -1523,7 +1523,7 @@ METHOD(kernel_net_t, add_route, status_t, METHOD(kernel_net_t, del_route, status_t, private_kernel_pfroute_net_t *this, chunk_t dst_net, uint8_t prefixlen, - host_t *gateway, host_t *src_ip, char *if_name) + host_t *gateway, host_t *src_ip, char *if_name, bool pass) { status_t status; route_entry_t *found, route = { diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c index db684b060..19d4f3ef4 100644 --- a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c +++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c @@ -1402,7 +1402,8 @@ static bool uninstall_route(private_kernel_wfp_ipsec_t *this, if (charon->kernel->get_interface(charon->kernel, src, &name)) { res = charon->kernel->del_route(charon->kernel, - dst->get_address(dst), mask, gtw, src, name) == SUCCESS; + dst->get_address(dst), mask, gtw, src, + name, FALSE) == SUCCESS; free(name); } route = this->routes->remove(this->routes, route); @@ -1446,8 +1447,8 @@ static bool install_route(private_kernel_wfp_ipsec_t *this, { if (charon->kernel->get_interface(charon->kernel, src, &name)) { - if (charon->kernel->add_route(charon->kernel, - dst->get_address(dst), mask, gtw, src, name) == SUCCESS) + if (charon->kernel->add_route(charon->kernel, dst->get_address(dst), + mask, gtw, src, name, FALSE) == SUCCESS) { INIT(route, .dst = dst->clone(dst),