Формирование трафика на OSX 10.10 с помощью pfctl и dnctl

Формирование трафика на OSX 10.10 с помощью pfctl и dnctl

Я пытаюсь реализовать шейпинг (регулирование) трафика на Mac OS X 10.10 с помощью pfctlи dnctl.

Я реализовал простой тестовый сервер и клиент с netcat ( nc) и синтетическим случайным файлом полезной нагрузки, чтобы проверить, правильно ли работает регулирование канала dummynet. До сих пор, пытаясь настроить канал dummynet с помощьюграфическое приложение брандмауэра Murusпохоже, некорректно регулирует трафик (передача 64 МБ завершается примерно за 200 мс).

Вот скрипт оболочки OSX bash (требуется brew install coreutilsдля gdate), который является полным примером. Если вы запустите его в одном терминале как:

./throttle-test.sh server

и еще один как

./throttle-test.sh client

Они попытаются передать полезную нагрузку объемом 64 МБ через ваш en0интерфейс (не используя его, lo0поскольку его огромный MTU не аналогичен трафику WAN).

Я также протестировал передачу файла на удаленный ноутбук с Linux, чтобы проверить, обходят ли ограничение IP-адреса источника и назначения, поскольку они оба являются локальными IP-адресами, но даже на удаленной машине в моей локальной сети/Wi-Fi скорость намного выше ограниченного предела.

Мой вопрос заключается в том, какой скрипт будет правильным для настройки pfctlи dnctlограничения этой передачи файлов до заданного предела пропускной способности (например, 8 Мбит/с). Областью регулирования может быть определенный порт TCP.

Обратите внимание, что OS X 10.10 больше не поддерживает , ipfwпоэтому я ищу что-то с использованием pfctlи dnctl.

Вот мой throttle-test.shфайл:

#!/bin/bash
set -o errexit    # always exit on error
set -o errtrace   # trap errors in functions as well
set -o pipefail   # don't ignore exit codes when piping output
set -o posix      # more strict failures in subshells
# set -x          # enable debugging

IFS="$(printf "\n\t")"

setup_payload() {
  local payload_path="$1"
  local size_kbytes="$2"
  mkdir -p $(basename "${payload_path}")

  if [[ -f "${payload_path}" ]]; then
    local on_disk=$(wc -c < "${payload_path}")
  fi
  if [[ ${on_disk} -ne $((${size_kbytes} * 1024)) ]]; then
    echo "creating payload file ${payload_path}"
    dd if=/dev/urandom of="${payload_path}" \
      bs=1024 count="${size_kbytes}" &> /dev/null
  fi
}

start_server() {
  local payload_path="$1"
  local ip="$2"
  local port="$3"
  while true; do
    echo "Starting netcat server for ${payload_path} on ${ip}:${port}"
    nc -l "${ip}" "${port}" < "${payload_path}"
    sleep 1
  done
}

hash() {
  shasum -a 256 "$1" | cut -d " " -f 1
}

verify_hashes() {
  # Sanity check no funny business
  from_hash=$(hash "$1")
  to_hash=$(hash "$2")
  if [[ "${from_hash}" != "${to_hash}" ]]; then
    echo "checksums did not match ${from_hash} ${to_hash}" 1>&2
    exit 10
  fi
}

client() {
  local payload_path="$1"
  local ip="$2"
  local port="$3"

  # time how long it takes to transfer the payload file
  start=$(gdate +%s%3N)
  nc -d "${ip}" "${port}" > "${payload_path}.client"
  stop=$(gdate +%s%3N)

  verify_hashes "${payload_path}" "${payload_path}.client"

  local duration=$((${stop} - ${start}))
  echo "${duration}"
}

main() {
  local size_kbytes=$((64 * 1024)) # 64 MB
  local payload_path="/tmp/throttle-test/data-${size_kbytes}.bin"
  local port="${PORT-9112}"
  # You may need to change this if you are on linux
  local interface="${INTERFACE-en0}"
  local ip=$(ipconfig getifaddr "${interface}")

  setup_payload "${payload_path}" "${size_kbytes}"
  case "$1" in
    server)
      start_server "${payload_path}" "${ip}" "${port}"
    ;;
    client)
      local duration=$(client "${payload_path}" "${ip}" "${port}")
      echo "Transfered ${size_kbytes} kbytes in ${duration} ms"
    ;;
    *)
      echo "Usage: $0 <server|client>"
    ;;
  esac
}

main "$@"

Обновлять

Вот что у меня есть на данный момент. Кажется, это работает правильно для направления загрузки, но совсем не тормозит в направлении выгрузки.

throttle_start() {
  local down_mbps="$1"
  local up_mbps="$2"
  local delay=$(($3 / 2))
  sudo dnctl pipe 1 config bw "${down_mbps}Mbit/s" delay "${delay}"
  sudo dnctl pipe 2 config bw "${up_mbps}Mbit/s" delay "${delay}"
  (cat /etc/pf.conf && \
    echo 'dummynet-anchor "throttle"' && \
    echo 'anchor "throttle"') | sudo pfctl -f -
  cat << EOF | sudo pfctl -a throttle -f -
dummynet in quick proto tcp from any port = 9112 to any pipe 1
dummynet out quick proto tcp from any to any port = 9112 pipe 2
EOF
  sudo pfctl -q -e
}

решение1

Это скрипт, который я с некоторым успехом использовал на El Capitan 10.11:

#!/bin/bash

# Reset dummynet to default config
dnctl -f flush

# Compose an addendum to the default config: creates a new anchor
(cat /etc/pf.conf &&
  echo 'dummynet-anchor "my_anchor"' &&
  echo 'anchor "my_anchor"') | pfctl -q -f -

# Configure the new anchor
cat <<EOF | pfctl -q -a my_anchor -f -
no dummynet quick on lo0 all
dummynet out proto tcp from any to any port 1:65535 pipe 1
EOF

# Create the dummynet queue
dnctl pipe 1 config bw 40000byte/s

# Activate PF
pfctl -E

# to check that dnctl is properly configured: sudo dnctl list

Единственное существенное отличие no dummynet quick on lo0 all, похоже, находится здесь, хотя я и не совсем понимаю, что он делает:https://www.reddit.com/r/osx/comments/3g7dim/limiting_bandwidth_per_application/

Связанный контент