static int receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) { NMRDisc *rdisc = (NMRDisc *) user_data; NMRDiscConfigMap changed = 0; struct ndp_msgra *msgra = ndp_msgra (msg); NMRDiscGateway gateway; guint32 now = nm_utils_get_monotonic_timestamp_s (); int offset; int hop_limit; /* Router discovery is subject to the following RFC documents: * * http://tools.ietf.org/html/rfc4861 * http://tools.ietf.org/html/rfc4862 * * The biggest difference from good old DHCP is that all configuration * items have their own lifetimes and they are merged from various * sources. Router discovery is *not* contract-based, so there is *no* * single time when the configuration is finished and updates can * come at any time. */ _LOGD ("received router advertisement at %u", now); /* DHCP level: * * The problem with DHCP level is what to do if subsequent * router advertisements carry different flags. Currently we just * rewrite the flag with every inbound RA. */ { NMRDiscDHCPLevel dhcp_level; if (ndp_msgra_flag_managed (msgra)) dhcp_level = NM_RDISC_DHCP_LEVEL_MANAGED; else if (ndp_msgra_flag_other (msgra)) dhcp_level = NM_RDISC_DHCP_LEVEL_OTHERCONF; else dhcp_level = NM_RDISC_DHCP_LEVEL_NONE; if (dhcp_level != rdisc->dhcp_level) { rdisc->dhcp_level = dhcp_level; changed |= NM_RDISC_CONFIG_DHCP_LEVEL; } } /* Default gateway: * * Subsequent router advertisements can represent new default gateways * on the network. We should present all of them in router preference * order. */ memset (&gateway, 0, sizeof (gateway)); gateway.address = *ndp_msg_addrto (msg); gateway.timestamp = now; gateway.lifetime = ndp_msgra_router_lifetime (msgra); gateway.preference = translate_preference (ndp_msgra_route_preference (msgra)); if (nm_rdisc_add_gateway (rdisc, &gateway)) changed |= NM_RDISC_CONFIG_GATEWAYS; /* Addresses & Routes */ ndp_msg_opt_for_each_offset (offset, msg, NDP_MSG_OPT_PREFIX) { NMRDiscRoute route; NMRDiscAddress address; /* Device route */ memset (&route, 0, sizeof (route)); route.plen = ndp_msg_opt_prefix_len (msg, offset); nm_utils_ip6_address_clear_host_address (&route.network, ndp_msg_opt_prefix (msg, offset), route.plen); route.timestamp = now; if (ndp_msg_opt_prefix_flag_on_link (msg, offset)) { route.lifetime = ndp_msg_opt_prefix_valid_time (msg, offset); if (nm_rdisc_add_route (rdisc, &route)) changed |= NM_RDISC_CONFIG_ROUTES; } /* Address */ if (ndp_msg_opt_prefix_flag_auto_addr_conf (msg, offset)) { if (route.plen == 64 && rdisc->iid.id) { memset (&address, 0, sizeof (address)); address.address = route.network; address.timestamp = now; address.lifetime = ndp_msg_opt_prefix_valid_time (msg, offset); address.preferred = ndp_msg_opt_prefix_preferred_time (msg, offset); if (address.preferred > address.lifetime) address.preferred = address.lifetime; /* Add the Interface Identifier to the lower 64 bits */ nm_utils_ipv6_addr_set_interface_identfier (&address.address, rdisc->iid); if (nm_rdisc_add_address (rdisc, &address)) changed |= NM_RDISC_CONFIG_ADDRESSES; } } }
static gboolean receive_ra (gpointer user_data) { NMFakeRDisc *self = user_data; NMFakeRDiscPrivate *priv = NM_FAKE_RDISC_GET_PRIVATE (self); NMRDisc *rdisc = NM_RDISC (self); FakeRa *ra = priv->ras->data; NMRDiscConfigMap changed = 0; guint32 now = nm_utils_get_monotonic_timestamp_s (); guint i; priv->receive_ra_id = 0; if (rdisc->dhcp_level != ra->dhcp_level) { rdisc->dhcp_level = ra->dhcp_level; changed |= NM_RDISC_CONFIG_DHCP_LEVEL; } for (i = 0; i < ra->gateways->len; i++) { NMRDiscGateway *item = &g_array_index (ra->gateways, NMRDiscGateway, i); if (nm_rdisc_add_gateway (rdisc, item)) changed |= NM_RDISC_CONFIG_GATEWAYS; } for (i = 0; i < ra->addresses->len; i++) { NMRDiscAddress *item = &g_array_index (ra->addresses, NMRDiscAddress, i); if (nm_rdisc_add_address (rdisc, item)) changed |= NM_RDISC_CONFIG_ADDRESSES; } for (i = 0; i < ra->routes->len; i++) { NMRDiscRoute *item = &g_array_index (ra->routes, NMRDiscRoute, i); if (nm_rdisc_add_route (rdisc, item)) changed |= NM_RDISC_CONFIG_ROUTES; } for (i = 0; i < ra->dns_servers->len; i++) { NMRDiscDNSServer *item = &g_array_index (ra->dns_servers, NMRDiscDNSServer, i); if (nm_rdisc_add_dns_server (rdisc, item)) changed |= NM_RDISC_CONFIG_DNS_SERVERS; } for (i = 0; i < ra->dns_domains->len; i++) { NMRDiscDNSDomain *item = &g_array_index (ra->dns_domains, NMRDiscDNSDomain, i); if (nm_rdisc_add_dns_domain (rdisc, item)) changed |= NM_RDISC_CONFIG_DNS_DOMAINS; } if (rdisc->mtu != ra->mtu) { rdisc->mtu = ra->mtu; changed |= NM_RDISC_CONFIG_MTU; } if (rdisc->hop_limit != ra->hop_limit) { rdisc->hop_limit = ra->hop_limit; changed |= NM_RDISC_CONFIG_HOP_LIMIT; } priv->ras = g_slist_remove (priv->ras, priv->ras->data); fake_ra_free (ra); nm_rdisc_ra_received (NM_RDISC (self), now, changed); /* Schedule next RA */ if (priv->ras) { ra = priv->ras->data; priv->receive_ra_id = g_timeout_add_seconds (ra->when, receive_ra, self); } return G_SOURCE_REMOVE; }