├── .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 | ![](static/architecture.png) 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 | ![](static/workflow.png) 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 | --------------------------------------------------------------------------------