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