blob: 3ba8baa24447c7e23eb398be5517960ff4126634 [file] [log] [blame]
adamdunkelsa2f3c422004-09-12 20:24:53 +00001/*
2 * Copyright (c) 2004, Swedish Institute of Computer Science.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the Institute nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * This file is part of the Contiki operating system.
30 *
31 * Author: Adam Dunkels <adam@sics.se>
32 *
oliverschmidt37420872005-02-23 22:38:43 +000033 * $Id: uip-fw.c,v 1.9 2005/02/23 22:40:40 oliverschmidt Exp $
adamdunkelsa2f3c422004-09-12 20:24:53 +000034 */
adamdunkels4b706112003-09-23 08:57:40 +000035/**
adamdunkelsb489e7a2003-10-14 11:12:50 +000036 * \addtogroup uip
37 * @{
38 */
39
40/**
41 * \defgroup uipfw uIP packet forwarding
42 * @{
43 *
44 */
45
46/**
adamdunkels4b706112003-09-23 08:57:40 +000047 * \file
48 * uIP packet forwarding.
49 * \author Adam Dunkels <adam@sics.se>
50 *
51 * This file implements a number of simple functions which do packet
52 * forwarding over multiple network interfaces with uIP.
53 *
54 */
55
56#include "uip.h"
adamdunkels75df6362003-10-01 11:34:46 +000057#include "uip_arch.h"
adamdunkels4b706112003-09-23 08:57:40 +000058#include "uip-fw.h"
adamdunkelsa4253172004-06-06 06:11:15 +000059#include "uip-conf.h"
60
61#include <string.h> /* for memcpy() */
adamdunkels4b706112003-09-23 08:57:40 +000062
63/**
64 * \internal
65 * The list of registered network interfaces.
66 */
67static struct uip_fw_netif *netifs = NULL;
68
69/**
70 * \internal
71 * A pointer to the default network interface.
72 */
73static struct uip_fw_netif *defaultnetif = NULL;
74
75struct tcpip_hdr {
76 /* IP header. */
77 u8_t vhl,
78 tos;
79 u16_t len,
80 ipid,
81 ipoffset;
82 u8_t ttl,
83 proto;
84 u16_t ipchksum;
85 u16_t srcipaddr[2],
86 destipaddr[2];
87
88 /* TCP header. */
89 u16_t srcport,
90 destport;
91 u8_t seqno[4],
92 ackno[4],
93 tcpoffset,
94 flags,
95 wnd[2];
96 u16_t tcpchksum;
97 u8_t urgp[2];
98 u8_t optdata[4];
99};
100
adamdunkels75df6362003-10-01 11:34:46 +0000101struct icmpip_hdr {
102 /* IP header. */
103 u8_t vhl,
104 tos,
105 len[2],
106 ipid[2],
107 ipoffset[2],
108 ttl,
109 proto;
110 u16_t ipchksum;
111 u16_t srcipaddr[2],
112 destipaddr[2];
113 /* ICMP (echo) header. */
114 u8_t type, icode;
115 u16_t icmpchksum;
116 u16_t id, seqno;
117 u8_t payload[1];
118};
119
120/**
121 * \internal
122 * ICMP ECHO type definition.
123 */
124#define ICMP_ECHO 8
125
126/**
127 * \internal
128 * ICMP TIME-EXCEEDED type definition.
129 */
130#define ICMP_TE 11
131
adamdunkels4b706112003-09-23 08:57:40 +0000132/**
133 * \internal
134 * Pointer to the TCP/IP headers of the packet in the uip_buf buffer.
135 */
136#define BUF ((struct tcpip_hdr *)&uip_buf[UIP_LLH_LEN])
137
138/**
139 * \internal
adamdunkels75df6362003-10-01 11:34:46 +0000140 * Pointer to the ICMP/IP headers of the packet in the uip_buf buffer.
141 */
142#define ICMPBUF ((struct icmpip_hdr *)&uip_buf[UIP_LLH_LEN])
143
144/**
145 * \internal
adamdunkels4b706112003-09-23 08:57:40 +0000146 * Certain fields of an IP packet that are used for identifying
147 * duplicate packets.
148 */
149struct fwcache_entry {
adamdunkels048a33f2004-02-16 20:48:53 +0000150 u16_t timer;
adamdunkels4b706112003-09-23 08:57:40 +0000151
152 u16_t len, offset, ipid;
153 u16_t srcipaddr[2];
154 u16_t destipaddr[2];
adamdunkels048a33f2004-02-16 20:48:53 +0000155 u16_t payload[2];
156 u8_t proto;
adamdunkels4b706112003-09-23 08:57:40 +0000157};
158
159/**
160 * \internal
161 * The number of packets to remember when looking for duplicates.
162 */
adamdunkelsa4253172004-06-06 06:11:15 +0000163#ifdef UIP_CONF_FWCACHE_SIZE
164#define FWCACHE_SIZE UIP_CONF_FWCACHE_SIZE
165#else
adamdunkels63ba9112004-02-24 09:56:39 +0000166#define FWCACHE_SIZE 2
adamdunkelsa4253172004-06-06 06:11:15 +0000167#endif
168
adamdunkels4b706112003-09-23 08:57:40 +0000169
170/**
171 * \internal
172 * A cache of packet header fields which are used for
173 * identifying duplicate packets.
174 */
175static struct fwcache_entry fwcache[FWCACHE_SIZE];
176
adamdunkels048a33f2004-02-16 20:48:53 +0000177/**
178 * \internal
179 * The time that a packet cache is active.
180 */
181#define FW_TIME 20
182
adamdunkels4b706112003-09-23 08:57:40 +0000183/*------------------------------------------------------------------------------*/
184/**
185 * Initialize the uIP packet forwarding module.
186 */
187/*------------------------------------------------------------------------------*/
188void
189uip_fw_init(void)
190{
191 defaultnetif = netifs = NULL;
192}
193/*------------------------------------------------------------------------------*/
194/**
195 * \internal
196 * Check if an IP address is within the network defined by an IP
197 * address and a netmask.
198 *
199 * \param ipaddr The IP address to be checked.
200 * \param netipaddr The IP address of the network.
201 * \param netmask The netmask of the network.
202 *
203 * \return Non-zero if IP address is in network, zero otherwise.
204 */
205/*------------------------------------------------------------------------------*/
206static unsigned char
207ipaddr_maskcmp(u16_t *ipaddr, u16_t *netipaddr, u16_t *netmask)
208{
209 return (ipaddr[0] & netmask [0]) == (netipaddr[0] & netmask[0]) &&
210 (ipaddr[1] & netmask[1]) == (netipaddr[1] & netmask[1]);
211}
212/*------------------------------------------------------------------------------*/
213/**
214 * \internal
adamdunkels75df6362003-10-01 11:34:46 +0000215 * Send out an ICMP TIME-EXCEEDED message.
216 *
217 * This function replaces the packet in the uip_buf buffer with the
218 * ICMP packet.
219 */
220/*------------------------------------------------------------------------------*/
221static void
222time_exceeded(void)
223{
224 u16_t tmp16;
adamdunkels5f132992003-11-27 15:49:53 +0000225
226 /* We don't send out ICMP errors for ICMP messages. */
227 if(ICMPBUF->proto == UIP_PROTO_ICMP) {
228 uip_len = 0;
229 return;
230 }
adamdunkels75df6362003-10-01 11:34:46 +0000231 /* Copy fields from packet header into payload of this ICMP packet. */
232 memcpy(&(ICMPBUF->payload[0]), ICMPBUF, 28);
233
234 /* Set the ICMP type and code. */
235 ICMPBUF->type = ICMP_TE;
236 ICMPBUF->icode = 0;
237
238 /* Calculate the ICMP checksum. */
239 ICMPBUF->icmpchksum = 0;
240 ICMPBUF->icmpchksum = ~uip_chksum((u16_t *)&(ICMPBUF->type), 36);
241
242 /* Set the IP destination address to be the source address of the
243 original packet. */
244 tmp16= BUF->destipaddr[0];
245 BUF->destipaddr[0] = BUF->srcipaddr[0];
246 BUF->srcipaddr[0] = tmp16;
247 tmp16 = BUF->destipaddr[1];
248 BUF->destipaddr[1] = BUF->srcipaddr[1];
249 BUF->srcipaddr[1] = tmp16;
250
251 /* Set our IP address as the source address. */
252 BUF->srcipaddr[0] = uip_hostaddr[0];
253 BUF->srcipaddr[1] = uip_hostaddr[1];
254
255 /* The size of the ICMP time exceeded packet is 36 + the size of the
256 IP header (20) = 56. */
257 uip_len = 56;
258 ICMPBUF->len[0] = 0;
259 ICMPBUF->len[1] = uip_len;
260
261 /* Fill in the other fields in the IP header. */
262 ICMPBUF->vhl = 0x45;
263 ICMPBUF->tos = 0;
264 ICMPBUF->ipoffset[0] = ICMPBUF->ipoffset[1] = 0;
265 ICMPBUF->ttl = UIP_TTL;
266 ICMPBUF->proto = UIP_PROTO_ICMP;
267
268 /* Calculate IP checksum. */
269 ICMPBUF->ipchksum = 0;
270 ICMPBUF->ipchksum = ~(uip_ipchksum());
271
272
273}
274/*------------------------------------------------------------------------------*/
275/**
276 * \internal
adamdunkels4b706112003-09-23 08:57:40 +0000277 * Register a packet in the forwarding cache so that it won't be
278 * forwarded again.
279 */
280/*------------------------------------------------------------------------------*/
281static void
282fwcache_register(void)
283{
284 struct fwcache_entry *fw;
adamdunkels048a33f2004-02-16 20:48:53 +0000285 int i, oldest;
adamdunkels75df6362003-10-01 11:34:46 +0000286
adamdunkels048a33f2004-02-16 20:48:53 +0000287 oldest = FW_TIME;
288 fw = NULL;
289
290 /* Find the oldest entry in the cache. */
291 for(i = 0; i < FWCACHE_SIZE; ++i) {
292 if(fwcache[i].timer == 0) {
293 fw = &fwcache[i];
294 break;
295 } else if(fwcache[i].timer <= oldest) {
296 fw = &fwcache[i];
297 oldest = fwcache[i].timer;
298 }
299 }
300
301 fw->timer = FW_TIME;
adamdunkels4b706112003-09-23 08:57:40 +0000302 fw->len = BUF->len;
303 fw->offset = BUF->ipoffset;
304 fw->ipid = BUF->ipid;
305 fw->srcipaddr[0] = BUF->srcipaddr[0];
306 fw->srcipaddr[1] = BUF->srcipaddr[1];
307 fw->destipaddr[0] = BUF->destipaddr[0];
308 fw->destipaddr[1] = BUF->destipaddr[1];
309 fw->payload[0] = BUF->srcport;
310 fw->payload[1] = BUF->destport;
adamdunkels048a33f2004-02-16 20:48:53 +0000311 fw->proto = BUF->proto;
adamdunkels4b706112003-09-23 08:57:40 +0000312}
313/*------------------------------------------------------------------------------*/
314/**
adamdunkels75df6362003-10-01 11:34:46 +0000315 * \internal
316 * Find a network interface for the IP packet in uip_buf.
317 */
318/*------------------------------------------------------------------------------*/
319static struct uip_fw_netif *
320find_netif(void)
321{
322 struct uip_fw_netif *netif;
323
324 /* Walk through every network interface to check for a match. */
325 for(netif = netifs; netif != NULL; netif = netif->next) {
326 if(ipaddr_maskcmp(BUF->destipaddr, netif->ipaddr,
327 netif->netmask)) {
328 /* If there was a match, we break the loop. */
329 return netif;
330 }
331 }
332
333 /* If no matching netif was found, we use default netif. */
334 return defaultnetif;
335}
336/*------------------------------------------------------------------------------*/
337/**
adamdunkels4b706112003-09-23 08:57:40 +0000338 * Output an IP packet on the correct network interface.
339 *
340 * The IP packet should be present in the uip_buf buffer and its
341 * length in the global uip_len variable.
adamdunkels5f132992003-11-27 15:49:53 +0000342 *
343 * \retval UIP_FW_ZEROLEN Indicates that a zero-length packet
344 * transmission was attempted and that no packet was sent.
345 *
346 * \retval UIP_FW_NOROUTE No suitable network interface could be found
347 * for the outbound packet, and the packet was not sent.
348 *
349 * \return The return value from the actual network interface output
350 * function is passed unmodified as a return value.
adamdunkels4b706112003-09-23 08:57:40 +0000351 */
352/*------------------------------------------------------------------------------*/
adamdunkels5f132992003-11-27 15:49:53 +0000353u8_t
adamdunkels4b706112003-09-23 08:57:40 +0000354uip_fw_output(void)
355{
356 struct uip_fw_netif *netif;
adamdunkels5f132992003-11-27 15:49:53 +0000357
358 if(uip_len == 0) {
359 return UIP_FW_ZEROLEN;
360 }
adamdunkelsa4253172004-06-06 06:11:15 +0000361
362#if UIP_BROADCAST
363 /* Link local broadcasts go out on all interfaces. */
364 if(BUF->proto == UIP_PROTO_UDP &&
365 BUF->destipaddr[0] == 0xffff &&
366 BUF->destipaddr[1] == 0xffff) {
367 for(netif = netifs; netif != NULL; netif = netif->next) {
368 netif->output();
369 }
370 return UIP_FW_OK;
371 }
372#endif /* UIP_BROADCAST */
373
adamdunkels4b706112003-09-23 08:57:40 +0000374
adamdunkels75df6362003-10-01 11:34:46 +0000375 netif = find_netif();
adamdunkels5f132992003-11-27 15:49:53 +0000376 /* printf("uip_fw_output: netif %p ->output %p len %d\n", netif,
377 netif->output,
378 uip_len);*/
379
380 if(netif == NULL) {
381 return UIP_FW_NOROUTE;
382 }
adamdunkels4b706112003-09-23 08:57:40 +0000383 /* If we now have found a suitable network interface, we call its
384 output function to send out the packet. */
adamdunkels5f132992003-11-27 15:49:53 +0000385 fwcache_register();
386 return netif->output();
adamdunkels4b706112003-09-23 08:57:40 +0000387}
388/*------------------------------------------------------------------------------*/
389/**
390 * Forward an IP packet in the uip_buf buffer.
391 *
392 *
393 *
adamdunkels048a33f2004-02-16 20:48:53 +0000394 * \return UIP_FW_FORWARDED if the packet was forwarded, UIP_FW_LOCAL if
adamdunkels5f132992003-11-27 15:49:53 +0000395 * the packet should be processed locally.
adamdunkels4b706112003-09-23 08:57:40 +0000396 */
397/*------------------------------------------------------------------------------*/
adamdunkels5f132992003-11-27 15:49:53 +0000398u8_t
adamdunkels4b706112003-09-23 08:57:40 +0000399uip_fw_forward(void)
400{
401 struct uip_fw_netif *netif;
402 struct fwcache_entry *fw;
adamdunkels75df6362003-10-01 11:34:46 +0000403
adamdunkels4b706112003-09-23 08:57:40 +0000404 /* First check if the packet is destined for ourselves and return 0
405 to indicate that the packet should be processed locally. */
406 if(BUF->destipaddr[0] == uip_hostaddr[0] &&
407 BUF->destipaddr[1] == uip_hostaddr[1]) {
adamdunkels048a33f2004-02-16 20:48:53 +0000408 return UIP_FW_LOCAL;
adamdunkels4b706112003-09-23 08:57:40 +0000409 }
410
adamdunkelsa4253172004-06-06 06:11:15 +0000411#if UIP_BROADCAST
412 if(BUF->proto == UIP_PROTO_UDP &&
413 BUF->destipaddr[0] == 0xffff &&
414 BUF->destipaddr[1] == 0xffff) {
415 return UIP_FW_LOCAL;
416 }
417#endif /* UIP_BROADCAST */
418
adamdunkels75df6362003-10-01 11:34:46 +0000419 /* If we use ping IP address configuration, and our IP address is
420 not yet configured, we should intercept all ICMP echo packets. */
421#if UIP_PINGADDRCONF
422 if((uip_hostaddr[0] | uip_hostaddr[1]) == 0 &&
423 BUF->proto == UIP_PROTO_ICMP &&
424 ICMPBUF->type == ICMP_ECHO) {
adamdunkels048a33f2004-02-16 20:48:53 +0000425 return UIP_FW_LOCAL;
adamdunkels75df6362003-10-01 11:34:46 +0000426 }
427#endif /* UIP_PINGADDRCONF */
428
adamdunkels4b706112003-09-23 08:57:40 +0000429 /* Check if the packet is in the forwarding cache already, and if so
430 we drop it. */
adamdunkels048a33f2004-02-16 20:48:53 +0000431
432 for(fw = fwcache; fw <= &fwcache[FWCACHE_SIZE]; ++fw) {
433 if(fw->timer != 0 &&
434 fw->len == BUF->len &&
adamdunkels4b706112003-09-23 08:57:40 +0000435 fw->offset == BUF->ipoffset &&
adamdunkels048a33f2004-02-16 20:48:53 +0000436 fw->ipid == BUF->ipid &&
adamdunkels4b706112003-09-23 08:57:40 +0000437 fw->srcipaddr[0] == BUF->srcipaddr[0] &&
438 fw->srcipaddr[1] == BUF->srcipaddr[1] &&
439 fw->destipaddr[0] == BUF->destipaddr[0] &&
440 fw->destipaddr[1] == BUF->destipaddr[1] &&
adamdunkels048a33f2004-02-16 20:48:53 +0000441 fw->proto == BUF->proto &&
adamdunkels4b706112003-09-23 08:57:40 +0000442 fw->payload[0] == BUF->srcport &&
443 fw->payload[1] == BUF->destport) {
444 /* Drop packet. */
adamdunkels048a33f2004-02-16 20:48:53 +0000445 return UIP_FW_FORWARDED;
adamdunkels4b706112003-09-23 08:57:40 +0000446 }
447 }
adamdunkels048a33f2004-02-16 20:48:53 +0000448
adamdunkels75df6362003-10-01 11:34:46 +0000449 netif = find_netif();
adamdunkels4b706112003-09-23 08:57:40 +0000450
451 /* Decrement the TTL (time-to-live) value in the IP header */
adamdunkels75df6362003-10-01 11:34:46 +0000452 BUF->ttl = BUF->ttl - 1;
adamdunkels4b706112003-09-23 08:57:40 +0000453
adamdunkels75df6362003-10-01 11:34:46 +0000454 /* Update the IP checksum. */
adamdunkels63ba9112004-02-24 09:56:39 +0000455 if(BUF->ipchksum >= HTONS(0xffff - 0x0100)) {
adamdunkels4b706112003-09-23 08:57:40 +0000456 BUF->ipchksum = BUF->ipchksum + HTONS(0x0100) + 1;
457 } else {
458 BUF->ipchksum = BUF->ipchksum + HTONS(0x0100);
459 }
460
adamdunkels75df6362003-10-01 11:34:46 +0000461 /* If the TTL reaches zero we procude an ICMP time exceeded message
462 in the uip_buf buffer and forward that packet back to the sender
463 of the packet. */
464 if(BUF->ttl == 0) {
465 time_exceeded();
466 netif = find_netif();
467 }
468
adamdunkels4b706112003-09-23 08:57:40 +0000469 /* If we now have found a suitable network interface, we call its
470 output function to send out the packet. */
471 if(netif != NULL && uip_len > 0) {
oliverschmidt37420872005-02-23 22:38:43 +0000472 uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_TCPIP_HLEN];
adamdunkels4b706112003-09-23 08:57:40 +0000473 fwcache_register();
474 netif->output();
475 }
476
477 /* Return non-zero to indicate that the packet was forwarded and that no
478 other processing should be made. */
adamdunkels048a33f2004-02-16 20:48:53 +0000479 return UIP_FW_FORWARDED;
adamdunkels4b706112003-09-23 08:57:40 +0000480}
481/*------------------------------------------------------------------------------*/
482/**
483 * Register a network interface with the forwarding module.
484 *
485 * \param netif A pointer to the network interface that is to be
486 * registered.
487 */
488/*------------------------------------------------------------------------------*/
489void
490uip_fw_register(struct uip_fw_netif *netif)
491{
492 netif->next = netifs;
493 netifs = netif;
494}
495/*------------------------------------------------------------------------------*/
496/**
497 * Register a default network interface.
498 *
499 * All packets that don't go out on any of the other interfaces will
500 * be routed to the default interface.
501 *
502 * \param netif A pointer to the network interface that is to be
503 * registered.
504 */
505/*------------------------------------------------------------------------------*/
506void
507uip_fw_default(struct uip_fw_netif *netif)
508{
509 defaultnetif = netif;
510}
511/*------------------------------------------------------------------------------*/
adamdunkels048a33f2004-02-16 20:48:53 +0000512/**
513 * Perform periodic processing.
514 */
515/*------------------------------------------------------------------------------*/
516void
517uip_fw_periodic(void)
518{
519 struct fwcache_entry *fw;
520 for(fw = fwcache; fw <= &fwcache[FWCACHE_SIZE]; ++fw) {
521 if(fw->timer > 0) {
522 --fw->timer;
523 }
524 }
525}
526/*------------------------------------------------------------------------------*/