|
|
|
|
@ -46,18 +46,37 @@ struct nl_req {
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static int netlink_route(int ifindex, uint32_t network, uint8_t prefix_len, int cmd, int flags) { |
|
|
|
|
int fd = socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, NETLINK_ROUTE); |
|
|
|
|
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
|
|
|
|
if (fd < 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create netlink socket: %s", strerror(errno)); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Bind to receive responses
|
|
|
|
|
struct sockaddr_nl addr; |
|
|
|
|
memset(&addr, 0, sizeof(addr)); |
|
|
|
|
addr.nl_family = AF_NETLINK; |
|
|
|
|
addr.nl_pid = getpid(); |
|
|
|
|
addr.nl_groups = 0; |
|
|
|
|
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to bind netlink socket: %s", strerror(errno)); |
|
|
|
|
close(fd); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Prepare destination address (kernel)
|
|
|
|
|
struct sockaddr_nl dest; |
|
|
|
|
memset(&dest, 0, sizeof(dest)); |
|
|
|
|
dest.nl_family = AF_NETLINK; |
|
|
|
|
dest.nl_pid = 0; // kernel
|
|
|
|
|
dest.nl_groups = 0; |
|
|
|
|
|
|
|
|
|
struct nl_req req; |
|
|
|
|
memset(&req, 0, sizeof(req)); |
|
|
|
|
|
|
|
|
|
req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); |
|
|
|
|
req.nl.nlmsg_type = cmd; // RTM_NEWROUTE or RTM_DELROUTE
|
|
|
|
|
req.nl.nlmsg_flags = flags | NLM_F_REQUEST; |
|
|
|
|
req.nl.nlmsg_flags = flags | NLM_F_REQUEST | NLM_F_ACK; // Request ACK from kernel
|
|
|
|
|
req.nl.nlmsg_seq = 1; |
|
|
|
|
req.nl.nlmsg_pid = getpid(); |
|
|
|
|
|
|
|
|
|
@ -82,12 +101,15 @@ static int netlink_route(int ifindex, uint32_t network, uint8_t prefix_len, int
|
|
|
|
|
memcpy(RTA_DATA(rta), &ifindex, 4); |
|
|
|
|
req.nl.nlmsg_len = NLMSG_ALIGN(req.nl.nlmsg_len) + RTA_ALIGN(rta->rta_len); |
|
|
|
|
|
|
|
|
|
if (send(fd, &req, req.nl.nlmsg_len, 0) < 0) { |
|
|
|
|
if (sendto(fd, &req, req.nl.nlmsg_len, 0, (struct sockaddr *)&dest, sizeof(dest)) < 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to send netlink message: %s", strerror(errno)); |
|
|
|
|
close(fd); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Netlink message sent: seq=%u pid=%u len=%d", |
|
|
|
|
req.nl.nlmsg_seq, req.nl.nlmsg_pid, req.nl.nlmsg_len); |
|
|
|
|
|
|
|
|
|
// Wait for response with timeout using poll
|
|
|
|
|
struct pollfd pfd = { .fd = fd, .events = POLLIN }; |
|
|
|
|
int poll_ret = poll(&pfd, 1, 1000); // 1 second timeout
|
|
|
|
|
@ -96,11 +118,13 @@ static int netlink_route(int ifindex, uint32_t network, uint8_t prefix_len, int
|
|
|
|
|
close(fd); |
|
|
|
|
return -1; |
|
|
|
|
} else if (poll_ret == 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Timeout waiting for netlink response"); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Timeout waiting for netlink response (sent %d bytes)", req.nl.nlmsg_len); |
|
|
|
|
close(fd); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Poll returned: revents=%d", pfd.revents); |
|
|
|
|
|
|
|
|
|
// Receive response
|
|
|
|
|
char reply[4096]; |
|
|
|
|
ssize_t len = recv(fd, reply, sizeof(reply), 0); |
|
|
|
|
@ -110,7 +134,12 @@ static int netlink_route(int ifindex, uint32_t network, uint8_t prefix_len, int
|
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Received netlink response: len=%d", (int)len); |
|
|
|
|
|
|
|
|
|
struct nlmsghdr *nl_hdr = (struct nlmsghdr *)reply; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Netlink msg: type=%d seq=%d pid=%d", |
|
|
|
|
nl_hdr->nlmsg_type, nl_hdr->nlmsg_seq, nl_hdr->nlmsg_pid); |
|
|
|
|
|
|
|
|
|
if (nl_hdr->nlmsg_type == NLMSG_ERROR) { |
|
|
|
|
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nl_hdr); |
|
|
|
|
if (err->error < 0) { |
|
|
|
|
@ -138,44 +167,42 @@ static int ip_route_cmd(const char *ifname, uint32_t network, uint8_t prefix_len
|
|
|
|
|
return (ret == 0) ? 0 : -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int tun_route_add(uint32_t ifindex, uint32_t network, uint8_t prefix_len) { |
|
|
|
|
if (ifindex == 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Interface index is 0"); |
|
|
|
|
int tun_route_add(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) { |
|
|
|
|
if (ifindex == 0 && (!ifname || !ifname[0])) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Interface index and name are both invalid"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
network = htonl(network); |
|
|
|
|
|
|
|
|
|
// Try netlink first
|
|
|
|
|
if (netlink_route(ifindex, network, prefix_len, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL) == 0) { |
|
|
|
|
if (ifindex != 0 && netlink_route(ifindex, network, prefix_len, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL) == 0) { |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Fallback to ip route command
|
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Netlink failed, trying ip route command for ifindex %u", ifindex); |
|
|
|
|
char ifname[16]; |
|
|
|
|
snprintf(ifname, sizeof(ifname), "%u", ifindex); |
|
|
|
|
return ip_route_cmd(ifname, ntohl(network), prefix_len, "add"); |
|
|
|
|
const char *dev = ifname ? ifname : ""; |
|
|
|
|
return ip_route_cmd(dev, ntohl(network), prefix_len, "add"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int tun_route_del(uint32_t ifindex, uint32_t network, uint8_t prefix_len) { |
|
|
|
|
if (ifindex == 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_route_del: ifindex is 0"); |
|
|
|
|
int tun_route_del(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) { |
|
|
|
|
if (ifindex == 0 && (!ifname || !ifname[0])) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_route_del: ifindex and ifname are both invalid"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
network = htonl(network); |
|
|
|
|
|
|
|
|
|
// Try netlink first
|
|
|
|
|
if (netlink_route(ifindex, network, prefix_len, RTM_DELROUTE, 0) == 0) { |
|
|
|
|
if (ifindex != 0 && netlink_route(ifindex, network, prefix_len, RTM_DELROUTE, 0) == 0) { |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Fallback to ip route command
|
|
|
|
|
char ifname[16]; |
|
|
|
|
snprintf(ifname, sizeof(ifname), "%u", ifindex); |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Netlink failed, trying ip route command for ifindex %u", ifindex); |
|
|
|
|
return ip_route_cmd(ifname, ntohl(network), prefix_len, "del"); |
|
|
|
|
const char *dev = ifname ? ifname : ""; |
|
|
|
|
return ip_route_cmd(dev, ntohl(network), prefix_len, "del"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int tun_route_flush(const char *ifname) { |
|
|
|
|
@ -196,11 +223,30 @@ int tun_route_flush(const char *ifname) {
|
|
|
|
|
return (ret == 0) ? 0 : -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int tun_route_del_all(uint32_t ifindex, const char *ifname, struct CFG_ROUTE_ENTRY *routes) { |
|
|
|
|
int count = 0; |
|
|
|
|
struct CFG_ROUTE_ENTRY *entry = routes; |
|
|
|
|
|
|
|
|
|
while (entry) { |
|
|
|
|
uint32_t network = ntohl(entry->ip.addr.v4.s_addr); |
|
|
|
|
if (entry->ip.family == AF_INET) { |
|
|
|
|
if (tun_route_del(ifindex, ifname, network, entry->netmask) == 0) { |
|
|
|
|
count++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
entry = entry->next; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Deleted %d routes via ifindex=%lu", count, (unsigned long)ifindex); |
|
|
|
|
return count; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#elif defined(_WIN32) |
|
|
|
|
|
|
|
|
|
// Windows IP Helper API implementation
|
|
|
|
|
|
|
|
|
|
int tun_route_add(uint32_t ifindex, uint32_t network, uint8_t prefix_len) { |
|
|
|
|
int tun_route_add(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) { |
|
|
|
|
(void)ifname; // Not used on Windows
|
|
|
|
|
if (ifindex == 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_route_add: ifindex is 0"); |
|
|
|
|
return -1; |
|
|
|
|
@ -248,7 +294,8 @@ int tun_route_add(uint32_t ifindex, uint32_t network, uint8_t prefix_len) {
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int tun_route_del(uint32_t ifindex, uint32_t network, uint8_t prefix_len) { |
|
|
|
|
int tun_route_del(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) { |
|
|
|
|
(void)ifname; // Not used on Windows
|
|
|
|
|
if (ifindex == 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_route_del: ifindex is 0"); |
|
|
|
|
return -1; |
|
|
|
|
@ -318,14 +365,14 @@ int tun_route_flush(const char *ifname) {
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int tun_route_del_all(uint32_t ifindex, struct CFG_ROUTE_ENTRY *routes) { |
|
|
|
|
int tun_route_del_all(uint32_t ifindex, const char *ifname, struct CFG_ROUTE_ENTRY *routes) { |
|
|
|
|
int count = 0; |
|
|
|
|
struct CFG_ROUTE_ENTRY *entry = routes; |
|
|
|
|
|
|
|
|
|
while (entry) { |
|
|
|
|
uint32_t network = ntohl(entry->ip.addr.v4.s_addr); |
|
|
|
|
if (entry->ip.family == AF_INET) { |
|
|
|
|
if (tun_route_del(ifindex, network, entry->netmask) == 0) { |
|
|
|
|
if (tun_route_del(ifindex, ifname, network, entry->netmask) == 0) { |
|
|
|
|
count++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -415,40 +462,38 @@ static int route_cmd(const char *ifname, uint32_t network, uint8_t prefix_len, c
|
|
|
|
|
return (ret == 0) ? 0 : -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int tun_route_add(uint32_t ifindex, uint32_t network, uint8_t prefix_len) { |
|
|
|
|
if (ifindex == 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Interface index is 0"); |
|
|
|
|
int tun_route_add(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) { |
|
|
|
|
if (ifindex == 0 && (!ifname || !ifname[0])) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Interface index and name are both invalid"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Try routing socket first
|
|
|
|
|
if (routing_socket_cmd(ifindex, network, prefix_len, RTM_ADD) == 0) { |
|
|
|
|
if (ifindex != 0 && routing_socket_cmd(ifindex, network, prefix_len, RTM_ADD) == 0) { |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Fallback to route command
|
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Routing socket failed, trying route command for ifindex %u", ifindex); |
|
|
|
|
char ifname[16]; |
|
|
|
|
snprintf(ifname, sizeof(ifname), "%u", ifindex); |
|
|
|
|
return route_cmd(ifname, network, prefix_len, "add"); |
|
|
|
|
const char *dev = ifname ? ifname : ""; |
|
|
|
|
return route_cmd(dev, network, prefix_len, "add"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int tun_route_del(uint32_t ifindex, uint32_t network, uint8_t prefix_len) { |
|
|
|
|
if (ifindex == 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_route_del: ifindex is 0"); |
|
|
|
|
int tun_route_del(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) { |
|
|
|
|
if (ifindex == 0 && (!ifname || !ifname[0])) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_route_del: ifindex and ifname are both invalid"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Try routing socket first
|
|
|
|
|
if (routing_socket_cmd(ifindex, network, prefix_len, RTM_DELETE) == 0) { |
|
|
|
|
if (ifindex != 0 && routing_socket_cmd(ifindex, network, prefix_len, RTM_DELETE) == 0) { |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Fallback to route command
|
|
|
|
|
char ifname[16]; |
|
|
|
|
snprintf(ifname, sizeof(ifname), "%u", ifindex); |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Routing socket failed, trying route command for ifindex %u", ifindex); |
|
|
|
|
return route_cmd(ifname, network, prefix_len, "delete"); |
|
|
|
|
const char *dev = ifname ? ifname : ""; |
|
|
|
|
return route_cmd(dev, network, prefix_len, "delete"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int tun_route_flush(const char *ifname) { |
|
|
|
|
@ -474,7 +519,7 @@ int tun_route_add_all(uint32_t ifindex, const char *ifname, struct CFG_ROUTE_ENT
|
|
|
|
|
while (entry) { |
|
|
|
|
uint32_t network = ntohl(entry->ip.addr.v4.s_addr); |
|
|
|
|
if (entry->ip.family == AF_INET) { |
|
|
|
|
if (tun_route_add(ifindex, network, entry->netmask) == 0) { |
|
|
|
|
if (tun_route_add(ifindex, ifname, network, entry->netmask) == 0) { |
|
|
|
|
struct in_addr addr; |
|
|
|
|
addr.s_addr = entry->ip.addr.v4.s_addr; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Added route %s/%d via %s", ip_to_str(&addr, AF_INET).str, entry->netmask, ifname); |
|
|
|
|
|