From: Marcin Siodelski Date: Thu, 10 Jul 2014 07:46:38 +0000 (+0200) Subject: [2893] Assume that there may be multiple packets received in BPF buffer. X-Git-Tag: trac3482_base~81^2~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=67b078ed33e0284292afd7b2e3fc1739144b9b78;p=thirdparty%2Fkea.git [2893] Assume that there may be multiple packets received in BPF buffer. --- diff --git a/src/lib/dhcp/pkt_filter_bpf.cc b/src/lib/dhcp/pkt_filter_bpf.cc index 1575afa322..ba8dcf6e07 100644 --- a/src/lib/dhcp/pkt_filter_bpf.cc +++ b/src/lib/dhcp/pkt_filter_bpf.cc @@ -329,29 +329,71 @@ PktFilterBPF::receive(const Iface& iface, const SocketInfo& socket_info) { // Now that we finished getting data from the fallback socket, we // have to get the data from the raw socket too. - int data_len = read(socket_info.sockfd_, iface.getReadBufferPtr(), - iface.getReadBufferSize()); + memset(iface.getReadBufferPtr(), 0, iface.getReadBufferSize()); + datalen = read(socket_info.sockfd_, iface.getReadBufferPtr(), + iface.getReadBufferSize()); // If negative value is returned by read(), it indicates that an // error occured. If returned value is 0, no data was read from the - // socket.b In both cases something has gone wrong, because we expect + // socket. In both cases something has gone wrong, because we expect // that a chunk of data is there. We signal the lack of data by // returing an empty packet. - if (data_len <= 0) { + if (datalen <= 0) { return Pkt4Ptr(); } + datalen = BPF_WORDALIGN(datalen); + // Holds BPF header. struct bpf_hdr bpfh; - memcpy(static_cast(&bpfh), - static_cast(iface.getReadBufferPtr()), - sizeof(bpfh)); - if (bpfh.bh_hdrlen >= data_len) { - isc_throw(SocketReadError, "packet received from the BPF device" - << " attached to interface " << iface.getName() - << " is truncated"); + + /// @todo BPF may occasionally append more than one packet in a + /// single read. Our current libdhcp++ API is oriented towards receiving + /// one packet at the time so we just pick first usable packet here + /// and drop other packets. In the future the additional packets should + /// be queued and processed. For now, we just iterate over the packets + /// in the buffer and pick the first usable one. + int offset = 0; + while (offset < datalen) { + // Check if the BPF header fits in the reminder of the buffer. + // If it doesn't something is really wrong. + if (datalen - offset < sizeof(bpf_hdr)) { + isc_throw(SocketReadError, "packet received over the BPF device on" + " interface " << iface.getName() << " has a truncated " + " BPF header"); + } + + // Copy the BPF header. + memcpy(static_cast(&bpfh), + static_cast(iface.getReadBufferPtr()), + sizeof(bpfh)); + + // Check if the captured data fit into the reminder of the buffer. + // Again, something is really wrong here. + if (offset + bpfh.bh_hdrlen + bpfh.bh_caplen > datalen) { + isc_throw(SocketReadError, "packet received from the BPF device" + << " attached to interface " << iface.getName() + << " is truncated"); + } + + // Check if the whole packet has been captured. + if (bpfh.bh_caplen != bpfh.bh_datalen) { + // Not whole packet captured, proceed to next received packet. + offset = BPF_WORDALIGN(offset + bpfh.bh_hdrlen + bpfh.bh_caplen); + continue; + } + + // All checks passed, let's use the packet at the offset found. + // Typically it will be at offset 0. + break; + }; + + // No parsable packet found, so return. + if (offset >= datalen) { + return (Pkt4Ptr()); } - InputBuffer buf(iface.getReadBufferPtr() + bpfh.bh_hdrlen, - data_len - bpfh.bh_hdrlen); + // Skip the BPF header and create the buffer holding a frame. + InputBuffer buf(iface.getReadBufferPtr() + offset + bpfh.bh_hdrlen, + datalen - bpfh.bh_hdrlen - offset); // @todo: This is awkward way to solve the chicken and egg problem