Merge "ledpattern: Initial implementation of LED tap codes"
diff --git a/cmds/Makefile b/cmds/Makefile
index 3d53395..24bc8a1 100644
--- a/cmds/Makefile
+++ b/cmds/Makefile
@@ -19,6 +19,7 @@
 TARGETS=\
 	$(PORTABLE_TARGETS) \
 	alivemonitor \
+	anonid \
 	bsa2bluez \
 	burnin-flash \
 	buttonmon \
@@ -31,6 +32,7 @@
 	diskbench \
 	dnsck \
 	freemegs \
+	gfhd254_reboot \
 	gstatic \
 	http_bouncer \
 	ionice \
@@ -255,6 +257,9 @@
 		--includes --output-file=$@ $<
 hostnamelookup.tmp.o: CFLAGS += -Wno-missing-field-initializers
 host-hostnamelookup.tmp.o: CFLAGS += -Wno-missing-field-initializers
+anonid: anonid.o
+host-anonid: host-anonid.o
+anonid host-anonid: LIBS += -lcrypto
 
 
 TESTS = $(wildcard test-*.sh) $(wildcard test-*.py) $(wildcard *_test.py) $(TEST_TARGETS)
diff --git a/cmds/anonid.c b/cmds/anonid.c
new file mode 100644
index 0000000..e7854ad
--- /dev/null
+++ b/cmds/anonid.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2015 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <openssl/md5.h>
+#include <openssl/hmac.h>
+
+
+const char SOFT[] = "AEIOUY" "V";
+const char HARD[] = "BCDFGHJKLMNPQRSTVWXYZ" "AEIOU";
+const char *consensus_key_file = "/tmp/waveguide/consensus_key";
+#define CONSENSUS_KEY_LEN 16
+uint8_t consensus_key[CONSENSUS_KEY_LEN] = {0};
+#define MAC_ADDR_LEN 17
+
+void default_consensus_key()
+{
+  int fd;
+
+  if ((fd = open("/dev/urandom", O_RDONLY)) >= 0) {
+    ssize_t siz = sizeof(consensus_key);
+    if (read(fd, consensus_key, siz) != siz) {
+      /* https://xkcd.com/221/ */
+      memset(consensus_key, time(NULL), siz);
+    }
+    close(fd);
+  }
+}
+
+/* Read the waveguide consensus_key, if any. Returns 0 if
+ * a key was present, 1 if not or something fails. */
+int get_consensus_key()
+{
+  int fd, rc = 1;
+  uint8_t new_key[sizeof(consensus_key)];
+
+  if ((fd = open(consensus_key_file, O_RDONLY)) < 0) {
+    return 1;
+  }
+
+  if (read(fd, new_key, sizeof(new_key)) == sizeof(new_key)) {
+    memcpy(consensus_key, new_key, sizeof(consensus_key));
+    rc = 0;
+  }
+  close(fd);
+
+  return rc;
+}
+
+/* Given a value from 0..4095, encode it as a cons+vowel+cons sequence. */
+void trigraph(int num, char *out)
+{
+  int ns = sizeof(SOFT) - 1;
+  int nh = sizeof(HARD) - 1;
+  int c1, c2, c3;
+
+  c3 = num % nh;
+  c2 = (num / nh) % ns;
+  c1 = num / nh / ns;
+  out[0] = HARD[c1];
+  out[1] = SOFT[c2];
+  out[2] = HARD[c3];
+}
+
+int hex_chr_to_int(char hex) {
+  switch(hex) {
+    case '0' ... '9':
+      return hex - '0';
+    case 'a' ... 'f':
+      return hex - 'a' + 10;
+    case 'A' ... 'F':
+      return hex - 'A' + 10;
+  }
+
+  return 0;
+}
+
+/*
+ * Convert a string of the form "00:11:22:33:44:55" to
+ * a binary array 001122334455.
+ */
+void get_binary_mac(const char *mac, uint8_t *out) {
+  int i;
+  for (i = 0; i < MAC_ADDR_LEN; i += 3) {
+    *out = (hex_chr_to_int(mac[i]) << 4) | hex_chr_to_int(mac[i+1]);
+    out++;
+  }
+}
+
+
+void get_anonid_for_mac(const char *mac, char *out) {
+  unsigned char digest[EVP_MAX_MD_SIZE];
+  unsigned int digest_len = sizeof(digest);
+  uint8_t macbin[6];
+  uint32_t num;
+
+  get_binary_mac(mac, macbin);
+  HMAC(EVP_md5(), consensus_key, CONSENSUS_KEY_LEN, macbin, sizeof(macbin),
+      digest, &digest_len);
+  num = (digest[0] << 16) | (digest[1] << 8) | digest[2];
+  trigraph((num >> 12) & 0x0fff, out);
+  trigraph((num      ) & 0x0fff, out + 3);
+}
+
+
+void usage(const char *progname)
+{
+  fprintf(stderr, "usage: %s: -a ##:##:##:##:##:## [-k consensus_key]\n",
+      progname);
+  fprintf(stderr, "\t-a addr: MAC address to generate an anonid for\n");
+  fprintf(stderr, "\t-k key: Use a specific consensus_key. "
+      "Default is to read it from %s\n", consensus_key_file);
+  exit(1);
+}
+
+
+int main(int argc, char **argv)
+{
+  struct option long_options[] = {
+    {"addr",          required_argument, 0, 'a'},
+    {"consensus_key", required_argument, 0, 'k'},
+    {0,          0,                 0, 0},
+  };
+  const char *addr = NULL;
+  char anonid[7];
+  size_t lim;
+  int c;
+
+  setlinebuf(stdout);
+  alarm(30);
+
+  if (get_consensus_key()) {
+    default_consensus_key();
+  }
+
+  while ((c = getopt_long(argc, argv, "a:k:", long_options, NULL)) != -1) {
+    switch (c) {
+    case 'a':
+      addr = optarg;
+      break;
+    case 'k':
+      lim = (sizeof(consensus_key) > strlen(optarg)) ? strlen(optarg) :
+        sizeof(consensus_key);
+      memset(consensus_key, 0, sizeof(consensus_key));
+      memcpy(consensus_key, optarg, lim);
+      break;
+    default:
+      usage(argv[0]);
+      break;
+    }
+  }
+
+  if (addr == NULL) {
+    usage(argv[0]);
+  }
+
+  get_anonid_for_mac(addr, anonid);
+  printf("%s\n", anonid);
+
+  exit(0);
+}
diff --git a/cmds/gfhd254_reboot.c b/cmds/gfhd254_reboot.c
new file mode 100644
index 0000000..fdc7e32
--- /dev/null
+++ b/cmds/gfhd254_reboot.c
@@ -0,0 +1,65 @@
+// GFHD254 has a bug where software reset doesn't reset the entire
+// chip, some state in the SAGE engine isn't getting reset.  This
+// drives a gpio that connects back to the chips own external reset
+// pin, resetting the chip with this pin works around the issue as
+// the SAGE engine is completely reset in this path.
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define REG_BASE 0xf0410000
+#define REG_SIZE 0x8000
+
+
+#define GPIO_DATA (0x7404 / 4)
+#define GPIO_IODIR (0x7408 / 4)
+#define CTRL_MUX_0 (0x0700 / 4)
+#define CTRL_MUX_1 (0x0704 / 4)
+
+static void *mmap_(
+    void* addr, size_t size, int prot, int flags, int fd,
+    off_t offset) {
+#ifdef __ANDROID__
+  return mmap64(addr, size, prot, flags, fd,
+                (off64_t)(uint64_t)(uint32_t)offset);
+#else
+  return mmap(addr, size, prot, flags, fd, offset);
+#endif
+}
+
+// TODO(jnewlin):  Revist this after the exact gpio being used
+// is settled on.
+
+int main() {
+  int fd = open("/dev/mem", O_RDWR);
+  volatile uint32_t* reg;
+
+  if (fd < 0) {
+    perror("mmap");
+    return 1;
+  }
+
+  reg = mmap_(NULL, REG_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
+              fd, REG_BASE);
+  if (reg == MAP_FAILED) {
+    perror("mmap");
+    return 1;
+  }
+
+  // Set the pin mux to gpio, value of zero selects gpio mode, this
+  // is the reset value so this is probably not required, but just
+  // in case.
+  reg[CTRL_MUX_0] &= ~((0xf << 8) | (0xf << 12)); // aon_gio2 and 3
+  reg[CTRL_MUX_1] &= ~(0xf << 4); // aon_gio9
+
+
+  // Set the direction to be an output and drive it low.
+  reg[GPIO_IODIR] &= ~((1 << 2) | (1 << 3) | (1 << 9));
+  reg[GPIO_DATA] &= ~((1 << 2) | (1 << 3) | (1 << 9));
+
+  return 0;
+}
diff --git a/cmds/logos.c b/cmds/logos.c
index 93577ce..57a6d02 100644
--- a/cmds/logos.c
+++ b/cmds/logos.c
@@ -25,7 +25,6 @@
  *  - cleans up control characters (ie. chars < 32).
  *  - makes sure output lines are in "facility: message" format.
  *  - doesn't rely on syslogd.
