From: fendo Date: Wed, 6 Mar 2013 23:45:05 +0000 (+0100) Subject: merge from multibind X-Git-Tag: 2.2.1-2sames3.8~6 X-Git-Url: http://git.sameswireless.fr/l2tpns.git/commitdiff_plain/a478f1c2a22514a4de4521ffe3ed93d3c2c427b8?hp=137bcc99810a8bfc97030eceee52a96c75e91987 merge from multibind --- diff --git a/Makefile b/Makefile index 23189e0..c0c7c4e 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ INSTALL = install -c -D -o root -g root l2tpns.LIBS = -lm -lcli -ldl OBJS = arp.o cli.o cluster.o constants.o control.o icmp.o l2tpns.o \ - ll.o md5.o ppp.o radius.o tbf.o util.o pppoe.o l2tplac.o + ll.o md5.o ppp.o radius.o tbf.o util.o pppoe.o l2tplac.o grpsess.o PROGRAMS = l2tpns nsctl PLUGINS = autosnoop.so autothrottle.so garden.so sessionctl.so \ @@ -127,6 +127,7 @@ tbf.o: tbf.c l2tpns.h util.h tbf.h util.o: util.c l2tpns.h bgp.h pppoe.o: pppoe.c l2tpns.h cluster.h constants.h md5.h util.h l2tplac.o: l2tplac.c md5.h l2tpns.h util.h cluster.h l2tplac.h pppoe.h +grpsess.o: grpsess.c l2tpns.h util.h bgp.h bgp.o: bgp.c l2tpns.h bgp.h util.h autosnoop.so: autosnoop.c l2tpns.h plugin.h autothrottle.so: autothrottle.c l2tpns.h plugin.h diff --git a/arp.c b/arp.c index 6d55756..e9c7584 100644 --- a/arp.c +++ b/arp.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "l2tpns.h" diff --git a/autosnoop.c b/autosnoop.c index ddc699f..5633824 100644 --- a/autosnoop.c +++ b/autosnoop.c @@ -1,4 +1,7 @@ #include +#include +#include + #include "l2tpns.h" #include "plugin.h" diff --git a/autothrottle.c b/autothrottle.c index 73f9a94..007f926 100644 --- a/autothrottle.c +++ b/autothrottle.c @@ -1,4 +1,7 @@ #include +#include +#include + #include "l2tpns.h" #include "plugin.h" diff --git a/bgp.c b/bgp.c index 7c1e6c0..36894be 100644 --- a/bgp.c +++ b/bgp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "l2tpns.h" #include "bgp.h" diff --git a/cli.c b/cli.c index fc150d7..e5ac57c 100644 --- a/cli.c +++ b/cli.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "l2tpns.h" #include "constants.h" @@ -75,6 +76,7 @@ static char *debug_levels[] = { #endif static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc); +static int cmd_show_group(struct cli_def *cli, char *command, char **argv, int argc); static int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int argc); static int cmd_show_users(struct cli_def *cli, char *command, char **argv, int argc); static int cmd_show_radius(struct cli_def *cli, char *command, char **argv, int argc); @@ -154,6 +156,7 @@ void init_cli() cli_register_command(cli, c, "running-config", cmd_show_run, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Show the currently running configuration"); cli_register_command(cli, c, "remotelns-conf", cmd_show_rmtlnsconf, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Show a list of remote LNS configuration"); cli_register_command(cli, c, "session", cmd_show_session, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of sessions or details for a single session"); + cli_register_command(cli, c, "group", cmd_show_group, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of groups or details for a single group"); cli_register_command(cli, c, "tbf", cmd_show_tbf, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all token bucket filters in use"); cli_register_command(cli, c, "throttle", cmd_show_throttle, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all throttled sessions and associated TBFs"); cli_register_command(cli, c, "tunnels", cmd_show_tunnels, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of tunnels or details for a single tunnel"); @@ -3110,6 +3113,77 @@ static int cmd_reload(struct cli_def *cli, char *command, char **argv, int argc) return CLI_OK; } +static int cmd_show_group(struct cli_def *cli, char *command, char **argv, int argc) +{ + int i; + groupidt g; + + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, 1, + "<1-%d>", MAXGROUPE-1, "Show specific group by id", + NULL); + + time(&time_now); + if (argc > 0) + { + // Show individual group + for (i = 0; i < argc; i++) + { + sessionidt s; + + g = atoi(argv[i]); + if (g <= 0 || g >= MAXGROUPE) + { + cli_print(cli, "Invalid group id \"%s\"", argv[i]); + continue; + } + + cli_print(cli, "\r\nGroup %d:", g); + cli_print(cli, "\tNb Session:\t\t%d", grpsession[g].nbsession); + cli_print(cli, "\tNb Routes:\t\t%d", grpsession[g].nbroutesgrp); + cli_print(cli, "\ttime_changed:\t\t%d\n", grpsession[g].time_changed); + + for (i = 0; i < grpsession[g].nbsession; i++) + { + if ((s = grpsession[g].sesslist[i].sid)) + { + cli_print(cli, "\tSession:\t%d\tTx Rate:%d Kbps\t\t\tweight:\t%d", + s, + grpsession[g].sesslist[i].tx_rate/(1024/8), + grpsession[g].sesslist[i].weight); + } + } + + for (i = 0; i < grpsession[g].nbroutesgrp; i++) + { + if (grpsession[g].route[i].ip != 0) + { + cli_print(cli, "\tRoute:\t%s/%d", fmtaddr(htonl(grpsession[g].route[i].ip), 0), grpsession[g].route[i].prefixlen); + } + } + } + return CLI_OK; + } + + // Show Summary + cli_print(cli, "%5s %7s %9s %12s", + "GID", + "Nb Sess", + "Nb Routes", + "Time changed"); + + for (g = gnextgrpid; g != 0; g = grpsession[g].prev) + { + cli_print(cli, "%5d %7d %9d %12d", + g, + grpsession[g].nbsession, + grpsession[g].nbroutesgrp, + grpsession[g].time_changed); + } + + return CLI_OK; +} + static int cmd_setforward(struct cli_def *cli, char *command, char **argv, int argc) { int ret; diff --git a/cluster.c b/cluster.c index 651307a..5b6f208 100644 --- a/cluster.c +++ b/cluster.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "l2tpns.h" #include "cluster.h" @@ -710,6 +711,7 @@ void cluster_check_master(void) increment_counter(&session[i].cout, &session[i].cout_wrap, sess_local[i].cout); session[i].cin_delta += sess_local[i].cin; session[i].cout_delta += sess_local[i].cout; + session[i].coutgrp_delta += sess_local[i].cout; session[i].pin += sess_local[i].pin; session[i].pout += sess_local[i].pout; @@ -1264,6 +1266,7 @@ static int cluster_handle_bytes(uint8_t *data, int size) session[b->sid].cin_delta += b->cin; session[b->sid].cout_delta += b->cout; + session[b->sid].coutgrp_delta += b->cout; if (b->cin) session[b->sid].last_packet = session[b->sid].last_data = time_now; diff --git a/control.c b/control.c index 3d38b22..092ab8e 100644 --- a/control.c +++ b/control.c @@ -1,6 +1,9 @@ // L2TPNS: control #include +#include +#include + #include "l2tpns.h" #include "control.h" diff --git a/debian/changelog b/debian/changelog index 1d9c0b7..5c63054 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,10 +1,17 @@ -l2tpns (2.2.1-2fdn3.6) unstable; urgency=low +l2tpns (2.2.1-2sames3.7) unstable; urgency=low + * Merge from master * Fix Warning: dereferencing type-punned pointer will break strict... * Fix: Tunnel creation does not work when the length of the hostname is odd. (revert fix: Add a uint16_t control buffer type, as a union) -- Fernando Alves Tue, 26 Feb 2013 09:07:16 +0100 +l2tpns (2.2.1-2sames3.6) unstable; urgency=low + + * Sames l2tpns version. + + -- Fernando Alves Tue, 12 Feb 2013 20:20:17 +0100 + l2tpns (2.2.1-2fdn3.5) unstable; urgency=low * Update debian/changelog diff --git a/dictionary.sames b/dictionary.sames new file mode 100644 index 0000000..a8a244d --- /dev/null +++ b/dictionary.sames @@ -0,0 +1,18 @@ +# -*- text -*- +############################################################################## +# Vendor specific attributes SamesWireless +# http://www.sameswireless.fr +# +# $Id$ +# +############################################################################## + +VENDOR SAMES 64520 + +BEGIN-VENDOR SAMES + +ATTRIBUTE SAMES-Group-Framed-Route 22 string +ATTRIBUTE SAMES-Group-Session-Weight 23 string + +END-VENDOR SAMES + diff --git a/garden.c b/garden.c index 994ecb5..0b7d763 100644 --- a/garden.c +++ b/garden.c @@ -3,6 +3,9 @@ #include #include #include +#include +#include + #include "l2tpns.h" #include "plugin.h" #include "control.h" @@ -219,7 +222,7 @@ int garden_session(sessiont *s, int flag, char *newuser) /* Clean up counters */ s->pin = s->pout = 0; s->cin = s->cout = 0; - s->cin_delta = s->cout_delta = 0; + s->cin_delta = s->cout_delta = s->coutgrp_delta = 0; s->cin_wrap = s->cout_wrap = 0; snprintf(cmd, sizeof(cmd), diff --git a/grpsess.c b/grpsess.c new file mode 100644 index 0000000..bfc7d8d --- /dev/null +++ b/grpsess.c @@ -0,0 +1,589 @@ +/* + * Fernando ALVES 2013 + * Grouped session for load balancing and fail-over + * GPL licenced + */ + +#include +#include +#include +#include +#include + +#include "l2tpns.h" +#include "util.h" + +#ifdef BGP +#include "bgp.h" +#endif + +union grp_iphash { + groupidt grp; + union grp_iphash *idx; +} grp_ip_hash[256]; // Mapping from IP address to group structures. + +groupidt gnextgrpid = 0; + +// Find gruop by IP, < 1 for not found +// +// Confusingly enough, this 'ip' must be +// in _network_ order. This being the common +// case when looking it up from IP packet headers. +static groupidt grp_lookup_ipmap(in_addr_t ip) +{ + uint8_t *a = (uint8_t *) &ip; + union grp_iphash *h = grp_ip_hash; + + if (!(h = h[*a++].idx)) return 0; + if (!(h = h[*a++].idx)) return 0; + if (!(h = h[*a++].idx)) return 0; + + return h[*a].grp; +} + +// +// Take an IP address in HOST byte order and +// add it to the grouid by IP cache. +// +// (It's actually cached in network order) +// +static void grp_cache_ipmap(in_addr_t ip, groupidt g) +{ + 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; + union grp_iphash *h = grp_ip_hash; + int i; + + for (i = 0; i < 3; i++) + { + if (!(h[a[i]].idx || (h[a[i]].idx = calloc(256, sizeof(union grp_iphash))))) + return; + + h = h[a[i]].idx; + } + + h[a[3]].grp = g; + + if (g > 0) + LOG(4, 0, 0, "Caching Group:%d ip address %s\n", g, fmtaddr(nip, 0)); + else if (g == 0) + LOG(4, 0, 0, "Un-caching Group ip address %s\n", fmtaddr(nip, 0)); +} + +groupidt grp_groupbyip(in_addr_t ip) +{ + groupidt g = grp_lookup_ipmap(ip); + + if (g > 0 && g < MAXGROUPE) + return g; + + return 0; +} + +// Add a route +// +// This adds it to the routing table, advertises it +// via BGP if enabled, and stuffs it into the +// 'sessionbyip' cache. +// +// 'ip' must be in _host_ order. +// +static void grp_routeset(groupidt g, in_addr_t ip, int prefixlen, int add) +{ + struct { + struct nlmsghdr nh; + struct rtmsg rt; + char buf[32]; + } req; + int i; + in_addr_t n_ip; + + if (!prefixlen) prefixlen = 32; + + ip &= 0xffffffff << (32 - prefixlen);; // Force the ip to be the first one in the route. + + memset(&req, 0, sizeof(req)); + + if (add) + { + req.nh.nlmsg_type = RTM_NEWROUTE; + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE; + } + else + { + req.nh.nlmsg_type = RTM_DELROUTE; + req.nh.nlmsg_flags = NLM_F_REQUEST; + } + + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.rt)); + + req.rt.rtm_family = AF_INET; + req.rt.rtm_dst_len = prefixlen; + req.rt.rtm_table = RT_TABLE_MAIN; + req.rt.rtm_protocol = 42; + req.rt.rtm_scope = RT_SCOPE_LINK; + req.rt.rtm_type = RTN_UNICAST; + + netlink_addattr(&req.nh, RTA_OIF, &tunidx, sizeof(int)); + n_ip = htonl(ip); + netlink_addattr(&req.nh, RTA_DST, &n_ip, sizeof(n_ip)); + + LOG(1, 0, 0, "Route (Group) %s %s/%d\n", add ? "add" : "del", fmtaddr(htonl(ip), 0), prefixlen); + + if (netlink_send(&req.nh) < 0) + LOG(0, 0, 0, "grp_routeset() error in sending netlink message: %s\n", strerror(errno)); + +#ifdef BGP + if (add) + bgp_add_route(htonl(ip), prefixlen); + else + bgp_del_route(htonl(ip), prefixlen); +#endif /* BGP */ + + // Add/Remove the IPs to the 'groupbyip' cache. + // Note that we add the zero address in the case of + // a network route. Roll on CIDR. + + // Note that 'g == 0' implies this is the address pool. + // We still cache it here, because it will pre-fill + // the malloc'ed tree. + if (g) + { + if (!add) // Are we deleting a route? + g = 0; // Caching the session as '0' is the same as uncaching. + + for (i = ip; i < ip+(1<<(32-prefixlen)) ; ++i) + grp_cache_ipmap(i, g); + } +} + +// Set all route of a group +void grp_setgrouproute(groupidt g, int add) +{ + int i; + for (i = 0; i < grpsession[g].nbroutesgrp; i++) + { + if (grpsession[g].route[i].ip != 0) + { + grp_routeset(g, grpsession[g].route[i].ip, grpsession[g].route[i].prefixlen, add); + } + } +} + +// return group id by session +groupidt grp_groupbysession(sessionidt s) +{ + groupidt g = 0; + int i; + for (g = gnextgrpid; g != 0; g = grpsession[g].prev) + { + for (i = 0; i < grpsession[g].nbsession; i++) + { + if (grpsession[g].sesslist[i].sid == s) + { // session found in group + return g; + } + } + } + + return 0; +} + +// Remove a session to a group +// return 1 if OK +void grp_removesession(groupidt g, sessionidt s) +{ + int i; + + if (grpsession[g].nbsession <= 0) + return; + + for (i = 0; i < grpsession[g].nbsession; i++) + { + if (grpsession[g].sesslist[i].sid == s) + { // session found on group + --grpsession[g].nbsession; + if (grpsession[g].nbsession == 0) + { + // Group is empty, remove it + + // Del all routes + grp_setgrouproute(g, 0); + + if (gnextgrpid == g) + { + gnextgrpid = 0; + } + else + { + groupidt g2; + for (g2 = gnextgrpid; g2 != 0; g2 = grpsession[g2].prev) + { + if (grpsession[g2].prev == g) + { + grpsession[g2].prev = grpsession[g].prev; + break; + } + } + } + + memset(&grpsession[g], 0, sizeof(grpsession[0])); + } + else + { + // remove the session + memmove(&grpsession[g].sesslist[i], + &grpsession[g].sesslist[i+1], + (grpsession[g].nbsession - i) * sizeof(grpsession[g].sesslist[i])); + } + return; + } + } +} + +// Add a session to a group +// return 1 if OK +static int grp_addsession(groupidt g, sessionidt s, uint8_t weight) +{ + int i; + + for (i = 0; i < grpsession[g].nbsession; i++) + { + if (grpsession[g].sesslist[i].sid == s) + { // already in group + if ((!grpsession[g].sesslist[i].weight) || (weight > 1)) + grpsession[g].sesslist[i].weight = weight; // update Weight session (for load-balancing) + + return 1; + } + } + + if (i >= MAXSESSINGRP) + { + LOG(1, s, session[s].tunnel, " Too many session for Group %d\n", g); + return 0; + } + else + { // add session id to group + if (i == 0) + { + // it's the first session of the group, set to next group + grpsession[g].prev = gnextgrpid; + gnextgrpid = g; + } + grpsession[g].sesslist[i].sid = s; + grpsession[g].sesslist[i].weight = weight; + grpsession[g].nbsession++; + + return 1; + } + return 0; +} + +// Add a route to a group +// return 1 if OK +static int grp_addroute(groupidt g, sessionidt s, in_addr_t ip, int prefixlen) +{ + int i; + + for (i = 0; i < MAXROUTEINGRP; i++) + { + if ((i >= grpsession[g].nbroutesgrp)) + { + LOG(3, s, session[s].tunnel, " Radius reply Group %d contains route for %s/%d\n", + g, fmtaddr(htonl(ip), 0), prefixlen); + + grpsession[g].route[i].ip = ip; + grpsession[g].route[i].prefixlen = prefixlen; + grpsession[g].nbroutesgrp++; + return 1; + } + else if ((grpsession[g].route[i].ip == ip) && (grpsession[g].route[i].prefixlen == prefixlen)) + { + // route already defined in group + LOG(3, s, session[s].tunnel, + " Radius reply Group %d contains route for %s/%d (this already defined)\n", + g, fmtaddr(htonl(ip), 0), prefixlen); + + return 1; + } + else if (grpsession[g].route[i].ip == 0) + { + LOG(3, s, session[s].tunnel, " Radius reply Group %d contains route for %s/%d (find empty on list!!!)\n", + g, fmtaddr(htonl(ip), 0), prefixlen); + + grpsession[g].route[i].ip = ip; + grpsession[g].route[i].prefixlen = prefixlen; + return 1; + } + } + + if (i >= MAXROUTEINGRP) + { + LOG(1, s, session[s].tunnel, " Too many routes for Group %d\n", g); + } + return 0; +} + +// Process Sames vendor specific attribut radius +void grp_processvendorspecific(sessionidt s, uint8_t *pvs) +{ + uint8_t attrib = *pvs; + groupidt grpid = 0; + uint8_t *n = pvs + 2; + uint8_t *e = pvs + pvs[1]; + + if ((attrib >= 22) && (attrib <= 23)) + { + while (n < e && isdigit(*n)) + { + grpid = grpid * 10 + *n - '0'; + n++; + } + if ((grpid == 0) || (grpid >= MAXGROUPE)) + { + LOG(1, s, session[s].tunnel, " Group Attribute Id %d not allowed\n", grpid); + return; + } + else if (*n != ',') + { + LOG(1, s, session[s].tunnel, " Group Attribute Id not defined\n"); + return; + } + + if (!grp_addsession(grpid, s, 1)) + { + return; + } + + n++; + } + + //process, Sames vendor-specific 64520 + if (attrib == 22) //SAMES-Group-Framed-Route + { + in_addr_t ip = 0; + uint8_t u = 0; + uint8_t bits = 0; + + while (n < e && (isdigit(*n) || *n == '.')) + { + if (*n == '.') + { + ip = (ip << 8) + u; + u = 0; + } + else + u = u * 10 + *n - '0'; + n++; + } + ip = (ip << 8) + u; + if (*n == '/') + { + n++; + while (n < e && isdigit(*n)) + bits = bits * 10 + *n++ - '0'; + } + else if ((ip >> 24) < 128) + bits = 8; + else if ((ip >> 24) < 192) + bits = 16; + else + bits = 24; + + if (!grp_addroute(grpid, s, ip, bits)) + return; + } + else if (attrib == 23) //SAMES-Group-Session-Weight + { + uint8_t weight = 0; + + while (n < e && isdigit(*n)) + weight = weight * 10 + *n++ - '0'; + + if (!weight) + { + LOG(1, s, session[s].tunnel, " Group-Session-Weight 0 GroupId %d not allowed\n", grpid); + return; + } + if (!grp_addsession(grpid, s, weight)) + { + return; + } + } + else + { + LOG(3, s, session[s].tunnel, " Unknown vendor-specific: 64520, Attrib: %d\n", attrib); + } +} + +// Init data structures +void grp_initdata() +{ + // Set default value (10s) + config->grp_txrate_average_time = 10; + + if (!(grpsession = shared_malloc(sizeof(groupsesst) * MAXGROUPE))) + { + LOG(0, 0, 0, "Error doing malloc for grouped session: %s\n", strerror(errno)); + exit(1); + } + + memset(grpsession, 0, sizeof(grpsession[0]) * MAXGROUPE); +} + +// Update time_changed of the group +void grp_time_changed() +{ + groupidt g; + + for (g = gnextgrpid; g != 0; g = grpsession[g].prev) + { + grpsession[g].time_changed++; + } +} + +// Uncache all IP of a session +static void grp_uncache_ipsession(groupidt g, sessionidt s) +{ + int i; + uint8_t *a; + in_addr_t ip; + in_addr_t n_ip, j; + int prefixlen; + union iphash *h; + + for (i = 0; i < grpsession[g].nbroutesgrp; i++) + { + if (grpsession[g].route[i].ip != 0) + { + prefixlen = grpsession[g].route[i].prefixlen; + ip = grpsession[g].route[i].ip & (0xffffffff << (32 - prefixlen)); // Force the ip to be the first one in the route. + + for (j = ip; j < ip+(1<<(32-prefixlen)) ; ++j) + { + n_ip = htonl(j); // To network order + a = (uint8_t *) &n_ip; + h = ip_hash; + + if (!(h = h[*a++].idx)) continue; + if (!(h = h[*a++].idx)) continue; + if (!(h = h[*a++].idx)) continue; + + if (s == h[*a].sess) + { + h[*a].sess = 0; + //LOG(3, s, session[s].tunnel, "UnCaching ip address %s\n", fmtaddr(n_ip, 0)); + } + } + } + } +} + +// return the next session can be used on the group +sessionidt grp_getnextsession(groupidt g, in_addr_t ip) +{ + sessionidt s = 0, s2 = 0, s3 = 0; + int i; + uint32_t ltime_changed = 0, mintxrate = 0xFFFFFFFF, maxtxrate = 0; + uint32_t txrate; + + if (g >= MAXGROUPE) + return 0; + + if (grpsession[g].time_changed >= config->grp_txrate_average_time) + { + // recalculation txrate + ltime_changed = grpsession[g].time_changed; + grpsession[g].time_changed = 0; + for (i = 0; i < grpsession[g].nbsession; i++) + { + if ((s2 = grpsession[g].sesslist[i].sid)) + { + grpsession[g].sesslist[i].tx_rate = session[s2].coutgrp_delta/ltime_changed; + session[s2].coutgrp_delta = 0; + + txrate = grpsession[g].sesslist[i].tx_rate/grpsession[g].sesslist[i].weight; + if (txrate < mintxrate) + { + if ( session[s2].ppp.phase > Establish && + (time_now - session[s2].last_packet <= (config->echo_timeout + 1)) ) + { + grpsession[g].smin = s2; + mintxrate = txrate; + } + } + + if (txrate > maxtxrate) + { + if ( session[s2].ppp.phase > Establish && + (time_now - session[s2].last_packet <= (config->echo_timeout + 1)) ) + { + grpsession[g].smax = s2; + maxtxrate = txrate; + } + } + } + } + } + + if ((s = sessionbyip(ip))) + { + if (s == grpsession[g].smax) + { + s = grpsession[g].smin; + grpsession[g].smax = 0; + } + else if ( (session[s].ppp.phase > Establish) && + (time_now - session[s].last_packet <= (config->echo_timeout + 1)) ) + { + return s; + } + else + { + s = 0; + } + } + + if (!s) + { + // random between 0 and nbsession-1 + uint indexsess = (rand() % grpsession[g].nbsession); + + if (indexsess >= grpsession[g].nbsession) + indexsess = 0; //Sanity checks. + + s2 = grpsession[g].sesslist[indexsess].sid; + if (s2 && + (session[s2].ppp.phase > Establish) && + (time_now - session[s2].last_packet <= (config->echo_timeout + 1))) + { + s = s2; + } + else + { + for (i = 0; i < grpsession[g].nbsession; i++) + { + if ((s2 = grpsession[g].sesslist[i].sid)) + { + s3 = s2; + + if ( session[s2].ppp.phase > Establish && + (time_now - session[s2].last_packet <= (config->echo_timeout + 1)) ) + { + s = s2; + break; + } + } + } + } + } + + if (!s) + s = s3; + + if (s) + cache_ipmap(ntohl(ip), s); + + return s; +} diff --git a/icmp.c b/icmp.c index fa947b7..5f05127 100644 --- a/icmp.c +++ b/icmp.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "l2tpns.h" #include "pppoe.h" diff --git a/l2tplac.c b/l2tplac.c index 0d11b45..d41a26d 100644 --- a/l2tplac.c +++ b/l2tplac.c @@ -7,6 +7,8 @@ #include #include +#include +#include #include "md5.h" #include "l2tpns.h" @@ -526,6 +528,7 @@ int lac_session_forward(uint8_t *buf, int len, sessionidt sess, uint16_t proto, // Update STAT OUT increment_counter(&session[s].cout, &session[s].cout_wrap, len); // byte count session[s].cout_delta += len; + session[s].coutgrp_delta += len; session[s].pout++; sess_local[s].cout += len; sess_local[s].pout++; diff --git a/l2tpns.c b/l2tpns.c index 4545d6c..08c8bed 100644 --- 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; @@ -185,6 +182,7 @@ 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), { NULL, 0, 0, 0 } }; @@ -213,6 +211,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. @@ -223,9 +222,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); @@ -254,8 +250,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 @@ -614,7 +611,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; @@ -650,7 +647,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; @@ -1028,7 +1025,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; @@ -1397,6 +1394,7 @@ static void update_session_out_stat(sessionidt s, sessiont *sp, int len) { increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count sp->cout_delta += len; + sp->coutgrp_delta += len; sp->pout++; sp->last_data = time_now; @@ -1410,6 +1408,7 @@ 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; @@ -1446,7 +1445,31 @@ void processipout(uint8_t *buf, int len) } ip = *(uint32_t *)(buf + 16); - if (!(s = sessionbyip(ip))) + if ((g = grp_groupbyip(ip))) + { + s = grp_getnextsession(g, ip); + 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. @@ -1781,6 +1804,7 @@ static void processipv6out(uint8_t * buf, int len) increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count sp->cout_delta += len; + sp->coutgrp_delta += len; sp->pout++; udp_tx += len; @@ -1830,6 +1854,7 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len) increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count sp->cout_delta += len; + sp->coutgrp_delta += len; sp->pout++; udp_tx += len; @@ -2077,7 +2102,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++) @@ -2277,6 +2302,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 @@ -2305,6 +2332,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); } @@ -3696,7 +3729,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]; @@ -4633,6 +4666,8 @@ static void initdata(int optdebug, char *optconfig) #endif /* BGP */ lac_initremotelnsdata(); + + grp_initdata(); } static int assign_ip_address(sessionidt s) @@ -5617,6 +5652,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++) @@ -5639,6 +5675,11 @@ int sessionsetup(sessionidt s, tunnelidt t) } else cache_ipmap(session[s].ip, s); + + if ((g = grp_groupbysession(s))) + { + grp_setgrouproute(g, 1); + } } sess_local[s].lcp_authtype = 0; // RADIUS authentication complete diff --git a/l2tpns.h b/l2tpns.h index e962256..4fe9a5c 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -24,6 +24,10 @@ #define MAXADDRESS 20 // Maximum length for the Endpoint Discrminiator address #define MAXSESSION 60000 // could be up to 65535 #define MAXTBFS 6000 // Maximum token bucket filters. Might need up to 2 * session. +#define MAXSESSINGRP 12 // Maximum number of member links in grouped session +#define MAXGRPINSESS 12 // Maximum number of member links in session group +#define MAXGROUPE 300 // could be up to 65535, Maximum number of grouped session +#define MAXROUTEINGRP 15 // max static routes per group // Tunnel Id reserved for pppoe #define TUNNEL_ID_PPPOE 1 @@ -214,6 +218,7 @@ enum { typedef uint16_t sessionidt; typedef uint16_t bundleidt; typedef uint16_t tunnelidt; +typedef uint16_t groupidt; typedef uint32_t clockt; typedef uint8_t hasht[16]; @@ -331,10 +336,37 @@ typedef struct struct in6_addr ipv6route; // Static IPv6 route sessionidt forwardtosession; // LNS id_session to forward uint8_t src_hwaddr[ETH_ALEN]; // MAC addr source (for pppoe sessions 6 bytes) - char reserved[4]; // Space to expand structure without changing HB_VERSION + uint32_t coutgrp_delta; } sessiont; +typedef struct +{ + uint32_t tx_rate; + sessionidt sid; + uint8_t weight; +} +groupsessionidt; + +typedef struct +{ + uint32_t time_changed; + groupidt prev; + sessionidt smax; + sessionidt smin; + groupsessionidt sesslist[MAXSESSINGRP]; + routet route[MAXROUTEINGRP]; // static routes + uint8_t nbroutesgrp; + uint8_t nbsession; +} +groupsesst; + +union iphash +{ + sessionidt sess; + union iphash *idx; +}; // Mapping from IP address to session structures. + typedef struct { int state; // current state (bundlestate enum) @@ -781,6 +813,7 @@ typedef struct in_addr_t bind_n_address[MAX_BINDADDR]; in_addr_t iftun_n_address[MAX_BINDADDR]; char bind_multi_address[256]; + int grp_txrate_average_time; // caculation txrate average time (default 10s) } configt; enum config_typet { INT, STRING, UNSIGNED_LONG, SHORT, BOOL, IPv4, IPv6 }; @@ -944,12 +977,26 @@ int ip_filter(uint8_t *buf, int len, uint8_t filter); int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc); int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc); int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc); +void netlink_addattr(struct nlmsghdr *nh, int type, const void *data, int alen); +ssize_t netlink_send(struct nlmsghdr *nh); +void cache_ipmap(in_addr_t ip, sessionidt s); + tunnelidt lac_new_tunnel(); void lac_tunnelclear(tunnelidt t); void lac_send_SCCRQ(tunnelidt t, uint8_t * auth, unsigned int auth_len); void lac_send_ICRQ(tunnelidt t, sessionidt s); void lac_tunnelshutdown(tunnelidt t, char *reason, int result, int error, char *msg); +// grpsess.c +sessionidt grp_getnextsession(groupidt g, in_addr_t ip); +void grp_initdata(void); +void grp_processvendorspecific(sessionidt s, uint8_t *pvs); +groupidt grp_groupbysession(sessionidt s); +groupidt grp_groupbyip(in_addr_t ip); +void grp_setgrouproute(groupidt g, int add); +void grp_time_changed(void); +void grp_removesession(groupidt g, sessionidt s); + #undef LOG #undef LOG_HEX #define LOG(D, s, t, f, ...) ({ if (D <= config->debug) _log(D, s, t, f, ## __VA_ARGS__); }) @@ -984,6 +1031,8 @@ extern bundlet *bundle; extern sessiont *session; extern sessionlocalt *sess_local; extern ippoolt *ip_address_pool; +extern groupsesst *grpsession; +groupidt gnextgrpid; #define sessionfree (session[0].next) @@ -996,6 +1045,8 @@ extern struct Tstats *_statistics; extern in_addr_t my_address; extern int clifd; extern int epollfd; +extern int tunidx; // ifr_ifindex of tun device +extern union iphash ip_hash[256]; struct event_data { enum { diff --git a/nsctl.c b/nsctl.c index 59ad0c6..98785cd 100644 --- a/nsctl.c +++ b/nsctl.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "l2tpns.h" #include "control.h" diff --git a/ppp.c b/ppp.c index 960a269..c8e128e 100644 --- a/ppp.c +++ b/ppp.c @@ -5,6 +5,9 @@ #include #include #include +#include +#include + #include "l2tpns.h" #include "constants.h" #include "plugin.h" diff --git a/pppoe.c b/pppoe.c index 01cc153..f804b0e 100644 --- a/pppoe.c +++ b/pppoe.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "l2tpns.h" #include "cluster.h" @@ -955,6 +956,7 @@ static void pppoe_forwardto_session_rmlns(uint8_t *pack, int size, sessionidt se // Update STAT OUT increment_counter(&session[s].cout, &session[s].cout_wrap, ll2tp); // byte count session[s].cout_delta += ll2tp; + session[s].coutgrp_delta += ll2tp; session[s].pout++; sess_local[s].cout += ll2tp; sess_local[s].pout++; @@ -1026,6 +1028,7 @@ void pppoe_forwardto_session_pppoe(uint8_t *pack, int size, sessionidt sess, uin // Update STAT OUT increment_counter(&session[s].cout, &session[s].cout_wrap, lpppoe); // byte count session[s].cout_delta += lpppoe; + session[s].coutgrp_delta += lpppoe; session[s].pout++; sess_local[s].cout += lpppoe; sess_local[s].pout++; diff --git a/radius.c b/radius.c index 8481561..266f100 100644 --- a/radius.c +++ b/radius.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "md5.h" #include "constants.h" @@ -663,11 +664,19 @@ void processrad(uint8_t *buf, int len, char socket_index) else if (vendor == 529 && attrib >= 135 && attrib <= 136) // Ascend { // handle old-format ascend DNS attributes below - p += 6; + p += 6; + } + else if (vendor == 64520) // Sames + { + //Sames vendor-specific 64520 + uint8_t *pvs = p + 6; // pvs set to begin to attribute + LOG(3, s, session[s].tunnel, " Sames vendor-specific: %d, Attrib: %d, lenght: %d\n", vendor, attrib, attrib_length); + grp_processvendorspecific(s, pvs); + continue; } else { - LOG(3, s, session[s].tunnel, " Unknown vendor-specific\n"); + LOG(3, s, session[s].tunnel, " Unknown vendor-specific: %d, Attrib: %d\n", vendor, attrib); continue; } } diff --git a/sessionctl.c b/sessionctl.c index 805b794..a8c11bd 100644 --- a/sessionctl.c +++ b/sessionctl.c @@ -1,4 +1,7 @@ #include +#include +#include + #include "l2tpns.h" #include "plugin.h" #include "control.h" diff --git a/setrxspeed.c b/setrxspeed.c index 52fcf4a..f987378 100644 --- a/setrxspeed.c +++ b/setrxspeed.c @@ -1,4 +1,7 @@ #include +#include +#include + #include "l2tpns.h" #include "plugin.h" diff --git a/snoopctl.c b/snoopctl.c index d538a23..97d4bd0 100644 --- a/snoopctl.c +++ b/snoopctl.c @@ -1,4 +1,7 @@ #include +#include +#include + #include "l2tpns.h" #include "plugin.h" #include "control.h" diff --git a/stripdomain.c b/stripdomain.c index 877617e..c251dba 100644 --- a/stripdomain.c +++ b/stripdomain.c @@ -1,4 +1,7 @@ #include +#include +#include + #include "l2tpns.h" #include "plugin.h" diff --git a/tbf.c b/tbf.c index c6273ce..8b488d6 100644 --- a/tbf.c +++ b/tbf.c @@ -1,6 +1,9 @@ // L2TPNS: token bucket filters #include +#include +#include + #include "l2tpns.h" #include "util.h" #include "tbf.h" diff --git a/throttlectl.c b/throttlectl.c index adcff27..bcc0478 100644 --- a/throttlectl.c +++ b/throttlectl.c @@ -1,4 +1,7 @@ #include +#include +#include + #include "l2tpns.h" #include "plugin.h" #include "control.h" diff --git a/util.c b/util.c index bbf94ba..e412fc1 100644 --- a/util.c +++ b/util.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "l2tpns.h" #ifdef BGP