Skip to content

Instantly share code, notes, and snippets.

@troglobit
Last active May 3, 2021 09:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save troglobit/17e14693288c0c923ca503618fc4366b to your computer and use it in GitHub Desktop.
Save troglobit/17e14693288c0c923ca503618fc4366b to your computer and use it in GitHub Desktop.
Read IPv4 routes from main routing table in Linux using netlink
/* Read kernel IPv4 routes from main routing table
*
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <err.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#define BUFSIZE 8192
struct route_info {
struct in_addr dst;
unsigned short len;
struct in_addr src;
struct in_addr gw;
int ifindex;
char ifname[IFNAMSIZ];
};
static int opt_n = 0;
static int read_netlink(int sd, char *buf, size_t len, unsigned int seq, unsigned int pid)
{
struct nlmsghdr *nlh;
int msg_len = 0;
int sz = 0;
do {
if ((sz = recv(sd, buf, len - msg_len, 0)) < 0)
return -1;
nlh = (struct nlmsghdr *)buf;
if ((NLMSG_OK(nlh, sz) == 0) || (nlh->nlmsg_type == NLMSG_ERROR))
return -1;
if (nlh->nlmsg_type == NLMSG_DONE)
break;
buf += sz;
msg_len += sz;
if ((nlh->nlmsg_flags & NLM_F_MULTI) == 0)
break;
}
while ((nlh->nlmsg_seq != seq) || (nlh->nlmsg_pid != pid));
return msg_len;
}
static void print_route(struct route_info *ri)
{
char tmp[INET_ADDRSTRLEN + 5];
struct in_addr nil = { 0 };
if (opt_n || memcmp(&ri->dst, &nil, sizeof(nil))) {
char prefix[5];
strcpy(tmp, inet_ntoa(ri->dst));
snprintf(prefix, sizeof(prefix), "/%d", ri->len);
strcat(tmp, prefix);
} else
sprintf(tmp, "default");
fprintf(stdout, "%-20s ", tmp);
fprintf(stdout, "%-16s ", inet_ntoa(ri->gw));
fprintf(stdout, "%-16s ", ri->ifname);
fprintf(stdout, "%-16s\n", inet_ntoa(ri->src));
}
static int parse_nlmsg(struct nlmsghdr *nlh, struct route_info *ri)
{
struct rtattr *a;
struct rtmsg *r;
int len;
r = (struct rtmsg *)NLMSG_DATA(nlh);
if ((r->rtm_family != AF_INET) || (r->rtm_table != RT_TABLE_MAIN))
return -1;
len = RTM_PAYLOAD(nlh);
for (a = RTM_RTA(r); RTA_OK(a, len); a = RTA_NEXT(a, len)) {
switch (a->rta_type) {
case RTA_OIF:
ri->ifindex = *(int *)RTA_DATA(a);
if_indextoname(ri->ifindex, ri->ifname);
break;
case RTA_GATEWAY:
memcpy(&ri->gw, RTA_DATA(a), sizeof(ri->gw));
break;
case RTA_PREFSRC:
memcpy(&ri->src, RTA_DATA(a), sizeof(ri->src));
break;
case RTA_DST:
memcpy(&ri->dst, RTA_DATA(a), sizeof(ri->dst));
ri->len = r->rtm_dst_len;
break;
}
}
print_route(ri);
return 0;
}
int main(int argc, char *argv[])
{
static char buf[BUFSIZE] = { 0 };
struct nlmsghdr *nlmsg;
struct route_info ri;
int sd, len;
int seq = 0;
if (argc > 1 && !strcmp(argv[1], "-n"))
opt_n = 1;
if ((sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
err(1, "netlink socket");
nlmsg = (struct nlmsghdr *)buf;
nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
nlmsg->nlmsg_type = RTM_GETROUTE;
nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
nlmsg->nlmsg_seq = seq++;
nlmsg->nlmsg_pid = getpid();
if (send(sd, nlmsg, nlmsg->nlmsg_len, 0) < 0)
err(1, "Failed netlink route request");
len = read_netlink(sd, buf, sizeof(buf), seq, getpid());
if (len < 0)
err(1, "Failed reading netlink response");
fprintf(stdout, "\e[7m%-20s %-16s %-16s %-16s\e[0m\n", "Destination", "Gateway", "Interface", "Source");
for (; NLMSG_OK(nlmsg, len); nlmsg = NLMSG_NEXT(nlmsg, len)) {
memset(&ri, 0, sizeof(ri));
parse_nlmsg(nlmsg, &ri);
}
close(sd);
return 0;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment