Commit abf25568 authored by Pedro Oliveira's avatar Pedro Oliveira Committed by GitHub

Multiple VRFs (#4)

* First try IPv6 support... based on HPIM-DM IPv6 commit

* fix membership removal of interface (IGMP/MLD)

* Add support for unicast and multicast VRFs && remove some commented code && IGMP/MLD snake case global variables
parent 023dcf97
......@@ -5,7 +5,8 @@ from ctypes import create_string_buffer, addressof
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 @@ class InterfaceIGMP(Interface):
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,
}
##################
......
......@@ -8,6 +8,7 @@ from abc import abstractmethod, ABCMeta
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)
......
......@@ -9,8 +9,9 @@ import _pickle as pickle
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")
......
......@@ -3,6 +3,7 @@ import ipaddress
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 @@ class UnicastRouting(object):
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
......
......@@ -4,7 +4,7 @@ from threading import Timer
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 @@ class GroupState(object):
# 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 @@ class GroupState(object):
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 @@ class GroupState(object):
# 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 @@ class GroupState(object):
# 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()
......
......@@ -8,7 +8,7 @@ from pimdm.rwlock.RWLock import RWLockWrite
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 @@ class RouterState(object):
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 @@ class RouterState(object):
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 @@ class RouterState(object):
# 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 @@ class RouterState(object):
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()
# 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
......@@ -7,6 +7,9 @@ if TYPE_CHECKING:
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
......@@ -7,6 +7,9 @@ if TYPE_CHECKING:
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)
......@@ -6,29 +6,44 @@ if TYPE_CHECKING:
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
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 @@ class NonQuerier:
@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 @@ class NonQuerier:
@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()
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
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
......@@ -6,24 +6,36 @@ if TYPE_CHECKING:
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
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 @@ if TYPE_CHECKING:
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 @@ class Querier:
@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 @@ class Querier:
@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
......@@ -6,6 +6,9 @@ if TYPE_CHECKING:
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
......@@ -2,6 +2,7 @@ from pimdm.utils import TYPE_CHECKING
if TYPE_CHECKING:
from ..RouterState import RouterState
def get_state(router_state: 'RouterState'):
return router_state.interface_state.get_no_members_present_state()
......
......@@ -4,7 +4,7 @@ from threading import Timer
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 @@ class GroupState(object):
# 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 @@ class GroupState(object):
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 @@ class GroupState(object):
# 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 @@ class GroupState(object):
# 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()
......
......@@ -8,7 +8,8 @@ from pimdm.rwlock.RWLock import RWLockWrite
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 @@ class RouterState(object):
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 @@ class RouterState(object):
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 @@ class RouterState(object):
# 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 @@ class RouterState(object):
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()
#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
......
......@@ -7,24 +7,36 @@ if TYPE_CHECKING:
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
......@@ -7,23 +7,35 @@ if TYPE_CHECKING:
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
......@@ -6,6 +6,9 @@ if TYPE_CHECKING:
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
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 @@ if TYPE_CHECKING:
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 @@ class NonQuerier:
@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 @@ class NonQuerier:
@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
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()
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
......@@ -5,6 +5,9 @@ if TYPE_CHECKING:
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
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 @@ if TYPE_CHECKING:
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 @@ class Querier:
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 @@ class Querier:
@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
# PIM-DM TIMER VARIABLES
ASSERT_TIME = 180
GRAFT_RETRY_PERIOD = 3
JP_OVERRIDE_INTERVAL = 3.0
......@@ -7,8 +8,17 @@ REFRESH_INTERVAL = 60 # State Refresh Interval
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
# 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
......@@ -59,12 +59,11 @@ class TreeInterfaceUpstream(TreeInterface):
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 @@ class TreeInterfaceUpstream(TreeInterface):
if self._source_active_timer is not None:
self._source_active_timer.cancel()
###########################################
# Timer timeout
###########################################
......@@ -182,7 +180,6 @@ class TreeInterfaceUpstream(TreeInterface):
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 @@ class TreeInterfaceUpstream(TreeInterface):
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 @@ class TreeInterfaceUpstream(TreeInterface):
def olist_is_not_null(self):
self._graft_prune_state.olistIsNowNotNull(self)
###########################################
# Changes to RPF'(s)
###########################################
......@@ -265,7 +260,6 @@ class TreeInterfaceUpstream(TreeInterface):
else:
self._graft_prune_state.RPFnbrChanges_olistIsNotNull(self)
####################################################################
#Override
def is_forwarding(self):
......@@ -300,10 +294,9 @@ class TreeInterfaceUpstream(TreeInterface):
def is_originator(self):
return self._originator_state == OriginatorState.Originator
#-------------------------------------------------------------------------
#########################################################################
# Properties
#-------------------------------------------------------------------------
#########################################################################
@property
def t_override(self):
oi = self.get_interface()._override_interval
......
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 @@ setup(
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",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment