Skip to content

Instantly share code, notes, and snippets.

@cynron
Last active January 8, 2016 07:21
Show Gist options
  • Save cynron/5f22ca78894d352a9889 to your computer and use it in GitHub Desktop.
Save cynron/5f22ca78894d352a9889 to your computer and use it in GitHub Desktop.
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
uint32_t seq = 0;
#define IPV4_ADDR_LEN 4
int init_nl_sock() {
/* TODO: error handling */
struct sockaddr_nl local;
int bufsz =1024 * 1024;
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bufsz, sizeof(bufsz));
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsz, sizeof(bufsz));
local.nl_family = AF_NETLINK;
local.nl_groups = RTMGRP_IPV4_ROUTE;
bind(fd, (struct sockaddr*)&local, sizeof(local));
return fd;
}
int handle(struct nlmsghdr* n) {
fprintf(stderr, "got nlmsghdr seq: %u\n", n->nlmsg_seq);
if (n->nlmsg_type == RTM_NEWROUTE) {
/* it is the answer for our route request */
if (n->nlmsg_seq == seq) {
struct rtmsg* r = NLMSG_DATA(n);
int len = n->nlmsg_len;
struct rtattr* rta = RTM_RTA(r);
int alen;
char* addr;
int index;
fprintf(stderr, "got answer\n");
len -= NLMSG_LENGTH(sizeof(*r));
assert(len >= 0);
while (RTA_OK(rta, len)) {
/* may not have gateway attr */
if (rta->rta_type == RTA_GATEWAY) {
alen = RTA_PAYLOAD(rta);
/* IPv4, 4 byte */
assert(alen == IPV4_ADDR_LEN);
addr = RTA_DATA(rta);
fprintf(stderr, "via len: %d, addr: %d.%d.%d.%d\n", alen, addr[0], addr[1], addr[2], addr[3]);
}
if (rta->rta_type == RTA_OIF) {
alen = RTA_PAYLOAD(rta);
/* int */
assert(alen == sizeof(index));
index = *(int*)RTA_DATA(rta);
fprintf(stderr, "out device index: %d\n", index);
}
rta = RTA_NEXT(rta, len);
}
assert(len == 0);
} else {
fprintf(stderr, "got route addition broadcast\n");
}
}
if (n->nlmsg_type == RTM_DELROUTE) {
fprintf(stderr, "got route deletion broadcast\n");
}
return 0;
}
int r(int fd) {
struct iovec iov;
char buf[8192];
int status;
struct msghdr msg = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = &iov,
.msg_iovlen = 1
};
struct nlmsghdr *n;
iov.iov_base = buf;
iov.iov_len = 8192;
/* TODO: error handling, EINTR, EAGAIN */
status = recvmsg(fd, &msg, 0);
assert(status > 0);
/* may return multi nlmsgs once recvmsg */
for (n = (struct nlmsghdr*)buf; status >= sizeof(*n); ) {
int len = n->nlmsg_len;
int l = len - sizeof(*n);
assert(l >= 0 && len <= status);
handle(n);
status -= NLMSG_ALIGN(len);
n = (struct nlmsghdr*)((void*)n + NLMSG_ALIGN(len));
}
return 0;
}
/* send route request */
int s(int fd, char* addr) {
struct {
struct nlmsghdr n;
struct rtmsg r;
char attr[1024];
} req;
struct rtattr* rta;
struct iovec iov;
struct msghdr msg;
struct nlmsghdr* n = &req.n;
struct sockaddr_nl dst_addr;
int len;
req.r.rtm_family = AF_INET;
req.r.rtm_table = 0;
req.r.rtm_protocol = 0;
req.r.rtm_scope = 0;
req.r.rtm_type = 0;
req.r.rtm_src_len = 0;
req.r.rtm_tos = 0;
n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
n->nlmsg_flags = NLM_F_REQUEST;
n->nlmsg_type = RTM_GETROUTE;
/*
* route request seq id
* used to trace route request
*/
n->nlmsg_seq = ++seq;
rta = (struct rtattr*)((void*)n + NLMSG_ALIGN(n->nlmsg_len));
len = RTA_LENGTH(IPV4_ADDR_LEN);
rta->rta_type = RTA_DST;
rta->rta_len = len;
memcpy(RTA_DATA(rta), addr, IPV4_ADDR_LEN);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
/* address mask 32 */
req.r.rtm_dst_len = 32;
iov.iov_base = (void*) n;
iov.iov_len = n->nlmsg_len;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &dst_addr;
msg.msg_namelen = sizeof(dst_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
memset(&dst_addr, 0, sizeof(dst_addr));
dst_addr.nl_family = AF_NETLINK;
/* TODO: error handling */
return sendmsg(fd, &msg, 0);
}
int main() {
int fd = init_nl_sock();
/* get route for 7.7.7.7/32 */
char a[4] = {7, 7, 7, 7};
s(fd, a);
while (1) {
r(fd);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment