aboutsummaryrefslogtreecommitdiffstats
path: root/lib/lufa/Projects/AVRISP-MKII/Lib/XPROG/XMEGANVM.h
blob: 8a8c5b048aa516cd4d5cf34f06976f3a812dc9c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
             LUFA Library
     Copyright (C) Dean Camera, 2017.

  dean [at] fourwalledcubicle [dot] com
           www.lufa-lib.org
*/

/*
  Copyright 2017  Dean Camera (dean [at] fourwalledcubicle [dot] com)

  Permission to use, copy, modify, distribute, and sell this
  software and its documentation for any purpose is hereby granted
  without fee, provided that the above copyright notice appear in
  all copies and that both that the copyright notice and this
  permission notice and warranty disclaimer appear in supporting
  documentation, and that the name of the author not be used in
  advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.

  The author disclaims all warranties with regard to this
  software, including all implied warranties of merchantability
  and fitness.  In no event shall the author be liable for any
  special, indirect or consequential damages or any damages
  whatsoever resulting from loss of use, data or profits, whether
  in an action of contract, negligence or other tortious action,
  arising out of or in connection with the use or performance of
  this software.
*/

/** \file
 *
 *  Header file for XMEGANVM.c.
 */

#ifndef _XMEGA_NVM_
#define _XMEGA_NVM_

	/* Includes: */
		#include <avr/io.h>
		#include <avr/interrupt.h>
		#include <stdbool.h>

		#include <LUFA/Common/Common.h>

		#include "XPROGProtocol.h"
		#include "XPROGTarget.h"
		#include "Config/AppConfig.h"

	/* Preprocessor Checks: */
		#if ((BOARD == BOARD_XPLAIN) || (BOARD == BOARD_XPLAIN_REV1))
			#undef ENABLE_ISP_PROTOCOL

			#if !defined(ENABLE_XPROG_PROTOCOL)
				#define ENABLE_XPROG_PROTOCOL
			#endif
		#endif

	/* Defines: */
		#define XMEGA_CRC_LENGTH_BYTES               3

		#define XMEGA_NVM_REG_ADDR0                  0x00
		#define XMEGA_NVM_REG_ADDR1                  0x01
		#define XMEGA_NVM_REG_ADDR2                  0x02
		#define XMEGA_NVM_REG_DAT0                   0x04
		#define XMEGA_NVM_REG_DAT1                   0x05
		#define XMEGA_NVM_REG_DAT2                   0x06
		#define XMEGA_NVM_REG_CMD                    0x0A
		#define XMEGA_NVM_REG_CTRLA                  0x0B
		#define XMEGA_NVM_REG_CTRLB                  0x0C
		#define XMEGA_NVM_REG_INTCTRL                0x0D
		#define XMEGA_NVM_REG_STATUS                 0x0F
		#define XMEGA_NVM_REG_LOCKBITS               0x10

		#define XMEGA_NVM_BIT_CTRLA_CMDEX            (1 << 0)

		#define XMEGA_NVM_CMD_NOOP                   0x00
		#define XMEGA_NVM_CMD_CHIPERASE              0x40
		#define XMEGA_NVM_CMD_READNVM                0x43
		#define XMEGA_NVM_CMD_LOADFLASHPAGEBUFF      0x23
		#define XMEGA_NVM_CMD_ERASEFLASHPAGEBUFF     0x26
		#define XMEGA_NVM_CMD_ERASEFLASHPAGE         0x2B
		#define XMEGA_NVM_CMD_WRITEFLASHPAGE         0x2E
		#define XMEGA_NVM_CMD_ERASEWRITEFLASH        0x2F
		#define XMEGA_NVM_CMD_FLASHCRC               0x78
		#define XMEGA_NVM_CMD_ERASEAPPSEC            0x20
		#define XMEGA_NVM_CMD_ERASEAPPSECPAGE        0x22
		#define XMEGA_NVM_CMD_WRITEAPPSECPAGE        0x24
		#define XMEGA_NVM_CMD_ERASEWRITEAPPSECPAGE   0x25
		#define XMEGA_NVM_CMD_APPCRC                 0x38
		#define XMEGA_NVM_CMD_ERASEBOOTSEC           0x68
		#define XMEGA_NVM_CMD_ERASEBOOTSECPAGE       0x2A
		#define XMEGA_NVM_CMD_WRITEBOOTSECPAGE       0x2C
		#define XMEGA_NVM_CMD_ERASEWRITEBOOTSECPAGE  0x2D
		#define XMEGA_NVM_CMD_BOOTCRC                0x39
		#define XMEGA_NVM_CMD_READUSERSIG            0x03
		#define XMEGA_NVM_CMD_ERASEUSERSIG           0x18
		#define XMEGA_NVM_CMD_WRITEUSERSIG           0x1A
		#define XMEGA_NVM_CMD_READCALIBRATION        0x02
		#define XMEGA_NVM_CMD_READFUSE               0x07
		#define XMEGA_NVM_CMD_WRITEFUSE              0x4C
		#define XMEGA_NVM_CMD_WRITELOCK              0x08
		#define XMEGA_NVM_CMD_LOADEEPROMPAGEBUFF     0x33
		#define XMEGA_NVM_CMD_ERASEEEPROMPAGEBUFF    0x36
		#define XMEGA_NVM_CMD_ERASEEEPROM            0x30
		#define XMEGA_NVM_CMD_ERASEEEPROMPAGE        0x32
		#define XMEGA_NVM_CMD_WRITEEEPROMPAGE        0x34
		#define XMEGA_NVM_CMD_ERASEWRITEEEPROMPAGE   0x35
		#define XMEGA_NVM_CMD_READEEPROM             0x06

	/* Function Prototypes: */
		bool XMEGANVM_WaitWhileNVMBusBusy(void);
		bool XMEGANVM_WaitWhileNVMControllerBusy(void);
		bool XMEGANVM_EnablePDI(void);
		void XMEGANVM_DisablePDI(void);
		bool XMEGANVM_GetMemoryCRC(const uint8_t CRCCommand,
			                       uint32_t* const CRCDest);
		bool XMEGANVM_ReadMemory(const uint32_t ReadAddress,
		                         uint8_t* ReadBuffer,
		                         uint16_t ReadSize);
		bool XMEGANVM_WriteByteMemory(const uint8_t WriteCommand,
		                              const uint32_t WriteAddress,
		                              const uint8_t Byte);
		bool XMEGANVM_WritePageMemory(const uint8_t WriteBuffCommand,
		                              const uint8_t EraseBuffCommand,
		                              const uint8_t WritePageCommand,
		                              const uint8_t PageMode,
		                              const uint32_t WriteAddress,
		                              const uint8_t* WriteBuffer,
		                              uint16_t WriteSize);
		bool XMEGANVM_EraseMemory(const uint8_t EraseCommand,
		                          const uint32_t Address);

		#if defined(INCLUDE_FROM_XMEGANVM_C)
			static void XMEGANVM_SendNVMRegAddress(const uint8_t Register);
			static void XMEGANVM_SendAddress(const uint32_t AbsoluteAddress);
		#endif

