绑定和分配mmap内核空间(bind_ring)--(七)

本文介绍了PF_RING中套接字绑定的过程,包括bind函数调用的内部实现,以及packet_ring_bind函数的具体操作,如设备查找、旧绑定移除、新绑定添加等。

用户态在创建了套接字后,接下来就调用bind函数,绑定套接字,而PF_RING实际做的就是为RING分配相应的空间。也就是说,一个套接字,都有一个与之对应的RING。在前面我们已经提到  sock->ops = &ring_ops;这样当应用空间调用bind函数中,内核调用ring_bind函数;即当系统调用bind触发时,内核ring_bind函数就触发了。下面讲解ring_bind函数;

/* Bind to a device */

static int ring_bind(struct socket*sock, struct sockaddr *sa, int addr_len)

{

 struct sock *sk = sock->sk;

 if(enable_debug)

   printk("[PF_RING] ring_bind() called\n");

  /*

   *     Check legality

  */

 if(addr_len != sizeof(struct sockaddr))

   return -EINVAL;

 if(sa->sa_family != PF_RING)

   return -EINVAL;

 if(sa->sa_data == NULL)

   return -EINVAL;

  /* Safety check: add trailing zero if missing*/

 sa->sa_data[sizeof(sa->sa_data) - 1] = '\0';

  if(enable_debug)

   printk("[PF_RING] searching device %s\n", sa->sa_data);

#if 0

 if(strcmp(sa->sa_data, "any") == 0)

   dev = &any_dev;

 else {

   if((dev = __dev_get_by_name(

#if(LINUX_VERSION_CODE >=KERNEL_VERSION(2,6,24))

                            &init_net,

#endif

                            sa->sa_data))== NULL) {

      if(enable_debug)

       printk("[PF_RING]search failed\n");

      return(-EINVAL);

   }

 }

#endif

 return(packet_ring_bind(sk,sa->sa_data));

}

 

ring_bind 函数前面都是一系列的判断,主要起作用的还是packet_ring_bind函数,函数定义如下:

static int packet_ring_bind(structsock *sk, char *dev_name)

{

 struct pf_ring_socket *pfr = ring_sk(sk);

 struct list_head *ptr, *tmp_ptr;

 ring_device_element *dev = NULL;

 if(dev_name == NULL)

   return(-EINVAL);

 list_for_each_safe(ptr, tmp_ptr, &ring_aware_device_list) {

   ring_device_element *dev_ptr = list_entry(ptr, ring_device_element,device_list);

   if(strcmp(dev_ptr->dev->name, dev_name) == 0) {

      dev = dev_ptr;

      break;

   }

 }

 

 if((dev == NULL) || (dev->dev->type != ARPHRD_ETHER))

   return(-EINVAL);

 if(enable_debug)

   printk("[PF_RING] packet_ring_bind(%s, bucket_len=%d)called\n",

          dev->dev->name, pfr->bucket_len);

 

  /* Remove old binding (by default binding tonone)

     BEFORE binding to a new device

  */

 ring_proc_remove(pfr);

 

  /*

IMPORTANT

Leave this statementhere as last one. In fact when

    the ring_netdev != &none_device_elementthe socket is ready to be used.

  */

 pfr->ring_netdev = dev;

 

  /* Timeto rebind to a new device */

 ring_proc_add(pfr);

  /*As the 'struct net_device' does not containthe number

    of RX queues, we can guess that its numberis the same as the number

    of TX queues. After the first packet hasbeen received by the adapter

    the num of RX queues is updated with thereal value

  */

#if(LINUX_VERSION_CODE >=KERNEL_VERSION(2,6,31))

 pfr->num_rx_channels =pfr->ring_netdev->dev->real_num_tx_queues;

#else

 pfr->num_rx_channels = 1;

#endif

 if(dev == &any_device_element)

   num_any_rings++;

 else {

   if(dev->dev->ifindex < MAX_NUM_IFIDX)

     num_rings_per_device[dev->dev->ifindex]++;

   else

      printk("[PF_RING] INTERNAL ERROR:ifindex %d for %s is > than MAX_NUM_IFIDX\n",

            dev->dev->ifindex, dev->dev->name);

 }

 return(0);

}

 

这个函数的主要作用是创建一个环状缓冲区的socket,然后对socket进行绑定。

`packet_mmap` 是 Linux 内核提供的一种高性能、零拷贝的网络数据包捕获机制。使用 `packet_mmap` 可以实现对网络数据包的实时捕获分析,常用于网络流量监控、安全审计等领域。 下面是一个基于 `packet_mmap` 的简单例子: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <linux/if_packet.h> #include <linux/if_ether.h> #include <linux/if.h> #define MAX_PACKET_SIZE 2048 int main(int argc, char *argv[]) { int sockfd, ret; struct ifreq ifr; struct sockaddr_ll sll; char buffer[MAX_PACKET_SIZE]; struct tpacket_req req; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; char control_buffer[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; int num_packets = 10; int packet_idx = 0; int bytes_received; int i; if (argc < 2) { printf("Usage: %s <interface_name>\n", argv[0]); return 1; } sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sockfd < 0) { perror("socket"); return 1; } memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, argv[1], IFNAMSIZ); if (ioctl(sockfd, SIOCGIFINDEX, &ifr) == -1) { perror("ioctl"); close(sockfd); return 1; } memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; sll.sll_ifindex = ifr.ifr_ifindex; sll.sll_protocol = htons(ETH_P_ALL); if (bind(sockfd, (struct sockaddr *)&sll, sizeof(sll)) < 0) { perror("bind"); close(sockfd); return 1; } memset(&req, 0, sizeof(req)); req.tp_block_size = getpagesize() * 4; req.tp_block_nr = 1; req.tp_frame_size = getpagesize(); req.tp_frame_nr = req.tp_block_size / req.tp_frame_size; if (setsockopt(sockfd, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)) < 0) { perror("setsockopt"); close(sockfd); return 1; } iov.iov_base = buffer; iov.iov_len = MAX_PACKET_SIZE; memset(&msg, 0, sizeof(msg)); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control_buffer; msg.msg_controllen = sizeof(control_buffer); for (i = 0; i < num_packets; i++) { bytes_received = recvmsg(sockfd, &msg, 0); if (bytes_received < 0) { perror("recvmsg"); close(sockfd); return 1; } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) { struct tpacket_auxdata *aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg); if (packet_idx == aux->tp_frame_nr) { // Process the received packet here printf("Received packet %d of size %d\n", packet_idx, bytes_received); packet_idx++; break; } } } } close(sockfd); return 0; } ``` 这个例子实现了从指定的网络接口上捕获 10 个数据包,并输出每个数据包的序号大小。具体实现中,首先创建一个 `AF_PACKET` 类型的 socket,并绑定到指定的网络接口上。然后设置 `PACKET_RX_RING` 选项,以便使用 `packet_mmap` 机制来捕获网络数据包。接下来,循环调用 `recvmsg` 函数来接收数据包,每次接收完一个数据包后,从 `msg` 结构体中获取附加数据 `PACKET_AUXDATA`,从而得到当前数据包的序号 `tp_frame_nr`,并输出序号大小。最后关闭 socket 并退出程序。 需要注意的是,使用 `packet_mmap` 机制需要对硬件环境内核配置有一定的要求,否则可能会出现性能瓶颈或者捕获不到数据包的情况。同时,使用 `packet_mmap` 机制也要注意防止缓冲区溢出等安全问题。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值