Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 28 Jun 2011 21:09:09 +0200 (CEST)
From:      sthaug@nethelp.no
To:        benoit.panizzon@imp.ch
Cc:        freebsd-net@freebsd.org
Subject:   Re: udp checksum implementation error in FreeBSD 7.2?
Message-ID:  <20110628.210909.74655931.sthaug@nethelp.no>
In-Reply-To: <1658896945.9968.1309271076776.JavaMail.root@erie.cs.uoguelph.ca>
References:  <201106281148.36754.benoit.panizzon@imp.ch> <1658896945.9968.1309271076776.JavaMail.root@erie.cs.uoguelph.ca>

next in thread | previous in thread | raw e-mail | index | archive | help
> > What we observe is:
> > 
> > DHCP Request with UDP checksum set => Packet reaches DHCP Daemon and
> > is being
> > answered.
> > DHCP Request with UDP checksum 0x0000 => ICMP Port Unreachable from
> > FreeBSD.
> > 
> > Can someone confirm this non RFC conform behaviour and knows how to
> > fix it?
> > 
> Well, I took a quick look at the sources (which are in sys/netinet/udp_usrreq.c
> in a function called udp_input() at about line#300) and it only does the
> checksum if it is non-zero. It looks like:
>    if (uh->uh_sum) {
>       ....do checksum 
> (If you don't have kernel sources handy, you can find them here:
>   http://svn.freebsd.org/viewvc/base/releng/7.2)

As another poster commented, ISC dhcpd by default uses BPF, *not* the
kernel UDP implementation - this is done to be able to handle broadcast
packets. However, the mystery doesn't end here - because the ISC dhcpd
implementation *also* only cares about UDP packets with a non-zero
checksum. The code is in common/packet.c, routine decode_udp_ip_header()
which is used by BPF and other link level access methods (e.g. DLPI on
Solaris). From dhcp-4.2.0-P2/common/packet.c:

----------------------------------------------------------------------
  /* Compute UDP checksums, including the ``pseudo-header'', the UDP
     header and the data.   If the UDP checksum field is zero, we're
     not supposed to do a checksum. */

  data = upp + sizeof(udp);
  len = ulen - sizeof(udp);

  usum = udp.uh_sum;
  udp.uh_sum = 0;

  /* XXX: We have to pass &udp, because we have to zero the checksum
   * field before calculating the sum...'upp' isn't zeroed.
   */
  sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
                         checksum(data, len,
                                  checksum((unsigned char *)&ip.ip_src,
                                           8, IPPROTO_UDP + ulen))));

  udp_packets_seen++;
  if (usum && usum != sum) {
          udp_packets_bad_checksum++;
          if (udp_packets_seen > 4 &&
              (udp_packets_seen / udp_packets_bad_checksum) < 2) {
                  log_info ("%d bad udp checksums in %d packets",
                            udp_packets_bad_checksum, udp_packets_seen);
                  udp_packets_seen = udp_packets_bad_checksum = 0;
          }
          return -1;
  }
----------------------------------------------------------------------

The way I read the code - the UDP checksum is computed in all cases,
but is only *compared* with the original checksum field of the packet
if this field is non-zero.

Returning to the original claim of

> DHCP Request with UDP checksum 0x0000 => ICMP Port Unreachable from
> FreeBSD.

I can see (e.g. using tcpdump) FreeBSD handle packets with a UDP checksum
field of 0 just fine - for instance on a busy name server. So I am quite
confident that your observed ICMP Port Unreachable is not generated by
the FreeBSD kernel, as long as you have the DHCP server listening on UDP
port 67.

Steinar Haug, Nethelp consulting, sthaug@nethelp.no




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20110628.210909.74655931.sthaug>