Add PPPOE-SERVER functionality
authorfendo <fendo@bi12info.com>
Tue, 8 Jan 2013 09:50:57 +0000 (10:50 +0100)
committerfendo <fendo@bi12info.com>
Tue, 8 Jan 2013 09:50:57 +0000 (10:50 +0100)
15 files changed:
Makefile
cli.c
cluster.c
cluster.h
debian/changelog
icmp.c
l2tplac.c
l2tpns.c
l2tpns.h
ppp.c
pppoe.c [new file with mode: 0644]
pppoe.h [new file with mode: 0644]
radius.c
util.c
util.h

index 6af8491..21221ab 100644 (file)
--- 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 \
 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
+    ll.o md5.o ppp.o radius.o tbf.o util.o pppoe.o
 
 PROGRAMS = l2tpns nsctl
 PLUGINS = autosnoop.so autothrottle.so garden.so sessionctl.so \
 
 PROGRAMS = l2tpns nsctl
 PLUGINS = autosnoop.so autothrottle.so garden.so sessionctl.so \
@@ -114,22 +114,23 @@ install: all
 arp.o: arp.c l2tpns.h
 cli.o: cli.c l2tpns.h constants.h util.h cluster.h tbf.h ll.h bgp.h \
  l2tplac.h
 arp.o: arp.c l2tpns.h
 cli.o: cli.c l2tpns.h constants.h util.h cluster.h tbf.h ll.h bgp.h \
  l2tplac.h
-cluster.o: cluster.c l2tpns.h cluster.h util.h tbf.h bgp.h
+cluster.o: cluster.c l2tpns.h cluster.h util.h tbf.h pppoe.h bgp.h
 constants.o: constants.c constants.h
 control.o: control.c l2tpns.h control.h
 constants.o: constants.c constants.h
 control.o: control.c l2tpns.h control.h
-icmp.o: icmp.c l2tpns.h
+icmp.o: icmp.c l2tpns.h pppoe.h
 l2tpns.o: l2tpns.c md5.h l2tpns.h cluster.h plugin.h ll.h constants.h \
 l2tpns.o: l2tpns.c md5.h l2tpns.h cluster.h plugin.h ll.h constants.h \
- control.h util.h tbf.h bgp.h l2tplac.h
+ control.h util.h tbf.h bgp.h l2tplac.h pppoe.h
 ll.o: ll.c ll.h
 md5.o: md5.c md5.h
 ppp.o: ppp.c l2tpns.h constants.h plugin.h util.h tbf.h cluster.h \
 ll.o: ll.c ll.h
 md5.o: md5.c md5.h
 ppp.o: ppp.c l2tpns.h constants.h plugin.h util.h tbf.h cluster.h \
- l2tplac.h
+ l2tplac.h pppoe.h
 radius.o: radius.c md5.h constants.h l2tpns.h plugin.h util.h cluster.h \
 radius.o: radius.c md5.h constants.h l2tpns.h plugin.h util.h cluster.h \
- l2tplac.h
+ l2tplac.h pppoe.h
 tbf.o: tbf.c l2tpns.h util.h tbf.h
 util.o: util.c l2tpns.h bgp.h
 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
 bgp.o: bgp.c l2tpns.h bgp.h util.h
 bgp.o: bgp.c l2tpns.h bgp.h util.h
-l2tplac.o: l2tplac.c md5.h l2tpns.h util.h cluster.h l2tplac.h
+l2tplac.o: l2tplac.c md5.h l2tpns.h util.h cluster.h l2tplac.h pppoe.h
 autosnoop.so: autosnoop.c l2tpns.h plugin.h
 autothrottle.so: autothrottle.c l2tpns.h plugin.h
 garden.so: garden.c l2tpns.h plugin.h control.h
 autosnoop.so: autosnoop.c l2tpns.h plugin.h
 autothrottle.so: autothrottle.c l2tpns.h plugin.h
 garden.so: garden.c l2tpns.h plugin.h control.h
diff --git a/cli.c b/cli.c
index 09ea768..3f950e0 100644 (file)
--- a/cli.c
+++ b/cli.c
@@ -564,9 +564,9 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                        "idle",
                        "Rem.Time",
 #ifdef LAC
                        "idle",
                        "Rem.Time",
 #ifdef LAC
-                       "LAC(L)/R LNS(R)",
+                       "LAC(L)/RLNS(R)/PPPOE(P)",
 #else
 #else
-                       "LAC",
+                       "LAC(L)/PPPOE(P)",
 #endif
                        "CLI");
 
 #endif
                        "CLI");
 
@@ -579,9 +579,9 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                else
                        rem_time = session[i].timeout ? (session[i].timeout - (time_now-session[i].opened)) : 0;
 #ifdef LAC
                else
                        rem_time = session[i].timeout ? (session[i].timeout - (time_now-session[i].opened)) : 0;
 #ifdef LAC
-               cli_print(cli, "%5d %7d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %3s%-15s %s",
+               cli_print(cli, "%5d %7d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %3s%-20s %s",
 #else
 #else
-               cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %-15s %s",
+               cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %3s%-20s %s",
 #endif
                                i,
 #ifdef LAC
 #endif
                                i,
 #ifdef LAC
@@ -600,9 +600,11 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                                abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)),
                                (unsigned long)(rem_time),
 #ifdef LAC
                                abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)),
                                (unsigned long)(rem_time),
 #ifdef LAC
-                               tunnel[session[i].tunnel].isremotelns?"(R)":"(L)",
+                               (session[i].tunnel == TUNNEL_ID_PPPOE)?"(P)":(tunnel[session[i].tunnel].isremotelns?"(R)":"(L)"),
+#else
+                               (session[i].tunnel == TUNNEL_ID_PPPOE)?"(P)":"(L)",
 #endif
 #endif
-                               fmtaddr(htonl(tunnel[ session[i].tunnel ].ip), 1),
+                               (session[i].tunnel == TUNNEL_ID_PPPOE)?fmtMacAddr(session[i].src_hwaddr):fmtaddr(htonl(tunnel[session[i].tunnel].ip), 1),
                                session[i].calling[0] ? session[i].calling : "*");
        }
        return CLI_OK;
                                session[i].calling[0] ? session[i].calling : "*");
        }
        return CLI_OK;
@@ -686,18 +688,16 @@ static int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int
                if (!show_all && (!tunnel[i].ip || tunnel[i].die)) continue;
 
                for (x = 0; x < MAXSESSION; x++) if (session[x].tunnel == i && session[x].opened && !session[x].die) sessions++;
                if (!show_all && (!tunnel[i].ip || tunnel[i].die)) continue;
 
                for (x = 0; x < MAXSESSION; x++) if (session[x].tunnel == i && session[x].opened && !session[x].die) sessions++;
-#ifdef LAC
                cli_print(cli, "%4d %20s %20s %6s %6d %s",
                cli_print(cli, "%4d %20s %20s %6s %6d %s",
-#else
-               cli_print(cli, "%4d %20s %20s %6s %6d",
-#endif
                                i,
                                *tunnel[i].hostname ? tunnel[i].hostname : "(null)",
                                fmtaddr(htonl(tunnel[i].ip), 0),
                                states[tunnel[i].state],
                                sessions
 #ifdef LAC
                                i,
                                *tunnel[i].hostname ? tunnel[i].hostname : "(null)",
                                fmtaddr(htonl(tunnel[i].ip), 0),
                                states[tunnel[i].state],
                                sessions
 #ifdef LAC
-                               ,(tunnel[i].isremotelns?"Tunnel To Remote LNS":"Tunnel To LAC")
+                               ,(i == TUNNEL_ID_PPPOE)?"Tunnel pppoe":(tunnel[i].isremotelns?"Tunnel To Remote LNS":"Tunnel To LAC")
+#else
+                               ,(i == TUNNEL_ID_PPPOE)?"Tunnel pppoe":"Tunnel To LAC"
 #endif
                                );
        }
 #endif
                                );
        }
index 9277f41..cf65972 100644 (file)
--- a/cluster.c
+++ b/cluster.c
@@ -21,6 +21,7 @@
 #include "cluster.h"
 #include "util.h"
 #include "tbf.h"
 #include "cluster.h"
 #include "util.h"
 #include "tbf.h"
+#include "pppoe.h"
 
 #ifdef BGP
 #include "bgp.h"
 
 #ifdef BGP
 #include "bgp.h"
