[prev in list] [next in list] [prev in thread] [next in thread] 

List:       helix-server-cvs
Subject:    [Server-cvs] tools/testtools simloss.py,NONE,1.1
From:       jgordon () helixcommunity ! org
Date:       2009-02-21 0:42:24
Message-ID: 200902210046.n1L0kweq022019 () mailer ! progressive-comp ! com
[Download RAW message or body]

Update of /cvsroot/server/tools/testtools
In directory cvs01.internal.helixcommunity.org:/tmp/cvs-serv19988

Added Files:
	simloss.py 
Log Message:
UDP packet forwarder with loss simulation


--- NEW FILE: simloss.py ---
#!/usr/bin/env python
# 
# ***** BEGIN LICENSE BLOCK ***** 
# Source last modified: $Id: simloss.py,v 1.1 2009/02/21 00:42:21 jgordon Exp $
# 
# Copyright Notices:
# 
# Portions Copyright (c) 1995-2006 RealNetworks, Inc. All Rights Reserved.
#
# Patent Notices: This file may contain technology protected by one or 
# more of the patents listed at www.helixcommunity.org
#
# 1.   The contents of this file, and the files included with this file,
# are protected by copyright controlled by RealNetworks and its 
# licensors, and made available by RealNetworks subject to the current 
# version of the RealNetworks Public Source License (the "RPSL") 
# available at http://www.helixcommunity.org/content/rpsl unless 
# you have licensed the file under the current version of the 
# RealNetworks Community Source License (the "RCSL") available at
# http://www.helixcommunity.org/content/rcsl, in which case the RCSL
# will apply.  You may also obtain the license terms directly from
# RealNetworks.  You may not use this file except in compliance with
# the RPSL or, if you have a valid RCSL with RealNetworks applicable
# to this file, the RCSL.  Please see the applicable RPSL or RCSL for
# the rights, obligations and limitations governing use of the
# contents of the file.
# 
# This file is part of the Helix DNA Technology.  RealNetworks is the
# developer of the Original Code and owns the copyrights in the
# portions it created.   Copying, including reproducing, storing, 
# adapting or translating, any or all of this material other than 
# pursuant to the license terms referred to above requires the prior 
# written consent of RealNetworks and its licensors
# 
# This file, and the files included with this file, is distributed
# and made available by RealNetworks on an 'AS IS' basis, WITHOUT 
# WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS 
# AND ITS LICENSORS HEREBY DISCLAIM  ALL SUCH WARRANTIES, INCLUDING 
# WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
# FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
# 
# Technology Compatibility Kit Test Suite(s) Location: 
#    http://www.helixcommunity.org/content/tck
# 
# Contributor(s): 
#  
# ***** END LICENSE BLOCK ***** 

import sys
import string
import select
import random
import optparse

from socket import *

try:
    import msvcrt
    windows = True
except:
    windows = False


read_size = 2048

class UDPSockPair:
    def __init__(self, source_addr, dest_addr):
        self.read_addr = source_addr
        self.write_addr = dest_addr
        self.errstr = None
    # end func __init__

    def start(self):
        try:
            self.read_sock = socket(AF_INET, SOCK_DGRAM)
            self.write_sock = socket(AF_INET, SOCK_DGRAM)
        
            self.read_sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
            self.write_sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        
            self.read_sock.setblocking(0)
            self.read_sock.bind(self.read_addr)
            
        except socket.error, e:
            self.errstr = "socket error (%d): %s" % (e[0], e[1])
        # end try
    # end func start
    
    def close(self):
        try:
            self.read_sock.close()
            self.write_sock.close()
        except socket.error, e:
            self.errstr = "socket error (%d): %s" % (e[0], e[1])
        # end try
    # end func close

    def receive(self, loss=0):
        try:
            pkt = self.read_sock.recv(read_size)
            if (pkt):
                # randomly drop (don't forward) N% of packets read
                if (loss == 0 or random.randint(1, 100) > loss):
                    self.write_sock.sendto(pkt, self.write_addr)
                # end if
            # end if
        except socket.error, e:
            self.errstr = "socket error (%d): %s" % (e[0], e[1])
        # end try
    # end func receive
#end class UDPSockPair

