Commit | Line | Data |
---|---|---|
d4ee6ee2 JM |
1 | #pragma GCC diagnostic ignored "-Wstrict-aliasing" |
2 | #pragma GCC diagnostic ignored "-Wcast-qual" | |
3 | #pragma GCC diagnostic ignored "-Wcast-align" | |
4 | ||
5 | #include "CommandQueue.h" | |
6 | ||
7 | #include "Kernel.h" | |
61134a65 JM |
8 | #include "Config.h" |
9 | #include "SlowTicker.h" | |
d4ee6ee2 JM |
10 | |
11 | #include "Network.h" | |
12 | #include "PublicDataRequest.h" | |
13 | #include "PlayerPublicAccess.h" | |
14 | #include "net_util.h" | |
15 | #include "uip_arp.h" | |
16 | #include "clock-arch.h" | |
0d9c5e2a | 17 | #include "NetworkPublicAccess.h" |
7af0714f | 18 | #include "checksumm.h" |
8d54c34c | 19 | #include "ConfigValue.h" |
d4ee6ee2 JM |
20 | |
21 | #include "uip.h" | |
22 | #include "telnetd.h" | |
23 | #include "webserver.h" | |
24 | #include "dhcpc.h" | |
25 | #include "sftpd.h" | |
54be3bab JM |
26 | |
27 | #ifndef NOPLAN9 | |
e66e09b2 | 28 | #include "plan9.h" |
54be3bab | 29 | #endif |
d4ee6ee2 JM |
30 | |
31 | #include <mri.h> | |
32 | ||
33 | #define BUF ((struct uip_eth_hdr *)&uip_buf[0]) | |
34 | ||
0d9c5e2a JM |
35 | #define network_enable_checksum CHECKSUM("enable") |
36 | #define network_webserver_checksum CHECKSUM("webserver") | |
37 | #define network_telnet_checksum CHECKSUM("telnet") | |
e66e09b2 | 38 | #define network_plan9_checksum CHECKSUM("plan9") |
0d9c5e2a JM |
39 | #define network_mac_override_checksum CHECKSUM("mac_override") |
40 | #define network_ip_address_checksum CHECKSUM("ip_address") | |
b8c26a0e | 41 | #define network_hostname_checksum CHECKSUM("hostname") |
0d9c5e2a JM |
42 | #define network_ip_gateway_checksum CHECKSUM("ip_gateway") |
43 | #define network_ip_mask_checksum CHECKSUM("ip_mask") | |
44 | ||
d4ee6ee2 JM |
45 | extern "C" void uip_log(char *m) |
46 | { | |
47 | printf("uIP log message: %s\n", m); | |
48 | } | |
49 | ||
54be3bab | 50 | static Network* theNetwork; |
d4ee6ee2 | 51 | |
d4ee6ee2 JM |
52 | Network::Network() |
53 | { | |
54be3bab | 54 | theNetwork= this; |
d4ee6ee2 JM |
55 | ethernet = new LPC17XX_Ethernet(); |
56 | tickcnt= 0; | |
d4ee6ee2 | 57 | sftpd= NULL; |
b8c26a0e | 58 | hostname = NULL; |
54be3bab JM |
59 | plan9_enabled= false; |
60 | command_q= CommandQueue::getInstance(); | |
d4ee6ee2 JM |
61 | } |
62 | ||
63 | Network::~Network() | |
64 | { | |
65 | delete ethernet; | |
b8c26a0e | 66 | if (hostname != NULL) { |
67 | delete hostname; | |
68 | } | |
54be3bab | 69 | theNetwork= nullptr; |
d4ee6ee2 JM |
70 | } |
71 | ||
72 | static uint32_t getSerialNumberHash() | |
73 | { | |
74 | #define IAP_LOCATION 0x1FFF1FF1 | |
75 | uint32_t command[1]; | |
76 | uint32_t result[5]; | |
77 | typedef void (*IAP)(uint32_t *, uint32_t *); | |
78 | IAP iap = (IAP) IAP_LOCATION; | |
79 | ||
80 | __disable_irq(); | |
81 | ||
82 | command[0] = 58; | |
83 | iap(command, result); | |
84 | __enable_irq(); | |
85 | return crc32((uint8_t *)&result[1], 4 * 4); | |
86 | } | |
87 | ||
a20d8df1 | 88 | static bool parse_ip_str(const string &s, uint8_t *a, int len, int base=10, char sep = '.') |
d4ee6ee2 JM |
89 | { |
90 | int p = 0; | |
91 | const char *n; | |
92 | for (int i = 0; i < len; i++) { | |
93 | if (i < len - 1) { | |
94 | size_t o = s.find(sep, p); | |
95 | if (o == string::npos) return false; | |
96 | n = s.substr(p, o - p).c_str(); | |
97 | p = o + 1; | |
98 | } else { | |
99 | n = s.substr(p).c_str(); | |
100 | } | |
a20d8df1 | 101 | a[i] = (int)strtol(n, NULL, base); |
d4ee6ee2 JM |
102 | } |
103 | return true; | |
104 | } | |
105 | ||
b8c26a0e | 106 | static bool parse_hostname(const string &s) |
107 | { | |
108 | const std::string::size_type str_len = s.size(); | |
109 | if(str_len > 63){ | |
110 | return false; | |
111 | } | |
112 | for (unsigned int i = 0; i < str_len; i++) { | |
113 | const char c = s.at(i); | |
114 | if(!(c >= 'a' && c <= 'z') | |
115 | && !(c >= 'A' && c <= 'Z') | |
116 | && !(i != 0 && c >= '0' && c <= '9') | |
117 | && !(i != 0 && i != str_len - 1 && c == '-')){ | |
118 | return false; | |
119 | } | |
120 | } | |
121 | return true; | |
122 | } | |
123 | ||
d4ee6ee2 JM |
124 | void Network::on_module_loaded() |
125 | { | |
126 | if ( !THEKERNEL->config->value( network_checksum, network_enable_checksum )->by_default(false)->as_bool() ) { | |
127 | // as not needed free up resource | |
128 | delete this; | |
129 | return; | |
130 | } | |
131 | ||
132 | webserver_enabled = THEKERNEL->config->value( network_checksum, network_webserver_checksum, network_enable_checksum )->by_default(false)->as_bool(); | |
133 | telnet_enabled = THEKERNEL->config->value( network_checksum, network_telnet_checksum, network_enable_checksum )->by_default(false)->as_bool(); | |
e66e09b2 | 134 | plan9_enabled = THEKERNEL->config->value( network_checksum, network_plan9_checksum, network_enable_checksum )->by_default(false)->as_bool(); |
d4ee6ee2 JM |
135 | string mac = THEKERNEL->config->value( network_checksum, network_mac_override_checksum )->by_default("")->as_string(); |
136 | if (mac.size() == 17 ) { // parse mac address | |
a20d8df1 | 137 | if (!parse_ip_str(mac, mac_address, 6, 16, ':')) { |
d4ee6ee2 JM |
138 | printf("Invalid MAC address: %s\n", mac.c_str()); |
139 | printf("Network not started due to errors in config"); | |
140 | return; | |
141 | } | |
142 | ||
143 | } else { // autogenerate | |
144 | uint32_t h = getSerialNumberHash(); | |
145 | mac_address[0] = 0x00; // OUI | |
146 | mac_address[1] = 0x1F; // OUI | |
147 | mac_address[2] = 0x11; // OUI | |
148 | mac_address[3] = 0x02; // Openmoko allocation for smoothie board | |
149 | mac_address[4] = 0x04; // 04-14 03 bits -> chip id, 1 bits -> hashed serial | |
150 | mac_address[5] = h & 0xFF; // 00-FF 8bits -> hashed serial | |
151 | } | |
152 | ||
153 | ethernet->set_mac(mac_address); | |
154 | ||
155 | // get IP address, mask and gateway address here.... | |
3fd8df03 | 156 | string s = THEKERNEL->config->value( network_checksum, network_ip_address_checksum )->by_default("auto")->as_string(); |
d4ee6ee2 JM |
157 | if (s == "auto") { |
158 | use_dhcp = true; | |
b8c26a0e | 159 | s = THEKERNEL->config->value( network_checksum, network_hostname_checksum )->as_string(); |
160 | if (!s.empty()) { | |
161 | if(parse_hostname(s)){ | |
162 | hostname = new char [s.length() + 1]; | |
163 | strcpy(hostname, s.c_str()); | |
164 | }else{ | |
165 | printf("Invalid hostname: %s\n", s.c_str()); | |
166 | } | |
167 | } | |
d4ee6ee2 | 168 | } else { |
b8c26a0e | 169 | bool bad = false; |
d4ee6ee2 JM |
170 | use_dhcp = false; |
171 | if (!parse_ip_str(s, ipaddr, 4)) { | |
172 | printf("Invalid IP address: %s\n", s.c_str()); | |
173 | bad = true; | |
174 | } | |
175 | s = THEKERNEL->config->value( network_checksum, network_ip_mask_checksum )->by_default("255.255.255.0")->as_string(); | |
176 | if (!parse_ip_str(s, ipmask, 4)) { | |
177 | printf("Invalid IP Mask: %s\n", s.c_str()); | |
178 | bad = true; | |
179 | } | |
180 | s = THEKERNEL->config->value( network_checksum, network_ip_gateway_checksum )->by_default("192.168.3.1")->as_string(); | |
181 | if (!parse_ip_str(s, ipgw, 4)) { | |
182 | printf("Invalid IP gateway: %s\n", s.c_str()); | |
183 | bad = true; | |
184 | } | |
d4ee6ee2 JM |
185 | if (bad) { |
186 | printf("Network not started due to errors in config"); | |
187 | return; | |
188 | } | |
189 | } | |
190 | ||
191 | THEKERNEL->add_module( ethernet ); | |
192 | THEKERNEL->slow_ticker->attach( 100, this, &Network::tick ); | |
193 | ||
194 | // Register for events | |
195 | this->register_for_event(ON_IDLE); | |
196 | this->register_for_event(ON_MAIN_LOOP); | |
197 | this->register_for_event(ON_GET_PUBLIC_DATA); | |
198 | ||
199 | this->init(); | |
200 | } | |
201 | ||
202 | void Network::on_get_public_data(void* argument) { | |
203 | PublicDataRequest* pdr = static_cast<PublicDataRequest*>(argument); | |
204 | ||
205 | if(!pdr->starts_with(network_checksum)) return; | |
206 | ||
207 | if(pdr->second_element_is(get_ip_checksum)) { | |
208 | pdr->set_data_ptr(this->ipaddr); | |
209 | pdr->set_taken(); | |
210 | ||
211 | }else if(pdr->second_element_is(get_ipconfig_checksum)) { | |
212 | // NOTE caller must free the returned string when done | |
213 | char buf[200]; | |
214 | int n1= snprintf(buf, sizeof(buf), "IP Addr: %d.%d.%d.%d\n", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); | |
215 | int n2= snprintf(&buf[n1], sizeof(buf)-n1, "IP GW: %d.%d.%d.%d\n", ipgw[0], ipgw[1], ipgw[2], ipgw[3]); | |
216 | int n3= snprintf(&buf[n1+n2], sizeof(buf)-n1-n2, "IP mask: %d.%d.%d.%d\n", ipmask[0], ipmask[1], ipmask[2], ipmask[3]); | |
217 | int n4= snprintf(&buf[n1+n2+n3], sizeof(buf)-n1-n2-n3, "MAC Address: %02X:%02X:%02X:%02X:%02X:%02X\n", | |
218 | mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5]); | |
219 | char *str = (char *)malloc(n1+n2+n3+n4+1); | |
220 | memcpy(str, buf, n1+n2+n3+n4); | |
221 | str[n1+n2+n3+n4]= '\0'; | |
222 | pdr->set_data_ptr(str); | |
223 | pdr->set_taken(); | |
224 | } | |
225 | } | |
226 | ||
227 | uint32_t Network::tick(uint32_t dummy) | |
228 | { | |
229 | do_tick(); | |
230 | tickcnt++; | |
231 | return 0; | |
232 | } | |
233 | ||
234 | void Network::on_idle(void *argument) | |
235 | { | |
236 | if (!ethernet->isUp()) return; | |
237 | ||
319567fa | 238 | int len= sizeof(uip_buf); // set maximum size |
d4ee6ee2 JM |
239 | if (ethernet->_receive_frame(uip_buf, &len)) { |
240 | uip_len = len; | |
241 | this->handlePacket(); | |
242 | ||
243 | } else { | |
244 | ||
245 | if (timer_expired(&periodic_timer)) { /* no packet but periodic_timer time out (0.1s)*/ | |
246 | timer_reset(&periodic_timer); | |
247 | ||
248 | for (int i = 0; i < UIP_CONNS; i++) { | |
249 | uip_periodic(i); | |
250 | /* If the above function invocation resulted in data that | |
251 | should be sent out on the network, the global variable | |
252 | uip_len is set to a value > 0. */ | |
253 | if (uip_len > 0) { | |
254 | uip_arp_out(); | |
255 | tapdev_send(uip_buf, uip_len); | |
256 | } | |
257 | } | |
258 | ||
259 | #if UIP_CONF_UDP | |
260 | for (int i = 0; i < UIP_UDP_CONNS; i++) { | |
261 | uip_udp_periodic(i); | |
262 | /* If the above function invocation resulted in data that | |
263 | should be sent out on the network, the global variable | |
264 | uip_len is set to a value > 0. */ | |
265 | if (uip_len > 0) { | |
266 | uip_arp_out(); | |
267 | tapdev_send(uip_buf, uip_len); | |
268 | } | |
269 | } | |
270 | #endif | |
271 | } | |
272 | /* | |
273 | This didn't work actually made it worse,it should have worked though | |
274 | else{ | |
275 | // TODO if the command queue is below a certain amount we should poll any stopped connections | |
276 | if(command_q->size() < 4) { | |
277 | for (struct uip_conn *connr = &uip_conns[0]; connr <= &uip_conns[UIP_CONNS - 1]; ++connr) { | |
278 | if(uip_stopped(connr)){ | |
279 | // Force a poll of this | |
280 | printf("Force poll of connection\n"); | |
281 | uip_poll_conn(connr); | |
282 | } | |
283 | } | |
284 | } | |
285 | } | |
286 | */ | |
287 | /* Call the ARP timer function every 10 seconds. */ | |
288 | if (timer_expired(&arp_timer)) { | |
289 | timer_reset(&arp_timer); | |
290 | uip_arp_timer(); | |
291 | } | |
292 | } | |
293 | } | |
294 | ||
54be3bab | 295 | void Network::setup_servers() |
d4ee6ee2 JM |
296 | { |
297 | if (webserver_enabled) { | |
298 | // Initialize the HTTP server, listen to port 80. | |
299 | httpd_init(); | |
300 | printf("Webserver initialized\n"); | |
301 | } | |
302 | ||
303 | if (telnet_enabled) { | |
304 | // Initialize the telnet server | |
305 | Telnetd::init(); | |
306 | printf("Telnetd initialized\n"); | |
307 | } | |
308 | ||
54be3bab | 309 | #ifndef NOPLAN9 |
e66e09b2 DM |
310 | if (plan9_enabled) { |
311 | // Initialize the plan9 server | |
312 | Plan9::init(); | |
313 | printf("Plan9 initialized\n"); | |
314 | } | |
54be3bab | 315 | #endif |
e66e09b2 | 316 | |
d4ee6ee2 JM |
317 | // sftpd service, which is lazily created on reciept of first packet |
318 | uip_listen(HTONS(115)); | |
319 | } | |
320 | ||
321 | extern "C" void dhcpc_configured(const struct dhcpc_state *s) | |
322 | { | |
323 | printf("Got IP address %d.%d.%d.%d\n", | |
324 | uip_ipaddr1(&s->ipaddr), uip_ipaddr2(&s->ipaddr), | |
325 | uip_ipaddr3(&s->ipaddr), uip_ipaddr4(&s->ipaddr)); | |
326 | printf("Got netmask %d.%d.%d.%d\n", | |
327 | uip_ipaddr1(&s->netmask), uip_ipaddr2(&s->netmask), | |
328 | uip_ipaddr3(&s->netmask), uip_ipaddr4(&s->netmask)); | |
329 | printf("Got DNS server %d.%d.%d.%d\n", | |
330 | uip_ipaddr1(&s->dnsaddr), uip_ipaddr2(&s->dnsaddr), | |
331 | uip_ipaddr3(&s->dnsaddr), uip_ipaddr4(&s->dnsaddr)); | |
332 | printf("Got default router %d.%d.%d.%d\n", | |
333 | uip_ipaddr1(&s->default_router), uip_ipaddr2(&s->default_router), | |
334 | uip_ipaddr3(&s->default_router), uip_ipaddr4(&s->default_router)); | |
335 | printf("Lease expires in %ld seconds\n", ntohl(s->lease_time)); | |
336 | ||
337 | theNetwork->dhcpc_configured(s->ipaddr, s->netmask, s->default_router); | |
338 | } | |
339 | ||
340 | void Network::dhcpc_configured(uint32_t ipaddr, uint32_t ipmask, uint32_t ipgw) | |
341 | { | |
342 | memcpy(this->ipaddr, &ipaddr, 4); | |
343 | memcpy(this->ipmask, &ipmask, 4); | |
344 | memcpy(this->ipgw, &ipgw, 4); | |
345 | ||
346 | uip_sethostaddr((u16_t*)this->ipaddr); | |
347 | uip_setnetmask((u16_t*)this->ipmask); | |
348 | uip_setdraddr((u16_t*)this->ipgw); | |
349 | ||
350 | setup_servers(); | |
351 | } | |
352 | ||
353 | void Network::init(void) | |
354 | { | |
355 | // two timers for tcp/ip | |
356 | timer_set(&periodic_timer, CLOCK_SECOND / 2); /* 0.5s */ | |
357 | timer_set(&arp_timer, CLOCK_SECOND * 10); /* 10s */ | |
358 | ||
359 | // Initialize the uIP TCP/IP stack. | |
360 | uip_init(); | |
361 | ||
362 | uip_setethaddr(mac_address); | |
363 | ||
364 | if (!use_dhcp) { // manual setup of ip | |
365 | uip_ipaddr_t tip; /* local IP address */ | |
366 | uip_ipaddr(tip, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); | |
367 | uip_sethostaddr(tip); /* host IP address */ | |
368 | printf("IP Addr: %d.%d.%d.%d\n", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); | |
369 | ||
370 | uip_ipaddr(tip, ipgw[0], ipgw[1], ipgw[2], ipgw[3]); | |
371 | uip_setdraddr(tip); /* router IP address */ | |
372 | printf("IP GW: %d.%d.%d.%d\n", ipgw[0], ipgw[1], ipgw[2], ipgw[3]); | |
373 | ||
374 | uip_ipaddr(tip, ipmask[0], ipmask[1], ipmask[2], ipmask[3]); | |
375 | uip_setnetmask(tip); /* mask */ | |
376 | printf("IP mask: %d.%d.%d.%d\n", ipmask[0], ipmask[1], ipmask[2], ipmask[3]); | |
377 | setup_servers(); | |
378 | ||
379 | }else{ | |
380 | #if UIP_CONF_UDP | |
b8c26a0e | 381 | dhcpc_init(mac_address, sizeof(mac_address), hostname); |
d4ee6ee2 JM |
382 | dhcpc_request(); |
383 | printf("Getting IP address....\n"); | |
384 | #endif | |
385 | } | |
386 | } | |
387 | ||
388 | void Network::on_main_loop(void *argument) | |
389 | { | |
390 | // issue commands here if any available | |
820b74a4 JM |
391 | // while(command_q->pop()) { |
392 | // // keep feeding them until empty | |
393 | // } | |
394 | ||
395 | // issue one comamnd per iteration of main loop like USB serial does | |
396 | command_q->pop(); | |
397 | ||
d4ee6ee2 JM |
398 | } |
399 | ||
80dd1a8e JM |
400 | extern "C" const char *get_query_string() |
401 | { | |
402 | const char* dup= strdup(THEKERNEL->get_query_string().c_str()); | |
403 | return dup; | |
404 | } | |
405 | ||
d4ee6ee2 JM |
406 | // select between webserver and telnetd server |
407 | extern "C" void app_select_appcall(void) | |
408 | { | |
409 | switch (uip_conn->lport) { | |
410 | case HTONS(80): | |
54be3bab | 411 | if (theNetwork->webserver_enabled) httpd_appcall(); |
d4ee6ee2 JM |
412 | break; |
413 | ||
414 | case HTONS(23): | |
54be3bab | 415 | if (theNetwork->telnet_enabled) Telnetd::appcall(); |
d4ee6ee2 JM |
416 | break; |
417 | ||
54be3bab | 418 | #ifndef NOPLAN9 |
e66e09b2 | 419 | case HTONS(564): |
54be3bab | 420 | if (theNetwork->plan9_enabled) Plan9::appcall(); |
e66e09b2 | 421 | break; |
54be3bab | 422 | #endif |
e66e09b2 | 423 | |
d4ee6ee2 | 424 | case HTONS(115): |
54be3bab JM |
425 | if(theNetwork->sftpd == NULL) { |
426 | theNetwork->sftpd= new Sftpd(); | |
427 | theNetwork->sftpd->init(); | |
d4ee6ee2 JM |
428 | printf("Created sftpd service\n"); |
429 | } | |
54be3bab | 430 | theNetwork->sftpd->appcall(); |
d4ee6ee2 JM |
431 | break; |
432 | ||
433 | default: | |
434 | printf("unknown app for port: %d\n", uip_conn->lport); | |
435 | ||
436 | } | |
437 | } | |
438 | ||
439 | void Network::tapdev_send(void *pPacket, unsigned int size) | |
440 | { | |
441 | memcpy(ethernet->request_packet_buffer(), pPacket, size); | |
442 | ethernet->write_packet((uint8_t *) pPacket, size); | |
443 | } | |
444 | ||
445 | // define this to split full frames into two to illicit an ack from the endpoint | |
446 | #define SPLIT_OUTPUT | |
447 | ||
448 | #ifdef SPLIT_OUTPUT | |
449 | extern "C" void uip_split_output(void); | |
450 | extern "C" void tcpip_output() | |
451 | { | |
452 | theNetwork->tapdev_send(uip_buf, uip_len); | |
453 | } | |
454 | void network_device_send() | |
455 | { | |
456 | uip_split_output(); | |
457 | //tcpip_output(); | |
458 | } | |
459 | #else | |
460 | void network_device_send() | |
461 | { | |
462 | tapdev_send(uip_buf, uip_len); | |
463 | } | |
464 | #endif | |
465 | ||
466 | void Network::handlePacket(void) | |
467 | { | |
468 | if (uip_len > 0) { /* received packet */ | |
469 | //printf("handlePacket: %d\n", uip_len); | |
470 | ||
471 | if (BUF->type == htons(UIP_ETHTYPE_IP)) { /* IP packet */ | |
472 | uip_arp_ipin(); | |
473 | uip_input(); | |
474 | /* If the above function invocation resulted in data that | |
475 | should be sent out on the network, the global variable | |
476 | uip_len is set to a value > 0. */ | |
477 | ||
478 | if (uip_len > 0) { | |
479 | uip_arp_out(); | |
480 | network_device_send(); | |
481 | } | |
482 | ||
483 | } else if (BUF->type == htons(UIP_ETHTYPE_ARP)) { /*ARP packet */ | |
484 | uip_arp_arpin(); | |
485 | /* If the above function invocation resulted in data that | |
486 | should be sent out on the network, the global variable | |
487 | uip_len is set to a value > 0. */ | |
488 | if (uip_len > 0) { | |
489 | tapdev_send(uip_buf, uip_len); /* ARP ack*/ | |
490 | } | |
491 | ||
492 | } else { | |
493 | printf("Unknown ethernet packet type %04X\n", htons(BUF->type)); | |
494 | uip_len = 0; | |
495 | } | |
496 | } | |
497 | } |