#!/usr/bin/env python # vim: set ts=8 sw=4 sts=4 et ai: # Standalone UDP ping utility to keep an UDP connection through a NATing # gateway alive. Run from the same machine as your sip client/PBX which needs # its NAT to stay punctured. # # Usage: udp-keepalive.py sip.myprovider.com # Walter Doekes 2011-2012, for Voys Telecom # # v2: added SO_REUSEADDR # v3: never abort on failure, add log instead, add basic dameonize capability, # add a few helpful notes import socket, sys def udp_keepalive(hosts, data='\0\0\0\0', dport=None, sport=None, quiet=False): fromaddr = ('0.0.0.0', sport) for host in hosts: addrinfo = socket.getaddrinfo(host, dport, socket.AF_INET, socket.SOCK_DGRAM, socket.SOL_UDP) for family, type, proto, unused, address in addrinfo: sock = socket.socket(family, type, proto) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: sock.bind(fromaddr) sock.sendto(data, address) if not quiet: print 'sent %r to %r' % (data, address) except: print >>sys.stderr, ('could not send from %r to %r' % (fromaddr, address)) finally: sock.close() if __name__ == '__main__': import os, time from optparse import OptionParser argv0 = os.path.basename(__file__) parser = OptionParser() parser.add_option('-d', '--dport', dest='dport', default='5060', action='store', type='int', help='destination PORT (defaults to 5060)', metavar='PORT') parser.add_option('-s', '--sport', dest='sport', default='5060', action='store', type='int', help='source PORT (defaults to 5060)', metavar='PORT') parser.add_option('-q', '--quiet', dest='quiet', default=False, action='store_true', help='quiet operation') parser.add_option('-D', '--daemon', dest='daemon', default=False, action='store_true', help='fork into daemon mode') (opt, args) = parser.parse_args() if not args: print >>sys.stderr, '%s: Need one or more hosts to ping.' % argv0 print >>sys.stderr, "Try `%s --help' for more information." % argv0 sys.exit(1) if opt.daemon: opt.quiet = True os.chdir('/') sys.stdin.close() sys.stdout.close() sys.stderr.close() # Double fork, since everyone (including upstart) expects us to. if os.fork(): os._exit(0) if os.fork(): os._exit(0) # .. and run setsid?? # Reopen stderr, so we can write and not die. sys.stderr = open('/dev/null', 'a') if not opt.quiet: print 'Press CTRL+C to quit' while True: try: udp_keepalive(args, dport=opt.dport, sport=opt.sport, quiet=opt.quiet) time.sleep(15) except KeyboardInterrupt: sys.exit(0)