Acme-RFC4824

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

Revision history for Acme-RFC4824

0.02    2007/04/25
        - Corrected ASCII art that is incorrect in the RFC (see
          http://lists.mur.at/pipermail/ip-sfs/2007-April/000052.html)
        - fixed IPv6 support
        - added example scripts and documentation for them

0.01    2007/04/06
        - First version, does not support fragmenting, checksums or
          gzipped frames (yet).

README  view on Meta::CPAN

directory (note that you need Net::Pcap for them):

# ipsfss_receive.pl <interface> [filter]
Receives packets from an interface and shows you what to signal. So
say you want to signal all packets to 10.23.23.23 arriving on en0, just
use
# ipsfss_receive.pl en0 "ip and dst 10.23.23.23"

# ipsfss_send.pl <interface> <source MAC> <destination MAC>
If you receive signals from someone, you can use ipsfss_send.pl to
convert those frames into IP packets. Say you accept frames for 
01:23:45:67:89:0a and your MAC is 00:11:22:33:44:55:66, you can run it
as 
# ipsfss_send.pl en0 00:11:22:33:44:55:66 01:23:45:67:89:0a

Please note that both example programs assume an interface which
either is or looks like Ethernet.

UPPORT AND DOCUMENTATION

After installing, you can find documentation for this module with the perldoc command.

examples/ipsfss_receive.pl  view on Meta::CPAN

#!/usr/bin/env perl
# 
# Example script for Acme::RFC4824
# receives datagrams on an interface (with a possible pcap filter)
# and converts them into IP over SFSS frames to be signaled
#
# (c) 2007 Alexander Klink
# released under the same terms as Perl itself

use strict;
use warnings;
use bytes;

use Net::Pcap qw( loop );
use Acme::RFC4824;

examples/ipsfss_send.pl  view on Meta::CPAN

#!/usr/bin/perl
#
# Example script for Acme::RFC4824
# receives signaled frames as ASCII on STDIN and outputs them to the
# network
#
# (c) 2007 Alexander Klink
# released under the same terms as Perl itself

use strict;
use warnings;
use bytes;

use Net::Pcap;

examples/ipsfss_send.pl  view on Meta::CPAN

$src_mac =~ s/://g;
$dst_mac =~ s/://g;

my $err;
my $pcap = Net::Pcap::open_live($interface, -1, 1, 100, \$err);
if ($err) {
    die "Can't open '$interface': $err";
}

my $sfss = Acme::RFC4824->new();
print "Enter received frames line by line\n";
while (my $line = <STDIN>) {
    chomp($line);
    my $packet = $sfss->decode({
        FRAME => $line,
    });
    my $eth_packet = pack('H12', $src_mac)
                   . pack('H12', $dst_mac)
                   . pack('H4', '0800') . $packet;                
    Net::Pcap::sendpacket($pcap, $eth_packet);
}

lib/Acme/RFC4824.pm  view on Meta::CPAN

use Carp;
use bytes;

our $VERSION = '0.02';

# a hash ref of mappings from ASCII to ASCII art representations
has 'ascii2art_map'     => (
    is => 'ro',
);

# the default SFS frame size in bytes
has 'default_framesize' => (
    is      => 'ro',
    isa     => 'Int',
    default => 255,
);

sub BUILD {
    my $self    = shift;
    my $arg_ref = shift;

    if (exists $arg_ref->{'DEFAULT_FRAMESIZE'}) {
        if ($arg_ref->{'DEFAULT_FRAMESIZE'} > 255) {
            croak "Frame size too large, can at most be 255";
        }
        $self->{'default_framesize'} = $arg_ref->{'DEFAULT_FRAMESIZE'};
    }
    # initialize mapping from characters to ASCII art
    # ASCII-Art comes directly from RFC4824
    $self->{'ascii2art_map'}->{'A'} = << 'XEOF';
 0 
/||
/ \
XEOF
    $self->{'ascii2art_map'}->{'B'} = << 'XEOF';
__0 

lib/Acme/RFC4824.pm  view on Meta::CPAN

 |\
/ \
XEOF
    return 1;
}

sub decode {
    my $self    = shift;
    my $arg_ref = shift;

    my $frame   = $arg_ref->{FRAME};
    if (! defined $frame) {
        croak "You need to pass a frame to be decoded.";
    }
    my $last_frame_undo = rindex $frame, 'T';
    if ($last_frame_undo > 0) {
        # if a FUN was found, take everything to the right to be the
        # new frame.
        $frame = 'Q' . substr($frame, $last_frame_undo + 2);
    }
    while ($frame =~ m{ (.*) [^S]S (.*) }xms) {
        # delete the signal before a 'S' (SUN, signal undo)
        $frame = $1 . $2;
    }
    $frame =~ s/[U-Y]//g; # ignore ACK, KAL, NAK, RTR and RTT signals
    my ($header, $payload, $checksum) =
        ($frame =~ m{\A Q([A-E][A-B][A-P]{2}) ([A-P]+) ([A-P]{4})R \z}xms);
    if (! defined $header || ! defined $payload || ! defined $checksum) {
        croak "Invalid frame format.";
    }
    return $self->__pack($payload);
}

sub __pack {
    my $self  = shift;
    my $frame = shift;

    # convert from ASCII to hex
    $frame =~ tr/A-J/0-9/;
    $frame =~ tr/K-P/a-f/;
    return pack('H*', $frame);
}

sub __unpack {
    my $self = shift;
    my $data = shift;

    # unpack
    my $result = unpack('H*', $data);
    $result =~ tr/0-9/A-J/;
    $result =~ tr/a-f/K-P/;
    return $result;
}

sub encode {
    my $self    = shift;
    my $arg_ref = shift;

    my $sfs_frame = 'Q'; # Frame Start FST

    # type is ASCII or ASCII-ART
    my $type = 'ASCII';
    if (defined $arg_ref->{TYPE}) {
        $type = $arg_ref->{TYPE};
    }
    if ($type ne 'ASCII' && $type ne 'ASCII art') {
        croak "Invalid output type";
    }

lib/Acme/RFC4824.pm  view on Meta::CPAN

        $checksum = $arg_ref->{CHECKSUM};
    };
    # TODO - implement CRC 16 support
    if ($checksum == 1) {
        croak "CRC 16 support not implemented (yet).";
    }
    elsif ($checksum > 1) {
        croak "Invalid checksum type";
    }

    my $framesize = $self->{default_framesize};
    if (exists $arg_ref->{FRAMESIZE}) {
        $framesize = $arg_ref->{FRAMESIZE};
    }
    # TODO - implement fragmenting
    # note: honor DF bit in IP packets
    if (length($packet) > $framesize) {
        croak "Fragmenting not implemented (yet).";
    }

    # TODO - implement support for gzipped frames
    my $gzip = $arg_ref->{GZIP};
    if ($gzip) {
        croak "GZIP support not implemented (yet).";
    }

    my $packet_ascii = $self->__unpack($packet);
    if (substr($packet_ascii, 0, 1) eq 'E') { # E=4: IPv4
        $sfs_frame .= 'B';
    }
    elsif (substr($packet_ascii, 0, 1) eq 'G') { # G=6: IPv6
        $sfs_frame .= 'C';
    }
    else {
        croak "Invalid IP version";
    }

    $sfs_frame .= 'A';    # Checksum Type: none
    $sfs_frame .= 'AA';   # Frame number 0x00

    $sfs_frame .= $packet_ascii;

    $sfs_frame .= 'AAAA'; # No checksum, so we just set it zeros 
    $sfs_frame .= 'R';    # Frame End, FEN

    if ($type eq 'ASCII') {
        return $sfs_frame;
    }
    else { # ASCII-ART
        my @sfss_ascii_art_frames = ();
        for (my $i = 0; $i < length($sfs_frame); $i++) {
            my $char = substr($sfs_frame, $i, 1);
            my $aa_repr = $self->ascii2art_map->{$char};
            if (! defined $aa_repr) {
                die "No ASCII-Art representation for '$char'";
            }
            push @sfss_ascii_art_frames, $aa_repr;
        }
        if (wantarray) {
            return @sfss_ascii_art_frames;
        }
        else {
            return join "\n", @sfss_ascii_art_frames;
        }
    }
}
1;
__END__

