- Add startup-config(5) manpage [FIXME].
[l2tpns.git] / l2tpns.c
index 5171caf..4bdf862 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
 
-char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.47 2004/11/09 08:05:02 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.51 2004/11/17 08:23:34 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -49,46 +49,44 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.47 2004/11/09 08:05:02 bodea Exp
 #include "util.h"
 #include "tbf.h"
 
+#ifdef BGP
+#include "bgp.h"
+#endif /* BGP */
+
 // Globals
 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 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 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
-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.
 
-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];
 
-#ifdef BGP
-#include "bgp.h"
-struct bgp_peer *bgp_peers = 0;
-struct bgp_route_list *bgp_routes = 0;
-int bgp_configured = 0;
-#endif /* BGP */
-
 #define membersize(STRUCT, MEMBER) sizeof(((STRUCT *)0)->MEMBER)
 #define CONFIG(NAME, MEMBER, TYPE) { NAME, offsetof(struct configt, MEMBER), membersize(struct configt, MEMBER), TYPE }
 
@@ -123,17 +121,10 @@ struct config_descriptt config_values[] = {
        CONFIG("cluster_interface", cluster_interface, STRING),
        CONFIG("cluster_hb_interval", cluster_hb_interval, INT),
        CONFIG("cluster_hb_timeout", cluster_hb_timeout, INT),
-#ifdef BGP
-       CONFIG("as_number", as_number, SHORT),
-       CONFIG("bgp_peer1", bgp_peer[0], STRING),
-       CONFIG("bgp_peer1_as", bgp_peer_as[0], SHORT),
-       CONFIG("bgp_peer2", bgp_peer[1], STRING),
-       CONFIG("bgp_peer2_as", bgp_peer_as[1], SHORT),
-#endif /* BGP */
        { NULL, 0, 0, 0 },
 };
 
-char *plugin_functions[] = {
+static char *plugin_functions[] = {
        NULL,
        "plugin_pre_auth",
        "plugin_post_auth",
@@ -155,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.
-controlt *controlfree = 0;
+static controlt *controlfree = 0;
 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 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)
-clockt now(void)
+static clockt now(void)
 {
        struct timeval t;
        gettimeofday(&t, 0);
@@ -296,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
-// 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.
 //
-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;
@@ -341,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.
-               // 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.
@@ -360,7 +357,7 @@ void routeset(sessionidt s, ipt ip, ipt mask, ipt gw, u8 add)
 
 //
 // Set up TUN interface
-void inittun(void)
+static void inittun(void)
 {
        struct ifreq ifr;
        struct sockaddr_in sin = {0};
@@ -408,16 +405,10 @@ void inittun(void)
                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
-void initudp(void)
+static void initudp(void)
 {
        int on = 1;
        struct sockaddr_in addr;
@@ -443,8 +434,8 @@ void initudp(void)
        // 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)
        {
@@ -468,7 +459,7 @@ void initudp(void)
 // IP address.
 //
 
-int lookup_ipmap(ipt ip)
+static int lookup_ipmap(ipt ip)
 {
        u8 *a = (u8 *)&ip;
        char **d = (char **) ip_hash;
@@ -629,13 +620,13 @@ void send_garp(ipt ip)
 }
 
 // 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];
 }
 
-sessionidt sessionidtbysessiont(sessiont *s)
+static sessionidt sessionidtbysessiont(sessiont *s)
 {
        sessionidt val = s-session;
        if (s < session || val > MAXSESSION) return 0;
@@ -710,7 +701,7 @@ int tun_write(u8 * data, int size)
 
 // process outgoing (to tunnel) IP
 //
-void processipout(u8 * buf, int len)
+static void processipout(u8 * buf, int len)
 {
        sessionidt s;
        sessiont *sp;
@@ -815,7 +806,7 @@ void processipout(u8 * buf, int len)
 // 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;
@@ -866,7 +857,7 @@ void send_ipout(sessionidt s, u8 *buf, int len)
 }
 
 // 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);
@@ -877,7 +868,7 @@ void control16(controlt * c, u16 avp, u16 val, u8 m)
 }
 
 // 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);
@@ -888,7 +879,7 @@ void control32(controlt * c, u16 avp, u32 val, u8 m)
 }
 
 // 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);
@@ -899,7 +890,7 @@ void controls(controlt * c, u16 avp, char *val, u8 m)
 }
 
 // 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);
@@ -910,7 +901,7 @@ void controlb(controlt * c, u16 avp, char *val, unsigned int len, u8 m)
 }
 
 // new control connection
-controlt *controlnew(u16 mtype)
+static controlt *controlnew(u16 mtype)
 {
        controlt *c;
        if (!controlfree)
@@ -930,7 +921,7 @@ controlt *controlnew(u16 mtype)
 
 // 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.
@@ -946,7 +937,7 @@ void controlnull(tunnelidt t)
 }
 
 // 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
@@ -1018,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)
 {
-       int dead = session[s].die;
        int walled_garden = session[s].walled_garden;
 
 
@@ -1030,18 +1020,15 @@ void sessionshutdown(sessionidt s, char *reason)
                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] };
