Socket的创建函数,在PF_RING,创建sokcet的函数为ring_create,当pfring.c中通过sokcet函数建立socket时,内核调用的ring_create函数,ring_create源码如下:
staticint ring_create(
#if(LINUX_VERSION_CODE>= KERNEL_VERSION(2,6,24))
struct net *net,
#endif
struct socket *sock, int protocol
#if(LINUX_VERSION_CODE>= KERNEL_VERSION(2,6,33))
, intkern
#endif
)
{
struct sock *sk;
struct pf_ring_socket *pfr;
int err = -ENOMEM;
if(enable_debug)
printk("[PF_RING]ring_create()\n");
/* Are you root, superuseror so ? 权限验证*/
if(!capable(CAP_NET_ADMIN))
return -EPERM;
//协议簇验证
if(sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
//协议验证
if(protocol != htons(ETH_P_ALL))
return -EPROTONOSUPPORT;
#if(LINUX_VERSION_CODE<= KERNEL_VERSION(2,6,11))
sk = sk_alloc(PF_RING, GFP_KERNEL, 1, NULL); //内核版本<2.6.11
#else
#if(LINUX_VERSION_CODE< KERNEL_VERSION(2,6,24))
// BD: API changed in2.6.12, ref:
//http://svn.clkao.org/svnweb/linux/revision/?rev=28201
sk = sk_alloc(PF_RING, GFP_ATOMIC,&ring_proto, 1); //内核版本 小于2.6.24
#else
sk = sk_alloc(net, PF_INET, GFP_KERNEL,&ring_proto); //内核版本 大于2.6.24
//根据内核版本的不同,调用不同的sk_alloc分配内存;
#endif
#endif
//sk内存分配失败
if(sk == NULL)
goto out;
sock->ops = &ring_ops;
/*设定sock的ops,这样当用户采用bind,connect等函数时,内核对应相应的处理函数;下面看看ring_ops结构体的定义;
static struct proto_ops ring_ops = {
.family = PF_RING,
.owner = THIS_MODULE,
/* Operations that make nosense on ring sockets. 这些操作在ring socket中没有使用*/
.connect = sock_no_connect,
.socketpair =sock_no_socketpair,
.accept = sock_no_accept,
.getname = sock_no_getname,
.listen = sock_no_listen,
.shutdown =sock_no_shutdown,
.sendpage =sock_no_sendpage,
/* Now the operations thatreally occur. 下面的函数在ring socket使用*/
.release = ring_release,
.bind = ring_bind,
.mmap = ring_mmap,
.poll = ring_poll,
.setsockopt = ring_setsockopt,
.getsockopt =ring_getsockopt,
.ioctl = ring_ioctl,
.recvmsg = ring_recvmsg,
.sendmsg = ring_sendmsg,
};
也就是说当应用程序采用bind绑定时,内核是调用的ring_bind函数,而应用程序调用mmap函数时,内核调用的是ring_mmap函数;当应用程序调用setsockopt/getsockopt函数时,内核调用的是ring_setsockopt/ring_getsockopt;
思考题2:它们是怎么对应起来的呢,原理是怎样的?
可以参考linux内核情景分析,熟悉socket机制
*/
sock_init_data(sock, sk);
// 初始化sock结构(即sk)各成员,并设定与套接字socket(即sock)的关联
#if(LINUX_VERSION_CODE<= KERNEL_VERSION(2,6,11))
sk_set_owner(sk, THIS_MODULE); //设置owner
#endif
ring_sk(sk) =ring_sk_datatype(kmalloc(sizeof(*pfr), GFP_KERNEL));
/*ring_sk和ring_sk_datatype是什么东东,是函数吗,没见过这么用的,难道是宏,一般也要使用大写啊,跟踪下源码。
#define ring_sk_datatype(__sk) ((struct pf_ring_socket *)__sk)
ring_sk_datatype(kmalloc(sizeof(*pfr), GFP_KERNEL))就是分配pf_ring_socket大小的内存,
#define ring_sk(__sk) ((__sk)->sk_protinfo)
sk->sk_protinfo指向pf_ring_socket大小的内存;
*/
//内存分配失败
if(!(pfr = ring_sk(sk))) {
sk_free(sk);
goto out;
}
//将pfr指向的内存清0,// pfr初始化各个成员
memset(pfr, 0, sizeof(*pfr));
//激活标志
pfr->ring_active = 0; /* We activate assoon as somebody waits for packets */
pfr->num_rx_channels =UNKNOWN_NUM_RX_CHANNELS;
// 通道ID
pfr->channel_id = RING_ANY_CHANNEL;
//RING的每个槽位的桶的大小,用户态也可以使用setsocketopt来调整
pfr->bucket_len = DEFAULT_BUCKET_LEN;
pfr->poll_num_pkts_watermark = 1; //DEFAULT_MIN_PKT_QUEUED;
//过滤器hash
pfr->handle_hash_rule =handle_sw_filtering_hash_bucket;
pfr->add_packet_to_ring =add_packet_to_ring;
//初始化等待队列
init_waitqueue_head(&pfr->ring_slots_waitqueue);
//初始化RING的锁
rwlock_init(&pfr->ring_index_lock);
rwlock_init(&pfr->ring_rules_lock);
//初始化使用的用户
atomic_set(&pfr->num_ring_users, 0);
INIT_LIST_HEAD(&pfr->sw_filtering_rules);
INIT_LIST_HEAD(&pfr->hw_filtering_rules);
//设定协议簇
sk->sk_family = PF_RING;
sk->sk_destruct = ring_sock_destruct;
//将sk插入队列
ring_insert(sk);
/*
ring_insert将刚刚创建的套接字插入ring_table,源码如下:
static inline void ring_insert(struct sock *sk)
{
struct ring_element *next;
/*
struct ring_element {
struct list_head list;
/*
list_head的结构体定义如下:
structlist_head {
struct list_head *next, *prev;
}
*/
struct sock *sk;
};
*/
struct pf_ring_socket *pfr;
if(enable_debug)
printk("[PF_RING]ring_insert()\n");
next = kmalloc(sizeof(struct ring_element),GFP_ATOMIC);
if(next != NULL) {
next->sk = sk;
ring_write_lock();
list_add(&next->list,&ring_table);
/*
static struct list_headring_table; //呵呵,ring_table是一个链表的表头
那么list_add函数的作用就是在链表ring_table的前面插入一个节点next->list
List_add函数定义如下:
staticinline void list_add(struct list_head *new,struct list_head *head)
{
__list_add(new, head, head->next);
}
其中_list_add定义如下:
#ifndef CONFIG_DEBUG_LIST
static inlinevoid __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
#else
extern void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next);
#endif
而_list_add函数根据是否定义CONFIG_DEBUG_LIST,有不同的实现,没有定义CONFIG_DEBUG_LIST,插入的方法是在链表的prev和next之间插入一个新的节点new,如果定义了CONFIG_DEBUG_LIST那么执行的函数不一样了。搜了一下,没有找到下面的那个_list_add,应该这个的作用和上面那个一样,插入一个节点。
*/
ring_write_unlock();
} else {
if(net_ratelimit())
printk("[PF_RING]net_ratelimit() failure\n");
}
ring_table_size++;
pfr = (structpf_ring_socket *)ring_sk(sk);
pfr->ring_pid =current->pid;
}
*/
pfr->master_ring = NULL;
pfr->ring_netdev =&none_device_element; /* Unbound socket */
pfr->sample_rate = 1; /* No sampling */
pfr->ring_id = ring_id_serial++;
ring_proc_add(pfr);
if(enable_debug)
printk("[PF_RING] ring_create():created\n");
return(0);
out:
return err;
}
其中ring_proc_add函数定义如下,这个函数再前面的讲解中已经提到过了:
staticvoid ring_proc_add(struct pf_ring_socket *pfr)
{
int debug = 0;
if(ring_proc_dir != NULL) {
char name[64];
snprintf(name, sizeof(name),"%d-%s.%d", pfr->ring_pid,
pfr->ring_netdev->dev->name, pfr->ring_id);
create_proc_read_entry(name,0 /* read-only */,
ring_proc_dir,
ring_proc_get_info, pfr);
//create_proc_read_entry函数的作用是创建一个目录
if(debug) printk("[PF_RING] Added/proc/net/pf_ring/%s\n", name);
}
}
讲到这里了,有必要总结一下,ring_create函数的作用,ring_create函数通过调用sk_alloc分配PF_RING的sock结构,然后使用sock_init_data初始化sock,专门用于描述特定net簇的私有区域的sock->sk_protinfo会被初始化为ring_opt类型指针*pfr,pfr包含了接收和插入数据包所需要的最基本的信息,有了pfr结构就有了PF_RING的一切信息,最后将生成的sock插入到ring_table。