platform: merge branch 'th/platform-netlink-events'

Add test cases for certain netlink peculiarities
and fix our NMLinxPlatform implementation to handle
them better.
This commit is contained in:
Thomas Haller 2015-11-27 14:43:18 +01:00
commit 7300e59c14
21 changed files with 535 additions and 1096 deletions

View file

@ -69,6 +69,8 @@
* If you set the level to DEBUG or TRACE, it also sets G_MESSAGES_DEBUG=all (unless
* in assert-logging mode and unless G_MESSAGES_DEBUG is already defined).
*
* "TRACE", this is shorthand for "log-level=TRACE".
*
* "sudo-cmd=PATH": when running root tests as normal user, the test will execute
* itself by invoking sudo at PATH.
* For example
@ -126,16 +128,26 @@ nmtst_assert_error (GError *error,
}
}
#define NMTST_BUSY_WAIT(max_wait_ms, condition, wait) \
G_STMT_START { \
#define NMTST_WAIT(max_wait_ms, wait) \
({ \
gboolean _not_expired = TRUE; \
gint64 _nmtst_end, _nmtst_max_wait_us = (max_wait_ms) * 1000L; \
\
_nmtst_end = g_get_monotonic_time () + _nmtst_max_wait_us; \
while (!(condition)) { \
while (TRUE) { \
{ wait }; \
if (g_get_monotonic_time () > _nmtst_end) \
g_assert_not_reached (); \
if (g_get_monotonic_time () > _nmtst_end) { \
_not_expired = FALSE; \
break; \
} \
} \
_not_expired; \
})
#define NMTST_WAIT_ASSERT(max_wait_ms, wait) \
G_STMT_START { \
if (!(NMTST_WAIT (max_wait_ms, wait))) \
g_assert_not_reached (); \
} G_STMT_END
inline static void
@ -322,6 +334,9 @@ __nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_
} else if (!g_ascii_strncasecmp (debug, "log-level=", strlen ("log-level="))) {
g_free (c_log_level);
log_level = c_log_level = g_strdup (&debug[strlen ("log-level=")]);
} else if (!g_ascii_strcasecmp (debug, "TRACE")) {
g_free (c_log_level);
log_level = c_log_level = g_strdup (debug);
} else if (!g_ascii_strncasecmp (debug, "log-domains=", strlen ("log-domains="))) {
g_free (c_log_domains);
log_domains = c_log_domains = g_strdup (&debug[strlen ("log-domains=")]);

View file

@ -292,7 +292,7 @@ error:
}
static void
link_changed_cb (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMDeviceAdsl *self)
link_changed_cb (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMDeviceAdsl *self)
{
if (change_type == NM_PLATFORM_SIGNAL_REMOVED) {
NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);

View file

@ -1580,7 +1580,6 @@ link_changed_cb (NMPlatform *platform,
int ifindex,
NMPlatformLink *info,
NMPlatformSignalChangeType change_type,
NMPlatformReason reason,
NMDevice *self)
{
NMDevicePrivate *priv;
@ -1590,12 +1589,6 @@ link_changed_cb (NMPlatform *platform,
priv = NM_DEVICE_GET_PRIVATE (self);
/* We don't filter by 'reason' because we are interested in *all* link
* changes. For example a call to nm_platform_link_set_up() may result
* in an internal carrier change (i.e. we ask the kernel to set IFF_UP
* and it results in also setting IFF_LOWER_UP.
*/
if (ifindex == nm_device_get_ifindex (self)) {
if (!priv->device_link_changed_id) {
priv->device_link_changed_id = g_idle_add ((GSourceFunc) device_link_changed, self);
@ -7932,7 +7925,6 @@ device_ipx_changed (NMPlatform *platform,
int ifindex,
gpointer platform_object,
NMPlatformSignalChangeType change_type,
NMPlatformReason reason,
NMDevice *self)
{
NMDevicePrivate *priv;

View file

@ -1359,7 +1359,6 @@ _platform_changed_cb (NMPlatform *platform,
int ifindex,
gpointer platform_object,
NMPlatformSignalChangeType change_type,
NMPlatformReason reason,
NMDefaultRouteManager *self)
{
switch (obj_type) {

View file

@ -332,7 +332,6 @@ ip6_address_changed (NMPlatform *platform,
int iface,
NMPlatformIP6Address *addr,
NMPlatformSignalChangeType change_type,
NMPlatformReason reason,
NMRDisc *rdisc)
{
if ( (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->flags & IFA_F_DADFAILED)

View file

@ -1965,7 +1965,6 @@ platform_link_cb (NMPlatform *platform,
int ifindex,
NMPlatformLink *plink,
NMPlatformSignalChangeType change_type,
NMPlatformReason reason,
gpointer user_data)
{
PlatformLinkCbData *data;

View file

@ -1009,7 +1009,6 @@ _ip4_device_routes_ip4_route_changed (NMPlatform *platform,
int ifindex,
const NMPlatformIP4Route *route,
NMPlatformSignalChangeType change_type,
NMPlatformReason reason,
NMRouteManager *self)
{
NMRouteManagerPrivate *priv;

View file

@ -308,7 +308,7 @@ link_add (NMPlatform *platform,
g_array_append_val (priv->links, device);
if (device.link.ifindex) {
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, device.link.ifindex, &device, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, device.link.ifindex, &device, NM_PLATFORM_SIGNAL_ADDED);
link_changed (platform, &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1), FALSE);
}
@ -360,7 +360,7 @@ link_delete (NMPlatform *platform, int ifindex)
memset (route, 0, sizeof (*route));
}
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, ifindex, &deleted_device, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, ifindex, &deleted_device, NM_PLATFORM_SIGNAL_REMOVED);
return TRUE;
}
@ -378,7 +378,7 @@ link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_s
int i;
if (raise_signal)
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, device->link.ifindex, &device->link, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, device->link.ifindex, &device->link, NM_PLATFORM_SIGNAL_CHANGED);
if (device->link.ifindex && !IN6_IS_ADDR_UNSPECIFIED (&device->ip6_lladdr)) {
if (device->link.connected)
@ -925,12 +925,12 @@ ip4_address_add (NMPlatform *platform,
memcpy (item, &address, sizeof (address));
if (changed)
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED);
return TRUE;
}
g_array_append_val (priv->ip4_addresses, address);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED);
return TRUE;
}
@ -971,12 +971,12 @@ ip6_address_add (NMPlatform *platform,
memcpy (item, &address, sizeof (address));
if (changed)
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED);
return TRUE;
}
g_array_append_val (priv->ip6_addresses, address);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED);
return TRUE;
}
@ -998,7 +998,7 @@ ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen,
memcpy (&deleted_address, address, sizeof (deleted_address));
memset (address, 0, sizeof (*address));
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED);
return TRUE;
}
}
@ -1022,7 +1022,7 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int
memcpy (&deleted_address, address, sizeof (deleted_address));
memset (address, 0, sizeof (*address));
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED);
return TRUE;
}
}
@ -1146,7 +1146,7 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen
memcpy (&deleted_route, route, sizeof (deleted_route));
g_array_remove_index (priv->ip4_routes, i);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED);
}
return TRUE;
@ -1172,7 +1172,7 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, in
memcpy (&deleted_route, route, sizeof (deleted_route));
g_array_remove_index (priv->ip6_routes, i);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED);
}
return TRUE;
@ -1235,12 +1235,12 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
}
memcpy (item, &route, sizeof (route));
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED);
return TRUE;
}
g_array_append_val (priv->ip4_routes, route);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED);
return TRUE;
}
@ -1302,12 +1302,12 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
}
memcpy (item, &route, sizeof (route));
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED);
return TRUE;
}
g_array_append_val (priv->ip6_routes, route);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL);
g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED);
return TRUE;
}

View file

