rdisc: libndp implementation
This commit is contained in:
631
src/rdisc/nm-lndp-rdisc.c
Normal file
631
src/rdisc/nm-lndp-rdisc.c
Normal file
@@ -0,0 +1,631 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||||
|
/* nm-lndp-rdisc.c - Router discovery implementation using libndp
|
||||||
|
*
|
||||||
|
* 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 <string.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
/* stdarg.h included because of a bug in ndp.h */
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <ndp.h>
|
||||||
|
|
||||||
|
#include "nm-lndp-rdisc.h"
|
||||||
|
|
||||||
|
#include "nm-logging.h"
|
||||||
|
|
||||||
|
#define debug(...) nm_log_dbg (LOGD_IP6, __VA_ARGS__)
|
||||||
|
#define warning(...) nm_log_warn (LOGD_IP6, __VA_ARGS__)
|
||||||
|
#define error(...) nm_log_err (LOGD_IP6, __VA_ARGS__)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct ndp *ndp;
|
||||||
|
|
||||||
|
guint send_rs_id;
|
||||||
|
GIOChannel *event_channel;
|
||||||
|
guint event_id;
|
||||||
|
guint timeout_id;
|
||||||
|
} NMLNDPRDiscPrivate;
|
||||||
|
|
||||||
|
#define NM_LNDP_RDISC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LNDP_RDISC, NMLNDPRDiscPrivate))
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (NMLNDPRDisc, nm_lndp_rdisc, NM_TYPE_RDISC)
|
||||||
|
|
||||||
|
/******************************************************************/
|
||||||
|
|
||||||
|
NMRDisc *
|
||||||
|
nm_lndp_rdisc_new (int ifindex, const char *ifname)
|
||||||
|
{
|
||||||
|
NMRDisc *rdisc = g_object_new (NM_TYPE_LNDP_RDISC, NULL);
|
||||||
|
|
||||||
|
g_assert (rdisc);
|
||||||
|
|
||||||
|
rdisc->ifindex = ifindex;
|
||||||
|
rdisc->ifname = g_strdup (ifname);
|
||||||
|
|
||||||
|
return rdisc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
add_gateway (NMRDisc *rdisc, const NMRDiscGateway *new)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
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)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_insert_val (rdisc->gateways, i, *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)) {
|
||||||
|
memcpy (item, new, sizeof (*new));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_insert_val (rdisc->addresses, i, *new);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
add_route (NMRDisc *rdisc, const NMRDiscRoute *new)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
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)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_insert_val (rdisc->routes, i, *new);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
add_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))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_insert_val (rdisc->dns_servers, i, *new);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
add_domain (NMRDisc *rdisc, const NMRDiscDNSDomain *new)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < rdisc->dns_domains->len; i++) {
|
||||||
|
NMRDiscDNSDomain *item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i);
|
||||||
|
|
||||||
|
if (!g_strcmp0 (item->domain, new->domain))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_insert_val (rdisc->dns_domains, i, *new);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RETRY 10
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
send_rs (NMRDisc *rdisc)
|
||||||
|
{
|
||||||
|
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
|
||||||
|
struct ndp_msg *msg;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = ndp_msg_new (&msg, NDP_MSG_RS);
|
||||||
|
g_assert (!error);
|
||||||
|
ndp_msg_ifindex_set (msg, rdisc->ifindex);
|
||||||
|
|
||||||
|
debug ("(%s): sending router solicitation: %d", rdisc->ifname, rdisc->ifindex);
|
||||||
|
|
||||||
|
error = ndp_msg_send (priv->ndp, msg);
|
||||||
|
if (error)
|
||||||
|
error ("(%s): cannot send router solicitation: %d.", rdisc->ifname, error);
|
||||||
|
|
||||||
|
ndp_msg_destroy (msg);
|
||||||
|
|
||||||
|
debug ("(%s): scheduling router solicitation retry in %d seconds.", rdisc->ifname, RETRY);
|
||||||
|
priv->send_rs_id = g_timeout_add_seconds (RETRY, (GSourceFunc) send_rs, rdisc);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clean_gateways (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < rdisc->gateways->len; i++) {
|
||||||
|
NMRDiscGateway *item = &g_array_index (rdisc->gateways, NMRDiscGateway, i);
|
||||||
|
guint32 expiry = item->timestamp + item->lifetime;
|
||||||
|
|
||||||
|
if (item->lifetime == UINT_MAX)
|
||||||
|
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, guint32 *nextevent)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < rdisc->addresses->len; i++) {
|
||||||
|
NMRDiscAddress *item = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
|
||||||
|
guint32 expiry = item->timestamp + item->lifetime;
|
||||||
|
|
||||||
|
if (item->lifetime == UINT_MAX)
|
||||||
|
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, guint32 *nextevent)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < rdisc->routes->len; i++) {
|
||||||
|
NMRDiscRoute *item = &g_array_index (rdisc->routes, NMRDiscRoute, i);
|
||||||
|
guint32 expiry = item->timestamp + item->lifetime;
|
||||||
|
|
||||||
|
if (item->lifetime == UINT_MAX)
|
||||||
|
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_servers (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < rdisc->dns_servers->len; i++) {
|
||||||
|
NMRDiscDNSServer *item = &g_array_index (rdisc->dns_servers, NMRDiscDNSServer, i);
|
||||||
|
guint32 expiry = item->timestamp + item->lifetime;
|
||||||
|
guint32 refresh = item->timestamp + item->lifetime / 2;
|
||||||
|
|
||||||
|
if (item->lifetime == UINT_MAX)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (now >= expiry) {
|
||||||
|
g_array_remove_index (rdisc->dns_servers, i--);
|
||||||
|
*changed |= NM_RDISC_CONFIG_ROUTES;
|
||||||
|
} else if (now >= refresh)
|
||||||
|
solicit (rdisc);
|
||||||
|
else if (*nextevent > refresh)
|
||||||
|
*nextevent = refresh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clean_domains (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < rdisc->dns_domains->len; i++) {
|
||||||
|
NMRDiscDNSDomain *item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i);
|
||||||
|
guint32 expiry = item->timestamp + item->lifetime;
|
||||||
|
guint32 refresh = item->timestamp + item->lifetime / 2;
|
||||||
|
|
||||||
|
if (item->lifetime == UINT_MAX)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (now >= expiry) {
|
||||||
|
g_array_remove_index (rdisc->dns_domains, i--);
|
||||||
|
*changed |= NM_RDISC_CONFIG_ROUTES;
|
||||||
|
} 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 distant enough future as there's no guint32 max macro. */
|
||||||
|
guint32 never = G_MAXINT32;
|
||||||
|
guint32 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_servers (rdisc, now, &changed, &nextevent);
|
||||||
|
clean_domains (rdisc, now, &changed, &nextevent);
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
g_signal_emit_by_name (rdisc, NM_RDISC_CONFIG_CHANGED, changed);
|
||||||
|
|
||||||
|
if (nextevent != never) {
|
||||||
|
debug ("Scheduling next now/lifetime check: %d seconds", (int) nextevent);
|
||||||
|
priv->timeout_id = g_timeout_add_seconds (nextevent, timeout_cb, rdisc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint32
|
||||||
|
get_time (void)
|
||||||
|
{
|
||||||
|
struct timespec tp;
|
||||||
|
|
||||||
|
clock_gettime (CLOCK_MONOTONIC, &tp);
|
||||||
|
|
||||||
|
return tp.tv_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
timeout_cb (gpointer user_data)
|
||||||
|
{
|
||||||
|
check_timestamps (user_data, get_time (), 0);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NMRDiscPreference
|
||||||
|
translate_preference (enum ndp_route_preference preference)
|
||||||
|
{
|
||||||
|
switch (preference) {
|
||||||
|
case NDP_ROUTE_PREF_LOW:
|
||||||
|
return NM_RDISC_PREFERENCE_LOW;
|
||||||
|
case NDP_ROUTE_PREF_MEDIUM:
|
||||||
|
return NM_RDISC_PREFERENCE_MEDIUM;
|
||||||
|
case NDP_ROUTE_PREF_HIGH:
|
||||||
|
return NM_RDISC_PREFERENCE_HIGH;
|
||||||
|
default:
|
||||||
|
return NM_RDISC_PREFERENCE_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fill_address_from_mac (struct in6_addr *address, const char *mac)
|
||||||
|
{
|
||||||
|
unsigned char *identifier = address->s6_addr + 8;
|
||||||
|
|
||||||
|
if (!mac)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Translate 48-bit MAC address to a 64-bit modified interface identifier
|
||||||
|
* and write it to the second half of the IPv6 address.
|
||||||
|
*
|
||||||
|
* See http://tools.ietf.org/html/rfc3513#page-21
|
||||||
|
*/
|
||||||
|
memcpy (identifier, mac, 3);
|
||||||
|
identifier[0] ^= 0x02;
|
||||||
|
identifier[3] = 0xff;
|
||||||
|
identifier[4] = 0xfe;
|
||||||
|
memcpy (identifier + 5, mac + 3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
|
||||||
|
{
|
||||||
|
NMRDisc *rdisc = (NMRDisc *) user_data;
|
||||||
|
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
|
||||||
|
NMRDiscConfigMap changed = 0;
|
||||||
|
size_t lladdrlen;
|
||||||
|
const char *lladdr = g_bytes_get_data (rdisc->lladdr, &lladdrlen);
|
||||||
|
struct ndp_msgra *msgra = ndp_msgra (msg);
|
||||||
|
NMRDiscGateway gateway;
|
||||||
|
guint32 now = get_time ();
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
debug ("Recieved router advertisement: %d at %d", rdisc->ifindex, (int) now);
|
||||||
|
|
||||||
|
if (priv->send_rs_id) {
|
||||||
|
g_source_remove (priv->send_rs_id);
|
||||||
|
priv->send_rs_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 (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.network = *ndp_msg_opt_prefix (msg, offset);
|
||||||
|
route.plen = ndp_msg_opt_prefix_len (msg, offset);
|
||||||
|
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))
|
||||||
|
changed |= NM_RDISC_CONFIG_ROUTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Address */
|
||||||
|
if (ndp_msg_opt_prefix_flag_auto_addr_conf (msg, offset)) {
|
||||||
|
if (route.plen == 64 && lladdrlen == 6) {
|
||||||
|
memset (&address, 0, sizeof (address));
|
||||||
|
address.address = route.network;
|
||||||
|
address.timestamp = now;
|
||||||
|
address.lifetime = ndp_msg_opt_prefix_valid_time (msg, offset);
|
||||||
|
address.preferred_lft = ndp_msg_opt_prefix_preferred_time (msg, offset);
|
||||||
|
|
||||||
|
fill_address_from_mac (&address.address, lladdr);
|
||||||
|
|
||||||
|
if (add_address (rdisc, &address))
|
||||||
|
changed |= NM_RDISC_CONFIG_ADDRESSES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_ROUTE) {
|
||||||
|
NMRDiscRoute route;
|
||||||
|
|
||||||
|
/* Routers through this particular gateway */
|
||||||
|
memset (&route, 0, sizeof (route));
|
||||||
|
route.gateway = gateway.address;
|
||||||
|
route.network = *ndp_msg_opt_route_prefix (msg, offset);
|
||||||
|
route.plen = ndp_msg_opt_route_prefix_len (msg, offset);
|
||||||
|
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))
|
||||||
|
changed |= NM_RDISC_CONFIG_ROUTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DNS information */
|
||||||
|
ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_RDNSS) {
|
||||||
|
static struct in6_addr *addr;
|
||||||
|
int addr_index;
|
||||||
|
|
||||||
|
ndp_msg_opt_rdnss_for_each_addr (addr, addr_index, msg, offset) {
|
||||||
|
NMRDiscDNSServer dns_server;
|
||||||
|
|
||||||
|
memset (&dns_server, 0, sizeof (dns_server));
|
||||||
|
dns_server.address = *addr;
|
||||||
|
dns_server.timestamp = now;
|
||||||
|
dns_server.lifetime = ndp_msg_opt_rdnss_lifetime (msg, offset);
|
||||||
|
/* Pad the lifetime somewhat to give a bit of slack in cases
|
||||||
|
* where one RA gets lost or something (which can happen on unreliable
|
||||||
|
* links like WiFi where certain types of frames are not retransmitted).
|
||||||
|
* Note that 0 has special meaning and is therefore not adjusted.
|
||||||
|
*/
|
||||||
|
if (dns_server.lifetime && dns_server.lifetime < 7200)
|
||||||
|
dns_server.lifetime = 7200;
|
||||||
|
if (add_server (rdisc, &dns_server))
|
||||||
|
changed |= NM_RDISC_CONFIG_DNS_SERVERS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_DNSSL) {
|
||||||
|
char *domain;
|
||||||
|
int domain_index;
|
||||||
|
|
||||||
|
ndp_msg_opt_dnssl_for_each_domain (domain, domain_index, msg, offset) {
|
||||||
|
NMRDiscDNSDomain dns_domain;
|
||||||
|
|
||||||
|
memset (&dns_domain, 0, sizeof (dns_domain));
|
||||||
|
dns_domain.domain = g_strdup (domain);
|
||||||
|
dns_domain.timestamp = now;
|
||||||
|
dns_domain.lifetime = ndp_msg_opt_rdnss_lifetime (msg, offset);
|
||||||
|
/* Pad the lifetime somewhat to give a bit of slack in cases
|
||||||
|
* where one RA gets lost or something (which can happen on unreliable
|
||||||
|
* links like WiFi where certain types of frames are not retransmitted).
|
||||||
|
* Note that 0 has special meaning and is therefore not adjusted.
|
||||||
|
*/
|
||||||
|
if (dns_domain.lifetime && dns_domain.lifetime < 7200)
|
||||||
|
dns_domain.lifetime = 7200;
|
||||||
|
if (add_domain (rdisc, &dns_domain))
|
||||||
|
changed |= NM_RDISC_CONFIG_DNS_DOMAINS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check_timestamps (rdisc, now, changed);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
process_events (NMRDisc *rdisc)
|
||||||
|
{
|
||||||
|
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
|
||||||
|
|
||||||
|
debug ("(%s): processing libndp events.", rdisc->ifname);
|
||||||
|
ndp_callall_eventfd_handler (priv->ndp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
event_ready (GIOChannel *source, GIOCondition condition, NMRDisc *rdisc)
|
||||||
|
{
|
||||||
|
process_events (rdisc);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
start (NMRDisc *rdisc)
|
||||||
|
{
|
||||||
|
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
|
||||||
|
int fd = ndp_get_eventfd (priv->ndp);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* Flush any pending messages to avoid using obsolete information */
|
||||||
|
process_events (rdisc);
|
||||||
|
|
||||||
|
ndp_msgrcv_handler_register (priv->ndp, &receive_ra, NDP_MSG_RA, rdisc->ifindex, rdisc);
|
||||||
|
solicit (rdisc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
nm_lndp_rdisc_init (NMLNDPRDisc *lndp_rdisc)
|
||||||
|
{
|
||||||
|
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (lndp_rdisc);
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = ndp_open (&priv->ndp);
|
||||||
|
g_assert (!error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nm_lndp_rdisc_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (object);
|
||||||
|
|
||||||
|
if (priv->send_rs_id)
|
||||||
|
g_source_remove (priv->send_rs_id);
|
||||||
|
if (priv->timeout_id)
|
||||||
|
g_source_remove (priv->timeout_id);
|
||||||
|
if (priv->event_channel)
|
||||||
|
g_io_channel_unref (priv->event_channel);
|
||||||
|
if (priv->event_id)
|
||||||
|
g_source_remove (priv->event_id);
|
||||||
|
|
||||||
|
ndp_close (priv->ndp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nm_lndp_rdisc_class_init (NMLNDPRDiscClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
NMRDiscClass *rdisc_class = NM_RDISC_CLASS (klass);
|
||||||
|
|
||||||
|
g_type_class_add_private (klass, sizeof (NMLNDPRDiscPrivate));
|
||||||
|
|
||||||
|
object_class->finalize = nm_lndp_rdisc_finalize;
|
||||||
|
rdisc_class->start = start;
|
||||||
|
}
|
49
src/rdisc/nm-lndp-rdisc.h
Normal file
49
src/rdisc/nm-lndp-rdisc.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||||
|
/* nm-lndp-rdisc.h - Implementation of router discovery using libndp
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NM_LNDP_RDISC_H
|
||||||
|
#define NM_LNDP_RDISC_H
|
||||||
|
|
||||||
|
#include "nm-rdisc.h"
|
||||||
|
|
||||||
|
#define NM_TYPE_LNDP_RDISC (nm_lndp_rdisc_get_type ())
|
||||||
|
#define NM_LNDP_RDISC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_LNDP_RDISC, NMLNDPRDisc))
|
||||||
|
#define NM_LNDP_RDISC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_LNDP_RDISC, NMLNDPRDiscClass))
|
||||||
|
#define NM_IS_LNDP_RDISC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_LNDP_RDISC))
|
||||||
|
#define NM_IS_LNDP_RDISC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_LNDP_RDISC))
|
||||||
|
#define NM_LNDP_RDISC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_LNDP_RDISC, NMLNDPRDiscClass))
|
||||||
|
|
||||||
|
/******************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
NMRDisc parent;
|
||||||
|
} NMLNDPRDisc;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
NMRDiscClass parent;
|
||||||
|
} NMLNDPRDiscClass;
|
||||||
|
|
||||||
|
/******************************************************************/
|
||||||
|
|
||||||
|
GType nm_lndp_rdisc_get_type (void);
|
||||||
|
|
||||||
|
NMRDisc *nm_lndp_rdisc_new (int ifindex, const char *ifname);
|
||||||
|
|
||||||
|
#endif /* NM_LNDP_RDISC_H */
|
@@ -5,7 +5,8 @@ AM_CPPFLAGS = \
|
|||||||
-I${top_srcdir}/libnm-util \
|
-I${top_srcdir}/libnm-util \
|
||||||
-I${srcdir}/.. \
|
-I${srcdir}/.. \
|
||||||
$(GLIB_CFLAGS) \
|
$(GLIB_CFLAGS) \
|
||||||
$(LIBNL_CFLAGS)
|
$(LIBNL_CFLAGS) \
|
||||||
|
$(LIBNDP_CFLAGS)
|
||||||
|
|
||||||
AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
|
AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
|
||||||
AM_LDFLAGS = $(GLIB_LIBS) $(CODE_COVERAGE_LDFLAGS)
|
AM_LDFLAGS = $(GLIB_LIBS) $(CODE_COVERAGE_LDFLAGS)
|
||||||
@@ -19,4 +20,6 @@ rdisc_SOURCES = \
|
|||||||
rdisc.c \
|
rdisc.c \
|
||||||
../nm-rdisc.c \
|
../nm-rdisc.c \
|
||||||
../nm-fake-rdisc.c \
|
../nm-fake-rdisc.c \
|
||||||
|
../nm-lndp-rdisc.c \
|
||||||
../../logging/nm-logging.c
|
../../logging/nm-logging.c
|
||||||
|
rdisc_LDADD = $(LIBNDP_LIBS)
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
|
#include <string.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
#include "nm-rdisc.h"
|
#include "nm-rdisc.h"
|
||||||
#include "nm-fake-rdisc.h"
|
#include "nm-fake-rdisc.h"
|
||||||
|
#include "nm-lndp-rdisc.h"
|
||||||
#include "nm-logging.h"
|
#include "nm-logging.h"
|
||||||
|
|
||||||
int
|
int
|
||||||
@@ -9,13 +12,30 @@ main (int argc, char **argv)
|
|||||||
{
|
{
|
||||||
GMainLoop *loop;
|
GMainLoop *loop;
|
||||||
NMRDisc *rdisc;
|
NMRDisc *rdisc;
|
||||||
|
NMRDisc *(*new) (int ifindex, const char *ifname) = nm_lndp_rdisc_new;
|
||||||
|
int ifindex = 1;
|
||||||
|
char ifname[IF_NAMESIZE];
|
||||||
|
char mac[6] = { 0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0xee };
|
||||||
|
|
||||||
|
if_indextoname (ifindex, ifname);
|
||||||
|
|
||||||
g_type_init ();
|
g_type_init ();
|
||||||
loop = g_main_loop_new (NULL, FALSE);
|
loop = g_main_loop_new (NULL, FALSE);
|
||||||
nm_logging_setup ("debug", NULL, NULL);
|
nm_logging_setup ("debug", NULL, NULL);
|
||||||
openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR, LOG_DAEMON);
|
openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR, LOG_DAEMON);
|
||||||
|
|
||||||
rdisc = nm_fake_rdisc_new (1);
|
argv++;
|
||||||
|
for (; *argv; argv++) {
|
||||||
|
if (!g_strcmp0 (*argv, "--fake"))
|
||||||
|
new = nm_fake_rdisc_new;
|
||||||
|
else {
|
||||||
|
strncpy (ifname, *argv, IF_NAMESIZE);
|
||||||
|
ifindex = if_nametoindex (ifname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rdisc = new (ifindex, ifname);
|
||||||
|
nm_rdisc_set_lladdr (rdisc, mac, 6);
|
||||||
|
|
||||||
nm_rdisc_start (rdisc);
|
nm_rdisc_start (rdisc);
|
||||||
g_main_loop_run (loop);
|
g_main_loop_run (loop);
|
||||||
|
Reference in New Issue
Block a user