Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4826362
  • 博文数量: 930
  • 博客积分: 12070
  • 博客等级: 上将
  • 技术积分: 11448
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-15 16:57
文章分类

全部博文(930)

文章存档

2011年(60)

2010年(220)

2009年(371)

2008年(279)

分类: LINUX

2010-01-23 15:59:48

ARP缓存表arp_tbl由协议栈在运行期间自动维护,包括邻居新建,更新,回收等。同时,TCP/IP协议栈的实现中也提供了三个命令,可以用来由用户维护arp_tbl,这三个命令分别是SIOCDARP(删除arp_tbl中的一个邻居),SIOCSARP(设置arp_tbl中的一个邻居), SIOCGARP(获取arp_tbl中的一个邻居)。用户使用系统调用ioctl来传递这三个命令,命令参数是结构体struct arpreq,其定义如下:

    struct arpreq {
      struct sockaddr   arp_pa;     //协议地址
      struct sockaddr   arp_ha;     //硬件地址
      int               arp_flags;      //标志位
      struct sockaddr   arp_netmask;    //网络掩码(只用于代理ARP)
      char              arp_dev[16];    //对应的网络设备接口的名称。
    };

    使用SIOCDARP命令时,只要在struct arpreq结构体填入arp_pa,arp_dev,内核会根据arp_pa中的邻居IP地址从myarp_tbl的哈希表hash_buckets中找出该邻居,并把它的状态更新为NUD_FAILED(失败),则在下一次的arp垃圾回收中,该邻居就会被会收掉。
    使用SIOCSARP命令时,需要设置struct arpreq的arp_pa,arp_ha,arp_flags。如果arp_tbl中已存在该邻居,会更新该邻居,否则新建一个邻居,其状态为 NUD_STALE,如果arp_flags中有ATF_PERM,状态再加上NUD_PERMANENT。
    使用SIOCGARP时,只需要arp_pa,从arp_tbl中找到该邻居并返回,不过,一般要查看arp缓存,并不使用SIOCGARP命令,而是从proc/net/arp文件中取。

    struct arpreq的arp_flags上的标志有如下一些:
    #define ATF_COM         0x02        //已完成的邻居 (成员ha有效,且含有正确的MAC地址)
    #define ATF_PERM        0x04        //永久性的邻居(邻居状态有NUD_PERMANENT)
    #define ATF_PUBL        0x08        //发布该邻居。
    #define ATF_USETRAILERS 0x10        //不是非常清楚。
    #define ATF_NETMASK     0x20        //仅用于代理ARP。
    #define ATF_DONTPUB     0x40        //不处理该邻居。
 
下面是我写的一个简单的测试程序
 
 

#include <sys/types.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <net/if_arp.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

#define DEV_NAME "eth0"
//#define SIOCDARP    0x8953        /* delete ARP table entry    */

//#define SIOCGARP    0x8954        /* get ARP table entry        */

//#define SIOCSARP    0x8955        /* set ARP table entry        */


static int sd;

/* Delete an entry from the ARP cache. */
static int arp_del(char* ip)
{
 struct arpreq arpreq;
 struct sockaddr_in *sin;
 struct in_addr ina;
 int rc;
    
  printf("del arp entry for IP : %s\n", ip);
 
  /*you must add this becasue some system will return "Invlid argument"
    because some argument isn't zero*/

  memset(&arpreq, 0, sizeof(struct arpreq));

  sin = (struct sockaddr_in *) &arpreq.arp_pa;
  memset(sin, 0, sizeof(struct sockaddr_in));
  sin->sin_family = AF_INET;
  ina.s_addr = inet_addr(ip);
  memcpy(&sin->sin_addr, (char *)&ina, sizeof(struct in_addr));
        
  strcpy(arpreq.arp_dev, DEV_NAME);
  rc = ioctl(sd, SIOCDARP, &arpreq);
  if (rc < 0)
    {
      printf("%s\n", "del arp error...");
      return -1;
    }
  else
     printf("%s\n", "del arp success...");
  
  return 0;
}


