Add in_send and ICMP echo request/reply

This commit is contained in:
Mark
2019-11-03 14:50:15 +02:00
parent 05f2838df4
commit 4ded684f92
6 changed files with 185 additions and 11 deletions
+2 -1
View File
@@ -63,7 +63,8 @@ OBJS+=$(O)/sys/debug.o \
$(O)/sys/ring.o \
$(O)/sys/net/eth.o \
$(O)/sys/net/arp.o \
$(O)/sys/net/in.o
$(O)/sys/net/in.o \
$(O)/sys/net/netdev.o
DIRS+=$(O)/sys/vfs/ext2 \
$(O)/sys/net \
$(O)/sys/blk
+10
View File
@@ -21,4 +21,14 @@ struct in_frame {
char payload[];
} __attribute__((packed));
struct in_route {
struct netdev *dev;
uint32_t mask;
uint32_t network_inaddr;
uint32_t gateway;
struct in_route *next;
};
// frame is required to be a fully-wrapped L2/L3/payload frame for now
void in_send(struct netdev *dev, char *frame, size_t length);
void in_handle_frame(struct netdev *dev, struct in_frame *frame);
+10
View File
@@ -5,8 +5,18 @@ struct netdev;
typedef int (*netdev_tx_func_t) (struct netdev *, const void *, size_t);
struct link_info {
struct in_route *in_routes_head;
struct in_route *in_routes_tail;
};
struct netdev {
uint8_t hwaddr[6];
char name[32];
netdev_tx_func_t tx;
struct link_info link;
};
void link_add_route_in(struct netdev *dev, uint32_t net, uint32_t mask, uint32_t gw);
struct in_route *link_find_route_in(struct netdev *dev, uint32_t dst_inaddr);
+3
View File
@@ -267,6 +267,9 @@ void pci_rtl8139_init(pci_addr_t addr) {
kdebug("RTL8139 MAC: " MAC_FMT "\n", MAC_VA(rtl->net.hwaddr));
rtl->net.tx = rtl8139_tx;
// TODO: call some kind of netdev_add to bind a name to the device
// XXX: for testing
link_add_route_in(&rtl->net, 0x0002000A, 0x00FFFFFF, 0x0102000A);
}
static __init void pci_rtl8139_register(void) {
+131 -10
View File
@@ -1,6 +1,8 @@
#include "sys/net/in.h"
#include "sys/net/net.h"
#include "sys/net/eth.h"
#include "sys/net/arp.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/debug.h"
@@ -12,6 +14,14 @@ struct udp_frame {
char payload[];
};
struct icmp_frame {
uint8_t type;
uint8_t code;
uint16_t checksum;
uint32_t rest;
char payload[];
};
static void udp_handle_frame(struct netdev *dev, int in6, void *frame, struct udp_frame *udp) {
// in6 is required to be 0 now, I haven't yet implemented IPv6 support
_assert(!in6);
@@ -26,26 +36,137 @@ static void udp_handle_frame(struct netdev *dev, int in6, void *frame, struct ud
debug_dump(DEBUG_DEFAULT, udp->payload, htons(udp->length) - 8);
}
uint16_t in_checksum_compute(uint8_t *data, size_t length) {
// Initialise the accumulator.
uint32_t acc = 0xffff;
// Handle complete 16-bit blocks.
for (size_t i = 0; i + 1 < length; i += 2) {
uint16_t word;
memcpy(&word, data + i, 2);
acc += htons(word);
if (acc > 0xffff) {
acc -= 0xffff;
}
}
// Handle any partial block at the end of the data.
if (length & 1) {
uint16_t word = 0;
memcpy(&word, data + length - 1, 1);
acc += htons(word);
if (acc > 0xffff) {
acc -= 0xffff;
}
}
// Return the checksum in network byte order.
return htons(~acc);
}
int in_send_frame(struct netdev *dev, char *frame, size_t size) {
/*
* Caller is required to fill the following:
* - in_frame: everything except TTL (optional) and src_inaddr
*/
struct eth_frame *eth_frame = (struct eth_frame *) frame;
struct in_frame *in_frame = (struct in_frame *) eth_frame->payload;
char *payload = in_frame->payload;
kdebug("Send in_frame to " INADDR_FMT "\n", INADDR_VA(in_frame->dst_inaddr));
struct in_route *route = link_find_route_in(dev, in_frame->dst_inaddr);
if (!route) {
kwarn(INADDR_FMT ": no route to host\n", INADDR_VA(in_frame->dst_inaddr));
return -1;
}
kdebug("Found a route to " INADDR_FMT " via " INADDR_FMT "\n",
INADDR_VA(in_frame->dst_inaddr),
INADDR_VA(route->gateway));
// Find MAC route to next hop
struct arp_route *arp_route = arp_find_route_in(dev, route->gateway);
if (!arp_route) {
kwarn(INADDR_FMT ": no route to gateway\n", INADDR_VA(route->gateway));
return -1;
}
// L3:
// TODO: get actual address from netdev->link_info
in_frame->src_inaddr = 0x0F02000A;
in_frame->version = 0x45;
in_frame->dscp = 0;
in_frame->length = htons(size - sizeof(struct eth_frame));
in_frame->ttl = 0x40;
in_frame->header_chksum = 0;
in_frame->header_chksum = in_checksum_compute((uint8_t *) in_frame, htons(in_frame->length));
// L2:
// Fill netdev->hwaddr -> eth_frame->src_mac
memcpy(eth_frame->src_mac, dev->hwaddr, 6);
memcpy(eth_frame->dst_mac, arp_route->hwaddr, 6);
eth_frame->ethertype = htons(ETHERTYPE_IPV4);
debug_dump(DEBUG_DEFAULT, frame, size);
dev->tx(dev, frame, size);
return 0;
}
static void icmp_handle_frame(struct netdev *dev, int in6, void *frame, struct icmp_frame *icmp) {
// in6 is required to be 0 now, I haven't yet implemented IPv6 support
_assert(!in6);
struct in_frame *in_frame = frame;
if (icmp->type == 0x08) {
size_t payload_length = htons(in_frame->length) - sizeof(struct in_frame) - 8;
size_t reply_size = htons(in_frame->length) + sizeof(struct eth_frame);
char reply[reply_size];
struct eth_frame *eth_reply_frame = (struct eth_frame *) reply;
struct in_frame *in_reply_frame = (struct in_frame *) eth_reply_frame->payload;
struct icmp_frame *icmp_reply_frame = (struct icmp_frame *) in_reply_frame->payload;
// L4
memcpy(icmp_reply_frame, icmp, payload_length + 8);
icmp_reply_frame->type = 0;
icmp_reply_frame->checksum = 0;
icmp_reply_frame->checksum = in_checksum_compute((uint8_t *) icmp_reply_frame, payload_length + 8);
// L3
in_reply_frame->dst_inaddr = in_frame->src_inaddr;
in_reply_frame->id = in_frame->id;
in_reply_frame->flag_frag_offset = in_frame->flag_frag_offset;
in_reply_frame->protocol = in_frame->protocol;
kdebug("PING REPLY\n");
in_send_frame(dev, reply, reply_size);
}
}
void in_handle_frame(struct netdev *dev, struct in_frame *frame) {
kdebug("INET frame\n");
kdebug("\tTotal length: %u\n", htons(frame->length));
kdebug("\tsrcaddr: " INADDR_FMT "\n", INADDR_VA(frame->src_inaddr));
kdebug("\tdstaddr: " INADDR_FMT "\n", INADDR_VA(frame->dst_inaddr));
uint16_t checksum = 0;
for (size_t i = 0; i < sizeof(struct in_frame) / 2; ++i) {
checksum += ((uint16_t *) frame)[i];
}
uint16_t checksum = in_checksum_compute((uint8_t *) frame, htons(frame->length));
kdebug("IP checksum = %04x\n", checksum);
//if (checksum != 0xFFFF) {
// kwarn("Packet IP checksum mismatch\n");
// // Drop packet
// return;
//}
if (checksum != 0) {
kwarn("Packet IP checksum mismatch\n");
// Drop packet
return;
}
switch (frame->protocol) {
case 0x01:
icmp_handle_frame(dev, 0, frame, (struct icmp_frame *) frame->payload);
break;
case IN_PROTO_UDP:
udp_handle_frame(dev, 0, frame, (struct udp_frame *) frame->payload);
break;
+29
View File
@@ -0,0 +1,29 @@
#include "sys/net/netdev.h"
#include "sys/net/in.h"
#include "sys/heap.h"
void link_add_route_in(struct netdev *dev, uint32_t net, uint32_t mask, uint32_t gw) {
struct in_route *route = (struct in_route *) kmalloc(sizeof(struct in_route));
route->dev = dev;
route->network_inaddr = net;
route->mask = mask;
route->gateway = gw;
route->next = NULL;
if (dev->link.in_routes_tail) {
dev->link.in_routes_tail->next = route;
} else {
dev->link.in_routes_head = route;
}
dev->link.in_routes_tail = route;
}
struct in_route *link_find_route_in(struct netdev *dev, uint32_t dst_inaddr) {
for (struct in_route *route = dev->link.in_routes_head; route; route = route->next) {
if (route->network_inaddr == (dst_inaddr & route->mask)) {
return route;
}
}
return NULL;
}