add Acct-Terminate-Cause to RADIUS stop records
[l2tpns.git] / l2tpns.c
index 0d9a2c2..5c7289c 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.148 2005/11/14 21:08:30 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.160 2006/04/13 11:14:35 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -96,9 +96,9 @@ uint32_t eth_tx = 0;
 static uint32_t 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.
-int time_changed = 0;                  // time_now changed
+static int time_changed = 0;           // time_now changed
 char main_quit = 0;                    // True if we're in the process of exiting.
-char main_reload = 0;                  // Re-load pending
+static char main_reload = 0;           // Re-load pending
 linked_list *loaded_plugins;
 linked_list *plugins[MAX_PLUGIN_TYPES];
 
@@ -200,7 +200,7 @@ 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(uint8_t *buf, int len, struct sockaddr_in *addr, int alen);
+static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struct in_addr *local);
 static tunnelidt new_tunnel(void);
 static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len);
 
@@ -554,6 +554,13 @@ static void inittun(void)
                LOG(0, 0, 0, "Error setting tun queue length: %s\n", strerror(errno));
                exit(1);
        }
+       /* set MTU to modem MRU */
+       ifr.ifr_mtu = MRU;
+       if (ioctl(ifrfd, SIOCSIFMTU, (void *) &ifr) < 0)
+       {
+               LOG(0, 0, 0, "Error setting tun MTU: %s\n", strerror(errno));
+               exit(1);
+       }
        ifr.ifr_flags = IFF_UP;
        if (ioctl(ifrfd, SIOCSIFFLAGS, (void *) &ifr) < 0)
        {
@@ -627,6 +634,7 @@ static void initudp(void)
        addr.sin_port = htons(NSCTL_PORT);
        controlfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+       setsockopt(controlfd, SOL_IP, IP_PKTINFO, &on, sizeof(on)); // recvfromto
        if (bind(controlfd, (void *) &addr, sizeof(addr)) < 0)
        {
                LOG(0, 0, 0, "Error in control bind: %s\n", strerror(errno));
@@ -639,6 +647,7 @@ static void initudp(void)
        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));
+       setsockopt(daefd, SOL_IP, IP_PKTINFO, &on, sizeof(on)); // recvfromto
        if (bind(daefd, (void *) &addr, sizeof(addr)) < 0)
        {
                LOG(0, 0, 0, "Error in DAE bind: %s\n", strerror(errno));
@@ -933,18 +942,14 @@ void tunnelsend(uint8_t * buf, uint16_t l, tunnelidt t)
 
        if (!t)
        {
-               static int backtrace_count = 0;
                LOG(0, 0, t, "tunnelsend called with 0 as tunnel id\n");
                STAT(tunnel_tx_errors);
-               log_backtrace(backtrace_count, 5)
                return;
        }
 
        if (!tunnel[t].ip)
        {
-               static int backtrace_count = 0;
                LOG(1, 0, t, "Error sending data out tunnel: no remote endpoint (tunnel not set up)\n");
-               log_backtrace(backtrace_count, 5)
                STAT(tunnel_tx_errors);
                return;
        }
@@ -1043,7 +1048,7 @@ void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *t
        sum = orig + (~MSS & 0xffff);
        sum += ntohs(*(uint16_t *) (tcp + 16));
        sum = (sum & 0xffff) + (sum >> 16);
-       *(uint16_t *) (tcp + 16) = htons(sum);
+       *(uint16_t *) (tcp + 16) = htons(sum + (sum >> 16));
 }
 
 // process outgoing (to tunnel) IP
@@ -1170,7 +1175,8 @@ static void processipout(uint8_t *buf, int len)
                        master_throttle_packet(sp->tbf_out, data, size);
                return;
        }
-       else if (sp->walled_garden && !config->cluster_iam_master)
+
+       if (sp->walled_garden && !config->cluster_iam_master)
        {
                // We are walled-gardening this
                master_garden_packet(s, data, size);
@@ -1553,7 +1559,7 @@ void filter_session(sessionidt s, int filter_in, int filter_out)
 }
 
 // start tidy shutdown of session
