~blainsmith/public-inbox

hare-icmp: Various updates, add basic ping tool v1 APPLIED

Thanks for starting hare-icmp! I fixed some small issues and adapted to
recent changes in the stdlib. This allowed me to create a stupid, but
working, "ping" tool (also included). My next goal would be IPv6
support. Let me know what you think!

Conrad Hoffmann (6):
  Reformat according to official style guide
  test: adapt to memio/bufio changes in stdlib
  test: properly initialize structs
  Fix checksum calculation and test it
  Fix memory leak
  Add a _very_ basic ping utility

 cmd/ping/main.ha    |  78 +++++++++
 net/icmp/+test.ha   | 321 ++++++++++++++++++++-----------------
 net/icmp/README     |  13 +-
 net/icmp/message.ha | 377 ++++++++++++++++++++++----------------------
 4 files changed, 449 insertions(+), 340 deletions(-)
 create mode 100644 cmd/ping/main.ha

-- 
2.41.0
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~blainsmith/public-inbox/patches/43568/mbox | git am -3
Learn more about email & git

[PATCH hare-icmp 1/6] Reformat according to official style guide Export this patch

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>
---
 net/icmp/+test.ha   | 288 +++++++++++++++++-----------------
 net/icmp/README     |  13 +-
 net/icmp/message.ha | 374 ++++++++++++++++++++++----------------------
 3 files changed, 339 insertions(+), 336 deletions(-)

diff --git a/net/icmp/+test.ha b/net/icmp/+test.ha
index 5c15ddf..690b426 100644
--- a/net/icmp/+test.ha
+++ b/net/icmp/+test.ha
@@ -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);
};
diff --git a/net/icmp/README b/net/icmp/README
index 8ac9849..717807e 100644
--- a/net/icmp/README
+++ b/net/icmp/README
@@ -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.
diff --git a/net/icmp/message.ha b/net/icmp/message.ha
index ea55941..cbeefeb 100644
--- a/net/icmp/message.ha
+++ b/net/icmp/message.ha
@@ -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;
};
-- 
2.41.0

[PATCH hare-icmp 2/6] test: adapt to memio/bufio changes in stdlib Export this patch

See https://git.sr.ht/~sircmpwn/hare/commit/ed762a2

Signed-off-by: Conrad Hoffmann <ch@bitfehler.net>
---
 net/icmp/+test.ha | 33 ++++++++++++++++-----------------
 1 file changed, 16 insertions(+), 17 deletions(-)

diff --git a/net/icmp/+test.ha b/net/icmp/+test.ha
index 690b426..c2a275a 100644
--- a/net/icmp/+test.ha
+++ b/net/icmp/+test.ha
@@ -1,6 +1,5 @@
use bufio;
use memio;
use bytes;
use io;

@test fn encode_decode_echo() void = {
	let data: []u8 = alloc(['h', 'e', 'l', 'l', 'o']);
@@ -16,14 +15,14 @@ use io;
		},
	};

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

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

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

@@ -53,14 +52,14 @@ use io;
		},
	};

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

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

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

@@ -86,14 +85,14 @@ use io;
		},
	};

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

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

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

@@ -112,14 +111,14 @@ use io;
		},
	};

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

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

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

@@ -139,14 +138,14 @@ use io;
		},
	};

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

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

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

-- 
2.41.0

[PATCH hare-icmp 3/6] test: properly initialize structs Export this patch

Not sure this still makes for a good API like this, but just make it
compile for now.

Signed-off-by: Conrad Hoffmann <ch@bitfehler.net>
---
 net/icmp/+test.ha | 35 ++++++++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/net/icmp/+test.ha b/net/icmp/+test.ha
index c2a275a..fe79d92 100644
--- a/net/icmp/+test.ha
+++ b/net/icmp/+test.ha
@@ -21,7 +21,12 @@ use bytes;

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

	let outmsg = message {...};
	let outmsg = message {
		body = echo {
			...
		},
		...
	};
	let inbuf = memio::dynamic_from(msg);
	let decsz = decode(&outmsg, &inbuf)! as size;
	assert(decsz == 13);
@@ -58,7 +63,12 @@ use bytes;

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

	let outmsg = message {...};
	let outmsg = message {
		body = reply {
			...
		},
		...
	};
	let inbuf = memio::dynamic_from(msg);
	let decsz = decode(&outmsg, &inbuf)! as size;
	assert(decsz == 13);
@@ -91,7 +101,12 @@ use bytes;

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

	let outmsg = message {...};
	let outmsg = message {
		body = destination_unreachable {
			...
		},
		...
	};
	let inbuf = memio::dynamic_from(msg);
	let decsz = decode(&outmsg, &inbuf)! as size;
	assert(decsz == 4);
@@ -117,7 +132,12 @@ use bytes;

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

	let outmsg = message {...};
	let outmsg = message {
		body = time_exeeded {
			...
		},
		...
	};
	let inbuf = memio::dynamic_from(msg);
	let decsz = decode(&outmsg, &inbuf)! as size;
	assert(decsz == 4);
@@ -144,7 +164,12 @@ use bytes;

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

	let outmsg = message {...};
	let outmsg = message {
		body = parameter_problem {
			...
		},
		...
	};
	let inbuf = memio::dynamic_from(msg);
	let decsz = decode(&outmsg, &inbuf)! as size;
	assert(decsz == 6);
-- 
2.41.0

[PATCH hare-icmp 4/6] Fix checksum calculation and test it Export this patch

Signed-off-by: Conrad Hoffmann <ch@bitfehler.net>
---
 net/icmp/+test.ha   |  5 +++++
 net/icmp/message.ha | 14 ++++++--------
 2 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/net/icmp/+test.ha b/net/icmp/+test.ha
index fe79d92..0d4a5b6 100644
--- a/net/icmp/+test.ha
+++ b/net/icmp/+test.ha
@@ -18,6 +18,7 @@ use bytes;
	let outbuf = memio::dynamic();
	let encsz = encode(&outbuf, &inmsg)!;
	assert(encsz == 13);
	assert(inmsg.checksum == 8672);

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

@@ -60,6 +61,7 @@ use bytes;
	let outbuf = memio::dynamic();
	let encsz = encode(&outbuf, &inmsg)!;
	assert(encsz == 13);
	assert(inmsg.checksum == 10720);

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

@@ -98,6 +100,7 @@ use bytes;
	let outbuf = memio::dynamic();
	let encsz = encode(&outbuf, &inmsg)!;
	assert(encsz == 4);
	assert(inmsg.checksum == 64757);

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

@@ -129,6 +132,7 @@ use bytes;
	let outbuf = memio::dynamic();
	let encsz = encode(&outbuf, &inmsg)!;
	assert(encsz == 4);
	assert(inmsg.checksum == 62709);

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

@@ -161,6 +165,7 @@ use bytes;
	let outbuf = memio::dynamic();
	let encsz = encode(&outbuf, &inmsg)!;
	assert(encsz == 6);
	assert(inmsg.checksum == 62440);

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

diff --git a/net/icmp/message.ha b/net/icmp/message.ha
index cbeefeb..9e2c91f 100644
--- a/net/icmp/message.ha
+++ b/net/icmp/message.ha
@@ -56,21 +56,19 @@ 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;
	for (let i = 0z; i < cov; i += 2) {
		s += ((msg[i]: u32) << 8) + msg[i + 1]: u32;
	};

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

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

	s = s + (s >> 16);

	s ^= s: u16;
	for ((s >> 16) != 0) {
		s = (s >> 16) + (s & 0xffff);
	};

	return s: u16;
	return ~s: u16;
};

// Encodes a [[message]] into the [[io::handle]] provided
-- 
2.41.0

[PATCH hare-icmp 5/6] Fix memory leak Export this patch