@@ -303,10 +304,39 @@ static int _forward_packet(uint8_t *data, int size, in_addr_t addr, int port, in
 //
 // The master just processes the payload as if it had
 // received it off the tun device.
 //
 // The master just processes the payload as if it had
 // received it off the tun device.
-//
+//(note: THIS ROUTINE WRITES TO pack[-6]).
 int master_forward_packet(uint8_t *data, int size, in_addr_t addr, int port)
 {
 int master_forward_packet(uint8_t *data, int size, in_addr_t addr, int port)
 {
-       return _forward_packet(data, size, addr, port, C_FORWARD);
+       uint8_t *p = data - (3 * sizeof(uint32_t));
+       uint8_t *psave = p;
+
+       if (!config->cluster_master_address) // No election has been held yet. Just skip it.
+               return -1;
+
+       LOG(4, 0, 0, "Forwarding packet from %s to master (size %d)\n", fmtaddr(addr, 0), size);
+
+       STAT(c_forwarded);
+       add_type(&p, C_FORWARD, addr, (uint8_t *) &port, sizeof(port)); // ick. should be uint16_t
+
+       return peer_send_data(config->cluster_master_address, psave, size + (3 * sizeof(uint32_t)));
+}
+
+// Forward PPPOE packet to the master.
+//(note: THIS ROUTINE WRITES TO pack[-4]).
+int master_forward_pppoe_packet(uint8_t *data, int size, uint8_t codepad)
+{
+       uint8_t *p = data - (2 * sizeof(uint32_t));
+       uint8_t *psave = p;
+
+       if (!config->cluster_master_address) // No election has been held yet. Just skip it.
+               return -1;
+
+       LOG(4, 0, 0, "Forward PPPOE packet to master, code %s (size %d)\n", get_string_codepad(codepad), size);
+
+       STAT(c_forwarded);
+       add_type(&p, C_PPPOE_FORWARD, codepad, NULL, 0);
+
+       return peer_send_data(config->cluster_master_address, psave, size + (2 * sizeof(uint32_t)));
 }
 
 // Forward a DAE RADIUS packet to the master.
 }
 
 // Forward a DAE RADIUS packet to the master.
@@ -606,6 +636,7 @@ void cluster_check_master(void)
                // to become a master!!!
 
        config->cluster_iam_master = 1;
                // to become a master!!!
 
        config->cluster_iam_master = 1;
+       pppoe_send_garp(); // gratuitous arp of the pppoe interface
 
        LOG(0, 0, 0, "I am declaring myself the master!\n");
 
 
        LOG(0, 0, 0, "I am declaring myself the master!\n");
 
@@ -1871,6 +1902,17 @@ int processcluster(uint8_t *data, int size, in_addr_t addr)
 
                        return 0;
                }
 
                        return 0;
                }
+       case C_PPPOE_FORWARD:
+               if (!config->cluster_iam_master)
+               {
+                       LOG(0, 0, 0, "I'm not the master, but I got a C_PPPOE_FORWARD from %s?\n", fmtaddr(addr, 0));
+                       return -1;
+               }
+               else
+               {
+                       pppoe_process_forward(p, s, addr);
+                       return 0;
+               }
 
        case C_MPPP_FORWARD:
                // Receive a MPPP packet from a slave.
 
        case C_MPPP_FORWARD:
                // Receive a MPPP packet from a slave.
index 0581647..59a6aa6 100644 (file)
--- a/cluster.h
+++ b/cluster.h
@@ -24,6 +24,7 @@
 #define C_BUNDLE               17      // Bundle structure.
 #define C_CBUNDLE              18      // Compressed bundle structure.
 #define C_MPPP_FORWARD 19      // MPPP Forwarded packet..
 #define C_BUNDLE               17      // Bundle structure.
 #define C_CBUNDLE              18      // Compressed bundle structure.
 #define C_MPPP_FORWARD 19      // MPPP Forwarded packet..
+#define C_PPPOE_FORWARD        20      // PPPOE Forwarded packet..
 
 #ifdef LAC
 #define HB_VERSION             7       // Protocol version number..
 
 #ifdef LAC
 #define HB_VERSION             7       // Protocol version number..
@@ -97,5 +98,6 @@ void cluster_heartbeat(void);
 void cluster_check_master(void);
 void cluster_check_slaves(void);
 int cmd_show_cluster(struct cli_def *cli, char *command, char **argv, int argc);
 void cluster_check_master(void);
 void cluster_check_slaves(void);
 int cmd_show_cluster(struct cli_def *cli, char *command, char **argv, int argc);
+int master_forward_pppoe_packet(uint8_t *data, int size, uint8_t codepad);
 
 #endif /* __CLUSTER_H__ */
 
 #endif /* __CLUSTER_H__ */
index a9aa82b..2c8c543 100644 (file)
@@ -1,3 +1,9 @@
+l2tpns (2.2.1-2fdn3.2-pppoe) unstable; urgency=low
+
+  * pppoe server functionality
+
+ -- Fernando Alves <fernando.alves@sameswireless.fr>  Thu, 27 Dec 2012 22:54:02 +0100
+
 l2tpns (2.2.1-2fdn3.2) unstable; urgency=low
 
   * Authorize to change the source IP of the tunnels l2tp.
 l2tpns (2.2.1-2fdn3.2) unstable; urgency=low
 
   * Authorize to change the source IP of the tunnels l2tp.
diff --git a/icmp.c b/icmp.c
index 6d90467..fa947b7 100644 (file)
--- a/icmp.c
+++ b/icmp.c
@@ -15,6 +15,7 @@
 #include <memory.h>
 
 #include "l2tpns.h"
 #include <memory.h>
 
 #include "l2tpns.h"
+#include "pppoe.h"
 
 static uint16_t _checksum(uint8_t *addr, int count);
 
 
 static uint16_t _checksum(uint8_t *addr, int count);
 
@@ -130,7 +131,9 @@ void send_ipv6_ra(sessionidt s, tunnelidt t, struct in6_addr *ip)
        *(o+9) = 0x80;
        *(o+23) = 1;
        if (ip != NULL)
        *(o+9) = 0x80;
        *(o+23) = 1;
        if (ip != NULL)
+       {
                memcpy(o+24, ip, 16);   // dest = ip
                memcpy(o+24, ip, 16);   // dest = ip
+       }
        else
        {
                // FF02::1 - all hosts
        else
        {
                // FF02::1 - all hosts
index 281bbc4..6ad4683 100644 (file)
--- a/l2tplac.c
+++ b/l2tplac.c
@@ -1,7 +1,10 @@
 /*
 /*
+ * Fernando ALVES 2013
  * Add functionality "LAC" to l2tpns.
  * Used to forward a ppp session to another "LNS".
  * Add functionality "LAC" to l2tpns.
  * Used to forward a ppp session to another "LNS".
+ * GPL licenced
  */
  */
+
 #include <errno.h>
 #include <string.h>
 
 #include <errno.h>
 #include <string.h>
 
@@ -11,6 +14,7 @@
 #include "cluster.h"
 
 #include "l2tplac.h"
 #include "cluster.h"
 
 #include "l2tplac.h"
+#include "pppoe.h"
 
 /* sequence diagram: Client <--> LAC <--> LNS1 <--> LNS2
  *
 
 /* sequence diagram: Client <--> LAC <--> LNS1 <--> LNS2
  *
@@ -466,7 +470,7 @@ int lac_session_forward(uint8_t *buf, int len, sessionidt sess, uint16_t proto,
 
        if ((!tunnel[t].isremotelns) && (!tunnel[session[sess].tunnel].isremotelns))
        {
 
        if ((!tunnel[t].isremotelns) && (!tunnel[session[sess].tunnel].isremotelns))
        {
-               LOG(0, sess, session[sess].tunnel, "Link Tunnel Session (%u) broken\n", s);
+               LOG(0, sess, session[sess].tunnel, "Link Tunnel Session (%u/%u) broken\n", s, t);
                return 0;
        }
 
                return 0;
        }
 
@@ -483,6 +487,12 @@ int lac_session_forward(uint8_t *buf, int len, sessionidt sess, uint16_t proto,
                }
        }
 
                }
        }
 
+       if (t == TUNNEL_ID_PPPOE)
+       {
+               pppoe_forwardto_session_pppoe(buf, len, sess, proto);
+               return 1;
+       }
+
        if (*buf & 0x40)
        {       // length
                p += 2;
        if (*buf & 0x40)
        {       // length
                p += 2;
index 9db5bc3..71d5857 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -56,6 +56,7 @@
 #ifdef LAC
 #include "l2tplac.h"
 #endif
 #ifdef LAC
 #include "l2tplac.h"
 #endif
+#include "pppoe.h"
 
 #ifdef LAC
 char * Vendor_name = "Linux L2TPNS";
 
 #ifdef LAC
 char * Vendor_name = "Linux L2TPNS";
@@ -186,6 +187,9 @@ config_descriptt config_values[] = {
        CONFIG("bind_address_remotelns", bind_address_remotelns, IPv4),
        CONFIG("bind_portremotelns", bind_portremotelns, SHORT),
 #endif
        CONFIG("bind_address_remotelns", bind_address_remotelns, IPv4),
        CONFIG("bind_portremotelns", bind_portremotelns, SHORT),
 #endif
+       CONFIG("pppoe_if_name", pppoe_if_name, STRING),
+       CONFIG("pppoe_service_name", pppoe_service_name, STRING),
+       CONFIG("pppoe_ac_name", pppoe_ac_name, STRING),
        { NULL, 0, 0, 0 },
 };
 
        { NULL, 0, 0, 0 },
 };
 
@@ -1195,6 +1199,12 @@ void tunnelsend(uint8_t * buf, uint16_t l, tunnelidt t)
                return;
        }
 
                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");
        if (!tunnel[t].ip)
        {
                LOG(1, 0, t, "Error sending data out tunnel: no remote endpoint (tunnel not set up)\n");
@@ -2134,20 +2144,28 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e
                throttle_session(s, 0, 0);
 
        if (cdn_result)
                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
                }
                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
        }
 
        // update filter refcounts
@@ -2414,6 +2432,12 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                STAT(tunnel_rx_errors);
                return;
        }
                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);
        if (*buf & 0x08)
        {                          // ns/nr
                ns = ntohs(*(uint16_t *) p);
@@ -3441,6 +3465,9 @@ static void regular_cleanups(double period)
                if (t > config->cluster_highest_tunnelid)
                        t = 1;
 
                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)
                {
                // check for expired tunnels
                if (tunnel[t].die && tunnel[t].die <= TIME)
                {
@@ -3664,7 +3691,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));
 
                        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++;
                }
                        sess_local[s].last_echo = time_now;
                        s_actions++;
                }
@@ -3916,11 +3944,11 @@ static int still_busy(void)
 #endif
 
 #ifdef LAC
 #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
 #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
 #endif
 
 // additional polled fds
@@ -3935,8 +3963,9 @@ static void mainloop(void)
 {
        int i;
        uint8_t buf[65536];
 {
        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 + 24; // for the hearder of the forwarded MPPP packet (see C_MPPP_FORWARD)
+                                               // and the forwarded pppoe session
+       int size_bufp = sizeof(buf) - 24;
        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);
        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);
@@ -3948,11 +3977,11 @@ static void mainloop(void)
        }
 
 #ifdef LAC
        }
 
 #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
 #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 */
 #endif
 
        /* setup our fds to poll for input */
@@ -3999,6 +4028,14 @@ static void mainloop(void)
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, udplacfd, &e);
 #endif
                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
        }
 
 #ifdef BGP
