/************************************************************* * File: lib/ether.c * Purpose: common ethernet functions * Author: Phil Bunce (pjb@carmel.com) * Revision History: * 980615 Created from sonic.c * 981127 Moved the GETRXREC swapPkt here from drivers. */ /* This module contains all of the chip-independent code needed to * PMON/IMON with an Ethernet download facility. The code is rather * primitive, it only understands UDP and ARP messages, and is only * able to send two types of packet (UDP REPLY and ARP REPLY). The * ether_xxx functions are performed by either sonic.c or am79970.c. */ #include #ifdef ETHERNET /*#define VERBOSE /* be verbose */ #include #include #include #ifdef CHECKS_ON #include #else #define assert(x) #endif #define swap32(n) ( \ ((((Ulong)(n))>>24)&0x000000ff) | \ ((((Ulong)(n))>>8)&0x0000ff00) | \ ((((Ulong)(n))<<8)&0x00ff0000) | \ ((((Ulong)(n))<<24)&0xff000000) \ ) #define swap16(n) ( \ ((((Ushort)(n))>>8)&0x00ff) | \ ((((Ushort)(n))<<8)&0xff00) \ ) #define ether_driver(a,b,c) (*ether_driver_ptr)(a,b,c) /* This is how you call the chip-dependent code. ether_driver_ptr * should have been initialized by your cpu-dependent module (eg. c4101.c), * to point to the appropriate driver (eg. sonic_driver). */ vpFunc *ether_driver_ptr; static Ushort last_udp_seqno; static char *last_dest_addr; static Ushort tx_ip_id; static char *rx_byte_ptr; static int rx_byte_cnt; static int dotcnt; static RXREC *rx_desc; static char *dest_addr; static Uchar EnetAddr[6]; /* the MAC address */ static filbuf(); char *getMsg(); #ifdef VERBOSE static printIp(); static printUdp(); static printArp(); #endif extern int re_ether; extern int vflag; /************************************************************* * ether_open(etheraddr) * Just like the standard open() except that it opens the * Ethernet controller. * 'etheraddr' is a string containing the MAC address in the * form aa:bb:cc:dd:ee:ff. */ ether_open(etheraddr) char *etheraddr; { char tmp[30],*p; int i,n; if (etheraddr == 0 || ether_driver_ptr == 0) return(-1); rx_byte_cnt = 0; rx_desc = 0; /* convert the mac address string into an array of 6 bytes */ strcpy(tmp,etheraddr); for (i=0,p=strtok(tmp,":");p;p=strtok(0,":"),i++) { sscanf(p,"%x",&n); EnetAddr[i] = n; } if (!ether_driver(ETHER_INIT,EnetAddr,0)) return(-1); flush_cache(DCACHE); return(ETHERFD); } /************************************************************* * ether_close() * Just like the standard close() except that it closes the * Ethernet controller. */ ether_close() { char *rxptr; int len; if (rx_desc) { rxptr = (char *)ether_driver(ETHER_GETRBA,rx_desc,&len); sendAck(rxptr,len); ether_driver(ETHER_RXDONE,rx_desc,0); } } /************************************************************* * ether_read(fd,ptr,size) * Just like the standard read() except that it reads from the * Ethernet controller. */ ether_read(fd,ptr,size) int fd,size; char *ptr; { int i,c; for (i=0;iether_type == ETHERTYPE_ARP) { arpRequest(e); ether_driver(ETHER_RXDONE,q,0); continue; } rx_byte_ptr = getMsg(e,len,&rx_byte_cnt); if (rx_byte_ptr) break; ether_driver(ETHER_RXDONE,q,0); } assert(rx_byte_cnt != 0); rx_desc = q; if (no_rtn) return(0); rx_byte_cnt--; return(*rx_byte_ptr++); } /************************************************************* * sendAck(re,len) * send an ACK back to the host */ sendAck(re,len) struct ether_header *re; Ushort len; { int i,n; Ushort *sp; Ulong t; struct ether_header *te; struct ip *ti,*ri; struct udphdr *tu,*ru; if (vflag) { printf("sendAck(%08x,%d)\n",re,len); printMem((char *)re,len); } assert(re != 0); n = 64; te = (struct ether_header *) ether_driver(ETHER_GETTBA,0,&n); te->ether_shost = re->ether_dhost; te->ether_dhost = re->ether_shost; te->ether_type = re->ether_type; ti = (struct ip *) (te+1); ri = (struct ip *) (re+1); ti->ip_vhl = ri->ip_vhl; ti->ip_tos = ri->ip_tos; ti->ip_len = sizeof(struct ip)+sizeof(struct udphdr); ti->ip_id = tx_ip_id++; ti->ip_off = 0; ti->ip_ttl = UDP_TTL; ti->ip_p = IP_UDP; bcopy(&(ri->ip_dst.s_addr),&(ti->ip_src.s_addr),sizeof(struct in_addr)); bcopy(&(ri->ip_src.s_addr),&(ti->ip_dst.s_addr),sizeof(struct in_addr)); tu = (struct udphdr *) (ti+1); ru = (struct udphdr *) (ri+1); tu->uh_sport = ru->uh_sport; tu->uh_dport = ru->uh_sport; tu->uh_ulen = 12; tu->uh_sum = 0; /* haven't figured this out, try zero */ tu->uh_type = UDP_ACK; tu->uh_seqno = 0; if (ru->uh_type == UDP_DATA) tu->uh_seqno = ru->uh_seqno; #ifndef MIPSEB unswapPkt(te); #endif /* compute csum of ip hdr -> t */ ti->ip_sum = 0; sp = (Ushort *) ti; for (i=0,t=0;i<(sizeof(struct ip)/sizeof(Ushort));i++,sp++) t += *sp; t = (t&0xffff)+(t>>16); ti->ip_sum = ~t; if (vflag) printMem((char *)te,64); #ifdef VERBOSE if (vflag) printPkt(te,64); #endif if (re_ether) swap32n(te,64); ether_driver(ETHER_TBARDY,0,0); } /************************************************************* * char *getMsg(e,size,cnt) * Interprets the packet. * Is only able to understand UDP packets of type UDP_OPEN * and UDP_DATA. */ char *getMsg(e,size,cnt) struct ether_header *e; Ushort size; int *cnt; { struct ip *i; struct udphdr *u; int minlen; *cnt = 0; assert(e != 0); minlen = sizeof(struct ether_header)+sizeof(struct ip)+sizeof(struct udphdr); assert(size >= minlen); i = (struct ip *)(e+1); if (pingRequest(e)) return(0); if (i->ip_p != IP_UDP) return(0); u = (struct udphdr *)(i+1); switch (u->uh_type) { case UDP_OPEN : #ifndef MIPSEB u->uh_seqno = swap16(u->uh_seqno); #endif sscanf((char *)&(u->uh_seqno),"%x",&dest_addr); last_udp_seqno = 0; tx_ip_id = i->ip_id; sendAck(e,size); return(0); case UDP_DATA : if (u->uh_seqno > last_udp_seqno) { last_dest_addr = dest_addr; last_udp_seqno = u->uh_seqno; } else { /* retry packet */ dest_addr = last_dest_addr; } *cnt = u->uh_ulen-sizeof(struct udphdr); return((char *)(u+1)); } return(0); } /************************************************************* * pingRequest(e) */ pingRequest(e) struct ether_header *e; { struct ether_header *te; struct ip *i,*ti; struct icmp *m,*tm; Ushort *sp; Ulong t; int n,len; if (e->ether_type != ETHERTYPE_IP) return(0); i = (struct ip *)(e+1); if (i->ip_p != IP_ICMP) return(0); m = (struct icmp *)(i+1); if (m->icmp_type != ICMP_ECHO) return(0); /* This is a ping request */ len = i->ip_len+sizeof(struct ether_header); te = (struct ether_header *) ether_driver(ETHER_GETTBA,0,&len); te->ether_dhost = e->ether_shost; bcopy(EnetAddr,&(te->ether_shost),6); te->ether_type = e->ether_type; /* build ip header */ ti = (struct ip *) (te+1); ti->ip_vhl = i->ip_vhl; ti->ip_tos = i->ip_tos; ti->ip_len = i->ip_len; ti->ip_id = i->ip_id; ti->ip_off = i->ip_off; ti->ip_ttl = i->ip_ttl; ti->ip_p = i->ip_p; /* proto */ bcopy(&(i->ip_dst.s_addr),&(ti->ip_src.s_addr),sizeof(struct in_addr)); bcopy(&(i->ip_src.s_addr),&(ti->ip_dst.s_addr),sizeof(struct in_addr)); /* build icmp header */ tm = (struct icmp *) (ti+1); tm->icmp_type = ICMP_ECHOREPLY; tm->icmp_code = m->icmp_code; tm->icmp_cksum = 0; tm->icmp_id = m->icmp_id; tm->icmp_seq = m->icmp_seq; bcopy(m->icmp_data,tm->icmp_data,i->ip_len-sizeof(struct ip)); /* this is a hack. The real checksum calc appears to be 16-bit add starting at icmp_type and continuing for 62 bytes. Then add 4. Write the one's comp value into the chksum field. */ tm->icmp_cksum = ~((~m->icmp_cksum) - 0x800); #ifndef MIPSEB unswapPkt(te); #endif /* compute csum of ip hdr -> t */ ti->ip_sum = 0; sp = (Ushort *) ti; for (n=0,t=0;n<(sizeof(struct ip)/sizeof(Ushort));n++,sp++) t += *sp; t = (t&0xffff)+(t>>16); ti->ip_sum = ~t; if (vflag) printMem((char *)te,i->ip_len+sizeof(struct ether_header)); #ifdef VERBOSE if (vflag) printPkt(te,64); #endif if (re_ether) swap32n(te,i->ip_len+sizeof(struct ether_header)); ether_driver(ETHER_TBARDY,0,0); return(1); } /************************************************************* * arpRequest(e) */ arpRequest(re) struct ether_header *re; { struct arphdr *ra,*ta; Ulong myip,tpa; struct ether_header *te; int ipi[4],len; assert(re != 0); ra = (struct arphdr *)(re+1); if (!(ra->ar_hrd == ARPHRD_ETHER && ra->ar_pro == 0x800 && ra->ar_hln == 6 && ra->ar_pln == 4 && ra->ar_op == ARPOP_REQUEST)) return; /* check the IP address */ sscanf(getMonEnv("ipaddr"),"%d.%d.%d.%d", &ipi[0],&ipi[1],&ipi[2],&ipi[3]); myip = ((ipi[0]<<24) | (ipi[1]<<16) | (ipi[2]<<8) | ipi[3]); bcopy(ra->ar_tpa,&tpa,4); if (tpa != myip) return; /* not me */ len = 64; te = (struct ether_header *) ether_driver(ETHER_GETTBA,0,&len); te->ether_dhost = re->ether_shost; bcopy(EnetAddr,&(te->ether_shost),6); te->ether_type = re->ether_type; ta = (struct arphdr *) (te+1); ta->ar_hrd = ra->ar_hrd; ta->ar_pro = ra->ar_pro; ta->ar_hln = ra->ar_hln; ta->ar_pln = ra->ar_pln; ta->ar_op = ARPOP_REPLY; bcopy(EnetAddr,ta->ar_sha,6); bcopy(ra->ar_tpa,ta->ar_spa,4); bcopy(ra->ar_sha,ta->ar_tha,6); bcopy(ra->ar_spa,ta->ar_tpa,4); #ifndef MIPSEB unswapPkt(te); #endif if (vflag) printMem((char *)te,64); #ifdef VERBOSE if (vflag) printPkt(te,64); #endif if (re_ether) swap32n(te,64); ether_driver(ETHER_TBARDY,0,0); } /************************************************************* * printMem(p,n) * print 'n' bytes of memory starting at 'p'. */ printMem(p,n) char *p; int n; { int i,len; char ascii[17]; #ifdef HDRONLY if (n > 80) n = 80; #endif printf("\n%08x ",p); len = 0; for (i=0;;i++) { if (i && i%16 == 0) { ascii[len] = 0; printf(" %s\n",ascii); if (i>=n) break; len = 0; printf("%08x ",&p[i]); } if (i>=n) { printf(" "); ascii[len++] = ' '; } else { printf("%02x ",0xff&p[i]); ascii[len++] = (isprint(p[i]))?p[i]:'.'; } } } #ifdef VERBOSE /************************************************************* * printPkt(e,len) * print the contents of an Ethernet header * If the next item is an IP header, print that also. */ printPkt(e,len) struct ether_header *e; int len; { printMem((char *)e,len); printf("dhost=%02x:%02x:%02x:%02x:%02x:%02x ", e->ether_dhost.ether_addr_octet[0], e->ether_dhost.ether_addr_octet[1], e->ether_dhost.ether_addr_octet[2], e->ether_dhost.ether_addr_octet[3], e->ether_dhost.ether_addr_octet[4], e->ether_dhost.ether_addr_octet[5]); printf("shost=%02x:%02x:%02x:%02x:%02x:%02x\n", e->ether_shost.ether_addr_octet[0], e->ether_shost.ether_addr_octet[1], e->ether_shost.ether_addr_octet[2], e->ether_shost.ether_addr_octet[3], e->ether_shost.ether_addr_octet[4], e->ether_shost.ether_addr_octet[5]); switch (e->ether_type) { case ETHERTYPE_IP : printIp(e+1); break; case ETHERTYPE_PUP: printf("type=PUP, not impl\n"); break; case ETHERTYPE_ARP: printArp(e+1); break; case ETHERTYPE_REVARP: printf("type=REVARP, not impl\n"); break; default : printf("type=%04x\n",e->ether_type); } } /************************************************************* * printIp(p) * print the contents of an IP header * If the next item is a UDP header, print that also. */ static printIp(p) struct ip *p; { struct ip q; bcopy(p,&q,sizeof(struct ip)); printf("ip: ver=%02x len=%04x id=%04x ttl=%02x proto=%02x src=%08x dst=%08x\n", getIP_V(q.ip_vhl),q.ip_len,q.ip_id,q.ip_ttl,q.ip_p, q.ip_src.s_addr,q.ip_dst.s_addr); switch (q.ip_p) { case IP_IP: printf("ip protocol\n"); break; case IP_ICMP: printf("icmp protocol\n"); break; case IP_IGMP: printf("igmp protocol\n"); break; case IP_GGP: printf("ggp protocol\n"); break; case IP_TCP: printf("tcp protocol\n"); break; case IP_PUP: printf("pup protocol\n"); break; case IP_UDP: printUdp(p+1); break; default : printf("%02x: unknown ip protocol\n",q.ip_p); return(1); } } /************************************************************* * printUdp(p) * print the contents of a UDP header */ static printUdp(p) struct udphdr *p; { printf("udp: sport=%04x dport=%04x ulen=%04x sum=%04x type=%04x seq=%04x ", p->uh_sport,p->uh_dport,p->uh_ulen,p->uh_sum,p->uh_type,p->uh_seqno); switch (p->uh_type) { case UDP_OPEN : printf("open \"%s\"\n",&(p->uh_seqno)); break; case UDP_ACK : printf("ack\n"); break; case UDP_DATA : printf("data\n"); break; default : printf("%4x: unknown udp type\n",p->uh_type); } } /************************************************************* * printArp(p) * print the contents of a ARP header */ static printArp(p) struct arphdr *p; { printf("arp: hrd=%04x pro=%04x hln=%02x pln=%02x op=%04x \n", p->ar_hrd,p->ar_pro,p->ar_hln,p->ar_pln,p->ar_op); printf("sha=%02x:%02x:%02x:%02x:%02x:%02x ", p->ar_sha[0], p->ar_sha[1], p->ar_sha[2], p->ar_sha[3], p->ar_sha[4], p->ar_sha[5]); printf("spa=%02x.%02x.%02x.%02x\n", p->ar_spa[0], p->ar_spa[1], p->ar_spa[2], p->ar_spa[3]); printf("tha=%02x:%02x:%02x:%02x:%02x:%02x ", p->ar_tha[0], p->ar_tha[1], p->ar_tha[2], p->ar_tha[3], p->ar_tha[4], p->ar_tha[5]); printf("tpa=%02x.%02x.%02x.%02x\n", p->ar_tpa[0], p->ar_tpa[1], p->ar_tpa[2], p->ar_tpa[3]); } #endif /************************************************************* * swap32n(p,n) * Byte-swap the contents of 'n', 32-bit words pointed to by 'p'. */ swap32n(p,n) long *p; int n; { int i; if (n%4) n = (n/4)+1; /* 971115 */ else n /= 4; for (i=0;iether_type == ETHERTYPE_ARP) { swapArp((struct arphdr *)(e+1)); return(1); } if (e->ether_type != ETHERTYPE_IP) return(0); i = (struct ip *)(e+1); swapIp(i); if (i->ip_p == IP_ICMP) swapIcmp((struct icmp *)(i+1)); else if (i->ip_p == IP_UDP) swapUdp((struct udphdr *)(i+1)); else printf("swapPkt: %02x: bad ip_p\n",i->ip_p); return(1); } /************************************************************* * unswapPkt(e) * unByte-swap the headers of an Ethernet packet */ unswapPkt(e) struct ether_header *e; { struct ip *i; struct udphdr *u; if (e->ether_type == ETHERTYPE_ARP) { swapArp((struct arphdr *)(e+1)); swapEh(e); return; } if (e->ether_type != ETHERTYPE_IP) { printf("unswapPkt: %04x: bad ether_type\n",e->ether_type); return; } i = (struct ip *)(e+1); if (i->ip_p == IP_ICMP) { swapIcmp((struct icmp *)(i+1)); swapIp(i); swapEh(e); return; } if (i->ip_p != IP_UDP) { printf("unswapPkt: %02x: bad ip_p\n",i->ip_p); return; } u = (struct udphdr *)(i+1); swapUdp(u); swapIp(i); swapEh(e); } /************************************************************* * swapEh(e) * Byte-swap the Ethernet header */ swapEh(e) struct ether_header *e; { e->ether_type = swap16(e->ether_type); } /************************************************************* * swapIp(i) * Byte-swap the IP header */ swapIp(i) struct ip *i; { Ulong t; i->ip_len = swap16(i->ip_len); i->ip_id = swap16(i->ip_id); i->ip_off = swap16(i->ip_off); i->ip_sum = swap16(i->ip_sum); bcopy(&(i->ip_src.s_addr),&t,4); t = swap32(t); bcopy(&t,&(i->ip_src.s_addr),4); bcopy(&(i->ip_dst.s_addr),&t,4); t = swap32(t); bcopy(&t,&(i->ip_dst.s_addr),4); } /************************************************************* * swapUdp(u) * Byte-swap the UDP header */ swapUdp(u) struct udphdr *u; { u->uh_sport = swap16(u->uh_sport); u->uh_dport = swap16(u->uh_dport); u->uh_ulen = swap16(u->uh_ulen); u->uh_sum = swap16(u->uh_sum); u->uh_type = swap16(u->uh_type); u->uh_seqno = swap16(u->uh_seqno); } /************************************************************* * swapArp(a) */ swapArp(a) struct arphdr *a; { Ulong t; a->ar_hrd = swap16(a->ar_hrd); a->ar_pro = swap16(a->ar_pro); a->ar_op = swap16(a->ar_op); bcopy(a->ar_spa,&t,4); t = swap32(t); bcopy(&t,a->ar_spa,4); bcopy(a->ar_tpa,&t,4); t = swap32(t); bcopy(&t,a->ar_tpa,4); } /************************************************************* * swapIcmp(a) */ swapIcmp(m) struct icmp *m; { m->icmp_cksum = swap16(m->icmp_cksum); m->icmp_id = swap16(m->icmp_id); m->icmp_seq = swap16(m->icmp_seq); } #endif #else /* Tasking tools don't like empty files (sigh) */ ether_foobar() {} #endif /* ETHERNET */