A Brief Overview of Nuclear NAT in FreeBSD

    There are quite a few common variants of NAT under FreeBSD. This is natd, ipnat, pfnat, ng_nat or as an option "buy ASA 5550 and do not stand out."


    Unfortunately, recently I have come across very few good and most importantly accessible articles about ipfw nat that appeared if memory serves me as early as 7.0.
    Then we will consider it under a magnifying glass today.

    True pf adherents are advised not to look under the cat, so as not to injure themselves with excessive knowledge.



    First, I will explain why ipfw nat
    Firstly, because I am a supporter of simple and obvious solutions, the support of which is possible practically at first glance without long meditations and reflections on the topic “how does it work? Oo. " Unfortunately, I can not attribute non-trivial hybrids of the form ipfw + pfnat to such.
    Secondly, there are already a lot of documentation on ng_nat and pfnat (nat on intif from somenet to any -> extif - s?).
    Thirdly, I use ipfw, and I love it - for its simplicity, visibility, predictability, dummynet and hybrids of the form ipfw + pf + altq ... in general, see “First.”

    Materiel.
    We will consider ipfw nat as an example of a spherical horse-shaped network in a vacuum depicted just above. Actually, according to the scheme, everything is pretty clear - we will make a reservation right away that we will network 172.16.0.0/21, em1 has an address, let's say 8.8.8.8 and internal network resources to which we do not need to translate addresses - such as, for example, corporate porn archives mail servers and other necessary things will live with addresses say 8.8.8.9, 8.8.8.10
    The ipfw nat functionality almost completely duplicates the libalias functionality, which natd, ng_nat also use.
    If the very goal of the NAT server is reduced to replacing src-ip in packets arriving at the internal interface (in our case em0) with the address of the outgoing interface (in our example em1) and entering data into its correspondence table for the inverse transformation upon arrival of the necessary packets in reply. Further, all packets properly packaged accordingly depart either by default or wherever you need (if, say, heaped PBR).

    In total, in the basic version, we need the following

    Enumerate the kernel with support for ipfw, ipfw nat, libalias and then to taste. Then we insert something similar to this: We assemble and install the new kernel as a precaution, backing up the old one. In / etc / rc.conf add

    # cd /sys/i386/conf/
    # cp GENERIC NAT
    # vim NAT




    ident NAT
    options IPFIREWALL
    options IPFIREWALL_DEFAULT_TO_ACCEPT
    options IPFIREWALL_FORWARD
    options IPFIREWALL_VERBOSE
    options IPFIREWALL_VERBOSE_LIMIT=50
    options IPFIREWALL_NAT
    options LIBALIAS
    options ROUTETABLES=2
    options DUMMYNET
    options HZ="1000"




    # cp -R /boot/kernel /boot/good
    # config NAT
    # cd ../compile/NAT
    # make depend
    # make
    # make install



    firewall_enable="YES"
    firewall_nat_enable="YES"
    dummynet_enable="YES"
    firewall_type="/etc/firewall"


    In order to hint that we will perform ipfw initialization using / etc / firewall which we would like to look in the simplest case, for example, like this: To calm your conscience, you can do something else in /etc/sysctl.conf After reboots everything should work. It’s possible to verify the operability of this all by a simple method. Here is such meager statistics compared to pfctl –sa, using the log parameter it collects ipfw nat. Briefly run through the options, the meager knowledge we can learn from man: if - the interface on which the address will be translated log - includes logging of "exhaustive" statistics on internal connections that we have already seen (ipfw nat1 show) reset

    # наша сеть которую нужно выпустить в интернет
    table 2 add 172.16.0.0/21

    # внутренние ресурсы которые нету смысла выпускать сквозь nat
    table 9 add 8.8.8.8
    table 9 add 8.8.8.9
    table 9 add 8.8.8.10

    #следующие три строчки собственно и есть nat =)
    nat 1 config log if em1 reset same_ports
    add 1200 nat 1 ip from table\(2\) to not table\(9\) via em1
    add 1300 nat 1 ip from any to 8.8.8.8 via em1




    net.inet.ip.fw.one_pass=1
    net.inet.ip.fastforwarding=1
    net.inet.tcp.maxtcptw=40960
    kern.ipc.somaxconn=4096
    kern.ipc.nmbclusters=65536
    net.inet.tcp.nolocaltimewait=1
    net.inet.ip.portrange.randomized=0




    ipfw nat 1 show
    nat 1: icmp=271, udp=45462, tcp=61534, sctp=0, pptp=0, proto=1, frag_id=2 frag_ptr=0 / tot=107270








    - hints that when changing the interface, the internal label
    deny_in should be cleared - prohibits passing packets from outside for which no matches were found in the internal label.
    same_ports - if possible, leave the original ports in outgoing packets (it is recommended to enable it)
    unreg_only - mask only packets coming from "unreal" networks .
    redirect_addr intip extip - instructs to wrap the traffic coming to extip to the machine with intip. The redirect_port and redirect_proto options work in a similar way, which are useful for “rendering” some machines with the necessary services for NAT.

    Actually, we can spawn nat instances as many and on any / ip interfaces (ipfw nat 2 config log if em2 reset same_ports, for example) and wrap traffic in them as we like.
    The memory that ipfw nat uses in its work is the memory available to the kernel and looks with the help of Twist it using the kernel option KVA_PAGES (maximum 512 which corresponds to 2GB for i386). Somehow, subjectively, with a good load on the channel (I get out at> 350-400Mbit / s), a periodic restart of the nat instance may be required in order to free up the memory that the plates eat. I think the dog rummaged in the slow mechanisms for shooting expired sessions. Restart can be done with an elementary script of the form:
    # sysctl -a | grep kmem
    vm.kmem_size_scale: 3
    vm.kmem_size_max: 335544320
    vm.kmem_size_min: 0
    vm.kmem_size: 335544320






    #!/bin/sh
    /sbin/ipfw nat 1 delete && /sbin/ipfw nat 1 config log if em1 reset same_ports


    As well as everyone walks on a rake with the fact that libalias itself is categorically not friendly with the hardware counters of checksums and also with tcp segmentation offload, then we recommend to configure the cards in a similar way from the beginning: That's all. Everything works transparently, does not initially have diseases with services like pptp, ftp (pfnat) and does not suffer from depressing slowness due to switching to user space (natd). The only thing that may really be missing is the normal way to nat from under the address pool. The only thing that comes to mind in this case is to spawn a lot of copies on aliases, but it looks like it's not as pretty as pfnat's say. Somehow messy happened but I hope clearly. The next article begs the question

    cat /etc/rc.conf | grep ifconfig
    ifconfig_em1="inet 8.8.8.8 netmask 255.255.255.0 -rxcsum -txcsum -tso"






    - and someone would be interested to see a step-by-step guide on setting up billing with traffic deletion on remote NAS and accounting on netflow? Well, actually, how are solutions scalable horizontally unlike everything that I described in previous articles? Or write about something less narrowly specific?

    PS They hinted to me here that I’ve already started using dns google in my examples under the guise of a “real” ip. Yes, I’m aware of RFC3330 where it says that “192.0.2.0/24 is assigned as“ TEST-NET “for use in documentation and example code”, but somehow I don’t know, it’s more obvious for myself :)

    Also popular now: