Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add BRSKI (CCM) commissioning and sim-hosts support #157

Merged
merged 36 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
76a52a4
[pylibs] Add CCM-commissioning unit test; extend Commissioning unit t…
EskoDijk May 12, 2024
2cd6444
[pylibs] test extended to include normal and CCM joiner in one test.
EskoDijk May 13, 2024
42ac323
Rebased to 'main' branch.
EskoDijk May 14, 2024
fe47624
WIP - sending of UDP and IPv6 datagrams to simulator by BR.
EskoDijk May 14, 2024
7e446bb
WIP - extended Udp/Ip6 event structure to contain metadata and select…
EskoDijk May 15, 2024
69f8ff3
Rebased to 'main' branch.
EskoDijk May 17, 2024
47d619c
WIP build and sim_hosts unit test fixed.
EskoDijk May 17, 2024
76fec3d
WIP - added UDP return event and bugfixing.
EskoDijk May 18, 2024
b0a4c6a
Rebased to 'main' branch.
EskoDijk May 19, 2024
344713f
WIP: added UDP checksum calculation in OTNS; make-pretty
EskoDijk May 20, 2024
aaf5771
WIP: adding UdpToHost message handling for relay
EskoDijk May 21, 2024
f82783b
[ot-rfsim] fixed filter on outgoing (off mesh) packets
EskoDijk May 21, 2024
8aa745a
Rebased to 'main' branch.
EskoDijk May 21, 2024
26514e8
WIP - Extended SimHosts unit tests and fixed aiocoap async use
EskoDijk May 22, 2024
44b03d9
WIP / Rebase to 'main' branch including 'openthread' submodule differ…
EskoDijk May 24, 2024
4da19bf
[ot-rfsim] fix build for FTD
EskoDijk May 25, 2024
7f7df31
make-pretty
EskoDijk Jun 18, 2024
f794237
[event] fix unit test post-rebase
EskoDijk Jun 18, 2024
cc1a7b8
[pylibs][script] fix aiocoap install for unit-tests only
EskoDijk Jun 18, 2024
2300e09
[ot-rfsim] fix script (remove merge artifacts)
EskoDijk Jun 18, 2024
8e1ca1d
[dispatcher][pylibs] added HostEvents counter
EskoDijk Jun 18, 2024
bb688ae
[dispatcher][pylibs] added HostEvents counter
EskoDijk Jun 18, 2024
e4ca79e
[pylibs] fix unit test (TMF Coap messages may be sent in parallel of …
EskoDijk Jun 18, 2024
bdd0548
[pcap] insert a dummy 802.15.4 frame at start of every PCAP file, to …
EskoDijk Jun 21, 2024
bf09f36
[pcap] insert a dummy 802.15.4 frame at start of every PCAP file, to …
EskoDijk Jun 21, 2024
e780c69
[pylibs] added further comments in partial_dataset case study.
EskoDijk Jun 22, 2024
a6b4642
[pcap] fix unit test, related to introduction of t=0 time-reference f…
EskoDijk Jun 22, 2024
5e974f7
[pcap] fix unit test, related to introduction of t=0 time-reference f…
EskoDijk Jun 22, 2024
9681cca
[ot-rfsim] fix macos build error (due to warn)
EskoDijk Jun 22, 2024
8afac15
[.github] restrict py-ver-unittests to Ubuntu OS, for the moment (mac…
EskoDijk Jun 22, 2024
bc892ac
[ot-rfsim] add C lang definitions for macOS build cpp in ot-rfsim pro…
EskoDijk Jun 22, 2024
1bfc612
[ot-rfsim] adding 'a' in front of function call parameters; and minor…
EskoDijk Jun 22, 2024
bb6136e
[pylibs] added more documentation to test_sim_hosts and fixed testing…
EskoDijk Jun 22, 2024
4ba0577
[ot-rfsim] fix build for v1.1, v1.2 for using ot....() platform loggi…
EskoDijk Jun 22, 2024
b990d29
[pylibs] fixes to test_commissioning - test sometimes failed.
EskoDijk Jun 23, 2024
b182dec
[pylibs] narrow test topology to avoid spurious test fails.
EskoDijk Jun 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,27 @@ jobs:
- name: Test single OT-version
run: |
./script/test py-unittests

py-ver-unittests:
name: Unittests-Version (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
python-version: ['3.10']
os: [ubuntu-22.04]
env:
HOMEBREW_NO_AUTO_UPDATE: 1
steps:
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- uses: actions/checkout@v3
- name: Test multiple OT-versions
run: |
./script/test py-ver-unittests
Expand Down
56 changes: 56 additions & 0 deletions cli/CmdRunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import (
"github.com/pkg/errors"
"gopkg.in/yaml.v3"

"net/netip"

"github.com/openthread/ot-ns/dispatcher"
"github.com/openthread/ot-ns/logger"
"github.com/openthread/ot-ns/progctx"
Expand Down Expand Up @@ -321,6 +323,8 @@ func (rt *CmdRunner) execute(cmd *Command, output io.Writer) {
rt.executeSave(cc, cmd.Save)
} else if cmd.Send != nil {
rt.executeSend(cc, cmd.Send)
} else if cmd.Host != nil {
rt.executeHost(cc, cmd.Host)
} else {
logger.Panicf("unimplemented command: %#v", cmd)
}
Expand Down Expand Up @@ -1467,3 +1471,55 @@ func (rt *CmdRunner) executeSend(cc *CommandContext, cmd *SendCmd) {
}
})
}

func (rt *CmdRunner) executeHost(cc *CommandContext, cmd *HostCmd) {
rt.postAsyncWait(cc, func(sim *simulation.Simulation) {
var addr netip.Addr
var err error

if cmd.IpAddr != nil {
addr, err = netip.ParseAddr(cmd.IpAddr.Addr)
if err != nil {
cc.errorf("invalid IPv6 address argument: '%s'", cmd.IpAddr.Addr)
return
}
}

host := simulation.SimHostEndpoint{
HostName: cmd.Hostname,
Ip6Addr: addr,
Port: cmd.Port,
PortMapped: cmd.PortMapped,
}

switch cmd.SubCmd {
case "add":
if cmd.Port == 0 || cmd.PortMapped == 0 {
cc.errorf("invalid port argument(s)")
return
}
if err = sim.SimHosts().AddHost(host); err != nil {
cc.errorf("could not add host: %v", err)
return
}
case "del":
if len(cmd.Hostname) == 0 {
cc.errorf("missing <hostname> or <ipaddr> argument")
return
}
addr, _ = netip.ParseAddr(cmd.Hostname)
for h := range sim.SimHosts().Hosts {
if cmd.Hostname == h.HostName || addr == h.Ip6Addr {
sim.SimHosts().RemoveHost(h)
}
}

case "list":
sh := sim.SimHosts()
cc.outputf("Hostname Destination IPv6 address DstPort MapPort RxBytes TxBytes\n")
for h := range sim.SimHosts().Hosts {
cc.outputf("%-40s %-40s % 6d % 6d % 8d % 8d\n", h.HostName, h.Ip6Addr.String(), h.Port, h.PortMapped, sh.GetRxBytes(&h), sh.GetTxBytes(&h))
}
}
})
}
18 changes: 18 additions & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,24 @@ Show help text for specific, or all, OTNS CLI commands.
help [ <command> ]
```

### host

Add a simulated IP host for Thread nodes to communicate with.

```shell
host add "<hostname>" "<ipaddr>" <port> <maps-to-local-port>
host del "<hostname>" | "<ipaddr>"
host list
```

Any UDP/TCP packets sent to an off-mesh destination by a Thread node will first be routed over the mesh to a Thread
Border Router (node type `br`). The BR will notify OTNS about such received packets. OTNS then looks up if any
simulated IP host matches the packet's destination address/port and if so, it delivers the packet locally (localhost)
to the mapped port number `<maps-to-local-port>`. This enables simulations with the behavior of Thread-external servers
to be done fully locally, without causing any real network traffic.

Note: currently only IPv6 hosts are supported; IPv4 (via NAT64) may be added later.

### joins

Displays finished joiner sessions.
Expand Down
11 changes: 11 additions & 0 deletions cli/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type Command struct {
Exit *ExitCmd `| @@` //nolint
Go *GoCmd `| @@` //nolint
Help *HelpCmd `| @@` //nolint
Host *HostCmd `| @@` //nolint
Joins *JoinsCmd `| @@` //nolint
Kpi *KpiCmd `| @@` //nolint
Load *LoadCmd `| @@` //nolint
Expand Down Expand Up @@ -581,6 +582,16 @@ type SendCmd struct {
DataSize *DataSizeFlag `[ @@ ]` //nolint
}

// noinspection GoVetStructTag
type HostCmd struct {
Cmd struct{} `"host"` //nolint
SubCmd string `@("add"|"del"|"list")` //nolint
Hostname string `[ @String ]` //nolint
IpAddr *Ipv6Address `[ @@ ]` //nolint
Port uint16 `[ @Int ]` //nolint
PortMapped uint16 `[ @Int ]` //nolint
}

// noinspection GoVetStructTag
type FailTimeParams struct {
Dummy struct{} `"ft"` //nolint
Expand Down
3 changes: 3 additions & 0 deletions cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ func TestParseBytes(t *testing.T) {
assert.Nil(t, parseBytes([]byte("go 100 speed 2"), &cmd))
assert.NotNil(t, cmd.Go)

assert.True(t, parseBytes([]byte("help"), &cmd) == nil && cmd.Help != nil)
assert.True(t, parseBytes([]byte("host add \"myhost.example.com\" \"fc00::1234\" 35683 1717"), &cmd) == nil && cmd.Host != nil)

assert.True(t, parseBytes([]byte("joins"), &cmd) == nil && cmd.Joins != nil)

assert.True(t, parseBytes([]byte("log"), &cmd) == nil && cmd.LogLevel != nil)
Expand Down
3 changes: 3 additions & 0 deletions dispatcher/FailureCtrl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ func (m mockDispatcherCallback) OnNextEventTime(nextTimeUs uint64) {
func (m mockDispatcherCallback) OnRfSimEvent(nodeid NodeId, evt *event.Event) {
}

func (m mockDispatcherCallback) OnMsgToHost(nodeid NodeId, evt *event.Event) {
}

func mockNode1() *Node {
return &Node{
Id: 0x1,
Expand Down
3 changes: 2 additions & 1 deletion dispatcher/Node.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ func (node *Node) sendEvent(evt *Event) {

// time keeping - move node's time to the current send-event's time.
node.D.alarmMgr.SetNotified(node.Id)
node.D.setAlive(node.Id)
node.CurTime += evt.Delay
logger.AssertTrue(node.CurTime == node.D.CurTime)

Expand All @@ -203,6 +202,8 @@ func (node *Node) sendEvent(evt *Event) {
if err != nil {
node.logger.Error(err)
node.err = err
} else {
node.D.setAlive(node.Id)
}
}

Expand Down
10 changes: 4 additions & 6 deletions dispatcher/coaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
package dispatcher

import (
"github.com/openthread/ot-ns/logger"
. "github.com/openthread/ot-ns/types"
)

Expand Down Expand Up @@ -81,11 +80,11 @@ func (coaps *coapsHandler) OnSend(curTime uint64, nodeId NodeId, messageId int,

func (coaps *coapsHandler) OnRecv(curTime uint64, nodeId NodeId, messageId int, coapType CoapType, coapCode CoapCode, uri string, peerAddr string, peerPort int) {
msg := coaps.findMessage(messageId, coapType, coapCode, uri)
if msg == nil {
logger.Warnf("CoAP message %d,%d,%d,%s not sent but received by Node %d", messageId, coapType, coapCode, uri, nodeId)
if msg == nil { // may happen if CoAP message originated from outside of mesh (external process)
return
}

// keep all received CoAP messages for KPI purposes.
msg.Receivers = append(msg.Receivers, CoapMessageRecvInfo{
Timestamp: curTime,
DstNode: nodeId,
Expand All @@ -96,12 +95,11 @@ func (coaps *coapsHandler) OnRecv(curTime uint64, nodeId NodeId, messageId int,

func (coaps *coapsHandler) OnSendError(nodeId NodeId, messageId int, coapType CoapType, coapCode CoapCode, uri string, peerAddr string, peerPort int, error string) {
msg := coaps.findMessage(messageId, coapType, coapCode, uri)
if msg == nil {
logger.Warnf("CoAP message %d,%d,%d,%s not sent but received by Node %d", messageId, coapType, coapCode, uri, nodeId)
if msg == nil { // may happen if CoAP message originated from outside of mesh (external process) ?
return
}

msg.Error = error
msg.Error = error // mark the message as having had a send error
}

func (coaps *coapsHandler) findMessage(id int, coapType CoapType, coapCode CoapCode, uri string) *CoapMessage {
Expand Down
46 changes: 42 additions & 4 deletions dispatcher/dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,22 @@ import (
"github.com/openthread/ot-ns/visualize"
)

// CallbackHandler handles callbacks from Dispatcher to its managing entity (e.g. a Simulation).
type CallbackHandler interface {
// OnUartWrite Notifies that the node's UART was written with data.
OnUartWrite(nodeid NodeId, data []byte)

// OnLogWrite Notifies that a log item wsa written to the node's log.
OnLogWrite(nodeid NodeId, data []byte)

// OnNextEventTime Notifies that the Dispatcher simulation will move shortly to the next event time.
// OnNextEventTime Notifies that the Dispatcher simulated-time will move to the next event time.
OnNextEventTime(nextTimeUs uint64)

// OnRfSimEvent Notifies that Dispatcher received an OT-RFSIM platform event that it didn't handle itself.
OnRfSimEvent(nodeid NodeId, evt *Event)

// OnMsgToHost Notifies that the Dispatcher received a message from a node, to be handled by the node's host.
OnMsgToHost(nodeid NodeId, event *Event)
}

// goDuration represents a particular duration of the simulation at a given speed.
Expand All @@ -84,6 +88,7 @@ type Dispatcher struct {
StatusPushEvents uint64
UartWriteEvents uint64
LogWriteEvents uint64
HostEvents uint64
OtherEvents uint64
// Packet-related event dispatching counters
DispatchByExtAddrSucc uint64
Expand Down Expand Up @@ -168,7 +173,7 @@ func NewDispatcher(ctx *progctx.ProgCtx, cfg *Config, cbHandler CallbackHandler)
}
d.speed = d.normalizeSpeed(d.speed)
if d.cfg.PcapEnabled {
d.pcap, err = pcap.NewFile("current.pcap", cfg.PcapFrameType)
d.pcap, err = pcap.NewFile("current.pcap", cfg.PcapFrameType, true)
logger.PanicIfError(err)
d.waitGroup.Add(1)
go d.pcapFrameWriter()
Expand Down Expand Up @@ -376,7 +381,7 @@ func (d *Dispatcher) goSimulateForDuration(duration goDuration) {
}
}

// handleRecvEvent is the central handler for all events externally received from OpenThread nodes.
// handleRecvEvent is the central handler for all events externally received from nodes/entities.
// It may only process events immediately that are to be executed at time d.CurTime. Future events
// will need to be queued (scheduled).
func (d *Dispatcher) handleRecvEvent(evt *Event) {
Expand All @@ -387,7 +392,9 @@ func (d *Dispatcher) handleRecvEvent(evt *Event) {
return
}

node.conn = evt.Conn // store socket connection for this node.
if node.conn == nil {
node.conn = evt.Conn // store socket connection for this node.
}
evt.Timestamp = d.CurTime // timestamp the incoming event

// TODO document this use (for alarm messages)
Expand Down Expand Up @@ -428,6 +435,16 @@ func (d *Dispatcher) handleRecvEvent(evt *Event) {
logger.Debugf("%s socket disconnected.", node)
d.setSleeping(node.Id)
d.alarmMgr.SetTimestamp(node.Id, Ever)
case EventTypeUdpToHost,
EventTypeIp6ToHost:
d.Counters.HostEvents += 1
d.sendMsgToHost(node, evt)
d.cbHandler.OnMsgToHost(node.Id, evt)
case EventTypeUdpFromHost,
EventTypeIp6FromHost:
d.Counters.HostEvents += 1
evt.MustDispatch = true // asap resend again to the target (BR) node.
d.eventQueue.Add(evt)
default:
d.Counters.OtherEvents += 1
d.cbHandler.OnRfSimEvent(node.Id, evt)
Expand Down Expand Up @@ -570,6 +587,9 @@ func (d *Dispatcher) processNextEvent(simSpeed float64) bool {
d.sendRadioCommRxStartEvents(node, evt)
case EventTypeRadioRxDone:
d.sendRadioCommRxDoneEvents(node, evt)
case EventTypeUdpFromHost,
EventTypeIp6FromHost:
node.sendEvent(evt) // TODO no loss on external network is simulated currently.
default:
if d.radioModel.OnEventDispatch(node.RadioNode, node.RadioNode, evt) {
node.sendEvent(evt)
Expand Down Expand Up @@ -845,6 +865,20 @@ func (d *Dispatcher) sendOneRadioFrame(evt *Event, srcnode *Node, dstnode *Node)
}
}

func (d *Dispatcher) sendMsgToHost(node *Node, evt *Event) {
if d.cfg.PcapEnabled {
/** TODO write IPv6 packet to a separate pcap file
d.pcapFrameChan <- pcap.Frame{
Timestamp: evt.Timestamp,
Data: evt.Data,
Channel: 0,
Rssi: RssiInvalid,
}
*/
node.logger.Debugf("sendMsgToHost: %v", evt)
}
}

func (d *Dispatcher) setAlive(nodeid NodeId) {
logger.AssertFalse(d.isDeleted(nodeid))
d.aliveNodes[nodeid] = struct{}{}
Expand Down Expand Up @@ -1147,6 +1181,10 @@ func (d *Dispatcher) PostAsync(task func()) {
d.taskChan <- task
}

func (d *Dispatcher) PostEventAsync(ev *Event) {
d.eventChan <- ev
}

func (d *Dispatcher) handleTasks() {
defer func() {
err := recover()
Expand Down
Loading
Loading