COPS-Client

 view release on metacpan or  search on metacpan

lib/COPS/Client.pm  view on Meta::CPAN

    PRKS_Flags                     - Ignore, further work is required, however if you
                                     understand this usage it is available to be set.

    SRKS_IPAddress                 - This is the SECONDARY (SRKS) reporting server IP address.
                                     This is ONLY used if the primary is considered down.
                                     It should be specified as an IP, hostnames are not
                                     supported and only IPV4 is available.

    SRKS_Port                      - This is the Port that reporting messages are sent to
                                     for the SECONDARY reporting server.

    SRKS_Flags                     - Ignore, further work is required, however if you
                                     understand this usage it is available to be set.

    
    Billing Correlation Identification

    BCID_TimeStamp                 - This is a 32bit number and EPOCH is a good use here.
                                  
    BCID_ElementID                 - This is an eight (8) character entry and should be
                                     alphanumeric only to be supported by all vendors.

    BCID_TimeZone                  - This is an eight(8) character entry and specifies
                                     the timezone of the entry. 

    BCID_EventCounter              - This is a 32bit number and can be anything within that
                                     range. This could be an auto-increment in a table, so
                                     allowing GateID to be linked back later.

    An example of use would be

        my $timer=time();

        $cops_client->rks_set (
                        [
                        PRKS_IPAddress          => '192.168.50.2',
                        PRKS_Port               => 2000,
                        PRKS_Flags              => 0,
                        SRKS_IPAddress          => 0,
                        SRKS_Port               => 0,
                        SRKS_Flags              => 0,
                        BCID_TimeStamp          => $timer,
                        BCID_ElementID          => '99999999',
                        BCID_TimeZone           => '00000000',
                        BCID_EventCounter       => 12347890
                        ]
                        );

    You can omit fields which are not used and they will default to 0, but for completeness
    are included above.
 
=head2 decode_radius_attribute

    This function takes the output from FreeRadius 2.1.9 and expands it where possible. The
    supported attributes are

         CableLabs-Event-Message
         CableLabs-QoS-Descriptor

    When called this function returns the converted attribute into a hash of the attributes
    found and decoded.

    An example of use would be

    my %return_data;

    $cops_client->decode_radius_attribute("CableLabs-Event-Message",
        "
        0x00034c163b873939393939393939303030303030303000bc69f2000700022020203232323200312b3030303030300000002b32303130303631343135313233382e3032330000000080000400",
        \%return_data);

    Note the 0x is required at the beginning so validity checking will pass.

    The %return_data has should then contain the following keys with values.

        EventMessageVersionID        -  3
        TimeZone                     -  1+000000
        Status                       -  0
        AttributeCount               -  4
        SequenceNumber               -  43
        BCID_TimeZone                -  00000000
        EventObject                  -  0
        ElementType                  -  2
        EventMessageType             -  7
        BCID_Timestamp               -  1276525447
        BCID_ElementID               -  99999999
        BCID_EventCounter            -  12347890
        EventMessageTypeName         -  QoS_Reserve
        Priority                     -  128
        ElementID                    -  '   2222'
        EventTime                    -  20121019163303.51

=head2 volume_set

    This functions adds a volume limit to the gate being sent. You should be aware the CMTS
    may not stop traffic flowing through the gate when the limit is reached, implementation
    dependent, however should send a RKS notification.

    This function just takes the Volume in the number of bytes, 64 bit number.

    An example of use would be

    $cops_client->volume_set( 3000000000 );
   
    This would set the volume to 3Gigabytes.

=head2 timebase_set

    This function add a time limit to the gate being sent. You should be aware the CMTS may
    not stop traffifc flowing through the gate when the limit is reached, implementation
    dependent, however should sent a RKS notification.

    This function just takes the time in seconds , 32bit number.
        
    An example of use would be

    $cops_client->timebase_set( 60 );

    This would set the time limit to 60 seconds.

=head2 opaque_set

    This function allows you to add arbitary data to the COPS message sent which *may* be
    recorded against the gate by the remote CMTS.

    The only attribute for this function is

        OpaqueData                   - This be any data, although keeping it to something
                                       humanly readable is probably a good idea.

    An example of use would be

    $cops_client->opaque_set(
        [
        OpaqueData => 'a test string'
        ]
        );

    This would add 'a test string' as Opaque data to the gate.

=head2 Summary

    This is very much a 'work in progress'. 

=cut

