socket的创建(六)

socket的创建(六)

分类: libpcap+PF_RING源码分析   390人阅读  评论(1)  收藏  举报

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;

/*设定sockops,这样当用户采用bindconnect等函数时,内核对应相应的处理函数;下面看看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_skring_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,插入的方法是在链表的prevnext之间插入一个新的节点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。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值