Netlink 是内核与用户空间进程通信的一种机制,采用与 UDP socket 非常类似的编程风格。(关于 UDP socket 编程,请看这里)
与 UDP 类似,Netlink 通信也有服务器端和客户端。例如,Linux 内核作为服务器端,已经运行在那里。我们则编写 Netlink 客户端,给内核发消息,并获得我们想要的信息。
例子 1
通过 Netlink,从内核中得到所有网口的名字。
link-list.c:
/*
* Display all network interface names
*/
#include //printf, perror
#include //memset, strlen
#include //exit
#include //close
#include //msghdr
#include //inet_ntop
#include //sockaddr_nl
#include //rtgenmsg,ifinfomsg
#define BUFSIZE 8192
struct nl_req_s {
struct nlmsghdr hdr;
struct rtgenmsg gen;
};
void die(char *s)
{
perror(s);
exit(1);
}
void rtnl_print_link(struct nlmsghdr * h)
{
struct ifinfomsg * iface;
struct rtattr * attr;
int len;
iface = NLMSG_DATA(h);
len = RTM_PAYLOAD(h);
/* loop over all attributes for the NEWLINK message */
for (attr = IFLA_RTA(iface); RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
{
switch (attr->rta_type)
{
case IFLA_IFNAME:
printf("Interface %d : %s\n", iface->ifi_index, (char *)RTA_DATA(attr));
break;
default:
break;
}
}
}
int main(void)
{
struct sockaddr_nl kernel;
int s, end=0, len;
struct msghdr msg;
struct nl_req_s req;
struct iovec io;
char buf[BUFSIZE];
//build kernel netlink address
memset(&kernel, 0, sizeof(kernel));
kernel.nl_family = AF_NETLINK;
kernel.nl_groups = 0;
//create a Netlink socket
if ((s=socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
{
die("socket");
}
//build netlink message
memset(&req, 0, sizeof(req));
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
req.hdr.nlmsg_type = RTM_GETLINK;
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.hdr.nlmsg_seq = 1;
req.hdr.nlmsg_pid = getpid();
req.gen.rtgen_family = AF_INET;
memset(&io, 0, sizeof(io));
io.iov_base = &req;
io.iov_len = req.hdr.nlmsg_len;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_name = &kernel;
msg.msg_namelen = sizeof(kernel);
//send the message
if (sendmsg(s, &msg, 0) < 0)
{
die("sendmsg");
}
//parse reply
while (!end)
{
memset(buf, 0, BUFSIZE);
msg.msg_iov->iov_base = buf;
msg.msg_iov->iov_len = BUFSIZE;
if ((len=recvmsg(s, &msg, 0)) < 0)
{
die("recvmsg");
}
for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
{
switch (msg_ptr->nlmsg_type)
{
case NLMSG_DONE:
end++;
break;
case RTM_NEWLINK:
rtnl_print_link(msg_ptr);
break;
default:
printf("Ignored msg: type=%d, len=%d\n", msg_ptr->nlmsg_type, msg_ptr->nlmsg_len);
break;
}
}
}
close(s);
return 0;
}
# gcc link-list.c -o link-list && ./link-list
Interface 1 : lo
Interface 2 : eth0
例子 2
通过 Netlink,从内核中得到所有网口的 IP 地址。
/*
* Display all IPv4 addresses
*/
#include //printf, perror
#include //memset, strlen
#include //exit
#include //close
#include //msghdr
#include //inet_ntop
#include //sockaddr_nl
#include //rtgenmsg,ifinfomsg
#define BUFSIZE 8192
struct nl_req_s {
struct nlmsghdr hdr;
struct rtgenmsg gen;
};
void die(char *s)
{
perror(s);
exit(1);
}
void rtnl_print_addr(struct nlmsghdr * h)
{
struct ifaddrmsg * addr;
struct rtattr * attr;
int len;
addr = NLMSG_DATA(h);
len = RTM_PAYLOAD(h);
/* loop over all attributes for the NEWLINK message */
for (attr = IFLA_RTA(addr); RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
{
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;
}
}
}
int main(void)
{
struct sockaddr_nl kernel;
int s, end=0, len;
struct msghdr msg;
struct nl_req_s req;
struct iovec io;
char buf[BUFSIZE];
//build kernel netlink address
memset(&kernel, 0, sizeof(kernel));
kernel.nl_family = AF_NETLINK;
kernel.nl_groups = 0;
//create a Netlink socket
if ((s=socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
{
die("socket");
}
//build netlink message
memset(&req, 0, sizeof(req));
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
req.hdr.nlmsg_type = RTM_GETADDR;
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.hdr.nlmsg_seq = 1;
req.hdr.nlmsg_pid = getpid();
req.gen.rtgen_family = AF_INET;
memset(&io, 0, sizeof(io));
io.iov_base = &req;
io.iov_len = req.hdr.nlmsg_len;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_name = &kernel;
msg.msg_namelen = sizeof(kernel);
//send the message
if (sendmsg(s, &msg, 0) < 0)
{
die("sendmsg");
}
//parse reply
while (!end)
{
memset(buf, 0, BUFSIZE);
msg.msg_iov->iov_base = buf;
msg.msg_iov->iov_len = BUFSIZE;
if ((len=recvmsg(s, &msg, 0)) < 0)
{
die("recvmsg");
}
for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
{
switch (msg_ptr->nlmsg_type)
{
case NLMSG_DONE:
end++;
break;
case RTM_NEWADDR:
rtnl_print_addr(msg_ptr);
break;
default:
printf("Ignored msg: type=%d, len=%d\n", msg_ptr->nlmsg_type, msg_ptr->nlmsg_len);
break;
}
}
}
close(s);
return 0;
}
# gcc addr-list.c -o addr-list && ./addr-list
IP Address : 127.0.0.1
Interface : lo
IP Address : 10.254.115.149
Interface : eth0
例子 3
通过 Netlink 列出当前系统中主路由表中的所有路由。
/*
* Display all routes
*/
#include //printf, perror
#include //memset, strlen
#include //exit
#include //close
#include //msghdr
#include //inet_ntop
#include //sockaddr_nl,NLMSG_DATA
#include //rtgenmsg,ifinfomsg
#define BUFSIZE 8192
struct nl_req_s {
struct nlmsghdr hdr;
struct rtgenmsg gen;
};
void die(char *s)
{
perror(s);
exit(1);
}
void rtnl_print_route(struct nlmsghdr * h)
{
struct rtmsg * rte = NLMSG_DATA(h);
struct rtattr * attr = RTM_RTA(rte);
int len = RTM_PAYLOAD(h);
char dest[32] = {0};
char gway[32] = {"unspecified"};
if (rte->rtm_table != RT_TABLE_MAIN) {
return;
}
for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
{
switch (attr->rta_type)
{
case RTA_DST:
inet_ntop(AF_INET, RTA_DATA(attr), dest, sizeof(dest));
break;
case RTA_GATEWAY:
inet_ntop(AF_INET, RTA_DATA(attr), gway, sizeof(gway));
break;
default:
break;
}
}
printf("%s/%d gateway %s\n", dest, rte->rtm_dst_len, gway);
}
int main(void)
{
struct sockaddr_nl kernel;
int s, end=0, len;
struct msghdr msg;
struct nl_req_s req;
struct iovec io;
char buf[BUFSIZE];
//build kernel netlink address
memset(&kernel, 0, sizeof(kernel));
kernel.nl_family = AF_NETLINK;
kernel.nl_groups = 0;
//create a Netlink socket
if ((s=socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
{
die("socket");
}
//build netlink message
memset(&req, 0, sizeof(req));
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
req.hdr.nlmsg_type = RTM_GETROUTE;
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.hdr.nlmsg_seq = 1;
req.hdr.nlmsg_pid = getpid();
req.gen.rtgen_family = AF_INET;
memset(&io, 0, sizeof(io));
io.iov_base = &req;
io.iov_len = req.hdr.nlmsg_len;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_name = &kernel;
msg.msg_namelen = sizeof(kernel);
//send the message
if (sendmsg(s, &msg, 0) < 0)
{
die("sendmsg");
}
// parse reply
while (!end)
{
memset(buf, 0, BUFSIZE);
msg.msg_iov->iov_base = buf;
msg.msg_iov->iov_len = BUFSIZE;
if ((len=recvmsg(s, &msg, 0)) < 0)
{
die("recvmsg");
}
for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
{
switch (msg_ptr->nlmsg_type)
{
case NLMSG_DONE:
end++;
break;
case RTM_NEWROUTE:
rtnl_print_route(msg_ptr);
break;
default:
printf("Ignored msg: type=%d, len=%d\n", msg_ptr->nlmsg_type, msg_ptr->nlmsg_len);
break;
}
}
}
close(s);
return 0;
}
# gcc route-list.c -o route-list && ./route-list
/0 gateway 10.254.96.136
10.254.96.0/19 gateway unspecified
例子 4
通过 Netlink 监测主路由表中路由的变化。
/*
* Monitor route change
*/
#include //printf, perror
#include //memset, strlen
#include //exit
#include //close
#include //msghdr
#include //inet_ntop
#include //sockaddr_nl,NLMSG_DATA
#include //rtgenmsg,ifinfomsg
#define BUFSIZE 4096
void die(char *s)
{
perror(s);
exit(1);
}
void print_route(struct nlmsghdr * h)
{
struct rtmsg * rte = NLMSG_DATA(h);
struct rtattr * attr = RTM_RTA(rte);
int len = RTM_PAYLOAD(h);
char dest[32] = {0};
char gway[32] = {"unspecified"};
if (rte->rtm_table != RT_TABLE_MAIN) {
return;
}
for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
{
switch (attr->rta_type)
{
case RTA_DST:
inet_ntop(AF_INET, RTA_DATA(attr), dest, sizeof(dest));
break;
case RTA_GATEWAY:
inet_ntop(AF_INET, 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);
}
int main(void)
{
int s, len;
struct sockaddr_nl me;
char buf[BUFSIZE];
memset(&me, 0, sizeof(me));
if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
{
die("socket");
}
me.nl_family = AF_NETLINK;
me.nl_groups = RTMGRP_IPV4_ROUTE;
if (bind(s,(struct sockaddr *)&me, sizeof(me)) < 0)
{
die("bind");
}
while (1)
{
memset(buf, 0, BUFSIZE);
if ((len=recv(s, buf, BUFSIZE, 0)) < 0)
{
die("recv");
}
for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
{
switch (msg_ptr->nlmsg_type)
{
case RTM_NEWROUTE:
case RTM_DELROUTE:
print_route(msg_ptr);
break;
default:
printf("Ignored msg: type=%d\n", msg_ptr->nlmsg_type);
break;
}
}
}
close(s);
return 0;
}
Terminal 1:
# gcc route-chg.c -o route-chg && ./route-chg
Terminal 2:
# ip route add 199.1.1.0/24 via 10.254.96.136
# ip route del 199.1.1.0/24 via 10.254.96.136
Terminal 1:
# gcc route-chg.c -o route-chg && ./route-chg
add 199.1.1.0/24 gateway 10.254.96.136
del 199.1.1.0/24 gateway 10.254.96.136
例子 5
通过 Netlink 监测网口的变化。
/*
* Monitor network interface change
*/
#include //printf, perror
#include //memset, strlen
#include //exit
#include //close
#include //msghdr
#include //inet_ntop
#include //sockaddr_nl,NLMSG_DATA
#include //rtgenmsg,ifinfomsg
#define BUFSIZE 4096
void die(char *s)
{
perror(s);
exit(1);
}
void print_link(struct nlmsghdr * h)
{
struct ifinfomsg * iface;
struct rtattr * attr;
int len;
iface = NLMSG_DATA(h); //point to payload
len = RTM_PAYLOAD(h);
/* loop over all attributes for the NEWLINK message */
for (attr = IFLA_RTA(iface); RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
{
switch (attr->rta_type)
{
case IFLA_IFNAME:
if (h->nlmsg_type == RTM_NEWLINK) {
printf("add ");
}
else if (h->nlmsg_type == RTM_DELLINK) {
printf("del ");
}
else {
printf("nlmsg_type=%d ", h->nlmsg_type);
}
printf("Interface %d : %s\n", iface->ifi_index, (char *)RTA_DATA(attr));
break;
default:
break;
}
}
}
int main(void)
{
int s, len;
struct sockaddr_nl me;
char buf[BUFSIZE];
memset(&me, 0, sizeof(me));
if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
{
die("socket");
}
int on = 1;
if (setsockopt(s, SOL_NETLINK, NETLINK_LISTEN_ALL_NSID, &on, sizeof(on)) < 0)
{
die("setsockopt");
}
me.nl_family = AF_NETLINK;
me.nl_groups = RTMGRP_LINK;
if (bind(s,(struct sockaddr *)&me, sizeof(me)) < 0)
{
die("bind");
}
while (1)
{
memset(buf, 0, BUFSIZE);
if ((len=recv(s, buf, BUFSIZE, 0)) < 0)
{
die("recv");
}
for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
{
switch (msg_ptr->nlmsg_type)
{
case RTM_NEWLINK:
case RTM_DELLINK:
print_link(msg_ptr);
break;
default:
printf("Ignored msg: type=%d\n", msg_ptr->nlmsg_type);
break;
}
}
}
close(s);
return 0;
}
Terminal 1:
# gcc link-chg.c -o link-chg && ./link-chg
Terminal 2:
# ip link add veth1 type veth
# ip link del veth1 type veth
Terminal 1:
# gcc link-chg.c -o link-chg && ./link-chg
add Interface 8 : veth0
add Interface 9 : veth1
del Interface 9 : veth1
del Interface 8 : veth0
例子 6
通过 Netlink 监测 IP 地址的变化。
/*
* Monitor IP address change
*/
#include //printf, perror
#include //memset, strlen
#include //exit
#include //close
#include //msghdr
#include //inet_ntop
#include //sockaddr_nl,NLMSG_DATA
#include //rtgenmsg,ifinfomsg
#define BUFSIZE 4096
void die(char *s)
{
perror(s);
exit(1);
}
void print_addr(struct nlmsghdr * h)
{
struct ifaddrmsg * addr;
struct rtattr * attr;
int len;
addr = NLMSG_DATA(h);
len = RTM_PAYLOAD(h);
/* loop over all attributes for the NEWLINK message */
for (attr = IFLA_RTA(addr); RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
{
switch (attr->rta_type)
{
case IFA_LABEL:
printf("Interface : %s\n", (char *)RTA_DATA(attr));
break;
case IFA_LOCAL:
{
if (h->nlmsg_type == RTM_NEWADDR) {
printf("add ");
}
else if (h->nlmsg_type == RTM_DELADDR) {
printf("del ");
}
else {
printf("nlmsg_type=%d ", h->nlmsg_type);
}
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;
}
}
}
int main(void)
{
int s, len;
struct sockaddr_nl me;
char buf[BUFSIZE];
memset(&me, 0, sizeof(me));
if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
{
die("socket");
}
int on = 1;
if (setsockopt(s, SOL_NETLINK, NETLINK_LISTEN_ALL_NSID, &on, sizeof(on)) < 0)
{
die("setsockopt");
}
me.nl_family = AF_NETLINK;
me.nl_groups = RTMGRP_IPV4_IFADDR;
if (bind(s,(struct sockaddr *)&me, sizeof(me)) < 0)
{
die("bind");
}
while (1)
{
memset(buf, 0, BUFSIZE);
if ((len=recv(s, buf, BUFSIZE, 0)) < 0)
{
die("recv");
}
for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
{
switch (msg_ptr->nlmsg_type)
{
case RTM_NEWADDR:
case RTM_DELADDR:
print_addr(msg_ptr);
break;
default:
printf("Ignored msg: type=%d\n", msg_ptr->nlmsg_type);
break;
}
}
}
close(s);
return 0;
}
Terminal 1:
# gcc addr-chg.c -o addr-chg && ./addr-chg
Terminal 2:
# ip address add 20.20.20.2/24 dev eth0
# ip address del 20.20.20.2/24 dev eth0
Terminal 1:
# gcc addr-chg.c -o addr-chg && ./addr-chg
add IP Address : 20.20.20.2
Interface : eth0
del IP Address : 20.20.20.2
Interface : eth0
例子 7
通过 Netlink 监测所有 namespace 中的 IP 地址的变化。
/*
* Monitor IP address change
*/
#include //printf, perror
#include //memset, strlen
#include //exit
#include //close
#include //msghdr
#include //inet_ntop
#include //sockaddr_nl,NLMSG_DATA
#include //rtgenmsg,ifinfomsg
#define BUFSIZE 4096
void die(char *s)
{
perror(s);
exit(1);
}
void print_addr(struct nlmsghdr * h)
{
struct ifaddrmsg * addr;
struct rtattr * attr;
int len;
addr = NLMSG_DATA(h);
len = RTM_PAYLOAD(h);
/* loop over all attributes for the NEWLINK message */
for (attr = IFLA_RTA(addr); RTA_OK(attr, len); attr = RTA_NEXT(attr, l)
{
switch (attr->rta_type)
{
case IFA_LABEL:
printf("Interface : %s\n", (char *)RTA_DATA(attr));
break;
case IFA_LOCAL:
{
if (h->nlmsg_type == RTM_NEWADDR) {
printf("ADD ");
}
else if (h->nlmsg_type == RTM_DELADDR) {
printf("DEL ");
}
else {
printf("nlmsg_type=%d ", h->nlmsg_type);
}
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, bytes[3]);
break;
}
default:
break;
}
}
printf("\n");
}
int main(void)
{
int s, len;
struct sockaddr_nl me;
struct msghdr msg;
struct iovec io;
char buf[BUFSIZE];
char cmsgbuf[BUFSIZE];
if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
{
die("socket");
}
int on = 1;
if (setsockopt(s, SOL_NETLINK, NETLINK_LISTEN_ALL_NSID, &on, sizeof(on< 0)
{
die("setsockopt");
}
memset(&me, 0, sizeof(me));
me.nl_family = AF_NETLINK;
me.nl_groups = RTMGRP_IPV4_IFADDR;
if (bind(s,(struct sockaddr *)&me, sizeof(me)) < 0)
{
die("bind");
}
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_name = &me;
msg.msg_namelen = sizeof(me);
msg.msg_control = &cmsgbuf;
msg.msg_controllen = BUFSIZE;
memset(&io, 0, sizeof(io));
msg.msg_iov->iov_base = buf;
msg.msg_iov->iov_len = BUFSIZE;
while (1)
{
memset(buf, 0, BUFSIZE);
memset(cmsgbuf, 0, BUFSIZE);
msg.msg_iov->iov_len = BUFSIZE;
msg.msg_controllen = BUFSIZE;
if ((len=recvmsg(s, &msg, 0)) < 0)
{
die("recvmsg");
}
for (struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level == SOL_NETLINK &&
cmsg->cmsg_type == NETLINK_LISTEN_ALL_NSID &&
cmsg->cmsg_len == CMSG_LEN(sizeof(int)))
{
int * data = (int *)CMSG_DATA(cmsg);
printf("Namespace ID : %d\n", *data);
}
}
for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
{
switch (msg_ptr->nlmsg_type)
{
case RTM_NEWADDR:
case RTM_DELADDR:
print_addr(msg_ptr);
break;
default:
printf("Ignored msg: type=%d\n", msg_ptr->nlmsg_type);
break;
}
}
}
close(s);
return 0;
}
Terminal 1:
# gcc addr-chg.c -o addr-chg && ./addr-chg
Terminal 2:
# ip netns add red
# ip netns set red 123
# ip netns
red (id: 123)
# ip address add 20.20.20.2/24 dev lo
# ip address del 20.20.20.2/24 dev lo
# ip netns exec red ip address add 20.20.20.3/24 dev lo
# ip netns exec red ip address del 20.20.20.3/24 dev lo
Terminal 1:
# gcc addr-chg.c -o addr-chg && ./addr-chg
ADD IP Address : 20.20.20.2
Interface : lo
DEL IP Address : 20.20.20.2
Interface : lo
Namespace ID : 123
ADD IP Address : 20.20.20.3
Interface : lo
Namespace ID : 123
DEL IP Address : 20.20.20.3
Interface : lo
参考文献
1 http://man7.org/linux/man-pages/man7/rtnetlink.7.html
2 http://man7.org/linux/man-pages/man7/netlink.7.html
3 https://gist.github.com/cl4u2/5204374