blob: 3fa507bb76ad0a587596d1a534ac086e019123f0 [file] [log] [blame]
adamdunkels4b706112003-09-23 08:57:40 +00001/**
adamdunkelsb489e7a2003-10-14 11:12:50 +00002 * \addtogroup uip
3 * @{
4 */
5
6/**
7 * \defgroup uipfw uIP packet forwarding
8 * @{
9 *
10 */
11
12/**
adamdunkels4b706112003-09-23 08:57:40 +000013 * \file
14 * uIP packet forwarding.
15 * \author Adam Dunkels <adam@sics.se>
16 *
17 * This file implements a number of simple functions which do packet
18 * forwarding over multiple network interfaces with uIP.
19 *
20 */
21
22#include "uip.h"
adamdunkels75df6362003-10-01 11:34:46 +000023#include "uip_arch.h"
adamdunkels4b706112003-09-23 08:57:40 +000024#include "uip-fw.h"
25
26/**
27 * \internal
28 * The list of registered network interfaces.
29 */
30static struct uip_fw_netif *netifs = NULL;
31
32/**
33 * \internal
34 * A pointer to the default network interface.
35 */
36static struct uip_fw_netif *defaultnetif = NULL;
37
38struct tcpip_hdr {
39 /* IP header. */
40 u8_t vhl,
41 tos;
42 u16_t len,
43 ipid,
44 ipoffset;
45 u8_t ttl,
46 proto;
47 u16_t ipchksum;
48 u16_t srcipaddr[2],
49 destipaddr[2];
50
51 /* TCP header. */
52 u16_t srcport,
53 destport;
54 u8_t seqno[4],
55 ackno[4],
56 tcpoffset,
57 flags,
58 wnd[2];
59 u16_t tcpchksum;
60 u8_t urgp[2];
61 u8_t optdata[4];
62};
63
adamdunkels75df6362003-10-01 11:34:46 +000064struct icmpip_hdr {
65 /* IP header. */
66 u8_t vhl,
67 tos,
68 len[2],
69 ipid[2],
70 ipoffset[2],
71 ttl,
72 proto;
73 u16_t ipchksum;
74 u16_t srcipaddr[2],
75 destipaddr[2];
76 /* ICMP (echo) header. */
77 u8_t type, icode;
78 u16_t icmpchksum;
79 u16_t id, seqno;
80 u8_t payload[1];
81};
82
83/**
84 * \internal
85 * ICMP ECHO type definition.
86 */
87#define ICMP_ECHO 8
88
89/**
90 * \internal
91 * ICMP TIME-EXCEEDED type definition.
92 */
93#define ICMP_TE 11
94
adamdunkels4b706112003-09-23 08:57:40 +000095/**
96 * \internal
97 * Pointer to the TCP/IP headers of the packet in the uip_buf buffer.
98 */
99#define BUF ((struct tcpip_hdr *)&uip_buf[UIP_LLH_LEN])
100
101/**
102 * \internal
adamdunkels75df6362003-10-01 11:34:46 +0000103 * Pointer to the ICMP/IP headers of the packet in the uip_buf buffer.
104 */
105#define ICMPBUF ((struct icmpip_hdr *)&uip_buf[UIP_LLH_LEN])
106
107/**
108 * \internal
adamdunkels4b706112003-09-23 08:57:40 +0000109 * Certain fields of an IP packet that are used for identifying
110 * duplicate packets.
111 */
112struct fwcache_entry {
adamdunkels048a33f2004-02-16 20:48:53 +0000113 u16_t timer;
adamdunkels4b706112003-09-23 08:57:40 +0000114
115 u16_t len, offset, ipid;
116 u16_t srcipaddr[2];
117 u16_t destipaddr[2];
adamdunkels048a33f2004-02-16 20:48:53 +0000118 u16_t payload[2];
119 u8_t proto;
adamdunkels4b706112003-09-23 08:57:40 +0000120};
121
122/**
123 * \internal
124 * The number of packets to remember when looking for duplicates.
125 */
adamdunkels048a33f2004-02-16 20:48:53 +0000126#define FWCACHE_SIZE 20
adamdunkels4b706112003-09-23 08:57:40 +0000127
128/**
129 * \internal
130 * A cache of packet header fields which are used for
131 * identifying duplicate packets.
132 */
133static struct fwcache_entry fwcache[FWCACHE_SIZE];
134
adamdunkels048a33f2004-02-16 20:48:53 +0000135/**
136 * \internal
137 * The time that a packet cache is active.
138 */
139#define FW_TIME 20
140
adamdunkels4b706112003-09-23 08:57:40 +0000141/*------------------------------------------------------------------------------*/
142/**
143 * Initialize the uIP packet forwarding module.
144 */
145/*------------------------------------------------------------------------------*/
146void
147uip_fw_init(void)
148{
149 defaultnetif = netifs = NULL;
150}
151/*------------------------------------------------------------------------------*/
152/**
153 * \internal
154 * Check if an IP address is within the network defined by an IP
155 * address and a netmask.
156 *
157 * \param ipaddr The IP address to be checked.
158 * \param netipaddr The IP address of the network.
159 * \param netmask The netmask of the network.
160 *
161 * \return Non-zero if IP address is in network, zero otherwise.
162 */
163/*------------------------------------------------------------------------------*/
164static unsigned char
165ipaddr_maskcmp(u16_t *ipaddr, u16_t *netipaddr, u16_t *netmask)
166{
167 return (ipaddr[0] & netmask [0]) == (netipaddr[0] & netmask[0]) &&
168 (ipaddr[1] & netmask[1]) == (netipaddr[1] & netmask[1]);
169}
170/*------------------------------------------------------------------------------*/
171/**
172 * \internal
adamdunkels75df6362003-10-01 11:34:46 +0000173 * Send out an ICMP TIME-EXCEEDED message.
174 *
175 * This function replaces the packet in the uip_buf buffer with the
176 * ICMP packet.
177 */
178/*------------------------------------------------------------------------------*/
179static void
180time_exceeded(void)
181{
182 u16_t tmp16;
adamdunkels5f132992003-11-27 15:49:53 +0000183
184 /* We don't send out ICMP errors for ICMP messages. */
185 if(ICMPBUF->proto == UIP_PROTO_ICMP) {
186 uip_len = 0;
187 return;
188 }
adamdunkels75df6362003-10-01 11:34:46 +0000189 /* Copy fields from packet header into payload of this ICMP packet. */
190 memcpy(&(ICMPBUF->payload[0]), ICMPBUF, 28);
191
192 /* Set the ICMP type and code. */
193 ICMPBUF->type = ICMP_TE;
194 ICMPBUF->icode = 0;
195
196 /* Calculate the ICMP checksum. */
197 ICMPBUF->icmpchksum = 0;
198 ICMPBUF->icmpchksum = ~uip_chksum((u16_t *)&(ICMPBUF->type), 36);
199
200 /* Set the IP destination address to be the source address of the
201 original packet. */
202 tmp16= BUF->destipaddr[0];
203 BUF->destipaddr[0] = BUF->srcipaddr[0];
204 BUF->srcipaddr[0] = tmp16;
205 tmp16 = BUF->destipaddr[1];
206 BUF->destipaddr[1] = BUF->srcipaddr[1];
207 BUF->srcipaddr[1] = tmp16;
208
209 /* Set our IP address as the source address. */
210 BUF->srcipaddr[0] = uip_hostaddr[0];
211 BUF->srcipaddr[1] = uip_hostaddr[1];
212
213 /* The size of the ICMP time exceeded packet is 36 + the size of the
214 IP header (20) = 56. */
215 uip_len = 56;
216 ICMPBUF->len[0] = 0;
217 ICMPBUF->len[1] = uip_len;
218
219 /* Fill in the other fields in the IP header. */
220 ICMPBUF->vhl = 0x45;
221 ICMPBUF->tos = 0;
222 ICMPBUF->ipoffset[0] = ICMPBUF->ipoffset[1] = 0;
223 ICMPBUF->ttl = UIP_TTL;
224 ICMPBUF->proto = UIP_PROTO_ICMP;
225
226 /* Calculate IP checksum. */
227 ICMPBUF->ipchksum = 0;
228 ICMPBUF->ipchksum = ~(uip_ipchksum());
229
230
231}
232/*------------------------------------------------------------------------------*/
233/**
234 * \internal
adamdunkels4b706112003-09-23 08:57:40 +0000235 * Register a packet in the forwarding cache so that it won't be
236 * forwarded again.
237 */
238/*------------------------------------------------------------------------------*/
239static void
240fwcache_register(void)
241{
242 struct fwcache_entry *fw;
adamdunkels048a33f2004-02-16 20:48:53 +0000243 int i, oldest;
adamdunkels75df6362003-10-01 11:34:46 +0000244
adamdunkels048a33f2004-02-16 20:48:53 +0000245 oldest = FW_TIME;
246 fw = NULL;
247
248 /* Find the oldest entry in the cache. */
249 for(i = 0; i < FWCACHE_SIZE; ++i) {
250 if(fwcache[i].timer == 0) {
251 fw = &fwcache[i];
252 break;
253 } else if(fwcache[i].timer <= oldest) {
254 fw = &fwcache[i];
255 oldest = fwcache[i].timer;
256 }
257 }
258
259 fw->timer = FW_TIME;
adamdunkels4b706112003-09-23 08:57:40 +0000260 fw->len = BUF->len;
261 fw->offset = BUF->ipoffset;
262 fw->ipid = BUF->ipid;
263 fw->srcipaddr[0] = BUF->srcipaddr[0];
264 fw->srcipaddr[1] = BUF->srcipaddr[1];
265 fw->destipaddr[0] = BUF->destipaddr[0];
266 fw->destipaddr[1] = BUF->destipaddr[1];
267 fw->payload[0] = BUF->srcport;
268 fw->payload[1] = BUF->destport;
adamdunkels048a33f2004-02-16 20:48:53 +0000269 fw->proto = BUF->proto;
adamdunkels4b706112003-09-23 08:57:40 +0000270}
271/*------------------------------------------------------------------------------*/
272/**
adamdunkels75df6362003-10-01 11:34:46 +0000273 * \internal
274 * Find a network interface for the IP packet in uip_buf.
275 */
276/*------------------------------------------------------------------------------*/
277static struct uip_fw_netif *
278find_netif(void)
279{
280 struct uip_fw_netif *netif;
281
282 /* Walk through every network interface to check for a match. */
283 for(netif = netifs; netif != NULL; netif = netif->next) {
284 if(ipaddr_maskcmp(BUF->destipaddr, netif->ipaddr,
285 netif->netmask)) {
286 /* If there was a match, we break the loop. */
287 return netif;
288 }
289 }
290
291 /* If no matching netif was found, we use default netif. */
292 return defaultnetif;
293}
294/*------------------------------------------------------------------------------*/
295/**
adamdunkels4b706112003-09-23 08:57:40 +0000296 * Output an IP packet on the correct network interface.
297 *
298 * The IP packet should be present in the uip_buf buffer and its
299 * length in the global uip_len variable.
adamdunkels5f132992003-11-27 15:49:53 +0000300 *
301 * \retval UIP_FW_ZEROLEN Indicates that a zero-length packet
302 * transmission was attempted and that no packet was sent.
303 *
304 * \retval UIP_FW_NOROUTE No suitable network interface could be found
305 * for the outbound packet, and the packet was not sent.
306 *
307 * \return The return value from the actual network interface output
308 * function is passed unmodified as a return value.
adamdunkels4b706112003-09-23 08:57:40 +0000309 */
310/*------------------------------------------------------------------------------*/
adamdunkels5f132992003-11-27 15:49:53 +0000311u8_t
adamdunkels4b706112003-09-23 08:57:40 +0000312uip_fw_output(void)
313{
314 struct uip_fw_netif *netif;
adamdunkels5f132992003-11-27 15:49:53 +0000315
316 if(uip_len == 0) {
317 return UIP_FW_ZEROLEN;
318 }
adamdunkels4b706112003-09-23 08:57:40 +0000319
adamdunkels75df6362003-10-01 11:34:46 +0000320 netif = find_netif();
adamdunkels5f132992003-11-27 15:49:53 +0000321 /* printf("uip_fw_output: netif %p ->output %p len %d\n", netif,
322 netif->output,
323 uip_len);*/
324
325 if(netif == NULL) {
326 return UIP_FW_NOROUTE;
327 }
adamdunkels4b706112003-09-23 08:57:40 +0000328 /* If we now have found a suitable network interface, we call its
329 output function to send out the packet. */
adamdunkels5f132992003-11-27 15:49:53 +0000330 fwcache_register();
331 return netif->output();
adamdunkels4b706112003-09-23 08:57:40 +0000332}
333/*------------------------------------------------------------------------------*/
334/**
335 * Forward an IP packet in the uip_buf buffer.
336 *
337 *
338 *
adamdunkels048a33f2004-02-16 20:48:53 +0000339 * \return UIP_FW_FORWARDED if the packet was forwarded, UIP_FW_LOCAL if
adamdunkels5f132992003-11-27 15:49:53 +0000340 * the packet should be processed locally.
adamdunkels4b706112003-09-23 08:57:40 +0000341 */
342/*------------------------------------------------------------------------------*/
adamdunkels5f132992003-11-27 15:49:53 +0000343u8_t
adamdunkels4b706112003-09-23 08:57:40 +0000344uip_fw_forward(void)
345{
346 struct uip_fw_netif *netif;
347 struct fwcache_entry *fw;
adamdunkels75df6362003-10-01 11:34:46 +0000348
adamdunkels4b706112003-09-23 08:57:40 +0000349 /* First check if the packet is destined for ourselves and return 0
350 to indicate that the packet should be processed locally. */
351 if(BUF->destipaddr[0] == uip_hostaddr[0] &&
352 BUF->destipaddr[1] == uip_hostaddr[1]) {
adamdunkels048a33f2004-02-16 20:48:53 +0000353 return UIP_FW_LOCAL;
adamdunkels4b706112003-09-23 08:57:40 +0000354 }
355
adamdunkels75df6362003-10-01 11:34:46 +0000356 /* If we use ping IP address configuration, and our IP address is
357 not yet configured, we should intercept all ICMP echo packets. */
358#if UIP_PINGADDRCONF
359 if((uip_hostaddr[0] | uip_hostaddr[1]) == 0 &&
360 BUF->proto == UIP_PROTO_ICMP &&
361 ICMPBUF->type == ICMP_ECHO) {
adamdunkels048a33f2004-02-16 20:48:53 +0000362 return UIP_FW_LOCAL;
adamdunkels75df6362003-10-01 11:34:46 +0000363 }
364#endif /* UIP_PINGADDRCONF */
365
adamdunkels4b706112003-09-23 08:57:40 +0000366 /* Check if the packet is in the forwarding cache already, and if so
367 we drop it. */
adamdunkels048a33f2004-02-16 20:48:53 +0000368
369 for(fw = fwcache; fw <= &fwcache[FWCACHE_SIZE]; ++fw) {
370 if(fw->timer != 0 &&
371 fw->len == BUF->len &&
adamdunkels4b706112003-09-23 08:57:40 +0000372 fw->offset == BUF->ipoffset &&
adamdunkels048a33f2004-02-16 20:48:53 +0000373 fw->ipid == BUF->ipid &&
adamdunkels4b706112003-09-23 08:57:40 +0000374 fw->srcipaddr[0] == BUF->srcipaddr[0] &&
375 fw->srcipaddr[1] == BUF->srcipaddr[1] &&
376 fw->destipaddr[0] == BUF->destipaddr[0] &&
377 fw->destipaddr[1] == BUF->destipaddr[1] &&
adamdunkels048a33f2004-02-16 20:48:53 +0000378 fw->proto == BUF->proto &&
adamdunkels4b706112003-09-23 08:57:40 +0000379 fw->payload[0] == BUF->srcport &&
380 fw->payload[1] == BUF->destport) {
381 /* Drop packet. */
adamdunkels048a33f2004-02-16 20:48:53 +0000382 return UIP_FW_FORWARDED;
adamdunkels4b706112003-09-23 08:57:40 +0000383 }
384 }
adamdunkels048a33f2004-02-16 20:48:53 +0000385
adamdunkels75df6362003-10-01 11:34:46 +0000386 netif = find_netif();
adamdunkels4b706112003-09-23 08:57:40 +0000387
388 /* Decrement the TTL (time-to-live) value in the IP header */
adamdunkels75df6362003-10-01 11:34:46 +0000389 BUF->ttl = BUF->ttl - 1;
adamdunkels4b706112003-09-23 08:57:40 +0000390
adamdunkels75df6362003-10-01 11:34:46 +0000391 /* Update the IP checksum. */
adamdunkels4b706112003-09-23 08:57:40 +0000392 if(BUF->ipchksum >= htons(0xffff - 0x0100)) {
393 BUF->ipchksum = BUF->ipchksum + HTONS(0x0100) + 1;
394 } else {
395 BUF->ipchksum = BUF->ipchksum + HTONS(0x0100);
396 }
397
adamdunkels75df6362003-10-01 11:34:46 +0000398 /* If the TTL reaches zero we procude an ICMP time exceeded message
399 in the uip_buf buffer and forward that packet back to the sender
400 of the packet. */
401 if(BUF->ttl == 0) {
402 time_exceeded();
403 netif = find_netif();
404 }
405
adamdunkels4b706112003-09-23 08:57:40 +0000406 /* If we now have found a suitable network interface, we call its
407 output function to send out the packet. */
408 if(netif != NULL && uip_len > 0) {
409 uip_appdata = &uip_buf[UIP_LLH_LEN + 40];
410 fwcache_register();
411 netif->output();
412 }
413
414 /* Return non-zero to indicate that the packet was forwarded and that no
415 other processing should be made. */
adamdunkels048a33f2004-02-16 20:48:53 +0000416 return UIP_FW_FORWARDED;
adamdunkels4b706112003-09-23 08:57:40 +0000417}
418/*------------------------------------------------------------------------------*/
419/**
420 * Register a network interface with the forwarding module.
421 *
422 * \param netif A pointer to the network interface that is to be
423 * registered.
424 */
425/*------------------------------------------------------------------------------*/
426void
427uip_fw_register(struct uip_fw_netif *netif)
428{
429 netif->next = netifs;
430 netifs = netif;
431}
432/*------------------------------------------------------------------------------*/
433/**
434 * Register a default network interface.
435 *
436 * All packets that don't go out on any of the other interfaces will
437 * be routed to the default interface.
438 *
439 * \param netif A pointer to the network interface that is to be
440 * registered.
441 */
442/*------------------------------------------------------------------------------*/
443void
444uip_fw_default(struct uip_fw_netif *netif)
445{
446 defaultnetif = netif;
447}
448/*------------------------------------------------------------------------------*/
adamdunkels048a33f2004-02-16 20:48:53 +0000449/**
450 * Perform periodic processing.
451 */
452/*------------------------------------------------------------------------------*/
453void
454uip_fw_periodic(void)
455{
456 struct fwcache_entry *fw;
457 for(fw = fwcache; fw <= &fwcache[FWCACHE_SIZE]; ++fw) {
458 if(fw->timer > 0) {
459 --fw->timer;
460 }
461 }
462}
463/*------------------------------------------------------------------------------*/