add DAE support (PoD/CoA) from Vladislav Bjelic
[l2tpns.git] / l2tpns.c
index b0b51a2..50fde1d 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.108 2005-06-04 15:42:35 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.113 2005-06-28 14:48:20 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -60,6 +60,7 @@ 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 daefd = -1;                        // Socket listening for DAE 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
@@ -114,6 +115,7 @@ config_descriptt config_values[] = {
        CONFIG("radius_interim", radius_interim, INT),
        CONFIG("radius_secret", radiussecret, STRING),
        CONFIG("radius_authtypes", radius_authtypes_s, STRING),
+       CONFIG("radius_dae_port", radius_dae_port, SHORT),
        CONFIG("allow_duplicate_users", allow_duplicate_users, BOOL),
        CONFIG("bind_address", bind_address, IPv4),
        CONFIG("peer_address", peer_address, IPv4),
@@ -148,6 +150,7 @@ static char *plugin_functions[] = {
        "plugin_kill_session",
        "plugin_control",
        "plugin_radius_response",
+       "plugin_radius_reset",
        "plugin_become_master",
        "plugin_new_session_master",
 };
@@ -163,7 +166,7 @@ sessiont *session = NULL;           // Array of session structures.
 sessionlocalt *sess_local = NULL;      // Array of local per-session counters.
 radiust *radius = NULL;                        // Array of radius structures.
 ippoolt *ip_address_pool = NULL;       // Array of dynamic IP addresses.
-ip_filtert *ip_filters = NULL; // Array of named filters.
+ip_filtert *ip_filters = NULL;         // Array of named filters.
 static controlt *controlfree = 0;
 struct Tstats *_statistics = NULL;
 #ifdef RINGBUFFER
@@ -177,8 +180,7 @@ static void free_ip_address(sessionidt s);
 static void dump_acct_info(int all);
 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 shutdown_handler(int sig);
 static void sigchild_handler(int sig);
 static void build_chap_response(char *challenge, uint8_t id, uint16_t challenge_length, char **challenge_response);
 static void update_config(void);
@@ -194,6 +196,10 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec
 // on slaves, alow BGP to withdraw cleanly before exiting
 #define QUIT_DELAY     5
 
+// quit actions (master)
+#define QUIT_FAILOVER  1 // SIGTERM: exit when all control messages have been acked (for cluster failover)
+#define QUIT_SHUTDOWN  2 // SIGQUIT: shutdown sessions/tunnels, reject new connections
+
 // return internal time (10ths since process startup), set f if given
 static clockt now(double *f)
 {
@@ -323,7 +329,8 @@ static void initrandom(char *source)
                return;
 
        // close previous source, if any
-       if (rand_fd >= 0) close(rand_fd);
+       if (rand_fd >= 0)
+               close(rand_fd);
 
        rand_fd = -1;
 
@@ -340,13 +347,6 @@ static void initrandom(char *source)
                                        path, strerror(errno));
                }
        }
-
-       // no source: seed prng
-       {
-               unsigned seed = time_now ^ getpid();
-               LOG(4, 0, 0, "Seeding the pseudo random generator: %u\n", seed);
-               srand(seed);
-       }
 }
 
 // fill buffer with random data
@@ -367,7 +367,7 @@ void random_data(uint8_t *buf, int len)
                                        strerror(errno));
 
                                // fall back to rand()
-                               initrandom(0);
+                               initrandom(NULL);
                        }
 
                        n = 0;
@@ -581,7 +581,7 @@ static void inittun(void)
        }
 }
 
-// set up UDP port
+// set up UDP ports
 static void initudp(void)
 {
        int on = 1;
@@ -603,7 +603,6 @@ static void initudp(void)
                LOG(0, 0, 0, "Error in UDP bind: %s\n", strerror(errno));
                exit(1);
        }
-       snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 
        // Control
        memset(&addr, 0, sizeof(addr));
@@ -616,6 +615,21 @@ static void initudp(void)
                LOG(0, 0, 0, "Error in control bind: %s\n", strerror(errno));
                exit(1);
        }
+
+       // Dynamic Authorization Extensions to RADIUS
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(config->radius_dae_port);
+       daefd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       setsockopt(daefd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+       if (bind(daefd, (void *) &addr, sizeof(addr)) < 0)
+       {
+               LOG(0, 0, 0, "Error in DAE bind: %s\n", strerror(errno));
+               exit(1);
+       }
+
+       // Intercept
+       snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 }
 
 //
@@ -1418,7 +1432,7 @@ void throttle_session(sessionidt s, int rate_in, int rate_out)
 }
 
 // add/remove filters from session (-1 = no change)
