[go: up one dir, main page]

Skip to content

Commit

Permalink
simple daemon implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
jackhart committed May 21, 2023
1 parent af68c80 commit 7e88572
Show file tree
Hide file tree
Showing 14 changed files with 405 additions and 175 deletions.
2 changes: 1 addition & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ tasks:
desc: Run pytests.
deps: [ install, setup-network ]
cmds:
- sudo ip netns exec basic {{.USER_WORKING_DIR}}/venv/bin/python3 -m pytest {{.CLI_ARGS}}
- sudo ip netns exec basic {{.USER_WORKING_DIR}}/venv/bin/python3 -m pytest -s {{.CLI_ARGS}}

setup-network:
desc: Setup the network namespaces for testing.
Expand Down
4 changes: 3 additions & 1 deletion commands
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ export PYTHONPATH=/home/jack/Documents/projects/pygmp:$PYTHONPATH
source venv/bin/activate
python3 pygmp interactive

ip netns exec basic python3 -m pytest tests/test_simple.py

ip netns exec basic python3 pygmp smcrouted
ip netns exec basic ping -c 3 -W 1 -I a3 -t 2 239.0.0.4

ip netns exec basic ip mroute
Usage: add vif <index> <ttl threshold> <rate limit> <interface addr or index> <remote addr>
add vif 0 1 0 10.0.0.1 0.0.0.0
add vif 1 1 0 20.0.0.1 0.0.0.0
Expand Down
19 changes: 18 additions & 1 deletion network-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,19 @@ summary () {
basic () {
printf "\nCreating basic network...\n"
ip netns add basic

# setup veth pair for REST API access
ip link add veth0 type veth peer name veth1
ip link set veth1 netns basic
ip addr add 172.20.0.1/24 dev veth0
ip link set veth0 up
ip netns exec basic ip addr add 172.20.0.2/24 dev veth1
ip netns exec basic ip link set veth1 up

# setup iptables for veth pair
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 172.20.0.2:8000
ip netns exec basic iptables -t nat -A POSTROUTING -p tcp --sport 8000 -j MASQUERADE

ip netns exec basic ip link add a1 type dummy
ip netns exec basic ip link set a1 up
ip netns exec basic ip link set a1 multicast on
Expand All @@ -61,6 +74,8 @@ basic () {
ip netns exec basic ip addr add 20.0.0.1/24 dev a2
ip netns exec basic ip addr add 30.0.0.1/24 dev a3

ip netns exec basic ip link set lo up

summary basic

}
Expand All @@ -69,8 +84,10 @@ main () {

# a basc network setup =============================================
if [ "$OVERWRITE" == true ] && [ "$(ip netns list | grep -wc "basic")" -eq 1 ]; then
echo "Deleting existing basic namespace..."
echo "Deleting existing basic namespace and iptable rules..."
iptables -t nat -D PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 172.20.0.2:8000
ip netns delete basic
ip link del veth0
fi

[[ $(ip netns list | grep -wc "basic") -eq 0 ]] && {
Expand Down
13 changes: 8 additions & 5 deletions pygmp/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
# SOFTWARE.

import argparse

import kernel
from pygmp.daemons import interactive, simple
from fastapi import FastAPI
import uvicorn


def build_args():
Expand All @@ -41,10 +45,9 @@ def build_args():
return parser.parse_args()


def main():
args = build_args()
args.daemon(args)


if __name__ == "__main__":
main()
with kernel.igmp_socket() as sock:
args = build_args()
app = args.daemon(sock, args, FastAPI())
uvicorn.run(app, host="172.20.0.2", port=8000)
74 changes: 47 additions & 27 deletions pygmp/daemons/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@
from pygmp import kernel, data


_DEFAULT_SOURCE = ip_address("0.0.0.0")
_MROUTE_PREFIX = "mroute_"


@dataclass
class MRoute:
# TODO - support from address
from_: data.Interface
"""Represents a multicast route."""
from_: str
group: IPv4Address
to: list[data.Interface]
source: IPv4Address = ip_address("0.0.0.0")
to: dict[str, int]
source: IPv4Address = _DEFAULT_SOURCE


@dataclass
Expand All @@ -43,45 +47,45 @@ class Config:


def load_config(file_name: str) -> Config:

config = configparser.ConfigParser()
config.read(file_name)

phyints = _get_phyints(config)
mroutes = _get_mroutes(config, phyints)
# TODO - better config validation
phyints = _parse_phyints(config)
mroutes = _parse_mroutes(config)
return Config(phyint=phyints, mroute=mroutes)


def _get_mroutes(config_parser: configparser.ConfigParser, phyints: list[data.Interface]) -> list[MRoute]:
pyints_dict = {p.name: p for p in phyints}
def _parse_mroutes(config_parser: configparser.ConfigParser) -> list[MRoute]:
mroutes = []
for name in config_parser.sections():
if name.startswith("mroute_"):
ii = pyints_dict[config_parser.get(name, "from")]
oil = [pyints_dict[inf] for inf in _str_list(config_parser.get(name, "to"))]
group = _get_group_address(config_parser.get(name, "group"))
if name.startswith(_MROUTE_PREFIX):
outgoing_interface_dict = _parse_outgoing_map(config_parser.get(name, "to"))
group = _parse_group_address(config_parser.get(name, "group"))
source = ip_address(config_parser.get(name, "source", fallback="0.0.0.0"))
mroutes.append(MRoute(from_=ii, group=group, to=oil, source=source))

mroutes.append(MRoute(from_=config_parser.get(name, "from"), group=group,
to=outgoing_interface_dict, source=source))
return mroutes


def _get_group_address(group_address: str) -> IPv4Address:
group = ip_address(group_address)
# TODO - prefix len
if not group.is_multicast:
raise ValueError(f"invalid group address {group_address}")
def _parse_phyints(config_parser: configparser.ConfigParser):
"""Parse physical interfaces from the configuration."""
current_interfaces = kernel.network_interfaces()
names = _str_to_list(config_parser.get("phyints", "names", fallback=""))
return [_get_interface(current_interfaces, name) for name in names]

return group

def _parse_group_address(group_address: str) -> IPv4Address:
"""Validate and convert group address to IPv4Address object."""
group = ip_address(group_address) # TODO - prefix len support
if not group.is_multicast:
raise ValueError(f"Invalid group address {group_address}")

def _get_phyints(config_parser: configparser.ConfigParser):
current_interfaces = kernel.network_interfaces()
names = _str_list(config_parser.get("phyints", "names", fallback=""))
return [_get_interface(current_interfaces, name) for name in names]
return group


def _get_interface(interfaces, name):
"""Get interface by name and validate it."""
try:
interface = interfaces[name]
except KeyError:
Expand All @@ -96,5 +100,21 @@ def _get_interface(interfaces, name):
return interface


def _str_list(str_list: str) -> list[str]:
return [s.strip() for s in str_list.split(',')]
def _parse_outgoing_map(to: str) -> dict[str, int]:
return {inf: ttl for inf, ttl in
(_str_to_key_value(inf_raw) for inf_raw in _str_to_list(to))}


def _str_to_key_value(str_pair: str, default_value=1) -> tuple[str, int]:
"""Convert a key=value string pair to a tuple."""
parts = str_pair.split('=')
if not parts[0].strip():
raise ValueError(f"Invalid key: {str_pair}")

value = int(parts[1].strip()) if len(parts) > 1 and parts[1].strip().isdigit() else default_value
return parts[0].strip(), value


def _str_to_list(str_list: str) -> list[str]:
return [s.strip() for s in str_list.split(',')]

2 changes: 1 addition & 1 deletion pygmp/daemons/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
_logger = get_logger(__name__)


def main(args):
def main(args, app):

with kernel.igmp_socket() as sock:
_clean(sock)
Expand Down
Loading

0 comments on commit 7e88572

Please sign in to comment.