@ -149,7 +149,6 @@ static void do_request_link (NMPlatform *platform, int ifindex, const char *name
static void do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean handle_delayed_action);
static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data);
static gboolean event_handler_read_netlink_all (NMPlatform *platform, gboolean wait_for_acks);
static NMPCacheOpsType cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_id, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason);
/******************************************************************
* Support IFLA_INET6_ADDR_GEN_MODE
@ -2200,7 +2199,6 @@ struct _NMLinuxPlatformPrivate {
} delayed_action;
GHashTable *prune_candidates;
GHashTable *delayed_deletion;
GHashTable *wifi_data;
};
@ -2257,7 +2255,7 @@ process_events (NMPlatform *platform)
/******************************************************************/
static void
do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cache_op, gboolean was_visible, NMPlatformReason reason)
do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cache_op, gboolean was_visible)
{
gboolean is_visible;
NMPObject obj_clone;
@ -2304,16 +2302,21 @@ do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cach
klass = NMP_OBJECT_GET_CLASS (obj);
_LOGt ("emit signal %s %s: %s (%ld)",
_LOGt ("emit signal %s %s: %s",
klass->signal_type,
nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op),
nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0),
(long) reason);
nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
/* don't expose @obj directly, but clone the public fields. A signal handler might
* call back into NMPlatform which could invalidate (or modify) @obj. */
memcpy (&obj_clone.object, &obj->object, klass->sizeof_public);
g_signal_emit_by_name (platform, klass->signal_type, klass->obj_type, obj_clone.object.ifindex, &obj_clone.object, (NMPlatformSignalChangeType) cache_op, reason);
g_signal_emit (platform,
_nm_platform_signal_id_get (klass->signal_type_id),
0,
klass->obj_type,
obj_clone.object.ifindex,
&obj_clone.object,
(NMPlatformSignalChangeType) cache_op);
}
/******************************************************************/
@ -2373,7 +2376,7 @@ delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex
NMPCacheOpsType cache_op;
cache_op = nmp_cache_update_link_master_connected (priv->cache, master_ifindex, &obj_cache, &was_visible, cache_pre_hook, platform);
do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL);
do_emit_signal (platform, obj_cache, cache_op, was_visible);
}
static void
@ -2490,33 +2493,6 @@ delayed_action_handle_idle (gpointer user_data)
return G_SOURCE_REMOVE;
}
static void
delayed_action_clear_REFRESH_LINK (NMPlatform *platform, int ifindex)
{
NMLinuxPlatformPrivate *priv;
gssize idx;
gpointer user_data;
if (ifindex <= 0)
return;
priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
if (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK))
return;
user_data = GINT_TO_POINTER (ifindex);
idx = _nm_utils_ptrarray_find_first (priv->delayed_action.list_refresh_link->pdata, priv->delayed_action.list_refresh_link->len, user_data);
if (idx < 0)
return;
_LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, user_data, "clear");
g_ptr_array_remove_index_fast (priv->delayed_action.list_refresh_link, idx);
if (priv->delayed_action.list_refresh_link->len == 0)
priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK;
}
static void
delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data)
{
@ -2619,45 +2595,12 @@ cache_prune_candidates_prune (NMPlatform *platform)
_LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0));
cache_op = nmp_cache_remove (priv->cache, obj, TRUE, &obj_cache, &was_visible, cache_pre_hook, platform);
do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL);
do_emit_signal (platform, obj_cache, cache_op, was_visible);
}
g_hash_table_unref (prune_candidates);
}
static void
cache_delayed_deletion_prune (NMPlatform *platform)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
GPtrArray *prune_list = NULL;
GHashTableIter iter;
guint i;
NMPObject *obj;
if (g_hash_table_size (priv->delayed_deletion) == 0)
return;
g_hash_table_iter_init (&iter, priv->delayed_deletion);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &obj)) {
if (obj) {
if (!prune_list)
prune_list = g_ptr_array_new_full (g_hash_table_size (priv->delayed_deletion), (GDestroyNotify) nmp_object_unref);
g_ptr_array_add (prune_list, nmp_object_ref (obj));
}
}
g_hash_table_remove_all (priv->delayed_deletion);
if (prune_list) {
for (i = 0; i < prune_list->len; i++) {
obj = prune_list->pdata[i];
_LOGt ("delayed-deletion: delete %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0));
cache_remove_netlink (platform, obj, NULL, NULL, NM_PLATFORM_REASON_EXTERNAL);
}
g_ptr_array_unref (prune_list);
}
}
static void
cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data)
{
@ -2738,7 +2681,8 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP
/* removal of a link could be caused by moving the link to another netns.
* In this case, we potentially have to update other links that have this link as parent.
* Currently, kernel misses to sent us a notification in this case (rh #1262908). */
* Currently, kernel misses to sent us a notification in this case
* (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */
if ( ops_type == NMP_CACHE_OPS_REMOVED
&& old /* <-- nonsensical, make coverity happy */
@ -2778,6 +2722,40 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP
NULL);
}
}
if ( NM_IN_SET (ops_type, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED)
&& (new && new->_link.netlink.is_in_netlink)
&& (!old || !old->_link.netlink.is_in_netlink))
{
if (!new->_link.netlink.lnk) {
/* certain link-types also come with a IFLA_INFO_DATA/lnk_data. It may happen that
* kernel didn't send this notification, thus when we first learn about a link
* that lacks an lnk_data we re-request it again.
*
* For example https://bugzilla.redhat.com/show_bug.cgi?id=1284001 */
switch (new->link.type) {
case NM_LINK_TYPE_GRE:
case NM_LINK_TYPE_INFINIBAND:
case NM_LINK_TYPE_MACVLAN:
case NM_LINK_TYPE_VLAN:
case NM_LINK_TYPE_VXLAN:
delayed_action_schedule (platform,
DELAYED_ACTION_TYPE_REFRESH_LINK,
GINT_TO_POINTER (new->link.ifindex));
break;
default:
break;
}
}
if ( new->link.type == NM_LINK_TYPE_VETH
&& new->link.parent == 0) {
/* the initial notification when adding a veth pair can lack the parent/IFLA_LINK
* (https://bugzilla.redhat.com/show_bug.cgi?id=1285827).
* Request it again. */
delayed_action_schedule (platform,
DELAYED_ACTION_TYPE_REFRESH_LINK,
GINT_TO_POINTER (new->link.ifindex));
}
}
{
/* on enslave/release, we also refresh the master. */
int ifindex1 = 0, ifindex2 = 0;
@ -2797,7 +2775,22 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP
if (ifindex2 > 0 && ifindex1 != ifindex2)
delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex2));
}
}
{
if ( ( (ops_type == NMP_CACHE_OPS_REMOVED)
|| ( (ops_type == NMP_CACHE_OPS_UPDATED)
&& new
&& !new->_link.netlink.is_in_netlink))
&& old
&& old->_link.netlink.is_in_netlink
&& old->link.master) {
/* sometimes we receive a wrong RTM_DELLINK message when unslaving
* a device. Refetch the link again to check whether the device
* is really gone.
*
* https://bugzilla.redhat.com/show_bug.cgi?id=1285719#c2 */
delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (old->link.ifindex));
}
}
break;
case NMP_OBJECT_TYPE_IP4_ADDRESS:
@ -2819,7 +2812,7 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP
}
static NMPCacheOpsType
cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_id, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason)
cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_id, NMPObject **out_obj_cache, gboolean *out_was_visible)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
NMPObject *obj_cache;
@ -2827,7 +2820,7 @@ cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_id, NMPObject *
NMPCacheOpsType cache_op;
cache_op = nmp_cache_remove_netlink (priv->cache, obj_id, &obj_cache, &was_visible, cache_pre_hook, platform);
do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL);
do_emit_signal (platform, obj_cache, cache_op, was_visible);
if (out_obj_cache)
*out_obj_cache = obj_cache;
@ -2840,7 +2833,7 @@ cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_id, NMPObject *
}
static NMPCacheOpsType
cache_update_netlink (NMPlatform *platform, NMPObject *obj, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason)
cache_update_netlink (NMPlatform *platform, NMPObject *obj, NMPObject **out_obj_cache, gboolean *out_was_visible)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
NMPObject *obj_cache;
@ -2851,7 +2844,7 @@ cache_update_netlink (NMPlatform *platform, NMPObject *obj, NMPObject **out_obj_
* at once. */
cache_op = nmp_cache_update_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform);
do_emit_signal (platform, obj_cache, cache_op, was_visible, reason);
do_emit_signal (platform, obj_cache, cache_op, was_visible);
if (out_obj_cache)
*out_obj_cache = obj_cache;
@ -2891,13 +2884,8 @@ do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean h
_LOGD ("do-request-link: %d %s", ifindex, name ? name : "");
if (ifindex > 0) {
NMPObject *obj;
cache_prune_candidates_record_one (platform,
(NMPObject *) nmp_cache_lookup_link (priv->cache, ifindex));
obj = nmp_object_new_link (ifindex);
_LOGt ("delayed-deletion: protect object %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0));
g_hash_table_insert (priv->delayed_deletion, obj, NULL);
}
event_handler_read_netlink_all (platform, FALSE);
@ -2918,7 +2906,6 @@ do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean h
event_handler_read_netlink_all (platform, TRUE);
cache_delayed_deletion_prune (platform);
cache_prune_candidates_prune (platform);
if (handle_delayed_action)
@ -3076,34 +3063,15 @@ event_notification (struct nl_msg *msg, gpointer user_data)
switch (msghdr->nlmsg_type) {
case RTM_NEWLINK:
if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) {
if (g_hash_table_lookup (priv->delayed_deletion, obj) != NULL) {
/* the object is scheduled for delayed deletion. Replace that object
* by clearing the value from priv->delayed_deletion. */
_LOGt ("delayed-deletion: clear delayed deletion of protected object %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0));
g_hash_table_insert (priv->delayed_deletion, nmp_object_ref (obj), NULL);
}
delayed_action_clear_REFRESH_LINK (platform, obj->link.ifindex);
}
/* fall-through */
case RTM_NEWADDR:
case RTM_NEWROUTE:
cache_update_netlink (platform, obj, &obj_cache, NULL, NM_PLATFORM_REASON_EXTERNAL);
cache_update_netlink (platform, obj, &obj_cache, NULL);
break;
case RTM_DELLINK:
if ( NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK
&& g_hash_table_contains (priv->delayed_deletion, obj)) {
/* We sometimes receive spurious RTM_DELLINK events. In this case, we want to delay
* the deletion of the object until later. */
_LOGt ("delayed-deletion: delay deletion of protected object %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0));
g_hash_table_insert (priv->delayed_deletion, nmp_object_ref (obj), nmp_object_ref (obj));
break;
}
/* fall-through */
case RTM_DELADDR:
case RTM_DELROUTE:
cache_remove_netlink (platform, obj, &obj_cache, NULL, NM_PLATFORM_REASON_EXTERNAL);
cache_remove_netlink (platform, obj, &obj_cache, NULL);
break;
default:
@ -5201,7 +5169,7 @@ cache_update_link_udev (NMPlatform *platform, int ifindex, GUdevDevice *udev_dev
NMPCacheOpsType cache_op;
cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udev_device, &obj_cache, &was_visible, cache_pre_hook, platform);
do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL);
do_emit_signal (platform, obj_cache, cache_op, was_visible);
}
static void
@ -5304,10 +5272,6 @@ nm_linux_platform_init (NMLinuxPlatform *self)
self->priv = priv;
priv->delayed_deletion = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash,
(GEqualFunc) nmp_object_id_equal,
(GDestroyNotify) nmp_object_unref,
(GDestroyNotify) nmp_object_unref);
priv->cache = nmp_cache_new ();
priv->delayed_action.list_master_connected = g_ptr_array_new ();
priv->delayed_action.list_refresh_link = g_ptr_array_new ();
@ -5410,7 +5374,6 @@ dispose (GObject *object)
nm_clear_g_source (&priv->delayed_action.idle_id);
g_clear_pointer (&priv->prune_candidates, g_hash_table_unref);
g_clear_pointer (&priv->delayed_deletion, g_hash_table_unref);
G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object);
}

