Linux-ARP请求C程序
2017.05.23学习ARP协议好⼏天了。今天终于把在Linux上的ARP请求程序完成了。中间经历了好多坎坷。弄不出来搞的我⼗分紧张。终于在16:30分解决掉遇到的所有问题了。
下⾯简述下我的经历和存留的疑问。直接上代码,分析代码的问题。
**2017.06.04这下⼜⽆法淡定了。同样的程序我就加了⼀⾏
printf("mac=%02x:%02x:%02x:%02x:%02x:%02x\n", local_mac[0],local_mac[1], local_mac[2], local_mac[3],local_mac[4],local_mac[5]);
然后sendto⼜出现了invalid arguements的报错信息。再次删除这句printf语句,程序⼜可以正常执⾏了。
还请⼤神帮忙看看。⼩弟感激不尽。。**。
**2017.06.21补充。sendto()报错的问题终于解决了。彻底解决了。现在加上我的笔记。
arp发送和接受的socket有两种⽅法。不同的⽅法对应着不同的流程。
/
* 先填充arp包头, padding使⽤bzero或者memset清空⼀下 */
1、sockfd = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ARP) );
根据man 2 socket中的内容,这种⽅法已经不推荐使⽤了,但是继续使⽤也是可以的。
流程如下:
struct sockaddr sa;
bzero(&sa, sizeof(sa));
sa.sa_family = AF_INET;        //指定地址族,要和socket()函数中使⽤的相同。
strcpy(sa.sa_data, “eth0”);    //指定发送的接⼝。
sendto(sockfd, &arp_pck, sizeof(arp_pck), 0,  &sa, sizeof(sa));
2、或者
sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP) );
struct sockaddr_ll sa_ll;
bzero(&sa_ll, sizeof(sa_ll));  //之前忘记使⽤bzero清空,也会导致sendto报“invalid argument”错误。
sa_ll.sll_family = PF_PACKET,
sa_ll.sll_ifindex = if_nametoindex(“eth0”);
sendto(sockfd, &arp_pck, sizeof(arp_pck), 0, (struct sockaddr*)&sa_ll, sizeof(sa_ll) );
总结:sendto()报“invalid argument”带来的错误引出的思考和注意点。
1、  socket()函数的使⽤要注意,尤其是原始套接字。
2、对于不同的socket()⽅法,使⽤的结构体不同。struct sockaddr和struct sockadd_ll。其数据成员的填充⽅式不同。
3、上⾯提到的结构体,定义之后⼀定要进⾏清内存操作。否则也会导致sendto传⼊的参数错误。
**2017.06.21
链接:[pan.baidu/s/1pLFMjmN](pan.baidu/s/1pLFMjmN) 密码:yplq
MAC.h获取指定⽹卡的IP地址和MAC地址
#ifndef _MAC_H
#define _MAC_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <linux/if.h>
#include <sys/ioctl.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
extern int get_eth_MAC(char *eth_name, unsigned char *MAC);
extern int get_eth_IP(char *eth_name, unsigned char *IP);
extern int get_eth_broadaddr(char *eth_name, unsigned char *broadaddr);
#endif
MAC.c暂时能⽤,还有简写。(可以加⼊⼀个选项参数,选择获取ip或者MAC)#include "MAC.h"
int get_eth_MAC(char *eth_name, unsigned char *MAC)
{
struct ifreq ifr;
int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if(sock_fd < 0)
{
perror("socket");
return sock_fd;
}
strncpy(ifr.ifr_name,(char *)eth_name, sizeof(ifr.ifr_name) );
int ret_ioctl = ioctl(sock_fd, SIOCGIFHWADDR, &ifr);
if(ret_ioctl < 0)
{
perror("ioctl");
return ret_ioctl;
}
int i = 0;
for(i = 0 ; i < 14; i++)
{
printf("%02x\t",(unsigned char)ifr.ifr_hwaddr.sa_data[i]);
}
printf("\n");
memcpy(MAC, ifr.ifr_hwaddr.sa_data, 6);
close(sock_fd);
return0;
}
int get_eth_IP(char *eth_name, unsigned char *IP)
{
perror("socket");
return sock_fd;
c语言struct头文件}
strncpy(ifr.ifr_name,(char *)eth_name, sizeof(ifr.ifr_name) );
int ret_ioctl = ioctl(sock_fd, SIOCGIFADDR, &ifr);
if(ret_ioctl < 0)
{
perror("ioctl");
return ret_ioctl;
}
int i = 0;
for(i = 0; i < 14; i++)
{
printf("%d\t", (unsigned char)ifr.ifr_addr.sa_data[i]);
}
printf("\n");
memcpy(IP, ifr.ifr_addr.sa_data+2, 4);
close(sock_fd);
return0;
}
int get_eth_broadaddr(char *eth_name, unsigned char *broadaddr)
{
struct ifreq ifr;
int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if(sock_fd < 0)
{
perror("socket");
return sock_fd;
}
strncpy(ifr.ifr_name,(char *)eth_name, sizeof(ifr.ifr_name) );
int ret_ioctl = ioctl(sock_fd, SIOCGIFBRDADDR, &ifr);
int i = 0;
for(i = 0; i < 14; i++)
{
printf("%d\t", (unsigned char)ifr.ifr_broadaddr.sa_data[i]);
}
printf("\n");
memcpy(broadaddr, ifr.ifr_broadaddr.sa_data+2, 4);
close(sock_fd);
return0;
}
main.c实现ARP请求的代码
#include "MAC.h"
#include <errno.h>
#pragma pack(1)
typedef struct
{
//DLC HEADER
unsigned char dlc_dst_mac[6];
unsigned char dlc_src_mac[6];
unsigned short dlc_frame;
unsigned char  arp_hwlen;
unsigned char  arp_prolen;
unsigned short arp_op;
unsigned char arp_src_mac[6];
unsigned char arp_src_ip[4];
unsigned char arp_dst_mac[6];
unsigned char arp_dst_ip[4];
unsigne char arp_padding[18];
}arp_packet;
int main()
{
printf("sizeof(arp_packet)=%d\n", sizeof(arp_packet));
//fill ARP packet
arp_packet arp_pck;
unsigned char brd_mac[6] = {0xff,0xff,0xff,0xff,0xff,0xff};  //⼴播地址
unsigned char dst_ip[4] = {192,168,1,110};                  //⽬标ip
unsigned char local_mac[6] = {0};
unsigned char local_ip[4] = {0};
get_eth_MAC("eth0", local_mac);
get_eth_IP("eth0", local_ip);
//DLC HEADER填充
memcpy(arp_pck.dlc_src_mac, local_mac, 6);
memcpy(arp_pck.dlc_dst_mac, brd_mac, 6);
arp_pck.dlc_frame = htons(ETH_P_ARP);
//arp 包填充
arp_pck.arp_hwtype = htons(0x0001);
arp_pck.arp_protype = htons(ETH_P_IP);
arp_pck.arp_hwlen  = 6;
arp_pck.arp_prolen = 4;
arp_pck.arp_opr = htons(0x0001);
memcpy(arp_pck.arp_src_mac, local_mac, 6);
memcpy(arp_pck.arp_src_ip, local_ip, 4);
memcpy(arp_pck.arp_dst_mac, brd_mac, 6);
memcpy(arp_pck.arp_dst_ip, dst_ip, 4);
//struct sockaddr_ll
struct sockaddr_ll eth_in;
bzero(ð_in, sizeof(eth_in);
eth_in.sll_family = PF_PACKET;
printf("index=%d\n", eth_in.sll_ifindex = if_nametoindex("eth0") );
//raw_socket
int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP) );
if(sockfd < 0)
{
perror("socket");
return0;
}
printf("sockfd=%d\n", sockfd);
int ret = sendto(sockfd, &arp_pck, sizeof(arp_pck), 0, (struct sockaddr *)ð_in, sizeof(eth_in) ); if(ret < 0)
{
perror("sendto");
printf("errno=%d\n", errno);
}
return0;
接下来就说说为了让程序能够正确运⾏经历的⼼酸历程:
1、关于sockaddr的困惑。
通过ioctl()函数可以获取指定⽹卡的IP和MAC值。参见博客。
观察struct ifreq结构体代码⽚段:
union
{
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
struct sockaddr ifru_hwaddr;
short int ifru_flags;
int ifru_ivalue;
int ifru_mtu;
struct ifmap ifru_map;
char ifru_slave[IFNAMSIZ];  /* Just fits the size */
char ifru_newname[IFNAMSIZ];
__caddr_t ifru_data;
} ifr_ifru;
# define ifr_name  ifr_ifrn.ifrn_name          /* interface name  */
# define ifr_hwaddr ifr_ifru.ifru_hwaddr        /* MAC address      */
# define ifr_addr  ifr_ifru.ifru_addr          /* address      */
# define ifr_dstaddr    ifr_ifru.ifru_dstaddr  /* other end of p-p lnk */
# define ifr_broadaddr  ifr_ifru.ifru_broadaddr /* broadcast address    */
# define ifr_netmask    ifr_ifru.ifru_netmask  /* interface net mask  */
# define ifr_flags  ifr_ifru.ifru_flags        /* flags        */
# define ifr_metric ifr_ifru.ifru_ivalue        /* metric      */
# define ifr_mtu    ifr_ifru.ifru_mtu          /* mtu          */
# define ifr_map    ifr_ifru.ifru_map          /* device map      */
# define ifr_slave  ifr_ifru.ifru_slave        /* slave device    */
# define ifr_data  ifr_ifru.ifru_data          /* for use by interface */
# define ifr_ifindex    ifr_ifru.ifru_ivalue    /* interface index      */
# define ifr_bandwidth  ifr_ifru.ifru_ivalue    /* link bandwidth  */
# define ifr_qlen  ifr_ifru.ifru_ivalue        /* queue length    */
# define ifr_newname    ifr_ifru.ifru_newname  /* New name    */
发现ifr_name,ifr_addr,ifr_hwaddr等都是sockaddr结构体类型的数据:#include <netinet/in.h>
struct sockaddr
{
unsigned short    sa_family;    // 2 bytes address family, AF_xxx
char              sa_data[14];  // 14 bytes of protocol address
};
// IPv4 AF_INET sockets:
struct sockaddr_in
{
short            sin_family;    // 2 AF_INET, AF_INET6
unsigned short  sin_port;      // 2 htons(3490)
struct in_addr  sin_addr;      // 4 bytes see struct in_addr, below
char            sin_zero[8];    // 8 bytes zero this if you want to
};
struct in_addr
{
unsigned long s_addr;            // 4 bytes load with inet_pton()