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
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;
return recvmsg(nlfd, &msg, 0);
}
+/* adapted from iproute2 */
+void netlink_addattr(struct nlmsghdr *nh, int type, const void *data, int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ rta = (struct rtattr *)(((void *)nh) + NLMSG_ALIGN(nh->nlmsg_len));
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), data, alen);
+ nh->nlmsg_len = NLMSG_ALIGN(nh->nlmsg_len) + RTA_ALIGN(len);
+}
+
+// messages corresponding to different phases seq number
+static char *tun_nl_phase_msg[] = {
+ "initialized",
+ "getting tun interface index",
+ "setting tun interface parameters",
+ "setting tun IPv4 address",
+ "setting tun LL IPv6 address",
+ "setting tun global IPv6 address",
+};
+
//
// Set up TUN interface
static void inittun(void)
LOG(0, 0, 0, "Can't set tun interface: %s\n", strerror(errno));
exit(1);
}
- 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) - 1);
+ strncpy(config->tundevice, ifr.ifr_name, sizeof(config->tundevice));
{
// get the interface index
req.nh.nlmsg_type = RTM_GETLINK;
req.nh.nlmsg_flags = NLM_F_REQUEST;
+ req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfo));
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);
+ netlink_addattr(&req.nh, IFLA_IFNAME, config->tundevice, strlen(config->tundevice)+1);
if(netlink_send(&req.nh) < 0 || (len = netlink_recv(buf, sizeof(buf))) < 0)
{
resp_nh = (struct nlmsghdr *)buf;
if (!NLMSG_OK (resp_nh, len))
{
- LOG(0, 0, 0, "Malformed answer getting tun ifindex %d\n", len);
+ LOG(0, 0, 0, "Malformed answer getting tun ifindex %ld\n", len);
exit(1);
}
} 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;
+ uint32_t txqlen, mtu;
+ struct in_addr ip;
memset(&req, 0, sizeof(req));
req.nh.nlmsg_type = RTM_SETLINK;
req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_MULTI;
+ req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifinfo));
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;
+ txqlen = 1000;
+ netlink_addattr(&req.nh, IFLA_TXQLEN, &txqlen, sizeof(txqlen));
/* 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);
+ mtu = MRU;
+ netlink_addattr(&req.nh, IFLA_MTU, &mtu, sizeof(mtu));
if (netlink_send(&req.nh) < 0)
- {
- LOG(0, 0, 0, "Error setting up tun device interface: %s\n", strerror(errno));
- exit(1);
- }
+ goto senderror;
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.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifaddr));
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 (config->bind_address)
+ ip.s_addr = config->bind_address;
+ else
+ ip.s_addr = 0x01010101, // 1.1.1.1
+ netlink_addattr(&req.nh, IFA_LOCAL, &ip, sizeof(ip));
if (netlink_send(&req.nh) < 0)
- {
- LOG(0, 0, 0, "Error setting up tun device IPv4 address: %s\n", strerror(errno));
- exit(1);
- }
+ goto senderror;
// Only setup IPv6 on the tun device if we have a configured prefix
if (config->ipv6_prefix.s6_addr[0]) {
+ struct in6_addr ip6;
+
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.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifaddr));
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);
+ memset(&ip6, 0, sizeof(ip6));
+ ip6.s6_addr[0] = 0xFE;
+ ip6.s6_addr[1] = 0x80;
+ ip6.s6_addr[15] = 1;
+ netlink_addattr(&req.nh, IFA_LOCAL, &ip6, sizeof(ip6));
if (netlink_send(&req.nh) < 0)
- {
- LOG(0, 0, 0, "Error setting up tun device IPv6 LL address: %s\n", strerror(errno));
- exit(1);
- }
+ goto senderror;
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.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifaddr));
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);
+ ip6 = config->ipv6_prefix;
+ ip6.s6_addr[15] = 1;
+ netlink_addattr(&req.nh, IFA_LOCAL, &ip6, sizeof(ip6));
if (netlink_send(&req.nh) < 0)
- {
- LOG(0, 0, 0, "Error setting up tun device IPv6 global address: %s\n", strerror(errno));
- exit(1);
- }
+ goto senderror;
}
memset(&req, 0, sizeof(req));
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);
- }
+ goto senderror;
+
+ // 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 = 5 + 1; // idx + if + addr + 2*addr6
+ else
+ min_initok_nlseqnum = 3 + 1; // idx + if + addr
}
+
+ return;
+
+senderror:
+ LOG(0, 0, 0, "Error while setting up tun device: %s\n", strerror(errno));
+ exit(1);
}
// set up UDP ports
# 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
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 */
{
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
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)
+ {
+ if (errmsg->msg.nlmsg_seq < min_initok_nlseqnum)
+ {
+ LOG(0, 0, 0, "Got a fatal netlink error (while %s): %s\n", tun_nl_phase_msg[nh->nlmsg_seq], strerror(-errmsg->error));
+ exit(1);
+ }
+ else
+
+ LOG(0, 0, 0, "Got a netlink error: %s\n", strerror(-errmsg->error));
+ }
+ // else it's a ack
+ }
+ 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);
}