DIY TCP/IP 网络设备模块1

上一篇:Gannicus Guo的DIY TCP/IP之旅
5. 网络设备模块的实现
本章介绍DIY TCP/IP的网络设备模块的实现。网络设备层的逻辑包括,接收逻辑和发送逻辑。接收逻辑:DIY TCP/IP通过PF_PACKET域的socket接收链路层数据帧,模拟硬件网卡的接收。接收队列异步处理链路层接收的数据帧,解析以太网头部,根据以太网头部类型,将数据帧交给DIY TCP/IP的上层模块处理。发送逻辑:接收上层模块发送的数据帧,交给发送队列异步处理,同样通过PF_PACKET域的socket模拟硬件网卡的发送。本章基于第4章(从0开始)device.c的实现,继续模块化device.c,定义网络设备数据结构,封装pcap_loop接收的数据帧,实现接收队列和接收线程,在接收线程中异步处理pcap_loop接收的数据帧,解析数据帧,为调用上层模块的接口函数预留代码位置。后续章节实现DIY TCP/IP上层模块的接口函数后,替换预留的代码位置即可。
5.1 网络设备结构体
本节基于4.5节device.c的实现,继续模块化device.c,将与网络设备模块相关的逻辑放在device.c中,无关的逻辑放在其他模块文件中,先来看device.h中网络设备结构体的定义。
device.h

  #ifndef _DEVICE_H_
  #define _DEVICE_H_
  #include <pcap.h>
  
  #define MAX_NETWORK_SEGMENT_SIZE        65535
  #define PROMISC_ENABLE                  1
  #define PROMISC_DISABLE                 0
  #define TIMEOUT_MS                      512
  #define FILTER_BUFFER_SIZE              256
 
  typedef struct _net_device {
   pcap_t *pcap_dev;
  } net_device_t;
 
  net_device_t *netdev_init(char *ifname);
  void netdev_deinit(net_device_t *ndev);
  void netdev_start_loop(net_device_t *ndev);
  void netdev_stop_loop(net_device_t *ndev);
  net_device_t *netdev_get(void);
  #endif

Line 4-9: 与4.6节一致。
Line 11-12: 定义网络设备结构体net_device_t。便于描述,将pcap_t结构体称为pcap设备,网络设备结构体目前只是封装了pcap设备,尚未加入发送队列和接收队列数据结构的定义。
Line 15-19: 声明网络设备模块暴露给其他模块的接口函数。netdev_init初始化网络设备;netdev_deinit销毁网络设备;netdev_start_loop,开始从链路层接收数据帧;netdev_stop_loop,结束从链路层接收数据帧;netdev_get获取网络设备结构体的指针。根据这些函数接口的定义,重写device.c,实现这些接口函数,分离与网络设备层无关的代码逻辑。
device.c

  #include <stdio.h>
  #include <string.h>
  #include <errno.h>
  #include <malloc.h>
  
  #include "init.h"
  #include "common.h"
  #include "device.h"
  
  static net_device_t *gndev = NULL;
 
...
 

Line10: 定义静态net_device_t指针,用于保存netdev_init初始化的网络设备结构体的指针。
Line11 之后省略掉的代码是init_packet_filter与4.5节一致,pcap_callback与4.6节一致。

 net_device_t * netdev_init(char *ifname)
 {
  net_device_t *ndev = NULL;
  char pcap_packet_filter[FILTER_BUFFER_SIZE];
  char err_buf[PCAP_ERRBUF_SIZE];
  struct bpf_program filter_code;
 
  ndev = (net_device_t *)malloc(sizeof(net_device_t));
  if (ndev == NULL) {
      printf("No memory for net device, %s (%d)\n",
          strerror(errno), errno);
      return NULL;
  }
  gndev = ndev;
 
  printf("Network device init\n");
  /* obtain packet capture handle */
  ndev->pcap_dev = pcap_open_live(DEFAULT_IFNAME,
          MAX_NETWORK_SEGMENT_SIZE, PROMISC_ENABLE, TIMEOUT_MS, err_buf);
  if (ndev->pcap_dev == NULL) {
          printf("pcap_open_live failed, %s (%d)\n",
                         strerror(errno), errno);
      goto out;
  }
 
  /* set pcap filter */
  memset(pcap_packet_filter, 0, FILTER_BUFFER_SIZE);
  init_packet_filter(pcap_packet_filter, FILTER_BUFFER_SIZE);
  if (pcap_compile(ndev->pcap_dev, &filter_code,
                 pcap_packet_filter, 1, IPV4_NETWORK_MASK) < 0) {
          pcap_perror(ndev->pcap_dev, "pcap_compile");
      goto out;
  }
  if (pcap_setfilter(ndev->pcap_dev, &filter_code) < 0) {
          pcap_perror(ndev->pcap_dev, "pcap_setfilter");
      goto out;
  }
 
  return ndev;
 out:
  netdev_deinit(ndev);
  return NULL;
 }


