improved load balancing algorithm.
[l2tpns.git] / l2tpns.c
index 8f1ace5..9861e88 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -75,7 +75,7 @@ int cluster_sockfd = -1;      // Intra-cluster communications socket.
 int epollfd = -1;              // event polling
 time_t basetime = 0;           // base clock
 char hostname[MAXHOSTNAME] = "";       // us.
-static int tunidx;             // ifr_ifindex of tun device
+int tunidx;                            // ifr_ifindex of tun device
 int nlseqnum = 0;              // netlink sequence number
 int min_initok_nlseqnum = 0;   // minimun seq number for messages after init is ok
 static int syslog_log = 0;     // are we logging to syslog
@@ -92,10 +92,7 @@ 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
 
-union iphash {
-       sessionidt sess;
-       union iphash *idx;
-} ip_hash[256];                        // Mapping from IP address to session structures.
+union iphash ip_hash[256];     // Mapping from IP address to session structures.
 
 struct ipv6radix {
        sessionidt sess;
@@ -152,6 +149,7 @@ config_descriptt config_values[] = {
        CONFIG("throttle_speed", rl_rate, UNSIGNED_LONG),
        CONFIG("throttle_buckets", num_tbfs, INT),
        CONFIG("accounting_dir", accounting_dir, STRING),
+       CONFIG("account_all_origin", account_all_origin, BOOL),
        CONFIG("dump_speed", dump_speed, BOOL),
        CONFIG("multi_read_count", multi_read_count, INT),
        CONFIG("scheduler_fifo", scheduler_fifo, BOOL),
@@ -185,7 +183,9 @@ config_descriptt config_values[] = {
        CONFIG("disable_sending_hello", disable_sending_hello, BOOL),
        CONFIG("disable_no_spoof", disable_no_spoof, BOOL),
        CONFIG("bind_multi_address", bind_multi_address, STRING),
+       CONFIG("grp_txrate_average_time", grp_txrate_average_time, INT),
        CONFIG("pppoe_only_equal_svc_name", pppoe_only_equal_svc_name, BOOL),
+       CONFIG("multi_hostname", multi_hostname, STRING),
        { NULL, 0, 0, 0 }
 };
 
@@ -214,6 +214,7 @@ tunnelt *tunnel = NULL;                     // Array of tunnel structures.
 bundlet *bundle = NULL;                        // Array of bundle structures.
 fragmentationt *frag = NULL;           // Array of fragmentation structures.
 sessiont *session = NULL;              // Array of session structures.
+groupsesst *grpsession = NULL;         // Array of groupsesst 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.
@@ -224,9 +225,6 @@ struct Tstats *_statistics = NULL;
 struct Tringbuffer *ringbuffer = NULL;
 #endif
 
-static ssize_t netlink_send(struct nlmsghdr *nh);
-static void netlink_addattr(struct nlmsghdr *nh, int type, const void *data, int alen);
-static void cache_ipmap(in_addr_t ip, sessionidt s);
 static void uncache_ipmap(in_addr_t ip);
 static void cache_ipv6map(struct in6_addr ip, int prefixlen, sessionidt s);
 static void free_ip_address(sessionidt s);
@@ -255,8 +253,9 @@ static clockt now(double *f)
        if (f) *f = t.tv_sec + t.tv_usec / 1000000.0;
        if (t.tv_sec != time_now)
        {
-           time_now = t.tv_sec;
-           time_changed++;
+               time_now = t.tv_sec;
+               time_changed++;
+               grp_time_changed();
        }
 
        // Time in milliseconds
@@ -615,7 +614,7 @@ static void initnetlink(void)
        }
 }
 
-static ssize_t netlink_send(struct nlmsghdr *nh)
+ssize_t netlink_send(struct nlmsghdr *nh)
 {
        struct sockaddr_nl nladdr;
        struct iovec iov;
@@ -651,7 +650,7 @@ static ssize_t netlink_recv(void *buf, ssize_t len)
 }
 
 /* adapted from iproute2 */
