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 <lk/err.h>
13 #include <stdio.h>
14 #include <lk/debug.h>
15 #include <endian.h>
16 #include <errno.h>
17 #include <iovec.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <lk/trace.h>
21 #include <malloc.h>
22 #include <lk/list.h>
23 #include <kernel/thread.h>
24 
25 static struct list_node arp_list = LIST_INITIAL_VALUE(arp_list);
26 
27 // TODO
28 // 1. Tear endian code out into something that flips words before/after tx/rx calls
29 
30 #define LOCAL_TRACE 0
31 static uint32_t minip_ip      = IPV4_NONE;
32 static uint32_t minip_netmask = IPV4_NONE;
33 static uint32_t minip_broadcast = IPV4_BCAST;
34 static uint32_t minip_gateway = IPV4_NONE;
35 
36 static const uint8_t broadcast_mac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
37 static uint8_t minip_mac[6] = {0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC};
38 
39 static char minip_hostname[32] = "";
40 
41 static void dump_mac_address(const uint8_t *mac);
42 static void dump_ipv4_addr(uint32_t addr);
43 
minip_set_hostname(const char * name)44 void minip_set_hostname(const char *name) {
45     strlcpy(minip_hostname, name, sizeof(minip_hostname));
46 }
47 
minip_get_hostname(void)48 const char *minip_get_hostname(void) {
49     return minip_hostname;
50 }
51 
compute_broadcast_address(void)52 static void compute_broadcast_address(void) {
53     minip_broadcast = (minip_ip & minip_netmask) | (IPV4_BCAST & ~minip_netmask);
54 }
55 
minip_get_macaddr(uint8_t * addr)56 void minip_get_macaddr(uint8_t *addr) {
57     mac_addr_copy(addr, minip_mac);
58 }
59 
minip_set_macaddr(const uint8_t * addr)60 void minip_set_macaddr(const uint8_t *addr) {
61     mac_addr_copy(minip_mac, addr);
62 }
63 
minip_get_ipaddr(void)64 uint32_t minip_get_ipaddr(void) {
65     return minip_ip;
66 }
67 
minip_set_ipaddr(const uint32_t addr)68 void minip_set_ipaddr(const uint32_t addr) {
69     minip_ip = addr;
70     compute_broadcast_address();
71 }
72 
gen_random_mac_address(uint8_t * mac_addr)73 void gen_random_mac_address(uint8_t *mac_addr) {
74     for (size_t i = 0; i < 6; i++) {
75         mac_addr[i] = rand() & 0xff;
76     }
77     /* unicast and locally administered */
78     mac_addr[0] &= ~(1<<0);
79     mac_addr[0] |= (1<<1);
80 }
81 
82 /* This function is called by minip to send packets */
83 tx_func_t minip_tx_handler;
84 void *minip_tx_arg;
85 
minip_init(tx_func_t tx_handler,void * tx_arg,uint32_t ip,uint32_t mask,uint32_t gateway)86 void minip_init(tx_func_t tx_handler, void *tx_arg,
87                 uint32_t ip, uint32_t mask, uint32_t gateway) {
88     minip_tx_handler = tx_handler;
89     minip_tx_arg = tx_arg;
90 
91     minip_ip = ip;
92     minip_netmask = mask;
93     minip_gateway = gateway;
94     compute_broadcast_address();
95 
96     arp_cache_init();
97     net_timer_init();
98 }
99 
ipv4_payload_len(struct ipv4_hdr * pkt)100 static uint16_t ipv4_payload_len(struct ipv4_hdr *pkt) {
101     return (pkt->len - ((pkt->ver_ihl >> 4) * 5));
102 }
103 
minip_build_mac_hdr(struct eth_hdr * pkt,const uint8_t * dst,uint16_t type)104 void minip_build_mac_hdr(struct eth_hdr *pkt, const uint8_t *dst, uint16_t type) {
105     mac_addr_copy(pkt->dst_mac, dst);
106     mac_addr_copy(pkt->src_mac, minip_mac);
107     pkt->type = htons(type);
108 }
109 
minip_build_ipv4_hdr(struct ipv4_hdr * ipv4,uint32_t dst,uint8_t proto,uint16_t len)110 void minip_build_ipv4_hdr(struct ipv4_hdr *ipv4, uint32_t dst, uint8_t proto, uint16_t len) {
111     ipv4->ver_ihl       = 0x45;
112     ipv4->dscp_ecn      = 0;
113     ipv4->len           = htons(20 + len); // 5 * 4 from ihl, plus payload length
114     ipv4->id            = 0;
115     ipv4->flags_frags   = 0x40; // no offset, no fragments
116     ipv4->ttl           = 64;
117     ipv4->proto         = proto;
118     ipv4->dst_addr      = dst;
119     ipv4->src_addr      = minip_ip;
120 
121     /* This may be unnecessary if the controller supports checksum offloading */
122     ipv4->chksum = 0;
123     ipv4->chksum = rfc1701_chksum((uint8_t *) ipv4, sizeof(struct ipv4_hdr));
124 }
125 
send_arp_request(uint32_t addr)126 static int send_arp_request(uint32_t addr) {
127     pktbuf_t *p;
128     struct eth_hdr *eth;
129     struct arp_pkt *arp;
130 
131     if ((p = pktbuf_alloc()) == NULL) {
132         return -1;
133     }
134 
135     eth = pktbuf_prepend(p, sizeof(struct eth_hdr));
136     arp = pktbuf_append(p, sizeof(struct arp_pkt));
137     minip_build_mac_hdr(eth, bcast_mac, ETH_TYPE_ARP);
138 
139     arp->htype = htons(0x0001);
140     arp->ptype = htons(0x0800);
141     arp->hlen = 6;
142     arp->plen = 4;
143     arp->oper = htons(ARP_OPER_REQUEST);
144     arp->spa = minip_ip;
145     arp->tpa = addr;
146     mac_addr_copy(arp->sha, minip_mac);
147     mac_addr_copy(arp->tha, bcast_mac);
148 
149     minip_tx_handler(p);
150     return 0;
151 }
152 
handle_arp_timeout_cb(void * arg)153 static void handle_arp_timeout_cb(void *arg) {
154     *(bool *)arg = true;
155 }
156 
get_dest_mac(uint32_t host)157 const uint8_t *get_dest_mac(uint32_t host) {
158     uint8_t *dst_mac = NULL;
159     bool arp_timeout = false;
160     net_timer_t arp_timeout_timer;
161 
162     if (host == IPV4_BCAST) {
163         return bcast_mac;
164     }
165 
166     dst_mac = arp_cache_lookup(host);
167     if (dst_mac == NULL) {
168         send_arp_request(host);
169         memset(&arp_timeout_timer, 0, sizeof(arp_timeout_timer));
170         net_timer_set(&arp_timeout_timer, handle_arp_timeout_cb, &arp_timeout, 100);
171         while (!arp_timeout) {
172             dst_mac = arp_cache_lookup(host);
173             if (dst_mac) {
174                 net_timer_cancel(&arp_timeout_timer);
175                 break;
176             }
177         }
178     }
179 
180     return dst_mac;
181 }
182 
minip_ipv4_send(pktbuf_t * p,uint32_t dest_addr,uint8_t proto)183 status_t minip_ipv4_send(pktbuf_t *p, uint32_t dest_addr, uint8_t proto) {
184     status_t ret = 0;
185     size_t data_len = p->dlen;
186     const uint8_t *dst_mac;
187 
188     struct ipv4_hdr *ip = pktbuf_prepend(p, sizeof(struct ipv4_hdr));
189     struct eth_hdr *eth = pktbuf_prepend(p, sizeof(struct eth_hdr));
190 
191 
192     if (dest_addr == IPV4_BCAST || dest_addr == minip_broadcast) {
193         dst_mac = bcast_mac;
194         goto ready;
195     }
196 
197     dst_mac = get_dest_mac(dest_addr);
198     if (!dst_mac) {
199         pktbuf_free(p, true);
200         ret = -EHOSTUNREACH;
201         goto err;
202     }
203 
204 ready:
205     minip_build_mac_hdr(eth, dst_mac, ETH_TYPE_IPV4);
206     minip_build_ipv4_hdr(ip, dest_addr, proto, data_len);
207 
208     minip_tx_handler(p);
209 
210 err:
211     return ret;
212 }
213 
214 /* Swap the dst/src ip addresses and send an ICMP ECHO REPLY with the same payload.
215  * According to spec the data portion doesn't matter, but ping itself validates that
216  * the payload is identical
217  */
send_ping_reply(uint32_t ipaddr,struct icmp_pkt * req,size_t reqdatalen)218 static void send_ping_reply(uint32_t ipaddr, struct icmp_pkt *req, size_t reqdatalen) {
219     pktbuf_t *p;
220     size_t len;
221     struct eth_hdr *eth;
222     struct ipv4_hdr *ip;
223     struct icmp_pkt *icmp;
224 
225     if ((p = pktbuf_alloc()) == NULL) {
226         return;
227     }
228 
229     icmp = pktbuf_prepend(p, sizeof(struct icmp_pkt));
230     ip = pktbuf_prepend(p, sizeof(struct ipv4_hdr));
231     eth = pktbuf_prepend(p, sizeof(struct eth_hdr));
232     pktbuf_append_data(p, req->data, reqdatalen);
233 
234     len = sizeof(struct icmp_pkt) + reqdatalen;
235 
236     minip_build_mac_hdr(eth, arp_cache_lookup(ipaddr), ETH_TYPE_IPV4);
237     minip_build_ipv4_hdr(ip, ipaddr, IP_PROTO_ICMP, len);
238 
239     icmp->type = ICMP_ECHO_REPLY;
240     icmp->code = 0;
241     memcpy(icmp->hdr_data, req->hdr_data, sizeof(icmp->hdr_data));
242     icmp->chksum = 0;
243     icmp->chksum = rfc1701_chksum((uint8_t *) icmp, len);
244 
245     minip_tx_handler(p);
246 }
247 
dump_ipv4_addr(uint32_t addr)248 static void dump_ipv4_addr(uint32_t addr) {
249     const uint8_t *a = (void *)&addr;
250 
251     printf("%hhu.%hhu.%hhu.%hhu", a[0], a[1], a[2], a[3]);
252 }
253 
dump_ipv4_packet(const struct ipv4_hdr * ip)254 static void dump_ipv4_packet(const struct ipv4_hdr *ip) {
255     printf("IP ");
256     dump_ipv4_addr(ip->src_addr);
257     printf(" -> ");
258     dump_ipv4_addr(ip->dst_addr);
259     printf(" hlen 0x%x, prot 0x%x, cksum 0x%x, len 0x%x, ident 0x%x, frag offset 0x%x\n",
260            (ip->ver_ihl & 0xf) * 4, ip->proto, ntohs(ip->chksum), ntohs(ip->len), ntohs(ip->id), ntohs(ip->flags_frags) & 0x1fff);
261 }
262 
handle_ipv4_packet(pktbuf_t * p,const uint8_t * src_mac)263 __NO_INLINE static void handle_ipv4_packet(pktbuf_t *p, const uint8_t *src_mac) {
264     struct ipv4_hdr *ip;
265 
266     ip = (struct ipv4_hdr *)p->data;
267     if (p->dlen < sizeof(struct ipv4_hdr))
268         return;
269 
270     /* print packets for us */
271     if (LOCAL_TRACE) {
272         dump_ipv4_packet(ip);
273     }
274 
275     /* reject bad packets */
276     if (((ip->ver_ihl >> 4) & 0xf) != 4) {
277         /* not version 4 */
278         LTRACEF("REJECT: not version 4\n");
279         return;
280     }
281 
282     /* do we have enough buffer to hold the full header + options? */
283     size_t header_len = (ip->ver_ihl & 0xf) * 4;
284     if (p->dlen < header_len) {
285         LTRACEF("REJECT: not enough buffer to hold header\n");
286         return;
287     }
288 
289     /* compute checksum */
290     if (rfc1701_chksum((void *)ip, header_len) != 0) {
291         /* bad checksum */
292         LTRACEF("REJECT: bad checksum\n");
293         return;
294     }
295 
296     /* is the pkt_buf large enough to hold the length the header says the packet is? */
297     if (htons(ip->len) > p->dlen) {
298         LTRACEF("REJECT: packet exceeds size of buffer (header %d, dlen %d)\n", htons(ip->len), p->dlen);
299         return;
300     }
301 
302     /* trim any excess bytes at the end of the packet */
303     if (p->dlen > htons(ip->len)) {
304         pktbuf_consume_tail(p, p->dlen - htons(ip->len));
305     }
306 
307     /* remove the header from the front of the packet_buf  */
308     if (pktbuf_consume(p, header_len) == NULL) {
309         return;
310     }
311 
312     /* the packet is good, we can use it to populate our arp cache */
313     arp_cache_update(ip->src_addr, src_mac);
314 
315     /* see if it's for us */
316     if (ip->dst_addr != IPV4_BCAST) {
317         if (minip_ip != IPV4_NONE && ip->dst_addr != minip_ip && ip->dst_addr != minip_broadcast) {
318             LTRACEF("REJECT: for another host\n");
319             return;
320         }
321     }
322 
323     /* We only handle UDP and ECHO REQUEST */
324     switch (ip->proto) {
325         case IP_PROTO_ICMP: {
326             struct icmp_pkt *icmp;
327             if ((icmp = pktbuf_consume(p, sizeof(struct icmp_pkt))) == NULL) {
328                 break;
329             }
330             if (icmp->type == ICMP_ECHO_REQUEST) {
331                 send_ping_reply(ip->src_addr, icmp, p->dlen);
332             }
333         }
334         break;
335 
336         case IP_PROTO_UDP:
337             udp_input(p, ip->src_addr);
338             break;
339 
340         case IP_PROTO_TCP:
341             tcp_input(p, ip->src_addr, ip->dst_addr);
342             break;
343     }
344 }
345 
handle_arp_pkt(pktbuf_t * p)346 __NO_INLINE static int handle_arp_pkt(pktbuf_t *p) {
347     struct eth_hdr *eth;
348     struct arp_pkt *arp;
349 
350     eth = (void *) (p->data - sizeof(struct eth_hdr));
351 
352     if ((arp = pktbuf_consume(p, sizeof(struct arp_pkt))) == NULL) {
353         return -1;
354     }
355 
356     switch (ntohs(arp->oper)) {
357         case ARP_OPER_REQUEST: {
358             pktbuf_t *rp;
359             struct eth_hdr *reth;
360             struct arp_pkt *rarp;
361 
362             if (memcmp(&arp->tpa, &minip_ip, sizeof(minip_ip)) == 0) {
363                 if ((rp = pktbuf_alloc()) == NULL) {
364                     break;
365                 }
366 
367                 reth = pktbuf_prepend(rp, sizeof(struct eth_hdr));
368                 rarp = pktbuf_append(rp, sizeof(struct arp_pkt));
369 
370                 // Eth header
371                 minip_build_mac_hdr(reth, eth->src_mac, ETH_TYPE_ARP);
372 
373                 // ARP packet
374                 rarp->oper = htons(ARP_OPER_REPLY);
375                 rarp->htype = htons(0x0001);
376                 rarp->ptype = htons(0x0800);
377                 rarp->hlen = 6;
378                 rarp->plen = 4;
379                 mac_addr_copy(rarp->sha, minip_mac);
380                 rarp->spa = minip_ip;
381                 mac_addr_copy(rarp->tha, arp->sha);
382                 rarp->tpa = arp->spa;
383 
384                 minip_tx_handler(rp);
385             }
386         }
387         break;
388 
389         case ARP_OPER_REPLY: {
390             uint32_t addr;
391             memcpy(&addr, &arp->spa, sizeof(addr)); // unaligned word
392             arp_cache_update(addr, arp->sha);
393         }
394         break;
395     }
396 
397     return 0;
398 }
399 
dump_mac_address(const uint8_t * mac)400 static void dump_mac_address(const uint8_t *mac) {
401     printf("%02x:%02x:%02x:%02x:%02x:%02x",
402            mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
403 }
404 
dump_eth_packet(const struct eth_hdr * eth)405 static void dump_eth_packet(const struct eth_hdr *eth) {
406     printf("ETH src ");
407     dump_mac_address(eth->src_mac);
408     printf(" dst ");
409     dump_mac_address(eth->dst_mac);
410     printf(" type 0x%hx\n", htons(eth->type));
411 }
412 
minip_rx_driver_callback(pktbuf_t * p)413 void minip_rx_driver_callback(pktbuf_t *p) {
414     struct eth_hdr *eth;
415 
416     if ((eth = (void *) pktbuf_consume(p, sizeof(struct eth_hdr))) == NULL) {
417         return;
418     }
419 
420     if (LOCAL_TRACE) {
421         dump_eth_packet(eth);
422     }
423 
424     if (memcmp(eth->dst_mac, minip_mac, 6) != 0 &&
425             memcmp(eth->dst_mac, broadcast_mac, 6) != 0) {
426         /* not for us */
427         return;
428     }
429 
430     switch (htons(eth->type)) {
431         case ETH_TYPE_IPV4:
432             LTRACEF("ipv4 pkt\n");
433             handle_ipv4_packet(p, eth->src_mac);
434             break;
435 
436         case ETH_TYPE_ARP:
437             LTRACEF("arp pkt\n");
438             handle_arp_pkt(p);
439             break;
440     }
441 }
442 
minip_parse_ipaddr(const char * ipaddr_str,size_t len)443 uint32_t minip_parse_ipaddr(const char *ipaddr_str, size_t len) {
444     uint8_t ip[4] = { 0, 0, 0, 0 };
445     uint8_t pos = 0, i = 0;
446 
447     while (pos < len) {
448         char c = ipaddr_str[pos];
449         if (c == '.') {
450             i++;
451         } else if (c == '\0') {
452             break;
453         } else {
454             ip[i] *= 10;
455             ip[i] += c - '0';
456         }
457         pos++;
458     }
459 
460     return IPV4_PACK(ip);
461 }
462