├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── daemon-checks.yml
│ └── documentation.yml
├── .gitignore
├── ASSIGNMENT_OF_COPYRIGHT.pdf
├── CHANGELOG.md
├── LICENSE
├── Makefile.am
├── README.md
├── bootstrap.sh
├── configure.ac
├── daemon
├── .pre-commit-config.yaml
├── Makefile.am
├── core
│ ├── .gitignore
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ └── grpc
│ │ │ ├── __init__.py
│ │ │ ├── client.py
│ │ │ ├── events.py
│ │ │ ├── grpcutils.py
│ │ │ ├── server.py
│ │ │ └── wrappers.py
│ ├── config.py
│ ├── constants.py.in
│ ├── emane
│ │ ├── __init__.py
│ │ ├── emanemanager.py
│ │ ├── emanemanifest.py
│ │ ├── emanemodel.py
│ │ ├── eventmanager.py
│ │ ├── linkmonitor.py
│ │ ├── modelmanager.py
│ │ ├── models
│ │ │ ├── __init__.py
│ │ │ ├── bypass.py
│ │ │ ├── commeffect.py
│ │ │ ├── ieee80211abg.py
│ │ │ ├── rfpipe.py
│ │ │ └── tdma.py
│ │ └── nodes.py
│ ├── emulator
│ │ ├── __init__.py
│ │ ├── broadcast.py
│ │ ├── controlnets.py
│ │ ├── coreemu.py
│ │ ├── data.py
│ │ ├── distributed.py
│ │ ├── enumerations.py
│ │ ├── hooks.py
│ │ ├── links.py
│ │ ├── session.py
│ │ └── sessionconfig.py
│ ├── errors.py
│ ├── executables.py
│ ├── gui
│ │ ├── __init__.py
│ │ ├── app.py
│ │ ├── appconfig.py
│ │ ├── coreclient.py
│ │ ├── data
│ │ │ ├── backgrounds
│ │ │ │ ├── sample1-bg.gif
│ │ │ │ └── sample4-bg.jpg
│ │ │ ├── icons
│ │ │ │ ├── OVS.gif
│ │ │ │ ├── alert.png
│ │ │ │ ├── antenna.png
│ │ │ │ ├── cancel.png
│ │ │ │ ├── core-icon.png
│ │ │ │ ├── delete.png
│ │ │ │ ├── docker.png
│ │ │ │ ├── document-new.gif
│ │ │ │ ├── document-properties.gif
│ │ │ │ ├── document-save.gif
│ │ │ │ ├── edit-delete.gif
│ │ │ │ ├── edit-node.png
│ │ │ │ ├── emane.png
│ │ │ │ ├── error.png
│ │ │ │ ├── fileopen.gif
│ │ │ │ ├── host.png
│ │ │ │ ├── hub.png
│ │ │ │ ├── lanswitch.png
│ │ │ │ ├── link.png
│ │ │ │ ├── marker.png
│ │ │ │ ├── markerclear.png
│ │ │ │ ├── mdr.png
│ │ │ │ ├── observe.png
│ │ │ │ ├── oval.png
│ │ │ │ ├── pause.png
│ │ │ │ ├── pc.png
│ │ │ │ ├── plot.gif
│ │ │ │ ├── podman.png
│ │ │ │ ├── prouter.png
│ │ │ │ ├── rectangle.png
│ │ │ │ ├── rj45.png
│ │ │ │ ├── router.png
│ │ │ │ ├── run.png
│ │ │ │ ├── select.png
│ │ │ │ ├── shadow.png
│ │ │ │ ├── shutdown.png
│ │ │ │ ├── start.png
│ │ │ │ ├── stop.png
│ │ │ │ ├── text.png
│ │ │ │ ├── tunnel.png
│ │ │ │ ├── twonode.png
│ │ │ │ ├── wireless.png
│ │ │ │ └── wlan.png
│ │ │ ├── mobility
│ │ │ │ └── sample1.scen
│ │ │ └── xmls
│ │ │ │ ├── emane-demo-antenna.xml
│ │ │ │ ├── emane-demo-eel.xml
│ │ │ │ ├── emane-demo-files.xml
│ │ │ │ ├── emane-demo-gpsd.xml
│ │ │ │ ├── emane-demo-precomputed.xml
│ │ │ │ └── sample1.xml
│ │ ├── dialogs
│ │ │ ├── __init__.py
│ │ │ ├── about.py
│ │ │ ├── alerts.py
│ │ │ ├── canvassizeandscale.py
│ │ │ ├── canvaswallpaper.py
│ │ │ ├── colorpicker.py
│ │ │ ├── customnodes.py
│ │ │ ├── dialog.py
│ │ │ ├── emaneconfig.py
│ │ │ ├── emaneinstall.py
│ │ │ ├── error.py
│ │ │ ├── executepython.py
│ │ │ ├── find.py
│ │ │ ├── hooks.py
│ │ │ ├── ipdialog.py
│ │ │ ├── linkconfig.py
│ │ │ ├── macdialog.py
│ │ │ ├── mobilityconfig.py
│ │ │ ├── mobilityplayer.py
│ │ │ ├── nodecommands.py
│ │ │ ├── nodeconfig.py
│ │ │ ├── nodeservice.py
│ │ │ ├── observers.py
│ │ │ ├── preferences.py
│ │ │ ├── runtool.py
│ │ │ ├── servers.py
│ │ │ ├── serviceconfig.py
│ │ │ ├── sessionoptions.py
│ │ │ ├── sessions.py
│ │ │ ├── shapemod.py
│ │ │ ├── throughput.py
│ │ │ ├── wirelessconfig.py
│ │ │ └── wlanconfig.py
│ │ ├── frames
│ │ │ ├── __init__.py
│ │ │ ├── base.py
│ │ │ ├── default.py
│ │ │ ├── link.py
│ │ │ └── node.py
│ │ ├── graph
│ │ │ ├── __init__.py
│ │ │ ├── edges.py
│ │ │ ├── enums.py
│ │ │ ├── graph.py
│ │ │ ├── manager.py
│ │ │ ├── node.py
│ │ │ ├── shape.py
│ │ │ ├── shapeutils.py
│ │ │ ├── tags.py
│ │ │ └── tooltip.py
│ │ ├── images.py
│ │ ├── interface.py
│ │ ├── menubar.py
│ │ ├── nodeutils.py
│ │ ├── observers.py
│ │ ├── statusbar.py
│ │ ├── task.py
│ │ ├── themes.py
│ │ ├── toolbar.py
│ │ ├── tooltip.py
│ │ ├── utils.py
│ │ ├── validation.py
│ │ └── widgets.py
│ ├── location
│ │ ├── __init__.py
│ │ ├── event.py
│ │ ├── geo.py
│ │ └── mobility.py
│ ├── nodes
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── docker.py
│ │ ├── interface.py
│ │ ├── netclient.py
│ │ ├── network.py
│ │ ├── physical.py
│ │ ├── podman.py
│ │ └── wireless.py
│ ├── player.py
│ ├── plugins
│ │ ├── __init__.py
│ │ └── sdt.py
│ ├── scripts
│ │ ├── __init__.py
│ │ ├── cleanup.py
│ │ ├── cli.py
│ │ ├── daemon.py
│ │ ├── gui.py
│ │ ├── player.py
│ │ ├── routemonitor.py
│ │ └── serviceupdate.py
│ ├── services
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── defaults
│ │ │ ├── __init__.py
│ │ │ ├── frrservices
│ │ │ │ ├── __init__.py
│ │ │ │ ├── services.py
│ │ │ │ └── templates
│ │ │ │ │ ├── frrboot.sh
│ │ │ │ │ └── usr
│ │ │ │ │ └── local
│ │ │ │ │ └── etc
│ │ │ │ │ └── frr
│ │ │ │ │ ├── daemons
│ │ │ │ │ ├── frr.conf
│ │ │ │ │ └── vtysh.conf
│ │ │ ├── nrlservices
│ │ │ │ ├── __init__.py
│ │ │ │ ├── services.py
│ │ │ │ └── templates
│ │ │ │ │ ├── etc
│ │ │ │ │ └── olsrd
│ │ │ │ │ │ └── olsrd.conf
│ │ │ │ │ ├── mgensink.sh
│ │ │ │ │ ├── nrlnhdp.sh
│ │ │ │ │ ├── nrlolsrd.sh
│ │ │ │ │ ├── nrlolsrv2.sh
│ │ │ │ │ ├── olsrd.sh
│ │ │ │ │ ├── sink.mgen
│ │ │ │ │ ├── start_mgen_actor.sh
│ │ │ │ │ └── startsmf.sh
│ │ │ ├── quaggaservices
│ │ │ │ ├── __init__.py
│ │ │ │ ├── services.py
│ │ │ │ └── templates
│ │ │ │ │ ├── quaggaboot.sh
│ │ │ │ │ └── usr
│ │ │ │ │ └── local
│ │ │ │ │ └── etc
│ │ │ │ │ └── quagga
│ │ │ │ │ ├── Quagga.conf
│ │ │ │ │ └── vtysh.conf
│ │ │ ├── securityservices
│ │ │ │ ├── __init__.py
│ │ │ │ ├── services.py
│ │ │ │ └── templates
│ │ │ │ │ ├── firewall.sh
│ │ │ │ │ ├── ipsec.sh
│ │ │ │ │ ├── nat.sh
│ │ │ │ │ ├── vpnclient.sh
│ │ │ │ │ └── vpnserver.sh
│ │ │ └── utilservices
│ │ │ │ ├── __init__.py
│ │ │ │ ├── services.py
│ │ │ │ └── templates
│ │ │ │ ├── defaultmroute.sh
│ │ │ │ ├── defaultroute.sh
│ │ │ │ ├── etc
│ │ │ │ ├── apache2
│ │ │ │ │ ├── apache2.conf
│ │ │ │ │ └── envvars
│ │ │ │ ├── dhcp
│ │ │ │ │ └── dhcpd.conf
│ │ │ │ ├── radvd
│ │ │ │ │ └── radvd.conf
│ │ │ │ └── ssh
│ │ │ │ │ └── sshd_config
│ │ │ │ ├── ipforward.sh
│ │ │ │ ├── pcap.sh
│ │ │ │ ├── startatd.sh
│ │ │ │ ├── startdhcpclient.sh
│ │ │ │ ├── startsshd.sh
│ │ │ │ ├── staticroute.sh
│ │ │ │ ├── var
│ │ │ │ └── www
│ │ │ │ │ └── index.html
│ │ │ │ └── vsftpd.conf
│ │ ├── dependencies.py
│ │ └── manager.py
│ ├── utils.py
│ └── xml
│ │ ├── __init__.py
│ │ ├── corexml.py
│ │ ├── corexmldeployment.py
│ │ └── emanexml.py
├── doc
│ ├── .gitignore
│ ├── Makefile.am
│ └── conf.py.in
├── poetry.lock
├── proto
│ ├── Makefile.am
│ └── core
│ │ └── api
│ │ └── grpc
│ │ ├── common.proto
│ │ ├── core.proto
│ │ ├── emane.proto
│ │ ├── mobility.proto
│ │ ├── services.proto
│ │ └── wlan.proto
├── pyproject.toml
├── setup.cfg
└── tests
│ ├── conftest.py
│ ├── emane
│ └── test_emane.py
│ ├── mobility.scen
│ ├── myservices
│ ├── __init__.py
│ └── sample.py
│ ├── test_conf.py
│ ├── test_core.py
│ ├── test_distributed.py
│ ├── test_grpc.py
│ ├── test_links.py
│ ├── test_mobility.py
│ ├── test_nodes.py
│ ├── test_services.py
│ ├── test_utils.py
│ └── test_xml.py
├── dockerfiles
├── Dockerfile.build
├── Dockerfile.emane-python
├── Dockerfile.rocky
└── Dockerfile.ubuntu
├── docs
├── Makefile.am
├── _config.yml
├── architecture.md
├── ctrlnet.md
├── devguide.md
├── diagrams
│ ├── architecture.plantuml
│ └── workflow.plantuml
├── distributed.md
├── docker.md
├── emane.md
├── emane
│ ├── antenna.md
│ ├── eel.md
│ ├── files.md
│ ├── gpsd.md
│ └── precomputed.md
├── grpc.md
├── gui.md
├── hitl.md
├── index.md
├── install.md
├── install_docker.md
├── install_rocky.md
├── install_ubuntu.md
├── nodetypes.md
├── performance.md
├── python.md
├── services.md
├── services
│ ├── bird.md
│ ├── emane.md
│ ├── frr.md
│ ├── nrl.md
│ ├── quagga.md
│ ├── sdn.md
│ ├── security.md
│ ├── utility.md
│ └── xorp.md
├── static
│ ├── architecture.png
│ ├── controlnetwork.png
│ ├── core-gui.png
│ ├── emane-configuration.png
│ ├── emane-single-pc.png
│ ├── gui
│ │ ├── host.png
│ │ ├── hub.png
│ │ ├── lanswitch.png
│ │ ├── link.png
│ │ ├── marker.png
│ │ ├── mdr.png
│ │ ├── oval.png
│ │ ├── pc.png
│ │ ├── rectangle.png
│ │ ├── rj45.png
│ │ ├── router.png
│ │ ├── run.png
│ │ ├── select.png
│ │ ├── start.png
│ │ ├── stop.png
│ │ ├── text.png
│ │ ├── tunnel.png
│ │ └── wlan.png
│ ├── tutorial-common
│ │ ├── running-join.png
│ │ └── running-open.png
│ ├── tutorial1
│ │ ├── link-config-dialog.png
│ │ ├── link-config.png
│ │ └── scenario.png
│ ├── tutorial2
│ │ ├── wireless-config-delay.png
│ │ ├── wireless-configuration.png
│ │ └── wireless.png
│ ├── tutorial3
│ │ ├── mobility-script.png
│ │ ├── motion_continued_breaks_link.png
│ │ ├── motion_from_ns2_file.png
│ │ └── move-n2.png
│ ├── tutorial5
│ │ ├── VM-network-settings.png
│ │ ├── configure-the-rj45.png
│ │ ├── rj45-connector.png
│ │ └── rj45-unassigned.png
│ ├── tutorial6
│ │ ├── configure-icon.png
│ │ ├── create-nodes.png
│ │ ├── hidden-nodes.png
│ │ ├── linked-nodes.png
│ │ ├── only-node1-moving.png
│ │ ├── scenario-with-motion.png
│ │ ├── scenario-with-terrain.png
│ │ ├── select-wallpaper.png
│ │ └── wlan-links.png
│ ├── tutorial7
│ │ └── scenario.png
│ └── workflow.png
└── tutorials
│ ├── common
│ └── grpc.md
│ ├── overview.md
│ ├── setup.md
│ ├── tutorial1.md
│ ├── tutorial2.md
│ ├── tutorial3.md
│ ├── tutorial4.md
│ ├── tutorial5.md
│ ├── tutorial6.md
│ └── tutorial7.md
├── man
├── Makefile.am
├── core-cleanup.1
├── core-daemon.1
├── core-gui.1
├── netns.1
├── vcmd.1
└── vnoded.1
├── mkdocs.yml
├── netns
├── .gitignore
├── MANIFEST.in
├── Makefile.am
├── myerr.h
├── netns.c
├── netns.h
├── netns_main.c
├── netnsmodule.c
├── setup.py.in
├── vcmd_main.c
├── vcmdmodule.c
├── version.h.in
├── vnode_chnl.c
├── vnode_chnl.h
├── vnode_client.c
├── vnode_client.h
├── vnode_cmd.c
├── vnode_cmd.h
├── vnode_io.c
├── vnode_io.h
├── vnode_msg.c
├── vnode_msg.h
├── vnode_server.c
├── vnode_server.h
├── vnode_tlv.h
└── vnoded_main.c
├── package
├── after-install-deb.sh
├── after-install-rpm.sh
├── after-remove-deb.sh
├── after-remove-rpm.sh
├── core-daemon.service
├── etc
│ ├── core.conf
│ └── logging.conf
└── share
│ ├── examples
│ ├── controlnet_updown
│ ├── custom_emane
│ │ ├── __init__.py
│ │ └── examplemodel.py
│ ├── custom_service
│ │ ├── __init__.py
│ │ └── exampleservice.py
│ ├── docker
│ │ ├── docker2core.py
│ │ ├── docker2docker.py
│ │ └── switch.py
│ ├── grpc
│ │ ├── __init__.py
│ │ ├── custom_service.py
│ │ ├── distributed_switch.py
│ │ ├── emane80211.py
│ │ ├── peertopeer.py
│ │ ├── switch.py
│ │ ├── wireless.py
│ │ └── wlan.py
│ ├── python
│ │ ├── configure_services.py
│ │ ├── distributed_emane.py
│ │ ├── distributed_ptp.py
│ │ ├── distributed_switch.py
│ │ ├── emane80211.py
│ │ ├── peertopeer.py
│ │ ├── switch.py
│ │ ├── wireless.py
│ │ └── wlan.py
│ └── tdma
│ │ └── schedule.xml
│ ├── services
│ ├── sampleFirewall
│ ├── sampleIPsec
│ ├── sampleVPNClient
│ └── sampleVPNServer
│ └── tutorials
│ ├── chatapp
│ ├── chatapp
│ │ ├── __init__.py
│ │ ├── client.py
│ │ └── server.py
│ ├── chatapp_service.py
│ └── setup.py
│ ├── tutorial1
│ ├── scenario.py
│ ├── scenario.xml
│ ├── scenario_service.py
│ └── scenario_service.xml
│ ├── tutorial2
│ ├── scenario.py
│ └── scenario.xml
│ ├── tutorial3
│ ├── move-node2.py
│ ├── movements1.txt
│ ├── scenario.py
│ └── scenario.xml
│ ├── tutorial4
│ └── tests
│ │ ├── conftest.py
│ │ └── test_ping.py
│ ├── tutorial5
│ ├── client_for_windows.py
│ ├── scenario.py
│ └── scenario.xml
│ ├── tutorial6
│ ├── completed-scenario.xml
│ ├── demo.py
│ ├── drone.png
│ └── terrain.png
│ └── tutorial7
│ ├── scenario.py
│ ├── scenario.xml
│ ├── scenario_service.py
│ └── scenario_service.xml
├── setup.sh
└── tasks.py
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 | root = true
4 |
5 | [*]
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.py]
12 | indent_style = space
13 | indent_size = 4
14 | max_line_length = 88
15 |
16 | [*.am]
17 | indent_style = tab
18 | indent_size = 4
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG]"
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. Ubuntu 18.04]
28 | - CORE Version [e.g. 5.2.1]
29 | - EMANE Version [e.g. 1.2.4]
30 |
31 | **Additional context**
32 | Add any other context about the problem here.
33 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[FEATURE]"
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/daemon-checks.yml:
--------------------------------------------------------------------------------
1 | name: Daemon Checks
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-22.04
8 | steps:
9 | - uses: actions/checkout@v1
10 | - name: Set up Python
11 | uses: actions/setup-python@v1
12 | with:
13 | python-version: 3.10.17
14 | - name: install poetry
15 | run: |
16 | python -m pip install --upgrade pip
17 | pip install poetry
18 | cd daemon
19 | cp core/constants.py.in core/constants.py
20 | sed -i 's/required=True/required=False/g' core/emulator/coreemu.py
21 | poetry install
22 | - name: isort
23 | run: |
24 | cd daemon
25 | poetry run isort -c --df .
26 | - name: black
27 | run: |
28 | cd daemon
29 | poetry run black --check .
30 | - name: flake8
31 | run: |
32 | cd daemon
33 | poetry run flake8 .
34 | - name: grpc
35 | run: |
36 | cd daemon/proto
37 | poetry run python -m grpc_tools.protoc -I . --python_out=.. --grpc_python_out=.. core/api/grpc/*.proto
38 | - name: test
39 | run: |
40 | cd daemon
41 | poetry run pytest --mock tests
42 |
--------------------------------------------------------------------------------
/.github/workflows/documentation.yml:
--------------------------------------------------------------------------------
1 | name: documentation
2 | on:
3 | push:
4 | branches:
5 | - master
6 | permissions:
7 | contents: write
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v3
13 | - uses: actions/setup-python@v4
14 | with:
15 | python-version: 3.x
16 | - uses: actions/cache@v4
17 | with:
18 | key: ${{ github.ref }}
19 | path: .cache
20 | - run: pip install mkdocs-material
21 | - run: mkdocs gh-deploy --force
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .debbuild
2 | .deps
3 | .rpmbuild
4 | .version
5 | .version.date
6 | Makefile
7 | !kernel/**/Makefile
8 | Makefile.in
9 | aclocal.m4
10 | autom4te.cache
11 | /config
12 | config.h
13 | config.h.in
14 | config.log
15 | config.status
16 | configure
17 | configure~
18 | debian
19 | stamp-h1
20 |
21 | # python virtual environments
22 | venv
23 |
24 | # generated protobuf files
25 | *_pb2.py
26 | *_pb2_grpc.py
27 |
28 | # python build directory
29 | dist
30 |
31 | # vscode
32 | .vscode
33 |
34 | # intellij
35 | *.iml
36 | .idea
37 |
38 | # sonarqube sonar-scanner
39 | .scannerwork
40 |
41 | # ignore test coverage files
42 | coverage.xml
43 |
44 | # python files
45 | *.egg-info
46 | *.pyc
47 | *.pyi
48 |
49 | # ignore package files
50 | *.rpm
51 | *.deb
52 | *.tar.gz
53 |
54 | # pytest cache files
55 | .cache
56 |
57 | # ignore swap files
58 | *.swp
59 |
60 | # ignore built input files
61 | netns/setup.py
62 | daemon/setup.py
63 |
64 | # python
65 | __pycache__
66 |
67 | # ignore core player files
68 | *.core
69 |
--------------------------------------------------------------------------------
/ASSIGNMENT_OF_COPYRIGHT.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/ASSIGNMENT_OF_COPYRIGHT.pdf
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2005-2018, the Boeing Company.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are met:
5 |
6 | 1. Redistributions of source code must retain the above copyright notice,
7 | this list of conditions and the following disclaimer.
8 | 2. Redistributions in binary form must reproduce the above copyright notice,
9 | this list of conditions and the following disclaimer in the documentation
10 | and/or other materials provided with the distribution.
11 |
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
13 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
16 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
22 | THE POSSIBILITY OF SUCH DAMAGE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CORE
2 |
3 | CORE: Common Open Research Emulator
4 |
5 | Copyright (c)2005-2025 the Boeing Company.
6 |
7 | See the LICENSE file included in this distribution.
8 |
9 | ## About
10 |
11 | The Common Open Research Emulator (CORE) is a tool for emulating
12 | networks on one or more machines. You can connect these emulated
13 | networks to live networks. CORE consists of a GUI for drawing
14 | topologies of lightweight virtual machines, and Python modules for
15 | scripting network emulation.
16 |
17 | ## Documentation & Support
18 |
19 | We are leveraging GitHub hosted documentation and Discord for persistent
20 | chat rooms. This allows for more dynamic conversations and the
21 | capability to respond faster. Feel free to join us at the link below.
22 |
23 | * [Documentation](https://coreemu.github.io/core/)
24 | * [Discord Channel](https://discord.gg/AKd7kmP)
25 |
26 | ## Quick Start
27 |
28 | Requires Python 3.9+. More detailed instructions and install options can be found
29 | [here](https://coreemu.github.io/core/install.html).
30 |
31 | ### Package Install
32 |
33 | Grab the latest deb/rpm from [releases](https://github.com/coreemu/core/releases).
34 |
35 | This will install vnoded/vcmd, system dependencies, and CORE within a python
36 | virtual environment at `/opt/core/venv`.
37 |
38 | ```shell
39 | sudo install -y ./
40 | ```
41 |
42 | Then install OSPF MDR from source:
43 |
44 | ```shell
45 | git clone https://github.com/USNavalResearchLaboratory/ospf-mdr.git
46 | cd ospf-mdr
47 | ./bootstrap.sh
48 | ./configure --disable-doc --enable-user=root --enable-group=root \
49 | --with-cflags=-ggdb --sysconfdir=/usr/local/etc/quagga --enable-vtysh \
50 | --localstatedir=/var/run/quagga
51 | make -j$(nproc)
52 | sudo make install
53 | ```
54 |
55 | ### Script Install
56 |
57 | The following should get you up and running on Ubuntu 22.04. This would
58 | install CORE into a python3 virtual environment and install
59 | [OSPF MDR](https://github.com/USNavalResearchLaboratory/ospf-mdr) from source.
60 |
61 | ```shell
62 | git clone https://github.com/coreemu/core.git
63 | cd core
64 | # install dependencies to run installation task
65 | ./setup.sh
66 | # run the following or open a new terminal
67 | source ~/.bashrc
68 | # Ubuntu
69 | inv install
70 | # CentOS
71 | inv install -p /usr
72 | ```
73 |
--------------------------------------------------------------------------------
/bootstrap.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # Bootstrap the autoconf system.
4 | #
5 |
6 | # PASS
7 | if [ x$1 = x ]; then
8 | echo "Bootstrapping the autoconf system..."
9 | echo "(Messages below about copying and installing files are normal.)"
10 | # clean - take out the trash
11 | elif [ x$1 = xclean ]; then
12 | echo "Cleaning up the autoconf mess..."
13 | rm -rf autom4te.cache config
14 | exit 0;
15 | # help text
16 | else
17 | echo "usage: $0 [clean]"
18 | echo -n " Use this script to bootstrap the autoconf build system prior to "
19 | echo "running the "
20 | echo " ./configure script."
21 | exit 1;
22 | fi
23 |
24 | # try to keep everything nice and tidy in ./config
25 | if ! [ -d "config" ]; then
26 | mkdir config
27 | fi
28 |
29 | # bootstrapping
30 | echo "(1/4) Running aclocal..." && aclocal -I config \
31 | && echo "(2/4) Running autoheader..." && autoheader \
32 | && echo "(3/4) Running automake..." \
33 | && automake --add-missing --copy --foreign \
34 | && echo "(4/4) Running autoconf..." && autoconf \
35 | && echo "" \
36 | && echo "You are now ready to run \"./configure\"."
37 |
--------------------------------------------------------------------------------
/daemon/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: local
3 | hooks:
4 | - id: isort
5 | name: isort
6 | stages: [pre-commit]
7 | language: system
8 | entry: bash -c 'cd daemon && poetry run isort --atomic .'
9 | types: [python]
10 |
11 | - id: black
12 | name: black
13 | stages: [pre-commit]
14 | language: system
15 | entry: bash -c 'cd daemon && poetry run black .'
16 | types: [python]
17 |
18 | - id: flake8
19 | name: flake8
20 | stages: [pre-commit]
21 | language: system
22 | entry: bash -c 'cd daemon && poetry run flake8 .'
23 | types: [python]
24 |
--------------------------------------------------------------------------------
/daemon/Makefile.am:
--------------------------------------------------------------------------------
1 | # CORE
2 | #
3 | # Makefile for building netns components.
4 | #
5 |
6 | if WANT_DOCS
7 | DOCS = doc
8 | endif
9 |
10 | SUBDIRS = proto $(DOCS)
11 |
12 | # because we include entire directories with EXTRA_DIST, we need to clean up
13 | # the source control files
14 | dist-hook:
15 | -rm -rf `find $(distdir)/ -name '*.pyc'`
16 |
17 | distclean-local:
18 | -rm -rf core.egg-info
19 |
20 | DISTCLEANFILES = Makefile.in
21 |
22 | # files to include with distribution tarball
23 | EXTRA_DIST = core \
24 | doc/conf.py.in \
25 | tests \
26 | setup.cfg \
27 | poetry.lock \
28 | pyproject.toml
29 |
--------------------------------------------------------------------------------
/daemon/core/.gitignore:
--------------------------------------------------------------------------------
1 | constants.py
2 |
--------------------------------------------------------------------------------
/daemon/core/__init__.py:
--------------------------------------------------------------------------------
1 | import logging.config
2 |
3 | # setup default null handler
4 | logging.getLogger(__name__).addHandler(logging.NullHandler())
5 |
--------------------------------------------------------------------------------
/daemon/core/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/api/__init__.py
--------------------------------------------------------------------------------
/daemon/core/api/grpc/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/api/grpc/__init__.py
--------------------------------------------------------------------------------
/daemon/core/constants.py.in:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | COREDPY_VERSION: str = "@PACKAGE_VERSION@"
4 | CORE_CONF_DIR: Path = Path("@CORE_CONF_DIR@")
5 | CORE_DATA_DIR: Path = Path("@CORE_DATA_DIR@")
6 |
--------------------------------------------------------------------------------
/daemon/core/emane/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/emane/__init__.py
--------------------------------------------------------------------------------
/daemon/core/emane/models/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/emane/models/__init__.py
--------------------------------------------------------------------------------
/daemon/core/emane/models/bypass.py:
--------------------------------------------------------------------------------
1 | """
2 | EMANE Bypass model for CORE
3 | """
4 | from pathlib import Path
5 |
6 | from core.config import ConfigBool, Configuration
7 | from core.emane import emanemodel
8 |
9 |
10 | class EmaneBypassModel(emanemodel.EmaneModel):
11 | name: str = "emane_bypass"
12 |
13 | # values to ignore, when writing xml files
14 | config_ignore: set[str] = {"none"}
15 |
16 | # mac definitions
17 | mac_library: str = "bypassmaclayer"
18 | mac_config: list[Configuration] = [
19 | ConfigBool(
20 | id="none",
21 | default="0",
22 | label="There are no parameters for the bypass model.",
23 | )
24 | ]
25 |
26 | # phy definitions
27 | phy_library: str = "bypassphylayer"
28 | phy_config: list[Configuration] = []
29 |
30 | @classmethod
31 | def load(cls, emane_prefix: Path) -> None:
32 | cls._load_platform_config(emane_prefix)
33 |
--------------------------------------------------------------------------------
/daemon/core/emane/models/ieee80211abg.py:
--------------------------------------------------------------------------------
1 | """
2 | ieee80211abg.py: EMANE IEEE 802.11abg model for CORE
3 | """
4 | from pathlib import Path
5 |
6 | from core.emane import emanemodel
7 |
8 |
9 | class EmaneIeee80211abgModel(emanemodel.EmaneModel):
10 | # model name
11 | name: str = "emane_ieee80211abg"
12 |
13 | # mac configuration
14 | mac_library: str = "ieee80211abgmaclayer"
15 | mac_xml: str = "ieee80211abgmaclayer.xml"
16 |
17 | @classmethod
18 | def load(cls, emane_prefix: Path) -> None:
19 | cls.mac_defaults["pcrcurveuri"] = str(
20 | emane_prefix / "share/emane/xml/models/mac/ieee80211abg/ieee80211pcr.xml"
21 | )
22 | super().load(emane_prefix)
23 |
--------------------------------------------------------------------------------
/daemon/core/emane/models/rfpipe.py:
--------------------------------------------------------------------------------
1 | """
2 | rfpipe.py: EMANE RF-PIPE model for CORE
3 | """
4 | from pathlib import Path
5 |
6 | from core.emane import emanemodel
7 |
8 |
9 | class EmaneRfPipeModel(emanemodel.EmaneModel):
10 | # model name
11 | name: str = "emane_rfpipe"
12 |
13 | # mac configuration
14 | mac_library: str = "rfpipemaclayer"
15 | mac_xml: str = "rfpipemaclayer.xml"
16 |
17 | @classmethod
18 | def load(cls, emane_prefix: Path) -> None:
19 | cls.mac_defaults["pcrcurveuri"] = str(
20 | emane_prefix / "share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
21 | )
22 | super().load(emane_prefix)
23 |
--------------------------------------------------------------------------------
/daemon/core/emulator/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/emulator/__init__.py
--------------------------------------------------------------------------------
/daemon/core/emulator/broadcast.py:
--------------------------------------------------------------------------------
1 | from collections.abc import Callable
2 | from typing import TypeVar
3 |
4 | from core.emulator.data import AlertData, EventData, LinkData, NodeData
5 | from core.errors import CoreError
6 |
7 | T = TypeVar("T", bound=EventData | AlertData | NodeData | LinkData)
8 |
9 |
10 | class BroadcastManager:
11 | def __init__(self) -> None:
12 | """
13 | Creates a BroadcastManager instance.
14 | """
15 | self.handlers: dict[type[T], set[Callable[[T], None]]] = {}
16 |
17 | def send(self, data: T) -> None:
18 | """
19 | Retrieve handlers for data, and run all current handlers.
20 |
21 | :param data: data to provide to handlers
22 | :return: nothing
23 | """
24 | handlers = self.handlers.get(type(data), set())
25 | for handler in handlers:
26 | handler(data)
27 |
28 | def add_handler(self, data_type: type[T], handler: Callable[[T], None]) -> None:
29 | """
30 | Add a handler for a given data type.
31 |
32 | :param data_type: type of data to add handler for
33 | :param handler: handler to add
34 | :return: nothing
35 | """
36 | handlers = self.handlers.setdefault(data_type, set())
37 | if handler in handlers:
38 | raise CoreError(
39 | f"cannot add data({data_type}) handler({repr(handler)}), "
40 | f"already exists"
41 | )
42 | handlers.add(handler)
43 |
44 | def remove_handler(self, data_type: type[T], handler: Callable[[T], None]) -> None:
45 | """
46 | Remove a handler for a given data type.
47 |
48 | :param data_type: type of data to remove handler for
49 | :param handler: handler to remove
50 | :return: nothing
51 | """
52 | handlers = self.handlers.get(data_type, set())
53 | if handler not in handlers:
54 | raise CoreError(
55 | f"cannot remove data({data_type}) handler({repr(handler)}), "
56 | f"does not exist"
57 | )
58 | handlers.remove(handler)
59 |
--------------------------------------------------------------------------------
/daemon/core/errors.py:
--------------------------------------------------------------------------------
1 | """
2 | Provides CORE specific errors.
3 | """
4 | import subprocess
5 |
6 |
7 | class CoreCommandError(subprocess.CalledProcessError):
8 | """
9 | Used when encountering internal CORE command errors.
10 | """
11 |
12 | def __str__(self) -> str:
13 | return (
14 | f"command({self.cmd}), status({self.returncode}):\n"
15 | f"stdout: {self.output}\nstderr: {self.stderr}"
16 | )
17 |
18 |
19 | class CoreError(Exception):
20 | """
21 | Used for errors when dealing with CoreEmu and Sessions.
22 | """
23 |
24 | pass
25 |
26 |
27 | class CoreXmlError(Exception):
28 | """
29 | Used when there was an error parsing a CORE xml file.
30 | """
31 |
32 | pass
33 |
34 |
35 | class CoreServiceError(Exception):
36 | """
37 | Used when there is an error related to accessing a service.
38 | """
39 |
40 | pass
41 |
42 |
43 | class CoreServiceBootError(Exception):
44 | """
45 | Used when there is an error booting a service.
46 | """
47 |
48 | pass
49 |
50 |
51 | class CoreConfigError(Exception):
52 | """
53 | Used when there is an error defining a configurable option.
54 | """
55 |
56 | pass
57 |
--------------------------------------------------------------------------------
/daemon/core/executables.py:
--------------------------------------------------------------------------------
1 | BASH: str = "bash"
2 | ETHTOOL: str = "ethtool"
3 | IP: str = "ip"
4 | MOUNT: str = "mount"
5 | NFTABLES: str = "nft"
6 | OVS_VSCTL: str = "ovs-vsctl"
7 | SYSCTL: str = "sysctl"
8 | TC: str = "tc"
9 | TEST: str = "test"
10 | UMOUNT: str = "umount"
11 | VCMD: str = "vcmd"
12 | VNODED: str = "vnoded"
13 |
14 | COMMON_REQUIREMENTS: list[str] = [
15 | BASH,
16 | ETHTOOL,
17 | IP,
18 | MOUNT,
19 | NFTABLES,
20 | SYSCTL,
21 | TC,
22 | TEST,
23 | UMOUNT,
24 | VCMD,
25 | VNODED,
26 | ]
27 | OVS_REQUIREMENTS: list[str] = [OVS_VSCTL]
28 |
29 |
30 | def get_requirements(use_ovs: bool) -> list[str]:
31 | """
32 | Retrieve executable requirements needed to run CORE.
33 |
34 | :param use_ovs: True if OVS is being used, False otherwise
35 | :return: list of executable requirements
36 | """
37 | requirements = COMMON_REQUIREMENTS
38 | if use_ovs:
39 | requirements += OVS_REQUIREMENTS
40 | return requirements
41 |
--------------------------------------------------------------------------------
/daemon/core/gui/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/__init__.py
--------------------------------------------------------------------------------
/daemon/core/gui/data/backgrounds/sample1-bg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/backgrounds/sample1-bg.gif
--------------------------------------------------------------------------------
/daemon/core/gui/data/backgrounds/sample4-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/backgrounds/sample4-bg.jpg
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/OVS.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/OVS.gif
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/alert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/alert.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/antenna.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/antenna.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/cancel.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/core-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/core-icon.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/delete.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/docker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/docker.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/document-new.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/document-new.gif
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/document-properties.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/document-properties.gif
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/document-save.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/document-save.gif
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/edit-delete.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/edit-delete.gif
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/edit-node.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/edit-node.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/emane.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/emane.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/error.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/fileopen.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/fileopen.gif
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/host.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/host.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/hub.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/hub.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/lanswitch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/lanswitch.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/link.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/marker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/marker.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/markerclear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/markerclear.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/mdr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/mdr.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/observe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/observe.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/oval.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/oval.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/pause.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/pc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/pc.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/plot.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/plot.gif
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/podman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/podman.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/prouter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/prouter.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/rectangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/rectangle.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/rj45.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/rj45.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/router.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/router.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/run.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/select.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/shadow.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/shutdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/shutdown.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/start.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/stop.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/text.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/tunnel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/tunnel.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/twonode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/twonode.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/wireless.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/wireless.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/icons/wlan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/data/icons/wlan.png
--------------------------------------------------------------------------------
/daemon/core/gui/data/mobility/sample1.scen:
--------------------------------------------------------------------------------
1 | #
2 | # nodes: 4, max time: 27.000000, max x: 600.00, max y: 600.00
3 | # nominal range: 300.00 link bw: 54000000.00
4 | # pause: 30.00, min speed 1.50 max speed: 4.50
5 |
6 | $node_(6) set X_ 780.0
7 | $node_(6) set Y_ 228.0
8 | $node_(6) set Z_ 0.00
9 | $node_(7) set X_ 816.0
10 | $node_(7) set Y_ 348.0
11 | $node_(7) set Z_ 0.00
12 | $node_(8) set X_ 672.0
13 | $node_(8) set Y_ 420.0
14 | $node_(8) set Z_ 0.00
15 | $node_(9) set X_ 672.0
16 | $node_(9) set Y_ 96.0
17 | $node_(9) set Z_ 0.00
18 | $ns_ at 1.00 "$node_(6) setdest 500.0 178.0 25.0"
19 | $ns_ at 2.00 "$node_(7) setdest 400.0 288.0 15.0"
20 | $ns_ at 1.00 "$node_(8) setdest 590.0 520.0 17.0"
21 | $ns_ at 3.00 "$node_(9) setdest 720.0 300.0 20.0"
22 | $ns_ at 8.00 "$node_(7) setdest 600.0 350.0 10.0"
23 | $ns_ at 9.00 "$node_(8) setdest 730.0 300.0 15.0"
24 | $ns_ at 10.00 "$node_(6) setdest 600.0 108.0 10.0"
25 | $ns_ at 16.00 "$node_(9) setdest 672.0 96.0 20.0"
26 | $ns_ at 17.00 "$node_(7) setdest 816.0 348.0 20.0"
27 | $ns_ at 18.00 "$node_(6) setdest 780.0 228.0 25.0"
28 | $ns_ at 22.00 "$node_(8) setdest 672.0 420.0 20.0"
29 |
--------------------------------------------------------------------------------
/daemon/core/gui/dialogs/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/dialogs/__init__.py
--------------------------------------------------------------------------------
/daemon/core/gui/dialogs/about.py:
--------------------------------------------------------------------------------
1 | import tkinter as tk
2 | from tkinter import ttk
3 | from typing import TYPE_CHECKING
4 |
5 | from core.gui.dialogs.dialog import Dialog
6 | from core.gui.widgets import CodeText
7 |
8 | if TYPE_CHECKING:
9 | from core.gui.app import Application
10 |
11 | LICENSE = """\
12 | Copyright (c) 2005-2020, the Boeing Company.
13 |
14 | Redistribution and use in source and binary forms, with or without
15 | modification, are permitted provided that the following conditions are met:
16 |
17 | 1. Redistributions of source code must retain the above copyright notice,
18 | this list of conditions and the following disclaimer.
19 | 2. Redistributions in binary form must reproduce the above copyright notice,
20 | this list of conditions and the following disclaimer in the documentation
21 | and/or other materials provided with the distribution.
22 |
23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33 | THE POSSIBILITY OF SUCH DAMAGE.\
34 | """
35 |
36 |
37 | class AboutDialog(Dialog):
38 | def __init__(self, app: "Application") -> None:
39 | super().__init__(app, "About CORE")
40 | self.draw()
41 |
42 | def draw(self) -> None:
43 | self.top.columnconfigure(0, weight=1)
44 | self.top.rowconfigure(0, weight=1)
45 |
46 | codetext = CodeText(self.top)
47 | codetext.text.insert("1.0", LICENSE)
48 | codetext.text.config(state=tk.DISABLED)
49 | codetext.grid(sticky=tk.NSEW)
50 |
51 | label = ttk.Label(
52 | self.top, text="Icons from https://icons8.com", anchor=tk.CENTER
53 | )
54 | label.grid(sticky=tk.EW)
55 |
--------------------------------------------------------------------------------
/daemon/core/gui/dialogs/dialog.py:
--------------------------------------------------------------------------------
1 | import tkinter as tk
2 | from tkinter import ttk
3 | from typing import TYPE_CHECKING
4 |
5 | from core.gui import images
6 | from core.gui.images import ImageEnum
7 | from core.gui.themes import DIALOG_PAD
8 |
9 | if TYPE_CHECKING:
10 | from core.gui.app import Application
11 |
12 |
13 | class Dialog(tk.Toplevel):
14 | def __init__(
15 | self,
16 | app: "Application",
17 | title: str,
18 | modal: bool = True,
19 | master: tk.BaseWidget = None,
20 | ) -> None:
21 | if master is None:
22 | master = app
23 | super().__init__(master)
24 | self.withdraw()
25 | self.app: "Application" = app
26 | self.modal: bool = modal
27 | self.title(title)
28 | self.protocol("WM_DELETE_WINDOW", self.destroy)
29 | image = images.from_enum(ImageEnum.CORE, width=images.DIALOG_SIZE)
30 | self.tk.call("wm", "iconphoto", self._w, image)
31 | self.columnconfigure(0, weight=1)
32 | self.rowconfigure(0, weight=1)
33 | self.top: ttk.Frame = ttk.Frame(self, padding=DIALOG_PAD)
34 | self.top.grid(sticky=tk.NSEW)
35 |
36 | def show(self) -> None:
37 | self.transient(self.master)
38 | self.focus_force()
39 | self.update()
40 | self.deiconify()
41 | if self.modal:
42 | self.wait_visibility()
43 | self.grab_set()
44 | self.wait_window()
45 |
46 | def draw_spacer(self, row: int = None) -> None:
47 | frame = ttk.Frame(self.top)
48 | frame.grid(row=row, sticky=tk.NSEW)
49 | frame.rowconfigure(0, weight=1)
50 | self.top.rowconfigure(frame.grid_info()["row"], weight=1)
51 |
--------------------------------------------------------------------------------
/daemon/core/gui/dialogs/emaneinstall.py:
--------------------------------------------------------------------------------
1 | import tkinter as tk
2 | import webbrowser
3 | from tkinter import ttk
4 |
5 | from core.gui.dialogs.dialog import Dialog
6 | from core.gui.themes import PADY
7 |
8 |
9 | class EmaneInstallDialog(Dialog):
10 | def __init__(self, app) -> None:
11 | super().__init__(app, "EMANE Error")
12 | self.draw()
13 |
14 | def draw(self) -> None:
15 | self.top.columnconfigure(0, weight=1)
16 | label = ttk.Label(self.top, text="EMANE needs to be installed!")
17 | label.grid(sticky=tk.EW, pady=PADY)
18 | button = ttk.Button(
19 | self.top, text="EMANE Documentation", command=self.click_doc
20 | )
21 | button.grid(sticky=tk.EW, pady=PADY)
22 | button = ttk.Button(self.top, text="Close", command=self.destroy)
23 | button.grid(sticky=tk.EW)
24 |
25 | def click_doc(self) -> None:
26 | webbrowser.open_new("https://coreemu.github.io/core/emane.html")
27 |
--------------------------------------------------------------------------------
/daemon/core/gui/dialogs/error.py:
--------------------------------------------------------------------------------
1 | import tkinter as tk
2 | from tkinter import ttk
3 | from typing import TYPE_CHECKING
4 |
5 | from core.gui import images
6 | from core.gui.dialogs.dialog import Dialog
7 | from core.gui.images import ImageEnum
8 | from core.gui.themes import PADY
9 | from core.gui.widgets import CodeText
10 |
11 | if TYPE_CHECKING:
12 | from core.gui.app import Application
13 |
14 |
15 | class ErrorDialog(Dialog):
16 | def __init__(
17 | self, app: "Application", title: str, message: str, details: str
18 | ) -> None:
19 | super().__init__(app, title)
20 | self.message: str = message
21 | self.details: str = details
22 | self.error_message: CodeText | None = None
23 | self.draw()
24 |
25 | def draw(self) -> None:
26 | self.top.columnconfigure(0, weight=1)
27 | self.top.rowconfigure(1, weight=1)
28 | image = images.from_enum(ImageEnum.ERROR, width=images.ERROR_SIZE)
29 | label = ttk.Label(
30 | self.top, text=self.message, image=image, compound=tk.LEFT, anchor=tk.CENTER
31 | )
32 | label.image = image
33 | label.grid(sticky=tk.W, pady=PADY)
34 | self.error_message = CodeText(self.top)
35 | self.error_message.text.insert("1.0", self.details)
36 | self.error_message.text.config(state=tk.DISABLED)
37 | self.error_message.grid(sticky=tk.EW, pady=PADY)
38 | button = ttk.Button(self.top, text="Close", command=lambda: self.destroy())
39 | button.grid(sticky=tk.EW)
40 |
--------------------------------------------------------------------------------
/daemon/core/gui/dialogs/macdialog.py:
--------------------------------------------------------------------------------
1 | import tkinter as tk
2 | from tkinter import messagebox, ttk
3 | from typing import TYPE_CHECKING
4 |
5 | import netaddr
6 |
7 | from core.gui.dialogs.dialog import Dialog
8 | from core.gui.themes import PADX, PADY
9 |
10 | if TYPE_CHECKING:
11 | from core.gui.app import Application
12 |
13 |
14 | class MacConfigDialog(Dialog):
15 | def __init__(self, app: "Application") -> None:
16 | super().__init__(app, "MAC Configuration")
17 | mac = self.app.guiconfig.mac
18 | self.mac_var: tk.StringVar = tk.StringVar(value=mac)
19 | self.draw()
20 |
21 | def draw(self) -> None:
22 | self.top.columnconfigure(0, weight=1)
23 | self.top.rowconfigure(0, weight=1)
24 |
25 | # draw explanation label
26 | text = (
27 | "MAC addresses will be generated for nodes starting with the\n"
28 | "provided value below and increment by value in order."
29 | )
30 | label = ttk.Label(self.top, text=text)
31 | label.grid(sticky=tk.EW, pady=PADY)
32 |
33 | # draw input
34 | frame = ttk.Frame(self.top)
35 | frame.columnconfigure(0, weight=1)
36 | frame.columnconfigure(1, weight=3)
37 | frame.grid(stick="ew", pady=PADY)
38 | label = ttk.Label(frame, text="Starting MAC")
39 | label.grid(row=0, column=0, sticky=tk.EW, padx=PADX)
40 | entry = ttk.Entry(frame, textvariable=self.mac_var)
41 | entry.grid(row=0, column=1, sticky=tk.EW)
42 |
43 | # draw buttons
44 | frame = ttk.Frame(self.top)
45 | frame.grid(stick="ew")
46 | for i in range(2):
47 | frame.columnconfigure(i, weight=1)
48 | button = ttk.Button(frame, text="Save", command=self.click_save)
49 | button.grid(row=0, column=0, sticky=tk.EW, padx=PADX)
50 | button = ttk.Button(frame, text="Cancel", command=self.destroy)
51 | button.grid(row=0, column=1, sticky=tk.EW)
52 |
53 | def click_save(self) -> None:
54 | mac = self.mac_var.get()
55 | if not netaddr.valid_mac(mac):
56 | messagebox.showerror("MAC Error", f"{mac} is an invalid mac")
57 | else:
58 | self.app.core.ifaces_manager.mac = netaddr.EUI(mac)
59 | self.app.guiconfig.mac = mac
60 | self.app.save_config()
61 | self.destroy()
62 |
--------------------------------------------------------------------------------
/daemon/core/gui/dialogs/mobilityconfig.py:
--------------------------------------------------------------------------------
1 | """
2 | mobility configuration
3 | """
4 | import tkinter as tk
5 | from tkinter import ttk
6 | from typing import TYPE_CHECKING
7 |
8 | import grpc
9 |
10 | from core.api.grpc.wrappers import ConfigOption, Node
11 | from core.gui.dialogs.dialog import Dialog
12 | from core.gui.themes import PADX, PADY
13 | from core.gui.widgets import ConfigFrame
14 |
15 | if TYPE_CHECKING:
16 | from core.gui.app import Application
17 |
18 |
19 | class MobilityConfigDialog(Dialog):
20 | def __init__(self, app: "Application", node: Node) -> None:
21 | super().__init__(app, f"{node.name} Mobility Configuration")
22 | self.node: Node = node
23 | self.config_frame: ConfigFrame | None = None
24 | self.has_error: bool = False
25 | try:
26 | config = self.node.mobility_config
27 | if not config:
28 | config = self.app.core.get_mobility_config(self.node.id)
29 | self.config: dict[str, ConfigOption] = config
30 | self.draw()
31 | except grpc.RpcError as e:
32 | self.app.show_grpc_exception("Get Mobility Config Error", e)
33 | self.has_error: bool = True
34 | self.destroy()
35 |
36 | def draw(self) -> None:
37 | self.top.columnconfigure(0, weight=1)
38 | self.top.rowconfigure(0, weight=1)
39 | self.config_frame = ConfigFrame(self.top, self.app, self.config)
40 | self.config_frame.draw_config()
41 | self.config_frame.grid(sticky=tk.NSEW, pady=PADY)
42 | self.draw_apply_buttons()
43 |
44 | def draw_apply_buttons(self) -> None:
45 | frame = ttk.Frame(self.top)
46 | frame.grid(sticky=tk.EW)
47 | for i in range(2):
48 | frame.columnconfigure(i, weight=1)
49 |
50 | button = ttk.Button(frame, text="Apply", command=self.click_apply)
51 | button.grid(row=0, column=0, padx=PADX, sticky=tk.EW)
52 |
53 | button = ttk.Button(frame, text="Cancel", command=self.destroy)
54 | button.grid(row=0, column=1, sticky=tk.EW)
55 |
56 | def click_apply(self) -> None:
57 | self.config_frame.parse_config()
58 | self.node.mobility_config = self.config
59 | self.destroy()
60 |
--------------------------------------------------------------------------------
/daemon/core/gui/dialogs/sessionoptions.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import tkinter as tk
3 | from tkinter import ttk
4 | from typing import TYPE_CHECKING
5 |
6 | from core.gui.dialogs.dialog import Dialog
7 | from core.gui.themes import PADX, PADY
8 | from core.gui.widgets import ConfigFrame
9 |
10 | logger = logging.getLogger(__name__)
11 |
12 | if TYPE_CHECKING:
13 | from core.gui.app import Application
14 |
15 |
16 | class SessionOptionsDialog(Dialog):
17 | def __init__(self, app: "Application") -> None:
18 | super().__init__(app, "Session Options")
19 | self.config_frame: ConfigFrame | None = None
20 | self.has_error: bool = False
21 | self.enabled: bool = not self.app.core.is_runtime()
22 | if not self.has_error:
23 | self.draw()
24 |
25 | def draw(self) -> None:
26 | self.top.columnconfigure(0, weight=1)
27 | self.top.rowconfigure(0, weight=1)
28 | options = self.app.core.session.options
29 | self.config_frame = ConfigFrame(self.top, self.app, options, self.enabled)
30 | self.config_frame.draw_config()
31 | self.config_frame.grid(sticky=tk.NSEW, pady=PADY)
32 |
33 | frame = ttk.Frame(self.top)
34 | frame.grid(sticky=tk.EW)
35 | for i in range(2):
36 | frame.columnconfigure(i, weight=1)
37 | state = tk.NORMAL if self.enabled else tk.DISABLED
38 | button = ttk.Button(frame, text="Save", command=self.save, state=state)
39 | button.grid(row=0, column=0, padx=PADX, sticky=tk.EW)
40 | button = ttk.Button(frame, text="Cancel", command=self.destroy)
41 | button.grid(row=0, column=1, sticky=tk.EW)
42 |
43 | def save(self) -> None:
44 | config = self.config_frame.parse_config()
45 | for key, value in config.items():
46 | self.app.core.session.options[key].value = value
47 | self.destroy()
48 |
--------------------------------------------------------------------------------
/daemon/core/gui/dialogs/wirelessconfig.py:
--------------------------------------------------------------------------------
1 | import tkinter as tk
2 | from tkinter import ttk
3 | from typing import TYPE_CHECKING
4 |
5 | import grpc
6 |
7 | from core.api.grpc.wrappers import ConfigOption, Node
8 | from core.gui.dialogs.dialog import Dialog
9 | from core.gui.themes import PADX, PADY
10 | from core.gui.widgets import ConfigFrame
11 |
12 | if TYPE_CHECKING:
13 | from core.gui.app import Application
14 | from core.gui.graph.node import CanvasNode
15 |
16 |
17 | class WirelessConfigDialog(Dialog):
18 | def __init__(self, app: "Application", canvas_node: "CanvasNode"):
19 | super().__init__(app, f"Wireless Configuration - {canvas_node.core_node.name}")
20 | self.node: Node = canvas_node.core_node
21 | self.config_frame: ConfigFrame | None = None
22 | self.config: dict[str, ConfigOption] = {}
23 | try:
24 | config = self.node.wireless_config
25 | if not config:
26 | config = self.app.core.get_wireless_config(self.node.id)
27 | self.config: dict[str, ConfigOption] = config
28 | self.draw()
29 | except grpc.RpcError as e:
30 | self.app.show_grpc_exception("Wireless Config Error", e)
31 | self.has_error: bool = True
32 | self.destroy()
33 |
34 | def draw(self) -> None:
35 | self.top.columnconfigure(0, weight=1)
36 | self.top.rowconfigure(0, weight=1)
37 | self.config_frame = ConfigFrame(self.top, self.app, self.config)
38 | self.config_frame.draw_config()
39 | self.config_frame.grid(sticky=tk.NSEW, pady=PADY)
40 | self.draw_buttons()
41 |
42 | def draw_buttons(self) -> None:
43 | frame = ttk.Frame(self.top)
44 | frame.grid(sticky=tk.EW)
45 | for i in range(2):
46 | frame.columnconfigure(i, weight=1)
47 | button = ttk.Button(frame, text="Apply", command=self.click_apply)
48 | button.grid(row=0, column=0, padx=PADX, sticky=tk.EW)
49 | button = ttk.Button(frame, text="Cancel", command=self.destroy)
50 | button.grid(row=0, column=1, sticky=tk.EW)
51 |
52 | def click_apply(self) -> None:
53 | self.config_frame.parse_config()
54 | self.node.wireless_config = self.config
55 | self.destroy()
56 |
--------------------------------------------------------------------------------
/daemon/core/gui/frames/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/frames/__init__.py
--------------------------------------------------------------------------------
/daemon/core/gui/frames/base.py:
--------------------------------------------------------------------------------
1 | import tkinter as tk
2 | from tkinter import ttk
3 | from typing import TYPE_CHECKING
4 |
5 | from core.gui.themes import FRAME_PAD, PADX, PADY
6 |
7 | if TYPE_CHECKING:
8 | from core.gui.app import Application
9 |
10 |
11 | class InfoFrameBase(ttk.Frame):
12 | def __init__(self, master: tk.BaseWidget, app: "Application") -> None:
13 | super().__init__(master, padding=FRAME_PAD)
14 | self.app: "Application" = app
15 |
16 | def draw(self) -> None:
17 | raise NotImplementedError
18 |
19 |
20 | class DetailsFrame(ttk.Frame):
21 | def __init__(self, master: tk.BaseWidget) -> None:
22 | super().__init__(master)
23 | self.columnconfigure(1, weight=1)
24 | self.row = 0
25 |
26 | def add_detail(self, label: str, value: str) -> None:
27 | label = ttk.Label(self, text=label, anchor=tk.W)
28 | label.grid(row=self.row, sticky=tk.EW, column=0, padx=PADX)
29 | label = ttk.Label(self, text=value, anchor=tk.W, state=tk.DISABLED)
30 | label.grid(row=self.row, sticky=tk.EW, column=1)
31 | self.row += 1
32 |
33 | def add_separator(self) -> None:
34 | separator = ttk.Separator(self)
35 | separator.grid(row=self.row, sticky=tk.EW, columnspan=2, pady=PADY)
36 | self.row += 1
37 |
--------------------------------------------------------------------------------
/daemon/core/gui/frames/default.py:
--------------------------------------------------------------------------------
1 | import tkinter as tk
2 | from tkinter import ttk
3 | from typing import TYPE_CHECKING
4 |
5 | from core.gui.frames.base import InfoFrameBase
6 |
7 | if TYPE_CHECKING:
8 | from core.gui.app import Application
9 |
10 |
11 | class DefaultInfoFrame(InfoFrameBase):
12 | def __init__(self, master: tk.BaseWidget, app: "Application") -> None:
13 | super().__init__(master, app)
14 |
15 | def draw(self) -> None:
16 | label = ttk.Label(self, text="Click a Node/Link", anchor=tk.CENTER)
17 | label.grid(sticky=tk.EW)
18 | label = ttk.Label(self, text="to see details", anchor=tk.CENTER)
19 | label.grid(sticky=tk.EW)
20 |
--------------------------------------------------------------------------------
/daemon/core/gui/frames/node.py:
--------------------------------------------------------------------------------
1 | import tkinter as tk
2 | from typing import TYPE_CHECKING
3 |
4 | from core.api.grpc.wrappers import NodeType
5 | from core.gui import nodeutils as nutils
6 | from core.gui.frames.base import DetailsFrame, InfoFrameBase
7 |
8 | if TYPE_CHECKING:
9 | from core.gui.app import Application
10 | from core.gui.graph.node import CanvasNode
11 |
12 |
13 | class NodeInfoFrame(InfoFrameBase):
14 | def __init__(self, master, app: "Application", canvas_node: "CanvasNode") -> None:
15 | super().__init__(master, app)
16 | self.canvas_node: "CanvasNode" = canvas_node
17 |
18 | def draw(self) -> None:
19 | self.columnconfigure(0, weight=1)
20 | node = self.canvas_node.core_node
21 | frame = DetailsFrame(self)
22 | frame.grid(sticky=tk.EW)
23 | frame.add_detail("ID", str(node.id))
24 | frame.add_detail("Name", node.name)
25 | if nutils.is_model(node):
26 | frame.add_detail("Type", node.model)
27 | if nutils.is_container(node):
28 | for index, service in enumerate(sorted(node.services)):
29 | if index == 0:
30 | frame.add_detail("Services", service)
31 | else:
32 | frame.add_detail("", service)
33 | if node.type == NodeType.EMANE:
34 | emane = "".join(node.emane.split("_")[1:])
35 | frame.add_detail("EMANE", emane)
36 | if nutils.has_image(node.type):
37 | frame.add_detail("Image", node.image)
38 | if nutils.is_container(node):
39 | server = node.server if node.server else "localhost"
40 | frame.add_detail("Server", server)
41 |
--------------------------------------------------------------------------------
/daemon/core/gui/graph/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/gui/graph/__init__.py
--------------------------------------------------------------------------------
/daemon/core/gui/graph/enums.py:
--------------------------------------------------------------------------------
1 | import enum
2 |
3 |
4 | class GraphMode(enum.Enum):
5 | SELECT = 0
6 | EDGE = 1
7 | PICKNODE = 2
8 | NODE = 3
9 | ANNOTATION = 4
10 | OTHER = 5
11 |
12 |
13 | class ScaleOption(enum.Enum):
14 | NONE = 0
15 | UPPER_LEFT = 1
16 | CENTERED = 2
17 | SCALED = 3
18 | TILED = 4
19 |
--------------------------------------------------------------------------------
/daemon/core/gui/graph/shapeutils.py:
--------------------------------------------------------------------------------
1 | import enum
2 |
3 |
4 | class ShapeType(enum.Enum):
5 | MARKER = "marker"
6 | OVAL = "oval"
7 | RECTANGLE = "rectangle"
8 | TEXT = "text"
9 |
10 |
11 | SHAPES: set[ShapeType] = {ShapeType.OVAL, ShapeType.RECTANGLE}
12 |
13 |
14 | def is_draw_shape(shape_type: ShapeType) -> bool:
15 | return shape_type in SHAPES
16 |
17 |
18 | def is_shape_text(shape_type: ShapeType) -> bool:
19 | return shape_type == ShapeType.TEXT
20 |
21 |
22 | def is_marker(shape_type: ShapeType) -> bool:
23 | return shape_type == ShapeType.MARKER
24 |
--------------------------------------------------------------------------------
/daemon/core/gui/graph/tags.py:
--------------------------------------------------------------------------------
1 | ANNOTATION: str = "annotation"
2 | GRIDLINE: str = "gridline"
3 | SHAPE: str = "shape"
4 | SHAPE_TEXT: str = "shapetext"
5 | EDGE: str = "edge"
6 | LOSS_EDGES: str = "loss-edge"
7 | LINK_LABEL: str = "linklabel"
8 | WIRELESS_EDGE: str = "wireless"
9 | ANTENNA: str = "antenna"
10 | NODE_LABEL: str = "nodename"
11 | NODE: str = "node"
12 | WALLPAPER: str = "wallpaper"
13 | SELECTION: str = "selectednodes"
14 | MARKER: str = "marker"
15 | HIDDEN: str = "hidden"
16 | ORGANIZE_TAGS: list[str] = [
17 | WALLPAPER,
18 | GRIDLINE,
19 | SHAPE,
20 | SHAPE_TEXT,
21 | EDGE,
22 | WIRELESS_EDGE,
23 | LINK_LABEL,
24 | ANTENNA,
25 | NODE,
26 | NODE_LABEL,
27 | SELECTION,
28 | MARKER,
29 | ]
30 | RESET_TAGS: list[str] = [
31 | EDGE,
32 | NODE,
33 | NODE_LABEL,
34 | WALLPAPER,
35 | LINK_LABEL,
36 | ANTENNA,
37 | WIRELESS_EDGE,
38 | SELECTION,
39 | SHAPE,
40 | SHAPE_TEXT,
41 | MARKER,
42 | ]
43 |
--------------------------------------------------------------------------------
/daemon/core/gui/task.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import threading
3 | import time
4 | import tkinter as tk
5 | from typing import TYPE_CHECKING, Any, Callable
6 |
7 | logger = logging.getLogger(__name__)
8 |
9 | if TYPE_CHECKING:
10 | from core.gui.app import Application
11 |
12 |
13 | class ProgressTask:
14 | def __init__(
15 | self,
16 | app: "Application",
17 | title: str,
18 | task: Callable,
19 | callback: Callable = None,
20 | args: tuple[Any] = None,
21 | ):
22 | self.app: "Application" = app
23 | self.title: str = title
24 | self.task: Callable = task
25 | self.callback: Callable = callback
26 | if args is None:
27 | args = ()
28 | self.args: tuple[Any] = args
29 | self.time: float | None = None
30 |
31 | def start(self) -> None:
32 | self.app.progress.grid(sticky=tk.EW, columnspan=2)
33 | self.app.progress.start()
34 | self.time = time.perf_counter()
35 | thread = threading.Thread(target=self.run, daemon=True)
36 | thread.start()
37 |
38 | def run(self) -> None:
39 | try:
40 | values = self.task(*self.args)
41 | if values is None:
42 | values = ()
43 | elif values is not None and not isinstance(values, tuple):
44 | values = (values,)
45 | if self.callback:
46 | self.app.after(0, self.callback, *values)
47 | except Exception as e:
48 | logger.exception("progress task exception")
49 | self.app.show_exception("Task Error", e)
50 | finally:
51 | self.app.after(0, self.complete)
52 |
53 | def complete(self) -> None:
54 | self.app.progress.stop()
55 | self.app.progress.grid_forget()
56 | total = time.perf_counter() - self.time
57 | self.time = None
58 | message = f"{self.title} ran for {total:.3f} seconds"
59 | self.app.statusbar.set_status(message)
60 |
--------------------------------------------------------------------------------
/daemon/core/gui/tooltip.py:
--------------------------------------------------------------------------------
1 | import tkinter as tk
2 | from tkinter import ttk
3 |
4 | from core.gui.themes import Styles
5 |
6 |
7 | class Tooltip:
8 | """
9 | Create tool tip for a given widget
10 | """
11 |
12 | def __init__(self, widget: tk.BaseWidget, text: str = "widget info") -> None:
13 | self.widget: tk.BaseWidget = widget
14 | self.text: str = text
15 | self.widget.bind("", self.on_enter)
16 | self.widget.bind("", self.on_leave)
17 | self.waittime: int = 400
18 | self.id: str | None = None
19 | self.tw: tk.Toplevel | None = None
20 |
21 | def on_enter(self, event: tk.Event = None) -> None:
22 | self.schedule()
23 |
24 | def on_leave(self, event: tk.Event = None) -> None:
25 | self.unschedule()
26 | self.close(event)
27 |
28 | def schedule(self):
29 | self.unschedule()
30 | self.id = self.widget.after(self.waittime, self.enter)
31 |
32 | def unschedule(self):
33 | id_ = self.id
34 | self.id = None
35 | if id_:
36 | self.widget.after_cancel(id_)
37 |
38 | def enter(self, event: tk.Event = None):
39 | x, y, cx, cy = self.widget.bbox("insert")
40 | x += self.widget.winfo_rootx()
41 | y += self.widget.winfo_rooty() + 32
42 | self.tw = tk.Toplevel(self.widget)
43 | self.tw.wm_overrideredirect(True)
44 | self.tw.wm_geometry(f"+{x:d}+{y:d}")
45 | self.tw.rowconfigure(0, weight=1)
46 | self.tw.columnconfigure(0, weight=1)
47 | frame = ttk.Frame(self.tw, style=Styles.tooltip_frame, padding=3)
48 | frame.grid(sticky=tk.NSEW)
49 | label = ttk.Label(frame, text=self.text, style=Styles.tooltip)
50 | label.grid()
51 |
52 | def close(self, event: tk.Event = None):
53 | if self.tw:
54 | self.tw.destroy()
55 |
--------------------------------------------------------------------------------
/daemon/core/gui/utils.py:
--------------------------------------------------------------------------------
1 | def bandwidth_text(bandwidth: int) -> str:
2 | size = {0: "bps", 1: "Kbps", 2: "Mbps", 3: "Gbps"}
3 | unit = 1000
4 | i = 0
5 | while bandwidth > unit:
6 | bandwidth /= unit
7 | i += 1
8 | if i == 3:
9 | break
10 | return f"{bandwidth} {size[i]}"
11 |
12 |
13 | def delay_jitter_text(delay: int, jitter: int) -> str | None:
14 | line = None
15 | if delay > 0 and jitter > 0:
16 | line = f"{delay} us (\u00B1{jitter} us)"
17 | elif jitter > 0:
18 | line = f"0 us (\u00B1{jitter} us)"
19 | return line
20 |
--------------------------------------------------------------------------------
/daemon/core/location/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/location/__init__.py
--------------------------------------------------------------------------------
/daemon/core/nodes/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/nodes/__init__.py
--------------------------------------------------------------------------------
/daemon/core/plugins/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/plugins/__init__.py
--------------------------------------------------------------------------------
/daemon/core/scripts/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/scripts/__init__.py
--------------------------------------------------------------------------------
/daemon/core/scripts/gui.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import logging
3 | from logging.handlers import TimedRotatingFileHandler
4 |
5 | from core import utils
6 | from core.gui import appconfig, images
7 | from core.gui.app import Application
8 |
9 |
10 | def main() -> None:
11 | # parse flags
12 | parser = argparse.ArgumentParser(description="CORE Python GUI")
13 | parser.add_argument(
14 | "-l",
15 | "--level",
16 | choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
17 | default="INFO",
18 | help="logging level",
19 | )
20 | parser.add_argument("-p", "--proxy", action="store_true", help="enable proxy")
21 | parser.add_argument("-s", "--session", type=int, help="session id to join")
22 | parser.add_argument(
23 | "--create-dir", action="store_true", help="create gui directory and exit"
24 | )
25 | args = parser.parse_args()
26 |
27 | # check home directory exists and create if necessary
28 | appconfig.check_directory()
29 | if args.create_dir:
30 | return
31 |
32 | # setup logging
33 | log_format = "%(asctime)s - %(levelname)s - %(module)s:%(funcName)s - %(message)s"
34 | stream_handler = logging.StreamHandler()
35 | file_handler = TimedRotatingFileHandler(
36 | filename=appconfig.LOG_PATH, when="D", backupCount=5
37 | )
38 | log_level = logging.getLevelName(args.level)
39 | logging.basicConfig(
40 | level=log_level, format=log_format, handlers=[stream_handler, file_handler]
41 | )
42 | logging.getLogger("PIL").setLevel(logging.ERROR)
43 |
44 | # enable xhost for root
45 | if utils.which("xhost", False):
46 | utils.cmd("xhost +SI:localuser:root")
47 |
48 | # start app
49 | images.load_all()
50 | app = Application(args.proxy, args.session)
51 | app.mainloop()
52 |
53 |
54 | if __name__ == "__main__":
55 | main()
56 |
--------------------------------------------------------------------------------
/daemon/core/scripts/player.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import logging
3 | import sys
4 | from pathlib import Path
5 |
6 | from core.player import CorePlayer
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 |
11 | def path_type(value: str) -> Path:
12 | file_path = Path(value)
13 | if not file_path.is_file():
14 | raise argparse.ArgumentTypeError(f"file does not exist: {value}")
15 | return file_path
16 |
17 |
18 | def parse_args() -> argparse.Namespace:
19 | """
20 | Setup and parse command line arguments.
21 |
22 | :return: parsed arguments
23 | """
24 | parser = argparse.ArgumentParser(
25 | description="core player runs files that can move nodes and send commands",
26 | formatter_class=argparse.ArgumentDefaultsHelpFormatter,
27 | )
28 | parser.add_argument(
29 | "-f", "--file", required=True, type=path_type, help="core file to play"
30 | )
31 | parser.add_argument(
32 | "-s",
33 | "--session",
34 | type=int,
35 | help="session to play to, first found session otherwise",
36 | )
37 | return parser.parse_args()
38 |
39 |
40 | def main() -> None:
41 | logging.basicConfig(level=logging.INFO)
42 | args = parse_args()
43 | player = CorePlayer(args.file)
44 | result = player.init(args.session)
45 | if not result:
46 | sys.exit(1)
47 | player.start()
48 |
49 |
50 | if __name__ == "__main__":
51 | main()
52 |
--------------------------------------------------------------------------------
/daemon/core/services/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/services/__init__.py
--------------------------------------------------------------------------------
/daemon/core/services/defaults/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/services/defaults/__init__.py
--------------------------------------------------------------------------------
/daemon/core/services/defaults/frrservices/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/services/defaults/frrservices/__init__.py
--------------------------------------------------------------------------------
/daemon/core/services/defaults/frrservices/templates/usr/local/etc/frr/daemons:
--------------------------------------------------------------------------------
1 | #
2 | # When activation a daemon at the first time, a config file, even if it is
3 | # empty, has to be present *and* be owned by the user and group "frr", else
4 | # the daemon will not be started by /etc/init.d/frr. The permissions should
5 | # be u=rw,g=r,o=.
6 | # When using "vtysh" such a config file is also needed. It should be owned by
7 | # group "frrvty" and set to ug=rw,o= though. Check /etc/pam.d/frr, too.
8 | #
9 | # The watchfrr and zebra daemons are always started.
10 | #
11 | bgpd=yes
12 | ospfd=yes
13 | ospf6d=yes
14 | ripd=yes
15 | ripngd=yes
16 | isisd=yes
17 | pimd=yes
18 | ldpd=yes
19 | nhrpd=yes
20 | eigrpd=yes
21 | babeld=yes
22 | sharpd=yes
23 | staticd=yes
24 | pbrd=yes
25 | bfdd=yes
26 | fabricd=yes
27 |
28 | #
29 | # If this option is set the /etc/init.d/frr script automatically loads
30 | # the config via "vtysh -b" when the servers are started.
31 | # Check /etc/pam.d/frr if you intend to use "vtysh"!
32 | #
33 | vtysh_enable=yes
34 | zebra_options=" -A 127.0.0.1 -s 90000000"
35 | bgpd_options=" -A 127.0.0.1"
36 | ospfd_options=" -A 127.0.0.1"
37 | ospf6d_options=" -A ::1"
38 | ripd_options=" -A 127.0.0.1"
39 | ripngd_options=" -A ::1"
40 | isisd_options=" -A 127.0.0.1"
41 | pimd_options=" -A 127.0.0.1"
42 | ldpd_options=" -A 127.0.0.1"
43 | nhrpd_options=" -A 127.0.0.1"
44 | eigrpd_options=" -A 127.0.0.1"
45 | babeld_options=" -A 127.0.0.1"
46 | sharpd_options=" -A 127.0.0.1"
47 | pbrd_options=" -A 127.0.0.1"
48 | staticd_options="-A 127.0.0.1"
49 | bfdd_options=" -A 127.0.0.1"
50 | fabricd_options="-A 127.0.0.1"
51 |
52 | # The list of daemons to watch is automatically generated by the init script.
53 | #watchfrr_options=""
54 |
55 | # for debugging purposes, you can specify a "wrap" command to start instead
56 | # of starting the daemon directly, e.g. to use valgrind on ospfd:
57 | # ospfd_wrap="/usr/bin/valgrind"
58 | # or you can use "all_wrap" for all daemons, e.g. to use perf record:
59 | # all_wrap="/usr/bin/perf record --call-graph -"
60 | # the normal daemon command is added to this at the end.
61 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/frrservices/templates/usr/local/etc/frr/frr.conf:
--------------------------------------------------------------------------------
1 | % for iface, ip4s, ip6s, is_control in ifaces:
2 | interface ${iface.name}
3 | % if want_ip4:
4 | % for addr in ip4s:
5 | ip address ${addr}
6 | % endfor
7 | % endif
8 | % if want_ip6:
9 | % for addr in ip6s:
10 | ipv6 address ${addr}
11 | % endfor
12 | % endif
13 | % if not is_control:
14 | % for service in services:
15 | % for line in service.frr_iface_config(iface).split("\n"):
16 | ${line}
17 | % endfor
18 | % endfor
19 | % endif
20 | !
21 | % endfor
22 |
23 | % for service in services:
24 | ${service.frr_config()}
25 | % endfor
26 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/frrservices/templates/usr/local/etc/frr/vtysh.conf:
--------------------------------------------------------------------------------
1 | service integrated-vtysh-config
2 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/nrlservices/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/services/defaults/nrlservices/__init__.py
--------------------------------------------------------------------------------
/daemon/core/services/defaults/nrlservices/templates/mgensink.sh:
--------------------------------------------------------------------------------
1 | mgen input sink.mgen output mgen_${node.name}.log
2 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/nrlservices/templates/nrlnhdp.sh:
--------------------------------------------------------------------------------
1 | <%
2 | ifaces = "-i " + " -i ".join(ifnames)
3 | smf = ""
4 | if has_smf:
5 | smf = "-flooding ecds -smfClient %s_smf" % node.name
6 | %>
7 | nrlnhdp -l /var/log/nrlnhdp.log -rpipe ${node.name}_nhdp ${smf} ${ifaces}
8 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/nrlservices/templates/nrlolsrd.sh:
--------------------------------------------------------------------------------
1 | <%
2 | smf = ""
3 | if has_smf:
4 | smf = "-flooding s-mpr -smfClient %s_smf" % node.name
5 | zebra = ""
6 | if has_zebra:
7 | zebra = "-z"
8 | %>
9 | nrlolsrd -i ${ifname} -l /var/log/nrlolsrd.log -rpipe ${node.name}_olsr ${smf} ${zebra}
10 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/nrlservices/templates/nrlolsrv2.sh:
--------------------------------------------------------------------------------
1 | <%
2 | ifaces = "-i " + " -i ".join(ifnames)
3 | smf = ""
4 | if has_smf:
5 | smf = "-flooding ecds -smfClient %s_smf" % node.name
6 | %>
7 | nrlolsrv2 -l /var/log/nrlolsrv2.log -rpipe ${node.name}_olsrv2 -p olsr ${smf} ${ifaces}
8 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/nrlservices/templates/olsrd.sh:
--------------------------------------------------------------------------------
1 | <%
2 | ifaces = "-i " + " -i ".join(ifnames)
3 | %>
4 | olsrd ${ifaces}
5 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/nrlservices/templates/sink.mgen:
--------------------------------------------------------------------------------
1 | 0.0 LISTEN UDP 5000
2 | % for ifname in ifnames:
3 | 0.0 Join 224.225.1.2 INTERFACE ${ifname}
4 | % endfor
5 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/nrlservices/templates/start_mgen_actor.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # auto-generated by MgenActor service
3 | mgenBasicActor.py -n ${node.name} -a 0.0.0.0 < /dev/null > /dev/null 2>&1 &
4 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/nrlservices/templates/startsmf.sh:
--------------------------------------------------------------------------------
1 | <%
2 | ifaces = ",".join(ifnames)
3 | if has_nhdp:
4 | flood = "ecds"
5 | elif has_olsr:
6 | flood = "smpr"
7 | else:
8 | flood = "cf"
9 | %>
10 | #!/bin/sh
11 | # auto-generated by NrlSmf service
12 | nrlsmf instance ${node.name}_smf ${flood} ${ifaces} hash MD5 log /var/log/nrlsmf.log < /dev/null > /dev/null 2>&1 &
13 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/quaggaservices/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/services/defaults/quaggaservices/__init__.py
--------------------------------------------------------------------------------
/daemon/core/services/defaults/quaggaservices/templates/usr/local/etc/quagga/Quagga.conf:
--------------------------------------------------------------------------------
1 | % for iface, ip4s, ip6s, configs in ifaces:
2 | interface ${iface.name}
3 | % if want_ip4:
4 | % for addr in ip4s:
5 | ip address ${addr}
6 | % endfor
7 | % endif
8 | % if want_ip6:
9 | % for addr in ip6s:
10 | ipv6 address ${addr}
11 | % endfor
12 | % endif
13 | % for config in configs:
14 | % for line in config:
15 | ${line}
16 | % endfor
17 | % endfor
18 | !
19 | % endfor
20 |
21 | % for service in services:
22 | ${service.quagga_config()}
23 | % endfor
24 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/quaggaservices/templates/usr/local/etc/quagga/vtysh.conf:
--------------------------------------------------------------------------------
1 | service integrated-vtysh-config
2 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/securityservices/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/services/defaults/securityservices/__init__.py
--------------------------------------------------------------------------------
/daemon/core/services/defaults/securityservices/templates/firewall.sh:
--------------------------------------------------------------------------------
1 | # -------- CUSTOMIZATION REQUIRED --------
2 | #
3 | # Below are sample iptables firewall rules that you can uncomment and edit.
4 | # You can also use ip6tables rules for IPv6.
5 | #
6 |
7 | # start by flushing all firewall rules (so this script may be re-run)
8 | #iptables -F
9 |
10 | # allow traffic related to established connections
11 | #iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
12 |
13 | # allow TCP packets from any source destined for 192.168.1.1
14 | #iptables -A INPUT -s 0/0 -i eth0 -d 192.168.1.1 -p TCP -j ACCEPT
15 |
16 | # allow OpenVPN server traffic from eth0
17 | #iptables -A INPUT -p udp --dport 1194 -j ACCEPT
18 | #iptables -A INPUT -i eth0 -j DROP
19 | #iptables -A OUTPUT -p udp --sport 1194 -j ACCEPT
20 | #iptables -A OUTPUT -o eth0 -j DROP
21 |
22 | # allow ICMP ping traffic
23 | #iptables -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
24 | #iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
25 |
26 | # allow SSH traffic
27 | #iptables -A -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
28 |
29 | # drop all other traffic coming in eth0
30 | #iptables -A INPUT -i eth0 -j DROP
31 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/securityservices/templates/nat.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # generated by security.py
3 | # NAT out the first interface by default
4 | % for index, ifname in enumerate(ifnames):
5 | % if index == 0:
6 | iptables -t nat -A POSTROUTING -o ${ifname} -j MASQUERADE
7 | iptables -A FORWARD -i ${ifname} -m state --state RELATED,ESTABLISHED -j ACCEPT
8 | iptables -A FORWARD -i ${ifname} -j DROP
9 | % else:
10 | # iptables -t nat -A POSTROUTING -o ${ifname} -j MASQUERADE
11 | # iptables -A FORWARD -i ${ifname} -m state --state RELATED,ESTABLISHED -j ACCEPT
12 | # iptables -A FORWARD -i ${ifname} -j DROP
13 | % endif
14 | % endfor
15 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/securityservices/templates/vpnclient.sh:
--------------------------------------------------------------------------------
1 | # -------- CUSTOMIZATION REQUIRED --------
2 | #
3 | # The VPNClient service builds a VPN tunnel to the specified VPN server using
4 | # OpenVPN software and a virtual TUN/TAP device.
5 |
6 | # directory containing the certificate and key described below
7 | keydir=${config["keydir"]}
8 |
9 | # the name used for a "$keyname.crt" certificate and "$keyname.key" private key.
10 | keyname=${config["keyname"]}
11 |
12 | # the public IP address of the VPN server this client should connect with
13 | vpnserver=${config["server"]}
14 |
15 | # optional next hop for adding a static route to reach the VPN server
16 | #nexthop="10.0.1.1"
17 |
18 | # --------- END CUSTOMIZATION --------
19 |
20 | # validate addresses
21 | if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then
22 | echo "WARNING: ip validation disabled because package sipcalc not installed
23 | " > $PWD/vpnclient.log
24 | else
25 | if [ "$(sipcalc "$vpnserver" "$nexthop" | grep ERR)" != "" ]; then
26 | echo "ERROR: invalide address $vpnserver or $nexthop " > $PWD/vpnclient.log
27 | fi
28 | fi
29 |
30 | # validate key and certification files
31 | if [ ! -e $keydir\/$keyname.key ] || [ ! -e $keydir\/$keyname.crt ] \
32 | || [ ! -e $keydir\/ca.crt ] || [ ! -e $keydir\/dh1024.pem ]; then
33 | echo "ERROR: missing certification or key files under $keydir $keyname.key or $keyname.crt or ca.crt or dh1024.pem" >> $PWD/vpnclient.log
34 | fi
35 |
36 | # if necessary, add a static route for reaching the VPN server IP via the IF
37 | vpnservernet=$${}{vpnserver%.*}.0/24
38 | if [ "$nexthop" != "" ]; then
39 | ip route add $vpnservernet via $nexthop
40 | fi
41 |
42 | # create openvpn client.conf
43 | (
44 | cat << EOF
45 | client
46 | dev tun
47 | proto udp
48 | remote $vpnserver 1194
49 | nobind
50 | ca $keydir/ca.crt
51 | cert $keydir/$keyname.crt
52 | key $keydir/$keyname.key
53 | dh $keydir/dh1024.pem
54 | cipher AES-256-CBC
55 | log $PWD/openvpn-client.log
56 | verb 4
57 | daemon
58 | EOF
59 | ) > client.conf
60 |
61 | openvpn --config client.conf
62 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/services/defaults/utilservices/__init__.py
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/defaultmroute.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # auto-generated by DefaultMulticastRoute service (utility.py)
3 | # the first interface is chosen below; please change it as needed
4 | ip route add 224.0.0.0/4 dev ${ifname}
5 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/defaultroute.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # auto-generated by DefaultRoute service
3 | % for route in routes:
4 | ip route add default via ${route}
5 | % endfor
6 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/etc/apache2/envvars:
--------------------------------------------------------------------------------
1 | # this file is used by apache2ctl - generated by utility.py:HttpService
2 | # these settings come from a default Ubuntu apache2 installation
3 | export APACHE_RUN_USER=www-data
4 | export APACHE_RUN_GROUP=www-data
5 | export APACHE_PID_FILE=/var/run/apache2.pid
6 | export APACHE_RUN_DIR=/var/run/apache2
7 | export APACHE_LOCK_DIR=/var/lock/apache2
8 | export APACHE_LOG_DIR=/var/log/apache2
9 | export LANG=C
10 | export LANG
11 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/etc/dhcp/dhcpd.conf:
--------------------------------------------------------------------------------
1 | # auto-generated by DHCP service (utility.py)
2 | # NOTE: move these option lines into the desired pool { } block(s) below
3 | #option domain-name "test.com";
4 | #option domain-name-servers 10.0.0.1;
5 | #option routers 10.0.0.1;
6 |
7 | log-facility local6;
8 |
9 | default-lease-time 600;
10 | max-lease-time 7200;
11 |
12 | ddns-update-style none;
13 |
14 | % for subnet, netmask, rangelow, rangehigh, addr in subnets:
15 | subnet ${subnet} netmask ${netmask} {
16 | pool {
17 | range ${rangelow} ${rangehigh};
18 | default-lease-time 600;
19 | option routers ${addr};
20 | }
21 | }
22 | % endfor
23 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/etc/radvd/radvd.conf:
--------------------------------------------------------------------------------
1 | # auto-generated by RADVD service (utility.py)
2 | % for ifname, prefixes in ifaces:
3 | interface ${ifname}
4 | {
5 | AdvSendAdvert on;
6 | MinRtrAdvInterval 3;
7 | MaxRtrAdvInterval 10;
8 | AdvDefaultPreference low;
9 | AdvHomeAgentFlag off;
10 | % for prefix in prefixes:
11 | prefix ${prefix}
12 | {
13 | AdvOnLink on;
14 | AdvAutonomous on;
15 | AdvRouterAddr on;
16 | };
17 | % endfor
18 | };
19 | % endfor
20 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/etc/ssh/sshd_config:
--------------------------------------------------------------------------------
1 | # auto-generated by SSH service (utility.py)
2 | Port 22
3 | Protocol 2
4 | HostKey ${sshcfgdir}/ssh_host_rsa_key
5 | UsePrivilegeSeparation yes
6 | PidFile ${sshstatedir}/sshd.pid
7 |
8 | KeyRegenerationInterval 3600
9 | ServerKeyBits 768
10 |
11 | SyslogFacility AUTH
12 | LogLevel INFO
13 |
14 | LoginGraceTime 120
15 | PermitRootLogin yes
16 | StrictModes yes
17 |
18 | RSAAuthentication yes
19 | PubkeyAuthentication yes
20 |
21 | IgnoreRhosts yes
22 | RhostsRSAAuthentication no
23 | HostbasedAuthentication no
24 |
25 | PermitEmptyPasswords no
26 | ChallengeResponseAuthentication no
27 |
28 | X11Forwarding yes
29 | X11DisplayOffset 10
30 | PrintMotd no
31 | PrintLastLog yes
32 | TCPKeepAlive yes
33 |
34 | AcceptEnv LANG LC_*
35 | Subsystem sftp ${sshlibdir}/sftp-server
36 | UsePAM yes
37 | UseDNS no
38 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/ipforward.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # auto-generated by IPForward service (utility.py)
3 | sysctl -w net.ipv4.conf.all.forwarding=1
4 | sysctl -w net.ipv4.conf.default.forwarding=1
5 | sysctl -w net.ipv6.conf.all.forwarding=1
6 | sysctl -w net.ipv6.conf.default.forwarding=1
7 | sysctl -w net.ipv4.conf.all.send_redirects=0
8 | sysctl -w net.ipv4.conf.default.send_redirects=0
9 | sysctl -w net.ipv4.conf.all.rp_filter=0
10 | sysctl -w net.ipv4.conf.default.rp_filter=0
11 | # setup forwarding for node interfaces
12 | % for devname in devnames:
13 | sysctl -w net.ipv4.conf.${devname}.forwarding=1
14 | sysctl -w net.ipv4.conf.${devname}.send_redirects=0
15 | sysctl -w net.ipv4.conf.${devname}.rp_filter=0
16 | sysctl -w net.ipv6.conf.${devname}.forwarding=1
17 | % endfor
18 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/pcap.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # set tcpdump options here (see 'man tcpdump' for help)
3 | # (-s snap length, -C limit pcap file length, -n disable name resolution)
4 | if [ "x$1" = "xstart" ]; then
5 | % for ifname in ifnames:
6 | tcpdump -s 12288 -C 10 -n -w ${node.name}.${ifname}.pcap -i ${ifname} > /dev/null 2>&1 &
7 | % endfor
8 | elif [ "x$1" = "xstop" ]; then
9 | mkdir -p $SESSION_DIR/pcap
10 | mv *.pcap $SESSION_DIR/pcap
11 | fi;
12 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/startatd.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | echo 00001 > /var/spool/cron/atjobs/.SEQ
3 | chown -R daemon /var/spool/cron/*
4 | chmod -R 700 /var/spool/cron/*
5 | atd
6 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/startdhcpclient.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # auto-generated by DHCPClient service (utility.py)
3 | # uncomment this mkdir line and symlink line to enable client-side DNS\n# resolution based on the DHCP server response.
4 | #mkdir -p /var/run/resolvconf/interface
5 | % for ifname in ifnames:
6 | #ln -s /var/run/resolvconf/interface/${ifname}.dhclient /var/run/resolvconf/resolv.conf
7 | dhclient -nw -pf /var/run/dhclient-${ifname}.pid -lf /var/run/dhclient-${ifname}.lease ${ifname}
8 | % endfor
9 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/startsshd.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # auto-generated by SSH service (utility.py)
3 | ssh-keygen -q -t rsa -N "" -f ${sshcfgdir}/ssh_host_rsa_key
4 | chmod 655 ${sshstatedir}
5 | # wait until RSA host key has been generated to launch sshd
6 | $(which sshd) -f ${sshcfgdir}/sshd_config
7 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/staticroute.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # auto-generated by StaticRoute service (utility.py)
3 | # NOTE: this service must be customized to be of any use
4 | # Below are samples that you can uncomment and edit.
5 | % for dest, addr in routes:
6 | #ip route add ${dest} via ${addr}
7 | % endfor
8 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/var/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ${node.name} web server
5 | This is the default web page for this server.
6 | The web server software is running but no content has been added, yet.
7 |
8 | % for iface in ifaces:
9 | - ${iface.name} - ${[str(x) for x in iface.ip4s]}
10 | % endfor
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/daemon/core/services/defaults/utilservices/templates/vsftpd.conf:
--------------------------------------------------------------------------------
1 | # vsftpd.conf auto-generated by FTP service (utility.py)
2 | listen=YES
3 | anonymous_enable=YES
4 | local_enable=YES
5 | dirmessage_enable=YES
6 | use_localtime=YES
7 | xferlog_enable=YES
8 | connect_from_port_20=YES
9 | xferlog_file=/var/log/vsftpd.log
10 | ftpd_banner=Welcome to the CORE FTP service
11 | secure_chroot_dir=/var/run/vsftpd/empty
12 | anon_root=/var/ftp
13 |
--------------------------------------------------------------------------------
/daemon/core/xml/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/core/xml/__init__.py
--------------------------------------------------------------------------------
/daemon/doc/.gitignore:
--------------------------------------------------------------------------------
1 | *.rst
2 | _build
3 | conf.py
4 |
--------------------------------------------------------------------------------
/daemon/proto/Makefile.am:
--------------------------------------------------------------------------------
1 | all:
2 | ../../venv/bin/python -m grpc_tools.protoc -I . --python_out=.. --pyi_out=.. core/api/grpc/*.proto
3 | ../../venv/bin/python -m grpc_tools.protoc -I . --grpc_python_out=.. --pyi_out=.. core/api/grpc/core.proto
4 |
5 | clean:
6 | -rm -f ../core/api/grpc/*_pb2*
7 |
--------------------------------------------------------------------------------
/daemon/proto/core/api/grpc/common.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package common;
4 |
5 | message ConfigOption {
6 | string label = 1;
7 | string name = 2;
8 | string value = 3;
9 | int32 type = 4;
10 | repeated string select = 5;
11 | string group = 6;
12 | string regex = 7;
13 | }
14 |
15 | message MappedConfig {
16 | map config = 1;
17 | }
18 |
--------------------------------------------------------------------------------
/daemon/proto/core/api/grpc/mobility.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package mobility;
4 |
5 | import "core/api/grpc/common.proto";
6 |
7 | message MobilityAction {
8 | enum Enum {
9 | START = 0;
10 | PAUSE = 1;
11 | STOP = 2;
12 | }
13 | }
14 |
15 | message MobilityConfig {
16 | int32 node_id = 1;
17 | map config = 2;
18 | }
19 |
20 | message GetMobilityConfigRequest {
21 | int32 session_id = 1;
22 | int32 node_id = 2;
23 | }
24 |
25 | message GetMobilityConfigResponse {
26 | map config = 1;
27 | }
28 |
29 | message SetMobilityConfigRequest {
30 | int32 session_id = 1;
31 | MobilityConfig mobility_config = 2;
32 | }
33 |
34 | message SetMobilityConfigResponse {
35 | bool result = 1;
36 | }
37 |
38 | message MobilityActionRequest {
39 | int32 session_id = 1;
40 | int32 node_id = 2;
41 | MobilityAction.Enum action = 3;
42 | }
43 |
44 | message MobilityActionResponse {
45 | bool result = 1;
46 | }
47 |
--------------------------------------------------------------------------------
/daemon/proto/core/api/grpc/wlan.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package wlan;
4 |
5 | import "core/api/grpc/common.proto";
6 |
7 | message WlanConfig {
8 | int32 node_id = 1;
9 | map config = 2;
10 | }
11 |
12 | message GetWlanConfigRequest {
13 | int32 session_id = 1;
14 | int32 node_id = 2;
15 | }
16 |
17 | message GetWlanConfigResponse {
18 | map config = 1;
19 | }
20 |
21 | message SetWlanConfigRequest {
22 | int32 session_id = 1;
23 | WlanConfig wlan_config = 2;
24 | }
25 |
26 | message SetWlanConfigResponse {
27 | bool result = 1;
28 | }
29 |
30 | message WlanLinkRequest {
31 | int32 session_id = 1;
32 | int32 wlan = 2;
33 | int32 node1_id = 3;
34 | int32 node2_id = 4;
35 | bool linked = 5;
36 | }
37 |
38 | message WlanLinkResponse {
39 | bool result = 1;
40 | }
41 |
--------------------------------------------------------------------------------
/daemon/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "core"
3 | version = "9.2.1"
4 | description = "CORE Common Open Research Emulator"
5 | authors = ["Boeing Research and Technology"]
6 | license = "BSD-2-Clause"
7 | repository = "https://github.com/coreemu/core"
8 | documentation = "https://coreemu.github.io/core/"
9 | include = [
10 | "core/api/grpc/*",
11 | "core/services/defaults/*/templates",
12 | "core/constants.py",
13 | "core/gui/data/**/*",
14 | ]
15 | exclude = ["core/constants.py.in"]
16 |
17 | [tool.poetry.scripts]
18 | core-daemon = "core.scripts.daemon:main"
19 | core-cli = "core.scripts.cli:main"
20 | core-gui = "core.scripts.gui:main"
21 | core-player = "core.scripts.player:main"
22 | core-route-monitor = "core.scripts.routemonitor:main"
23 | core-service-update = "core.scripts.serviceupdate:main"
24 | core-cleanup = "core.scripts.cleanup:main"
25 |
26 | [tool.poetry.dependencies]
27 | python = "^3.10"
28 | fabric = "3.2.2"
29 | grpcio = "1.69.0"
30 | invoke = "2.2.0"
31 | lxml = "5.2.2"
32 | netaddr = "0.10.1"
33 | protobuf = "5.29.3"
34 | pyproj = "3.6.1"
35 | Mako = "1.2.3"
36 | PyYAML = "6.0.1"
37 | pillow = "11.1.0"
38 |
39 | [tool.poetry.group.dev.dependencies]
40 | pytest = "8.3.4"
41 | grpcio-tools = "1.69.0"
42 | black = "22.12.0"
43 | flake8 = "7.1.1"
44 | isort = {version = "5.13.2", extras = ["pyproject"]}
45 | mock = "4.0.2"
46 | pre-commit = "4.1.0"
47 |
48 | [tool.isort]
49 | skip_glob = "*_pb2*.py,doc,build"
50 | multi_line_output = 3
51 | include_trailing_comma = "True"
52 | force_grid_wrap = 0
53 | use_parentheses = "True"
54 | line_length = 88
55 |
56 | [tool.black]
57 | line_length = 88
58 | exclude = ".+_pb2.*.py|doc/|build/|__pycache__/"
59 |
60 | [build-system]
61 | requires = ["poetry>=0.12"]
62 | build-backend = "poetry.masonry.api"
63 |
64 |
--------------------------------------------------------------------------------
/daemon/setup.cfg:
--------------------------------------------------------------------------------
1 | [flake8]
2 | ignore=E501,W503,E203
3 | max-line-length=88
4 | max-complexity=26
5 | select=B,C,E,F,W,T4
6 | exclude=*_pb2*.py,doc,build
7 |
8 | [tool:pytest]
9 | norecursedirs=distributed emane
10 |
--------------------------------------------------------------------------------
/daemon/tests/mobility.scen:
--------------------------------------------------------------------------------
1 | # nodes: 3, max time: 35.000000, max x: 600.00, max y: 600.00
2 | $node_(2) set X_ 144.0
3 | $node_(2) set Y_ 240.0
4 | $node_(2) set Z_ 0.00
5 | $ns_ at 1.00 "$node_(2) setdest 130.0 280.0 15.0"
6 |
--------------------------------------------------------------------------------
/daemon/tests/myservices/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/daemon/tests/myservices/__init__.py
--------------------------------------------------------------------------------
/daemon/tests/myservices/sample.py:
--------------------------------------------------------------------------------
1 | """
2 | Sample user-defined services for testing.
3 | """
4 |
5 | from core.services.coreservices import CoreService
6 |
7 |
8 | class MyService(CoreService):
9 | name = "MyService"
10 | group = "Utility"
11 | configs = ("myservice.sh",)
12 | startup = ("sh myservice.sh",)
13 | shutdown = ("sh myservice.sh",)
14 |
15 | @classmethod
16 | def generate_config(cls, node, filename):
17 | return "# test file"
18 |
19 |
20 | class MyService2(MyService):
21 | name = "MyService2"
22 | group = "Utility"
23 | configs = ("myservice2.sh",)
24 | startup = ("sh myservice2.sh",)
25 | shutdown = startup
26 | validate = startup
27 |
28 | @classmethod
29 | def generate_config(cls, node, filename):
30 | return "exit 1"
31 |
--------------------------------------------------------------------------------
/daemon/tests/test_distributed.py:
--------------------------------------------------------------------------------
1 | from core.emulator.session import Session
2 | from core.nodes.base import CoreNode
3 | from core.nodes.network import HubNode
4 |
5 |
6 | class TestDistributed:
7 | def test_remote_node(self, session: Session):
8 | # given
9 | server_name = "core2"
10 | host = "127.0.0.1"
11 |
12 | # when
13 | session.distributed.add_server(server_name, host)
14 | node = session.add_node(CoreNode, server=server_name)
15 | session.instantiate()
16 |
17 | # then
18 | assert node.server is not None
19 | assert node.server.name == server_name
20 | assert node.server.host == host
21 |
22 | def test_remote_bridge(self, session: Session):
23 | # given
24 | server_name = "core2"
25 | host = "127.0.0.1"
26 | session.distributed.address = host
27 |
28 | # when
29 | session.distributed.add_server(server_name, host)
30 | node1 = session.add_node(HubNode)
31 | node2 = session.add_node(HubNode, server=server_name)
32 | session.add_link(node1.id, node2.id)
33 | session.instantiate()
34 |
35 | # then
36 | assert node2.server is not None
37 | assert node2.server.name == server_name
38 | assert node2.server.host == host
39 | assert len(session.distributed.tunnels) == 1
40 |
--------------------------------------------------------------------------------
/daemon/tests/test_mobility.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from core.location.mobility import WayPoint
4 |
5 | POSITION = (0.0, 0.0, 0.0)
6 |
7 |
8 | class TestMobility:
9 | @pytest.mark.parametrize(
10 | "wp1, wp2, expected",
11 | [
12 | (WayPoint(10.0, 1, POSITION, 1.0), WayPoint(1.0, 2, POSITION, 1.0), False),
13 | (WayPoint(1.0, 1, POSITION, 1.0), WayPoint(10.0, 2, POSITION, 1.0), True),
14 | (WayPoint(1.0, 1, POSITION, 1.0), WayPoint(1.0, 2, POSITION, 1.0), True),
15 | (WayPoint(1.0, 2, POSITION, 1.0), WayPoint(1.0, 1, POSITION, 1.0), False),
16 | ],
17 | )
18 | def test_waypoint_lessthan(self, wp1, wp2, expected):
19 | assert (wp1 < wp2) == expected
20 |
--------------------------------------------------------------------------------
/daemon/tests/test_utils.py:
--------------------------------------------------------------------------------
1 | import netaddr
2 |
3 | from core import utils
4 |
5 |
6 | class TestUtils:
7 | def test_make_tuple_fromstr(self):
8 | # given
9 | no_args = "()"
10 | one_arg = "('one',)"
11 | two_args = "('one', 'two')"
12 | unicode_args = "('one', 'two', 'three')"
13 |
14 | # when
15 | no_args = utils.make_tuple_fromstr(no_args, str)
16 | one_arg = utils.make_tuple_fromstr(one_arg, str)
17 | two_args = utils.make_tuple_fromstr(two_args, str)
18 | unicode_args = utils.make_tuple_fromstr(unicode_args, str)
19 |
20 | # then
21 | assert no_args == ()
22 | assert len(one_arg) == 1
23 | assert len(two_args) == 2
24 | assert len(unicode_args) == 3
25 |
26 | def test_random_mac(self):
27 | value = utils.random_mac()
28 | assert netaddr.EUI(value) is not None
29 |
--------------------------------------------------------------------------------
/dockerfiles/Dockerfile.build:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1
2 | FROM ubuntu:20.04
3 | ENV DEBIAN_FRONTEND=noninteractive
4 | WORKDIR /opt
5 | RUN apt-get update -y && \
6 | apt-get install -y --no-install-recommends \
7 | automake \
8 | pkg-config \
9 | libev-dev \
10 | nftables \
11 | iproute2 \
12 | ethtool \
13 | tk \
14 | bash \
15 | gem \
16 | curl \
17 | rpm \
18 | ruby \
19 | ca-certificates \
20 | build-essential \
21 | git && \
22 | apt-get autoremove -y && \
23 | rm -rf /var/lib/apt/lists/*
24 |
25 | # clone and build core
26 | ARG BRANCH=master
27 | ARG PIPX_VERSION=1.7.1
28 | ARG GRPC_VERSION=1.69.0
29 | ARG INVOKE_VERSION=2.2.0
30 | ARG POETRY_VERSION=1.2.1
31 | ENV PATH="/root/.local/bin:${PATH}"
32 | RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \
33 | git clone https://github.com/coreemu/core.git && \
34 | cd core && \
35 | uv self update && \
36 | uv python install 3.10 && \
37 | uv venv venv && \
38 | uv pip install --python ./venv/bin/python pipx==${PIPX_VERSION} grpcio==${GRPC_VERSION} grpcio-tools==${GRPC_VERSION} && \
39 | ./venv/bin/python -m pipx ensurepath && \
40 | ./venv/bin/python -m pipx install invoke==${INVOKE_VERSION} && \
41 | ./venv/bin/python -m pipx install poetry==${POETRY_VERSION} && \
42 | gem install dotenv -v 2.8.1 && \
43 | gem install fpm && \
44 | git checkout ${BRANCH} && \
45 | ./bootstrap.sh && \
46 | PYTHON=./venv/bin/python ./configure --prefix=/usr && \
47 | make -j$(nproc) && \
48 | make fpm
49 |
--------------------------------------------------------------------------------
/dockerfiles/Dockerfile.emane-python:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1
2 | FROM ubuntu:22.04
3 | RUN apt-get update -y && \
4 | apt-get install -y --no-install-recommends \
5 | automake \
6 | ca-certificates \
7 | g++ \
8 | git \
9 | libpcap-dev \
10 | libpcre3-dev \
11 | libprotobuf-dev \
12 | libtool \
13 | libxml2-dev \
14 | make \
15 | pkg-config \
16 | python3 \
17 | python3-pip \
18 | unzip \
19 | uuid-dev \
20 | wget && \
21 | apt-get autoremove -y && \
22 | rm -rf /var/lib/apt/lists/*
23 | WORKDIR /opt
24 | ARG PROTOC_VERSION=3.19.6
25 | RUN wget -q https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip && \
26 | mkdir protoc && \
27 | unzip protoc-${PROTOC_VERSION}-linux-x86_64.zip -d protoc && \
28 | git clone https://github.com/adjacentlink/emane.git && \
29 | cd emane && \
30 | git checkout v1.5.2 && \
31 | ./autogen.sh && \
32 | PYTHON=python3 ./configure --prefix=/usr && \
33 | cd src/python && \
34 | PATH=/opt/protoc/bin:$PATH make && \
35 | python3 setup.py bdist_wheel && \
36 | mv dist/*.whl /opt/ && \
37 | cd /opt && \
38 | rm -rf protoc && \
39 | rm -rf emane && \
40 | rm -f protoc-${PROTOC_VERSION}-linux-x86_64.zip
41 |
--------------------------------------------------------------------------------
/docs/Makefile.am:
--------------------------------------------------------------------------------
1 | EXTRA_DIST = $(wildcard *)
2 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/docs/architecture.md:
--------------------------------------------------------------------------------
1 | # CORE Architecture
2 |
3 | ## Main Components
4 |
5 | * core-daemon
6 | * Manages emulated sessions of nodes and links for a given network
7 | * Nodes are created using Linux namespaces
8 | * Links are created using Linux bridges and virtual ethernet peers
9 | * Packets sent over links are manipulated using traffic control
10 | * Provides gRPC API
11 | * core-gui
12 | * GUI and daemon communicate over gRPC API
13 | * Drag and drop creation for nodes and links
14 | * Can launch terminals for emulated nodes in running sessions
15 | * Can save/open scenario files to recreate previous sessions
16 | * vnoded
17 | * Command line utility for creating CORE node namespaces
18 | * vcmd
19 | * Command line utility for sending shell commands to nodes
20 |
21 | 
22 |
23 | ## Sessions
24 |
25 | CORE can create and run multiple emulated sessions at once, below is an
26 | overview of the states a session will transition between during typical
27 | GUI interactions.
28 |
29 | 
30 |
31 | ## How Does it Work?
32 |
33 | The CORE framework runs on Linux and uses Linux namespacing for creating
34 | node containers. These nodes are linked together using Linux bridging and
35 | virtual interfaces. CORE sessions are a set of nodes and links operating
36 | together for a specific purpose.
37 |
38 | ### Linux
39 |
40 | Linux network namespaces (also known as netns) is the primary
41 | technique used by CORE. Most recent Linux distributions have
42 | namespaces-enabled kernels out of the box. Each namespace has its own process
43 | environment and private network stack. Network namespaces share the same
44 | filesystem in CORE.
45 |
46 | CORE combines these namespaces with Linux Ethernet bridging to form networks.
47 | Link characteristics are applied using Linux Netem queuing disciplines.
48 | Nftables provides Ethernet frame filtering on Linux bridges. Wireless networks are
49 | emulated by controlling which interfaces can send and receive with nftables
50 | rules.
51 |
52 | ## Open Source Project and Resources
53 |
54 | CORE has been released by Boeing to the open source community under the BSD
55 | license. If you find CORE useful for your work, please contribute back to the
56 | project. Contributions can be as simple as reporting a bug, dropping a line of
57 | encouragement, or can also include submitting patches or maintaining aspects
58 | of the tool.
59 |
--------------------------------------------------------------------------------
/docs/diagrams/architecture.plantuml:
--------------------------------------------------------------------------------
1 | @startuml
2 | skinparam {
3 | RoundCorner 8
4 | ComponentStyle uml2
5 | ComponentBorderColor #Black
6 | InterfaceBorderColor #Black
7 | InterfaceBackgroundColor #Yellow
8 | }
9 |
10 | package User {
11 | component "core-gui" as gui #DeepSkyBlue
12 | component "python scripts" as scripts #DeepSkyBlue
13 | component vcmd #DeepSkyBlue
14 | }
15 | package Server {
16 | component "core-daemon" as daemon #DarkSeaGreen
17 | }
18 | package Python {
19 | component core #LightSteelBlue
20 | }
21 | package "Linux System" {
22 | component nodes #SpringGreen [
23 | nodes
24 | (linux namespaces)
25 | ]
26 | component links #SpringGreen [
27 | links
28 | (bridging and traffic manipulation)
29 | ]
30 | }
31 |
32 | package API {
33 | interface gRPC as grpc
34 | }
35 |
36 | gui <..> grpc
37 | scripts <..> grpc
38 | grpc -- daemon
39 | scripts -- core
40 | daemon - core
41 | core <..> nodes
42 | core <..> links
43 | vcmd <..> nodes
44 | @enduml
45 |
--------------------------------------------------------------------------------
/docs/diagrams/workflow.plantuml:
--------------------------------------------------------------------------------
1 | @startuml
2 | skinparam {
3 | RoundCorner 8
4 | StateBorderColor #Black
5 | StateBackgroundColor #LightSteelBlue
6 | }
7 |
8 | Definition: Session XML
9 | Definition: GUI Drawing
10 | Definition: Scripts
11 |
12 | Configuration: Configure Hooks
13 | Configuration: Configure Services
14 | Configuration: Configure WLAN / Mobility
15 | Configuration: Configure EMANE
16 |
17 | Instantiation: Create Nodes
18 | Instantiation: Create Interfaces
19 | Instantiation: Create Bridges
20 | Instantiation: Start Services
21 |
22 | Runtime: Interactive Shells
23 | Runtime: Traffic Scripts
24 | Runtime: Mobility
25 | Runtime: Widgets
26 |
27 | Datacollect: Collect Files
28 | Datacollect: Other Results
29 |
30 | Shutdown: Shutdown Services
31 | Shutdown: Destroy Brdges
32 | Shutdown: Destroy Interfaces
33 | Shutdown: Destroy Nodes
34 |
35 | Definition -> Configuration
36 | Configuration -> Instantiation
37 | Instantiation -> Runtime
38 | Runtime -> Datacollect
39 | Datacollect -> Shutdown
40 | @enduml
41 |
--------------------------------------------------------------------------------
/docs/docker.md:
--------------------------------------------------------------------------------
1 | # Docker Node Support
2 |
3 | ## Overview
4 |
5 | Provided below is some information for helping setup and use Docker
6 | nodes within a CORE scenario.
7 |
8 | ## Installation
9 |
10 | ### Debian Systems
11 |
12 | ```shell
13 | sudo apt install docker.io
14 | ```
15 |
16 | ### RHEL Systems
17 |
18 | ## Configuration
19 |
20 | Custom configuration required to avoid iptable rules being added and removing
21 | the need for the default docker network, since core will be orchestrating
22 | connections between nodes.
23 |
24 | Place the file below in **/etc/docker/daemon.json**
25 |
26 | ```json
27 | {
28 | "bridge": "none",
29 | "iptables": false
30 | }
31 | ```
32 |
33 | ## Group Setup
34 |
35 | To use Docker nodes within the python GUI, you will need to make sure the
36 | user running the GUI is a member of the docker group.
37 |
38 | ```shell
39 | # add group if does not exist
40 | sudo groupadd docker
41 |
42 | # add user to group
43 | sudo usermod -aG docker $USER
44 |
45 | # to get this change to take effect, log out and back in or run the following
46 | newgrp docker
47 | ```
48 |
49 | ## Image Requirements
50 |
51 | Images used by Docker nodes in CORE need to have networking tools installed for
52 | CORE to automate setup and configuration of the network within the container.
53 |
54 | Example Dockerfile:
55 |
56 | ```
57 | FROM ubuntu:latest
58 | RUN apt-get update
59 | RUN apt-get install -y iproute2 ethtool
60 | ```
61 |
62 | Build image:
63 |
64 | ```shell
65 | sudo docker build -t .
66 | ```
67 |
68 | ## Tools and Versions Tested With
69 |
70 | * Docker version 18.09.5, build e8ff056
71 | * nsenter from util-linux 2.31.1
72 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # CORE Documentation
2 |
3 | ## Introduction
4 |
5 | CORE (Common Open Research Emulator) is a tool for building virtual networks. As an emulator, CORE builds a
6 | representation of a real computer network that runs in real time, as opposed to simulation, where abstract models are
7 | used. The live-running emulation can be connected to physical networks and routers. It provides an environment for
8 | running real applications and protocols, taking advantage of tools provided by the Linux operating system.
9 |
10 | CORE is typically used for network and protocol research, demonstrations, application and platform testing, evaluating
11 | networking scenarios, security studies, and increasing the size of physical test networks.
12 |
13 | ### Key Features
14 |
15 | * Efficient and scalable
16 | * Runs applications and protocols without modification
17 | * Drag and drop GUI
18 | * Highly customizable
19 |
--------------------------------------------------------------------------------
/docs/install_docker.md:
--------------------------------------------------------------------------------
1 | # Install Docker
2 |
3 | ## Overview
4 |
5 | CORE can be installed into and ran from a Docker container. This section will cover how you can build and run
6 | CORE from a Docker based image.
7 |
8 | ## Build Image
9 |
10 | You can leverage one of the provided Dockerfiles to build a CORE based image. Since CORE nodes will leverage software
11 | available within the system for a given use case, make sure to update and build the Dockerfile with desired software.
12 |
13 | The example Dockerfiles are not meant to be an end all solution, but a solid starting point for running CORE.
14 |
15 | Provided Dockerfiles:
16 |
17 | * Dockerfile.emane-python - Build EMANE python bindings for use in files below
18 | * Dockerfile.rocky - Rocky Linux 8, CORE from latest package, OSPF MDR, and EMANE
19 | * Dockerfile.ubuntu - Ubuntu 22.04, CORE from latest package, OSPF MDR, and EMANE
20 |
21 | ```shell
22 | # clone core
23 | git clone https://github.com/coreemu/core.git
24 | cd core
25 | # first you must build EMANE python bindings
26 | sudo docker build -t emane-python -f dockerfiles/Dockerfile.emane-python .
27 | # build desired CORE image, OSPF is a build stage dependency within the file
28 | sudo docker build -t core -f dockerfiles/ .
29 | ```
30 |
31 | ## Run Container
32 |
33 | There are some required parameters when starting a CORE based Docker container for CORE to function properly. These
34 | are shown below in the run command.
35 |
36 | ```shell
37 | # start container into the background and run the core-daemon by default
38 | sudo docker run -itd --name core -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix:rw \
39 | --privileged --init --entrypoint /opt/core/venv/bin/core-daemon core
40 | # enable xhost access to the root user, this will allow you to run the core-gui from the container
41 | xhost +local:root
42 | # launch core-gui from the running container launched previously
43 | sudo docker exec -it core core-gui
44 | ```
45 |
--------------------------------------------------------------------------------
/docs/nodetypes.md:
--------------------------------------------------------------------------------
1 | # Node Types
2 |
3 | ## Overview
4 |
5 | Different node types can be used within CORE, each with their own
6 | tradeoffs and functionality.
7 |
8 | ## CORE Nodes
9 |
10 | CORE nodes are the standard node type typically used in CORE. They are
11 | backed by Linux network namespaces. They use very little system resources
12 | in order to emulate a network. They do however share the hosts file system
13 | as they do not get their own. CORE nodes will have a directory uniquely
14 | created for them as a place to keep their files and mounted directories
15 | (`/tmp/pycore./
21 |
22 |
23 |
--------------------------------------------------------------------------------
/docs/tutorials/overview.md:
--------------------------------------------------------------------------------
1 | # CORE Tutorials
2 |
3 | These tutorials will cover various use cases within CORE. These
4 | tutorials will provide example python, gRPC, XML, and related files, as well
5 | as an explanation for their usage and purpose.
6 |
7 | ## Checklist
8 |
9 | These are the items you should become familiar with for running all the tutorials below.
10 |
11 | * [Install CORE](../install.md)
12 | * [Tutorial Setup](setup.md)
13 |
14 | ## Tutorials
15 |
16 | * [Tutorial 1 - Wired Network](tutorial1.md)
17 | * Covers interactions when using a simple 2 node wired network
18 | * [Tutorial 2 - Wireless Network](tutorial2.md)
19 | * Covers interactions when using a simple 3 node wireless network
20 | * [Tutorial 3 - Basic Mobility](tutorial3.md)
21 | * Covers mobility interactions when using a simple 3 node wireless network
22 | * [Tutorial 4 - Tests](tutorial4.md)
23 | * Covers automating scenarios as tests to validate software
24 | * [Tutorial 5 - RJ45 Node](tutorial5.md)
25 | * Covers using the RJ45 node to connect a Windows OS
26 | * [Tutorial 6 - Improve Visuals](tutorial6.md)
27 | * Covers changing the look of a scenario within the CORE GUI
28 | * [Tutorial 7 - EMANE](tutorial7.md)
29 | * Covers using EMANE within CORE for higher fidelity RF networks
30 |
--------------------------------------------------------------------------------
/man/Makefile.am:
--------------------------------------------------------------------------------
1 | # CORE
2 |
3 | if WANT_DAEMON
4 | DAEMON_MANS = vnoded.1 vcmd.1 netns.1 core-daemon.1 core-gui.1 \
5 | core-cleanup.1
6 | endif
7 | man_MANS = $(DAEMON_MANS)
8 |
9 | .PHONY: generate-mans
10 | generate-mans:
11 | $(HELP2MAN) --source CORE 'sh $(top_srcdir)/gui/core-gui' -o core-gui.1
12 | $(HELP2MAN) --no-info --source CORE $(top_srcdir)/netns/vnoded -o vnoded.1
13 | $(HELP2MAN) --no-info --source CORE $(top_srcdir)/netns/vcmd -o vcmd.1
14 | $(HELP2MAN) --no-info --source CORE $(top_srcdir)/netns/netns -o netns.1
15 | $(HELP2MAN) --version-string=$(PACKAGE_VERSION) --no-info --source CORE $(top_srcdir)/daemon/scripts/core-daemon -o core-daemon.1
16 | $(HELP2MAN) --version-string=$(PACKAGE_VERSION) --no-info --source CORE $(top_srcdir)/daemon/scripts/core-cleanup -o core-cleanup.1
17 |
18 | .PHONY: diff
19 | diff:
20 | for m in ${man_MANS}; do \
21 | colordiff -u $$m $$m.new | less -R; \
22 | done;
23 |
24 | clean-local:
25 | -rm -f $(addsuffix .new,$(GUI_MANS))
26 | -rm -f $(addsuffix .new,$(DAEMON_MANS))
27 |
28 | DISTCLEANFILES = Makefile.in
29 | EXTRA_DIST = $(man_MANS)
30 |
--------------------------------------------------------------------------------
/man/core-cleanup.1:
--------------------------------------------------------------------------------
1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
2 | .TH CORE-CLEANUP "1" "June 2020" "CORE" "User Commands"
3 | .SH NAME
4 | core-cleanup \- manual page for core-cleanup 6.4.0
5 | .SH DESCRIPTION
6 | usage: ../daemon/scripts/core\-cleanup [\-d [\-l]]
7 | .IP
8 | Clean up all CORE namespaces processes, bridges, interfaces, and session
9 | directories. Options:
10 | .TP
11 | \fB\-h\fR
12 | show this help message and exit
13 | .TP
14 | \fB\-d\fR
15 | also kill the Python daemon
16 | .TP
17 | \fB\-l\fR
18 | remove the core\-daemon.log file
19 |
--------------------------------------------------------------------------------
/man/core-daemon.1:
--------------------------------------------------------------------------------
1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
2 | .TH CORE-DAEMON "1" "June 2020" "CORE" "User Commands"
3 | .SH NAME
4 | core-daemon \- manual page for core-daemon 6.4.0
5 | .SH DESCRIPTION
6 | usage: core\-daemon [\-h] [\-f CONFIGFILE] [\-p PORT] [\-\-ovs]
7 | .IP
8 | [\-\-grpc\-port GRPCPORT] [\-\-grpc\-address GRPCADDRESS]
9 | [\-l LOGFILE]
10 | .PP
11 | CORE daemon v.6.4.0 instantiates Linux network namespace nodes.
12 | .SS "optional arguments:"
13 | .TP
14 | \fB\-h\fR, \fB\-\-help\fR
15 | show this help message and exit
16 | .TP
17 | \fB\-f\fR CONFIGFILE, \fB\-\-configfile\fR CONFIGFILE
18 | read config from specified file; default =
19 | \fI\,/etc/core/core.conf\/\fP
20 | .TP
21 | \fB\-p\fR PORT, \fB\-\-port\fR PORT
22 | port number to listen on; default = 4038
23 | .TP
24 | \fB\-\-ovs\fR
25 | enable experimental ovs mode, default is false
26 | .TP
27 | \fB\-\-grpc\-port\fR GRPCPORT
28 | grpc port to listen on; default 50051
29 | .TP
30 | \fB\-\-grpc\-address\fR GRPCADDRESS
31 | grpc address to listen on; default localhost
32 | .TP
33 | \fB\-l\fR LOGFILE, \fB\-\-logfile\fR LOGFILE
34 | core logging configuration; default
35 | \fI\,/etc/core/logging.conf\/\fP
36 |
--------------------------------------------------------------------------------
/man/core-gui.1:
--------------------------------------------------------------------------------
1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
2 | .TH CORE-GUI "1" "June 2020" "CORE" "User Commands"
3 | .SH NAME
4 | core-gui \- manual page for core-gui version 6.4.0 (20200513)
5 | .SH SYNOPSIS
6 | .B core-gui
7 | [\fI\,-h|-v\/\fR] [\fI\,-b|-c \/\fR] [\fI\,-s\/\fR] [\fI\,-a address\/\fR] [\fI\,-p port\/\fR]
8 | .SH DESCRIPTION
9 | .IP
10 | []
11 | .PP
12 | Launches the CORE Tcl/Tk X11 GUI or starts an imn\-based emulation.
13 | .TP
14 | \-(\fB\-h\fR)elp
15 | show help message and exit
16 | .TP
17 | \-(\fB\-v\fR)ersion
18 | show version number and exit
19 | .TP
20 | \-(\fB\-b\fR)atch
21 | batch mode (no X11 GUI)
22 | .TP
23 | \-(\fB\-c\fR)losebatch
24 | stop and clean up a batch mode session
25 | .TP
26 | \-(\fB\-s\fR)tart
27 | start in execute mode, not edit mode
28 | .TP
29 | \-(\fB\-a\fR)ddress
30 | connect to the specified IP address (default 127.0.0.1)
31 | .TP
32 | \-(\fB\-p\fR)port
33 | connect to the specified TCP port (default 4038)
34 | .TP
35 |
36 | (optional) load the specified imn scenario file
37 | .PP
38 | With no parameters, starts the GUI in edit mode with a blank canvas.
39 | .SH "SEE ALSO"
40 | The full documentation for
41 | .B core-gui
42 | is maintained as a Texinfo manual. If the
43 | .B info
44 | and
45 | .B core-gui
46 | programs are properly installed at your site, the command
47 | .IP
48 | .B info core-gui
49 | .PP
50 | should give you access to the complete manual.
51 |
--------------------------------------------------------------------------------
/man/netns.1:
--------------------------------------------------------------------------------
1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
2 | .TH NETNS "1" "June 2020" "CORE" "User Commands"
3 | .SH NAME
4 | netns \- manual page for netns version 6.4.0
5 | .SH SYNOPSIS
6 | .B netns
7 | [\fI\,-h|-V\/\fR] [\fI\,-w\/\fR] \fI\,-- command \/\fR[\fI\,args\/\fR...]
8 | .SH DESCRIPTION
9 | Run the specified command in a new network namespace.
10 | .SH OPTIONS
11 | .TP
12 | \fB\-h\fR, \fB\-\-help\fR
13 | show this help message and exit
14 | .TP
15 | \fB\-V\fR, \fB\-\-version\fR
16 | show version number and exit
17 | .TP
18 | \fB\-w\fR
19 | wait for command to complete (useful for interactive commands)
20 |
--------------------------------------------------------------------------------
/man/vcmd.1:
--------------------------------------------------------------------------------
1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
2 | .TH VCMD "1" "June 2020" "CORE" "User Commands"
3 | .SH NAME
4 | vcmd \- manual page for vcmd version 6.4.0
5 | .SH SYNOPSIS
6 | .B vcmd
7 | [\fI\,-h|-V\/\fR] [\fI\,-v\/\fR] [\fI\,-q|-i|-I\/\fR] \fI\,-c -- command args\/\fR...
8 | .SH DESCRIPTION
9 | Run the specified command in the Linux namespace container specified by the
10 | control , with the specified arguments.
11 | .SH OPTIONS
12 | .TP
13 | \fB\-h\fR, \fB\-\-help\fR
14 | show this help message and exit
15 | .TP
16 | \fB\-V\fR, \fB\-\-version\fR
17 | show version number and exit
18 | .TP
19 | \fB\-v\fR
20 | enable verbose logging
21 | .TP
22 | \fB\-q\fR
23 | run the command quietly, without local input or output
24 | .TP
25 | \fB\-i\fR
26 | run the command interactively (use PTY)
27 | .TP
28 | \fB\-I\fR
29 | run the command non\-interactively (without PTY)
30 | .TP
31 | \fB\-c\fR
32 | control channel name (e.g. '/tmp/pycore.45647/n3')
33 |
--------------------------------------------------------------------------------
/man/vnoded.1:
--------------------------------------------------------------------------------
1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
2 | .TH VNODED "1" "June 2020" "CORE" "User Commands"
3 | .SH NAME
4 | vnoded \- manual page for vnoded version 6.4.0
5 | .SH SYNOPSIS
6 | .B vnoded
7 | [\fI\,-h|-V\/\fR] [\fI\,-v\/\fR] [\fI\,-n\/\fR] [\fI\,-C \/\fR] [\fI\,-l \/\fR] [\fI\,-p \/\fR] \fI\,-c \/\fR
8 | .SH DESCRIPTION
9 | Linux namespace container server daemon runs as PID 1 in the container.
10 | Normally this process is launched automatically by the CORE daemon.
11 | .SH OPTIONS
12 | .TP
13 | \fB\-h\fR, \fB\-\-help\fR
14 | show this help message and exit
15 | .TP
16 | \fB\-V\fR, \fB\-\-version\fR
17 | show version number and exit
18 | .TP
19 | \fB\-v\fR
20 | enable verbose logging
21 | .TP
22 | \fB\-n\fR
23 | do not create and run daemon within a new network namespace (for debug)
24 | .TP
25 | \fB\-C\fR
26 | change to the specified directory
27 | .TP
28 | \fB\-l\fR
29 | log output to the specified file
30 | .TP
31 | \fB\-p\fR
32 | write process id to the specified file
33 | .TP
34 | \fB\-c\fR
35 | establish the specified for receiving control commands
36 |
--------------------------------------------------------------------------------
/netns/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | Makefile
3 | Makefile.in
4 | build
5 | netns
6 | vcmd
7 | version.h
8 | vnoded
9 |
--------------------------------------------------------------------------------
/netns/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.h
2 |
--------------------------------------------------------------------------------
/netns/Makefile.am:
--------------------------------------------------------------------------------
1 | # CORE
2 | #
3 | # Makefile for building netns.
4 | #
5 |
6 | AM_CFLAGS = -Wall -fno-strict-aliasing -O3 -g @libev_CFLAGS@
7 |
8 | SRC_COMMON = vnode_msg.c vnode_cmd.c vnode_chnl.c vnode_io.c \
9 | vnode_msg.h vnode_cmd.h vnode_chnl.h vnode_io.h \
10 | vnode_tlv.h myerr.h netns.h
11 | SRC_VNODED = vnoded_main.c vnode_server.c netns.c \
12 | vnode_server.h
13 | SRC_VCMD = vcmd_main.c vnode_client.c \
14 | vnode_client.h
15 | SRC_NETNS = netns_main.c netns.c netns.h
16 |
17 | bin_PROGRAMS = vnoded vcmd netns
18 | vnoded_LDADD = @libev_LIBS@
19 | vnoded_SOURCES = $(SRC_COMMON) $(SRC_VNODED)
20 | vcmd_LDADD = @libev_LIBS@
21 | vcmd_SOURCES = $(SRC_COMMON) $(SRC_VCMD)
22 | netns_SOURCES = $(SRC_NETNS)
23 |
24 | # extra cruft to remove
25 | DISTCLEANFILES = Makefile.in MANIFEST
26 |
--------------------------------------------------------------------------------
/netns/myerr.h:
--------------------------------------------------------------------------------
1 | /*
2 | * CORE
3 | * Copyright (c)2010-2012 the Boeing Company.
4 | * See the LICENSE file included in this distribution.
5 | *
6 | * author: Tom Goff
7 | *
8 | * myerr.h
9 | *
10 | * Custom error printing macros.
11 | */
12 |
13 | #ifndef _MYERR_H_
14 | #define _MYERR_H_
15 |
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 |
23 | #include
24 |
25 | static void __myerrprintf(const char *func, const char *file, const int line,
26 | FILE *stream, const char *fmt, ...)
27 | {
28 | extern const char *__progname;
29 | va_list ap;
30 | pid_t pid;
31 | struct timeval tv;
32 |
33 | va_start(ap, fmt);
34 |
35 | pid = getpid();
36 | if (gettimeofday(&tv, NULL))
37 | {
38 | fprintf(stream, "%s[%u]: %s[%s:%d]: ", __progname, pid, func, file, line);
39 | }
40 | else
41 | {
42 | char timestr[9];
43 | strftime(timestr, sizeof(timestr), "%H:%M:%S", localtime(&tv.tv_sec));
44 | fprintf(stream, "%s[%u]: %s.%06ld %s[%s:%d]: ",
45 | __progname, pid, timestr, tv.tv_usec, func, file, line);
46 | }
47 |
48 | vfprintf(stream, fmt, ap);
49 | fputs("\n", stream);
50 |
51 | va_end(ap);
52 |
53 | return;
54 | }
55 |
56 | #define INFO(fmt, args...) \
57 | __myerrprintf(__func__, __FILE__, __LINE__, \
58 | stdout, fmt, ##args)
59 |
60 | #define WARNX(fmt, args...) \
61 | __myerrprintf(__func__, __FILE__, __LINE__, \
62 | stderr, fmt, ##args)
63 |
64 | #define WARN(fmt, args...) \
65 | __myerrprintf(__func__, __FILE__, __LINE__, \
66 | stderr, fmt ": %s", ##args, strerror(errno))
67 |
68 | #define ERRX(eval, fmt, args...) \
69 | do { \
70 | WARNX(fmt, ##args); \
71 | exit(eval); \
72 | } while (0)
73 |
74 | #define ERR(eval, fmt, args...) \
75 | do { \
76 | WARN(fmt, ##args); \
77 | exit(eval); \
78 | } while (0)
79 |
80 | #endif /* _MYERR_H_ */
81 |
--------------------------------------------------------------------------------
/netns/netns.h:
--------------------------------------------------------------------------------
1 | /*
2 | * CORE
3 | * Copyright (c)2010-2012 the Boeing Company.
4 | * See the LICENSE file included in this distribution.
5 | *
6 | * author: Tom Goff
7 | *
8 | * netns.h
9 | *
10 | */
11 |
12 | #ifndef _FORKNS_H_
13 | #define _FORKNS_H_
14 |
15 | #include
16 |
17 | pid_t nsfork(int flags);
18 | pid_t nsexecvp(char *argv[]);
19 |
20 | #endif /* _FORKNS_H_ */
21 |
--------------------------------------------------------------------------------
/netns/setup.py.in:
--------------------------------------------------------------------------------
1 | """
2 | Defines how CORE netns will be build for installation.
3 | """
4 |
5 | from setuptools import setup, Extension
6 |
7 |
8 | netns = Extension(
9 | "netns",
10 | sources=[
11 | "netnsmodule.c",
12 | "netns.c"
13 | ]
14 | )
15 |
16 | vcmd = Extension(
17 | "vcmd",
18 | sources=[
19 | "vcmdmodule.c",
20 | "vnode_client.c",
21 | "vnode_chnl.c",
22 | "vnode_io.c",
23 | "vnode_msg.c",
24 | "vnode_cmd.c",
25 | ],
26 | library_dirs=["build/lib"],
27 | libraries=["ev"]
28 | )
29 |
30 | setup(
31 | name="core-netns",
32 | version="@PACKAGE_VERSION@",
33 | description="Extension modules to support virtual nodes using Linux network namespaces",
34 | scripts=["vcmd", "vnoded", "netns"],
35 | ext_modules=[
36 | netns,
37 | vcmd
38 | ],
39 | url="https://github.com/coreemu/core",
40 | author="Boeing Research & Technology",
41 | license="BSD",
42 | long_description="Extension modules and utilities to support virtual nodes using Linux network namespaces",
43 | )
44 |
--------------------------------------------------------------------------------
/netns/version.h.in:
--------------------------------------------------------------------------------
1 | /*
2 | * CORE
3 | *
4 | * version.h
5 | *
6 | */
7 | #ifndef _VERSION_H_
8 | #define _VERSION_H_
9 |
10 | #define CORE_VERSION "@PACKAGE_VERSION@"
11 |
12 | #endif /* _VERSION_H_ */
13 |
--------------------------------------------------------------------------------
/netns/vnode_chnl.h:
--------------------------------------------------------------------------------
1 | /*
2 | * CORE
3 | * Copyright (c)2010-2012 the Boeing Company.
4 | * See the LICENSE file included in this distribution.
5 | *
6 | * author: Tom Goff
7 | *
8 | * vnode_chnl.h
9 | *
10 | */
11 |
12 | #ifndef _VNODE_CHNL_H_
13 | #define _VNODE_CHNL_H_
14 |
15 | int vnode_connect(const char *name);
16 | int vnode_listen(const char *name);
17 |
18 | #endif /* _VNODE_CHNL_H_ */
19 |
--------------------------------------------------------------------------------
/netns/vnode_client.h:
--------------------------------------------------------------------------------
1 | /*
2 | * CORE
3 | * Copyright (c)2010-2012 the Boeing Company.
4 | * See the LICENSE file included in this distribution.
5 | *
6 | * author: Tom Goff
7 | *
8 | * vnode_client.h
9 | *
10 | */
11 |
12 | #ifndef _VNODE_CLIENT_H_
13 | #define _VNODE_CLIENT_H_
14 |
15 | #include
16 | #include
17 |
18 | #include "vnode_msg.h"
19 | #include "vnode_cmd.h"
20 | #include "vnode_io.h"
21 |
22 | struct vnode_client;
23 | typedef void (*vnode_clientcb_t)(struct vnode_client *client);
24 |
25 | typedef struct vnode_client {
26 | TAILQ_HEAD(cmdlist, cmdentry) cmdlisthead;
27 |
28 | struct ev_loop *loop;
29 | int serverfd;
30 | struct vnode_msgio msgio;
31 | void *data;
32 |
33 | vnode_clientcb_t ioerrorcb;
34 |
35 | int32_t cmdid;
36 | } vnode_client_t;
37 |
38 | typedef void (*vnode_client_cmddonecb_t)(int32_t cmdid, pid_t pid,
39 | int status, void *data);
40 |
41 | typedef enum {
42 | VCMD_IO_NONE = 0,
43 | VCMD_IO_FD,
44 | VCMD_IO_PIPE,
45 | VCMD_IO_PTY,
46 | } vnode_client_cmdiotype_t;
47 |
48 | typedef struct {
49 | vnode_client_cmdiotype_t iotype;
50 | union {
51 | stdio_fd_t stdiofd;
52 | stdio_pipe_t stdiopipe;
53 | stdio_pty_t stdiopty;
54 | };
55 | } vnode_client_cmdio_t;
56 |
57 | #define SET_STDIOFD(clcmdio, ifd, ofd, efd) \
58 | do { \
59 | (clcmdio)->iotype = VCMD_IO_FD; \
60 | (clcmdio)->stdiofd.infd = (ifd); \
61 | (clcmdio)->stdiofd.outfd = (ofd); \
62 | (clcmdio)->stdiofd.errfd = (efd); \
63 | } while (0)
64 |
65 | vnode_client_t *vnode_client(struct ev_loop *loop, const char *ctrlchnlname,
66 | vnode_clientcb_t ioerrorcb, void *data);
67 | void vnode_delclient(vnode_client_t *client);
68 |
69 | vnode_client_cmdio_t *vnode_open_clientcmdio(vnode_client_cmdiotype_t iotype);
70 | void vnode_close_clientcmdio(vnode_client_cmdio_t *clientcmdio);
71 |
72 | int vnode_client_cmdreq(vnode_client_t *client,
73 | vnode_client_cmdio_t *clientcmdio,
74 | vnode_client_cmddonecb_t cmddonecb, void *data,
75 | int argc, char *argv[]);
76 |
77 | #endif /* _VNODE_CLIENT_H_ */
78 |
--------------------------------------------------------------------------------
/netns/vnode_cmd.h:
--------------------------------------------------------------------------------
1 | /*
2 | * CORE
3 | * Copyright (c)2010-2012 the Boeing Company.
4 | * See the LICENSE file included in this distribution.
5 | *
6 | * author: Tom Goff
7 | *
8 | * vnode_cmd.h
9 | *
10 | */
11 |
12 | #ifndef _VNODE_CMD_H_
13 | #define _VNODE_CMD_H_
14 |
15 | #include
16 | #include
17 |
18 | #include "vnode_msg.h"
19 |
20 | typedef struct {
21 | int infd;
22 | int outfd;
23 | int errfd;
24 | } vnode_cmdio_t;
25 |
26 | typedef struct {
27 | int32_t cmdid;
28 | vnode_cmdio_t cmdio;
29 | char *cmdarg[VNODE_ARGMAX];
30 | } vnode_cmdreq_t;
31 |
32 | #define CMDREQ_INIT {}
33 |
34 | typedef struct {
35 | int32_t cmdid;
36 | int32_t pid;
37 | } vnode_cmdreqack_t;
38 |
39 | #define CMDREQACK_INIT {.cmdid = 0, .pid = -1}
40 |
41 | typedef struct {
42 | int32_t cmdid;
43 | int32_t status;
44 | } vnode_cmdstatus_t;
45 |
46 | #define CMDSTATUS_INIT {.cmdid = 0, .status = -1}
47 |
48 | typedef struct {
49 | int32_t cmdid;
50 | int32_t signum;
51 | } vnode_cmdsignal_t;
52 |
53 | #define CMDSIGNAL_INIT {.cmdid = 0, .signum = 0}
54 |
55 | typedef struct cmdentry {
56 | TAILQ_ENTRY(cmdentry) entries;
57 |
58 | int32_t cmdid;
59 | pid_t pid;
60 | int status;
61 | void *data;
62 | } vnode_cmdentry_t;
63 |
64 | void vnode_recv_cmdreq(vnode_msgio_t *msgio);
65 | int vnode_send_cmdreq(int fd, int32_t cmdid, char *argv[],
66 | int infd, int outfd, int errfd);
67 | int vnode_send_cmdstatus(int fd, int32_t cmdid, int32_t status);
68 | int vnode_send_cmdsignal(int fd, int32_t cmdid, int32_t signum);
69 | void vnode_recv_cmdsignal(vnode_msgio_t *msgio);
70 |
71 | #endif /* _VNODE_CMD_H_ */
72 |
--------------------------------------------------------------------------------
/netns/vnode_io.h:
--------------------------------------------------------------------------------
1 | /*
2 | * CORE
3 | * Copyright (c)2010-2012 the Boeing Company.
4 | * See the LICENSE file included in this distribution.
5 | *
6 | * author: Tom Goff
7 | *
8 | * vnode_io.h
9 | *
10 | */
11 |
12 | #ifndef _VNODE_IO_H_
13 | #define _VNODE_IO_H_
14 |
15 | typedef struct {
16 | int infd;
17 | int outfd;
18 | int errfd;
19 | } stdio_fd_t;
20 |
21 | #define INIT_STDIO_FD(s) \
22 | do { \
23 | (s)->infd = -1; \
24 | (s)->outfd = -1; \
25 | (s)->errfd = -1; \
26 | } while (0)
27 |
28 | typedef struct {
29 | int masterfd;
30 | int slavefd;
31 | } stdio_pty_t;
32 |
33 | #define INIT_STDIO_PTY(s) \
34 | do { \
35 | (s)->masterfd = -1; \
36 | (s)->slavefd = -1; \
37 | } while (0)
38 |
39 | typedef struct {
40 | int infd[2];
41 | int outfd[2];
42 | int errfd[2];
43 | } stdio_pipe_t;
44 |
45 | #define INIT_STDIO_PIPE(s) \
46 | do { \
47 | (s)->infd[0] = (s)->infd[1] = -1; \
48 | (s)->outfd[0] = (s)->outfd[1] = -1; \
49 | (s)->errfd[0] = (s)->errfd[1] = -1; \
50 | } while (0)
51 |
52 | int set_nonblock(int fd);
53 | int clear_nonblock(int fd);
54 |
55 | int set_cloexec(int fd);
56 |
57 | int open_stdio_pty(stdio_pty_t *stdiopty);
58 | void close_stdio_pty(stdio_pty_t *stdiopty);
59 |
60 | int open_stdio_pipe(stdio_pipe_t *stdiopipe);
61 | void close_stdio_pipe(stdio_pipe_t *stdiopipe);
62 |
63 | #endif /* _VNODE_IO_H_ */
64 |
--------------------------------------------------------------------------------
/netns/vnode_server.h:
--------------------------------------------------------------------------------
1 | /*
2 | * CORE
3 | * Copyright (c)2010-2012 the Boeing Company.
4 | * See the LICENSE file included in this distribution.
5 | *
6 | * author: Tom Goff
7 | *
8 | * vnode_server.h
9 | *
10 | */
11 |
12 | #ifndef _VNODE_SERVER_H_
13 | #define _VNODE_SERVER_H_
14 |
15 | #include
16 | #include
17 |
18 | #include
19 |
20 | #include "vnode_msg.h"
21 |
22 | typedef struct {
23 | TAILQ_HEAD(clientlist, cliententry) clientlisthead;
24 | TAILQ_HEAD(cmdlist, cmdentry) cmdlisthead;
25 | struct ev_loop *loop;
26 | char ctrlchnlname[PATH_MAX];
27 | char pidfilename[PATH_MAX];
28 | int serverfd;
29 | ev_io fdwatcher;
30 | ev_child childwatcher;
31 | } vnode_server_t;
32 |
33 | typedef struct cliententry {
34 | TAILQ_ENTRY(cliententry) entries;
35 |
36 | vnode_server_t *server;
37 | int clientfd;
38 | vnode_msgio_t msgio;
39 | } vnode_cliententry_t;
40 |
41 | vnode_server_t *vnoded(int newnetns, const char *ctrlchnlname,
42 | const char *logfilename, const char *pidfilename,
43 | const char *chdirname);
44 | void vnode_delserver(vnode_server_t *server);
45 | #endif /* _VNODE_SERVER_H_ */
46 |
--------------------------------------------------------------------------------
/netns/vnode_tlv.h:
--------------------------------------------------------------------------------
1 | /*
2 | * CORE
3 | * Copyright (c)2010-2012 the Boeing Company.
4 | * See the LICENSE file included in this distribution.
5 | *
6 | * author: Tom Goff
7 | *
8 | * vnode_tlv.h
9 | *
10 | */
11 |
12 | #ifndef _VNODE_TLV_H_
13 | #define _VNODE_TLV_H_
14 |
15 | static inline int tlv_string(char **var, vnode_tlv_t *tlv)
16 | {
17 | if (tlv->val[tlv->vallen - 1] != '\0')
18 | {
19 | WARNX("string not null-terminated");
20 | return -1;
21 | }
22 |
23 | *var = (char *)tlv->val;
24 |
25 | return 0;
26 | }
27 |
28 | static inline int tlv_int32(int32_t *var, vnode_tlv_t *tlv)
29 | {
30 | if (tlv->vallen != sizeof(int32_t))
31 | {
32 | WARNX("invalid value length for int32: %u", tlv->vallen);
33 | return -1;
34 | }
35 |
36 | *var = *(int32_t *)tlv->val;
37 |
38 | return 0;
39 | }
40 |
41 | #endif /* _VNODE_TLV_H_ */
42 |
--------------------------------------------------------------------------------
/package/after-install-deb.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | if [ ! -z "${NO_PYTHON}" ]; then
3 | exit 0
4 | fi
5 |
6 | PYTHON="${PYTHON:=python3}"
7 | if [ ! -z "${NO_VENV}" ]; then
8 | ${PYTHON} -m pip install /opt/core/core-*.whl
9 | sed -i 's|$DAEMON|/usr/local/bin/core-daemon|g' /lib/systemd/system/core-daemon.service
10 | else
11 | ${PYTHON} -m venv /opt/core/venv
12 | . /opt/core/venv/bin/activate
13 | pip install --upgrade pip
14 | pip install /opt/core/core-*.whl
15 | sed -i 's|$DAEMON|/opt/core/venv/bin/core-daemon|g' /lib/systemd/system/core-daemon.service
16 | ln -s /opt/core/venv/bin/core-* /usr/bin/
17 | fi
18 |
--------------------------------------------------------------------------------
/package/after-install-rpm.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | if [ ! -z "${NO_PYTHON}" ]; then
3 | exit 0
4 | fi
5 |
6 | PYTHON="${PYTHON:=python3}"
7 | if [ ! -z "${NO_VENV}" ]; then
8 | ${PYTHON} -m pip install /opt/core/core-*.whl
9 | sed -i 's|$DAEMON|/usr/local/bin/core-daemon|g' /usr/lib/systemd/system/core-daemon.service
10 | else
11 | ${PYTHON} -m venv /opt/core/venv
12 | . /opt/core/venv/bin/activate
13 | pip install --upgrade pip
14 | pip install /opt/core/core-*.whl
15 | sed -i 's|$DAEMON|/opt/core/venv/bin/core-daemon|g' /usr/lib/systemd/system/core-daemon.service
16 | ln -s /opt/core/venv/bin/core-* /usr/bin/
17 | fi
18 | systemctl preset core-daemon
19 |
--------------------------------------------------------------------------------
/package/after-remove-deb.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | if [ ! -z "${NO_PYTHON}" ]; then
3 | exit 0
4 | fi
5 |
6 | PYTHON="${PYTHON:=python3}"
7 | if [ ! -z "${NO_VENV}" ]; then
8 | ${PYTHON} -m pip uninstall -y core
9 | else
10 | ${PYTHON} -m venv /opt/core/venv
11 | . /opt/core/venv/bin/activate
12 | pip uninstall -y core
13 | rm -rf /opt/core/venv
14 | rm -rf /opt/core/share
15 | fi
16 |
--------------------------------------------------------------------------------
/package/after-remove-rpm.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | if [ ! -z "${NO_PYTHON}" ]; then
3 | exit 0
4 | fi
5 |
6 | PYTHON="${PYTHON:=python3}"
7 | if [ ! -z "${NO_VENV}" ]; then
8 | ${PYTHON} -m pip uninstall -y core
9 | else
10 | ${PYTHON} -m venv /opt/core/venv
11 | . /opt/core/venv/bin/activate
12 | pip uninstall -y core
13 | rm -rf /opt/core/venv
14 | rm -rf /opt/core/share
15 | fi
16 | systemctl --no-reload disable core-daemon
17 | systemctl stop core-daemon
18 |
--------------------------------------------------------------------------------
/package/core-daemon.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Common Open Research Emulator Service
3 | After=network.target
4 |
5 | [Service]
6 | Type=simple
7 | ExecStart=$DAEMON
8 | TasksMax=infinity
9 |
10 | [Install]
11 | WantedBy=multi-user.target
12 |
--------------------------------------------------------------------------------
/package/etc/logging.conf:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "handlers": {
4 | "console": {
5 | "class": "logging.StreamHandler",
6 | "formatter": "default",
7 | "level": "DEBUG",
8 | "stream": "ext://sys.stdout"
9 | }
10 | },
11 | "formatters": {
12 | "default": {
13 | "format": "%(asctime)s - %(levelname)s - %(module)s:%(funcName)s - %(message)s"
14 | }
15 | },
16 | "loggers": {
17 | "": {
18 | "level": "WARNING",
19 | "handlers": ["console"],
20 | "propagate": false
21 | },
22 | "core": {
23 | "level": "INFO",
24 | "handlers": ["console"],
25 | "propagate": false
26 | },
27 | "__main__": {
28 | "level": "INFO",
29 | "handlers": ["console"],
30 | "propagate": false
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/package/share/examples/controlnet_updown:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Sample controlnet up/down script that will be executed when the control
3 | # network is brought up or down. This script either adds an interface to the
4 | # controlnet bridge or adds a permissive iptables firewall rule.
5 |
6 | controlnet_intf=$1
7 | action=$2
8 |
9 | config_type=iptables # iptables or brctl
10 |
11 | iptables_address=10.205.15.132
12 | brctl_intf=eth2
13 |
14 | BRCTL=/sbin/brctl
15 | IPTABLES=/usr/sbin/iptables
16 |
17 | case "$action" in
18 | startup)
19 | case "$config_type" in
20 | iptables)
21 | $IPTABLES -I FORWARD -i $controlnet_intf -d $iptables_address -j ACCEPT
22 | $IPTABLES -I FORWARD -o $controlnet_intf -s $iptables_address -j ACCEPT
23 | ;;
24 | brctl)
25 | $BRCTL addif $controlnet_intf $brctl_intf
26 | ;;
27 | *)
28 | echo "Invalid config_type $config_type"
29 | ;;
30 | esac
31 | ;;
32 |
33 | shutdown)
34 | case "$config_type" in
35 | iptables)
36 | $IPTABLES -D FORWARD -i $controlnet_intf -d $iptables_address -j ACCEPT
37 | $IPTABLES -D FORWARD -o $controlnet_intf -s $iptables_address -j ACCEPT
38 | ;;
39 | brctl)
40 | $BRCTL delif $controlnet_intf $brctl_intf
41 | ;;
42 | *)
43 | echo "Invalid config_type $config_type"
44 | ;;
45 | esac
46 | ;;
47 |
48 | *)
49 | echo "Invalid action $action"
50 | exit 1
51 | ;;
52 | esac
53 | exit 0
54 |
--------------------------------------------------------------------------------
/package/share/examples/custom_emane/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/package/share/examples/custom_emane/__init__.py
--------------------------------------------------------------------------------
/package/share/examples/custom_service/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/package/share/examples/custom_service/__init__.py
--------------------------------------------------------------------------------
/package/share/examples/docker/docker2core.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from core.emulator.coreemu import CoreEmu
4 | from core.emulator.data import IpPrefixes
5 | from core.emulator.enumerations import EventTypes
6 | from core.nodes.base import CoreNode
7 | from core.nodes.docker import DockerNode
8 |
9 | if __name__ == "__main__":
10 | logging.basicConfig(level=logging.DEBUG)
11 | coreemu = CoreEmu()
12 | session = coreemu.create_session()
13 | session.set_state(EventTypes.CONFIGURATION_STATE)
14 |
15 | try:
16 | prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
17 |
18 | # create node one
19 | options = DockerNode.create_options()
20 | options.image = "ubuntu"
21 | node1 = session.add_node(DockerNode, options=options)
22 | interface1_data = prefixes.create_iface(node1)
23 |
24 | # create node two
25 | node2 = session.add_node(CoreNode)
26 | interface2_data = prefixes.create_iface(node2)
27 |
28 | # add link
29 | session.add_link(node1.id, node2.id, interface1_data, interface2_data)
30 |
31 | # instantiate
32 | session.instantiate()
33 | finally:
34 | input("continue to shutdown")
35 | coreemu.shutdown()
36 |
--------------------------------------------------------------------------------
/package/share/examples/docker/docker2docker.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from core.emulator.coreemu import CoreEmu
4 | from core.emulator.data import IpPrefixes
5 | from core.emulator.enumerations import EventTypes
6 | from core.nodes.docker import DockerNode
7 |
8 | if __name__ == "__main__":
9 | logging.basicConfig(level=logging.DEBUG)
10 |
11 | coreemu = CoreEmu()
12 | session = coreemu.create_session()
13 | session.set_state(EventTypes.CONFIGURATION_STATE)
14 |
15 | # create nodes and interfaces
16 | try:
17 | prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
18 |
19 | # create node one
20 | options = DockerNode.create_options()
21 | options.image = "ubuntu"
22 | node1 = session.add_node(DockerNode, options=options)
23 | interface1_data = prefixes.create_iface(node1)
24 |
25 | # create node two
26 | node2 = session.add_node(DockerNode, options=options)
27 | interface2_data = prefixes.create_iface(node2)
28 |
29 | # add link
30 | session.add_link(node1.id, node2.id, interface1_data, interface2_data)
31 |
32 | # instantiate
33 | session.instantiate()
34 | finally:
35 | input("continue to shutdown")
36 | coreemu.shutdown()
37 |
--------------------------------------------------------------------------------
/package/share/examples/docker/switch.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from core.emulator.coreemu import CoreEmu
4 | from core.emulator.data import IpPrefixes
5 | from core.emulator.enumerations import EventTypes
6 | from core.nodes.base import CoreNode
7 | from core.nodes.docker import DockerNode
8 | from core.nodes.network import SwitchNode
9 |
10 | if __name__ == "__main__":
11 | logging.basicConfig(level=logging.DEBUG)
12 |
13 | # create core session
14 | coreemu = CoreEmu()
15 | session = coreemu.create_session()
16 | session.set_state(EventTypes.CONFIGURATION_STATE)
17 | try:
18 | prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
19 |
20 | # create switch
21 | switch = session.add_node(SwitchNode)
22 |
23 | # node one
24 | options = DockerNode.create_options()
25 | options.image = "ubuntu"
26 | node1 = session.add_node(DockerNode, options=options)
27 | interface1_data = prefixes.create_iface(node1)
28 |
29 | # node two
30 | node2 = session.add_node(DockerNode, options=options)
31 | interface2_data = prefixes.create_iface(node2)
32 |
33 | # node three
34 | node_three = session.add_node(CoreNode)
35 | interface_three = prefixes.create_iface(node_three)
36 |
37 | # add links
38 | session.add_link(node1.id, switch.id, interface1_data)
39 | session.add_link(node2.id, switch.id, interface2_data)
40 | session.add_link(node_three.id, switch.id, interface_three)
41 |
42 | # instantiate
43 | session.instantiate()
44 |
45 | print(f"{node2.name}: {node2.volumes.values()}")
46 | finally:
47 | input("continue to shutdown")
48 | coreemu.shutdown()
49 |
--------------------------------------------------------------------------------
/package/share/examples/grpc/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/package/share/examples/grpc/__init__.py
--------------------------------------------------------------------------------
/package/share/examples/grpc/custom_service.py:
--------------------------------------------------------------------------------
1 | from core.api.grpc import client
2 | from core.api.grpc.wrappers import Position, Service
3 |
4 | # interface helper
5 | iface_helper = client.InterfaceHelper(ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64")
6 |
7 | # create grpc client and connect
8 | core = client.CoreGrpcClient()
9 | core.connect()
10 |
11 | # create new service
12 | file_name = "example.sh"
13 | custom_service = Service(
14 | name="custom",
15 | group="Custom",
16 | files=[file_name],
17 | startup=[f"bash {file_name}"],
18 | )
19 | templates = {file_name: "# this is a custom service ${node.name}"}
20 | core.create_service(custom_service, templates)
21 |
22 | # add session
23 | session = core.create_session()
24 |
25 | # create nodes
26 | position = Position(x=100, y=100)
27 | node1 = session.add_node(1, position=position)
28 | node1.services = ["custom"]
29 | position = Position(x=300, y=100)
30 | node2 = session.add_node(2, position=position)
31 |
32 | # create link
33 | iface1 = iface_helper.create_iface(node1.id, 0)
34 | iface2 = iface_helper.create_iface(node2.id, 0)
35 | session.add_link(node1=node1, node2=node2, iface1=iface1, iface2=iface2)
36 |
37 | # start session
38 | core.start_session(session)
39 |
--------------------------------------------------------------------------------
/package/share/examples/grpc/distributed_switch.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import logging
3 |
4 | from core.api.grpc import client
5 | from core.api.grpc.wrappers import NodeType, Position, Server
6 |
7 |
8 | def log_event(event):
9 | logging.info("event: %s", event)
10 |
11 |
12 | def main(args):
13 | # helper to create interfaces
14 | interface_helper = client.InterfaceHelper(ip4_prefix="10.83.0.0/16")
15 |
16 | # create grpc client and connect
17 | core = client.CoreGrpcClient()
18 | core.connect()
19 |
20 | # create session
21 | session = core.create_session()
22 |
23 | # add distributed server
24 | server = Server(name="core2", host=args.server)
25 | session.servers.append(server)
26 |
27 | # handle events session may broadcast
28 | core.events(session.id, log_event)
29 |
30 | # create switch node
31 | position = Position(x=150, y=100)
32 | switch = session.add_node(1, _type=NodeType.SWITCH, position=position)
33 | position = Position(x=100, y=50)
34 | node1 = session.add_node(2, position=position)
35 | position = Position(x=200, y=50)
36 | node2 = session.add_node(3, position=position, server=server.name)
37 |
38 | # create links
39 | iface1 = interface_helper.create_iface(node1.id, 0)
40 | session.add_link(node1=node1, node2=switch, iface1=iface1)
41 | iface1 = interface_helper.create_iface(node2.id, 0)
42 | session.add_link(node1=node2, node2=switch, iface1=iface1)
43 |
44 | # start session
45 | core.start_session(session)
46 |
47 |
48 | if __name__ == "__main__":
49 | logging.basicConfig(level=logging.DEBUG)
50 | parser = argparse.ArgumentParser(description="Run distributed_switch example")
51 | parser.add_argument(
52 | "-a",
53 | "--address",
54 | required=True,
55 | help="local address that distributed servers will use for gre tunneling",
56 | )
57 | parser.add_argument(
58 | "-s",
59 | "--server",
60 | required=True,
61 | help="distributed server to use for creating nodes",
62 | )
63 | args = parser.parse_args()
64 | main(args)
65 |
--------------------------------------------------------------------------------
/package/share/examples/grpc/emane80211.py:
--------------------------------------------------------------------------------
1 | # required imports
2 | from core.api.grpc import client
3 | from core.api.grpc.wrappers import NodeType, Position, Interface
4 | from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
5 |
6 | # interface helper
7 | iface_helper = client.InterfaceHelper(ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64")
8 |
9 | # create grpc client and connect
10 | core = client.CoreGrpcClient()
11 | core.connect()
12 |
13 | # add session
14 | session = core.create_session()
15 |
16 | # create nodes
17 | position = Position(x=200, y=200)
18 | emane = session.add_node(
19 | 1, _type=NodeType.EMANE, position=position, emane=EmaneIeee80211abgModel.name
20 | )
21 | position = Position(x=100, y=100)
22 | node1 = session.add_node(2, model="mdr", position=position)
23 | position = Position(x=300, y=100)
24 | node2 = session.add_node(3, model="mdr", position=position)
25 |
26 | # create links
27 | iface1 = Interface(id=0, ip4="10.0.0.1", ip4_mask=24)
28 | session.add_link(node1=node1, node2=emane, iface1=iface1)
29 | iface1 = Interface(id=0, ip4="10.0.0.2", ip4_mask=24)
30 | session.add_link(node1=node2, node2=emane, iface1=iface1)
31 |
32 | # setup emane configurations using a dict mapping currently support values as strings
33 | emane.set_emane_model(
34 | EmaneIeee80211abgModel.name, {"eventservicettl": "2", "unicastrate": "3"}
35 | )
36 |
37 | # start session
38 | core.start_session(session)
39 |
--------------------------------------------------------------------------------
/package/share/examples/grpc/peertopeer.py:
--------------------------------------------------------------------------------
1 | from core.api.grpc import client
2 | from core.api.grpc.wrappers import Position
3 |
4 | # interface helper
5 | iface_helper = client.InterfaceHelper(ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64")
6 |
7 | # create grpc client and connect
8 | core = client.CoreGrpcClient()
9 | core.connect()
10 |
11 | # add session
12 | session = core.create_session()
13 |
14 | # create nodes
15 | position = Position(x=100, y=100)
16 | node1 = session.add_node(1, position=position)
17 | position = Position(x=300, y=100)
18 | node2 = session.add_node(2, position=position)
19 |
20 | # create link
21 | iface1 = iface_helper.create_iface(node1.id, 0)
22 | iface2 = iface_helper.create_iface(node2.id, 0)
23 | session.add_link(node1=node1, node2=node2, iface1=iface1, iface2=iface2)
24 |
25 | # start session
26 | core.start_session(session)
27 |
--------------------------------------------------------------------------------
/package/share/examples/grpc/switch.py:
--------------------------------------------------------------------------------
1 | from core.api.grpc import client, wrappers
2 | from core.api.grpc.wrappers import NodeType, Position
3 |
4 | # interface helper
5 | iface_helper = client.InterfaceHelper(ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64")
6 |
7 | # create grpc client and connect
8 | core = client.CoreGrpcClient()
9 | core.connect()
10 |
11 | # add session
12 | session = core.create_session()
13 |
14 | # create nodes
15 | position = Position(x=200, y=200)
16 | switch = session.add_node(1, _type=NodeType.SWITCH, position=position)
17 | position = Position(x=100, y=100)
18 | node1 = session.add_node(2, position=position)
19 | position = Position(x=300, y=100)
20 | node2 = session.add_node(3, position=position)
21 |
22 | # create links
23 | node1_iface1 = iface_helper.create_iface(node1.id, 0)
24 | switch_iface1 = wrappers.Interface(0)
25 | session.add_link(node1=node1, node2=switch, iface1=node1_iface1, iface2=switch_iface1)
26 | node2_iface1 = iface_helper.create_iface(node2.id, 0)
27 | switch_iface2 = wrappers.Interface(1)
28 | session.add_link(node1=node2, node2=switch, iface1=node2_iface1, iface2=switch_iface2)
29 |
30 | # start session
31 | core.start_session(session)
32 |
--------------------------------------------------------------------------------
/package/share/examples/grpc/wireless.py:
--------------------------------------------------------------------------------
1 | from core.api.grpc import client
2 | from core.api.grpc.wrappers import NodeType, Position, Interface
3 |
4 | # create grpc client and connect
5 | core = client.CoreGrpcClient()
6 | core.connect()
7 |
8 | # add session
9 | session = core.create_session()
10 |
11 | # create nodes
12 | position = Position(x=200, y=200)
13 | wlan = session.add_node(1, _type=NodeType.WIRELESS, position=position)
14 | position = Position(x=100, y=100)
15 | node1 = session.add_node(2, model="mdr", position=position)
16 | position = Position(x=300, y=100)
17 | node2 = session.add_node(3, model="mdr", position=position)
18 |
19 | # create links
20 | iface1 = Interface(id=0, ip4="10.0.0.1", ip4_mask=24, ip6="2001::1", ip6_mask=128)
21 | session.add_link(node1=node1, node2=wlan, iface1=iface1)
22 | iface1 = Interface(id=0, ip4="10.0.0.2", ip4_mask=24, ip6="2001::2", ip6_mask=128)
23 | session.add_link(node1=node2, node2=wlan, iface1=iface1)
24 |
25 | # start session
26 | core.start_session(session)
27 |
--------------------------------------------------------------------------------
/package/share/examples/grpc/wlan.py:
--------------------------------------------------------------------------------
1 | from core.api.grpc import client
2 | from core.api.grpc.wrappers import NodeType, Position
3 |
4 | # interface helper
5 | iface_helper = client.InterfaceHelper(ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64")
6 |
7 | # create grpc client and connect
8 | core = client.CoreGrpcClient()
9 | core.connect()
10 |
11 | # add session
12 | session = core.create_session()
13 |
14 | # create nodes
15 | position = Position(x=200, y=200)
16 | wlan = session.add_node(1, _type=NodeType.WIRELESS_LAN, position=position)
17 | position = Position(x=100, y=100)
18 | node1 = session.add_node(2, model="mdr", position=position)
19 | position = Position(x=300, y=100)
20 | node2 = session.add_node(3, model="mdr", position=position)
21 |
22 | # create links
23 | iface1 = iface_helper.create_iface(node1.id, 0)
24 | session.add_link(node1=node1, node2=wlan, iface1=iface1)
25 | iface1 = iface_helper.create_iface(node2.id, 0)
26 | session.add_link(node1=node2, node2=wlan, iface1=iface1)
27 |
28 | # set wlan config using a dict mapping currently
29 | # support values as strings
30 | wlan.set_wlan(
31 | {
32 | "range": "280",
33 | "bandwidth": "55000000",
34 | "delay": "6000",
35 | "jitter": "5",
36 | "error": "5",
37 | }
38 | )
39 |
40 | # start session
41 | core.start_session(session)
42 |
--------------------------------------------------------------------------------
/package/share/examples/python/configure_services.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from core.emulator.coreemu import CoreEmu
4 | from core.emulator.data import IpPrefixes
5 | from core.emulator.enumerations import EventTypes
6 | from core.nodes.base import CoreNode
7 | from core.nodes.network import SwitchNode
8 |
9 | if __name__ == "__main__":
10 | logging.basicConfig(level=logging.DEBUG)
11 |
12 | # setup basic network
13 | prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
14 | coreemu = CoreEmu()
15 | session = coreemu.create_session()
16 | session.set_state(EventTypes.CONFIGURATION_STATE)
17 | switch = session.add_node(SwitchNode)
18 |
19 | # node one
20 | options = CoreNode.create_options()
21 | options.services = ["DefaultRoute", "IPForward"]
22 | node1 = session.add_node(CoreNode, options=options)
23 | interface = prefixes.create_iface(node1)
24 | session.add_link(node1.id, switch.id, iface1_data=interface)
25 |
26 | # node two
27 | node2 = session.add_node(CoreNode, options=options)
28 | interface = prefixes.create_iface(node2)
29 | session.add_link(node2.id, switch.id, iface1_data=interface)
30 |
31 | # start session and run services
32 | session.instantiate()
33 |
34 | input("press enter to exit")
35 | session.shutdown()
36 |
--------------------------------------------------------------------------------
/package/share/examples/python/distributed_ptp.py:
--------------------------------------------------------------------------------
1 | """
2 | Example for scripting a standalone distributed peer to peer session that does not
3 | interact with the GUI.
4 | """
5 |
6 | import argparse
7 | import logging
8 |
9 | from core.emulator.coreemu import CoreEmu
10 | from core.emulator.data import IpPrefixes
11 | from core.emulator.enumerations import EventTypes
12 | from core.nodes.base import CoreNode
13 |
14 |
15 | def parse(name):
16 | parser = argparse.ArgumentParser(description=f"Run {name} example")
17 | parser.add_argument(
18 | "-a",
19 | "--address",
20 | help="local address that distributed servers will use for gre tunneling",
21 | )
22 | parser.add_argument(
23 | "-s", "--server", help="distributed server to use for creating nodes"
24 | )
25 | options = parser.parse_args()
26 | return options
27 |
28 |
29 | def main(args):
30 | # ip generator for example
31 | prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
32 |
33 | # create emulator instance for creating sessions and utility methods
34 | coreemu = CoreEmu({"distributed_address": args.address})
35 | session = coreemu.create_session()
36 |
37 | # initialize distributed
38 | server_name = "core2"
39 | session.distributed.add_server(server_name, args.server)
40 |
41 | # must be in configuration state for nodes to start, when using "node_add" below
42 | session.set_state(EventTypes.CONFIGURATION_STATE)
43 |
44 | # create local node, switch, and remote nodes
45 | node1 = session.add_node(CoreNode)
46 | node2 = session.add_node(CoreNode, server=server_name)
47 |
48 | # create node interfaces and link
49 | interface1_data = prefixes.create_iface(node1)
50 | interface2_data = prefixes.create_iface(node2)
51 | session.add_link(node1.id, node2.id, interface1_data, interface2_data)
52 |
53 | # instantiate session
54 | session.instantiate()
55 |
56 | # pause script for verification
57 | input("press enter for shutdown")
58 |
59 | # shutdown session
60 | coreemu.shutdown()
61 |
62 |
63 | if __name__ == "__main__":
64 | logging.basicConfig(level=logging.INFO)
65 | args = parse(__file__)
66 | main(args)
67 |
--------------------------------------------------------------------------------
/package/share/examples/python/emane80211.py:
--------------------------------------------------------------------------------
1 | # required imports
2 | from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
3 | from core.emane.nodes import EmaneNet
4 | from core.emulator.coreemu import CoreEmu
5 | from core.emulator.data import InterfaceData
6 | from core.emulator.enumerations import EventTypes
7 | from core.nodes.base import CoreNode, Position
8 |
9 | # create emulator instance for creating sessions and utility methods
10 | coreemu = CoreEmu()
11 | session = coreemu.create_session()
12 |
13 | # location information is required to be set for emane
14 | session.location.setrefgeo(47.57917, -122.13232, 2.0)
15 | session.location.refscale = 150.0
16 |
17 | # must be in configuration state for nodes to start, when using "node_add" below
18 | session.set_state(EventTypes.CONFIGURATION_STATE)
19 |
20 | # create emane
21 | options = EmaneNet.create_options()
22 | options.emane_model = EmaneIeee80211abgModel.name
23 | position = Position(x=200, y=200)
24 | emane = session.add_node(EmaneNet, position=position, options=options)
25 |
26 | # create nodes
27 | options = CoreNode.create_options()
28 | options.model = "mdr"
29 | position = Position(x=100, y=100)
30 | n1 = session.add_node(CoreNode, position=position, options=options)
31 | options = CoreNode.create_options()
32 | options.model = "mdr"
33 | position = Position(x=300, y=100)
34 | n2 = session.add_node(CoreNode, position=position, options=options)
35 |
36 | # configure emane settings
37 | # configuration values are currently supported as strings
38 | session.emane.set_config(
39 | emane.id,
40 | EmaneIeee80211abgModel.name,
41 | {"unicastrate": "3", "eventservicettl": "2"},
42 | )
43 |
44 | # link nodes to emane
45 | iface1 = InterfaceData(ip4="10.0.0.1", ip4_mask=32)
46 | session.add_link(n1.id, emane.id, iface1)
47 | iface1 = InterfaceData(ip4="10.0.0.2", ip4_mask=32)
48 | session.add_link(n2.id, emane.id, iface1)
49 |
50 | # start session
51 | session.instantiate()
52 |
53 | # do whatever you like here
54 | input("press enter to shutdown")
55 |
56 | # stop session
57 | session.shutdown()
58 |
--------------------------------------------------------------------------------
/package/share/examples/python/peertopeer.py:
--------------------------------------------------------------------------------
1 | # required imports
2 | from core.emulator.coreemu import CoreEmu
3 | from core.emulator.data import IpPrefixes
4 | from core.emulator.enumerations import EventTypes
5 | from core.nodes.base import CoreNode, Position
6 |
7 | # ip nerator for example
8 | ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
9 |
10 | # create emulator instance for creating sessions and utility methods
11 | coreemu = CoreEmu()
12 | session = coreemu.create_session()
13 |
14 | # must be in configuration state for nodes to start, when using "node_add" below
15 | session.set_state(EventTypes.CONFIGURATION_STATE)
16 |
17 | # create nodes
18 | position = Position(x=100, y=100)
19 | n1 = session.add_node(CoreNode, position=position)
20 | position = Position(x=300, y=100)
21 | n2 = session.add_node(CoreNode, position=position)
22 |
23 | # link nodes together
24 | iface1 = ip_prefixes.create_iface(n1)
25 | iface2 = ip_prefixes.create_iface(n2)
26 | session.add_link(n1.id, n2.id, iface1, iface2)
27 |
28 | # start session
29 | session.instantiate()
30 |
31 | # do whatever you like here
32 | input("press enter to shutdown")
33 |
34 | # stop session
35 | session.shutdown()
36 |
--------------------------------------------------------------------------------
/package/share/examples/python/switch.py:
--------------------------------------------------------------------------------
1 | # required imports
2 | from core.emulator.coreemu import CoreEmu
3 | from core.emulator.data import IpPrefixes
4 | from core.emulator.enumerations import EventTypes
5 | from core.nodes.base import CoreNode, Position
6 | from core.nodes.network import SwitchNode
7 |
8 | # ip nerator for example
9 | ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
10 |
11 | # create emulator instance for creating sessions and utility methods
12 | coreemu = CoreEmu()
13 | session = coreemu.create_session()
14 |
15 | # must be in configuration state for nodes to start, when using "node_add" below
16 | session.set_state(EventTypes.CONFIGURATION_STATE)
17 |
18 | # create switch
19 | position = Position(x=200, y=200)
20 | switch = session.add_node(SwitchNode, position=position)
21 |
22 | # create nodes
23 | position = Position(x=100, y=100)
24 | n1 = session.add_node(CoreNode, position=position)
25 | position = Position(x=300, y=100)
26 | n2 = session.add_node(CoreNode, position=position)
27 |
28 | # link nodes to switch
29 | iface1 = ip_prefixes.create_iface(n1)
30 | session.add_link(n1.id, switch.id, iface1)
31 | iface1 = ip_prefixes.create_iface(n2)
32 | session.add_link(n2.id, switch.id, iface1)
33 |
34 | # start session
35 | session.instantiate()
36 |
37 | # do whatever you like here
38 | input("press enter to shutdown")
39 |
40 | # stop session
41 | session.shutdown()
42 |
--------------------------------------------------------------------------------
/package/share/examples/python/wireless.py:
--------------------------------------------------------------------------------
1 | # required imports
2 | import logging
3 |
4 | from core.emulator.coreemu import CoreEmu
5 | from core.emulator.data import InterfaceData
6 | from core.emulator.enumerations import EventTypes
7 | from core.nodes.base import CoreNode, Position
8 | from core.nodes.network import WlanNode
9 |
10 | # enable info logging
11 | logging.basicConfig(level=logging.INFO)
12 |
13 | # create emulator instance for creating sessions and utility methods
14 | coreemu = CoreEmu()
15 | session = coreemu.create_session()
16 |
17 | # must be in configuration state for nodes to start
18 | session.set_state(EventTypes.CONFIGURATION_STATE)
19 |
20 | # create wireless
21 | position = Position(x=200, y=200)
22 | wireless = session.add_node(WlanNode, position=position)
23 |
24 | # create nodes
25 | options = CoreNode.create_options()
26 | options.model = "mdr"
27 | position = Position(x=100, y=100)
28 | n1 = session.add_node(CoreNode, position=position, options=options)
29 | options = CoreNode.create_options()
30 | options.model = "mdr"
31 | position = Position(x=300, y=100)
32 | n2 = session.add_node(CoreNode, position=position, options=options)
33 |
34 | # link nodes to wireless
35 | iface1 = InterfaceData(ip4="10.0.0.1", ip4_mask=32, ip6="2001::1", ip6_mask=128)
36 | session.add_link(n1.id, wireless.id, iface1)
37 | iface1 = InterfaceData(ip4="10.0.0.2", ip4_mask=32, ip6="2001::2", ip6_mask=128)
38 | session.add_link(n2.id, wireless.id, iface1)
39 |
40 | # start session
41 | session.instantiate()
42 |
43 | # do whatever you like here
44 | input("press enter to shutdown")
45 |
46 | # stop session
47 | session.shutdown()
48 |
--------------------------------------------------------------------------------
/package/share/examples/python/wlan.py:
--------------------------------------------------------------------------------
1 | # required imports
2 | from core.emulator.coreemu import CoreEmu
3 | from core.emulator.data import IpPrefixes
4 | from core.emulator.enumerations import EventTypes
5 | from core.location.mobility import BasicRangeModel
6 | from core.nodes.base import CoreNode, Position
7 | from core.nodes.network import WlanNode
8 |
9 | # ip nerator for example
10 | ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
11 |
12 | # create emulator instance for creating sessions and utility methods
13 | coreemu = CoreEmu()
14 | session = coreemu.create_session()
15 |
16 | # must be in configuration state for nodes to start, when using "node_add" below
17 | session.set_state(EventTypes.CONFIGURATION_STATE)
18 |
19 | # create wlan
20 | position = Position(x=200, y=200)
21 | wlan = session.add_node(WlanNode, position=position)
22 |
23 | # create nodes
24 | options = CoreNode.create_options()
25 | options.model = "mdr"
26 | position = Position(x=100, y=100)
27 | n1 = session.add_node(CoreNode, position=position, options=options)
28 | options = CoreNode.create_options()
29 | options.model = "mdr"
30 | position = Position(x=300, y=100)
31 | n2 = session.add_node(CoreNode, position=position, options=options)
32 |
33 | # configuring wlan
34 | session.mobility.set_model_config(
35 | wlan.id,
36 | BasicRangeModel.name,
37 | {
38 | "range": "280",
39 | "bandwidth": "55000000",
40 | "delay": "6000",
41 | "jitter": "5",
42 | "error": "5",
43 | },
44 | )
45 |
46 | # link nodes to wlan
47 | iface1 = ip_prefixes.create_iface(n1)
48 | session.add_link(n1.id, wlan.id, iface1)
49 | iface1 = ip_prefixes.create_iface(n2)
50 | session.add_link(n2.id, wlan.id, iface1)
51 |
52 | # start session
53 | session.instantiate()
54 |
55 | # do whatever you like here
56 | input("press enter to shutdown")
57 |
58 | # stop session
59 | session.shutdown()
60 |
--------------------------------------------------------------------------------
/package/share/examples/tdma/schedule.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/package/share/services/sampleFirewall:
--------------------------------------------------------------------------------
1 | # -------- CUSTOMIZATION REQUIRED --------
2 | #
3 | # Below are sample iptables firewall rules that you can uncomment and edit.
4 | # You can also use ip6tables rules for IPv6.
5 | #
6 |
7 | # start by flushing all firewall rules (so this script may be re-run)
8 | #iptables -F
9 |
10 | # allow traffic related to established connections
11 | #iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
12 |
13 | # allow TCP packets from any source destined for 192.168.1.1
14 | #iptables -A INPUT -s 0/0 -i eth0 -d 192.168.1.1 -p TCP -j ACCEPT
15 |
16 | # allow OpenVPN server traffic from eth0
17 | #iptables -A INPUT -p udp --dport 1194 -j ACCEPT
18 | #iptables -A INPUT -i eth0 -j DROP
19 | #iptables -A OUTPUT -p udp --sport 1194 -j ACCEPT
20 | #iptables -A OUTPUT -o eth0 -j DROP
21 |
22 | # allow ICMP ping traffic
23 | #iptables -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
24 | #iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
25 |
26 | # allow SSH traffic
27 | #iptables -A -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
28 |
29 | # drop all other traffic coming in eth0
30 | #iptables -A INPUT -i eth0 -j DROP
31 |
--------------------------------------------------------------------------------
/package/share/services/sampleVPNClient:
--------------------------------------------------------------------------------
1 | # -------- CUSTOMIZATION REQUIRED --------
2 | #
3 | # The VPNClient service builds a VPN tunnel to the specified VPN server using
4 | # OpenVPN software and a virtual TUN/TAP device.
5 |
6 | # directory containing the certificate and key described below
7 | keydir=/opt/core/etc/keys
8 |
9 | # the name used for a "$keyname.crt" certificate and "$keyname.key" private key.
10 | keyname=client1
11 |
12 | # the public IP address of the VPN server this client should connect with
13 | vpnserver="10.0.2.10"
14 |
15 | # optional next hop for adding a static route to reach the VPN server
16 | #nexthop="10.0.1.1"
17 |
18 | # --------- END CUSTOMIZATION --------
19 |
20 | # validate addresses
21 | if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then
22 | echo "WARNING: ip validation disabled because package sipcalc not installed
23 | " > $PWD/vpnclient.log
24 | else
25 | if [ "$(sipcalc "$vpnserver" "$nexthop" | grep ERR)" != "" ]; then
26 | echo "ERROR: invalide address $vpnserver or $nexthop \
27 | " > $PWD/vpnclient.log
28 | fi
29 | fi
30 |
31 | # validate key and certification files
32 | if [ ! -e $keydir\/$keyname.key ] || [ ! -e $keydir\/$keyname.crt ] \
33 | || [ ! -e $keydir\/ca.crt ] || [ ! -e $keydir\/dh1024.pem ]; then
34 | echo "ERROR: missing certification or key files under $keydir \
35 | $keyname.key or $keyname.crt or ca.crt or dh1024.pem" >> $PWD/vpnclient.log
36 | fi
37 |
38 | # if necessary, add a static route for reaching the VPN server IP via the IF
39 | vpnservernet=${vpnserver%.*}.0/24
40 | if [ "$nexthop" != "" ]; then
41 | /sbin/ip route add $vpnservernet via $nexthop
42 | fi
43 |
44 | # create openvpn client.conf
45 | (
46 | cat << EOF
47 | client
48 | dev tun
49 | proto udp
50 | remote $vpnserver 1194
51 | nobind
52 | ca $keydir/ca.crt
53 | cert $keydir/$keyname.crt
54 | key $keydir/$keyname.key
55 | dh $keydir/dh1024.pem
56 | cipher AES-256-CBC
57 | log $PWD/openvpn-client.log
58 | verb 4
59 | daemon
60 | EOF
61 | ) > client.conf
62 |
63 | openvpn --config client.conf
64 |
--------------------------------------------------------------------------------
/package/share/tutorials/chatapp/chatapp/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/package/share/tutorials/chatapp/chatapp/__init__.py
--------------------------------------------------------------------------------
/package/share/tutorials/chatapp/chatapp/client.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import select
3 | import socket
4 | import sys
5 | import termios
6 |
7 | DEFAULT_PORT: int = 9001
8 | READ_SIZE: int = 4096
9 |
10 |
11 | def prompt():
12 | sys.stdout.write(">> ")
13 | sys.stdout.flush()
14 |
15 |
16 | class ChatClient:
17 | def __init__(self, address, port):
18 | self.address = address
19 | self.port = port
20 |
21 | def run(self):
22 | server = socket.create_connection((self.address, self.port))
23 | sockname = server.getsockname()
24 | print(
25 | f"connected to server({self.address}:{self.port}) as "
26 | f"client({sockname[0]}:{sockname[1]})"
27 | )
28 | sockets = [sys.stdin, server]
29 | prompt()
30 | try:
31 | while True:
32 | read_sockets, write_socket, error_socket = select.select(
33 | sockets, [], []
34 | )
35 | for sock in read_sockets:
36 | if sock == server:
37 | message = server.recv(READ_SIZE)
38 | if not message:
39 | print("server closed")
40 | sys.exit(1)
41 | else:
42 | termios.tcflush(sys.stdin, termios.TCIOFLUSH)
43 | print("\x1b[2K\r", end="")
44 | print(message.decode().strip())
45 | prompt()
46 | else:
47 | message = sys.stdin.readline().strip()
48 | server.sendall(f"{message}\n".encode())
49 | prompt()
50 | except KeyboardInterrupt:
51 | print("client exiting")
52 | server.close()
53 |
54 |
55 | def main():
56 | parser = argparse.ArgumentParser(
57 | description="chat app client",
58 | formatter_class=argparse.ArgumentDefaultsHelpFormatter,
59 | )
60 | parser.add_argument("-a", "--address", help="address to listen on", required=True)
61 | parser.add_argument(
62 | "-p", "--port", type=int, help="port to listen on", default=DEFAULT_PORT
63 | )
64 | args = parser.parse_args()
65 | client = ChatClient(args.address, args.port)
66 | client.run()
67 |
68 |
69 | if __name__ == "__main__":
70 | main()
71 |
--------------------------------------------------------------------------------
/package/share/tutorials/chatapp/chatapp_service.py:
--------------------------------------------------------------------------------
1 | from typing import Dict, List
2 |
3 | from core.config import Configuration
4 | from core.services.base import CoreService, ServiceMode, ShadowDir
5 |
6 |
7 | class ChatAppService(CoreService):
8 | name: str = "ChatApp Server"
9 | group: str = "ChatApp"
10 | directories: List[str] = []
11 | files: List[str] = ["chatapp.sh"]
12 | executables: List[str] = []
13 | dependencies: List[str] = []
14 | startup: List[str] = [f"bash {files[0]}"]
15 | validate: List[str] = []
16 | shutdown: List[str] = []
17 | validation_mode: ServiceMode = ServiceMode.BLOCKING
18 | default_configs: List[Configuration] = []
19 | modes: Dict[str, Dict[str, str]] = {}
20 | shadow_directories: List[ShadowDir] = []
21 |
22 | def get_text_template(self, _name: str) -> str:
23 | return """
24 | export PATH=$PATH:/usr/local/bin
25 | PYTHONUNBUFFERED=1 chatapp-server > chatapp.log 2>&1 &
26 | """
27 |
--------------------------------------------------------------------------------
/package/share/tutorials/chatapp/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 | setup(
4 | name="chatapp",
5 | version="0.1.0",
6 | packages=find_packages(),
7 | description="Chat App",
8 | entry_points={
9 | "console_scripts": [
10 | "chatapp-client = chatapp.client:main",
11 | "chatapp-server = chatapp.server:main",
12 | ],
13 | },
14 | include_package_data=True,
15 | zip_safe=False,
16 | python_requires=">=3.6",
17 | )
18 |
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial1/scenario.py:
--------------------------------------------------------------------------------
1 | from core.api.grpc import client
2 | from core.api.grpc.wrappers import Position
3 |
4 |
5 | def main():
6 | # interface helper
7 | iface_helper = client.InterfaceHelper(
8 | ip4_prefix="10.0.0.0/24",
9 | ip6_prefix="2001::/64",
10 | )
11 |
12 | # create grpc client and connect
13 | core = client.CoreGrpcClient()
14 | core.connect()
15 |
16 | # create session
17 | session = core.create_session()
18 |
19 | # create nodes
20 | position = Position(x=250, y=250)
21 | node1 = session.add_node(_id=1, name="n1", position=position)
22 | position = Position(x=500, y=250)
23 | node2 = session.add_node(_id=2, name="n2", position=position)
24 |
25 | # create link
26 | node1_iface = iface_helper.create_iface(node_id=node1.id, iface_id=0)
27 | node1_iface.ip4 = "10.0.0.20"
28 | node1_iface.ip6 = "2001::14"
29 | node2_iface = iface_helper.create_iface(node_id=node2.id, iface_id=0)
30 | node2_iface.ip4 = "10.0.0.21"
31 | node2_iface.ip6 = "2001::15"
32 | session.add_link(node1=node1, node2=node2, iface1=node1_iface, iface2=node2_iface)
33 |
34 | # start session
35 | core.start_session(session=session)
36 |
37 |
38 | if __name__ == "__main__":
39 | main()
40 |
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial1/scenario_service.py:
--------------------------------------------------------------------------------
1 | from core.api.grpc import client
2 | from core.api.grpc.wrappers import Position
3 |
4 |
5 | def main():
6 | # interface helper
7 | iface_helper = client.InterfaceHelper(
8 | ip4_prefix="10.0.0.0/24",
9 | ip6_prefix="2001::/64",
10 | )
11 |
12 | # create grpc client and connect
13 | core = client.CoreGrpcClient()
14 | core.connect()
15 |
16 | # create session
17 | session = core.create_session()
18 |
19 | # create nodes
20 | position = Position(x=250, y=250)
21 | node1 = session.add_node(_id=1, name="n1", position=position)
22 | node1.services.add("ChatApp Server")
23 | position = Position(x=500, y=250)
24 | node2 = session.add_node(_id=2, name="n2", position=position)
25 |
26 | # create link
27 | node1_iface = iface_helper.create_iface(node_id=node1.id, iface_id=0)
28 | node1_iface.ip4 = "10.0.0.20"
29 | node1_iface.ip6 = "2001::14"
30 | node2_iface = iface_helper.create_iface(node_id=node2.id, iface_id=0)
31 | node2_iface.ip4 = "10.0.0.21"
32 | node2_iface.ip6 = "2001::15"
33 | session.add_link(node1=node1, node2=node2, iface1=node1_iface, iface2=node2_iface)
34 |
35 | # start session
36 | core.start_session(session=session)
37 |
38 |
39 | if __name__ == "__main__":
40 | main()
41 |
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial2/scenario.py:
--------------------------------------------------------------------------------
1 | from core.api.grpc import client
2 | from core.api.grpc.wrappers import NodeType, Position
3 |
4 |
5 | def main():
6 | # interface helper
7 | iface_helper = client.InterfaceHelper(
8 | ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64"
9 | )
10 |
11 | # create grpc client and connect
12 | core = client.CoreGrpcClient()
13 | core.connect()
14 |
15 | # add session
16 | session = core.create_session()
17 |
18 | # create nodes
19 | position = Position(x=200, y=200)
20 | wlan = session.add_node(
21 | 1, name="wlan1", _type=NodeType.WIRELESS_LAN, position=position
22 | )
23 | position = Position(x=100, y=100)
24 | node1 = session.add_node(2, name="n2", model="mdr", position=position)
25 | position = Position(x=300, y=100)
26 | node2 = session.add_node(3, name="n3", model="mdr", position=position)
27 | position = Position(x=500, y=100)
28 | node3 = session.add_node(4, name="n4", model="mdr", position=position)
29 |
30 | # create links
31 | iface1 = iface_helper.create_iface(node1.id, 0)
32 | session.add_link(node1=node1, node2=wlan, iface1=iface1)
33 | iface1 = iface_helper.create_iface(node2.id, 0)
34 | session.add_link(node1=node2, node2=wlan, iface1=iface1)
35 | iface1 = iface_helper.create_iface(node3.id, 0)
36 | session.add_link(node1=node3, node2=wlan, iface1=iface1)
37 |
38 | # start session
39 | core.start_session(session)
40 |
41 |
42 | if __name__ == "__main__":
43 | main()
44 |
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial3/move-node2.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 | from core.api.grpc import client
4 | from core.api.grpc.wrappers import Position
5 |
6 |
7 | def main():
8 | # create grpc client and connect
9 | core = client.CoreGrpcClient("172.16.0.254:50051")
10 | core.connect()
11 |
12 | # get session
13 | sessions = core.get_sessions()
14 |
15 | print("sessions=", sessions)
16 | for i in range(300):
17 | position = Position(x=100, y=100 + i)
18 | core.move_node(sessions[0].id, 2, position=position)
19 | time.sleep(1)
20 | print("press enter to quit")
21 | input()
22 |
23 |
24 | if __name__ == "__main__":
25 | main()
26 |
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial3/movements1.txt:
--------------------------------------------------------------------------------
1 | $node_(1) set X_ 208.1
2 | $node_(1) set Y_ 211.05
3 | $node_(1) set Z_ 0
4 | $ns_ at 0.0 "$node_(1) setdest 208.1 211.05 0.00"
5 | $node_(2) set X_ 393.1
6 | $node_(2) set Y_ 223.05
7 | $node_(2) set Z_ 0
8 | $ns_ at 0.0 "$node_(2) setdest 393.1 223.05 0.00"
9 | $node_(4) set X_ 499.1
10 | $node_(4) set Y_ 186.05
11 | $node_(4) set Z_ 0
12 | $ns_ at 0.0 "$node_(4) setdest 499.1 186.05 0.00"
13 | $ns_ at 1.0 "$node_(1) setdest 190.1 225.05 0.00"
14 | $ns_ at 1.0 "$node_(2) setdest 393.1 225.05 0.00"
15 | $ns_ at 1.0 "$node_(4) setdest 515.1 186.05 0.00"
16 | $ns_ at 2.0 "$node_(1) setdest 175.1 250.05 0.00"
17 | $ns_ at 2.0 "$node_(2) setdest 393.1 250.05 0.00"
18 | $ns_ at 2.0 "$node_(4) setdest 530.1 186.05 0.00"
19 | $ns_ at 3.0 "$node_(1) setdest 160.1 275.05 0.00"
20 | $ns_ at 3.0 "$node_(2) setdest 393.1 275.05 0.00"
21 | $ns_ at 3.0 "$node_(4) setdest 530.1 186.05 0.00"
22 | $ns_ at 4.0 "$node_(1) setdest 160.1 300.05 0.00"
23 | $ns_ at 4.0 "$node_(2) setdest 393.1 300.05 0.00"
24 | $ns_ at 4.0 "$node_(4) setdest 550.1 186.05 0.00"
25 | $ns_ at 5.0 "$node_(1) setdest 160.1 275.05 0.00"
26 | $ns_ at 5.0 "$node_(2) setdest 393.1 275.05 0.00"
27 | $ns_ at 5.0 "$node_(4) setdest 530.1 186.05 0.00"
28 | $ns_ at 6.0 "$node_(1) setdest 175.1 250.05 0.00"
29 | $ns_ at 6.0 "$node_(2) setdest 393.1 250.05 0.00"
30 | $ns_ at 6.0 "$node_(4) setdest 515.1 186.05 0.00"
31 | $ns_ at 7.0 "$node_(1) setdest 190.1 225.05 0.00"
32 | $ns_ at 7.0 "$node_(2) setdest 393.1 225.05 0.00"
33 | $ns_ at 7.0 "$node_(4) setdest 499.1 186.05 0.00"
34 |
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial3/scenario.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 | from core.api.grpc import client
4 | from core.api.grpc.wrappers import NodeType, Position
5 |
6 |
7 | def main():
8 | # interface helper
9 | iface_helper = client.InterfaceHelper(
10 | ip4_prefix="10.0.0.0/24",
11 | ip6_prefix="2001::/64",
12 | )
13 |
14 | # create grpc client and connect
15 | core = client.CoreGrpcClient()
16 | core.connect()
17 |
18 | # add session
19 | session = core.create_session()
20 |
21 | # create nodes
22 | position = Position(x=200, y=200)
23 | wlan = session.add_node(
24 | 3,
25 | name="wlan3",
26 | _type=NodeType.WIRELESS_LAN,
27 | position=position,
28 | )
29 | position = Position(x=100, y=100)
30 | node1 = session.add_node(1, name="n1", model="mdr", position=position)
31 | position = Position(x=300, y=100)
32 | node2 = session.add_node(2, name="n2", model="mdr", position=position)
33 | position = Position(x=500, y=100)
34 | node3 = session.add_node(4, name="n4", model="mdr", position=position)
35 |
36 | # create links
37 | iface1 = iface_helper.create_iface(node1.id, 0)
38 | session.add_link(node1=node1, node2=wlan, iface1=iface1)
39 | iface1 = iface_helper.create_iface(node2.id, 0)
40 | session.add_link(node1=node2, node2=wlan, iface1=iface1)
41 | iface1 = iface_helper.create_iface(node3.id, 0)
42 | session.add_link(node1=node3, node2=wlan, iface1=iface1)
43 |
44 | # start session
45 | core.start_session(session)
46 | input("start motion, press enter")
47 |
48 | # move node 4
49 | for i in range(300):
50 | position = Position(x=500 + i, y=100)
51 | core.move_node(session.id, 4, position=position)
52 | time.sleep(1)
53 | x = 800
54 | for i in range(300):
55 | position = Position(x=800 - i, y=100)
56 | core.move_node(session.id, 4, position=position)
57 | time.sleep(1)
58 |
59 |
60 | if __name__ == "__main__":
61 | main()
62 |
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial4/tests/conftest.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from core.emulator.coreemu import CoreEmu
4 | from core.emulator.data import IpPrefixes
5 | from core.emulator.enumerations import EventTypes
6 |
7 |
8 | @pytest.fixture(scope="session")
9 | def global_session():
10 | core = CoreEmu()
11 | session = core.create_session()
12 | yield session
13 | core.shutdown()
14 |
15 |
16 | @pytest.fixture
17 | def session(global_session):
18 | global_session.set_state(EventTypes.CONFIGURATION_STATE)
19 | yield global_session
20 | global_session.clear()
21 |
22 |
23 | @pytest.fixture(scope="session")
24 | def ip_prefixes():
25 | return IpPrefixes(ip4_prefix="10.0.0.0/24")
26 |
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial4/tests/test_ping.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from core.emulator.data import IpPrefixes, LinkOptions
4 | from core.emulator.session import Session
5 | from core.errors import CoreCommandError
6 | from core.nodes.base import CoreNode
7 |
8 |
9 | class TestPing:
10 | def test_success(self, session: Session, ip_prefixes: IpPrefixes):
11 | # create nodes
12 | node1 = session.add_node(CoreNode)
13 | node2 = session.add_node(CoreNode)
14 |
15 | # link nodes together
16 | iface1_data = ip_prefixes.create_iface(node1)
17 | iface2_data = ip_prefixes.create_iface(node2)
18 | session.add_link(node1.id, node2.id, iface1_data, iface2_data)
19 |
20 | # ping node, expect a successful command
21 | node1.cmd(f"ping -c 1 {iface2_data.ip4}")
22 |
23 | def test_failure(self, session: Session, ip_prefixes: IpPrefixes):
24 | # create nodes
25 | node1 = session.add_node(CoreNode)
26 | node2 = session.add_node(CoreNode)
27 |
28 | # link nodes together
29 | iface1_data = ip_prefixes.create_iface(node1)
30 | iface2_data = ip_prefixes.create_iface(node2)
31 | options = LinkOptions(loss=100.0)
32 | session.add_link(node1.id, node2.id, iface1_data, iface2_data, options)
33 |
34 | # ping node, expect command to fail and raise exception due to 100% loss
35 | with pytest.raises(CoreCommandError):
36 | node1.cmd(f"ping -c 1 {iface2_data.ip4}")
37 |
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial5/client_for_windows.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import select
3 | import socket
4 | import sys
5 |
6 | DEFAULT_PORT: int = 9001
7 | READ_SIZE: int = 4096
8 |
9 |
10 | def prompt():
11 | sys.stdout.write(">> ")
12 | sys.stdout.flush()
13 |
14 |
15 | class ChatClient:
16 | def __init__(self, address, port):
17 | self.address = address
18 | self.port = port
19 |
20 | def run(self):
21 | server = socket.create_connection((self.address, self.port))
22 | sockname = server.getsockname()
23 | print(
24 | f"connected to server({self.address}:{self.port}) as "
25 | f"client({sockname[0]}:{sockname[1]})"
26 | )
27 | sockets = [server]
28 | prompt()
29 | try:
30 | while True:
31 | read_sockets, write_socket, error_socket = select.select(
32 | sockets, [], [], 10
33 | )
34 | for sock in read_sockets:
35 | if sock == server:
36 | message = server.recv(READ_SIZE)
37 | if not message:
38 | print("server closed")
39 | sys.exit(1)
40 | else:
41 | print("\x1b[2K\r", end="")
42 | print(message.decode().strip())
43 | prompt()
44 | # waiting for input
45 | message = input(".")
46 | server.sendall(f"{message}\n".encode())
47 | except KeyboardInterrupt:
48 | print("client exiting")
49 | server.close()
50 |
51 |
52 | def main():
53 | parser = argparse.ArgumentParser(
54 | description="chat app client",
55 | formatter_class=argparse.ArgumentDefaultsHelpFormatter,
56 | )
57 | parser.add_argument("-a", "--address", help="address to listen on", required=True)
58 | parser.add_argument(
59 | "-p", "--port", type=int, help="port to listen on", default=DEFAULT_PORT
60 | )
61 | args = parser.parse_args()
62 | client = ChatClient(args.address, args.port)
63 | client.run()
64 |
65 |
66 | if __name__ == "__main__":
67 | main()
68 |
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial5/scenario.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | from core.api.grpc import client, wrappers
4 | from core.api.grpc.wrappers import NodeType, Position
5 |
6 |
7 | def main():
8 | if len(sys.argv) != 2:
9 | print("usage core-python scenario.py ")
10 | exit()
11 |
12 | # interface helper
13 | iface_helper = client.InterfaceHelper(
14 | ip4_prefix="10.0.0.0/24",
15 | ip6_prefix="2001::/64",
16 | )
17 |
18 | # create grpc client and connect
19 | core = client.CoreGrpcClient()
20 | core.connect()
21 |
22 | # add session
23 | session = core.create_session()
24 |
25 | # create nodes
26 | position = Position(x=100, y=100)
27 | node1 = session.add_node(1, name="n1", position=position)
28 | position = Position(x=300, y=100)
29 | rj45 = session.add_node(2, name=sys.argv[1], _type=NodeType.RJ45, position=position)
30 |
31 | # create link
32 | iface1 = iface_helper.create_iface(node1.id, 0)
33 | iface1.ip4 = "10.0.0.20"
34 | iface1.ip6 = "2001::14"
35 | rj45_iface1 = wrappers.Interface(0)
36 | session.add_link(node1=node1, node2=rj45, iface1=iface1, iface2=rj45_iface1)
37 |
38 | # start session
39 | core.start_session(session)
40 |
41 |
42 | if __name__ == "__main__":
43 | main()
44 |
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial6/drone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/package/share/tutorials/tutorial6/drone.png
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial6/terrain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coreemu/core/a5cff7f99e02a2acf629e19c307926c62c818fd6/package/share/tutorials/tutorial6/terrain.png
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial7/scenario.py:
--------------------------------------------------------------------------------
1 | from core.api.grpc import client
2 | from core.api.grpc.wrappers import Position, NodeType
3 | from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
4 |
5 |
6 | def main():
7 | # interface helper
8 | iface_helper = client.InterfaceHelper(
9 | ip4_prefix="10.0.0.0/24",
10 | ip6_prefix="2001::/64",
11 | )
12 |
13 | # create grpc client and connect
14 | core = client.CoreGrpcClient()
15 | core.connect()
16 |
17 | # create session
18 | session = core.create_session()
19 |
20 | # create nodes
21 | position = Position(x=375, y=500)
22 | emane_net = session.add_node(
23 | _id=1,
24 | _type=NodeType.EMANE,
25 | name="emane1",
26 | position=position,
27 | emane=EmaneIeee80211abgModel.name,
28 | )
29 | position = Position(x=250, y=250)
30 | node2 = session.add_node(_id=2, model="mdr", name="n2", position=position)
31 | position = Position(x=500, y=250)
32 | node3 = session.add_node(_id=3, model="mdr", name="n3", position=position)
33 |
34 | # create links to emane
35 | node2_iface = iface_helper.create_iface(node_id=node2.id, iface_id=0)
36 | node2_iface.ip4 = "10.0.0.1"
37 | node2_iface.ip4_mask = 32
38 | node2_iface.ip6 = "2001::1"
39 | node2_iface.ip6_mask = 128
40 | session.add_link(node1=node2, node2=emane_net, iface1=node2_iface)
41 | node3_iface = iface_helper.create_iface(node_id=node3.id, iface_id=0)
42 | node3_iface.ip4 = "10.0.0.2"
43 | node3_iface.ip4_mask = 32
44 | node3_iface.ip6 = "2001::2"
45 | node3_iface.ip6_mask = 128
46 | session.add_link(node1=node3, node2=emane_net, iface1=node3_iface)
47 |
48 | # start session
49 | core.start_session(session=session)
50 |
51 |
52 | if __name__ == "__main__":
53 | main()
54 |
--------------------------------------------------------------------------------
/package/share/tutorials/tutorial7/scenario_service.py:
--------------------------------------------------------------------------------
1 | from core.api.grpc import client
2 | from core.api.grpc.wrappers import Position, NodeType
3 | from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
4 |
5 |
6 | def main():
7 | # interface helper
8 | iface_helper = client.InterfaceHelper(
9 | ip4_prefix="10.0.0.0/24",
10 | ip6_prefix="2001::/64",
11 | )
12 |
13 | # create grpc client and connect
14 | core = client.CoreGrpcClient()
15 | core.connect()
16 |
17 | # create session
18 | session = core.create_session()
19 |
20 | # create nodes
21 | position = Position(x=375, y=500)
22 | emane_net = session.add_node(
23 | _id=1,
24 | _type=NodeType.EMANE,
25 | name="emane1",
26 | position=position,
27 | emane=EmaneIeee80211abgModel.name,
28 | )
29 | position = Position(x=250, y=250)
30 | node2 = session.add_node(_id=2, model="mdr", name="n2", position=position)
31 | node2.services.add("ChatApp Server")
32 | position = Position(x=500, y=250)
33 | node3 = session.add_node(_id=3, model="mdr", name="n3", position=position)
34 |
35 | # create links to emane
36 | node2_iface = iface_helper.create_iface(node_id=node2.id, iface_id=0)
37 | node2_iface.ip4 = "10.0.0.1"
38 | node2_iface.ip4_mask = 32
39 | node2_iface.ip6 = "2001::1"
40 | node2_iface.ip6_mask = 128
41 | session.add_link(node1=node2, node2=emane_net, iface1=node2_iface)
42 | node3_iface = iface_helper.create_iface(node_id=node3.id, iface_id=0)
43 | node3_iface.ip4 = "10.0.0.2"
44 | node3_iface.ip4_mask = 32
45 | node3_iface.ip6 = "2001::2"
46 | node3_iface.ip6_mask = 128
47 | session.add_link(node1=node3, node2=emane_net, iface1=node3_iface)
48 |
49 | # start session
50 | core.start_session(session=session)
51 |
52 |
53 | if __name__ == "__main__":
54 | main()
55 |
--------------------------------------------------------------------------------
/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | PYTHON="${PYTHON:=python3}"
4 | PYTHON_DEP="${PYTHON_DEP:=python3}"
5 | PIPX_VERSION=1.7.1
6 | GRPC_VERSION=1.69.0
7 | INVOKE_VERSION=2.2.0
8 | POETRY_VERSION=1.2.1
9 |
10 | # install pre-reqs using yum/apt
11 | if [ -z "${NO_SYSTEM}" ]; then
12 | if command -v apt &> /dev/null
13 | then
14 | echo "setup to install CORE using apt"
15 | sudo apt install -y ${PYTHON_DEP}-pip ${PYTHON_DEP}-venv python3-pip
16 | ${PYTHON} -m venv venv
17 | . ./venv/bin/activate
18 | python -m pip install pipx==${PIPX_VERSION} grpcio==${GRPC_VERSION} grpcio-tools==${GRPC_VERSION}
19 | python -m pipx ensurepath
20 | python -m pipx install invoke==${INVOKE_VERSION}
21 | python -m pipx install poetry==${POETRY_VERSION}
22 | elif command -v yum &> /dev/null
23 | then
24 | echo "setup to install CORE using yum"
25 | sudo yum install -y ${PYTHON_DEP}-pip
26 | ${PYTHON} -m venv venv
27 | . ./venv/bin/activate
28 | python -m pip install pipx==${PIPX_VERSION} grpcio==${GRPC_VERSION} grpcio-tools==${GRPC_VERSION}
29 | python -m pipx ensurepath
30 | python -m pipx install invoke==${INVOKE_VERSION}
31 | python -m pipx install poetry==${POETRY_VERSION}
32 | else
33 | echo "apt/yum was not found"
34 | echo "install python3, pip, venv, pipx, and invoke to run the automated install"
35 | exit 1
36 | fi
37 | fi
38 |
--------------------------------------------------------------------------------