/* net/rom level 3 low level processing * Copyright 1989 by Daniel M. Frank, W9NK. Permission granted for * non-commercial distribution only. */ #include #include #include "global.h" #include "mbuf.h" #include "iface.h" #include "pktdrvr.h" #include "netuser.h" #include "arp.h" #include "slip.h" #include "ax25.h" #include "netrom.h" #include "nr4.h" #include "lapb.h" #include "socket.h" #include "trace.h" #include "ip.h" #include "commands.h" static int accept_bc __ARGS((char *addr,unsigned ifnum)); static struct nr_bind *find_best __ARGS((struct nr_bind *list,unsigned obso)); static struct nr_bind *find_binding __ARGS((struct nr_bind *list,struct nrnbr_tab *neighbor)); static struct nrnbr_tab *find_nrnbr __ARGS((char *, unsigned)); static struct nrnf_tab *find_nrnf __ARGS((char *, unsigned)); static struct nr_bind *find_worst __ARGS((struct nr_bind *list)); static int ismycall __ARGS((char *addr)); #ifdef notdef static char *nr_getroute __ARGS((char *)); #endif static struct raw_nr *Raw_nr; /* Nodes message broadcast address: "NODES" in shifted ASCII */ char Nr_nodebc[AXALEN] = { 'N'<<1, 'O'<<1, 'D'<<1, 'E'<<1, 'S'<<1, ' '<<1, ('0'<<1) | E }; struct nriface Nrifaces[NRNUMIFACE]; unsigned Nr_numiface; struct nrnbr_tab *Nrnbr_tab[NRNUMCHAINS]; struct nrroute_tab *Nrroute_tab[NRNUMCHAINS]; struct nrnf_tab *Nrnf_tab[NRNUMCHAINS]; unsigned Nr_nfmode = NRNF_NOFILTER; unsigned short Nr_ttl = 64; static unsigned Obso_init = 6; static unsigned Obso_minbc = 5; static unsigned Nr_maxroutes = 5; static unsigned Nr_autofloor = 1; int Nr_verbose = 0; struct iface *Nr_iface; /* send a NET/ROM layer 3 datagram */ void nr3output(dest, data) char *dest; struct mbuf *data; { struct nr3hdr n3hdr; struct mbuf *n3b; memcpy(n3hdr.dest,dest,AXALEN); /* copy destination field */ n3hdr.ttl = Nr_ttl; /* time to live from initializer parm */ if((n3b = htonnr3(&n3hdr)) == NULLBUF){ free_p(data); return; } append(&n3b, data); /* The null interface indicates that the packet needs to have */ /* an appropriate source address inserted by nr_route */ nr_route(n3b,NULLAX25); } /* send IP datagrams across a net/rom network connection */ int nr_send(bp,iface,gateway,tos) struct mbuf *bp; struct iface *iface; int32 gateway; int tos; { struct arp_tab *arp; dump(iface,IF_TRACE_OUT,CL_NETROM,bp); iface->rawsndcnt++; iface->lastsent = secclock(); if((arp = arp_lookup(ARP_NETROM,gateway)) == NULLARP){ free_p(bp); /* drop the packet if no route */ return -1; } nr_sendraw(arp->hw_addr, NRPROTO_IP, NRPROTO_IP, bp); return 0; } /* Send arbitrary protocol data on top of a NET/ROM connection */ void nr_sendraw(dest,family,proto,data) char *dest; unsigned family; unsigned proto; struct mbuf *data; { struct mbuf *pbp; struct nr4hdr n4hdr; /* Create a "network extension" transport header */ n4hdr.opcode = NR4OPPID; n4hdr.u.pid.family = family; n4hdr.u.pid.proto = proto; if((pbp = htonnr4(&n4hdr)) == NULLBUF){ free_p(data); return; } append(&pbp,data); /* Append the data to that */ nr3output(dest, pbp); /* and pass off to level 3 code */ } /* Arrange for receipt of raw NET/ROM datagrams */ struct raw_nr * raw_nr(protocol) char protocol; { register struct raw_nr *rp; rp = (struct raw_nr *)callocw(1,sizeof(struct raw_nr)); rp->protocol = protocol; rp->next = Raw_nr; if(rp->next != NULLRNR) rp->next->prev = rp; Raw_nr = rp; return rp; } /* Free a raw NET/ROM descriptor */ void del_rnr(rpp) struct raw_nr *rpp; { register struct raw_nr *rp; /* Do sanity check on arg */ for(rp = Raw_nr;rp != NULLRNR;rp = rp->next) if(rp == rpp) break; if(rp == NULLRNR) return; /* Doesn't exist */ /* Unlink */ if(rp->prev != NULLRNR) rp->prev->next = rp->next; else Raw_nr = rp->next; if(rp->next != NULLRNR) rp->next->prev = rp->prev; /* Free resources */ free_q(&rp->rcvq); free((char *)rp); } /* Figure out if a call is assigned to one of my net/rom * interfaces. */ static int ismycall(addr) char *addr; { register int i; int found = 0; for(i = 0; i < Nr_numiface; i++) if(addreq(Nrifaces[i].iface->hwaddr,addr)){ found = 1; break; } return found; } /* Route net/rom network layer packets. */ void nr_route(bp, iaxp) struct mbuf *bp; /* network packet */ struct ax25_cb *iaxp; /* incoming ax25 control block */ { struct nr3hdr n3hdr; struct nr4hdr n4hdr; struct ax25_cb *axp; struct mbuf *hbp, *pbp; struct raw_nr *rnr; register struct nrnbr_tab *np; register struct nrroute_tab *rp; register struct nr_bind *bindp; struct iface *iface; unsigned ifnum; if(ntohnr3(&n3hdr,&bp) == -1){ free_p(bp); return; } /* If this isn't an internally generated network packet, * give the router a chance to record a route back to the * sender, in case they aren't in the local node's routing * table yet. */ if(iaxp != NULLAX25 && ax_lookup(iaxp->remote) != NULLAXR){ /* find the interface number */ for(ifnum = 0; ifnum < Nr_numiface; ifnum++) if(iaxp->iface == Nrifaces[ifnum].iface) break; if(ifnum == Nr_numiface){ /* Not a net/rom interface! */ free_p(bp); return; } /* Add (possibly) a zero-quality recorded route via */ /* the neighbor from which this packet was received */ /* Note that this doesn't work with digipeated neighbors. */ (void) nr_routeadd(" ",n3hdr.source,ifnum,0,iaxp->remote,0,1); } /* A packet from me, to me, can only be one thing: * a horrible routing loop. This will probably result * from a bad manual ARP entry, but we should fix these * obscure errors as we find them. */ if(ismycall(n3hdr.dest)){ /* Toss if from me, or if we can't read the header */ if(iaxp == NULLAX25 || ntohnr4(&n4hdr,&bp) == -1){ free_p(bp); } else if((n4hdr.opcode & NR4OPCODE) == NR4OPPID){ for(rnr = Raw_nr;rnr!=NULLRNR;rnr = rnr->next){ if(rnr->protocol!=n4hdr.u.pid.family || rnr->protocol != n4hdr.u.pid.proto) continue; /* Duplicate the data portion, and put the * level 3 header back on */ dup_p(&pbp,bp,0,len_p(bp)); if(pbp != NULLBUF && (hbp = htonnr3(&n3hdr)) != NULLBUF){ append(&hbp,pbp); enqueue(&rnr->rcvq,hbp); } else { free_p(pbp); free_p(hbp); } } /* IP does not use a NET/ROM level 3 socket */ if(n4hdr.u.pid.family == NRPROTO_IP && n4hdr.u.pid.proto == NRPROTO_IP) ip_route(iaxp->iface,bp,0); else /* we don't do this proto */ free_p(bp); } else { /* Must be net/rom transport: */ nr4input(&n4hdr,bp); } return; } if((rp = find_nrroute(n3hdr.dest)) == NULLNRRTAB){ /* no route, drop the packet */ free_p(bp); return; } if((bindp = find_best(rp->routes,1)) == NULLNRBIND){ /* This shouldn't happen yet, but might if we add */ /* dead route detection */ free_p(bp); return; } np = bindp->via; iface = Nrifaces[np->iface].iface; /* Now check to see if iaxp is null. That is */ /* a signal that the packet originates here, */ /* so we need to insert the callsign of the appropriate */ /* interface */ if(iaxp == NULLAX25) memcpy(n3hdr.source,iface->hwaddr,AXALEN); /* Make sure there is a connection to the neighbor */ if((axp = find_ax25(np->call)) == NULLAX25 || (axp->state != LAPB_CONNECTED && axp->state != LAPB_RECOVERY)){ /* Open a new connection or reinitialize old one */ /* hwaddr has been advanced to point to neighbor + digis */ axp = open_ax25(iface,iface->hwaddr,np->call, AX_ACTIVE, Axwindow, s_arcall, s_atcall, s_ascall,-1); if(axp == NULLAX25){ free_p(bp); return; } } if(--n3hdr.ttl == 0){ /* the packet's time to live is over! */ free_p(bp); return; } /* now format network header */ if((pbp = htonnr3(&n3hdr)) == NULLBUF){ free_p(bp); return; } append(&pbp,bp); /* append data to header */ /* put AX.25 PID on front */ bp = pushdown(pbp,1); bp->data[0] = PID_NETROM; if((pbp = segmenter(bp,axp->paclen)) == NULLBUF){ free_p(bp); return; } send_ax25(axp,pbp,-1); /* pass it off to ax25 code */ } /* Perform a nodes broadcast on interface # ifno in the net/rom * interface table. */ void nr_bcnodes(ifno) unsigned ifno; { struct mbuf *hbp, *dbp, *savehdr; struct nrroute_tab *rp; struct nrnbr_tab *np; struct nr_bind * bp; struct nr3dest nrdest; int i, didsend = 0, numdest = 0; register char *cp; struct iface *axif = Nrifaces[ifno].iface; /* prepare the header */ if((hbp = alloc_mbuf(NR3NODEHL)) == NULLBUF) return; hbp->cnt = NR3NODEHL; *hbp->data = NR3NODESIG; memcpy(hbp->data+1,Nrifaces[ifno].alias,ALEN); /* Some people don't want to advertise any routes; they * just want to be a terminal node. In that case we just * want to send our call and alias and be done with it. */ if(!Nr_verbose){ (*axif->output)(axif, Nr_nodebc, axif->hwaddr, PID_NETROM, hbp); /* send it */ return; } /* make a copy of the header in case we need to send more than */ /* one packet */ savehdr = copy_p(hbp,NR3NODEHL); /* now scan through the routing table, finding the best routes */ /* and their neighbors. create destination subpackets and append */ /* them to the header */ for(i = 0; i < NRNUMCHAINS; i++){ for(rp = Nrroute_tab[i]; rp != NULLNRRTAB; rp = rp->next){ /* look for best, non-obsolescent route */ if((bp = find_best(rp->routes,0)) == NULLNRBIND) continue; /* no non-obsolescent routes found */ if(bp->quality == 0) /* this is a loopback route */ continue; /* we never broadcast these */ np = bp->via; /* insert best neighbor */ memcpy(nrdest.neighbor,np->call,AXALEN); /* insert destination from route table */ memcpy(nrdest.dest,rp->call,AXALEN); /* insert alias from route table */ strcpy(nrdest.alias,rp->alias); /* insert quality from binding */ nrdest.quality = bp->quality; /* create a network format destination subpacket */ if((dbp = htonnrdest(&nrdest)) == NULLBUF){ free_p(hbp); /* drop the whole idea ... */ free_p(savehdr); return; } /* we now have a partially filled packet */ didsend = 0; append(&hbp,dbp);/* append to header and others */ /* see if we have appended as many destinations * as we can fit into a single broadcast. If we * have, go ahead and send them out. */ if(++numdest == NRDESTPERPACK){ /* filled it up */ /* indicate that we did broadcast */ didsend = 1; /* reset the destination counter */ numdest = 0; (*axif->output)(axif, Nr_nodebc, axif->hwaddr, PID_NETROM,hbp); /* send it */ /* new header */ hbp = copy_p(savehdr,NR3NODEHL); } } } /* Now, here is something totally weird. If our interfaces */ /* have different callsigns than this one, advertise a very */ /* high quality route to them. Is this a good idea? I don't */ /* know. However, it allows us to simulate a bunch of net/roms */ /* hooked together with a diode matrix coupler. */ for(i = 0; i < Nr_numiface; i++){ if(i == ifno) continue; /* don't bother with ours */ cp = Nrifaces[i].iface->hwaddr; if(!addreq((char *)axif->hwaddr,cp)){ /* both destination and neighbor address */ memcpy(nrdest.dest,cp,AXALEN); memcpy(nrdest.neighbor,cp,AXALEN); /* alias of the interface */ strcpy(nrdest.alias,Nrifaces[i].alias); /* and the very highest quality */ nrdest.quality = 255; /* create a network format destination subpacket */ if((dbp = htonnrdest(&nrdest)) == NULLBUF){ free_p(hbp); /* drop the whole idea ... */ free_p(savehdr); return; } /* we now have a partially filled packet */ didsend = 0; /* append to header and others */ append(&hbp,dbp); if(++numdest == NRDESTPERPACK){ /* filled it up */ /* indicate that we did broadcast */ didsend = 1; /* reset the destination counter */ numdest = 0; (*axif->output)(axif, Nr_nodebc, axif->hwaddr, PID_NETROM,hbp); /* send it */ /* new header */ hbp = copy_p(savehdr,NR3NODEHL); } } } /* If we have a partly filled packet left over, or we never */ /* sent one at all, we broadcast: */ if(!didsend || numdest > 0) (*axif->output)(axif, Nr_nodebc, axif->hwaddr,PID_NETROM, hbp); /* free the header copies */ if(numdest == 0) free_p(hbp); free_p(savehdr); } /* attach the net/rom interface. no parms for now. */ int nr_attach(argc,argv,p) int argc; char *argv[]; void *p; { if(Nr_iface != (struct iface *)0){ tprintf("netrom interface already attached\n"); return -1; } Nr_iface = (struct iface *)callocw(1,sizeof(struct iface)); Nr_iface->addr = Ip_addr; /* The strdup is needed to keep the detach routine happy (it'll * free the allocated memory) */ Nr_iface->name = strdup("netrom"); if(Nr_iface->hwaddr == NULLCHAR){ Nr_iface->hwaddr = mallocw(AXALEN); memcpy(Nr_iface->hwaddr,Mycall,AXALEN); } Nr_iface->mtu = NR4MAXINFO; setencap(Nr_iface,"NETROM"); Nr_iface->next = Ifaces; Ifaces = Nr_iface; memcpy(Nr4user,Mycall,AXALEN); Nr_iface->txproc = newproc("nr tx",512,if_tx,0,Nr_iface,NULL,0); return 0; } /* This function checks an ax.25 address and interface number against * the filter table and mode, and returns 1 if the address is to be * accepted, and 0 if it is to be filtered out. */ static int accept_bc(addr,ifnum) char *addr; unsigned ifnum; { struct nrnf_tab *fp; if(Nr_nfmode == NRNF_NOFILTER) /* no filtering in effect */ return 1; fp = find_nrnf(addr,ifnum); /* look it up */ if((fp != NULLNRNFTAB && Nr_nfmode == NRNF_ACCEPT) || (fp == NULLNRNFTAB && Nr_nfmode == NRNF_REJECT)) return 1; else return 0; } /* receive and process node broadcasts. */ void nr_nodercv(iface,source,bp) struct iface *iface; char *source; struct mbuf *bp; { register int ifnum; char bcalias[AXALEN]; struct nr3dest ds; /* First, see if this is even a net/rom interface: */ for(ifnum = 0; ifnum < Nr_numiface; ifnum++) if(iface == Nrifaces[ifnum].iface) break; if(ifnum == Nr_numiface){ /* not in the interface table */ free_p(bp); return; } if(!accept_bc(source,ifnum)){ /* check against filter */ free_p(bp); return; } /* See if it has a routing broadcast signature: */ if(PULLCHAR(&bp) != NR3NODESIG){ free_p(bp); return; } /* now try to get the alias */ if(pullup(&bp,bcalias,ALEN) < ALEN){ free_p(bp); return; } bcalias[ALEN] = '\0'; /* null terminate */ /* enter the neighbor into our routing table */ if(nr_routeadd(bcalias,source,ifnum,Nrifaces[ifnum].quality, source, 0, 0) == -1){ free_p(bp); return; } /* we've digested the header; now digest the actual */ /* routing information */ while(ntohnrdest(&ds,&bp) != -1){ /* ignore routes to me! */ if(ismycall(ds.dest)) continue; /* ignore routes below the minimum quality threshhold */ if(ds.quality < Nr_autofloor) continue; /* set loopback paths to 0 quality */ if(ismycall(ds.neighbor)) ds.quality = 0; else ds.quality = ((ds.quality * Nrifaces[ifnum].quality + 128) / 256) & 0xff; if(nr_routeadd(ds.alias,ds.dest,ifnum,ds.quality,source,0,0) == -1) break; } free_p(bp); /* This will free the mbuf if anything fails above */ } /* The following are utilities for manipulating the routing table */ /* hash function for callsigns. Look familiar? */ int16 nrhash(s) char *s; { register char x; register int i; x = 0; for(i = ALEN; i !=0; i--) x ^= *s++ & 0xfe; x ^= *s & SSID; return (int16)(uchar(x) % NRNUMCHAINS); } /* Find a neighbor table entry. Neighbors are determined by * their callsign and the interface number. This takes care * of the case where the same switch or hosts uses the same * callsign on two different channels. This isn't done by * net/rom, but it might be done by stations running *our* * software. */ static struct nrnbr_tab * find_nrnbr(addr,ifnum) register char *addr; unsigned ifnum; { int16 hashval; register struct nrnbr_tab *np; /* Find appropriate hash chain */ hashval = nrhash(addr); /* search hash chain */ for(np = Nrnbr_tab[hashval]; np != NULLNTAB; np = np->next){ /* convert first in list to ax25 address format */ if(addreq(np->call,addr) && np->iface == ifnum){ return np; } } return NULLNTAB; } /* Find a route table entry */ struct nrroute_tab * find_nrroute(addr) register char *addr; { int16 hashval; register struct nrroute_tab *rp; /* Find appropriate hash chain */ hashval = nrhash(addr); /* search hash chain */ for(rp = Nrroute_tab[hashval]; rp != NULLNRRTAB; rp = rp->next){ if(addreq(rp->call,addr)){ return rp; } } return NULLNRRTAB; } /* Try to find the AX.25 address of a node with the given alias. Return */ /* a pointer to the AX.25 address if found, otherwise NULLCHAR. The alias */ /* should be a six character, blank-padded, upper-case string. */ char * find_nralias(alias) char *alias; { int i; register struct nrroute_tab *rp; /* Since the route entries are hashed by ax.25 address, we'll */ /* have to search all the chains */ for(i = 0; i < NRNUMCHAINS; i++) for(rp = Nrroute_tab[i]; rp != NULLNRRTAB; rp = rp->next) if(strncmp(alias, rp->alias, 6) == 0) return rp->call; /* If we get to here, we're out of luck */ return NULLCHAR; } /* Find a binding in a list by its neighbor structure's address */ static struct nr_bind * find_binding(list,neighbor) struct nr_bind *list; register struct nrnbr_tab *neighbor; { register struct nr_bind *bp; for(bp = list; bp != NULLNRBIND; bp = bp->next) if(bp->via == neighbor) return bp; return NULLNRBIND; } /* Find the worst quality non-permanent binding in a list */ static struct nr_bind * find_worst(list) struct nr_bind *list; { register struct nr_bind *bp; struct nr_bind *worst = NULLNRBIND; unsigned minqual = 1000; /* infinity */ for(bp = list; bp != NULLNRBIND; bp = bp->next) if(!(bp->flags & NRB_PERMANENT) && bp->quality < minqual){ worst = bp; minqual = bp->quality; } return worst; } /* Find the best binding of any sort in a list. If obso is 1, * include entries below the obsolescence threshhold in the * search (used when this is called for routing broadcasts). * If it is 0, routes below the threshhold are treated as * though they don't exist. */ static struct nr_bind * find_best(list,obso) struct nr_bind *list; unsigned obso; { register struct nr_bind *bp; struct nr_bind *best = NULLNRBIND; int maxqual = -1; /* negative infinity */ for(bp = list; bp != NULLNRBIND; bp = bp->next) if((int)bp->quality > maxqual) if(obso || bp->obsocnt >= Obso_minbc){ best = bp; maxqual = bp->quality; } return best; } /* Add a route to the net/rom routing table */ int nr_routeadd(alias,dest,ifnum,quality,neighbor,permanent,record) char *alias; /* net/rom node alias, blank-padded and */ /* null-terminated */ char *dest; /* destination node callsign */ unsigned ifnum; /* net/rom interface number */ unsigned quality; /* route quality */ char *neighbor; /* neighbor node + 2 digis (max) in arp format */ unsigned permanent; /* 1 if route is permanent (hand-entered) */ unsigned record; /* 1 if route is a "record route" */ { struct nrroute_tab *rp; struct nr_bind *bp; struct nrnbr_tab *np; int16 rhash, nhash; /* See if a routing table entry exists for this destination */ if((rp = find_nrroute(dest)) == NULLNRRTAB){ rp = (struct nrroute_tab *)callocw(1,sizeof(struct nrroute_tab)); /* create a new route table entry */ strncpy(rp->alias,alias,6); memcpy(rp->call,dest,AXALEN); rhash = nrhash(dest); rp->next = Nrroute_tab[rhash]; if(rp->next != NULLNRRTAB) rp->next->prev = rp; Nrroute_tab[rhash] = rp; /* link at head of hash chain */ } else if(!record){ strncpy(rp->alias,alias,6); /* update the alias */ } /* See if an entry exists for this neighbor */ if((np = find_nrnbr(neighbor,ifnum)) == NULLNTAB){ np = (struct nrnbr_tab *)callocw(1,sizeof(struct nrnbr_tab)); /* create a new neighbor entry */ memcpy(np->call,neighbor,AXALEN); np->iface = ifnum; nhash = nrhash(neighbor); np->next = Nrnbr_tab[nhash]; if(np->next != NULLNTAB) np->next->prev = np; Nrnbr_tab[nhash] = np; } else if(permanent){ /* force this path to the neighbor */ memcpy(np->call,neighbor,AXALEN); } /* See if there is a binding between the dest and neighbor */ if((bp = find_binding(rp->routes,np)) == NULLNRBIND){ bp = (struct nr_bind *)callocw(1,sizeof(struct nr_bind)); /* create a new binding and link it in */ bp->via = np; /* goes via this neighbor */ bp->next = rp->routes; /* link into binding chain */ if(bp->next != NULLNRBIND) bp->next->prev = bp; rp->routes = bp; rp->num_routes++; /* bump route count */ np->refcnt++; /* bump neighbor ref count */ bp->quality = quality; bp->obsocnt = Obso_init; /* use initial value */ if(permanent) bp->flags |= NRB_PERMANENT; else if(record) /* notice permanent overrides record! */ bp->flags |= NRB_RECORDED; } else { if(permanent){ /* permanent request trumps all */ bp->quality = quality; bp->obsocnt = Obso_init; bp->flags |= NRB_PERMANENT; bp->flags &= ~NRB_RECORDED; /* perm is not recorded */ } else if(!(bp->flags & NRB_PERMANENT)){ /* not permanent */ if(record){ /* came from nr_route */ if(bp->flags & NRB_RECORDED){ /* no mod non-rec bindings */ bp->quality = quality; bp->obsocnt = Obso_init; /* freshen recorded routes */ } } else { /* came from a routing broadcast */ bp->quality = quality; bp->obsocnt = Obso_init; bp->flags &= ~NRB_RECORDED; /* no longer a recorded route */ } } } /* Now, check to see if we have too many bindings, and drop */ /* the worst if we do */ if(rp->num_routes > Nr_maxroutes){ /* since find_worst never returns permanent entries, the */ /* limitation on number of routes is circumvented for */ /* permanent routes */ if((bp = find_worst(rp->routes)) != NULLNRBIND){ nr_routedrop(dest,bp->via->call,bp->via->iface); } } return 0; } /* Drop a route to dest via neighbor */ int nr_routedrop(dest,neighbor,ifnum) char *dest, *neighbor; unsigned ifnum; { register struct nrroute_tab *rp; register struct nrnbr_tab *np; register struct nr_bind *bp; if((rp = find_nrroute(dest)) == NULLNRRTAB) return -1; if((np = find_nrnbr(neighbor,ifnum)) == NULLNTAB) return -1; if((bp = find_binding(rp->routes,np)) == NULLNRBIND) return -1; /* drop the binding first */ if(bp->next != NULLNRBIND) bp->next->prev = bp->prev; if(bp->prev != NULLNRBIND) bp->prev->next = bp->next; else rp->routes = bp->next; free((char *)bp); rp->num_routes--; /* decrement the number of bindings */ np->refcnt--; /* and the number of neighbor references */ /* now see if we should drop the route table entry */ if(rp->num_routes == 0){ if(rp->next != NULLNRRTAB) rp->next->prev = rp->prev; if(rp->prev != NULLNRRTAB) rp->prev->next = rp->next; else Nrroute_tab[nrhash(dest)] = rp->next; free((char *)rp); } /* and check to see if this neighbor can be dropped */ if(np->refcnt == 0){ if(np->next != NULLNTAB) np->next->prev = np->prev; if(np->prev != NULLNTAB) np->prev->next = np->next; else Nrnbr_tab[nrhash(neighbor)] = np->next; free((char *)np); } return 0; } #ifdef notused /* Find the best neighbor for destination dest, in arp format */ static char * nr_getroute(dest) char *dest; { register struct nrroute_tab *rp; register struct nr_bind *bp; if((rp = find_nrroute(dest)) == NULLNRRTAB) return NULLCHAR; if((bp = find_best(rp->routes,1)) == NULLNRBIND) /* shouldn't happen! */ return NULLCHAR; return bp->via->call; } #endif /* notused */ /* Find an entry in the filter table */ static struct nrnf_tab * find_nrnf(addr,ifnum) register char *addr; unsigned ifnum; { int16 hashval; register struct nrnf_tab *fp; /* Find appropriate hash chain */ hashval = nrhash(addr); /* search hash chain */ for(fp = Nrnf_tab[hashval]; fp != NULLNRNFTAB; fp = fp->next){ if(addreq(fp->neighbor,addr) && fp->iface == ifnum){ return fp; } } return NULLNRNFTAB; } /* Add an entry to the filter table. Return 0 on success, * -1 on failure */ int nr_nfadd(addr,ifnum) char *addr; unsigned ifnum; { struct nrnf_tab *fp; int16 hashval; if(find_nrnf(addr,ifnum) != NULLNRNFTAB) return 0; /* already there; it's a no-op */ fp = (struct nrnf_tab *)callocw(1,sizeof(struct nrnf_tab)); hashval = nrhash(addr); memcpy(fp->neighbor,addr,AXALEN); fp->iface = ifnum; fp->next = Nrnf_tab[hashval]; if(fp->next != NULLNRNFTAB) fp->next->prev = fp; Nrnf_tab[hashval] = fp; return 0; } /* Drop a neighbor from the filter table. Returns 0 on success, -1 * on failure. */ int nr_nfdrop(addr,ifnum) char *addr; unsigned ifnum; { struct nrnf_tab *fp; if((fp = find_nrnf(addr,ifnum)) == NULLNRNFTAB) return -1; /* not in the table */ if(fp->next != NULLNRNFTAB) fp->next->prev = fp->prev; if(fp->prev != NULLNRNFTAB) fp->prev->next = fp->next; else Nrnf_tab[nrhash(addr)] = fp->next; free((char *)fp); return 0; }