As agreed at today's IRC meeting, I created a test plugin showing the usage of a new plugin hook called OPENVPN_PLUGIN_ACCOUNTING
Please see https://community.openvpn.net/openvpn/ticket/15 for the plugin code. As agreed on IRC, I cooked up a demo implementation of this new plugin hook. Below is an except of the comment I added to the track ticket: Tks, Basically the idea is to be able to implement AAA (http://en.wikipedia.org/wiki/AAA_protocol) (through radius for instance) with an openvpn plugin. The benefit is that it is then possible to get network usage per client from within a plugin without starting a new process that will handle that in the background by parsing regurlarly openvpn-status.log or initiating a connection to the management console. The plugin is taking advantage of the return_list parameter available in openvpn_plugin_func_v2 to update the interval at which is wants to receive update for a specific client. A default value of 60 second is hardcoded within the options, but it can be changed at startup through --accounting-freq n command switch A value of 0 will disable accounting information to be sent. For compiling instructions and how to run the testing environmen, please check the comments in testplugin.c The scenario would be as follow: 1. plugin in invoke with type OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY plugin sends credentials to authentication server and returns success/error to main loop 2. on success, plugin is called with OPENVPN_PLUGIN_CLIENT_CONNECT_V2 * plugin send a message to accounting server informing it that the session starts * plugin receive an update interval value (let call it acct_interval) at which it should send updated information to accounting server. this acct_interval is passed to accounting server through return_list 3. time passes, traffic goes through 4. when acct_interval time is passed, the plugin is invoked with OPENVPN_PLUGIN_ACCOUNTING. bytes_sent/bytes_received being passed through envp * plugin contact accounting server and give it those value * accounting server might reply with a new acct_interval that plugin can pass back to openvpn server through return_list 5. 3) and 4) repeat 6. Client is disconnected, openvpn main loop will invoke plugin with OPENVPN_PLUGIN_CLIENT_DISCONNECT, plugin informs accounting server that session is stopped and that bytes_sent/bytes_received was used over time_duration Here is the output of a session realised with the attached test plugin,. Mind that after 1minute or so i disconnected the client, hence no packet were going through so the eventtimeout was not triggered at the exact second. In a normal scenario, update would not happen every so often, here low random values were given to illustrate the usage. Any things that are unclear, le me know. Tks Thu Jun 10 16:19:30 2010 192.168.51.1:40289 [ssss] Peer Connection Initiated with 192.168.51.1:40289 ACCT Thu Jun 10 16:19:30 2010 Sending start time to accounting server 1276179570 ACCT Thu Jun 10 16:19:30 2010 Plugin requested accounting update in 11 seconds -- Thu Jun 10 16:19:41 2010 DEBUG: ACCOUNTING triggerred after 11s for ssss ACCT Thu Jun 10 16:19:41 2010 Sending accounting information to server 5724tx/3663rx ACCT Thu Jun 10 16:19:41 2010 Acounting server requesting new update in 5 seconds -- Thu Jun 10 16:19:46 2010 DEBUG: ACCOUNTING triggerred after 5s for ssss ACCT Thu Jun 10 16:19:46 2010 Sending accounting information to server 6349tx/4288rx ACCT Thu Jun 10 16:19:46 2010 Acounting server requesting new update in 9 seconds -- Thu Jun 10 16:19:55 2010 DEBUG: ACCOUNTING triggerred after 9s for ssss ACCT Thu Jun 10 16:19:55 2010 Sending accounting information to server 7474tx/5413rx ACCT Thu Jun 10 16:19:55 2010 Acounting server requesting new update in 19 seconds -- Thu Jun 10 16:20:14 2010 DEBUG: ACCOUNTING triggerred after 19s for ssss ACCT Thu Jun 10 16:20:14 2010 Sending accounting information to server 9849tx/7788rx ACCT Thu Jun 10 16:20:14 2010 Acounting server requesting new update in 16 seconds -- http://www.debuntu.org !DSPAM:4c1151c761671553511658!
/** * vim: tabstop=2:shiftwidth=2:softtabstop=2:expandtab * Plugin demonstrating the use of * https://community.openvpn.net/openvpn/ticket/15 * * It simulates a user session where * * authentication occurs * user bandwidth consumption is updated to accounting server regularly * session close * * compile with * gcc -shared -Wl,-soname,accounting-test.so -o \ * accounting-test.so testplugin.c */ /* * server conf: * plugin /path/to/accounting-test.so * * server need to be configured with --enable-accounting * which is available in patch attached to * https://community.openvpn.net/openvpn/ticket/15 */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <stdarg.h> #include <time.h> #include "openvpn-plugin.h" /* * Our context, where we keep our state. */ struct plugin_context{ char *context; }; char *strdupf (const char *fmt, ...); const char *get_env (const char *name, const char *envp[]); OPENVPN_EXPORT openvpn_plugin_handle_t openvpn_plugin_open_v2 (unsigned int *type_mask, const char *argv[], const char *envp[], struct openvpn_plugin_string_list **return_list) { struct plugin_context *context; int rc = 0; /* * Allocate our context */ context = malloc (sizeof (struct plugin_context)); *type_mask = OPENVPN_PLUGIN_MASK ( OPENVPN_PLUGIN_CLIENT_CONNECT_V2) | OPENVPN_PLUGIN_MASK ( OPENVPN_PLUGIN_CLIENT_DISCONNECT ) | OPENVPN_PLUGIN_MASK ( OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY ) | OPENVPN_PLUGIN_MASK ( OPENVPN_PLUGIN_ACCOUNTING ); return (openvpn_plugin_handle_t) context; } OPENVPN_EXPORT int openvpn_plugin_func_v2 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[], void *per_client_context, struct openvpn_plugin_string_list **return_list) { struct plugin_context *context = (struct plugin_context *) handle; int acc_time; time_t now; char *now_str; time(&now); now_str = ctime(&now); if (type == OPENVPN_PLUGIN_CLIENT_CONNECT_V2){ acc_time = rand()%20 + 1; *return_list = malloc (sizeof (struct openvpn_plugin_string_list)); (*return_list)->next = NULL; (*return_list)->name = strdup ("config"); (*return_list)->value = strdupf("accounting-freq %d", acc_time); printf ("ACCT %s Sending start time to accounting server %s\n", now_str, get_env("time_unix", envp)); printf ("ACCT %s Plugin requested accounting update in %d seconds\n", now_str, acc_time); return OPENVPN_PLUGIN_FUNC_SUCCESS; } if (type == OPENVPN_PLUGIN_ACCOUNTING){ const char *tx, *rx; tx = get_env("bytes_sent", envp); rx = get_env("bytes_received", envp); printf ("ACCT %s Sending accounting information to server %stx/%srx\n", now_str, tx ? tx : "0", rx ? rx : "0"); *return_list = malloc (sizeof (struct openvpn_plugin_string_list)); (*return_list)->next = NULL; (*return_list)->name = strdup ("config"); acc_time = rand() % 20 + 1; (*return_list)->value = strdupf("accounting-freq %d", acc_time); printf("ACCT %s Acounting server requesting new update in %d seconds\n", now_str, acc_time); return OPENVPN_PLUGIN_FUNC_SUCCESS; } if (type == OPENVPN_PLUGIN_CLIENT_DISCONNECT){ const char *tx, *rx; tx = get_env("bytes_sent", envp); rx = get_env("bytes_received", envp); printf ("ACCT %s Sending accounting information to server %stx/%srx, session started at %s and last %s\n", now_str, tx ? tx : "0", rx ? rx : "0", get_env("time_unix", envp), get_env("time_duration", envp)); return OPENVPN_PLUGIN_FUNC_SUCCESS; } if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY){ return OPENVPN_PLUGIN_FUNC_SUCCESS; } return OPENVPN_PLUGIN_FUNC_ERROR; } /** * plugin_close_v1: * Called immediately prior to plug-in unload */ OPENVPN_EXPORT void openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle) { struct plugin_context *context = (struct plugin_context *) handle; free (context); } const char * get_env (const char *name, const char *envp[]) { if (envp){ int i; const int namelen = strlen (name); for (i = 0; envp[i]; ++i){ if (!strncmp (envp[i], name, namelen)) { const char *cp = envp[i] + namelen; if (*cp == '=') return cp + 1; } } } return NULL; } char * strdupf (const char *fmt, ...){ va_list vargs; char buf[BUFSIZ]; char *p; if (!fmt) { return (NULL); } va_start (vargs, fmt); vsnprintf (buf, sizeof (buf), fmt, vargs); va_end (vargs); buf[sizeof (buf) - 1] = '\0'; /* ensure buf is NUL-terminated */ if (!(p = strdup (buf))) { return (NULL); } return (p); }