diff -ur v2.4.26/linux/include/linux/rtnetlink.h linux/include/linux/rtnetlink.h --- v2.4.26/linux/include/linux/rtnetlink.h 2004-02-19 00:23:39.000000000 +0200 +++ linux/include/linux/rtnetlink.h 2004-03-28 16:30:35.353294056 +0300 @@ -46,7 +46,11 @@ #define RTM_DELTFILTER (RTM_BASE+29) #define RTM_GETTFILTER (RTM_BASE+30) -#define RTM_MAX (RTM_BASE+31) +#define RTM_NEWARPRULE (RTM_BASE+32) +#define RTM_DELARPRULE (RTM_BASE+33) +#define RTM_GETARPRULE (RTM_BASE+34) + +#define RTM_MAX (RTM_BASE+35) /* Generic structure for encapsulation of optional route information. @@ -527,6 +531,54 @@ #define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) #define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) +/****************************************************************************** + * 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)) /* SUMMARY: maximal rtattr understood by kernel */ @@ -550,6 +602,8 @@ #define RTMGRP_DECnet_IFADDR 0x1000 #define RTMGRP_DECnet_ROUTE 0x4000 +#define RTMGRP_ARP 0x00010000 + /* End of information exported to user level */ #ifdef __KERNEL__ diff -ur v2.4.26/linux/net/core/rtnetlink.c linux/net/core/rtnetlink.c --- v2.4.26/linux/net/core/rtnetlink.c 2003-08-25 22:06:13.000000000 +0300 +++ linux/net/core/rtnetlink.c 2004-03-28 16:30:35.353294056 +0300 @@ -89,7 +89,8 @@ NLMSG_LENGTH(sizeof(struct rtmsg)), NLMSG_LENGTH(sizeof(struct tcmsg)), NLMSG_LENGTH(sizeof(struct tcmsg)), - NLMSG_LENGTH(sizeof(struct tcmsg)) + NLMSG_LENGTH(sizeof(struct tcmsg)), + NLMSG_LENGTH(sizeof(struct arpmsg)), }; static const int rta_max[(RTM_MAX+1-RTM_BASE)/4] = @@ -101,7 +102,8 @@ RTA_MAX, TCA_MAX, TCA_MAX, - TCA_MAX + TCA_MAX, + ARPA_MAX, }; void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data) diff -ur v2.4.26/linux/net/ipv4/arp.c linux/net/ipv4/arp.c --- v2.4.26/linux/net/ipv4/arp.c 2004-03-28 16:23:57.000000000 +0300 +++ linux/net/ipv4/arp.c 2004-03-28 16:33:06.746278824 +0300 @@ -70,6 +70,7 @@ * arp_xmit so intermediate drivers like * bonding can change the skb before * sending (e.g. insert 8021q tag). + * Julian Anastasov: ARP filtering via netlink */ #include @@ -91,6 +92,7 @@ #include #include #include +#include #include #ifdef CONFIG_SYSCTL #include @@ -195,6 +197,47 @@ gc_thresh3: 1024, }; +struct arpf_node { + struct arpf_node * at_next; + u32 at_pref; + u32 at_from; + u32 at_from_mask; + u32 at_to; + u32 at_to_mask; + u32 at_src; + atomic_t at_packets; + atomic_t at_refcnt; + unsigned at_flags; + unsigned char at_from_len; + unsigned char at_to_len; + unsigned char at_action; + char at_dead; + unsigned char at_llfrom_len; + unsigned char at_llto_len; + unsigned char at_llsrc_len; + unsigned char at_lldst_len; + unsigned char at_iif_len; + unsigned char at_oif_len; + unsigned short at__pad1; + unsigned char at_llfrom[MAX_ADDR_LEN]; + unsigned char at_llto[MAX_ADDR_LEN]; + unsigned char at_llsrc[MAX_ADDR_LEN]; + unsigned char at_lldst[MAX_ADDR_LEN]; + char at_iif[IFNAMSIZ]; + char at_oif[IFNAMSIZ]; +}; + +static struct arpf_node *arp_tabs[3]; + +static kmem_cache_t *arpf_cachep; + +static rwlock_t arpf_lock = RW_LOCK_UNLOCKED; + +static void +arpf_send(int table, struct sk_buff *skb, u32 sip, u32 tip, + unsigned char *from_hw, unsigned char *to_hw, + struct net_device *idev, struct net_device *odev); + int arp_mc_map(u32 addr, u8 *haddr, struct net_device *dev, int dir) { switch (dev->type) { @@ -327,6 +370,7 @@ u32 target = *(u32*)neigh->primary_key; int probes = atomic_read(&neigh->probes); struct in_device *in_dev = in_dev_get(dev); + unsigned char tha[MAX_ADDR_LEN]; if (!in_dev) return; @@ -360,8 +404,10 @@ if ((probes -= neigh->parms->ucast_probes) < 0) { if (!(neigh->nud_state&NUD_VALID)) printk(KERN_DEBUG "trying to ucast probe in NUD_INVALID\n"); - dst_ha = neigh->ha; + dst_ha = tha; read_lock_bh(&neigh->lock); + memcpy(dst_ha, neigh->ha, dev->addr_len); + read_unlock_bh(&neigh->lock); } else if ((probes -= neigh->parms->app_probes) < 0) { #ifdef CONFIG_ARPD neigh_app_ns(neigh); @@ -369,10 +415,7 @@ return; } - arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr, - dst_ha, dev->dev_addr, NULL); - if (dst_ha) - read_unlock_bh(&neigh->lock); + arpf_send(ARPA_TABLE_OUTPUT,skb,saddr,target,NULL,dst_ha,NULL,dev); } static int arp_ignore(struct in_device *in_dev, struct net_device *dev, @@ -849,7 +892,7 @@ if (arp->ar_op == htons(ARPOP_REQUEST) && inet_addr_type(tip) == RTN_LOCAL && !arp_ignore(in_dev,dev,sip,tip)) - arp_send(ARPOP_REPLY,ETH_P_ARP,tip,dev,tip,sha,dev->dev_addr,dev->dev_addr); + arpf_send(ARPA_TABLE_INPUT,skb,sip,tip,sha,tha,dev,NULL); goto out; } @@ -869,7 +912,8 @@ if (!dont_send && IN_DEV_ARPFILTER(in_dev)) dont_send |= arp_filter(sip,tip,dev); if (!dont_send) - arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha); + arpf_send(ARPA_TABLE_INPUT,skb, + sip,tip,sha,tha,dev,NULL); neigh_release(n); } @@ -885,7 +929,9 @@ if (skb->stamp.tv_sec == 0 || skb->pkt_type == PACKET_HOST || in_dev->arp_parms->proxy_delay == 0) { - arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha); + arpf_send(ARPA_TABLE_FORWARD,skb, + sip,tip,sha,tha,dev, + rt->u.dst.dev); } else { pneigh_enqueue(&arp_tbl, in_dev->arp_parms, skb); in_dev_put(in_dev); @@ -1339,6 +1385,554 @@ } +static void arpf_destroy(struct arpf_node *afp) +{ + if (!afp->at_dead) { + printk(KERN_ERR "Destroying alive arp table node %p from %08lx\n", afp, + *(((unsigned long*)&afp)-1)); + return; + } + kmem_cache_free(arpf_cachep, afp); +} + +static inline void arpf_put(struct arpf_node *afp) +{ + if (atomic_dec_and_test(&afp->at_refcnt)) + arpf_destroy(afp); +} + +static inline struct arpf_node * +arpf_lookup(int table, struct sk_buff *skb, u32 sip, u32 tip, + unsigned char *from_hw, unsigned char *to_hw, + struct net_device *idev, struct net_device *odev) +{ + int sz_iif = idev? strlen(idev->name) : 0; + int sz_oif = odev? strlen(odev->name) : 0; + int alen; + struct arpf_node *afp; + + if (ARPA_TABLE_OUTPUT != table) { + alen = idev->addr_len; + } else { + if (!from_hw) from_hw = odev->dev_addr; + if (!to_hw) to_hw = odev->broadcast; + alen = odev->addr_len; + } + + read_lock(&arpf_lock); + for (afp = arp_tabs[table]; afp; afp = afp->at_next) { + if ((tip ^ afp->at_to) & afp->at_to_mask) + continue; + if ((sip ^ afp->at_from) & afp->at_from_mask) + continue; + if (afp->at_llfrom_len && + (afp->at_llfrom_len > alen || + memcmp(from_hw, afp->at_llfrom, afp->at_llfrom_len))) + continue; + if (afp->at_llto_len && + (afp->at_llto_len > alen || + memcmp(to_hw, afp->at_llto, afp->at_llto_len))) + continue; + if (afp->at_iif_len && + (afp->at_iif_len > sz_iif || + memcmp(afp->at_iif, idev->name, afp->at_iif_len) || + (sz_iif != afp->at_iif_len && + !(afp->at_flags & ARPM_F_WILDIIF)))) + continue; + if (afp->at_oif_len && + (afp->at_oif_len > sz_oif || + memcmp(afp->at_oif, odev->name, afp->at_oif_len) || + (sz_oif != afp->at_oif_len && + !(afp->at_flags & ARPM_F_WILDOIF)))) + continue; + if (afp->at_flags & ARPM_F_BROADCAST && + skb->pkt_type == PACKET_HOST) + continue; + if (afp->at_flags & ARPM_F_UNICAST && + skb->pkt_type != PACKET_HOST) + continue; + if (afp->at_llsrc_len && afp->at_llsrc_len != alen) + continue; + if (afp->at_lldst_len && afp->at_lldst_len != alen) + continue; + atomic_inc(&afp->at_packets); + break; + } + read_unlock(&arpf_lock); + return afp; +} + +static void +arpf_send(int table, struct sk_buff *skb, u32 sip, u32 tip, + unsigned char *from_hw, unsigned char *to_hw, + struct net_device *idev, struct net_device *odev) +{ + struct arpf_node *afp = NULL; + + if (!arp_tabs[table] || + !(afp = arpf_lookup(table, skb, sip, tip, + from_hw, to_hw, idev, odev))) { + switch (table) { + case ARPA_TABLE_INPUT: + if (!sip) { + arp_send(ARPOP_REPLY, ETH_P_ARP, tip, idev, tip, + from_hw, idev->dev_addr, + idev->dev_addr); + break; + } + /* continue */ + case ARPA_TABLE_FORWARD: + arp_send(ARPOP_REPLY, ETH_P_ARP, sip, idev, tip, + from_hw, idev->dev_addr, from_hw); + break; + case ARPA_TABLE_OUTPUT: + arp_send(ARPOP_REQUEST, ETH_P_ARP, tip, odev, sip, + to_hw, odev->dev_addr, NULL); + break; + } + return; + } + + /* deny? */ + if (!afp->at_action) goto out; + + switch (table) { + case ARPA_TABLE_INPUT: + if (!sip) { + arp_send(ARPOP_REPLY, ETH_P_ARP, tip, idev, tip, + from_hw, + afp->at_llsrc_len?afp->at_llsrc:idev->dev_addr, + afp->at_llsrc_len?afp->at_llsrc:idev->dev_addr); + break; + } + /* continue */ + case ARPA_TABLE_FORWARD: + arp_send(ARPOP_REPLY, ETH_P_ARP, sip, idev, tip, + afp->at_lldst_len?afp->at_lldst:from_hw, + afp->at_llsrc_len?afp->at_llsrc:idev->dev_addr, + afp->at_lldst_len?afp->at_lldst:from_hw); + break; + case ARPA_TABLE_OUTPUT: + if (afp->at_flags & ARPM_F_PREFSRC && afp->at_src == 0) { + struct rtable *rt; + + if (ip_route_output(&rt, tip, 0, 0, odev->ifindex) < 0) + break; + sip = rt->rt_src; + ip_rt_put(rt); + if (!sip) + break; + } + arp_send(ARPOP_REQUEST, ETH_P_ARP, tip, odev, afp->at_src?:sip, + afp->at_lldst_len?afp->at_lldst:to_hw, + afp->at_llsrc_len?afp->at_llsrc:odev->dev_addr, + NULL); + break; + } + +out: + arpf_put(afp); +} + +static int +arpf_fill_node(struct sk_buff *skb, u32 pid, u32 seq, unsigned flags, + int event, int table, struct arpf_node *afp) +{ + struct arpmsg *am; + struct nlmsghdr *nlh; + unsigned char *b = skb->tail; + u32 packets = atomic_read(&afp->at_packets); + + nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*am)); + nlh->nlmsg_flags = flags; + am = NLMSG_DATA(nlh); + am->arpm_family = AF_UNSPEC; + am->arpm_table = table; + am->arpm_action = afp->at_action; + am->arpm_from_len = afp->at_from_len; + am->arpm_to_len = afp->at_to_len; + am->arpm_pref = afp->at_pref; + am->arpm_flags = afp->at_flags; + if (afp->at_from_len) + RTA_PUT(skb, ARPA_FROM, 4, &afp->at_from); + if (afp->at_to_len) + RTA_PUT(skb, ARPA_TO, 4, &afp->at_to); + if (afp->at_src || afp->at_flags & ARPM_F_PREFSRC) + RTA_PUT(skb, ARPA_SRC, 4, &afp->at_src); + if (afp->at_iif[0]) + RTA_PUT(skb, ARPA_IIF, sizeof(afp->at_iif), afp->at_iif); + if (afp->at_oif[0]) + RTA_PUT(skb, ARPA_OIF, sizeof(afp->at_oif), afp->at_oif); + if (afp->at_llfrom_len) + RTA_PUT(skb, ARPA_LLFROM, afp->at_llfrom_len, afp->at_llfrom); + if (afp->at_llto_len) + RTA_PUT(skb, ARPA_LLTO, afp->at_llto_len, afp->at_llto); + if (afp->at_llsrc_len) + RTA_PUT(skb, ARPA_LLSRC, afp->at_llsrc_len, afp->at_llsrc); + if (afp->at_lldst_len) + RTA_PUT(skb, ARPA_LLDST, afp->at_lldst_len, afp->at_lldst); + RTA_PUT(skb, ARPA_PACKETS, 4, &packets); + nlh->nlmsg_len = skb->tail - b; + return skb->len; + +nlmsg_failure: +rtattr_failure: + skb_trim(skb, b - skb->data); + return -1; +} + +static int +arpmsg_notify(struct sk_buff *oskb, struct nlmsghdr *n, int table, + struct arpf_node *afp, int event) +{ + struct sk_buff *skb; + u32 pid = oskb ? NETLINK_CB(oskb).pid : 0; + int size = NLMSG_SPACE(sizeof(struct arpmsg)+256); + + skb = alloc_skb(size, GFP_KERNEL); + if (!skb) + return -ENOBUFS; + + if (arpf_fill_node(skb, pid, n->nlmsg_seq, 0, event, table, afp) <= 0) { + kfree_skb(skb); + return -EINVAL; + } + + return rtnetlink_send(skb, pid, RTMGRP_ARP, n->nlmsg_flags&NLM_F_ECHO); +} + +static inline int +arpf_str_size(int a, struct rtattr **rta, int maxlen) +{ + int size = 0; + + if (rta[a-1] && (size = RTA_PAYLOAD(rta[a-1]))) { + if (size > maxlen) + size = maxlen; + } + return size; +} + +static inline int +arpf_get_str(int a, struct rtattr **rta, unsigned char *p, + int maxlen, unsigned char *l) +{ + int size = arpf_str_size(a, rta, maxlen); + + if (size) { + memcpy(p, RTA_DATA(rta[a-1]), size); + *l = size; + } + return size; +} + +#define ARPF_MATCH_U32(ind, field) ( \ + (!rta[ind-1] && r->at_ ## field == 0) || \ + (rta[ind-1] && \ + *(u32*) RTA_DATA(rta[ind-1]) == r->at_ ## field)) + +#define ARPF_MATCH_STR(ind, field) ( \ + (!rta[ind-1] && r->at_ ## field ## _len == 0) || \ + (rta[ind-1] && r->at_ ## field ## _len && \ + r->at_ ## field ## _len < RTA_PAYLOAD(rta[ind-1]) && \ + strcmp(RTA_DATA(rta[ind-1]), r->at_ ## field) == 0)) + +#define ARPF_MATCH_DATA(ind, field) ( \ + (!rta[ind-1] && r->at_ ## field ## _len == 0) || \ + (rta[ind-1] && r->at_ ## field ## _len && \ + r->at_ ## field ## _len == RTA_PAYLOAD(rta[ind-1]) && \ + memcmp(RTA_DATA(rta[ind-1]), &r->at_ ## field, \ + r->at_ ## field ## _len) == 0)) + +/* RTM_NEWARPRULE/RTM_DELARPRULE/RTM_GETARPRULE */ + +int arpf_rule_ctl(struct sk_buff *skb, struct nlmsghdr* n, void *arg) +{ + struct rtattr **rta = arg; + struct arpmsg *am = NLMSG_DATA(n); + struct arpf_node *r, **rp, **prevp = 0, **delp = 0, *newp = 0; + unsigned pref = 1; + int size, ret = -EINVAL; + + if (am->arpm_table >= sizeof(arp_tabs)/sizeof(arp_tabs[0])) + goto out; + if (!((~am->arpm_flags) & (ARPM_F_BROADCAST|ARPM_F_UNICAST))) + goto out; + if (am->arpm_action > 1) + goto out; + if (am->arpm_to_len > 32 || am->arpm_from_len > 32) + goto out; + if (am->arpm_flags & ARPM_F_WILDIIF && + (!rta[ARPA_IIF-1] || !RTA_PAYLOAD(rta[ARPA_IIF-1]) || + !*(char*)RTA_DATA(rta[ARPA_IIF-1]))) + am->arpm_flags &= ~ARPM_F_WILDIIF; + if (am->arpm_flags & ARPM_F_WILDOIF && + (!rta[ARPA_OIF-1] || !RTA_PAYLOAD(rta[ARPA_OIF-1]) || + !*(char*)RTA_DATA(rta[ARPA_OIF-1]))) + am->arpm_flags &= ~ARPM_F_WILDOIF; + switch (am->arpm_table) { + case ARPA_TABLE_INPUT: + if (rta[ARPA_SRC-1] || rta[ARPA_OIF-1]) + goto out; + break; + case ARPA_TABLE_OUTPUT: + if (rta[ARPA_IIF-1]) + goto out; + if (am->arpm_flags & (ARPM_F_BROADCAST|ARPM_F_UNICAST)) + goto out; + break; + case ARPA_TABLE_FORWARD: + if (rta[ARPA_SRC-1]) + goto out; + break; + } + if (rta[ARPA_SRC-1] && !*(u32*) RTA_DATA(rta[ARPA_SRC-1])) + am->arpm_flags |= ARPM_F_PREFSRC; + else + am->arpm_flags &= ~ARPM_F_PREFSRC; + + for (rp = &arp_tabs[am->arpm_table]; (r=*rp) != NULL; rp=&r->at_next) { + if (pref < r->at_pref) + prevp = rp; + if (am->arpm_pref == r->at_pref || + (!am->arpm_pref && + am->arpm_to_len == r->at_to_len && + am->arpm_from_len == r->at_from_len && + !((am->arpm_flags ^ r->at_flags) & + (ARPM_F_BROADCAST | ARPM_F_UNICAST | + ARPM_F_WILDIIF | ARPM_F_WILDOIF)) && + ARPF_MATCH_U32(ARPA_TO, to) && + ARPF_MATCH_U32(ARPA_FROM, from) && + ARPF_MATCH_DATA(ARPA_LLFROM, llfrom) && + ARPF_MATCH_DATA(ARPA_LLTO, llto) && + ARPF_MATCH_STR(ARPA_IIF, iif) && + ARPF_MATCH_STR(ARPA_OIF, oif) && + (n->nlmsg_type != RTM_DELARPRULE || + /* DEL matches more keys */ + (am->arpm_flags == r->at_flags && + am->arpm_action == r->at_action && + ARPF_MATCH_U32(ARPA_SRC, src) && + ARPF_MATCH_DATA(ARPA_LLSRC, llsrc) && + ARPF_MATCH_DATA(ARPA_LLDST, lldst) + ) + ) + ) + ) + break; + if (am->arpm_pref && r->at_pref > am->arpm_pref) { + r = NULL; + break; + } + pref = r->at_pref+1; + } + + /* + * r=NULL: *rp != NULL (stopped before next pref), pref: not valid + * *rp == NULL (not found), pref: ready to use + * r!=NULL: found, pref: not valid + * + * prevp=NULL: no free slot + * prevp!=NULL: free slot for rule + */ + + if (n->nlmsg_type == RTM_DELARPRULE) { + if (!r) + return -ESRCH; + delp = rp; + goto dequeue; + } + + if (r) { + /* Existing rule */ + ret = -EEXIST; + if (n->nlmsg_flags&NLM_F_EXCL) + goto out; + + if (n->nlmsg_flags&NLM_F_REPLACE) { + pref = r->at_pref; + prevp = delp = rp; + goto replace; + } + } + + if (n->nlmsg_flags&NLM_F_APPEND) { + if (r) { + pref = r->at_pref+1; + for (rp=&r->at_next; (r=*rp) != NULL; rp=&r->at_next) { + if (pref != r->at_pref) + break; + pref ++; + } + ret = -EBUSY; + if (!pref) + goto out; + } else if (am->arpm_pref) + pref = am->arpm_pref; + prevp = rp; + } + + if (!(n->nlmsg_flags&NLM_F_CREATE)) { + ret = -ENOENT; + if (n->nlmsg_flags&NLM_F_EXCL || r) + ret = 0; + goto out; + } + + if (!(n->nlmsg_flags&NLM_F_APPEND)) { + if (!prevp) { + ret = -EBUSY; + if (r || *rp || + (!am->arpm_pref && arp_tabs[am->arpm_table])) + goto out; + prevp = rp; + pref = am->arpm_pref? : 99; + } else { + if (r || !am->arpm_pref) { + pref = (*prevp)->at_pref - 1; + if (am->arpm_pref && am->arpm_pref < pref) + pref = am->arpm_pref; + } else { + prevp = rp; + pref = am->arpm_pref; + } + } + } + +replace: + + ret = -ENOMEM; + r = kmem_cache_alloc(arpf_cachep, SLAB_KERNEL); + if (!r) + return ret; + memset(r, 0, sizeof(*r)); + + arpf_get_str(ARPA_LLFROM, rta, r->at_llfrom, MAX_ADDR_LEN, + &r->at_llfrom_len); + arpf_get_str(ARPA_LLTO, rta, r->at_llto, MAX_ADDR_LEN, + &r->at_llto_len); + arpf_get_str(ARPA_LLSRC, rta, r->at_llsrc, MAX_ADDR_LEN, + &r->at_llsrc_len); + arpf_get_str(ARPA_LLDST, rta, r->at_lldst, MAX_ADDR_LEN, + &r->at_lldst_len); + + if (delp) + r->at_next = (*delp)->at_next; + else if (*prevp) + r->at_next = *prevp; + + r->at_pref = pref; + r->at_from_len = am->arpm_from_len; + r->at_from_mask = inet_make_mask(r->at_from_len); + if (rta[ARPA_FROM-1]) + r->at_from = *(u32*) RTA_DATA(rta[ARPA_FROM-1]); + r->at_from &= r->at_from_mask; + r->at_to_len = am->arpm_to_len; + r->at_to_mask = inet_make_mask(r->at_to_len); + if (rta[ARPA_TO-1]) + r->at_to = *(u32*) RTA_DATA(rta[ARPA_TO-1]); + r->at_to &= r->at_to_mask; + if (rta[ARPA_SRC-1]) + r->at_src = *(u32*) RTA_DATA(rta[ARPA_SRC-1]); + if (rta[ARPA_PACKETS-1]) { + u32 packets = *(u32*) RTA_DATA(rta[ARPA_PACKETS-1]); + atomic_set(&r->at_packets, packets); + } + atomic_set(&r->at_refcnt, 1); + r->at_flags = am->arpm_flags; + r->at_action = am->arpm_action; + + if (rta[ARPA_IIF-1] && (size = RTA_PAYLOAD(rta[ARPA_IIF-1]))) { + if (size >= sizeof(r->at_iif)) + size = sizeof(r->at_iif)-1; + memcpy(r->at_iif, RTA_DATA(rta[ARPA_IIF-1]), size); + r->at_iif_len = strlen(r->at_iif); + } + if (rta[ARPA_OIF-1] && (size = RTA_PAYLOAD(rta[ARPA_OIF-1]))) { + if (size >= sizeof(r->at_oif)) + size = sizeof(r->at_oif)-1; + memcpy(r->at_oif, RTA_DATA(rta[ARPA_OIF-1]), size); + r->at_oif_len = strlen(r->at_oif); + } + + newp = r; + +dequeue: + + if (delp) { + r = *delp; + write_lock_bh(&arpf_lock); + if (newp) { + if (!rta[ARPA_PACKETS-1]) + atomic_set(&newp->at_packets, + atomic_read(&r->at_packets)); + *delp = newp; + } else { + *delp = r->at_next; + } + r->at_dead = 1; + write_unlock_bh(&arpf_lock); + arpmsg_notify(skb, n, am->arpm_table, r, RTM_DELARPRULE); + arpf_put(r); + prevp = 0; + } + + if (newp) { + if (prevp) { + write_lock_bh(&arpf_lock); + *prevp = newp; + write_unlock_bh(&arpf_lock); + } + arpmsg_notify(skb, n, am->arpm_table, newp, RTM_NEWARPRULE); + } + + ret = 0; + +out: + return ret; +} + +int arpf_dump_table(int t, struct sk_buff *skb, struct netlink_callback *cb) +{ + int idx, ret = -1; + struct arpf_node *afp; + int s_idx = cb->args[1]; + + for (idx=0, afp = arp_tabs[t]; afp; afp = afp->at_next, idx++) { + if (idx < s_idx) + continue; + if (arpf_fill_node(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWARPRULE, t, afp) < 0) + goto out; + } + + ret = skb->len; + +out: + cb->args[1] = idx; + + return ret; +} + +int arpf_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) +{ + int idx; + int s_idx = cb->args[0]; + + read_lock_bh(&arpf_lock); + for (idx = 0; idx < sizeof(arp_tabs)/sizeof(arp_tabs[0]); idx++) { + if (idx < s_idx) + continue; + if (idx > s_idx) + memset(&cb->args[1], 0, sizeof(cb->args)-1*sizeof(cb->args[0])); + if (arpf_dump_table(idx, skb, cb) < 0) + break; + } + read_unlock_bh(&arpf_lock); + cb->args[0] = idx; + + return skb->len; +} + /* * Called once on startup. */ @@ -1351,6 +1945,20 @@ void __init arp_init (void) { + struct rtnetlink_link *link_p = rtnetlink_links[PF_UNSPEC]; + + arpf_cachep = kmem_cache_create("ip_arpf_cache", + sizeof(struct arpf_node), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!arpf_cachep) + panic("IP: failed to allocate ip_arpf_cache\n"); + + if (link_p) { + link_p[RTM_NEWARPRULE-RTM_BASE].doit = arpf_rule_ctl; + link_p[RTM_DELARPRULE-RTM_BASE].doit = arpf_rule_ctl; + link_p[RTM_GETARPRULE-RTM_BASE].dumpit = arpf_dump_rules; + } + neigh_table_init(&arp_tbl); dev_add_pack(&arp_packet_type);