1 /* netcore.c -- Networking core routines.
3 * From: Rick van Rein <rick@openfortress.nl>
14 #include <sys/types.h>
15 #include <sys/ioctl.h>
16 #include <sys/socket.h>
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>
26 #include <linux/if_tun.h>
27 #include <linux/if_ether.h>
29 #include <0cpm/netfun.h>
30 #include <0cpm/netdb.h>
36 extern struct tunbuf rbuf, wbuf; // TODO: Testing setup
37 extern int tunsox; // TODO: Testing setup
39 extern uint8_t ether_mine [ETHER_ADDR_LEN];
41 /* netcore_checksum_areas (void *area0, uint16_t len0, ..., NULL)
42 * Calculate the Internet checksum over the given areas
44 uint16_t netcore_checksum_areas (void *area0, ...) {
48 va_start (pairs, area0);
49 area = (uint16_t *) area0;
51 uint16_t len = (int16_t) va_arg (pairs, int);
53 csum = csum + ntohs (*area++);
57 csum = csum + * (uint8_t *) area;
59 area = va_arg (pairs, uint16_t *);
62 csum = (csum & 0xffff) + (csum >> 16);
63 csum = (csum & 0xffff) + (csum >> 16);
64 return htons ((uint16_t) ~csum);
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.
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];
82 // Checksum UDPv6 on IPv6
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 (
93 udp6 + 1, (int) htons (udp6->len) - 8,
97 // Checksum ICMPv6 on IPv6
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 (
107 icmp6, (int) mem [MEM_ALL_DONE] - (int) mem [MEM_ICMP6_HEAD],
111 // Checksum UDPv4 under IPv6 (6bed4 tunnel)
113 udp4->len = htons (mem [MEM_ALL_DONE] - mem [MEM_UDP4_HEAD]);
116 // Checksum UDPv4 on IPv4
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 (
126 udp4 + 1, (int) ntohs (udp4->len) - 8,
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],
142 ip4->check = netcore_checksum_areas (
148 // Determine the tunnel prefix info and ethernet protocol
150 wbuf->prefix.proto = htons (ETH_P_IP);
152 wbuf->prefix.proto = htons (ETH_P_IPV6);
154 wbuf->prefix.proto = htons (ETH_P_ARP);
158 eth->h_proto = wbuf->prefix.proto;
159 wbuf->prefix.flags = 0;
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");
170 // No guaranteed delivery -- swallow problem
179 /* Send a router solicitation.
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);
195 /* Start to obtain an IPv4 address.
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);
209 /* Start to obtain an IPv6 address.
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);
224 /* Boot the network: Obtain an IPv6 address, possibly based on an IPv4
225 * and a 6bed4 tunnel. The routine returns success as nonzero.
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
237 int netcore_bootstrap (void) {
238 int process_packets (int sox, int secs); //TODO:NOTHERE
240 int rnd1 = 2 + ((ether_mine [5] & 0x1c) >> 2);
241 int rnd2 = ether_mine [5] & 0x03;
243 // TODO: better wipe bindings at physical boot
244 bzero (ip4binding, sizeof (ip4binding));
245 bzero (ip6binding, sizeof (ip6binding));
247 // TODO: ensure physical network connectivity
249 // Obtain an IPv6 address through stateless autoconfiguration
253 process_packets (tunsox, 2);
254 // TODO: return 1 if bound
257 // Obtain an IPv6 address through DHCPv6
261 process_packets (tunsox, 3);
262 // TODO: return 1 if bound
265 // Obtain an IPv4 address through DHCPv4
269 process_packets (tunsox, rnd2);
270 // TODO: break if bound
272 // TODO: fail (return 0) if no IPv4 connection
274 // Obtain an IPv6 address through 6bed4 over IPv4
277 ip6binding [0].ip4binding = &ip4binding [0];
278 ip6binding [0].flags |= I6B_ROUTE_SOURCE_6BED4_FLAG;
279 solicit_router (); //TODO: Over 6bed4
281 // TODO:return 1 if bound