4-byte (32-bit) BGP Neighbor Information with AsDotNotation
A problem currently exists within Solarwinds where 4-byte (32-bit) ASNs overflow into a negative number when utilizing a portion of the private scoped ASN space specifically, the ranges 2,147,483,648 (32768.0) through 4,294,967,296 (65535.65535) overflow into -2,147,483,648 through -1 respectively.
To show the correct data, we created a custom table utilizing the following WSQL to generate the correct information. See past the snippet for an explanation as to how and why this works.
SELECT NeighborIP, ProtocolName, ProtocolStatus, -- AutonomousSystem is stored in a signed 32 bit integer, and will therefore -- overflow into a negative number when the number exceeds 2,147,483,648. -- High Bound of AS before this event is 32768.65535. -- NOTE: THIS FIELD IS FOR REFERENCE ONLY AND CAN BE DROPPED IN PRODUCTION. AutonomousSystem AS RawAS, -- AsPlainNotation represents the correct enumeration for 4-byte AS (as well -- as 2-byte AS by adding the Autonomous system to 4,294,967,296) CASE WHEN AutonomousSystem < 0 THEN ( -- NEGATIVE ASN INTEGER OVERFLOW ENCOUNTERED -- Add 64-bit CONST Integer representing positive offset to counteract -- negative overflow... 4294967296 + AutonomousSystem ) ELSE ( -- ASN WITHIN NORMAL BOUNDS -- Return RAW ASN. AutonomousSystem ) END AS AsPlainNotation, -- AsDotNotation represents the X.Y Dot Notation specified in RFC4893 for -- the 4-byte (or 32-bit) BGP Peer's AS. CASE WHEN AutonomousSystem < 0 THEN ( -- NEGATIVE ASN INTEGER OVERFLOW ENCOUNTERED -- Add 64-bit CONST Integer representing positive offset to counteract -- negative overflow... split components: -- + To get most significant 16-bits divide by 2^16 (65536) and -- truncate with FLOOR. -- + To get least significant 16-bits perform 'most significant bit -- calculation' and Left shift 16-bits by multiplying by 2^16 -- and subtracting from the full 32-bit value. TOSTRING( FLOOR( (4294967296 + AutonomousSystem) / 65536) ) + '.' + TOSTRING( (4294967296 + AutonomousSystem) - (FLOOR( (4294967296 + AutonomousSystem) / 65536) * 65536) ) ) WHEN AutonomousSystem < 0 THEN ( -- POSITIVE 4-BYTE ASN ENCOUNTERED -- Split components: -- + To get most significant 16-bits divide by 2^16 (65536) and -- truncate with FLOOR. -- + To get least significant 16-bits perform 'most significant bit -- calculation' and Left shift 16-bits by multiplying by 2^16 -- and subtracting from the full 32-bit value. WHEN AutonomousSystem > 65535 THEN ( TOSTRING( FLOOR( (AutonomousSystem) / 65536) ) + '.' + TOSTRING( (AutonomousSystem) - (FLOOR( (AutonomousSystem) / 65536) * 65536) ) ) ELSE ( -- POSITIVE 2-BYTE ASN ENCOUNTERED -- Append 0. to the autonomous system. '0.' + TOSTRING(AutonomousSystem) ) END AS AsDotNotation FROM Orion.Routing.Neighbors WHERE NodeID = ${NodeID}
NOTE: RFC6996 defines private scoped ASNs as 4,200,000,000 (64806.0) through 4294967294 (65535.65535) inclusive.
Linear assignment of Binary Symbol to Decimal Number for a 32-bit Signed Integer: 10000000 00000000 00000000 00000000 = -2,147,483,648 10000000 00000000 00000000 00000001 = -2,147,483,647 10000000 00000000 00000000 00000010 = -2,147,483,646 10000000 00000000 00000000 00000011 = -2,147,483,645 ... lines removed for brevity ... 11111111 11111111 11111111 11111100 = -4 11111111 11111111 11111111 11111101 = -3 11111111 11111111 11111111 11111110 = -2 11111111 11111111 11111111 11111111 = -1 00000000 00000000 00000000 00000000 = 0 00000000 00000000 00000000 00000001 = 1 00000000 00000000 00000000 00000010 = 2 00000000 00000000 00000000 00000011 = 3 00000000 00000000 00000000 00000100 = 4 ... lines removed for brevity ... 01111111 11111111 11111111 11111100 = 2,147,483,644 01111111 11111111 11111111 11111101 = 2,147,483,645 01111111 11111111 11111111 11111110 = 2,147,483,646 01111111 11111111 11111111 11111111 = 2,147,483,647 10000000 00000000 00000000 00000000 = -2,147,483,648 NEGATIVE OVERFLOW
Details
Storage of a 4-byte/32-bit ASN in 32-bit signed integer causes
2,147,483,648 (32768.0) through 4,294,967,295 (65535.65535)
to be be interpreted as:
-2,147,483,648 through -1
which do not represent legitimate ASNs.
We don’t care about negative numbers when dealing with ASNs, and so the better storage mechanism would be to keep these in Unsigned 32-bit Integers or 64-bit Signed integers. However, that is beyond the scope of this.. I only note this to point out, we can easily fix the output by summing this 32-bit signed (negative) integer with a 64-bit signed integer in the amount of the offset needed.
Lets imagine we are using the AS Number of 32768.4 (2,147,483,652)
So what happens when we store this number in a 32-bit Singed Integer, the accumulator (in theory) would get to:
2,147,483,647 (32767.65535)
and increase the value by 1, which becomes into
-2,147,483,648 (32768.0) ... which should be 2,147,483,648
then the accumulator continues increase the value by 1, 4 more times:
-2,147,483,647 (32768.1) ... which should be 2,147,483,649 -2,147,483,646 (32768.2) ... which should be 2,147,483,650 -2,147,483,645 (32768.3) ... which should be 2,147,483,651 -2,147,483,644 (32768.4) ... which should be 2,147,483,652
What you will notice is the result of -2,147,483,644 is always the total bit storage quantity offset from its intended value of 2,147,483,652.
This total bit storage quantity of a 32-bit word provides 4,294,967,296 total symbols. By making the 32-bit Integer Signed, the system is simply using half of those symbols for negative integers.
I fail to see the problem here.
As you say, RFC6996 defines privately scoped ASN’s as 4.200.000.000 through 4.294.967.294 inclusive.
If you interpret a 4-byte ASN in that range as a 4-byte signed integer, it stands to reason that you will get a negative number.
I fail to see why you don’t just cast it to a more compatible type from the get-go.
– for math purposes bigint
– for storage purposes binary(4)
Now you interpret it as int and then proceed to complain about it behaving like an int. 🙂
Or am I missing something here?
Glen, thanks for the comment. I should have clarified in my original article. There are two issues that generated this posting.
The first issue is the Solarwinds ORION product incorrectly produces the negative integer in the user interface. This could be rectified by their built in page generation casting to either a 64-bit signed or 32-bit unsigned integer prior to outputting the content on the page or better yet, simply storing it in a long integer in the database (which will waste unnecessary space.) They however have yet to resolve this bug and it confuses my users. So we proceed to problem 2 when trying to customize the output to be correct.
The second problem arises because while this looks like native SQL, it is not. The solarwinds product provides the ability to create custom components/dashboards by utilizing a very limited SQL derivative via their web API–SWQL. This mechanism does not provide the ability to cast or convert or really any other operation beyond basic math and aggregate functions. Hence the ugly mess I posted above. However, on the off chance I missed the ability to do casts/converts in Solarwinds without modifying the database or interacting with RAW SQL, I would greatly appreciate an example as I’m working on another snippet that I didn’t think I can accomplish without such a mechanism.
I also had need to generate the AS Dot Notation so my userbase doesn’t have to calculate it on their own, so while I was at it, I added the ability to output that information.
On the off chance that someone else might find this handy, I put it up here for safe keeping.
I appreciate the comment/question. It gave me the opportunity to flesh out information I didn’t find relevant to most ORION users above, but which makes a great discussion point.