Add netlink fd polling.
[l2tpns.git] / l2tpns.c
index d849711..8517ccf 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -73,6 +73,7 @@ time_t basetime = 0;          // base clock
 char hostname[1000] = "";      // us.
 static 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
 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
@@ -540,7 +541,7 @@ static void initnetlink(void)
        }
 }
 
-static ssize_t netlink_send(struct nlmsghdr *nh, int want_ack)
+static ssize_t netlink_send(struct nlmsghdr *nh)
 {
        struct sockaddr_nl nladdr;
        struct iovec iov;
@@ -548,8 +549,6 @@ static ssize_t netlink_send(struct nlmsghdr *nh, int want_ack)
 
        nh->nlmsg_pid = getpid();
        nh->nlmsg_seq = ++nlseqnum;
-       if (want_ack)
-               nh->nlmsg_flags |= NLM_F_ACK;
 
        // set kernel address
        memset(&nladdr, 0, sizeof(nladdr));
@@ -561,7 +560,7 @@ static ssize_t netlink_send(struct nlmsghdr *nh, int want_ack)
        return sendmsg(nlfd, &msg, 0);
 }
 
-static ssize_t netlink_recv(char *buf, ssize_t len)
+static ssize_t netlink_recv(void *buf, ssize_t len)
 {
        struct sockaddr_nl nladdr;
        struct iovec iov;
@@ -577,50 +576,12 @@ static ssize_t netlink_recv(char *buf, ssize_t len)
        return recvmsg(nlfd, &msg, 0);
 }
 
-static ssize_t netlink_check_ack(struct nlmsghdr *ack_nh, ssize_t len, int msg_count)
-{
-       uint32_t seqnum = 0;
-
-       // expect ack
-       for (; NLMSG_OK (ack_nh, len); ack_nh = NLMSG_NEXT (ack_nh, len))
-       {
-               if (ack_nh->nlmsg_type == NLMSG_DONE)
-                       return msg_count != 0 || seqnum != nlseqnum;
-                       // 1 => lost an 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
-                       else
-                       {
-                               // ack received
-                               --msg_count;
-                               if (errmsg->msg.nlmsg_seq > seqnum);
-                                       seqnum = errmsg->msg.nlmsg_seq;
-                               // if this is a single message, return now
-                               if (!(ack_nh->nlmsg_flags & NLM_F_MULTI))
-                                       return msg_count != 0 || seqnum != nlseqnum;
-                       }
-               }
-               else
-                       // unknown message
-                       LOG(3, 0, 0, "Got an unknown netlink message: type %d\n", ack_nh->nlmsg_type);
-       }
-
-       return 1; // malformed message?!
-}
-
 //
 // Set up TUN interface
 static void inittun(void)
 {
        struct ifinfomsg ifinfo;
        struct ifreq ifr;
-       char buf[4096];
-       ssize_t len;
-       struct nlmsghdr *resp_nh;
 
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_flags = IFF_TUN;
@@ -651,6 +612,9 @@ static void inittun(void)
                        struct rtattr ifname_rta __attribute__ ((aligned(RTA_ALIGNTO)));
                        char ifname[IFNAMSIZ];
                } req;
+               char buf[4096];
+               ssize_t len;
+               struct nlmsghdr *resp_nh;
 
                req.nh.nlmsg_type = RTM_GETLINK;
                req.nh.nlmsg_flags = NLM_F_REQUEST;
@@ -664,7 +628,7 @@ static void inittun(void)
                req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfo)
                                + req.ifname_rta.rta_len);
 
-               if(netlink_send(&req.nh, 0) < 0 || (len = netlink_recv(buf, sizeof(buf))) < 0)
+               if(netlink_send(&req.nh) < 0 || (len = netlink_recv(buf, sizeof(buf))) < 0)
                {
                        LOG(0, 0, 0, "Error getting tun ifindex: %s\n", strerror(errno));
                        exit(1);
@@ -673,7 +637,7 @@ static void inittun(void)
                resp_nh = (struct nlmsghdr *)buf;
                if (!NLMSG_OK (resp_nh, len))
                {
-                       LOG(0, 0, 0, "Malformed answer getting tun ifindex\n");
+                       LOG(0, 0, 0, "Malformed answer getting tun ifindex %ld\n", len);
                        exit(1);
                }
 
@@ -682,7 +646,6 @@ static void inittun(void)
                tunidx = ifinfo.ifi_index;
        }
        {
-               ssize_t err;
                struct {
                        // interface setting
                        struct nlmsghdr nh;
@@ -704,8 +667,6 @@ static void inittun(void)
                        struct rtattr rta;
                        struct in6_addr addr6 __attribute__ ((aligned(RTA_ALIGNTO)));;
                } ipv6_attr;
-               char buf[256];
-               ssize_t buf_len;
                char *buf_ptr;
 
                memset(&req, 0, sizeof(req));
@@ -736,7 +697,7 @@ static void inittun(void)
                req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifinfo)
                                + u32_attr.rta.rta_len * 2);
 
-               if (netlink_send(&req.nh, 1) < 0)
+               if (netlink_send(&req.nh) < 0)
                {
                        LOG(0, 0, 0, "Error setting up tun device interface: %s\n", strerror(errno));
                        exit(1);
@@ -760,16 +721,16 @@ static void inittun(void)
                req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifaddr)
                                + ipv4_attr.rta.rta_len);
 
-               if (netlink_send(&req.nh, 1) < 0)
+               if (netlink_send(&req.nh) < 0)
                {
                        LOG(0, 0, 0, "Error setting up tun device IPv4 address: %s\n", strerror(errno));
                        exit(1);
                }
 
