From 5abcc66cb6e83f02b5218802d3eca74c0d29bebf Mon Sep 17 00:00:00 2001
From: dzwdz
Date: Wed, 17 Aug 2022 18:05:43 +0200
Subject: user/net: answer to ARP

---
 src/kernel/arch/amd64/driver/rtl8139.c |   6 +-
 src/kernel/arch/amd64/pci.c            |   2 -
 src/user/app/ethdump/ethdump.c         | 172 +++++++++++++++++++++------------
 3 files changed, 114 insertions(+), 66 deletions(-)

(limited to 'src')

diff --git a/src/kernel/arch/amd64/driver/rtl8139.c b/src/kernel/arch/amd64/driver/rtl8139.c
index 5b09027..428d675 100644
--- a/src/kernel/arch/amd64/driver/rtl8139.c
+++ b/src/kernel/arch/amd64/driver/rtl8139.c
@@ -39,7 +39,7 @@ static size_t rxpos;
 static char txbuf[4][txbuf_len];
 
 static void rx_irq_enable(bool v) {
-	uint16_t mask = 1 | 4; /* rx/tx ok */
+	uint16_t mask = 1; /* rx ok */
 	port_out16(iobase + INTRMASK, v ? mask : 0);
 }
 
@@ -85,11 +85,11 @@ void rtl8139_init(uint32_t bdf) {
 
 void rtl8139_irq(void) {
 	uint16_t status = port_in16(iobase + INTRSTATUS);
-	if (status != 1) {
+	if (!(status & 1)) {
 		kprintf("bad rtl8139 status 0x%x\n", status);
 		panic_unimplemented();
 	}
-	// TODO don't assume this is an rx irq
+	status &= 1;
 
 	do {
 		if (blocked_on) {
diff --git a/src/kernel/arch/amd64/pci.c b/src/kernel/arch/amd64/pci.c
index 93fffec..92e2697 100644
--- a/src/kernel/arch/amd64/pci.c
+++ b/src/kernel/arch/amd64/pci.c
@@ -60,9 +60,7 @@ static void scan_bus(uint32_t bus) {
 			}
 
 			if (id == 0x813910ec) {
-				kprintf("rtl irq %u\n", pcicfg_r8(bdf, 0x3C));
 				pcicfg_w32(bdf, 0x3C, IRQ_RTL8139);
-				kprintf("rtl irq %u\n", pcicfg_r8(bdf, 0x3C));
 				rtl8139_init(bdf);
 			}
 		}
diff --git a/src/user/app/ethdump/ethdump.c b/src/user/app/ethdump/ethdump.c
index adcb95e..8d5be34 100644
--- a/src/user/app/ethdump/ethdump.c
+++ b/src/user/app/ethdump/ethdump.c
@@ -1,4 +1,5 @@
 #include <camellia/syscalls.h>
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -6,8 +7,14 @@
 
 #define eprintf(fmt, ...) fprintf(stderr, "ethdump: "fmt"\n" __VA_OPT__(,) __VA_ARGS__)
 
-static void hexdump(void *vbuf, size_t len) {
-	uint8_t *buf = vbuf;
+handle_t eth_h;
+
+// TODO dynamically get mac
+uint8_t my_mac[] = {0x52, 0x54, 0x00, 0xCA, 0x77, 0x1A};
+uint32_t my_ip = (192 << 24) + (168 << 16) + 11;
+
+static void hexdump(const void *vbuf, size_t len) {
+	const uint8_t *buf = vbuf;
 	for (size_t i = 0; i < len; i += 16) {
 		printf("%08x  ", i);
 
@@ -20,19 +27,42 @@ static void hexdump(void *vbuf, size_t len) {
 	}
 }
 
+static void nput16(void *vbuf, uint16_t n) {
+	uint8_t *b = vbuf;
+	b[0] = n >> 8;
+	b[1] = n >> 0;
+}
+
+static void nput32(void *vbuf, uint32_t n) {
+	uint8_t *b = vbuf;
+	b[0] = n >> 24;
+	b[1] = n >> 16;
+	b[2] = n >>  8;
+	b[3] = n >>  0;
+}
+
+static uint16_t nget16(const void *vbuf) {
+	const uint8_t *b = vbuf;
+	return (b[0] << 8)
+	     | (b[1] << 0);
+}
+
+static uint32_t nget32(const void *vbuf) {
+	const uint8_t *b = vbuf;
+	return (b[0] << 24)
+	     | (b[1] << 16)
+	     | (b[2] <<  8)
+	     | (b[3] <<  0);
+}
+
+
 static void parse_icmp(const uint8_t *buf, size_t len) {
 	if (len < 4) return;
 	uint8_t type = buf[0];
-	switch (type) {
-		case 8:
-			printf("icmp type %u - echo request\n", type);
-			break;
-		default:
-			printf("icmp type %u - unknown\n", type);
-			break;
-	}
+	printf("ICMP type %u\n", type);
 }
 
+
 static void parse_ipv4(const uint8_t *buf, size_t len) {
 	uint8_t version, headerlen, proto;
 	uint16_t packetlen, id, fragoffset;
@@ -44,17 +74,11 @@ static void parse_ipv4(const uint8_t *buf, size_t len) {
 		return;
 	}
 	headerlen = (buf[0] & 0xf) * 4;
-	packetlen = (buf[2] << 8) | buf[3];
-	id        = (buf[4] << 8) | buf[5];
+	packetlen = nget16(buf + 2);
+	id        = nget16(buf + 4);
 	proto     = buf[9];
-	src  = (buf[12] << 24)
-	     | (buf[13] << 16)
-	     | (buf[14] <<  8)
-	     | (buf[15] <<  0);
-	dest = (buf[16] << 24)
-	     | (buf[17] << 16)
-	     | (buf[18] <<  8)
-	     | (buf[19] <<  0);
+	src  = nget32(buf + 12);
+	dest = nget32(buf + 16);
 
 	// TODO checksum
 	// TODO fragmentation
@@ -77,11 +101,53 @@ static void parse_ipv4(const uint8_t *buf, size_t len) {
 	}
 }
 
+static void parse_arp(const uint8_t *buf, size_t len) {
+	// TODO no bound checks
+	uint16_t htype = nget16(buf + 0);
+	uint16_t ptype = nget16(buf + 2);
+	uint16_t op = nget16(buf + 6);
+
+	const char *ops = "bad operation";
+	if (op == 1) ops = "request";
+	if (op == 2) ops = "reply";
+
+	printf("ARP htype 0x%x, ptype 0x%x, %s\n", htype, ptype, ops);
+
+	if (!(htype == 1 && ptype == 0x800)) return;
+	/* IPv4 over Ethernet */
+
+	if (op != 1) return;
+	/* a request */
+
+	uint32_t daddr = nget32(buf + 24);
+	printf("IPv4 request for %08x\n", daddr);
+
+	if (daddr == my_ip) {/* send a response */
+		char res[64];
+		memset(res, 0, 64);
+		/* Ethernet */
+		memcpy(res + 0, buf + 8, 6); /* from ARP sender MAC */
+		memcpy(res + 6, my_mac,  6);
+		nput16(res + 12, 0x0806); /* ethertype == ARP */
+
+		/* ARP */
+		nput16(res + 14, 1);      /* Ethernet */
+		nput16(res + 16, 0x800);  /* IPv4 */
+		res[18] = 6; res[19] = 4; /* address lengths */
+		nput16(res + 20, 2);      /* operation == reply */
+		memcpy(res + 22, my_mac, 6);
+		nput32(res + 28, my_ip);
+		memcpy(res + 32, buf + 8, 10); /* sender's MAC and IP */
+		printf("sending ARP response:\n");
+		hexdump(res, sizeof res);
+		_syscall_write(eth_h, res, sizeof res, 0, 0);
+	}
+}
+
 /* https://www.w3.org/TR/PNG/#D-CRCAppendix */
 uint32_t crc_table[256];
 static uint32_t crc32(const uint8_t *buf, size_t len) {
 	if (!crc_table[1]) {
-		eprintf("building crc cache");
 		for (int i = 0; i < 256; i++) {
 			uint32_t c = i;
 			for (int j = 0; j < 8; j++)
@@ -107,63 +173,47 @@ static void parse_ethernet(const uint8_t *buf, size_t len) {
 	printf("to   %02x:%02x:%02x:%02x:%02x:%02x\n",
 		dmac[0], dmac[1], dmac[2], dmac[3], dmac[4], dmac[5]);
 
-	/* byte order switched on purpose */
-	uint32_t crc = (buf[len - 1] << 24)
-	             | (buf[len - 2] << 16)
-	             | (buf[len - 3] <<  8)
-	             | (buf[len - 4] <<  0);
-	printf("fcf %x, crc %x\n", crc, crc32(buf, len - 4));
-
-	uint16_t ethertype = (buf[12] << 8) | buf[13];
-	if (ethertype == 0x800) {
-		printf("ethertype %u - IPv4\n", ethertype);
-		parse_ipv4(buf + 14, len - 14);
-	} else {
-		printf("ethertype %u - unknown\n", ethertype);
+	if (false) {
+		/* byte order switched on purpose */
+		uint32_t crc = (buf[len - 1] << 24)
+		             | (buf[len - 2] << 16)
+		             | (buf[len - 3] <<  8)
+		             | (buf[len - 4] <<  0);
+		printf("fcf %x, crc %x\n", crc, crc32(buf, len - 4));
 	}
-}
 
-static void dummy_packet(handle_t h) {
-	const uint8_t packet[] = {
-		/* raw dump of a ICMP broadcast ping */
-		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x52, 0x54,
-		0x00, 0x12, 0x34, 0x56, 0x08, 0x00, 0x45, 0x00,
-		0x00, 0x54, 0x06, 0xa6, 0x40, 0x00, 0x40, 0x01,
-		0x73, 0x5a, 0xc0, 0xa8, 0x00, 0x01, 0xff, 0xff,
-		0xff, 0xff, 0x08, 0x00, 0x82, 0x62, 0xef, 0x07,
-		0x00, 0xcb, 0x3b, 0x83, 0x4a, 0x47, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00,
-		/* 0x20, 0xcf, 0x98, 0x7f, */ /* crc */
-	};
-	_syscall_write(h, packet, sizeof packet, 0, 0);
+	uint16_t ethertype = nget16(buf + 12);
+	printf("ethertype %u\n", ethertype);
+	switch (ethertype) {
+		case 0x800:
+			parse_ipv4(buf + 14, len - 14);
+			break;
+		case 0x806:
+			parse_arp(buf + 14, len - 14);
+			break;
+		default:
+			printf("(unknown)\n");
+			break;
+	}
 }
 
 int main(void) {
 	const char *path = "/kdev/eth";
-	handle_t h = _syscall_open(path, strlen(path), 0);
-	if (h < 0) {
+	eth_h = _syscall_open(path, strlen(path), 0);
+	if (eth_h < 0) {
 		eprintf("couldn't open %s", path);
 		return 1;
 	}
 
-	for (int i = 0; i < 20; i++)
-		dummy_packet(h);
-	return 0;
-
 	const size_t buflen = 4096;
 	char *buf = malloc(buflen);
 	for (;;) {
-		long ret = _syscall_read(h, buf, buflen, -1);
+		long ret = _syscall_read(eth_h, buf, buflen, -1);
 		if (ret < 0) break;
 		printf("\npacket of length %u\n", ret);
 		hexdump(buf, ret);
 		parse_ethernet((void*)buf, ret);
 	}
+	free(buf);
 	return 0;
 }
-- 
cgit v1.2.3