- *  - suppresses logging of filenames of personal media.
  */
 #include <assert.h>
 #include <ctype.h>
@@ -460,48 +459,6 @@
 }
 
 
-/*
- * Return true for a character which we expect to terminate a
- * media filename.
- */
-static int is_filename_terminator(char c) {
-  switch(c) {
-    case ' ':
-    case '\'':
-    case '"':
-      return 1;
-  }
-
-  return 0;
-}
-
-/*
- * search for text patterns which look like filenames of
- * personal media, and cross out the filename portion with
- * 'X' characters.
- */
-static void suppress_media_filenames(uint8_t *line, ssize_t len,
-                                     const char *path) {
-  uint8_t *s = line;
-  ssize_t pathlen = strlen(path);
-
-  while (len > pathlen) {
-    if (strncmp((char *)s, path, pathlen) == 0) {
-      /* Found a filename, blot it out. */
-      s += pathlen;
-      len -= pathlen;
-      while (len > 0 && !is_filename_terminator(*s)) {
-        *s++ = 'X';
-        len--;
-      }
-    } else {
-      s += 1;
-      len -= 1;
-    }
-  }
-}
-
-
 static void usage(void) {
   fprintf(stderr,
       "Usage: [LOGOS_DEBUG=1] logos <facilityname> [bytes/burst] [bytes/day]\n"
@@ -657,8 +614,6 @@
       uint8_t *start = buf, *next = buf + used, *end = buf + used + got, *p;
       while ((p = memchr(next, '\n', end - next)) != NULL) {
         ssize_t linelen = p - start;
-        suppress_media_filenames(start, linelen, "/var/media/pictures/");
-        suppress_media_filenames(start, linelen, "/var/media/videos/");
         flush(header, headerlen, start, linelen);
         if (overlong) {
           // that flush() was the first newline after buffer length
diff --git a/cmds/test-anonid.sh b/cmds/test-anonid.sh
new file mode 100755
index 0000000..87f2b0f
--- /dev/null
+++ b/cmds/test-anonid.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+. ./wvtest/wvtest.sh
+
+WVSTART "anonid test"
+ANONID="./host-anonid"
+
+WVPASSEQ "$($ANONID -a 00:11:22:33:44:55 -k 0123456789)" "KEALAE"
+WVPASSEQ "$($ANONID -a 00:11:22:33:44:66 -k 6789abcdef)" "AAKLYK"
diff --git a/cmds/test-http_bouncer.sh b/cmds/test-http_bouncer.sh
index 4129d52..9cc0d9e 100755
--- a/cmds/test-http_bouncer.sh
+++ b/cmds/test-http_bouncer.sh
@@ -40,10 +40,6 @@
 INPUTS[3]=$(printf "\n\n"; printf "$SENTINEL")
 OUTPUTS[3]=$(printf "HTTP/1.0 302 Found\r\nLocation: $URL\r\n\r\n"; printf "$SENTINEL")
 
-INPUTS[4]=$(printf "GET /GIAG2.crl HTTP/1.0\r\nHost: pki.google.com\r\n\r\n"; printf "$SENTINEL")
-OUTPUTS[4]=$(curl "http://pki.google.com/GIAG2.crl"; printf "$SENTINEL")
-STRIP_HEADER[4]=1
-
 WVSTART "http_bouncer test"
 
 # fail with no arguments
@@ -59,10 +55,13 @@
 i=0
 while [ $i -lt ${#INPUTS[@]} ]; do
   output=$(echo -n "${INPUTS[$i]}" | nc localhost $PORT; printf "$SENTINEL")
-  if [ ${STRIP_HEADER[$i]} ]; then
-    output=$(echo -n "$output" | sed '1,/^\r$/d')
-  fi
-
   WVPASSEQ "$output" "${OUTPUTS[$i]}"
   i=$(expr $i + 1)
 done
+
+# Make sure we can download a CRL even through the bouncer.
+# Some Internet Explorer versions will refuse to connect if we can't.
+WVPASS printf "GET /GIAG2.crl HTTP/1.0\r\nHost: pki.google.com\r\n\r\n" |\
+  nc localhost $PORT |\
+  sed '1,/^\r$/d' |\
+  openssl crl -inform DER
diff --git a/cmds/test-logos.py b/cmds/test-logos.py
index d930ccb..34f5693 100755
--- a/cmds/test-logos.py
+++ b/cmds/test-logos.py
@@ -86,23 +86,6 @@
   os.write(fd1, '\n')
   WVPASSEQ('<7>fac: booga!\n', _Read())
 
-  # Filenames
-  os.write(fd1, 'Accessing /var/media/pictures/MyPicture.jpg for decode\n')
-  WVPASSEQ('<7>fac: Accessing /var/media/pictures/XXXXXXXXXXXXX for decode\n',
-           _Read())
-  os.write(fd1, '/var/media/pictures/MyPicture.jpg\n')
-  WVPASSEQ('<7>fac: /var/media/pictures/XXXXXXXXXXXXX\n',
-           _Read())
-  os.write(fd1, 'Accessing /var/media/videos/MyMovie.mpg for decode\n')
-  WVPASSEQ('<7>fac: Accessing /var/media/videos/XXXXXXXXXXX for decode\n',
-           _Read())
-  os.write(fd1, 'Accessing /var/media/tv/MyTvShow.ts for decode\n')
-  WVPASSEQ('<7>fac: Accessing /var/media/tv/MyTvShow.ts for decode\n',
-           _Read())
-  os.write(fd1, 'check "/var/media/videos/MyTvShow.ts"len=1024\n')
-  WVPASSEQ('<7>fac: check "/var/media/videos/XXXXXXXXXXX"len=1024\n',
-           _Read())
-
   # rate limiting
   os.write(fd1, (('x'*80) + '\n') * 500)
   result = ''
diff --git a/hnvram/hnvram_main.c b/hnvram/hnvram_main.c
index b850048..323d62e 100644
--- a/hnvram/hnvram_main.c
+++ b/hnvram/hnvram_main.c
@@ -83,6 +83,7 @@
   {"LASER_CHANNEL",        NVRAM_FIELD_LASER_CHANNEL,     HNVRAM_STRING},
   {"MAC_ADDR_PON",         NVRAM_FIELD_MAC_ADDR_PON,      HNVRAM_MAC},
   {"PRODUCTION_UNIT",      NVRAM_FIELD_PRODUCTION_UNIT,   HNVRAM_STRING},
+  {"BOOT_TARGET",          NVRAM_FIELD_BOOT_TARGET,       HNVRAM_STRING},
 };
 
 const hnvram_field_t* get_nvram_field(const char* name) {
diff --git a/waveguide/log.py b/waveguide/log.py
index af05667..cf9ccb6 100644
--- a/waveguide/log.py
+++ b/waveguide/log.py
@@ -17,15 +17,11 @@
 """Helper functions for logging."""
 
 import errno
-import hmac
 import os
-import struct
 import sys
-import helpers
 
 
 LOGLEVEL = 0
-ANONYMIZE = True
 STATUS_DIR = None
 
 
@@ -47,62 +43,6 @@
     Log(s, *args)
 
 
-SOFT = 'AEIOUY' 'V'
-HARD = 'BCDFGHJKLMNPQRSTVWXYZ' 'AEIOU'
-
-
-def Trigraph(num):
-  """Given a value from 0..4095, encode it as a cons+vowel+cons sequence."""
-  ns = len(SOFT)
-  nh = len(HARD)
-  assert nh * ns * nh >= 4096
-  c3 = num % nh
-  c2 = (num / nh) % ns
-  c1 = num / nh / ns
-  return HARD[c1] + SOFT[c2] + HARD[c3]
-
-
-def WordFromBinary(s):
-  """Encode a binary blob into a string of pronounceable syllables."""
-  out = []
-  while s:
-    part = s[:3]
-    s = s[3:]
-    while len(part) < 4:
-      part = '\0' + part
-    bits = struct.unpack('!I', part)[0]
-    out += [(bits >> 12) & 0xfff,
-            (bits >> 0)  & 0xfff]
-  return ''.join(Trigraph(i) for i in out)
-
-
-# Note(apenwarr): There are a few ways to do this.  I elected to go with
-# short human-usable strings (allowing for the small possibility of
-# collisions) since the log messages will probably be "mostly" used by
-# humans.
-#
-# An alternative would be to use "format preserving encryption" (basically
-# a secure 1:1 mapping of unencrypted to anonymized, in the same number of
-# bits) and then produce longer "words" with no possibility of collision.
-# But with our current WordFromBinary() implementation, that would be
-# 12 characters long, which is kind of inconvenient and we probably don't
-# need that level of care.  Inside waveguide we use the real MAC addresses
-# so collisions won't cause a real problem.
-#
-# TODO(apenwarr): consider not anonymizing the OUI.
-#   That way we could see any behaviour differences between vendors.
-#   Sadly, that might make it too easy to brute force a MAC address back out;
-#   the remaining 3 bytes have too little entropy.
-#
-def AnonymizeMAC(consensus_key, macbin):
-  """Anonymize a binary MAC address using the given key."""
-  assert len(macbin) == 6
-  if consensus_key and ANONYMIZE:
-    return WordFromBinary(hmac.new(consensus_key, macbin).digest())[:6]
-  else:
-    return helpers.DecodeMAC(macbin)
-
-
 def WriteEventFile(name):
   """Create a file in STATUS_DIR if it does not already exist.
 
diff --git a/waveguide/log_test.py b/waveguide/log_test.py
deleted file mode 100755
index fabc09f..0000000
--- a/waveguide/log_test.py
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/python
-import log
-from wvtest import wvtest
-
-
-@wvtest.wvtest
-def AnonTest():
-  m1 = '\x01\x02\x03\x04\x05\x06'
-  m2 = '\x31\x32\x33\x34\x35\x36'
-
-  s1 = log.AnonymizeMAC(None, m1)
-  s2 = log.AnonymizeMAC(None, m2)
-  a1a = log.AnonymizeMAC('key', m1)
-  a2a = log.AnonymizeMAC('key', m2)
-  a1b = log.AnonymizeMAC('key2', m1)
-  a2b = log.AnonymizeMAC('key2', m2)
-
-  # make sure they're printable strings
-  wvtest.WVPASSEQ(s1, str(s1))
-  wvtest.WVPASSEQ(a1a, str(a1a))
-  wvtest.WVPASSEQ(a1b, str(a1b))
-
-  # and reasonably sized
-  wvtest.WVPASSLE(len(a1a), 8)
-
-  # and change when the key or MAC changes
-  wvtest.WVPASSNE(s1, s2)
-  wvtest.WVPASSNE(a1a, a1b)
-  wvtest.WVPASSNE(a2a, a2b)
-  wvtest.WVPASSNE(a1a, a2a)
-  wvtest.WVPASSNE(a1b, a2b)
-
-
-if __name__ == '__main__':
-  wvtest.wvtest_main()
diff --git a/waveguide/waveguide.py b/waveguide/waveguide.py
index 87324c3..594f83f 100755
--- a/waveguide/waveguide.py
+++ b/waveguide/waveguide.py
@@ -58,8 +58,7 @@
 tx-interval=      Seconds between state transmits (0 to disable) [15]
 autochan-interval= Seconds between autochannel decisions (0 to disable) [300]
 print-interval=   Seconds between state printouts to log (0 to disable) [16]
-D,debug           Increase (non-anonymized!) debug output level
-no-anonymize      Don't anonymize MAC addresses in logs
+D,debug           Increase debug output level
 status-dir=       Directory to store status information [/tmp/waveguide]
 watch-pid=        Shut down if the given process pid disappears
 auto-disable-threshold=  Shut down if >= RSSI received from other AP [-30]
@@ -240,11 +239,8 @@
   def Filename(self, suffix):
     return os.path.join(opt.status_dir, '%s.%s' % (self.vdevname, suffix))
 
-  def AnonymizeMAC(self, mac):
-    return log.AnonymizeMAC(consensus_key, mac)
-
   def _LogPrefix(self):
-    return '%s(%s): ' % (self.vdevname, self.AnonymizeMAC(self.mac))
+    return '%s(%s): ' % (self.vdevname, helpers.DecodeMAC(self.mac))
 
   def Log(self, s, *args):
     log.Log(self._LogPrefix() + s, *args)
@@ -291,7 +287,7 @@
       self.Debug('ignoring peer due to key mismatch')
       return 0
     if p.me.mac not in self.peer_list:
-      self.Log('added a peer: %s', self.AnonymizeMAC(p.me.mac))
+      self.Log('added a peer: %s', helpers.DecodeMAC(p.me.mac))
     self.peer_list[p.me.mac] = p
     self.MaybeAutoDisable()
     return 1
@@ -445,7 +441,7 @@
       return None
     for peer in sorted(self.peer_list.values(), key=lambda p: p.me.mac):
       self.Debug('considering auto disable: peer=%s',
-                 self.AnonymizeMAC(peer.me.mac))
+                 helpers.DecodeMAC(peer.me.mac))
       if peer.me.mac not in self.bss_list:
         self.Debug('--> peer no match')
       else:
@@ -478,11 +474,11 @@
     """Writes/removes the auto-disable file based on ShouldAutoDisable()."""
     ad = self.ShouldAutoDisable()
     if ad and self.auto_disabled != ad:
-      self.Log('auto-disabling because of %s', self.AnonymizeMAC(ad))
+      self.Log('auto-disabling because of %s', helpers.DecodeMAC(ad))
       helpers.WriteFileAtomic(self.Filename('disabled'), helpers.DecodeMAC(ad))
     elif self.auto_disabled and not ad:
       self.Log('auto-enabling because %s disappeared',
-               self.AnonymizeMAC(self.auto_disabled))
+               helpers.DecodeMAC(self.auto_disabled))
       helpers.Unlink(self.Filename('disabled'))
     self.auto_disabled = ad
 
@@ -960,17 +956,12 @@
       helpers.WriteFileAtomic(os.path.join(WIFIBLASTER_DIR, g.group()),
                               '%d %s' % (time.time(), line))
 
-  def _AnonymizeResult(self, line):
-    def Repl(match):
-      return log.AnonymizeMAC(consensus_key, helpers.EncodeMAC(match.group()))
-    return re.sub(MACADDR_REGEX, Repl, line)
-
   def _HandleResults(self, errcode, stdout, stderr):
     """Callback for 'wifiblaster' results."""
     log.Debug('wifiblaster err:%r stdout:%r stderr:%r', errcode, stdout[:70],
               stderr)
     for line in stdout.splitlines():
-      log.Log('wifiblaster: %s' % self._AnonymizeResult(line))
+      log.Log('wifiblaster: %s' % line)
       self._SaveResult(line)
 
   def _StrToBool(self, s):
@@ -1090,7 +1081,6 @@
   if opt.watch_pid and opt.watch_pid <= 1:
     o.fatal('--watch-pid must be empty or > 1')
   log.LOGLEVEL = opt.debug
-  log.ANONYMIZE = opt.anonymize
   log.STATUS_DIR = opt.status_dir
 
   try:
@@ -1232,11 +1222,11 @@
         self_signals[m.mac] = bss_signal
         peer_data[m.mac] = seen_peers
         log.Log('%s: APs=%-4d peer-APs=%s stations=%s',
-                m.AnonymizeMAC(p.me.mac), len(p.seen_bss),
-                ','.join('%s(%d)' % (m.AnonymizeMAC(i.mac), i.rssi)
+                helpers.DecodeMAC(p.me.mac), len(p.seen_bss),
+                ','.join('%s(%d)' % (helpers.DecodeMAC(i.mac), i.rssi)
                          for i in sorted(seen_bss_peers,
                                          key=lambda i: -i.rssi)),
-                ','.join('%s(%d)' % (m.AnonymizeMAC(i.mac), i.rssi)
+                ','.join('%s(%d)' % (helpers.DecodeMAC(i.mac), i.rssi)
                          for i in sorted(p.assoc,
                                          key=lambda i: -i.rssi)))
 
@@ -1251,7 +1241,7 @@
       can2G_count = can5G_count = 0
       for m in managers:
         for assoc in m.assoc_list.itervalues():
-          anon = m.AnonymizeMAC(assoc.mac)
+          station = helpers.DecodeMAC(assoc.mac)
           if log_sta_band_capabilities:
             if assoc.can5G:
               can5G_count += 1
@@ -1259,11 +1249,10 @@
             else:
               can2G_count += 1
               capability = '2.4'
-            log.Log('Connected station %s supports %s GHz', anon, capability)
-          station = helpers.DecodeMAC(assoc.mac)
+            log.Log('Connected station %s supports %s GHz', station, capability)
           species = clientinfo.taxonomize(station)
           if species:
-            log.Log('Connected station %s taxonomy: %s' % (anon, species))
+            log.Log('Connected station %s taxonomy: %s', station, species)
       if log_sta_band_capabilities:
         log.Log('Connected stations: total %d, 5 GHz %d, 2.4 GHz %d',
                 can5G_count + can2G_count, can5G_count, can2G_count)
diff --git a/waveguide/wifiblaster_controller_test.py b/waveguide/wifiblaster_controller_test.py
index 9e300f2..12ff480 100755
--- a/waveguide/wifiblaster_controller_test.py
+++ b/waveguide/wifiblaster_controller_test.py
@@ -71,13 +71,6 @@
     stdout = ('version=1 mac=11:11:11:11:11:11 throughput=10000000 '
               'samples=5000000,15000000\n'
               'malformed 11:11:11:11:11:11 but has macs 11:11:11:11:11:11\n')
-
-    result = wc._AnonymizeResult(stdout)
-    expected = ('version=1 mac=CYAFVU throughput=10000000 '
-                'samples=5000000,15000000\n'
-                'malformed CYAFVU but has macs CYAFVU\n')
-    wvtest.WVPASSEQ(result, expected)
-
     expected = [('version=1 mac=11:11:11:11:11:11 throughput=10000000 '
                  'samples=5000000,15000000'),
                 'malformed 11:11:11:11:11:11 but has macs 11:11:11:11:11:11']