Session group update rate calculation
[l2tpns.git] / l2tpns.c
index 9dbde7a..52872a5 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -56,6 +56,7 @@
 #ifdef LAC
 #include "l2tplac.h"
 #endif
+#include "pppoe.h"
 
 #ifdef LAC
 char * Vendor_name = "Linux L2TPNS";
@@ -80,7 +81,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
@@ -179,12 +180,20 @@ config_descriptt config_values[] = {
        CONFIG("echo_timeout", echo_timeout, INT),
        CONFIG("idle_echo_timeout", idle_echo_timeout, INT),
        CONFIG("iftun_address", iftun_address, IPv4),
+       CONFIG("tundevicename", tundevicename, STRING),
 #ifdef LAC
        CONFIG("disable_lac_func", disable_lac_func, BOOL),
+       CONFIG("auth_tunnel_change_addr_src", auth_tunnel_change_addr_src, BOOL),
        CONFIG("bind_address_remotelns", bind_address_remotelns, IPv4),
        CONFIG("bind_portremotelns", bind_portremotelns, SHORT),
 #endif
-       { NULL, 0, 0, 0 },
+       CONFIG("pppoe_if_to_bind", pppoe_if_to_bind, STRING),
+       CONFIG("pppoe_service_name", pppoe_service_name, STRING),
+       CONFIG("pppoe_ac_name", pppoe_ac_name, STRING),
+       CONFIG("disable_sending_hello", disable_sending_hello, BOOL),
+       CONFIG("disable_no_spoof", disable_no_spoof, BOOL),
+       CONFIG("grp_txrate_average_time", grp_txrate_average_time, INT),
+       { NULL, 0, 0, 0 }
 };
 
 static char *plugin_functions[] = {
@@ -212,6 +221,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.
@@ -222,9 +232,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);
@@ -253,12 +260,14 @@ 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
-       time_now_ms = (t.tv_sec * 1000) + (t.tv_usec/1000);
+       // TODO FOR MLPPP DEV
+       //time_now_ms = (t.tv_sec * 1000) + (t.tv_usec/1000);
 
        return (t.tv_sec - basetime) * 10 + t.tv_usec / 100000 + 1;
 }
@@ -612,7 +621,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;
@@ -648,7 +657,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;
@@ -689,15 +698,19 @@ static void inittun(void)
                int flags = fcntl(tunfd, F_GETFL, 0);
                fcntl(tunfd, F_SETFL, flags | O_NONBLOCK);
        }
+
+   if (*config->tundevicename)
+         strncpy(ifr.ifr_name, config->tundevicename, IFNAMSIZ);
+
        if (ioctl(tunfd, TUNSETIFF, (void *) &ifr) < 0)
        {
                LOG(0, 0, 0, "Can't set tun interface: %s\n", strerror(errno));
                exit(1);
        }
-       assert(strlen(ifr.ifr_name) < sizeof(config->tundevice) - 1);
-       strncpy(config->tundevice, ifr.ifr_name, sizeof(config->tundevice));
+       assert(strlen(ifr.ifr_name) < sizeof(config->tundevicename) - 1);
+       strncpy(config->tundevicename, ifr.ifr_name, sizeof(config->tundevicename));
 
-       tunidx = if_nametoindex(config->tundevice);
+       tunidx = if_nametoindex(config->tundevicename);
        if (tunidx == 0)
        {
                LOG(0, 0, 0, "Can't get tun interface index\n");
@@ -990,7 +1003,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;
@@ -1188,6 +1201,12 @@ void tunnelsend(uint8_t * buf, uint16_t l, tunnelidt t)
                return;
        }
 
+       if (t == TUNNEL_ID_PPPOE)
+       {
+               pppoe_sess_send(buf, l, t);
+               return;
+       }
+
        if (!tunnel[t].ip)
        {
                LOG(1, 0, t, "Error sending data out tunnel: no remote endpoint (tunnel not set up)\n");
@@ -1356,6 +1375,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;
 
@@ -1369,6 +1389,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;
@@ -1405,7 +1426,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.
@@ -1607,17 +1652,15 @@ void processipout(uint8_t *buf, int len)
                else
                {
                        // Send it as one frame (NO MPPP Frame)
-                       uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, 0, 0);
-                       if (!p) return;
-                       tunnelsend(fragbuf, len + (p-fragbuf), t); // send it...
+                       uint8_t *p = opt_makeppp(buf, len, s, t, PPPIP, 0, 0, 0);
+                       tunnelsend(p, len + (buf-p), t); // send it...
                        update_session_out_stat(s, sp, len);
                }
        }
        else
        {
-               uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, 0, 0);
-               if (!p) return;
-               tunnelsend(fragbuf, len + (p-fragbuf), t); // send it...
+               uint8_t *p = opt_makeppp(buf, len, s, t, PPPIP, 0, 0, 0);
+               tunnelsend(p, len + (buf-p), t); // send it...
                update_session_out_stat(s, sp, len);
        }
 
