Skip to content

Commit

Permalink
Multiple VRFs (#4)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
pedrofran12 committed Jun 10, 2020
1 parent 023dcf9 commit abf2556
Show file tree
Hide file tree
Showing 31 changed files with 662 additions and 134 deletions.
11 changes: 6 additions & 5 deletions pimdm/InterfaceIGMP.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import netifaces
from pimdm.Interface import Interface
from pimdm.packet.ReceivedPacket import ReceivedPacket
from pimdm.igmp.igmp_globals import Version_1_Membership_Report, Version_2_Membership_Report, Leave_Group, Membership_Query
from pimdm.igmp.igmp_globals import VERSION_1_MEMBERSHIP_REPORT, VERSION_2_MEMBERSHIP_REPORT, LEAVE_GROUP, \
MEMBERSHIP_QUERY
if not hasattr(socket, 'SO_BINDTODEVICE'):
socket.SO_BINDTODEVICE = 25

Expand Down Expand Up @@ -101,10 +102,10 @@ def receive_unknown_type(self, packet):
return

PKT_FUNCTIONS = {
Version_1_Membership_Report: receive_version_1_membership_report,
Version_2_Membership_Report: receive_version_2_membership_report,
Leave_Group: receive_leave_group,
Membership_Query: receive_membership_query,
VERSION_1_MEMBERSHIP_REPORT: receive_version_1_membership_report,
VERSION_2_MEMBERSHIP_REPORT: receive_version_2_membership_report,
LEAVE_GROUP: receive_leave_group,
MEMBERSHIP_QUERY: receive_membership_query,
}

##################
Expand Down
15 changes: 15 additions & 0 deletions pimdm/Kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from pimdm import UnicastRouting, Main
from pimdm.rwlock.RWLock import RWLockWrite
from pimdm.tree.globals import MULTICAST_TABLE_ID

from pimdm.InterfaceMLD import InterfaceMLD
from pimdm.InterfaceIGMP import InterfaceIGMP
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down
7 changes: 4 additions & 3 deletions pimdm/Run.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

from pimdm import Main
from pimdm.daemon.Daemon import Daemon
from pimdm.tree.globals import MULTICAST_TABLE_ID

VERSION = "1.1"
VERSION = "1.1.1"


def client_socket(data_to_send):
Expand Down Expand Up @@ -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")
Expand Down
12 changes: 7 additions & 5 deletions pimdm/UnicastRouting.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from threading import RLock
from socket import if_indextoname
from pyroute2 import IPDB
from pimdm.tree.globals import UNICAST_TABLE_ID


def get_route(ip_dst: str):
Expand Down Expand Up @@ -64,17 +65,18 @@ def get_route(ip_dst: str):
dst_network = str(ipaddress.ip_interface(ip_dst + "/" + str(mask_len)).network)

print(dst_network)
if dst_network in ipdb.routes:
if dst_network in ipdb.routes.tables[UNICAST_TABLE_ID]:
print(info)
if ipdb.routes[{'dst': dst_network, 'family': family}]['ipdb_scope'] != 'gc':
info = ipdb.routes[dst_network]
if ipdb.routes[{'dst': dst_network, 'family': family,
'table': UNICAST_TABLE_ID}]['ipdb_scope'] != 'gc':
info = ipdb.routes[{'dst': dst_network, 'family': family, 'table': UNICAST_TABLE_ID}]
break
else:
continue
if not info:
print("0.0.0.0/0 or ::/0")
if "default" in ipdb.routes:
info = ipdb.routes[{'dst': 'default', 'family': family}]
if "default" in ipdb.routes.tables[UNICAST_TABLE_ID]:
info = ipdb.routes[{'dst': 'default', 'family': family, 'table': UNICAST_TABLE_ID}]
print(info)
return info

Expand Down
78 changes: 72 additions & 6 deletions pimdm/igmp/GroupState.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from pimdm.utils import TYPE_CHECKING
from .wrapper import NoMembersPresent
from .igmp_globals import GroupMembershipInterval, LastMemberQueryInterval
from .igmp_globals import GROUP_MEMBERSHIP_INTERVAL, LAST_MEMBER_QUERY_INTERVAL

if TYPE_CHECKING:
from .RouterState import RouterState
Expand Down Expand Up @@ -40,16 +40,22 @@ def print_state(self):
# Set state
###########################################
def set_state(self, state):
"""
Set membership state regarding this Group
"""
self.state = state
self.group_state_logger.debug("change membership state to: " + state.print_state())

###########################################
# Set timers
###########################################
def set_timer(self, alternative: bool=False, max_response_time: int=None):
def set_timer(self, alternative: bool = False, max_response_time: int = None):
"""
Set timer
"""
self.clear_timer()
if not alternative:
time = GroupMembershipInterval
time = GROUP_MEMBERSHIP_INTERVAL
else:
time = self.router_state.interface_state.get_group_membership_time(max_response_time)

Expand All @@ -58,98 +64,158 @@ def set_timer(self, alternative: bool=False, max_response_time: int=None):
self.timer = timer

def clear_timer(self):
"""
Stop timer
"""
if self.timer is not None:
self.timer.cancel()

def set_v1_host_timer(self):
"""
Set v1 host timer
"""
self.clear_v1_host_timer()
v1_host_timer = Timer(GroupMembershipInterval, self.group_membership_v1_timeout)
v1_host_timer = Timer(GROUP_MEMBERSHIP_INTERVAL, self.group_membership_v1_timeout)
v1_host_timer.start()
self.v1_host_timer = v1_host_timer

def clear_v1_host_timer(self):
"""
Stop v1 host timer
"""
if self.v1_host_timer is not None:
self.v1_host_timer.cancel()

def set_retransmit_timer(self):
"""
Set retransmit timer
"""
self.clear_retransmit_timer()
retransmit_timer = Timer(LastMemberQueryInterval, self.retransmit_timeout)
retransmit_timer = Timer(LAST_MEMBER_QUERY_INTERVAL, self.retransmit_timeout)
retransmit_timer.start()
self.retransmit_timer = retransmit_timer

def clear_retransmit_timer(self):
"""
Stop retransmit timer
"""
if self.retransmit_timer is not None:
self.retransmit_timer.cancel()


###########################################
# Get group state from specific interface state
###########################################
def get_interface_group_state(self):
"""
Get specific implementation of the membership state machine (depending on the querier state machine)
"""
return self.state.get_state(self.router_state)

###########################################
# Timer timeout
###########################################
def group_membership_timeout(self):
"""
Timer has expired
"""
with self.lock:
self.get_interface_group_state().group_membership_timeout(self)

def group_membership_v1_timeout(self):
"""
v1 host timer has expired
"""
with self.lock:
self.get_interface_group_state().group_membership_v1_timeout(self)

def retransmit_timeout(self):
"""
Retransmit timer has expired
"""
with self.lock:
self.get_interface_group_state().retransmit_timeout(self)

###########################################
# 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()
Expand Down
Loading

0 comments on commit abf2556

Please sign in to comment.