View file

@ -77,17 +77,7 @@ G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OF
G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT)
/* NMPlatform signals */
enum {
SIGNAL_LINK_CHANGED,
SIGNAL_IP4_ADDRESS_CHANGED,
SIGNAL_IP6_ADDRESS_CHANGED,
SIGNAL_IP4_ROUTE_CHANGED,
SIGNAL_IP6_ROUTE_CHANGED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = { 0 };
enum {
PROP_0,
@ -101,6 +91,18 @@ typedef struct {
/******************************************************************/
guint
_nm_platform_signal_id_get (NMPlatformSignalIdType signal_type)
{
nm_assert ( signal_type > 0
&& signal_type != NM_PLATFORM_SIGNAL_ID_NONE
&& signal_type < _NM_PLATFORM_SIGNAL_ID_LAST);
return signals[signal_type];
}
/******************************************************************/
/* Singleton NMPlatform subclass instance and cached class object */
NM_DEFINE_SINGLETON_INSTANCE (NMPlatform);
@ -3697,14 +3699,6 @@ nm_platform_init (NMPlatform *object)
{
}
#define SIGNAL(signal_id, method) signals[signal_id] = \
g_signal_new_class_handler (NM_PLATFORM_ ## signal_id, \
G_OBJECT_CLASS_TYPE (object_class), \
G_SIGNAL_RUN_FIRST, \
G_CALLBACK (method), \
NULL, NULL, NULL, \
G_TYPE_NONE, 5, NM_TYPE_POBJECT_TYPE, G_TYPE_INT, G_TYPE_POINTER, NM_TYPE_PLATFORM_SIGNAL_CHANGE_TYPE, NM_TYPE_PLATFORM_REASON);
static void
nm_platform_class_init (NMPlatformClass *platform_class)
{
@ -3725,10 +3719,21 @@ nm_platform_class_init (NMPlatformClass *platform_class)
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
#define SIGNAL(signal, signal_id, method) \
G_STMT_START { \
signals[signal] = \
g_signal_new_class_handler (""signal_id"", \
G_OBJECT_CLASS_TYPE (object_class), \
G_SIGNAL_RUN_FIRST, \
G_CALLBACK (method), \
NULL, NULL, NULL, \
G_TYPE_NONE, 4, NM_TYPE_POBJECT_TYPE, G_TYPE_INT, G_TYPE_POINTER, NM_TYPE_PLATFORM_SIGNAL_CHANGE_TYPE); \
} G_STMT_END
/* Signals */
SIGNAL (SIGNAL_LINK_CHANGED, log_link)
SIGNAL (SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address)
SIGNAL (SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address)
SIGNAL (SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route)
SIGNAL (SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route)
SIGNAL (NM_PLATFORM_SIGNAL_ID_LINK, NM_PLATFORM_SIGNAL_LINK_CHANGED, log_link);
SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address);
SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address);
SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route);
SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route);
}

View file

