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" | |
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 |
41 | extern "C" void uip_log(char *m) |
42 | { | |
43 | printf("uIP log message: %s\n", m); | |
44 | } | |
45 | ||
46 | static bool webserver_enabled, telnet_enabled, use_dhcp; | |
47 | static Network *theNetwork; | |
48 | static Sftpd *sftpd; | |
49 | static CommandQueue *command_q= CommandQueue::getInstance(); | |
50 | ||
51 | Network* Network::instance; | |
52 | Network::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 | ||
62 | Network::~Network() | |
63 | { | |
64 | delete ethernet; | |
b8c26a0e | 65 | if (hostname != NULL) { |
66 | delete hostname; | |
67 | } | |
d4ee6ee2 JM |
68 | } |
69 | ||
70 | static 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 | 86 | static 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 | 104 | static 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 |
122 | void 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 | ||
200 | void 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 | ||
225 | uint32_t Network::tick(uint32_t dummy) | |
226 | { | |
227 | do_tick(); | |
228 | tickcnt++; | |
229 | return 0; | |
230 | } | |
231 | ||
232 | void 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 | ||
293 | static 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 | ||
311 | extern "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 | ||
330 | void 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 | ||
343 | void 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 | ||
378 | void 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 | |
387 | extern "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 | ||
413 | void 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 | |
423 | extern "C" void uip_split_output(void); | |
424 | extern "C" void tcpip_output() | |
425 | { | |
426 | theNetwork->tapdev_send(uip_buf, uip_len); | |
427 | } | |
428 | void network_device_send() | |
429 | { | |
430 | uip_split_output(); | |
431 | //tcpip_output(); | |
432 | } | |
433 | #else | |
434 | void network_device_send() | |
435 | { | |
436 | tapdev_send(uip_buf, uip_len); | |
437 | } | |
438 | #endif | |
439 | ||
440 | void 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 | } |