相關定義
-
ARP條目相關操作的定義
/* include/linux/sockios.h */
/* ARP cache control calls. */
/* 0x8950 - 0x8952 * obsolete calls, don`t re-use */
#define SIOCDARP 0x8953 /* delete ARP table entry */
#define SIOCGARP 0x8954 /* get ARP table entry */
#define SIOCSARP 0x8955 /* set ARP table entry */
-
ARP條目操作的結構與相關標誌
/* include/linux/if_arp.h */
/* ARP ioctl request. */
struct arpreq {
struct sockaddr arp_pa; /* protocol address */
struct sockaddr arp_ha; /* hardware address */
int arp_flags; /* flags */
struct sockaddr arp_netmask; /* netmask (only for proxy arps) */
char arp_dev[16];
};
/* ARP Flag values. */
#define ATF_COM 0x02 /* completed entry (ha valid) */
#define ATF_PERM 0x04 /* permanent entry */
#define ATF_PUBL 0x08 /* publish entry */
#define ATF_USETRAILERS 0x10 /* has requested trailers */
#define ATF_NETMASK 0x20 /* want to use a netmask (only
for proxy entries) */
#define ATF_DONTPUB 0x40 /* don`t answer this addresses */
SIOCDARP/SIOCGARP/SIOCSARP作為ioctl(2)的第2個引數,分別用於在ARP表中刪除/獲取/新增單個條目
struct arpreq結構地址作為ioctl(2)的第3個引數;執行操作時,arp_pa被轉換為IPv4套接字地址結構(struct sockaddr_in),表示目的端的IP地址;arp_ha為通用套接字地址結構,sa_family為AF_UNSPEC,sa_data中包含網路介面的2層地址,如乙太網mac地址,表示目的端的2層地址;arp_flags用於指定ARP操作的標誌;arp_netmask用於代理ARP;arp_dev用於指定本地網路介面的名稱
獲取ARP條目時,需要初始化arp_pa,arp_dev,然後呼叫ioctl(SIOCGARP),成功返回後arp_pa中包含的IP地址對應的mac地址儲存在arp_ha.sa_data中;若指定的網路介面不存在,或存在但與指定的目標主機IP地址不對應,則ioctl以”(ENXIO)No such device or address”錯誤呼叫失敗
無法通過ioctl操作獲取整個ARP表,arp(8)命令的-a選項是通過讀取系統中的/proc/net/arp檔案內容實現該目的
新增ARP條目時,需要同時初始化arp_pa,arp_dev,arp_ha.sa_data以及arp_flags,然後呼叫ioctl(SIOCSARP)
刪除ARP條目時,需要初始化arp_pa,arp_dev,然後呼叫ioctl(SIOCDARP),成功返回後,指定目標地址對應的ARP條目被刪除;若指定的網路介面不存在,或存在但與指定的目標主機IP地址不對應,則ioctl以”(ENXIO)No such device or address”錯誤呼叫失敗
示例程式
系統雙網路卡,名稱分別為eth0與eth1
初始的ARP表內容為:
# arp -n
Address HWtype HWaddress Flags Mask Iface
192.168.56.254 ether 00:50:56:f8:57:2f C eth0
192.168.56.2 ether 00:50:56:f4:a3:a0 C eth0
192.168.56.1 ether 00:50:56:c0:00:08 C eth0
1 . 指定本地網路介面名稱與目標主機IP地址作為引數,獲取對應的mac地址
/* get_arp_entry_ioctl.c */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if_arp.h>
static void get_arp_entry(const char *, const char *);
int main(int argc, char *argv[])
{
if (argc != 3) {
fprintf(stderr,
"Usage: %s [local interface name] [dst ip addres]
", argv[0]);
exit(EXIT_FAILURE);
}
char ifname[IFNAMSIZ] = {` `};
strncpy(ifname, argv[1], IFNAMSIZ-1);
char ipaddr[INET_ADDRSTRLEN] = {` `};
strncpy(ipaddr, argv[2], INET_ADDRSTRLEN);
get_arp_entry(ifname, ipaddr);
return 0;
}
static void get_arp_entry(const char *dev, const char *ip)
{
int sfd, saved_errno, ret;
unsigned char *mac;
struct arpreq arp_req;
struct sockaddr_in *sin;
sin = (struct sockaddr_in *)&(arp_req.arp_pa);
memset(&arp_req, 0, sizeof(arp_req));
sin->sin_family = AF_INET;
inet_pton(AF_INET, ip, &(sin->sin_addr));
strncpy(arp_req.arp_dev, dev, IFNAMSIZ-1);
sfd = socket(AF_INET, SOCK_DGRAM, 0);
saved_errno = errno;
ret = ioctl(sfd, SIOCGARP, &arp_req);
if (ret < 0) {
fprintf(stderr, "Get ARP entry failed : %s
", strerror(errno));
exit(EXIT_FAILURE);
}
errno = saved_errno;
if (arp_req.arp_flags & ATF_COM) {
mac = (unsigned char *)arp_req.arp_ha.sa_data;
printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x
",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
} else {
printf("MAC: Not in the ARP cache.
");
}
}
編譯並執行
# gcc get_arp_entry_ioctl.c -g3 -DC=-5 -o get_arp_entry_ioctl
#
# ./get_arp_entry_ioctl eth0 192.168.56.1
MAC: 00:50:56:c0:00:08
#
# ./get_arp_entry_ioctl eth1 192.168.56.1
Get ARP entry failed : No such device or address
#
# ./get_arp_entry_ioctl eth1 192.168.56.100
Get ARP entry failed : No such device or address
2 . 指定本地網路介面名稱,目標主機IP地址與mac地址作為引數,新增ARP條目
/* set_arp_entry_ioctl.c */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/if_arp.h>
static void set_arp_entry(char *, char *, char *);
static int parse_mac(char *, char *);
int main(int argc, char *argv[])
{
if (argc != 4) {
fprintf(stderr,
"Usage: %s [local interface name] [dst ip addres] [dst mac address]
",
argv[0]);
exit(EXIT_FAILURE);
}
char ifname[IFNAMSIZ] = {` `};
strncpy(ifname, argv[1], IFNAMSIZ-1);
char ipaddr[INET_ADDRSTRLEN] = {` `};
strncpy(ipaddr, argv[2], INET_ADDRSTRLEN);
int mac_size = strlen(argv[3]);
unsigned char *macaddr = (unsigned char *)malloc(mac_size);
memcpy(macaddr, argv[3], mac_size);
set_arp_entry(ifname, ipaddr, macaddr);
printf("Adding an arp entry done.
");
return 0;
}
static void set_arp_entry(char *dev, char *ip, char *mac)
{
int sfd, saved_errno, ret;
struct arpreq arp_req;
struct sockaddr_in *sin;
sfd = socket(AF_INET, SOCK_DGRAM, 0);
sin = (struct sockaddr_in *)&(arp_req.arp_pa);
memset(&arp_req, 0, sizeof(arp_req));
sin->sin_family = AF_INET;
inet_pton(AF_INET, ip, &(sin->sin_addr));
strncpy(arp_req.arp_dev, dev, IFNAMSIZ-1);
parse_mac(mac, arp_req.arp_ha.sa_data);
arp_req.arp_flags = ATF_PERM | ATF_COM;
saved_errno = errno;
ret = ioctl(sfd, SIOCSARP, &arp_req);
if (ret < 0) {
fprintf(stderr, "Set ARP entry failed : %s
", strerror(errno));
exit(EXIT_FAILURE);
}
errno = saved_errno;
}
static int parse_mac(char *mac, char *dst)
{
int i;
unsigned byte;
char *s;
i = 0;
while ((s = strsep(&mac, ":")) != NULL) {
if (sscanf(s, "%x", &byte) != 1 || byte > 0xff)
return -1;
*(dst+i) = byte;
i++;
}
if (i != ETH_ALEN) {
fprintf(stderr, "Invalid mac address
");
exit(EXIT_FAILURE);
}
return 0;
}
編譯並執行
# gcc set_arp_entry_ioctl.c -g3 -DC=-5 -o set_arp_entry_ioctl
#
# ./set_arp_entry_ioctl eth0 192.168.56.100 aa:bb:cc:dd:ee:ff
Adding an arp entry done.
#
# ./get_arp_entry_ioctl eth0 192.168.56.100
MAC: aa:bb:cc:dd:ee:ff
#
# arp -n
Address HWtype HWaddress Flags Mask Iface
192.168.56.1 ether 00:50:56:c0:00:08 C eth0
192.168.56.254 ether 00:50:56:f8:57:2f C eth0
192.168.56.100 ether aa:bb:cc:dd:ee:ff CM eth0
192.168.56.2 ether 00:50:56:f4:a3:a0 C eth0
3 . 指定本地網路介面名稱與目標主機IP地址作為引數,刪除對應的ARP條目
/* del_arp_entry_ioctl.c */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if_arp.h>
static void del_arp_entry(char *, char *);
int main(int argc, char *argv[])
{
if (argc != 3) {
fprintf(stderr,
"Usage: %s [local interface name] [dst ip addres]
", argv[0]);
exit(EXIT_FAILURE);
}
char ifname[IFNAMSIZ] = {` `};
strncpy(ifname, argv[1], IFNAMSIZ-1);
char ipaddr[INET_ADDRSTRLEN] = {` `};
strncpy(ipaddr, argv[2], INET_ADDRSTRLEN);
del_arp_entry(ifname, ipaddr);
printf("Deleting an arp entry done.
");
}
static void del_arp_entry(char *dev, char *ip)
{
int sfd, saved_errno, ret;
struct arpreq arp_req;
struct sockaddr_in *sin;
sfd = socket(AF_INET, SOCK_DGRAM, 0);
sin = (struct sockaddr_in *)&(arp_req.arp_pa);
memset(&arp_req, 0, sizeof(arp_req));
sin->sin_family = AF_INET;
strncpy(arp_req.arp_dev, dev, IFNAMSIZ-1);
inet_pton(AF_INET, ip, &(sin->sin_addr));
saved_errno = errno;
ret = ioctl(sfd, SIOCDARP, &arp_req);
if (ret < 0) {
fprintf(stderr, "Delete ARP entry failed : %s
", strerror(errno));
exit(EXIT_FAILURE);
}
errno = saved_errno;
}
編譯並執行
# gcc del_arp_entry_ioctl.c -g3 -DC=-5 -o del_arp_entry_ioctl
#
# ./del_arp_entry_ioctl eth0 192.168.56.100
Deleting an arp entry done.
#
# ./del_arp_entry_ioctl eth0 192.168.56.200
Delete ARP entry failed : No such device or address
#
# ./del_arp_entry_ioctl eth1 192.168.56.100
Delete ARP entry failed : No such device or address
#
# arp -n
Address HWtype HWaddress Flags Mask Iface
192.168.56.1 ether 00:50:56:c0:00:08 C eth0
192.168.56.254 ether 00:50:56:f8:57:2f C eth0
192.168.56.100 (incomplete) eth0
192.168.56.2 ether 00:50:56:f4:a3:a0 C eth0
#
# cat /proc/net/arp
IP address HW type Flags HW address Mask Device
192.168.56.1 0x1 0x2 00:50:56:c0:00:08 * eth0
192.168.56.254 0x1 0x2 00:50:56:f8:57:2f * eth0
192.168.56.100 0x1 0x0 aa:bb:cc:dd:ee:ff * eth0
192.168.56.2 0x1 0x2 00:50:56:f4:a3:a0 * eth0
#
# ./get_arp_entry_ioctl eth0 192.168.56.100
MAC: Not in the ARP cache.