-static void filter_session(sessionidt s, int filter_in, int filter_out)
+void filter_session(sessionidt s, int filter_in, int filter_out)
 {
        if (!session[s].opened)
                return; // No-one home.
@@ -1717,7 +1731,7 @@ static void tunnelshutdown(tunnelidt t, char *reason, int result, int error, cha
        // close session
        for (s = 1; s <= config->cluster_highest_sessionid ; ++s)
                if (session[s].tunnel == t)
-                       sessionshutdown(s, reason, 3, 0);
+                       sessionshutdown(s, reason, 0, 0);
 
        tunnel[t].state = TUNNELDIE;
        tunnel[t].die = TIME + 700; // Clean up in 70 seconds
@@ -2027,8 +2041,6 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                                continue;
                                        }
 
-                                       LOG(4, s, t, "Hidden AVP\n");
-
                                        // Unhide the AVP
                                        unhide_value(b, n, mtype, session[s].random_vector, session[s].random_vector_length);
 
@@ -2049,7 +2061,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        n = orig_len;
                                }
 
-                               LOG(4, s, t, "   AVP %d (%s) len %d\n", mtype, avp_name(mtype), n);
+                               LOG(4, s, t, "   AVP %d (%s) len %d%s%s\n", mtype, avp_name(mtype), n,
+                                       flags & 0x40 ? ", hidden" : "", flags & 0x80 ? ", mandatory" : "");
+
                                switch (mtype)
                                {
                                case 0:     // message type
@@ -2271,6 +2285,8 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                case 36:    // Random Vector
                                        LOG(4, s, t, "   Random Vector received.  Enabled AVP Hiding.\n");
                                        memset(session[s].random_vector, 0, sizeof(session[s].random_vector));
+                                       if (n > sizeof(session[s].random_vector))
+                                               n = sizeof(session[s].random_vector);
                                        memcpy(session[s].random_vector, b, n);
                                        session[s].random_vector_length = n;
                                        break;
@@ -2293,6 +2309,8 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                switch (message)
                                {
                                case 1:       // SCCRQ - Start Control Connection Request
+                                       tunnel[t].state = TUNNELOPENING;
+                                       if (main_quit != QUIT_SHUTDOWN)
                                        {
                                                controlt *c = controlnew(2); // sending SCCRP
                                                control16(c, 2, version, 1); // protocol version
@@ -2302,7 +2320,10 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                                control16(c, 9, t, 1); // assigned tunnel
                                                controladd(c, t, 0); // send the resply
                                        }
-                                       tunnel[t].state = TUNNELOPENING;
+                                       else
+                                       {
+                                               tunnelshutdown(t, "Shutting down", 6, 0, 0);
+                                       }
                                        break;
                                case 2:       // SCCRP
                                        tunnel[t].state = TUNNELOPEN;
@@ -2328,7 +2349,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        // TBA
                                        break;
                                case 10:      // ICRQ
-                                       if (sessionfree)
+                                       if (sessionfree && main_quit != QUIT_SHUTDOWN)
                                        {
                                                uint16_t r;
 
@@ -2370,8 +2391,12 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
 
                                        {
                                                controlt *c = controlnew(14); // CDN
-                                               control16(c, 1, 4, 1); // temporary lack of resources
-                                               controladd(c, session[s].tunnel, asession); // send the message
+                                               if (main_quit == QUIT_SHUTDOWN)
+                                                       control16(c, 1, 2, 7); // try another
+                                               else
+                                                       control16(c, 1, 4, 0); // temporary lack of resources
+
+                                               controladd(c, t, asession); // send the message
                                        }
                                        return;
                                case 11:      // ICRP
@@ -2709,7 +2734,8 @@ static void regular_cleanups(double period)
                        continue;
                }
 
-               if (session[s].ip && !(session[s].flags & SF_IPCP_ACKED))
+               if (session[s].ip && !(session[s].flags & SF_IPCP_ACKED)
+                   && !(sess_local[s].radius && radius[sess_local[s].radius].state == RADIUSIPCP))
                {
                        // IPCP has not completed yet. Resend
                        LOG(3, s, session[s].tunnel, "No ACK for initial IPCP ConfigReq... resending\n");
@@ -2825,7 +2851,8 @@ static void regular_cleanups(double period)
                    && !sess_local[s].radius // RADIUS already in progress
                    && time_now - sess_local[s].last_interim >= config->radius_interim)
                {
-                       if (!(r = radiusnew(s)))
+                       int rad = radiusnew(s);
+                       if (!rad)
                        {
                                LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Interim message\n");
                                STAT(radius_overflow);
@@ -2835,7 +2862,7 @@ static void regular_cleanups(double period)
                        LOG(3, s, session[s].tunnel, "Sending RADIUS Interim for %s (%u)\n",
                                session[s].user, session[s].unique_id);
 
-                       radiussend(r, RADIUSINTERIM);
+                       radiussend(rad, RADIUSINTERIM);
                        sess_local[s].last_interim = time_now;
                        s_actions++;
                }
@@ -2885,6 +2912,22 @@ static int still_busy(void)
                return 0;
        }
 
+       if (main_quit == QUIT_SHUTDOWN)
+       {
+               static int dropped = 0;
+               if (!dropped)
+               {
+                       int i;
+
+                       LOG(1, 0, 0, "Dropping sessions and tunnels\n");
+                       for (i = 1; i < MAXTUNNEL; i++)
+                               if (tunnel[i].ip || tunnel[i].state)
+                                       tunnelshutdown(i, "L2TPNS Closing", 6, 0, 0);
+
+                       dropped = 1;
+               }
+       }
+
        if (start_busy_wait == 0)
                start_busy_wait = TIME;
 
@@ -2933,8 +2976,8 @@ static int still_busy(void)
 # include "fake_epoll.h"
 #endif
 
-// the base set of fds polled: control, cli, udp, tun, cluster
-#define BASE_FDS       5
+// the base set of fds polled: cli, cluster, tun, udp, control, dae
+#define BASE_FDS       6
 
 // additional polled fds
 #ifdef BGP
@@ -2958,8 +3001,8 @@ static void mainloop(void)
                exit(1);
        }
 
-       LOG(4, 0, 0, "Beginning of main loop.  udpfd=%d, tunfd=%d, cluster_sockfd=%d, controlfd=%d\n",
-               udpfd, tunfd, cluster_sockfd, controlfd);
+       LOG(4, 0, 0, "Beginning of main loop.  clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d\n",
+               clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd);
 
        /* setup our fds to poll for input */
        {
@@ -2969,25 +3012,29 @@ static void mainloop(void)
                e.events = EPOLLIN;
                i = 0;
 
-               d[i].type = FD_TYPE_CONTROL;
-               e.data.ptr = &d[i++];
-               epoll_ctl(epollfd, EPOLL_CTL_ADD, controlfd, &e);
-
                d[i].type = FD_TYPE_CLI;
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, clifd, &e);
 
-               d[i].type = FD_TYPE_UDP;
+               d[i].type = FD_TYPE_CLUSTER;
                e.data.ptr = &d[i++];
-               epoll_ctl(epollfd, EPOLL_CTL_ADD, udpfd, &e);
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, cluster_sockfd, &e);
 
                d[i].type = FD_TYPE_TUN;
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, tunfd, &e);
 
