When I read/hear about bitwise operations, It is often said; Yeah, that's good to know but in your day to day job as software engineer you don't have to deal with that very often. Especially when you work in higher level apps. That might be true but there is one use case for bitwise operations which is in my opinion very useful and that's when it comes down to validate if a ip address is in a given subnet or not. To make that work you basically use the bits of the subnet mask and start address to validate the bits of the ip address with bitwise &
operations. Confused? Well me to 😅 it is always better to draw such things out step by step which I'am going to try now and explain it a bit further:
Let's assume we have a very small network with a subnet mask of 255.255.255.192
and a start address with 192.168.50.65
. Next let's take two ip addresses 192.168.50.20
and 192.168.50.80
.
Yeah I know it is pretty obvious to see by eye which address is in the network and which not but for the example and simplicity it will do 🙂 And of course we wan't to do that in code and not just by eye 😅
So first we have a look on our example addresses and what they look like if we represent them as bits:
Now, as you can see the subnet mask has always a 1
bit in the network ID section and a 0
bit in the host ID section. This bit arrangement applies for all possible subnet masks out there and exactly this behaviour can we use to reach our goal.
If you are confused about what Network ID and what Host ID is, then let me just say to that:
Network ID
A network ID or NetID is the fragment of IP address
that classifies the network for a specified host i.e.,
it tells us which network the host belongs to.
Host ID
It is the fragment of an IP address that uniquely
classifies a host on a specified TCP/IP network.
Source of this quotes and more details you can find here
Back to our task, lets use now the bits of our subnet mask and start address to create some sort of validation bits with a bitwise &
operation:
In case you have forgotten how bitwise &
works you can look it up here. Or just keep in mind 1 & 1 = 1
and all other possibilities = 0
😉
And now we can do the same for our two ip addresses and compare the result to our validation bits and if they match you can tell that the ip is in the given network.
Starting with the first ip 192.168.50.20
:
You can see that the validation bits not match with the result where i marked it red. So this ip is not in our network. If we do the same now for our second address:
It's a match 🎉
Now before I go on with a code example to actually implement that. If you want do try it by your own you can practise the implementation in a codewars kata which I have created here. It is still in beta though but c#, js and golang are ready to use 😊
In my example I am going to us golang, which is the language I'am learning at the moment and where I can use some practise 😅
First of all, we can not really compare bits directly in our code. Therefore we need to parse our bits to a int
value or more precisely uint
since ip's have no negative values.
This is actually pretty easy and ready to copy from golang playground here. For simplicity in our example we don't return a error at this function and just do a panic
directly if a wrong ip string is inserted.
func Ip2long(ipAddr string) (uint32) {
ip := net.ParseIP(ipAddr)
if ip == nil {
panic("wrong ipAddr format")
}
ip = ip.To4()
return binary.BigEndian.Uint32(ip)
}
Next we need our comparing or validation function which just returns a bool value whether the ip is in
or not in
the given network. The bitwise &
operation is done in the return
where you can see that we use for both, ip and start address, the subnet mask to create our validation bits (as uint) and then compare it against each other.
func IsInSubnet(ip string, startAddress string, mask string) bool {
uIntIp := Ip2long(ip)
uIntStartAdress := Ip2long(startAddress)
uIntMask := Ip2long(mask)
return (uIntIp & uIntMask) == (uIntStartAdress & uIntMask)
}
Putting all together which you can use to play around:
package main
import (
"encoding/binary"
"fmt"
"net"
)
func main() {
mask := "255.255.255.192"
startAddress := "192.168.50.65"
ip1 := "192.168.50.20"
ip2 := "192.168.50.80"
fmt.Println("IP1 Result: ", IsInSubnet(ip1, startAddress, mask))
fmt.Println("IP2 Result: ", IsInSubnet(ip2, startAddress, mask))
}
func IsInSubnet(ip string, startAddress string, mask string) bool {
uIntIp := Ip2long(ip)
uIntStartAdress := Ip2long(startAddress)
uIntMask := Ip2long(mask)
return (uIntIp & uIntMask) == (uIntStartAdress & uIntMask)
}
func Ip2long(ipAddr string) uint32 {
ip := net.ParseIP(ipAddr)
if ip == nil {
panic("wrong ipAddr format")
}
ip = ip.To4()
return binary.BigEndian.Uint32(ip)
}
Have fun and a nice day 🙂!
Top comments (0)