platform: extend API for adding routes

Via the flags of the RTM_NEWROUTE netlink message, kernel and iproute2
support various variants to add a route.

 - ip route add
 - ip route change
 - ip route replace
 - ip route prepend
 - ip route append
 - ip route test

Previously, our nm_platform_ip4_route_add() function was basically
`ip route replace`. In the future, we should rather user `ip route
append` instead.

Anyway, expose the netlink message flags in the API. This allows to
use the various forms, and makes it also more apparent to the user that
they even exist.
This commit is contained in:
Thomas Haller 2017-08-02 10:27:32 +02:00
parent 75dc0fdd27
commit d373855e98
9 changed files with 137 additions and 104 deletions

View file

@ -304,7 +304,7 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g
rt.plen = 0;
rt.metric = entry->effective_metric;
success = nm_platform_ip4_route_add (priv->platform, &rt);
success = nm_platform_ip4_route_add (priv->platform, NMP_NLM_FLAG_REPLACE, &rt);
} else {
NMPlatformIP6Route rt = entry->route.r6;
@ -312,7 +312,7 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g
rt.plen = 0;
rt.metric = entry->effective_metric;
success = nm_platform_ip6_route_add (priv->platform, &rt);
success = nm_platform_ip6_route_add (priv->platform, NMP_NLM_FLAG_REPLACE, &rt);
}
if (!success) {
_LOGW (vtable->vt->addr_family, "failed to add default route %s with effective metric %u",

View file

@ -896,15 +896,16 @@ next:
gateway_routes = g_array_new (FALSE, FALSE, sizeof (guint));
g_array_append_val (gateway_routes, i_ipx_routes);
} else
vtable->vt->route_add (priv->platform, 0, cur_ipx_route, *p_effective_metric);
vtable->vt->route_add (priv->platform, NMP_NLM_FLAG_REPLACE,
cur_ipx_route, 0, *p_effective_metric);
}
if (gateway_routes) {
for (i = 0; i < gateway_routes->len; i++) {
i_ipx_routes = g_array_index (gateway_routes, guint, i);
vtable->vt->route_add (priv->platform, 0,
vtable->vt->route_add (priv->platform, NMP_NLM_FLAG_REPLACE,
ipx_routes->index->entries[i_ipx_routes],
effective_metrics[i_ipx_routes]);
0, effective_metrics[i_ipx_routes]);
}
g_array_unref (gateway_routes);
}
@ -953,7 +954,8 @@ next:
|| route_dest_cmp_result != 0
|| !_route_equals_ignoring_ifindex (vtable, cur_plat_route, cur_ipx_route, *p_effective_metric)) {
if (!vtable->vt->route_add (priv->platform, ifindex, cur_ipx_route, *p_effective_metric)) {
if (!vtable->vt->route_add (priv->platform, NMP_NLM_FLAG_REPLACE,
cur_ipx_route, ifindex, *p_effective_metric)) {
if (cur_ipx_route->rx.rt_source < NM_IP_CONFIG_SOURCE_USER) {
_LOGD (vtable->vt->addr_family,
"ignore error adding IPv%c route to kernel: %s",

View file

@ -1192,7 +1192,10 @@ ip_route_delete (NMPlatform *platform, const NMPObject *obj)
}
static gboolean
ipx_route_add (NMPlatform *platform, int addr_family, const NMPlatformObject *route)
ip_route_add (NMPlatform *platform,
NMPNlmFlags flags,
int addr_family,
const NMPlatformIPRoute *route)
{
NMDedupMultiIter iter;
nm_auto_nmpobj NMPObject *obj = NULL;
@ -1208,10 +1211,13 @@ ipx_route_add (NMPlatform *platform, int addr_family, const NMPlatformObject *ro
g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6));
/* currently, only replace is implemented. */
g_assert (flags == NMP_NLM_FLAG_REPLACE);
obj = nmp_object_new (addr_family == AF_INET
? NMP_OBJECT_TYPE_IP4_ROUTE
: NMP_OBJECT_TYPE_IP6_ROUTE,
route);
(const NMPlatformObject *) route);
rt = &obj->ip_route;
rt->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt->rt_source);
@ -1274,18 +1280,6 @@ ipx_route_add (NMPlatform *platform, int addr_family, const NMPlatformObject *ro
return TRUE;
}
static gboolean
ip4_route_add (NMPlatform *platform, const NMPlatformIP4Route *route)
{
return ipx_route_add (platform, AF_INET, (const NMPlatformObject *) route);
}
static gboolean
ip6_route_add (NMPlatform *platform, const NMPlatformIP6Route *route)
{
return ipx_route_add (platform, AF_INET6, (const NMPlatformObject *) route);
}
/*****************************************************************************/
static void
@ -1396,7 +1390,6 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass)
platform_class->ip4_address_delete = ip4_address_delete;
platform_class->ip6_address_delete = ip6_address_delete;
platform_class->ip4_route_add = ip4_route_add;
platform_class->ip6_route_add = ip6_route_add;
platform_class->ip_route_add = ip_route_add;
platform_class->ip_route_delete = ip_route_delete;
}

