diff --git a/modules/responses-tcp.sh b/modules/responses-tcp.sh new file mode 100755 index 0000000000000000000000000000000000000000..109a9a673e35db2d43384931bd6d353973e9c07e --- /dev/null +++ b/modules/responses-tcp.sh @@ -0,0 +1,260 @@ +#!/usr/bin/env bash + +#set -x + +PKT_OVERHEAD=42 +IPTABLES="iptables" + +# Discover network setup +function net_discover() +{ + UNIQ="guniq" + if [ `uname` = "Linux" ]; then + UNIQ="uniq" + fi + + ip4=""; ip6=""; mac="" + addr=""; link="" + if [ "`ssh $1 uname`" = "Linux" ]; then + addr="$(ssh $1 ip -d -o addr|grep ": ${2}"|grep -o -E "(inet|inet6) ([^ ]+)"|grep -v Link|$UNIQ -w 5)" + link="$(ssh $1 ip -d -o link|grep ": ${2}"|grep -o -E "(ether) ([^ ]+)"|$UNIQ -w 5)" + else + addr="$(ssh $1 ifconfig ${2}|grep -o -E "inet(6)? ([^ ]+)"|$UNIQ|grep -v -E "(fe80)|%")" + link="$(ssh $1 ifconfig ${2}|grep -o -E "ether ([^ ]+)"|$UNIQ -w 5)" + fi + + while read -r l; do + l=( $(echo $l) ) + case ${l[0]} in + inet) ip4=( ${net_v4[@]} $(echo ${l[1]}|cut -d\/ -f1) ) ;; + inet6) ip6=( ${net_v6[@]} $(echo ${l[1]}|cut -d\/ -f1) ) ;; + esac + done <<< "$addr" + while read -r l; do + l=( $(echo $l) ) + case ${l[0]} in + ether) mac=( ${net_mac[@]} ${l[1]} ) ;; + esac + done <<< "$link" + + # Decide used address + output=() + + if [[ ${IP_VERSION} -eq 4 ]]; then + output=("${mac}" "${ip4}") + else + output=("${mac}" "${ip6}") + fi + + echo ${output[@]} +} + +# Rewrite pcap file according to current network settings +function trace_rewrite() +{ + player=( $(echo ${players[$2]}|tr ':' ' ') ) + net_dst=( $(net_discover ${target} ${iface}) ) + net_src=( $(net_discover ${player[0]} ${player[1]}) ) + + tcprewrite --dlt=enet -i ${1} -o ${1}.live.${2} &>>${LOG} + tcpprep --auto=client --cachefile=tmp.cache --pcap=${1}.live.${2} &>>${LOG} + + if [ -z "${net_dst[1]}" ]; then + echo "FATAL ERROR: net_dst[1] is empty. Is the dst interface UP?" + return + fi + + if [ -z "${net_src[1]}" ]; then + echo "FATAL ERROR: net_src[1] is empty. Is the src interface UP?" + return + fi + + #Store TCPGen configuration at player + cat <<- CONFIGURATION | ssh -T ${player[0]} "cat > /tmp/tcpgen.conf" + source-mac ${net_src[0]} + destination-mac ${net_dst[0]} + + ipv${IP_VERSION}-source-network 10.10.0.0 + ipv${IP_VERSION}-source-netmask 255.255.0.0 + ipv${IP_VERSION}-destination-ip ${net_dst[1]} + + tcp-destination-port ${PORT} + #ip-ipv6-probability 0.5 + CONFIGURATION + + # Copy rewrited file + scp ${BASEDIR}/${1}.live.${2} ${player[0]}:/tmp/ &>> ${LOG} + + #TODO Test on BSD systems + if [ `uname` = "Linux" ]; then + sudo ip -${IP_VERSION} neigh replace "${net_src[1]}" lladdr "${net_src[0]}" dev "${iface}" + fi + + RP="arp" + if [ $IP_VERSION -eq "6" ]; then + RP="ndp" + fi + sudo $RP -s ${net_src[1]} ${net_src[0]} +} + +function stats() { + RX_bytes=`grep -E "RX bytes" tcpgen.out | awk '{print $3}' | paste -sd+ - | bc` + RX_responses=`grep -E "RX responses" tcpgen.out | awk '{print $3}' | paste -sd+ - | bc` + TX_bytes=`grep -E "TX bytes" tcpgen.out | awk '{print $3}' | paste -sd+ - | bc` + TX_queries=`grep -E "TX queries" tcpgen.out | awk '{print $3}' | paste -sd+ - | bc` + + echo ${TX_bytes} ${TX_queries} ${RX_bytes} ${RX_responses} +} + +function load_dpdk_driver() +{ + [ $# -lt 2 ] && echo "Not enough arguments!" + info=`ssh $1 "dpdk-devbind --status-dev net | grep if=$2"` + + original_driver=`echo $info | sed -e "s/^.*drv=//" -e "s/ .*$//"` + bus=`echo $info | awk '{print $1}' | cut -c 6-` + ssh $1 "sudo ip link set $2 down" + ssh $1 "sudo dpdk-devbind -b igb_uio $bus" + echo $bus $original_driver +} + +function unload_dpdk_driver() +{ + ssh $1 "sudo dpdk-devbind -b $3 $2" +} + +# Replay pcap file to nameserver and evaluate number of answered responses +function trace_replay() +{ + # Prepare tcpreplay flags + speed="-g 0" + [ "${1}" -gt 0 ] && speed="-g $(( 1000000 * ${#players[@]} / ${1} ))" + duration="-r $(( ${replay_duration:-30} * 1000000 ))" + rflags="-p 1 ${speed} ${duration}" + # Replay the trace files + jobs=() + i=0; while [ ${i} -lt ${#players[@]} ]; do + player=( $(echo ${players[$i]}|tr ':' ' ') ) + ret=( $(load_dpdk_driver ${player[0]} ${player[1]}) ) + + cmd="sudo ~/dpdk-tcp-generator/build/tcpgen -c 1 -- ${rflags} -c /tmp/tcpgen.conf --pcap /tmp/${2}.live.${i}" + (ssh "${player[0]}" "${cmd}" 2>&1 | grep -ve 'EAL' >> tcpgen.out) & + + jobs=( ${jobs[@]} $! ) + bus=( ${bus[@]} ${ret[0]} ) + drivers=( ${drivers[@]} ${ret[1]} ) + (( i += 1 )) + done + i=0; while [ ${i} -lt ${#players[@]} ]; do + wait ${jobs[$i]} + player=( $(echo ${players[$i]}|tr ':' ' ') ) + unload_dpdk_driver $player ${bus[$i]} ${drivers[$i]} + (( i += 1 )) + done + + # Wait for delayed packets + sleep ${replay_duration:-30} + + statistics=( $(stats) ) + + query_size="${statistics[0]}" + query_pkts="${statistics[1]}" + reply_size="${statistics[2]}" + reply_pkts="${statistics[3]}" + + # Calculate results + answered=$(bc <<< "scale=2; ${reply_pkts}*100.0/${query_pkts}") + query_rate=$(bc <<< "scale=0; ${query_pkts}/${replay_duration:-30}") + reply_rate=$(bc <<< "scale=0; ${reply_pkts}/${replay_duration:-30}") + query_avglen=$(bc <<< "scale=0; ${query_size}/${query_pkts}") + reply_avglen=$(bc <<< "scale=0; ${reply_size}/${reply_pkts}") + + echo ${answered} ${query_rate} ${query_avglen} ${reply_rate} ${reply_avglen} +} + +function module_init() +{ + line=`tar --gzip --list --verbose --file=${DATADIR}/${2}.tgz | grep .pcap` + if [[ `uname` == "Linux" ]]; then + TRACE=`echo $line | awk '{print $6}'` + else + TRACE=`echo $line | awk '{print $9}'` + fi + #Get number of IPv from pcap file + ip=${TRACE:11:1} + IP_VERSION=$ip + + # Check for IPv6 measurement + if [ ${ip} ] && [ ${ip} = "6" ]; then + PKT_OVERHEAD=62 + IPTABLES="ip6tables" + fi + + if [ ! -f ${TRACE} ]; then + echo "'${TRACE}' not found, skipping" + return 1 + fi + + # Populate address list + addr=( $(net_discover ${target} ${iface}) ) + ADDRLIST=( 127.0.0.1 ${addr[1]} ) +} + +function module_exec() +{ + output="${BASEDIR}/results/${1}.data" + + # Drop filesystem and page caches + sudo sync + echo 3|sudo tee /proc/sys/vm/drop_caches &>/dev/null + + # Start nameserver + ns_prep ${NAMECONF} + ns_start ${NAMECONF} + if [ $? -gt 0 ]; then + echo "error: failed to start '${1}'" + return 1 + fi + + if [[ `uname` != "Linux" ]]; then + SEQ="gseq" + else + SEQ="seq" + fi + + # Perform measurement of answered queries + do_rates=(0) + if [ "${replay_rates}" ]; then + if [ "${replay_step}" ] && [ ${#replay_rates[@]} -ge 2 ]; then + do_rates=( $(${SEQ} ${replay_rates[0]} ${replay_step} ${replay_rates[1]}) ) + else + do_rates=${replay_rates[@]} + fi + fi + + # Prepare pcap files for replaying + i=0; while [ ${i} -lt ${#players[@]} ]; do + trace_rewrite ${TRACE} ${i} + if [ $? -ne 0 ]; then + echo "error: failed to prepare tracefile for ${players[$i]}" + return 1 + fi + (( i += 1 )) + done + + # Replay the pcap files + for pps in ${do_rates[@]}; do + result=( $(trace_replay ${pps} ${TRACE}) ) + [ ${pps} -eq 0 ] && pps="maximum" + echo "${1} ${result[0]} % answered for ${result[1]} q/s, ${result[2]} B avg (${result[3]} a/s, ${result[4]} B avg)" + echo -e "${result[1]}\t${result[0]}\t${result[2]}" >> ${output}.tmp + done + + # Stop nameserver + ns_stop ${NAMECONF} + + # Save results + echo -e "# $(ns_info)" > ${output} + cat ${output}.tmp|sort -n >> ${output} + rm ${output}.tmp +}