-static void netlink_addattr(struct nlmsghdr *nh, int type, const void *data, int alen)
+void netlink_addattr(struct nlmsghdr *nh, int type, const void *data, int alen)
 {
        int len = RTA_LENGTH(alen);
        struct rtattr *rta;
@@ -1029,7 +1028,7 @@ sessionidt sessionbyipv6(struct in6_addr ip)
 //
 // (It's actually cached in network order)
 //
-static void cache_ipmap(in_addr_t ip, sessionidt s)
+void cache_ipmap(in_addr_t ip, sessionidt s)
 {
        in_addr_t nip = htonl(ip);      // MUST be in network order. I.e. MSB must in be ((char *) (&ip))[0]
        uint8_t *a = (uint8_t *) &nip;
@@ -1411,9 +1410,10 @@ static void update_session_out_stat(sessionidt s, sessiont *sp, int len)
 void processipout(uint8_t *buf, int len)
 {
        sessionidt s;
+       groupidt g;
        sessiont *sp;
        tunnelidt t;
-       in_addr_t ip;
+       in_addr_t ip, ip_src;
 
        uint8_t *data = buf;    // Keep a copy of the originals.
        int size = len;
@@ -1446,8 +1446,33 @@ void processipout(uint8_t *buf, int len)
                return;
        }
 
+       ip_src = *(uint32_t *)(buf + 12);
        ip = *(uint32_t *)(buf + 16);
-       if (!(s = sessionbyip(ip)))
+       if ((g = grp_groupbyip(ip)))
+       {
+               s = grp_getnextsession(g, ip, ip_src);
+               if (!s)
+               {
+                       // Is this a packet for a session that doesn't exist?
+                       static int rate = 0;    // Number of ICMP packets we've sent this second.
+                       static int last = 0;    // Last time we reset the ICMP packet counter 'rate'.
+
+                       if (last != time_now)
+                       {
+                               last = time_now;
+                               rate = 0;
+                       }
+
+                       if (rate++ < config->icmp_rate) // Only send a max of icmp_rate per second.
+                       {
+                               LOG(4, 0, 0, "IP: Sending ICMP host unreachable to %s\n", fmtaddr(*(in_addr_t *)(buf + 12), 0));
+                               host_unreachable(*(in_addr_t *)(buf + 12), *(uint16_t *)(buf + 4),
+                                       config->bind_address ? config->bind_address : my_address, buf, len);
+                       }
+                       return;
+               }
+       }
+       else if (!(s = sessionbyip(ip)))
        {
                // Is this a packet for a session that doesn't exist?
                static int rate = 0;    // Number of ICMP packets we've sent this second.
@@ -2078,7 +2103,7 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e
                session[s].die = TIME + 150; // Clean up in 15 seconds
 
        if (session[s].ip)
-       {                          // IP allocated, clear and unroute
+       {       // IP allocated, clear and unroute
                int r;
                int routed = 0;
                for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++)
@@ -2278,6 +2303,8 @@ static void sessionclear(sessionidt s)
 // kill a session now
 void sessionkill(sessionidt s, char *reason)
 {
+       groupidt g;
+
        CSTAT(sessionkill);
 
        if (!session[s].opened) // not alive
@@ -2306,6 +2333,12 @@ void sessionkill(sessionidt s, char *reason)
        }
 
        LOG(2, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason);
+
+       if ((g = grp_groupbysession(s)))
+       {
+               grp_removesession(g, s);
+       }
+
        sessionclear(s);
        cluster_send_session(s);
 }
@@ -2411,7 +2444,8 @@ static void tunnelshutdown(tunnelidt t, char *reason, int result, int error, cha
 // read and process packet on tunnel (UDP)
 void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexudpfd)
 {
-       uint8_t *chapresponse = NULL;
+       uint8_t *sendchalresponse = NULL;
+       uint8_t *recvchalresponse = NULL;
        uint16_t l = len, t = 0, s = 0, ns = 0, nr = 0;
        uint8_t *p = buf + 2;
 
@@ -2815,17 +2849,20 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexu
                                                tunnel[t].window = 1; // window of 0 is silly
                                        LOG(4, s, t, "   rx window = %u\n", tunnel[t].window);
                                        break;
-                               case 11:        // Challenge
+                               case 11:        // Request Challenge
                                        {
                                                LOG(4, s, t, "   LAC requested CHAP authentication for tunnel\n");
-                                               build_chap_response(b, 2, n, &chapresponse);
+                                               if (message == 1)
+                                                       build_chap_response(b, 2, n, &sendchalresponse);
+                                               else if (message == 2)
+                                                       build_chap_response(b, 3, n, &sendchalresponse);
                                        }
                                        break;
-                               case 13:    // Response
+                               case 13:    // receive challenge Response
                                        if (tunnel[t].isremotelns)
                                        {
-                                               chapresponse = calloc(17, 1);
-                                               memcpy(chapresponse, b, (n < 17) ? n : 16);
+                                               recvchalresponse = calloc(17, 1);
+                                               memcpy(recvchalresponse, b, (n < 17) ? n : 16);
                                                LOG(3, s, t, "received challenge response from REMOTE LNS\n");
                                        }
                                        else
@@ -3061,8 +3098,8 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexu
                                                controlt *c = controlnew(2); // sending SCCRP
                                                control16(c, 2, version, 1); // protocol version
                                                control32(c, 3, 3, 1); // framing
-                                               controls(c, 7, hostname, 1); // host name
-                                               if (chapresponse) controlb(c, 13, chapresponse, 16, 1); // Challenge response
+                                               controls(c, 7, config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname, 1); // host name
+                                               if (sendchalresponse) controlb(c, 13, sendchalresponse, 16, 1); // Send Challenge response
                                                control16(c, 9, t, 1); // assigned tunnel
                                                controladd(c, 0, t); // send the resply
                                        }
@@ -3077,20 +3114,21 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexu
                                        LOG(3, s, t, "Received SCCRP\n");
                                        if (main_quit != QUIT_SHUTDOWN)
                                        {
-                                               if (tunnel[t].isremotelns && chapresponse)
+                                               if (tunnel[t].isremotelns && recvchalresponse)
                                                {
                                                        hasht hash;
 
                                                        lac_calc_rlns_auth(t, 2, hash); // id = 2 (SCCRP)
                                                        // check authenticator
-                                                       if (memcmp(hash, chapresponse, 16) == 0)
+                                                       if (memcmp(hash, recvchalresponse, 16) == 0)
                                                        {
                                                                LOG(3, s, t, "sending SCCCN to REMOTE LNS\n");
                                                                controlt *c = controlnew(3); // sending SCCCN
-                                                               controls(c, 7, hostname, 1); // host name
+                                                               controls(c, 7, config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname, 1); // host name
                                                                controls(c, 8, Vendor_name, 1); // Vendor name
                                                                control16(c, 2, version, 1); // protocol version
                                                                control32(c, 3, 3, 1); // framing Capabilities
+                                                               if (sendchalresponse) controlb(c, 13, sendchalresponse, 16, 1); // Challenge response
                                                                control16(c, 9, t, 1); // assigned tunnel
                                                                controladd(c, 0, t); // send
                                                        }
@@ -3211,7 +3249,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexu
 
                                        // Set multilink options before sending initial LCP packet
                                        sess_local[s].mp_mrru = 1614;
-                                       sess_local[s].mp_epdis = ntohl(config->iftun_n_address[tunnel[t].indexudp] ? config->iftun_n_address[tunnel[t].indexudp] : my_address);
+                                       sess_local[s].mp_epdis = ntohl(config->iftun_address ? config->iftun_address : my_address);
 
                                        sendlcp(s, t);
                                        change_state(s, lcp, RequestSent);
@@ -3233,7 +3271,8 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexu
                                                LOG(1, s, t, "Unknown message type %u\n", message);
                                        break;
                                }
-                       if (chapresponse) free(chapresponse);
+                       if (sendchalresponse) free(sendchalresponse);
+                       if (recvchalresponse) free(recvchalresponse);
                        cluster_send_tunnel(t);
                }
                else
@@ -3697,7 +3736,7 @@ static void regular_cleanups(double period)
 
                // No data in ECHO_TIMEOUT seconds, send LCP ECHO
                if (session[s].ppp.phase >= Establish && (time_now - session[s].last_packet >= config->echo_timeout) &&
-                       (time_now - sess_local[s].last_echo >= ECHO_TIMEOUT))
+                       (time_now - sess_local[s].last_echo >= config->echo_timeout))
                {
                        uint8_t b[MAXETHER];
 
@@ -4634,6 +4673,8 @@ static void initdata(int optdebug, char *optconfig)
 #endif /* BGP */
 
        lac_initremotelnsdata();
+
+       grp_initdata();
 }
 
 static int assign_ip_address(sessionidt s)
@@ -4939,6 +4980,21 @@ static int dump_session(FILE **f, sessiont *s)
                }
 
                LOG(3, 0, 0, "Dumping accounting information to %s\n", filename);
+               if(config->account_all_origin)
+               {
+               fprintf(*f, "# dslwatch.pl dump file V1.01\n"
+                       "# host: %s\n"
+                       "# endpoint: %s\n"
+                       "# time: %ld\n"
+                       "# uptime: %ld\n"
+                       "# format: username ip qos uptxoctets downrxoctets origin(L=LAC, R=Remote LNS, P=PPPOE)\n",
+                       hostname,
+                       fmtaddr(config->iftun_n_address[tunnel[s->tunnel].indexudp] ? config->iftun_n_address[tunnel[s->tunnel].indexudp] : my_address, 0),
+                       now,
+                       now - basetime);
+               }
+               else
+               {
                fprintf(*f, "# dslwatch.pl dump file V1.01\n"
                        "# host: %s\n"
                        "# endpoint: %s\n"
@@ -4949,15 +5005,29 @@ static int dump_session(FILE **f, sessiont *s)
                        fmtaddr(config->iftun_n_address[tunnel[s->tunnel].indexudp] ? config->iftun_n_address[tunnel[s->tunnel].indexudp] : my_address, 0),
                        now,
                        now - basetime);
+               }
        }
 
        LOG(4, 0, 0, "Dumping accounting information for %s\n", s->user);
