|
|
BSD 4.3
/* pathalias -- by steve bellovin, as told to peter honeyman */
#ifndef lint
static char *sccsid = "@(#)mapit.c 8.1 (down!honey) 86/01/19";
#endif
#include "def.h"
/* privates */
extern void reheap(), insert(), heapswap();
extern link *min_node(), *rmlink();
extern Cost costof();
static long Nheap;
static long Heaphighwater;
static link **Heap;
/* transform the graph to a shortest-path tree by marking tree edges */
mapit()
{
register node *n, *next;
register link *l;
link *lprev, *lnext;
Cost cost;
/*
* re-use the hash table space for the heap.
*/
Heap = (link **) Table;
pack(); /* remove holes in the Table */
if (Linkout && *Linkout) /* dump cheapest links */
showlinks();
if (Graphout && *Graphout) /* dump the edge list */
dumpgraph();
/* invent and insert a link for Home to get things started */
l = newlink();
l->l_to = Home;
(void) dehash(Home);
insert(l);
/* main mapping loop */
remap:
Heaphighwater = Nheap;
while ((l = min_node()) != 0) {
l->l_flag |= LTREE;
n = l->l_to;
n->n_flag |= MAPPED;
/* add children to heap */
lprev = 0;
for (l = n->n_link; l != 0; l = lnext) {
next = l->l_to; /* neighboring node */
if (next->n_flag & MAPPED) {
lnext = rmlink(l, lprev, n);
continue;
}
cost = costof(n, l);
if (skiplink(l, n, cost)) {
lnext = rmlink(l, lprev, n);
continue;
}
/*
* put this link in the heap, in a place where it may
* percolate up, but not down. if new, or if cost is
* being increased, move to end. otherwise, cost is
* same or less, so leave it where it is. unfortunately,
* freeing a link already in the heap is too costly at
* this point.
*
* TODO: avoid heaping aliases and network members.
*/
if (dehash(next) == 0) /* first time in heap */
insert(l); /* insert at end */
else {
/* replace heaped link by this one */
if (cost > next->n_cost) { /* gateway */
/* move old link to end of heap */
heapswap((long) (next->n_tloc), Nheap);
next->n_tloc = Nheap;
}
Heap[next->n_tloc] = l;
}
next->n_cost = cost;
next->n_parent = n;
reheap(l); /* restore heap property */
/*
* note whether we got here via a gatewayed net.
* domains are presumed to require gateways.
* aliases inherit parent's gateway status.
*/
next->n_flag &= ~GATEWAYIN;
if (l->l_flag & LALIAS)
next->n_flag |= (n->n_flag & GATEWAYIN);
else if (GATEWAYED(n))
next->n_flag |= GATEWAYIN;
lprev = l; /* this link's a keeper */
lnext = l->l_next;
}
}
vprintf(stderr, "heap high water mark was %d\n", Heaphighwater);
/* sanity check on implementation */
if (Nheap != 0) {
fprintf(stderr, "%s: null entry found in heap\n", ProgName);
badmagic(1);
}
if (Hashpart < Tabsize) {
/*
* add back links from unreachable hosts to reachable
* neighbors, then remap. asymptotically, this is
* quadratic. in practice, this is done exactly once.
*/
backlinks();
if (Nheap)
goto remap;
}
if (Hashpart < Tabsize) {
fputs("You can't get there from here:\n", stderr);
for ( ; Hashpart < Tabsize; Hashpart++) {
fprintf(stderr, "\t%s", Table[Hashpart]->n_name);
if (Table[Hashpart]->n_flag & (ISPRIVATE|COLLISION))
fputs(" (private)", stderr);
putc('\n', stderr);
}
}
}
/*
* can this link be ignored? if yes, return 1, o.w. 0.
* a link can be skipped if it is not in the shortest path tree.
*/
STATIC int
skiplink(l, parent, cost)
link *l; /* new link to this node */
node *parent; /* new parent of this node */
Cost cost; /* new cost to this node */
{
node *n; /* this node */
link *lheap; /* existing link to this node */
n = l->l_to;
/* first time we've reached this node? */
if (n->n_tloc >= Hashpart)
return(0);
lheap = Heap[n->n_tloc];
/* examine links to nets that require gateways */
if (GATEWAYED(n)) {
/* if exactly one is a gateway, use it */
if ((lheap->l_flag & LGATEWAY) && !(l->l_flag & LGATEWAY))
return(1); /* old is gateway */
if (!(lheap->l_flag & LGATEWAY) && (l->l_flag & LGATEWAY))
return(0); /* new is gateway */
/* no gateway or both gateways; resolve in standard way ... */
}
/* examine dup link (sanity check) */
if (n->n_parent == parent && ((lheap->l_flag & LDEAD) || (l->l_flag & LDEAD))) {
fprintf(stderr, "%s: dup dead link not eliminated: %s -> %s\n",
ProgName, parent->n_name, n->n_name);
badmagic(1);
}
/* examine cost */
if (cost < n->n_cost)
return(0);
if (cost > n->n_cost)
return(1);
/* all other things being equal, consult the oracle */
return(tiebreaker(n, parent));
}
STATIC Cost
costof(prev, l)
register node *prev;
register link *l;
{
register node *next;
register Cost cost;
next = l->l_to;
if (l->l_flag & LALIAS) {
/* copy left/right bits */
next->n_flag &= ~(HASLEFT|HASRIGHT);
next->n_flag |= (prev->n_flag & (HASLEFT|HASRIGHT));
return(prev->n_cost); /* by definition */
}
cost = prev->n_cost + l->l_cost; /* basic cost */
/*
* heuristics:
* charge for a dead link.
* charge for getting out of a dead host.
* charge for getting into a gatewayed net (except at a gateway).
* discourage mixing of left and right syntax when next is a host.
* charge for leaving a gatewayed net.
*
* life was simpler when pathalias computed true shortest paths.
*/
if (l->l_flag & LDEAD) /* dead link */
cost += INF/2;
if (DEADHOST(prev)) /* dead host */
cost += INF/2;
if (GATEWAYED(next) && !(l->l_flag & LGATEWAY)) /* not gateway */
cost += INF/2;
if (!ISANET(next)) {
/* charge for mixed syntax here */
if ((NETDIR(l) == LLEFT && (prev->n_flag & HASRIGHT))
|| (NETDIR(l) == LRIGHT && (prev->n_flag & HASLEFT)))
cost += DEFCOST;
}
/*
* if reached by a gatewayed net, discourage further links.
* this has some relevance to common carriers and the FCC ...
* the penalty inheres to hosts, not aliases, nets, or domains.
*/
if ((prev->n_flag & GATEWAYIN) && !ISADOMAIN(prev) && !(prev->n_flag & NNET))
cost += INF/2; /* heavyweight, but appropriate */
/* set left/right bits */
next->n_flag &= ~(HASLEFT|HASRIGHT);
if (NETDIR(l) == LLEFT || (prev->n_flag & HASLEFT))
next->n_flag |= HASLEFT;
if (NETDIR(l) == LRIGHT || (prev->n_flag & HASRIGHT))
next->n_flag |= HASRIGHT;
return(cost);
}
STATIC link *
rmlink(l, lprev, n)
link *l, *lprev;
node *n;
{
link *lnext;
lnext = l->l_next;
if (lprev)
lprev->l_next = l->l_next;
else
n->n_link = l->l_next;
free((char *) l);
return(lnext);
}
/*
* binary heap implementation of priority queue.
* TODO: make the heap smaller by giving inserting a placeholder
* for net members when the net is extracted. this requires storing the
* cost of a net in the net node itself -- yuck. is it worth it?
*/
STATIC void
insert(l)
link *l;
{
register node *n;
n = l->l_to;
Heap[n->n_tloc] = 0;
if (Heap[Nheap+1] != 0) {
fprintf(stderr, "%s: heap error in insert\n", ProgName);
badmagic(1);
}
if (Nheap++ == 0) {
Heap[1] = l;
n->n_tloc = 1;
return;
}
if (Vflag && Nheap > Heaphighwater)
Heaphighwater = Nheap; /* diagnostics */
/* insert at the end. caller must reheap(). */
Heap[Nheap] = l;
n->n_tloc = Nheap;
}
/*
* replace existing link in heap by this one, then
* "percolate" up the heap by exchanging with the parent
*/
STATIC void
reheap(l)
link *l;
{
register long loc, parent;
register Cost cost;
register node *n, *np;
n = l->l_to;
cost = n->n_cost;
for (loc = n->n_tloc; loc > 1; loc = parent) {
parent = loc / 2;
/* sanity check on implementation */
if (Heap[parent] == 0) {
fprintf(stderr, "%s: heap error in insert\n", ProgName);
badmagic(1);
}
np = Heap[parent]->l_to;
if (cost > np->n_cost)
return;
/* move nets below hosts for output stability */
if (cost == np->n_cost && ((n->n_flag & NNET) || !(np->n_flag & NNET)))
return;
heapswap(loc, parent);
}
}
/* extract min (== Heap[1]) from heap */
STATIC link *
min_node()
{
link *rval;
register link **regheap;
register long i, child;
if (Nheap == 0)
return(0);
regheap = Heap; /* in register -- heavily used */
rval = regheap[1]; /* return this one */
/* move last entry into root, percolate down */
regheap[1] = regheap[Nheap];
regheap[1]->l_to->n_tloc = 1;
regheap[Nheap] = 0;
if (--Nheap == 0)
return(rval);
i = 1;
for (;;) {
/* swap with smaller child down the tree */
child = i * 2; /* lhs child is 2i, rhs is 2i+1. */
if (child >= Nheap)
return(rval);
/* use rhs child if smaller than lhs child */
if (regheap[child]->l_to->n_cost > regheap[child+1]->l_to->n_cost
|| (regheap[child]->l_to->n_cost == regheap[child+1]->l_to->n_cost
&& !ISANET(regheap[child+1]->l_to)))
child++;
if (regheap[i]->l_to->n_cost < regheap[child]->l_to->n_cost)
return(rval);
/* move nets below hosts for output stability */
if (regheap[i]->l_to->n_cost == regheap[child]->l_to->n_cost
&& (!ISANET(regheap[i]->l_to) || ISANET(regheap[child]->l_to)))
return(rval);
heapswap(i, child);
i = child;
}
/*NOTREACHED*/
}
/* exchange Heap[i] and Heap[j] pointers */
STATIC void
heapswap(i, j)
long i, j;
{
register link *temp, **regheap;
regheap = Heap; /* heavily used -- put in register */
temp = regheap[i];
regheap[i] = regheap[j];
regheap[j] = temp;
regheap[j]->l_to->n_tloc = j;
regheap[i]->l_to->n_tloc = i;
}
/* return 1 if n is already de-hashed (n_tloc < Hashpart), 0 o.w. */
dehash(n)
register node *n;
{
if (n->n_tloc < Hashpart)
return(1);
/* swap with entry in Table[Hashpart] */
Table[Hashpart]->n_tloc = n->n_tloc;
Table[n->n_tloc] = Table[Hashpart];
Table[Hashpart] = n;
n->n_tloc = Hashpart++;
return(0);
}
backlinks()
{
link *l;
node *n, *parent, *nomap;
long i;
for (i = Hashpart; i < Tabsize; i++) {
nomap = Table[i];
parent = 0;
for (l = nomap->n_link; l; l = l->l_next) {
n = l->l_to;
if (!(n->n_flag & MAPPED))
continue;
if (parent == 0) {
parent = n;
continue;
}
if (n->n_cost > parent->n_cost)
continue;
if (n->n_cost == parent->n_cost) {
nomap->n_parent = parent;
if (tiebreaker(nomap, n))
continue;
}
parent = n;
}
if (parent == 0)
continue;
(void) dehash(nomap);
l = addlink(parent, nomap, INF, DEFNET, DEFDIR);
nomap->n_parent = parent;
nomap->n_cost = costof(parent, l);
insert(l);
reheap(l);
}
vprintf(stderr, "%d backlinks\n", Nheap);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.