void get_hw_addr (u_char *buf, char *str)
{
 int i;
 unsigned int p[6];
 
 i=sscanf(str, "%x:%x:%x:%x:%x:%x", &p[0],&p[1],&p[2],
                 &p[3],&p[4],&p[5]);        

 if (i != 6)
  {
   printf("%s\n","error parsing MAC");
   exit(1);
  }        
                
  for(i = 0; i<6; i++)
     buf[i] = p[i];
}

/* Set an entry in the ARP cache. */
static int arp_set(char* ip, char* mac)
{
 struct arpreq arpreq;
 struct sockaddr_in *sin;
 struct in_addr ina;
 int flags;
 int rc;

 printf("set arp entry for IP:%s\tMAC:%s\n", ip, mac);
        
  /*you must add this becasue some system will return "Invlid argument"
    because some argument isn't zero*/

  memset(&arpreq, 0, sizeof(struct arpreq));
  sin = (struct sockaddr_in *) &arpreq.arp_pa;
  memset(sin, 0, sizeof(struct sockaddr_in));
  sin->sin_family = AF_INET;
  ina.s_addr = inet_addr(ip);
  memcpy(&sin->sin_addr, (char *)&ina, sizeof(struct in_addr));
  
  get_hw_addr((unsigned char*)arpreq.arp_ha.sa_data, mac);
  strcpy(arpreq.arp_dev, DEV_NAME);
  
  flags = ATF_PERM | ATF_COM;//note, must set flag, if not,you will get error

  arpreq.arp_flags = flags;
  
  rc = ioctl(sd, SIOCSARP, &arpreq);
  if (rc < 0)
    {
      printf("%s\n", "set arp error...");
      return -1;
    }
  else
     printf("%s\n", "set arp successfully");

  return 0;
}

static int arp_get(char* ip)
{
 struct arpreq arpreq;
 struct sockaddr_in *sin;
 struct in_addr ina;
 unsigned char *hw_addr;
 int rc;

  printf("Find arp entry for IP : %s\n", ip);
        
  /*you must add this becasue some system will return "Invlid argument"
    because some argument isn't zero*/


   memset(&arpreq, 0, sizeof(struct arpreq));

  sin = (struct sockaddr_in *) &arpreq.arp_pa;
  memset(sin, 0, sizeof(struct sockaddr_in));
  sin->sin_family = AF_INET;
  ina.s_addr = inet_addr(ip);
  memcpy(&sin->sin_addr, (char *)&ina, sizeof(struct in_addr));
        
  strcpy(arpreq.arp_dev, DEV_NAME);
  rc = ioctl(sd, SIOCGARP, &arpreq);
  if (rc < 0)
    {
      printf("%s\n", "Entry not available in cache...");
      return -1;
    }
  else
   {
     printf("%s\n", "entry has been successfully retreived");
     hw_addr = (unsigned char *) arpreq.arp_ha.sa_data;

     printf("HWAddr found : %02x:%02x:%02x:%02x:%02x:%02x\n",
                hw_addr[0], hw_addr[1], hw_addr[2], hw_addr[3], hw_addr[4], hw_addr[5]);
    }
  return 0;
}
    
int main(int argc, char **argv)
{
 sd = socket(AF_INET, SOCK_DGRAM, 0);
 if (sd < 0)
  {
    perror("socket() error\n");
    exit(1);
   }
    
  if(strcmp(argv[1],"add")==0)
      arp_set(argv[2], argv[3]);
  else if(strcmp(argv[1],"del")==0)
      arp_del(argv[2]);
  else if(strcmp(argv[1], "get")==0)    
      arp_get(argv[2]);
 
  close(sd);
  return 0;    
}


阅读(2843) | 评论(2) | 转发(2) |
给主人留下些什么吧!~~

ubuntuer2010-04-23 10:28:45

不用socket你怎么把数据传下去? 你连手机都没有,光有手机号和你想发送的信息,想说的话!无用!

chinaunix网友2010-04-21 15:13:17

你好: 我大致学习了一下你的程序,我想问,如果删除本机的arp表怎么删除,也就是 ioctl(sd, SIOCGARP, &arpreq)中的sd应该是什么?就不用socket的了吧? 等待你的回复,谢谢