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