@@ -4065,6 +4102,8 @@ static void mainloop(void)
                        int udplac_ready = 0;
                        int udplac_pkts = 0;
 #endif
                        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;
                        int tun_ready = 0;
                        int cluster_ready = 0;
                        int udp_pkts = 0;
@@ -4105,6 +4144,14 @@ static void mainloop(void)
 #ifdef LAC
                                case FD_TYPE_UDPLAC:    udplac_ready++; break;
 #endif
 #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);
                                case FD_TYPE_CONTROL: // nsctl commands
                                        alen = sizeof(addr);
                                        s = recvfromto(controlfd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local);
@@ -4183,9 +4230,9 @@ static void mainloop(void)
                                if (udp_ready)
                                {
                                        alen = sizeof(addr);
                                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
                                                udp_pkts++;
                                        }
                                        else
@@ -4199,10 +4246,10 @@ static void mainloop(void)
                                if (udplac_ready)
                                {
                                        alen = sizeof(addr);
                                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)
                                        {
                                                if (!config->disable_lac_func)
-                                                       processudp(buf, s, &addr);
+                                                       processudp(p, s, &addr);
 
                                                udplac_pkts++;
                                        }
 
                                                udplac_pkts++;
                                        }
@@ -4228,13 +4275,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);
                                // 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
                                                cluster_pkts++;
                                        }
                                        else
@@ -5059,6 +5121,11 @@ int main(int argc, char *argv[])
        inittun();
        LOG(1, 0, 0, "Set up on interface %s\n", config->tundevicename);
 
        inittun();
        LOG(1, 0, 0, "Set up on interface %s\n", config->tundevicename);
 
+       if (*config->pppoe_if_name)
+       {
+               init_pppoe();
+               LOG(1, 0, 0, "Set up on pppoe interface %s\n", config->pppoe_if_name);
+       }
        initudp();
        initrad();
        initippool();
        initudp();
        initrad();
        initippool();
@@ -5300,6 +5367,9 @@ static void update_config()
        if(!config->iftun_address)
                config->iftun_address = config->bind_address;
 
        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);
 
        // re-initialise the random number source
        initrandom(config->random_device);
 
@@ -6036,7 +6106,7 @@ static tunnelidt new_tunnel()
        tunnelidt i;
        for (i = 1; i < MAXTUNNEL; i++)
        {
        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)
                {
                        LOG(4, 0, i, "Assigning tunnel ID %u\n", i);
                        if (i > config->cluster_highest_tunnelid)
index 5dbaee7..b4ab8e8 100644 (file)
--- a/l2tpns.h
+++ b/l2tpns.h
@@ -9,6 +9,7 @@
 #include <signal.h>
 #include <stdlib.h>
 #include <netinet/in.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <netinet/in.h>
+#include <net/ethernet.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <sys/types.h>
@@ -24,6 +25,9 @@
 #define MAXSESSION     60000           // could be up to 65535
 #define MAXTBFS                6000            // Maximum token bucket filters. Might need up to 2 * session.
 
 #define MAXSESSION     60000           // could be up to 65535
 #define MAXTBFS                6000            // Maximum token bucket filters. Might need up to 2 * session.
 
+// Tunnel Id reserved for pppoe
+#define TUNNEL_ID_PPPOE        1
+
 #define RADIUS_SHIFT   6
 #define RADIUS_FDS     (1 << RADIUS_SHIFT)
 #define RADIUS_MASK    ((1 << RADIUS_SHIFT) - 1)
 #define RADIUS_SHIFT   6
 #define RADIUS_FDS     (1 << RADIUS_SHIFT)
 #define RADIUS_MASK    ((1 << RADIUS_SHIFT) - 1)
@@ -323,9 +327,11 @@ typedef struct
        struct in6_addr ipv6route;      // Static IPv6 route
 #ifdef LAC
        sessionidt forwardtosession;    // LNS id_session to forward
        struct in6_addr ipv6route;      // Static IPv6 route
 #ifdef LAC
        sessionidt forwardtosession;    // LNS id_session to forward
-       char reserved[10];              // Space to expand structure without changing HB_VERSION
+       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
 #else
 #else
-       char reserved[12];              // Space to expand structure without changing HB_VERSION
+       uint8_t src_hwaddr[ETH_ALEN];   // MAC addr source (for pppoe sessions 6 bytes)
+       char reserved[6];               // Space to expand structure without changing HB_VERSION
 #endif
 }
 sessiont;
 #endif
 }
 sessiont;
@@ -769,6 +775,10 @@ typedef struct
        uint16_t bind_portremotelns;
        in_addr_t bind_address_remotelns;
 #endif
        uint16_t bind_portremotelns;
        in_addr_t bind_address_remotelns;
 #endif
+       char pppoe_if_name[IFNAMSIZ];   // Name pppoe interface to bind
+       char pppoe_service_name[64];    // pppoe service name
+       char pppoe_ac_name[64];
+       uint8_t pppoe_hwaddr[ETH_ALEN]; // MAC addr of interface pppoe to bind
 } configt;
 
 enum config_typet { INT, STRING, UNSIGNED_LONG, SHORT, BOOL, IPv4, IPv6 };
 } configt;
 
 enum config_typet { INT, STRING, UNSIGNED_LONG, SHORT, BOOL, IPv4, IPv6 };
@@ -990,18 +1000,20 @@ extern int epollfd;
 
 struct event_data {
        enum {
 
 struct event_data {
        enum {
-               FD_TYPE_CLI,
-               FD_TYPE_CLUSTER,
-               FD_TYPE_TUN,
-               FD_TYPE_UDP,
-               FD_TYPE_CONTROL,
-               FD_TYPE_DAE,
+               FD_TYPE_CLI,
+               FD_TYPE_CLUSTER,
+               FD_TYPE_TUN,
+               FD_TYPE_UDP,
+               FD_TYPE_CONTROL,
+               FD_TYPE_DAE,
                FD_TYPE_RADIUS,
                FD_TYPE_BGP,
                FD_TYPE_NETLINK,
 #ifdef LAC
                FD_TYPE_UDPLAC,
 #endif
                FD_TYPE_RADIUS,
                FD_TYPE_BGP,
                FD_TYPE_NETLINK,
 #ifdef LAC
                FD_TYPE_UDPLAC,
 #endif
+               FD_TYPE_PPPOEDISC,
+               FD_TYPE_PPPOESESS
        } type;
        int index; // for RADIUS, BGP
 };
        } type;
        int index; // for RADIUS, BGP
 };
diff --git a/ppp.c b/ppp.c
index 236188e..5ed8563 100644 (file)
--- a/ppp.c
+++ b/ppp.c
@@ -15,6 +15,7 @@
 #ifdef LAC
 #include "l2tplac.h"
 #endif
 #ifdef LAC
 #include "l2tplac.h"
 #endif
+#include "pppoe.h"
 
 extern tunnelt *tunnel;
 extern bundlet *bundle;
 
 extern tunnelt *tunnel;
 extern bundlet *bundle;
@@ -2521,6 +2522,11 @@ uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelid
        uint16_t type = mtype;
        uint8_t *start = b;
 
        uint16_t type = mtype;
        uint8_t *start = b;
 
