ToDo
This is an interim implementation that tries to work around an apparent bug in the Mentat router code (it seems to return the info for the wrong network in response to any query except "xr←nets𡤊t←hop"). Here everything is implemented by enumerating the nets by distance, assuming there's only one connected network (so the "immediate" field an a RoutingTableEntry is uninteresting) and using the default 0.5 sec overhead and 1.0 sec internet hop delay time values. The commented procs below ought to be used when the Mentat code gets fixed.
ConvertDotCardFromCLiteral and ConvertDotCLiteralFromCard ought to move to Convert.
Parameters and global data
smallBufSize: CARD ~ 100; -- this is big enough for most things ...
largeBufSize: CARD ~ 4096; -- this is the largest buffer Unix(tm) stream ioctl accepts ...
byteAddressedOrElse: BOOL[TRUE..TRUE] ¬ (BYTES[CARD32] = UNITS[CARD32]);
smallBuf: REF TEXT ¬ RefText.New[smallBufSize];
largeBuf: REF TEXT ¬ RefText.New[largeBufSize];
Types
Hops:
TYPE ~ XNSRouter.Hops;
maxHops: Hops ~ XNSRouter.maxHops;
unreachable: Hops ~ XNSRouter.unreachable;
Net:
TYPE ~
XNS.Net;
unknownNet: Net ~ XNS.unknownNet;
RoutingTableEntry: TYPE ~ XNSRouter.RoutingTableEntry;
ROPE: TYPE ~ Rope.ROPE;
Glue
XNSRouterHandle: TYPE ~ RECORD[h: INT]; -- opaque
ReturnCode: TYPE ~ INT;
BytePtr: TYPE ~ LONG POINTER TO BYTE;
IsError:
PROC [rc: ReturnCode]
RETURNS [err:
BOOL] ~
INLINE { err ¬ (rc < 0) };
ReturnCodeFromHandle: PROC [h: XNSRouterHandle] RETURNS [ReturnCode] ~ INLINE { RETURN [MIN[0, h.h]] };
XRCreateXNSRouterHandle:
PROC
RETURNS [h: XNSRouterHandle] ~
TRUSTED
MACHINE
CODE { "XR𡤌reateXNSRouterHandle" };
XRDestroyXNSRouterHandle: PROC [h: XNSRouterHandle] ~ TRUSTED MACHINE CODE { "XRstroyXNSRouterHandle" };
XRGetXNSRouterParameter:
UNSAFE PROC [h: XNSRouterHandle, buf: BytePtr, nBytes:
CARD32]
RETURNS [rc: ReturnCode] ~
UNCHECKED
MACHINE
CODE { "XR←GetXNSRouterParameter" };
XRSetXNSRouterParameter: PROC [h: XNSRouterHandle, buf: BytePtr, nBytes: CARD32] RETURNS [rc: ReturnCode] ~ TRUSTED MACHINE CODE { "XR←SetXNSRouterParameter" };
Utilities that ought to be in Convert
ConvertDotCardFromCLiteral:
PROC [r:
ROPE, defaultBase: Convert.Base ¬ 10]
RETURNS [ans:
CARD] ~ {
pos, len, lim: INT;
pos ¬ Rope.SkipTo[r, 0, "0123456789"];
len ¬ Rope.Length[r];
IF pos >= len THEN ERROR Convert.Error[syntax, len];
lim ¬ Rope.SkipOver[r, pos, "0123456789abcdefABCDEFxX"];
IF (pos > 0) OR (lim < len) THEN r ¬ Rope.Substr[r, pos, lim-pos];
SELECT
TRUE
FROM
Rope.IsPrefix["0x", r,
FALSE] => {
ans ¬ Convert.CardFromRope[Rope.Substr[r, 2], 16];
};
(Rope.Fetch[r, 0] = '0) => {
ans ¬ Convert.CardFromRope[r, 8];
};
ENDCASE => {
ans ¬ Convert.CardFromRope[r, defaultBase];
};
};
ConvertDotCLiteralFromCard:
PROC [card:
CARD, base:
CARD ¬ 10]
RETURNS [r:
ROPE] ~ {
IF card = 0 THEN RETURN ["0"];
SELECT base
FROM
8 => r ¬ IO.PutFR1["0%%b", IO.card[card]];
10 => r ¬ IO.PutFR1["%d", IO.card[card]];
16 => r ¬ IO.PutFR1["0x%x", IO.card[card]];
ENDCASE => ERROR Convert.Error[invalidBase, 0];
};
Private Procs
NetFromNDString:
PROC [nds:
ROPE]
RETURNS [net: Net] ~ {
DBMsg[NIL, IO.PutFR["NetFromNDString %s", IO.rope[nds]]];
net ¬ Basics.FFromCard32[Convert.CardFromRope[nds, 16]];
};
NDStringFromNet:
PROC [net: Net]
RETURNS [ndString:
ROPE] ~ {
temp: ROPE ¬ IO.PutFR1["%x", IO.card[Basics.Card32FromF[net]]];
len: INT ¬ Rope.Length[temp];
ndString ¬ IF len >= 8 THEN temp ELSE Rope.Replace["00000000", 8-len, len, temp];
};
AppendWord:
PROC [t:
REF
TEXT, w:
ROPE]
RETURNS [new:
REF
TEXT] ~ {
new ¬ RefText.AppendRope[to~t, from~w];
new ¬ RefText.AppendChar[to~new, from~VAL[0]];
};
FetchWord:
PROC [t:
REF
TEXT, start:
CARD]
RETURNS [next:
CARD, w:
ROPE] ~ {
IF start >= t.length THEN RETURN[start, NIL];
FOR next ¬ start, next.SUCC WHILE (next < t.length) AND (t[next] # VAL[0]) DO NULL ENDLOOP;
RETURN[w~Rope.FromRefText[t, start, (next-start)], next~next.SUCC];
};
Exported to XNSRouter
GetRouterParameterInternal:
INTERNAL
PROC [h: XNSRouterHandle, buf:
REF
TEXT, key:
ROPE, subKey:
ROPE ¬
NIL]
RETURNS [ok:
BOOL ¬
FALSE] ~ {
rc: ReturnCode;
buf.length ¬ 0;
buf ¬ AppendWord[buf, key];
buf ¬ AppendWord[buf, subKey];
buf ¬ AppendWord[buf, NIL]; -- n.b. appending two NILs is okay ...
{ temp:
REF
TEXT;
temp ¬ RefText.Append[to~RefText.New[buf.length], from~buf];
DBMsg[temp, "calling GetRouterParameterInternal"];
};
TRUSTED {
rc ¬ XRGetXNSRouterParameter[ h, LOOPHOLE[buf, BytePtr]+BYTES[TEXT[0]], buf.maxLength];
};
IF
NOT IsError[rc]
THEN {
buf.length ¬ buf.maxLength;
ok ¬ TRUE;
};
};
NewGetHops:
PUBLIC
ENTRY
PROC [net: Net]
RETURNS [hops: Hops] ~ {
h: XNSRouterHandle;
hops ¬ unreachable;
h ¬ XRCreateXNSRouterHandle[];
IF
NOT IsError[ReturnCodeFromHandle[h]]
THEN {
{
ENABLE Convert.Error => {
DBMsg[smallBuf, "convert.err"];
CONTINUE;
};
netLiteral, r: ROPE;
netLiteral ¬ NDStringFromNet[net];
IF GetRouterParameterInternal[h, smallBuf, "xr←hops", netLiteral].ok
THEN {
[w~r] ¬ FetchWord[smallBuf, 0];
hops ¬ Convert.CardFromDecimalLiteral[r];
};
};
XRDestroyXNSRouterHandle[h];
};
};
NewGetRouting:
PUBLIC
ENTRY
PROC [net: Net]
RETURNS [rte: RoutingTableEntry] ~ {
h: XNSRouterHandle;
rte ¬ [hops~unreachable, immediate~unknownNet, delay~CARD.LAST];
h ¬ XRCreateXNSRouterHandle[];
IF
NOT IsError[ReturnCodeFromHandle[h]]
THEN {
{
r, netRope: ROPE;
netRope ¬ NDStringFromNet[net];
IF GetRouterParameterInternal[h, smallBuf, "xr←hops", netRope].ok
THEN {
ENABLE Convert.Error => CONTINUE;
[w~r] ¬ FetchWord[smallBuf, 0];
rte.hops ¬ Convert.CardFromDecimalLiteral[r];
};
IF GetRouterParameterInternal[h, smallBuf, "xrst←relative←netid", netRope].ok
THEN {
ENABLE Convert.Error => CONTINUE;
[w~r] ¬ FetchWord[smallBuf, 0];
rte.immediate ¬ NetFromNDString[r];
};
IF GetRouterParameterInternal[h, smallBuf, "xr←rtt", netRope].ok
THEN {
ENABLE Convert.Error => CONTINUE;
[w~r] ¬ FetchWord[smallBuf, 0];
rte.delay ¬ ConvertDotCardFromCLiteral[r];
};
};
XRDestroyXNSRouterHandle[h];
};
};
EnumerateInner:
INTERNAL
PROC [low, high: Hops, proc:
PROC[Net, RoutingTableEntry]
RETURNS [
BOOL]] ~ {
h: XNSRouterHandle;
rte: RoutingTableEntry;
pos: INT;
netRope: ROPE;
h ¬ XRCreateXNSRouterHandle[];
IF
NOT IsError[ReturnCodeFromHandle[h]]
THEN {
directlyConnectedNet: Net;
IF NOT GetRouterParameterInternal[h, largeBuf, "xr←nets𡤊t←hop", "0"].ok
THEN GOTO Out;
[w~netRope] ¬ FetchWord[largeBuf, 0];
directlyConnectedNet ¬ NetFromNDString[netRope ! Convert.Error => GOTO Out];
FOR hops: Hops
IN [low .. high]
DO
-- for each distance
hopsLiteral: ROPE ¬ ConvertDotCLiteralFromCard[hops];
IF GetRouterParameterInternal[h, largeBuf, "xr←nets𡤊t←hop", hopsLiteral].ok
THEN {
pos ¬ 0;
DO
-- for each net at that distance
[pos, netRope] ¬ FetchWord[largeBuf, pos];
IF Rope.IsEmpty[netRope] THEN EXIT;
rte ¬ [hops~hops, immediate~unknownNet, delay~CARD.LAST];
IF GetRouterParameterInternal[h, smallBuf, "xrst←relative←netid", netRope].ok THEN {
immRope: ROPE;
[w~immRope] ← FetchWord[smallBuf, 0];
rte.immediate ← NetFromNDString[immRope ! Convert.Error => CONTINUE];
};
rte.immediate ¬ directlyConnectedNet; -- TEMPORARY KLUDGE
IF GetRouterParameterInternal[h, smallBuf, "xr←rtt", netRope].ok THEN {
delayRope: ROPE;
[w~delayRope] ← FetchWord[smallBuf, 0];
rte.delay ← ConvertDotCardFromCLiteral[delayRope ! Convert.Error => CONTINUE];
};
rte.delay ¬ 1000 * hops + 500; -- TEMPORARY KLUDGE
{
ENABLE Convert.Error =>
CONTINUE;
net: Net ¬ NetFromNDString[netRope];
IF NOT proc[ net, rte ] THEN GOTO Out
};
ENDLOOP; -- for each net at that distance
};
ENDLOOP; -- for each distance
GOTO Out;
EXITS
Out => XRDestroyXNSRouterHandle[h];
};
};
Enumerate:
PUBLIC
ENTRY
PROC [low, high: Hops, proc:
PROC[Net, RoutingTableEntry]] ~ {
ENABLE UNWIND => NULL;
MyProc:
PROC [n: Net, rte: RoutingTableEntry]
RETURNS [continue:
BOOL] ~ {
proc[n, rte];
RETURN[TRUE];
};
EnumerateInner[low, high, MyProc];
};
GetHops:
PUBLIC
ENTRY
PROC [net: Net]
RETURNS [hops: Hops ¬ unreachable] ~ {
ENABLE UNWIND => NULL;
MyProc:
PROC [n: Net, rte: RoutingTableEntry]
RETURNS[
BOOL] ~ {
IF n = net THEN { hops ¬ rte.hops; RETURN [FALSE] };
RETURN [TRUE];
};
EnumerateInner[0, maxHops, MyProc];
};
GetRouting:
PUBLIC
ENTRY
PROC [net: Net]
RETURNS [entry: RoutingTableEntry] ~ {
ENABLE UNWIND => NULL;
MyProc:
PROC [n: Net, rte: RoutingTableEntry]
RETURNS[
BOOL] ~ {
IF n = net THEN { entry ¬ rte; RETURN [FALSE] };
RETURN [TRUE];
};
entry ¬ [unreachable, unknownNet, CARD.LAST];
EnumerateInner[0, maxHops, MyProc];
};
GetNetsAtHops:
ENTRY
PROC [hops: Hops]
RETURNS [list:
LIST
OF
ROPE ¬
NIL] ~ {
ENABLE UNWIND => NULL;
MyProc:
PROC [n: Net, rte: RoutingTableEntry]
RETURNS [
BOOL] ~ {
list ¬ CONS[NDStringFromNet[n], list];
RETURN [TRUE];
};
EnumerateInner[hops, hops, MyProc];
};
}.