@@ -1742,6 +1785,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;
 
@@ -1791,6 +1835,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;
 
@@ -2009,7 +2054,6 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e
                struct param_kill_session data = { &tunnel[session[s].tunnel], &session[s] };
                LOG(2, s, session[s].tunnel, "Shutting down session %u: %s\n", s, reason);
                run_plugins(PLUGIN_KILL_SESSION, &data);
-               session[s].die = TIME + 150; // Clean up in 15 seconds
        }
 
        if (session[s].ip && !walled_garden && !session[s].die)
@@ -2034,8 +2078,11 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e
                        memcpy(&shut_acct[shut_acct_n++], &session[s], sizeof(session[s]));
        }
 
+       if (!session[s].die)
+               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++)
@@ -2127,20 +2174,28 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e
                throttle_session(s, 0, 0);
 
        if (cdn_result)
-       {                            // Send CDN
-               controlt *c = controlnew(14); // sending CDN
-               if (cdn_error)
+       {
+               if (session[s].tunnel == TUNNEL_ID_PPPOE)
                {
-                       uint16_t buf[2];
-                       buf[0] = htons(cdn_result);
-                       buf[1] = htons(cdn_error);
-                       controlb(c, 1, (uint8_t *)buf, 4, 1);
+                       pppoe_shutdown_session(s);
                }
                else
-                       control16(c, 1, cdn_result, 1);
+               {
+                       // Send CDN
+                       controlt *c = controlnew(14); // sending CDN
+                       if (cdn_error)
+                       {
+                               uint16_t buf[2];
+                               buf[0] = htons(cdn_result);
+                               buf[1] = htons(cdn_error);
+                               controlb(c, 1, (uint8_t *)buf, 4, 1);
+                       }
+                       else
+                               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
+                       control16(c, 14, s, 1);   // assigned session (our end)
+                       controladd(c, session[s].far, session[s].tunnel); // send the message
+               }
        }
 
        // update filter refcounts
@@ -2227,6 +2282,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
@@ -2257,6 +2314,12 @@ void sessionkill(sessionidt s, char *reason)
 #endif
 
        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);
 }
@@ -2407,6 +2470,12 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                STAT(tunnel_rx_errors);
                return;
        }
