From: Nogah Frankel <nogahf(a)mellanox.com>
The Linux kernel multicast routing support is done via the mroute socket.
Using setsockopt on that socket, user can:
- Add/Remove MFCs - MFC (Multicast Forwarding Cache) is practically the
multicast counterpart to a unicast route.
- Add/Remove VIFs - VIF (Virtual Interface) is a way to configure a port
to do multicast forwarding. When a VIF is assigned to a port, the VIF
index is used when adding an MFC entry.
- Initialize PIM support.
- Create the pimreg VIF - A specific interface dedicated for the PIM
protocol.
In addition, the kernel sends multicast routing notifications to that
socket in a form of IGMP packets.
Implement these operations in the NetTestSlave module. In addition, make
the machine_cleanup function clean the added MFC and VIFs by closing the
mroute socket.
Signed-off-by: Nogah Frankel <nogahf(a)mellanox.com>
Signed-off-by: Yotam Gigi <yotamg(a)mellanox.com>
---
lnst/Slave/NetTestSlave.py | 129 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 129 insertions(+)
diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py
index 9e9087e..e3b0f4a 100644
--- a/lnst/Slave/NetTestSlave.py
+++ b/lnst/Slave/NetTestSlave.py
@@ -20,6 +20,7 @@ import socket
import ctypes
import multiprocessing
import re
+import struct
from time import sleep, time
from xmlrpclib import Binary
from tempfile import NamedTemporaryFile
@@ -37,6 +38,7 @@ from lnst.Common.ConnectionHandler import send_data
from lnst.Common.ConnectionHandler import ConnectionHandler
from lnst.Common.Config import lnst_config
from lnst.Common.Config import DefaultRPCPort
+from lnst.Common.Consts import MROUTE
from lnst.Slave.InterfaceManager import InterfaceManager
from lnst.Slave.BridgeTool import BridgeTool
from lnst.Slave.SlaveSecSocket import SlaveSecSocket, SecSocketException
@@ -59,6 +61,7 @@ class SlaveMethods:
self._copy_targets = {}
self._copy_sources = {}
self._system_config = {}
+ self.mroute_sockets = {}
self._cache = ResourceCache(lnst_config.get_option("cache",
"dir"),
lnst_config.get_option("cache",
"expiration_period"))
@@ -535,6 +538,11 @@ class SlaveMethods:
logging.info("Performing machine cleanup.")
self._command_context.cleanup()
+ for mroute_soc in self.mroute_sockets.values():
+ mroute_soc.close()
+ del mroute_soc
+ self.mroute_sockets = {}
+
self.restore_system_config()
devs = self._if_manager.get_mapped_devices()
@@ -1074,6 +1082,127 @@ class SlaveMethods:
return False
return True
+ def mroute_operation(self, op_type, op, table_id):
+ if not self.mroute_sockets.has_key(table_id):
+ logging.error("mroute %s table was not init", table_id)
+ return False
+ try:
+ self.mroute_sockets[table_id].setsockopt(socket.IPPROTO_IP,
+ op_type, op)
+ except Exception as e:
+ raise Exception("mroute operation failed")
+ return True
+
+ def mroute_init(self, table_id):
+ logging.debug("Initializing mroute socket")
+ if not self.mroute_sockets.has_key(table_id):
+ self.mroute_sockets[table_id] = socket.socket(socket.AF_INET,
+ socket.SOCK_RAW,
+ socket.IPPROTO_IGMP)
+ self.mroute_sockets[table_id].settimeout(0.5)
+ init_struct = struct.pack("I", MROUTE.INIT)
+ res = self.mroute_operation(MROUTE.INIT, init_struct, table_id)
+ return res
+
+ def mroute_finish(self, table_id):
+ logging.debug("Closing mroute socket")
+ finish_struct = struct.pack("I", MROUTE.FINISH)
+ res = self.mroute_operation(MROUTE.FINISH, finish_struct, table_id)
+ return res
+
+ def mroute_pim_init(self, table_id, pim_stop=False):
+ logging.debug("Initializing mroute PIM")
+ pim_struct = struct.pack("I", not pim_stop)
+ return self.mroute_operation(MROUTE.PIM_INIT, pim_struct, table_id)
+
+ def mroute_table(self, index):
+ logging.debug("Creating mroute table %d" % index)
+
+ self.mroute_sockets[index] = socket.socket(socket.AF_INET,
+ socket.SOCK_RAW,
+ socket.IPPROTO_IGMP)
+ table_struct = struct.pack("I", index)
+ return self.mroute_operation(MROUTE.TABLE, table_struct, index)
+
+ def mroute_add_vif(self, if_id, vif_id, table_id):
+ logging.debug("Adding mroute VIF index %d" % vif_id)
+
+ dev = self._if_manager.get_mapped_device(if_id)
+ if dev is None:
+ logging.error("Device with id '%s' not found." % if_id)
+ return False
+
+ if_index = dev.get_if_index()
+ vif_struct = struct.pack("HBBIII", vif_id, MROUTE.USE_IF_INDEX,
+ MROUTE.DEFAULT_TTL, 0, if_index, 0)
+ return self.mroute_operation(MROUTE.VIF_ADD, vif_struct, table_id)
+
+ def mroute_del_vif(self, if_id, vif_id, table_id):
+ logging.debug("Deleting mroute VIF index %d" % vif_id)
+ vif_struct = struct.pack("HBBIII", vif_id, 0,0, 0, 0, 0)
+ return self.mroute_operation(MROUTE.VIF_DEL, vif_struct, table_id)
+
+ def mroute_add_vif_reg(self, vif_id, table_id):
+ logging.debug("Adding mroute pimreg VIF with index %d" % vif_id)
+ vif_struct = struct.pack("HBBIII", vif_id, MROUTE.REGISET_VIF,
+ MROUTE.DEFAULT_TTL, 0, 0, 0)
+ return self.mroute_operation(MROUTE.VIF_ADD, vif_struct, table_id)
+
+ def mroute_add_mfc(self, source, group, source_vif, out_vifs,
+ table_id, proxi = False):
+ logging.debug("Adding mroute MFC route (%s, %s) -> %s" %
+ (source, group, str(out_vifs)))
+
+ ttls = [0] * MROUTE.MAX_VIF
+ for vif, ttl in out_vifs.items():
+ if vif >= MROUTE.MAX_VIF:
+ logging.error("ilegal VIF was asked")
+ return False
+ ttls[vif] = ttl
+
+ mfc_struct = socket.inet_aton(source) + socket.inet_aton(group) + \
+ struct.pack("H32B", source_vif, *ttls) + \
+ struct.pack("IIIIH", 0,0,0,0,0)
+
+ op_type = MROUTE.MFC_ADD if not proxi else MROUTE.MFC_ADD_PROXI
+ return self.mroute_operation(op_type, mfc_struct, table_id)
+
+ def mroute_del_mfc(self, source, group, source_vif, table_id,
+ proxi = False):
+ logging.debug("Deleting mroute MFC route (%s, %s)" % (source, group))
+
+ ttls = [0] * MROUTE.MAX_VIF
+ mfc_struct = socket.inet_aton(source) + socket.inet_aton(group) + \
+ struct.pack("H32B", source_vif, *ttls) + \
+ struct.pack("IIIIH",0, 0,0,0,0)
+
+ op_type = MROUTE.MFC_DEL if not proxi else MROUTE.MFC_DEL_PROXI
+ return self.mroute_operation(op_type, mfc_struct, table_id)
+
+ def mroute_get_notif(self, table_id):
+ if not self.mroute_sockets.has_key(table_id):
+ logging.error("mroute table %s was not init", table_id)
+ return False
+ try:
+ notif = self.mroute_sockets[table_id].recv(65*1024)
+ except:
+ return {}
+
+ if len(notif) < 28:
+ raise Exception("notif of wrong size was capture")
+
+ notif_type, zero, source_vif = struct.unpack("BBB", notif[8:11])
+ res = {}
+ if zero != 0:
+ res = {"error": True}
+ res["notif_type"] = notif_type
+ res["source_vif"] = source_vif
+ res["source_ip"] = socket.inet_ntoa(notif[12:16])
+ res["group_ip"] = socket.inet_ntoa(notif[16:20])
+ res["raw"] = notif
+ res["data"] = notif[28:]
+ return res
+
class ServerHandler(ConnectionHandler):
def __init__(self, addr):
super(ServerHandler, self).__init__()
--
2.8.4