diff --git a/components/dfs/filesystems/devfs/devfs.c b/components/dfs/filesystems/devfs/devfs.c index 969c5d3321..1f4569558f 100644 --- a/components/dfs/filesystems/devfs/devfs.c +++ b/components/dfs/filesystems/devfs/devfs.c @@ -131,6 +131,7 @@ int dfs_device_fs_close(struct dfs_fd *file) int dfs_device_fs_open(struct dfs_fd *file) { + rt_err_t result; rt_device_t device; if (file->flags & DFS_O_CREAT) @@ -186,9 +187,16 @@ int dfs_device_fs_open(struct dfs_fd *file) if (device == RT_NULL) return -DFS_STATUS_ENODEV; - file->data = device; - - return DFS_STATUS_OK; + /* to open device */ + result = rt_device_open(device, RT_DEVICE_OFLAG_RDWR); + if (result == RT_EOK || result == -RT_ENOSYS) + { + file->data = device; + return DFS_STATUS_OK; + } + + /* open device failed. */ + return -DFS_STATUS_EIO; } int dfs_device_fs_stat(struct dfs_filesystem *fs, const char *path, struct stat *st) diff --git a/components/libc/SConscript b/components/libc/SConscript index 799ecccb36..4ca00c5b86 100644 --- a/components/libc/SConscript +++ b/components/libc/SConscript @@ -11,6 +11,8 @@ if GetDepend('RT_USING_LIBC'): objs = objs + SConscript('newlib/SConscript') elif rtconfig.PLATFORM == 'armcc': objs = objs + SConscript('armlibc/SConscript') + elif rtconfig.PLATFORM == 'iar': + objs = objs + SConscript('dlib/SConscript') else: if rtconfig.PLATFORM == 'gcc': objs = objs + SConscript('minilibc/SConscript') diff --git a/components/libc/armlibc/stubs.c b/components/libc/armlibc/stubs.c index b74710e056..bd57ca4c54 100644 --- a/components/libc/armlibc/stubs.c +++ b/components/libc/armlibc/stubs.c @@ -13,7 +13,7 @@ * Date Author Notes * 2012-11-23 Yihui The first version * 2013-11-24 aozima fixed _sys_read()/_sys_write() issues. - * 2014-08-03 bernard If using msh, use system() implementation + * 2014-08-03 bernard If using msh, use system() implementation * in msh. */ @@ -48,11 +48,11 @@ const char __stderr_name[] = "STDERR"; */ FILEHANDLE _sys_open(const char *name, int openmode) { -#ifdef RT_USING_DFS +#ifdef RT_USING_DFS int fd; int mode = O_RDONLY; #endif - + /* Register standard Input Output devices. */ if (strcmp(name, __stdin_name) == 0) return (STDIN); @@ -64,34 +64,34 @@ FILEHANDLE _sys_open(const char *name, int openmode) #ifndef RT_USING_DFS return -1; #else - /* Correct openmode from fopen to open */ - if (openmode & OPEN_PLUS) - { - if (openmode & OPEN_W) - { - mode |= (O_RDWR | O_TRUNC | O_CREAT); - } - else if (openmode & OPEN_A) - { - mode |= (O_RDWR | O_APPEND | O_CREAT); - } - else - mode |= O_RDWR; - } - else - { - if (openmode & OPEN_W) - { - mode |= (O_WRONLY | O_TRUNC | O_CREAT); - } - else if (openmode & OPEN_A) - { + /* Correct openmode from fopen to open */ + if (openmode & OPEN_PLUS) + { + if (openmode & OPEN_W) + { + mode |= (O_RDWR | O_TRUNC | O_CREAT); + } + else if (openmode & OPEN_A) + { + mode |= (O_RDWR | O_APPEND | O_CREAT); + } + else + mode |= O_RDWR; + } + else + { + if (openmode & OPEN_W) + { + mode |= (O_WRONLY | O_TRUNC | O_CREAT); + } + else if (openmode & OPEN_A) + { mode |= (O_WRONLY | O_APPEND | O_CREAT); - } - } + } + } fd = open(name, mode, 0); - if(fd < 0) + if (fd < 0) return -1; else return fd + STDERR + 1; @@ -121,10 +121,10 @@ int _sys_close(FILEHANDLE fh) */ int _sys_read(FILEHANDLE fh, unsigned char *buf, unsigned len, int mode) { -#ifdef RT_USING_DFS +#ifdef RT_USING_DFS int size; #endif - + if (fh == STDIN) { /* TODO */ @@ -138,7 +138,7 @@ int _sys_read(FILEHANDLE fh, unsigned char *buf, unsigned len, int mode) return 0; #else size = read(fh - STDERR - 1, buf, len); - if(size >= 0) + if (size >= 0) return len - size; else return -1; @@ -159,7 +159,7 @@ int _sys_write(FILEHANDLE fh, const unsigned char *buf, unsigned len, int mode) #ifdef RT_USING_DFS int size; #endif - + if ((fh == STDOUT) || (fh == STDERR)) { #ifndef RT_USING_CONSOLE @@ -170,18 +170,18 @@ int _sys_write(FILEHANDLE fh, const unsigned char *buf, unsigned len, int mode) console_device = rt_console_get_device(); if (console_device != 0) rt_device_write(console_device, 0, buf, len); - return 0; + return 0; #endif } - if(fh == STDIN) + if (fh == STDIN) return -1; #ifndef RT_USING_DFS return 0; #else size = write(fh - STDERR - 1, buf, len); - if(size >= 0) + if (size >= 0) return len - size; else return -1; @@ -270,6 +270,6 @@ int remove(const char *filename) int system(const char *string) { RT_ASSERT(0); - for(;;); + for (;;); } #endif diff --git a/components/libc/dlib/README.md b/components/libc/dlib/README.md new file mode 100644 index 0000000000..846eb3f5c0 --- /dev/null +++ b/components/libc/dlib/README.md @@ -0,0 +1,4 @@ +Dlib(IAR) porting for RT-Thread. + +Please define RT_USING_LIBC and compile RT-Thread with IAR compiler. + diff --git a/components/libc/dlib/SConscript b/components/libc/dlib/SConscript new file mode 100644 index 0000000000..a6b7554a07 --- /dev/null +++ b/components/libc/dlib/SConscript @@ -0,0 +1,15 @@ +from building import * +import rtconfig + +src = Glob('*.c') +cwd = GetCurrentDir() +group = [] + +CPPPATH = [cwd] +CPPDEFINES = ['RT_USING_DLIBC'] + +if rtconfig.PLATFORM == 'iar': + group = DefineGroup('dlib', src, depend = ['RT_USING_LIBC'], + CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES) + +Return('group') diff --git a/components/libc/dlib/environ.c b/components/libc/dlib/environ.c new file mode 100644 index 0000000000..1e44eb1627 --- /dev/null +++ b/components/libc/dlib/environ.c @@ -0,0 +1,25 @@ +/* File: environ.c + * this file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2015-01-28 Bernard first version + */ + +const char *__environ = "OS=RT-Thread"; + diff --git a/components/libc/dlib/rmtx.c b/components/libc/dlib/rmtx.c new file mode 100644 index 0000000000..4bf7140271 --- /dev/null +++ b/components/libc/dlib/rmtx.c @@ -0,0 +1,74 @@ +/* + * File : rmtx.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2015-01-28 Bernard first version + */ +#include +#include + +/* + * for IAR compiler, we recommand to define _DLIB_THREAD_SUPPORT + * as 2 for dlib multi-thread support. + */ + +#if _DLIB_THREAD_SUPPORT +typedef void* _Rmtx; +void _Mtxinit(_Rmtx *m) +{ + rt_mutex_t mutex; + + RT_ASSERT(m != RT_NULL); + + mutex = (rt_mutex_t)m; + rt_mutex_init(mutex, "iarMtx", RT_IPC_FLAG_FIFO); +} + +void _Mtxdst(_Rmtx *m) +{ + rt_mutex_t mutex; + + RT_ASSERT(m != RT_NULL); + + mutex = (rt_mutex_t)m; + rt_mutex_detach(mutex); +} + +void _Mtxlock(_Rmtx *m) +{ + rt_mutex_t mutex; + + RT_ASSERT(m != RT_NULL); + + mutex = (rt_mutex_t)m; + rt_mutex_take(mutex, RT_WAITING_FOREVER); +} + +void _Mtxunlock(_Rmtx *m) +{ + rt_mutex_t mutex; + + RT_ASSERT(m != RT_NULL); + + mutex = (rt_mutex_t)m; + rt_mutex_release(mutex); +} +#endif + diff --git a/components/libc/dlib/syscall_close.c b/components/libc/dlib/syscall_close.c new file mode 100644 index 0000000000..5792bb3806 --- /dev/null +++ b/components/libc/dlib/syscall_close.c @@ -0,0 +1,43 @@ +/* + * File : syscall_close.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2015-01-28 Bernard first version + */ +#include +#ifdef RT_USING_DFS +#include +#endif +#include + +#pragma module_name = "?__close" +int __close(int handle) +{ + if (handle == _LLIO_STDOUT || + handle == _LLIO_STDERR || + handle == _LLIO_STDIN) + return _LLIO_ERROR; + +#ifdef RT_USING_DFS + return close(handle); +#else + return 0; +#endif +} diff --git a/components/libc/dlib/syscall_lseek.c b/components/libc/dlib/syscall_lseek.c new file mode 100644 index 0000000000..950ba52f65 --- /dev/null +++ b/components/libc/dlib/syscall_lseek.c @@ -0,0 +1,43 @@ +/* + * File : syscall_lseek.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2015-01-28 Bernard first version + */ +#include +#ifdef RT_USING_DFS +#include +#endif +#include + +#pragma module_name = "?__lseek" +long __lseek(int handle, long offset, int whence) +{ + if (handle == _LLIO_STDOUT || + handle == _LLIO_STDERR || + handle == _LLIO_STDIN) + return _LLIO_ERROR; + +#ifdef RT_USING_DFS + return lseek(handle, offset, whence); +#else + return _LLIO_ERROR; +#endif +} diff --git a/components/libc/dlib/syscall_mem.c b/components/libc/dlib/syscall_mem.c new file mode 100644 index 0000000000..10eb298b9c --- /dev/null +++ b/components/libc/dlib/syscall_mem.c @@ -0,0 +1,44 @@ +/* + * File : syscall_mem.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2015-01-28 Bernard first version + */ +#include + +void *malloc(rt_size_t n) +{ + return rt_malloc(n); +} + +void *realloc(void *rmem, rt_size_t newsize) +{ + return rt_realloc(rmem, newsize); +} + +void *calloc(rt_size_t nelem, rt_size_t elsize) +{ + return rt_calloc(nelem, elsize); +} + +void free(void *rmem) +{ + rt_free(rmem); +} diff --git a/components/libc/dlib/syscall_open.c b/components/libc/dlib/syscall_open.c new file mode 100644 index 0000000000..95fa43b9c6 --- /dev/null +++ b/components/libc/dlib/syscall_open.c @@ -0,0 +1,88 @@ +/* +* File : syscall_open.c +* This file is part of RT-Thread RTOS +* COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +* +* Change Logs: +* Date Author Notes +* 2015-01-28 Bernard first version +*/ + +#include +#include +#ifdef RT_USING_DFS +#include +#endif + +#pragma module_name = "?__open" + +int __open(const char *filename, int mode) +{ +#ifndef RT_USING_DFS + return -1; +#else + int handle; + int open_mode = O_RDONLY; + + if (mode & _LLIO_CREAT) + { + open_mode |= O_CREAT; + + /* Check what we should do with it if it exists. */ + if (mode & _LLIO_APPEND) + { + /* Append to the existing file. */ + open_mode |= O_APPEND; + } + + if (mode & _LLIO_TRUNC) + { + /* Truncate the existsing file. */ + open_mode |= O_TRUNC; + } + } + + if (mode & _LLIO_TEXT) + { + /* we didn't support text mode */ + } + + switch (mode & _LLIO_RDWRMASK) + { + case _LLIO_RDONLY: + break; + + case _LLIO_WRONLY: + open_mode |= O_WRONLY; + break; + + case _LLIO_RDWR: + /* The file should be opened for both reads and writes. */ + open_mode |= O_RDWR; + break; + + default: + return -1; + } + + handle = open(filename, open_mode, 0); + if (handle < 0) + return -1; + + return handle + _LLIO_STDERR + 1; +#endif +} diff --git a/components/libc/dlib/syscall_read.c b/components/libc/dlib/syscall_read.c new file mode 100644 index 0000000000..aa13654189 --- /dev/null +++ b/components/libc/dlib/syscall_read.c @@ -0,0 +1,53 @@ +/* + * File : syscall_read.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2015-01-28 Bernard first version + */ + +#include +#ifdef RT_USING_DFS +#include +#endif +#include + +#pragma module_name = "?__read" +size_t __read(int handle, unsigned char *buf, size_t len) +{ +#ifdef RT_USING_DFS + int size; +#endif + + if (handle == _LLIO_STDIN) + { + /* TODO */ + return 0; + } + + if ((handle == _LLIO_STDOUT) || (handle == _LLIO_STDERR)) + return _LLIO_ERROR; + +#ifndef RT_USING_DFS + return _LLIO_ERROR; +#else + size = read(handle - _LLIO_STDERR - 1, buf, len); + return size; +#endif +} diff --git a/components/libc/dlib/syscall_remove.c b/components/libc/dlib/syscall_remove.c new file mode 100644 index 0000000000..b7dd9477b7 --- /dev/null +++ b/components/libc/dlib/syscall_remove.c @@ -0,0 +1,38 @@ +/* + * File : syscall_remove.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2015-01-28 Bernard first version + */ +#include +#ifdef RT_USING_DFS +#include +#endif +#include + +#pragma module_name = "?remove" +int remove(const char *val) +{ +#ifdef RT_USING_DFS + dfs_file_unlink(val); +#endif + + return 0; +} diff --git a/components/libc/dlib/syscall_write.c b/components/libc/dlib/syscall_write.c new file mode 100644 index 0000000000..a7140b9818 --- /dev/null +++ b/components/libc/dlib/syscall_write.c @@ -0,0 +1,62 @@ +/* + * File : syscall_write.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2015-01-28 Bernard first version + */ + +#include +#ifdef RT_USING_DFS +#include +#endif +#include + +#pragma module_name = "?__write" + +size_t __write(int handle, const unsigned char *buf, size_t len) +{ +#ifdef RT_USING_DFS + int size; +#endif + + if ((handle == _LLIO_STDOUT) || (handle == _LLIO_STDERR)) + { +#ifndef RT_USING_CONSOLE + return _LLIO_ERROR; +#else + rt_device_t console_device; + + console_device = rt_console_get_device(); + if (console_device != 0) rt_device_write(console_device, 0, buf, len); + + return len; +#endif + } + + if (handle == _LLIO_STDIN) return -1; + +#ifndef RT_USING_DFS + return _LLIO_ERROR; +#else + size = write(handle - _LLIO_STDERR - 1, buf, len); + return size; +#endif +} + diff --git a/components/libc/dlib/syscalls.h b/components/libc/dlib/syscalls.h new file mode 100644 index 0000000000..2c38854f72 --- /dev/null +++ b/components/libc/dlib/syscalls.h @@ -0,0 +1,23 @@ +/* File: syscalls.h + * this file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2015-01-28 Bernard first version + */ + diff --git a/components/net/lwip-1.4.1/src/core/ipv4/ip.c b/components/net/lwip-1.4.1/src/core/ipv4/ip.c index 95d2db404c..17bcd3929a 100644 --- a/components/net/lwip-1.4.1/src/core/ipv4/ip.c +++ b/components/net/lwip-1.4.1/src/core/ipv4/ip.c @@ -312,6 +312,11 @@ ip_input(struct pbuf *p, struct netif *inp) int check_ip_src=1; #endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ +#if IP_NAT + extern u8_t ip_nat_input(struct pbuf *p); + extern u8_t ip_nat_out(struct pbuf *p); +#endif + IP_STATS_INC(ip.recv); snmp_inc_ipinreceives(); @@ -487,15 +492,30 @@ ip_input(struct pbuf *p, struct netif *inp) /* packet not for us? */ if (netif == NULL) { +#if IP_FORWARD || IP_NAT + u8_t taken = 0; +#endif /* IP_FORWARD || IP_NAT */ /* packet not for us, route or discard */ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n")); -#if IP_FORWARD +#if IP_FORWARD || IP_NAT /* non-broadcast packet? */ - if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp)) { - /* try to forward IP packet on (other) interfaces */ - ip_forward(p, iphdr, inp); - } else + if (!ip_addr_isbroadcast(&(iphdr->dest), inp)) { +#if IP_NAT + /* check if we want to perform NAT with this packet. */ + taken = ip_nat_out(p); + if (!taken) +#endif /* IP_NAT */ + { +#if IP_FORWARD + /* try to forward IP packet on (other) interfaces */ + if (ip_forward(p, iphdr, inp) != NULL) { + taken = 1; + } #endif /* IP_FORWARD */ + } + } + if (!taken) +#endif /* IP_FORWARD || IP_NAT */ { snmp_inc_ipinaddrerrors(); snmp_inc_ipindiscards(); @@ -553,6 +573,13 @@ ip_input(struct pbuf *p, struct netif *inp) current_netif = inp; current_header = iphdr; +#if IP_NAT + if (!ip_addr_isbroadcast(&(iphdr->dest), inp) && + (ip_nat_input(p) != 0)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet consumed by nat layer\n")); + } else +#endif /* IP_NAT */ + #if LWIP_RAW /* raw input did not eat the packet? */ if (raw_input(p, inp) == 0) diff --git a/components/net/lwip-1.4.1/src/lwipopts.h b/components/net/lwip-1.4.1/src/lwipopts.h index bff172e280..cb7def8ae6 100644 --- a/components/net/lwip-1.4.1/src/lwipopts.h +++ b/components/net/lwip-1.4.1/src/lwipopts.h @@ -161,6 +161,12 @@ */ #define SYS_LIGHTWEIGHT_PROT (NO_SYS==0) +#ifdef LWIP_USING_NAT +#define IP_NAT 1 +#else +#define IP_NAT 0 +#endif + /* ---------- TCP options ---------- */ #ifdef RT_LWIP_TCP #define LWIP_TCP 1 diff --git a/components/net/lwip_dhcpd/SConscript b/components/net/lwip_dhcpd/SConscript new file mode 100644 index 0000000000..d5704c8c8b --- /dev/null +++ b/components/net/lwip_dhcpd/SConscript @@ -0,0 +1,10 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + +CPPPATH = [cwd] + +group = DefineGroup('LwIP', src, depend = ['RT_USING_LWIP', 'LWIP_USING_DHCPD'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/net/lwip_dhcpd/dhcp_server.c b/components/net/lwip_dhcpd/dhcp_server.c new file mode 100644 index 0000000000..47547a598b --- /dev/null +++ b/components/net/lwip_dhcpd/dhcp_server.c @@ -0,0 +1,398 @@ +/* + * File : dhcp_server.c + * A simple DHCP server implementation + * + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2013-2015, Shanghai Real-Thread Technology Co., Ltd + * http://www.rt-thread.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2013-01-30 aozima the first version + * 2013-08-08 aozima support different network segments. + * 2015-01-30 bernard release to RT-Thread RTOS. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* DHCP server option */ + +/* allocated client ip range */ +#ifndef DHCPD_CLIENT_IP_MIN +#define DHCPD_CLIENT_IP_MIN 2 +#endif +#ifndef DHCPD_CLIENT_IP_MAX +#define DHCPD_CLIENT_IP_MAX 254 +#endif + +/* the DHCP server address */ +#ifndef DHCPD_SERVER_IPADDR0 +#define DHCPD_SERVER_IPADDR0 192UL +#define DHCPD_SERVER_IPADDR1 168UL +#define DHCPD_SERVER_IPADDR2 169UL +#define DHCPD_SERVER_IPADDR3 1UL +#endif + +//#define DHCP_DEBUG_PRINTF + +#ifdef DHCP_DEBUG_PRINTF +#define DEBUG_PRINTF rt_kprintf("[DHCP] "); rt_kprintf +#else +#define DEBUG_PRINTF(...) +#endif /* DHCP_DEBUG_PRINTF */ + +/* we need some routines in the DHCP of lwIP */ +#undef LWIP_DHCP +#define LWIP_DHCP 1 +#include + +/* buffer size for receive DHCP packet */ +#define BUFSZ 1024 + +static uint8_t next_client_ip = DHCPD_CLIENT_IP_MIN; +static rt_err_t _low_level_dhcp_send(struct netif *netif, + const void *buffer, + rt_size_t size) +{ + struct pbuf *p; + struct eth_hdr *ethhdr; + struct ip_hdr *iphdr; + struct udp_hdr *udphdr; + + p = pbuf_alloc(PBUF_LINK, + SIZEOF_ETH_HDR + sizeof(struct ip_hdr) + + sizeof(struct udp_hdr) + size, + PBUF_RAM); + if (p == RT_NULL) return -RT_ENOMEM; + + ethhdr = (struct eth_hdr *)p->payload; + iphdr = (struct ip_hdr *)((char *)ethhdr + SIZEOF_ETH_HDR); + udphdr = (struct udp_hdr *)((char *)iphdr + sizeof(struct ip_hdr)); + + ETHADDR32_COPY(ðhdr->dest, (struct eth_addr *)ðbroadcast); + ETHADDR16_COPY(ðhdr->src, netif->hwaddr); + ethhdr->type = PP_HTONS(ETHTYPE_IP); + + iphdr->src.addr = 0x00000000; /* src: 0.0.0.0 */ + iphdr->dest.addr = 0xFFFFFFFF; /* src: 255.255.255.255 */ + + IPH_VHL_SET(iphdr, 4, IP_HLEN / 4); + IPH_TOS_SET(iphdr, 0x00); + IPH_LEN_SET(iphdr, htons(IP_HLEN + sizeof(struct udp_hdr) + size)); + IPH_ID_SET(iphdr, htons(2)); + IPH_OFFSET_SET(iphdr, 0); + IPH_TTL_SET(iphdr, 255); + IPH_PROTO_SET(iphdr, IP_PROTO_UDP); + IPH_CHKSUM_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + + udphdr->src = htons(DHCP_SERVER_PORT); + udphdr->dest = htons(DHCP_CLIENT_PORT); + udphdr->len = htons(sizeof(struct udp_hdr) + size); + udphdr->chksum = 0; + + memcpy((char *)udphdr + sizeof(struct udp_hdr), + buffer, size); + + return netif->linkoutput(netif, p); +} + +static void dhcpd_thread_entry(void *parameter) +{ + struct netif *netif = RT_NULL; + int sock; + int bytes_read; + char *recv_data; + rt_uint32_t addr_len; + struct sockaddr_in server_addr, client_addr; + struct dhcp_msg *msg; + int optval = 1; + + /* get ethernet interface. */ + netif = (struct netif*) parameter; + RT_ASSERT(netif != RT_NULL); + + /* our DHCP server information */ + DEBUG_PRINTF("DHCP server IP: %d.%d.%d.%d client IP: %d.%d.%d.%d-%d\n", + DHCPD_SERVER_IPADDR0, DHCPD_SERVER_IPADDR1, + DHCPD_SERVER_IPADDR2, DHCPD_SERVER_IPADDR3, + DHCPD_SERVER_IPADDR0, DHCPD_SERVER_IPADDR1, + DHCPD_SERVER_IPADDR2, DHCPD_CLIENT_IP_MIN, DHCPD_CLIENT_IP_MAX); + + /* allocate buffer for receive */ + recv_data = rt_malloc(BUFSZ); + if (recv_data == RT_NULL) + { + /* No memory */ + DEBUG_PRINTF("Out of memory\n"); + return; + } + + /* create a socket with UDP */ + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + { + DEBUG_PRINTF("create socket failed, errno = %d\n", errno); + rt_free(recv_data); + return; + } + + /* set to receive broadcast packet */ + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)); + + /* initialize server address */ + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(DHCP_SERVER_PORT); + server_addr.sin_addr.s_addr = INADDR_ANY; + rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero)); + + /* bind socket to the server address */ + if (bind(sock, (struct sockaddr *)&server_addr, + sizeof(struct sockaddr)) == -1) + { + /* bind failed. */ + DEBUG_PRINTF("bind server address failed, errno=%d\n", errno); + rt_free(recv_data); + return; + } + + addr_len = sizeof(struct sockaddr); + DEBUG_PRINTF("DHCP server listen on port %d...\n", DHCP_SERVER_PORT); + + while (1) + { + bytes_read = recvfrom(sock, recv_data, BUFSZ - 1, 0, + (struct sockaddr *)&client_addr, &addr_len); + if (bytes_read < DHCP_MSG_LEN) + { + DEBUG_PRINTF("packet too short, wait for next!\n"); + continue; + } + + msg = (struct dhcp_msg *)recv_data; + /* check message type to make sure we can handle it */ + if ((msg->op != DHCP_BOOTREQUEST) || (msg->cookie != PP_HTONL(DHCP_MAGIC_COOKIE))) + { + continue; + } + + /* handler. */ + { + uint8_t *dhcp_opt; + uint8_t option; + uint8_t length; + + uint8_t message_type = 0; + uint8_t finished = 0; + uint32_t request_ip = 0; + + dhcp_opt = (uint8_t *)msg + DHCP_OPTIONS_OFS; + while (finished == 0) + { + option = *dhcp_opt; + length = *(dhcp_opt + 1); + + switch (option) + { + case DHCP_OPTION_REQUESTED_IP: + request_ip = *(dhcp_opt + 2) << 24 | *(dhcp_opt + 3) << 16 + | *(dhcp_opt + 4) << 8 | *(dhcp_opt + 5); + break; + + case DHCP_OPTION_END: + finished = 1; + break; + + case DHCP_OPTION_MESSAGE_TYPE: + message_type = *(dhcp_opt + 2); + break; + + default: + break; + } /* switch(option) */ + + dhcp_opt += (2 + length); + } + + /* reply. */ + dhcp_opt = (uint8_t *)msg + DHCP_OPTIONS_OFS; + + /* check. */ + if (request_ip) + { + uint32_t client_ip = DHCPD_SERVER_IPADDR0 << 24 | DHCPD_SERVER_IPADDR1 << 16 + | DHCPD_SERVER_IPADDR2 << 8 | (next_client_ip); + + if (request_ip != client_ip) + { + *dhcp_opt++ = DHCP_OPTION_MESSAGE_TYPE; + *dhcp_opt++ = DHCP_OPTION_MESSAGE_TYPE_LEN; + *dhcp_opt++ = DHCP_NAK; + *dhcp_opt++ = DHCP_OPTION_END; + + DEBUG_PRINTF("requested IP invalid, reply DHCP_NAK\n"); + if (netif != RT_NULL) + { + int send_byte = (dhcp_opt - (uint8_t *)msg); + _low_level_dhcp_send(netif, msg, send_byte); + DEBUG_PRINTF("DHCP server send %d byte\n", send_byte); + } + next_client_ip++; + if (next_client_ip > DHCPD_CLIENT_IP_MAX) + next_client_ip = DHCPD_CLIENT_IP_MIN; + continue; + } + } + + if (message_type == DHCP_DISCOVER) + { + DEBUG_PRINTF("request DHCP_DISCOVER\n"); + DEBUG_PRINTF("reply DHCP_OFFER\n"); + + // DHCP_OPTION_MESSAGE_TYPE + *dhcp_opt++ = DHCP_OPTION_MESSAGE_TYPE; + *dhcp_opt++ = DHCP_OPTION_MESSAGE_TYPE_LEN; + *dhcp_opt++ = DHCP_OFFER; + + // DHCP_OPTION_SERVER_ID + *dhcp_opt++ = DHCP_OPTION_SERVER_ID; + *dhcp_opt++ = 4; + *dhcp_opt++ = DHCPD_SERVER_IPADDR0; + *dhcp_opt++ = DHCPD_SERVER_IPADDR1; + *dhcp_opt++ = DHCPD_SERVER_IPADDR2; + *dhcp_opt++ = DHCPD_SERVER_IPADDR3; + + // DHCP_OPTION_LEASE_TIME + *dhcp_opt++ = DHCP_OPTION_LEASE_TIME; + *dhcp_opt++ = 4; + *dhcp_opt++ = 0x00; + *dhcp_opt++ = 0x01; + *dhcp_opt++ = 0x51; + *dhcp_opt++ = 0x80; + } + else if (message_type == DHCP_REQUEST) + { + DEBUG_PRINTF("request DHCP_REQUEST\n"); + DEBUG_PRINTF("reply DHCP_ACK\n"); + + // DHCP_OPTION_MESSAGE_TYPE + *dhcp_opt++ = DHCP_OPTION_MESSAGE_TYPE; + *dhcp_opt++ = DHCP_OPTION_MESSAGE_TYPE_LEN; + *dhcp_opt++ = DHCP_ACK; + + // DHCP_OPTION_SERVER_ID + *dhcp_opt++ = DHCP_OPTION_SERVER_ID; + *dhcp_opt++ = 4; + *dhcp_opt++ = DHCPD_SERVER_IPADDR0; + *dhcp_opt++ = DHCPD_SERVER_IPADDR1; + *dhcp_opt++ = DHCPD_SERVER_IPADDR2; + *dhcp_opt++ = DHCPD_SERVER_IPADDR3; + + // DHCP_OPTION_SUBNET_MASK + *dhcp_opt++ = DHCP_OPTION_SUBNET_MASK; + *dhcp_opt++ = 4; + *dhcp_opt++ = 0xFF; + *dhcp_opt++ = 0xFF; + *dhcp_opt++ = 0xFF; + *dhcp_opt++ = 0x00; + +#ifdef DHCPD_USING_ROUTER + // DHCP_OPTION_ROUTER + *dhcp_opt++ = DHCP_OPTION_ROUTER; + *dhcp_opt++ = 4; + *dhcp_opt++ = DHCPD_SERVER_IPADDR0; + *dhcp_opt++ = DHCPD_SERVER_IPADDR1; + *dhcp_opt++ = DHCPD_SERVER_IPADDR2; + *dhcp_opt++ = 1; +#endif + + // DHCP_OPTION_DNS_SERVER, use the default DNS server address in lwIP + *dhcp_opt++ = DHCP_OPTION_DNS_SERVER; + *dhcp_opt++ = 4; + *dhcp_opt++ = 208; + *dhcp_opt++ = 67; + *dhcp_opt++ = 222; + *dhcp_opt++ = 222; + + // DHCP_OPTION_LEASE_TIME + *dhcp_opt++ = DHCP_OPTION_LEASE_TIME; + *dhcp_opt++ = 4; + *dhcp_opt++ = 0x00; + *dhcp_opt++ = 0x01; + *dhcp_opt++ = 0x51; + *dhcp_opt++ = 0x80; + } + else + { + DEBUG_PRINTF("un handle message:%d\n", message_type); + } + + // append DHCP_OPTION_END + *dhcp_opt++ = DHCP_OPTION_END; + + /* send reply. */ + if ((message_type == DHCP_DISCOVER) || (message_type == DHCP_REQUEST)) + { + msg->op = DHCP_BOOTREPLY; + IP4_ADDR(&msg->yiaddr, + DHCPD_SERVER_IPADDR0, DHCPD_SERVER_IPADDR1, + DHCPD_SERVER_IPADDR2, next_client_ip); + + client_addr.sin_addr.s_addr = INADDR_BROADCAST; + + if (netif != RT_NULL) + { + int send_byte = (dhcp_opt - (uint8_t *)msg); + _low_level_dhcp_send(netif, msg, send_byte); + DEBUG_PRINTF("DHCP server send %d byte\n", send_byte); + } + } + } /* handler. */ + } +} + +void dhcpd_start(char* netif_name) +{ + rt_thread_t thread; + struct netif *netif = RT_NULL; + + /* find ethernet interface. */ + netif = netif_find(netif_name); + if (netif == RT_NULL) + { + DEBUG_PRINTF("Not found network interface:%s\n", netif_name); + } + + thread = rt_thread_create("dhcpd", + dhcpd_thread_entry, netif, + 1024, + RT_THREAD_PRIORITY_MAX - 3, + 2); + if (thread != RT_NULL) + { + rt_thread_startup(thread); + } +} + diff --git a/components/net/lwip_dhcpd/dhcp_server.h b/components/net/lwip_dhcpd/dhcp_server.h new file mode 100644 index 0000000000..4bd1163706 --- /dev/null +++ b/components/net/lwip_dhcpd/dhcp_server.h @@ -0,0 +1,36 @@ +/* + * File : dhcp_server.h + * A simple DHCP server implementation + * + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2013-2015, Shanghai Real-Thread Technology Co., Ltd + * http://www.rt-thread.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2013-01-30 aozima the first version + * 2013-08-08 aozima support different network segments. + * 2015-01-30 bernard release to RT-Thread RTOS. + */ + +#ifndef DHCPV4_SERVER_H__ +#define DHCPV4_SERVER_H__ + +void dhcpd_start(char* netif_name); + +#endif + diff --git a/components/net/lwip_nat/README.md b/components/net/lwip_nat/README.md new file mode 100644 index 0000000000..9672a11b54 --- /dev/null +++ b/components/net/lwip_nat/README.md @@ -0,0 +1,19 @@ +lwIP NAT componenent + +If you want to use lwIP NAT componenent, please define LWIP_USING_NAT in rtconfig.h. + +In this case the network 213.129.231.168/29 is nat'ed when packets are sent to the +destination network 10.0.0.0/24 (untypical example - most users will have the other +way around). + +Use following code to add a NAT entry: + + ip_nat_entry_t nat_entry; + + nat_entry.out_if = (struct netif *)&emac_if1; + nat_entry.in_if = (struct netif *)&emac_if2; + IP4_ADDR(&nat_entry.source_net, 213, 129, 231, 168); + IP4_ADDR(&nat_entry.source_netmask, 255, 255, 255, 248); + IP4_ADDR(&nat_entry.dest_net, 10, 0, 0, 0); + IP4_ADDR(&nat_entry.source_netmask, 255, 0, 0, 0); + ip_nat_add(&_nat_entry); diff --git a/components/net/lwip_nat/SConscript b/components/net/lwip_nat/SConscript new file mode 100644 index 0000000000..ce684f677a --- /dev/null +++ b/components/net/lwip_nat/SConscript @@ -0,0 +1,10 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + +CPPPATH = [cwd] + +group = DefineGroup('LwIP', src, depend = ['RT_USING_LWIP', 'LWIP_USING_NAT'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/net/lwip_nat/ipv4_nat.c b/components/net/lwip_nat/ipv4_nat.c new file mode 100644 index 0000000000..13c1877f2c --- /dev/null +++ b/components/net/lwip_nat/ipv4_nat.c @@ -0,0 +1,1154 @@ +/** + * NAT - NAT implementation for lwIP supporting TCP/UDP and ICMP. + * Copyright (c) 2009 Christian Walter, ?Embedded Solutions, Vienna 2009. + * + * Copyright (c) 2010 lwIP project ;-) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + */ + +/* + * File : ipv4_nat.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2015, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2015-01-26 Hichard porting to RT-Thread + * 2015-01-27 Bernard code cleanup for lwIP in RT-Thread + */ + +/* + * TODOS: + * - we should decide if we want to use static tables for NAT or a linked + * list. + * - we should allocate icmp ping id if multiple clients are sending + * ping requests. + * - maybe we could hash the identifiers for TCP, ICMP and UDP and use + * a single table structure. This would reduce the code amount although + * it will cost performance. + * - NAT code must check for broadcast addresses and NOT forward + * them. + * + * - netif_remove must notify NAT code when a NAT'ed interface is removed + * - allocate NAT entries from a new memp pool instead of the heap + * - let ttl be ticks, not seconds + * + * HOWTO USE: + * + * Shows how to create NAT between a PPP interface and an internal NIC. + * In this case the network 213.129.231.168/29 is nat'ed when packets + * are sent to the destination network 10.0.0.0/24 (untypical example - + * most users will have the other way around). + * + * Step 1) Execute when network interfaces are ready. + * + * new_nat_entry.out_if = (struct netif *)&PPP_IF; + * new_nat_entry.in_if = (struct netif *)&EMAC_if; + * IP4_ADDR(&new_nat_entry.source_net, 213, 129, 231, 168); + * IP4_ADDR(&new_nat_entry.source_netmask, 255, 255, 255, 248); + * IP4_ADDR(&new_nat_entry.dest_net, 10, 0, 0, 0); + * IP4_ADDR(&new_nat_entry.source_netmask, 255, 0, 0, 0); + * ip_nat_add(&new_nat_entry); + */ + +#include "ipv4_nat.h" +#include "lwip/opt.h" + +#ifdef LWIP_USING_NAT + +#include "lwip/ip.h" +#include "lwip/inet.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/icmp.h" +#include "lwip/tcp_impl.h" +#include "lwip/udp.h" +#include "lwip/mem.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "netif/etharp.h" + +#include +#include + +/** Define this to enable debug output of this module */ +#ifndef LWIP_NAT_DEBUG +#define LWIP_NAT_DEBUG LWIP_DBG_OFF +#endif + +#define LWIP_NAT_TTL_INFINITE (INT_MAX) +#define LWIP_NAT_DEFAULT_TTL_SECONDS (128) +#define LWIP_NAT_FORWARD_HEADER_SIZE_MIN (sizeof(struct eth_hdr)) + +#define LWIP_NAT_DEFAULT_STATE_TABLES_ICMP (4) +#define LWIP_NAT_DEFAULT_STATE_TABLES_TCP (32) +#define LWIP_NAT_DEFAULT_STATE_TABLES_UDP (32) + +#define LWIP_NAT_DEFAULT_TCP_SOURCE_PORT (40000) +#define LWIP_NAT_DEFAULT_UDP_SOURCE_PORT (40000) + +#define IPNAT_ENTRY_RESET(x) do { \ + (x)->ttl = 0; \ +} while(0) + +typedef struct ip_nat_conf +{ + struct ip_nat_conf *next; + ip_nat_entry_t entry; +} ip_nat_conf_t; + +typedef struct ip_nat_entry_common +{ + s32_t ttl; /* @todo: do we really need this to be signed?? */ + ip_addr_t source; + ip_addr_t dest; + ip_nat_conf_t *cfg; +} ip_nat_entry_common_t; + +typedef struct ip_nat_entries_icmp +{ + ip_nat_entry_common_t common; + u16_t id; + u16_t seqno; +} ip_nat_entries_icmp_t; + +typedef struct ip_nat_entries_tcp +{ + ip_nat_entry_common_t common; + u16_t nport; + u16_t sport; + u16_t dport; +} ip_nat_entries_tcp_t; + +typedef struct ip_nat_entries_udp +{ + ip_nat_entry_common_t common; + u16_t nport; + u16_t sport; + u16_t dport; +} ip_nat_entries_udp_t; + +typedef union u_nat_entry +{ + ip_nat_entry_common_t *cmn; + ip_nat_entries_tcp_t *tcp; + ip_nat_entries_icmp_t *icmp; + ip_nat_entries_udp_t *udp; +} nat_entry_t; + +static ip_nat_conf_t *ip_nat_cfg = NULL; +static ip_nat_entries_icmp_t ip_nat_icmp_table[LWIP_NAT_DEFAULT_STATE_TABLES_ICMP]; +static ip_nat_entries_tcp_t ip_nat_tcp_table[LWIP_NAT_DEFAULT_STATE_TABLES_TCP]; +static ip_nat_entries_udp_t ip_nat_udp_table[LWIP_NAT_DEFAULT_STATE_TABLES_UDP]; + +/* ----------------------- Static functions (COMMON) --------------------*/ +static void ip_nat_chksum_adjust(u8_t *chksum, const u8_t *optr, s16_t olen, const u8_t *nptr, s16_t nlen); +static void ip_nat_cmn_init(ip_nat_conf_t *nat_config, const struct ip_hdr *iphdr, + ip_nat_entry_common_t *nat_entry); +static ip_nat_conf_t *ip_nat_shallnat(const struct ip_hdr *iphdr); +static void ip_nat_reset_state(ip_nat_conf_t *cfg); + +/* ----------------------- Static functions (DEBUG) ---------------------*/ +#if defined(LWIP_DEBUG) && (LWIP_NAT_DEBUG & LWIP_DBG_ON) +static void ip_nat_dbg_dump(const char *msg, const struct ip_hdr *iphdr); +static void ip_nat_dbg_dump_ip(const ip_addr_t *addr); +static void ip_nat_dbg_dump_icmp_nat_entry(const char *msg, const ip_nat_entries_icmp_t *nat_entry); +static void ip_nat_dbg_dump_tcp_nat_entry(const char *msg, const ip_nat_entries_tcp_t *nat_entry); +static void ip_nat_dbg_dump_udp_nat_entry(const char *msg, const ip_nat_entries_udp_t *nat_entry); +static void ip_nat_dbg_dump_init(ip_nat_conf_t *ip_nat_cfg_new); +static void ip_nat_dbg_dump_remove(ip_nat_conf_t *cur); +#else /* defined(LWIP_DEBUG) && (LWIP_NAT_DEBUG & LWIP_DBG_ON) */ +#define ip_nat_dbg_dump(msg, iphdr) +#define ip_nat_dbg_dump_ip(addr) +#define ip_nat_dbg_dump_icmp_nat_entry(msg, nat_entry) +#define ip_nat_dbg_dump_tcp_nat_entry(msg, nat_entry) +#define ip_nat_dbg_dump_udp_nat_entry(msg, nat_entry) +#define ip_nat_dbg_dump_init(ip_nat_cfg_new) +#define ip_nat_dbg_dump_remove(cur) +#endif /* defined(LWIP_DEBUG) && (LWIP_NAT_DEBUG & LWIP_DBG_ON) */ + +/* ----------------------- Static functions (TCP) -----------------------*/ +static ip_nat_entries_tcp_t *ip_nat_tcp_lookup_incoming(const struct ip_hdr *iphdr, const struct tcp_hdr *tcphdr); +static ip_nat_entries_tcp_t *ip_nat_tcp_lookup_outgoing(ip_nat_conf_t *nat_config, + const struct ip_hdr *iphdr, const struct tcp_hdr *tcphdr, + u8_t allocate); + +/* ----------------------- Static functions (UDP) -----------------------*/ +static ip_nat_entries_udp_t *ip_nat_udp_lookup_incoming(const struct ip_hdr *iphdr, const struct udp_hdr *udphdr); +static ip_nat_entries_udp_t *ip_nat_udp_lookup_outgoing(ip_nat_conf_t *nat_config, + const struct ip_hdr *iphdr, const struct udp_hdr *udphdr, + u8_t allocate); + +/** + * Timer callback function that calls ip_nat_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +nat_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: nat_timer()\n")); + + ip_nat_tmr(); + sys_timeout(LWIP_NAT_TMR_INTERVAL_SEC, nat_timer, NULL); +} + +/** Initialize this module */ +void +ip_nat_init(void) +{ + int i; + extern void lwip_ip_input_set_hook(int (*hook)(struct pbuf *p, struct netif *inp)); + + /* @todo: this can be omitted since we trust static variables + to be initialized to zero */ + for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_ICMP; i++) { + IPNAT_ENTRY_RESET(&ip_nat_icmp_table[i].common); + } + for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_TCP; i++) { + IPNAT_ENTRY_RESET(&ip_nat_tcp_table[i].common); + } + for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_UDP; i++) { + IPNAT_ENTRY_RESET(&ip_nat_udp_table[i].common); + } + + /* we must lock scheduler to protect following code */ + rt_enter_critical(); + + /* add a lwip timer for NAT */ + sys_timeout(LWIP_NAT_TMR_INTERVAL_SEC, nat_timer, NULL); + + /* un-protect */ + rt_exit_critical(); +} + +/** Allocate a new ip_nat_conf_t item */ +static ip_nat_conf_t* +ip_nat_alloc(void) +{ + ip_nat_conf_t *ret = (ip_nat_conf_t*)mem_malloc(sizeof(ip_nat_conf_t)); + return ret; +} + +/** Free a removed ip_nat_conf_t item */ +static void +ip_nat_free(ip_nat_conf_t *item) +{ + LWIP_ASSERT("item != NULL", item != NULL); + mem_free(item); +} + +/** Add a new NAT entry + * + * @param new_entry pointer to a structure used to initialize the entry + * @return ERR_OK if succeeded + */ +err_t +ip_nat_add(const ip_nat_entry_t *new_entry) +{ + err_t err = ERR_VAL; + ip_nat_conf_t *cur = ip_nat_cfg; + ip_nat_conf_t *ip_nat_cfg_new = ip_nat_alloc(); + LWIP_ASSERT("new_entry != NULL", new_entry != NULL); + + if (ip_nat_cfg_new != NULL) { + SMEMCPY(&ip_nat_cfg_new->entry, new_entry, sizeof(ip_nat_entry_t)); + ip_nat_cfg_new->next = NULL; + + ip_nat_dbg_dump_init(ip_nat_cfg_new); + + if (ip_nat_cfg == NULL) { + ip_nat_cfg = ip_nat_cfg_new; + } else { + /* @todo: do we really need to enqueue the new entry at the end?? */ + while (cur->next != NULL) { + cur = cur->next; + } + cur->next = ip_nat_cfg_new; + } + err = ERR_OK; + } else { + err = ERR_MEM; + } + return err; +} + +/** Remove a NAT entry previously added by 'ip_nat_add()'. + * + * @param remove_entry describes the entry to remove + */ +void +ip_nat_remove(const ip_nat_entry_t *remove_entry) +{ + ip_nat_conf_t *cur = ip_nat_cfg; + ip_nat_conf_t *next; + ip_nat_conf_t *previous = NULL; + + while (cur != NULL) { + /* Remove the NAT interfaces */ + if ((cur->entry.source_net.addr == remove_entry->source_net.addr) && + (cur->entry.source_netmask.addr == remove_entry->source_netmask.addr) && + (cur->entry.dest_net.addr == remove_entry->dest_net.addr) && + (cur->entry.dest_netmask.addr == remove_entry->dest_netmask.addr) && + (cur->entry.out_if == remove_entry->out_if) && + (cur->entry.in_if == remove_entry->in_if)) + { + ip_nat_dbg_dump_remove(cur); + + ip_nat_reset_state(cur); + next = cur->next; + if (cur == ip_nat_cfg) { + ip_nat_cfg = next; + } else { + LWIP_ASSERT("NULL != previous", NULL != previous); + previous->next = next; + } + /* free 'cur' or there will be a memory leak */ + ip_nat_free(cur); + return; + } else { + previous = cur; + cur = cur->next; + } + } +} + +/** Reset a NAT configured entry to be reused. + * Effectively calls IPNAT_ENTRY_RESET() on 'cfg'. + * + * @param cfg NAT entry to reset + */ +static void +ip_nat_reset_state(ip_nat_conf_t *cfg) +{ + int i; + + /* @todo: optimize this!!! + why do we search for it anyway, if we have the pointer??? */ + for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_ICMP; i++) { + if(ip_nat_icmp_table[i].common.cfg == cfg) { + IPNAT_ENTRY_RESET(&ip_nat_icmp_table[i].common); + } + } + for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_TCP; i++) { + if(ip_nat_icmp_table[i].common.cfg == cfg) { + IPNAT_ENTRY_RESET(&ip_nat_tcp_table[i].common); + } + } + for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_UDP; i++) { + if(ip_nat_icmp_table[i].common.cfg == cfg) { + IPNAT_ENTRY_RESET(&ip_nat_udp_table[i].common); + } + } +} + +/** Check if this packet should be routed or should be translated + * + * @param iphdr the IP header to check + * @return - a NAT entry if the packet shall be translated, + * - NULL if the packet shall be routed normally + */ +static ip_nat_conf_t * +ip_nat_shallnat(const struct ip_hdr *iphdr) +{ + ip_nat_conf_t *nat_config = ip_nat_cfg; + + for (nat_config = ip_nat_cfg; nat_config != NULL; nat_config = nat_config->next) { + if (ip_addr_netcmp(&(iphdr->dest), &(nat_config->entry.dest_net), + &(nat_config->entry.dest_netmask)) || + ip_addr_netcmp(&(iphdr->src), &(nat_config->entry.source_net), + &(nat_config->entry.source_netmask))) { + break; + } + } + + return nat_config; +} + +/** Check if the IP header can be hidden and if the remaining packet + * is long enough. p->payload is reset to the IP header on return. + * + * @param p received packet, p->payload pointing to IP header + * @param min_size minimum p->tot_len after hiding IP header + * @return a pointer to the next header (after IP header), + * NULL if hiding IP header fails or the packet is too short + */ +static void* +ip_nat_check_header(struct pbuf *p, u16_t min_size) +{ + void *ret = NULL; + struct ip_hdr *iphdr = (struct ip_hdr*)p->payload; + s16_t iphdr_len = IPH_HL(iphdr) * 4; + + if(!pbuf_header(p, -iphdr_len)) { + if(p->tot_len >= min_size) { + ret = p->payload; + } + /* Restore pbuf payload pointer from previous header check. */ + pbuf_header(p, iphdr_len); + } + return ret; +} + +/** Input processing: check if a received packet belongs to a NAT entry + * and if so, translated it and send it on. + * + * @param p received packet + * @return 1 if the packet has been consumed (it was a NAT packet), + * 0 if the packet has not been consumed (no NAT packet) + */ +u8_t +ip_nat_input(struct pbuf *p) +{ + struct ip_hdr *iphdr = (struct ip_hdr*)p->payload; + struct tcp_hdr *tcphdr; + struct udp_hdr *udphdr; + struct icmp_echo_hdr *icmphdr; + nat_entry_t nat_entry; + err_t err; + u8_t consumed = 0; + int i; + struct pbuf *q = NULL; + + nat_entry.cmn = NULL; + ip_nat_dbg_dump("ip_nat_in: checking nat for", iphdr); + + switch (IPH_PROTO(iphdr)) { + case IP_PROTO_TCP: + tcphdr = (struct tcp_hdr*)ip_nat_check_header(p, sizeof(struct tcp_hdr)); + if (tcphdr == NULL) { + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_input: short tcp packet (%" U16_F " bytes) discarded\n", p->tot_len)); + } else { + nat_entry.tcp = ip_nat_tcp_lookup_incoming(iphdr, tcphdr); + if (nat_entry.tcp != NULL) { + /* Refresh TCP entry */ + nat_entry.tcp->common.ttl = LWIP_NAT_DEFAULT_TTL_SECONDS; + tcphdr->dest = nat_entry.tcp->sport; + /* Adjust TCP checksum for changed destination port */ + ip_nat_chksum_adjust((u8_t *)&(tcphdr->chksum), + (u8_t *)&(nat_entry.tcp->nport), 2, (u8_t *)&(tcphdr->dest), 2); + /* Adjust TCP checksum for changing dest IP address */ + ip_nat_chksum_adjust((u8_t *)&(tcphdr->chksum), + (u8_t *)&(nat_entry.cmn->cfg->entry.out_if->ip_addr.addr), 4, + (u8_t *)&(nat_entry.cmn->source.addr), 4); + + consumed = 1; + } + } + break; + + case IP_PROTO_UDP: + udphdr = (struct udp_hdr *)ip_nat_check_header(p, sizeof(struct udp_hdr)); + if (udphdr == NULL) { + LWIP_DEBUGF(LWIP_NAT_DEBUG, + ("ip_nat_input: short udp packet (%" U16_F " bytes) discarded\n", + p->tot_len)); + } else { + nat_entry.udp = ip_nat_udp_lookup_incoming(iphdr, udphdr); + if (nat_entry.udp != NULL) { + /* Refresh UDP entry */ + nat_entry.udp->common.ttl = LWIP_NAT_DEFAULT_TTL_SECONDS; + udphdr->dest = nat_entry.udp->sport; + /* Adjust UDP checksum for changed destination port */ + ip_nat_chksum_adjust((u8_t *)&(udphdr->chksum), + (u8_t *)&(nat_entry.udp->nport), 2, (u8_t *)&(udphdr->dest), 2); + /* Adjust UDP checksum for changing dest IP address */ + ip_nat_chksum_adjust((u8_t *)&(udphdr->chksum), + (u8_t *)&(nat_entry.cmn->cfg->entry.out_if->ip_addr.addr), 4, + (u8_t *)&(nat_entry.cmn->source.addr), 4); + + consumed = 1; + } + } + break; + + case IP_PROTO_ICMP: + icmphdr = (struct icmp_echo_hdr *)ip_nat_check_header(p, sizeof(struct icmp_echo_hdr)); + if (icmphdr == NULL) { + LWIP_DEBUGF(LWIP_NAT_DEBUG, + ("ip_nat_out: short icmp echo reply packet (%" U16_F " bytes) discarded\n", + p->tot_len)); + } else { + if (ICMP_ER == ICMPH_TYPE(icmphdr)) { + for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_ICMP; i++) { + nat_entry.icmp = &ip_nat_icmp_table[i]; + if ((nat_entry.icmp->common.ttl) && + (iphdr->src.addr == nat_entry.icmp->common.dest.addr) && + (nat_entry.icmp->id == icmphdr->id) && + (nat_entry.icmp->seqno == icmphdr->seqno)) { + ip_nat_dbg_dump_icmp_nat_entry("found existing nat entry: ", nat_entry.icmp); + consumed = 1; + IPNAT_ENTRY_RESET(nat_entry.cmn); + break; + } + } + } + } + break; + + default: + break; + } + + if(consumed) { + /* packet consumed, send it out on in_if */ + struct netif *in_if; + + /* check if the pbuf has room for link headers */ + if (pbuf_header(p, PBUF_LINK_HLEN)) { + /* pbuf has no room for link headers, allocate an extra pbuf */ + q = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_input: no pbuf for outgoing header\n")); + // rt_kprintf("ip_nat_input: no pbuf for outgoing header\n"); + /* @todo: stats? */ + pbuf_free(p); + p = NULL; + return 1; + } else { + pbuf_cat(q, p); + } + } else { + /* restore p->payload to IP header */ + if (pbuf_header(p, -PBUF_LINK_HLEN)) { + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_input: restoring header failed\n")); + // rt_kprintf("ip_nat_input: restoring header failed\n"); + /* @todo: stats? */ + pbuf_free(p); + p = NULL; + return 1; + } + else q = p; + } + /* if we come here, q is the pbuf to send (either points to p or to a chain) */ + in_if = nat_entry.cmn->cfg->entry.in_if; + iphdr->dest.addr = nat_entry.cmn->source.addr; + ip_nat_chksum_adjust((u8_t *) & IPH_CHKSUM(iphdr), + (u8_t *) & (nat_entry.cmn->cfg->entry.out_if->ip_addr.addr), 4, + (u8_t *) & (iphdr->dest.addr), 4); + + ip_nat_dbg_dump("ip_nat_input: packet back to source after nat: ", iphdr); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_input: sending packet on interface (")); + ip_nat_dbg_dump_ip(&(in_if->ip_addr)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (")\n")); + + err = in_if->output(in_if, q, (ip_addr_t *)&(iphdr->dest)); + if(err != ERR_OK) { + LWIP_DEBUGF(LWIP_NAT_DEBUG, + ("ip_nat_input: failed to send rewritten packet. link layer returned %d\n", + err)); + // rt_kprintf("ip_nat_input: failed to send rewritten packet. link layer returned %d\n", err); + } + /* now that q (and/or p) is sent (or not), give up the reference to it + this frees the input pbuf (p) as we have consumed it. */ + pbuf_free(q); + } + return consumed; +} + +/** Check if one NAT entry timed out */ +static void +ip_nat_check_timeout(ip_nat_entry_common_t *nat_entry) +{ + if(nat_entry->ttl > 0) { + if(nat_entry->ttl != LWIP_NAT_TTL_INFINITE) { + /* this is not a 'no-timeout' entry */ + if(nat_entry->ttl > LWIP_NAT_TMR_INTERVAL_SEC) { + nat_entry->ttl -= LWIP_NAT_TMR_INTERVAL_SEC; + } else { + nat_entry->ttl = 0; + } + } + } +} + +/** The NAT timer function, to be called at an interval of + * LWIP_NAT_TMR_INTERVAL_SEC seconds. + */ +void +ip_nat_tmr(void) +{ + int i; + + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_tmr: removing old entries\n")); + + for(i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_ICMP; i++) { + ip_nat_check_timeout((ip_nat_entry_common_t *) & ip_nat_icmp_table[i]); + } + for(i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_TCP; i++) { + ip_nat_check_timeout((ip_nat_entry_common_t *) & ip_nat_tcp_table[i]); + } + for(i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_UDP; i++) { + ip_nat_check_timeout((ip_nat_entry_common_t *) & ip_nat_udp_table[i]); + } +} + +/** Vheck if we want to perform NAT with this packet. If so, send it out on + * the correct interface. + * + * @param p the packet to test/send + * @return 1: the packet has been sent using NAT, + * 0: the packet did not belong to a NAT entry + */ +u8_t +ip_nat_out(struct pbuf *p) +{ + u8_t sent = 0; + err_t err; + struct ip_hdr *iphdr = p->payload; + struct icmp_echo_hdr *icmphdr; + struct tcp_hdr *tcphdr; + struct udp_hdr *udphdr; + ip_nat_conf_t *nat_config; + nat_entry_t nat_entry; + int i; + + nat_entry.cmn = NULL; + + ip_nat_dbg_dump("ip_nat_out: checking nat for", iphdr); + + /* Check if this packet should be routed or should be translated */ + nat_config = ip_nat_shallnat(iphdr); + if (nat_config != NULL ) { + if (nat_config->entry.out_if == NULL) { + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_out: no external interface for nat table entry\n")); + } else { + switch (IPH_PROTO(iphdr)) + { + case IP_PROTO_TCP: + tcphdr = (struct tcp_hdr *)ip_nat_check_header(p, sizeof(struct tcp_hdr)); + if (tcphdr == NULL) { + LWIP_DEBUGF(LWIP_NAT_DEBUG, + ("ip_nat_out: short tcp packet (%" U16_F " bytes) discarded\n", p->tot_len)); + } else { + nat_entry.tcp = ip_nat_tcp_lookup_outgoing(nat_config, iphdr, tcphdr, 1); + if (nat_entry.tcp != NULL) { + /* Adjust TCP checksum for changing source port */ + tcphdr->src = nat_entry.tcp->nport; + ip_nat_chksum_adjust((u8_t *)&(tcphdr->chksum), + (u8_t *)&(nat_entry.tcp->sport), 2, (u8_t *)&(tcphdr->src), 2); + /* Adjust TCP checksum for changing source IP address */ + ip_nat_chksum_adjust((u8_t *)&(tcphdr->chksum), + (u8_t *)&(nat_entry.cmn->source.addr), 4, + (u8_t *)&(nat_entry.cmn->cfg->entry.out_if->ip_addr.addr), 4); + } + } + break; + + case IP_PROTO_UDP: + udphdr = (struct udp_hdr *)ip_nat_check_header(p, sizeof(struct udp_hdr)); + if (udphdr == NULL) { + LWIP_DEBUGF(LWIP_NAT_DEBUG, + ("ip_nat_out: short udp packet (%" U16_F " bytes) discarded\n", p->tot_len)); + } else { + nat_entry.udp = ip_nat_udp_lookup_outgoing(nat_config, iphdr, udphdr, 1); + if (nat_entry.udp != NULL) { + /* Adjust UDP checksum for changing source port */ + udphdr->src = nat_entry.udp->nport; + ip_nat_chksum_adjust((u8_t *)&(udphdr->chksum), + (u8_t *)&(nat_entry.udp->sport), 2, (u8_t *) & (udphdr->src), 2); + /* Adjust UDP checksum for changing source IP address */ + ip_nat_chksum_adjust((u8_t *)&(udphdr->chksum), + (u8_t *)&(nat_entry.cmn->source.addr), 4, + (u8_t *)&(nat_entry.cmn->cfg->entry.out_if->ip_addr.addr), 4); + } + } + break; + + case IP_PROTO_ICMP: + icmphdr = (struct icmp_echo_hdr *)ip_nat_check_header(p, sizeof(struct icmp_echo_hdr)); + if(icmphdr == NULL) { + LWIP_DEBUGF(LWIP_NAT_DEBUG, + ("ip_nat_out: short icmp echo packet (%" U16_F " bytes) discarded\n", p->tot_len)); + } else { + if (ICMPH_TYPE(icmphdr) == ICMP_ECHO) { + for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_ICMP; i++) { + if (!ip_nat_icmp_table[i].common.ttl) { + nat_entry.icmp = &ip_nat_icmp_table[i]; + ip_nat_cmn_init(nat_config, iphdr, nat_entry.cmn); + nat_entry.icmp->id = icmphdr->id; + nat_entry.icmp->seqno = icmphdr->seqno; + ip_nat_dbg_dump_icmp_nat_entry(" ip_nat_out: created new NAT entry ", nat_entry.icmp); + break; + } + } + if (NULL == nat_entry.icmp) + { + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_out: no more NAT entries for ICMP available\n")); + } + } + } + break; + default: + break; + } + + if (nat_entry.cmn != NULL) { + struct netif *out_if = nat_entry.cmn->cfg->entry.out_if; + /* Exchange the IP source address with the address of the interface + * where the packet will be sent. + */ + /* @todo: check nat_config->entry.out_if agains nat_entry.cmn->cfg->entry.out_if */ + iphdr->src.addr = nat_config->entry.out_if->ip_addr.addr; + ip_nat_chksum_adjust((u8_t *) & IPH_CHKSUM(iphdr), + (u8_t *) & (nat_entry.cmn->source.addr), 4, (u8_t *) & iphdr->src.addr, 4); + + ip_nat_dbg_dump("ip_nat_out: rewritten packet", iphdr); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_out: sending packet on interface (")); + ip_nat_dbg_dump_ip(&(out_if->ip_addr)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (")\n")); + + err = out_if->output(out_if, p, (ip_addr_t *)&(iphdr->dest)); + if (err != ERR_OK) { + LWIP_DEBUGF(LWIP_NAT_DEBUG, + ("ip_nat_out: failed to send rewritten packet. link layer returned %d\n", err)); + // rt_kprintf("ip_nat_out: failed to send rewritten packet. link layer returned %d\n", err); + } else { + sent = 1; + } + } + } + } + + return sent; +} + +/** Initialize common parts of a NAT entry + * + * @param nat_config NAT config entry + * @param iphdr IP header from which to initialize the entry + * @param nat_entry entry to initialize + */ +static void +ip_nat_cmn_init(ip_nat_conf_t *nat_config, const struct ip_hdr *iphdr, ip_nat_entry_common_t *nat_entry) +{ + LWIP_ASSERT("NULL != nat_entry", NULL != nat_entry); + LWIP_ASSERT("NULL != nat_config", NULL != nat_config); + LWIP_ASSERT("NULL != iphdr", NULL != iphdr); + nat_entry->cfg = nat_config; + nat_entry->dest = *((ip_addr_t *)&iphdr->dest); + nat_entry->source = *((ip_addr_t *)&iphdr->src); + nat_entry->ttl = LWIP_NAT_DEFAULT_TTL_SECONDS; +} + +/** + * This function checks for incoming packets if we already have a NAT entry. + * If yes a pointer to the NAT entry is returned. Otherwise NULL. + * + * @param nat_config NAT configuration. + * @param iphdr The IP header. + * @param udphdr The UDP header. + * @return A pointer to an existing NAT entry or + * NULL if none is found. + */ +static ip_nat_entries_udp_t * +ip_nat_udp_lookup_incoming(const struct ip_hdr *iphdr, const struct udp_hdr *udphdr) +{ + int i; + ip_nat_entries_udp_t *nat_entry = NULL; + + for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_UDP; i++) { + if (ip_nat_udp_table[i].common.ttl) { + if ((iphdr->src.addr == ip_nat_udp_table[i].common.dest.addr) && + (udphdr->src == ip_nat_udp_table[i].dport) && + (udphdr->dest == ip_nat_udp_table[i].nport)) { + nat_entry = &ip_nat_udp_table[i]; + ip_nat_dbg_dump_udp_nat_entry("ip_nat_udp_lookup_incoming: found existing nat entry: ", + nat_entry); + break; + } + } + } + return nat_entry; +} + +/** + * This function checks if we already have a NAT entry for this UDP connection. + * If yes the a pointer to this NAT entry is returned. + * + * @param iphdr The IP header. + * @param udphdr The UDP header. + * @param allocate If no existing NAT entry is found and this flag is true + * a NAT entry is allocated. + */ +static ip_nat_entries_udp_t * +ip_nat_udp_lookup_outgoing(ip_nat_conf_t *nat_config, const struct ip_hdr *iphdr, + const struct udp_hdr *udphdr, u8_t allocate) +{ + int i; + nat_entry_t nat_entry; + int last_free = -1; + + nat_entry.cmn = NULL; + for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_UDP; i++) { + if (ip_nat_udp_table[i].common.ttl) { + if ((iphdr->src.addr == ip_nat_udp_table[i].common.source.addr) && + (iphdr->dest.addr == ip_nat_udp_table[i].common.dest.addr) && + (udphdr->src == ip_nat_udp_table[i].sport) && + (udphdr->dest == ip_nat_udp_table[i].dport)) { + nat_entry.udp = &ip_nat_udp_table[i]; + + ip_nat_dbg_dump_udp_nat_entry("ip_nat_udp_lookup_outgoing: found existing nat entry: ", + nat_entry.udp); + break; + } + } else { + last_free = i; + } + } + if (nat_entry.cmn == NULL) { + if (allocate) { + if (last_free != -1) { + nat_entry.udp = &ip_nat_udp_table[last_free]; + nat_entry.udp->nport = htons((u16_t) (LWIP_NAT_DEFAULT_UDP_SOURCE_PORT + i)); + nat_entry.udp->sport = udphdr->src; + nat_entry.udp->dport = udphdr->dest; + ip_nat_cmn_init(nat_config, iphdr, nat_entry.cmn); + + ip_nat_dbg_dump_udp_nat_entry("ip_nat_udp_lookup_outgoing: created new nat entry: ", + nat_entry.udp); + } else { + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_udp_lookup_outgoing: no more NAT entries available\n")); + // rt_kprintf("ip_nat_udp_lookup_outgoing: no more NAT entries available\n"); + } + } + } + return nat_entry.udp; +} + +/** + * This function checks for incoming packets if we already have a NAT entry. + * If yes a pointer to the NAT entry is returned. Otherwise NULL. + * + * @param nat_config NAT configuration. + * @param iphdr The IP header. + * @param tcphdr The TCP header. + * @return A pointer to an existing NAT entry or NULL if none is found. + */ +static ip_nat_entries_tcp_t * +ip_nat_tcp_lookup_incoming(const struct ip_hdr *iphdr, const struct tcp_hdr *tcphdr) +{ + int i; + ip_nat_entries_tcp_t *nat_entry = NULL; + + for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_TCP; i++) { + if (ip_nat_tcp_table[i].common.ttl) { + if ((iphdr->src.addr == ip_nat_tcp_table[i].common.dest.addr) && + (tcphdr->src == ip_nat_tcp_table[i].dport) && + (tcphdr->dest == ip_nat_tcp_table[i].nport)) { + nat_entry = &ip_nat_tcp_table[i]; + + ip_nat_dbg_dump_tcp_nat_entry("ip_nat_tcp_lookup_incoming: found existing nat entry: ", + nat_entry); + break; + } + } + } + return nat_entry; +} + +/** + * This function checks if we already have a NAT entry for this TCP connection. + * If yes the a pointer to this NAT entry is returned. + * + * @param iphdr The IP header. + * @param tcphdr The TCP header. + * @param allocate If no existing NAT entry is found and this flag is true + * a NAT entry is allocated. + */ +static ip_nat_entries_tcp_t * +ip_nat_tcp_lookup_outgoing(ip_nat_conf_t *nat_config, const struct ip_hdr *iphdr, + const struct tcp_hdr *tcphdr, u8_t allocate) +{ + int i; + nat_entry_t nat_entry; + int last_free = -1; + + nat_entry.cmn = NULL; + for (i = 0; i < LWIP_NAT_DEFAULT_STATE_TABLES_TCP; i++) { + if (ip_nat_tcp_table[i].common.ttl) { + if ((iphdr->src.addr == ip_nat_tcp_table[i].common.source.addr) && + (iphdr->dest.addr == ip_nat_tcp_table[i].common.dest.addr) && + (tcphdr->src == ip_nat_tcp_table[i].sport) && + (tcphdr->dest == ip_nat_tcp_table[i].dport)) { + nat_entry.tcp = &ip_nat_tcp_table[i]; + + ip_nat_dbg_dump_tcp_nat_entry("ip_nat_tcp_lookup_outgoing: found existing nat entry: ", + nat_entry.tcp); + break; + } + } else { + last_free = i; + } + } + if (nat_entry.cmn == NULL) { + if (allocate) { + if (last_free != -1) { + nat_entry.tcp = &ip_nat_tcp_table[last_free]; + nat_entry.tcp->nport = htons((u16_t) (LWIP_NAT_DEFAULT_TCP_SOURCE_PORT + i)); + nat_entry.tcp->sport = tcphdr->src; + nat_entry.tcp->dport = tcphdr->dest; + ip_nat_cmn_init(nat_config, iphdr, nat_entry.cmn); + + ip_nat_dbg_dump_tcp_nat_entry("ip_nat_tcp_lookup_outgoing: created new nat entry: ", + nat_entry.tcp); + } else { + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_udp_lookup_outgoing: no more NAT entries available\n")); + // rt_kprintf("ip_nat_udp_lookup_outgoing: no more NAT entries available\n"); + } + } + } + return nat_entry.tcp; +} + +/** Adjusts the checksum of a NAT'ed packet without having to completely recalculate it + * @todo: verify this works for little- and big-endian + * + * @param chksum points to the chksum in the packet + * @param optr points to the old data in the packet + * @param olen length of old data + * @param nptr points to the new data in the packet + * @param nlen length of new data + */ +static void +ip_nat_chksum_adjust(u8_t *chksum, const u8_t *optr, s16_t olen, const u8_t *nptr, s16_t nlen) +{ + s32_t x, oldval, newval; + + LWIP_ASSERT("NULL != chksum", NULL != chksum); + LWIP_ASSERT("NULL != optr", NULL != optr); + LWIP_ASSERT("NULL != nptr", NULL != nptr); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_chksum_adjust: chksum=%p, optr=%p, olen=%" U16_F ", nptr=%p, nlen=%" U16_F "\n", + chksum, optr, olen, nptr, nlen)); + x = chksum[0] * 256 + chksum[1]; + x = ~x & 0xFFFF; + while (olen) { + oldval = optr[0] * 256 + optr[1]; + optr += 2; + x -= oldval & 0xffff; + if (x <= 0) { + x--; + x &= 0xffff; + } + olen -= 2; + } + while (nlen) { + newval = nptr[0] * 256 + nptr[1]; + nptr += 2; + x += newval & 0xffff; + if (x & 0x10000) { + x++; + x &= 0xffff; + } + nlen -= 2; + } + x = ~x & 0xFFFF; + chksum[0] = x / 256; + chksum[1] = x & 0xff; + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_chksum_adjust: chksum = 0x%x\n", *((u16_t *) chksum))); +} + +#if defined(LWIP_DEBUG) && (LWIP_NAT_DEBUG & LWIP_DBG_ON) +/** + * This function dumps an IP address + * + * @param addr IP address + */ +static void +ip_nat_dbg_dump_ip(const ip_addr_t *addr) +{ + LWIP_ASSERT("NULL != addr", NULL != addr); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("%" U16_F ".%" U16_F ".%" U16_F ".%" U16_F, + ip4_addr1(addr), ip4_addr2(addr), ip4_addr3(addr), ip4_addr4(addr))); +} + +/** + * This function dumps an IP header + * + * @param msg a message to print + * @param iphdr IP header + */ +static void +ip_nat_dbg_dump(const char *msg, const struct ip_hdr *iphdr) +{ + LWIP_ASSERT("NULL != msg", NULL != msg); + LWIP_ASSERT("NULL != iphdr", NULL != iphdr); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("%s: IP: (", msg)); + ip_nat_dbg_dump_ip((ip_addr_t *)&(iphdr->src)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (" --> ")); + ip_nat_dbg_dump_ip((ip_addr_t *)&(iphdr->dest)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (" id=%" U16_F ", chksum=%" U16_F ")\n", + ntohs(IPH_ID(iphdr)), ntohs(IPH_CHKSUM(iphdr)))); +} + +/** + * This function dumps an ICMP echo reply/recho request nat entry. + * + * @param msg a message to print + * @param nat_entry the ICMP NAT entry to print + */ +static void +ip_nat_dbg_dump_icmp_nat_entry(const char *msg, const ip_nat_entries_icmp_t *nat_entry) +{ + LWIP_ASSERT("NULL != msg", NULL != msg); + LWIP_ASSERT("NULL != nat_entry", NULL != nat_entry); + LWIP_ASSERT("NULL != nat_entry->common.cfg", NULL != nat_entry->common.cfg); + LWIP_ASSERT("NULL != nat_entry->common.cfg->entry.out_if", + NULL != nat_entry->common.cfg->entry.out_if); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("%s", msg)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ICMP : (")); + ip_nat_dbg_dump_ip(&(nat_entry->common.source)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (" --> ")); + ip_nat_dbg_dump_ip(&(nat_entry->common.dest)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (" id=%" U16_F, ntohs(nat_entry->id))); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (", seq=%" U16_F, ntohs(nat_entry->seqno))); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (") mapped at (")); + ip_nat_dbg_dump_ip(&(nat_entry->common.cfg->entry.out_if->ip_addr)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (" --> ")); + ip_nat_dbg_dump_ip(&(nat_entry->common.dest)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (" id=%" U16_F, ntohs(nat_entry->id))); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (", seq=%" U16_F, ntohs(nat_entry->seqno))); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (")\n")); +} + +/** + * This function dumps an TCP nat entry. + * + * @param msg a message to print + * @param nat_entry the TCP NAT entry to print + */ +static void +ip_nat_dbg_dump_tcp_nat_entry(const char *msg, const ip_nat_entries_tcp_t *nat_entry) +{ + LWIP_ASSERT("NULL != msg", NULL != msg); + LWIP_ASSERT("NULL != nat_entry", NULL != nat_entry); + LWIP_ASSERT("NULL != nat_entry->common.cfg", NULL != nat_entry->common.cfg); + LWIP_ASSERT("NULL != nat_entry->common.cfg->entry.out_if", + NULL != nat_entry->common.cfg->entry.out_if); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("%s", msg)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("TCP : (")); + ip_nat_dbg_dump_ip(&(nat_entry->common.source)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->sport))); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (" --> ")); + ip_nat_dbg_dump_ip(&(nat_entry->common.dest)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->dport))); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (") mapped at (")); + ip_nat_dbg_dump_ip(&(nat_entry->common.cfg->entry.out_if->ip_addr)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->nport))); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (" --> ")); + ip_nat_dbg_dump_ip(&(nat_entry->common.dest)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->dport))); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (")\n")); +} + +/** + * This function dumps a UDP NAT entry. + * + * @param msg a message to print + * @param nat_entry the UDP NAT entry to print + */ +static void +ip_nat_dbg_dump_udp_nat_entry(const char *msg, const ip_nat_entries_udp_t *nat_entry) +{ + LWIP_ASSERT("NULL != msg", NULL != msg); + LWIP_ASSERT("NULL != nat_entry", NULL != nat_entry); + LWIP_ASSERT("NULL != nat_entry->common.cfg", NULL != nat_entry->common.cfg); + LWIP_ASSERT("NULL != nat_entry->common.cfg->entry.out_if", + NULL != nat_entry->common.cfg->entry.out_if); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("%s", msg)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("UDP : (")); + ip_nat_dbg_dump_ip(&(nat_entry->common.source)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->sport))); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (" --> ")); + ip_nat_dbg_dump_ip(&(nat_entry->common.dest)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->dport))); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (") mapped at (")); + ip_nat_dbg_dump_ip(&(nat_entry->common.cfg->entry.out_if->ip_addr)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->nport))); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (" --> ")); + ip_nat_dbg_dump_ip(&(nat_entry->common.dest)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (":%" U16_F, ntohs(nat_entry->dport))); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (")\n")); +} + +/** Prints some info when creating a new NAT entry */ +static void +ip_nat_dbg_dump_init(ip_nat_conf_t *ip_nat_cfg_new) +{ + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_init: added new NAT interface\n")); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_init: ")); + ip_nat_dbg_dump_ip(&(ip_nat_cfg_new->entry.source_net)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("/")); + ip_nat_dbg_dump_ip(&(ip_nat_cfg_new->entry.source_netmask)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("@")); + ip_nat_dbg_dump_ip(&(ip_nat_cfg_new->entry.in_if->ip_addr)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (" --> ")); + ip_nat_dbg_dump_ip(&(ip_nat_cfg_new->entry.dest_net)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("/")); + ip_nat_dbg_dump_ip(&(ip_nat_cfg_new->entry.dest_netmask)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("@")); + ip_nat_dbg_dump_ip(&(ip_nat_cfg_new->entry.out_if->ip_addr)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("\n")); +} + +/** Prints some info when removing a NAT entry */ +static void +ip_nat_dbg_dump_remove(ip_nat_conf_t *cur) +{ + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_remove: removing existing NAT interface\n")); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_remove: ")); + ip_nat_dbg_dump_ip(&(cur->entry.source_net)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("/")); + ip_nat_dbg_dump_ip(&(cur->entry.source_netmask)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("@")); + ip_nat_dbg_dump_ip(&(cur->entry.in_if->ip_addr)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, (" --> ")); + ip_nat_dbg_dump_ip(&(cur->entry.dest_net)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("/")); + ip_nat_dbg_dump_ip(&(cur->entry.dest_netmask)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("@")); + ip_nat_dbg_dump_ip(&(cur->entry.out_if->ip_addr)); + LWIP_DEBUGF(LWIP_NAT_DEBUG, ("\n")); +} +#endif /* defined(LWIP_DEBUG) && (LWIP_NAT_DEBUG & LWIP_DBG_ON) */ + +#endif /* IP_NAT */ diff --git a/components/net/lwip_nat/ipv4_nat.h b/components/net/lwip_nat/ipv4_nat.h new file mode 100644 index 0000000000..e369c781da --- /dev/null +++ b/components/net/lwip_nat/ipv4_nat.h @@ -0,0 +1,103 @@ +/** + * NAT - NAT implementation for lwIP supporting TCP/UDP and ICMP. + * Copyright (c) 2009 Christian Walter, ?Embedded Solutions, Vienna 2009. + * + * Copyright (c) 2010 lwIP project ;-) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + */ + +/* + * File : ipv4_nat.h + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2015, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2015-01-26 Hichard porting to RT-Thread + * 2015-01-27 Bernard code cleanup for lwIP in RT-Thread + */ + +#ifndef __LWIP_NAT_H__ +#define __LWIP_NAT_H__ + +#include + +#ifdef LWIP_USING_NAT + +#include "lwip/err.h" +#include "lwip/ip_addr.h" +#include "lwip/opt.h" + +/** Timer interval at which to call ip_nat_tmr() */ +#define LWIP_NAT_TMR_INTERVAL_SEC (30*1000) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct netif; +struct pbuf; + +typedef struct ip_nat_entry +{ + ip_addr_t source_net; + ip_addr_t source_netmask; + ip_addr_t dest_net; + ip_addr_t dest_netmask; + struct netif *out_if; + struct netif *in_if; +} ip_nat_entry_t; + +void ip_nat_init(void); +void ip_nat_tmr(void); +u8_t ip_nat_input(struct pbuf *p); +u8_t ip_nat_out(struct pbuf *p); + +err_t ip_nat_add(const ip_nat_entry_t *new_entry); +void ip_nat_remove(const ip_nat_entry_t *remove_entry); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* IP_NAT */ + +#endif /* __LWIP_NAT_H__ */