+       if (t == TUNNEL_ID_PPPOE)
+       {
+               LOG(1, s, t, "Received UDP packet with tunnel ID reserved for pppoe\n");
+               STAT(tunnel_rx_errors);
+               return;
+       }
        if (*buf & 0x08)
        {                          // ns/nr
                ns = ntohs(*(uint16_t *) p);
@@ -3223,10 +3292,22 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                if (session[s].forwardtosession)
                {
                        LOG(5, s, t, "Forwarding data session to session %u\n", session[s].forwardtosession);
-                       // Forward to LAC or Remote LNS session
-                       lac_session_forward(buf, len, s, proto);
+                       // Forward to LAC/BAS or Remote LNS session
+                       lac_session_forward(buf, len, s, proto, addr->sin_addr.s_addr, addr->sin_port);
                        return;
                }
+               else if (config->auth_tunnel_change_addr_src)
+               {
+                       if (tunnel[t].ip != ntohl(addr->sin_addr.s_addr) &&
+                               tunnel[t].port == ntohs(addr->sin_port))
+                       {
+                               // The remotes BAS are a clustered l2tpns server and the source IP has changed
+                               LOG(5, s, t, "The tunnel IP source (%s) has changed by new IP (%s)\n",
+                                       fmtaddr(htonl(tunnel[t].ip), 0), fmtaddr(addr->sin_addr.s_addr, 0));
+
+                               tunnel[t].ip = ntohl(addr->sin_addr.s_addr);
+                       }
+               }
 #endif /* LAC */
 
                if (s && !session[s].opened)    // Is something wrong??
@@ -3422,6 +3503,9 @@ static void regular_cleanups(double period)
                if (t > config->cluster_highest_tunnelid)
                        t = 1;
 
+               if (t == TUNNEL_ID_PPPOE)
+                       continue;
+
                // check for expired tunnels
                if (tunnel[t].die && tunnel[t].die <= TIME)
                {
@@ -3454,10 +3538,13 @@ static void regular_cleanups(double period)
                // Send hello
                if (tunnel[t].state == TUNNELOPEN && !tunnel[t].controlc && (time_now - tunnel[t].lastrec) > 60)
                {
-                       controlt *c = controlnew(6); // sending HELLO
-                       controladd(c, 0, t); // send the message
-                       LOG(3, 0, t, "Sending HELLO message\n");
-                       t_actions++;
+                       if (!config->disable_sending_hello)
+                       {
+                               controlt *c = controlnew(6); // sending HELLO
+                               controladd(c, 0, t); // send the message
+                               LOG(3, 0, t, "Sending HELLO message\n");
+                               t_actions++;
+                       }
                }
 
                // Check for tunnel changes requested from the CLI
@@ -3631,7 +3718,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];
 
@@ -3645,7 +3732,8 @@ static void regular_cleanups(double period)
 
                        LOG(4, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n",
                                        (int)(time_now - session[s].last_packet));
-                       tunnelsend(b, 24, session[s].tunnel); // send it
+
+                       tunnelsend(b, (q - b) + 8, session[s].tunnel); // send it
                        sess_local[s].last_echo = time_now;
                        s_actions++;
                }
@@ -3897,11 +3985,11 @@ static int still_busy(void)
 #endif
 
 #ifdef LAC
-// the base set of fds polled: cli, cluster, tun, udp, control, dae, netlink, udplac
-#define BASE_FDS       8
+// the base set of fds polled: cli, cluster, tun, udp, control, dae, netlink, udplac, pppoedisc, pppoesess
+#define BASE_FDS       10
 #else
-// the base set of fds polled: cli, cluster, tun, udp, control, dae, netlink
-#define BASE_FDS       7
+// the base set of fds polled: cli, cluster, tun, udp, control, dae, netlink, pppoedisc, pppoesess
+#define BASE_FDS       9
 #endif
 
 // additional polled fds
@@ -3916,8 +4004,9 @@ static void mainloop(void)
 {
        int i;
        uint8_t buf[65536];
-       uint8_t *p = buf + 8; // for the hearder of the forwarded MPPP packet (see C_MPPP_FORWARD)
-       int size_bufp = sizeof(buf) - 8;
+       uint8_t *p = buf + 32; // for the hearder of the forwarded MPPP packet (see C_MPPP_FORWARD)
+                                               // and the forwarded pppoe session
+       int size_bufp = sizeof(buf) - 32;
        clockt next_cluster_ping = 0;   // send initial ping immediately
        struct epoll_event events[BASE_FDS + RADIUS_FDS + EXTRA_FDS];
        int maxevent = sizeof(events)/sizeof(*events);
@@ -3929,11 +4018,11 @@ static void mainloop(void)
        }
 
 #ifdef LAC
-       LOG(4, 0, 0, "Beginning of main loop.  clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d, nlfd=%d , udplacfd=%d\n",
-               clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd, nlfd, udplacfd);
+       LOG(4, 0, 0, "Beginning of main loop.  clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d, nlfd=%d , udplacfd=%d, pppoefd=%d, pppoesessfd=%d\n",
+               clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd, nlfd, udplacfd, pppoediscfd, pppoesessfd);
 #else
-       LOG(4, 0, 0, "Beginning of main loop.  clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d, nlfd=%d\n",
-               clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd, nlfd);
+       LOG(4, 0, 0, "Beginning of main loop.  clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d, nlfd=%d, pppoefd=%d, pppoesessfd=%d\n",
+               clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd, nlfd, pppoediscfd, pppoesessfd);
 #endif
 
        /* setup our fds to poll for input */
@@ -3980,6 +4069,14 @@ static void mainloop(void)
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, udplacfd, &e);
 #endif
