Fix RADIUS authentication on DAE responses.
[l2tpns.git] / l2tpns.c
index 6839c25..3b4b6af 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.131 2005-09-13 14:27:14 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.142 2005-09-19 02:39:57 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -75,6 +75,10 @@ static int syslog_log = 0;   // are we logging to syslog
 static FILE *log_stream = 0;   // file handle for direct logging (i.e. direct into file, not via syslog).
 uint32_t last_id = 0;          // Unique ID for radius accounting
 
+// calculated from config->l2tp_mtu
+uint16_t MRU = 0;              // PPP MRU
+uint16_t MSS = 0;              // TCP MSS
+
 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
 
@@ -92,7 +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
 char main_quit = 0;                    // True if we're in the process of exiting.
+char main_reload = 0;                  // Re-load pending
 linked_list *loaded_plugins;
 linked_list *plugins[MAX_PLUGIN_TYPES];
 
@@ -104,7 +110,8 @@ config_descriptt config_values[] = {
        CONFIG("log_file", log_filename, STRING),
        CONFIG("pid_file", pid_file, STRING),
        CONFIG("random_device", random_device, STRING),
-       CONFIG("l2tp_secret", l2tpsecret, STRING),
+       CONFIG("l2tp_secret", l2tp_secret, STRING),
+       CONFIG("l2tp_mtu", l2tp_mtu, INT),
        CONFIG("ppp_restart_time", ppp_restart_time, INT),
        CONFIG("ppp_max_configure", ppp_max_configure, INT),
        CONFIG("ppp_max_failure", ppp_max_failure, INT),
@@ -183,7 +190,6 @@ static void cache_ipv6map(struct in6_addr ip, int prefixlen, int s);
 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 shutdown_handler(int sig);
 static void sigchild_handler(int sig);
 static void build_chap_response(uint8_t *challenge, uint8_t id, uint16_t challenge_length, uint8_t **challenge_response);
@@ -205,11 +211,17 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec
 #define QUIT_SHUTDOWN  2 // SIGQUIT: shutdown sessions/tunnels, reject new connections
 
 // return internal time (10ths since process startup), set f if given
+// as a side-effect sets time_now, and time_changed
 static clockt now(double *f)
 {
        struct timeval t;
        gettimeofday(&t, 0);
        if (f) *f = t.tv_sec + t.tv_usec / 1000000.0;
+       if (t.tv_sec != time_now)
+       {
+           time_now = t.tv_sec;
+           time_changed++;
+       }
        return (t.tv_sec - basetime) * 10 + t.tv_usec / 100000 + 1;
 }
 
@@ -978,6 +990,61 @@ int tun_write(uint8_t * data, int size)
        return write(tunfd, data, size);
 }
 
+// adjust tcp mss to avoid fragmentation (called only for tcp packets with syn set)
+void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *tcp)
+{
+       int d = (tcp[12] >> 4) * 4;
+       uint8_t *mss = 0;
+       uint8_t *opts;
+       uint8_t *data;
+       uint16_t orig;
+       uint32_t sum;
+
+       if ((tcp[13] & 0x3f) & ~(TCP_FLAG_SYN|TCP_FLAG_ACK)) // only want SYN and SYN,ACK
+               return;
+
+       if (tcp + d > buf + len) // short?
+               return;
+
+       opts = tcp + 20;
+       data = tcp + d;
+
+       while (opts < data)
+       {
+               if (*opts == 2 && opts[1] == 4) // mss option (2), length 4
+               {
+                       mss = opts + 2;
+                       if (mss + 2 > data) return; // short?
+                       break;
+               }
+
+               if (*opts == 0) return; // end of options
+               if (*opts == 1 || !opts[1]) // no op (one byte), or no length (prevent loop)
+                       opts++;
+               else
+                       opts += opts[1]; // skip over option
+       }
+
+       if (!mss) return; // not found
+       orig = ntohs(*(uint16_t *) mss);
+
+       if (orig <= MSS) return; // mss OK
+
+       LOG(5, s, t, "TCP: %s:%u -> %s:%u SYN%s: adjusted mss from %u to %u\n",
+               fmtaddr(*(in_addr_t *) (buf + 12), 0), ntohs(*(uint16_t *) tcp),
+               fmtaddr(*(in_addr_t *) (buf + 16), 1), ntohs(*(uint16_t *) (tcp + 2)),
+               (tcp[13] & TCP_FLAG_ACK) ? ",ACK" : "", orig, MSS);
+
+       // set mss
+       *(int16_t *) mss = htons(MSS);
+
+       // adjust checksum (see rfc1141)
+       sum = orig + (~MSS & 0xffff);
+       sum += ntohs(*(uint16_t *) (tcp + 16));
+       sum = (sum & 0xffff) + (sum >> 16);
+       *(uint16_t *) (tcp + 16) = htons(sum);
+}
+
 // process outgoing (to tunnel) IP
 //
 static void processipout(uint8_t *buf, int len)
