summaryrefslogtreecommitdiff
path: root/src/user/app/ethdump/ether.c
blob: 3f6a40db6f0ed3953707f157e24fdf73b0c7c5a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <camellia/syscalls.h>
#include <stdlib.h>
#include <string.h>
#include "proto.h"
#include "util.h"

enum {
	DstMAC    =  0,
	SrcMAC    =  6,
	EtherType = 12,
	Payload   = 14,
};
struct queue_entry *ether_queue;

void ether_parse(const uint8_t *buf, size_t len) {
	struct ethernet ether = (struct ethernet){
		.src = buf + SrcMAC,
		.dst = buf + DstMAC,
		.type = nget16(buf + EtherType),
	};

	struct queue_entry **iter = &ether_queue;
	while (iter && *iter) {
		struct queue_entry *qe = *iter;
		_syscall_fs_respond(qe->h, buf, len, 0);
		/* remove entry */
		/* yes, doing it this way here doesn't make sense. i'm preparing for filtering */
		*iter = qe->next;
		free(qe);
	}

	switch (ether.type) {
		case ET_IPv4:
			ipv4_parse(buf + Payload, len - Payload, ether);
			break;
		case ET_ARP:
			arp_parse(buf + Payload, len - Payload);
			break;
	}
}

static const size_t fhoff = sizeof(size_t);
uint8_t *ether_start(size_t len, struct ethernet ether) {
	if (len < 60 - Payload) len = 60 - Payload;

	if (!ether.dst) eprintf("NULL ether.dst!"); // TODO arp? i guess?
	if (!ether.src) ether.src = &state.mac;

	uint8_t *buf = malloc(fhoff + Payload + len);
	memset(buf, 0, fhoff + Payload + len);
	*(size_t*)buf = len + Payload;
	memcpy(buf + fhoff + DstMAC, ether.dst, 6);
	memcpy(buf + fhoff + SrcMAC, ether.src, 6);
	nput16(buf + fhoff + EtherType, ether.type);
	return buf + fhoff + Payload;
}
void ether_finish(uint8_t *pkt) {
	uint8_t *buf = pkt - Payload - fhoff;
	size_t len = *(size_t*)buf;
	_syscall_write(state.raw_h, buf + fhoff, len, 0, 0);
	free(buf);
}