#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <netinet/ether.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define BUFSIZE 8192
struct gw_info {
uint32_t ip;
char mac[ETH_ALEN];
};
int send_req(int sock, char *buf, size_t nlseq, size_t req_type)
{
struct nlmsghdr *nlmsg;
memset(buf, 0, BUFSIZE);
nlmsg = reinterpret_cast<struct nlmsghdr *>(buf);
nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
nlmsg->nlmsg_type = req_type;
nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
nlmsg->nlmsg_seq = nlseq++;
nlmsg->nlmsg_pid = getpid();
if (send(sock, buf, nlmsg->nlmsg_len, 0) < 0)
return -1;
return nlseq;
}
int read_res(int sock, char *buf, size_t nlseq)
{
struct nlmsghdr *nlmsg;
int len;
size_t total_len = 0;
do {
len = recv(sock, buf, BUFSIZE - total_len, 0);
if (len < 0)
return -1;
nlmsg = reinterpret_cast<struct nlmsghdr *>(buf);
if (NLMSG_OK(nlmsg, len) == 0)
return -1;
if (nlmsg->nlmsg_type == NLMSG_ERROR)
return -1;
if (nlmsg->nlmsg_type == NLMSG_DONE)
break;
buf += len;
total_len += len;
if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0)
break;
} while (nlmsg->nlmsg_seq != nlseq || nlmsg->nlmsg_pid != getpid());
return total_len;
}
int print_gw(struct gw_info *gw)
{
char buf[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &gw->ip, buf, INET_ADDRSTRLEN) == NULL)
return -1;
printf("gateway ip: %s\n", buf);
printf("gateway mac: ");
for (size_t i = 0; i < ETH_ALEN - 1; ++i)
printf("%02hhx:", gw->mac[i]);
printf("%02hhx\n", gw->mac[ETH_ALEN - 1]);
return 0;
}
void parse_route(struct nlmsghdr *nlmsg, void *gw)
{
struct rtmsg *rtmsg;
struct rtattr *attr;
uint32_t gw_tmp;
size_t len;
struct gw_info *info;
info = reinterpret_cast<struct gw_info *>(gw);
rtmsg = reinterpret_cast<struct rtmsg *>(NLMSG_DATA(nlmsg));
if (rtmsg->rtm_family != AF_INET || rtmsg->rtm_table != RT_TABLE_MAIN)
return;
attr = reinterpret_cast<struct rtattr *>(RTM_RTA(rtmsg));
len = RTM_PAYLOAD(nlmsg);
for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
if (attr->rta_type != RTA_GATEWAY)
continue;
info->ip = *reinterpret_cast<uint32_t *>(RTA_DATA(attr));
break;
}
}
void parse_neigh(struct nlmsghdr *nlmsg, void *gw)
{
struct ndmsg *ndmsg;
struct rtattr *attr;
size_t len;
char mac[ETH_ALEN];
uint32_t ip = 0;
struct gw_info *info;
info = reinterpret_cast<struct gw_info *>(gw);
ndmsg = reinterpret_cast<struct ndmsg *>(NLMSG_DATA(nlmsg));
if (ndmsg->ndm_family != AF_INET)
return;
attr = reinterpret_cast<struct rtattr *>(RTM_RTA(ndmsg));
len = RTM_PAYLOAD(nlmsg);
for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
if (attr->rta_type == NDA_LLADDR)
memcpy(mac, RTA_DATA(attr), ETH_ALEN);
if (attr->rta_type == NDA_DST)
ip = *reinterpret_cast<uint32_t *>(RTA_DATA(attr));
}
if (ip && ip == info->ip)
memcpy(info->mac, mac, ETH_ALEN);
}
void parse_response(char *buf, size_t len, void (cb)(struct nlmsghdr *, void *),
void *arg)
{
struct nlmsghdr *nlmsg;
nlmsg = reinterpret_cast<struct nlmsghdr *>(buf);
for (; NLMSG_OK(nlmsg, len); nlmsg = NLMSG_NEXT(nlmsg, len))
cb(nlmsg, arg);
}
int main(int argc, char **argv)
{
int sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
char buf[BUFSIZE];
size_t nlseq = 0;
size_t msg_len;
struct gw_info gw;
if (sock <= 0)
return -1;
nlseq = send_req(sock, buf, nlseq, RTM_GETROUTE);
msg_len = read_res(sock, buf, nlseq);
if (msg_len <= 0)
return -1;
parse_response(buf, msg_len, &parse_route, &gw);
nlseq = send_req(sock, buf, nlseq, RTM_GETNEIGH);
msg_len = read_res(sock, buf, nlseq);
if (msg_len <= 0)
return -1;
parse_response(buf, msg_len, &parse_neigh, &gw);
print_gw(&gw);
return 0;
}