-               d[i].type = FD_TYPE_CLUSTER;
+               d[i].type = FD_TYPE_UDP;
                e.data.ptr = &d[i++];
-               epoll_ctl(epollfd, EPOLL_CTL_ADD, cluster_sockfd, &e);
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, udpfd, &e);
+
+               d[i].type = FD_TYPE_CONTROL;
+               e.data.ptr = &d[i++];
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, controlfd, &e);
+
+               d[i].type = FD_TYPE_DAE;
+               e.data.ptr = &d[i++];
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, daefd, &e);
        }
 
 #ifdef BGP
@@ -3031,8 +3078,7 @@ static void mainloop(void)
                                continue;
 
                        LOG(0, 0, 0, "Error returned from select(): %s\n", strerror(errno));
-                       main_quit++;
-                       break;
+                       break; // exit
                }
 
                if (n)
@@ -3055,12 +3101,6 @@ static void mainloop(void)
                                struct event_data *d = events[i].data.ptr;
                                switch (d->type)
                                {
-                               case FD_TYPE_CONTROL: // nsctl commands
-                                       alen = sizeof(addr);
-                                       processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
-                                       n--;
-                                       break;
-
                                case FD_TYPE_CLI: // CLI connections
                                {
                                        int cli;
@@ -3079,9 +3119,21 @@ static void mainloop(void)
                                }
 
                                // these are handled below, with multiple interleaved reads
-                               case FD_TYPE_UDP:       udp_ready++; break;
-                               case FD_TYPE_TUN:       tun_ready++; break;
                                case FD_TYPE_CLUSTER:   cluster_ready++; break;
+                               case FD_TYPE_TUN:       tun_ready++; break;
+                               case FD_TYPE_UDP:       udp_ready++; break;
+
+                               case FD_TYPE_CONTROL: // nsctl commands
+                                       alen = sizeof(addr);
+                                       processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
+                                       n--;
+                                       break;
+
+                               case FD_TYPE_DAE: // DAE requests
+                                       alen = sizeof(addr);
+                                       processdae(buf, recvfrom(daefd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
+                                       n--;
+                                       break;
 
                                case FD_TYPE_RADIUS: // RADIUS response
                                        s = recv(radfds[d->index], buf, sizeof(buf), 0);
@@ -3253,7 +3305,7 @@ static void mainloop(void)
 
        //
        // Important!!! We MUST not process any packets past this point!
-       LOG(1, 0, 0, "Clean shutdown complete\n");
+       LOG(1, 0, 0, "Shutdown complete\n");
 }
 
 static void stripdomain(char *host)
@@ -3902,11 +3954,18 @@ int main(int argc, char *argv[])
        initrad();
        initippool();
 
-       signal(SIGHUP, sighup_handler);
-       signal(SIGTERM, sigterm_handler);
-       signal(SIGINT, sigterm_handler);
-       signal(SIGQUIT, sigquit_handler);
+       // seed prng
+       {
+               unsigned seed = time_now ^ getpid();
+               LOG(4, 0, 0, "Seeding the pseudo random generator: %u\n", seed);
+               srand(seed);
+       }
+
+       signal(SIGHUP,  sighup_handler);
        signal(SIGCHLD, sigchild_handler);
+       signal(SIGTERM, shutdown_handler);
+       signal(SIGINT,  shutdown_handler);
+       signal(SIGQUIT, shutdown_handler);
 
        // Prevent us from getting paged out
        if (config->lock_pages)
@@ -3984,33 +4043,10 @@ static void sigalrm_handler(int sig)
 
 }
 
-static void sigterm_handler(int sig)
+static void shutdown_handler(int sig)
 {
-       LOG(1, 0, 0, "Shutting down cleanly\n");
-       main_quit++;
-}
-
-static void sigquit_handler(int sig)
-{
-       int i;
-
-       LOG(1, 0, 0, "Shutting down without saving sessions\n");
-
-       if (config->cluster_iam_master)
-       {
-               for (i = 1; i < MAXSESSION; i++)
-               {
-                       if (session[i].opened)
-                               sessionkill(i, "L2TPNS Closing");
-               }
-               for (i = 1; i < MAXTUNNEL; i++)
-               {
-                       if (tunnel[i].ip || tunnel[i].state)
-                               tunnelshutdown(i, "L2TPNS Closing", 6, 0, 0);
-               }
-       }
-
-       main_quit++;
+       LOG(1, 0, 0, "Shutting down\n");
+       main_quit = (sig == SIGQUIT) ? QUIT_SHUTDOWN : QUIT_FAILOVER;
 }
 
 static void sigchild_handler(int sig)
@@ -4177,6 +4213,9 @@ static void update_config()
                        strcat(config->radius_authtypes_s, ", pap");
        }
 
