#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "ip.h"
/*
* address resolution tables
*/
enum
{
NHASH = (1<<6),
NCACHE = 256,
AOK = 1,
AWAIT = 2,
};
char *arpstate[] =
{
"UNUSED",
"OK",
"WAIT",
};
enum
{
CMadd,
CMflush,
};
/*
* one per Fs
*/
struct Arp
{
QLock;
Fs *f;
Arpent *hash[NHASH];
Arpent cache[NCACHE];
};
static
Cmdtab arpcmd[] =
{
CMadd, "add", 0,
CMflush, "flush", 1,
};
char *Ebadarp = "bad arp";
#define haship(s) ((s)[IPaddrlen-1]%NHASH)
void
arpinit(Fs *f)
{
f->arp = smalloc(sizeof(Arp));
f->arp->f = f;
}
static Arpent*
newarp(Arp *arp, uchar *ip, Medium *m)
{
uint t;
Block *next, *xp;
Arpent *a, *e, *f, **l;
/* find oldest entry */
e = &arp->cache[NCACHE];
a = arp->cache;
t = a->used;
for(f = a; f < e; f++){
if(f->used < t){
t = f->used;
a = f;
}
}
/* dump waiting packets */
xp = a->hold;
a->hold = nil;
while(xp){
next = xp->list;
freeblist(xp);
xp = next;
}
/* take out of current chain */
l = &arp->hash[haship(a->ip)];
for(f = *l; f; f = f->hash){
if(f == a) {
*l = a->hash;
break;
}
l = &f->hash;
}
/* insert into new chain */
l = &arp->hash[haship(ip)];
a->hash = *l;
*l = a;
memmove(a->ip, ip, sizeof(a->ip));
a->used = msec;
a->time = 0;
a->type = m;
return a;
}
Arpent*
arpget(Arp *arp, Block *bp, int version, Medium *type, uchar *ip, uchar *mac)
{
int hash;
Arpent *a;
uchar v6ip[IPaddrlen];
if(version == V4) {
v4tov6(v6ip, ip);
ip = v6ip;
}
qlock(arp);
hash = haship(ip);
for(a = arp->hash[hash]; a; a = a->hash) {
if(memcmp(ip, a->ip, sizeof(a->ip)) == 0)
if(type == a->type)
break;
}
if(a == nil){
a = newarp(arp, ip, type);
a->state = AWAIT;
}
a->used = msec;
if(a->state == AWAIT){
if(bp != nil){
if(a->hold)
a->last->list = bp;
else
a->hold = bp;
a->last = bp;
bp->list = nil;
}
return a; /* return with arp qlocked */
}
memmove(mac, a->mac, a->type->maclen);
qunlock(arp);
return nil;
}
/*
* called with arp locked
*/
void
arprelease(Arp *arp, Arpent*)
{
qunlock(arp);
}
/*
* called with arp locked
*/
Block*
arpresolve(Arp *arp, Arpent *a, Medium *type, uchar *mac)
{
Block *bp;
memmove(a->mac, mac, type->maclen);
a->type = type;
a->state = AOK;
a->used = msec;
bp = a->hold;
a->hold = nil;
qunlock(arp);
return bp;
}
void
arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh)
{
Arp *arp;
Route *r;
Arpent *a;
Ipifc *ifc;
Medium *type;
Block *bp, *next;
uchar v6ip[IPaddrlen];
arp = fs->arp;
if(n != 6) {
// print("arp: len = %d\n", n);
return;
}
if(version == V4) {
r = v4lookup(fs, ip);
v4tov6(v6ip, ip);
ip = v6ip;
}
else
r = v6lookup(fs, ip);
if(r == nil) {
// print("arp: no route for entry\n");
return;
}
ifc = r->ifc;
type = ifc->m;
qlock(arp);
for(a = arp->hash[haship(ip)]; a; a = a->hash) {
if(a->type != type || (a->state != AWAIT && a->state != AOK))
continue;
if(ipcmp(a->ip, ip) == 0) {
a->state = AOK;
memmove(a->mac, mac, type->maclen);
bp = a->hold;
a->hold = nil;
if(version == V4)
ip += IPv4off;
qunlock(arp);
while(bp) {
next = bp->list;
if(ifc != nil){
if(waserror()){
runlock(ifc);
nexterror();
}
rlock(ifc);
if(ifc->m != nil)
ifc->m->bwrite(ifc, bp, version, ip);
else
freeb(bp);
runlock(ifc);
poperror();
} else
freeb(bp);
bp = next;
}
a->used = msec;
return;
}
}
if(refresh == 0){
a = newarp(arp, ip, type);
a->state = AOK;
a->type = type;
memmove(a->mac, mac, type->maclen);
}
qunlock(arp);
}
int
arpwrite(Fs *fs, char *s, int len)
{
int n;
Route *r;
Arp *arp;
Block *bp;
Arpent *a;
Cmdbuf *cb;
Cmdtab *ct;
Medium *m;
uchar ip[IPaddrlen], mac[MAClen];
arp = fs->arp;
cb = parsecmd(s, len);
if(waserror()){
free(cb);
nexterror();
}
ct = lookupcmd(cb, arpcmd, nelem(arpcmd));
switch(ct->index){
case CMadd:
switch(cb->nf) {
default:
cmderror(cb, Ecmdargs);
case 3:
parseip(ip, cb->f[1]);
r = v4lookup(fs, ip+IPv4off);
if(r == nil)
error("destination unreachable");
m = r->ifc->m;
n = parsemac(mac, cb->f[2], m->maclen);
break;
case 4:
m = ipfindmedium(cb->f[1]);
if(m == nil)
error(Ebadarp);
parseip(ip, cb->f[2]);
n = parsemac(mac, cb->f[3], m->maclen);
break;
}
if(m->ares == nil)
error(Ebadarp);
m->ares(fs, V6, ip, mac, n, 0);
break;
case CMflush:
qlock(arp);
for(a = arp->cache; a < &arp->cache[NCACHE]; a++){
memset(a->ip, 0, sizeof(a->ip));
memset(a->mac, 0, sizeof(a->mac));
a->hash = nil;
a->state = 0;
a->used = 0;
while(a->hold != nil) {
bp = a->hold->list;
freeblist(a->hold);
a->hold = bp;
}
}
memset(arp->hash, 0, sizeof(arp->hash));
qunlock(arp);
break;
}
poperror();
free(cb);
return len;
}
enum
{
Alinelen= 66,
};
char *aformat = "%-6.6s %-8.8s %-16.16I %-32.32s\n";
static void
convmac(char *p, uchar *mac, int n)
{
while(n-- > 0)
p += sprint(p, "%2.2ux", *mac++);
}
int
arpread(Arp *arp, char *p, ulong offset, int len)
{
Arpent *a;
int n;
char mac[2*MAClen+1];
if(offset % Alinelen)
return 0;
offset = offset/Alinelen;
len = len/Alinelen;
n = 0;
for(a = arp->cache; len > 0 && a < &arp->cache[NCACHE]; a++){
if(a->state == 0)
continue;
if(offset > 0){
offset--;
continue;
}
len--;
qlock(arp);
convmac(mac, a->mac, a->type->maclen);
n += sprint(p+n, aformat, a->type->name, arpstate[a->state], a->ip, mac);
qunlock(arp);
}
return n;
}