Unsolved Validating a Subnet Mask
-
Does anyone know of any existing algorithms within
Qt
which could validate a network subnet mask including checking that contiguous bits are set?For example, the following are valid subnets:
255.255.255.0 11111111.11111111.11111111.00000000 255.255.224.0 11111111.11111111.11110000.00000000 255.224.0.0 11111111.11110000.00000000.00000000
Whereas these are not valid subnet masks
255.15.0.0 11111111.00001111.00000000.00000000 255.255.88.0 11111111.11111111.01011000.00000000 224.15.0.0 11110000.00001111.00000000.00000000
I could go through each bit in turn starting with MSB, then set a flag when the bit is clear. If I see another set bit after the flag has been set then I can determine that the netmask is invalid.
Seems a little clunky though and I'd perhaps expect that this is already covered somewhere in the
QtNetwork
library?uint32_t netmask = 0xFFF00000; bool flag = false; QString str; for (int i = 31; i >= 0; i--) { bool set = (((netmask & (1 << i)) >> i) == 1); if (set) { str += "1"; } else { str += "0"; } if (!set) flag = true; if (set && flag) error = true; } qDebug() << str;
-
@webzoid There are not that many numbers to check against:
00000000 10000000 11000000 11100000 11110000 11111000 11111100 11111110 11111111
Simply check each byte of the mask whether it is one of this numbers. You don't even need to convert to binary representation.
-
@jsulm Sorry, I wasn't very explicit - the subnet mask is ultimately stored as a 32-bit number hence why I'm looping through all 32 bit positions.
-
@webzoid But then you can iterate over each byte instead of iterating over each bit.
-
@webzoid said in Validating a Subnet Mask:
the subnet mask is ultimately stored as a 32-bit number
There may be better ways, but you can do it like:
bool isContiguous(quint32 mask) { for (;mask != 0; mask <<=1) { if ((mask & (1<<31)) == 0) return false; // Highest bit is now zero, but mask is non-zero. } return true; // Mask was, or became 0. }
And calling it with your sample masks:
qDebug() << isContiguous(QHostAddress(QStringLiteral("255.255.255.0")).toIPv4Address()); qDebug() << isContiguous(QHostAddress(QStringLiteral("255.255.224.0")).toIPv4Address()); qDebug() << isContiguous(QHostAddress(QStringLiteral("255.224.0.0")).toIPv4Address()); qDebug() << isContiguous(QHostAddress(QStringLiteral("255.15.0.0")).toIPv4Address()); qDebug() << isContiguous(QHostAddress(QStringLiteral("255.255.88.0")).toIPv4Address()); qDebug() << isContiguous(QHostAddress(QStringLiteral("224.15.0.0")).toIPv4Address());
Gives:
true true true false false false
Cheers.
-
@Paul-Colby Thanks Paul, that looks like a neat little way to do it.
-
As @jsulm pointed out, it should be rather trivial. Here's an example, but you need to modify and/or fix it, as I don't test my snippets:
bool isContiguous(const QString & maskString) { static const quint8 bits[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; quint32 mask = QHostAddress(maskString).toIPv4Address(); quint32 validity = ~(mask ^ ~(mask << 1)); char * data = reinterpret_cast<char *>(&validity); quint8 raised = 0; for (qint8 i = 0; i < sizeof(quint32); i++) raised += bits[data[i] & 0x0f] + (bits[data[i] >> 4]); return raised == 1; }
PS.
The idea of the above snippet is to count the "discontinuities" in the bit stream (hence the shift and xor in the beginning). Basically you should have only one single bit set in thevalidity
integer to have a valid mask. More than one raised bit means the mask is not valid (i.e. more than one switch from 0 to 1 or 1 to 0). The loop on the bytes is just for more efficient bit counting. If your compiler supports it you can use thepopcnt
instruction instead of the wholefor
loop.
gcc:__builtin_popcount
msvc:__popcnt