sub new {

        my $self = {};
        bless $self;

        my ( $class , $attr ) =@_;

        my ( %template );
        my ( %current_data );
        my ( %complete_decoded_data );
        my ( %handles );

        $self->{_GLOBAL}{'DEBUG'}=0;

        while (my($field, $val) = splice(@{$attr}, 0, 2))
                { $self->{_GLOBAL}{$field}=$val; }

        $self->{_GLOBAL}{'STATUS'}="OK";

        if ( !$self->{_GLOBAL}{'VendorID'} )
                { $self->{_GLOBAL}{'VendorID'}="Generic Client"; }

        if ( !$self->{_GLOBAL}{'ServerIP'} )
                { die "ServerIP Required"; }

        if ( !$self->{_GLOBAL}{'ServerPort'} )
                { die "ServerPort Required"; }

        if ( !$self->{_GLOBAL}{'KeepAlive'} )
                { $self->{_GLOBAL}{'KeepAlive'}=60; }

        if ( !$self->{_GLOBAL}{'Timeout'} )
                { $self->{_GLOBAL}{'Timeout'}=5; }

        if ( !$self->{_GLOBAL}{'ListenIP'} )
                { $self->{_GLOBAL}{'ListenIP'}=""; }

        if ( !$self->{_GLOBAL}{'ListenPort'} )
                { $self->{_GLOBAL}{'ListenPort'}=""; }

        if ( !$self->{_GLOBAL}{'ListenServer'} )
                { $self->{_GLOBAL}{'ListenServer'}=0; }

        if ( !$self->{_GLOBAL}{'RemotePassword'} )
                { $self->{_GLOBAL}{'RemotePassword'}=""; }

        if ( !$self->{_GLOBAL}{'RemoteSpeed'} )
                { $self->{_GLOBAL}{'RemoteSpeed'}=10; }

        if ( !$self->{_GLOBAL}{'TMPDirectory'} )
                { $self->{_GLOBAL}{'TMPDirectory'}="/tmp/"; }

        $self->{_GLOBAL}{'data_ack'}=0;
        $self->{_GLOBAL}{'TRANSACTION_COUNT'}=1;
        $self->{_GLOBAL}{'ERROR'}="" ;
        $self->{_GLOBAL}{'data_processing'}=0;
        $self->{_GLOBAL}{'current_command'}="";
	$self->{_GLOBAL}{'Classifier_Encoded'}="";
	$self->{_GLOBAL}{'Envelope_Encoded'}="";
	$self->{_GLOBAL}{'TimeLimit'}="";
	$self->{_GLOBAL}{'VolumeLimit'}="";
	$self->{_GLOBAL}{'OpaqueData'}="";
	$self->{_GLOBAL}{'RKS_Encoded'}="";

        $self->{_GLOBAL}{'template'}= \%template;
        $self->{_GLOBAL}{'current_data'}= \%current_data;
        $self->{_GLOBAL}{'complete_decoded_data'} = \%complete_decoded_data;

	$self->{_GLOBAL}{'Listener_HandlesP'}= \%handles;

        return $self;
}

sub disconnect
{
my ( $self ) = shift;
if ( $self->{_GLOBAL}{'Handle'} )
	{
	$self->{_GLOBAL}{'Handle'}->close();
	}
return 1;
}