=head1 NAME

Acme::RFC4824 - Internet Protocol over Semaphore Flag Signaling System (SFSS)

=head1 VERSION

Version 0.01

=head1 SYNOPSIS

This module is used to help you implement RFC 4824 - The Transmission
of IP Datagrams over the Semaphore Flag Signaling System (SFSS).

It can be used to convert IP datagrams to SFS frames and the other
way round. Furthemore, it can be used to display an ASCII art representation
of the SFS frame.

    use Acme::RFC4824;

    my $sfss = Acme::RFC4824->new();
    
    # get IP datagram from somewhere (for example Net::Pcap)
    # print a representation of the SFS frame
    print $sfss->encode({
        TYPE   => 'ASCII art',
        PACKET => $datagram, 
    });

    # get an ASCII representation of the SFS frame
    my $sfs_frame = $sfss->encode({
        TYPE   => 'ASCII',
        PACKET => $datagram,
    });

    # get an SFS frame from somewhere
    # (for example from someone signaling you)
    # get an IP datagram from the frame
    my $datagram = $sfss->decode({
        FRAME => $frame,
    });

=head1 EXPORT

As this module is supposed to be used in an object oriented fashion, it
does not export anything.

=head1 FUNCTIONS

=head2 BUILD

lib/Acme/RFC4824.pm  view on Meta::CPAN

