-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinet6.c
465 lines (391 loc) · 11.9 KB
/
inet6.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "inet6.h"
#include "netboot.h"
// Enable at your own risk. Some of these packet errors can be fairly
// common when the buffers start to overflow.
#if 0
#define BAD(n, ...) \
do { \
printf("error: "); \
printf(n, ##__VA_ARGS__); \
printf("\n"); \
return; \
} while (0)
#else
#define BAD(n, ...) \
do { \
return; \
} while (0)
#endif
// useful addresses
const ip6_addr ip6_ll_all_nodes = {
.x = {0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
};
const ip6_addr ip6_ll_all_routers = {
.x = {0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2},
};
// Convert MAC Address to IPv6 Link Local Address
// aa:bb:cc:dd:ee:ff => FF80::aabb:ccFF:FEdd:eeff
// bit 2 (U/L) of the mac is inverted
void ll6addr_from_mac(ip6_addr* _ip, const mac_addr* _mac) {
uint8_t* ip = _ip->x;
const uint8_t* mac = _mac->x;
memset(ip, 0, IP6_ADDR_LEN);
ip[0] = 0xFE;
ip[1] = 0x80;
memset(ip + 2, 0, 6);
ip[8] = mac[0] ^ 2;
ip[9] = mac[1];
ip[10] = mac[2];
ip[11] = 0xFF;
ip[12] = 0xFE;
ip[13] = mac[3];
ip[14] = mac[4];
ip[15] = mac[5];
}
// Convert MAC Address to IPv6 Solicit Neighbor Multicast Address
// aa:bb:cc:dd:ee:ff -> FF02::1:FFdd:eeff
void snmaddr_from_mac(ip6_addr* _ip, const mac_addr* _mac) {
uint8_t* ip = _ip->x;
const uint8_t* mac = _mac->x;
ip[0] = 0xFF;
ip[1] = 0x02;
memset(ip + 2, 0, 9);
ip[11] = 0x01;
ip[12] = 0xFF;
ip[13] = mac[3];
ip[14] = mac[4];
ip[15] = mac[5];
}
// Convert IPv6 Multicast Address to Ethernet Multicast Address
void multicast_from_ip6(mac_addr* _mac, const ip6_addr* _ip6) {
const uint8_t* ip = _ip6->x;
uint8_t* mac = _mac->x;
mac[0] = 0x33;
mac[1] = 0x33;
mac[2] = ip[12];
mac[3] = ip[13];
mac[4] = ip[14];
mac[5] = ip[15];
}
// ip6 stack configuration
mac_addr ll_mac_addr;
ip6_addr ll_ip6_addr;
mac_addr snm_mac_addr;
ip6_addr snm_ip6_addr;
// cache for the last source addresses we've seen
static mac_addr rx_mac_addr;
static ip6_addr rx_ip6_addr;
void ip6_init(void* macaddr) {
char tmp[IP6TOAMAX];
mac_addr all;
// save our ethernet MAC and synthesize link layer addresses
memcpy(&ll_mac_addr, macaddr, 6);
ll6addr_from_mac(&ll_ip6_addr, &ll_mac_addr);
snmaddr_from_mac(&snm_ip6_addr, &ll_mac_addr);
multicast_from_ip6(&snm_mac_addr, &snm_ip6_addr);
eth_add_mcast_filter(&snm_mac_addr);
multicast_from_ip6(&all, &ip6_ll_all_nodes);
eth_add_mcast_filter(&all);
printf("macaddr: %02x:%02x:%02x:%02x:%02x:%02x\n",
ll_mac_addr.x[0], ll_mac_addr.x[1], ll_mac_addr.x[2],
ll_mac_addr.x[3], ll_mac_addr.x[4], ll_mac_addr.x[5]);
printf("ip6addr: %s\n", ip6toa(tmp, &ll_ip6_addr));
printf("snmaddr: %s\n", ip6toa(tmp, &snm_ip6_addr));
}
mac_addr eth_addr(void) {
return ll_mac_addr;
}
static int resolve_ip6(mac_addr* _mac, const ip6_addr* _ip) {
const uint8_t* ip = _ip->x;
// Multicast addresses are a simple transform
if (ip[0] == 0xFF) {
multicast_from_ip6(_mac, _ip);
return 0;
}
// Trying to send to the IP that we last received a packet from?
// Assume their mac address has not changed
if (memcmp(_ip, &rx_ip6_addr, sizeof(rx_ip6_addr)) == 0) {
memcpy(_mac, &rx_mac_addr, sizeof(rx_mac_addr));
return 0;
}
// We don't know how to find peers or routers yet, so give up...
return -1;
}
static uint16_t checksum(const void* _data, size_t len, uint16_t _sum) {
uint32_t sum = _sum;
const uint16_t* data = _data;
while (len > 1) {
sum += *data++;
len -= 2;
}
if (len) {
sum += (*data & 0xFF);
}
while (sum > 0xFFFF) {
sum = (sum & 0xFFFF) + (sum >> 16);
}
return sum;
}
typedef struct {
uint8_t eth[16];
ip6_hdr ip6;
uint8_t data[0];
} ip6_pkt;
typedef struct {
uint8_t eth[16];
ip6_hdr ip6;
udp_hdr udp;
uint8_t data[0];
} udp_pkt;
static unsigned ip6_checksum(ip6_hdr* ip, unsigned type, size_t length) {
uint16_t sum;
// length and protocol field for pseudo-header
sum = checksum(&ip->length, 2, htons(type));
// src/dst for pseudo-header + payload
sum = checksum(ip->src, 32 + length, sum);
// 0 is illegal, so 0xffff remains 0xffff
if (sum != 0xffff) {
return ~sum;
} else {
return sum;
}
}
static int ip6_setup(ip6_pkt* p, const ip6_addr* daddr, size_t length, uint8_t type) {
mac_addr dmac;
if (resolve_ip6(&dmac, daddr))
return -1;
// ethernet header
memcpy(p->eth + 2, &dmac, ETH_ADDR_LEN);
memcpy(p->eth + 8, &ll_mac_addr, ETH_ADDR_LEN);
p->eth[14] = (ETH_IP6 >> 8) & 0xFF;
p->eth[15] = ETH_IP6 & 0xFF;
// ip6 header
p->ip6.ver_tc_flow = 0x60; // v=6, tc=0, flow=0
p->ip6.length = htons(length);
p->ip6.next_header = type;
p->ip6.hop_limit = 255;
memcpy(p->ip6.src, &ll_ip6_addr, sizeof(ip6_addr));
memcpy(p->ip6.dst, daddr, sizeof(ip6_addr));
return 0;
}
#define UDP6_MAX_PAYLOAD (ETH_MTU - ETH_HDR_LEN - IP6_HDR_LEN - UDP_HDR_LEN)
int udp6_send(const void* data, size_t dlen, const ip6_addr* daddr, uint16_t dport, uint16_t sport) {
size_t length = dlen + UDP_HDR_LEN;
udp_pkt* p = eth_get_buffer(ETH_MTU + 2);
if (p == NULL)
return -1;
if (dlen > UDP6_MAX_PAYLOAD) {
printf("Internal error: UDP write request is too long\n");
goto fail;
}
if (ip6_setup((void*)p, daddr, length, HDR_UDP)) {
printf("Error: ip6_setup failed!\n");
goto fail;
}
// udp header
p->udp.src_port = htons(sport);
p->udp.dst_port = htons(dport);
p->udp.length = htons(length);
p->udp.checksum = 0;
memcpy(p->data, data, dlen);
p->udp.checksum = ip6_checksum(&p->ip6, HDR_UDP, length);
return eth_send(p->eth + 2, ETH_HDR_LEN + IP6_HDR_LEN + length);
fail:
eth_put_buffer(p);
return -1;
}
#define ICMP6_MAX_PAYLOAD (ETH_MTU - ETH_HDR_LEN - IP6_HDR_LEN)
static int icmp6_send(const void* data, size_t length, const ip6_addr* daddr) {
ip6_pkt* p;
icmp6_hdr* icmp;
p = eth_get_buffer(ETH_MTU + 2);
if (p == NULL)
return -1;
if (length > ICMP6_MAX_PAYLOAD) {
printf("Internal error: ICMP write request is too long\n");
goto fail;
}
if (ip6_setup(p, daddr, length, HDR_ICMP6)) {
printf("Error: ip6_setup failed!\n");
goto fail;
}
icmp = (void*)p->data;
memcpy(icmp, data, length);
icmp->checksum = ip6_checksum(&p->ip6, HDR_ICMP6, length);
return eth_send(p->eth + 2, ETH_HDR_LEN + IP6_HDR_LEN + length);
fail:
eth_put_buffer(p);
return -1;
}
void udp6_recv(ip6_hdr* ip, void* _data, size_t len) {
udp_hdr* udp = _data;
uint16_t sum, n;
if (len < UDP_HDR_LEN)
BAD("Bogus Header Len");
if (udp->checksum == 0)
BAD("Missing checksum");
if (udp->checksum == 0xFFFF)
udp->checksum = 0;
sum = checksum(&ip->length, 2, htons(HDR_UDP));
sum = checksum(ip->src, 32 + len, sum);
if (sum != 0xFFFF)
BAD("Checksum Incorrect");
n = ntohs(udp->length);
if (n < UDP_HDR_LEN)
BAD("Bogus Header Len");
if (n > len)
BAD("Packet Too Short");
len = n - UDP_HDR_LEN;
uint16_t dport = ntohs(udp->dst_port);
uint16_t sport = ntohs(udp->src_port);
switch (dport) {
case NB_SERVER_PORT:
netboot_recv((uint8_t*)_data + UDP_HDR_LEN, len, (void*)ip->src, sport);
break;
case NB_TFTP_INCOMING_PORT:
case NB_TFTP_OUTGOING_PORT:
tftp_recv((uint8_t*)_data + UDP_HDR_LEN, len, (void*)ip->dst, dport, (void*)ip->src, sport);
break;
default:
// Ignore
return;
}
}
void icmp6_recv(ip6_hdr* ip, void* _data, size_t len) {
icmp6_hdr* icmp = _data;
uint16_t sum;
if (icmp->checksum == 0)
BAD("Checksum Invalid");
if (icmp->checksum == 0xFFFF)
icmp->checksum = 0;
sum = checksum(&ip->length, 2, htons(HDR_ICMP6));
sum = checksum(ip->src, 32 + len, sum);
if (sum != 0xFFFF)
BAD("Checksum Incorrect");
if (icmp->type == ICMP6_NDP_N_SOLICIT) {
ndp_n_hdr* ndp = _data;
struct {
ndp_n_hdr hdr;
uint8_t opt[8];
} msg;
if (len < sizeof(ndp_n_hdr))
BAD("Bogus NDP Message");
if (ndp->code != 0)
BAD("Bogus NDP Code");
if (memcmp(ndp->target, &ll_ip6_addr, IP6_ADDR_LEN))
BAD("NDP Not For Me");
msg.hdr.type = ICMP6_NDP_N_ADVERTISE;
msg.hdr.code = 0;
msg.hdr.checksum = 0;
msg.hdr.flags = 0x60; // (S)olicited and (O)verride flags
memcpy(msg.hdr.target, &ll_ip6_addr, IP6_ADDR_LEN);
msg.opt[0] = NDP_N_TGT_LL_ADDR;
msg.opt[1] = 1;
memcpy(msg.opt + 2, &ll_mac_addr, ETH_ADDR_LEN);
icmp6_send(&msg, sizeof(msg), (void*)ip->src);
return;
}
if (icmp->type == ICMP6_ECHO_REQUEST) {
icmp->checksum = 0;
icmp->type = ICMP6_ECHO_REPLY;
icmp6_send(_data, len, (void*)ip->src);
return;
}
BAD("ICMP6 Unhandled %d", icmp->type);
}
void eth_recv(void* _data, size_t len) {
uint8_t* data = _data;
ip6_hdr* ip;
uint32_t n;
if (len < (ETH_HDR_LEN + IP6_HDR_LEN))
BAD("Bogus Header Len");
if (data[12] != (ETH_IP6 >> 8) || data[13] != (ETH_IP6 & 0xFF))
BAD("Not IP6");
ip = (void*)(data + ETH_HDR_LEN);
data += (ETH_HDR_LEN + IP6_HDR_LEN);
len -= (ETH_HDR_LEN + IP6_HDR_LEN);
// require v6
if ((ip->ver_tc_flow & 0xF0) != 0x60)
BAD("Unknown IP6 Version");
// ensure length is sane
n = ntohs(ip->length);
if (n > len)
BAD("IP6 Length Mismatch %d %zu", n, len);
// ignore any trailing data in the ethernet frame
len = n;
// require that we are the destination
if (memcmp(&ll_ip6_addr, ip->dst, IP6_ADDR_LEN) &&
memcmp(&snm_ip6_addr, ip->dst, IP6_ADDR_LEN) &&
memcmp(&ip6_ll_all_nodes, ip->dst, IP6_ADDR_LEN)) {
return;
}
// stash the sender's info to simplify replies
memcpy(&rx_mac_addr, (uint8_t*)_data + 6, ETH_ADDR_LEN);
memcpy(&rx_ip6_addr, ip->src, IP6_ADDR_LEN);
if (ip->next_header == HDR_ICMP6) {
icmp6_recv(ip, data, len);
return;
}
if (ip->next_header == HDR_UDP) {
udp6_recv(ip, data, len);
return;
}
BAD("Unhandled IP6 %d", ip->next_header);
}
char* ip6toa(char* _out, void* ip6addr) {
const uint8_t* x = ip6addr;
const uint8_t* end = x + 16;
char* out = _out;
uint16_t n;
n = (x[0] << 8) | x[1];
while ((n == 0) && (x < end)) {
x += 2;
n = (x[0] << 8) | x[1];
}
if ((end - x) < 16) {
if (end == x) {
// all 0s - special case
sprintf(out, "::");
return _out;
}
// we consumed some number of leading 0s
out += sprintf(out, ":");
while (x < end) {
out += sprintf(out, ":%x", n);
x += 2;
n = (x[0] << 8) | x[1];
}
return _out;
}
while (x < (end - 2)) {
out += sprintf(out, "%x:", n);
x += 2;
n = (x[0] << 8) | x[1];
if (n == 0)
goto middle_zeros;
}
out += sprintf(out, "%x", n);
return _out;
middle_zeros:
while ((n == 0) && (x < end)) {
x += 2;
n = (x[0] << 8) | x[1];
}
if (x == end) {
out += sprintf(out, ":");
return _out;
}
out += sprintf(out, ":%x", n);
while (x < (end - 2)) {
x += 2;
n = (x[0] << 8) | x[1];
out += sprintf(out, ":%x", n);
}
return _out;
}