in reply to Re^2: Allowed VLANs with SNMP
in thread Allowed VLANs with SNMP

A string of octets containing one bit per VLAN in the management domain on this trunk port. The first octet corresponds to VLANs with VlanIndex values of 0 through 7; the second octet to VLANs 8 through 15; etc. The most significant bit of each octet corresponds to the lowest value VlanIndex in that octet.

Well that's pretty clear. It's also the exact reverse of the previous form.

Perl's bit-vector is similar, except that the bits are in the opposite order in each byte. So you can translate between this format and a Perl bit-vector so:

sub xlat { my ($octets) = @_ ; return pack('B*', unpack('b*', $octets)) ; } ;
The translation reverses itself, and as you can see unpacks the bytes in one bit order and promptly repacks in the other. This shows the effect:
my $test = "\xC5\x11\x01\x80\x5A" ; print showbits($test), "\n" ; print showbits(xlat($test)), "\n" ; print showbits(xlat(xlat($test))), "\n" ; sub showbits { my ($octets) = @_ ; my $s = unpack('B*', $octets) ; $s =~ s/([01]{8})(?=[01])/$1:/g ; return $s ; } ;
giving:
  11000101:00010001:00000001:10000000:01011010
  10100011:10001000:10000000:00000001:01011010
  11000101:00010001:00000001:10000000:01011010
which appears to be what's required.

Replies are listed 'Best First'.
Re^4: Allowed VLANs with SNMP
by spivey49 (Monk) on Oct 24, 2008 at 15:53 UTC

    Thanks again. I don't think I'm fully grasping how vec works, even after reading the doc, or the routine using vec to map the bits to the vlans and creating the ranges.

    The routine formating numbers like 1,2,3 as 1-3 is great, but doesn't seem to work properly with the octet string example. Of course it won't work with Grandfather's example from what I can see since @ports just contains the VLANs allowed

    Trying to shoehorn the previous example into this one:

    sub xlat { my ($octets) = @_ ; return pack('B*', unpack('b*', $octets)) ; } ; my $test = '0x4000040000000200800000000000000000000002' .'0000000000c004000108008000000000000000000' .'00000000000000000000000000000000000000000' .'00000000000000000000000000000000000000000' .'00000000000000000000000000000000000000000' .'00000000000000000000000000000000000000000' .'00000000000'; my $bits; $bits = showbits($test); my @s = ranges($bits); print "Ranges: ", join(', ', @s); $bits = showbits(xlat($test)); @s = ranges($bits); print join(',',@s); $bits = showbits(xlat(xlat($test))); @s = ranges($bits); print join(',',@s); sub ranges{ my $test = shift; my $r = undef ; my @s = () ; for my $vn (1..length($test) * 8) { if (vec($test, $vn, 1)) { if (!defined($r)) { push @s, "$vn-" ; } ; $r = $vn ; } else { if (defined($r)) { $s[-1] .= "$r" ; $r = undef ; } ; } ; } ; return @s; } sub showbits { my ($octets) = @_ ; my $s = unpack('B*', $octets) ; $s =~ s/([01]{8})(?=[01])/$1/g ; ranges($s) ; } ;

    Should, at least in my misunderstanding, produce Ranges: 1,21,54,64,158,200-201,213,231,236,248.

    Instead it produces:

    Ranges: 1-1, 4-5, 8-8, 10-10, 12-13, 18-18, 20-21, 24-24, 28-291-1,4-5,8-8,10-10,12-13,18-18,20-21,28-291-1,4-5,8-8,10-10,12-13,18-18,20-21,24-24,28-29

    Using GrandFather's example

    my $ports = '0x4000040000000200800000000000000000000002' .'0000000000c004000108008000000000000000000' .'00000000000000000000000000000000000000000' .'00000000000000000000000000000000000000000' .'00000000000000000000000000000000000000000' .'00000000000000000000000000000000000000000' .'00000000000'; my $basePort = 0; my @ports; $ports = substr $ports, 2; while ($ports) { my $octet = hex substr $ports, 0, 2, ''; my $index = 0; while ($octet) { next unless $octet & 0x80; push @ports, $basePort + $index; } continue { ++$index; $octet = ($octet << 1) & 0xff; } $basePort += 8; } print join ',', @ports;

    produces: 1,21,54,64,158,200,201,213,231,236,248

    After hours of playing with both these examples I managed to confuse myself even more. Can you point out what I'm missing?

    Update:After some more tinkering I came up with the following kludge to format Grandfather's example, but something tells me there's a better way to do this. Still haven't figured out how to convert the string properly with vec.

    my $ports = '0x4000040000000200800000000000000000000002' .'0000000000c004000108008000000000000000000' .'00000000000000000000000000000000000000000' .'00000000000000000000000000000000000000000' .'00000000000000000000000000000000000000000' .'00000000000000000000000000000000000000000' .'00000000000'; my $basePort = 0; my @ports; $ports = substr $ports, 2; while ($ports) { my $octet = hex substr $ports, 0, 2, ''; my $index = 0; while ($octet) { next unless $octet & 0x80; push @ports, $basePort + $index; } continue { ++$index; $octet = ($octet << 1) & 0xff; } $basePort += 8; } print join ',', @ports,"\n"; range_format(@ports); sub range_format{ my (@numbers,$i,$start,$end); @numbers = @_; for ($i = 0; $i < @numbers; ++$i){ my $prev = $numbers[$i-1]; my $next = $numbers[$i+1]; my $cur = $numbers[$i]; if (($cur+1 == $next) and ($cur-1!= $prev)){ $start = $cur; } if (($cur-1 == $prev) and ($cur+1 != $next)){ $end = $cur; } if ((defined $start) and (defined $end)){ print "$start-$end,"; $start = undef; $end = undef; }else{ unless((defined $start)||(defined $end)){print "$cur,";}} } }

      OK, it's all down to representations.

      I suggested Perl bit-vectors, so let's start there and work back up.

      A Perl bit-vector should be viewed as an abstract entity, which for these purposes we're going to treat as a collection of bits, numbered from 0..'n'. We can read bit '$b' by:  $v = vec($bits, 1, $b) ; where $bits contains the bit-vector, 1 indicates this is a vector of single bit values, and $b contains the number of the bit we want the value of. Similarly we can set a bit by:  vec($bits, 1, $b) = $v ;.

      What we want to do is to translate from the forms you have, to a Perl bit-vector, so that the bit number corresponds to a VLAN number.

      The documentation tells us that a Perl bit-vector is a string of bytes, where (for single bit values) bit number 1 is stored as Bit0 of Byte0, bit number 7 as Bit7 of Byte0, bit number 8 as Bit0 of Byte1, and so on.

      The second form you mentioned claimed to be in octets, with VLAN 0 represented by Bit7 of Byte0, VLAN 7 by Bit0 of Byte0, VLAN 8, by Bit7 of Byte1, and so on. This is similar to a Perl bit-vector, but with the bit numbers within each byte reversed.

      The xlat routine translates between these two forms.

      Now, what you appear to have in your hands is the second form, but not as raw (binary) octets, but in a hex representation of those octets. So the first step is to pack from there to the binary. That can be given to xlat and the result is the Perl bit-vector form, which the range subroutine can unpick.

      Code below illustrates all the above. Output is:

        01000000::00000100::::00000010::10000000:::::::::::00000010::::::11000000:00000100:...
        00000010::00100000::::01000000::00000001:::::::::::01000000::::::00000011:00100000:...
        Ranges: 1, 21, 54, 64, 158, 200-201, 213, 231, 236, 248
      
      Hope this helps clarify.