+       if (t == TUNNEL_ID_PPPOE)
+       {
+               return pppoe_makeppp(b, size, p, l, s, t, mtype, prio, bid, mp_bits);
+       }
+
        if (size < 16) // Need more space than this!!
        {
                LOG(0, s, t, "makeppp buffer too small for L2TP header (size=%d)\n", size);
        if (size < 16) // Need more space than this!!
        {
                LOG(0, s, t, "makeppp buffer too small for L2TP header (size=%d)\n", size);
diff --git a/pppoe.c b/pppoe.c
new file mode 100644 (file)
index 0000000..d00c64f
--- /dev/null
+++ b/pppoe.c
@@ -0,0 +1,1158 @@
+/*
+ * Fernando ALVES 2013
+ * Add functionality "server pppoe" to l2tpns.
+ * GPL licenced
+ */
+
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <netpacket/packet.h>
+#include <arpa/inet.h>
+#include <linux/if_pppox.h>
+
+#include "l2tpns.h"
+#include "cluster.h"
+#include "constants.h"
+#include "md5.h"
+#include "util.h"
+
+int pppoediscfd = -1;
+int pppoesessfd = -1;
+
+static uint8_t bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+/* PPPoE codes */
+#define CODE_PADI           0x09
+#define CODE_PADO           0x07
+#define CODE_PADR           0x19
+#define CODE_PADS           0x65
+#define CODE_PADT           0xA7
+#define CODE_SESS           0x00
+
+/* PPPoE Tags */
+#define TAG_END_OF_LIST        0x0000
+#define TAG_SERVICE_NAME       0x0101
+#define TAG_AC_NAME            0x0102
+#define TAG_HOST_UNIQ          0x0103
+#define TAG_AC_COOKIE          0x0104
+#define TAG_VENDOR_SPECIFIC    0x0105
+#define TAG_RELAY_SESSION_ID   0x0110
+#define TAG_SERVICE_NAME_ERROR 0x0201
+#define TAG_AC_SYSTEM_ERROR    0x0202
+#define TAG_GENERIC_ERROR      0x0203
+
+static char *code_pad[] = {
+       "PADI",
+       "PADO",
+       "PADR",
+       "PADS",
+       "PADT",
+       "SESS",
+       NULL
+};
+
+enum
+{
+       INDEX_PADI = 0,
+       INDEX_PADO,
+       INDEX_PADR,
+       INDEX_PADS,
+       INDEX_PADT,
+       INDEX_SESS
+};
+
+// set up pppoe discovery socket
+static void init_pppoe_disc(void)
+{
+       int on = 1;
+       struct ifreq ifr;
+       struct sockaddr_ll sa;
+
+       memset(&ifr, 0, sizeof(ifr));
+       memset(&sa, 0, sizeof(sa));
+
+       pppoediscfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PPP_DISC));
+       if (pppoediscfd < 0)
+       {
+               LOG(0, 0, 0, "Error pppoe: socket: %s\n", strerror(errno));
+               exit(1);
+       }
+
+       fcntl(pppoediscfd, F_SETFD, fcntl(pppoediscfd, F_GETFD) | FD_CLOEXEC);
+
+       if (setsockopt(pppoediscfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)))
+       {
+               LOG(0, 0, 0, "Error pppoe: setsockopt(SO_BROADCAST): %s\n", strerror(errno));
+               exit(1);
+       }
+
+       assert(strlen(ifr.ifr_name) < sizeof(config->pppoe_if_name) - 1);
+       if (*config->pppoe_if_name)
+               strncpy(ifr.ifr_name, config->pppoe_if_name, IFNAMSIZ);
+
+       if (ioctl(pppoediscfd, SIOCGIFHWADDR, &ifr))
+       {
+               LOG(0, 0, 0, "Error pppoe: ioctl(SIOCGIFHWADDR): %s\n", strerror(errno));
+               exit(1);
+       }
+
+       if ((ifr.ifr_hwaddr.sa_data[0] & 1) != 0)
+       {
+               LOG(0, 0, 0, "Error pppoe: interface %s has not unicast address\n", config->pppoe_if_name);
+               exit(1);
+       }
+
+       memcpy(config->pppoe_hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+       if (ioctl(pppoediscfd, SIOCGIFMTU, &ifr))
+       {
+               LOG(0, 0, 0, "Error pppoe: ioctl(SIOCGIFMTU): %s\n", strerror(errno));
+               exit(1);
+       }
+
+       if (ifr.ifr_mtu < ETH_DATA_LEN)
+               LOG(0, 0, 0, "Error pppoe: interface %s has MTU of %i, should be %i\n", config->pppoe_if_name, ifr.ifr_mtu, ETH_DATA_LEN);
+
+       if (ioctl(pppoediscfd, SIOCGIFINDEX, &ifr))
+       {
+               LOG(0, 0, 0, "Error pppoe: ioctl(SIOCGIFINDEX): %s\n", strerror(errno));
+               exit(1);
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sll_family = AF_PACKET;
+       sa.sll_protocol = htons(ETH_P_PPP_DISC);
+       sa.sll_ifindex = ifr.ifr_ifindex;
+
+       if (bind(pppoediscfd, (struct sockaddr *)&sa, sizeof(sa)))
+       {
+               LOG(0, 0, 0, "Error pppoe: bind: %s\n", strerror(errno));
+               exit(1);
+       }
+
+       if (fcntl(pppoediscfd, F_SETFL, O_NONBLOCK))
+       {
+               LOG(0, 0, 0, "Error pppoe: failed to set nonblocking mode: %s\n", strerror(errno));
+               exit(1);
+       }
+
+}
+
+// set up pppoe session socket
+static void init_pppoe_sess(void)
+{
+       int on = 1;
+       struct ifreq ifr;
+       struct sockaddr_ll sa;
+
+       memset(&ifr, 0, sizeof(ifr));
+       memset(&sa, 0, sizeof(sa));
+
+       pppoesessfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PPP_SES));
+       if (pppoesessfd < 0)
+       {
+               LOG(0, 0, 0, "Error pppoe: socket: %s\n", strerror(errno));
+               exit(1);
+       }
+
+       fcntl(pppoesessfd, F_SETFD, fcntl(pppoesessfd, F_GETFD) | FD_CLOEXEC);
+
+       if (setsockopt(pppoesessfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)))
+       {
+               LOG(0, 0, 0, "Error pppoe: setsockopt(SO_BROADCAST): %s\n", strerror(errno));
+               exit(1);
+       }
+
+       assert(strlen(ifr.ifr_name) < sizeof(config->pppoe_if_name) - 1);
+       if (*config->pppoe_if_name)
+               strncpy(ifr.ifr_name, config->pppoe_if_name, IFNAMSIZ);
+
+       if (ioctl(pppoesessfd, SIOCGIFHWADDR, &ifr))
+       {
+               LOG(0, 0, 0, "Error pppoe: ioctl(SIOCGIFHWADDR): %s\n", strerror(errno));
+               exit(1);
+       }
+
+       if ((ifr.ifr_hwaddr.sa_data[0] & 1) != 0)
+       {
+               LOG(0, 0, 0, "Error pppoe: interface %s has not unicast address\n", config->pppoe_if_name);
+               exit(1);
+       }
+
+       memcpy(config->pppoe_hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+       if (ioctl(pppoesessfd, SIOCGIFMTU, &ifr))
+       {
+               LOG(0, 0, 0, "Error pppoe: ioctl(SIOCGIFMTU): %s\n", strerror(errno));
+               exit(1);
+       }
+
+       if (ifr.ifr_mtu < ETH_DATA_LEN)
+               LOG(0, 0, 0, "Error pppoe: interface %s has MTU of %i, should be %i\n", config->pppoe_if_name, ifr.ifr_mtu, ETH_DATA_LEN);
+
+       if (ioctl(pppoesessfd, SIOCGIFINDEX, &ifr))
+       {
+               LOG(0, 0, 0, "Error pppoe: ioctl(SIOCGIFINDEX): %s\n", strerror(errno));
+               exit(1);
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sll_family = AF_PACKET;
+       sa.sll_protocol = htons(ETH_P_PPP_SES);
+       sa.sll_ifindex = ifr.ifr_ifindex;
+
+       if (bind(pppoesessfd, (struct sockaddr *)&sa, sizeof(sa)))
+       {
+               LOG(0, 0, 0, "Error pppoe: bind: %s\n", strerror(errno));
+               exit(1);
+       }
+
+       if (fcntl(pppoesessfd, F_SETFL, O_NONBLOCK))
+       {
+               LOG(0, 0, 0, "Error pppoe: failed to set nonblocking mode: %s\n", strerror(errno));
+               exit(1);
+       }
+}
+
+// set up pppoe discovery/session socket
+void init_pppoe(void)
+{
+       tunnelidt t = TUNNEL_ID_PPPOE;
+
+       init_pppoe_disc();
+       init_pppoe_sess();
+
+       // Reserve the a pseudo tunnel for pppoe server
+       if (t > config->cluster_highest_tunnelid)
+               config->cluster_highest_tunnelid = t;
+
+       memset(&tunnel[t], 0, sizeof(tunnel[t]));
+       tunnel[t].state = TUNNELOPEN;
+       STAT(tunnel_created);
+}
+
+char * get_string_codepad(uint8_t codepad)
+{
+       char * ptrch = NULL;
+       switch(codepad)
+       {
+               case CODE_PADI:
+               ptrch = code_pad[INDEX_PADI];
+               break;
+
+               case CODE_PADO:
+               ptrch = code_pad[INDEX_PADO];
+               break;
+
+               case CODE_PADR:
+               ptrch = code_pad[INDEX_PADR];
+               break;
+
+               case CODE_PADS:
+               ptrch = code_pad[INDEX_PADS];
+               break;
+
+               case CODE_PADT:
+               ptrch = code_pad[INDEX_PADT];
+               break;
+
+               case CODE_SESS:
+               ptrch = code_pad[INDEX_SESS];
+               break;
+       }
+       
+       return ptrch;
+}
+
+static uint8_t * setup_header(uint8_t *pack, const uint8_t *src, const uint8_t *dst, int code, uint16_t sid, uint16_t h_proto)
+{
+       uint8_t * p;
+
+       // 14 bytes ethernet Header + 6 bytes header pppoe
+       struct ethhdr *ethhdr = (struct ethhdr *)pack;
+       struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+
+       memcpy(ethhdr->h_source, src, ETH_ALEN);
+       memcpy(ethhdr->h_dest, dst, ETH_ALEN);
+       ethhdr->h_proto = htons(h_proto);
+
+       hdr->ver = 1;
+       hdr->type = 1;
+       hdr->code = code;
+       hdr->sid = htons(sid);
+       hdr->length = 0;
+
+       p = (uint8_t *)(pack + ETH_HLEN + sizeof(*hdr));
+
+       return p;
+}
+
+static void add_tag(uint8_t *pack, int type, const uint8_t *data, int len)
+{
+       struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+       struct pppoe_tag *tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length));
+
+       tag->tag_type = htons(type);
+       tag->tag_len = htons(len);
+       memcpy(tag->tag_data, data, len);
+
+       hdr->length = htons(ntohs(hdr->length) + sizeof(*tag) + len);
+}
+
+static void add_tag2(uint8_t *pack, const struct pppoe_tag *t)
+{
+       struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+       struct pppoe_tag *tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length));
+
+       memcpy(tag, t, sizeof(*t) + ntohs(t->tag_len));
+       
+       hdr->length = htons(ntohs(hdr->length) + sizeof(*tag) + ntohs(t->tag_len));
+}
+
+static void pppoe_disc_send(const uint8_t *pack)
+{
+       struct ethhdr *ethhdr = (struct ethhdr *)pack;
+       struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+       int n, s;
+
+       s = ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length);
+
+       LOG(3, 0, 0, "SENT pppoe_disc: Code %s to %s\n", get_string_codepad(hdr->code), fmtMacAddr(ethhdr->h_dest));
+       LOG_HEX(5, "pppoe_disc_send", pack, s);
+
+       n = write(pppoediscfd, pack, s);
+       if (n < 0 )
+               LOG(0, 0, 0, "pppoe: write: %s\n", strerror(errno));
+       else if (n != s) {
+               LOG(0, 0, 0, "pppoe: short write %i/%i\n", n,s);
+       }
+}
+
+void pppoe_sess_send(const uint8_t *pack, uint16_t l, tunnelidt t)
+{
+       struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+       int n;
+       uint16_t sizeppp;
+       sessionidt s;
+
+       if (t != TUNNEL_ID_PPPOE)
+       {
+               LOG(3, 0, t, "ERROR pppoe_sess_send: Tunnel %d is not a tunnel pppoe\n", t);
+               return;
+       }
+
+       s = ntohs(hdr->sid);
+       if (session[s].tunnel != t)
+       {
+               LOG(3, s, t, "ERROR pppoe_sess_send: Session is not a session pppoe\n");
+               return;
+       }
+
+       if (l < (ETH_HLEN + sizeof(*hdr) + 3))
+       {
+               LOG(0, s, t, "ERROR pppoe_sess_send: packet too small for pppoe sent (size=%d)\n", l);
+               return;
+       }
+
+       // recalculate the ppp frame length
+       sizeppp = l - (ETH_HLEN + sizeof(*hdr));
+       hdr->length = htons(sizeppp);
+
+       LOG_HEX(5, "pppoe_sess_send", pack, l);
+
+       n = write(pppoesessfd, pack, l);
+       if (n < 0 )
+               LOG(0, s, t, "pppoe_sess_send: write: %s\n", strerror(errno));
+       else if (n != l)
+               LOG(0, s, t, "pppoe_sess_send: short write %i/%i\n", n,l);
+}
+
+static void pppoe_send_err(const uint8_t *addr, const struct pppoe_tag *host_uniq, const struct pppoe_tag *relay_sid, int code, int tag_type)
+{
+       uint8_t pack[ETHER_MAX_LEN];
+
+       setup_header(pack, config->pppoe_hwaddr, addr, code, 0, ETH_P_PPP_DISC);
+
+       add_tag(pack, TAG_AC_NAME, (uint8_t *)config->pppoe_ac_name, strlen(config->pppoe_ac_name));
+       add_tag(pack, tag_type, NULL, 0);
+
+       if (host_uniq)
+               add_tag2(pack, host_uniq);
+       
+       if (relay_sid)
+               add_tag2(pack, relay_sid);
+
+       pppoe_disc_send(pack);
+}
+
+// generate cookie
+static void pppoe_gen_cookie(const uint8_t *serv_hwaddr, const uint8_t *client_hwaddr, uint8_t *out)
+{
+       MD5_CTX ctx;
+
+       MD5_Init(&ctx);
+       MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret));
+       MD5_Update(&ctx, (void *) serv_hwaddr, ETH_ALEN);
+       MD5_Update(&ctx, (void *) client_hwaddr, ETH_ALEN);
+       MD5_Final(out, &ctx);
+}
+
+// check cookie
+static int pppoe_check_cookie(const uint8_t *serv_hwaddr, const uint8_t *client_hwaddr, uint8_t *cookie)
+{
+       hasht hash;
+
+       pppoe_gen_cookie(serv_hwaddr, client_hwaddr, hash);
+
+       return memcmp(hash, cookie, 16);
+}
+
+static void pppoe_send_PADO(const uint8_t *addr, const struct pppoe_tag *host_uniq, const struct pppoe_tag *relay_sid, const struct pppoe_tag *service_name)
+{
+       uint8_t pack[ETHER_MAX_LEN];
+       hasht hash;
+
+       setup_header(pack, config->pppoe_hwaddr, addr, CODE_PADO, 0, ETH_P_PPP_DISC);
+
+       add_tag(pack, TAG_AC_NAME, (uint8_t *)config->pppoe_ac_name, strlen(config->pppoe_ac_name));
+
+       if (service_name)
+               add_tag2(pack, service_name);
+
+       pppoe_gen_cookie(config->pppoe_hwaddr, addr, hash);
+       add_tag(pack, TAG_AC_COOKIE, hash, 16);
+
+       if (host_uniq)
+               add_tag2(pack, host_uniq);
+       
+       if (relay_sid)
+               add_tag2(pack, relay_sid);
+
+       pppoe_disc_send(pack);
+}
+
+static void pppoe_send_PADS(uint16_t sid, const uint8_t *addr, const struct pppoe_tag *host_uniq, const struct pppoe_tag *relay_sid, const struct pppoe_tag *service_name)
+{
+       uint8_t pack[ETHER_MAX_LEN];
+
+       setup_header(pack, config->pppoe_hwaddr, addr, CODE_PADS, sid, ETH_P_PPP_DISC);
+
+       add_tag(pack, TAG_AC_NAME, (uint8_t *)config->pppoe_ac_name, strlen(config->pppoe_ac_name));
+
+       add_tag2(pack, service_name);
+
+       if (host_uniq)
+               add_tag2(pack, host_uniq);
+       
+       if (relay_sid)
+               add_tag2(pack, relay_sid);
+
+       pppoe_disc_send(pack);
+}
+
+static void pppoe_send_PADT(uint16_t sid)
+{
+       uint8_t pack[ETHER_MAX_LEN];
+
+       setup_header(pack, config->pppoe_hwaddr, session[sid].src_hwaddr, CODE_PADT, sid, ETH_P_PPP_DISC);
+
+       add_tag(pack, TAG_AC_NAME, (uint8_t *)config->pppoe_ac_name, strlen(config->pppoe_ac_name));
+
+       LOG(3, sid, session[sid].tunnel, "pppoe: Sent PADT\n");
+
+       pppoe_disc_send(pack);
+}
+
+void pppoe_shutdown_session(sessionidt s)
+{
+
+       if (session[s].tunnel != TUNNEL_ID_PPPOE)
+       {
+               LOG(3, s, session[s].tunnel, "ERROR pppoe_shutdown_session: Session is not a session pppoe\n");
+               return;
+       }
+
+       pppoe_send_PADT(s);
+}
+
+static void pppoe_recv_PADI(uint8_t *pack, int size)
+{
+       struct ethhdr *ethhdr = (struct ethhdr *)pack;
+       struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+       struct pppoe_tag *tag;
+       struct pppoe_tag *host_uniq_tag = NULL;
+       struct pppoe_tag *relay_sid_tag = NULL;
+       struct pppoe_tag *service_name_tag = NULL;
+       int n, service_match = 0;
+       int len;
+
+       if (hdr->sid)
+               return;
+
+       len = ntohs(hdr->length);
+       for (n = 0; n < len; n += sizeof(*tag) + ntohs(tag->tag_len)) {
+               tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + n);
+               if (n + sizeof(*tag) + ntohs(tag->tag_len) > len)
+                       return;
+               switch (ntohs(tag->tag_type)) {
+                       case TAG_END_OF_LIST:
+                               break;
+                       case TAG_SERVICE_NAME:
+                               if (*config->pppoe_service_name && tag->tag_len)
+                               {
+                                       if (ntohs(tag->tag_len) != strlen(config->pppoe_service_name))
+                                               break;
+                                       if (memcmp(tag->tag_data, config->pppoe_service_name, ntohs(tag->tag_len)))
+                                               break;
+                                       service_match = 1;
+                               }
+                               else
+                               {
+                                       service_name_tag = tag;
+                                       service_match = 1;
+                               }
+                               break;
+                       case TAG_HOST_UNIQ:
+                               host_uniq_tag = tag;
+                               break;
+                       case TAG_RELAY_SESSION_ID:
+                               relay_sid_tag = tag;
+                               break;
+               }
+       }
+
+       if (!service_match)
+       {
+               LOG(3, 0, 0, "pppoe: discarding PADI packet (Service-Name mismatch)\n");
+               return;
+       }
+
+       pppoe_send_PADO(ethhdr->h_source, host_uniq_tag, relay_sid_tag, service_name_tag);
+}
+
+static void pppoe_recv_PADR(uint8_t *pack, int size)
+{
+       struct ethhdr *ethhdr = (struct ethhdr *)pack;
+       struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+       struct pppoe_tag *tag;
+       struct pppoe_tag *host_uniq_tag = NULL;
+       struct pppoe_tag *relay_sid_tag = NULL;
+       struct pppoe_tag *ac_cookie_tag = NULL;
+       struct pppoe_tag *service_name_tag = NULL;
+       int n, service_match = 0;
+       uint16_t sid;
+
+       if (!memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN))
+       {
+               LOG(1, 0, 0, "Rcv pppoe: discard PADR (destination address is broadcast)\n");
+               return;
+       }
+
+       if (hdr->sid)
+       {
+               LOG(1, 0, 0, "Rcv pppoe: discarding PADR packet (sid is not zero)\n");
+               return;
+       }
+
+       for (n = 0; n < ntohs(hdr->length); n += sizeof(*tag) + ntohs(tag->tag_len))
+       {
+               tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + n);
+               switch (ntohs(tag->tag_type))
+               {
+                       case TAG_END_OF_LIST:
+                               break;
+                       case TAG_SERVICE_NAME:
+                               service_name_tag = tag;
+                               if (tag->tag_len == 0)
+                                       service_match = 1;
+                               else if (*config->pppoe_service_name)
+                               {
+                                       if (ntohs(tag->tag_len) != strlen(config->pppoe_service_name))
+                                               break;
+                                       if (memcmp(tag->tag_data, config->pppoe_service_name, ntohs(tag->tag_len)))
+                                               break;
+                                       service_match = 1;
+                               }
+                               else
+                               {
+                                       service_match = 1;
+                               }
+                               break;
+                       case TAG_HOST_UNIQ:
+                               host_uniq_tag = tag;
+                               break;
+                       case TAG_AC_COOKIE:
+                               ac_cookie_tag = tag;
+                               break;
+                       case TAG_RELAY_SESSION_ID:
+                               relay_sid_tag = tag;
+                               break;
+               }
+       }
+
+       if (!service_match)
+       {
+               LOG(3, 0, 0, "pppoe: Service-Name mismatch\n");
+               pppoe_send_err(ethhdr->h_source, host_uniq_tag, relay_sid_tag, CODE_PADS, TAG_SERVICE_NAME_ERROR);
+               return;
+       }
+
+       if (!ac_cookie_tag)
+       {
+               LOG(3, 0, 0, "pppoe: discard PADR packet (no AC-Cookie tag present)\n");
+               return;
+       }
+
+       if (ntohs(ac_cookie_tag->tag_len) != 16)
+       {
+               LOG(3, 0, 0, "pppoe: discard PADR packet (incorrect AC-Cookie tag length)\n");
+               return;
+       }
+
+       if (pppoe_check_cookie(ethhdr->h_dest, ethhdr->h_source, (uint8_t *) ac_cookie_tag->tag_data))
+       {
+               LOG(3, 0, 0, "pppoe: discard PADR packet (incorrect AC-Cookie)\n");
+               return;
+       }
+
+       sid = sessionfree;
+       sessionfree = session[sid].next;
+       memset(&session[sid], 0, sizeof(session[0]));
+
+       if (sid > config->cluster_highest_sessionid)
+               config->cluster_highest_sessionid = sid;
+
+       session[sid].opened = time_now;
+       session[sid].tunnel = TUNNEL_ID_PPPOE;
+       session[sid].last_packet = session[sid].last_data = time_now;
+
+       //strncpy(session[sid].called, called, sizeof(session[sid].called) - 1);
+       //strncpy(session[sid].calling, calling, sizeof(session[sid].calling) - 1);
+
+       session[sid].ppp.phase = Establish;
+       session[sid].ppp.lcp = Starting;
+
+       session[sid].magic = time_now; // set magic number
+       session[sid].mru = PPPoE_MRU; // default
+
+       // start LCP
+       sess_local[sid].lcp_authtype = config->radius_authprefer;
+       sess_local[sid].ppp_mru = MRU;
+
+       // Set multilink options before sending initial LCP packet
+       sess_local[sid].mp_mrru = 1614;
+       sess_local[sid].mp_epdis = ntohl(config->iftun_address ? config->iftun_address : my_address);
+
+       memcpy(session[sid].src_hwaddr, ethhdr->h_source, ETH_ALEN);
+       pppoe_send_PADS(sid, ethhdr->h_source, host_uniq_tag, relay_sid_tag, service_name_tag);
+
+       sendlcp(sid, session[sid].tunnel);
+       change_state(sid, lcp, RequestSent);
+
+}
+
+static void pppoe_recv_PADT(uint8_t *pack)
+{
+       struct ethhdr *ethhdr = (struct ethhdr *)pack;
+       struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+
+       if (!memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN))
+       {
+               LOG(3, 0, 0, "pppoe: discard PADT (destination address is broadcast)\n");
+               return;
+       }
+
+       if (hdr->sid)
+       {
+               if ((hdr->sid < MAXSESSION) && (session[hdr->sid].tunnel == TUNNEL_ID_PPPOE))
+                       sessionshutdown(hdr->sid, "Client shutdown", CDN_ADMIN_DISC, 0);
+       }
+}
+
+// fill in a PPPOE message with a PPP frame,
+// returns start of PPP frame
+uint8_t *pppoe_makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t,
+                                               uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits)
+{
+       uint16_t type = mtype;
+       uint8_t *start = b;
+       struct pppoe_hdr *hdr = (struct pppoe_hdr *)(b + ETH_HLEN);
+
+       if (t != TUNNEL_ID_PPPOE)
+               return NULL;
+
+       if (size < 28) // Need more space than this!!
+       {
+               LOG(0, s, t, "pppoe_makeppp buffer too small for pppoe header (size=%d)\n", size);
+               return NULL;
+       }
+
+       // 14 bytes ethernet Header + 6 bytes header pppoe
+       b = setup_header(b, config->pppoe_hwaddr, session[s].src_hwaddr, CODE_SESS, s, ETH_P_PPP_SES);
+
+       // Check whether this session is part of multilink
+       if (bid)
+       {
+               if (bundle[bid].num_of_links > 1)
+                       type = PPPMP; // Change PPP message type to the PPPMP
+               else
+                       bid = 0;
+       }
+
+       *(uint16_t *) b = htons(type);
+       b += 2;
+       hdr->length += 2;
+
+       if (bid)
+       {
+               // Set the sequence number and (B)egin (E)nd flags
+               if (session[s].mssf)
+               {
+                       // Set the multilink bits
+                       uint16_t bits_send = mp_bits;
+                       *(uint16_t *) b = htons((bundle[bid].seq_num_t & 0x0FFF)|bits_send);
+                       b += 2;
+                       hdr->length += 2;
+               }
+               else
+               {
+                       *(uint32_t *) b = htonl(bundle[bid].seq_num_t);
+                       // Set the multilink bits
+                       *b = mp_bits;
+                       b += 4;
+                       hdr->length += 4;
+               }
+
+               bundle[bid].seq_num_t++;
+
+               // Add the message type if this fragment has the begin bit set
+               if (mp_bits & MP_BEGIN)
+               {
+                       //*b++ = mtype; // The next two lines are instead of this 
+                       *(uint16_t *) b = htons(mtype); // Message type
+                       b += 2;
+                       hdr->length += 2;
+               }
+       }
+
+       if ((b - start) + l > size)
+       {
+               LOG(0, s, t, "pppoe_makeppp would overflow buffer (size=%d, header+payload=%td)\n", size, (b - start) + l);
+               return NULL;
+       }
+
+       // Copy the payload
+       if (p && l)
+       {
+               memcpy(b, p, l);
+               hdr->length += l;
+       }
+
+       return b;
+}
+
+// pppoe discovery recv data
+void process_pppoe_disc(uint8_t *pack, int size)
+{
+       struct ethhdr *ethhdr = (struct ethhdr *)pack;
+       struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+
+       LOG(3, 0, 0, "RCV pppoe_disc: Code %s from %s\n", get_string_codepad(hdr->code), fmtMacAddr(ethhdr->h_source));
+       LOG_HEX(5, "PPPOE Disc", pack, size);
+
+       if (!config->cluster_iam_master)
+       {
+               if (hdr->code == CODE_PADI)
+                       return; // Discard because the PADI is received by all (PADI is a broadcast diffusion)
+
+               master_forward_pppoe_packet(pack, size, hdr->code);
+               return;
+       }
+
+       if (size < (ETH_HLEN + sizeof(*hdr)))
+       {
+               LOG(1, 0, 0, "Error pppoe_disc: short packet received (%i)\n", size);
+               return;
+       }
+
+       if (memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN) && memcmp(ethhdr->h_dest, config->pppoe_hwaddr, ETH_ALEN))
+       {
+               LOG(1, 0, 0, "Error pppoe_disc: h_dest != bc_addr and  h_dest != config->pppoe_hwaddr\n");
+               return;
+       }
+
+       if (!memcmp(ethhdr->h_source, bc_addr, ETH_ALEN))
+       {
+               LOG(1, 0, 0, "Error pppoe_disc: discarding packet (source address is broadcast)\n");
+               return;
+       }
+
+       if ((ethhdr->h_source[0] & 1) != 0)
+       {
+               LOG(1, 0, 0, "Error pppoe_disc: discarding packet (host address is not unicast)\n");
+               return;
+       }
+
+       if (size < ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length))
+       {
+               LOG(1, 0, 0, "Error pppoe_disc: short packet received\n");
+               return;
+       }
+
+       if (hdr->ver != 1)
+       {
+               LOG(1, 0, 0, "Error pppoe_disc: discarding packet (unsupported version %i)\n", hdr->ver);
+               return;
+       }
+
+       if (hdr->type != 1)
+       {
+               LOG(1, 0, 0, "Error pppoe_disc: discarding packet (unsupported type %i)\n", hdr->type);
+               return;
+       }
+
+       switch (hdr->code) {
+               case CODE_PADI:
+                       pppoe_recv_PADI(pack, size);
+                       break;
+               case CODE_PADR:
+                       pppoe_recv_PADR(pack, size);
+                       break;
+               case CODE_PADT:
+                       pppoe_recv_PADT(pack);
+                       break;
+       }
+}
+
+#ifdef LAC
+// Forward from pppoe to l2tp remote LNS
+static void pppoe_forwardto_session_rmlns(uint8_t *pack, int size, sessionidt sess, uint16_t proto)
+{
+       struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+       uint16_t lppp = ntohs(hdr->length);
+       uint16_t ll2tp = lppp + 6;
+       uint8_t *pppdata = (uint8_t *) hdr->tag;
+       uint8_t *pl2tp = pppdata - 6;
+       uint8_t *p = pl2tp;
+       uint16_t t = 0, s = 0;
+
+       s = session[sess].forwardtosession;
+       if (session[s].forwardtosession != sess)
+       {
+               LOG(3, sess, session[sess].tunnel, "pppoe: Link Session (%u) broken\n", s);
+               return;
+       }
+
+       t = session[s].tunnel;
+       if (t >= MAXTUNNEL)
+       {
+               LOG(1, s, t, "pppoe: Session with invalid tunnel ID\n");
+               return;
+       }
+
+       if (!tunnel[t].isremotelns)
+       {
+               LOG(3, sess, session[sess].tunnel, "pppoe: Link Tunnel/Session (%u/%u) broken\n", s, t);
+               return;
+       }
+
+       // First word L2TP options (with no options)
+       *(uint16_t *) p = htons(0x0002);
+       p += 2;
+       *(uint16_t *) p = htons(tunnel[t].far); // tunnel
+       p += 2;
+       *(uint16_t *) p = htons(session[s].far); // session
+       p += 2;
+
+       if ((proto == PPPIP) || (proto == PPPMP) ||(proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0]))
+       {
+               session[sess].last_packet = session[sess].last_data = time_now;
+               // Update STAT IN
+               increment_counter(&session[sess].cin, &session[sess].cin_wrap, ll2tp);
+               session[sess].cin_delta += ll2tp;
+               session[sess].pin++;
+               sess_local[sess].cin += ll2tp;
+               sess_local[sess].pin++;
+
+               session[s].last_data = time_now;
+               // Update STAT OUT
+               increment_counter(&session[s].cout, &session[s].cout_wrap, ll2tp); // byte count
+               session[s].cout_delta += ll2tp;
+               session[s].pout++;
+               sess_local[s].cout += ll2tp;
+               sess_local[s].pout++;
+       }
+       else
+               session[sess].last_packet = time_now;
+
+       tunnelsend(pl2tp, ll2tp, t); // send it...
+}
+
+// Forward from l2tp to pppoe
+// (note: THIS ROUTINE WRITES TO pack[-20]).
+void pppoe_forwardto_session_pppoe(uint8_t *pack, int size, sessionidt sess, uint16_t proto)
+{
+       uint16_t t = 0, s = 0;
+       uint16_t lpppoe = size - 2;
+       uint8_t *p = pack + 2; // First word L2TP options
+
+       LOG(5, sess, session[sess].tunnel, "Forwarding data session to pppoe session %u\n", session[sess].forwardtosession);
+
+       s = session[sess].forwardtosession;
+       t = session[s].tunnel;
+
+       if (*pack & 0x40)
+       {       // length
+               p += 2;
+               lpppoe -= 2;
+       }
+
+       *(uint16_t *) p = htons(tunnel[t].far); // tunnel
+       p += 2;
+       *(uint16_t *) p = htons(session[s].far); // session
+       p += 2;
+       lpppoe -= 4;
+
+       if (*pack & 0x08)
+       {   // ns/nr
+               *(uint16_t *) p = htons(tunnel[t].ns); // sequence
+               p += 2;
+               *(uint16_t *) p = htons(tunnel[t].nr); // sequence
+               p += 2;
+               lpppoe -= 4;
+       }
+
+       if (lpppoe > 2 && p[0] == 0xFF && p[1] == 0x03)
+       {
+               // HDLC address header, discard in pppoe
+               p += 2;
+               lpppoe -= 2;
+       }
+
+       lpppoe += (ETH_HLEN + sizeof(struct pppoe_hdr));
+       p -= (ETH_HLEN + sizeof(struct pppoe_hdr));
+
+       // 14 bytes ethernet Header + 6 bytes header pppoe
+       setup_header(p, config->pppoe_hwaddr, session[s].src_hwaddr, CODE_SESS, s, ETH_P_PPP_SES);
+
+       if ((proto == PPPIP) || (proto == PPPMP) ||(proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0]))
+       {
+               session[sess].last_packet = session[sess].last_data = time_now;
+               // Update STAT IN
+               increment_counter(&session[sess].cin, &session[sess].cin_wrap, lpppoe);
+               session[sess].cin_delta += lpppoe;
+               session[sess].pin++;
+               sess_local[sess].cin += lpppoe;
+               sess_local[sess].pin++;
+
+               session[s].last_data = time_now;
+               // Update STAT OUT
+               increment_counter(&session[s].cout, &session[s].cout_wrap, lpppoe); // byte count
+               session[s].cout_delta += lpppoe;
+               session[s].pout++;
+               sess_local[s].cout += lpppoe;
+               sess_local[s].pout++;
+       }
+       else
+               session[sess].last_packet = time_now;
+
+       tunnelsend(p, lpppoe, t); // send it....
+}
+#endif
+
+void process_pppoe_sess(uint8_t *pack, int size)
+{
+       //struct ethhdr *ethhdr = (struct ethhdr *)pack;
+       struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+       uint16_t lppp = ntohs(hdr->length);
+       uint8_t *pppdata = (uint8_t *) hdr->tag;
+       uint16_t proto, sid, t;
+
+       sid = ntohs(hdr->sid);
+       t = TUNNEL_ID_PPPOE;
+
+       LOG_HEX(5, "RCV PPPOE Sess", pack, size);
+
+       if (sid >= MAXSESSION)
+       {
+               LOG(0, sid, t, "Received pppoe packet with invalid session ID\n");
+               STAT(tunnel_rx_errors);
+               return;
+       }
+
+       if (session[sid].tunnel != t)
+       {
+               if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; }
+
+               LOG(1, sid, t, "ERROR process_pppoe_sess: Session is not a session pppoe\n");
+               return;
+       }
+
+       if (hdr->ver != 1)
+       {
+               LOG(3, sid, t, "Error process_pppoe_sess: discarding packet (unsupported version %i)\n", hdr->ver);
+               return;
+       }
+
+       if (hdr->type != 1)
+       {
+               LOG(3, sid, t, "Error process_pppoe_sess: discarding packet (unsupported type %i)\n", hdr->type);
+               return;
+       }
+
+       if (lppp > 2 && pppdata[0] == 0xFF && pppdata[1] == 0x03)
+       {       // HDLC address header, discard
+               LOG(5, sid, t, "pppoe_sess: HDLC address header, discard\n");
+               pppdata += 2;
+               lppp -= 2;
+       }
+       if (lppp < 2)
+       {
+               LOG(3, sid, t, "Error process_pppoe_sess: Short ppp length %d\n", lppp);
+               return;
+       }
+       if (*pppdata & 1)
+       {
+               proto = *pppdata++;
+               lppp--;
+       }
+       else
+       {
+               proto = ntohs(*(uint16_t *) pppdata);
+               pppdata += 2;
+               lppp -= 2;
+       }
+
+#ifdef LAC
+       if (session[sid].forwardtosession)
+       {       // Must be forwaded to a remote lns tunnel l2tp
+               pppoe_forwardto_session_rmlns(pack, size, sid, proto);
+               return;
+       }
+#endif
+
+       if (proto == PPPPAP)
+       {
+               session[sid].last_packet = time_now;
+               if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; }
+               processpap(sid, t, pppdata, lppp);
+       }
+       else if (proto == PPPCHAP)
+       {
+               session[sid].last_packet = time_now;
+               if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; }
+               processchap(sid, t, pppdata, lppp);
+       }
+       else if (proto == PPPLCP)
+       {
+               session[sid].last_packet = time_now;
+               if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; }
+               processlcp(sid, t, pppdata, lppp);
+       }
+       else if (proto == PPPIPCP)
+       {
+               session[sid].last_packet = time_now;
+               if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; }
+               processipcp(sid, t, pppdata, lppp);
+       }
+       else if (proto == PPPIPV6CP && config->ipv6_prefix.s6_addr[0])
+       {
+               session[sid].last_packet = time_now;
+               if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; }
+               processipv6cp(sid, t, pppdata, lppp);
+       }
+       else if (proto == PPPCCP)
+       {
+               session[sid].last_packet = time_now;
+               if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; }
+               processccp(sid, t, pppdata, lppp);
+       }
+       else if (proto == PPPIP)
+       {
+               session[sid].last_packet = session[sid].last_data = time_now;
+               if (session[sid].walled_garden && !config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; }
+               processipin(sid, t, pppdata, lppp);
+       }
+       else if (proto == PPPMP)
+       {
+               session[sid].last_packet = session[sid].last_data = time_now;
+               if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; }
+               processmpin(sid, t, pppdata, lppp);
+       }
+       else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0])
+       {
+               session[sid].last_packet = session[sid].last_data = time_now;
+               if (session[sid].walled_garden && !config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; }
+               processipv6in(sid, t, pppdata, lppp);
+       }
+       else if (session[sid].ppp.lcp == Opened)
+       {
+               session[sid].last_packet = time_now;
+               if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; }
+               protoreject(sid, t, pppdata, lppp, proto);
+       }
+       else
+       {
+               LOG(3, sid, t, "process_pppoe_sess: Unknown PPP protocol 0x%04X received in LCP %s state\n",
+                       proto, ppp_state(session[sid].ppp.lcp));
+       }
+}
+
+void pppoe_send_garp()
+{
+       int s;
+       struct ifreq ifr;
+       uint8_t mac[6];
+
+       if (!*config->pppoe_if_name)
+               return;
+
+       s = socket(PF_INET, SOCK_DGRAM, 0);
+       if (s < 0)
+       {
+               LOG(0, 0, 0, "Error creating socket for GARP: %s\n", strerror(errno));
+               return;
+       }
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, config->pppoe_if_name, sizeof(ifr.ifr_name) - 1);
+       if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0)
+       {
+               LOG(0, 0, 0, "Error getting eth0 hardware address for GARP: %s\n", strerror(errno));
+               close(s);
+               return;
+       }
+       memcpy(mac, &ifr.ifr_hwaddr.sa_data, 6*sizeof(char));
+       if (ioctl(s, SIOCGIFINDEX, &ifr) < 0)
+       {
+               LOG(0, 0, 0, "Error getting eth0 interface index for GARP: %s\n", strerror(errno));
+               close(s);
+               return;
+       }
+       close(s);
+
+       sendarp(ifr.ifr_ifindex, mac, config->iftun_address);
+}
+
+// rcv pppoe data from slave
+void pppoe_process_forward(uint8_t *pack, int size, in_addr_t addr)
+{
+       struct ethhdr *ethhdr = (struct ethhdr *)pack;
+
+       if (ethhdr->h_proto == htons(ETH_P_PPP_DISC))
+               process_pppoe_disc(pack, size);
+       else if (ethhdr->h_proto == htons(ETH_P_PPP_SES))
+               process_pppoe_sess(pack, size);
+       else
+               LOG(0, 0, 0, "pppoe_process_forward: I got a C_PPPOE_FORWARD from %s, but not a PPPOE data?\n", fmtaddr(addr, 0));
+}
diff --git a/pppoe.h b/pppoe.h
new file mode 100644 (file)
index 0000000..62a8208
--- /dev/null
+++ b/pppoe.h
@@ -0,0 +1,23 @@
+
+#ifndef __PPPOE_H__
+#define __PPPOE_H__
+
+#define DEFAULT_PPPOE_AC_NAME  "l2tpns-pppoe"
+
+// pppoe.c
+void init_pppoe(void);
+void process_pppoe_disc(uint8_t *pack, int size);
+void process_pppoe_sess(uint8_t *pack, int size);
+void pppoe_sess_send(const uint8_t *pack, uint16_t l, tunnelidt t);
+uint8_t *pppoe_makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t,
+                                               uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits);
+void pppoe_shutdown_session(sessionidt s);
+void pppoe_forwardto_session_pppoe(uint8_t *pack, int size, sessionidt sess, uint16_t proto);
+void pppoe_process_forward(uint8_t *pack, int size, in_addr_t addr);
+void pppoe_send_garp();
+char * get_string_codepad(uint8_t codepad);
+
+extern int pppoediscfd;                // pppoe discovery socket
+extern int pppoesessfd;        // pppoe session socket
+
+#endif /* __PPPOE_H__ */
index e783323..34806c5 100644 (file)
--- a/radius.c
+++ b/radius.c
@@ -22,6 +22,7 @@
 #ifdef LAC
 #include "l2tplac.h"
 #endif
 #ifdef LAC
 #include "l2tplac.h"
 #endif