View file

@ -2430,7 +2430,7 @@ ip_route_get_lock_flag (const NMPlatformIPRoute *route)
/* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */
static struct nl_msg *
_nl_msg_new_route (int nlmsg_type,
int nlmsg_flags,
NMPNlmFlags nlmsgflags,
const NMPObject *obj)
{
struct nl_msg *msg;
@ -2460,7 +2460,8 @@ _nl_msg_new_route (int nlmsg_type,
nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE));
nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE));
msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags);
nm_assert (((NMPNlmFlags) ((int) nlmsgflags)) == nlmsgflags);
msg = nlmsg_alloc_simple (nlmsg_type, (int) nlmsgflags);
if (!msg)
g_return_val_if_reached (NULL);
@ -5699,41 +5700,39 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, gui
/*****************************************************************************/
static gboolean
ip4_route_add (NMPlatform *platform, const NMPlatformIP4Route *route)
ip_route_add (NMPlatform *platform,
NMPNlmFlags flags,
int addr_family,
const NMPlatformIPRoute *route)
{
NMPObject obj;
NMPlatformIP4Route *r;
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP4_ROUTE, (const NMPlatformObject *) route);
r = NMP_OBJECT_CAST_IP4_ROUTE (&obj);
r->network = nm_utils_ip4_address_clear_host_address (r->network, r->plen);
r->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r->rt_source),
r->scope_inv = nm_platform_route_scope_inv (!r->gateway
? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE);
nlmsg = _nl_msg_new_route (RTM_NEWROUTE,
NLM_F_CREATE | NLM_F_REPLACE,
&obj);
return do_add_addrroute (platform, &obj, nlmsg);
}
static gboolean
ip6_route_add (NMPlatform *platform, const NMPlatformIP6Route *route)
{
NMPObject obj;
NMPlatformIP6Route *r;
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
NMPlatformIP4Route *r4;
NMPlatformIP6Route *r6;
nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route);
r = NMP_OBJECT_CAST_IP6_ROUTE (&obj);
nm_utils_ip6_address_clear_host_address (&r->network, &r->network, r->plen);
r->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r->rt_source),
nm_utils_ip6_address_clear_host_address (&r->src, &r->src, r->src_plen);
switch (addr_family) {
case AF_INET:
nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP4_ROUTE, (const NMPlatformObject *) route);
r4 = NMP_OBJECT_CAST_IP4_ROUTE (&obj);
r4->network = nm_utils_ip4_address_clear_host_address (r4->network, r4->plen);
r4->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r4->rt_source),
r4->scope_inv = nm_platform_route_scope_inv (!r4->gateway
? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE);
break;
case AF_INET6:
nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route);
r6 = NMP_OBJECT_CAST_IP6_ROUTE (&obj);
nm_utils_ip6_address_clear_host_address (&r6->network, &r6->network, r6->plen);
r6->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r6->rt_source),
nm_utils_ip6_address_clear_host_address (&r6->src, &r6->src, r6->src_plen);
break;
default:
nm_assert_not_reached ();
}
nlmsg = _nl_msg_new_route (RTM_NEWROUTE,
NLM_F_CREATE | NLM_F_REPLACE,
&obj);
nlmsg = _nl_msg_new_route (RTM_NEWROUTE, flags, &obj);
if (!nlmsg)
g_return_val_if_reached (FALSE);
return do_add_addrroute (platform, &obj, nlmsg);
}
@ -5781,11 +5780,9 @@ ip_route_delete (NMPlatform *platform,
}
}
nlmsg = _nl_msg_new_route (RTM_DELROUTE,
0,
obj);
nlmsg = _nl_msg_new_route (RTM_DELROUTE, 0, obj);
if (!nlmsg)
return FALSE;
g_return_val_if_reached (FALSE);
return do_delete_object (platform, obj, nlmsg);
}
@ -6516,8 +6513,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
platform_class->ip4_address_delete = ip4_address_delete;
platform_class->ip6_address_delete = ip6_address_delete;
platform_class->ip4_route_add = ip4_route_add;
platform_class->ip6_route_add = ip6_route_add;
platform_class->ip_route_add = ip_route_add;
platform_class->ip_route_delete = ip_route_delete;
platform_class->check_support_kernel_extended_ifa_flags = check_support_kernel_extended_ifa_flags;