+
+               d[i].type = FD_TYPE_PPPOEDISC;
+               e.data.ptr = &d[i++];
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, pppoediscfd, &e);
+
+               d[i].type = FD_TYPE_PPPOESESS;
+               e.data.ptr = &d[i++];
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, pppoesessfd, &e);
        }
 
 #ifdef BGP
@@ -4046,6 +4143,8 @@ static void mainloop(void)
                        int udplac_ready = 0;
                        int udplac_pkts = 0;
 #endif
+                       int pppoesess_ready = 0;
+                       int pppoesess_pkts = 0;
                        int tun_ready = 0;
                        int cluster_ready = 0;
                        int udp_pkts = 0;
@@ -4086,28 +4185,36 @@ static void mainloop(void)
 #ifdef LAC
                                case FD_TYPE_UDPLAC:    udplac_ready++; break;
 #endif
+                               case FD_TYPE_PPPOESESS: pppoesess_ready++; break;
+
+                               case FD_TYPE_PPPOEDISC: // pppoe discovery
+                                       s = read(pppoediscfd, p, size_bufp);
+                                       if (s > 0) process_pppoe_disc(p, s);
+                                       n--;
+                                       break;
+
                                case FD_TYPE_CONTROL: // nsctl commands
                                        alen = sizeof(addr);
-                                       s = recvfromto(controlfd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local);
-                                       if (s > 0) processcontrol(buf, s, &addr, alen, &local);
+                                       s = recvfromto(controlfd, p, size_bufp, MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local);
+                                       if (s > 0) processcontrol(p, s, &addr, alen, &local);
                                        n--;
                                        break;
 
                                case FD_TYPE_DAE: // DAE requests
                                        alen = sizeof(addr);
-                                       s = recvfromto(daefd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local);
-                                       if (s > 0) processdae(buf, s, &addr, alen, &local);
+                                       s = recvfromto(daefd, p, size_bufp, MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local);
+                                       if (s > 0) processdae(p, s, &addr, alen, &local);
                                        n--;
                                        break;
 
                                case FD_TYPE_RADIUS: // RADIUS response
                                        alen = sizeof(addr);