@ -83,19 +83,6 @@ typedef enum { /*< skip >*/
NM_PLATFORM_ERROR_NO_FIRMWARE,
} NMPlatformError;
typedef enum {
NM_PLATFORM_REASON_NONE,
/* Event was requested by NetworkManager. */
NM_PLATFORM_REASON_INTERNAL,
/* Event came from the kernel. */
NM_PLATFORM_REASON_EXTERNAL,
/* Event is a result of cache checking and cleanups. */
NM_PLATFORM_REASON_CACHE_CHECK,
/* Internal reason to suppress announcing change events */
_NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL,
} NMPlatformReason;
typedef struct {
union {
@ -166,6 +153,18 @@ struct _NMPlatformLink {
guint mtu;
};
typedef enum { /*< skip >*/
NM_PLATFORM_SIGNAL_ID_NONE,
NM_PLATFORM_SIGNAL_ID_LINK,
NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS,
NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS,
NM_PLATFORM_SIGNAL_ID_IP4_ROUTE,
NM_PLATFORM_SIGNAL_ID_IP6_ROUTE,
_NM_PLATFORM_SIGNAL_ID_LAST,
} NMPlatformSignalIdType;
guint _nm_platform_signal_id_get (NMPlatformSignalIdType signal_type);
typedef enum {
NM_PLATFORM_SIGNAL_NONE,
NM_PLATFORM_SIGNAL_ADDED,

View file

@ -1931,6 +1931,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.obj_type_name = "link",
.addr_family = AF_UNSPEC,
.rtm_gettype = RTM_GETLINK,
.signal_type_id = NM_PLATFORM_SIGNAL_ID_LINK,
.signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED,
.cmd_obj_cmp = _vt_cmd_obj_cmp_link,
.cmd_obj_copy = _vt_cmd_obj_copy_link,
@ -1953,6 +1954,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.obj_type_name = "ip4-address",
.addr_family = AF_INET,
.rtm_gettype = RTM_GETADDR,
.signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS,
.signal_type = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED,
.cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address,
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_address,
@ -1972,6 +1974,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.obj_type_name = "ip6-address",
.addr_family = AF_INET6,
.rtm_gettype = RTM_GETADDR,
.signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS,
.signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED,
.cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address,
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_address,
@ -1991,6 +1994,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.obj_type_name = "ip4-route",
.addr_family = AF_INET,
.rtm_gettype = RTM_GETROUTE,
.signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE,
.signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED,
.cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route,
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_route,
@ -2010,6 +2014,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.obj_type_name = "ip6-route",
.addr_family = AF_INET6,
.rtm_gettype = RTM_GETROUTE,
.signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ROUTE,
.signal_type = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED,
.cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route,
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_route,

View file

@ -116,6 +116,7 @@ typedef struct {
int rtm_gettype;
int sizeof_data;
int sizeof_public;
NMPlatformSignalIdType signal_type_id;
const char *obj_type_name;
const char *signal_type;

View file

@ -32,7 +32,6 @@ PLATFORM_LDADD = \
noinst_PROGRAMS = \
monitor \
platform \
test-link-fake \
test-link-linux \
test-address-fake \
@ -49,9 +48,6 @@ EXTRA_DIST = test-common.h
monitor_SOURCES = monitor.c $(PLATFORM_SOURCES)
monitor_LDADD = $(PLATFORM_LDADD)
platform_SOURCES = platform.c $(PLATFORM_SOURCES)
platform_LDADD = $(PLATFORM_LDADD)
test_link_fake_SOURCES = test-link.c $(TEST_SOURCES)
test_link_fake_CPPFLAGS = \
$(AM_CPPFLAGS) \

View file

@ -1,892 +0,0 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* nm-platform.c - Handle runtime kernel networking configuration
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2013 Red Hat, Inc.
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netlink/route/addr.h>
#include "nm-default.h"
#include "nm-platform.h"
#include "nm-linux-platform.h"
#include "nm-fake-platform.h"
#include "nm-utils.h"
#define error(...) fprintf (stderr, __VA_ARGS__)
typedef gboolean boolean_t;
typedef int decimal_t;
typedef const char *string_t;
#define print_boolean(value) printf ("%s\n", value ? "yes" : "no")
#define print_decimal(value) printf ("%d\n", value)
#define print_string(value) printf ("%s\n", value)
static gboolean
do_sysctl_set (char **argv)
{
return nm_platform_sysctl_set (NM_PLATFORM_GET, argv[0], argv[1]);
}
static gboolean
do_sysctl_get (char **argv)
{
gs_free char *value = nm_platform_sysctl_get (NM_PLATFORM_GET, argv[0]);
printf ("%s\n", value);
return !!value;
}
static int
parse_ifindex (const char *str)
{
char *endptr;
int ifindex = 0;
ifindex = strtol (str, &endptr, 10);
if (*endptr) {
ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, str);
}
return ifindex;
}
static gboolean
do_link_get_all (char **argv)
{
GArray *links;
NMPlatformLink *device;
int i;
links = nm_platform_link_get_all (NM_PLATFORM_GET);
for (i = 0; i < links->len; i++) {
device = &g_array_index (links, NMPlatformLink, i);
printf ("%d: %s type %d\n", device->ifindex, device->name, device->type);
}
g_array_unref (links);
return TRUE;
}
static gboolean
do_dummy_add (char **argv)
{
return nm_platform_dummy_add (NM_PLATFORM_GET, argv[0], NULL) == NM_PLATFORM_ERROR_SUCCESS;
}
static gboolean
do_bridge_add (char **argv)
{
return nm_platform_bridge_add (NM_PLATFORM_GET, argv[0], NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS;
}
static gboolean
do_bond_add (char **argv)
{
return nm_platform_bond_add (NM_PLATFORM_GET, argv[0], NULL) == NM_PLATFORM_ERROR_SUCCESS;
}
static gboolean
do_team_add (char **argv)
{
return nm_platform_team_add (NM_PLATFORM_GET, argv[0], NULL) == NM_PLATFORM_ERROR_SUCCESS;
}
static gboolean
do_vlan_add (char **argv)
{
const char *name = *argv++;
int parent = parse_ifindex (*argv++);
int vlanid = strtol (*argv++, NULL, 10);
guint32 vlan_flags = strtol (*argv++, NULL, 10);
return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent, vlanid, vlan_flags, NULL) == NM_PLATFORM_ERROR_SUCCESS;
}
static gboolean
do_link_exists (char **argv)
{
gboolean value = !!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, argv[0]);
print_boolean (value);
return TRUE;
}
#define LINK_CMD(cmdname) \
static gboolean \
do_link_##cmdname (char **argv) \
{ \
int ifindex = parse_ifindex (argv[0]); \
return ifindex ? nm_platform_link_##cmdname (NM_PLATFORM_GET, ifindex) : FALSE; \
}
#define LINK_CMD_GET_FULL(cmdname, type, cond) \
static gboolean \
do_link_##cmdname (char **argv) \
{ \
int ifindex = parse_ifindex (argv[0]); \
if (ifindex) { \
type##_t value = nm_platform_link_##cmdname (NM_PLATFORM_GET, ifindex); \
if (cond) { \
print_##type (value); \
return TRUE; \
} \
} \
return FALSE; \
}
#define LINK_CMD_GET(cmdname, type) LINK_CMD_GET_FULL (cmdname, type, TRUE);
LINK_CMD (delete)
/* do_link_delete_by_ifname:
*
* We don't need this as we allow ifname instead of ifindex anyway.
*/
static gboolean
do_link_get_ifindex (char **argv)
{
int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, argv[0]);
if (ifindex)
printf ("%d\n", ifindex);
return !!ifindex;
}
LINK_CMD_GET_FULL (get_name, string, value)
LINK_CMD_GET_FULL (get_type, decimal, value > 0)
LINK_CMD_GET (is_software, boolean)
LINK_CMD_GET (supports_slaves, boolean)
static gboolean
do_link_set_up (char **argv)
{
int ifindex = parse_ifindex (argv[0]);
return ifindex ? nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL) : FALSE;
}
LINK_CMD (set_down)
LINK_CMD (set_arp)
LINK_CMD (set_noarp)
LINK_CMD_GET (is_up, boolean)
LINK_CMD_GET (is_connected, boolean)
LINK_CMD_GET (uses_arp, boolean)
static gboolean
do_link_set_address (char **argv)
{
int ifindex = parse_ifindex (*argv++);
char *hex = *argv++;
int hexlen = strlen (hex);
char address[hexlen/2];
char *endptr;
int i;
g_assert (!(hexlen % 2));
for (i = 0; i < sizeof (address); i++) {
char digit[3];
digit[0] = hex[2*i];
digit[1] = hex[2*i+1];
digit[2] = '\0';
address[i] = strtoul (digit, &endptr, 16);
g_assert (!*endptr);
}
return nm_platform_link_set_address (NM_PLATFORM_GET, ifindex, address, sizeof (address));
}
static gboolean
do_link_get_address (char **argv)
{
int ifindex = parse_ifindex (*argv++);
const char *address;
size_t length;
int i;
address = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &length);
if (!address || length <= 0)
return FALSE;
for (i = 0; i < length; i++)
printf ("%02x", address[i]);
printf ("\n");
return TRUE;
}
static gboolean
do_link_set_mtu (char **argv)
{
int ifindex = parse_ifindex (*argv++);
int mtu = strtoul (*argv++, NULL, 10);
return nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, mtu);
}
LINK_CMD_GET (get_mtu, decimal);
LINK_CMD_GET (supports_carrier_detect, boolean)
LINK_CMD_GET (supports_vlans, boolean)
static gboolean
do_link_enslave (char **argv)
{
int master = parse_ifindex (*argv++);
int slave = parse_ifindex (*argv++);
return nm_platform_link_enslave (NM_PLATFORM_GET, master, slave);
}
static gboolean
do_link_release (char **argv)
{
int master = parse_ifindex (*argv++);
int slave = parse_ifindex (*argv++);
return nm_platform_link_release (NM_PLATFORM_GET, master, slave);
}
LINK_CMD_GET (get_master, decimal)
static gboolean
do_master_set_option (char **argv)
{
int ifindex = parse_ifindex (*argv++);
const char *option = *argv++;
const char *value = *argv++;
return nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, option, value);
}
static gboolean
do_master_get_option (char **argv)
{
int ifindex = parse_ifindex (*argv++);
const char *option = *argv++;
gs_free char *value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, option);
printf ("%s\n", value);
return !!value;
}
static gboolean
do_slave_set_option (char **argv)
{
int ifindex = parse_ifindex (*argv++);
const char *option = *argv++;
const char *value = *argv++;
return nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, option, value);
}
static gboolean
do_slave_get_option (char **argv)
{
int ifindex = parse_ifindex (*argv++);
const char *option = *argv++;
gs_free char *value = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex, option);
printf ("%s\n", value);
return !!value;
}
static gboolean
do_vlan_get_info (char **argv)
{
int ifindex = parse_ifindex (*argv++);
const NMPlatformLink *plink;
const NMPlatformLnkVlan *plnk;
plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, &plink);
if (!plnk)
return FALSE;
printf ("%d %d\n", plink->parent, plnk->id);
return TRUE;
}
static gboolean
do_vlan_set_ingress_map (char **argv)
{
int ifindex = parse_ifindex (*argv++);
int from = strtol (*argv++, NULL, 10);
int to = strtol (*argv++, NULL, 10);
return nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, from, to);
}
static gboolean
do_vlan_set_egress_map (char **argv)
{
int ifindex = parse_ifindex (*argv++);
int from = strtol (*argv++, NULL, 10);
int to = strtol (*argv++, NULL, 10);
return nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, from, to);
}
static gboolean
do_tun_get_properties (char **argv)
{
int ifindex = parse_ifindex (*argv++);
NMPlatformTunProperties props;
if (!nm_platform_tun_get_properties (NM_PLATFORM_GET, ifindex, &props))
return FALSE;
printf ("mode: %s\n", props.mode);
if (props.owner == -1)
printf ("owner: none\n");
else
printf ("owner: %lu\n", (gulong) props.owner);
if (props.group == -1)
printf ("group: none\n");
else
printf ("group: %lu\n", (gulong) props.group);
printf ("no-pi: ");
print_boolean (props.no_pi);
printf ("vnet-hdr: ");
print_boolean (props.vnet_hdr);
printf ("multi-queue: ");
print_boolean (props.multi_queue);
return TRUE;
}
static gboolean
do_macvlan_get_properties (char **argv)
{
int ifindex = parse_ifindex (*argv++);
const NMPlatformLink *plink;
const NMPlatformLnkMacvlan *props;
props = nm_platform_link_get_lnk_macvlan (NM_PLATFORM_GET, ifindex, &plink);
if (!props)
return FALSE;
printf ("parent: %d\n", plink->parent);
printf ("mode: %s\n", props->mode);
printf ("no-promisc: ");
print_boolean (props->no_promisc);
return TRUE;
}
static gboolean
do_vxlan_get_properties (char **argv)
{
int ifindex = parse_ifindex (*argv++);
const NMPlatformLnkVxlan *props;
char addrstr[INET6_ADDRSTRLEN];
props = nm_platform_link_get_lnk_vxlan (NM_PLATFORM_GET, ifindex, NULL);
if (props)
return FALSE;
printf ("parent-ifindex: %u\n", props->parent_ifindex);
printf ("id: %u\n", props->id);
if (props->group)
inet_ntop (AF_INET, &props->group, addrstr, sizeof (addrstr));
else if (props->group6.s6_addr[0])
inet_ntop (AF_INET6, &props->group6, addrstr, sizeof (addrstr));
else
strcpy (addrstr, "-");
printf ("group: %s\n", addrstr);
if (props->local)
inet_ntop (AF_INET, &props->local, addrstr, sizeof (addrstr));
else if (props->local6.s6_addr[0])
inet_ntop (AF_INET6, &props->local6, addrstr, sizeof (addrstr));
else
strcpy (addrstr, "-");
printf ("local: %s\n", addrstr);
printf ("tos: %u\n", props->tos);
printf ("ttl: %u\n", props->ttl);
printf ("learning: ");
print_boolean (props->learning);
printf ("ageing: %u\n", props->ageing);
printf ("limit: %u\n", props->limit);
printf ("dst-port: %u\n", props->dst_port);
printf ("src-port-min: %u\n", props->src_port_min);
printf ("src-port-max: %u\n", props->src_port_max);
printf ("proxy: ");
print_boolean (props->proxy);
printf ("rsc: ");
print_boolean (props->rsc);
printf ("l2miss: ");
print_boolean (props->l2miss);
printf ("l3miss: ");
print_boolean (props->l3miss);
return TRUE;
}
static gboolean
do_gre_get_properties (char **argv)
{
int ifindex = parse_ifindex (*argv++);
const NMPlatformLnkGre *props;
char addrstr[INET_ADDRSTRLEN];
props = nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, ifindex, NULL);
if (!props)
return FALSE;
printf ("parent-ifindex: %u\n", props->parent_ifindex);
printf ("input-flags: %u\n", props->input_flags);
printf ("output-flags: %u\n", props->input_flags);
printf ("input-key: %u\n", props->input_key);
printf ("output-key: %u\n", props->output_key);
if (props->local)
inet_ntop (AF_INET, &props->local, addrstr, sizeof (addrstr));
else
strcpy (addrstr, "-");
printf ("local: %s\n", addrstr);
if (props->remote)
inet_ntop (AF_INET, &props->remote, addrstr, sizeof (addrstr));
else
strcpy (addrstr, "-");
printf ("remote: %s\n", addrstr);
printf ("ttl: %u\n", props->ttl);
printf ("tos: %u\n", props->tos);
printf ("path-mtu-discovery: ");
print_boolean (props->path_mtu_discovery);
return TRUE;
}
static gboolean
do_ip4_address_get_all (char **argv)
{
int ifindex = parse_ifindex (argv[0]);
GArray *addresses;
NMPlatformIP4Address *address;
int i;
if (ifindex) {
addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex);
for (i = 0; i < addresses->len; i++) {
address = &g_array_index (addresses, NMPlatformIP4Address, i);
printf ("%s", nm_utils_inet4_ntop (address->address, NULL));
if (address->address != address->peer_address)
printf (" peer %s", nm_utils_inet4_ntop (address->peer_address, NULL));
printf ("/%d\n", address->plen);
}
g_array_unref (addresses);
}
return !!ifindex;
}
static gboolean
do_ip6_address_get_all (char **argv)
{
int ifindex = parse_ifindex (argv[0]);
GArray *addresses;
NMPlatformIP6Address *address;
char addrstr[INET6_ADDRSTRLEN];
int i;
if (ifindex) {
addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex);
for (i = 0; i < addresses->len; i++) {
address = &g_array_index (addresses, NMPlatformIP6Address, i);
inet_ntop (AF_INET6, &address->address, addrstr, sizeof (addrstr));
printf ("%s/%d\n", addrstr, address->plen);
}
g_array_unref (addresses);
}
return !!ifindex;
}
static gboolean
parse_ip_address (int family, char *str, gpointer address, int *plen)
{
char *endptr;
if (plen)
*plen = 0;
if (plen) {
char *ptr = strchr (str, '/');
if (ptr) {
*ptr++ = '\0';
*plen = strtol (ptr, &endptr, 10);
if (*endptr)
ptr = NULL;
}
if (!ptr) {
error ("Bad format of IP address, expected address/plen.\n");
return FALSE;
}
}
if (inet_pton (family, str, address))
return TRUE;
error ("Bad format of IP address, expected address%s.\n", plen ? "/plen" : "");
return FALSE;
}
typedef in_addr_t ip4_t;
typedef struct in6_addr ip6_t;
#define parse_ip4_address(s, a, p) parse_ip_address (AF_INET, s, a, p)
#define parse_ip6_address(s, a, p) parse_ip_address (AF_INET6, s, a, p)
static gboolean
do_ip4_address_add (char **argv)
{
int ifindex = parse_ifindex (*argv++);
ip4_t address;
int plen;
if (ifindex && parse_ip4_address (*argv++, &address, &plen)) {
guint32 lifetime = strtol (*argv++, NULL, 10);
guint32 preferred = strtol (*argv++, NULL, 10);
gboolean value = nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, address, plen, address, lifetime, preferred, NULL);
return value;
} else
return FALSE;
}
static gboolean
do_ip6_address_add (char **argv)
{
int ifindex = parse_ifindex (*argv++);
ip6_t address;
int plen;
if (ifindex && parse_ip6_address (*argv++, &address, &plen)) {
guint32 lifetime = strtol (*argv++, NULL, 10);
guint32 preferred = strtol (*argv++, NULL, 10);
guint flags = 0; /* don't support flags */
gboolean value = nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, address, plen, in6addr_any, lifetime, preferred, flags);
return value;
} else
return FALSE;
}
#define ADDR_CMD_FULL(v, cmdname, print, ...) \
static gboolean \
do_##v##_address_##cmdname (char **argv) \
{ \
int ifindex = parse_ifindex (*argv++); \
v##_t address; \
int plen; \
if (ifindex && parse_##v##_address (*argv++, &address, &plen)) { \
gboolean value = !!nm_platform_##v##_address_##cmdname (NM_PLATFORM_GET, ifindex, address, plen, ##__VA_ARGS__); \
if (print) { \
print_boolean (value); \
return TRUE; \
} else \
return value; \
} else \
return FALSE; \
}
#define ADDR_CMD(cmdname, ...) ADDR_CMD_FULL (ip4, cmdname, FALSE, address, ##__VA_ARGS__) ADDR_CMD_FULL (ip6, cmdname, FALSE)
#define ADDR_CMD_PRINT(cmdname, ...) ADDR_CMD_FULL (ip4, cmdname, TRUE, ##__VA_ARGS__) ADDR_CMD_FULL (ip6, cmdname, TRUE)
ADDR_CMD (delete)
ADDR_CMD_PRINT (get, address)
static gboolean
do_ip4_route_get_all (char **argv)
{
int ifindex = parse_ifindex (argv[0]);
GArray *routes;
NMPlatformIP4Route *route;
char networkstr[INET_ADDRSTRLEN], gatewaystr[INET_ADDRSTRLEN];
int i;
if (ifindex) {
routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT);
for (i = 0; i < routes->len; i++) {
route = &g_array_index (routes, NMPlatformIP4Route, i);
inet_ntop (AF_INET, &route->network, networkstr, sizeof (networkstr));
inet_ntop (AF_INET, &route->gateway, gatewaystr, sizeof (gatewaystr));
printf ("%s/%d via %s metric %d\n",
networkstr, route->plen, gatewaystr, route->metric);
}
g_array_unref (routes);
}
return !!ifindex;
}
static gboolean
do_ip6_route_get_all (char **argv)
{
int ifindex = parse_ifindex (argv[0]);
GArray *routes;
NMPlatformIP6Route *route;
char networkstr[INET6_ADDRSTRLEN], gatewaystr[INET6_ADDRSTRLEN];
int i;
if (ifindex) {
routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT);
for (i = 0; i < routes->len; i++) {
route = &g_array_index (routes, NMPlatformIP6Route, i);
inet_ntop (AF_INET6, &route->network, networkstr, sizeof (networkstr));
inet_ntop (AF_INET6, &route->gateway, gatewaystr, sizeof (gatewaystr));
printf ("%s/%d via %s metric %d\n",
networkstr, route->plen, gatewaystr, route->metric);
}
g_array_unref (routes);
}
return !!ifindex;
}
static gboolean
do_ip4_route_add (char **argv)
{
int ifindex = parse_ifindex (*argv++);
in_addr_t network, gateway;
int plen, metric, mss;
parse_ip4_address (*argv++, &network, &plen);
parse_ip4_address (*argv++, &gateway, NULL);
metric = strtol (*argv++, NULL, 10);
mss = strtol (*argv++, NULL, 10);
return nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER,
network, plen, gateway, 0,
metric, mss);
}
static gboolean
do_ip6_route_add (char **argv)
{
int ifindex = parse_ifindex (*argv++);
struct in6_addr network, gateway;
int plen, metric, mss;
parse_ip6_address (*argv++, &network, &plen);
parse_ip6_address (*argv++, &gateway, NULL);
metric = strtol (*argv++, NULL, 10);
mss = strtol (*argv++, NULL, 10);
return nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER,
network, plen, gateway,
metric, mss);
}
static gboolean
do_ip4_route_delete (char **argv)
{
int ifindex = parse_ifindex (*argv++);
in_addr_t network;
int plen, metric;
parse_ip4_address (*argv++, &network, &plen);
metric = strtol (*argv++, NULL, 10);
return nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric);
}
static gboolean
do_ip6_route_delete (char **argv)
{
int ifindex = parse_ifindex (*argv++);
struct in6_addr network;
int plen, metric;
parse_ip6_address (*argv++, &network, &plen);
metric = strtol (*argv++, NULL, 10);
return nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric);
}
static gboolean
do_ip4_route_get (char **argv)
{
int ifindex = parse_ifindex (*argv++);
in_addr_t network;
int plen, metric;
parse_ip4_address (*argv++, &network, &plen);
metric = strtol (*argv++, NULL, 10);
print_boolean (!!nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric));
return TRUE;
}
static gboolean
do_ip6_route_get (char **argv)
{
int ifindex = parse_ifindex (*argv++);
struct in6_addr network;
int plen, metric;
parse_ip6_address (*argv++, &network, &plen);
metric = strtol (*argv++, NULL, 10);
print_boolean (!!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric));
return TRUE;
}
typedef struct {
const char *name;
const char *help;
int (*handler) (char **argv);
int argc;
const char *arghelp;
} command_t;
static const command_t commands[] = {
{ "sysctl-set", "get /proc/sys or /sys value", do_sysctl_set, 2, "<path> <value>" },
{ "sysctl-get", "get /proc/sys or /sys value", do_sysctl_get, 1, "<value>" },
{ "link-get-all", "print all links", do_link_get_all, 0, "" },
{ "dummy-add", "add dummy interface", do_dummy_add, 1, "<ifname>" },
{ "bridge-add", "add bridge interface", do_bridge_add, 1, "<ifname>" },
{ "bond-add", "add bond interface", do_bond_add, 1, "<ifname>" },
{ "team-add", "add team interface", do_team_add, 1, "<ifname>" },
{ "vlan-add", "add vlan interface", do_vlan_add, 4, "<ifname> <parent> <vlanid> <vlanflags>" },
{ "link-exists", "check ifname for existance", do_link_exists, 1, "<ifname>" },
{ "link-delete", "delete interface", do_link_delete, 1, "<ifname/ifindex>" },
{ "link-get-ifindex>", "get interface index", do_link_get_ifindex, 1, "<ifname>" },
{ "link-get-name", "get interface name", do_link_get_name, 1, "<ifindex>" },
{ "link-get-type", "get interface type", do_link_get_type, 1, "<ifname/ifindex>" },
{ "link-is-software", "check if interface is a software one", do_link_is_software, 1, "<ifname/ifindex>" },
{ "link-supports-slaves", "check if interface supports slaves", do_link_supports_slaves, 1, "<ifname/ifindex>" },
{ "link-set-up", "set interface up", do_link_set_up, 1, "<ifname/ifindex>" },
{ "link-set-down", "set interface down", do_link_set_down, 1, "<ifname/ifindex>" },
{ "link-set-arp", "activate interface arp", do_link_set_arp, 1, "<ifname/ifindex>" },
{ "link-set-noarp", "deactivate interface arp", do_link_set_noarp, 1, "<ifname/ifindex>" },
{ "link-is-up", "check if interface is up", do_link_is_up, 1, "<ifname/ifindex>" },
{ "link-is-connected", "check interface carrier", do_link_is_connected, 1, "<ifname/ifindex>" },
{ "link-uses-arp", "check whether interface uses arp", do_link_uses_arp, 1, "<ifname/ifindex>" },
{ "link-get-address", "print link address", do_link_get_address, 1, "<ifname/ifindex>" },
{ "link-set-address", "set link address", do_link_set_address, 2, "<ifname/ifindex> <hex>" },
{ "link-get-mtu", "print link mtu", do_link_get_mtu, 1, "<ifname/ifindex>" },
{ "link-set-mtu", "set link mtu", do_link_set_mtu, 2, "<ifname/ifindex> <mtu>" },
{ "link-supports-carrier-detect", "check whether interface supports carrier detect",
do_link_supports_carrier_detect, 1, "<ifname/ifindex>" },
{ "link-supports-vlans", "check whether interface supports VLANs",
do_link_supports_vlans, 1, "<ifname/ifindex>" },
{ "link-enslave", "enslave slave interface with master", do_link_enslave, 2, "<master> <slave>" },
{ "link-release", "release save interface from master", do_link_release, 2, "<master> <slave>" },
{ "link-get-master", "print master interface of a slave", do_link_get_master, 1, "<ifname/ifindex>" },
{ "link-master-set-option", "set master option", do_master_set_option, 3,
"<ifname/ifindex> <option> <value>" },
{ "link-master-get-option", "get master option", do_master_get_option, 2,
"<ifname/ifindex> <option>" },
{ "link-slave-set-option", "set slave option", do_slave_set_option, 3,
"<ifname/ifindex> <option>" },
{ "link-slave-get-option", "get slave option", do_slave_get_option, 2,
"<ifname/ifindex> <option>" },
{ "vlan-get-info", "get vlan info", do_vlan_get_info, 1, "<ifname/ifindex>" },
{ "vlan-set-ingress-map", "set vlan ingress map", do_vlan_set_ingress_map, 3,
"<ifname/ifindex> <from> <to>" },
{ "vlan-set-egress-map", "set vlan egress map", do_vlan_set_egress_map, 3,
"<ifname/ifindex> <from> <to>" },
{ "tun-get-properties", "get tun/tap properties", do_tun_get_properties, 1,
"<ifname/ifindex>" },
{ "macvlan-get-properties", "get macvlan properties", do_macvlan_get_properties, 1,
"<ifname/ifindex>" },
{ "vxlan-get-properties", "get vxlan properties", do_vxlan_get_properties, 1,
"<ifname/ifindex>" },
{ "gre-get-properties", "get gre properties", do_gre_get_properties, 1,
"<ifname/ifindex>" },
{ "ip4-address-get-all", "print all IPv4 addresses", do_ip4_address_get_all, 1, "<ifname/ifindex>" },
{ "ip6-address-get-all", "print all IPv6 addresses", do_ip6_address_get_all, 1, "<ifname/ifindex>" },
{ "ip4-address-add", "add IPv4 address", do_ip4_address_add, 4, "<ifname/ifindex> <address>/<plen> <lifetime> <>" },
{ "ip6-address-add", "add IPv6 address", do_ip6_address_add, 4, "<ifname/ifindex> <address>/<plen> <lifetime> [<flags>] <>" },
{ "ip4-address-delete", "delete IPv4 address", do_ip4_address_delete, 2,
"<ifname/ifindex> <address>/<plen>" },
{ "ip6-address-delete", "delete IPv6 address", do_ip6_address_delete, 2,
"<ifname/ifindex> <address>/<plen>" },
{ "ip4-address-exists", "check for existence of IPv4 address", do_ip4_address_get, 2,
"<ifname/ifindex> <address>/<plen>" },
{ "ip6-address-exists", "check for existence of IPv6 address", do_ip6_address_get, 2,
"<ifname/ifindex> <address>/<plen>" },
{ "ip4-route-get-all", "print all IPv4 routes", do_ip4_route_get_all, 1, "<ifname/ifindex>" },
{ "ip6-route-get-all", "print all IPv6 routes", do_ip6_route_get_all, 1, "<ifname/ifindex>" },
{ "ip4-route-add", "add IPv4 route", do_ip4_route_add, 5,
"<ifname/ifindex> <network>/<plen> <gateway> <metric> <mss>" },
{ "ip6-route-add", "add IPv6 route", do_ip6_route_add, 5,
"<ifname/ifindex> <network>/<plen> <gateway> <metric> <mss>" },
{ "ip4-route-delete", "delete IPv4 route", do_ip4_route_delete, 3,
"<ifname/ifindex> <network>/<plen> <metric>" },
{ "ip6-route-delete", "delete IPv6 route", do_ip6_route_delete, 3,
"<ifname/ifindex> <network>/<plen> <metric>" },
{ "ip4-route-exists", "check for existence of IPv4 route", do_ip4_route_get, 3,
"<ifname/ifindex> <network>/<plen> <metric>" },
{ "ip6-route-exists", "check for existence of IPv6 route", do_ip6_route_get, 3,
"<ifname/ifindex> <network>/<plen> <metric>" },
{ NULL, NULL, NULL, 0, NULL },
};
int
main (int argc, char **argv)
{
const char *arg0 = *argv++;
const command_t *command = NULL;
gboolean status = TRUE;
nm_g_type_init ();
if (*argv && !g_strcmp0 (argv[1], "--fake")) {
nm_fake_platform_setup ();
} else
nm_linux_platform_setup ();
if (*argv)
for (command = commands; command->name; command++)
if (g_str_has_prefix (command->name, *argv))
break;
if (command && command->name) {
argv++;
if (g_strv_length (argv) == command->argc)
status = command->handler (argv);
else {
error ("Wrong number of arguments to '%s' (expected %d).\n\nUsage: %s %s %s\n-- %s\n",
command->name, command->argc,
arg0, command->name, command->arghelp, command->help);
return EXIT_FAILURE;
}
} else {
error ("Usage: %s COMMAND\n\n", arg0);
error ("COMMAND\n");
for (command = commands; command->name; command++)
error (" %s %s\n -- %s\n", command->name, command->arghelp, command->help);
error ("\n");
}
return EXIT_SUCCESS;
}

