Commit 369a6b29 authored by Pedro Oliveira's avatar Pedro Oliveira

remove IGMP and MLD code... they are now deployed on a separate packages to ease maintenance

parent 0cdd2a70
import yaml
from pimdm.tree import pim_globals
from pimdm.igmp import igmp_globals
from pimdm.mld import mld_globals
from igmp.igmp2 import igmp_globals
from mld.mld1 import mld_globals
from pimdm import Main
......
import socket
import struct
from ipaddress import IPv4Address
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
if not hasattr(socket, 'SO_BINDTODEVICE'):
socket.SO_BINDTODEVICE = 25
class InterfaceIGMP(Interface):
ETH_P_IP = 0x0800 # Internet Protocol packet
SO_ATTACH_FILTER = 26
FILTER_IGMP = [
struct.pack('HBBI', 0x28, 0, 0, 0x0000000c),
struct.pack('HBBI', 0x15, 0, 3, 0x00000800),
struct.pack('HBBI', 0x30, 0, 0, 0x00000017),
struct.pack('HBBI', 0x15, 0, 1, 0x00000002),
struct.pack('HBBI', 0x6, 0, 0, 0x00040000),
struct.pack('HBBI', 0x6, 0, 0, 0x00000000),
]
def __init__(self, interface_name: str, vif_index: int):
# SEND SOCKET
snd_s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IGMP)
# bind to interface
snd_s.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, str(interface_name + "\0").encode('utf-8'))
# RECEIVE SOCKET
rcv_s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(InterfaceIGMP.ETH_P_IP))
# receive only IGMP packets by setting a BPF filter
bpf_filter = b''.join(InterfaceIGMP.FILTER_IGMP)
b = create_string_buffer(bpf_filter)
mem_addr_of_filters = addressof(b)
fprog = struct.pack('HL', len(InterfaceIGMP.FILTER_IGMP), mem_addr_of_filters)
rcv_s.setsockopt(socket.SOL_SOCKET, InterfaceIGMP.SO_ATTACH_FILTER, fprog)
# bind to interface
rcv_s.bind((interface_name, 0x0800))
super().__init__(interface_name=interface_name, recv_socket=rcv_s, send_socket=snd_s, vif_index=vif_index)
self.interface_enabled = True
from pimdm.igmp.RouterState import RouterState
self.interface_state = RouterState(self)
super()._enable()
def get_ip(self):
return netifaces.ifaddresses(self.interface_name)[netifaces.AF_INET][0]['addr']
@property
def ip_interface(self):
"""
Get IP of this interface
"""
return self.get_ip()
def send(self, data: bytes, address: str="224.0.0.1"):
super().send(data, address)
def _receive(self, raw_bytes, ancdata, src_addr):
if raw_bytes:
raw_bytes = raw_bytes[14:]
packet = ReceivedPacket(raw_bytes, self)
ip_src = packet.ip_header.ip_src
if not (ip_src == "0.0.0.0" or IPv4Address(ip_src).is_multicast):
self.PKT_FUNCTIONS.get(packet.payload.get_igmp_type(), InterfaceIGMP.receive_unknown_type)(self, packet)
###########################################
# Recv packets
###########################################
def receive_version_1_membership_report(self, packet):
ip_dst = packet.ip_header.ip_dst
igmp_group = packet.payload.group_address
if ip_dst == igmp_group and IPv4Address(igmp_group).is_multicast:
self.interface_state.receive_v1_membership_report(packet)
def receive_version_2_membership_report(self, packet):
ip_dst = packet.ip_header.ip_dst
igmp_group = packet.payload.group_address
if ip_dst == igmp_group and IPv4Address(igmp_group).is_multicast:
self.interface_state.receive_v2_membership_report(packet)
def receive_leave_group(self, packet):
ip_dst = packet.ip_header.ip_dst
igmp_group = packet.payload.group_address
if ip_dst == "224.0.0.2" and IPv4Address(igmp_group).is_multicast:
self.interface_state.receive_leave_group(packet)
def receive_membership_query(self, packet):
ip_dst = packet.ip_header.ip_dst
igmp_group = packet.payload.group_address
if (IPv4Address(igmp_group).is_multicast and ip_dst == igmp_group) or \
(ip_dst == "224.0.0.1" and igmp_group == "0.0.0.0"):
self.interface_state.receive_query(packet)
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,
}
##################
def remove(self):
super().remove()
self.interface_state.remove()
import socket
import struct
import netifaces
import ipaddress
from socket import if_nametoindex
from ipaddress import IPv6Address
from .Interface import Interface
from .packet.ReceivedPacket import ReceivedPacket_v6
from .mld.mld_globals import MULTICAST_LISTENER_QUERY_TYPE, MULTICAST_LISTENER_DONE_TYPE, MULTICAST_LISTENER_REPORT_TYPE
from ctypes import create_string_buffer, addressof
ETH_P_IPV6 = 0x86DD # IPv6 over bluebook
SO_ATTACH_FILTER = 26
ICMP6_FILTER = 1
IPV6_ROUTER_ALERT = 22
def ICMP6_FILTER_SETBLOCKALL():
return struct.pack("I"*8, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF)
def ICMP6_FILTER_SETPASS(type, filterp):
return filterp[:type >> 5] + (bytes([(filterp[type >> 5] & ~(1 << ((type) & 31)))])) + filterp[(type >> 5) + 1:]
class InterfaceMLD(Interface):
IPv6_LINK_SCOPE_ALL_NODES = IPv6Address("ff02::1")
IPv6_LINK_SCOPE_ALL_ROUTERS = IPv6Address("ff02::2")
IPv6_ALL_ZEROS = IPv6Address("::")
FILTER_MLD = [
struct.pack('HBBI', 0x28, 0, 0, 0x0000000c),
struct.pack('HBBI', 0x15, 0, 9, 0x000086dd),
struct.pack('HBBI', 0x30, 0, 0, 0x00000014),
struct.pack('HBBI', 0x15, 0, 7, 0x00000000),
struct.pack('HBBI', 0x30, 0, 0, 0x00000036),
struct.pack('HBBI', 0x15, 0, 5, 0x0000003a),
struct.pack('HBBI', 0x30, 0, 0, 0x0000003e),
struct.pack('HBBI', 0x15, 2, 0, 0x00000082),
struct.pack('HBBI', 0x15, 1, 0, 0x00000083),
struct.pack('HBBI', 0x15, 0, 1, 0x00000084),
struct.pack('HBBI', 0x6, 0, 0, 0x00040000),
struct.pack('HBBI', 0x6, 0, 0, 0x00000000),
]
def __init__(self, interface_name: str, vif_index: int):
# SEND SOCKET
s = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_ICMPV6)
# set socket output interface
if_index = if_nametoindex(interface_name)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, struct.pack('@I', if_index))
"""
# set ICMP6 filter to only receive MLD packets
icmp6_filter = ICMP6_FILTER_SETBLOCKALL()
icmp6_filter = ICMP6_FILTER_SETPASS(MULTICAST_LISTENER_QUERY_TYPE, icmp6_filter)
icmp6_filter = ICMP6_FILTER_SETPASS(MULTICAST_LISTENER_REPORT_TYPE, icmp6_filter)
icmp6_filter = ICMP6_FILTER_SETPASS(MULTICAST_LISTENER_DONE_TYPE, icmp6_filter)
s.setsockopt(socket.IPPROTO_ICMPV6, ICMP6_FILTER, icmp6_filter)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_RECVPKTINFO, True)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, False)
s.setsockopt(socket.IPPROTO_IPV6, self.IPV6_ROUTER_ALERT, 0)
rcv_s = s
"""
ip_interface = "::"
for if_addr in netifaces.ifaddresses(interface_name)[netifaces.AF_INET6]:
ip_interface = if_addr["addr"]
if ipaddress.IPv6Address(ip_interface.split("%")[0]).is_link_local:
# bind to interface
s.bind(socket.getaddrinfo(ip_interface, None, 0, socket.SOCK_RAW, 0, socket.AI_PASSIVE)[0][4])
ip_interface = ip_interface.split("%")[0]
break
self.ip_interface = ip_interface
# RECEIVE SOCKET
rcv_s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_IPV6))
# receive only MLD packets by setting a BPF filter
bpf_filter = b''.join(InterfaceMLD.FILTER_MLD)
b = create_string_buffer(bpf_filter)
mem_addr_of_filters = addressof(b)
fprog = struct.pack('HL', len(InterfaceMLD.FILTER_MLD), mem_addr_of_filters)
rcv_s.setsockopt(socket.SOL_SOCKET, SO_ATTACH_FILTER, fprog)
# bind to interface
rcv_s.bind((interface_name, ETH_P_IPV6))
super().__init__(interface_name=interface_name, recv_socket=rcv_s, send_socket=s, vif_index=vif_index)
self.interface_enabled = True
from .mld.RouterState import RouterState
self.interface_state = RouterState(self)
super()._enable()
@staticmethod
def _get_address_family():
return socket.AF_INET6
def get_ip(self):
return self.ip_interface
def send(self, data: bytes, address: str = "FF02::1"):
# send router alert option
cmsg_level = socket.IPPROTO_IPV6
cmsg_type = socket.IPV6_HOPOPTS
cmsg_data = b'\x3a\x00\x05\x02\x00\x00\x01\x00'
self._send_socket.sendmsg([data], [(cmsg_level, cmsg_type, cmsg_data)], 0, (address, 0))
"""
def receive(self):
while self.interface_enabled:
try:
(raw_bytes, ancdata, _, src_addr) = self._recv_socket.recvmsg(256 * 1024, 500)
if raw_bytes:
self._receive(raw_bytes, ancdata, src_addr)
except Exception:
import traceback
traceback.print_exc()
continue
"""
def _receive(self, raw_bytes, ancdata, src_addr):
if raw_bytes:
raw_bytes = raw_bytes[14:]
src_addr = (socket.inet_ntop(socket.AF_INET6, raw_bytes[8:24]),)
print("MLD IP_SRC bf= ", src_addr)
dst_addr = raw_bytes[24:40]
(next_header,) = struct.unpack("B", raw_bytes[6:7])
print("NEXT HEADER:", next_header)
payload_starts_at_len = 40
if next_header == 0:
# Hop by Hop options
(next_header,) = struct.unpack("B", raw_bytes[40:41])
if next_header != 58:
return
(hdr_ext_len,) = struct.unpack("B", raw_bytes[payload_starts_at_len +1:payload_starts_at_len + 2])
if hdr_ext_len > 0:
payload_starts_at_len = payload_starts_at_len + 1 + hdr_ext_len*8
else:
payload_starts_at_len = payload_starts_at_len + 8
raw_bytes = raw_bytes[payload_starts_at_len:]
ancdata = [(socket.IPPROTO_IPV6, socket.IPV6_PKTINFO, dst_addr)]
print("RECEIVE MLD")
print("ANCDATA: ", ancdata, "; SRC_ADDR: ", src_addr)
packet = ReceivedPacket_v6(raw_bytes, ancdata, src_addr, 58, self)
ip_src = packet.ip_header.ip_src
print("MLD IP_SRC = ", ip_src)
if not (ip_src == "::" or IPv6Address(ip_src).is_multicast):
self.PKT_FUNCTIONS.get(packet.payload.get_mld_type(), InterfaceMLD.receive_unknown_type)(self, packet)
"""
def _receive(self, raw_bytes, ancdata, src_addr):
if raw_bytes:
packet = ReceivedPacket_v6(raw_bytes, ancdata, src_addr, 58, self)
self.PKT_FUNCTIONS[packet.payload.get_mld_type(), InterfaceMLD.receive_unknown_type](self, packet)
"""
###########################################
# Recv packets
###########################################
def receive_multicast_listener_report(self, packet):
print("RECEIVE MULTICAST LISTENER REPORT")
ip_dst = packet.ip_header.ip_dst
mld_group = packet.payload.group_address
ipv6_group = IPv6Address(mld_group)
ipv6_dst = IPv6Address(ip_dst)
if ipv6_dst == ipv6_group and ipv6_group.is_multicast:
self.interface_state.receive_report(packet)
def receive_multicast_listener_done(self, packet):
print("RECEIVE MULTICAST LISTENER DONE")
ip_dst = packet.ip_header.ip_dst
mld_group = packet.payload.group_address
if IPv6Address(ip_dst) == self.IPv6_LINK_SCOPE_ALL_ROUTERS and IPv6Address(mld_group).is_multicast:
self.interface_state.receive_done(packet)
def receive_multicast_listener_query(self, packet):
print("RECEIVE MULTICAST LISTENER QUERY")
ip_dst = packet.ip_header.ip_dst
mld_group = packet.payload.group_address
ipv6_group = IPv6Address(mld_group)
ipv6_dst = IPv6Address(ip_dst)
if (ipv6_group.is_multicast and ipv6_dst == ipv6_group) or\
(ipv6_dst == self.IPv6_LINK_SCOPE_ALL_NODES and ipv6_group == self.IPv6_ALL_ZEROS):
self.interface_state.receive_query(packet)
def receive_unknown_type(self, packet):
raise Exception("UNKNOWN MLD TYPE: " + str(packet.payload.get_mld_type()))
PKT_FUNCTIONS = {
MULTICAST_LISTENER_REPORT_TYPE: receive_multicast_listener_report,
MULTICAST_LISTENER_DONE_TYPE: receive_multicast_listener_done,
MULTICAST_LISTENER_QUERY_TYPE: receive_multicast_listener_query,
}
##################
def remove(self):
super().remove()
self.interface_state.remove()
......@@ -78,7 +78,7 @@ class InterfacePim(Interface):
# don't receive outgoing packets
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 0)
#self.drop_packet_type = None
self.drop_packet_type = None
super().__init__(interface_name, s, s, vif_index)
super()._enable()
......@@ -109,9 +109,11 @@ class InterfacePim(Interface):
"""
Send a new packet destined to group_ip IP
"""
#if self.drop_packet_type is not None and data.payload.get_pim_type() == self.drop_packet_type:
# self.drop_packet_type = None
# return
packet = PacketPimHeader.parse_bytes(data)
if self.drop_packet_type is not None:
if packet.get_pim_type() == self.drop_packet_type:
self.drop_packet_type = None
return
super().send(data=data, group_ip=group_ip)
......@@ -199,15 +201,13 @@ class InterfacePim(Interface):
"""
Get list of known neighbors
"""
with self.neighbors_lock.genRlock():
return self.neighbors.values()
return list(self.neighbors.values())
def get_neighbor(self, ip):
"""
Get specific neighbor by its IP
"""
with self.neighbors_lock.genRlock():
return self.neighbors.get(ip)
return self.neighbors.get(ip)
def remove_neighbor(self, ip):
"""
......
......@@ -11,8 +11,8 @@ from pimdm import UnicastRouting, Main
from pimdm.rwlock.RWLock import RWLockWrite
from pimdm.tree import pim_globals
from pimdm.InterfaceMLD import InterfaceMLD
from pimdm.InterfaceIGMP import InterfaceIGMP
from mld.InterfaceMLD import InterfaceMLD
from igmp.InterfaceIGMP import InterfaceIGMP
from pimdm.InterfacePIM import InterfacePim
from pimdm.InterfacePIM6 import InterfacePim6
from pimdm.tree.KernelEntry import KernelEntry
......@@ -113,14 +113,14 @@ class Kernel(metaclass=ABCMeta):
else:
index = list(range(0, self.MAXVIFS) - self.vif_index_to_name_dic.keys())[0]
ip_interface = None
if interface_name not in self.membership_interface:
igmp_interface = self._create_membership_interface_object(interface_name, index)
self.membership_interface[interface_name] = igmp_interface
ip_interface = igmp_interface.ip_interface
membership_interface = self._create_membership_interface_object(interface_name, index)
self.membership_interface[interface_name] = membership_interface
ip_interface = membership_interface.ip_interface
if not vif_already_exists:
self.create_virtual_interface(ip_interface=ip_interface, interface_name=interface_name, index=index)
if not vif_already_exists:
self.create_virtual_interface(ip_interface=ip_interface, interface_name=interface_name, index=index)
membership_interface.enable()
@abstractmethod
def _create_membership_interface_object(self, interface_name, index):
......
......@@ -226,6 +226,8 @@ def get_config():
return Config.get_yaml_file()
except ModuleNotFoundError:
return "PYYAML needs to be installed. Execute \"pip3 install pyyaml\""
except ImportError:
return "PYYAML needs to be installed. Execute \"pip3 install pyyaml\""
def set_config(file_path):
......@@ -264,8 +266,19 @@ def main():
# logging
global logger
logger = logging.getLogger('pim')
mld_logger = logging.getLogger('mld')
igmp_logger = logging.getLogger('igmp')
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler(sys.stdout))
igmp_logger.setLevel(logging.DEBUG)
mld_logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler(sys.stdout)
handler.addFilter(RootFilter(""))
handler.setLevel(logging.DEBUG)
handler.setFormatter(logging.Formatter('%(asctime)-20s %(name)-50s %(tree)-35s %(vif)-2s %(interfacename)-5s '
'%(routername)-2s %(message)s'))
logger.addHandler(handler)
igmp_logger.addHandler(handler)
mld_logger.addHandler(handler)
global kernel
from pimdm.Kernel import Kernel4
......
......@@ -15,7 +15,7 @@ from pimdm import Main
from pimdm.tree import pim_globals
from pimdm.daemon.Daemon import Daemon
VERSION = "1.2"
VERSION = "1.3"
def client_socket(data_to_send, print_output=True):
......@@ -156,7 +156,7 @@ def main():
group.add_argument("-v", "--verbose", action="store_true", default=False, help="Verbose (print all debug messages)")
group.add_argument("-t", "--test", nargs=2, metavar=('ROUTER_NAME', 'SERVER_LOG_IP'), help="Tester... send log information to SERVER_LOG_IP. Set the router name to ROUTER_NAME")
group.add_argument("-config", "--config", nargs=1, metavar='CONFIG_FILE_PATH', type=str,
help="File path for configuration file. This command should only be used with -start")
help="File path for configuration file.")
group.add_argument("-get_config", "--get_config", action="store_true", default=False,
help="Get configuration file of live daemon.")
#group.add_argument("-drop", "--drop", nargs=2, metavar=('INTERFACE_NAME', 'PACKET_TYPE'), type=str)
......@@ -229,6 +229,9 @@ def main():
except ModuleNotFoundError:
print("PYYAML needs to be installed. Execute \"pip3 install pyyaml\"")
sys.exit(0)
except ImportError:
print("PYYAML needs to be installed. Execute \"pip3 install pyyaml\"")
sys.exit(0)
elif args.verbose:
os.system("tail -f {}".format(pim_globals.DAEMON_LOG_STDOUT_FILE.format(pim_globals.MULTICAST_TABLE_ID)))
sys.exit(0)
......
import logging
from threading import Lock
from threading import Timer
from pimdm.utils import TYPE_CHECKING
from .wrapper import NoMembersPresent
from .igmp_globals import GROUP_MEMBERSHIP_INTERVAL, LAST_MEMBER_QUERY_INTERVAL
if TYPE_CHECKING:
from .RouterState import RouterState
class GroupState(object):
LOGGER = logging.getLogger('pim.igmp.RouterState.GroupState')
def __init__(self, router_state: 'RouterState', group_ip: str):
#logger
extra_dict_logger = router_state.router_state_logger.extra.copy()
extra_dict_logger['tree'] = '(*,' + group_ip + ')'
self.group_state_logger = logging.LoggerAdapter(GroupState.LOGGER, extra_dict_logger)
#timers and state
self.router_state = router_state
self.group_ip = group_ip
self.state = NoMembersPresent
self.timer = None
self.v1_host_timer = None
self.retransmit_timer = None
# lock
self.lock = Lock()
# KernelEntry's instances to notify change of igmp state
self.multicast_interface_state = []
self.multicast_interface_state_lock = Lock()
def print_state(self):
return self.state.print_state()
###########################################
# 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):
"""
Set timer
"""
self.clear_timer()
if not alternative:
time = GROUP_MEMBERSHIP_INTERVAL
else:
time = self.router_state.interface_state.get_group_membership_time(max_response_time)
timer = Timer(time, self.group_membership_timeout)
timer.start()
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(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(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)
###########################################
# 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)
###########################################
# 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()
self.clear_v1_host_timer()
for interface_state in self.multicast_interface_state:
interface_state.notify_membership(has_members=False)
del self.multicast_interface_state[:]
from threading import Timer
import logging
from pimdm.packet.PacketIGMPHeader import PacketIGMPHeader
from pimdm.packet.ReceivedPacket import ReceivedPacket
from pimdm.utils import TYPE_CHECKING
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, QUERY_RESPONSE_INTERVAL, QUERY_INTERVAL, OTHER_QUERIER_PRESENT_INTERVAL
if TYPE_CHECKING:
from pimdm.InterfaceIGMP import InterfaceIGMP
class RouterState(object):
ROUTER_STATE_LOGGER = logging.getLogger('pim.igmp.RouterState')
def __init__(self, interface: 'InterfaceIGMP'):
#logger
logger_extra = dict()
logger_extra['vif'] = interface.vif_index
logger_extra['interfacename'] = interface.interface_name
self.router_state_logger = logging.LoggerAdapter(RouterState.ROUTER_STATE_LOGGER, logger_extra)
# interface of the router connected to the network
self.interface = interface
# state of the router (Querier/NonQuerier)
self.interface_state = Querier
# state of each group
# Key: GroupIPAddress, Value: GroupState object
self.group_state = {}
self.group_state_lock = RWLockWrite()
# send general query
packet = PacketIGMPHeader(type=MEMBERSHIP_QUERY, max_resp_time=QUERY_RESPONSE_INTERVAL * 10)
self.interface.send(packet.bytes())
# set initial general query timer
timer = Timer(QUERY_INTERVAL, self.general_query_timeout)
timer.start()
self.general_query_timer = timer
# present timer
self.other_querier_present_timer = None
# Send packet via interface
def send(self, data: bytes, address: str):
self.interface.send(data, address)
############################################
# interface_state methods
############################################
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(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(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')
else:
self.interface_state = NonQuerier
self.router_state_logger.debug('change querier state to -> NonQuerier')
############################################
# 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]
with self.group_state_lock.genWlock():
if group_ip in self.group_state:
group_state = self.group_state[group_ip]
else:
group_state = GroupState(self, group_ip)
self.group_state[group_ip] = group_state
return group_state
def receive_v1_membership_report(self, packet: ReceivedPacket):
"""
Received IGMP Version 1 Membership Report packet
"""
igmp_group = packet.payload.group_address
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
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
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:
max_response_time = packet.payload.max_resp_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)
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
from pimdm.utils import TYPE_CHECKING
from ..wrapper import NoMembersPresent
from ..wrapper import MembersPresent
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('NonQuerier CheckingMembership: group_membership_timeout')
group_state.set_state(NoMembersPresent)
# NOTIFY ROUTING - !!!!
group_state.notify_routing_remove()
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
from pimdm.utils import TYPE_CHECKING
from ..wrapper import NoMembersPresent
from ..wrapper import CheckingMembership
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('NonQuerier MembersPresent: group_membership_timeout')
group_state.set_state(NoMembersPresent)
# NOTIFY ROUTING - !!!!
group_state.notify_routing_remove()
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)
from pimdm.utils import TYPE_CHECKING
from ..wrapper import MembersPresent
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('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)
# NOTIFY ROUTING + !!!!
group_state.notify_routing_add()
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 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
if TYPE_CHECKING:
from ..RouterState import RouterState
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=QUERY_RESPONSE_INTERVAL * 10)
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 NonQuerier and received a Query packet
"""
router_state.router_state_logger.debug('NonQuerier state: receive_query')
source_ip = packet.ip_header.ip_src
# if source ip of membership query not lower than the ip of the received interface => ignore
if IPv4Address(source_ip) >= IPv4Address(router_state.interface.get_ip()):
return
# reset other present querier timer
router_state.set_other_querier_present_timer()
# TODO ver se existe uma melhor maneira de fazer isto
@staticmethod
def state_name():
return "Non Querier"
@staticmethod
def get_group_membership_time(max_response_time: int):
"""
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 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)
# NOTIFY ROUTING - !!!!
group_state.notify_routing_remove()
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=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()
group_state.set_state(Version1MembersPresent)
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.utils import TYPE_CHECKING
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)
# NOTIFY ROUTING - !!!!
group_state.notify_routing_remove()
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()
group_state.set_state(Version1MembersPresent)
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=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
from pimdm.utils import TYPE_CHECKING
from ..wrapper import MembersPresent
from ..wrapper import 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 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()
group_state.set_state(Version1MembersPresent)
# NOTIFY ROUTING + !!!!
group_state.notify_routing_add()
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)
# NOTIFY ROUTING + !!!!
group_state.notify_routing_add()
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, QUERY_RESPONSE_INTERVAL, LAST_MEMBER_QUERY_COUNT, \
LAST_MEMBER_QUERY_INTERVAL
from pimdm.packet.PacketIGMPHeader import PacketIGMPHeader
from pimdm.packet.ReceivedPacket import ReceivedPacket
from . import CheckingMembership, MembersPresent, Version1MembersPresent, NoMembersPresent
if TYPE_CHECKING:
from ..RouterState import RouterState
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=QUERY_RESPONSE_INTERVAL * 10)
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
@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
# if source ip of membership query not lower than the ip of the received interface => ignore
if IPv4Address(source_ip) >= IPv4Address(router_state.interface.get_ip()):
return
# if source ip of membership query lower than the ip of the received interface => change state
# change state of interface
# Querier -> Non Querier
router_state.change_interface_state(querier=False)
# set other present querier timer
router_state.clear_general_query_timer()
router_state.set_other_querier_present_timer()
# TODO ver se existe uma melhor maneira de fazer isto
@staticmethod
def state_name():
return "Querier"
@staticmethod
def get_group_membership_time(max_response_time: int):
"""
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
from pimdm.utils import TYPE_CHECKING
from ..wrapper import NoMembersPresent
from ..wrapper import MembersPresent
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 Version1MembersPresent: group_membership_timeout')
group_state.set_state(NoMembersPresent)
# NOTIFY ROUTING - !!!!
group_state.notify_routing_remove()
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
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_checking_membership_state()
def print_state():
return "CheckingMembership"
'''
def group_membership_timeout(group_state):
get_state(group_state).group_membership_timeout(group_state)
def group_membership_v1_timeout(group_state):
get_state(group_state).group_membership_v1_timeout(group_state)
def retransmit_timeout(group_state):
get_state(group_state).retransmit_timeout(group_state)
def receive_v1_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v1_membership_report(group_state, packet)
def receive_v2_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v2_membership_report(group_state, packet)
def receive_leave_group(group_state, packet: ReceivedPacket):
get_state(group_state).receive_leave_group(group_state, packet)
def receive_group_specific_query(group_state, packet: ReceivedPacket):
get_state(group_state).receive_group_specific_query(group_state, packet)
'''
\ No newline at end of file
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_members_present_state()
def print_state():
return "MembersPresent"
'''
def group_membership_timeout(group_state):
get_state(group_state).group_membership_timeout(group_state)
def group_membership_v1_timeout(group_state):
get_state(group_state).group_membership_v1_timeout(group_state)
def retransmit_timeout(group_state):
get_state(group_state).retransmit_timeout(group_state)
def receive_v1_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v1_membership_report(group_state, packet)
def receive_v2_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v2_membership_report(group_state, packet)
def receive_leave_group(group_state, packet: ReceivedPacket):
get_state(group_state).receive_leave_group(group_state, packet)
def receive_group_specific_query(group_state, packet: ReceivedPacket):
get_state(group_state).receive_group_specific_query(group_state, packet)
'''
\ No newline at end of file
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()
def print_state():
return "NoMembersPresent"
'''
def group_membership_timeout(group_state):
get_state(group_state).group_membership_timeout(group_state)
def group_membership_v1_timeout(group_state):
get_state(group_state).group_membership_v1_timeout(group_state)
def retransmit_timeout(group_state):
get_state(group_state).retransmit_timeout(group_state)
def receive_v1_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v1_membership_report(group_state, packet)
def receive_v2_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v2_membership_report(group_state, packet)
def receive_leave_group(group_state, packet: ReceivedPacket):
get_state(group_state).receive_leave_group(group_state, packet)
def receive_group_specific_query(group_state, packet: ReceivedPacket):
get_state(group_state).receive_group_specific_query(group_state, packet)
'''
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_version_1_members_present_state()
def print_state():
return "Version1MembersPresent"
'''
def group_membership_timeout(group_state):
get_state(group_state).group_membership_timeout(group_state)
def group_membership_v1_timeout(group_state):
get_state(group_state).group_membership_v1_timeout(group_state)
def retransmit_timeout(group_state):
get_state(group_state).retransmit_timeout(group_state)
def receive_v1_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v1_membership_report(group_state, packet)
def receive_v2_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v2_membership_report(group_state, packet)
def receive_leave_group(group_state, packet: ReceivedPacket):
get_state(group_state).receive_leave_group(group_state, packet)
def receive_group_specific_query(group_state, packet: ReceivedPacket):
get_state(group_state).receive_group_specific_query(group_state, packet)
'''
import logging
from threading import Lock
from threading import Timer
from pimdm.utils import TYPE_CHECKING
from .wrapper import NoListenersPresent
from .mld_globals import MULTICAST_LISTENER_INTERVAL, LAST_LISTENER_QUERY_INTERVAL
if TYPE_CHECKING:
from .RouterState import RouterState
class GroupState(object):
LOGGER = logging.getLogger('pim.mld.RouterState.GroupState')
def __init__(self, router_state: 'RouterState', group_ip: str):
#logger
extra_dict_logger = router_state.router_state_logger.extra.copy()
extra_dict_logger['tree'] = '(*,' + group_ip + ')'
self.group_state_logger = logging.LoggerAdapter(GroupState.LOGGER, extra_dict_logger)
#timers and state
self.router_state = router_state
self.group_ip = group_ip
self.state = NoListenersPresent
self.timer = None
self.retransmit_timer = None
# lock
self.lock = Lock()
# KernelEntry's instances to notify change of igmp state
self.multicast_interface_state = []
self.multicast_interface_state_lock = Lock()
def print_state(self):
return self.state.print_state()
###########################################
# 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):
"""
Set timer
"""
self.clear_timer()
if not alternative:
time = MULTICAST_LISTENER_INTERVAL
else:
time = self.router_state.interface_state.get_group_membership_time(max_response_time)
timer = Timer(time, self.group_membership_timeout)
timer.start()
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(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)
###########################################
# 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)
###########################################
# 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()
for interface_state in self.multicast_interface_state:
interface_state.notify_membership(has_members=False)
del self.multicast_interface_state[:]
import logging
from threading import Timer
from pimdm.packet.PacketMLDHeader import PacketMLDHeader
from pimdm.packet.ReceivedPacket import ReceivedPacket
from pimdm.utils import TYPE_CHECKING
from pimdm.rwlock.RWLock import RWLockWrite
from .querier.Querier import Querier
from .nonquerier.NonQuerier import NonQuerier
from .GroupState import GroupState
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
class RouterState(object):
ROUTER_STATE_LOGGER = logging.getLogger('pim.mld.RouterState')
def __init__(self, interface: 'InterfaceMLD'):
#logger
logger_extra = dict()
logger_extra['vif'] = interface.vif_index
logger_extra['interfacename'] = interface.interface_name
self.router_state_logger = logging.LoggerAdapter(RouterState.ROUTER_STATE_LOGGER, logger_extra)
# interface of the router connected to the network
self.interface = interface
# state of the router (Querier/NonQuerier)
self.interface_state = Querier
# state of each group
# Key: GroupIPAddress, Value: GroupState object
self.group_state = {}
self.group_state_lock = RWLockWrite()
# send general query
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(QUERY_INTERVAL, self.general_query_timeout)
timer.start()
self.general_query_timer = timer
# present timer
self.other_querier_present_timer = None
# Send packet via interface
def send(self, data: bytes, address: str):
self.interface.send(data, address)
############################################
# interface_state methods
############################################
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(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(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')
else:
self.interface_state = NonQuerier
self.router_state_logger.debug('change querier state to -> NonQuerier')
############################################
# 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]
with self.group_state_lock.genWlock():
if group_ip in self.group_state:
group_state = self.group_state[group_ip]
else:
group_state = GroupState(self, group_ip)
self.group_state[group_ip] = group_state
return group_state
def receive_report(self, packet: ReceivedPacket):
"""
Received MLD Report packet
"""
mld_group = packet.payload.group_address
self.get_group_state(mld_group).receive_report()
def receive_done(self, packet: ReceivedPacket):
"""
Received MLD Done packet
"""
mld_group = packet.payload.group_address
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:
max_response_time = packet.payload.max_resp_delay
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)
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
MULTICAST_LISTENER_QUERY_TYPE = 130
MULTICAST_LISTENER_REPORT_TYPE = 131
MULTICAST_LISTENER_DONE_TYPE = 132
\ No newline at end of file
from pimdm.utils import TYPE_CHECKING
from ..wrapper import NoListenersPresent
from ..wrapper import ListenersPresent
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('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)
# NOTIFY ROUTING - !!!!
group_state.notify_routing_remove()
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
from pimdm.utils import TYPE_CHECKING
from ..wrapper import NoListenersPresent
from ..wrapper import CheckingListeners
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('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)
# NOTIFY ROUTING - !!!!
group_state.notify_routing_remove()
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
from pimdm.utils import TYPE_CHECKING
from ..wrapper import ListenersPresent
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('NonQuerier NoListenersPresent: receive_report')
group_state.set_timer()
group_state.set_state(ListenersPresent)
# NOTIFY ROUTING + !!!!
group_state.notify_routing_add()
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 QUERY_RESPONSE_INTERVAL, LAST_LISTENER_QUERY_COUNT
from pimdm.packet.PacketMLDHeader import PacketMLDHeader
from pimdm.packet.ReceivedPacket import ReceivedPacket
from . import NoListenersPresent, ListenersPresent, CheckingListeners
if TYPE_CHECKING:
from ..RouterState import RouterState
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=QUERY_RESPONSE_INTERVAL * 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 NonQuerier and received a Query packet
"""
router_state.router_state_logger.debug('NonQuerier state: receive_query')
source_ip = packet.ip_header.ip_src
# if source ip of membership query not lower than the ip of the received interface => ignore
if IPv6Address(source_ip) >= IPv6Address(router_state.interface.get_ip()):
return
# reset other present querier timer
router_state.set_other_querier_present_timer()
# TODO ver se existe uma melhor maneira de fazer isto
@staticmethod
def state_name():
return "Non Querier"
@staticmethod
def get_group_membership_time(max_response_time: int):
"""
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 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)
# NOTIFY ROUTING - !!!!
group_state.notify_routing_remove()
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=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 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
group_state.set_timer(alternative=True)
group_state.set_retransmit_timer()
packet = PacketMLDHeader(type=PacketMLDHeader.MULTICAST_LISTENER_QUERY_TYPE,
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)
# NOTIFY ROUTING - !!!!
group_state.notify_routing_remove()
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
from pimdm.utils import TYPE_CHECKING
from ..wrapper import ListenersPresent
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 NoListenersPresent: receive_report')
group_state.set_timer()
group_state.set_state(ListenersPresent)
# NOTIFY ROUTING + !!!!
group_state.notify_routing_add()
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 LAST_LISTENER_QUERY_INTERVAL, LAST_LISTENER_QUERY_COUNT, QUERY_RESPONSE_INTERVAL
from pimdm.packet.PacketMLDHeader import PacketMLDHeader
from pimdm.packet.ReceivedPacket import ReceivedPacket
from . import CheckingListeners, ListenersPresent, NoListenersPresent
if TYPE_CHECKING:
from ..RouterState import RouterState
class Querier:
@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
# if source ip of membership query not lower than the ip of the received interface => ignore
if IPv6Address(source_ip) >= IPv6Address(router_state.interface.get_ip()):
return
# if source ip of membership query lower than the ip of the received interface => change state
# change state of interface
# Querier -> Non Querier
router_state.change_interface_state(querier=False)
# set other present querier timer
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
# TODO ver se existe uma melhor maneira de fazer isto
@staticmethod
def state_name():
return "Querier"
@staticmethod
def get_group_membership_time(max_response_time: int):
"""
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
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_checking_listeners_state()
def print_state():
return "CheckingListeners"
'''
def group_membership_timeout(group_state):
get_state(group_state).group_membership_timeout(group_state)
def group_membership_v1_timeout(group_state):
get_state(group_state).group_membership_v1_timeout(group_state)
def retransmit_timeout(group_state):
get_state(group_state).retransmit_timeout(group_state)
def receive_v1_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v1_membership_report(group_state, packet)
def receive_v2_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v2_membership_report(group_state, packet)
def receive_leave_group(group_state, packet: ReceivedPacket):
get_state(group_state).receive_leave_group(group_state, packet)
def receive_group_specific_query(group_state, packet: ReceivedPacket):
get_state(group_state).receive_group_specific_query(group_state, packet)
'''
\ No newline at end of file
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_listeners_present_state()
def print_state():
return "ListenersPresent"
'''
def group_membership_timeout(group_state):
get_state(group_state).group_membership_timeout(group_state)
def group_membership_v1_timeout(group_state):
get_state(group_state).group_membership_v1_timeout(group_state)
def retransmit_timeout(group_state):
get_state(group_state).retransmit_timeout(group_state)
def receive_v1_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v1_membership_report(group_state, packet)
def receive_v2_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v2_membership_report(group_state, packet)
def receive_leave_group(group_state, packet: ReceivedPacket):
get_state(group_state).receive_leave_group(group_state, packet)
def receive_group_specific_query(group_state, packet: ReceivedPacket):
get_state(group_state).receive_group_specific_query(group_state, packet)
'''
\ No newline at end of file
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_listeners_present_state()
def print_state():
return "NoListenersPresent"
'''
def group_membership_timeout(group_state):
get_state(group_state).group_membership_timeout(group_state)
def group_membership_v1_timeout(group_state):
get_state(group_state).group_membership_v1_timeout(group_state)
def retransmit_timeout(group_state):
get_state(group_state).retransmit_timeout(group_state)
def receive_v1_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v1_membership_report(group_state, packet)
def receive_v2_membership_report(group_state, packet: ReceivedPacket):
get_state(group_state).receive_v2_membership_report(group_state, packet)
def receive_leave_group(group_state, packet: ReceivedPacket):
get_state(group_state).receive_leave_group(group_state, packet)
def receive_group_specific_query(group_state, packet: ReceivedPacket):
get_state(group_state).receive_group_specific_query(group_state, packet)
'''
import struct
from pimdm.utils import checksum
import socket
from .PacketPayload import PacketPayload
'''
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Max Resp Time | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Group Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Resv |S| QRV | QQIC | Number of Sources (N) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address [1] |
+- -+
| Source Address [2] |
+- . -+
. . .
. . .
+- -+
| Source Address [N] |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
'''
class PacketIGMPHeader(PacketPayload):
IGMP_TYPE = 2
IGMP_HDR = "! BB H 4s"
IGMP_HDR_LEN = struct.calcsize(IGMP_HDR)
IGMP3_SRC_ADDR_HDR = "! BB H "
IGMP3_SRC_ADDR_HDR_LEN = struct.calcsize(IGMP3_SRC_ADDR_HDR)
IPv4_HDR = "! 4s"
IPv4_HDR_LEN = struct.calcsize(IPv4_HDR)
Membership_Query = 0x11
Version_2_Membership_Report = 0x16
Leave_Group = 0x17
Version_1_Membership_Report = 0x12
def __init__(self, type: int, max_resp_time: int, group_address: str="0.0.0.0"):
# todo check type
self.type = type
self.max_resp_time = max_resp_time
self.group_address = group_address
def get_igmp_type(self):
return self.type
def bytes(self) -> bytes:
# obter mensagem e criar checksum
msg_without_chcksum = struct.pack(PacketIGMPHeader.IGMP_HDR, self.type, self.max_resp_time, 0,
socket.inet_aton(self.group_address))
igmp_checksum = checksum(msg_without_chcksum)
msg = msg_without_chcksum[0:2] + struct.pack("! H", igmp_checksum) + msg_without_chcksum[4:]
return msg
def __len__(self):
return len(self.bytes())
@staticmethod
def parse_bytes(data: bytes):
#print("parseIGMPHdr: ", data)
igmp_hdr = data[0:PacketIGMPHeader.IGMP_HDR_LEN]
(type, max_resp_time, rcv_checksum, group_address) = struct.unpack(PacketIGMPHeader.IGMP_HDR, igmp_hdr)
#print(type, max_resp_time, rcv_checksum, group_address)
msg_to_checksum = data[0:2] + b'\x00\x00' + data[4:]
#print("checksum calculated: " + str(checksum(msg_to_checksum)))
if checksum(msg_to_checksum) != rcv_checksum:
#print("wrong checksum")
raise Exception("wrong checksum")
igmp_hdr = igmp_hdr[PacketIGMPHeader.IGMP_HDR_LEN:]
group_address = socket.inet_ntoa(group_address)
pkt = PacketIGMPHeader(type, max_resp_time, group_address)
return pkt
\ No newline at end of file
import struct
import socket
from .PacketPayload import PacketPayload
"""
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Maximum Response Delay | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Multicast Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
"""
class PacketMLDHeader(PacketPayload):
MLD_TYPE = 58
MLD_HDR = "! BB H H H 16s"
MLD_HDR_LEN = struct.calcsize(MLD_HDR)
MULTICAST_LISTENER_QUERY_TYPE = 130
MULTICAST_LISTENER_REPORT_TYPE = 131
MULTICAST_LISTENER_DONE_TYPE = 132
def __init__(self, type: int, max_resp_delay: int, group_address: str = "::"):
# todo check type
self.type = type
self.max_resp_delay = max_resp_delay
self.group_address = group_address
def get_mld_type(self):
return self.type
def bytes(self) -> bytes:
# obter mensagem e criar checksum
msg_without_chcksum = struct.pack(PacketMLDHeader.MLD_HDR, self.type, 0, 0, self.max_resp_delay, 0,
socket.inet_pton(socket.AF_INET6, self.group_address))
#mld_checksum = checksum(msg_without_chcksum)
#msg = msg_without_chcksum[0:2] + struct.pack("! H", mld_checksum) + msg_without_chcksum[4:]
# checksum handled by linux kernel
return msg_without_chcksum
def __len__(self):
return len(self.bytes())
@staticmethod
def parse_bytes(data: bytes):
mld_hdr = data[0:PacketMLDHeader.MLD_HDR_LEN]
if len(mld_hdr) < PacketMLDHeader.MLD_HDR_LEN:
raise Exception("MLD packet length is lower than expected")
(mld_type, _, _, max_resp_delay, _, group_address) = struct.unpack(PacketMLDHeader.MLD_HDR, mld_hdr)
# checksum is handled by linux kernel
mld_hdr = mld_hdr[PacketMLDHeader.MLD_HDR_LEN:]
group_address = socket.inet_ntop(socket.AF_INET6, group_address)
pkt = PacketMLDHeader(mld_type, max_resp_delay, group_address)
return pkt
import socket
from .Packet import Packet
from .PacketPimHeader import PacketPimHeader
from .PacketMLDHeader import PacketMLDHeader
from .PacketIGMPHeader import PacketIGMPHeader
from .PacketIpHeader import PacketIpv4Header, PacketIpv6Header
from pimdm.utils import TYPE_CHECKING
if TYPE_CHECKING:
......@@ -11,7 +9,7 @@ if TYPE_CHECKING:
class ReceivedPacket(Packet):
# choose payload protocol class based on ip protocol number
payload_protocol = {2: PacketIGMPHeader, 103: PacketPimHeader}
payload_protocol = {103: PacketPimHeader}
def __init__(self, raw_packet: bytes, interface: 'Interface'):
self.interface = interface
......@@ -28,7 +26,7 @@ class ReceivedPacket(Packet):
class ReceivedPacket_v6(Packet):
# choose payload protocol class based on ip protocol number
payload_protocol_v6 = {58: PacketMLDHeader, 103: PacketPimHeader}
payload_protocol_v6 = {103: PacketPimHeader}
def __init__(self, raw_packet: bytes, ancdata: list, src_addr: str, next_header: int, interface: 'Interface'):
self.interface = interface
......
......@@ -2,3 +2,5 @@ PrettyTable
netifaces
ipaddress
pyroute2
py-mld==1.0
igmp==1.0.2
\ No newline at end of file
......@@ -13,7 +13,7 @@ setup(
long_description=open("README.md", "r").read(),
long_description_content_type="text/markdown",
keywords="PIM-DM Multicast Routing Protocol PIM Dense-Mode Router RFC3973 IPv4 IPv6",
version="1.2",
version="1.3",
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