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

IPv6 multicast routing support (#3)

parent f6ac9c82
......@@ -8,11 +8,14 @@ We have implemented PIM-DM specification ([RFC3973](https://tools.ietf.org/html/
This repository stores the implementation of this protocol. The implementation is written in Python language and is destined to Linux systems.
Additionally, IGMPv2 and MLDv1 are implemented alongside with PIM-DM to detect interest of hosts.
# Requirements
- Linux machine
- Python3 (we have written all code to be compatible with at least Python v3.2)
- Unicast routing protocol
- Python3 (we have written all code to be compatible with at least Python v3.3)
- pip (to install all dependencies)
- tcpdump
......@@ -41,6 +44,8 @@ In order to start the protocol you first need to explicitly start it. This will
sudo pim-dm -start
```
IPv4 and IPv6 multicast is supported. By default all commands will be executed on IPv4 daemon. To execute a command on the IPv6 daemon use `-6`.
#### Add interface
......@@ -49,21 +54,27 @@ After starting the protocol process you can enable the protocol in specific inte
- To enable PIM-DM without State-Refresh, in a given interface, you need to run the following command:
```
sudo pim-dm -ai INTERFACE_NAME
sudo pim-dm -ai INTERFACE_NAME [-4 | -6]
```
- To enable PIM-DM with State-Refresh, in a given interface, you need to run the following command:
```
sudo pim-dm -aisf INTERFACE_NAME
sudo pim-dm -aisf INTERFACE_NAME [-4 | -6]
```
- To enable IGMP in a given interface, you need to run the following command:
- To enable IGMP/MLD in a given interface, you need to run the following command:
- IGMP:
```
sudo pim-dm -aiigmp INTERFACE_NAME
```
- MLD:
```
sudo pim-dm -aimld INTERFACE_NAME
```
#### Remove interface
To remove a previously added interface, you need run the following commands:
......@@ -71,15 +82,20 @@ To remove a previously added interface, you need run the following commands:
- To remove a previously added PIM-DM interface:
```
sudo pim-dm -ri INTERFACE_NAME
sudo pim-dm -ri INTERFACE_NAME [-4 | -6]
```
- To remove a previously added IGMP interface:
- To remove a previously added IGMP/MLD interface:
- IGMP:
```
sudo pim-dm -riigmp INTERFACE_NAME
```
- MLD:
```
sudo pim-dm -rimld INTERFACE_NAME
```
#### Stop protocol process
......@@ -96,31 +112,31 @@ We have built some list commands that can be used to check the "internals" of th
- #### List interfaces:
Show all router interfaces and which ones have PIM-DM and IGMP enabled. For IGMP enabled interfaces check the IGMP Querier state.
Show all router interfaces and which ones have PIM-DM and IGMP/MLD enabled. For IGMP/MLD enabled interfaces you can check the Querier state.
```
sudo pim-dm -li
sudo pim-dm -li [-4 | -6]
```
- #### List neighbors
Verify neighbors that have established a neighborhood relationship.
```
sudo pim-dm -ln
sudo pim-dm -ln [-4 | -6]
```
- #### List state
List all state machines and corresponding state of all trees that are being monitored. Also list IGMP state for each group being monitored.
```
sudo pim-dm -ls
sudo pim-dm -ls [-4 | -6]
```
- #### Multicast Routing Table
List Linux Multicast Routing Table (equivalent to `ip mroute -show`)
List Linux Multicast Routing Table (equivalent to `ip mroute show`)
```
sudo pim-dm -mr
sudo pim-dm -mr [-4 | -6]
```
......@@ -131,15 +147,10 @@ In order to determine which commands and corresponding arguments are available y
pim-dm -h
```
or
```
pim-dm --help
```
## Change settings
Files tree/globals.py and igmp/igmp_globals.py store all timer values and some configurations regarding IGMP and the PIM-DM. If you want to tune the implementation, you can change the values of these files. These configurations are used by all interfaces, meaning that there is no tuning per interface.
Files tree/globals.py, igmp/igmp_globals.py and mld/mld_globals.py store all timer values and some configurations regarding PIM-DM, IGMP and MLD. If you want to tune the implementation, you can change the values of these files. These configurations are used by all interfaces, meaning that there is no tuning per interface.
## Tests
......@@ -151,4 +162,4 @@ We have performed tests to our PIM-DM implementation. You can check on the corre
- [Test_PIM_Assert](https://github.com/pedrofran12/pim_dm/tree/Test_PIM_Assert) - Topology used to test the election of the AssertWinner.
- [Test_PIM_Join_Prune_Graft](https://github.com/pedrofran12/pim_dm/tree/Test_PIM_Join_Prune_Graft) - Topology used to test the Pruning and Grafting of the multicast distribution tree.
- [Test_PIM_StateRefresh](https://github.com/pedrofran12/pim_dm/tree/Test_PIM_StateRefresh) - Topology used to test PIM-DM State Refresh.
- [Test_IGMP](https://github.com/pedrofran12/hpim_dm/tree/Test_IGMP) - Topology used to test our IGMPv2 implementation.
- [Test_IGMP](https://github.com/pedrofran12/pim_dm/tree/Test_IGMP) - Topology used to test our IGMPv2 implementation.
......@@ -18,8 +18,11 @@ class Interface(metaclass=ABCMeta):
self._recv_socket = recv_socket
self.interface_enabled = False
def _enable(self):
"""
Enable this interface
This will start a thread to be executed in the background to be used in the reception of control packets
"""
self.interface_enabled = True
# run receive method in background
receive_thread = threading.Thread(target=self.receive)
......@@ -27,24 +30,39 @@ class Interface(metaclass=ABCMeta):
receive_thread.start()
def receive(self):
"""
Method that will be executed in the background for the reception of control packets
"""
while self.interface_enabled:
try:
(raw_bytes, _) = self._recv_socket.recvfrom(256 * 1024)
(raw_bytes, ancdata, _, src_addr) = self._recv_socket.recvmsg(256 * 1024, 500)
if raw_bytes:
self._receive(raw_bytes)
self._receive(raw_bytes, ancdata, src_addr)
except Exception:
traceback.print_exc()
continue
@abstractmethod
def _receive(self, raw_bytes):
def _receive(self, raw_bytes, ancdata, src_addr):
"""
Subclass method to be implemented
This method will be invoked whenever a new control packet is received
"""
raise NotImplementedError
def send(self, data: bytes, group_ip: str):
"""
Send a control packet through this interface
Explicitly destined to group_ip (can be unicast or multicast IP)
"""
if self.interface_enabled and data:
self._send_socket.sendto(data, (group_ip, 0))
def remove(self):
"""
This interface is no longer active....
Clear all state regarding it
"""
self.interface_enabled = False
try:
self._recv_socket.shutdown(socket.SHUT_RDWR)
......@@ -54,8 +72,14 @@ class Interface(metaclass=ABCMeta):
self._send_socket.close()
def is_enabled(self):
"""
Verify if this interface is enabled
"""
return self.interface_enabled
@abstractmethod
def get_ip(self):
"""
Get IP of this interface
"""
raise NotImplementedError
......@@ -4,7 +4,7 @@ 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.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
......@@ -48,18 +48,20 @@ class InterfaceIGMP(Interface):
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):
def _receive(self, raw_bytes, ancdata, src_addr):
if raw_bytes:
raw_bytes = raw_bytes[14:]
packet = ReceivedPacket(raw_bytes, self)
......@@ -91,7 +93,8 @@ class InterfaceIGMP(Interface):
def receive_membership_query(self, packet):
ip_dst = packet.ip_header.ip_dst
igmp_group = packet.payload.group_address
if ip_dst == igmp_group or (ip_dst == "224.0.0.1" and igmp_group == "0.0.0.0"):
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):
......
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()
This diff is collapsed.
import socket
import random
import struct
import logging
import ipaddress
import netifaces
from pimdm import Main
from socket import if_nametoindex
from pimdm.Interface import Interface
from .InterfacePIM import InterfacePim
from pimdm.rwlock.RWLock import RWLockWrite
from pimdm.packet.ReceivedPacket import ReceivedPacket_v6
class InterfacePim6(InterfacePim):
MCAST_GRP = "ff02::d"
def __init__(self, interface_name: str, vif_index:int, state_refresh_capable:bool=False):
# generation id
self.generation_id = random.getrandbits(32)
# When PIM is enabled on an interface or when a router first starts, the Hello Timer (HT)
# MUST be set to random value between 0 and Triggered_Hello_Delay
self.hello_timer = None
# state refresh capable
self._state_refresh_capable = state_refresh_capable
self._neighbors_state_refresh_capable = False
# todo: lan delay enabled
self._lan_delay_enabled = False
# todo: propagation delay
self._propagation_delay = self.PROPAGATION_DELAY
# todo: override interval
self._override_interval = self.OVERRIDE_INTERNAL
# pim neighbors
self._had_neighbors = False
self.neighbors = {}
self.neighbors_lock = RWLockWrite()
self.interface_logger = logging.LoggerAdapter(InterfacePim.LOGGER, {'vif': vif_index, 'interfacename': interface_name})
# SOCKET
s = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_PIM)
ip_interface = ""
for if_addr in netifaces.ifaddresses(interface_name)[netifaces.AF_INET6]:
ip_interface = if_addr["addr"]
if ipaddress.IPv6Address(if_addr['addr'].split("%")[0]).is_link_local:
ip_interface = if_addr['addr'].split("%")[0]
# bind to interface
s.bind(socket.getaddrinfo(if_addr['addr'], None, 0, socket.SOCK_RAW, 0, socket.AI_PASSIVE)[0][4])
break
self.ip_interface = ip_interface
# allow other sockets to bind this port too
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# explicitly join the multicast group on the interface specified
if_index = if_nametoindex(interface_name)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP,
socket.inet_pton(socket.AF_INET6, InterfacePim6.MCAST_GRP) + struct.pack('@I', if_index))
s.setsockopt(socket.SOL_SOCKET, 25, str(interface_name + '\0').encode('utf-8'))
# set socket output interface
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, struct.pack('@I', if_index))
# set socket TTL to 1
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 1)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_UNICAST_HOPS, 1)
# don't receive outgoing packets
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, 0)
Interface.__init__(self, interface_name, s, s, vif_index)
Interface._enable(self)
self.force_send_hello()
@staticmethod
def get_kernel():
return Main.kernel_v6
def send(self, data: bytes, group_ip: str=MCAST_GRP):
super().send(data=data, group_ip=group_ip)
def _receive(self, raw_bytes, ancdata, src_addr):
if raw_bytes:
packet = ReceivedPacket_v6(raw_bytes, ancdata, src_addr, 103, self)
self.PKT_FUNCTIONS[packet.payload.get_pim_type()](self, packet)
This diff is collapsed.
This diff is collapsed.
from threading import Timer
import time
from pimdm.utils import HELLO_HOLD_TIME_NO_TIMEOUT, HELLO_HOLD_TIME_TIMEOUT, TYPE_CHECKING
from threading import Lock, RLock
import logging
from threading import Timer
from threading import Lock, RLock
from pimdm.tree.globals import HELLO_HOLD_TIME_NO_TIMEOUT, HELLO_HOLD_TIME_TIMEOUT
from pimdm.utils import TYPE_CHECKING
if TYPE_CHECKING:
from pimdm.InterfacePIM import InterfacePim
......@@ -10,7 +12,6 @@ if TYPE_CHECKING:
class Neighbor:
LOGGER = logging.getLogger('pim.Interface.Neighbor')
def __init__(self, contact_interface: "InterfacePim", ip, generation_id: int, hello_hold_time: int,
state_refresh_capable: bool):
if hello_hold_time == HELLO_HOLD_TIME_TIMEOUT:
......@@ -37,7 +38,6 @@ class Neighbor:
self.tree_interface_nlt_subscribers = []
self.tree_interface_nlt_subscribers_lock = RLock()
def set_hello_hold_time(self, hello_hold_time: int):
self.hello_hold_time = hello_hold_time
if self.neighbor_liveness_timer is not None:
......@@ -85,11 +85,9 @@ class Neighbor:
for tree_if in self.tree_interface_nlt_subscribers:
tree_if.assert_winner_nlt_expires()
def reset(self):
self.contact_interface.new_or_reset_neighbor(self.ip)
def receive_hello(self, generation_id, hello_hold_time, state_refresh_capable):
self.neighbor_logger.debug('Receive Hello message with HelloHoldTime: ' + str(hello_hold_time) +
'; GenerationID: ' + str(generation_id) + '; StateRefreshCapable: ' +
......
import struct
import socket
'''
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
'''
class PacketIpHeader:
IP_HDR = "! BBH HH BBH 4s 4s"
IP_HDR_LEN = struct.calcsize(IP_HDR)
def __init__(self, ver, hdr_len, ttl, proto, ip_src, ip_dst):
self.version = ver
self.hdr_length = hdr_len
self.ttl = ttl
self.proto = proto
self.ip_src = ip_src
self.ip_dst = ip_dst
def __len__(self):
return self.hdr_length
@staticmethod
def parse_bytes(data: bytes):
(verhlen, tos, iplen, ipid, frag, ttl, proto, cksum, src, dst) = \
struct.unpack(PacketIpHeader.IP_HDR, data)
ver = (verhlen & 0xf0) >> 4
hlen = (verhlen & 0x0f) * 4
'''
"VER": ver,
"HLEN": hlen,
"TOS": tos,
"IPLEN": iplen,
"IPID": ipid,
"FRAG": frag,
"TTL": ttl,
"PROTO": proto,
"CKSUM": cksum,
"SRC": socket.inet_ntoa(src),
"DST": socket.inet_ntoa(dst)
'''
src_ip = socket.inet_ntoa(src)
dst_ip = socket.inet_ntoa(dst)
return PacketIpHeader(ver, hlen, ttl, proto, src_ip, dst_ip)
#!/usr/bin/env python3
from pimdm.Daemon.Daemon import Daemon
from pimdm import Main
import _pickle as pickle
import socket
import sys
import os
import sys
import socket
import argparse
import traceback
import _pickle as pickle
from pimdm import Main
from pimdm.daemon.Daemon import Daemon
VERSION = "1.1"
VERSION = "1.0.4.2"
def client_socket(data_to_send):
# Create a UDS socket
......@@ -58,26 +60,36 @@ class MyDaemon(Daemon):
print(sys.stderr, 'sending data back to the client')
print(pickle.loads(data))
args = pickle.loads(data)
if 'ipv4' not in args and 'ipv6' not in args or not (args.ipv4 or args.ipv6):
args.ipv4 = True
args.ipv6 = False
if 'list_interfaces' in args and args.list_interfaces:
connection.sendall(pickle.dumps(Main.list_enabled_interfaces()))
connection.sendall(pickle.dumps(Main.list_enabled_interfaces(ipv4=args.ipv4, ipv6=args.ipv6)))
elif 'list_neighbors' in args and args.list_neighbors:
connection.sendall(pickle.dumps(Main.list_neighbors()))
connection.sendall(pickle.dumps(Main.list_neighbors(ipv4=args.ipv4, ipv6=args.ipv6)))
elif 'list_state' in args and args.list_state:
connection.sendall(pickle.dumps(Main.list_state()))
connection.sendall(pickle.dumps(Main.list_state(ipv4=args.ipv4, ipv6=args.ipv6)))
elif 'add_interface' in args and args.add_interface:
Main.add_pim_interface(args.add_interface[0], False)
Main.add_pim_interface(args.add_interface[0], False, ipv4=args.ipv4, ipv6=args.ipv6)
connection.shutdown(socket.SHUT_RDWR)
elif 'add_interface_sr' in args and args.add_interface_sr:
Main.add_pim_interface(args.add_interface_sr[0], True)
Main.add_pim_interface(args.add_interface_sr[0], True, ipv4=args.ipv4, ipv6=args.ipv6)
connection.shutdown(socket.SHUT_RDWR)
elif 'add_interface_igmp' in args and args.add_interface_igmp:
Main.add_igmp_interface(args.add_interface_igmp[0])
Main.add_membership_interface(interface_name=args.add_interface_igmp[0], ipv4=True, ipv6=False)
connection.shutdown(socket.SHUT_RDWR)
elif 'add_interface_mld' in args and args.add_interface_mld:
Main.add_membership_interface(interface_name=args.add_interface_mld[0], ipv4=False, ipv6=True)
connection.shutdown(socket.SHUT_RDWR)
elif 'remove_interface' in args and args.remove_interface:
Main.remove_interface(args.remove_interface[0], pim=True)
Main.remove_interface(args.remove_interface[0], pim=True, ipv4=args.ipv4, ipv6=args.ipv6)
connection.shutdown(socket.SHUT_RDWR)
elif 'remove_interface_igmp' in args and args.remove_interface_igmp:
Main.remove_interface(args.remove_interface_igmp[0], igmp=True)
Main.remove_interface(args.remove_interface_igmp[0], membership=True, ipv4=True, ipv6=False)
connection.shutdown(socket.SHUT_RDWR)
elif 'remove_interface_mld' in args and args.remove_interface_mld:
Main.remove_interface(args.remove_interface_mld[0], membership=True, ipv4=False, ipv6=True)
connection.shutdown(socket.SHUT_RDWR)
elif 'stop' in args and args.stop:
Main.stop()
......@@ -102,18 +114,30 @@ def main():
group.add_argument("-start", "--start", action="store_true", default=False, help="Start PIM")
group.add_argument("-stop", "--stop", action="store_true", default=False, help="Stop PIM")
group.add_argument("-restart", "--restart", action="store_true", default=False, help="Restart PIM")
group.add_argument("-li", "--list_interfaces", action="store_true", default=False, help="List All PIM Interfaces")
group.add_argument("-ln", "--list_neighbors", action="store_true", default=False, help="List All PIM Neighbors")
group.add_argument("-ls", "--list_state", action="store_true", default=False, help="List state of IGMP")
group.add_argument("-mr", "--multicast_routes", action="store_true", default=False, help="List Multicast Routing table")
group.add_argument("-ai", "--add_interface", nargs=1, metavar='INTERFACE_NAME', help="Add PIM interface")
group.add_argument("-aisr", "--add_interface_sr", nargs=1, metavar='INTERFACE_NAME', help="Add PIM interface with State Refresh enabled")
group.add_argument("-li", "--list_interfaces", action="store_true", default=False, help="List All PIM Interfaces. "
"Use -4 or -6 to specify IPv4 or IPv6 interfaces.")
group.add_argument("-ln", "--list_neighbors", action="store_true", default=False, help="List All PIM Neighbors. "
"Use -4 or -6 to specify IPv4 or IPv6 PIM neighbors.")
group.add_argument("-ls", "--list_state", action="store_true", default=False, help="List IGMP/MLD and PIM-DM state machines."
" Use -4 or -6 to specify IPv4 or IPv6 state respectively.")
group.add_argument("-mr", "--multicast_routes", action="store_true", default=False, help="List Multicast Routing table. "
"Use -4 or -6 to specify IPv4 or IPv6 multicast routing table.")
group.add_argument("-ai", "--add_interface", nargs=1, metavar='INTERFACE_NAME', help="Add PIM interface. "
"Use -4 or -6 to specify IPv4 or IPv6 interface.")
group.add_argument("-aisr", "--add_interface_sr", nargs=1, metavar='INTERFACE_NAME', help="Add PIM interface with State Refresh enabled. "
"Use -4 or -6 to specify IPv4 or IPv6 interface.")
group.add_argument("-aiigmp", "--add_interface_igmp", nargs=1, metavar='INTERFACE_NAME', help="Add IGMP interface")
group.add_argument("-ri", "--remove_interface", nargs=1, metavar='INTERFACE_NAME', help="Remove PIM interface")
group.add_argument("-aimld", "--add_interface_mld", nargs=1, metavar='INTERFACE_NAME', help="Add MLD interface")
group.add_argument("-ri", "--remove_interface", nargs=1, metavar='INTERFACE_NAME', help="Remove PIM interface. "
"Use -4 or -6 to specify IPv4 or IPv6 interface.")
group.add_argument("-riigmp", "--remove_interface_igmp", nargs=1, metavar='INTERFACE_NAME', help="Remove IGMP interface")
group.add_argument("-rimld", "--remove_interface_mld", nargs=1, metavar='INTERFACE_NAME', help="Remove MLD interface")
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("--version", action='version', version='%(prog)s ' + VERSION)
group_ipversion = parser.add_mutually_exclusive_group(required=False)
group_ipversion.add_argument("-4", "--ipv4", action="store_true", default=False, help="Setting for IPv4")
group_ipversion.add_argument("-6", "--ipv6", action="store_true", default=False, help="Setting for IPv6")
args = parser.parse_args()
#print(parser.parse_args())
......@@ -137,7 +161,10 @@ def main():
os.system("tail -f /var/log/pimdm/stdout")
sys.exit(0)
elif args.multicast_routes:
os.system("ip mroute show")
if args.ipv4 or not args.ipv6:
os.system("ip mroute show")
elif args.ipv6:
os.system("ip -6 mroute show")
sys.exit(0)
elif not daemon.is_running():
print("PIM-DM is not running")
......
import socket
import ipaddress
from pyroute2 import IPDB
from threading import RLock
from pimdm.utils import if_indextoname
from socket import if_indextoname
from pyroute2 import IPDB
def get_route(ip_dst: str):
......@@ -48,27 +47,34 @@ class UnicastRouting(object):
@staticmethod
def get_route(ip_dst: str):
ip_bytes = socket.inet_aton(ip_dst)
ip_int = int.from_bytes(ip_bytes, byteorder='big')
ip_version = ipaddress.ip_address(ip_dst).version
if ip_version == 4:
family = socket.AF_INET
full_mask = 32
elif ip_version == 6:
family = socket.AF_INET6
full_mask = 128
else:
raise Exception("Unknown IP version")
info = None
with UnicastRouting.lock:
ipdb = UnicastRouting.ipdb # type:IPDB
for mask_len in range(32, 0, -1):
ip_bytes = (ip_int & (0xFFFFFFFF << (32 - mask_len))).to_bytes(4, "big")
ip_dst = socket.inet_ntoa(ip_bytes) + "/" + str(mask_len)
print(ip_dst)
if ip_dst in ipdb.routes:
for mask_len in range(full_mask, 0, -1):
dst_network = str(ipaddress.ip_interface(ip_dst + "/" + str(mask_len)).network)
print(dst_network)
if dst_network in ipdb.routes:
print(info)
if ipdb.routes[ip_dst]['ipdb_scope'] != 'gc':
info = ipdb.routes[ip_dst]
if ipdb.routes[{'dst': dst_network, 'family': family}]['ipdb_scope'] != 'gc':
info = ipdb.routes[dst_network]
break
else:
continue
if not info:
print("0.0.0.0/0")
print("0.0.0.0/0 or ::/0")
if "default" in ipdb.routes:
info = ipdb.routes["default"]
info = ipdb.routes[{'dst': 'default', 'family': family}]
print(info)
return info
......@@ -85,13 +91,16 @@ class UnicastRouting(object):
oif = unicast_route.get("oif")
next_hop = unicast_route["gateway"]
multipaths = unicast_route["multipath"]
# prefsrc = unicast_route.get("prefsrc")
#prefsrc = unicast_route.get("prefsrc")
# rpf_node = ip_dst if (next_hop is None and prefsrc is not None) else next_hop
#rpf_node = ip_dst if (next_hop is None and prefsrc is not None) else next_hop
rpf_node = next_hop if next_hop is not None else ip_dst
highest_ip = ipaddress.ip_address("0.0.0.0")
if ipaddress.ip_address(ip_dst).version == 4:
highest_ip = ipaddress.ip_address("0.0.0.0")
else:
highest_ip = ipaddress.ip_address("::")
for m in multipaths:
if m["gateway"] is None:
if m.get("gateway", None) is None:
oif = m.get('oif')
rpf_node = ip_dst
break
......@@ -107,14 +116,22 @@ class UnicastRouting(object):
interface_name = None if oif is None else if_indextoname(int(oif))
from pimdm import Main
rpf_if = Main.kernel.vif_name_to_index_dic.get(interface_name)
if ipaddress.ip_address(ip_dst).version == 4:
rpf_if = Main.kernel.vif_name_to_index_dic.get(interface_name)
else:
rpf_if = Main.kernel_v6.vif_name_to_index_dic.get(interface_name)
return (metric_administrative_distance, metric_cost, rpf_node, rpf_if, mask)
@staticmethod
def unicast_changes(ipdb, msg, action):
"""
Kernel notified about a change
Verify the type of change and recheck all trees if necessary
"""
print("unicast change?")
print(action)
UnicastRouting.lock.acquire()
family = msg['family']
if action == "RTM_NEWROUTE" or action == "RTM_DELROUTE":
print(ipdb.routes)
mask_len = msg["dst_len"]
......@@ -126,8 +143,10 @@ class UnicastRouting(object):
if key == "RTA_DST":
network_address = value
break
if network_address is None:
if network_address is None and family == socket.AF_INET:
network_address = "0.0.0.0"
elif network_address is None and family == socket.AF_INET6:
network_address = "::"
print(network_address)
print(mask_len)
print(network_address + "/" + str(mask_len))
......@@ -135,7 +154,10 @@ class UnicastRouting(object):
print(str(subnet))
UnicastRouting.lock.release()
from pimdm import Main
Main.kernel.notify_unicast_changes(subnet)
if family == socket.AF_INET:
Main.kernel.notify_unicast_changes(subnet)
elif family == socket.AF_INET6:
Main.kernel_v6.notify_unicast_changes(subnet)
'''
elif action == "RTM_NEWADDR" or action == "RTM_DELADDR":
print(action)
......@@ -154,7 +176,7 @@ class UnicastRouting(object):
import traceback
traceback.print_exc()
pass
bnet = ipaddress.ip_network("0.0.0.0/0")
subnet = ipaddress.ip_network("0.0.0.0/0")
Main.kernel.notify_unicast_changes(subnet)
elif action == "RTM_NEWLINK" or action == "RTM_DELLINK":
attrs = msg["attrs"]
......@@ -172,7 +194,7 @@ class UnicastRouting(object):
print(if_name + ": " + operation)
UnicastRouting.lock.release()
if operation == 'DOWN':
Main.kernel.remove_interface(if_name, igmp=True, pim=True)
Main.kernel.remove_interface(if_name, membership=True, pim=True)
subnet = ipaddress.ip_network("0.0.0.0/0")
Main.kernel.notify_unicast_changes(subnet)
'''
......@@ -180,6 +202,10 @@ class UnicastRouting(object):
UnicastRouting.lock.release()
def stop(self):
"""
No longer monitor unicast changes....
Invoked whenever the protocol is stopped
"""
if self._ipdb:
self._ipdb.release()
if UnicastRouting.ipdb:
......
......@@ -129,13 +129,13 @@ class GroupState(object):
with self.multicast_interface_state_lock:
print("notify+", self.multicast_interface_state)
for interface_state in self.multicast_interface_state:
interface_state.notify_igmp(has_members=True)
interface_state.notify_membership(has_members=True)
def notify_routing_remove(self):
with self.multicast_interface_state_lock:
print("notify-", self.multicast_interface_state)
for interface_state in self.multicast_interface_state:
interface_state.notify_igmp(has_members=False)
interface_state.notify_membership(has_members=False)
def add_multicast_routing_entry(self, kernel_entry):
with self.multicast_interface_state_lock:
......@@ -155,5 +155,5 @@ class GroupState(object):
self.clear_timer()
self.clear_v1_host_timer()
for interface_state in self.multicast_interface_state:
interface_state.notify_igmp(has_members=False)
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.packet.PacketIGMPHeader import PacketIGMPHeader
from pimdm.packet.ReceivedPacket import ReceivedPacket
from pimdm.utils import TYPE_CHECKING
from pimdm.RWLock.RWLock import RWLockWrite
from pimdm.rwlock.RWLock import RWLockWrite
from .querier.Querier import Querier
from .nonquerier.NonQuerier import NonQuerier
from .GroupState import GroupState
......
from ipaddress import IPv4Address
from pimdm.utils import TYPE_CHECKING
from ..igmp_globals import Membership_Query, QueryResponseInterval, LastMemberQueryCount
from pimdm.Packet.PacketIGMPHeader import PacketIGMPHeader
from pimdm.Packet.ReceivedPacket import ReceivedPacket
from pimdm.packet.PacketIGMPHeader import PacketIGMPHeader
from pimdm.packet.ReceivedPacket import ReceivedPacket
from . import NoMembersPresent, MembersPresent, CheckingMembership
if TYPE_CHECKING:
......
from pimdm.Packet.PacketIGMPHeader import PacketIGMPHeader
from pimdm.packet.PacketIGMPHeader import PacketIGMPHeader
from pimdm.utils import TYPE_CHECKING
from ..igmp_globals import Membership_Query, LastMemberQueryInterval
from ..wrapper import NoMembersPresent, MembersPresent, Version1MembersPresent
......
from pimdm.Packet.PacketIGMPHeader import PacketIGMPHeader
from pimdm.packet.PacketIGMPHeader import PacketIGMPHeader
from pimdm.utils import TYPE_CHECKING
from ..igmp_globals import Membership_Query, LastMemberQueryInterval
from ..wrapper import Version1MembersPresent, CheckingMembership, NoMembersPresent
......
......@@ -3,8 +3,8 @@ from ipaddress import IPv4Address
from pimdm.utils import TYPE_CHECKING
from ..igmp_globals import Membership_Query, QueryResponseInterval, LastMemberQueryCount, LastMemberQueryInterval
from pimdm.Packet.PacketIGMPHeader import PacketIGMPHeader
from pimdm.Packet.ReceivedPacket import ReceivedPacket
from pimdm.packet.PacketIGMPHeader import PacketIGMPHeader
from pimdm.packet.ReceivedPacket import ReceivedPacket
from . import CheckingMembership, MembersPresent, Version1MembersPresent, NoMembersPresent
if TYPE_CHECKING:
......
import logging
from threading import Lock
from threading import Timer
from pimdm.utils import TYPE_CHECKING
from .wrapper import NoListenersPresent
from .mld_globals import MulticastListenerInterval, LastListenerQueryInterval
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):
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):
self.clear_timer()
if not alternative:
time = MulticastListenerInterval
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):
if self.timer is not None:
self.timer.cancel()
def set_retransmit_timer(self):
self.clear_retransmit_timer()
retransmit_timer = Timer(LastListenerQueryInterval, self.retransmit_timeout)
retransmit_timer.start()
self.retransmit_timer = retransmit_timer
def clear_retransmit_timer(self):
if self.retransmit_timer is not None:
self.retransmit_timer.cancel()
###########################################
# Get group state from specific interface state
###########################################
def get_interface_group_state(self):
return self.state.get_state(self.router_state)
###########################################
# Timer timeout
###########################################
def group_membership_timeout(self):
with self.lock:
self.get_interface_group_state().group_membership_timeout(self)
def retransmit_timeout(self):
with self.lock:
self.get_interface_group_state().retransmit_timeout(self)
###########################################
# Receive Packets
###########################################
def receive_report(self):
with self.lock:
self.get_interface_group_state().receive_report(self)
def receive_done(self):
with self.lock:
self.get_interface_group_state().receive_done(self)
def receive_group_specific_query(self, max_response_time: int):
with self.lock:
self.get_interface_group_state().receive_group_specific_query(self, max_response_time)
###########################################
# Notify Routing
###########################################
def notify_routing_add(self):
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):
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):
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):
with self.multicast_interface_state_lock:
self.multicast_interface_state.remove(kernel_entry)
def has_members(self):
return self.state is not NoListenersPresent
def remove(self):
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 QueryResponseInterval, QueryInterval, OtherQuerierPresentInterval, 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=QueryResponseInterval*1000)
self.interface.send(packet.bytes())
# set initial general query timer
timer = Timer(QueryInterval, 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):
self.clear_general_query_timer()
general_query_timer = Timer(QueryInterval, self.general_query_timeout)
general_query_timer.start()
self.general_query_timer = general_query_timer
def clear_general_query_timer(self):
if self.general_query_timer is not None:
self.general_query_timer.cancel()
def set_other_querier_present_timer(self):
self.clear_other_querier_present_timer()
other_querier_present_timer = Timer(OtherQuerierPresentInterval, 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):
if self.other_querier_present_timer is not None:
self.other_querier_present_timer.cancel()
def general_query_timeout(self):
self.interface_state.general_query_timeout(self)
def other_querier_present_timeout(self):
self.interface_state.other_querier_present_timeout(self)
def change_interface_state(self, querier: bool):
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):
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):
mld_group = packet.payload.group_address
#if igmp_group not in self.group_state:
# self.group_state[igmp_group] = GroupState(self, igmp_group)
#self.group_state[igmp_group].receive_v2_membership_report()
self.get_group_state(mld_group).receive_report()
def receive_done(self, packet: ReceivedPacket):
mld_group = packet.payload.group_address
#if igmp_group in self.group_state:
# self.group_state[igmp_group].receive_leave_group()
self.get_group_state(mld_group).receive_done()
def receive_query(self, packet: ReceivedPacket):
self.interface_state.receive_query(self, packet)
mld_group = packet.payload.group_address
# process group specific query
if mld_group != "::" and mld_group in self.group_state:
#if igmp_group != "0.0.0.0":
max_response_time = packet.payload.max_resp_delay
#self.group_state[igmp_group].receive_group_specific_query(max_response_time)
self.get_group_state(mld_group).receive_group_specific_query(max_response_time)
def remove(self):
for group in self.group_state.values():
group.remove()
#MLD timers (in seconds)
RobustnessVariable = 2
QueryInterval = 125
QueryResponseInterval = 10
MulticastListenerInterval = (RobustnessVariable * QueryInterval) + (QueryResponseInterval)
OtherQuerierPresentInterval = (RobustnessVariable * QueryInterval) + 0.5 * QueryResponseInterval
StartupQueryInterval = (1/4) * QueryInterval
StartupQueryCount = RobustnessVariable
LastListenerQueryInterval = 1
LastListenerQueryCount = RobustnessVariable
UnsolicitedReportInterval = 10
# MLD 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'):
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'):
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):
group_state.group_state_logger.debug('NonQuerier CheckingListeners: receive_group_specific_query')
# do nothing
return
def group_membership_timeout(group_state: 'GroupState'):
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'):
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'):
group_state.group_state_logger.debug('NonQuerier ListenersPresent: receive_report')
group_state.set_timer()
def receive_done(group_state: '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):
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'):
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'):
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'):
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'):
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):
group_state.group_state_logger.debug('NonQuerier NoListenersPresent: receive_group_specific_query')
# do nothing
return
def group_membership_timeout(group_state: 'GroupState'):
group_state.group_state_logger.debug('NonQuerier NoListenersPresent: group_membership_timeout')
# do nothing
return
def retransmit_timeout(group_state: 'GroupState'):
group_state.group_state_logger.debug('NonQuerier NoListenersPresent: retransmit_timeout')
# do nothing
return
from ipaddress import IPv6Address
from pimdm.utils import TYPE_CHECKING
from ..mld_globals import QueryResponseInterval, LastListenerQueryCount
from 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'):
router_state.router_state_logger.debug('NonQuerier state: general_query_timeout')
# do nothing
return
@staticmethod
def other_querier_present_timeout(router_state: 'RouterState'):
router_state.router_state_logger.debug('NonQuerier state: other_querier_present_timeout')
#change state to Querier
router_state.change_interface_state(querier=True)
# send general query
packet = PacketMLDHeader(type=PacketMLDHeader.MULTICAST_LISTENER_QUERY_TYPE,
max_resp_delay=QueryResponseInterval*1000)
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):
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):
return (max_response_time/1000.0) * LastListenerQueryCount
# State
@staticmethod
def get_checking_listeners_state():
return CheckingListeners
@staticmethod
def get_listeners_present_state():
return ListenersPresent
@staticmethod
def get_no_listeners_present_state():
return NoListenersPresent
from pimdm.packet.PacketMLDHeader import PacketMLDHeader
from pimdm.utils import TYPE_CHECKING
from ..mld_globals import LastListenerQueryInterval
from ..wrapper import ListenersPresent, NoListenersPresent
if TYPE_CHECKING:
from ..GroupState import GroupState
def receive_report(group_state: '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'):
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):
group_state.group_state_logger.debug('Querier CheckingListeners: receive_group_specific_query')
# do nothing
return
def group_membership_timeout(group_state: 'GroupState'):
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'):
group_state.group_state_logger.debug('Querier CheckingListeners: retransmit_timeout')
group_addr = group_state.group_ip
packet = PacketMLDHeader(type=PacketMLDHeader.MULTICAST_LISTENER_QUERY_TYPE,
max_resp_delay=LastListenerQueryInterval*1000, group_address=group_addr)
group_state.router_state.send(data=packet.bytes(), address=group_addr)
group_state.set_retransmit_timer()
from pimdm.packet.PacketMLDHeader import PacketMLDHeader
from pimdm.utils import TYPE_CHECKING
from ..mld_globals import LastListenerQueryInterval
from ..wrapper import CheckingListeners, NoListenersPresent
if TYPE_CHECKING:
from ..GroupState import GroupState
def receive_report(group_state: 'GroupState'):
group_state.group_state_logger.debug('Querier ListenersPresent: receive_report')
group_state.set_timer()
def receive_done(group_state: '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=LastListenerQueryInterval*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):
group_state.group_state_logger.debug('Querier ListenersPresent: receive_group_specific_query')
# do nothing
return
def group_membership_timeout(group_state: 'GroupState'):
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'):
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'):
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'):
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):
group_state.group_state_logger.debug('Querier NoListenersPresent: receive_group_specific_query')
# do nothing
return
def group_membership_timeout(group_state: 'GroupState'):
group_state.group_state_logger.debug('Querier NoListenersPresent: group_membership_timeout')
# do nothing
return
def retransmit_timeout(group_state: 'GroupState'):
group_state.group_state_logger.debug('Querier NoListenersPresent: retransmit_timeout')
# do nothing
return
from ipaddress import IPv6Address
from pimdm.utils import TYPE_CHECKING
from ..mld_globals import LastListenerQueryInterval, LastListenerQueryCount, QueryResponseInterval
from 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 general_query_timeout(router_state: 'RouterState'):
router_state.router_state_logger.debug('Querier state: general_query_timeout')
# send general query
packet = PacketMLDHeader(type=PacketMLDHeader.MULTICAST_LISTENER_QUERY_TYPE,
max_resp_delay=QueryResponseInterval*1000)
router_state.interface.send(packet.bytes())
# set general query timer
router_state.set_general_query_timer()
@staticmethod
def receive_query(router_state: 'RouterState', packet: ReceivedPacket):
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 other_querier_present_timeout(router_state: 'RouterState'):
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):
return LastListenerQueryInterval * LastListenerQueryCount
# State
@staticmethod
def get_checking_listeners_state():
return CheckingListeners
@staticmethod
def get_listeners_present_state():
return ListenersPresent
@staticmethod
def get_no_listeners_present_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
import socket
class PacketIpHeader:
"""
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version|
+-+-+-+-+
"""
IP_HDR = "! B"
IP_HDR_LEN = struct.calcsize(IP_HDR)
def __init__(self, ver, hdr_len):
self.version = ver
self.hdr_length = hdr_len
def __len__(self):
return self.hdr_length
@staticmethod
def parse_bytes(data: bytes):
(verhlen, ) = struct.unpack(PacketIpHeader.IP_HDR, data[:PacketIpHeader.IP_HDR_LEN])
ver = (verhlen & 0xF0) >> 4
print("ver:", ver)
return PACKET_HEADER.get(ver).parse_bytes(data)
class PacketIpv4Header(PacketIpHeader):
"""
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
"""
IP_HDR = "! BBH HH BBH 4s 4s"
IP_HDR_LEN = struct.calcsize(IP_HDR)
def __init__(self, ver, hdr_len, ttl, proto, ip_src, ip_dst):
super().__init__(ver, hdr_len)
self.ttl = ttl
self.proto = proto
self.ip_src = ip_src
self.ip_dst = ip_dst
def __len__(self):
return self.hdr_length
@staticmethod
def parse_bytes(data: bytes):
(verhlen, tos, iplen, ipid, frag, ttl, proto, cksum, src, dst) = \
struct.unpack(PacketIpv4Header.IP_HDR, data[:PacketIpv4Header.IP_HDR_LEN])
ver = (verhlen & 0xf0) >> 4
hlen = (verhlen & 0x0f) * 4
'''
"VER": ver,
"HLEN": hlen,
"TOS": tos,
"IPLEN": iplen,
"IPID": ipid,
"FRAG": frag,
"TTL": ttl,
"PROTO": proto,
"CKSUM": cksum,
"SRC": socket.inet_ntoa(src),
"DST": socket.inet_ntoa(dst)
'''
src_ip = socket.inet_ntoa(src)
dst_ip = socket.inet_ntoa(dst)
return PacketIpv4Header(ver, hlen, ttl, proto, src_ip, dst_ip)
class PacketIpv6Header(PacketIpHeader):
"""
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| Traffic Class | Flow Label |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Payload Length | Next Header | Hop Limit |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Source Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Destination Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
"""
IP6_HDR = "! I HBB 16s 16s"
IP6_HDR_LEN = struct.calcsize(IP6_HDR)
def __init__(self, ver, next_header, hop_limit, ip_src, ip_dst):
# TODO: confirm hdr_length in case of multiple options/headers
super().__init__(ver, PacketIpv6Header.IP6_HDR_LEN)
self.next_header = next_header
self.hop_limit = hop_limit
self.ip_src = ip_src
self.ip_dst = ip_dst
def __len__(self):
return PacketIpv6Header.IP6_HDR_LEN
@staticmethod
def parse_bytes(data: bytes):
(ver_tc_fl, _, next_header, hop_limit, src, dst) = \
struct.unpack(PacketIpv6Header.IP6_HDR, data[:PacketIpv6Header.IP6_HDR_LEN])
ver = (ver_tc_fl & 0xf0000000) >> 28
#tc = (ver_tc_fl & 0x0ff00000) >> 20
#fl = (ver_tc_fl & 0x000fffff)
'''
"VER": ver,
"TRAFFIC CLASS": tc,
"FLOW LABEL": fl,
"PAYLOAD LEN": payload_length,
"NEXT HEADER": next_header,
"HOP LIMIT": hop_limit,
"SRC": socket.inet_atop(socket.AF_INET6, src),
"DST": socket.inet_atop(socket.AF_INET6, dst)
'''
src_ip = socket.inet_ntop(socket.AF_INET6, src)
dst_ip = socket.inet_ntop(socket.AF_INET6, dst)
return PacketIpv6Header(ver, next_header, hop_limit, src_ip, dst_ip)
PACKET_HEADER = {
4: PacketIpv4Header,
6: PacketIpv6Header,
}
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
......@@ -55,7 +55,7 @@ class PacketPimEncodedGroupAddress:
elif version == 6:
return (PacketPimEncodedGroupAddress.IPV6_HDR, PacketPimEncodedGroupAddress.FAMILY_IPV6, socket.AF_INET6)
else:
raise Exception
raise Exception("Unknown address family")
def __len__(self):
version = ipaddress.ip_address(self.group_address).version
......@@ -64,7 +64,7 @@ class PacketPimEncodedGroupAddress:
elif version == 6:
return self.PIM_ENCODED_GROUP_ADDRESS_HDR_LEN_IPv6
else:
raise Exception
raise Exception("Unknown address family")
@staticmethod
def parse_bytes(data: bytes):
......@@ -72,13 +72,14 @@ class PacketPimEncodedGroupAddress:
(addr_family, encoding, _, mask_len) = struct.unpack(PacketPimEncodedGroupAddress.PIM_ENCODED_GROUP_ADDRESS_HDR_WITHOUT_GROUP_MULTICAST_ADDRESS, data_without_group_addr)
data_group_addr = data[PacketPimEncodedGroupAddress.PIM_ENCODED_GROUP_ADDRESS_HDR_WITHOUT_GROUP_ADDRESS_LEN:]
ip = None
if addr_family == PacketPimEncodedGroupAddress.FAMILY_IPV4:
(ip,) = struct.unpack("! " + PacketPimEncodedGroupAddress.IPV4_HDR, data_group_addr[:4])
ip = socket.inet_ntop(socket.AF_INET, ip)
elif addr_family == PacketPimEncodedGroupAddress.FAMILY_IPV6:
(ip,) = struct.unpack("! " + PacketPimEncodedGroupAddress.IPV6_HDR, data_group_addr[:16])
ip = socket.inet_ntop(socket.AF_INET6, ip)
else:
raise Exception("Unknown address family")
if encoding != 0:
print("unknown encoding")
......
......@@ -57,7 +57,7 @@ class PacketPimEncodedSourceAddress:
elif version == 6:
return (PacketPimEncodedSourceAddress.IPV6_HDR, PacketPimEncodedSourceAddress.FAMILY_IPV6, socket.AF_INET6)
else:
raise Exception
raise Exception("Unknown address family")
def __len__(self):
version = ipaddress.ip_address(self.source_address).version
......@@ -66,7 +66,7 @@ class PacketPimEncodedSourceAddress:
elif version == 6:
return self.PIM_ENCODED_SOURCE_ADDRESS_HDR_LEN_IPV6
else:
raise Exception
raise Exception("Unknown address family")
@staticmethod
def parse_bytes(data: bytes):
......@@ -74,13 +74,14 @@ class PacketPimEncodedSourceAddress:
(addr_family, encoding, _, mask_len) = struct.unpack(PacketPimEncodedSourceAddress.PIM_ENCODED_SOURCE_ADDRESS_HDR_WITHOUT_SOURCE_ADDRESS, data_without_source_addr)
data_source_addr = data[PacketPimEncodedSourceAddress.PIM_ENCODED_SOURCE_ADDRESS_HDR_WITHOUT_SOURCE_ADDRESS_LEN:]
ip = None
if addr_family == PacketPimEncodedSourceAddress.FAMILY_IPV4:
(ip,) = struct.unpack("! " + PacketPimEncodedSourceAddress.IPV4_HDR, data_source_addr[:4])
ip = socket.inet_ntop(socket.AF_INET, ip)
elif addr_family == PacketPimEncodedSourceAddress.FAMILY_IPV6:
(ip,) = struct.unpack("! " + PacketPimEncodedSourceAddress.IPV6_HDR, data_source_addr[:16])
ip = socket.inet_ntop(socket.AF_INET6, ip)
else:
raise Exception("Unknown address family")
if encoding != 0:
print("unknown encoding")
......
......@@ -46,7 +46,7 @@ class PacketPimEncodedUnicastAddress:
elif version == 6:
return (PacketPimEncodedUnicastAddress.IPV6_HDR, PacketPimEncodedUnicastAddress.FAMILY_IPV6, socket.AF_INET6)
else:
raise Exception
raise Exception("Unknown address family")
def __len__(self):
version = ipaddress.ip_address(self.unicast_address).version
......@@ -55,7 +55,7 @@ class PacketPimEncodedUnicastAddress:
elif version == 6:
return self.PIM_ENCODED_UNICAST_ADDRESS_HDR_LEN_IPV6
else:
raise Exception
raise Exception("Unknown address family")
@staticmethod
def parse_bytes(data: bytes):
......@@ -69,6 +69,8 @@ class PacketPimEncodedUnicastAddress:
elif addr_family == PacketPimEncodedUnicastAddress.FAMILY_IPV6:
(ip,) = struct.unpack("! " + PacketPimEncodedUnicastAddress.IPV6_HDR, data_unicast_addr[:16])
ip = socket.inet_ntop(socket.AF_INET6, ip)
else:
raise Exception("Unknown address family")
if encoding != 0:
print("unknown encoding")
......
import socket
from .Packet import Packet
from .PacketIpHeader import PacketIpHeader
from .PacketIGMPHeader import PacketIGMPHeader
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:
from pimdm.Interface import Interface
......@@ -13,13 +15,32 @@ class ReceivedPacket(Packet):
def __init__(self, raw_packet: bytes, interface: 'Interface'):
self.interface = interface
# Parse ao packet e preencher objeto Packet
packet_ip_hdr = raw_packet[:PacketIpHeader.IP_HDR_LEN]
ip_header = PacketIpHeader.parse_bytes(packet_ip_hdr)
# Parse packet and fill Packet super class
ip_header = PacketIpv4Header.parse_bytes(raw_packet)
protocol_number = ip_header.proto
packet_without_ip_hdr = raw_packet[ip_header.hdr_length:]
payload = ReceivedPacket.payload_protocol[protocol_number].parse_bytes(packet_without_ip_hdr)
super().__init__(ip_header=ip_header, payload=payload)
class ReceivedPacket_v6(Packet):
# choose payload protocol class based on ip protocol number
payload_protocol_v6 = {58: PacketMLDHeader, 103: PacketPimHeader}
def __init__(self, raw_packet: bytes, ancdata: list, src_addr: str, next_header: int, interface: 'Interface'):
self.interface = interface
# Parse packet and fill Packet super class
dst_addr = "::"
for cmsg_level, cmsg_type, cmsg_data in ancdata:
if cmsg_level == socket.IPPROTO_IPV6 and cmsg_type == socket.IPV6_PKTINFO:
dst_addr = socket.inet_ntop(socket.AF_INET6, cmsg_data[:16])
break
src_addr = src_addr[0].split("%")[0]
ipv6_packet = PacketIpv6Header(ver=6, hop_limit=1, next_header=next_header, ip_src=src_addr, ip_dst=dst_addr)
payload = ReceivedPacket_v6.payload_protocol_v6[next_header].parse_bytes(raw_packet)
super().__init__(ip_header=ipv6_packet, payload=payload)
import logging
from time import time
from threading import Lock, RLock
from pimdm import UnicastRouting
from .metric import AssertMetric
from .tree_if_upstream import TreeInterfaceUpstream
from .tree_if_downstream import TreeInterfaceDownstream
from .tree_interface import TreeInterface
from threading import Lock, RLock
from .metric import AssertMetric
from pimdm import UnicastRouting, Main
from time import time
import logging
class KernelEntry:
TREE_TIMEOUT = 180
KERNEL_LOGGER = logging.getLogger('pim.KernelEntry')
def __init__(self, source_ip: str, group_ip: str):
self.kernel_entry_logger = logging.LoggerAdapter(KernelEntry.KERNEL_LOGGER, {'tree': '(' + source_ip + ',' + group_ip + ')'})
def __init__(self, source_ip: str, group_ip: str, kernel_entry_interface):
self.kernel_entry_logger = logging.LoggerAdapter(KernelEntry.KERNEL_LOGGER,
{'tree': '(' + source_ip + ',' + group_ip + ')'})
self.kernel_entry_logger.debug('Create KernelEntry')
self.source_ip = source_ip
self.group_ip = group_ip
self._kernel_entry_interface = kernel_entry_interface
# OBTAIN UNICAST ROUTING INFORMATION###################################################
(metric_administrative_distance, metric_cost, rpf_node, root_if, mask) = \
UnicastRouting.get_unicast_info(source_ip)
......@@ -38,7 +42,7 @@ class KernelEntry:
self.interface_state = {} # type: Dict[int, TreeInterface]
with self.CHANGE_STATE_LOCK:
for i in Main.kernel.vif_index_to_name_dic.keys():
for i in self.get_kernel().vif_index_to_name_dic.keys():
try:
if i == self.inbound_interface_index:
self.interface_state[i] = TreeInterfaceUpstream(self, i)
......@@ -55,22 +59,31 @@ class KernelEntry:
print('Tree created')
def get_inbound_interface_index(self):
"""
Get VIF of root interface of this tree
"""
return self.inbound_interface_index
def get_outbound_interfaces_indexes(self):
outbound_indexes = [0] * Main.kernel.MAXVIFS
for (index, state) in self.interface_state.items():
outbound_indexes[index] = state.is_forwarding()
return outbound_indexes
"""
Get OIL of this tree
"""
return self._kernel_entry_interface.get_outbound_interfaces_indexes(self)
################################################
# Receive (S,G) data packets or control packets
################################################
def recv_data_msg(self, index):
"""
Receive data packet regarding this tree in interface with VIF index
"""
print("recv data")
self.interface_state[index].recv_data_msg()
def recv_assert_msg(self, index, packet):
"""
Receive assert packet regarding this tree in interface with VIF index
"""
print("recv assert")
pkt_assert = packet.payload.payload
metric = pkt_assert.metric
......@@ -81,28 +94,43 @@ class KernelEntry:
self.interface_state[index].recv_assert_msg(received_metric)
def recv_prune_msg(self, index, packet):
"""
Receive Prune packet regarding this tree in interface with VIF index
"""
print("recv prune msg")
holdtime = packet.payload.payload.hold_time
upstream_neighbor_address = packet.payload.payload.upstream_neighbor_address
self.interface_state[index].recv_prune_msg(upstream_neighbor_address=upstream_neighbor_address, holdtime=holdtime)
def recv_join_msg(self, index, packet):
"""
Receive Join packet regarding this tree in interface with VIF index
"""
print("recv join msg")
upstream_neighbor_address = packet.payload.payload.upstream_neighbor_address
self.interface_state[index].recv_join_msg(upstream_neighbor_address)
def recv_graft_msg(self, index, packet):
"""
Receive Graft packet regarding this tree in interface with VIF index
"""
print("recv graft msg")
upstream_neighbor_address = packet.payload.payload.upstream_neighbor_address
source_ip = packet.ip_header.ip_src
self.interface_state[index].recv_graft_msg(upstream_neighbor_address, source_ip)
def recv_graft_ack_msg(self, index, packet):
"""
Receive GraftAck packet regarding this tree in interface with VIF index
"""
print("recv graft ack msg")
source_ip = packet.ip_header.ip_src
self.interface_state[index].recv_graft_ack_msg(source_ip)
def recv_state_refresh_msg(self, index, packet):
"""
Receive StateRefresh packet regarding this tree in interface with VIF index
"""
print("recv state refresh msg")
source_of_state_refresh = packet.ip_header.ip_src
......@@ -129,11 +157,13 @@ class KernelEntry:
self.forward_state_refresh_msg(packet.payload.payload)
################################################
# Send state refresh msg
################################################
def forward_state_refresh_msg(self, state_refresh_packet):
"""
Forward StateRefresh packet through all interfaces
"""
for interface in self.interface_state.values():
interface.send_state_refresh(state_refresh_packet)
......@@ -142,6 +172,9 @@ class KernelEntry:
# Unicast Changes to RPF
###############################################################
def network_update(self):
"""
Unicast routing table suffered an update and this tree might be affected by it
"""
# TODO TALVEZ OUTRO LOCK PARA BLOQUEAR ENTRADA DE PACOTES
with self.CHANGE_STATE_LOCK:
......@@ -184,24 +217,34 @@ class KernelEntry:
self.rpf_node = rpf_node
self.interface_state[self.inbound_interface_index].change_on_unicast_routing()
# check if add/removal of neighbors from interface afects olist and forward/prune state of interface
def change_at_number_of_neighbors(self):
"""
Check if modification of number of neighbors causes changes to OIL and interest of interface
"""
with self.CHANGE_STATE_LOCK:
self.change()
self.evaluate_olist_change()
def new_or_reset_neighbor(self, if_index, neighbor_ip):
"""
An interface identified by if_index has a new neighbor
"""
# todo maybe lock de interfaces
self.interface_state[if_index].new_or_reset_neighbor(neighbor_ip)
def is_olist_null(self):
"""
Check if olist is null
"""
for interface in self.interface_state.values():
if interface.is_forwarding():
return False
return True
def evaluate_olist_change(self):
"""
React to changes on the olist
"""
with self._lock_test2:
is_olist_null = self.is_olist_null()
......@@ -214,33 +257,74 @@ class KernelEntry:
self._was_olist_null = is_olist_null
def get_source(self):
"""
Get source IP of multicast source
"""
return self.source_ip
def get_group(self):
"""
Get group IP of multicast tree
"""
return self.group_ip
def change(self):
"""
Trigger an update on the multicast routing table
"""
with self._multicast_change:
Main.kernel.set_multicast_route(self)
self.get_kernel().set_multicast_route(self)
def delete(self):
"""
Remove kernel entry
"""
with self._multicast_change:
for state in self.interface_state.values():
state.delete()
Main.kernel.remove_multicast_route(self)
self.get_kernel().remove_multicast_route(self)
def get_interface_name(self, interface_id):
"""
Get interface name of interface identified by interface_id
"""
return self._kernel_entry_interface.get_interface_name(interface_id)
def get_interface(self, interface_id):
"""
Get PIM interface
"""
return self._kernel_entry_interface.get_interface(self, interface_id)
def get_membership_interface(self, interface_id):
"""
Get IGMP/MLD interface
"""
return self._kernel_entry_interface.get_membership_interface(self, interface_id)
def get_kernel(self):
"""
Get kernel
"""
return self._kernel_entry_interface.get_kernel()
######################################
# Interface change
#######################################
def new_interface(self, index):
"""
React to a new interface that was added and in which a tree was already built
"""
with self.CHANGE_STATE_LOCK:
self.interface_state[index] = TreeInterfaceDownstream(self, index)
self.change()
self.evaluate_olist_change()
def remove_interface(self, index):
"""
React to removal of an interface of a tree that was already built
"""
with self.CHANGE_STATE_LOCK:
#check if removed interface is root interface
if self.inbound_interface_index == index:
......
from pimdm import Main
from abc import abstractmethod, ABCMeta
class KernelEntryInterface(metaclass=ABCMeta):
@staticmethod
@abstractmethod
def get_outbound_interfaces_indexes(kernel_tree):
"""
Get OIL of this tree
"""
pass
@staticmethod
@abstractmethod
def get_interface_name(interface_id):
"""
Get name of interface from vif id
"""
pass
@staticmethod
@abstractmethod
def get_interface(kernel_tree, interface_id):
"""
Get PIM interface from interface id
"""
pass
@staticmethod
@abstractmethod
def get_membership_interface(kernel_tree, interface_id):
"""
Get IGMP/MLD interface from interface id
"""
pass
@staticmethod
@abstractmethod
def get_kernel():
"""
Get kernel
"""
pass
class KernelEntry4Interface(KernelEntryInterface):
@staticmethod
def get_outbound_interfaces_indexes(kernel_tree):
"""
Get OIL of this tree
"""
outbound_indexes = [0] * Main.kernel.MAXVIFS
for (index, state) in kernel_tree.interface_state.items():
outbound_indexes[index] = state.is_forwarding()
return outbound_indexes
@staticmethod
def get_interface_name(interface_id):
"""
Get name of interface from vif id
"""
return Main.kernel.vif_index_to_name_dic[interface_id]
@staticmethod
def get_interface(kernel_tree, interface_id):
"""
Get PIM interface from interface id
"""
interface_name = kernel_tree.get_interface_name(interface_id)
return Main.interfaces.get(interface_name, None)
@staticmethod
def get_membership_interface(kernel_tree, interface_id):
"""
Get IGMP interface from interface id
"""
interface_name = kernel_tree.get_interface_name(interface_id)
return Main.igmp_interfaces.get(interface_name, None) # type: InterfaceIGMP
@staticmethod
def get_kernel():
"""
Get kernel
"""
return Main.kernel
class KernelEntry6Interface(KernelEntryInterface):
@staticmethod
def get_outbound_interfaces_indexes(kernel_tree):
"""
Get OIL of this tree
"""
outbound_indexes = [0] * 8
for (index, state) in kernel_tree.interface_state.items():
outbound_indexes[index // 32] |= state.is_forwarding() << (index % 32)
return outbound_indexes
@staticmethod
def get_interface_name(interface_id):
"""
Get name of interface from vif id
"""
return Main.kernel_v6.vif_index_to_name_dic[interface_id]
@staticmethod
def get_interface(kernel_tree, interface_id):
"""
Get PIM interface from interface id
"""
interface_name = kernel_tree.get_interface_name(interface_id)
return Main.interfaces_v6.get(interface_name, None)
@staticmethod
def get_membership_interface(kernel_tree, interface_id):
"""
Get MLD interface from interface id
"""
interface_name = kernel_tree.get_interface_name(interface_id)
return Main.mld_interfaces.get(interface_name, None) # type: InterfaceMLD
@staticmethod
def get_kernel():
"""
Get kernel
"""
return Main.kernel_v6
......@@ -112,12 +112,10 @@ class AssertStateABC(metaclass=ABCMeta):
"""
raise NotImplementedError()
def _sendAssert_setAT(interface: "TreeInterfaceDownstream"):
interface.set_assert_timer(pim_globals.ASSERT_TIME)
interface.send_assert()
# Override
def __str__(self) -> str:
return "AssertSM:" + self.__class__.__name__
......@@ -289,7 +287,6 @@ class WinnerState(AssertStateABC):
return "Winner"
class LoserState(AssertStateABC):
'''
I am Assert Loser (L)
......@@ -370,6 +367,7 @@ class LoserState(AssertStateABC):
def __str__(self) -> str:
return "Loser"
class AssertState():
NoInfo = NoInfoState()
Winner = WinnerState()
......
import subprocess
import struct
import socket
import ipaddress
import subprocess
from ctypes import create_string_buffer, addressof
SO_ATTACH_FILTER = 26
ETH_P_IP = 0x0800 # Internet Protocol packet
ETH_P_IP = 0x0800 # Internet Protocol packet
ETH_P_IPV6 = 0x86DD # IPv6 over bluebook
SO_RCVBUFFORCE = 33
def get_s_g_bpf_filter_code(source, group, interface_name):
#cmd = "tcpdump -ddd \"(udp or icmp) and host %s and dst %s\"" % (source, group)
cmd = "tcpdump -ddd \"(ip proto not 2) and host %s and dst %s\"" % (source, group)
ip_source_version = ipaddress.ip_address(source).version
ip_group_version = ipaddress.ip_address(source).version
if ip_source_version == ip_group_version == 4:
# cmd = "tcpdump -ddd \"(udp or icmp) and host %s and dst %s\"" % (source, group)
cmd = "tcpdump -ddd \"(ip proto not 2) and host %s and dst %s\"" % (source, group)
protocol = ETH_P_IP
elif ip_source_version == ip_group_version == 6:
# TODO: allow ICMPv6 echo request/echo response to be considered multicast packets
cmd = "tcpdump -ddd \"(ip6 proto not 58) and host %s and dst %s\"" % (source, group)
protocol = ETH_P_IPV6
else:
raise Exception("Unknown IP family")
result = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
bpf_filter = b''
......@@ -28,10 +42,10 @@ def get_s_g_bpf_filter_code(source, group, interface_name):
# Create listening socket with filters
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, ETH_P_IP)
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, protocol)
s.setsockopt(socket.SOL_SOCKET, SO_ATTACH_FILTER, fprog)
# todo pequeno ajuste (tamanho de buffer pequeno para o caso de trafego em rajadas):
#s.setsockopt(socket.SOL_SOCKET, SO_RCVBUFFORCE, 1)
s.bind((interface_name, ETH_P_IP))
s.bind((interface_name, protocol))
return s
......@@ -7,4 +7,8 @@ REFRESH_INTERVAL = 60 # State Refresh Interval
SOURCE_LIFETIME = 210
T_LIMIT = 210
HELLO_HOLD_TIME_NO_TIMEOUT = 0xFFFF
HELLO_HOLD_TIME = 160
HELLO_HOLD_TIME_TIMEOUT = 0
ASSERT_CANCEL_METRIC = 0xFFFFFFFF
\ No newline at end of file
from abc import ABCMeta, abstractstaticmethod
from abc import ABCMeta, abstractmethod
class OriginatorStateABC(metaclass=ABCMeta):
@abstractstaticmethod
@staticmethod
@abstractmethod
def recvDataMsgFromSource(tree):
pass
@abstractstaticmethod
@staticmethod
@abstractmethod
def SRTexpires(tree):
pass
@abstractstaticmethod
@staticmethod
@abstractmethod
def SATexpires(tree):
pass
@abstractstaticmethod
@staticmethod
@abstractmethod
def SourceNotConnected(tree):
pass
......
'''
Created on Jul 16, 2015
@author: alex
'''
from threading import Timer
from pimdm.CustomTimer.RemainingTimer import RemainingTimer
from .assert_ import AssertState
from pimdm.custom_timer.RemainingTimer import RemainingTimer
from .assert_state import AssertState
from .downstream_prune import DownstreamState, DownstreamStateABS
from .tree_interface import TreeInterface
from pimdm.Packet.PacketPimStateRefresh import PacketPimStateRefresh
from pimdm.Packet.Packet import Packet
from pimdm.Packet.PacketPimHeader import PacketPimHeader
from pimdm.packet.PacketPimStateRefresh import PacketPimStateRefresh
from pimdm.packet.Packet import Packet
from pimdm.packet.PacketPimHeader import PacketPimHeader
import traceback
import logging
from .. import Main
class TreeInterfaceDownstream(TreeInterface):
......@@ -22,7 +16,7 @@ class TreeInterfaceDownstream(TreeInterface):
def __init__(self, kernel_entry, interface_id):
extra_dict_logger = kernel_entry.kernel_entry_logger.extra.copy()
extra_dict_logger['vif'] = interface_id
extra_dict_logger['interfacename'] = Main.kernel.vif_index_to_name_dic[interface_id]
extra_dict_logger['interfacename'] = kernel_entry.get_interface_name(interface_id)
logger = logging.LoggerAdapter(TreeInterfaceDownstream.LOGGER, extra_dict_logger)
TreeInterface.__init__(self, kernel_entry, interface_id, logger)
self.logger.debug('Created DownstreamInterface')
......
'''
Created on Jul 16, 2015
@author: alex
'''
from .tree_interface import TreeInterface
from .upstream_prune import UpstreamState
from threading import Timer
from pimdm.CustomTimer.RemainingTimer import RemainingTimer
from pimdm.custom_timer.RemainingTimer import RemainingTimer
from .globals import *
import random
from .metric import AssertMetric
from .originator import OriginatorState, OriginatorStateABC
from pimdm.Packet.PacketPimStateRefresh import PacketPimStateRefresh
from pimdm.packet.PacketPimStateRefresh import PacketPimStateRefresh
import traceback
from . import DataPacketsSocket
from . import data_packets_socket
import threading
import logging
from .. import Main
class TreeInterfaceUpstream(TreeInterface):
......@@ -25,7 +19,7 @@ class TreeInterfaceUpstream(TreeInterface):
def __init__(self, kernel_entry, interface_id):
extra_dict_logger = kernel_entry.kernel_entry_logger.extra.copy()
extra_dict_logger['vif'] = interface_id
extra_dict_logger['interfacename'] = Main.kernel.vif_index_to_name_dic[interface_id]
extra_dict_logger['interfacename'] = kernel_entry.get_interface_name(interface_id)
logger = logging.LoggerAdapter(TreeInterfaceUpstream.LOGGER, extra_dict_logger)
TreeInterface.__init__(self, kernel_entry, interface_id, logger)
......@@ -47,15 +41,16 @@ class TreeInterfaceUpstream(TreeInterface):
if self.is_S_directly_conn():
self._graft_prune_state.sourceIsNowDirectConnect(self)
if self.get_interface().is_state_refresh_enabled():
interface = self.get_interface()
if interface is not None and interface.is_state_refresh_enabled():
self._originator_state.recvDataMsgFromSource(self)
# TODO TESTE SOCKET RECV DATA PCKTS
self.socket_is_enabled = True
(s,g) = self.get_tree_id()
interface_name = self.get_interface().interface_name
self.socket_pkt = DataPacketsSocket.get_s_g_bpf_filter_code(s, g, interface_name)
(s, g) = self.get_tree_id()
interface_name = self.get_interface_name()
self.socket_pkt = data_packets_socket.get_s_g_bpf_filter_code(s, g, interface_name)
# run receive method in background
receive_thread = threading.Thread(target=self.socket_recv)
......@@ -182,8 +177,10 @@ class TreeInterfaceUpstream(TreeInterface):
def recv_data_msg(self):
if not self.is_prune_limit_timer_running() and not self.is_S_directly_conn() and self.is_olist_null():
self._graft_prune_state.dataArrivesRPFinterface_OListNull_PLTstoped(self)
elif self.is_S_directly_conn() and self.get_interface().is_state_refresh_enabled():
self._originator_state.recvDataMsgFromSource(self)
elif self.is_S_directly_conn():
interface = self.get_interface()
if interface is not None and interface.is_state_refresh_enabled():
self._originator_state.recvDataMsgFromSource(self)
def recv_join_msg(self, upstream_neighbor_address):
......
'''
Created on Jul 16, 2015
@author: alex
'''
from abc import ABCMeta, abstractmethod
from .. import Main
from threading import RLock
import traceback
from .downstream_prune import DownstreamState
from .assert_ import AssertState, AssertStateABC
from .assert_state import AssertState, AssertStateABC
from pimdm.Packet.PacketPimGraft import PacketPimGraft
from pimdm.Packet.PacketPimGraftAck import PacketPimGraftAck
from pimdm.Packet.PacketPimJoinPruneMulticastGroup import PacketPimJoinPruneMulticastGroup
from pimdm.Packet.PacketPimHeader import PacketPimHeader
from pimdm.Packet.Packet import Packet
from pimdm.packet.PacketPimGraft import PacketPimGraft
from pimdm.packet.PacketPimGraftAck import PacketPimGraftAck
from pimdm.packet.PacketPimJoinPruneMulticastGroup import PacketPimJoinPruneMulticastGroup
from pimdm.packet.PacketPimHeader import PacketPimHeader
from pimdm.packet.Packet import Packet
from pimdm.Packet.PacketPimJoinPrune import PacketPimJoinPrune
from pimdm.Packet.PacketPimAssert import PacketPimAssert
from pimdm.Packet.PacketPimStateRefresh import PacketPimStateRefresh
from pimdm.packet.PacketPimJoinPrune import PacketPimJoinPrune
from pimdm.packet.PacketPimAssert import PacketPimAssert
from pimdm.packet.PacketPimStateRefresh import PacketPimStateRefresh
from .metric import AssertMetric
from threading import Timer
from .local_membership import LocalMembership
from .globals import *
from .globals import T_LIMIT
import logging
class TreeInterface(metaclass=ABCMeta):
def __init__(self, kernel_entry, interface_id, logger: logging.LoggerAdapter):
self._kernel_entry = kernel_entry
......@@ -36,9 +31,8 @@ class TreeInterface(metaclass=ABCMeta):
# Local Membership State
try:
interface_name = Main.kernel.vif_index_to_name_dic[interface_id]
igmp_interface = Main.igmp_interfaces[interface_name] # type: InterfaceIGMP
group_state = igmp_interface.interface_state.get_group_state(kernel_entry.group_ip)
membership_interface = self.get_membership_interface()
group_state = membership_interface.interface_state.get_group_state(kernel_entry.group_ip)
#self._igmp_has_members = group_state.add_multicast_routing_entry(self)
igmp_has_members = group_state.add_multicast_routing_entry(self)
self._local_membership_state = LocalMembership.Include if igmp_has_members else LocalMembership.NoInfo
......@@ -60,8 +54,7 @@ class TreeInterface(metaclass=ABCMeta):
# Received prune hold time
self._received_prune_holdtime = None
self._igmp_lock = RLock()
self._membership_lock = RLock()
############################################
# Set ASSERT State
......@@ -90,7 +83,6 @@ class TreeInterface(metaclass=ABCMeta):
finally:
self._assert_winner_metric = new_assert_metric
############################################
# ASSERT Timer
############################################
......@@ -106,7 +98,6 @@ class TreeInterface(metaclass=ABCMeta):
def assert_timeout(self):
self._assert_state.assertTimerExpires(self)
###########################################
# Recv packets
###########################################
......@@ -145,7 +136,6 @@ class TreeInterface(metaclass=ABCMeta):
def recv_state_refresh_msg(self, received_metric: AssertMetric, prune_indicator):
self.recv_assert_msg(received_metric)
######################################
# Send messages
######################################
......@@ -163,7 +153,6 @@ class TreeInterface(metaclass=ABCMeta):
traceback.print_exc()
return
def send_graft_ack(self, ip_sender):
print("send graft ack")
try:
......@@ -177,7 +166,6 @@ class TreeInterface(metaclass=ABCMeta):
traceback.print_exc()
return
def send_prune(self, holdtime=None):
if holdtime is None:
holdtime = T_LIMIT
......@@ -195,7 +183,6 @@ class TreeInterface(metaclass=ABCMeta):
traceback.print_exc()
return
def send_pruneecho(self):
holdtime = T_LIMIT
try:
......@@ -210,7 +197,6 @@ class TreeInterface(metaclass=ABCMeta):
traceback.print_exc()
return
def send_join(self):
print("send join")
......@@ -225,7 +211,6 @@ class TreeInterface(metaclass=ABCMeta):
traceback.print_exc()
return
def send_assert(self):
print("send assert")
......@@ -240,7 +225,6 @@ class TreeInterface(metaclass=ABCMeta):
traceback.print_exc()
return
def send_assert_cancel(self):
print("send assert cancel")
......@@ -254,7 +238,6 @@ class TreeInterface(metaclass=ABCMeta):
traceback.print_exc()
return
def send_state_refresh(self, state_refresh_msg_received: PacketPimStateRefresh):
pass
......@@ -282,9 +265,8 @@ class TreeInterface(metaclass=ABCMeta):
(s, g) = self.get_tree_id()
# unsubscribe igmp information
try:
interface_name = Main.kernel.vif_index_to_name_dic[self._interface_id]
igmp_interface = Main.igmp_interfaces[interface_name] # type: InterfaceIGMP
group_state = igmp_interface.interface_state.get_group_state(g)
membership_interface = self.get_membership_interface()
group_state = membership_interface.interface_state.get_group_state(g)
group_state.remove_multicast_routing_entry(self)
except:
pass
......@@ -306,29 +288,29 @@ class TreeInterface(metaclass=ABCMeta):
def evaluate_ingroup(self):
self._kernel_entry.evaluate_olist_change()
#############################################################
# Local Membership (IGMP)
############################################################
def notify_igmp(self, has_members: bool):
def notify_membership(self, has_members: bool):
with self.get_state_lock():
with self._igmp_lock:
with self._membership_lock:
if has_members != self._local_membership_state.has_members():
self._local_membership_state = LocalMembership.Include if has_members else LocalMembership.NoInfo
self.change_tree()
self.evaluate_ingroup()
def igmp_has_members(self):
with self._igmp_lock:
with self._membership_lock:
return self._local_membership_state.has_members()
def get_interface_name(self):
return self._kernel_entry.get_interface_name(self._interface_id)
def get_interface(self):
kernel = Main.kernel
interface_name = kernel.vif_index_to_name_dic[self._interface_id]
interface = Main.interfaces[interface_name]
return interface
return self._kernel_entry.get_interface(self._interface_id)
def get_membership_interface(self):
return self._kernel_entry.get_membership_interface(self._interface_id)
def get_ip(self):
ip = self.get_interface().get_ip()
......@@ -353,9 +335,6 @@ class TreeInterface(metaclass=ABCMeta):
def is_downstream(self):
raise NotImplementedError()
# obtain ip of RPF'(S)
def get_neighbor_RPF(self):
'''
......@@ -375,8 +354,6 @@ class TreeInterface(metaclass=ABCMeta):
def get_received_prune_holdtime(self):
return self._received_prune_holdtime
###################################################
# ASSERT
###################################################
......
import array
'''
import struct
if struct.pack("H",1) == "\x00\x01": # big endian
def checksum(pkt):
if len(pkt) % 2 == 1:
pkt += "\0"
s = sum(array.array("H", pkt))
s = (s >> 16) + (s & 0xffff)
s += s >> 16
s = ~s
return s & 0xffff
else:
def checksum(pkt):
if len(pkt) % 2 == 1:
pkt += "\0"
s = sum(array.array("H", pkt))
s = (s >> 16) + (s & 0xffff)
s += s >> 16
s = ~s
return (((s>>8)&0xff)|s<<8) & 0xffff
'''
HELLO_HOLD_TIME_NO_TIMEOUT = 0xFFFF
HELLO_HOLD_TIME = 160
HELLO_HOLD_TIME_TIMEOUT = 0
def checksum(pkt: bytes) -> bytes:
......@@ -36,35 +11,6 @@ def checksum(pkt: bytes) -> bytes:
return (((s >> 8) & 0xff) | s << 8) & 0xffff
import ctypes
import ctypes.util
libc = ctypes.CDLL(ctypes.util.find_library('c'))
def if_nametoindex(name):
if not isinstance(name, str):
raise TypeError('name must be a string.')
ret = libc.if_nametoindex(name)
if not ret:
raise RuntimeError("Invalid Name")
return ret
def if_indextoname(index):
if not isinstance(index, int):
raise TypeError('index must be an int.')
libc.if_indextoname.argtypes = [ctypes.c_uint32, ctypes.c_char_p]
libc.if_indextoname.restype = ctypes.c_char_p
ifname = ctypes.create_string_buffer(32)
ifname = libc.if_indextoname(index, ifname)
if not ifname:
raise RuntimeError ("Inavlid Index")
return ifname.decode("utf-8")
# obtain TYPE_CHECKING (for type hinting)
try:
from typing import TYPE_CHECKING
......
......@@ -12,8 +12,8 @@ setup(
description="PIM-DM protocol",
long_description=open("README.md", "r").read(),
long_description_content_type="text/markdown",
keywords="PIM-DM Multicast Routing Protocol Dense-Mode Router RFC3973",
version="1.0.4.2",
keywords="PIM-DM Multicast Routing Protocol Dense-Mode Router RFC3973 IPv4 IPv6",
version="1.1",
url="http://github.com/pedrofran12/pim_dm",
author="Pedro Oliveira",
author_email="pedro.francisco.oliveira@tecnico.ulisboa.pt",
......@@ -38,7 +38,6 @@ setup(
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.2",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
......@@ -46,5 +45,5 @@ setup(
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
],
python_requires=">=3.2",
python_requires=">=3.3",
)
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