-void sessionshutdown(sessionidt s, char *reason, int result, int error)
+void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_error, int term_cause)
 {
        int walled_garden = session[s].walled_garden;
 
@@ -1581,7 +1587,11 @@ void sessionshutdown(sessionidt s, char *reason, int result, int error)
                {
                        // stop, if not already trying
                        if (radius[r].state != RADIUSSTOP)
+                       {
+                               radius[r].term_cause = term_cause;
+                               radius[r].term_msg = reason;
                                radiussend(r, RADIUSSTOP);
+                       }
                }
                else
                        LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Stop message\n");
@@ -1621,18 +1631,18 @@ void sessionshutdown(sessionidt s, char *reason, int result, int error)
        if (session[s].throttle_in || session[s].throttle_out) // Unthrottle if throttled.
                throttle_session(s, 0, 0);
 
-       if (result)
+       if (cdn_result)
        {                            // Send CDN
                controlt *c = controlnew(14); // sending CDN
-               if (error)
+               if (cdn_error)
                {
                        uint8_t buf[4];
-                       *(uint16_t *) buf     = htons(result);
-                       *(uint16_t *) (buf+2) = htons(error);
+                       *(uint16_t *) buf     = htons(cdn_result);
+                       *(uint16_t *) (buf+2) = htons(cdn_error);
                        controlb(c, 1, buf, 4, 1);
                }
                else
-                       control16(c, 1, result, 1);
+                       control16(c, 1, cdn_result, 1);
 
                control16(c, 14, s, 1);   // assigned session (our end)
                controladd(c, session[s].far, session[s].tunnel); // send the message
@@ -1738,7 +1748,7 @@ void sessionkill(sessionidt s, char *reason)
        }
 
        session[s].die = TIME;
-       sessionshutdown(s, reason, 3, 0);  // close radius/routes, etc.
+       sessionshutdown(s, reason, CDN_ADMIN_DISC, TERM_ADMIN_RESET);  // close radius/routes, etc.
        if (sess_local[s].radius)
                radiusclear(sess_local[s].radius, s); // cant send clean accounting data, session is killed
 
@@ -1803,7 +1813,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, 0, 0);
+                       sessionshutdown(s, reason, CDN_NONE, TERM_ADMIN_RESET);
 
        tunnel[t].state = TUNNELDIE;
        tunnel[t].die = TIME + 700; // Clean up in 70 seconds
@@ -2042,6 +2052,12 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                        int error = 0;
                        char *msg = 0;
 
