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)
514 char iface[IFNAMSIZ];
515 if (!if_indextoname(ifindex, iface))
517 OPENVPN_LOG(__func__ <<
": rtnl: can't get ifname for index "
533 <<
" with shorter route prefix " << route.
to_string());
541 <<
" with higher metrics " <<
metric);
571 std::string &best_iface)
574 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
r));
575 req.
n.nlmsg_type = RTM_GETROUTE;
576 req.
n.nlmsg_flags = NLM_F_REQUEST;
584 if (!is_safe_conversion<unsigned char>(route.
addr.family()))
586 res.
family = req.
r.rtm_family =
static_cast<unsigned char>(route.
addr.family());
590 req.
r.rtm_dst_len =
static_cast<decltype(req.r.rtm_dst_len)
>(route.
prefix_len);
592 if (route.
addr.family() == AF_INET)
594 req.
n.nlmsg_flags |= NLM_F_DUMP;
602 route.
addr.to_byte_string_variable(bytestr);
604 if (!is_safe_conversion<uint16_t>(route.
addr.size_bytes()))
613 best_gw = std::move(res.
gw);
614 best_iface = std::move(res.
iface);
616 OPENVPN_LOG(__func__ <<
" result: via " << best_gw <<
" dev " << best_iface);
620 OPENVPN_LOG(__func__ <<
": failed to retrieve route, err=" <<
ret);
639 struct ifaddrmsg *ifa = (
struct ifaddrmsg *)NLMSG_DATA(n);
640 struct rtattr *rta = IFA_RTA(ifa);
641 auto len = n->nlmsg_len - NLMSG_LENGTH(
sizeof(*ifa));
643 struct ifaddrmsg save = {};
645 while (RTA_OK(rta, len))
647 switch (rta->rta_type)
652 const unsigned char *bytestr = (
unsigned char *)RTA_DATA(rta);
659 <<
" family=" <<
int(ifa->ifa_family)
660 <<
" prefixlen=" <<
int(ifa->ifa_prefixlen)
661 <<
" flags=" <<
int(ifa->ifa_flags)
662 <<
" scope=" <<
int(ifa->ifa_scope)
663 <<
" index=" <<
int(ifa->ifa_index));
669 <<
" family=" <<
int(ifa->ifa_family)
670 <<
" prefixlen=" <<
int(ifa->ifa_prefixlen)
671 <<
" flags=" <<
int(ifa->ifa_flags)
672 <<
" scope=" <<
int(ifa->ifa_scope)
673 <<
" index=" <<
int(ifa->ifa_index));
680 rta = RTA_NEXT(rta, len);
685 res->
route = std::move(route);
704 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
r));
705 req.
n.nlmsg_type = RTM_GETADDR;
706 req.
n.nlmsg_flags = NLM_F_REQUEST;
711 if (!is_safe_conversion<unsigned char>(family))
713 res.
family = req.
r.rtm_family =
static_cast<unsigned char>(family);
715 req.
n.nlmsg_flags |= NLM_F_DUMP;
724 <<
" ifindex=" << res.
ifindex);
728 OPENVPN_LOG(__func__ <<
": failed to retrieve addr, err=" <<
ret);
736 const unsigned short flags,
737 const std::string &iface,
740 unsigned char prefixlen,
748 OPENVPN_LOG(__func__ <<
": passed empty interface");
754 OPENVPN_LOG(__func__ <<
": passed zero IP address");
758 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
i));
759 req.
n.nlmsg_type = cmd;
760 req.
n.nlmsg_flags = NLM_F_REQUEST |
flags;
764 req.
i.ifa_family =
static_cast<decltype(req.i.ifa_family)
>(local.
family());
765 req.
i.ifa_index = if_nametoindex(iface.c_str());
766 if (req.
i.ifa_index == 0)
768 OPENVPN_LOG(__func__ <<
": cannot get ifindex for " << iface <<
" "
778 prefixlen =
static_cast<decltype(prefixlen)
>(local.
size());
780 req.
i.ifa_prefixlen = prefixlen;
786 if (!is_safe_conversion<uint16_t>(local.
size_bytes()))
793 if (!is_safe_conversion<uint16_t>(remote.
size_bytes()))
801 if (!is_safe_conversion<uint16_t>(broadcast.
size_bytes()))
808 if ((
ret < 0) && (errno == EEXIST))
824 NLM_F_CREATE | NLM_F_REPLACE,
846 const unsigned short flags,
847 const std::string &iface,
850 const enum rt_class_t table,
852 const enum rt_scope_t scope,
853 const unsigned char protocol,
854 const unsigned char type)
859 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
r));
860 req.
n.nlmsg_type = cmd;
861 req.
n.nlmsg_flags = NLM_F_REQUEST |
flags;
865 req.
r.rtm_family =
static_cast<decltype(req.r.rtm_family)
>(route.
addr.family());
866 req.
r.rtm_scope = scope;
867 req.
r.rtm_protocol = protocol;
868 req.
r.rtm_type = type;
871 req.
r.rtm_dst_len =
static_cast<decltype(req.r.rtm_dst_len)
>(route.
prefix_len);
875 req.
r.rtm_table =
static_cast<decltype(req.r.rtm_table)
>(table);
879 req.
r.rtm_table = RT_TABLE_UNSPEC;
886 route.
addr.to_byte_string_variable(bytestr);
887 if (!is_safe_conversion<uint16_t>(route.
addr.size_bytes()))
894 if (!is_safe_conversion<uint16_t>(gw.
size_bytes()))
902 int ifindex = if_nametoindex(iface.c_str());
905 OPENVPN_LOG(__func__ <<
": rtnl: cannot get ifindex for " << iface);
918 if ((
ret < 0) && (errno == EEXIST))
931 unsigned char prefixlen,
935 NLM_F_CREATE | NLM_F_REPLACE,
958 const std::string &iface,
959 const uint32_t table,
967 (
enum rt_class_t)(!table ? RT_TABLE_MAIN : table),
977 const std::string &iface,
978 const uint32_t table,
986 (
enum rt_class_t)(!table ? RT_TABLE_MAIN : table),
997 std::string &best_iface,
998 const std::string &iface_to_ignore =
"")
1002 OPENVPN_LOG(__func__ <<
" query IPv6: " << route);
1019 std::string &best_iface,
1020 const std::string &iface_to_ignore =
"")
1024 OPENVPN_LOG(__func__ <<
" query IPv4: " << route);
1049 unsigned int ifindex = if_nametoindex(iface.c_str());
1071 struct rtattr *tail = NULL;
1075 OPENVPN_LOG(__func__ <<
": passed empty interface");
1079 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
i));
1080 req.
n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
1081 req.
n.nlmsg_type = RTM_NEWLINK;
1083 if (!is_safe_conversion<uint16_t>(iface.length() + 1))
1085 SITNL_ADDATTR(&req.
n,
sizeof(req), IFLA_IFNAME, iface.c_str(),
static_cast<uint16_t
>(iface.length() + 1));
1088 if (!is_safe_conversion<uint16_t>(type.length() + 1))
1090 SITNL_ADDATTR(&req.
n,
sizeof(req), IFLA_INFO_KIND, type.c_str(),
static_cast<uint16_t
>(type.length() + 1));
1093 tail->rta_len =
static_cast<decltype(tail-
>rta_len)>((uint8_t *)
NLMSG_TAIL(&req.
n) - (uint8_t *)tail);
1095 req.
i.ifi_family = AF_PACKET;
1096 req.
i.ifi_index = 0;
1098 OPENVPN_LOG(__func__ <<
": add " << iface <<
" type " << type);
1114 OPENVPN_LOG(__func__ <<
": passed empty interface");
1118 ifindex = if_nametoindex(iface.c_str());
1121 OPENVPN_LOG(__func__ <<
": rtnl: cannot get ifindex for " << iface
1122 <<
": " << strerror(errno));
1126 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
i));
1127 req.
n.nlmsg_flags = NLM_F_REQUEST;
1128 req.
n.nlmsg_type = RTM_DELLINK;
1130 req.
i.ifi_family = AF_PACKET;
1131 req.
i.ifi_index = ifindex;
1146 OPENVPN_LOG(__func__ <<
": passed empty interface");
1150 ifindex = if_nametoindex(iface.c_str());
1153 OPENVPN_LOG(__func__ <<
": rtnl: cannot get ifindex for " << iface
1154 <<
": " << strerror(errno));
1158 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
i));
1159 req.
n.nlmsg_flags = NLM_F_REQUEST;
1160 req.
n.nlmsg_type = RTM_NEWLINK;
1162 req.
i.ifi_family = AF_PACKET;
1163 req.
i.ifi_index = ifindex;
1164 req.
i.ifi_change |= IFF_UP;
1167 req.
i.ifi_flags |= IFF_UP;
1171 req.
i.ifi_flags &= ~IFF_UP;
1174 OPENVPN_LOG(__func__ <<
": set " << iface <<
" " << (up ?
"up" :
"down"));
1187 OPENVPN_LOG(__func__ <<
": passed empty interface");
1191 ifindex = if_nametoindex(iface.c_str());
1194 OPENVPN_LOG(__func__ <<
": rtnl: cannot get ifindex for " << iface);
1198 req.
n.nlmsg_len = NLMSG_LENGTH(
sizeof(req.
i));
1199 req.
n.nlmsg_flags = NLM_F_REQUEST;
1200 req.
n.nlmsg_type = RTM_NEWLINK;
1202 req.
i.ifi_family = AF_PACKET;
1203 req.
i.ifi_index = ifindex;
1207 OPENVPN_LOG(__func__ <<
": mtu " << mtu <<
" for " << iface);
1216 const unsigned char prefixlen,
1219 OPENVPN_LOG(__func__ <<
": " << addr <<
"/" << +prefixlen
1220 <<
" brd " << broadcast
1221 <<
" dev " << iface);
1232 const unsigned char prefixlen)
1234 OPENVPN_LOG(__func__ <<
": " << addr <<
"/" << +prefixlen
1235 <<
" dev " << iface);
1246 const unsigned char prefixlen)
1248 OPENVPN_LOG(__func__ <<
": " << addr <<
"/" << +prefixlen
1249 <<
" dev " << iface);
1259 const unsigned char prefixlen)
1261 OPENVPN_LOG(__func__ <<
": " << addr <<
"/" << +prefixlen
1262 <<
" dev " << iface);
1275 <<
" peer " << remote
1276 <<
" dev " << iface);
1289 <<
" dev " << iface);
1298 const std::string &iface,
1299 const uint32_t table,
1305 <<
" table " << table
1306 <<
" metric " <<
metric);
1318 const std::string &iface,
1319 const uint32_t table,
1325 <<
" table " << table
1326 <<
" metric " <<
metric);
1338 const std::string &iface,
1339 const uint32_t table,
1342 OPENVPN_LOG(__func__ <<
": " << route <<
" via " << gw <<
" dev " << iface
1343 <<
" table " << table <<
" metric " <<
metric);
1355 const std::string &iface,
1356 const uint32_t table,
1362 <<
" table " << table
1363 <<
" 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)
int(*)(struct nlmsghdr *msg, void *arg) sitnl_parse_reply_cb
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="")
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