patch-2.4.21 linux-2.4.21/net/ipv4/netfilter/ip_nat_helper.c
Next file: linux-2.4.21/net/ipv4/netfilter/ip_nat_rule.c
Previous file: linux-2.4.21/net/ipv4/netfilter/ip_nat_core.c
Back to the patch index
Back to the overall index
- Lines: 305
- Date:
2003-06-13 07:51:39.000000000 -0700
- Orig file:
linux-2.4.20/net/ipv4/netfilter/ip_nat_helper.c
- Orig date:
2002-11-28 15:53:15.000000000 -0800
diff -urN linux-2.4.20/net/ipv4/netfilter/ip_nat_helper.c linux-2.4.21/net/ipv4/netfilter/ip_nat_helper.c
@@ -8,6 +8,9 @@
* - add support for SACK adjustment
* 14 Mar 2002 Harald Welte <laforge@gnumonks.org>:
* - merge SACK support into newnat API
+ * 16 Aug 2002 Brian J. Murrell <netfilter@interlinx.bc.ca>:
+ * - make ip_nat_resize_packet more generic (TCP and UDP)
+ * - add ip_nat_mangle_udp_packet
*/
#include <linux/version.h>
#include <linux/config.h>
@@ -22,6 +25,7 @@
#include <net/icmp.h>
#include <net/ip.h>
#include <net/tcp.h>
+#include <net/udp.h>
#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock)
#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock)
@@ -51,18 +55,12 @@
int new_size)
{
struct iphdr *iph;
- struct tcphdr *tcph;
- void *data;
int dir;
struct ip_nat_seq *this_way, *other_way;
DEBUGP("ip_nat_resize_packet: old_size = %u, new_size = %u\n",
(*skb)->len, new_size);
- iph = (*skb)->nh.iph;
- tcph = (void *)iph + iph->ihl*4;
- data = (void *)tcph + tcph->doff*4;
-
dir = CTINFO2DIR(ctinfo);
this_way = &ct->nat.info.seq[dir];
@@ -84,37 +82,41 @@
}
iph = (*skb)->nh.iph;
- tcph = (void *)iph + iph->ihl*4;
- data = (void *)tcph + tcph->doff*4;
-
- DEBUGP("ip_nat_resize_packet: Seq_offset before: ");
- DUMP_OFFSET(this_way);
+ if (iph->protocol == IPPROTO_TCP) {
+ struct tcphdr *tcph = (void *)iph + iph->ihl*4;
+ void *data = (void *)tcph + tcph->doff*4;
+
+ DEBUGP("ip_nat_resize_packet: Seq_offset before: ");
+ DUMP_OFFSET(this_way);
+
+ LOCK_BH(&ip_nat_seqofs_lock);
+
+ /* SYN adjust. If it's uninitialized, of this is after last
+ * correction, record it: we don't handle more than one
+ * adjustment in the window, but do deal with common case of a
+ * retransmit */
+ if (this_way->offset_before == this_way->offset_after
+ || before(this_way->correction_pos, ntohl(tcph->seq))) {
+ this_way->correction_pos = ntohl(tcph->seq);
+ this_way->offset_before = this_way->offset_after;
+ this_way->offset_after = (int32_t)
+ this_way->offset_before + new_size -
+ (*skb)->len;
+ }
- LOCK_BH(&ip_nat_seqofs_lock);
+ UNLOCK_BH(&ip_nat_seqofs_lock);
- /* SYN adjust. If it's uninitialized, of this is after last
- * correction, record it: we don't handle more than one
- * adjustment in the window, but do deal with common case of a
- * retransmit */
- if (this_way->offset_before == this_way->offset_after
- || before(this_way->correction_pos, ntohl(tcph->seq))) {
- this_way->correction_pos = ntohl(tcph->seq);
- this_way->offset_before = this_way->offset_after;
- this_way->offset_after = (int32_t)
- this_way->offset_before + new_size - (*skb)->len;
+ DEBUGP("ip_nat_resize_packet: Seq_offset after: ");
+ DUMP_OFFSET(this_way);
}
-
- UNLOCK_BH(&ip_nat_seqofs_lock);
-
- DEBUGP("ip_nat_resize_packet: Seq_offset after: ");
- DUMP_OFFSET(this_way);
return 1;
}
/* Generic function for mangling variable-length address changes inside
- * NATed connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX command in FTP).
+ * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX
+ * command in FTP).
*
* Takes care about all the nasty sequence number changes, checksumming,
* skb enlargement, ...
@@ -174,10 +176,11 @@
tcph = (void *)iph + iph->ihl*4;
data = (void *)tcph + tcph->doff*4;
- /* move post-replacement */
- memmove(data + match_offset + rep_len,
- data + match_offset + match_len,
- (*skb)->tail - (data + match_offset + match_len));
+ if (rep_len != match_len)
+ /* move post-replacement */
+ memmove(data + match_offset + rep_len,
+ data + match_offset + match_len,
+ (*skb)->tail - (data + match_offset + match_len));
/* insert data from buffer */
memcpy(data + match_offset, rep_buffer, rep_len);
@@ -207,6 +210,114 @@
return 1;
}
+
+/* Generic function for mangling variable-length address changes inside
+ * NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX
+ * command in the Amanda protocol)
+ *
+ * Takes care about all the nasty sequence number changes, checksumming,
+ * skb enlargement, ...
+ *
+ * XXX - This function could be merged with ip_nat_mangle_tcp_packet which
+ * should be fairly easy to do.
+ */
+int
+ip_nat_mangle_udp_packet(struct sk_buff **skb,
+ struct ip_conntrack *ct,
+ enum ip_conntrack_info ctinfo,
+ unsigned int match_offset,
+ unsigned int match_len,
+ char *rep_buffer,
+ unsigned int rep_len)
+{
+ struct iphdr *iph = (*skb)->nh.iph;
+ struct udphdr *udph = (void *)iph + iph->ihl * 4;
+ unsigned char *data;
+ u_int32_t udplen, newlen, newudplen;
+
+ udplen = (*skb)->len - iph->ihl*4;
+ newudplen = udplen - match_len + rep_len;
+ newlen = iph->ihl*4 + newudplen;
+
+ if (newlen > 65535) {
+ if (net_ratelimit())
+ printk("ip_nat_mangle_udp_packet: nat'ed packet "
+ "exceeds maximum packet size\n");
+ return 0;
+ }
+
+ if ((*skb)->len != newlen) {
+ if (!ip_nat_resize_packet(skb, ct, ctinfo, newlen)) {
+ printk("resize_packet failed!!\n");
+ return 0;
+ }
+ }
+
+ /* Alexey says: if a hook changes _data_ ... it can break
+ original packet sitting in tcp queue and this is fatal */
+ if (skb_cloned(*skb)) {
+ struct sk_buff *nskb = skb_copy(*skb, GFP_ATOMIC);
+ if (!nskb) {
+ if (net_ratelimit())
+ printk("Out of memory cloning TCP packet\n");
+ return 0;
+ }
+ /* Rest of kernel will get very unhappy if we pass it
+ a suddenly-orphaned skbuff */
+ if ((*skb)->sk)
+ skb_set_owner_w(nskb, (*skb)->sk);
+ kfree_skb(*skb);
+ *skb = nskb;
+ }
+
+ /* skb may be copied !! */
+ iph = (*skb)->nh.iph;
+ udph = (void *)iph + iph->ihl*4;
+ data = (void *)udph + sizeof(struct udphdr);
+
+ if (rep_len != match_len)
+ /* move post-replacement */
+ memmove(data + match_offset + rep_len,
+ data + match_offset + match_len,
+ (*skb)->tail - (data + match_offset + match_len));
+
+ /* insert data from buffer */
+ memcpy(data + match_offset, rep_buffer, rep_len);
+
+ /* update skb info */
+ if (newlen > (*skb)->len) {
+ DEBUGP("ip_nat_mangle_udp_packet: Extending packet by "
+ "%u to %u bytes\n", newlen - (*skb)->len, newlen);
+ skb_put(*skb, newlen - (*skb)->len);
+ } else {
+ DEBUGP("ip_nat_mangle_udp_packet: Shrinking packet from "
+ "%u to %u bytes\n", (*skb)->len, newlen);
+ skb_trim(*skb, newlen);
+ }
+
+ /* update the length of the UDP and IP packets to the new values*/
+ udph->len = htons((*skb)->len - iph->ihl*4);
+ iph->tot_len = htons(newlen);
+
+ /* fix udp checksum if udp checksum was previously calculated */
+ if ((*skb)->csum != 0) {
+ (*skb)->csum = csum_partial((char *)udph +
+ sizeof(struct udphdr),
+ newudplen - sizeof(struct udphdr),
+ 0);
+
+ udph->check = 0;
+ udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
+ newudplen, IPPROTO_UDP,
+ csum_partial((char *)udph,
+ sizeof(struct udphdr),
+ (*skb)->csum));
+ }
+
+ ip_send_check(iph);
+
+ return 1;
+}
/* Adjust one found SACK option including checksum correction */
static void
@@ -255,54 +366,49 @@
}
-/* TCP SACK sequence number adjustment, return 0 if sack found and adjusted */
-static inline int
+/* TCP SACK sequence number adjustment. */
+static inline void
ip_nat_sack_adjust(struct sk_buff *skb,
- struct ip_conntrack *ct,
- enum ip_conntrack_info ctinfo)
+ struct ip_conntrack *ct,
+ enum ip_conntrack_info ctinfo)
{
- struct iphdr *iph;
struct tcphdr *tcph;
- unsigned char *ptr;
- int length, dir, sack_adjusted = 0;
+ unsigned char *ptr, *optend;
+ unsigned int dir;
- iph = skb->nh.iph;
- tcph = (void *)iph + iph->ihl*4;
- length = (tcph->doff*4)-sizeof(struct tcphdr);
+ tcph = (void *)skb->nh.iph + skb->nh.iph->ihl*4;
+ optend = (unsigned char *)tcph + tcph->doff*4;
ptr = (unsigned char *)(tcph+1);
dir = CTINFO2DIR(ctinfo);
- while (length > 0) {
- int opcode = *ptr++;
+ while (ptr < optend) {
+ int opcode = ptr[0];
int opsize;
switch (opcode) {
case TCPOPT_EOL:
- return !sack_adjusted;
+ return;
case TCPOPT_NOP:
- length--;
+ ptr++;
continue;
default:
- opsize = *ptr++;
- if (opsize > length) /* no partial opts */
- return !sack_adjusted;
+ opsize = ptr[1];
+ /* no partial opts */
+ if (ptr + opsize > optend || opsize < 2)
+ return;
if (opcode == TCPOPT_SACK) {
/* found SACK */
if((opsize >= (TCPOLEN_SACK_BASE
+TCPOLEN_SACK_PERBLOCK)) &&
!((opsize - TCPOLEN_SACK_BASE)
% TCPOLEN_SACK_PERBLOCK))
- sack_adjust(tcph, ptr-2,
+ sack_adjust(tcph, ptr,
&ct->nat.info.seq[!dir]);
-
- sack_adjusted = 1;
}
- ptr += opsize-2;
- length -= opsize;
+ ptr += opsize;
}
}
- return !sack_adjusted;
}
/* TCP sequence number adjustment */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)