#endif
1'>611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
--- /dev/null
+++ b/include/linux/netfilter/nf_conntrack_rtsp.h
@@ -0,0 +1,63 @@
+/*
+ * RTSP extension for IP connection tracking.
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ * based on ip_conntrack_irc.h
+ *
+ *      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.
+ */
+#ifndef _IP_CONNTRACK_RTSP_H
+#define _IP_CONNTRACK_RTSP_H
+
+//#define IP_NF_RTSP_DEBUG 1
+#define IP_NF_RTSP_VERSION "0.6.21"
+
+#ifdef __KERNEL__
+/* port block types */
+typedef enum {
+    pb_single,  /* client_port=x */
+    pb_range,   /* client_port=x-y */
+    pb_discon   /* client_port=x/y (rtspbis) */
+} portblock_t;
+
+/* We record seq number and length of rtsp headers here, all in host order. */
+
+/*
+ * This structure is per expected connection.  It is a member of struct
+ * ip_conntrack_expect.  The TCP SEQ for the conntrack expect is stored
+ * there and we are expected to only store the length of the data which
+ * needs replaced.  If a packet contains multiple RTSP messages, we create
+ * one expected connection per message.
+ *
+ * We use these variables to mark the entire header block.  This may seem
+ * like overkill, but the nature of RTSP requires it.  A header may appear
+ * multiple times in a message.  We must treat two Transport headers the
+ * same as one Transport header with two entries.
+ */
+struct ip_ct_rtsp_expect
+{
+    u_int32_t   len;        /* length of header block */
+    portblock_t pbtype;     /* Type of port block that was requested */
+    u_int16_t   loport;     /* Port that was requested, low or first */
+    u_int16_t   hiport;     /* Port that was requested, high or second */
+#if 0
+    uint        method;     /* RTSP method */
+    uint        cseq;       /* CSeq from request */
+#endif
+};
+
+extern unsigned int (*nf_nat_rtsp_hook)(struct sk_buff *skb,
+				 enum ip_conntrack_info ctinfo,
+				 unsigned int matchoff, unsigned int matchlen,
+				 struct ip_ct_rtsp_expect *prtspexp,
+				 struct nf_conntrack_expect *exp);
+
+extern void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp);
+
+#define RTSP_PORT   554
+
+#endif /* __KERNEL__ */
+
+#endif /* _IP_CONNTRACK_RTSP_H */
--- /dev/null
+++ b/include/linux/netfilter_helpers.h
@@ -0,0 +1,133 @@
+/*
+ * Helpers for netfiler modules.  This file provides implementations for basic
+ * functions such as strncasecmp(), etc.
+ *
+ * gcc will warn for defined but unused functions, so we only include the
+ * functions requested.  The following macros are used:
+ *   NF_NEED_STRNCASECMP        nf_strncasecmp()
+ *   NF_NEED_STRTOU16           nf_strtou16()
+ *   NF_NEED_STRTOU32           nf_strtou32()
+ */
+#ifndef _NETFILTER_HELPERS_H
+#define _NETFILTER_HELPERS_H
+
+/* Only include these functions for kernel code. */
+#ifdef __KERNEL__
+
+#include <linux/ctype.h>
+#define iseol(c) ( (c) == '\r' || (c) == '\n' )
+
+/*
+ * The standard strncasecmp()
+ */
+#ifdef NF_NEED_STRNCASECMP
+static int
+nf_strncasecmp(const char* s1, const char* s2, u_int32_t len)
+{
+    if (s1 == NULL || s2 == NULL)
+    {
+        if (s1 == NULL && s2 == NULL)
+        {
+            return 0;
+        }
+        return (s1 == NULL) ? -1 : 1;
+    }
+    while (len > 0 && tolower(*s1) == tolower(*s2))
+    {
+        len--;
+        s1++;
+        s2++;
+    }
+    return ( (len == 0) ? 0 : (tolower(*s1) - tolower(*s2)) );
+}
+#endif /* NF_NEED_STRNCASECMP */
+
+/*
+ * Parse a string containing a 16-bit unsigned integer.
+ * Returns the number of chars used, or zero if no number is found.
+ */
+#ifdef NF_NEED_STRTOU16
+static int
+nf_strtou16(const char* pbuf, u_int16_t* pval)
+{
+    int n = 0;
+
+    *pval = 0;
+    while (isdigit(pbuf[n]))
+    {
+        *pval = (*pval * 10) + (pbuf[n] - '0');
+        n++;
+    }
+
+    return n;
+}
+#endif /* NF_NEED_STRTOU16 */
+
+/*
+ * Parse a string containing a 32-bit unsigned integer.
+ * Returns the number of chars used, or zero if no number is found.
+ */
+#ifdef NF_NEED_STRTOU32
+static int
+nf_strtou32(const char* pbuf, u_int32_t* pval)
+{
+    int n = 0;
+
+    *pval = 0;
+    while (pbuf[n] >= '0' && pbuf[n] <= '9')
+    {
+        *pval = (*pval * 10) + (pbuf[n] - '0');
+        n++;
+    }
+
+    return n;
+}
+#endif /* NF_NEED_STRTOU32 */
+
+/*
+ * Given a buffer and length, advance to the next line and mark the current
+ * line.
+ */
+#ifdef NF_NEED_NEXTLINE
+static int
+nf_nextline(char* p, uint len, uint* poff, uint* plineoff, uint* plinelen)
+{
+    uint    off = *poff;
+    uint    physlen = 0;
+
+    if (off >= len)
+    {
+        return 0;
+    }
+
+    while (p[off] != '\n')
+    {
+        if (len-off <= 1)
+        {
+            return 0;
+        }
+
+        physlen++;
+        off++;
+    }
+
+    /* if we saw a crlf, physlen needs adjusted */
+    if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r')
+    {
+        physlen--;
+    }
+
+    /* advance past the newline */
+    off++;
+
+    *plineoff = *poff;
+    *plinelen = physlen;
+    *poff = off;
+
+    return 1;
+}
+#endif /* NF_NEED_NEXTLINE */
+
+#endif /* __KERNEL__ */
+
+#endif /* _NETFILTER_HELPERS_H */
--- /dev/null
+++ b/include/linux/netfilter_mime.h
@@ -0,0 +1,89 @@
+/*
+ * MIME functions for netfilter modules.  This file provides implementations
+ * for basic MIME parsing.  MIME headers are used in many protocols, such as
+ * HTTP, RTSP, SIP, etc.
+ *
+ * gcc will warn for defined but unused functions, so we only include the
+ * functions requested.  The following macros are used:
+ *   NF_NEED_MIME_NEXTLINE      nf_mime_nextline()
+ */
+#ifndef _NETFILTER_MIME_H
+#define _NETFILTER_MIME_H
+
+/* Only include these functions for kernel code. */
+#ifdef __KERNEL__
+
+#include <linux/ctype.h>
+
+/*
+ * Given a buffer and length, advance to the next line and mark the current
+ * line.  If the current line is empty, *plinelen will be set to zero.  If
+ * not, it will be set to the actual line length (including CRLF).
+ *
+ * 'line' in this context means logical line (includes LWS continuations).
+ * Returns 1 on success, 0 on failure.
+ */
+#ifdef NF_NEED_MIME_NEXTLINE
+static int
+nf_mime_nextline(char* p, uint len, uint* poff, uint* plineoff, uint* plinelen)
+{
+    uint    off = *poff;
+    uint    physlen = 0;
+    int     is_first_line = 1;
+
+    if (off >= len)
+    {
+        return 0;
+    }
+
+    do
+    {
+        while (p[off] != '\n')
+        {
+            if (len-off <= 1)
+            {
+                return 0;
+            }
+
+            physlen++;
+            off++;
+        }
+
+        /* if we saw a crlf, physlen needs adjusted */
+        if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r')
+        {
+            physlen--;
+        }
+
+        /* advance past the newline */
+        off++;
+
+        /* check for an empty line */
+        if (physlen == 0)
+        {
+            break;
+        }
+
+        /* check for colon on the first physical line */
+        if (is_first_line)
+        {
+            is_first_line = 0;
+            if (memchr(p+(*poff), ':', physlen) == NULL)
+            {
+                return 0;
+            }
+        }
+    }
+    while (p[off] == ' ' || p[off] == '\t');
+
+    *plineoff = *poff;
+    *plinelen = (physlen == 0) ? 0 : (off - *poff);
+    *poff = off;
+
+    return 1;
+}
+#endif /* NF_NEED_MIME_NEXTLINE */
+
+#endif /* __KERNEL__ */
+
+#endif /* _NETFILTER_MIME_H */
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_NF_NAT_AMANDA) += nf_nat_am
 obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o
 obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o
 obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o
