Example #1
0
adf_nbuf_t
ol_rx_pn_check_base(
    struct ol_txrx_vdev_t *vdev,
    struct ol_txrx_peer_t *peer,
    unsigned tid,
    adf_nbuf_t msdu_list)
{
    struct ol_txrx_pdev_t *pdev = vdev->pdev;
    union htt_rx_pn_t *last_pn;
    adf_nbuf_t out_list_head = NULL;
    adf_nbuf_t out_list_tail = NULL;
    adf_nbuf_t mpdu;
    int index; /* unicast vs. multicast */
    int pn_len;
    void *rx_desc;
    int last_pn_valid;

    /* Make sure host pn check is not redundant */
    if ((adf_os_atomic_read(&peer->fw_pn_check)) ||
         (vdev->opmode == wlan_op_mode_ibss)) {
        return msdu_list;
    }

    /* First, check whether the PN check applies */
    rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, msdu_list);
    adf_os_assert(htt_rx_msdu_has_wlan_mcast_flag(pdev->htt_pdev, rx_desc));
    index = htt_rx_msdu_is_wlan_mcast(pdev->htt_pdev, rx_desc) ?
        txrx_sec_mcast : txrx_sec_ucast;
    pn_len = pdev->rx_pn[peer->security[index].sec_type].len;
    if (pn_len == 0) {
        return msdu_list;
    }

    last_pn_valid = peer->tids_last_pn_valid[tid];
    last_pn = &peer->tids_last_pn[tid];
    mpdu = msdu_list;
    while (mpdu) {
        adf_nbuf_t mpdu_tail, next_mpdu;
        union htt_rx_pn_t new_pn;
        int pn_is_replay = 0;
        rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, mpdu);

        /*
         * Find the last MSDU within this MPDU, and
         * the find the first MSDU within the next MPDU.
         */
        ol_rx_mpdu_list_next(pdev, mpdu, &mpdu_tail, &next_mpdu);

        /* Don't check the PN replay for non-encrypted frames */
        if (!htt_rx_mpdu_is_encrypted(pdev->htt_pdev, rx_desc)) {
            ADD_MPDU_TO_LIST(out_list_head, out_list_tail, mpdu, mpdu_tail);
            mpdu = next_mpdu;
            continue;
        }

        /* retrieve PN from rx descriptor */
        htt_rx_mpdu_desc_pn(pdev->htt_pdev, rx_desc, &new_pn, pn_len);

        /* if there was no prior PN, there's nothing to check */
        if (last_pn_valid) {
            pn_is_replay = pdev->rx_pn[peer->security[index].sec_type].cmp(
                &new_pn, last_pn, index == txrx_sec_ucast, vdev->opmode);
        } else {
            last_pn_valid = peer->tids_last_pn_valid[tid] = 1;
        }

        if (pn_is_replay) {
            adf_nbuf_t msdu;
            static u_int32_t last_pncheck_print_time = 0;
            int log_level;
            u_int32_t current_time_ms;

            /*
             * This MPDU failed the PN check:
             * 1.  Notify the control SW of the PN failure
             *     (so countermeasures can be taken, if necessary)
             * 2.  Discard all the MSDUs from this MPDU.
             */
            msdu = mpdu;
            current_time_ms = adf_os_ticks_to_msecs(adf_os_ticks());
            if (TXRX_PN_CHECK_FAILURE_PRINT_PERIOD_MS <
                     (current_time_ms - last_pncheck_print_time)) {
                last_pncheck_print_time = current_time_ms;
                log_level = TXRX_PRINT_LEVEL_WARN;
            }
            else {
                log_level = TXRX_PRINT_LEVEL_INFO2;
            }

            TXRX_PRINT(log_level,
                "PN check failed - TID %d, peer %p "
                "(%02x:%02x:%02x:%02x:%02x:%02x) %s\n"
                "    old PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n"
                "    new PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n"
                "    new seq num = %d\n",
                tid, peer,
                peer->mac_addr.raw[0], peer->mac_addr.raw[1],
                peer->mac_addr.raw[2], peer->mac_addr.raw[3],
                peer->mac_addr.raw[4], peer->mac_addr.raw[5],
                (index == txrx_sec_ucast) ? "ucast" : "mcast",
                last_pn->pn128[1],
                last_pn->pn128[0],
                last_pn->pn128[0] & 0xffffffffffffULL,
                new_pn.pn128[1],
                new_pn.pn128[0],
                new_pn.pn128[0] & 0xffffffffffffULL,
                htt_rx_mpdu_desc_seq_num(pdev->htt_pdev, rx_desc));
#if defined(ENABLE_RX_PN_TRACE)
            ol_rx_pn_trace_display(pdev, 1);
#endif /* ENABLE_RX_PN_TRACE */
            ol_rx_err(
                pdev->ctrl_pdev,
                vdev->vdev_id, peer->mac_addr.raw, tid,
                htt_rx_mpdu_desc_tsf32(pdev->htt_pdev, rx_desc),
                OL_RX_ERR_PN, mpdu, NULL, 0);
            /* free all MSDUs within this MPDU */
            do {
                adf_nbuf_t next_msdu;
                OL_RX_ERR_STATISTICS_1(pdev, vdev, peer, rx_desc, OL_RX_ERR_PN);
                next_msdu = adf_nbuf_next(msdu);
                htt_rx_desc_frame_free(pdev->htt_pdev, msdu);
                if (msdu == mpdu_tail) {
                    break;
                } else {
                    msdu = next_msdu;
                }
            } while (1);
        } else {
            ADD_MPDU_TO_LIST(out_list_head, out_list_tail, mpdu, mpdu_tail);
            /*
             * Remember the new PN.
             * For simplicity, just do 2 64-bit word copies to cover the worst
             * case (WAPI), regardless of the length of the PN.
             * This is more efficient than doing a conditional branch to copy
             * only the relevant portion.
             */
            last_pn->pn128[0] = new_pn.pn128[0];
            last_pn->pn128[1] = new_pn.pn128[1];
            OL_RX_PN_TRACE_ADD(pdev, peer, tid, rx_desc);
        }

        mpdu = next_mpdu;
    }
    /* make sure the list is null-terminated */
    if (out_list_tail) {
        adf_nbuf_set_next(out_list_tail, NULL);
    }
    return out_list_head;
}
Example #2
0
adf_nbuf_t
ol_rx_pn_check_base(
    struct ol_txrx_vdev_t *vdev,
    struct ol_txrx_peer_t *peer,
    unsigned tid,
    adf_nbuf_t msdu_list)
{
    struct ol_txrx_pdev_t *pdev = vdev->pdev;
    union htt_rx_pn_t *last_pn, *global_pn, *suspect_pn;
    adf_nbuf_t out_list_head = NULL;
    adf_nbuf_t out_list_tail = NULL;
    adf_nbuf_t mpdu;
    int index; /* unicast vs. multicast */
    int pn_len;
    void *rx_desc;
    int last_pn_valid;

    /* First, check whether the PN check applies */
    rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, msdu_list);
    adf_os_assert(htt_rx_msdu_has_wlan_mcast_flag(pdev->htt_pdev, rx_desc));
    index = htt_rx_msdu_is_wlan_mcast(pdev->htt_pdev, rx_desc) ?
        txrx_sec_mcast : txrx_sec_ucast;
    pn_len = pdev->rx_pn[peer->security[index].sec_type].len;
    if (pn_len == 0) {
        return msdu_list;
    }

    last_pn_valid = peer->tids_last_pn_valid[tid];
    last_pn = &peer->tids_last_pn[tid];
    global_pn = &peer->global_pn;
    suspect_pn = &peer->tids_suspect_pn[tid];
    mpdu = msdu_list;
    while (mpdu) {
        adf_nbuf_t mpdu_tail, next_mpdu;
        union htt_rx_pn_t new_pn;
        int pn_is_replay = 0, update_last_pn = 1;
#if ATH_SUPPORT_WAPI
        bool is_mpdu_encrypted = 0;
        bool is_unencrypted_pkt_wai = 0;
#endif

        rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, mpdu);

        /*
         * Find the last MSDU within this MPDU, and
         * the find the first MSDU within the next MPDU.
         */
        ol_rx_mpdu_list_next(pdev, mpdu, &mpdu_tail, &next_mpdu);