Signed-off-by: Conrad Hoffmann <ch@bitfehler.net>
---
 net/icmp/message.ha | 1 +
 1 file changed, 1 insertion(+)

diff --git a/net/icmp/message.ha b/net/icmp/message.ha
index 9e2c91f..6d91b24 100644
--- a/net/icmp/message.ha
+++ b/net/icmp/message.ha
@@ -74,6 +74,7 @@ fn checksum(msg: []u8) u16 = {
// Encodes a [[message]] into the [[io::handle]] provided
export fn encode(out: io::handle, in: *message) (size | io::error) = {
	let msg: []u8 = [];
	defer free(msg);
	let sz = 0z;

	match (in.body) {
-- 
2.41.0

[PATCH hare-icmp 6/6] Add a _very_ basic ping utility Export this patch

Currently IPv4 only, for various reasons.

Signed-off-by: Conrad Hoffmann <ch@bitfehler.net>
---
 cmd/ping/main.ha | 78 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)
 create mode 100644 cmd/ping/main.ha

diff --git a/cmd/ping/main.ha b/cmd/ping/main.ha
new file mode 100644
index 0000000..d992a5d
--- /dev/null
+++ b/cmd/ping/main.ha
@@ -0,0 +1,78 @@
use fmt;
use getopt;
use io;
use memio;
use net::icmp;
use net::ip;
use os;
use rt;

export fn main() void = {

	const cmd = getopt::parse(os::args,
		"send ICMP echo requests",
		"address",
	);
	defer getopt::finish(&cmd);

	if (len(cmd.args) != 1) {
		fmt::fatal("Must specify exactly one target address");
	};
	let addr = ip::parse(cmd.args[0])!;
	let (icmp_proto, domain, proto) = match (addr) {
	case ip::addr4 =>
		yield (icmp::IP_V4, rt::AF_INET, rt::IPPROTO_ICMP);
	case ip::addr6 =>
		// TODO imcp::encode does not handle v6 atm
		// TODO rt is missing IPPROTO_ICMP6
		//yield (icmp::IP_V6, rt::AF_INET6, 58);
		fmt::fatal("Error: IPv6 is currently not supported!");
	};

	static let buf: [512]u8 = [0...];

	let s = match (rt::socket(domain: int, rt::SOCK_DGRAM, proto)) {
	case let s: int =>
		yield s;
	case let err: rt::errno =>
		fmt::fatalf("Failed to create socket: {}", rt::strerror(err));
	};
	defer io::close(s)!;

	// This would be nice, but rt currently doesn't know about SO_RCVTIMEO
	//let timeout = rt::timeval {
	//	tv_sec = 2,
	//	tv_usec = 0,
	//};
	//let i = rt::setsockopt(s, rt::SOL_SOCKET, rt::SO_RCVTIMEO, &timeout: *void, size(rt::timeval))!;

	let echo = icmp::echo {
		id = 1234,
		seq = 1,
		data = ['1': u8, '2': u8, '3': u8, '4': u8, '5': u8],
	};

	let inmsg = icmp::message {
		proto = proto,
		code = 0,
		checksum = 0,
		body = echo,
	};

	let outbuf = memio::fixed(buf[..]);
	let encsz = icmp::encode(&outbuf, &inmsg)!;
	let sa = ip::to_native(addr, 0);
	let data = memio::buffer(&outbuf);
	let addrsz = size(rt::sockaddr): u32;
	let sz = rt::sendto(s, data: *[*]u8, encsz, 0, &sa, addrsz);
	match (sz) {
	case let err: rt::errno =>
		fmt::fatalf("sendto: {}", rt::strerror(err));
	case let s: size =>
		yield s;
	};

	sz = rt::recvfrom(s, &buf: *[*]u8, 512, 0, &sa, &addrsz)!;
	let (from, port) = ip::from_native(sa);
	fmt::printfln("Received response from {}", ip::string(from))!;
};
-- 
2.41.0