2006-10-04 01:01:26 +04:00
/* linux/net/ipv4/arp.c
2005-04-17 02:20:36 +04:00
*
* Copyright ( C ) 1994 by Florian La Roche
*
* This module implements the Address Resolution Protocol ARP ( RFC 826 ) ,
* which is used to convert IP addresses ( or in the future maybe other
* high - level addresses ) into a low - level hardware address ( like an Ethernet
* address ) .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
* Fixes :
2007-02-09 17:24:47 +03:00
* Alan Cox : Removed the Ethernet assumptions in
2005-04-17 02:20:36 +04:00
* Florian ' s code
2007-02-09 17:24:47 +03:00
* Alan Cox : Fixed some small errors in the ARP
2005-04-17 02:20:36 +04:00
* logic
* Alan Cox : Allow > 4 K in / proc
* Alan Cox : Make ARP add its own protocol entry
* Ross Martin : Rewrote arp_rcv ( ) and arp_get_info ( )
* Stephen Henson : Add AX25 support to arp_get_info ( )
* Alan Cox : Drop data when a device is downed .
* Alan Cox : Use init_timer ( ) .
* Alan Cox : Double lock fixes .
* Martin Seine : Move the arphdr structure
* to if_arp . h for compatibility .
* with BSD based programs .
* Andrew Tridgell : Added ARP netmask code and
* re - arranged proxy handling .
* Alan Cox : Changed to use notifiers .
* Niibe Yutaka : Reply for this device or proxies only .
* Alan Cox : Don ' t proxy across hardware types !
* Jonathan Naylor : Added support for NET / ROM .
* Mike Shaver : RFC1122 checks .
* Jonathan Naylor : Only lookup the hardware address for
* the correct hardware type .
* Germano Caronni : Assorted subtle races .
2007-02-09 17:24:47 +03:00
* Craig Schlenter : Don ' t modify permanent entry
2005-04-17 02:20:36 +04:00
* during arp_rcv .
* Russ Nelson : Tidied up a few bits .
* Alexey Kuznetsov : Major changes to caching and behaviour ,
2007-02-09 17:24:47 +03:00
* eg intelligent arp probing and
2005-04-17 02:20:36 +04:00
* generation
* of host down events .
* Alan Cox : Missing unlock in device events .
* Eckes : ARP ioctl control errors .
* Alexey Kuznetsov : Arp free fix .
* Manuel Rodriguez : Gratuitous ARP .
2007-02-09 17:24:47 +03:00
* Jonathan Layes : Added arpd support through kerneld
2005-04-17 02:20:36 +04:00
* message queue ( 960314 )
* Mike Shaver : / proc / sys / net / ipv4 / arp_ * support
* Mike McLagan : Routing by source
* Stuart Cheshire : Metricom and grat arp fixes
* * * * FOR 2.1 clean this up * * *
* Lawrence V . Stefani : ( 08 / 12 / 96 ) Added FDDI support .
* Alan Cox : Took the AP1000 nasty FDDI hack and
* folded into the mainstream FDDI code .
* Ack spit , Linus how did you allow that
* one in . . .
* Jes Sorensen : Make FDDI work again in 2.1 . x and
* clean up the APFDDI & gen . FDDI bits .
* Alexey Kuznetsov : new arp state machine ;
* now it is in net / core / neighbour . c .
* Krzysztof Halasa : Added Frame Relay ARP support .
* Arnaldo C . Melo : convert / proc / net / arp to seq_file
* Shmulik Hen : Split arp_send to arp_create and
* arp_xmit so intermediate drivers like
* bonding can change the skb before
* sending ( e . g . insert 8021 q tag ) .
* Harald Welte : convert to make use of jenkins hash
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/string.h>
# include <linux/kernel.h>
2006-01-11 23:17:47 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/socket.h>
# include <linux/sockios.h>
# include <linux/errno.h>
# include <linux/in.h>
# include <linux/mm.h>
# include <linux/inet.h>
2005-12-27 07:43:12 +03:00
# include <linux/inetdevice.h>
2005-04-17 02:20:36 +04:00
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/fddidevice.h>
# include <linux/if_arp.h>
# include <linux/trdevice.h>
# include <linux/skbuff.h>
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
# include <linux/stat.h>
# include <linux/init.h>
# include <linux/net.h>
# include <linux/rcupdate.h>
# include <linux/jhash.h>
# ifdef CONFIG_SYSCTL
# include <linux/sysctl.h>
# endif
2007-09-12 14:01:34 +04:00
# include <net/net_namespace.h>
2005-04-17 02:20:36 +04:00
# include <net/ip.h>
# include <net/icmp.h>
# include <net/route.h>
# include <net/protocol.h>
# include <net/tcp.h>
# include <net/sock.h>
# include <net/arp.h>
# include <net/ax25.h>
# include <net/netrom.h>
# if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
# include <net/atmclip.h>
struct neigh_table * clip_tbl_hook ;
# endif
# include <asm/system.h>
# include <asm/uaccess.h>
# include <linux/netfilter_arp.h>
/*
* Interface to generic neighbour cache .
*/
static u32 arp_hash ( const void * pkey , const struct net_device * dev ) ;
static int arp_constructor ( struct neighbour * neigh ) ;
static void arp_solicit ( struct neighbour * neigh , struct sk_buff * skb ) ;
static void arp_error_report ( struct neighbour * neigh , struct sk_buff * skb ) ;
static void parp_redo ( struct sk_buff * skb ) ;
static struct neigh_ops arp_generic_ops = {
. family = AF_INET ,
. solicit = arp_solicit ,
. error_report = arp_error_report ,
. output = neigh_resolve_output ,
. connected_output = neigh_connected_output ,
. hh_output = dev_queue_xmit ,
. queue_xmit = dev_queue_xmit ,
} ;
static struct neigh_ops arp_hh_ops = {
. family = AF_INET ,
. solicit = arp_solicit ,
. error_report = arp_error_report ,
. output = neigh_resolve_output ,
. connected_output = neigh_resolve_output ,
. hh_output = dev_queue_xmit ,
. queue_xmit = dev_queue_xmit ,
} ;
static struct neigh_ops arp_direct_ops = {
. family = AF_INET ,
. output = dev_queue_xmit ,
. connected_output = dev_queue_xmit ,
. hh_output = dev_queue_xmit ,
. queue_xmit = dev_queue_xmit ,
} ;
struct neigh_ops arp_broken_ops = {
. family = AF_INET ,
. solicit = arp_solicit ,
. error_report = arp_error_report ,
. output = neigh_compat_output ,
. connected_output = neigh_compat_output ,
. hh_output = dev_queue_xmit ,
. queue_xmit = dev_queue_xmit ,
} ;
struct neigh_table arp_tbl = {
. family = AF_INET ,
. entry_size = sizeof ( struct neighbour ) + 4 ,
. key_len = 4 ,
. hash = arp_hash ,
. constructor = arp_constructor ,
. proxy_redo = parp_redo ,
. id = " arp_cache " ,
. parms = {
. tbl = & arp_tbl ,
. base_reachable_time = 30 * HZ ,
. retrans_time = 1 * HZ ,
. gc_staletime = 60 * HZ ,
. reachable_time = 30 * HZ ,
. delay_probe_time = 5 * HZ ,
. queue_len = 3 ,
. ucast_probes = 3 ,
. mcast_probes = 3 ,
. anycast_delay = 1 * HZ ,
. proxy_delay = ( 8 * HZ ) / 10 ,
. proxy_qlen = 64 ,
. locktime = 1 * HZ ,
} ,
. gc_interval = 30 * HZ ,
. gc_thresh1 = 128 ,
. gc_thresh2 = 512 ,
. gc_thresh3 = 1024 ,
} ;
2006-11-15 07:51:49 +03:00
int arp_mc_map ( __be32 addr , u8 * haddr , struct net_device * dev , int dir )
2005-04-17 02:20:36 +04:00
{
switch ( dev - > type ) {
case ARPHRD_ETHER :
case ARPHRD_FDDI :
case ARPHRD_IEEE802 :
ip_eth_mc_map ( addr , haddr ) ;
2007-02-09 17:24:47 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
case ARPHRD_IEEE802_TR :
ip_tr_mc_map ( addr , haddr ) ;
return 0 ;
case ARPHRD_INFINIBAND :
2007-12-10 23:38:41 +03:00
ip_ib_mc_map ( addr , dev - > broadcast , haddr ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
default :
if ( dir ) {
memcpy ( haddr , dev - > broadcast , dev - > addr_len ) ;
return 0 ;
}
}
return - EINVAL ;
}
static u32 arp_hash ( const void * pkey , const struct net_device * dev )
{
return jhash_2words ( * ( u32 * ) pkey , dev - > ifindex , arp_tbl . hash_rnd ) ;
}
static int arp_constructor ( struct neighbour * neigh )
{
2006-09-27 09:17:51 +04:00
__be32 addr = * ( __be32 * ) neigh - > primary_key ;
2005-04-17 02:20:36 +04:00
struct net_device * dev = neigh - > dev ;
struct in_device * in_dev ;
struct neigh_parms * parms ;
rcu_read_lock ( ) ;
2005-10-04 01:35:55 +04:00
in_dev = __in_dev_get_rcu ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( in_dev = = NULL ) {
rcu_read_unlock ( ) ;
return - EINVAL ;
}
2008-03-25 15:47:49 +03:00
neigh - > type = inet_addr_type ( dev_net ( dev ) , addr ) ;
2008-01-15 09:56:01 +03:00
2005-04-17 02:20:36 +04:00
parms = in_dev - > arp_parms ;
__neigh_parms_put ( neigh - > parms ) ;
neigh - > parms = neigh_parms_clone ( parms ) ;
rcu_read_unlock ( ) ;
2007-10-09 12:40:57 +04:00
if ( ! dev - > header_ops ) {
2005-04-17 02:20:36 +04:00
neigh - > nud_state = NUD_NOARP ;
neigh - > ops = & arp_direct_ops ;
neigh - > output = neigh - > ops - > queue_xmit ;
} else {
/* Good devices (checked by reading texts, but only Ethernet is
tested )
ARPHRD_ETHER : ( ethernet , apfddi )
ARPHRD_FDDI : ( fddi )
ARPHRD_IEEE802 : ( tr )
ARPHRD_METRICOM : ( strip )
ARPHRD_ARCNET :
etc . etc . etc .
ARPHRD_IPDDP will also work , if author repairs it .
I did not it , because this driver does not work even
in old paradigm .
*/
# if 1
/* So... these "amateur" devices are hopeless.
The only thing , that I can say now :
It is very sad that we need to keep ugly obsolete
code to make them happy .
They should be moved to more reasonable state , now
they use rebuild_header INSTEAD OF hard_start_xmit ! ! !
Besides that , they are sort of out of date
( a lot of redundant clones / copies , useless in 2.1 ) ,
I wonder why people believe that they work .
*/
switch ( dev - > type ) {
default :
break ;
2007-02-09 17:24:47 +03:00
case ARPHRD_ROSE :
2005-04-17 02:20:36 +04:00
# if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
case ARPHRD_AX25 :
# if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
case ARPHRD_NETROM :
# endif
neigh - > ops = & arp_broken_ops ;
neigh - > output = neigh - > ops - > output ;
return 0 ;
# endif
; }
# endif
if ( neigh - > type = = RTN_MULTICAST ) {
neigh - > nud_state = NUD_NOARP ;
arp_mc_map ( addr , neigh - > ha , dev , 1 ) ;
} else if ( dev - > flags & ( IFF_NOARP | IFF_LOOPBACK ) ) {
neigh - > nud_state = NUD_NOARP ;
memcpy ( neigh - > ha , dev - > dev_addr , dev - > addr_len ) ;
} else if ( neigh - > type = = RTN_BROADCAST | | dev - > flags & IFF_POINTOPOINT ) {
neigh - > nud_state = NUD_NOARP ;
memcpy ( neigh - > ha , dev - > broadcast , dev - > addr_len ) ;
}
2007-10-09 12:40:57 +04:00
if ( dev - > header_ops - > cache )
2005-04-17 02:20:36 +04:00
neigh - > ops = & arp_hh_ops ;
else
neigh - > ops = & arp_generic_ops ;
2007-10-09 12:40:57 +04:00
2005-04-17 02:20:36 +04:00
if ( neigh - > nud_state & NUD_VALID )
neigh - > output = neigh - > ops - > connected_output ;
else
neigh - > output = neigh - > ops - > output ;
}
return 0 ;
}
static void arp_error_report ( struct neighbour * neigh , struct sk_buff * skb )
{
dst_link_failure ( skb ) ;
kfree_skb ( skb ) ;
}
static void arp_solicit ( struct neighbour * neigh , struct sk_buff * skb )
{
2006-09-27 08:27:54 +04:00
__be32 saddr = 0 ;
2005-04-17 02:20:36 +04:00
u8 * dst_ha = NULL ;
struct net_device * dev = neigh - > dev ;
2006-09-27 08:27:54 +04:00
__be32 target = * ( __be32 * ) neigh - > primary_key ;
2005-04-17 02:20:36 +04:00
int probes = atomic_read ( & neigh - > probes ) ;
struct in_device * in_dev = in_dev_get ( dev ) ;
if ( ! in_dev )
return ;
switch ( IN_DEV_ARP_ANNOUNCE ( in_dev ) ) {
default :
case 0 : /* By default announce any local IP */
2008-03-25 15:47:49 +03:00
if ( skb & & inet_addr_type ( dev_net ( dev ) , ip_hdr ( skb ) - > saddr ) = = RTN_LOCAL )
2007-04-21 09:47:35 +04:00
saddr = ip_hdr ( skb ) - > saddr ;
2005-04-17 02:20:36 +04:00
break ;
case 1 : /* Restrict announcements of saddr in same subnet */
if ( ! skb )
break ;
2007-04-21 09:47:35 +04:00
saddr = ip_hdr ( skb ) - > saddr ;
2008-03-25 15:47:49 +03:00
if ( inet_addr_type ( dev_net ( dev ) , saddr ) = = RTN_LOCAL ) {
2005-04-17 02:20:36 +04:00
/* saddr should be known to target */
if ( inet_addr_onlink ( in_dev , target , saddr ) )
break ;
}
saddr = 0 ;
break ;
case 2 : /* Avoid secondary IPs, get a primary/preferred one */
break ;
}
if ( in_dev )
in_dev_put ( in_dev ) ;
if ( ! saddr )
saddr = inet_select_addr ( dev , target , RT_SCOPE_LINK ) ;
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 ;
2008-02-18 05:39:54 +03:00
read_lock_bh ( & neigh - > lock ) ;
2005-04-17 02:20:36 +04:00
} else if ( ( probes - = neigh - > parms - > app_probes ) < 0 ) {
# ifdef CONFIG_ARPD
neigh_app_ns ( neigh ) ;
# endif
return ;
}
arp_send ( ARPOP_REQUEST , ETH_P_ARP , target , dev , saddr ,
dst_ha , dev - > dev_addr , NULL ) ;
2008-02-18 05:39:54 +03:00
if ( dst_ha )
read_unlock_bh ( & neigh - > lock ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-15 10:05:55 +03:00
static int arp_ignore ( struct in_device * in_dev , __be32 sip , __be32 tip )
2005-04-17 02:20:36 +04:00
{
int scope ;
switch ( IN_DEV_ARP_IGNORE ( in_dev ) ) {
case 0 : /* Reply, the tip is already validated */
return 0 ;
case 1 : /* Reply only if tip is configured on the incoming interface */
sip = 0 ;
scope = RT_SCOPE_HOST ;
break ;
case 2 : /*
* Reply only if tip is configured on the incoming interface
* and is in same subnet as sip
*/
scope = RT_SCOPE_HOST ;
break ;
case 3 : /* Do not reply for scope host addresses */
sip = 0 ;
scope = RT_SCOPE_LINK ;
break ;
case 4 : /* Reserved */
case 5 :
case 6 :
case 7 :
return 0 ;
case 8 : /* Do not reply */
return 1 ;
default :
return 0 ;
}
2008-01-15 10:05:55 +03:00
return ! inet_confirm_addr ( in_dev , sip , tip , scope ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-28 05:36:36 +04:00
static int arp_filter ( __be32 sip , __be32 tip , struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
struct flowi fl = { . nl_u = { . ip4_u = { . daddr = sip ,
. saddr = tip } } } ;
struct rtable * rt ;
2007-02-09 17:24:47 +03:00
int flag = 0 ;
2005-04-17 02:20:36 +04:00
/*unsigned long now; */
2008-07-17 07:28:42 +04:00
struct net * net = dev_net ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-07-17 07:28:42 +04:00
if ( ip_route_output_key ( net , & rt , & fl ) < 0 )
2005-04-17 02:20:36 +04:00
return 1 ;
2007-02-09 17:24:47 +03:00
if ( rt - > u . dst . dev ! = dev ) {
2008-07-17 07:31:16 +04:00
NET_INC_STATS_BH ( net , LINUX_MIB_ARPFILTER ) ;
2005-04-17 02:20:36 +04:00
flag = 1 ;
2007-02-09 17:24:47 +03:00
}
ip_rt_put ( rt ) ;
return flag ;
}
2005-04-17 02:20:36 +04:00
/* OBSOLETE FUNCTIONS */
/*
* Find an arp mapping in the cache . If not found , post a request .
*
* It is very UGLY routine : it DOES NOT use skb - > dst - > neighbour ,
* even if it exists . It is supposed that skb - > dev was mangled
* by a virtual device ( eql , shaper ) . Nobody but broken devices
* is allowed to use this function , it is scheduled to be removed . - - ANK
*/
2006-09-28 05:36:36 +04:00
static int arp_set_predefined ( int addr_hint , unsigned char * haddr , __be32 paddr , struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
switch ( addr_hint ) {
case RTN_LOCAL :
printk ( KERN_DEBUG " ARP: arp called for own IP address \n " ) ;
memcpy ( haddr , dev - > dev_addr , dev - > addr_len ) ;
return 1 ;
case RTN_MULTICAST :
arp_mc_map ( paddr , haddr , dev , 1 ) ;
return 1 ;
case RTN_BROADCAST :
memcpy ( haddr , dev - > broadcast , dev - > addr_len ) ;
return 1 ;
}
return 0 ;
}
int arp_find ( unsigned char * haddr , struct sk_buff * skb )
{
struct net_device * dev = skb - > dev ;
2006-09-27 09:17:51 +04:00
__be32 paddr ;
2005-04-17 02:20:36 +04:00
struct neighbour * n ;
if ( ! skb - > dst ) {
printk ( KERN_DEBUG " arp_find is called with dst==NULL \n " ) ;
kfree_skb ( skb ) ;
return 1 ;
}
2008-03-06 05:30:47 +03:00
paddr = skb - > rtable - > rt_gateway ;
2005-04-17 02:20:36 +04:00
2008-03-25 15:47:49 +03:00
if ( arp_set_predefined ( inet_addr_type ( dev_net ( dev ) , paddr ) , haddr , paddr , dev ) )
2005-04-17 02:20:36 +04:00
return 0 ;
n = __neigh_lookup ( & arp_tbl , & paddr , dev , 1 ) ;
if ( n ) {
n - > used = jiffies ;
if ( n - > nud_state & NUD_VALID | | neigh_event_send ( n , skb ) = = 0 ) {
read_lock_bh ( & n - > lock ) ;
2007-02-09 17:24:47 +03:00
memcpy ( haddr , n - > ha , dev - > addr_len ) ;
2005-04-17 02:20:36 +04:00
read_unlock_bh ( & n - > lock ) ;
neigh_release ( n ) ;
return 0 ;
}
neigh_release ( n ) ;
} else
kfree_skb ( skb ) ;
return 1 ;
}
/* END OF OBSOLETE FUNCTIONS */
int arp_bind_neighbour ( struct dst_entry * dst )
{
struct net_device * dev = dst - > dev ;
struct neighbour * n = dst - > neighbour ;
if ( dev = = NULL )
return - EINVAL ;
if ( n = = NULL ) {
2008-11-03 13:48:14 +03:00
__be32 nexthop = ( ( struct rtable * ) dst ) - > rt_gateway ;
2005-04-17 02:20:36 +04:00
if ( dev - > flags & ( IFF_LOOPBACK | IFF_POINTOPOINT ) )
nexthop = 0 ;
n = __neigh_lookup_errno (
# if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
dev - > type = = ARPHRD_ATM ? clip_tbl_hook :
# endif
& arp_tbl , & nexthop , dev ) ;
if ( IS_ERR ( n ) )
return PTR_ERR ( n ) ;
dst - > neighbour = n ;
}
return 0 ;
}
/*
* Check if we can use proxy ARP for this path
*/
static inline int arp_fwd_proxy ( struct in_device * in_dev , struct rtable * rt )
{
struct in_device * out_dev ;
int imi , omi = - 1 ;
if ( ! IN_DEV_PROXY_ARP ( in_dev ) )
return 0 ;
if ( ( imi = IN_DEV_MEDIUM_ID ( in_dev ) ) = = 0 )
return 1 ;
if ( imi = = - 1 )
return 0 ;
/* place to check for proxy_arp for routes */
if ( ( out_dev = in_dev_get ( rt - > u . dst . dev ) ) ! = NULL ) {
omi = IN_DEV_MEDIUM_ID ( out_dev ) ;
in_dev_put ( out_dev ) ;
}
return ( omi ! = imi & & omi ! = - 1 ) ;
}
/*
* Interface to link layer : send routine and receive handler .
*/
/*
* Create an arp packet . If ( dest_hw = = NULL ) , we create a broadcast
* message .
*/
2006-09-28 05:36:36 +04:00
struct sk_buff * arp_create ( int type , int ptype , __be32 dest_ip ,
struct net_device * dev , __be32 src_ip ,
2008-01-31 14:59:24 +03:00
const unsigned char * dest_hw ,
const unsigned char * src_hw ,
const unsigned char * target_hw )
2005-04-17 02:20:36 +04:00
{
struct sk_buff * skb ;
struct arphdr * arp ;
unsigned char * arp_ptr ;
/*
* Allocate a buffer
*/
2007-02-09 17:24:47 +03:00
2008-05-13 07:48:31 +04:00
skb = alloc_skb ( arp_hdr_len ( dev ) + LL_ALLOCATED_SPACE ( dev ) , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( skb = = NULL )
return NULL ;
skb_reserve ( skb , LL_RESERVED_SPACE ( dev ) ) ;
2007-04-11 07:45:18 +04:00
skb_reset_network_header ( skb ) ;
2008-03-03 23:20:57 +03:00
arp = ( struct arphdr * ) skb_put ( skb , arp_hdr_len ( dev ) ) ;
2005-04-17 02:20:36 +04:00
skb - > dev = dev ;
skb - > protocol = htons ( ETH_P_ARP ) ;
if ( src_hw = = NULL )
src_hw = dev - > dev_addr ;
if ( dest_hw = = NULL )
dest_hw = dev - > broadcast ;
/*
* Fill the device header for the ARP frame
*/
2007-10-09 12:36:32 +04:00
if ( dev_hard_header ( skb , dev , ptype , dest_hw , src_hw , skb - > len ) < 0 )
2005-04-17 02:20:36 +04:00
goto out ;
/*
* Fill out the arp protocol part .
*
* The arp hardware type should match the device type , except for FDDI ,
* which ( according to RFC 1390 ) should always equal 1 ( Ethernet ) .
*/
/*
* Exceptions everywhere . AX .25 uses the AX .25 PID value not the
* DIX code for the protocol . Make these device structure fields .
*/
switch ( dev - > type ) {
default :
arp - > ar_hrd = htons ( dev - > type ) ;
arp - > ar_pro = htons ( ETH_P_IP ) ;
break ;
# if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
case ARPHRD_AX25 :
arp - > ar_hrd = htons ( ARPHRD_AX25 ) ;
arp - > ar_pro = htons ( AX25_P_IP ) ;
break ;
# if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
case ARPHRD_NETROM :
arp - > ar_hrd = htons ( ARPHRD_NETROM ) ;
arp - > ar_pro = htons ( AX25_P_IP ) ;
break ;
# endif
# endif
# ifdef CONFIG_FDDI
case ARPHRD_FDDI :
arp - > ar_hrd = htons ( ARPHRD_ETHER ) ;
arp - > ar_pro = htons ( ETH_P_IP ) ;
break ;
# endif
# ifdef CONFIG_TR
case ARPHRD_IEEE802_TR :
arp - > ar_hrd = htons ( ARPHRD_IEEE802 ) ;
arp - > ar_pro = htons ( ETH_P_IP ) ;
break ;
# endif
}
arp - > ar_hln = dev - > addr_len ;
arp - > ar_pln = 4 ;
arp - > ar_op = htons ( type ) ;
arp_ptr = ( unsigned char * ) ( arp + 1 ) ;
memcpy ( arp_ptr , src_hw , dev - > addr_len ) ;
2008-11-03 13:48:14 +03:00
arp_ptr + = dev - > addr_len ;
memcpy ( arp_ptr , & src_ip , 4 ) ;
arp_ptr + = 4 ;
2005-04-17 02:20:36 +04:00
if ( target_hw ! = NULL )
memcpy ( arp_ptr , target_hw , dev - > addr_len ) ;
else
memset ( arp_ptr , 0 , dev - > addr_len ) ;
2008-11-03 13:48:14 +03:00
arp_ptr + = dev - > addr_len ;
2005-04-17 02:20:36 +04:00
memcpy ( arp_ptr , & dest_ip , 4 ) ;
return skb ;
out :
kfree_skb ( skb ) ;
return NULL ;
}
/*
* Send an arp packet .
*/
void arp_xmit ( struct sk_buff * skb )
{
/* Send it off, maybe filter it using firewalling first. */
2008-10-20 14:34:51 +04:00
NF_HOOK ( NFPROTO_ARP , NF_ARP_OUT , skb , NULL , skb - > dev , dev_queue_xmit ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Create and send an arp packet .
*/
2006-09-28 05:36:36 +04:00
void arp_send ( int type , int ptype , __be32 dest_ip ,
struct net_device * dev , __be32 src_ip ,
2008-01-31 14:59:24 +03:00
const unsigned char * dest_hw , const unsigned char * src_hw ,
const unsigned char * target_hw )
2005-04-17 02:20:36 +04:00
{
struct sk_buff * skb ;
/*
* No arp on this interface .
*/
2007-02-09 17:24:47 +03:00
2005-04-17 02:20:36 +04:00
if ( dev - > flags & IFF_NOARP )
return ;
skb = arp_create ( type , ptype , dest_ip , dev , src_ip ,
dest_hw , src_hw , target_hw ) ;
if ( skb = = NULL ) {
return ;
}
arp_xmit ( skb ) ;
}
/*
* Process an arp request .
*/
static int arp_process ( struct sk_buff * skb )
{
struct net_device * dev = skb - > dev ;
struct in_device * in_dev = in_dev_get ( dev ) ;
struct arphdr * arp ;
unsigned char * arp_ptr ;
struct rtable * rt ;
2007-12-20 10:38:11 +03:00
unsigned char * sha ;
2006-09-27 08:25:20 +04:00
__be32 sip , tip ;
2005-04-17 02:20:36 +04:00
u16 dev_type = dev - > type ;
int addr_type ;
struct neighbour * n ;
2008-03-25 15:47:49 +03:00
struct net * net = dev_net ( dev ) ;
2005-04-17 02:20:36 +04:00
/* arp_rcv below verifies the ARP header and verifies the device
* is ARP ' able .
*/
if ( in_dev = = NULL )
goto out ;
2007-03-13 02:56:31 +03:00
arp = arp_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
switch ( dev_type ) {
2007-02-09 17:24:47 +03:00
default :
2005-04-17 02:20:36 +04:00
if ( arp - > ar_pro ! = htons ( ETH_P_IP ) | |
htons ( dev_type ) ! = arp - > ar_hrd )
goto out ;
break ;
case ARPHRD_ETHER :
case ARPHRD_IEEE802_TR :
case ARPHRD_FDDI :
case ARPHRD_IEEE802 :
/*
* ETHERNET , Token Ring and Fibre Channel ( which are IEEE 802
* devices , according to RFC 2625 ) devices will accept ARP
* hardware types of either 1 ( Ethernet ) or 6 ( IEEE 802.2 ) .
* This is the case also of FDDI , where the RFC 1390 says that
* FDDI devices should accept ARP hardware of ( 1 ) Ethernet ,
* however , to be more robust , we ' ll accept both 1 ( Ethernet )
* or 6 ( IEEE 802.2 )
*/
if ( ( arp - > ar_hrd ! = htons ( ARPHRD_ETHER ) & &
arp - > ar_hrd ! = htons ( ARPHRD_IEEE802 ) ) | |
arp - > ar_pro ! = htons ( ETH_P_IP ) )
goto out ;
break ;
case ARPHRD_AX25 :
if ( arp - > ar_pro ! = htons ( AX25_P_IP ) | |
arp - > ar_hrd ! = htons ( ARPHRD_AX25 ) )
goto out ;
break ;
case ARPHRD_NETROM :
if ( arp - > ar_pro ! = htons ( AX25_P_IP ) | |
arp - > ar_hrd ! = htons ( ARPHRD_NETROM ) )
goto out ;
break ;
}
/* Understand only these message types */
if ( arp - > ar_op ! = htons ( ARPOP_REPLY ) & &
arp - > ar_op ! = htons ( ARPOP_REQUEST ) )
goto out ;
/*
* Extract fields
*/
arp_ptr = ( unsigned char * ) ( arp + 1 ) ;
sha = arp_ptr ;
arp_ptr + = dev - > addr_len ;
memcpy ( & sip , arp_ptr , 4 ) ;
arp_ptr + = 4 ;
arp_ptr + = dev - > addr_len ;
memcpy ( & tip , arp_ptr , 4 ) ;
2007-02-09 17:24:47 +03:00
/*
2005-04-17 02:20:36 +04:00
* Check for bad requests for 127. x . x . x and requests for multicast
* addresses . If this is one such , delete it .
*/
2007-12-17 00:45:43 +03:00
if ( ipv4_is_loopback ( tip ) | | ipv4_is_multicast ( tip ) )
2005-04-17 02:20:36 +04:00
goto out ;
/*
* Special case : We must set Frame Relay source Q .922 address
*/
if ( dev_type = = ARPHRD_DLCI )
sha = dev - > broadcast ;
/*
* Process entry . The idea here is we want to send a reply if it is a
* request for us or if it is a request for someone else that we hold
* a proxy for . We want to add an entry to our cache if it is a reply
2007-02-09 17:24:47 +03:00
* to us or if it is a request for our address .
* ( The assumption for this last is that if someone is requesting our
* address , they are probably intending to talk to us , so it saves time
* if we cache their address . Their address is also probably not in
2005-04-17 02:20:36 +04:00
* our cache , since ours is not in their cache . )
2007-02-09 17:24:47 +03:00
*
2005-04-17 02:20:36 +04:00
* Putting this another way , we only care about replies if they are to
* us , in which case we add them to the cache . For requests , we care
* about those for us and those for our proxies . We reply to both ,
2007-02-09 17:24:47 +03:00
* and in the case of requests for us we add the requester to the arp
2005-04-17 02:20:36 +04:00
* cache .
*/
/* Special case: IPv4 duplicate address detection packet (RFC2131) */
if ( sip = = 0 ) {
if ( arp - > ar_op = = htons ( ARPOP_REQUEST ) & &
2008-03-25 01:28:12 +03:00
inet_addr_type ( net , tip ) = = RTN_LOCAL & &
2008-01-15 10:05:55 +03:00
! arp_ignore ( in_dev , sip , tip ) )
2007-11-21 04:38:16 +03:00
arp_send ( ARPOP_REPLY , ETH_P_ARP , sip , dev , tip , sha ,
dev - > dev_addr , sha ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
if ( arp - > ar_op = = htons ( ARPOP_REQUEST ) & &
ip_route_input ( skb , tip , sip , 0 , dev ) = = 0 ) {
2008-03-06 05:30:47 +03:00
rt = skb - > rtable ;
2005-04-17 02:20:36 +04:00
addr_type = rt - > rt_type ;
if ( addr_type = = RTN_LOCAL ) {
2008-11-17 06:19:38 +03:00
int dont_send = 0 ;
if ( ! dont_send )
dont_send | = arp_ignore ( in_dev , sip , tip ) ;
if ( ! dont_send & & IN_DEV_ARPFILTER ( in_dev ) )
dont_send | = arp_filter ( sip , tip , dev ) ;
if ( ! dont_send ) {
n = neigh_event_ns ( & arp_tbl , sha , & sip , dev ) ;
if ( n ) {
2005-04-17 02:20:36 +04:00
arp_send ( ARPOP_REPLY , ETH_P_ARP , sip , dev , tip , sha , dev - > dev_addr , sha ) ;
2008-11-17 06:19:38 +03:00
neigh_release ( n ) ;
}
2005-04-17 02:20:36 +04:00
}
goto out ;
} else if ( IN_DEV_FORWARD ( in_dev ) ) {
2008-01-09 11:18:24 +03:00
if ( addr_type = = RTN_UNICAST & & rt - > u . dst . dev ! = dev & &
2008-03-25 01:28:12 +03:00
( arp_fwd_proxy ( in_dev , rt ) | | pneigh_lookup ( & arp_tbl , net , & tip , dev , 0 ) ) ) {
2005-04-17 02:20:36 +04:00
n = neigh_event_ns ( & arp_tbl , sha , & sip , dev ) ;
if ( n )
neigh_release ( n ) ;
2007-02-09 17:24:47 +03:00
if ( NEIGH_CB ( skb ) - > flags & LOCALLY_ENQUEUED | |
2005-04-17 02:20:36 +04:00
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 ) ;
} else {
pneigh_enqueue ( & arp_tbl , in_dev - > arp_parms , skb ) ;
in_dev_put ( in_dev ) ;
return 0 ;
}
goto out ;
}
}
}
/* Update our ARP tables */
n = __neigh_lookup ( & arp_tbl , & sip , dev , 0 ) ;
2008-03-25 15:47:49 +03:00
if ( IPV4_DEVCONF_ALL ( dev_net ( dev ) , ARP_ACCEPT ) ) {
2006-03-21 09:39:47 +03:00
/* Unsolicited ARP is not accepted by default.
It is possible , that this option should be enabled for some
devices ( strip is candidate )
*/
if ( n = = NULL & &
arp - > ar_op = = htons ( ARPOP_REPLY ) & &
2008-03-25 01:28:12 +03:00
inet_addr_type ( net , sip ) = = RTN_UNICAST )
2007-07-15 07:51:44 +04:00
n = __neigh_lookup ( & arp_tbl , & sip , dev , 1 ) ;
2006-03-21 09:39:47 +03:00
}
2005-04-17 02:20:36 +04:00
if ( n ) {
int state = NUD_REACHABLE ;
int override ;
/* If several different ARP replies follows back-to-back,
use the FIRST one . It is possible , if several proxy
agents are active . Taking the first reply prevents
arp trashing and chooses the fastest router .
*/
override = time_after ( jiffies , n - > updated + n - > parms - > locktime ) ;
/* Broadcast replies and request packets
do not assert neighbour reachability .
*/
if ( arp - > ar_op ! = htons ( ARPOP_REPLY ) | |
skb - > pkt_type ! = PACKET_HOST )
state = NUD_STALE ;
neigh_update ( n , sha , state , override ? NEIGH_UPDATE_F_OVERRIDE : 0 ) ;
neigh_release ( n ) ;
}
out :
if ( in_dev )
in_dev_put ( in_dev ) ;
kfree_skb ( skb ) ;
return 0 ;
}
2005-10-04 01:18:10 +04:00
static void parp_redo ( struct sk_buff * skb )
{
arp_process ( skb ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Receive an arp request from the device layer .
*/
2006-04-13 00:57:59 +04:00
static int arp_rcv ( struct sk_buff * skb , struct net_device * dev ,
struct packet_type * pt , struct net_device * orig_dev )
2005-04-17 02:20:36 +04:00
{
struct arphdr * arp ;
/* ARP header, plus 2 device addresses, plus 2 IP addresses. */
2008-03-03 23:20:57 +03:00
if ( ! pskb_may_pull ( skb , arp_hdr_len ( dev ) ) )
2005-04-17 02:20:36 +04:00
goto freeskb ;
2007-03-13 02:56:31 +03:00
arp = arp_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
if ( arp - > ar_hln ! = dev - > addr_len | |
dev - > flags & IFF_NOARP | |
skb - > pkt_type = = PACKET_OTHERHOST | |
skb - > pkt_type = = PACKET_LOOPBACK | |
arp - > ar_pln ! = 4 )
goto freeskb ;
if ( ( skb = skb_share_check ( skb , GFP_ATOMIC ) ) = = NULL )
goto out_of_mem ;
2005-08-15 04:24:31 +04:00
memset ( NEIGH_CB ( skb ) , 0 , sizeof ( struct neighbour_cb ) ) ;
2008-10-20 14:34:51 +04:00
return NF_HOOK ( NFPROTO_ARP , NF_ARP_IN , skb , dev , NULL , arp_process ) ;
2005-04-17 02:20:36 +04:00
freeskb :
kfree_skb ( skb ) ;
out_of_mem :
return 0 ;
}
/*
* User level interface ( ioctl )
*/
/*
* Set ( create ) an ARP cache entry .
*/
2007-12-17 00:30:39 +03:00
static int arp_req_set_proxy ( struct net * net , struct net_device * dev , int on )
2007-12-06 08:20:50 +03:00
{
if ( dev = = NULL ) {
2007-12-17 00:32:48 +03:00
IPV4_DEVCONF_ALL ( net , PROXY_ARP ) = on ;
2007-12-06 08:20:50 +03:00
return 0 ;
}
if ( __in_dev_get_rtnl ( dev ) ) {
IN_DEV_CONF_SET ( __in_dev_get_rtnl ( dev ) , PROXY_ARP , on ) ;
return 0 ;
}
return - ENXIO ;
}
2007-12-17 00:30:39 +03:00
static int arp_req_set_public ( struct net * net , struct arpreq * r ,
struct net_device * dev )
2007-12-06 08:19:44 +03:00
{
__be32 ip = ( ( struct sockaddr_in * ) & r - > arp_pa ) - > sin_addr . s_addr ;
__be32 mask = ( ( struct sockaddr_in * ) & r - > arp_netmask ) - > sin_addr . s_addr ;
if ( mask & & mask ! = htonl ( 0xFFFFFFFF ) )
return - EINVAL ;
if ( ! dev & & ( r - > arp_flags & ATF_COM ) ) {
2008-01-15 09:58:55 +03:00
dev = dev_getbyhwaddr ( net , r - > arp_ha . sa_family ,
2007-12-06 08:19:44 +03:00
r - > arp_ha . sa_data ) ;
if ( ! dev )
return - ENODEV ;
}
if ( mask ) {
2008-01-15 09:58:55 +03:00
if ( pneigh_lookup ( & arp_tbl , net , & ip , dev , 1 ) = = NULL )
2007-12-06 08:19:44 +03:00
return - ENOBUFS ;
return 0 ;
}
2007-12-06 08:20:50 +03:00
2007-12-17 00:30:39 +03:00
return arp_req_set_proxy ( net , dev , 1 ) ;
2007-12-06 08:19:44 +03:00
}
2007-12-17 00:30:39 +03:00
static int arp_req_set ( struct net * net , struct arpreq * r ,
struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2007-12-06 08:19:44 +03:00
__be32 ip ;
2005-04-17 02:20:36 +04:00
struct neighbour * neigh ;
int err ;
2007-12-06 08:19:44 +03:00
if ( r - > arp_flags & ATF_PUBL )
2007-12-17 00:30:39 +03:00
return arp_req_set_public ( net , r , dev ) ;
2005-04-17 02:20:36 +04:00
2007-12-06 08:19:44 +03:00
ip = ( ( struct sockaddr_in * ) & r - > arp_pa ) - > sin_addr . s_addr ;
2005-04-17 02:20:36 +04:00
if ( r - > arp_flags & ATF_PERM )
r - > arp_flags | = ATF_COM ;
if ( dev = = NULL ) {
struct flowi fl = { . nl_u = { . ip4_u = { . daddr = ip ,
. tos = RTO_ONLINK } } } ;
struct rtable * rt ;
2008-01-23 09:07:34 +03:00
if ( ( err = ip_route_output_key ( net , & rt , & fl ) ) ! = 0 )
2005-04-17 02:20:36 +04:00
return err ;
dev = rt - > u . dst . dev ;
ip_rt_put ( rt ) ;
if ( ! dev )
return - EINVAL ;
}
switch ( dev - > type ) {
# ifdef CONFIG_FDDI
case ARPHRD_FDDI :
/*
* According to RFC 1390 , FDDI devices should accept ARP
* hardware types of 1 ( Ethernet ) . However , to be more
* robust , we ' ll accept hardware types of either 1 ( Ethernet )
* or 6 ( IEEE 802.2 ) .
*/
if ( r - > arp_ha . sa_family ! = ARPHRD_FDDI & &
r - > arp_ha . sa_family ! = ARPHRD_ETHER & &
r - > arp_ha . sa_family ! = ARPHRD_IEEE802 )
return - EINVAL ;
break ;
# endif
default :
if ( r - > arp_ha . sa_family ! = dev - > type )
return - EINVAL ;
break ;
}
neigh = __neigh_lookup_errno ( & arp_tbl , & ip , dev ) ;
err = PTR_ERR ( neigh ) ;
if ( ! IS_ERR ( neigh ) ) {
unsigned state = NUD_STALE ;
if ( r - > arp_flags & ATF_PERM )
state = NUD_PERMANENT ;
err = neigh_update ( neigh , ( r - > arp_flags & ATF_COM ) ?
2007-02-09 17:24:47 +03:00
r - > arp_ha . sa_data : NULL , state ,
2005-04-17 02:20:36 +04:00
NEIGH_UPDATE_F_OVERRIDE |
NEIGH_UPDATE_F_ADMIN ) ;
neigh_release ( neigh ) ;
}
return err ;
}
static unsigned arp_state_to_flags ( struct neighbour * neigh )
{
unsigned flags = 0 ;
if ( neigh - > nud_state & NUD_PERMANENT )
flags = ATF_PERM | ATF_COM ;
else if ( neigh - > nud_state & NUD_VALID )
flags = ATF_COM ;
return flags ;
}
/*
* Get an ARP cache entry .
*/
static int arp_req_get ( struct arpreq * r , struct net_device * dev )
{
2006-09-28 05:36:36 +04:00
__be32 ip = ( ( struct sockaddr_in * ) & r - > arp_pa ) - > sin_addr . s_addr ;
2005-04-17 02:20:36 +04:00
struct neighbour * neigh ;
int err = - ENXIO ;
neigh = neigh_lookup ( & arp_tbl , & ip , dev ) ;
if ( neigh ) {
read_lock_bh ( & neigh - > lock ) ;
memcpy ( r - > arp_ha . sa_data , neigh - > ha , dev - > addr_len ) ;
r - > arp_flags = arp_state_to_flags ( neigh ) ;
read_unlock_bh ( & neigh - > lock ) ;
r - > arp_ha . sa_family = dev - > type ;
strlcpy ( r - > arp_dev , dev - > name , sizeof ( r - > arp_dev ) ) ;
neigh_release ( neigh ) ;
err = 0 ;
}
return err ;
}
2007-12-17 00:30:39 +03:00
static int arp_req_delete_public ( struct net * net , struct arpreq * r ,
struct net_device * dev )
2007-12-06 08:20:18 +03:00
{
__be32 ip = ( ( struct sockaddr_in * ) & r - > arp_pa ) - > sin_addr . s_addr ;
__be32 mask = ( ( struct sockaddr_in * ) & r - > arp_netmask ) - > sin_addr . s_addr ;
if ( mask = = htonl ( 0xFFFFFFFF ) )
2008-01-15 09:58:55 +03:00
return pneigh_delete ( & arp_tbl , net , & ip , dev ) ;
2007-12-06 08:20:18 +03:00
2007-12-06 08:20:50 +03:00
if ( mask )
return - EINVAL ;
2007-12-17 00:30:39 +03:00
return arp_req_set_proxy ( net , dev , 0 ) ;
2007-12-06 08:20:18 +03:00
}
2007-12-17 00:30:39 +03:00
static int arp_req_delete ( struct net * net , struct arpreq * r ,
struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
int err ;
2007-12-06 08:20:18 +03:00
__be32 ip ;
2005-04-17 02:20:36 +04:00
struct neighbour * neigh ;
2007-12-06 08:20:18 +03:00
if ( r - > arp_flags & ATF_PUBL )
2007-12-17 00:30:39 +03:00
return arp_req_delete_public ( net , r , dev ) ;
2005-04-17 02:20:36 +04:00
2007-12-06 08:20:18 +03:00
ip = ( ( struct sockaddr_in * ) & r - > arp_pa ) - > sin_addr . s_addr ;
2005-04-17 02:20:36 +04:00
if ( dev = = NULL ) {
struct flowi fl = { . nl_u = { . ip4_u = { . daddr = ip ,
. tos = RTO_ONLINK } } } ;
struct rtable * rt ;
2008-01-23 09:07:34 +03:00
if ( ( err = ip_route_output_key ( net , & rt , & fl ) ) ! = 0 )
2005-04-17 02:20:36 +04:00
return err ;
dev = rt - > u . dst . dev ;
ip_rt_put ( rt ) ;
if ( ! dev )
return - EINVAL ;
}
err = - ENXIO ;
neigh = neigh_lookup ( & arp_tbl , & ip , dev ) ;
if ( neigh ) {
if ( neigh - > nud_state & ~ NUD_NOARP )
2007-02-09 17:24:47 +03:00
err = neigh_update ( neigh , NULL , NUD_FAILED ,
2005-04-17 02:20:36 +04:00
NEIGH_UPDATE_F_OVERRIDE |
NEIGH_UPDATE_F_ADMIN ) ;
neigh_release ( neigh ) ;
}
return err ;
}
/*
* Handle an ARP layer I / O control request .
*/
2007-12-17 00:30:39 +03:00
int arp_ioctl ( struct net * net , unsigned int cmd , void __user * arg )
2005-04-17 02:20:36 +04:00
{
int err ;
struct arpreq r ;
struct net_device * dev = NULL ;
switch ( cmd ) {
case SIOCDARP :
case SIOCSARP :
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
case SIOCGARP :
err = copy_from_user ( & r , arg , sizeof ( struct arpreq ) ) ;
if ( err )
return - EFAULT ;
break ;
default :
return - EINVAL ;
}
if ( r . arp_pa . sa_family ! = AF_INET )
return - EPFNOSUPPORT ;
if ( ! ( r . arp_flags & ATF_PUBL ) & &
( r . arp_flags & ( ATF_NETMASK | ATF_DONTPUB ) ) )
return - EINVAL ;
if ( ! ( r . arp_flags & ATF_NETMASK ) )
( ( struct sockaddr_in * ) & r . arp_netmask ) - > sin_addr . s_addr =
htonl ( 0xFFFFFFFFUL ) ;
rtnl_lock ( ) ;
if ( r . arp_dev [ 0 ] ) {
err = - ENODEV ;
2008-01-15 09:58:55 +03:00
if ( ( dev = __dev_get_by_name ( net , r . arp_dev ) ) = = NULL )
2005-04-17 02:20:36 +04:00
goto out ;
/* Mmmm... It is wrong... ARPHRD_NETROM==0 */
if ( ! r . arp_ha . sa_family )
r . arp_ha . sa_family = dev - > type ;
err = - EINVAL ;
if ( ( r . arp_flags & ATF_COM ) & & r . arp_ha . sa_family ! = dev - > type )
goto out ;
} else if ( cmd = = SIOCGARP ) {
err = - ENODEV ;
goto out ;
}
2007-03-09 07:44:43 +03:00
switch ( cmd ) {
2005-04-17 02:20:36 +04:00
case SIOCDARP :
2007-12-17 00:30:39 +03:00
err = arp_req_delete ( net , & r , dev ) ;
2005-04-17 02:20:36 +04:00
break ;
case SIOCSARP :
2007-12-17 00:30:39 +03:00
err = arp_req_set ( net , & r , dev ) ;
2005-04-17 02:20:36 +04:00
break ;
case SIOCGARP :
err = arp_req_get ( & r , dev ) ;
if ( ! err & & copy_to_user ( arg , & r , sizeof ( r ) ) )
err = - EFAULT ;
break ;
}
out :
rtnl_unlock ( ) ;
return err ;
}
static int arp_netdev_event ( struct notifier_block * this , unsigned long event , void * ptr )
{
struct net_device * dev = ptr ;
switch ( event ) {
case NETDEV_CHANGEADDR :
neigh_changeaddr ( & arp_tbl , dev ) ;
2008-07-06 06:00:44 +04:00
rt_cache_flush ( dev_net ( dev ) , 0 ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
break ;
}
return NOTIFY_DONE ;
}
static struct notifier_block arp_netdev_notifier = {
. notifier_call = arp_netdev_event ,
} ;
/* Note, that it is not on notifier chain.
It is necessary , that this routine was called after route cache will be
flushed .
*/
void arp_ifdown ( struct net_device * dev )
{
neigh_ifdown ( & arp_tbl , dev ) ;
}
/*
* Called once on startup .
*/
static struct packet_type arp_packet_type = {
. type = __constant_htons ( ETH_P_ARP ) ,
. func = arp_rcv ,
} ;
static int arp_proc_init ( void ) ;
void __init arp_init ( void )
{
neigh_table_init ( & arp_tbl ) ;
dev_add_pack ( & arp_packet_type ) ;
arp_proc_init ( ) ;
# ifdef CONFIG_SYSCTL
neigh_sysctl_register ( NULL , & arp_tbl . parms , NET_IPV4 ,
NET_IPV4_NEIGH , " ipv4 " , NULL , NULL ) ;
# endif
register_netdevice_notifier ( & arp_netdev_notifier ) ;
}
# ifdef CONFIG_PROC_FS
# if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
/* ------------------------------------------------------------------------ */
/*
* ax25 - > ASCII conversion
*/
static char * ax2asc2 ( ax25_address * a , char * buf )
{
char c , * s ;
int n ;
for ( n = 0 , s = buf ; n < 6 ; n + + ) {
c = ( a - > ax25_call [ n ] > > 1 ) & 0x7F ;
if ( c ! = ' ' ) * s + + = c ;
}
2007-02-09 17:24:47 +03:00
2005-04-17 02:20:36 +04:00
* s + + = ' - ' ;
if ( ( n = ( ( a - > ax25_call [ 6 ] > > 1 ) & 0x0F ) ) > 9 ) {
* s + + = ' 1 ' ;
n - = 10 ;
}
2007-02-09 17:24:47 +03:00
2005-04-17 02:20:36 +04:00
* s + + = n + ' 0 ' ;
* s + + = ' \0 ' ;
if ( * buf = = ' \0 ' | | * buf = = ' - ' )
return " * " ;
return buf ;
}
# endif /* CONFIG_AX25 */
# define HBUFFERLEN 30
static void arp_format_neigh_entry ( struct seq_file * seq ,
struct neighbour * n )
{
char hbuffer [ HBUFFERLEN ] ;
int k , j ;
char tbuf [ 16 ] ;
struct net_device * dev = n - > dev ;
int hatype = dev - > type ;
read_lock ( & n - > lock ) ;
/* Convert hardware address to XX:XX:XX:XX ... form. */
# if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
if ( hatype = = ARPHRD_AX25 | | hatype = = ARPHRD_NETROM )
ax2asc2 ( ( ax25_address * ) n - > ha , hbuffer ) ;
else {
# endif
for ( k = 0 , j = 0 ; k < HBUFFERLEN - 3 & & j < dev - > addr_len ; j + + ) {
2008-05-22 04:34:32 +04:00
hbuffer [ k + + ] = hex_asc_hi ( n - > ha [ j ] ) ;
hbuffer [ k + + ] = hex_asc_lo ( n - > ha [ j ] ) ;
2005-04-17 02:20:36 +04:00
hbuffer [ k + + ] = ' : ' ;
}
hbuffer [ - - k ] = 0 ;
# if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
}
# endif
2008-10-31 10:53:57 +03:00
sprintf ( tbuf , " %pI4 " , n - > primary_key ) ;
2005-04-17 02:20:36 +04:00
seq_printf ( seq , " %-16s 0x%-10x0x%-10x%s * %s \n " ,
tbuf , hatype , arp_state_to_flags ( n ) , hbuffer , dev - > name ) ;
read_unlock ( & n - > lock ) ;
}
static void arp_format_pneigh_entry ( struct seq_file * seq ,
struct pneigh_entry * n )
{
struct net_device * dev = n - > dev ;
int hatype = dev ? dev - > type : 0 ;
char tbuf [ 16 ] ;
2008-10-31 10:53:57 +03:00
sprintf ( tbuf , " %pI4 " , n - > key ) ;
2005-04-17 02:20:36 +04:00
seq_printf ( seq , " %-16s 0x%-10x0x%-10x%s * %s \n " ,
tbuf , hatype , ATF_PUBL | ATF_PERM , " 00:00:00:00:00:00 " ,
dev ? dev - > name : " * " ) ;
}
static int arp_seq_show ( struct seq_file * seq , void * v )
{
if ( v = = SEQ_START_TOKEN ) {
seq_puts ( seq , " IP address HW type Flags "
" HW address Mask Device \n " ) ;
} else {
struct neigh_seq_state * state = seq - > private ;
if ( state - > flags & NEIGH_SEQ_IS_PNEIGH )
arp_format_pneigh_entry ( seq , v ) ;
else
arp_format_neigh_entry ( seq , v ) ;
}
return 0 ;
}
static void * arp_seq_start ( struct seq_file * seq , loff_t * pos )
{
/* Don't want to confuse "arp -a" w/ magic entries,
* so we tell the generic iterator to skip NUD_NOARP .
*/
return neigh_seq_start ( seq , pos , & arp_tbl , NEIGH_SEQ_SKIP_NOARP ) ;
}
/* ------------------------------------------------------------------------ */
2007-03-13 00:34:29 +03:00
static const struct seq_operations arp_seq_ops = {
2005-04-17 02:20:36 +04:00
. start = arp_seq_start ,
. next = neigh_seq_next ,
. stop = neigh_seq_stop ,
. show = arp_seq_show ,
} ;
static int arp_seq_open ( struct inode * inode , struct file * file )
{
2008-01-24 11:13:18 +03:00
return seq_open_net ( inode , file , & arp_seq_ops ,
sizeof ( struct neigh_seq_state ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-12 11:55:35 +03:00
static const struct file_operations arp_seq_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. open = arp_seq_open ,
. read = seq_read ,
. llseek = seq_lseek ,
2008-01-24 11:13:18 +03:00
. release = seq_release_net ,
2005-04-17 02:20:36 +04:00
} ;
2008-03-25 01:28:43 +03:00
static int __net_init arp_net_init ( struct net * net )
2005-04-17 02:20:36 +04:00
{
2008-03-25 01:28:43 +03:00
if ( ! proc_net_fops_create ( net , " arp " , S_IRUGO , & arp_seq_fops ) )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
return 0 ;
}
2008-03-25 01:28:43 +03:00
static void __net_exit arp_net_exit ( struct net * net )
{
proc_net_remove ( net , " arp " ) ;
}
static struct pernet_operations arp_net_ops = {
. init = arp_net_init ,
. exit = arp_net_exit ,
} ;
static int __init arp_proc_init ( void )
{
return register_pernet_subsys ( & arp_net_ops ) ;
}
2005-04-17 02:20:36 +04:00
# else /* CONFIG_PROC_FS */
static int __init arp_proc_init ( void )
{
return 0 ;
}
# endif /* CONFIG_PROC_FS */
EXPORT_SYMBOL ( arp_broken_ops ) ;
EXPORT_SYMBOL ( arp_find ) ;
EXPORT_SYMBOL ( arp_create ) ;
EXPORT_SYMBOL ( arp_xmit ) ;
EXPORT_SYMBOL ( arp_send ) ;
EXPORT_SYMBOL ( arp_tbl ) ;
# if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
EXPORT_SYMBOL ( clip_tbl_hook ) ;
# endif