View file

@ -16,7 +16,7 @@ static int EX = -1;
/*****************************************************************************/
static void
ip4_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformIP4Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data)
ip4_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformIP4Address *received, NMPlatformSignalChangeType change_type, SignalData *data)
{
g_assert (received);
g_assert_cmpint (received->ifindex, ==, ifindex);
@ -36,7 +36,7 @@ ip4_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex,
}
static void
ip6_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformIP6Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data)
ip6_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformIP6Address *received, NMPlatformSignalChangeType change_type, SignalData *data)
{
g_assert (received);
g_assert_cmpint (received->ifindex, ==, ifindex);

View file

@ -2,6 +2,8 @@
#include <sys/mount.h>
#include <sched.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "test-common.h"
@ -107,7 +109,7 @@ _free_signal (const char *file, int line, const char *func, SignalData *data)
}
void
link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data)
link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, SignalData *data)
{
GArray *links;
NMPlatformLink *cached;
@ -297,7 +299,6 @@ _wait_for_signal_cb (NMPlatform *platform,
int ifindex,
NMPlatformLink *plink,
NMPlatformSignalChangeType change_type,
NMPlatformReason reason,
gpointer user_data)
{
WaitForSignalData *data = user_data;
@ -367,13 +368,13 @@ nmtstp_wait_for_signal_until (gint64 until_ms)
}
const NMPlatformLink *
nmtstp_wait_for_link (const char *ifname, guint timeout_ms)
nmtstp_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms)
{
return nmtstp_wait_for_link_until (ifname, nm_utils_get_monotonic_timestamp_ms () + timeout_ms);
return nmtstp_wait_for_link_until (ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms);
}
const NMPlatformLink *
nmtstp_wait_for_link_until (const char *ifname, gint64 until_ms)
nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms)
{
const NMPlatformLink *plink;
gint64 now;
@ -382,7 +383,8 @@ nmtstp_wait_for_link_until (const char *ifname, gint64 until_ms)
now = nm_utils_get_monotonic_timestamp_ms ();
plink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname);
if (plink)
if ( plink
&& (expected_link_type == NM_LINK_TYPE_NONE || plink->type == expected_link_type))
return plink;
if (until_ms < now)
@ -403,10 +405,8 @@ nmtstp_assert_wait_for_link_until (const char *ifname, NMLinkType expected_link_
{
const NMPlatformLink *plink;
plink = nmtstp_wait_for_link_until (ifname, until_ms);
plink = nmtstp_wait_for_link_until (ifname, expected_link_type, until_ms);
g_assert (plink);
if (expected_link_type != NM_LINK_TYPE_NONE)
g_assert_cmpint (plink->type, ==, expected_link_type);
return plink;
}
@ -880,6 +880,169 @@ nmtstp_link_set_updown (gboolean external_command,
/*****************************************************************************/
struct _NMTstpNamespaceHandle {
pid_t pid;
int pipe_fd;
};
NMTstpNamespaceHandle *
nmtstp_namespace_create (int unshare_flags, GError **error)
{
NMTstpNamespaceHandle *ns_handle;
int e;
int errsv;
pid_t pid, pid2;
int pipefd_c2p[2];
int pipefd_p2c[2];
ssize_t r;
e = pipe (pipefd_c2p);
if (e != 0) {
errsv = errno;
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
"pipe() failed with %d (%s)", errsv, strerror (errsv));
return FALSE;
}
e = pipe (pipefd_p2c);
if (e != 0) {
errsv = errno;
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
"pipe() failed with %d (%s)", errsv, strerror (errsv));
close (pipefd_c2p[0]);
close (pipefd_c2p[1]);
return FALSE;
}
pid = fork ();
if (pid < 0) {
errsv = errno;
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
"fork() failed with %d (%s)", errsv, strerror (errsv));
close (pipefd_c2p[0]);
close (pipefd_c2p[1]);
close (pipefd_p2c[0]);
close (pipefd_p2c[1]);
return FALSE;
}
if (pid == 0) {
char read_buf[1];
close (pipefd_c2p[0]); /* close read-end */
close (pipefd_p2c[1]); /* close write-end */
if (unshare (unshare_flags) != 0) {
errsv = errno;
if (errsv == 0)
errsv = -1;
} else
errsv = 0;
/* sync with parent process and send result. */
do {
r = write (pipefd_c2p[1], &errsv, sizeof (errsv));
} while (r < 0 && errno == EINTR);
if (r != sizeof (errsv)) {
errsv = errno;
if (errsv == 0)
errsv = -2;
}
close (pipefd_c2p[1]);
/* wait until parent process terminates (or kills us). */
if (errsv == 0) {
do {
r = read (pipefd_p2c[0], read_buf, sizeof (read_buf));
} while (r < 0 && errno == EINTR);
}
close (pipefd_p2c[0]);
_exit (0);
}
close (pipefd_c2p[1]); /* close write-end */
close (pipefd_p2c[0]); /* close read-end */
/* sync with child process. */
do {
r = read (pipefd_c2p[0], &errsv, sizeof (errsv));
} while (r < 0 && errno == EINTR);
close (pipefd_c2p[0]);
if ( r != sizeof (errsv)
|| errsv != 0) {
int status;
if (r != sizeof (errsv)) {
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
"child process failed for unknown reason");
} else {
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
"child process signaled failure %d (%s)", errsv, strerror (errsv));
}
close (pipefd_p2c[1]);
kill (pid, SIGKILL);
do {
pid2 = waitpid (pid, &status, 0);
} while (pid2 == -1 && errno == EINTR);
return FALSE;
}
ns_handle = g_new0 (NMTstpNamespaceHandle, 1);
ns_handle->pid = pid;
ns_handle->pipe_fd = pipefd_p2c[1];
return ns_handle;
}
pid_t
nmtstp_namespace_handle_get_pid (NMTstpNamespaceHandle *ns_handle)
{
g_return_val_if_fail (ns_handle, 0);
g_return_val_if_fail (ns_handle->pid > 0, 0);
return ns_handle->pid;
}
void
nmtstp_namespace_handle_release (NMTstpNamespaceHandle *ns_handle)
{
pid_t pid;
int status;
if (!ns_handle)
return;
g_return_if_fail (ns_handle->pid > 0);
close (ns_handle->pipe_fd);
ns_handle->pipe_fd = 0;
kill (ns_handle->pid, SIGKILL);
do {
pid = waitpid (ns_handle->pid, &status, 0);
} while (pid == -1 && errno == EINTR);
ns_handle->pid = 0;
g_free (ns_handle);
}
int
nmtstp_namespace_get_fd_for_process (pid_t pid, const char *ns_name)
{
char p[1000];
g_return_val_if_fail (pid > 0, 0);
g_return_val_if_fail (ns_name && ns_name[0] && strlen (ns_name) < 50, 0);
nm_sprintf_buf (p, "/proc/%lu/ns/%s", (long unsigned) pid, ns_name);
return open(p, O_RDONLY);
}
/*****************************************************************************/
NMTST_DEFINE();
static gboolean

