Add Interface listener system (plus linux os implementation)
authorHenning Rogge <henning.rogge@fkie.fraunhofer.de>
Wed, 19 Oct 2011 13:26:31 +0000 (15:26 +0200)
committerHenning Rogge <henning.rogge@fkie.fraunhofer.de>
Wed, 19 Oct 2011 13:26:31 +0000 (15:26 +0200)
18 files changed:
src/common/avl.h
src/common/list.h
src/core/olsr_interface.c [new file with mode: 0644]
src/core/olsr_interface.h [moved from src/core/interface.h with 57% similarity]
src/core/olsr_logging_cfg.c
src/core/olsr_logging_sources.c
src/core/olsr_logging_sources.h
src/core/olsr_packet_socket.c
src/core/olsr_stream_socket.c
src/core/os_net.h
src/core/os_net_generic.c
src/core/os_system.h
src/core/os_system_generic.c
src/olsr_main.c
src/os_bsd/os_system_bsd.h
src/os_linux/os_net_linux.c [new file with mode: 0644]
src/os_linux/os_system_linux.c [new file with mode: 0644]
src/os_linux/os_system_linux.h

index ee91b1e..19a0dbf 100644 (file)
@@ -179,6 +179,15 @@ avl_is_empty(struct avl_tree *tree) {
   return tree->count == 0;
 }
 
+/**
+ * @param node pointer to avl node
+ * @return true if node is currently in a tree, false otherwise
+ */
+static INLINE bool
+avl_is_node_added(struct avl_node *node) {
+  return list_is_node_added(&node->list);
+}
+
 /**
  * Legacy function for code that still use the old avl_delete
  * function instead of the new avl_remove one.
index 18a6f06..a4caeec 100644 (file)
@@ -175,7 +175,7 @@ list_is_empty(struct list_entity *head) {
  *   false otherwise
  */
 static INLINE bool
