summaryrefslogtreecommitdiffstats
path: root/net/802/tr.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/802/tr.c')
-rw-r--r--net/802/tr.c285
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);
+
+}
+