Merge branch 'python-module'

The pypackage version is ready for use by non-nerds
This commit is contained in:
Pranav Jerry 2021-07-23 21:43:00 +05:30
commit c8e98b2c4f
No known key found for this signature in database
GPG Key ID: F1DCDC4FED0A0C5B
14 changed files with 480 additions and 154 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
.DS_Store
.idea
*.log
tmp/
*.py[cod]
*.egg*
build
htmlcov
__pycache__

27
HACKING.md Normal file
View File

@ -0,0 +1,27 @@
# Hacking
Everyone <!-- including anti-nationals and urban naxals are -->
is welcome to [hack][] naxalnet. See below for how to hack.
## Reporting issues and suggesting ideas
To report a bug or suggest an idea, create a new issue at
<https://git.disroot.org/pranav/naxalnet/issues> with a
relevant label.
## Contribute code
To push to this repo, you need your username to be in the
contributors list.
To add you as a contributor, email any of the authors with
your username:
- `echo yvoervangbe cyhf akyarg ng qvfebbg qbg bet | tr 'A-Za-z' 'N-ZA-Mn-za-m' | sed 's/plus/+/' | sed 's/ at /@/' | sed 's/dot/./' | tr -d ' '`
## Packaging
naxalnet needs distro packages in Debian, Fedora, openSUSE,
and nixos. If you know/like to package it in your distro,
post to issue #6.
[hack]: https://catb.org/jargon/html/H/hack.html

4
MANIFEST.in Normal file
View File

