Detecting a valid IPv4 in Go like a boss

A short analysis of different alternatives to check IPv4 validity comparing complexity vs performance of their implementations.

Image for post
Image for post

In this post, we will analyze how Golang can be used to squeeze computer performance at maximum, with a tiny example of how to parse and validate content. In this case, we will be using IPv4 detection methods in order to make different approaches, and to benchmark & compare how Golang behaves in each of them. We will be testing:

  • net package.
  • regular expressions.
  • custom implementation.

All tests are executed with latest available version of Go at the time of writing this article which is go version go1.12.9 linux/amd64.

Method 1: net package

func IsIpv4Net(host string) bool {
return net.ParseIP(host) != nil
}

Method 2: regex implementation

^(([0–9]|[1–9][0–9]|1[0–9]{2}|2[0–4][0–9]|25[0–5])\.){3}([0–9]|[1–9][0–9]|1[0–9]{2}|2[0–4][0–9]|25[0–5])$

And this is how we use it:

var (
ipRegex, _ = regexp.Compile(`^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`)
)
func IsIpv4Regex(ipAddress string) bool {
ipAddress = strings.Trim(ipAddress, " ")
return ipRegex.MatchString(ipAddress)
}

Method 3: custom implementation

func IsIpv4(s string) bool {
var p [IPv4len]byte
for i := 0; i < IPv4len; i++ {
if len(s) == 0 {
// Missing octets.
return false
}
if i > 0 {
if s[0] != '.' {
return false
}
s = s[1:]
}
var n int
var i int
var ok bool
for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
n = n*10 + int(s[i]-'0')
if n >= big {
n = big; ok = false
}
}
if i == 0 {
n = 0; i = 0; ok = false
}
ok = true
if !ok || n > 0xFF {
return false
}
s = s[i:]
p[i] = byte(n)
}
if len(s) != 0 {
return false
}
return true
}

Benchmarking our implementations

All test will have following configuration:

  • Measure allocations per operation
  • Measure ns per operation
  • Measure bytes per operation

All benchmarking test will be executed of course, with testing machine in idle state with no other workloads being executed at same time to avoid bias results.

Method 1 Benchmarking: net package

b.Run("is-valid-ipv4-net-pkg", func(b *testing.B) {
b.ReportAllocs()
b.SetBytes(1)
b.ResetTimer()
for n := 0; n < b.N; n++ {
_ = IsIpv4Net("10.41.132.6")
}
})
Image for post
Image for post
Results of method 1 benchmarking

Method 2 Benchmarking: regex package

b.Run("is-valid-ipv4-method-3", func(b *testing.B) {
b.ReportAllocs()
b.SetBytes(1)
b.ResetTimer()
for n := 0; n < b.N; n++ {
_ = IsIpv4Regex("10.41.132.6")
}
})
Image for post
Image for post

Method 3 Benchmarking: custom method

b.Run("is-valid-ipv4-custom-method", func(b *testing.B) {
b.ReportAllocs()
b.SetBytes(1)
b.ResetTimer()
for n := 0; n < b.N; n++ {
_ = IsIpv4("10.41.132.6")
}
})
Image for post
Image for post

Final Results

Image for post
Image for post
IPv4 validation algorithms. Benchmarking final results.
Image for post
Image for post
Plotted final results of our different implementations (bigger is better)

Conclusions

For a http server, this means that for each validation it will require to make one allocations while the optimized version is zero-alloc! And compared to regex version, the optimized code is x20 times faster.

Thanks for checking this out and I hope you found the info useful! This is a basic introduction to how to optimize stuff for performance.

If you want to see more content on — hit the 👏 button as many times as you can and share with your colleges, co-workers, FFF, etc so I know you want more content like this!

Written by

Cybersecurity // Blockchain Research

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store