linux netlink 机制,Linux Netlink 编程

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

参考文献

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值