linux 指定网卡发送UDP数据

2023-05-16

问题:

在这里插入图片描述

解决方法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;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux 指定网卡发送UDP数据 的相关文章

  • UDP 广播发送失败:在 Linux 2.6.30 上“网络无法访问”

    我用udp广播写了一个程序 代码段如下 struct sockaddr in broadcast addr socklen t sock len sizeof broadcast addr bzero broadcast addr sock
  • sqlite 插入需要很长时间

    我正在将不到 200 000 行插入到 sqlite 数据库表中 我只是在终端中通过 sqlite3 使用一个非常简单的 sql 文件 我打赌它已经运行了至少 30 分钟 这是正常现象还是我应该关闭该过程并尝试不同的方法 sqlite中的插
  • 我应该害怕使用 UDP 进行客户端/服务器广播通话吗?

    我在过去的两天里阅读了每一篇StackOverflow问题和答案 以及googling当然 关于印地TCP and UDP协议 以便决定在我的用户应用程序和 Windows 服务之间的通信方法中应该使用哪一种 从我目前所看到的来看 UDP是
  • Linux、ARM:为什么仅当启动时存在 I2C GPIO 扩展器时才创建 gpiochip

    在 imx6sx 硬件平台 NXP 嵌入式 ARM 上使用 Linux 3 14 52 问题是设备树中指定的 PCF8575 I2C GPIO 扩展器不会实例化为 sys class gpio 结构中的设备 除非它们在内核启动期间存在 这些
  • 在 shell 脚本中查找和替换

    是否可以使用 shell 在文件中搜索然后替换值 当我安装服务时 我希望能够在配置文件中搜索变量 然后在该值中替换 插入我自己的设置 当然 您可以使用 sed 或 awk 来完成此操作 sed 示例 sed i s Andrew James
  • 如何通过代理将套接字连接到http服务器?

    最近 我使用 C 语言编写了一个程序 用于连接到本地运行的 HTTP 服务器 从而向该服务器发出请求 这对我来说效果很好 之后 我尝试使用相同的代码连接到网络上的另一台服务器 例如 www google com 但我无法连接并从网络中的代理
  • 如何从 Linux 命令行获取视频文件的分辨率(宽度和高度)?

    我一直在挖掘 mplayer mencoder 和 ffmpeg 文档 但我似乎无法想出anything 我对输出格式不是特别挑剔 因为我可以使用正则表达式将其拉出来 我只是似乎无法首先获取数据 Use ffprobe https ffmp
  • Scrapy FakeUserAgentError:获取浏览器时发生错误

    我使用 Scrapy FakeUserAgent 并在我的 Linux 服务器上不断收到此错误 Traceback most recent call last File usr local lib64 python2 7 site pack
  • 如何查明 Ubuntu 上安装了哪个版本的 GTK+?

    我需要确定 Ubuntu 上安装了哪个版本的 GTK 男人似乎不帮忙 这个建议 https stackoverflow com a 126145 会告诉您安装了哪个 2 0 的次要版本 不同的主要版本将具有不同的包名称 因为它们可以在系统上
  • 将数组传递给函数名称冲突

    Specs GNU bash 版本 3 1 17 无法升级 Premise 我一直在摆弄数组 我想知道是否有任何方法可以让函数的本地变量与所述函数外部的数组同名 Example 在下面的示例中 我将尝试显示该问题 Working bin b
  • 操作系统什么时候清除进程的内存

    进程在某些操作系统上成功或异常终止 操作系统何时决定擦除分配给该进程的内存 数据 代码等 在退出时或当它想为新进程分配内存时 这个清除内存分配过程在所有操作系统 winXP Win7 linux Mac 上都相同吗 据我了解 页表具有该进程
  • numpy 未定义符号:PyFPE_jbuf

    我正在尝试使用一百万首歌曲数据集 为此我必须安装 python 表 numpy cython hdf5 numexpr 等 昨天我设法安装了我需要的所有内容 在使用 hdf5 遇到一些麻烦之后 我下载了预编译的二进制包并将它们保存在我的 b
  • 如何用X11复制到剪贴板?

    使用 OS X 上的框架 我可以使用以下命令将 PNG 复制到粘贴板 在 C 中 显然我可以将 NSPasteboard 与 Cocoa 一起使用 include
  • 怎样才能使 Windows 成为一个开箱即用的 POSIX 兼容操作系统?

    这个问题的动机是我的一个牵强的梦想 即 nix 平台上可用的许多优秀软件可以轻松移植到 Windows 微软最近对开源和开放性采取了不同的方法 所以我真的很想知道如果微软有这样的倾向 这样的事情会有多可行 我很好奇的一些更具体的事情是 是否
  • 伊迪德信息

    重新定义问题 有什么方法可以获取所连接显示器的序列号吗 我想收集显示器的Eid信息 当我使用 logverbose 选项运行 X 时 我可以从 xorg 0 log 文件中获取它 但问题是 如果我切换显示器 拔出当前显示器 然后插入另一个显
  • 标准头文件中的 C 编译器错误 - 未定义的 C++ 定义

    我正在尝试编译 C 程序 但收到许多错误 这些错误是在标准 C 头文件 inttypes h stdio h stat h 等 中遇到的 错误的来源是以下未定义的常量 BEGIN DECLS END DECLS BEGIN NAMESPAC
  • 无法执行'x86_64-conda_cos6-linux-gnu-gcc':没有这样的文件或目录(pysam安装)

    我正在尝试安装 pysam 执行后 python path to pysam master setup py build 这个错误的产生是 unable to execute x86 64 conda cos6 linux gnu gcc
  • 为 Qt 应用程序创建 Linux 安装

    我刚刚用 Qt Creator 制作了一个很棒的程序 我对自己很满意 如何将其从台式机移至笔记本电脑 那么 最好的方法是安装程序 对吗 对于 Ubuntu 这是一个 Debian 软件包 对吗 我怎么做 有人这样做过吗 他们可以分享 QT
  • 如何让“grep”从文件中读取模式?

    假设有一个很大的文本文件 我只想打印与某些模式不匹配的行 显然 我可以使用egrep v patter1 pattern2 pattern3 现在 如果所有这些模式都在一个文本文件中怎么办 最好的制作方法是什么egrep从文件中读取模式 g
  • 无需 cron 在后台发送邮件

    我想知道是否有一种方法可以运行 PHP 循环 以便在后台向订阅者发送几百封电子邮件 我的目标是格式化新闻通讯 单击发送 然后关闭浏览器或更改页面 当然 发送电子邮件的实际过程将在后台运行 不会因浏览器关闭而中断 我知道这可以通过 cron

