Linux how to smoothly validate NAT-BUGFIX

Source: Internet
Author: User
There are two problems with the code in "How to smoothly implement Nat in Linux. This is only what has been found, but there are still many, which is why I don't start to complicate the code.
1. A bug comes with an optimization:

Note the following code:

If (! Nf_nat_initialized (CT, maniptype) {// The Nat has not been set to conn ...} when else // Nat has been set to Conn, pf_debug ("already setup manip % s for CT % P \ n", maniptype = ip_nat_manip_src? "Src": "DST", CT );

When Nat has been set to Conn, only one line of log is printed, And the nf_nat_initialized implementation that determines whether Nat has been set to Conn is exactly two bits. Different hook points have different judgment bits, in my original implementation, the bit set by alloc_null_binding is cleared. If alloc_null_binding is used to set Nat to Conn, no Nat rule is matched, and the matching continues, this achieves the goal of "if the data stream is already confirm when no Nat is configured, it will take effect immediately after Nat is configured.
However, there is a problem, that is, when the original Nat rules change, the new Nat rules cannot be activated instantly. This problem is hard to solve. I put it aside first. Even if I don't consider this problem, the above Code also has a problem. Why clear the bit after alloc_null_binding? Simply put the logic of "continue searching in the location where the else prints the log, right? So I restored$ K/NET/IPv4/Netfilter/nf_nat_standalone.cFile modification, that is, the nf_nat_rule_find function does not need to be modified. Instead, you only need to modify nf_nat_fn and add a goto to the place where else prints the log (! Nf_nat_initialized (CT, maniptype) determines the next row, and the remaining modifications remain unchanged.
Now we can consider the problem that the new Nat rule of the conntrack in the established State takes effect in a timely manner. The reason for this is that it is difficult, this is because we need to let the nf_nat_fn function know when a new Nat rule will replace the old one. This behavior is nothing more than a series of search and comparison operations for computers, if this is done, it is better not to differentiate the States of new and established. simply execute nf_nat_rule_find for each packet, and the stateful semantics and efficiency of Linux Nat implementation will be completely invalidated. Therefore, this highly strategized configuration should be determined by the caller. Therefore, my Modification Scheme is to add a sysctl parameter. If the user administrator wants to change the NAT even if it takes effect, set this parameter to a non-zero value to change the Matching Behavior of the NAT rule:

Static unsigned intnf_nat_fn (unsigned int hooknum, struct sk_buff * SKB, const struct net_device * In, const struct net_device * Out, INT (* okfn) (struct sk_buff *)) {...... nat = nfct_nat (CT); If (! Nat) {/* Nat module was loaded late. * // the original implementation is: as long as the NAT module is loaded after the confirm, it will not work! If (/* set a switch to enable */0 & nf_ct_is_confirmed (CT) in smooth transition mode) return nf_accept; Nat = nf_ct_ext_add (CT, nf_ct_ext_nat, gfp_atomic ); if (NAT = NULL) {pr_debug ("failed to add Nat extension \ n"); Return nf_accept ;}} switch (ctinfo) {Case ip_ct_related: Case ip_ct_related + ip_ct_is_reply: if (ip_hdr (SKB)-> protocol = ipproto_icmp) {If (! Nf_nat_icmp_reply_translation (CT, ctinfo, hooknum, SKB) return nf_drop; else return nf_accept;}/* fall thru... (Only icmps can be ip_ct_is_reply) * // as long as no returned package arrives, it will always be new case ip_ct_new:/* seen it before? This can happen for loopback, retrans, or local packets... */If (! Nf_nat_initialized (CT, maniptype) {retry: renew: Unsigned int ret; If (hooknum = nf_inet_local_in)/* local_in hook doesn't have a chain! */Ret = alloc_null_binding (CT, hooknum); else ret = nf_nat_rule_find (SKB, hooknum, in, out, CT); If (Ret! = Nf_accept) return ret; // The following newly added key points: // if it is a packet that has completely passed through this box and has never been successfully Nat by iptables rules, // continue to match the iptables Nat rule, because a new // iptables rule may be added during packet retransmission. If (nf_ct_is_confirmed (CT) {struct net * Net = nf_ct_net (CT); // if a new rule is matched, the position of the tuple in the chian is updated. Hlist_nulls_del_rcu (& CT-> tuplehash [ip_ct_dir_original]. hnnode); hlist_nulls_del_rcu (& CT-> tuplehash [ip_ct_dir_reply]. hnnode); // If the Del and reinsert operations are not performed, the returned package cannot be converted into the original package! Nf_conntrack_hash_insert (CT) ;}// the above is not optimized !! The optimization point is: The tuple's chain location will be updated only when the non-alloc_null_binding call is successful //; otherwise, it will not be useless!} Else {pf_debug ("already setup manip % s for CT % P, but retry \ n", maniptype = ip_nat_manip_src? "Src": "DST", CT); goto retry;} break default: /* established */nf_ct_assert (ctinfo = ip_ct_established | ctinfo = (Bytes + ip_ct_is_reply); // for connections in the estableshed state, the sysctl parameter is 1, forcibly search for Nat rules! If (ctinfo = ip_ct_established & ctinfo! = (Response + ip_ct_is_reply) & nf_nat_slowpath) {pf_debug ("already setup manip % s for CT % P, but renew \ n", maniptype = ip_nat_manip_src? "Src": "DST", CT); // change the name! Goto renew ;}...}

If you want to change the NAT policy for a data stream that has already taken effect, set sysctl_nf_nat_slowpath to 1. After $ max_time, set it back to 0. Why? Because it cannot damage the original Linux Nat logic and affect the efficiency! So how to choose max_time? Of course, it is the elders of all conntrack time-out periods plus 10, because if there is no data packet for such a long time, conntrack time-out is released, and the next incoming package is new, if a packet exists during this period of time, the new NAT will take effect directly, and 5 to 10 seconds will be used as the clock correction for the user-State program. Therefore, there are not many max_time settings. There are still problems!
The default maximum time for conntrack is the TCP establish State, which lasts for 5 days, which is also too long. Therefore, the timeout time for all conntrack instances is shortened to 120 seconds, set the sysctl parameters nf_ct_tcp_be_liberal and nf_ct_tcp_loose to 1 to cancel the detailed semantics of TCP.
2. A bugfix

TheHlist_nulls_del_rcu, nf_conntrack_hash_insertWhen linked list operations involve addition and deletion, lock protection must be required, but I do not have it in my code. This is an obvious bug. Therefore, the protection of nf_conntrack_lock is required:

If (nf_ct_is_confirmed (CT) {struct net * Net = nf_ct_net (CT); spin_lock_bh (& nf_conntrack_lock); // if a new rule is matched, update the position of the tuple in the Chian. Hlist_nulls_del_rcu (& CT-> tuplehash [ip_ct_dir_original]. hnnode); hlist_nulls_del_rcu (& CT-> tuplehash [ip_ct_dir_reply]. hnnode); // If the Del and reinsert operations are not performed, the returned package cannot be converted into the original package! Nf_conntrack_hash_insert (CT); spin_unlock_bh (& nf_conntrack_lock );...

What I want to do more perfectly is to put all delete/insert operations into an RCU sequence, because I am afraid of gaps between Delete and insert, A find operation occurs when a data packet of the same stream enters the conntrack_in of the protocol stack. Therefore, tuple is not found, and a new conntrack is rebuilt. Therefore, I want to use the RCU lock for protection, make sure that the delete/insert operation is performed without any execution of the thread find hash. After all, the find operation is really protected by the RCU lock! However, I soon found myself worrying about this. If the above situation occurs, the newly created conntrack will not be successfully confirm when it leaves the protocol stack, because it will perform a find operation at the time of confirm, if it has been found, it will drop! What if the deleted node has not been inserted before "find" when entering confirm? If it was my original version, it would be finished. However, if I had just implemented the bugfix, isn't it protected by nf_conntrack_lock? So the above situation won't happen.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.