OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [net/] [ipv4/] [rarp.c] - Rev 1765

Compare with Previous | Blame | View Log

/* linux/net/inet/rarp.c
 *
 * Copyright (C) 1994 by Ross Martin
 * Based on linux/net/inet/arp.c, Copyright (C) 1994 by Florian La Roche
 *
 * This module implements the Reverse Address Resolution Protocol 
 * (RARP, RFC 903), which is used to convert low level addresses such
 * as ethernet addresses into high level addresses such as IP addresses.
 * The most common use of RARP is as a means for a diskless workstation 
 * to discover its IP address during a network boot.
 *
 **
 ***	WARNING:::::::::::::::::::::::::::::::::WARNING
 ****
 *****	SUN machines seem determined to boot solely from the person who
 ****	answered their RARP query. NEVER add a SUN to your RARP table
 ***	unless you have all the rest to boot the box from it. 
 **
 * 
 * Currently, only ethernet address -> IP address is likely to work.
 * (Is RARP ever used for anything else?)
 *
 * This code 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	:	Rarp delete on device down needed as
 *				reported by Walter Wolfgang.
 *  Lawrence V. Stefani :	Added FDDI support.
 *      San Mehat	:	Fixed bug where rarp would fail to build
 *				if procfs was not compiled into the kernel
 */
 
#include <linux/module.h>
 
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/in.h>
#include <linux/config.h>
 
#include <asm/system.h>
#include <asm/segment.h>
#include <stdarg.h>
#include <linux/inet.h>
#include <linux/etherdevice.h>
#include <net/ip.h>
#include <net/route.h>
#include <net/protocol.h>
#include <net/tcp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/arp.h>
#include <net/rarp.h>
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
#include <net/ax25.h>
#endif
#include <linux/proc_fs.h>
#include <linux/stat.h>
 
extern int (*rarp_ioctl_hook)(unsigned int,void*);
 
/*
 *	This structure defines the RARP mapping cache. As long as we make 
 *	changes in this structure, we keep interrupts off.
 */
 
struct rarp_table
{
	struct rarp_table  *next;             /* Linked entry list           */
	unsigned long      ip;                /* ip address of entry         */
	unsigned char      ha[MAX_ADDR_LEN];  /* Hardware address            */
	unsigned char      hlen;              /* Length of hardware address  */
	unsigned char      htype;             /* Type of hardware in use     */
	struct device      *dev;              /* Device the entry is tied to */
};
 
struct rarp_table *rarp_tables = NULL;
 
static int rarp_rcv(struct sk_buff *, struct device *, struct packet_type *);
 
static struct packet_type rarp_packet_type =
{
	0,  /* Should be: __constant_htons(ETH_P_RARP) - but this _doesn't_ come out constant! */
	0,                /* copy */
	rarp_rcv,
	NULL,
	NULL
};
 
static int initflag = 1;
 
 
/*
 *	Release the memory for this entry.
 */
 
static inline void rarp_release_entry(struct rarp_table *entry)
{
	kfree_s(entry, sizeof(struct rarp_table));
	MOD_DEC_USE_COUNT;
	return;
}
 
/*
 *	Delete a RARP mapping entry in the cache.
 */
 
static void rarp_destroy(unsigned long ip_addr)
{
	struct rarp_table *entry;
	struct rarp_table **pentry;
 
	cli();
	pentry = &rarp_tables;
	while ((entry = *pentry) != NULL)
	{
		if (entry->ip == ip_addr)
		{
			*pentry = entry->next;
			sti();
			rarp_release_entry(entry);
			return;
		}
		pentry = &entry->next;
	}
	sti();
}
 
/*
 *	Flush a device.
 */
 
static void rarp_destroy_dev(struct device *dev)
{
	struct rarp_table *entry;
	struct rarp_table **pentry;
 
	cli();
	pentry = &rarp_tables;
	while ((entry = *pentry) != NULL)
	{
		if (entry->dev == dev)
		{
			*pentry = entry->next;
			rarp_release_entry(entry);
		}
		else
			pentry = &entry->next;
	}
	sti();
}
 
