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" |
d4ee6ee2 JM |
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 | ||
0d9c5e2a JM |
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 | ||
d4ee6ee2 JM |
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; | |
3fd8df03 | 130 | string s = THEKERNEL->config->value( network_checksum, network_ip_address_checksum )->by_default("auto")->as_string(); |
d4ee6ee2 JM |
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 | } |