class UDPForwarder:
    def __init__(self):
        self.readers = []
        self.sockpairs = []
        self.readmap = {}
        self.end_str = ""
        self.loss = 0
    #end func __init__

    def check_sock_error(self, sockpair):
        if (sockpair.errstr):
            self.end_str = sockpair.errstr
            sockpair.close()
            self.close()
            return True
        # end if
        return False
    # end func check_sock_error
    
    def setup(self, options, stop_seq=[]):
        self.stop_seq = stop_seq
        self.loss = options.loss_pct
        
        for i in range(len(options.source_ports)):
            sockpair = UDPSockPair(
                (options.source_addr, options.source_ports[i]),
                (options.dest_addr, options.dest_ports[i]))
            
            if (self.check_sock_error(sockpair)):
                break

            self.sockpairs.append(sockpair)
        # end loop
    # end func setup
    
    def run(self):
        random.seed()
                    
        for sock in self.sockpairs:
            sock.start()
            if (self.check_sock_error(sock)):
                return
            
            self.readmap[sock.read_sock] = sock
            self.readers.append(sock.read_sock)
        # end loop

        # stdin is not a socket on Windows and can't be used in select
        if (not windows):
            self.readers.append(sys.stdin)
        # end if
        
        terminating = False
        errno = None
        errstr = None
        
        while not terminating:
            try:
                readers, writers, errs = select.select(self.readers, [], [], 2)
            except select.error, e:
                terminating = True
                self.end_str = "select error (%d): %s" % (e[0], e[1])
                break
            except socket.error, e:
                self.end_str = "socket error (%d): %s" % (e[0], e[1])
                break
            # end try
                
            for r in readers:
                if (r == sys.stdin):
                    data = sys.stdin.read
                    if (data in self.stop_seq):
                        terminating = True
                        self.end_str = "terminated by user"
                        break
                    # end if
                else:
                    sock_pair = self.readmap[r]
                    if (sock_pair):
                        sock_pair.receive(self.loss)
                        if (self.check_sock_error(sock_pair)):
                            break
                        # end if
                    # end if
                # end if
            # end loop

            # handle keyboard input on windows
            if (windows and msvcrt.kbhit()):
                data = msvcrt.getch()
                if (data in self.stop_seq):
                    terminating = True
                    self.end_str = "terminated by user"
                # end if
            # end if
        # end loop

        self.close()
    # end func run

    def close(self):
        for sock in self.sockpairs:
            sock.close()
        # end loop
        self.sockpairs = []
    # end func close

#end class UDPForwarder

def parse_ports(ports_str):
    range_list = ports_str.split(',')
    port_list = []
    for range_val in range_list:
        vals = string.split(range_val, '/', 2)
        str_count = len(vals)
        start_val = 0
        count = 0

        if (str_count > 0 and vals[0].isdigit()):
            start_val = int(vals[0])
            if (start_val > 0):
                count = 1
            # end if
        # end if
        
        if (count > 0 and str_count > 1 and vals[1].isdigit()):
            count = int(vals[1])
            if (count < 1):
                count = 0
            # end if
        #end if

        for i in range(0, count):
            port_list.append(start_val + i)
        # end loop
    #end loop

    return port_list
# end func parse_ports

def parse_command_line():
    cmd_parser = optparse.OptionParser()
    cmd_parser.add_option("-a", "--addr", 
                          dest="dest_addr", default="127.0.0.1",
                          help="destination address [default: %default]",
                          metavar="ADDRESS")
    cmd_parser.add_option("-p", "--port", 
                          dest="dest_port_str", default="",
                          help="destination port(s) [required]",
                          metavar="PORT1/COUNT1,...,PORTn/COUNTn")
    cmd_parser.add_option("-s", "--source-addr",
                          dest="source_addr", default="127.0.0.1",
                          help="source address (you probably don't need this) [default: %default]",
                          metavar="ADDRESS") 
    cmd_parser.add_option("-r", "--source-port", 
                          dest="source_port_str", default="",
                          help="source port or port range [default: destination port]",
                          metavar="PORT1/COUNT1,...,PORTn/COUNTn")
    cmd_parser.add_option("-l", "--loss",
                          dest="loss_pct", type="int", default=0,
                          help="percentage of packets to drop [default: 0]",
                          metavar="LOSS")
    
    options, args = cmd_parser.parse_args()

    if (options.dest_port_str == ""):
        cmd_parser.error("port(s) to forward must be provided!")
    else:
        options.dest_ports = parse_ports(options.dest_port_str)
    # end if
        
    if (options.source_port_str == ""):
        options.source_ports = options.dest_ports
    else:
        options.source_ports = parse_ports(options.source_port_str)
    # end if

    if (len(options.source_ports) == 0 or len(options.dest_ports) == 0):
        cmd_parser.error("invalid port specification")
    # end if
    
    if (len(options.source_ports) != len(options.dest_ports)):
        cmd_parser.error("mismatched source and destination ports")
    # end if

    for i in range(len(options.source_ports)):
        if (options.source_addr == options.dest_addr and
            options.source_ports[i] == options.dest_ports[i]):
            
            cmd_parser.error("cannot forward %s:%d to itself!" % \
                             (options.dest_addr, options.dest_ports[i]))
        # end if
        
        print "forwarding %s:%d to %s:%d" % \
              (options.source_addr, options.source_ports[i],
               options.dest_addr, options.dest_ports[i])
    # end loop

    if (options.loss_pct < 0 or options.loss_pct > 100):
        cmd_parser.error("invalid loss percentage %d" % options.loss_pct)
    
    return options
# end func parse_command_line

def main():
    # parse_command_line will terminate executation in case of failure
    options = parse_command_line()
    
    proxy = UDPForwarder()
    proxy.setup(options, ['q', 'Q'])

    print("")
    print "Press 'q' to quit"
    print("")
    
    proxy.run()

    print(proxy.end_str)
#end func main


if (__name__ == "__main__"):
    main()
# end __main __


_______________________________________________
Server-cvs mailing list
Server-cvs@helixcommunity.org
http://lists.helixcommunity.org/mailman/listinfo/server-cvs
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic