--- v2.2.20/linux/include/linux/rtnetlink.h Sat Oct 21 12:11:58 2000 +++ linux/include/linux/rtnetlink.h Sat Apr 6 22:33:01 2002 @@ -49,7 +49,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 optional route information. @@ -488,6 +492,55 @@ #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 */ @@ -507,6 +560,8 @@ #define RTMGRP_IPV6_IFADDR 0x100 #define RTMGRP_IPV6_MROUTE 0x200 #define RTMGRP_IPV6_ROUTE 0x400 + +#define RTMGRP_ARP 0x00010000 /* End of information exported to user level */ --- v2.2.20/linux/net/socket.c Sat Aug 4 12:52:33 2001 +++ linux/net/socket.c Sat Apr 6 22:31:52 2002 @@ -1445,6 +1445,9 @@ #ifdef CONFIG_WAN_ROUTER extern void wanrouter_init(void); #endif +#ifdef CONFIG_RTNETLINK +extern void arpf_init(void); +#endif void __init sock_init(void) { @@ -1505,6 +1508,9 @@ #endif #ifdef CONFIG_NETLINK_DEV init_netlink(); +#endif +#ifdef CONFIG_RTNETLINK + arpf_init(); #endif } --- v2.2.20/linux/net/core/rtnetlink.c Sat Oct 21 12:11:28 2000 +++ linux/net/core/rtnetlink.c Thu Apr 4 22:27:40 2002 @@ -100,7 +100,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] = @@ -112,7 +113,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) --- v2.2.20/linux/net/ipv4/arp.c Sun Nov 4 10:16:16 2001 +++ linux/net/ipv4/arp.c Sat Apr 6 22:38:37 2002 @@ -68,6 +68,7 @@ * Krzysztof Halasa: Added Frame Relay ARP support. * Julian Anastasov: "hidden" flag: hide the * interface and don't reply for it + * Julian Anastasov: ARP filtering via netlink */ /* RFC1122 Status: @@ -100,6 +101,7 @@ #include #include #include +#include #include #ifdef CONFIG_SYSCTL #include @@ -193,6 +195,49 @@ 30*HZ, 128, 512, 1024, }; +#ifdef CONFIG_RTNETLINK + +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; + unsigned at_flags; + unsigned char at_from_len; + unsigned char at_to_len; + unsigned char at_action; + 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 char at__pad0; + 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; + +#endif /* CONFIG_RTNETLINK */ + + +static void +arpf_send(int table, struct sk_buff *skb, u32 sip, u32 tip, + unsigned char *from_hw, unsigned char *to_hw, + struct device *idev, struct device *odev); + int arp_mc_map(u32 addr, u8 *haddr, struct device *dev, int dir) { switch (dev->type) { @@ -324,6 +369,8 @@ else saddr = inet_select_addr(dev, target, RT_SCOPE_LINK); + if (!saddr) + return; if ((probes -= neigh->parms->ucast_probes) < 0) { if (!(neigh->nud_state&NUD_VALID)) printk(KERN_DEBUG "trying to ucast probe in NUD_INVALID\n"); @@ -335,8 +382,7 @@ return; } - arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr, - dst_ha, dev->dev_addr, NULL); + arpf_send(ARPA_TABLE_OUTPUT,skb,saddr,target,NULL,dst_ha,NULL,dev); } static int arp_filter(__u32 sip, __u32 tip, struct device *dev) @@ -692,7 +738,7 @@ (dev2 == dev || ((in_dev2 = dev2->ip_ptr) != NULL && !IN_DEV_HIDDEN(in_dev2)))) - 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; } @@ -722,7 +768,8 @@ 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); } goto out; @@ -737,7 +784,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); return 0; @@ -1139,6 +1188,549 @@ neigh_ifdown(&arp_tbl, dev); } +#ifdef CONFIG_RTNETLINK + +static inline void arpf_destroy(struct arpf_node *afp) +{ + kmem_cache_free(arpf_cachep, 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 device *idev, struct 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; + } + + 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; + } + return afp; +} + +#endif + +static void +arpf_send(int table, struct sk_buff *skb, u32 sip, u32 tip, + unsigned char *from_hw, unsigned char *to_hw, + struct device *idev, struct device *odev) +{ +#ifdef CONFIG_RTNETLINK + struct arpf_node *afp; + + if (!arp_tabs[table] || + !(afp = arpf_lookup(table, skb, sip, tip, + from_hw, to_hw, idev, odev))) +#endif + { + 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; + } + +#ifdef CONFIG_RTNETLINK + + /* 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: + return; +#endif /* CONFIG_RTNETLINK */ +} + +#ifdef CONFIG_RTNETLINK + +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 */ + +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); + } + 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; + start_bh_atomic(); + if (newp) { + if (!rta[ARPA_PACKETS-1]) + atomic_set(&newp->at_packets, + atomic_read(&r->at_packets)); + *delp = newp; + } else { + *delp = r->at_next; + } + end_bh_atomic(); + arpmsg_notify(skb, n, am->arpm_table, r, RTM_DELARPRULE); + arpf_destroy(r); + prevp = 0; + } + + if (newp) { + if (prevp) { + *prevp = newp; + synchronize_bh(); + } + 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]; + + 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; + } + cb->args[0] = idx; + + return skb->len; +} +#endif /* CONFIG_RTNETLINK */ + /* * Called once on startup. @@ -1160,6 +1752,26 @@ 0, &proc_net_inode_operations, arp_get_info }; +#endif + +#ifdef CONFIG_RTNETLINK + +void arpf_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; + } +} #endif __initfunc(void arp_init (void))