📄 arp.c
字号:
/* linux/net/inet/arp.c * * Version: $Id: arp.c,v 1.77.2.4 1999/09/23 19:03:36 davem Exp $ * * 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: * Alan Cox : Removed the Ethernet assumptions in * Florian's code * Alan Cox : Fixed some small errors in the ARP * logic * Alan Cox : Allow >4K 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. * Craig Schlenter : Don't modify permanent entry * during arp_rcv. * Russ Nelson : Tidied up a few bits. * Alexey Kuznetsov: Major changes to caching and behaviour, * eg intelligent arp probing and * 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. * Jonathan Layes : Added arpd support through kerneld * 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. * Julian Anastasov: "hidden" flag: hide the * interface and don't reply for it *//* RFC1122 Status: 2.3.2.1 (ARP Cache Validation): MUST provide mechanism to flush stale cache entries (OK) SHOULD be able to configure cache timeout (OK) MUST throttle ARP retransmits (OK) 2.3.2.2 (ARP Packet Queue): SHOULD save at least one packet from each "conversation" with an unresolved IP address. (OK) 950727 -- MS*/#include <linux/types.h>#include <linux/string.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/config.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/errno.h>#include <linux/in.h>#include <linux/mm.h>#include <linux/inet.h>#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/stat.h>#include <linux/init.h>#ifdef CONFIG_SYSCTL#include <linux/sysctl.h>#endif#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>#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)#include <net/ax25.h>#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)#include <net/netrom.h>#endif#endif#include <asm/system.h>#include <asm/uaccess.h>/* * Interface to generic neighbour cache. */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 ={ AF_INET, NULL, arp_solicit, arp_error_report, neigh_resolve_output, neigh_connected_output, dev_queue_xmit, dev_queue_xmit};static struct neigh_ops arp_hh_ops ={ AF_INET, NULL, arp_solicit, arp_error_report, neigh_resolve_output, neigh_resolve_output, dev_queue_xmit, dev_queue_xmit};static struct neigh_ops arp_direct_ops ={ AF_INET, NULL, NULL, NULL, dev_queue_xmit, dev_queue_xmit, dev_queue_xmit, dev_queue_xmit};struct neigh_ops arp_broken_ops ={ AF_INET, NULL, arp_solicit, arp_error_report, neigh_compat_output, neigh_compat_output, dev_queue_xmit, dev_queue_xmit,};struct neigh_table arp_tbl ={ NULL, AF_INET, sizeof(struct neighbour) + 4, 4, arp_constructor, NULL, NULL, parp_redo, { NULL, NULL, &arp_tbl, 0, NULL, NULL, 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 64, 1*HZ }, 30*HZ, 128, 512, 1024,};int arp_mc_map(u32 addr, u8 *haddr, struct device *dev, int dir){ switch (dev->type) { case ARPHRD_ETHER: case ARPHRD_FDDI: ip_eth_mc_map(addr, haddr) ; return 0 ; case ARPHRD_IEEE802: if ( (dev->name[0] == 't') && (dev->name[1] == 'r')) /* Token Ring */ ip_tr_mc_map(addr,haddr) ; else ip_eth_mc_map(addr, haddr); return 0; default: if (dir) { memcpy(haddr, dev->broadcast, dev->addr_len); return 0; } } return -EINVAL;}static int arp_constructor(struct neighbour *neigh){ u32 addr = *(u32*)neigh->primary_key; struct device *dev = neigh->dev; struct in_device *in_dev = dev->ip_ptr; if (in_dev == NULL) return -EINVAL; neigh->type = inet_addr_type(addr); if (in_dev->arp_parms) neigh->parms = in_dev->arp_parms; if (dev->hard_header == NULL) { 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; case ARPHRD_ROSE:#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 break; }#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); } if (dev->hard_header_cache) neigh->ops = &arp_hh_ops; else neigh->ops = &arp_generic_ops; 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){ u32 saddr; u8 *dst_ha = NULL; struct device *dev = neigh->dev; struct device *dev2; struct in_device *in_dev2; u32 target = *(u32*)neigh->primary_key; int probes = neigh->probes; if (skb && (dev2 = ip_dev_find(skb->nh.iph->saddr)) != NULL && (in_dev2 = dev2->ip_ptr) != NULL && !IN_DEV_HIDDEN(in_dev2)) saddr = skb->nh.iph->saddr; else 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; } 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);}/* 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 */static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, struct device * dev){ 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 device *dev = skb->dev; u32 paddr; struct neighbour *n; if (!skb->dst) { printk(KERN_DEBUG "arp_find is called with dst==NULL\n"); kfree_skb(skb); return 1; } paddr = ((struct rtable*)skb->dst)->rt_gateway; if (arp_set_predefined(inet_addr_type(paddr), haddr, paddr, dev)) return 0; start_bh_atomic(); 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) { memcpy(haddr, n->ha, dev->addr_len); neigh_release(n); end_bh_atomic(); return 0; } neigh_release(n); } else kfree_skb(skb); end_bh_atomic(); return 1;}/* END OF OBSOLETE FUNCTIONS *//* * Note: requires bh_atomic locking. */int arp_bind_neighbour(struct dst_entry *dst){ struct device *dev = dst->dev; if (dev == NULL) return 0; if (dst->neighbour == NULL) { u32 nexthop = ((struct rtable*)dst)->rt_gateway; if (dev->flags&(IFF_LOOPBACK|IFF_POINTOPOINT)) nexthop = 0; dst->neighbour = __neigh_lookup(&arp_tbl, &nexthop, dev, 1); } return (dst->neighbour != NULL);}/* * Interface to link layer: send routine and receive handler. *//* * Create and send an arp packet. If (dest_hw == NULL), we create a broadcast * message. */void arp_send(int type, int ptype, u32 dest_ip, struct device *dev, u32 src_ip, unsigned char *dest_hw, unsigned char *src_hw, unsigned char *target_hw){ struct sk_buff *skb; struct arphdr *arp; unsigned char *arp_ptr; /* * No arp on this interface. */ if (dev->flags&IFF_NOARP) return; /* * Allocate a buffer */ skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4) + dev->hard_header_len + 15, GFP_ATOMIC); if (skb == NULL) return; skb_reserve(skb, (dev->hard_header_len+15)&~15); skb->nh.raw = skb->data; arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4)); skb->dev = dev; skb->protocol = __constant_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 */ dev->hard_header(skb,dev,ptype,dest_hw,src_hw,skb->len); /* * 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 = __constant_htons(ETH_P_IP); break;#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) case ARPHRD_AX25: arp->ar_hrd = __constant_htons(ARPHRD_AX25); arp->ar_pro = __constant_htons(AX25_P_IP); break;#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE) case ARPHRD_NETROM: arp->ar_hrd = __constant_htons(ARPHRD_NETROM); arp->ar_pro = __constant_htons(AX25_P_IP); break;#endif#endif#ifdef CONFIG_FDDI case ARPHRD_FDDI: arp->ar_hrd = __constant_htons(ARPHRD_ETHER); arp->ar_pro = __constant_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); arp_ptr+=dev->addr_len; memcpy(arp_ptr, &src_ip,4); arp_ptr+=4; if (target_hw != NULL) memcpy(arp_ptr, target_hw, dev->addr_len); else memset(arp_ptr, 0, dev->addr_len); arp_ptr+=dev->addr_len; memcpy(arp_ptr, &dest_ip, 4); skb->dev = dev; dev_queue_xmit(skb);}static void parp_redo(struct sk_buff *skb){ arp_rcv(skb, skb->dev, NULL);}/* * Receive an arp request by the device layer. */int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt){ struct arphdr *arp = skb->nh.arph; unsigned char *arp_ptr= (unsigned char *)(arp+1); struct rtable *rt; unsigned char *sha, *tha; u32 sip, tip; u16 dev_type = dev->type; int addr_type; struct in_device *in_dev = dev->ip_ptr; struct neighbour *n;/* * The hardware length of the packet should match the hardware length * of the device. Similarly, the hardware types should match. The * device should be ARP-able. Also, if pln is not 4, then the lookup * is not from an IP number. We can't currently handle this, so toss * it. */ if (in_dev == NULL || 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 out; switch (dev_type) { default: if (arp->ar_pro != __constant_htons(ETH_P_IP)) goto out; if (htons(dev_type) != arp->ar_hrd) goto out; break;#ifdef CONFIG_NET_ETHERNET case ARPHRD_ETHER: /* * ETHERNET devices will accept ARP hardware types of either * 1 (Ethernet) or 6 (IEEE 802.2). */ if (arp->ar_hrd != __constant_htons(ARPHRD_ETHER) && arp->ar_hrd != __constant_htons(ARPHRD_IEEE802)) goto out; if (arp->ar_pro != __constant_htons(ETH_P_IP)) goto out; break;#endif#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). */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -