netlink学习

netlink socket是一种用于用户态进程和内核态进程之间的通信机制。它通过为内核模块提供一组特殊的API,并为用户程序提供了一组标准的socket接口的方式,实现了全双工的通讯连接。

内核版本中,已经有许多内核模块使用netlink 机制,其中驱动模型中使用的uevent 就是基于netlink 实现。目前 netlink 协议族支持32种协议类型,它们定义在 include/uapi/linux/netlink.h 中:

#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
#define NETLINK_CTRL           17
/* 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_INET_DIAG    NETLINK_SOCK_DIAG
 
#define MAX_LINKS 32</span>

在4.1.x 的内核版本中已经定义了22种协议类型,其中NETLINK_ROUTE是用于设置和查询路由表等网络核心模块的,NETLINK_KOBJECT_UEVENT是用于uevent消息通信

以上这几种专用的协议类型无法满足,这时可以在不超过最大32种类型的基础之上自行添加。但是一般情况下这样做有些不妥,于是内核开发者就设计了一种通用netlink 协议类型(Generic Netlink)NETLINK_GENERIC,它就是一个Netlink复用器,便于用户自行扩展子协议类型

struct sockaddr_nl
{
  sa_family_t    nl_family;
  unsigned short nl_pad;
  __u32          nl_pid;
  __u32          nl_groups;
};

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 */
};

/* Flags values */
#define NLM_F_REQUEST           1       /* It is request message.       */
#define NLM_F_MULTI             2       /* Multipart message, terminated by NLMSG_DONE */
#define NLM_F_ACK               4       /* Reply with ack, with zero or error code */
#define NLM_F_ECHO              8       /* Echo this request            */
/* Modifiers to GET request */
#define NLM_F_ROOT      0x100   /* specify tree root    */
#define NLM_F_MATCH     0x200   /* return all matching  */
#define NLM_F_ATOMIC    0x400   /* atomic GET           */
#define NLM_F_DUMP      (NLM_F_ROOT|NLM_F_MATCH)
/* Modifiers to NEW request */
#define NLM_F_REPLACE   0x100   /* Override existing            */
#define NLM_F_EXCL      0x200   /* Do not touch, if it exists   */
#define NLM_F_CREATE    0x400   /* Create, if it does not exist */
#define NLM_F_APPEND    0x800   /* Add to end of list           */

在linux/netlink.h中定义了一些方便对消息进行处理的宏,这些宏包括:

宏NLMSG_DATA(nlhdr)           返回指向消息的数据部分的指针

#define NLMSG_ALIGNTO   4

#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
宏NLMSG_ALIGN(len)用于将 len 参数按 4 字节对齐。

#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr)))
宏NLMSG_LENGTH(len)用于计算整个 Netlink 消息的长度,包括头部和数据部分len。

#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
宏NLMSG_SPACE(len)返回计算整个 Netlink 消息的空间大小,包括头部和数据部分。该宏将 len 参数按 4 字节对齐,并加上头部长度和尾部填充字节,得到整个 Netlink 消息的空间大小。

#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
宏NLMSG_DATA(nlh)用于取得消息的数据部分的首地址,设置和读取消息数据部分时需要使用该宏。

