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