View file

@ -3409,53 +3409,60 @@ nm_platform_address_flush (NMPlatform *self, int ifindex)
/*****************************************************************************/
/**
* nm_platform_ip4_route_add:
* @self:
* @route:
*
* For kernel, a gateway can be either explicitly set or left
* at zero (0.0.0.0). In addition, there is the scope of the IPv4
* route.
* When adding a route with
* $ ip route add default dev $IFNAME
* the resulting route will have gateway 0.0.0.0 and scope "link".
* Contrary to
* $ ip route add default via 0.0.0.0 dev $IFNAME
* which adds the route with scope "global".
*
* NetworkManager's Platform can currently only add on-link-routes with scope
* "link" (and gateway 0.0.0.0) or gateway-routes with scope "global" (and
* gateway not 0.0.0.0).
*
* It does not support adding globally scoped routes via 0.0.0.0.
*
* Returns: %TRUE in case of success.
*/
NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nmp_nlm_flag_to_string_lookup, NMPNlmFlags,
NM_UTILS_LOOKUP_DEFAULT (NULL),
NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_ADD, "add"),
NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_CHANGE, "change"),
NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_REPLACE, "replace"),
NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_PREPEND, "prepend"),
NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_APPEND, "append"),
NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_TEST, "test"),
NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_F_APPEND),
);
#define _nmp_nlm_flag_to_string(flags) \
({ \
NMPNlmFlags _flags = (flags); \
\
_nmp_nlm_flag_to_string_lookup (flags) ?: nm_sprintf_bufa (100, "new[0x%x]", (unsigned) _flags); \
})
gboolean
nm_platform_ip4_route_add (NMPlatform *self, const NMPlatformIP4Route *route)
nm_platform_ip4_route_add (NMPlatform *self,
NMPNlmFlags flags,
const NMPlatformIP4Route *route)
{
char sbuf[sizeof (_nm_utils_to_string_buffer)];
_CHECK_SELF (self, klass, FALSE);
g_return_val_if_fail (route, FALSE);
g_return_val_if_fail (route->plen <= 32, FALSE);
_LOGD ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (route, NULL, 0));
_LOGD ("route: %-10s IPv4 route: %s",
_nmp_nlm_flag_to_string (flags),
nm_platform_ip4_route_to_string (route, sbuf, sizeof (sbuf)));
return klass->ip4_route_add (self, route);
return klass->ip_route_add (self, flags, AF_INET, (const NMPlatformIPRoute *) route);
}
gboolean
nm_platform_ip6_route_add (NMPlatform *self, const NMPlatformIP6Route *route)
nm_platform_ip6_route_add (NMPlatform *self,
NMPNlmFlags flags,
const NMPlatformIP6Route *route)
{
char sbuf[sizeof (_nm_utils_to_string_buffer)];
_CHECK_SELF (self, klass, FALSE);
g_return_val_if_fail (route, FALSE);
g_return_val_if_fail (route->plen <= 128, FALSE);
_LOGD ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (route, NULL, 0));
_LOGD ("route: %-10s IPv6 route: %s",
_nmp_nlm_flag_to_string (flags),
nm_platform_ip6_route_to_string (route, sbuf, sizeof (sbuf)));
return klass->ip6_route_add (self, route);
return klass->ip_route_add (self, flags, AF_INET6, (const NMPlatformIPRoute *) route);
}
gboolean
@ -3467,7 +3474,7 @@ nm_platform_ip_route_delete (NMPlatform *self,
nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE,
NMP_OBJECT_TYPE_IP6_ROUTE));
_LOGD ("route: deleting IPv%c route %s",
_LOGD ("route: delete IPv%c route %s",
NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE ? '4' : '6',
nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
@ -5177,7 +5184,11 @@ nm_platform_netns_push (NMPlatform *self, NMPNetns **netns)
/*****************************************************************************/
static gboolean
_vtr_v4_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric)
_vtr_v4_route_add (NMPlatform *self,
NMPNlmFlags flags,
const NMPlatformIPXRoute *route,
int ifindex,
gint64 metric)
{
NMPlatformIP4Route rt = route->r4;
@ -5186,11 +5197,15 @@ _vtr_v4_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *rout
if (metric >= 0)
rt.metric = metric;
return nm_platform_ip4_route_add (self, &rt);
return nm_platform_ip4_route_add (self, flags, &rt);
}
static gboolean
_vtr_v6_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric)
_vtr_v6_route_add (NMPlatform *self,
NMPNlmFlags flags,
const NMPlatformIPXRoute *route,
int ifindex,
gint64 metric)
{
NMPlatformIP6Route rt = route->r6;
@ -5199,7 +5214,7 @@ _vtr_v6_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *rout
if (metric >= 0)
rt.metric = metric;
return nm_platform_ip6_route_add (self, &rt);
return nm_platform_ip6_route_add (self, flags, &rt);
}
static guint32