+obj-$(CONFIG_NF_NAT_RTSP) += nf_nat_rtsp.o
 obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o
 obj-$(CONFIG_NF_NAT_SIP) += nf_nat_sip.o
 obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -259,6 +259,16 @@ config NF_CONNTRACK_TFTP
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
+config NF_CONNTRACK_RTSP
+	tristate "RTSP protocol support"
+	depends on NF_CONNTRACK
+	help
+		Support the RTSP protocol.  This allows UDP transports to be setup
+		properly, including RTP and RDT.
+
+		If you want to compile it as a module, say 'M' here and read
+		Documentation/modules.txt.  If unsure, say 'Y'.
+
 config NF_CT_NETLINK
 	tristate 'Connection tracking netlink interface'
 	select NETFILTER_NETLINK
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_NF_CONNTRACK_PPTP) += nf_co
 obj-$(CONFIG_NF_CONNTRACK_SANE) += nf_conntrack_sane.o
 obj-$(CONFIG_NF_CONNTRACK_SIP) += nf_conntrack_sip.o
 obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o
+obj-$(CONFIG_NF_CONNTRACK_RTSP) += nf_conntrack_rtsp.o
 
 # transparent proxy support
 obj-$(CONFIG_NETFILTER_TPROXY) += nf_tproxy_core.o
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -257,6 +257,11 @@ config NF_NAT_IRC
 	depends on NF_CONNTRACK && NF_NAT
 	default NF_NAT && NF_CONNTRACK_IRC
 
+config NF_NAT_RTSP
+	tristate
+ 	depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT
+ 	default NF_NAT && NF_CONNTRACK_RTSP
+
 config NF_NAT_TFTP
 	tristate
 	depends on NF_CONNTRACK && NF_NAT
