织梦CMS - 轻松建站从此开始!

罗索

数据链路层转发的简单实现

jackyhwei 发布于 2011-10-07 19:33 点击:次 
这个程序有个问题就是效率低。因为linux的PF_PACKET接口并不提供内核缓冲。可通过在用户层增加缓冲来缓解这个问题。还有个小问题是,因为网 络终端读写的特殊性,单纯的read或write并不能保证每次接收或发送出的都是一个完整的包
TAG:

前两天,老师安排我做一个在数据链路层转发数据的linux下的小程序。老师蛮严肃,所以这个程序的具体应用我也不是特别清楚。不过它的大致功能是这样 的:能够将ethx收到的所有数据从ethy接口转发出去,整个过程操作在数据链路层,这里ethx和ethy可能相同。

网上查了下资料并结合unp,了解到linux下访问数据链路层大致有两类方法。第一类方法是协议栈提供的操作接口,SOCK_PACKET接口或PF_PACKET接口;第二类方法是利用第三方提供的库,如libpcap或libnet等。

因为这个小程序的目标是简单易用,不可能让用户使用前都去装libpcap之类的库,所以我选择了第一类方法。第一类方法中的两个接口调用方法如下。

  1. fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL)); /* 这是较旧的方法,不推荐使用 */ 
  2. fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); /* 这是较新的方法 */ 

可以看出,这两个接口的使用方法与tcp或udp的接口的使用非常类似,当然socket函数的第三个参数用了“ htons(ETH_PALL)”,以前没见过这样的字序转换(tcp和udp中没见过,不知道这里为什么接口有点小不同)。PS:SOCK_RAW,表 示返回原始包,即不剥去链路层帧头;而SOCK_DGRAM,则表示返回煮熟了的包(链路层帧头被剥掉了)。ETH_P_ALL抓取链路层的所有包(不管 是arp包还是ip包),而ETH_P_IP则表示只抓取IP包。这些选项参数,请参考man文档(如man 7 packet).

程序的流程。解析命令行参数(如根据参数,判断从哪个网络接口读数据,又从哪个网络接口写数据)-->初始化网络接口(主要是bind接口和置接口为混杂模式)-->处理数据(打印并转发)。

为什么要bind网络接口呢?因为默认情况下,读或写操作操作的是所有网络接口,如果我们只对某个网络接口感兴趣,如eth0,我们就需要bind eth0。

置网络接口为混杂模式,能使我们抓取到所有经过该接口的数据包而不是通常情况下的只有目的地位为该接口的包。注意置网络接口为混杂模式,因为协议栈(IP层的筛选功能),并不会导致上层应用程序受到太大影响,当然这无疑增加了协议栈的负担。

