问题:
解决方法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"
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;
}
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 (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;
}
for (i = 0; i < ifc.ifc_len;) {
struct ifreq *pifr = (struct ifreq *)(buf + i);
if (ioctl(sock, SIOCGIFADDR, pifr) < 0) {
perror("ioctl error");
return -1;
}
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 = 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);
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;
}
if (sendto(fd1, (const char *)chrUDP1, sizeof(chrUDP1), 0,
(struct sockaddr *)&mcast_addr1, sizeof(mcast_addr1)) < 0) {
perror("sendto1");
return -1;
}
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_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);
return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)