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