From: Jakub Sitnicki Date: Fri, 18 Sep 2015 06:13:00 +0000 (+0200) Subject: staging: rtl8188eu: Introduce monitor interface for IEEE 802.11 frames X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=0a0796eb16f387b01e0da968883ebce7faf17972;p=linux-beck.git 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 --- diff --git a/drivers/staging/rtl8188eu/Makefile b/drivers/staging/rtl8188eu/Makefile index 31ac15961a00..ed723585b502 100644 --- a/drivers/staging/rtl8188eu/Makefile +++ b/drivers/staging/rtl8188eu/Makefile @@ -42,6 +42,7 @@ r8188eu-y := \ hal/usb_halinit.o \ os_dep/ioctl_linux.o \ os_dep/mlme_linux.o \ + os_dep/mon.o \ os_dep/os_intfs.o \ os_dep/osdep_service.o \ os_dep/recv_linux.o \ diff --git a/drivers/staging/rtl8188eu/core/rtw_recv.c b/drivers/staging/rtl8188eu/core/rtw_recv.c index 44eeb03213e6..cb90ad5f6143 100644 --- a/drivers/staging/rtl8188eu/core/rtw_recv.c +++ b/drivers/staging/rtl8188eu/core/rtw_recv.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -1329,6 +1330,19 @@ static int validate_recv_frame(struct adapter *adapter, break; } + /* + * This is the last moment before management and control frames get + * discarded. So we need to forward them to the monitor now or never. + * + * At the same time data frames can still be encrypted if software + * decryption is in use. However, decryption can occur not until later + * (see recv_func()). + * + * Hence forward the frame to the monitor anyway to preserve the order + * in which frames were received. + */ + rtl88eu_mon_recv_hook(adapter->pmondev, precv_frame); + exit: return retval; diff --git a/drivers/staging/rtl8188eu/core/rtw_xmit.c b/drivers/staging/rtl8188eu/core/rtw_xmit.c index 5dc0b90e8ab5..cabb810369bd 100644 --- a/drivers/staging/rtl8188eu/core/rtw_xmit.c +++ b/drivers/staging/rtl8188eu/core/rtw_xmit.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -1100,6 +1101,9 @@ s32 rtw_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, struct memcpy(mem_start, pbuf_start + hw_hdr_offset, pattrib->hdrlen); } + /* Frame is about to be encrypted. Forward it to the monitor first. */ + rtl88eu_mon_xmit_hook(padapter->pmondev, pxmitframe, frg_len); + if (xmitframe_addmic(padapter, pxmitframe) == _FAIL) { RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("xmitframe_addmic(padapter, pxmitframe) == _FAIL\n")); DBG_88E("xmitframe_addmic(padapter, pxmitframe) == _FAIL\n"); diff --git a/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c b/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c index 44335605f2f6..7c5086ecff17 100644 --- a/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c +++ b/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c @@ -20,6 +20,7 @@ #define _RTL8188E_XMIT_C_ #include #include +#include #include #include #include @@ -684,6 +685,9 @@ enqueue: s32 rtl8188eu_mgnt_xmit(struct adapter *adapt, struct xmit_frame *pmgntframe) { + struct xmit_priv *xmitpriv = &adapt->xmitpriv; + + rtl88eu_mon_xmit_hook(adapt->pmondev, pmgntframe, xmitpriv->frag_len); return rtw_dump_xframe(adapt, pmgntframe); } diff --git a/drivers/staging/rtl8188eu/include/drv_types.h b/drivers/staging/rtl8188eu/include/drv_types.h index bcc74dcd8207..0729bd40b02a 100644 --- a/drivers/staging/rtl8188eu/include/drv_types.h +++ b/drivers/staging/rtl8188eu/include/drv_types.h @@ -131,6 +131,7 @@ struct registry_priv { u8 if2name[16]; u8 notch_filter; + bool monitor_enable; }; /* For registry parameters */ @@ -209,6 +210,7 @@ struct adapter { void (*intf_start)(struct adapter *adapter); void (*intf_stop)(struct adapter *adapter); struct net_device *pnetdev; + struct net_device *pmondev; /* used by rtw_rereg_nd_name related function */ struct rereg_nd_name_data { diff --git a/drivers/staging/rtl8188eu/include/mon.h b/drivers/staging/rtl8188eu/include/mon.h new file mode 100644 index 000000000000..f31fa688e092 --- /dev/null +++ b/drivers/staging/rtl8188eu/include/mon.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +/* + * Monitor interface receives all transmitted and received IEEE 802.11 + * frames, both Data and Management, and passes them up to userspace + * preserving the WLAN headers. + */ + +#ifndef _MON_H_ +#define _MON_H_ + +struct net_device; +struct recv_frame; +struct xmit_frame; + +struct net_device *rtl88eu_mon_init(void); +void rtl88eu_mon_deinit(struct net_device *dev); + +void rtl88eu_mon_recv_hook(struct net_device *dev, struct recv_frame *frame); +void rtl88eu_mon_xmit_hook(struct net_device *dev, struct xmit_frame *frame, + uint frag_len); + +#endif /* _MON_H_ */ 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);