install: updates to support building deb/rpm packages that contain python wheels and install core from a single file, updates to core to install scripts by way of python directly
This commit is contained in:
parent
cd6bb319ad
commit
fcf6f30302
54 changed files with 528 additions and 187 deletions
35
package/examples/configservices/switch.py
Normal file
35
package/examples/configservices/switch.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import logging
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.base import CoreNode
|
||||
from core.nodes.network import SwitchNode
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
# setup basic network
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
coreemu = CoreEmu()
|
||||
session = coreemu.create_session()
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
switch = session.add_node(SwitchNode)
|
||||
|
||||
# node one
|
||||
options = CoreNode.create_options()
|
||||
options.config_services = ["DefaultRoute", "IPForward"]
|
||||
node1 = session.add_node(CoreNode, options=options)
|
||||
interface = prefixes.create_iface(node1)
|
||||
session.add_link(node1.id, switch.id, iface1_data=interface)
|
||||
|
||||
# node two
|
||||
node2 = session.add_node(CoreNode, options=options)
|
||||
interface = prefixes.create_iface(node2)
|
||||
session.add_link(node2.id, switch.id, iface1_data=interface)
|
||||
|
||||
# start session and run services
|
||||
session.instantiate()
|
||||
|
||||
input("press enter to exit")
|
||||
session.shutdown()
|
53
package/examples/controlnet_updown
Executable file
53
package/examples/controlnet_updown
Executable file
|
@ -0,0 +1,53 @@
|
|||
#!/bin/bash
|
||||
# Sample controlnet up/down script that will be executed when the control
|
||||
# network is brought up or down. This script either adds an interface to the
|
||||
# controlnet bridge or adds a permissive iptables firewall rule.
|
||||
|
||||
controlnet_intf=$1
|
||||
action=$2
|
||||
|
||||
config_type=iptables # iptables or brctl
|
||||
|
||||
iptables_address=10.205.15.132
|
||||
brctl_intf=eth2
|
||||
|
||||
BRCTL=/sbin/brctl
|
||||
IPTABLES=/usr/sbin/iptables
|
||||
|
||||
case "$action" in
|
||||
startup)
|
||||
case "$config_type" in
|
||||
iptables)
|
||||
$IPTABLES -I FORWARD -i $controlnet_intf -d $iptables_address -j ACCEPT
|
||||
$IPTABLES -I FORWARD -o $controlnet_intf -s $iptables_address -j ACCEPT
|
||||
;;
|
||||
brctl)
|
||||
$BRCTL addif $controlnet_intf $brctl_intf
|
||||
;;
|
||||
*)
|
||||
echo "Invalid config_type $config_type"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
shutdown)
|
||||
case "$config_type" in
|
||||
iptables)
|
||||
$IPTABLES -D FORWARD -i $controlnet_intf -d $iptables_address -j ACCEPT
|
||||
$IPTABLES -D FORWARD -o $controlnet_intf -s $iptables_address -j ACCEPT
|
||||
;;
|
||||
brctl)
|
||||
$BRCTL delif $controlnet_intf $brctl_intf
|
||||
;;
|
||||
*)
|
||||
echo "Invalid config_type $config_type"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Invalid action $action"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit 0
|
35
package/examples/docker/docker2core.py
Normal file
35
package/examples/docker/docker2core.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import logging
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.base import CoreNode
|
||||
from core.nodes.docker import DockerNode
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
coreemu = CoreEmu()
|
||||
session = coreemu.create_session()
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
try:
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create node one
|
||||
options = DockerNode.create_options()
|
||||
options.image = "ubuntu"
|
||||
node1 = session.add_node(DockerNode, options=options)
|
||||
interface1_data = prefixes.create_iface(node1)
|
||||
|
||||
# create node two
|
||||
node2 = session.add_node(CoreNode)
|
||||
interface2_data = prefixes.create_iface(node2)
|
||||
|
||||
# add link
|
||||
session.add_link(node1.id, node2.id, interface1_data, interface2_data)
|
||||
|
||||
# instantiate
|
||||
session.instantiate()
|
||||
finally:
|
||||
input("continue to shutdown")
|
||||
coreemu.shutdown()
|
36
package/examples/docker/docker2docker.py
Normal file
36
package/examples/docker/docker2docker.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
import logging
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.docker import DockerNode
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
coreemu = CoreEmu()
|
||||
session = coreemu.create_session()
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create nodes and interfaces
|
||||
try:
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create node one
|
||||
options = DockerNode.create_options()
|
||||
options.image = "ubuntu"
|
||||
node1 = session.add_node(DockerNode, options=options)
|
||||
interface1_data = prefixes.create_iface(node1)
|
||||
|
||||
# create node two
|
||||
node2 = session.add_node(DockerNode, options=options)
|
||||
interface2_data = prefixes.create_iface(node2)
|
||||
|
||||
# add link
|
||||
session.add_link(node1.id, node2.id, interface1_data, interface2_data)
|
||||
|
||||
# instantiate
|
||||
session.instantiate()
|
||||
finally:
|
||||
input("continue to shutdown")
|
||||
coreemu.shutdown()
|
48
package/examples/docker/switch.py
Normal file
48
package/examples/docker/switch.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
import logging
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.base import CoreNode
|
||||
from core.nodes.docker import DockerNode
|
||||
from core.nodes.network import SwitchNode
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
# create core session
|
||||
coreemu = CoreEmu()
|
||||
session = coreemu.create_session()
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
try:
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create switch
|
||||
switch = session.add_node(SwitchNode)
|
||||
|
||||
# node one
|
||||
options = DockerNode.create_options()
|
||||
options.image = "ubuntu"
|
||||
node1 = session.add_node(DockerNode, options=options)
|
||||
interface1_data = prefixes.create_iface(node1)
|
||||
|
||||
# node two
|
||||
node2 = session.add_node(DockerNode, options=options)
|
||||
interface2_data = prefixes.create_iface(node2)
|
||||
|
||||
# node three
|
||||
node_three = session.add_node(CoreNode)
|
||||
interface_three = prefixes.create_iface(node_three)
|
||||
|
||||
# add links
|
||||
session.add_link(node1.id, switch.id, interface1_data)
|
||||
session.add_link(node2.id, switch.id, interface2_data)
|
||||
session.add_link(node_three.id, switch.id, interface_three)
|
||||
|
||||
# instantiate
|
||||
session.instantiate()
|
||||
|
||||
print(f"{node2.name}: {node2.volumes.values()}")
|
||||
finally:
|
||||
input("continue to shutdown")
|
||||
coreemu.shutdown()
|
0
package/examples/grpc/__init__.py
Normal file
0
package/examples/grpc/__init__.py
Normal file
64
package/examples/grpc/distributed_switch.py
Normal file
64
package/examples/grpc/distributed_switch.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
import argparse
|
||||
import logging
|
||||
|
||||
from core.api.grpc import client
|
||||
from core.api.grpc.wrappers import NodeType, Position, Server
|
||||
|
||||
|
||||
def log_event(event):
|
||||
logging.info("event: %s", event)
|
||||
|
||||
|
||||
def main(args):
|
||||
# helper to create interfaces
|
||||
interface_helper = client.InterfaceHelper(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create grpc client and connect
|
||||
core = client.CoreGrpcClient()
|
||||
core.connect()
|
||||
|
||||
# create session
|
||||
session = core.create_session()
|
||||
|
||||
# add distributed server
|
||||
server = Server(name="core2", host=args.server)
|
||||
session.servers.append(server)
|
||||
|
||||
# handle events session may broadcast
|
||||
core.events(session.id, log_event)
|
||||
|
||||
# create switch node
|
||||
position = Position(x=150, y=100)
|
||||
switch = session.add_node(1, _type=NodeType.SWITCH, position=position)
|
||||
position = Position(x=100, y=50)
|
||||
node1 = session.add_node(2, position=position)
|
||||
position = Position(x=200, y=50)
|
||||
node2 = session.add_node(3, position=position, server=server.name)
|
||||
|
||||
# create links
|
||||
iface1 = interface_helper.create_iface(node1.id, 0)
|
||||
session.add_link(node1=node1, node2=switch, iface1=iface1)
|
||||
iface1 = interface_helper.create_iface(node2.id, 0)
|
||||
session.add_link(node1=node2, node2=switch, iface1=iface1)
|
||||
|
||||
# start session
|
||||
core.start_session(session)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
parser = argparse.ArgumentParser(description="Run distributed_switch example")
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--address",
|
||||
required=True,
|
||||
help="local address that distributed servers will use for gre tunneling",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--server",
|
||||
required=True,
|
||||
help="distributed server to use for creating nodes",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
main(args)
|
38
package/examples/grpc/emane80211.py
Normal file
38
package/examples/grpc/emane80211.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
# required imports
|
||||
from core.api.grpc import client
|
||||
from core.api.grpc.wrappers import NodeType, Position
|
||||
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
|
||||
|
||||
# interface helper
|
||||
iface_helper = client.InterfaceHelper(ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64")
|
||||
|
||||
# create grpc client and connect
|
||||
core = client.CoreGrpcClient()
|
||||
core.connect()
|
||||
|
||||
# add session
|
||||
session = core.create_session()
|
||||
|
||||
# create nodes
|
||||
position = Position(x=200, y=200)
|
||||
emane = session.add_node(
|
||||
1, _type=NodeType.EMANE, position=position, emane=EmaneIeee80211abgModel.name
|
||||
)
|
||||
position = Position(x=100, y=100)
|
||||
node1 = session.add_node(2, model="mdr", position=position)
|
||||
position = Position(x=300, y=100)
|
||||
node2 = session.add_node(3, model="mdr", position=position)
|
||||
|
||||
# create links
|
||||
iface1 = iface_helper.create_iface(node1.id, 0)
|
||||
session.add_link(node1=node1, node2=emane, iface1=iface1)
|
||||
iface1 = iface_helper.create_iface(node2.id, 0)
|
||||
session.add_link(node1=node2, node2=emane, iface1=iface1)
|
||||
|
||||
# setup emane configurations using a dict mapping currently support values as strings
|
||||
emane.set_emane_model(
|
||||
EmaneIeee80211abgModel.name, {"eventservicettl": "2", "unicastrate": "3"}
|
||||
)
|
||||
|
||||
# start session
|
||||
core.start_session(session)
|
26
package/examples/grpc/peertopeer.py
Normal file
26
package/examples/grpc/peertopeer.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
from core.api.grpc import client
|
||||
from core.api.grpc.wrappers import Position
|
||||
|
||||
# interface helper
|
||||
iface_helper = client.InterfaceHelper(ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64")
|
||||
|
||||
# create grpc client and connect
|
||||
core = client.CoreGrpcClient()
|
||||
core.connect()
|
||||
|
||||
# add session
|
||||
session = core.create_session()
|
||||
|
||||
# create nodes
|
||||
position = Position(x=100, y=100)
|
||||
node1 = session.add_node(1, position=position)
|
||||
position = Position(x=300, y=100)
|
||||
node2 = session.add_node(2, position=position)
|
||||
|
||||
# create link
|
||||
iface1 = iface_helper.create_iface(node1.id, 0)
|
||||
iface2 = iface_helper.create_iface(node2.id, 0)
|
||||
session.add_link(node1=node1, node2=node2, iface1=iface1, iface2=iface2)
|
||||
|
||||
# start session
|
||||
core.start_session(session)
|
29
package/examples/grpc/switch.py
Normal file
29
package/examples/grpc/switch.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
from core.api.grpc import client
|
||||
from core.api.grpc.wrappers import NodeType, Position
|
||||
|
||||
# interface helper
|
||||
iface_helper = client.InterfaceHelper(ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64")
|
||||
|
||||
# create grpc client and connect
|
||||
core = client.CoreGrpcClient()
|
||||
core.connect()
|
||||
|
||||
# add session
|
||||
session = core.create_session()
|
||||
|
||||
# create nodes
|
||||
position = Position(x=200, y=200)
|
||||
switch = session.add_node(1, _type=NodeType.SWITCH, position=position)
|
||||
position = Position(x=100, y=100)
|
||||
node1 = session.add_node(2, position=position)
|
||||
position = Position(x=300, y=100)
|
||||
node2 = session.add_node(3, position=position)
|
||||
|
||||
# create links
|
||||
iface1 = iface_helper.create_iface(node1.id, 0)
|
||||
session.add_link(node1=node1, node2=switch, iface1=iface1)
|
||||
iface1 = iface_helper.create_iface(node2.id, 0)
|
||||
session.add_link(node1=node2, node2=switch, iface1=iface1)
|
||||
|
||||
# start session
|
||||
core.start_session(session)
|
29
package/examples/grpc/wireless.py
Normal file
29
package/examples/grpc/wireless.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
from core.api.grpc import client
|
||||
from core.api.grpc.wrappers import NodeType, Position
|
||||
|
||||
# interface helper
|
||||
iface_helper = client.InterfaceHelper(ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64")
|
||||
|
||||
# create grpc client and connect
|
||||
core = client.CoreGrpcClient()
|
||||
core.connect()
|
||||
|
||||
# add session
|
||||
session = core.create_session()
|
||||
|
||||
# create nodes
|
||||
position = Position(x=200, y=200)
|
||||
wlan = session.add_node(1, _type=NodeType.WIRELESS, position=position)
|
||||
position = Position(x=100, y=100)
|
||||
node1 = session.add_node(2, model="mdr", position=position)
|
||||
position = Position(x=300, y=100)
|
||||
node2 = session.add_node(3, model="mdr", position=position)
|
||||
|
||||
# create links
|
||||
iface1 = iface_helper.create_iface(node1.id, 0)
|
||||
session.add_link(node1=node1, node2=wlan, iface1=iface1)
|
||||
iface1 = iface_helper.create_iface(node2.id, 0)
|
||||
session.add_link(node1=node2, node2=wlan, iface1=iface1)
|
||||
|
||||
# start session
|
||||
core.start_session(session)
|
41
package/examples/grpc/wlan.py
Normal file
41
package/examples/grpc/wlan.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
from core.api.grpc import client
|
||||
from core.api.grpc.wrappers import NodeType, Position
|
||||
|
||||
# interface helper
|
||||
iface_helper = client.InterfaceHelper(ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64")
|
||||
|
||||
# create grpc client and connect
|
||||
core = client.CoreGrpcClient()
|
||||
core.connect()
|
||||
|
||||
# add session
|
||||
session = core.create_session()
|
||||
|
||||
# create nodes
|
||||
position = Position(x=200, y=200)
|
||||
wlan = session.add_node(1, _type=NodeType.WIRELESS_LAN, position=position)
|
||||
position = Position(x=100, y=100)
|
||||
node1 = session.add_node(2, model="mdr", position=position)
|
||||
position = Position(x=300, y=100)
|
||||
node2 = session.add_node(3, model="mdr", position=position)
|
||||
|
||||
# create links
|
||||
iface1 = iface_helper.create_iface(node1.id, 0)
|
||||
session.add_link(node1=node1, node2=wlan, iface1=iface1)
|
||||
iface1 = iface_helper.create_iface(node2.id, 0)
|
||||
session.add_link(node1=node2, node2=wlan, iface1=iface1)
|
||||
|
||||
# set wlan config using a dict mapping currently
|
||||
# support values as strings
|
||||
wlan.set_wlan(
|
||||
{
|
||||
"range": "280",
|
||||
"bandwidth": "55000000",
|
||||
"delay": "6000",
|
||||
"jitter": "5",
|
||||
"error": "5",
|
||||
}
|
||||
)
|
||||
|
||||
# start session
|
||||
core.start_session(session)
|
35
package/examples/lxd/lxd2core.py
Normal file
35
package/examples/lxd/lxd2core.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import logging
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.base import CoreNode
|
||||
from core.nodes.lxd import LxcNode
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
coreemu = CoreEmu()
|
||||
session = coreemu.create_session()
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
try:
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create node one
|
||||
options = LxcNode.create_options()
|
||||
options.image = "ubuntu"
|
||||
node1 = session.add_node(LxcNode, options=options)
|
||||
interface1_data = prefixes.create_iface(node1)
|
||||
|
||||
# create node two
|
||||
node2 = session.add_node(CoreNode)
|
||||
interface2_data = prefixes.create_iface(node2)
|
||||
|
||||
# add link
|
||||
session.add_link(node1.id, node2.id, interface1_data, interface2_data)
|
||||
|
||||
# instantiate
|
||||
session.instantiate()
|
||||
finally:
|
||||
input("continue to shutdown")
|
||||
coreemu.shutdown()
|
36
package/examples/lxd/lxd2lxd.py
Normal file
36
package/examples/lxd/lxd2lxd.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
import logging
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.lxd import LxcNode
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
coreemu = CoreEmu()
|
||||
session = coreemu.create_session()
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create nodes and interfaces
|
||||
try:
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create node one
|
||||
options = LxcNode.create_options()
|
||||
options.image = "ubuntu:18.04"
|
||||
node1 = session.add_node(LxcNode, options=options)
|
||||
interface1_data = prefixes.create_iface(node1)
|
||||
|
||||
# create node two
|
||||
node2 = session.add_node(LxcNode, options=options)
|
||||
interface2_data = prefixes.create_iface(node2)
|
||||
|
||||
# add link
|
||||
session.add_link(node1.id, node2.id, interface1_data, interface2_data)
|
||||
|
||||
# instantiate
|
||||
session.instantiate()
|
||||
finally:
|
||||
input("continue to shutdown")
|
||||
coreemu.shutdown()
|
46
package/examples/lxd/switch.py
Normal file
46
package/examples/lxd/switch.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
import logging
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.base import CoreNode
|
||||
from core.nodes.lxd import LxcNode
|
||||
from core.nodes.network import SwitchNode
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
coreemu = CoreEmu()
|
||||
session = coreemu.create_session()
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
try:
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create switch
|
||||
switch = session.add_node(SwitchNode)
|
||||
|
||||
# node one
|
||||
options = LxcNode.create_options()
|
||||
options.image = "ubuntu"
|
||||
node1 = session.add_node(LxcNode, options=options)
|
||||
interface1_data = prefixes.create_iface(node1)
|
||||
|
||||
# node two
|
||||
node2 = session.add_node(LxcNode, options=options)
|
||||
interface2_data = prefixes.create_iface(node2)
|
||||
|
||||
# node three
|
||||
node3 = session.add_node(CoreNode)
|
||||
interface3_data = prefixes.create_iface(node3)
|
||||
|
||||
# add links
|
||||
session.add_link(node1.id, switch.id, interface1_data)
|
||||
session.add_link(node2.id, switch.id, interface2_data)
|
||||
session.add_link(node3.id, switch.id, interface3_data)
|
||||
|
||||
# instantiate
|
||||
session.instantiate()
|
||||
finally:
|
||||
input("continue to shutdown")
|
||||
coreemu.shutdown()
|
0
package/examples/myemane/__init__.py
Normal file
0
package/examples/myemane/__init__.py
Normal file
73
package/examples/myemane/examplemodel.py
Normal file
73
package/examples/myemane/examplemodel.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
"""
|
||||
Example custom emane model.
|
||||
"""
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Set
|
||||
|
||||
from core.config import Configuration
|
||||
from core.emane import emanemanifest, emanemodel
|
||||
|
||||
|
||||
class ExampleModel(emanemodel.EmaneModel):
|
||||
"""
|
||||
Custom emane model.
|
||||
|
||||
:cvar name: defines the emane model name that will show up in the GUI
|
||||
|
||||
Mac Definition:
|
||||
:cvar mac_library: defines that mac library that the model will reference
|
||||
:cvar mac_xml: defines the mac manifest file that will be parsed to obtain configuration options,
|
||||
that will be displayed within the GUI
|
||||
:cvar mac_defaults: allows you to override options that are maintained within the manifest file above
|
||||
:cvar mac_config: parses the manifest file and converts configurations into core supported formats
|
||||
|
||||
Phy Definition:
|
||||
NOTE: phy configuration will default to the universal model as seen below and the below section does not
|
||||
have to be included
|
||||
:cvar phy_library: defines that phy library that the model will reference, used if you need to
|
||||
provide a custom phy
|
||||
:cvar phy_xml: defines the phy manifest file that will be parsed to obtain configuration options,
|
||||
that will be displayed within the GUI
|
||||
:cvar phy_defaults: allows you to override options that are maintained within the manifest file above
|
||||
or for the default universal model
|
||||
:cvar phy_config: parses the manifest file and converts configurations into core supported formats
|
||||
|
||||
Custom Override Options:
|
||||
NOTE: these options default to what's seen below and do not have to be included
|
||||
:cvar config_ignore: allows you to ignore options within phy/mac, used typically if you needed to add
|
||||
a custom option for display within the gui
|
||||
"""
|
||||
|
||||
name: str = "emane_example"
|
||||
mac_library: str = "rfpipemaclayer"
|
||||
mac_xml: str = "rfpipemaclayer.xml"
|
||||
mac_defaults: Dict[str, str] = {
|
||||
"pcrcurveuri": "/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
|
||||
}
|
||||
mac_config: List[Configuration] = []
|
||||
phy_library: Optional[str] = None
|
||||
phy_xml: str = "emanephy.xml"
|
||||
phy_defaults: Dict[str, str] = {
|
||||
"subid": "1",
|
||||
"propagationmodel": "2ray",
|
||||
"noisemode": "none",
|
||||
}
|
||||
phy_config: List[Configuration] = []
|
||||
config_ignore: Set[str] = set()
|
||||
|
||||
@classmethod
|
||||
def load(cls, emane_prefix: Path) -> None:
|
||||
"""
|
||||
Called after being loaded within the EmaneManager. Provides configured
|
||||
emane_prefix for parsing xml files.
|
||||
|
||||
:param emane_prefix: configured emane prefix path
|
||||
:return: nothing
|
||||
"""
|
||||
manifest_path = "share/emane/manifest"
|
||||
# load mac configuration
|
||||
mac_xml_path = emane_prefix / manifest_path / cls.mac_xml
|
||||
cls.mac_config = emanemanifest.parse(mac_xml_path, cls.mac_defaults)
|
||||
# load phy configuration
|
||||
phy_xml_path = emane_prefix / manifest_path / cls.phy_xml
|
||||
cls.phy_config = emanemanifest.parse(phy_xml_path, cls.phy_defaults)
|
7
package/examples/myservices/__init__.py
Normal file
7
package/examples/myservices/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
"""myservices
|
||||
|
||||
Custom services that you define can be put in this directory. Everything
|
||||
listed in __all__ is automatically loaded when you add this directory to the
|
||||
custom_services_dir = '/full/path/to/here' core.conf file option.
|
||||
"""
|
||||
__all__ = ["sample"]
|
115
package/examples/myservices/exampleservice.py
Normal file
115
package/examples/myservices/exampleservice.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
"""
|
||||
Simple example custom service, used to drive shell commands on a node.
|
||||
"""
|
||||
from typing import Tuple
|
||||
|
||||
from core.nodes.base import CoreNode
|
||||
from core.services.coreservices import CoreService, ServiceMode
|
||||
|
||||
|
||||
class ExampleService(CoreService):
|
||||
"""
|
||||
Example Custom CORE Service
|
||||
|
||||
:cvar name: name used as a unique ID for this service and is required, no spaces
|
||||
:cvar group: allows you to group services within the GUI under a common name
|
||||
:cvar executables: executables this service depends on to function, if executable is
|
||||
not on the path, service will not be loaded
|
||||
:cvar dependencies: services that this service depends on for startup, tuple of
|
||||
service names
|
||||
:cvar dirs: directories that this service will create within a node
|
||||
:cvar configs: files that this service will generate, without a full path this file
|
||||
goes in the node's directory e.g. /tmp/pycore.12345/n1.conf/myfile
|
||||
:cvar startup: commands used to start this service, any non-zero exit code will
|
||||
cause a failure
|
||||
:cvar validate: commands used to validate that a service was started, any non-zero
|
||||
exit code will cause a failure
|
||||
:cvar validation_mode: validation mode, used to determine startup success.
|
||||
NON_BLOCKING - runs startup commands, and validates success with validation commands
|
||||
BLOCKING - runs startup commands, and validates success with the startup commands themselves
|
||||
TIMER - runs startup commands, and validates success by waiting for "validation_timer" alone
|
||||
:cvar validation_timer: time in seconds for a service to wait for validation, before
|
||||
determining success in TIMER/NON_BLOCKING modes.
|
||||
:cvar validation_period: period in seconds to wait before retrying validation,
|
||||
only used in NON_BLOCKING mode
|
||||
:cvar shutdown: shutdown commands to stop this service
|
||||
"""
|
||||
|
||||
name: str = "ExampleService"
|
||||
group: str = "Utility"
|
||||
executables: Tuple[str, ...] = ()
|
||||
dependencies: Tuple[str, ...] = ()
|
||||
dirs: Tuple[str, ...] = ()
|
||||
configs: Tuple[str, ...] = ("myservice1.sh", "myservice2.sh")
|
||||
startup: Tuple[str, ...] = tuple(f"sh {x}" for x in configs)
|
||||
validate: Tuple[str, ...] = ()
|
||||
validation_mode: ServiceMode = ServiceMode.NON_BLOCKING
|
||||
validation_timer: int = 5
|
||||
validation_period: float = 0.5
|
||||
shutdown: Tuple[str, ...] = ()
|
||||
|
||||
@classmethod
|
||||
def on_load(cls) -> None:
|
||||
"""
|
||||
Provides a way to run some arbitrary logic when the service is loaded, possibly
|
||||
to help facilitate dynamic settings for the environment.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def get_configs(cls, node: CoreNode) -> Tuple[str, ...]:
|
||||
"""
|
||||
Provides a way to dynamically generate the config files from the node a service
|
||||
will run. Defaults to the class definition and can be left out entirely if not
|
||||
needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of config files to create
|
||||
"""
|
||||
return cls.configs
|
||||
|
||||
@classmethod
|
||||
def generate_config(cls, node: CoreNode, filename: str) -> str:
|
||||
"""
|
||||
Returns a string representation for a file, given the node the service is
|
||||
starting on the config filename that this information will be used for. This
|
||||
must be defined, if "configs" are defined.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:param filename: configuration file to generate
|
||||
:return: configuration file content
|
||||
"""
|
||||
cfg = "#!/bin/sh\n"
|
||||
if filename == cls.configs[0]:
|
||||
cfg += "# auto-generated by MyService (sample.py)\n"
|
||||
for iface in node.get_ifaces():
|
||||
cfg += f'echo "Node {node.name} has interface {iface.name}"\n'
|
||||
elif filename == cls.configs[1]:
|
||||
cfg += "echo hello"
|
||||
return cfg
|
||||
|
||||
@classmethod
|
||||
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]:
|
||||
"""
|
||||
Provides a way to dynamically generate the startup commands from the node a
|
||||
service will run. Defaults to the class definition and can be left out entirely
|
||||
if not needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of startup commands to run
|
||||
"""
|
||||
return cls.startup
|
||||
|
||||
@classmethod
|
||||
def get_validate(cls, node: CoreNode) -> Tuple[str, ...]:
|
||||
"""
|
||||
Provides a way to dynamically generate the validate commands from the node a
|
||||
service will run. Defaults to the class definition and can be left out entirely
|
||||
if not needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of commands to validate service startup with
|
||||
"""
|
||||
return cls.validate
|
82
package/examples/python/distributed_emane.py
Normal file
82
package/examples/python/distributed_emane.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
"""
|
||||
Example for scripting a standalone distributed EMANE session that does not interact
|
||||
with the GUI.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.base import CoreNode
|
||||
|
||||
|
||||
def parse(name):
|
||||
parser = argparse.ArgumentParser(description=f"Run {name} example")
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--address",
|
||||
help="local address that distributed servers will use for gre tunneling",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s", "--server", help="distributed server to use for creating nodes"
|
||||
)
|
||||
options = parser.parse_args()
|
||||
return options
|
||||
|
||||
|
||||
def main(args):
|
||||
# ip generator for example
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu(
|
||||
{
|
||||
"controlnet": "core1:172.16.1.0/24 core2:172.16.2.0/24 core3:172.16.3.0/24 "
|
||||
"core4:172.16.4.0/24 core5:172.16.5.0/24",
|
||||
"distributed_address": args.address,
|
||||
}
|
||||
)
|
||||
session = coreemu.create_session()
|
||||
|
||||
# initialize distributed
|
||||
server_name = "core2"
|
||||
session.distributed.add_server(server_name, args.server)
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create local node, switch, and remote nodes
|
||||
options = CoreNode.create_options()
|
||||
options.model = "mdr"
|
||||
node1 = session.add_node(CoreNode, options=options)
|
||||
options = EmaneNet.create_options()
|
||||
options.emane_model = EmaneIeee80211abgModel.name
|
||||
emane_net = session.add_node(EmaneNet, options=options)
|
||||
options = CoreNode.create_options()
|
||||
options.server = server_name
|
||||
node2 = session.add_node(CoreNode, options=options)
|
||||
|
||||
# create node interfaces and link
|
||||
interface1_data = prefixes.create_iface(node1)
|
||||
interface2_data = prefixes.create_iface(node2)
|
||||
session.add_link(node1.id, emane_net.id, iface1_data=interface1_data)
|
||||
session.add_link(node2.id, emane_net.id, iface1_data=interface2_data)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# pause script for verification
|
||||
input("press enter for shutdown")
|
||||
|
||||
# shutdown session
|
||||
coreemu.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
args = parse(__file__)
|
||||
main(args)
|
69
package/examples/python/distributed_lxd.py
Normal file
69
package/examples/python/distributed_lxd.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
"""
|
||||
Example for scripting a standalone distributed LXD session that does not interact
|
||||
with the GUI.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.lxd import LxcNode
|
||||
|
||||
|
||||
def parse(name):
|
||||
parser = argparse.ArgumentParser(description=f"Run {name} example")
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--address",
|
||||
help="local address that distributed servers will use for gre tunneling",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s", "--server", help="distributed server to use for creating nodes"
|
||||
)
|
||||
options = parser.parse_args()
|
||||
return options
|
||||
|
||||
|
||||
def main(args):
|
||||
# ip generator for example
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu({"distributed_address": args.address})
|
||||
session = coreemu.create_session()
|
||||
|
||||
# initialize distributed
|
||||
server_name = "core2"
|
||||
session.distributed.add_server(server_name, args.server)
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create local node, switch, and remote nodes
|
||||
options = LxcNode.create_options()
|
||||
options.image = "ubuntu:18.04"
|
||||
node1 = session.add_node(LxcNode, options=options)
|
||||
options.server = server_name
|
||||
node2 = session.add_node(LxcNode, options=options)
|
||||
|
||||
# create node interfaces and link
|
||||
interface1_data = prefixes.create_iface(node1)
|
||||
interface2_data = prefixes.create_iface(node2)
|
||||
session.add_link(node1.id, node2.id, interface1_data, interface2_data)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# pause script for verification
|
||||
input("press enter for shutdown")
|
||||
|
||||
# shutdown session
|
||||
coreemu.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
args = parse(__file__)
|
||||
main(args)
|
66
package/examples/python/distributed_ptp.py
Normal file
66
package/examples/python/distributed_ptp.py
Normal file
|
@ -0,0 +1,66 @@
|
|||
"""
|
||||
Example for scripting a standalone distributed peer to peer session that does not
|
||||
interact with the GUI.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.base import CoreNode
|
||||
|
||||
|
||||
def parse(name):
|
||||
parser = argparse.ArgumentParser(description=f"Run {name} example")
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--address",
|
||||
help="local address that distributed servers will use for gre tunneling",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s", "--server", help="distributed server to use for creating nodes"
|
||||
)
|
||||
options = parser.parse_args()
|
||||
return options
|
||||
|
||||
|
||||
def main(args):
|
||||
# ip generator for example
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu({"distributed_address": args.address})
|
||||
session = coreemu.create_session()
|
||||
|
||||
# initialize distributed
|
||||
server_name = "core2"
|
||||
session.distributed.add_server(server_name, args.server)
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create local node, switch, and remote nodes
|
||||
node1 = session.add_node(CoreNode)
|
||||
node2 = session.add_node(CoreNode, server=server_name)
|
||||
|
||||
# create node interfaces and link
|
||||
interface1_data = prefixes.create_iface(node1)
|
||||
interface2_data = prefixes.create_iface(node2)
|
||||
session.add_link(node1.id, node2.id, interface1_data, interface2_data)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# pause script for verification
|
||||
input("press enter for shutdown")
|
||||
|
||||
# shutdown session
|
||||
coreemu.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
args = parse(__file__)
|
||||
main(args)
|
73
package/examples/python/distributed_switch.py
Normal file
73
package/examples/python/distributed_switch.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
"""
|
||||
Example for scripting a standalone distributed switch session that does not
|
||||
interact with the GUI.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.base import CoreNode
|
||||
from core.nodes.network import SwitchNode
|
||||
|
||||
|
||||
def parse(name):
|
||||
parser = argparse.ArgumentParser(description=f"Run {name} example")
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--address",
|
||||
help="local address that distributed servers will use for gre tunneling",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s", "--server", help="distributed server to use for creating nodes"
|
||||
)
|
||||
options = parser.parse_args()
|
||||
return options
|
||||
|
||||
|
||||
def main(args):
|
||||
# ip generator for example
|
||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu(
|
||||
{"controlnet": "172.16.0.0/24", "distributed_address": args.address}
|
||||
)
|
||||
session = coreemu.create_session()
|
||||
|
||||
# initialize distributed
|
||||
server_name = "core2"
|
||||
session.distributed.add_server(server_name, args.server)
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create local node, switch, and remote nodes
|
||||
node1 = session.add_node(CoreNode)
|
||||
switch = session.add_node(SwitchNode)
|
||||
options = CoreNode.create_options()
|
||||
options.server = server_name
|
||||
node2 = session.add_node(CoreNode, options=options)
|
||||
|
||||
# create node interfaces and link
|
||||
interface1_data = prefixes.create_iface(node1)
|
||||
interface2_data = prefixes.create_iface(node2)
|
||||
session.add_link(node1.id, switch.id, iface1_data=interface1_data)
|
||||
session.add_link(node2.id, switch.id, iface1_data=interface2_data)
|
||||
|
||||
# instantiate session
|
||||
session.instantiate()
|
||||
|
||||
# pause script for verification
|
||||
input("press enter for shutdown")
|
||||
|
||||
# shutdown session
|
||||
coreemu.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
args = parse(__file__)
|
||||
main(args)
|
62
package/examples/python/emane80211.py
Normal file
62
package/examples/python/emane80211.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
# required imports
|
||||
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.base import CoreNode, Position
|
||||
|
||||
# ip nerator for example
|
||||
ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu()
|
||||
session = coreemu.create_session()
|
||||
|
||||
# location information is required to be set for emane
|
||||
session.location.setrefgeo(47.57917, -122.13232, 2.0)
|
||||
session.location.refscale = 150.0
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create emane
|
||||
options = EmaneNet.create_options()
|
||||
options.emane_model = EmaneIeee80211abgModel.name
|
||||
position = Position(x=200, y=200)
|
||||
emane = session.add_node(EmaneNet, position=position, options=options)
|
||||
|
||||
# create nodes
|
||||
options = CoreNode.create_options()
|
||||
options.model = "mdr"
|
||||
position = Position(x=100, y=100)
|
||||
n1 = session.add_node(CoreNode, position=position, options=options)
|
||||
options = CoreNode.create_options()
|
||||
options.model = "mdr"
|
||||
position = Position(x=300, y=100)
|
||||
n2 = session.add_node(CoreNode, position=position, options=options)
|
||||
|
||||
# configure general emane settings
|
||||
config = session.emane.get_configs()
|
||||
config.update({"eventservicettl": "2"})
|
||||
|
||||
# configure emane model settings
|
||||
# using a dict mapping currently support values as strings
|
||||
session.emane.set_model_config(
|
||||
emane.id, EmaneIeee80211abgModel.name, {"unicastrate": "3"}
|
||||
)
|
||||
|
||||
# link nodes to emane
|
||||
iface1 = ip_prefixes.create_iface(n1)
|
||||
session.add_link(n1.id, emane.id, iface1)
|
||||
iface1 = ip_prefixes.create_iface(n2)
|
||||
session.add_link(n2.id, emane.id, iface1)
|
||||
|
||||
# start session
|
||||
session.instantiate()
|
||||
|
||||
# do whatever you like here
|
||||
input("press enter to shutdown")
|
||||
|
||||
# stop session
|
||||
session.shutdown()
|
35
package/examples/python/peertopeer.py
Normal file
35
package/examples/python/peertopeer.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
# required imports
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.base import CoreNode, Position
|
||||
|
||||
# ip nerator for example
|
||||
ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu()
|
||||
session = coreemu.create_session()
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create nodes
|
||||
position = Position(x=100, y=100)
|
||||
n1 = session.add_node(CoreNode, position=position)
|
||||
position = Position(x=300, y=100)
|
||||
n2 = session.add_node(CoreNode, position=position)
|
||||
|
||||
# link nodes together
|
||||
iface1 = ip_prefixes.create_iface(n1)
|
||||
iface2 = ip_prefixes.create_iface(n2)
|
||||
session.add_link(n1.id, n2.id, iface1, iface2)
|
||||
|
||||
# start session
|
||||
session.instantiate()
|
||||
|
||||
# do whatever you like here
|
||||
input("press enter to shutdown")
|
||||
|
||||
# stop session
|
||||
session.shutdown()
|
41
package/examples/python/switch.py
Normal file
41
package/examples/python/switch.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
# required imports
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.base import CoreNode, Position
|
||||
from core.nodes.network import SwitchNode
|
||||
|
||||
# ip nerator for example
|
||||
ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu()
|
||||
session = coreemu.create_session()
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create switch
|
||||
position = Position(x=200, y=200)
|
||||
switch = session.add_node(SwitchNode, position=position)
|
||||
|
||||
# create nodes
|
||||
position = Position(x=100, y=100)
|
||||
n1 = session.add_node(CoreNode, position=position)
|
||||
position = Position(x=300, y=100)
|
||||
n2 = session.add_node(CoreNode, position=position)
|
||||
|
||||
# link nodes to switch
|
||||
iface1 = ip_prefixes.create_iface(n1)
|
||||
session.add_link(n1.id, switch.id, iface1)
|
||||
iface1 = ip_prefixes.create_iface(n2)
|
||||
session.add_link(n2.id, switch.id, iface1)
|
||||
|
||||
# start session
|
||||
session.instantiate()
|
||||
|
||||
# do whatever you like here
|
||||
input("press enter to shutdown")
|
||||
|
||||
# stop session
|
||||
session.shutdown()
|
50
package/examples/python/wireless.py
Normal file
50
package/examples/python/wireless.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
# required imports
|
||||
import logging
|
||||
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.nodes.base import CoreNode, Position
|
||||
from core.nodes.network import WlanNode
|
||||
|
||||
# enable info logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
# ip nerator for example
|
||||
ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu()
|
||||
session = coreemu.create_session()
|
||||
|
||||
# must be in configuration state for nodes to start
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create wireless
|
||||
position = Position(x=200, y=200)
|
||||
wireless = session.add_node(WlanNode, position=position)
|
||||
|
||||
# create nodes
|
||||
options = CoreNode.create_options()
|
||||
options.model = "mdr"
|
||||
position = Position(x=100, y=100)
|
||||
n1 = session.add_node(CoreNode, position=position, options=options)
|
||||
options = CoreNode.create_options()
|
||||
options.model = "mdr"
|
||||
position = Position(x=300, y=100)
|
||||
n2 = session.add_node(CoreNode, position=position, options=options)
|
||||
|
||||
# link nodes to wireless
|
||||
iface1 = ip_prefixes.create_iface(n1)
|
||||
session.add_link(n1.id, wireless.id, iface1)
|
||||
iface1 = ip_prefixes.create_iface(n2)
|
||||
session.add_link(n2.id, wireless.id, iface1)
|
||||
|
||||
# start session
|
||||
session.instantiate()
|
||||
|
||||
# do whatever you like here
|
||||
input("press enter to shutdown")
|
||||
|
||||
# stop session
|
||||
session.shutdown()
|
59
package/examples/python/wlan.py
Normal file
59
package/examples/python/wlan.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# required imports
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.enumerations import EventTypes
|
||||
from core.location.mobility import BasicRangeModel
|
||||
from core.nodes.base import CoreNode, Position
|
||||
from core.nodes.network import WlanNode
|
||||
|
||||
# ip nerator for example
|
||||
ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
|
||||
|
||||
# create emulator instance for creating sessions and utility methods
|
||||
coreemu = CoreEmu()
|
||||
session = coreemu.create_session()
|
||||
|
||||
# must be in configuration state for nodes to start, when using "node_add" below
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
# create wlan
|
||||
position = Position(x=200, y=200)
|
||||
wlan = session.add_node(WlanNode, position=position)
|
||||
|
||||
# create nodes
|
||||
options = CoreNode.create_options()
|
||||
options.model = "mdr"
|
||||
position = Position(x=100, y=100)
|
||||
n1 = session.add_node(CoreNode, position=position, options=options)
|
||||
options = CoreNode.create_options()
|
||||
options.model = "mdr"
|
||||
position = Position(x=300, y=100)
|
||||
n2 = session.add_node(CoreNode, position=position, options=options)
|
||||
|
||||
# configuring wlan
|
||||
session.mobility.set_model_config(
|
||||
wlan.id,
|
||||
BasicRangeModel.name,
|
||||
{
|
||||
"range": "280",
|
||||
"bandwidth": "55000000",
|
||||
"delay": "6000",
|
||||
"jitter": "5",
|
||||
"error": "5",
|
||||
},
|
||||
)
|
||||
|
||||
# link nodes to wlan
|
||||
iface1 = ip_prefixes.create_iface(n1)
|
||||
session.add_link(n1.id, wlan.id, iface1)
|
||||
iface1 = ip_prefixes.create_iface(n2)
|
||||
session.add_link(n2.id, wlan.id, iface1)
|
||||
|
||||
# start session
|
||||
session.instantiate()
|
||||
|
||||
# do whatever you like here
|
||||
input("press enter to shutdown")
|
||||
|
||||
# stop session
|
||||
session.shutdown()
|
30
package/examples/services/sampleFirewall
Normal file
30
package/examples/services/sampleFirewall
Normal file
|
@ -0,0 +1,30 @@
|
|||
# -------- CUSTOMIZATION REQUIRED --------
|
||||
#
|
||||
# Below are sample iptables firewall rules that you can uncomment and edit.
|
||||
# You can also use ip6tables rules for IPv6.
|
||||
#
|
||||
|
||||
# start by flushing all firewall rules (so this script may be re-run)
|
||||
#iptables -F
|
||||
|
||||
# allow traffic related to established connections
|
||||
#iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
|
||||
# allow TCP packets from any source destined for 192.168.1.1
|
||||
#iptables -A INPUT -s 0/0 -i eth0 -d 192.168.1.1 -p TCP -j ACCEPT
|
||||
|
||||
# allow OpenVPN server traffic from eth0
|
||||
#iptables -A INPUT -p udp --dport 1194 -j ACCEPT
|
||||
#iptables -A INPUT -i eth0 -j DROP
|
||||
#iptables -A OUTPUT -p udp --sport 1194 -j ACCEPT
|
||||
#iptables -A OUTPUT -o eth0 -j DROP
|
||||
|
||||
# allow ICMP ping traffic
|
||||
#iptables -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
|
||||
#iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
|
||||
|
||||
# allow SSH traffic
|
||||
#iptables -A -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
|
||||
|
||||
# drop all other traffic coming in eth0
|
||||
#iptables -A INPUT -i eth0 -j DROP
|
119
package/examples/services/sampleIPsec
Normal file
119
package/examples/services/sampleIPsec
Normal file
|
@ -0,0 +1,119 @@
|
|||
# -------- CUSTOMIZATION REQUIRED --------
|
||||
#
|
||||
# The IPsec service builds ESP tunnels between the specified peers using the
|
||||
# racoon IKEv2 keying daemon. You need to provide keys and the addresses of
|
||||
# peers, along with subnets to tunnel.
|
||||
|
||||
# directory containing the certificate and key described below
|
||||
keydir=/etc/core/keys
|
||||
|
||||
# the name used for the "$certname.pem" x509 certificate and
|
||||
# "$certname.key" RSA private key, which can be generated using openssl
|
||||
certname=ipsec1
|
||||
|
||||
# list the public-facing IP addresses, starting with the localhost and followed
|
||||
# by each tunnel peer, separated with a single space
|
||||
tunnelhosts="172.16.0.1AND172.16.0.2 172.16.0.1AND172.16.2.1"
|
||||
|
||||
# Define T<i> where i is the index for each tunnel peer host from
|
||||
# the tunnel_hosts list above (0 is localhost).
|
||||
# T<i> is a list of IPsec tunnels with peer i, with a local subnet address
|
||||
# followed by the remote subnet address:
|
||||
# T<i>="<local>AND<remote> <local>AND<remote>"
|
||||
# For example, 172.16.0.0/24 is a local network (behind this node) to be
|
||||
# tunneled and 172.16.2.0/24 is a remote network (behind peer 1)
|
||||
T1="172.16.3.0/24AND172.16.5.0/24"
|
||||
T2="172.16.4.0/24AND172.16.5.0/24 172.16.4.0/24AND172.16.6.0/24"
|
||||
|
||||
# -------- END CUSTOMIZATION --------
|
||||
|
||||
echo "building config $PWD/ipsec.conf..."
|
||||
echo "building config $PWD/ipsec.conf..." > $PWD/ipsec.log
|
||||
|
||||
checkip=0
|
||||
if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then
|
||||
echo "WARNING: ip validation disabled because package sipcalc not installed
|
||||
" >> $PWD/ipsec.log
|
||||
checkip=1
|
||||
fi
|
||||
|
||||
echo "#!/usr/sbin/setkey -f
|
||||
# Flush the SAD and SPD
|
||||
flush;
|
||||
spdflush;
|
||||
|
||||
# Security policies \
|
||||
" > $PWD/ipsec.conf
|
||||
i=0
|
||||
for hostpair in $tunnelhosts; do
|
||||
i=`expr $i + 1`
|
||||
# parse tunnel host IP
|
||||
thishost=${hostpair%%AND*}
|
||||
peerhost=${hostpair##*AND}
|
||||
if [ $checkip = "0" ] &&
|
||||
[ "$(sipcalc "$thishost" "$peerhost" | grep ERR)" != "" ]; then
|
||||
echo "ERROR: invalid host address $thishost or $peerhost \
|
||||
" >> $PWD/ipsec.log
|
||||
fi
|
||||
# parse each tunnel addresses
|
||||
tunnel_list_var_name=T$i
|
||||
eval tunnels="$"$tunnel_list_var_name""
|
||||
for ttunnel in $tunnels; do
|
||||
lclnet=${ttunnel%%AND*}
|
||||
rmtnet=${ttunnel##*AND}
|
||||
if [ $checkip = "0" ] &&
|
||||
[ "$(sipcalc "$lclnet" "$rmtnet"| grep ERR)" != "" ]; then
|
||||
echo "ERROR: invalid tunnel address $lclnet and $rmtnet \
|
||||
" >> $PWD/ipsec.log
|
||||
fi
|
||||
# add tunnel policies
|
||||
echo "
|
||||
spdadd $lclnet $rmtnet any -P out ipsec
|
||||
esp/tunnel/$thishost-$peerhost/require;
|
||||
spdadd $rmtnet $lclnet any -P in ipsec
|
||||
esp/tunnel/$peerhost-$thishost/require; \
|
||||
" >> $PWD/ipsec.conf
|
||||
done
|
||||
done
|
||||
|
||||
echo "building config $PWD/racoon.conf..."
|
||||
if [ ! -e $keydir\/$certname.key ] || [ ! -e $keydir\/$certname.pem ]; then
|
||||
echo "ERROR: missing certification files under $keydir \
|
||||
$certname.key or $certname.pem " >> $PWD/ipsec.log
|
||||
fi
|
||||
echo "
|
||||
path certificate \"$keydir\";
|
||||
listen {
|
||||
adminsock disabled;
|
||||
}
|
||||
remote anonymous
|
||||
{
|
||||
exchange_mode main;
|
||||
certificate_type x509 \"$certname.pem\" \"$certname.key\";
|
||||
ca_type x509 \"ca-cert.pem\";
|
||||
my_identifier asn1dn;
|
||||
peers_identifier asn1dn;
|
||||
|
||||
proposal {
|
||||
encryption_algorithm 3des ;
|
||||
hash_algorithm sha1;
|
||||
authentication_method rsasig ;
|
||||
dh_group modp768;
|
||||
}
|
||||
}
|
||||
sainfo anonymous
|
||||
{
|
||||
pfs_group modp768;
|
||||
lifetime time 1 hour ;
|
||||
encryption_algorithm 3des, blowfish 448, rijndael ;
|
||||
authentication_algorithm hmac_sha1, hmac_md5 ;
|
||||
compression_algorithm deflate ;
|
||||
}
|
||||
" > $PWD/racoon.conf
|
||||
|
||||
# the setkey program is required from the ipsec-tools package
|
||||
echo "running setkey -f $PWD/ipsec.conf..."
|
||||
setkey -f $PWD/ipsec.conf
|
||||
|
||||
echo "running racoon -d -f $PWD/racoon.conf..."
|
||||
racoon -d -f $PWD/racoon.conf -l racoon.log
|
63
package/examples/services/sampleVPNClient
Normal file
63
package/examples/services/sampleVPNClient
Normal file
|
@ -0,0 +1,63 @@
|
|||
# -------- CUSTOMIZATION REQUIRED --------
|
||||
#
|
||||
# The VPNClient service builds a VPN tunnel to the specified VPN server using
|
||||
# OpenVPN software and a virtual TUN/TAP device.
|
||||
|
||||
# directory containing the certificate and key described below
|
||||
keydir=/etc/core/keys
|
||||
|
||||
# the name used for a "$keyname.crt" certificate and "$keyname.key" private key.
|
||||
keyname=client1
|
||||
|
||||
# the public IP address of the VPN server this client should connect with
|
||||
vpnserver="10.0.2.10"
|
||||
|
||||
# optional next hop for adding a static route to reach the VPN server
|
||||
#nexthop="10.0.1.1"
|
||||
|
||||
# --------- END CUSTOMIZATION --------
|
||||
|
||||
# validate addresses
|
||||
if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then
|
||||
echo "WARNING: ip validation disabled because package sipcalc not installed
|
||||
" > $PWD/vpnclient.log
|
||||
else
|
||||
if [ "$(sipcalc "$vpnserver" "$nexthop" | grep ERR)" != "" ]; then
|
||||
echo "ERROR: invalide address $vpnserver or $nexthop \
|
||||
" > $PWD/vpnclient.log
|
||||
fi
|
||||
fi
|
||||
|
||||
# validate key and certification files
|
||||
if [ ! -e $keydir\/$keyname.key ] || [ ! -e $keydir\/$keyname.crt ] \
|
||||
|| [ ! -e $keydir\/ca.crt ] || [ ! -e $keydir\/dh1024.pem ]; then
|
||||
echo "ERROR: missing certification or key files under $keydir \
|
||||
$keyname.key or $keyname.crt or ca.crt or dh1024.pem" >> $PWD/vpnclient.log
|
||||
fi
|
||||
|
||||
# if necessary, add a static route for reaching the VPN server IP via the IF
|
||||
vpnservernet=${vpnserver%.*}.0/24
|
||||
if [ "$nexthop" != "" ]; then
|
||||
/sbin/ip route add $vpnservernet via $nexthop
|
||||
fi
|
||||
|
||||
# create openvpn client.conf
|
||||
(
|
||||
cat << EOF
|
||||
client
|
||||
dev tun
|
||||
proto udp
|
||||
remote $vpnserver 1194
|
||||
nobind
|
||||
ca $keydir/ca.crt
|
||||
cert $keydir/$keyname.crt
|
||||
key $keydir/$keyname.key
|
||||
dh $keydir/dh1024.pem
|
||||
cipher AES-256-CBC
|
||||
log $PWD/openvpn-client.log
|
||||
verb 4
|
||||
daemon
|
||||
EOF
|
||||
) > client.conf
|
||||
|
||||
openvpn --config client.conf
|
147
package/examples/services/sampleVPNServer
Normal file
147
package/examples/services/sampleVPNServer
Normal file
|
@ -0,0 +1,147 @@
|
|||
# -------- CUSTOMIZATION REQUIRED --------
|
||||
#
|
||||
# The VPNServer service sets up the OpenVPN server for building VPN tunnels
|
||||
# that allow access via TUN/TAP device to private networks.
|
||||
#
|
||||
# note that the IPForward and DefaultRoute services should be enabled
|
||||
|
||||
# directory containing the certificate and key described below, in addition to
|
||||
# a CA certificate and DH key
|
||||
keydir=/etc/core/keys
|
||||
|
||||
# the name used for a "$keyname.crt" certificate and "$keyname.key" private key.
|
||||
keyname=server2
|
||||
|
||||
# the VPN subnet address from which the client VPN IP (for the TUN/TAP)
|
||||
# will be allocated
|
||||
vpnsubnet=10.0.200.0
|
||||
|
||||
# public IP address of this vpn server (same as VPNClient vpnserver= setting)
|
||||
vpnserver=10.0.2.10
|
||||
|
||||
# optional list of private subnets reachable behind this VPN server
|
||||
# each subnet and next hop is separated by a space
|
||||
# "<subnet1>,<nexthop1> <subnet2>,<nexthop2> ..."
|
||||
#privatenets="10.0.11.0,10.0.10.1 10.0.12.0,10.0.10.1"
|
||||
|
||||
# optional list of VPN clients, for statically assigning IP addresses to
|
||||
# clients; also, an optional client subnet can be specified for adding static
|
||||
# routes via the client
|
||||
# Note: VPN addresses x.x.x.0-3 are reserved
|
||||
# "<keyname>,<vpnIP>,<subnetIP> <keyname>,<vpnIP>,<subnetIP> ..."
|
||||
#vpnclients="client1KeyFilename,10.0.200.5,10.0.0.0 client2KeyFilename,,"
|
||||
|
||||
# NOTE: you may need to enable the StaticRoutes service on nodes within the
|
||||
# private subnet, in order to have routes back to the client.
|
||||
# /sbin/ip ro add <vpnsubnet>/24 via <vpnServerRemoteInterface>
|
||||
# /sbin/ip ro add <vpnClientSubnet>/24 via <vpnServerRemoteInterface>
|
||||
|
||||
# -------- END CUSTOMIZATION --------
|
||||
|
||||
echo > $PWD/vpnserver.log
|
||||
rm -f -r $PWD/ccd
|
||||
|
||||
# validate key and certification files
|
||||
if [ ! -e $keydir\/$keyname.key ] || [ ! -e $keydir\/$keyname.crt ] \
|
||||
|| [ ! -e $keydir\/ca.crt ] || [ ! -e $keydir\/dh1024.pem ]; then
|
||||
echo "ERROR: missing certification or key files under $keydir \
|
||||
$keyname.key or $keyname.crt or ca.crt or dh1024.pem" >> $PWD/vpnserver.log
|
||||
fi
|
||||
|
||||
# validate configuration IP addresses
|
||||
checkip=0
|
||||
if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then
|
||||
echo "WARNING: ip validation disabled because package sipcalc not installed\
|
||||
" >> $PWD/vpnserver.log
|
||||
checkip=1
|
||||
else
|
||||
if [ "$(sipcalc "$vpnsubnet" "$vpnserver" | grep ERR)" != "" ]; then
|
||||
echo "ERROR: invalid vpn subnet or server address \
|
||||
$vpnsubnet or $vpnserver " >> $PWD/vpnserver.log
|
||||
fi
|
||||
fi
|
||||
|
||||
# create client vpn ip pool file
|
||||
(
|
||||
cat << EOF
|
||||
EOF
|
||||
)> $PWD/ippool.txt
|
||||
|
||||
# create server.conf file
|
||||
(
|
||||
cat << EOF
|
||||
# openvpn server config
|
||||
local $vpnserver
|
||||
server $vpnsubnet 255.255.255.0
|
||||
push "redirect-gateway def1"
|
||||
EOF
|
||||
)> $PWD/server.conf
|
||||
|
||||
# add routes to VPN server private subnets, and push these routes to clients
|
||||
for privatenet in $privatenets; do
|
||||
if [ $privatenet != "" ]; then
|
||||
net=${privatenet%%,*}
|
||||
nexthop=${privatenet##*,}
|
||||
if [ $checkip = "0" ] &&
|
||||
[ "$(sipcalc "$net" "$nexthop" | grep ERR)" != "" ]; then
|
||||
echo "ERROR: invalid vpn server private net address \
|
||||
$net or $nexthop " >> $PWD/vpnserver.log
|
||||
fi
|
||||
echo push route $net 255.255.255.0 >> $PWD/server.conf
|
||||
/sbin/ip ro add $net/24 via $nexthop
|
||||
/sbin/ip ro add $vpnsubnet/24 via $nexthop
|
||||
fi
|
||||
done
|
||||
|
||||
# allow subnet through this VPN, one route for each client subnet
|
||||
for client in $vpnclients; do
|
||||
if [ $client != "" ]; then
|
||||
cSubnetIP=${client##*,}
|
||||
cVpnIP=${client#*,}
|
||||
cVpnIP=${cVpnIP%%,*}
|
||||
cKeyFilename=${client%%,*}
|
||||
if [ "$cSubnetIP" != "" ]; then
|
||||
if [ $checkip = "0" ] &&
|
||||
[ "$(sipcalc "$cSubnetIP" "$cVpnIP" | grep ERR)" != "" ]; then
|
||||
echo "ERROR: invalid vpn client and subnet address \
|
||||
$cSubnetIP or $cVpnIP " >> $PWD/vpnserver.log
|
||||
fi
|
||||
echo route $cSubnetIP 255.255.255.0 >> $PWD/server.conf
|
||||
if ! test -d $PWD/ccd; then
|
||||
mkdir -p $PWD/ccd
|
||||
echo client-config-dir $PWD/ccd >> $PWD/server.conf
|
||||
fi
|
||||
if test -e $PWD/ccd/$cKeyFilename; then
|
||||
echo iroute $cSubnetIP 255.255.255.0 >> $PWD/ccd/$cKeyFilename
|
||||
else
|
||||
echo iroute $cSubnetIP 255.255.255.0 > $PWD/ccd/$cKeyFilename
|
||||
fi
|
||||
fi
|
||||
if [ "$cVpnIP" != "" ]; then
|
||||
echo $cKeyFilename,$cVpnIP >> $PWD/ippool.txt
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
(
|
||||
cat << EOF
|
||||
keepalive 10 120
|
||||
ca $keydir/ca.crt
|
||||
cert $keydir/$keyname.crt
|
||||
key $keydir/$keyname.key
|
||||
dh $keydir/dh1024.pem
|
||||
cipher AES-256-CBC
|
||||
status /var/log/openvpn-status.log
|
||||
log /var/log/openvpn-server.log
|
||||
ifconfig-pool-linear
|
||||
ifconfig-pool-persist $PWD/ippool.txt
|
||||
port 1194
|
||||
proto udp
|
||||
dev tun
|
||||
verb 4
|
||||
daemon
|
||||
EOF
|
||||
)>> $PWD/server.conf
|
||||
|
||||
# start vpn server
|
||||
openvpn --config server.conf
|
17
package/examples/tdma/schedule.xml
Normal file
17
package/examples/tdma/schedule.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<emane-tdma-schedule >
|
||||
<structure frames="1" slots="10" slotoverhead="0" slotduration="1500" bandwidth="1M"/>
|
||||
<multiframe frequency="2.4G" power="0" class="0" datarate="100M">
|
||||
<frame index="0">
|
||||
<slot index="0" nodes="1"/>
|
||||
<slot index="1" nodes="2"/>
|
||||
<slot index="2" nodes="3"/>
|
||||
<slot index="3" nodes="4"/>
|
||||
<slot index="4" nodes="5"/>
|
||||
<slot index="5" nodes="6"/>
|
||||
<slot index="6" nodes="7"/>
|
||||
<slot index="7" nodes="8"/>
|
||||
<slot index="8" nodes="9"/>
|
||||
<slot index="9" nodes="10"/>
|
||||
</frame>
|
||||
</multiframe>
|
||||
</emane-tdma-schedule>
|
Loading…
Add table
Add a link
Reference in a new issue