~blainsmith/hare-icmp

dae59856789e4b7160dfb1efca21673ff594f495 — Conrad Hoffmann 6 months ago 15a877e
Reformat according to official style guide

Use tabs for indentation, break README lines at 80 characters.

See https://harelang.org/style/#a-general-conventions

Signed-off-by: Conrad Hoffmann <ch@bitfehler.net>
3 files changed, 339 insertions(+), 336 deletions(-)

M net/icmp/+test.ha
M net/icmp/README
M net/icmp/message.ha
M net/icmp/+test.ha => net/icmp/+test.ha +144 -144
@@ 3,159 3,159 @@ 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));
	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));
	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 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);
	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);
	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
	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);
};

M net/icmp/README => net/icmp/README +8 -5
@@ 1,9 1,12 @@
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.
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:
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
- ICMP extensions for interface and next-hop identification as defined in RFC
  5837.
- PROBE: A utility for probing interfaces as defined in RFC 8335.

M net/icmp/message.ha => net/icmp/message.ha +187 -187
@@ 11,234 11,234 @@ export type body = (raw | echo | reply | destination_unreachable | time_exeeded 

// A complete ICMP message
export type message = struct {
    proto: proto,
    code: int,
    checksum: int,
    body: body,
	proto: proto,
	code: int,
	checksum: int,
	body: body,
};

// A raw [[body]] of data in an ICMP message
export type raw = struct {
    data: []u8,
	data: []u8,
};

// An echo request message [[body]]
export type echo = struct {
    id: int,
    seq: int,
    data: []u8,
	id: int,
	seq: int,
	data: []u8,
};

// An echo reply message [[body]]
export type reply = struct {
    id: int,
    seq: int,
    data: []u8,
	id: int,
	seq: int,
	data: []u8,
};

// A destination unreachable reply message [[body]]
export type destination_unreachable = struct {
    data: []u8,
	data: []u8,
};

// A time exceeded reply message [[body]]
export type time_exeeded = struct {
    data: []u8,
	data: []u8,
};

// A parameter problem reply message [[body]]
export type parameter_problem = struct {
    pointer: uintptr,
    data: []u8,
	pointer: uintptr,
	data: []u8,
};

fn checksum(msg: []u8) u16 = {
    let cov: size = len(msg) - 1z;
	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;
    };
	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;
    };
	if (cov & 1 == 0) {
		s += msg[cov]: u32;
	};

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

    s = s + (s >> 16);
	s = s + (s >> 16);

    s ^= s: u16;
	s ^= s: u16;

    return 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 =>
        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 =>
        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 =>
        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 =>
        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 =>
        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)?;
	let msg: []u8 = [];
	let sz = 0z;

	match (in.body) {
	case let m: echo =>
		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 =>
		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 =>
		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 =>
		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 =>
		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 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;
	let msg: [1500]u8 = [0...];
	let sz = 0z;

    sz = match(io::read(in, msg)?) {
    case let s: size => 
        yield s;
    case io::EOF =>
	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.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.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.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.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.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.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 sz;
	};

	switch (msg[0]: msgtype) {
	case V4_ECHO, V6_ECHO_REQUEST =>
		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.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.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.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.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.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 sz;
};