随机推荐

  • 修改运行中的docker容器的端口映射(简单、高效)

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 文章目录 前言查看已经存在容器id关闭docker服务 xff0c 修改配置文件结束 xff0c 启动容器 前言 在docker run创
  • 人脸识别系列(十六):AMSoftmax

    论文链接 xff1a Additive Margin Softmax for Face Verification 2019 1 24 xff1a 注 xff1a 腾讯AI Lab的 CosFace Large Margin Cosine L
  • C语言:strtok()的用法。

    char strtok char str const char sep 1 sep参数是个字符串 xff0c 定义了用作分隔符的字符集合 xff1b 2 第一个参数指定一个字符串 xff0c 它包含了0个或者多个由sep字符串中一个或者多个
  • 日积月累

    目录 PYTHONUBUNTUTRACKINGNEURAL NETWORKMACHINE LEARNING PYTHON 有路径的地方要注意区别 和 ubuntu使用 分割文件夹 xff1b 对于别人的代码 xff0c 首先仔细阅读read
  • 19.RFID复习

    题型 填空题30分 xff08 一空一分 xff09 简答题30分 xff08 三道题 xff09 综合题40分 xff08 两道题 xff09 重点章节第一章 xff0c 第二章 xff08 见整理的练习题 xff09 xff0c 第四章
  • [Linux驱动]-----NAND FLASH

    一 NAND原理及硬件操作 C xff1a fopen xff0c fread xff0c fwrite APP open read write 1 txt 文件读写 文件系统 xff1a vfat ext2 ext3 yaffs xff0
  • sklearn中train_test_split里,参数stratify含义解析

    直接上代码 xff1a span class token keyword from span sklearn span class token punctuation span model selection span class toke
  • CentOS 7/8 预装的新型防火墙firewalld配置详解,

    前言 本文将会详细介绍CentOS 7 firewalld的来由 功能 常用命令 常用的案例及使用方法 希望能帮助读者全面了解firewalld xff0c 并能正确配置和使用它 centos7 firewalld firewalld与ip
  • webpack5常用配置

    该常用配置使用的npm包 xff1a span class token string property property 34 dependencies 34 span span class token operator span span
  • 【Linux】CentOS & Makefile

    目录 1 Ubuntu入门1 1 文件1 2 网络相关1 2 1 Linux网络相关概念和修改IP地址的方法IP信息临时修改ip地址NetworkManager修改主机名 1 2 2 关闭防火墙并设置开机开不启动firewalld临时和永久
  • HTTP请求报文(请求行、请求头、请求体)

    HTTP协议 1 简介 HTTP协议 xff08 Hyper Text Transfer Protocol xff0c 超文本传输协议 xff09 是用于从万维网 xff08 WWW World Wide Web xff09 服务器传输超文
  • Linux 下使用vscode调试C/C++程序记录

    Linux 下使用vscode调试C C 43 43 程序记录 本文主要介绍了 xff0c 在linux下使用vscode调试工程时的 xff0c 一些经验记录 基础配置 常用的配置方法网上的材料很多 xff0c 可以直接参照 链接 lin
  • 形状特征的提取

    物体和区域的形状是图像表达和图像检索中的另一重要的特征 但不同于颜色或纹理等底层特征 xff0c 形状特征的表达必须以对图像中物体或区域的划分为基础 由于当前的技术无法做到准确而鲁棒的自动图像分割 xff0c 图像检索中的形状特征只能用于某
  • c语言中全局变量在不同文件中的引用

    c语言中全局变量在不同文件中的引用 c语言中在某个文件中定义的全局变量可以在不同的文件中引用 xff0c 对数组和指针这两种全局变量在使用时必须要注意 xff0c 外部引用格式不正确时会出现编译或运行错误 下面通过不同的例子来说明数组和指针
  • Linux上MathGL的安装和使用

    Linux上MathGL的安装和使用 本人参考http mathgl sourceforge net doc en index html SEC Contents MathGL官网教程第一种方案在Ubuntu上对MathGL进行了编译安装
  • kvaser在linux下的二次开发

    kvaser在linux下的二次开发 linux下kvaser的驱动和SDK包安装 下载连接 http www kvaser cn software 7330130980754 V5 13 0 linuxcan tar gz 安装过程 解压
  • linux 交叉编译 C和C++

    linux 交叉编译 C和C 43 43 安装编译器Cmake中选择编译器编译运行注意 在ubuntu 中搭建arm交叉编译环境的过程记录 安装编译器 安装aarch64 linux gnu编译器 xff0c 可以参考 参考链接 安装完成后
  • vscode中git使用

    vscode中git使用 目的 xff1a 减少git的命令输出 xff0c 尽量使用vscode可视化完成日常代码提交管理 将该过程简单记录 创建文件 xff0c 并使用Vscode打开 vscode中打开终端 xff0c 并进行git的
  • 程序调试正常 运行错误排查

    调试正常 运行错误排查 检查是否生成core文件 输入ulimit a 第一行core file size 如果0 xff0c core文件不会生成 xff0c 需要设置 ulimit c unlimited 生成core文件 xff0c
  • linux 指定网卡发送UDP数据

    问题 xff1a 解决方法1 xff08 有权限要求 xff09 可以使用SO BINDTODEVICE绕过路由表查找 xff0c 解决该问题 注意 xff1a 该方法需要程序有cap net raw和cap net bind servic