-                                       s = recvfrom(radfds[d->index], buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen);
+                                       s = recvfrom(radfds[d->index], p, size_bufp, MSG_WAITALL, (struct sockaddr *) &addr, &alen);
                                        if (s >= 0 && config->cluster_iam_master)
                                        {
                                                if (addr.sin_addr.s_addr == config->radiusserver[0] ||
                                                    addr.sin_addr.s_addr == config->radiusserver[1])
-                                                       processrad(buf, s, d->index);
+                                                       processrad(p, s, d->index);
                                                else
                                                        LOG(3, 0, 0, "Dropping RADIUS packet from unknown source %s\n",
                                                                fmtaddr(addr.sin_addr.s_addr, 0));
@@ -4125,8 +4232,8 @@ static void mainloop(void)
 
                                case FD_TYPE_NETLINK:
                                {
-                                       struct nlmsghdr *nh = (struct nlmsghdr *)buf;
-                                       s = netlink_recv(buf, sizeof(buf));
+                                       struct nlmsghdr *nh = (struct nlmsghdr *)p;
+                                       s = netlink_recv(p, size_bufp);
                                        if (nh->nlmsg_type == NLMSG_ERROR)
                                        {
                                                struct nlmsgerr *errmsg = NLMSG_DATA(nh);
@@ -4164,9 +4271,9 @@ static void mainloop(void)
                                if (udp_ready)
                                {
                                        alen = sizeof(addr);
-                                       if ((s = recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0)
+                                       if ((s = recvfrom(udpfd, p, size_bufp, 0, (void *) &addr, &alen)) > 0)
                                        {
-                                               processudp(buf, s, &addr);
+                                               processudp(p, s, &addr);
                                                udp_pkts++;
                                        }
                                        else
@@ -4180,10 +4287,10 @@ static void mainloop(void)
                                if (udplac_ready)
                                {
                                        alen = sizeof(addr);
-                                       if ((s = recvfrom(udplacfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0)
+                                       if ((s = recvfrom(udplacfd, p, size_bufp, 0, (void *) &addr, &alen)) > 0)
                                        {
                                                if (!config->disable_lac_func)
-                                                       processudp(buf, s, &addr);
+                                                       processudp(p, s, &addr);
 
                                                udplac_pkts++;
                                        }
@@ -4209,13 +4316,28 @@ static void mainloop(void)
                                        }
                                }
 
+                               // pppoe session
+                               if (pppoesess_ready)
+                               {
+                                       if ((s = read(pppoesessfd, p, size_bufp)) > 0)
+                                       {
+                                               process_pppoe_sess(p, s);
+                                               pppoesess_pkts++;
+                                       }
+                                       else
+                                       {
+                                               pppoesess_ready = 0;
+                                               n--;
+                                       }
+                               }
+
                                // cluster
                                if (cluster_ready)
                                {
                                        alen = sizeof(addr);
-                                       if ((s = recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)) > 0)
+                                       if ((s = recvfrom(cluster_sockfd, p, size_bufp, MSG_WAITALL, (void *) &addr, &alen)) > 0)
                                        {
-                                               processcluster(buf, s, addr.sin_addr.s_addr);
+                                               processcluster(p, s, addr.sin_addr.s_addr);
                                                cluster_pkts++;
                                        }
                                        else
@@ -4232,11 +4354,11 @@ static void mainloop(void)
                        if (c >= config->multi_read_count)
                        {
 #ifdef LAC
-                               LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun %d cluster and %d rmlns packets\n",
-                                       config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts, udplac_pkts);
+                               LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun %d cluster %d rmlns and %d pppoe packets\n",
+                                       config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts, udplac_pkts, pppoesess_pkts);
 #else
-                               LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun and %d cluster packets\n",
-                                       config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts);
+                               LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun %d cluster and %d pppoe packets\n",
+                                       config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts, pppoesess_pkts);
 #endif
                                STAT(multi_read_exceeded);
                                more++;
@@ -4575,6 +4697,8 @@ static void initdata(int optdebug, char *optconfig)
 #ifdef LAC
        lac_initremotelnsdata();
 #endif
+
+       grp_initdata();
 }
 
 static int assign_ip_address(sessionidt s)
@@ -5038,8 +5162,13 @@ int main(int argc, char *argv[])
                exit(1);
 
        inittun();
-       LOG(1, 0, 0, "Set up on interface %s\n", config->tundevice);
+       LOG(1, 0, 0, "Set up on interface %s\n", config->tundevicename);
 
+       if (*config->pppoe_if_to_bind)
+       {
+               init_pppoe();
+               LOG(1, 0, 0, "Set up on pppoe interface %s\n", config->pppoe_if_to_bind);
+       }
        initudp();
        initrad();
        initippool();
@@ -5281,6 +5410,9 @@ static void update_config()
        if(!config->iftun_address)
                config->iftun_address = config->bind_address;
 
+       if (!*config->pppoe_ac_name)
+               strncpy(config->pppoe_ac_name, DEFAULT_PPPOE_AC_NAME, sizeof(config->pppoe_ac_name) - 1);
+
        // re-initialise the random number source
        initrandom(config->random_device);
 
@@ -5459,8 +5591,7 @@ int sessionsetup(sessionidt s, tunnelidt t)
 
                        if (ip == session[i].ip)
                        {
-                               sessionkill(i, "Duplicate IP address");
-                               cluster_listinvert_session(s, i);
+                               sessionshutdown(i, "Duplicate IP address", CDN_ADMIN_DISC, TERM_ADMIN_RESET);  // close radius/routes, etc.
                                continue;
                        }
 
@@ -5481,7 +5612,7 @@ int sessionsetup(sessionidt s, tunnelidt t)
 
                        // Drop the new session in case of duplicate sessionss, not the old one.
                        if (!strcasecmp(user, session[i].user))
-                               sessionkill(i, "Duplicate session for users");
+                               sessionshutdown(i, "Duplicate session for users", CDN_ADMIN_DISC, TERM_ADMIN_RESET);  // close radius/routes, etc.
                }
        }
 
@@ -5489,6 +5620,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++)
@@ -5511,6 +5643,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
@@ -6017,7 +6154,7 @@ static tunnelidt new_tunnel()
        tunnelidt i;
        for (i = 1; i < MAXTUNNEL; i++)
        {
-               if (tunnel[i].state == TUNNELFREE)
+               if ((tunnel[i].state == TUNNELFREE) && (i != TUNNEL_ID_PPPOE))
                {
                        LOG(4, 0, i, "Assigning tunnel ID %u\n", i);
                        if (i > config->cluster_highest_tunnelid)