View file

@ -46,6 +46,19 @@ typedef struct {
gboolean nmtstp_is_root_test (void);
gboolean nmtstp_is_sysfs_writable (void);
/******************************************************************************/
typedef struct _NMTstpNamespaceHandle NMTstpNamespaceHandle;
NMTstpNamespaceHandle *nmtstp_namespace_create (int flags, GError **error);
void nmtstp_namespace_handle_release (NMTstpNamespaceHandle *handle);
pid_t nmtstp_namespace_handle_get_pid (NMTstpNamespaceHandle *handle);
int nmtstp_namespace_get_fd_for_process (pid_t pid, const char *ns_name);
/******************************************************************************/
SignalData *add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCallback callback, int ifindex, const char *ifname);
#define add_signal(name, change_type, callback) add_signal_full (name, change_type, (GCallback) callback, 0, NULL)
#define add_signal_ifindex(name, change_type, callback, ifindex) add_signal_full (name, change_type, (GCallback) callback, ifindex, NULL)
@ -68,15 +81,15 @@ gboolean ip4_route_exists (const char *ifname, guint32 network, int plen, guint3
void _assert_ip4_route_exists (const char *file, guint line, const char *func, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric);
#define assert_ip4_route_exists(exists, ifname, network, plen, metric) _assert_ip4_route_exists (__FILE__, __LINE__, G_STRFUNC, exists, ifname, network, plen, metric)
void link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data);
void link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, SignalData *data);
int nmtstp_run_command (const char *format, ...) __attribute__((__format__ (__printf__, 1, 2)));
#define nmtstp_run_command_check(format, ...) do { g_assert_cmpint (nmtstp_run_command (format, __VA_ARGS__), ==, 0); } while (0)
#define nmtstp_run_command_check(...) do { g_assert_cmpint (nmtstp_run_command (__VA_ARGS__), ==, 0); } while (0)
gboolean nmtstp_wait_for_signal (guint timeout_ms);
gboolean nmtstp_wait_for_signal_until (gint64 until_ms);
const NMPlatformLink *nmtstp_wait_for_link (const char *ifname, guint timeout_ms);
const NMPlatformLink *nmtstp_wait_for_link_until (const char *ifname, gint64 until_ms);
const NMPlatformLink *nmtstp_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms);
const NMPlatformLink *nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms);
const NMPlatformLink *nmtstp_assert_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms);
const NMPlatformLink *nmtstp_assert_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms);

