diff --git a/pimdm/InterfaceIGMP.py b/pimdm/InterfaceIGMP.py index bf9b6fc..887706b 100644 --- a/pimdm/InterfaceIGMP.py +++ b/pimdm/InterfaceIGMP.py @@ -5,7 +5,8 @@ import netifaces from pimdm.Interface import Interface from pimdm.packet.ReceivedPacket import ReceivedPacket -from pimdm.igmp.igmp_globals import Version_1_Membership_Report, Version_2_Membership_Report, Leave_Group, Membership_Query +from pimdm.igmp.igmp_globals import VERSION_1_MEMBERSHIP_REPORT, VERSION_2_MEMBERSHIP_REPORT, LEAVE_GROUP, \ + MEMBERSHIP_QUERY if not hasattr(socket, 'SO_BINDTODEVICE'): socket.SO_BINDTODEVICE = 25 @@ -101,10 +102,10 @@ def receive_unknown_type(self, packet): return PKT_FUNCTIONS = { - Version_1_Membership_Report: receive_version_1_membership_report, - Version_2_Membership_Report: receive_version_2_membership_report, - Leave_Group: receive_leave_group, - Membership_Query: receive_membership_query, + VERSION_1_MEMBERSHIP_REPORT: receive_version_1_membership_report, + VERSION_2_MEMBERSHIP_REPORT: receive_version_2_membership_report, + LEAVE_GROUP: receive_leave_group, + MEMBERSHIP_QUERY: receive_membership_query, } ################## diff --git a/pimdm/Kernel.py b/pimdm/Kernel.py index ccefe41..bf3840c 100644 --- a/pimdm/Kernel.py +++ b/pimdm/Kernel.py @@ -8,6 +8,7 @@ from pimdm import UnicastRouting, Main from pimdm.rwlock.RWLock import RWLockWrite +from pimdm.tree.globals import MULTICAST_TABLE_ID from pimdm.InterfaceMLD import InterfaceMLD from pimdm.InterfaceIGMP import InterfaceIGMP @@ -276,6 +277,13 @@ class Kernel4(Kernel): def __init__(self): s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IGMP) + # MRT TABLE + if MULTICAST_TABLE_ID != 0: + try: + s.setsockopt(socket.IPPROTO_IP, self.MRT_TABLE, MULTICAST_TABLE_ID) + except: + traceback.print_exc() + # MRT INIT s.setsockopt(socket.IPPROTO_IP, self.MRT_INIT, 1) @@ -513,6 +521,13 @@ class Kernel6(Kernel): def __init__(self): s = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_ICMPV6) + # MRT TABLE + if MULTICAST_TABLE_ID != 0: + try: + s.setsockopt(socket.IPPROTO_IPV6, self.MRT6_TABLE, MULTICAST_TABLE_ID) + except: + traceback.print_exc() + # MRT INIT s.setsockopt(socket.IPPROTO_IPV6, self.MRT6_INIT, 1) diff --git a/pimdm/Run.py b/pimdm/Run.py index 74451b5..66172aa 100644 --- a/pimdm/Run.py +++ b/pimdm/Run.py @@ -9,8 +9,9 @@ from pimdm import Main from pimdm.daemon.Daemon import Daemon +from pimdm.tree.globals import MULTICAST_TABLE_ID -VERSION = "1.1" +VERSION = "1.1.1" def client_socket(data_to_send): @@ -162,9 +163,9 @@ def main(): sys.exit(0) elif args.multicast_routes: if args.ipv4 or not args.ipv6: - os.system("ip mroute show") + os.system("ip mroute show table " + str(MULTICAST_TABLE_ID)) elif args.ipv6: - os.system("ip -6 mroute show") + os.system("ip -6 mroute show table " + str(MULTICAST_TABLE_ID)) sys.exit(0) elif not daemon.is_running(): print("PIM-DM is not running") diff --git a/pimdm/UnicastRouting.py b/pimdm/UnicastRouting.py index 4cbc0f9..160027e 100644 --- a/pimdm/UnicastRouting.py +++ b/pimdm/UnicastRouting.py @@ -3,6 +3,7 @@ from threading import RLock from socket import if_indextoname from pyroute2 import IPDB +from pimdm.tree.globals import UNICAST_TABLE_ID def get_route(ip_dst: str): @@ -64,17 +65,18 @@ def get_route(ip_dst: str): dst_network = str(ipaddress.ip_interface(ip_dst + "/" + str(mask_len)).network) print(dst_network) - if dst_network in ipdb.routes: + if dst_network in ipdb.routes.tables[UNICAST_TABLE_ID]: print(info) - if ipdb.routes[{'dst': dst_network, 'family': family}]['ipdb_scope'] != 'gc': - info = ipdb.routes[dst_network] + if ipdb.routes[{'dst': dst_network, 'family': family, + 'table': UNICAST_TABLE_ID}]['ipdb_scope'] != 'gc': + info = ipdb.routes[{'dst': dst_network, 'family': family, 'table': UNICAST_TABLE_ID}] break else: continue if not info: print("0.0.0.0/0 or ::/0") - if "default" in ipdb.routes: - info = ipdb.routes[{'dst': 'default', 'family': family}] + if "default" in ipdb.routes.tables[UNICAST_TABLE_ID]: + info = ipdb.routes[{'dst': 'default', 'family': family, 'table': UNICAST_TABLE_ID}] print(info) return info diff --git a/pimdm/igmp/GroupState.py b/pimdm/igmp/GroupState.py index f135e88..5fb30b1 100644 --- a/pimdm/igmp/GroupState.py +++ b/pimdm/igmp/GroupState.py @@ -4,7 +4,7 @@ from pimdm.utils import TYPE_CHECKING from .wrapper import NoMembersPresent -from .igmp_globals import GroupMembershipInterval, LastMemberQueryInterval +from .igmp_globals import GROUP_MEMBERSHIP_INTERVAL, LAST_MEMBER_QUERY_INTERVAL if TYPE_CHECKING: from .RouterState import RouterState @@ -40,16 +40,22 @@ def print_state(self): # Set state ########################################### def set_state(self, state): + """ + Set membership state regarding this Group + """ self.state = state self.group_state_logger.debug("change membership state to: " + state.print_state()) ########################################### # Set timers ########################################### - def set_timer(self, alternative: bool=False, max_response_time: int=None): + def set_timer(self, alternative: bool = False, max_response_time: int = None): + """ + Set timer + """ self.clear_timer() if not alternative: - time = GroupMembershipInterval + time = GROUP_MEMBERSHIP_INTERVAL else: time = self.router_state.interface_state.get_group_membership_time(max_response_time) @@ -58,48 +64,74 @@ def set_timer(self, alternative: bool=False, max_response_time: int=None): self.timer = timer def clear_timer(self): + """ + Stop timer + """ if self.timer is not None: self.timer.cancel() def set_v1_host_timer(self): + """ + Set v1 host timer + """ self.clear_v1_host_timer() - v1_host_timer = Timer(GroupMembershipInterval, self.group_membership_v1_timeout) + v1_host_timer = Timer(GROUP_MEMBERSHIP_INTERVAL, self.group_membership_v1_timeout) v1_host_timer.start() self.v1_host_timer = v1_host_timer def clear_v1_host_timer(self): + """ + Stop v1 host timer + """ if self.v1_host_timer is not None: self.v1_host_timer.cancel() def set_retransmit_timer(self): + """ + Set retransmit timer + """ self.clear_retransmit_timer() - retransmit_timer = Timer(LastMemberQueryInterval, self.retransmit_timeout) + retransmit_timer = Timer(LAST_MEMBER_QUERY_INTERVAL, self.retransmit_timeout) retransmit_timer.start() self.retransmit_timer = retransmit_timer def clear_retransmit_timer(self): + """ + Stop retransmit timer + """ if self.retransmit_timer is not None: self.retransmit_timer.cancel() - ########################################### # Get group state from specific interface state ########################################### def get_interface_group_state(self): + """ + Get specific implementation of the membership state machine (depending on the querier state machine) + """ return self.state.get_state(self.router_state) ########################################### # Timer timeout ########################################### def group_membership_timeout(self): + """ + Timer has expired + """ with self.lock: self.get_interface_group_state().group_membership_timeout(self) def group_membership_v1_timeout(self): + """ + v1 host timer has expired + """ with self.lock: self.get_interface_group_state().group_membership_v1_timeout(self) def retransmit_timeout(self): + """ + Retransmit timer has expired + """ with self.lock: self.get_interface_group_state().retransmit_timeout(self) @@ -107,18 +139,30 @@ def retransmit_timeout(self): # Receive Packets ########################################### def receive_v1_membership_report(self): + """ + Received IGMP Version 1 Membership Report packet regarding this group + """ with self.lock: self.get_interface_group_state().receive_v1_membership_report(self) def receive_v2_membership_report(self): + """ + Received IGMP Membership Report packet regarding this group + """ with self.lock: self.get_interface_group_state().receive_v2_membership_report(self) def receive_leave_group(self): + """ + Received IGMP Leave packet regarding this group + """ with self.lock: self.get_interface_group_state().receive_leave_group(self) def receive_group_specific_query(self, max_response_time: int): + """ + Received IGMP Group Specific Query packet regarding this group + """ with self.lock: self.get_interface_group_state().receive_group_specific_query(self, max_response_time) @@ -126,30 +170,52 @@ def receive_group_specific_query(self, max_response_time: int): # Notify Routing ########################################### def notify_routing_add(self): + """ + Notify all tree entries that IGMP considers to have hosts interested in this group + """ with self.multicast_interface_state_lock: print("notify+", self.multicast_interface_state) for interface_state in self.multicast_interface_state: interface_state.notify_membership(has_members=True) def notify_routing_remove(self): + """ + Notify all tree entries that IGMP considers to have not hosts interested in this group + """ with self.multicast_interface_state_lock: print("notify-", self.multicast_interface_state) for interface_state in self.multicast_interface_state: interface_state.notify_membership(has_members=False) def add_multicast_routing_entry(self, kernel_entry): + """ + A new tree is being monitored by the multicast routing protocol that has the same group + IGMP will store these entries in order to accelerate the notification process regarding changes in IGMP state + """ with self.multicast_interface_state_lock: self.multicast_interface_state.append(kernel_entry) return self.has_members() def remove_multicast_routing_entry(self, kernel_entry): + """ + A tree is no longer being monitored by the multicast routing protocol + Remove this tree from this object + """ with self.multicast_interface_state_lock: self.multicast_interface_state.remove(kernel_entry) def has_members(self): + """ + Determine if IGMP considers to have hosts interested in receiving data packets + """ return self.state is not NoMembersPresent def remove(self): + """ + Remove this group from the IGMP process + Notify all trees that this group no longer considers to be connected to hosts + This method will be invoked whenever an IGMP interface is removed + """ with self.multicast_interface_state_lock: self.clear_retransmit_timer() self.clear_timer() diff --git a/pimdm/igmp/RouterState.py b/pimdm/igmp/RouterState.py index f86345c..bff57b5 100644 --- a/pimdm/igmp/RouterState.py +++ b/pimdm/igmp/RouterState.py @@ -8,7 +8,7 @@ from .querier.Querier import Querier from .nonquerier.NonQuerier import NonQuerier from .GroupState import GroupState -from .igmp_globals import Membership_Query, QueryResponseInterval, QueryInterval, OtherQuerierPresentInterval +from .igmp_globals import MEMBERSHIP_QUERY, QUERY_RESPONSE_INTERVAL, QUERY_INTERVAL, OTHER_QUERIER_PRESENT_INTERVAL if TYPE_CHECKING: from pimdm.InterfaceIGMP import InterfaceIGMP @@ -36,11 +36,11 @@ def __init__(self, interface: 'InterfaceIGMP'): self.group_state_lock = RWLockWrite() # send general query - packet = PacketIGMPHeader(type=Membership_Query, max_resp_time=QueryResponseInterval*10) + packet = PacketIGMPHeader(type=MEMBERSHIP_QUERY, max_resp_time=QUERY_RESPONSE_INTERVAL * 10) self.interface.send(packet.bytes()) # set initial general query timer - timer = Timer(QueryInterval, self.general_query_timeout) + timer = Timer(QUERY_INTERVAL, self.general_query_timeout) timer.start() self.general_query_timer = timer @@ -58,32 +58,53 @@ def print_state(self): return self.interface_state.state_name() def set_general_query_timer(self): + """ + Set general query timer + """ self.clear_general_query_timer() - general_query_timer = Timer(QueryInterval, self.general_query_timeout) + general_query_timer = Timer(QUERY_INTERVAL, self.general_query_timeout) general_query_timer.start() self.general_query_timer = general_query_timer def clear_general_query_timer(self): + """ + Stop general query timer + """ if self.general_query_timer is not None: self.general_query_timer.cancel() def set_other_querier_present_timer(self): + """ + Set other querier present timer + """ self.clear_other_querier_present_timer() - other_querier_present_timer = Timer(OtherQuerierPresentInterval, self.other_querier_present_timeout) + other_querier_present_timer = Timer(OTHER_QUERIER_PRESENT_INTERVAL, self.other_querier_present_timeout) other_querier_present_timer.start() self.other_querier_present_timer = other_querier_present_timer def clear_other_querier_present_timer(self): + """ + Stop other querier present timer + """ if self.other_querier_present_timer is not None: self.other_querier_present_timer.cancel() def general_query_timeout(self): + """ + General Query timer has expired + """ self.interface_state.general_query_timeout(self) def other_querier_present_timeout(self): + """ + Other Querier Present timer has expired + """ self.interface_state.other_querier_present_timeout(self) def change_interface_state(self, querier: bool): + """ + Change state regarding querier state machine (Querier/NonQuerier) + """ if querier: self.interface_state = Querier self.router_state_logger.debug('change querier state to -> Querier') @@ -95,6 +116,9 @@ def change_interface_state(self, querier: bool): # group state methods ############################################ def get_group_state(self, group_ip): + """ + Get object that monitors a given group (with group_ip IP address) + """ with self.group_state_lock.genRlock(): if group_ip in self.group_state: return self.group_state[group_ip] @@ -108,38 +132,42 @@ def get_group_state(self, group_ip): return group_state def receive_v1_membership_report(self, packet: ReceivedPacket): + """ + Received IGMP Version 1 Membership Report packet + """ igmp_group = packet.payload.group_address - #if igmp_group not in self.group_state: - # self.group_state[igmp_group] = GroupState(self, igmp_group) - - #self.group_state[igmp_group].receive_v1_membership_report() self.get_group_state(igmp_group).receive_v1_membership_report() def receive_v2_membership_report(self, packet: ReceivedPacket): + """ + Received IGMP Membership Report packet + """ igmp_group = packet.payload.group_address - #if igmp_group not in self.group_state: - # self.group_state[igmp_group] = GroupState(self, igmp_group) - - #self.group_state[igmp_group].receive_v2_membership_report() self.get_group_state(igmp_group).receive_v2_membership_report() def receive_leave_group(self, packet: ReceivedPacket): + """ + Received IGMP Leave packet + """ igmp_group = packet.payload.group_address - #if igmp_group in self.group_state: - # self.group_state[igmp_group].receive_leave_group() self.get_group_state(igmp_group).receive_leave_group() def receive_query(self, packet: ReceivedPacket): + """ + Received IGMP Query packet + """ self.interface_state.receive_query(self, packet) igmp_group = packet.payload.group_address # process group specific query if igmp_group != "0.0.0.0" and igmp_group in self.group_state: - #if igmp_group != "0.0.0.0": max_response_time = packet.payload.max_resp_time - #self.group_state[igmp_group].receive_group_specific_query(max_response_time) self.get_group_state(igmp_group).receive_group_specific_query(max_response_time) def remove(self): + """ + Remove this IGMP interface + Clear all state + """ for group in self.group_state.values(): - group.remove() \ No newline at end of file + group.remove() diff --git a/pimdm/igmp/igmp_globals.py b/pimdm/igmp/igmp_globals.py index 924c38f..18c5f66 100644 --- a/pimdm/igmp/igmp_globals.py +++ b/pimdm/igmp/igmp_globals.py @@ -1,20 +1,20 @@ # IGMP timers (in seconds) -RobustnessVariable = 2 -QueryInterval = 125 -QueryResponseInterval = 10 -MaxResponseTime_QueryResponseInterval = QueryResponseInterval*10 -GroupMembershipInterval = RobustnessVariable * QueryInterval + QueryResponseInterval -OtherQuerierPresentInterval = RobustnessVariable * QueryInterval + QueryResponseInterval/2 -StartupQueryInterval = QueryInterval / 4 -StartupQueryCount = RobustnessVariable -LastMemberQueryInterval = 1 -MaxResponseTime_LastMemberQueryInterval = LastMemberQueryInterval*10 -LastMemberQueryCount = RobustnessVariable -UnsolicitedReportInterval = 10 -Version1RouterPresentTimeout = 400 +ROBUSTNESS_VARIABLE = 2 +QUERY_INTERVAL = 125 +QUERY_RESPONSE_INTERVAL = 10 +MAX_RESPONSE_TIME_QUERY_RESPONSE_INTERVAL = QUERY_RESPONSE_INTERVAL * 10 +GROUP_MEMBERSHIP_INTERVAL = ROBUSTNESS_VARIABLE * QUERY_INTERVAL + QUERY_RESPONSE_INTERVAL +OTHER_QUERIER_PRESENT_INTERVAL = ROBUSTNESS_VARIABLE * QUERY_INTERVAL + QUERY_RESPONSE_INTERVAL / 2 +STARTUP_QUERY_INTERVAL = QUERY_INTERVAL / 4 +STARTUP_QUERY_COUNT = ROBUSTNESS_VARIABLE +LAST_MEMBER_QUERY_INTERVAL = 1 +MAX_RESPONSE_TIME_LAST_MEMBER_QUERY_INTERVAL = LAST_MEMBER_QUERY_INTERVAL * 10 +LAST_MEMBER_QUERY_COUNT = ROBUSTNESS_VARIABLE +UNSOLICITED_REPORT_INTERVAL = 10 +VERSION_1_ROUTER_PRESENT_TIMEOUT = 400 # IGMP msg type -Membership_Query = 0x11 -Version_1_Membership_Report = 0x12 -Version_2_Membership_Report = 0x16 -Leave_Group = 0x17 \ No newline at end of file +MEMBERSHIP_QUERY = 0x11 +VERSION_1_MEMBERSHIP_REPORT = 0x12 +VERSION_2_MEMBERSHIP_REPORT = 0x16 +LEAVE_GROUP = 0x17 diff --git a/pimdm/igmp/nonquerier/CheckingMembership.py b/pimdm/igmp/nonquerier/CheckingMembership.py index 62c0b82..fb24322 100644 --- a/pimdm/igmp/nonquerier/CheckingMembership.py +++ b/pimdm/igmp/nonquerier/CheckingMembership.py @@ -7,6 +7,9 @@ def group_membership_timeout(group_state: 'GroupState'): + """ + timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier CheckingMembership: group_membership_timeout') group_state.set_state(NoMembersPresent) @@ -15,35 +18,53 @@ def group_membership_timeout(group_state: 'GroupState'): def group_membership_v1_timeout(group_state: 'GroupState'): + """ + v1 host timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier CheckingMembership: group_membership_v1_timeout') # do nothing return def retransmit_timeout(group_state: 'GroupState'): + """ + retransmit timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier CheckingMembership: retransmit_timeout') # do nothing return def receive_v1_membership_report(group_state: 'GroupState'): + """ + Received IGMP Version 1 Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier CheckingMembership: receive_v1_membership_report') receive_v2_membership_report(group_state) def receive_v2_membership_report(group_state: 'GroupState'): + """ + Received IGMP Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier CheckingMembership: receive_v2_membership_report') group_state.set_timer() group_state.set_state(MembersPresent) def receive_leave_group(group_state: 'GroupState'): + """ + Received IGMP Leave packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier CheckingMembership: receive_leave_group') # do nothing return def receive_group_specific_query(group_state: 'GroupState', max_response_time: int): + """ + Received IGMP Group Specific Query packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier CheckingMembership: receive_group_specific_query') # do nothing return diff --git a/pimdm/igmp/nonquerier/MembersPresent.py b/pimdm/igmp/nonquerier/MembersPresent.py index f51e9b3..619cb2f 100644 --- a/pimdm/igmp/nonquerier/MembersPresent.py +++ b/pimdm/igmp/nonquerier/MembersPresent.py @@ -7,6 +7,9 @@ def group_membership_timeout(group_state: 'GroupState'): + """ + timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier MembersPresent: group_membership_timeout') group_state.set_state(NoMembersPresent) @@ -15,34 +18,52 @@ def group_membership_timeout(group_state: 'GroupState'): def group_membership_v1_timeout(group_state: 'GroupState'): + """ + v1 host timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier MembersPresent: group_membership_v1_timeout') # do nothing return def retransmit_timeout(group_state: 'GroupState'): + """ + retransmit timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier MembersPresent: retransmit_timeout') # do nothing return def receive_v1_membership_report(group_state: 'GroupState'): + """ + Received IGMP Version 1 Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier MembersPresent: receive_v1_membership_report') receive_v2_membership_report(group_state) def receive_v2_membership_report(group_state: 'GroupState'): + """ + Received IGMP Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier MembersPresent: receive_v2_membership_report') group_state.set_timer() def receive_leave_group(group_state: 'GroupState'): + """ + Received IGMP Leave packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier MembersPresent: receive_leave_group') # do nothing return def receive_group_specific_query(group_state: 'GroupState', max_response_time: int): + """ + Received IGMP Group Specific Query packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier MembersPresent: receive_group_specific_query') group_state.set_timer(alternative=True, max_response_time=max_response_time) group_state.set_state(CheckingMembership) diff --git a/pimdm/igmp/nonquerier/NoMembersPresent.py b/pimdm/igmp/nonquerier/NoMembersPresent.py index 704bdb8..40f01fa 100644 --- a/pimdm/igmp/nonquerier/NoMembersPresent.py +++ b/pimdm/igmp/nonquerier/NoMembersPresent.py @@ -6,29 +6,44 @@ def group_membership_timeout(group_state: 'GroupState'): + """ + timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier NoMembersPresent: group_membership_timeout') # do nothing return def group_membership_v1_timeout(group_state: 'GroupState'): + """ + v1 host timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier NoMembersPresent: group_membership_v1_timeout') # do nothing return def retransmit_timeout(group_state: 'GroupState'): + """ + retransmit timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier NoMembersPresent: retransmit_timeout') # do nothing return def receive_v1_membership_report(group_state: 'GroupState'): + """ + Received IGMP Version 1 Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier NoMembersPresent: receive_v1_membership_report') receive_v2_membership_report(group_state) def receive_v2_membership_report(group_state: 'GroupState'): + """ + Received IGMP Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier NoMembersPresent: receive_v2_membership_report') group_state.set_timer() group_state.set_state(MembersPresent) @@ -38,12 +53,18 @@ def receive_v2_membership_report(group_state: 'GroupState'): def receive_leave_group(group_state: 'GroupState'): + """ + Received IGMP Leave packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier NoMembersPresent: receive_leave_group') # do nothing return def receive_group_specific_query(group_state: 'GroupState', max_response_time: int): + """ + Received IGMP Group Specific Query packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier NoMembersPresent: receive_group_specific_query') # do nothing return diff --git a/pimdm/igmp/nonquerier/NonQuerier.py b/pimdm/igmp/nonquerier/NonQuerier.py index 90ab892..0fa4bd1 100644 --- a/pimdm/igmp/nonquerier/NonQuerier.py +++ b/pimdm/igmp/nonquerier/NonQuerier.py @@ -1,6 +1,6 @@ from ipaddress import IPv4Address from pimdm.utils import TYPE_CHECKING -from ..igmp_globals import Membership_Query, QueryResponseInterval, LastMemberQueryCount +from pimdm.igmp.igmp_globals import MEMBERSHIP_QUERY, QUERY_RESPONSE_INTERVAL, LAST_MEMBER_QUERY_COUNT from pimdm.packet.PacketIGMPHeader import PacketIGMPHeader from pimdm.packet.ReceivedPacket import ReceivedPacket from . import NoMembersPresent, MembersPresent, CheckingMembership @@ -13,18 +13,24 @@ class NonQuerier: @staticmethod def general_query_timeout(router_state: 'RouterState'): + """ + General Query timer has expired + """ router_state.router_state_logger.debug('NonQuerier state: general_query_timeout') # do nothing return @staticmethod def other_querier_present_timeout(router_state: 'RouterState'): + """ + Other Query Present timer has expired + """ router_state.router_state_logger.debug('NonQuerier state: other_querier_present_timeout') #change state to Querier router_state.change_interface_state(querier=True) # send general query - packet = PacketIGMPHeader(type=Membership_Query, max_resp_time=QueryResponseInterval*10) + packet = PacketIGMPHeader(type=MEMBERSHIP_QUERY, max_resp_time=QUERY_RESPONSE_INTERVAL * 10) router_state.interface.send(packet.bytes()) # set general query timer @@ -32,6 +38,9 @@ def other_querier_present_timeout(router_state: 'RouterState'): @staticmethod def receive_query(router_state: 'RouterState', packet: ReceivedPacket): + """ + Interface associated with RouterState is NonQuerier and received a Query packet + """ router_state.router_state_logger.debug('NonQuerier state: receive_query') source_ip = packet.ip_header.ip_src @@ -49,21 +58,37 @@ def state_name(): @staticmethod def get_group_membership_time(max_response_time: int): - return (max_response_time/10.0) * LastMemberQueryCount + """ + Get time to set timer* + """ + return (max_response_time/10.0) * LAST_MEMBER_QUERY_COUNT # State @staticmethod def get_checking_membership_state(): + """ + Get implementation of CheckingMembership state machine of interface in NonQuerier state + """ return CheckingMembership @staticmethod def get_members_present_state(): + """ + Get implementation of MembersPresent state machine of interface in NonQuerier state + """ return MembersPresent @staticmethod def get_no_members_present_state(): + """ + Get implementation of NoMembersPresent state machine of interface in NonQuerier state + """ return NoMembersPresent @staticmethod def get_version_1_members_present_state(): + """ + Get implementation of Version1MembersPresent state machine of interface in NonQuerier state + This will return implementation of MembersPresent state machine + """ return NonQuerier.get_members_present_state() diff --git a/pimdm/igmp/querier/CheckingMembership.py b/pimdm/igmp/querier/CheckingMembership.py index bcb82c7..ee0141a 100644 --- a/pimdm/igmp/querier/CheckingMembership.py +++ b/pimdm/igmp/querier/CheckingMembership.py @@ -1,12 +1,15 @@ from pimdm.packet.PacketIGMPHeader import PacketIGMPHeader from pimdm.utils import TYPE_CHECKING -from ..igmp_globals import Membership_Query, LastMemberQueryInterval +from pimdm.igmp.igmp_globals import MEMBERSHIP_QUERY, LAST_MEMBER_QUERY_INTERVAL from ..wrapper import NoMembersPresent, MembersPresent, Version1MembersPresent if TYPE_CHECKING: from ..GroupState import GroupState def group_membership_timeout(group_state: 'GroupState'): + """ + timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier CheckingMembership: group_membership_timeout') group_state.clear_retransmit_timer() group_state.set_state(NoMembersPresent) @@ -16,21 +19,31 @@ def group_membership_timeout(group_state: 'GroupState'): def group_membership_v1_timeout(group_state: 'GroupState'): + """ + v1 host timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier CheckingMembership: group_membership_v1_timeout') # do nothing return def retransmit_timeout(group_state: 'GroupState'): + """ + retransmit timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier CheckingMembership: retransmit_timeout') group_addr = group_state.group_ip - packet = PacketIGMPHeader(type=Membership_Query, max_resp_time=LastMemberQueryInterval*10, group_address=group_addr) + packet = PacketIGMPHeader(type=MEMBERSHIP_QUERY, max_resp_time=LAST_MEMBER_QUERY_INTERVAL * 10, + group_address=group_addr) group_state.router_state.send(data=packet.bytes(), address=group_addr) group_state.set_retransmit_timer() def receive_v1_membership_report(group_state: 'GroupState'): + """ + Received IGMP Version 1 Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier CheckingMembership: receive_v1_membership_report') group_state.set_timer() group_state.set_v1_host_timer() @@ -38,18 +51,27 @@ def receive_v1_membership_report(group_state: 'GroupState'): def receive_v2_membership_report(group_state: 'GroupState'): + """ + Received IGMP Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier CheckingMembership: receive_v2_membership_report') group_state.set_timer() group_state.set_state(MembersPresent) def receive_leave_group(group_state: 'GroupState'): + """ + Received IGMP Leave packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier CheckingMembership: receive_leave_group') # do nothing return def receive_group_specific_query(group_state: 'GroupState', max_response_time: int): + """ + Received IGMP Group Specific Query packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier CheckingMembership: receive_group_specific_query') # do nothing return diff --git a/pimdm/igmp/querier/MembersPresent.py b/pimdm/igmp/querier/MembersPresent.py index 279ea89..3b0e9cb 100644 --- a/pimdm/igmp/querier/MembersPresent.py +++ b/pimdm/igmp/querier/MembersPresent.py @@ -1,12 +1,15 @@ -from pimdm.packet.PacketIGMPHeader import PacketIGMPHeader from pimdm.utils import TYPE_CHECKING -from ..igmp_globals import Membership_Query, LastMemberQueryInterval +from pimdm.packet.PacketIGMPHeader import PacketIGMPHeader +from pimdm.igmp.igmp_globals import MEMBERSHIP_QUERY, LAST_MEMBER_QUERY_INTERVAL from ..wrapper import Version1MembersPresent, CheckingMembership, NoMembersPresent if TYPE_CHECKING: from ..GroupState import GroupState def group_membership_timeout(group_state: 'GroupState'): + """ + timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier MembersPresent: group_membership_timeout') group_state.set_state(NoMembersPresent) @@ -15,18 +18,27 @@ def group_membership_timeout(group_state: 'GroupState'): def group_membership_v1_timeout(group_state: 'GroupState'): + """ + v1 host timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier MembersPresent: group_membership_v1_timeout') # do nothing return def retransmit_timeout(group_state: 'GroupState'): + """ + retransmit timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier MembersPresent: retransmit_timeout') # do nothing return def receive_v1_membership_report(group_state: 'GroupState'): + """ + Received IGMP Version 1 Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier MembersPresent: receive_v1_membership_report') group_state.set_timer() group_state.set_v1_host_timer() @@ -34,24 +46,34 @@ def receive_v1_membership_report(group_state: 'GroupState'): def receive_v2_membership_report(group_state: 'GroupState'): + """ + Received IGMP Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier MembersPresent: receive_v2_membership_report') group_state.set_timer() def receive_leave_group(group_state: 'GroupState'): + """ + Received IGMP Leave packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier MembersPresent: receive_leave_group') group_ip = group_state.group_ip group_state.set_timer(alternative=True) group_state.set_retransmit_timer() - packet = PacketIGMPHeader(type=Membership_Query, max_resp_time=LastMemberQueryInterval*10, group_address=group_ip) + packet = PacketIGMPHeader(type=MEMBERSHIP_QUERY, max_resp_time=LAST_MEMBER_QUERY_INTERVAL * 10, + group_address=group_ip) group_state.router_state.send(data=packet.bytes(), address=group_ip) group_state.set_state(CheckingMembership) def receive_group_specific_query(group_state: 'GroupState', max_response_time): + """ + Received IGMP Group Specific Query packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier MembersPresent: receive_group_specific_query') # do nothing return diff --git a/pimdm/igmp/querier/NoMembersPresent.py b/pimdm/igmp/querier/NoMembersPresent.py index 4176301..d453776 100644 --- a/pimdm/igmp/querier/NoMembersPresent.py +++ b/pimdm/igmp/querier/NoMembersPresent.py @@ -6,24 +6,36 @@ def group_membership_timeout(group_state: 'GroupState'): + """ + timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier NoMembersPresent: group_membership_timeout') # do nothing return def group_membership_v1_timeout(group_state: 'GroupState'): + """ + v1 host timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier NoMembersPresent: group_membership_v1_timeout') # do nothing return def retransmit_timeout(group_state: 'GroupState'): + """ + retransmit timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier NoMembersPresent: retransmit_timeout') # do nothing return def receive_v1_membership_report(group_state: 'GroupState'): + """ + Received IGMP Version 1 Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier NoMembersPresent: receive_v1_membership_report') group_state.set_timer() group_state.set_v1_host_timer() @@ -34,6 +46,9 @@ def receive_v1_membership_report(group_state: 'GroupState'): def receive_v2_membership_report(group_state: 'GroupState'): + """ + Received IGMP Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier NoMembersPresent: receive_v2_membership_report') group_state.set_timer() group_state.set_state(MembersPresent) @@ -43,12 +58,18 @@ def receive_v2_membership_report(group_state: 'GroupState'): def receive_leave_group(group_state: 'GroupState'): + """ + Received IGMP Leave packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier NoMembersPresent: receive_leave_group') # do nothing return def receive_group_specific_query(group_state: 'GroupState', max_response_time: int): + """ + Received IGMP Group Specific Query packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier NoMembersPresent: receive_group_specific_query') # do nothing return diff --git a/pimdm/igmp/querier/Querier.py b/pimdm/igmp/querier/Querier.py index 1f72913..b22b285 100644 --- a/pimdm/igmp/querier/Querier.py +++ b/pimdm/igmp/querier/Querier.py @@ -1,7 +1,8 @@ from ipaddress import IPv4Address from pimdm.utils import TYPE_CHECKING -from ..igmp_globals import Membership_Query, QueryResponseInterval, LastMemberQueryCount, LastMemberQueryInterval +from ..igmp_globals import MEMBERSHIP_QUERY, QUERY_RESPONSE_INTERVAL, LAST_MEMBER_QUERY_COUNT, \ + LAST_MEMBER_QUERY_INTERVAL from pimdm.packet.PacketIGMPHeader import PacketIGMPHeader from pimdm.packet.ReceivedPacket import ReceivedPacket @@ -14,9 +15,12 @@ class Querier: @staticmethod def general_query_timeout(router_state: 'RouterState'): + """ + General Query timer has expired + """ router_state.router_state_logger.debug('Querier state: general_query_timeout') # send general query - packet = PacketIGMPHeader(type=Membership_Query, max_resp_time=QueryResponseInterval*10) + packet = PacketIGMPHeader(type=MEMBERSHIP_QUERY, max_resp_time=QUERY_RESPONSE_INTERVAL * 10) router_state.interface.send(packet.bytes()) # set general query timer @@ -24,12 +28,18 @@ def general_query_timeout(router_state: 'RouterState'): @staticmethod def other_querier_present_timeout(router_state: 'RouterState'): + """ + Other Querier Present timer has expired + """ router_state.router_state_logger.debug('Querier state: other_querier_present_timeout') # do nothing return @staticmethod def receive_query(router_state: 'RouterState', packet: ReceivedPacket): + """ + Interface associated with RouterState is Querier and received a Query packet + """ router_state.router_state_logger.debug('Querier state: receive_query') source_ip = packet.ip_header.ip_src @@ -54,22 +64,37 @@ def state_name(): @staticmethod def get_group_membership_time(max_response_time: int): - return LastMemberQueryInterval * LastMemberQueryCount + """ + Get time to set timer* + """ + return LAST_MEMBER_QUERY_INTERVAL * LAST_MEMBER_QUERY_COUNT # State @staticmethod def get_checking_membership_state(): + """ + Get implementation of CheckingMembership state machine of interface in Querier state + """ return CheckingMembership @staticmethod def get_members_present_state(): + """ + Get implementation of MembersPresent state machine of interface in Querier state + """ return MembersPresent @staticmethod def get_no_members_present_state(): + """ + Get implementation of NoMembersPresent state machine of interface in Querier state + """ return NoMembersPresent @staticmethod def get_version_1_members_present_state(): + """ + Get implementation of Version1MembersPresent state machine of interface in Querier state + """ return Version1MembersPresent diff --git a/pimdm/igmp/querier/Version1MembersPresent.py b/pimdm/igmp/querier/Version1MembersPresent.py index 10e5ee1..253df05 100644 --- a/pimdm/igmp/querier/Version1MembersPresent.py +++ b/pimdm/igmp/querier/Version1MembersPresent.py @@ -6,6 +6,9 @@ def group_membership_timeout(group_state: 'GroupState'): + """ + timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier Version1MembersPresent: group_membership_timeout') group_state.set_state(NoMembersPresent) @@ -14,33 +17,51 @@ def group_membership_timeout(group_state: 'GroupState'): def group_membership_v1_timeout(group_state: 'GroupState'): + """ + v1 host timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier Version1MembersPresent: group_membership_v1_timeout') group_state.set_state(MembersPresent) def retransmit_timeout(group_state: 'GroupState'): + """ + retransmit timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier Version1MembersPresent: retransmit_timeout') # do nothing return def receive_v1_membership_report(group_state: 'GroupState'): + """ + Received IGMP Version 1 Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier Version1MembersPresent: receive_v1_membership_report') group_state.set_timer() group_state.set_v1_host_timer() def receive_v2_membership_report(group_state: 'GroupState'): + """ + Received IGMP Membership Report packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier Version1MembersPresent: receive_v2_membership_report') group_state.set_timer() def receive_leave_group(group_state: 'GroupState'): + """ + Received IGMP Leave packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier Version1MembersPresent: receive_leave_group') # do nothing return def receive_group_specific_query(group_state: 'GroupState', max_response_time: int): + """ + Received IGMP Group Specific Query packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier Version1MembersPresent: receive_group_specific_query') # do nothing return diff --git a/pimdm/igmp/wrapper/NoMembersPresent.py b/pimdm/igmp/wrapper/NoMembersPresent.py index 2e5d853..f6a7109 100644 --- a/pimdm/igmp/wrapper/NoMembersPresent.py +++ b/pimdm/igmp/wrapper/NoMembersPresent.py @@ -2,6 +2,7 @@ if TYPE_CHECKING: from ..RouterState import RouterState + def get_state(router_state: 'RouterState'): return router_state.interface_state.get_no_members_present_state() diff --git a/pimdm/mld/GroupState.py b/pimdm/mld/GroupState.py index 17b41cc..280cb27 100644 --- a/pimdm/mld/GroupState.py +++ b/pimdm/mld/GroupState.py @@ -4,7 +4,7 @@ from pimdm.utils import TYPE_CHECKING from .wrapper import NoListenersPresent -from .mld_globals import MulticastListenerInterval, LastListenerQueryInterval +from .mld_globals import MULTICAST_LISTENER_INTERVAL, LAST_LISTENER_QUERY_INTERVAL if TYPE_CHECKING: from .RouterState import RouterState @@ -39,16 +39,22 @@ def print_state(self): # Set state ########################################### def set_state(self, state): + """ + Set membership state regarding this Group + """ self.state = state self.group_state_logger.debug("change membership state to: " + state.print_state()) ########################################### # Set timers ########################################### - def set_timer(self, alternative: bool=False, max_response_time: int=None): + def set_timer(self, alternative: bool = False, max_response_time: int = None): + """ + Set timer + """ self.clear_timer() if not alternative: - time = MulticastListenerInterval + time = MULTICAST_LISTENER_INTERVAL else: time = self.router_state.interface_state.get_group_membership_time(max_response_time) @@ -57,34 +63,51 @@ def set_timer(self, alternative: bool=False, max_response_time: int=None): self.timer = timer def clear_timer(self): + """ + Stop timer + """ if self.timer is not None: self.timer.cancel() def set_retransmit_timer(self): + """ + Set retransmit timer + """ self.clear_retransmit_timer() - retransmit_timer = Timer(LastListenerQueryInterval, self.retransmit_timeout) + retransmit_timer = Timer(LAST_LISTENER_QUERY_INTERVAL, self.retransmit_timeout) retransmit_timer.start() self.retransmit_timer = retransmit_timer def clear_retransmit_timer(self): + """ + Stop retransmit timer + """ if self.retransmit_timer is not None: self.retransmit_timer.cancel() - ########################################### # Get group state from specific interface state ########################################### def get_interface_group_state(self): + """ + Get specific implementation of the membership state machine (depending on the querier state machine) + """ return self.state.get_state(self.router_state) ########################################### # Timer timeout ########################################### def group_membership_timeout(self): + """ + Timer has expired + """ with self.lock: self.get_interface_group_state().group_membership_timeout(self) def retransmit_timeout(self): + """ + Retransmit timer has expired + """ with self.lock: self.get_interface_group_state().retransmit_timeout(self) @@ -92,14 +115,23 @@ def retransmit_timeout(self): # Receive Packets ########################################### def receive_report(self): + """ + Received MLD Report packet regarding this group + """ with self.lock: self.get_interface_group_state().receive_report(self) def receive_done(self): + """ + Received MLD Done packet regarding this group + """ with self.lock: self.get_interface_group_state().receive_done(self) def receive_group_specific_query(self, max_response_time: int): + """ + Received MLD Group Specific Query packet regarding this group + """ with self.lock: self.get_interface_group_state().receive_group_specific_query(self, max_response_time) @@ -107,30 +139,52 @@ def receive_group_specific_query(self, max_response_time: int): # Notify Routing ########################################### def notify_routing_add(self): + """ + Notify all tree entries that MLD considers to have hosts interested in this group + """ with self.multicast_interface_state_lock: print("notify+", self.multicast_interface_state) for interface_state in self.multicast_interface_state: interface_state.notify_membership(has_members=True) def notify_routing_remove(self): + """ + Notify all tree entries that MLD considers to have not hosts interested in this group + """ with self.multicast_interface_state_lock: print("notify-", self.multicast_interface_state) for interface_state in self.multicast_interface_state: interface_state.notify_membership(has_members=False) def add_multicast_routing_entry(self, kernel_entry): + """ + A new tree is being monitored by the multicast routing protocol that has the same group + MLD will store these entries in order to accelerate the notification process regarding changes in MLD state + """ with self.multicast_interface_state_lock: self.multicast_interface_state.append(kernel_entry) return self.has_members() def remove_multicast_routing_entry(self, kernel_entry): + """ + A tree is no longer being monitored by the multicast routing protocol + Remove this tree from this object + """ with self.multicast_interface_state_lock: self.multicast_interface_state.remove(kernel_entry) def has_members(self): + """ + Determine if MLD considers to have hosts interested in receiving data packets + """ return self.state is not NoListenersPresent def remove(self): + """ + Remove this group from the MLD process + Notify all trees that this group no longer considers to be connected to hosts + This method will be invoked whenever an MLD interface is removed + """ with self.multicast_interface_state_lock: self.clear_retransmit_timer() self.clear_timer() diff --git a/pimdm/mld/RouterState.py b/pimdm/mld/RouterState.py index 0554e04..900039a 100644 --- a/pimdm/mld/RouterState.py +++ b/pimdm/mld/RouterState.py @@ -8,7 +8,8 @@ from .querier.Querier import Querier from .nonquerier.NonQuerier import NonQuerier from .GroupState import GroupState -from .mld_globals import QueryResponseInterval, QueryInterval, OtherQuerierPresentInterval, MULTICAST_LISTENER_QUERY_TYPE +from .mld_globals import QUERY_RESPONSE_INTERVAL, QUERY_INTERVAL, OTHER_QUERIER_PRESENT_INTERVAL, \ + MULTICAST_LISTENER_QUERY_TYPE if TYPE_CHECKING: from pimdm.InterfaceMLD import InterfaceMLD @@ -36,11 +37,11 @@ def __init__(self, interface: 'InterfaceMLD'): self.group_state_lock = RWLockWrite() # send general query - packet = PacketMLDHeader(type=MULTICAST_LISTENER_QUERY_TYPE, max_resp_delay=QueryResponseInterval*1000) + packet = PacketMLDHeader(type=MULTICAST_LISTENER_QUERY_TYPE, max_resp_delay=QUERY_RESPONSE_INTERVAL * 1000) self.interface.send(packet.bytes()) # set initial general query timer - timer = Timer(QueryInterval, self.general_query_timeout) + timer = Timer(QUERY_INTERVAL, self.general_query_timeout) timer.start() self.general_query_timer = timer @@ -58,32 +59,53 @@ def print_state(self): return self.interface_state.state_name() def set_general_query_timer(self): + """ + Set general query timer + """ self.clear_general_query_timer() - general_query_timer = Timer(QueryInterval, self.general_query_timeout) + general_query_timer = Timer(QUERY_INTERVAL, self.general_query_timeout) general_query_timer.start() self.general_query_timer = general_query_timer def clear_general_query_timer(self): + """ + Stop general query timer + """ if self.general_query_timer is not None: self.general_query_timer.cancel() def set_other_querier_present_timer(self): + """ + Set other querier present timer + """ self.clear_other_querier_present_timer() - other_querier_present_timer = Timer(OtherQuerierPresentInterval, self.other_querier_present_timeout) + other_querier_present_timer = Timer(OTHER_QUERIER_PRESENT_INTERVAL, self.other_querier_present_timeout) other_querier_present_timer.start() self.other_querier_present_timer = other_querier_present_timer def clear_other_querier_present_timer(self): + """ + Stop other querier present timer + """ if self.other_querier_present_timer is not None: self.other_querier_present_timer.cancel() def general_query_timeout(self): + """ + General Query timer has expired + """ self.interface_state.general_query_timeout(self) def other_querier_present_timeout(self): + """ + Other Querier Present timer has expired + """ self.interface_state.other_querier_present_timeout(self) def change_interface_state(self, querier: bool): + """ + Change state regarding querier state machine (Querier/NonQuerier) + """ if querier: self.interface_state = Querier self.router_state_logger.debug('change querier state to -> Querier') @@ -95,6 +117,9 @@ def change_interface_state(self, querier: bool): # group state methods ############################################ def get_group_state(self, group_ip): + """ + Get object that monitors a given group (with group_ip IP address) + """ with self.group_state_lock.genRlock(): if group_ip in self.group_state: return self.group_state[group_ip] @@ -108,30 +133,35 @@ def get_group_state(self, group_ip): return group_state def receive_report(self, packet: ReceivedPacket): + """ + Received MLD Report packet + """ mld_group = packet.payload.group_address - #if igmp_group not in self.group_state: - # self.group_state[igmp_group] = GroupState(self, igmp_group) - - #self.group_state[igmp_group].receive_v2_membership_report() self.get_group_state(mld_group).receive_report() def receive_done(self, packet: ReceivedPacket): + """ + Received MLD Done packet + """ mld_group = packet.payload.group_address - #if igmp_group in self.group_state: - # self.group_state[igmp_group].receive_leave_group() self.get_group_state(mld_group).receive_done() def receive_query(self, packet: ReceivedPacket): + """ + Received MLD Query packet + """ self.interface_state.receive_query(self, packet) mld_group = packet.payload.group_address # process group specific query if mld_group != "::" and mld_group in self.group_state: - #if igmp_group != "0.0.0.0": max_response_time = packet.payload.max_resp_delay - #self.group_state[igmp_group].receive_group_specific_query(max_response_time) self.get_group_state(mld_group).receive_group_specific_query(max_response_time) def remove(self): + """ + Remove this MLD interface + Clear all state + """ for group in self.group_state.values(): group.remove() diff --git a/pimdm/mld/mld_globals.py b/pimdm/mld/mld_globals.py index 882063d..028d6b2 100644 --- a/pimdm/mld/mld_globals.py +++ b/pimdm/mld/mld_globals.py @@ -1,14 +1,14 @@ -#MLD timers (in seconds) -RobustnessVariable = 2 -QueryInterval = 125 -QueryResponseInterval = 10 -MulticastListenerInterval = (RobustnessVariable * QueryInterval) + (QueryResponseInterval) -OtherQuerierPresentInterval = (RobustnessVariable * QueryInterval) + 0.5 * QueryResponseInterval -StartupQueryInterval = (1/4) * QueryInterval -StartupQueryCount = RobustnessVariable -LastListenerQueryInterval = 1 -LastListenerQueryCount = RobustnessVariable -UnsolicitedReportInterval = 10 +# MLD timers (in seconds) +ROBUSTNESS_VARIABLE = 2 +QUERY_INTERVAL = 125 +QUERY_RESPONSE_INTERVAL = 10 +MULTICAST_LISTENER_INTERVAL = (ROBUSTNESS_VARIABLE * QUERY_INTERVAL) + (QUERY_RESPONSE_INTERVAL) +OTHER_QUERIER_PRESENT_INTERVAL = (ROBUSTNESS_VARIABLE * QUERY_INTERVAL) + 0.5 * QUERY_RESPONSE_INTERVAL +STARTUP_QUERY_INTERVAL = (1 / 4) * QUERY_INTERVAL +STARTUP_QUERY_COUNT = ROBUSTNESS_VARIABLE +LAST_LISTENER_QUERY_INTERVAL = 1 +LAST_LISTENER_QUERY_COUNT = ROBUSTNESS_VARIABLE +UNSOLICITED_REPORT_INTERVAL = 10 # MLD msg type diff --git a/pimdm/mld/nonquerier/CheckingListeners.py b/pimdm/mld/nonquerier/CheckingListeners.py index 8273439..3b6fb08 100644 --- a/pimdm/mld/nonquerier/CheckingListeners.py +++ b/pimdm/mld/nonquerier/CheckingListeners.py @@ -7,24 +7,36 @@ def receive_report(group_state: 'GroupState'): + """ + Received MLD Report packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier CheckingListeners: receive_report') group_state.set_timer() group_state.set_state(ListenersPresent) def receive_done(group_state: 'GroupState'): + """ + Received MLD Done packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier CheckingListeners: receive_done') # do nothing return def receive_group_specific_query(group_state: 'GroupState', max_response_time: int): + """ + Received MLD Group Specific Query packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier CheckingListeners: receive_group_specific_query') # do nothing return def group_membership_timeout(group_state: 'GroupState'): + """ + timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier CheckingListeners: group_membership_timeout') group_state.set_state(NoListenersPresent) @@ -33,6 +45,9 @@ def group_membership_timeout(group_state: 'GroupState'): def retransmit_timeout(group_state: 'GroupState'): + """ + retransmit timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier CheckingListeners: retransmit_timeout') # do nothing return diff --git a/pimdm/mld/nonquerier/ListenersPresent.py b/pimdm/mld/nonquerier/ListenersPresent.py index 9145422..0995688 100644 --- a/pimdm/mld/nonquerier/ListenersPresent.py +++ b/pimdm/mld/nonquerier/ListenersPresent.py @@ -7,23 +7,35 @@ def receive_report(group_state: 'GroupState'): + """ + Received MLD Report packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier ListenersPresent: receive_report') group_state.set_timer() def receive_done(group_state: 'GroupState'): + """ + Received MLD Done packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier ListenersPresent: receive_done') # do nothing return def receive_group_specific_query(group_state: 'GroupState', max_response_time: int): + """ + Received MLD Group Specific Query packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier ListenersPresent: receive_group_specific_query') group_state.set_timer(alternative=True, max_response_time=max_response_time) group_state.set_state(CheckingListeners) def group_membership_timeout(group_state: 'GroupState'): + """ + timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier ListenersPresent: group_membership_timeout') group_state.set_state(NoListenersPresent) @@ -32,8 +44,9 @@ def group_membership_timeout(group_state: 'GroupState'): def retransmit_timeout(group_state: 'GroupState'): + """ + retransmit timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier ListenersPresent: retransmit_timeout') # do nothing return - - diff --git a/pimdm/mld/nonquerier/NoListenersPresent.py b/pimdm/mld/nonquerier/NoListenersPresent.py index c108cc7..4c3e650 100644 --- a/pimdm/mld/nonquerier/NoListenersPresent.py +++ b/pimdm/mld/nonquerier/NoListenersPresent.py @@ -6,6 +6,9 @@ def receive_report(group_state: 'GroupState'): + """ + Received MLD Report packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier NoListenersPresent: receive_report') group_state.set_timer() group_state.set_state(ListenersPresent) @@ -15,24 +18,36 @@ def receive_report(group_state: 'GroupState'): def receive_done(group_state: 'GroupState'): + """ + Received MLD Done packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier NoListenersPresent: receive_done') # do nothing return def receive_group_specific_query(group_state: 'GroupState', max_response_time: int): + """ + Received MLD Group Specific Query packet regarding group GroupState + """ group_state.group_state_logger.debug('NonQuerier NoListenersPresent: receive_group_specific_query') # do nothing return def group_membership_timeout(group_state: 'GroupState'): + """ + timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier NoListenersPresent: group_membership_timeout') # do nothing return def retransmit_timeout(group_state: 'GroupState'): + """ + retransmit timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('NonQuerier NoListenersPresent: retransmit_timeout') # do nothing return diff --git a/pimdm/mld/nonquerier/NonQuerier.py b/pimdm/mld/nonquerier/NonQuerier.py index 222af7b..c0cc4ad 100644 --- a/pimdm/mld/nonquerier/NonQuerier.py +++ b/pimdm/mld/nonquerier/NonQuerier.py @@ -1,6 +1,6 @@ from ipaddress import IPv6Address from pimdm.utils import TYPE_CHECKING -from ..mld_globals import QueryResponseInterval, LastListenerQueryCount +from ..mld_globals import QUERY_RESPONSE_INTERVAL, LAST_LISTENER_QUERY_COUNT from pimdm.packet.PacketMLDHeader import PacketMLDHeader from pimdm.packet.ReceivedPacket import ReceivedPacket from . import NoListenersPresent, ListenersPresent, CheckingListeners @@ -12,19 +12,25 @@ class NonQuerier: @staticmethod def general_query_timeout(router_state: 'RouterState'): + """ + General Query timer has expired + """ router_state.router_state_logger.debug('NonQuerier state: general_query_timeout') # do nothing return @staticmethod def other_querier_present_timeout(router_state: 'RouterState'): + """ + Other Query Present timer has expired + """ router_state.router_state_logger.debug('NonQuerier state: other_querier_present_timeout') #change state to Querier router_state.change_interface_state(querier=True) # send general query packet = PacketMLDHeader(type=PacketMLDHeader.MULTICAST_LISTENER_QUERY_TYPE, - max_resp_delay=QueryResponseInterval*1000) + max_resp_delay=QUERY_RESPONSE_INTERVAL * 1000) router_state.interface.send(packet.bytes()) # set general query timer @@ -32,6 +38,9 @@ def other_querier_present_timeout(router_state: 'RouterState'): @staticmethod def receive_query(router_state: 'RouterState', packet: ReceivedPacket): + """ + Interface associated with RouterState is NonQuerier and received a Query packet + """ router_state.router_state_logger.debug('NonQuerier state: receive_query') source_ip = packet.ip_header.ip_src @@ -49,17 +58,29 @@ def state_name(): @staticmethod def get_group_membership_time(max_response_time: int): - return (max_response_time/1000.0) * LastListenerQueryCount + """ + Get time to set timer* + """ + return (max_response_time/1000.0) * LAST_LISTENER_QUERY_COUNT # State @staticmethod def get_checking_listeners_state(): + """ + Get implementation of CheckingListeners state machine of interface in NonQuerier state + """ return CheckingListeners @staticmethod def get_listeners_present_state(): + """ + Get implementation of ListenersPresent state machine of interface in NonQuerier state + """ return ListenersPresent @staticmethod def get_no_listeners_present_state(): + """ + Get implementation of NoListenersPresent state machine of interface in NonQuerier state + """ return NoListenersPresent diff --git a/pimdm/mld/querier/CheckingListeners.py b/pimdm/mld/querier/CheckingListeners.py index 2fb8431..886dc00 100644 --- a/pimdm/mld/querier/CheckingListeners.py +++ b/pimdm/mld/querier/CheckingListeners.py @@ -1,30 +1,43 @@ from pimdm.packet.PacketMLDHeader import PacketMLDHeader from pimdm.utils import TYPE_CHECKING -from ..mld_globals import LastListenerQueryInterval +from ..mld_globals import LAST_LISTENER_QUERY_INTERVAL from ..wrapper import ListenersPresent, NoListenersPresent if TYPE_CHECKING: from ..GroupState import GroupState def receive_report(group_state: 'GroupState'): + """ + Received MLD Report packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier CheckingListeners: receive_report') group_state.set_timer() group_state.clear_retransmit_timer() group_state.set_state(ListenersPresent) + def receive_done(group_state: 'GroupState'): + """ + Received MLD Done packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier CheckingListeners: receive_done') # do nothing return def receive_group_specific_query(group_state: 'GroupState', max_response_time: int): + """ + Received MLD Group Specific Query packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier CheckingListeners: receive_group_specific_query') # do nothing return def group_membership_timeout(group_state: 'GroupState'): + """ + timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier CheckingListeners: group_membership_timeout') group_state.clear_retransmit_timer() group_state.set_state(NoListenersPresent) @@ -34,10 +47,13 @@ def group_membership_timeout(group_state: 'GroupState'): def retransmit_timeout(group_state: 'GroupState'): + """ + retransmit timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier CheckingListeners: retransmit_timeout') group_addr = group_state.group_ip packet = PacketMLDHeader(type=PacketMLDHeader.MULTICAST_LISTENER_QUERY_TYPE, - max_resp_delay=LastListenerQueryInterval*1000, group_address=group_addr) + max_resp_delay=LAST_LISTENER_QUERY_INTERVAL * 1000, group_address=group_addr) group_state.router_state.send(data=packet.bytes(), address=group_addr) group_state.set_retransmit_timer() diff --git a/pimdm/mld/querier/ListenersPresent.py b/pimdm/mld/querier/ListenersPresent.py index d939fdf..da18b73 100644 --- a/pimdm/mld/querier/ListenersPresent.py +++ b/pimdm/mld/querier/ListenersPresent.py @@ -1,17 +1,23 @@ from pimdm.packet.PacketMLDHeader import PacketMLDHeader from pimdm.utils import TYPE_CHECKING -from ..mld_globals import LastListenerQueryInterval +from ..mld_globals import LAST_LISTENER_QUERY_INTERVAL from ..wrapper import CheckingListeners, NoListenersPresent if TYPE_CHECKING: from ..GroupState import GroupState def receive_report(group_state: 'GroupState'): + """ + Received MLD Report packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier ListenersPresent: receive_report') group_state.set_timer() def receive_done(group_state: 'GroupState'): + """ + Received MLD Done packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier ListenersPresent: receive_done') group_ip = group_state.group_ip @@ -19,19 +25,25 @@ def receive_done(group_state: 'GroupState'): group_state.set_retransmit_timer() packet = PacketMLDHeader(type=PacketMLDHeader.MULTICAST_LISTENER_QUERY_TYPE, - max_resp_delay=LastListenerQueryInterval*1000, group_address=group_ip) + max_resp_delay=LAST_LISTENER_QUERY_INTERVAL * 1000, group_address=group_ip) group_state.router_state.send(data=packet.bytes(), address=group_ip) group_state.set_state(CheckingListeners) def receive_group_specific_query(group_state: 'GroupState', max_response_time): + """ + Received MLD Group Specific Query packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier ListenersPresent: receive_group_specific_query') # do nothing return def group_membership_timeout(group_state: 'GroupState'): + """ + timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier ListenersPresent: group_membership_timeout') group_state.set_state(NoListenersPresent) @@ -40,8 +52,9 @@ def group_membership_timeout(group_state: 'GroupState'): def retransmit_timeout(group_state: 'GroupState'): + """ + retransmit timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier ListenersPresent: retransmit_timeout') # do nothing return - - diff --git a/pimdm/mld/querier/NoListenersPresent.py b/pimdm/mld/querier/NoListenersPresent.py index b40b0e7..8f1f6e7 100644 --- a/pimdm/mld/querier/NoListenersPresent.py +++ b/pimdm/mld/querier/NoListenersPresent.py @@ -5,6 +5,9 @@ def receive_report(group_state: 'GroupState'): + """ + Received MLD Report packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier NoListenersPresent: receive_report') group_state.set_timer() group_state.set_state(ListenersPresent) @@ -14,25 +17,36 @@ def receive_report(group_state: 'GroupState'): def receive_done(group_state: 'GroupState'): + """ + Received MLD Done packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier NoListenersPresent: receive_done') # do nothing return def receive_group_specific_query(group_state: 'GroupState', max_response_time: int): + """ + Received MLD Group Specific Query packet regarding group GroupState + """ group_state.group_state_logger.debug('Querier NoListenersPresent: receive_group_specific_query') # do nothing return def group_membership_timeout(group_state: 'GroupState'): + """ + timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier NoListenersPresent: group_membership_timeout') # do nothing return def retransmit_timeout(group_state: 'GroupState'): + """ + retransmit timer associated with group GroupState object has expired + """ group_state.group_state_logger.debug('Querier NoListenersPresent: retransmit_timeout') # do nothing return - diff --git a/pimdm/mld/querier/Querier.py b/pimdm/mld/querier/Querier.py index f4c3fef..fda0eb8 100644 --- a/pimdm/mld/querier/Querier.py +++ b/pimdm/mld/querier/Querier.py @@ -1,7 +1,7 @@ from ipaddress import IPv6Address from pimdm.utils import TYPE_CHECKING -from ..mld_globals import LastListenerQueryInterval, LastListenerQueryCount, QueryResponseInterval +from ..mld_globals import LAST_LISTENER_QUERY_INTERVAL, LAST_LISTENER_QUERY_COUNT, QUERY_RESPONSE_INTERVAL from pimdm.packet.PacketMLDHeader import PacketMLDHeader from pimdm.packet.ReceivedPacket import ReceivedPacket @@ -12,19 +12,11 @@ class Querier: - @staticmethod - def general_query_timeout(router_state: 'RouterState'): - router_state.router_state_logger.debug('Querier state: general_query_timeout') - # send general query - packet = PacketMLDHeader(type=PacketMLDHeader.MULTICAST_LISTENER_QUERY_TYPE, - max_resp_delay=QueryResponseInterval*1000) - router_state.interface.send(packet.bytes()) - - # set general query timer - router_state.set_general_query_timer() - @staticmethod def receive_query(router_state: 'RouterState', packet: ReceivedPacket): + """ + Interface associated with RouterState is Querier and received a Query packet + """ router_state.router_state_logger.debug('Querier state: receive_query') source_ip = packet.ip_header.ip_src @@ -41,8 +33,25 @@ def receive_query(router_state: 'RouterState', packet: ReceivedPacket): router_state.clear_general_query_timer() router_state.set_other_querier_present_timer() + @staticmethod + def general_query_timeout(router_state: 'RouterState'): + """ + General Query timer has expired + """ + router_state.router_state_logger.debug('Querier state: general_query_timeout') + # send general query + packet = PacketMLDHeader(type=PacketMLDHeader.MULTICAST_LISTENER_QUERY_TYPE, + max_resp_delay=QUERY_RESPONSE_INTERVAL * 1000) + router_state.interface.send(packet.bytes()) + + # set general query timer + router_state.set_general_query_timer() + @staticmethod def other_querier_present_timeout(router_state: 'RouterState'): + """ + Other Querier Present timer has expired + """ router_state.router_state_logger.debug('Querier state: other_querier_present_timeout') # do nothing return @@ -54,17 +63,29 @@ def state_name(): @staticmethod def get_group_membership_time(max_response_time: int): - return LastListenerQueryInterval * LastListenerQueryCount + """ + Get time to set timer* + """ + return LAST_LISTENER_QUERY_INTERVAL * LAST_LISTENER_QUERY_COUNT # State @staticmethod def get_checking_listeners_state(): + """ + Get implementation of CheckingListeners state machine of interface in Querier state + """ return CheckingListeners @staticmethod def get_listeners_present_state(): + """ + Get implementation of ListenersPresent state machine of interface in Querier state + """ return ListenersPresent @staticmethod def get_no_listeners_present_state(): + """ + Get implementation of NoListenersPresent state machine of interface in Querier state + """ return NoListenersPresent diff --git a/pimdm/tree/globals.py b/pimdm/tree/globals.py index 4ee9f96..ab2169b 100644 --- a/pimdm/tree/globals.py +++ b/pimdm/tree/globals.py @@ -1,3 +1,4 @@ +# PIM-DM TIMER VARIABLES ASSERT_TIME = 180 GRAFT_RETRY_PERIOD = 3 JP_OVERRIDE_INTERVAL = 3.0 @@ -7,8 +8,17 @@ SOURCE_LIFETIME = 210 T_LIMIT = 210 +# PIM-DM VARIABLES HELLO_HOLD_TIME_NO_TIMEOUT = 0xFFFF HELLO_HOLD_TIME = 160 HELLO_HOLD_TIME_TIMEOUT = 0 -ASSERT_CANCEL_METRIC = 0xFFFFFFFF \ No newline at end of file +ASSERT_CANCEL_METRIC = 0xFFFFFFFF + +# MULTIPLE TABLES SUPPORT +# Define which unicast routing table to be used for RPF checks and to get route metric information +# Default unicast routing table is 254 +UNICAST_TABLE_ID = 254 +# Define which multicast routing table to be used for setting multicast trees +# Default multicast routing table is 0 +MULTICAST_TABLE_ID = 0 diff --git a/pimdm/tree/tree_if_upstream.py b/pimdm/tree/tree_if_upstream.py index 90a31c5..24eff0f 100644 --- a/pimdm/tree/tree_if_upstream.py +++ b/pimdm/tree/tree_if_upstream.py @@ -59,12 +59,11 @@ def __init__(self, kernel_entry, interface_id): self.logger.debug('Created UpstreamInterface') - def socket_recv(self): while self.socket_is_enabled: try: self.socket_pkt.recvfrom(0) - print("PACOTE DADOS RECEBIDO") + print("DATA RECEIVED") self.recv_data_msg() except: traceback.print_exc() @@ -151,7 +150,6 @@ def clear_source_active_timer(self): if self._source_active_timer is not None: self._source_active_timer.cancel() - ########################################### # Timer timeout ########################################### @@ -182,7 +180,6 @@ def recv_data_msg(self): if interface is not None and interface.is_state_refresh_enabled(): self._originator_state.recvDataMsgFromSource(self) - def recv_join_msg(self, upstream_neighbor_address): super().recv_join_msg(upstream_neighbor_address) if upstream_neighbor_address == self.get_neighbor_RPF(): @@ -208,7 +205,6 @@ def recv_state_refresh_msg(self, received_metric: AssertMetric, prune_indicator: elif prune_indicator == 0 and not self.is_prune_limit_timer_running(): self._graft_prune_state.stateRefreshArrivesRPFnbr_pruneIs0_PLTstoped(self) - #################################### def create_state_refresh_msg(self): self._prune_now_counter+=1 @@ -235,7 +231,6 @@ def olist_is_null(self): def olist_is_not_null(self): self._graft_prune_state.olistIsNowNotNull(self) - ########################################### # Changes to RPF'(s) ########################################### @@ -265,7 +260,6 @@ def change_rpf(self, olist_is_null, interface_change=False): else: self._graft_prune_state.RPFnbrChanges_olistIsNotNull(self) - #################################################################### #Override def is_forwarding(self): @@ -300,10 +294,9 @@ def is_downstream(self): def is_originator(self): return self._originator_state == OriginatorState.Originator - #------------------------------------------------------------------------- + ######################################################################### # Properties - #------------------------------------------------------------------------- - + ######################################################################### @property def t_override(self): oi = self.get_interface()._override_interval diff --git a/setup.py b/setup.py index 62133c4..0fab225 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,9 @@ import sys from setuptools import setup, find_packages -# we only support Python 3 version >= 3.4 -#if len(sys.argv) >= 2 and sys.argv[1] == "install" and sys.version_info < (3, 4): -# raise SystemExit("Python 3.4 or higher is required") +# we only support Python 3 version >= 3.3 +if len(sys.argv) >= 2 and sys.argv[1] == "install" and sys.version_info < (3, 3): + raise SystemExit("Python 3.3 or higher is required") dependencies = open("requirements.txt", "r").read().splitlines() @@ -13,7 +13,7 @@ long_description=open("README.md", "r").read(), long_description_content_type="text/markdown", keywords="PIM-DM Multicast Routing Protocol Dense-Mode Router RFC3973 IPv4 IPv6", - version="1.1", + version="1.1.1", url="http://github.com/pedrofran12/pim_dm", author="Pedro Oliveira", author_email="pedro.francisco.oliveira@tecnico.ulisboa.pt",