相关API
#include <sys/types.h>
#include <sys/socket.h>
/**
* [sendto 发送]
* @param sockfd [套接字文件描述符]
* @param buf [原始套接字需要发送数据帧]
* @param len [长度]
* @param flags [数据发送的方式,在原始套接字中一般使用0表示阻塞模式发送]
* @param dest_addr [需要发送的网络接口(对应的网卡设备)]
* @param addrlen [上个参数大小]
* @return [成功返回发送数据的字节数,失败返回-1且修改errno的值]
*/
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
#include <netpaket/packet.h> /* 头文件的路径:/usr/include */
struct sockaddr_ll
{
unsigned short int sll_family; /* 一般为AP_PACKET */
unsigned short int sll_protocol; /* 上次协议 */
int sll_ifindex; /* 需要发送数据的接口类型 */
unsigned short int sll_hatype; /* 报头类型 */
unsigned char sll_pkttype; /* 包类型 */
unsigned char sll_halen; /* 地址长度 */
unsigned char sll_addr[8]; /* MAC地址 */
};
//PS:注意在整个数据发送的过程,只需要初始化接口类型成员(.sll_ifindex);
#include <sys/ioctl.h>
/*
功能:设置网络接口类型
参数:
参数1:fd表示需要设置设备文件的文件描述符;
参数2:request请求:由于是用来设置网络设备接口类型,所以使用SIOCGIFINDEX,
SIOCGIFINDEX获取网络接口
SIOCSIFADDR设置接口地址
SIOCGIGADDR获取接口地址
SIOCSIFFLAGS设置接口标志
SIOCGIFFLAGS获取接口标志
参数3的数据类型:struct ifreq
*/
int ioctl(int fd, unsigned long request, ...);
实例
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ether.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netpacket/packet.h>
#include <sys/ioctl.h>
int SendTo(int sockfd, void *msg, int msg_len, char *eth_name)
{
int ret;
struct ifreq ethreq;
struct sockaddr_ll sll;
/* 获取网络接口类型 */
memset(ðreq, 0, sizeof(ethreq));
strncpy(ethreq.ifr_name, eth_name, IFNAMSIZ);
ret = ioctl(sockfd, SIOCGIFINDEX, ðreq);
if (ret == -1)
{
perror("ioctl");
return -1;
}
/* 设置网络接口类型 */
memset(&sll, 0, sizeof(sll));
sll.sll_ifindex = ethreq.ifr_ifindex;
/* 发送数据 */
ret = sendto(sockfd, msg, msg_len, 0, (const struct sockaddr *)&sll, sizeof(sll));
if (ret == -1)
{
perror("sendto");
return -1;
}
}
int main()
{
int ret;
int sockfd;
int msg_len;
unsigned char buf[1024];
/* 创建原始套接字文件:基于链路层,可用于任意协议数据的收发 */
sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sockfd == -1)
{
perror("socket");
return -1;
}
printf("sockfd = %d\n", sockfd);
/* 组ARP请求报文数据 */
unsigned char msg[1024] =
{
/* --------------组以太网数据报文-----14---------- */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,/* 目的mac:在ARP的请求时候以广播形式发送,所以目的mac为广播mac */
0x00, 0x0c, 0x29, 0x84, 0x0e, 0x96,/* 源mac:发送主机的mac地址 */
0x08, 0x06,/* 数据帧类型:ARP请求的类型编号为0x0806 */
/* --------------组ARP请求报文-----28---------- */
0x00, 0x01,/* 硬件类型:1 = 以太网类型 */
0x08, 0x00,/* 协议类型:0x0800 IP协议 */
0x06,/* 硬件地址(mac地址长度):6 */
0x04,/* 协议地址(IP地址长度):4 */
0x00, 0x01,/* ARP报文类型:1 = ARP请求*/
0x00, 0x0c, 0x29, 0x84, 0x0e, 0x96,/* 源mac:发送主机的mac地址 */
10, 7, 181, 108,/* 源IP:发送主机的IP地址 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,/* 目的mac:接收主机的mac地址 */
10, 7, 181, 28/*目的IP:接收主机的IP地址*/
};
msg_len = 14 + 28;
/* 发送数据 */
SendTo(sockfd, msg, msg_len, "ens33");
while(1)
{
ret = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
if (ret == -1)
{
perror("recvfrom");
return -1;
}
if (ntohs(*(unsigned short *)(buf + 12)) != 0x0806)
{
continue;
}
if (ntohs(*(unsigned short *)(buf + 20)) != 2)
{
continue;
}
/* ARP 应答 */
char src_ip[16];
inet_ntop(AF_INET, buf + 28, src_ip, sizeof(src_ip));
char src_mac[18];
sprintf(src_mac, "%02x:%02x:%02x:%02x:%02x:%02x", buf[22], buf[23], buf[24], buf[25], buf[26], buf[27]);
if (strcmp(src_ip, "10.7.181.67") == 0)
{
printf("%s -> %s\n", src_ip, src_mac);
break;
}
}
}
网友评论