static int rarp_device_event(struct notifier_block *this, unsigned long event, void *ptr)
{
	if(event!=NETDEV_DOWN)
		return NOTIFY_DONE;
	rarp_destroy_dev((struct device *)ptr);
	return NOTIFY_DONE;
}
 
/*
 *	Called once when data first added to rarp cache with ioctl.
 */
 
static struct notifier_block rarp_dev_notifier={
	rarp_device_event,
	NULL,
	0
};
 
static int rarp_pkt_inited=0;
 
static void rarp_init_pkt (void)
{
	/* Register the packet type */
	rarp_packet_type.type=htons(ETH_P_RARP);
	dev_add_pack(&rarp_packet_type);
	register_netdevice_notifier(&rarp_dev_notifier);
        rarp_pkt_inited=1;
}
 
#ifdef MODULE
 
static void rarp_end_pkt(void)
{
        if(!rarp_pkt_inited)
                return;
        dev_remove_pack(&rarp_packet_type);
        unregister_netdevice_notifier(&rarp_dev_notifier);
        rarp_pkt_inited=0;
}
 
#endif
 
 
/*
 *	Receive an arp request by the device layer.  Maybe it should be 
 *	rewritten to use the incoming packet for the reply. The current 
 *	"overhead" time isn't that high...
 */
 
static int rarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
/*
 *	We shouldn't use this type conversion. Check later.
 */
	struct arphdr *rarp = (struct arphdr *) skb->data;
	unsigned char *rarp_ptr = skb_pull(skb,sizeof(struct arphdr));
	struct rarp_table *entry;
	long sip,tip;
	unsigned char *sha,*tha;            /* s for "source", t for "target" */
 
/*
 *	If this test doesn't pass, it's not IP, or we should ignore it anyway
 */
#ifdef CONFIG_FDDI
	if (dev->type == ARPHRD_FDDI)
	{
		/*
		 * Since the dev->type for FDDI is "made up", compare the rarp->ar_hrd
		 * field against ARPHRD_ETHER and ARPHRD_IEEE802.
		 *
		 * Ought to move to a device specifc 'arp_type_ok()'
		 */
		if (rarp->ar_hln != dev->addr_len
			|| ((ntohs(rarp->ar_hrd) != ARPHRD_ETHER) && (ntohs(rarp->ar_hrd) != ARPHRD_IEEE802))
			|| dev->flags&IFF_NOARP)
		{
			kfree_skb(skb, FREE_READ);
			return 0;
		}
	}
	else
	{
		if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd) 
			|| dev->flags&IFF_NOARP)
			{
			kfree_skb(skb, FREE_READ);
			return 0;
		}
	}
#else
	if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd) 
		|| dev->flags&IFF_NOARP)
	{
		kfree_skb(skb, FREE_READ);
		return 0;
	}
#endif
 
/*
 *	If it's not a RARP request, delete it.
 */
	if (rarp->ar_op != htons(ARPOP_RREQUEST))
	{
		kfree_skb(skb, FREE_READ);
		return 0;
	}
 
/*
 *	For now we will only deal with IP addresses.
 */
 
	if (
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
		(rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
#endif
		(rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25)
		|| rarp->ar_pln != 4)
	{
		/*
		 *	This packet is not for us. Remove it. 
		 */
		kfree_skb(skb, FREE_READ);
		return 0;
	}
 
/*
 *	Extract variable width fields
 */
 
	sha=rarp_ptr;
	rarp_ptr+=dev->addr_len;
	memcpy(&sip,rarp_ptr,4);
	rarp_ptr+=4;
	tha=rarp_ptr;
	rarp_ptr+=dev->addr_len;
	memcpy(&tip,rarp_ptr,4);
 
/*
 *	Process entry. Use tha for table lookup according to RFC903.
 */
 
	cli();
	for (entry = rarp_tables; entry != NULL; entry = entry->next)
		if (!memcmp(entry->ha, tha, rarp->ar_hln))
			break;
 
	if (entry != NULL)
	{
		sip=entry->ip;
		sti();
 
		arp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, dev->pa_addr, sha, 
			dev->dev_addr, sha);
	}
	else
		sti();
 
	kfree_skb(skb, FREE_READ);
	return 0;
}
 
 
/*
 *	Set (create) a RARP cache entry.
 */
 
static int rarp_req_set(struct arpreq *req)
{
	struct arpreq r;
	struct rarp_table *entry;
	struct sockaddr_in *si;
	int htype, hlen;
	unsigned long ip;
	struct rtable *rt;
	struct device * dev;
 
	memcpy_fromfs(&r, req, sizeof(r));
 
	/*
	 *	We only understand about IP addresses... 
	 */
 
	if (r.arp_pa.sa_family != AF_INET)
		return -EPFNOSUPPORT;
 
	switch (r.arp_ha.sa_family) 
	{
		case ARPHRD_ETHER:
			htype = ARPHRD_ETHER;
			hlen = ETH_ALEN;
			break;
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
		case ARPHRD_AX25:
			htype = ARPHRD_AX25;
			hlen = 7;
		break;
#endif
		default:
			return -EPFNOSUPPORT;
	}
 
	si = (struct sockaddr_in *) &r.arp_pa;
	ip = si->sin_addr.s_addr;
	if (ip == 0)
	{
		printk(KERN_DEBUG "RARP: SETRARP: requested PA is 0.0.0.0 !\n");
		return -EINVAL;
	}
 
/*
 *	Is it reachable directly ?
 */
 
	rt = ip_rt_route(ip, 0, NULL);
	if (rt == NULL)
		return -ENETUNREACH;
	dev = rt->rt_dev;
	ip_rt_put(rt);
 
/*
 *	Is there an existing entry for this address?  Find out...
 */
 
	cli();
	for (entry = rarp_tables; entry != NULL; entry = entry->next)
		if (entry->ip == ip)
			break;
 
/*
 *	If no entry was found, create a new one.
 */
 
	if (entry == NULL)
	{
		entry = (struct rarp_table *) kmalloc(sizeof(struct rarp_table),
				    GFP_ATOMIC);
		if (entry == NULL)
		{
			sti();
			return -ENOMEM;
		}
		if (initflag)
		{
			rarp_init_pkt();
			initflag=0;
		}
 
		entry->next = rarp_tables;
		rarp_tables = entry;
	}
 
	entry->ip = ip;
	entry->hlen = hlen;
	entry->htype = htype;
	memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);
	entry->dev = dev;
 
	/* Don't unlink if we have entries to serve. */
	MOD_INC_USE_COUNT;
 
	sti();  
 
	return 0;
}
 
 
/*
 *        Get a RARP cache entry.
 */
 
static int rarp_req_get(struct arpreq *req)
{
	struct arpreq r;
	struct rarp_table *entry;
	struct sockaddr_in *si;
	unsigned long ip;
 
/*
 *	We only understand about IP addresses...
 */
 
	memcpy_fromfs(&r, req, sizeof(r));
 
	if (r.arp_pa.sa_family != AF_INET)
		return -EPFNOSUPPORT;
 
/*
 *        Is there an existing entry for this address?
 */
 
	si = (struct sockaddr_in *) &r.arp_pa;
	ip = si->sin_addr.s_addr;
 
	cli();
	for (entry = rarp_tables; entry != NULL; entry = entry->next)
		if (entry->ip == ip)
			break;
 
	if (entry == NULL)
	{
		sti();
		return -ENXIO;
	}
 
/*
 *        We found it; copy into structure.
 */
 
	memcpy(r.arp_ha.sa_data, &entry->ha, entry->hlen);
	r.arp_ha.sa_family = entry->htype;
	sti();
 
/*
 *        Copy the information back
 */
 
	memcpy_tofs(req, &r, sizeof(r));
	return 0;
}
 
 
/*
 *	Handle a RARP layer I/O control request.
 */
 
int rarp_ioctl(unsigned int cmd, void *arg)
{
	struct arpreq r;
	struct sockaddr_in *si;
	int err;
 
	switch(cmd)
	{
		case SIOCDRARP:
			if (!suser())
				return -EPERM;
			err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
			if(err)
				return err;
			memcpy_fromfs(&r, arg, sizeof(r));
			if (r.arp_pa.sa_family != AF_INET)
				return -EPFNOSUPPORT;
			si = (struct sockaddr_in *) &r.arp_pa;
			rarp_destroy(si->sin_addr.s_addr);
			return 0;
 
		case SIOCGRARP:
			err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq));
			if(err)
				return err;
			return rarp_req_get((struct arpreq *)arg);
		case SIOCSRARP:
			if (!suser())
				return -EPERM;
			err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
			if(err)
				return err;
			return rarp_req_set((struct arpreq *)arg);
		default:
			return -EINVAL;
	}
 
	/*NOTREACHED*/
	return 0;
}
 
int rarp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
{
	int len=0;
	off_t begin=0;
	off_t pos=0;
	int size;
	struct rarp_table *entry;
	char ipbuffer[20];
	unsigned long netip;
	if (initflag)
	{
		size = sprintf(buffer,"RARP disabled until entries added to cache.\n");
		pos+=size;
		len+=size;
	}   
	else
	{
		size = sprintf(buffer,
			"IP address       HW type             HW address\n");
		pos+=size;
		len+=size;
 
		cli();
		for(entry=rarp_tables; entry!=NULL; entry=entry->next)
		{
			netip=htonl(entry->ip);          /* switch to network order */
			sprintf(ipbuffer,"%d.%d.%d.%d",
				(unsigned int)(netip>>24)&255,
				(unsigned int)(netip>>16)&255,
				(unsigned int)(netip>>8)&255,
				(unsigned int)(netip)&255);
 
			size = sprintf(buffer+len,
				"%-17s%-20s%02x:%02x:%02x:%02x:%02x:%02x\n",
				ipbuffer,
				"10Mbps Ethernet",
				(unsigned int)entry->ha[0],
				(unsigned int)entry->ha[1],
				(unsigned int)entry->ha[2],
				(unsigned int)entry->ha[3],
				(unsigned int)entry->ha[4],
			 	(unsigned int)entry->ha[5]);
 
			len+=size;
			pos=begin+len;
 
			if(pos<offset)
			{
				len=0;
				begin=pos;
			}
			if(pos>offset+length)
				break;
		}
		sti();
	}      
 
	*start = buffer+(offset-begin);	/* Start of wanted data */
	len   -= (offset-begin);	/* Start slop */
	if (len>length)
		len = length;		/* Ending slop */
	return len;
}
 
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry pde1 = {
		PROC_NET_RARP, 4, "rarp",
		S_IFREG | S_IRUGO, 1, 0, 0,
		0, &proc_net_inode_operations,
		rarp_get_info
	};
#endif
 
void
rarp_init(void)
{
#ifdef CONFIG_PROC_FS
	proc_net_register(&pde1);
#endif
	rarp_ioctl_hook = rarp_ioctl;
}
 
#ifdef MODULE
 
int init_module(void)
{
	rarp_init();
	return 0;
}
 
void cleanup_module(void)
{
	struct rarp_table *rt, *rt_next;
#ifdef CONFIG_PROC_FS
	proc_net_unregister(PROC_NET_RARP);
#endif
	rarp_ioctl_hook = NULL;
	cli();
	/* Destroy the RARP-table */
	rt = rarp_tables;
	rarp_tables = NULL;
	sti();
	/* ... and free it. */
	for ( ; rt != NULL; rt = rt_next) {
		rt_next = rt->next;
		rarp_release_entry(rt);
	}
	rarp_end_pkt();
}
#endif
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.