+               LOG(2, 0, s, session[s].tunnel, "Shutting down session %d: %s\n", s, reason);
                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)
@@ -1067,7 +1054,7 @@ void sessionshutdown(sessionidt s, char *reason)
                int 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);
+                       routeset(s, session[s].route[r].ip, session[s].route[r].mask, 0, 0);
                        session[s].route[r].ip = 0;
                }
 
@@ -1090,6 +1077,9 @@ void sessionshutdown(sessionidt s, char *reason)
                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);
 }
 
@@ -1139,7 +1129,7 @@ void sendipcp(tunnelidt t, sessionidt s)
 }
 
 // kill a session now
-void sessionkill(sessionidt s, char *reason)
+static void sessionkill(sessionidt s, char *reason)
 {
 
        CSTAT(call_sessionkill);
@@ -1159,8 +1149,15 @@ void sessionkill(sessionidt s, char *reason)
        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
-void tunnelkill(tunnelidt t, char *reason)
+static void tunnelkill(tunnelidt t, char *reason)
 {
        sessionidt s;
        controlt *c;
@@ -1191,7 +1188,7 @@ void tunnelkill(tunnelidt t, char *reason)
 }
 
 // shut down a tunnel cleanly
-void tunnelshutdown(tunnelidt t, char *reason)
+static void tunnelshutdown(tunnelidt t, char *reason)
 {
        sessionidt s;
 
@@ -1208,7 +1205,7 @@ void tunnelshutdown(tunnelidt t, char *reason)
        // 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
@@ -1931,7 +1928,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
 }
 
 // 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);
@@ -1959,7 +1956,7 @@ void processtun(u8 * buf, int len)
 // 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;
@@ -2159,7 +2156,7 @@ int regular_cleanups(void)
 // 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;
@@ -2209,7 +2206,7 @@ static fd_set readset;
 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];
@@ -2327,7 +2324,7 @@ void mainloop(void)
                        }
 
                        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))
                        {
@@ -2484,7 +2481,7 @@ static void stripdomain(char *host)
 }
 
 // Init data structures
-void initdata(int optdebug, char *optconfig)
+static void initdata(int optdebug, char *optconfig)
 {
        int i;
 
@@ -2585,23 +2582,15 @@ void initdata(int optdebug, char *optconfig)
        _statistics->start_time = _statistics->last_reset = time(NULL);
 
 #ifdef BGP
-       if (!(bgp_peers = shared_malloc(sizeof(struct bgp_peer) * BGP_NUM_PEERS)))
-       {
-               LOG(0, 0, 0, 0, "Error doing malloc for bgp: %s\n", strerror(errno));
-               exit(1);
-       }
+       if (!(bgp_peers = shared_malloc(sizeof(struct bgp_peer) * BGP_NUM_PEERS)))
+       {
+               LOG(0, 0, 0, 0, "Error doing malloc for bgp: %s\n", strerror(errno));
+               exit(1);
+       }
 #endif /* BGP */
 }
 
-void initiptables(void)
-{
-       /* Flush the tables here so that we have a clean slate */
-
-// Not needed. 'nat' is setup by garden.c
-// mangle isn't used (as throttling is done by tbf inhouse).
-}
-
-int assign_ip_address(sessionidt s)
+static int assign_ip_address(sessionidt s)
 {
        u32 i;
        int best = -1;
@@ -2658,10 +2647,13 @@ int assign_ip_address(sessionidt s)
        return 1;
 }
 
-void free_ip_address(sessionidt s)
+static void free_ip_address(sessionidt s)
 {
        int i = session[s].ip_pool_index;
 
+
+       CSTAT(call_free_ip_address);
+
        if (!session[s].ip)
                return; // what the?
 
@@ -2674,10 +2666,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;
-
-
-       CSTAT(call_free_ip_address);
-
 }
 
 //
@@ -2744,7 +2732,7 @@ void rebuild_address_pool(void)
 //
 // 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;
 
@@ -2765,7 +2753,7 @@ void fix_address_pool(int sid)
 //
 // 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)
@@ -2793,7 +2781,7 @@ void add_to_ip_pool(u32 addr, u32 mask)
 }
 
 // Initialize the IP address pool
-void initippool()
+static void initippool()
 {
        FILE *f;
        char *p;
@@ -2879,7 +2867,7 @@ void snoop_send_packet(char *packet, u16 size, ipt destination, u16 port)
        STAT(packets_snooped);
 }
 
