/* cat > ./nr3.c << '\Rogue\Monster\' */ /* net/rom level 3 low level processing */ #include #include "global.h" #include "mbuf.h" #include "iface.h" #include "timer.h" #include "arp.h" #include "slip.h" #include "ax25.h" #include "netrom.h" #include "lapb.h" #include /* Nodes message broadcast address: "NODES" in shifted ASCII */ struct ax25_addr nr_nodebc = { '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 nr_ttl = 64 ; unsigned obso_init = 6 ; unsigned obso_minbc = 5 ; unsigned nr_maxroutes = 5 ; unsigned nr_autofloor = 1 ; struct interface *nr_interface ; /* send IP datagrams across a net/rom network connection */ int nr_send(bp,interface,gateway,precedence,delay,throughput,reliability) struct mbuf *bp ; struct interface *interface ; int32 gateway ; char precedence ; char delay ; char throughput ; char reliability ; { struct ax25_addr dest ; struct mbuf *tbp, *htonnr3() ; struct nr3hdr n3hdr ; char *hwaddr ; struct arp_tab *arp ; if ((arp = arp_lookup(ARP_NETROM,gateway)) == NULLARP) { free_p(bp) ; /* drop the packet if no route */ return ; } hwaddr = arp->hw_addr ; /* points to destination */ memcpy(dest.call, hwaddr, ALEN) ; dest.ssid = hwaddr[ALEN] ; /* set up host format header */ /* The null source call is a signal to nr_route to insert the call */ /* of the outbound interface. We can't do it here because */ /* we don't know the interface it's going to go out, and */ /* the interfaces might have different callsigns. */ n3hdr.source.call[0] = '\0' ; n3hdr.dest = dest ; n3hdr.ttl = nr_ttl ; /* Convert to a network format header. This will be stripped */ /* back off in nr_route, but them's the breaks. */ if ((tbp = htonnr3(&n3hdr)) == NULLBUF) { free_p(bp) ; return ; } append(tbp,bp) ; /* append data to header */ nr_route(tbp) ; /* pass off to level 3 routing code */ } /* Figure out if a call is assigned to one of my net/rom * interfaces. */ static int ismycall(addr) struct ax25_addr *addr ; { register int i ; int found = 0 ; for (i = 0 ; i < nr_numiface ; i++) if (addreq((struct ax25_addr *)(nrifaces[i].interface->hwaddr), addr)) { found = 1 ; break ; } return found ; } /* Route net/rom network layer packets. */ nr_route(bp) struct mbuf *bp ; { struct nr3hdr n3hdr ; struct ax25_cb *axp, *find_ax25(), *open_ax25() ; struct ax25 naxhdr ; struct ax25_addr neighbor ; struct mbuf *hbp, *pbp, *htonnr3() ; extern int16 axwindow ; void ax_incom() ; register struct nrnbr_tab *np ; register struct nrroute_tab *rp ; register struct nr_bind *bindp, *find_best() ; struct interface *interface ; if (ntohnr3(&n3hdr,&bp) == -1) { free_p(bp) ; return ; } if (ismycall(&n3hdr.dest)) { ip_route(bp,0) ; 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 ; memcpy(neighbor.call,np->call,ALEN) ; neighbor.ssid = np->call[ALEN] ; interface = nrifaces[np->interface].interface ; /* Now check to see if the source call is null. That is */ /* a signal from nr_send that the packet originates here, */ /* so we need to insert the callsign of the appropriate */ /* interface */ if (n3hdr.source.call[0] == '\0') memcpy(&n3hdr.source,interface->hwaddr,AXALEN) ; /* Make sure there is a connection to the neighbor */ if ((axp = find_ax25(&neighbor)) == NULLAX25 || axp->state != CONNECTED) { /* Open a new connection or reinitialize old one */ /* hwaddr has been advanced to point to neighbor + digis */ atohax25(&naxhdr, np->call, (struct ax25_addr *)interface->hwaddr) ; axp = open_ax25(&naxhdr, axwindow, ax_incom, NULLVFP, NULLVFP, interface,(char *)0) ; if (axp == NULLAX25) { free_p(bp) ; return ; } } if (--n3hdr.ttl == 0) { /* the packet's time to live is over! */ free_p(bp) ; return ; } /* allocate and fill PID mbuf */ if ((pbp = alloc_mbuf(1)) == NULLBUF) { free_p(bp) ; return ; } pbp->cnt = 1 ; *pbp->data = (PID_FIRST | PID_LAST | PID_NETROM) ; /* now format network header */ if ((hbp = htonnr3(&n3hdr)) == NULLBUF) { free_p(pbp) ; free_p(bp) ; return ; } append(pbp,hbp) ; /* append header to pid */ append(pbp,bp) ; /* append data to header */ send_ax25(axp,pbp) ; /* pass it off to ax25 code */ } /* Perform a nodes broadcast on interface # ifno in the net/rom * interface table. */ 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 interface *axif = nrifaces[ifno].interface ; /* prepare the header */ if ((hbp = alloc_mbuf(NR3NODEHL)) == NULLBUF) return ; hbp->cnt = NR3NODEHL ; *hbp->data = NR3NODESIG ; memcpy(hbp->data+1,nrifaces[ifno].alias,ALEN) ; /* 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.call,np->call,ALEN) ; nrdest.neighbor.ssid = np->call[ALEN] ; /* insert destination from route table */ nrdest.dest = rp->call ; /* 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 ; } 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 */ didsend = 1 ; /* indicate that we did broadcast */ numdest = 0 ; /* reset the destination counter */ (*axif->output)(axif, (char *)&nr_nodebc, axif->hwaddr, (PID_FIRST | PID_LAST | PID_NETROM), hbp) ; /* send it */ hbp = copy_p(savehdr,NR3NODEHL) ; /* new header */ } } } /* 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].interface->hwaddr ; if (!addreq((struct ax25_addr *)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 ; } append(hbp,dbp) ; /* append to header and others */ if (++numdest == NRDESTPERPACK) { /* filled it up */ didsend = 1 ; /* indicate that we did broadcast */ numdest = 0 ; /* reset the destination counter */ (*axif->output)(axif, (char *)&nr_nodebc, axif->hwaddr, (PID_FIRST | PID_LAST | PID_NETROM), hbp) ; /* send it */ hbp = copy_p(savehdr,NR3NODEHL) ; /* new header */ } } } /* 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, (char *)&nr_nodebc, axif->hwaddr, (PID_FIRST | PID_LAST | PID_NETROM), hbp) ; free_p(savehdr) ; /* free the header copy */ } /* initialize fake arp entry for netrom */ nr3arp() { int psax25(), setpath() ; arp_init(ARP_NETROM,AXALEN,0,0,NULLCHAR,psax25,setpath) ; } /* attach the net/rom interface. no parms for now. */ nr_attach(argc,argv) int argc ; char *argv[] ; { if (nr_interface != (struct interface *)0) { printf("netrom interface already attached\n") ; return -1 ; } nr3arp() ; nr_interface = (struct interface *)calloc(1,sizeof(struct interface)) ; nr_interface->name = "netrom" ; nr_interface->mtu = NR3DLEN ; nr_interface->send = nr_send ; nr_interface->next = ifaces ; ifaces = nr_interface ; 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) struct ax25_addr *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. */ nr_nodercv(interface,source,bp) struct interface *interface ; struct ax25_addr *source ; struct mbuf *bp ; { register int ifnum ; char bcalias[7] ; char buf[16] ; struct nr3dest ds ; char sbuf[AXALEN*3] ; /* First, see if this is even a net/rom interface: */ for (ifnum = 0 ; ifnum < nr_numiface ; ifnum++) if (interface == nrifaces[ifnum].interface) 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) & 0xff) != NR3NODESIG) { /* DG2KK: uchar(pul ) */ 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 */ /* copy source address and convert to arp format */ memcpy(sbuf,source->call,ALEN) ; sbuf[ALEN] = (source->ssid | E) ; /* terminate */ /* enter the neighbor into our routing table */ if (nr_routeadd(bcalias,source,ifnum,nrifaces[ifnum].quality, sbuf, 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,sbuf,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) struct ax25_addr *s ; { register char x ; register int i ; register char *cp ; x = 0 ; cp = s->call ; for (i = ALEN ; i !=0 ; i--) x ^= *cp++ & 0xfe ; x ^= s->ssid & SSID ; return (x & 0xff) % NRNUMCHAINS ; /* DG2KK: was: uchar(x) */ } /* 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. */ struct nrnbr_tab * find_nrnbr(addr,ifnum) register struct ax25_addr *addr ; unsigned ifnum ; { int16 hashval ; register struct nrnbr_tab *np ; char i_state ; struct ax25_addr ncall ; /* Find appropriate hash chain */ hashval = nrhash(addr) ; /* search hash chain */ i_state = disable() ; for (np = nrnbr_tab[hashval] ; np != NULLNTAB ; np = np->next) { memcpy(ncall.call,np->call,ALEN) ; /* convert first in */ ncall.ssid = np->call[ALEN] ; /* list to ax25 address format */ if (addreq(&ncall,addr) && np->interface == ifnum) { restore(i_state) ; return np ; } } restore(i_state) ; return NULLNTAB ; } /* Find a route table entry */ struct nrroute_tab * find_nrroute(addr) register struct ax25_addr *addr ; { int16 hashval ; register struct nrroute_tab *rp ; char i_state ; /* Find appropriate hash chain */ hashval = nrhash(addr) ; /* search hash chain */ i_state = disable() ; for (rp = nrroute_tab[hashval] ; rp != NULLNRRTAB ; rp = rp->next) { if (addreq(&rp->call,addr)) { restore(i_state) ; return rp ; } } restore(i_state) ; return NULLNRRTAB ; } /* Find a binding in a list by its neighbor structure's address */ 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 */ nr_routeadd(alias,dest,ifnum,quality,neighbor,permanent) char *alias ; /* net/rom node alias, blank-padded and */ /* null-terminated */ struct ax25_addr *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) */ { struct nrroute_tab *rp ; struct nr_bind *bp ; struct nrnbr_tab *np ; int16 rhash, nhash ; struct ax25_addr ncall ; /* See if a routing table entry exists for this destination */ if ((rp = find_nrroute(dest)) == NULLNRRTAB) { if ((rp = (struct nrroute_tab *)calloc(1,sizeof(struct nrroute_tab))) == NULLNRRTAB) return -1 ; else { /* create a new route table entry */ strncpy(rp->alias,alias,6) ; rp->call = *dest ; 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 */ } } /* See if an entry exists for this neighbor */ memcpy(ncall.call,neighbor,ALEN) ; /* no digis included */ ncall.ssid = neighbor[ALEN] ; if ((np = find_nrnbr(&ncall,ifnum)) == NULLNTAB) { if ((np = (struct nrnbr_tab *)calloc(1,sizeof(struct nrnbr_tab))) == NULLNTAB) { if (rp->num_routes == 0) { /* we just added to table */ nrroute_tab[rhash] = rp->next ; free(rp) ; /* so get rid of it */ } return -1 ; } else { /* create a new neighbor entry */ memcpy(np->call,neighbor,3 * AXALEN) ; np->interface = ifnum ; nhash = nrhash(&ncall) ; 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,3 * AXALEN) ; } /* See if there is a binding between the dest and neighbor */ if ((bp = find_binding(rp->routes,np)) == NULLNRBIND) { if ((bp = (struct nr_bind *)calloc(1,sizeof(struct nr_bind))) == NULLNRBIND) { if (rp->num_routes == 0) { /* we just added to table */ nrroute_tab[rhash] = rp->next ; free(rp) ; /* so get rid of it */ } if (np->refcnt == 0) { /* we just added it */ nrnbr_tab[nhash] = np->next ; free(np) ; } return -1 ; } else { /* 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 ; } } /* Don't allow broadcasts to modify permanent entries */ else if (permanent || !(bp->flags & NRB_PERMANENT)) { bp->quality = quality ; bp->obsocnt = obso_init ; if (permanent) bp->flags |= NRB_PERMANENT ; /* make it permanent */ } /* 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) { memcpy(ncall.call,bp->via->call,ALEN) ; ncall.ssid = bp->via->call[ALEN] ; nr_routedrop(dest,&ncall,bp->via->interface) ; } } return 0 ; } /* Drop a route to dest via neighbor */ nr_routedrop(dest,neighbor,ifnum) struct ax25_addr *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(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(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(np) ; } return 0 ; } /* Find the best neighbor for destination dest, in arp format */ char * nr_getroute(dest) struct ax25_addr *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)) == NULLNRBIND) /* shouldn't happen! */ return NULLCHAR ; return bp->via->call ; } /* Find an entry in the filter table */ struct nrnf_tab * find_nrnf(addr,ifnum) register struct ax25_addr *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->interface == ifnum) { return fp ; } } return NULLNRNFTAB ; } /* Add an entry to the filter table. Return 0 on success, * -1 on failure */ int nr_nfadd(addr,ifnum) struct ax25_addr *addr ; unsigned ifnum ; { struct nrnf_tab *fp ; int16 hashval ; if (find_nrnf(addr,ifnum) != NULLNRNFTAB) return 0 ; /* already there; it's a no-op */ if ((fp = (struct nrnf_tab *)calloc(1,sizeof(struct nrnf_tab))) == NULLNRNFTAB) return -1 ; /* no storage */ hashval = nrhash(addr) ; fp->neighbor = *addr ; fp->interface = 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) struct ax25_addr *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(fp) ; return 0 ; }