下面是我的代码:

  1. /**************nd_rdwt.c***************/ 
  2. *author:bripengandre                  * 
  3. ****************************************/ 
  4. #include <stdio.h> 
  5. #include <string.h> 
  6. #include <unistd.h> 
  7. #include <ctype.h> 
  8. #include <stdlib.h> 
  9. #include <net/if.h> 
  10. #include <netpacket/packet.h> 
  11. #include <net/ethernet.h> 
  12. #include <arpa/inet.h> 
  13. #include <sys/select.h> 
  14. #include <sys/ioctl.h> 
  15. #include <errno.h> 
  16.  
  17. #define MAX_IF_NAME_SIZE 20 
  18. #define MAX_IF_CNT 10 
  19. #define IF_CNT_STEP 2 
  20. #define RECV_BUF_SIZE 1600 
  21. #define SEND_BUF_SIZE 1600 
  22.  
  23. enum _FD_TYPE 
  24.     SOCKFD_RD = 0, 
  25.     SOCKFD_WT = 1 
  26. }; 
  27.  
  28. typedef struct _if_config 
  29.     char if_rdwt_name[MAX_IF_NAME_SIZE+1]; 
  30.     char if_rd_name[MAX_IF_NAME_SIZE+1]; 
  31.     char if_wt_name[MAX_IF_NAME_SIZE+1]; 
  32.     long cap_cnt; 
  33.     char is_verbose; 
  34.     char is_promiscuous; 
  35. }if_conf_t, *pif_conf_t; 
  36.  
  37. static long cap_cnt; 
  38.  
  39. static void init_if_conf(if_conf_t * if_conf); 
  40. static void process_cmd(if_conf_t * if_conf); 
  41. static int init_sockfd(if_conf_t *if_conf, int type); 
  42. static void process_recv_pkt(char *recv_buf, int recv_len, char **send_buf
  43. int *send_len, if_conf_t *if_conf); 
  44. static void usage(char *exe_name); 
  45. static int get_if_index(int fd, char *if_name); 
  46. static int set_promiscuous(int fd, char *if_name); 
  47. static void dis_pkt(char *buf, int len); 
  48. static void print_char(char ch); 
  49.  
  50. int main(int argc, char *argv[]) 
  51.    int op, recv_len, send_len; 
  52.    if_conf_t if_conf; 
  53.    int fd_rd, fd_wt, max_fd; 
  54.    fd_set rd_set, wt_set; 
  55.    char recv_buf[RECV_BUF_SIZE], send_buf[SEND_BUF_SIZE]; 
  56.    char *buf; 
  57.    
  58.    init_if_conf(&if_conf); 
  59.    
  60.    while( (op = getopt(argc, argv, "i:r:w:c:np")) != -1) 
  61.    { 
  62.        switch(op) 
  63.        { 
  64.             case 'i'
  65.                 //printf("%d, %s/n", optind, argv[optind]); 
  66.                 strncpy(if_conf.if_rdwt_name, optarg, MAX_IF_NAME_SIZE); 
  67.                 //printf("%s/n", argv[optind]); 
  68.                 break
  69.             case 'r'
  70.                 strncpy(if_conf.if_rd_name, optarg, MAX_IF_NAME_SIZE); 
  71.                 break
  72.             case 'w'
  73.                 strncpy(if_conf.if_wt_name, optarg, MAX_IF_NAME_SIZE); 
  74.                 break
  75.             case 'c'
  76.                 if_conf.cap_cnt = atol(optarg); 
  77.                 break
  78.             case 'n'
  79.                 if_conf.is_verbose = 0; 
  80.                 break
  81.             case 'p'
  82.                 if_conf.is_promiscuous = 0; 
  83.                 break
  84.             default
  85.                 usage(argv[0]); 
  86.                 exit(0); 
  87.                 break
  88.         } 
  89.     } 
  90.     if(argc-optind >= 1) 
  91.     { 
  92.         usage(argv[0]); 
  93.         exit(0); 
  94.     } 
  95.  
  96.     process_cmd(&if_conf); 
  97.     if( (fd_rd = init_sockfd(&if_conf, SOCKFD_RD)) < 0) 
  98.     { 
  99.         fprintf(stderr, "init_sockfd error!/n"); 
  100.         exit(1); 
  101.     } 
  102.     if(strncmp(if_conf.if_rd_name, if_conf.if_wt_name, MAX_IF_NAME_SIZE) != 0) 
  103.     { 
  104.         if( (fd_wt = init_sockfd(&if_conf, SOCKFD_WT)) < 0) 
  105.         { 
  106.             fprintf(stderr, "init_sockfd error!/n"); 
  107.             exit(1); 
  108.         } 
  109.     } 
  110.     else 
  111.     { 
  112.         fd_wt = fd_rd; 
  113.     } 
  114.  
  115.     printf("start to capture packts from %s, and then send them to %s/n"
  116. ,if_conf.if_rd_name, if_conf.if_wt_name); 
  117.  
  118.     cap_cnt = 0; 
  119.     max_fd = fd_rd+1; 
  120.     while(1) 
  121.     { 
  122.         FD_ZERO(&rd_set); 
  123.         FD_SET(fd_rd, &rd_set); 
  124.         
  125.         switch(select(max_fd, &rd_set, NULL, NULL, NULL)) 
  126.         { 
  127.             case 0:  /* time out , should not execute here */ 
  128.                 break
  129.                 
  130.             case -1:  /* error */ 
  131.                 if(errno == EINTR) 
  132.                 { 
  133.                     continue
  134.                 } 
  135.                 else 
  136.                 { 
  137.                     sleep(1); /* sleep 1 second to wait for resource */ 
  138.                 } 
  139.                 break
  140.                 
  141.             default:  /* normal */ 
  142.                 if(FD_ISSET(fd_rd, &rd_set)) 
  143.                 { 
  144.                     memset(recv_buf, 0, sizeof(recv_buf)); /* not needed */ 
  145.                     recv_len = read(fd_rd, recv_buf, sizeof(recv_buf)); 
  146.                     if(recv_len < 0 ) 
  147.                     { 
  148.                         if(errno == EINTR) 
  149.                         { 
  150.                             continue
  151.                         } 
  152.                         else 
  153.                         { 
  154.                             fprintf(stderr, "read error: %s/n", strerror(errno)); 
  155.                             exit(1); 
  156.                         } 
  157.                     } 
  158.                     if(recv_len > 0) 
  159.                     { 
  160. send_len = SEND_BUF_SIZE; 
  161. buf = send_buf; 
  162. process_recv_pkt(recv_buf, recv_len, &buf, &send_len, &if_conf); 
  163. //memcpy(buf+send_len-12, "Hello World!", 12); 
  164. write(fd_wt, buf, send_len); /* should assure send successfully */ 
  165. if(if_conf.cap_cnt != 0 && cap_cnt >= if_conf.cap_cnt) 
  166. goto MAIN_EXIT; 
  167.                     } 
  168.                 } 
  169.         } 
  170.     } 
  171.    
  172. MAIN_EXIT:  
  173.     return 0; 
  174.  
  175. static void init_if_conf(if_conf_t * if_conf) 
  176.     memset(if_conf->if_rdwt_name, 0, sizeof(if_conf->if_rdwt_name)); 
  177.     strncpy(if_conf->if_rd_name, "eth0", MAX_IF_NAME_SIZE); 
  178.     strncpy(if_conf->if_wt_name, "eth0", MAX_IF_NAME_SIZE); 
  179.     if_conf->cap_cnt = 0; 
  180.     if_conf->is_verbose = 1; 
  181.     if_conf->is_promiscuous = 0; 
  182.  
  183. static void process_cmd(if_conf_t * if_conf) 
  184.     if(if_conf->if_rdwt_name[0] != '/0'
  185.     { 
  186.         strncpy(if_conf->if_rd_name, if_conf->if_rdwt_name, MAX_IF_NAME_SIZE); 
  187.         strncpy(if_conf->if_wt_name, if_conf->if_rdwt_name, MAX_IF_NAME_SIZE); 
  188.     } 
  189.  
  190. static int init_sockfd(if_conf_t *if_conf, int type) 
  191.     int sockfd; 
  192.     struct sockaddr_ll sll; 
  193.     char if_name[MAX_IF_NAME_SIZE+1]; 
  194.     
  195.     if( (sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0) 
  196.     { 
  197.         fprintf(stderr, "socket error: %s/n", strerror(errno)); 
  198.         return (-1); 
  199.     } 
  200.     
  201.     if(type == SOCKFD_RD) 
  202.     { 
  203.         strncpy(if_name, if_conf->if_rd_name, MAX_IF_NAME_SIZE); 
  204.     } 
  205.     else if(type == SOCKFD_WT) 
  206.     { 
  207.         strncpy(if_name, if_conf->if_wt_name, MAX_IF_NAME_SIZE); 
  208.     } 
  209.     
  210.     memset(&sll, 0, sizeof(sll)); 
  211.     sll.sll_family = PF_PACKET; 
  212.     if( (sll.sll_ifindex = get_if_index(sockfd, if_name)) == -1) 
  213.     { 
  214.         fprintf(stderr, "get_if_index of %s error!/n", if_name); 
  215.         return (-1); 
  216.     } 
  217.     sll.sll_protocol = htons(ETH_P_IP); 
  218.     if(bind(sockfd, (struct sockaddr *)&sll, sizeof(sll)) < 0) 
  219.     { 
  220.         fprintf(stderr, "bind error: %s/n", strerror(errno)); 
  221.         return (-1); 
  222.     } 
  223.     
  224.     if(type == SOCKFD_RD && if_conf->is_promiscuous) 
  225.     { 
  226.         if(set_promiscuous(sockfd, if_name) == 0) 
  227.         { 
  228.             fprintf(stderr, "set %s promiscuous error!/n", if_name); 
  229.         } 
  230.     } 
  231.     return sockfd; 
  232.  
  233. static void process_recv_pkt(char *recv_buf, int recv_len, char **send_buf
  234. int *send_len, if_conf_t *if_conf) 
  235.     cap_cnt++; 
  236.     if(if_conf->is_verbose) 
  237.     { 
  238.         printf("*****************packet %ld***********************/n", cap_cnt); 
  239.         dis_pkt(recv_buf, recv_len); 
  240.         printf("/n"); 
  241.     } 
  242.     *send_buf = recv_buf; 
  243.     *send_len = recv_len; 
  244.     
  245.  
  246. static void usage(char *exe_name) 
  247.     if(exe_name == NULL) 
  248.     { 
  249.         return
  250.     } 
  251.     fprintf(stderr, "Usage: %s [-i eth0] [-r eth1] [-w eth2] [-c 1000][-n] [-p]/n"
  252. , exe_name); 
  253.  
  254. static int get_if_index(int fd, char *if_name) 
  255.     struct ifreq ifr; 
  256.     
  257.     strncpy(ifr.ifr_name, if_name, MAX_IF_NAME_SIZE); 
  258.     if(ioctl(fd, SIOCGIFINDEX, &ifr) < 0) 
  259.     { 
  260.         fprintf(stderr, "get_if_index::ioctl error: %s!/n", strerror(errno)); 
  261.         return (-1); /* ifr.if_index can't be -1?? */ 
  262.     } 
  263.     return ifr.ifr_ifindex; 
  264.  
  265. static int set_promiscuous(int fd, char *if_name) 
  266.     struct ifreq ifr; 
  267.     
  268.     strncpy(ifr.ifr_name, if_name, MAX_IF_NAME_SIZE); 
  269.     if(ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) 
  270.     { 
  271.         fprintf(stderr, "set_promiscuous::ioctl get error: %s!/n", strerror(errno)); 
  272.         return 0; 
  273.     } 
  274.     ifr.ifr_flags |= IFF_PROMISC; 
  275.     if(ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) 
  276.     { 
  277.         fprintf(stderr, "set_promiscuous::ioctl set error: %s!/n", strerror(errno)); 
  278.         return 0; 
  279.     } 
  280.     
  281.     return 1; 
  282.  
  283. static void dis_pkt(char *buf, int len) 
  284.     unsigned int idx; 
  285.     unsigned char *ptr; 
  286.     int is_interpret; 
  287.     int tail_start, i, tail_len, space_cnt; 
  288.     
  289.     if(buf == NULL) 
  290.     { 
  291.         return
  292.     } 
  293.     
  294.     idx = 0; 
  295.     ptr = buf; 
  296.     is_interpret = 0; 
  297.     while(idx < len) 
  298.     { 
  299.         if(!is_interpret) 
  300.         { 
  301.             if((idx%16) == 0) 
  302.             { 
  303.                 printf("%08x: ", idx); 
  304.             } 
  305.             printf("%02x ", ptr[idx]); 
  306.             if((idx+1)%16 == 0) 
  307.             { 
  308.                 idx++; 
  309.                 idx-=16; 
  310.                 is_interpret = 1; 
  311.                 continue
  312.             } 
  313.             idx++; 
  314.         } 
  315.         else 
  316.         { 
  317.             if((idx)%16 == 0) 
  318.             { 
  319.                 printf("      "); 
  320.             } 
  321.             print_char(ptr[idx]); 
  322.             if((idx+1)%16 == 0) 
  323.             { 
  324.                 printf("/n"); 
  325.                 is_interpret = 0; 
  326.             } 
  327.             idx++; 
  328.         } 
  329.     }   
  330.     
  331.     if(idx%16 != 0) 
  332.     { 
  333.         tail_len = idx%16; 
  334.         tail_start = idx - tail_len; 
  335.         space_cnt = (16-tail_len)*3; 
  336.         
  337.         for(i = 0; i < space_cnt; i++) 
  338.         { 
  339.             fputc(' ', stdout); 
  340.         } 
  341.         printf("      "); 
  342.         for(; tail_start < idx; tail_start++) 
  343.         { 
  344.             print_char(ptr[tail_start]); 
  345.         } 
  346.     } 
  347.  
  348. static void print_char(char ch) 
  349.     if(isprint(ch)) 
  350.     { 
  351.         fputc(ch, stdout); 
  352.     } 
  353.     else 
  354.     { 
  355.         fputc('.', stdout); 
  356.     } 

这个程序有个问题就是效率低。因为linux的PF_PACKET接口并不提供内核缓冲。可通过在用户层增加缓冲来缓解这个问题。还有个小问题是,因为网 络终端读写的特殊性,单纯的read或write并不能保证每次接收或发送出的都是一个完整的包(tcp流读写是这样的,但链路层这个读写可能是根据帧结 构的,即有记录边界,所以这个问题可能并不存在,有待了解)~

因为寝室要断网了,就写到这里了,大家将就着看^_^

(bripengandre)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201110/15086.html]
本文出处:blog.csdn.net/bripengandre 作者:bripengandre
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容