hi guys,

first of all I want to say thank you to all openvpn developers for making such a cool and usefull tool. While working with it, we found two limitations regarding
the "IP Routing Table". There is a static buffer holding the routes.
The limit is defined in route.h ( #define MAX_ROUTES 100 ).
The second limit is the size of the push buffer ( see options.h
#define MAX_PUSH_LIST_LEN TLS_CHANNEL_BUF_SIZE / 2 )
To avoid this problem I found a patch from peter warasin in 2007 for
openvpn 2.0.9.
This is the patch for the same problem adapted to openvpn 2.1_rc15.
If you find the patch useful please integrate it in the next release.
The patch is backward compatible as long as the push list will not
overflow the MAX_PUSH_LIST_LEN limit.


*** openvpn-2.1_rc15/options.c  Tue Nov 18 05:59:13 2008
--- openvpn-2.1_rc15_hc/options.c       Tue Dec  9 10:23:24 2008
***************
*** 941,946 ****
--- 941,947 ----
  show_p2mp_parms (const struct options *o)
  {
    struct gc_arena gc = gc_new ();
+   struct push_list_item *item = NULL;

  #if P2MP_SERVER
msg (D_SHOW_PARMS, " server_network = %s", print_in_addr_t (o- >server_network, 0, &gc));
***************
*** 951,959 ****
msg (D_SHOW_PARMS, " server_bridge_pool_end = %s", print_in_addr_t (o->server_bridge_pool_end, 0, &gc));
    if (o->push_list)
      {
!       const struct push_list *l = o->push_list;
!       const char *printable_push_list = l->options;
!       msg (D_SHOW_PARMS, "  push_list = '%s'", printable_push_list);
      }
    SHOW_BOOL (ifconfig_pool_defined);
msg (D_SHOW_PARMS, " ifconfig_pool_start = %s", print_in_addr_t (o->ifconfig_pool_start, 0, &gc));
--- 952,963 ----
msg (D_SHOW_PARMS, " server_bridge_pool_end = %s", print_in_addr_t (o->server_bridge_pool_end, 0, &gc));
    if (o->push_list)
      {
!       msg (D_SHOW_PARMS, "  push_list = {");
! for (item = o->push_list->options; item != NULL; item = item- >next)
!         {
!           msg (D_SHOW_PARMS, "    %s", item->item);
!         }
!       msg (D_SHOW_PARMS, "              }");
      }
    SHOW_BOOL (ifconfig_pool_defined);
msg (D_SHOW_PARMS, " ifconfig_pool_start = %s", print_in_addr_t (o->ifconfig_pool_start, 0, &gc));
***************
*** 998,1003 ****
--- 1002,1021 ----

  #endif /* ENABLE_DEBUG */

+
+ void
+ add_push_option(struct push_list *pl, struct gc_arena *gc, const char *option)
+ {
+   struct push_list_item *item;
+
+   ALLOC_OBJ_CLEAR_GC (item, struct push_list_item, gc);
+   strncpy(item->item, option, sizeof(item->item));
+
+   item->next = pl->options;
+   pl->options = item;
+ }
+
+
  #if P2MP_SERVER

  static void
***************
*** 1051,1064 ****
  void
  options_detach (struct options *o)
  {
    gc_detach (&o->gc);
    o->routes = NULL;
  #if P2MP_SERVER
    if (o->push_list) /* clone push_list */
      {
        const struct push_list *old = o->push_list;
!       ALLOC_OBJ_GC (o->push_list, struct push_list, &o->gc);
!       strcpy (o->push_list->options, old->options);
      }
  #endif
  }
--- 1069,1085 ----
  void
  options_detach (struct options *o)
  {
+   struct gc_arena gc = o->gc;
    gc_detach (&o->gc);
    o->routes = NULL;
  #if P2MP_SERVER
    if (o->push_list) /* clone push_list */
      {
        const struct push_list *old = o->push_list;
!       struct push_list_item *item = NULL;
!       ALLOC_OBJ_CLEAR_GC (o->push_list, struct push_list, &o->gc);
!       for (item = old->options; item != NULL; item = item->next)
!         add_push_option(o->push_list, &o->gc, item->item);
      }
  #endif
  }
***************
*** 3177,3200 ****
      }
  }

  bool
  apply_push_options (struct options *options,
-                   struct buffer *buf,
                    unsigned int permission_mask,
                    unsigned int *option_types_found,
                    struct env_set *es)
  {
-   char line[OPTION_PARM_SIZE];
    int line_num = 0;
    const char *file = "[PUSH-OPTIONS]";
    const int msglevel = D_PUSH_ERRORS|M_OPTERR;

!   while (buf_parse (buf, ',', line, sizeof (line)))
      {
        char *p[MAX_PARMS];
        CLEAR (p);
        ++line_num;
! if (parse_line (line, p, SIZE (p), file, line_num, msglevel, &options->gc))
        {
add_option (options, p, file, line_num, 0, msglevel, permission_mask, option_types_found, es);
        }
--- 3198,3254 ----
      }
  }

+ void
+ push_list_to_buf(struct push_list *pl, struct buffer *buf)
+ {
+   struct push_list_item *item;
+   bool first = true;
+
+   if (! pl)
+     return;
+
+   for (item = pl->options; item != NULL; item = item->next)
+     if (first)
+       buf_printf(buf, "%s", item->item);
+     else
+       buf_printf(buf, ",%s", item->item);
+ }
+
+ void
+ store_pull_buffer (struct options *options,
+                  struct buffer *buf)
+ {
+   char line[OPTION_PARM_SIZE];
+
+   if (options->pull_buffer == NULL)
+ ALLOC_OBJ_CLEAR_GC (options->pull_buffer, struct push_list, &options->gc);
+
+   while (buf_parse (buf, ',', line, sizeof (line)))
+     add_push_option(options->pull_buffer, &options->gc, line);
+
+   options->pull_buffer->chunks++;
+ }
+
  bool
  apply_push_options (struct options *options,
                    unsigned int permission_mask,
                    unsigned int *option_types_found,
                    struct env_set *es)
  {
    int line_num = 0;
    const char *file = "[PUSH-OPTIONS]";
    const int msglevel = D_PUSH_ERRORS|M_OPTERR;
+   struct push_list_item *item = NULL;

!   if (! options->pull_buffer)
!     return true;
!
! for (item = options->pull_buffer->options; item != NULL; item = item->next)
      {
        char *p[MAX_PARMS];
        CLEAR (p);
        ++line_num;
! if (parse_line (item->item, p, SIZE (p), file, line_num, msglevel, &options->gc))
        {
add_option (options, p, file, line_num, 0, msglevel, permission_mask, option_types_found, es);
        }
*** openvpn-2.1_rc15/options.h  Tue Nov 18 05:43:47 2008
--- openvpn-2.1_rc15_hc/options.h       Tue Dec  9 10:23:24 2008
***************
*** 60,70 ****
  #if P2MP_SERVER
  /* parameters to be pushed to peer */

! #define MAX_PUSH_LIST_LEN TLS_CHANNEL_BUF_SIZE /* This parm is related to PLAINTEXT_BUFFER_SIZE in ssl.h */

  struct push_list {
    /* newline delimited options, like config file */
!   char options[MAX_PUSH_LIST_LEN];
  };
  #endif

--- 60,83 ----
  #if P2MP_SERVER
  /* parameters to be pushed to peer */

! /* keep for backwards compatibility */
! #define MAX_PUSH_LIST_LEN TLS_CHANNEL_BUF_SIZE / 2 /* This parm is related to PLAINTEXT_BUFFER_SIZE in ssl.h */
!
! struct push_list_item {
!   /* one option per entry, items are like config file */
!   char item[OPTION_LINE_SIZE];
!   struct push_list_item *next;
! };

  struct push_list {
    /* newline delimited options, like config file */
!   struct push_list_item *options;
!
!   /* points to the next to be sent item */
!   struct push_list_item *sent;
!
!   /* chunk counter */
!   int chunks;
  };
  #endif

***************
*** 358,363 ****
--- 371,377 ----
    in_addr_t server_bridge_pool_end;

    struct push_list *push_list;
+   struct push_list *pull_buffer;
    bool ifconfig_pool_defined;
    in_addr_t ifconfig_pool_start;
    in_addr_t ifconfig_pool_end;
***************
*** 631,641 ****
  void pre_pull_restore (struct options *o);

  bool apply_push_options (struct options *options,
-                        struct buffer *buf,
                         unsigned int permission_mask,
                         unsigned int *option_types_found,
                         struct env_set *es);

  bool is_persist_option (const struct options *o);
  bool is_stateful_restart (const struct options *o);

--- 645,659 ----
  void pre_pull_restore (struct options *o);

  bool apply_push_options (struct options *options,
                         unsigned int permission_mask,
                         unsigned int *option_types_found,
                         struct env_set *es);

+ void add_push_option(struct push_list *pl, struct gc_arena *gc, const char *option);
+ void push_list_to_buf(struct push_list *pl, struct buffer *buf);
+ void store_pull_buffer (struct options *options,
+                       struct buffer *buf);
+
  bool is_persist_option (const struct options *o);
  bool is_stateful_restart (const struct options *o);

*** openvpn-2.1_rc15/push.c     Mon Oct  6 09:22:21 2008
--- openvpn-2.1_rc15_hc/push.c  Tue Dec  9 10:23:24 2008
***************
*** 99,104 ****
--- 99,109 ----

    if (status == PUSH_MSG_ERROR)
msg (D_PUSH_ERRORS, "WARNING: Received bad push/pull message: % s", BSTR (buffer));
+   else if (status == PUSH_MSG_CHUNKED_REPLY)
+     {
+       if (option_types_found)
+       do_deferred_options (c, option_types_found);
+     }
    else if (status == PUSH_MSG_REPLY)
      {
do_up (c, true, option_types_found); /* delay bringing tun/ tap up until --push parms received from remote */
***************
*** 115,151 ****
  }

  #if P2MP_SERVER
  bool
  send_push_reply (struct context *c)
  {
    struct gc_arena gc = gc_new ();
    struct buffer buf = alloc_buf_gc (MAX_PUSH_LIST_LEN + 256, &gc);
    bool ret = false;

!   buf_printf (&buf, "PUSH_REPLY");
!
!   if (c->options.push_list && strlen (c->options.push_list->options))
!     buf_printf (&buf, ",%s", c->options.push_list->options);

! if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c- >c2.push_ifconfig_remote_netmask)
!     buf_printf (&buf, ",ifconfig %s %s",
!               print_in_addr_t (c->c2.push_ifconfig_local, 0, &gc),
! print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, &gc));

!   if (strlen (BSTR (&buf)) < MAX_PUSH_LIST_LEN)
!     ret = send_control_channel_string (c, BSTR (&buf), D_PUSH);
    else
! msg (M_WARN, "Maximum length of --push buffer (%d) has been exceeded", MAX_PUSH_LIST_LEN);

    gc_free (&gc);
    return ret;
  }

  void
  push_option (struct options *o, const char *opt, int msglevel)
  {
    int len;
-   bool first = false;

    if (!string_class (opt, CC_ANY, CC_COMMA))
      {
--- 120,216 ----
  }

  #if P2MP_SERVER
+
+ bool
+ create_next_chunk(struct buffer *buf, struct options *o)
+ {
+   struct push_list *pl = o->push_list;
+   struct push_list_item *item;
+
+   if (pl == NULL)
+       return false;
+
+   if (pl->sent == NULL)
+       pl->sent = pl->options;
+
+   pl->chunks++;
+
+   item = pl->sent;
+   while (item && (strlen (BSTR (buf)) < MAX_PUSH_LIST_LEN))
+     {
+       buf_printf (buf, ",%s", item->item);
+       item = item->next;
+     }
+   pl->sent = item;
+
+   if (item)
+     return true;
+   return false;
+ }
+
  bool
  send_push_reply (struct context *c)
  {
    struct gc_arena gc = gc_new ();
    struct buffer buf = alloc_buf_gc (MAX_PUSH_LIST_LEN + 256, &gc);
+ struct buffer push_buf = alloc_buf_gc (MAX_PUSH_LIST_LEN + 256, &gc);
+   struct push_list *pl = NULL;
    bool ret = false;
+   bool more_chunks = false;
+   bool first_chunk = false;

!   if (c->options.push_list && !c->options.push_list->sent)
!     first_chunk = true;

!   if (c->options.push_list)
!     more_chunks = create_next_chunk(&push_buf, &c->options);
!   else
!     first_chunk = true;

!   if (! more_chunks)
!     buf_printf (&buf, "PUSH_REPLY");
    else
!     buf_printf (&buf, "PUSH_CHUNKED_REPLY");
!
!   if (! more_chunks)
!     {
! if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask)
!         buf_printf (&buf, ",ifconfig %s %s",
!                   print_in_addr_t (c->c2.push_ifconfig_local, 0, &gc),
! print_in_addr_t (c- >c2.push_ifconfig_remote_netmask, 0, &gc));
!     }
!
!   buf_printf (&buf, "%s", BSTR(&push_buf));
!
!   if (more_chunks)
! msg (M_WARN, "Maximum length of --push buffer (%d) has been exceeded. Send as chunks. Old clients will accept only the last chunk", MAX_PUSH_LIST_LEN);
!
!   ret = send_control_channel_string (c, BSTR (&buf), D_PUSH);

    gc_free (&gc);
    return ret;
  }

+ bool
+ push_buffer_exceeded(struct push_list_item *pl)
+ {
+   int size = 0;
+   struct push_list_item *item = NULL;
+   if (! pl)
+     return false;
+   for (item = pl; item != NULL; item = item->next)
+     {
+       size += strlen(item->item) + 2;
+       if (size >= MAX_PUSH_LIST_LEN)
+         return true;
+     }
+   return false;
+ }
+
  void
  push_option (struct options *o, const char *opt, int msglevel)
  {
    int len;

    if (!string_class (opt, CC_ANY, CC_COMMA))
      {
***************
*** 154,175 ****
    else
      {
        if (!o->push_list)
!       {
          ALLOC_OBJ_CLEAR_GC (o->push_list, struct push_list, &o->gc);
!         first = true;
!       }
!
!       len = strlen (o->push_list->options);
!       if (len + strlen (opt) + 2 >= MAX_PUSH_LIST_LEN)
!       {
! msg (msglevel, "Maximum length of --push buffer (%d) has been exceeded", MAX_PUSH_LIST_LEN);
!       }
!       else
!       {
!         if (!first)
!           strcat (o->push_list->options, ",");
!         strcat (o->push_list->options, opt);
!       }
      }
  }

--- 219,228 ----
    else
      {
        if (!o->push_list)
!         {
          ALLOC_OBJ_CLEAR_GC (o->push_list, struct push_list, &o->gc);
!         }
!       add_push_option(o->push_list, &o->gc, opt);
      }
  }

***************
*** 219,243 ****
    else
  #endif

! if (honor_received_options && buf_string_compare_advance (&buf, "PUSH_REPLY"))
      {
!       const uint8_t ch = buf_read_u8 (&buf);
!       if (ch == ',')
!       {
!         pre_pull_restore (&c->options);
! c->c2.pulled_options_string = string_alloc (BSTR (&buf), &c- >c2.gc);
!         if (apply_push_options (&c->options,
!                                 &buf,
!                                 permission_mask,
!                                 option_types_found,
!                                 c->c2.es))
            ret = PUSH_MSG_REPLY;
!       }
!       else if (ch == '\0')
!       {
!         ret = PUSH_MSG_REPLY;
!       }
!       /* show_settings (&c->options); */
      }
    return ret;
  }
