1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-16 00:00:07 +01:00
libwebsockets/lib/core-net/route.c
Andy Green 9eb4c4fac2 client: rfc6724 dns results sorting
RFC6724 defines an ipv6-centric DNS result sorting algorithm, that
takes route and source address route information for the results
given by the DNS resolution, and sorts them in order of preferability,
which defines the order they should be tried in.

If LWS_WITH_NETLINK, then lws takes care about collecting and monitoring
the interface, route and source address information, and uses it to
perform the RFC6724 sorting to re-sort the DNS before trying to make
the connections.
2020-11-28 10:58:07 +00:00

296 lines
8.1 KiB
C

/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* We mainly focus on the routing table / gateways because those are the
* elements that decide if we can get on to the internet or not.
*
* Everything here is _ because the caller needs to hold the pt lock in order
* to access the pt routing table safely
*/
#include <private-lib-core.h>
#if defined(_DEBUG)
void
_lws_routing_entry_dump(lws_route_t *rou)
{
char da[48], gw[48];
lws_sa46_write_numeric_address(&rou->dest, da, sizeof(da));
lws_sa46_write_numeric_address(&rou->gateway, gw, sizeof(gw));
lwsl_info(" (%d)%s/%d, gw: (%d)%s, ifidx: %d, pri: %d, proto: %d\n",
rou->dest.sa4.sin_family, da, rou->dest_len,
rou->gateway.sa4.sin_family, gw,
rou->if_idx, rou->priority, rou->proto);
}
void
_lws_routing_table_dump(struct lws_context_per_thread *pt)
{
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&pt->routing_table)) {
lws_route_t *rou = lws_container_of(d, lws_route_t, list);
_lws_routing_entry_dump(rou);
} lws_end_foreach_dll(d);
}
#endif
/*
* We will provide a "fingerprint ordinal" as the route uidx that is unique in
* the routing table. Wsi that connect mark themselves with the uidx of the
* route they are estimated to be using.
*
* This lets us detect things like gw changes, eg when switching from wlan to
* lte there may still be a valid gateway route, but all existing tcp
* connections previously using the wlan gateway will be broken, since their
* connections are from its gateway to the peer.
*
* So when we take down a route, we take care to look for any wsi that was
* estimated to be using that route, eg, for gateway, and close those wsi.
*
* It's OK if the route uidx wraps, we explicitly confirm nobody else is using
* the uidx before assigning one to a new route.
*
* We won't use uidx 0, so it can be understood to mean the uidx was never set.
*/
lws_route_uidx_t
_lws_route_get_uidx(struct lws_context_per_thread *pt)
{
if (!pt->route_uidx)
pt->route_uidx++;
while (1) {
char again = 0;
/* Anybody in the table already uses the pt's next uidx? */
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&pt->routing_table)) {
lws_route_t *rou = lws_container_of(d, lws_route_t, list);
if (rou->uidx == pt->route_uidx) {
/* if so, bump and restart the check */
pt->route_uidx++;
if (!pt->route_uidx)
pt->route_uidx++;
again = 1;
}
} lws_end_foreach_dll(d);
if (!again)
return pt->route_uidx++;
}
}
int
_lws_route_remove(struct lws_context_per_thread *pt, lws_route_t *robj)
{
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&pt->routing_table)) {
lws_route_t *rou = lws_container_of(d, lws_route_t, list);
if (!lws_sa46_compare_ads(&robj->dest, &rou->dest) &&
!lws_sa46_compare_ads(&robj->gateway, &rou->gateway) &&
robj->dest_len == rou->dest_len &&
robj->if_idx == rou->if_idx &&
robj->priority == rou->priority) {
// lwsl_debug("%s: deleting route\n", __func__);
_lws_route_pt_close_route_users(pt, robj->uidx);
lws_dll2_remove(&rou->list);
lws_free(rou);
return 0;
}
} lws_end_foreach_dll(d);
return 1;
}
void
_lws_route_table_empty(struct lws_context_per_thread *pt)
{
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
lws_dll2_get_head(&pt->routing_table)) {
lws_route_t *rou = lws_container_of(d, lws_route_t, list);
lws_dll2_remove(&rou->list);
lws_free(rou);
} lws_end_foreach_dll_safe(d, d1);
}
void
_lws_route_table_ifdown(struct lws_context_per_thread *pt, int idx)
{
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
lws_dll2_get_head(&pt->routing_table)) {
lws_route_t *rou = lws_container_of(d, lws_route_t, list);
if (rou->if_idx == idx) {
lws_dll2_remove(&rou->list);
lws_free(rou);
}
} lws_end_foreach_dll_safe(d, d1);
}
lws_route_t *
_lws_route_est_outgoing(struct lws_context_per_thread *pt,
const lws_sockaddr46 *dest)
{
lws_route_t *best_gw = NULL;
int best_gw_priority = INT_MAX;
if (!dest->sa4.sin_family) {
lwsl_notice("%s: dest has 0 AF\n", __func__);
/* leave it alone */
return NULL;
}
/*
* Given the dest address and the current routing table, select the
* route we think it would go out on
*/
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&pt->routing_table)) {
lws_route_t *rou = lws_container_of(d, lws_route_t, list);
// _lws_routing_entry_dump(rou);
if (rou->dest.sa4.sin_family &&
!lws_sa46_on_net(dest, &rou->dest, rou->dest_len)) {
/*
* Yes, he has a matching network route, it beats out
* any gateway route. This is like finding a route for
* 192.168.0.0/24 when dest is 192.168.0.1.
*/
// lwsl_notice("%s: returning %p\n", __func__, rou);
return rou;
}
lwsl_debug("%s: dest af %d, rou gw af %d, pri %d\n", __func__,
dest->sa4.sin_family,
rou->gateway.sa4.sin_family, rou->priority);
if (rou->gateway.sa4.sin_family &&
/*
* dest gw
* 4 4 OK
* 4 6 OK with ::ffff:x:x
* 6 4 not supported directly
* 6 6 OK
*/
(dest->sa4.sin_family == rou->gateway.sa4.sin_family ||
(dest->sa4.sin_family == AF_INET &&
rou->gateway.sa4.sin_family == AF_INET6)) &&
rou->priority < best_gw_priority) {
lwsl_info("%s: gw hit\n", __func__);
best_gw_priority = rou->priority;
best_gw = rou;
}
} lws_end_foreach_dll(d);
/*
* Either best_gw is the best gw route and we set *best_gw_priority to
* the best one's priority, or we're returning NULL as no network or
* gw route for dest.
*/
lwsl_info("%s: returning %p\n", __func__, best_gw);
return best_gw;
}
int
_lws_route_check_wsi(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
if (!wsi->sa46_peer.sa4.sin_family ||
wsi->desc.sockfd == LWS_SOCK_INVALID)
/* not a socket or not connected, leave it alone */
return 0; /* OK */
return !_lws_route_est_outgoing(pt, &wsi->sa46_peer);
}
int
_lws_route_pt_close_unroutable(struct lws_context_per_thread *pt)
{
struct lws *wsi;
unsigned int n;
if (!pt->context->nl_initial_done ||
pt->context->mgr_system.state < LWS_SYSTATE_IFACE_COLDPLUG)
return 0;
for (n = 0; n < pt->fds_count; n++) {
wsi = wsi_from_fd(pt->context, pt->fds[n].fd);
if (!wsi)
continue;
if (_lws_route_check_wsi(wsi)) {
lwsl_info("%s: culling wsi %p\n", __func__, wsi);
lws_wsi_close(wsi, LWS_TO_KILL_ASYNC);
}
}
return 0;
}
int
_lws_route_pt_close_route_users(struct lws_context_per_thread *pt,
lws_route_uidx_t uidx)
{
struct lws *wsi;
unsigned int n;
if (!uidx)
return 0;
lwsl_info("%s: closing users of route %d\n", __func__, uidx);
for (n = 0; n < pt->fds_count; n++) {
wsi = wsi_from_fd(pt->context, pt->fds[n].fd);
if (!wsi)
continue;
if (wsi->desc.sockfd != LWS_SOCK_INVALID &&
wsi->sa46_peer.sa4.sin_family &&
wsi->peer_route_uidx == uidx) {
lwsl_info("%s: culling wsi %p\n", __func__, wsi);
lws_wsi_close(wsi, LWS_TO_KILL_ASYNC);
}
}
return 0;
}