使用 ioctl 或 netlink 向接口添加和删除 IP 地址
Posted
技术标签:
【中文标题】使用 ioctl 或 netlink 向接口添加和删除 IP 地址【英文标题】:add and remove IP addresses to an interface using ioctl or netlink 【发布时间】:2013-01-16 22:25:16 【问题描述】:是否可以在 C 中从接口(如环回)添加和删除 IP 地址?
我找到了 ioctl 和一些解释如何做到这一点的文档(例如 this link),但是它们都是用于设置地址而不是添加和删除?
按照我运行 strace 以添加新环回的建议,结果如下:
$ sudo strace ip addr add 1.2.3.4 dev lo
execve("/sbin/ip", ["ip", "addr", "add", "1.2.3.4", "dev", "lo"], [/* 17 vars */]) = 0
brk(0) = 0x1bab000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f221ed04000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, st_mode=S_IFREG|0644, st_size=109414, ...) = 0
mmap(NULL, 109414, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f221ece9000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340\r\0\0\0\0\0\0"..., 832) = 832
fstat(3, st_mode=S_IFREG|0644, st_size=14768, ...) = 0
mmap(NULL, 2109704, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f221e8e0000
mprotect(0x7f221e8e2000, 2097152, PROT_NONE) = 0
mmap(0x7f221eae2000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f221eae2000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200\30\2\0\0\0\0\0"..., 832) = 832
fstat(3, st_mode=S_IFREG|0755, st_size=1811128, ...) = 0
mmap(NULL, 3925208, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f221e521000
mprotect(0x7f221e6d6000, 2093056, PROT_NONE) = 0
mmap(0x7f221e8d5000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b4000) = 0x7f221e8d5000
mmap(0x7f221e8db000, 17624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f221e8db000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f221ece8000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f221ece7000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f221ece6000
arch_prctl(ARCH_SET_FS, 0x7f221ece7700) = 0
mprotect(0x7f221e8d5000, 16384, PROT_READ) = 0
mprotect(0x7f221eae2000, 4096, PROT_READ) = 0
mprotect(0x638000, 4096, PROT_READ) = 0
mprotect(0x7f221ed06000, 4096, PROT_READ) = 0
munmap(0x7f221ece9000, 109414) = 0
socket(PF_NETLINK, SOCK_RAW, 0) = 3
setsockopt(3, SOL_SOCKET, SO_SNDBUF, [32768], 4) = 0
setsockopt(3, SOL_SOCKET, SO_RCVBUF, [1048576], 4) = 0
bind(3, sa_family=AF_NETLINK, pid=0, groups=00000000, 12) = 0
getsockname(3, sa_family=AF_NETLINK, pid=6804, groups=00000000, [12]) = 0
sendto(3, "\24\0\0\0\22\0\1\3\214;\367P\0\0\0\0\0\0\0\0", 20, 0, NULL, 0) = 20
recvmsg(3, msg_name(12)=sa_family=AF_NETLINK, pid=0, groups=00000000, msg_iov(1)=["\344\3\0\0\20\0\2\0\214;\367P\224\32\0\0\0\0\4\3\1\0\0\0I\0\1\0\0\0\0\0"..., 16384], msg_controllen=0, msg_flags=0, 0) = 2000
brk(0) = 0x1bab000
brk(0x1bcc000) = 0x1bcc000
recvmsg(3, msg_name(12)=sa_family=AF_NETLINK, pid=0, groups=00000000, msg_iov(1)=["\24\0\0\0\3\0\2\0\214;\367P\224\32\0\0\0\0\0\0\1\0\0\0I\0\1\0\0\0\0\0"..., 16384], msg_controllen=0, msg_flags=0, 0) = 20
sendmsg(3, msg_name(12)=sa_family=AF_NETLINK, pid=0, groups=00000000, msg_iov(1)=["(\0\0\0\24\0\5\6\215;\367P\0\0\0\0\2 \0\0\1\0\0\0\10\0\2\0\1\2\3\4"..., 40], msg_controllen=0, msg_flags=0, 0) = 40
recvmsg(3, msg_name(12)=sa_family=AF_NETLINK, pid=0, groups=00000000, msg_iov(1)=["$\0\0\0\2\0\0\0\215;\367P\224\32\0\0\0\0\0\0(\0\0\0\24\0\5\6\215;\367P"..., 16384], msg_controllen=0, msg_flags=0, 0) = 36
exit_group(0) = ?
【问题讨论】:
你为什么不“strace ifconfig 相关参数”看看ifconfig
做了什么来添加它们。或者看看 ifconfig 的源代码,也许?
看来它根本没有为此使用ioctl...hmm
您可能想在这里查看ip
的来源:git.kernel.org/?p=linux/kernel/git/shemminger/…,也可以在这里阅读:linuxfoundation.org/collaborate/workgroups/networking/netlink
更奇怪的是,如果您使用 ioctl 为IPv4 设置地址(替换旧地址),但为IPv6 添加地址(让旧地址到位)。跨度>
@alk 您的链接现在已损坏。
【参考方案1】:
在 avahi 项目的帮助下。 这是代码:
$ cat netlink-test.cc
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <malloc.h>
#include <string.h>
#include <iostream>
using namespace std;
struct rtnl_handle
int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;
__u32 seq;
__u32 dump;
;
typedef struct
__u8 family;
__u8 bytelen;
__s16 bitlen;
__u32 flags;
__u32 data[8];
inet_prefix;
/* This uses a non-standard parsing (ie not inet_aton, or inet_pton)
* because of legacy choice to parse 10.8 as 10.8.0.0 not 10.0.0.8
*/
static int get_addr_ipv4(__u8 *ap, const char *cp)
int i;
for (i = 0; i < 4; i++)
unsigned long n;
char *endp;
n = strtoul(cp, &endp, 0);
if (n > 255)
return -1; /* bogus network value */
if (endp == cp) /* no digits */
return -1;
ap[i] = n;
if (*endp == '\0')
break;
if (i == 3 || *endp != '.')
return -1; /* extra characters */
cp = endp + 1;
return 1;
// This function is to open the netlink socket as the name suggests.
int netlink_open(struct rtnl_handle* rth)
int addr_len;
memset(rth, 0, sizeof(rth));
// Creating the netlink socket of family NETLINK_ROUTE
rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (rth->fd < 0)
perror("cannot open netlink socket");
return -1;
memset(&rth->local, 0, sizeof(rth->local));
rth->local.nl_family = AF_NETLINK;
rth->local.nl_groups = 0;
// Binding the netlink socket
if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0)
perror("cannot bind netlink socket");
return -1;
addr_len = sizeof(rth->local);
if (getsockname(rth->fd, (struct sockaddr*)&rth->local, (socklen_t*) &addr_len) < 0)
perror("cannot getsockname");
return -1;
if (addr_len != sizeof(rth->local))
fprintf(stderr, "wrong address lenght %d\n", addr_len);
return -1;
if (rth->local.nl_family != AF_NETLINK)
fprintf(stderr, "wrong address family %d\n", rth->local.nl_family);
return -1;
rth->seq = time(NULL);
return 0;
// This function does the actual reading and writing to the netlink socket
int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
unsigned groups, struct nlmsghdr *answer)
int status;
struct nlmsghdr *h;
struct sockaddr_nl nladdr;
// Forming the iovector with the netlink packet.
struct iovec iov = (void*)n, n->nlmsg_len ;
char buf[8192];
// Forming the message to be sent.
struct msghdr msg = (void*)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 ;
// Filling up the details of the netlink socket to be contacted in the
// kernel.
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = peer;
nladdr.nl_groups = groups;
n->nlmsg_seq = ++rtnl->seq;
if (answer == NULL)
n->nlmsg_flags |= NLM_F_ACK;
// Actual sending of the message, status contains success/failure
status = sendmsg(rtnl->fd, &msg, 0);
if (status < 0)
return -1;
// This is the utility function for adding the parameters to the packet.
int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
int len = RTA_LENGTH(alen);
struct rtattr *rta;
if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
return -1;
rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
rta->rta_type = type;
rta->rta_len = len;
memcpy(RTA_DATA(rta), data, alen);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
return 0;
int get_addr_1(inet_prefix *addr, const char *name, int family)
memset(addr, 0, sizeof(*addr));
if (strcmp(name, "default") == 0 ||
strcmp(name, "all") == 0 ||
strcmp(name, "any") == 0)
if (family == AF_DECnet)
return -1;
addr->family = family;
addr->bytelen = (family == AF_INET6 ? 16 : 4);
addr->bitlen = -1;
return 0;
if (strchr(name, ':'))
addr->family = AF_INET6;
if (family != AF_UNSPEC && family != AF_INET6)
return -1;
if (inet_pton(AF_INET6, name, addr->data) <= 0)
return -1;
addr->bytelen = 16;
addr->bitlen = -1;
return 0;
addr->family = AF_INET;
if (family != AF_UNSPEC && family != AF_INET)
return -1;
if (get_addr_ipv4((__u8 *)addr->data, name) <= 0)
return -1;
addr->bytelen = 4;
addr->bitlen = -1;
return 0;
int get_prefix(inet_prefix *dst, char *arg, int family)
int err;
unsigned plen;
memset(dst, 0, sizeof(*dst));
if (strcmp(arg, "default") == 0 ||
strcmp(arg, "any") == 0 ||
strcmp(arg, "all") == 0)
if (family == AF_DECnet)
return -1;
dst->family = family;
dst->bytelen = 0;
dst->bitlen = 0;
return 0;
err = get_addr_1(dst, arg, family);
if (err == 0)
switch(dst->family)
case AF_INET6:
dst->bitlen = 128;
break;
case AF_DECnet:
dst->bitlen = 16;
break;
default:
case AF_INET:
dst->bitlen = 32;
return err;
int add_IP_Address(char * IP, struct rtnl_handle * rth)
inet_prefix lcl;
// structure of the netlink packet.
struct
struct nlmsghdr n;
struct ifaddrmsg ifa;
char buf[1024];
req;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.n.nlmsg_type = RTM_NEWADDR;
req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL | NLM_F_REQUEST;
// req.n.nlmsg_type = RTM_DELADDR;
// req.n.nlmsg_flags = NLM_F_REQUEST;
req.ifa.ifa_family = AF_INET ;
req.ifa.ifa_prefixlen = 32 ;
req.ifa.ifa_index = 1 ; // get the loopback index
req.ifa.ifa_scope = 0 ;
get_prefix(&lcl, IP, req.ifa.ifa_family);
if (req.ifa.ifa_family == AF_UNSPEC)
req.ifa.ifa_family = lcl.family;
addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
if (rtnl_talk(rth, &req.n, 0, 0, NULL) < 0)
return -2;
int main(int argc, char **argv)
struct rtnl_handle * rth;
netlink_open(rth);
char * ip = "1.2.3.4";
return add_IP_Address(ip,rth);
删除只需要取消注释即可:
req.n.nlmsg_type = RTM_DELADDR;
req.n.nlmsg_flags = NLM_F_REQUEST;
和评论:
req.n.nlmsg_type = RTM_NEWADDR;
req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL | NLM_F_REQUEST;
【讨论】:
【参考方案2】:首先,此响应仅适用于 linux。网络/接口/路由配置没有在任何标准中定义,因此每个操作系统都实现了自己的方式来配置网络。
在 Linux 中,网络配置是通过 NETLINK 套接字(不是 ioctl,可能在我不知道的其他操作系统中)执行的,这是一个特殊的套接字系列,用于通信内核和用户空间。更准确地说,它是由 NETLINK_ROUTE 协议通过 NETLINK 套接字配置的。在您发布的 strace 中,您可以查看它是如何创建 NETLIK 套接字的:
socket(PF_NETLINK, SOCK_RAW, 0) = 3
还有
sendto(3, "\24\0\0\0\22\0\1\3\214;\367P\0\0\0\0\0\0\0\0", 20, 0, NULL, 0) = 20
我确定这是一个带有 RTM_NEWADDR 消息的 NETLINK_ROUTE 请求。 strace 的其余部分可以解释为 NETLINK_ROUTE 协议消息通信。
您可以了解有关 NETLINK_ROUTE here 的更多信息,并查找示例 here 和 Here,正如 @alk 所建议的那样,阅读 ip
源代码可能会很有趣,以了解究竟发生了什么。
【讨论】:
以上是关于使用 ioctl 或 netlink 向接口添加和删除 IP 地址的主要内容,如果未能解决你的问题,请参考以下文章