linux实现ARP协议
// arp协议
// 以太网头部 || ARP请求/应答
// 目的mac地址 | 源mac地址 | 帧类型 || 硬件类型 | 协议类型 | 硬件地址长度 | 协议地址长度 | 操作方式 | 发送方硬件地址 | 发送方IP地址 | 接收方硬件地址 | 接收方IP地址 |
// 6byte | 6byte | 2byte || 2byte | 2byte | 1byte | 1byte | 2byte | 6byte | 4byte | 6byte | 4byte |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/types.h>
#include <asm/types.h>
#include <features.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
// arp协议
// 以太网头部 || ARP请求/应答
// 目的mac地址 | 源mac地址 | 帧类型 || 硬件类型 | 协议类型 | 硬件地址长度 | 协议地址长度 | 操作方式 | 发送方硬件地址 | 发送方IP地址 | 接收方硬件地址 | 接收方IP地址 |
// 6byte | 6byte | 2byte || 2byte | 2byte | 1byte | 1byte | 2byte | 6byte | 4byte | 6byte | 4byte |
typedef struct
{
/* 以太网头部 */
unsigned char nDstMac[6]; /* 目的mac地址 */
unsigned char nSrcMac[6]; /* 源mac地址 */
unsigned short nProto; /* 帧类型 */
/* ARP请求/应答 */
unsigned short hardwareType; /* 硬件类型 */
unsigned short protoType; /* 协议类型 */
unsigned char hardwareLen; /* 硬件地址长度 */
unsigned char protoLen; /* 协议地址长度 */
unsigned short optionCode; /* 操作方式 */
/*mac地址 IP地址*/
unsigned char srcMac[6]; /* 发送方硬件地址 */
unsigned char szSrcIp[4]; /* 发送方IP地址 */
unsigned char dstMac[6]; /* 接收方硬件地址 */
unsigned char szDstIp[4]; /* 接收方IP地址 */
} ARP_PARAM;
void makeArpMsg(ARP_PARAM *pArpMsg, unsigned char *srcMac, unsigned char *srcIp, unsigned char *dstMac, unsigned char *dstIp, int op)
{
if (NULL == pArpMsg || NULL == srcMac || NULL == srcIp || NULL == dstMac || NULL == dstIp)
{
printf("param is null!\n");
return;
}
/*Ether*/
memcpy(pArpMsg->nSrcMac, srcMac, sizeof(pArpMsg->nSrcMac));
memset(pArpMsg->nDstMac, 0xff, sizeof(pArpMsg->nDstMac)); /*广播地址ff:ff:ff:ff:ff:ff*/
pArpMsg->nProto = htons(ETH_P_ARP);
/*ARP*/
pArpMsg->hardwareType = htons(ARPHRD_ETHER);
pArpMsg->protoType = htons(ETH_P_IP);
pArpMsg->hardwareLen = 6; /*mac长度为6字节*/
pArpMsg->protoLen = 4; /*协议长度为4字节,代表ipv4*/
pArpMsg->optionCode = htons(op == 1 ? ARPOP_REQUEST : ARPOP_REPLY);
/*ARP报文请求地址填写*/
memcpy(pArpMsg->srcMac, srcMac, sizeof(pArpMsg->srcMac));
memcpy(pArpMsg->dstMac, dstMac, sizeof(pArpMsg->dstMac));
memcpy(pArpMsg->szSrcIp, srcIp, sizeof(pArpMsg->szSrcIp));
memcpy(pArpMsg->szDstIp, dstIp, sizeof(pArpMsg->szDstIp));
}
int main()
{
/*arp*/
ARP_PARAM stuMsg;
unsigned char srcMac[6] = {0x00, 0x0c, 0x29, 0xf7, 0x7b, 0x0d}; // 源mac地址, ether 00:0c:29:f7:7b:0d
unsigned char dstMac[6] = {0};
unsigned int uSrcIp = 0;
unsigned int uDstIp = 0;
memset(&stuMsg, 0, sizeof(stuMsg));
/*填写源ip 和目的ip*/
uSrcIp = inet_addr("192.168.68.113");
uDstIp = inet_addr("192.168.68.48");
/*op为1代表arp报文类型为请求包*/
makeArpMsg(&stuMsg, srcMac, (unsigned char *)&uSrcIp, dstMac, (unsigned char *)&uDstIp, 1);
/*创建原始套接字*/
int arpFd = socket(AF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
/*设置socket属性为广播*/
int opt = 1;
setsockopt(arpFd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));
/*设置发送超时 避免阻塞*/
struct timeval stuTimeout;
stuTimeout.tv_sec = 5;
stuTimeout.tv_usec = 0;
setsockopt(arpFd, SOL_SOCKET, SO_SNDTIMEO, (char *)&stuTimeout, sizeof(stuTimeout));
struct sockaddr sock_addr; /* for interface name */
memset(&sock_addr, 0, sizeof(sock_addr));
/*指定网卡发送*/
memcpy(&sock_addr.sa_data, "ens33", strlen("ens33"));
char recvarp[42] = {0};
while (1)
{
int iRet = sendto(arpFd, &stuMsg, sizeof(stuMsg), 0, &sock_addr, sizeof(sock_addr));
if (iRet < 0)
{
printf("send time out\n");
}
else
{
printf("send success\n");
}
int len = sizeof(sock_addr);
if (recvfrom(arpFd, recvarp, 42, 0, &sock_addr, &len) == 42)
{
memcpy(dstMac, recvarp + 22, 6);
// 以太网的目的MAC
struct ether_header *h1 = (struct ether_header *)recvarp;
// ARP包
struct ether_arp *arp = (struct ether_arp *)(recvarp + 14); // 以太网的源MAC6个字节 ,目的MAC 6个字节,类型为2字节
int i = 0;
// printf("目的MAC:");
// for (i = 0; i < ETH_ALEN; i++)
// {
// printf("%02x-", h1->ether_dhost[i]);
// }
// printf("\n");
// // 源MAC地址
// printf("源MAC:");
// for (i = 0; i < ETH_ALEN; i++)
// {
// printf("%02x-", h1->ether_shost[i]);
// }
// printf("\n");
if ((ntohs)(arp->arp_op) == 2) // 判断是否是ARP响应包,如果是操作方式码为2
{
// 硬件类型
printf("硬件类型:%0x\n", (ntohs)(arp->arp_hrd)); // 1代表硬件接口为以太网接口
// 协议类型
printf("协议类型:%0x\n", (ntohs)(arp->arp_pro)); // 0x800代表高层协议为IP
// 硬件地址长度
printf("硬件地址长度:%0x\n", arp->arp_hln);
// 协议地址长度
printf("协议地址长度:%0x\n", arp->arp_pln);
// 发送方的MAC地址
printf("发送方的MAC:");
for (i = 0; i < ETH_ALEN; i++)
{
printf("%02x-", arp->arp_sha[i]);
}
printf("\n");
printf("发送方的IP:");
char ip[16];
inet_ntop(AF_INET, arp->arp_spa, &ip, 16); // arp_spa是一个unsigned char数组
printf("%s\n", ip);
break;
}
sleep(1);
}
}
makeArpMsg(&stuMsg, srcMac, (unsigned char *)&uSrcIp, dstMac, (unsigned char *)&uDstIp, 1);
while (1)
{
if (sendto(arpFd, &stuMsg, sizeof(stuMsg), 0, &sock_addr, sizeof(sock_addr)) == 42)
{
printf("发送ARP\n");
}
else
{
perror("发送");
return EXIT_FAILURE;
}
sleep(1);
}
close(arpFd);
return 0;
}