First netlink functions.
[l2tpns.git] / l2tpns.c
index f47ae66..af19f9a 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -4,7 +4,7 @@
 // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced
 // vim: sw=8 ts=8
 
 // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced
 // vim: sw=8 ts=8
 
-char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.173 2009/12/08 14:49:28 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.176 2011/01/20 12:48:40 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -39,6 +39,7 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.173 2009/12/08 14:49:28 bodea Exp
 #include <sched.h>
 #include <sys/sysinfo.h>
 #include <libcli.h>
 #include <sched.h>
 #include <sys/sysinfo.h>
 #include <libcli.h>
+#include <linux/netlink.h>
 
 #include "md5.h"
 #include "l2tpns.h"
 
 #include "md5.h"
 #include "l2tpns.h"
@@ -56,6 +57,7 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.173 2009/12/08 14:49:28 bodea Exp
 
 // Globals
 configt *config = NULL;                // all configuration
 
 // Globals
 configt *config = NULL;                // all configuration
+int nlfd = -1;                 // netlink socket
 int tunfd = -1;                        // tun interface file handle. (network device)
 int udpfd = -1;                        // UDP file handle
 int controlfd = -1;            // Control signal handle
 int tunfd = -1;                        // tun interface file handle. (network device)
 int udpfd = -1;                        // UDP file handle
 int controlfd = -1;            // Control signal handle
@@ -71,6 +73,7 @@ int epollfd = -1;             // event polling
 time_t basetime = 0;           // base clock
 char hostname[1000] = "";      // us.
 static int tunidx;             // ifr_ifindex of tun device
 time_t basetime = 0;           // base clock
 char hostname[1000] = "";      // us.
 static int tunidx;             // ifr_ifindex of tun device
+int nlseqnum = 0;              // netlink sequence number
 static int syslog_log = 0;     // are we logging to syslog
 static FILE *log_stream = 0;   // file handle for direct logging (i.e. direct into file, not via syslog).
 uint32_t last_id = 0;          // Unique ID for radius accounting
 static int syslog_log = 0;     // are we logging to syslog
 static FILE *log_stream = 0;   // file handle for direct logging (i.e. direct into file, not via syslog).
 uint32_t last_id = 0;          // Unique ID for radius accounting
@@ -514,6 +517,79 @@ void route6set(sessionidt s, struct in6_addr ip, int prefixlen, int add)
        return;
 }
 
        return;
 }
 
+//
+// Set up netlink socket
+static void initnetlink(void)
+{
+       struct sockaddr_nl nladdr;
+
+       nlfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+       if (nlfd < 0)
+               return; // don't use netlink
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+       nladdr.nl_pid = getpid();
+
+       if (bind(fd, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0)
+       {
+               close(fd);
+               fd = -1;
+               return; // don't use netlink
+       }
+}
+
+static ssize_t netlink_send(struct nlmsghdr *nh)
+{
+       struct sockaddr_nl nladdr;
+       struct iovec iov;
+       struct msghdr msg;
+       ssize_t len;
+       char buf[4096];
+       struct nlmsghdr *ack_nh;
+
+       nh->nlmsg_pid = getpid();
+       nh->nlmsg_seq = ++nlseqnum;
+       nh->nlmsg_flags |= NLM_F_ACK;
+
+       iov = { (void *)nh, nh->msg_len };
+       // set kernel address
+       memset(nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+       msg = { (void *)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 };
+
+       if ((len = sendmsg(nlfd, &msg, 0)) < 0)
+               return len; // return error code
+
+       // expect ack
+       iov = { buf, sizeof(buf) };
+       msg = { (void *)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 };
+       if ((len = recvmsg(nlfd, &msg, 0)) < 0)
+               return len; // return error code
+
+       for (ack_nh = (struct nlmsghdr *)buf; NLMSG_OK (ack_nh, len);
+                       ack_nh = NLMSG_NEXT (ack_nh, len))
+       {
+               if (ack_nh->nlmsg_type == NLMSG_DONE)
+                       return 1; // didn't get the ack
+
+               if (ack_nh->nlmsg_type == NLMSG_ERROR)
+               {
+                       struct nlmsgerr *errmsg = NLMSG_DATA(ack_nh);
+                       if (errmsg->error)
+                               return errmsg->error; // got an error back
+                       if (errmsg->msg.nlmsg_seq == nh->nlmsg_seq)
+                               return 0; // ack received
+                       else
+                               return 2; // wrong seq number?!
+               }
+               // unknown message
+               LOG(3, 0, 0, "Got an unknown netlink message: type %d\n", ack_nh->nlmsg_type);
+       }
+
+       return 3; // no end message?!
+}
+
 // defined in linux/ipv6.h, but tricky to include from user-space
 // TODO: move routing to use netlink rather than ioctl
 struct in6_ifreq {
 // defined in linux/ipv6.h, but tricky to include from user-space
 // TODO: move routing to use netlink rather than ioctl
 struct in6_ifreq {
@@ -549,6 +625,14 @@ static void inittun(void)
        }
        assert(strlen(ifr.ifr_name) < sizeof(config->tundevice));
        strncpy(config->tundevice, ifr.ifr_name, sizeof(config->tundevice) - 1);
        }
        assert(strlen(ifr.ifr_name) < sizeof(config->tundevice));
        strncpy(config->tundevice, ifr.ifr_name, sizeof(config->tundevice) - 1);
+       {
+               struct nlmsghdr nlh;
+
+               {
+                       LOG(0, 0, 0, "Error setting up tun device: %s", strerror(errno));
+                       exit(1);
+               }
+       }
        ifrfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
 
        sin.sin_family = AF_INET;
        ifrfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
 
        sin.sin_family = AF_INET;
@@ -1730,6 +1814,7 @@ 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);
                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)
        }
 
        if (session[s].ip && !walled_garden && !session[s].die)
