hi guys i run into the same problem as some of you, since i need the possibility to push way more routes to the clients. but currently the push buffer is limited which does not let you push more than about 15 routes.
so i wrote a patch which removes this limitation. please find the patch attached to this posting. it is agains 2.0.9, tested on linux as client/server. the patch makes the push_list dynamic and sends it to the clients as chunks. in order not to break older clients this will happen only if the push buffer will be exceeded. however if exceeded, older clients can still connect, print out warnings and connect successfully but certainly don't apply the entire push list. there is still a limitation. the openvpn routing table is still static and holds max 100 routes, so you will not be able to push more than 100 routes. that value could be easily increased and/or if necessary this could also made dynamic (however with deeper changes to the source). please let me know what you think. if you find it useful i will port the patch to the 2.1 branch peter -- :: e n d i a n :: open source - open minds :: peter warasin :: http://www.endian.it :: pe...@endian.it
--- openvpn-2.0.9/options.c.orig 2007-01-24 23:23:42.000000000 +0100 +++ openvpn-2.0.9/options.c 2007-02-02 20:47:32.000000000 +0100 @@ -773,6 +773,7 @@ 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)); @@ -783,9 +784,12 @@ 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); + 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)); @@ -828,6 +832,20 @@ #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 @@ -901,14 +919,17 @@ 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; - ALLOC_OBJ_GC (o->push_list, struct push_list, &o->gc); - strcpy (o->push_list->options, old->options); + 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 } @@ -2520,24 +2541,57 @@ } } +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, - 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; + struct push_list_item *item = NULL; - while (buf_parse (buf, ',', line, sizeof (line))) + 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 (line, p, SIZE (p), file, line_num, msglevel, &options->gc)) + if (parse_line (item->item, p, SIZE (p), file, line_num, msglevel, &options->gc)) { add_option (options, 0, p, file, line_num, 0, msglevel, permission_mask, option_types_found, es); } --- openvpn-2.0.9/options.h.orig 2007-01-24 23:23:54.000000000 +0100 +++ openvpn-2.0.9/options.h 2007-02-02 17:11:18.000000000 +0100 @@ -59,11 +59,24 @@ #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 */ +/* 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 */ - char options[MAX_PUSH_LIST_LEN]; + struct push_list_item *options; + + /* points to the next to be sent item */ + struct push_list_item *sent; + + /* chunk counter */ + int chunks; }; #endif @@ -290,6 +303,7 @@ 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; @@ -516,11 +530,15 @@ 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); +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.0.9/push.c.orig 2007-01-24 23:24:04.000000000 +0100 +++ openvpn-2.0.9/push.c 2007-02-02 20:51:44.000000000 +0100 @@ -104,6 +104,11 @@ 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 */ @@ -120,37 +125,97 @@ } #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; - 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->options.push_list && !c->options.push_list->sent) + first_chunk = true; - 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 (c->options.push_list) + more_chunks = create_next_chunk(&push_buf, &c->options); + else + first_chunk = true; - if (strlen (BSTR (&buf)) < MAX_PUSH_LIST_LEN) - ret = send_control_channel_string (c, BSTR (&buf), D_PUSH); + if (! more_chunks) + buf_printf (&buf, "PUSH_REPLY"); else - msg (M_WARN, "Maximum length of --push buffer (%d) has been exceeded", MAX_PUSH_LIST_LEN); + 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; - bool first = false; if (!string_class (opt, CC_ANY, CC_COMMA)) { @@ -159,22 +224,10 @@ 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); - } + } + add_push_option(o->push_list, &o->gc, opt); } } @@ -217,25 +270,48 @@ else #endif - if (honor_received_options && buf_string_compare_advance (&buf, "PUSH_REPLY")) + if (honor_received_options) { - 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)) + 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; - } - else if (ch == '\0') - { - ret = PUSH_MSG_REPLY; - } - /* show_settings (&c->options); */ + + 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; } @@ -250,27 +326,18 @@ 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)); + struct push_list_item *prev = NULL; + struct push_list_item *item = NULL; /* cycle through the push list */ - while (buf_parse (&in, ',', line, MAX_PUSH_LIST_LEN)) + for (item = o->push_list->options; item != NULL; item = item->next) { char *p[MAX_PARMS]; - bool copy = true; + bool copy = true; /* parse the push item */ CLEAR (p); - if (parse_line (line, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc)) + 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] && p[1] && p[2] && !strcmp (p[0], "route")) @@ -298,25 +365,18 @@ } } - /* 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); + /* 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; } -#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); } --- openvpn-2.0.9/push.h.orig 2007-01-26 00:06:45.000000000 +0100 +++ openvpn-2.0.9/push.h 2007-01-26 00:20:11.000000000 +0100 @@ -34,6 +34,7 @@ #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);
<<attachment: peter.vcf>>