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