Hi all,

please find attached the first attempt to dynamically update iroute table. Diff 
is against OpenVPN 2.1.1 source.

There are some caveats, though:

- Only tested (and probably only works) with linux based systems, yet.

- It only works with "topology subnet". The problem is that only "topology 
subnet" ensures the correct determination of the gateway behind the kernel 
route in iroute table.

- We filter for zebra routing messages. Therefore a running quagga daemon is 
needed. For testing purposes, one can manually add routes through zebra.

Any comments and thoughts are welcome.

Regards,
Sebastian

diff -rupN openvpn.orig/dynroute.h openvpn.patch/dynroute.h
--- openvpn.orig/dynroute.h1970-01-01 01:00:00.000000000 +0100
+++ openvpn.patch/dynroute.h2010-05-27 17:49:50.000000000 +0200
@@ -0,0 +1,370 @@
+//#include <stdbool.h>
+
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <string.h>
+#include <fcntl.h>
+
+#ifndef DYNROUTE_H
+#define DYNROUTE_H
+
+#define DYNROUTE_DEBUG_CHATTY 99
+#define DYNROUTE_DEBUG DYNROUTE_DEBUG_CHATTY-1
+
+#define DRA_NONE 0
+#define DRA_NEW 1
+#define DRA_REP 2
+#define DRA_DEL 3
+
+struct dynroute
+{
+int sock;
+int last_error;
+int count;
+bool ready;
+bool nope;//* 2come
+int action;
+in_addr_t gateway;
+struct iroute ir;
+};
+
+static struct multi_instance *
+multi_learn_in_addr_t (struct multi_context *m,
+       struct multi_instance *mi,
+       in_addr_t a,
+       int netbits, /* -1 if host route, otherwise # of network bits in 
address */
+       bool primary);
+
+/* Utility function for parse rtattr. */
+static void netlink_parse_rtattr (struct rtattr **tb, int max, struct rtattr 
*rta, int len)
+{
+while (RTA_OK (rta, len))
+{
+if (rta->rta_type <= max)
+tb[rta->rta_type] = rta;
+rta = RTA_NEXT (rta, len);
+}
+}
+
+static uint32_t endian_swap (uint32_t x)
+{
+x = (x>>24) | 
+((x<<8) & 0x00FF0000) |
+((x>>8) & 0x0000FF00) |
+(x<<24);
+return x;
+}
+
+static bool dynroute_init (struct dynroute *dr)
+{
+if (dr->ready != true)
+{
+dr->count = 0;
+dr->last_error = 0;
+dr->action = DRA_NONE;
+dr->ready = false;
+dr->nope = false;
+dr->sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+if (dr->sock < 0)
+{
+#ifdef DYNROUTE_DEBUG
+dr->last_error = dr->sock;
+fprintf (stderr, "dynroute_init: ERROR socket() : %d\n", dr->last_error);
+#endif
+return dr->ready;
+}
+struct sockaddr_nl addr;
+memset ((void *) &addr, 0, sizeof (addr));
+addr.nl_family = AF_NETLINK;
+addr.nl_pid = getpid ();
+addr.nl_groups = RTMGRP_IPV4_ROUTE;
+dr->last_error = bind (dr->sock, (struct sockaddr *) &addr, sizeof (addr));
+if (dr->last_error < 0)
+{
+#ifdef DYNROUTE_DEBUG
+fprintf (stderr, "dynroute_init: ERROR bind() : %d\n", dr->last_error);
+#endif
+return dr->ready;
+}
+dr->last_error = fcntl (dr->sock, F_SETFL, O_NONBLOCK);
+if (dr->last_error < 0)
+{
+#ifdef DYNROUTE_DEBUG
+fprintf (stderr, "dynroute_init: ERROR fcntl() : %d\n", dr->last_error);
+#endif
+return dr->ready;
+}
+#ifdef DYNROUTE_DEBUG
+fprintf (stderr, "dynroute_init: OK\n");
+#endif
+dr->ready = true;
+}
+#ifdef DYNROUTE_DEBUG
+else
+{
+fprintf (stderr, "dynroute_init: allready initialised, skipped.\n");
+}
+#endif
+return dr->ready;
+}//* int open_socket ()
+
+static bool dynroute_read (struct dynroute *dr)
+{
+dr->action = DRA_NONE;
+bool retval = false;
+if (dr->ready != true)
+{
+#ifdef DYNROUTE_DEBUG
+fprintf (stderr, "dynroute_read: not ready, trying dynroute_init().\n");
+#endif
+if (dynroute_init (dr) != true)
+return dr->ready;
+}
+
+struct nlmsghdr *nlh;
+struct rtmsg *rtm;
+struct sockaddr_nl addr;
+struct rtattr *tb[RTA_MAX + 1];
+
+char buf [4096];
+struct iovec iov = { buf, sizeof (buf) };
+struct msghdr msg;
+int len;
+
+msg.msg_name = (void *) &addr;
+msg.msg_namelen = sizeof (addr);
+msg.msg_iov = &iov;
+msg.msg_iovlen = 1;
+
+dr->last_error = recvmsg (dr->sock, &msg, 0);
+if (dr->last_error < 0)
+{
+#ifdef DYNROUTE_DEBUG
+if (DYNROUTE_DEBUG == DYNROUTE_DEBUG_CHATTY)
+fprintf (stderr, "dynroute_read: recvmsg() : %d\n", dr->last_error);
+#endif
+return false;
+}
+nlh = (struct nlmsghdr *) buf;
+rtm = NLMSG_DATA(nlh);
+
+if (rtm->rtm_protocol != RTPROT_ZEBRA)
+{
+#ifdef DYNROUTE_DEBUG
+//if (DYNROUTE_DEBUG == DYNROUTE_DEBUG_CHATTY)
+fprintf (stderr, "dynroute_read: no zebra\n");
+#endif
+return false;
+}
+
+
+len = nlh->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg));
+dr->last_error = len;
+if (dr->last_error < 0)
+{
+#ifdef DYNROUTE_DEBUG
+fprintf (stderr, "dynroute_read: ERROR nlh->nlmsg_len : %d\n", dr->last_error);
+#endif
+return false;
+}
+
+memset (tb, 0, sizeof tb);
+netlink_parse_rtattr (tb, RTA_MAX, RTM_RTA (rtm), len);
+
+in_addr_t *dstp;
+in_addr_t *gw;
+
+if (tb[RTA_GATEWAY])
+gw = RTA_DATA (tb[RTA_GATEWAY]);
+if (tb[RTA_DST])
+dstp = RTA_DATA (tb[RTA_DST]);
+
+in_addr_t *srcp;
+if (tb[RTA_SRC])
+srcp = RTA_DATA (tb[RTA_SRC]);
+if (tb[RTA_MULTIPATH])
+{
+//fprintf (stderr, "\nRTA_MULTIPATH\n");
+      struct rtnexthop *rtnh =
+        (struct rtnexthop *) RTA_DATA (tb[RTA_MULTIPATH]);
+      len = RTA_PAYLOAD (tb[RTA_MULTIPATH]);
+
+      if (len < (int) sizeof (*rtnh))
+        return 0;
+      if (rtnh->rtnh_len > len)
+        return 0;
+
+      int index = rtnh->rtnh_ifindex;
+      if (rtnh->rtnh_len > sizeof (*rtnh))
+        {
+          memset (tb, 0, sizeof (tb));
+          netlink_parse_rtattr (tb, RTA_MAX, RTNH_DATA (rtnh),
+                                rtnh->rtnh_len - sizeof (*rtnh));
+          if (tb[RTA_GATEWAY])
+            gw = RTA_DATA (tb[RTA_GATEWAY]);
+        }
+    }
+
+/*
++  if (tb[RTA_MULTIPATH])
++    {
++      //* Look for the interface and gateway from the first path *
++      struct rtnexthop *rtnh =
++        (struct rtnexthop *) RTA_DATA (tb[RTA_MULTIPATH]);
++      len = RTA_PAYLOAD (tb[RTA_MULTIPATH]);
++
++      if (len < (int) sizeof (*rtnh))
++        return 0;
++      if (rtnh->rtnh_len > len)
++        return 0;
++
++      index = rtnh->rtnh_ifindex;
++      if (rtnh->rtnh_len > sizeof (*rtnh))
++        {
++          memset (tb, 0, sizeof (tb));
++          netlink_parse_rtattr (tb, RTA_MAX, RTNH_DATA (rtnh),
++                                rtnh->rtnh_len - sizeof (*rtnh));
++          if (tb[RTA_GATEWAY])
++            gate = RTA_DATA (tb[RTA_GATEWAY]);
++        }
++    }
+*/
+//fprintf (stderr, "dynroute_read: rtm_dst/src_len : %d/%d\n", 
rtm->rtm_dst_len,rtm->rtm_src_len);
+
+#ifdef DYNROUTE_DEBUG
+if (DYNROUTE_DEBUG == DYNROUTE_DEBUG_CHATTY)
+fprintf (stderr, "dynroute_read: [#%d]", dr->count++);
+//fprintf (stderr, "[PROT %d]", rtm->rtm_protocol);
+#endif
+
+switch (nlh->nlmsg_type)
+{
+case RTM_NEWROUTE:
+switch (nlh->nlmsg_flags)
+{
+case 0:
+case NLM_F_CREATE:
+dr->action = DRA_NEW;
+#ifdef DYNROUTE_DEBUG
+if (DYNROUTE_DEBUG == DYNROUTE_DEBUG_CHATTY)
+fprintf (stderr, "[NEW]");
+#endif
+break;
+case NLM_F_REPLACE:
+dr->action = DRA_REP;
+#ifdef DYNROUTE_DEBUG
+if (DYNROUTE_DEBUG == DYNROUTE_DEBUG_CHATTY)
+fprintf (stderr, "[CHANGED]");
+#endif
+break;
+default:
+dr->action = DRA_NONE;
+#ifdef DYNROUTE_DEBUG
+if (DYNROUTE_DEBUG == DYNROUTE_DEBUG_CHATTY)
+fprintf (stderr, "[Dunno what %d means (nlh->nlmsg_flags)]", nlh->nlmsg_flags);
+#endif
+break;
+}
+break;
+case RTM_DELROUTE:
+dr->action = DRA_DEL;
+#ifdef DYNROUTE_DEBUG
+if (DYNROUTE_DEBUG == DYNROUTE_DEBUG_CHATTY)
+fprintf (stderr, "[DEL]");
+#endif
+break;
+}
+dr->gateway = endian_swap (*gw);
+dr->ir.network = endian_swap (*dstp);
+dr->ir.netbits = rtm->rtm_dst_len;
+#ifdef DYNROUTE_DEBUG
+if (DYNROUTE_DEBUG == DYNROUTE_DEBUG_CHATTY)
+fprintf (stderr, "dynroute_read: OK\n");
+#endif
+return dr->ready;
+}
+
+static bool dynroute_re (struct multi_context *m, struct iroute ir)
+{
+bool ret = false;
+struct openvpn_sockaddr remote_si;
+struct mroute_addr addr;
+struct hash_element *he;
+
+CLEAR (remote_si);
+remote_si.sa.sin_family = AF_INET;
+remote_si.sa.sin_addr.s_addr = htonl (ir.network);
+ASSERT (mroute_extract_openvpn_sockaddr (&addr, &remote_si, false));
+if (ir.netbits >= 0)
+{
+addr.type |= MR_WITH_NETBITS;
+addr.netbits = (uint8_t) ir.netbits;
+}
+const uint32_t hv = hash_value (m->vhash, &addr);
+struct hash_bucket *bucket = hash_bucket (m->vhash, hv);
+struct multi_route *oldroute = NULL;
+
+hash_bucket_lock (bucket);
+/* if route currently exists, get the instance which owns it */
+he = hash_lookup_fast (m->vhash, bucket, &addr, hv);
+if (he)
+oldroute = (struct multi_route *) he->value;
+hash_bucket_unlock (bucket);
+ret = (oldroute && multi_route_defined (m, oldroute));
+return ret;
+}
+
+static bool dynroute_eval (struct multi_context *m, struct dynroute *dr)
+{
+bool ret = true;
+if (dr->action > DRA_NONE)// route added / deleted
+{
+struct hash_iterator hi;
+struct hash_element *he;
+hash_iterator_init (m->hash, &hi, true);
+while ((he = hash_iterator_next (&hi)))
+{
+struct gc_arena gc = gc_new ();
+struct multi_instance *mi = (struct multi_instance *) he->value;
+struct openvpn_sockaddr remote_si;
+struct mroute_addr addr;
+if (!mi->halt)
+{
+if (mi->reporting_addr == dr->gateway)// route gateway matches instance ip
+{
+switch (dr->action)
+{
+case DRA_NEW:
+msg (D_MULTI_LOW, "DYNROUTE: New: %s/%d -> %s",
+print_in_addr_t (dr->ir.network, IA_EMPTY_IF_UNDEF, &gc),
+dr->ir.netbits,
+multi_instance_string (mi, false, &gc));
+mroute_helper_add_iroute (m->route_helper, (struct iroute *) &dr->ir);
+multi_learn_in_addr_t (m, mi, dr->ir.network, dr->ir.netbits, false);
+break;
+case DRA_DEL:
+if (dynroute_re (m, dr->ir))
+{
+mroute_helper_del_iroute (m->route_helper, (struct iroute *) &dr->ir);
+msg (D_MULTI_LOW, "DYNROUTE: Delete: %s/%d -> %s",
+print_in_addr_t (dr->ir.network, IA_EMPTY_IF_UNDEF, &gc), dr->ir.netbits,
+print_in_addr_t (dr->gateway, IA_EMPTY_IF_UNDEF, &gc));
+}
+break;
+default:
+msg (D_MULTI_LOW, "DYNROUTE: UNKNOWN ACTION: %d", dr->action);
+break;
+}
+}// if (mi->reporting_addr == dr->gateway)
+}// if (!mi->halt)
+gc_free (&gc);
+}// while ((he = hash_iterator_next (&hi)))
+hash_iterator_free (&hi);
+}// if (dr->action > DRA_NONE)
+return ret;
+}
+
+#endif /* DYNROUTE_H */
diff -rupN openvpn.orig/multi.c openvpn.patch/multi.c
--- openvpn.orig/multi.c2009-10-24 17:17:30.000000000 +0200
+++ openvpn.patch/multi.c2010-05-31 12:39:09.406250000 +0200
@@ -37,6 +37,9 @@
 #include "forward-inline.h"
 #include "pf-inline.h"
 
+#include "dynroute.h"
+struct dynroute dr;
+
 /*#define MULTI_DEBUG_EVENT_LOOP*/
 
 #ifdef MULTI_DEBUG_EVENT_LOOP
@@ -2266,6 +2269,9 @@ multi_process_timeout (struct multi_cont
 {
   bool ret = true;
 
+ if (dynroute_read (&dr))// checking socket
+dynroute_eval (m, &dr);
+
 #ifdef MULTI_DEBUG_EVENT_LOOP
   printf ("%s -> TIMEOUT\n", id(m->earliest_wakeup));
 #endif




Reply via email to