Skip to content

RTM_NEWROUTE installs every route type as a unicast route #14966

Description

@dany74q

localRoute copies rtm_type into the parsed route (pkg/sentry/socket/netstack/stack.go:1000) and never reads it again, so the tcpip.Route it builds (stack.go:1071) and NewRoute adds (stack.go:1122) is a unicast route whatever the type, where Linux keeps the type and fails a lookup that matches an unreachable, prohibit, blackhole or throw route (net/ipv4/fib_semantics.c:113).

ip route add unreachable 198.51.100.0/24 succeeds inside the sandbox, and a later connect to 198.51.100.7 succeeds through the default route, which Linux fails with EHOSTUNREACH.

reproducer
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <errno.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <sched.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

static int fd;
static unsigned seq;

struct request {
  struct nlmsghdr hdr;
  union {
    struct ifinfomsg ifi;
    struct ifaddrmsg ifa;
    struct rtmsg rtm;
  };
  char attrs[64];
};

static void add_attr(struct request* req, int type, const void* data,
                     int len) {
  struct rtattr* rta =
      (struct rtattr*)((char*)req + NLMSG_ALIGN(req->hdr.nlmsg_len));
  rta->rta_type = type;
  rta->rta_len = RTA_LENGTH(len);
  memcpy(RTA_DATA(rta), data, len);
  req->hdr.nlmsg_len = NLMSG_ALIGN(req->hdr.nlmsg_len) + RTA_LENGTH(len);
}

static int send_request(struct request* req) {
  req->hdr.nlmsg_flags |= NLM_F_REQUEST | NLM_F_ACK;
  req->hdr.nlmsg_seq = ++seq;
  send(fd, req, req->hdr.nlmsg_len, 0);
  char buf[4096];
  int n = recv(fd, buf, sizeof(buf), 0);
  struct nlmsghdr* ack = (struct nlmsghdr*)buf;
  if (n < (int)NLMSG_LENGTH(sizeof(struct nlmsgerr)) ||
      ack->nlmsg_type != NLMSG_ERROR) {
    return -1;
  }
  return -((struct nlmsgerr*)NLMSG_DATA(ack))->error;
}

static struct request route(int type, const char* dst, int dst_len) {
  struct request req = {};
  req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.rtm));
  req.hdr.nlmsg_type = RTM_NEWROUTE;
  req.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;
  req.rtm.rtm_family = AF_INET;
  req.rtm.rtm_dst_len = dst_len;
  req.rtm.rtm_table = RT_TABLE_MAIN;
  req.rtm.rtm_protocol = RTPROT_BOOT;
  req.rtm.rtm_scope = RT_SCOPE_UNIVERSE;
  req.rtm.rtm_type = type;
  if (dst) {
    struct in_addr addr;
    inet_pton(AF_INET, dst, &addr);
    add_attr(&req, RTA_DST, &addr, sizeof(addr));
  }
  return req;
}

int main(void) {
  unshare(CLONE_NEWNET);
  fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  int lo = if_nametoindex("lo");

  struct request up = {};
  up.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(up.ifi));
  up.hdr.nlmsg_type = RTM_NEWLINK;
  up.ifi.ifi_index = lo;
  up.ifi.ifi_flags = IFF_UP;
  up.ifi.ifi_change = IFF_UP;
  send_request(&up);

  struct request addr = {};
  addr.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(addr.ifa));
  addr.hdr.nlmsg_type = RTM_NEWADDR;
  addr.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;
  addr.ifa.ifa_family = AF_INET;
  addr.ifa.ifa_prefixlen = 24;
  addr.ifa.ifa_index = lo;
  struct in_addr local;
  inet_pton(AF_INET, "192.0.2.1", &local);
  add_attr(&addr, IFA_LOCAL, &local, sizeof(local));
  send_request(&addr);

  struct request def = route(RTN_UNICAST, NULL, 0);
  struct in_addr gateway;
  inet_pton(AF_INET, "192.0.2.2", &gateway);
  add_attr(&def, RTA_GATEWAY, &gateway, sizeof(gateway));
  add_attr(&def, RTA_OIF, &lo, sizeof(lo));
  printf("add default via 192.0.2.2 dev lo: %s\n",
         strerror(send_request(&def)));

  struct request unreachable = route(RTN_UNREACHABLE, "198.51.100.0", 24);
  printf("add unreachable 198.51.100.0/24: %s\n",
         strerror(send_request(&unreachable)));

  int s = socket(AF_INET, SOCK_DGRAM, 0);
  struct sockaddr_in sin = {.sin_family = AF_INET, .sin_port = htons(9)};
  inet_pton(AF_INET, "198.51.100.7", &sin.sin_addr);
  int ret = connect(s, (struct sockaddr*)&sin, sizeof(sin));
  printf("connect to 198.51.100.7: %s\n", strerror(ret == 0 ? 0 : errno));
  return 0;
}

Proposed fix in #14968.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions