URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [net/] [ipv4/] [rarp.c] - Rev 1772
Go to most recent revision | 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
Go to most recent revision | Compare with Previous | Blame | View Log