1 /*
2 * Copyright (c) 2014 Chris Anderson
3 * Copyright (c) 2014 Brian Swetland
4 *
5 * Use of this source code is governed by a MIT-style
6 * license that can be found in the LICENSE file or at
7 * https://opensource.org/licenses/MIT
8 */
9
10 #include "minip-internal.h"
11
12 #include <assert.h>
13 #include <lk/err.h>
14 #include <stdio.h>
15 #include <lk/debug.h>
16 #include <endian.h>
17 #include <errno.h>
18 #include <iovec.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <lk/trace.h>
22 #include <malloc.h>
23 #include <lk/list.h>
24 #include <lk/init.h>
25 #include <kernel/event.h>
26 #include <kernel/thread.h>
27
28 // TODO
29 // 1. Tear endian code out into something that flips words before/after tx/rx calls
30
31 #define LOCAL_TRACE 0
32 static uint32_t minip_ip = IPV4_NONE;
33 static uint32_t minip_netmask = IPV4_NONE;
34 static uint32_t minip_broadcast = IPV4_BCAST;
35 static uint32_t minip_gateway = IPV4_NONE;
36
37 static const uint8_t broadcast_mac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
38 static uint8_t minip_mac[6] = {0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC};
39
40 static char minip_hostname[32] = "";
41
42 static volatile bool minip_configured = false;
43 static event_t minip_configured_event = EVENT_INITIAL_VALUE(minip_configured_event, false, 0);
44
45 /* This function is called by minip to send packets */
46 tx_func_t minip_tx_handler;
47 void *minip_tx_arg;
48
49 static void dump_mac_address(const uint8_t *mac);
50 static void dump_ipv4_addr(uint32_t addr);
51
52 /* if all the important configuration bits are set, signal that we're configured */
check_and_set_configured(void)53 static void check_and_set_configured(void) {
54 if (minip_ip == IPV4_NONE) return;
55 if (minip_netmask == IPV4_NONE) return;
56 if (minip_broadcast == IPV4_BCAST) return;
57 // minip_gateway doesn't have to be set
58 if (minip_mac[0] == 0xcc &&
59 minip_mac[1] == 0xcc &&
60 minip_mac[2] == 0xcc &&
61 minip_mac[3] == 0xcc &&
62 minip_mac[4] == 0xcc &&
63 minip_mac[5] == 0xcc) return;
64
65 // we're configured
66 printf("MINIP: setting configured state\n");
67 minip_set_configured();
68 }
69
minip_set_hostname(const char * name)70 void minip_set_hostname(const char *name) {
71 strlcpy(minip_hostname, name, sizeof(minip_hostname));
72 check_and_set_configured();
73 }
74
minip_get_hostname(void)75 const char *minip_get_hostname(void) {
76 return minip_hostname;
77 }
78
compute_broadcast_address(void)79 static void compute_broadcast_address(void) {
80 minip_broadcast = (minip_ip & minip_netmask) | (IPV4_BCAST & ~minip_netmask);
81 }
82
minip_get_macaddr(uint8_t * addr)83 void minip_get_macaddr(uint8_t *addr) {
84 mac_addr_copy(addr, minip_mac);
85 }
86
minip_get_ipaddr(void)87 uint32_t minip_get_ipaddr(void) {
88 return minip_ip;
89 }
90
minip_set_ipaddr(const uint32_t addr)91 void minip_set_ipaddr(const uint32_t addr) {
92 minip_ip = addr;
93 compute_broadcast_address();
94 check_and_set_configured();
95 }
96
minip_get_broadcast(void)97 uint32_t minip_get_broadcast(void) {
98 return minip_broadcast;
99 }
100
minip_get_netmask(void)101 uint32_t minip_get_netmask(void) {
102 return minip_netmask;
103 }
104
minip_set_netmask(const uint32_t netmask)105 void minip_set_netmask(const uint32_t netmask) {
106 minip_netmask = netmask;
107 compute_broadcast_address();
108 check_and_set_configured();
109 }
110
minip_get_gateway(void)111 uint32_t minip_get_gateway(void) {
112 return minip_gateway;
113 }
114
minip_set_gateway(const uint32_t addr)115 void minip_set_gateway(const uint32_t addr) {
116 minip_gateway = addr;
117 // TODO: check that it is reacheable on local network
118 check_and_set_configured();
119 }
120
minip_set_configured(void)121 void minip_set_configured(void) {
122 minip_configured = true;
123 event_signal(&minip_configured_event, true);
124 }
125
minip_is_configured(void)126 bool minip_is_configured(void) {
127 return minip_configured;
128 }
129
minip_wait_for_configured(lk_time_t timeout)130 status_t minip_wait_for_configured(lk_time_t timeout) {
131 return event_wait_timeout(&minip_configured_event, timeout);
132 }
133
gen_random_mac_address(uint8_t * mac_addr)134 void gen_random_mac_address(uint8_t *mac_addr) {
135 for (size_t i = 0; i < 6; i++) {
136 mac_addr[i] = rand() & 0xff;
137 }
138 /* unicast and locally administered */
139 mac_addr[0] &= ~(1<<0);
140 mac_addr[0] |= (1<<1);
141 }
142
minip_start_static(uint32_t ip,uint32_t mask,uint32_t gateway)143 void minip_start_static(uint32_t ip, uint32_t mask, uint32_t gateway) {
144 minip_set_ipaddr(ip);
145 minip_set_netmask(mask);
146 minip_set_gateway(gateway);
147 }
148
minip_set_eth(tx_func_t tx_handler,void * tx_arg,const uint8_t * macaddr)149 void minip_set_eth(tx_func_t tx_handler, void *tx_arg, const uint8_t *macaddr) {
150 LTRACEF("handler %p, arg %p, macaddr %p\n", tx_handler, tx_arg, macaddr);
151
152 DEBUG_ASSERT(minip_tx_handler == NULL);
153 DEBUG_ASSERT(minip_tx_arg == NULL);
154 // TODO: assert mac address is not already set
155
156 // set up the low level driver handler
157 minip_tx_handler = tx_handler;
158 minip_tx_arg = tx_arg;
159
160 mac_addr_copy(minip_mac, macaddr);
161 }
162
ipv4_payload_len(struct ipv4_hdr * pkt)163 static uint16_t ipv4_payload_len(struct ipv4_hdr *pkt) {
164 return (pkt->len - ((pkt->ver_ihl >> 4) * 5));
165 }
166
minip_build_mac_hdr(struct eth_hdr * pkt,const uint8_t * dst,uint16_t type)167 void minip_build_mac_hdr(struct eth_hdr *pkt, const uint8_t *dst, uint16_t type) {
168 mac_addr_copy(pkt->dst_mac, dst);
169 mac_addr_copy(pkt->src_mac, minip_mac);
170 pkt->type = htons(type);
171 }
172
minip_build_ipv4_hdr(struct ipv4_hdr * ipv4,uint32_t dst,uint8_t proto,uint16_t len)173 void minip_build_ipv4_hdr(struct ipv4_hdr *ipv4, uint32_t dst, uint8_t proto, uint16_t len) {
174 ipv4->ver_ihl = 0x45;
175 ipv4->dscp_ecn = 0;
176 ipv4->len = htons(20 + len); // 5 * 4 from ihl, plus payload length
177 ipv4->id = 0;
178 ipv4->flags_frags = 0x40; // no offset, no fragments
179 ipv4->ttl = 64;
180 ipv4->proto = proto;
181 ipv4->dst_addr = dst;
182 ipv4->src_addr = minip_ip;
183
184 /* This may be unnecessary if the controller supports checksum offloading */
185 ipv4->chksum = 0;
186 ipv4->chksum = rfc1701_chksum((uint8_t *) ipv4, sizeof(struct ipv4_hdr));
187 }
188
send_arp_request(uint32_t addr)189 static int send_arp_request(uint32_t addr) {
190 pktbuf_t *p;
191 struct eth_hdr *eth;
192 struct arp_pkt *arp;
193
194 if ((p = pktbuf_alloc()) == NULL) {
195 return -1;
196 }
197
198 eth = pktbuf_prepend(p, sizeof(struct eth_hdr));
199 arp = pktbuf_append(p, sizeof(struct arp_pkt));
200 minip_build_mac_hdr(eth, bcast_mac, ETH_TYPE_ARP);
201
202 arp->htype = htons(0x0001);
203 arp->ptype = htons(0x0800);
204 arp->hlen = 6;
205 arp->plen = 4;
206 arp->oper = htons(ARP_OPER_REQUEST);
207 arp->spa = minip_ip;
208 arp->tpa = addr;
209 mac_addr_copy(arp->sha, minip_mac);
210 mac_addr_copy(arp->tha, bcast_mac);
211
212 minip_tx_handler(minip_tx_arg, p);
213 return 0;
214 }
215
handle_arp_timeout_cb(void * arg)216 static void handle_arp_timeout_cb(void *arg) {
217 *(bool *)arg = true;
218 }
219
get_dest_mac(uint32_t host)220 const uint8_t *get_dest_mac(uint32_t host) {
221 uint8_t *dst_mac = NULL;
222 bool arp_timeout = false;
223 net_timer_t arp_timeout_timer;
224
225 if (host == IPV4_BCAST) {
226 return bcast_mac;
227 }
228
229 dst_mac = arp_cache_lookup(host);
230 if (dst_mac == NULL) {
231 send_arp_request(host);
232 memset(&arp_timeout_timer, 0, sizeof(arp_timeout_timer));
233 net_timer_set(&arp_timeout_timer, handle_arp_timeout_cb, &arp_timeout, 100);
234 while (!arp_timeout) {
235 dst_mac = arp_cache_lookup(host);
236 if (dst_mac) {
237 net_timer_cancel(&arp_timeout_timer);
238 break;
239 }
240 }
241 }
242
243 return dst_mac;
244 }
245
minip_ipv4_send(pktbuf_t * p,uint32_t dest_addr,uint8_t proto)246 status_t minip_ipv4_send(pktbuf_t *p, uint32_t dest_addr, uint8_t proto) {
247 status_t ret = 0;
248 size_t data_len = p->dlen;
249 const uint8_t *dst_mac;
250
251 struct ipv4_hdr *ip = pktbuf_prepend(p, sizeof(struct ipv4_hdr));
252 struct eth_hdr *eth = pktbuf_prepend(p, sizeof(struct eth_hdr));
253
254 // are we sending a broadcast packet?
255 if (dest_addr == IPV4_BCAST || dest_addr == minip_broadcast) {
256 dst_mac = bcast_mac;
257 goto ready;
258 }
259
260 // is this a local subnet packet or do we need to send to the router?
261 uint32_t target_addr = dest_addr;
262 if ((dest_addr & minip_netmask) != (minip_ip & minip_netmask)) {
263 // need to use the gateway
264 if (minip_gateway == IPV4_NONE) {
265 return ERR_NOT_FOUND; // TODO: better error code
266 }
267
268 target_addr = minip_gateway;
269 }
270
271 dst_mac = arp_get_dest_mac(target_addr);
272 if (!dst_mac) {
273 pktbuf_free(p, true);
274 ret = -EHOSTUNREACH;
275 goto err;
276 }
277
278 ready:
279 if (LOCAL_TRACE) {
280 printf("sending ipv4\n");
281 }
282
283 minip_build_mac_hdr(eth, dst_mac, ETH_TYPE_IPV4);
284 minip_build_ipv4_hdr(ip, dest_addr, proto, data_len);
285
286 minip_tx_handler(minip_tx_arg, p);
287
288 err:
289 return ret;
290 }
291
292 /* Swap the dst/src ip addresses and send an ICMP ECHO REPLY with the same payload.
293 * According to spec the data portion doesn't matter, but ping itself validates that
294 * the payload is identical
295 */
send_ping_reply(uint32_t ipaddr,struct icmp_pkt * req,size_t reqdatalen)296 static void send_ping_reply(uint32_t ipaddr, struct icmp_pkt *req, size_t reqdatalen) {
297 pktbuf_t *p;
298 size_t len;
299 struct eth_hdr *eth;
300 struct ipv4_hdr *ip;
301 struct icmp_pkt *icmp;
302
303 if ((p = pktbuf_alloc()) == NULL) {
304 return;
305 }
306
307 icmp = pktbuf_prepend(p, sizeof(struct icmp_pkt));
308 ip = pktbuf_prepend(p, sizeof(struct ipv4_hdr));
309 eth = pktbuf_prepend(p, sizeof(struct eth_hdr));
310 pktbuf_append_data(p, req->data, reqdatalen);
311
312 len = sizeof(struct icmp_pkt) + reqdatalen;
313
314 minip_build_mac_hdr(eth, arp_cache_lookup(ipaddr), ETH_TYPE_IPV4);
315 minip_build_ipv4_hdr(ip, ipaddr, IP_PROTO_ICMP, len);
316
317 icmp->type = ICMP_ECHO_REPLY;
318 icmp->code = 0;
319 memcpy(icmp->hdr_data, req->hdr_data, sizeof(icmp->hdr_data));
320 icmp->chksum = 0;
321 icmp->chksum = rfc1701_chksum((uint8_t *) icmp, len);
322
323 minip_tx_handler(minip_tx_arg, p);
324 }
325
dump_ipv4_addr(uint32_t addr)326 static void dump_ipv4_addr(uint32_t addr) {
327 const uint8_t *a = (void *)&addr;
328
329 printf("%hhu.%hhu.%hhu.%hhu", a[0], a[1], a[2], a[3]);
330 }
331
dump_ipv4_packet(const struct ipv4_hdr * ip)332 static void dump_ipv4_packet(const struct ipv4_hdr *ip) {
333 printf("IP ");
334 dump_ipv4_addr(ip->src_addr);
335 printf(" -> ");
336 dump_ipv4_addr(ip->dst_addr);
337 printf(" hlen 0x%x, prot 0x%x, cksum 0x%x, len 0x%x, ident 0x%x, frag offset 0x%x\n",
338 (ip->ver_ihl & 0xf) * 4, ip->proto, ntohs(ip->chksum), ntohs(ip->len), ntohs(ip->id), ntohs(ip->flags_frags) & 0x1fff);
339 }
340
handle_ipv4_packet(pktbuf_t * p,const uint8_t * src_mac)341 __NO_INLINE static void handle_ipv4_packet(pktbuf_t *p, const uint8_t *src_mac) {
342 struct ipv4_hdr *ip;
343
344 ip = (struct ipv4_hdr *)p->data;
345 if (p->dlen < sizeof(struct ipv4_hdr))
346 return;
347
348 /* print packets for us */
349 if (LOCAL_TRACE) {
350 dump_ipv4_packet(ip);
351 }
352
353 /* reject bad packets */
354 if (((ip->ver_ihl >> 4) & 0xf) != 4) {
355 /* not version 4 */
356 LTRACEF("REJECT: not version 4\n");
357 return;
358 }
359
360 /* do we have enough buffer to hold the full header + options? */
361 size_t header_len = (ip->ver_ihl & 0xf) * 4;
362 if (p->dlen < header_len) {
363 LTRACEF("REJECT: not enough buffer to hold header\n");
364 return;
365 }
366
367 /* compute checksum */
368 if (rfc1701_chksum((void *)ip, header_len) != 0) {
369 /* bad checksum */
370 LTRACEF("REJECT: bad checksum\n");
371 return;
372 }
373
374 /* is the pkt_buf large enough to hold the length the header says the packet is? */
375 if (htons(ip->len) > p->dlen) {
376 LTRACEF("REJECT: packet exceeds size of buffer (header %d, dlen %d)\n", htons(ip->len), p->dlen);
377 return;
378 }
379
380 /* trim any excess bytes at the end of the packet */
381 if (p->dlen > htons(ip->len)) {
382 pktbuf_consume_tail(p, p->dlen - htons(ip->len));
383 }
384
385 /* remove the header from the front of the packet_buf */
386 if (pktbuf_consume(p, header_len) == NULL) {
387 return;
388 }
389
390 /* the packet is good, we can use it to populate our arp cache */
391 arp_cache_update(ip->src_addr, src_mac);
392
393 /* see if it's for us */
394 if (ip->dst_addr != IPV4_BCAST) {
395 if (minip_ip != IPV4_NONE && ip->dst_addr != minip_ip && ip->dst_addr != minip_broadcast) {
396 LTRACEF("REJECT: for another host\n");
397 return;
398 }
399 }
400
401 /* We only handle UDP and ECHO REQUEST */
402 switch (ip->proto) {
403 case IP_PROTO_ICMP: {
404 struct icmp_pkt *icmp;
405 if ((icmp = pktbuf_consume(p, sizeof(struct icmp_pkt))) == NULL) {
406 break;
407 }
408 if (icmp->type == ICMP_ECHO_REQUEST) {
409 send_ping_reply(ip->src_addr, icmp, p->dlen);
410 }
411 }
412 break;
413
414 case IP_PROTO_UDP:
415 udp_input(p, ip->src_addr);
416 break;
417
418 case IP_PROTO_TCP:
419 tcp_input(p, ip->src_addr, ip->dst_addr);
420 break;
421 }
422 }
423
handle_arp_pkt(pktbuf_t * p)424 __NO_INLINE static int handle_arp_pkt(pktbuf_t *p) {
425 struct eth_hdr *eth;
426 struct arp_pkt *arp;
427
428 eth = (void *) (p->data - sizeof(struct eth_hdr));
429
430 if ((arp = pktbuf_consume(p, sizeof(struct arp_pkt))) == NULL) {
431 return -1;
432 }
433
434 switch (ntohs(arp->oper)) {
435 case ARP_OPER_REQUEST: {
436 pktbuf_t *rp;
437 struct eth_hdr *reth;
438 struct arp_pkt *rarp;
439
440 if (memcmp(&arp->tpa, &minip_ip, sizeof(minip_ip)) == 0) {
441 if ((rp = pktbuf_alloc()) == NULL) {
442 break;
443 }
444
445 reth = pktbuf_prepend(rp, sizeof(struct eth_hdr));
446 rarp = pktbuf_append(rp, sizeof(struct arp_pkt));
447
448 // Eth header
449 minip_build_mac_hdr(reth, eth->src_mac, ETH_TYPE_ARP);
450
451 // ARP packet
452 rarp->oper = htons(ARP_OPER_REPLY);
453 rarp->htype = htons(0x0001);
454 rarp->ptype = htons(0x0800);
455 rarp->hlen = 6;
456 rarp->plen = 4;
457 mac_addr_copy(rarp->sha, minip_mac);
458 rarp->spa = minip_ip;
459 mac_addr_copy(rarp->tha, arp->sha);
460 rarp->tpa = arp->spa;
461
462 minip_tx_handler(minip_tx_arg, rp);
463 }
464 }
465 break;
466
467 case ARP_OPER_REPLY: {
468 uint32_t addr;
469 memcpy(&addr, &arp->spa, sizeof(addr)); // unaligned word
470 arp_cache_update(addr, arp->sha);
471 }
472 break;
473 }
474
475 return 0;
476 }
477
dump_eth_packet(const struct eth_hdr * eth)478 static void dump_eth_packet(const struct eth_hdr *eth) {
479 printf("ETH src ");
480 dump_mac_address(eth->src_mac);
481 printf(" dst ");
482 dump_mac_address(eth->dst_mac);
483 printf(" type 0x%hx\n", htons(eth->type));
484 }
485
minip_rx_driver_callback(pktbuf_t * p)486 void minip_rx_driver_callback(pktbuf_t *p) {
487 struct eth_hdr *eth;
488
489 if ((eth = (void *) pktbuf_consume(p, sizeof(struct eth_hdr))) == NULL) {
490 return;
491 }
492
493 if (LOCAL_TRACE) {
494 dump_eth_packet(eth);
495 }
496
497 if (memcmp(eth->dst_mac, minip_mac, 6) != 0 &&
498 memcmp(eth->dst_mac, broadcast_mac, 6) != 0) {
499 /* not for us */
500 return;
501 }
502
503 switch (htons(eth->type)) {
504 case ETH_TYPE_IPV4:
505 LTRACEF("ipv4 pkt\n");
506 handle_ipv4_packet(p, eth->src_mac);
507 break;
508
509 case ETH_TYPE_ARP:
510 LTRACEF("arp pkt\n");
511 handle_arp_pkt(p);
512 break;
513 }
514 }
515
dump_mac_address(const uint8_t * mac)516 void dump_mac_address(const uint8_t *mac) {
517 printf("%02x:%02x:%02x:%02x:%02x:%02x",
518 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
519 }
520
minip_parse_ipaddr(const char * ipaddr_str,size_t len)521 uint32_t minip_parse_ipaddr(const char *ipaddr_str, size_t len) {
522 uint8_t ip[4] = { 0, 0, 0, 0 };
523 uint8_t pos = 0, i = 0;
524
525 while (pos < len) {
526 char c = ipaddr_str[pos];
527 if (c == '.') {
528 i++;
529 } else if (c == '\0') {
530 break;
531 } else {
532 ip[i] *= 10;
533 ip[i] += c - '0';
534 }
535 pos++;
536 }
537
538 return IPV4_PACK(ip);
539 }
540
541 // printf the ip address passed in
printip(uint32_t x)542 void printip(uint32_t x) {
543 union {
544 u32 u;
545 u8 b[4];
546 } ip;
547 ip.u = x;
548 printf("%d.%d.%d.%d", ip.b[0], ip.b[1], ip.b[2], ip.b[3]);
549 }
550
printip_named(const char * s,uint32_t x)551 void printip_named(const char *s, uint32_t x) {
552 printf("%s ", s);
553 printip(x);
554 }
555
556 // run static initialization
minip_init(uint level)557 static void minip_init(uint level) {
558 arp_cache_init();
559 net_timer_init();
560 }
561
562
563 LK_INIT_HOOK(minip, minip_init, LK_INIT_LEVEL_THREADING);
564