您的位置:首页 > 理论基础 > 计算机网络

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

2010-12-22 15:54 477 查看
一个简单的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地址已经变为代码中的地址了。

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息