-void dump_acct_info()
+static void dump_acct_info()
 {
        char filename[1024];
        char timestr[64];
@@ -2935,45 +2923,42 @@ void dump_acct_info()
 // Main program
 int main(int argc, char *argv[])
 {
-       int o;
+       int i;
        int optdebug = 0;
        char *optconfig = CONFIGFILE;
 
-       _program_name = strdup(argv[0]);
-
        time(&basetime);             // start clock
 
        // scan args
-       while ((o = getopt(argc, argv, "dvc:h:")) >= 0)
+       while ((i = getopt(argc, argv, "dvc:h:")) >= 0)
        {
-               switch (o)
+               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;
                }
        }
 
@@ -2983,7 +2968,6 @@ int main(int argc, char *argv[])
        signal(SIGALRM, sigalrm_handler);
        siginterrupt(SIGALRM, 0);
 
-       initiptables();
        initplugins();
        initdata(optdebug, optconfig);
 
@@ -3031,20 +3015,20 @@ int main(int argc, char *argv[])
        }
 
        /* Set up the cluster communications port. */
-       if (cluster_init(config->bind_address) < 0)
+       if (cluster_init() < 0)
                exit(1);
 
 #ifdef BGP
        signal(SIGPIPE, SIG_IGN);
        bgp_setup(config->as_number);
        bgp_add_route(config->bind_address, 0xffffffff);
-       if (*config->bgp_peer[0])
-               bgp_start(&bgp_peers[0], config->bgp_peer[0],
-                   config->bgp_peer_as[0], 0); /* 0 = routing disabled */
-
-       if (*config->bgp_peer[1])
-               bgp_start(&bgp_peers[1], config->bgp_peer[1],
-                   config->bgp_peer_as[1], 0);
+       for (i = 0; i < BGP_NUM_PEERS; i++)
+       {
+               if (config->neighbour[i].name[0])
+                       bgp_start(&bgp_peers[i], config->neighbour[i].name,
+                               config->neighbour[i].as, config->neighbour[i].keepalive,
+                               config->neighbour[i].hold, 0); /* 0 = routing disabled */
+       }
 #endif /* BGP */
 
        inittun();
@@ -3082,12 +3066,9 @@ int main(int argc, char *argv[])
 #ifdef BGP
        /* try to shut BGP down cleanly; with luck the sockets will be
           writable since we're out of the select */
-       {
-               int i;
-               for (i = 0; i < BGP_NUM_PEERS; i++)
-                       if (bgp_peers[i].state == Established)
-                               bgp_stop(&bgp_peers[i]);
-       }
+       for (i = 0; i < BGP_NUM_PEERS; i++)
+               if (bgp_peers[i].state == Established)
+                       bgp_stop(&bgp_peers[i]);
 #endif /* BGP */
 
        /* remove plugins (so cleanup code gets run) */
@@ -3103,7 +3084,7 @@ int main(int argc, char *argv[])
        return 0;
 }
 
-void sighup_handler(int junk)
+static void sighup_handler(int sig)
 {
        if (log_stream && log_stream != stderr)
        {
@@ -3114,7 +3095,7 @@ void sighup_handler(int junk)
        read_config_file();
 }
 
-void sigalrm_handler(int junk)
+static void sigalrm_handler(int sig)
 {
        // Log current traffic stats
 
@@ -3147,7 +3128,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)
@@ -3156,7 +3137,7 @@ void sigterm_handler(int junk)
        main_quit++;
 }
 
-void sigquit_handler(int junk)
+static void sigquit_handler(int sig)
 {
        int i;
 
@@ -3175,13 +3156,13 @@ void sigquit_handler(int junk)
        main_quit++;
 }
 
-void sigchild_handler(int signal)
+static void sigchild_handler(int sig)
 {
        while (waitpid(-1, NULL, WNOHANG) > 0)
            ;
 }
 
-void read_state()
+static void read_state()
 {
        struct stat sb;
        int i;
@@ -3306,7 +3287,7 @@ void read_state()
        LOG(0, 0, 0, 0, "Loaded saved state information\n");
 }
 
-void dump_state()
+static void dump_state()
 {
        FILE *f;
        u32 buf[2];
@@ -3357,7 +3338,7 @@ void dump_state()
        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;
@@ -3392,7 +3373,7 @@ static int facility_value(char *name)
        return 0;
 }
 
-void update_config()
+static void update_config()
 {
        int i;
        static int timeout = 0;
@@ -3533,7 +3514,7 @@ void update_config()
        config->reload_config = 0;
 }
 
