[Linux网络编程]ARP简单实例

一个简单的ARP实例。

先声明,本文是基于于一篇来自邹鑫的专栏的文章,地址如下:

http://blog.csdn.net/zouxinfox/archive/2008/10/25/3141323.aspx

山人根据自己的实际情况稍微改动一下代码,不过山人纯粹是用来学习的,没有干坏事。

 

下面的图片依然来自《TCP/IP详解》一书。

 

在前篇文章中,我们知道,一个ARP包(分组)除了以太网头部和ARP请求/应答字段外,还有18个字节的填充字段(山人根据PAD一词猜的),这样一个ARP包就有14+28+18=60字节(不相信的话,大家可以慢慢数一数每个字段的字节数)。下面我们将会看到,代码中就是分配了60字节的空间。

 

ARP头部结构体是这样的:

struct arphdr
  {
    unsigned short int ar_hrd;  /* Format of hardware address.  */
    unsigned short int ar_pro;  /* Format of protocol address.  */
    unsigned char ar_hln;  /* Length of hardware address.  */
    unsigned char ar_pln;  /* Length of protocol address.  */
    unsigned short int ar_op;  /* ARP opcode (command).  */
  };

(来自<net/if_arp.h>)

它其实就是“28字节ARP请求/应答”中前面的8个字节。

第一个成员表示硬件地址的格式,就是图中的“硬件类型”,它有如下这些:

#define ARPHRD_NETROM 0  /* From KA9Q: NET/ROM pseudo. */
#define ARPHRD_ETHER  1  /* Ethernet 10/100Mbps.  */
#define ARPHRD_EETHER 2  /* Experimental Ethernet.  */
#define ARPHRD_AX25 3  /* AX.25 Level 2.  */
#define ARPHRD_PRONET 4  /* PROnet token ring.  */
#define ARPHRD_CHAOS 5  /* Chaosnet.  */
#define ARPHRD_IEEE802 6  /* IEEE 802.2 Ethernet/TR/TB.  */
#define ARPHRD_ARCNET 7  /* ARCnet.  */
#define ARPHRD_APPLETLK 8  /* APPLEtalk.  */
#define ARPHRD_DLCI 15  /* Frame Relay DLCI.  */
#define ARPHRD_ATM 19  /* ATM.  */
#define ARPHRD_METRICOM 23  /* Metricom STRIP (new IANA id).  */
#define ARPHRD_IEEE1394 24  /* IEEE 1394 IPv4 - RFC 2734.  */
#define ARPHRD_EUI64  27  /* EUI-64.  */
#define ARPHRD_INFINIBAND 32  /* InfiniBand.  */

(同上)

其实第2个是以太网,在代码中使用它。

 

第二个成员表示协议类型,这里是ARP协议(一句必要的废话)。

最后一个成员表示操作类型,在<net/if_arp.h>是这样的:

/* ARP protocol opcodes. */
#define ARPOP_REQUEST 1  /* ARP request.  */
#define ARPOP_REPLY 2  /* ARP reply.  */
#define ARPOP_RREQUEST 3  /* RARP request.  */
#define ARPOP_RREPLY 4  /* RARP reply.  */
#define ARPOP_InREQUEST 8  /* InARP request.  */
#define ARPOP_InREPLY 9  /* InARP reply.  */
#define ARPOP_NAK 10  /* (ATM)ARP NAK.  */

其中就是ARP请求,ARP应答。

 

但是,光有这个结构体还是不行的,我们需要的是ARP包。

这个结构体是这样的:

struct ether_arp {
 struct arphdr ea_hdr;  /* fixed-size header */
 u_int8_t arp_sha[ETH_ALEN]; /* sender hardware address */
 u_int8_t arp_spa[4];  /* sender protocol address */
 u_int8_t arp_tha[ETH_ALEN]; /* target hardware address */
 u_int8_t arp_tpa[4];  /* target protocol address */
};
#define arp_hrd ea_hdr.ar_hrd
#define arp_pro ea_hdr.ar_pro
#define arp_hln ea_hdr.ar_hln
#define arp_pln ea_hdr.ar_pln
#define arp_op ea_hdr.ar_op
(来自<netinet/if_ether.h>)

