Add http get request queryt to immediaterly return the query string (like ?)
[clinton/Smoothieware.git] / src / libs / Network / uip / Network.cpp
CommitLineData
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
45extern "C" void uip_log(char *m)
46{
47 printf("uIP log message: %s\n", m);
48}
49
54be3bab 50static Network* theNetwork;
d4ee6ee2 51
d4ee6ee2
JM
52Network::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
63Network::~Network()
64{
65 delete ethernet;
b8c26a0e 66 if (hostname != NULL) {
67 delete hostname;
68 }
54be3bab 69 theNetwork= nullptr;
d4ee6ee2
JM
70}
71
72static 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 88static 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 106static 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
124void 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
202void 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
227uint32_t Network::tick(uint32_t dummy)
228{
229 do_tick();
230 tickcnt++;
231 return 0;
232}
233
234void 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 295void 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
321extern "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
340void 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
353void 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
388void 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
400extern "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
407extern "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
439void 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
449extern "C" void uip_split_output(void);
450extern "C" void tcpip_output()
451{
452 theNetwork->tapdev_send(uip_buf, uip_len);
453}
454void network_device_send()
455{
456 uip_split_output();
457 //tcpip_output();
458}
459#else
460void network_device_send()
461{
462 tapdev_send(uip_buf, uip_len);
463}
464#endif
465
466void 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}