• RAW socket


    • raw socket分为2种

    • 网络层数据帧

    socket(AF_INET, SOCK_RAW, IPPROTO_XXX)           // 发送、接收网络层IP数据包 网络层

    • 数据链路帧

    socket(PF_PACKET, SOCK_RAW, htons(ETH_P_XXX))    // 发送、接收数据链路层

    【以ptp报文帧为例】

    raw socket通过帧报文中的DMAC地址知道发送给谁

    要注意的是send/recv函数使用MSG_WAITALL的时候,sockfd 必须是处于阻塞模式下,否则WAITALL不能起作用。

    • raw socket如何过滤

    • 用raw socket会收到很多的以太网帧
    • solve:告诉底层 raw socket, 哪些包是自己想要的,直接在底层被过滤掉,即bpf
    • 在终端输入命令获取过滤参数: sudo  tcpdump -dd -i eth0 tcp port 9090 -s 0
    • 在将过滤参数写入代码

    raw socket 使用 BPF 过滤报文_confirmwz的博客-CSDN博客_bpf socket

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. unsigned char buffer[102];
    12. void main()
    13. {
    14. int fd, n, i;
    15. struct sock_fprog filter;
    16. struct sock_filter code[] = {
    17. { 0x28, 0, 0, 0x0000000c },
    18. { 0x15, 0, 6, 0x000086dd },
    19. { 0x30, 0, 0, 0x00000014 },
    20. { 0x15, 0, 15, 0x00000006 },
    21. { 0x28, 0, 0, 0x00000036 },
    22. { 0x15, 12, 0, 0x00002382 },
    23. { 0x28, 0, 0, 0x00000038 },
    24. { 0x15, 10, 11, 0x00002382 },
    25. { 0x15, 0, 10, 0x00000800 },
    26. { 0x30, 0, 0, 0x00000017 },
    27. { 0x15, 0, 8, 0x00000006 },
    28. { 0x28, 0, 0, 0x00000014 },
    29. { 0x45, 6, 0, 0x00001fff },
    30. { 0xb1, 0, 0, 0x0000000e },
    31. { 0x48, 0, 0, 0x0000000e },
    32. { 0x15, 2, 0, 0x00002382 },
    33. { 0x48, 0, 0, 0x00000010 },
    34. { 0x15, 0, 1, 0x00002382 },
    35. { 0x6, 0, 0, 0x00040000 },
    36. { 0x6, 0, 0, 0x00000000 },
    37. };
    38. filter.len = sizeof(code)/sizeof(struct sock_filter);
    39. filter.filter = code;
    40. fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    41. if (fd < 0 ) {
    42. perror("socket fail\n");
    43. exit(1);
    44. }
    45. //设置 sk_filter
    46. if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0) {
    47. perror("setsockopt fail\n");
    48. exit(1);
    49. }
    50. /*block*/
    51. n = recvfrom(fd, buffer, sizeof(buffer), 0, NULL, NULL);
    52. if (n < 0) {
    53. perror("recvfrom none\n");
    54. exit(1);
    55. }
    56. for (i = 0; i < n ;i++) {
    57. printf("%02x ", buffer[i]);
    58. }
    59. }
    • 过滤指定mac地址的mac帧

    sudo tcpdump ether host 01:1b:19:00:00:00 -vvv -xx

    • 过滤指定的以太网帧类型为0x88f7的包

    root@pc123:/usrdata# tcpdump -dd ether proto 0x88F7
    [ 2073.317491] device eth0 entered promiscuous mode
    { 0x28, 0, 0, 0x0000000c },
    { 0x15, 0, 1, 0x000088f7 },
    { 0x6, 0, 0, 0x00040000 },
    { 0x6, 0, 0, 0x00000000 },
    [ 2073.357828] device eth0 left promiscuous mode

    • 混杂模式:

    在调试过程中,用tcpdump发现可以抓到包,但应用程序拿不到这个包。

    查资料发现:默认情况下,网卡接收一个包,网卡只把发给本机的包(包括广播包)传到上层的APP

           混杂模式就是指网卡能接受所有通过它的数据流,无论是什么模式、什么地址的。当网卡处于这种“混杂”模式时,它对所有遇到的每一个数据帧都产生一个硬件中断,以提醒操作系统处理流经该物理媒体上的每一个报文包。

    • 设置网卡eth0为混杂模式

    [root@localhost test]# ifconfig eth0 promisc

    • 取消网卡混杂模式

    [root@localhost test]# ifconfig enp0s3 -promisc

    • 查看方式: ifconfig 看网卡信息是否有PROMISC字段
    • 一个用raw socket的client/server例子 

    • 编译: g++ main.cpp -lpthread
    • 运行: ./a.out master 或者 ./a.out slave
    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. #include
    16. #include
    17. #include
    18. #include /* See NOTES */
    19. #include
    20. #include
    21. #include
    22. #include
    23. #include
    24. #include
    25. #include
    26. #include
    27. enum role {
    28. master,
    29. slave,
    30. };
    31. enum role mTmprole;
    32. int fd_raw;
    33. void master_process_msg()
    34. {
    35. uint8_t tmp_buffer[300] = {'0'};
    36. ssize_t size = recvfrom(fd_raw, tmp_buffer, sizeof(tmp_buffer), 0, NULL, NULL);
    37. if (size > 14) {
    38. printf("master recv msg size [%ld]\n", size);
    39. }
    40. }
    41. void slave_process_msg()
    42. {
    43. uint8_t tmp_buffer[300] = {'0'};
    44. ssize_t size = recvfrom(fd_raw, tmp_buffer, sizeof(tmp_buffer), 0, NULL, NULL);
    45. if (size > 14) {
    46. printf(" recv msg size [%ld] \n", size);
    47. }
    48. }
    49. void process_msg()
    50. {
    51. if (mTmprole == master) {
    52. master_process_msg();
    53. } else if (mTmprole == slave) {
    54. slave_process_msg();
    55. } else {
    56. printf("please selecet role\n");
    57. exit(0);
    58. }
    59. }
    60. void *thread_recv(void* arg)
    61. {
    62. printf("thread is running!!!!\n");
    63. while(1)
    64. {
    65. process_msg();
    66. }
    67. printf("thread recv over!!!!!!\n");
    68. }
    69. int start_recv_msg_thread()
    70. {
    71. int res;
    72. pthread_t a_thread;
    73. // void *thread_result;
    74. res = pthread_create(&a_thread, NULL, thread_recv, NULL);
    75. if(res != 0){
    76. printf("error create thread\n");
    77. }
    78. pthread_detach(a_thread);
    79. return 0;
    80. }
    81. void add_raw_filter()
    82. {
    83. // sudo tcpdump -dd ether host 01:1b:19:00:00:00 -vvv -xx
    84. /*struct sock_filter code[] = {
    85. { 0x20, 0, 0, 0x00000008 },
    86. { 0x15, 0, 2, 0x19000000 },
    87. { 0x28, 0, 0, 0x00000006 },
    88. { 0x15, 4, 0, 0x0000011b },
    89. { 0x20, 0, 0, 0x00000002 },
    90. { 0x15, 0, 3, 0x19000000 },
    91. { 0x28, 0, 0, 0x00000000 },
    92. { 0x15, 0, 1, 0x0000011b },
    93. { 0x6, 0, 0, 0x00040000 },
    94. { 0x6, 0, 0, 0x00000000 },
    95. };*/
    96. struct sock_fprog filter;
    97. filter.len = sizeof(code)/sizeof(struct sock_filter);
    98. filter.filter = code;
    99. if (setsockopt(fd_raw, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0) {
    100. perror("setsockopt fail\n");
    101. exit(1);
    102. }
    103. }
    104. int open_raw_socket(const char* interface)
    105. {
    106. fd_raw = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); //收发数据链路层的数据包
    107. if (fd_raw == -1) {
    108. printf("socket failed: [%d] [%s]\n", fd_raw, strerror(errno));
    109. return -1;
    110. } else {
    111. printf("socket [%d] \n", fd_raw);
    112. }
    113. // add_raw_filter();
    114. ioctl(fd_raw, FIONBIO, 0); //1:非阻塞 0:阻塞
    115. struct sockaddr_ll addr;
    116. memset(&addr, 0, sizeof(addr));
    117. addr.sll_ifindex = if_nametoindex(interface);
    118. addr.sll_family = AF_PACKET;
    119. addr.sll_protocol = htons(ETH_P_ALL);
    120. int err = bind(fd_raw, (struct sockaddr *) &addr, sizeof(addr));
    121. if (err != 0) {
    122. printf("raw socket bind failed: [%d] [%s]\n", err, strerror(errno));
    123. return -1;
    124. } else {
    125. printf("raw socket bind sucess [%s] [%d]\n", interface, fd_raw);
    126. }
    127. // 开启接收线程
    128. start_recv_msg_thread();
    129. return 0;
    130. }
    131. void enter_promis_mode(char* mac_name)
    132. {
    133. // 进入混杂模式
    134. char cmd[50] ={'0'}; // ifconfig %s promisc
    135. snprintf(cmd, 50, "ifconfig %s promisc", mac_name);
    136. printf("[%s]\n", cmd);
    137. system(cmd);
    138. }
    139. int main(int argc, char** argv)
    140. {
    141. if (argc >= 2) {
    142. printf("argv = %s\n", argv[1]);
    143. if (strcmp(argv[1], "master")) {
    144. mTmprole = master;
    145. } else if (strcmp(argv[1], "slave")) {
    146. mTmprole = slave;
    147. }
    148. } else {
    149. exit(0);
    150. }
    151. enter_promis_mode("eth0");
    152. open_raw_socket("eth0");
    153. while(1)
    154. {
    155. sleep(3);
    156. }
    157. exit(0);
    158. }

  • 相关阅读:
    go defer简介
    【计算机网络】广域网协议分析
    Ceres 自动求导解析-从原理到实践
    [错题]Mocha and Railgun
    私域运营系统的两个基础
    机器学习-集成学习LightGBM
    爬虫入门教程:爬虫概述
    基于ssm008医院门诊挂号系统+jsp【附PPT|开题|任务书|万字文档(LW)和搭建文档】
    Jenkins使用pipeline部署服务到远程服务器
    python烟花代码通过编写程序来模拟烟花的绽放过程
  • 原文地址:https://blog.csdn.net/m0_37844072/article/details/127212560