linux 指定网卡发送UDP数据

问题:

在这里插入图片描述

解决方法1(有权限要求):

可以使用SO_BINDTODEVICE绕过路由表查找,解决该问题。

注意:该方法需要程序有cap_net_raw和cap_net_bind_service权限,可以直接root,也可以使用setcap进行设置:

sudo setcap cap_net_raw,cap_net_bind_service=+ep 执行程序

代码实现1:

#include <arpa/inet.h>
#include <errno.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <netpacket/packet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>

#define UDP_ADDR "192.168.100.99"
#define UDP_PORT 2368
#define SEND_PORT 2368
#define MAC_SIZE 18
#define IP_SIZE 16
#define ETHX "eth0"

#define UDP_ADDR1 "192.168.100.99"
#define UDP_PORT1 2369
#define SEND_PORT1 2369
#define MAC_SIZE1 18
#define IP_SIZE1 16
#define ETHX1 "eth1"


// 获取本机mac
int get_local_mac(const char *eth_inf, char *mac) {
  struct ifreq ifr;
  int sd;

  bzero(&ifr, sizeof(struct ifreq));
  if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    printf("get %s mac address socket creat error\n", eth_inf);
    return -1;
  }

  strncpy(ifr.ifr_name, eth_inf, sizeof(ifr.ifr_name) - 1);

  if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) {
    printf("get %s mac address error\n", eth_inf);
    close(sd);
    return -1;
  }

  snprintf(mac, MAC_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
           (unsigned char)ifr.ifr_hwaddr.sa_data[0],
           (unsigned char)ifr.ifr_hwaddr.sa_data[1],
           (unsigned char)ifr.ifr_hwaddr.sa_data[2],
           (unsigned char)ifr.ifr_hwaddr.sa_data[3],
           (unsigned char)ifr.ifr_hwaddr.sa_data[4],
           (unsigned char)ifr.ifr_hwaddr.sa_data[5]);

  close(sd);

  return 0;
}
// 获取本机ip
int get_local_ip(const char *eth_inf, char *ip) {
  int sd;
  struct sockaddr_in sin;
  struct ifreq ifr;

  sd = socket(AF_INET, SOCK_DGRAM, 0);
  if (-1 == sd) {
    printf("socket error: %s\n", strerror(errno));
    return -1;
  }

  strncpy(ifr.ifr_name, eth_inf, IFNAMSIZ);
  ifr.ifr_name[IFNAMSIZ - 1] = 0;

  // if error: No such device
  if (ioctl(sd, SIOCGIFADDR, &ifr) < 0) {
    printf("ioctl error: %s\n", strerror(errno));
    close(sd);
    return -1;
  }

  memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
  snprintf(ip, IP_SIZE, "%s", inet_ntoa(sin.sin_addr));

  close(sd);
  return 0;
}
// 获取本机网卡名称
int get_local_dev(char *eth_name, const char *ip) {
  
  int sock;
  struct sockaddr_in sin;
  struct ifreq ifr;
  struct ifconf ifc;
  char buf[1024];
  int i;

  // 创建一个套接字
  sock = socket(AF_INET, SOCK_DGRAM, 0);
  if (sock == -1) {
    perror("socket error");
    return -1;
  }
  // 获取系统中所有网卡的信息
  ifc.ifc_len = sizeof(buf);
  ifc.ifc_buf = buf;
  if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
    perror("ioctl error");
    return -1;
  }

  // 遍历所有网卡,获取每个网卡的 IP 地址
  for (i = 0; i < ifc.ifc_len;) {
    struct ifreq *pifr = (struct ifreq *)(buf + i);

    // 调用 ioctl 函数获取网卡 IP 地址
    if (ioctl(sock, SIOCGIFADDR, pifr) < 0) {
      perror("ioctl error");
      return -1;
    }

    // 如果网卡 IP 地址与输入的 IP 地址相同,则该网卡就是输入的 IP 地址对应的

    if (strcmp(inet_ntoa(((struct sockaddr_in *)&pifr->ifr_addr)->sin_addr),
               ip) == 0) {
      strcpy(eth_name,pifr->ifr_name);
      break;
    }
    // 移动指针到下一个网卡
    i += sizeof(struct ifreq);
  }

  close(sock);
  return 0;
}

