20#include <sys/socket.h>
21#include <linux/netlink.h>
22#include <linux/rtnetlink.h>
31#ifndef OPENVPN_LOG_RTNL
33#define OPENVPN_LOG_RTNL(_x) OPENVPN_LOG(_x)
35#define OPENVPN_LOG_RTNL(_x)
41#define SNDBUF_SIZE (1024 * 2)
42#define RCVBUF_SIZE (1024 * 4)
44#define SITNL_ADDATTR(_msg, _max_size, _attr, _data, _size) \
46 if (sitnl_addattr(_msg, _max_size, _attr, _data, _size) < 0) \
52#define NLMSG_TAIL(nmsg) \
53 ((struct rtattr *)(((uint8_t *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
95 sitnl_addattr(
struct nlmsghdr *n,
int maxlen, uint16_t type,
const void *data, uint16_t alen)
97 uint16_t len = RTA_LENGTH(alen);
100 if ((
int)(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen)
102 OPENVPN_LOG(__func__ <<
": rtnl: message exceeded bound of " << maxlen);
107 rta->rta_type = type;
112 memset(RTA_DATA(rta), 0, alen);
116 memcpy(RTA_DATA(rta), data, alen);
119 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
134 fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
137 OPENVPN_LOG(__func__ <<
": cannot open netlink socket");
141 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf,
sizeof(sndbuf)) < 0)
148 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf,
sizeof(rcvbuf)) < 0)
165 struct sockaddr_nl local = {};
167 local.nl_family = AF_NETLINK;
168 local.nl_groups = groups;
170 if (bind(fd, (
struct sockaddr *)&local,
sizeof(local)) < 0)
172 OPENVPN_LOG(__func__ <<
": cannot bind netlink socket");
176 addr_len =
sizeof(local);
177 if (getsockname(fd, (
struct sockaddr *)&local, &addr_len) < 0)
183 if (addr_len !=
sizeof(local))
185 OPENVPN_LOG(__func__ <<
": wrong address length " << addr_len);
189 if (local.nl_family != AF_NETLINK)
191 OPENVPN_LOG(__func__ <<
": wrong address family " << local.nl_family);
225 const size_t buf_len = 16 * 1024;
226 struct sockaddr_nl nladdr = {
227 .nl_family = AF_NETLINK,
228 .nl_pid =
static_cast<__u32
>(peer),
233 .iov_len = payload->nlmsg_len,
235 struct msghdr nlmsg = {
237 .msg_namelen =
sizeof(nladdr),
242 payload->nlmsg_seq =
static_cast<__u32
>(time(NULL));
247 payload->nlmsg_flags |= NLM_F_ACK;
255 OPENVPN_LOG(__func__ <<
": can't open rtnl socket");
261 OPENVPN_LOG(__func__ <<
": can't bind rtnl socket");
266 if (sendmsg(fd, &nlmsg, 0) < 0)
268 OPENVPN_LOG(__func__ <<
": rtnl: error on sendmsg()");
274 buf = calloc(1, buf_len);
290 iov.iov_len = buf_len;
291 ssize_t rcv_len = recvmsg(fd, &nlmsg, 0);
295 if ((errno == EINTR) || (errno == EAGAIN))
300 OPENVPN_LOG(__func__ <<
": rtnl: error on recvmsg()");
307 OPENVPN_LOG(__func__ <<
": rtnl: socket reached unexpected EOF");
312 if (nlmsg.msg_namelen !=
sizeof(nladdr))
314 OPENVPN_LOG(__func__ <<
": sender address length: "
315 << nlmsg.msg_namelen <<
" (expected " <<
sizeof(nladdr)
321 struct nlmsghdr *h = (
struct nlmsghdr *)buf;
322 while (rcv_len >=
static_cast<ssize_t
>(
sizeof(*h)))
324 const auto len = h->nlmsg_len;
325 const auto data_len = len -
sizeof(*h);
327 if ((
sizeof(*h) > len) || (len > rcv_len))
329 if (nlmsg.msg_flags & MSG_TRUNC)
335 OPENVPN_LOG(__func__ <<
": malformed message: len=" << len);
340 if (h->nlmsg_type == NLMSG_DONE)
345 if (h->nlmsg_type == NLMSG_ERROR)
347 if (data_len <
sizeof(
struct nlmsgerr))
354 struct nlmsgerr *err = (
struct nlmsgerr *)NLMSG_DATA(h);
364 << strerror(-err->error)
365 <<
" (" << err->error <<
")");
377 OPENVPN_LOG(__func__ <<
": RTNL: unexpected reply");
380 rcv_len -= NLMSG_ALIGN(len);
381 h = (
struct nlmsghdr *)((
char *)h + NLMSG_ALIGN(len));
384 if (nlmsg.msg_flags & MSG_TRUNC)
393 <<
" not parsed bytes");
399 if (!(h->nlmsg_flags & NLM_F_MULTI))
425 struct rtmsg *r = (
struct rtmsg *)NLMSG_DATA(n);
426 struct rtattr *rta = RTM_RTA(r);
427 auto len = n->nlmsg_len - NLMSG_LENGTH(
sizeof(*r));
444 while (RTA_OK(rta, len))
446 switch (rta->rta_type)
450 ifindex = *(
unsigned int *)RTA_DATA(rta);
455 const unsigned char *bytestr = (
unsigned char *)RTA_DATA(rta);
470 metric = *(
unsigned int *)RTA_DATA(rta);
475 const unsigned char *bytestr = (
unsigned char *)RTA_DATA(rta);
489 rta = RTA_NEXT(rta, len);
494 <<
" ifindex=" << ifindex
495 <<
" proto=" <<
int(r->rtm_protocol)
496 <<
" scope=" <<
int(r->rtm_scope)
497 <<
" type=" <<
int(r->rtm_type)
498 <<
" table=" <<
int(r->rtm_table));
500 if (!gw.
defined() || ifindex <= 0)
515 char iface[IFNAMSIZ];
516 if (!if_indextoname(ifindex, iface))
518 OPENVPN_LOG(__func__ <<
": rtnl: can't get ifname for index "
534 <<
" with shorter route prefix " << route.
to_string());
542 <<
" with higher metrics " <<
metric);
572 std::string &best_iface)
575 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
r));
576 req.
n.nlmsg_type = RTM_GETROUTE;
577 req.
n.nlmsg_flags = NLM_F_REQUEST;
585 if (!is_safe_conversion<unsigned char>(route.
addr.family()))
587 res.
family = req.
r.rtm_family =
static_cast<unsigned char>(route.
addr.family());
591 req.
r.rtm_dst_len =
static_cast<decltype(req.r.rtm_dst_len)
>(route.
prefix_len);
593 if (route.
addr.family() == AF_INET)
595 req.
n.nlmsg_flags |= NLM_F_DUMP;
603 route.
addr.to_byte_string_variable(bytestr);
605 if (!is_safe_conversion<uint16_t>(route.
addr.size_bytes()))
614 best_gw = std::move(res.
gw);
615 best_iface = std::move(res.
iface);
617 OPENVPN_LOG(__func__ <<
" result: via " << best_gw <<
" dev " << best_iface);
621 OPENVPN_LOG(__func__ <<
": failed to retrieve route, err=" <<
ret);
640 struct ifaddrmsg *ifa = (
struct ifaddrmsg *)NLMSG_DATA(n);
641 struct rtattr *rta = IFA_RTA(ifa);
642 auto len = n->nlmsg_len - NLMSG_LENGTH(
sizeof(*ifa));
644 struct ifaddrmsg save = {};
646 while (RTA_OK(rta, len))
648 switch (rta->rta_type)
653 const unsigned char *bytestr = (
unsigned char *)RTA_DATA(rta);
660 <<
" family=" <<
int(ifa->ifa_family)
661 <<
" prefixlen=" <<
int(ifa->ifa_prefixlen)
662 <<
" flags=" <<
int(ifa->ifa_flags)
663 <<
" scope=" <<
int(ifa->ifa_scope)
664 <<
" index=" <<
int(ifa->ifa_index));
670 <<
" family=" <<
int(ifa->ifa_family)
671 <<
" prefixlen=" <<
int(ifa->ifa_prefixlen)
672 <<
" flags=" <<
int(ifa->ifa_flags)
673 <<
" scope=" <<
int(ifa->ifa_scope)
674 <<
" index=" <<
int(ifa->ifa_index));
681 rta = RTA_NEXT(rta, len);
686 res->
route = std::move(route);
705 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
r));
706 req.
n.nlmsg_type = RTM_GETADDR;
707 req.
n.nlmsg_flags = NLM_F_REQUEST;
712 if (!is_safe_conversion<unsigned char>(family))
714 res.
family = req.
r.rtm_family =
static_cast<unsigned char>(family);
716 req.
n.nlmsg_flags |= NLM_F_DUMP;
725 <<
" ifindex=" << res.
ifindex);
729 OPENVPN_LOG(__func__ <<
": failed to retrieve addr, err=" <<
ret);
737 const unsigned short flags,
738 const std::string &iface,
741 unsigned char prefixlen,
749 OPENVPN_LOG(__func__ <<
": passed empty interface");
755 OPENVPN_LOG(__func__ <<
": passed zero IP address");
759 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
i));
760 req.
n.nlmsg_type = cmd;
761 req.
n.nlmsg_flags = NLM_F_REQUEST |
flags;
765 req.
i.ifa_family =
static_cast<decltype(req.i.ifa_family)
>(local.
family());
766 req.
i.ifa_index = if_nametoindex(iface.c_str());
767 if (req.
i.ifa_index == 0)
769 OPENVPN_LOG(__func__ <<
": cannot get ifindex for " << iface <<
" "
779 prefixlen =
static_cast<decltype(prefixlen)
>(local.
size());
781 req.
i.ifa_prefixlen = prefixlen;
787 if (!is_safe_conversion<uint16_t>(local.
size_bytes()))
794 if (!is_safe_conversion<uint16_t>(remote.
size_bytes()))
802 if (!is_safe_conversion<uint16_t>(broadcast.
size_bytes()))
809 if ((
ret < 0) && (errno == EEXIST))
825 NLM_F_CREATE | NLM_F_REPLACE,
847 const unsigned short flags,
848 const std::string &iface,
851 const enum rt_class_t table,
853 const enum rt_scope_t scope,
854 const unsigned char protocol,
855 const unsigned char type)
860 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
r));
861 req.
n.nlmsg_type = cmd;
862 req.
n.nlmsg_flags = NLM_F_REQUEST |
flags;
866 req.
r.rtm_family =
static_cast<decltype(req.r.rtm_family)
>(route.
addr.family());
867 req.
r.rtm_scope = scope;
868 req.
r.rtm_protocol = protocol;
869 req.
r.rtm_type = type;
872 req.
r.rtm_dst_len =
static_cast<decltype(req.r.rtm_dst_len)
>(route.
prefix_len);
876 req.
r.rtm_table =
static_cast<decltype(req.r.rtm_table)
>(table);
880 req.
r.rtm_table = RT_TABLE_UNSPEC;
887 route.
addr.to_byte_string_variable(bytestr);
888 if (!is_safe_conversion<uint16_t>(route.
addr.size_bytes()))
895 if (!is_safe_conversion<uint16_t>(gw.
size_bytes()))
903 int ifindex = if_nametoindex(iface.c_str());
906 OPENVPN_LOG(__func__ <<
": rtnl: cannot get ifindex for " << iface);
919 if ((
ret < 0) && (errno == EEXIST))
932 unsigned char prefixlen,
936 NLM_F_CREATE | NLM_F_REPLACE,
959 const std::string &iface,
960 const uint32_t table,
968 (
enum rt_class_t)(!table ? RT_TABLE_MAIN : table),
978 const std::string &iface,
979 const uint32_t table,
987 (
enum rt_class_t)(!table ? RT_TABLE_MAIN : table),
998 std::string &best_iface,
999 const std::string &iface_to_ignore =
"")
1003 OPENVPN_LOG(__func__ <<
" query IPv6: " << route);
1020 std::string &best_iface,
1021 const std::string &iface_to_ignore =
"")
1025 OPENVPN_LOG(__func__ <<
" query IPv4: " << route);
1050 unsigned int ifindex = if_nametoindex(iface.c_str());
1072 struct rtattr *tail = NULL;
1076 OPENVPN_LOG(__func__ <<
": passed empty interface");
1080 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
i));
1081 req.
n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
1082 req.
n.nlmsg_type = RTM_NEWLINK;
1084 if (!is_safe_conversion<uint16_t>(iface.length() + 1))
1086 SITNL_ADDATTR(&req.
n,
sizeof(req), IFLA_IFNAME, iface.c_str(),
static_cast<uint16_t
>(iface.length() + 1));
1089 if (!is_safe_conversion<uint16_t>(type.length() + 1))
1091 SITNL_ADDATTR(&req.
n,
sizeof(req), IFLA_INFO_KIND, type.c_str(),
static_cast<uint16_t
>(type.length() + 1));
1094 tail->rta_len =
static_cast<decltype(tail-
>rta_len)>((uint8_t *)
NLMSG_TAIL(&req.
n) - (uint8_t *)tail);
1096 req.
i.ifi_family = AF_PACKET;
1097 req.
i.ifi_index = 0;
1099 OPENVPN_LOG(__func__ <<
": add " << iface <<
" type " << type);
1115 OPENVPN_LOG(__func__ <<
": passed empty interface");
1119 ifindex = if_nametoindex(iface.c_str());
1122 OPENVPN_LOG(__func__ <<
": rtnl: cannot get ifindex for " << iface
1123 <<
": " << strerror(errno));
1127 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
i));
1128 req.
n.nlmsg_flags = NLM_F_REQUEST;
1129 req.
n.nlmsg_type = RTM_DELLINK;
1131 req.
i.ifi_family = AF_PACKET;
1132 req.
i.ifi_index = ifindex;
1147 OPENVPN_LOG(__func__ <<
": passed empty interface");
1151 ifindex = if_nametoindex(iface.c_str());
1154 OPENVPN_LOG(__func__ <<
": rtnl: cannot get ifindex for " << iface
1155 <<
": " << strerror(errno));
1159 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
i));
1160 req.
n.nlmsg_flags = NLM_F_REQUEST;
1161 req.
n.nlmsg_type = RTM_NEWLINK;
1163 req.
i.ifi_family = AF_PACKET;
1164 req.
i.ifi_index = ifindex;
1165 req.
i.ifi_change |= IFF_UP;
1168 req.
i.ifi_flags |= IFF_UP;
1172 req.
i.ifi_flags &= ~IFF_UP;
1175 OPENVPN_LOG(__func__ <<
": set " << iface <<
" " << (up ?
"up" :
"down"));
1188 OPENVPN_LOG(__func__ <<
": passed empty interface");
1192 ifindex = if_nametoindex(iface.c_str());
1195 OPENVPN_LOG(__func__ <<
": rtnl: cannot get ifindex for " << iface);
1199 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
i));
1200 req.
n.nlmsg_flags = NLM_F_REQUEST;
1201 req.
n.nlmsg_type = RTM_NEWLINK;
1203 req.
i.ifi_family = AF_PACKET;
1204 req.
i.ifi_index = ifindex;
1208 OPENVPN_LOG(__func__ <<
": mtu " << mtu <<
" for " << iface);
1217 const unsigned char prefixlen,
1220 OPENVPN_LOG(__func__ <<
": " << addr <<
"/" << +prefixlen
1221 <<
" brd " << broadcast
1222 <<
" dev " << iface);
1233 const unsigned char prefixlen)
1235 OPENVPN_LOG(__func__ <<
": " << addr <<
"/" << +prefixlen
1236 <<
" dev " << iface);
1247 const unsigned char prefixlen)
1249 OPENVPN_LOG(__func__ <<
": " << addr <<
"/" << +prefixlen
1250 <<
" dev " << iface);
1260 const unsigned char prefixlen)
1262 OPENVPN_LOG(__func__ <<
": " << addr <<
"/" << +prefixlen
1263 <<
" dev " << iface);
1276 <<
" peer " << remote
1277 <<
" dev " << iface);
1290 <<
" dev " << iface);
1299 const std::string &iface,
1300 const uint32_t table,
1306 <<
" table " << table
1307 <<
" metric " <<
metric);
1319 const std::string &iface,
1320 const uint32_t table,
1326 <<
" table " << table
1327 <<
" metric " <<
metric);
1339 const std::string &iface,
1340 const uint32_t table,
1343 OPENVPN_LOG(__func__ <<
": " << route <<
" via " << gw <<
" dev " << iface
1344 <<
" table " << table <<
" metric " <<
metric);
1356 const std::string &iface,
1357 const uint32_t table,
1363 <<
" table " << table
1364 <<
" metric " <<
metric);
std::string to_string() const
unsigned int size() const
unsigned int size_bytes() const
const IPv4::Addr & to_ipv4() const
const IPv6::Addr & to_ipv6() const
static Addr from_ipv6(IPv6::Addr addr)
static Addr from_ipv4(IPv4::Addr addr)
void to_byte_string_variable(unsigned char *bytestr) const
static Addr from_zero(const Version v)
void validate_prefix_length(const TITLE &title)
std::string to_string() const
bool contains(const ADDR &a) const
static Addr from_bytes_net(const unsigned char *bytes)
static Addr from_byte_string(const unsigned char *bytestr)
static int net_route_del(const IP::Route6 &route, const IPv6::Addr &gw, const std::string &iface, const uint32_t table, const int metric)
static int sitnl_addr_del(const std::string &iface, const IP::Addr &addr, unsigned char prefixlen)
static int sitnl_iface_addr_save(struct nlmsghdr *n, void *arg)
static IP::Route net_iface_addr(const std::string &iface, const int family)
Get interface address/netmask.
static int sitnl_route_best_gw(const std::string &iface_to_ignore, const IP::Route &route, IP::Addr &best_gw, std::string &best_iface)
static int net_addr_del(const std::string &iface, const IPv6::Addr &addr, const unsigned char prefixlen)
static int sitnl_addr_add(const std::string &iface, const IP::Addr &addr, unsigned char prefixlen, const IP::Addr &broadcast)
static int net_route_del(const IP::Route4 &route, const IPv4::Addr &gw, const std::string &iface, const uint32_t table, const int metric)
static int sitnl_addr_ptp_add(const std::string &iface, const IP::Addr &local, const IP::Addr &remote)
static int sitnl_iface_addr(const int ifindex, const int family, IP::Route &route)
static int net_addr_add(const std::string &iface, const IPv6::Addr &addr, const unsigned char prefixlen)
static int net_iface_del(const std::string &iface)
static int net_route_add(const IP::Route6 &route, const IPv6::Addr &gw, const std::string &iface, const uint32_t table, const int metric)
static int sitnl_socket(void)
static int net_addr_add(const std::string &iface, const IPv4::Addr &addr, const unsigned char prefixlen, const IPv4::Addr &broadcast)
static int net_route_best_gw(const IP::Route6 &route, IPv6::Addr &best_gw6, std::string &best_iface, const std::string &iface_to_ignore="")
int(* sitnl_parse_reply_cb)(struct nlmsghdr *msg, void *arg)
static int sitnl_route_del(const IP::Route &route, const IP::Addr &gw, const std::string &iface, const uint32_t table, const int metric)
static int net_iface_up(std::string &iface, bool up)
static int sitnl_addr_ptp_del(const std::string &iface, const IP::Addr &local)
static int net_iface_mtu_set(std::string &iface, uint32_t mtu)
static int net_addr_ptp_del(const std::string &iface, const IPv4::Addr &local, const IPv4::Addr &remote)
static int sitnl_send(struct nlmsghdr *payload, pid_t peer, unsigned int groups, sitnl_parse_reply_cb cb, void *arg_cb)
static int net_route_add(const IP::Route4 &route, const IPv4::Addr &gw, const std::string &iface, const uint32_t table, const int metric)
static int sitnl_route_save(struct nlmsghdr *n, void *arg)
static int sitnl_addr_set(const unsigned short cmd, const unsigned short flags, const std::string &iface, const IP::Addr &local, const IP::Addr &remote, unsigned char prefixlen, const IP::Addr &broadcast)
static int sitnl_bind(int fd, uint32_t groups)
static int sitnl_route_set(const unsigned short cmd, const unsigned short flags, const std::string &iface, const IP::Route &route, const IP::Addr &gw, const enum rt_class_t table, const int metric, const enum rt_scope_t scope, const unsigned char protocol, const unsigned char type)
static int net_addr_del(const std::string &iface, const IPv4::Addr &addr, const unsigned char prefixlen)
static int net_iface_new(const std::string &iface, const std::string &type)
Add new interface (similar to ip link add)
static int net_route_best_gw(const IP::Route4 &route, IPv4::Addr &best_gw4, std::string &best_iface, const std::string &iface_to_ignore="")
static int net_addr_ptp_add(const std::string &iface, const IPv4::Addr &local, const IPv4::Addr &remote)
static int sitnl_addattr(struct nlmsghdr *n, int maxlen, uint16_t type, const void *data, uint16_t alen)
static int sitnl_route_add(const IP::Route &route, const IP::Addr &gw, const std::string &iface, const uint32_t table, const int metric)
#define OPENVPN_LOG(args)
RouteType< IP::Addr > Route
bool is_safe_conversion(InT inVal)
Returns true if the given value can be contained by the out type.
#define OPENVPN_LOG_RTNL(_x)
#define SITNL_ADDATTR(_msg, _max_size, _attr, _data, _size)
std::string iface_to_ignore
static std::stringstream out