diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 0899680ea..709316338 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -30,6 +30,8 @@ typedef struct { GArray *links; + GArray *ip4_addresses; + GArray *ip6_addresses; } NMFakePlatformPrivate; #define NM_FAKE_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_FAKE_PLATFORM, NMFakePlatformPrivate)) @@ -266,12 +268,185 @@ link_uses_arp (NMPlatform *platform, int ifindex) /******************************************************************/ +static GArray * +ip4_address_get_all (NMPlatform *platform, int ifindex) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + GArray *addresses; + NMPlatformIP4Address *address; + int count = 0, i; + + /* Count addresses */ + for (i = 0; i < priv->ip4_addresses->len; i++) { + address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); + if (address && address->ifindex == ifindex) + count++; + } + + addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP4Address), count); + + /* Fill addresses */ + for (i = 0; i < priv->ip4_addresses->len; i++) { + address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); + if (address && address->ifindex == ifindex) + g_array_append_val (addresses, *address); + } + + return addresses; +} + +static GArray * +ip6_address_get_all (NMPlatform *platform, int ifindex) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + GArray *addresses; + NMPlatformIP6Address *address; + int count = 0, i; + + /* Count addresses */ + for (i = 0; i < priv->ip6_addresses->len; i++) { + address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); + if (address && address->ifindex == ifindex) + count++; + } + + addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP6Address), count); + + /* Fill addresses */ + count = 0; + for (i = 0; i < priv->ip6_addresses->len; i++) { + address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); + if (address && address->ifindex == ifindex) + g_array_append_val (addresses, *address); + } + + return addresses; +} + +static gboolean +ip4_address_add (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + NMPlatformIP4Address address; + + memset (&address, 0, sizeof (address)); + address.ifindex = ifindex; + address.address = addr; + address.plen = plen; + + g_array_append_val (priv->ip4_addresses, address); + + g_signal_emit_by_name (platform, NM_PLATFORM_IP4_ADDRESS_ADDED, &address); + + return TRUE; +} + +static gboolean +ip6_address_add (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + NMPlatformIP6Address address; + + memset (&address, 0, sizeof (address)); + address.ifindex = ifindex; + address.address = addr; + address.plen = plen; + + g_array_append_val (priv->ip6_addresses, address); + + g_signal_emit_by_name (platform, NM_PLATFORM_IP6_ADDRESS_ADDED, &address); + + return TRUE; +} + +static gboolean +ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + int i; + + for (i = 0; i < priv->ip4_addresses->len; i++) { + NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); + + if (address->ifindex == ifindex && address->plen == plen && address->address == addr) { + NMPlatformIP4Address deleted_address; + + memcpy (&deleted_address, address, sizeof (deleted_address)); + memset (address, 0, sizeof (*address)); + g_signal_emit_by_name (platform, NM_PLATFORM_IP4_ADDRESS_REMOVED, &deleted_address); + return TRUE; + } + } + + g_assert_not_reached (); +} + +static gboolean +ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + int i; + + for (i = 0; i < priv->ip6_addresses->len; i++) { + NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); + + if (address->ifindex == ifindex && address->plen == plen + && IN6_ARE_ADDR_EQUAL (&address->address, &addr)) { + NMPlatformIP6Address deleted_address; + + memcpy (&deleted_address, address, sizeof (deleted_address)); + memset (address, 0, sizeof (*address)); + g_signal_emit_by_name (platform, NM_PLATFORM_IP6_ADDRESS_REMOVED, &deleted_address); + return TRUE; + } + } + + g_assert_not_reached (); +} + +static gboolean +ip4_address_exists (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + int i; + + for (i = 0; i < priv->ip4_addresses->len; i++) { + NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); + + if (address->ifindex == ifindex && address->plen == plen && address->address == addr) + return TRUE; + } + + return FALSE; +} + +static gboolean +ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + int i; + + for (i = 0; i < priv->ip6_addresses->len; i++) { + NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); + + if (address->ifindex == ifindex && address->plen == plen && + IN6_ARE_ADDR_EQUAL (&address->address, &addr)) + return TRUE; + } + + return FALSE; +} + +/******************************************************************/ + static void nm_fake_platform_init (NMFakePlatform *fake_platform) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (fake_platform); priv->links = g_array_new (TRUE, TRUE, sizeof (NMPlatformLink)); + priv->ip4_addresses = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Address)); + priv->ip6_addresses = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Address)); } static gboolean @@ -297,6 +472,8 @@ nm_fake_platform_finalize (GObject *object) NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (object); g_array_unref (priv->links); + g_array_unref (priv->ip4_addresses); + g_array_unref (priv->ip6_addresses); G_OBJECT_CLASS (nm_fake_platform_parent_class)->finalize (object); } @@ -328,4 +505,13 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->link_is_up = link_is_up; platform_class->link_is_connected = link_is_connected; platform_class->link_uses_arp = link_uses_arp; + + platform_class->ip4_address_get_all = ip4_address_get_all; + platform_class->ip6_address_get_all = ip6_address_get_all; + platform_class->ip4_address_add = ip4_address_add; + platform_class->ip6_address_add = ip6_address_add; + platform_class->ip4_address_delete = ip4_address_delete; + platform_class->ip6_address_delete = ip6_address_delete; + platform_class->ip4_address_exists = ip4_address_exists; + platform_class->ip6_address_exists = ip6_address_exists; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index b3fab2651..939c8eb9b 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "nm-linux-platform.h" #include "nm-logging.h" @@ -45,6 +46,7 @@ typedef struct { struct nl_sock *nlh; struct nl_sock *nlh_event; struct nl_cache *link_cache; + struct nl_cache *address_cache; GIOChannel *event_channel; guint event_id; } NMLinuxPlatformPrivate; @@ -76,11 +78,39 @@ put_nl_object (void *ptr) } } +#define auto_nl_addr __attribute__((cleanup(put_nl_addr))) +static void +put_nl_addr (void *ptr) +{ + struct nl_addr **object = ptr; + + if (object && *object) { + nl_addr_put (*object); + *object = NULL; + } +} + /* libnl doesn't use const where due */ #define nl_addr_build(family, addr, addrlen) nl_addr_build (family, (gpointer) addr, addrlen) +/* rtnl_addr_set_prefixlen fails to update the nl_addr prefixlen */ +static void +nm_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) +{ + struct nl_addr *nladdr; + + rtnl_addr_set_prefixlen (rtnladdr, plen); + + nladdr = rtnl_addr_get_local (rtnladdr); + if (nladdr) + nl_addr_set_prefixlen (nladdr, plen); +} +#define rtnl_addr_set_prefixlen nm_rtnl_addr_set_prefixlen + typedef enum { LINK, + IP4_ADDRESS, + IP6_ADDRESS, N_TYPES } ObjectType; @@ -98,7 +128,16 @@ object_type_from_nl_object (const struct nl_object *object) if (!strcmp (nl_object_get_type (object), "route/link")) return LINK; - else + else if (!strcmp (nl_object_get_type (object), "route/addr")) { + switch (rtnl_addr_get_family ((struct rtnl_addr *) object)) { + case AF_INET: + return IP4_ADDRESS; + case AF_INET6: + return IP6_ADDRESS; + default: + g_assert_not_reached (); + } + } else g_assert_not_reached (); } @@ -172,6 +211,9 @@ add_kernel_object (struct nl_sock *sock, struct nl_object *object) switch (object_type_from_nl_object (object)) { case LINK: return rtnl_link_add (sock, (struct rtnl_link *) object, NLM_F_CREATE); + case IP4_ADDRESS: + case IP6_ADDRESS: + return rtnl_addr_add (sock, (struct rtnl_addr *) object, NLM_F_CREATE); default: g_assert_not_reached (); } @@ -184,6 +226,9 @@ delete_kernel_object (struct nl_sock *sock, struct nl_object *object) switch (object_type_from_nl_object (object)) { case LINK: return rtnl_link_delete (sock, (struct rtnl_link *) object); + case IP4_ADDRESS: + case IP6_ADDRESS: + return rtnl_addr_delete (sock, (struct rtnl_addr *) object, 0); default: g_assert_not_reached (); } @@ -245,12 +290,42 @@ link_init (NMPlatformLink *info, struct rtnl_link *rtnllink) info->arp = !(rtnl_link_get_flags (rtnllink) & IFF_NOARP); } +static void +init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr) +{ + struct nl_addr *nladdr = rtnl_addr_get_local (rtnladdr); + + g_assert (nladdr); + + memset (address, 0, sizeof (*address)); + + address->ifindex = rtnl_addr_get_ifindex (rtnladdr); + address->plen = rtnl_addr_get_prefixlen (rtnladdr); + g_assert (nl_addr_get_len (nladdr) == sizeof (address->address)); + memcpy (&address->address, nl_addr_get_binary_addr (nladdr), sizeof (address->address)); +} + +static void +init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr) +{ + struct nl_addr *nladdr = rtnl_addr_get_local (rtnladdr); + + memset (address, 0, sizeof (*address)); + + address->ifindex = rtnl_addr_get_ifindex (rtnladdr); + address->plen = rtnl_addr_get_prefixlen (rtnladdr); + g_assert (nl_addr_get_len (nladdr) == sizeof (address->address)); + memcpy (&address->address, nl_addr_get_binary_addr (nladdr), sizeof (address->address)); +} + /******************************************************************/ /* Object and cache manipulation */ static const char *signal_by_type_and_status[N_TYPES][N_STATUSES] = { { NM_PLATFORM_LINK_ADDED, NM_PLATFORM_LINK_CHANGED, NM_PLATFORM_LINK_REMOVED }, + { NM_PLATFORM_IP4_ADDRESS_ADDED, NM_PLATFORM_IP4_ADDRESS_CHANGED, NM_PLATFORM_IP4_ADDRESS_REMOVED }, + { NM_PLATFORM_IP6_ADDRESS_ADDED, NM_PLATFORM_IP6_ADDRESS_CHANGED, NM_PLATFORM_IP6_ADDRESS_REMOVED }, }; static struct nl_cache * @@ -261,6 +336,9 @@ choose_cache (NMPlatform *platform, struct nl_object *object) switch (object_type_from_nl_object (object)) { case LINK: return priv->link_cache; + case IP4_ADDRESS: + case IP6_ADDRESS: + return priv->address_cache; default: g_assert_not_reached (); } @@ -281,6 +359,22 @@ announce_object (NMPlatform *platform, const struct nl_object *object, ObjectSta g_signal_emit_by_name (platform, sig, &device); } return; + case IP4_ADDRESS: + { + NMPlatformIP4Address address; + + init_ip4_address (&address, (struct rtnl_addr *) object); + g_signal_emit_by_name (platform, sig, &address); + } + return; + case IP6_ADDRESS: + { + NMPlatformIP6Address address; + + init_ip6_address (&address, (struct rtnl_addr *) object); + g_signal_emit_by_name (platform, sig, &address); + } + return; default: error ("Announcing object: object type unknown: %d", object_type); } @@ -404,6 +498,7 @@ event_notification (struct nl_msg *msg, gpointer user_data) /* Removed object */ switch (event) { case RTM_DELLINK: + case RTM_DELADDR: /* Ignore inconsistent deletion * * Quick external deletion and addition can be occasionally @@ -420,6 +515,7 @@ event_notification (struct nl_msg *msg, gpointer user_data) return NL_OK; case RTM_NEWLINK: + case RTM_NEWADDR: /* Ignore inconsistent addition or change (kernel will send a good one) * * Quick sequence of RTM_NEWLINK notifications can be occasionally @@ -642,6 +738,137 @@ link_set_noarp (NMPlatform *platform, int ifindex) /******************************************************************/ +static int +ip_address_mark_all (NMPlatform *platform, int family, int ifindex) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + struct nl_object *object; + int count = 0; + + for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { + nl_object_unmark (object); + if (rtnl_addr_get_family ((struct rtnl_addr *) object) != family) + continue; + if (rtnl_addr_get_ifindex ((struct rtnl_addr *) object) != ifindex) + continue; + nl_object_mark (object); + count++; + } + + return count; +} + +static GArray * +ip4_address_get_all (NMPlatform *platform, int ifindex) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + GArray *addresses; + NMPlatformIP4Address address; + struct nl_object *object; + int count; + + count = ip_address_mark_all (platform, AF_INET, ifindex); + addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP4Address), count); + + for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { + if (nl_object_is_marked (object)) { + init_ip4_address (&address, (struct rtnl_addr *) object); + g_array_append_val (addresses, address); + nl_object_unmark (object); + } + } + + return addresses; +} + +static GArray * +ip6_address_get_all (NMPlatform *platform, int ifindex) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + GArray *addresses; + NMPlatformIP6Address address; + struct nl_object *object; + int count; + + count = ip_address_mark_all (platform, AF_INET6, ifindex); + addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP6Address), count); + + for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { + if (nl_object_is_marked (object)) { + init_ip6_address (&address, (struct rtnl_addr *) object); + g_array_append_val (addresses, address); + nl_object_unmark (object); + } + } + + return addresses; +} + +static struct nl_object * +build_rtnl_addr (int family, int ifindex, gconstpointer addr, int plen) +{ + struct rtnl_addr *rtnladdr = rtnl_addr_alloc (); + int addrlen = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); + auto_nl_addr struct nl_addr *nladdr = nl_addr_build (family, addr, addrlen); + int nle; + + g_assert (rtnladdr && nladdr); + + rtnl_addr_set_ifindex (rtnladdr, ifindex); + nle = rtnl_addr_set_local (rtnladdr, nladdr); + g_assert (!nle); + rtnl_addr_set_prefixlen (rtnladdr, plen); + + return (struct nl_object *) rtnladdr; +} + +static gboolean +ip4_address_add (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +{ + return add_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr, plen)); +} + +static gboolean +ip6_address_add (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) +{ + return add_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, plen)); +} + +static gboolean +ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +{ + return delete_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr, plen)); +} + +static gboolean +ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) +{ + return delete_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, plen)); +} + +static gboolean +ip_address_exists (NMPlatform *platform, int family, int ifindex, gconstpointer addr, int plen) +{ + auto_nl_object struct nl_object *object = build_rtnl_addr (family, ifindex, addr, plen); + auto_nl_object struct nl_object *cached_object = nl_cache_search (choose_cache (platform, object), object); + + return !!cached_object; +} + +static gboolean +ip4_address_exists (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +{ + return ip_address_exists (platform, AF_INET, ifindex, &addr, plen); +} + +static gboolean +ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) +{ + return ip_address_exists (platform, AF_INET6, ifindex, &addr, plen); +} + +/******************************************************************/ + #define EVENT_CONDITIONS ((GIOCondition) (G_IO_IN | G_IO_PRI)) #define ERROR_CONDITIONS ((GIOCondition) (G_IO_ERR | G_IO_NVAL)) #define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP)) @@ -735,6 +962,7 @@ setup (NMPlatform *platform) g_assert (!nle); nle = nl_socket_add_memberships (priv->nlh_event, RTNLGRP_LINK, + RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, NULL); g_assert (!nle); debug ("Netlink socket for events established: %d", nl_socket_get_local_port (priv->nlh_event)); @@ -753,7 +981,8 @@ setup (NMPlatform *platform) /* Allocate netlink caches */ rtnl_link_alloc_cache (priv->nlh, AF_UNSPEC, &priv->link_cache); - g_assert (priv->link_cache); + rtnl_addr_alloc_cache (priv->nlh, &priv->address_cache); + g_assert (priv->link_cache && priv->address_cache); return TRUE; } @@ -769,6 +998,7 @@ nm_linux_platform_finalize (GObject *object) nl_socket_free (priv->nlh); nl_socket_free (priv->nlh_event); nl_cache_free (priv->link_cache); + nl_cache_free (priv->address_cache); G_OBJECT_CLASS (nm_linux_platform_parent_class)->finalize (object); } @@ -802,4 +1032,13 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_is_up = link_is_up; platform_class->link_is_connected = link_is_connected; platform_class->link_uses_arp = link_uses_arp; + + platform_class->ip4_address_get_all = ip4_address_get_all; + platform_class->ip6_address_get_all = ip6_address_get_all; + platform_class->ip4_address_add = ip4_address_add; + platform_class->ip6_address_add = ip6_address_add; + platform_class->ip4_address_delete = ip4_address_delete; + platform_class->ip6_address_delete = ip6_address_delete; + platform_class->ip4_address_exists = ip4_address_exists; + platform_class->ip6_address_exists = ip6_address_exists; } diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index b2ffeac0f..5323bcdf9 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "nm-platform.h" #include "nm-logging.h" @@ -37,6 +38,12 @@ enum { LINK_ADDED, LINK_CHANGED, LINK_REMOVED, + IP4_ADDRESS_ADDED, + IP4_ADDRESS_CHANGED, + IP4_ADDRESS_REMOVED, + IP6_ADDRESS_ADDED, + IP6_ADDRESS_CHANGED, + IP6_ADDRESS_REMOVED, LAST_SIGNAL }; @@ -473,6 +480,128 @@ nm_platform_link_set_noarp (int ifindex) /******************************************************************/ +GArray * +nm_platform_ip4_address_get_all (int ifindex) +{ + reset_error (); + + g_return_val_if_fail (ifindex > 0, NULL); + g_return_val_if_fail (klass->ip4_address_get_all, NULL); + + return klass->ip4_address_get_all (platform, ifindex); +} + +GArray * +nm_platform_ip6_address_get_all (int ifindex) +{ + reset_error (); + + g_return_val_if_fail (ifindex > 0, NULL); + g_return_val_if_fail (klass->ip6_address_get_all, NULL); + + return klass->ip6_address_get_all (platform, ifindex); +} + +gboolean +nm_platform_ip4_address_add (int ifindex, in_addr_t address, int plen) +{ + reset_error (); + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (plen > 0, FALSE); + g_return_val_if_fail (klass->ip4_address_add, FALSE); + + if (nm_platform_ip4_address_exists (ifindex, address, plen)) { + debug ("address already exists"); + platform->error = NM_PLATFORM_ERROR_EXISTS; + return FALSE; + } + + debug ("address: adding IPv4 address"); + return klass->ip4_address_add (platform, ifindex, address, plen); +} + +gboolean +nm_platform_ip6_address_add (int ifindex, struct in6_addr address, int plen) +{ + reset_error (); + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (plen > 0, FALSE); + g_return_val_if_fail (klass->ip6_address_add, FALSE); + + if (nm_platform_ip6_address_exists (ifindex, address, plen)) { + debug ("address already exists"); + platform->error = NM_PLATFORM_ERROR_EXISTS; + return FALSE; + } + + debug ("address: adding IPv6 address"); + return klass->ip6_address_add (platform, ifindex, address, plen); +} + +gboolean +nm_platform_ip4_address_delete (int ifindex, in_addr_t address, int plen) +{ + reset_error (); + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (plen > 0, FALSE); + g_return_val_if_fail (klass->ip4_address_delete, FALSE); + + if (!nm_platform_ip4_address_exists (ifindex, address, plen)) { + debug ("address doesn't exists"); + platform->error = NM_PLATFORM_ERROR_NOT_FOUND; + return FALSE; + } + + debug ("address: deleting IPv4 address"); + return klass->ip4_address_delete (platform, ifindex, address, plen); +} + +gboolean +nm_platform_ip6_address_delete (int ifindex, struct in6_addr address, int plen) +{ + reset_error (); + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (plen > 0, FALSE); + g_return_val_if_fail (klass->ip6_address_delete, FALSE); + + if (!nm_platform_ip6_address_exists (ifindex, address, plen)) { + debug ("address doesn't exists"); + platform->error = NM_PLATFORM_ERROR_NOT_FOUND; + return FALSE; + } + + debug ("address: deleting IPv6 address"); + return klass->ip6_address_delete (platform, ifindex, address, plen); +} + +gboolean +nm_platform_ip4_address_exists (int ifindex, in_addr_t address, int plen) +{ + reset_error (); + + g_return_val_if_fail (plen > 0, FALSE); + g_return_val_if_fail (klass->ip4_address_exists, FALSE); + + return klass->ip4_address_exists (platform, ifindex, address, plen); +} + +gboolean +nm_platform_ip6_address_exists (int ifindex, struct in6_addr address, int plen) +{ + reset_error (); + + g_return_val_if_fail (plen > 0, FALSE); + g_return_val_if_fail (klass->ip6_address_exists, FALSE); + + return klass->ip6_address_exists (platform, ifindex, address, plen); +} + +/******************************************************************/ + static void log_link_added (NMPlatform *p, NMPlatformLink *info, gpointer user_data) { @@ -491,6 +620,66 @@ log_link_removed (NMPlatform *p, NMPlatformLink *info, gpointer user_data) debug ("signal: link removed: '%s' (%d)", info->name, info->ifindex); } +static void +log_ip4_address (NMPlatformIP4Address *address, const char *change_type) +{ + char addr[INET_ADDRSTRLEN]; + int plen = address->plen; + const char *name = nm_platform_link_get_name (address->ifindex); + + inet_ntop (AF_INET, &address->address, addr, sizeof (addr)); + + debug ("signal: address %s: %s/%d dev %s", change_type, addr, plen, name); +} + +static void +log_ip4_address_added (NMPlatform *p, NMPlatformIP4Address *address, gpointer user_data) +{ + log_ip4_address (address, "added"); +} + +static void +log_ip4_address_changed (NMPlatform *p, NMPlatformIP4Address *address, gpointer user_data) +{ + log_ip4_address (address, "changed"); +} + +static void +log_ip4_address_removed (NMPlatform *p, NMPlatformIP4Address *address, gpointer user_data) +{ + log_ip4_address (address, "removed"); +} + +static void +log_ip6_address (NMPlatformIP6Address *address, const char *change_type) +{ + char addr[INET6_ADDRSTRLEN]; + int plen = address->plen; + const char *name = nm_platform_link_get_name (address->ifindex); + + inet_ntop (AF_INET6, &address->address, addr, sizeof (addr)); + + debug ("signal: address %s: %s/%d dev %s", change_type, addr, plen, name); +} + +static void +log_ip6_address_added (NMPlatform *p, NMPlatformIP6Address *address, gpointer user_data) +{ + log_ip6_address (address, "added"); +} + +static void +log_ip6_address_changed (NMPlatform *p, NMPlatformIP6Address *address, gpointer user_data) +{ + log_ip6_address (address, "changed"); +} + +static void +log_ip6_address_removed (NMPlatform *p, NMPlatformIP6Address *address, gpointer user_data) +{ + log_ip6_address (address, "removed"); +} + /******************************************************************/ static void @@ -516,4 +705,10 @@ nm_platform_class_init (NMPlatformClass *platform_class) SIGNAL (LINK_ADDED, log_link_added) SIGNAL (LINK_CHANGED, log_link_changed) SIGNAL (LINK_REMOVED, log_link_removed) + SIGNAL (IP4_ADDRESS_ADDED, log_ip4_address_added) + SIGNAL (IP4_ADDRESS_CHANGED, log_ip4_address_changed) + SIGNAL (IP4_ADDRESS_REMOVED, log_ip4_address_removed) + SIGNAL (IP6_ADDRESS_ADDED, log_ip6_address_added) + SIGNAL (IP6_ADDRESS_CHANGED, log_ip6_address_changed) + SIGNAL (IP6_ADDRESS_REMOVED, log_ip6_address_removed) } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 3ef02c609..fd15afe3c 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -52,6 +52,18 @@ typedef struct { gboolean arp; } NMPlatformLink; +typedef struct { + int ifindex; + in_addr_t address; + int plen; +} NMPlatformIP4Address; + +typedef struct { + int ifindex; + struct in6_addr address; + int plen; +} NMPlatformIP6Address; + /******************************************************************/ /* NMPlatform abstract class and its implementations provide a layer between @@ -107,6 +119,15 @@ typedef struct { gboolean (*link_is_up) (NMPlatform *, int ifindex); gboolean (*link_is_connected) (NMPlatform *, int ifindex); gboolean (*link_uses_arp) (NMPlatform *, int ifindex); + + GArray * (*ip4_address_get_all) (NMPlatform *, int ifindex); + GArray * (*ip6_address_get_all) (NMPlatform *, int ifindex); + gboolean (*ip4_address_add) (NMPlatform *, int ifindex, in_addr_t address, int plen); + gboolean (*ip6_address_add) (NMPlatform *, int ifindex, struct in6_addr address, int plen); + gboolean (*ip4_address_delete) (NMPlatform *, int ifindex, in_addr_t address, int plen); + gboolean (*ip6_address_delete) (NMPlatform *, int ifindex, struct in6_addr address, int plen); + gboolean (*ip4_address_exists) (NMPlatform *, int ifindex, in_addr_t address, int plen); + gboolean (*ip6_address_exists) (NMPlatform *, int ifindex, struct in6_addr address, int plen); } NMPlatformClass; /* NMPlatform signals @@ -123,6 +144,12 @@ typedef struct { #define NM_PLATFORM_LINK_ADDED "link-added" #define NM_PLATFORM_LINK_CHANGED "link-changed" #define NM_PLATFORM_LINK_REMOVED "link-removed" +#define NM_PLATFORM_IP4_ADDRESS_ADDED "ip4-address-added" +#define NM_PLATFORM_IP4_ADDRESS_CHANGED "ip4-address-changed" +#define NM_PLATFORM_IP4_ADDRESS_REMOVED "ip4-address-removed" +#define NM_PLATFORM_IP6_ADDRESS_ADDED "ip6-address-added" +#define NM_PLATFORM_IP6_ADDRESS_CHANGED "ip6-address-changed" +#define NM_PLATFORM_IP6_ADDRESS_REMOVED "ip6-address-removed" /* NMPlatform error codes */ enum { @@ -164,4 +191,13 @@ gboolean nm_platform_link_is_up (int ifindex); gboolean nm_platform_link_is_connected (int ifindex); gboolean nm_platform_link_uses_arp (int ifindex); +GArray *nm_platform_ip4_address_get_all (int ifindex); +GArray *nm_platform_ip6_address_get_all (int ifindex); +gboolean nm_platform_ip4_address_add (int ifindex, in_addr_t address, int plen); +gboolean nm_platform_ip6_address_add (int ifindex, struct in6_addr address, int plen); +gboolean nm_platform_ip4_address_delete (int ifindex, in_addr_t address, int plen); +gboolean nm_platform_ip6_address_delete (int ifindex, struct in6_addr address, int plen); +gboolean nm_platform_ip4_address_exists (int ifindex, in_addr_t address, int plen); +gboolean nm_platform_ip6_address_exists (int ifindex, struct in6_addr address, int plen); + #endif /* NM_PLATFORM_H */ diff --git a/src/platform/tests/.gitignore b/src/platform/tests/.gitignore index e8c0a56b9..0ea0e1894 100644 --- a/src/platform/tests/.gitignore +++ b/src/platform/tests/.gitignore @@ -2,3 +2,5 @@ /monitor /test-link-fake /test-link-linux +/test-address-fake +/test-address-linux diff --git a/src/platform/tests/Makefile.am b/src/platform/tests/Makefile.am index 6e8bc117d..068fa1770 100644 --- a/src/platform/tests/Makefile.am +++ b/src/platform/tests/Makefile.am @@ -20,7 +20,9 @@ noinst_PROGRAMS = \ dump \ monitor \ test-link-fake \ - test-link-linux + test-link-linux \ + test-address-fake \ + test-address-linux EXTRA_DIST = test-common.h @@ -54,6 +56,28 @@ test_link_linux_CPPFLAGS = \ -DKERNEL_HACKS=1 test_link_linux_LDADD = $(COMMON_LDADD) +test_address_fake_SOURCES = \ + test-address.c \ + test-common.c \ + ${srcdir}/../nm-platform.c \ + ${srcdir}/../nm-fake-platform.c +test_address_fake_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DSETUP=nm_fake_platform_setup \ + -DKERNEL_HACKS=0 +test_address_fake_LDADD = $(COMMON_LDADD) + +test_address_linux_SOURCES = \ + test-address.c \ + test-common.c \ + ${srcdir}/../nm-platform.c \ + ${srcdir}/../nm-linux-platform.c +test_address_linux_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DSETUP=nm_linux_platform_setup \ + -DKERNEL_HACKS=1 +test_address_linux_LDADD = $(COMMON_LDADD) + # Unfortunately, we cannot run nm-linux-platform-test as an automatic test # program by default, as it requires root access and modifies kernel # configuration. @@ -61,8 +85,8 @@ test_link_linux_LDADD = $(COMMON_LDADD) # However, we can check whether the fake platform fakes platform behavior # correctly. @VALGRIND_RULES@ -TESTS = ./test-link-fake -ROOTTESTS = ./test-link-linux +TESTS = ./test-link-fake ./test-address-fake +ROOTTESTS = ./test-link-linux ./test-address-linux # If explicitly enabled, we can run the root tests if RUN_ROOT_TESTS diff --git a/src/platform/tests/dump.c b/src/platform/tests/dump.c index 7a9cade30..c3ff3e796 100644 --- a/src/platform/tests/dump.c +++ b/src/platform/tests/dump.c @@ -1,5 +1,6 @@ #include #include +#include #include "nm-platform.h" #include "nm-linux-platform.h" @@ -23,6 +24,13 @@ type_to_string (NMLinkType type) static void dump_interface (NMPlatformLink *link) { + GArray *ip6_addresses; + GArray *ip4_addresses; + const NMPlatformIP6Address *ip6_address; + const NMPlatformIP4Address *ip4_address; + char addrstr[INET6_ADDRSTRLEN]; + int i; + g_assert (link->up || !link->connected); printf ("%d: %s: %s", link->ifindex, link->name, type_to_string (link->type)); @@ -33,6 +41,27 @@ dump_interface (NMPlatformLink *link) if (!link->arp) printf (" noarp"); printf ("\n"); + + ip4_addresses = nm_platform_ip4_address_get_all (link->ifindex); + ip6_addresses = nm_platform_ip6_address_get_all (link->ifindex); + + g_assert (ip4_addresses); + g_assert (ip6_addresses); + + for (i = 0; i < ip4_addresses->len; i++) { + ip4_address = &g_array_index (ip4_addresses, NMPlatformIP4Address, i); + inet_ntop (AF_INET, &ip4_address->address, addrstr, sizeof (addrstr)); + printf (" ip4-address %s/%d\n", addrstr, ip4_address->plen); + } + + for (i = 0; i < ip6_addresses->len; i++) { + ip6_address = &g_array_index (ip6_addresses, NMPlatformIP6Address, i); + inet_ntop (AF_INET6, &ip6_address->address, addrstr, sizeof (addrstr)); + printf (" ip6-address %s/%d\n", addrstr, ip6_address->plen); + } + + g_array_unref (ip4_addresses); + g_array_unref (ip6_addresses); } static void diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c new file mode 100644 index 000000000..12b25013b --- /dev/null +++ b/src/platform/tests/test-address.c @@ -0,0 +1,252 @@ +#include "test-common.h" + +#define DEVICE_NAME "nm-test-device" +#define IP4_ADDRESS "192.0.2.1" +#define IP4_PLEN 24 +#define IP6_ADDRESS "2001:db8:a:b:1:2:3:4" +#define IP6_PLEN 64 + +static void +ip4_address_callback (NMPlatform *platform, NMPlatformIP4Address *received, SignalData *data) +{ + g_assert (received); + + if (data->ifindex && data->ifindex != received->ifindex) + return; + + if (data->loop) + g_main_loop_quit (data->loop); + + if (data->received) + g_error ("Received signal '%s' a second time.", data->name); + + data->received = TRUE; +} + +static void +ip6_address_callback (NMPlatform *platform, NMPlatformIP6Address *received, SignalData *data) +{ + g_assert (received); + + if (data->ifindex && data->ifindex != received->ifindex) + return; + + if (data->loop) + g_main_loop_quit (data->loop); + + if (data->received) + g_error ("Received signal '%s' a second time.", data->name); + + data->received = TRUE; +} + +static void +test_ip4_address (void) +{ + SignalData *address_added = add_signal (NM_PLATFORM_IP4_ADDRESS_ADDED, ip4_address_callback); + SignalData *address_removed = add_signal (NM_PLATFORM_IP4_ADDRESS_REMOVED, ip4_address_callback); + int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + GArray *addresses; + NMPlatformIP4Address addrs[2]; + in_addr_t addr; + + inet_pton (AF_INET, IP4_ADDRESS, &addr); + + /* Add address */ + g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + no_error (); + g_assert (nm_platform_ip4_address_add (ifindex, addr, IP4_PLEN)); + no_error (); + g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + no_error (); + accept_signal (address_added); + + /* Add address again */ + g_assert (!nm_platform_ip4_address_add (ifindex, addr, IP4_PLEN)); + error (NM_PLATFORM_ERROR_EXISTS); + + /* Test address listing */ + addresses = nm_platform_ip4_address_get_all (ifindex); + g_assert (addresses); + no_error (); + memset (addrs, 0, sizeof (addrs)); + addrs[0].ifindex = ifindex; + addrs[0].address = addr; + addrs[0].plen = IP4_PLEN; + g_assert (addresses->len == 1); + g_assert (!memcmp (addresses->data, addrs, sizeof (addrs))); + g_array_unref (addresses); + + /* Remove address */ + g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN)); + no_error (); + g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + accept_signal (address_removed); + + /* Remove address again */ + g_assert (!nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN)); + error (NM_PLATFORM_ERROR_NOT_FOUND); + + free_signal (address_added); + free_signal (address_removed); +} + +static void +test_ip6_address (void) +{ + SignalData *address_added = add_signal (NM_PLATFORM_IP6_ADDRESS_ADDED, ip6_address_callback); + SignalData *address_removed = add_signal (NM_PLATFORM_IP6_ADDRESS_REMOVED, ip6_address_callback); + int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + GArray *addresses; + NMPlatformIP6Address addrs[2]; + struct in6_addr addr; + + inet_pton (AF_INET6, IP6_ADDRESS, &addr); + + /* Add address */ + g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + no_error (); + g_assert (nm_platform_ip6_address_add (ifindex, addr, IP6_PLEN)); + no_error (); + g_assert (nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + no_error (); + accept_signal (address_added); + + /* Add address again */ + g_assert (!nm_platform_ip6_address_add (ifindex, addr, IP6_PLEN)); + error (NM_PLATFORM_ERROR_EXISTS); + + /* Test address listing */ + addresses = nm_platform_ip6_address_get_all (ifindex); + g_assert (addresses); + no_error (); + memset (addrs, 0, sizeof (addrs)); + addrs[0].ifindex = ifindex; + addrs[0].address = addr; + addrs[0].plen = IP6_PLEN; + g_assert (addresses->len == 1); + g_assert (!memcmp (addresses->data, addrs, sizeof (addrs))); + g_array_unref (addresses); + + /* Remove address */ + g_assert (nm_platform_ip6_address_delete (ifindex, addr, IP6_PLEN)); + no_error (); + g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + accept_signal (address_removed); + + /* Remove address again */ + g_assert (!nm_platform_ip6_address_delete (ifindex, addr, IP6_PLEN)); + error (NM_PLATFORM_ERROR_NOT_FOUND); + + free_signal (address_added); + free_signal (address_removed); +} + +static void +test_ip4_address_external (void) +{ + SignalData *address_added = add_signal (NM_PLATFORM_IP4_ADDRESS_ADDED, ip4_address_callback); + SignalData *address_removed = add_signal (NM_PLATFORM_IP4_ADDRESS_REMOVED, ip4_address_callback); + int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + in_addr_t addr; + + inet_pton (AF_INET, IP4_ADDRESS, &addr); + g_assert (ifindex > 0); + + /* Looks like addresses are not announced by kerenl when the interface + * is down. Link-local IPv6 address is automatically added. + */ + g_assert (nm_platform_link_set_up (nm_platform_link_get_ifindex (DEVICE_NAME))); + + /* Add/delete notification */ + run_command ("ip address add %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME); + wait_signal (address_added); + g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + run_command ("ip address delete %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME); + wait_signal (address_removed); + g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + + /* Add/delete conflict */ + run_command ("ip address add %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME); + g_assert (nm_platform_ip4_address_add (ifindex, addr, IP4_PLEN)); + no_error (); + g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + accept_signal (address_added); + /*run_command ("ip address delete %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME); + g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN)); + no_error (); + g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + accept_signal (address_removed);*/ + + free_signal (address_added); + free_signal (address_removed); +} + +static void +test_ip6_address_external (void) +{ + SignalData *address_added = add_signal (NM_PLATFORM_IP6_ADDRESS_ADDED, ip6_address_callback); + SignalData *address_removed = add_signal (NM_PLATFORM_IP6_ADDRESS_REMOVED, ip6_address_callback); + int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + struct in6_addr addr; + + inet_pton (AF_INET6, IP6_ADDRESS, &addr); + + /* Add/delete notification */ + run_command ("ip address add %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME); + wait_signal (address_added); + g_assert (nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + run_command ("ip address delete %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME); + wait_signal (address_removed); + g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + + /* Add/delete conflict */ + run_command ("ip address add %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME); + g_assert (nm_platform_ip6_address_add (ifindex, addr, IP6_PLEN)); + no_error (); + g_assert (nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + accept_signal (address_added); + /*run_command ("ip address delete %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME); + g_assert (nm_platform_ip6_address_delete (ifindex, addr, IP6_PLEN)); + no_error (); + g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + wait_signal (address_removed);*/ + + free_signal (address_added); + free_signal (address_removed); +} + +int +main (int argc, char **argv) +{ + int result; + + openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR, LOG_DAEMON); + g_type_init (); + g_test_init (&argc, &argv, NULL); + /* Enable debug messages if called with --debug */ + for (; *argv; argv++) + if (!g_strcmp0 (*argv, "--debug")) + nm_logging_setup ("debug", NULL, NULL); + + SETUP (); + + /* Clean up */ + nm_platform_link_delete_by_name (DEVICE_NAME); + g_assert (!nm_platform_link_exists (DEVICE_NAME)); + nm_platform_dummy_add (DEVICE_NAME); + + g_test_add_func ("/address/internal/ip4", test_ip4_address); + g_test_add_func ("/address/internal/ip6", test_ip6_address); + + if (strcmp (g_type_name (G_TYPE_FROM_INSTANCE (nm_platform_get ())), "NMFakePlatform")) { + g_test_add_func ("/address/external/ip4", test_ip4_address_external); + g_test_add_func ("/address/external/ip6", test_ip6_address_external); + } + + result = g_test_run (); + + nm_platform_link_delete_by_name (DEVICE_NAME); + nm_platform_free (); + return result; +} diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 9c5f9e916..46657d482 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -2,6 +2,7 @@ #include #include #include +#include #include "nm-logging.h" #include "nm-platform.h"