IP-Range-Listen generieren

Nehmen wir an, Du hast irgendeinen openwrt Router und möchtest IP-Sets verwenden, um ganze Adress-Ranges in Deiner Firewall zu blockieren. Dann ist die dringendste Frage erstmal: wo bekomme ich zB die Adressen her, die Google verwendet, oder Facebook?

IP-Adressen (IPv4 und IPv6) werden von zentralen Registrierungsstellen vergeben. Das Gute ist, dass man diese abrufen kann. Gehen wir das eben schnell durch.

Es gibt folgende Registrierungsstellen, die im Internet die IP Adressen vergeben und dauerhaft zuordnen. Das sind die AfriNIC, ARIN, APNIC, LACNIC, RIPE NCC.

Zum Beispiel RIPE bietet eine Schnittstelle an, die wir mit Scripten anvisieren können, um uns zB die Infos, die wir benötigen, automatisiert zu holen. Wir wollen im ersten Schritt die ASN Nummern von Unternehmen haben, die wir gerne komplett blocken wollen. Das sind die Nummern, unter denen die Firmen registriert sind und denen ganze IP-Adress-Ranges zugewiesen werden.

ASN-lookup bash-script
#!/usr/bin/env bash
# asn_lookup.sh — Find all ASNs registered to one or more company/org names.
#
# Sources (all tried, results merged & deduplicated):
#   1. bgp.tools CSV     — bgp.tools/asns.csv        (global, all RIRs, grepped locally)
#   2. ARIN Whois-RWS    — whois.arin.net REST API    (ARIN region: US/CA/...)
#   3. RIPE NCC RDAP     — rdap.db.ripe.net           (Europe / Middle East / Central Asia)
#   4. Hurricane Electric— bgp.he.net (HTML scrape)  (global cross-reference)
#
# No API keys required. Dependencies: curl, jq
#
# Usage:
#   ./asn_lookup.sh -c "Cloudflare"
#   ./asn_lookup.sh -c "Alphabet" "Meta" -o companies --format txt
#   ./asn_lookup.sh -f companies.txt -o results --format csv
#   ./asn_lookup.sh -c "Amazon" --show-source -o amazon

set -euo pipefail

# ── Defaults ──────────────────────────────────────────────────────────────────
COMPANIES=()
COMPANY_FILE=""
OUTPUT=""
FORMAT="txt"
QUIET=false
NO_COLOR=false
SHOW_SOURCE=false
NO_DEDUP=false
TIMEOUT=10       # per-request curl timeout in seconds

UA="Mozilla/5.0 asn-lookup-sh/2.0"

CACHE_DIR="/tmp/.asn_lookup_$$"
BGP_TOOLS_CSV="$CACHE_DIR/asns.csv"
RESULTS_FILE="$CACHE_DIR/results.txt"
mkdir -p "$CACHE_DIR"
trap 'rm -rf "$CACHE_DIR"' EXIT

# ── Colours ───────────────────────────────────────────────────────────────────
setup_colors() {
  if [[ "$NO_COLOR" == true ]] || [[ ! -t 2 ]]; then
    BOLD=""; RESET=""; GREEN=""; YELLOW=""; RED=""; CYAN=""; DIM=""; BLUE=""
  else
    BOLD=$'\033[1m'; RESET=$'\033[0m'; GREEN=$'\033[32m'
    YELLOW=$'\033[33m'; RED=$'\033[31m'; CYAN=$'\033[36m'
    DIM=$'\033[2m';    BLUE=$'\033[34m'
  fi
}

log_info()   { [[ "$QUIET" == false ]] && printf '  %s\n' "${DIM}${1}${RESET}" >&2; }
log_ok()     { [[ "$QUIET" == false ]] && printf '  %s\n' "${GREEN}✔ ${1}${RESET}" >&2; }
log_warn()   { printf '  %s\n' "${YELLOW}⚠ ${1}${RESET}" >&2; }
log_source() { [[ "$QUIET" == false ]] && printf '    %s\n' "${BLUE}↳ ${1}${RESET}" >&2; }
log_header() { [[ "$QUIET" == false ]] && printf '%s\n' "${BOLD}${CYAN}${1}${RESET}" >&2; }
die()        { printf '%s\n' "${RED:-}Error: ${1}${RESET:-}" >&2; exit 1; }

# ── Deps ──────────────────────────────────────────────────────────────────────
check_deps() {
  local missing=()
  command -v curl &>/dev/null || missing+=("curl")
  command -v jq   &>/dev/null || missing+=("jq")
  [[ ${#missing[@]} -eq 0 ]] && return
  die "missing tools: ${missing[*]}
  Install: sudo apt install ${missing[*]}  |  brew install ${missing[*]}"
}

# ── Usage ─────────────────────────────────────────────────────────────────────
usage() {
  cat <<EOF
${BOLD}asn_lookup.sh${RESET} — find all ASNs registered to one or more company/org names

${BOLD}USAGE${RESET}
  asn_lookup.sh -c "Company" ["Company2"…] [OPTIONS]
  asn_lookup.sh -f FILE [OPTIONS]

${BOLD}INPUT${RESET}
  -c, --company NAME [NAME…]  Company/org names to search
  -f, --file FILE             Text file, one company name per line

${BOLD}OUTPUT${RESET}
  -o, --output FILENAME       Output filename (no extension); omit = stdout
      --format txt|csv        txt = one ASN per line (default)
                              csv = Company,ASN,Source

${BOLD}OPTIONS${RESET}
      --show-source           Annotate results with the source that found them
      --no-dedup              Keep duplicates across sources
      --timeout N             Curl timeout per request in seconds (default: 10)
      --no-color              Disable colours
  -q, --quiet                 Suppress progress output
  -h, --help                  Show this help

${BOLD}SOURCES${RESET}
  1. bgp.tools CSV    — global BGP table, all 5 RIRs (downloaded once, grepped locally)
  2. ARIN Whois-RWS   — US/Canada/Caribbean org registry
  3. RIPE NCC RDAP    — Europe/Middle East/Central Asia
  4. Hurricane Electric bgp.he.net — global cross-reference

${BOLD}EXAMPLES${RESET}
  asn_lookup.sh -c "Cloudflare"
  asn_lookup.sh -c "Alphabet" "Meta" -o results --format csv
  asn_lookup.sh -f companies.txt --show-source -o out
  asn_lookup.sh -c "Amazon" -q -o amazon
EOF
  exit 0
}

# ── Argument parsing ──────────────────────────────────────────────────────────
parse_args() {
  [[ $# -eq 0 ]] && usage
  while [[ $# -gt 0 ]]; do
    case "$1" in
      -c|--company)
        shift
        while [[ $# -gt 0 && ! "$1" =~ ^- ]]; do COMPANIES+=("$1"); shift; done ;;
      -f|--file)      COMPANY_FILE="$2"; shift 2 ;;
      -o|--output)    OUTPUT="$2";       shift 2 ;;
      --format)       FORMAT="$2";       shift 2 ;;
      --timeout)      TIMEOUT="$2";      shift 2 ;;
      --show-source)  SHOW_SOURCE=true;  shift ;;
      --no-dedup)     NO_DEDUP=true;     shift ;;
      --no-color)     NO_COLOR=true;     shift ;;
      -q|--quiet)     QUIET=true;        shift ;;
      -h|--help)      usage ;;
      *) die "unknown option: $1  (--help for usage)" ;;
    esac
  done
}

