[技術] 在Linux中透過ioctl來設定ARP Table

Written on 11:39 上午 by Yu Lai

一般我們在Linux上可以使用arp這個指令來顯示或設定ARP Table。
而在程式中如果要操作系統的ARP Table的話,除了用system()來呼叫
arp指令外,也可以透過socket fd所提供的ioctl命令來達成,共有3種,
分別是SIOCSARP(設定ARP mapping),SIOCDARP(刪除ARP mapping),
SIOCGARP(獲取ARP mapping)。在操作上需配合struct arpreq,
定義如下:

struct arpreq {
struct sockaddr arp_pa; /* protocol address */
struct sockaddr arp_ha; /* hardware address */
int arp_flags; /* flags */
struct sockaddr arp_netmask; /* netmask of protocol address */
char arp_dev[16];
};


其中,arp_pa必須是AF_INET的socket。而指定的network device
也必須填入arp_dev中。而arp_flag的value定義如下:
ATF_COM = Lookup complete
ATF_PERM = Permanent entry
ATF_PUBL = Publish entry
ATF_USETRAILERS = Trailers requested
ATF_NETMASK = Use a netmask
ATF_DONTPUB = Don't answer

使用SIOCSARP命令時,需設定arpreq的arp_pa,arp_ha和arp_flags。
此時若ARP Table己經存在這筆IP位址,它會將它取出更新。若不存在,則新增。

使用SIOCDARP命令時,只需設定arpreq中的arp_pa和arp_dev,kernel會
根據arp_pa的IP位址從ARP Table的hash中取出,並將其state更新為NUD_FAILED。
這樣在下一次ARP GC時就會被清掉。

使用SIOCGARP命令時,只需要設定arpreq中的arp_pa即可。不過一般我們
不會透過SIOCGARP命令,而是使用/proc/net/arp直接讀取。

以下是SIOCSARP的範例程式:
#include <sys/ioctl.h> 
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>

int set_arp(char * ip, char * mac, char * dev) {

struct arpreq req;
struct sockaddr_in addr;
int sockfd, rc;
unsigned int s_addr;

sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0) {
return 1;
}

/* Inet sockaddr_in struct */
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip);

/* ARP arpreq struct */
memset(&req, 0, sizeof(req));
memcpy(&req.arp_pa, &addr, sizeof(struct sockaddr));
memcpy(req.arp_ha.sa_data, mac, 6);
req.arp_flags = ATF_PERM | ATF_COM;
strcpy(req.arp_dev, dev);

rc = ioctl(sockfd, SIOCSARP, &req);
close(sockfd);

return rc;

}