View file

@ -1,5 +1,7 @@
#include "config.h"
#include <sched.h>
#include "nmp-object.h"
#include "test-common.h"
@ -136,7 +138,6 @@ test_link_changed_signal_cb (NMPlatform *platform,
int ifindex,
const NMPlatformIP4Route *route,
NMPlatformSignalChangeType change_type,
NMPlatformReason reason,
gboolean *p_test_link_changed_signal_arg)
{
/* test invocation of platform signals with multiple listeners
@ -159,9 +160,6 @@ test_link_changed_signal_cb (NMPlatform *platform,
g_assert_cmpint ((gint64) change_type, !=, (gint64) 0);
g_assert_cmpint (change_type, !=, NM_PLATFORM_SIGNAL_NONE);
g_assert_cmpint ((gint64) reason, !=, (gint64) 0);
g_assert_cmpint (reason, !=, NM_PLATFORM_REASON_NONE);
*p_test_link_changed_signal_arg = TRUE;
}
@ -287,10 +285,14 @@ test_slave (int master, int type, SignalData *master_changed)
}
/* Release */
ensure_no_signal (link_added);
ensure_no_signal (link_changed);
ensure_no_signal (link_removed);
g_assert (nm_platform_link_release (NM_PLATFORM_GET, master, ifindex));
g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, 0);
accept_signals (link_added, 0, 1);
accept_signals (link_changed, 1, 3);
accept_signals (link_removed, 0, 1);
accept_signals (master_changed, 1, 2);
ensure_no_signal (master_changed);
@ -302,7 +304,9 @@ test_slave (int master, int type, SignalData *master_changed)
ensure_no_signal (master_changed);
/* Remove */
ensure_no_signal (link_added);
ensure_no_signal (link_changed);
ensure_no_signal (link_removed);
g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex));
accept_signals (master_changed, 0, 1);
accept_signals (link_changed, 0, 1);
@ -1371,6 +1375,181 @@ test_vlan_set_xgress (void)
/*****************************************************************************/
static void
test_nl_bugs_veth (void)
{
const char *IFACE_VETH0 = "nm-test-veth0";
const char *IFACE_VETH1 = "nm-test-veth1";
int ifindex_veth0, ifindex_veth1;
int i;
const NMPlatformLink *pllink_veth0, *pllink_veth1;
gs_free_error GError *error = NULL;
NMTstpNamespaceHandle *ns_handle = NULL;
/* create veth pair. */
nmtstp_run_command_check ("ip link add dev %s type veth peer name %s", IFACE_VETH0, IFACE_VETH1);
ifindex_veth0 = nmtstp_assert_wait_for_link (IFACE_VETH0, NM_LINK_TYPE_VETH, 100)->ifindex;
ifindex_veth1 = nmtstp_assert_wait_for_link (IFACE_VETH1, NM_LINK_TYPE_VETH, 100)->ifindex;
/* assert that nm_platform_veth_get_properties() returns the expected peer ifindexes. */
g_assert (nm_platform_veth_get_properties (NM_PLATFORM_GET, ifindex_veth0, &i));
g_assert_cmpint (i, ==, ifindex_veth1);
g_assert (nm_platform_veth_get_properties (NM_PLATFORM_GET, ifindex_veth1, &i));
g_assert_cmpint (i, ==, ifindex_veth0);
/* assert that NMPlatformLink.parent is the peer-ifindex. */
pllink_veth0 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth0);
g_assert (pllink_veth0);
if (pllink_veth0->parent == 0) {
/* pre-4.1 kernels don't support exposing the veth peer as IFA_LINK. skip the remainder
* of the test. */
goto out;
}
g_assert_cmpint (pllink_veth0->parent, ==, ifindex_veth1);
/* The following tests whether we have a workaround for kernel bug
* https://bugzilla.redhat.com/show_bug.cgi?id=1285827 in place. */
pllink_veth1 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth1);
g_assert (pllink_veth1);
g_assert_cmpint (pllink_veth1->parent, ==, ifindex_veth0);
/* move one veth peer to another namespace and check that the
* parent/IFLA_LINK of the remaining peer properly updates
* (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */
ns_handle = nmtstp_namespace_create (CLONE_NEWNET, &error);
g_assert_no_error (error);
g_assert (ns_handle);
nmtstp_run_command_check ("ip link set %s netns %ld", IFACE_VETH1, (long) nmtstp_namespace_handle_get_pid (ns_handle));
NMTST_WAIT_ASSERT (100, {
nmtstp_wait_for_signal (50);
nm_platform_process_events (NM_PLATFORM_GET);
pllink_veth1 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth1);
pllink_veth0 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth0);
if ( !pllink_veth1
&& pllink_veth0
&& pllink_veth0->parent == NM_PLATFORM_LINK_OTHER_NETNS) {
break;
}
});
out:
nm_platform_link_delete (NM_PLATFORM_GET, ifindex_veth0);
nm_platform_link_delete (NM_PLATFORM_GET, ifindex_veth1);
nmtstp_namespace_handle_release (ns_handle);
}
/*****************************************************************************/
static void
test_nl_bugs_spuroius_newlink (void)
{
const char *IFACE_BOND0 = "nm-test-bond0";
const char *IFACE_DUMMY0 = "nm-test-dummy0";
int ifindex_bond0, ifindex_dummy0;
const NMPlatformLink *pllink;
gboolean wait_for_settle;
/* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */
nmtstp_run_command_check ("ip link add %s type dummy", IFACE_DUMMY0);
ifindex_dummy0 = nmtstp_assert_wait_for_link (IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex;
nmtstp_run_command_check ("ip link add %s type bond", IFACE_BOND0);
ifindex_bond0 = nmtstp_assert_wait_for_link (IFACE_BOND0, NM_LINK_TYPE_BOND, 100)->ifindex;
nmtstp_link_set_updown (-1, ifindex_bond0, TRUE);
nmtstp_run_command_check ("ip link set %s master %s", IFACE_DUMMY0, IFACE_BOND0);
NMTST_WAIT_ASSERT (100, {
nmtstp_wait_for_signal (50);
pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0);
g_assert (pllink);
if (pllink->master == ifindex_bond0)
break;
});
nmtstp_run_command_check ("ip link del %s", IFACE_BOND0);
wait_for_settle = TRUE;
nmtstp_wait_for_signal (50);
again:
nm_platform_process_events (NM_PLATFORM_GET);
pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_bond0);
g_assert (!pllink);
if (wait_for_settle) {
wait_for_settle = FALSE;
NMTST_WAIT (300, { nmtstp_wait_for_signal (50); });
goto again;
}
nm_platform_link_delete (NM_PLATFORM_GET, ifindex_bond0);
nm_platform_link_delete (NM_PLATFORM_GET, ifindex_dummy0);
}
/*****************************************************************************/
static void
test_nl_bugs_spuroius_dellink (void)
{
const char *IFACE_BRIDGE0 = "nm-test-bridge0";
const char *IFACE_DUMMY0 = "nm-test-dummy0";
int ifindex_bridge0, ifindex_dummy0;
const NMPlatformLink *pllink;
gboolean wait_for_settle;
/* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */
nmtstp_run_command_check ("ip link add %s type dummy", IFACE_DUMMY0);
ifindex_dummy0 = nmtstp_assert_wait_for_link (IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex;
nmtstp_run_command_check ("ip link add %s type bridge", IFACE_BRIDGE0);
ifindex_bridge0 = nmtstp_assert_wait_for_link (IFACE_BRIDGE0, NM_LINK_TYPE_BRIDGE, 100)->ifindex;
nmtstp_link_set_updown (-1, ifindex_bridge0, TRUE);
nmtstp_run_command_check ("ip link set %s master %s", IFACE_DUMMY0, IFACE_BRIDGE0);
NMTST_WAIT_ASSERT (100, {
nmtstp_wait_for_signal (50);
pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0);
g_assert (pllink);
if (pllink->master == ifindex_bridge0)
break;
});
nm_platform_process_events (NM_PLATFORM_GET);
nmtstp_run_command_check ("ip link set %s nomaster", IFACE_DUMMY0);
wait_for_settle = TRUE;
nmtstp_wait_for_signal (50);
again:
nm_platform_process_events (NM_PLATFORM_GET);
pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_bridge0);
g_assert (pllink);
pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0);
g_assert (pllink);
g_assert_cmpint (pllink->parent, ==, 0);
if (wait_for_settle) {
wait_for_settle = FALSE;
NMTST_WAIT (300, { nmtstp_wait_for_signal (50); });
goto again;
}
nm_platform_link_delete (NM_PLATFORM_GET, ifindex_bridge0);
nm_platform_link_delete (NM_PLATFORM_GET, ifindex_dummy0);
}
/*****************************************************************************/
void
init_tests (int *argc, char ***argv)
{
@ -1406,5 +1585,9 @@ setup_tests (void)
test_software_detect_add ("/link/software/detect/vxlan/1", NM_LINK_TYPE_VXLAN, 1);
g_test_add_func ("/link/software/vlan/set-xgress", test_vlan_set_xgress);
g_test_add_func ("/link/nl-bugs/veth", test_nl_bugs_veth);
g_test_add_func ("/link/nl-bugs/spurious-newlink", test_nl_bugs_spuroius_newlink);
g_test_add_func ("/link/nl-bugs/spurious-dellink", test_nl_bugs_spuroius_dellink);
}
}

View file

@ -9,7 +9,7 @@
#define DEVICE_NAME "nm-test-device"
static void
ip4_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const NMPlatformIP4Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data)
ip4_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const NMPlatformIP4Route *received, NMPlatformSignalChangeType change_type, SignalData *data)
{
g_assert (received);
g_assert_cmpint (received->ifindex, ==, ifindex);
@ -29,7 +29,7 @@ ip4_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, c
}
static void
ip6_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const NMPlatformIP6Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data)
ip6_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const NMPlatformIP6Route *received, NMPlatformSignalChangeType change_type, SignalData *data)
{
g_assert (received);
g_assert_cmpint (received->ifindex, ==, ifindex);

View file

@ -108,7 +108,7 @@ devtimeout_ready (gpointer user_data)
static void
link_changed (NMPlatform *platform, NMPObjectType *obj_type, int ifindex, const NMPlatformLink *link,
NMPlatformSignalChangeType change_type, NMPlatformReason reason,
NMPlatformSignalChangeType change_type,
NMConnection *self)
{
NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self);