本文的大纲如下:
一、基础知识
1. Netfilter
2. Netlink机制
二、IP Queue编程接口
三、一个实现接收内核态发送的IP Queue数据包的用户态例程
1. libipq.h
2. libipq.c
3. ipq_user.c
四、应用程序的测试
1. 测试环境的建立
2. 程序的测试
附件
struct nlmsghdr
{
__u32 nlmsg_len; /*消息长度*/
__u16 nlmsg_type;/*消息类型*/
__u16 nlmsg_flags;/*额外的标志*/
__u32 nlmsg_seq; /*序列号*/
__u32 nlmsg_pid; /*进程号*/
};
/* Messages sent from kernel */
typedef struct ipq_packet_msg {
unsigned long packet_id; /* 报文的ID号 */
unsigned long mark; /* NF标记值 */
long timestamp_sec; /*报文到达时间(秒) */
long timestamp_usec; /* 报文到达时间(毫秒) */
unsigned int hook; /* 报文所处的NF hook点 */
char indev_name[IFNAMSIZ]; /* 流入网口名称 */
char outdev_name[IFNAMSIZ]; /* 流出网口名称 */
unsigned short hw_protocol; /*硬件协议(网络顺序)*/
unsigned short hw_type; /* 硬件类型 */
unsigned char hw_addrlen; /*硬件地址长度*/
unsigned char hw_addr[8]; /* 硬件地址 */
size_t data_len; /* 报文数据的长度 */
unsigned char payload[0]; /* 报文本身的数据,可选 */
} ipq_packet_msg_t;
typedef struct ipq_peer_msg {
union {
ipq_verdict_msg_t verdict;
ipq_mode_msg_t mode;
} msg;
} ipq_peer_msg_t;
typedef struct ipq_mode_msg {
unsigned char value;/* 请求的模式 */
size_t range;/* 请求拷贝的报文长度*/
} ipq_mode_msg_t;
typedef struct ipq_verdict_msg {
unsigned int value;
unsigned long id;
size_t data_len;
unsigned char payload[0];
} ipq_verdict_msg_t;
struct sockaddr_nl {
sa_family_t nl_family; /* AF_NETLINK */
unsigned short nl_pad; /* Zero. */
pid_t nl_pid; /* Process ID. */
__u32 nl_groups; /* Multicast groups mask. */
};
struct ipq_handle
{
int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;
};
struct ipq_handle *ipq_create_handle(u_int32_t flags, u_int32_t protocol);
int ipq_destroy_handle(struct ipq_handle *h);
ssize_t ipq_read(const struct ipq_handle *h, unsigned char *buf, size_t len);
int ipq_set_mode(const struct ipq_handle *h, u_int8_t mode, size_t len);
ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf);
int ipq_message_type(const unsigned char *buf);
int ipq_get_msgerr(const unsigned char *buf);
int ipq_set_verdict(const struct ipq_handle *h,
ipq_id_t id,
unsigned int verdict,
size_t data_len,
unsigned char *buf);
int ipq_ctl(const struct ipq_handle *h, int request, ...);
char *ipq_errstr(void);
struct ipq_handle *ipq_create_handle()
{
int status;
struct ipq_handle *h;
h = (struct ipq_handle *)malloc(sizeof(struct ipq_handle));
if (h == NULL) {
ipq_errno = IPQ_ERR_HANDLE;
return NULL;
}
memset(h, 0, sizeof(struct ipq_handle));
if (protocol == PF_INET)
h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);
else {
ipq_errno = IPQ_ERR_PROTOCOL;
free(h);
return NULL;
}
if (h->fd == -1) {
ipq_errno = IPQ_ERR_SOCKET;
close(h->fd);
free(h);
return NULL;
}
memset(&h->local, 0, sizeof(struct sockaddr_nl));
h->local.nl_family = AF_NETLINK;
/*传递本地的pid*/
h->local.nl_pid = getpid();
h->local.nl_groups = 0;
status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local));
if (status == -1) {
ipq_errno = IPQ_ERR_BIND;
close(h->fd);
free(h);
return NULL;
}
memset(&h->peer, 0, sizeof(struct sockaddr_nl));
h->peer.nl_family = AF_NETLINK;
/*代表通信的另一方为内核*/
h->peer.nl_pid = 0;
h->peer.nl_groups = 0;
return h;
}
int ipq_destroy_handle(struct ipq_handle *h)
{
if (h) {
close(h->fd);
free(h);
}
return 0;
}
int ipq_set_mode(const struct ipq_handle *h,
u_int8_t mode, size_t range)
{
/*构造一个向内核发送报文的结构体*/
struct {
struct nlmsghdr nlh;
ipq_peer_msg_t pm;
} req;
memset(&req, 0, sizeof(req));
req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req));
req.nlh.nlmsg_flags = NLM_F_REQUEST;
req.nlh.nlmsg_type = IPQM_MODE;
req.nlh.nlmsg_pid = h->local.nl_pid;
/*告诉协议栈所请求的报文传递模式*/
req.pm.msg.mode.value = mode;
/*请求内核返回报文的长度*/
req.pm.msg.mode.range = range;
return ipq_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len);
}
static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
const void *msg, size_t len)
{
int status = sendto(h->fd, msg, len, 0,
(struct sockaddr *)&h->peer, sizeof(h->peer));
if (status < 0)
ipq_errno = IPQ_ERR_SEND;
return status;
}
ssize_t ipq_read(const struct ipq_handle *h,
unsigned char *buf, size_t len)
{
return ipq_netlink_recvfrom(h, buf, len);
}
该函数直接调用ipq_netlink_recvfrom()函数,其源码为:
static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
unsigned char *buf, size_t len)
{
unsigned int addrlen;
int status;
struct nlmsghdr *nlh;
/*buf长度的校验,不能小于Netlink Message的头部长度*/
if (len < sizeof(struct nlmsgerr)) {
ipq_errno = IPQ_ERR_RECVBUF;
return -1;
}
addrlen = sizeof(h->peer);
status = recvfrom(h->fd, buf, len, 0,
(struct sockaddr *)&h->peer, &addrlen);
if (status < 0) {
ipq_errno = IPQ_ERR_RECV;
return status;
}
/*判断接收到的发送方的地址长度是否正确*/
if (addrlen != sizeof(h->peer)) {
ipq_errno = IPQ_ERR_RECV;
return -1;
}
/*内核态向用户态发送数据报文时,其pid=0*/
if (h->peer.nl_pid != 0) {
ipq_errno = IPQ_ERR_RECV;
return -1;
}
if (status == 0) {
ipq_errno = IPQ_ERR_NLEOF;
return -1;
}
nlh = (struct nlmsghdr *)buf;
/*判断是否发生数据报文被截断的情况*/
if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) {
ipq_errno = IPQ_ERR_RTRUNC;
return -1;
}
return status;
}
char *ipq_errstr(void)
{
return ipq_strerror(ipq_errno);
}
static char *ipq_strerror(int errcode)
{
if (errcode < 0 || errcode > IPQ_MAXERR)
errcode = IPQ_ERR_IMPL;
return ipq_errmap[errcode].message;
}
/*
* ipq_usr.c
*
* Testing program for receiving IP Queue packets from kernel 2.6.18.3
*
* Dec 1, 2008
* Godbach created.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "libipq.h"
struct ipq_handle *h = NULL;
static void sig_int(int signo)
{
ipq_destroy_handle(h);
printf("Exit: %s\n", ipq_errstr());
exit(0);
}
int main(void)
{
unsigned char buf[1024];
/* creat handle*/
h = ipq_create_handle(0, PF_INET);
if(h == NULL){
printf("%s\n", ipq_errstr());
return 0;
}
printf("ipq_creat_handle success!\n");
/*set mode*/
unsigned char mode = IPQ_COPY_PACKET;
int range = sizeof(buf);
int ret = ipq_set_mode(h, mode, range);
printf("ipq_set_mode: send bytes =%d, range=%d\n", ret, range);
/*register signal handler*/
signal(SIGINT, sig_int);
/*read packet from kernel*/
int status;
struct nlmsghdr *nlh;
ipq_packet_msg_t *ipq_packet;
while(1){
status = ipq_read(h, buf, sizeof(buf));
if(status > sizeof(struct nlmsghdr))
{
nlh = (struct nlmsghdr *)buf;
ipq_packet = ipq_get_packet(buf);
printf("recv bytes =%d, nlmsg_len=%d, indev=%s, datalen=%d, packet_id=%x\n", status, nlh->nlmsg_len,
ipq_packet->indev_name, ipq_packet->data_len, ipq_packet->packet_id);
}
}
return 0;
}
gcc libipq.c ipq_user.c -o ipq_user
[root@localhost ipq_user]# ./ipq_user
ipq_creat_handle success!
ipq_set_mode: send bytes =44, range=1024
[root@localhost ipq_user]# ./ipq_user
ipq_creat_handle success!
ipq_set_mode: send bytes =44, range=1024
recv bytes =148, nlmsg_len=148, indev=eth0, datalen=60, packet_id=c2f9b500
recv bytes =148, nlmsg_len=148, indev=eth0, datalen=60, packet_id=cb0c8c00
recv bytes =148, nlmsg_len=148, indev=eth0, datalen=60, packet_id=c72aa920
recv bytes =148, nlmsg_len=148, indev=eth0, datalen=60, packet_id=c2f9b3c0
Exit: No error
7.97 KB, 下载次数: 215
cao.jpg (58.14 KB, 下载次数: 185)
实际代码
欢迎光临 Chinaunix (http://bbs.chinaunix.net/) | Powered by Discuz! X3.2 |