forked from pandax381/microps
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ether.c
141 lines (127 loc) · 3.87 KB
/
ether.c
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "util.h"
#include "net.h"
#include "ether.h"
struct ether_hdr {
uint8_t dst[ETHER_ADDR_LEN];
uint8_t src[ETHER_ADDR_LEN];
uint16_t type;
};
const uint8_t ETHER_ADDR_ANY[ETHER_ADDR_LEN] = {"\x00\x00\x00\x00\x00\x00"};
const uint8_t ETHER_ADDR_BROADCAST[ETHER_ADDR_LEN] = {"\xff\xff\xff\xff\xff\xff"};
int
ether_addr_pton(const char *p, uint8_t *n)
{
int index;
char *ep;
long val;
if (!p || !n) {
return -1;
}
for (index = 0; index < ETHER_ADDR_LEN; index++) {
val = strtol(p, &ep, 16);
if (ep == p || val < 0 || val > 0xff || (index < ETHER_ADDR_LEN - 1 && *ep != ':')) {
break;
}
n[index] = (uint8_t)val;
p = ep + 1;
}
if (index != ETHER_ADDR_LEN || *ep != '\0') {
return -1;
}
return 0;
}
static const char *
ether_type_ntoa(uint16_t type)
{
switch (ntoh16(type)) {
case ETHER_TYPE_IP:
return "IP";
case ETHER_TYPE_ARP:
return "ARP";
case ETHER_TYPE_IPV6:
return "IPv6";
}
return "UNKNOWN";
}
char *
ether_addr_ntop(const uint8_t *n, char *p, size_t size)
{
if (!n || !p) {
return NULL;
}
snprintf(p, size, "%02x:%02x:%02x:%02x:%02x:%02x", n[0], n[1], n[2], n[3], n[4], n[5]);
return p;
}
static void
ether_dump(const uint8_t *frame, size_t flen)
{
struct ether_hdr *hdr;
char addr[ETHER_ADDR_STR_LEN];
hdr = (struct ether_hdr *)frame;
flockfile(stderr);
fprintf(stderr, " src: %s\n", ether_addr_ntop(hdr->src, addr, sizeof(addr)));
fprintf(stderr, " dst: %s\n", ether_addr_ntop(hdr->dst, addr, sizeof(addr)));
fprintf(stderr, " type: 0x%04x (%s)\n", ntoh16(hdr->type), ether_type_ntoa(hdr->type));
#ifdef HEXDUMP
hexdump(stderr, frame, flen);
#endif
funlockfile(stderr);
}
int
ether_transmit_helper(struct net_device *dev, uint16_t type, const uint8_t *data, size_t len, const void *dst, ssize_t (*callback)(struct net_device *dev, const uint8_t *data, size_t len))
{
uint8_t frame[ETHER_FRAME_SIZE_MAX] = {};
struct ether_hdr *hdr;
size_t flen, pad = 0;
hdr = (struct ether_hdr *)frame;
memcpy(hdr->dst, dst, ETHER_ADDR_LEN);
memcpy(hdr->src, dev->addr, ETHER_ADDR_LEN);
hdr->type = hton16(type);
memcpy(hdr + 1, data, len);
if (len < ETHER_PAYLOAD_SIZE_MIN) {
pad = ETHER_PAYLOAD_SIZE_MIN - len;
}
flen = sizeof(*hdr) + len + pad;
debugf("dev=%s, type=%s(0x%04x), len=%zu", dev->name, ether_type_ntoa(hdr->type), type, flen);
ether_dump(frame, flen);
return callback(dev, frame, flen) == (ssize_t)flen ? 0 : -1;
}
int
ether_poll_helper(struct net_device *dev, ssize_t (*callback)(struct net_device *dev, uint8_t *buf, size_t size))
{
uint8_t frame[ETHER_FRAME_SIZE_MAX];
ssize_t flen;
struct ether_hdr *hdr;
uint16_t type;
flen = callback(dev, frame, sizeof(frame));
if (flen < (ssize_t)sizeof(*hdr)) {
errorf("input data is too short");
return -1;
}
hdr = (struct ether_hdr *)frame;
if (memcmp(dev->addr, hdr->dst, ETHER_ADDR_LEN) != 0) {
if (memcmp(ETHER_ADDR_BROADCAST, hdr->dst, ETHER_ADDR_LEN) != 0) {
/* for other host */
return -1;
}
}
type = ntoh16(hdr->type);
debugf("dev=%s, type=%s(0x%04x), len=%zu", dev->name, ether_type_ntoa(hdr->type), type, flen);
ether_dump(frame, flen);
return net_input_handler(type, (uint8_t *)(hdr + 1), flen - sizeof(*hdr), dev);
}
void
ether_setup_helper(struct net_device *dev)
{
dev->type = NET_DEVICE_TYPE_ETHERNET;
dev->mtu = ETHER_PAYLOAD_SIZE_MAX;
dev->flags = (NET_DEVICE_FLAG_BROADCAST | NET_DEVICE_FLAG_NEED_ARP);
dev->hlen = ETHER_HDR_SIZE;
dev->alen = ETHER_ADDR_LEN;
memcpy(dev->broadcast, ETHER_ADDR_BROADCAST, ETHER_ADDR_LEN);
}