Line 8-14: 申请内存,创建网络设备。将网络设备结构体的指针保存在静态指针gndev中。
Line 16-39: 网络设备结构体net_device_t封装了pcap设备。后续章节用到的libpcap库函数,包括本节的pcap_open_live,,pcap_compile和pcap_setfilter均通过网络设备结构体访问pcap设备。在netdev_init函数中,打开pcap设备,设置帧过滤条件,返回网路设备结构体的指针。如果line55-71的libpcap库函数返回出错,调用netdev_deinit销毁网络设备,返回NULL。

  void netdev_deinit(net_device_t *ndev)
  {
      if (ndev == NULL)
          return;
      printf("Network device deinit\n");
      if (ndev->pcap_dev)
          pcap_close(ndev->pcap_dev);
      free(ndev);
      gndev = NULL;
  }
  
  void netdev_start_loop(net_device_t *ndev)
  {
      if (ndev == NULL || ndev->pcap_dev == NULL)
          return;
      pcap_loop(ndev->pcap_dev, -1, pcap_callback, NULL);
      printf("pcap_loop ended\n");
  }
 
  void netdev_stop_loop(net_device_t *ndev)
  {
      if (ndev == NULL || ndev->pcap_dev == NULL)
          return;
      pcap_breakloop(ndev->pcap_dev);
  }

  net_device_t *netdev_get(void)
  {
      return gndev;
  }

Line 1-10: 销毁网络设备,调用pcap_close关闭pcap设备,然后释放netdev_init中为网络设备申请的内存。
Line 12-25: netdev_start_loop和netdev_stop_loop,是对pcap_loop和pcap_breakloop的封装,用于开始接和结束接收链路层数据帧。
Line 27-30: netdev_get返回网络设备结构体的指针,gndev供其他模块获取网络设备。
5.2 初始化模块
5.1节重写了device.c,目前device.c只包含与网络设备相关的逻辑。从零开始代码中的信号处理函数和入口main函数,放在DIY TCP/IP的初始化文件init.c中,init.c是DIY TCP/IP执行的入口。

  #include <stdio.h>
  #include <signal.h>
  
  #include "init.h"
  #include "device.h"
  
  void signal_handler(int sig_num)
  {
   net_device_t *ndev = NULL;
   ndev = netdev_get();
   if (ndev)
       netdev_stop_loop(ndev);
  } 
  
  int main(int argc, char *argv[])
  {
   int ret = 0;
   net_device_t *ndev = NULL;
 
   signal(SIGINT, signal_handler);
  
   ndev = netdev_init(DEFAULT_IFNAME);
   if (ndev == NULL)
       return -1;
  
   netdev_start_loop(ndev);
 
  out:
   netdev_deinit(ndev);
   return ret;
  }



Line 7-13: 信号处理函数,调用netdev_get获取网络设备结构体的指针,再调用netdev_stop_loop结束主循环。
Line 16-32: main函数是DIY TCP/IP的入口函数,先注册新号处理函数用于捕获SIGINT信号,netdev_init初始化网络设备模块,调用netdev_start_loop开始主循环。netdev_start_loop是对pcap_loop的封装,开始从链路层接收数据帧,直到终端键入ctrl+c结束循环。netdev_start_loop返回后,main函数继续执行到netdev_deinit销毁网络设备层,确保整个过程无内存泄漏。
把DIY TCP/IP初始化模块init.c加入到Makefile中,与device.c一起编译链接。Makefile修改如下:

 tcp_ip_stack:device.o init.o
   gcc -o tcp_ip_stack device.o init.o -lpcap
 device.o:device.c device.h init.h common.h
   gcc -c device.c
 init.o:init.c init.h
   gcc -c init.c
 clean:
   rm -rf *.o
   rm -rf tcp_ip_stack

Line 1-2: 目标tcp_ip_stack,加入对init.o的依赖,命令行gcc链接tcp_ip_stack时加入init.o。
Line 3-4: 与4.6节一致。
Line 5-7: 加入init.o目标的依赖init.c和init.h,命令行gcc –c编译生成目标文件init.o
下一篇:DIY TCP/IP 网络设备模块2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值