--- 272,319 ----
    else
  #endif

!   if (honor_received_options)
      {
! bool chunked = buf_string_compare_advance (&buf, "PUSH_CHUNKED_REPLY");
!       bool reply = buf_string_compare_advance (&buf, "PUSH_REPLY");
!
!       if (reply || chunked)
!         {
!           const uint8_t ch = buf_read_u8 (&buf);
!           if (ch == ',')
!           {
!               store_pull_buffer(&c->options, &buf);
! event_timeout_init (&c->c2.push_request_interval, 1, now);
!               reset_coarse_timers (c);
!
!               if (chunked)
!               ret = PUSH_MSG_CHUNKED_REPLY;
!               else
!               ret = PUSH_MSG_REPLY;
!           }
!           else if (ch == '\0')
            ret = PUSH_MSG_REPLY;
!
!           if (ret == PUSH_MSG_REPLY)
!             {
!             pre_pull_restore (&c->options);
!             if (apply_push_options (&c->options,
!                                     permission_mask,
!                                     option_types_found,
!                                     c->c2.es))
!                 {
! struct buffer pull_list = alloc_buf_gc ((MAX_PUSH_LIST_LEN + 256)*4, &c->c2.gc);
!
! push_list_to_buf(c->options.pull_buffer, &pull_list); ! c->c2.pulled_options_string = string_alloc (BSTR (&pull_list), &c->c2.gc);
!                   c->options.pull_buffer = NULL;
!                 }
!                 /* show_settings (&c->options); */
!               else
!               ret = PUSH_MSG_ERROR;
!
!             }
!         }
      }
    return ret;
  }