#define NLMSG_NEXT(nlh,len)      ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
                      (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
宏NLMSG_NEXT(nlh,len)用于得到下一个消息的首地址,同时len也减少为剩余消息的总长度,该宏一般在一个消息被分成几个部分发送或接收时使用。

#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
                           (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
                           (nlh)->nlmsg_len <= (len))
宏NLMSG_OK(nlh,len)用于判断消息是否有len这么长。

#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
宏NLMSG_PAYLOAD(nlh,len)用于返回payload的长度。


#define NLMSG_TAIL(nmsg) \
    ((struct rtattr*)(((void*)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
宏NLMSG_TAIL(nlh) 返回指向 Netlink 消息数据部分结尾的指针,可以用于将数据添加到 Netlink 消息的末尾。

#define NLMSG_FOREACH(nmsg, len) \
    for ((nmsg) = (struct nlmsghdr*)(void*)(buf); \
         NLMSG_OK((nmsg), (len)); \
         (nmsg) = NLMSG_NEXT((nmsg), (len)))
宏NLMSG_FOREACH(nlh, len) 用于遍历一个包含多个 Netlink 消息的序列,它接受一个指向第一个 Netlink 消息的指针 nlh,以及序列的总长度 len,然后在每个循环迭代中将指针移动到下一个 Netlink 消息

struct nlattr 是 Netlink 协议中的一个重要结构体,用于表示 Netlink 消息的属性

struct nlattr {
    uint16_t nla_len; // 属性长度
    uint16_t nla_type; // 属性类型
};

nla_len 表示属性的长度,包括头部和数据部分

nla_type 表示属性的类型,不同类型的属性具有不同的含义。属性的类型是一个 16 位的无符号整数,由用户空间和内核空间之间的协商确定

struct nlattr 结构体通常被嵌入在 Netlink 消息的数据部分中,作为一个属性的头部,多个属性按照顺序依次排列,形成一个属性列表。在属性列表中,可以通过 nla_lennla_type 字段来确定每个属性的长度和类型。

由于属性的长度和类型都是固定长度的整数,因此可以通过指针运算来遍历属性列表。在 Linux 内核中,可以使用 nla_for_each_attr 宏来遍历属性列表,例如:

struct nlattr *attr;
int rem = nla_len;
nla_for_each_attr(attr, nlh, rem) {
    // 处理每个属性
}

其中,attr 是指向当前属性的指针,nla_for_each_attr 宏会在每个循环迭代中将指针移动到下一个属性。在循环体中,可以使用 nla_lennla_type 字段来访问当前属性的长度和类型。


struct rtattr 是 Linux 内核中路由相关数据结构中的一个重要结构体,用于表示一条路由的属性。

struct rtattr {
    unsigned short rta_len;  // 属性长度
    unsigned short rta_type; // 属性类型
};

  rta_len 表示属性的长度,包括头部和数据部分;

rta_type 表示属性的类型,不同类型的属性具有不同的含义。属性的类型是一个 16 位的无符号整数,由用户空间和内核空间之间的协商确定

struct rtattr 结构体通常被嵌入在一个路由表项的数据部分中,作为一个属性的头部。多个属性按照顺序依次排列,形成一个属性列表。在属性列表中,可以通过 rta_lenrta_type 字段来确定每个属性的长度和类型。

由于属性的长度和类型都是固定长度的整数,因此可以通过指针运算来遍历属性列表。在 Linux 内核中,可以使用 RTA_OKRTA_NEXT 宏来遍历属性列表,例如

struct rtattr *attr;
int len = rtmsg.rtm_msglen - NLMSG_ALIGN(sizeof(struct rtmsg));
for (attr = RTM_RTA(rtmsg); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
    // 处理每个属性
}

其中,attr 是指向当前属性的指针,RTM_RTA 宏返回指向第一个属性的指针,RTA_OK 宏判断当前属性是否合法,RTA_NEXT 宏返回下一个属性的指针。在循环体中,可以使用 rta_lenrta_type 字段来访问当前属性的长度和类型。

在路由相关的数据结构中,struct rtattr 结构体通常与 struct rtmsg 结构体一起使用,struct rtmsg 结构体用于表示一条路由的基本信息,而 struct rtattr 结构体用于表示路由的属性

RTA中涉及到的宏:

#define RTA_DATA(rta) \
    ((void*)(((char*)(rta)) + RTA_HDRLEN))

#define RTA_NEXT(rta, len) \
    ((len) -= RTA_ALIGN((rta)->rta_len), \
     (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len)))
RTA_NEXT 宏返回下一个属性的指针
	 

#define RTA_OK(rta, len) \
    ((len) >= (ssize_t)sizeof(struct rtattr) && \
     (rta)->rta_len >= sizeof(struct rtattr) && \
     (rta)->rta_len <= (len))
RTA_OK 宏判断当前属性是否合法


#define RTA_FOREACH(rta, len, attr) \
    for ((rta) = (struct rtattr*)(void*)(attr); \
         RTA_OK((rta), (len)); \
         (rta) = RTA_NEXT((rta), (len)))

#define RTA_HDRLEN      ((int) NLMSG_ALIGN(sizeof(struct rtattr)))

struct rtattr 结构体通常与 struct rtmsg 结构体一起使用,struct rtmsg 结构体用于表示一条路由的基本信息,而 struct rtattr 结构体用于表示路由的属性

struct rtmsg 是 Linux 内核中路由相关数据结构中的一个重要结构体,用于表示一条路由的基本信息。

struct rtmsg {
    unsigned char rtm_family;   // 地址族,如 AF_INET、AF_INET6
    unsigned char rtm_dst_len;  // 目标地址的掩码长度
    unsigned char rtm_src_len;  // 源地址的掩码长度
    unsigned char rtm_tos;      // 服务类型字段
    unsigned char rtm_table;    // 路由表标识
    unsigned char rtm_protocol; // 路由的协议标识
    unsigned char rtm_scope;    // 路由的可见性范围
    unsigned char rtm_type;     // 路由的类型
    unsigned int rtm_flags;     // 路由的标志位
};

其中,各个字段的含义如下:

  • rtm_family:地址族,表示地址的类型,例如 IPv4 或 IPv6。
  • rtm_dst_len:目标地址的掩码长度。
  • rtm_src_len:源地址的掩码长度。
  • rtm_tos:服务类型字段,用于标识 QoS(Quality of Service)信息。
  • rtm_table:路由表标识,用于区分不同的路由表。
  • rtm_protocol:路由的协议标识,用于标识路由的来源,例如 RIP、OSPF 等。
  • rtm_scope:路由的可见性范围,用于标识路由的作用域,例如本地路由、全局路由等。
  • rtm_type:路由的类型,用于标识路由的作用类型,例如主机路由、网关路由等。
  • rtm_flags:路由的标志位,用于表示路由的属性,例如是否是默认路由等。

struct rtmsg 结构体通常用于表示一条路由的基本信息,例如目标地址、掩码长度、下一跳地址等。在读取或设置路由信息时,可以使用 struct rtmsg 结构体来表示路由的基本属性,以便与 struct rtattr 结构体一起表示完整的路由信息


       struct iovec
struct msghdr

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值