@@ -1085,6 +1152,14 @@ static void processipout(uint8_t *buf, int len)
        if (session[s].filter_out && !ip_filter(buf, len, session[s].filter_out - 1))
                return;
 
+       // adjust MSS on SYN and SYN,ACK packets with options
+       if ((ntohs(*(uint16_t *) (buf + 6)) & 0x1fff) == 0 && buf[9] == IPPROTO_TCP) // first tcp fragment
+       {
+               int ihl = (buf[0] & 0xf) * 4; // length of IP header
+               if (len >= ihl + 20 && (buf[ihl + 13] & TCP_FLAG_SYN) && ((buf[ihl + 12] >> 4) > 5))
+                       adjust_tcp_mss(s, t, buf, len, buf + ihl);
+       }
+
        if (sp->tbf_out)
        {
                // Are we throttling this session?
@@ -2008,7 +2083,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        uint16_t orig_len;
 
                                        // handle hidden AVPs
-                                       if (!*config->l2tpsecret)
+                                       if (!*config->l2tp_secret)
                                        {
                                                LOG(1, s, t, "Hidden AVP requested, but no L2TP secret.\n");
                                                fatal = flags;
@@ -2381,7 +2456,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].l2tp_flags = aflags; // set flags received
-                                       session[s].mru = DEFAULT_MRU;
+                                       session[s].mru = PPPMTU; // default
                                        controlnull(t); // ack
 
                                        // start LCP
@@ -2389,6 +2464,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        sess_local[s].lcp.conf_sent = 1;
                                        sess_local[s].lcp.nak_sent = 0;
                                        sess_local[s].lcp_authtype = config->radius_authprefer;
+                                       sess_local[s].ppp_mru = MRU;
                                        session[s].ppp.lcp = RequestSent;
                                        sendlcp(s, t);
 
@@ -2534,19 +2610,17 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                        uint8_t buf[MAXETHER];
                        uint8_t *q;
                        int mru = session[s].mru;
-
-                       if (!mru) mru = MAXMRU;
                        if (mru > sizeof(buf)) mru = sizeof(buf);
 
                        l += 6;
                        if (l > mru) l = mru;
 
-                       q = makeppp(buf, sizeof(buf), 0, 0, s, t, proto);
+                       q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPLCP);
                        if (!q) return;
 
-                       *q = CodeRej;
+                       *q = ProtocolRej;
                        *(q + 1) = ++sess_local[s].lcp_ident;
-                       *(uint16_t *)(q + 2) = l;
+                       *(uint16_t *)(q + 2) = htons(l);
                        *(uint16_t *)(q + 4) = htons(proto);
                        memcpy(q + 6, p, l - 6);
 
@@ -3161,9 +3235,17 @@ static void mainloop(void)
                int more = 0;
                int n;
 
+
+               if (main_reload)
+               {
+                       main_reload = 0;
+                       read_config_file();
+                       config->reload_config++;
+               }
+
                if (config->reload_config)
                {
-                       // Update the config state based on config settings
+                       config->reload_config = 0;
                        update_config();
                }
 
@@ -3327,6 +3409,35 @@ static void mainloop(void)
                        }
                }
 
+               if (time_changed)
+               {
+                       double Mbps = 1024.0 * 1024.0 / 8 * time_changed;
+
+                       // Log current traffic stats
+                       snprintf(config->bandwidth, sizeof(config->bandwidth),
+                               "UDP-ETH:%1.0f/%1.0f  ETH-UDP:%1.0f/%1.0f  TOTAL:%0.1f   IN:%u OUT:%u",
+                               (udp_rx / Mbps), (eth_tx / Mbps), (eth_rx / Mbps), (udp_tx / Mbps),
+                               ((udp_tx + udp_rx + eth_tx + eth_rx) / Mbps),
+                               udp_rx_pkt / time_changed, eth_rx_pkt / time_changed);
+                
+                       udp_tx = udp_rx = 0;
+                       udp_rx_pkt = eth_rx_pkt = 0;
+                       eth_tx = eth_rx = 0;
+                       time_changed = 0;
+                
+                       if (config->dump_speed)
+                               printf("%s\n", config->bandwidth);
+                
+                       // Update the internal time counter
+                       strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now));
+                
+                       {
+                               // Run timer hooks
+                               struct param_timer p = { time_now };
+                               run_plugins(PLUGIN_TIMER, &p);
+                       }
+               }
+
                        // Runs on every machine (master and slaves).
                if (next_cluster_ping <= TIME)
                {
@@ -4002,14 +4113,13 @@ int main(int argc, char *argv[])
        // Start the timer routine off
        time(&time_now);
        strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now));
