diff --git a/README.md b/README.md index 0937173..5390f74 100644 --- a/README.md +++ b/README.md @@ -242,6 +242,12 @@ Start the record proxy: Just browse. Any traffic to and from the selected names will be recorded. Terminate netplayer with CTRL+C when finished. +#### Incomplete records + +The last HTTP payload of a record may be incomplete. If that happens, the run command will hang and display the faulty record number after a timeout. +Re-run with `-d --record ` and note the missing packet number. +Run `netreplay remove ` to remove the packet. + ### Measure Measure resource cost on a different machine. diff --git a/exp.py b/exp.py index 898d980..0e10a34 100644 --- a/exp.py +++ b/exp.py @@ -15,7 +15,7 @@ CONFIGS = { "server-local", ], "records": [ - { "filename": "wikipedia", "repeat": 1 }, + { "filename": "youtube", "repeat": 1 }, ], "repodir": "/home/tuxmain/reps/tlsbench", "expdir": "/dev/shm/exp", @@ -74,7 +74,7 @@ CONFIGS = { "server", ], "records": [ - { "filename": "wikipedia", "repeat": 500 }, + { "filename": "wikipedia", "repeat": 400 }, ], "repodir": "/home/tuxmain/reps/tlsbench", "expdir": "/dev/shm/exp", @@ -184,8 +184,9 @@ CONFIGS = { "i5": { "experiments": [ "impl-cipher-ver", - #"impl-cert-ver", - #"impl-kex-ver", + "impl-cert-ver", + "impl-kex-ver", + "zrtt", ], "setups": [ "none", @@ -301,11 +302,11 @@ CERT_SIGN_ALGS = [ ] IMPLS = [ "aws_lc", # Amazon's Rust crypto widely used in Rust stuff - #"boring", # Google's fork of OpenSSL used in Chrome and Android - #"graviola", # New crypto in Rust - #"openssl", # widely used - #"ring", # used in most Rust stuff - #"symcrypt", # Microsoft's crypto + "boring", # Google's fork of OpenSSL used in Chrome and Android + "graviola", # New crypto in Rust + "openssl", # widely used + "ring", # used in most Rust stuff + "symcrypt", # Microsoft's crypto #"wolfcrypt" # used in embedded (won't build with rpxy for now) ] # Symmetric ciphers @@ -340,9 +341,9 @@ EXPERIMENTS = { "AES_128_GCM_SHA256", "AES_256_GCM_SHA384", "CHACHA20_POLY1305_SHA256", - #"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256", - #"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384", - #"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", ], "kexes": ["X25519"], "cert": ["prime256v1"], @@ -353,7 +354,7 @@ EXPERIMENTS = { "impls": IMPLS, "ciphers": [ "AES_128_GCM_SHA256", - #"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256", ], "kexes": ["X25519"], "cert": [ @@ -370,7 +371,7 @@ EXPERIMENTS = { "impls": IMPLS, "ciphers": [ "AES_128_GCM_SHA256", - #"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256", ], "kexes": [ "X25519", @@ -385,17 +386,11 @@ EXPERIMENTS = { }, # Compare 0-RTT with no early data "zrtt": { - "impls": [ - "aws_lc", - #"ring" - ], + "impls": IMPLS, "ciphers": [ "AES_128_GCM_SHA256", #"AES_256_GCM_SHA384", #"CHACHA20_POLY1305_SHA256", - #"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256", - #"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384", - #"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", ], "kexes": ["X25519"], "cert": ["prime256v1"], @@ -415,6 +410,9 @@ EXPERIMENTS = { # Some algorithms are not available in all implementations def alg_filter(kex, cert, cipher, impl): + if "MLKEM" in kex and "WITH" in cipher: + # WITH means TLS1.2 + return False if "MLKEM" in kex and impl != "openssl" and impl != "aws_lc" and impl != "graviola": return False if kex == "SECP256R1MLKEM768" and impl == "openssl": @@ -425,6 +423,8 @@ def alg_filter(kex, cert, cipher, impl): return False if kex == "MLKEM768" and impl == "graviola": return False + #if cipher == "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384" and impl == "openssl": + # return False return True DOMAINS = [] @@ -533,6 +533,7 @@ def make_certs(outdir, domains, alg, make_ca): make_cert(outdir+root+".crt", outdir+root+".key", outdir+"ca.crt", outdir+"ca.key", root, roots[root]) # Make a cert for all domains because choosing a certificate as a proxy is a real pain + # It is used by netreplay when recording. Rpxy will use specific certs. make_sk(outdir+"all.key", alg) make_cert(outdir+"all.crt", outdir+"all.key", outdir+"ca.crt", outdir+"ca.key", "wikipedia.org", domains) @@ -688,7 +689,7 @@ def choose_impl(expdir, p, impl): expdir += "/" os.symlink(os.getcwd()+"/rpxy_rustls_"+impl, expdir+str(p)+"_rpxy", False) -def run_netreplay(expdir, repodir, record, p2_addr, p2_port, listen_port, tls_mode, only_record=None, ciphers=None, kexes=None, earlydata="0"): +def run_netreplay(expdir, repodir, record, p2_addr, p2_port, listen_port, tls_mode, only_record=None, ciphers=None, kexes=None, earlydata="0", debug=False): if expdir[-1] != "/": expdir += "/" repodir = repodir.removesuffix("/") @@ -700,6 +701,8 @@ def run_netreplay(expdir, repodir, record, p2_addr, p2_port, listen_port, tls_mo cmd = [repodir+"/netreplay", repodir+"/records/"+record["filename"], "play", p2_addr, str(p2_port), str(listen_port), expdir+"current_certs", tls_mode, "-r", str(record["repeat"]), #"--record", "21" ] + if debug: + cmd.append("-d") if only_record != None: cmd += ["--record", only_record] print(" ".join(cmd)) @@ -754,7 +757,7 @@ def get_rapl_energy(ssh, repodir): energy += int(item) return energy -def run_exp(config, only_record=None, idle=False, shutdown=False): +def run_exp(config, only_record=None, idle=False, shutdown=False, debug=False): ssh = None if "p2_ssh" in config: ssh = connect_ssh(config) @@ -899,6 +902,7 @@ def run_exp(config, only_record=None, idle=False, shutdown=False): ciphers=cipher, kexes=kex, earlydata=earlydata, + debug=debug, ) # TODO detect when netreplay has finished @@ -1032,6 +1036,7 @@ Run options: --record Only play this record --idle Also measure when idle --shutdown Shutdown host and target when finished + --debug Print netreplay's debug """.format( sig_algs = " ".join(CERT_SIGN_ALGS), configs = " ".join([i for i in CONFIGS]), @@ -1092,7 +1097,8 @@ Run options: print("Cannot power off") exit(1) - run_exp(config, only_record=getargv("--record", None), idle="--idle" in sys.argv, shutdown=shutdown) + debug = "--debug" in sys.argv + run_exp(config, only_record=getargv("--record", None), idle="--idle" in sys.argv, shutdown=shutdown, debug=debug) if shutdown: bus_proxy.PowerOff(False) diff --git a/plots.py b/plots.py index 5e7786d..64cd9eb 100644 --- a/plots.py +++ b/plots.py @@ -48,6 +48,8 @@ COL = { "cert": "alg", "kex": "kex", "ed": "ed", + "side": "setup", + "record": "record", } # Physical units by object UNIT = { @@ -142,7 +144,7 @@ replot f.close() os.system("gnuplot {plots_dir}/{object}_by_{criterion}_{side}_{record}.gnuplot".format(plots_dir=PLOTS_DIR, **kwargs)) -def make_log_plot(logs, exp, criterion, side, obj, record, machine=None): +def make_log_plot(logs, exp, criterion, side, obj, record, machine=None, version=None): f = open(f"/dev/shm/plots/{obj}_by_{criterion}_{side}_{record}.dat", "w") ciphers = {} impls = [] @@ -162,6 +164,12 @@ def make_log_plot(logs, exp, criterion, side, obj, record, machine=None): for log in logs: if log["exp"] == exp and log["record"] == record and log["setup"] == side: + #ver = VER_LABEL[log["cipher"]] + #if log[COL[criterion]]+"/"+ver not in ciphers: + # ciphers[log[COL[criterion]]+"/"+ver] = {} + #ciphers[log[COL[criterion]]+"/"+ver][log["impl"]] = float(log[COL[obj]]) - idle_val * float(log["time"]) + if version != None and VER_LABEL[log["cipher"]] != version: + continue if log[COL[criterion]] not in ciphers: ciphers[log[COL[criterion]]] = {} ciphers[log[COL[criterion]]][log["impl"]] = float(log[COL[obj]]) - idle_val * float(log["time"]) @@ -174,14 +182,15 @@ def make_log_plot(logs, exp, criterion, side, obj, record, machine=None): for impl in impls: if impl not in ciphers[cipher]: ciphers[cipher][impl] = 0 - """f.write("{}({}) - {}\n".format( - ALG_LABEL[cipher], - VER_LABEL[log["cipher"]], - " ".join([ - str(ciphers[cipher][impl]) - for impl in impls - ]), - ))""" + #cipher_parts = cipher.split("/") + #f.write("{}({}) - {}\n".format( + # ALG_LABEL[cipher_parts[0]], + # cipher_parts[1], + # " ".join([ + # str(ciphers[cipher][impl]) + # for impl in impls + # ]), + #)) f.write("{} - {}\n".format( ALG_LABEL[cipher], " ".join([ @@ -298,6 +307,48 @@ def make_linear_regression(logs): #plt.show() plt.savefig(f"{PLOTS_DIR}/correlation_energy_cpu.png") +# Measure relative difference between TLS versions +def cmp_versions(logs, exps, criteria, objs): + ciphers = {} + idle_val = None + + for log in logs: + if log["exp"] == "idle": + idle_val = {obj:float(log[COL[obj]]) / float(log["time"]) for obj in objs} + + for log in logs: + if log["exp"] not in exps or log["setup"] == "none": + continue + ver = VER_LABEL[log["cipher"]] + if ver not in ciphers: + ciphers[ver] = {} + key = [] + for criterion in criteria: + key.append(ALG_LABEL.get(log[COL[criterion]], log[COL[criterion]])) + key = "/".join(key) + if key not in ciphers[ver]: + ciphers[ver][key] = log + + diff_rel_max = {obj:0.0 for obj in objs} + diff_rel_sum = {obj:0.0 for obj in objs} + diff_rel_num = {obj:0 for obj in objs} + for key in ciphers["1.2"]: + if key not in ciphers["1.3"]: + continue + log12 = ciphers["1.2"][key] + log13 = ciphers["1.3"][key] + for obj in objs: + val12 = float(log12[COL[obj]]) - idle_val[obj] * float(log12["time"]) + val13 = float(log13[COL[obj]]) - idle_val[obj] * float(log13["time"]) + # Difference relative to the mean of the two values + diff_rel = abs(val12 - val13) / ((val12 + val13) / 2) + diff_rel_max[obj] = max(diff_rel_max[obj], diff_rel) + diff_rel_sum[obj] += diff_rel + diff_rel_num[obj] += 1 + diff_rel_avg = {obj:diff_rel_sum[obj]/diff_rel_num[obj] for obj in objs} + print("Diff rel max: ", diff_rel_max) + print("Diff rel avg: ", diff_rel_avg) + def getargv(arg:str, default="", n:int=1, args:list=sys.argv): if arg in args and len(args) > args.index(arg)+n: return args[args.index(arg)+n] @@ -331,16 +382,17 @@ if __name__ == "__main__": machine = getargv("-m", None) if cmd == "log": + cmp_versions(logs, ["impl-cipher-ver", "impl-cert-ver", "impl-kex-ver"], ["side", "cipher", "cert", "kex", "record"], ["cpu", "energy"]) for side in ["client", "server"]: for record in records: - make_log_plot(logs, "impl-cipher-ver", "cipher", side, "cpu", record, machine=machine) - make_log_plot(logs, "impl-cipher-ver", "cipher", side, "energy", record, machine=machine) - make_log_plot(logs, "impl-cert-ver", "cert", side, "cpu", record, machine=machine) - make_log_plot(logs, "impl-cert-ver", "cert", side, "energy", record, machine=machine) - make_log_plot(logs, "impl-kex-ver", "kex", side, "cpu", record, machine=machine) - make_log_plot(logs, "impl-kex-ver", "kex", side, "energy", record, machine=machine) - make_log_plot(logs, "zrtt", "ed", side, "cpu", record, machine=machine) - make_log_plot(logs, "zrtt", "ed", side, "energy", record, machine=machine) + make_log_plot(logs, "impl-cipher-ver", "cipher", side, "cpu", record, machine=machine, version="1.3") + make_log_plot(logs, "impl-cipher-ver", "cipher", side, "energy", record, machine=machine, version="1.3") + make_log_plot(logs, "impl-cert-ver", "cert", side, "cpu", record, machine=machine, version="1.3") + make_log_plot(logs, "impl-cert-ver", "cert", side, "energy", record, machine=machine, version="1.3") + make_log_plot(logs, "impl-kex-ver", "kex", side, "cpu", record, machine=machine, version="1.3") + make_log_plot(logs, "impl-kex-ver", "kex", side, "energy", record, machine=machine, version="1.3") + make_log_plot(logs, "zrtt", "ed", side, "cpu", record, machine=machine, version="1.3") + make_log_plot(logs, "zrtt", "ed", side, "energy", record, machine=machine, version="1.3") elif cmd == "prof": for side in ["client-local", "server-local"]: for record in records: