declare local var.ip IP;
declare local var.anon_ip STRING;
declare local var.ip_str STRING;

set var.ip = req.http.Fastly-Client-Ip;

// Uncomment this line to use an IPv6 address for testing
//set var.ip = "2a04:4e42:400::c0ff:ee:cafe";

if (addr.is_ipv4(var.ip)) {
  # IPv4 is simple, just zero out the last octet.
  set var.anon_ip = regsub(var.ip, "\d+$", "0");

} else if (addr.is_ipv6(var.ip)) {
  # To anonymize an IPv6 IP, we want to retain the first 48 bits, which
  # is the first 3 groups of hex numbers. However, they need not all
  # exist. Regular expressions cannot easily make sense of all the
  # variations these may take, but we can rely on the normalizing that
  # will happen to a real IPv6 to catch the unlikely corner cases...

  # Convert the IP to a normalized string so that it can be regexp'd...
  set var.ip_str = var.ip;

  # This will catch nearly every IPv6 address seen in the wild.
  # "1:2:3:..." or "1:2::..."
  if ( var.ip_str ~ "(?i)^((?:(?:^|:)[0-9a-f]{1,4}){2,3}):" ) {
    set var.anon_ip = re.group.1 + "::";

  # pathological...
  # "1::3:4:5:6:7:8"
  } elseif ( var.ip_str ~ "(?i)^(?:([0-9a-f]{1,4}))::([0-9a-f]{1,4}):" ) {
    set var.anon_ip = re.group.1 + ":0:" + re.group.2 + "::";

  # even more unlikely...
  # "::2:3:4:5:6:7:8" or "::3:4:5:6:7:8"
  } elseif ( var.ip_str ~ "(?i)::([0-9a-f]{1,4}:)?([0-9a-f]{1,4})(?::[0-9a-f]{1,4}){5}$" ) {
    set var.anon_ip = "0:" + if(re.group.1, re.group.1, "0:") + re.group.2 + "::";

  # things which really shouldn't happen,
  # "::7:8:9", etc.
  } else {
    set var.anon_ip = "::";
  }
}

log "IP " + var.ip + " anonymized to " + var.anon_ip;