1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 import socket
34 import struct
35 import fcntl
36 import array
37 import platform
38
39 import rospy
42 '''
43 The McastSocket class enables the send and receive UDP messages to a multicast
44 group.
45 '''
46
47 - def __init__(self, port, mgroup, reuse=True, ttl=20):
48 '''
49 Creates a socket, bind it to a given port and join to a given multicast group.
50 IPv4 and IPv6 are supported.
51 @param port: the port to bind the socket
52 @type port: int
53 @param mgroup: the multicast group to join
54 @type mgroup: str
55 @param reuse: allows the reusing of the port
56 @type reuse: boolean (Default: True)
57 @param ttl: time to leave
58 @type ttl: int (Default: 20)
59 '''
60
61 addrinfo = socket.getaddrinfo(mgroup, None)[0]
62 socket.socket.__init__(self, addrinfo[0], socket.SOCK_DGRAM, socket.IPPROTO_UDP)
63
64
65 if(reuse):
66 self.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
67 if hasattr(socket, "SO_REUSEPORT"):
68 self.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
69
70 self.bind(('', port))
71
72 ttl_bin = struct.pack('@i', ttl)
73 if addrinfo[0] == socket.AF_INET:
74 self.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl_bin)
75 self.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
76 else:
77 self.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, ttl_bin)
78 socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, 1)
79
80
81 group_bin = socket.inet_pton(addrinfo[0], addrinfo[4][0])
82 try:
83 if addrinfo[0] == socket.AF_INET:
84 mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
85 self.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
86 else:
87 mreq = group_bin + struct.pack('@I', 0)
88 self.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
89 except socket.error, (errn, msg):
90 err = str(msg)
91 if errn in [19]:
92 err = ''.join(["socket.error[", str(errn), "]: ", msg, ",\nis multicast route set? e.g. sudo route add -net 224.0.0.0 netmask 224.0.0.0 eth0"])
93 raise Exception(err)
94
95 self.addrinfo = addrinfo
96 self.group_bin = group_bin
97 self.sock_5_error_printed = []
98
99
101 '''
102 Unregister from the multicast group and close the socket.
103 '''
104 if self.addrinfo[0] == socket.AF_INET:
105 mreq = self.group_bin + struct.pack('=I', socket.INADDR_ANY)
106 self.setsockopt(socket.IPPROTO_IP, socket.IP_DROP_MEMBERSHIP, mreq)
107 else:
108 mreq = self.group_bin + struct.pack('@I', 0)
109 self.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_LEAVE_GROUP, mreq)
110 socket.socket.close(self)
111
113 '''
114 Sends the given message to the joined multicast group. Some errors on send
115 will be ignored (C{ENETRESET}, C{ENETDOWN}, C{ENETUNREACH})
116 @param msg: message to send
117 @type msg: C{str}
118 '''
119 try:
120 self.sendto(msg, (self.addrinfo[4][0], self.getsockname()[1]))
121 except socket.error, (errn, msg):
122 if not errn in [100, 101, 102]:
123 raise
124
126 '''
127 Sends the given message to the joined multicast group. Some errors on send
128 will be ignored (C{ENETRESET}, C{ENETDOWN}, C{ENETUNREACH})
129 @param msg: message to send
130 @type msg: C{str}
131 @param addr: IPv4 or IPv6 address
132 @type addr: C{str}
133 '''
134 try:
135 self.sendto(msg, (addr, self.getsockname()[1]))
136 except socket.error, (errn, msg):
137 if errn in [-5]:
138 if not addr in self.sock_5_error_printed:
139 rospy.logwarn("socket.error[%d]: %s, addr: %s", errn, msg, addr)
140 self.sock_5_error_printed.append(addr)
141 elif not errn in [100, 101, 102]:
142 raise
143
145 '''
146 Test all enabled interfaces for a MULTICAST flag. If no enabled interfaces
147 has a multicast support, False will be returned.
148 @return: C{True}, if any interfaces with multicast support are enabled.
149 @rtype: C{boolean}
150 '''
151 SIOCGIFFLAGS = 0x8913
152 IFF_MULTICAST = 0x1000
153 IFF_UP = 0x1
154 for (ifname, ip) in McastSocket.localifs():
155 args = (ifname + '\0'*32)[:32]
156 try:
157 result = fcntl.ioctl(self.fileno(), SIOCGIFFLAGS, args)
158 except IOError:
159 return False
160 flags, = struct.unpack('H', result[16:18])
161 if ((flags & IFF_MULTICAST) != 0) & ((flags & IFF_UP) != 0):
162 return True
163 return False
164
165 @staticmethod
167 '''
168 Used to get a list of the up interfaces and associated IP addresses
169 on this machine (linux only).
170
171 @return:
172 List of interface tuples. Each tuple consists of
173 (interface name, interface IP)
174 @rtype: C{[(str, str)]}
175 '''
176 SIOCGIFCONF = 0x8912
177 MAXBYTES = 8096
178
179 arch = platform.architecture()[0]
180
181
182 var1 = -1
183 var2 = -1
184 if arch == '32bit':
185 var1 = 32
186 var2 = 32
187 elif arch == '64bit':
188 var1 = 16
189 var2 = 40
190 else:
191 raise OSError("Unknown architecture: %s" % arch)
192
193 sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
194 names = array.array('B', '\0' * MAXBYTES)
195 outbytes = struct.unpack('iL', fcntl.ioctl(
196 sockfd.fileno(),
197 SIOCGIFCONF,
198 struct.pack('iL', MAXBYTES, names.buffer_info()[0])
199 ))[0]
200
201 namestr = names.tostring()
202 return [(namestr[i:i+var1].split('\0', 1)[0], socket.inet_ntoa(namestr[i+20:i+24])) \
203 for i in xrange(0, outbytes, var2)]
204