detect interfaces that are going down (via rtnetlink)
authorMarkus Kittenberger <Markus.Kittenberger@gmx.at>
Sat, 19 Sep 2009 22:43:47 +0000 (00:43 +0200)
committerMarkus Kittenberger <Markus.Kittenberger@gmx.at>
Sat, 19 Sep 2009 22:43:47 +0000 (00:43 +0200)
src/ifnet.h
src/kernel_routes.h
src/linux/kernel_routes.c
src/main.c
src/olsr_cfg.h
src/unix/ifnet.c
src/win32/ifnet.c

index b4e0b99..ea60090 100644 (file)
@@ -60,6 +60,8 @@ void check_interface_updates(void *);
 
 int chk_if_changed(struct olsr_if *);
 
+void RemoveInterface(struct olsr_if *, bool);
+
 int chk_if_up(struct olsr_if *, int);
 
 int add_hemu_if(struct olsr_if *);
index 0208119..646a41f 100644 (file)
@@ -55,6 +55,12 @@ int olsr_ioctl_del_route6(const struct rt_entry *);
 
 #if LINUX_POLICY_ROUTING
 int olsr_netlink_rule(uint8_t, uint8_t, uint16_t);
+
+#if LINUX_RTNETLINK_LISTEN
+int rtnetlink_register_socket(int);
+void rtnetlink_read(int);
+#endif /*LINUX_RTNETLINK_LISTEN*/
+
 #endif
 
 #endif
index adc3e7a..5e44123 100644 (file)
@@ -63,12 +63,115 @@ static int delete_all_inet_gws(void);
 #include <linux/types.h>
 #include <linux/rtnetlink.h>
 
+extern struct rtnl_handle rth;
+
 struct olsr_rtreq {
   struct nlmsghdr n;
   struct rtmsg r;
   char buf[512];
 };
 