+                       // default disconnect cause/message on receipt
+                       // of CDN (set to more specific value from
+                       // attribute 46 if present below).
+                       int disc_cause = TERM_NAS_REQUEST;
+                       char const *disc_reason = "Closed (Received CDN).";
+
                        // process AVPs
                        while (l && !(fatal & 0x80)) // 0x80 = mandatory AVP
                        {
@@ -2049,6 +2065,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                uint8_t *b = p;
                                uint8_t flags = *p;
                                uint16_t mtype;
+
                                if (n > l)
                                {
                                        LOG(1, s, t, "Invalid length in AVP\n");
@@ -2185,17 +2202,13 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        }
                                        break;
                                case 3:     // framing capabilities
-//                                     LOG(4, s, t, "Framing capabilities\n");
                                        break;
                                case 4:     // bearer capabilities
-//                                     LOG(4, s, t, "Bearer capabilities\n");
                                        break;
                                case 5:         // tie breaker
                                        // We never open tunnels, so we don't care about tie breakers
-//                                     LOG(4, s, t, "Tie breaker\n");
                                        continue;
                                case 6:     // firmware revision
-//                                     LOG(4, s, t, "Firmware revision\n");
                                        break;
                                case 7:     // host name
                                        memset(tunnel[t].hostname, 0, sizeof(tunnel[t].hostname));
@@ -2350,6 +2363,84 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        memcpy(session[s].random_vector, b, n);
                                        session[s].random_vector_length = n;
                                        break;
+                               case 46:    // ppp disconnect cause
+                                       if (n >= 5)
+                                       {
+                                               uint16_t code = ntohs(*(uint16_t *) b);
+                                               uint16_t proto = ntohs(*(uint16_t *) (b + 2));
+                                               uint8_t dir = *(b + 4);
+
+                                               LOG(4, s, t, "   PPP disconnect cause "
+                                                       "(code=%u, proto=%04X, dir=%u, msg=\"%.*s\")\n",
+                                                       code, proto, dir, n - 5, b + 5);
+
+                                               switch (code)
+                                               {
+                                               case 1: // admin disconnect
+                                                       disc_cause = TERM_ADMIN_RESET;
+                                                       disc_reason = "Administrative disconnect";
+                                                       break;
+                                               case 3: // lcp terminate
+                                                       if (dir != 1) break; // 1=peer, 2=local
+                                                       disc_cause = TERM_USER_REQUEST;
+                                                       disc_reason = "Normal disconnection";
+                                                       break;
+                                               case 4: // compulsory encryption unavailable
+                                                       if (dir != 2) break; // 1=refused by peer, 2=local
+                                                       disc_cause = TERM_USER_ERROR;
+                                                       disc_reason = "Compulsory encryption refused";
+                                                       break;
+                                               case 5: // lcp: fsm timeout
+                                                       disc_cause = TERM_PORT_ERROR;
+                                                       disc_reason = "LCP: FSM timeout";
+                                                       break;
+                                               case 6: // lcp: no recognisable lcp packets received
+                                                       disc_cause = TERM_PORT_ERROR;
+                                                       disc_reason = "LCP: no recognisable LCP packets";
+                                                       break;
+                                               case 7: // lcp: magic-no error (possibly looped back)
+                                                       disc_cause = TERM_PORT_ERROR;
+                                                       disc_reason = "LCP: magic-no error (possible loop)";
+                                                       break;
+                                               case 8: // lcp: echo request timeout
+                                                       disc_cause = TERM_PORT_ERROR;
+                                                       disc_reason = "LCP: echo request timeout";
+                                                       break;
+                                               case 13: // auth: fsm timeout
+                                                       disc_cause = TERM_SERVICE_UNAVAILABLE;
+                                                       disc_reason = "Authentication: FSM timeout";
+                                                       break;
+                                               case 15: // auth: unacceptable auth protocol
+                                                       disc_cause = TERM_SERVICE_UNAVAILABLE;
+                                                       disc_reason = "Unacceptable authentication protocol";
+                                                       break;
+                                               case 16: // auth: authentication failed
+                                                       disc_cause = TERM_SERVICE_UNAVAILABLE;
+                                                       disc_reason = "Authentication failed";
+                                                       break;
+                                               case 17: // ncp: fsm timeout
+                                                       disc_cause = TERM_SERVICE_UNAVAILABLE;
+                                                       disc_reason = "NCP: FSM timeout";
+                                                       break;
+                                               case 18: // ncp: no ncps available
+                                                       disc_cause = TERM_SERVICE_UNAVAILABLE;
+                                                       disc_reason = "NCP: no NCPs available";
+                                                       break;
+                                               case 19: // ncp: failure to converge on acceptable address
+                                                       disc_cause = TERM_SERVICE_UNAVAILABLE;
+                                                       disc_reason = (dir == 1)
+                                                               ? "NCP: too many Configure-Naks received from peer"
+                                                               : "NCP: too many Configure-Naks sent to peer";
+                                                       break;
+                                               case 20: // ncp: user not permitted to use any address
+                                                       disc_cause = TERM_SERVICE_UNAVAILABLE;
+                                                       disc_reason = (dir == 1)
+                                                               ? "NCP: local link address not acceptable to peer"
+                                                               : "NCP: remote link address not acceptable";
+                                                       break;
+                                               }
+                                       }
+                                       break;
                                default:
                                        {
                                                static char e[] = "unknown AVP 0xXXXX";
@@ -2459,7 +2550,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        if (amagic == 0) amagic = time_now;
                                        session[s].magic = amagic; // set magic number
                                        session[s].flags = aflags; // set flags received
-                                       session[s].mru = PPPMTU; // default
+                                       session[s].mru = PPPoE_MRU; // default
                                        controlnull(t); // ack
 
                                        // start LCP
@@ -2471,7 +2562,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
 
                                case 14:      // CDN
                                        controlnull(t); // ack
-                                       sessionshutdown(s, "Closed (Received CDN).", 0, 0);
+                                       sessionshutdown(s, disc_reason, CDN_NONE, disc_cause);
                                        break;
                                case 0xFFFF:
                                        LOG(1, s, t, "Missing message type\n");
@@ -2607,29 +2698,9 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                }
                else if (session[s].ppp.lcp == Opened)
                {
-                       uint8_t buf[MAXETHER];
-                       uint8_t *q;
-                       int mru = session[s].mru;
-                       if (mru > sizeof(buf)) mru = sizeof(buf);
-
-                       l += 6;
-                       if (l > mru) l = mru;
-
-                       q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPLCP);
-                       if (!q) return;
-
-                       *q = ProtocolRej;
-                       *(q + 1) = ++sess_local[s].lcp_ident;
-                       *(uint16_t *)(q + 2) = htons(l);
-                       *(uint16_t *)(q + 4) = htons(proto);
-                       memcpy(q + 6, p, l - 6);
-
-                       if (proto == PPPIPV6CP)
-                               LOG(3, s, t, "LCP: send ProtocolRej (IPV6CP: not configured)\n");
-                       else
-                               LOG(2, s, t, "LCP: sent ProtocolRej (0x%04X: unsupported)\n", proto);
-
-                       tunnelsend(buf, l + (q - buf), t);
+                       session[s].last_packet = time_now;
+                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; }
+                       protoreject(s, t, p, l, proto);
                }
                else
                {
@@ -2821,7 +2892,7 @@ static void regular_cleanups(double period)
                                }
                                else
                                {
-                                       sessionshutdown(s, "No response to LCP ConfigReq.", 3, 0);
+                                       sessionshutdown(s, "No response to LCP ConfigReq.", CDN_ADMIN_DISC, TERM_LOST_SERVICE);
                                        STAT(session_timeout);
                                }
 