see new()

=head2 new

Constructs a new object for you. Takes the following named parameters:

=over 1

=item * DEFAULT_FRAMESIZE (optional)

The framesize in bytes that is used whenever the FRAMESIZE paramter is
not given for encode. Defaults to 255 bytes (the maximum SFS frame size).

=back

=head2 encode

Encodes an IP datagram into one or more SFS frames. Currently, fragmenting
is not (yet) supported, so it will always encode into one frame (or
complain that the IP packet is too large to encode into one frame).

Takes the following named parameters:

=over 4

=item * TYPE

Determines the output format. Can either be 'ASCII' or 'ASCII art'.
In the first case, a string representation of the SFS frame is returned.
In the second case, an ASCII art representation is returned - as an
array of ASCII art strings in list context or as the concatenation in
scalar context.

=item * PACKET

The IP packet that you want to convert

=item * CHECKSUM (optional)

The checksum algorithm. Only 0 (no checksum) is implemented at the moment.

=item * FRAMESIZE (optional)

The optional maximal frame size of the SFS frame. Will later be used
to fragment, currently only limits the size of the packet you can encode.

=item * GZIP (optional)

Not implemented yet, meant to support the gzipped frame variant of RFC 4824.

=back

=head2 decode

Decodes one or more SFS frame into an IP datagram.

Takes the following named parameters:

=over 4

=item * FRAME

An ASCII representation of the SFS frame which you would like to decode
into an IP datagram.

=back

=head2 ascii2art_map

Read-only accessor for the attribute with the same name.
Returns a hash reference that maps SFS ASCII characters to an ASCII art
representation of the given character. There is probably no need to use
this from the outside.

=head2 default_framesize

Read-only accessor for the attribute with the same name.
Returns the default SFS framesize. There is probably no need to use this
from the outside.

=head2 meta

From Moose.pm: This is a method which provides access to the current class's
meta-class. Only used internally.

=head1 AUTHOR

Alexander Klink, C<< <alech at cpan.org> >>

t/01-encode_decode.t  view on Meta::CPAN

    FRAME => $ascii,
});
if ($ENV{DEBUG}) {
    diag "Decoded packet (in hex): " . unpack('H*', $packet);
}
# test that decoding the ASCII representation yields the original packet
ok($packet eq $test_packet, 'Re-encoding yields original representation');

# test that adding errors that are cancelled using SUN (signal undo)
# does not change the resulting packet
my $frame_cancelled_errors = 'AAASSS' . $ascii;
$packet = $sfss->decode({
    FRAME => $frame_cancelled_errors,
});
if ($ENV{DEBUG}) {
    diag "Decoded packet (in hex): " . unpack('H*', $packet);
}
is(unpack('H*', $packet), unpack('H*', $test_packet), 'Signal Undo cancelling works');

$frame_cancelled_errors = 'ABCDEFGHAAAT' . $ascii;
$packet = $sfss->decode({
    FRAME => $frame_cancelled_errors,
});
if ($ENV{DEBUG}) {
    diag "Decoded packet (in hex): " . unpack('H*', $packet);
}
# test that adding errors that are cancelled using FUN (frame undo)
# does not change the resulting packet
ok($packet eq $test_packet, 'Frame Undo cancelling works');



( run in 0.686 second using v1.01-cache-2.11-cpan-df04353d9ac )