【问题标题】:How I can choose network device in socket ()?如何在 socket() 中选择网络设备?
【发布时间】:2021-07-24 12:32:02
【问题描述】:

我的计划是为 Internet 连接编写基于用户的带宽控制。为此,我首先想写一个网线模拟器。

我的 Linux 机器有三个网络设备:

  • Eth0 正常连接网络。
  • Eth1 Eth2 是模拟网线的末端。

所以,我的程序要做的只是从eth1的输入中获取每个网络数据包,并将其放到eth2的输出中,并从eth2的输入中获取每个网络数据包并放入到eth1的输出。

使用s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)),我可以访问每个网络数据包。希望eth1eth2 处于混杂模式。

但是,我该如何选择网络设备呢?稍后,我想区分每个数据包中的 IP 源地址,以及该数据包必须等待多长时间,然后才能将其发送到其他网络设备。

【问题讨论】:

  • 使用socket()创建套接字后,您可以根据需要使用bind()setsockopt(SO_BINDTODEVICE)为其分配特定设备。
  • @stackunderflow 你看过linux桥接吗?这正是桥的作用,只是没有任何用户空间代码。
  • @Malt 目前这正是桥的作用。但是后来我的代码可以延迟满足某些要求的数据包(IP地址,服务,...)所以我可以对服务质量进行编程,例如。防止大量下载关闭实时应用程序的互联网线路。想象一下,我的电缆仿真器位于可能有 50 个用户的池的网关和 internetrouter 之间。我可以过滤和延迟所有用户进出互联网的所有数据包。而这正是桥梁无法做到的。 10 分钟前我写了工作代码。我会尽快发布。
  • @stackunderflow 您可能可以使用 iptables + netem 获得相同的结果。但是,如果您想编写自己的应用程序,请查看nfqueue

标签: linux sockets network-programming


【解决方案1】:

使用 socket() 你不能选择你的网络设备。这是通过 sendto () 和 recvfrom () 完成的。在 recvfrom () 和 sendto () 的参数 5 中,您放置了一个 sockaddr 结构。那里有一个 sockaddr.sll_ifindex 字段,您可以在其中选择网卡。在我的环境中,enp3s0 为 3,enp3s1 为 4。

查看电缆模拟器代码。我测试了 20 分钟,它没有问题。

// network cable emulator

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>

signed long long llmac (unsigned char* pmac)
  {
  signed long long sum= 0;
  signed long long sumd;
  for (signed long lauf= 0; lauf < 6; lauf++)
    {
    sumd= pmac[lauf];
    sum= sum | (sumd << (40 - lauf*8));
    }
  return sum; 
  }

struct macliste
  {
  signed long long anz;
  signed long long mac[100000];
  
  macliste ();
  void add (unsigned char* pmac);
  signed long neu (unsigned char* pmac);
  };

macliste::macliste ()
  {
  anz= 0;
  }

void macliste::add (unsigned char* pmac)
  {
  if (neu (pmac))
    {
    mac[anz]= llmac (pmac);
    anz++;
    }
  }

signed long macliste::neu (unsigned char* pmac)
  {
  signed long long smac;

  smac= llmac (pmac);
  for (signed long lauf= 0; lauf < anz; lauf++)
    if (mac[lauf] == smac)
      return 0;
  return 1;
  }

