don't route Framed-IP-Address if contained in a Framed-Route
[l2tpns.git] / l2tpns.c
index cf7ef18..b7945f6 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -4,7 +4,7 @@
 // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced
 // vim: sw=8 ts=8
 
 // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced
 // vim: sw=8 ts=8
 
-char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.48 2004-11-11 03:07:42 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.54 2004-11-18 09:02:29 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -58,31 +58,32 @@ struct configt *config = NULL;      // all configuration
 int tunfd = -1;                        // tun interface file handle. (network device)
 int udpfd = -1;                        // UDP file handle
 int controlfd = -1;            // Control signal handle
 int tunfd = -1;                        // tun interface file handle. (network device)
 int udpfd = -1;                        // UDP file handle
 int controlfd = -1;            // Control signal handle
+int clifd = -1;                        // Socket listening for CLI connections.
 int snoopfd = -1;              // UDP file handle for sending out intercept data
 int *radfds = NULL;            // RADIUS requests file handles
 int ifrfd = -1;                        // File descriptor for routing, etc
 time_t basetime = 0;           // base clock
 char hostname[1000] = "";      // us.
 int snoopfd = -1;              // UDP file handle for sending out intercept data
 int *radfds = NULL;            // RADIUS requests file handles
 int ifrfd = -1;                        // File descriptor for routing, etc
 time_t basetime = 0;           // base clock
 char hostname[1000] = "";      // us.
-int tunidx;                    // ifr_ifindex of tun device
-u32 sessionid = 0;             // session id for radius accounting
-int syslog_log = 0;            // are we logging to syslog
-FILE *log_stream = NULL;       // file handle for direct logging (i.e. direct into file, not via syslog).
+static u32 sessionid = 0;      // session id for radius accounting
+static int syslog_log = 0;     // are we logging to syslog
+static FILE *log_stream = NULL;        // file handle for direct logging (i.e. direct into file, not via syslog).
 extern int cluster_sockfd;     // Intra-cluster communications socket.
 u32 last_id = 0;               // Last used PPP SID. Can I kill this?? -- mo
 extern int cluster_sockfd;     // Intra-cluster communications socket.
 u32 last_id = 0;               // Last used PPP SID. Can I kill this?? -- mo
-int clifd = 0;                 // Socket listening for CLI connections.
 
 struct cli_session_actions *cli_session_actions = NULL;        // Pending session changes requested by CLI
 struct cli_tunnel_actions *cli_tunnel_actions = NULL;  // Pending tunnel changes required by CLI
 
 static void *ip_hash[256];     // Mapping from IP address to session structures.
 
 
 struct cli_session_actions *cli_session_actions = NULL;        // Pending session changes requested by CLI
 struct cli_tunnel_actions *cli_tunnel_actions = NULL;  // Pending tunnel changes required by CLI
 
 static void *ip_hash[256];     // Mapping from IP address to session structures.
 
-u32 udp_tx = 0, udp_rx = 0, udp_rx_pkt = 0;    // Global traffic counters.
-u32 eth_tx = 0, eth_rx = 0, eth_rx_pkt = 0;
-u32 ip_pool_size = 1;          // Size of the pool of addresses used for dynamic address allocation.
-time_t time_now = 0;           // Current time in seconds since epoch.
-char time_now_string[64] = {0};        // Current time as a string.
-char main_quit = 0;            // True if we're in the process of exiting.
-char *_program_name = NULL;
+// Traffic counters.
+static u32 udp_rx = 0, udp_rx_pkt = 0, udp_tx = 0;
+static u32 eth_rx = 0, eth_rx_pkt = 0;
+u32 eth_tx = 0;
+
+static u32 ip_pool_size = 1;           // Size of the pool of addresses used for dynamic address allocation.
+time_t time_now = 0;                   // Current time in seconds since epoch.
+static char time_now_string[64] = {0}; // Current time as a string.
+static char main_quit = 0;             // True if we're in the process of exiting.
 linked_list *loaded_plugins;
 linked_list *plugins[MAX_PLUGIN_TYPES];
 
 linked_list *loaded_plugins;
 linked_list *plugins[MAX_PLUGIN_TYPES];
 
@@ -123,7 +124,7 @@ struct config_descriptt config_values[] = {
        { NULL, 0, 0, 0 },
 };
 
        { NULL, 0, 0, 0 },
 };
 