-void read_config_file()
+static void read_config_file()
 {
        FILE *f;
 
@@ -3606,7 +3587,7 @@ int sessionsetup(tunnelidt t, sessionidt s)
                cache_ipmap(session[s].ip, s);
 
        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);
 
        if (!session[s].unique_id)
        {
@@ -3675,7 +3656,7 @@ int load_session(sessionidt s, sessiont *new)
                        // 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);
+                               routeset(s, session[s].route[i].ip, session[s].route[i].mask, 0, 0);
                                session[s].route[i].ip = 0;
                        }
 
@@ -3703,10 +3684,10 @@ int load_session(sessionidt s, sessiont *new)
                        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);
+                       routeset(s, session[s].route[i].ip, session[s].route[i].mask, 0, 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);
+                       routeset(s, new->route[i].ip, new->route[i].mask, 0, 1);
        }
 
        if (new->tunnel && s > config->cluster_highest_sessionid)       // Maintain this in the slave. It's used
@@ -3728,22 +3709,7 @@ int load_session(sessionidt s, sessiont *new)
        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;
 
@@ -3785,7 +3751,7 @@ static void *getconfig(char *key, enum config_typet type)
        return 0;
 }
 
-void add_plugin(char *plugin_name)
+static int add_plugin(char *plugin_name)
 {
        static struct pluginfuncs funcs = {
                _log,
@@ -3807,22 +3773,22 @@ void add_plugin(char *plugin_name)
        if (!p)
        {
                LOG(1, 0, 0, 0, "   Plugin load failed: %s\n", dlerror());
-               return;
+               return -1;
        }
 
        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);
-                       return;
+                       return -1;
                }
        }
 
@@ -3832,7 +3798,7 @@ void add_plugin(char *plugin_name)
                {
                        LOG(1, 0, 0, 0, "   Plugin load failed: plugin_init() returned FALSE: %s\n", dlerror());
                        dlclose(p);
-                       return;
+                       return -1;
                }
        }
 
@@ -3849,6 +3815,7 @@ void add_plugin(char *plugin_name)
        }
 
        LOG(2, 0, 0, 0, "   Loaded plugin %s\n", plugin_name);
+       return 1;
 }
 
 static void run_plugin_done(void *plugin)
@@ -3859,29 +3826,32 @@ static void run_plugin_done(void *plugin)
                donefunc();
 }
 
-void remove_plugin(char *plugin_name)
+static int remove_plugin(char *plugin_name)
 {
        void *p = open_plugin(plugin_name, 0);
-       int i;
+       int loaded = 0;
 
        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))
        {
+               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);
+               loaded = 1;
        }
 
        dlclose(p);
        LOG(2, 0, 0, 0, "Removed plugin %s\n", plugin_name);
+       return loaded;
 }
 
 int run_plugins(int plugin_type, void *data)
@@ -3900,7 +3870,7 @@ int run_plugins(int plugin_type, void *data)
        return 1;
 }
 
-void plugins_done()
+static void plugins_done()
 {
        void *p;
 
@@ -3909,70 +3879,152 @@ void plugins_done()
                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)
        {
-               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;
+               }
 
-       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;
+               break;
 
-       run_plugins(PLUGIN_CONTROL, &param);
+       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;
+               }
 
-       if (param.send_response)
-       {
-               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");
-       }
+               break;
 
-       free(resp);
-}
+       case NSCTL_REQ_HELP:
+               response.type = NSCTL_RES_OK;
+               response.argc = 0;
 
-/*
- * HACK
- * Go through all of the tunnels and do some cleanups
- */
-void tunnel_clean()
-{
-       int i;
+               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++;
+               }
 
-       LOG(1, 0, 0, 0, "Cleaning tunnels array\n");
+               break;
 
-       for (i = 1; i < MAXTUNNEL; i++)
+       case NSCTL_REQ_CONTROL:
+               {
+                       struct param_control param = { request.argc, request.argv, 0, NULL };
+                       if (!run_plugins(PLUGIN_CONTROL, &param))
+                       {
+                               response.type = NSCTL_RES_ERR;
+                               response.argc = 1;
+                               response.argv[0] = param.additional
+                                       ? param.additional
+                                       : "error returned by plugin";
+                       }
+                       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)
        {
-               if (!tunnel[i].ip
-                               || !*tunnel[i].hostname
-                               || (tunnel[i].state == TUNNELDIE && tunnel[i].die >= time_now))
+               LOG(2, ntohl(addr->sin_addr.s_addr), 0, 0, "Failed to allocate nsctl response\n");
+               return;
+       }
+
+       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)
                {
-                       tunnelclear(i);
+                       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++)
@@ -4019,7 +4071,6 @@ void become_master(void)
        // 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];
@@ -4113,7 +4164,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.
  */
-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;