***************
*** 252,278 ****
    if (o && o->push_list && o->iroutes)
      {
        struct gc_arena gc = gc_new ();
!       struct push_list *pl;
!       struct buffer in, out;
!       char *line;
!       bool first = true;
!
!       /* prepare input and output buffers */
!       ALLOC_OBJ_CLEAR_GC (pl, struct push_list, &gc);
!       ALLOC_ARRAY_CLEAR_GC (line, char, MAX_PUSH_LIST_LEN, &gc);
!
! buf_set_read (&in, (const uint8_t*) o->push_list->options, strlen (o->push_list->options)); ! buf_set_write (&out, (uint8_t*) pl->options, sizeof (pl- >options));

        /* cycle through the push list */
!       while (buf_parse (&in, ',', line, MAX_PUSH_LIST_LEN))
        {
          char *p[MAX_PARMS];
!         bool copy = true;

          /* parse the push item */
          CLEAR (p);
! if (parse_line (line, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc))
            {
              /* is the push item a route directive? */
              if (p[0] && !strcmp (p[0], "route") && !p[3])
--- 328,345 ----
    if (o && o->push_list && o->iroutes)
      {
        struct gc_arena gc = gc_new ();
!       struct push_list_item *prev = NULL;
!       struct push_list_item *item = NULL;

        /* cycle through the push list */
! for (item = o->push_list->options; item != NULL; item = item- >next)
        {
          char *p[MAX_PARMS];
!           bool copy = true;

          /* parse the push item */
          CLEAR (p);
! if (parse_line (item->item, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc))
            {
              /* is the push item a route directive? */
              if (p[0] && !strcmp (p[0], "route") && !p[3])
***************
*** 300,324 ****
                }
            }

!         /* should we copy the push item? */
!         if (copy)
!           {
!             if (!first)
!               buf_printf (&out, ",");
!             buf_printf (&out, "%s", line);
!             first = false;
!           }
!         else
!           msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", line);
        }

- #if 0
-       msg (M_INFO, "BEFORE: '%s'", o->push_list->options);
-       msg (M_INFO, "AFTER:  '%s'", pl->options);
- #endif
-
-       /* copy new push list back to options */
-       *o->push_list = *pl;

        gc_free (&gc);
      }
--- 367,384 ----
                }
            }

!         /* should we remove the push items? */
!         if (! copy)
!             {
!             msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", item->item);
!               if (prev)
!                   prev->next = item->next;
!               else
!                   o->push_list->options = item->next;
!             }
!           prev = item;
        }


        gc_free (&gc);
      }
*** openvpn-2.1_rc15/push.h     Mon Oct  6 09:22:20 2008
--- openvpn-2.1_rc15_hc/push.h  Tue Dec  9 10:23:24 2008
***************
*** 34,39 ****
--- 34,40 ----
  #define PUSH_MSG_REPLY            2
  #define PUSH_MSG_REQUEST_DEFERRED 3
  #define PUSH_MSG_AUTH_FAILURE     4
+ #define PUSH_MSG_CHUNKED_REPLY    5

  void incoming_push_message (struct context *c,
                            const struct buffer *buffer);






CU
--
Gerd Pauli
http://www.high-consulting.de



Reply via email to