def defrag(plist):
    """defrag(plist) -> ([not fragmented], [defragmented],
                  [ [bad fragments], [bad fragments], ... ])"""
    frags = defaultdict(PacketList)
    nofrag = PacketList()
    for p in plist:
        ip = p[IP]
        if IP not in p:
            nofrag.append(p)
            continue
        if ip.frag == 0 and ip.flags & 1 == 0:
            nofrag.append(p)
            continue
        uniq = (ip.id, ip.src, ip.dst, ip.proto)
        frags[uniq].append(p)
    defrag = []
    missfrag = []
    for lst in frags.values():
        lst.sort(key=lambda x: x.frag)
        p = lst[0]
        lastp = lst[-1]
        if p.frag > 0 or lastp.flags & 1 != 0:  # first or last fragment missing
            missfrag.append(lst)
            continue
        p = p.copy()
        if conf.padding_layer in p:
            del p[conf.padding_layer].underlayer.payload
        ip = p[IP]
        if ip.len is None or ip.ihl is None:
            clen = len(ip.payload)
        else:
            clen = ip.len - (ip.ihl << 2)
        txt = conf.raw_layer()
        for q in lst[1:]:
            if clen != q.frag << 3:  # Wrong fragmentation offset
                if clen > q.frag << 3:
                    warning("Fragment overlap (%i > %i) %r || %r ||  %r" % (clen, q.frag << 3, p, txt, q))
                missfrag.append(lst)
                break
            if q[IP].len is None or q[IP].ihl is None:
                clen += len(q[IP].payload)
            else:
                clen += q[IP].len - (q[IP].ihl << 2)
            if conf.padding_layer in q:
                del q[conf.padding_layer].underlayer.payload
            txt.add_payload(q[IP].payload.copy())
        else:
            ip.flags &= ~1  # !MF
            del ip.chksum
            del ip.len
            p = p / txt
            defrag.append(p)
    defrag2 = PacketList()
    for p in defrag:
        defrag2.append(p.__class__(bytes(p)))
    return nofrag, defrag2, missfrag