From 0a0796eb16f387b01e0da968883ebce7faf17972 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Fri, 18 Sep 2015 08:13:00 +0200 Subject: staging: rtl8188eu: Introduce monitor interface for IEEE 802.11 frames This adds support for monitoring IEEE 802.11 Data and Management frames received or transmitted by a RTL8188EU-based device handled by this driver. The monitor interface is not enabled by default and will be registered only if monitor_enable module parameter is set to 1. When enabled it will show up as a monX network device, which can be used by the userspace programs for monitoring network traffic. It is intended as an exploratory/debugging tool for rtl8188eu driver. Signed-off-by: Jakub Sitnicki Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8188eu/os_dep/mon.c | 194 ++++++++++++++++++++++++++++ drivers/staging/rtl8188eu/os_dep/os_intfs.c | 5 + drivers/staging/rtl8188eu/os_dep/usb_intf.c | 10 ++ 3 files changed, 209 insertions(+) create mode 100644 drivers/staging/rtl8188eu/os_dep/mon.c (limited to 'drivers/staging/rtl8188eu/os_dep') diff --git a/drivers/staging/rtl8188eu/os_dep/mon.c b/drivers/staging/rtl8188eu/os_dep/mon.c new file mode 100644 index 000000000000..3dd3dc05fd53 --- /dev/null +++ b/drivers/staging/rtl8188eu/os_dep/mon.c @@ -0,0 +1,194 @@ +/* + * RTL8188EU monitor interface + * + * Copyright (C) 2015 Jakub Sitnicki + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + */ + +#include +#include +#include + +#include +#include +#include + +/** + * 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; + memcpy(skb_put(skb, data_len), 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; + int iv_len, icv_len; + int data_len; + u8 *data; + + if (!dev || !frame) + return; + if (!netif_running(dev)) + return; + + attr = &frame->attrib; + data = frame->rx_data; + data_len = frame->len; + + /* 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) + 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_change_mtu = eth_change_mtu, + .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; + dev->destructor = free_netdev; + ether_setup(dev); + dev->tx_queue_len = 0; + 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); +} diff --git a/drivers/staging/rtl8188eu/os_dep/os_intfs.c b/drivers/staging/rtl8188eu/os_dep/os_intfs.c index 2361bce480c3..d85647c07c6c 100644 --- a/drivers/staging/rtl8188eu/os_dep/os_intfs.c +++ b/drivers/staging/rtl8188eu/os_dep/os_intfs.c @@ -185,6 +185,10 @@ MODULE_PARM_DESC(rtw_notch_filter, "0:Disable, 1:Enable, 2:Enable only for P2P") module_param_named(debug, rtw_debug, int, 0444); MODULE_PARM_DESC(debug, "Set debug level (1-9) (default 1)"); +static bool rtw_monitor_enable; +module_param_named(monitor_enable, rtw_monitor_enable, bool, 0444); +MODULE_PARM_DESC(monitor_enable, "Enable monitor inferface (default: false)"); + static int netdev_open(struct net_device *pnetdev); static int netdev_close(struct net_device *pnetdev); @@ -604,6 +608,7 @@ static void loadparam(struct adapter *padapter, struct net_device *pnetdev) snprintf(registry_par->ifname, 16, "%s", ifname); snprintf(registry_par->if2name, 16, "%s", if2name); registry_par->notch_filter = (u8)rtw_notch_filter; + registry_par->monitor_enable = rtw_monitor_enable; } static int rtw_net_set_mac_address(struct net_device *pnetdev, void *p) diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c index 33bfe054f867..82a7c27c517f 100644 --- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c +++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -348,6 +349,7 @@ static struct adapter *rtw_usb_if1_init(struct dvobj_priv *dvobj, { struct adapter *padapter = NULL; struct net_device *pnetdev = NULL; + struct net_device *pmondev; int status = _FAIL; padapter = (struct adapter *)vzalloc(sizeof(*padapter)); @@ -366,6 +368,13 @@ static struct adapter *rtw_usb_if1_init(struct dvobj_priv *dvobj, SET_NETDEV_DEV(pnetdev, dvobj_to_dev(dvobj)); padapter = rtw_netdev_priv(pnetdev); + if (padapter->registrypriv.monitor_enable) { + pmondev = rtl88eu_mon_init(); + if (pmondev == NULL) + netdev_warn(pnetdev, "Failed to initialize monitor interface"); + padapter->pmondev = pmondev; + } + /* step 2. hook HalFunc, allocate HalData */ hal_set_hal_ops(padapter); @@ -458,6 +467,7 @@ static void rtw_usb_if1_deinit(struct adapter *if1) unregister_netdev(pnetdev); rtw_proc_remove_one(pnetdev); } + rtl88eu_mon_deinit(if1->pmondev); rtw_cancel_all_timer(if1); rtw_dev_unload(if1); -- cgit