Calculating IP addresses in TeX

Just testing this blog, why not writing a post as an exercise. 🙂

In big projects I have hundreds of sub networks. There’s a lot of small transport networks with small subnet masks, that partition a summarizing net, and there are gateways. All these data is part of documentation and drawings. When I have a second project of the same kind, I can re-use documentation and drawings, just based on another IP address range. Will I calculate all again, typing the new IP addresses into each place at the drawings and docs? No, I like to automate it. TeX can do it.

You all know, what an IP address is:

500px-ipv4_address-svg

I chose Lua for calculating things, and LuaLaTeX compiles my documents. I will post a simplified example here.

On the Internet, I found a nice snippet ipv4.lua by Nick Barret, so I did not have to program all myself. How to use it? Here are basic steps for a possible implementation:

  • Add \usepackage{luacode} to your preamble
  • Get some Lua code (google for <keyword> and Lua) or program yourself
  • Use the code as argument of a \luaexec command
  • Modify the Lua code a bit:
    • remove Lua comments or change them (incompatible syntax)
    • quote % by a backslash: \%
    • quote backslashes, such as here: tex.sprint( "\\def\\ipDevice{#1}" )
  • Instead of print, use tex.sprint for handing over things to TeX

A compilable demo:

\documentclass{article}
\usepackage{luacode}
\newcommand{\ip
}[2]{%
\luaexec{
local ip1= string.gsub("#1", "\%.(\%d+).(\%d+).(\%d+)","")
local ip2= string.gsub("#1", "(\%d+)\%.","",1)
local ip2= string.gsub(ip2, "\%.(\%d+).(\%d+)","")
local ip3= string.gsub("#1", "(\%d+)\%.(\%d+)\%.","",1)
local ip3= string.gsub(ip3, "\%.(\%d+)","")
local ip4= string.gsub("#1", "(\%d+)\%.(\%d+)\%.(\%d+)\%.","",1)
local mask = #2
local ip = { tonumber( ip1 ), tonumber( ip2 ), tonumber( ip3 ), tonumber( ip4 ) }
local masks = {
    [1] = { 127, 255, 255, 255 },
    [2] = { 63, 255, 255, 255 },
    [3] = { 31, 255, 255, 255 },
    [4] = { 15, 255, 255, 255 },
    [5] = { 7, 255, 255, 255 },
    [6] = { 3, 255, 255, 255 },
    [7] = { 1, 255, 255, 255 },
    [8] = { 0, 255, 255, 255 },
    [9] = { 0, 127, 255, 255 },
    [10] = { 0, 63, 255, 255 },
    [11] = { 0, 31, 255, 255 },
    [12] = { 0, 15, 255, 255 },
    [13] = { 0, 7, 255, 255 },
    [14] = { 0, 3, 255, 255 },
    [15] = { 0, 1, 255, 255 },
    [16] = { 0, 0, 255, 255 },
    [17] = { 0, 0, 127, 255 },
    [18] = { 0, 0, 63, 255 },
    [19] = { 0, 0, 31, 255 },
    [20] = { 0, 0, 15, 255 },
    [21] = { 0, 0, 7, 255 },
    [22] = { 0, 0, 3, 255 },
    [23] = { 0, 0, 1, 255 },
    [24] = { 0, 0, 0, 255 },
    [25] = { 0, 0, 0, 127 },
    [26] = { 0, 0, 0, 63 },
    [27] = { 0, 0, 0, 31 },
    [28] = { 0, 0, 0, 15 },
    [29] = { 0, 0, 0, 7 },
    [30] = { 0, 0, 0, 3 },
    [31] = { 0, 0, 0, 1 }
}
local wildcard = masks[tonumber( mask )]
local ipcount = math.pow( 2, ( 32 - mask ) )
local bottomip = {}
for k, v in pairs( ip ) do
    if wildcard[k] == 0 then
        bottomip[k] = v
    elseif wildcard[k] == 255 then
        bottomip[k] = 0
    else
        local mod = v \% ( wildcard[k] + 1 )
        bottomip[k] = v - mod
    end
end
local topip = {}
for k, v in pairs( bottomip ) do
    topip[k] = v + wildcard[k]
end
tex.sprint( "&#92;def&#92;ipMask{" .. 255-wildcard[1] .. "." .. 255-wildcard[2]
  .. "." .. 255-wildcard[3] .. "." .. 255-wildcard[4].. "}" )
local isnetworkip = ( ip[1] == bottomip[1] and ip[2] == bottomip[2]
  and ip[3] == bottomip[3] and ip[4] == bottomip[4] )
local isbroadcastip = ( ip[1] == topip[1] and ip[2] == topip[2]
  and ip[3] == topip[3] and ip[4] == topip[4] )
  tex.sprint( "&#92;def&#92;ipDevice{#1}" )
  tex.sprint( "&#92;def&#92;ipNetwork{" .. bottomip[1] .. "." .. bottomip[2]
      .. "." .. bottomip[3] .. "." .. bottomip[4] .. "/" .. mask .. "}" )
  tex.sprint( "&#92;def&#92;ipGateway{" .. bottomip[1] .. "." .. bottomip[2]
      .. "." .. bottomip[3] .. "." .. bottomip[4]+1 .. "}" )
  tex.sprint( "&#92;def&#92;ipBroadcast{" .. topip[1] .. "." .. topip[2]
      .. "." .. topip[3] .. "." .. topip[4] .. "}" )
  tex.sprint( "&#92;def&#92;ipHostRange{" .. bottomip[1] .. "." .. bottomip[2]
      .. "." .. bottomip[3] .. "." .. bottomip[4] + 2 .. " - " .. topip[1]
      .. "." .. topip[2] .. "." .. topip[3] .. "." .. topip[4] - 1 .. "}")
}}
\begin{document}
\ip{10.240.3.38}{29}
Network data for device \ipDevice:
\bigskip

\begin{tabular}{rl}
Subnet:      &amp; \ipNetwork   &#92;
Subnet mask: &amp; \ipMask      &#92;
Gateway:     &amp; \ipGateway   &#92;
Broadcast:   &amp; \ipBroadcast &#92;
Host range:  &amp; \ipHostRange &#92;
\end{tabular}
\end{document
}

In this demo, I specified a device IP and its net mask, an got these values in return:

Network data for device 10.240.3.38:

Subnet: 10.240.3.32/29
Subnet mask: 255.255.255.248
Gateway: 10.240.3.33
Broadcast: 10.240.3.39
Host range: 10.240.3.34 – 10.240.3.38

In the real world, I use loops to fill tables with such data or use it in labels on edges in TikZ drawings, such as here:

routing

Leave a Reply

Your email address will not be published. Required fields are marked *