资讯专栏INFORMATION COLUMN

Linux网络接口操作之ioctl-2_ARP条目

lncwwn / 773人阅读

摘要:相关定义条目相关操作的定义条目操作的结构与相关标志作为的第个参数,分别用于在表中删除获取添加单个条目结构地址作为的第个参数执行操作时,被转换为套接字地址结构,表示目的端的地址为通用套接字地址结构,为,中包含网络接口的层地址,如以太网地址

相关定义

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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/9301.html

相关文章

  • Linux网络接口操作ioctl-1_接口参数

    摘要:提供了一系列网络接口操作相关的命令集,其中,一些传统的工具,如软件包中的,,等都是通过系统调用实现本篇介绍使用进行网络接口参数的获取与设置函数原型第一个参数指定一个由创建的文件描述符第二个参数指定操作的类型,即对该文件描述符执行何种操作第 Linux提供了一系列网络接口操作相关的命令集,其中,一些传统的工具,如net-tools软件包中的ifconfig(8),arp(8),route...

    FleyX 评论0 收藏0
  • Linux网络接口操作ioctl-1_接口参数

    摘要:提供了一系列网络接口操作相关的命令集,其中,一些传统的工具,如软件包中的,,等都是通过系统调用实现本篇介绍使用进行网络接口参数的获取与设置函数原型第一个参数指定一个由创建的文件描述符第二个参数指定操作的类型,即对该文件描述符执行何种操作第 Linux提供了一系列网络接口操作相关的命令集,其中,一些传统的工具,如net-tools软件包中的ifconfig(8),arp(8),route...

    Flands 评论0 收藏0
  • OpenFlow(OVS)下的“路由技术”

    摘要:需要修改数据包的二层源目地址以及三层包头的因为路由是逐跳转发的,每一跳都需要做这些工作,即使是现在通过流表转发,中间的转发器直接转发报文,到达倒数第一跳的时候还是需要把数据包的目的地址修改为接受端的地址。 前言 熟悉这款设备的同学,应该也快到不惑之年了吧!这应该是Cisco最古老的路由器了。上个世纪80年代至今,路由交换技术不断发展,但是在这波澜壮阔的变化之中,总有一些东西在嘈杂的机房...

    cyqian 评论0 收藏0
  • OpenFlow(OVS)下的“路由技术”

    摘要:需要修改数据包的二层源目地址以及三层包头的因为路由是逐跳转发的,每一跳都需要做这些工作,即使是现在通过流表转发,中间的转发器直接转发报文,到达倒数第一跳的时候还是需要把数据包的目的地址修改为接受端的地址。 前言 熟悉这款设备的同学,应该也快到不惑之年了吧!这应该是Cisco最古老的路由器了。上个世纪80年代至今,路由交换技术不断发展,但是在这波澜壮阔的变化之中,总有一些东西在嘈杂的机房...

    ityouknow 评论0 收藏0
  • OpenFlow(OVS)下的“路由技术”

    摘要:需要修改数据包的二层源目地址以及三层包头的因为路由是逐跳转发的,每一跳都需要做这些工作,即使是现在通过流表转发,中间的转发器直接转发报文,到达倒数第一跳的时候还是需要把数据包的目的地址修改为接受端的地址。 前言 熟悉这款设备的同学,应该也快到不惑之年了吧!这应该是Cisco最古老的路由器了。上个世纪80年代至今,路由交换技术不断发展,但是在这波澜壮阔的变化之中,总有一些东西在嘈杂的机房...

    zhigoo 评论0 收藏0

发表评论

0条评论

lncwwn

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<