+//
+// 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);
+}
+
+// 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",