--- /dev/null
+++ b/net/netfilter/nf_conntrack_rtsp.c
@@ -0,0 +1,517 @@
+/*
+ * RTSP extension for IP connection tracking
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ * based on ip_conntrack_irc.c
+ *
+ *      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.
+ *
+ * Module load syntax:
+ *   insmod nf_conntrack_rtsp.o ports=port1,port2,...port<MAX_PORTS>
+ *                              max_outstanding=n setup_timeout=secs
+ *
+ * If no ports are specified, the default will be port 554.
+ *
+ * With max_outstanding you can define the maximum number of not yet
+ * answered SETUP requests per RTSP session (default 8).
+ * With setup_timeout you can specify how long the system waits for
+ * an expected data channel (default 300 seconds).
+ *
+ * 2005-02-13: Harald Welte <laforge at netfilter.org>
+ * 	- port to 2.6
+ * 	- update to recent post-2.6.11 api changes
+ * 2006-09-14: Steven Van Acker <deepstar at singularity.be>
+ *      - removed calls to NAT code from conntrack helper: NAT no longer needed to use rtsp-conntrack
+ * 2007-04-18: Michael Guntsche <mike at it-loops.com>
+ * 			- Port to new NF API
+ */
+
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <linux/inet.h>
+#include <net/tcp.h>
+
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+#include <linux/netfilter/nf_conntrack_rtsp.h>
+
+#define NF_NEED_STRNCASECMP
+#define NF_NEED_STRTOU16
+#define NF_NEED_STRTOU32
+#define NF_NEED_NEXTLINE
+#include <linux/netfilter_helpers.h>
+#define NF_NEED_MIME_NEXTLINE
+#include <linux/netfilter_mime.h>
+
+#include <linux/ctype.h>
+#define MAX_SIMUL_SETUP 8 /* XXX: use max_outstanding */
+#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args)
+#if 0
+#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args)
+#else
+#define DEBUGP(fmt, args...)
+#endif
+
+#define MAX_PORTS 8
+static int ports[MAX_PORTS];
+static int num_ports = 0;
+static int max_outstanding = 8;
+static unsigned int setup_timeout = 300;
+
+MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
+MODULE_DESCRIPTION("RTSP connection tracking module");
+MODULE_LICENSE("GPL");
+module_param_array(ports, int, &num_ports, 0400);
+MODULE_PARM_DESC(ports, "port numbers of RTSP servers");
+module_param(max_outstanding, int, 0400);
+MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP requests per RTSP session");
+module_param(setup_timeout, int, 0400);
+MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data channels");
+
+static char *rtsp_buffer;
+static DEFINE_SPINLOCK(rtsp_buffer_lock);
+
+unsigned int (*nf_nat_rtsp_hook)(struct sk_buff *skb,
+				 enum ip_conntrack_info ctinfo,
+				 unsigned int matchoff, unsigned int matchlen,struct ip_ct_rtsp_expect* prtspexp,
+				 struct nf_conntrack_expect *exp);
+void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp);
+
+EXPORT_SYMBOL_GPL(nf_nat_rtsp_hook);
+
+/*
+ * Max mappings we will allow for one RTSP connection (for RTP, the number
+ * of allocated ports is twice this value).  Note that SMIL burns a lot of
+ * ports so keep this reasonably high.  If this is too low, you will see a
+ * lot of "no free client map entries" messages.
+ */
+#define MAX_PORT_MAPS 16
+
+/*** default port list was here in the masq code: 554, 3030, 4040 ***/
+
+#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; }
+
+/*
+ * Parse an RTSP packet.
+ *
+ * Returns zero if parsing failed.
+ *
+ * Parameters:
+ *  IN      ptcp        tcp data pointer
+ *  IN      tcplen      tcp data len
+ *  IN/OUT  ptcpoff     points to current tcp offset
+ *  OUT     phdrsoff    set to offset of rtsp headers
+ *  OUT     phdrslen    set to length of rtsp headers
+ *  OUT     pcseqoff    set to offset of CSeq header
+ *  OUT     pcseqlen    set to length of CSeq header
+ */
+static int
+rtsp_parse_message(char* ptcp, uint tcplen, uint* ptcpoff,
+                   uint* phdrsoff, uint* phdrslen,
+                   uint* pcseqoff, uint* pcseqlen,
+                   uint* transoff, uint* translen)
+{
+	uint    entitylen = 0;
+	uint    lineoff;
+	uint    linelen;
+
+	if (!nf_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen))
+		return 0;
+
+	*phdrsoff = *ptcpoff;
+	while (nf_mime_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) {
+		if (linelen == 0) {
+			if (entitylen > 0)
+				*ptcpoff += min(entitylen, tcplen - *ptcpoff);
+			break;
+		}
+		if (lineoff+linelen > tcplen) {
+			INFOP("!! overrun !!\n");
+			break;
+		}
+
+		if (nf_strncasecmp(ptcp+lineoff, "CSeq:", 5) == 0) {
+			*pcseqoff = lineoff;
+			*pcseqlen = linelen;
+		}
+
+		if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) {
+			*transoff = lineoff;
+			*translen = linelen;
+		}
+
+		if (nf_strncasecmp(ptcp+lineoff, "Content-Length:", 15) == 0) {
+			uint off = lineoff+15;
+			SKIP_WSPACE(ptcp+lineoff, linelen, off);
+			nf_strtou32(ptcp+off, &entitylen);
+		}
+	}
+	*phdrslen = (*ptcpoff) - (*phdrsoff);
+
+	return 1;
+}
+
+/*
+ * Find lo/hi client ports (if any) in transport header
+ * In:
+ *   ptcp, tcplen = packet
+ *   tranoff, tranlen = buffer to search
+ *
+ * Out:
+ *   pport_lo, pport_hi = lo/hi ports (host endian)
+ *
+ * Returns nonzero if any client ports found
+ *
+ * Note: it is valid (and expected) for the client to request multiple
+ * transports, so we need to parse the entire line.
+ */
+static int
+rtsp_parse_transport(char* ptran, uint tranlen,
+                     struct ip_ct_rtsp_expect* prtspexp)
+{
+	int     rc = 0;
+	uint    off = 0;
+
+	if (tranlen < 10 || !iseol(ptran[tranlen-1]) ||
+	    nf_strncasecmp(ptran, "Transport:", 10) != 0) {
+		INFOP("sanity check failed\n");
+		return 0;
+	}
+
+	DEBUGP("tran='%.*s'\n", (int)tranlen, ptran);
+	off += 10;
+	SKIP_WSPACE(ptran, tranlen, off);
+
+	/* Transport: tran;field;field=val,tran;field;field=val,... */
+	while (off < tranlen) {
+		const char* pparamend;
+		uint        nextparamoff;
+
+		pparamend = memchr(ptran+off, ',', tranlen-off);
+		pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
+		nextparamoff = pparamend-ptran;
+
+		while (off < nextparamoff) {
+			const char* pfieldend;
+			uint        nextfieldoff;
+
+			pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+			nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
+
+			if (strncmp(ptran+off, "client_port=", 12) == 0) {
+				u_int16_t   port;
+				uint        numlen;
+
+				off += 12;
+				numlen = nf_strtou16(ptran+off, &port);
+				off += numlen;
+				if (prtspexp->loport != 0 && prtspexp->loport != port)
+					DEBUGP("multiple ports found, port %hu ignored\n", port);
+				else {
+					DEBUGP("lo port found : %hu\n", port);
+					prtspexp->loport = prtspexp->hiport = port;
+					if (ptran[off] == '-') {
+						off++;
+						numlen = nf_strtou16(ptran+off, &port);
+						off += numlen;
+						prtspexp->pbtype = pb_range;
+						prtspexp->hiport = port;
+
+						// If we have a range, assume rtp:
+						// loport must be even, hiport must be loport+1
+						if ((prtspexp->loport & 0x0001) != 0 ||
+						    prtspexp->hiport != prtspexp->loport+1) {
+							DEBUGP("incorrect range: %hu-%hu, correcting\n",
+							       prtspexp->loport, prtspexp->hiport);
+							prtspexp->loport &= 0xfffe;
+							prtspexp->hiport = prtspexp->loport+1;
+						}
+					} else if (ptran[off] == '/') {
+						off++;
+						numlen = nf_strtou16(ptran+off, &port);
+						off += numlen;
+						prtspexp->pbtype = pb_discon;
+						prtspexp->hiport = port;
+					}
+					rc = 1;
+				}
+			}
+
+			/*
+			 * Note we don't look for the destination parameter here.
+			 * If we are using NAT, the NAT module will handle it.  If not,
+			 * and the client is sending packets elsewhere, the expectation
+			 * will quietly time out.
+			 */
+
+			off = nextfieldoff;
+		}
+
+		off = nextparamoff;
+	}
+
+	return rc;
+}
+
+void expected(struct nf_conn *ct, struct nf_conntrack_expect *exp)
+{
+    if(nf_nat_rtsp_hook_expectfn) {
+        nf_nat_rtsp_hook_expectfn(ct,exp);
+    }
+}
+
+/*** conntrack functions ***/
+
+/* outbound packet: client->server */
+
+static inline int
+help_out(struct sk_buff *skb, unsigned char *rb_ptr, unsigned int datalen,
+                struct nf_conn *ct, enum ip_conntrack_info ctinfo)
+{
+	struct ip_ct_rtsp_expect expinfo;
+
+	int dir = CTINFO2DIR(ctinfo);   /* = IP_CT_DIR_ORIGINAL */
+	//struct  tcphdr* tcph = (void*)iph + iph->ihl * 4;
+	//uint    tcplen = pktlen - iph->ihl * 4;
+	char*   pdata = rb_ptr;
+	//uint    datalen = tcplen - tcph->doff * 4;
+	uint    dataoff = 0;
+	int ret = NF_ACCEPT;
+
+	struct nf_conntrack_expect *exp;
+
+	__be16 be_loport;
+
+	memset(&expinfo, 0, sizeof(expinfo));
+
+	while (dataoff < datalen) {
+		uint    cmdoff = dataoff;
+		uint    hdrsoff = 0;
+		uint    hdrslen = 0;
+		uint    cseqoff = 0;
+		uint    cseqlen = 0;
+		uint    transoff = 0;
+		uint    translen = 0;
+		uint    off;
+
+		if (!rtsp_parse_message(pdata, datalen, &dataoff,
+					&hdrsoff, &hdrslen,
+					&cseqoff, &cseqlen,
+					&transoff, &translen))
+			break;      /* not a valid message */
+
+		if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0)
+			continue;   /* not a SETUP message */
+		DEBUGP("found a setup message\n");
+
+		off = 0;
+		if(translen) {
+			rtsp_parse_transport(pdata+transoff, translen, &expinfo);
+		}
+
+		if (expinfo.loport == 0) {
+			DEBUGP("no udp transports found\n");
+			continue;   /* no udp transports found */
+		}
+
+		DEBUGP("udp transport found, ports=(%d,%hu,%hu)\n",
+		       (int)expinfo.pbtype, expinfo.loport, expinfo.hiport);
+
+		exp = nf_ct_expect_alloc(ct);
+		if (!exp) {
+			ret = NF_DROP;
+			goto out;
+		}
+
+		be_loport = htons(expinfo.loport);
+
+		nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT,
+			ct->tuplehash[!dir].tuple.src.l3num,
+			&ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3,
+			IPPROTO_UDP, NULL, &be_loport);
+
+		exp->master = ct;
+
+		exp->expectfn = expected;
+		exp->flags = 0;
+
+		if (expinfo.pbtype == pb_range) {
+			DEBUGP("Changing expectation mask to handle multiple ports\n");
+			exp->mask.src.u.udp.port  = 0xfffe;
+		}
+
+		DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n",
+		       NIPQUAD(exp->tuple.src.u3.ip),
+		       ntohs(exp->tuple.src.u.udp.port),
+		       NIPQUAD(exp->tuple.dst.u3.ip),
+		       ntohs(exp->tuple.dst.u.udp.port));
+
+		if (nf_nat_rtsp_hook)
+			/* pass the request off to the nat helper */
+			ret = nf_nat_rtsp_hook(skb, ctinfo, hdrsoff, hdrslen, &expinfo, exp);
+		else if (nf_ct_expect_related(exp) != 0) {
+			INFOP("nf_ct_expect_related failed\n");
+			ret  = NF_DROP;
+		}
+		nf_ct_expect_put(exp);
+		goto out;
+	}
+out:
+
+	return ret;
+}
+
+
+static inline int
+help_in(struct sk_buff *skb, size_t pktlen,
+ struct nf_conn* ct, enum ip_conntrack_info ctinfo)
+{
+ return NF_ACCEPT;
+}
+
+static int help(struct sk_buff *skb, unsigned int protoff,
+		struct nf_conn *ct, enum ip_conntrack_info ctinfo)
+{
+	struct tcphdr _tcph, *th;
+	unsigned int dataoff, datalen;
+	char *rb_ptr;
+	int ret = NF_DROP;
+
+	/* Until there's been traffic both ways, don't look in packets. */
+	if (ctinfo != IP_CT_ESTABLISHED &&
+	    ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
+		DEBUGP("conntrackinfo = %u\n", ctinfo);
+		return NF_ACCEPT;
+	}
+
+	/* Not whole TCP header? */
+	th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
+
+	if (!th)
+		return NF_ACCEPT;
+
+	/* No data ? */
+	dataoff = protoff + th->doff*4;
+	datalen = skb->len - dataoff;
+	if (dataoff >= skb->len)
+		return NF_ACCEPT;
+
+	spin_lock_bh(&rtsp_buffer_lock);
+	rb_ptr = skb_header_pointer(skb, dataoff,
+				    skb->len - dataoff, rtsp_buffer);
+	BUG_ON(rb_ptr == NULL);
+
+#if 0
+	/* Checksum invalid?  Ignore. */
+	/* FIXME: Source route IP option packets --RR */
+	if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
+			 csum_partial((char*)tcph, tcplen, 0)))
+	{
+		DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
+		       tcph, tcplen, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+		return NF_ACCEPT;
+	}
+#endif
+
+	switch (CTINFO2DIR(ctinfo)) {
+	case IP_CT_DIR_ORIGINAL:
+		ret = help_out(skb, rb_ptr, datalen, ct, ctinfo);
+		break;
+	case IP_CT_DIR_REPLY:
+		DEBUGP("IP_CT_DIR_REPLY\n");
+		/* inbound packet: server->client */
+		ret = NF_ACCEPT;
+		break;
+	}
+
+	spin_unlock_bh(&rtsp_buffer_lock);
+
+	return ret;
+}
+
+static struct nf_conntrack_helper rtsp_helpers[MAX_PORTS];
+static char rtsp_names[MAX_PORTS][10];
+static struct nf_conntrack_expect_policy rtsp_expect_policy;
+
+/* This function is intentionally _NOT_ defined as __exit */
+static void
+fini(void)
+{
+	int i;
+	for (i = 0; i < num_ports; i++) {
+		DEBUGP("unregistering port %d\n", ports[i]);
+		nf_conntrack_helper_unregister(&rtsp_helpers[i]);
+	}
+	kfree(rtsp_buffer);
+}
+
+static int __init
+init(void)
+{
+	int i, ret;
+	struct nf_conntrack_helper *hlpr;
+	char *tmpname;
+
+	printk("nf_conntrack_rtsp v" IP_NF_RTSP_VERSION " loading\n");
+
+	if (max_outstanding < 1) {
+		printk("nf_conntrack_rtsp: max_outstanding must be a positive integer\n");
+		return -EBUSY;
+	}
+	if (setup_timeout < 0) {
+		printk("nf_conntrack_rtsp: setup_timeout must be a positive integer\n");
+		return -EBUSY;
+	}
+
+	rtsp_expect_policy.max_expected = max_outstanding;
+	rtsp_expect_policy.timeout = setup_timeout;
+
+	rtsp_buffer = kmalloc(65536, GFP_KERNEL);
+	if (!rtsp_buffer)
+		return -ENOMEM;
+
+	/* If no port given, default to standard rtsp port */
+	if (ports[0] == 0) {
+		ports[0] = RTSP_PORT;
+	}
+
+	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
+		hlpr = &rtsp_helpers[i];
+		memset(hlpr, 0, sizeof(struct nf_conntrack_helper));
+		hlpr->tuple.src.u.tcp.port = htons(ports[i]);
+		hlpr->tuple.dst.protonum = IPPROTO_TCP;
+		hlpr->expect_policy = &rtsp_expect_policy;
+		hlpr->me = THIS_MODULE;
+		hlpr->help = help;
+
+		tmpname = &rtsp_names[i][0];
+		if (ports[i] == RTSP_PORT) {
+			sprintf(tmpname, "rtsp");
+		} else {
+			sprintf(tmpname, "rtsp-%d", i);
+		}
+		hlpr->name = tmpname;
+
+		DEBUGP("port #%d: %d\n", i, ports[i]);
+
+		ret = nf_conntrack_helper_register(hlpr);
+
+		if (ret) {
+			printk("nf_conntrack_rtsp: ERROR registering port %d\n", ports[i]);
+			fini();
+			return -EBUSY;
+		}
+		num_ports++;
+	}
+	return 0;
+}
+
+module_init(init);
+module_exit(fini);
+
+EXPORT_SYMBOL(nf_nat_rtsp_hook_expectfn);
+
--- /dev/null
+++ b/net/ipv4/netfilter/nf_nat_rtsp.c
@@ -0,0 +1,496 @@
+/*
+ * RTSP extension for TCP NAT alteration
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ * based on ip_nat_irc.c
+ *
+ *      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.
+ *
+ * Module load syntax:
+ *      insmod nf_nat_rtsp.o ports=port1,port2,...port<MAX_PORTS>
+ *                           stunaddr=<address>
+ *                           destaction=[auto|strip|none]
+ *
+ * If no ports are specified, the default will be port 554 only.
+ *
+ * stunaddr specifies the address used to detect that a client is using STUN.
+ * If this address is seen in the destination parameter, it is assumed that
+ * the client has already punched a UDP hole in the firewall, so we don't
+ * mangle the client_port.  If none is specified, it is autodetected.  It
+ * only needs to be set if you have multiple levels of NAT.  It should be
+ * set to the external address that the STUN clients detect.  Note that in
+ * this case, it will not be possible for clients to use UDP with servers
+ * between the NATs.
+ *
+ * If no destaction is specified, auto is used.
+ *   destaction=auto:  strip destination parameter if it is not stunaddr.
+ *   destaction=strip: always strip destination parameter (not recommended).
+ *   destaction=none:  do not touch destination parameter (not recommended).
+ */
+
+#include <linux/module.h>
+#include <net/tcp.h>
+#include <net/netfilter/nf_nat_helper.h>
+#include <net/netfilter/nf_nat_rule.h>
+#include <linux/netfilter/nf_conntrack_rtsp.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+
+#include <linux/inet.h>
+#include <linux/ctype.h>
+#define NF_NEED_STRNCASECMP
+#define NF_NEED_STRTOU16
+#include <linux/netfilter_helpers.h>
+#define NF_NEED_MIME_NEXTLINE
+#include <linux/netfilter_mime.h>
+
+#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args)
+#if 0
+#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args)
+#else
+#define DEBUGP(fmt, args...)
+#endif
+
+#define MAX_PORTS       8
+#define DSTACT_AUTO     0
+#define DSTACT_STRIP    1
+#define DSTACT_NONE     2
+
+static char*    stunaddr = NULL;
+static char*    destaction = NULL;
+
+static u_int32_t extip = 0;
+static int       dstact = 0;
+
+MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
+MODULE_DESCRIPTION("RTSP network address translation module");
+MODULE_LICENSE("GPL");
+module_param(stunaddr, charp, 0644);
+MODULE_PARM_DESC(stunaddr, "Address for detecting STUN");
+module_param(destaction, charp, 0644);
+MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/strip/none)");
+
+#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; }
+
+/*** helper functions ***/
+
+static void
+get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint* ptcpdatalen)
+{
+    struct iphdr*   iph  = ip_hdr(skb);
+    struct tcphdr*  tcph = (void *)iph + ip_hdrlen(skb);
+
+    *pptcpdata = (char*)tcph +  tcph->doff*4;
+    *ptcpdatalen = ((char*)skb_transport_header(skb) + skb->len) - *pptcpdata;
+}
+
+/*** nat functions ***/
+
+/*
+ * Mangle the "Transport:" header:
+ *   - Replace all occurences of "client_port=<spec>"
+ *   - Handle destination parameter
+ *
+ * In:
+ *   ct, ctinfo = conntrack context
+ *   skb        = packet
+ *   tranoff    = Transport header offset from TCP data
+ *   tranlen    = Transport header length (incl. CRLF)
+ *   rport_lo   = replacement low  port (host endian)
+ *   rport_hi   = replacement high port (host endian)
+ *
+ * Returns packet size difference.
+ *
+ * Assumes that a complete transport header is present, ending with CR or LF
+ */
+static int
+rtsp_mangle_tran(enum ip_conntrack_info ctinfo,
+                 struct nf_conntrack_expect* exp,
+								 struct ip_ct_rtsp_expect* prtspexp,
+                 struct sk_buff* skb, uint tranoff, uint tranlen)
+{
+    char*       ptcp;
+    uint        tcplen;
+    char*       ptran;
+    char        rbuf1[16];      /* Replacement buffer (one port) */
+    uint        rbuf1len;       /* Replacement len (one port) */
+    char        rbufa[16];      /* Replacement buffer (all ports) */
+    uint        rbufalen;       /* Replacement len (all ports) */
+    u_int32_t   newip;
+    u_int16_t   loport, hiport;
+    uint        off = 0;
+    uint        diff;           /* Number of bytes we removed */
+
+    struct nf_conn *ct = exp->master;
+    struct nf_conntrack_tuple *t;
+
+    char    szextaddr[15+1];
+    uint    extaddrlen;
+    int     is_stun;
+
+    get_skb_tcpdata(skb, &ptcp, &tcplen);
+    ptran = ptcp+tranoff;
+
+    if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen ||
+        tranlen < 10 || !iseol(ptran[tranlen-1]) ||
+        nf_strncasecmp(ptran, "Transport:", 10) != 0)
+    {
+        INFOP("sanity check failed\n");
+        return 0;
+    }
+    off += 10;
+    SKIP_WSPACE(ptcp+tranoff, tranlen, off);
+
+    newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip;
+    t = &exp->tuple;
+    t->dst.u3.ip = newip;
+
+    extaddrlen = extip ? sprintf(szextaddr, "%u.%u.%u.%u", &extip)
+                       : sprintf(szextaddr, "%u.%u.%u.%u", &newip);
+    DEBUGP("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto"));
+
+    rbuf1len = rbufalen = 0;
+    switch (prtspexp->pbtype)
+    {
+    case pb_single:
+        for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */
+        {
+            t->dst.u.udp.port = htons(loport);
+            if (nf_ct_expect_related(exp) == 0)
+            {
+                DEBUGP("using port %hu\n", loport);
+                break;
+            }
+        }
+        if (loport != 0)
+        {
+            rbuf1len = sprintf(rbuf1, "%hu", loport);
+            rbufalen = sprintf(rbufa, "%hu", loport);
+        }
+        break;
+    case pb_range:
+        for (loport = prtspexp->loport; loport != 0; loport += 2) /* XXX: improper wrap? */
+        {
+            t->dst.u.udp.port = htons(loport);
+            if (nf_ct_expect_related(exp) == 0)
+            {
+                hiport = loport + ~exp->mask.src.u.udp.port;
+                DEBUGP("using ports %hu-%hu\n", loport, hiport);
+                break;
+            }
+        }
+        if (loport != 0)
+        {
+            rbuf1len = sprintf(rbuf1, "%hu", loport);
+            rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1);
+        }
+        break;
+    case pb_discon:
+        for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */
+        {
+            t->dst.u.udp.port = htons(loport);
+            if (nf_ct_expect_related(exp) == 0)
+            {
+                DEBUGP("using port %hu (1 of 2)\n", loport);
+                break;
+            }
+        }
+        for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */
+        {
+            t->dst.u.udp.port = htons(hiport);
+            if (nf_ct_expect_related(exp) == 0)
+            {
+                DEBUGP("using port %hu (2 of 2)\n", hiport);
+                break;
+            }
+        }
+        if (loport != 0 && hiport != 0)
+        {
+            rbuf1len = sprintf(rbuf1, "%hu", loport);
+            if (hiport == loport+1)
+            {
+                rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport);
+            }
+            else
+            {
+                rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport);
+            }
+        }
+        break;
+    }
+
+    if (rbuf1len == 0)
+    {
+        return 0;   /* cannot get replacement port(s) */
+    }
+
+    /* Transport: tran;field;field=val,tran;field;field=val,... */
+    while (off < tranlen)
+    {
+        uint        saveoff;
+        const char* pparamend;
+        uint        nextparamoff;
+
+        pparamend = memchr(ptran+off, ',', tranlen-off);
+        pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
+        nextparamoff = pparamend-ptcp;
+
+        /*
+         * We pass over each param twice.  On the first pass, we look for a
+         * destination= field.  It is handled by the security policy.  If it
+         * is present, allowed, and equal to our external address, we assume
+         * that STUN is being used and we leave the client_port= field alone.
+         */
+        is_stun = 0;
+        saveoff = off;
+        while (off < nextparamoff)
+        {
+            const char* pfieldend;
+            uint        nextfieldoff;
+
+            pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+            nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
+
+            if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0)
+            {
+                if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0)
+                {
+                    is_stun = 1;
+                }
+                if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun))
+                {
+                    diff = nextfieldoff-off;
+                    if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
+                                                         off, diff, NULL, 0))
+                    {
+                        /* mangle failed, all we can do is bail */
+			nf_ct_unexpect_related(exp);
+                        return 0;
+                    }
+                    get_skb_tcpdata(skb, &ptcp, &tcplen);
+                    ptran = ptcp+tranoff;
+                    tranlen -= diff;
+                    nextparamoff -= diff;
+                    nextfieldoff -= diff;
+                }
+            }
+
+            off = nextfieldoff;
+        }
+        if (is_stun)
+        {
+            continue;
+        }
+        off = saveoff;
+        while (off < nextparamoff)
+        {
+            const char* pfieldend;
+            uint        nextfieldoff;
+
+            pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+            nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
+
+            if (strncmp(ptran+off, "client_port=", 12) == 0)
+            {
+                u_int16_t   port;
+                uint        numlen;
+                uint        origoff;
+                uint        origlen;
+                char*       rbuf    = rbuf1;
+                uint        rbuflen = rbuf1len;
+
+                off += 12;
+                origoff = (ptran-ptcp)+off;
+                origlen = 0;
+                numlen = nf_strtou16(ptran+off, &port);
+                off += numlen;
+                origlen += numlen;
+                if (port != prtspexp->loport)
+                {
+                    DEBUGP("multiple ports found, port %hu ignored\n", port);
+                }
+                else
+                {
+                    if (ptran[off] == '-' || ptran[off] == '/')
+                    {
+                        off++;
+                        origlen++;
+                        numlen = nf_strtou16(ptran+off, &port);
+                        off += numlen;
+                        origlen += numlen;
+                        rbuf = rbufa;
+                        rbuflen = rbufalen;
+                    }
+
+                    /*
+                     * note we cannot just memcpy() if the sizes are the same.
+                     * the mangle function does skb resizing, checks for a
+                     * cloned skb, and updates the checksums.
+                     *
+                     * parameter 4 below is offset from start of tcp data.
+                     */
+                    diff = origlen-rbuflen;
+                    if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
+                                              origoff, origlen, rbuf, rbuflen))
+                    {
+                        /* mangle failed, all we can do is bail */
+			nf_ct_unexpect_related(exp);
+                        return 0;
+                    }
+                    get_skb_tcpdata(skb, &ptcp, &tcplen);
+                    ptran = ptcp+tranoff;
+                    tranlen -= diff;
+                    nextparamoff -= diff;
+                    nextfieldoff -= diff;
+                }
+            }
+
+            off = nextfieldoff;
+        }
+
+        off = nextparamoff;
+    }
+
+    return 1;
+}
+
+static uint
+help_out(struct sk_buff *skb, enum ip_conntrack_info ctinfo,
+	 unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp,
+	 struct nf_conntrack_expect* exp)
+{
+    char*   ptcp;
+    uint    tcplen;
+    uint    hdrsoff;
+    uint    hdrslen;
+    uint    lineoff;
+    uint    linelen;
+    uint    off;
+
+    //struct iphdr* iph = (struct iphdr*)skb->nh.iph;
+    //struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4);
+
+    get_skb_tcpdata(skb, &ptcp, &tcplen);
+    hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq);
+    hdrslen = matchlen;
+    off = hdrsoff;
+    DEBUGP("NAT rtsp help_out\n");
+
+    while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen))
+    {
+        if (linelen == 0)
+        {
+            break;
+        }
+        if (off > hdrsoff+hdrslen)
+        {
+            INFOP("!! overrun !!");
+            break;
+        }
+        DEBUGP("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
+
+        if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0)
+        {
+            uint oldtcplen = tcplen;
+	    DEBUGP("hdr: Transport\n");
+            if (!rtsp_mangle_tran(ctinfo, exp, prtspexp, skb, lineoff, linelen))
+            {
+		DEBUGP("hdr: Transport mangle failed");
+                break;
+            }
+            get_skb_tcpdata(skb, &ptcp, &tcplen);
+            hdrslen -= (oldtcplen-tcplen);
+            off -= (oldtcplen-tcplen);
+            lineoff -= (oldtcplen-tcplen);
+            linelen -= (oldtcplen-tcplen);
+            DEBUGP("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
+        }
+    }
+
+    return NF_ACCEPT;
+}
+
+static unsigned int
+help(struct sk_buff *skb, enum ip_conntrack_info ctinfo,
+     unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp,
+     struct nf_conntrack_expect* exp)
+{
+    int dir = CTINFO2DIR(ctinfo);
+    int rc = NF_ACCEPT;
+
+    switch (dir)
+    {
+    case IP_CT_DIR_ORIGINAL:
+        rc = help_out(skb, ctinfo, matchoff, matchlen, prtspexp, exp);
+        break;
+    case IP_CT_DIR_REPLY:
+	DEBUGP("unmangle ! %u\n", ctinfo);
+    	/* XXX: unmangle */
+	rc = NF_ACCEPT;
+        break;
+    }
+    //UNLOCK_BH(&ip_rtsp_lock);
+
+    return rc;
+}
+
+static void expected(struct nf_conn* ct, struct nf_conntrack_expect *exp)
+{
+    struct nf_nat_multi_range_compat mr;
+    u_int32_t newdstip, newsrcip, newip;
+
+    struct nf_conn *master = ct->master;
+
+    newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
+    newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
+    //FIXME (how to port that ?)
+    //code from 2.4 : newip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) ? newsrcip : newdstip;
+    newip = newdstip;
+
+    DEBUGP("newsrcip=%u.%u.%u.%u, newdstip=%u.%u.%u.%u, newip=%u.%u.%u.%u\n",
+           &newsrcip, &newdstip, &newip);
+
+    mr.rangesize = 1;
+    // We don't want to manip the per-protocol, just the IPs.
+    mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
+    mr.range[0].min_ip = mr.range[0].max_ip = newip;
+
+    nf_nat_setup_info(ct, &mr.range[0], IP_NAT_MANIP_DST);
+}
+
+
+static void __exit fini(void)
+{
+	nf_nat_rtsp_hook = NULL;
+        nf_nat_rtsp_hook_expectfn = NULL;
+	synchronize_net();
+}
+
+static int __init init(void)
+{
+	printk("nf_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n");
+
+	BUG_ON(nf_nat_rtsp_hook);
+	nf_nat_rtsp_hook = help;
+        nf_nat_rtsp_hook_expectfn = &expected;
+
+	if (stunaddr != NULL)
+		extip = in_aton(stunaddr);
+
+	if (destaction != NULL) {
+	        if (strcmp(destaction, "auto") == 0)
+			dstact = DSTACT_AUTO;
+
+		if (strcmp(destaction, "strip") == 0)
+			dstact = DSTACT_STRIP;
+
+		if (strcmp(destaction, "none") == 0)
+			dstact = DSTACT_NONE;
+	}
+
+	return 0;
+}
+
+module_init(init);
+module_exit(fini);