9

For example, if I have a network spec like 172.20.10.0/24, "24" is the bitcount. What's the best way to convert that to a netmask like 0xffffff00 ?

3
  • Do you mean pro grammatically, or on paper? Little more detail will be helpful. Commented Oct 20, 2008 at 14:23
  • oops. I meant programmatically. In particular, I'm interested in doing it in Powershell. But interested in various approaches overall. Commented Oct 20, 2008 at 14:40
  • the two parts are called the prefix and suffix. Commented Feb 24, 2009 at 20:16

9 Answers 9

7

Assuming 32-bit mask and 32-bit int.

int keepBits = 24; /* actually get it from somewhere else? */ int mask = (0xffffffff >> (32 - keepBits )) << (32 - keepBits); 

Note: this isn't necessarily the answer to the question "What's the best way to get the network mask for an interface?"

Sign up to request clarification or add additional context in comments.

2 Comments

Another one that assumes keepBits > 0.
Actually, I set keepBits equal to 24 so it is by definition > 0
4

I always do it like that (in your case cidr = 24):

uint32_t ipv4Netmask; ipv4Netmask = UINT32_MAX; ipv4Netmask <<= 32 - cidr; ipv4Netmask = htonl(ipv4Netmask); 

This will only work with ipv4Netmask to be actually uint32_t, don't make it int, as int doesn't have to be 32 Bit on every system. The result is converted to network byte order, as that's what most system functions expect.

Note that this code will fail if cidr is zero as then the code would shift a 32 bit variable by 32 bit and, believe it or not, that is undefined behavior in C. One would expect the result to always be zero but the C standard says that this is not defined to begin with. If your CIDR can be zero (which would only be allowed in the any IP address placeholder 0.0.0.0/0), then the code must catch special case.

1 Comment

"the result is converted to network byte order" -- I think you intended to type htonl(ipv4Netmask)
2

Why waste time with subtraction or ternary statements?

int suffix = 24; int mask = 0xffffffff ^ 0xffffffff >> suffix; 

If you know your integer is exactly 32 bits long then you only need to type 0xffffffff once.

int32_t mask = ~(0xffffffff >> suffix); 

Both compile to the exact same assembly code.

1 Comment

However pay attention that shifting a 32 bit value b 32 bit positions is undefined in many assembly languages. However on a 64-bit machine this worked fine in Perl: printf('%x', ~(0xffffffff >> 32) & 0xffffffff)
2

This is not a programming question, but in linux you can use whatmask.

whatmask 72.20.10.0/24 

returns

IP Entered = ..................: 72.20.10.0 CIDR = ........................: /24 Netmask = .....................: 255.255.255.0 Netmask (hex) = ...............: 0xffffff00 Wildcard Bits = ...............: 0.0.0.255 ------------------------------------------------ Network Address = .............: 72.20.10.0 Broadcast Address = ...........: 72.20.10.255 Usable IP Addresses = .........: 254 First Usable IP Address = .....: 72.20.10.1 Last Usable IP Address = ......: 72.20.10.254 

Comments

1
int keepbits = 24; int mask = keepbits > 0 ? 0x00 - (1<<(32 - keepbits)) : 0xFFFFFFFF; 

5 Comments

What if keepbits == 0? Watch out, those integer overflows will get you.
If keepbits is 0, then the '1' will shift all the way out of the register (assuming 32-bit register) and the netmask will be 0. Isn't that the correct answer?
en.wikipedia.org/wiki/… says left-shift is undefined if an overflow occurs. MSVC and GCC both evaluate 1<<32 as 1, not 0.
int mask = keepbits > 0 ? 0x00 - (1<<(32 - keepbits)) : 0xFFFFFFFF;
You should declare mask as uint32_t.
0

Here's a solution in VBScript, FWIW

option explicit 'whatmask 72.20.10.0/24 If WScript.Arguments.Unnamed.Count < 1 Then WScript.Echo "WhatMask xxx.xxx.xxx.xxx/xx" Wscript.Quit End If Dim sToFind Dim aParts Dim nSubnet sToFind = WScript.Arguments(0) aParts = Split( sToFind, "/", 2 ) nSubnet = aParts(1) if nSubnet < 1 or nSubnet > 32 then WScript.echo "Subnet out of range [1..32]" Wscript.quit end if Dim sBinary sBinary = String( nSubnet, "1") sBinary = sBinary & String( 32 - nSubnet, "0" ) wscript.echo "0x" & lcase( binary2hexadecimal( sBinary ) ) function binary2hexadecimal( sBin ) dim sSlice dim sResult dim i for i = 1 to len( sBin ) step 4 sSlice = mid( sBin, i, 4 ) sResult = sResult & hex( binary2decimal( sSlice ) ) next binary2hexadecimal = sResult end function function binary2decimal( sFourbits ) dim i dim bit dim nResult nResult = 0 for i = 4 to 1 step -1 bit = mid(sFourbits, i, 1 ) nResult = nResult * 2 + bit next binary2decimal = nResult end function 

From the command line

>whatmask.vbs 123.12.123.17/23 0xfffff700 

Comments

0

Be careful when you use the previous answers with code like:

0xFFFFFFFF << 32 - cidr 

or

-1 << 32 - cidr 

In C# at least, it will mask the shift count with 0x1F first. So, for a cidr with prefix 0 (ie the entire IPv4 address range):

int cidr=0; 0xFFFFFFFF << (32 - cidr) == 0xFFFFFFFF 

which is not what you want. Instead, you should use:

int cidr=0; (int)(0xFFFFFFFFL << (32 - cidr)) == 0 

Comments

0
/* C# version merging some of the other contributions and corrected for byte order. */ int cidr = 24; var ipv4Netmask = 0xFFFFFFFF; ipv4Netmask <<= 32 - cidr; byte[] bytes = BitConverter.GetBytes(ipv4Netmask); Array.Reverse(bytes); ipv4Netmask = BitConverter.ToUInt32(bytes, 0); // mask is now ready for use such as: var netmask = new IPAddress(ipv4Netmask); 

1 Comment

This is what I needed, without reversing byte order you get the wrong mask e.g. CIDR prefix of 20 should be 255.255.240.0, not 255.255.15.0
-1

You could try something simple, like taking the bitcount and dividing by 4. That'd give you the leading F's in the mask. And then take the remainder and have a switch from 0 bits to 3 bits.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.