diff -urNp iproute2-2.6.24-rc7/include/linux/rtnetlink.h iproute2-2.6.24-rc7-iparp/include/linux/rtnetlink.h
--- iproute2-2.6.24-rc7/include/linux/rtnetlink.h 2008-01-08 18:59:32.000000000 +0200
+++ iproute2-2.6.24-rc7-iparp/include/linux/rtnetlink.h 2008-01-27 17:09:04.000000000 +0200
@@ -74,6 +74,17 @@ enum {
RTM_GETTFILTER,
#define RTM_GETTFILTER RTM_GETTFILTER
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ RTM_NEWARPRULE = 48,
+#define RTM_NEWARPRULE RTM_NEWARPRULE
+ RTM_DELARPRULE,
+#define RTM_DELARPRULE RTM_DELARPRULE
+ RTM_GETARPRULE,
+#define RTM_GETARPRULE RTM_GETARPRULE
+#endif
+/* Constants below are kernel 2.6 specific, so they can overlap with
+ the 2.4-specific above */
+
RTM_NEWACTION = 48,
#define RTM_NEWACTION RTM_NEWACTION
RTM_DELACTION,
@@ -90,6 +101,17 @@ enum {
RTM_GETANYCAST = 62,
#define RTM_GETANYCAST RTM_GETANYCAST
+/* Warning! this is only valid for 2.6.0-2.6.12, because the
+ RTM_NEWNEIGHTBL messages were added in 2.6.13 ! */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) && \
+ LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,12)
+ RTM_NEWARPRULE = 64,
+#define RTM_NEWARPRULE RTM_NEWARPRULE
+ RTM_DELARPRULE,
+#define RTM_DELARPRULE RTM_DELARPRULE
+ RTM_GETARPRULE,
+#define RTM_GETARPRULE RTM_GETARPRULE
+#endif
RTM_NEWNEIGHTBL = 64,
#define RTM_NEWNEIGHTBL RTM_NEWNEIGHTBL
RTM_GETNEIGHTBL = 66,
@@ -100,6 +122,23 @@ enum {
RTM_NEWNDUSEROPT = 68,
#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT
+/* Warning! you should check that this does not overlap with any define
+ above and that it matches your kernel's definitions ! */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ /* 2.6.24 */
+ RTM_NEWARPRULE = 72,
+#else
+ /* 2.6.13 - 2.6.23 */
+ RTM_NEWARPRULE = 68,
+#endif
+#define RTM_NEWARPRULE RTM_NEWARPRULE
+ RTM_DELARPRULE,
+#define RTM_DELARPRULE RTM_DELARPRULE
+ RTM_GETARPRULE,
+#define RTM_GETARPRULE RTM_GETARPRULE
+#endif
+
__RTM_MAX,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
};
@@ -527,6 +566,8 @@ enum
#define RTMGRP_DECnet_IFADDR 0x1000
#define RTMGRP_DECnet_ROUTE 0x4000
+#define RTMGRP_ARP 0x00010000
+
#define RTMGRP_IPV6_PREFIX 0x20000
/* RTnetlink multicast groups */
@@ -571,6 +612,8 @@ enum rtnetlink_groups {
#define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE
RTNLGRP_ND_USEROPT,
#define RTNLGRP_ND_USEROPT RTNLGRP_ND_USEROPT
+ RTNLGRP_ARP,
+#define RTNLGRP_ARP RTNLGRP_ARP
__RTNLGRP_MAX
};
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
@@ -590,5 +633,53 @@ struct tcamsg
/* End of information exported to user level */
+/******************************************************************************
+ * Definitions used in ARP tables administration
+ ****/
+
+#define ARPA_TABLE_INPUT 0
+#define ARPA_TABLE_OUTPUT 1
+#define ARPA_TABLE_FORWARD 2
+#define ARPA_TABLE_ALL -1
+
+#define ARPM_F_PREFSRC 0x0001
+#define ARPM_F_WILDIIF 0x0002
+#define ARPM_F_WILDOIF 0x0004
+#define ARPM_F_BROADCAST 0x0008
+#define ARPM_F_UNICAST 0x0010
+
+struct arpmsg
+{
+ unsigned char arpm_family;
+ unsigned char arpm_table;
+ unsigned char arpm_action;
+ unsigned char arpm_from_len;
+ unsigned char arpm_to_len;
+ unsigned char arpm__pad1;
+ unsigned short arpm__pad2;
+ unsigned arpm_pref;
+ unsigned arpm_flags;
+};
+
+enum
+{
+ ARPA_UNSPEC,
+ ARPA_FROM, /* FROM IP prefix */
+ ARPA_TO, /* TO IP prefix */
+ ARPA_LLFROM, /* FROM LL prefix */
+ ARPA_LLTO, /* TO LL prefix */
+ ARPA_LLSRC, /* New SRC lladdr */
+ ARPA_LLDST, /* New DST lladdr */
+ ARPA_IIF, /* In interface prefix */
+ ARPA_OIF, /* Out interface prefix */
+ ARPA_SRC, /* New IP SRC */
+ ARPA_DST, /* New IP DST, not used */
+ ARPA_PACKETS, /* Packets */
+};
+
+#define ARPA_MAX ARPA_PACKETS
+
+#define ARPA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct arpmsg))))
+#define ARPA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct arpmsg))
#endif /* __LINUX_RTNETLINK_H */
diff -urNp iproute2-2.6.24-rc7/ip/Makefile iproute2-2.6.24-rc7-iparp/ip/Makefile
--- iproute2-2.6.24-rc7/ip/Makefile 2008-01-08 18:59:32.000000000 +0200
+++ iproute2-2.6.24-rc7-iparp/ip/Makefile 2008-01-27 16:58:37.000000000 +0200
@@ -1,4 +1,4 @@
-IPOBJ=ip.o ipaddress.o iproute.o iprule.o \
+IPOBJ=ip.o ipaddress.o iproute.o iprule.o iparp.o \
rtm_map.o iptunnel.o ip6tunnel.o tunnel.o ipneigh.o ipntable.o iplink.o \
ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o \
ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
diff -urNp iproute2-2.6.24-rc7/ip/ip.c iproute2-2.6.24-rc7-iparp/ip/ip.c
--- iproute2-2.6.24-rc7/ip/ip.c 2008-01-08 18:59:32.000000000 +0200
+++ iproute2-2.6.24-rc7-iparp/ip/ip.c 2008-01-27 16:56:18.000000000 +0200
@@ -46,7 +46,7 @@ static void usage(void)
fprintf(stderr,
"Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n"
" ip [ -force ] [-batch filename\n"
-"where OBJECT := { link | addr | route | rule | neigh | ntable | tunnel |\n"
+"where OBJECT := { link | addr | route | rule | neigh | arp | ntable | tunnel |\n"
" maddr | mroute | monitor | xfrm }\n"
" OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
" -f[amily] { inet | inet6 | ipx | dnet | link } |\n"
@@ -69,6 +69,7 @@ static const struct cmd {
{ "rule", do_iprule },
{ "neighbor", do_ipneigh },
{ "neighbour", do_ipneigh },
+ { "arp", do_iparprule },
{ "ntable", do_ipntable },
{ "ntbl", do_ipntable },
{ "link", do_iplink },
diff -urNp iproute2-2.6.24-rc7/ip/ip_common.h iproute2-2.6.24-rc7-iparp/ip/ip_common.h
--- iproute2-2.6.24-rc7/ip/ip_common.h 2008-01-08 18:59:32.000000000 +0200
+++ iproute2-2.6.24-rc7-iparp/ip/ip_common.h 2008-01-27 16:56:18.000000000 +0200
@@ -30,6 +30,8 @@ extern int do_ipntable(int argc, char **
extern int do_iptunnel(int argc, char **argv);
extern int do_ip6tunnel(int argc, char **argv);
extern int do_iplink(int argc, char **argv);
+extern int do_iparprule(int argc, char **argv);
+extern int print_arprule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
extern int do_ipmonitor(int argc, char **argv);
extern int do_multiaddr(int argc, char **argv);
extern int do_multiroute(int argc, char **argv);
diff -urNp iproute2-2.6.24-rc7/ip/iparp.c iproute2-2.6.24-rc7-iparp/ip/iparp.c
--- iproute2-2.6.24-rc7/ip/iparp.c 1970-01-01 02:00:00.000000000 +0200
+++ iproute2-2.6.24-rc7-iparp/ip/iparp.c 2008-01-27 16:56:18.000000000 +0200
@@ -0,0 +1,943 @@
+/*
+ * iparp.c "ip arp".
+ *
+ * Authors: Julian Anastasov , March 2002
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation;
+ *
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "rt_names.h"
+#include "utils.h"
+
+static void usage(void) __attribute__((noreturn));
+
+#define CMD_LIST 0x0001 /* list, lst, show */
+#define CMD_APPEND 0x0002 /* append */
+#define CMD_PREPEND 0x0004 /* prepend, insert */
+#define CMD_ADD 0x0008 /* add, create */
+#define CMD_DEL 0x0010 /* delete, remove */
+#define CMD_CHANGE 0x0020 /* change, chg, update */
+#define CMD_REPLACE 0x0040 /* replace, set */
+#define CMD_FLUSH 0x0080 /* flush */
+#define CMD_TEST 0x0100 /* test */
+
+typedef struct {
+ char *name;
+ int code;
+} strarr_t;
+
+static strarr_t cmds[] = {
+ { "list", CMD_LIST },
+ { "lst", CMD_LIST },
+ { "show", CMD_LIST },
+ { "append", CMD_APPEND },
+ { "prepend", CMD_PREPEND },
+ { "insert", CMD_PREPEND },
+ { "add", CMD_ADD },
+ { "create", CMD_ADD },
+ { "delete", CMD_DEL },
+ { "remove", CMD_DEL },
+ { "change", CMD_CHANGE },
+ { "chg", CMD_CHANGE },
+ { "update", CMD_CHANGE },
+ { "replace", CMD_REPLACE },
+ { "set", CMD_REPLACE },
+ { "flush", CMD_FLUSH },
+ { "test", CMD_TEST },
+
+ { NULL, 0 }
+};
+
+static strarr_t arp_tables[] = {
+ { "input", ARPA_TABLE_INPUT },
+ { "output", ARPA_TABLE_OUTPUT },
+ { "forward", ARPA_TABLE_FORWARD },
+
+ { NULL, 0 }
+};
+
+static strarr_t arp_actions[] = {
+ { "deny", 0 },
+ { "allow", 1 },
+
+ { NULL, 0 }
+};
+
+static int cmd;
+
+static struct
+{
+ int tb;
+ int pref;
+ int action;
+ int flushed;
+ char *flushb;
+ int flushp;
+ int flushe;
+ struct rtnl_handle *rth;
+ inet_prefix rto;
+ inet_prefix mto;
+ inet_prefix rfrom;
+ inet_prefix mfrom;
+ inet_prefix rsrc;
+ __u8 llfrom[MAX_ADDR_LEN];
+ __u8 llto[MAX_ADDR_LEN];
+ __u8 llsrc[MAX_ADDR_LEN];
+ __u8 lldst[MAX_ADDR_LEN];
+ int llfrom_len;
+ int llto_len;
+ int llsrc_len;
+ int lldst_len;
+ int broadcasts;
+ int unicasts;
+ char *iif;
+ char *oif;
+ __u32 packets;
+} f = {
+ tb: ARPA_TABLE_ALL,
+ action: -1,
+};
+
+static strarr_t *lookup_strarr(strarr_t *arr, char *name)
+{
+ strarr_t *p;
+
+ for (p = arr; p->name; p++) {
+ if (matches(name, p->name) == 0)
+ return p;
+ }
+ return NULL;
+}
+
+static strarr_t *lookup_strarr_code(strarr_t *arr, int code)
+{
+ strarr_t *p;
+
+ for (p = arr; p->name; p++) {
+ if (p->code == code)
+ return p;
+ }
+ return NULL;
+}
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: ip arp [ list | flush ] [ RULE ]\n");
+ fprintf(stderr, " ip arp [ append | prepend | add | del | change | replace | test ] RULE\n");
+ fprintf(stderr, "RULE := [ table TABLE_NAME ] [ pref NUMBER ] [ from PREFIX ] [ to PREFIX ]\n");
+ fprintf(stderr, " [ iif STRING ] [ oif STRING ] [ llfrom PREFIX ] [ llto PREFIX ]\n");
+ fprintf(stderr, " [ broadcasts ] [ unicasts ] [ ACTION ] [ ALTER ]\n");
+ fprintf(stderr, "TABLE_NAME := [ input | forward | output ]\n");
+ fprintf(stderr, "ACTION := [ deny | allow ]\n");
+ fprintf(stderr, "ALTER := [ src IP ] [ llsrc LLADDR ] [ lldst LLADDR ]\n");
+ exit(-1);
+}
+
+static int flush_update(void)
+{
+ if (rtnl_send(f.rth, f.flushb, f.flushp) < 0) {
+ perror("Failed to send flush request\n");
+ return -1;
+ }
+ f.flushp = 0;
+ return 0;
+}
+
+int print_arprule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+ FILE *fp = (FILE*)arg;
+ struct arpmsg *r = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ int host_len = 32, family = AF_INET;
+ struct rtattr * tb[RTA_MAX+1];
+ inet_prefix vto, vfrom, vsrc;
+ __u32 packets = 0;
+ char abuf[256];
+ SPRINT_BUF(b1);
+
+ if (n->nlmsg_type != RTM_NEWARPRULE && n->nlmsg_type != RTM_DELARPRULE)
+ return 0;
+
+ if (f.flushb && n->nlmsg_type != RTM_NEWARPRULE)
+ return 0;
+
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0)
+ return -1;
+
+ if (f.tb >= 0 && f.tb != r->arpm_table)
+ return 0;
+
+ if (f.pref > 0 && f.pref != r->arpm_pref)
+ return 0;
+
+ if (f.action >= 0 && f.action != r->arpm_action)
+ return 0;
+
+ if (f.rto.family && f.rto.bitlen > r->arpm_to_len)
+ return 0;
+ if (f.mto.family && f.mto.bitlen >= 0 &&
+ f.mto.bitlen < r->arpm_to_len)
+ return 0;
+
+ if (f.rfrom.family && f.rfrom.bitlen > r->arpm_from_len)
+ return 0;
+ if (f.mfrom.family && f.mfrom.bitlen >= 0 &&
+ f.mfrom.bitlen < r->arpm_from_len)
+ return 0;
+
+ if (f.broadcasts && !(r->arpm_flags & ARPM_F_BROADCAST))
+ return 0;
+
+ if (f.unicasts && !(r->arpm_flags & ARPM_F_UNICAST))
+ return 0;
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, ARPA_MAX, ARPA_RTA(r), len);
+
+ memset(&vto, 0, sizeof(vto));
+ if (tb[ARPA_TO])
+ memcpy(&vto.data, RTA_DATA(tb[ARPA_TO]), (r->arpm_to_len+7)/8);
+
+ memset(&vfrom, 0, sizeof(vfrom));
+ if (tb[ARPA_FROM])
+ memcpy(&vfrom.data, RTA_DATA(tb[ARPA_FROM]),
+ (r->arpm_from_len+7)/8);
+
+ memset(&vsrc, 0, sizeof(vsrc));
+ if (tb[ARPA_SRC])
+ memcpy(&vsrc.data, RTA_DATA(tb[ARPA_SRC]), 4);
+
+ if (f.rto.family && inet_addr_match(&vto, &f.rto, f.rto.bitlen))
+ return 0;
+ if (f.mto.family && f.mto.bitlen >= 0 &&
+ inet_addr_match(&vto, &f.mto, r->arpm_to_len))
+ return 0;
+
+ if (f.rfrom.family && inet_addr_match(&vfrom, &f.rfrom, f.rfrom.bitlen))
+ return 0;
+ if (f.mfrom.family && f.mfrom.bitlen >= 0 &&
+ inet_addr_match(&vfrom, &f.mfrom, r->arpm_from_len))
+ return 0;
+
+ if (f.rsrc.family && inet_addr_match(&vsrc, &f.rsrc, f.rsrc.bitlen))
+ return 0;
+
+ if (f.iif) {
+ char *dev, *cp;
+ int size;
+
+ if (!tb[ARPA_IIF])
+ return 0;
+ dev = (char*)RTA_DATA(tb[ARPA_IIF]);
+ size = strlen(dev);
+ if ((cp = strchr(f.iif, '+')) && size > cp - f.iif)
+ size = cp - f.iif;
+ if (strncmp(dev, f.iif, size) ||
+ (0 != f.iif[size] && '+' != f.iif[size]))
+ return 0;
+ }
+
+ if (f.oif) {
+ char *dev, *cp;
+ int size;
+
+ if (!tb[ARPA_OIF])
+ return 0;
+ dev = (char*)RTA_DATA(tb[ARPA_OIF]);
+ size = strlen(dev);
+ if ((cp = strchr(f.oif, '+')) && size > cp - f.oif)
+ size = cp - f.oif;
+ if (strncmp(dev, f.oif, size) ||
+ (0 != f.oif[size] && '+' != f.oif[size]))
+ return 0;
+ }
+
+ if (f.packets) {
+ if (tb[ARPA_PACKETS])
+ memcpy(&packets, RTA_DATA(tb[ARPA_PACKETS]), 4);
+ if (packets < f.packets)
+ return 0;
+ }
+
+ if (f.flushb) {
+ struct nlmsghdr *fn;
+ if (NLMSG_ALIGN(f.flushp) + n->nlmsg_len > f.flushe) {
+ if (flush_update())
+ return -1;
+ }
+ fn = (struct nlmsghdr*)(f.flushb + NLMSG_ALIGN(f.flushp));
+ memcpy(fn, n, n->nlmsg_len);
+ fn->nlmsg_type = RTM_DELARPRULE;
+ fn->nlmsg_flags = NLM_F_REQUEST;
+ fn->nlmsg_seq = ++f.rth->seq;
+ f.flushp = (((char*)fn) + n->nlmsg_len) - f.flushb;
+ f.flushed++;
+ if (show_stats < 2)
+ return 0;
+ }
+
+ if (n->nlmsg_type == RTM_DELARPRULE)
+ fprintf(fp, "Deleted ");
+
+ {
+ strarr_t *p = lookup_strarr_code(arp_tables, r->arpm_table);
+ fprintf(fp, "%s", p? p->name:"unknown");
+ }
+
+ fprintf(fp, " rule %-2u", r->arpm_pref);
+
+ {
+ strarr_t *p = lookup_strarr_code(arp_actions, r->arpm_action);
+ fprintf(fp, " %-5s ", p? p->name:"unk");
+ }
+
+ if (tb[ARPA_FROM]) {
+ if (r->arpm_from_len != host_len) {
+ fprintf(fp, "from %s/%u",
+ rt_addr_n2a(family,
+ RTA_PAYLOAD(tb[ARPA_FROM]),
+ RTA_DATA(tb[ARPA_FROM]),
+ abuf, sizeof(abuf)),
+ r->arpm_from_len
+ );
+ } else {
+ fprintf(fp, "from %s",
+ format_host(family,
+ RTA_PAYLOAD(tb[ARPA_FROM]),
+ RTA_DATA(tb[ARPA_FROM]),
+ abuf, sizeof(abuf))
+ );
+ }
+ } else if (r->arpm_from_len) {
+ fprintf(fp, "from 0/%d", r->arpm_from_len);
+ } else {
+ fprintf(fp, "from all");
+ }
+
+ if (tb[ARPA_TO]) {
+ if (r->arpm_to_len != host_len) {
+ fprintf(fp, " to %s/%u",
+ rt_addr_n2a(family,
+ RTA_PAYLOAD(tb[ARPA_TO]),
+ RTA_DATA(tb[ARPA_TO]),
+ abuf, sizeof(abuf)),
+ r->arpm_to_len
+ );
+ } else {
+ fprintf(fp, " to %s",
+ format_host(family,
+ RTA_PAYLOAD(tb[ARPA_TO]),
+ RTA_DATA(tb[ARPA_TO]),
+ abuf, sizeof(abuf))
+ );
+ }
+ } else if (r->arpm_to_len) {
+ fprintf(fp, " to 0/%d", r->arpm_to_len);
+ } else {
+ fprintf(fp, " to all");
+ }
+
+ if (tb[ARPA_LLFROM]) {
+ fprintf(fp, " llfrom %s",
+ ll_addr_n2a(RTA_DATA(tb[ARPA_LLFROM]),
+ RTA_PAYLOAD(tb[ARPA_LLFROM]),
+ ARPHRD_VOID,
+ b1, sizeof(b1)));
+ }
+
+ if (tb[ARPA_LLTO]) {
+ fprintf(fp, " llto %s",
+ ll_addr_n2a(RTA_DATA(tb[ARPA_LLTO]),
+ RTA_PAYLOAD(tb[ARPA_LLTO]),
+ ARPHRD_VOID,
+ b1, sizeof(b1)));
+ }
+
+ if (tb[ARPA_IIF]) {
+ fprintf(fp, " iif %s%s",
+ (char*)RTA_DATA(tb[ARPA_IIF]),
+ (r->arpm_flags & ARPM_F_WILDIIF) ? "+" : ""
+ );
+ }
+
+ if (tb[ARPA_OIF]) {
+ fprintf(fp, " oif %s%s",
+ (char*)RTA_DATA(tb[ARPA_OIF]),
+ (r->arpm_flags & ARPM_F_WILDOIF) ? "+" : ""
+ );
+ }
+
+ if (r->arpm_flags & ARPM_F_BROADCAST) {
+ fprintf(fp, " broadcasts");
+ }
+
+ if (r->arpm_flags & ARPM_F_UNICAST) {
+ fprintf(fp, " unicasts");
+ }
+
+ if (tb[ARPA_LLSRC]) {
+ fprintf(fp, " llsrc %s",
+ ll_addr_n2a(RTA_DATA(tb[ARPA_LLSRC]),
+ RTA_PAYLOAD(tb[ARPA_LLSRC]),
+ ARPHRD_VOID,
+ b1, sizeof(b1)));
+ }
+
+ if (tb[ARPA_LLDST]) {
+ fprintf(fp, " lldst %s",
+ ll_addr_n2a(RTA_DATA(tb[ARPA_LLDST]),
+ RTA_PAYLOAD(tb[ARPA_LLDST]),
+ ARPHRD_VOID,
+ b1, sizeof(b1)));
+ }
+
+ if (tb[ARPA_SRC]) {
+ fprintf(fp, " src %s",
+ format_host(family,
+ RTA_PAYLOAD(tb[ARPA_SRC]),
+ RTA_DATA(tb[ARPA_SRC]),
+ abuf, sizeof(abuf))
+ );
+ }
+
+ if (show_stats) {
+ fprintf(fp, "%s", _SL_);
+
+ if (tb[ARPA_PACKETS])
+ fprintf(fp, " packets %u",
+ *(unsigned*) RTA_DATA(tb[ARPA_PACKETS]));
+ }
+
+ fprintf(fp, "\n");
+
+ fflush(fp);
+ return 0;
+}
+
+int iparprule_do_cmd(int c, int argc, char **argv)
+{
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct arpmsg r;
+ char buf[1024];
+ } req;
+ int af = AF_INET;
+ int wild = c & (CMD_LIST | CMD_FLUSH);
+ char *dev = 0;
+
+ f.tb = ARPA_TABLE_ALL;
+ f.action = -1;
+
+ if (!wild) {
+ memset(&req, 0, sizeof(req));
+
+ if (c & CMD_DEL)
+ req.n.nlmsg_type = RTM_DELARPRULE;
+ else
+ req.n.nlmsg_type = RTM_NEWARPRULE;
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct arpmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.r.arpm_table = ARPA_TABLE_ALL;
+
+ switch (c) {
+ case CMD_APPEND:
+ req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_APPEND;
+ break;
+ case CMD_PREPEND:
+ req.n.nlmsg_flags |= NLM_F_CREATE;
+ break;
+ case CMD_ADD:
+ req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
+ break;
+ case CMD_DEL:
+ req.n.nlmsg_flags |= 0;
+ break;
+ case CMD_CHANGE:
+ req.n.nlmsg_flags |= NLM_F_REPLACE;
+ break;
+ case CMD_REPLACE:
+ req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_REPLACE;
+ break;
+ case CMD_TEST:
+ req.n.nlmsg_flags |= NLM_F_EXCL;
+ break;
+ }
+ }
+
+ for (; argc > 0; argc--, argv++) {
+ if (matches(*argv, "table") == 0 ||
+ matches(*argv, "lookup") == 0) {
+ strarr_t *c;
+
+ NEXT_ARG();
+ if (f.tb != ARPA_TABLE_ALL) duparg("table", *argv);
+ c = lookup_strarr(arp_tables, *argv);
+ if (!c && strcmp(*argv, "all") != 0)
+ invarg("table value is invalid", *argv);
+ if (c)
+ f.tb = c->code;
+ continue;
+ }
+
+ if (matches(*argv, "rule") == 0 ||
+ matches(*argv, "preference") == 0 ||
+ matches(*argv, "order") == 0 ||
+ matches(*argv, "priority") == 0) {
+ NEXT_ARG();
+ goto get_pref_;
+ }
+
+ if (matches(*argv, "input") == 0) {
+ if (f.tb != ARPA_TABLE_ALL) duparg("input", *argv);
+ f.tb = ARPA_TABLE_INPUT;
+ continue;
+ }
+
+ if (matches(*argv, "output") == 0) {
+ if (f.tb != ARPA_TABLE_ALL) duparg("output", *argv);
+ f.tb = ARPA_TABLE_OUTPUT;
+ continue;
+ }
+
+ if (matches(*argv, "forward") == 0) {
+ if (f.tb != ARPA_TABLE_ALL) duparg("forward", *argv);
+ f.tb = ARPA_TABLE_FORWARD;
+ continue;
+ }
+
+ if (matches(*argv, "allow") == 0) {
+ f.action = 1;
+ continue;
+ }
+
+ if (matches(*argv, "deny") == 0 ||
+ matches(*argv, "drop") == 0) {
+ f.action = 0;
+ continue;
+ }
+
+ if (matches(*argv, "from") == 0) {
+ /* For any command */
+ NEXT_ARG();
+ if (wild && matches(*argv, "root") == 0) {
+ NEXT_ARG();
+ get_prefix(&f.rfrom, *argv, af);
+ continue;
+ }
+ if (wild && matches(*argv, "match") == 0) {
+ NEXT_ARG();
+ get_prefix(&f.mfrom, *argv, af);
+ continue;
+ }
+ if (matches(*argv, "exact") == 0) {
+ NEXT_ARG();
+ }
+ if (f.mfrom.family) duparg("from", *argv);
+ get_prefix(&f.mfrom, *argv, af);
+ f.rfrom = f.mfrom;
+ if (!wild) {
+ req.r.arpm_from_len = f.mfrom.bitlen;
+ addattr_l(&req.n, sizeof(req), ARPA_FROM,
+ &f.mfrom.data, 4);
+ }
+ continue;
+ }
+
+ if (matches(*argv, "to") == 0) {
+ /* For any command */
+ NEXT_ARG();
+ if (wild && matches(*argv, "root") == 0) {
+ NEXT_ARG();
+ get_prefix(&f.rto, *argv, af);
+ continue;
+ }
+ if (wild && matches(*argv, "match") == 0) {
+ NEXT_ARG();
+ get_prefix(&f.mto, *argv, af);
+ continue;
+ }
+ if (matches(*argv, "exact") == 0) {
+ NEXT_ARG();
+ }
+ if (f.mto.family) duparg("to", *argv);
+ get_prefix(&f.mto, *argv, af);
+ f.rto = f.mto;
+ if (!wild) {
+ req.r.arpm_to_len = f.mto.bitlen;
+ addattr_l(&req.n, sizeof(req), ARPA_TO,
+ &f.mto.data, 4);
+ }
+ continue;
+ }
+
+ if (matches(*argv, "src") == 0) {
+ NEXT_ARG();
+ if (wild && matches(*argv, "root") == 0) {
+ NEXT_ARG();
+ get_prefix(&f.rsrc, *argv, af);
+ continue;
+ }
+ if (matches(*argv, "exact") == 0) {
+ NEXT_ARG();
+ }
+ if (f.rsrc.family) duparg("src", *argv);
+ get_prefix(&f.rsrc, *argv, af);
+ if (!f.rsrc.bitlen) {
+ f.rsrc.bitlen = 32;
+ f.rsrc.data[0] = 0;
+ }
+ if (f.rsrc.bitlen != 32)
+ invarg("src value is invalid", *argv);
+ if (!wild)
+ addattr_l(&req.n, sizeof(req), ARPA_SRC,
+ &f.rsrc.data, 4);
+ continue;
+ }
+
+ if (matches(*argv, "llfrom") == 0) {
+ /* For any command */
+ __u8 llabuf[MAX_ADDR_LEN];
+ int l;
+
+ NEXT_ARG();
+
+ if (f.llfrom_len) duparg("llfrom", *argv);
+
+ l = ll_addr_a2n(llabuf, sizeof(llabuf), *argv);
+ if (l < 0) return 1;
+ if (l > sizeof(llabuf)) l = sizeof(llabuf);
+ f.llfrom_len = l;
+ memcpy(f.llfrom, llabuf, l);
+ if (!wild)
+ addattr_l(&req.n, sizeof(req), ARPA_LLFROM,
+ llabuf, l);
+ continue;
+ }
+
+ if (matches(*argv, "llto") == 0) {
+ /* For any command */
+ __u8 llabuf[MAX_ADDR_LEN];
+ int l;
+
+ NEXT_ARG();
+
+ if (f.llto_len) duparg("llto", *argv);
+
+ l = ll_addr_a2n(llabuf, sizeof(llabuf), *argv);
+ if (l < 0) return 1;
+ if (l > sizeof(llabuf)) l = sizeof(llabuf);
+ f.llto_len = l;
+ memcpy(f.llto, llabuf, l);
+ if (!wild)
+ addattr_l(&req.n, sizeof(req), ARPA_LLTO,
+ llabuf, l);
+ continue;
+ }
+
+ if (matches(*argv, "llsrc") == 0) {
+ /* For any command */
+ __u8 llabuf[MAX_ADDR_LEN];
+ int l;
+
+ NEXT_ARG();
+
+ if (f.llsrc_len) duparg("llsrc", *argv);
+
+ l = ll_addr_a2n(llabuf, sizeof(llabuf), *argv);
+ if (l < 0) return 1;
+ if (l > sizeof(llabuf)) l = sizeof(llabuf);
+ f.llsrc_len = l;
+ memcpy(f.llsrc, llabuf, l);
+ if (!wild)
+ addattr_l(&req.n, sizeof(req), ARPA_LLSRC,
+ llabuf, l);
+ continue;
+ }
+
+ if (matches(*argv, "lldst") == 0) {
+ /* For any command */
+ __u8 llabuf[MAX_ADDR_LEN];
+ int l;
+
+ NEXT_ARG();
+
+ if (f.lldst_len) duparg("lldst", *argv);
+
+ l = ll_addr_a2n(llabuf, sizeof(llabuf), *argv);
+ if (l < 0) return 1;
+ if (l > sizeof(llabuf)) l = sizeof(llabuf);
+ f.lldst_len = l;
+ memcpy(f.lldst, llabuf, l);
+ if (!wild)
+ addattr_l(&req.n, sizeof(req), ARPA_LLDST,
+ llabuf, l);
+ continue;
+ }
+
+ if (strcmp(*argv, "dev") == 0) {
+ NEXT_ARG();
+ if (dev) duparg("dev", *argv);
+ dev = *argv;
+ continue;
+ }
+
+ if (strcmp(*argv, "iif") == 0) {
+ NEXT_ARG();
+ if (f.iif) duparg("iif", *argv);
+ f.iif = *argv;
+ continue;
+ }
+
+ if (strcmp(*argv, "oif") == 0) {
+ NEXT_ARG();
+ if (f.oif) duparg("oif", *argv);
+ f.oif = *argv;
+ continue;
+ }
+
+ if (matches(*argv, "packets") == 0) {
+ NEXT_ARG();
+ if (f.packets) duparg("packets", *argv);
+ if (get_u32(&f.packets, *argv, 0))
+ invarg("packets value is invalid", *argv);
+ if (!wild)
+ addattr32(&req.n, sizeof(req), ARPA_PACKETS,
+ f.packets);
+ continue;
+ }
+
+ if (matches(*argv, "broadcasts") == 0 ||
+ strcmp(*argv, "brd") == 0) {
+ f.broadcasts = 1;
+ continue;
+ }
+
+ if (matches(*argv, "unicasts") == 0) {
+ f.unicasts = 1;
+ continue;
+ }
+
+ if (matches(*argv, "help") == 0)
+ usage();
+
+ get_pref_:
+
+ {
+ int pref;
+
+ if (f.pref) duparg("preference", *argv);
+
+ if (get_u32(&pref, *argv, 0) || !pref)
+ invarg("preference value is invalid", *argv);
+ f.pref = pref;
+ }
+ }
+
+ if (f.tb == ARPA_TABLE_ALL && !wild) {
+ f.tb = ARPA_TABLE_INPUT;
+ }
+
+ if (dev) {
+ if (f.tb == ARPA_TABLE_INPUT && !f.iif)
+ f.iif = dev;
+ else
+ if (f.tb == ARPA_TABLE_OUTPUT && !f.oif)
+ f.oif = dev;
+ else
+ invarg("unexpected or duplicate value for dev", dev);
+ }
+
+ if (!wild) {
+ if (f.action < 0) {
+ /* Default action is allow */
+ f.action = 1;
+ }
+ }
+
+ /* Unexpected parameters for table input */
+ if (!wild && f.tb == ARPA_TABLE_INPUT) {
+ if (f.rsrc.family) {
+ fprintf(stderr, "Unexpected argument for table input: src\n");
+ return 1;
+ }
+ if (f.oif) {
+ fprintf(stderr, "Unexpected argument for table input: oif\n");
+ return 1;
+ }
+ }
+
+ /* Unexpected parameters for table output */
+ if (!wild && f.tb == ARPA_TABLE_OUTPUT) {
+ if (f.iif) {
+ fprintf(stderr, "Unexpected argument for table output: iif\n");
+ return 1;
+ }
+ if (f.broadcasts) {
+ fprintf(stderr, "Unexpected argument for table output: broadcasts\n");
+ return 1;
+ }
+ if (f.unicasts) {
+ fprintf(stderr, "Unexpected argument for table output: unicasts\n");
+ return 1;
+ }
+ }
+
+ /* Unexpected parameters for table forward */
+ if (!wild && f.tb == ARPA_TABLE_FORWARD) {
+ if (f.rsrc.family) {
+ fprintf(stderr, "Unexpected argument for table forward: src\n");
+ return 1;
+ }
+ }
+
+ if (!wild && f.broadcasts && f.unicasts) {
+ fprintf(stderr, "Incompatible arguments: broadcasts and unicasts\n");
+ return 1;
+ }
+
+ if (!wild) {
+ if (!(f.pref || f.mfrom.family || f.mto.family ||
+ f.llfrom_len || f.llto_len || f.iif || f.oif ||
+ f.broadcasts || f.unicasts)) {
+ fprintf(stderr, "No arguments for rule selection.\n");
+ return 1;
+ }
+ }
+
+ if (!wild) {
+ req.r.arpm_family = AF_INET;
+ req.r.arpm_table = f.tb;
+ req.r.arpm_pref = f.pref;
+ req.r.arpm_action = f.action;
+
+ if (f.broadcasts)
+ req.r.arpm_flags |= ARPM_F_BROADCAST;
+ if (f.unicasts)
+ req.r.arpm_flags |= ARPM_F_UNICAST;
+
+ if (f.iif) {
+ char *cp = strchr(f.iif, '+');
+
+ if (cp) {
+ *cp = 0;
+ req.r.arpm_flags |= ARPM_F_WILDIIF;
+ }
+ if (strlen(f.iif) > IFNAMSIZ-1) {
+ fprintf(stderr, "Value for iif is too long\n");
+ return 1;
+ }
+ addattr_l(&req.n, sizeof(req), ARPA_IIF,
+ f.iif, strlen(f.iif)+1);
+ }
+ if (f.oif) {
+ char *cp = strchr(f.oif, '+');
+
+ if (cp) {
+ *cp = 0;
+ req.r.arpm_flags |= ARPM_F_WILDOIF;
+ }
+ if (strlen(f.oif) > IFNAMSIZ-1) {
+ fprintf(stderr, "Value for oif is too long\n");
+ return 1;
+ }
+ addattr_l(&req.n, sizeof(req), ARPA_OIF,
+ f.oif, strlen(f.oif)+1);
+ }
+ }
+
+ if (rtnl_open(&rth, 0) < 0)
+ return 1;
+
+ if (c & CMD_FLUSH) {
+ int round = 0;
+ char flushb[4096-512];
+
+ f.flushb = flushb;
+ f.flushp = 0;
+ f.flushe = sizeof(flushb);
+ f.rth = &rth;
+
+ for (;;) {
+ if (rtnl_wilddump_request(&rth, af, RTM_GETARPRULE) < 0) {
+ perror("Cannot send dump request");
+ exit(1);
+ }
+ f.flushed = 0;
+ if (rtnl_dump_filter(&rth, print_arprule, stdout, NULL, NULL) < 0) {
+ fprintf(stderr, "Flush terminated\n");
+ exit(1);
+ }
+ if (f.flushed == 0) {
+ if (round == 0) {
+ fprintf(stderr, "Nothing to flush.\n");
+ }
+ else
+ if (show_stats)
+ printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
+ fflush(stdout);
+ return 0;
+ }
+ round++;
+ if (flush_update() < 0)
+ exit(1);
+ if (show_stats) {
+ printf("\n*** Round %d, deleting %d entries ***\n", round, f.flushed);
+ fflush(stdout);
+ }
+ }
+ }
+ else
+ if (wild) {
+ if (rtnl_wilddump_request(&rth, af, RTM_GETARPRULE) < 0) {
+ perror("Cannot send dump request");
+ return 1;
+ }
+
+ if (rtnl_dump_filter(&rth, print_arprule, stdout, NULL, NULL) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ return 1;
+ }
+ } else {
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+ return 2;
+ }
+
+ return 0;
+}
+
+int do_iparprule(int argc, char **argv)
+{
+strarr_t *c;
+
+ if (argc < 1)
+ return iparprule_do_cmd(CMD_LIST, 0, NULL);
+ c = lookup_strarr(cmds, argv[0]);
+ if (c) {
+ cmd = c->code;
+ return iparprule_do_cmd(cmd, argc-1, argv+1);
+ }
+ if (matches(argv[0], "help") == 0)
+ usage();
+
+ fprintf(stderr, "Command \"%s\" is unknown, try \"ip arp help\".\n", *argv);
+ exit(-1);
+}
+
diff -urNp iproute2-2.6.24-rc7/ip/ipmonitor.c iproute2-2.6.24-rc7-iparp/ip/ipmonitor.c
--- iproute2-2.6.24-rc7/ip/ipmonitor.c 2008-01-08 18:59:32.000000000 +0200
+++ iproute2-2.6.24-rc7-iparp/ip/ipmonitor.c 2008-01-27 17:05:56.000000000 +0200
@@ -66,6 +66,10 @@ int accept_msg(const struct sockaddr_nl
print_rule(who, n, arg);
return 0;
}
+ if (n->nlmsg_type == RTM_NEWARPRULE || n->nlmsg_type == RTM_DELARPRULE) {
+ print_arprule(who, n, arg);
+ return 0;
+ }
if (n->nlmsg_type == 15) {
char *tstr;
time_t secs = ((__u32*)NLMSG_DATA(n))[0];
@@ -98,6 +102,7 @@ int do_ipmonitor(int argc, char **argv)
int laddr=0;
int lroute=0;
int lprefix=0;
+ int larprule=0;
rtnl_close(&rth);
ipaddr_reset_filter(1);
@@ -120,6 +125,9 @@ int do_ipmonitor(int argc, char **argv)
} else if (matches(*argv, "prefix") == 0) {
lprefix=1;
groups = 0;
+ } else if (matches(*argv, "arprules") == 0) {
+ larprule=1;
+ groups = 0;
} else if (strcmp(*argv, "all") == 0) {
groups = ~RTMGRP_TC;
} else if (matches(*argv, "help") == 0) {
@@ -149,6 +157,8 @@ int do_ipmonitor(int argc, char **argv)
if (!preferred_family || preferred_family == AF_INET6)
groups |= nl_mgrp(RTNLGRP_IPV6_PREFIX);
}
+ if (larprule)
+ groups |= nl_mgrp(RTNLGRP_ARP);
if (file) {
FILE *fp;