diff --git a/hw-ip.cabal b/hw-ip.cabal
--- a/hw-ip.cabal
+++ b/hw-ip.cabal
@@ -1,6 +1,6 @@
 cabal-version: 1.12
 name: hw-ip
-version: 2.1.0.0
+version: 2.2.0.0
 license: BSD3
 license-file: LICENSE
 copyright: 2018 John Ky, David Turnbull, Jian Wan
diff --git a/src/HaskellWorks/Data/Network/Ip/Ipv6.hs b/src/HaskellWorks/Data/Network/Ip/Ipv6.hs
--- a/src/HaskellWorks/Data/Network/Ip/Ipv6.hs
+++ b/src/HaskellWorks/Data/Network/Ip/Ipv6.hs
@@ -25,6 +25,7 @@
   , rangeToBlocksDL
   , blockToRange
   , isCanonical
+  , canonicaliseIpBlock
   , splitIpRange
   ) where
 
@@ -144,10 +145,9 @@
       [0, 0, 0, 0]
 
 isCanonical :: IpBlock v -> Bool
-isCanonical (IpBlock (IpAddress w) (IpNetMask m)) =
-  let lt = masksIp m
-      ipv6 = I.word32x4ToWords w in
-    ipv6 == zipWith (B..&.) ipv6 (zipWith B.xor ipv6 lt)
+isCanonical block@(IpBlock (IpAddress w) (IpNetMask m)) =
+  let IpBlock (IpAddress cw) (IpNetMask cm) = canonicaliseIpBlock block
+  in cw == w && cm == m
 
 {-# DEPRECATED fromV4 "Deprecated due to poor naming. Use fromIpv4Block instead." #-}
 fromV4 :: V4.IpBlock Canonical -> IpBlock v
@@ -160,6 +160,16 @@
 
 fromIpv4 :: V4.IpAddress -> IpAddress
 fromIpv4 (V4.IpAddress w32) = IpAddress (0, 0, 0xFFFF, w32)
+
+canonicaliseIpBlock :: IpBlock v -> IpBlock Canonical
+canonicaliseIpBlock (IpBlock (IpAddress w) (IpNetMask m))
+  = case zipWith (B..&.) ipv6 (zipWith B.xor ipv6 masks) of
+    [nw1,nw2,nw3,nw4] -> IpBlock (IpAddress (nw1, nw2, nw3, nw4)) (IpNetMask m)
+    _                 -> error "Very mal-formed IPv6. This should never happen."
+  where
+    masks = masksIp m
+    ipv6 = I.word32x4ToWords w
+
 
 firstIpAddress :: IpBlock Canonical -> IpAddress
 firstIpAddress (IpBlock b _) = b
diff --git a/test/HaskellWorks/Data/Network/Ipv6Spec.hs b/test/HaskellWorks/Data/Network/Ipv6Spec.hs
--- a/test/HaskellWorks/Data/Network/Ipv6Spec.hs
+++ b/test/HaskellWorks/Data/Network/Ipv6Spec.hs
@@ -78,6 +78,23 @@
       V6.splitIpRange (IR.Range (read "::3") (read "::88")) === (V6.IpBlock (V6.IpAddress (0, 0, 0, 3)) (V6.IpNetMask 128), Just (IR.Range (read "::4") (read "::88")))
       V6.splitIpRange (IR.Range (read "::127") (read "::129")) === (V6.IpBlock (V6.IpAddress (0, 0, 0, 0x127)) (V6.IpNetMask 128), Just (IR.Range (read "::128") (read "::129")))
 
+    it "should canonicalise block" $ requireTest $ do
+      let ipv4ify w32 block = V6.IpBlock (V6.IpAddress (0x00000000, 0x00000000, 0x0000ffff, w32)) (V6.IpNetMask $ block + 96)
+      -- These are the same tests we run for IPv4
+      V6.canonicaliseIpBlock (ipv4ify 0x01020304 32) === ipv4ify 0x01020304 32
+      V6.canonicaliseIpBlock (ipv4ify 0x01020304 24) === ipv4ify 0x01020300 24
+      V6.canonicaliseIpBlock (ipv4ify 0x01020304 16) === ipv4ify 0x01020000 16
+      V6.canonicaliseIpBlock (ipv4ify 0x01020304  8) === ipv4ify 0x01000000  8
+
+      -- Some IPv6 specific tests.
+      let ipv6 = V6.IpAddress (0xdeadbeef, 0xfeedface, 0xcafebabe, 0xbaadc0de)
+      V6.canonicaliseIpBlock (V6.IpBlock ipv6 (V6.IpNetMask 128)) === V6.IpBlock ipv6                                                            (V6.IpNetMask 128)
+      V6.canonicaliseIpBlock (V6.IpBlock ipv6 (V6.IpNetMask 112)) === V6.IpBlock (V6.IpAddress (0xdeadbeef, 0xfeedface, 0xcafebabe, 0xbaad0000)) (V6.IpNetMask 112)
+      V6.canonicaliseIpBlock (V6.IpBlock ipv6 (V6.IpNetMask  80)) === V6.IpBlock (V6.IpAddress (0xdeadbeef, 0xfeedface, 0xcafe0000, 0x00000000)) (V6.IpNetMask  80)
+      V6.canonicaliseIpBlock (V6.IpBlock ipv6 (V6.IpNetMask  48)) === V6.IpBlock (V6.IpAddress (0xdeadbeef, 0xfeed0000, 0x00000000, 0x00000000)) (V6.IpNetMask  48)
+      V6.canonicaliseIpBlock (V6.IpBlock ipv6 (V6.IpNetMask  16)) === V6.IpBlock (V6.IpAddress (0xdead0000, 0x00000000, 0x00000000, 0x00000000)) (V6.IpNetMask  16)
+      V6.canonicaliseIpBlock (V6.IpBlock ipv6 (V6.IpNetMask   0)) === V6.IpBlock (V6.IpAddress (0x00000000, 0x00000000, 0x00000000, 0x00000000)) (V6.IpNetMask   0)
+
   describe "should get blocks from ranges" $ do
     it ":: - ::ff" $ requireTest $ do
       V6.rangeToBlocks (R.Range (V6.IpAddress 0) (V6.IpAddress 0xff)) === [ V6.IpBlock (V6.IpAddress 0) (V6.IpNetMask 120)]