+       if(config->account_all_origin)
+       {
+       fprintf(*f, "%s %s %d %u %u %s\n",
+               s->user,                                                // username
+               fmtaddr(htonl(s->ip), 0),                               // ip
+               (s->throttle_in || s->throttle_out) ? 2 : 1,            // qos
+               (uint32_t) s->cin_delta,                                // uptxoctets
+               (uint32_t) s->cout_delta,                               // downrxoctets
+               (s->tunnel == TUNNEL_ID_PPPOE)?"P":(tunnel[s->tunnel].isremotelns?"R":"L"));    // Origin
+       }
+       else if (!tunnel[s->tunnel].isremotelns && (s->tunnel != TUNNEL_ID_PPPOE))
+       {
        fprintf(*f, "%s %s %d %u %u\n",
                s->user,                                                // username
                fmtaddr(htonl(s->ip), 0),                               // ip
                (s->throttle_in || s->throttle_out) ? 2 : 1,            // qos
                (uint32_t) s->cin_delta,                                // uptxoctets
                (uint32_t) s->cout_delta);                              // downrxoctets
+       }
 
        s->cin_delta = s->cout_delta = 0;
 
@@ -5145,6 +5215,9 @@ int main(int argc, char *argv[])
                        LOG(0, 0, 0, "Can't lock pages: %s\n", strerror(errno));
        }
 