@@ -2850,7 +2921,7 @@ static void regular_cleanups(double period)
                                }
                                else
                                {
-                                       sessionshutdown(s, "No response to IPCP ConfigReq.", 3, 0);
+                                       sessionshutdown(s, "No response to IPCP ConfigReq.", CDN_ADMIN_DISC, TERM_LOST_SERVICE);
                                        STAT(session_timeout);
                                }
 
@@ -2916,7 +2987,7 @@ static void regular_cleanups(double period)
                // Drop sessions who have not responded within IDLE_TIMEOUT seconds
                if (session[s].last_packet && (time_now - session[s].last_packet >= IDLE_TIMEOUT))
                {
-                       sessionshutdown(s, "No response to LCP ECHO requests.", 3, 0);
+                       sessionshutdown(s, "No response to LCP ECHO requests.", CDN_ADMIN_DISC, TERM_LOST_SERVICE);
                        STAT(session_timeout);
                        s_actions++;
                        continue;
@@ -2934,7 +3005,7 @@ static void regular_cleanups(double period)
                        *q = EchoReq;
                        *(uint8_t *)(q + 1) = (time_now % 255); // ID
                        *(uint16_t *)(q + 2) = htons(8); // Length
-                       *(uint32_t *)(q + 4) = 0; // Magic Number (not supported)
+                       *(uint32_t *)(q + 4) = session[s].ppp.lcp == Opened ? htonl(session[s].magic) : 0; // Magic Number
 
                        LOG(4, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n",
                                        (int)(time_now - session[s].last_packet));
@@ -2952,7 +3023,7 @@ static void regular_cleanups(double period)
                        if (a & CLI_SESS_KILL)
                        {
                                LOG(2, s, session[s].tunnel, "Dropping session by CLI\n");
-                               sessionshutdown(s, "Requested by administrator.", 3, 0);
+                               sessionshutdown(s, "Requested by administrator.", CDN_ADMIN_DISC, TERM_ADMIN_RESET);
                                a = 0; // dead, no need to check for other actions
                                s_actions++;
                        }
@@ -3264,6 +3335,7 @@ static void mainloop(void)
                if (n)
                {
                        struct sockaddr_in addr;
+                       struct in_addr local;
                        socklen_t alen;
                        int c, s;
                        int udp_ready = 0;
@@ -3280,6 +3352,7 @@ static void mainloop(void)
                        for (c = n, i = 0; i < c; i++)
                        {
                                struct event_data *d = events[i].data.ptr;
+
                                switch (d->type)
                                {
                                case FD_TYPE_CLI: // CLI connections
@@ -3306,20 +3379,30 @@ static void mainloop(void)
 
                                case FD_TYPE_CONTROL: // nsctl commands
                                        alen = sizeof(addr);
-                                       processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
+                                       s = recvfromto(controlfd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local);
+                                       if (s > 0) processcontrol(buf, s, &addr, alen, &local);
                                        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);
+                                       s = recvfromto(daefd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local);
+                                       if (s > 0) processdae(buf, s, &addr, alen, &local);
                                        n--;
                                        break;
 
                                case FD_TYPE_RADIUS: // RADIUS response
-                                       s = recv(radfds[d->index], buf, sizeof(buf), 0);
+                                       alen = sizeof(addr);
+                                       s = recvfrom(radfds[d->index], buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen);
                                        if (s >= 0 && config->cluster_iam_master)
-                                               processrad(buf, s, d->index);
+                                       {
+                                               if (addr.sin_addr.s_addr == config->radiusserver[0] ||
+                                                   addr.sin_addr.s_addr == config->radiusserver[1])
+                                                       processrad(buf, s, d->index);
+                                               else
+                                                       LOG(3, 0, 0, "Dropping RADIUS packet from unknown source %s\n",
+                                                               fmtaddr(addr.sin_addr.s_addr, 0));
+                                       }
 
                                        n--;
                                        break;
@@ -3965,7 +4048,7 @@ static void initippool()
                else
                {
                        // It's a single ip address
-                       add_to_ip_pool(inet_addr(pool), 0);
+                       add_to_ip_pool(ntohl(inet_addr(pool)), 0);
                }
        }
        fclose(f);
@@ -4311,12 +4394,15 @@ static void update_config()
 #define L2TP_HDRS              (20+8+6+4)      // L2TP data encaptulation: ip + udp + l2tp (data) + ppp (inc hdlc)
 #define TCP_HDRS               (20+20)         // TCP encapsulation: ip + tcp
 
-       if (config->l2tp_mtu <= 0)              config->l2tp_mtu = PPPMTU;
+       if (config->l2tp_mtu <= 0)              config->l2tp_mtu = 1500; // ethernet default
        else if (config->l2tp_mtu < MINMTU)     config->l2tp_mtu = MINMTU;
        else if (config->l2tp_mtu > MAXMTU)     config->l2tp_mtu = MAXMTU;
 
        // reset MRU/MSS globals
        MRU = config->l2tp_mtu - L2TP_HDRS;
+       if (MRU > PPPoE_MRU)
+               MRU = PPPoE_MRU;
+
        MSS = MRU - TCP_HDRS;
 
        // Update radius
@@ -4501,7 +4587,7 @@ int sessionsetup(sessionidt s, tunnelidt t)
                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); // try another
+                       sessionshutdown(s, "No IP addresses available.", CDN_TRY_ANOTHER, TERM_SERVICE_UNAVAILABLE);
                        return 0;
                }
                LOG(3, s, t, "   No IP allocated.  Assigned %s from pool\n",
@@ -4888,7 +4974,7 @@ static void plugins_done()
                run_plugin_done(p);
 }
 
-static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen)
+static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struct in_addr *local)
 {
        struct nsctl request;
        struct nsctl response;
@@ -5046,7 +5132,7 @@ static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int
        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);
+               sendtofrom(controlfd, buf, r, 0, (const struct sockaddr *) addr, alen, local);
                if (log_stream && config->debug >= 4)
                {
                        LOG(4, 0, 0, "Sent [%s] ", fmtaddr(addr->sin_addr.s_addr, 0));