+#if LINUX_RTNETLINK_LISTEN
+#include "ifnet.h"
+#include "socket_parser.h"
+
+int rtnetlink_register_socket(int rtnl_mgrp)
+{
+  int sock = socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE);
+  struct sockaddr_nl addr;
+
+  if (sock<0) {
+    OLSR_PRINTF(1,"could not create rtnetlink socket! %d",sock);
+  }
+  else {
+    addr.nl_family = AF_NETLINK;
+    addr.nl_pid = 0; //kernel will assign appropiate number instead of pid (which is already used by primaray rtnetlink socket to add/delete routes)
+    addr.nl_groups = rtnl_mgrp;
+    if (bind(sock,(struct sockaddr *)&addr,sizeof(addr))<0) {
+      OLSR_PRINTF(1,"could not bind socket! (%d %s)",errno,strerror(errno));
+    }
+    else {
+      add_olsr_socket(sock, &rtnetlink_read);
+    }
+    fcntl(sock, F_SETFL, O_NONBLOCK);
+  }
+  return sock;
+}
+
+static void netlink_process_link(struct nlmsghdr *h)
+{
+  struct ifinfomsg *ifi = (struct ifinfomsg *) NLMSG_DATA(h);
+  struct interface *iface;
+  
+  //all IFF flags: LOOPBACK,BROADCAST;POINTOPOINT;MULTICAST;NOARP;ALLMULTI;PROMISC;MASTER;SLAVE;DEBUG;DYNAMIC;AUTOMEDIA;PORTSEL;NOTRAILERS;UP;LOWER_UP;DORMANT
+
+  /* check if interface is up and running? (a not running interface keeps its routes, so better not react like on ifdown!!??) */
+  if (ifi->ifi_flags&IFF_UP) {
+    OLSR_PRINTF(3,"interface %s changed but is still up! ", iface->int_name);
+    return; //we are currently only interested in interfaces that are/go down
+  } else {
+    OLSR_PRINTF(1,"interface %s is down! ", iface->int_name);
+  }
+
+  iface = if_ifwithindex(ifi->ifi_index);
+
+  //only for still configured interfaces (ifup has to be detected with regular interface polling)
+  if ( iface != NULL ) {
+    struct olsr_if *tmp_if;
+    for (tmp_if = olsr_cnf->interfaces; tmp_if != NULL; tmp_if = tmp_if->next) {
+      if (tmp_if->interf==iface) {
+        OLSR_PRINTF(1,"-> removing %s from olsr config! ", iface->int_name);
+        RemoveInterface(tmp_if,true);
+        break;
+      }
+    }
+  }
+}
+
+
+void rtnetlink_read(int sock)
+{
+  int len, plen;
+  struct iovec iov;
+  struct sockaddr_nl nladdr;
+  struct msghdr msg = {
+    &nladdr,
+    sizeof(nladdr),
+    &iov,
+    1,
+    NULL,
+    0,
+    0
+  };
+
+  char buffer[4096];
+  struct nlmsghdr *nlh = (struct nlmsghdr *)(ARM_NOWARN_ALIGN) buffer;
+  int ret;
+
+  iov.iov_base = (void *) buffer;
+  iov.iov_len = sizeof(buffer);
+
+  while (true) { //read until ret<0;
+    ret=recvmsg(sock, &msg, 0);
+    if (ret<0) {
+      if (errno != EAGAIN) OLSR_PRINTF(1,"\nnetlink listen error %u - %s",errno,strerror(errno));
+      return;
+    }
+    /*check message*/
+    len = nlh->nlmsg_len;
+    plen = len - sizeof(nlh);
+    if (len > ret || plen < 0) {
+      OLSR_PRINTF(1,"Malformed netlink message: "
+             "len=%d left=%d plen=%d",
+              len, ret, plen);
+      return;
+    }
+    if ( (nlh->nlmsg_type == RTM_NEWLINK) || ( nlh->nlmsg_type == RTM_DELLINK) ) netlink_process_link(nlh);
+  }
+}
+
+#endif /*linux_rtnetlink_listen*/
+
 static void
 olsr_netlink_addreq(struct olsr_rtreq *req, int type, const void *data, int len)
 {
index 0620a54..239abb6 100644 (file)
@@ -424,6 +424,13 @@ int main(int argc, char *argv[]) {
   if ((olsr_cnf->rttable < 253) & (olsr_cnf->rttable > 0)) {
     olsr_netlink_rule(olsr_cnf->ip_version, olsr_cnf->rttable, RTM_NEWRULE);
   }
+
+  /* Create rtnetlink socket to listen on interface change events RTMGRP_LINK and RTMGRP_IPV4_ROUTE */
+
+#if LINUX_RTNETLINK_LISTEN
+  rtnetlink_register_socket(RTMGRP_LINK);
+#endif /*LINUX_RTNETLINK_LISTEN*/
+
 #endif
 
   /* Start syslog entry */
index 8b1ce47..8e7c762 100644 (file)
 
 #include "olsr_types.h"
 
+/* set to 1 to enable a second rtnetlink socket 
+ * used for listening and reating on interface change events
+ * (requires LINUX_POLICY_ROUTING to be enabled aswell) */
+#define LINUX_RTNETLINK_LISTEN 1
+
 #define TESTLIB_PATH 0
 #define SYSLOG_NUMBERING 0
 #define SOURCE_IP_ROUTES 0
index d4b3560..634a03c 100644 (file)
@@ -144,7 +144,7 @@ check_interface_updates(void *foo __attribute__ ((unused)))
 int
 chk_if_changed(struct olsr_if *iface)
 {
-  struct interface *ifp, *tmp_ifp;
+  struct interface *ifp;
   struct ifreq ifr;
   struct sockaddr_in6 tmp_saddr6;
   int if_changes;
@@ -360,6 +360,20 @@ chk_if_changed(struct olsr_if *iface)
   return if_changes;
 
 remove_interface:
+
+RemoveInterface(iface, false);
+
+return 0;
+}
+
+/*should move to interfaces.c*/
+void 
+RemoveInterface(struct olsr_if * iface, bool went_down)
+{
+  struct interface *ifp, *tmp_ifp;
+  struct rt_entry *rt;
+  ifp = iface->interf;
+
   OLSR_PRINTF(1, "Removing interface %s\n", iface->name);
   olsr_syslog(OLSR_LOG_INFO, "Removing interface %s\n", iface->name);
 
@@ -370,6 +384,16 @@ remove_interface:
    */
   run_ifchg_cbs(ifp, IFCHG_IF_REMOVE);
 
+  /*remove all routes*/
+    if (went_down) {
+    OLSR_FOR_ALL_RT_ENTRIES(rt) {
+      if (rt->rt_nexthop.iif_index == ifp->if_index) {
+       rt->rt_nexthop.iif_index=-1;//marks route as unexisting in kernel, do this better !?
+      }
+    }
+    OLSR_FOR_ALL_RT_ENTRIES_END(rt);
+  }
+
   /* Dequeue */
   if (ifp == ifnet) {
     ifnet = ifp->int_next;
@@ -425,8 +449,6 @@ remove_interface:
     kill(getpid(), SIGINT);
   }
 
-  return 0;
-
 }
 
 /**
index 356f60d..f09890c 100644 (file)
@@ -118,7 +118,6 @@ char *StrError(unsigned int ErrNo);
 
 void ListInterfaces(void);
 int GetIntInfo(struct InterfaceInfo *Info, char *Name);
-void RemoveInterface(struct olsr_if *IntConf);
 
 #define MAX_INTERFACES 100
 
@@ -487,7 +486,7 @@ ListInterfaces(void)
 }
 
 void
-RemoveInterface(struct olsr_if *IntConf)
+RemoveInterface(struct olsr_if *IntConf, bool went_down)
 {
   struct interface *Int, *Prev;
 
@@ -497,6 +496,9 @@ RemoveInterface(struct olsr_if *IntConf)
 
   run_ifchg_cbs(Int, IFCHG_IF_ADD);
 
+  /*remove all routes*/
+  if (went_down) OLSR_PRINTF(1,"Hint: ifdown handling unimplemented");
+
   if (Int == ifnet)
     ifnet = Int->int_next;
 
@@ -709,7 +711,7 @@ chk_if_changed(struct olsr_if *IntConf)
   Int = IntConf->interf;
 
   if (GetIntInfo(&Info, IntConf->name) < 0) {
-    RemoveInterface(IntConf);
+    RemoveInterface(IntConf,false);
     return 1;
   }