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