-               memset(&req, 0, sizeof(req));
-
                // Only setup IPv6 on the tun device if we have a configured prefix
                if (config->ipv6_prefix.s6_addr[0]) {
+                       memset(&req, 0, sizeof(req));
+
                        req.nh.nlmsg_type = RTM_NEWADDR;
                        req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_MULTI;
 
@@ -790,7 +751,7 @@ static void inittun(void)
                        req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifaddr)
                                        + ipv6_attr.rta.rta_len);
 
-                       if (netlink_send(&req.nh, 1) < 0)
+                       if (netlink_send(&req.nh) < 0)
                        {
                                LOG(0, 0, 0, "Error setting up tun device IPv6 LL address: %s\n", strerror(errno));
                                exit(1);
@@ -816,50 +777,30 @@ static void inittun(void)
                        req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifaddr)
                                        + ipv6_attr.rta.rta_len);
 
-                       if (netlink_send(&req.nh, 1) < 0)
+                       if (netlink_send(&req.nh) < 0)
                        {
                                LOG(0, 0, 0, "Error setting up tun device IPv6 global address: %s\n", strerror(errno));
                                exit(1);
                        }
-
-                       memset(&req, 0, sizeof(req));
                }
 
+               memset(&req, 0, sizeof(req));
+
                req.nh.nlmsg_type = NLMSG_DONE;
                req.nh.nlmsg_len = NLMSG_LENGTH(0);
 
-               if (netlink_send(&req.nh, 1) < 0)
+               if (netlink_send(&req.nh) < 0)
                {
                        LOG(0, 0, 0, "Error finishing setting up tun device: %s\n", strerror(errno));
                        exit(1);
                }
 
-               buf_ptr = buf;
-               buf_len = sizeof(buf);
-               do
-               {
-                       len = netlink_recv(buf_ptr, sizeof(buf_len));
-                       if (len < 0)
-                       {
-                               LOG(0, 0, 0, "Error waiting for ack setting up tun device: %s\n", strerror(errno));
-                               exit(1);
-                       }
-
-                       resp_nh = (struct nlmsghdr *)buf_ptr;
-                       buf_len -= len;
-                       buf_ptr += len;
-               }
-               while (buf_len > 0 && (resp_nh->nlmsg_flags & NLM_F_MULTI)
-                               && resp_nh->nlmsg_type != NLMSG_DONE);
-
-               if ((err = netlink_check_ack((struct nlmsghdr *)buf, buf_len, 4)))
-               {
-                       if (err < 0)
-                               LOG(0, 0, 0, "Error while receiving tun device ack: %s\n", strerror(errno));
-                       else
-                               LOG(0, 0, 0, "Error while receiving tun device ack\n");
-                       exit(1);
-               }
+               // if we get an error for seqnum < min_initok_nlseqnum,
+               // we must exit as initialization went wrong
+               if (config->ipv6_prefix.s6_addr[0])
+                       min_initok_nlseqnum = 3 + 1; // idx + if + addr
+               else
+                       min_initok_nlseqnum = 5 + 1; // idx + if + addr + 2*addr6
        }
 }
 
@@ -3736,8 +3677,8 @@ static int still_busy(void)
 # include "fake_epoll.h"
 #endif
 
-// the base set of fds polled: cli, cluster, tun, udp, control, dae
-#define BASE_FDS       6
+// the base set of fds polled: cli, cluster, tun, udp, control, dae, netlink
+#define BASE_FDS       7
 
 // additional polled fds
 #ifdef BGP
@@ -3761,8 +3702,8 @@ static void mainloop(void)
                exit(1);
        }
 
-       LOG(4, 0, 0, "Beginning of main loop.  clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d\n",
-               clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd);
+       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);
 
        /* setup our fds to poll for input */
        {
@@ -3798,6 +3739,10 @@ static void mainloop(void)
                d[i].type = FD_TYPE_DAE;
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, daefd, &e);
+
+               d[i].type = FD_TYPE_NETLINK;
+               e.data.ptr = &d[i++];
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, nlfd, &e);
        }
 
 #ifdef BGP
@@ -3934,6 +3879,27 @@ static void mainloop(void)
                                        break;
 #endif /* BGP */
 
+                               case FD_TYPE_NETLINK:
+                               {
+                                       struct nlmsghdr *nh = (struct nlmsghdr *)buf;
+                                       s = netlink_recv(buf, sizeof(buf));
+                                       if (nh->nlmsg_type == NLMSG_ERROR)
+                                       {
+                                               struct nlmsgerr *errmsg = NLMSG_DATA(nh);
+                                               if (errmsg->error && errmsg->msg.nlmsg_seq < min_initok_nlseqnum)
+                                               {
+                                                       LOG(0, 0, 0, "Got a fatal netlink error: seq %d flags %d code %d\n", nh->nlmsg_seq, nh->nlmsg_flags, errmsg->error);
+                                                       exit(1);
+                                               }
+                                               else
+                                                       LOG(1, 0, 0, "Got a netlink error: seq %d flags %d code %d\n", nh->nlmsg_seq, nh->nlmsg_flags, errmsg->error);
+                                       }
+                                       else
+                                               LOG(1, 0, 0, "Got a unknown netlink message: type %d seq %d flags %d\n", nh->nlmsg_type, nh->nlmsg_seq, nh->nlmsg_flags);
+                                       n--;
+                                       break;
+                               }
+
                                default:
                                        LOG(0, 0, 0, "Unexpected fd type returned from epoll_wait: %d\n", d->type);
                                }