load_companies() {
  if [[ -n "$COMPANY_FILE" ]]; then
    [[ -f "$COMPANY_FILE" ]] || die "file not found: $COMPANY_FILE"
    while IFS= read -r line || [[ -n "$line" ]]; do
      line="${line#"${line%%[![:space:]]*}"}"; line="${line%"${line##*[![:space:]]}"}"
      [[ -z "$line" || "$line" =~ ^# ]] && continue
      COMPANIES+=("$line")
    done < "$COMPANY_FILE"
  fi
  [[ ${#COMPANIES[@]} -gt 0 ]] || die "no company names provided"
}

# ── Helpers ───────────────────────────────────────────────────────────────────
norm_asn()     { echo "$1" | tr '[:lower:]' '[:upper:]' | sed 's/^AS//' | tr -d '[:space:]'; }
is_valid_asn() { [[ "$1" =~ ^[0-9]+$ ]] && [[ "$1" -gt 0 ]] && [[ "$1" -lt 4294967296 ]]; }

add_result() {
  # add_result "company" "asn_raw" "source"
  local company="$1" source="$3"
  local asn; asn=$(norm_asn "$2")
  is_valid_asn "$asn" || return 0
  printf '%s|%s|%s\n' "$company" "$asn" "$source" >> "$RESULTS_FILE"
}

# Wrapper: curl with timeout, suppress errors, return 1 on failure
safe_curl() {
  curl -fsSL --max-time "$TIMEOUT" --connect-timeout 5 -A "$UA" "$@" 2>/dev/null
}

# ═══════════════════════════════════════════════════════════════════════════════
# SOURCE 1 — bgp.tools/asns.csv
# Downloaded once per run. Format: AS15169,"Google LLC",Eyeball
# We grep case-insensitively. The name column is the registered BGP holder name.
# ═══════════════════════════════════════════════════════════════════════════════
fetch_bgp_tools() {
  [[ -f "$BGP_TOOLS_CSV" ]] && return 0
  log_info "  downloading bgp.tools ASN list (one-time, ~3 MB)…"
  if safe_curl "https://bgp.tools/asns.csv" -o "$BGP_TOOLS_CSV"; then
    local count; count=$(wc -l < "$BGP_TOOLS_CSV")
    log_ok "  bgp.tools: ${count} entries cached"
    return 0
  fi
  rm -f "$BGP_TOOLS_CSV"
  return 1
}

source_bgptools() {
  local company="$1"
  fetch_bgp_tools || { log_warn "bgp.tools: download failed — skipping"; return 1; }

  local found=0
  # Each line: AS12345,"Some Org Name",Class
  # grep -i for the company name in the whole line, then extract the AS number
  while IFS= read -r line; do
    [[ -z "$line" ]] && continue
    local raw_asn; raw_asn=$(echo "$line" | cut -d',' -f1)
    local asn; asn=$(norm_asn "$raw_asn")
    if is_valid_asn "$asn"; then
      add_result "$company" "$asn" "bgp.tools"
      (( found++ )) || true
    fi
  done < <(grep -i -- "$company" "$BGP_TOOLS_CSV" 2>/dev/null || true)

  echo "$found"
}

# ═══════════════════════════════════════════════════════════════════════════════
# SOURCE 2 — ARIN Whois-RWS
# Step 1: search orgs by name wildcard → get org handles
# Step 2: for each handle, fetch /asns to get the ASN list
# ═══════════════════════════════════════════════════════════════════════════════
source_arin() {
  local company="$1"
  local found=0

  # jq's @uri encodes the string for use in a URL
  local encoded; encoded=$(printf '%s' "$company" | jq -sRr @uri)
  local search_url="https://whois.arin.net/rest/orgs;name=*${encoded}*.json"

  local orgs_json
  orgs_json=$(safe_curl -H "Accept: application/json" "$search_url") || return 1

  # The orgRef can be a single object or an array — normalise to a stream
  local handles
  handles=$(printf '%s' "$orgs_json" | jq -r '
    ( .orgs.orgRef // empty )
    | if type == "array" then .[] else . end
    | .["@handle"]
  ' 2>/dev/null) || return 1

  [[ -z "$handles" ]] && return 0

  while IFS= read -r handle; do
    [[ -z "$handle" ]] && continue
    local asns_json
    asns_json=$(safe_curl -H "Accept: application/json" \
      "https://whois.arin.net/rest/org/${handle}/asns.json") || continue

    # asnRef can be single object or array
    while IFS= read -r ref_url; do
      [[ -z "$ref_url" ]] && continue
      local asn; asn=$(basename "$ref_url" | sed 's/^AS//')
      if is_valid_asn "$asn"; then
        add_result "$company" "$asn" "ARIN"
        (( found++ )) || true
      fi
    done < <(printf '%s' "$asns_json" | jq -r '
      ( .asns.asnRef // empty )
      | if type == "array" then .[] else . end
      | .["$"]
    ' 2>/dev/null || true)
  done <<< "$handles"

  echo "$found"
}

# ═══════════════════════════════════════════════════════════════════════════════
# SOURCE 3 — RIPE NCC full-text search (RDAP)
# Searches aut-num objects whose org/descr fields contain the company name.
# Returns up to 100 results per query.
# ═══════════════════════════════════════════════════════════════════════════════
source_ripe() {
  local company="$1"
  local found=0

  local encoded; encoded=$(printf '%s' "$company" | jq -sRr @uri)
  # RIPE full-text search for aut-num objects
  local url="https://rest.db.ripe.net/search.json?query-string=${encoded}&type-filter=aut-num&flags=no-referenced&flags=no-irt"

  local json
  json=$(safe_curl -H "Accept: application/json" "$url") || return 1

  while IFS= read -r asn_raw; do
    [[ -z "$asn_raw" ]] && continue
    local asn; asn=$(norm_asn "$asn_raw")
    if is_valid_asn "$asn"; then
      add_result "$company" "$asn" "RIPE"
      (( found++ )) || true
    fi
  done < <(printf '%s' "$json" | jq -r '
    .objects.object[]?
    | select(.type == "aut-num")
    | .["primary-key"].attribute[]?
    | select(.name == "aut-num")
    | .value
  ' 2>/dev/null || true)

  echo "$found"
}

# ═══════════════════════════════════════════════════════════════════════════════
# SOURCE 4 — Hurricane Electric bgp.he.net
# Plain HTML search — extracts ASNs from search results table.
# ═══════════════════════════════════════════════════════════════════════════════
source_he() {
  local company="$1"
  local found=0

  local encoded; encoded=$(printf '%s' "$company" | jq -sRr @uri)
  local url="https://bgp.he.net/search?search%5Bsearch%5D=${encoded}&commit=Search"

  local html
  html=$(safe_curl "$url") || return 1

  # Extract ASNs from href="/AS12345" patterns in the results table
  while IFS= read -r asn; do
    [[ -z "$asn" ]] && continue
    if is_valid_asn "$asn"; then
      add_result "$company" "$asn" "HE"
      (( found++ )) || true
    fi
  done < <(echo "$html" | grep -oE 'href="/AS[0-9]+"' | grep -oE '[0-9]+' || true)

  echo "$found"
}

# ── Per-company orchestrator ──────────────────────────────────────────────────
lookup_company() {
  local company="$1"
  log_header ""
  log_header "  ▸ ${company}"

  local b; b=$(wc -l < "$RESULTS_FILE" 2>/dev/null || echo 0)

  # ── Source 1: bgp.tools
  log_info "[1/4] bgp.tools CSV…"
  local n1=0
  n1=$(source_bgptools "$company" 2>/dev/null || echo 0)
  log_source "bgp.tools: ${n1} hit(s)"

  # ── Source 2: ARIN
  log_info "[2/4] ARIN Whois-RWS…"
  local n2=0
  n2=$(source_arin "$company" 2>/dev/null || echo 0)
  log_source "ARIN: ${n2} hit(s)"

  # ── Source 3: RIPE NCC
  log_info "[3/4] RIPE NCC RDAP…"
  local n3=0
  n3=$(source_ripe "$company" 2>/dev/null || echo 0)
  log_source "RIPE: ${n3} hit(s)"

  # ── Source 4: Hurricane Electric
  log_info "[4/4] Hurricane Electric bgp.he.net…"
  local n4=0
  n4=$(source_he "$company" 2>/dev/null || echo 0)
  log_source "HE: ${n4} hit(s)"

  local after; after=$(wc -l < "$RESULTS_FILE" 2>/dev/null || echo 0)
  local raw=$(( after - b ))
  log_ok "${company}: ${raw} raw result(s) from all sources"
}

# ── Dedup & output ────────────────────────────────────────────────────────────
dedup() {
  # Keep first occurrence of each Company|ASN pair
  awk -F'|' '!seen[$1"|"$2]++' "$RESULTS_FILE"
}

write_output() {
  local data="$1"
  if [[ "$FORMAT" == "csv" ]]; then
    echo "Company,ASN,Source"
    while IFS='|' read -r company asn source; do
      [[ -z "$asn" ]] && continue
      printf '"%s",AS%s,%s\n' "$company" "$asn" "$source"
    done <<< "$data"
  else
    if [[ "$SHOW_SOURCE" == true ]]; then
      while IFS='|' read -r company asn source; do
        [[ -z "$asn" ]] && continue
        printf 'AS%-10s  # %-24s [%s]\n' "$asn" "$company" "$source"
      done <<< "$data"
    else
      while IFS='|' read -r _ asn _; do
        [[ -z "$asn" ]] && continue
        printf 'AS%s\n' "$asn"
      done <<< "$data"
    fi
  fi
}

# ── Main ──────────────────────────────────────────────────────────────────────
main() {
  parse_args "$@"
  setup_colors
  check_deps
  load_companies

  [[ "$FORMAT" != "txt" && "$FORMAT" != "csv" ]] && die "invalid format '$FORMAT'"

  touch "$RESULTS_FILE"

  log_header ""
  log_header "asn_lookup — searching ${#COMPANIES[@]} name(s) across 4 sources"

  for company in "${COMPANIES[@]}"; do
    lookup_company "$company"
  done

  local final_data
  if [[ "$NO_DEDUP" == true ]]; then
    final_data=$(cat "$RESULTS_FILE")
  else
    final_data=$(dedup)
  fi

  # Count non-empty lines
  local total=0
  [[ -n "$final_data" ]] && total=$(printf '%s\n' "$final_data" | grep -c . || true)

  log_header ""
  log_ok "Done — ${total} unique ASN(s) total"
  log_header ""

  if [[ -n "$OUTPUT" ]]; then
    local fname="$OUTPUT"
    [[ "$fname" != *".${FORMAT}" ]] && fname="${fname}.${FORMAT}"
    write_output "$final_data" > "$fname"
    [[ "$QUIET" == false ]] && \
      printf '  %s\n\n' "${BOLD}Saved → ${fname}  (${total} entries)${RESET}" >&2
  else
    write_output "$final_data"
  fi
}

main "$@"

Wir lassen das Tool für uns arbeiten, indem wir es auf Kommandzeile benutzen. Das heißt, wir speichern uns den Quelltext ab, zB unter asn-lookup.sh, machen das Programm ausführbar und öffnen dann das Kommandozeilenprogramm. Wir navigieren dann in den Ordner, in dem Du das Programm abgespeichert hast und können dann damit arbeiten, ohne lange Adress-Pfade anzugeben.

Im Kopf des Quelltextes siehst Du schon, wie man es benutzen kann, bzw. findest dort Hinweise auf die möglichen Optionen.

./asn_lookup.sh -c „C1“ „C2“ -o DATEINAME –format txt

Das Script prüft nun bei den Registrierungswebseiten die Unternehmensnamen C1 und C2. Die Liste kann aber theoretisch so lang sein, wie Du willst. Möchtest Du alle Companies zusammen lookuppen, dann passt Du Dir das einfach an, wie gewünscht. Aber amP macht das keinen Sinn. Fangen wir mit einem einfachen und wohlverdienten Beispiel an: Google.

./asn_lookup.sh -c „Google“ -o google –format txt

Wenn Du nun das Script ausführst, erstell es in dem Ordner in dem das Script liegt, eine Datei die da heißt google.txt. In diesem befinden sich alle ASN Nummern, die zu dem Unternehmensnamen gefunden wurden.

Mit dieser Liste können wir nun die IP-Ranges abfragen, die wir für unsere IP-Sets brauchen, damit unser Router künftig alle diese Adressen blockiert.

Wir befinden uns immer noch im gleichen Ordner und speichern uns das nachfolgende Script als asn-ranges.sh ab.

ASN-Ranges bash-script
#!/usr/bin/env bash
# asn_ranges.sh — Collect all IPv4/IPv6 prefixes announced by one or more ASNs.
#
# Queries ALL available sources in parallel per ASN, then merges + deduplicates:
#   1. RIPE NCC stat API       (no key required)
#   2. bgp.tools               (no key required; replaces defunct BGPView)
#   3. RADB / IRR whois        (no key; needs: whois)
#   4. RouteViews API          (no key required; optional: --rv-key for higher rate limits)
#   5. Cloudflare Radar API    (requires free token: --cf-token)
#
# Dependencies: curl, jq
# Optional:     whois  (for RADB/IRR source)
#
# Usage examples:
#   ./asn_ranges.sh -a 13335 15169
#   ./asn_ranges.sh -a AS13335 AS15169 -o cloudflare-google --format csv
#   ./asn_ranges.sh -f asns.txt -o output --format txt --ipv4-only
#   ./asn_ranges.sh -a 13335 --ipv6-only -o ranges --format csv
#   ./asn_ranges.sh -a 13335 15169 --delay 0.5 --no-color -q
#   ./asn_ranges.sh -a 13335 --cf-token TOKEN -o out
#   ./asn_ranges.sh -a 13335 --rv-key KEY -o out
#
# Output file naming (when -o is given):
#   <name>-ipv4.<ext>  and/or  <name>-ipv6.<ext>

set -euo pipefail

# ── Defaults ──────────────────────────────────────────────────────────────────
FORMAT="txt"
OUTPUT=""
DELAY="0.25"
WANT_V4=true
WANT_V6=true
QUIET=false
NO_COLOR=false
ASN_LIST=()
ASN_FILE=""
CF_TOKEN=""        # Cloudflare Radar API token (optional)
RV_KEY=""          # RouteViews API key (optional, relaxes rate limits)

UA="asn-ranges-sh/3.0"

# API URL templates
RIPE_URL="https://stat.ripe.net/data/announced-prefixes/data.json?resource=AS{ASN}&sourceapp=asn-ranges-sh"
BGPTOOLS_URL="https://bgp.tools/table.jsonl"
RV_URL="https://api.routeviews.org/asn/{ASN}"
CF_URL="https://api.cloudflare.com/client/v4/radar/bgp/routes/pfx2as?asn={ASN}&format=json"

# ── Colours ───────────────────────────────────────────────────────────────────
setup_colors() {
  if [[ "$NO_COLOR" == true ]] || [[ ! -t 2 ]]; then
    BOLD=""; RESET=""; GREEN=""; YELLOW=""; RED=""; CYAN=""; DIM=""; BLUE=""
  else
    BOLD=$'\033[1m'; RESET=$'\033[0m'; GREEN=$'\033[32m'
    YELLOW=$'\033[33m'; RED=$'\033[31m'; CYAN=$'\033[36m'
    DIM=$'\033[2m';   BLUE=$'\033[34m'
  fi
}

log_info()   { [[ "$QUIET" == false ]] && printf '%s\n' "  ${DIM}${1}${RESET}" >&2 || true; }
log_ok()     { [[ "$QUIET" == false ]] && printf '%s\n' "  ${GREEN}✔ ${1}${RESET}" >&2 || true; }
log_warn()   { printf '%s\n' "  ${YELLOW}⚠ ${1}${RESET}" >&2; }
log_err()    { printf '%s\n' "  ${RED}✘ ${1}${RESET}" >&2; }
log_header() { [[ "$QUIET" == false ]] && printf '%s\n' "${BOLD}${CYAN}${1}${RESET}" >&2 || true; }
log_plain()  { [[ "$QUIET" == false ]] && printf '%s\n' "  ${1}" >&2 || true; }
log_src()    { [[ "$QUIET" == false ]] && printf '%s\n' "    ${BLUE}↳ ${1}${RESET}" >&2 || true; }

die() { printf '%s\n' "${RED}Error: ${1}${RESET}" >&2; exit 1; }

# ── Dependency check ──────────────────────────────────────────────────────────
check_deps() {
  local missing=()
  command -v curl &>/dev/null || missing+=("curl")
  command -v jq   &>/dev/null || missing+=("jq")
  if [[ ${#missing[@]} -gt 0 ]]; then
    die "missing required tools: ${missing[*]}
  Install with:
    Debian/Ubuntu : sudo apt install ${missing[*]}
    macOS         : brew install ${missing[*]}
    Arch          : sudo pacman -S ${missing[*]}"
  fi
  if ! command -v whois &>/dev/null; then
    log_warn "whois not found — RADB/IRR source will be skipped"
    log_warn "  Install: sudo apt install whois  OR  brew install whois"
  fi
}

# ── Usage ─────────────────────────────────────────────────────────────────────
usage() {
  cat <<EOF
${BOLD}asn_ranges.sh${RESET} — fetch all IPv4/IPv6 prefixes for one or more ASNs
Queries all available data sources and merges + deduplicates results.

${BOLD}USAGE${RESET}
  asn_ranges.sh -a ASN [ASN …] [OPTIONS]
  asn_ranges.sh -f FILE        [OPTIONS]

${BOLD}INPUT (one of -a or -f required)${RESET}
  -a, --asn ASN [ASN …]   One or more ASNs (e.g. 13335 or AS15169)
  -f, --file FILE          Text file: one ASN per line (or comma-separated)

${BOLD}OUTPUT${RESET}
  -o, --output FILENAME    Base output filename (no extension).
                           Writes <FILENAME>-ipv4.<ext> and <FILENAME>-ipv6.<ext>
                           Omit to print to stdout.
      --format txt|csv     txt = one prefix per line (default)
                           csv = two-column ASN,Prefix

${BOLD}ADDRESS FAMILIES${RESET}
      --ipv4-only          Only fetch/output IPv4 prefixes
      --ipv6-only          Only fetch/output IPv6 prefixes

${BOLD}DATA SOURCES${RESET}
  Sources queried automatically (no key required):
    • RIPE NCC stat API
    • BGPView API
    • RADB / IRR  (whois must be installed)
    • RouteViews  (1 req/s guest rate limit without a key)

  Optional authenticated sources:
      --cf-token TOKEN     Cloudflare Radar API bearer token
                           (free at https://dash.cloudflare.com/profile/api-tokens)
      --rv-key KEY         RouteViews API key
                           (free via PeeringDB at https://api.routeviews.org/member-area/)
      --skip-source NAME   Skip a specific source (ripe|bgptools|radb|routeviews|cloudflare)
                           Can be repeated.

${BOLD}MISC${RESET}
      --delay SECONDS      Pause between ASN iterations (default: 0.25)
      --no-color           Disable coloured output
  -q, --quiet              Suppress progress; errors still shown
  -h, --help               Show this help

${BOLD}EXAMPLES${RESET}
  asn_ranges.sh -a 13335 15169
  asn_ranges.sh -a AS13335 -o cloudflare --format csv
    → cloudflare-ipv4.csv  +  cloudflare-ipv6.csv
  asn_ranges.sh -f asns.txt -o output --ipv4-only
    → output-ipv4.txt
  asn_ranges.sh -a 13335 --cf-token \$CF_TOKEN --rv-key \$RV_KEY -o out
  asn_ranges.sh -a 13335 --skip-source bgpview --skip-source radb -o out
EOF
  exit 0
}

# ── Argument parsing ──────────────────────────────────────────────────────────
SKIP_SOURCES=()

parse_args() {
  [[ $# -eq 0 ]] && usage

  while [[ $# -gt 0 ]]; do
    case "$1" in
      -a|--asn)
        shift
        while [[ $# -gt 0 && ! "$1" =~ ^- ]]; do
          IFS=',' read -ra parts <<< "$1"
          ASN_LIST+=("${parts[@]}")
          shift
        done
        ;;
      -f|--file)        ASN_FILE="$2";          shift 2 ;;
      -o|--output)      OUTPUT="$2";            shift 2 ;;
      --format)         FORMAT="$2";            shift 2 ;;
      --delay)          DELAY="$2";             shift 2 ;;
      --ipv4-only)      WANT_V6=false;          shift   ;;
      --ipv6-only)      WANT_V4=false;          shift   ;;
      --no-color)       NO_COLOR=true;          shift   ;;
      -q|--quiet)       QUIET=true;             shift   ;;
      --cf-token)       CF_TOKEN="$2";          shift 2 ;;
      --rv-key)         RV_KEY="$2";            shift 2 ;;
      --skip-source)    SKIP_SOURCES+=("$2");   shift 2 ;;
      -h|--help)        usage ;;
      *) die "unknown option: $1 — run with --help for usage" ;;
    esac
  done
}

source_enabled() {
  local name="$1"
  for s in "${SKIP_SOURCES[@]:-}"; do
    [[ "$s" == "$name" ]] && return 1
  done
  return 0
}

# ── ASN helpers ───────────────────────────────────────────────────────────────
normalize_asn() {
  echo "$1" | tr '[:lower:]' '[:upper:]' | sed 's/^[[:space:]]*AS//' | tr -d '[:space:]'
}

is_valid_asn() { [[ "$1" =~ ^[0-9]+$ ]]; }

load_asns() {
  local -n _out=$1
  local raw=()

  if [[ -n "$ASN_FILE" ]]; then
    [[ -f "$ASN_FILE" ]] || die "file not found: $ASN_FILE"
    while IFS= read -r line || [[ -n "$line" ]]; do
      [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
      IFS=',' read -ra parts <<< "$line"
      raw+=("${parts[@]}")
    done < "$ASN_FILE"
  else
    raw=("${ASN_LIST[@]}")
  fi

  local -a seen=()
  for raw_asn in "${raw[@]}"; do
    local n
    n=$(normalize_asn "$raw_asn")
    [[ -z "$n" ]] && continue
    is_valid_asn "$n" || { log_warn "skipping invalid ASN: '$raw_asn'"; continue; }
    local dup=false
    for s in "${seen[@]:-}"; do [[ "$s" == "$n" ]] && dup=true && break; done
    if [[ "$dup" == false ]]; then
      _out+=("$n")
      seen+=("$n")
    fi
  done
}

# ── Source fetchers ───────────────────────────────────────────────────────────
# Each fetcher prints "IPv4|<prefix>" or "IPv6|<prefix>" lines to stdout.
# Returns 0 on success, 1 on failure.

fetch_ripe() {
  local asn="$1"
  local url="${RIPE_URL/\{ASN\}/$asn}"
  local http_code json

  http_code=$(curl -o /tmp/_asn_ripe.json -w "%{http_code}" -fsSL \
    --max-time 20 -A "$UA" "$url" 2>/dev/null) || true

  [[ "$http_code" != "200" ]] && return 1
  json=$(cat /tmp/_asn_ripe.json)

  local status
  status=$(printf '%s' "$json" | jq -r '.status // "error"')
  [[ "$status" != "ok" ]] && return 1

  if [[ "$WANT_V4" == true ]]; then
    printf '%s' "$json" | jq -r '
      .data.prefixes[]?.prefix // empty
      | select(test(":") | not)
      | "IPv4|\(.)"
    '
  fi
  if [[ "$WANT_V6" == true ]]; then
    printf '%s' "$json" | jq -r '
      .data.prefixes[]?.prefix // empty
      | select(test(":"))
      | "IPv6|\(.)"
    '
  fi
}

fetch_bgptools() {
  local asn="$1"
  local http_code

  # bgp.tools/table.jsonl: one JSON object per line, e.g.
  #   {"CIDR":"1.1.1.0/24","ASN":13335,"Hits":509}
  # We stream it and filter for the target ASN in jq to avoid loading the full file.
  http_code=$(curl -o /tmp/_asn_bgptools.jsonl -w "%{http_code}" -fsSL \
    --max-time 60 -A "$UA" "$BGPTOOLS_URL" 2>/dev/null) || true

  [[ "$http_code" != "200" ]] && return 1
  [[ ! -s /tmp/_asn_bgptools.jsonl ]] && return 1

  if [[ "$WANT_V4" == true ]]; then
    jq -r --argjson asn "$asn" '
      select(.ASN == $asn)
      | .CIDR
      | select(test(":") | not)
      | "IPv4|\(.)"
    ' /tmp/_asn_bgptools.jsonl 2>/dev/null || true
  fi
  if [[ "$WANT_V6" == true ]]; then
    jq -r --argjson asn "$asn" '
      select(.ASN == $asn)
      | .CIDR
      | select(test(":"))
      | "IPv6|\(.)"
    ' /tmp/_asn_bgptools.jsonl 2>/dev/null || true
  fi
}

fetch_radb() {
  local asn="$1"
  command -v whois &>/dev/null || return 1

  if [[ "$WANT_V4" == true ]]; then
    # !gasNNN — get all IPv4 routes for an origin ASN
    whois -h whois.radb.net "!gas${asn}" 2>/dev/null \
      | tr ' ' '\n' \
      | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$' \
      | sed 's/^/IPv4|/' \
      || true
  fi
  if [[ "$WANT_V6" == true ]]; then
    # !6asNNN — get all IPv6 routes for an origin ASN
    whois -h whois.radb.net "!6as${asn}" 2>/dev/null \
      | tr ' ' '\n' \
      | grep -E '^[0-9a-fA-F:]+/[0-9]+$' \
      | sed 's/^/IPv6|/' \
      || true
  fi
}

fetch_routeviews() {
  local asn="$1"
  local url="${RV_URL/\{ASN\}/$asn}"
  local http_code json
  local -a curl_args=(-fsSL --max-time 30 -A "$UA")
  [[ -n "$RV_KEY" ]] && curl_args+=(-H "Api-Key: ${RV_KEY}")

  http_code=$(curl -o /tmp/_asn_rv.json -w "%{http_code}" \
    "${curl_args[@]}" "$url" 2>/dev/null) || true

  [[ "$http_code" != "200" ]] && return 1
  json=$(cat /tmp/_asn_rv.json)

  # RouteViews /asn/{ASN} returns .data.prefixes[] with each prefix as a string
  local error
  error=$(printf '%s' "$json" | jq -r '.error // empty')
  [[ -n "$error" ]] && return 1

  if [[ "$WANT_V4" == true ]]; then
    printf '%s' "$json" | jq -r '
      .data.prefixes[]? // empty
      | select(test(":") | not)
      | "IPv4|\(.)"
    ' 2>/dev/null || true
  fi
  if [[ "$WANT_V6" == true ]]; then
    printf '%s' "$json" | jq -r '
      .data.prefixes[]? // empty
      | select(test(":"))
      | "IPv6|\(.)"
    ' 2>/dev/null || true
  fi
}

fetch_cloudflare() {
  local asn="$1"
  [[ -z "$CF_TOKEN" ]] && return 1

  local url="${CF_URL/\{ASN\}/$asn}"
  local http_code json

  http_code=$(curl -o /tmp/_asn_cf.json -w "%{http_code}" -fsSL \
    --max-time 20 -A "$UA" \
    -H "Authorization: Bearer ${CF_TOKEN}" \
    "$url" 2>/dev/null) || true

  [[ "$http_code" != "200" ]] && return 1
  json=$(cat /tmp/_asn_cf.json)

  local success
  success=$(printf '%s' "$json" | jq -r '.success // false')
  [[ "$success" != "true" ]] && return 1

  # Cloudflare pfx2as returns .result.prefix_origins[].prefix
  if [[ "$WANT_V4" == true ]]; then
    printf '%s' "$json" | jq -r '
      .result.prefix_origins[]?.prefix // empty
      | select(test(":") | not)
      | "IPv4|\(.)"
    '
  fi
  if [[ "$WANT_V6" == true ]]; then
    printf '%s' "$json" | jq -r '
      .result.prefix_origins[]?.prefix // empty
      | select(test(":"))
      | "IPv6|\(.)"
    '
  fi
}

# ── Multi-source fetch + merge ────────────────────────────────────────────────
# Calls all enabled sources, merges output, deduplicates.
# Prints "SOURCE|VERSION|PREFIX" lines so the summary can show per-source counts.
fetch_all_sources() {
  local asn="$1"
  local combined_v4="" combined_v6=""

  # Helper: count tagged lines safely (never triggers set -e)
  count_lines() { printf '%s\n' "$1" | grep -c "$2" 2>/dev/null || echo 0; }

  # Helper: accumulate prefixes from a source's output into combined_v4 / combined_v6
  accumulate() {
    local src_out="$1" label="$2"
    local n4 n6
    n4=$(count_lines "$src_out" "^IPv4|")
    n6=$(count_lines "$src_out" "^IPv6|")
    log_src "${label}: ${n4} IPv4  ${n6} IPv6"
    [[ -n "$src_out" ]] || return 0
    local raw4 raw6
    raw4=$(printf '%s\n' "$src_out" | grep "^IPv4|" | cut -d'|' -f2 2>/dev/null || true)
    raw6=$(printf '%s\n' "$src_out" | grep "^IPv6|" | cut -d'|' -f2 2>/dev/null || true)
    # Only append if there is actual content — avoids stray newlines that
    # confuse the dedup regex later
    [[ -n "${raw4//[$'\n\r ']/}" ]] && combined_v4+=$'\n'"$raw4"
    [[ -n "${raw6//[$'\n\r ']/}" ]] && combined_v6+=$'\n'"$raw6"
  }

  # --- RIPE ---
  if source_enabled "ripe"; then
    local ripe_out=""
    if ripe_out=$(fetch_ripe "$asn" 2>/dev/null); then
      accumulate "$ripe_out" "RIPE NCC     "
    else
      log_src "RIPE NCC      : ${RED}failed${RESET}"
    fi
  fi

  # --- bgp.tools ---
  if source_enabled "bgptools"; then
    local bgptools_out=""
    if bgptools_out=$(fetch_bgptools "$asn" 2>/dev/null); then
      accumulate "$bgptools_out" "bgp.tools    "
    else
      log_src "bgp.tools     : ${RED}failed${RESET}"
    fi
  fi

  # --- RADB/IRR ---
  if source_enabled "radb"; then
    local radb_out=""
    if radb_out=$(fetch_radb "$asn" 2>/dev/null); then
      accumulate "$radb_out" "RADB/IRR     "
    else
      log_src "RADB/IRR      : ${YELLOW}failed / skipped${RESET}"
    fi
  fi

  # --- RouteViews ---
  if source_enabled "routeviews"; then
    local rv_out=""
    if rv_out=$(fetch_routeviews "$asn" 2>/dev/null); then
      accumulate "$rv_out" "RouteViews   "
    else
      log_src "RouteViews    : ${RED}failed${RESET}"
    fi
  fi

  # --- Cloudflare Radar ---
  if source_enabled "cloudflare"; then
    if [[ -n "$CF_TOKEN" ]]; then
      local cf_out=""
      if cf_out=$(fetch_cloudflare "$asn" 2>/dev/null); then
        accumulate "$cf_out" "CF Radar     "
      else
        log_src "CF Radar      : ${RED}failed${RESET}"
      fi
    else
      log_src "CF Radar      : ${DIM}skipped (no --cf-token)${RESET}"
    fi
  fi

  # Deduplicate and emit as "IPv4|prefix" / "IPv6|prefix" lines
  # These tagged lines are what gets stored in RECORDS_DATA and read by write_output_family
  if [[ "$WANT_V4" == true && -n "$combined_v4" ]]; then
    printf '%s\n' "$combined_v4" \
      | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$' \
      | sort -u \
      | sed 's/^/IPv4|/'
  fi
  if [[ "$WANT_V6" == true && -n "$combined_v6" ]]; then
    printf '%s\n' "$combined_v6" \
      | grep -E '^[0-9a-fA-F:]+:[0-9a-fA-F:]+/[0-9]+$' \
      | sort -u \
      | sed 's/^/IPv6|/'
  fi
}

# ── Output helpers ────────────────────────────────────────────────────────────
write_output_family() {
  local family="$1"

  if [[ "$FORMAT" == "csv" ]]; then
    echo "ASN,Prefix"
    for idx in "${!RECORDS_ASN[@]}"; do
      local asn="${RECORDS_ASN[$idx]}"
      while IFS='|' read -r ver prefix; do
        [[ "$ver" == "$family" && -n "$prefix" ]] && echo "AS${asn},${prefix}" || true
      done <<< "${RECORDS_DATA[$idx]}"
    done
  else
    for idx in "${!RECORDS_DATA[@]}"; do
      while IFS='|' read -r ver prefix; do
        [[ "$ver" == "$family" && -n "$prefix" ]] && echo "$prefix" || true
      done <<< "${RECORDS_DATA[$idx]}"
    done
  fi
}

write_output_stdout() {
  if [[ "$FORMAT" == "csv" ]]; then
    echo "ASN,Prefix"
    for idx in "${!RECORDS_ASN[@]}"; do
      local asn="${RECORDS_ASN[$idx]}"
      while IFS='|' read -r _ prefix; do
        [[ -n "$prefix" ]] && echo "AS${asn},${prefix}" || true
      done <<< "${RECORDS_DATA[$idx]}"
    done
  else
    for idx in "${!RECORDS_DATA[@]}"; do
      while IFS='|' read -r _ prefix; do
        [[ -n "$prefix" ]] && echo "$prefix" || true
      done <<< "${RECORDS_DATA[$idx]}"
    done
  fi
}

# ── Main ──────────────────────────────────────────────────────────────────────
main() {
  parse_args "$@"
  setup_colors
  check_deps

  [[ -z "$ASN_FILE" && ${#ASN_LIST[@]} -eq 0 ]] && die "provide ASNs via -a or -f  (--help for usage)"
  [[ -n "$ASN_FILE" && ${#ASN_LIST[@]} -gt 0 ]]  && die "-a and -f are mutually exclusive"
  [[ "$WANT_V4" == false && "$WANT_V6" == false ]] && die "--ipv4-only and --ipv6-only cannot be combined"
  [[ "$FORMAT" != "txt" && "$FORMAT" != "csv" ]]   && die "invalid format '$FORMAT' — use txt or csv"

  local -a asns=()
  load_asns asns
  [[ ${#asns[@]} -eq 0 ]] && die "no valid ASNs found in input"

  # Show which sources are active
  log_header ""
  log_header "asn_ranges — fetching prefixes for ${#asns[@]} ASN(s)"
  log_header ""
  if [[ "$QUIET" == false ]]; then
    local src_list="RIPE NCC, bgp.tools, RADB/IRR, RouteViews"
    [[ -n "$CF_TOKEN" ]] && src_list+=", Cloudflare Radar"
    printf '%s\n' "  ${DIM}Sources : ${src_list}${RESET}" >&2
    [[ -z "$CF_TOKEN" ]] && printf '%s\n' "  ${DIM}Tip     : add --cf-token TOKEN to include Cloudflare Radar${RESET}" >&2
    [[ -z "$RV_KEY"   ]] && printf '%s\n' "  ${DIM}Tip     : add --rv-key KEY for higher RouteViews rate limits${RESET}" >&2
    printf '\n' >&2
  fi

  RECORDS_ASN=()
  RECORDS_DATA=()
  local -a failed=()
  local total=0 total_v4=0 total_v6=0

  for i in "${!asns[@]}"; do
    local asn="${asns[$i]}"
    local idx=$(( i + 1 ))
    log_info "[${idx}/${#asns[@]}] AS${asn} — querying all sources …"

    local lines=""
    if lines=$(fetch_all_sources "$asn"); then
      local v4_n v6_n
      v4_n=$(printf '%s\n' "$lines" | grep -c "^IPv4|" 2>/dev/null || echo 0)
      v6_n=$(printf '%s\n' "$lines" | grep -c "^IPv6|" 2>/dev/null || echo 0)

      if [[ $(( v4_n + v6_n )) -eq 0 ]]; then
        log_err "AS${asn}: no prefixes found across all sources"
        failed+=("$asn")
      else
        RECORDS_ASN+=("$asn")
        RECORDS_DATA+=("$lines")
        log_ok "AS${asn}: ${v4_n} IPv4   ${v6_n} IPv6  (merged + deduplicated)"
        (( total    += v4_n + v6_n )) || true
        (( total_v4 += v4_n        )) || true
        (( total_v6 += v6_n        )) || true
      fi
    else
      log_err "AS${asn}: all sources failed"
      failed+=("$asn")
    fi

    [[ $idx -lt ${#asns[@]} ]] && sleep "$DELAY"
    [[ "$QUIET" == false ]] && printf '\n' >&2
  done

  [[ $total -eq 0 ]] && die "no prefixes retrieved from any source"

  # Summary
  log_header "────────────────────────────────────────"
  log_plain "${BOLD}Total (deduplicated)${RESET}"
  [[ "$QUIET" == false ]] && printf '%s\n' "  ${GREEN}IPv4 : ${total_v4}${RESET}" >&2
  [[ "$QUIET" == false ]] && printf '%s\n' "  ${YELLOW}IPv6 : ${total_v6}${RESET}" >&2
  [[ ${#failed[@]} -gt 0 ]] && log_err "No data : $(printf 'AS%s ' "${failed[@]}")"
  log_header "────────────────────────────────────────"
  log_plain ""

  if [[ -n "$OUTPUT" ]]; then
    local ext="$FORMAT"
    local base="${OUTPUT%.*}"

    if [[ "$WANT_V4" == true ]]; then
      local fname_v4="${base}-ipv4.${ext}"
      write_output_family "IPv4" > "$fname_v4"
      log_plain "${BOLD}Saved → ${fname_v4}  (${total_v4} prefixes)${RESET}"
    fi

    if [[ "$WANT_V6" == true ]]; then
      local fname_v6="${base}-ipv6.${ext}"
      write_output_family "IPv6" > "$fname_v6"
      log_plain "${BOLD}Saved → ${fname_v6}  (${total_v6} prefixes)${RESET}"
    fi

    log_plain ""
  else
    write_output_stdout
  fi

  rm -f /tmp/_asn_ripe.json /tmp/_asn_bgptools.jsonl /tmp/_asn_rv.json /tmp/_asn_cf.json
}

main "$@"

Wir machen das Script noch ausführbar und können dann anhand unserer vorher angefertigten Textdatei, die alle ASN Nummern enthält, eine Abfrage der IP-Ranges machen.

./asn_ranges.sh -f TEXTDATEIMITASNS -o DATEINAMEN –format txt

Also: ./asn_ranges.sh -f google.txt -o google –format txt

Das wird uns zwei Textfiles erzeugen, einmal für IPv4-Adressen und einmal für IPv6-Adressen. Das hat den Hintergrund, dass Openwrt, bzw. dessen Firewall die Adressen getrennt benötigt. Das wird etwas weiter unten klar, warum das so ist.

Nun haben wir zwei Textdateien, die wir, so wie sie sind, in unserer openwrt Firewall verwenden können. Im folgenden benutzen wir die grafische Oberfläche LuCI.

Erstellen eines IP-Sets

Wenn ihr im LuCI Adminpanel seid, navigiert ihr zum Firewall-Setup.

Dann sogleich zu den IP-Sets.

Hier habt ihr die Möglichkeit neue IP-Sets anzulegen. Klickt auf „add“ und erstellt ein neues IP-Set. Wie erwähnt müssen wir das zwei Mal machen, einmal für die IPv4- und dann für die IPv6-Adressranges. Zuerst erstellen wir das Set für IPv4.

Unter Include File könnt ihr die eben erstellte Textdatei mit den IPv4 Adressen hochladen (das Hochladen wird hier nicht in einem Bild dargestellt, weil das in den verschiedenen Versionen von LuCi auch anders aussieht. Ihr werdet aber damit klarkommen die Datei hochzuladen. Hier seht ihr im letzten Bild, dass die Datei bereits hochgeladen ist. Wichtig ist, dass wir zuerst ein IPv4 Set erstellen wollen.)

Immer schön Saven und dann wiederholen wir das für das IPv6 Set.

Damit hätten wir im Router nun die Adressranges hinterlegt. Jetzt müssen wir noch eine Firewall-Regel daraus machen und gehen in den nächsten Reiter „Traffic-Rules“. Das kann und wird leider in manchen LuCi Versionen auch anders aussehen.

Dort erstellt ihr eine neue Regel. Auch zuerst wieder die Regel für IPv4.

Dann wechselt ihr auf den Reiter Advanced Settings, um dort das IPv4 Set anzugeben, welches wir eben gerade erstellt haben.

Hier lasst ihr alle anderen Einstellungen so, und klickt auf „Use IP-Set“. Dort wählt ihr dann das IPv4-Set aus. Wichtig ist hier auch, dass das übereinstimmt mit der Option Restrict to adress family. Ansonsten bekommt ihr Fehlermeldungen des Todes und die Regel wird nicht greifen.

Das gleiche wiederholen wir mit dem IPv6-Set. Vergesst nicht, danach alles zu speichern und einmal die Firewall neu zustarten wenn ihr mit ssh eingeloggt seid. fw4 restart

Und wenn ihr alles korrekt umgesetzt habt, und ihr euch über euren Router mit dem Netz verbindet, solltet ihr beim Aufruf von google.de die folgende Seite sehen:

Mission Accomplished! Congrats! <3

Nachteile so klar wie Kloßbrühe

Bei Google ist mir jeder Block tatsächlich willkommen. Youtube wird ebenso geblockt, wie googletagmanager.com. Doch blocke ich zB die ASNs von Microslop, dann blockt es mir auch zB duckduckgo.com weg. Das ist ärgerlich, denn ich mochte ddg eigentlich immer und die Suchmaschine war meine bisher favorisierte. Ich könnte die ASN IP Ranges von ddg jetzt aus der Liste händisch entfernen, aber ganz im Ernst: darauf habe ich gar keine Lust.

Wer eine so deutliche Kollaboration mit Microsoft eingeht, dass die IPs der Heimat-URL auf deren Hostnamen aufgelöst werden (oder andersrum), dann ist mir dieser Block tatsächlich auch willkommen, weil dann leider ddg unbenutzbar geworden ist. Es gibt andere Suchmaschinen, die auch zu irgendwelchen Ergebnissen führen. Außerdem ist das auch immer das Risiko, wenn man nomadig durch das Internet zieht. Ein Dienst geht, ein anderer kommt. Es ist ok. Aber für Dich vielleicht nicht.

Daher ist es wichtig sich klar zu machen, dass das Blocken ganzer IP Ranges von missbräuchlichen Companies als Holzhammermethode tatsächlich eine bedeutende Klarheit schafft und man sich damit sehr festlegt.

Doch genau das ist mir zB auch persönlich wichtig. Denn es wird sich nichts ändern, wenn wir Dienste weiterhin nutzen, die unsere Privatsphäre und den Datenschutz so krass missachten. Da wo wir sagen: tja kann man nichts machen und einen Dienst weiterhin nutzen, kann man genauso gut auch sagen, tja, kann man nichts machen und sich eine Alternative suchen. Ein ASN-weiter Block schafft die Grundlage für ein gesundes tja, kann man halt nichts machen.

In diesem Sinne, viel Spaß beim Arschtritte verteilen! Kann man halt nichts machen! <3