View file

@ -73,6 +73,25 @@ struct udev_device;
/* Redefine this in host's endianness */
#define NM_GRE_KEY 0x2000
typedef enum {
/* use our own platform enum for the nlmsg-flags. Otherwise, we'd have
* to include <linux/netlink.h> */
NMP_NLM_FLAG_F_REPLACE = 0x100, /* NLM_F_REPLACE, Override existing */
NMP_NLM_FLAG_F_EXCL = 0x200, /* NLM_F_EXCL, Do not touch, if it exists */
NMP_NLM_FLAG_F_CREATE = 0x400, /* NLM_F_CREATE, Create, if it does not exist */
NMP_NLM_FLAG_F_APPEND = 0x800, /* NLM_F_APPEND, Add to end of list */
/* the following aliases correspond to iproute2's `ip route CMD` for
* RTM_NEWROUTE, with CMD being one of add, change, replace, prepend,
* append and test. */
NMP_NLM_FLAG_ADD = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_EXCL,
NMP_NLM_FLAG_CHANGE = NMP_NLM_FLAG_F_REPLACE,
NMP_NLM_FLAG_REPLACE = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_REPLACE,
NMP_NLM_FLAG_PREPEND = NMP_NLM_FLAG_F_CREATE,
NMP_NLM_FLAG_APPEND = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_APPEND,
NMP_NLM_FLAG_TEST = NMP_NLM_FLAG_F_EXCL,
} NMPNlmFlags;
typedef enum {
/* compare fields which kernel considers as similar routes.
* It is a looser comparisong then NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID
@ -489,7 +508,11 @@ typedef struct {
gsize sizeof_route;
int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type);
const char *(*route_to_string) (const NMPlatformIPXRoute *route, char *buf, gsize len);
gboolean (*route_add) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric);
gboolean (*route_add) (NMPlatform *self,
NMPNlmFlags flags,
const NMPlatformIPXRoute *route,
int ifindex,
gint64 metric);
guint32 (*metric_normalize) (guint32 metric);
} NMPlatformVTableRoute;
@ -763,8 +786,10 @@ typedef struct {
gboolean (*ip4_address_delete) (NMPlatform *, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address);
gboolean (*ip6_address_delete) (NMPlatform *, int ifindex, struct in6_addr address, guint8 plen);
gboolean (*ip4_route_add) (NMPlatform *, const NMPlatformIP4Route *route);
gboolean (*ip6_route_add) (NMPlatform *, const NMPlatformIP6Route *route);
gboolean (*ip_route_add) (NMPlatform *,
NMPNlmFlags flags,
int addr_family,
const NMPlatformIPRoute *route);
gboolean (*ip_route_delete) (NMPlatform *, const NMPObject *obj);
gboolean (*check_support_kernel_extended_ifa_flags) (NMPlatform *);
@ -1077,8 +1102,10 @@ gboolean nm_platform_address_flush (NMPlatform *self, int ifindex);
const NMPlatformIP4Route *nm_platform_ip4_route_get (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric);
const NMPlatformIP6Route *nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric);
gboolean nm_platform_ip4_route_add (NMPlatform *self, const NMPlatformIP4Route *route);
gboolean nm_platform_ip6_route_add (NMPlatform *self, const NMPlatformIP6Route *route);
gboolean nm_platform_ip4_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route);
gboolean nm_platform_ip6_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route);
gboolean nm_platform_ip_route_delete (NMPlatform *self, const NMPObject *route);
const char *nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len);

View file

@ -903,7 +903,7 @@ void nmtstp_ip4_route_add (NMPlatform *platform,
route.metric = metric;
route.mss = mss;
g_assert (nm_platform_ip4_route_add (platform, &route));
g_assert (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_REPLACE, &route));
}
void nmtstp_ip6_route_add (NMPlatform *platform,
@ -927,7 +927,7 @@ void nmtstp_ip6_route_add (NMPlatform *platform,
route.metric = metric;
route.mss = mss;
g_assert (nm_platform_ip6_route_add (platform, &route));
g_assert (nm_platform_ip6_route_add (platform, NMP_NLM_FLAG_REPLACE, &route));
}
/*****************************************************************************/

View file

@ -432,7 +432,7 @@ test_ip4_route_options (void)
route.mtu = 1350;
route.lock_cwnd = TRUE;
g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, &route));
g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &route));
/* Test route listing */
routes = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex);
@ -532,7 +532,7 @@ test_ip6_route_options (gconstpointer test_data)
_wait_for_ipv6_addr_non_tentative (NM_PLATFORM_GET, 400, IFINDEX, addr_n, addr_in6);
for (i = 0; i < rts_n; i++)
g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, &rts_add[i]));
g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]));
routes = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, IFINDEX);
switch (TEST_IDX) {

View file

@ -857,7 +857,7 @@ test_ip4_full_sync (test_fixture *fixture, gconstpointer user_data)
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02);
_assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r03);
vtable->route_add (NM_PLATFORM_GET, 0, (const NMPlatformIPXRoute *) &r03, -1);
vtable->route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, (const NMPlatformIPXRoute *) &r03, 0, -1);
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01);
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02);