Using tunnelbroker with Apple's Airport Extreme

Inspired by the IPv6 episode from ChaosRadioExpress I tried tunnelbroker.net as a my 6in4 provider, because Unity Media cannot get native IPv6 running for long-term customers (ridiculous: new customers get it without any problem). tunnelbroker enables you to build a static tunnel, which is quite problematic if you have a DSL connection or something similar that changes it’s IPv4 frequently. I use my Raspberry-PI, running on ArchLinux ARM, together with this litte ruby script to keep my public IP updated.

  #!/usr/bin/env ruby
  require 'net/http'
  require 'net/https'

  BASEDIR = '/etc'
  LAST_IP_FILE = "#{BASEDIR}/tunnelbroker.lastip"

  # for debugging.
  BE_VERBOSE = false

  # point your browser to http://ipv4.tunnelbroker.net/ipv4_end.php for more detailed information
  tunnel_options = {
    ip: 'auto', # use auto here, do not use any response from ifconfig.me or similar services for security reasons
    pass: 'echo -n YOURPASSWORD | md5sum',
    apikey: 'YOUR API KEY, FIND IT ON THE MAIN PAGE OF YOUR TB ACCOUNT',
    tid: 000000 # your tunnel id as int
  }
  # Enpoint to update the tunnel ip. This must be called via https!
  tunnel_url = 'ipv4.tunnelbroker.net'
  tunnel_path = '/ipv4_end.php'

  # Check external ip via this service.
  # If you want to change this, you must ensure that only the ip is returned from that
  # endpoint. e.g. "123.123.123.123\n"
  check_ip_host = 'ifconfig.me'
  check_ip_path = '/ip'

  # Set fake ip for comparison
  ip = "127.0.0.1"

  # check for actual file
  $stdout.puts "Checking for existing last-ip-file at #{LAST_IP_FILE}" if BE_VERBOSE
  if File.exist? LAST_IP_FILE
    last_ip_file = File.open(LAST_IP_FILE, 'r')
    ip = last_ip_file.lines.first.chomp
    $stdout.puts "Found one with IP: #{ip}" if BE_VERBOSE
    last_ip_file.close
  end
  $stdout.puts "Checking WAN IP" if BE_VERBOSE
  external_ip = Net::HTTP.get(check_ip_host, check_ip_path).chomp
  $stdout.puts "#{check_ip_host} tells us #{external_ip} as WAN IPv4" if BE_VERBOSE

  if ip == external_ip
    $stdout.puts 'IPs are equal, nothing to do. Exiting.' if BE_VERBOSE
    exit 0
  end

  # Save the last ip in a file
  $stdout.puts "Writing new IP to file" if BE_VERBOSE
  last_ip_file = File.open(LAST_IP_FILE, 'w')
  last_ip_file << external_ip
  last_ip_file.close

  # Do not reuse the external ip here. this could lead to a security flaw.
  # If you do not understand why, please do not change this anyway.
  $stdout.puts "Requesting tunnel endpoint to update your WAN IP" if BE_VERBOSE
  uri = URI.parse("https://#{tunnel_url}#{tunnel_path}")
  uri.query = URI.encode_www_form(tunnel_options)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  request = Net::HTTP::Get.new(uri.request_uri)
  update_response = http.request(request)

  unless update_response.is_a?(Net::HTTPSuccess)
    $stderr.puts "Request unsuccessful", update_response
    exit 1
  end

  update_result = update_response.body.chomp
  $stdout.puts "Recieved [#{update_result}]" if BE_VERBOSE
  $stderr.puts update_result if update_result.match /^-ERROR:/