Initial release:
[firmerware] / src / net / core.c
1 /* netcore.c -- Networking core routines.
2  *
3  * From: Rick van Rein <rick@openfortress.nl>
4  */
5
6
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <stdint.h>
10 #include <stdarg.h>
11 #include <string.h>
12 #include <fcntl.h>
13
14 #include <sys/types.h>
15 #include <sys/ioctl.h>
16 #include <sys/socket.h>
17
18 #include <netinet/ip.h>
19 #include <netinet/ip6.h>
20 #include <netinet/udp.h>
21 #include <netinet/ip_icmp.h>
22 #include <netinet/icmp6.h>
23 #include <net/ethernet.h>
24
25 #include <linux/if.h>
26 #include <linux/if_tun.h>
27 #include <linux/if_ether.h>
28
29 #include <0cpm/netfun.h>
30 #include <0cpm/netdb.h>
31
32
33 /* Global variables
34  */
35
36 extern struct tunbuf rbuf, wbuf;        // TODO: Testing setup
37 extern int tunsox;                      // TODO: Testing setup
38
39 extern uint8_t ether_mine [ETHER_ADDR_LEN];
40
41 /* netcore_checksum_areas (void *area0, uint16_t len0, ..., NULL)
42  * Calculate the Internet checksum over the given areas
43  */
44 uint16_t netcore_checksum_areas (void *area0, ...) {
45         uint32_t csum = 0;
46         uint16_t *area;
47         va_list pairs;
48         va_start (pairs, area0);
49         area = (uint16_t *) area0;
50         while (area) {
51                 uint16_t len = (int16_t) va_arg (pairs, int);
52                 while (len > 1) {
53                         csum = csum + ntohs (*area++);
54                         len -= 2;
55                 }
56                 if (len > 0) {
57                         csum = csum + * (uint8_t *) area;
58                 }
59                 area = va_arg (pairs, uint16_t *);
60         }
61         va_end (pairs);
62         csum = (csum & 0xffff) + (csum >> 16);
63         csum = (csum & 0xffff) + (csum >> 16);
64         return htons ((uint16_t) ~csum);
65 }
66
67
68 /* Send a buffer with a given MEM[] array of internal pointers.
69  * This calculates length and checksum fields, then sends the
70  * message into the world.
71  */
72 int netcore_send_buffer (int tunsox, uint32_t *mem, struct tunbuf *wbuf) {
73         struct icmp6_hdr *icmp6 = (void *) mem [MEM_ICMP6_HEAD];
74         struct icmp      *icmp4 = (void *) mem [MEM_ICMP4_HEAD];
75         struct udphdr *udp4 = (void *) mem [MEM_UDP4_HEAD];
76         struct udphdr *udp6 = (void *) mem [MEM_UDP6_HEAD];
77         struct iphdr   *ip4 = (void *) mem [MEM_IP4_HEAD];
78         struct ip6_hdr *ip6 = (void *) mem [MEM_IP6_HEAD];
79         struct arphdr  *arp = (void *) mem [MEM_ARP_HEAD];
80         struct ethhdr  *eth = (void *) mem [MEM_ETHER_HEAD];
81         //
82         // Checksum UDPv6 on IPv6
83         if (ip6 && udp6) {
84                 uint16_t pload6 = htons (IPPROTO_UDP);
85                 udp6->len = htons (mem [MEM_ALL_DONE] - mem [MEM_UDP6_HEAD]);
86                 ip6->ip6_plen = udp6->len;
87                 ip6->ip6_nxt = IPPROTO_UDP;
88                 udp6->check = netcore_checksum_areas (
89                                 &ip6->ip6_src, 32,
90                                 &ip6->ip6_plen, 2,
91                                 &pload6, 2,
92                                 udp6, 6,
93                                 udp6 + 1, (int) htons (udp6->len) - 8,
94                                 NULL);
95         }
96         //
97         // Checksum ICMPv6 on IPv6
98         if (ip6 && icmp6) {
99                 ip6->ip6_nxt = IPPROTO_ICMPV6;
100                 uint16_t pload6 = htons (IPPROTO_ICMPV6);
101                 ip6->ip6_plen = htons (mem [MEM_ALL_DONE] - mem [MEM_ICMP6_HEAD]);
102                 icmp6->icmp6_cksum = 0;
103                 icmp6->icmp6_cksum = netcore_checksum_areas (
104                                 &ip6->ip6_src, 32,
105                                 &ip6->ip6_plen, 2,
106                                 &pload6, 2,
107                                 icmp6, (int) mem [MEM_ALL_DONE] - (int) mem [MEM_ICMP6_HEAD],
108                                 NULL);
109         }
110         //
111         // Checksum UDPv4 under IPv6 (6bed4 tunnel)
112         if (udp4) {
113                 udp4->len = htons (mem [MEM_ALL_DONE] - mem [MEM_UDP4_HEAD]);
114         }
115         //
116         // Checksum UDPv4 on IPv4
117         if (ip4 && udp4) {
118                 ip4->protocol = IPPROTO_UDP;
119                 uint16_t pload4 = htons (IPPROTO_UDP);
120                 ip4->tot_len = htons (20 + ntohs (udp4->len));
121                 udp4->check = netcore_checksum_areas (
122                                 &ip4->saddr, 8,
123                                 &pload4, 2,
124                                 &udp4->len, 2,
125                                 udp4, 6,
126                                 udp4 + 1, (int) ntohs (udp4->len) - 8,
127                                 NULL);
128         }
129         //
130         // Checksum ICMPv4
131         if (icmp4) {
132                 ip4->tot_len = htons (mem [MEM_ALL_DONE] - mem [MEM_IP4_HEAD]);
133                 ip4->protocol = IPPROTO_ICMP;
134                 icmp4->icmp_cksum = 0;
135                 icmp4->icmp_cksum = netcore_checksum_areas (
136                                 icmp4, (int) mem [MEM_ALL_DONE] - (int) mem [MEM_ICMP4_HEAD],
137                                 NULL);
138         }
139         //
140         // Checksum IPv4
141         if (ip4) {
142                 ip4->check = netcore_checksum_areas (
143                                 ip4, 10,
144                                 &ip4->saddr, 8,
145                                 NULL);
146         }
147         //
148         // Determine the tunnel prefix info and ethernet protocol
149         if (ip4) {
150                 wbuf->prefix.proto = htons (ETH_P_IP);
151         } else if (ip6) {
152                 wbuf->prefix.proto = htons (ETH_P_IPV6);
153         } else if (arp) {
154                 wbuf->prefix.proto = htons (ETH_P_ARP);
155         } else {
156                 return -1;
157         }
158         eth->h_proto = wbuf->prefix.proto;
159         wbuf->prefix.flags = 0;
160         //
161         // Actually send the packet
162         int alen = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD] + sizeof (struct tun_pi);
163         if ((alen > 0) && (alen < 1500+12)) {
164                 int wlen = write (tunsox, wbuf, alen);
165 printf ("Written %d out of %d:\n", wlen, alen); int i; for (i=0; i<alen; i++) { printf ("%02x%s", ((uint8_t *) wbuf) [i], (i%16 == 0? "\n": " ")); }; printf ("\n");
166                 if (wlen != alen) {
167                         if (wlen < 0) {
168                                 return -1;
169                         } else {
170                                 // No guaranteed delivery -- swallow problem
171                                 return 0;
172                         }
173                 }
174         }
175         return 0;
176 }
177
178
179 /* Send a router solicitation.
180  */
181 static void solicit_router (void) {
182         uint8_t *start = wbuf.data;
183         uint32_t mem [MEM_NETVAR_COUNT];
184         bzero (mem, sizeof (mem));
185         mem [MEM_BINDING6] = (uint32_t) &ip6binding [0];
186         uint8_t *stop = netsend_icmp6_router_solicit (start, mem);
187         mem [MEM_ALL_DONE] = (uint32_t) stop;
188         uint32_t len = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD];
189         if ((len > 0) && (len < 1500 + 18)) {
190                 netcore_send_buffer (tunsox, mem, &wbuf);
191         }
192 }
193
194
195 /* Start to obtain an IPv4 address.
196  */
197 static void get_dhcp4_lease (void) {
198         uint8_t *start = wbuf.data;
199         uint32_t mem [MEM_NETVAR_COUNT];
200         bzero (mem, sizeof (mem));
201         uint8_t *stop = netsend_dhcp4_discover (start, mem);
202         mem [MEM_ALL_DONE] = (uint32_t) stop;
203         uint32_t len = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD];
204         if ((len > 0) && (len < 1500 + 18)) {
205                 netcore_send_buffer (tunsox, mem, &wbuf);
206         }
207 }
208
209 /* Start to obtain an IPv6 address.
210  */
211 static void get_dhcp6_lease (void) {
212         uint8_t *start = wbuf.data;
213         uint32_t mem [MEM_NETVAR_COUNT];
214         bzero (mem, sizeof (mem));
215         uint8_t *stop = netsend_dhcp6_solicit (start, mem);
216         mem [MEM_ALL_DONE] = (uint32_t) stop;
217         uint32_t len = mem [MEM_ALL_DONE] - mem [MEM_ETHER_HEAD];
218         if ((len > 0) && (len < 1500 + 18)) {
219                 netcore_send_buffer (tunsox, mem, &wbuf);
220         }
221 }
222
223
224 /* Boot the network: Obtain an IPv6 address, possibly based on an IPv4
225  * and a 6bed4 tunnel.  The routine returns success as nonzero.
226  *
227  * The booting procedure works through the following 1-second steps:
228  *  - ensure that the interface is connected to a network
229  *  - try IPv6 autoconfiguration a few times (random number)
230  *     - first try to reuse an older IPv6 address, even if it was DHCPv6
231  *  - try DHCPv6 a few times
232  *     - first try to reuse an older IPv6 address, even if it was autoconfig
233  *  - try DHCPv4 a few times
234  *     - first try to reuse an older IPv4 address
235  *     - when an IPv4 address is acquired, autoconfigure 6bed4 over it
236  */
237 int netcore_bootstrap (void) {
238         int process_packets (int sox, int secs); //TODO:NOTHERE
239         int i;
240         int rnd1 = 2 + ((ether_mine [5] & 0x1c) >> 2);
241         int rnd2 =       ether_mine [5] & 0x03;
242         //
243         // TODO: better wipe bindings at physical boot
244         bzero (ip4binding, sizeof (ip4binding));
245         bzero (ip6binding, sizeof (ip6binding));
246         //
247         // TODO: ensure physical network connectivity
248         //
249         // Obtain an IPv6 address through stateless autoconfiguration
250         i = rnd1;
251         while (i-- > 0) {
252                 solicit_router ();
253                 process_packets (tunsox, 2);
254                 // TODO: return 1 if bound
255         }
256         //
257         // Obtain an IPv6 address through DHCPv6
258         i = 3;
259         while (i-- > 0) {
260                 get_dhcp6_lease ();
261                 process_packets (tunsox, 3);
262                 // TODO: return 1 if bound
263         }
264         //
265         // Obtain an IPv4 address through DHCPv4
266         i = 3;
267         while (i-- > 0) {
268                 get_dhcp4_lease ();
269                 process_packets (tunsox, rnd2);
270                 // TODO: break if bound
271         }
272         // TODO: fail (return 0) if no IPv4 connection
273         //
274         // Obtain an IPv6 address through 6bed4 over IPv4
275         i = rnd1;
276         while (i-- > 0) {
277                 ip6binding [0].ip4binding = &ip4binding [0];
278                 ip6binding [0].flags |= I6B_ROUTE_SOURCE_6BED4_FLAG;
279                 solicit_router ();      //TODO: Over 6bed4
280                 sleep (1);
281                 // TODO:return 1 if bound
282         }
283         return 0;
284 }
285