+#include "pppoe.h"
 
 extern radiust *radius;
 extern sessiont *session;
 
 extern radiust *radius;
 extern sessiont *session;
diff --git a/util.c b/util.c
index e132be6..d4dbd4f 100644 (file)
--- a/util.c
+++ b/util.c
@@ -28,6 +28,17 @@ char *fmtaddr(in_addr_t addr, int n)
     return strcpy(addrs[n], inet_ntoa(in));
 }
 
     return strcpy(addrs[n], inet_ntoa(in));
 }
 
+char *fmtMacAddr(uint8_t *pMacAddr)
+{
+       static char strMAC[2*ETH_ALEN];
+
+       sprintf(strMAC, "%02X:%02X:%02X:%02X:%02X:%02X",
+                       pMacAddr[0], pMacAddr[1], pMacAddr[2],
+                       pMacAddr[3], pMacAddr[4], pMacAddr[5]);
+
+  return strMAC;
+}
+
 void *shared_malloc(unsigned int size)
 {
     void * p;
 void *shared_malloc(unsigned int size)
 {
     void * p;
diff --git a/util.h b/util.h
index ee066f6..2345860 100644 (file)
--- a/util.h
+++ b/util.h
@@ -2,6 +2,7 @@
 #define __UTIL_H__
 
 char *fmtaddr(in_addr_t addr, int n);
 #define __UTIL_H__
 
 char *fmtaddr(in_addr_t addr, int n);
+char *fmtMacAddr(uint8_t *pMacAddr);
 void *shared_malloc(unsigned int size);
 pid_t fork_and_close(void);
 ssize_t sendtofrom(int s, void const *buf, size_t len, int flags,
 void *shared_malloc(unsigned int size);
 pid_t fork_and_close(void);
 ssize_t sendtofrom(int s, void const *buf, size_t len, int flags,