@@ -1832,9 +1917,6 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e
                controladd(c, session[s].far, session[s].tunnel); // send the message
        }
 
                controladd(c, session[s].far, session[s].tunnel); // send the message
        }
 
-       if (!session[s].die)
-               session[s].die = TIME + 150; // Clean up in 15 seconds
-
        // update filter refcounts
        if (session[s].filter_in) ip_filters[session[s].filter_in - 1].used--;
        if (session[s].filter_out) ip_filters[session[s].filter_out - 1].used--;
        // update filter refcounts
        if (session[s].filter_in) ip_filters[session[s].filter_in - 1].used--;
        if (session[s].filter_out) ip_filters[session[s].filter_out - 1].used--;
@@ -1930,8 +2012,9 @@ void sessionkill(sessionidt s, char *reason)
                return;
        }
 
                return;
        }
 
-       session[s].die = TIME;
-       sessionshutdown(s, reason, CDN_ADMIN_DISC, TERM_ADMIN_RESET);  // close radius/routes, etc.
+       if (!session[s].die)
+               sessionshutdown(s, reason, CDN_ADMIN_DISC, TERM_ADMIN_RESET);  // close radius/routes, etc.
+
        if (sess_local[s].radius)
                radiusclear(sess_local[s].radius, s); // cant send clean accounting data, session is killed
 
        if (sess_local[s].radius)
                radiusclear(sess_local[s].radius, s); // cant send clean accounting data, session is killed
 
@@ -2687,9 +2770,11 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        break;
                                case 2:       // SCCRP
                                        tunnel[t].state = TUNNELOPEN;
                                        break;
                                case 2:       // SCCRP
                                        tunnel[t].state = TUNNELOPEN;
+                                       tunnel[t].lastrec = time_now;
                                        break;
                                case 3:       // SCCN
                                        tunnel[t].state = TUNNELOPEN;
                                        break;
                                case 3:       // SCCN
                                        tunnel[t].state = TUNNELOPEN;
+                                       tunnel[t].lastrec = time_now;
                                        controlnull(t); // ack
                                        break;
                                case 4:       // StopCCN
                                        controlnull(t); // ack
                                        break;
                                case 4:       // StopCCN
@@ -3032,7 +3117,7 @@ static void regular_cleanups(double period)
                        if (tunnel[t].retry <= TIME)
                        {
                                controlt *c = tunnel[t].controls;
                        if (tunnel[t].retry <= TIME)
                        {
                                controlt *c = tunnel[t].controls;
-                               uint8_t w = tunnel[t].window;
+                               uint16_t w = tunnel[t].window;
                                tunnel[t].try++; // another try
                                if (tunnel[t].try > 5)
                                        tunnelkill(t, "Timeout on control message"); // game over
                                tunnel[t].try++; // another try
                                if (tunnel[t].try > 5)
                                        tunnelkill(t, "Timeout on control message"); // game over