[Swan-dev] Why enable KLIPS to have NAT-T detection with NETKEY?

Oleg Rosowiecki orosowiecki at gmail.com
Mon Oct 17 16:45:23 UTC 2016


Libreswan code related to NAT-T support detection in the kernel looks very confusing to me.
Could someone explain why we need to compile in KLIPS in order for NAT-T to be detected with NETKEY?

NAT-T support gets detected in nat_traversal_espinudp_socket() as follows
(I'm omitting irrelevant code to reduce clutter):

int nat_traversal_espinudp_socket(int sk, const char *fam)
{
        int r;
        struct ifreq ifr;
        int *fdp = (int *) &ifr.ifr_data;
        const char *ifn;

        DBG(DBG_NATT, DBG_log("NAT-Traversal: Trying new style NAT-T"));
        zero(&ifr);
        switch (kern_interface) {
        /* ... OR: omitted for brevity */

        case USE_NETKEY:
                /* Let's hope we have at least one ethernet device */
                ifn = "eth0";
                break;

        /* ... OR: omitted for brevity */
        }
        strncpy(ifr.ifr_name, ifn, sizeof(ifr.ifr_name));

        fdp[0] = sk;
        fdp[1] = ESPINUDP_WITH_NON_ESP; /* no longer support non-ike or non-floating */
        r = ioctl(sk, IPSEC_UDP_ENCAP_CONVERT, &ifr);
        if (r == -1) {
                /* OR: signal failure to support new style NAT-T and errno */
        } else {
                /* OR: signal success */
                return r;
        }

        /* OR: If we failed to detect new style NAT-T, try old style NAT-T detection... */
}

As far as I can see, the above code is bound to fail with NETKEY. The ioctl()
system call on the socket is serviced in the kernel by sock_ioctl()
and then subsequently by dev_ifsioc() as follows.

static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
{
        int err;
        struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
        const struct net_device_ops *ops;

        if (!dev)
                return -ENODEV;

        ops = dev->netdev_ops;

        switch (cmd) {

        /* OR: omitted for brevity */

        default:
            if ((cmd >= SIOCDEVPRIVATE &&
                 cmd <= SIOCDEVPRIVATE + 15) /* omitted */
                        err = -EOPNOTSUPP;
                        if (ops->ndo_do_ioctl) {
                                if (netif_device_present(dev))
                                        err = ops->ndo_do_ioctl(dev, ifr, cmd);
                                else
                                        err = -ENODEV;
                        }
        }

}

First of all, we hard-code "eth0" in our struct ifreq that gets passed to the kernel.
On a machine with no "eth0", this will fail with ENODEV, as the above code illustrates.
On a machine that does have "eth0", this is most probably going to fail with EOPNOTSUPP,
since SIOCDEVPRIVATE + 3 means nothing to an average NIC driver (?).

The question is: is new NAT-T detection really supposed to work with NETKEY?

The test for the old style NAT-T is compiled conditionally:

#if defined(KLIPS)
        DBG(DBG_NATT, DBG_log("NAT-Traversal: Trying old style NAT-T"));
        const int type = ESPINUDP_WITH_NON_ESP;
        r = setsockopt(sk, SOL_UDP, UDP_ESPINUDP, &type, sizeof(type));
        if (r == -1) {
                /* OR: signal failure to support old style NAT-T */
        } else {
                /* OR: signal success */
                return r;
        }
# else
        DBG(DBG_NATT,
                DBG_log("NAT-Traversal: ESPINUDP() setup for old style NAT-T family not available - KLIPS support not compiled in"));
#endif

Setting the socket option works perfectly with NETKEY. At first sight I'd say
the KLIPS-related #if directives rather belong around the new style NAT-T detection portion.

Could anybody explain the reason of the conditional compilation for old-style NAT-T?
Many thanks,
Oleg Rosowiecki


More information about the Swan-dev mailing list