1#!/usr/bin/env python3 2# 3# Copyright (c) 2021, 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# 29# 30# This script generates and sends MLD Query message on the given interface. 31# 32# Multicast Listener Query Message (RFC3810): 33# 34# 0 1 2 3 35# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 36# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 37# | Type = 130 | Code | Checksum | 38# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 39# | Maximum Response Code | Reserved | 40# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 41# | | 42# * * 43# | | 44# * Multicast Address * 45# | | 46# * * 47# | | 48# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 49# | Resv |S| QRV | QQIC | Number of Sources (N) | 50# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 51# 52# IPv6 Router Alert option (RFC2711) in a Hop-by-Hop Options header: 53# 54# 0 1 2 3 55# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 56# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 57# | Next Header | Hdr Ext Len |0 0 0|0 0 1 0 1|0 0 0 0 0 0 1 0| 58# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 59# | Value (2 octets) | PadN | 60# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 61# 62 63import sys 64import socket 65import syslog 66import struct 67 68ICMP6_TYPE_MLD_QUERY = 130 69IPV6_EXT_HBH_ROUTER_ALERT = 5 70 71def main(): 72 interface = sys.argv[1] 73 dst = sys.argv[2] 74 75 log = '====otbr-agent=send_mld_query=== send_mld_query.py started' 76 syslog.syslog(syslog.LOG_ERR, log) 77 print(log) 78 79 log = 'interface %s' % interface 80 syslog.syslog(syslog.LOG_ERR, log) 81 print(log) 82 83 log = 'dst %s' % dst 84 syslog.syslog(syslog.LOG_ERR, log) 85 print(log) 86 87 try: 88 sock = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_ICMPV6) 89 90 # Configure SO_BINDTODEVICE to bind the network interface. 91 sock.setsockopt(socket.SOL_SOCKET, 25, interface.encode('utf-8')) 92 sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 1) 93 94 # Build ICMPv6 MLD Query message header 95 query = struct.pack("B", ICMP6_TYPE_MLD_QUERY) # Type: 130 96 query += struct.pack("B", 0) # Code 97 query += struct.pack("!H", 0) # Checksum 98 99 # Query Response Interval: 10 seconds (RFC 3810, section 9.3) 100 query += struct.pack("!H", 10000) 101 102 # Reserved field 103 query += struct.pack("!H", 0) 104 105 # Multicast address (unspecified) 106 query += struct.pack("!16s", ''.encode()) 107 108 # Querier's Robustness Variable: 2 (RFC 3810, section 9.1) 109 query += struct.pack("B", 2) 110 111 # Querier's Query Interval Code: 125 (RFC 3810, section 9.2) 112 query += struct.pack("B", 125) 113 114 # Number of sources 115 query += struct.pack("!H", 0) 116 117 # Build IPv6 Hop-by-Hop header 118 ext_hdr = struct.pack("B", socket.getprotobyname('ipv6-icmp')) 119 ext_hdr += struct.pack("B", 0) 120 121 # Include Router Alert option (RFC 2711) 122 ext_hdr += struct.pack("B", IPV6_EXT_HBH_ROUTER_ALERT) 123 ext_hdr += struct.pack("B", 2) # Length 124 ext_hdr += struct.pack("!H", 0) # MLD message 125 126 # Insert PadN option to keep alignment (2-bytes) 127 ext_hdr += struct.pack("B", 1) 128 ext_hdr += struct.pack("B", 0) 129 130 # Send MLD Query message 131 sock.sendmsg([query], [(socket.IPPROTO_IPV6, socket.IPV6_HOPOPTS, ext_hdr)], 0, (dst, 0, 0)) 132 133 except Exception as e: 134 log = '====otbr-agent=send_mld_query=== sendmsg %s' % str(e) 135 syslog.syslog(syslog.LOG_ERR, log) 136 print(log) 137 138 sock.close() 139 log = '====otbr-agent=send_mld_query=== close' 140 syslog.syslog(syslog.LOG_ERR, log) 141 print(log) 142 143if __name__ == '__main__': 144 main() 145