它包含了ARP头部那个结构体,所以在程序中直接使用这个就可以了。这个结构体就是根据上面那个图来定义,没啥好说的。不过,这里要注意一下人家代码的技巧。它在下面定义一些宏,一开始我很不理解,后面才发现这种用法很好,值得学习。比如我们要赋值给ARP头部的op,可以这样

arp->ea_hdr->ar_op

也可以这样

arp->arp_op

(假设arp是ether_arp结构体的指针)。代码中就是使用后面那种方式的,比较简便。

 

废话讲完,该上菜了。下面给出代码

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <net/ethernet.h> #include <netinet/if_ether.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #define SRC_IP "192.168.184.100" #define TARGET_IP "192.168.184.1" short SRC_MAC[]={0x6c,0x61,0x74,0x65,0x6c,0x65}; /* latele */ short TARGET_MAC[]={0x00,0x50,0x56,0xc0,0x00,0x08}; void send_arp_reply(void) { struct ether_header *eth_hdr; struct ether_arp *arp; char datagram[60]; eth_hdr=(struct ether_header *)datagram; memset(datagram,0,sizeof(datagram)); //set the ethernet header eth_hdr->ether_dhost[0]=TARGET_MAC[0]; eth_hdr->ether_dhost[1]=TARGET_MAC[1]; eth_hdr->ether_dhost[2]=TARGET_MAC[2]; eth_hdr->ether_dhost[3]=TARGET_MAC[3]; eth_hdr->ether_dhost[4]=TARGET_MAC[4]; eth_hdr->ether_dhost[5]=TARGET_MAC[5]; eth_hdr->ether_shost[0]=SRC_MAC[0]; eth_hdr->ether_shost[1]=SRC_MAC[1]; eth_hdr->ether_shost[2]=SRC_MAC[2]; eth_hdr->ether_shost[3]=SRC_MAC[3]; eth_hdr->ether_shost[4]=SRC_MAC[4]; eth_hdr->ether_shost[5]=SRC_MAC[5]; eth_hdr->ether_type=htons(ETHERTYPE_ARP); // 0x0806 //set the arp header arp=(struct ether_arp*)(datagram+sizeof(struct ether_header)); arp->arp_hrd=htons(ARPHRD_ETHER); arp->arp_pro=htons(ETHERTYPE_IP); arp->arp_hln=6; arp->arp_pln=4; arp->arp_op=htons(ARPOP_REPLY); //arp body //sender MAC and IP memcpy((void*)arp->arp_sha,(void*)eth_hdr->ether_shost,6); struct in_addr inadd_sender; inet_aton(SRC_IP,&inadd_sender); memcpy((void*)arp->arp_spa,(void*)&inadd_sender,4); //target MAC and IP memcpy((void*)arp->arp_tha,(void*)eth_hdr->ether_dhost,6); struct in_addr inadd_target; inet_aton(TARGET_IP,&inadd_target); memcpy((void *)arp->arp_tpa,(void*)&inadd_target,4); //establish socket int fd=socket(AF_INET,SOCK_PACKET,htons(ETH_P_ARP)); if(fd<0) { perror("socket"); exit(-1); } struct sockaddr sa; strcpy(sa.sa_data,"eth7"); sendto(fd,datagram,sizeof(datagram),0,&sa,sizeof(sa)); close(fd); } int main(void) { while(1) { printf("send arp reply.../n"); send_arp_reply(); sleep(30); } return 0; }

再次声明,代码非原创,链接地址见文章开头处。

在虚拟机Linux中测试,在物理机Windows系统中查看结果,网络连接为NAT8。

Linux系统,eth7,IP地址192.168.184.1.100,代码中的物理地址是瞎写的。

运行程序后,在Windows系统的的cmd命令行下输入arp -a,可以看到Linux系统中的MAC地址已经变为代码中的地址了。

限于环境及个人能力,不敢作太多测试。权当学习例子而已。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(编程,linux,网络,struct,header)