Last active
May 3, 2021 09:46
-
-
Save troglobit/17e14693288c0c923ca503618fc4366b to your computer and use it in GitHub Desktop.
Read IPv4 routes from main routing table in Linux using netlink
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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