#
# $Id: Layer.pm 2002 2015-02-15 16:50:35Z gomor $
#
package Net::Packet::Layer;
use strict;
use warnings;
use Carp;

require Class::Gomor::Array;
our @ISA = qw(Class::Gomor::Array);

use Net::Packet::Consts qw(:layer);

our @AS = qw(
   raw
   payload
);
__PACKAGE__->cgBuildIndices;
__PACKAGE__->cgBuildAccessorsScalar(\@AS);

no strict 'vars';

sub new {
   my $self = shift->SUPER::new(@_);
   $self->unpack if $self->raw;
   $self;
}

sub is {
   my $layer = ref(shift);
   $layer =~ s/^Net::Packet:://;
   $layer;
}

sub pack {
   my $self = shift;
   my ($fmt, @args) = @_;
   my $res;
   eval { $res = CORE::pack($fmt, @args) };
   $@ ? do { carp("@{[ref($self)]}: unable to pack structure\n"); undef }
      : $res;
}

sub unpack {
   my $self = shift;
   my ($fmt, $arg) = @_;
   my @res;
   eval { @res = CORE::unpack($fmt, $arg) };
   $@ ? do { carp("@{[ref($self)]}: unable to unpack structure\n"); () }
      : @res;
}

sub getPayloadLength {
   my $self = shift;
   $self->[$__payload] ? length($self->[$__payload]) : 0;
}

sub layer            { NP_LAYER_N_UNKNOWN }
sub encapsulate      { NP_LAYER_NONE      }
sub getKey           { shift->is          }
sub getKeyReverse    { shift->is          }
sub computeLengths   { 1                  }
sub computeChecksums { 1                  }
sub print            { ""                 }
sub getLength        { 0                  }

sub dump {
   my $self = shift;

   my $hex = CORE::unpack('H*', $self->raw);
   $hex =~ s/(..)/\\x$1/g;
   $hex =~ s/\\x$//;
   sprintf "@{[$self->layer]}:+@{[$self->is]}: \"$hex\"";
}

sub _isLayer { shift->layer eq shift()       }
sub isLayer2 { shift->_isLayer(NP_LAYER_N_2) }
sub isLayer3 { shift->_isLayer(NP_LAYER_N_3) }
sub isLayer4 { shift->_isLayer(NP_LAYER_N_4) }
sub isLayer7 { shift->_isLayer(NP_LAYER_N_7) }

1;

__END__

=head1 NAME

Net::Packet::Layer - base class for all layer modules

=head1 DESCRIPTION

This is the base class for B<Net::Packet::Layer2>, B<Net::Packet::Layer3>, B<Net::Packet::Layer4> and B<Net::Packet::Layer7> modules.

It just provides those layers with inheritable attributes and methods.

=head1 ATTRIBUTES

=over 4

=item B<raw>

Stores the raw layer (as captured from the network, or packed to send to network).

=item B<payload>

Stores what is not part of the layer, that is the encapsulated part to be decoded by upper layers.

=back

=head1 METHODS

=over 4

=item B<is>

Returns the string describing the layer type (example: 'IPv4').

=item B<layer>

Returns the string describing the layer number (example: 'L3' for IPv4).

=item B<encapsulate>

Returns the next layer type (parsed from payload). This is the same string as returned by B<is> method.

=item B<computeLengths>

=item B<computeChecksums>

Generally, when a layer is built, some attributes are not yet known until the full Net::Packet::Frame is assembled. Those methods computes various lengths and checksums attributes found in a specific layer. Return 1 on success, undef otherwise.

=item B<print>

Just returns a string in a human readable format describing attributes found in the layer.

=item B<dump>

Just returns a string in hexadecimal format which is how the layer appears on the network.

=item B<getKey>

=item B<getKeyReverse>

These methods are used to respectively store and retrieve analyzed frames respectively to and from a hashref. This is to make it quick to get possible responses from a probe.

=item B<pack>

Will pack all attributes into raw network format. This method MUST be implemented into each supported layers.

=item B<unpack>

Will unpack raw network format to respective attributes. This method MUST be implemented into each supported layers.

=item B<getLength>

Returns the layer length in bytes.

=item B<getPayloadLength>

Returns the total length of remaining raw data in bytes (without calling layer length).

=item B<isLayer2>

=item B<isLayer3>

=item B<isLayer4>

=item B<isLayer7>

Returns true if the calling object is, respectively, layer 2, 3, 4 or 7.

=back

=head1 CONSTANTS

Load them: use Net::Packet::Consts qw(:layer);

=over 4

=item B<NP_LAYER>

Base layer string.

=item B<NP_LAYER_ETH>

=item B<NP_LAYER_NULL>

=item B<NP_LAYER_RAW>

=item B<NP_LAYER_SLL>

Layer 2 strings.

=item B<NP_LAYER_ARP>

=item B<NP_LAYER_IPv4>

=item B<NP_LAYER_IPv6>

=item B<NP_LAYER_VLAN>

=item B<NP_LAYER_PPPoE>

=item B<NP_LAYER_PPP>

=item B<NP_LAYER_LLC>

Layer 3 strings.

=item B<NP_LAYER_TCP>

=item B<NP_LAYER_UDP>

=item B<NP_LAYER_ICMPv4>

=item B<NP_LAYER_PPPLCP>

=item B<NP_LAYER_CDP>

Layer 4 strings.

=item B<NP_LAYER_7>

Layer 7 string.

=item B<NP_LAYER_NONE>

=item B<NP_LAYER_UNKNOWN>

Other strings.

=item B<NP_LAYER_N_2>

=item B<NP_LAYER_N_3>

=item B<NP_LAYER_N_4>

=item B<NP_LAYER_N_7>

=item B<NP_LAYER_N_UNKNOWN>

Layer number N strings.

=back

=head1 AUTHOR

Patrice E<lt>GomoRE<gt> Auffret

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2004-2015, Patrice E<lt>GomoRE<gt> Auffret

You may distribute this module under the terms of the Artistic license.
See LICENSE.Artistic file in the source distribution archive.

=head1 RELATED MODULES

L<NetPacket>, L<Net::RawIP>, L<Net::RawSock>

=cut