1#!/usr/bin/env python3 2# 3# Copyright (c) 2020, The OpenThread Authors. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 1. Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# 2. Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in the 12# documentation and/or other materials provided with the distribution. 13# 3. Neither the name of the copyright holder nor the 14# names of its contributors may be used to endorse or promote products 15# derived from this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27# POSSIBILITY OF SUCH DAMAGE. 28# 29import ctypes 30import ctypes.util 31import socket 32import struct 33import sys 34import time 35 36MYPORT = 8123 37MYTTL = 1 # Increase to reach other networks 38 39libc = ctypes.CDLL(ctypes.util.find_library('c')) 40 41 42def if_nametoindex(name): 43 if not isinstance(name, str): 44 raise TypeError('name must be a string.') 45 ret = libc.if_nametoindex(name.encode('ascii')) 46 if not ret: 47 raise RuntimeError("Invalid Name") 48 return ret 49 50 51def if_indextoname(index): 52 if not isinstance(index, int): 53 raise TypeError('index must be an int.') 54 libc.if_indextoname.argtypes = [ctypes.c_uint32, ctypes.c_char_p] 55 libc.if_indextoname.restype = ctypes.c_char_p 56 57 ifname = ctypes.create_string_buffer(32) 58 ifname = libc.if_indextoname(index, ifname) 59 if not ifname: 60 raise RuntimeError("Invalid Index") 61 return ifname 62 63 64def main(): 65 args = sys.argv[1:] 66 is_sender = False 67 68 if args[0] == '-s': 69 is_sender = True 70 args.pop(0) 71 elif args[0] == '-u': 72 is_multicast_receiver = False 73 args.pop(0) 74 else: 75 is_multicast_receiver = True 76 77 ifname, group = args 78 79 if is_sender: 80 sender(ifname, group) 81 else: 82 receiver(ifname, group, is_multicast_receiver=is_multicast_receiver) 83 84 85def sender(ifname, group): 86 addrinfo = socket.getaddrinfo(group, None)[0] 87 88 s = socket.socket(addrinfo[0], socket.SOCK_DGRAM) 89 s.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, (ifname + '\0').encode('ascii')) 90 91 # Set Time-to-live (optional) 92 ttl_bin = struct.pack('@i', MYTTL) 93 assert addrinfo[0] == socket.AF_INET6 94 s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, ttl_bin) 95 96 while True: 97 data = repr(time.time()) 98 s.sendto((data + '\0').encode('ascii'), (addrinfo[4][0], MYPORT)) 99 time.sleep(1) 100 101 102def receiver(ifname, group, is_multicast_receiver=True): 103 # Look up multicast group address in name server and find out IP version 104 addrinfo = socket.getaddrinfo(group, None)[0] 105 assert addrinfo[0] == socket.AF_INET6 106 107 # Create a socket 108 s = socket.socket(addrinfo[0], socket.SOCK_DGRAM) 109 s.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, (ifname + '\0').encode('ascii')) 110 111 # Allow multiple copies of this program on one machine 112 # (not strictly needed) 113 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 114 115 # Bind it to the port 116 s.bind(('', MYPORT)) 117 118 if is_multicast_receiver: 119 group_bin = socket.inet_pton(addrinfo[0], addrinfo[4][0]) 120 # Join group 121 interface_index = if_nametoindex(ifname) 122 mreq = group_bin + struct.pack('@I', interface_index) 123 s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) 124 125 # Loop, printing any data we receive 126 while True: 127 data, sender = s.recvfrom(1500) 128 while data[-1:] == '\0': 129 data = data[:-1] # Strip trailing \0's 130 print(str(sender) + ' ' + repr(data)) 131 132 133if __name__ == '__main__': 134 main() 135