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