#!/bin/sh
set -eu

# Fix LXD/Docker connectivity issues caused by Docker setting FORWARD policy to DROP.
# Reference:
# https://documentation.ubuntu.com/lxd/default/howto/network_bridge_firewalld/#prevent-connectivity-issues-with-lxd-and-docker

require_root() {
  if [ "$(id -u)" -ne 0 ]; then
    echo "Error: run as root (use sudo)." >&2
    exit 1
  fi
}

list_lxd_managed_bridges() {
  if ! command -v lxc >/dev/null 2>&1; then
    echo "Error: lxc command not found. Install/start LXD and retry." >&2
    return 1
  fi

  # Prefer CSV output: name, managed, type
  bridges="$(lxc network list --format csv -c nmt 2>/dev/null \
    | awk -F, 'tolower($2)=="yes" && tolower($3)=="bridge" {print $1}' \
    | sed '/^$/d' | sort -u)"

  # Fallback for older lxc versions that don't support --format csv/-c nmt reliably.
  if [ -z "$bridges" ]; then
    bridges="$(lxc network list 2>/dev/null \
      | awk -F'|' '
          BEGIN { OFS="" }
          /^\+/ { next }
          NF >= 8 {
            name=$2; type=$3; managed=$4;
            gsub(/^[ \t]+|[ \t]+$/, "", name);
            gsub(/^[ \t]+|[ \t]+$/, "", type);
            gsub(/^[ \t]+|[ \t]+$/, "", managed);
            if (tolower(type)=="bridge" && tolower(managed)=="yes") print name;
          }
        ' | sed '/^$/d' | sort -u)"
  fi

  echo "$bridges"
}

ensure_iptables_tools() {
  if ! command -v iptables >/dev/null 2>&1 || ! command -v ip6tables >/dev/null 2>&1; then
    echo "Error: iptables/ip6tables not found. Install iptables and retry." >&2
    exit 1
  fi
}

ensure_chain() {
  table_bin="$1"
  if ! "$table_bin" -nL DOCKER-USER >/dev/null 2>&1; then
    echo "Error: ${table_bin} chain DOCKER-USER not found." >&2
    echo "Start Docker first so the DOCKER-USER chain exists, then re-run this script." >&2
    exit 1
  fi
}

ensure_rule() {
  table_bin="$1"
  shift
  if "$table_bin" -C DOCKER-USER "$@" >/dev/null 2>&1; then
    echo "${table_bin}: rule already present: $*"
  else
    "$table_bin" -I DOCKER-USER "$@"
    echo "${table_bin}: added rule: $*"
  fi
}

apply_docker_user_rules() {
  bridge="$1"

  ensure_iptables_tools
  ensure_chain iptables
  ensure_chain ip6tables

  ensure_rule iptables  -i "$bridge" -j ACCEPT
  ensure_rule ip6tables -i "$bridge" -j ACCEPT
  ensure_rule iptables  -o "$bridge" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
  ensure_rule ip6tables -o "$bridge" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

  if command -v netfilter-persistent >/dev/null 2>&1; then
    netfilter-persistent save
    echo "Saved firewall rules with netfilter-persistent."
  elif command -v iptables-save >/dev/null 2>&1; then
    echo "Important: make firewall rules persistent across reboots for your distro."
    echo "Suggestion (Debian/Ubuntu): install iptables-persistent (netfilter-persistent)."
  fi
}

select_bridges() {
  bridges="$(list_lxd_managed_bridges)"

  if [ -z "$bridges" ]; then
    echo "Error: no managed LXD bridge networks found in 'lxc network list'." >&2
    exit 1
  fi

  first_bridge="$(echo "$bridges" | awk 'NR==1 {print; exit}')"

  if echo "$bridges" | grep -Fxq "lxdbr0"; then
    default_bridge="lxdbr0"
  else
    default_bridge="$first_bridge"
  fi

  echo "Available managed LXD bridges:" >&2
  echo "$bridges" | sed 's/^/- /' >&2

  INPUT="/dev/stdin"
  # For curl|bash, stdin is usually the script stream, so read user input from /dev/tty
  # only when stdout is attached to a terminal and /dev/tty is available.
  if [ ! -t 0 ] && [ -t 1 ] && [ -r /dev/tty ]; then
    INPUT="/dev/tty"
  fi

  printf "Apply fix to ALL bridges above? [Y/n]: " >&2
  read -r answer < "$INPUT" || answer=""

  case "${answer:-Y}" in
    y|Y|yes|YES|"")
      echo "$bridges"
      return
      ;;
  esac

  printf "Bridge to use [%s]: " "$default_bridge" >&2
  read -r selected < "$INPUT"
  selected=${selected:-$default_bridge}

  if ! echo "$bridges" | grep -Fxq "$selected"; then
    echo "Error: '$selected' is not a managed LXD bridge from 'lxc network list'." >&2
    exit 1
  fi

  echo "$selected"
}

require_root
SELECTED_BRIDGES="$(select_bridges)"

echo "$SELECTED_BRIDGES" | while IFS= read -r bridge; do
  [ -n "$bridge" ] || continue
  echo "Applying rules for bridge: $bridge"
  apply_docker_user_rules "$bridge"
done

echo "Done."
