X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/448f218ae7174d5053f8d96af75485616e82955d..7bdb42fe548e6b2f13b6c1d7b6cb47656639787d:/l2tpns.c diff --git a/l2tpns.c b/l2tpns.c index 72876be..8517ccf 100644 --- 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 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.175 2010/03/25 05:24:23 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.176 2011/01/20 12:48:40 bodea Exp $"; #include #include @@ -39,6 +39,8 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.175 2010/03/25 05:24:23 bodea Exp #include #include #include +#include +#include #include "md5.h" #include "l2tpns.h" @@ -56,6 +58,7 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.175 2010/03/25 05:24:23 bodea Exp // 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 @@ -63,14 +66,14 @@ int clifd = -1; // Socket listening for CLI connections. int daefd = -1; // Socket listening for DAE connections. int snoopfd = -1; // UDP file handle for sending out intercept data int *radfds = NULL; // RADIUS requests file handles -int ifrfd = -1; // File descriptor for routing, etc -int ifr6fd = -1; // File descriptor for IPv6 routing, etc int rand_fd = -1; // Random data source int cluster_sockfd = -1; // Intra-cluster communications socket. int epollfd = -1; // event polling 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 @@ -514,21 +517,72 @@ void route6set(sessionidt s, struct in6_addr ip, int prefixlen, int add) return; } -// defined in linux/ipv6.h, but tricky to include from user-space -// TODO: move routing to use netlink rather than ioctl -struct in6_ifreq { - struct in6_addr ifr6_addr; - __u32 ifr6_prefixlen; - unsigned int ifr6_ifindex; -}; +// +// Set up netlink socket +static void initnetlink(void) +{ + struct sockaddr_nl nladdr; + + nlfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (nlfd < 0) + { + LOG(0, 0, 0, "Can't create netlink socket: %s\n", strerror(errno)); + exit(1); + } + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = getpid(); + + if (bind(nlfd, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) + { + LOG(0, 0, 0, "Can't bind netlink socket: %s\n", strerror(errno)); + exit(1); + } +} + +static ssize_t netlink_send(struct nlmsghdr *nh) +{ + struct sockaddr_nl nladdr; + struct iovec iov; + struct msghdr msg; + + nh->nlmsg_pid = getpid(); + nh->nlmsg_seq = ++nlseqnum; + + // set kernel address + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + iov = (struct iovec){ (void *)nh, nh->nlmsg_len }; + msg = (struct msghdr){ (void *)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 }; + + return sendmsg(nlfd, &msg, 0); +} + +static ssize_t netlink_recv(void *buf, ssize_t len) +{ + struct sockaddr_nl nladdr; + struct iovec iov; + struct msghdr msg; + + // set kernel address + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + iov = (struct iovec){ buf, len }; + msg = (struct msghdr){ (void *)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 }; + + return recvmsg(nlfd, &msg, 0); +} // // Set up TUN interface static void inittun(void) { + struct ifinfomsg ifinfo; struct ifreq ifr; - struct in6_ifreq ifr6; - struct sockaddr_in sin = {0}; + memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN; @@ -549,72 +603,204 @@ static void inittun(void) } assert(strlen(ifr.ifr_name) < sizeof(config->tundevice)); strncpy(config->tundevice, ifr.ifr_name, sizeof(config->tundevice) - 1); - ifrfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); - - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = config->bind_address ? config->bind_address : 0x01010101; // 1.1.1.1 - memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); - if (ioctl(ifrfd, SIOCSIFADDR, (void *) &ifr) < 0) { - LOG(0, 0, 0, "Error setting tun address: %s\n", strerror(errno)); - exit(1); - } - /* Bump up the qlen to deal with bursts from the network */ - ifr.ifr_qlen = 1000; - if (ioctl(ifrfd, SIOCSIFTXQLEN, (void *) &ifr) < 0) - { - LOG(0, 0, 0, "Error setting tun queue length: %s\n", strerror(errno)); - exit(1); - } - /* set MTU to modem MRU */ - ifr.ifr_mtu = MRU; - if (ioctl(ifrfd, SIOCSIFMTU, (void *) &ifr) < 0) - { - LOG(0, 0, 0, "Error setting tun MTU: %s\n", strerror(errno)); - exit(1); - } - ifr.ifr_flags = IFF_UP; - if (ioctl(ifrfd, SIOCSIFFLAGS, (void *) &ifr) < 0) - { - LOG(0, 0, 0, "Error setting tun flags: %s\n", strerror(errno)); - exit(1); + // get the interface index + struct { + struct nlmsghdr nh; + struct ifinfomsg ifinfo __attribute__ ((aligned(NLMSG_ALIGNTO))); + 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; + + req.ifinfo.ifi_family = AF_UNSPEC; // as the man says + + req.ifname_rta.rta_len = RTA_LENGTH(strlen(config->tundevice)+1); + req.ifname_rta.rta_type = IFLA_IFNAME; + strncpy(req.ifname, config->tundevice, IFNAMSIZ-1); + + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfo) + + req.ifname_rta.rta_len); + + 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); + } + + resp_nh = (struct nlmsghdr *)buf; + if (!NLMSG_OK (resp_nh, len)) + { + LOG(0, 0, 0, "Malformed answer getting tun ifindex %ld\n", len); + exit(1); + } + + memcpy(&ifinfo, NLMSG_DATA(resp_nh), sizeof(ifinfo)); + // got index + tunidx = ifinfo.ifi_index; } - if (ioctl(ifrfd, SIOCGIFINDEX, (void *) &ifr) < 0) { - LOG(0, 0, 0, "Error getting tun ifindex: %s\n", strerror(errno)); - exit(1); - } - tunidx = ifr.ifr_ifindex; + struct { + // interface setting + struct nlmsghdr nh; + union { + struct ifinfomsg ifinfo; + struct ifaddrmsg ifaddr; + } ifmsg __attribute__ ((aligned(NLMSG_ALIGNTO))); + char rtdata[32]; // 32 should be enough + } req; + struct { + struct rtattr rta; + uint32_t u32 __attribute__ ((aligned(RTA_ALIGNTO)));; + } u32_attr; + struct { + struct rtattr rta; + struct in_addr addr __attribute__ ((aligned(RTA_ALIGNTO)));; + } ipv4_attr; + struct { + struct rtattr rta; + struct in6_addr addr6 __attribute__ ((aligned(RTA_ALIGNTO)));; + } ipv6_attr; + char *buf_ptr; + + 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]) { - ifr6fd = socket(PF_INET6, SOCK_DGRAM, 0); + req.nh.nlmsg_type = RTM_SETLINK; + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_MULTI; - // Link local address is FE80::1 - memset(&ifr6.ifr6_addr, 0, sizeof(ifr6.ifr6_addr)); - ifr6.ifr6_addr.s6_addr[0] = 0xFE; - ifr6.ifr6_addr.s6_addr[1] = 0x80; - ifr6.ifr6_addr.s6_addr[15] = 1; - ifr6.ifr6_prefixlen = 64; - ifr6.ifr6_ifindex = ifr.ifr_ifindex; - if (ioctl(ifr6fd, SIOCSIFADDR, (void *) &ifr6) < 0) + req.ifmsg.ifinfo = ifinfo; + req.ifmsg.ifinfo.ifi_flags |= IFF_UP; // set interface up + req.ifmsg.ifinfo.ifi_change = IFF_UP; // only change this flag + + buf_ptr = NLMSG_DATA(&req.nh) + sizeof(req.ifmsg.ifinfo); + + u32_attr.rta.rta_len = RTA_LENGTH(sizeof(u32_attr.u32)); + u32_attr.rta.rta_type = IFLA_TXQLEN; + /* Bump up the qlen to deal with bursts from the network */ + u32_attr.u32 = 1000; + memcpy(buf_ptr, &u32_attr, sizeof(u32_attr)); + + buf_ptr += RTA_ALIGN(u32_attr.rta.rta_len); + + u32_attr.rta.rta_len = RTA_LENGTH(sizeof(u32_attr.u32)); + u32_attr.rta.rta_type = IFLA_MTU; + /* set MTU to modem MRU */ + u32_attr.u32 = MRU; + memcpy(buf_ptr, &u32_attr, sizeof(u32_attr)); + + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifinfo) + + u32_attr.rta.rta_len * 2); + + if (netlink_send(&req.nh) < 0) { - LOG(0, 0, 0, "Error setting tun IPv6 link local address:" - " %s\n", strerror(errno)); + LOG(0, 0, 0, "Error setting up tun device interface: %s\n", strerror(errno)); + exit(1); } - // Global address is prefix::1 - memset(&ifr6.ifr6_addr, 0, sizeof(ifr6.ifr6_addr)); - ifr6.ifr6_addr = config->ipv6_prefix; - ifr6.ifr6_addr.s6_addr[15] = 1; - ifr6.ifr6_prefixlen = 64; - ifr6.ifr6_ifindex = ifr.ifr_ifindex; - if (ioctl(ifr6fd, SIOCSIFADDR, (void *) &ifr6) < 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; + + req.ifmsg.ifaddr.ifa_family = AF_INET; + req.ifmsg.ifaddr.ifa_prefixlen = 32; + req.ifmsg.ifaddr.ifa_index = ifinfo.ifi_index; + + ipv4_attr.rta.rta_len = RTA_LENGTH(sizeof(ipv4_attr.addr)); + ipv4_attr.rta.rta_type = IFA_ADDRESS; + ipv4_attr.addr.s_addr = config->bind_address ? + config->bind_address : 0x01010101; // 1.1.1.1 + memcpy(NLMSG_DATA(&req.nh) + sizeof(req.ifmsg.ifaddr), &ipv4_attr, sizeof(ipv4_attr)); + + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifaddr) + + ipv4_attr.rta.rta_len); + + if (netlink_send(&req.nh) < 0) { - LOG(0, 0, 0, "Error setting tun IPv6 global address: %s\n", - strerror(errno)); + LOG(0, 0, 0, "Error setting up tun device IPv4 address: %s\n", strerror(errno)); + exit(1); } + + // 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; + + req.ifmsg.ifaddr.ifa_family = AF_INET6; + req.ifmsg.ifaddr.ifa_prefixlen = 64; + req.ifmsg.ifaddr.ifa_scope = RT_SCOPE_LINK; + req.ifmsg.ifaddr.ifa_index = ifinfo.ifi_index; + + ipv6_attr.rta.rta_len = RTA_LENGTH(sizeof(ipv6_attr.addr6)); + ipv6_attr.rta.rta_type = IFA_LOCAL; + // Link local address is FE80::1 + memset(&ipv6_attr.addr6, 0, sizeof(ipv6_attr.addr6)); + ipv6_attr.addr6.s6_addr[0] = 0xFE; + ipv6_attr.addr6.s6_addr[1] = 0x80; + ipv6_attr.addr6.s6_addr[15] = 1; + memcpy(NLMSG_DATA(&req.nh) + sizeof(req.ifmsg.ifaddr), &ipv6_attr, sizeof(ipv6_attr)); + + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifaddr) + + ipv6_attr.rta.rta_len); + + if (netlink_send(&req.nh) < 0) + { + LOG(0, 0, 0, "Error setting up tun device IPv6 LL address: %s\n", strerror(errno)); + exit(1); + } + + memset(&req, 0, sizeof(req)); + + req.nh.nlmsg_type = RTM_NEWADDR; + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_MULTI; + + req.ifmsg.ifaddr.ifa_family = AF_INET6; + req.ifmsg.ifaddr.ifa_prefixlen = 64; + req.ifmsg.ifaddr.ifa_scope = RT_SCOPE_UNIVERSE; + req.ifmsg.ifaddr.ifa_index = ifinfo.ifi_index; + + ipv6_attr.rta.rta_len = RTA_LENGTH(sizeof(ipv6_attr.addr6)); + ipv6_attr.rta.rta_type = IFA_LOCAL; + // Global address is prefix::1 + ipv6_attr.addr6 = config->ipv6_prefix; + ipv6_attr.addr6.s6_addr[15] = 1; + memcpy(NLMSG_DATA(&req.nh) + sizeof(req.ifmsg.ifaddr), &ipv6_attr, sizeof(ipv6_attr)); + + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifaddr) + + ipv6_attr.rta.rta_len); + + 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)); + + req.nh.nlmsg_type = NLMSG_DONE; + req.nh.nlmsg_len = NLMSG_LENGTH(0); + + if (netlink_send(&req.nh) < 0) + { + LOG(0, 0, 0, "Error finishing setting up tun device: %s\n", strerror(errno)); + 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 } } @@ -3033,7 +3219,7 @@ static void regular_cleanups(double period) 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 @@ -3491,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 @@ -3516,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 */ { @@ -3553,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 @@ -3689,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); } @@ -4530,6 +4741,8 @@ int main(int argc, char *argv[]) } } + initnetlink(); + /* Set up the cluster communications port. */ if (cluster_init() < 0) exit(1);