route get/set working, remotecontrol plugin updated
authorHenning Rogge <henning.rogge@fkie.fraunhofer.de>
Fri, 24 Feb 2012 14:22:27 +0000 (15:22 +0100)
committerHenning Rogge <henning.rogge@fkie.fraunhofer.de>
Fri, 24 Feb 2012 14:22:27 +0000 (15:22 +0100)
12 files changed:
lib/httptelnet/src/httptelnet.c
lib/remotecontrol/src/remotecontrol.c
src/common/netaddr.c
src/core/olsr_logging.h
src/core/olsr_telnet.c
src/core/olsr_telnet.h
src/core/os_linux/os_routing_linux.c
src/core/os_linux/os_routing_linux.h
src/core/os_linux/os_system_linux.c
src/core/os_linux/os_system_linux.h
src/core/os_routing.h
src/core/os_system.h

index 59eac50..91309f0 100644 (file)
@@ -144,7 +144,7 @@ _cb_generate_site(struct autobuf *out, struct olsr_http_session *session) {
       session->content_type = HTTP_CONTENTTYPE_TEXT;
       return HTTP_200_OK;
 
-    case TELNET_RESULT_UNKNOWN_COMMAND:
+    case _TELNET_RESULT_UNKNOWN_COMMAND:
       return HTTP_404_NOT_FOUND;
 
     default:
index 0b649f8..329e7b4 100644 (file)
@@ -72,6 +72,8 @@ struct _remotecontrol_session {
   struct olsr_telnet_cleanup cleanup;
 
   struct log_handler_mask_entry *mask;
+
+  struct os_route route;
 };
 
 /* prototypes */
@@ -97,6 +99,8 @@ static void _stop_logging(struct olsr_telnet_data *data);
 static void _cb_print_log(struct log_handler_entry *,
     struct log_parameters *);
 
+static void _cb_route_finished(struct os_route *, int error);
+static void _cb_route_get(struct os_route *filter, struct os_route *route);
 
 static void _cb_config_changed(void);
 static struct _remotecontrol_session *
@@ -104,6 +108,7 @@ static struct _remotecontrol_session *
 static void _cb_handle_session_cleanup(struct olsr_telnet_cleanup *cleanup);
 
 
+
 /* plugin declaration */
 OLSR_PLUGIN7 {
   .descr = "OLSRD remote control and debug plugin",
@@ -165,12 +170,15 @@ static struct olsr_telnet_command _telnet_cmds[] = {
       "\"config format AUTO\":                              Set the format to automatic detection\n",
       .acl = &_remotecontrol_config.acl),
   TELNET_CMD("route", _cb_handle_route,
-      "\"route set [src <src-ip>] [gw <gateway ip>] [dst <destination prefix>] [table <table-id>]\n"
-      "            [proto <protocol-id>] [metric <metric>] interface <if-name>\n"
+      "\"route add [src <src-ip>] [gw <gateway ip>] dst <destination prefix> [table <table-id>]\n"
+      "            [proto <protocol-id>] [metric <metric>] if <if-name>\n"
       "                                                     Set a route in the kernel routing table\n"
-      "\"route remove [src <src-ip>] [gw <gateway ip>] [dst <destination prefix>] [table <table-id>]\n"
-      "               [proto <protocol-id>] [metric <metric>] interface <if-name>\n"
-      "                                                     Remove a route in the kernel routing table\n",
+      "\"route del [src <src-ip>] [gw <gateway ip>] dst <destination prefix> [table <table-id>]\n"
+      "               [proto <protocol-id>] [metric <metric>] if <if-name>\n"
+      "                                                     Remove a route in the kernel routing table\n"
+      "\"route get [src <src-ip>] [gw <gateway ip>] [dst <destination prefix>] [table <table-id>]\n"
+      "               [proto <protocol-id>] [metric <metric>] [if <if-name>] [ipv6]\n"
+      "                                                     Lists all known kernel routes matching a set of data\n",
       .acl = &_remotecontrol_config.acl),
 };
 
@@ -387,13 +395,13 @@ _update_logfilter(struct olsr_telnet_data *data,
 static void
 _cb_print_log(struct log_handler_entry *h __attribute__((unused)),
     struct log_parameters *param) {
-  struct olsr_telnet_session *telnet = h->custom;
+  struct olsr_telnet_data *data = h->custom;
 
-  abuf_puts(&telnet->session.out, param->buffer);
-  abuf_puts(&telnet->session.out, "\n");
+  abuf_puts(data->out, param->buffer);
+  abuf_puts(data->out, "\n");
 
   /* This might trigger logging output in olsr_socket_stream ! */
-  olsr_stream_flush(&telnet->session);
+  olsr_telnet_flush_session(data);
 }
 
 /**
@@ -490,7 +498,9 @@ _cb_handle_log(struct olsr_telnet_data *data) {
     return _update_logfilter(data, rc_session->mask, next, false);
   }
 
-  return TELNET_RESULT_UNKNOWN_COMMAND;
+  abuf_appendf(data->out, "Error, unknown subcommand for %s: %s",
+      data->command, data->parameter);
+  return TELNET_RESULT_ACTIVE;
 }
 
 /**
@@ -544,12 +554,82 @@ _cb_handle_config(struct olsr_telnet_data *data) {
         olsr_cfg_get_rawdb(), next, data->out);
   }
   else {
-    return TELNET_RESULT_UNKNOWN_COMMAND;
+    abuf_appendf(data->out, "Error, unknown subcommand for %s: %s",
+        data->command, data->parameter);
   }
 
   return TELNET_RESULT_ACTIVE;
 }
 
+static void
+_stop_route_feedback(struct olsr_telnet_data *session) {
+  struct os_route *route;
+
+  route = session->stop_data[0];
+  if (route->cb_finished) {
+    route->cb_finished(route, -1);
+  }
+}
+
+static void
+_cb_route_finished(struct os_route *rt, int error) {
+  struct _remotecontrol_session *session;
+
+  session = container_of(rt, struct _remotecontrol_session, route);
+
+  if (error) {
+    abuf_appendf(session->cleanup.data->out, "Command failed: %s (%d)\n",
+        strerror(error), error);
+  }
+  else {
+    abuf_puts(session->cleanup.data->out, "Command successful\n");
+  }
+  olsr_telnet_flush_session(session->cleanup.data);
+  olsr_telnet_stop(session->cleanup.data);
+}
+
+static void
+_cb_route_get(struct os_route *filter, struct os_route *route) {
+  struct _remotecontrol_session *session;
+  struct autobuf *out;
+  struct netaddr_str buf;
+  char if_buf[IF_NAMESIZE];
+
+  session = container_of(filter, struct _remotecontrol_session, route);
+  out = session->cleanup.data->out;
+
+  if (route->dst.type != AF_UNSPEC) {
+    abuf_appendf(out, "%s ", netaddr_to_string(&buf, &route->dst));
+  }
+  if (route->gw.type != AF_UNSPEC) {
+    abuf_appendf(out, "via %s ", netaddr_to_string(&buf, &route->gw));
+  }
+  if (route->src.type != AF_UNSPEC) {
+    abuf_appendf(out, "src %s ", netaddr_to_string(&buf, &route->src));
+  }
+  if (route->dst.type == AF_UNSPEC
+      && route->gw.type == AF_UNSPEC
+      && route->src.type == AF_UNSPEC) {
+    abuf_appendf(out, "%s ", route->family == AF_INET ? "ipv4" : "ipv6");
+  }
+
+  if (route->if_index) {
+    abuf_appendf(out, "dev %s (%d) ",
+        if_indextoname(route->if_index, if_buf), route->if_index);
+  }
+  if (route->protocol != RTPROT_UNSPEC) {
+    abuf_appendf(out, "prot %d ", route->protocol);
+  }
+  if (route->metric != -1) {
+    abuf_appendf(out, "metric %d ", route->metric);
+  }
+  if (route->table != RT_TABLE_UNSPEC) {
+    abuf_appendf(out, "table %d ", route->table);
+  }
+  abuf_puts(out, "\n");
+  olsr_telnet_flush_session(session->cleanup.data);
+}
+
 /**
  * Handle the route command
  * @param data pointer to telnet data
@@ -557,88 +637,129 @@ _cb_handle_config(struct olsr_telnet_data *data) {
  */
 static enum olsr_telnet_result
 _cb_handle_route(struct olsr_telnet_data *data) {
-  struct netaddr_str buf;
+  bool add  = false, del = false, get = false;
   const char *ptr = NULL, *next = NULL;
-  struct netaddr src, gw, dst;
-  int table = 0, protocol = 4, metric = -1, if_index = 0;
-  bool set;
+  struct _remotecontrol_session *session;
+  struct netaddr_str buf;
+  struct os_route route;
+  int result;
 
-  memset (&src, 0, sizeof(src));
-  memset (&gw, 0, sizeof(gw));
-  memset (&dst, 0, sizeof(dst));
+  memcpy(&route, &OS_ROUTE_WILDCARD, sizeof(route));
 
-  if ((next = str_hasnextword(data->parameter, "set")) != NULL
-      || (next = str_hasnextword(data->parameter, "remove")) != NULL) {
-    set = strncasecmp(data->parameter, "set", 3) == 0;
+  if ((next = str_hasnextword(data->parameter, "add")) != NULL) {
+    add = true;
+  }
+  else if ((next = str_hasnextword(data->parameter, "del")) != NULL) {
+    del = true;
+  }
+  else if ((next = str_hasnextword(data->parameter, "get")) != NULL) {
+    get = true;
+  }
 
+  if (add || del || get) {
     ptr = next;
-    while (ptr) {
+    while (ptr && *ptr) {
       if ((next = str_hasnextword(ptr, "src"))) {
         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
-        if (netaddr_from_string(&src, buf.buf)) {
+        if (netaddr_from_string(&route.src, buf.buf) != 0
+            || (route.src.type != AF_INET && route.src.type != AF_INET6)) {
           abuf_appendf(data->out, "Error, illegal source: %s", buf.buf);
           return TELNET_RESULT_ACTIVE;
         }
+        route.family = route.src.type;
       }
       else if ((next = str_hasnextword(ptr, "gw"))) {
         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
-        if (netaddr_from_string(&gw, buf.buf)) {
+        if (netaddr_from_string(&route.gw, buf.buf) != 0
+            || (route.gw.type != AF_INET && route.gw.type != AF_INET6)) {
           abuf_appendf(data->out, "Error, illegal gateway: %s", buf.buf);
           return TELNET_RESULT_ACTIVE;
         }
+        route.family = route.gw.type;
       }
       else if ((next = str_hasnextword(ptr, "dst"))) {
         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
-        if (netaddr_from_string(&dst, buf.buf)) {
+        if (netaddr_from_string(&route.dst, buf.buf) != 0
+            || (route.dst.type != AF_INET && route.dst.type != AF_INET6)) {
           abuf_appendf(data->out, "Error, illegal destination: %s", buf.buf);
           return TELNET_RESULT_ACTIVE;
         }
+        route.family = route.dst.type;
       }
       else if ((next = str_hasnextword(ptr, "table"))) {
         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
-        table = atoi(buf.buf);
+        route.table = atoi(buf.buf);
       }
       else if ((next = str_hasnextword(ptr, "proto"))) {
         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
-        protocol = atoi(buf.buf);
+        route.protocol = atoi(buf.buf);
       }
       else if ((next = str_hasnextword(ptr, "metric"))) {
         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
-        table = atoi(buf.buf);
+        route.table = atoi(buf.buf);
       }
-      else if ((next = str_hasnextword(ptr, "interface"))) {
+      else if ((next = str_hasnextword(ptr, "if"))) {
         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
-        if_index = if_nametoindex(buf.buf);
+        route.if_index = if_nametoindex(buf.buf);
+      }
+      else if ((next = str_hasnextword(ptr, "ipv6"))) {
+        route.family = AF_INET6;
+        ptr = next;
       }
       else {
         abuf_appendf(data->out, "Cannot parse remainder of parameter string: %s", ptr);
         return TELNET_RESULT_ACTIVE;
       }
     }
-    if (if_index == 0) {
+    if ((add||del) && route.if_index == 0) {
       abuf_appendf(data->out, "Missing or unknown interface");
       return TELNET_RESULT_ACTIVE;
     }
-    if (dst.type != AF_INET && dst.type != AF_INET6) {
-      abuf_appendf(data->out, "Error, IPv4 or IPv6 destination mandatory");
+    if ((add||del) && route.dst.type == AF_UNSPEC) {
+      abuf_appendf(data->out, "Error, IPv4 or IPv6 destination mandatory for add/del");
       return TELNET_RESULT_ACTIVE;
     }
-    if (src.type != 0 && src.type != dst.type) {
-      abuf_appendf(data->out, "Error, illegal address type of source ip");
+    if ((route.src.type != AF_UNSPEC && route.src.type != route.family)
+        || (route.gw.type != AF_UNSPEC && route.gw.type != route.family)
+        || (route.dst.type != AF_UNSPEC && route.dst.type != route.family)) {
+      abuf_appendf(data->out, "Error, IP address types do not match");
       return TELNET_RESULT_ACTIVE;
     }
-    if (gw.type == 0 || gw.type != dst.type) {
-      abuf_appendf(data->out, "Error, illegal or missing gateway ip");
+
+    if (route.family == AF_UNSPEC) {
+      route.family = AF_INET;
+    }
+
+    /* allocate permanent route datastructure for continous output */
+    session = _get_remotecontrol_session(data);
+    if (session == NULL) {
+      OLSR_WARN_OOM(LOG_PLUGINS);
+      return TELNET_RESULT_INTERNAL_ERROR;
+    }
+    memcpy(&session->route, &route, sizeof(route));
+
+    session->route.cb_finished = _cb_route_finished;
+    session->route.cb_get = _cb_route_get;
+
+    if (add || del) {
+      result = os_routing_set(&session->route, add, true);
+    }
+    else {
+      result = os_routing_get(&session->route);
+    }
+
+    if (result) {
+      abuf_puts(data->out, "Error while preparing netlink command");
       return TELNET_RESULT_ACTIVE;
     }
 
-    abuf_appendf(data->out, "set route: %d",
-        os_routing_set(src.type == 0 ? NULL: &src, &gw, &dst,
-            table, if_index, metric, protocol, set, true));
-    return TELNET_RESULT_ACTIVE;
+    data->stop_handler = _stop_route_feedback;
+    data->stop_data[0] = session;
+    return TELNET_RESULT_CONTINOUS;
   }
-
-  return TELNET_RESULT_UNKNOWN_COMMAND;
+  abuf_appendf(data->out, "Error, unknown subcommand for %s: %s",
+      data->command, data->parameter);
+  return TELNET_RESULT_ACTIVE;
 }
 
 /**
index 9b8801f..e35f709 100644 (file)
@@ -94,6 +94,7 @@ netaddr_from_binary(struct netaddr *dst, const void *binary,
   }
   else {
     /* unknown address type */
+    dst->type = AF_UNSPEC;
     return -1;
   }
 
@@ -157,6 +158,7 @@ netaddr_from_socket(struct netaddr *dst, const union netaddr_socket *src) {
   }
   else {
     /* unknown address type */
+    dst->type = AF_UNSPEC;
     return -1;
   }
   dst->type = (uint8_t)src->std.sa_family;
@@ -431,6 +433,7 @@ netaddr_from_string(struct netaddr *dst, const char *src) {
 
     if (*ptr2 == 0) {
       /* prefixlength is missing */
+      dst->type = AF_UNSPEC;
       return -1;
     }
 
@@ -479,19 +482,21 @@ netaddr_from_string(struct netaddr *dst, const char *src) {
 
   /* stop if an error happened */
   if (result) {
+    dst->type = AF_UNSPEC;
     return -1;
   }
 
   if (*ptr2) {
     if (prefix_len < 0 || prefix_len > dst->prefix_len) {
       /* prefix is too long */
+      dst->type = AF_UNSPEC;
       return -1;
     }
 
     /* store real prefix length */
     dst->prefix_len = (uint8_t)prefix_len;
   }
-  return result;
+  return 0;
 }
 
 /**
index 5d3c78c..b9da61b 100644 (file)
@@ -135,7 +135,7 @@ struct log_parameters {
 #define OLSR_INFO_NH(source, format, args...) do { } while(0)
 #else
 #define OLSR_INFO(source, format, args...) _OLSR_LOG(SEVERITY_INFO, source, false, format, ##args)
-#define OLSR_INFO_NH(source, format, args...) _OLSR_LOG(SEVERITY_WARN, source, true, format, ##args)
+#define OLSR_INFO_NH(source, format, args...) _OLSR_LOG(SEVERITY_INFO, source, true, format, ##args)
 #endif
 
 #ifdef REMOVE_LOG_WARN
index 842ced1..2f4a9e1 100644 (file)
@@ -179,6 +179,9 @@ olsr_telnet_stop(struct olsr_telnet_data *data) {
     data->stop_handler(data);
     data->stop_handler = NULL;
   }
+  data->show_echo = true;
+  abuf_puts(data->out, "> ");
+  olsr_telnet_flush_session(data);
 }
 
 /**
@@ -384,13 +387,14 @@ _cb_telnet_receive_data(struct olsr_stream_session *session) {
           case TELNET_RESULT_ACTIVE:
             break;
           case TELNET_RESULT_CONTINOUS:
+            telnet_session->data.show_echo = false;
             break;
           case TELNET_RESULT_INTERNAL_ERROR:
             abuf_setlen(&session->out, len);
             abuf_appendf(&session->out,
                 "Error in autobuffer during command '%s'.\n", cmd);
             break;
-          case TELNET_RESULT_UNKNOWN_COMMAND:
+          case _TELNET_RESULT_UNKNOWN_COMMAND:
             abuf_setlen(&session->out, len);
             abuf_appendf(&session->out, "Error, unknown command '%s'\n", cmd);
             break;
@@ -439,7 +443,7 @@ _telnet_handle_command(struct olsr_telnet_data *data) {
 #endif
   cmd = _check_telnet_command(data, data->command, NULL);
   if (cmd == NULL) {
-    return TELNET_RESULT_UNKNOWN_COMMAND;
+    return _TELNET_RESULT_UNKNOWN_COMMAND;
   }
 
   OLSR_INFO(LOG_TELNET, "Executing command from %s: %s %s",
index 9f692f2..a1a3ff9 100644 (file)
@@ -19,7 +19,12 @@ enum olsr_telnet_result {
   TELNET_RESULT_CONTINOUS,
   TELNET_RESULT_INTERNAL_ERROR,
   TELNET_RESULT_QUIT,
-  TELNET_RESULT_UNKNOWN_COMMAND,
+
+  /*
+   * this one is used internally for the telnet API,
+   * it should not be returned by a command handler
+   */
+  _TELNET_RESULT_UNKNOWN_COMMAND,
 };
 
 struct olsr_telnet_cleanup {
@@ -116,4 +121,17 @@ olsr_telnet_remove_cleanup(struct olsr_telnet_cleanup *cleanup) {
   list_remove(&cleanup->node);
 }
 
+/**
+ * Flushs the output stream of a telnet session. This will be only
+ * necessary for continous output.
+ * @param data pointer to telnet data
+ */
+static INLINE void
+olsr_telnet_flush_session(struct olsr_telnet_data *data) {
+  struct olsr_telnet_session *session;
+
+  session = container_of(data, struct olsr_telnet_session, data);
+  olsr_stream_flush(&session->session);
+}
+
 #endif /* OLSR_TELNET_H_ */
index a4e733d..47d92cc 100644 (file)
 #define PROC_IF_SPOOF "/proc/sys/net/ipv4/conf/%s/rp_filter"
 #define PROC_ALL_SPOOF "/proc/sys/net/ipv4/conf/all/rp_filter"
 
+static int _routing_set(struct nlmsghdr *msg, struct os_route *route,
+    unsigned char rt_type, unsigned char rt_scope);
+
 static bool _is_at_least_linuxkernel_2_6_31(void);
 static int _os_linux_writeToProc(const char *file, char *old, char value);
 
-static void _cb_rtnetlink_feedback(uint32_t seq, int error);
+static void _cb_rtnetlink_message(struct nlmsghdr *);
+static void _cb_rtnetlink_error(uint32_t seq, int error);
+static void _cb_rtnetlink_done(uint32_t seq);
 static void _cb_rtnetlink_timeout(void);
 
 /* global procfile state before initialization */
@@ -80,9 +85,22 @@ static char _original_icmp_redirect;
 
 /* netlink socket for route set/get commands */
 struct os_system_netlink _rtnetlink_socket;
+struct list_entity _rtnetlink_feedback;
 
 OLSR_SUBSYSTEM_STATE(_os_routing_state);
 
+/* default wildcard route */
+const struct os_route OS_ROUTE_WILDCARD = {
+  .family = AF_UNSPEC,
+  .src = { .type = AF_UNSPEC },
+  .gw = { .type = AF_UNSPEC },
+  .dst = { .type = AF_UNSPEC },
+  .table = RT_TABLE_UNSPEC,
+  .metric = -1,
+  .protocol = RTPROT_UNSPEC,
+  .if_index = 0
+};
+
 /**
  * Initialize routing subsystem
  * @return -1 if an error happened, 0 otherwise
@@ -96,7 +114,9 @@ os_routing_init(void) {
     return -1;
   }
 
-  _rtnetlink_socket.cb_feedback = _cb_rtnetlink_feedback;
+  _rtnetlink_socket.cb_message = _cb_rtnetlink_message;
+  _rtnetlink_socket.cb_error = _cb_rtnetlink_error;
+  _rtnetlink_socket.cb_done = _cb_rtnetlink_done;
   _rtnetlink_socket.cb_timeout = _cb_rtnetlink_timeout;
 
   if (_os_linux_writeToProc(PROC_ALL_REDIRECT, &_original_icmp_redirect, '0')) {
@@ -113,6 +133,8 @@ os_routing_init(void) {
     }
   }
 
+  list_init_head(&_rtnetlink_feedback);
+
   olsr_subsystem_init(&_os_routing_state);
   return 0;
 }
@@ -122,9 +144,16 @@ os_routing_init(void) {
  */
 void
 os_routing_cleanup(void) {
+  struct os_route *rt, *rt_it;
+
   if (olsr_subsystem_cleanup(&_os_routing_state))
     return;
 
+  list_for_each_element_safe(&_rtnetlink_feedback, rt, _internal._node, rt_it) {
+    rt->cb_finished(rt, true);
+    list_remove(&rt->_internal._node);
+  }
+
   if (_original_icmp_redirect != 0
       && _os_linux_writeToProc(PROC_ALL_REDIRECT, &_original_icmp_redirect, '0') != 0) {
     OLSR_WARN(LOG_OS_SYSTEM,
@@ -216,35 +245,26 @@ os_routing_cleanup_mesh_if(struct olsr_interface *interf) {
   return;
 }
 
-static int
-_routing_set(struct nlmsghdr *msg,
-    const struct netaddr *src, const struct netaddr *gw, const struct netaddr *dst,
-    int rttable, int if_index, int metric, int protocol, unsigned char scope);
-
 /**
  * Update an entry of the kernel routing table. This call will only trigger
  * the change, the real change will be done as soon as the netlink socket is
  * writable.
- * @param src source address of route (NULL if source ip not set)
- * @param gw gateway of route (NULL if direct connection)
- * @param dst destination prefix of route
- * @param rttable routing table
- * @param if_index interface index
- * @param metric routing metric
- * @param protocol routing protocol
+ * @param route data of route to be set/removed
  * @param set true if route should be set, false if it should be removed
  * @param del_similar true if similar routes that block this one should be
  *   removed.
- * @return -1 if an error happened, rtnetlink sequence number otherwise
+ * @return -1 if an error happened, 0 otherwise
  */
 int
-os_routing_set(const struct netaddr *src, const struct netaddr *gw, const struct netaddr *dst,
-    int rttable, int if_index, int metric, int protocol, bool set, bool del_similar) {
+os_routing_set(struct os_route *route, bool set, bool del_similar) {
   uint8_t buffer[UIO_MAXIOV];
   struct nlmsghdr *msg;
   unsigned char scope;
+  struct os_route os_rt;
+  int seq;
 
   memset(buffer, 0, sizeof(buffer));
+  memcpy(&os_rt, route, sizeof(os_rt));
 
   /* get pointers for netlink message */
   msg = (void *)&buffer[0];
@@ -263,153 +283,328 @@ os_routing_set(const struct netaddr *src, const struct netaddr *gw, const struct
   } else {
     msg->nlmsg_type = RTM_DELROUTE;
 
-    protocol = -1;
-    src = NULL;
+    os_rt.protocol = -1;
+    os_rt.src.type = AF_UNSPEC;
 
     if (del_similar) {
       /* no interface necessary */
-      if_index = -1;
+      os_rt.if_index = -1;
 
       /* as wildcard for fuzzy deletion */
       scope = RT_SCOPE_NOWHERE;
     }
   }
 
-  if (gw == NULL && dst->prefix_len == netaddr_get_maxprefix(dst)) {
+  if (os_rt.gw.type == AF_UNSPEC
+      && os_rt.dst.prefix_len == netaddr_get_maxprefix(&os_rt.dst)) {
     /* use destination as gateway, to 'force' linux kernel to do proper source address selection */
-    gw = dst;
+    os_rt.gw = os_rt.dst;
+  }
+
+  if (_routing_set(msg, &os_rt, RTN_UNICAST, scope)) {
+    return -1;
   }
 
-  if (_routing_set(msg, src, gw, dst, rttable, if_index, metric, protocol, scope)) {
+  seq = os_system_netlink_send(&_rtnetlink_socket, msg);
+  if (seq < 0) {
+    return -1;
+  }
+
+  if (route->cb_finished) {
+    list_add_tail(&_rtnetlink_feedback, &route->_internal._node);
+    route->_internal.nl_seq = seq;
+  }
+  return 0;
+}
+
+/**
+ * Request all routing dataof a certain address family
+ * @param af AF_INET or AF_INET6
+ * @return -1 if an error happened, rtnetlink sequence number otherwise
+ */
+int
+os_routing_get(struct os_route *route) {
+  uint8_t buffer[UIO_MAXIOV];
+  struct nlmsghdr *msg;
+  struct rtgenmsg *rt_gen;
+  int seq;
+
+  assert (route->cb_finished != NULL && route->cb_get != NULL);
+  memset(buffer, 0, sizeof(buffer));
+
+  /* get pointers for netlink message */
+  msg = (void *)&buffer[0];
+  rt_gen = NLMSG_DATA(msg);
+
+  msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+
+  /* set length of netlink message with rtmsg payload */
+  msg->nlmsg_len = NLMSG_LENGTH(sizeof(*rt_gen));
+
+  msg->nlmsg_type = RTM_GETROUTE;
+  rt_gen->rtgen_family = route->family;
+
+  seq = os_system_netlink_send(&_rtnetlink_socket, msg);
+  if (seq < 0) {
     return -1;
   }
 
-  return os_system_netlink_send(&_rtnetlink_socket, msg);
+  list_add_tail(&_rtnetlink_feedback, &route->_internal._node);
+  route->_internal.nl_seq = seq;
+  return 0;
 }
 
 /**
  * Initiatize the an netlink routing message
  * @param msg pointer to netlink message header
- * @param src source address of route (NULL if not set)
- * @param gw gateway of route (NULL if not set)
- * @param dst destination prefix of route (NULL if not set)
- * @param rttable routing table (mandatory)
- * @param if_index interface index, -1 if not set
- * @param metric routing metric, -1 if not set
- * @param protocol routing protocol, -1 if not set
+ * @param route data to be added to the netlink message
  * @param scope scope of route to be set/removed
  * @return -1 if an error happened, 0 otherwise
  */
 static int
-_routing_set(struct nlmsghdr *msg,
-    const struct netaddr *src, const struct netaddr *gw, const struct netaddr *dst,
-    int rttable, int if_index, int metric, int protocol, unsigned char scope) {
+_routing_set(struct nlmsghdr *msg, struct os_route *route,
+    unsigned char rt_type, unsigned char rt_scope) {
   struct rtmsg *rt_msg;
-  int type = -1;
 
-  /* calculate address type */
-  if (dst) {
-    type = dst->type;
+  /* calculate address af_type */
+  if (route->dst.type != AF_UNSPEC) {
+    route->family = route->dst.type;
   }
-  if (gw) {
-    if (type != -1 && type != gw->type) {
+  if (route->gw.type != AF_UNSPEC) {
+    if (route->family  != AF_UNSPEC && route->family  != route->gw.type) {
       return -1;
     }
-    type = gw->type;
+    route->family  = route->gw.type;
   }
-  if (src) {
-    if (type != -1 && type != src->type) {
+  if (route->src.type != AF_UNSPEC) {
+    if (route->family  != AF_UNSPEC && route->family  != route->src.type) {
       return -1;
     }
-    type = gw->type;
+    route->family  = route->src.type;
   }
 
-  /* and do a consistency check */
-  assert (type == AF_INET || type == AF_INET6);
+  if (route->family  == AF_UNSPEC) {
+    route->family  = AF_INET;
+  }
 
   /* initialize rtmsg payload */
   rt_msg = NLMSG_DATA(msg);
 
-  rt_msg->rtm_family = type;
-  rt_msg->rtm_scope = scope;
-
-  /*
-   * RTN_UNSPEC would be the wildcard,
-   * but blackhole, broadcast or NAT rules should usually not conflict
-   */
-  /* -> olsr only adds deletes unicast routes at the moment */
-  rt_msg->rtm_type = RTN_UNICAST;
-
-  if (protocol != -1) {
-    /* set protocol */
-    rt_msg->rtm_protocol = protocol;
-  }
-
-  if (rttable != -1) {
-    /* set routing table */
-    rt_msg->rtm_table = rttable;
-  }
+  rt_msg->rtm_family = route->family ;
+  rt_msg->rtm_scope = rt_scope;
+  rt_msg->rtm_type = rt_type;
+  rt_msg->rtm_protocol = route->protocol;
+  rt_msg->rtm_table = route->table;
 
   /* add attributes */
-  if (src != NULL) {
-    rt_msg->rtm_src_len = src->prefix_len;
+  if (route->src.type != AF_UNSPEC) {
+    rt_msg->rtm_src_len = route->src.prefix_len;
 
     /* add src-ip */
-    if (os_system_netlink_addnetaddr(msg, RTA_PREFSRC, src)) {
+    if (os_system_netlink_addnetaddr(msg, RTA_PREFSRC, &route->src)) {
       return -1;
     }
   }
 
-  if (gw != NULL) {
+  if (route->gw.type != AF_UNSPEC) {
     rt_msg->rtm_flags = RTNH_F_ONLINK;
 
     /* add gateway */
-    if (os_system_netlink_addnetaddr(msg, RTA_GATEWAY, gw)) {
+    if (os_system_netlink_addnetaddr(msg, RTA_GATEWAY, &route->gw)) {
       return -1;
     }
   }
 
-  if (dst != 0) {
-    rt_msg->rtm_dst_len = dst->prefix_len;
+  if (route->dst.type != AF_UNSPEC) {
+    rt_msg->rtm_dst_len = route->dst.prefix_len;
 
     /* add destination */
-    if (os_system_netlink_addnetaddr(msg, RTA_DST, dst)) {
+    if (os_system_netlink_addnetaddr(msg, RTA_DST, &route->dst)) {
       return -1;
     }
   }
 
-  if (metric != -1) {
+  if (route->metric != -1) {
     /* add metric */
-    if (os_system_netlink_addreq(msg, RTA_PRIORITY, &metric, sizeof(metric))) {
+    if (os_system_netlink_addreq(msg, RTA_PRIORITY, &route->metric, sizeof(route->metric))) {
       return -1;
     }
   }
 
-  if (if_index != -1) {
+  if (route->if_index) {
     /* add interface*/
-    if (os_system_netlink_addreq(msg, RTA_OIF, &if_index, sizeof(if_index))) {
+    if (os_system_netlink_addreq(msg, RTA_OIF, &route->if_index, sizeof(route->if_index))) {
       return -1;
     }
   }
   return 0;
 }
 
+static int
+_routing_parse_nlmsg(struct os_route *route, struct nlmsghdr *msg) {
+  struct rtmsg *rt_msg;
+  struct rtattr *rt_attr;
+  int rt_len;
+
+  rt_msg = NLMSG_DATA(msg);
+  rt_attr = (struct rtattr *) RTM_RTA(rt_msg);
+  rt_len = RTM_PAYLOAD(msg);
+
+  memcpy(route, &OS_ROUTE_WILDCARD, sizeof(*route));
+
+  route->protocol = rt_msg->rtm_protocol;
+  route->table = rt_msg->rtm_table;
+  route->family = rt_msg->rtm_family;
+
+  if (route->family != AF_INET && route->family != AF_INET6) {
+    return -1;
+  }
+
+  for(; RTA_OK(rt_attr, rt_len); rt_attr = RTA_NEXT(rt_attr,rt_len)) {
+    switch(rt_attr->rta_type) {
+      case RTA_SRC:
+        netaddr_from_binary(&route->src, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr), rt_msg->rtm_family);
+        route->src.prefix_len = rt_msg->rtm_src_len;
+        break;
+      case RTA_GATEWAY:
+        netaddr_from_binary(&route->gw, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr), rt_msg->rtm_family);
+        break;
+      case RTA_DST:
+        netaddr_from_binary(&route->dst, RTA_DATA(rt_attr), RTA_PAYLOAD(rt_attr), rt_msg->rtm_family);
+        route->dst.prefix_len = rt_msg->rtm_dst_len;
+        break;
+      case RTA_PRIORITY:
+        memcpy(&route->metric, RTA_DATA(rt_attr), sizeof(route->metric));
+        break;
+      case RTA_OIF:
+        memcpy(&route->if_index, RTA_DATA(rt_attr), sizeof(route->if_index));
+        break;
+      default:
+        break;
+    }
+  }
+
+  if (route->dst.type == AF_UNSPEC) {
+    route->dst = route->family == AF_INET ? NETADDR_IPV4_ANY : NETADDR_IPV6_ANY;
+    route->dst.prefix_len = rt_msg->rtm_dst_len;
+  }
+  return 0;
+}
+
+static bool
+_match_routes(struct os_route *filter, struct os_route *route) {
+  if (filter->family != route->family) {
+    return false;
+  }
+  if (filter->src.type != AF_UNSPEC
+      && memcmp(&filter->src, &route->src, sizeof(filter->src)) != 0) {
+    return false;
+  }
+  if (filter->gw.type != AF_UNSPEC
+      && memcmp(&filter->gw, &route->gw, sizeof(filter->gw)) != 0) {
+    return false;
+  }
+  if (filter->dst.type != AF_UNSPEC
+      && memcmp(&filter->dst, &route->dst, sizeof(filter->dst)) != 0) {
+    return false;
+  }
+  if (filter->metric != -1 && filter->metric != route->metric) {
+    return false;
+  }
+  if (filter->table != RT_TABLE_UNSPEC && filter->table != route->table) {
+    return false;
+  }
+  if (filter->protocol != RTPROT_UNSPEC && filter->protocol != route->protocol) {
+    return false;
+  }
+  return filter->if_index == 0 || filter->if_index == route->if_index;
+}
+
+static void
+_cb_rtnetlink_message(struct nlmsghdr *msg) {
+  struct os_route *filter;
+  struct os_route rt;
+
+  OLSR_DEBUG(LOG_OS_ROUTING, "Got message: %d %d", msg->nlmsg_seq, msg->nlmsg_type);
+
+  if (msg->nlmsg_type != RTM_NEWROUTE && msg->nlmsg_type != RTM_DELROUTE) {
+    return;
+  }
+
+  if (_routing_parse_nlmsg(&rt, msg)) {
+    OLSR_WARN(LOG_OS_ROUTING, "Error while processing route reply");
+    return;
+  }
+
+  list_for_each_element(&_rtnetlink_feedback, filter, _internal._node) {
+    OLSR_DEBUG_NH(LOG_OS_ROUTING, "  Compare with seq: %d", filter->_internal.nl_seq);
+    if (msg->nlmsg_seq == filter->_internal.nl_seq) {
+      if (filter->cb_get != NULL && _match_routes(filter, &rt)) {
+        filter->cb_get(filter, &rt);
+      }
+      break;
+    }
+  }
+}
+
 /**
- * TODO: Handle feedback from netlink socket
+ * Handle feedback from netlink socket
  * @param seq
  * @param error
  */
 static void
-_cb_rtnetlink_feedback(uint32_t seq, int error) {
-  OLSR_INFO(LOG_OS_ROUTING, "Got feedback: %d %d", seq, error);
+_cb_rtnetlink_error(uint32_t seq, int error) {
+  struct os_route *route;
+
+  OLSR_DEBUG(LOG_OS_ROUTING, "Got feedback: %d %d", seq, error);
+
+  list_for_each_element(&_rtnetlink_feedback, route, _internal._node) {
+    if (seq == route->_internal.nl_seq) {
+      if (route->cb_finished) {
+        route->cb_finished(route, error);
+      }
+      list_remove(&route->_internal._node);
+      break;
+    }
+  }
 }
 
 /**
- * TODO: Handle ack timeout from netlink socket
+ * Handle ack timeout from netlink socket
  */
 static void
 _cb_rtnetlink_timeout(void) {
-  OLSR_INFO(LOG_OS_ROUTING, "Got timeout");
+  struct os_route *route, *rt_it;
+
+  OLSR_DEBUG(LOG_OS_ROUTING, "Got timeout");
+
+  list_for_each_element_safe(&_rtnetlink_feedback, route, _internal._node, rt_it) {
+    if (route->cb_finished) {
+      route->cb_finished(route, true);
+    }
+    list_remove(&route->_internal._node);
+  }
+}
+
+/**
+ * Handle done from multipart netlink messages
+ * @param seq
+ */
+static void
+_cb_rtnetlink_done(uint32_t seq) {
+  struct os_route *route;
+
+  OLSR_DEBUG(LOG_OS_ROUTING, "Got done: %u", seq);
+
+  list_for_each_element(&_rtnetlink_feedback, route, _internal._node) {
+    if (seq == route->_internal.nl_seq) {
+      if (route->cb_finished) {
+        route->cb_finished(route, false);
+      }
+      list_remove(&route->_internal._node);
+      break;
+    }
+  }
 }
 
 /**
index cd65415..285e086 100644 (file)
 #error "DO not include this file directly, always use 'os_system.h'"
 #endif
 
+#include "common/common_types.h"
+#include "common/list.h"
 #include "os_helper.h"
 
+struct os_route_internal {
+  struct list_entity _node;
+
+  uint32_t nl_seq;
+};
 #endif /* OS_ROUTING_LINUX_H_ */
index 2b94629..660fa23 100644 (file)
@@ -288,7 +288,7 @@ os_system_netlink_remove(struct os_system_netlink *nl) {
 int
 os_system_netlink_send(struct os_system_netlink *nl,
     struct nlmsghdr *nl_hdr) {
-  nl->seq_used++;
+  nl->seq_used = (nl->seq_used + 1) & INT32_MAX;
 
   nl_hdr->nlmsg_seq = nl->seq_used;
   nl_hdr->nlmsg_flags |= NLM_F_ACK | NLM_F_MULTI;
@@ -380,6 +380,18 @@ _flush_netlink_buffer(struct os_system_netlink *nl) {
   }
 }
 
+static void
+_netlink_job_finished(struct os_system_netlink *nl) {
+  if (nl->msg_in_transit > 0) {
+    nl->msg_in_transit--;
+  }
+  if (nl->msg_in_transit == 0) {
+    olsr_timer_stop(nl->timeout);
+    nl->timeout= NULL;
+    nl->seq_used = 0;
+  }
+}
+
 /**
  * Handler for incoming async netlink messages
  * @param fd
@@ -426,6 +438,11 @@ _netlink_handler(int fd, void *data, bool event_read, bool event_write) {
         break;
 
       case NLMSG_DONE:
+        OLSR_DEBUG(LOG_OS_SYSTEM, "Netlink message done: %d", nh->nlmsg_seq);
+        if (nl->cb_done) {
+          nl->cb_done(nh->nlmsg_seq);
+        }
+        _netlink_job_finished(nl);
         /* End of a multipart netlink message reached */
         return;
 
@@ -492,18 +509,11 @@ _handle_nl_err(struct os_system_netlink *nl, struct nlmsghdr *nh) {
 
   err = (struct nlmsgerr *) NLMSG_DATA(nh);
 
-  OLSR_DEBUG(LOG_OS_SYSTEM, "Received netlink feedback (%u bytes): %d",
-      nh->nlmsg_len, err->error);
+  OLSR_DEBUG(LOG_OS_SYSTEM, "Received netlink feedback (%u bytes): %s (%d)",
+      nh->nlmsg_len, strerror(-err->error), err->error);
 
-  if (nl->cb_feedback) {
-    nl->cb_feedback(err->msg.nlmsg_seq, err->error);
-  }
-  if (nl->msg_in_transit > 0) {
-    nl->msg_in_transit--;
-  }
-  if (nl->msg_in_transit == 0) {
-    olsr_timer_stop(nl->timeout);
-    nl->timeout= NULL;
-    nl->seq_used = 0;
+  if (nl->cb_error) {
+    nl->cb_error(err->msg.nlmsg_seq, err->error);
   }
+  _netlink_job_finished(nl);
 }
index 595b0e3..e016954 100644 (file)
@@ -30,14 +30,15 @@ struct os_system_netlink {
   struct autobuf out;
   struct nlmsghdr *in;
 
-  int seq_used;
-  int seq_sent;
+  uint32_t seq_used;
+  uint32_t seq_sent;
 
   int msg_in_transit;
 
   void (*cb_message)(struct nlmsghdr *hdr);
-  void (*cb_feedback)(uint32_t seq, int error);
+  void (*cb_error)(uint32_t seq, int error);
   void (*cb_timeout)(void);
+  void (*cb_done)(uint32_t seq);
 
   struct olsr_timer_entry *timeout;
 };
index 7c7f996..5539c55 100644 (file)
 
 #undef OS_NET_SPECIFIC_INCLUDE
 
+/* make sure default values for routing are there */
+#ifndef RTPROT_UNSPEC
+#define RTPROT_UNSPEC 0
+#endif
+#ifndef RT_TABLE_UNSPEC
+#define RT_TABLE_UNSPEC 0
+#endif
+
+struct os_route_callbacks;
+
+struct os_route {
+  /* used for delivering feedback about netlink commands */
+  struct os_route_internal _internal;
+
+  /* address family */
+  unsigned char family;
+
+  /* source, gateway and destination */
+  struct netaddr src, gw, dst;
+
+  /* metric of the route */
+  int metric;
+
+  /* routing table and routing protocol */
+  unsigned char table, protocol;
+
+  /* index of outgoing interface */
+  unsigned int if_index;
+
+  /* callback when operation is finished */
+  void (*cb_finished)(struct os_route *, int error);
+
+  /* callback for os_routing_get() */
+  void (*cb_get)(struct os_route *filter, struct os_route *route);
+};
+
+struct os_route_callbacks {
+};
+
+EXPORT extern const struct os_route OS_ROUTE_WILDCARD;
+
 /* prototypes for all os_routing functions */
 EXPORT int os_routing_init(void);
 EXPORT void os_routing_cleanup(void);
@@ -92,7 +133,7 @@ EXPORT void os_routing_cleanup(void);
 EXPORT int os_routing_init_mesh_if(struct olsr_interface *);
 EXPORT void os_routing_cleanup_mesh_if(struct olsr_interface *);
 
-EXPORT int os_routing_set(const struct netaddr *src, const struct netaddr *gw, const struct netaddr *dst,
-    int rttable, int if_index, int metric, int protocol, bool set, bool del_similar);
+EXPORT int os_routing_set(struct os_route *, bool set, bool del_similar);
+EXPORT int os_routing_get(struct os_route *);
 
 #endif /* OS_ROUTING_H_ */
index 0cfb2cc..37d4037 100644 (file)
@@ -67,8 +67,6 @@
  * #define OS_SYSTEM_LOG          OS_GENERIC
  */
 
-struct olsr_system_feedback;
-
 /* set the guard macro so we can include the os specific settings */
 #define OS_NET_SPECIFIC_INCLUDE
 #include "os_helper.h"
@@ -87,20 +85,6 @@ struct olsr_system_feedback;
 
 #undef OS_NET_SPECIFIC_INCLUDE
 
-struct olsr_system_feedback {
-  struct list_entity _node;
-
-  uint32_t seq_min, seq_max;
-  int outstanding_fb;
-
-  void (*cb_ack)(struct olsr_system_feedback *, uint32_t seq, int error);
-  void (*cb_timeout)(struct olsr_system_feedback *);
-
-  struct olsr_timer_entry *timeout;
-
-  void *custom;
-};
-
 /* prototypes for all os_system functions */
 EXPORT int os_system_init(void);
 EXPORT void os_system_cleanup(void);
@@ -111,9 +95,6 @@ EXPORT void os_system_openlog(void);
 EXPORT void os_system_closelog(void);
 EXPORT void os_system_log(enum log_severity, const char *);
 
-EXPORT void os_system_feedback_add(struct olsr_system_feedback *feedback);
-EXPORT void os_system_feedback_remove(struct olsr_system_feedback *feedback);
-
 /*
  * INLINE implementations for generic os_net functions
  */