diff options
Diffstat (limited to 'net/802/tr.c')
-rw-r--r-- | net/802/tr.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/net/802/tr.c b/net/802/tr.c new file mode 100644 index 000000000..643cf64c5 --- /dev/null +++ b/net/802/tr.c @@ -0,0 +1,285 @@ +#include <asm/segment.h> +#include <asm/system.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/trdevice.h> +#include <linux/skbuff.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/net.h> +#include <net/arp.h> + +static void tr_source_route(struct trh_hdr *trh,struct device *dev); +static void tr_add_rif_info(struct trh_hdr *trh); +static void rif_check_expire(unsigned long dummy); + +typedef struct rif_cache_s *rif_cache; + +struct rif_cache_s { + unsigned char addr[TR_ALEN]; + unsigned short rcf; + unsigned short rseg[8]; + rif_cache next; + unsigned long last_used; +}; + +#define RIF_TABLE_SIZE 16 +rif_cache rif_table[RIF_TABLE_SIZE]={ NULL, }; + +#define RIF_TIMEOUT 60*10*HZ +#define RIF_CHECK_INTERVAL 60*HZ +static struct timer_list rif_timer={ NULL,NULL,RIF_CHECK_INTERVAL,0L,rif_check_expire }; + +int tr_header(unsigned char *buff, struct device *dev, unsigned short type, + void *daddr, void *saddr, unsigned len, struct sk_buff *skb) { + + struct trh_hdr *trh=(struct trh_hdr *)buff; + struct trllc *trllc=(struct trllc *)(buff+sizeof(struct trh_hdr)); + + trh->ac=AC; + trh->fc=LLC_FRAME; + + if(saddr) + memcpy(trh->saddr,saddr,dev->addr_len); + else + memset(trh->saddr,0,dev->addr_len); /* Adapter fills in address */ + + trllc->dsap=trllc->ssap=EXTENDED_SAP; + trllc->llc=UI_CMD; + + trllc->protid[0]=trllc->protid[1]=trllc->protid[2]=0x00; + trllc->ethertype=htons(type); + + if(daddr) { + memcpy(trh->daddr,daddr,dev->addr_len); + tr_source_route(trh,dev); + return(dev->hard_header_len); + } + return -dev->hard_header_len; + +} + +int tr_rebuild_header(void *buff, struct device *dev, unsigned long dest, + struct sk_buff *skb) { + + struct trh_hdr *trh=(struct trh_hdr *)buff; + struct trllc *trllc=(struct trllc *)(buff+sizeof(struct trh_hdr)); + + if(trllc->ethertype != htons(ETH_P_IP)) { + printk("tr_rebuild_header: Don't know how to resolve type %04X addresses ?\n",(unsigned int)htons( trllc->ethertype)); + return 0; + } + + if(arp_find(trh->daddr, dest, dev, dev->pa_addr, skb)) { + return 1; + } + else { + tr_source_route(trh,dev); + return 0; + } +} + +unsigned short tr_type_trans(struct sk_buff *skb, struct device *dev) { + + struct trh_hdr *trh=(struct trh_hdr *)skb->data; + struct trllc *trllc=(struct trllc *)(skb->data+sizeof(struct trh_hdr)); + + if(trh->saddr[0] & TR_RII) + tr_add_rif_info(trh); + + if(*trh->daddr & 1) + { + if(!memcmp(trh->daddr,dev->broadcast,TR_ALEN)) + skb->pkt_type=PACKET_BROADCAST; + else + skb->pkt_type=PACKET_MULTICAST; + } + + else if(dev->flags & IFF_PROMISC) + { + if(memcmp(trh->daddr, dev->dev_addr, TR_ALEN)) + skb->pkt_type=PACKET_OTHERHOST; + } + + return trllc->ethertype; +} + +/* We try to do source routing... */ + +static void tr_source_route(struct trh_hdr *trh,struct device *dev) { + + int i; + unsigned int hash; + rif_cache entry; + + /* Broadcasts are single route as stated in RFC 1042 */ + if(!memcmp(&(trh->daddr[0]),&(dev->broadcast[0]),TR_ALEN)) { + trh->rcf=htons((((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK) + | TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST); + trh->saddr[0]|=TR_RII; + } + else { + for(i=0,hash=0;i<TR_ALEN;hash+=trh->daddr[i++]); + hash&=RIF_TABLE_SIZE-1; + for(entry=rif_table[hash];entry && memcmp(&(entry->addr[0]),&(trh->daddr[0]),TR_ALEN);entry=entry->next); + + if(entry) { +#if 0 +printk("source routing for %02X %02X %02X %02X %02X %02X\n",trh->daddr[0], + trh->daddr[1],trh->daddr[2],trh->daddr[3],trh->daddr[4],trh->daddr[5]); +#endif + if((ntohs(entry->rcf) & TR_RCF_LEN_MASK) >> 8) { + trh->rcf=entry->rcf; + memcpy(&trh->rseg[0],&entry->rseg[0],8*sizeof(unsigned short)); + trh->rcf^=htons(TR_RCF_DIR_BIT); + trh->rcf&=htons(0x1fff); /* Issam Chehab <ichehab@madge1.demon.co.uk> */ + + trh->saddr[0]|=TR_RII; + entry->last_used=jiffies; + } + } + else { + trh->rcf=htons((((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK) + | TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST); + trh->saddr[0]|=TR_RII; + } + } + +} + +static void tr_add_rif_info(struct trh_hdr *trh) { + + int i; + unsigned int hash; + rif_cache entry; + + + trh->saddr[0]&=0x7f; + for(i=0,hash=0;i<TR_ALEN;hash+=trh->saddr[i++]); + hash&=RIF_TABLE_SIZE-1; +#if 0 + printk("hash: %d\n",hash); +#endif + for(entry=rif_table[hash];entry && memcmp(&(entry->addr[0]),&(trh->saddr[0]),TR_ALEN);entry=entry->next); + + if(entry==NULL) { +#if 0 +printk("adding rif_entry: addr:%02X:%02X:%02X:%02X:%02X:%02X rcf:%04X\n", + trh->saddr[0],trh->saddr[1],trh->saddr[2], + trh->saddr[3],trh->saddr[4],trh->saddr[5], + trh->rcf); +#endif + entry=kmalloc(sizeof(struct rif_cache_s),GFP_ATOMIC); + if(!entry) { + printk("tr.c: Couldn't malloc rif cache entry !\n"); + return; + } + entry->rcf=trh->rcf; + memcpy(&(entry->rseg[0]),&(trh->rseg[0]),8*sizeof(unsigned short)); + memcpy(&(entry->addr[0]),&(trh->saddr[0]),TR_ALEN); + entry->next=rif_table[hash]; + entry->last_used=jiffies; + rif_table[hash]=entry; + } +/* Y. Tahara added */ + else { + if ( entry->rcf != trh->rcf ) { + if (!(trh->rcf & htons(TR_RCF_BROADCAST_MASK))) { +#if 0 +printk("updating rif_entry: addr:%02X:%02X:%02X:%02X:%02X:%02X rcf:%04X\n", + trh->saddr[0],trh->saddr[1],trh->saddr[2], + trh->saddr[3],trh->saddr[4],trh->saddr[5], + trh->rcf); +#endif + entry->rcf = trh->rcf; + memcpy(&(entry->rseg[0]),&(trh->rseg[0]),8*sizeof(unsigned short)); + entry->last_used=jiffies; + } + } + } + +} + +static void rif_check_expire(unsigned long dummy) { + + int i; + unsigned long now=jiffies,flags; + + save_flags(flags); + cli(); + + for(i=0; i < RIF_TABLE_SIZE;i++) { + + rif_cache entry, *pentry=rif_table+i; + + while((entry=*pentry)) + if((now-entry->last_used) > RIF_TIMEOUT) { + *pentry=entry->next; + kfree_s(entry,sizeof(struct rif_cache_s)); + } + else + pentry=&entry->next; + } + restore_flags(flags); + + del_timer(&rif_timer); + rif_timer.expires=RIF_CHECK_INTERVAL; + add_timer(&rif_timer); + +} + +int rif_get_info(char *buffer,char **start, off_t offset, int length) { + + int len=0; + off_t begin=0; + off_t pos=0; + int size,i; + + rif_cache entry; + + size=sprintf(buffer, +" TR address rcf routing segments TTL\n\n"); + pos+=size; + len+=size; + + for(i=0;i < RIF_TABLE_SIZE;i++) { + for(entry=rif_table[i];entry;entry=entry->next) { + size=sprintf(buffer+len,"%02X:%02X:%02X:%02X:%02X:%02X %04X %04X %04X %04X %04X %04X %04X %04X %04X %lu\n", + entry->addr[0],entry->addr[1],entry->addr[2],entry->addr[3],entry->addr[4],entry->addr[5], + entry->rcf,entry->rseg[0],entry->rseg[1],entry->rseg[2],entry->rseg[3], + entry->rseg[4],entry->rseg[5],entry->rseg[6],entry->rseg[7],jiffies-entry->last_used); + len+=size; + pos=begin+len; + + if(pos<offset) { + len=0; + begin=pos; + } + if(pos>offset+length) + break; + } + if(pos>offset+length) + break; + } + + *start=buffer+(offset-begin); /* Start of wanted data */ + len-=(offset-begin); /* Start slop */ + if(len>length) + len=length; /* Ending slop */ + return len; +} + +void rif_init(struct net_proto *unused) { + + add_timer(&rif_timer); + +} + |