1# 2# SPDX-License-Identifier: BSD-2-Clause 3# 4# Copyright (c) 2024 Klara, Inc. 5# Copyright (c) 2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org> 6# 7 8import argparse 9import scapy.all as sp 10import sys 11 12 13# 14# Emit a router advertisement with the specified prefix. 15# 16def main(): 17 parser = argparse.ArgumentParser("ra.py", 18 description="Emits Router Advertisement packets") 19 parser.add_argument('--sendif', nargs=1, required=True, 20 help='The interface through which the packet will be sent') 21 parser.add_argument('--src', nargs=1, required=True, 22 help='The source IP address') 23 parser.add_argument('--dst', nargs=1, required=True, 24 help='The destination IP address') 25 parser.add_argument('--hoplimit', nargs=1, required=False, 26 type=int, default=255, 27 help='The hop limit of IPv6 packet') 28 parser.add_argument('--rtrpref', nargs=1, required=False, 29 type=int, default=1, 30 help='The router preference advertised') 31 parser.add_argument('--rtrltime', nargs=1, required=False, 32 type=int, default=1800, 33 help='The router preference advertised') 34 parser.add_argument('--prefix', nargs=1, required=False, 35 help='The prefix to be advertised') 36 parser.add_argument('--prefixlen', nargs=1, required=False, 37 type=int, default=64, 38 help='The prefix length to be advertised') 39 parser.add_argument('--validlifetime', nargs=1, required=False, 40 type=int, default=4294967295, 41 help='The valid lifetime of the prefix') 42 parser.add_argument('--preferredlifetime', nargs=1, required=False, 43 type=int, default=4294967295, 44 help='The preferred lifetime of the prefix') 45 parser.add_argument('--route', nargs='*', required=False, 46 action='extend', type=str, default=[], 47 help='The route to be advertised') 48 parser.add_argument('--routelen', nargs='*', required=False, 49 action='extend', type=int, default=[], 50 help='The route length to be advertised') 51 parser.add_argument('--rtipref', nargs='*', required=False, 52 action='extend', type=int, default=[], 53 help='The route info preference advertised') 54 parser.add_argument('--rtiltime', nargs='*', required=False, 55 action='extend', type=int, default=[], 56 help='The route info preference advertised') 57 58 args = parser.parse_args() 59 pkt = sp.Ether() / \ 60 sp.IPv6(src=args.src, dst=args.dst, hlim=args.hoplimit) / \ 61 sp.ICMPv6ND_RA(chlim=64, 62 prf=args.rtrpref, 63 routerlifetime=args.rtrltime) 64 65 if (args.prefix): 66 pkt = pkt / \ 67 sp.ICMPv6NDOptPrefixInfo(prefix=args.prefix, 68 prefixlen=args.prefixlen, 69 validlifetime=args.validlifetime, 70 preferredlifetime=args.preferredlifetime) 71 72 for i in range(0, len(args.route)): 73 pkt = pkt / \ 74 sp.ICMPv6NDOptRouteInfo(prefix=args.route[i], 75 plen=args.routelen[i], 76 prf=args.rtipref[i], 77 rtlifetime=args.rtiltime[i]) 78 79 sp.sendp(pkt, iface=args.sendif[0], verbose=False) 80 sys.exit(0) 81 82 83if __name__ == '__main__': 84 main() 85