-list_node_added(struct list_entity *node) {
+list_is_node_added(struct list_entity *node) {
   return node->next != NULL && node->prev != NULL;
 }
 
diff --git a/src/core/olsr_interface.c b/src/core/olsr_interface.c
new file mode 100644 (file)
index 0000000..ecfb2fe
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * olsr_interface.c
+ *
+ *  Created on: Oct 18, 2011
+ *      Author: rogge
+ */
+
+#include "common/common_types.h"
+#include "common/avl.h"
+#include "common/avl_comp.h"
+#include "common/list.h"
+#include "common/netaddr.h"
+
+#include "olsr_interface.h"
+#include "olsr_logging.h"
+#include "olsr_timer.h"
+#include "olsr.h"
+#include "os_net.h"
+
+/* timeinterval to delay change in interface to trigger actions */
+#define OLSR_INTERFACE_CHANGE_INTERVAL 100
+
+static void _interface_add(const char *name);
+static void _interface_remove(const char *name);
+static void _cb_change_handler(void *);
+static void _trigger_change_timer(struct olsr_interface *);
+
+/* global tree of known interfaces */
+struct avl_tree olsr_interface_tree;
+
+/* remember state of subsystem */
+OLSR_SUBSYSTEM_STATE(_interface_state);
+
+static struct list_entity _interface_listener;
+static struct olsr_timer_info *_change_timer_info;
+
+/**
+ * Initialize interface subsystem
+ * @return -1 if an error happened, 0 otherwise
+ */
+int
+olsr_interface_init(void) {
+  if (olsr_subsystem_is_initialized(&_interface_state))
+    return 0;
+
+  _change_timer_info = olsr_timer_add(
+      "Interface change", _cb_change_handler, false);
+  if (_change_timer_info == NULL) {
+    return -1;
+  }
+
+  avl_init(&olsr_interface_tree, avl_comp_strcasecmp, false, NULL);
+  list_init_head(&_interface_listener);
+
+  olsr_subsystem_init(&_interface_state);
+  return 0;
+}
+
+/**
+ * Cleanup interface subsystem
+ */
+void
+olsr_interface_cleanup(void) {
+  struct olsr_interface_listener *listener, *l_it;
+
+  if (olsr_subsystem_cleanup(&_interface_state))
+    return;
+
+  list_for_each_element_safe(&_interface_listener, listener, node, l_it) {
+    olsr_interface_remove_listener(listener);
+  }
+
+  olsr_timer_remove(_change_timer_info);
+}
+
+/**
+ * Add a listener to a specific interface
+ * @param listener initialized listener object
+ */
+void
+olsr_interface_add_listener(struct olsr_interface_listener *listener) {
+  list_add_tail(&_interface_listener, &listener->node);
+  _interface_add(listener->name);
+}
+
+/**
+ * Removes a listener to an interface object
+ * @param listener pointer to listener object
+ */
+void
+olsr_interface_remove_listener(struct olsr_interface_listener *listener) {
+  if (list_is_node_added(&listener->node)) {
+    list_remove(&listener->node);
+    _interface_remove(listener->name);
+  }
+}
+
+
+/**
+ * Trigger a potential change in the interface settings. Normally
+ * called by os_system code
+ * @param name pointer to name of interface
+ */
+void
+olsr_interface_trigger_change(const char *name) {
+  struct olsr_interface *interf;
+
+  interf = avl_find_element(&olsr_interface_tree, name, interf, node);
+  if (interf == NULL) {
+    return;
+  }
+
+  /* trigger interface reload in 100 ms */
+  _trigger_change_timer(interf);
+}
+
+/**
+ * Add an interface to the listener system
+ * @param name pointer to interface name
+ */
+static void
+_interface_add(const char *name) {
+  struct olsr_interface *interf;
+
+  interf = avl_find_element(&olsr_interface_tree, name, interf, node);
+  if (interf) {
+    interf->usage_counter++;
+    return;
+  }
+
+  interf = calloc(1, sizeof(*interf));
+  if (interf == NULL) {
+    OLSR_WARN_OOM(LOG_INTERFACE);
+    return;
+  }
+
+  /* hookup */
+  interf->name = strdup(name);
+  if (interf->name == NULL) {
+    OLSR_WARN_OOM(LOG_INTERFACE);
+    free(interf);
+    return;
+  }
+
+  interf->node.key = interf->name;
+  avl_insert(&olsr_interface_tree, &interf->node);
+
+  /* grab data of interface */
+  os_net_update_interface(&interf->data, interf->name);
+
+  /* trigger update */
+  _trigger_change_timer(interf);
+}
+
+/**
+ * Remove an interface from the listener system. If multiple listeners
+ * share an interface, this will only decrease the reference counter.
+ * @param name pointer to interface name
+ */
+static void
+_interface_remove(const char *name) {
+  struct olsr_interface *interf;
+
+  interf = avl_find_element(&olsr_interface_tree, name, interf, node);
+  if (!interf) {
+    return;
+  }
+
+  if (interf->usage_counter > 0) {
+    interf->usage_counter--;
+    return;
+  }
+
+  avl_remove(&olsr_interface_tree, &interf->node);
+
+  free((void *)interf->name);
+  free(interf);
+}
+
+
+/**
+ * Timer callback to handle potential change of data of an interface
+ * @param ptr pointer to interface object
+ */
+static void
+_cb_change_handler(void *ptr) {
+  struct olsr_interface_data old_data, new_data;
+  struct olsr_interface_listener *listener, *l_it;
+  struct olsr_interface *interf;
+
+  interf = ptr;
+
+  /* read interface data */
+  if (os_net_update_interface(&interf->data, interf->name)) {
+    /* an error happened, try again */
+    _trigger_change_timer(interf);
+    return;
+  }
+
+  /* something changed ? */
+  if (memcmp(&interf->data, &new_data, sizeof(new_data)) == 0) {
+    return;
+  }
+
+  /* copy data to interface object, but remember the old data */
+  memcpy(&old_data, &interf->data, sizeof(old_data));
+  memcpy(&interf->data, &new_data, sizeof(interf->data));
+
+  /* call listeners */
+  list_for_each_element_safe(&_interface_listener, listener, node, l_it) {
+    if (listener->name == NULL || strcmp(listener->name, interf->name) == 0) {
+      listener->process(&old_data);
+    }
+  }
+}
+
+/**
+ * Activate the change timer of an interface object
+ * @param interf pointer to interface object
+ */
+static void
+_trigger_change_timer(struct olsr_interface *interf) {
+  olsr_timer_set(&interf->change_timer,
+      OLSR_INTERFACE_CHANGE_INTERVAL, 0, interf, _change_timer_info);
+}
similarity index 57%
rename from src/core/interface.h
rename to src/core/olsr_interface.h
index 77d2c85..3c50c70 100644 (file)
 #include <net/if.h>
 
 #include "common/common_types.h"
+#include "common/avl.h"
+#include "common/list.h"
 #include "common/netaddr.h"
 
+#include "olsr_timer.h"
+
+struct olsr_interface_data {
+  /* Interface addresses with mesh-wide scope (at least) */
+  struct netaddr if_v4, if_v6;
+
+  /* IPv6 Interface address with global scope */
+  struct netaddr linklocal_v6;
+
+  /* interface index */
+  unsigned index;
+
+  /* true if the interface exists and is up */
+  bool up;
+};
+
 struct olsr_interface {
-  struct netaddr local_v4, local_v6;
-  char name[IF_NAMESIZE];
-  int index;
+  /* hook interfaces into tree */
+  struct avl_node node;
+
+  /* name of interface */
+  const char *name;
+
+  /* data of interface */
+  struct olsr_interface_data data;
+
+  /*
+   * usage counter to allow multiple instances to add the same
+   * interface
+   */
+  int usage_counter;
+
+  /* timer for lazy interface change handling */
+  struct olsr_timer_entry *change_timer;
+};
+
+struct olsr_interface_listener {
+  /* hook into list of listeners */
+  struct list_entity node;
+
+  /* restrict listener to one interface or NULL for all interfaces */
+  const char *name;
+
+  /* callback for interface change */
+  void (*process)(struct olsr_interface_data *old);
 };
 
+#define OLSR_FOR_ALL_INTERFACES(interf, ptr) avl_for_each_element_safe(&olsr_interface_tree, interf, node, ptr)
+EXPORT extern struct avl_tree olsr_interface_tree;
+
+int olsr_interface_init(void) __attribute__((warn_unused_result));
+void olsr_interface_cleanup(void);
+
+EXPORT void olsr_interface_add_listener(struct olsr_interface_listener *);
+EXPORT void olsr_interface_remove_listener(struct olsr_interface_listener *);
+
+EXPORT void olsr_interface_trigger_change(const char *name);
+
 #endif /* INTERFACE_H_ */
index 25971da..f8c2ce3 100644 (file)
@@ -145,13 +145,13 @@ olsr_logcfg_cleanup(void) {
   cfg_delta_remove_handler(olsr_cfg_get_delta(), &logcfg_delta_handler);
 
   /* clean up former handlers */
-  if (list_node_added(&stderr_handler.node)) {
+  if (list_is_node_added(&stderr_handler.node)) {
     olsr_log_removehandler(&stderr_handler);
   }
-  if (list_node_added(&syslog_handler.node)) {
+  if (list_is_node_added(&syslog_handler.node)) {
     olsr_log_removehandler(&syslog_handler);
   }
-  if (list_node_added(&file_handler.node)) {
+  if (list_is_node_added(&file_handler.node)) {
     FILE *f;
 
     f = file_handler.custom;
@@ -237,7 +237,7 @@ olsr_logcfg_apply(struct cfg_db *db) {
 
   /* and finally modify the logging handlers */
   /* log.file */
-  if (activate_file && !list_node_added(&file_handler.node)) {
+  if (activate_file && !list_is_node_added(&file_handler.node)) {
     FILE *f;
 
     f = fopen(file_name, "w");
@@ -250,7 +250,7 @@ olsr_logcfg_apply(struct cfg_db *db) {
       activate_file = false;
     }
   }
-  else if (!activate_file && list_node_added(&file_handler.node)) {
+  else if (!activate_file && list_is_node_added(&file_handler.node)) {
     FILE *f = file_handler.custom;
     olsr_log_removehandler(&file_handler);
 
@@ -263,10 +263,10 @@ olsr_logcfg_apply(struct cfg_db *db) {
     activate_stderr |= !(activate_syslog || activate_file);
   }
 
-  if (activate_stderr && !list_node_added(&stderr_handler.node)) {
+  if (activate_stderr && !list_is_node_added(&stderr_handler.node)) {
     olsr_log_addhandler(&stderr_handler);
   }
-  else if (!activate_stderr && list_node_added(&stderr_handler.node)) {
+  else if (!activate_stderr && list_is_node_added(&stderr_handler.node)) {
     olsr_log_removehandler(&stderr_handler);
   }
 
@@ -275,10 +275,10 @@ olsr_logcfg_apply(struct cfg_db *db) {
     activate_syslog |= !(activate_stderr || activate_file);
   }
 
-  if (activate_syslog && !list_node_added(&syslog_handler.node)) {
+  if (activate_syslog && !list_is_node_added(&syslog_handler.node)) {
     olsr_log_addhandler(&syslog_handler);
   }
-  else if (!activate_syslog && list_node_added(&syslog_handler.node)) {
+  else if (!activate_syslog && list_is_node_added(&syslog_handler.node)) {
     olsr_log_removehandler(&syslog_handler);
   }
 
index 8c50f60..532048e 100644 (file)
@@ -56,6 +56,9 @@ const char *LOG_SOURCE_NAMES[LOG_SOURCE_COUNT] = {
   "memcookie",
   "socket-stream",
   "socket-packet",
+  "interface",
+  "os-net",
+  "os-system",
   "plugin-loader",
   "telnet",
   "plugins",
index 96650aa..894a9cc 100644 (file)
@@ -57,6 +57,9 @@ enum log_source {
   LOG_MEMCOOKIE,
   LOG_SOCKET_STREAM,
   LOG_SOCKET_PACKET,
+  LOG_INTERFACE,
+  LOG_OS_NET,
+  LOG_OS_SYSTEM,
   LOG_PLUGINLOADER,
   LOG_TELNET,
   LOG_PLUGINS,
index 9304f9c..5e168d6 100644 (file)
@@ -128,7 +128,7 @@ olsr_packet_add(struct olsr_packet_socket *pktsocket,
  */
 void
 olsr_packet_remove(struct olsr_packet_socket *pktsocket) {
-  if (list_node_added(&pktsocket->node)) {
+  if (list_is_node_added(&pktsocket->node)) {
     olsr_socket_remove(&pktsocket->scheduler_entry);
     os_close(pktsocket->scheduler_entry.fd);
     list_remove(&pktsocket->node);
index 58d8c28..08a81e7 100644 (file)
@@ -205,7 +205,7 @@ void
 olsr_stream_remove(struct olsr_stream_socket *stream_socket) {
   struct olsr_stream_session *session;
 
-  if (list_node_added(&stream_socket->node)) {
+  if (list_is_node_added(&stream_socket->node)) {
     stream_socket = list_first_element(&olsr_stream_head, stream_socket, node);
     while (!list_is_empty(&stream_socket->session)) {
       session = list_first_element(&stream_socket->session, session, node);
index 82f9773..9ae3700 100644 (file)
@@ -48,7 +48,7 @@
 #include "common/common_types.h"
 #include "common/netaddr.h"
 #include "olsr_logging.h"
-#include "interface.h"
+#include "olsr_interface.h"
 
 /*
  * Set one of the following defines in the os specific os_net includes
@@ -95,6 +95,8 @@ enum olsr_socket_opt {
 };
 
 /* prototypes for all os_net functions */
+EXPORT int os_net_init(void) __attribute__((warn_unused_result));
+EXPORT void os_net_cleanup(void);
 EXPORT int os_net_getsocket(union netaddr_socket *bindto,
     enum olsr_socket_opt flags, int recvbuf, enum log_source log_src);
 EXPORT int os_net_configsocket(int sock, union netaddr_socket *bindto,
@@ -102,6 +104,7 @@ EXPORT int os_net_configsocket(int sock, union netaddr_socket *bindto,
 EXPORT int net_os_join_mcast(int sock, union netaddr_socket *multicast,
     struct olsr_interface *oif, enum log_source log_src);
 EXPORT int os_net_set_nonblocking(int sock);
+EXPORT int os_net_update_interface(struct olsr_interface_data *, const char *);
 EXPORT int os_recvfrom(
     int fd, void *buf, size_t length, union netaddr_socket *source);
 EXPORT int os_sendto(
index ae82eda..1787be6 100644 (file)
@@ -46,6 +46,7 @@
 #include "common/netaddr.h"
 #include "common/string.h"
 #include "olsr_logging.h"
+#include "olsr_interface.h"
 #include "os_net.h"
 
 #if OS_NET_CONFIGSOCKET == OS_GENERIC
@@ -169,7 +170,7 @@ os_net_getsocket(union netaddr_socket *bindto,
  * Join a socket into a multicast group
  * @param sock filedescriptor of socket
  * @param multicast multicast ip/port to join
- * @param oif pointer to outgoing interface for multicast
+ * @param oif pointer to outgoing interface data for multicast
  * @param log_src logging source for error messages
  * @return -1 if an error happened, 0 otherwise
  */
@@ -185,19 +186,14 @@ net_os_join_mcast(int sock, union netaddr_socket *multicast,
   char p;
 
   if (multicast->std.sa_family == AF_INET) {
-    if (!IN_MULTICAST(ntohl(multicast->v4.sin_addr.s_addr))) {
-      /* TODO: silent fail ? */
-      return 0;
-    }
-
     OLSR_DEBUG(log_src,
         "Socket on interface %s joining multicast %s (src %s)\n",
         oif->name,
         netaddr_socket_to_string(&buf2, multicast),
-        netaddr_to_string(&buf1, &oif->local_v4));
+        netaddr_to_string(&buf1, &oif->data.if_v4));
 
     v4_mreq.imr_multiaddr = multicast->v4.sin_addr;
-    netaddr_to_binary(&v4_mreq.imr_interface, &oif->local_v4, 4);
+    netaddr_to_binary(&v4_mreq.imr_interface, &oif->data.if_v4, 4);
 
     if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
         &v4_mreq, sizeof(v4_mreq)) < 0) {
@@ -205,7 +201,7 @@ net_os_join_mcast(int sock, union netaddr_socket *multicast,
       return -1;
     }
 
-    if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, oif->local_v4.addr, 4) < 0) {
+    if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, oif->data.if_v4.addr, 4) < 0) {
       OLSR_WARN(log_src, "Cannot set multicast interface: %s (%d)\n",
           strerror(errno), errno);
       return -1;
@@ -223,10 +219,10 @@ net_os_join_mcast(int sock, union netaddr_socket *multicast,
         "Socket on interface %s joining multicast %s (src %s)\n",
         oif->name,
         netaddr_socket_to_string(&buf2, multicast),
-        netaddr_to_string(&buf1, &oif->local_v6));
+        netaddr_to_string(&buf1, &oif->data.linklocal_v6));
 
     v6_mreq.ipv6mr_multiaddr = multicast->v6.sin6_addr;
-    v6_mreq.ipv6mr_interface = oif->index;
+    v6_mreq.ipv6mr_interface = oif->data.index;
 
     /* Send multicast */
     if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
@@ -236,7 +232,7 @@ net_os_join_mcast(int sock, union netaddr_socket *multicast,
       return -1;
     }
     if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
-        &oif->index, sizeof(oif->index)) < 0) {
+        &oif->data.index, sizeof(oif->data.index)) < 0) {
       OLSR_WARN(log_src, "Cannot set multicast interface: %s (%d)\n",
           strerror(errno), errno);
       return -1;
index 5d665b2..b8ccf09 100644 (file)
@@ -82,6 +82,9 @@
 #undef OS_NET_SPECIFIC_INCLUDE
 
 /* prototypes for all os_system functions */
+EXPORT int os_system_init(void);
+EXPORT void os_system_cleanup(void);
+
 EXPORT void os_system_openlog(void);
 EXPORT void os_system_closelog(void);
 EXPORT void os_system_log(enum log_severity, const char *);
index f0a4008..7fb1b72 100644 (file)
 OLSR_SUBSYSTEM_STATE(_os_log_state);
 #endif
 
+#if OS_SYSTEM_INIT == OS_GENERIC
+
+/**
+ * Do nothing on system initialization
+ * @return 0
+ */
+int
+os_system_init(void) {
+  return 0;
+}
+
+/**
+ * Do nothing on system cleanup
+ */
+void os_system_cleanup(void) {
+}
+
+#endif
 
 #if OS_SYSTEM_LOG == OS_GENERIC
 /**
index 0bee3b7..73e033b 100644 (file)
@@ -61,6 +61,7 @@
 #include "olsr_cfg.h"
 #include "olsr_clock.h"
 #include "olsr_http.h"
+#include "olsr_interface.h"
 #include "olsr_logging.h"
 #include "olsr_logging_cfg.h"
 #include "olsr_memcookie.h"
@@ -233,6 +234,18 @@ main(int argc, char **argv) {
   if (olsr_stream_init()) {
     goto olsrd_cleanup;
   }
+
+  /* activate os-specific code */
+  if (os_system_init()) {
+    goto olsrd_cleanup;
+  }
+
+  /* activate interface listening system */
+  if (olsr_interface_init()) {
+    goto olsrd_cleanup;
+  }
+
+  /* activate telnet and http */
   if (olsr_telnet_init()) {
     goto olsrd_cleanup;
   }
@@ -282,6 +295,8 @@ olsrd_cleanup:
   /* free framework resources */
   olsr_http_cleanup();
   olsr_telnet_cleanup();
+  olsr_interface_cleanup();
+  os_system_cleanup();
   olsr_stream_cleanup();
   olsr_packet_cleanup();
   olsr_socket_cleanup();
index 01a8ef9..f488882 100644 (file)
@@ -15,6 +15,7 @@
 #include "os_helper.h"
 
 /* BSD os_system runs on "all default" */
+#define OS_SYSTEM_INIT         OS_GENERIC
 #define OS_SYSTEM_GETTIMEOFDAY OS_GENERIC
 #define OS_SYSTEM_LOG          OS_GENERIC
 
diff --git a/src/os_linux/os_net_linux.c b/src/os_linux/os_net_linux.c
new file mode 100644 (file)
index 0000000..7fb026d
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * os_net_linux.c
+ *
+ *  Created on: Oct 18, 2011
+ *      Author: rogge
+ */
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <ifaddrs.h>
+
+#include "common/common_types.h"
+
+#include "olsr_cfg.h"
+#include "olsr_interface.h"
+#include "olsr_logging.h"
+#include "olsr.h"
+#include "os_net.h"
+
+static int _ioctl_v4, _ioctl_v6;
+
+OLSR_SUBSYSTEM_STATE(_os_net_state);
+
+/**
+ * Initialize os_net subsystem
+ * @return -1 if an error happened, 0 otherwise
+ */
+int
+os_net_init(void) {
+  if (olsr_subsystem_is_initialized(&_os_net_state))
+    return 0;
+
+  _ioctl_v4 = socket(AF_INET, SOCK_DGRAM, 0);
+  if (_ioctl_v4 == -1) {
+    OLSR_WARN(LOG_OS_NET, "Cannot open ipv4 ioctl socket: %s (%d)",
+        strerror(errno), errno);
+    return -1;
+  }
+
+  if (config_global.ipv6) {
+    _ioctl_v6 = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (_ioctl_v6 == -1) {
+      OLSR_WARN(LOG_OS_NET, "Cannot open ipv6 ioctl socket: %s (%d)",
+          strerror(errno), errno);
+      close(_ioctl_v4);
+      return -1;
+    }
+  }
+  else {
+    _ioctl_v6 = -1;
+  }
+
+  olsr_subsystem_init(&_os_net_state);
+  return 0;
+}
+
+/**
+ * Cleanup os_net subsystem
+ */
+void
+os_net_cleanup(void) {
+  if (olsr_subsystem_cleanup(&_os_net_state))
+    return;
+
+  if (_ioctl_v4 != -1) {
+    close (_ioctl_v4);
+    _ioctl_v4 = -1;
+  }
+  if (_ioctl_v6 != -1) {
+    close (_ioctl_v6);
+    _ioctl_v6 = -1;
+  }
+}
+
+/**
+ * Updates the data of an interface.
+ * @param interf pointer to interface object.
+ * @param name name of interface
+ * @return -1 if an error happened, 0 otherwise
+ */
+int
+os_net_update_interface(struct olsr_interface_data *data,
+    const char *name) {
+  struct ifaddrs *ifaddr, *ifa;
+  union netaddr_socket *sock;
+  struct netaddr addr;
+  struct ifreq ifr;
+
+  memset(data, 0, sizeof(*data));
+
+  /* get interface index */
+  data->index = if_nametoindex(name);
+
+  if (data->index == 0) {
+    return 0;
+  }
+
+  if (getifaddrs(&ifaddr) == -1) {
+    OLSR_WARN(LOG_OS_NET, "Cannot get interface addresses: %s (%d)",
+        strerror(errno), errno);
+    return -1;
+  }
+
+  for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+    if (strcmp(ifa->ifa_name, name) != 0) {
+      continue;
+    }
+
+    sock = (union netaddr_socket *)ifa->ifa_addr;
+
+    if (netaddr_from_socket(&addr, sock)) {
+      /* just ignore other interfaces */
+      continue;
+    }
+
+    if (addr.type == AF_INET) {
+      memcpy(&data->if_v4, &addr, sizeof(data->if_v4));
+    }
+    else if (addr.type == AF_INET6) {
+      if (IN6_IS_ADDR_LINKLOCAL(addr.addr)) {
+        memcpy(&data->linklocal_v6, &addr, sizeof(data->linklocal_v6));
+      }
+      else if (!(IN6_IS_ADDR_LOOPBACK(addr.addr)
+          || IN6_IS_ADDR_MULTICAST(addr.addr)
+          || IN6_IS_ADDR_UNSPECIFIED(addr.addr)
+          || IN6_IS_ADDR_V4COMPAT(addr.addr)
+          || IN6_IS_ADDR_V4MAPPED(addr.addr))) {
+        memcpy(&data->if_v6, &addr, sizeof(data->if_v6));
+      }
+    }
+  }
+
+  memset(&ifr, 0, sizeof(ifr));
+  strscpy(ifr.ifr_name, name, IFNAMSIZ);
+
+  if (ioctl(_ioctl_v4, SIOCGIFFLAGS, &ifr) < 0) {
+    OLSR_WARN(LOG_OS_NET,
+        "ioctl SIOCGIFFLAGS (get flags) error on device %s: %s (%d)\n",
+        name, strerror(errno), errno);
+    return -1;
+  }
+
+  if ((ifr.ifr_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP|IFF_RUNNING)) {
+    data->up = true;
+  }
+
+  return 0;
+}
diff --git a/src/os_linux/os_system_linux.c b/src/os_linux/os_system_linux.c
new file mode 100644 (file)
index 0000000..039a3b6
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * os_netlink.c
+ *
+ *  Created on: Oct 19, 2011
+ *      Author: rogge
+ */
+
+#include <sys/socket.h>
+#include <linux/types.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "common/common_types.h"
+#include "olsr_interface.h"
+#include "olsr_socket.h"
+#include "olsr.h"
+#include "os_system.h"
+
+#define NETLINK_BUFFER_SIZE 4096
+
+static void _netlink_handler(int fd, void *data,
+    bool event_read, bool event_write);
+static void _handle_nl_link(void);
+static void _handle_nl_addr(void);
+
+static int _rtnetlink_fd = -1;
+static struct olsr_socket_entry _rtnetlink_socket = {
+  .process = _netlink_handler,
+  .event_read = true,
+};
+
+/* static buffers for reading a netlink message */
+static struct iovec _netlink_iov;
+static struct sockaddr_nl _netlink_nladdr;
+static struct msghdr _netlink_msg = {
+  &_netlink_nladdr,
+  sizeof(_netlink_nladdr),
+  &_netlink_iov,
+  1,
+  NULL,
+  0,
+  0
+};
+
+struct nlmsghdr *_netlink_header;
+
+OLSR_SUBSYSTEM_STATE(_os_system_state);
+
+int
+os_system_init(void) {
+  struct sockaddr_nl addr;
+
+  if (olsr_subsystem_is_initialized(&_os_system_state)) {
+    return 0;
+  }
+
+  _netlink_header = calloc(NETLINK_BUFFER_SIZE, 1);
+  if (_netlink_header == NULL) {
+    OLSR_WARN_OOM(LOG_OS_SYSTEM);
+    return -1;
+  }
+  _netlink_iov.iov_base = _netlink_header;
+  _netlink_iov.iov_len = NETLINK_BUFFER_SIZE;
+
+  _rtnetlink_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+  if (_rtnetlink_fd < 0) {
+    OLSR_WARN(LOG_OS_SYSTEM, "Cannot open rtnetlink socket: %s (%d)",
+        strerror(errno), errno);
+    free(_netlink_header);
+    return -1;
+  }
+
+  memset(&addr, 0, sizeof(addr));
+  addr.nl_family = AF_NETLINK;
+  addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
+
+  /* kernel will assign appropiate number instead of pid */
+  addr.nl_pid = 0;
+
+  if (bind(_rtnetlink_fd, (struct sockaddr *)&addr, sizeof(addr))<0) {
+    OLSR_WARN(LOG_OS_SYSTEM, "Could not bind rtnetlink socket: %s (%d)",
+        strerror(errno), errno);
+    close (_rtnetlink_fd);
+    free(_netlink_header);
+    return -1;
+  }
+
+  /* add socket listener */
+  _rtnetlink_socket.fd = _rtnetlink_fd;
+  olsr_socket_add(&_rtnetlink_socket);
+
+  olsr_subsystem_init(&_os_system_state);
+  return 0;
+}
+
+void
+os_system_cleanup(void) {
+  if (olsr_subsystem_cleanup(&_os_system_state))
+    return;
+
+  close(_rtnetlink_fd);
+}
+
+static void
+_netlink_handler(int fd,
+    void *data __attribute__((unused)),
+    bool event_read,
+    bool event_write __attribute__((unused))) {
+  int len, plen;
+  int ret;
+
+  if (!event_read) {
+    return;
+  }
+
+  if ((ret = recvmsg(fd, &_netlink_msg, MSG_DONTWAIT)) >= 0) {
+    /*check message*/
+    len = _netlink_header->nlmsg_len;
+    plen = len - sizeof(_netlink_header);
+    if (len > ret || plen < 0) {
+      OLSR_WARN(LOG_OS_SYSTEM,
+          "Malformed netlink message: len=%d left=%d plen=%d\n",
+              len, ret, plen);
+      return;
+    }
+
+    OLSR_DEBUG(LOG_OS_SYSTEM,
+        "Netlink message received: type %d\n", _netlink_header->nlmsg_type);
+
+    switch (_netlink_header->nlmsg_type) {
+      case RTM_NEWLINK:
+      case RTM_DELLINK:
+        _handle_nl_link();
+        break;
+
+      case RTM_NEWADDR:
+      case RTM_DELADDR:
+        _handle_nl_addr();
+        break;
+      default:
+        break;
+    }
+  }
+  else if (errno != EAGAIN) {
+    OLSR_WARN(LOG_OS_SYSTEM,"netlink recvmsg error: %s (%d)\n",
+        strerror(errno), errno);
+  }
+}
+
+static void
+_handle_nl_link(void) {
+  struct ifinfomsg *ifi;
+  char if_name[IF_NAMESIZE];
+
+  ifi = (struct ifinfomsg *) NLMSG_DATA(_netlink_header);
+
+  if (if_indextoname(ifi->ifi_index, if_name) == NULL) {
+    OLSR_WARN(LOG_OS_SYSTEM,
+        "Failed to convert if-index to name: %d", ifi->ifi_index);
+    return;
+  }
+
+  OLSR_DEBUG(LOG_OS_SYSTEM, "Linkstatus of interface '%s' changed", if_name);
+  olsr_interface_trigger_change(if_name);
+}
+
+static void
+_handle_nl_addr(void) {
+  struct ifaddrmsg *ifa;
+
+  char if_name[IF_NAMESIZE];
+
+  ifa = (struct ifaddrmsg *) NLMSG_DATA(_netlink_header);
+
+  if (if_indextoname(ifa->ifa_index, if_name) == NULL) {
+    OLSR_WARN(LOG_OS_SYSTEM,
+        "Failed to convert if-index to name: %d", ifa->ifa_index);
+    return;
+  }
+
+  OLSR_DEBUG(LOG_OS_SYSTEM, "Address of interface '%s' changed", if_name);
+  olsr_interface_trigger_change(if_name);
+}
index 56776b9..4678da3 100644 (file)
@@ -14,7 +14,8 @@
 
 #include "os_helper.h"
 
-/* Linux os_system runs on "all default" */
+/* Linux os_system runs on "all default" except for init/cleanup */
+#define OS_SYSTEM_INIT         OS_SPECIFIC
 #define OS_SYSTEM_GETTIMEOFDAY OS_GENERIC
 #define OS_SYSTEM_LOG          OS_GENERIC