Skip to content

Commit

Permalink
IPv6 multicast routing support (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrofran12 authored Jun 6, 2020
1 parent f6ac9c8 commit b26c621
Show file tree
Hide file tree
Showing 73 changed files with 2,562 additions and 617 deletions.
51 changes: 31 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

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

Expand All @@ -49,37 +54,48 @@ 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:

- 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

Expand All @@ -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]
```


Expand All @@ -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
Expand All @@ -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.
32 changes: 28 additions & 4 deletions pimdm/Interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,51 @@ def __init__(self, interface_name, recv_socket, send_socket, vif_index):
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)
receive_thread.daemon = True
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)
Expand All @@ -54,8 +72,14 @@ def remove(self):
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
11 changes: 7 additions & 4 deletions pimdm/InterfaceIGMP.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
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
Expand Down Expand Up @@ -48,18 +48,20 @@ def __init__(self, interface_name: str, vif_index: int):
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)
Expand Down Expand Up @@ -91,7 +93,8 @@ def receive_leave_group(self, packet):
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):
Expand Down
Loading

0 comments on commit b26c621

Please sign in to comment.