-char *plugin_functions[] = {
+static char *plugin_functions[] = {
        NULL,
        "plugin_pre_auth",
        "plugin_post_auth",
        NULL,
        "plugin_pre_auth",
        "plugin_post_auth",
@@ -145,30 +146,36 @@ sessiont *session = NULL;         // Array of session structures.
 sessioncountt *sess_count = NULL;      // Array of partial per-session traffic counters.
 radiust *radius = NULL;                        // Array of radius structures.
 ippoolt *ip_address_pool = NULL;       // Array of dynamic IP addresses.
 sessioncountt *sess_count = NULL;      // Array of partial per-session traffic counters.
 radiust *radius = NULL;                        // Array of radius structures.
 ippoolt *ip_address_pool = NULL;       // Array of dynamic IP addresses.
-controlt *controlfree = 0;
+static controlt *controlfree = 0;
 struct Tstats *_statistics = NULL;
 #ifdef RINGBUFFER
 struct Tringbuffer *ringbuffer = NULL;
 #endif
 
 struct Tstats *_statistics = NULL;
 #ifdef RINGBUFFER
 struct Tringbuffer *ringbuffer = NULL;
 #endif
 
-void sigalrm_handler(int);
-void sighup_handler(int);
-void sigterm_handler(int);
-void sigquit_handler(int);
-void sigchild_handler(int);
-void read_config_file();
-void read_state();
-void dump_state();
-void tunnel_clean();
-tunnelidt new_tunnel();
-void update_config();
-int unhide_avp(u8 *avp, tunnelidt t, sessionidt s, u16 length);
-
 static void cache_ipmap(ipt ip, int s);
 static void uncache_ipmap(ipt ip);
 static void cache_ipmap(ipt ip, int s);
 static void uncache_ipmap(ipt ip);
+static void free_ip_address(sessionidt s);
+static void dump_acct_info(void);
+static void sighup_handler(int sig);
+static void sigalrm_handler(int sig);
+static void sigterm_handler(int sig);
+static void sigquit_handler(int sig);
+static void sigchild_handler(int sig);
+static void read_state(void);
+static void dump_state(void);
+static void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **challenge_response);
+static void update_config(void);
+static void read_config_file(void);
+static void initplugins(void);
+static int add_plugin(char *plugin_name);
+static int remove_plugin(char *plugin_name);
+static void plugins_done(void);
+static void processcontrol(u8 * buf, int len, struct sockaddr_in *addr, int alen);
+static tunnelidt new_tunnel(void);
+static int unhide_avp(u8 *avp, tunnelidt t, sessionidt s, u16 length);
 
 // return internal time (10ths since process startup)
 
 // return internal time (10ths since process startup)
-clockt now(void)
+static clockt now(void)
 {
        struct timeval t;
        gettimeofday(&t, 0);
 {
        struct timeval t;
        gettimeofday(&t, 0);
@@ -286,12 +293,12 @@ void _log_hex(int level, const char *title, const char *data, int maxsize)
 // Add a route
 //
 // This adds it to the routing table, advertises it
 // Add a route
 //
 // This adds it to the routing table, advertises it
-// via iBGP if enabled, and stuffs it into the
+// via BGP if enabled, and stuffs it into the
 // 'sessionbyip' cache.
 //
 // 'ip' and 'mask' must be in _host_ order.
 //
 // 'sessionbyip' cache.
 //
 // 'ip' and 'mask' must be in _host_ order.
 //
-void routeset(sessionidt s, ipt ip, ipt mask, ipt gw, u8 add)
+static void routeset(sessionidt s, ipt ip, ipt mask, ipt gw, u8 add)
 {
        struct rtentry r;
        int i;
 {
        struct rtentry r;
        int i;
@@ -331,7 +338,7 @@ void routeset(sessionidt s, ipt ip, ipt mask, ipt gw, u8 add)
 #endif /* BGP */
 
                // Add/Remove the IPs to the 'sessionbyip' cache.
 #endif /* BGP */
 
                // Add/Remove the IPs to the 'sessionbyip' cache.
-               // Note that we add the zero address in the case of 
+               // Note that we add the zero address in the case of
                // a network route. Roll on CIDR.
 
                // Note that 's == 0' implies this is the address pool.
                // a network route. Roll on CIDR.
 
                // Note that 's == 0' implies this is the address pool.
@@ -350,7 +357,7 @@ void routeset(sessionidt s, ipt ip, ipt mask, ipt gw, u8 add)
 
 //
 // Set up TUN interface
 
 //
 // Set up TUN interface
-void inittun(void)
+static void inittun(void)
 {
        struct ifreq ifr;
        struct sockaddr_in sin = {0};
 {
        struct ifreq ifr;
        struct sockaddr_in sin = {0};
@@ -398,16 +405,10 @@ void inittun(void)
                LOG(0, 0, 0, 0, "Error setting tun flags: %s\n", strerror(errno));
                exit(1);
        }
                LOG(0, 0, 0, 0, "Error setting tun flags: %s\n", strerror(errno));
                exit(1);
        }
-       if (ioctl(ifrfd, SIOCGIFINDEX, (void *) &ifr) < 0)
-       {
-               LOG(0, 0, 0, 0, "Error setting tun ifindex: %s\n", strerror(errno));
-               exit(1);
-       }
-       tunidx = ifr.ifr_ifindex;
 }
 
 // set up UDP port
 }
 
 // set up UDP port
-void initudp(void)
+static void initudp(void)
 {
        int on = 1;
        struct sockaddr_in addr;
 {
        int on = 1;
        struct sockaddr_in addr;
@@ -433,8 +434,8 @@ void initudp(void)
        // Control
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        // Control
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
-       addr.sin_port = htons(1702);
-       controlfd = socket(AF_INET, SOCK_DGRAM, 17);
+       addr.sin_port = htons(NSCTL_PORT);
+       controlfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
        if (bind(controlfd, (void *) &addr, sizeof(addr)) < 0)
        {
        setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
        if (bind(controlfd, (void *) &addr, sizeof(addr)) < 0)
        {
@@ -458,7 +459,7 @@ void initudp(void)
 // IP address.
 //
 
 // IP address.
 //
 
-int lookup_ipmap(ipt ip)
+static int lookup_ipmap(ipt ip)
 {
        u8 *a = (u8 *)&ip;
        char **d = (char **) ip_hash;
 {
        u8 *a = (u8 *)&ip;
        char **d = (char **) ip_hash;
@@ -619,13 +620,13 @@ void send_garp(ipt ip)
 }
 
 // Find session by username, 0 for not found
 }
 
 // Find session by username, 0 for not found
-sessiont *sessiontbysessionidt(sessionidt s)
+static sessiont *sessiontbysessionidt(sessionidt s)
 {
        if (!s || s > MAXSESSION) return NULL;
        return &session[s];
 }
 
 {
        if (!s || s > MAXSESSION) return NULL;
        return &session[s];
 }
 
-sessionidt sessionidtbysessiont(sessiont *s)
+static sessionidt sessionidtbysessiont(sessiont *s)
 {
        sessionidt val = s-session;
        if (s < session || val > MAXSESSION) return 0;
 {
        sessionidt val = s-session;
        if (s < session || val > MAXSESSION) return 0;
@@ -700,7 +701,7 @@ int tun_write(u8 * data, int size)
 
 // process outgoing (to tunnel) IP
 //
 
 // process outgoing (to tunnel) IP
 //
-void processipout(u8 * buf, int len)
+static void processipout(u8 * buf, int len)
 {
        sessionidt s;
        sessiont *sp;
 {
        sessionidt s;
        sessiont *sp;
@@ -805,7 +806,7 @@ void processipout(u8 * buf, int len)
 // Helper routine for the TBF filters.
 // Used to send queued data in to the user!
 //
 // Helper routine for the TBF filters.
 // Used to send queued data in to the user!
 //
-void send_ipout(sessionidt s, u8 *buf, int len)
+static void send_ipout(sessionidt s, u8 *buf, int len)
 {
        sessiont *sp;
        tunnelidt t;
 {
        sessiont *sp;
        tunnelidt t;
@@ -856,7 +857,7 @@ void send_ipout(sessionidt s, u8 *buf, int len)
 }
 
 // add an AVP (16 bit)
 }
 
 // add an AVP (16 bit)
-void control16(controlt * c, u16 avp, u16 val, u8 m)
+static void control16(controlt * c, u16 avp, u16 val, u8 m)
 {
        u16 l = (m ? 0x8008 : 0x0008);
        *(u16 *) (c->buf + c->length + 0) = htons(l);
 {
        u16 l = (m ? 0x8008 : 0x0008);
        *(u16 *) (c->buf + c->length + 0) = htons(l);
@@ -867,7 +868,7 @@ void control16(controlt * c, u16 avp, u16 val, u8 m)
 }
 
 // add an AVP (32 bit)
 }
 
 // add an AVP (32 bit)
-void control32(controlt * c, u16 avp, u32 val, u8 m)
+static void control32(controlt * c, u16 avp, u32 val, u8 m)
 {
        u16 l = (m ? 0x800A : 0x000A);
        *(u16 *) (c->buf + c->length + 0) = htons(l);
 {
        u16 l = (m ? 0x800A : 0x000A);
        *(u16 *) (c->buf + c->length + 0) = htons(l);
@@ -878,7 +879,7 @@ void control32(controlt * c, u16 avp, u32 val, u8 m)
 }
 
 // add an AVP (32 bit)
 }
 
 // add an AVP (32 bit)
-void controls(controlt * c, u16 avp, char *val, u8 m)
+static void controls(controlt * c, u16 avp, char *val, u8 m)
 {
        u16 l = ((m ? 0x8000 : 0) + strlen(val) + 6);
        *(u16 *) (c->buf + c->length + 0) = htons(l);
 {
        u16 l = ((m ? 0x8000 : 0) + strlen(val) + 6);
        *(u16 *) (c->buf + c->length + 0) = htons(l);
@@ -889,7 +890,7 @@ void controls(controlt * c, u16 avp, char *val, u8 m)
 }
 
 // add a binary AVP
 }
 
 // add a binary AVP
-void controlb(controlt * c, u16 avp, char *val, unsigned int len, u8 m)
+static void controlb(controlt * c, u16 avp, char *val, unsigned int len, u8 m)
 {
        u16 l = ((m ? 0x8000 : 0) + len + 6);
        *(u16 *) (c->buf + c->length + 0) = htons(l);
 {
        u16 l = ((m ? 0x8000 : 0) + len + 6);
        *(u16 *) (c->buf + c->length + 0) = htons(l);
@@ -900,7 +901,7 @@ void controlb(controlt * c, u16 avp, char *val, unsigned int len, u8 m)
 }
 
 // new control connection
 }
 
 // new control connection
-controlt *controlnew(u16 mtype)
+static controlt *controlnew(u16 mtype)
 {
        controlt *c;
        if (!controlfree)
 {
        controlt *c;
        if (!controlfree)
@@ -920,7 +921,7 @@ controlt *controlnew(u16 mtype)
 
 // send zero block if nothing is waiting
 // (ZLB send).
 
 // send zero block if nothing is waiting
 // (ZLB send).
-void controlnull(tunnelidt t)
+static void controlnull(tunnelidt t)
 {
        u8 buf[12];
        if (tunnel[t].controlc) // Messages queued; They will carry the ack.
 {
        u8 buf[12];
        if (tunnel[t].controlc) // Messages queued; They will carry the ack.
@@ -936,7 +937,7 @@ void controlnull(tunnelidt t)
 }
 
 // add a control message to a tunnel, and send if within window
 }
 
 // add a control message to a tunnel, and send if within window
-void controladd(controlt * c, tunnelidt t, sessionidt s)
+static void controladd(controlt * c, tunnelidt t, sessionidt s)
 {
        *(u16 *) (c->buf + 2) = htons(c->length); // length
        *(u16 *) (c->buf + 4) = htons(tunnel[t].far); // tunnel
 {
        *(u16 *) (c->buf + 2) = htons(c->length); // length
        *(u16 *) (c->buf + 4) = htons(tunnel[t].far); // tunnel
@@ -1008,7 +1009,6 @@ void throttle_session(sessionidt s, int rate_in, int rate_out)
 // start tidy shutdown of session
 void sessionshutdown(sessionidt s, char *reason)
 {
 // start tidy shutdown of session
 void sessionshutdown(sessionidt s, char *reason)
 {
-       int dead = session[s].die;
        int walled_garden = session[s].walled_garden;
 
 
        int walled_garden = session[s].walled_garden;
 
 
@@ -1020,18 +1020,15 @@ void sessionshutdown(sessionidt s, char *reason)
                return;                   // not a live session
        }
 
                return;                   // not a live session
        }
 
-       if (!dead)
-               LOG(2, 0, s, session[s].tunnel, "Shutting down session %d: %s\n", s, reason);
-
-       session[s].die = now() + 150; // Clean up in 15 seconds
-
+       if (!session[s].die)
        {
                struct param_kill_session data = { &tunnel[session[s].tunnel], &session[s] };
        {
                struct param_kill_session data = { &tunnel[session[s].tunnel], &session[s] };
+               LOG(2, 0, s, session[s].tunnel, "Shutting down session %d: %s\n", s, reason);
                run_plugins(PLUGIN_KILL_SESSION, &data);
        }
 
        // RADIUS Stop message
                run_plugins(PLUGIN_KILL_SESSION, &data);
        }
 
        // RADIUS Stop message
-       if (session[s].opened && !walled_garden && !dead)
+       if (session[s].opened && !walled_garden && !session[s].die)
        {
                u16 r = session[s].radius;
                if (!r)
        {
                u16 r = session[s].radius;
                if (!r)
@@ -1055,15 +1052,20 @@ void sessionshutdown(sessionidt s, char *reason)
        if (session[s].ip)
        {                          // IP allocated, clear and unroute
                int r;
        if (session[s].ip)
        {                          // IP allocated, clear and unroute
                int r;
+               int routed = 0;
                for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++)
                {
                for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++)
                {
-                       routeset(s, session[s].route[r].ip, session[s].route[r].mask, session[s].ip, 0);
+                       if ((session[s].ip & session[s].route[r].mask) ==
+                           (session[s].route[r].ip & session[s].route[r].mask))
+                               routed++;
+
+                       routeset(s, session[s].route[r].ip, session[s].route[r].mask, 0, 0);
                        session[s].route[r].ip = 0;
                }
 
                if (session[s].ip_pool_index == -1) // static ip
                {
                        session[s].route[r].ip = 0;
                }
 
                if (session[s].ip_pool_index == -1) // static ip
                {
-                       routeset(s, session[s].ip, 0, 0, 0);    // Delete route.
+                       if (!routed) routeset(s, session[s].ip, 0, 0, 0);
                        session[s].ip = 0;
                }
                else
                        session[s].ip = 0;
                }
                else
@@ -1080,6 +1082,9 @@ void sessionshutdown(sessionidt s, char *reason)
                controladd(c, session[s].tunnel, s); // send the message
        }
 
                controladd(c, session[s].tunnel, s); // send the message
        }
 
+       if (!session[s].die)
+               session[s].die = now() + 150; // Clean up in 15 seconds
+
        cluster_send_session(s);
 }
 
        cluster_send_session(s);
 }
 
@@ -1129,7 +1134,7 @@ void sendipcp(tunnelidt t, sessionidt s)
 }
 
 // kill a session now
 }
 
 // kill a session now
-void sessionkill(sessionidt s, char *reason)
+static void sessionkill(sessionidt s, char *reason)
 {
 
        CSTAT(call_sessionkill);
 {
 
        CSTAT(call_sessionkill);
@@ -1149,8 +1154,15 @@ void sessionkill(sessionidt s, char *reason)
        cluster_send_session(s);
 }
 
        cluster_send_session(s);
 }
 
+static void tunnelclear(tunnelidt t)
+{
+       if (!t) return;
+       memset(&tunnel[t], 0, sizeof(tunnel[t]));
+       tunnel[t].state = TUNNELFREE;
+}
+
 // kill a tunnel now
 // kill a tunnel now
-void tunnelkill(tunnelidt t, char *reason)
+static void tunnelkill(tunnelidt t, char *reason)
 {
        sessionidt s;
        controlt *c;
 {
        sessionidt s;
        controlt *c;
@@ -1181,7 +1193,7 @@ void tunnelkill(tunnelidt t, char *reason)
 }
 
 // shut down a tunnel cleanly
 }
 
 // shut down a tunnel cleanly
-void tunnelshutdown(tunnelidt t, char *reason)
+static void tunnelshutdown(tunnelidt t, char *reason)
 {
        sessionidt s;
 
 {
        sessionidt s;
 
@@ -1198,7 +1210,7 @@ void tunnelshutdown(tunnelidt t, char *reason)
        // close session
        for (s = 1; s < MAXSESSION; s++)
                if (session[s].tunnel == t)
        // close session
        for (s = 1; s < MAXSESSION; s++)
                if (session[s].tunnel == t)
-                       sessionkill(s, reason);
+                       sessionshutdown(s, reason);
 
        tunnel[t].state = TUNNELDIE;
        tunnel[t].die = now() + 700; // Clean up in 70 seconds
 
        tunnel[t].state = TUNNELDIE;
        tunnel[t].die = now() + 700; // Clean up in 70 seconds
@@ -1921,7 +1933,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
 }
 
 // read and process packet on tun
 }
 
 // read and process packet on tun
-void processtun(u8 * buf, int len)
+static void processtun(u8 * buf, int len)
 {
        LOG_HEX(5, "Receive TUN Data", buf, len);
        STAT(tun_rx_packets);
 {
        LOG_HEX(5, "Receive TUN Data", buf, len);
        STAT(tun_rx_packets);
@@ -1949,7 +1961,7 @@ void processtun(u8 * buf, int len)
 // at once.
 #define MAX_ACTIONS 500
 
 // at once.
 #define MAX_ACTIONS 500
 
-int regular_cleanups(void)
+static int regular_cleanups(void)
 {
        static sessionidt s = 0;        // Next session to check for actions on.
        tunnelidt t;
 {
        static sessionidt s = 0;        // Next session to check for actions on.
        tunnelidt t;
@@ -2149,7 +2161,7 @@ int regular_cleanups(void)
 // Are we in the middle of a tunnel update, or radius
 // requests??
 //
 // Are we in the middle of a tunnel update, or radius
 // requests??
 //
-int still_busy(void)
+static int still_busy(void)
 {
        int i;
        static clockt last_talked = 0;
 {
        int i;
        static clockt last_talked = 0;
@@ -2199,7 +2211,7 @@ static fd_set readset;
 static int readset_n = 0;
 
 // main loop - gets packets on tun or udp and processes them
 static int readset_n = 0;
 
 // main loop - gets packets on tun or udp and processes them
-void mainloop(void)
+static void mainloop(void)
 {
        int i;
        u8 buf[65536];
 {
        int i;
        u8 buf[65536];
@@ -2317,7 +2329,7 @@ void mainloop(void)
                        }
 
                        if (FD_ISSET(controlfd, &r))
                        }
 
                        if (FD_ISSET(controlfd, &r))
-                               processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr);
+                               processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
 
                        if (FD_ISSET(clifd, &r))
                        {
 
                        if (FD_ISSET(clifd, &r))
                        {
@@ -2474,7 +2486,7 @@ static void stripdomain(char *host)
 }
 
 // Init data structures
 }
 
 // Init data structures
-void initdata(int optdebug, char *optconfig)
+static void initdata(int optdebug, char *optconfig)
 {
        int i;
 
 {
        int i;
 
@@ -2583,7 +2595,7 @@ void initdata(int optdebug, char *optconfig)
 #endif /* BGP */
 }
 
 #endif /* BGP */
 }
 
-int assign_ip_address(sessionidt s)
+static int assign_ip_address(sessionidt s)
 {
        u32 i;
        int best = -1;
 {
        u32 i;
        int best = -1;
@@ -2640,10 +2652,13 @@ int assign_ip_address(sessionidt s)
        return 1;
 }
 
        return 1;
 }
 
-void free_ip_address(sessionidt s)
+static void free_ip_address(sessionidt s)
 {
        int i = session[s].ip_pool_index;
 
 {
        int i = session[s].ip_pool_index;
 
+
+       CSTAT(call_free_ip_address);
+
        if (!session[s].ip)
                return; // what the?
 
        if (!session[s].ip)
                return; // what the?
 
@@ -2656,10 +2671,6 @@ void free_ip_address(sessionidt s)
        ip_address_pool[i].assigned = 0;
        ip_address_pool[i].session = 0;
        ip_address_pool[i].last = time_now;
        ip_address_pool[i].assigned = 0;
        ip_address_pool[i].session = 0;
        ip_address_pool[i].last = time_now;
-
-
-       CSTAT(call_free_ip_address);
-
 }
 
 //
 }
 
 //
@@ -2726,7 +2737,7 @@ void rebuild_address_pool(void)
 //
 // Fix the address pool to match a changed session.
 // (usually when the master sends us an update).
 //
 // Fix the address pool to match a changed session.
 // (usually when the master sends us an update).
-void fix_address_pool(int sid)
+static void fix_address_pool(int sid)
 {
        int ipid;
 
 {
        int ipid;
 
@@ -2747,7 +2758,7 @@ void fix_address_pool(int sid)
 //
 // Add a block of addresses to the IP pool to hand out.
 //
 //
 // Add a block of addresses to the IP pool to hand out.
 //
-void add_to_ip_pool(u32 addr, u32 mask)
+static void add_to_ip_pool(u32 addr, u32 mask)
 {
        int i;
        if (mask == 0)
 {
        int i;
        if (mask == 0)
@@ -2775,7 +2786,7 @@ void add_to_ip_pool(u32 addr, u32 mask)
 }
 
 // Initialize the IP address pool
 }
 
 // Initialize the IP address pool
-void initippool()
+static void initippool()
 {
        FILE *f;
        char *p;
 {
        FILE *f;
        char *p;
@@ -2861,7 +2872,7 @@ void snoop_send_packet(char *packet, u16 size, ipt destination, u16 port)
        STAT(packets_snooped);
 }
 
        STAT(packets_snooped);
 }
 
-void dump_acct_info()
+static void dump_acct_info()
 {
        char filename[1024];
        char timestr[64];
 {
        char filename[1024];
        char timestr[64];
@@ -2921,8 +2932,6 @@ int main(int argc, char *argv[])
        int optdebug = 0;
        char *optconfig = CONFIGFILE;
 
        int optdebug = 0;
        char *optconfig = CONFIGFILE;
 
-       _program_name = strdup(argv[0]);
-
        time(&basetime);             // start clock
 
        // scan args
        time(&basetime);             // start clock
 
        // scan args
@@ -2930,32 +2939,31 @@ int main(int argc, char *argv[])
        {
                switch (i)
                {
        {
                switch (i)
                {
-                       case 'd':
-                               if (fork()) exit(0);
-                               setsid();
-                               freopen("/dev/null", "r", stdin);
-                               freopen("/dev/null", "w", stdout);
-                               freopen("/dev/null", "w", stderr);
-                               break;
-                       case 'v':
-                               optdebug++;
-                               break;
-                       case 'c':
-                               optconfig = optarg;
-                               break;
-                       case 'h':
-                               snprintf(hostname, sizeof(hostname), "%s", optarg);
-                               break;
-                       default:
-                               printf("Args are:\n"
-                                      "\t-d\tDetach from terminal\n"
-                                      "\t-c <file>\tConfig file\n"
-                                      "\t-h <hostname>\tForce hostname\n"
-                                      "\t-a <address>\tUse specific address\n"
-                                      "\t-v\t\tDebug\n");
-
-                               return (0);
-                               break;
+               case 'd':
+                       if (fork()) exit(0);
+                       setsid();
+                       freopen("/dev/null", "r", stdin);
+                       freopen("/dev/null", "w", stdout);
+                       freopen("/dev/null", "w", stderr);
+                       break;
+               case 'v':
+                       optdebug++;
+                       break;
+               case 'c':
+                       optconfig = optarg;
+                       break;
+               case 'h':
+                       snprintf(hostname, sizeof(hostname), "%s", optarg);
+                       break;
+               default:
+                       printf("Args are:\n"
+                              "\t-d\t\tDetach from terminal\n"
+                              "\t-c <file>\tConfig file\n"
+                              "\t-h <hostname>\tForce hostname\n"
+                              "\t-v\t\tDebug\n");
+
+                       return (0);
+                       break;
                }
        }
 
                }
        }
 
@@ -3012,7 +3020,7 @@ int main(int argc, char *argv[])
        }
 
        /* Set up the cluster communications port. */
        }
 
        /* Set up the cluster communications port. */
-       if (cluster_init(config->bind_address) < 0)
+       if (cluster_init() < 0)
                exit(1);
 
 #ifdef BGP
                exit(1);
 
 #ifdef BGP
@@ -3081,7 +3089,7 @@ int main(int argc, char *argv[])
        return 0;
 }
 
        return 0;
 }
 
-void sighup_handler(int junk)
+static void sighup_handler(int sig)
 {
        if (log_stream && log_stream != stderr)
        {
 {
        if (log_stream && log_stream != stderr)
        {
@@ -3092,7 +3100,7 @@ void sighup_handler(int junk)
        read_config_file();
 }
 
        read_config_file();
 }
 
-void sigalrm_handler(int junk)
+static void sigalrm_handler(int sig)
 {
        // Log current traffic stats
 
 {
        // Log current traffic stats
 
@@ -3125,7 +3133,7 @@ void sigalrm_handler(int junk)
 
 }
 
 
 }
 
-void sigterm_handler(int junk)
+static void sigterm_handler(int sig)
 {
        LOG(1, 0, 0, 0, "Shutting down cleanly\n");
        if (config->save_state)
 {
        LOG(1, 0, 0, 0, "Shutting down cleanly\n");
        if (config->save_state)
@@ -3134,7 +3142,7 @@ void sigterm_handler(int junk)
        main_quit++;
 }
 
        main_quit++;
 }
 
-void sigquit_handler(int junk)
+static void sigquit_handler(int sig)
 {
        int i;
 
 {
        int i;
 
@@ -3153,13 +3161,13 @@ void sigquit_handler(int junk)
        main_quit++;
 }
 
        main_quit++;
 }
 
-void sigchild_handler(int signal)
+static void sigchild_handler(int sig)
 {
        while (waitpid(-1, NULL, WNOHANG) > 0)
            ;
 }
 
 {
        while (waitpid(-1, NULL, WNOHANG) > 0)
            ;
 }
 
-void read_state()
+static void read_state()
 {
        struct stat sb;
        int i;
 {
        struct stat sb;
        int i;
@@ -3284,7 +3292,7 @@ void read_state()
        LOG(0, 0, 0, 0, "Loaded saved state information\n");
 }
 
        LOG(0, 0, 0, 0, "Loaded saved state information\n");
 }
 
-void dump_state()
+static void dump_state()
 {
        FILE *f;
        u32 buf[2];
 {
        FILE *f;
        u32 buf[2];
@@ -3335,7 +3343,7 @@ void dump_state()
        unlink(STATEFILE);
 }
 
        unlink(STATEFILE);
 }
 
-void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **challenge_response)
+static void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **challenge_response)
 {
        MD5_CTX ctx;
        *challenge_response = NULL;
 {
        MD5_CTX ctx;
        *challenge_response = NULL;
@@ -3370,7 +3378,7 @@ static int facility_value(char *name)
        return 0;
 }
 
        return 0;
 }
 
-void update_config()
+static void update_config()
 {
        int i;
        static int timeout = 0;
 {
        int i;
        static int timeout = 0;
@@ -3511,7 +3519,7 @@ void update_config()
        config->reload_config = 0;
 }
 
        config->reload_config = 0;
 }
 
-void read_config_file()
+static void read_config_file()
 {
        FILE *f;
 
 {
        FILE *f;
 
@@ -3573,18 +3581,30 @@ int sessionsetup(tunnelidt t, sessionidt s)
                }
        }
 
                }
        }
 
+       {
+               int routed = 0;
+
                // Add the route for this session.
                // Add the route for this session.
-               //
-               // Static IPs need to be routed. Anything else
-               // is part of the IP address pool and is already routed,
-               // it just needs to be added to the IP cache.
-       if (session[s].ip_pool_index == -1) // static ip
-               routeset(s, session[s].ip, 0, 0, 1);
-       else
-               cache_ipmap(session[s].ip, s);
+               for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++)
+               {
+                       if ((session[s].ip & session[s].route[r].mask) ==
+                           (session[s].route[r].ip & session[s].route[r].mask))
+                               routed++;
 
 
-       for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++)
-               routeset(s, session[s].route[r].ip, session[s].route[r].mask, session[s].ip, 1);
+                       routeset(s, session[s].route[r].ip, session[s].route[r].mask, 0, 1);
+               }
+
+               // Static IPs need to be routed if not already
+               // convered by a Framed-Route.  Anything else is part
+               // of the IP address pool and is already routed, it
+               // just needs to be added to the IP cache.
+               if (session[s].ip_pool_index == -1) // static ip
+               {
+                       if (!routed) routeset(s, session[s].ip, 0, 0, 1);
+               }
+               else
+                       cache_ipmap(session[s].ip, s);
+       }
 
        if (!session[s].unique_id)
        {
 
        if (!session[s].unique_id)
        {
@@ -3628,6 +3648,7 @@ int sessionsetup(tunnelidt t, sessionidt s)
 int load_session(sessionidt s, sessiont *new)
 {
        int i;
 int load_session(sessionidt s, sessiont *new)
 {
        int i;
+       int newip = 0;
 
                // Sanity checks.
        if (new->ip_pool_index >= MAXIPPOOL ||
 
                // Sanity checks.
        if (new->ip_pool_index >= MAXIPPOOL ||
@@ -3645,48 +3666,66 @@ int load_session(sessionidt s, sessiont *new)
 
        session[s].tunnel = new->tunnel; // For logging in cache_ipmap
 
 
        session[s].tunnel = new->tunnel; // For logging in cache_ipmap
 
+       // See if routes/ip cache need updating
+       if (new->ip != session[s].ip)
+               newip++;
+
+       for (i = 0; !newip && i < MAXROUTE && (session[s].route[i].ip || new->route[i].ip); i++)
+               if (new->route[i].ip != session[s].route[i].ip ||
+                   new->route[i].mask != session[s].route[i].mask)
+                       newip++;
 
 
-       if (new->ip != session[s].ip)   // Changed ip. fix up hash tables.
+       // needs update
+       if (newip)
        {
        {
-               if (session[s].ip)      // If there's an old one, remove it.
+               int routed = 0;
+
+               // remove old routes...
+               for (i = 0; i < MAXROUTE && session[s].route[i].ip; i++)
                {
                {
-                       // Remove any routes if the IP has changed
-                       for (i = 0; i < MAXROUTE && session[s].route[i].ip; i++)
-                       {
-                               routeset(s, session[s].route[i].ip, session[s].route[i].mask, session[s].ip, 0);
-                               session[s].route[i].ip = 0;
-                       }
+                       if ((session[s].ip & session[s].route[i].mask) ==
+                           (session[s].route[i].ip & session[s].route[i].mask))
+                               routed++;
 
 
+                       routeset(s, session[s].route[i].ip, session[s].route[i].mask, 0, 0);
+               }
+
+               // ...ip
+               if (session[s].ip)
+               {
                        if (session[s].ip_pool_index == -1) // static IP
                        if (session[s].ip_pool_index == -1) // static IP
-                               routeset(s, session[s].ip, 0, 0, 0);
+                       {
+                               if (!routed) routeset(s, session[s].ip, 0, 0, 0);
+                       }
                        else            // It's part of the IP pool, remove it manually.
                                uncache_ipmap(session[s].ip);
                }
 
                        else            // It's part of the IP pool, remove it manually.
                                uncache_ipmap(session[s].ip);
                }
 
+               routed = 0;
+
+               // add new routes...
+               for (i = 0; i < MAXROUTE && new->route[i].ip; i++)
+               {
+                       if ((new->ip & new->route[i].mask) ==
+                           (new->route[i].ip & new->route[i].mask))
+                               routed++;
+
+                       routeset(s, new->route[i].ip, new->route[i].mask, 0, 1);
+               }
+
+               // ...ip
                if (new->ip)
                {
                        // If there's a new one, add it.
                        if (new->ip_pool_index == -1)
                if (new->ip)
                {
                        // If there's a new one, add it.
                        if (new->ip_pool_index == -1)
-                               routeset(s, new->ip, 0, 0, 1);
+                       {
+                               if (!routed) routeset(s, new->ip, 0, 0, 1);
+                       }
                        else
                                cache_ipmap(new->ip, s);
                }
        }
 
                        else
                                cache_ipmap(new->ip, s);
                }
        }
 
-       // Update routed networks
-       for (i = 0; i < MAXROUTE && (session[s].route[i].ip || new->route[i].ip); i++)
-       {
-               if (new->route[i].ip == session[s].route[i].ip &&
-                   new->route[i].mask == session[s].route[i].mask)
-                       continue;
-
-               if (session[s].route[i].ip) // Remove the old one if it exists.
-                       routeset(s, session[s].route[i].ip, session[s].route[i].mask, session[s].ip, 0);
-
-               if (new->route[i].ip)   // Add the new one if it exists.
-                       routeset(s, new->route[i].ip, new->route[i].mask, new->ip, 1);
-       }
-
        if (new->tunnel && s > config->cluster_highest_sessionid)       // Maintain this in the slave. It's used
                                        // for walking the sessions to forward byte counts to the master.
                config->cluster_highest_sessionid = s;
        if (new->tunnel && s > config->cluster_highest_sessionid)       // Maintain this in the slave. It's used
                                        // for walking the sessions to forward byte counts to the master.
                config->cluster_highest_sessionid = s;
@@ -3706,22 +3745,7 @@ int load_session(sessionidt s, sessiont *new)
        return 1;
 }
 
        return 1;
 }
 
-#ifdef RINGBUFFER
-void ringbuffer_dump(FILE *stream)
-{
-       int i = ringbuffer->head;
-
-       while (i != ringbuffer->tail)
-       {
-               if (*ringbuffer->buffer[i].message)
-                       fprintf(stream, "%d-%s", ringbuffer->buffer[i].level, ringbuffer->buffer[i].message);
-               if (++i == ringbuffer->tail) break;
-               if (i == RINGBUFFER_SIZE) i = 0;
-       }
-}
-#endif
-
-void initplugins()
+static void initplugins()
 {
        int i;
 
 {
        int i;
 
@@ -3763,7 +3787,7 @@ static void *getconfig(char *key, enum config_typet type)
        return 0;
 }
 
        return 0;
 }
 
-void add_plugin(char *plugin_name)
+static int add_plugin(char *plugin_name)
 {
        static struct pluginfuncs funcs = {
                _log,
 {
        static struct pluginfuncs funcs = {
                _log,
@@ -3772,10 +3796,12 @@ void add_plugin(char *plugin_name)
                sessionbyuser,
                sessiontbysessionidt,
                sessionidtbysessiont,
                sessionbyuser,
                sessiontbysessionidt,
                sessionidtbysessiont,
-               sessionkill,
                radiusnew,
                radiussend,
                getconfig,
                radiusnew,
                radiussend,
                getconfig,
+               sessionkill,
+               throttle_session,
+               cluster_send_session,
        };
 
        void *p = open_plugin(plugin_name, 1);
        };
 
        void *p = open_plugin(plugin_name, 1);
@@ -3785,22 +3811,22 @@ void add_plugin(char *plugin_name)
        if (!p)
        {
                LOG(1, 0, 0, 0, "   Plugin load failed: %s\n", dlerror());
        if (!p)
        {
                LOG(1, 0, 0, 0, "   Plugin load failed: %s\n", dlerror());
-               return;
+               return -1;
        }
 
        if (ll_contains(loaded_plugins, p))
        {
                dlclose(p);
        }
 
        if (ll_contains(loaded_plugins, p))
        {
                dlclose(p);
-               return;
+               return 0; // already loaded
        }
 
        {
        }
 
        {
-               int *v = dlsym(p, "__plugin_api_version");
+               int *v = dlsym(p, "plugin_api_version");
                if (!v || *v != PLUGIN_API_VERSION)
                {
                        LOG(1, 0, 0, 0, "   Plugin load failed: API version mismatch: %s\n", dlerror());
                        dlclose(p);
                if (!v || *v != PLUGIN_API_VERSION)
                {
                        LOG(1, 0, 0, 0, "   Plugin load failed: API version mismatch: %s\n", dlerror());
                        dlclose(p);
-                       return;
+                       return -1;
                }
        }
 
                }
        }
 
@@ -3810,7 +3836,7 @@ void add_plugin(char *plugin_name)
                {
                        LOG(1, 0, 0, 0, "   Plugin load failed: plugin_init() returned FALSE: %s\n", dlerror());
                        dlclose(p);
                {
                        LOG(1, 0, 0, 0, "   Plugin load failed: plugin_init() returned FALSE: %s\n", dlerror());
                        dlclose(p);
-                       return;
+                       return -1;
                }
        }
 
                }
        }
 
@@ -3827,6 +3853,7 @@ void add_plugin(char *plugin_name)
        }
 
        LOG(2, 0, 0, 0, "   Loaded plugin %s\n", plugin_name);
        }
 
        LOG(2, 0, 0, 0, "   Loaded plugin %s\n", plugin_name);
+       return 1;
 }
 
 static void run_plugin_done(void *plugin)
 }
 
 static void run_plugin_done(void *plugin)
@@ -3837,48 +3864,54 @@ static void run_plugin_done(void *plugin)
                donefunc();
 }
 
                donefunc();
 }
 
-void remove_plugin(char *plugin_name)
+static int remove_plugin(char *plugin_name)
 {
        void *p = open_plugin(plugin_name, 0);
 {
        void *p = open_plugin(plugin_name, 0);
-       int i;
+       int loaded = 0;
 
        if (!p)
 
        if (!p)
-               return;
-
-       for (i = 0; i < max_plugin_functions; i++)
-       {
-               void *x;
-               if (plugin_functions[i] && (x = dlsym(p, plugin_functions[i])))
-                       ll_delete(plugins[i], x);
-       }
+               return -1;
 
        if (ll_contains(loaded_plugins, p))
        {
 
        if (ll_contains(loaded_plugins, p))
        {
+               int i;
+               for (i = 0; i < max_plugin_functions; i++)
+               {
+                       void *x;
+                       if (plugin_functions[i] && (x = dlsym(p, plugin_functions[i])))
+                               ll_delete(plugins[i], x);
+               }
+
                ll_delete(loaded_plugins, p);
                run_plugin_done(p);
                ll_delete(loaded_plugins, p);
                run_plugin_done(p);
+               loaded = 1;
        }
 
        dlclose(p);
        LOG(2, 0, 0, 0, "Removed plugin %s\n", plugin_name);
        }
 
        dlclose(p);
        LOG(2, 0, 0, 0, "Removed plugin %s\n", plugin_name);
+       return loaded;
 }
 
 int run_plugins(int plugin_type, void *data)
 {
        int (*func)(void *data);
 }
 
 int run_plugins(int plugin_type, void *data)
 {
        int (*func)(void *data);
-       if (!plugins[plugin_type] || plugin_type > max_plugin_functions) return 1;
+
+       if (!plugins[plugin_type] || plugin_type > max_plugin_functions)
+               return PLUGIN_RET_ERROR;
 
        ll_reset(plugins[plugin_type]);
        while ((func = ll_next(plugins[plugin_type])))
        {
 
        ll_reset(plugins[plugin_type]);
        while ((func = ll_next(plugins[plugin_type])))
        {
-               int rc;
-               rc = func(data);
-               if (rc == PLUGIN_RET_STOP) return 1;
-               if (rc == PLUGIN_RET_ERROR) return 0;
+               int r = func(data);
+
+               if (r != PLUGIN_RET_OK)
+                       return r; // stop here
        }
        }
-       return 1;
+
+       return PLUGIN_RET_OK;
 }
 
 }
 
-void plugins_done()
+static void plugins_done()
 {
        void *p;
 
 {
        void *p;
 
@@ -3887,49 +3920,177 @@ void plugins_done()
                run_plugin_done(p);
 }
 
                run_plugin_done(p);
 }
 
-void processcontrol(u8 * buf, int len, struct sockaddr_in *addr)
+static void processcontrol(u8 * buf, int len, struct sockaddr_in *addr, int alen)
 {
 {
-       char *resp;
-       int l;
-       struct param_control param = { buf, len, ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port), NULL, 0, 0 };
-
+       struct nsctl request;
+       struct nsctl response;
+       int type = unpack_control(&request, buf, len);
+       int r;
+       void *p;
 
        if (log_stream && config->debug >= 4)
        {
 
        if (log_stream && config->debug >= 4)
        {
-               LOG(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Received ");
-               dump_packet(buf, log_stream);
+               if (type < 0)
+               {
+                       LOG(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Bogus control message (%d)\n", type);
+               }
+               else
+               {
+                       LOG(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Received ");
+                       dump_control(&request, log_stream);
+               }
        }
 
        }
 
-       resp = calloc(1400, 1);
-       l = new_packet(PKT_RESP_ERROR, resp);
-       *(int *)(resp + 6) = *(int *)(buf + 6);
+       switch (type)
+       {
+       case NSCTL_REQ_LOAD:
+               if (request.argc != 1)
+               {
+                       response.type = NSCTL_RES_ERR;
+                       response.argc = 1;
+                       response.argv[0] = "name of plugin required";
+               }
+               else if ((r = add_plugin(request.argv[0])) < 1)
+               {
+                       response.type = NSCTL_RES_ERR;
+                       response.argc = 1;
+                       response.argv[0] = !r
+                               ? "plugin already loaded"
+                               : "error loading plugin";
+               }
+               else
+               {
+                       response.type = NSCTL_RES_OK;
+                       response.argc = 0;
+               }
+
+               break;
 
 
-       param.type = ntohs(*(short *)(buf + 2));
-       param.id = ntohl(*(int *)(buf + 6));
-       param.data_length = ntohs(*(short *)(buf + 4)) - 10;
-       param.data = (param.data_length > 0) ? (char *)(buf + 10) : NULL;
-       param.response = resp;
-       param.response_length = l;
+       case NSCTL_REQ_UNLOAD:
+               if (request.argc != 1)
+               {
+                       response.type = NSCTL_RES_ERR;
+                       response.argc = 1;
+                       response.argv[0] = "name of plugin required";
+               }
+               else if ((r = remove_plugin(request.argv[0])) < 1)
+               {
+                       response.type = NSCTL_RES_ERR;
+                       response.argc = 1;
+                       response.argv[0] = !r
+                               ? "plugin not loaded"
+                               : "plugin not found";
+               }
+               else
+               {
+                       response.type = NSCTL_RES_OK;
+                       response.argc = 0;
+               }
 
 
-       run_plugins(PLUGIN_CONTROL, &param);
+               break;
 
 
-       if (param.send_response)
+       case NSCTL_REQ_HELP:
+               response.type = NSCTL_RES_OK;
+               response.argc = 0;
+
+               ll_reset(loaded_plugins);
+               while ((p = ll_next(loaded_plugins)))
+               {
+                       char **help = dlsym(p, "plugin_control_help");
+                       while (response.argc < 0xff && help && *help)
+                               response.argv[response.argc++] = *help++;
+               }
+
+               break;
+
+       case NSCTL_REQ_CONTROL:
+               {
+                       struct param_control param = {
+                               config->cluster_iam_master,
+                               request.argc,
+                               request.argv,
+                               0,
+                               NULL,
+                       };
+
+                       int r = run_plugins(PLUGIN_CONTROL, &param);
+
+                       if (r == PLUGIN_RET_ERROR)
+                       {
+                               response.type = NSCTL_RES_ERR;
+                               response.argc = 1;
+                               response.argv[0] = param.additional
+                                       ? param.additional
+                                       : "error returned by plugin";
+                       }
+                       else if (r == PLUGIN_RET_NOTMASTER)
+                       {
+                               static char msg[] = "must be run on master: 000.000.000.000";
+
+                               response.type = NSCTL_RES_ERR;
+                               response.argc = 1;
+                               if (config->cluster_master_address)
+                               {
+                                       strcpy(msg + 23, inet_toa(config->cluster_master_address));
+                                       response.argv[0] = msg;
+                               }
+                               else
+                               {
+                                       response.argv[0] = "must be run on master: none elected";
+                               }
+                       }
+                       else if (!(param.response & NSCTL_RESPONSE))
+                       {
+                               response.type = NSCTL_RES_ERR;
+                               response.argc = 1;
+                               response.argv[0] = param.response
+                                       ? "unrecognised response value from plugin"
+                                       : "unhandled action";
+                       }
+                       else
+                       {
+                               response.type = param.response;
+                               response.argc = 0;
+                               if (param.additional)
+                               {
+                                       response.argc = 1;
+                                       response.argv[0] = param.additional;
+                               }
+                       }
+               }
+
+               break;
+
+       default:
+               response.type = NSCTL_RES_ERR;
+               response.argc = 1;
+               response.argv[0] = "error unpacking control packet";
+       }
+
+       buf = calloc(NSCTL_MAX_PKT_SZ, 1);
+       if (!buf)
        {
        {
-               send_packet(controlfd, ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port), param.response, param.response_length);
-               LOG(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Sent Control packet response\n");
+               LOG(2, ntohl(addr->sin_addr.s_addr), 0, 0, "Failed to allocate nsctl response\n");
+               return;
        }
 
        }
 
-       free(resp);
-}
+       r = pack_control(buf, NSCTL_MAX_PKT_SZ, response.type, response.argc, response.argv);
+       if (r > 0)
+       {
+               sendto(controlfd, buf, r, 0, (const struct sockaddr *) addr, alen);
+               if (log_stream && config->debug >= 4)
+               {
+                       LOG(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Sent ");
+                       dump_control(&response, log_stream);
+               }
+       }
+       else
+               LOG(2, ntohl(addr->sin_addr.s_addr), 0, 0, "Failed to pack nsctl response (%d)\n", r);
 
 
-void tunnelclear(tunnelidt t)
-{
-       if (!t) return;
-       memset(&tunnel[t], 0, sizeof(tunnel[t]));
-       tunnel[t].state = TUNNELFREE;
+       free(buf);
 }
 
 }
 
-tunnelidt new_tunnel()
+static tunnelidt new_tunnel()
 {
        tunnelidt i;
        for (i = 1; i < MAXTUNNEL; i++)
 {
        tunnelidt i;
        for (i = 1; i < MAXTUNNEL; i++)
@@ -3976,7 +4137,6 @@ void become_master(void)
        // add radius fds
        for (i = 0; i < config->num_radfds; i++)
        {
        // add radius fds
        for (i = 0; i < config->num_radfds; i++)
        {
-               if (!radfds[i]) continue;
                FD_SET(radfds[i], &readset);
                if (radfds[i] > readset_n)
                        readset_n = radfds[i];
                FD_SET(radfds[i], &readset);
                if (radfds[i] > readset_n)
                        readset_n = radfds[i];
@@ -4070,7 +4230,7 @@ int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc
  *
  * Based on code from rp-l2tpd by Roaring Penguin Software Inc.
  */
  *
  * Based on code from rp-l2tpd by Roaring Penguin Software Inc.
  */
-int unhide_avp(u8 *avp, tunnelidt t, sessionidt s, u16 length)
+static int unhide_avp(u8 *avp, tunnelidt t, sessionidt s, u16 length)
 {
        MD5_CTX ctx;
        u8 *cursor;
 {
        MD5_CTX ctx;
        u8 *cursor;