#if ATH_SUPPORT_WAPI
        /* Don't check the PN replay for non-encrypted frames
           or if this is a WAI packet */
        is_mpdu_encrypted = htt_rx_mpdu_is_encrypted(pdev->htt_pdev, rx_desc);
        is_unencrypted_pkt_wai = is_mpdu_encrypted  ?  false : vdev->osif_check_wai(vdev->osif_vdev, mpdu, mpdu_tail);
        if ((!vdev->drop_unenc && !is_mpdu_encrypted) || is_unencrypted_pkt_wai) {
#else
        /* Don't check the PN replay for non-encrypted frames */
        if (!vdev->drop_unenc && !htt_rx_mpdu_is_encrypted(pdev->htt_pdev, rx_desc)) {
#endif
                ADD_MPDU_TO_LIST(out_list_head, out_list_tail, mpdu, mpdu_tail);
                mpdu = next_mpdu;
                continue;
        }

        /* retrieve PN from rx descriptor */
        htt_rx_mpdu_desc_pn(pdev->htt_pdev, rx_desc, &new_pn, pn_len);

        /* if there was no prior PN, there's nothing to check */
        if (last_pn_valid) {
            pn_is_replay = pdev->rx_pn[peer->security[index].sec_type].cmp(
                &new_pn, last_pn, index == txrx_sec_ucast, vdev->opmode);
		} else if (peer->authorize) {
                last_pn_valid = peer->tids_last_pn_valid[tid] = 1;
        }

        if (peer->authorize && peer->security[index].sec_type == htt_sec_type_aes_ccmp) {
            if ((new_pn.pn48 & 0xffffffffffffULL) >
                 ((last_pn->pn48 + MAX_CCMP_PN_GAP_ERR_CHECK) & 0xffffffffffffULL)) {
                /* PN jump wrt last_pn is > MAX_CCMP_PN_GAP_ERR_CHECK - PN of current frame is suspected */
                if (suspect_pn->pn48) {
                    /* Check whether PN of the current frame is following prev PN seq or not */
                    if ((new_pn.pn48 & 0xffffffffffffULL) < (suspect_pn->pn48 & 0xffffffffffffULL)) {
                        /*
                         * PN number of the curr frame < PN no of prev rxed frame
                         * As we are not sure about prev suspect PN, to detect replay,
                         * check the current PN with global PN
                         */
                        if ((new_pn.pn48 & 0xffffffffffffULL) < (global_pn->pn48 & 0xffffffffffffULL)) {
                            /* Replay violation */
                            pn_is_replay = 1;
                        } else {
                            /* Current PN is following global PN, so mark this as suspected PN
                             * Don't update last_pn & global_pn
                             */
                            suspect_pn->pn128[0] = new_pn.pn128[0];
                            suspect_pn->pn128[1] = new_pn.pn128[1];
                            update_last_pn = 0;
                        }
                    } else if ((new_pn.pn48 & 0xffffffffffffULL) <
                                ((suspect_pn->pn48 + MAX_CCMP_PN_GAP_ERR_CHECK) & 0xffffffffffffULL)) {
                        /* Current PN is following prev suspected PN seq
                         * Update last_pn & global_pn (update_last_pn = 1;)
                         */
                    } else {
                        /*
                         * Current PN is neither following prev suspected PN nor last_pn
                         * Mark this as new suspect and don't update last_pn & global_pn
                         */
                        suspect_pn->pn128[0] = new_pn.pn128[0];
                        suspect_pn->pn128[1] = new_pn.pn128[1];
                        update_last_pn = 0;
                    }
                } else {
                    /* New Jump in PN observed
                     * So mark this PN as suspected and don't update last_pn/global_pn
                     */
                    suspect_pn->pn128[0] = new_pn.pn128[0];
                    suspect_pn->pn128[1] = new_pn.pn128[1];
                    update_last_pn = 0;
                }
            } else {
                /* Valid PN, update last_pn & global_pn (update_last_pn = 1;) */
            }
        }

        if (pn_is_replay) {
            adf_nbuf_t msdu;
            /*
             * This MPDU failed the PN check:
             * 1.  Notify the control SW of the PN failure
             *     (so countermeasures can be taken, if necessary)
             * 2.  Discard all the MSDUs from this MPDU.
             */
            msdu = mpdu;
            TXRX_PRINT(TXRX_PRINT_LEVEL_INFO1,
                "PN check failed on offload path- TID %d, peer %p "
                "(%02x:%02x:%02x:%02x:%02x:%02x) %s\n"
                "    old PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n"
                "    new PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n"
                "    global PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n"
                "    suspect PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n"
#if RX_DEBUG
                "    htt_status = %d\n"
#endif
                "    prev seq num = %d\n",
                "    new seq num = %d\n",
                tid, peer,
                peer->mac_addr.raw[0], peer->mac_addr.raw[1],
                peer->mac_addr.raw[2], peer->mac_addr.raw[3],
                peer->mac_addr.raw[4], peer->mac_addr.raw[5],
                (index == txrx_sec_ucast) ? "ucast" : "mcast",
                last_pn->pn128[1],
                last_pn->pn128[0],
                last_pn->pn128[0] & 0xffffffffffffULL,
                new_pn.pn128[1],
                new_pn.pn128[0],
                new_pn.pn128[0] & 0xffffffffffffULL,
                global_pn->pn128[1],
                global_pn->pn128[0],
                global_pn->pn128[0] & 0xffffffffffffULL,
                suspect_pn->pn128[1],
                suspect_pn->pn128[0],
                suspect_pn->pn128[0] & 0xffffffffffffULL,
#if RX_DEBUG
                htt_rx_mpdu_status(pdev->htt_pdev),
#endif
                peer->tids_last_seq[tid],
                htt_rx_mpdu_desc_seq_num(pdev->htt_pdev, rx_desc));
            ol_rx_pn_trace_display(pdev, 1);
            ol_rx_err(
                pdev->ctrl_pdev,
                vdev->vdev_id, peer->mac_addr.raw, tid,
                htt_rx_mpdu_desc_tsf32(pdev->htt_pdev, rx_desc),
                OL_RX_ERR_PN, mpdu);
            /* free all MSDUs within this MPDU */
            do {
                adf_nbuf_t next_msdu;
                next_msdu = adf_nbuf_next(msdu);
                htt_rx_desc_frame_free(pdev->htt_pdev, msdu);
                if (msdu == mpdu_tail) {
                    break;
                } else {
                    msdu = next_msdu;
                }
            } while (1);
        } else {
            ADD_MPDU_TO_LIST(out_list_head, out_list_tail, mpdu, mpdu_tail);
            if(peer->authorize) {
                /*
                 * Remember the new PN.
                 * For simplicity, just do 2 64-bit word copies to cover the worst
                 * case (WAPI), regardless of the length of the PN.
                 * This is more efficient than doing a conditional branch to copy
                 * only the relevant portion.
                 */
                if (update_last_pn) {
                    last_pn->pn128[0] = new_pn.pn128[0];
                    last_pn->pn128[1] = new_pn.pn128[1];
                    global_pn->pn128[0] = new_pn.pn128[0];
                    global_pn->pn128[1] = new_pn.pn128[1];
                    suspect_pn->pn128[0] = 0;
                    suspect_pn->pn128[1] = 0;
                }
                OL_RX_PN_TRACE_ADD(pdev, peer, tid, rx_desc);
            }
        }

        mpdu = next_mpdu;
    }
    /* make sure the list is null-terminated */
    if (out_list_tail) {
        adf_nbuf_set_next(out_list_tail, NULL);
    }
    return out_list_head;
}

void
ol_rx_pn_check(
    struct ol_txrx_vdev_t *vdev,
    struct ol_txrx_peer_t *peer,
    unsigned tid,
    adf_nbuf_t msdu_list)
{
    msdu_list = ol_rx_pn_check_base(vdev, peer, tid, msdu_list);
    ol_rx_fwd_check(vdev, peer, tid, msdu_list);
}