+       if (!config->radius_dae_port)
+               config->radius_dae_port = DAEPORT;
+
        // re-initialise the random number source
        initrandom(config->random_device);
 
@@ -4285,7 +4324,7 @@ int sessionsetup(tunnelidt t, sessionidt s)
                if (!session[s].ip)
                {
                        LOG(0, s, t, "   No IP allocated.  The IP address pool is FULL!\n");
-                       sessionshutdown(s, "No IP addresses available.", 2, 7);
+                       sessionshutdown(s, "No IP addresses available.", 2, 7); // try another
                        return 0;
                }
                LOG(3, s, t, "   No IP allocated.  Assigned %s from pool\n",
@@ -5004,11 +5043,11 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec
        uint8_t digest[16];
        uint8_t *last;
        size_t d = 0;
+       uint16_t m = htons(type);
 
        // Compute initial pad
        MD5Init(&ctx);
-       MD5Update(&ctx, (uint8_t) (type >> 8) & 0xff, 1);
-       MD5Update(&ctx, (uint8_t)  type       & 0xff, 1);
+       MD5Update(&ctx, (unsigned char *) &m, 2);
        MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret));
        MD5Update(&ctx, vector, vec_len);
        MD5Final(digest, &ctx);
@@ -5035,6 +5074,31 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec
        }
 }
 
+int find_filter(char const *name, size_t len)
+{
+       int free = -1;
+       int i;
+
+       for (i = 0; i < MAXFILTER; i++)
+       {
+               if (!*ip_filters[i].name)
+               {
+                       if (free < 0)
+                               free = i;
+
+                       continue;
+               }
+
+               if (strlen(ip_filters[i].name) != len)
+                       continue;
+
+               if (!strncmp(ip_filters[i].name, name, len))
+                       return i;
+       }
+                       
+       return free;
+}
+
 static int ip_filter_port(ip_filter_portt *p, uint16_t port)
 {
        switch (p->op)