sub connect
{
my ( $self ) = shift;

my $lsn = IO::Socket::INET->new
                        (
                        PeerAddr => $self->{_GLOBAL}{'ServerIP'},
                        PeerPort => $self->{_GLOBAL}{'ServerPort'},
                        ReuseAddr => 1,
                        Proto     => 'tcp',
                        Timeout    => $self->{_GLOBAL}{'Timeout'}
                        );

if (!$lsn)
        {
        $self->{_GLOBAL}{'STATUS'}="Failed to bind to address '".$self->{_GLOBAL}{'ServerIP'}."' ";;
        $self->{_GLOBAL}{'STATUS'}.="and port '".$self->{_GLOBAL}{'ServerPort'};
        $self->{_GLOBAL}{'ERROR'}=$!;
        return 0;
        }

$self->{_GLOBAL}{'LocalIP'}=$lsn->sockhost();
$self->{_GLOBAL}{'LocalPort'}=$lsn->sockport();
$self->{_GLOBAL}{'Handle'} = $lsn;
$self->{_GLOBAL}{'Selector'}=new IO::Select( $lsn );
$self->{_GLOBAL}{'STATUS'}="Success Connected";

if ( $self->{_GLOBAL}{'ListenServer'} )
	{
	# we should do a fork here so the listener can wait for commands
	# how we signal when data is ready not sure.
	my $child;
	if ($child=fork)
		{}
		elsif (defined $child)
		{
		my $lsn2 = IO::Socket::INET->new
       	                 (
       	                 Listen    => 1024,
       	                 LocalAddr => $self->{_GLOBAL}{'ListenIP'},
       	                 LocalPort => $self->{_GLOBAL}{'ListenPort'},
       	                 ReuseAddr => 1,
       	                 Proto     => 'tcp',
       	                 Timeout    => $self->{_GLOBAL}{'Timeout'}

lib/COPS/Client.pm  view on Meta::CPAN

        {
	print "Checking handles.\n";
	$self->get_listener_connect(); 
	}

$self->{_GLOBAL}{'STATUS'}="Socket Closed";
$self->{_GLOBAL}{'ERROR'}="Socket Closed";
}

sub get_listener_connect
{
my ( $self ) = shift;
my ( $dataset ) = "";
my ( $handles ) = $self->{_GLOBAL}{'Listener_HandlesP'};
my ( $current_handles ) = $self->{_GLOBAL}{'Listener_Handles'};

foreach my $handle ( @{$current_handles} )
        {
        print "Handle is '$handle'\n" if $self->{_GLOBAL}{'DEBUG'}>5;
        if ( $handle==$self->{_GLOBAL}{'Listen_Handle'} )
                {
                my $new = $self->{_GLOBAL}{'Listen_Handle'}->accept;
                $self->{_GLOBAL}{'Listen_Selector'}->add($new);
                }
                else
                {
                my $link = 0;
                $dataset="";
                $link = sysread($handle,$dataset,1024);
                if ( !$link )
                        {
                        my $child;
                        ${$handles}{$handle}{'data'}.=$dataset;
                        if ($child=fork)
                                { } elsif (defined $child)
                                {
                                print "rmote address is '".${$handles}{$handle}{'addr'}."'\n" if $self->{_GLOBAL}{'DEBUG'}>5;
                                print "rmote port is '".${$handles}{$handle}{'port'}."'\n" if $self->{_GLOBAL}{'DEBUG'}>5;
                                if ( !${$handles}{$handle}{'data'} )
                                        {} else {
					my $tmp_filename="COPS_".time().rand(5000);
					print "Writing data to '".$self->{_GLOBAL}{'TMPDirectory'}."/".$tmp_filename."-data'\n";
                                        if ( open (__FILE,">".$self->{_GLOBAL}{'TMPDirectory'}."/".$tmp_filename."-data") )
                                                {
                                                print __FILE ${$handles}{$handle}{'data'};
                                                close __FILE;
                                                }
					if ( open (__FILE,">".$self->{_GLOBAL}{'TMPDirectory'}."/".$tmp_filename."-lock") )
						{
						close (__FILE);
						}
                                	}
                                foreach my $handler ( keys %{$handles} )
                                        { if ( $handler ne $handle ) { delete ${$handles}{$handler}; } }
                                if ( !${$handles}{$handle} ) { waitpid($child,0); exit(0); }
                                waitpid($child,0);
                                exit(0);
                                }
                        if ( ${$handles}{$handle}{'addr'} )
                                {
                                if ( $self->{_GLOBAL}{'complete_decoded_data'}{ ${$handles}{$handle}{'addr'} } )
                                        { undef $self->{_GLOBAL}{'complete_decoded_data'}{ ${$handles}{$handle}{'addr'} }; }
                                }
                        delete ${$handles}{$handle};
                        $self->{_GLOBAL}{'Listen_Selector'}->remove($handle);
                        $handle->close();
                        }

                        if ( $link )
                                {
				print "Got data set as '$dataset'\n";
                                ${$handles}{$handle}{'data'}.=$dataset;
                                ${$handles}{$handle}{'addr'}=$handle->peerhost() if !${$handles}{$handle}{'addr'};
                                ${$handles}{$handle}{'port'}=$handle->peerport() if !${$handles}{$handle}{'port'};
                                }
                }
        }
return 1;
}



sub check_data_available
{
my ( $self ) = shift;

$self->{_GLOBAL}{'data_sync'}=0;

while ( $self->check_data_handles && $self->{_GLOBAL}{'ERROR'}!~/not connected/i )
        {

        $self->get_data_segment();

	while ( $self->{_GLOBAL}{'data_processing'}==1 )
		{

		my $message = $self->{_GLOBAL}{'data_received'};
	
		$self->decode_message_type();

		

	        if ( length($message)==0 || $self->{_GLOBAL}{'message_opcode'}=~/^null$/i )
                {
                $self->{_GLOBAL}{'data_processing'}=0;
                }

		if ( $self->{_GLOBAL}{'DEBUG'}>0 )
			{
			if ( $self->{_GLOBAL}{'message_client_id'} )
				{
				print "Client is is '".$self->{_GLOBAL}{'message_client_id'}."'\n\n\n\n";
				}

			for($a=0;$a<length($message);$a++)
				{
				printf("%02x-", ord(substr($message,$a,1)));
				}
	        	print "\n";
			}

		if ( $self->{_GLOBAL}{'message_opcode'}=~/^opn$/i )

lib/COPS/Client.pm  view on Meta::CPAN

my ( $self ) = shift;
if ( !$self->{_GLOBAL}{'cpe_device_ip'} )
	{
	return $self->_IpIntToQuad(0);
	}
return $self->{_GLOBAL}{'cpe_device_ip'};
}

sub get_error
{
my ( $self ) = shift;
return $self->{_GLOBAL}{'ERROR'};
}

sub decode_message_type
{
my ( $self ) = shift;
my ( $decode_data ) = $self->{_GLOBAL}{'data_received'};

$self->{_GLOBAL}{'message_count'}=0;

if ( !$decode_data ) 
	{
	$self->{_GLOBAL}{'message_opcode'} = "NULL"; 
	return 1; }

my ( $vflags, $v2, $client_id, $total_length ) = unpack("CCnN",$decode_data);
my ( $version )  = $vflags;

$version >>= 4; $version = $version & 0x0F; $vflags = $vflags & 0x0F;
my ( $opcode ) = $self->decode_cops_operations( $v2 );

print "Version is '$version' flags is '$vflags'\n" if $self->{_GLOBAL}{'DEBUG'}>0;
print "Opcode is '$opcode'\n" if $self->{_GLOBAL}{'DEBUG'}>0;
print "client id is '$client_id'\n" if $self->{_GLOBAL}{'DEBUG'}>0;

print "New total length is '$total_length'\n" if $self->{_GLOBAL}{'DEBUG'}>0;
print "Length of dd '".length($decode_data)."\n" if $self->{_GLOBAL}{'DEBUG'}>0;

$decode_data = substr ( $decode_data, 8, $total_length-8 );


if ( $self->{_GLOBAL}{'DEBUG'}>0 )
	{
for($a=0;$a<length($decode_data);$a++)
{
printf("%02x-", ord(substr($decode_data,$a,1)));
}
print "\n";
	}


print "Remaing data set length is '".length($decode_data)."'\n" if $self->{_GLOBAL}{'DEBUG'}>0;

while ( length($decode_data)> 0 )
	{
	$decode_data = $self-> decode_cops_message ( $decode_data );
	}


print "all cops messages decoded.\n" if $self->{_GLOBAL}{'DEBUG'}>0;

$self->{_GLOBAL}{'message_client_id'} = $client_id;
$self->{_GLOBAL}{'message_opcode'} = $opcode;

$self->{_GLOBAL}{'data_received'} = substr( $self->{_GLOBAL}{'data_received'}, $total_length, length($self->{_GLOBAL}{'data_received'})-$total_length);

return 1;
}

sub decode_cops_message
{
my ( $self ) = shift;
my ( $data ) = shift;

my ( $total_length, $cnum, $ctype ) = unpack("nCC",$data);

print "Cops message length is '$total_length'\n" if $self->{_GLOBAL}{'DEBUG'}>0;

$cnum = $self->decode_c_num_type ( $cnum );
$ctype = $self->decode_c_num_type ( $ctype );

print "Cnum is '$cnum'\n" if $self->{_GLOBAL}{'DEBUG'}>0;
print "CType is '$ctype'\n" if $self->{_GLOBAL}{'DEBUG'}>0;

print "Total length is '$total_length'\n" if $self->{_GLOBAL}{'DEBUG'}>0;

my $message = substr($data,4, $total_length-4 );

print "Pre length data is '".length($message)."'\n" if $self->{_GLOBAL}{'DEBUG'}>0;

$self->{_GLOBAL}{'message'}{ $cnum }{$ctype } = $message;

print "Length of message is '".length($self->{_GLOBAL}{'message'}{ $cnum }{$ctype })."'\n" if $self->{_GLOBAL}{'DEBUG'}>0;
	
$data = substr($data,$total_length, length($data)-$total_length );

return $data;
}


sub decode_cops_operations
{
my ( $self ) = shift;
my ( $opcode ) = shift;

my %opcodes = (
		'1'	=> 'REQ',
		'2'	=> 'DEC',
		'3'	=> 'RPT',
		'4'	=> 'DRQ',
		'5'	=> 'SSQ',
		'6'	=> 'OPN',
		'7'	=> 'CAT',
		'8'	=> 'CC',
		'9'	=> 'KA',
		'10'	=> 'SSC'
		);

return $opcodes{$opcode};
}



( run in 1.302 second using v1.01-cache-2.11-cpan-2398b32b56e )