int main(int argc, char **argv) {

  struct sockaddr_in addr, mcast_addr;
  int fd = 0;
  struct ip_mreq ipmr;
  char ip[IP_SIZE];
  int ret = -1;
  unsigned char chrUDP[124] = {0};

  struct sockaddr_in addr1, mcast_addr1;
  int fd1 = 0;
  struct ip_mreq ipmr1;
  char ip1[IP_SIZE];
  int ret1 = -1;
  unsigned char chrUDP1[100] = {0};

  char dev_name[100] = {0};
  get_local_dev(dev_name, "192.168.100.202");

  printf("The device name is: %s\n", dev_name);

  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("create socket failed!");
    return -1;
  }

  get_local_ip(ETHX, ip);
  printf("local %s ip: %s\n", ETHX, ip);
  inet_pton(AF_INET, ip, &addr.sin_addr.s_addr);
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(SEND_PORT);
  // addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_addr.s_addr = inet_addr(ip);
  ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
  if (ret < 0) {
    printf("bind error!!!");
    perror("bind:");
    close(fd);
    return -1;
  }
  /* 指定接口 绑定接口 */
  struct ifreq nif;
  strcpy(nif.ifr_name, ETHX);
  if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&nif, sizeof(nif)) <
      0) {
    close(fd);
    printf("bind interface fail, errno: %d \r\n", errno);
    return -1;
  } else {
    printf("bind interface success \r\n");
  }

  memset(&mcast_addr, 0, sizeof(mcast_addr));
  mcast_addr.sin_family = AF_INET;
  mcast_addr.sin_addr.s_addr = inet_addr(UDP_ADDR);
  mcast_addr.sin_port = htons(UDP_PORT);

  if ((fd1 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("create socket1 failed!");
    return -1;
  }
  get_local_ip(ETHX1, ip1);
  printf("local1 %s ip1: %s\n", ETHX1, ip1);
  inet_pton(AF_INET, ip1, &addr1.sin_addr.s_addr);
  memset(&addr1, 0, sizeof(addr1));
  addr1.sin_family = AF_INET;
  addr1.sin_port = htons(SEND_PORT1);
  // addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr1.sin_addr.s_addr = inet_addr(ip1);
  ret1 = bind(fd1, (struct sockaddr *)&addr1, sizeof(addr1));
  if (ret1 < 0) {
    printf("bind1 error!!!");
    perror("bind1:");
    close(fd1);
    return -1;
  }
  /* 绑定接口 */
  /* 指定接口 */
  struct ifreq nif1;
  strcpy(nif1.ifr_name, ETHX1);
  if (setsockopt(fd1, SOL_SOCKET, SO_BINDTODEVICE, (char *)&nif1,
                 sizeof(nif1)) < 0) {
    close(fd1);
    printf("bind1 interface fail, errno: %d \r\n", errno);
    return -1;
  } else {
    printf("bind1 interface success \r\n");
  }
  memset(&mcast_addr1, 0, sizeof(mcast_addr1));
  mcast_addr1.sin_family = AF_INET;
  mcast_addr1.sin_addr.s_addr = inet_addr(UDP_ADDR1);
  mcast_addr1.sin_port = htons(UDP_PORT1);

  while (1) {
    if (sendto(fd, (const char *)chrUDP, sizeof(chrUDP), 0,
               (struct sockaddr *)&mcast_addr, sizeof(mcast_addr)) < 0) {
      perror("sendto");
      return -1;
    }
    // printf("send ok!\n");

    if (sendto(fd1, (const char *)chrUDP1, sizeof(chrUDP1), 0,
               (struct sockaddr *)&mcast_addr1, sizeof(mcast_addr1)) < 0) {
      perror("sendto1");
      return -1;
    }
    // printf("send1 ok!\n");

    ms_sleep(0, 20);
  }

  setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mcast_addr,
             sizeof(mcast_addr));
  close(fd);
  setsockopt(fd1, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mcast_addr1,
             sizeof(mcast_addr1));
  close(fd1);

  return 0;
}

