Netlink编程-使用NETLINK_INET_DIAG协议

2012年8月25日 由 edsionte 留言 »

Netlink可以使得内核和用户进程进行双向通信,前文已经介绍过用户进程主动发起会话请求的例子。在那个示例程序中,必须同时编写用户态程序和内核模块,因为他们之间通信的协议是我们自己设定的,并没有使用netlink已有的通信协议。如果使用netlink已有的通信协议,那么我们无需编写内核模块,只需编写用户态程序即可。

本文将说明如何在用户态使用NETLINK_INET_DIAG协议。

1.创建netlink套接字

Netlink的使用方法与普通套接字并无太大差异,前文已经说明参数的差异,这里不再赘述。

struct sk_req {
	struct nlmsghdr nlh;
	struct inet_diag_req r;
};

int main(int argc, char **argv)
{
	int fd;
	struct sk_req req;
	struct sockaddr_nl dest_addr;
	struct msghdr msg;
	char buf[8192];
	char src_ip[20];
	char dest_ip[20];
	struct iovec iov;

	if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG)) < 0) {
		eprint(__LINE__, errno, "socket");
		return -1;
	}

2.发送消息到内核

用户进程通过msghdr结构将消息发送到内核中,因此必须首先初始化msghdr类型的变量msg。该数据结构与iovec类型的变量iov和sockaddr_nl类型的变量dest_addr关联,iov指向数据缓冲区,dest_addr用于描述目的套接字地址。

这里需要将nlmsghdr结构中的nlmsg_type指定为TCPDIAG_GETSOCK,说明获取的是TCP套接字。同时需要将nlmsg_flags字段指定为NLM_F_REQUEST |  NLM_F_ROOT,NLM_F_REQUEST是所有向内核发出消息请求的用户进程所必须所设置的,NLM_F_ROOT则指明返回所有的套接字。

	req.nlh.nlmsg_len = sizeof(req);
	req.nlh.nlmsg_type = TCPDIAG_GETSOCK;
	req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
	req.nlh.nlmsg_pid = 0;

	memset(&req.r, 0, sizeof(req.r));
	req.r.idiag_family = AF_INET;
	req.r.idiag_states = ((1 << TCP_CLOSING + 1) - 1);

	iov.iov_base = &req;
	iov.iov_len = sizeof(req);

	memset(&dest_addr, 0, sizeof(dest_addr));
	dest_addr.nl_family = AF_NETLINK;
	dest_addr.nl_pid = 0;
	dest_addr.nl_groups = 0;

	memset(&msg, 0, sizeof(msg));
	msg.msg_name = (void *)&dest_addr;
	msg.msg_namelen = sizeof(dest_addr);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

数据缓冲区通过req结构来表示,它封装了两个数据结构nlmsghdr和inet_diag_req。前者用来表示netlink消息头,它是必须封装的数据结构。后者是NETLINK_INET_DIAG协议所特有的请求会话的数据结构,具体结构如下:

struct inet_diag_req {
        __u8    idiag_family;           /* Family of addresses. */
        __u8    idiag_src_len;
        __u8    idiag_dst_len;
        __u8    idiag_ext;              /* Query extended information */

        struct inet_diag_sockid id;

        __u32   idiag_states;           /* States to dump */
        __u32   idiag_dbs;              /* Tables to dump (NI) */
};

这里需要特别注意的是inet_diag_req结构中的idiag_states字段,它用来表示内核将要反馈哪些状态的套接字到用户空间。用户空间通过一个枚举类型来表示套接字状态:

enum
{
  TCP_ESTABLISHED = 1,
  TCP_SYN_SENT,
  TCP_SYN_RECV,
  TCP_FIN_WAIT1,
  TCP_FIN_WAIT2,
  TCP_TIME_WAIT,
  TCP_CLOSE,
  TCP_CLOSE_WAIT,
  TCP_LAST_ACK,
  TCP_LISTEN,
  TCP_CLOSING
};

idiag_states字段的每一位表示一个状态,因此通过位偏移可以将具体某个状态位置1。上述的实例程序中,将表示所有状态的位都置1,因此内核将向用户进程反馈所有状态的套接字。

	if (sendmsg(fd, &msg, 0) < 0) {
		eprint(__LINE__, errno, "sendmsg");
		return -1;
	}

初始化相关的数据结构之后,接下来用户进程通过sendmsg函数发送消息到内核中。

3.用户进程接收消息

用户进程通过两层循环来接受并处理内核发送的消息。外层循环通过recvmsg函数不断接收内核发送的数据,在接收数据之前还要将新的数据缓冲区buf与iov进行绑定。内层循环将内核通过一次系统调用所发送的数据进行分批处理。对于本文所描述的NETLINK_INET_DIAG协议,内核每次向用户进程发送的消息通过inet_diag_msg结构描述:

struct inet_diag_msg {
        __u8    idiag_family;
        __u8    idiag_state;
        __u8    idiag_timer;
        __u8    idiag_retrans;

        struct inet_diag_sockid id;

        __u32   idiag_expires;
        __u32   idiag_rqueue;
        __u32   idiag_wqueue;
        __u32   idiag_uid;
        __u32   idiag_inode;
};

每一次外层循环将接收到的数据存放在buf缓冲区中,该缓冲区中存放了多条消息,结构如下:

struct nlhdrmsg struct inet_idiag_msg || struct nlhdrmsg struct inet_idiag_msg || ……

按照这样的数据存储方式,内层循环要做的就是依次获取这些数据结构。由于每条数据报都至少封装了nlmsghdr结构,因此具体的处理方法通过NLMSG_XXX宏即可完成。

	memset(buf, 0 ,sizeof(buf));
	iov.iov_base = buf;
	iov.iov_len = sizeof(buf);

	while (1) {
		int status;
		struct nlmsghdr *h;

		msg = (struct msghdr) {
			(void *)&dest_addr, sizeof(dest_addr),
				&iov, 1, NULL, 0, 0
		};
		status = recvmsg(fd, &msg, 0);
		if (status < 0) {
			if (errno == EINTR)
				continue;
			eprint(__LINE__, errno, "recvmsg");
			continue;
		}

		if (status == 0) {
			printf("EOF on netlink\n");
			close(fd);
			return 0;
		}

		h = (struct nlmsghdr *)buf;

		while (NLMSG_OK(h, status)) {
			struct inet_diag_msg *pkg = NULL;

			if (h->nlmsg_type == NLMSG_DONE) {
				close(fd);
				printf("NLMSG_DONE\n");
				return 0;
			}

			if (h->nlmsg_type == NLMSG_ERROR) {
				struct nlmsgerr *err;
				err = (struct nlmsgerr*)NLMSG_DATA(h);
				fprintf(stderr, "%d Error %d:%s\n", __LINE__, -(err->error), strerror(-(err->error)));
				close(fd);
				printf("NLMSG_ERROR\n");
				return 0;
			}

			pkg = (struct inet_diag_msg *)NLMSG_DATA(h);
			print_skinfo(pkg);
			get_tcp_state(pkg->idiag_state);
			h = NLMSG_NEXT(h, status);
		}//while
	}//while
	close(fd);
	return 0;

NLMSG_OK宏每次判断buf中的数据是否读取完毕,NLMSG_DATA取当前netlink消息头结构紧邻的inet_diag_msg结构,NLMSG_NEXT则取下一个netlink消息头结构。

pkg指向当前获取到的消息,接下来具体需求处理各个字段即可。上述程序中,print_skinfo函数打印pkg中的各个字段,get_tcp_state则是打印每个套接字连接的状态。

广告位

发表评论

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key