aboutgitcodebugslistschat
path: root/netlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'netlink.c')
-rw-r--r--netlink.c51
1 files changed, 42 insertions, 9 deletions
diff --git a/netlink.c b/netlink.c
index 9b3dba2..cdfe68c 100644
--- a/netlink.c
+++ b/netlink.c
@@ -33,8 +33,13 @@
#include "util.h"
#include "passt.h"
#include "log.h"
+#include "ip.h"
#include "netlink.h"
+/* Same as RTA_NEXT() but for nexthops: RTNH_NEXT() doesn't take 'attrlen' */
+#define RTNH_NEXT_AND_DEC(rtnh, attrlen) \
+ ((attrlen) -= RTNH_ALIGN((rtnh)->rtnh_len), RTNH_NEXT(rtnh))
+
/* Netlink expects a buffer of at least 8kiB or the system page size,
* whichever is larger. 32kiB is recommended for more efficient.
* Since the largest page size on any remotely common Linux setup is
@@ -270,6 +275,7 @@ unsigned int nl_get_ext_if(int s, sa_family_t af)
seq = nl_send(s, &req, RTM_GETROUTE, NLM_F_DUMP, sizeof(req));
nl_foreach_oftype(nh, status, s, buf, seq, RTM_NEWROUTE) {
struct rtmsg *rtm = (struct rtmsg *)NLMSG_DATA(nh);
+ const void *dst = NULL;
unsigned thisifi = 0;
if (rtm->rtm_family != af)
@@ -284,12 +290,23 @@ unsigned int nl_get_ext_if(int s, sa_family_t af)
rtnh = (struct rtnexthop *)RTA_DATA(rta);
thisifi = rtnh->rtnh_ifindex;
+ } else if (rta->rta_type == RTA_DST) {
+ dst = RTA_DATA(rta);
}
}
if (!thisifi)
continue; /* No interface for this route */
+ /* Skip routes to link-local addresses */
+ if (af == AF_INET && dst &&
+ IN4_IS_PREFIX_LINKLOCAL(dst, rtm->rtm_dst_len))
+ continue;
+
+ if (af == AF_INET6 && dst &&
+ IN6_IS_PREFIX_LINKLOCAL(dst, rtm->rtm_dst_len))
+ continue;
+
if (rtm->rtm_dst_len == 0) {
/* Default route */
ndef++;
@@ -309,7 +326,7 @@ unsigned int nl_get_ext_if(int s, sa_family_t af)
if (defifi) {
if (ndef > 1)
info("Multiple default %s routes, picked first",
- af == AF_INET ? "IPv4" : "IPv6");
+ af_name(af));
return defifi;
}
@@ -318,11 +335,11 @@ unsigned int nl_get_ext_if(int s, sa_family_t af)
return anyifi;
info("Multiple interfaces with %s routes, use -i to select one",
- af == AF_INET ? "IPv4" : "IPv6");
+ af_name(af));
}
if (!nany)
- info("No interfaces with %s routes", af == AF_INET ? "IPv4" : "IPv6");
+ info("No interfaces with usable %s routes", af_name(af));
return 0;
}
@@ -336,12 +353,13 @@ unsigned int nl_get_ext_if(int s, sa_family_t af)
*/
bool nl_route_get_def_multipath(struct rtattr *rta, void *gw)
{
+ size_t nh_len = RTA_PAYLOAD(rta);
struct rtnexthop *rtnh;
bool found = false;
int hops = -1;
for (rtnh = (struct rtnexthop *)RTA_DATA(rta);
- RTNH_OK(rtnh, RTA_PAYLOAD(rta)); rtnh = RTNH_NEXT(rtnh)) {
+ RTNH_OK(rtnh, nh_len); rtnh = RTNH_NEXT_AND_DEC(rtnh, nh_len)) {
size_t len = rtnh->rtnh_len - sizeof(*rtnh);
struct rtattr *rta_inner;
@@ -546,12 +564,20 @@ int nl_route_dup(int s_src, unsigned int ifi_src,
for (rta = RTM_RTA(rtm), na = RTM_PAYLOAD(nh); RTA_OK(rta, na);
rta = RTA_NEXT(rta, na)) {
+ /* RTA_OIF and RTA_MULTIPATH attributes carry the
+ * identifier of a host interface. Change them to match
+ * the corresponding identifier in the target namespace.
+ */
if (rta->rta_type == RTA_OIF) {
- /* The host obviously list's the host interface
- * id here, we need to change it to the
- * namespace's interface id
- */
*(unsigned int *)RTA_DATA(rta) = ifi_dst;
+ } else if (rta->rta_type == RTA_MULTIPATH) {
+ size_t nh_len = RTA_PAYLOAD(rta);
+ struct rtnexthop *rtnh;
+
+ for (rtnh = (struct rtnexthop *)RTA_DATA(rta);
+ RTNH_OK(rtnh, nh_len);
+ rtnh = RTNH_NEXT_AND_DEC(rtnh, nh_len))
+ rtnh->rtnh_ifindex = ifi_dst;
} else if (rta->rta_type == RTA_PREFSRC) {
/* Host routes might include a preferred source
* address, which must be one of the host's
@@ -648,7 +674,8 @@ int nl_addr_get(int s, unsigned int ifi, sa_family_t af,
for (rta = IFA_RTA(ifa), na = IFA_PAYLOAD(nh); RTA_OK(rta, na);
rta = RTA_NEXT(rta, na)) {
- if (rta->rta_type != IFA_ADDRESS)
+ if ((af == AF_INET && rta->rta_type != IFA_LOCAL) ||
+ (af == AF_INET6 && rta->rta_type != IFA_ADDRESS))
continue;
if (af == AF_INET && ifa->ifa_prefixlen > prefix_max) {
@@ -783,6 +810,8 @@ int nl_addr_dup(int s_src, unsigned int ifi_src,
continue;
ifa->ifa_index = ifi_dst;
+ /* Same as nl_addr_set(), but here it's more than a default */
+ ifa->ifa_flags |= IFA_F_NODAD;
for (rta = IFA_RTA(ifa), na = IFA_PAYLOAD(nh); RTA_OK(rta, na);
rta = RTA_NEXT(rta, na)) {
@@ -790,6 +819,10 @@ int nl_addr_dup(int s_src, unsigned int ifi_src,
if (rta->rta_type == IFA_LABEL ||
rta->rta_type == IFA_CACHEINFO)
rta->rta_type = IFA_UNSPEC;
+
+ /* If 32-bit flags are used, add IFA_F_NODAD there */
+ if (rta->rta_type == IFA_FLAGS)
+ *(uint32_t *)RTA_DATA(rta) |= IFA_F_NODAD;
}
rc = nl_do(s_dst, nh, RTM_NEWADDR,