#include "all.h" #include "../ip/ip.h" #define DEBUG if(cons.flags&arpcache.flag)print #define ORDER 1 /* 1 send last frag first, faster */ typedef struct Arpentry Arpentry; typedef struct Arpstats Arpstats; typedef struct Arpe Arpe; struct Arpe { uchar tpa[Pasize]; uchar tha[Easize]; }; static int ipahash(uchar*); static void cmd_arp(int, char*[]); static struct { Lock; uchar null[Pasize]; int start; int idgen; ulong flag; Msgbuf* unresol; struct { int laste; Arpe arpe[Ne]; } abkt[Nb]; } arpcache; int nhgets(uchar *p) { return (p[0]<<8) | p[1]; } long nhgetl(uchar *p) { return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; } void hnputs(uchar *p, int x) { p[0] = x>>8; p[1] = x; } void hnputl(uchar *p, long x) { p[0] = x>>24; p[1] = x>>16; p[2] = x>>8; p[3] = x; } void arpstart(void) { if(arpcache.start == 0) { lock(&arpcache); if(arpcache.start == 0) { cmd_install("arp", "subcommand -- arp protocol", cmd_arp); arpcache.flag = flag_install("arp", "-- verbose"); arpcache.start = 1; iprouteinit(); } unlock(&arpcache); } } void arpreceive(Enpkt *ep, int l, Ifc *ifc) { Ilp* ilp; Arppkt *p, *q; Msgbuf *mb, **mbp; Arpe *a; uchar *tpa; int type, i, h; Timet t; if(l < Ensize+Arpsize) return; p = (Arppkt*)ep; if(nhgets(p->pro) != Iptype || nhgets(p->hrd) != 1 || p->pln != Pasize || p->hln != Easize) return; type = nhgets(p->op); switch(type) { case Arprequest: /* update entry for this source */ h = ipahash(p->spa); a = arpcache.abkt[h].arpe; lock(&arpcache); for(i=0; itpa, p->spa, Pasize) == 0) { memmove(a->tha, p->sha, Easize); break; } } unlock(&arpcache); if(memcmp(p->tpa, ifc->ipa, Pasize) != 0) break; DEBUG("rcv arp req for %I from %I\n", p->tpa, p->spa); mb = mballoc(Ensize+Arpsize, 0, Mbarp1); q = (Arppkt*)mb->data; memmove(q, p, Ensize+Arpsize); hnputs(q->op, Arpreply); memmove(q->tha, p->sha, Easize); memmove(q->tpa, p->spa, Pasize); memmove(q->sha, ifc->ea, Easize); memmove(q->spa, ifc->ipa, Pasize); memmove(q->d, q->s, Easize); send(ifc->reply, mb); break; case Arpreply: DEBUG("rcv arp rpl for %I is %E\n", p->spa, p->sha); h = ipahash(p->spa); a = arpcache.abkt[h].arpe; lock(&arpcache); for(i=0; itpa, p->spa, Pasize) == 0) { memmove(a->tha, p->sha, Easize); goto out; } } i = arpcache.abkt[h].laste + 1; if(i < 0 || i >= Ne) i = 0; arpcache.abkt[h].laste = i; a = &arpcache.abkt[h].arpe[i]; memmove(a->tpa, p->spa, Pasize); memmove(a->tha, p->sha, Easize); /* * go thru unresolved queue */ out: t = toytime(); mbp = &arpcache.unresol; for(mb = *mbp; mb; mb = *mbp) { if(t >= mb->param) { *mbp = mb->next; unlock(&arpcache); mbfree(mb); lock(&arpcache); goto out; } ilp = mb->chan->pdata; tpa = ilp->ipgate; if(memcmp(a->tpa, tpa, Pasize) == 0) { *mbp = mb->next; mb->next = 0; unlock(&arpcache); ipsend(mb); lock(&arpcache); goto out; } mbp = &mb->next; } unlock(&arpcache); break; } } static int ipahash(uchar *p) { ulong h; h = p[0]; h = h*7 + p[1]; h = h*7 + p[2]; h = h*7 + p[3]; return h%Nb; } void ipsend1(Msgbuf *mb, Ifc *ifc, uchar *ipgate) { Msgbuf **mbp, *m; Ippkt *p; Arppkt *q; Arpe *a; int i, id, len, dlen, off; Timet t; p = (Ippkt*)mb->data; a = arpcache.abkt[ipahash(ipgate)].arpe; lock(&arpcache); for(i=0; itpa, ipgate, Pasize) == 0) goto found; /* * queue ip pkt to be resolved later */ again: i = 0; // q length t = toytime(); mbp = &arpcache.unresol; for(m = *mbp; m; m = *mbp) { if(t >= m->param) { *mbp = m->next; unlock(&arpcache); mbfree(m); lock(&arpcache); goto again; } mbp = &m->next; i++; } if(mb->chan && i < 10) { mb->param = t + SECOND(10); mb->next = 0; *mbp = mb; unlock(&arpcache); } else { unlock(&arpcache); mbfree(mb); } /* * send an arp request */ m = mballoc(Ensize+Arpsize, 0, Mbarp2); q = (Arppkt*)m->data; DEBUG("snd arp req target %I ip dest %I\n", ipgate, p->dst); memset(q->d, 0xff, Easize); /* broadcast */ hnputs(q->type, Arptype); hnputs(q->hrd, 1); hnputs(q->pro, Iptype); q->hln = Easize; q->pln = Pasize; hnputs(q->op, Arprequest); memmove(q->sha, ifc->ea, Easize); memmove(q->spa, ifc->ipa, Pasize); memset(q->tha, 0, Easize); memmove(q->tpa, ipgate, Pasize); send(ifc->reply, m); return; found: len = mb->count; /* includes Ensize+Ipsize+Ilsize */ memmove(p->d, a->tha, Easize); p->vihl = IP_VER|IP_HLEN; p->tos = 0; p->ttl = 255; id = arpcache.idgen; if(id == 0) id = toytime() * 80021; arpcache.idgen = id+1; unlock(&arpcache); hnputs(p->id, id); hnputs(p->type, Iptype); /* * If we dont need to fragment just send it */ if(len <= ETHERMAXTU) { hnputs(p->length, len-Ensize); p->frag[0] = 0; p->frag[1] = 0; p->cksum[0] = 0; p->cksum[1] = 0; hnputs(p->cksum, ipcsum(&p->vihl)); send(ifc->reply, mb); return; } off = 0; len -= Ensize+Ipsize; /* just ip data */ while(len > 0) { dlen = (ETHERMAXTU-(Ensize+Ipsize)) & ~7; if(dlen > len) dlen = len; len -= dlen; /* * use first frag in place, * make copies of subsequent frags * this saves a copy of a MTU-size buffer */ if(ORDER && off == 0) { m = 0; mb->count = (Ensize+Ipsize)+dlen; p = (Ippkt*)mb->data; } else { m = mballoc((Ensize+Ipsize)+dlen, 0, Mbip1); p = (Ippkt*)m->data; memmove(m->data, mb->data, Ensize+Ipsize); memmove(m->data+(Ensize+Ipsize), mb->data+(Ensize+Ipsize)+off, dlen); } hnputs(p->length, dlen+Ipsize); if(len == 0) hnputs(p->frag, off>>3); else hnputs(p->frag, (off>>3)|IP_MF); p->cksum[0] = 0; p->cksum[1] = 0; hnputs(p->cksum, ipcsum(&p->vihl)); if(m) send(ifc->reply, m); off += dlen; } if(ORDER) send(ifc->reply, mb); else mbfree(mb); } void ipsend(Msgbuf *mb) { Ilp *ilp; Chan *cp; cp = mb->chan; if(cp == 0) { print("cp = 0\n"); mbfree(mb); return; } ilp = cp->pdata; ipsend1(mb, cp->ifc, ilp->ipgate); } int ipforme(uchar addr[Pasize], Ifc *ifc) { ulong haddr; if(memcmp(addr, ifc->ipa, Pasize) == 0) return 1; haddr = nhgetl(addr); /* My subnet broadcast */ if((haddr&ifc->mask) == (ifc->ipaddr&ifc->mask)) return 1; /* Real ip broadcast */ if(haddr == 0) return 1; /* Old style 255.255.255.255 address */ if(haddr == ~0) return 1; return 0; } /* * ipcsum - Compute internet header checksums */ int ipcsum(uchar *addr) { int len; ulong sum = 0; len = (addr[0]&0xf) << 2; while(len > 0) { sum += (addr[0]<<8) | addr[1] ; len -= 2; addr += 2; } sum = (sum & 0xffff) + (sum >> 16); sum = (sum & 0xffff) + (sum >> 16); return sum^0xffff; } /* * protcol checksum routine */ static short endian = 1; static char* aendian = (char*)&endian; #define LITTLE *aendian int ptclcsum(uchar *addr, int len) { ulong losum, hisum, mdsum, x; ulong t1, t2; losum = 0; hisum = 0; mdsum = 0; x = 0; if((ulong)addr & 1) { if(len) { hisum += addr[0]; len--; addr++; } x = 1; } while(len >= 16) { t1 = *(ushort*)(addr+0); t2 = *(ushort*)(addr+2); mdsum += t1; t1 = *(ushort*)(addr+4); mdsum += t2; t2 = *(ushort*)(addr+6); mdsum += t1; t1 = *(ushort*)(addr+8); mdsum += t2; t2 = *(ushort*)(addr+10); mdsum += t1; t1 = *(ushort*)(addr+12); mdsum += t2; t2 = *(ushort*)(addr+14); mdsum += t1; mdsum += t2; len -= 16; addr += 16; } while(len >= 2) { mdsum += *(ushort*)addr; len -= 2; addr += 2; } if(x) { if(len) losum += addr[0]; if(LITTLE) losum += mdsum; else hisum += mdsum; } else { if(len) hisum += addr[0]; if(LITTLE) hisum += mdsum; else losum += mdsum; } losum += hisum >> 8; losum += (hisum & 0xff) << 8; while(hisum = losum>>16) losum = hisum + (losum & 0xffff); return ~losum & 0xffff; } static void cmd_arp(int argc, char *argv[]) { int h, i, j; Arpe *a; if(argc <= 1) { print("arp flush -- clear cache\n"); print("arp print -- print cache\n"); return; } for(i=1; itpa, Pasize) == 0) continue; print("%-15I %E\n", a->tpa, a->tha); prflush(); } } continue; } } }