Skip to content

Commit

Permalink
Configuration file support
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrofran12 committed Jul 31, 2020
1 parent 3896b38 commit 0cdd2a7
Show file tree
Hide file tree
Showing 8 changed files with 370 additions and 11 deletions.
39 changes: 34 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ If `-uvrf` is not defined, the default unicast table id will be used (table id 2
After starting the protocol process, if the default multicast table is not used, the following commands (for adding interfaces and listing state) need to have the argument `-mvrf` defined to specify the corresponding daemon process.



#### Multi daemon support

Multiple daemons are supported, each bind to a given multicast routing table id.

To perform configurations on one of these daemons use `-mvrf` command and define the daemon by its multicast table id.


To see all daemons that are currently running:

```
sudo pim-dm -instances
```

#### Add interface

After starting the protocol process you can enable the protocol in specific interfaces. You need to specify which interfaces will have IGMP enabled and which interfaces will have PIM-DM enabled.
Expand Down Expand Up @@ -149,18 +163,33 @@ We have built some list commands that can be used to check the "internals" of th
sudo pim-dm -mr [-4 | -6] [-mvrf MULTICAST_TABLE_ID]
```

## Config File

## Help command
In order to determine which commands and corresponding arguments are available you can call the help command:
It is possible to configure the protocol using a YAML file. This configuration file can be used to set all interfaces that will have PIM-DM/IGMP/MLD enabled, as well to fine tune these protocols by setting their timers. Currently the settings are shared by all interfaces. In a future release it will be possible to set timers per interface.

To use this feature you need to manually install PyYaml. PyYaml is not automatically installed with `pim-dm` to support older Python versions (as of now PyYaml requires at least Python v3.5).

[This YAML file](https://github.com/pedrofran12/pim_dm/tree/master/config/config_example.yml) is a configuration file example.

It it also possible to get an YAML configuration file from the current settings of the daemon. This will output an YAML template that can be used later for enabling the daemon with the same settings (enabled interfaces and timers). The command for this matter is the following:

```
pim-dm -h
sudo pim-dm -get_config [-mvrf MULTICAST_TABLE_ID]
```

To input an YAML configuration file to the daemon:

```
sudo pim-dm -config CONFIGURATION_FILE_PATH
```


## Change settings
## Help command
In order to determine which commands and corresponding arguments are available you can call the help command:

Files tree/pim_globals.py, igmp/igmp_globals.py and mld/mld_globals.py store all timer values and some configurations regarding PIM-DM, IGMP and MLD. If you want to tune the implementation, you can change the values of these files. These configurations are used by all interfaces, meaning that there is no tuning per interface.
```
pim-dm -h
```


## Tests
Expand Down
72 changes: 72 additions & 0 deletions config/config_example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
MulticastVRF: 0
UnicastVRF: 254

PIM-DM:
DefaultTimers:
ASSERT_TIME: 180
GRAFT_RETRY_PERIOD: 3
JP_OVERRIDE_INTERVAL: 3.0
OVERRIDE_INTERVAL: 2.5
PROPAGATION_DELAY: 0.5
REFRESH_INTERVAL: 60
SOURCE_LIFETIME: 210
T_LIMIT: 210
Interfaces:
eth0:
ipv4:
enabled: true
ipv6:
enabled: true
eth1:
ipv4:
enabled: true
ipv6:
enabled: true
eth2:
ipv4:
enabled: true
ipv6:
enabled: true

IGMP:
Settings:
GROUP_MEMBERSHIP_INTERVAL: 260
LAST_MEMBER_QUERY_COUNT: 2
LAST_MEMBER_QUERY_INTERVAL: 1
MAX_RESPONSE_TIME_LAST_MEMBER_QUERY_INTERVAL: 10
MAX_RESPONSE_TIME_QUERY_RESPONSE_INTERVAL: 100
OTHER_QUERIER_PRESENT_INTERVAL: 255.0
QUERY_INTERVAL: 125
QUERY_RESPONSE_INTERVAL: 10
ROBUSTNESS_VARIABLE: 2
STARTUP_QUERY_COUNT: 2
STARTUP_QUERY_INTERVAL: 31.25
UNSOLICITED_REPORT_INTERVAL: 10
VERSION_1_ROUTER_PRESENT_TIMEOUT: 400
Interfaces:
eth0:
enabled: true
eth1:
enabled: true
eth2:
enabled: true

MLD:
Settings:
LAST_LISTENER_QUERY_COUNT: 2
LAST_LISTENER_QUERY_INTERVAL: 1
MULTICAST_LISTENER_INTERVAL: 260
OTHER_QUERIER_PRESENT_INTERVAL: 255.0
QUERY_INTERVAL: 125
QUERY_RESPONSE_INTERVAL: 10
ROBUSTNESS_VARIABLE: 2
STARTUP_QUERY_COUNT: 2
STARTUP_QUERY_INTERVAL: 31.25
UNSOLICITED_REPORT_INTERVAL: 10
Interfaces:
eth0:
enabled: true
eth1:
enabled: true
eth2:
enabled: true
186 changes: 186 additions & 0 deletions pimdm/Config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import yaml
from pimdm.tree import pim_globals
from pimdm.igmp import igmp_globals
from pimdm.mld import mld_globals
from pimdm import Main


def parse_config_file(file_path):
"""
Parse yaml file and set everything on protocol process accordingly
"""
with open(file_path) as f:
data = yaml.load(f, Loader=yaml.FullLoader)
print(data)

print(type(data.get("UnicastVRF", 254)))

multicast_vrf = data.get("MulticastVRF", 0)
pim_globals.MULTICAST_TABLE_ID = multicast_vrf
pim_globals.UNICAST_TABLE_ID = data.get("UnicastVRF", 254)
pim_config = data.get("PIM-DM", {})
igmp_config = data.get("IGMP", {})
mld_config = data.get("MLD", {})

##### PIM config ######
if "DefaultTimers" in pim_config:
pim_globals.ASSERT_TIME = pim_config["DefaultTimers"].get("ASSERT_TIME", pim_globals.ASSERT_TIME)
pim_globals.GRAFT_RETRY_PERIOD = pim_config["DefaultTimers"].get("GRAFT_RETRY_PERIOD", pim_globals.GRAFT_RETRY_PERIOD)
pim_globals.JP_OVERRIDE_INTERVAL = pim_config["DefaultTimers"].get("JP_OVERRIDE_INTERVAL", pim_globals.JP_OVERRIDE_INTERVAL)
pim_globals.OVERRIDE_INTERVAL = pim_config["DefaultTimers"].get("OVERRIDE_INTERVAL", pim_globals.OVERRIDE_INTERVAL)
pim_globals.PROPAGATION_DELAY = pim_config["DefaultTimers"].get("PROPAGATION_DELAY", pim_globals.PROPAGATION_DELAY)
pim_globals.REFRESH_INTERVAL = pim_config["DefaultTimers"].get("REFRESH_INTERVAL", pim_globals.REFRESH_INTERVAL)
pim_globals.SOURCE_LIFETIME = pim_config["DefaultTimers"].get("SOURCE_LIFETIME", pim_globals.SOURCE_LIFETIME)
pim_globals.T_LIMIT = pim_config["DefaultTimers"].get("T_LIMIT", pim_globals.T_LIMIT)

if "Interfaces" in pim_config:
interfaces = pim_config["Interfaces"] # type: dict

for if_name, if_value in interfaces.items():
if if_value.get("ipv4", False):
if_ipv4 = if_value["ipv4"]
if if_ipv4.get("enabled", False):
Main.add_pim_interface(interface_name=if_name, ipv4=True, ipv6=False)

if if_value.get("ipv6", False):
if_ipv6 = if_value["ipv6"]
if if_ipv6.get("enabled", False):
Main.add_pim_interface(interface_name=if_name, ipv4=False, ipv6=True)


##### IGMP config #######
if "Settings" in igmp_config:
igmp_globals.ROBUSTNESS_VARIABLE = igmp_config["Settings"].get("ROBUSTNESS_VARIABLE", igmp_globals.ROBUSTNESS_VARIABLE)
igmp_globals.QUERY_INTERVAL = igmp_config["Settings"].get("QUERY_INTERVAL", igmp_globals.QUERY_INTERVAL)
igmp_globals.QUERY_RESPONSE_INTERVAL = igmp_config["Settings"].get("QUERY_RESPONSE_INTERVAL", igmp_globals.QUERY_RESPONSE_INTERVAL)
igmp_globals.MAX_RESPONSE_TIME_QUERY_RESPONSE_INTERVAL = igmp_config["Settings"].get("MAX_RESPONSE_TIME_QUERY_RESPONSE_INTERVAL", igmp_globals.QUERY_RESPONSE_INTERVAL*10)
igmp_globals.GROUP_MEMBERSHIP_INTERVAL = igmp_config["Settings"].get("GROUP_MEMBERSHIP_INTERVAL", igmp_globals.ROBUSTNESS_VARIABLE * igmp_globals.QUERY_INTERVAL + igmp_globals.QUERY_RESPONSE_INTERVAL)
igmp_globals.OTHER_QUERIER_PRESENT_INTERVAL = igmp_config["Settings"].get("OTHER_QUERIER_PRESENT_INTERVAL", igmp_globals.ROBUSTNESS_VARIABLE * igmp_globals.QUERY_INTERVAL + igmp_globals.QUERY_RESPONSE_INTERVAL / 2)
igmp_globals.STARTUP_QUERY_INTERVAL = igmp_config["Settings"].get("STARTUP_QUERY_INTERVAL", igmp_globals.QUERY_INTERVAL / 4)
igmp_globals.STARTUP_QUERY_COUNT = igmp_config["Settings"].get("STARTUP_QUERY_COUNT", igmp_globals.ROBUSTNESS_VARIABLE)
igmp_globals.LAST_MEMBER_QUERY_INTERVAL = igmp_config["Settings"].get("LAST_MEMBER_QUERY_INTERVAL", igmp_globals.LAST_MEMBER_QUERY_INTERVAL)
igmp_globals.MAX_RESPONSE_TIME_LAST_MEMBER_QUERY_INTERVAL = igmp_config["Settings"].get("LAST_MEMBER_QUERY_COUNT", igmp_globals.LAST_MEMBER_QUERY_INTERVAL * 10)
igmp_globals.LAST_MEMBER_QUERY_COUNT = igmp_config["Settings"].get("LAST_MEMBER_QUERY_COUNT", igmp_globals.ROBUSTNESS_VARIABLE)
igmp_globals.UNSOLICITED_REPORT_INTERVAL = igmp_config["Settings"].get("UNSOLICITED_REPORT_INTERVAL", igmp_globals.UNSOLICITED_REPORT_INTERVAL)
igmp_globals.VERSION_1_ROUTER_PRESENT_TIMEOUT = igmp_config["Settings"].get("VERSION_1_ROUTER_PRESENT_TIMEOUT", igmp_globals.VERSION_1_ROUTER_PRESENT_TIMEOUT)

if "Interfaces" in igmp_config:
interfaces = igmp_config["Interfaces"] # type: dict

for if_name, if_value in interfaces.items():
if if_value.get("enabled", False):
Main.add_membership_interface(interface_name=if_name, ipv4=True, ipv6=False)

##### MLD config #######
if "Settings" in mld_config:
mld_globals.ROBUSTNESS_VARIABLE = mld_config["Settings"].get("ROBUSTNESS_VARIABLE", mld_globals.ROBUSTNESS_VARIABLE)
mld_globals.QUERY_INTERVAL = mld_config["Settings"].get("QUERY_INTERVAL", mld_globals.QUERY_INTERVAL)
mld_globals.QUERY_RESPONSE_INTERVAL = mld_config["Settings"].get("QUERY_RESPONSE_INTERVAL", mld_globals.QUERY_RESPONSE_INTERVAL)
mld_globals.MULTICAST_LISTENER_INTERVAL = mld_config["Settings"].get("MULTICAST_LISTENER_INTERVAL", (mld_globals.ROBUSTNESS_VARIABLE * mld_globals.QUERY_INTERVAL) + (mld_globals.QUERY_RESPONSE_INTERVAL))
mld_globals.OTHER_QUERIER_PRESENT_INTERVAL = mld_config["Settings"].get("OTHER_QUERIER_PRESENT_INTERVAL", (mld_globals.ROBUSTNESS_VARIABLE * mld_globals.QUERY_INTERVAL) + 0.5 * mld_globals.QUERY_RESPONSE_INTERVAL)
mld_globals.STARTUP_QUERY_INTERVAL = mld_config["Settings"].get("STARTUP_QUERY_INTERVAL", (1 / 4) * mld_globals.QUERY_INTERVAL)
mld_globals.STARTUP_QUERY_COUNT = mld_config["Settings"].get("STARTUP_QUERY_COUNT", mld_globals.ROBUSTNESS_VARIABLE)
mld_globals.LAST_LISTENER_QUERY_INTERVAL = mld_config["Settings"].get("LAST_LISTENER_QUERY_INTERVAL", mld_globals.LAST_LISTENER_QUERY_INTERVAL)
mld_globals.LAST_LISTENER_QUERY_COUNT = mld_config["Settings"].get("LAST_LISTENER_QUERY_COUNT", mld_globals.ROBUSTNESS_VARIABLE)
mld_globals.UNSOLICITED_REPORT_INTERVAL = mld_config["Settings"].get("UNSOLICITED_REPORT_INTERVAL", mld_globals.UNSOLICITED_REPORT_INTERVAL)

if "Interfaces" in mld_config:
interfaces = mld_config["Interfaces"] # type: dict

for if_name, if_value in interfaces.items():
if if_value.get("enabled", False):
Main.add_membership_interface(interface_name=if_name, ipv4=False, ipv6=True)


def get_yaml_file():
"""
Get configuration file from live protocol process
"""
dict_file = {
'MulticastVRF': pim_globals.MULTICAST_TABLE_ID,
'UnicastVRF': pim_globals.UNICAST_TABLE_ID,
'PIM-DM': {
"DefaultTimers": {
"ASSERT_TIME": pim_globals.ASSERT_TIME,
"GRAFT_RETRY_PERIOD": pim_globals.GRAFT_RETRY_PERIOD,
"JP_OVERRIDE_INTERVAL": pim_globals.JP_OVERRIDE_INTERVAL,
"OVERRIDE_INTERVAL": pim_globals.OVERRIDE_INTERVAL,
"PROPAGATION_DELAY": pim_globals.PROPAGATION_DELAY,
"REFRESH_INTERVAL": pim_globals.REFRESH_INTERVAL,
"SOURCE_LIFETIME": pim_globals.SOURCE_LIFETIME,
"T_LIMIT": pim_globals.T_LIMIT,
},
"Interfaces": {},
},
'IGMP': {
"Settings": {
"ROBUSTNESS_VARIABLE": igmp_globals.ROBUSTNESS_VARIABLE,
"QUERY_INTERVAL": igmp_globals.QUERY_INTERVAL,
"QUERY_RESPONSE_INTERVAL": igmp_globals.QUERY_RESPONSE_INTERVAL,
"MAX_RESPONSE_TIME_QUERY_RESPONSE_INTERVAL": igmp_globals.MAX_RESPONSE_TIME_QUERY_RESPONSE_INTERVAL,
"GROUP_MEMBERSHIP_INTERVAL": igmp_globals.GROUP_MEMBERSHIP_INTERVAL,
"OTHER_QUERIER_PRESENT_INTERVAL": igmp_globals.OTHER_QUERIER_PRESENT_INTERVAL,
"STARTUP_QUERY_INTERVAL": igmp_globals.STARTUP_QUERY_INTERVAL,
"STARTUP_QUERY_COUNT": igmp_globals.STARTUP_QUERY_COUNT,
"LAST_MEMBER_QUERY_INTERVAL": igmp_globals.LAST_MEMBER_QUERY_INTERVAL,
"MAX_RESPONSE_TIME_LAST_MEMBER_QUERY_INTERVAL": igmp_globals.MAX_RESPONSE_TIME_LAST_MEMBER_QUERY_INTERVAL,
"LAST_MEMBER_QUERY_COUNT": igmp_globals.LAST_MEMBER_QUERY_COUNT,
"UNSOLICITED_REPORT_INTERVAL": igmp_globals.UNSOLICITED_REPORT_INTERVAL,
"VERSION_1_ROUTER_PRESENT_TIMEOUT": igmp_globals.VERSION_1_ROUTER_PRESENT_TIMEOUT,
},
"Interfaces": {},
},
'MLD': {
"Settings": {
"ROBUSTNESS_VARIABLE": mld_globals.ROBUSTNESS_VARIABLE,
"QUERY_INTERVAL": mld_globals.QUERY_INTERVAL,
"QUERY_RESPONSE_INTERVAL": mld_globals.QUERY_RESPONSE_INTERVAL,
"MULTICAST_LISTENER_INTERVAL": mld_globals.MULTICAST_LISTENER_INTERVAL,
"OTHER_QUERIER_PRESENT_INTERVAL": mld_globals.OTHER_QUERIER_PRESENT_INTERVAL,
"STARTUP_QUERY_INTERVAL": mld_globals.STARTUP_QUERY_INTERVAL,
"STARTUP_QUERY_COUNT": mld_globals.STARTUP_QUERY_COUNT,
"LAST_LISTENER_QUERY_INTERVAL": mld_globals.LAST_LISTENER_QUERY_INTERVAL,
"LAST_LISTENER_QUERY_COUNT": mld_globals.LAST_LISTENER_QUERY_COUNT,
"UNSOLICITED_REPORT_INTERVAL": mld_globals.UNSOLICITED_REPORT_INTERVAL,
},
"Interfaces": {},
}
}

for if_name, if_value in Main.interfaces.items():
dict_file["PIM-DM"]["Interfaces"][if_name] = {}
dict_file["PIM-DM"]["Interfaces"][if_name]["ipv4"] = {
"enabled": True,
}

for if_name, if_value in Main.interfaces_v6.items():
if if_name not in dict_file["PIM-DM"]["Interfaces"]:
dict_file["PIM-DM"]["Interfaces"][if_name] = {}

dict_file["PIM-DM"]["Interfaces"][if_name]["ipv6"] = {
"enabled": True,
}

for if_name in Main.igmp_interfaces.keys():
dict_file["IGMP"]["Interfaces"][if_name] = {
"enabled": True,
}

for if_name in Main.mld_interfaces.keys():
dict_file["MLD"]["Interfaces"][if_name] = {
"enabled": True,
}

return yaml.dump(dict_file)


def get_vrfs(file_path):
"""
Get vrf configuration from yaml file.
This is only used by Run.py to create the correct daemons accordingly (daemons are bound to specific VRFs).
"""
with open(file_path) as f:
data = yaml.load(f, Loader=yaml.FullLoader)
multicast_vrf = data.get("MulticastVRF", 0)
unicast_vrf = data.get("UnicastVRF", 254)
return [multicast_vrf, unicast_vrf]
6 changes: 6 additions & 0 deletions pimdm/InterfacePIM.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ def __init__(self, interface_name: str, vif_index:int, state_refresh_capable:boo
# don't receive outgoing packets
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 0)

#self.drop_packet_type = None

super().__init__(interface_name, s, s, vif_index)
super()._enable()
self.force_send_hello()
Expand Down Expand Up @@ -107,6 +109,10 @@ def send(self, data: bytes, group_ip: str=MCAST_GRP):
"""
Send a new packet destined to group_ip IP
"""
#if self.drop_packet_type is not None and data.payload.get_pim_type() == self.drop_packet_type:
# self.drop_packet_type = None
# return

super().send(data=data, group_ip=group_ip)

#Random interval for initial Hello message on bootup or triggered Hello message to a rebooting neighbor
Expand Down
27 changes: 27 additions & 0 deletions pimdm/Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,33 @@ def test(router_name, server_logger_ip):
logger.addHandler(socketHandler)


def get_config():
"""
Get live configuration of PIM-DM process
"""
try:
from . import Config
return Config.get_yaml_file()
except ModuleNotFoundError:
return "PYYAML needs to be installed. Execute \"pip3 install pyyaml\""


def set_config(file_path):
"""
Set configuration of PIM-DM process
"""
from . import Config
try:
Config.parse_config_file(file_path)
except:
import traceback
traceback.print_exc()


def drop(interface_name, packet_type):
interfaces.get(interface_name).drop_packet_type = packet_type


def enable_ipv6_kernel():
"""
Function to explicitly enable IPv6 Multicast Routing stack.
Expand Down

0 comments on commit 0cdd2a7

Please sign in to comment.