解决方法2(无权限要求):

使用ioctl和IP_BOUND_IF绑定网卡设备

注意:该方法中linux 低版本内核可能不适用

代码实现2:

int get_interface_name(const char *ip_address, char *interface_name) {
    if (ip_address == NULL) return -1;
    struct ifconf ifc;
    struct ifreq *ifr;
    int sockfd;
    int i, n;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket");
        return -1;
    }

    ifc.ifc_len = 512;
    ifc.ifc_req = (struct ifreq *) malloc(ifc.ifc_len);

    if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) {
        perror("ioctl");
        return -1;
    }

    ifr = ifc.ifc_req;
    n = ifc.ifc_len / sizeof(struct ifreq);

    for (i = 0; i < n; i++) {
        if (ioctl(sockfd, SIOCGIFADDR, &ifr[i]) == -1) {
            perror("ioctl");
            continue;
        }
        if (strcmp(inet_ntoa(((struct sockaddr_in *) &ifr[i].ifr_addr)->sin_addr),
                   ip_address) == 0) {
            strcpy(interface_name, ifr[i].ifr_name);
            break;
        }
    }

    close(sockfd);
    free(ifc.ifc_req);
    return 0;
}

然后使用setsockopt绑定
  struct ifreq ifr;
  int ifindex;

  ifindex = if_nametoindex(dev_name);
  if (ifindex == 0) {
    perror("if_nametoindex");
    return -1;
  }

  memset(&ifr, 0, sizeof(ifr));
  ifr.ifr_ifindex = ifindex;
  if (setsockopt(fd, SOL_SOCKET, IP_BOUND_IF, (void *)&ifr, sizeof(ifr)) ==
      -1) {
    perror("setsockopt");
    return -1;
  }

也可以在代码中设置CAP_NET_RAW 和 CAP_NET_BIND_SERVICE 权限,避免root情况

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/capability.h>

