2018-06-25 15:41:56 -06:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2015-09-18 00:13:00 -06:00
|
|
|
/*
|
|
|
|
* 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>
|
2015-09-30 09:20:47 -06:00
|
|
|
#include <mon.h>
|
2015-09-18 00:13:00 -06:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 06:29:20 -06:00
|
|
|
skb_put_data(skb, data, data_len);
|
2015-09-18 00:13:00 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2017-11-02 03:30:13 -06:00
|
|
|
int iv_len, icv_len;
|
2015-09-18 00:13:00 -06:00
|
|
|
int data_len;
|
|
|
|
u8 *data;
|
|
|
|
|
|
|
|
if (!dev || !frame)
|
|
|
|
return;
|
|
|
|
if (!netif_running(dev))
|
|
|
|
return;
|
|
|
|
|
|
|
|
attr = &frame->attrib;
|
2017-02-06 10:23:28 -07:00
|
|
|
data = frame->pkt->data;
|
|
|
|
data_len = frame->pkt->len;
|
2015-09-18 00:13:00 -06:00
|
|
|
|
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);
|
|
|
|
|
2015-09-18 00:13:00 -06:00
|
|
|
if (attr->bdecrypted)
|
2017-11-02 03:30:13 -06:00
|
|
|
mon_recv_decrypted(dev, data, data_len, iv_len, icv_len);
|
2015-09-18 00:13:00 -06:00
|
|
|
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;
|
2015-09-18 00:13:00 -06:00
|
|
|
ether_setup(dev);
|
2016-04-15 11:14:19 -06:00
|
|
|
dev->priv_flags |= IFF_NO_QUEUE;
|
2015-09-18 00:13:00 -06:00
|
|
|
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);
|
|
|
|
}
|