📄 arp.c
字号:
/*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* This file implements the Address Resolution Protocol (ARP),
* which is used by TCP/IP to map the IP addresses from a host
* to a low-level hardware address (like an Ethernet address)
* which it can use to talk to that host.
*
* NOTE: This module will be rewritten completely in the near future,
* because I want it to become a multi-address-family address
* resolver, like it should be. It will be put in a separate
* directory under 'net', being a protocol of its own. -FvK
*
* Version: @(#)arp.c 1.0.15 05/25/93
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Arnt Gulbrandsen, <agulbra@pvv.unit.no>
*
* Fixes:
* Stephen A. Wood : arp problems
* 'Mr Linux' : arp problems.
* Alan Cox : arp_ioctl now checks memory areas with verify_area.
* Alan Cox : Non IP arp message now only appears with debugging on.
* Alan Cox : arp queue is volatile (may be altered by arp messages while doing sends)
* Generic queue code is urgently needed!
* Alan Cox : Deleting your own ip addr now gives EINVAL not a printk message.
* Alan Cox : Fix to arp linked list error
* Alan Cox : Ignore broadcast arp (Linus' idea 8-))
* Alan Cox : arp_send memory leak removed
* Alan Cox : generic skbuff code fixes.
* Alan Cox : 'Bad Packet' only reported on debugging
* Alan Cox : Proxy arp.
* Alan Cox : skb->link3 maintained by letting the other xmit queue kill the packet.
* Alan Cox : Knows about type 3 devices (AX.25) using an AX.25 protocol ID not the ethernet
* one.
* Dominik Kubla : Better checking
* Tegge : Assorted corrections on cross port stuff
* Alan Cox : ATF_PERM was backwards! - might be useful now (sigh)
* Alan Cox : Arp timer added.
*
* To Fix:
* : arp response allocates an skbuff to send. However there is a perfectly
* good spare skbuff the right size about to be freed (the query). Use the
* query for the reply. This avoids an out of memory case _and_ speeds arp
* up.
* : FREE_READ v FREE_WRITE errors. Not critical as loopback arps don't occur
*
*
* 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.
*/
#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/timer.h>
#include <linux/errno.h>
#include <linux/if_arp.h>
#include <linux/in.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <stdarg.h>
#include "inet.h"
#include "dev.h"
#include "eth.h"
#include "ip.h"
#include "route.h"
#include "protocol.h"
#include "tcp.h"
#include "skbuff.h"
#include "sock.h"
#include "arp.h"
#define ARP_MAX_TRIES 3
static char *unk_print(unsigned char *, int);
static char *eth_aprint(unsigned char *, int);
static char *arp_cmds[] = {
"0x%04X",
"REQUEST",
"REPLY",
"REVERSE REQUEST",
"REVERSE REPLY",
NULL
};
#define ARP_MAX_CMDS (sizeof(arp_cmds) / sizeof(arp_cmds[0]))
static struct {
char *name;
char *(*print)(unsigned char *ptr, int len);
} arp_types[] = {
{ "0x%04X", unk_print },
{ "10 Mbps Ethernet", eth_aprint },
{ "3 Mbps Ethernet", eth_aprint },
{ "AX.25", unk_print },
{ "Pronet", unk_print },
{ "Chaos", unk_print },
{ "IEEE 802.2 Ethernet (?)", eth_aprint },
{ "Arcnet", unk_print },
{ "AppleTalk", unk_print },
{ NULL, NULL }
};
#define ARP_MAX_TYPE (sizeof(arp_types) / sizeof(arp_types[0]))
struct arp_table *arp_tables[ARP_TABLE_SIZE] = {
NULL,
};
static int arp_proxies=0; /* So we can avoid the proxy arp
overhead with the usual case of
no proxy arps */
struct sk_buff * volatile arp_q = NULL;
static struct arp_table *arp_lookup(unsigned long addr);
static struct arp_table *arp_lookup_proxy(unsigned long addr);
/* Dump the ADDRESS bytes of an unknown hardware type. */
static char *
unk_print(unsigned char *ptr, int len)
{
static char buff[32];
char *bufp = buff;
int i;
for (i = 0; i < len; i++)
bufp += sprintf(bufp, "%02X ", (*ptr++ & 0377));
return(buff);
}
/* Dump the ADDRESS bytes of an Ethernet hardware type. */
static char *
eth_aprint(unsigned char *ptr, int len)
{
if (len != ETH_ALEN) return("");
return(eth_print(ptr));
}
/* Dump an ARP packet. Not complete yet for non-Ethernet packets. */
static void
arp_print(struct arphdr *arp)
{
int len, idx;
unsigned char *ptr;
if (inet_debug != DBG_ARP) return;
printk("ARP: ");
if (arp == NULL) {
printk("(null)\n");
return;
}
/* Print the opcode name. */
len = htons(arp->ar_op);
if (len < ARP_MAX_CMDS) idx = len;
else idx = 0;
printk("op ");
printk(arp_cmds[idx], len);
/* Print the ARP header. */
len = htons(arp->ar_hrd);
if (len < ARP_MAX_TYPE) idx = len;
else idx = 0;
printk(" hrd = "); printk(arp_types[idx].name, len);
printk(" pro = 0x%04X\n", htons(arp->ar_pro));
printk(" hlen = %d plen = %d\n", arp->ar_hln, arp->ar_pln);
/*
* Print the variable data.
* When ARP gets redone (after the formal introduction of NET-2),
* this part will be redone. ARP will then be a multi-family address
* resolver, and the code below will be made more general. -FvK
*/
ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short);
printk(" sender HA = %s ", arp_types[idx].print(ptr, arp->ar_hln));
ptr += arp->ar_hln;
printk(" PA = %s\n", in_ntoa(*(unsigned long *) ptr));
ptr += arp->ar_pln;
printk(" target HA = %s ", arp_types[idx].print(ptr, arp->ar_hln));
ptr += arp->ar_hln;
printk(" PA = %s\n", in_ntoa(*(unsigned long *) ptr));
}
/* This will try to retransmit everything on the queue. */
static void
arp_send_q(void)
{
struct sk_buff *skb;
struct sk_buff *volatile work_q;
cli();
work_q = arp_q;
skb_new_list_head(&work_q);
arp_q = NULL;
sti();
while((skb=skb_dequeue(&work_q))!=NULL)
{
IS_SKB(skb);
skb->magic = 0;
skb->next = NULL;
skb->prev = NULL;
/* Decrement the 'tries' counter. */
cli();
skb->tries--;
if (skb->tries == 0) {
/*
* Grmpf.
* We have tried ARP_MAX_TRIES to resolve the IP address
* from this datagram. This means that the machine does
* not listen to our ARP requests. Perhaps someone tur-
* ned off the thing?
* In any case, trying further is useless. So, we kill
* this packet from the queue. (grinnik) -FvK
*/
skb->sk = NULL;
if(skb->free)
kfree_skb(skb, FREE_WRITE);
/* If free was 0, magic is now 0, next is 0 and
the write queue will notice and kill */
sti();
continue;
}
/* Can we now complete this packet? */
sti();
if (skb->arp || !skb->dev->rebuild_header(skb->data, skb->dev)) {
skb->arp = 1;
skb->dev->queue_xmit(skb, skb->dev, 0);
} else {
/* Alas. Re-queue it... */
skb->magic = ARP_QUEUE_MAGIC;
skb_queue_head(&arp_q,skb);
}
}
}
static struct timer_list arp_timer;
static void arp_queue_ticker(unsigned long data);
static void arp_queue_kick(void)
{
arp_timer.expires = 500; /* 5 seconds */
arp_timer.data = 0;
arp_timer.function = arp_queue_ticker;
del_timer(&arp_timer);
add_timer(&arp_timer);
}
static void arp_queue_ticker(unsigned long data/*UNUSED*/)
{
arp_send_q();
if (skb_peek(&arp_q))
arp_queue_kick();
}
/* Create and send our response to an ARP request. */
static int
arp_response(struct arphdr *arp1, struct device *dev, int addrtype)
{
struct arphdr *arp2;
struct sk_buff *skb;
unsigned long src, dst;
unsigned char *ptr1, *ptr2;
int hlen;
struct arp_table *apt = NULL;/* =NULL otherwise the compiler gives warnings */
/* Decode the source (REQUEST) message. */
ptr1 = ((unsigned char *) &arp1->ar_op) + sizeof(u_short);
src = *((unsigned long *) (ptr1 + arp1->ar_hln));
dst = *((unsigned long *) (ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln));
if(addrtype!=IS_MYADDR)
{
apt=arp_lookup_proxy(dst);
if(apt==NULL)
return(1);
}
/* Get some mem and initialize it for the return trip. */
skb = alloc_skb(sizeof(struct sk_buff) +
sizeof(struct arphdr) +
(2 * arp1->ar_hln) + (2 * arp1->ar_pln) +
dev->hard_header_len, GFP_ATOMIC);
if (skb == NULL) {
printk("ARP: no memory available for ARP REPLY!\n");
return(1);
}
skb->mem_addr = skb;
skb->len = sizeof(struct arphdr) + (2 * arp1->ar_hln) +
(2 * arp1->ar_pln) + dev->hard_header_len;
skb->mem_len = sizeof(struct sk_buff) + skb->len;
hlen = dev->hard_header(skb->data, dev, ETH_P_ARP, src, dst, skb->len);
if (hlen < 0) {
printk("ARP: cannot create HW frame header for REPLY !\n");
kfree_skb(skb, FREE_WRITE);
return(1);
}
/*
* Fill in the ARP REPLY packet.
* This looks ugly, but we have to deal with the variable-length
* ARP packets and such. It is not as bad as it looks- FvK
*/
arp2 = (struct arphdr *) (skb->data + hlen);
ptr2 = ((unsigned char *) &arp2->ar_op) + sizeof(u_short);
arp2->ar_hrd = arp1->ar_hrd;
arp2->ar_pro = arp1->ar_pro;
arp2->ar_hln = arp1->ar_hln;
arp2->ar_pln = arp1->ar_pln;
arp2->ar_op = htons(ARPOP_REPLY);
if(addrtype==IS_MYADDR)
memcpy(ptr2, dev->dev_addr, arp2->ar_hln);
else /* Proxy arp, so pull from the table */
memcpy(ptr2, apt->ha, arp2->ar_hln);
ptr2 += arp2->ar_hln;
memcpy(ptr2, ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln, arp2->ar_pln);
ptr2 += arp2->ar_pln;
memcpy(ptr2, ptr1, arp2->ar_hln);
ptr2 += arp2->ar_hln;
memcpy(ptr2, ptr1 + arp1->ar_hln, arp2->ar_pln);
skb->free = 1;
skb->arp = 1;
skb->sk = NULL;
skb->next = NULL;
DPRINTF((DBG_ARP, ">>"));
arp_print(arp2);
/* Queue the packet for transmission. */
dev->queue_xmit(skb, dev, 0);
return(0);
}
/* This will find an entry in the ARP table by looking at the IP address. */
static struct arp_table *
arp_lookup(unsigned long paddr)
{
struct arp_table *apt;
unsigned long hash;
DPRINTF((DBG_ARP, "ARP: lookup(%s)\n", in_ntoa(paddr)));
/* We don't want to ARP ourselves. */
if (chk_addr(paddr) == IS_MYADDR) {
printk("ARP: ARPing my own IP address %s !\n", in_ntoa(paddr));
return(NULL);
}
/* Loop through the table for the desired address. */
hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
cli();
apt = arp_tables[hash];
while(apt != NULL) {
if (apt->ip == paddr) {
sti();
return(apt);
}
apt = apt->next;
}
sti();
return(NULL);
}
/* This will find a proxy in the ARP table by looking at the IP address. */
static struct arp_table *arp_lookup_proxy(unsigned long paddr)
{
struct arp_table *apt;
unsigned long hash;
DPRINTF((DBG_ARP, "ARP: lookup proxy(%s)\n", in_ntoa(paddr)));
/* Loop through the table for the desired address. */
hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
cli();
apt = arp_tables[hash];
while(apt != NULL) {
if (apt->ip == paddr && (apt->flags & ATF_PUBL) ) {
sti();
return(apt);
}
apt = apt->next;
}
sti();
return(NULL);
}
/* Delete an ARP mapping entry in the cache. */
void
arp_destructor(unsigned long paddr, int force)
{
struct arp_table *apt;
struct arp_table **lapt;
unsigned long hash;
DPRINTF((DBG_ARP, "ARP: destroy(%s)\n", in_ntoa(paddr)));
/* We cannot destroy our own ARP entry. */
if (chk_addr(paddr) == IS_MYADDR) {
DPRINTF((DBG_ARP, "ARP: Destroying my own IP address %s !\n",
in_ntoa(paddr)));
return;
}
hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
cli();
lapt = &arp_tables[hash];
while ((apt = *lapt) != NULL) {
if (apt->ip == paddr) {
if((apt->flags&ATF_PERM) && !force)
return;
*lapt = apt->next;
if(apt->flags&ATF_PUBL)
arp_proxies--;
kfree_s(apt, sizeof(struct arp_table));
sti();
return;
}
lapt = &apt->next;
}
sti();
}
/*
* Kill an entry - eg for ioctl()
*/
void arp_destroy(unsigned long paddr)
{
arp_destructor(paddr,1);
}
/*
* Delete a possibly invalid entry (see timer.c)
*/
void arp_destroy_maybe(unsigned long paddr)
{
arp_destructor(paddr,0);
}
/* Create an ARP entry. The caller should check for duplicates! */
static struct arp_table *
arp_create(unsigned long paddr, unsigned char *addr, int hlen, int htype)
{
struct arp_table *apt;
unsigned long hash;
DPRINTF((DBG_ARP, "ARP: create(%s, ", in_ntoa(paddr)));
DPRINTF((DBG_ARP, "%s, ", eth_print(addr)));
DPRINTF((DBG_ARP, "%d, %d)\n", hlen, htype));
apt = (struct arp_table *) kmalloc(sizeof(struct arp_table), GFP_ATOMIC);
if (apt == NULL) {
printk("ARP: no memory available for new ARP entry!\n");
return(NULL);
}
/* Fill in the allocated ARP cache entry. */
hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
apt->ip = paddr;
apt->hlen = hlen;
apt->htype = htype;
apt->flags = (ATF_INUSE | ATF_COM); /* USED and COMPLETED entry */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -