alistair23-linux/drivers/staging/rtl8188eu/os_dep/mon.c

187 lines
4.2 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* RTL8188EU monitor interface
*
* Copyright (C) 2015 Jakub Sitnicki
*/
#include <linux/ieee80211.h>
#include <linux/netdevice.h>
#include <net/cfg80211.h>
#include <drv_types.h>
#include <rtw_recv.h>
#include <rtw_xmit.h>
#include <mon.h>
/**
* unprotect_frame() - unset Protected flag and strip off IV and ICV/MIC
*/
static void unprotect_frame(struct sk_buff *skb, int iv_len, int icv_len)
{
struct ieee80211_hdr *hdr;
int hdr_len;
hdr = (struct ieee80211_hdr *)skb->data;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
if (skb->len < hdr_len + iv_len + icv_len)
return;
if (!ieee80211_has_protected(hdr->frame_control))
return;
hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_PROTECTED);
memmove(skb->data + iv_len, skb->data, hdr_len);
skb_pull(skb, iv_len);
skb_trim(skb, skb->len - icv_len);
}
static void mon_recv_decrypted(struct net_device *dev, const u8 *data,
int data_len, int iv_len, int icv_len)
{
struct sk_buff *skb;
skb = netdev_alloc_skb(dev, data_len);
if (!skb)
return;
skb_put_data(skb, data, data_len);
/*
* Frame data is not encrypted. Strip off protection so
* userspace doesn't think that it is.
*/
unprotect_frame(skb, iv_len, icv_len);
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
}
static void mon_recv_encrypted(struct net_device *dev, const u8 *data,
int data_len)
{
if (net_ratelimit())
netdev_info(dev, "Encrypted packets are not supported");
}
/**
* rtl88eu_mon_recv_hook() - forward received frame to the monitor interface
*
* Assumes that the frame contains an IV and an ICV/MIC, and that
* encrypt field in frame->attrib have been set accordingly.
*/
void rtl88eu_mon_recv_hook(struct net_device *dev, struct recv_frame *frame)
{
struct rx_pkt_attrib *attr;
staging: rtl8188eu: Revert 4 commits breaking ARP Commit 2ba8444c97b1 ("staging:r8188eu: move IV/ICV trimming into decrypt() and also place it after rtl88eu_mon_recv_hook()") breaks ARP. After this commit ssh-ing to a laptop with r8188eu wifi no longer works if the machine connecting has never communicated with the laptop before. This is 100% reproducable using "arp -d <ipv4> && ssh <ipv4>" to ssh to a laptop with r8188eu wifi. This commit reverts 4 commits in total: 1. Commit 79650ffde38e ("staging:r8188eu: trim IV/ICV fields in validate_recv_data_frame()") This commit depends on 2 of the other commits being reverted. 2. Commit 02b19b4c4920 ("staging:r8188eu: inline unprotect_frame() in mon_recv_decrypted_recv()") The inline code is wrong the un-inlined version contains: if (skb->len < hdr_len + iv_len + icv_len) return; ... Where as the inline-ed code introduced by this commit does: if (skb->len < hdr_len + iv_len + icv_len) { ... Note the same check, but now to actually continue doing ... instead of to not do it, so this commit is no good. 3. Commit d86e16da6a5d ("staging:r8188eu: use different mon_recv_decrypted() inside rtl88eu_mon_recv_hook() and rtl88eu_mon_xmit_hook().") This commit introduced a 1:1 copy of a function so that one of the 2 copies can be modified in the 2 commits we're already reverting. 4. Commit 2ba8444c97b1 ("staging:r8188eu: move IV/ICV trimming into decrypt() and also place it after rtl88eu_mon_recv_hook()") This is the commit actually breaking ARP. Note this commit is a straight-forward squash of the revert of these 4 commits, without any changes. Cc: stable@vger.kernel.org Cc: Ivan Safonov <insafonov@gmail.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-02 03:30:13 -06:00
int iv_len, icv_len;
int data_len;
u8 *data;
if (!dev || !frame)
return;
if (!netif_running(dev))
return;
attr = &frame->attrib;
data = frame->pkt->data;
data_len = frame->pkt->len;
staging: rtl8188eu: Revert 4 commits breaking ARP Commit 2ba8444c97b1 ("staging:r8188eu: move IV/ICV trimming into decrypt() and also place it after rtl88eu_mon_recv_hook()") breaks ARP. After this commit ssh-ing to a laptop with r8188eu wifi no longer works if the machine connecting has never communicated with the laptop before. This is 100% reproducable using "arp -d <ipv4> && ssh <ipv4>" to ssh to a laptop with r8188eu wifi. This commit reverts 4 commits in total: 1. Commit 79650ffde38e ("staging:r8188eu: trim IV/ICV fields in validate_recv_data_frame()") This commit depends on 2 of the other commits being reverted. 2. Commit 02b19b4c4920 ("staging:r8188eu: inline unprotect_frame() in mon_recv_decrypted_recv()") The inline code is wrong the un-inlined version contains: if (skb->len < hdr_len + iv_len + icv_len) return; ... Where as the inline-ed code introduced by this commit does: if (skb->len < hdr_len + iv_len + icv_len) { ... Note the same check, but now to actually continue doing ... instead of to not do it, so this commit is no good. 3. Commit d86e16da6a5d ("staging:r8188eu: use different mon_recv_decrypted() inside rtl88eu_mon_recv_hook() and rtl88eu_mon_xmit_hook().") This commit introduced a 1:1 copy of a function so that one of the 2 copies can be modified in the 2 commits we're already reverting. 4. Commit 2ba8444c97b1 ("staging:r8188eu: move IV/ICV trimming into decrypt() and also place it after rtl88eu_mon_recv_hook()") This is the commit actually breaking ARP. Note this commit is a straight-forward squash of the revert of these 4 commits, without any changes. Cc: stable@vger.kernel.org Cc: Ivan Safonov <insafonov@gmail.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-02 03:30:13 -06:00
/* Broadcast and multicast frames don't have attr->{iv,icv}_len set */
SET_ICE_IV_LEN(iv_len, icv_len, attr->encrypt);
if (attr->bdecrypted)
staging: rtl8188eu: Revert 4 commits breaking ARP Commit 2ba8444c97b1 ("staging:r8188eu: move IV/ICV trimming into decrypt() and also place it after rtl88eu_mon_recv_hook()") breaks ARP. After this commit ssh-ing to a laptop with r8188eu wifi no longer works if the machine connecting has never communicated with the laptop before. This is 100% reproducable using "arp -d <ipv4> && ssh <ipv4>" to ssh to a laptop with r8188eu wifi. This commit reverts 4 commits in total: 1. Commit 79650ffde38e ("staging:r8188eu: trim IV/ICV fields in validate_recv_data_frame()") This commit depends on 2 of the other commits being reverted. 2. Commit 02b19b4c4920 ("staging:r8188eu: inline unprotect_frame() in mon_recv_decrypted_recv()") The inline code is wrong the un-inlined version contains: if (skb->len < hdr_len + iv_len + icv_len) return; ... Where as the inline-ed code introduced by this commit does: if (skb->len < hdr_len + iv_len + icv_len) { ... Note the same check, but now to actually continue doing ... instead of to not do it, so this commit is no good. 3. Commit d86e16da6a5d ("staging:r8188eu: use different mon_recv_decrypted() inside rtl88eu_mon_recv_hook() and rtl88eu_mon_xmit_hook().") This commit introduced a 1:1 copy of a function so that one of the 2 copies can be modified in the 2 commits we're already reverting. 4. Commit 2ba8444c97b1 ("staging:r8188eu: move IV/ICV trimming into decrypt() and also place it after rtl88eu_mon_recv_hook()") This is the commit actually breaking ARP. Note this commit is a straight-forward squash of the revert of these 4 commits, without any changes. Cc: stable@vger.kernel.org Cc: Ivan Safonov <insafonov@gmail.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-02 03:30:13 -06:00
mon_recv_decrypted(dev, data, data_len, iv_len, icv_len);
else
mon_recv_encrypted(dev, data, data_len);
}
/**
* rtl88eu_mon_xmit_hook() - forward trasmitted frame to the monitor interface
*
* Assumes that:
* - frame header contains an IV and frame->attrib.iv_len is set accordingly,
* - data is not encrypted and ICV/MIC has not been appended yet.
*/
void rtl88eu_mon_xmit_hook(struct net_device *dev, struct xmit_frame *frame,
uint frag_len)
{
struct pkt_attrib *attr;
u8 *data;
int i, offset;
if (!dev || !frame)
return;
if (!netif_running(dev))
return;
attr = &frame->attrib;
offset = TXDESC_SIZE + frame->pkt_offset * PACKET_OFFSET_SZ;
data = frame->buf_addr + offset;
for (i = 0; i < attr->nr_frags - 1; i++) {
mon_recv_decrypted(dev, data, frag_len, attr->iv_len, 0);
data += frag_len;
data = (u8 *)round_up((size_t)data, 4);
}
/* Last fragment has different length */
mon_recv_decrypted(dev, data, attr->last_txcmdsz, attr->iv_len, 0);
}
static netdev_tx_t mon_xmit(struct sk_buff *skb, struct net_device *dev)
{
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
static const struct net_device_ops mon_netdev_ops = {
.ndo_start_xmit = mon_xmit,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
static void mon_setup(struct net_device *dev)
{
dev->netdev_ops = &mon_netdev_ops;
net: Fix inconsistent teardown and release of private netdev state. Network devices can allocate reasources and private memory using netdev_ops->ndo_init(). However, the release of these resources can occur in one of two different places. Either netdev_ops->ndo_uninit() or netdev->destructor(). The decision of which operation frees the resources depends upon whether it is necessary for all netdev refs to be released before it is safe to perform the freeing. netdev_ops->ndo_uninit() presumably can occur right after the NETDEV_UNREGISTER notifier completes and the unicast and multicast address lists are flushed. netdev->destructor(), on the other hand, does not run until the netdev references all go away. Further complicating the situation is that netdev->destructor() almost universally does also a free_netdev(). This creates a problem for the logic in register_netdevice(). Because all callers of register_netdevice() manage the freeing of the netdev, and invoke free_netdev(dev) if register_netdevice() fails. If netdev_ops->ndo_init() succeeds, but something else fails inside of register_netdevice(), it does call ndo_ops->ndo_uninit(). But it is not able to invoke netdev->destructor(). This is because netdev->destructor() will do a free_netdev() and then the caller of register_netdevice() will do the same. However, this means that the resources that would normally be released by netdev->destructor() will not be. Over the years drivers have added local hacks to deal with this, by invoking their destructor parts by hand when register_netdevice() fails. Many drivers do not try to deal with this, and instead we have leaks. Let's close this hole by formalizing the distinction between what private things need to be freed up by netdev->destructor() and whether the driver needs unregister_netdevice() to perform the free_netdev(). netdev->priv_destructor() performs all actions to free up the private resources that used to be freed by netdev->destructor(), except for free_netdev(). netdev->needs_free_netdev is a boolean that indicates whether free_netdev() should be done at the end of unregister_netdevice(). Now, register_netdevice() can sanely release all resources after ndo_ops->ndo_init() succeeds, by invoking both ndo_ops->ndo_uninit() and netdev->priv_destructor(). And at the end of unregister_netdevice(), we invoke netdev->priv_destructor() and optionally call free_netdev(). Signed-off-by: David S. Miller <davem@davemloft.net>
2017-05-08 10:52:56 -06:00
dev->needs_free_netdev = true;
ether_setup(dev);
dev->priv_flags |= IFF_NO_QUEUE;
dev->type = ARPHRD_IEEE80211;
/*
* Use a locally administered address (IEEE 802)
* XXX: Copied from mac80211_hwsim driver. Revisit.
*/
eth_zero_addr(dev->dev_addr);
dev->dev_addr[0] = 0x12;
}
struct net_device *rtl88eu_mon_init(void)
{
struct net_device *dev;
int err;
dev = alloc_netdev(0, "mon%d", NET_NAME_UNKNOWN, mon_setup);
if (!dev)
goto fail;
err = register_netdev(dev);
if (err < 0)
goto fail_free_dev;
return dev;
fail_free_dev:
free_netdev(dev);
fail:
return NULL;
}
void rtl88eu_mon_deinit(struct net_device *dev)
{
if (!dev)
return;
unregister_netdev(dev);
}