📄 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 3static 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 voidarp_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 voidarp_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 intarp_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. */voidarp_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 + -