Porting notes¶
Reference for maintaining the two adapters and the NS-2 patch.
Bugs fixed during extraction¶
These were latent in the original NS-2 module and are fixed in core/:
- Header-resident heap pointers. The packet header stored
AntTimeEntry**malloc'd arrays. NS-2 pools and byte-copies header memory, so broadcasting an ant duplicated the pointer and led to a double-free; the arrays also leaked, andsize()reportedsizeof(pointer)rather than the real path length. Replaced byAntMessagevalue types + a length-prefixed codec. - Evaporation aged the wrong link.
updateRegularPheromoneevaporated / removedneighbor(the link the ant travelled) inside the loop branch meant for the other links, so competing links never decayed. Now operates on the iterated link. - 8-bit sequence number.
seqNumwasu_int8_tand wrapped after 256 ants, aliasing ant 256 onto ant 0 in(src,seq)dedup. Widened to 32-bit. - Unbounded dedup set. The
(src,seq)history grew for the whole run. Now FIFO-capped (Config::maxHistory). randomDestinationnever randomised. Integer divisioncount/sizewas 0 for every entry but the last. Now a proper uniform draw.
What an adapter actually does (NS-2 vs NS-3)¶
The same three steps in both: decode to AntMessage → call the core →
execute the returned RouteDecisions. Only the simulator-facing calls
differ. This is the contract a third adapter (OMNeT++/INET,
#32) has to satisfy.
sequenceDiagram
autonumber
participant SIM as simulator
participant AD as adapter
participant CO as AntRouterLogic (core)
SIM->>AD: packet arrives
Note over AD: NS-2: AntPacketHeader (POD, pooled)<br/>NS-3: AntHeader : ns3::Header
AD->>AD: decode via AntMessageCodec<br/>(canonical little-endian, shared)
AD->>CO: onReceiveAnt(msg, prevHop)
Note over CO: pure — no I/O, no simulator types.<br/>Clock/RNG reached only through ports
CO-->>AD: a list of RouteDecision
loop each decision
alt Unicast / Broadcast
AD->>AD: encode AntMessage back to the header
Note over AD: NS-2: Scheduler::schedule + send()<br/>NS-3: Simulator::Schedule + Socket::SendTo
AD->>SIM: transmit
else Queue
AD->>AD: hold in the pending queue<br/>(released by a later Deliver)
else Deliver
AD->>SIM: hand to the local transport,<br/>flush the pending queue for that dest
else Drop / None
Note over AD: nothing leaves the node
end
end
Ports the adapter must supply for the core to stay I/O-free — the whole seam, and the entire porting checklist:
| Port | NS-2 | NS-3 |
|---|---|---|
IClock |
Scheduler::instance().clock() |
Simulator::Now() |
IRng |
Random |
UniformRandomVariable |
ITimerScheduler |
Scheduler::schedule |
Simulator::Schedule |
INeighborProvider |
pheromone-table view (advisory only — never evicts, ADR-0008) | same |
The asymmetry that makes NS-2 the harder target is not in this diagram: NS-3
receives an additive contrib/ module, while NS-2 needs edits inside the
simulator's own tree, which is why that side ships as an idempotent
anchor-based patch (ADR-0005) rather
than a drop-in — see the anchors below.
NS-2 patch anchors¶
ns2/patch/apply-patch.sh injects by these stable anchors (never line
numbers). If an upstream release moves one, the script fails loudly (missing
anchor) rather than corrupting the file.
| File | Anchor (regex) | Edit |
|---|---|---|
common/packet.h |
PT_NTYPE = |
insert PT_ANT (= old PT_NTYPE), bump PT_NTYPE |
common/packet.h |
if (type == PT_DSR |
early if (type == PT_ANT) return ROUTING; |
common/packet.h |
name_[PT_AODV] = |
name_[PT_ANT] = "AntHocNet"; |
trace/cmu-trace.h |
void format_aodv( |
format_anthocnet declaration |
trace/cmu-trace.cc |
first #include |
include ant_packet_ns2.h |
trace/cmu-trace.cc |
case PT_AODV: |
case PT_ANT: before it |
trace/cmu-trace.cc |
(EOF) | format_anthocnet implementation |
Makefile.in |
aodv/aodv.o |
AntHocNet + core .o files |
tcl/lib/ns-packet.tcl |
AODV token |
register AntHocNet header |
tcl/lib/ns-default.tcl |
(EOF) | Agent/AntHocNet defaults |
tcl/lib/ns-lib.tcl |
switch -exact $routingAgent_ { |
AntHocNet case |
tcl/lib/ns-lib.tcl |
(EOF) | create-ahn-agent instproc |
Idempotency / markers¶
Marked edits are wrapped in // ANTHOCNET-BEGIN <tag> / // ANTHOCNET-END
<tag> (or # ... in top-level Tcl). Two contexts cannot take a comment
marker, because # is not a comment there — so those are inserted unmarked and
guarded by a content grep instead:
- inside the Tcl
foreach/switchbraces (a#becomes a list element); - inside the backslash-continued
OBJ_CC =list inMakefile.in(a#would truncate the make variable).
revert-patch.sh strips the marked regions and deletes the unmarked inserts by
pattern, restoring PT_NTYPE to the value PT_ANT had taken. Validated on a
synthetic tree: apply is idempotent and revert restores byte-for-byte.
Dynamic PT_ANT¶
PT_ANT is assigned the tree's current PT_NTYPE value at install time and
PT_NTYPE is bumped, so the id never collides across NS-2 releases (2.34 and
2.35 ship different built-in packet-type counts).
Wire format¶
The byte-level layout, the version byte, and the field-by-field diff against the
original implementation and the protocol papers live in
wire-format.md (the single source of truth).
In short: core/include/anthocnet/core/ant_message_codec.h defines the canonical
little-endian layout, prefixed by a 1-byte kWireVersion
(ADR-0006). The NS-2 header
(ant_packet_ns2) and the NS-3 header (AntHeader) follow the same field order.
The NS-3 header serializes directly against Buffer::Iterator; the NS-2 header
is a fixed-capacity POD whose wireSize() reports the same byte count the codec
would emit. Any field or semantic change bumps kWireVersion.
Version caveats¶
- The core uses C++14 (aggregate init with default member initializers). Modern
g++ defaults to C++14+. For an old NS-2 toolchain, add
-std=c++14toCCOPTin$(NS2DIR)/Makefile. - NS-3 module targets 3.36+ (CMake); the
wscriptcovers waf-era ns-3. - Cross-simulator metric parity is not guaranteed — the MAC/PHY models differ.