From fc2ac55c3ef8d6eab7c49b8955a06f3d1bff9cdb Mon Sep 17 00:00:00 2001 From: Scott Silver Date: Tue, 1 Sep 2020 17:57:19 -0700 Subject: [PATCH 1/6] Support authentication and server output. --- examples/authorized_users.txt | 1 + examples/client_with_auth.py | 38 ++++++++ examples/private.pem | 30 ++++++ examples/private_not_protected.pem | 27 ++++++ examples/public.pem | 9 ++ examples/server.py | 3 +- examples/server_with_auth.py | 54 +++++++++++ iperf3/iperf3.py | 148 +++++++++++++++++++++++++++-- tests/authorized_users.txt | 1 + tests/generate_auth_test_files.sh | 33 +++++++ tests/private_not_protected.pem | 27 ++++++ tests/public.pem | 9 ++ tests/test_iperf3.py | 104 +++++++++++++++++++- 13 files changed, 470 insertions(+), 14 deletions(-) create mode 100644 examples/authorized_users.txt create mode 100644 examples/client_with_auth.py create mode 100644 examples/private.pem create mode 100644 examples/private_not_protected.pem create mode 100644 examples/public.pem create mode 100644 examples/server_with_auth.py create mode 100644 tests/authorized_users.txt create mode 100755 tests/generate_auth_test_files.sh create mode 100644 tests/private_not_protected.pem create mode 100644 tests/public.pem diff --git a/examples/authorized_users.txt b/examples/authorized_users.txt new file mode 100644 index 0000000..e90c101 --- /dev/null +++ b/examples/authorized_users.txt @@ -0,0 +1 @@ +test,0a40291ff1b65f6ddd11f8979ee64981f43b6dec2d804bdb4bdc26d5889b4454 diff --git a/examples/client_with_auth.py b/examples/client_with_auth.py new file mode 100644 index 0000000..2254f03 --- /dev/null +++ b/examples/client_with_auth.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +import iperf3 +import base64 + +# Return the contents of a PEM file B64 encoded +def get_key_as_base64(key_file): + with open(key_file, 'rb') as file: + raw_key = file.read() + return base64.b64encode(raw_key).decode("ascii") + +client = iperf3.Client() +client.duration = 1 +client.server_hostname = '127.0.0.1' +client.port = 5200 +client.rsa_pubkey = get_key_as_base64("public.pem") +client.username = "test" +client.password = "test7" + +print('Connecting to {0}:{1}'.format(client.server_hostname, client.port)) +result = client.run() + +if result.error: + print(result.error) +else: + print('') + print('Test completed:') + print(' started at {0}'.format(result.time)) + print(' bytes transmitted {0}'.format(result.sent_bytes)) + print(' retransmits {0}'.format(result.retransmits)) + print(' avg cpu load {0}%\n'.format(result.local_cpu_total)) + + print('Average transmitted data in all sorts of networky formats:') + print(' bits per second (bps) {0}'.format(result.sent_bps)) + print(' Kilobits per second (kbps) {0}'.format(result.sent_kbps)) + print(' Megabits per second (Mbps) {0}'.format(result.sent_Mbps)) + print(' KiloBytes per second (kB/s) {0}'.format(result.sent_kB_s)) + print(' MegaBytes per second (MB/s) {0}'.format(result.sent_MB_s)) diff --git a/examples/private.pem b/examples/private.pem new file mode 100644 index 0000000..25d4cc7 --- /dev/null +++ b/examples/private.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,ED1E03B144BE45A0 + +TFaQqvTeL5ta07M6iodK22R8v2T0ssbgABL2avgOTvif3YlQB2FKIYLw1qW4e9Va +0kLV8rqifoadR7Cx3ub/gCIr6yQ9cQIK/eSC6knWlZysguJglwzIJSzZx88pxDf9 +cUtgRp2m5M3en4urM9xV58Y+gud+JQzW1yMUA7wCRyNf5RKfS/D/FwzsStuhHw3s +2TbpZrh1XOr06uM37O1baLs35PIcW5QPxClCRizBM64e6eD7VA+GMUDjCADBuAT7 +dfHj1P95jd8pnh0lcLiVu4RJ4o8+J6p0Ks9pRc/qifdMoOGy7P4Zj4bDkdYUaNsd +Gy5TTzwUrUt/dCnrJQfUzY9VMTnx2zownQu9kfGt/rkxDC3N6I8TrWA2WZM2TpTe +b/8Ch5mVjMaltbYflmhx4l+grN1YuWrsj1+B6d+kPzMr+G5+UO+t9vtWoUlx4E3t +4O/BM9dw+hn6gsDhlzekjt7T7wGKpNxLIJuVGSTCR2o5bwBSQpIxfKf2Jf2DaeYs +Cigvt1FpCx4o1n3ReDMF+M/cqzdFvjjgL23U0IMC7FO307zrF5IBWUGrjpTkL+wn +37uqRfWbGy/NzwT3bxFRjxwf/8Jrl5fYNe3giaKgXy5z7wxeLhdSnYRBnVeCqZF0 +diu080Y5fdgsPYPTnVNoNtkXtm9nkjRDf7SJ7nIfZwCyH7ukl0B7r+V6DtcwjzJ2 +XNB3bxOv24N1H7+BwhXIF5Y8xCkaoLSLJZr6z3gFUrWEd0R3gsEEFTfDWCMmZlD/ +wMU7L18zEDz5oeUobTQGV5qgflc3fdxg5EdmgPeP5x4DYOzaXDl/+gZF9CfsVCL9 +95l7LSQp6uIsVFBLwwvUx/4fwc1t4q7SCMp1f8gHApHsCamQU4SQSRhjKnrAkuJ6 +K5hg4V0CnySRrQMjAFfmmduYNd+cU2FDV5VBWCllP/tSXDLugkTo9PO2sNQb8316 +lTvoCQZpwNWpphahN37AQAOgpfbqs5sijA4Ns1+vSgYewttRPAtbJT0VbHhkFAfd +37TPtXRacbI4mre1CEFjccBaIZEA9goZZOFkzvC4YihqLeKhjDwwt5hFKFtjgR4k +NyLOs4m8lxiku3d0rPpC4EOtHHM7Ny7WskDK+6//Bra/h/EQVp0fM+PgIaXvMwx+ +QT7eX9ZURTO3Pjg5z5WC6P6Ek5o0jej8FlmrDOEKadWOz/2euPueDTN3m/MQMa+M +HtFeQj/pLcF2loOi6vOAjdDPtok9dID9PfFquT6o9DyIpsLCZIectz940yk9tKnk +asq3ym0cAgLKCfb93MN4hYS5Z3KOeZCTECAAWCrXn9ldXz+/Qgw2ARIZxPc8i3i1 +YtRlCo1TGErKVAvmIDIIx1prjkD6i7X3G2gm6C8zSdWoIoZQrySeaaSTj1ie8oDt +E4BaN2cgYvKRYPeJM/VyS4mYwh3Fod0LKfY6w8cat2DEnE4TZql5YByaeT0JHAZt +mTIsU3cTuQX9lPSoCKm5J5NG0Y7t3ZDqj2FqVuy705e+LDmYRrq+WtA4X19VCC2d +Y3YZKYIZyv4za/2M4DaNVz5zd2Y+hYXLR1IyKKGFjhD0lwouiTU4Pm94mqKm4Zoy +-----END RSA PRIVATE KEY----- diff --git a/examples/private_not_protected.pem b/examples/private_not_protected.pem new file mode 100644 index 0000000..f3161ea --- /dev/null +++ b/examples/private_not_protected.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAsSsC4nLqgVEaWTu2t8mvR36InaRAlJymV5S1HlSq6WHGJnWw +rVEIsGbkbWcVwr6KWZybpskmkhg2nLCrSAG8TaObebyCtRslS0ZxGzn+zRFG5WQg +s6c6yHm+gSS2ZQSri1IerNym8RdgoV3Gv8LoAFIM1R1a0T3agUEwQmIjwqnay0lk +pAabAcsRoxhvddvvZhHUFzyKLZonblHA+NNB+I9ySQTSh6sbgBwLBpA3rVl1Qght +jlKAHiQ/r9bJbHjqWNRL6Bx47/g33Rp5mQcJryyXVgDudGaIxWeo7J5Fz95u9YbJ +IKlGywddWWmEC3qWatQ8MCkGzKorXdu+AtPhkQIDAQABAoIBAD96EqGB3OWSYmdg +TroQx/1Mp7QbmovkiHDOtgn0+ft+bZlwB2pBo2RYqTVNfu10iWtMAjTC00/63u5L +GkNFY+tJWDZIxWgQHs+WoIXJxLJCo20i79iObTBQ9mGc/sC46KGVPapfsGynHJcU +W1ZvheI/g5uD1fssNzKiYaEXOorO6U0zp+WfNwCSgrz759WfLv/1tPIflfAkZz8Y +o46vaE4r/Oglx8VVb8yK8/iNlb/qRmqEswICn7EESHcd4v9N1sfrC/mgMx4Oehd3 +trQd1czAg+2Fdeg8udeFr8TN7t3mzRdO87DWC7hxuuIzLJ7Kw1w5TkfDbL2FuWtP +/Azu5QUCgYEA6/SxZUvq0Wo7yPKTpM/labIi0jBjeklzPCDZYJ6IeQt6g0OMFm9E +NeP9VkWdC5vOToe+UYLWgwbvAZF34DqYd0Cz+l81S3Hx5yr1AUGMraEwFL2ddDSE +4RsfQjyU/2JPibYfh9C5JQVMKllL39Hy6yYTYOCCae+SXdCunlDOdi8CgYEAwDfd +nonYHsOfKle46D4d+3gopk0miLnQbQk+ejLm6y+mGURORUpPnmgCT39PqkI7yfND +q9923iR/kfcCVk3l1Z9GUIvVeZalk4oPbGqSujkzPmwFuQ0DxI3MxniW93lvPu4M +hmzSSTCzdHWcVGiMR3Iv+svuTTjSK3Qmmaag9D8CgYEA4YLf+Nxx9Ov/b7LIAYDg +1f/238R3juycQh9zIrDh5i5eTQ5F2TtGier4uXeW/P1QVCXQc78Enf4GeA078QaP +qYoheW19Vjn05OddenHXriqb9Xa0AdggF7UATyHvlZW0Rv3VbMzD5uoFPYRSy/uw +m6WsZ66rdewOi7Ni8NRyPukCgYEAv3Jk/1SXtvxr+QZqGpZT0nT5N7m72Y4Is7Dz +U06DhFdu/dd0hbrf1gwN0RQiSpexaREro3HsuJBtpvn4j/ECK2R56ht8QZ/zxiFJ +/1eeUHBMQ63ffVIjII+DF2JgKpf+XxVF0FVulIs3EZVaYQ/IOBjZBNg8vxkK61DC +aWL3CI0CgYEA4/pRb37I0MwijBRZl4SY6hDQDidAoL3Owkp1ZuXOn3rSnNz/+WGw +ukoktnNmb/RA88Ywcz25fazg1r3RoQj/5BayJv7eUFgSk65QYKT/kgiJf47GK1Q7 +It2SH9o6bHQhnc2Xvmx95GYZ7sDAviv7xArSGjA+y7n0NanyeMqQ5WI= +-----END RSA PRIVATE KEY----- diff --git a/examples/public.pem b/examples/public.pem new file mode 100644 index 0000000..811822c --- /dev/null +++ b/examples/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsSsC4nLqgVEaWTu2t8mv +R36InaRAlJymV5S1HlSq6WHGJnWwrVEIsGbkbWcVwr6KWZybpskmkhg2nLCrSAG8 +TaObebyCtRslS0ZxGzn+zRFG5WQgs6c6yHm+gSS2ZQSri1IerNym8RdgoV3Gv8Lo +AFIM1R1a0T3agUEwQmIjwqnay0lkpAabAcsRoxhvddvvZhHUFzyKLZonblHA+NNB ++I9ySQTSh6sbgBwLBpA3rVl1QghtjlKAHiQ/r9bJbHjqWNRL6Bx47/g33Rp5mQcJ +ryyXVgDudGaIxWeo7J5Fz95u9YbJIKlGywddWWmEC3qWatQ8MCkGzKorXdu+AtPh +kQIDAQAB +-----END PUBLIC KEY----- diff --git a/examples/server.py b/examples/server.py index fbebcfa..d09946e 100755 --- a/examples/server.py +++ b/examples/server.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -import iperf3 - +import iperf3.iperf3 server = iperf3.Server() print('Running server: {0}:{1}'.format(server.bind_address, server.port)) diff --git a/examples/server_with_auth.py b/examples/server_with_auth.py new file mode 100644 index 0000000..949bbbd --- /dev/null +++ b/examples/server_with_auth.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 + +import iperf3.iperf3 +import re + +server = iperf3.Server() +rsa_privkey = '''MIIEpAIBAAKCAQEA0AKoqwFjngchlz/If0DLBP+5/dmgWpJKgVlHokkQk3ZRj0PR +bSt8zLnmbytsZL8QdknhRuNZXT3S0kY/E5V6+jbTaM59MjIfr0n76xIwIC9fxp6P +qb0YNvC3T2seX8x9GQZsm8k8Ur7hQtLpi3DTibGgD7I4qfquqg37b+4ZHRnwlok2 +0tznkoboCGqpzp2DsgXfpstEAA8lajkEdkJopx6VDq8r3o8Gu33TTyAzSZn0ATcN +jGe7jJ/RiGGJb3X2qnGsqRgECoTmXG1M/H1G5cXp/TLQKOx1pnQe9BNrIaMZdiNo ++FBU2XYqqMRA4w+Blu14hURTm4APL316P5gtzwIDAQABAoIBAQCklNX7p/e3+5B6 +ab8r4IpCBEyXK/ASeZl1yrxBDWqOIpnZryEvLa9rFNPcthDnjb1qun4CZrQ4cEg0 +k9wolKde/q47SNYKN1qWiQVIM3XcoV84ehDVjoZwQfnoXqsDxXpdqJWalZijJ+B3 +zQV4jObzFZW9lZf79hro9nMxVsSMHqLohg08trPd8OMY+zIpXGa1+8XgowXXWLfW +h8tFUv8kqD6S61oBN0d41SntVKB5s+shweY+rHy8PtazyQS7MPnAkcVQUMNlMyzH +57/f1/6hOF7xJJrUf6XhIdrTioVmmTvsmGFI88BlPTzcSNRJH+IDbOd+3yA/Yc+a +6ny37woxAoGBAOm5gt1FZ5DPkGZdmiNufSc6Qz9MThBX4DckQhEYQkn6IQIeBhYN +bn1W6E8q0x9TWFOiW2QcrzUrnS4JJvf5Lo8GkP9ECzg3XaNeTX3N+jE3QsFGskYz +t54YeqQvq9N0h20qBV5Msm0aQT4HiNsuvdR4R2DdsaUG845RXCk3xKK1AoGBAOPV +w0SToTw8tsZ/qgz2q65mO1NJLPvqx6Tne7xLiPWmyGq1SgtquolBDgDbNkAi0zj2 +iROlmVON8svZM2/NCsjSMsd4oScRylk+OQxLCc+4Imn7S68ltFx7lK/uXzHuJOmT +UP3RYRhj/mFrnk+bW2gBiTyDzoWhe7uQdjbDlEzzAoGAM8WhBEycbUpdDR/Mxe5y +kJ7qSHopjJs6klxYuhqqjGJ4r1RhOr9M6zy2BttQms7GcPg00E8+TEPV9F2YoTM0 +KgBlW/YBmjlBZ6+68JQQyJgaFGAJm11XXhDEEdxxbkyQtxCo0cOhfmNjck8O9KY8 +7HScMwvIjuqkRrEk/ghZaUUCgYAdSfitUzEyFjgE8pqAyiEt7VSJE2omBHuf1mZQ +wYEc21D+bsnTB+htBqDvOT8TJabztrXOgcZhOGlTDVwQblKJmIQQopBM/nt914Lr +8qWTP3+lEjobjQRPs09dAo7YU14JbPfHeWg7A3fLPFOAtl8c0r26utMM2MKYTSz1 +Q1VIQwKBgQC2bHZ/97JyeFCdprerX5khUv7tgZimaTlApVbx+GiF4ifpUcy1+aNe +ECtT7nEtplN1P1AinWUEXCtjETzR5eEKv0Rxof/YUXVMBr5XwpQZujNtmPHEgnNM +QnYrdXI6zUXOntc0SfDNkVJI9wLIM2flZ1/oV3mboTk/a3hefbY6zQ==''' +server.rsa_privkey = re.sub(r"[\n\t\s]*", "", rsa_privkey) +server.authorized_users = "authorized_users.txt" +print('Running server: {0}:{1}'.format(server.bind_address, server.port)) + +while True: + result = server.run() + + if result.error: + print(result.error) + else: + print('') + print('Test results from {0}:{1}'.format(result.remote_host, + result.remote_port)) + print(' started at {0}'.format(result.time)) + print(' bytes received {0}'.format(result.received_bytes)) + + print('Average transmitted received in all sorts of networky formats:') + print(' bits per second (bps) {0}'.format(result.received_bps)) + print(' Kilobits per second (kbps) {0}'.format(result.received_kbps)) + print(' Megabits per second (Mbps) {0}'.format(result.received_Mbps)) + print(' KiloBytes per second (kB/s) {0}'.format(result.received_kB_s)) + print(' MegaBytes per second (MB/s) {0}'.format(result.received_MB_s)) + print('') diff --git a/iperf3/iperf3.py b/iperf3/iperf3.py index c47dd67..f46446d 100755 --- a/iperf3/iperf3.py +++ b/iperf3/iperf3.py @@ -23,6 +23,7 @@ class provides common settings for the :class:`Client` and :class:`Server` import select import json import threading +import re from socket import SOCK_DGRAM, SOCK_STREAM try: @@ -30,10 +31,10 @@ class provides common settings for the :class:`Client` and :class:`Server` except ImportError: from Queue import Queue # Python2 compatibility - __version__ = '0.1.11' + MAX_UDP_BULKSIZE = (65535 - 8 - 20) @@ -80,6 +81,9 @@ def output_to_screen(stdout_fd, stderr_fd): os.dup2(stdout_fd, 1) # os.dup2(stderr_fd, 2) +def get_iperf_version(version_string): + match = re.match(r"iperf ([0-9])\.([0-9]).([0-9])", version_string) + return (int(match[1]), int(match[2]), int(match[3])) if match else (2, 0, 0) class IPerf3(object): """The base class used by both the iperf3 :class:`Server` and :class:`Client` @@ -191,6 +195,24 @@ def __init__(self, # Only available from iperf v3.1 and onwards self.lib.iperf_get_test_json_output_string.restype = c_char_p self.lib.iperf_get_test_json_output_string.argtypes = (c_void_p,) + self.lib.iperf_set_test_get_server_output.argtypes = (c_void_p, c_int,) + self.lib.iperf_get_test_get_server_output.restype = c_char_p + self.lib.iperf_get_test_get_server_output.argtypes = (c_void_p,) + except AttributeError: + pass + + try: + # Only available from iperf v3.8 and onwards + self.lib.iperf_set_test_client_username.restype = None + self.lib.iperf_set_test_client_username.argtypes = (c_void_p, c_char_p) + self.lib.iperf_set_test_client_password.restype = None + self.lib.iperf_set_test_client_password.argtypes = (c_void_p, c_char_p) + self.lib.iperf_set_test_client_rsa_pubkey.restype = None + self.lib.iperf_set_test_client_rsa_pubkey.argtypes = (c_void_p, c_char_p) + self.lib.iperf_set_test_server_authorized_users.restype = None + self.lib.iperf_set_test_server_authorized_users.argtypes = (c_void_p, c_char_p) + self.lib.iperf_set_test_server_rsa_privkey.restype = None + self.lib.iperf_set_test_server_rsa_privkey.argtypes = (c_void_p, c_char_p) except AttributeError: pass @@ -328,6 +350,7 @@ def json_output(self): @json_output.setter def json_output(self, enabled): if enabled: + print("enabling") self.lib.iperf_set_test_json_output(self._test, 1) else: self.lib.iperf_set_test_json_output(self._test, 0) @@ -427,6 +450,10 @@ def __init__(self, *args, **kwargs): self._duration = None self._bandwidth = None self._protocol = None + self._username = None + self._password = None + self._rsa_pubkey = None + self._server_output = False @property def server_hostname(self): @@ -606,6 +633,59 @@ def reverse(self, enabled): self._reverse = enabled + @property + def username(self): + """Username for authenticated iperf.""" + return self._username + + @username.setter + def username(self, username): + print("calling it") + self.lib.iperf_set_test_client_username( + self._test, + c_char_p(username.encode('utf-8')) + ) + self._username = username + + @property + def password(self): + """Password for authenticated iperf.""" + return self._password + + @password.setter + def password(self, password): + print("calling it2") + self.lib.iperf_set_test_client_password( + self._test, + c_char_p(password.encode('utf-8')) + ) + self._password = password + + @property + def rsa_pubkey(self): + """RSA pubkey to use for encrypting authentication information.""" + return self._rsa_pubkey + + @rsa_pubkey.setter + def rsa_pubkey(self, rsa_pubkey): + self.lib.iperf_set_test_client_rsa_pubkey( + self._test, + c_char_p(rsa_pubkey.encode('ascii')) + ) + self._rsa_pubkey = rsa_pubkey + + + @property + def server_output(self): + """Server output.""" + self._server_output = True if self.lib.iperf_get_test_get_server_output(self._test) else False + return self._server_output + + @server_output.setter + def server_output(self, server_output): + self.lib.iperf_set_test_get_server_output(self._test, c_int(1 if server_output else 0)) + self._server_output = server_output + def run(self): """Run the current test client. @@ -615,19 +695,23 @@ def run(self): output_to_pipe(self._pipe_in) # Disable stdout error = self.lib.iperf_run_client(self._test) - if not self.iperf_version.startswith('iperf 3.1'): - data = read_pipe(self._pipe_out) - if data.startswith('Control connection'): - data = '{' + data.split('{', 1)[1] - else: + # Only some versions >= 3.1 support json_output_string + # Also some version of iperf have a bug where the json_output_string + # is empty. In those cases try the old way if data is still empty. + (major, minor, mini) = get_iperf_version(self.iperf_version) + if major >= 3 and minor >= 1: data = c_char_p( self.lib.iperf_get_test_json_output_string(self._test) ).value if data: data = data.decode('utf-8') - output_to_screen(self._stdout_fd, self._stderr_fd) # enable stdout + if not data: + data = read_pipe(self._pipe_out) + if data.startswith('Control connection'): + data = '{' + data.split('{', 1)[1] + output_to_screen(self._stdout_fd, self._stderr_fd) # enable stdout if not data or error: data = '{"error": "%s"}' % self._error_to_string(self._errno) @@ -656,20 +740,58 @@ class Server(IPerf3): def __init__(self, *args, **kwargs): """Initialise the iperf3 server instance""" super(Server, self).__init__(role='s', *args, **kwargs) + self._authorized_users = None + self._rsa_privkey = None + + @property + def authorized_users(self): + return self._authorized_users + + @authorized_users.setter + def authorized_users(self, authorized_users): + ''' + authorized_users: path to file containing the authorized_users in the format described. + ''' + self.lib.iperf_set_test_server_authorized_users( + self._test, + c_char_p(authorized_users.encode('utf-8')) + ) + self._authorized_users = authorized_users + + @property + def rsa_privkey(self): + return self._rsa_privkey + + @rsa_privkey.setter + def rsa_privkey(self, rsa_privkey): + ''' + rsa_privkey: Base64 encoded string of the _unencrypted_ private key. + ''' + self.lib.iperf_set_test_server_rsa_privkey( + self._test, + c_char_p(rsa_privkey.encode('ascii')) + ) + self._rsa_privkey = rsa_privkey def run(self): """Run the iperf3 server instance. :rtype: instance of :class:`TestResult` """ + def append(s): + with open("test.txt", "a") as myfile: + myfile.write(s + "\n") def _run_in_thread(self, data_queue): """Runs the iperf_run_server :param data_queue: thread-safe queue """ + append("xx") output_to_pipe(self._pipe_in) # disable stdout + append("yy %d" % self._port) error = self.lib.iperf_run_server(self._test) + append("zz") output_to_screen(self._stdout_fd, self._stderr_fd) # enable stdout # TODO json_output_string not available on earlier iperf3 builds @@ -686,6 +808,7 @@ def _run_in_thread(self, data_queue): self.lib.iperf_reset_test(self._test) data_queue.put(data) + if self.json_output: data_queue = Queue() @@ -782,7 +905,13 @@ def __init__(self, result): """ # The full result data self.text = result - self.json = json.loads(result) + + # When using authentication iperf3 does a funny things + # and prints out the following before the JSON response: + # Authentication successed for user 'test' ts 1598806741 + # So we just skip past it to the first '{' + authentication_stripped_result = result[result.find('{'):] + self.json = json.loads(authentication_stripped_result) if 'error' in self.json: self.error = self.json['error'] @@ -821,6 +950,9 @@ def __init__(self, result): self.remote_cpu_user = cpu_utilization_perc['remote_user'] self.remote_cpu_system = cpu_utilization_perc['remote_system'] + # FIX check for client + self.server_output_text = self.json['server_output_text'] if 'server_output_text' in self.json else '' + # TCP specific test results if self.protocol == 'TCP': sent_json = self.json['end']['sum_sent'] diff --git a/tests/authorized_users.txt b/tests/authorized_users.txt new file mode 100644 index 0000000..e90c101 --- /dev/null +++ b/tests/authorized_users.txt @@ -0,0 +1 @@ +test,0a40291ff1b65f6ddd11f8979ee64981f43b6dec2d804bdb4bdc26d5889b4454 diff --git a/tests/generate_auth_test_files.sh b/tests/generate_auth_test_files.sh new file mode 100755 index 0000000..0e51055 --- /dev/null +++ b/tests/generate_auth_test_files.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Generate the private key, public key, and authorized users files we need +# to be able to run a test. + +# This presumes you have openssl available. +# Also note this slightly deviates from the iperf3 man page as of August 2020 when +# they suggest using sha256sum. This isn't by default available on MacOS +# So I just use openssl for it, too. + +# You can test this works by running the following commands. +# +# server: +# iperf3 -s -p 2310 --authorized-users-path authorized_users.txt --rsa-private-key-path private_not_protected.pem +# +# client: +# IPERF3_PASSWORD=test iperf3 -c 127.0.0.1 -p 2310 --username=test --rsa-public-key-path public.pem +# +# This should output a normal test run. + +# Generate the server side unecrypted private key that the server will use. +openssl genrsa -out private_not_protected.pem 2048 + +# Generate the public key that the client will use send stuff privately to the server. +openssl rsa -in private_not_protected.pem -outform PEM -pubout -out public.pem + +# Create the "authorized_users.txt" file +# Format is +# username,sha256hash("{username},password") +USER="test" +PASSWD="test" +SHA256_USER_PASSWD=`echo -n {$USER}$PASSWD | openssl dgst -sha256` +echo "$USER,$SHA256_USER_PASSWD" > authorized_users.txt diff --git a/tests/private_not_protected.pem b/tests/private_not_protected.pem new file mode 100644 index 0000000..f3161ea --- /dev/null +++ b/tests/private_not_protected.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAsSsC4nLqgVEaWTu2t8mvR36InaRAlJymV5S1HlSq6WHGJnWw +rVEIsGbkbWcVwr6KWZybpskmkhg2nLCrSAG8TaObebyCtRslS0ZxGzn+zRFG5WQg +s6c6yHm+gSS2ZQSri1IerNym8RdgoV3Gv8LoAFIM1R1a0T3agUEwQmIjwqnay0lk +pAabAcsRoxhvddvvZhHUFzyKLZonblHA+NNB+I9ySQTSh6sbgBwLBpA3rVl1Qght +jlKAHiQ/r9bJbHjqWNRL6Bx47/g33Rp5mQcJryyXVgDudGaIxWeo7J5Fz95u9YbJ +IKlGywddWWmEC3qWatQ8MCkGzKorXdu+AtPhkQIDAQABAoIBAD96EqGB3OWSYmdg +TroQx/1Mp7QbmovkiHDOtgn0+ft+bZlwB2pBo2RYqTVNfu10iWtMAjTC00/63u5L +GkNFY+tJWDZIxWgQHs+WoIXJxLJCo20i79iObTBQ9mGc/sC46KGVPapfsGynHJcU +W1ZvheI/g5uD1fssNzKiYaEXOorO6U0zp+WfNwCSgrz759WfLv/1tPIflfAkZz8Y +o46vaE4r/Oglx8VVb8yK8/iNlb/qRmqEswICn7EESHcd4v9N1sfrC/mgMx4Oehd3 +trQd1czAg+2Fdeg8udeFr8TN7t3mzRdO87DWC7hxuuIzLJ7Kw1w5TkfDbL2FuWtP +/Azu5QUCgYEA6/SxZUvq0Wo7yPKTpM/labIi0jBjeklzPCDZYJ6IeQt6g0OMFm9E +NeP9VkWdC5vOToe+UYLWgwbvAZF34DqYd0Cz+l81S3Hx5yr1AUGMraEwFL2ddDSE +4RsfQjyU/2JPibYfh9C5JQVMKllL39Hy6yYTYOCCae+SXdCunlDOdi8CgYEAwDfd +nonYHsOfKle46D4d+3gopk0miLnQbQk+ejLm6y+mGURORUpPnmgCT39PqkI7yfND +q9923iR/kfcCVk3l1Z9GUIvVeZalk4oPbGqSujkzPmwFuQ0DxI3MxniW93lvPu4M +hmzSSTCzdHWcVGiMR3Iv+svuTTjSK3Qmmaag9D8CgYEA4YLf+Nxx9Ov/b7LIAYDg +1f/238R3juycQh9zIrDh5i5eTQ5F2TtGier4uXeW/P1QVCXQc78Enf4GeA078QaP +qYoheW19Vjn05OddenHXriqb9Xa0AdggF7UATyHvlZW0Rv3VbMzD5uoFPYRSy/uw +m6WsZ66rdewOi7Ni8NRyPukCgYEAv3Jk/1SXtvxr+QZqGpZT0nT5N7m72Y4Is7Dz +U06DhFdu/dd0hbrf1gwN0RQiSpexaREro3HsuJBtpvn4j/ECK2R56ht8QZ/zxiFJ +/1eeUHBMQ63ffVIjII+DF2JgKpf+XxVF0FVulIs3EZVaYQ/IOBjZBNg8vxkK61DC +aWL3CI0CgYEA4/pRb37I0MwijBRZl4SY6hDQDidAoL3Owkp1ZuXOn3rSnNz/+WGw +ukoktnNmb/RA88Ywcz25fazg1r3RoQj/5BayJv7eUFgSk65QYKT/kgiJf47GK1Q7 +It2SH9o6bHQhnc2Xvmx95GYZ7sDAviv7xArSGjA+y7n0NanyeMqQ5WI= +-----END RSA PRIVATE KEY----- diff --git a/tests/public.pem b/tests/public.pem new file mode 100644 index 0000000..811822c --- /dev/null +++ b/tests/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsSsC4nLqgVEaWTu2t8mv +R36InaRAlJymV5S1HlSq6WHGJnWwrVEIsGbkbWcVwr6KWZybpskmkhg2nLCrSAG8 +TaObebyCtRslS0ZxGzn+zRFG5WQgs6c6yHm+gSS2ZQSri1IerNym8RdgoV3Gv8Lo +AFIM1R1a0T3agUEwQmIjwqnay0lkpAabAcsRoxhvddvvZhHUFzyKLZonblHA+NNB ++I9ySQTSh6sbgBwLBpA3rVl1QghtjlKAHiQ/r9bJbHjqWNRL6Bx47/g33Rp5mQcJ +ryyXVgDudGaIxWeo7J5Fz95u9YbJIKlGywddWWmEC3qWatQ8MCkGzKorXdu+AtPh +kQIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/test_iperf3.py b/tests/test_iperf3.py index 6d8e286..9b5cff7 100644 --- a/tests/test_iperf3.py +++ b/tests/test_iperf3.py @@ -2,11 +2,37 @@ import iperf3 import pytest import subprocess +import base64 +import re from time import sleep def isclose(a, b, rel_tol=1e-09, abs_tol=0.0): return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) +# Return paths files local to this module. +# Given file, assumed to be in directory as this module, transform it into the full path to find it. +def get_test_data_file_path(file): + dirname = os.path.dirname(os.path.abspath(__file__)) + x= os.path.join(dirname, file) + print(x) + return x + +# Return the contents of a PEM file Base 64 encoded +# And yes, if you're wondering the "key" part of the PEM +# file is already Base64 encoded. Turns out the iperf_api is funky +# and assumes the file to be "double" encoded. +def get_key_as_base64(key_file): + with open(get_test_data_file_path(key_file), 'rb') as file: + raw_key = file.read() + return base64.b64encode(raw_key).decode("ascii") + +# Return (major, minor, mini) tuple of version from version string +# like iperf 3.2.1. +# Return 3.0.0 if the version string is poorly formed. +def get_iperf_version(version_string): + match = re.match(r"iperf ([0-9])\.([0-9]).([0-9])", version_string) + return (int(match[1]), int(match[2]), int(match[3])) if match else (3, 0, 0) + class TestPyPerf: def test_init_client(self): @@ -18,7 +44,7 @@ def test_init_server(self): assert server._test def test_lib_name(self): - client = iperf3.Client(lib_name='libiperf.so.0') + client = iperf3.Client() assert client._test def test_run_not_implemented(self): @@ -233,10 +259,10 @@ def test_server_failed_run(self): server = iperf3.Server() server.bind_address = '127.0.0.1' server.port = 5201 - + print("here") server2 = subprocess.Popen(["iperf3", "-s"]) sleep(.3) # give the server some time to start - + print("server started") response = server.run() server2.kill() @@ -256,7 +282,8 @@ def test_server_run(self): ) response = server.run() - if server.iperf_version.startswith('iperf 3.0') or server.iperf_version.startswith('iperf 3.1'): + (major, minor, mini) = get_iperf_version(server.iperf_version) + if major >= 3 and minor >= 0: assert not response.error assert response.local_host == '127.0.0.1' assert response.local_port == 5205 @@ -264,6 +291,7 @@ def test_server_run(self): else: assert response.error == 'the client has unexpectedly closed the connection' + def test_server_run_output_to_screen(self): server = iperf3.Server() server.bind_address = '127.0.0.1' @@ -332,3 +360,71 @@ def test_result(self): assert isclose(result.received_kB_s, 114046.387, rel_tol=0.01) assert isclose(result.received_MB_s, 111.373, rel_tol=0.01) + def test_client_successful_run_with_auth(self): + client = iperf3.Client() + client.server_hostname = '127.0.0.1' + client.port = 5209 + client.duration = 1 + client.username = "test" + client.password = "test" + client.rsa_pubkey = get_key_as_base64("public.pem") + + server = subprocess.Popen(["iperf3", "-s", "-p", "5209", "--authorized-users-path", get_test_data_file_path("authorized_users.txt"), + "--rsa-private-key-path", get_test_data_file_path("private_not_protected.pem")]) + sleep(.3) # give the server some time to start + response = client.run() + server.kill() + + print(response) + assert response.remote_host == '127.0.0.1' + assert response.remote_port == 5209 + + # These are added to check some of the TestResult variables + assert not response.reverse + assert response.type == 'client' + assert response.__repr__() + + def test_client_failed_run_with_auth(self): + client = iperf3.Client() + client.server_hostname = '127.0.0.1' + client.port = 5210 + client.duration = 1 + client.username = "test" + client.password = "wrongpassword" + client.rsa_pubkey = get_key_as_base64("public.pem") + + server = subprocess.Popen(["iperf3", "-s", "-p", "5210", "--authorized-users-path", get_test_data_file_path("authorized_users.txt"), + "--rsa-private-key-path", get_test_data_file_path("private_not_protected.pem")]) + sleep(.3) # give the server some time to start + response = client.run() + server.kill() + + assert "test authorization failed" in response.error + + def test_server_run_with_auth(self): + server = iperf3.Server() + server.bind_address = '127.0.0.1' + server.port = 5211 + # Use authorized_users.txt, public.pem, private.pem and + # username/password of test/test for client / server auth + server.authorized_users = get_test_data_file_path("authorized_users.txt") + server.rsa_privkey = get_key_as_base64("private_not_protected.pem") + + # Launching the client with a sleep timer to give our server some time to start + client = subprocess.Popen( + 'sleep .3 && iperf3 -c 127.0.0.1 -p 5211 -t 1 --rsa-public-key-path ' + get_test_data_file_path("public.pem") + ' --username test', + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env={'IPERF3_PASSWORD': 'test'} + ) + response = server.run() + + (major, minor, mini) = get_iperf_version(server.iperf_version) + if major >= 3 and minor >= 8: + assert not response.error + assert response.local_host == '127.0.0.1' + assert response.local_port == 5211 + assert response.type == 'server' + else: + assert response.error == 'the client has unexpectedly closed the connection' From a9193ac014425ebe22b5d6cb2f42fae259f0382d Mon Sep 17 00:00:00 2001 From: Scott Silver Date: Tue, 1 Sep 2020 18:00:16 -0700 Subject: [PATCH 2/6] Missed some changes. --- iperf3/iperf3.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/iperf3/iperf3.py b/iperf3/iperf3.py index f46446d..38f0865 100755 --- a/iperf3/iperf3.py +++ b/iperf3/iperf3.py @@ -778,20 +778,13 @@ def run(self): :rtype: instance of :class:`TestResult` """ - def append(s): - with open("test.txt", "a") as myfile: - myfile.write(s + "\n") - def _run_in_thread(self, data_queue): """Runs the iperf_run_server :param data_queue: thread-safe queue """ - append("xx") output_to_pipe(self._pipe_in) # disable stdout - append("yy %d" % self._port) error = self.lib.iperf_run_server(self._test) - append("zz") output_to_screen(self._stdout_fd, self._stderr_fd) # enable stdout # TODO json_output_string not available on earlier iperf3 builds @@ -808,7 +801,6 @@ def _run_in_thread(self, data_queue): self.lib.iperf_reset_test(self._test) data_queue.put(data) - if self.json_output: data_queue = Queue() @@ -906,7 +898,7 @@ def __init__(self, result): # The full result data self.text = result - # When using authentication iperf3 does a funny things + # When using authentication iperf3 does a funny thing # and prints out the following before the JSON response: # Authentication successed for user 'test' ts 1598806741 # So we just skip past it to the first '{' From d043fb747785fee347d8cbf48cd54e62b808e6d7 Mon Sep 17 00:00:00 2001 From: Scott Silver Date: Wed, 2 Sep 2020 16:05:19 -0700 Subject: [PATCH 3/6] Remove one errant printf. --- iperf3/iperf3.py | 1 - 1 file changed, 1 deletion(-) diff --git a/iperf3/iperf3.py b/iperf3/iperf3.py index 38f0865..a075f0d 100755 --- a/iperf3/iperf3.py +++ b/iperf3/iperf3.py @@ -350,7 +350,6 @@ def json_output(self): @json_output.setter def json_output(self, enabled): if enabled: - print("enabling") self.lib.iperf_set_test_json_output(self._test, 1) else: self.lib.iperf_set_test_json_output(self._test, 0) From b2409e01b63aca3dc27cbf5129c1a310e3273563 Mon Sep 17 00:00:00 2001 From: Scott Silver Date: Fri, 4 Sep 2020 15:26:00 -0700 Subject: [PATCH 4/6] Break out auth generation so I could use it to add my own users easily. --- iperf3/iperf3.py | 2 -- tests/add_user.sh | 12 ++++++++++++ tests/generate_auth_test_files.sh | 10 ++++++---- 3 files changed, 18 insertions(+), 6 deletions(-) create mode 100755 tests/add_user.sh diff --git a/iperf3/iperf3.py b/iperf3/iperf3.py index a075f0d..6f5a4ba 100755 --- a/iperf3/iperf3.py +++ b/iperf3/iperf3.py @@ -639,7 +639,6 @@ def username(self): @username.setter def username(self, username): - print("calling it") self.lib.iperf_set_test_client_username( self._test, c_char_p(username.encode('utf-8')) @@ -653,7 +652,6 @@ def password(self): @password.setter def password(self, password): - print("calling it2") self.lib.iperf_set_test_client_password( self._test, c_char_p(password.encode('utf-8')) diff --git a/tests/add_user.sh b/tests/add_user.sh new file mode 100755 index 0000000..5fe2e79 --- /dev/null +++ b/tests/add_user.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Command line positional parameters +USER=$1 +PASSWD=$2 +AUTHORIZED_USERS_FILE=$3 + +# Create the "authorized_users.txt" file +# Format is +# username,sha256hash("{username},password") +SHA256_USER_PASSWD=`echo -n {$USER}$PASSWD | openssl dgst -sha256 -binary | xxd -p -c 256` +echo "$USER,$SHA256_USER_PASSWD" >> $AUTHORIZED_USERS_FILE diff --git a/tests/generate_auth_test_files.sh b/tests/generate_auth_test_files.sh index 0e51055..4ef4ed0 100755 --- a/tests/generate_auth_test_files.sh +++ b/tests/generate_auth_test_files.sh @@ -27,7 +27,9 @@ openssl rsa -in private_not_protected.pem -outform PEM -pubout -out public.pem # Create the "authorized_users.txt" file # Format is # username,sha256hash("{username},password") -USER="test" -PASSWD="test" -SHA256_USER_PASSWD=`echo -n {$USER}$PASSWD | openssl dgst -sha256` -echo "$USER,$SHA256_USER_PASSWD" > authorized_users.txt +AUTHORIZED_USERS_FILE="authorized_users.txt" + +# Create user with test, test username, password. +rm $AUTHORIZED_USERS_FILE +./add_user.sh test test $AUTHORIZED_USERS_FILE + From 26ec2759daded4d72bbc9184e5afe85b9a06c085 Mon Sep 17 00:00:00 2001 From: Scott Silver Date: Sun, 6 Sep 2020 19:12:52 -0700 Subject: [PATCH 5/6] Fix bug where data was not initialized for certain versions of iperf. --- iperf3/iperf3.py | 1 + 1 file changed, 1 insertion(+) diff --git a/iperf3/iperf3.py b/iperf3/iperf3.py index 6f5a4ba..b625ff9 100755 --- a/iperf3/iperf3.py +++ b/iperf3/iperf3.py @@ -692,6 +692,7 @@ def run(self): output_to_pipe(self._pipe_in) # Disable stdout error = self.lib.iperf_run_client(self._test) + data = None # Only some versions >= 3.1 support json_output_string # Also some version of iperf have a bug where the json_output_string # is empty. In those cases try the old way if data is still empty. From 6f3b8a753d16beefe91bbf59d14c09e0f3e8a55e Mon Sep 17 00:00:00 2001 From: Scott Silver Date: Tue, 5 Jan 2021 08:11:03 -0800 Subject: [PATCH 6/6] Fixed version check and some tests --- iperf3/iperf3.py | 18 ++++++++++-- tests/authorized_users.txt | 1 + tests/private_not_protected.pem | 50 ++++++++++++++++----------------- tests/public.pem | 14 ++++----- 4 files changed, 49 insertions(+), 34 deletions(-) diff --git a/iperf3/iperf3.py b/iperf3/iperf3.py index b625ff9..c05c87d 100755 --- a/iperf3/iperf3.py +++ b/iperf3/iperf3.py @@ -82,8 +82,22 @@ def output_to_screen(stdout_fd, stderr_fd): # os.dup2(stderr_fd, 2) def get_iperf_version(version_string): - match = re.match(r"iperf ([0-9])\.([0-9]).([0-9])", version_string) - return (int(match[1]), int(match[2]), int(match[3])) if match else (2, 0, 0) + match = re.match(r"iperf ([0-9])\.([0-9]).?([0-9])?", version_string) + major = 3 + minor = 0 + mini = 0 + + # groups includes the whole string, too. + matchCount = len(match.groups()) - 1 + + if matchCount > 1: + major = int(match[1]) + minor = int(match[2]) + + if matchCount > 2: + mini = int(match[3]) + + return (major, minor, mini) class IPerf3(object): """The base class used by both the iperf3 :class:`Server` and :class:`Client` diff --git a/tests/authorized_users.txt b/tests/authorized_users.txt index e90c101..554166d 100644 --- a/tests/authorized_users.txt +++ b/tests/authorized_users.txt @@ -1 +1,2 @@ test,0a40291ff1b65f6ddd11f8979ee64981f43b6dec2d804bdb4bdc26d5889b4454 +ssilver,963b919de8133fb3605f464c15c98fa755450ccfdcf53fc45f5044ab71efe91a diff --git a/tests/private_not_protected.pem b/tests/private_not_protected.pem index f3161ea..a8135c0 100644 --- a/tests/private_not_protected.pem +++ b/tests/private_not_protected.pem @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY----- diff --git a/tests/public.pem b/tests/public.pem index 811822c..789085b 100644 --- a/tests/public.pem +++ b/tests/public.pem @@ -1,9 +1,9 @@ -----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsSsC4nLqgVEaWTu2t8mv -R36InaRAlJymV5S1HlSq6WHGJnWwrVEIsGbkbWcVwr6KWZybpskmkhg2nLCrSAG8 -TaObebyCtRslS0ZxGzn+zRFG5WQgs6c6yHm+gSS2ZQSri1IerNym8RdgoV3Gv8Lo -AFIM1R1a0T3agUEwQmIjwqnay0lkpAabAcsRoxhvddvvZhHUFzyKLZonblHA+NNB -+I9ySQTSh6sbgBwLBpA3rVl1QghtjlKAHiQ/r9bJbHjqWNRL6Bx47/g33Rp5mQcJ -ryyXVgDudGaIxWeo7J5Fz95u9YbJIKlGywddWWmEC3qWatQ8MCkGzKorXdu+AtPh -kQIDAQAB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvP6HkZ2br5TiLGRpCZ5a +5/ayG1dXJhdn/ErqaklveqBRuTdldh3d8CN5YSEH5LGfzcoz4ZI4h4kS3hCCJ177 +X1QVrI3wOEPluNsBREwD2IfQc8w5LxcIPICg2xsj5kjX7WyYZVame7LIqsb1688d +wvo0Lm8c4t8eOWSt848SFG2AUJmhkAg9pmP4KaH+OjZNcNUEgQKx6PrJdusr++xk +nya3806jUOlfmExnG+QVAKfx0cChMYvXsHq0jIsjluqzIB/mLZV70Bj5BL/wyF55 +IIlxE1hJr8ZHCNGvVegnHK82UuWjX2RMWtephnddyW/5yELRgXvglIPYomzx3U+h +KQIDAQAB -----END PUBLIC KEY-----