diff options
author | Dan Williams <dcbw@redhat.com> | 2015-04-09 13:28:09 -0500 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2015-04-09 13:28:09 -0500 |
commit | 5c4afb8040b2bb9739f945e9c51444bd32948147 (patch) | |
tree | 3661d548e786aa32bcaf4ac1287b6119c0da963c | |
parent | 5d88d40e63fcd0a3a7f5cc0915d666ac8e939e82 (diff) | |
download | NetworkManager-dcbw/rdisc-fixes.tar.gz |
rdisc: move most RA processing logic into base classdcbw/rdisc-fixes
Instead of having it all in the Linux implementation, move all the
timeout logic and most of the processing logic into the NMRDisc
base class so that it can be used by NMFakeRDisc as well. This
will help increase testability since now we can test the timeout
and expiry logic from the fake plugin too.
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/rdisc/nm-fake-rdisc.c | 160 | ||||
-rw-r--r-- | src/rdisc/nm-lndp-rdisc.c | 472 | ||||
-rw-r--r-- | src/rdisc/nm-rdisc.c | 416 | ||||
-rw-r--r-- | src/rdisc/nm-rdisc.h | 2 |
5 files changed, 573 insertions, 478 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 808e59eb33..a24ac6c4f2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -229,6 +229,7 @@ nm_sources = \ rdisc/nm-lndp-rdisc.h \ rdisc/nm-rdisc.c \ rdisc/nm-rdisc.h \ + rdisc/nm-rdisc-private.h \ \ ppp-manager/nm-ppp-manager.c \ ppp-manager/nm-ppp-manager.h \ diff --git a/src/rdisc/nm-fake-rdisc.c b/src/rdisc/nm-fake-rdisc.c index e631f28d65..f2efb785fa 100644 --- a/src/rdisc/nm-fake-rdisc.c +++ b/src/rdisc/nm-fake-rdisc.c @@ -24,6 +24,7 @@ #include <arpa/inet.h> #include "nm-fake-rdisc.h" +#include "nm-rdisc-private.h" #include "nm-logging.h" @@ -31,106 +32,159 @@ #define warning(...) nm_log_warn (LOGD_IP6, __VA_ARGS__) #define error(...) nm_log_err (LOGD_IP6, __VA_ARGS__) +typedef struct { + guint ra_received_id; +} NMFakeRDiscPrivate; + #define NM_FAKE_RDISC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_FAKE_RDISC, NMFakeRDiscPrivate)) G_DEFINE_TYPE (NMFakeRDisc, nm_fake_rdisc, NM_TYPE_RDISC) /******************************************************************/ -NMRDisc * -nm_fake_rdisc_new (int ifindex, const char *ifname) -{ - NMRDisc *rdisc = g_object_new (NM_TYPE_FAKE_RDISC, NULL); - - rdisc->ifindex = ifindex; - rdisc->ifname = g_strdup (ifname); - rdisc->max_addresses = NM_RDISC_MAX_ADDRESSES_DEFAULT; - rdisc->rtr_solicitations = NM_RDISC_RTR_SOLICITATIONS_DEFAULT; - rdisc->rtr_solicitation_interval = NM_RDISC_RTR_SOLICITATION_INTERVAL_DEFAULT; - - return rdisc; -} - -static void -delayed_start (NMRDisc *rdisc) +static gboolean +ra_received (gpointer user_data) { - int changed = - NM_RDISC_CONFIG_GATEWAYS | NM_RDISC_CONFIG_ADDRESSES | NM_RDISC_CONFIG_ROUTES | - NM_RDISC_CONFIG_DNS_SERVERS | NM_RDISC_CONFIG_DNS_DOMAINS; - debug ("%d", rdisc->dhcp_level); - - g_signal_emit_by_name ( - rdisc, NM_RDISC_CONFIG_CHANGED, changed); -} - -static void -start (NMRDisc *rdisc) -{ - g_idle_add ((GSourceFunc) (delayed_start), rdisc); -} - -/******************************************************************/ - -static void -nm_fake_rdisc_init (NMFakeRDisc *fake_rdisc) -{ - NMRDisc *rdisc = NM_RDISC (fake_rdisc); + NMFakeRDisc *self = NM_FAKE_RDISC (user_data); + NMRDisc *rdisc = NM_RDISC (self); + NMRDiscConfigMap changed = 0; + guint32 now = nm_utils_get_monotonic_timestamp_s (); NMRDiscGateway gateway; NMRDiscAddress address; NMRDiscRoute route; NMRDiscDNSServer dns_server; NMRDiscDNSDomain dns_domain; + NM_FAKE_RDISC_GET_PRIVATE (self)->ra_received_id = 0; + + debug ("(%s): received router advertisement at %u", NM_RDISC (self)->ifname, now); + rdisc->dhcp_level = NM_RDISC_DHCP_LEVEL_NONE; memset (&gateway, 0, sizeof (gateway)); inet_pton (AF_INET6, "fe80::1", &gateway.address); - g_array_append_val (rdisc->gateways, gateway); + if (nm_rdisc_add_gateway (rdisc, &gateway)) + changed |= NM_RDISC_CONFIG_GATEWAYS; inet_pton (AF_INET6, "fe80::2", &gateway.address); - g_array_append_val (rdisc->gateways, gateway); + if (nm_rdisc_add_gateway (rdisc, &gateway)) + changed |= NM_RDISC_CONFIG_GATEWAYS; inet_pton (AF_INET6, "fe80::3", &gateway.address); - g_array_append_val (rdisc->gateways, gateway); + if (nm_rdisc_add_gateway (rdisc, &gateway)) + changed |= NM_RDISC_CONFIG_GATEWAYS; memset (&address, 0, sizeof (address)); inet_pton (AF_INET6, "2001:db8:a:a::1", &address.address); - g_array_append_val (rdisc->addresses, address); + if (nm_rdisc_add_address (rdisc, &address)) + changed |= NM_RDISC_CONFIG_ADDRESSES; inet_pton (AF_INET6, "2001:db8:a:a::2", &address.address); - g_array_append_val (rdisc->addresses, address); + if (nm_rdisc_add_address (rdisc, &address)) + changed |= NM_RDISC_CONFIG_ADDRESSES; inet_pton (AF_INET6, "2001:db8:f:f::1", &address.address); - g_array_append_val (rdisc->addresses, address); + if (nm_rdisc_add_address (rdisc, &address)) + changed |= NM_RDISC_CONFIG_ADDRESSES; memset (&route, 0, sizeof (route)); route.plen = 64; inet_pton (AF_INET6, "2001:db8:a:a::", &route.network); - g_array_append_val (rdisc->routes, route); + if (nm_rdisc_add_route (rdisc, &route)) + changed |= NM_RDISC_CONFIG_ROUTES; inet_pton (AF_INET6, "2001:db8:b:b::", &route.network); - g_array_append_val (rdisc->routes, route); + if (nm_rdisc_add_route (rdisc, &route)) + changed |= NM_RDISC_CONFIG_ROUTES; memset (&dns_server, 0, sizeof (dns_server)); inet_pton (AF_INET6, "2001:db8:c:c::1", &dns_server.address); - g_array_append_val (rdisc->dns_servers, dns_server); + if (nm_rdisc_add_dns_server (rdisc, &dns_server)) + changed |= NM_RDISC_CONFIG_DNS_SERVERS; inet_pton (AF_INET6, "2001:db8:c:c::2", &dns_server.address); - g_array_append_val (rdisc->dns_servers, dns_server); + if (nm_rdisc_add_dns_server (rdisc, &dns_server)) + changed |= NM_RDISC_CONFIG_DNS_SERVERS; inet_pton (AF_INET6, "2001:db8:c:c::3", &dns_server.address); - g_array_append_val (rdisc->dns_servers, dns_server); + if (nm_rdisc_add_dns_server (rdisc, &dns_server)) + changed |= NM_RDISC_CONFIG_DNS_SERVERS; inet_pton (AF_INET6, "2001:db8:c:c::4", &dns_server.address); - g_array_append_val (rdisc->dns_servers, dns_server); + if (nm_rdisc_add_dns_server (rdisc, &dns_server)) + changed |= NM_RDISC_CONFIG_DNS_SERVERS; inet_pton (AF_INET6, "2001:db8:c:c::5", &dns_server.address); - g_array_append_val (rdisc->dns_servers, dns_server); + if (nm_rdisc_add_dns_server (rdisc, &dns_server)) + changed |= NM_RDISC_CONFIG_DNS_SERVERS; memset (&dns_domain, 0, sizeof (dns_domain)); dns_domain.domain = g_strdup ("example.net"); - g_array_append_val (rdisc->dns_domains, dns_domain); + if (nm_rdisc_add_dns_domain (rdisc, &dns_domain)) + changed |= NM_RDISC_CONFIG_DNS_DOMAINS; dns_domain.domain = g_strdup ("example.com"); - g_array_append_val (rdisc->dns_domains, dns_domain); + if (nm_rdisc_add_dns_domain (rdisc, &dns_domain)) + changed |= NM_RDISC_CONFIG_DNS_DOMAINS; dns_domain.domain = g_strdup ("example.org"); - g_array_append_val (rdisc->dns_domains, dns_domain); + if (nm_rdisc_add_dns_domain (rdisc, &dns_domain)) + changed |= NM_RDISC_CONFIG_DNS_DOMAINS; + + nm_rdisc_ra_received (NM_RDISC (self), now, changed); + return G_SOURCE_REMOVE; +} + +static gboolean +send_rs (NMRDisc *rdisc) +{ + NMFakeRDiscPrivate *priv = NM_FAKE_RDISC_GET_PRIVATE (rdisc); + + if (priv->ra_received_id) + g_source_remove (priv->ra_received_id); + priv->ra_received_id = g_timeout_add_seconds (3, ra_received, rdisc); + + return TRUE; +} + +static void +start (NMRDisc *rdisc) +{ + nm_rdisc_solicit (rdisc); +} + +/******************************************************************/ + +NMRDisc * +nm_fake_rdisc_new (int ifindex, const char *ifname) +{ + NMRDisc *rdisc = g_object_new (NM_TYPE_FAKE_RDISC, NULL); + + rdisc->ifindex = ifindex; + rdisc->ifname = g_strdup (ifname); + rdisc->max_addresses = NM_RDISC_MAX_ADDRESSES_DEFAULT; + rdisc->rtr_solicitations = NM_RDISC_RTR_SOLICITATIONS_DEFAULT; + rdisc->rtr_solicitation_interval = NM_RDISC_RTR_SOLICITATION_INTERVAL_DEFAULT; + + return rdisc; +} + +static void +nm_fake_rdisc_init (NMFakeRDisc *fake_rdisc) +{ +} + +static void +dispose (GObject *object) +{ + NMFakeRDiscPrivate *priv = NM_FAKE_RDISC_GET_PRIVATE (object); + + if (priv->ra_received_id) { + g_source_remove (priv->ra_received_id); + priv->ra_received_id = 0; + } + + G_OBJECT_CLASS (nm_fake_rdisc_parent_class)->dispose (object); } static void nm_fake_rdisc_class_init (NMFakeRDiscClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); NMRDiscClass *rdisc_class = NM_RDISC_CLASS (klass); + g_type_class_add_private (klass, sizeof (NMFakeRDiscPrivate)); + + object_class->dispose = dispose; rdisc_class->start = start; + rdisc_class->send_rs = send_rs; } diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c index a59cfa5b5b..f51f95ebcb 100644 --- a/src/rdisc/nm-lndp-rdisc.c +++ b/src/rdisc/nm-lndp-rdisc.c @@ -27,6 +27,7 @@ #include <ndp.h> #include "nm-lndp-rdisc.h" +#include "nm-rdisc-private.h" #include "NetworkManagerUtils.h" #include "nm-logging.h" @@ -39,13 +40,9 @@ typedef struct { struct ndp *ndp; - guint send_rs_id; GIOChannel *event_channel; guint event_id; - guint timeout_id; /* prefix/dns/etc lifetime timeout */ guint ra_timeout_id; /* first RA timeout */ - - int solicitations_left; } NMLNDPRDiscPrivate; #define NM_LNDP_RDISC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LNDP_RDISC, NMLNDPRDiscPrivate)) @@ -54,190 +51,6 @@ G_DEFINE_TYPE (NMLNDPRDisc, nm_lndp_rdisc, NM_TYPE_RDISC) /******************************************************************/ -static inline gint32 -ipv6_sysctl_get (const char *ifname, const char *property, gint32 defval) -{ - return nm_platform_sysctl_get_int32 (nm_utils_ip6_property_path (ifname, property), defval); -} - -NMRDisc * -nm_lndp_rdisc_new (int ifindex, const char *ifname) -{ - NMRDisc *rdisc; - NMLNDPRDiscPrivate *priv; - int error; - - rdisc = g_object_new (NM_TYPE_LNDP_RDISC, NULL); - - rdisc->ifindex = ifindex; - rdisc->ifname = g_strdup (ifname); - - rdisc->max_addresses = ipv6_sysctl_get (ifname, "max_addresses", - NM_RDISC_MAX_ADDRESSES_DEFAULT); - rdisc->rtr_solicitations = ipv6_sysctl_get (ifname, "router_solicitations", - NM_RDISC_RTR_SOLICITATIONS_DEFAULT); - rdisc->rtr_solicitation_interval = ipv6_sysctl_get (ifname, "router_solicitation_interval", - NM_RDISC_RTR_SOLICITATION_INTERVAL_DEFAULT); - - priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc); - error = ndp_open (&priv->ndp); - if (error != 0) { - g_object_unref (rdisc); - debug ("(%s): error creating socket for NDP; errno=%d", ifname, -error); - return NULL; - } - return rdisc; -} - -static gboolean -add_gateway (NMRDisc *rdisc, const NMRDiscGateway *new) -{ - int i, insert_idx = -1; - - for (i = 0; i < rdisc->gateways->len; i++) { - NMRDiscGateway *item = &g_array_index (rdisc->gateways, NMRDiscGateway, i); - - if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) { - if (item->preference != new->preference) { - g_array_remove_index (rdisc->gateways, i--); - continue; - } - memcpy (item, new, sizeof (*new)); - return FALSE; - } - - /* Put before less preferable gateways. */ - if (item->preference < new->preference && insert_idx < 0) - insert_idx = i; - } - - if (new->lifetime) - g_array_insert_val (rdisc->gateways, CLAMP (insert_idx, 0, G_MAXINT), *new); - return TRUE; -} - -static gboolean -add_address (NMRDisc *rdisc, const NMRDiscAddress *new) -{ - int i; - - for (i = 0; i < rdisc->addresses->len; i++) { - NMRDiscAddress *item = &g_array_index (rdisc->addresses, NMRDiscAddress, i); - - if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) { - gboolean changed = item->timestamp + item->lifetime != new->timestamp + new->lifetime || - item->timestamp + item->preferred != new->timestamp + new->preferred; - - *item = *new; - return changed; - } - } - - /* we create at most max_addresses autoconf addresses. This is different from - * what the kernel does, because it considers *all* addresses (including - * static and other temporary addresses). - **/ - if (rdisc->max_addresses && rdisc->addresses->len >= rdisc->max_addresses) - return FALSE; - - g_array_insert_val (rdisc->addresses, i, *new); - return TRUE; -} - -static gboolean -add_route (NMRDisc *rdisc, const NMRDiscRoute *new) -{ - int i, insert_idx = -1; - - for (i = 0; i < rdisc->routes->len; i++) { - NMRDiscRoute *item = &g_array_index (rdisc->routes, NMRDiscRoute, i); - - if (IN6_ARE_ADDR_EQUAL (&item->network, &new->network) && item->plen == new->plen) { - if (item->preference != new->preference) { - g_array_remove_index (rdisc->routes, i--); - continue; - } - memcpy (item, new, sizeof (*new)); - return FALSE; - } - - /* Put before less preferable routes. */ - if (item->preference < new->preference && insert_idx < 0) - insert_idx = i; - } - - if (new->lifetime) - g_array_insert_val (rdisc->routes, CLAMP (insert_idx, 0, G_MAXINT), *new); - return TRUE; -} - -static gboolean -add_dns_server (NMRDisc *rdisc, const NMRDiscDNSServer *new) -{ - int i; - - for (i = 0; i < rdisc->dns_servers->len; i++) { - NMRDiscDNSServer *item = &g_array_index (rdisc->dns_servers, NMRDiscDNSServer, i); - - if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) { - gboolean changed; - - if (new->lifetime == 0) { - g_array_remove_index (rdisc->dns_servers, i); - return TRUE; - } - - changed = (item->timestamp != new->timestamp || - item->lifetime != new->lifetime); - if (changed) { - item->timestamp = new->timestamp; - item->lifetime = new->lifetime; - } - return changed; - } - } - - if (new->lifetime) - g_array_insert_val (rdisc->dns_servers, i, *new); - return TRUE; -} - -/* Copies new->domain if 'new' is added to the dns_domains list */ -static gboolean -add_dns_domain (NMRDisc *rdisc, const NMRDiscDNSDomain *new) -{ - NMRDiscDNSDomain *item; - int i; - - for (i = 0; i < rdisc->dns_domains->len; i++) { - item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i); - - if (!g_strcmp0 (item->domain, new->domain)) { - gboolean changed; - - if (new->lifetime == 0) { - g_array_remove_index (rdisc->dns_domains, i); - return TRUE; - } - - changed = (item->timestamp != new->timestamp || - item->lifetime != new->lifetime); - if (changed) { - item->timestamp = new->timestamp; - item->lifetime = new->lifetime; - } - return changed; - } - } - - if (new->lifetime) { - g_array_insert_val (rdisc->dns_domains, i, *new); - item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i); - item->domain = g_strdup (new->domain); - } - return TRUE; -} - static gboolean send_rs (NMRDisc *rdisc) { @@ -249,185 +62,12 @@ send_rs (NMRDisc *rdisc) g_assert (!error); ndp_msg_ifindex_set (msg, rdisc->ifindex); - debug ("(%s): sending router solicitation", rdisc->ifname); - error = ndp_msg_send (priv->ndp, msg); - if (error) - error ("(%s): cannot send router solicitation: %d.", rdisc->ifname, error); - else - priv->solicitations_left--; - ndp_msg_destroy (msg); - - if (priv->solicitations_left > 0) { - debug ("(%s): scheduling router solicitation retry in %d seconds.", - rdisc->ifname, rdisc->rtr_solicitation_interval); - priv->send_rs_id = g_timeout_add_seconds (rdisc->rtr_solicitation_interval, - (GSourceFunc) send_rs, rdisc); - } else { - debug ("(%s): did not receive a router advertisement after %d solicitations.", - rdisc->ifname, rdisc->rtr_solicitations); - priv->send_rs_id = 0; - } - - return G_SOURCE_REMOVE; -} - -static void -solicit (NMRDisc *rdisc) -{ - NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc); - - if (!priv->send_rs_id) { - debug ("(%s): scheduling router solicitation.", rdisc->ifname); - priv->send_rs_id = g_idle_add ((GSourceFunc) send_rs, rdisc); - priv->solicitations_left = rdisc->rtr_solicitations; - } -} - -static void -clean_gateways (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint64 *nextevent) -{ - int i; - - for (i = 0; i < rdisc->gateways->len; i++) { - NMRDiscGateway *item = &g_array_index (rdisc->gateways, NMRDiscGateway, i); - guint64 expiry = (guint64) item->timestamp + item->lifetime; - - if (item->lifetime == G_MAXUINT32) - continue; - - if (now >= expiry) { - g_array_remove_index (rdisc->gateways, i--); - *changed |= NM_RDISC_CONFIG_GATEWAYS; - } else if (*nextevent > expiry) - *nextevent = expiry; - } -} - -static void -clean_addresses (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint64 *nextevent) -{ - int i; - - for (i = 0; i < rdisc->addresses->len; i++) { - NMRDiscAddress *item = &g_array_index (rdisc->addresses, NMRDiscAddress, i); - guint64 expiry = (guint64) item->timestamp + item->lifetime; - - if (item->lifetime == G_MAXUINT32) - continue; - - if (now >= expiry) { - g_array_remove_index (rdisc->addresses, i--); - *changed |= NM_RDISC_CONFIG_ADDRESSES; - } else if (*nextevent > expiry) - *nextevent = expiry; - } -} - -static void -clean_routes (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint64 *nextevent) -{ - int i; - - for (i = 0; i < rdisc->routes->len; i++) { - NMRDiscRoute *item = &g_array_index (rdisc->routes, NMRDiscRoute, i); - guint64 expiry = (guint64) item->timestamp + item->lifetime; - - if (item->lifetime == G_MAXUINT32) - continue; - - if (now >= expiry) { - g_array_remove_index (rdisc->routes, i--); - *changed |= NM_RDISC_CONFIG_ROUTES; - } else if (*nextevent > expiry) - *nextevent = expiry; - } -} - -static void -clean_dns_servers (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint64 *nextevent) -{ - int i; - - for (i = 0; i < rdisc->dns_servers->len; i++) { - NMRDiscDNSServer *item = &g_array_index (rdisc->dns_servers, NMRDiscDNSServer, i); - guint64 expiry = (guint64) item->timestamp + item->lifetime; - guint64 refresh = (guint64) item->timestamp + item->lifetime / 2; - - if (item->lifetime == G_MAXUINT32) - continue; - - if (now >= expiry) { - g_array_remove_index (rdisc->dns_servers, i--); - *changed |= NM_RDISC_CONFIG_DNS_SERVERS; - } else if (now >= refresh) - solicit (rdisc); - else if (*nextevent > refresh) - *nextevent = refresh; - } -} - -static void -clean_dns_domains (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint64 *nextevent) -{ - int i; - - for (i = 0; i < rdisc->dns_domains->len; i++) { - NMRDiscDNSDomain *item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i); - guint64 expiry = (guint64) item->timestamp + item->lifetime; - guint64 refresh = (guint64) item->timestamp + item->lifetime / 2; - - if (item->lifetime == G_MAXUINT32) - continue; - - if (now >= expiry) { - g_free (item->domain); - g_array_remove_index (rdisc->dns_domains, i--); - *changed |= NM_RDISC_CONFIG_DNS_DOMAINS; - } else if (now >= refresh) - solicit (rdisc); - else if (*nextevent > refresh) - *nextevent = refresh; - } -} - -static gboolean timeout_cb (gpointer user_data); - -static void -check_timestamps (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap changed) -{ - NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc); - /* Use a magic date in the distant future (~68 years) */ - const guint64 never = now + G_MAXINT32; - guint64 nextevent = never; - - if (priv->timeout_id) { - g_source_remove (priv->timeout_id); - priv->timeout_id = 0; - } - - clean_gateways (rdisc, now, &changed, &nextevent); - clean_addresses (rdisc, now, &changed, &nextevent); - clean_routes (rdisc, now, &changed, &nextevent); - clean_dns_servers (rdisc, now, &changed, &nextevent); - clean_dns_domains (rdisc, now, &changed, &nextevent); - - if (changed) - g_signal_emit_by_name (rdisc, NM_RDISC_CONFIG_CHANGED, changed); - - if (nextevent < never) { - g_return_if_fail (nextevent > now); - debug ("(%s): scheduling next now/lifetime check: %lu seconds", - rdisc->ifname, nextevent - now); - priv->timeout_id = g_timeout_add_seconds (nextevent - now, timeout_cb, rdisc); + if (error) { + error ("(%s): cannot send router solicitation: %d.", rdisc->ifname, error); + return FALSE; } -} - -static gboolean -timeout_cb (gpointer user_data) -{ - check_timestamps (user_data, nm_utils_get_monotonic_timestamp_s (), 0); return TRUE; } @@ -447,28 +87,6 @@ translate_preference (enum ndp_route_preference preference) } } -static void -clear_rs_timeout (NMLNDPRDisc *rdisc) -{ - NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc); - - if (priv->send_rs_id) { - g_source_remove (priv->send_rs_id); - priv->send_rs_id = 0; - } -} - -static void -clear_ra_timeout (NMLNDPRDisc *rdisc) -{ - NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc); - - if (priv->ra_timeout_id) { - g_source_remove (priv->ra_timeout_id); - priv->ra_timeout_id = 0; - } -} - static int receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) { @@ -493,9 +111,6 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) */ debug ("(%s): received router advertisement at %u", rdisc->ifname, now); - clear_ra_timeout (NM_LNDP_RDISC (rdisc)); - clear_rs_timeout (NM_LNDP_RDISC (rdisc)); - /* DHCP level: * * The problem with DHCP level is what to do if subsequent @@ -529,7 +144,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) gateway.timestamp = now; gateway.lifetime = ndp_msgra_router_lifetime (msgra); gateway.preference = translate_preference (ndp_msgra_route_preference (msgra)); - if (add_gateway (rdisc, &gateway)) + if (nm_rdisc_add_gateway (rdisc, &gateway)) changed |= NM_RDISC_CONFIG_GATEWAYS; /* Addresses & Routes */ @@ -544,7 +159,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) route.timestamp = now; if (ndp_msg_opt_prefix_flag_on_link (msg, offset)) { route.lifetime = ndp_msg_opt_prefix_valid_time (msg, offset); - if (add_route (rdisc, &route)) + if (nm_rdisc_add_route (rdisc, &route)) changed |= NM_RDISC_CONFIG_ROUTES; } @@ -562,7 +177,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) /* Add the Interface Identifier to the lower 64 bits */ nm_utils_ipv6_addr_set_interface_identfier (&address.address, rdisc->iid); - if (add_address (rdisc, &address)) + if (nm_rdisc_add_address (rdisc, &address)) changed |= NM_RDISC_CONFIG_ADDRESSES; } } @@ -578,7 +193,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) route.timestamp = now; route.lifetime = ndp_msg_opt_route_lifetime (msg, offset); route.preference = translate_preference (ndp_msg_opt_route_preference (msg, offset)); - if (add_route (rdisc, &route)) + if (nm_rdisc_add_route (rdisc, &route)) changed |= NM_RDISC_CONFIG_ROUTES; } @@ -601,7 +216,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) */ if (dns_server.lifetime && dns_server.lifetime < 7200) dns_server.lifetime = 7200; - if (add_dns_server (rdisc, &dns_server)) + if (nm_rdisc_add_dns_server (rdisc, &dns_server)) changed |= NM_RDISC_CONFIG_DNS_SERVERS; } } @@ -623,7 +238,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) */ if (dns_domain.lifetime && dns_domain.lifetime < 7200) dns_domain.lifetime = 7200; - if (add_dns_domain (rdisc, &dns_domain)) + if (nm_rdisc_add_dns_domain (rdisc, &dns_domain)) changed |= NM_RDISC_CONFIG_DNS_DOMAINS; } } @@ -649,8 +264,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) } } - check_timestamps (rdisc, now, changed); - + nm_rdisc_ra_received (rdisc, now, changed); return 0; } @@ -664,41 +278,60 @@ event_ready (GIOChannel *source, GIOCondition condition, NMRDisc *rdisc) return G_SOURCE_CONTINUE; } -static gboolean -rdisc_ra_timeout_cb (gpointer user_data) -{ - NMLNDPRDisc *rdisc = NM_LNDP_RDISC (user_data); - NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc); - - priv->ra_timeout_id = 0; - g_signal_emit_by_name (rdisc, NM_RDISC_RA_TIMEOUT); - return G_SOURCE_REMOVE; -} - static void start (NMRDisc *rdisc) { NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc); int fd = ndp_get_eventfd (priv->ndp); - guint ra_wait_secs; priv->event_channel = g_io_channel_unix_new (fd); priv->event_id = g_io_add_watch (priv->event_channel, G_IO_IN, (GIOFunc) event_ready, rdisc); - clear_ra_timeout (NM_LNDP_RDISC (rdisc)); - ra_wait_secs = CLAMP (rdisc->rtr_solicitations * rdisc->rtr_solicitation_interval, 30, 120); - priv->ra_timeout_id = g_timeout_add_seconds (ra_wait_secs, rdisc_ra_timeout_cb, rdisc); - debug ("(%s): scheduling RA timeout in %d seconds", rdisc->ifname, ra_wait_secs); - /* Flush any pending messages to avoid using obsolete information */ event_ready (priv->event_channel, 0, rdisc); ndp_msgrcv_handler_register (priv->ndp, receive_ra, NDP_MSG_RA, rdisc->ifindex, rdisc); - solicit (rdisc); + + nm_rdisc_solicit (rdisc); } /******************************************************************/ +static inline gint32 +ipv6_sysctl_get (const char *ifname, const char *property, gint32 defval) +{ + return nm_platform_sysctl_get_int32 (nm_utils_ip6_property_path (ifname, property), defval); +} + +NMRDisc * +nm_lndp_rdisc_new (int ifindex, const char *ifname) +{ + NMRDisc *rdisc; + NMLNDPRDiscPrivate *priv; + int error; + + rdisc = g_object_new (NM_TYPE_LNDP_RDISC, NULL); + + rdisc->ifindex = ifindex; + rdisc->ifname = g_strdup (ifname); + + rdisc->max_addresses = ipv6_sysctl_get (ifname, "max_addresses", + NM_RDISC_MAX_ADDRESSES_DEFAULT); + rdisc->rtr_solicitations = ipv6_sysctl_get (ifname, "router_solicitations", + NM_RDISC_RTR_SOLICITATIONS_DEFAULT); + rdisc->rtr_solicitation_interval = ipv6_sysctl_get (ifname, "router_solicitation_interval", + NM_RDISC_RTR_SOLICITATION_INTERVAL_DEFAULT); + + priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc); + error = ndp_open (&priv->ndp); + if (error != 0) { + g_object_unref (rdisc); + debug ("(%s): error creating socket for NDP; errno=%d", ifname, -error); + return NULL; + } + return rdisc; +} + static void nm_lndp_rdisc_init (NMLNDPRDisc *lndp_rdisc) { @@ -710,14 +343,6 @@ dispose (GObject *object) NMLNDPRDisc *rdisc = NM_LNDP_RDISC (object); NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc); - clear_rs_timeout (rdisc); - clear_ra_timeout (rdisc); - - if (priv->timeout_id) { - g_source_remove (priv->timeout_id); - priv->timeout_id = 0; - } - if (priv->event_id) { g_source_remove (priv->event_id); priv->event_id = 0; @@ -743,4 +368,5 @@ nm_lndp_rdisc_class_init (NMLNDPRDiscClass *klass) object_class->dispose = dispose; rdisc_class->start = start; + rdisc_class->send_rs = send_rs; } diff --git a/src/rdisc/nm-rdisc.c b/src/rdisc/nm-rdisc.c index dd19eed557..044113a70d 100644 --- a/src/rdisc/nm-rdisc.c +++ b/src/rdisc/nm-rdisc.c @@ -22,14 +22,25 @@ #include <stdlib.h> #include <arpa/inet.h> +#include <string.h> #include "nm-rdisc.h" +#include "nm-rdisc-private.h" #include "nm-logging.h" #include "nm-utils.h" #define debug(...) nm_log_dbg (LOGD_IP6, __VA_ARGS__) +typedef struct { + int solicitations_left; + guint send_rs_id; + guint ra_timeout_id; /* first RA timeout */ + guint timeout_id; /* prefix/dns/etc lifetime timeout */ +} NMRDiscPrivate; + +#define NM_RDISC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_RDISC, NMRDiscPrivate)) + G_DEFINE_TYPE (NMRDisc, nm_rdisc, G_TYPE_OBJECT) enum { @@ -42,6 +53,179 @@ static guint signals[LAST_SIGNAL] = { 0 }; /******************************************************************/ +gboolean +nm_rdisc_add_gateway (NMRDisc *rdisc, const NMRDiscGateway *new) +{ + int i, insert_idx = -1; + + for (i = 0; i < rdisc->gateways->len; i++) { + NMRDiscGateway *item = &g_array_index (rdisc->gateways, NMRDiscGateway, i); + + if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) { + if (item->preference != new->preference) { + g_array_remove_index (rdisc->gateways, i--); + continue; + } + memcpy (item, new, sizeof (*new)); + return FALSE; + } + + /* Put before less preferable gateways. */ + if (item->preference < new->preference && insert_idx < 0) + insert_idx = i; + } + + if (new->lifetime) + g_array_insert_val (rdisc->gateways, CLAMP (insert_idx, 0, G_MAXINT), *new); + return TRUE; +} + +gboolean +nm_rdisc_add_address (NMRDisc *rdisc, const NMRDiscAddress *new) +{ + int i; + + for (i = 0; i < rdisc->addresses->len; i++) { + NMRDiscAddress *item = &g_array_index (rdisc->addresses, NMRDiscAddress, i); + + if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) { + gboolean changed = item->timestamp + item->lifetime != new->timestamp + new->lifetime || + item->timestamp + item->preferred != new->timestamp + new->preferred; + + *item = *new; + return changed; + } + } + + /* we create at most max_addresses autoconf addresses. This is different from + * what the kernel does, because it considers *all* addresses (including + * static and other temporary addresses). + **/ + if (rdisc->max_addresses && rdisc->addresses->len >= rdisc->max_addresses) + return FALSE; + + g_array_insert_val (rdisc->addresses, i, *new); + return TRUE; +} + +gboolean +nm_rdisc_add_route (NMRDisc *rdisc, const NMRDiscRoute *new) +{ + int i, insert_idx = -1; + + for (i = 0; i < rdisc->routes->len; i++) { + NMRDiscRoute *item = &g_array_index (rdisc->routes, NMRDiscRoute, i); + + if (IN6_ARE_ADDR_EQUAL (&item->network, &new->network) && item->plen == new->plen) { + if (item->preference != new->preference) { + g_array_remove_index (rdisc->routes, i--); + continue; + } + memcpy (item, new, sizeof (*new)); + return FALSE; + } + + /* Put before less preferable routes. */ + if (item->preference < new->preference && insert_idx < 0) + insert_idx = i; + } + + if (new->lifetime) + g_array_insert_val (rdisc->routes, CLAMP (insert_idx, 0, G_MAXINT), *new); + return TRUE; +} + +gboolean +nm_rdisc_add_dns_server (NMRDisc *rdisc, const NMRDiscDNSServer *new) +{ + int i; + + for (i = 0; i < rdisc->dns_servers->len; i++) { + NMRDiscDNSServer *item = &g_array_index (rdisc->dns_servers, NMRDiscDNSServer, i); + + if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) { + gboolean changed; + + if (new->lifetime == 0) { + g_array_remove_index (rdisc->dns_servers, i); + return TRUE; + } + + changed = (item->timestamp != new->timestamp || + item->lifetime != new->lifetime); + if (changed) { + item->timestamp = new->timestamp; + item->lifetime = new->lifetime; + } + return changed; + } + } + + if (new->lifetime) + g_array_insert_val (rdisc->dns_servers, i, *new); + return TRUE; +} + +/* Copies new->domain if 'new' is added to the dns_domains list */ +gboolean +nm_rdisc_add_dns_domain (NMRDisc *rdisc, const NMRDiscDNSDomain *new) +{ + NMRDiscDNSDomain *item; + int i; + + for (i = 0; i < rdisc->dns_domains->len; i++) { + item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i); + + if (!g_strcmp0 (item->domain, new->domain)) { + gboolean changed; + + if (new->lifetime == 0) { + g_array_remove_index (rdisc->dns_domains, i); + return TRUE; + } + + changed = (item->timestamp != new->timestamp || + item->lifetime != new->lifetime); + if (changed) { + item->timestamp = new->timestamp; + item->lifetime = new->lifetime; + } + return changed; + } + } + + if (new->lifetime) { + g_array_insert_val (rdisc->dns_domains, i, *new); + item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i); + item->domain = g_strdup (new->domain); + } + return TRUE; +} + +/******************************************************************/ + +static void +clear_ra_timeout (NMRDisc *rdisc) +{ + NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc); + + if (priv->ra_timeout_id) { + g_source_remove (priv->ra_timeout_id); + priv->ra_timeout_id = 0; + } +} + +static void +clear_rs_timeout (NMRDisc *rdisc) +{ + NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc); + + if (priv->send_rs_id) { + g_source_remove (priv->send_rs_id); + priv->send_rs_id = 0; + } +} + void nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid) { @@ -50,15 +234,69 @@ nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid) rdisc->iid = iid; } +static gboolean +send_rs (NMRDisc *rdisc) +{ + NMRDiscClass *klass = NM_RDISC_GET_CLASS (rdisc); + NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc); + + debug ("(%s): sending router solicitation", rdisc->ifname); + + if (klass->send_rs (rdisc)) + priv->solicitations_left--; + + if (priv->solicitations_left > 0) { + debug ("(%s): scheduling router solicitation retry in %d seconds.", + rdisc->ifname, rdisc->rtr_solicitation_interval); + priv->send_rs_id = g_timeout_add_seconds (rdisc->rtr_solicitation_interval, + (GSourceFunc) send_rs, rdisc); + } else { + debug ("(%s): did not receive a router advertisement after %d solicitations.", + rdisc->ifname, rdisc->rtr_solicitations); + priv->send_rs_id = 0; + } + + return G_SOURCE_REMOVE; +} + +void +nm_rdisc_solicit (NMRDisc *rdisc) +{ + NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc); + + if (!priv->send_rs_id) { + debug ("(%s): scheduling router solicitation.", rdisc->ifname); + priv->send_rs_id = g_idle_add ((GSourceFunc) send_rs, rdisc); + priv->solicitations_left = rdisc->rtr_solicitations; + } +} + +static gboolean +rdisc_ra_timeout_cb (gpointer user_data) +{ + NMRDisc *rdisc = NM_RDISC (user_data); + + NM_RDISC_GET_PRIVATE (rdisc)->ra_timeout_id = 0; + g_signal_emit_by_name (rdisc, NM_RDISC_RA_TIMEOUT); + return G_SOURCE_REMOVE; +} + void nm_rdisc_start (NMRDisc *rdisc) { + NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc); NMRDiscClass *klass = NM_RDISC_GET_CLASS (rdisc); + guint ra_wait_secs; g_assert (klass->start); debug ("(%s): starting router discovery: %d", rdisc->ifname, rdisc->ifindex); + clear_ra_timeout (rdisc); + ra_wait_secs = CLAMP (rdisc->rtr_solicitations * rdisc->rtr_solicitation_interval, 30, 120); + priv->ra_timeout_id = g_timeout_add_seconds (ra_wait_secs, rdisc_ra_timeout_cb, rdisc); + debug ("(%s): scheduling RA timeout in %d seconds", rdisc->ifname, ra_wait_secs); + if (klass->start) klass->start (rdisc); } @@ -145,6 +383,161 @@ config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed) } } +static void +clean_gateways (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint64 *nextevent) +{ + int i; + + for (i = 0; i < rdisc->gateways->len; i++) { + NMRDiscGateway *item = &g_array_index (rdisc->gateways, NMRDiscGateway, i); + guint64 expiry = (guint64) item->timestamp + item->lifetime; + + if (item->lifetime == G_MAXUINT32) + continue; + + if (now >= expiry) { + g_array_remove_index (rdisc->gateways, i--); + *changed |= NM_RDISC_CONFIG_GATEWAYS; + } else if (*nextevent > expiry) + *nextevent = expiry; + } +} + +static void +clean_addresses (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint64 *nextevent) +{ + int i; + + for (i = 0; i < rdisc->addresses->len; i++) { + NMRDiscAddress *item = &g_array_index (rdisc->addresses, NMRDiscAddress, i); + guint64 expiry = (guint64) item->timestamp + item->lifetime; + + if (item->lifetime == G_MAXUINT32) + continue; + + if (now >= expiry) { + g_array_remove_index (rdisc->addresses, i--); + *changed |= NM_RDISC_CONFIG_ADDRESSES; + } else if (*nextevent > expiry) + *nextevent = expiry; + } +} + +static void +clean_routes (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint64 *nextevent) +{ + int i; + + for (i = 0; i < rdisc->routes->len; i++) { + NMRDiscRoute *item = &g_array_index (rdisc->routes, NMRDiscRoute, i); + guint64 expiry = (guint64) item->timestamp + item->lifetime; + + if (item->lifetime == G_MAXUINT32) + continue; + + if (now >= expiry) { + g_array_remove_index (rdisc->routes, i--); + *changed |= NM_RDISC_CONFIG_ROUTES; + } else if (*nextevent > expiry) + *nextevent = expiry; + } +} + +static void +clean_dns_servers (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint64 *nextevent) +{ + int i; + + for (i = 0; i < rdisc->dns_servers->len; i++) { + NMRDiscDNSServer *item = &g_array_index (rdisc->dns_servers, NMRDiscDNSServer, i); + guint64 expiry = (guint64) item->timestamp + item->lifetime; + guint64 refresh = (guint64) item->timestamp + item->lifetime / 2; + + if (item->lifetime == G_MAXUINT32) + continue; + + if (now >= expiry) { + g_array_remove_index (rdisc->dns_servers, i--); + *changed |= NM_RDISC_CONFIG_DNS_SERVERS; + } else if (now >= refresh) + nm_rdisc_solicit (rdisc); + else if (*nextevent > refresh) + *nextevent = refresh; + } +} + +static void +clean_dns_domains (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint64 *nextevent) +{ + int i; + + for (i = 0; i < rdisc->dns_domains->len; i++) { + NMRDiscDNSDomain *item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i); + guint64 expiry = (guint64) item->timestamp + item->lifetime; + guint64 refresh = (guint64) item->timestamp + item->lifetime / 2; + + if (item->lifetime == G_MAXUINT32) + continue; + + if (now >= expiry) { + g_free (item->domain); + g_array_remove_index (rdisc->dns_domains, i--); + *changed |= NM_RDISC_CONFIG_DNS_DOMAINS; + } else if (now >= refresh) + nm_rdisc_solicit (rdisc); + else if (*nextevent > refresh) + *nextevent = refresh; + } +} + +static gboolean timeout_cb (gpointer user_data); + +static void +check_timestamps (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap changed) +{ + NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc); + /* Use a magic date in the distant future (~68 years) */ + const guint64 never = now + G_MAXINT32; + guint64 nextevent = never; + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + clean_gateways (rdisc, now, &changed, &nextevent); + clean_addresses (rdisc, now, &changed, &nextevent); + clean_routes (rdisc, now, &changed, &nextevent); + clean_dns_servers (rdisc, now, &changed, &nextevent); + clean_dns_domains (rdisc, now, &changed, &nextevent); + + if (changed) + g_signal_emit_by_name (rdisc, NM_RDISC_CONFIG_CHANGED, changed); + + if (nextevent < never) { + g_return_if_fail (nextevent > now); + debug ("(%s): scheduling next now/lifetime check: %lu seconds", + rdisc->ifname, nextevent - now); + priv->timeout_id = g_timeout_add_seconds (nextevent - now, timeout_cb, rdisc); + } +} + +static gboolean +timeout_cb (gpointer user_data) +{ + NM_RDISC_GET_PRIVATE (user_data)->timeout_id = 0; + check_timestamps (NM_RDISC (user_data), nm_utils_get_monotonic_timestamp_s (), 0); + return G_SOURCE_REMOVE; +} + +void +nm_rdisc_ra_received (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap changed) +{ + clear_ra_timeout (rdisc); + clear_rs_timeout (rdisc); + check_timestamps (rdisc, now, changed); +} + /******************************************************************/ static void @@ -159,7 +552,24 @@ nm_rdisc_init (NMRDisc *rdisc) } static void -nm_rdisc_finalize (GObject *object) +dispose (GObject *object) +{ + NMRDisc *rdisc = NM_RDISC (object); + NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc); + + clear_ra_timeout (rdisc); + clear_rs_timeout (rdisc); + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + G_OBJECT_CLASS (nm_rdisc_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) { NMRDisc *rdisc = NM_RDISC (object); @@ -178,8 +588,10 @@ nm_rdisc_class_init (NMRDiscClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->finalize = nm_rdisc_finalize; + g_type_class_add_private (klass, sizeof (NMRDiscPrivate)); + object_class->dispose = dispose; + object_class->finalize = finalize; klass->config_changed = config_changed; signals[CONFIG_CHANGED] = g_signal_new ( diff --git a/src/rdisc/nm-rdisc.h b/src/rdisc/nm-rdisc.h index 2d83f0f020..a79e6c9690 100644 --- a/src/rdisc/nm-rdisc.h +++ b/src/rdisc/nm-rdisc.h @@ -133,7 +133,9 @@ typedef struct { GObjectClass parent; void (*start) (NMRDisc *rdisc); + gboolean (*send_rs) (NMRDisc *rdisc); void (*config_changed) (NMRDisc *rdisc, NMRDiscConfigMap changed); + void (*ra_process) (NMRDisc *rdisc); void (*ra_timeout) (NMRDisc *rdisc); } NMRDiscClass; |