diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 709316338..4c20b5628 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -32,6 +32,8 @@ typedef struct { GArray *links; GArray *ip4_addresses; GArray *ip6_addresses; + GArray *ip4_routes; + GArray *ip6_routes; } NMFakePlatformPrivate; #define NM_FAKE_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_FAKE_PLATFORM, NMFakePlatformPrivate)) @@ -439,6 +441,192 @@ ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int /******************************************************************/ +static GArray * +ip4_route_get_all (NMPlatform *platform, int ifindex) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + GArray *routes; + NMPlatformIP4Route *route; + int count = 0, i; + + /* Count routes */ + for (i = 0; i < priv->ip4_routes->len; i++) { + route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); + if (route && route->ifindex == ifindex) + count++; + } + + routes = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP4Route), count); + + /* Fill routes */ + for (i = 0; i < priv->ip4_routes->len; i++) { + route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); + if (route && route->ifindex == ifindex) + g_array_append_val (routes, *route); + } + + return routes; +} + +static GArray * +ip6_route_get_all (NMPlatform *platform, int ifindex) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + GArray *routes; + NMPlatformIP6Route *route; + int count = 0, i; + + /* Count routes */ + for (i = 0; i < priv->ip6_routes->len; i++) { + route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); + if (route && route->ifindex == ifindex) + count++; + } + + routes = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP6Route), count); + + /* Fill routes */ + for (i = 0; i < priv->ip6_routes->len; i++) { + route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); + if (route && route->ifindex == ifindex) + g_array_append_val (routes, *route); + } + + return routes; +} + +static gboolean +ip4_route_add (NMPlatform *platform, int ifindex, in_addr_t network, int plen, + in_addr_t gateway, int metric, int mss) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + NMPlatformIP4Route route; + + memset (&route, 0, sizeof (route)); + route.ifindex = ifindex; + route.network = network; + route.plen = plen; + route.gateway = gateway; + route.metric = metric; + + g_array_append_val (priv->ip4_routes, route); + + g_signal_emit_by_name (platform, NM_PLATFORM_IP4_ROUTE_ADDED, &route); + + return TRUE; +} + +static gboolean +ip6_route_add (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, + struct in6_addr gateway, int metric, int mss) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + NMPlatformIP6Route route; + + memset (&route, 0, sizeof (route)); + route.ifindex = ifindex; + route.network = network; + route.plen = plen; + route.gateway = gateway; + route.metric = metric; + + g_array_append_val (priv->ip6_routes, route); + + g_signal_emit_by_name (platform, NM_PLATFORM_IP6_ROUTE_ADDED, &route); + + return TRUE; +} + +static NMPlatformIP4Route * +ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, + in_addr_t gateway, int metric) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + int i; + + for (i = 0; i < priv->ip4_routes->len; i++) { + NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); + + if (route->ifindex == ifindex + && route->network == network + && route->plen == plen + && route->gateway == gateway + && route->metric == metric) + return route; + } + + return NULL; +} + +static NMPlatformIP6Route * +ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, + struct in6_addr gateway, int metric) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + int i; + + for (i = 0; i < priv->ip6_routes->len; i++) { + NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); + + if (route->ifindex == ifindex + && IN6_ARE_ADDR_EQUAL (&route->network, &network) + && route->plen == plen + && IN6_ARE_ADDR_EQUAL (&route->gateway, &gateway) + && route->metric == metric) + return route; + } + + return NULL; +} + +static gboolean +ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, + in_addr_t gateway, int metric) +{ + NMPlatformIP4Route *route = ip4_route_get (platform, ifindex, network, plen, gateway, metric); + NMPlatformIP4Route deleted_route; + + g_assert (route); + + memcpy (&deleted_route, route, sizeof (deleted_route)); + memset (route, 0, sizeof (*route)); + g_signal_emit_by_name (platform, NM_PLATFORM_IP4_ROUTE_REMOVED, &deleted_route); + + return TRUE; +} + +static gboolean +ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, + struct in6_addr gateway, int metric) +{ + NMPlatformIP6Route *route = ip6_route_get (platform, ifindex, network, plen, gateway, metric); + NMPlatformIP6Route deleted_route; + + g_assert (route); + + memcpy (&deleted_route, route, sizeof (deleted_route)); + memset (route, 0, sizeof (*route)); + g_signal_emit_by_name (platform, NM_PLATFORM_IP6_ROUTE_REMOVED, &deleted_route); + + return TRUE; +} + +static gboolean +ip4_route_exists (NMPlatform *platform, int ifindex, in_addr_t network, int plen, + in_addr_t gateway, int metric) +{ + return !!ip4_route_get (platform, ifindex, network, plen, gateway, metric); +} + +static gboolean +ip6_route_exists (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, + struct in6_addr gateway, int metric) +{ + return !!ip6_route_get (platform, ifindex, network, plen, gateway, metric); +} + +/******************************************************************/ + static void nm_fake_platform_init (NMFakePlatform *fake_platform) { @@ -447,6 +635,8 @@ nm_fake_platform_init (NMFakePlatform *fake_platform) priv->links = g_array_new (TRUE, TRUE, sizeof (NMPlatformLink)); priv->ip4_addresses = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Address)); priv->ip6_addresses = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Address)); + priv->ip4_routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Route)); + priv->ip6_routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Route)); } static gboolean @@ -474,6 +664,8 @@ nm_fake_platform_finalize (GObject *object) g_array_unref (priv->links); g_array_unref (priv->ip4_addresses); g_array_unref (priv->ip6_addresses); + g_array_unref (priv->ip4_routes); + g_array_unref (priv->ip6_routes); G_OBJECT_CLASS (nm_fake_platform_parent_class)->finalize (object); } @@ -514,4 +706,13 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->ip6_address_delete = ip6_address_delete; platform_class->ip4_address_exists = ip4_address_exists; platform_class->ip6_address_exists = ip6_address_exists; + + platform_class->ip4_route_get_all = ip4_route_get_all; + platform_class->ip6_route_get_all = ip6_route_get_all; + platform_class->ip4_route_add = ip4_route_add; + platform_class->ip6_route_add = ip6_route_add; + platform_class->ip4_route_delete = ip4_route_delete; + platform_class->ip6_route_delete = ip6_route_delete; + platform_class->ip4_route_exists = ip4_route_exists; + platform_class->ip6_route_exists = ip6_route_exists; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 939c8eb9b..7aa74f32d 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "nm-linux-platform.h" #include "nm-logging.h" @@ -47,6 +48,7 @@ typedef struct { struct nl_sock *nlh_event; struct nl_cache *link_cache; struct nl_cache *address_cache; + struct nl_cache *route_cache; GIOChannel *event_channel; guint event_id; } NMLinuxPlatformPrivate; @@ -111,6 +113,8 @@ typedef enum { LINK, IP4_ADDRESS, IP6_ADDRESS, + IP4_ROUTE, + IP6_ROUTE, N_TYPES } ObjectType; @@ -137,6 +141,15 @@ object_type_from_nl_object (const struct nl_object *object) default: g_assert_not_reached (); } + } else if (!strcmp (nl_object_get_type (object), "route/route")) { + switch (rtnl_route_get_family ((struct rtnl_route *) object)) { + case AF_INET: + return IP4_ROUTE; + case AF_INET6: + return IP6_ROUTE; + default: + g_assert_not_reached (); + } } else g_assert_not_reached (); } @@ -214,6 +227,9 @@ add_kernel_object (struct nl_sock *sock, struct nl_object *object) case IP4_ADDRESS: case IP6_ADDRESS: return rtnl_addr_add (sock, (struct rtnl_addr *) object, NLM_F_CREATE); + case IP4_ROUTE: + case IP6_ROUTE: + return rtnl_route_add (sock, (struct rtnl_route *) object, NLM_F_CREATE); default: g_assert_not_reached (); } @@ -229,6 +245,9 @@ delete_kernel_object (struct nl_sock *sock, struct nl_object *object) case IP4_ADDRESS: case IP6_ADDRESS: return rtnl_addr_delete (sock, (struct rtnl_addr *) object, 0); + case IP4_ROUTE: + case IP6_ROUTE: + return rtnl_route_delete (sock, (struct rtnl_route *) object, 0); default: g_assert_not_reached (); } @@ -318,6 +337,46 @@ init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr) memcpy (&address->address, nl_addr_get_binary_addr (nladdr), sizeof (address->address)); } +static void +init_ip4_route (NMPlatformIP4Route *route, struct rtnl_route *rtnlroute) +{ + struct nl_addr *dst, *gw; + struct rtnl_nexthop *nexthop; + + g_assert (rtnl_route_get_nnexthops (rtnlroute) == 1); + nexthop = rtnl_route_nexthop_n (rtnlroute, 0); + dst = rtnl_route_get_dst (rtnlroute); + gw = rtnl_route_nh_get_gateway (nexthop); + + memset (route, 0, sizeof (*route)); + route->ifindex = rtnl_route_nh_get_ifindex (nexthop); + route->plen = nl_addr_get_prefixlen (dst); + memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network)); + if (gw) + memcpy (&route->gateway, nl_addr_get_binary_addr (gw), sizeof (route->gateway)); + route->metric = rtnl_route_get_priority (rtnlroute); +} + +static void +init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute) +{ + struct nl_addr *dst, *gw; + struct rtnl_nexthop *nexthop; + + g_assert (rtnl_route_get_nnexthops (rtnlroute) == 1); + nexthop = rtnl_route_nexthop_n (rtnlroute, 0); + dst = rtnl_route_get_dst (rtnlroute); + gw = rtnl_route_nh_get_gateway (nexthop); + + memset (route, 0, sizeof (*route)); + route->ifindex = rtnl_route_nh_get_ifindex (nexthop); + route->plen = nl_addr_get_prefixlen (dst); + memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network)); + if (gw) + memcpy (&route->gateway, nl_addr_get_binary_addr (gw), sizeof (route->gateway)); + route->metric = rtnl_route_get_priority (rtnlroute); +} + /******************************************************************/ /* Object and cache manipulation */ @@ -326,6 +385,8 @@ static const char *signal_by_type_and_status[N_TYPES][N_STATUSES] = { { NM_PLATFORM_LINK_ADDED, NM_PLATFORM_LINK_CHANGED, NM_PLATFORM_LINK_REMOVED }, { NM_PLATFORM_IP4_ADDRESS_ADDED, NM_PLATFORM_IP4_ADDRESS_CHANGED, NM_PLATFORM_IP4_ADDRESS_REMOVED }, { NM_PLATFORM_IP6_ADDRESS_ADDED, NM_PLATFORM_IP6_ADDRESS_CHANGED, NM_PLATFORM_IP6_ADDRESS_REMOVED }, + { NM_PLATFORM_IP4_ROUTE_ADDED, NM_PLATFORM_IP4_ROUTE_CHANGED, NM_PLATFORM_IP4_ROUTE_REMOVED }, + { NM_PLATFORM_IP6_ROUTE_ADDED, NM_PLATFORM_IP6_ROUTE_CHANGED, NM_PLATFORM_IP6_ROUTE_REMOVED } }; static struct nl_cache * @@ -339,6 +400,9 @@ choose_cache (NMPlatform *platform, struct nl_object *object) case IP4_ADDRESS: case IP6_ADDRESS: return priv->address_cache; + case IP4_ROUTE: + case IP6_ROUTE: + return priv->route_cache; default: g_assert_not_reached (); } @@ -375,6 +439,22 @@ announce_object (NMPlatform *platform, const struct nl_object *object, ObjectSta g_signal_emit_by_name (platform, sig, &address); } return; + case IP4_ROUTE: + { + NMPlatformIP4Route route; + + init_ip4_route (&route, (struct rtnl_route *) object); + g_signal_emit_by_name (platform, sig, &route); + } + return; + case IP6_ROUTE: + { + NMPlatformIP6Route route; + + init_ip6_route (&route, (struct rtnl_route *) object); + g_signal_emit_by_name (platform, sig, &route); + } + return; default: error ("Announcing object: object type unknown: %d", object_type); } @@ -869,6 +949,165 @@ ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int /******************************************************************/ +static int +ip_route_mark_all (NMPlatform *platform, int family, int ifindex) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + struct nl_object *object; + int count = 0; + + for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) { + struct rtnl_route *rtnlroute = (struct rtnl_route *) object; + struct rtnl_nexthop *nexthop; + + nl_object_unmark (object); + if (rtnl_route_get_type (rtnlroute) != RTN_UNICAST) + continue; + if (rtnl_route_get_table (rtnlroute) != RT_TABLE_MAIN) + continue; + if (rtnl_route_get_family (rtnlroute) != family) + continue; + if (rtnl_route_get_nnexthops (rtnlroute) != 1) + continue; + nexthop = rtnl_route_nexthop_n (rtnlroute, 0); + if (rtnl_route_nh_get_ifindex (nexthop) != ifindex) + continue; + nl_object_mark (object); + count++; + } + + return count; +} + +static GArray * +ip4_route_get_all (NMPlatform *platform, int ifindex) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + GArray *routes; + NMPlatformIP4Route route; + struct nl_object *object; + int count = 0; + + count = ip_route_mark_all (platform, AF_INET, ifindex); + routes = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP4Route), count); + + for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) { + if (nl_object_is_marked (object)) { + init_ip4_route (&route, (struct rtnl_route *) object); + g_array_append_val (routes, route); + nl_object_unmark (object); + } + } + + return routes; +} + +static GArray * +ip6_route_get_all (NMPlatform *platform, int ifindex) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + GArray *routes; + NMPlatformIP6Route route; + struct nl_object *object; + int count; + + count = ip_route_mark_all (platform, AF_INET6, ifindex); + routes = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP6Route), count); + + for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) { + if (nl_object_is_marked (object)) { + init_ip6_route (&route, (struct rtnl_route *) object); + g_array_append_val (routes, route); + nl_object_unmark (object); + } + } + + return routes; +} + +static struct nl_object * +build_rtnl_route (int family, int ifindex, gconstpointer network, int plen, gconstpointer gateway, int metric, int mss) +{ + struct rtnl_route *rtnlroute = rtnl_route_alloc (); + struct rtnl_nexthop *nexthop = rtnl_route_nh_alloc (); + int addrlen = (family == AF_INET) ? sizeof (in_addr_t) : sizeof (struct in6_addr); + auto_nl_addr struct nl_addr *dst = nl_addr_build (family, network, addrlen); + auto_nl_addr struct nl_addr *gw = nl_addr_build (family, gateway, addrlen); + + g_assert (rtnlroute && dst && gw && nexthop); + + nl_addr_set_prefixlen (dst, plen); + + rtnl_route_set_table (rtnlroute, RT_TABLE_MAIN); + rtnl_route_set_tos (rtnlroute, 0); + rtnl_route_set_dst (rtnlroute, dst); + rtnl_route_set_priority (rtnlroute, metric); + + rtnl_route_nh_set_ifindex (nexthop, ifindex); + if (!nl_addr_iszero (gw)) + rtnl_route_nh_set_gateway (nexthop, gw); + rtnl_route_add_nexthop (rtnlroute, nexthop); + + if (mss > 0) + rtnl_route_set_metric (rtnlroute, RTAX_ADVMSS, mss); + + return (struct nl_object *) rtnlroute; +} + +static gboolean +ip4_route_add (NMPlatform *platform, int ifindex, in_addr_t network, int plen, in_addr_t gateway, int metric, int mss) +{ + return add_object (platform, build_rtnl_route (AF_INET, ifindex, &network, plen, &gateway, metric, mss)); +} + +static gboolean +ip6_route_add (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, struct in6_addr gateway, int metric, int mss) +{ + return add_object (platform, build_rtnl_route (AF_INET6, ifindex, &network, plen, &gateway, metric, mss)); +} + +static gboolean +ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, + in_addr_t gateway, int metric) +{ + return delete_object (platform, build_rtnl_route (AF_INET, ifindex, &network, plen, &gateway, metric, 0)); +} + +static gboolean +ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, + struct in6_addr gateway, int metric) +{ + return delete_object (platform, build_rtnl_route (AF_INET6, ifindex, &network, plen, &gateway, metric, 0)); +} + +static gboolean +ip_route_exists (NMPlatform *platform, int family, int ifindex, gpointer network, int plen, + gpointer gateway, int metric) +{ + auto_nl_object struct nl_object *object = build_rtnl_route ( + family, ifindex, network, plen, gateway, metric, 0); + auto_nl_object struct nl_object *cached_object = nl_cache_search ( + choose_cache (platform, object), object); + + return !!cached_object; +} + +static gboolean +ip4_route_exists (NMPlatform *platform, int ifindex, in_addr_t network, int plen, + in_addr_t gateway, int metric) +{ + return ip_route_exists (platform, AF_INET, ifindex, &network, plen, &gateway, metric); +} + +static gboolean +ip6_route_exists (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, + struct in6_addr gateway, int metric) +{ + return ip_route_exists (platform, AF_INET6, ifindex, &network, plen, &gateway, metric); +} + +/******************************************************************/ + #define EVENT_CONDITIONS ((GIOCondition) (G_IO_IN | G_IO_PRI)) #define ERROR_CONDITIONS ((GIOCondition) (G_IO_ERR | G_IO_NVAL)) #define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP)) @@ -982,7 +1221,8 @@ setup (NMPlatform *platform) /* Allocate netlink caches */ rtnl_link_alloc_cache (priv->nlh, AF_UNSPEC, &priv->link_cache); rtnl_addr_alloc_cache (priv->nlh, &priv->address_cache); - g_assert (priv->link_cache && priv->address_cache); + rtnl_route_alloc_cache (priv->nlh, AF_UNSPEC, 0, &priv->route_cache); + g_assert (priv->link_cache && priv->address_cache && priv->route_cache); return TRUE; } @@ -999,6 +1239,7 @@ nm_linux_platform_finalize (GObject *object) nl_socket_free (priv->nlh_event); nl_cache_free (priv->link_cache); nl_cache_free (priv->address_cache); + nl_cache_free (priv->route_cache); G_OBJECT_CLASS (nm_linux_platform_parent_class)->finalize (object); } @@ -1041,4 +1282,13 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->ip6_address_delete = ip6_address_delete; platform_class->ip4_address_exists = ip4_address_exists; platform_class->ip6_address_exists = ip6_address_exists; + + platform_class->ip4_route_get_all = ip4_route_get_all; + platform_class->ip6_route_get_all = ip6_route_get_all; + platform_class->ip4_route_add = ip4_route_add; + platform_class->ip6_route_add = ip6_route_add; + platform_class->ip4_route_delete = ip4_route_delete; + platform_class->ip6_route_delete = ip6_route_delete; + platform_class->ip4_route_exists = ip4_route_exists; + platform_class->ip6_route_exists = ip6_route_exists; } diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 5323bcdf9..9996437ab 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -44,6 +44,12 @@ enum { IP6_ADDRESS_ADDED, IP6_ADDRESS_CHANGED, IP6_ADDRESS_REMOVED, + IP4_ROUTE_ADDED, + IP4_ROUTE_CHANGED, + IP4_ROUTE_REMOVED, + IP6_ROUTE_ADDED, + IP6_ROUTE_CHANGED, + IP6_ROUTE_REMOVED, LAST_SIGNAL }; @@ -600,6 +606,129 @@ nm_platform_ip6_address_exists (int ifindex, struct in6_addr address, int plen) return klass->ip6_address_exists (platform, ifindex, address, plen); } +GArray * +nm_platform_ip4_route_get_all (int ifindex) +{ + reset_error (); + + g_return_val_if_fail (ifindex > 0, NULL); + g_return_val_if_fail (klass->ip4_route_get_all, NULL); + + return klass->ip4_route_get_all (platform, ifindex); +} + +GArray * +nm_platform_ip6_route_get_all (int ifindex) +{ + reset_error (); + + g_return_val_if_fail (ifindex > 0, NULL); + g_return_val_if_fail (klass->ip6_route_get_all, NULL); + + return klass->ip6_route_get_all (platform, ifindex); +} + +gboolean +nm_platform_ip4_route_add (int ifindex, + in_addr_t network, int plen, + in_addr_t gateway, int metric, int mss) +{ + reset_error (); + + g_return_val_if_fail (platform, FALSE); + g_return_val_if_fail (0 <= plen && plen <= 32, FALSE); + g_return_val_if_fail (metric >= 0, FALSE); + g_return_val_if_fail (mss >= 0, FALSE); + g_return_val_if_fail (klass->ip4_route_add, FALSE); + + if (nm_platform_ip4_route_exists (ifindex, network, plen, gateway, metric)) { + debug ("route already exists"); + platform->error = NM_PLATFORM_ERROR_EXISTS; + return FALSE; + } + + return klass->ip4_route_add (platform, ifindex, network, plen, gateway, metric, mss); +} + +gboolean +nm_platform_ip6_route_add (int ifindex, + struct in6_addr network, int plen, struct in6_addr gateway, int metric, int mss) +{ + g_return_val_if_fail (platform, FALSE); + g_return_val_if_fail (0 <= plen && plen <= 128, FALSE); + g_return_val_if_fail (metric >= 0, FALSE); + g_return_val_if_fail (mss >= 0, FALSE); + g_return_val_if_fail (klass->ip6_route_add, FALSE); + + if (nm_platform_ip6_route_exists (ifindex, network, plen, gateway, metric)) { + debug ("route already exists"); + platform->error = NM_PLATFORM_ERROR_EXISTS; + return FALSE; + } + + return klass->ip6_route_add (platform, ifindex, network, plen, gateway, metric, mss); +} + +gboolean +nm_platform_ip4_route_delete (int ifindex, + in_addr_t network, int plen, in_addr_t gateway, int metric) +{ + reset_error (); + + g_return_val_if_fail (platform, FALSE); + g_return_val_if_fail (klass->ip4_route_delete, FALSE); + + if (!nm_platform_ip4_route_exists (ifindex, network, plen, gateway, metric)) { + debug ("route not found"); + platform->error = NM_PLATFORM_ERROR_NOT_FOUND; + return FALSE; + } + + return klass->ip4_route_delete (platform,ifindex, network, plen, gateway, metric); +} + +gboolean +nm_platform_ip6_route_delete (int ifindex, + struct in6_addr network, int plen, struct in6_addr gateway, int metric) +{ + reset_error (); + + g_return_val_if_fail (platform, FALSE); + g_return_val_if_fail (klass->ip6_route_delete, FALSE); + + if (!nm_platform_ip6_route_exists (ifindex, network, plen, gateway, metric)) { + debug ("route not found"); + platform->error = NM_PLATFORM_ERROR_NOT_FOUND; + return FALSE; + } + + return klass->ip6_route_delete (platform, ifindex, network, plen, gateway, metric); +} + +gboolean +nm_platform_ip4_route_exists (int ifindex, + in_addr_t network, int plen, in_addr_t gateway, int metric) +{ + reset_error (); + + g_return_val_if_fail (platform, FALSE); + g_return_val_if_fail (klass->ip4_route_exists, FALSE); + + return klass->ip4_route_exists (platform,ifindex, network, plen, gateway, metric); +} + +gboolean +nm_platform_ip6_route_exists (int ifindex, + struct in6_addr network, int plen, struct in6_addr gateway, int metric) +{ + reset_error (); + + g_return_val_if_fail (platform, FALSE); + g_return_val_if_fail (klass->ip6_route_exists, FALSE); + + return klass->ip6_route_exists (platform, ifindex, network, plen, gateway, metric); +} + /******************************************************************/ static void @@ -680,6 +809,70 @@ log_ip6_address_removed (NMPlatform *p, NMPlatformIP6Address *address, gpointer log_ip6_address (address, "removed"); } +static void +log_ip4_route (NMPlatformIP4Route *route, const char *change_type) +{ + char network[INET_ADDRSTRLEN]; + char gateway[INET_ADDRSTRLEN]; + int plen = route->plen; + const char *name = nm_platform_link_get_name (route->ifindex); + + inet_ntop (AF_INET, &route->network, network, sizeof (network)); + inet_ntop (AF_INET, &route->gateway, gateway, sizeof (gateway)); + + debug ("signal: route %s: %s/%d via %s dev %s", change_type, network, plen, gateway, name); +} + +static void +log_ip4_route_added (NMPlatform *p, NMPlatformIP4Route *route, gpointer user_data) +{ + log_ip4_route (route, "added"); +} + +static void +log_ip4_route_changed (NMPlatform *p, NMPlatformIP4Route *route, gpointer user_data) +{ + log_ip4_route (route, "changed"); +} + +static void +log_ip4_route_removed (NMPlatform *p, NMPlatformIP4Route *route, gpointer user_data) +{ + log_ip4_route (route, "removed"); +} + +static void +log_ip6_route (NMPlatformIP6Route *route, const char *change_type) +{ + char network[INET6_ADDRSTRLEN]; + char gateway[INET6_ADDRSTRLEN]; + int plen = route->plen; + const char *name = nm_platform_link_get_name (route->ifindex); + + inet_ntop (AF_INET6, &route->network, network, sizeof (network)); + inet_ntop (AF_INET6, &route->gateway, gateway, sizeof (gateway)); + + debug ("signal: route %s: %s/%d via %s dev %s", change_type, network, plen, gateway, name); +} + +static void +log_ip6_route_added (NMPlatform *p, NMPlatformIP6Route *route, gpointer user_data) +{ + log_ip6_route (route, "added"); +} + +static void +log_ip6_route_changed (NMPlatform *p, NMPlatformIP6Route *route, gpointer user_data) +{ + log_ip6_route (route, "changed"); +} + +static void +log_ip6_route_removed (NMPlatform *p, NMPlatformIP6Route *route, gpointer user_data) +{ + log_ip6_route (route, "removed"); +} + /******************************************************************/ static void @@ -711,4 +904,10 @@ nm_platform_class_init (NMPlatformClass *platform_class) SIGNAL (IP6_ADDRESS_ADDED, log_ip6_address_added) SIGNAL (IP6_ADDRESS_CHANGED, log_ip6_address_changed) SIGNAL (IP6_ADDRESS_REMOVED, log_ip6_address_removed) + SIGNAL (IP4_ROUTE_ADDED, log_ip4_route_added) + SIGNAL (IP4_ROUTE_CHANGED, log_ip4_route_changed) + SIGNAL (IP4_ROUTE_REMOVED, log_ip4_route_removed) + SIGNAL (IP6_ROUTE_ADDED, log_ip6_route_added) + SIGNAL (IP6_ROUTE_CHANGED, log_ip6_route_changed) + SIGNAL (IP6_ROUTE_REMOVED, log_ip6_route_removed) } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index fd15afe3c..d5a1e6420 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -64,6 +64,22 @@ typedef struct { int plen; } NMPlatformIP6Address; +typedef struct { + int ifindex; + in_addr_t network; + int plen; + in_addr_t gateway; + int metric; +} NMPlatformIP4Route; + +typedef struct { + int ifindex; + struct in6_addr network; + int plen; + struct in6_addr gateway; + int metric; +} NMPlatformIP6Route; + /******************************************************************/ /* NMPlatform abstract class and its implementations provide a layer between @@ -128,6 +144,21 @@ typedef struct { gboolean (*ip6_address_delete) (NMPlatform *, int ifindex, struct in6_addr address, int plen); gboolean (*ip4_address_exists) (NMPlatform *, int ifindex, in_addr_t address, int plen); gboolean (*ip6_address_exists) (NMPlatform *, int ifindex, struct in6_addr address, int plen); + + GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex); + GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex); + gboolean (*ip4_route_add) (NMPlatform *, int ifindex, + in_addr_t network, int plen, in_addr_t gateway, int prio, int mss); + gboolean (*ip6_route_add) (NMPlatform *, int ifindex, + struct in6_addr network, int plen, struct in6_addr gateway, int prio, int mss); + gboolean (*ip4_route_delete) (NMPlatform *, int ifindex, + in_addr_t network, int plen, in_addr_t gateway, int metric); + gboolean (*ip6_route_delete) (NMPlatform *, int ifindex, + struct in6_addr network, int plen, struct in6_addr gateway, int metric); + gboolean (*ip4_route_exists) (NMPlatform *, int ifindex, + in_addr_t network, int plen, in_addr_t gateway, int metric); + gboolean (*ip6_route_exists) (NMPlatform *, int ifindex, + struct in6_addr network, int plen, struct in6_addr gateway, int metric); } NMPlatformClass; /* NMPlatform signals @@ -150,6 +181,12 @@ typedef struct { #define NM_PLATFORM_IP6_ADDRESS_ADDED "ip6-address-added" #define NM_PLATFORM_IP6_ADDRESS_CHANGED "ip6-address-changed" #define NM_PLATFORM_IP6_ADDRESS_REMOVED "ip6-address-removed" +#define NM_PLATFORM_IP4_ROUTE_ADDED "ip4-route-added" +#define NM_PLATFORM_IP4_ROUTE_CHANGED "ip4-route-changed" +#define NM_PLATFORM_IP4_ROUTE_REMOVED "ip4-route-removed" +#define NM_PLATFORM_IP6_ROUTE_ADDED "ip6-route-added" +#define NM_PLATFORM_IP6_ROUTE_CHANGED "ip6-route-changed" +#define NM_PLATFORM_IP6_ROUTE_REMOVED "ip6-route-removed" /* NMPlatform error codes */ enum { @@ -200,4 +237,20 @@ gboolean nm_platform_ip6_address_delete (int ifindex, struct in6_addr address, i gboolean nm_platform_ip4_address_exists (int ifindex, in_addr_t address, int plen); gboolean nm_platform_ip6_address_exists (int ifindex, struct in6_addr address, int plen); +GArray *nm_platform_ip4_route_get_all (int ifindex); +GArray *nm_platform_ip6_route_get_all (int ifindex); +gboolean nm_platform_route_set_metric (int ifindex, int metric); +gboolean nm_platform_ip4_route_add (int ifindex, + in_addr_t network, int plen, in_addr_t gateway, int metric, int mss); +gboolean nm_platform_ip6_route_add (int ifindex, + struct in6_addr network, int plen, struct in6_addr gateway, int metric, int mss); +gboolean nm_platform_ip4_route_delete (int ifindex, + in_addr_t network, int plen, in_addr_t gateway, int metric); +gboolean nm_platform_ip6_route_delete (int ifindex, + struct in6_addr network, int plen, struct in6_addr gateway, int metric); +gboolean nm_platform_ip4_route_exists (int ifindex, + in_addr_t network, int plen, in_addr_t gateway, int metric); +gboolean nm_platform_ip6_route_exists (int ifindex, + struct in6_addr network, int plen, struct in6_addr gateway, int metric); + #endif /* NM_PLATFORM_H */ diff --git a/src/platform/tests/Makefile.am b/src/platform/tests/Makefile.am index 068fa1770..b7e41d502 100644 --- a/src/platform/tests/Makefile.am +++ b/src/platform/tests/Makefile.am @@ -22,7 +22,9 @@ noinst_PROGRAMS = \ test-link-fake \ test-link-linux \ test-address-fake \ - test-address-linux + test-address-linux \ + test-route-fake \ + test-route-linux EXTRA_DIST = test-common.h @@ -78,6 +80,28 @@ test_address_linux_CPPFLAGS = \ -DKERNEL_HACKS=1 test_address_linux_LDADD = $(COMMON_LDADD) +test_route_fake_SOURCES = \ + test-route.c \ + test-common.c \ + ${srcdir}/../nm-platform.c \ + ${srcdir}/../nm-fake-platform.c +test_route_fake_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DSETUP=nm_fake_platform_setup \ + -DKERNEL_HACKS=0 +test_route_fake_LDADD = $(COMMON_LDADD) + +test_route_linux_SOURCES = \ + test-route.c \ + test-common.c \ + ${srcdir}/../nm-platform.c \ + ${srcdir}/../nm-linux-platform.c +test_route_linux_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DSETUP=nm_linux_platform_setup \ + -DKERNEL_HACKS=1 +test_route_linux_LDADD = $(COMMON_LDADD) + # Unfortunately, we cannot run nm-linux-platform-test as an automatic test # program by default, as it requires root access and modifies kernel # configuration. @@ -85,8 +109,8 @@ test_address_linux_LDADD = $(COMMON_LDADD) # However, we can check whether the fake platform fakes platform behavior # correctly. @VALGRIND_RULES@ -TESTS = ./test-link-fake ./test-address-fake -ROOTTESTS = ./test-link-linux ./test-address-linux +TESTS = ./test-link-fake ./test-address-fake ./test-route-fake +ROOTTESTS = ./test-link-linux ./test-address-linux ./test-route-linux # If explicitly enabled, we can run the root tests if RUN_ROOT_TESTS diff --git a/src/platform/tests/dump.c b/src/platform/tests/dump.c index c3ff3e796..42a43abcf 100644 --- a/src/platform/tests/dump.c +++ b/src/platform/tests/dump.c @@ -29,6 +29,12 @@ dump_interface (NMPlatformLink *link) const NMPlatformIP6Address *ip6_address; const NMPlatformIP4Address *ip4_address; char addrstr[INET6_ADDRSTRLEN]; + GArray *ip6_routes; + GArray *ip4_routes; + const NMPlatformIP6Route *ip6_route; + const NMPlatformIP4Route *ip4_route; + char networkstr[INET6_ADDRSTRLEN]; + char gatewaystr[INET6_ADDRSTRLEN]; int i; g_assert (link->up || !link->connected); @@ -62,6 +68,29 @@ dump_interface (NMPlatformLink *link) g_array_unref (ip4_addresses); g_array_unref (ip6_addresses); + + ip4_routes = nm_platform_ip4_route_get_all (link->ifindex); + ip6_routes = nm_platform_ip6_route_get_all (link->ifindex); + + g_assert (ip4_routes); + g_assert (ip6_routes); + + for (i = 0; i < ip4_routes->len; i++) { + ip4_route = &g_array_index (ip4_routes, NMPlatformIP4Route, i); + inet_ntop (AF_INET, &ip4_route->network, networkstr, sizeof (networkstr)); + inet_ntop (AF_INET, &ip4_route->gateway, gatewaystr, sizeof (gatewaystr)); + printf (" ip4-route %s/%d via %s\n", networkstr, ip4_route->plen, gatewaystr); + } + + for (i = 0; i < ip6_routes->len; i++) { + ip6_route = &g_array_index (ip6_routes, NMPlatformIP6Route, i); + inet_ntop (AF_INET6, &ip6_route->network, networkstr, sizeof (networkstr)); + inet_ntop (AF_INET6, &ip6_route->gateway, gatewaystr, sizeof (gatewaystr)); + printf (" ip6-route %s/%d via %s\n", networkstr, ip6_route->plen, gatewaystr); + } + + g_array_unref (ip4_routes); + g_array_unref (ip6_routes); } static void diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c new file mode 100644 index 000000000..2758486fc --- /dev/null +++ b/src/platform/tests/test-route.c @@ -0,0 +1,190 @@ +#include "test-common.h" + +#define DEVICE_NAME "nm-test-device" + +static void +ip4_route_callback (NMPlatform *platform, NMPlatformIP4Route *received, SignalData *data) +{ + g_assert (received); + + if (data->ifindex && data->ifindex != received->ifindex) + return; + + if (data->loop) + g_main_loop_quit (data->loop); + + if (data->received) + g_error ("Received signal '%s' a second time.", data->name); + + data->received = TRUE; +} + +static void +ip6_route_callback (NMPlatform *platform, NMPlatformIP6Route *received, SignalData *data) +{ + g_assert (received); + + if (data->ifindex && data->ifindex != received->ifindex) + return; + + if (data->loop) + g_main_loop_quit (data->loop); + + if (data->received) + g_error ("Received signal '%s' a second time.", data->name); + + data->received = TRUE; +} + +static void +test_ip4_route () +{ + SignalData *route_added = add_signal (NM_PLATFORM_IP4_ROUTE_ADDED, ip4_route_callback); + SignalData *route_removed = add_signal (NM_PLATFORM_IP4_ROUTE_REMOVED, ip4_route_callback); + int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + GArray *routes; + NMPlatformIP4Route rts[3]; + in_addr_t network; + int plen = 24; + in_addr_t gateway; + int metric = 20; + int mss = 1000; + + inet_pton (AF_INET, "192.0.2.0", &network); + inet_pton (AF_INET, "198.51.100.0", &gateway); + + /* Add route to gateway */ + g_assert (nm_platform_ip4_route_add (ifindex, gateway, 32, INADDR_ANY, metric, 0)); no_error (); + accept_signal (route_added); + + /* Add route */ + g_assert (!nm_platform_ip4_route_exists (ifindex, network, plen, gateway, metric)); no_error (); + g_assert (nm_platform_ip4_route_add (ifindex, network, plen, gateway, metric, mss)); no_error (); + g_assert (nm_platform_ip4_route_exists (ifindex, network, plen, gateway, metric)); no_error (); + accept_signal (route_added); + + /* Add route again */ + g_assert (!nm_platform_ip4_route_add (ifindex, network, plen, gateway, metric, mss)); + error (NM_PLATFORM_ERROR_EXISTS); + + /* Test route listing */ + routes = nm_platform_ip4_route_get_all (ifindex); + memset (rts, 0, sizeof (rts)); + rts[0].network = gateway; + rts[0].plen = 32; + rts[0].ifindex = ifindex; + rts[0].gateway = INADDR_ANY; + rts[0].metric = metric; + rts[1].network = network; + rts[1].plen = plen; + rts[1].ifindex = ifindex; + rts[1].gateway = gateway; + rts[1].metric = metric; + g_assert (routes->len == 2); + g_assert (!memcmp (routes->data, rts, sizeof (rts))); + g_array_unref (routes); + + /* Remove route */ + g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, gateway, metric)); no_error (); + g_assert (!nm_platform_ip4_route_exists (ifindex, network, plen, gateway, metric)); + accept_signal (route_removed); + + /* Remove route again */ + g_assert (!nm_platform_ip4_route_delete (ifindex, network, plen, gateway, metric)); + error (NM_PLATFORM_ERROR_NOT_FOUND); + + free_signal (route_added); + free_signal (route_removed); +} + +static void +test_ip6_route () +{ + SignalData *route_added = add_signal (NM_PLATFORM_IP6_ROUTE_ADDED, ip6_route_callback); + SignalData *route_removed = add_signal (NM_PLATFORM_IP6_ROUTE_REMOVED, ip6_route_callback); + int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + GArray *routes; + NMPlatformIP6Route rts[3]; + struct in6_addr network; + int plen = 64; + struct in6_addr gateway; + int metric = 20; + int mss = 1000; + + inet_pton (AF_INET6, "2001:db8:a:b:0:0:0:0", &network); + inet_pton (AF_INET6, "2001:db8:c:d:1:2:3:4", &gateway); + + /* Add gateway address */ + g_assert (nm_platform_ip6_route_add (ifindex, gateway, 128, in6addr_any, metric, 0)); no_error (); + accept_signal (route_added); + + /* Add route */ + g_assert (!nm_platform_ip6_route_exists (ifindex, network, plen, gateway, metric)); no_error (); + g_assert (nm_platform_ip6_route_add (ifindex, network, plen, gateway, metric, mss)); no_error (); + g_assert (nm_platform_ip6_route_exists (ifindex, network, plen, gateway, metric)); no_error (); + accept_signal (route_added); + + /* Add route again */ + g_assert (!nm_platform_ip6_route_add (ifindex, network, plen, gateway, metric, mss)); + error (NM_PLATFORM_ERROR_EXISTS); + + /* Test route listing */ + routes = nm_platform_ip6_route_get_all (ifindex); + memset (rts, 0, sizeof (rts)); + rts[0].network = gateway; + rts[0].plen = 128; + rts[0].ifindex = ifindex; + rts[0].gateway = in6addr_any; + rts[0].metric = metric; + rts[1].network = network; + rts[1].plen = plen; + rts[1].ifindex = ifindex; + rts[1].gateway = gateway; + rts[1].metric = metric; + g_assert (routes->len == 2); + g_assert (!memcmp (routes->data, rts, sizeof (rts))); + g_array_unref (routes); + + /* Remove route */ + g_assert (nm_platform_ip6_route_delete (ifindex, network, plen, gateway, metric)); no_error (); + g_assert (!nm_platform_ip6_route_exists (ifindex, network, plen, gateway, metric)); + accept_signal (route_removed); + + /* Remove route again */ + g_assert (!nm_platform_ip6_route_delete (ifindex, network, plen, gateway, metric)); + error (NM_PLATFORM_ERROR_NOT_FOUND); + + free_signal (route_added); + free_signal (route_removed); +} + +int +main (int argc, char **argv) +{ + int result; + + openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR, LOG_DAEMON); + g_type_init (); + g_test_init (&argc, &argv, NULL); + /* Enable debug messages if called with --debug */ + for (; *argv; argv++) + if (!g_strcmp0 (*argv, "--debug")) + nm_logging_setup ("debug", NULL, NULL); + + SETUP (); + + /* Clean up */ + nm_platform_link_delete_by_name (DEVICE_NAME); + g_assert (!nm_platform_link_exists (DEVICE_NAME)); + g_assert (nm_platform_dummy_add (DEVICE_NAME)); + g_assert (nm_platform_link_set_up (nm_platform_link_get_ifindex (DEVICE_NAME))); + + g_test_add_func ("/route/ip4", test_ip4_route); + g_test_add_func ("/route/ip6", test_ip6_route); + + result = g_test_run (); + + nm_platform_link_delete_by_name (DEVICE_NAME); + nm_platform_free (); + return result; +}