~blainsmith/hare-icmp

15a877edbe4babb38316efebef5d370660027264 — Blain Smith 1 year, 3 months ago f875e0c
working and tested encode/decode for all msg types

Signed-off-by: Blain Smith <rebelgeek@blainsmith.com>
3 files changed, 336 insertions(+), 42 deletions(-)

M net/icmp/+test.ha
A net/icmp/README
M net/icmp/message.ha
M net/icmp/+test.ha => net/icmp/+test.ha +158 -4
@@ 1,7 1,161 @@
@test fn encode() void = {
    assert(1 == 1);
use bufio;
use bytes;
use io;

@test fn encode_decode_echo() void = {
    let data: []u8 = alloc(['h', 'e', 'l', 'l', 'o']);

    let inmsg = message {
        proto = IP_V4,
        code = 10,
        checksum = 0,
        body = echo {
            id = 13,
            seq = 200,
            data = data,
        },
    };

    let outbuf = bufio::dynamic(io::mode::RDWR);
    let encsz = encode(&outbuf, &inmsg)!;
    assert(encsz == 13);

    let msg = bufio::buffer(&outbuf);

    let outmsg = message {...};
    let inbuf = bufio::dynamic_from(msg, io::mode::RDWR);
    let decsz = decode(&outmsg, &inbuf)! as size;
    assert(decsz == 13);

    assert(inmsg.proto == outmsg.proto);
    assert(inmsg.code == outmsg.code);
    assert(inmsg.checksum == outmsg.checksum);

    let inbody = inmsg.body as echo;
    let outbody = outmsg.body as echo;

    assert(inbody.id == outbody.id);
    assert(inbody.seq == outbody.seq);
    assert(bytes::equal(inbody.data, outbody.data));
};

@test fn encode_decode_reply() void = {
    let data: []u8 = alloc(['h', 'e', 'l', 'l', 'o']);

    let inmsg = message {
        proto = IP_V4,
        code = 10,
        checksum = 0,
        body = reply {
            id = 13,
            seq = 200,
            data = data,
        },
    };

    let outbuf = bufio::dynamic(io::mode::RDWR);
    let encsz = encode(&outbuf, &inmsg)!;
    assert(encsz == 13);

    let msg = bufio::buffer(&outbuf);

    let outmsg = message {...};
    let inbuf = bufio::dynamic_from(msg, io::mode::RDWR);
    let decsz = decode(&outmsg, &inbuf)! as size;
    assert(decsz == 13);

    assert(inmsg.proto == outmsg.proto);
    assert(inmsg.code == outmsg.code);
    assert(inmsg.checksum == outmsg.checksum);

    let inbody = inmsg.body as reply;
    let outbody = outmsg.body as reply;

    assert(inbody.id == outbody.id);
    assert(inbody.seq == outbody.seq);
    assert(bytes::equal(inbody.data, outbody.data));
};

@test fn decode() void = {
    assert(1 == 1);
@test fn encode_decode_destination_unreachable() void = {
    let inmsg = message {
        proto = IP_V4,
        code = 10,
        checksum = 0,
        body = destination_unreachable {
            ...,
        },
    };

    let outbuf = bufio::dynamic(io::mode::RDWR);
    let encsz = encode(&outbuf, &inmsg)!;
    assert(encsz == 4);

    let msg = bufio::buffer(&outbuf);

    let outmsg = message {...};
    let inbuf = bufio::dynamic_from(msg, io::mode::RDWR);
    let decsz = decode(&outmsg, &inbuf)! as size;
    assert(decsz == 4);

    assert(inmsg.proto == outmsg.proto);
    assert(inmsg.code == outmsg.code);
    assert(inmsg.checksum == outmsg.checksum);
};

@test fn encode_decode_time_exeeded() void = {
    let inmsg = message {
        proto = IP_V4,
        code = 10,
        checksum = 0,
        body = time_exeeded {
            ...,
        },
    };

    let outbuf = bufio::dynamic(io::mode::RDWR);
    let encsz = encode(&outbuf, &inmsg)!;
    assert(encsz == 4);

    let msg = bufio::buffer(&outbuf);

    let outmsg = message {...};
    let inbuf = bufio::dynamic_from(msg, io::mode::RDWR);
    let decsz = decode(&outmsg, &inbuf)! as size;
    assert(decsz == 4);

    assert(inmsg.proto == outmsg.proto);
    assert(inmsg.code == outmsg.code);
    assert(inmsg.checksum == outmsg.checksum);
};

@test fn encode_decode_parameter_problem() void = {
    let inmsg = message {
        proto = IP_V4,
        code = 10,
        checksum = 0,
        body = parameter_problem {
            pointer = 13,
            ...,
        },
    };

    let outbuf = bufio::dynamic(io::mode::RDWR);
    let encsz = encode(&outbuf, &inmsg)!;
    assert(encsz == 6);

    let msg = bufio::buffer(&outbuf);

    let outmsg = message {...};
    let inbuf = bufio::dynamic_from(msg, io::mode::RDWR);
    let decsz = decode(&outmsg, &inbuf)! as size;
    assert(decsz == 6);

    assert(inmsg.proto == outmsg.proto);
    assert(inmsg.code == outmsg.code);
    assert(inmsg.checksum == outmsg.checksum);

    let inbody = inmsg.body as parameter_problem;
    let outbody = outmsg.body as parameter_problem;

    assert(inbody.pointer == outbody.pointer);
};
\ No newline at end of file

A net/icmp/README => net/icmp/README +9 -0
@@ 0,0 1,9 @@
The icmp module provides basic functions for the manipulation of messages used in the Internet Control Message Protocols, ICMPv4 and ICMPv6. This can be used in conjunction with [[net]] to send and
receive ICMP packets.

ICMPv4 and ICMPv6 are defined in RFC 792 and RFC 4443. Additional support for the following are planned:

- Multi-part message support for ICMP as defined in RFC 4884.
- ICMP extensions for MPLS as defined in RFC 4950.
- ICMP extensions for interface and next-hop identification as defined in RFC 5837.
- PROBE: A utility for probing interfaces as defined in RFC 8335.
\ No newline at end of file

M net/icmp/message.ha => net/icmp/message.ha +169 -38
@@ 1,4 1,5 @@
use endian;
use io;

// Either IPv4 or IPv6
export type proto = int;


@@ 51,63 52,193 @@ export type parameter_problem = struct {
    data: []u8,
};

// Encodes a [[message]] into a slice of bytes ready to be transmitted over the wire
export fn encode(in: *message, out: []u8) void = {
    out[1] = in.code: u8;
    endian::beputu16(out[2..4], in.checksum: u16);
fn checksum(msg: []u8) u16 = {
    let cov: size = len(msg) - 1z;

    let s: u32 = 0;
    for (let i = 0z; i < cov; i += 1) {
        s += ((msg[i + 1]: u32) << 8) | msg[i]: u32;
    };

    if (cov & 1 == 0) {
        s += msg[cov]: u32;
    };

    s = (s >> 16) + (s & 0xffff);

    s = s + (s >> 16);

    s ^= s: u16;

    return s: u16;
};

// Encodes a [[message]] into the [[io::handle]] provided
export fn encode(out: io::handle, in: *message) (size | io::error) = {
    let msg: []u8 = [];
    let sz = 0z;

    match (in.body) {
    case let m: echo =>
        out[0] = V4_ECHO: u8;
        endian::beputu16(out[4..6], m.id: u16);
        endian::beputu16(out[6..8], m.seq: u16);
        out[8..] = m.data;
        msg = alloc([
            V4_ECHO: u8, in.code: u8,
            0, 0, // checksum
            0, 0, // id
            0, 0, // seq
        ]);

        endian::beputu16(msg[4..6], m.id: u16);
        endian::beputu16(msg[6..8], m.seq: u16);

        append(msg, m.data...);

        let cs = checksum(msg);
        in.checksum = cs: int;
        endian::beputu16(msg[2..4], cs);
    case let m: reply =>
        out[0] = V4_ECHO_REPLY: u8;
        endian::beputu16(out[4..6], m.id: u16);
        endian::beputu16(out[6..8], m.seq: u16);
        out[8..] = m.data;
        msg = alloc([
            V4_ECHO_REPLY: u8, in.code: u8,
            0, 0, // checksum
            0, 0, // id
            0, 0, // seq
        ]);

        endian::beputu16(msg[4..6], m.id: u16);
        endian::beputu16(msg[6..8], m.seq: u16);

        append(msg, m.data...);

        let cs = checksum(msg);
        in.checksum = cs: int;
        endian::beputu16(msg[2..4], cs);
    case let m: destination_unreachable =>
        out[0] = V4_DESTINATION_UNREACHABLE: u8;
        msg = alloc([
            V4_DESTINATION_UNREACHABLE: u8, in.code: u8,
            0, 0, // checksum
        ]);

        let cs = checksum(msg);
        in.checksum = cs: int;
        endian::beputu16(msg[2..4], cs);
    case let m: time_exeeded =>
        out[0] = V4_TIME_EXCEEDED: u8;
        msg = alloc([
            V4_TIME_EXCEEDED: u8, in.code: u8,
            0, 0, // checksum
        ]);

        let cs = checksum(msg);
        in.checksum = cs: int;
        endian::beputu16(msg[2..4], cs);
    case let m: parameter_problem =>
        out[0] = V4_PARAMETER_PROBLEM: u8;
        out[4] = m.pointer: u8;
        out[5..] = m.data;
        msg = alloc([
            V4_PARAMETER_PROBLEM: u8, in.code: u8,
            0, 0, // checksum
            0, 0, // pointer
        ]);

        endian::beputu16(msg[4..6], m.pointer: u16);

        let cs = checksum(msg);
        in.checksum = cs: int;
        endian::beputu16(msg[2..4], cs);
    };

    return io::write(out, msg)?;
};

// Decodes a slice of bytes from the wire into a [[message]]
export fn decode(out: *message, in: []u8) void = {
    out.code = in[1]: int;
    out.checksum = endian::begetu16(in[2..4]): int;
// Decodes from the [[io::handle]] into a [[message]]
export fn decode(out: *message, in: io::handle) (size | io::EOF | io::error) = {
    let msg: [1500]u8 = [0...];
    let sz = 0z;

    switch (in[0]: msgtype) {
    sz = match(io::read(in, msg)?) {
    case let s: size => 
        yield s;
    case io::EOF =>
		return io::EOF;
    };

    switch (msg[0]: msgtype) {
    case V4_ECHO, V6_ECHO_REQUEST =>
        out.body = echo {
            id = endian::begetu16(in[4..6]): int,
            seq = endian::begetu16(in[6..8]): int,
            data = in[8..],
        out.proto = IP_V4;
        out.code = msg[1]: int;
        out.checksum = endian::begetu16(msg[2..4]): int;
        
        let body = echo {
            id = endian::begetu16(msg[4..6]): int,
            seq = endian::begetu16(msg[6..8]): int,
            ...,
        };
        append(body.data, msg[8..sz]...);

        out.body = body;
    case V4_ECHO_REPLY, V6_ECHO_REPLY =>
        out.body = reply {
            id = endian::begetu16(in[4..6]): int,
            seq = endian::begetu16(in[6..8]): int,
            data = in[8..],
        out.proto = IP_V4;
        out.code = msg[1]: int;
        out.checksum = endian::begetu16(msg[2..4]): int;
        
        let body = reply {
            id = endian::begetu16(msg[4..6]): int,
            seq = endian::begetu16(msg[6..8]): int,
            ...,
        };
        append(body.data, msg[8..sz]...);

        out.body = body;
    case V4_DESTINATION_UNREACHABLE, V6_DESTINATION_UNREACHABLE =>
        out.body = destination_unreachable { data = in[4..] };
        out.proto = IP_V4;
        out.code = msg[1]: int;
        out.checksum = endian::begetu16(msg[2..4]): int;
        
        let body = destination_unreachable {
            ...,
        };
        if (sz > 4) {
            append(body.data, msg[5..sz]...);
        };

        out.body = body;
    case V4_TIME_EXCEEDED, V6_TIME_EXCEEDED =>
        out.body = time_exeeded { data = in[4..] };
        out.proto = IP_V4;
        out.code = msg[1]: int;
        out.checksum = endian::begetu16(msg[2..4]): int;
        
        let body = time_exeeded {
            ...,
        };
        if (sz > 4) {
            append(body.data, msg[5..sz]...);
        };

        out.body = body;
    case V4_PARAMETER_PROBLEM, V6_PARAMETER_PROBLEM =>
        out.body = parameter_problem { 
            pointer = in[4]: uintptr,
            data = in[5..],
        out.proto = IP_V4;
        out.code = msg[1]: int;
        out.checksum = endian::begetu16(msg[2..4]): int;
        
        let body = parameter_problem {
            pointer = endian::begetu16(msg[4..6]): uintptr,
            ...,
        };
        if (sz > 6) {
            append(body.data, msg[7..sz]...);
        };

        out.body = body;
    case =>
        out.body = raw { data = in[4..] };
        out.proto = IP_V4;
        out.code = msg[1]: int;
        out.checksum = endian::begetu16(msg[2..4]): int;
        
        let body = raw {
            ...,
        };
        if (sz > 4) {
            append(body.data, msg[5..sz]...);
        };

        out.body = body;
    };

    return;
};
\ No newline at end of file
    return sz;
};