libnl 是用来简化 Netlink 编程的。
例子 1
使用 libnl 提供的 API 列出当前 Linux 系统中的所有网卡。
/*
* List all network interfaces
*/
#include <stdio.h> //printf, perror
#include <stdlib.h> //exit
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
static int print_link(struct nl_msg * msg, void * arg)
{
struct nlmsghdr * h = nlmsg_hdr(msg);
struct ifinfomsg * iface = NLMSG_DATA(h);
struct rtattr * attr = IFLA_RTA(iface);
int remaining = RTM_PAYLOAD(h);
for (; RTA_OK(attr, remaining); attr = RTA_NEXT(attr, remaining))
{
switch (attr->rta_type)
{
case IFLA_IFNAME:
printf("Interface %d : %s\n", iface->ifi_index, (char *)RTA_DATA(attr));
break;
default:
break;
}
}
return NL_OK;
}
void die(char * s)
{
perror(s);
exit(1);
}
int main(void)
{
struct nl_sock * s = nl_socket_alloc();
if (s == NULL) {
die("nl_socket_alloc");
}
if (nl_connect(s, NETLINK_ROUTE) < 0) {
nl_socket_free(s);
die("nl_connet");
}
struct rtgenmsg rt_hdr = { .rtgen_family = AF_NETLINK, };
if (nl_send_simple(s, RTM_GETLINK, NLM_F_REQUEST|NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)) < 0) {
nl_socket_free(s);
die("nl_send_simple");
}
//Retrieve the kernel's answer.
nl_socket_modify_cb(s, NL_CB_VALID, NL_CB_CUSTOM, print_link, NULL);
nl_recvmsgs_default(s);
nl_socket_free(s);
return 0;
}
# gcc link-list.c -o link-list $(pkg-config --cflags --libs libnl-genl-3.0) && ./link-list
Interface 1 : lo
Interface 2 : eth0
例子 2
使用 libnl 提供的 API 列出当前 Linux 系统中的所有 IP 地址。
/*
* List all IP addresses
*/
#include <stdio.h> //printf, perror
#include <stdlib.h> //exit
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
static int print_addr(struct nl_msg * msg, void * arg)
{
struct nlmsghdr * h = nlmsg_hdr(msg);
struct ifaddrmsg * addr = NLMSG_DATA(h);
struct rtattr * attr = IFLA_RTA(addr);
int remaining = RTM_PAYLOAD(h);
for (; RTA_OK(attr, remaining); attr = RTA_NEXT(attr, remaining))
{
switch (attr->rta_type)
{
case IFA_LABEL:
printf("Interface : %s\n", (char *)RTA_DATA(attr));
break;
case IFA_LOCAL:
{
int ip = *(int*)RTA_DATA(attr);
unsigned char bytes[4];
bytes[0] = ip & 0xFF;
bytes[1] = (ip >> 8) & 0xFF;
bytes[2] = (ip >> 16) & 0xFF;
bytes[3] = (ip >> 24) & 0xFF;
printf("IP Address : %d.%d.%d.%d\n", bytes[0], bytes[1], bytes[2], bytes[3]);
break;
}
default:
break;
}
}
return NL_OK;
}
void die(char * s)
{
perror(s);
exit(1);
}
int main(void)
{
struct nl_sock * s = nl_socket_alloc();
if (s == NULL) {
die("nl_socket_alloc");
}
if (nl_connect(s, NETLINK_ROUTE) < 0) {
nl_socket_free(s);
die("nl_connet");
}
struct rtgenmsg rt_hdr = { .rtgen_family = AF_NETLINK, };
if (nl_send_simple(s, RTM_GETADDR, NLM_F_REQUEST|NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)) < 0) {
nl_socket_free(s);
die("nl_send_simple");
}
//Retrieve the kernel's answer.
nl_socket_modify_cb(s, NL_CB_VALID, NL_CB_CUSTOM, print_addr, NULL);
nl_recvmsgs_default(s);
nl_socket_free(s);
return 0;
}
# gcc addr-list.c -o addr-list $(pkg-config --cflags --libs libnl-genl-3.0) && ./addr-list
IP Address : 127.0.0.1
Interface : lo
IP Address : 10.254.108.206
Interface : eth0
例子 3
使用 libnl 提供的 API 列出当前 Linux 系统中主路由表中的所有路由。
/*
* List all routes in main route table
*/
#include <stdio.h> //printf, perror
#include <stdlib.h> //exit
#include <arpa/inet.h> //inet_ntop
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
static int print_route(struct nl_msg * msg, void * arg)
{
struct nlmsghdr * h = nlmsg_hdr(msg);
struct rtmsg * rte = NLMSG_DATA(h);
struct rtattr * attr = RTM_RTA(rte);
int remaining = RTM_PAYLOAD(h);
char dest[64] = {0};
char gway[64] = {"unspecified"};
if (rte->rtm_table != RT_TABLE_MAIN) {
return NL_OK;
}
for (; RTA_OK(attr, remaining); attr = RTA_NEXT(attr, remaining))
{
switch (attr->rta_type)
{
case RTA_DST:
if (rte->rtm_dst_len < 32)
inet_ntop(AF_INET, RTA_DATA(attr), dest, sizeof(dest));
else
inet_ntop(AF_INET6, RTA_DATA(attr), dest, sizeof(dest));
break;
case RTA_GATEWAY:
if (rte->rtm_dst_len < 32)
inet_ntop(AF_INET, RTA_DATA(attr), gway, sizeof(gway));
else
inet_ntop(AF_INET6, RTA_DATA(attr), gway, sizeof(gway));
break;
default:
break;
}
}
printf("%s/%d gateway %s\n", dest, rte->rtm_dst_len, gway);
return NL_OK;
}
void die(char * s)
{
perror(s);
exit(1);
}
int main(void)
{
struct nl_sock * s = nl_socket_alloc();
if (s == NULL) {
die("nl_socket_alloc");
}
if (nl_connect(s, NETLINK_ROUTE) < 0) {
nl_socket_free(s);
die("nl_connet");
}
struct rtgenmsg rt_hdr = { .rtgen_family = AF_NETLINK, };
if (nl_send_simple(s, RTM_GETROUTE, NLM_F_REQUEST|NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)) < 0) {
nl_socket_free(s);
die("nl_send_simple");
}
//Retrieve the kernel's answer.
nl_socket_modify_cb(s, NL_CB_VALID, NL_CB_CUSTOM, print_route, NULL);
nl_recvmsgs_default(s);
nl_socket_free(s);
return 0;
}
# gcc route-list.c -o route-list $(pkg-config --cflags --libs libnl-genl-3.0) && ./route-list
/0 gateway 10.254.96.136
10.254.96.0/19 gateway unspecified
fe80::/64 gateway unspecified
例子 4
使用 libnl 提供的 API,检测当前 Linux 系统中主路由表中的路由变化。
/*
* Monitor route change
*/
#include <stdio.h> //printf, perror
#include <stdlib.h> //exit
#include <arpa/inet.h> //inet_ntop
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
static int print_route(struct nl_msg * msg, void * arg)
{
struct nlmsghdr * h = nlmsg_hdr(msg);
struct rtmsg * rte = NLMSG_DATA(h);
struct rtattr * attr = RTM_RTA(rte);
int remaining = RTM_PAYLOAD(h);
char dest[64] = {0};
char gway[64] = {"unspecified"};
if (rte->rtm_table != RT_TABLE_MAIN) {
return NL_OK;
}
for (; RTA_OK(attr, remaining); attr = RTA_NEXT(attr, remaining))
{
switch (attr->rta_type)
{
case RTA_DST:
if (rte->rtm_dst_len < 32)
inet_ntop(AF_INET, RTA_DATA(attr), dest, sizeof(dest));
else
inet_ntop(AF_INET6, RTA_DATA(attr), dest, sizeof(dest));
break;
case RTA_GATEWAY:
if (rte->rtm_dst_len < 32)
inet_ntop(AF_INET, RTA_DATA(attr), gway, sizeof(gway));
else
inet_ntop(AF_INET6, RTA_DATA(attr), gway, sizeof(gway));
break;
default:
break;
}
}
if (h->nlmsg_type == RTM_NEWROUTE) {
printf("add ");
}
else if (h->nlmsg_type == RTM_DELROUTE) {
printf("del ");
}
else {
printf("nlmsg_type=%d ", h->nlmsg_type);
}
printf("%s/%d gateway %s\n", dest, rte->rtm_dst_len, gway);
return NL_OK;
}
void die(char * s)
{
perror(s);
exit(1);
}
int main(void)
{
struct nl_sock * s = nl_socket_alloc();
if (s == NULL) {
die("nl_socket_alloc");
}
nl_socket_disable_seq_check(s);
nl_socket_modify_cb(s, NL_CB_VALID, NL_CB_CUSTOM, print_route, NULL);
if (nl_connect(s, NETLINK_ROUTE) < 0) {
nl_socket_free(s);
die("nl_connet");
}
nl_socket_add_memberships(s, RTNLGRP_IPV4_ROUTE, 0);
while(1)
{
nl_recvmsgs_default(s);
}
nl_socket_free(s);
return 0;
}
Terminal 1:
# gcc route-chg.c -o route-chg $(pkg-config --cflags --libs libnl-genl-3.0) && ./route-chg
Terminal 2:
# ip route add 199.1.1.0/24 via 10.254.96.150
# ip route del 199.1.1.0/24 via 10.254.96.150
Terminal 1:
# gcc route-chg.c -o route-chg $(pkg-config --cflags --libs libnl-genl-3.0) && ./route-chg
add 199.1.1.0/24 gateway 10.254.96.150
del 199.1.1.0/24 gateway 10.254.96.150
参考文献
1 https://www.infradead.org/~tgr/libnl/doc/core.html
2 https://github.com/Robpol86/libnl/blob/master/example_c/list_network_interfaces.c