iproute2 ipv6地址设置源码分析
iproute 作为网络接口的设置工具
具备我们大部分需要的功能。
以设置ipv6 地址为例来分析一下它的源码
它的实质其实是与内核建立一个socket通信,通过建立的fd进行网络接口的设置和信息读取。
简单来说,就四步:
建立与内核的连接-> 发送数据到内核 -> 从内核读取数据 -> 关闭连接
- 建立与内核的连接
int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions)
{return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
}
其中rth结构体如下
struct rtnl_handle {int fd; // fd是socket 打开的值struct sockaddr_nl local; // local 是本地地址struct sockaddr_nl peer; // peer 是邻居地址__u32 seq; // seq 是32位序列号__u32 dump; // dump 是32位int proto; // proto 是协议号FILE *dump_fp; // dump_fp 是文件名int flags; // flags 是 标志字段
};seq:
TCP会话的每一端都包含一个32位(bit)的序列号,该序列号被用来跟踪该端发送的数据量。每一个包中都包含序列号,在接收端则通过确认号用来通知发送端数据成功接收proto:
具体定义:
#define NETLINK_ROUTE 0 /* Routing/device hook */
#define NETLINK_UNUSED 1 /* Unused number */
#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */
#define NETLINK_SOCK_DIAG 4 /* socket monitoring */
#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
#define NETLINK_XFRM 6 /* ipsec */
#define NETLINK_SELINUX 7 /* SELinux event notifications */
#define NETLINK_ISCSI 8 /* Open-iSCSI */
#define NETLINK_AUDIT 9 /* auditing */
#define NETLINK_FIB_LOOKUP 10
#define NETLINK_CONNECTOR 11
#define NETLINK_NETFILTER 12 /* netfilter subsystem */
#define NETLINK_IP6_FW 13
#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
#define NETLINK_GENERIC 16
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
#define NETLINK_ECRYPTFS 19
#define NETLINK_RDMA 20
#define NETLINK_CRYPTO 21 /* Crypto layer */
#define NETLINK_SMC 22 /* SMC monitoring */#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG指定要和内核中的哪个子系统进行交互,目前支持:
NETLINK_ROUTE 与路由信息相关,包括查询、设置和删除路由表中的条目等。待会儿我们将以这类family举个实际的例子;
NETLINK_FIREWALL 接收由IPv4防火墙代码发送的包;
NETLINK_ARPD 可以在用户空间进行arp缓存的管理;
NETLINK_ROUTE6 在用户空间发送和接收路由表信息更新;
还有几种虽然没有实现,但已经有了定义,为以后扩展做好了准备。dump 和dump_fd作用还待研究
- 发送数据到内核
先总结再细说
struct {struct nlmsghdr n;struct ifaddrmsg ifa;char buf[256];
} req
req的基地址(req第一个数据存放的就是struct nlmsghdr n;传n的地址相当于传req的基地址)传给 iov的iov_base
struct iovec iov = {.iov_base = n,.iov_len = n->nlmsg_len
};
再把iov这个结构体地址传给msg_iov = &iov。
struct msghdr msg = {.msg_name = &nladdr,.msg_namelen = sizeof(nladdr),.msg_iov = &iov,.msg_iovlen = 1,
};
再发送数据给到内核具体分析如下:
static int ipaddr_modify(int cmd, int flags, int argc, char **argv)struct {struct nlmsghdr n;struct ifaddrmsg ifa;char buf[256];} req = {.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),.n.nlmsg_flags = NLM_F_REQUEST | flags,.n.nlmsg_type = cmd,.ifa.ifa_family = preferred_family,};在ipaddr_modify初始化req结构体
struct nlmsghdr
{__u32 nlmsg_len; /* Length of message */
__u16 nlmsg_type; /* Message type*/
__u16 nlmsg_flags; /* Additional flags */
__u32 nlmsg_seq; /* Sequence number */
__u32 nlmsg_pid; /* Sending process PID */
};
字段 nlmsg_len 指定消息的总长度,包括紧跟该结构的数据部分长度以及该结构的大小
#define NLMSG_ALIGNTO 4U
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
这样返回值在len大于零时,都是以4的倍数对齐(1-4=4;5-8=8;如此类推)struct ifaddrmsg {unsigned char ifa_family; /* Address type */unsigned char ifa_prefixlen; /* Prefixlength of address */unsigned char ifa_flags; /* Address flags */unsigned char ifa_scope; /* Address scope */int ifa_index; /* Interface index */};ifa_family: 地址类型(通常为AF_INET or AF_INET6))
ifa_prefixlen: 地址的地址掩码长度,如果改地址定义在这个family
ifa_flags:
ifa_scope: 地址的作用域
ifa_index: 接口索引与接口地址关联
ifa_prefixlen: 就是掩码64,128...
ifa_index: 利用if_nametoindex(name);函数把设备名转换为接口index,这个
值对应的网络设备是固定不变的。.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
初始化是nlmsghdr和ifaddrmsg两个结构体长度24。具体的数据内容是通过addattr_l来添加的
int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,int alen)
{int len = RTA_LENGTH(alen);struct rtattr *rta;if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {fprintf(stderr,"addattr_l ERROR: message exceeded bound of %d\n",maxlen);return -1;}//printf("%s[%d] NLMSG_ALIGN(n->nlmsg_len)[%d]\n",__func__,__LINE__,NLMSG_ALIGN(n->nlmsg_len));rta = NLMSG_TAIL(n);rta->rta_type = type;rta->rta_len = len;if (alen)memcpy(RTA_DATA(rta), data, alen);n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);return 0;
}跳过nlmsg_len,把数据考到req.buf中。
#define NLMSG_TAIL(nmsg) \((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
memcpy(RTA_DATA(rta), data, alen);再更新数据长度
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
这样下次还有其他数据要加入时,就会在上一个数据末尾加入。
所以想看看自己发的内容对不对,可以查看buf里面的内容。而这个buf分为rta和data;
struct rtattr {unsigned short rta_len;unsigned short rta_type;
};
前面存的时rta结构体,接着存的是实际数据
像设置ipv6地址的话就是存一个inet_prefix *addr结构体
inet_pton(AF_INET6, name, addr->data)
将点分文本的IP地址转换为“二进制网络字节序”的IP地址最后利用rtnl_talk -> __rtnl_talk 把数据发送到内核
static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,struct nlmsghdr *answer, size_t maxlen,bool show_rtnl_err, nl_ext_ack_fn_t errfn)
{int status;unsigned int seq;struct nlmsghdr *h;struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };struct iovec iov = {.iov_base = n,.iov_len = n->nlmsg_len};struct msghdr msg = {.msg_name = &nladdr,.msg_namelen = sizeof(nladdr),.msg_iov = &iov,.msg_iovlen = 1,};
...n->nlmsg_seq = seq = ++rtnl->seq;status = sendmsg(rtnl->fd, &msg, 0);if (status < 0) {perror("Cannot talk to rtnetlink");return -1;}发送struct msghdr msg这个结构体到内核中去
struct iovec { /* Scatter/gather arrayitems */void *iov_base; /*Starting address */size_t iov_len; /* Number of bytes to transfer*/};/* iov_base: iov_base指向数据包缓冲区,即参数buff,iov_len是buff的长度。msghdr中允许一次传递多个buff,以数组的形式组织在 msg_iov中,msg_iovlen就记录数组的长度(即有多少个buff)*/struct msghdr {void *msg_name; /* optional address */socklen_t msg_namelen; /* size of address */struct iovec *msg_iov; /* scatter/gather array */size_t msg_iovlen; /* # elements in msg_iov */void *msg_control; /* ancillary data, see below */size_t msg_controllen; /* ancillary databuffer len */int msg_flags; /* flags on received message */};/* msg_name:数据的目的地址,网络包指向sockaddr_in, netlink则指向sockaddr_nl;msg_namelen: msg_name 所代表的地址长度msg_iov: 指向的是缓冲区数组msg_iovlen: 缓冲区数组长度msg_control: 辅助数据,控制信息(发送任何的控制信息)msg_controllen: 辅助信息长度msg_flags: 消息标识*/
- 从内核读取数据
收数据的话,利用
iov.iov_base = buf;iov.iov_len = sizeof(buf);status = recvmsg(rtnl->fd, &msg, 0);
for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); )
具体的解析内容就没有分析,可以根据发送来反推。
- 关闭连接
最后利用rth_close来关闭连接的通道。
iproute2 ipv6地址设置源码分析相关推荐
- docker ip地址_理解 Docker 网络(番外) -- 《Docker 源码分析》勘误
前言 本来打算这篇文章是分析 Docker Overlay 网络是如何建立以及如何手动实现 Docker 的跨主机通信的.但是在完成了上一篇文章之后,打算找一些文章或者书籍印证我的文章是否正确.这时看 ...
- 【Android 性能优化】应用启动优化 ( 阶段总结 | Trace 文件分析及解决方案 | 源码分析梳理 | 设置主题的方案总结 ) ★
文章目录 一. 常用的耗时方法优化方案 ( 重要 ) 二. 源码分析梳理 1. 应用启动时间计算相关源码分析 2. Launcher 应用中启动 Android 应用流程 三. 启动白屏解决方案 An ...
- JavaFX源码分析实战:如何设置窗体标题小图标和任务栏图标
JavaFX实战系列 JavaFX源码分析和实战:javaFX线程结构分析 JavaFX源码分析和实战之launcher启动器:两种启动javaFX的方式及launch(args[])参数设置和获取 ...
- flink设置watermark以及事件时间字段源码分析
flink设置watermark以及事件时间字段源码分析 背景 1.1.提取时间戳字段,用于事件时间语义处理数据 1.2.设置水位线(水印)watermark TimestampAssigner 核心 ...
- 蓝牙(Bluetooth)---源码目录及设置应用源码分析
一 Bluetooth 的设置应用 packages\apps\Settings\src\com\android\settings\bluetooth\* 蓝牙设置应用及设置参数,蓝牙状态,蓝牙设备等 ...
- Netty4.x: Server端 设置 option 警告 Unknown channel option ‘xxxx‘ for channel 分析及解决 (附源码分析)
一.问题背景: 最近某springboot项目想嵌入一个用户聊天功能,打算使用 Rabbitmq + Netty4.x + Redis 来开发高性能聊天功能.花费三天时间所有功能都已实现.启动时却警告 ...
- Django源码分析2:本地运行runserver分析
django源码分析 本文环境python3.5.2,django1.10.x系列1.根据上一篇文章分析了,django-admin startproject与startapp的分析流程后,根据dja ...
- linux nDPI 协议检测 源码分析
关于nDPI的基本功能就不在这介绍了,有兴趣了解的读者可以阅读官方的快速入门指南:https://github.com/ntop/nDPI/blob/dev/doc/nDPI_QuickStartGu ...
- nginx源码分析之网络初始化
nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...
最新文章
- Python 捕获警告
- 前端笔记——获取url里面的参数值
- DataBase project physical design
- Qt 原理-MOC(1)Meta Object Compiler
- 博士申请 | 美国罗格斯大学王灏助理教授招收机器学习方向博士生
- weblogic中ssrf漏洞修复_Weblogic-SSRF漏洞复现
- C++ Low level performance optimize 2
- 为什么大家越来越重视大数据的发展?
- python jieba库的使用
- 基于matlab的图像拼接论文,基于MATLAB的图像拼接算法实现研究
- APP推广要做哪些?渠道?方案?竞争分析?
- ad怎么修改栅格_AD 10怎么设置栅格?
- echarts-JSON请求数据
- CSU2020期中测试模拟题1 问题E:小帅的字符串
- eclipse theia_如何在Ubuntu 18.04上设置Eclipse Theia Cloud IDE平台[快速入门]
- springboot+vue+Elementui学生考勤在线请假系统
- doom emacs如何安装新插件和自定义快捷键
- vscode 修改快捷键 (回到上一处光标位置,下一处光标位置)
- IDEA设置Working directory及作用
- pd.columns和pd.columns.tolist