Description: The IPv6 Link Local Algorithm in Haskell
Authored: 2021-10-11;
Permalink: https://adamflott.com/programming/haskell/convert-mac-to-ipv6-link-local/
tags :
haskell;
ipv6;
networking;
categories :
programming;
The OS should take care of this for you, but if you needed the algorithm in
Haskell, here is how to make a IPv6 link local address from a MAC address.
Requires the relude
and ip
packages. Although
it's trivial to drop the Relude
requirement in favor of Prelude
.
References:
Online Converters:
-- prelude
import Relude
-- base
import Data.Bits
-- Hackage
import Net.IPv6
import Net.Mac
-- | Try and convert a MAC address to an IPv6 link local address
--
-- For example
--
-- @00:1c:a1:40:00:1f@ -> @fe80::21c:a1ff:fe40:1f@
mkIPV6LinkLocalAddress :: Text -> Maybe IPv6
mkIPV6LinkLocalAddress m = do
vm_mac <- Net.Mac.decode m
let (o1, o2, o3, o6, o7, o8) = Net.Mac.toOctets vm_mac
o1_inverted <- invertAt 6 (toBinary o1)
let o1' = toDecimal o1_inverted
s5 = toWord16 (fromIntegral o1') (fromIntegral o2)
s6 = toWord16 (fromIntegral o3) 0xff
s7 = toWord16 0xfe (fromIntegral o6)
s8 = toWord16 (fromIntegral o7) (fromIntegral o8)
Just (Net.IPv6.fromWord16s 0xfe80 0x0 0x0 0x0 s5 s6 s7 s8)
where
toWord16 :: Word16 -> Word16 -> Word16
toWord16 hi lo =
let hi' = Data.Bits.shift hi 8
lo' = (.&.) lo 0xff
in (.|.) hi' lo'
toDecimal :: Foldable t => t Word8 -> Word8
toDecimal n = snd (foldr f (0, 0) n)
where
f :: Word8 -> (Word8, Word8) -> (Word8, Word8)
f a (i, b) = (i + 1, b + (a * (2 ^ i)))
invertAt :: Int -> [Word8] -> Maybe [Word8]
invertAt i n = case n !!? i of
Nothing -> Nothing
Just e -> do
let (start, end) = splitAt i n
case viaNonEmpty tail end of
Nothing -> Nothing
Just end_dropped -> Just (start ++ [invert e] ++ end_dropped)
where
invert 0 = 1
invert 1 = 0
invert i' = i'
toBinary :: Integral a => a -> [a]
toBinary 0 = replicate 8 0
toBinary n = pad (reverse (helper n))
where
pad n' = replicate (8 - length n') 0 ++ n'
helper 0 = []
helper n' = let (q, r) = n' `divMod` 2 in r : helper q