@ -0,0 +1,4 @@
include LICENSE
include README.md
include naxalnet.service
include systemd-networkd/*

View File

@ -1,16 +1,8 @@
PREFIX := /usr
# This makefile uses setup.py under the hood
all: build
install: naxalnet
install -d $(DESTDIR)$(PREFIX)/bin
install -d $(DESTDIR)$(PREFIX)/lib/systemd/system/
install -m644 naxalnet.service $(DESTDIR)$(PREFIX)/lib/systemd/system/
install naxalnet $(DESTDIR)$(PREFIX)/bin/
install -d $(DESTDIR)$(PREFIX)/share/naxalnet/networkd
install -m644 systemd-networkd/* $(DESTDIR)$(PREFIX)/share/naxalnet/networkd
build:
python setup.py build
testdeps:
@for i in networkctl systemctl python3; do \
echo "Checking for $$i"; \
which $$i > /dev/null && echo " $$i found" || \
(echo " $$i not found"; exit 1); \
done
install: build
python setup.py install --root="$(DESTDIR)/" --optimize=1 --skip-build

View File

@ -33,6 +33,7 @@ or anyone else advocating for their rights).
no error then you already have it)
- iwd (for starting ad-hoc network)
- python3
- python-setuptools (for building)
- [python-dasbus][]
- wifi adapter with ad-hoc support
- two or more computers with wifi adapter, called nodes
@ -62,6 +63,14 @@ git clone https://git.disroot.org/pranav/naxalnet.git
cd naxalnet
```
<!--
Or, if you have an [IPFS client][ipfs] running, try:
```sh
git clone http://k51qzi5uqu5dlye74be0n9iihwk6sm54vexo7bf7pdr4w811y6mmrcp25djozv.ipns.localhost:8080/naxalnet.git
```
-->
Run `sudo make install` to install naxalnet. This will install naxalnet in
`/usr/bin/naxalnet`.
@ -152,12 +161,12 @@ and others which can work on an intranet.
## Uninstalling
If you installed naxalnet manually, there is now way to uninstall
If you installed naxalnet manually, there is no way to uninstall
than manually removing the files:
```sh
sudo rm -rf /usr/{bin,share}/naxalnet \
/usr/lib/systemd/system/naxalnet.service
sudo pip uninstall naxalnet
sudo rm -rf /usr/share/naxalnet* /usr/lib/systemd/system/naxalnet.service
```
## Similar projects
@ -181,8 +190,6 @@ the Free Software Foundation, either version 3 of the License, or
See [LICENSE](LICENSE) for the complete version of the
license.
This project is in alpha stage. Documentation is incomplete.
[batman-adv]: https://www.open-mesh.org/projects/batman-adv/wiki
[ipfs]: https://ipfs.io
[jami]: https://jami.net

134
naxalnet
View File

@ -1,134 +0,0 @@
#!/usr/bin/env python3
"""
Setup a working BATMAN Advanced network
with systemd-networkd and iwd
"""
# Copyright (C) 2021 The Authors
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
from pathlib import Path
from shutil import copy
from dasbus.connection import SystemMessageBus
from dasbus.error import DBusError
NETWORKD_CONFIGS = "/usr/share/naxalnet/networkd"
NETWORKD_VOLATILE_DIR = "/run/systemd/network"
ADHOC_SSID = "HelloWorld"
AP_SSID = "NaxalNet"
AP_PASSWD = "naxalnet256"
# Copy networkd configs to volatile dir.
# The D-Bus API does not support creating new interfaces
# or linking to bridges. So we use config files.
# See man:systemd.network(5)
try:
print("Copying network config files")
dest = Path(NETWORKD_VOLATILE_DIR)
src = Path(NETWORKD_CONFIGS)
# Create the volatile directory if it doesn't exist
dest.mkdir(parents=True, exist_ok=True)
# Copy all files in src to dest
for i in src.iterdir():
copy(i, dest)
except PermissionError as error:
print(error)
sys.exit("Make sure you are root")
# Now, the iwd part
try:
# connect to the System bus
bus = SystemMessageBus()
# iwd proxy
iwd = bus.get_proxy("net.connman.iwd", "/")
# Get list of all devices
print("Finding connected devices")
objects = iwd.GetManagedObjects()
# devices that support ad-hoc
adhoc_devices = []
# devices that support ap
ap_devices = []
for path, obj in objects.items():
if "net.connman.iwd.Device" in obj:
# add all devices to the list
name = obj["net.connman.iwd.Device"]["Name"]
print("Found device", name)
adapter_path = obj["net.connman.iwd.Device"]["Adapter"].get_string()
adapter = objects[adapter_path]["net.connman.iwd.Adapter"]
if "ad-hoc" in adapter["SupportedModes"]:
print(name, "supports ad-hoc")
adhoc_devices.append(path)
if "ap" in adapter["SupportedModes"]:
print(name, "supports ap")
ap_devices.append(path)
if len(adhoc_devices) != 0:
# Start ad-hoc on first device supporting ad-hoc
dev1path = adhoc_devices.pop()
# The same device is likely to have ap support too.
# But we can't start ad-hoc and ap on the same interface.
# Remove dev1 from ap_devices if it exists there
if dev1path in ap_devices:
ap_devices.remove(dev1path)
print("Working on ad-hoc")
dev1 = bus.get_proxy("net.connman.iwd", dev1path)
print("Starting ad-hoc on", dev1.Name)
if not dev1.Powered:
print("Device is off. Turning on")
dev1.Powered = True
if dev1.Mode != "ad-hoc":
print("Device is in", dev1.Mode)
print("Switching to ad-hoc")
dev1.Mode = "ad-hoc"
# Changing Mode needs connecting to the proxy again
dev1 = bus.get_proxy("net.connman.iwd", dev1path)
# If already connected to ad-hoc, stop it
if dev1.Started is True:
print("Already connected to ad-hoc. Stopping")
dev1.Stop()
# Reconnect to proxy or StartOpen won't work
dev1 = bus.get_proxy("net.connman.iwd", dev1path)
print("Starting ad-hoc network")
dev1.StartOpen(ADHOC_SSID)
# Start Access point if ap_device is not empty,
# ie, we have more devices
if len(ap_devices) != 0:
print("Working on AP")
dev2path = ap_devices.pop()
dev2 = bus.get_proxy("net.connman.iwd", dev2path)
if not dev1.Powered:
print("Device is off. Turning on")
dev1.Powered = True
if dev2.Mode != "ap":
print(dev2.Name, "is in", dev2.Mode)
print("Switching to ap")
dev2.Mode = "ap"
dev2 = bus.get_proxy("net.connman.iwd", dev2path)
if dev2.Started is True:
print("An AP is already started on", dev2.Name)
print("Stopping")
dev2.Stop()
dev2 = bus.get_proxy("net.connman.iwd", dev2path)
print("Starting AP on", dev2.Name)
dev2.Start(AP_SSID, AP_PASSWD)
except DBusError:
sys.exit("An error occured while communicating with iwd")
print("Bye")

3
naxalnet/__init__.py Normal file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env python3
__version__ = "0.1.0a"

6
naxalnet/__main__.py Normal file
View File

@ -0,0 +1,6 @@
#!/usr/bin/env python3
from naxalnet.scripts import here_be_dragons
if __name__ == "__main__":
here_be_dragons()

273
naxalnet/iwd.py Normal file
View File

@ -0,0 +1,273 @@
#!/usr/bin/env python3
# Copyright (C) 2021 The naxalnet Authors
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Manage wifi adapter via iwd D-Bus api"""
from dasbus.connection import SystemMessageBus
IWD_BUS = "net.connman.iwd"
IWD_ROOT_PATH = "/"
IWD_DEVICE_INTERFACE = "net.connman.iwd.Device"
IWD_ADAPTER_INTERFACE = "net.connman.iwd.Adapter"
# If you are new to D-Bus, you might want to use a program
# such as D-Feet (https://wiki.gnome.org/Apps/DFeet) for reference.
# And try out iwctl to understand iwd's bus objects
class IWD:
"""Manage iwd via dbus"""
def __init__(self, bus=SystemMessageBus()):
# self._bus and self._proxy are meant for use only in this file
self._bus = bus
self.reload()
def reload(self):
"""reload the proxy"""
self._proxy = self._bus.get_proxy(IWD_BUS, IWD_ROOT_PATH)
def get_name_from_path(self, path: str) -> str:
"""
returns device or adapter name when d-bus path is given as arg
"""
proxy = self._bus.get_proxy(IWD_BUS, path)
return proxy.Name
def get_device_path_from_name(self, name: str) -> str:
"""returns path of device as str"""
device_paths = self.get_all_device_paths()
for i in device_paths:
proxy = self._bus.get_proxy(IWD_BUS, i)
if proxy.Name == name:
return i
# If no devices were found, return None
return None
def get_adapter_path_from_name(self, name: str) -> str:
"""returns path of adapter as str"""
adapter_paths = self.get_all_adapter_paths()
for i in adapter_paths:
proxy = self._bus.get_proxy(IWD_BUS, i)
if proxy.Name == name:
return i
# If no adapters were found
return None
def get_all_device_paths(self) -> list:
"""returns list of paths of all devices"""
objects = self._proxy.GetManagedObjects()
paths = []
for key, value in objects.items():
# if value is a device, add its path to paths
if IWD_DEVICE_INTERFACE in value:
paths.append(key)
return paths
def get_all_adapter_paths(self) -> list:
"""returns list of paths of all adapters"""
objects = self._proxy.GetManagedObjects()
paths = []
for key, value in objects.items():
# if value is an adapter, add its path to paths
if IWD_ADAPTER_INTERFACE in value:
paths.append(key)
return paths
def get_devices(self) -> list:
"""
returns list of device names as str
example: ["wlan0", "wlan1"]
"""
devices = []
device_paths = self.get_all_device_paths()
for i in device_paths:
name = self.get_name_from_path(i)
devices.append(name)
return devices
def get_adapters(self) -> list:
"""
returns list of adapters
example: ["phy0","phy1"]
"""
adapters = []
adapter_paths = self.get_all_adapter_paths()
for i in adapter_paths:
name = self.get_name_from_path(i)
adapters.append(name)
return adapters
class Device:
"""
control devices with iwd
name: name of device (str)
adapter: name of adapter (str)
"""
def __init__(self, name: str, bus=SystemMessageBus()):
self._iwd = IWD(bus)
self._bus = self._iwd._bus
self._path = self._iwd.get_device_path_from_name(name)
self.reload()
def __str__(self):
return self.name
def is_powered_on(self) -> bool:
"""returns True if devie is powered on"""
return self._proxy.Powered
def power_on(self):
"""Turn on the device and reload the proxy"""
self._proxy.Powered = True
self.reload()
def power_off(self):
"""Turn off the device and reload the proxy"""
self._proxy.Powered = False
self.reload()
def reload(self):
"""reload the proxy after changing mode"""
self._proxy = self._bus.get_proxy(IWD_BUS, self._path)
self.name = self._proxy.Name
adapter_path = self._proxy.Adapter
# name of adapter ('phy0' for example)
self.adapter = self._iwd.get_name_from_path(adapter_path)
def is_adhoc_started(self):
"""
Returns True if an adhoc network is started on this device.
Returns None if device is not powered on or not in ad-hoc mode.
"""
if self.is_powered_on() and self.get_mode() == "ad-hoc":
return self._proxy.Started
# If above condition is not true, return None
return None
def is_ap_started(self):
"""
Same as is_adhoc_started(), but for ap
"""
if self.is_powered_on() and self.get_mode() == "ap":
return self._proxy.Started
return None
def get_mode(self) -> str:
"""
returns the mode in which the device is in
example: "ap"
"""
return self._proxy.Mode
def set_mode(self, mode: str):
"""change the device mode to mode"""
self._proxy.Mode = mode
self.reload()
def start_adhoc_open(self, name: str):
"""
Create ad-hoc network with name, changing mode to ad-hoc
if it isn't already on ad-hoc and power onn the device
if it is off
"""
if self.get_mode() != "ad-hoc":
self.set_mode("ad-hoc")
if not self.is_powered_on():
self.power_on()
# Stop adhoc if already started
self.stop_adhoc()
self._proxy.StartOpen(name)
def stop_adhoc(self):
"""stop adhoc if adhoc is started"""
if self.is_adhoc_started():
self._proxy.Stop()
self.reload()
def start_ap(self, ssid, passwd):
"""
Create ap network, changing mode to ap
if it isn't already on ap and turning
on the device if it is off
"""
if self.get_mode() != "ap":
self.set_mode("ap")
if not self.is_powered_on():
self.power_on()
# Stop ap if already started
self.stop_ap()
self._proxy.Start(ssid, passwd)
def stop_ap(self):
"""stop ap if an ap is started"""
if self.is_ap_started():
self._proxy.Stop()
self.reload()
class Adapter:
"""represents an adapter as a python object"""
def __init__(self, name: str, bus=SystemMessageBus()):
self._iwd = IWD(bus)
self._bus = self._iwd._bus
self._path = self._iwd.get_adapter_path_from_name(name)
# Initialise self._proxy
self.reload()
def __str__(self):
return self.name
def reload(self):
"""reload the proxy after changing mode"""
self._proxy = self._bus.get_proxy(IWD_BUS, self._path)
self.name = self._proxy.Name
self.supported_modes = self._proxy.SupportedModes
self.model = self._proxy.Model
self.vendor = self._proxy.Vendor
def is_powered_on(self) -> bool:
"""returns True if adapter is powered on, False otherwise"""
return self._proxy.Powered
def power_on(self):
"""power on the adapter"""
self._proxy.Powered = True
def power_off(self):
"""power off the adapter"""
self._proxy.Powered = False
def supports_mode(self, mode: str) -> bool:
"""
Returns True if the adapter supports the mode.
mode can be "ad-hoc", "ap" or "station"
"""
return mode in self.supported_modes

94
naxalnet/scripts.py Normal file
View File

@ -0,0 +1,94 @@
#!/usr/bin/env python3
# Copyright (C) 2021 The naxalnet Authors
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Setup a working BATMAN Advanced network
with systemd-networkd and iwd
"""
import sys
from pathlib import Path
from shutil import copy
# from dasbus.connection import SystemMessageBus
from dasbus.error import DBusError
from naxalnet.iwd import IWD, Device, Adapter
NETWORKD_CONFIGS = "/usr/share/naxalnet/networkd"
NETWORKD_VOLATILE_DIR = "/run/systemd/network"
ADHOC_SSID = "HelloWorld"
AP_SSID = "NaxalNet"
AP_PASSWD = "naxalnet256"
def here_be_dragons():
# Copy networkd configs to volatile dir.
# The D-Bus API does not support creating new interfaces
# or linking to bridges. So we use config files.
# See man:systemd.network(5)
try:
print("Copying network config files")
dest = Path(NETWORKD_VOLATILE_DIR)
src = Path(NETWORKD_CONFIGS)
# Create the volatile directory if it doesn't exist
dest.mkdir(parents=True, exist_ok=True)
# Copy all files in src to dest
for i in src.iterdir():
copy(i, dest)
except PermissionError as error:
print(error)
sys.exit("Make sure you are root")
# Now, the iwd part
try:
iwd = IWD()
devices = iwd.get_devices()
adhoc_devices = []
ap_devices = []
for i in devices:
d = Device(i)
a = Adapter(d.adapter)
if a.supports_mode("ad-hoc"):
adhoc_devices.append(i)
if a.supports_mode("ap"):
ap_devices.append(i)
if len(adhoc_devices) != 0:
# Start ad-hoc on first device supporting ad-hoc
adhoc_device = Device(adhoc_devices.pop())
# The same device is likely to have ap support too.
# But we can't start ad-hoc and ap on the same interface.
# Remove dev1 from ap_devices if it exists there
if adhoc_device.name in ap_devices:
ap_devices.remove(adhoc_device.name)
print("Working on ad-hoc")
adhoc_device.start_adhoc_open(ADHOC_SSID)
# Start Access point if ap_device is not empty,
# ie, we have more devices
if len(ap_devices) != 0:
print("Working on AP")
ap_device = Device(ap_devices.pop())
ap_device.start_ap(AP_SSID, AP_PASSWD)
except DBusError as error:
print(error)
sys.exit("An error occured while communicating with iwd")
print("Bye")

36
setup.cfg Normal file
View File

@ -0,0 +1,36 @@
[metadata]
name = naxalnet
version = attr: naxalnet.__version__
description = create mesh networks with batman-adv and systemd
long_description = file: README.md, LICENSE
url = https://git.disroot.org/pranav/naxalnet
author = Pranav Jerry
author_email = libreinator@disroot.org
license = GPLv3
classifiers =
License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Operating System :: POSIX :: Linux
Programming Language :: Python :: 3 :: Only
[options]
include_package_data = true
packages = find:
python_requires = >=3.6
install_requires =
dasbus
[options.entry_points]
console_scripts =
naxalnet = naxalnet.scripts:here_be_dragons
[options.data_files]
/usr/lib/systemd/system =
naxalnet.service
/usr/share/naxalnet/networkd =
systemd-networkd/01-batman.netdev
systemd-networkd/02-bridge.netdev
systemd-networkd/03-wireless-ad-hoc.network
systemd-networkd/04-batman.network
systemd-networkd/05-wireless-ap.network
systemd-networkd/06-eth.network
systemd-networkd/07-bridge.network

5
setup.py Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env python3
from setuptools import setup
setup()

View File

@ -1,5 +1,7 @@
# This file links any interface in ap mode
# to the bridge we created earlier
# to the bridge we created earlier.
# To start an AP, connect two adapters to the computer
# before starting naxalnet.service
# This file won't do anything if an ap interface is not found.
[Match]

View File

@ -2,6 +2,7 @@
# to the bridge made in 02-bridge.netdev
[Match]
Name=en*
Name=eth*
[Network]
Bridge=bridge0