Enhanced multicast support: generate IGMP messages for join/leave
authorClaudio-Daniel Freire <claudio-daniel.freire@inria.fr>
Fri, 26 Aug 2011 11:07:46 +0000 (13:07 +0200)
committerClaudio-Daniel Freire <claudio-daniel.freire@inria.fr>
Fri, 26 Aug 2011 11:07:46 +0000 (13:07 +0200)
The kernel seems to miss many of them when they're associated to virtual interfaces

src/nepi/testbeds/planetlab/scripts/tun_connect.py
src/nepi/util/ipaddr2.py
src/nepi/util/tunchannel.py

index 92f7fed..dce367b 100644 (file)
@@ -219,6 +219,8 @@ class MulticastThread(threading.Thread):
         self.igmp_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IGMP)
         self.igmp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF,
             socket.inet_aton(options.vif_addr) )
+        self.igmp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
+        self.igmp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)
         self._stop = False
         self.setDaemon(True)
     
@@ -226,6 +228,7 @@ class MulticastThread(threading.Thread):
         devnull = open('/dev/null','r+b')
         maddr_re = re.compile(r"\s*inet\s*(\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3})\s*")
         cur_maddr = set()
+        lastfullrefresh = time.time()
         while not self._stop:
             # Get current subscriptions
             proc = subprocess.Popen(['ip','maddr','show',tun_name],
@@ -239,19 +242,27 @@ class MulticastThread(threading.Thread):
                     new_maddr.add(match.group(1))
             proc.wait()
             
-            # Notify new subscriptions
-            for grp in new_maddr - cur_maddr:
-                self.igmp_socket.sendto(
-                    ipaddr2.igmp(0x16, 0, grp), 
-                    0, 
-                    (grp,0))
+            # Every now and then, send a full report
+            now = time.time()
+            report_new = new_maddr
+            if (now - lastfullrefresh) <= 30.0:
+                report_new = report_new - cur_maddr
+            else:
+                lastfullrefresh = now
+            
+            # Report subscriptions
+            for grp in report_new:
+                igmpp = ipaddr2.ipigmp(
+                    options.vif_addr, '224.0.0.2', 1, 0x16, 0, grp, 
+                    noipcksum=True)
+                self.igmp_socket.sendto(igmpp, 0, ('224.0.0.2',0))
 
             # Notify group leave
             for grp in cur_maddr - new_maddr:
-                self.igmp_socket.sendto(
-                    ipaddr2.igmp(0x17, 0, grp)
-                    0, 
-                    (grp,0))
+                igmpp = ipaddr2.ipigmp(
+                    options.vif_addr, '224.0.0.2', 1, 0x17, 0, grp
+                    noipcksum=True)
+                self.igmp_socket.sendto(igmpp, 0, ('224.0.0.2',0))
 
             cur_maddr = new_maddr
             
index 7211404..436979f 100644 (file)
@@ -49,32 +49,43 @@ def ipdistn(a,b):
 def inet_cksum(packet):
     words = array.array('H')
     words.fromstring(packet[:len(packet)&~0x1])
-    cksum = sum(words)
+    htons = socket.htons
+    cksum = 0
+    for word in words:
+        cksum += htons(word)
     if len(packet)&0x1:
-       cksum += ord(packet[-1])
+        cksum += ord(packet[-1])
+    cksum &= 0xffffffff
     cksum = (cksum >> 16) + (cksum & 0xffff)
     cksum += (cksum >> 16)
     return ~cksum
 
-def iphdr(src, dst, datalen, ttl, proto):
+def iphdr(src, dst, datalen, ttl, proto, tos=0, nocksum=False, ipid=0):
     cksum = 0
     src = socket.inet_aton(src)
     dst = socket.inet_aton(dst)
     hdr = struct.pack('!BBHHHBBH4s4s', 
-        0x45, 0, datalen + 5*32, int(random.random() * 65536) & 0xffff, 0, 
-        ttl, proto, cksum & 0xffff, src, dst)
-    cksum = inet_cksum(hdr)
-    hdr = struct.pack('!BBHHHBBH4s4s', 
-        0x45, 0, datalen + 5*32, int(random.random() * 65536) & 0xffff, 0, 
+        0x45, tos, datalen + 5*4, ipid, 0, 
         ttl, proto, cksum & 0xffff, src, dst)
+    if not nocksum:
+        cksum = inet_cksum(hdr)
+        hdr = struct.pack('!BBHHHBBH4s4s', 
+            0x45, tos, datalen + 5*4, ipid, 0, 
+            ttl, proto, cksum & 0xffff, src, dst)
     return hdr
 
-def igmp(type, mxrt, grp):
+def igmp(type, mxrt, grp, nocksum=False):
     cksum = 0
     grp = socket.inet_aton(grp)
     ighdr = struct.pack('!BBH4s', type, mxrt, cksum & 0xffff, grp)
-    cksum = inet_cksum(ighdr)
-    ighdr = struct.pack('!BBH4s', type, mxrt, cksum & 0xffff, grp)
+    if not nocksum:
+        cksum = inet_cksum(ighdr)
+        ighdr = struct.pack('!BBH4s', type, mxrt, cksum & 0xffff, grp)
     return ighdr
 
+def ipigmp(src, dst, ttl, type, mxrt, grp, noipcksum=False, noigmpcksum=False):
+    igmpp = igmp(type, mxrt, grp, nocksum=noigmpcksum)
+    iph = iphdr(src, dst, len(igmpp), ttl, 2, tos=0xc0, nocksum=noipcksum)
+    return iph+igmpp
+
 
index ee914fe..c44803f 100644 (file)
@@ -196,8 +196,9 @@ def tun_fwd(tun, remote, with_pi, ether_mode, cipher_key, udp, TERMINATE, stderr
         len=len, max=max, min=min, OSError=OSError, select=select.select, selecterror=select.error, os=os, socket=socket,
         retrycodes=(os.errno.EWOULDBLOCK, os.errno.EAGAIN, os.errno.EINTR) ):
     crypto_mode = False
+    crypter = None
     try:
-        if cipher_key:
+        if cipher_key and cipher:
             import Crypto.Cipher
             import hashlib
             __import__('Crypto.Cipher.'+cipher)