+       //LOG(3, 0, 0, "Debug sizeof struct: sessiont %lu, tunnelt %lu, bundlet %lu, groupsesst %lu\n",
+       //      sizeof(sessiont), sizeof(tunnelt), sizeof(bundlet), sizeof(groupsesst));
+
        mainloop();
 
        /* remove plugins (so cleanup code gets run) */
@@ -5390,6 +5463,8 @@ static void update_config()
                                config->iftun_n_address[config->nbmultiaddress] = htonl(ip);
                                config->nbmultiaddress++;
                                LOG(1, 0, 0, "Bind address %s\n", fmtaddr(htonl(ip), 0));
+
+                               if (config->nbmultiaddress >= MAX_BINDADDR) break;
                        }
 
                        sip = n;
@@ -5408,6 +5483,42 @@ static void update_config()
                config->iftun_n_address[0] = config->iftun_address;
        }
 
+       if (*config->multi_hostname)
+       {
+               char *shost = config->multi_hostname;
+               char *n = shost;
+               char *e = config->multi_hostname + strlen(config->multi_hostname);
+               config->nbmultihostname = 0;
+
+               while (*shost && (shost < e))
+               {
+                       while ((n < e) && (*n == ' ' || *n == ',' || *n == '\t')) n++;
+
+                       i = 0;
+                       while (n < e && (*n != ',') && (*n != '\t'))
+                       {
+                               config->multi_n_hostname[config->nbmultihostname][i] = *n;
+                               n++;i++;
+                       }
+
+                       if (i > 0)
+                       {
+                               config->multi_n_hostname[config->nbmultihostname][i] = 0;
+                               LOG(1, 0, 0, "Bind Hostname %s\n", config->multi_n_hostname[config->nbmultihostname]);
+                               config->nbmultihostname++;
+                               if (config->nbmultihostname >= MAX_NBHOSTNAME) break;
+                       }
+
+                       shost = n;
+               }
+
+               if (config->nbmultihostname >= 1)
+               {
+                       strcpy(hostname, config->multi_n_hostname[0]);
+                       strcpy(config->hostname, hostname);
+               }
+       }
+
        if (!*config->pppoe_ac_name)
                strncpy(config->pppoe_ac_name, DEFAULT_PPPOE_AC_NAME, sizeof(config->pppoe_ac_name) - 1);
 
@@ -5618,6 +5729,7 @@ int sessionsetup(sessionidt s, tunnelidt t)
        if (!session[s].bundle || (bundle[session[s].bundle].num_of_links == 1))
        {
                int routed = 0;
+               groupidt g;
 
                // Add the route for this session.
                for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++)
@@ -5640,6 +5752,12 @@ int sessionsetup(sessionidt s, tunnelidt t)
                }
                else
                        cache_ipmap(session[s].ip, s);
+
+               if ((g = grp_groupbysession(s)))
+               {
+                       grp_setgrouproute(g, 1);
+                       cluster_send_groupe(g);
+               }
        }
 
        sess_local[s].lcp_authtype = 0; // RADIUS authentication complete
@@ -6482,7 +6600,7 @@ void lac_send_SCCRQ(tunnelidt t, uint8_t * auth, unsigned int auth_len)
 
        // Sent SCCRQ - Start Control Connection Request
        controlt *c = controlnew(1); // sending SCCRQ
-       controls(c, 7, hostname, 1); // host name
+       controls(c, 7, config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname, 1); // host name
        controls(c, 8, Vendor_name, 1); // Vendor name
        control16(c, 2, version, 1); // protocol version
        control32(c, 3, 3, 1); // framing Capabilities