-       signal(SIGALRM, sigalrm_handler);
-       siginterrupt(SIGALRM, 0);
 
        initplugins();
        initdata(optdebug, optconfig);
 
        init_cli(hostname);
        read_config_file();
+       update_config();
        init_tbf(config->num_tbfs);
 
        LOG(0, 0, 0, "L2TPNS version " VERSION "\n");
@@ -4085,8 +4195,6 @@ int main(int argc, char *argv[])
                        LOG(0, 0, 0, "Can't lock pages: %s\n", strerror(errno));
        }
 
-       alarm(1);
-
        // Drop privileges here
        if (config->target_uid > 0 && geteuid() == 0)
                setuid(config->target_uid);
@@ -4108,53 +4216,11 @@ int main(int argc, char *argv[])
 
 static void sighup_handler(int sig)
 {
-       if (log_stream)
-       {
-               if (log_stream != stderr)
-                       fclose(log_stream);
-
-               log_stream = NULL;
-       }
-
-       read_config_file();
-}
-
-static void sigalrm_handler(int sig)
-{
-       // Log current traffic stats
-
-       snprintf(config->bandwidth, sizeof(config->bandwidth),
-               "UDP-ETH:%1.0f/%1.0f  ETH-UDP:%1.0f/%1.0f  TOTAL:%0.1f   IN:%u OUT:%u",
-               (udp_rx / 1024.0 / 1024.0 * 8),
-               (eth_tx / 1024.0 / 1024.0 * 8),
-               (eth_rx / 1024.0 / 1024.0 * 8),
-               (udp_tx / 1024.0 / 1024.0 * 8),
-               ((udp_tx + udp_rx + eth_tx + eth_rx) / 1024.0 / 1024.0 * 8),
-               udp_rx_pkt, eth_rx_pkt);
-
-       udp_tx = udp_rx = 0;
-       udp_rx_pkt = eth_rx_pkt = 0;
-       eth_tx = eth_rx = 0;
-
-       if (config->dump_speed)
-               printf("%s\n", config->bandwidth);
-
-       // Update the internal time counter
-       time(&time_now);
-       strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now));
-       alarm(1);
-
-       {
-               // Run timer hooks
-               struct param_timer p = { time_now };
-               run_plugins(PLUGIN_TIMER, &p);
-       }
-
+       main_reload++;
 }
 
 static void shutdown_handler(int sig)
 {
-       LOG(1, 0, 0, "Shutting down\n");
        main_quit = (sig == SIGQUIT) ? QUIT_SHUTDOWN : QUIT_FAILOVER;
 }
 
@@ -4169,7 +4235,7 @@ static void build_chap_response(uint8_t *challenge, uint8_t id, uint16_t challen
        MD5_CTX ctx;
        *challenge_response = NULL;
 
-       if (!*config->l2tpsecret)
+       if (!*config->l2tp_secret)
        {
                LOG(0, 0, 0, "LNS requested CHAP authentication, but no l2tp secret is defined\n");
                return;
@@ -4181,7 +4247,7 @@ static void build_chap_response(uint8_t *challenge, uint8_t id, uint16_t challen
 
        MD5_Init(&ctx);
        MD5_Update(&ctx, &id, 1);
-       MD5_Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret));
+       MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret));
        MD5_Update(&ctx, challenge, challenge_length);
        MD5_Final(*challenge_response, &ctx);
 
@@ -4248,6 +4314,17 @@ static void update_config()
                setbuf(log_stream, NULL);
        }
 
+#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;
+       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;
+       MSS = MRU - TCP_HDRS;
+
        // Update radius
        config->numradiusservers = 0;
        for (i = 0; i < MAXRADSERVER; i++)
@@ -4393,8 +4470,6 @@ static void update_config()
                        LOG(0, 0, 0, "Can't write to PID file %s: %s\n", config->pid_file, strerror(errno));
                }
        }
-
-       config->reload_config = 0;
 }
 
 static void read_config_file()
@@ -4412,7 +4487,6 @@ static void read_config_file()
        cli_do_file(f);
        LOG(3, 0, 0, "Done reading config file\n");
        fclose(f);
-       update_config();
 }
 
 int sessionsetup(sessionidt s, tunnelidt t)
@@ -5146,7 +5220,7 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec
        // Compute initial pad
        MD5_Init(&ctx);
        MD5_Update(&ctx, (unsigned char *) &m, 2);
-       MD5_Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret));
+       MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret));
        MD5_Update(&ctx, vector, vec_len);
        MD5_Final(digest, &ctx);
 
@@ -5159,7 +5233,7 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec
                if (d >= sizeof(digest))
                {
                        MD5_Init(&ctx);
-                       MD5_Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret));
+                       MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret));
                        MD5_Update(&ctx, last, sizeof(digest));
                        MD5_Final(digest, &ctx);