Please find below a patch to modify some existing and add some new convenience functions to the newnat core conntrack and nat functions. In summary: - make ip_nat_resize_packet more generic (TCP and UDP) - optimize away the memmove in ip_nat_mangle_tcp_packet when the replacement string is the same size as the match string - add ip_nat_mangle_udp_packet The latter change/function is an implementation of ip_nat_mangle_udp_packet which takes an array of modifications to be made to a packet. A similar TCP version could/should be made. These changes are a prerequisite for the patch I will be submitting subsequently which is a NAT/conntrack helper pair for the Amanda backup protocol. I will attempt to do that one patch-o-matic compliant. If there is anything unacceptable in either patch submissions, I will be most willing to make corrections. Thanx, b. diff -uNr linux-2.4.18-6mdk-pom-clean/include/linux/netfilter_ipv4/ip_nat_helper.h linux-2.4.18-6mdkuml-48um-pom/include/linux/netfilter_ipv4/ip_nat_helper.h --- linux-2.4.18-6mdk-pom-clean/include/linux/netfilter_ipv4/ip_nat_helper.h 2002-08-01 14:21:44.000000000 -0400 +++ linux-2.4.18-6mdkuml-48um-pom/include/linux/netfilter_ipv4/ip_nat_helper.h 2002-08-15 12:47:38.000000000 -0400 @@ -39,6 +39,13 @@ struct ip_nat_info *info); }; +struct ip_nat_mangle_rep { + unsigned int match_offset; + unsigned int match_len; + char *rep_buffer; + unsigned int rep_len; +}; + extern struct list_head helpers; extern int ip_nat_helper_register(struct ip_nat_helper *me); @@ -50,6 +57,13 @@ unsigned int match_len, char *rep_buffer, unsigned int rep_len); +extern 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); extern int ip_nat_seq_adjust(struct sk_buff *skb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo); diff -uNr linux-2.4.18-6mdk-pom-clean/net/ipv4/netfilter/ip_nat_helper.c linux-2.4.18-6mdkuml-48um-pom/net/ipv4/netfilter/ip_nat_helper.c --- linux-2.4.18-6mdk-pom-clean/net/ipv4/netfilter/ip_nat_helper.c 2002-07-25 08:56:27.000000000 -0400 +++ linux-2.4.18-6mdkuml-48um-pom/net/ipv4/netfilter/ip_nat_helper.c 2002-08-16 03:11:33.000000000 -0400 @@ -8,6 +8,9 @@ * - add support for SACK adjustment * 14 Mar 2002 Harald Welte : * - merge SACK support into newnat API + * 16 Aug 2002 Brian J. Murrell : + * - make ip_nat_resize_packet more generic (TCP and UDP) + * - add ip_nat_mangle_udp_packet */ #include #include @@ -22,6 +26,7 @@ #include #include #include +#include #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 +56,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 +83,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 +177,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 +211,109 @@ 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 checksum information */ + (*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 diff -uNr linux-2.4.18-6mdk-pom-clean/net/ipv4/netfilter/ip_nat_standalone.c linux-2.4.18-6mdkuml-48um-pom/net/ipv4/netfilter/ip_nat_standalone.c --- linux-2.4.18-6mdk-pom-clean/net/ipv4/netfilter/ip_nat_standalone.c 2002-08-16 03:56:03.000000000 -0400 +++ linux-2.4.18-6mdkuml-48um-pom/net/ipv4/netfilter/ip_nat_standalone.c 2002-08-16 03:55:29.000000000 -0400 @@ -358,5 +358,6 @@ EXPORT_SYMBOL(ip_nat_helper_unregister); EXPORT_SYMBOL(ip_nat_cheat_check); EXPORT_SYMBOL(ip_nat_mangle_tcp_packet); +EXPORT_SYMBOL(ip_nat_mangle_udp_packet); EXPORT_SYMBOL(ip_nat_used_tuple); MODULE_LICENSE("GPL"); -- Brian J. Murrell