1 /* netreply.c -- Directly responding to parsed packets
3 * This file is part of 0cpm Firmerware.
5 * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
7 * 0cpm Firmerware is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, version 3.
11 * 0cpm Firmerware is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with 0cpm Firmerware. If not, see <http://www.gnu.org/licenses/>.
26 // #include <netinet/ip.h>
27 // #include <netinet/udp.h>
28 // #include <netinet/ip6.h>
29 // #include <netinet/ip_icmp.h>
30 // #include <netinet/icmp6.h>
31 // #include <netinet/if_ether.h>
37 #include <0cpm/timer.h>
38 #include <0cpm/netinet.h>
39 #include <0cpm/netfun.h>
40 #include <0cpm/netdb.h>
41 #include <0cpm/cons.h>
44 /* A few well-known addresses to look for
46 extern uint8_t ether_broadcast [ETHER_ADDR_LEN];
47 uint8_t ether_zero [ETHER_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 };
48 uint8_t prefix_6bed4 [8] = {
49 0x20, 0x01, 0xab, 0xba, 0x6b, 0xed, 0x00, 0x04 };
50 extern uint32_t ip4_6bed4;
51 uint8_t ip4_mine [4] = { 192, 168, 3, 13 };
53 extern uint8_t linklocal_mine [];
55 //TODO:MAC_FROM_BOTTOM// uint8_t ether_mine [ETHER_ADDR_LEN] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
56 uint8_t ether_mine [ETHER_ADDR_LEN] = { 0x00, 0x0b, 0x82, 0x19, 0xa0, 0xf4 };
58 uint16_t bootsecs = 0;
61 /******************* HEADERVEL CREATION UTILITIES *******************/
64 /* Create an Ethernet header.
65 * Some fields are filled later: h_proto
67 uint8_t *netreply_ether (uint8_t *pout, intptr_t *mem) {
68 struct ethhdr *ethin = (struct ethhdr *) mem [MEM_ETHER_HEAD];
69 struct ethhdr *ethout = (struct ethhdr *) pout;
70 if (mem [MEM_ETHER_DST] != 0) {
71 memcpy (ethout->h_dest, (void *) mem [MEM_ETHER_DST], ETHER_ADDR_LEN);
72 } else if (memcmp (ethin->h_source, ether_zero, ETHER_ADDR_LEN) != 0) {
73 memcpy (ethout->h_dest, ethin->h_source, ETHER_ADDR_LEN);
75 memset (ethout->h_dest, 0xff, ETHER_ADDR_LEN); // Broadcast
77 memcpy (ethout->h_source, ether_mine, ETHER_ADDR_LEN);
78 mem [MEM_ETHER_HEAD] = (intptr_t) ethout;
79 return &pout [sizeof (struct ethhdr)];
82 /* Create an IPv4 header.
83 * Some IPv4 fields are filled later: protocol, tot_len, check.
85 uint8_t *netreply_ip4 (uint8_t *pout, intptr_t *mem) {
88 pout = netreply_ether (pout, mem);
89 ip4in = (struct iphdr *) mem [MEM_IP4_HEAD];
90 ip4out = (struct iphdr *) pout;
91 bzero (ip4out, sizeof (struct iphdr));
92 netset8 (ip4out->version_ihl, 0x45);
93 netset8 (ip4out->ttl, 64);
94 netset16 (ip4out->frag_off, 0x4000); // Don't fragment
95 memcpy (&ip4out->saddr, &ip4in->daddr, 4);
96 memcpy (&ip4out->daddr, &ip4in->saddr, 4);
97 mem [MEM_IP4_HEAD] = (uintptr_t) ip4out;
98 return &pout [sizeof (struct iphdr)];
101 /* Create an UDPv4 header.
102 * Some fields are filled later: len, check.
104 uint8_t *netreply_udp4 (uint8_t *pout, intptr_t *mem) {
106 pout = netreply_ip4 (pout, mem);
107 udp = (struct udphdr *) pout;
108 netset16 (udp->source, mem [MEM_UDP4_DST_PORT]);
109 netset16 (udp->dest , mem [MEM_UDP4_SRC_PORT]);
110 mem [MEM_UDP4_HEAD] = (intptr_t) udp;
111 return &pout [sizeof (struct udphdr)];
114 /* Create an UDPv4 header for 6bed4.
115 * Some fields are filled later: len, check.
117 uint8_t *netreply_udp4_6bed4 (uint8_t *pout, intptr_t *mem) {
119 mem [MEM_IP4_SRC] = htonl (ip4_6bed4); // TODO -- netset16?
120 pout = netreply_ip4 (pout, mem);
121 udp = (struct udphdr *) pout;
122 netset16 (udp->source, mem [MEM_UDP4_DST_PORT]);
123 netset16 (udp->dest , 3653);
124 mem [MEM_6BED4_PLOAD] =
125 mem [MEM_UDP4_HEAD] = (intptr_t) udp;
126 return &pout [sizeof (struct udphdr)];
129 /* Create an IPv6 header.
130 * Some fields are filled later: plen, nxt.
132 uint8_t *netreply_ip6 (uint8_t *pout, intptr_t *mem) {
133 struct ip6_hdr *in6 = (struct ip6_hdr *) mem [MEM_IP6_HEAD];
135 if (mem [MEM_6BED4_PLOAD] != 0) {
136 // Use 6bed4 for destination address
137 pout = netreply_udp4_6bed4 (pout, mem);
139 // Use LAN for destination address
140 pout = netreply_ether (pout, mem);
142 ip6 = (struct ip6_hdr *) pout;
143 netset32 (ip6->ip6_flow, 0x60000000);
144 netset8 (ip6->ip6_hlim, 64);
145 memcpy (&ip6->ip6_src, &in6->ip6_dst, 16);
146 memcpy (&ip6->ip6_dst, &in6->ip6_src, 16);
147 mem [MEM_IP6_HEAD] = (intptr_t) ip6;
148 return &pout [sizeof (struct ip6_hdr)];
151 /* Create an UDPv6 header.
152 * Some fields are filled later: len, check.
154 uint8_t *netreply_udp6 (uint8_t *pout, intptr_t *mem) {
156 pout = netreply_ip6 (pout, mem);
157 udp = (struct udphdr *) pout;
158 netset16 (udp->source, mem [MEM_UDP6_DST_PORT]);
159 netset16 (udp->dest , mem [MEM_UDP6_SRC_PORT]);
160 mem [MEM_UDP6_HEAD] = (intptr_t) udp;
161 return &pout [sizeof (struct udphdr)];
165 /******************* APPLICATION LEVEL ROUTINES *******************/
168 /* Create an ARP Reply packet to respond to an ARP Query
169 * There are no checksum or length fields in ARP.
171 uint8_t *netreply_arp_query (uint8_t *pout, intptr_t *mem) {
172 struct ether_arp *arpout;
173 struct ether_arp *arpin;
174 bottom_printf ("Received an ARP Request; replying\n");
175 pout = netreply_ether (pout, mem);
176 arpout = (struct ether_arp *) pout;
177 arpin = (struct ether_arp *) mem [MEM_ARP_HEAD];
178 //TODO:OLD// if (memcmp (arpin->arp_tpa, ip4_mine, 4) == 0) {
179 // TODO: Real binding for IPv4
180 if (netget32 (*(nint32_t *)arpin->arp_tpa) == ip4binding [0].ip4addr) {
181 // arpout->ea_hdr.ar_hrd = htons (ARPHRD_IEEE802);
182 netset16 (arpout->ea_hdr.ar_hrd, ARPHRD_ETHER);
183 netset16 (arpout->ea_hdr.ar_pro, ETHERTYPE_IP);
184 netset8 (arpout->ea_hdr.ar_hln, ETHER_ADDR_LEN);
185 netset8 (arpout->ea_hdr.ar_pln, 4); // IPv4 len
186 netset16 (arpout->ea_hdr.ar_op, 2); // ARP Reply
187 memcpy (arpout->arp_sha, ether_mine, ETHER_ADDR_LEN);
188 netset32 (*(nint32_t *)arpout->arp_spa, mem [MEM_IP4_DST]);
189 memcpy (arpout->arp_tha, arpin->arp_sha, ETHER_ADDR_LEN);
190 memcpy (arpout->arp_tpa, arpin->arp_spa, 4);
191 return pout + sizeof (struct ether_arp);
197 /* Create an ICMPv4 Echo Reply packet to respond to Echo Request
198 * Some fields are filled later: icmp_cksum.
200 uint8_t *netreply_icmp4_echo_req (uint8_t *pout, intptr_t *mem) {
201 struct icmphdr *icmp4out;
202 struct icmphdr *icmp4in;
204 bottom_printf ("Received an ICMPv4 Echo Request; replying\n");
205 pout = netreply_ip4 (pout, mem);
206 icmp4out = (struct icmphdr *) pout;
207 icmp4in = (struct icmphdr *) mem [MEM_ICMP4_HEAD];
208 netset8 (icmp4out->type, ICMP_ECHOREPLY);
209 netset8 (icmp4out->code, 0);
210 memcpy (&icmp4out->un.echo, &icmp4in->un.echo, 4);
211 pout = pout + sizeof (struct icmphdr);
212 alen = mem [MEM_ALL_DONE] - mem [MEM_ICMP4_HEAD] - sizeof (struct icmphdr);
213 if ((alen > 0) && (alen < 128)) {
214 memcpy (pout, &icmp4in [1], alen);
217 mem [MEM_ICMP4_HEAD] = (intptr_t) icmp4out;
221 /* Create an ICMPv6 Echo Reply packet to respond to Echo Request
222 * Some fields are filled later: icmp6_cksum.
224 uint8_t *netreply_icmp6_echo_req (uint8_t *pout, intptr_t *mem) {
225 struct icmp6_hdr *icmp6;
227 bottom_printf ("Received an ICMPv6 Echo Request; replying\n");
228 pout = netreply_ip6 (pout, mem);
229 icmp6 = (struct icmp6_hdr *) pout;
230 netset8 (icmp6->icmp6_type, ICMP6_ECHO_REPLY);
231 netset8 (icmp6->icmp6_code, 0);
232 len = mem [MEM_ALL_DONE] - mem [MEM_ICMP6_HEAD];
233 memcpy (&icmp6->icmp6_data8,
234 (void *) (mem [MEM_ICMP6_HEAD] + 4),
236 mem [MEM_ICMP6_HEAD] = (intptr_t) icmp6;
240 /* Create a Neighbour Advertisement packet to respond to Neighbour Discovery.
241 * This will also respond to ff02::1:ffxx:xxxx packets that end in the last
242 * 3 bytes of the ethernet address.
244 * Some fields are filled later: icmp6_cksum.
246 uint8_t *netreply_icmp6_ngb_disc (uint8_t *pout, intptr_t *mem) {
251 struct icmp6_hdr *icmp6;
252 bottom_printf ("Received an ICMPv6 Neighbour Discovery; replying\n");
253 // The first comparison is against the link local address fe80::...
254 bndidx = IP6BINDING_COUNT;
255 addr = linklocal_mine;
256 flgs = I6B_DEFEND_ME;
258 if ((flgs & (I6B_EXPIRED | I6B_DEFEND_ME)) == I6B_DEFEND_ME) {
259 if (memcmp ((void *) (mem [MEM_ICMP6_HEAD] + 8), addr, 16) == 0) {
264 addr = ip6binding [bndidx].ip6addr;
265 flgs = ip6binding [bndidx].flags;
266 } while (bndidx-- >= 0);
270 pout = netreply_ip6 (pout, mem);
271 ip6 = (struct ip6_hdr *) mem [MEM_IP6_HEAD];
272 memcpy (&ip6->ip6_src, addr, 16);
273 netset8 (ip6->ip6_hlim, 255);
274 icmp6 = (struct icmp6_hdr *) pout;
275 netset8 (icmp6->icmp6_type, ND_NEIGHBOR_ADVERT);
276 netset8 (icmp6->icmp6_code, 0);
277 netset32 (icmp6->icmp6_data32 [0], 0x60000000);
278 memcpy (&icmp6->icmp6_data32 [1], addr, 16);
279 netset8 (icmp6->icmp6_data8 [20], ND_OPT_TARGET_LINKADDR);
280 netset8 (icmp6->icmp6_data8 [21], 1); // 1x 8 bytes
281 memcpy (icmp6->icmp6_data8 + 22, ether_mine, ETHER_ADDR_LEN);
282 mem [MEM_ICMP6_HEAD] = (intptr_t) icmp6;
283 return pout + 8 + 16 + 8;
286 /* Respond to a DHCPv4 OFFER with a REQUEST. An address is being
287 * offered in the yiaddr field (offset 16) of the DHCP packet, but
288 * that will also be repeated in the future DHCP ACK.
290 uint8_t *netreply_dhcp4_offer (uint8_t *pout, intptr_t *mem) {
291 uint8_t *yiaddrptr = (uint8_t *) (mem [MEM_DHCP4_HEAD] + 16);
293 static const uint8_t dhcp4_options [] = {
294 99, 130, 83, 99, // Magic cookie, RFC 1497
295 53, 1, 3, // DHCP message type REQUEST
296 50, 4, 0, 0, 0, 0, // Requested IP @ 4 + 3 + 2
297 // 55, 4, 1, 3, 42, 2, // Param Request List:
298 // mask, router, ntp?, time offset?.
301 bottom_printf ("DHCPv4 offer for %d.%d.%d.%d received -- requesting its activation\n", (int) yiaddrptr [0], (int) yiaddrptr [1], (int) yiaddrptr [2], (int) yiaddrptr [3]);
302 // TODO: Validate offer to be mine
303 mem [MEM_ETHER_DST] = (intptr_t) ether_broadcast;
304 pout = netreply_udp4 (pout, mem);
305 netset32 (((struct iphdr *) mem [MEM_IP4_HEAD])->daddr, 0xffffffff);
306 bzero (pout, 576); // erase the acceptable package size
307 pout [0] = 1; // bootrequest
308 pout [1] = 1; // ARP hardware address type
309 pout [2] = 6; // ARP hardware address length
310 pout [3] = 0; // hops
311 memcpy (pout + 4, ether_mine + 2, 4); // client-randomiser
312 *(uint16_t *) (pout + 8) = htons (bootsecs++); // 0 or seconds trying -- TODO:netset16
313 // flags=0, no broadcast reply needed
314 // ciaddr [4] is 0.0.0.0, the initial client address
315 // yiaddr [4] is 0.0.0.0, the "your" address
316 // siaddr [4] is 0.0.0.0, the server address is returned
317 // giaddr [4] is 0.0.0.0, gateway address is not set by client
318 memcpy (pout + 28, ether_mine, 6); // client hw addr
319 // sname [64], the server hostname is empty
320 // file [128], the boot filename, is empty
323 memcpy (popt, dhcp4_options, sizeof (dhcp4_options));
324 memcpy (popt + 4 + 3 + 2, yiaddrptr, 4);
325 // return popt + sizeof (dhcp4_options);
326 return popt + sizeof (dhcp4_options);
330 /* Respond to a DHCPv6 advertisement with a request.
332 * Note that the phone is supportive of multi-homing, and wants to
333 * be reachable over as many IPv6 addresses as are available, so it
334 * will actually take hold of all the IPv6 space that it can get.
336 uint8_t *netreply_dhcp6_advertise (uint8_t *pout, intptr_t *mem) {
340 bottom_printf ("DHCPv6 advertisement\n");
341 // TODO: Validate offer to be mine
342 adlen = mem [MEM_ALL_DONE] - mem [MEM_DHCP6_HEAD];
344 return NULL; // Suspicuously long options
346 pout = netreply_udp6 (pout, mem);
347 udp = (struct udphdr *) mem [MEM_UDP6_HEAD];
348 netset16 (udp->dest, 547);
349 memcpy (pout, (void *) mem [MEM_DHCP6_HEAD], adlen);
350 mem [MEM_DHCP6_HEAD] = (intptr_t) pout;
351 netdb_dhcp6_recurse_options ((nint16_t *) (pout + 4), adlen - 4);