int main ()
  {
  int sockemp, socksenleft, socksenright;
  signed long result;
  
  unsigned char transferpuffer[10000];

  struct sockaddr_ll sockaddrleft;
  struct sockaddr_ll sockaddrright;
  struct sockaddr_ll sockaddrboth;
  signed long sockaddrsize= sizeof (sockaddrboth);

  char ifnameleft[IFNAMSIZ] = "enp3s0";
  char ifnameright[IFNAMSIZ] = "enp3s1";
  struct ifreq ifindexleft; 
  struct ifreq ifindexright; 
  int indexleft;
  int indexright;
  
  // Socket öffnen
  sockemp= socket (AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
  printf ("sockemp:      %5d\n", sockemp);
  
  // Socket öffnen
  socksenleft= socket (AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
  printf ("socksenleft:  %5d\n", socksenleft);
  
  // Socket öffnen
  socksenright= socket (AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
  printf ("socksenright: %5d\n", socksenright);

  printf ("\n");
  
  /* den Index des Interfaces ermitteln */
  printf ("Interfacename left:  %s\n", ifnameleft);
  printf ("Interfacename right: %s\n", ifnameright);
  printf ("\n");
  memset (&ifindexleft, 0, sizeof (struct ifreq));
  memset (&ifindexright, 0, sizeof (struct ifreq));
  strncpy (ifindexleft.ifr_name, ifnameleft, IFNAMSIZ);
  strncpy (ifindexright.ifr_name, ifnameright, IFNAMSIZ);
  result= ioctl (socksenleft, SIOCGIFINDEX, &ifindexleft);
  result= ioctl (socksenright, SIOCGIFINDEX, &ifindexright);

  indexleft= ifindexleft.ifr_ifindex;
  indexright= ifindexright.ifr_ifindex;
  printf ("interfaceresult: %5ld   indexleft  %5d\n", result, indexleft);
  printf ("interfaceresult: %5ld   indexright %5d\n", result, indexright);

  printf ("\n");

  /* Socketadresse vorbereiten */
  memset (&sockaddrleft, 0, sizeof(struct sockaddr_ll));
  sockaddrleft.sll_family   = PF_PACKET;             /* RAW communication */
  sockaddrleft.sll_hatype   = ARPHRD_ETHER;          /* Ethernet */
  sockaddrleft.sll_pkttype  = PACKET_OTHERHOST;      /* Ziel ist ein anderer Rechner */
  sockaddrleft.sll_ifindex  = indexleft;             /* Interface-Index */
  sockaddrleft.sll_halen    = ETH_ALEN;              /* Address length*/

  /* Socketadresse vorbereiten */
  memset (&sockaddrright, 0, sizeof(struct sockaddr_ll));
  sockaddrright.sll_family   = PF_PACKET;             /* RAW communication */
  sockaddrright.sll_hatype   = ARPHRD_ETHER;          /* Ethernet */
  sockaddrright.sll_pkttype  = PACKET_OTHERHOST;      /* Ziel ist ein anderer Rechner */
  sockaddrright.sll_ifindex  = indexright;            /* Interface-Index */
  sockaddrright.sll_halen    = ETH_ALEN;              /* Address length*/

  /* Socketadresse vorbereiten */
  memset (&sockaddrboth, 0, sizeof(struct sockaddr_ll));
  sockaddrboth.sll_family   = PF_PACKET;             /* RAW communication */
  sockaddrboth.sll_hatype   = ARPHRD_ETHER;          /* Ethernet */
  sockaddrboth.sll_pkttype  = PACKET_OTHERHOST;      /* Ziel ist ein anderer Rechner */
  sockaddrboth.sll_halen    = ETH_ALEN;              /* Address length*/

  // Schnittstellen in den Promiskuitätsmodus schalten
  
  struct ifreq ifr;
  int    raw_socket;
  char   deviceleft[100]= "enp3s0";
  char   deviceright[100]= "enp3s1";
 
  memset (&ifr, 0, sizeof (struct ifreq));

// Open A Raw Socket Deviceleft
    if ((raw_socket = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 1)
      {
      printf ("ERROR: Could not open socket, Got #?\n");
      exit (1);
      }

    /* Set the device to use */
    strcpy (ifr.ifr_name, deviceleft);

    /* Get the current flags that the device might have */
    if (ioctl (raw_socket, SIOCGIFFLAGS, &ifr) == -1)
      {
      perror ("Error: Could not retrive the flags from the device.\n");
      exit (1);
      }

    /* Set the old flags plus the IFF_PROMISC flag */
    ifr.ifr_flags |= IFF_PROMISC;
    if (ioctl (raw_socket, SIOCSIFFLAGS, &ifr) == -1)
      {
      perror ("Error: Could not set flag IFF_PROMISC");
      exit (1);
      }
    printf ("Entering promiscuous mode\n");

    /* Configure the device */

    if (ioctl (raw_socket, SIOCGIFINDEX, &ifr) < 0)
      {
      perror ("Error: Error getting the device index.\n");
      exit (1);
      }

// Open A Raw Socket Deviceright
    if ((raw_socket = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 1)
      {
      printf ("ERROR: Could not open socket, Got #?\n");
      exit (1);
      }

    /* Set the device to use */
    strcpy (ifr.ifr_name, deviceright);

    /* Get the current flags that the device might have */
    if (ioctl (raw_socket, SIOCGIFFLAGS, &ifr) == -1)
      {
      perror ("Error: Could not retrive the flags from the device.\n");
      exit (1);
      }

    /* Set the old flags plus the IFF_PROMISC flag */
    ifr.ifr_flags |= IFF_PROMISC;
    if (ioctl (raw_socket, SIOCSIFFLAGS, &ifr) == -1)
      {
      perror ("Error: Could not set flag IFF_PROMISC");
      exit (1);
      }
    printf ("Entering promiscuous mode\n");

    /* Configure the device */

    if (ioctl (raw_socket, SIOCGIFINDEX, &ifr) < 0)
      {
      perror ("Error: Error getting the device index.\n");
      exit (1);
      }

  // Pakete transferieren
  getchar ();
  signed long packetcounter= 0;
  
  macliste emplisteleft;
  macliste emplisteright;

  while (1)
    {
    result= recvfrom (sockemp, transferpuffer, 10000, 0, (struct sockaddr*) (&sockaddrboth), (socklen_t*)&sockaddrsize);
    if ((sockaddrboth.sll_ifindex != indexleft) && (sockaddrboth.sll_ifindex != indexright))
      continue;
    packetcounter++;
    if ((sockaddrboth.sll_ifindex == indexleft) && (emplisteright.neu (transferpuffer + 6)))
      {
      emplisteleft.add (transferpuffer + 6);   // Ethernet II Paket Quelladresse
      result= sendto (socksenright, transferpuffer, result, 0, (struct sockaddr*) &sockaddrright, sizeof (struct sockaddr_ll));
      printf ("sendresult: %5ld %2d\n", result, indexright);
      }
    if ((sockaddrboth.sll_ifindex == indexright) && (emplisteleft.neu (transferpuffer + 6)))
      {
      emplisteright.add (transferpuffer + 6);   // Ethernet II Paket Quelladresse
      result= sendto (socksenleft, transferpuffer, result, 0, (struct sockaddr*) &sockaddrleft, sizeof (struct sockaddr_ll));
      printf ("sendresult: %5ld %2d\n", result, indexleft);
      }
    printf ("Packet %10ld  empresult: %5ld %2d %20llx %10lld %10lld\n", packetcounter, result, sockaddrboth.sll_ifindex, llmac (transferpuffer + 6), emplisteleft.anz, emplisteright.anz);
    }
  return 0;
  }

在 ubuntu 20.04 服务器下使用 gcc 9.3.0 编译:

g++ transferraw.cc -std=c++17 -Wall -Wextra -Wconversion -pedantic-errors -O2 -o transferraw

【讨论】:

    猜你喜欢
    • 2019-08-12
    • 1970-01-01
    • 2023-02-21
    • 1970-01-01
    • 2013-07-30
    • 2016-10-26
    • 2011-03-04
    • 2021-05-02
    • 2012-04-21
    相关资源
    最近更新 更多