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