int main(int argc, char* argv[])
{
    cap_t caps;
    cap_value_t cap_list[2];

    // 初始化能力集
    caps = cap_get_proc();
    if (caps == NULL) {
        perror("cap_get_proc");
        exit(1);
    }

    // 设置 CAP_NET_RAW 和 CAP_NET_BIND_SERVICE 权限
    cap_list[0] = CAP_NET_RAW;
    cap_list[1] = CAP_NET_BIND_SERVICE;
    if (cap_set_flag(caps, CAP_PERMITTED, 2, cap_list, CAP_SET) < 0) {
        perror("cap_set_flag");
        exit(1);
    }
    if (cap_set_flag(caps, CAP_EFFECTIVE, 2, cap_list, CAP_SET) < 0) {
        perror("cap_set_flag");
        exit(1);
    }

    // 设置能力集
    if (cap_set_proc(caps) < 0) {
        perror("cap_set_proc");
        exit(1);
    }

    cap_free(caps);

    // 在此之后的代码中程序将拥有 CAP_NET_RAW 和 CAP_NET_BIND_SERVICE 权限
    // ......

    return 0;
}

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux网卡指的是在一台Linux服务器上同时使用两个网卡进行网络连接。通过配置双网卡,可以实现网络负载均衡、网络冗余和网络隔离等功能。 在Linux系统中,可以使用ifconfig命令查看和设置网卡信息。首先,通过ifconfig命令查看当前系统中存在的网卡接口,然后通过配置文件修改网络接口的配置信息,使得两个网卡同时工作。 在C程序中,可以使用socket函数创建UDP套接字,然后使用bind函数将套接字绑定到一个特定的本地IP地址和端口号上。接着,使用recvfrom函数从UDP套接字中接收数据报文,并使用sendto函数将数据报文发送指定的目标IP地址和端口号。 在实际应用中,可以使用两个UDP套接字分别监听两个网卡的IP地址和端口号,以实现双网卡的功能。通过套接字的接收和发送函数,可以在不同的网卡上接收和发送数据报文。 另外,还可以通过设置套接字的选项,如SO_REUSEADDR和SO_BINDTODEVICE等,来实现更精细的控制。 综上所述,Linux网卡和C语言的socket编程可以结合使用,通过配置双网卡实现网络负载均衡和网络隔离等功能,并使用UDP套接字进行数据的接收和发送。 ### 回答2: Linux网卡C语言Socket UDP是指在Linux系统下,基于C语言编程,使用Socket和UDP协议进行网络通信的双网卡配置。 网卡是计算机连接网络的一种硬件设备,有时我们需要使用多个网卡来实现更高效的网络通信。在Linux系统中,我们可以通过配置双网卡来实现此目的。 使用C语言编程,我们可以使用Socket库函数来进行网络通信。Socket是一种能够在不同计算机之间进行通信的API接口,可以用于UDP、TCP等网络协议。 UDP是用户数据报协议,它是一种无连接的、不可靠的传输协议,适用于一对一或多对一的通信。UDP在传输数据时效率高,但无法保证数据的可靠性和顺序性。 在双网卡配置中,我们可以使用C语言编写程序,通过Socket和UDP协议进行通信。首先,我们需要调用Socket函数创建一个套接字,并使用bind函数将套接字绑定到一个特定的IP地址和端口号上。然后,我们可以使用recvfrom函数接收来自其他计算机的数据,并使用sendto函数将数据发送给其他计算机。 通过配置两个不同的网卡,我们可以利用双网卡进行双向通信。例如,我们可以将一个网卡连接到局域网,另一个网卡连接到互联网,这样就可以实现局域网和互联网之间的数据传输。 总之,Linux网卡C语言Socket UDP是指在Linux系统下,利用C语言编程,通过Socket库函数和UDP协议进行双网卡配置和网络通信的技术。这种技术可以实现高效的双向数据传输,并适用于一对一或多对一的通信场景。 ### 回答3: Linux网卡 c socket UDP 是指在 Linux 操作系统中,使用 C 编程语言编写的程序,通过 Socket 编程接口,使用 UDP 协议进行网络通信。 双网卡是指计算机系统中同时安装了两块或多块网卡,通过这些网卡可以连接到不同的网络。在使用双网卡的情况下,我们可以在程序中指定使用哪一块网卡进行数据传输,实现数据的接收和发送。在使用双网卡的环境下,程序可以同时监听多个网络上的数据包,并且可以选择性地进行数据的处理。 Socket 是一种网络编程的接口,是一种软件接口,用于支持进程间的网络通信。在 C 语言中,我们使用 Socket 编程接口来创建网络连接,发送和接收数据。通过 Socket 编程接口,我们可以轻松地实现各种网络通信方式,包括 TCP、UDP 以及其他协议。 UDP 是用户数据报协议(User Datagram Protocol)的缩写,它是一种无连接的、不可靠的传输协议。与 TCP 不同,UDP 不需要在传输之前建立连接,直接将数据通过 UDP 数据发送给目标主机。由于 UDP 无连接且不可靠,所以它的传输速度比 TCP 快,但是在传输过程中可能会出现数据丢失或者乱序的问题。在使用 UDP 进行网络通信时,我们需要使用程序来处理这些问题,确保数据的可靠性。 总结来说,Linux网卡 C Socket UDP 是指在 Linux 操作系统中,通过 C 编程语言编写的程序,使用 Socket 编程接口,利用 UDP 协议进行网络通信。在双网卡环境下,程序可以指定使用哪一块网卡进行数据传输,并且可以实现数据的接收和发送。同时,程序也需要处理 UDP 协议带来的数据丢失或者乱序等问题,确保数据的可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值