Re: Problem with short udp packets

Stephan von Krawczynski <[email protected]> Sun, 4 Jan 2004 12:51:48 +0100
Newsgroups gmane.comp.encryption.cipe
Organization ith Kommunikationstechnik GmbH
Message-ID <[email protected]>
On Sat, 03 Jan 2004 21:57:07 +0100
Olaf Titz <[email protected]> wrote:

> > kernel: UDP: short packet: a.b.c.d:40025 1528/1512 to e.f.g.h:40025
> >
> > on the e.f.g.h side. The interesting part about it is that all this udp
> > packets contain an ID-field of 0, whereas the normal data packets are
> > counted up. After these appear the throughput seems to drop dramatically.
> > Can this be a problem in key exchange?
> 
> This looks like these are in fact the KX packets. Apparently the KX
> packets, which are sent out via the regular UDP sendmsg() mechanism,
> are treated differently wrt. IP ID generation than data packets, where
> we fill all IP headers ourselves. Being able to tell KX packets apart
> this way is a security problem, this must be fixed.
> 
> Which kernel version? This stuff is highly kernel version dependent.
> 
> For the original problem, can you try if the problem is still present
> in the CVS version?
> 
> Olaf

Hi Olaf,

I found this one, and it is pretty - well - odd. It does only occur under
kernel 2.4 and the reason is this code in output.c :

        /*
         *      Push down and install the CIPE/UDP header.
         */
        iph->version    =       4;
        iph->ihl        =       sizeof(struct iphdr)>>2;
        iph->tos        =       tos;
        iph->tot_len    =       htons(skb->len);
        ip_ident(iph, &rt->u.dst);
        iph->frag_off   =       df;
        iph->ttl        =       ttl;
        iph->protocol   =       IPPROTO_UDP;
        iph->saddr      =       rt->rt_src;
        iph->daddr      =       rt->rt_dst;

        ip_send_check(iph);


You won't believe it, this code has a major bug. The correct version looks like
this:

        /*
         *      Push down and install the CIPE/UDP header.
         */
        iph->version    =       4;
        iph->ihl        =       sizeof(struct iphdr)>>2;
        iph->tos        =       tos;
        iph->tot_len    =       htons(skb->len);
        iph->frag_off   =       df;
        iph->ttl        =       ttl;
        iph->protocol   =       IPPROTO_UDP;
        iph->saddr      =       rt->rt_src;
        iph->daddr      =       rt->rt_dst;

        ip_ident(iph, &rt->u.dst);
        ip_send_check(iph);

And the reason is in the kernel tree under /include/net/ip.h:

static inline void ip_select_ident(struct iphdr *iph, struct dst_entry *dst,
struct sock *sk)
{
        if (iph->frag_off&__constant_htons(IP_DF)) {
                /* This is only to work around buggy Windows95/2000
                 * VJ compression implementations.  If the ID field
                 * does not change, they drop every other packet in
                 * a TCP stream using header compression.
                 */ 
                iph->id = ((sk && sk->daddr) ? htons(sk->protinfo.af_inet.id++)
: 0);
        } else
                __ip_select_ident(iph, dst);
}


As you can see the ident-code relies on setting the frag_off as well as
sk->daddr. If you do not do that, everything runs well under low load. But as
soon as you have high load (and lars' ignoredf/forcemtu patch) you notice my
described phenomenon, remember skbs are heavily reused mem and may contain just
about any data...
It took me several days to figure that one out.

Few lines below this problem in output.c there is yet another one. I wonder if
anybody ever noticed that the interface TX byte-counters under kernel 2.4 are
plain wrong, doubled to name it. Reason is this:

        tunnel->stat.tx_bytes += skb->len;
        tunnel->stat.tx_packets++;
        dprintk(DEB_OUT, (KERN_INFO
                          "%s: sending %d from %s:%d to %s:%d\n",
                          dev->name, skb->len,
                          cipe_ntoa(iph->saddr), ntohs(udph->source),
                          cipe_ntoa(iph->daddr), ntohs(udph->dest)));
        if (tunnel->sockshost)
            dprintk(DEB_OUT, (KERN_INFO "%s: via SOCKS to %s:%d\n", dev->name,
                              cipe_ntoa(tunnel->sockshost),
                              ntohs(tunnel->socksport)));  
#if 0
        dprintk(DEB_OUT, (KERN_INFO "dst: (%d,%d) %s %d %d\n",
                          skb->dst->refcnt, skb->dst->use,
                          skb->dst->dev->name, skb->dst->pmtu,
                          skb->dst->error));
#endif
#ifdef DEBUG
        if (cipe_debug&DEB_PKOU)
            cipe_dump_packet("sending", skb, 0);
#endif
        nf_conntrack_null(skb);
#ifdef LINUX_24
        {
            int err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,
                              rt->u.dst.dev, ip_send);
            if (err==NET_XMIT_SUCCESS || err==NET_XMIT_CN) {
                tunnel->stat.tx_bytes += skb->len;
                tunnel->stat.tx_packets++;
            } else {
                tunnel->stat.tx_errors++;
                tunnel->stat.tx_aborted_errors++;
            }
        }
#else
        ip_send(skb);
#endif


As you can see they are simply added _twice_. Obviously the first two lines
should move inside the #else branch (which is kernel 2.2).

I would suggest to make a 1.5.5 release containing Lars Fennebergs
ignoredf/forcemtu patch (works really well), as well as fixes for above stuff
(I don't attach a patch because they are so simple). This should be the first
release of cipe really working with 2.4 kernel and mtu 1500 ;-)

Regards,
Stephan









--
Message sent by the [email protected] mailing list.
Unsubscribe: mail [email protected], "unsubscribe cipe-l" in body
Other commands available with "help" in body to the same address.
CIPE info and list archive: <URL:http://sites.inka.de/~bigred/devel/cipe.html>