rdisc: libndp implementation

This commit is contained in:
Pavel Šimerda
2013-05-31 15:49:07 +02:00
parent 21a8a71d48
commit c3a4656a68
4 changed files with 705 additions and 2 deletions

631
src/rdisc/nm-lndp-rdisc.c Normal file
View 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
View 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 */

View File

@@ -5,7 +5,8 @@ AM_CPPFLAGS = \
-I${top_srcdir}/libnm-util \
-I${srcdir}/.. \
$(GLIB_CFLAGS) \
$(LIBNL_CFLAGS)
$(LIBNL_CFLAGS) \
$(LIBNDP_CFLAGS)
AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
AM_LDFLAGS = $(GLIB_LIBS) $(CODE_COVERAGE_LDFLAGS)
@@ -19,4 +20,6 @@ rdisc_SOURCES = \
rdisc.c \
../nm-rdisc.c \
../nm-fake-rdisc.c \
../nm-lndp-rdisc.c \
../../logging/nm-logging.c
rdisc_LDADD = $(LIBNDP_LIBS)

View File

@@ -1,7 +1,10 @@
#include <string.h>
#include <syslog.h>
#include <net/if.h>
#include "nm-rdisc.h"
#include "nm-fake-rdisc.h"
#include "nm-lndp-rdisc.h"
#include "nm-logging.h"
int
@@ -9,13 +12,30 @@ main (int argc, char **argv)
{
GMainLoop *loop;
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 ();
loop = g_main_loop_new (NULL, FALSE);
nm_logging_setup ("debug", NULL, NULL);
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);
g_main_loop_run (loop);