diff options
Diffstat (limited to 'net')
200 files changed, 12753 insertions, 3246 deletions
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index ee070722a3a3..30ee4bc0f7cc 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -294,7 +294,7 @@ static void vlan_transfer_features(struct net_device *dev, else vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN; -#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) +#if IS_ENABLED(CONFIG_FCOE) vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid; #endif diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 402442402af7..4a6d31a082b9 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -409,7 +409,7 @@ static int vlan_dev_neigh_setup(struct net_device *dev, struct neigh_parms *pa) return err; } -#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) +#if IS_ENABLED(CONFIG_FCOE) static int vlan_dev_fcoe_ddp_setup(struct net_device *dev, u16 xid, struct scatterlist *sgl, unsigned int sgc) { @@ -531,6 +531,10 @@ static const struct header_ops vlan_header_ops = { .parse = eth_header_parse, }; +static struct device_type vlan_type = { + .name = "vlan", +}; + static const struct net_device_ops vlan_netdev_ops; static int vlan_dev_init(struct net_device *dev) @@ -564,7 +568,7 @@ static int vlan_dev_init(struct net_device *dev) if (is_zero_ether_addr(dev->broadcast)) memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len); -#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) +#if IS_ENABLED(CONFIG_FCOE) dev->fcoe_ddp_xid = real_dev->fcoe_ddp_xid; #endif @@ -579,6 +583,8 @@ static int vlan_dev_init(struct net_device *dev) dev->netdev_ops = &vlan_netdev_ops; + SET_NETDEV_DEVTYPE(dev, &vlan_type); + if (is_vlan_dev(real_dev)) subclass = 1; @@ -741,7 +747,7 @@ static const struct net_device_ops vlan_netdev_ops = { .ndo_do_ioctl = vlan_dev_ioctl, .ndo_neigh_setup = vlan_dev_neigh_setup, .ndo_get_stats64 = vlan_dev_get_stats64, -#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) +#if IS_ENABLED(CONFIG_FCOE) .ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup, .ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done, .ndo_fcoe_enable = vlan_dev_fcoe_enable, diff --git a/net/Makefile b/net/Makefile index 4f4ee083064c..e050d9d205e7 100644 --- a/net/Makefile +++ b/net/Makefile @@ -19,7 +19,7 @@ obj-$(CONFIG_NETFILTER) += netfilter/ obj-$(CONFIG_INET) += ipv4/ obj-$(CONFIG_XFRM) += xfrm/ obj-$(CONFIG_UNIX) += unix/ -obj-$(CONFIG_NET) += ipv6/ +obj-$(CONFIG_INET) += ipv6/ obj-$(CONFIG_PACKET) += packet/ obj-$(CONFIG_NET_KEY) += key/ obj-$(CONFIG_BRIDGE) += bridge/ diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig index 53f5244e28f8..250e0b58109c 100644 --- a/net/batman-adv/Kconfig +++ b/net/batman-adv/Kconfig @@ -25,6 +25,16 @@ config BATMAN_ADV_BLA more than one mesh node in the same LAN, you can safely remove this feature and save some space. +config BATMAN_ADV_DAT + bool "Distributed ARP Table" + depends on BATMAN_ADV && INET + default n + help + This option enables DAT (Distributed ARP Table), a DHT based + mechanism that increases ARP reliability on sparse wireless + mesh networks. If you think that your network does not need + this option you can safely remove it and save some space. + config BATMAN_ADV_DEBUG bool "B.A.T.M.A.N. debugging" depends on BATMAN_ADV diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile index 8676d2b1d574..e45e3b4e32e3 100644 --- a/net/batman-adv/Makefile +++ b/net/batman-adv/Makefile @@ -23,6 +23,7 @@ batman-adv-y += bat_iv_ogm.o batman-adv-y += bitarray.o batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o batman-adv-y += debugfs.o +batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o batman-adv-y += gateway_client.o batman-adv-y += gateway_common.o batman-adv-y += hard-interface.o diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index b02b75dae3a8..9f3925a85aab 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -57,20 +57,22 @@ out: static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface) { struct batadv_ogm_packet *batadv_ogm_packet; + unsigned char *ogm_buff; uint32_t random_seqno; int res = -ENOMEM; /* randomize initial seqno to avoid collision */ get_random_bytes(&random_seqno, sizeof(random_seqno)); - atomic_set(&hard_iface->seqno, random_seqno); + atomic_set(&hard_iface->bat_iv.ogm_seqno, random_seqno); - hard_iface->packet_len = BATADV_OGM_HLEN; - hard_iface->packet_buff = kmalloc(hard_iface->packet_len, GFP_ATOMIC); - - if (!hard_iface->packet_buff) + hard_iface->bat_iv.ogm_buff_len = BATADV_OGM_HLEN; + ogm_buff = kmalloc(hard_iface->bat_iv.ogm_buff_len, GFP_ATOMIC); + if (!ogm_buff) goto out; - batadv_ogm_packet = (struct batadv_ogm_packet *)hard_iface->packet_buff; + hard_iface->bat_iv.ogm_buff = ogm_buff; + + batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff; batadv_ogm_packet->header.packet_type = BATADV_IV_OGM; batadv_ogm_packet->header.version = BATADV_COMPAT_VERSION; batadv_ogm_packet->header.ttl = 2; @@ -87,15 +89,16 @@ out: static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface) { - kfree(hard_iface->packet_buff); - hard_iface->packet_buff = NULL; + kfree(hard_iface->bat_iv.ogm_buff); + hard_iface->bat_iv.ogm_buff = NULL; } static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface) { struct batadv_ogm_packet *batadv_ogm_packet; + unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff; - batadv_ogm_packet = (struct batadv_ogm_packet *)hard_iface->packet_buff; + batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff; memcpy(batadv_ogm_packet->orig, hard_iface->net_dev->dev_addr, ETH_ALEN); memcpy(batadv_ogm_packet->prev_sender, @@ -106,8 +109,9 @@ static void batadv_iv_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface) { struct batadv_ogm_packet *batadv_ogm_packet; + unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff; - batadv_ogm_packet = (struct batadv_ogm_packet *)hard_iface->packet_buff; + batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff; batadv_ogm_packet->flags = BATADV_PRIMARIES_FIRST_HOP; batadv_ogm_packet->header.ttl = BATADV_TTL; } @@ -407,9 +411,11 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, if ((atomic_read(&bat_priv->aggregated_ogms)) && (packet_len < BATADV_MAX_AGGREGATION_BYTES)) - skb_size = BATADV_MAX_AGGREGATION_BYTES + ETH_HLEN; + skb_size = BATADV_MAX_AGGREGATION_BYTES; else - skb_size = packet_len + ETH_HLEN; + skb_size = packet_len; + + skb_size += ETH_HLEN + NET_IP_ALIGN; forw_packet_aggr->skb = dev_alloc_skb(skb_size); if (!forw_packet_aggr->skb) { @@ -418,7 +424,7 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, kfree(forw_packet_aggr); goto out; } - skb_reserve(forw_packet_aggr->skb, ETH_HLEN); + skb_reserve(forw_packet_aggr->skb, ETH_HLEN + NET_IP_ALIGN); INIT_HLIST_NODE(&forw_packet_aggr->list); @@ -590,8 +596,10 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node, static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + unsigned char **ogm_buff = &hard_iface->bat_iv.ogm_buff; struct batadv_ogm_packet *batadv_ogm_packet; struct batadv_hard_iface *primary_if; + int *ogm_buff_len = &hard_iface->bat_iv.ogm_buff_len; int vis_server, tt_num_changes = 0; uint32_t seqno; uint8_t bandwidth; @@ -600,17 +608,16 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) primary_if = batadv_primary_if_get_selected(bat_priv); if (hard_iface == primary_if) - tt_num_changes = batadv_tt_append_diff(bat_priv, - &hard_iface->packet_buff, - &hard_iface->packet_len, + tt_num_changes = batadv_tt_append_diff(bat_priv, ogm_buff, + ogm_buff_len, BATADV_OGM_HLEN); - batadv_ogm_packet = (struct batadv_ogm_packet *)hard_iface->packet_buff; + batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff); /* change sequence number to network order */ - seqno = (uint32_t)atomic_read(&hard_iface->seqno); + seqno = (uint32_t)atomic_read(&hard_iface->bat_iv.ogm_seqno); batadv_ogm_packet->seqno = htonl(seqno); - atomic_inc(&hard_iface->seqno); + atomic_inc(&hard_iface->bat_iv.ogm_seqno); batadv_ogm_packet->ttvn = atomic_read(&bat_priv->tt.vn); batadv_ogm_packet->tt_crc = htons(bat_priv->tt.local_crc); @@ -631,8 +638,8 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) } batadv_slide_own_bcast_window(hard_iface); - batadv_iv_ogm_queue_add(bat_priv, hard_iface->packet_buff, - hard_iface->packet_len, hard_iface, 1, + batadv_iv_ogm_queue_add(bat_priv, hard_iface->bat_iv.ogm_buff, + hard_iface->bat_iv.ogm_buff_len, hard_iface, 1, batadv_iv_ogm_emit_send_time(bat_priv)); if (primary_if) @@ -1015,7 +1022,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, return; /* could be changed by schedule_own_packet() */ - if_incoming_seqno = atomic_read(&if_incoming->seqno); + if_incoming_seqno = atomic_read(&if_incoming->bat_iv.ogm_seqno); if (batadv_ogm_packet->flags & BATADV_DIRECTLINK) has_directlink_flag = 1; diff --git a/net/batman-adv/bitarray.c b/net/batman-adv/bitarray.c index aea174cdbfbd..5453b17d8df2 100644 --- a/net/batman-adv/bitarray.c +++ b/net/batman-adv/bitarray.c @@ -79,20 +79,17 @@ int batadv_bit_get_packet(void *priv, unsigned long *seq_bits, * or the old packet got delayed somewhere in the network. The * packet should be dropped without calling this function if the * seqno window is protected. + * + * seq_num_diff <= -BATADV_TQ_LOCAL_WINDOW_SIZE + * or + * seq_num_diff >= BATADV_EXPECTED_SEQNO_RANGE */ - if (seq_num_diff <= -BATADV_TQ_LOCAL_WINDOW_SIZE || - seq_num_diff >= BATADV_EXPECTED_SEQNO_RANGE) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Other host probably restarted!\n"); - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Other host probably restarted!\n"); - - bitmap_zero(seq_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); - if (set_mark) - batadv_set_bit(seq_bits, 0); - - return 1; - } + bitmap_zero(seq_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); + if (set_mark) + batadv_set_bit(seq_bits, 0); - /* never reached */ - return 0; + return 1; } diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index fd8d5afec0dd..bda8b1710806 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -40,15 +40,11 @@ static void batadv_bla_send_announce(struct batadv_priv *bat_priv, /* return the index of the claim */ static inline uint32_t batadv_choose_claim(const void *data, uint32_t size) { - const unsigned char *key = data; + struct batadv_claim *claim = (struct batadv_claim *)data; uint32_t hash = 0; - size_t i; - for (i = 0; i < ETH_ALEN + sizeof(short); i++) { - hash += key[i]; - hash += (hash << 10); - hash ^= (hash >> 6); - } + hash = batadv_hash_bytes(hash, &claim->addr, sizeof(claim->addr)); + hash = batadv_hash_bytes(hash, &claim->vid, sizeof(claim->vid)); hash += (hash << 3); hash ^= (hash >> 11); @@ -61,15 +57,11 @@ static inline uint32_t batadv_choose_claim(const void *data, uint32_t size) static inline uint32_t batadv_choose_backbone_gw(const void *data, uint32_t size) { - const unsigned char *key = data; + struct batadv_claim *claim = (struct batadv_claim *)data; uint32_t hash = 0; - size_t i; - for (i = 0; i < ETH_ALEN + sizeof(short); i++) { - hash += key[i]; - hash += (hash << 10); - hash ^= (hash >> 6); - } + hash = batadv_hash_bytes(hash, &claim->addr, sizeof(claim->addr)); + hash = batadv_hash_bytes(hash, &claim->vid, sizeof(claim->vid)); hash += (hash << 3); hash ^= (hash >> 11); @@ -362,7 +354,7 @@ out: */ static struct batadv_backbone_gw * batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig, - short vid) + short vid, bool own_backbone) { struct batadv_backbone_gw *entry; struct batadv_orig_node *orig_node; @@ -386,6 +378,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig, entry->crc = BATADV_BLA_CRC_INIT; entry->bat_priv = bat_priv; atomic_set(&entry->request_sent, 0); + atomic_set(&entry->wait_periods, 0); memcpy(entry->orig, orig, ETH_ALEN); /* one for the hash, one for returning */ @@ -409,6 +402,16 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig, "became a backbone gateway"); batadv_orig_node_free_ref(orig_node); } + + if (own_backbone) { + batadv_bla_send_announce(bat_priv, entry); + + /* this will be decreased in the worker thread */ + atomic_inc(&entry->request_sent); + atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS); + atomic_inc(&bat_priv->bla.num_requests); + } + return entry; } @@ -424,7 +427,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv, backbone_gw = batadv_bla_get_backbone_gw(bat_priv, primary_if->net_dev->dev_addr, - vid); + vid, true); if (unlikely(!backbone_gw)) return; @@ -632,7 +635,8 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv, if (memcmp(an_addr, batadv_announce_mac, 4) != 0) return 0; - backbone_gw = batadv_bla_get_backbone_gw(bat_priv, backbone_addr, vid); + backbone_gw = batadv_bla_get_backbone_gw(bat_priv, backbone_addr, vid, + false); if (unlikely(!backbone_gw)) return 1; @@ -730,7 +734,8 @@ static int batadv_handle_claim(struct batadv_priv *bat_priv, /* register the gateway if not yet available, and add the claim. */ - backbone_gw = batadv_bla_get_backbone_gw(bat_priv, backbone_addr, vid); + backbone_gw = batadv_bla_get_backbone_gw(bat_priv, backbone_addr, vid, + false); if (unlikely(!backbone_gw)) return 1; @@ -1140,6 +1145,24 @@ static void batadv_bla_periodic_work(struct work_struct *work) backbone_gw->lasttime = jiffies; batadv_bla_send_announce(bat_priv, backbone_gw); + + /* request_sent is only set after creation to avoid + * problems when we are not yet known as backbone gw + * in the backbone. + * + * We can reset this now after we waited some periods + * to give bridge forward delays and bla group forming + * some grace time. + */ + + if (atomic_read(&backbone_gw->request_sent) == 0) + continue; + + if (!atomic_dec_and_test(&backbone_gw->wait_periods)) + continue; + + atomic_dec(&backbone_gw->bat_priv->bla.num_requests); + atomic_set(&backbone_gw->request_sent, 0); } rcu_read_unlock(); } @@ -1585,23 +1608,11 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset) struct hlist_head *head; uint32_t i; bool is_own; - int ret = 0; uint8_t *primary_addr; - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - please specify interfaces to enable it\n", - net_dev->name); - goto out; - } - - if (primary_if->if_status != BATADV_IF_ACTIVE) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - primary interface not active\n", - net_dev->name); + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) goto out; - } primary_addr = primary_if->net_dev->dev_addr; seq_printf(seq, @@ -1628,7 +1639,7 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset) out: if (primary_if) batadv_hardif_free_ref(primary_if); - return ret; + return 0; } int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset) @@ -1643,23 +1654,11 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset) int secs, msecs; uint32_t i; bool is_own; - int ret = 0; uint8_t *primary_addr; - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - please specify interfaces to enable it\n", - net_dev->name); - goto out; - } - - if (primary_if->if_status != BATADV_IF_ACTIVE) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - primary interface not active\n", - net_dev->name); + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) goto out; - } primary_addr = primary_if->net_dev->dev_addr; seq_printf(seq, @@ -1693,5 +1692,5 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset) out: if (primary_if) batadv_hardif_free_ref(primary_if); - return ret; + return 0; } diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c index 391d4fb2026f..3f679cb2d0e2 100644 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@ -31,6 +31,7 @@ #include "vis.h" #include "icmp_socket.h" #include "bridge_loop_avoidance.h" +#include "distributed-arp-table.h" static struct dentry *batadv_debugfs; @@ -99,15 +100,17 @@ int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...) static int batadv_log_open(struct inode *inode, struct file *file) { + if (!try_module_get(THIS_MODULE)) + return -EBUSY; + nonseekable_open(inode, file); file->private_data = inode->i_private; - batadv_inc_module_count(); return 0; } static int batadv_log_release(struct inode *inode, struct file *file) { - batadv_dec_module_count(); + module_put(THIS_MODULE); return 0; } @@ -278,6 +281,19 @@ static int batadv_bla_backbone_table_open(struct inode *inode, #endif +#ifdef CONFIG_BATMAN_ADV_DAT +/** + * batadv_dat_cache_open - Prepare file handler for reads from dat_chache + * @inode: inode which was opened + * @file: file handle to be initialized + */ +static int batadv_dat_cache_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, batadv_dat_cache_seq_print_text, net_dev); +} +#endif + static int batadv_transtable_local_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; @@ -317,6 +333,9 @@ static BATADV_DEBUGINFO(bla_claim_table, S_IRUGO, batadv_bla_claim_table_open); static BATADV_DEBUGINFO(bla_backbone_table, S_IRUGO, batadv_bla_backbone_table_open); #endif +#ifdef CONFIG_BATMAN_ADV_DAT +static BATADV_DEBUGINFO(dat_cache, S_IRUGO, batadv_dat_cache_open); +#endif static BATADV_DEBUGINFO(transtable_local, S_IRUGO, batadv_transtable_local_open); static BATADV_DEBUGINFO(vis_data, S_IRUGO, batadv_vis_data_open); @@ -329,6 +348,9 @@ static struct batadv_debuginfo *batadv_mesh_debuginfos[] = { &batadv_debuginfo_bla_claim_table, &batadv_debuginfo_bla_backbone_table, #endif +#ifdef CONFIG_BATMAN_ADV_DAT + &batadv_debuginfo_dat_cache, +#endif &batadv_debuginfo_transtable_local, &batadv_debuginfo_vis_data, NULL, diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c new file mode 100644 index 000000000000..8e1d89d2b1c1 --- /dev/null +++ b/net/batman-adv/distributed-arp-table.c @@ -0,0 +1,1066 @@ +/* Copyright (C) 2011-2012 B.A.T.M.A.N. contributors: + * + * Antonio Quartulli + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include <linux/if_ether.h> +#include <linux/if_arp.h> +#include <net/arp.h> + +#include "main.h" +#include "hash.h" +#include "distributed-arp-table.h" +#include "hard-interface.h" +#include "originator.h" +#include "send.h" +#include "types.h" +#include "translation-table.h" +#include "unicast.h" + +static void batadv_dat_purge(struct work_struct *work); + +/** + * batadv_dat_start_timer - initialise the DAT periodic worker + * @bat_priv: the bat priv with all the soft interface information + */ +static void batadv_dat_start_timer(struct batadv_priv *bat_priv) +{ + INIT_DELAYED_WORK(&bat_priv->dat.work, batadv_dat_purge); + queue_delayed_work(batadv_event_workqueue, &bat_priv->dat.work, + msecs_to_jiffies(10000)); +} + +/** + * batadv_dat_entry_free_ref - decrements the dat_entry refcounter and possibly + * free it + * @dat_entry: the oentry to free + */ +static void batadv_dat_entry_free_ref(struct batadv_dat_entry *dat_entry) +{ + if (atomic_dec_and_test(&dat_entry->refcount)) + kfree_rcu(dat_entry, rcu); +} + +/** + * batadv_dat_to_purge - checks whether a dat_entry has to be purged or not + * @dat_entry: the entry to check + * + * Returns true if the entry has to be purged now, false otherwise + */ +static bool batadv_dat_to_purge(struct batadv_dat_entry *dat_entry) +{ + return batadv_has_timed_out(dat_entry->last_update, + BATADV_DAT_ENTRY_TIMEOUT); +} + +/** + * __batadv_dat_purge - delete entries from the DAT local storage + * @bat_priv: the bat priv with all the soft interface information + * @to_purge: function in charge to decide whether an entry has to be purged or + * not. This function takes the dat_entry as argument and has to + * returns a boolean value: true is the entry has to be deleted, + * false otherwise + * + * Loops over each entry in the DAT local storage and delete it if and only if + * the to_purge function passed as argument returns true + */ +static void __batadv_dat_purge(struct batadv_priv *bat_priv, + bool (*to_purge)(struct batadv_dat_entry *)) +{ + spinlock_t *list_lock; /* protects write access to the hash lists */ + struct batadv_dat_entry *dat_entry; + struct hlist_node *node, *node_tmp; + struct hlist_head *head; + uint32_t i; + + if (!bat_priv->dat.hash) + return; + + for (i = 0; i < bat_priv->dat.hash->size; i++) { + head = &bat_priv->dat.hash->table[i]; + list_lock = &bat_priv->dat.hash->list_locks[i]; + + spin_lock_bh(list_lock); + hlist_for_each_entry_safe(dat_entry, node, node_tmp, head, + hash_entry) { + /* if an helper function has been passed as parameter, + * ask it if the entry has to be purged or not + */ + if (to_purge && !to_purge(dat_entry)) + continue; + + hlist_del_rcu(node); + batadv_dat_entry_free_ref(dat_entry); + } + spin_unlock_bh(list_lock); + } +} + +/** + * batadv_dat_purge - periodic task that deletes old entries from the local DAT + * hash table + * @work: kernel work struct + */ +static void batadv_dat_purge(struct work_struct *work) +{ + struct delayed_work *delayed_work; + struct batadv_priv_dat *priv_dat; + struct batadv_priv *bat_priv; + + delayed_work = container_of(work, struct delayed_work, work); + priv_dat = container_of(delayed_work, struct batadv_priv_dat, work); + bat_priv = container_of(priv_dat, struct batadv_priv, dat); + + __batadv_dat_purge(bat_priv, batadv_dat_to_purge); + batadv_dat_start_timer(bat_priv); +} + +/** + * batadv_compare_dat - comparing function used in the local DAT hash table + * @node: node in the local table + * @data2: second object to compare the node to + * + * Returns 1 if the two entry are the same, 0 otherwise + */ +static int batadv_compare_dat(const struct hlist_node *node, const void *data2) +{ + const void *data1 = container_of(node, struct batadv_dat_entry, + hash_entry); + + return (memcmp(data1, data2, sizeof(__be32)) == 0 ? 1 : 0); +} + +/** + * batadv_arp_hw_src - extract the hw_src field from an ARP packet + * @skb: ARP packet + * @hdr_size: size of the possible header before the ARP packet + * + * Returns the value of the hw_src field in the ARP packet + */ +static uint8_t *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size) +{ + uint8_t *addr; + + addr = (uint8_t *)(skb->data + hdr_size); + addr += ETH_HLEN + sizeof(struct arphdr); + + return addr; +} + +/** + * batadv_arp_ip_src - extract the ip_src field from an ARP packet + * @skb: ARP packet + * @hdr_size: size of the possible header before the ARP packet + * + * Returns the value of the ip_src field in the ARP packet + */ +static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size) +{ + return *(__be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN); +} + +/** + * batadv_arp_hw_dst - extract the hw_dst field from an ARP packet + * @skb: ARP packet + * @hdr_size: size of the possible header before the ARP packet + * + * Returns the value of the hw_dst field in the ARP packet + */ +static uint8_t *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size) +{ + return batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN + 4; +} + +/** + * batadv_arp_ip_dst - extract the ip_dst field from an ARP packet + * @skb: ARP packet + * @hdr_size: size of the possible header before the ARP packet + * + * Returns the value of the ip_dst field in the ARP packet + */ +static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size) +{ + return *(__be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN * 2 + 4); +} + +/** + * batadv_hash_dat - compute the hash value for an IP address + * @data: data to hash + * @size: size of the hash table + * + * Returns the selected index in the hash table for the given data + */ +static uint32_t batadv_hash_dat(const void *data, uint32_t size) +{ + const unsigned char *key = data; + uint32_t hash = 0; + size_t i; + + for (i = 0; i < 4; i++) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash % size; +} + +/** + * batadv_dat_entry_hash_find - looks for a given dat_entry in the local hash + * table + * @bat_priv: the bat priv with all the soft interface information + * @ip: search key + * + * Returns the dat_entry if found, NULL otherwise + */ +static struct batadv_dat_entry * +batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip) +{ + struct hlist_head *head; + struct hlist_node *node; + struct batadv_dat_entry *dat_entry, *dat_entry_tmp = NULL; + struct batadv_hashtable *hash = bat_priv->dat.hash; + uint32_t index; + + if (!hash) + return NULL; + + index = batadv_hash_dat(&ip, hash->size); + head = &hash->table[index]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(dat_entry, node, head, hash_entry) { + if (dat_entry->ip != ip) + continue; + + if (!atomic_inc_not_zero(&dat_entry->refcount)) + continue; + + dat_entry_tmp = dat_entry; + break; + } + rcu_read_unlock(); + + return dat_entry_tmp; +} + +/** + * batadv_dat_entry_add - add a new dat entry or update it if already exists + * @bat_priv: the bat priv with all the soft interface information + * @ip: ipv4 to add/edit + * @mac_addr: mac address to assign to the given ipv4 + */ +static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip, + uint8_t *mac_addr) +{ + struct batadv_dat_entry *dat_entry; + int hash_added; + + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip); + /* if this entry is already known, just update it */ + if (dat_entry) { + if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr)) + memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN); + dat_entry->last_update = jiffies; + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "Entry updated: %pI4 %pM\n", &dat_entry->ip, + dat_entry->mac_addr); + goto out; + } + + dat_entry = kmalloc(sizeof(*dat_entry), GFP_ATOMIC); + if (!dat_entry) + goto out; + + dat_entry->ip = ip; + memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN); + dat_entry->last_update = jiffies; + atomic_set(&dat_entry->refcount, 2); + + hash_added = batadv_hash_add(bat_priv->dat.hash, batadv_compare_dat, + batadv_hash_dat, &dat_entry->ip, + &dat_entry->hash_entry); + + if (unlikely(hash_added != 0)) { + /* remove the reference for the hash */ + batadv_dat_entry_free_ref(dat_entry); + goto out; + } + + batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM\n", + &dat_entry->ip, dat_entry->mac_addr); + +out: + if (dat_entry) + batadv_dat_entry_free_ref(dat_entry); +} + +#ifdef CONFIG_BATMAN_ADV_DEBUG + +/** + * batadv_dbg_arp - print a debug message containing all the ARP packet details + * @bat_priv: the bat priv with all the soft interface information + * @skb: ARP packet + * @type: ARP type + * @hdr_size: size of the possible header before the ARP packet + * @msg: message to print together with the debugging information + */ +static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb, + uint16_t type, int hdr_size, char *msg) +{ + struct batadv_unicast_4addr_packet *unicast_4addr_packet; + struct batadv_bcast_packet *bcast_pkt; + uint8_t *orig_addr; + __be32 ip_src, ip_dst; + + if (msg) + batadv_dbg(BATADV_DBG_DAT, bat_priv, "%s\n", msg); + + ip_src = batadv_arp_ip_src(skb, hdr_size); + ip_dst = batadv_arp_ip_dst(skb, hdr_size); + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]\n", + batadv_arp_hw_src(skb, hdr_size), &ip_src, + batadv_arp_hw_dst(skb, hdr_size), &ip_dst); + + if (hdr_size == 0) + return; + + /* if the ARP packet is encapsulated in a batman packet, let's print + * some debug messages + */ + unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data; + + switch (unicast_4addr_packet->u.header.packet_type) { + case BATADV_UNICAST: + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "* encapsulated within a UNICAST packet\n"); + break; + case BATADV_UNICAST_4ADDR: + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "* encapsulated within a UNICAST_4ADDR packet (src: %pM)\n", + unicast_4addr_packet->src); + switch (unicast_4addr_packet->subtype) { + case BATADV_P_DAT_DHT_PUT: + batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: DAT_DHT_PUT\n"); + break; + case BATADV_P_DAT_DHT_GET: + batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: DAT_DHT_GET\n"); + break; + case BATADV_P_DAT_CACHE_REPLY: + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "* type: DAT_CACHE_REPLY\n"); + break; + case BATADV_P_DATA: + batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: DATA\n"); + break; + default: + batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: Unknown (%u)!\n", + unicast_4addr_packet->u.header.packet_type); + } + break; + case BATADV_BCAST: + bcast_pkt = (struct batadv_bcast_packet *)unicast_4addr_packet; + orig_addr = bcast_pkt->orig; + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "* encapsulated within a BCAST packet (src: %pM)\n", + orig_addr); + break; + default: + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "* encapsulated within an unknown packet type (0x%x)\n", + unicast_4addr_packet->u.header.packet_type); + } +} + +#else + +static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb, + uint16_t type, int hdr_size, char *msg) +{ +} + +#endif /* CONFIG_BATMAN_ADV_DEBUG */ + +/** + * batadv_is_orig_node_eligible - check whether a node can be a DHT candidate + * @res: the array with the already selected candidates + * @select: number of already selected candidates + * @tmp_max: address of the currently evaluated node + * @max: current round max address + * @last_max: address of the last selected candidate + * @candidate: orig_node under evaluation + * @max_orig_node: last selected candidate + * + * Returns true if the node has been elected as next candidate or false othrwise + */ +static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res, + int select, batadv_dat_addr_t tmp_max, + batadv_dat_addr_t max, + batadv_dat_addr_t last_max, + struct batadv_orig_node *candidate, + struct batadv_orig_node *max_orig_node) +{ + bool ret = false; + int j; + + /* Check if this node has already been selected... */ + for (j = 0; j < select; j++) + if (res[j].orig_node == candidate) + break; + /* ..and possibly skip it */ + if (j < select) + goto out; + /* sanity check: has it already been selected? This should not happen */ + if (tmp_max > last_max) + goto out; + /* check if during this iteration an originator with a closer dht + * address has already been found + */ + if (tmp_max < max) + goto out; + /* this is an hash collision with the temporary selected node. Choose + * the one with the lowest address + */ + if ((tmp_max == max) && + (batadv_compare_eth(candidate->orig, max_orig_node->orig) > 0)) + goto out; + + ret = true; +out: + return ret; +} + +/** + * batadv_choose_next_candidate - select the next DHT candidate + * @bat_priv: the bat priv with all the soft interface information + * @cands: candidates array + * @select: number of candidates already present in the array + * @ip_key: key to look up in the DHT + * @last_max: pointer where the address of the selected candidate will be saved + */ +static void batadv_choose_next_candidate(struct batadv_priv *bat_priv, + struct batadv_dat_candidate *cands, + int select, batadv_dat_addr_t ip_key, + batadv_dat_addr_t *last_max) +{ + batadv_dat_addr_t max = 0, tmp_max = 0; + struct batadv_orig_node *orig_node, *max_orig_node = NULL; + struct batadv_hashtable *hash = bat_priv->orig_hash; + struct hlist_node *node; + struct hlist_head *head; + int i; + + /* if no node is eligible as candidate, leave the candidate type as + * NOT_FOUND + */ + cands[select].type = BATADV_DAT_CANDIDATE_NOT_FOUND; + + /* iterate over the originator list and find the node with closest + * dat_address which has not been selected yet + */ + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { + /* the dht space is a ring and addresses are unsigned */ + tmp_max = BATADV_DAT_ADDR_MAX - orig_node->dat_addr + + ip_key; + + if (!batadv_is_orig_node_eligible(cands, select, + tmp_max, max, + *last_max, orig_node, + max_orig_node)) + continue; + + if (!atomic_inc_not_zero(&orig_node->refcount)) + continue; + + max = tmp_max; + if (max_orig_node) + batadv_orig_node_free_ref(max_orig_node); + max_orig_node = orig_node; + } + rcu_read_unlock(); + } + if (max_orig_node) { + cands[select].type = BATADV_DAT_CANDIDATE_ORIG; + cands[select].orig_node = max_orig_node; + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "dat_select_candidates() %d: selected %pM addr=%u dist=%u\n", + select, max_orig_node->orig, max_orig_node->dat_addr, + max); + } + *last_max = max; +} + +/** + * batadv_dat_select_candidates - selects the nodes which the DHT message has to + * be sent to + * @bat_priv: the bat priv with all the soft interface information + * @ip_dst: ipv4 to look up in the DHT + * + * An originator O is selected if and only if its DHT_ID value is one of three + * closest values (from the LEFT, with wrap around if needed) then the hash + * value of the key. ip_dst is the key. + * + * Returns the candidate array of size BATADV_DAT_CANDIDATE_NUM + */ +static struct batadv_dat_candidate * +batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst) +{ + int select; + batadv_dat_addr_t last_max = BATADV_DAT_ADDR_MAX, ip_key; + struct batadv_dat_candidate *res; + + if (!bat_priv->orig_hash) + return NULL; + + res = kmalloc(BATADV_DAT_CANDIDATES_NUM * sizeof(*res), GFP_ATOMIC); + if (!res) + return NULL; + + ip_key = (batadv_dat_addr_t)batadv_hash_dat(&ip_dst, + BATADV_DAT_ADDR_MAX); + + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "dat_select_candidates(): IP=%pI4 hash(IP)=%u\n", &ip_dst, + ip_key); + + for (select = 0; select < BATADV_DAT_CANDIDATES_NUM; select++) + batadv_choose_next_candidate(bat_priv, res, select, ip_key, + &last_max); + + return res; +} + +/** + * batadv_dat_send_data - send a payload to the selected candidates + * @bat_priv: the bat priv with all the soft interface information + * @skb: payload to send + * @ip: the DHT key + * @packet_subtype: unicast4addr packet subtype to use + * + * In this function the skb is copied by means of pskb_copy() and is sent as + * unicast packet to each of the selected candidates + * + * Returns true if the packet is sent to at least one candidate, false otherwise + */ +static bool batadv_dat_send_data(struct batadv_priv *bat_priv, + struct sk_buff *skb, __be32 ip, + int packet_subtype) +{ + int i; + bool ret = false; + int send_status; + struct batadv_neigh_node *neigh_node = NULL; + struct sk_buff *tmp_skb; + struct batadv_dat_candidate *cand; + + cand = batadv_dat_select_candidates(bat_priv, ip); + if (!cand) + goto out; + + batadv_dbg(BATADV_DBG_DAT, bat_priv, "DHT_SEND for %pI4\n", &ip); + + for (i = 0; i < BATADV_DAT_CANDIDATES_NUM; i++) { + if (cand[i].type == BATADV_DAT_CANDIDATE_NOT_FOUND) + continue; + + neigh_node = batadv_orig_node_get_router(cand[i].orig_node); + if (!neigh_node) + goto free_orig; + + tmp_skb = pskb_copy(skb, GFP_ATOMIC); + if (!batadv_unicast_4addr_prepare_skb(bat_priv, tmp_skb, + cand[i].orig_node, + packet_subtype)) { + kfree_skb(tmp_skb); + goto free_neigh; + } + + send_status = batadv_send_skb_packet(tmp_skb, + neigh_node->if_incoming, + neigh_node->addr); + if (send_status == NET_XMIT_SUCCESS) { + /* count the sent packet */ + switch (packet_subtype) { + case BATADV_P_DAT_DHT_GET: + batadv_inc_counter(bat_priv, + BATADV_CNT_DAT_GET_TX); + break; + case BATADV_P_DAT_DHT_PUT: + batadv_inc_counter(bat_priv, + BATADV_CNT_DAT_PUT_TX); + break; + } + + /* packet sent to a candidate: return true */ + ret = true; + } +free_neigh: + batadv_neigh_node_free_ref(neigh_node); +free_orig: + batadv_orig_node_free_ref(cand[i].orig_node); + } + +out: + kfree(cand); + return ret; +} + +/** + * batadv_dat_hash_free - free the local DAT hash table + * @bat_priv: the bat priv with all the soft interface information + */ +static void batadv_dat_hash_free(struct batadv_priv *bat_priv) +{ + if (!bat_priv->dat.hash) + return; + + __batadv_dat_purge(bat_priv, NULL); + + batadv_hash_destroy(bat_priv->dat.hash); + + bat_priv->dat.hash = NULL; +} + +/** + * batadv_dat_init - initialise the DAT internals + * @bat_priv: the bat priv with all the soft interface information + */ +int batadv_dat_init(struct batadv_priv *bat_priv) +{ + if (bat_priv->dat.hash) + return 0; + + bat_priv->dat.hash = batadv_hash_new(1024); + + if (!bat_priv->dat.hash) + return -ENOMEM; + + batadv_dat_start_timer(bat_priv); + + return 0; +} + +/** + * batadv_dat_free - free the DAT internals + * @bat_priv: the bat priv with all the soft interface information + */ +void batadv_dat_free(struct batadv_priv *bat_priv) +{ + cancel_delayed_work_sync(&bat_priv->dat.work); + + batadv_dat_hash_free(bat_priv); +} + +/** + * batadv_dat_cache_seq_print_text - print the local DAT hash table + * @seq: seq file to print on + * @offset: not used + */ +int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct batadv_priv *bat_priv = netdev_priv(net_dev); + struct batadv_hashtable *hash = bat_priv->dat.hash; + struct batadv_dat_entry *dat_entry; + struct batadv_hard_iface *primary_if; + struct hlist_node *node; + struct hlist_head *head; + unsigned long last_seen_jiffies; + int last_seen_msecs, last_seen_secs, last_seen_mins; + uint32_t i; + + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) + goto out; + + seq_printf(seq, "Distributed ARP Table (%s):\n", net_dev->name); + seq_printf(seq, " %-7s %-13s %5s\n", "IPv4", "MAC", + "last-seen"); + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(dat_entry, node, head, hash_entry) { + last_seen_jiffies = jiffies - dat_entry->last_update; + last_seen_msecs = jiffies_to_msecs(last_seen_jiffies); + last_seen_mins = last_seen_msecs / 60000; + last_seen_msecs = last_seen_msecs % 60000; + last_seen_secs = last_seen_msecs / 1000; + + seq_printf(seq, " * %15pI4 %14pM %6i:%02i\n", + &dat_entry->ip, dat_entry->mac_addr, + last_seen_mins, last_seen_secs); + } + rcu_read_unlock(); + } + +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + return 0; +} + +/** + * batadv_arp_get_type - parse an ARP packet and gets the type + * @bat_priv: the bat priv with all the soft interface information + * @skb: packet to analyse + * @hdr_size: size of the possible header before the ARP packet in the skb + * + * Returns the ARP type if the skb contains a valid ARP packet, 0 otherwise + */ +static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size) +{ + struct arphdr *arphdr; + struct ethhdr *ethhdr; + __be32 ip_src, ip_dst; + uint16_t type = 0; + + /* pull the ethernet header */ + if (unlikely(!pskb_may_pull(skb, hdr_size + ETH_HLEN))) + goto out; + + ethhdr = (struct ethhdr *)(skb->data + hdr_size); + + if (ethhdr->h_proto != htons(ETH_P_ARP)) + goto out; + + /* pull the ARP payload */ + if (unlikely(!pskb_may_pull(skb, hdr_size + ETH_HLEN + + arp_hdr_len(skb->dev)))) + goto out; + + arphdr = (struct arphdr *)(skb->data + hdr_size + ETH_HLEN); + + /* Check whether the ARP packet carries a valid + * IP information + */ + if (arphdr->ar_hrd != htons(ARPHRD_ETHER)) + goto out; + + if (arphdr->ar_pro != htons(ETH_P_IP)) + goto out; + + if (arphdr->ar_hln != ETH_ALEN) + goto out; + + if (arphdr->ar_pln != 4) + goto out; + + /* Check for bad reply/request. If the ARP message is not sane, DAT + * will simply ignore it + */ + ip_src = batadv_arp_ip_src(skb, hdr_size); + ip_dst = batadv_arp_ip_dst(skb, hdr_size); + if (ipv4_is_loopback(ip_src) || ipv4_is_multicast(ip_src) || + ipv4_is_loopback(ip_dst) || ipv4_is_multicast(ip_dst)) + goto out; + + type = ntohs(arphdr->ar_op); +out: + return type; +} + +/** + * batadv_dat_snoop_outgoing_arp_request - snoop the ARP request and try to + * answer using DAT + * @bat_priv: the bat priv with all the soft interface information + * @skb: packet to check + * + * Returns true if the message has been sent to the dht candidates, false + * otherwise. In case of true the message has to be enqueued to permit the + * fallback + */ +bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, + struct sk_buff *skb) +{ + uint16_t type = 0; + __be32 ip_dst, ip_src; + uint8_t *hw_src; + bool ret = false; + struct batadv_dat_entry *dat_entry = NULL; + struct sk_buff *skb_new; + struct batadv_hard_iface *primary_if = NULL; + + if (!atomic_read(&bat_priv->distributed_arp_table)) + goto out; + + type = batadv_arp_get_type(bat_priv, skb, 0); + /* If the node gets an ARP_REQUEST it has to send a DHT_GET unicast + * message to the selected DHT candidates + */ + if (type != ARPOP_REQUEST) + goto out; + + batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REQUEST"); + + ip_src = batadv_arp_ip_src(skb, 0); + hw_src = batadv_arp_hw_src(skb, 0); + ip_dst = batadv_arp_ip_dst(skb, 0); + + batadv_dat_entry_add(bat_priv, ip_src, hw_src); + + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst); + if (dat_entry) { + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src, + primary_if->soft_iface, ip_dst, hw_src, + dat_entry->mac_addr, hw_src); + if (!skb_new) + goto out; + + skb_reset_mac_header(skb_new); + skb_new->protocol = eth_type_trans(skb_new, + primary_if->soft_iface); + bat_priv->stats.rx_packets++; + bat_priv->stats.rx_bytes += skb->len + ETH_HLEN; + primary_if->soft_iface->last_rx = jiffies; + + netif_rx(skb_new); + batadv_dbg(BATADV_DBG_DAT, bat_priv, "ARP request replied locally\n"); + ret = true; + } else { + /* Send the request on the DHT */ + ret = batadv_dat_send_data(bat_priv, skb, ip_dst, + BATADV_P_DAT_DHT_GET); + } +out: + if (dat_entry) + batadv_dat_entry_free_ref(dat_entry); + if (primary_if) + batadv_hardif_free_ref(primary_if); + return ret; +} + +/** + * batadv_dat_snoop_incoming_arp_request - snoop the ARP request and try to + * answer using the local DAT storage + * @bat_priv: the bat priv with all the soft interface information + * @skb: packet to check + * @hdr_size: size of the encapsulation header + * + * Returns true if the request has been answered, false otherwise + */ +bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size) +{ + uint16_t type; + __be32 ip_src, ip_dst; + uint8_t *hw_src; + struct sk_buff *skb_new; + struct batadv_hard_iface *primary_if = NULL; + struct batadv_dat_entry *dat_entry = NULL; + bool ret = false; + int err; + + if (!atomic_read(&bat_priv->distributed_arp_table)) + goto out; + + type = batadv_arp_get_type(bat_priv, skb, hdr_size); + if (type != ARPOP_REQUEST) + goto out; + + hw_src = batadv_arp_hw_src(skb, hdr_size); + ip_src = batadv_arp_ip_src(skb, hdr_size); + ip_dst = batadv_arp_ip_dst(skb, hdr_size); + + batadv_dbg_arp(bat_priv, skb, type, hdr_size, + "Parsing incoming ARP REQUEST"); + + batadv_dat_entry_add(bat_priv, ip_src, hw_src); + + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst); + if (!dat_entry) + goto out; + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src, + primary_if->soft_iface, ip_dst, hw_src, + dat_entry->mac_addr, hw_src); + + if (!skb_new) + goto out; + + /* to preserve backwards compatibility, here the node has to answer + * using the same packet type it received for the request. This is due + * to that if a node is not using the 4addr packet format it may not + * support it. + */ + if (hdr_size == sizeof(struct batadv_unicast_4addr_packet)) + err = batadv_unicast_4addr_send_skb(bat_priv, skb_new, + BATADV_P_DAT_CACHE_REPLY); + else + err = batadv_unicast_send_skb(bat_priv, skb_new); + + if (!err) { + batadv_inc_counter(bat_priv, BATADV_CNT_DAT_CACHED_REPLY_TX); + ret = true; + } +out: + if (dat_entry) + batadv_dat_entry_free_ref(dat_entry); + if (primary_if) + batadv_hardif_free_ref(primary_if); + if (ret) + kfree_skb(skb); + return ret; +} + +/** + * batadv_dat_snoop_outgoing_arp_reply - snoop the ARP reply and fill the DHT + * @bat_priv: the bat priv with all the soft interface information + * @skb: packet to check + */ +void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, + struct sk_buff *skb) +{ + uint16_t type; + __be32 ip_src, ip_dst; + uint8_t *hw_src, *hw_dst; + + if (!atomic_read(&bat_priv->distributed_arp_table)) + return; + + type = batadv_arp_get_type(bat_priv, skb, 0); + if (type != ARPOP_REPLY) + return; + + batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REPLY"); + + hw_src = batadv_arp_hw_src(skb, 0); + ip_src = batadv_arp_ip_src(skb, 0); + hw_dst = batadv_arp_hw_dst(skb, 0); + ip_dst = batadv_arp_ip_dst(skb, 0); + + batadv_dat_entry_add(bat_priv, ip_src, hw_src); + batadv_dat_entry_add(bat_priv, ip_dst, hw_dst); + + /* Send the ARP reply to the candidates for both the IP addresses that + * the node got within the ARP reply + */ + batadv_dat_send_data(bat_priv, skb, ip_src, BATADV_P_DAT_DHT_PUT); + batadv_dat_send_data(bat_priv, skb, ip_dst, BATADV_P_DAT_DHT_PUT); +} +/** + * batadv_dat_snoop_incoming_arp_reply - snoop the ARP reply and fill the local + * DAT storage only + * @bat_priv: the bat priv with all the soft interface information + * @skb: packet to check + * @hdr_size: siaze of the encapsulation header + */ +bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size) +{ + uint16_t type; + __be32 ip_src, ip_dst; + uint8_t *hw_src, *hw_dst; + bool ret = false; + + if (!atomic_read(&bat_priv->distributed_arp_table)) + goto out; + + type = batadv_arp_get_type(bat_priv, skb, hdr_size); + if (type != ARPOP_REPLY) + goto out; + + batadv_dbg_arp(bat_priv, skb, type, hdr_size, + "Parsing incoming ARP REPLY"); + + hw_src = batadv_arp_hw_src(skb, hdr_size); + ip_src = batadv_arp_ip_src(skb, hdr_size); + hw_dst = batadv_arp_hw_dst(skb, hdr_size); + ip_dst = batadv_arp_ip_dst(skb, hdr_size); + + /* Update our internal cache with both the IP addresses the node got + * within the ARP reply + */ + batadv_dat_entry_add(bat_priv, ip_src, hw_src); + batadv_dat_entry_add(bat_priv, ip_dst, hw_dst); + + /* if this REPLY is directed to a client of mine, let's deliver the + * packet to the interface + */ + ret = !batadv_is_my_client(bat_priv, hw_dst); +out: + /* if ret == false -> packet has to be delivered to the interface */ + return ret; +} + +/** + * batadv_dat_drop_broadcast_packet - check if an ARP request has to be dropped + * (because the node has already got the reply via DAT) or not + * @bat_priv: the bat priv with all the soft interface information + * @forw_packet: the broadcast packet + * + * Returns true if the node can drop the packet, false otherwise + */ +bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, + struct batadv_forw_packet *forw_packet) +{ + uint16_t type; + __be32 ip_dst; + struct batadv_dat_entry *dat_entry = NULL; + bool ret = false; + const size_t bcast_len = sizeof(struct batadv_bcast_packet); + + if (!atomic_read(&bat_priv->distributed_arp_table)) + goto out; + + /* If this packet is an ARP_REQUEST and the node already has the + * information that it is going to ask, then the packet can be dropped + */ + if (forw_packet->num_packets) + goto out; + + type = batadv_arp_get_type(bat_priv, forw_packet->skb, bcast_len); + if (type != ARPOP_REQUEST) + goto out; + + ip_dst = batadv_arp_ip_dst(forw_packet->skb, bcast_len); + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst); + /* check if the node already got this entry */ + if (!dat_entry) { + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "ARP Request for %pI4: fallback\n", &ip_dst); + goto out; + } + + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "ARP Request for %pI4: fallback prevented\n", &ip_dst); + ret = true; + +out: + if (dat_entry) + batadv_dat_entry_free_ref(dat_entry); + return ret; +} diff --git a/net/batman-adv/distributed-arp-table.h b/net/batman-adv/distributed-arp-table.h new file mode 100644 index 000000000000..d060c033e7de --- /dev/null +++ b/net/batman-adv/distributed-arp-table.h @@ -0,0 +1,167 @@ +/* Copyright (C) 2011-2012 B.A.T.M.A.N. contributors: + * + * Antonio Quartulli + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _NET_BATMAN_ADV_ARP_H_ +#define _NET_BATMAN_ADV_ARP_H_ + +#ifdef CONFIG_BATMAN_ADV_DAT + +#include "types.h" +#include "originator.h" + +#include <linux/if_arp.h> + +#define BATADV_DAT_ADDR_MAX ((batadv_dat_addr_t)~(batadv_dat_addr_t)0) + +bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, + struct sk_buff *skb); +bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size); +void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, + struct sk_buff *skb); +bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size); +bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, + struct batadv_forw_packet *forw_packet); + +/** + * batadv_dat_init_orig_node_addr - assign a DAT address to the orig_node + * @orig_node: the node to assign the DAT address to + */ +static inline void +batadv_dat_init_orig_node_addr(struct batadv_orig_node *orig_node) +{ + uint32_t addr; + + addr = batadv_choose_orig(orig_node->orig, BATADV_DAT_ADDR_MAX); + orig_node->dat_addr = (batadv_dat_addr_t)addr; +} + +/** + * batadv_dat_init_own_addr - assign a DAT address to the node itself + * @bat_priv: the bat priv with all the soft interface information + * @primary_if: a pointer to the primary interface + */ +static inline void +batadv_dat_init_own_addr(struct batadv_priv *bat_priv, + struct batadv_hard_iface *primary_if) +{ + uint32_t addr; + + addr = batadv_choose_orig(primary_if->net_dev->dev_addr, + BATADV_DAT_ADDR_MAX); + + bat_priv->dat.addr = (batadv_dat_addr_t)addr; +} + +int batadv_dat_init(struct batadv_priv *bat_priv); +void batadv_dat_free(struct batadv_priv *bat_priv); +int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset); + +/** + * batadv_dat_inc_counter - increment the correct DAT packet counter + * @bat_priv: the bat priv with all the soft interface information + * @subtype: the 4addr subtype of the packet to be counted + * + * Updates the ethtool statistics for the received packet if it is a DAT subtype + */ +static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv, + uint8_t subtype) +{ + switch (subtype) { + case BATADV_P_DAT_DHT_GET: + batadv_inc_counter(bat_priv, + BATADV_CNT_DAT_GET_RX); + break; + case BATADV_P_DAT_DHT_PUT: + batadv_inc_counter(bat_priv, + BATADV_CNT_DAT_PUT_RX); + break; + } +} + +#else + +static inline bool +batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, + struct sk_buff *skb) +{ + return false; +} + +static inline bool +batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size) +{ + return false; +} + +static inline bool +batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, + struct sk_buff *skb) +{ + return false; +} + +static inline bool +batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size) +{ + return false; +} + +static inline bool +batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, + struct batadv_forw_packet *forw_packet) +{ + return false; +} + +static inline void +batadv_dat_init_orig_node_addr(struct batadv_orig_node *orig_node) +{ +} + +static inline void batadv_dat_init_own_addr(struct batadv_priv *bat_priv, + struct batadv_hard_iface *iface) +{ +} + +static inline void batadv_arp_change_timeout(struct net_device *soft_iface, + const char *name) +{ +} + +static inline int batadv_dat_init(struct batadv_priv *bat_priv) +{ + return 0; +} + +static inline void batadv_dat_free(struct batadv_priv *bat_priv) +{ +} + +static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv, + uint8_t subtype) +{ +} + +#endif /* CONFIG_BATMAN_ADV_DAT */ + +#endif /* _NET_BATMAN_ADV_ARP_H_ */ diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 15d67abc10a4..dd07c7e3654f 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -477,22 +477,11 @@ int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset) struct batadv_hard_iface *primary_if; struct batadv_gw_node *gw_node; struct hlist_node *node; - int gw_count = 0, ret = 0; + int gw_count = 0; - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - please specify interfaces to enable it\n", - net_dev->name); + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) goto out; - } - - if (primary_if->if_status != BATADV_IF_ACTIVE) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - primary interface not active\n", - net_dev->name); - goto out; - } seq_printf(seq, " %-12s (%s/%i) %17s [%10s]: gw_class ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n", @@ -519,7 +508,7 @@ int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset) out: if (primary_if) batadv_hardif_free_ref(primary_if); - return ret; + return 0; } static bool batadv_is_type_dhcprequest(struct sk_buff *skb, int header_len) diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index d112fd6750b0..365ed74f3946 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -18,6 +18,7 @@ */ #include "main.h" +#include "distributed-arp-table.h" #include "hard-interface.h" #include "soft-interface.h" #include "send.h" @@ -58,6 +59,45 @@ out: return hard_iface; } +/** + * batadv_is_on_batman_iface - check if a device is a batman iface descendant + * @net_dev: the device to check + * + * If the user creates any virtual device on top of a batman-adv interface, it + * is important to prevent this new interface to be used to create a new mesh + * network (this behaviour would lead to a batman-over-batman configuration). + * This function recursively checks all the fathers of the device passed as + * argument looking for a batman-adv soft interface. + * + * Returns true if the device is descendant of a batman-adv mesh interface (or + * if it is a batman-adv interface itself), false otherwise + */ +static bool batadv_is_on_batman_iface(const struct net_device *net_dev) +{ + struct net_device *parent_dev; + bool ret; + + /* check if this is a batman-adv mesh interface */ + if (batadv_softif_is_valid(net_dev)) + return true; + + /* no more parents..stop recursion */ + if (net_dev->iflink == net_dev->ifindex) + return false; + + /* recurse over the parent device */ + parent_dev = dev_get_by_index(&init_net, net_dev->iflink); + /* if we got a NULL parent_dev there is something broken.. */ + if (WARN(!parent_dev, "Cannot find parent device")) + return false; + + ret = batadv_is_on_batman_iface(parent_dev); + + if (parent_dev) + dev_put(parent_dev); + return ret; +} + static int batadv_is_valid_iface(const struct net_device *net_dev) { if (net_dev->flags & IFF_LOOPBACK) @@ -70,7 +110,7 @@ static int batadv_is_valid_iface(const struct net_device *net_dev) return 0; /* no batman over batman */ - if (batadv_softif_is_valid(net_dev)) + if (batadv_is_on_batman_iface(net_dev)) return 0; return 1; @@ -109,6 +149,8 @@ static void batadv_primary_if_update_addr(struct batadv_priv *bat_priv, if (!primary_if) goto out; + batadv_dat_init_own_addr(bat_priv, primary_if); + skb = bat_priv->vis.my_info->skb_packet; vis_packet = (struct batadv_vis_packet *)skb->data; memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN); @@ -450,8 +492,8 @@ batadv_hardif_add_interface(struct net_device *net_dev) /* This can't be called via a bat_priv callback because * we have no bat_priv yet. */ - atomic_set(&hard_iface->seqno, 1); - hard_iface->packet_buff = NULL; + atomic_set(&hard_iface->bat_iv.ogm_seqno, 1); + hard_iface->bat_iv.ogm_buff = NULL; return hard_iface; diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h index 977de9c75fc2..e05333905afd 100644 --- a/net/batman-adv/hash.h +++ b/net/batman-adv/hash.h @@ -82,6 +82,28 @@ static inline void batadv_hash_delete(struct batadv_hashtable *hash, } /** + * batadv_hash_bytes - hash some bytes and add them to the previous hash + * @hash: previous hash value + * @data: data to be hashed + * @size: number of bytes to be hashed + * + * Returns the new hash value. + */ +static inline uint32_t batadv_hash_bytes(uint32_t hash, void *data, + uint32_t size) +{ + const unsigned char *key = data; + int i; + + for (i = 0; i < size; i++) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + return hash; +} + +/** * batadv_hash_add - adds data to the hashtable * @hash: storage hash table * @compare: callback to determine if 2 hash elements are identical diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c index bde3cf747507..87ca8095b011 100644 --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c @@ -42,12 +42,16 @@ static int batadv_socket_open(struct inode *inode, struct file *file) unsigned int i; struct batadv_socket_client *socket_client; + if (!try_module_get(THIS_MODULE)) + return -EBUSY; + nonseekable_open(inode, file); socket_client = kmalloc(sizeof(*socket_client), GFP_KERNEL); - - if (!socket_client) + if (!socket_client) { + module_put(THIS_MODULE); return -ENOMEM; + } for (i = 0; i < ARRAY_SIZE(batadv_socket_client_hash); i++) { if (!batadv_socket_client_hash[i]) { @@ -59,6 +63,7 @@ static int batadv_socket_open(struct inode *inode, struct file *file) if (i == ARRAY_SIZE(batadv_socket_client_hash)) { pr_err("Error - can't add another packet client: maximum number of clients reached\n"); kfree(socket_client); + module_put(THIS_MODULE); return -EXFULL; } @@ -71,7 +76,6 @@ static int batadv_socket_open(struct inode *inode, struct file *file) file->private_data = socket_client; - batadv_inc_module_count(); return 0; } @@ -96,7 +100,7 @@ static int batadv_socket_release(struct inode *inode, struct file *file) spin_unlock_bh(&socket_client->lock); kfree(socket_client); - batadv_dec_module_count(); + module_put(THIS_MODULE); return 0; } @@ -173,13 +177,13 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, if (len >= sizeof(struct batadv_icmp_packet_rr)) packet_len = sizeof(struct batadv_icmp_packet_rr); - skb = dev_alloc_skb(packet_len + ETH_HLEN); + skb = dev_alloc_skb(packet_len + ETH_HLEN + NET_IP_ALIGN); if (!skb) { len = -ENOMEM; goto out; } - skb_reserve(skb, ETH_HLEN); + skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN); icmp_packet = (struct batadv_icmp_packet_rr *)skb_put(skb, packet_len); if (copy_from_user(icmp_packet, buff, packet_len)) { diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index b4aa470bc4a6..dc33a0c484a4 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -29,6 +29,7 @@ #include "hard-interface.h" #include "gateway_client.h" #include "bridge_loop_avoidance.h" +#include "distributed-arp-table.h" #include "vis.h" #include "hash.h" #include "bat_algo.h" @@ -128,6 +129,10 @@ int batadv_mesh_init(struct net_device *soft_iface) if (ret < 0) goto err; + ret = batadv_dat_init(bat_priv); + if (ret < 0) + goto err; + atomic_set(&bat_priv->gw.reselect, 0); atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE); @@ -155,21 +160,13 @@ void batadv_mesh_free(struct net_device *soft_iface) batadv_bla_free(bat_priv); + batadv_dat_free(bat_priv); + free_percpu(bat_priv->bat_counters); atomic_set(&bat_priv->mesh_state, BATADV_MESH_INACTIVE); } -void batadv_inc_module_count(void) -{ - try_module_get(THIS_MODULE); -} - -void batadv_dec_module_count(void) -{ - module_put(THIS_MODULE); -} - int batadv_is_my_mac(const uint8_t *addr) { const struct batadv_hard_iface *hard_iface; @@ -188,6 +185,42 @@ int batadv_is_my_mac(const uint8_t *addr) return 0; } +/** + * batadv_seq_print_text_primary_if_get - called from debugfs table printing + * function that requires the primary interface + * @seq: debugfs table seq_file struct + * + * Returns primary interface if found or NULL otherwise. + */ +struct batadv_hard_iface * +batadv_seq_print_text_primary_if_get(struct seq_file *seq) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct batadv_priv *bat_priv = netdev_priv(net_dev); + struct batadv_hard_iface *primary_if; + + primary_if = batadv_primary_if_get_selected(bat_priv); + + if (!primary_if) { + seq_printf(seq, + "BATMAN mesh %s disabled - please specify interfaces to enable it\n", + net_dev->name); + goto out; + } + + if (primary_if->if_status == BATADV_IF_ACTIVE) + goto out; + + seq_printf(seq, + "BATMAN mesh %s disabled - primary interface not active\n", + net_dev->name); + batadv_hardif_free_ref(primary_if); + primary_if = NULL; + +out: + return primary_if; +} + static int batadv_recv_unhandled_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if) { @@ -274,6 +307,8 @@ static void batadv_recv_handler_init(void) /* batman icmp packet */ batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet; + /* unicast with 4 addresses packet */ + batadv_rx_handler[BATADV_UNICAST_4ADDR] = batadv_recv_unicast_packet; /* unicast packet */ batadv_rx_handler[BATADV_UNICAST] = batadv_recv_unicast_packet; /* fragmented unicast packet */ diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index d57b746219de..8f149bb66817 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -44,6 +44,7 @@ #define BATADV_TT_LOCAL_TIMEOUT 3600000 /* in milliseconds */ #define BATADV_TT_CLIENT_ROAM_TIMEOUT 600000 /* in milliseconds */ #define BATADV_TT_CLIENT_TEMP_TIMEOUT 600000 /* in milliseconds */ +#define BATADV_DAT_ENTRY_TIMEOUT (5*60000) /* 5 mins in milliseconds */ /* sliding packet range of received originator messages in sequence numbers * (should be a multiple of our word size) */ @@ -73,6 +74,11 @@ #define BATADV_LOG_BUF_LEN 8192 /* has to be a power of 2 */ +/* msecs after which an ARP_REQUEST is sent in broadcast as fallback */ +#define ARP_REQ_DELAY 250 +/* numbers of originator to contact for any PUT/GET DHT operation */ +#define BATADV_DAT_CANDIDATES_NUM 3 + #define BATADV_VIS_INTERVAL 5000 /* 5 seconds */ /* how much worse secondary interfaces may be to be considered as bonding @@ -89,6 +95,7 @@ #define BATADV_BLA_PERIOD_LENGTH 10000 /* 10 seconds */ #define BATADV_BLA_BACKBONE_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 3) #define BATADV_BLA_CLAIM_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 10) +#define BATADV_BLA_WAIT_PERIODS 3 #define BATADV_DUPLIST_SIZE 16 #define BATADV_DUPLIST_TIMEOUT 500 /* 500 ms */ @@ -117,6 +124,9 @@ enum batadv_uev_type { #define BATADV_GW_THRESHOLD 50 +#define BATADV_DAT_CANDIDATE_NOT_FOUND 0 +#define BATADV_DAT_CANDIDATE_ORIG 1 + /* Debug Messages */ #ifdef pr_fmt #undef pr_fmt @@ -150,9 +160,9 @@ extern struct workqueue_struct *batadv_event_workqueue; int batadv_mesh_init(struct net_device *soft_iface); void batadv_mesh_free(struct net_device *soft_iface); -void batadv_inc_module_count(void); -void batadv_dec_module_count(void); int batadv_is_my_mac(const uint8_t *addr); +struct batadv_hard_iface * +batadv_seq_print_text_primary_if_get(struct seq_file *seq); int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev); @@ -165,13 +175,22 @@ int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops); int batadv_algo_select(struct batadv_priv *bat_priv, char *name); int batadv_algo_seq_print_text(struct seq_file *seq, void *offset); -/* all messages related to routing / flooding / broadcasting / etc */ +/** + * enum batadv_dbg_level - available log levels + * @BATADV_DBG_BATMAN: OGM and TQ computations related messages + * @BATADV_DBG_ROUTES: route added / changed / deleted + * @BATADV_DBG_TT: translation table messages + * @BATADV_DBG_BLA: bridge loop avoidance messages + * @BATADV_DBG_DAT: ARP snooping and DAT related messages + * @BATADV_DBG_ALL: the union of all the above log levels + */ enum batadv_dbg_level { BATADV_DBG_BATMAN = BIT(0), - BATADV_DBG_ROUTES = BIT(1), /* route added / changed / deleted */ - BATADV_DBG_TT = BIT(2), /* translation table operations */ - BATADV_DBG_BLA = BIT(3), /* bridge loop avoidance */ - BATADV_DBG_ALL = 15, + BATADV_DBG_ROUTES = BIT(1), + BATADV_DBG_TT = BIT(2), + BATADV_DBG_BLA = BIT(3), + BATADV_DBG_DAT = BIT(4), + BATADV_DBG_ALL = 31, }; #ifdef CONFIG_BATMAN_ADV_DEBUG diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index ac9bdf8f80a6..8c32cf1c2dec 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -18,6 +18,7 @@ */ #include "main.h" +#include "distributed-arp-table.h" #include "originator.h" #include "hash.h" #include "translation-table.h" @@ -220,9 +221,9 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, atomic_set(&orig_node->refcount, 2); orig_node->tt_initialised = false; - orig_node->tt_poss_change = false; orig_node->bat_priv = bat_priv; memcpy(orig_node->orig, addr, ETH_ALEN); + batadv_dat_init_orig_node_addr(orig_node); orig_node->router = NULL; orig_node->tt_crc = 0; atomic_set(&orig_node->last_ttvn, 0); @@ -415,23 +416,10 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset) int last_seen_msecs; unsigned long last_seen_jiffies; uint32_t i; - int ret = 0; - primary_if = batadv_primary_if_get_selected(bat_priv); - - if (!primary_if) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - please specify interfaces to enable it\n", - net_dev->name); - goto out; - } - - if (primary_if->if_status != BATADV_IF_ACTIVE) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - primary interface not active\n", - net_dev->name); + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) goto out; - } seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n", BATADV_SOURCE_VERSION, primary_if->net_dev->name, @@ -485,7 +473,7 @@ next: out: if (primary_if) batadv_hardif_free_ref(primary_if); - return ret; + return 0; } static int batadv_orig_node_add_if(struct batadv_orig_node *orig_node, diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index 2d23a14c220e..df548ed196d3 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -23,14 +23,29 @@ #define BATADV_ETH_P_BATMAN 0x4305 /* unofficial/not registered Ethertype */ enum batadv_packettype { - BATADV_IV_OGM = 0x01, - BATADV_ICMP = 0x02, - BATADV_UNICAST = 0x03, - BATADV_BCAST = 0x04, - BATADV_VIS = 0x05, - BATADV_UNICAST_FRAG = 0x06, - BATADV_TT_QUERY = 0x07, - BATADV_ROAM_ADV = 0x08, + BATADV_IV_OGM = 0x01, + BATADV_ICMP = 0x02, + BATADV_UNICAST = 0x03, + BATADV_BCAST = 0x04, + BATADV_VIS = 0x05, + BATADV_UNICAST_FRAG = 0x06, + BATADV_TT_QUERY = 0x07, + BATADV_ROAM_ADV = 0x08, + BATADV_UNICAST_4ADDR = 0x09, +}; + +/** + * enum batadv_subtype - packet subtype for unicast4addr + * @BATADV_P_DATA: user payload + * @BATADV_P_DAT_DHT_GET: DHT request message + * @BATADV_P_DAT_DHT_PUT: DHT store message + * @BATADV_P_DAT_CACHE_REPLY: ARP reply generated by DAT + */ +enum batadv_subtype { + BATADV_P_DATA = 0x01, + BATADV_P_DAT_DHT_GET = 0x02, + BATADV_P_DAT_DHT_PUT = 0x03, + BATADV_P_DAT_CACHE_REPLY = 0x04, }; /* this file is included by batctl which needs these defines */ @@ -106,13 +121,16 @@ struct batadv_bla_claim_dst { uint8_t magic[3]; /* FF:43:05 */ uint8_t type; /* bla_claimframe */ __be16 group; /* group id */ -} __packed; +}; struct batadv_header { uint8_t packet_type; uint8_t version; /* batman version field */ uint8_t ttl; -} __packed; + /* the parent struct has to add a byte after the header to make + * everything 4 bytes aligned again + */ +}; struct batadv_ogm_packet { struct batadv_header header; @@ -137,7 +155,7 @@ struct batadv_icmp_packet { __be16 seqno; uint8_t uid; uint8_t reserved; -} __packed; +}; #define BATADV_RR_LEN 16 @@ -153,13 +171,32 @@ struct batadv_icmp_packet_rr { uint8_t uid; uint8_t rr_cur; uint8_t rr[BATADV_RR_LEN][ETH_ALEN]; -} __packed; +}; struct batadv_unicast_packet { struct batadv_header header; uint8_t ttvn; /* destination translation table version number */ uint8_t dest[ETH_ALEN]; -} __packed; + /* "4 bytes boundary + 2 bytes" long to make the payload after the + * following ethernet header again 4 bytes boundary aligned + */ +}; + +/** + * struct batadv_unicast_4addr_packet - extended unicast packet + * @u: common unicast packet header + * @src: address of the source + * @subtype: packet subtype + */ +struct batadv_unicast_4addr_packet { + struct batadv_unicast_packet u; + uint8_t src[ETH_ALEN]; + uint8_t subtype; + uint8_t reserved; + /* "4 bytes boundary + 2 bytes" long to make the payload after the + * following ethernet header again 4 bytes boundary aligned + */ +}; struct batadv_unicast_frag_packet { struct batadv_header header; @@ -176,6 +213,9 @@ struct batadv_bcast_packet { uint8_t reserved; __be32 seqno; uint8_t orig[ETH_ALEN]; + /* "4 bytes boundary + 2 bytes" long to make the payload after the + * following ethernet header again 4 bytes boundary aligned + */ } __packed; struct batadv_vis_packet { @@ -187,7 +227,7 @@ struct batadv_vis_packet { uint8_t vis_orig[ETH_ALEN]; /* originator reporting its neighbors */ uint8_t target_orig[ETH_ALEN]; /* who should receive this packet */ uint8_t sender_orig[ETH_ALEN]; /* who sent or forwarded this packet */ -} __packed; +}; struct batadv_tt_query_packet { struct batadv_header header; diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 376b4cc6ca82..78d657264cbf 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -28,6 +28,7 @@ #include "vis.h" #include "unicast.h" #include "bridge_loop_avoidance.h" +#include "distributed-arp-table.h" static int batadv_route_unicast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); @@ -549,25 +550,18 @@ batadv_find_ifalter_router(struct batadv_orig_node *primary_orig, if (tmp_neigh_node->if_incoming == recv_if) continue; - if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) + if (router && tmp_neigh_node->tq_avg <= router->tq_avg) continue; - /* if we don't have a router yet - * or this one is better, choose it. - */ - if ((!router) || - (tmp_neigh_node->tq_avg > router->tq_avg)) { - /* decrement refcount of - * previously selected router - */ - if (router) - batadv_neigh_node_free_ref(router); + if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) + continue; - router = tmp_neigh_node; - atomic_inc_not_zero(&router->refcount); - } + /* decrement refcount of previously selected router */ + if (router) + batadv_neigh_node_free_ref(router); - batadv_neigh_node_free_ref(tmp_neigh_node); + /* we found a better router (or at least one valid router) */ + router = tmp_neigh_node; } /* use the first candidate if nothing was found. */ @@ -687,21 +681,8 @@ int batadv_recv_roam_adv(struct sk_buff *skb, struct batadv_hard_iface *recv_if) struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct batadv_roam_adv_packet *roam_adv_packet; struct batadv_orig_node *orig_node; - struct ethhdr *ethhdr; - - /* drop packet if it has not necessary minimum size */ - if (unlikely(!pskb_may_pull(skb, - sizeof(struct batadv_roam_adv_packet)))) - goto out; - - ethhdr = (struct ethhdr *)skb_mac_header(skb); - - /* packet with unicast indication but broadcast recipient */ - if (is_broadcast_ether_addr(ethhdr->h_dest)) - goto out; - /* packet with broadcast sender address */ - if (is_broadcast_ether_addr(ethhdr->h_source)) + if (batadv_check_unicast_packet(skb, sizeof(*roam_adv_packet)) < 0) goto out; batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_RX); @@ -730,12 +711,6 @@ int batadv_recv_roam_adv(struct sk_buff *skb, struct batadv_hard_iface *recv_if) BATADV_TT_CLIENT_ROAM, atomic_read(&orig_node->last_ttvn) + 1); - /* Roaming phase starts: I have new information but the ttvn has not - * been incremented yet. This flag will make me check all the incoming - * packets for the correct destination. - */ - bat_priv->tt.poss_change = true; - batadv_orig_node_free_ref(orig_node); out: /* returning NET_RX_DROP will make the caller function kfree the skb */ @@ -918,80 +893,161 @@ out: return ret; } +/** + * batadv_reroute_unicast_packet - update the unicast header for re-routing + * @bat_priv: the bat priv with all the soft interface information + * @unicast_packet: the unicast header to be updated + * @dst_addr: the payload destination + * + * Search the translation table for dst_addr and update the unicast header with + * the new corresponding information (originator address where the destination + * client currently is and its known TTVN) + * + * Returns true if the packet header has been updated, false otherwise + */ +static bool +batadv_reroute_unicast_packet(struct batadv_priv *bat_priv, + struct batadv_unicast_packet *unicast_packet, + uint8_t *dst_addr) +{ + struct batadv_orig_node *orig_node = NULL; + struct batadv_hard_iface *primary_if = NULL; + bool ret = false; + uint8_t *orig_addr, orig_ttvn; + + if (batadv_is_my_client(bat_priv, dst_addr)) { + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + orig_addr = primary_if->net_dev->dev_addr; + orig_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn); + } else { + orig_node = batadv_transtable_search(bat_priv, NULL, dst_addr); + if (!orig_node) + goto out; + + if (batadv_compare_eth(orig_node->orig, unicast_packet->dest)) + goto out; + + orig_addr = orig_node->orig; + orig_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); + } + + /* update the packet header */ + memcpy(unicast_packet->dest, orig_addr, ETH_ALEN); + unicast_packet->ttvn = orig_ttvn; + + ret = true; +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + if (orig_node) + batadv_orig_node_free_ref(orig_node); + + return ret; +} + static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, struct sk_buff *skb) { - uint8_t curr_ttvn; + uint8_t curr_ttvn, old_ttvn; struct batadv_orig_node *orig_node; struct ethhdr *ethhdr; struct batadv_hard_iface *primary_if; struct batadv_unicast_packet *unicast_packet; - bool tt_poss_change; int is_old_ttvn; - /* I could need to modify it */ - if (skb_cow(skb, sizeof(struct batadv_unicast_packet)) < 0) + /* check if there is enough data before accessing it */ + if (pskb_may_pull(skb, sizeof(*unicast_packet) + ETH_HLEN) < 0) + return 0; + + /* create a copy of the skb (in case of for re-routing) to modify it. */ + if (skb_cow(skb, sizeof(*unicast_packet)) < 0) return 0; unicast_packet = (struct batadv_unicast_packet *)skb->data; + ethhdr = (struct ethhdr *)(skb->data + sizeof(*unicast_packet)); - if (batadv_is_my_mac(unicast_packet->dest)) { - tt_poss_change = bat_priv->tt.poss_change; - curr_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn); - } else { + /* check if the destination client was served by this node and it is now + * roaming. In this case, it means that the node has got a ROAM_ADV + * message and that it knows the new destination in the mesh to re-route + * the packet to + */ + if (batadv_tt_local_client_is_roaming(bat_priv, ethhdr->h_dest)) { + if (batadv_reroute_unicast_packet(bat_priv, unicast_packet, + ethhdr->h_dest)) + net_ratelimited_function(batadv_dbg, BATADV_DBG_TT, + bat_priv, + "Rerouting unicast packet to %pM (dst=%pM): Local Roaming\n", + unicast_packet->dest, + ethhdr->h_dest); + /* at this point the mesh destination should have been + * substituted with the originator address found in the global + * table. If not, let the packet go untouched anyway because + * there is nothing the node can do + */ + return 1; + } + + /* retrieve the TTVN known by this node for the packet destination. This + * value is used later to check if the node which sent (or re-routed + * last time) the packet had an updated information or not + */ + curr_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn); + if (!batadv_is_my_mac(unicast_packet->dest)) { orig_node = batadv_orig_hash_find(bat_priv, unicast_packet->dest); - + /* if it is not possible to find the orig_node representing the + * destination, the packet can immediately be dropped as it will + * not be possible to deliver it + */ if (!orig_node) return 0; curr_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); - tt_poss_change = orig_node->tt_poss_change; batadv_orig_node_free_ref(orig_node); } - /* Check whether I have to reroute the packet */ + /* check if the TTVN contained in the packet is fresher than what the + * node knows + */ is_old_ttvn = batadv_seq_before(unicast_packet->ttvn, curr_ttvn); - if (is_old_ttvn || tt_poss_change) { - /* check if there is enough data before accessing it */ - if (pskb_may_pull(skb, sizeof(struct batadv_unicast_packet) + - ETH_HLEN) < 0) - return 0; + if (!is_old_ttvn) + return 1; - ethhdr = (struct ethhdr *)(skb->data + sizeof(*unicast_packet)); + old_ttvn = unicast_packet->ttvn; + /* the packet was forged based on outdated network information. Its + * destination can possibly be updated and forwarded towards the new + * target host + */ + if (batadv_reroute_unicast_packet(bat_priv, unicast_packet, + ethhdr->h_dest)) { + net_ratelimited_function(batadv_dbg, BATADV_DBG_TT, bat_priv, + "Rerouting unicast packet to %pM (dst=%pM): TTVN mismatch old_ttvn=%u new_ttvn=%u\n", + unicast_packet->dest, ethhdr->h_dest, + old_ttvn, curr_ttvn); + return 1; + } - /* we don't have an updated route for this client, so we should - * not try to reroute the packet!! - */ - if (batadv_tt_global_client_is_roaming(bat_priv, - ethhdr->h_dest)) - return 1; + /* the packet has not been re-routed: either the destination is + * currently served by this node or there is no destination at all and + * it is possible to drop the packet + */ + if (!batadv_is_my_client(bat_priv, ethhdr->h_dest)) + return 0; - orig_node = batadv_transtable_search(bat_priv, NULL, - ethhdr->h_dest); - - if (!orig_node) { - if (!batadv_is_my_client(bat_priv, ethhdr->h_dest)) - return 0; - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) - return 0; - memcpy(unicast_packet->dest, - primary_if->net_dev->dev_addr, ETH_ALEN); - batadv_hardif_free_ref(primary_if); - } else { - memcpy(unicast_packet->dest, orig_node->orig, - ETH_ALEN); - curr_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); - batadv_orig_node_free_ref(orig_node); - } + /* update the header in order to let the packet be delivered to this + * node's soft interface + */ + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + return 0; - batadv_dbg(BATADV_DBG_ROUTES, bat_priv, - "TTVN mismatch (old_ttvn %u new_ttvn %u)! Rerouting unicast packet (for %pM) to %pM\n", - unicast_packet->ttvn, curr_ttvn, ethhdr->h_dest, - unicast_packet->dest); + memcpy(unicast_packet->dest, primary_if->net_dev->dev_addr, ETH_ALEN); + + batadv_hardif_free_ref(primary_if); + + unicast_packet->ttvn = curr_ttvn; - unicast_packet->ttvn = curr_ttvn; - } return 1; } @@ -1000,7 +1056,19 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, { struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct batadv_unicast_packet *unicast_packet; + struct batadv_unicast_4addr_packet *unicast_4addr_packet; + uint8_t *orig_addr; + struct batadv_orig_node *orig_node = NULL; int hdr_size = sizeof(*unicast_packet); + bool is4addr; + + unicast_packet = (struct batadv_unicast_packet *)skb->data; + unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data; + + is4addr = unicast_packet->header.packet_type == BATADV_UNICAST_4ADDR; + /* the caller function should have already pulled 2 bytes */ + if (is4addr) + hdr_size = sizeof(*unicast_4addr_packet); if (batadv_check_unicast_packet(skb, hdr_size) < 0) return NET_RX_DROP; @@ -1008,12 +1076,28 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, if (!batadv_check_unicast_ttvn(bat_priv, skb)) return NET_RX_DROP; - unicast_packet = (struct batadv_unicast_packet *)skb->data; - /* packet for me */ if (batadv_is_my_mac(unicast_packet->dest)) { + if (is4addr) { + batadv_dat_inc_counter(bat_priv, + unicast_4addr_packet->subtype); + orig_addr = unicast_4addr_packet->src; + orig_node = batadv_orig_hash_find(bat_priv, orig_addr); + } + + if (batadv_dat_snoop_incoming_arp_request(bat_priv, skb, + hdr_size)) + goto rx_success; + if (batadv_dat_snoop_incoming_arp_reply(bat_priv, skb, + hdr_size)) + goto rx_success; + batadv_interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size, - NULL); + orig_node); + +rx_success: + if (orig_node) + batadv_orig_node_free_ref(orig_node); return NET_RX_SUCCESS; } @@ -1050,8 +1134,17 @@ int batadv_recv_ucast_frag_packet(struct sk_buff *skb, if (!new_skb) return NET_RX_SUCCESS; + if (batadv_dat_snoop_incoming_arp_request(bat_priv, new_skb, + hdr_size)) + goto rx_success; + if (batadv_dat_snoop_incoming_arp_reply(bat_priv, new_skb, + hdr_size)) + goto rx_success; + batadv_interface_rx(recv_if->soft_iface, new_skb, recv_if, sizeof(struct batadv_unicast_packet), NULL); + +rx_success: return NET_RX_SUCCESS; } @@ -1143,9 +1236,16 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, if (batadv_bla_is_backbone_gw(skb, orig_node, hdr_size)) goto out; + if (batadv_dat_snoop_incoming_arp_request(bat_priv, skb, hdr_size)) + goto rx_success; + if (batadv_dat_snoop_incoming_arp_reply(bat_priv, skb, hdr_size)) + goto rx_success; + /* broadcast for me */ batadv_interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size, orig_node); + +rx_success: ret = NET_RX_SUCCESS; goto out; diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 570a8bce0364..660d9bf7d219 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -18,6 +18,7 @@ */ #include "main.h" +#include "distributed-arp-table.h" #include "send.h" #include "routing.h" #include "translation-table.h" @@ -209,6 +210,9 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) goto out; + if (batadv_dat_drop_broadcast_packet(bat_priv, forw_packet)) + goto out; + /* rebroadcast packet */ rcu_read_lock(); list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index b9a28d2dd3e8..2d1f89517d99 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -20,6 +20,7 @@ #include "main.h" #include "soft-interface.h" #include "hard-interface.h" +#include "distributed-arp-table.h" #include "routing.h" #include "send.h" #include "debugfs.h" @@ -146,13 +147,16 @@ static int batadv_interface_tx(struct sk_buff *skb, struct batadv_bcast_packet *bcast_packet; struct vlan_ethhdr *vhdr; __be16 ethertype = __constant_htons(BATADV_ETH_P_BATMAN); - static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00, - 0x00}; + static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, + 0x00, 0x00}; + static const uint8_t ectp_addr[ETH_ALEN] = {0xCF, 0x00, 0x00, 0x00, + 0x00, 0x00}; unsigned int header_len = 0; int data_len = skb->len, ret; short vid __maybe_unused = -1; bool do_bcast = false; uint32_t seqno; + unsigned long brd_delay = 1; if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dropped; @@ -180,10 +184,16 @@ static int batadv_interface_tx(struct sk_buff *skb, /* don't accept stp packets. STP does not help in meshes. * better use the bridge loop avoidance ... + * + * The same goes for ECTP sent at least by some Cisco Switches, + * it might confuse the mesh when used with bridge loop avoidance. */ if (batadv_compare_eth(ethhdr->h_dest, stp_addr)) goto dropped; + if (batadv_compare_eth(ethhdr->h_dest, ectp_addr)) + goto dropped; + if (is_multicast_ether_addr(ethhdr->h_dest)) { do_bcast = true; @@ -216,6 +226,13 @@ static int batadv_interface_tx(struct sk_buff *skb, if (!primary_if) goto dropped; + /* in case of ARP request, we do not immediately broadcasti the + * packet, instead we first wait for DAT to try to retrieve the + * correct ARP entry + */ + if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb)) + brd_delay = msecs_to_jiffies(ARP_REQ_DELAY); + if (batadv_skb_head_push(skb, sizeof(*bcast_packet)) < 0) goto dropped; @@ -237,7 +254,7 @@ static int batadv_interface_tx(struct sk_buff *skb, seqno = atomic_inc_return(&bat_priv->bcast_seqno); bcast_packet->seqno = htonl(seqno); - batadv_add_bcast_packet_to_list(bat_priv, skb, 1); + batadv_add_bcast_packet_to_list(bat_priv, skb, brd_delay); /* a copy is stored in the bcast list, therefore removing * the original skb. @@ -252,7 +269,12 @@ static int batadv_interface_tx(struct sk_buff *skb, goto dropped; } - ret = batadv_unicast_send_skb(skb, bat_priv); + if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb)) + goto dropped; + + batadv_dat_snoop_outgoing_arp_reply(bat_priv, skb); + + ret = batadv_unicast_send_skb(bat_priv, skb); if (ret != 0) goto dropped_freed; } @@ -347,7 +369,51 @@ out: return; } +/* batman-adv network devices have devices nesting below it and are a special + * "super class" of normal network devices; split their locks off into a + * separate class since they always nest. + */ +static struct lock_class_key batadv_netdev_xmit_lock_key; +static struct lock_class_key batadv_netdev_addr_lock_key; + +/** + * batadv_set_lockdep_class_one - Set lockdep class for a single tx queue + * @dev: device which owns the tx queue + * @txq: tx queue to modify + * @_unused: always NULL + */ +static void batadv_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) +{ + lockdep_set_class(&txq->_xmit_lock, &batadv_netdev_xmit_lock_key); +} + +/** + * batadv_set_lockdep_class - Set txq and addr_list lockdep class + * @dev: network device to modify + */ +static void batadv_set_lockdep_class(struct net_device *dev) +{ + lockdep_set_class(&dev->addr_list_lock, &batadv_netdev_addr_lock_key); + netdev_for_each_tx_queue(dev, batadv_set_lockdep_class_one, NULL); +} + +/** + * batadv_softif_init - Late stage initialization of soft interface + * @dev: registered network device to modify + * + * Returns error code on failures + */ +static int batadv_softif_init(struct net_device *dev) +{ + batadv_set_lockdep_class(dev); + + return 0; +} + static const struct net_device_ops batadv_netdev_ops = { + .ndo_init = batadv_softif_init, .ndo_open = batadv_interface_open, .ndo_stop = batadv_interface_release, .ndo_get_stats = batadv_interface_stats, @@ -414,6 +480,9 @@ struct net_device *batadv_softif_create(const char *name) atomic_set(&bat_priv->aggregated_ogms, 1); atomic_set(&bat_priv->bonding, 0); atomic_set(&bat_priv->bridge_loop_avoidance, 0); +#ifdef CONFIG_BATMAN_ADV_DAT + atomic_set(&bat_priv->distributed_arp_table, 1); +#endif atomic_set(&bat_priv->ap_isolation, 0); atomic_set(&bat_priv->vis_mode, BATADV_VIS_TYPE_CLIENT_UPDATE); atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF); @@ -436,7 +505,6 @@ struct net_device *batadv_softif_create(const char *name) #endif bat_priv->tt.last_changeset = NULL; bat_priv->tt.last_changeset_len = 0; - bat_priv->tt.poss_change = false; bat_priv->primary_if = NULL; bat_priv->num_ifaces = 0; @@ -556,6 +624,13 @@ static const struct { { "tt_response_rx" }, { "tt_roam_adv_tx" }, { "tt_roam_adv_rx" }, +#ifdef CONFIG_BATMAN_ADV_DAT + { "dat_get_tx" }, + { "dat_get_rx" }, + { "dat_put_tx" }, + { "dat_put_rx" }, + { "dat_cached_reply_tx" }, +#endif }; static void batadv_get_strings(struct net_device *dev, uint32_t stringset, diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c index 66518c75c217..84a55cb19b0b 100644 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@ -20,6 +20,7 @@ #include "main.h" #include "sysfs.h" #include "translation-table.h" +#include "distributed-arp-table.h" #include "originator.h" #include "hard-interface.h" #include "gateway_common.h" @@ -122,55 +123,6 @@ ssize_t batadv_show_##_name(struct kobject *kobj, \ batadv_store_##_name) -#define BATADV_ATTR_HIF_STORE_UINT(_name, _min, _max, _post_func) \ -ssize_t batadv_store_##_name(struct kobject *kobj, \ - struct attribute *attr, char *buff, \ - size_t count) \ -{ \ - struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \ - struct batadv_hard_iface *hard_iface; \ - ssize_t length; \ - \ - hard_iface = batadv_hardif_get_by_netdev(net_dev); \ - if (!hard_iface) \ - return 0; \ - \ - length = __batadv_store_uint_attr(buff, count, _min, _max, \ - _post_func, attr, \ - &hard_iface->_name, net_dev); \ - \ - batadv_hardif_free_ref(hard_iface); \ - return length; \ -} - -#define BATADV_ATTR_HIF_SHOW_UINT(_name) \ -ssize_t batadv_show_##_name(struct kobject *kobj, \ - struct attribute *attr, char *buff) \ -{ \ - struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \ - struct batadv_hard_iface *hard_iface; \ - ssize_t length; \ - \ - hard_iface = batadv_hardif_get_by_netdev(net_dev); \ - if (!hard_iface) \ - return 0; \ - \ - length = sprintf(buff, "%i\n", atomic_read(&hard_iface->_name));\ - \ - batadv_hardif_free_ref(hard_iface); \ - return length; \ -} - -/* Use this, if you are going to set [name] in hard_iface to an - * unsigned integer value - */ -#define BATADV_ATTR_HIF_UINT(_name, _mode, _min, _max, _post_func) \ - static BATADV_ATTR_HIF_STORE_UINT(_name, _min, _max, _post_func)\ - static BATADV_ATTR_HIF_SHOW_UINT(_name) \ - static BATADV_ATTR(_name, _mode, batadv_show_##_name, \ - batadv_store_##_name) - - static int batadv_store_bool_attr(char *buff, size_t count, struct net_device *net_dev, const char *attr_name, atomic_t *attr) @@ -469,6 +421,9 @@ BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO | S_IWUSR, NULL); #ifdef CONFIG_BATMAN_ADV_BLA BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR, NULL); #endif +#ifdef CONFIG_BATMAN_ADV_DAT +BATADV_ATTR_SIF_BOOL(distributed_arp_table, S_IRUGO | S_IWUSR, NULL); +#endif BATADV_ATTR_SIF_BOOL(fragmentation, S_IRUGO | S_IWUSR, batadv_update_min_mtu); BATADV_ATTR_SIF_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL); static BATADV_ATTR(vis_mode, S_IRUGO | S_IWUSR, batadv_show_vis_mode, @@ -494,6 +449,9 @@ static struct batadv_attribute *batadv_mesh_attrs[] = { #ifdef CONFIG_BATMAN_ADV_BLA &batadv_attr_bridge_loop_avoidance, #endif +#ifdef CONFIG_BATMAN_ADV_DAT + &batadv_attr_distributed_arp_table, +#endif &batadv_attr_fragmentation, &batadv_attr_ap_isolation, &batadv_attr_vis_mode, @@ -730,7 +688,7 @@ int batadv_throw_uevent(struct batadv_priv *bat_priv, enum batadv_uev_type type, enum batadv_uev_action action, const char *data) { int ret = -ENOMEM; - struct batadv_hard_iface *primary_if = NULL; + struct batadv_hard_iface *primary_if; struct kobject *bat_kobj; char *uevent_env[4] = { NULL, NULL, NULL, NULL }; diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 112edd371b2f..9f5705fcf426 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -238,92 +238,134 @@ static int batadv_tt_local_init(struct batadv_priv *bat_priv) return 0; } +static void batadv_tt_global_free(struct batadv_priv *bat_priv, + struct batadv_tt_global_entry *tt_global, + const char *message) +{ + batadv_dbg(BATADV_DBG_TT, bat_priv, + "Deleting global tt entry %pM: %s\n", + tt_global->common.addr, message); + + batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt, + batadv_choose_orig, tt_global->common.addr); + batadv_tt_global_entry_free_ref(tt_global); + +} + void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, int ifindex) { struct batadv_priv *bat_priv = netdev_priv(soft_iface); - struct batadv_tt_local_entry *tt_local_entry = NULL; - struct batadv_tt_global_entry *tt_global_entry = NULL; + struct batadv_tt_local_entry *tt_local; + struct batadv_tt_global_entry *tt_global; struct hlist_head *head; struct hlist_node *node; struct batadv_tt_orig_list_entry *orig_entry; int hash_added; + bool roamed_back = false; - tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr); + tt_local = batadv_tt_local_hash_find(bat_priv, addr); + tt_global = batadv_tt_global_hash_find(bat_priv, addr); - if (tt_local_entry) { - tt_local_entry->last_seen = jiffies; - /* possibly unset the BATADV_TT_CLIENT_PENDING flag */ - tt_local_entry->common.flags &= ~BATADV_TT_CLIENT_PENDING; - goto out; + if (tt_local) { + tt_local->last_seen = jiffies; + if (tt_local->common.flags & BATADV_TT_CLIENT_PENDING) { + batadv_dbg(BATADV_DBG_TT, bat_priv, + "Re-adding pending client %pM\n", addr); + /* whatever the reason why the PENDING flag was set, + * this is a client which was enqueued to be removed in + * this orig_interval. Since it popped up again, the + * flag can be reset like it was never enqueued + */ + tt_local->common.flags &= ~BATADV_TT_CLIENT_PENDING; + goto add_event; + } + + if (tt_local->common.flags & BATADV_TT_CLIENT_ROAM) { + batadv_dbg(BATADV_DBG_TT, bat_priv, + "Roaming client %pM came back to its original location\n", + addr); + /* the ROAM flag is set because this client roamed away + * and the node got a roaming_advertisement message. Now + * that the client popped up again at its original + * location such flag can be unset + */ + tt_local->common.flags &= ~BATADV_TT_CLIENT_ROAM; + roamed_back = true; + } + goto check_roaming; } - tt_local_entry = kmalloc(sizeof(*tt_local_entry), GFP_ATOMIC); - if (!tt_local_entry) + tt_local = kmalloc(sizeof(*tt_local), GFP_ATOMIC); + if (!tt_local) goto out; batadv_dbg(BATADV_DBG_TT, bat_priv, "Creating new local tt entry: %pM (ttvn: %d)\n", addr, (uint8_t)atomic_read(&bat_priv->tt.vn)); - memcpy(tt_local_entry->common.addr, addr, ETH_ALEN); - tt_local_entry->common.flags = BATADV_NO_FLAGS; + memcpy(tt_local->common.addr, addr, ETH_ALEN); + tt_local->common.flags = BATADV_NO_FLAGS; if (batadv_is_wifi_iface(ifindex)) - tt_local_entry->common.flags |= BATADV_TT_CLIENT_WIFI; - atomic_set(&tt_local_entry->common.refcount, 2); - tt_local_entry->last_seen = jiffies; - tt_local_entry->common.added_at = tt_local_entry->last_seen; + tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; + atomic_set(&tt_local->common.refcount, 2); + tt_local->last_seen = jiffies; + tt_local->common.added_at = tt_local->last_seen; /* the batman interface mac address should never be purged */ if (batadv_compare_eth(addr, soft_iface->dev_addr)) - tt_local_entry->common.flags |= BATADV_TT_CLIENT_NOPURGE; + tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE; /* The local entry has to be marked as NEW to avoid to send it in * a full table response going out before the next ttvn increment * (consistency check) */ - tt_local_entry->common.flags |= BATADV_TT_CLIENT_NEW; + tt_local->common.flags |= BATADV_TT_CLIENT_NEW; hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt, - batadv_choose_orig, - &tt_local_entry->common, - &tt_local_entry->common.hash_entry); + batadv_choose_orig, &tt_local->common, + &tt_local->common.hash_entry); if (unlikely(hash_added != 0)) { /* remove the reference for the hash */ - batadv_tt_local_entry_free_ref(tt_local_entry); + batadv_tt_local_entry_free_ref(tt_local); goto out; } - batadv_tt_local_event(bat_priv, addr, tt_local_entry->common.flags); +add_event: + batadv_tt_local_event(bat_priv, addr, tt_local->common.flags); - /* remove address from global hash if present */ - tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr); - - /* Check whether it is a roaming! */ - if (tt_global_entry) { +check_roaming: + /* Check whether it is a roaming, but don't do anything if the roaming + * process has already been handled + */ + if (tt_global && !(tt_global->common.flags & BATADV_TT_CLIENT_ROAM)) { /* These node are probably going to update their tt table */ - head = &tt_global_entry->orig_list; + head = &tt_global->orig_list; rcu_read_lock(); hlist_for_each_entry_rcu(orig_entry, node, head, list) { - orig_entry->orig_node->tt_poss_change = true; - - batadv_send_roam_adv(bat_priv, - tt_global_entry->common.addr, + batadv_send_roam_adv(bat_priv, tt_global->common.addr, orig_entry->orig_node); } rcu_read_unlock(); - /* The global entry has to be marked as ROAMING and - * has to be kept for consistency purpose - */ - tt_global_entry->common.flags |= BATADV_TT_CLIENT_ROAM; - tt_global_entry->roam_at = jiffies; + if (roamed_back) { + batadv_tt_global_free(bat_priv, tt_global, + "Roaming canceled"); + tt_global = NULL; + } else { + /* The global entry has to be marked as ROAMING and + * has to be kept for consistency purpose + */ + tt_global->common.flags |= BATADV_TT_CLIENT_ROAM; + tt_global->roam_at = jiffies; + } } + out: - if (tt_local_entry) - batadv_tt_local_entry_free_ref(tt_local_entry); - if (tt_global_entry) - batadv_tt_global_entry_free_ref(tt_global_entry); + if (tt_local) + batadv_tt_local_entry_free_ref(tt_local); + if (tt_global) + batadv_tt_global_entry_free_ref(tt_global); } static void batadv_tt_realloc_packet_buff(unsigned char **packet_buff, @@ -434,22 +476,10 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) struct hlist_node *node; struct hlist_head *head; uint32_t i; - int ret = 0; - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - please specify interfaces to enable it\n", - net_dev->name); - goto out; - } - - if (primary_if->if_status != BATADV_IF_ACTIVE) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - primary interface not active\n", - net_dev->name); + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) goto out; - } seq_printf(seq, "Locally retrieved addresses (from %s) announced via TT (TTVN: %u):\n", @@ -479,7 +509,7 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) out: if (primary_if) batadv_hardif_free_ref(primary_if); - return ret; + return 0; } static void @@ -501,24 +531,57 @@ batadv_tt_local_set_pending(struct batadv_priv *bat_priv, tt_local_entry->common.addr, message); } -void batadv_tt_local_remove(struct batadv_priv *bat_priv, const uint8_t *addr, - const char *message, bool roaming) +/** + * batadv_tt_local_remove - logically remove an entry from the local table + * @bat_priv: the bat priv with all the soft interface information + * @addr: the MAC address of the client to remove + * @message: message to append to the log on deletion + * @roaming: true if the deletion is due to a roaming event + * + * Returns the flags assigned to the local entry before being deleted + */ +uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv, + const uint8_t *addr, const char *message, + bool roaming) { - struct batadv_tt_local_entry *tt_local_entry = NULL; - uint16_t flags; + struct batadv_tt_local_entry *tt_local_entry; + uint16_t flags, curr_flags = BATADV_NO_FLAGS; tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr); if (!tt_local_entry) goto out; + curr_flags = tt_local_entry->common.flags; + flags = BATADV_TT_CLIENT_DEL; - if (roaming) + /* if this global entry addition is due to a roaming, the node has to + * mark the local entry as "roamed" in order to correctly reroute + * packets later + */ + if (roaming) { flags |= BATADV_TT_CLIENT_ROAM; + /* mark the local client as ROAMed */ + tt_local_entry->common.flags |= BATADV_TT_CLIENT_ROAM; + } + + if (!(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW)) { + batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags, + message); + goto out; + } + /* if this client has been added right now, it is possible to + * immediately purge it + */ + batadv_tt_local_event(bat_priv, tt_local_entry->common.addr, + curr_flags | BATADV_TT_CLIENT_DEL); + hlist_del_rcu(&tt_local_entry->common.hash_entry); + batadv_tt_local_entry_free_ref(tt_local_entry); - batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags, message); out: if (tt_local_entry) batadv_tt_local_entry_free_ref(tt_local_entry); + + return curr_flags; } static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv, @@ -721,12 +784,23 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv, const unsigned char *tt_addr, uint8_t flags, uint8_t ttvn) { - struct batadv_tt_global_entry *tt_global_entry = NULL; + struct batadv_tt_global_entry *tt_global_entry; + struct batadv_tt_local_entry *tt_local_entry; int ret = 0; int hash_added; struct batadv_tt_common_entry *common; + uint16_t local_flags; tt_global_entry = batadv_tt_global_hash_find(bat_priv, tt_addr); + tt_local_entry = batadv_tt_local_hash_find(bat_priv, tt_addr); + + /* if the node already has a local client for this entry, it has to wait + * for a roaming advertisement instead of manually messing up the global + * table + */ + if ((flags & BATADV_TT_CLIENT_TEMP) && tt_local_entry && + !(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW)) + goto out; if (!tt_global_entry) { tt_global_entry = kzalloc(sizeof(*tt_global_entry), GFP_ATOMIC); @@ -738,6 +812,12 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv, common->flags = flags; tt_global_entry->roam_at = 0; + /* node must store current time in case of roaming. This is + * needed to purge this entry out on timeout (if nobody claims + * it) + */ + if (flags & BATADV_TT_CLIENT_ROAM) + tt_global_entry->roam_at = jiffies; atomic_set(&common->refcount, 2); common->added_at = jiffies; @@ -755,19 +835,31 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv, goto out_remove; } } else { + common = &tt_global_entry->common; /* If there is already a global entry, we can use this one for * our processing. - * But if we are trying to add a temporary client we can exit - * directly because the temporary information should never - * override any already known client state (whatever it is) + * But if we are trying to add a temporary client then here are + * two options at this point: + * 1) the global client is not a temporary client: the global + * client has to be left as it is, temporary information + * should never override any already known client state + * 2) the global client is a temporary client: purge the + * originator list and add the new one orig_entry */ - if (flags & BATADV_TT_CLIENT_TEMP) - goto out; + if (flags & BATADV_TT_CLIENT_TEMP) { + if (!(common->flags & BATADV_TT_CLIENT_TEMP)) + goto out; + if (batadv_tt_global_entry_has_orig(tt_global_entry, + orig_node)) + goto out_remove; + batadv_tt_global_del_orig_list(tt_global_entry); + goto add_orig_entry; + } /* if the client was temporary added before receiving the first * OGM announcing it, we have to clear the TEMP flag */ - tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_TEMP; + common->flags &= ~BATADV_TT_CLIENT_TEMP; /* If there is the BATADV_TT_CLIENT_ROAM flag set, there is only * one originator left in the list and we previously received a @@ -776,28 +868,40 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv, * We should first delete the old originator before adding the * new one. */ - if (tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM) { + if (common->flags & BATADV_TT_CLIENT_ROAM) { batadv_tt_global_del_orig_list(tt_global_entry); - tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_ROAM; + common->flags &= ~BATADV_TT_CLIENT_ROAM; tt_global_entry->roam_at = 0; } } +add_orig_entry: /* add the new orig_entry (if needed) or update it */ batadv_tt_global_orig_entry_add(tt_global_entry, orig_node, ttvn); batadv_dbg(BATADV_DBG_TT, bat_priv, "Creating new global tt entry: %pM (via %pM)\n", - tt_global_entry->common.addr, orig_node->orig); + common->addr, orig_node->orig); + ret = 1; out_remove: + /* remove address from local hash if present */ - batadv_tt_local_remove(bat_priv, tt_global_entry->common.addr, - "global tt received", - flags & BATADV_TT_CLIENT_ROAM); - ret = 1; + local_flags = batadv_tt_local_remove(bat_priv, tt_addr, + "global tt received", + !!(flags & BATADV_TT_CLIENT_ROAM)); + tt_global_entry->common.flags |= local_flags & BATADV_TT_CLIENT_WIFI; + + if (!(flags & BATADV_TT_CLIENT_ROAM)) + /* this is a normal global add. Therefore the client is not in a + * roaming state anymore. + */ + tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_ROAM; + out: if (tt_global_entry) batadv_tt_global_entry_free_ref(tt_global_entry); + if (tt_local_entry) + batadv_tt_local_entry_free_ref(tt_local_entry); return ret; } @@ -842,22 +946,10 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset) struct hlist_node *node; struct hlist_head *head; uint32_t i; - int ret = 0; - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - please specify interfaces to enable it\n", - net_dev->name); + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) goto out; - } - - if (primary_if->if_status != BATADV_IF_ACTIVE) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - primary interface not active\n", - net_dev->name); - goto out; - } seq_printf(seq, "Globally announced TT entries received via the mesh %s\n", @@ -881,7 +973,7 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset) out: if (primary_if) batadv_hardif_free_ref(primary_if); - return ret; + return 0; } /* deletes the orig list of a tt_global_entry */ @@ -927,21 +1019,6 @@ batadv_tt_global_del_orig_entry(struct batadv_priv *bat_priv, spin_unlock_bh(&tt_global_entry->list_lock); } -static void -batadv_tt_global_del_struct(struct batadv_priv *bat_priv, - struct batadv_tt_global_entry *tt_global_entry, - const char *message) -{ - batadv_dbg(BATADV_DBG_TT, bat_priv, - "Deleting global tt entry %pM: %s\n", - tt_global_entry->common.addr, message); - - batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt, - batadv_choose_orig, tt_global_entry->common.addr); - batadv_tt_global_entry_free_ref(tt_global_entry); - -} - /* If the client is to be deleted, we check if it is the last origantor entry * within tt_global entry. If yes, we set the BATADV_TT_CLIENT_ROAM flag and the * timer, otherwise we simply remove the originator scheduled for deletion. @@ -990,7 +1067,7 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv, const unsigned char *addr, const char *message, bool roaming) { - struct batadv_tt_global_entry *tt_global_entry = NULL; + struct batadv_tt_global_entry *tt_global_entry; struct batadv_tt_local_entry *local_entry = NULL; tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr); @@ -1002,8 +1079,8 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv, orig_node, message); if (hlist_empty(&tt_global_entry->orig_list)) - batadv_tt_global_del_struct(bat_priv, tt_global_entry, - message); + batadv_tt_global_free(bat_priv, tt_global_entry, + message); goto out; } @@ -1026,7 +1103,7 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv, if (local_entry) { /* local entry exists, case 2: client roamed to us. */ batadv_tt_global_del_orig_list(tt_global_entry); - batadv_tt_global_del_struct(bat_priv, tt_global_entry, message); + batadv_tt_global_free(bat_priv, tt_global_entry, message); } else /* no local entry exists, case 1: check for roaming */ batadv_tt_global_del_roaming(bat_priv, tt_global_entry, @@ -1205,7 +1282,8 @@ struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv, if (src && atomic_read(&bat_priv->ap_isolation)) { tt_local_entry = batadv_tt_local_hash_find(bat_priv, src); - if (!tt_local_entry) + if (!tt_local_entry || + (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)) goto out; } @@ -1471,11 +1549,11 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn, tt_tot = tt_len / sizeof(struct batadv_tt_change); len = tt_query_size + tt_len; - skb = dev_alloc_skb(len + ETH_HLEN); + skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN); if (!skb) goto out; - skb_reserve(skb, ETH_HLEN); + skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN); tt_response = (struct batadv_tt_query_packet *)skb_put(skb, len); tt_response->ttvn = ttvn; @@ -1537,11 +1615,11 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv, if (!tt_req_node) goto out; - skb = dev_alloc_skb(sizeof(*tt_request) + ETH_HLEN); + skb = dev_alloc_skb(sizeof(*tt_request) + ETH_HLEN + NET_IP_ALIGN); if (!skb) goto out; - skb_reserve(skb, ETH_HLEN); + skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN); tt_req_len = sizeof(*tt_request); tt_request = (struct batadv_tt_query_packet *)skb_put(skb, tt_req_len); @@ -1592,7 +1670,7 @@ static bool batadv_send_other_tt_response(struct batadv_priv *bat_priv, struct batadv_tt_query_packet *tt_request) { - struct batadv_orig_node *req_dst_orig_node = NULL; + struct batadv_orig_node *req_dst_orig_node; struct batadv_orig_node *res_dst_orig_node = NULL; struct batadv_neigh_node *neigh_node = NULL; struct batadv_hard_iface *primary_if = NULL; @@ -1652,11 +1730,11 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv, tt_tot = tt_len / sizeof(struct batadv_tt_change); len = sizeof(*tt_response) + tt_len; - skb = dev_alloc_skb(len + ETH_HLEN); + skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN); if (!skb) goto unlock; - skb_reserve(skb, ETH_HLEN); + skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN); packet_pos = skb_put(skb, len); tt_response = (struct batadv_tt_query_packet *)packet_pos; tt_response->ttvn = req_ttvn; @@ -1727,7 +1805,7 @@ static bool batadv_send_my_tt_response(struct batadv_priv *bat_priv, struct batadv_tt_query_packet *tt_request) { - struct batadv_orig_node *orig_node = NULL; + struct batadv_orig_node *orig_node; struct batadv_neigh_node *neigh_node = NULL; struct batadv_hard_iface *primary_if = NULL; uint8_t my_ttvn, req_ttvn, ttvn; @@ -1779,11 +1857,11 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv, tt_tot = tt_len / sizeof(struct batadv_tt_change); len = sizeof(*tt_response) + tt_len; - skb = dev_alloc_skb(len + ETH_HLEN); + skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN); if (!skb) goto unlock; - skb_reserve(skb, ETH_HLEN); + skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN); packet_pos = skb_put(skb, len); tt_response = (struct batadv_tt_query_packet *)packet_pos; tt_response->ttvn = req_ttvn; @@ -1893,7 +1971,7 @@ static void _batadv_tt_update_changes(struct batadv_priv *bat_priv, static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv, struct batadv_tt_query_packet *tt_response) { - struct batadv_orig_node *orig_node = NULL; + struct batadv_orig_node *orig_node; orig_node = batadv_orig_hash_find(bat_priv, tt_response->src); if (!orig_node) @@ -1935,7 +2013,7 @@ static void batadv_tt_update_changes(struct batadv_priv *bat_priv, bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr) { - struct batadv_tt_local_entry *tt_local_entry = NULL; + struct batadv_tt_local_entry *tt_local_entry; bool ret = false; tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr); @@ -1944,7 +2022,8 @@ bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr) /* Check if the client has been logically deleted (but is kept for * consistency purpose) */ - if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING) + if ((tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING) || + (tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM)) goto out; ret = true; out: @@ -1995,10 +2074,6 @@ void batadv_handle_tt_response(struct batadv_priv *bat_priv, /* Recalculate the CRC for this orig_node and store it */ orig_node->tt_crc = batadv_tt_global_crc(bat_priv, orig_node); - /* Roaming phase is over: tables are in sync again. I can - * unset the flag - */ - orig_node->tt_poss_change = false; out: if (orig_node) batadv_orig_node_free_ref(orig_node); @@ -2117,11 +2192,11 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client, if (!batadv_tt_check_roam_count(bat_priv, client)) goto out; - skb = dev_alloc_skb(sizeof(*roam_adv_packet) + ETH_HLEN); + skb = dev_alloc_skb(sizeof(*roam_adv_packet) + ETH_HLEN + NET_IP_ALIGN); if (!skb) goto out; - skb_reserve(skb, ETH_HLEN); + skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN); roam_adv_packet = (struct batadv_roam_adv_packet *)skb_put(skb, len); @@ -2289,7 +2364,6 @@ static int batadv_tt_commit_changes(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_TT, bat_priv, "Local changes committed, updating to ttvn %u\n", (uint8_t)atomic_read(&bat_priv->tt.vn)); - bat_priv->tt.poss_change = false; /* reset the sending counter */ atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX); @@ -2401,11 +2475,6 @@ void batadv_tt_update_orig(struct batadv_priv *bat_priv, */ if (orig_node->tt_crc != tt_crc) goto request_table; - - /* Roaming phase is over: tables are in sync again. I can - * unset the flag - */ - orig_node->tt_poss_change = false; } else { /* if we missed more than one change or our tables are not * in sync anymore -> request fresh tt data @@ -2438,12 +2507,38 @@ bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv, if (!tt_global_entry) goto out; - ret = tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM; + ret = !!(tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM); batadv_tt_global_entry_free_ref(tt_global_entry); out: return ret; } +/** + * batadv_tt_local_client_is_roaming - tells whether the client is roaming + * @bat_priv: the bat priv with all the soft interface information + * @addr: the MAC address of the local client to query + * + * Returns true if the local client is known to be roaming (it is not served by + * this node anymore) or not. If yes, the client is still present in the table + * to keep the latter consistent with the node TTVN + */ +bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv, + uint8_t *addr) +{ + struct batadv_tt_local_entry *tt_local_entry; + bool ret = false; + + tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr); + if (!tt_local_entry) + goto out; + + ret = tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM; + batadv_tt_local_entry_free_ref(tt_local_entry); +out: + return ret; + +} + bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, const unsigned char *addr) diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h index 811fffd4760c..46d4451a59ee 100644 --- a/net/batman-adv/translation-table.h +++ b/net/batman-adv/translation-table.h @@ -24,9 +24,9 @@ int batadv_tt_len(int changes_num); int batadv_tt_init(struct batadv_priv *bat_priv); void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, int ifindex); -void batadv_tt_local_remove(struct batadv_priv *bat_priv, - const uint8_t *addr, const char *message, - bool roaming); +uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv, + const uint8_t *addr, const char *message, + bool roaming); int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset); void batadv_tt_global_add_orig(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, @@ -59,6 +59,8 @@ int batadv_tt_append_diff(struct batadv_priv *bat_priv, int packet_min_len); bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv, uint8_t *addr); +bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv, + uint8_t *addr); bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, const unsigned char *addr); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index ac1e07a80454..7b3d0d7ef06a 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -28,20 +28,41 @@ (ETH_HLEN + max(sizeof(struct batadv_unicast_packet), \ sizeof(struct batadv_bcast_packet))) +#ifdef CONFIG_BATMAN_ADV_DAT + +/* batadv_dat_addr_t is the type used for all DHT addresses. If it is changed, + * BATADV_DAT_ADDR_MAX is changed as well. + * + * *Please be careful: batadv_dat_addr_t must be UNSIGNED* + */ +#define batadv_dat_addr_t uint16_t + +#endif /* CONFIG_BATMAN_ADV_DAT */ + +/** + * struct batadv_hard_iface_bat_iv - per hard interface B.A.T.M.A.N. IV data + * @ogm_buff: buffer holding the OGM packet + * @ogm_buff_len: length of the OGM packet buffer + * @ogm_seqno: OGM sequence number - used to identify each OGM + */ +struct batadv_hard_iface_bat_iv { + unsigned char *ogm_buff; + int ogm_buff_len; + atomic_t ogm_seqno; +}; + struct batadv_hard_iface { struct list_head list; int16_t if_num; char if_status; struct net_device *net_dev; - atomic_t seqno; atomic_t frag_seqno; - unsigned char *packet_buff; - int packet_len; struct kobject *hardif_obj; atomic_t refcount; struct packet_type batman_adv_ptype; struct net_device *soft_iface; struct rcu_head rcu; + struct batadv_hard_iface_bat_iv bat_iv; }; /** @@ -63,6 +84,9 @@ struct batadv_orig_node { uint8_t orig[ETH_ALEN]; uint8_t primary_addr[ETH_ALEN]; struct batadv_neigh_node __rcu *router; /* rcu protected pointer */ +#ifdef CONFIG_BATMAN_ADV_DAT + batadv_dat_addr_t dat_addr; +#endif unsigned long *bcast_own; uint8_t *bcast_own_sum; unsigned long last_seen; @@ -77,13 +101,6 @@ struct batadv_orig_node { spinlock_t tt_buff_lock; /* protects tt_buff */ atomic_t tt_size; bool tt_initialised; - /* The tt_poss_change flag is used to detect an ongoing roaming phase. - * If true, then I sent a Roaming_adv to this orig_node and I have to - * inspect every packet directed to it to check whether it is still - * the true destination or not. This flag will be reset to false as - * soon as I receive a new TTVN from this orig_node - */ - bool tt_poss_change; uint32_t last_real_seqno; uint8_t last_ttl; DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); @@ -162,6 +179,13 @@ enum batadv_counters { BATADV_CNT_TT_RESPONSE_RX, BATADV_CNT_TT_ROAM_ADV_TX, BATADV_CNT_TT_ROAM_ADV_RX, +#ifdef CONFIG_BATMAN_ADV_DAT + BATADV_CNT_DAT_GET_TX, + BATADV_CNT_DAT_GET_RX, + BATADV_CNT_DAT_PUT_TX, + BATADV_CNT_DAT_PUT_RX, + BATADV_CNT_DAT_CACHED_REPLY_TX, +#endif BATADV_CNT_NUM, }; @@ -181,7 +205,6 @@ struct batadv_priv_tt { atomic_t vn; atomic_t ogm_append_cnt; atomic_t local_changes; - bool poss_change; struct list_head changes_list; struct batadv_hashtable *local_hash; struct batadv_hashtable *global_hash; @@ -228,6 +251,20 @@ struct batadv_priv_vis { struct batadv_vis_info *my_info; }; +/** + * struct batadv_priv_dat - per mesh interface DAT private data + * @addr: node DAT address + * @hash: hashtable representing the local ARP cache + * @work: work queue callback item for cache purging + */ +#ifdef CONFIG_BATMAN_ADV_DAT +struct batadv_priv_dat { + batadv_dat_addr_t addr; + struct batadv_hashtable *hash; + struct delayed_work work; +}; +#endif + struct batadv_priv { atomic_t mesh_state; struct net_device_stats stats; @@ -237,6 +274,9 @@ struct batadv_priv { atomic_t fragmentation; /* boolean */ atomic_t ap_isolation; /* boolean */ atomic_t bridge_loop_avoidance; /* boolean */ +#ifdef CONFIG_BATMAN_ADV_DAT + atomic_t distributed_arp_table; /* boolean */ +#endif atomic_t vis_mode; /* VIS_TYPE_* */ atomic_t gw_mode; /* GW_MODE_* */ atomic_t gw_sel_class; /* uint */ @@ -255,7 +295,7 @@ struct batadv_priv { struct hlist_head forw_bcast_list; struct batadv_hashtable *orig_hash; spinlock_t forw_bat_list_lock; /* protects forw_bat_list */ - spinlock_t forw_bcast_list_lock; /* protects */ + spinlock_t forw_bcast_list_lock; /* protects forw_bcast_list */ struct delayed_work orig_work; struct batadv_hard_iface __rcu *primary_if; /* rcu protected pointer */ struct batadv_algo_ops *bat_algo_ops; @@ -265,6 +305,9 @@ struct batadv_priv { struct batadv_priv_gw gw; struct batadv_priv_tt tt; struct batadv_priv_vis vis; +#ifdef CONFIG_BATMAN_ADV_DAT + struct batadv_priv_dat dat; +#endif }; struct batadv_socket_client { @@ -318,6 +361,7 @@ struct batadv_backbone_gw { struct hlist_node hash_entry; struct batadv_priv *bat_priv; unsigned long lasttime; /* last time we heard of this backbone gw */ + atomic_t wait_periods; atomic_t request_sent; atomic_t refcount; struct rcu_head rcu; @@ -437,4 +481,36 @@ struct batadv_algo_ops { void (*bat_ogm_emit)(struct batadv_forw_packet *forw_packet); }; +/** + * struct batadv_dat_entry - it is a single entry of batman-adv ARP backend. It + * is used to stored ARP entries needed for the global DAT cache + * @ip: the IPv4 corresponding to this DAT/ARP entry + * @mac_addr: the MAC address associated to the stored IPv4 + * @last_update: time in jiffies when this entry was refreshed last time + * @hash_entry: hlist node for batadv_priv_dat::hash + * @refcount: number of contexts the object is used + * @rcu: struct used for freeing in an RCU-safe manner + */ +struct batadv_dat_entry { + __be32 ip; + uint8_t mac_addr[ETH_ALEN]; + unsigned long last_update; + struct hlist_node hash_entry; + atomic_t refcount; + struct rcu_head rcu; +}; + +/** + * struct batadv_dat_candidate - candidate destination for DAT operations + * @type: the type of the selected candidate. It can one of the following: + * - BATADV_DAT_CANDIDATE_NOT_FOUND + * - BATADV_DAT_CANDIDATE_ORIG + * @orig_node: if type is BATADV_DAT_CANDIDATE_ORIG this field points to the + * corresponding originator node structure + */ +struct batadv_dat_candidate { + int type; + struct batadv_orig_node *orig_node; +}; + #endif /* _NET_BATMAN_ADV_TYPES_H_ */ diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c index f39723281ca1..c9a1f6523c36 100644 --- a/net/batman-adv/unicast.c +++ b/net/batman-adv/unicast.c @@ -291,7 +291,111 @@ out: return ret; } -int batadv_unicast_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv) +/** + * batadv_unicast_push_and_fill_skb - extends the buffer and initializes the + * common fields for unicast packets + * @skb: packet + * @hdr_size: amount of bytes to push at the beginning of the skb + * @orig_node: the destination node + * + * Returns false if the buffer extension was not possible or true otherwise + */ +static bool batadv_unicast_push_and_fill_skb(struct sk_buff *skb, int hdr_size, + struct batadv_orig_node *orig_node) +{ + struct batadv_unicast_packet *unicast_packet; + uint8_t ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); + + if (batadv_skb_head_push(skb, hdr_size) < 0) + return false; + + unicast_packet = (struct batadv_unicast_packet *)skb->data; + unicast_packet->header.version = BATADV_COMPAT_VERSION; + /* batman packet type: unicast */ + unicast_packet->header.packet_type = BATADV_UNICAST; + /* set unicast ttl */ + unicast_packet->header.ttl = BATADV_TTL; + /* copy the destination for faster routing */ + memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); + /* set the destination tt version number */ + unicast_packet->ttvn = ttvn; + + return true; +} + +/** + * batadv_unicast_prepare_skb - encapsulate an skb with a unicast header + * @skb: the skb containing the payload to encapsulate + * @orig_node: the destination node + * + * Returns false if the payload could not be encapsulated or true otherwise + */ +static bool batadv_unicast_prepare_skb(struct sk_buff *skb, + struct batadv_orig_node *orig_node) +{ + size_t uni_size = sizeof(struct batadv_unicast_packet); + return batadv_unicast_push_and_fill_skb(skb, uni_size, orig_node); +} + +/** + * batadv_unicast_4addr_prepare_skb - encapsulate an skb with a unicast4addr + * header + * @bat_priv: the bat priv with all the soft interface information + * @skb: the skb containing the payload to encapsulate + * @orig_node: the destination node + * @packet_subtype: the batman 4addr packet subtype to use + * + * Returns false if the payload could not be encapsulated or true otherwise + */ +bool batadv_unicast_4addr_prepare_skb(struct batadv_priv *bat_priv, + struct sk_buff *skb, + struct batadv_orig_node *orig, + int packet_subtype) +{ + struct batadv_hard_iface *primary_if; + struct batadv_unicast_4addr_packet *unicast_4addr_packet; + bool ret = false; + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + /* pull the header space and fill the unicast_packet substructure. + * We can do that because the first member of the unicast_4addr_packet + * is of type struct unicast_packet + */ + if (!batadv_unicast_push_and_fill_skb(skb, + sizeof(*unicast_4addr_packet), + orig)) + goto out; + + unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data; + unicast_4addr_packet->u.header.packet_type = BATADV_UNICAST_4ADDR; + memcpy(unicast_4addr_packet->src, primary_if->net_dev->dev_addr, + ETH_ALEN); + unicast_4addr_packet->subtype = packet_subtype; + unicast_4addr_packet->reserved = 0; + + ret = true; +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + return ret; +} + +/** + * batadv_unicast_generic_send_skb - send an skb as unicast + * @bat_priv: the bat priv with all the soft interface information + * @skb: payload to send + * @packet_type: the batman unicast packet type to use + * @packet_subtype: the batman packet subtype. It is ignored if packet_type is + * not BATADV_UNICAT_4ADDR + * + * Returns 1 in case of error or 0 otherwise + */ +int batadv_unicast_generic_send_skb(struct batadv_priv *bat_priv, + struct sk_buff *skb, int packet_type, + int packet_subtype) { struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct batadv_unicast_packet *unicast_packet; @@ -324,21 +428,23 @@ find_router: if (!neigh_node) goto out; - if (batadv_skb_head_push(skb, sizeof(*unicast_packet)) < 0) + switch (packet_type) { + case BATADV_UNICAST: + batadv_unicast_prepare_skb(skb, orig_node); + break; + case BATADV_UNICAST_4ADDR: + batadv_unicast_4addr_prepare_skb(bat_priv, skb, orig_node, + packet_subtype); + break; + default: + /* this function supports UNICAST and UNICAST_4ADDR only. It + * should never be invoked with any other packet type + */ goto out; + } unicast_packet = (struct batadv_unicast_packet *)skb->data; - unicast_packet->header.version = BATADV_COMPAT_VERSION; - /* batman packet type: unicast */ - unicast_packet->header.packet_type = BATADV_UNICAST; - /* set unicast ttl */ - unicast_packet->header.ttl = BATADV_TTL; - /* copy the destination for faster routing */ - memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); - /* set the destination tt version number */ - unicast_packet->ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); - /* inform the destination node that we are still missing a correct route * for this client. The destination will receive this packet and will * try to reroute it because the ttvn contained in the header is less @@ -348,7 +454,9 @@ find_router: unicast_packet->ttvn = unicast_packet->ttvn - 1; dev_mtu = neigh_node->if_incoming->net_dev->mtu; - if (atomic_read(&bat_priv->fragmentation) && + /* fragmentation mechanism only works for UNICAST (now) */ + if (packet_type == BATADV_UNICAST && + atomic_read(&bat_priv->fragmentation) && data_len + sizeof(*unicast_packet) > dev_mtu) { /* send frag skb decreases ttl */ unicast_packet->header.ttl++; @@ -360,7 +468,6 @@ find_router: batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); ret = 0; - goto out; out: if (neigh_node) diff --git a/net/batman-adv/unicast.h b/net/batman-adv/unicast.h index 1c46e2eb1ef9..61abba58bd8f 100644 --- a/net/batman-adv/unicast.h +++ b/net/batman-adv/unicast.h @@ -29,10 +29,44 @@ int batadv_frag_reassemble_skb(struct sk_buff *skb, struct batadv_priv *bat_priv, struct sk_buff **new_skb); void batadv_frag_list_free(struct list_head *head); -int batadv_unicast_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv); int batadv_frag_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv, struct batadv_hard_iface *hard_iface, const uint8_t dstaddr[]); +bool batadv_unicast_4addr_prepare_skb(struct batadv_priv *bat_priv, + struct sk_buff *skb, + struct batadv_orig_node *orig_node, + int packet_subtype); +int batadv_unicast_generic_send_skb(struct batadv_priv *bat_priv, + struct sk_buff *skb, int packet_type, + int packet_subtype); + + +/** + * batadv_unicast_send_skb - send the skb encapsulated in a unicast packet + * @bat_priv: the bat priv with all the soft interface information + * @skb: the payload to send + */ +static inline int batadv_unicast_send_skb(struct batadv_priv *bat_priv, + struct sk_buff *skb) +{ + return batadv_unicast_generic_send_skb(bat_priv, skb, BATADV_UNICAST, + 0); +} + +/** + * batadv_unicast_send_skb - send the skb encapsulated in a unicast4addr packet + * @bat_priv: the bat priv with all the soft interface information + * @skb: the payload to send + * @packet_subtype: the batman 4addr packet subtype to use + */ +static inline int batadv_unicast_4addr_send_skb(struct batadv_priv *bat_priv, + struct sk_buff *skb, + int packet_subtype) +{ + return batadv_unicast_generic_send_skb(bat_priv, skb, + BATADV_UNICAST_4ADDR, + packet_subtype); +} static inline int batadv_frag_can_reassemble(const struct sk_buff *skb, int mtu) { diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index 5abd1454fb07..ad14a6c91d6a 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@ -396,12 +396,12 @@ batadv_add_packet(struct batadv_priv *bat_priv, return NULL; len = sizeof(*packet) + vis_info_len; - info->skb_packet = dev_alloc_skb(len + ETH_HLEN); + info->skb_packet = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN); if (!info->skb_packet) { kfree(info); return NULL; } - skb_reserve(info->skb_packet, ETH_HLEN); + skb_reserve(info->skb_packet, ETH_HLEN + NET_IP_ALIGN); packet = (struct batadv_vis_packet *)skb_put(info->skb_packet, len); kref_init(&info->refcount); @@ -873,12 +873,13 @@ int batadv_vis_init(struct batadv_priv *bat_priv) if (!bat_priv->vis.my_info) goto err; - len = sizeof(*packet) + BATADV_MAX_VIS_PACKET_SIZE + ETH_HLEN; + len = sizeof(*packet) + BATADV_MAX_VIS_PACKET_SIZE; + len += ETH_HLEN + NET_IP_ALIGN; bat_priv->vis.my_info->skb_packet = dev_alloc_skb(len); if (!bat_priv->vis.my_info->skb_packet) goto free_info; - skb_reserve(bat_priv->vis.my_info->skb_packet, ETH_HLEN); + skb_reserve(bat_priv->vis.my_info->skb_packet, ETH_HLEN + NET_IP_ALIGN); tmp_skb = bat_priv->vis.my_info->skb_packet; packet = (struct batadv_vis_packet *)skb_put(tmp_skb, sizeof(*packet)); diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 3537d385035e..1c11d0dcd863 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -11,6 +11,7 @@ menuconfig BT select CRYPTO_BLKCIPHER select CRYPTO_AES select CRYPTO_ECB + select CRYPTO_SHA256 help Bluetooth is low-cost, low-power, short-range wireless technology. It was designed as a replacement for cables and other short-range diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index fa6d94a4602a..dea6a287daca 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -10,4 +10,4 @@ obj-$(CONFIG_BT_HIDP) += hidp/ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ - a2mp.o + a2mp.o amp.o diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 0760d1fed6f0..d5136cfb57e2 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -16,6 +16,11 @@ #include <net/bluetooth/hci_core.h> #include <net/bluetooth/l2cap.h> #include <net/bluetooth/a2mp.h> +#include <net/bluetooth/amp.h> + +/* Global AMP Manager list */ +LIST_HEAD(amp_mgr_list); +DEFINE_MUTEX(amp_mgr_list_lock); /* A2MP build & send command helper functions */ static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data) @@ -37,8 +42,7 @@ static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data) return cmd; } -static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, - void *data) +void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data) { struct l2cap_chan *chan = mgr->a2mp_chan; struct a2mp_cmd *cmd; @@ -63,6 +67,14 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, kfree(cmd); } +u8 __next_ident(struct amp_mgr *mgr) +{ + if (++mgr->ident == 0) + mgr->ident = 1; + + return mgr->ident; +} + static inline void __a2mp_cl_bredr(struct a2mp_cl *cl) { cl->id = 0; @@ -161,6 +173,83 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb, return 0; } +static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb, + struct a2mp_cmd *hdr) +{ + struct a2mp_discov_rsp *rsp = (void *) skb->data; + u16 len = le16_to_cpu(hdr->len); + struct a2mp_cl *cl; + u16 ext_feat; + bool found = false; + + if (len < sizeof(*rsp)) + return -EINVAL; + + len -= sizeof(*rsp); + skb_pull(skb, sizeof(*rsp)); + + ext_feat = le16_to_cpu(rsp->ext_feat); + + BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(rsp->mtu), ext_feat); + + /* check that packet is not broken for now */ + while (ext_feat & A2MP_FEAT_EXT) { + if (len < sizeof(ext_feat)) + return -EINVAL; + + ext_feat = get_unaligned_le16(skb->data); + BT_DBG("efm 0x%4.4x", ext_feat); + len -= sizeof(ext_feat); + skb_pull(skb, sizeof(ext_feat)); + } + + cl = (void *) skb->data; + while (len >= sizeof(*cl)) { + BT_DBG("Remote AMP id %d type %d status %d", cl->id, cl->type, + cl->status); + + if (cl->id != HCI_BREDR_ID && cl->type == HCI_AMP) { + struct a2mp_info_req req; + + found = true; + req.id = cl->id; + a2mp_send(mgr, A2MP_GETINFO_REQ, __next_ident(mgr), + sizeof(req), &req); + } + + len -= sizeof(*cl); + cl = (void *) skb_pull(skb, sizeof(*cl)); + } + + /* Fall back to L2CAP init sequence */ + if (!found) { + struct l2cap_conn *conn = mgr->l2cap_conn; + struct l2cap_chan *chan; + + mutex_lock(&conn->chan_lock); + + list_for_each_entry(chan, &conn->chan_l, list) { + + BT_DBG("chan %p state %s", chan, + state_to_string(chan->state)); + + if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) + continue; + + l2cap_chan_lock(chan); + + if (chan->state == BT_CONNECT) + l2cap_send_conn_req(chan); + + l2cap_chan_unlock(chan); + } + + mutex_unlock(&conn->chan_lock); + } + + return 0; +} + static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { @@ -181,7 +270,6 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { struct a2mp_info_req *req = (void *) skb->data; - struct a2mp_info_rsp rsp; struct hci_dev *hdev; if (le16_to_cpu(hdr->len) < sizeof(*req)) @@ -189,53 +277,93 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, BT_DBG("id %d", req->id); - rsp.id = req->id; - rsp.status = A2MP_STATUS_INVALID_CTRL_ID; - hdev = hci_dev_get(req->id); - if (hdev && hdev->amp_type != HCI_BREDR) { - rsp.status = 0; - rsp.total_bw = cpu_to_le32(hdev->amp_total_bw); - rsp.max_bw = cpu_to_le32(hdev->amp_max_bw); - rsp.min_latency = cpu_to_le32(hdev->amp_min_latency); - rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap); - rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size); + if (!hdev || hdev->dev_type != HCI_AMP) { + struct a2mp_info_rsp rsp; + + rsp.id = req->id; + rsp.status = A2MP_STATUS_INVALID_CTRL_ID; + + a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), + &rsp); + + goto done; } + mgr->state = READ_LOC_AMP_INFO; + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); + +done: if (hdev) hci_dev_put(hdev); - a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), &rsp); - skb_pull(skb, sizeof(*req)); return 0; } +static int a2mp_getinfo_rsp(struct amp_mgr *mgr, struct sk_buff *skb, + struct a2mp_cmd *hdr) +{ + struct a2mp_info_rsp *rsp = (struct a2mp_info_rsp *) skb->data; + struct a2mp_amp_assoc_req req; + struct amp_ctrl *ctrl; + + if (le16_to_cpu(hdr->len) < sizeof(*rsp)) + return -EINVAL; + + BT_DBG("id %d status 0x%2.2x", rsp->id, rsp->status); + + if (rsp->status) + return -EINVAL; + + ctrl = amp_ctrl_add(mgr, rsp->id); + if (!ctrl) + return -ENOMEM; + + req.id = rsp->id; + a2mp_send(mgr, A2MP_GETAMPASSOC_REQ, __next_ident(mgr), sizeof(req), + &req); + + skb_pull(skb, sizeof(*rsp)); + return 0; +} + static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { struct a2mp_amp_assoc_req *req = (void *) skb->data; struct hci_dev *hdev; + struct amp_mgr *tmp; if (le16_to_cpu(hdr->len) < sizeof(*req)) return -EINVAL; BT_DBG("id %d", req->id); + /* Make sure that other request is not processed */ + tmp = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC); + hdev = hci_dev_get(req->id); - if (!hdev || hdev->amp_type == HCI_BREDR) { + if (!hdev || hdev->amp_type == HCI_BREDR || tmp) { struct a2mp_amp_assoc_rsp rsp; rsp.id = req->id; - rsp.status = A2MP_STATUS_INVALID_CTRL_ID; + + if (tmp) { + rsp.status = A2MP_STATUS_COLLISION_OCCURED; + amp_mgr_put(tmp); + } else { + rsp.status = A2MP_STATUS_INVALID_CTRL_ID; + } a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp), &rsp); - goto clean; + + goto done; } - /* Placeholder for HCI Read AMP Assoc */ + amp_read_loc_assoc(hdev, mgr); -clean: +done: if (hdev) hci_dev_put(hdev); @@ -243,6 +371,68 @@ clean: return 0; } +static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, + struct a2mp_cmd *hdr) +{ + struct a2mp_amp_assoc_rsp *rsp = (void *) skb->data; + u16 len = le16_to_cpu(hdr->len); + struct hci_dev *hdev; + struct amp_ctrl *ctrl; + struct hci_conn *hcon; + size_t assoc_len; + + if (len < sizeof(*rsp)) + return -EINVAL; + + assoc_len = len - sizeof(*rsp); + + BT_DBG("id %d status 0x%2.2x assoc len %zu", rsp->id, rsp->status, + assoc_len); + + if (rsp->status) + return -EINVAL; + + /* Save remote ASSOC data */ + ctrl = amp_ctrl_lookup(mgr, rsp->id); + if (ctrl) { + u8 *assoc; + + assoc = kzalloc(assoc_len, GFP_KERNEL); + if (!assoc) { + amp_ctrl_put(ctrl); + return -ENOMEM; + } + + memcpy(assoc, rsp->amp_assoc, assoc_len); + ctrl->assoc = assoc; + ctrl->assoc_len = assoc_len; + ctrl->assoc_rem_len = assoc_len; + ctrl->assoc_len_so_far = 0; + + amp_ctrl_put(ctrl); + } + + /* Create Phys Link */ + hdev = hci_dev_get(rsp->id); + if (!hdev) + return -EINVAL; + + hcon = phylink_add(hdev, mgr, rsp->id, true); + if (!hcon) + goto done; + + BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id); + + mgr->bredr_chan->ctrl_id = rsp->id; + + amp_create_phylink(hdev, mgr, hcon); + +done: + hci_dev_put(hdev); + skb_pull(skb, len); + return 0; +} + static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { @@ -250,6 +440,8 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_physlink_rsp rsp; struct hci_dev *hdev; + struct hci_conn *hcon; + struct amp_ctrl *ctrl; if (le16_to_cpu(hdr->len) < sizeof(*req)) return -EINVAL; @@ -265,9 +457,43 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, goto send_rsp; } - /* TODO process physlink create */ + ctrl = amp_ctrl_lookup(mgr, rsp.remote_id); + if (!ctrl) { + ctrl = amp_ctrl_add(mgr, rsp.remote_id); + if (ctrl) { + amp_ctrl_get(ctrl); + } else { + rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION; + goto send_rsp; + } + } - rsp.status = A2MP_STATUS_SUCCESS; + if (ctrl) { + size_t assoc_len = le16_to_cpu(hdr->len) - sizeof(*req); + u8 *assoc; + + assoc = kzalloc(assoc_len, GFP_KERNEL); + if (!assoc) { + amp_ctrl_put(ctrl); + return -ENOMEM; + } + + memcpy(assoc, req->amp_assoc, assoc_len); + ctrl->assoc = assoc; + ctrl->assoc_len = assoc_len; + ctrl->assoc_rem_len = assoc_len; + ctrl->assoc_len_so_far = 0; + + amp_ctrl_put(ctrl); + } + + hcon = phylink_add(hdev, mgr, req->local_id, false); + if (hcon) { + amp_accept_phylink(hdev, mgr, hcon); + rsp.status = A2MP_STATUS_SUCCESS; + } else { + rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION; + } send_rsp: if (hdev) @@ -286,6 +512,7 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_physlink_req *req = (void *) skb->data; struct a2mp_physlink_rsp rsp; struct hci_dev *hdev; + struct hci_conn *hcon; if (le16_to_cpu(hdr->len) < sizeof(*req)) return -EINVAL; @@ -296,14 +523,22 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, rsp.remote_id = req->local_id; rsp.status = A2MP_STATUS_SUCCESS; - hdev = hci_dev_get(req->local_id); + hdev = hci_dev_get(req->remote_id); if (!hdev) { rsp.status = A2MP_STATUS_INVALID_CTRL_ID; goto send_rsp; } + hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, mgr->l2cap_conn->dst); + if (!hcon) { + BT_ERR("No phys link exist"); + rsp.status = A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS; + goto clean; + } + /* TODO Disconnect Phys Link here */ +clean: hci_dev_put(hdev); send_rsp: @@ -377,10 +612,19 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) err = a2mp_discphyslink_req(mgr, skb, hdr); break; - case A2MP_CHANGE_RSP: case A2MP_DISCOVER_RSP: + err = a2mp_discover_rsp(mgr, skb, hdr); + break; + case A2MP_GETINFO_RSP: + err = a2mp_getinfo_rsp(mgr, skb, hdr); + break; + case A2MP_GETAMPASSOC_RSP: + err = a2mp_getampassoc_rsp(mgr, skb, hdr); + break; + + case A2MP_CHANGE_RSP: case A2MP_CREATEPHYSLINK_RSP: case A2MP_DISCONNPHYSLINK_RSP: err = a2mp_cmd_rsp(mgr, skb, hdr); @@ -455,9 +699,10 @@ static struct l2cap_ops a2mp_chan_ops = { .new_connection = l2cap_chan_no_new_connection, .teardown = l2cap_chan_no_teardown, .ready = l2cap_chan_no_ready, + .defer = l2cap_chan_no_defer, }; -static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn) +static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked) { struct l2cap_chan *chan; int err; @@ -492,7 +737,10 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn) chan->conf_state = 0; - l2cap_chan_add(conn, chan); + if (locked) + __l2cap_chan_add(conn, chan); + else + l2cap_chan_add(conn, chan); chan->remote_mps = chan->omtu; chan->mps = chan->omtu; @@ -503,11 +751,13 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn) } /* AMP Manager functions */ -void amp_mgr_get(struct amp_mgr *mgr) +struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr) { BT_DBG("mgr %p orig refcnt %d", mgr, atomic_read(&mgr->kref.refcount)); kref_get(&mgr->kref); + + return mgr; } static void amp_mgr_destroy(struct kref *kref) @@ -516,6 +766,11 @@ static void amp_mgr_destroy(struct kref *kref) BT_DBG("mgr %p", mgr); + mutex_lock(&_mgr_list_lock); + list_del(&mgr->list); + mutex_unlock(&_mgr_list_lock); + + amp_ctrl_list_flush(mgr); kfree(mgr); } @@ -526,7 +781,7 @@ int amp_mgr_put(struct amp_mgr *mgr) return kref_put(&mgr->kref, &_mgr_destroy); } -static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) +static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn, bool locked) { struct amp_mgr *mgr; struct l2cap_chan *chan; @@ -539,7 +794,7 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) mgr->l2cap_conn = conn; - chan = a2mp_chan_open(conn); + chan = a2mp_chan_open(conn, locked); if (!chan) { kfree(mgr); return NULL; @@ -552,6 +807,14 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) kref_init(&mgr->kref); + /* Remote AMP ctrl list initialization */ + INIT_LIST_HEAD(&mgr->amp_ctrls); + mutex_init(&mgr->amp_ctrls_lock); + + mutex_lock(&_mgr_list_lock); + list_add(&mgr->list, &_mgr_list); + mutex_unlock(&_mgr_list_lock); + return mgr; } @@ -560,7 +823,7 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, { struct amp_mgr *mgr; - mgr = amp_mgr_create(conn); + mgr = amp_mgr_create(conn, false); if (!mgr) { BT_ERR("Could not create AMP manager"); return NULL; @@ -570,3 +833,139 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, return mgr->a2mp_chan; } + +struct amp_mgr *amp_mgr_lookup_by_state(u8 state) +{ + struct amp_mgr *mgr; + + mutex_lock(&_mgr_list_lock); + list_for_each_entry(mgr, &_mgr_list, list) { + if (mgr->state == state) { + amp_mgr_get(mgr); + mutex_unlock(&_mgr_list_lock); + return mgr; + } + } + mutex_unlock(&_mgr_list_lock); + + return NULL; +} + +void a2mp_send_getinfo_rsp(struct hci_dev *hdev) +{ + struct amp_mgr *mgr; + struct a2mp_info_rsp rsp; + + mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_INFO); + if (!mgr) + return; + + BT_DBG("%s mgr %p", hdev->name, mgr); + + rsp.id = hdev->id; + rsp.status = A2MP_STATUS_INVALID_CTRL_ID; + + if (hdev->amp_type != HCI_BREDR) { + rsp.status = 0; + rsp.total_bw = cpu_to_le32(hdev->amp_total_bw); + rsp.max_bw = cpu_to_le32(hdev->amp_max_bw); + rsp.min_latency = cpu_to_le32(hdev->amp_min_latency); + rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap); + rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size); + } + + a2mp_send(mgr, A2MP_GETINFO_RSP, mgr->ident, sizeof(rsp), &rsp); + amp_mgr_put(mgr); +} + +void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status) +{ + struct amp_mgr *mgr; + struct amp_assoc *loc_assoc = &hdev->loc_assoc; + struct a2mp_amp_assoc_rsp *rsp; + size_t len; + + mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC); + if (!mgr) + return; + + BT_DBG("%s mgr %p", hdev->name, mgr); + + len = sizeof(struct a2mp_amp_assoc_rsp) + loc_assoc->len; + rsp = kzalloc(len, GFP_KERNEL); + if (!rsp) { + amp_mgr_put(mgr); + return; + } + + rsp->id = hdev->id; + + if (status) { + rsp->status = A2MP_STATUS_INVALID_CTRL_ID; + } else { + rsp->status = A2MP_STATUS_SUCCESS; + memcpy(rsp->amp_assoc, loc_assoc->data, loc_assoc->len); + } + + a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, mgr->ident, len, rsp); + amp_mgr_put(mgr); + kfree(rsp); +} + +void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status) +{ + struct amp_mgr *mgr; + struct amp_assoc *loc_assoc = &hdev->loc_assoc; + struct a2mp_physlink_req *req; + struct l2cap_chan *bredr_chan; + size_t len; + + mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC_FINAL); + if (!mgr) + return; + + len = sizeof(*req) + loc_assoc->len; + + BT_DBG("%s mgr %p assoc_len %zu", hdev->name, mgr, len); + + req = kzalloc(len, GFP_KERNEL); + if (!req) { + amp_mgr_put(mgr); + return; + } + + bredr_chan = mgr->bredr_chan; + if (!bredr_chan) + goto clean; + + req->local_id = hdev->id; + req->remote_id = bredr_chan->ctrl_id; + memcpy(req->amp_assoc, loc_assoc->data, loc_assoc->len); + + a2mp_send(mgr, A2MP_CREATEPHYSLINK_REQ, __next_ident(mgr), len, req); + +clean: + amp_mgr_put(mgr); + kfree(req); +} + +void a2mp_discover_amp(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + struct amp_mgr *mgr = conn->hcon->amp_mgr; + struct a2mp_discov_req req; + + BT_DBG("chan %p conn %p mgr %p", chan, conn, mgr); + + if (!mgr) { + mgr = amp_mgr_create(conn, true); + if (!mgr) + return; + } + + mgr->bredr_chan = chan; + + req.mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU); + req.ext_feat = 0; + a2mp_send(mgr, A2MP_DISCOVER_REQ, 1, sizeof(req), &req); +} diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index ba033f09196e..5355df63d39b 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -569,7 +569,6 @@ static int bt_seq_show(struct seq_file *seq, void *v) { struct bt_seq_state *s = seq->private; struct bt_sock_list *l = s->l; - bdaddr_t src_baswapped, dst_baswapped; if (v == SEQ_START_TOKEN) { seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Src Dst Parent"); @@ -583,18 +582,17 @@ static int bt_seq_show(struct seq_file *seq, void *v) } else { struct sock *sk = sk_entry(v); struct bt_sock *bt = bt_sk(sk); - baswap(&src_baswapped, &bt->src); - baswap(&dst_baswapped, &bt->dst); - seq_printf(seq, "%pK %-6d %-6u %-6u %-6u %-6lu %pM %pM %-6lu", + seq_printf(seq, + "%pK %-6d %-6u %-6u %-6u %-6lu %pMR %pMR %-6lu", sk, atomic_read(&sk->sk_refcnt), sk_rmem_alloc_get(sk), sk_wmem_alloc_get(sk), from_kuid(seq_user_ns(seq), sock_i_uid(sk)), sock_i_ino(sk), - &src_baswapped, - &dst_baswapped, + &bt->src, + &bt->dst, bt->parent? sock_i_ino(bt->parent): 0LU); if (l->custom_seq_show) { diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c new file mode 100644 index 000000000000..231d7ef53ecb --- /dev/null +++ b/net/bluetooth/amp.c @@ -0,0 +1,374 @@ +/* + Copyright (c) 2011,2012 Intel Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 and + only 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 <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci.h> +#include <net/bluetooth/hci_core.h> +#include <net/bluetooth/a2mp.h> +#include <net/bluetooth/amp.h> +#include <crypto/hash.h> + +/* Remote AMP Controllers interface */ +void amp_ctrl_get(struct amp_ctrl *ctrl) +{ + BT_DBG("ctrl %p orig refcnt %d", ctrl, + atomic_read(&ctrl->kref.refcount)); + + kref_get(&ctrl->kref); +} + +static void amp_ctrl_destroy(struct kref *kref) +{ + struct amp_ctrl *ctrl = container_of(kref, struct amp_ctrl, kref); + + BT_DBG("ctrl %p", ctrl); + + kfree(ctrl->assoc); + kfree(ctrl); +} + +int amp_ctrl_put(struct amp_ctrl *ctrl) +{ + BT_DBG("ctrl %p orig refcnt %d", ctrl, + atomic_read(&ctrl->kref.refcount)); + + return kref_put(&ctrl->kref, &_ctrl_destroy); +} + +struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id) +{ + struct amp_ctrl *ctrl; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return NULL; + + kref_init(&ctrl->kref); + ctrl->id = id; + + mutex_lock(&mgr->amp_ctrls_lock); + list_add(&ctrl->list, &mgr->amp_ctrls); + mutex_unlock(&mgr->amp_ctrls_lock); + + BT_DBG("mgr %p ctrl %p", mgr, ctrl); + + return ctrl; +} + +void amp_ctrl_list_flush(struct amp_mgr *mgr) +{ + struct amp_ctrl *ctrl, *n; + + BT_DBG("mgr %p", mgr); + + mutex_lock(&mgr->amp_ctrls_lock); + list_for_each_entry_safe(ctrl, n, &mgr->amp_ctrls, list) { + list_del(&ctrl->list); + amp_ctrl_put(ctrl); + } + mutex_unlock(&mgr->amp_ctrls_lock); +} + +struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id) +{ + struct amp_ctrl *ctrl; + + BT_DBG("mgr %p id %d", mgr, id); + + mutex_lock(&mgr->amp_ctrls_lock); + list_for_each_entry(ctrl, &mgr->amp_ctrls, list) { + if (ctrl->id == id) { + amp_ctrl_get(ctrl); + mutex_unlock(&mgr->amp_ctrls_lock); + return ctrl; + } + } + mutex_unlock(&mgr->amp_ctrls_lock); + + return NULL; +} + +/* Physical Link interface */ +static u8 __next_handle(struct amp_mgr *mgr) +{ + if (++mgr->handle == 0) + mgr->handle = 1; + + return mgr->handle; +} + +struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, + u8 remote_id, bool out) +{ + bdaddr_t *dst = mgr->l2cap_conn->dst; + struct hci_conn *hcon; + + hcon = hci_conn_add(hdev, AMP_LINK, dst); + if (!hcon) + return NULL; + + BT_DBG("hcon %p dst %pMR", hcon, dst); + + hcon->state = BT_CONNECT; + hcon->attempt++; + hcon->handle = __next_handle(mgr); + hcon->remote_id = remote_id; + hcon->amp_mgr = amp_mgr_get(mgr); + hcon->out = out; + + return hcon; +} + +/* AMP crypto key generation interface */ +static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output) +{ + int ret = 0; + struct crypto_shash *tfm; + + if (!ksize) + return -EINVAL; + + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + if (IS_ERR(tfm)) { + BT_DBG("crypto_alloc_ahash failed: err %ld", PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + + ret = crypto_shash_setkey(tfm, key, ksize); + if (ret) { + BT_DBG("crypto_ahash_setkey failed: err %d", ret); + } else { + struct { + struct shash_desc shash; + char ctx[crypto_shash_descsize(tfm)]; + } desc; + + desc.shash.tfm = tfm; + desc.shash.flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ret = crypto_shash_digest(&desc.shash, plaintext, psize, + output); + } + + crypto_free_shash(tfm); + return ret; +} + +int phylink_gen_key(struct hci_conn *conn, u8 *data, u8 *len, u8 *type) +{ + struct hci_dev *hdev = conn->hdev; + struct link_key *key; + u8 keybuf[HCI_AMP_LINK_KEY_SIZE]; + u8 gamp_key[HCI_AMP_LINK_KEY_SIZE]; + int err; + + if (!hci_conn_check_link_mode(conn)) + return -EACCES; + + BT_DBG("conn %p key_type %d", conn, conn->key_type); + + /* Legacy key */ + if (conn->key_type < 3) { + BT_ERR("Legacy key type %d", conn->key_type); + return -EACCES; + } + + *type = conn->key_type; + *len = HCI_AMP_LINK_KEY_SIZE; + + key = hci_find_link_key(hdev, &conn->dst); + if (!key) { + BT_DBG("No Link key for conn %p dst %pMR", conn, &conn->dst); + return -EACCES; + } + + /* BR/EDR Link Key concatenated together with itself */ + memcpy(&keybuf[0], key->val, HCI_LINK_KEY_SIZE); + memcpy(&keybuf[HCI_LINK_KEY_SIZE], key->val, HCI_LINK_KEY_SIZE); + + /* Derive Generic AMP Link Key (gamp) */ + err = hmac_sha256(keybuf, HCI_AMP_LINK_KEY_SIZE, "gamp", 4, gamp_key); + if (err) { + BT_ERR("Could not derive Generic AMP Key: err %d", err); + return err; + } + + if (conn->key_type == HCI_LK_DEBUG_COMBINATION) { + BT_DBG("Use Generic AMP Key (gamp)"); + memcpy(data, gamp_key, HCI_AMP_LINK_KEY_SIZE); + return err; + } + + /* Derive Dedicated AMP Link Key: "802b" is 802.11 PAL keyID */ + return hmac_sha256(gamp_key, HCI_AMP_LINK_KEY_SIZE, "802b", 4, data); +} + +void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle) +{ + struct hci_cp_read_local_amp_assoc cp; + struct amp_assoc *loc_assoc = &hdev->loc_assoc; + + BT_DBG("%s handle %d", hdev->name, phy_handle); + + cp.phy_handle = phy_handle; + cp.max_len = cpu_to_le16(hdev->amp_assoc_size); + cp.len_so_far = cpu_to_le16(loc_assoc->offset); + + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); +} + +void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) +{ + struct hci_cp_read_local_amp_assoc cp; + + memset(&hdev->loc_assoc, 0, sizeof(struct amp_assoc)); + memset(&cp, 0, sizeof(cp)); + + cp.max_len = cpu_to_le16(hdev->amp_assoc_size); + + mgr->state = READ_LOC_AMP_ASSOC; + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); +} + +void amp_read_loc_assoc_final_data(struct hci_dev *hdev, + struct hci_conn *hcon) +{ + struct hci_cp_read_local_amp_assoc cp; + struct amp_mgr *mgr = hcon->amp_mgr; + + cp.phy_handle = hcon->handle; + cp.len_so_far = cpu_to_le16(0); + cp.max_len = cpu_to_le16(hdev->amp_assoc_size); + + mgr->state = READ_LOC_AMP_ASSOC_FINAL; + + /* Read Local AMP Assoc final link information data */ + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); +} + +/* Write AMP Assoc data fragments, returns true with last fragment written*/ +static bool amp_write_rem_assoc_frag(struct hci_dev *hdev, + struct hci_conn *hcon) +{ + struct hci_cp_write_remote_amp_assoc *cp; + struct amp_mgr *mgr = hcon->amp_mgr; + struct amp_ctrl *ctrl; + u16 frag_len, len; + + ctrl = amp_ctrl_lookup(mgr, hcon->remote_id); + if (!ctrl) + return false; + + if (!ctrl->assoc_rem_len) { + BT_DBG("all fragments are written"); + ctrl->assoc_rem_len = ctrl->assoc_len; + ctrl->assoc_len_so_far = 0; + + amp_ctrl_put(ctrl); + return true; + } + + frag_len = min_t(u16, 248, ctrl->assoc_rem_len); + len = frag_len + sizeof(*cp); + + cp = kzalloc(len, GFP_KERNEL); + if (!cp) { + amp_ctrl_put(ctrl); + return false; + } + + BT_DBG("hcon %p ctrl %p frag_len %u assoc_len %u rem_len %u", + hcon, ctrl, frag_len, ctrl->assoc_len, ctrl->assoc_rem_len); + + cp->phy_handle = hcon->handle; + cp->len_so_far = cpu_to_le16(ctrl->assoc_len_so_far); + cp->rem_len = cpu_to_le16(ctrl->assoc_rem_len); + memcpy(cp->frag, ctrl->assoc, frag_len); + + ctrl->assoc_len_so_far += frag_len; + ctrl->assoc_rem_len -= frag_len; + + amp_ctrl_put(ctrl); + + hci_send_cmd(hdev, HCI_OP_WRITE_REMOTE_AMP_ASSOC, len, cp); + + kfree(cp); + + return false; +} + +void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle) +{ + struct hci_conn *hcon; + + BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle); + + hcon = hci_conn_hash_lookup_handle(hdev, handle); + if (!hcon) + return; + + amp_write_rem_assoc_frag(hdev, hcon); +} + +void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle) +{ + struct hci_conn *hcon; + + BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle); + + hcon = hci_conn_hash_lookup_handle(hdev, handle); + if (!hcon) + return; + + BT_DBG("%s phy handle 0x%2.2x hcon %p", hdev->name, handle, hcon); + + amp_write_rem_assoc_frag(hdev, hcon); +} + +void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, + struct hci_conn *hcon) +{ + struct hci_cp_create_phy_link cp; + + cp.phy_handle = hcon->handle; + + BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon, + hcon->handle); + + if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len, + &cp.key_type)) { + BT_DBG("Cannot create link key"); + return; + } + + hci_send_cmd(hdev, HCI_OP_CREATE_PHY_LINK, sizeof(cp), &cp); +} + +void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, + struct hci_conn *hcon) +{ + struct hci_cp_accept_phy_link cp; + + cp.phy_handle = hcon->handle; + + BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon, + hcon->handle); + + if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len, + &cp.key_type)) { + BT_DBG("Cannot create link key"); + return; + } + + hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp); +} diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 4a6620bc1570..a5b639702637 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -182,8 +182,7 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len) a2 = data; data += ETH_ALEN; - BT_DBG("mc filter %s -> %s", - batostr((void *) a1), batostr((void *) a2)); + BT_DBG("mc filter %pMR -> %pMR", a1, a2); /* Iterate from a1 to a2 */ set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter); diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c index 6c9c1fd601ca..e0a6ebf2baa6 100644 --- a/net/bluetooth/cmtp/core.c +++ b/net/bluetooth/cmtp/core.c @@ -353,7 +353,7 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) BT_DBG("mtu %d", session->mtu); - sprintf(session->name, "%s", batostr(&bt_sk(sock->sk)->dst)); + sprintf(session->name, "%pMR", &bt_sk(sock->sk)->dst); session->sock = sock; session->state = BT_CONFIG; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index b9196a44f759..fe646211c61f 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -130,6 +130,20 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason) hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp); } +static void hci_amp_disconn(struct hci_conn *conn, __u8 reason) +{ + struct hci_cp_disconn_phy_link cp; + + BT_DBG("hcon %p", conn); + + conn->state = BT_DISCONN; + + cp.phy_handle = HCI_PHY_HANDLE(conn->handle); + cp.reason = reason; + hci_send_cmd(conn->hdev, HCI_OP_DISCONN_PHY_LINK, + sizeof(cp), &cp); +} + static void hci_add_sco(struct hci_conn *conn, __u16 handle) { struct hci_dev *hdev = conn->hdev; @@ -230,11 +244,24 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status) } } +static void hci_conn_disconnect(struct hci_conn *conn) +{ + __u8 reason = hci_proto_disconn_ind(conn); + + switch (conn->type) { + case ACL_LINK: + hci_acl_disconn(conn, reason); + break; + case AMP_LINK: + hci_amp_disconn(conn, reason); + break; + } +} + static void hci_conn_timeout(struct work_struct *work) { struct hci_conn *conn = container_of(work, struct hci_conn, disc_work.work); - __u8 reason; BT_DBG("hcon %p state %s", conn, state_to_string(conn->state)); @@ -253,8 +280,7 @@ static void hci_conn_timeout(struct work_struct *work) break; case BT_CONFIG: case BT_CONNECTED: - reason = hci_proto_disconn_ind(conn); - hci_acl_disconn(conn, reason); + hci_conn_disconnect(conn); break; default: conn->state = BT_CLOSED; @@ -320,7 +346,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) { struct hci_conn *conn; - BT_DBG("%s dst %s", hdev->name, batostr(dst)); + BT_DBG("%s dst %pMR", hdev->name, dst); conn = kzalloc(sizeof(struct hci_conn), GFP_KERNEL); if (!conn) @@ -437,7 +463,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) int use_src = bacmp(src, BDADDR_ANY); struct hci_dev *hdev = NULL, *d; - BT_DBG("%s -> %s", batostr(src), batostr(dst)); + BT_DBG("%pMR -> %pMR", src, dst); read_lock(&hci_dev_list_lock); @@ -567,7 +593,7 @@ static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 dst_type, __u8 sec_level, __u8 auth_type) { - BT_DBG("%s dst %s type 0x%x", hdev->name, batostr(dst), type); + BT_DBG("%s dst %pMR type 0x%x", hdev->name, dst, type); switch (type) { case LE_LINK: @@ -963,3 +989,35 @@ void hci_chan_list_flush(struct hci_conn *conn) list_for_each_entry_safe(chan, n, &conn->chan_list, list) hci_chan_del(chan); } + +static struct hci_chan *__hci_chan_lookup_handle(struct hci_conn *hcon, + __u16 handle) +{ + struct hci_chan *hchan; + + list_for_each_entry(hchan, &hcon->chan_list, list) { + if (hchan->handle == handle) + return hchan; + } + + return NULL; +} + +struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_conn *hcon; + struct hci_chan *hchan = NULL; + + rcu_read_lock(); + + list_for_each_entry_rcu(hcon, &h->list, list) { + hchan = __hci_chan_lookup_handle(hcon, handle); + if (hchan) + break; + } + + rcu_read_unlock(); + + return hchan; +} diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8a0ce706aebd..5a3f941b610f 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -405,7 +405,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; - BT_DBG("cache %p, %s", cache, batostr(bdaddr)); + BT_DBG("cache %p, %pMR", cache, bdaddr); list_for_each_entry(e, &cache->all, all) { if (!bacmp(&e->data.bdaddr, bdaddr)) @@ -421,7 +421,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; - BT_DBG("cache %p, %s", cache, batostr(bdaddr)); + BT_DBG("cache %p, %pMR", cache, bdaddr); list_for_each_entry(e, &cache->unknown, list) { if (!bacmp(&e->data.bdaddr, bdaddr)) @@ -438,7 +438,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; - BT_DBG("cache %p bdaddr %s state %d", cache, batostr(bdaddr), state); + BT_DBG("cache %p bdaddr %pMR state %d", cache, bdaddr, state); list_for_each_entry(e, &cache->resolve, list) { if (!bacmp(bdaddr, BDADDR_ANY) && e->name_state == state) @@ -475,7 +475,7 @@ bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *ie; - BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr)); + BT_DBG("cache %p, %pMR", cache, &data->bdaddr); if (ssp) *ssp = data->ssp_mode; @@ -1259,7 +1259,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, list_add(&key->list, &hdev->link_keys); } - BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type); + BT_DBG("%s key for %pMR type %u", hdev->name, bdaddr, type); /* Some buggy controller combinations generate a changed * combination key for legacy pairing even when there's no @@ -1338,7 +1338,7 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) if (!key) return -ENOENT; - BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); + BT_DBG("%s removing %pMR", hdev->name, bdaddr); list_del(&key->list); kfree(key); @@ -1354,7 +1354,7 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr) if (bacmp(bdaddr, &k->bdaddr)) continue; - BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); + BT_DBG("%s removing %pMR", hdev->name, bdaddr); list_del(&k->list); kfree(k); @@ -1401,7 +1401,7 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr) if (!data) return -ENOENT; - BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); + BT_DBG("%s removing %pMR", hdev->name, bdaddr); list_del(&data->list); kfree(data); @@ -1440,7 +1440,7 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, memcpy(data->hash, hash, sizeof(data->hash)); memcpy(data->randomizer, randomizer, sizeof(data->randomizer)); - BT_DBG("%s for %s", hdev->name, batostr(bdaddr)); + BT_DBG("%s for %pMR", hdev->name, bdaddr); return 0; } @@ -2153,9 +2153,10 @@ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags) hdr->dlen = cpu_to_le16(len); } -static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue, +static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue, struct sk_buff *skb, __u16 flags) { + struct hci_conn *conn = chan->conn; struct hci_dev *hdev = conn->hdev; struct sk_buff *list; @@ -2163,7 +2164,18 @@ static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue, skb->data_len = 0; bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_add_acl_hdr(skb, conn->handle, flags); + + switch (hdev->dev_type) { + case HCI_BREDR: + hci_add_acl_hdr(skb, conn->handle, flags); + break; + case HCI_AMP: + hci_add_acl_hdr(skb, chan->handle, flags); + break; + default: + BT_ERR("%s unknown dev_type %d", hdev->name, hdev->dev_type); + return; + } list = skb_shinfo(skb)->frag_list; if (!list) { @@ -2202,14 +2214,13 @@ static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue, void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags) { - struct hci_conn *conn = chan->conn; - struct hci_dev *hdev = conn->hdev; + struct hci_dev *hdev = chan->conn->hdev; BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags); skb->dev = (void *) hdev; - hci_queue_acl(conn, &chan->data_q, skb, flags); + hci_queue_acl(chan, &chan->data_q, skb, flags); queue_work(hdev->workqueue, &hdev->tx_work); } @@ -2311,8 +2322,8 @@ static void hci_link_tx_to(struct hci_dev *hdev, __u8 type) /* Kill stalled connections */ list_for_each_entry_rcu(c, &h->list, list) { if (c->type == type && c->sent) { - BT_ERR("%s killing stalled connection %s", - hdev->name, batostr(&c->dst)); + BT_ERR("%s killing stalled connection %pMR", + hdev->name, &c->dst); hci_acl_disconn(c, HCI_ERROR_REMOTE_USER_TERM); } } @@ -2381,6 +2392,9 @@ static struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, case ACL_LINK: cnt = hdev->acl_cnt; break; + case AMP_LINK: + cnt = hdev->block_cnt; + break; case SCO_LINK: case ESCO_LINK: cnt = hdev->sco_cnt; @@ -2510,11 +2524,19 @@ static void hci_sched_acl_blk(struct hci_dev *hdev) struct hci_chan *chan; struct sk_buff *skb; int quote; + u8 type; __check_timeout(hdev, cnt); + BT_DBG("%s", hdev->name); + + if (hdev->dev_type == HCI_AMP) + type = AMP_LINK; + else + type = ACL_LINK; + while (hdev->block_cnt > 0 && - (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { + (chan = hci_chan_sent(hdev, type, "e))) { u32 priority = (skb_peek(&chan->data_q))->priority; while (quote > 0 && (skb = skb_peek(&chan->data_q))) { int blocks; @@ -2547,14 +2569,19 @@ static void hci_sched_acl_blk(struct hci_dev *hdev) } if (cnt != hdev->block_cnt) - hci_prio_recalculate(hdev, ACL_LINK); + hci_prio_recalculate(hdev, type); } static void hci_sched_acl(struct hci_dev *hdev) { BT_DBG("%s", hdev->name); - if (!hci_conn_num(hdev, ACL_LINK)) + /* No ACL link over BR/EDR controller */ + if (!hci_conn_num(hdev, ACL_LINK) && hdev->dev_type == HCI_BREDR) + return; + + /* No AMP link over AMP controller */ + if (!hci_conn_num(hdev, AMP_LINK) && hdev->dev_type == HCI_AMP) return; switch (hdev->flow_ctl_mode) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 2022b43c7353..0383635f91fb 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -30,6 +30,8 @@ #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> #include <net/bluetooth/mgmt.h> +#include <net/bluetooth/a2mp.h> +#include <net/bluetooth/amp.h> /* Handle HCI Event packets */ @@ -846,7 +848,7 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) - return; + goto a2mp_rsp; hdev->amp_status = rp->amp_status; hdev->amp_total_bw = __le32_to_cpu(rp->total_bw); @@ -860,6 +862,46 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev, hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to); hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status); + +a2mp_rsp: + a2mp_send_getinfo_rsp(hdev); +} + +static void hci_cc_read_local_amp_assoc(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_local_amp_assoc *rp = (void *) skb->data; + struct amp_assoc *assoc = &hdev->loc_assoc; + size_t rem_len, frag_len; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + goto a2mp_rsp; + + frag_len = skb->len - sizeof(*rp); + rem_len = __le16_to_cpu(rp->rem_len); + + if (rem_len > frag_len) { + BT_DBG("frag_len %zu rem_len %zu", frag_len, rem_len); + + memcpy(assoc->data + assoc->offset, rp->frag, frag_len); + assoc->offset += frag_len; + + /* Read other fragments */ + amp_read_loc_assoc_frag(hdev, rp->phy_handle); + + return; + } + + memcpy(assoc->data + assoc->offset, rp->frag, rem_len); + assoc->len = assoc->offset + rem_len; + assoc->offset = 0; + +a2mp_rsp: + /* Send A2MP Rsp when all fragments are received */ + a2mp_send_getampassoc_rsp(hdev, rp->status); + a2mp_send_create_phy_link_req(hdev, rp->status); } static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, @@ -1174,6 +1216,20 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status); } +static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_write_remote_amp_assoc *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x phy_handle 0x%2.2x", + hdev->name, rp->status, rp->phy_handle); + + if (rp->status) + return; + + amp_write_rem_assoc_continue(hdev, rp->phy_handle); +} + static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%2.2x", hdev->name, status); @@ -1210,7 +1266,7 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - BT_DBG("%s bdaddr %s hcon %p", hdev->name, batostr(&cp->bdaddr), conn); + BT_DBG("%s bdaddr %pMR hcon %p", hdev->name, &cp->bdaddr, conn); if (status) { if (conn && conn->state == BT_CONNECT) { @@ -1639,8 +1695,7 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) return; } - BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&conn->dst), - conn); + BT_DBG("%s bdaddr %pMR conn %p", hdev->name, &conn->dst, conn); conn->state = BT_CLOSED; mgmt_connect_failed(hdev, &conn->dst, conn->type, @@ -1657,6 +1712,38 @@ static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) BT_DBG("%s status 0x%2.2x", hdev->name, status); } +static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status) +{ + struct hci_cp_create_phy_link *cp; + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHY_LINK); + if (!cp) + return; + + amp_write_remote_assoc(hdev, cp->phy_handle); +} + +static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status) +{ + struct hci_cp_accept_phy_link *cp; + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_ACCEPT_PHY_LINK); + if (!cp) + return; + + amp_write_remote_assoc(hdev, cp->phy_handle); +} + static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -1822,7 +1909,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) struct hci_ev_conn_request *ev = (void *) skb->data; int mask = hdev->link_mode; - BT_DBG("%s bdaddr %s type 0x%x", hdev->name, batostr(&ev->bdaddr), + BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr, ev->link_type); mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type); @@ -2314,6 +2401,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_read_local_amp_info(hdev, skb); break; + case HCI_OP_READ_LOCAL_AMP_ASSOC: + hci_cc_read_local_amp_assoc(hdev, skb); + break; + case HCI_OP_DELETE_STORED_LINK_KEY: hci_cc_delete_stored_link_key(hdev, skb); break; @@ -2386,6 +2477,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_write_le_host_supported(hdev, skb); break; + case HCI_OP_WRITE_REMOTE_AMP_ASSOC: + hci_cc_write_remote_amp_assoc(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; @@ -2467,6 +2562,14 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_le_start_enc(hdev, ev->status); break; + case HCI_OP_CREATE_PHY_LINK: + hci_cs_create_phylink(hdev, ev->status); + break; + + case HCI_OP_ACCEPT_PHY_LINK: + hci_cs_accept_phylink(hdev, ev->status); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; @@ -2574,6 +2677,27 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb) queue_work(hdev->workqueue, &hdev->tx_work); } +static struct hci_conn *__hci_conn_lookup_handle(struct hci_dev *hdev, + __u16 handle) +{ + struct hci_chan *chan; + + switch (hdev->dev_type) { + case HCI_BREDR: + return hci_conn_hash_lookup_handle(hdev, handle); + case HCI_AMP: + chan = hci_chan_lookup_handle(hdev, handle); + if (chan) + return chan->conn; + break; + default: + BT_ERR("%s unknown dev_type %d", hdev->name, hdev->dev_type); + break; + } + + return NULL; +} + static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_num_comp_blocks *ev = (void *) skb->data; @@ -2595,13 +2719,13 @@ static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb) for (i = 0; i < ev->num_hndl; i++) { struct hci_comp_blocks_info *info = &ev->handles[i]; - struct hci_conn *conn; + struct hci_conn *conn = NULL; __u16 handle, block_count; handle = __le16_to_cpu(info->handle); block_count = __le16_to_cpu(info->blocks); - conn = hci_conn_hash_lookup_handle(hdev, handle); + conn = __hci_conn_lookup_handle(hdev, handle); if (!conn) continue; @@ -2609,6 +2733,7 @@ static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb) switch (conn->type) { case ACL_LINK: + case AMP_LINK: hdev->block_cnt += block_count; if (hdev->block_cnt > hdev->num_blocks) hdev->block_cnt = hdev->num_blocks; @@ -2705,13 +2830,13 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) key = hci_find_link_key(hdev, &ev->bdaddr); if (!key) { - BT_DBG("%s link key not found for %s", hdev->name, - batostr(&ev->bdaddr)); + BT_DBG("%s link key not found for %pMR", hdev->name, + &ev->bdaddr); goto not_found; } - BT_DBG("%s found key type %u for %s", hdev->name, key->type, - batostr(&ev->bdaddr)); + BT_DBG("%s found key type %u for %pMR", hdev->name, key->type, + &ev->bdaddr); if (!test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) && key->type == HCI_LK_DEBUG_COMBINATION) { @@ -3558,6 +3683,22 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) } } +static void hci_chan_selected_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_channel_selected *ev = (void *) skb->data; + struct hci_conn *hcon; + + BT_DBG("%s handle 0x%2.2x", hdev->name, ev->phy_handle); + + skb_pull(skb, sizeof(*ev)); + + hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle); + if (!hcon) + return; + + amp_read_loc_assoc_final_data(hdev, hcon); +} + void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_event_hdr *hdr = (void *) skb->data; @@ -3722,6 +3863,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_le_meta_evt(hdev, skb); break; + case HCI_EV_CHANNEL_SELECTED: + hci_chan_selected_evt(hdev, skb); + break; + case HCI_EV_REMOTE_OOB_DATA_REQUEST: hci_remote_oob_data_request_evt(hdev, skb); break; diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index a20e61c3653d..55cceee02a84 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -38,7 +38,7 @@ static ssize_t show_link_address(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_conn *conn = to_hci_conn(dev); - return sprintf(buf, "%s\n", batostr(&conn->dst)); + return sprintf(buf, "%pMR\n", &conn->dst); } static ssize_t show_link_features(struct device *dev, @@ -224,7 +224,7 @@ static ssize_t show_address(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%s\n", batostr(&hdev->bdaddr)); + return sprintf(buf, "%pMR\n", &hdev->bdaddr); } static ssize_t show_features(struct device *dev, @@ -406,8 +406,8 @@ static int inquiry_cache_show(struct seq_file *f, void *p) list_for_each_entry(e, &cache->all, all) { struct inquiry_data *data = &e->data; - seq_printf(f, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", - batostr(&data->bdaddr), + seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", + &data->bdaddr, data->pscan_rep_mode, data->pscan_period_mode, data->pscan_mode, data->dev_class[2], data->dev_class[1], data->dev_class[0], @@ -440,7 +440,7 @@ static int blacklist_show(struct seq_file *f, void *p) hci_dev_lock(hdev); list_for_each_entry(b, &hdev->blacklist, list) - seq_printf(f, "%s\n", batostr(&b->bdaddr)); + seq_printf(f, "%pMR\n", &b->bdaddr); hci_dev_unlock(hdev); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index ccd985da6518..0c0028463fa3 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -932,8 +932,12 @@ static int hidp_setup_hid(struct hidp_session *session, hid->country = req->country; strncpy(hid->name, req->name, 128); - strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64); - strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64); + + snprintf(hid->phys, sizeof(hid->phys), "%pMR", + &bt_sk(session->ctrl_sock->sk)->src); + + snprintf(hid->uniq, sizeof(hid->uniq), "%pMR", + &bt_sk(session->ctrl_sock->sk)->dst); hid->dev.parent = &session->conn->dev; hid->ll_driver = &hidp_hid_driver; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a91239dcda41..08efc256c931 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -48,19 +48,20 @@ static LIST_HEAD(chan_list); static DEFINE_RWLOCK(chan_list_lock); static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, - u8 code, u8 ident, u16 dlen, void *data); + u8 code, u8 ident, u16 dlen, void *data); static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, - void *data); + void *data); static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data); static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err); static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control, - struct sk_buff_head *skbs, u8 event); + struct sk_buff_head *skbs, u8 event); /* ---- L2CAP channels ---- */ -static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid) +static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, + u16 cid) { struct l2cap_chan *c; @@ -71,7 +72,8 @@ static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 return NULL; } -static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) +static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, + u16 cid) { struct l2cap_chan *c; @@ -84,7 +86,8 @@ static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 /* Find channel with given SCID. * Returns locked channel. */ -static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) +static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, + u16 cid) { struct l2cap_chan *c; @@ -97,7 +100,8 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 ci return c; } -static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) +static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, + u8 ident) { struct l2cap_chan *c; @@ -178,7 +182,7 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) static void __l2cap_state_change(struct l2cap_chan *chan, int state) { BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state), - state_to_string(state)); + state_to_string(state)); chan->state = state; chan->ops->state_change(chan, state); @@ -361,7 +365,7 @@ static void l2cap_seq_list_append(struct l2cap_seq_list *seq_list, u16 seq) static void l2cap_chan_timeout(struct work_struct *work) { struct l2cap_chan *chan = container_of(work, struct l2cap_chan, - chan_timer.work); + chan_timer.work); struct l2cap_conn *conn = chan->conn; int reason; @@ -373,7 +377,7 @@ static void l2cap_chan_timeout(struct work_struct *work) if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG) reason = ECONNREFUSED; else if (chan->state == BT_CONNECT && - chan->sec_level != BT_SECURITY_SDP) + chan->sec_level != BT_SECURITY_SDP) reason = ECONNREFUSED; else reason = ETIMEDOUT; @@ -455,7 +459,7 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) set_bit(FLAG_FORCE_ACTIVE, &chan->flags); } -static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) +void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, __le16_to_cpu(chan->psm), chan->dcid); @@ -504,7 +508,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) chan->local_msdu = L2CAP_DEFAULT_MAX_SDU_SIZE; chan->local_sdu_itime = L2CAP_DEFAULT_SDU_ITIME; chan->local_acc_lat = L2CAP_DEFAULT_ACC_LAT; - chan->local_flush_to = L2CAP_DEFAULT_FLUSH_TO; + chan->local_flush_to = L2CAP_EFS_DEFAULT_FLUSH_TO; l2cap_chan_hold(chan); @@ -527,6 +531,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) BT_DBG("chan %p, conn %p, err %d", chan, conn, err); if (conn) { + struct amp_mgr *mgr = conn->hcon->amp_mgr; /* Delete from channel list */ list_del(&chan->list); @@ -536,10 +541,12 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) if (chan->chan_type != L2CAP_CHAN_CONN_FIX_A2MP) hci_conn_put(conn->hcon); + + if (mgr && mgr->bredr_chan == chan) + mgr->bredr_chan = NULL; } - if (chan->ops->teardown) - chan->ops->teardown(chan, err); + chan->ops->teardown(chan, err); if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state)) return; @@ -573,19 +580,18 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) struct l2cap_conn *conn = chan->conn; struct sock *sk = chan->sk; - BT_DBG("chan %p state %s sk %p", chan, - state_to_string(chan->state), sk); + BT_DBG("chan %p state %s sk %p", chan, state_to_string(chan->state), + sk); switch (chan->state) { case BT_LISTEN: - if (chan->ops->teardown) - chan->ops->teardown(chan, 0); + chan->ops->teardown(chan, 0); break; case BT_CONNECTED: case BT_CONFIG: if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && - conn->hcon->type == ACL_LINK) { + conn->hcon->type == ACL_LINK) { __set_chan_timer(chan, sk->sk_sndtimeo); l2cap_send_disconn_req(conn, chan, reason); } else @@ -594,7 +600,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) case BT_CONNECT2: if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && - conn->hcon->type == ACL_LINK) { + conn->hcon->type == ACL_LINK) { struct l2cap_conn_rsp rsp; __u16 result; @@ -609,7 +615,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) rsp.result = cpu_to_le16(result); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); + sizeof(rsp), &rsp); } l2cap_chan_del(chan, reason); @@ -621,8 +627,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) break; default: - if (chan->ops->teardown) - chan->ops->teardown(chan, 0); + chan->ops->teardown(chan, 0); break; } } @@ -691,7 +696,8 @@ static u8 l2cap_get_ident(struct l2cap_conn *conn) return id; } -static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) +static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, + void *data) { struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); u8 flags; @@ -718,10 +724,10 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) u16 flags; BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len, - skb->priority); + skb->priority); if (!test_bit(FLAG_FLUSHABLE, &chan->flags) && - lmp_no_flush_capable(hcon->hdev)) + lmp_no_flush_capable(hcon->hdev)) flags = ACL_START_NO_FLUSH; else flags = ACL_START; @@ -946,7 +952,19 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan) return !test_bit(CONF_CONNECT_PEND, &chan->conf_state); } -static void l2cap_send_conn_req(struct l2cap_chan *chan) +static bool __amp_capable(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + + if (enable_hs && + chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED && + conn->fixed_chan_mask & L2CAP_FC_A2MP) + return true; + else + return false; +} + +void l2cap_send_conn_req(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; struct l2cap_conn_req req; @@ -972,6 +990,16 @@ static void l2cap_chan_ready(struct l2cap_chan *chan) chan->ops->ready(chan); } +static void l2cap_start_connection(struct l2cap_chan *chan) +{ + if (__amp_capable(chan)) { + BT_DBG("chan %p AMP capable: discover AMPs", chan); + a2mp_discover_amp(chan); + } else { + l2cap_send_conn_req(chan); + } +} + static void l2cap_do_start(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; @@ -986,8 +1014,9 @@ static void l2cap_do_start(struct l2cap_chan *chan) return; if (l2cap_chan_check_security(chan) && - __l2cap_no_conn_pending(chan)) - l2cap_send_conn_req(chan); + __l2cap_no_conn_pending(chan)) { + l2cap_start_connection(chan); + } } else { struct l2cap_info_req req; req.type = __constant_cpu_to_le16(L2CAP_IT_FEAT_MASK); @@ -997,8 +1026,8 @@ static void l2cap_do_start(struct l2cap_chan *chan) schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT); - l2cap_send_cmd(conn, conn->info_ident, - L2CAP_INFO_REQ, sizeof(req), &req); + l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ, + sizeof(req), &req); } } @@ -1018,7 +1047,8 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) } } -static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err) +static void l2cap_send_disconn_req(struct l2cap_conn *conn, + struct l2cap_chan *chan, int err) { struct sock *sk = chan->sk; struct l2cap_disconn_req req; @@ -1033,14 +1063,14 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c } if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) { - __l2cap_state_change(chan, BT_DISCONN); + l2cap_state_change(chan, BT_DISCONN); return; } req.dcid = cpu_to_le16(chan->dcid); req.scid = cpu_to_le16(chan->scid); - l2cap_send_cmd(conn, l2cap_get_ident(conn), - L2CAP_DISCONN_REQ, sizeof(req), &req); + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ, + sizeof(req), &req); lock_sock(sk); __l2cap_state_change(chan, BT_DISCONN); @@ -1069,20 +1099,20 @@ static void l2cap_conn_start(struct l2cap_conn *conn) if (chan->state == BT_CONNECT) { if (!l2cap_chan_check_security(chan) || - !__l2cap_no_conn_pending(chan)) { + !__l2cap_no_conn_pending(chan)) { l2cap_chan_unlock(chan); continue; } if (!l2cap_mode_supported(chan->mode, conn->feat_mask) - && test_bit(CONF_STATE2_DEVICE, + && test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) { l2cap_chan_close(chan, ECONNRESET); l2cap_chan_unlock(chan); continue; } - l2cap_send_conn_req(chan); + l2cap_start_connection(chan); } else if (chan->state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; @@ -1094,11 +1124,9 @@ static void l2cap_conn_start(struct l2cap_conn *conn) lock_sock(sk); if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { - struct sock *parent = bt_sk(sk)->parent; rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND); rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHOR_PEND); - if (parent) - parent->sk_data_ready(parent, 0); + chan->ops->defer(chan); } else { __l2cap_state_change(chan, BT_CONFIG); @@ -1112,17 +1140,17 @@ static void l2cap_conn_start(struct l2cap_conn *conn) } l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); + sizeof(rsp), &rsp); if (test_bit(CONF_REQ_SENT, &chan->conf_state) || - rsp.result != L2CAP_CR_SUCCESS) { + rsp.result != L2CAP_CR_SUCCESS) { l2cap_chan_unlock(chan); continue; } set_bit(CONF_REQ_SENT, &chan->conf_state); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, buf), buf); + l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } @@ -1204,8 +1232,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); - bt_accept_enqueue(parent, sk); - l2cap_chan_add(conn, chan); l2cap_chan_ready(chan); @@ -1270,7 +1296,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) list_for_each_entry(chan, &conn->chan_l, list) { if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) - __l2cap_chan_set_err(chan, err); + l2cap_chan_set_err(chan, err); } mutex_unlock(&conn->chan_lock); @@ -1279,7 +1305,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) static void l2cap_info_timeout(struct work_struct *work) { struct l2cap_conn *conn = container_of(work, struct l2cap_conn, - info_timer.work); + info_timer.work); conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_ident = 0; @@ -1333,7 +1359,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) static void security_timeout(struct work_struct *work) { struct l2cap_conn *conn = container_of(work, struct l2cap_conn, - security_timer.work); + security_timer.work); BT_DBG("conn %p", conn); @@ -1355,7 +1381,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) if (!hchan) return NULL; - conn = kzalloc(sizeof(struct l2cap_conn), GFP_ATOMIC); + conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL); if (!conn) { hci_chan_del(hchan); return NULL; @@ -1367,10 +1393,22 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan); - if (hcon->hdev->le_mtu && hcon->type == LE_LINK) - conn->mtu = hcon->hdev->le_mtu; - else + switch (hcon->type) { + case AMP_LINK: + conn->mtu = hcon->hdev->block_mtu; + break; + + case LE_LINK: + if (hcon->hdev->le_mtu) { + conn->mtu = hcon->hdev->le_mtu; + break; + } + /* fall through */ + + default: conn->mtu = hcon->hdev->acl_mtu; + break; + } conn->src = &hcon->hdev->bdaddr; conn->dst = &hcon->dst; @@ -1448,7 +1486,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, __u8 auth_type; int err; - BT_DBG("%s -> %s (type %u) psm 0x%2.2x", batostr(src), batostr(dst), + BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", src, dst, dst_type, __le16_to_cpu(psm)); hdev = hci_get_route(dst, src); @@ -1461,7 +1499,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, /* PSM must be odd and lsb of upper byte must be 0 */ if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid && - chan->chan_type != L2CAP_CHAN_RAW) { + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; goto done; } @@ -1770,7 +1808,7 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan) skb = l2cap_ertm_seq_in_queue(&chan->tx_q, seq); if (!skb) { BT_DBG("Error: Can't retransmit seq %d, frame missing", - seq); + seq); continue; } @@ -1795,9 +1833,9 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan) /* Cloned sk_buffs are read-only, so we need a * writeable copy */ - tx_skb = skb_copy(skb, GFP_ATOMIC); + tx_skb = skb_copy(skb, GFP_KERNEL); } else { - tx_skb = skb_clone(skb, GFP_ATOMIC); + tx_skb = skb_clone(skb, GFP_KERNEL); } if (!tx_skb) { @@ -1855,7 +1893,7 @@ static void l2cap_retransmit_all(struct l2cap_chan *chan, if (chan->unacked_frames) { skb_queue_walk(&chan->tx_q, skb) { if (bt_cb(skb)->control.txseq == control->reqseq || - skb == chan->tx_send_head) + skb == chan->tx_send_head) break; } @@ -2156,7 +2194,7 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan, } int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, - u32 priority) + u32 priority) { struct sk_buff *skb; int err; @@ -2543,7 +2581,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) /* Don't send frame to the socket it came from */ if (skb->sk == sk) continue; - nskb = skb_clone(skb, GFP_ATOMIC); + nskb = skb_clone(skb, GFP_KERNEL); if (!nskb) continue; @@ -2569,7 +2607,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen; count = min_t(unsigned int, conn->mtu, len); - skb = bt_skb_alloc(count, GFP_ATOMIC); + skb = bt_skb_alloc(count, GFP_KERNEL); if (!skb) return NULL; @@ -2599,7 +2637,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, while (len) { count = min_t(unsigned int, conn->mtu, len); - *frag = bt_skb_alloc(count, GFP_ATOMIC); + *frag = bt_skb_alloc(count, GFP_KERNEL); if (!*frag) goto fail; @@ -2618,7 +2656,8 @@ fail: return NULL; } -static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val) +static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, + unsigned long *val) { struct l2cap_conf_opt *opt = *ptr; int len; @@ -2692,7 +2731,7 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan) efs.msdu = cpu_to_le16(chan->local_msdu); efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime); efs.acc_lat = __constant_cpu_to_le32(L2CAP_DEFAULT_ACC_LAT); - efs.flush_to = __constant_cpu_to_le32(L2CAP_DEFAULT_FLUSH_TO); + efs.flush_to = __constant_cpu_to_le32(L2CAP_EFS_DEFAULT_FLUSH_TO); break; case L2CAP_MODE_STREAMING: @@ -2709,7 +2748,7 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan) } l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs), - (unsigned long) &efs); + (unsigned long) &efs); } static void l2cap_ack_timeout(struct work_struct *work) @@ -2798,13 +2837,13 @@ static inline bool __l2cap_efs_supported(struct l2cap_chan *chan) static inline void l2cap_txwin_setup(struct l2cap_chan *chan) { if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && - __l2cap_ews_supported(chan)) { + __l2cap_ews_supported(chan)) { /* use extended control field */ set_bit(FLAG_EXT_CTRL, &chan->flags); chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; } else { chan->tx_win = min_t(u16, chan->tx_win, - L2CAP_DEFAULT_TX_WINDOW); + L2CAP_DEFAULT_TX_WINDOW); chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW; } chan->ack_win = chan->tx_win; @@ -2844,7 +2883,7 @@ done: switch (chan->mode) { case L2CAP_MODE_BASIC: if (!(chan->conn->feat_mask & L2CAP_FEAT_ERTM) && - !(chan->conn->feat_mask & L2CAP_FEAT_STREAMING)) + !(chan->conn->feat_mask & L2CAP_FEAT_STREAMING)) break; rfc.mode = L2CAP_MODE_BASIC; @@ -2855,7 +2894,7 @@ done: rfc.max_pdu_size = 0; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) &rfc); + (unsigned long) &rfc); break; case L2CAP_MODE_ERTM: @@ -2865,18 +2904,17 @@ done: rfc.monitor_timeout = 0; size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - - L2CAP_EXT_HDR_SIZE - - L2CAP_SDULEN_SIZE - - L2CAP_FCS_SIZE); + L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE - + L2CAP_FCS_SIZE); rfc.max_pdu_size = cpu_to_le16(size); l2cap_txwin_setup(chan); rfc.txwin_size = min_t(u16, chan->tx_win, - L2CAP_DEFAULT_TX_WINDOW); + L2CAP_DEFAULT_TX_WINDOW); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) &rfc); + (unsigned long) &rfc); if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) l2cap_add_opt_efs(&ptr, chan); @@ -2885,14 +2923,14 @@ done: break; if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { + test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } if (test_bit(FLAG_EXT_CTRL, &chan->flags)) l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, - chan->tx_win); + chan->tx_win); break; case L2CAP_MODE_STREAMING: @@ -2904,13 +2942,12 @@ done: rfc.monitor_timeout = 0; size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - - L2CAP_EXT_HDR_SIZE - - L2CAP_SDULEN_SIZE - - L2CAP_FCS_SIZE); + L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE - + L2CAP_FCS_SIZE); rfc.max_pdu_size = cpu_to_le16(size); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) &rfc); + (unsigned long) &rfc); if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) l2cap_add_opt_efs(&ptr, chan); @@ -2919,7 +2956,7 @@ done: break; if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { + test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } @@ -3011,7 +3048,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) case L2CAP_MODE_ERTM: if (!test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) { chan->mode = l2cap_select_mode(rfc.mode, - chan->conn->feat_mask); + chan->conn->feat_mask); break; } @@ -3036,8 +3073,8 @@ done: if (chan->num_conf_rsp == 1) return -ECONNREFUSED; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), + (unsigned long) &rfc); } if (result == L2CAP_CONF_SUCCESS) { @@ -3054,8 +3091,8 @@ done: if (remote_efs) { if (chan->local_stype != L2CAP_SERV_NOTRAFIC && - efs.stype != L2CAP_SERV_NOTRAFIC && - efs.stype != chan->local_stype) { + efs.stype != L2CAP_SERV_NOTRAFIC && + efs.stype != chan->local_stype) { result = L2CAP_CONF_UNACCEPT; @@ -3063,8 +3100,8 @@ done: return -ECONNREFUSED; l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, - sizeof(efs), - (unsigned long) &efs); + sizeof(efs), + (unsigned long) &efs); } else { /* Send PENDING Conf Rsp */ result = L2CAP_CONF_PENDING; @@ -3087,10 +3124,8 @@ done: chan->remote_max_tx = rfc.max_transmit; size = min_t(u16, le16_to_cpu(rfc.max_pdu_size), - chan->conn->mtu - - L2CAP_EXT_HDR_SIZE - - L2CAP_SDULEN_SIZE - - L2CAP_FCS_SIZE); + chan->conn->mtu - L2CAP_EXT_HDR_SIZE - + L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE); rfc.max_pdu_size = cpu_to_le16(size); chan->remote_mps = size; @@ -3102,36 +3137,35 @@ done: set_bit(CONF_MODE_DONE, &chan->conf_state); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); + sizeof(rfc), (unsigned long) &rfc); if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) { chan->remote_id = efs.id; chan->remote_stype = efs.stype; chan->remote_msdu = le16_to_cpu(efs.msdu); chan->remote_flush_to = - le32_to_cpu(efs.flush_to); + le32_to_cpu(efs.flush_to); chan->remote_acc_lat = - le32_to_cpu(efs.acc_lat); + le32_to_cpu(efs.acc_lat); chan->remote_sdu_itime = le32_to_cpu(efs.sdu_itime); l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, - sizeof(efs), (unsigned long) &efs); + sizeof(efs), + (unsigned long) &efs); } break; case L2CAP_MODE_STREAMING: size = min_t(u16, le16_to_cpu(rfc.max_pdu_size), - chan->conn->mtu - - L2CAP_EXT_HDR_SIZE - - L2CAP_SDULEN_SIZE - - L2CAP_FCS_SIZE); + chan->conn->mtu - L2CAP_EXT_HDR_SIZE - + L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE); rfc.max_pdu_size = cpu_to_le16(size); chan->remote_mps = size; set_bit(CONF_MODE_DONE, &chan->conf_state); - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), + (unsigned long) &rfc); break; @@ -3152,7 +3186,8 @@ done: return ptr - data; } -static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, void *data, u16 *result) +static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, + void *data, u16 *result) { struct l2cap_conf_req *req = data; void *ptr = req->data; @@ -3179,7 +3214,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi case L2CAP_CONF_FLUSH_TO: chan->flush_to = val; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, - 2, chan->flush_to); + 2, chan->flush_to); break; case L2CAP_CONF_RFC: @@ -3187,13 +3222,13 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi memcpy(&rfc, (void *)val, olen); if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) && - rfc.mode != chan->mode) + rfc.mode != chan->mode) return -ECONNREFUSED; chan->fcs = 0; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); + sizeof(rfc), (unsigned long) &rfc); break; case L2CAP_CONF_EWS: @@ -3207,12 +3242,12 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi memcpy(&efs, (void *)val, olen); if (chan->local_stype != L2CAP_SERV_NOTRAFIC && - efs.stype != L2CAP_SERV_NOTRAFIC && - efs.stype != chan->local_stype) + efs.stype != L2CAP_SERV_NOTRAFIC && + efs.stype != chan->local_stype) return -ECONNREFUSED; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, - sizeof(efs), (unsigned long) &efs); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), + (unsigned long) &efs); break; } } @@ -3235,10 +3270,10 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) { chan->local_msdu = le16_to_cpu(efs.msdu); chan->local_sdu_itime = - le32_to_cpu(efs.sdu_itime); + le32_to_cpu(efs.sdu_itime); chan->local_acc_lat = le32_to_cpu(efs.acc_lat); chan->local_flush_to = - le32_to_cpu(efs.flush_to); + le32_to_cpu(efs.flush_to); } break; @@ -3253,7 +3288,8 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi return ptr - data; } -static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data, u16 result, u16 flags) +static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data, + u16 result, u16 flags) { struct l2cap_conf_rsp *rsp = data; void *ptr = rsp->data; @@ -3277,14 +3313,13 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) rsp.dcid = cpu_to_le16(chan->scid); rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, chan->ident, - L2CAP_CONN_RSP, sizeof(rsp), &rsp); + l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) return; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, buf), buf); + l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } @@ -3339,7 +3374,8 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) } } -static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_command_rej(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data; @@ -3347,7 +3383,7 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd return 0; if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) && - cmd->ident == conn->info_ident) { + cmd->ident == conn->info_ident) { cancel_delayed_work(&conn->info_timer); conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; @@ -3359,7 +3395,8 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd return 0; } -static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, + u8 *data, u8 rsp_code, u8 amp_id) { struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; struct l2cap_conn_rsp rsp; @@ -3386,7 +3423,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd /* Check if the ACL is secure enough (if not SDP) */ if (psm != __constant_cpu_to_le16(L2CAP_PSM_SDP) && - !hci_conn_check_link_mode(conn->hcon)) { + !hci_conn_check_link_mode(conn->hcon)) { conn->disc_reason = HCI_ERROR_AUTH_FAILURE; result = L2CAP_CR_SEC_BLOCK; goto response; @@ -3411,8 +3448,6 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd chan->psm = psm; chan->dcid = scid; - bt_accept_enqueue(parent, sk); - __l2cap_chan_add(conn, chan); dcid = chan->scid; @@ -3427,7 +3462,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd __l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_AUTHOR_PEND; - parent->sk_data_ready(parent, 0); + chan->ops->defer(chan); } else { __l2cap_state_change(chan, BT_CONFIG); result = L2CAP_CR_SUCCESS; @@ -3453,7 +3488,7 @@ sendresp: rsp.dcid = cpu_to_le16(dcid); rsp.result = cpu_to_le16(result); rsp.status = cpu_to_le16(status); - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); + l2cap_send_cmd(conn, cmd->ident, rsp_code, sizeof(rsp), &rsp); if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) { struct l2cap_info_req info; @@ -3464,23 +3499,29 @@ sendresp: schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT); - l2cap_send_cmd(conn, conn->info_ident, - L2CAP_INFO_REQ, sizeof(info), &info); + l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ, + sizeof(info), &info); } if (chan && !test_bit(CONF_REQ_SENT, &chan->conf_state) && - result == L2CAP_CR_SUCCESS) { + result == L2CAP_CR_SUCCESS) { u8 buf[128]; set_bit(CONF_REQ_SENT, &chan->conf_state); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, buf), buf); + l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } +} +static int l2cap_connect_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) +{ + l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0); return 0; } -static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_connect_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data; u16 scid, dcid, result, status; @@ -3494,7 +3535,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd status = __le16_to_cpu(rsp->status); BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", - dcid, scid, result, status); + dcid, scid, result, status); mutex_lock(&conn->chan_lock); @@ -3527,7 +3568,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd break; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, req), req); + l2cap_build_conf_req(chan, req), req); chan->num_conf_req++; break; @@ -3559,7 +3600,25 @@ static inline void set_default_fcs(struct l2cap_chan *chan) chan->fcs = L2CAP_FCS_CRC16; } -static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) +static void l2cap_send_efs_conf_rsp(struct l2cap_chan *chan, void *data, + u8 ident, u16 flags) +{ + struct l2cap_conn *conn = chan->conn; + + BT_DBG("conn %p chan %p ident %d flags 0x%4.4x", conn, chan, ident, + flags); + + clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); + set_bit(CONF_OUTPUT_DONE, &chan->conf_state); + + l2cap_send_cmd(conn, ident, L2CAP_CONF_RSP, + l2cap_build_conf_rsp(chan, data, + L2CAP_CONF_SUCCESS, flags), data); +} + +static inline int l2cap_config_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) { struct l2cap_conf_req *req = (struct l2cap_conf_req *) data; u16 dcid, flags; @@ -3584,7 +3643,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr rej.dcid = cpu_to_le16(chan->dcid); l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ, - sizeof(rej), &rej); + sizeof(rej), &rej); goto unlock; } @@ -3592,8 +3651,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr len = cmd_len - sizeof(*req); if (len < 0 || chan->conf_len + len > sizeof(chan->conf_req)) { l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, rsp, - L2CAP_CONF_REJECT, flags), rsp); + l2cap_build_conf_rsp(chan, rsp, + L2CAP_CONF_REJECT, flags), rsp); goto unlock; } @@ -3604,8 +3663,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (flags & L2CAP_CONF_FLAG_CONTINUATION) { /* Incomplete config. Send empty response. */ l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, rsp, - L2CAP_CONF_SUCCESS, flags), rsp); + l2cap_build_conf_rsp(chan, rsp, + L2CAP_CONF_SUCCESS, flags), rsp); goto unlock; } @@ -3643,23 +3702,22 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) { u8 buf[64]; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, buf), buf); + l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } /* Got Conf Rsp PENDING from remote side and asume we sent Conf Rsp PENDING in the code above */ if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) && - test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { + test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { /* check compatibility */ - clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); - set_bit(CONF_OUTPUT_DONE, &chan->conf_state); - - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, rsp, - L2CAP_CONF_SUCCESS, flags), rsp); + /* Send rsp for BR/EDR channel */ + if (!chan->ctrl_id) + l2cap_send_efs_conf_rsp(chan, rsp, cmd->ident, flags); + else + chan->ident = cmd->ident; } unlock: @@ -3667,7 +3725,8 @@ unlock: return err; } -static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_config_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data; u16 scid, flags, result; @@ -3699,7 +3758,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr char buf[64]; len = l2cap_parse_conf_rsp(chan, rsp->data, len, - buf, &result); + buf, &result); if (len < 0) { l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; @@ -3707,12 +3766,11 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr /* check compatibility */ - clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); - set_bit(CONF_OUTPUT_DONE, &chan->conf_state); - - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, buf, - L2CAP_CONF_SUCCESS, 0x0000), buf); + if (!chan->ctrl_id) + l2cap_send_efs_conf_rsp(chan, buf, cmd->ident, + 0); + else + chan->ident = cmd->ident; } goto done; @@ -3728,14 +3786,14 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr /* throw out any old stored conf requests */ result = L2CAP_CONF_SUCCESS; len = l2cap_parse_conf_rsp(chan, rsp->data, len, - req, &result); + req, &result); if (len < 0) { l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } l2cap_send_cmd(conn, l2cap_get_ident(conn), - L2CAP_CONF_REQ, len, req); + L2CAP_CONF_REQ, len, req); chan->num_conf_req++; if (result != L2CAP_CONF_SUCCESS) goto done; @@ -3773,7 +3831,8 @@ done: return err; } -static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_disconnect_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_disconn_req *req = (struct l2cap_disconn_req *) data; struct l2cap_disconn_rsp rsp; @@ -3819,7 +3878,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd return 0; } -static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data; u16 dcid, scid; @@ -3853,7 +3913,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd return 0; } -static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_information_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_info_req *req = (struct l2cap_info_req *) data; u16 type; @@ -3870,14 +3931,14 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm rsp->result = __constant_cpu_to_le16(L2CAP_IR_SUCCESS); if (!disable_ertm) feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING - | L2CAP_FEAT_FCS; + | L2CAP_FEAT_FCS; if (enable_hs) feat_mask |= L2CAP_FEAT_EXT_FLOW - | L2CAP_FEAT_EXT_WINDOW; + | L2CAP_FEAT_EXT_WINDOW; put_unaligned_le32(feat_mask, rsp->data); - l2cap_send_cmd(conn, cmd->ident, - L2CAP_INFO_RSP, sizeof(buf), buf); + l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), + buf); } else if (type == L2CAP_IT_FIXED_CHAN) { u8 buf[12]; struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; @@ -3890,20 +3951,21 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm rsp->type = __constant_cpu_to_le16(L2CAP_IT_FIXED_CHAN); rsp->result = __constant_cpu_to_le16(L2CAP_IR_SUCCESS); memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan)); - l2cap_send_cmd(conn, cmd->ident, - L2CAP_INFO_RSP, sizeof(buf), buf); + l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), + buf); } else { struct l2cap_info_rsp rsp; rsp.type = cpu_to_le16(type); rsp.result = __constant_cpu_to_le16(L2CAP_IR_NOTSUPP); - l2cap_send_cmd(conn, cmd->ident, - L2CAP_INFO_RSP, sizeof(rsp), &rsp); + l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp), + &rsp); } return 0; } -static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_information_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) data; u16 type, result; @@ -3915,7 +3977,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm /* L2CAP Info req/rsp are unbound to channels, add extra checks */ if (cmd->ident != conn->info_ident || - conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) + conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) return 0; cancel_delayed_work(&conn->info_timer); @@ -3940,7 +4002,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm conn->info_ident = l2cap_get_ident(conn); l2cap_send_cmd(conn, conn->info_ident, - L2CAP_INFO_REQ, sizeof(req), &req); + L2CAP_INFO_REQ, sizeof(req), &req); } else { conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_ident = 0; @@ -3962,8 +4024,8 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm } static inline int l2cap_create_channel_req(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, - void *data) + struct l2cap_cmd_hdr *cmd, + u16 cmd_len, void *data) { struct l2cap_create_chan_req *req = data; struct l2cap_create_chan_rsp rsp; @@ -3993,7 +4055,8 @@ static inline int l2cap_create_channel_req(struct l2cap_conn *conn, } static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, void *data) + struct l2cap_cmd_hdr *cmd, + void *data) { BT_DBG("conn %p", conn); @@ -4126,7 +4189,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, } static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency, - u16 to_multiplier) + u16 to_multiplier) { u16 max_latency; @@ -4147,7 +4210,8 @@ static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency, } static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u8 *data) + struct l2cap_cmd_hdr *cmd, + u8 *data) { struct hci_conn *hcon = conn->hcon; struct l2cap_conn_param_update_req *req; @@ -4169,7 +4233,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, to_multiplier = __le16_to_cpu(req->to_multiplier); BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x Timeout: 0x%4.4x", - min, max, latency, to_multiplier); + min, max, latency, to_multiplier); memset(&rsp, 0, sizeof(rsp)); @@ -4180,7 +4244,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, rsp.result = __constant_cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED); l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP, - sizeof(rsp), &rsp); + sizeof(rsp), &rsp); if (!err) hci_le_conn_update(hcon, min, max, latency, to_multiplier); @@ -4189,7 +4253,8 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, } static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) { int err = 0; @@ -4203,6 +4268,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, break; case L2CAP_CONN_RSP: + case L2CAP_CREATE_CHAN_RSP: err = l2cap_connect_rsp(conn, cmd, data); break; @@ -4241,10 +4307,6 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, err = l2cap_create_channel_req(conn, cmd, cmd_len, data); break; - case L2CAP_CREATE_CHAN_RSP: - err = l2cap_create_channel_rsp(conn, cmd, data); - break; - case L2CAP_MOVE_CHAN_REQ: err = l2cap_move_channel_req(conn, cmd, cmd_len, data); break; @@ -4271,7 +4333,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, } static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u8 *data) + struct l2cap_cmd_hdr *cmd, u8 *data) { switch (cmd->code) { case L2CAP_COMMAND_REJ: @@ -4290,7 +4352,7 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, } static inline void l2cap_sig_channel(struct l2cap_conn *conn, - struct sk_buff *skb) + struct sk_buff *skb) { u8 *data = skb->data; int len = skb->len; @@ -4307,7 +4369,8 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, cmd_len = le16_to_cpu(cmd.len); - BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len, cmd.ident); + BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len, + cmd.ident); if (cmd_len > len || !cmd.ident) { BT_DBG("corrupted command"); @@ -4326,7 +4389,8 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, /* FIXME: Map err to a valid reason */ rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD); - l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); + l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, + sizeof(rej), &rej); } data += cmd_len; @@ -4391,8 +4455,8 @@ static void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) } } -static void append_skb_frag(struct sk_buff *skb, - struct sk_buff *new_frag, struct sk_buff **last_frag) +static void append_skb_frag(struct sk_buff *skb, struct sk_buff *new_frag, + struct sk_buff **last_frag) { /* skb->len reflects data in skb as well as all fragments * skb->data_len reflects only data in fragments @@ -4641,7 +4705,7 @@ static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq) if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) { if (__seq_offset(chan, txseq, chan->last_acked_seq) >= - chan->tx_win) { + chan->tx_win) { /* See notes below regarding "double poll" and * invalid packets. */ @@ -4682,8 +4746,7 @@ static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq) } if (__seq_offset(chan, txseq, chan->last_acked_seq) < - __seq_offset(chan, chan->expected_tx_seq, - chan->last_acked_seq)){ + __seq_offset(chan, chan->expected_tx_seq, chan->last_acked_seq)) { BT_DBG("Duplicate - expected_tx_seq later than txseq"); return L2CAP_TXSEQ_DUPLICATE; } @@ -5323,7 +5386,7 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) int exact = 0, lm1 = 0, lm2 = 0; struct l2cap_chan *c; - BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); + BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr); /* Find listening sockets and check their link_mode */ read_lock(&chan_list_lock); @@ -5353,7 +5416,7 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) { struct l2cap_conn *conn; - BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); + BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status); if (!status) { conn = l2cap_conn_add(hcon, status); @@ -5443,7 +5506,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) } if (!status && (chan->state == BT_CONNECTED || - chan->state == BT_CONFIG)) { + chan->state == BT_CONFIG)) { struct sock *sk = chan->sk; clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags); @@ -5456,7 +5519,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (chan->state == BT_CONNECT) { if (!status) { - l2cap_send_conn_req(chan); + l2cap_start_connection(chan); } else { __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); } @@ -5470,11 +5533,9 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (!status) { if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { - struct sock *parent = bt_sk(sk)->parent; res = L2CAP_CR_PEND; stat = L2CAP_CS_AUTHOR_PEND; - if (parent) - parent->sk_data_ready(parent, 0); + chan->ops->defer(chan); } else { __l2cap_state_change(chan, BT_CONFIG); res = L2CAP_CR_SUCCESS; @@ -5494,7 +5555,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) rsp.result = cpu_to_le16(res); rsp.status = cpu_to_le16(stat); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); + sizeof(rsp), &rsp); if (!test_bit(CONF_REQ_SENT, &chan->conf_state) && res == L2CAP_CR_SUCCESS) { @@ -5519,6 +5580,12 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) { struct l2cap_conn *conn = hcon->l2cap_data; + struct l2cap_hdr *hdr; + int len; + + /* For AMP controller do not create l2cap conn */ + if (!conn && hcon->hdev->dev_type != HCI_BREDR) + goto drop; if (!conn) conn = l2cap_conn_add(hcon, 0); @@ -5528,10 +5595,10 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags); - if (!(flags & ACL_CONT)) { - struct l2cap_hdr *hdr; - int len; - + switch (flags) { + case ACL_START: + case ACL_START_NO_FLUSH: + case ACL_COMPLETE: if (conn->rx_len) { BT_ERR("Unexpected start frame (len %d)", skb->len); kfree_skb(conn->rx_skb); @@ -5560,20 +5627,22 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) if (skb->len > len) { BT_ERR("Frame is too long (len %d, expected len %d)", - skb->len, len); + skb->len, len); l2cap_conn_unreliable(conn, ECOMM); goto drop; } /* Allocate skb for the complete frame (with header) */ - conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC); + conn->rx_skb = bt_skb_alloc(len, GFP_KERNEL); if (!conn->rx_skb) goto drop; skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); + skb->len); conn->rx_len = len - skb->len; - } else { + break; + + case ACL_CONT: BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); if (!conn->rx_len) { @@ -5584,7 +5653,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) if (skb->len > conn->rx_len) { BT_ERR("Fragment is too long (len %d, expected %d)", - skb->len, conn->rx_len); + skb->len, conn->rx_len); kfree_skb(conn->rx_skb); conn->rx_skb = NULL; conn->rx_len = 0; @@ -5593,7 +5662,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) } skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); + skb->len); conn->rx_len -= skb->len; if (!conn->rx_len) { @@ -5601,6 +5670,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) l2cap_recv_frame(conn, conn->rx_skb); conn->rx_skb = NULL; } + break; } drop: @@ -5617,12 +5687,11 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p) list_for_each_entry(c, &chan_list, global_l) { struct sock *sk = c->sk; - seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n", - batostr(&bt_sk(sk)->src), - batostr(&bt_sk(sk)->dst), - c->state, __le16_to_cpu(c->psm), - c->scid, c->dcid, c->imtu, c->omtu, - c->sec_level, c->mode); + seq_printf(f, "%pMR %pMR %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n", + &bt_sk(sk)->src, &bt_sk(sk)->dst, + c->state, __le16_to_cpu(c->psm), + c->scid, c->dcid, c->imtu, c->omtu, + c->sec_level, c->mode); } read_unlock(&chan_list_lock); @@ -5653,8 +5722,8 @@ int __init l2cap_init(void) return err; if (bt_debugfs) { - l2cap_debugfs = debugfs_create_file("l2cap", 0444, - bt_debugfs, NULL, &l2cap_debugfs_fops); + l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs, + NULL, &l2cap_debugfs_fops); if (!l2cap_debugfs) BT_ERR("Failed to create L2CAP debug file"); } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 083f2bf065d4..89f1472939ec 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -40,7 +40,8 @@ static struct bt_sock_list l2cap_sk_list = { static const struct proto_ops l2cap_sock_ops; static void l2cap_sock_init(struct sock *sk, struct sock *parent); -static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); +static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, + int proto, gfp_t prio); static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { @@ -106,7 +107,8 @@ done: return err; } -static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) +static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, + int alen, int flags) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -134,7 +136,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al lock_sock(sk); err = bt_sock_wait_state(sk, BT_CONNECTED, - sock_sndtimeo(sk, flags & O_NONBLOCK)); + sock_sndtimeo(sk, flags & O_NONBLOCK)); release_sock(sk); @@ -185,7 +187,8 @@ done: return err; } -static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) +static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, + int flags) { DECLARE_WAITQUEUE(wait, current); struct sock *sk = sock->sk, *nsk; @@ -241,7 +244,8 @@ done: return err; } -static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) +static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, + int *len, int peer) { struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; @@ -266,7 +270,8 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l return 0; } -static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) +static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, + char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -309,7 +314,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us break; case BT_SECURITY_HIGH: opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | - L2CAP_LM_SECURE; + L2CAP_LM_SECURE; break; default: opt = 0; @@ -353,7 +358,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us return err; } -static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) +static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, + char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -377,19 +383,20 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case BT_SECURITY: if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && - chan->chan_type != L2CAP_CHAN_RAW) { + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; break; } memset(&sec, 0, sizeof(sec)); - if (chan->conn) + if (chan->conn) { sec.level = chan->conn->hcon->sec_level; - else - sec.level = chan->sec_level; - if (sk->sk_state == BT_CONNECTED) - sec.key_size = chan->conn->hcon->enc_key_size; + if (sk->sk_state == BT_CONNECTED) + sec.key_size = chan->conn->hcon->enc_key_size; + } else { + sec.level = chan->sec_level; + } len = min_t(unsigned int, len, sizeof(sec)); if (copy_to_user(optval, (char *) &sec, len)) @@ -411,14 +418,14 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch case BT_FLUSHABLE: if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags), - (u32 __user *) optval)) + (u32 __user *) optval)) err = -EFAULT; break; case BT_POWER: if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM - && sk->sk_type != SOCK_RAW) { + && sk->sk_type != SOCK_RAW) { err = -EINVAL; break; } @@ -466,7 +473,8 @@ static bool l2cap_valid_mtu(struct l2cap_chan *chan, u16 mtu) return true; } -static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) +static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, + char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -529,6 +537,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us chan->fcs = opts.fcs; chan->max_tx = opts.max_tx; chan->tx_win = opts.txwin_size; + chan->flush_to = opts.flush_to; break; case L2CAP_LM: @@ -564,7 +573,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us return err; } -static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) +static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -587,7 +597,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case BT_SECURITY: if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && - chan->chan_type != L2CAP_CHAN_RAW) { + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; break; } @@ -601,7 +611,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch } if (sec.level < BT_SECURITY_LOW || - sec.level > BT_SECURITY_HIGH) { + sec.level > BT_SECURITY_HIGH) { err = -EINVAL; break; } @@ -627,7 +637,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch /* or for ACL link */ } else if ((sk->sk_state == BT_CONNECT2 && - test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) || + test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) || sk->sk_state == BT_CONNECTED) { if (!l2cap_chan_check_security(chan)) set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags); @@ -684,7 +694,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch case BT_POWER: if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && - chan->chan_type != L2CAP_CHAN_RAW) { + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; break; } @@ -720,7 +730,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch } if (chan->mode != L2CAP_MODE_ERTM && - chan->mode != L2CAP_MODE_STREAMING) { + chan->mode != L2CAP_MODE_STREAMING) { err = -EOPNOTSUPP; break; } @@ -737,7 +747,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch return err; } -static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) +static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -762,7 +773,8 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms return err; } -static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) +static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags) { struct sock *sk = sock->sk; struct l2cap_pinfo *pi = l2cap_pi(sk); @@ -866,7 +878,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) err = bt_sock_wait_state(sk, BT_CLOSED, - sk->sk_lingertime); + sk->sk_lingertime); } if (!err && sk->sk_err) @@ -930,7 +942,7 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) } sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, - GFP_ATOMIC); + GFP_ATOMIC); if (!sk) return NULL; @@ -938,6 +950,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) l2cap_sock_init(sk, parent); + bt_accept_enqueue(parent, sk); + return l2cap_pi(sk)->chan; } @@ -1068,6 +1082,15 @@ static void l2cap_sock_ready_cb(struct l2cap_chan *chan) release_sock(sk); } +static void l2cap_sock_defer_cb(struct l2cap_chan *chan) +{ + struct sock *sk = chan->data; + struct sock *parent = bt_sk(sk)->parent; + + if (parent) + parent->sk_data_ready(parent, 0); +} + static struct l2cap_ops l2cap_chan_ops = { .name = "L2CAP Socket Interface", .new_connection = l2cap_sock_new_connection_cb, @@ -1076,6 +1099,7 @@ static struct l2cap_ops l2cap_chan_ops = { .teardown = l2cap_sock_teardown_cb, .state_change = l2cap_sock_state_change_cb, .ready = l2cap_sock_ready_cb, + .defer = l2cap_sock_defer_cb, .alloc_skb = l2cap_sock_alloc_skb_cb, }; @@ -1083,7 +1107,8 @@ static void l2cap_sock_destruct(struct sock *sk) { BT_DBG("sk %p", sk); - l2cap_chan_put(l2cap_pi(sk)->chan); + if (l2cap_pi(sk)->chan) + l2cap_chan_put(l2cap_pi(sk)->chan); if (l2cap_pi(sk)->rx_busy_skb) { kfree_skb(l2cap_pi(sk)->rx_busy_skb); l2cap_pi(sk)->rx_busy_skb = NULL; @@ -1159,7 +1184,8 @@ static struct proto l2cap_proto = { .obj_size = sizeof(struct l2cap_pinfo) }; -static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) +static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, + int proto, gfp_t prio) { struct sock *sk; struct l2cap_chan *chan; @@ -1204,7 +1230,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, sock->state = SS_UNCONNECTED; if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM && - sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) + sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) @@ -1261,7 +1287,8 @@ int __init l2cap_init_sockets(void) goto error; } - err = bt_procfs_init(THIS_MODULE, &init_net, "l2cap", &l2cap_sk_list, NULL); + err = bt_procfs_init(THIS_MODULE, &init_net, "l2cap", &l2cap_sk_list, + NULL); if (err < 0) { BT_ERR("Failed to create L2CAP proc file"); bt_sock_unregister(BTPROTO_L2CAP); diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index e1c97527e16c..b3fbc73516c4 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -41,20 +41,6 @@ void baswap(bdaddr_t *dst, bdaddr_t *src) } EXPORT_SYMBOL(baswap); -char *batostr(bdaddr_t *ba) -{ - static char str[2][18]; - static int i = 1; - - i ^= 1; - sprintf(str[i], "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", - ba->b[5], ba->b[4], ba->b[3], - ba->b[2], ba->b[1], ba->b[0]); - - return str[i]; -} -EXPORT_SYMBOL(batostr); - /* Bluetooth error codes to Unix errno mapping */ int bt_to_errno(__u16 code) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index aa2ea0a8142c..399e5024b5bd 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3125,6 +3125,9 @@ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, struct pending_cmd *cmd; int err; + mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, + hdev); + cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev); if (!cmd) return -ENOENT; @@ -3137,8 +3140,6 @@ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, mgmt_pending_remove(cmd); - mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, - hdev); return err; } diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index c75107ef8920..201fdf737209 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -377,8 +377,8 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, int err = 0; u8 dlci; - BT_DBG("dlc %p state %ld %s %s channel %d", - d, d->state, batostr(src), batostr(dst), channel); + BT_DBG("dlc %p state %ld %pMR -> %pMR channel %d", + d, d->state, src, dst, channel); if (channel < 1 || channel > 30) return -EINVAL; @@ -676,7 +676,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, struct socket *sock; struct sock *sk; - BT_DBG("%s %s", batostr(src), batostr(dst)); + BT_DBG("%pMR -> %pMR", src, dst); *err = rfcomm_l2sock_create(&sock); if (*err < 0) @@ -709,7 +709,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bacpy(&addr.l2_bdaddr, dst); addr.l2_family = AF_BLUETOOTH; - addr.l2_psm = cpu_to_le16(RFCOMM_PSM); + addr.l2_psm = __constant_cpu_to_le16(RFCOMM_PSM); addr.l2_cid = 0; *err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK); if (*err == 0 || *err == -EINPROGRESS) @@ -1987,7 +1987,7 @@ static int rfcomm_add_listener(bdaddr_t *ba) /* Bind socket */ bacpy(&addr.l2_bdaddr, ba); addr.l2_family = AF_BLUETOOTH; - addr.l2_psm = cpu_to_le16(RFCOMM_PSM); + addr.l2_psm = __constant_cpu_to_le16(RFCOMM_PSM); addr.l2_cid = 0; err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) { @@ -2125,11 +2125,10 @@ static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x) list_for_each_entry(d, &s->dlcs, list) { struct sock *sk = s->sock->sk; - seq_printf(f, "%s %s %ld %d %d %d %d\n", - batostr(&bt_sk(sk)->src), - batostr(&bt_sk(sk)->dst), - d->state, d->dlci, d->mtu, - d->rx_credits, d->tx_credits); + seq_printf(f, "%pMR %pMR %ld %d %d %d %d\n", + &bt_sk(sk)->src, &bt_sk(sk)->dst, + d->state, d->dlci, d->mtu, + d->rx_credits, d->tx_credits); } } diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index b3226f3658cf..4ddef57d03a7 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -334,7 +334,7 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr struct sock *sk = sock->sk; int err = 0; - BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr)); + BT_DBG("sk %p %pMR", sk, &sa->rc_bdaddr); if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; @@ -975,10 +975,9 @@ static int rfcomm_sock_debugfs_show(struct seq_file *f, void *p) read_lock(&rfcomm_sk_list.lock); sk_for_each(sk, node, &rfcomm_sk_list.head) { - seq_printf(f, "%s %s %d %d\n", - batostr(&bt_sk(sk)->src), - batostr(&bt_sk(sk)->dst), - sk->sk_state, rfcomm_pi(sk)->channel); + seq_printf(f, "%pMR %pMR %d %d\n", + &bt_sk(sk)->src, &bt_sk(sk)->dst, + sk->sk_state, rfcomm_pi(sk)->channel); } read_unlock(&rfcomm_sk_list.lock); diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index ccc248791d50..bd6fd0f43d2b 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -166,7 +166,7 @@ static struct device *rfcomm_get_device(struct rfcomm_dev *dev) static ssize_t show_address(struct device *tty_dev, struct device_attribute *attr, char *buf) { struct rfcomm_dev *dev = dev_get_drvdata(tty_dev); - return sprintf(buf, "%s\n", batostr(&dev->dst)); + return sprintf(buf, "%pMR\n", &dev->dst); } static ssize_t show_channel(struct device *tty_dev, struct device_attribute *attr, char *buf) @@ -663,8 +663,8 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) if (!dev) return -ENODEV; - BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), - dev->channel, dev->port.count); + BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst, + dev->channel, dev->port.count); spin_lock_irqsave(&dev->port.lock, flags); if (++dev->port.count > 1) { diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index dc42b917aaaf..450cdcd88e5c 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -172,7 +172,7 @@ static int sco_connect(struct sock *sk) struct hci_dev *hdev; int err, type; - BT_DBG("%s -> %s", batostr(src), batostr(dst)); + BT_DBG("%pMR -> %pMR", src, dst); hdev = hci_get_route(dst, src); if (!hdev) @@ -460,7 +460,7 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le struct sock *sk = sock->sk; int err = 0; - BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr)); + BT_DBG("sk %p %pMR", sk, &sa->sco_bdaddr); if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; @@ -893,7 +893,7 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) struct hlist_node *node; int lm = 0; - BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); + BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr); /* Find listening sockets */ read_lock(&sco_sk_list.lock); @@ -914,7 +914,7 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) void sco_connect_cfm(struct hci_conn *hcon, __u8 status) { - BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); + BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status); if (!status) { struct sco_conn *conn; @@ -959,8 +959,8 @@ static int sco_debugfs_show(struct seq_file *f, void *p) read_lock(&sco_sk_list.lock); sk_for_each(sk, node, &sco_sk_list.head) { - seq_printf(f, "%s %s %d\n", batostr(&bt_sk(sk)->src), - batostr(&bt_sk(sk)->dst), sk->sk_state); + seq_printf(f, "%pMR %pMR %d\n", &bt_sk(sk)->src, + &bt_sk(sk)->dst, sk->sk_state); } read_unlock(&sco_sk_list.lock); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 8c225ef349cd..9176bc17595c 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -32,6 +32,8 @@ #define SMP_TIMEOUT msecs_to_jiffies(30000) +#define AUTH_REQ_MASK 0x07 + static inline void swap128(u8 src[16], u8 dst[16]) { int i; @@ -165,7 +167,7 @@ static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code, lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->len = cpu_to_le16(sizeof(code) + dlen); - lh->cid = cpu_to_le16(L2CAP_CID_SMP); + lh->cid = __constant_cpu_to_le16(L2CAP_CID_SMP); memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code)); @@ -230,7 +232,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, req->max_key_size = SMP_MAX_ENC_KEY_SIZE; req->init_key_dist = 0; req->resp_key_dist = dist_keys; - req->auth_req = authreq; + req->auth_req = (authreq & AUTH_REQ_MASK); return; } @@ -239,7 +241,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE; rsp->init_key_dist = 0; rsp->resp_key_dist = req->resp_key_dist & dist_keys; - rsp->auth_req = authreq; + rsp->auth_req = (authreq & AUTH_REQ_MASK); } static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 070e8a68cfc6..7c78e2640190 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -313,6 +313,8 @@ static const struct net_device_ops br_netdev_ops = { .ndo_fdb_add = br_fdb_add, .ndo_fdb_del = br_fdb_delete, .ndo_fdb_dump = br_fdb_dump, + .ndo_bridge_getlink = br_getlink, + .ndo_bridge_setlink = br_setlink, }; static void br_dev_free(struct net_device *dev) @@ -356,7 +358,7 @@ void br_dev_setup(struct net_device *dev) br->bridge_id.prio[0] = 0x80; br->bridge_id.prio[1] = 0x00; - memcpy(br->group_addr, br_group_address, ETH_ALEN); + memcpy(br->group_addr, eth_reserved_addr_base, ETH_ALEN); br->stp_enabled = BR_NO_STP; br->group_fwd_mask = BR_GROUPFWD_DEFAULT; diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 76f15fda0212..4b34207419b1 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -19,9 +19,6 @@ #include <linux/export.h> #include "br_private.h" -/* Bridge group multicast address 802.1d (pg 51). */ -const u8 br_group_address[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; - /* Hook for brouter */ br_should_route_hook_t __rcu *br_should_route_hook __read_mostly; EXPORT_SYMBOL(br_should_route_hook); @@ -127,18 +124,6 @@ static int br_handle_local_finish(struct sk_buff *skb) return 0; /* process further */ } -/* Does address match the link local multicast address. - * 01:80:c2:00:00:0X - */ -static inline int is_link_local(const unsigned char *dest) -{ - __be16 *a = (__be16 *)dest; - static const __be16 *b = (const __be16 *)br_group_address; - static const __be16 m = cpu_to_be16(0xfff0); - - return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | ((a[2] ^ b[2]) & m)) == 0; -} - /* * Return NULL if skb is handled * note: already called with rcu_read_lock @@ -162,7 +147,7 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb) p = br_port_get_rcu(skb->dev); - if (unlikely(is_link_local(dest))) { + if (unlikely(is_link_local_ether_addr(dest))) { /* * See IEEE 802.1D Table 7-10 Reserved addresses * diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 093f527276a3..65429b99a2a3 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -20,16 +20,43 @@ #include "br_private.h" #include "br_private_stp.h" +static inline size_t br_port_info_size(void) +{ + return nla_total_size(1) /* IFLA_BRPORT_STATE */ + + nla_total_size(2) /* IFLA_BRPORT_PRIORITY */ + + nla_total_size(4) /* IFLA_BRPORT_COST */ + + nla_total_size(1) /* IFLA_BRPORT_MODE */ + + nla_total_size(1) /* IFLA_BRPORT_GUARD */ + + nla_total_size(1) /* IFLA_BRPORT_PROTECT */ + + 0; +} + static inline size_t br_nlmsg_size(void) { return NLMSG_ALIGN(sizeof(struct ifinfomsg)) - + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ - + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ - + nla_total_size(4) /* IFLA_MASTER */ - + nla_total_size(4) /* IFLA_MTU */ - + nla_total_size(4) /* IFLA_LINK */ - + nla_total_size(1) /* IFLA_OPERSTATE */ - + nla_total_size(1); /* IFLA_PROTINFO */ + + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ + + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ + + nla_total_size(4) /* IFLA_MASTER */ + + nla_total_size(4) /* IFLA_MTU */ + + nla_total_size(4) /* IFLA_LINK */ + + nla_total_size(1) /* IFLA_OPERSTATE */ + + nla_total_size(br_port_info_size()); /* IFLA_PROTINFO */ +} + +static int br_port_fill_attrs(struct sk_buff *skb, + const struct net_bridge_port *p) +{ + u8 mode = !!(p->flags & BR_HAIRPIN_MODE); + + if (nla_put_u8(skb, IFLA_BRPORT_STATE, p->state) || + nla_put_u16(skb, IFLA_BRPORT_PRIORITY, p->priority) || + nla_put_u32(skb, IFLA_BRPORT_COST, p->path_cost) || + nla_put_u8(skb, IFLA_BRPORT_MODE, mode) || + nla_put_u8(skb, IFLA_BRPORT_GUARD, !!(p->flags & BR_BPDU_GUARD)) || + nla_put_u8(skb, IFLA_BRPORT_PROTECT, !!(p->flags & BR_ROOT_BLOCK))) + return -EMSGSIZE; + + return 0; } /* @@ -67,10 +94,18 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por (dev->addr_len && nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) || (dev->ifindex != dev->iflink && - nla_put_u32(skb, IFLA_LINK, dev->iflink)) || - (event == RTM_NEWLINK && - nla_put_u8(skb, IFLA_PROTINFO, port->state))) + nla_put_u32(skb, IFLA_LINK, dev->iflink))) goto nla_put_failure; + + if (event == RTM_NEWLINK) { + struct nlattr *nest + = nla_nest_start(skb, IFLA_PROTINFO | NLA_F_NESTED); + + if (nest == NULL || br_port_fill_attrs(skb, port) < 0) + goto nla_put_failure; + nla_nest_end(skb, nest); + } + return nlmsg_end(skb, nlh); nla_put_failure: @@ -111,89 +146,133 @@ errout: /* * Dump information about all ports, in response to GETLINK */ -static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) +int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, + struct net_device *dev) { - struct net *net = sock_net(skb->sk); - struct net_device *dev; - int idx; - - idx = 0; - rcu_read_lock(); - for_each_netdev_rcu(net, dev) { - struct net_bridge_port *port = br_port_get_rcu(dev); - - /* not a bridge port */ - if (!port || idx < cb->args[0]) - goto skip; - - if (br_fill_ifinfo(skb, port, - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, RTM_NEWLINK, - NLM_F_MULTI) < 0) - break; -skip: - ++idx; + int err = 0; + struct net_bridge_port *port = br_port_get_rcu(dev); + + /* not a bridge port */ + if (!port) + goto out; + + err = br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, NLM_F_MULTI); +out: + return err; +} + +static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = { + [IFLA_BRPORT_STATE] = { .type = NLA_U8 }, + [IFLA_BRPORT_COST] = { .type = NLA_U32 }, + [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 }, + [IFLA_BRPORT_MODE] = { .type = NLA_U8 }, + [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, + [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 }, +}; + +/* Change the state of the port and notify spanning tree */ +static int br_set_port_state(struct net_bridge_port *p, u8 state) +{ + if (state > BR_STATE_BLOCKING) + return -EINVAL; + + /* if kernel STP is running, don't allow changes */ + if (p->br->stp_enabled == BR_KERNEL_STP) + return -EBUSY; + + if (!netif_running(p->dev) || + (!netif_carrier_ok(p->dev) && state != BR_STATE_DISABLED)) + return -ENETDOWN; + + p->state = state; + br_log_state(p); + br_port_state_selection(p->br); + return 0; +} + +/* Set/clear or port flags based on attribute */ +static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[], + int attrtype, unsigned long mask) +{ + if (tb[attrtype]) { + u8 flag = nla_get_u8(tb[attrtype]); + if (flag) + p->flags |= mask; + else + p->flags &= ~mask; } - rcu_read_unlock(); - cb->args[0] = idx; +} + +/* Process bridge protocol info on port */ +static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) +{ + int err; - return skb->len; + br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE); + br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); + + if (tb[IFLA_BRPORT_COST]) { + err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST])); + if (err) + return err; + } + + if (tb[IFLA_BRPORT_PRIORITY]) { + err = br_stp_set_port_priority(p, nla_get_u16(tb[IFLA_BRPORT_PRIORITY])); + if (err) + return err; + } + + if (tb[IFLA_BRPORT_STATE]) { + err = br_set_port_state(p, nla_get_u8(tb[IFLA_BRPORT_STATE])); + if (err) + return err; + } + return 0; } -/* - * Change state of port (ie from forwarding to blocking etc) - * Used by spanning tree in user space. - */ -static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +/* Change state and parameters on port. */ +int br_setlink(struct net_device *dev, struct nlmsghdr *nlh) { - struct net *net = sock_net(skb->sk); struct ifinfomsg *ifm; struct nlattr *protinfo; - struct net_device *dev; struct net_bridge_port *p; - u8 new_state; - - if (nlmsg_len(nlh) < sizeof(*ifm)) - return -EINVAL; + struct nlattr *tb[IFLA_BRPORT_MAX]; + int err; ifm = nlmsg_data(nlh); - if (ifm->ifi_family != AF_BRIDGE) - return -EPFNOSUPPORT; protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); - if (!protinfo || nla_len(protinfo) < sizeof(u8)) - return -EINVAL; - - new_state = nla_get_u8(protinfo); - if (new_state > BR_STATE_BLOCKING) - return -EINVAL; - - dev = __dev_get_by_index(net, ifm->ifi_index); - if (!dev) - return -ENODEV; + if (!protinfo) + return 0; p = br_port_get_rtnl(dev); if (!p) return -EINVAL; - /* if kernel STP is running, don't allow changes */ - if (p->br->stp_enabled == BR_KERNEL_STP) - return -EBUSY; - - if (!netif_running(dev) || - (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED)) - return -ENETDOWN; - - p->state = new_state; - br_log_state(p); + if (protinfo->nla_type & NLA_F_NESTED) { + err = nla_parse_nested(tb, IFLA_BRPORT_MAX, + protinfo, ifla_brport_policy); + if (err) + return err; + + spin_lock_bh(&p->br->lock); + err = br_setport(p, tb); + spin_unlock_bh(&p->br->lock); + } else { + /* Binary compatability with old RSTP */ + if (nla_len(protinfo) < sizeof(u8)) + return -EINVAL; - spin_lock_bh(&p->br->lock); - br_port_state_selection(p->br); - spin_unlock_bh(&p->br->lock); + spin_lock_bh(&p->br->lock); + err = br_set_port_state(p, nla_get_u8(protinfo)); + spin_unlock_bh(&p->br->lock); + } - br_ifinfo_notify(RTM_NEWLINK, p); + if (err == 0) + br_ifinfo_notify(RTM_NEWLINK, p); - return 0; + return err; } static int br_validate(struct nlattr *tb[], struct nlattr *data[]) @@ -218,29 +297,7 @@ struct rtnl_link_ops br_link_ops __read_mostly = { int __init br_netlink_init(void) { - int err; - - err = rtnl_link_register(&br_link_ops); - if (err < 0) - goto err1; - - err = __rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, - br_dump_ifinfo, NULL); - if (err) - goto err2; - err = __rtnl_register(PF_BRIDGE, RTM_SETLINK, - br_rtm_setlink, NULL, NULL); - if (err) - goto err3; - - return 0; - -err3: - rtnl_unregister_all(PF_BRIDGE); -err2: - rtnl_link_unregister(&br_link_ops); -err1: - return err; + return rtnl_link_register(&br_link_ops); } void __exit br_netlink_fini(void) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 9b278c4ebee1..eb9cd42146a5 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -135,6 +135,8 @@ struct net_bridge_port unsigned long flags; #define BR_HAIRPIN_MODE 0x00000001 +#define BR_BPDU_GUARD 0x00000002 +#define BR_ROOT_BLOCK 0x00000004 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING u32 multicast_startup_queries_sent; @@ -158,7 +160,9 @@ struct net_bridge_port static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev) { - struct net_bridge_port *port = rcu_dereference(dev->rx_handler_data); + struct net_bridge_port *port = + rcu_dereference_rtnl(dev->rx_handler_data); + return br_port_exists(dev) ? port : NULL; } @@ -288,7 +292,6 @@ struct br_input_skb_cb { pr_debug("%s: " format, (br)->dev->name, ##args) extern struct notifier_block br_device_notifier; -extern const u8 br_group_address[ETH_ALEN]; /* called under bridge lock */ static inline int br_is_root_bridge(const struct net_bridge *br) @@ -553,6 +556,9 @@ extern struct rtnl_link_ops br_link_ops; extern int br_netlink_init(void); extern void br_netlink_fini(void); extern void br_ifinfo_notify(int event, struct net_bridge_port *port); +extern int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg); +extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, + struct net_device *dev); #ifdef CONFIG_SYSFS /* br_sysfs_if.c */ @@ -566,10 +572,10 @@ extern void br_sysfs_delbr(struct net_device *dev); #else -#define br_sysfs_addif(p) (0) -#define br_sysfs_renameif(p) (0) -#define br_sysfs_addbr(dev) (0) -#define br_sysfs_delbr(dev) do { } while(0) +static inline int br_sysfs_addif(struct net_bridge_port *p) { return 0; } +static inline int br_sysfs_renameif(struct net_bridge_port *p) { return 0; } +static inline int br_sysfs_addbr(struct net_device *dev) { return 0; } +static inline void br_sysfs_delbr(struct net_device *dev) { return; } #endif /* CONFIG_SYSFS */ #endif diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index af9a12099ba4..b01849a74310 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -100,6 +100,21 @@ static int br_should_become_root_port(const struct net_bridge_port *p, return 0; } +static void br_root_port_block(const struct net_bridge *br, + struct net_bridge_port *p) +{ + + br_notice(br, "port %u(%s) tried to become root port (blocked)", + (unsigned int) p->port_no, p->dev->name); + + p->state = BR_STATE_LISTENING; + br_log_state(p); + br_ifinfo_notify(RTM_NEWLINK, p); + + if (br->forward_delay > 0) + mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay); +} + /* called under bridge lock */ static void br_root_selection(struct net_bridge *br) { @@ -107,7 +122,12 @@ static void br_root_selection(struct net_bridge *br) u16 root_port = 0; list_for_each_entry(p, &br->port_list, list) { - if (br_should_become_root_port(p, root_port)) + if (!br_should_become_root_port(p, root_port)) + continue; + + if (p->flags & BR_ROOT_BLOCK) + br_root_port_block(br, p); + else root_port = p->port_no; } diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index fd30a6022dea..7f884e3fb955 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -170,6 +170,13 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb, if (!ether_addr_equal(dest, br->group_addr)) goto out; + if (p->flags & BR_BPDU_GUARD) { + br_notice(br, "BPDU received on blocked port %u(%s)\n", + (unsigned int) p->port_no, p->dev->name); + br_stp_disable_port(p); + goto out; + } + buf = skb_pull(skb, 3); if (buf[0] == BPDU_TYPE_CONFIG) { diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index c5c059333eab..cffb76e2161c 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -14,6 +14,7 @@ #include <linux/capability.h> #include <linux/kernel.h> #include <linux/netdevice.h> +#include <linux/etherdevice.h> #include <linux/if_bridge.h> #include <linux/rtnetlink.h> #include <linux/spinlock.h> @@ -297,23 +298,18 @@ static ssize_t store_group_addr(struct device *d, const char *buf, size_t len) { struct net_bridge *br = to_bridge(d); - unsigned int new_addr[6]; + u8 new_addr[6]; int i; if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (sscanf(buf, "%x:%x:%x:%x:%x:%x", + if (sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &new_addr[0], &new_addr[1], &new_addr[2], &new_addr[3], &new_addr[4], &new_addr[5]) != 6) return -EINVAL; - /* Must be 01:80:c2:00:00:0X */ - for (i = 0; i < 5; i++) - if (new_addr[i] != br_group_address[i]) - return -EINVAL; - - if (new_addr[5] & ~0xf) + if (!is_link_local_ether_addr(new_addr)) return -EINVAL; if (new_addr[5] == 1 || /* 802.3x Pause address */ diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 13b36bdc76a7..80a4fc5d96ab 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -34,6 +34,28 @@ const struct brport_attribute brport_attr_##_name = { \ .store = _store, \ }; +#define BRPORT_ATTR_FLAG(_name, _mask) \ +static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \ +{ \ + return sprintf(buf, "%d\n", !!(p->flags & _mask)); \ +} \ +static int store_##_name(struct net_bridge_port *p, unsigned long v) \ +{ \ + unsigned long flags = p->flags; \ + if (v) \ + flags |= _mask; \ + else \ + flags &= ~_mask; \ + if (flags != p->flags) { \ + p->flags = flags; \ + br_ifinfo_notify(RTM_NEWLINK, p); \ + } \ + return 0; \ +} \ +static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR, \ + show_##_name, store_##_name) + + static ssize_t show_path_cost(struct net_bridge_port *p, char *buf) { return sprintf(buf, "%d\n", p->path_cost); @@ -133,21 +155,9 @@ static int store_flush(struct net_bridge_port *p, unsigned long v) } static BRPORT_ATTR(flush, S_IWUSR, NULL, store_flush); -static ssize_t show_hairpin_mode(struct net_bridge_port *p, char *buf) -{ - int hairpin_mode = (p->flags & BR_HAIRPIN_MODE) ? 1 : 0; - return sprintf(buf, "%d\n", hairpin_mode); -} -static int store_hairpin_mode(struct net_bridge_port *p, unsigned long v) -{ - if (v) - p->flags |= BR_HAIRPIN_MODE; - else - p->flags &= ~BR_HAIRPIN_MODE; - return 0; -} -static BRPORT_ATTR(hairpin_mode, S_IRUGO | S_IWUSR, - show_hairpin_mode, store_hairpin_mode); +BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE); +BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD); +BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK); #ifdef CONFIG_BRIDGE_IGMP_SNOOPING static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) @@ -181,6 +191,8 @@ static const struct brport_attribute *brport_attrs[] = { &brport_attr_hold_timer, &brport_attr_flush, &brport_attr_hairpin_mode, + &brport_attr_bpdu_guard, + &brport_attr_root_block, #ifdef CONFIG_BRIDGE_IGMP_SNOOPING &brport_attr_multicast_router, #endif diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 159aa8bef9e7..3ef1759403b4 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -2300,10 +2300,11 @@ restart: mutex_unlock(&con->mutex); return; } else { - con->ops->put(con); dout("con_work %p FAILED to back off %lu\n", con, con->delay); + set_bit(CON_FLAG_BACKOFF, &con->flags); } + goto done; } if (con->state == CON_STATE_STANDBY) { @@ -2749,7 +2750,8 @@ static int ceph_con_in_msg_alloc(struct ceph_connection *con, int *skip) msg = con->ops->alloc_msg(con, hdr, skip); mutex_lock(&con->mutex); if (con->state != CON_STATE_OPEN) { - ceph_msg_put(msg); + if (msg) + ceph_msg_put(msg); return -EAGAIN; } con->in_msg = msg; diff --git a/net/core/dev.c b/net/core/dev.c index b4978e2d6ddf..cf105e886cca 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -176,8 +176,10 @@ #define PTYPE_HASH_MASK (PTYPE_HASH_SIZE - 1) static DEFINE_SPINLOCK(ptype_lock); +static DEFINE_SPINLOCK(offload_lock); static struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly; static struct list_head ptype_all __read_mostly; /* Taps */ +static struct list_head offload_base __read_mostly; /* * The @dev_base_head list is protected by @dev_base_lock and the rtnl @@ -470,6 +472,82 @@ void dev_remove_pack(struct packet_type *pt) } EXPORT_SYMBOL(dev_remove_pack); + +/** + * dev_add_offload - register offload handlers + * @po: protocol offload declaration + * + * Add protocol offload handlers to the networking stack. The passed + * &proto_offload is linked into kernel lists and may not be freed until + * it has been removed from the kernel lists. + * + * This call does not sleep therefore it can not + * guarantee all CPU's that are in middle of receiving packets + * will see the new offload handlers (until the next received packet). + */ +void dev_add_offload(struct packet_offload *po) +{ + struct list_head *head = &offload_base; + + spin_lock(&offload_lock); + list_add_rcu(&po->list, head); + spin_unlock(&offload_lock); +} +EXPORT_SYMBOL(dev_add_offload); + +/** + * __dev_remove_offload - remove offload handler + * @po: packet offload declaration + * + * Remove a protocol offload handler that was previously added to the + * kernel offload handlers by dev_add_offload(). The passed &offload_type + * is removed from the kernel lists and can be freed or reused once this + * function returns. + * + * The packet type might still be in use by receivers + * and must not be freed until after all the CPU's have gone + * through a quiescent state. + */ +void __dev_remove_offload(struct packet_offload *po) +{ + struct list_head *head = &offload_base; + struct packet_offload *po1; + + spin_lock(&ptype_lock); + + list_for_each_entry(po1, head, list) { + if (po == po1) { + list_del_rcu(&po->list); + goto out; + } + } + + pr_warn("dev_remove_offload: %p not found\n", po); +out: + spin_unlock(&ptype_lock); +} +EXPORT_SYMBOL(__dev_remove_offload); + +/** + * dev_remove_offload - remove packet offload handler + * @po: packet offload declaration + * + * Remove a packet offload handler that was previously added to the kernel + * offload handlers by dev_add_offload(). The passed &offload_type is + * removed from the kernel lists and can be freed or reused once this + * function returns. + * + * This call sleeps to guarantee that no CPU is looking at the packet + * type after return. + */ +void dev_remove_offload(struct packet_offload *po) +{ + __dev_remove_offload(po); + + synchronize_net(); +} +EXPORT_SYMBOL(dev_remove_offload); + /****************************************************************************** Device Boot-time Settings Routines @@ -1666,7 +1744,7 @@ static inline int deliver_skb(struct sk_buff *skb, static inline bool skb_loop_sk(struct packet_type *ptype, struct sk_buff *skb) { - if (ptype->af_packet_priv == NULL) + if (!ptype->af_packet_priv || !skb->sk) return false; if (ptype->id_match) @@ -1994,7 +2072,7 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features) { struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); - struct packet_type *ptype; + struct packet_offload *ptype; __be16 type = skb->protocol; int vlan_depth = ETH_HLEN; int err; @@ -2023,18 +2101,17 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, } rcu_read_lock(); - list_for_each_entry_rcu(ptype, - &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) { - if (ptype->type == type && !ptype->dev && ptype->gso_segment) { + list_for_each_entry_rcu(ptype, &offload_base, list) { + if (ptype->type == type && ptype->callbacks.gso_segment) { if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { - err = ptype->gso_send_check(skb); + err = ptype->callbacks.gso_send_check(skb); segs = ERR_PTR(err); if (err || skb_gso_ok(skb, features)) break; __skb_push(skb, (skb->data - skb_network_header(skb))); } - segs = ptype->gso_segment(skb, features); + segs = ptype->callbacks.gso_segment(skb, features); break; } } @@ -3444,9 +3521,9 @@ static void flush_backlog(void *arg) static int napi_gro_complete(struct sk_buff *skb) { - struct packet_type *ptype; + struct packet_offload *ptype; __be16 type = skb->protocol; - struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK]; + struct list_head *head = &offload_base; int err = -ENOENT; if (NAPI_GRO_CB(skb)->count == 1) { @@ -3456,10 +3533,10 @@ static int napi_gro_complete(struct sk_buff *skb) rcu_read_lock(); list_for_each_entry_rcu(ptype, head, list) { - if (ptype->type != type || ptype->dev || !ptype->gro_complete) + if (ptype->type != type || !ptype->callbacks.gro_complete) continue; - err = ptype->gro_complete(skb); + err = ptype->callbacks.gro_complete(skb); break; } rcu_read_unlock(); @@ -3506,9 +3583,9 @@ EXPORT_SYMBOL(napi_gro_flush); enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) { struct sk_buff **pp = NULL; - struct packet_type *ptype; + struct packet_offload *ptype; __be16 type = skb->protocol; - struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK]; + struct list_head *head = &offload_base; int same_flow; int mac_len; enum gro_result ret; @@ -3521,7 +3598,7 @@ enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) rcu_read_lock(); list_for_each_entry_rcu(ptype, head, list) { - if (ptype->type != type || ptype->dev || !ptype->gro_receive) + if (ptype->type != type || !ptype->callbacks.gro_receive) continue; skb_set_network_header(skb, skb_gro_offset(skb)); @@ -3531,7 +3608,7 @@ enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) NAPI_GRO_CB(skb)->flush = 0; NAPI_GRO_CB(skb)->free = 0; - pp = ptype->gro_receive(&napi->gro_list, skb); + pp = ptype->callbacks.gro_receive(&napi->gro_list, skb); break; } rcu_read_unlock(); @@ -6661,6 +6738,8 @@ static int __init net_dev_init(void) for (i = 0; i < PTYPE_HASH_SIZE; i++) INIT_LIST_HEAD(&ptype_base[i]); + INIT_LIST_HEAD(&offload_base); + if (register_pernet_subsys(&netdev_net_ops)) goto out; diff --git a/net/core/filter.c b/net/core/filter.c index 3d92ebb7fbcf..c23543cba132 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -39,6 +39,7 @@ #include <linux/reciprocal_div.h> #include <linux/ratelimit.h> #include <linux/seccomp.h> +#include <linux/if_vlan.h> /* No hurry in this branch * @@ -341,6 +342,12 @@ load_b: case BPF_S_ANC_CPU: A = raw_smp_processor_id(); continue; + case BPF_S_ANC_VLAN_TAG: + A = vlan_tx_tag_get(skb); + continue; + case BPF_S_ANC_VLAN_TAG_PRESENT: + A = !!vlan_tx_tag_present(skb); + continue; case BPF_S_ANC_NLATTR: { struct nlattr *nla; @@ -600,6 +607,8 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) ANCILLARY(RXHASH); ANCILLARY(CPU); ANCILLARY(ALU_XOR_X); + ANCILLARY(VLAN_TAG); + ANCILLARY(VLAN_TAG_PRESENT); } } ftest->code = code; @@ -751,3 +760,133 @@ int sk_detach_filter(struct sock *sk) return ret; } EXPORT_SYMBOL_GPL(sk_detach_filter); + +static void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to) +{ + static const u16 decodes[] = { + [BPF_S_ALU_ADD_K] = BPF_ALU|BPF_ADD|BPF_K, + [BPF_S_ALU_ADD_X] = BPF_ALU|BPF_ADD|BPF_X, + [BPF_S_ALU_SUB_K] = BPF_ALU|BPF_SUB|BPF_K, + [BPF_S_ALU_SUB_X] = BPF_ALU|BPF_SUB|BPF_X, + [BPF_S_ALU_MUL_K] = BPF_ALU|BPF_MUL|BPF_K, + [BPF_S_ALU_MUL_X] = BPF_ALU|BPF_MUL|BPF_X, + [BPF_S_ALU_DIV_X] = BPF_ALU|BPF_DIV|BPF_X, + [BPF_S_ALU_MOD_K] = BPF_ALU|BPF_MOD|BPF_K, + [BPF_S_ALU_MOD_X] = BPF_ALU|BPF_MOD|BPF_X, + [BPF_S_ALU_AND_K] = BPF_ALU|BPF_AND|BPF_K, + [BPF_S_ALU_AND_X] = BPF_ALU|BPF_AND|BPF_X, + [BPF_S_ALU_OR_K] = BPF_ALU|BPF_OR|BPF_K, + [BPF_S_ALU_OR_X] = BPF_ALU|BPF_OR|BPF_X, + [BPF_S_ALU_XOR_K] = BPF_ALU|BPF_XOR|BPF_K, + [BPF_S_ALU_XOR_X] = BPF_ALU|BPF_XOR|BPF_X, + [BPF_S_ALU_LSH_K] = BPF_ALU|BPF_LSH|BPF_K, + [BPF_S_ALU_LSH_X] = BPF_ALU|BPF_LSH|BPF_X, + [BPF_S_ALU_RSH_K] = BPF_ALU|BPF_RSH|BPF_K, + [BPF_S_ALU_RSH_X] = BPF_ALU|BPF_RSH|BPF_X, + [BPF_S_ALU_NEG] = BPF_ALU|BPF_NEG, + [BPF_S_LD_W_ABS] = BPF_LD|BPF_W|BPF_ABS, + [BPF_S_LD_H_ABS] = BPF_LD|BPF_H|BPF_ABS, + [BPF_S_LD_B_ABS] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_PROTOCOL] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_PKTTYPE] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_IFINDEX] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_NLATTR] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_NLATTR_NEST] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_MARK] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_QUEUE] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_HATYPE] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_RXHASH] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_CPU] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_ALU_XOR_X] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_SECCOMP_LD_W] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_VLAN_TAG] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_VLAN_TAG_PRESENT] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_LD_W_LEN] = BPF_LD|BPF_W|BPF_LEN, + [BPF_S_LD_W_IND] = BPF_LD|BPF_W|BPF_IND, + [BPF_S_LD_H_IND] = BPF_LD|BPF_H|BPF_IND, + [BPF_S_LD_B_IND] = BPF_LD|BPF_B|BPF_IND, + [BPF_S_LD_IMM] = BPF_LD|BPF_IMM, + [BPF_S_LDX_W_LEN] = BPF_LDX|BPF_W|BPF_LEN, + [BPF_S_LDX_B_MSH] = BPF_LDX|BPF_B|BPF_MSH, + [BPF_S_LDX_IMM] = BPF_LDX|BPF_IMM, + [BPF_S_MISC_TAX] = BPF_MISC|BPF_TAX, + [BPF_S_MISC_TXA] = BPF_MISC|BPF_TXA, + [BPF_S_RET_K] = BPF_RET|BPF_K, + [BPF_S_RET_A] = BPF_RET|BPF_A, + [BPF_S_ALU_DIV_K] = BPF_ALU|BPF_DIV|BPF_K, + [BPF_S_LD_MEM] = BPF_LD|BPF_MEM, + [BPF_S_LDX_MEM] = BPF_LDX|BPF_MEM, + [BPF_S_ST] = BPF_ST, + [BPF_S_STX] = BPF_STX, + [BPF_S_JMP_JA] = BPF_JMP|BPF_JA, + [BPF_S_JMP_JEQ_K] = BPF_JMP|BPF_JEQ|BPF_K, + [BPF_S_JMP_JEQ_X] = BPF_JMP|BPF_JEQ|BPF_X, + [BPF_S_JMP_JGE_K] = BPF_JMP|BPF_JGE|BPF_K, + [BPF_S_JMP_JGE_X] = BPF_JMP|BPF_JGE|BPF_X, + [BPF_S_JMP_JGT_K] = BPF_JMP|BPF_JGT|BPF_K, + [BPF_S_JMP_JGT_X] = BPF_JMP|BPF_JGT|BPF_X, + [BPF_S_JMP_JSET_K] = BPF_JMP|BPF_JSET|BPF_K, + [BPF_S_JMP_JSET_X] = BPF_JMP|BPF_JSET|BPF_X, + }; + u16 code; + + code = filt->code; + + to->code = decodes[code]; + to->jt = filt->jt; + to->jf = filt->jf; + + if (code == BPF_S_ALU_DIV_K) { + /* + * When loaded this rule user gave us X, which was + * translated into R = r(X). Now we calculate the + * RR = r(R) and report it back. If next time this + * value is loaded and RRR = r(RR) is calculated + * then the R == RRR will be true. + * + * One exception. X == 1 translates into R == 0 and + * we can't calculate RR out of it with r(). + */ + + if (filt->k == 0) + to->k = 1; + else + to->k = reciprocal_value(filt->k); + + BUG_ON(reciprocal_value(to->k) != filt->k); + } else + to->k = filt->k; +} + +int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, unsigned int len) +{ + struct sk_filter *filter; + int i, ret; + + lock_sock(sk); + filter = rcu_dereference_protected(sk->sk_filter, + sock_owned_by_user(sk)); + ret = 0; + if (!filter) + goto out; + ret = filter->len; + if (!len) + goto out; + ret = -EINVAL; + if (len < filter->len) + goto out; + + ret = -EFAULT; + for (i = 0; i < filter->len; i++) { + struct sock_filter fb; + + sk_decode_filter(&filter->insns[i], &fb); + if (copy_to_user(&ubuf[i], &fb, sizeof(fb))) + goto out; + } + + ret = filter->len; +out: + release_sock(sk); + return ret; +} diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 1d1c216ffd9a..b29dacf900f9 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -419,20 +419,6 @@ struct pktgen_thread { #define REMOVE 1 #define FIND 0 -static inline ktime_t ktime_now(void) -{ - struct timespec ts; - ktime_get_ts(&ts); - - return timespec_to_ktime(ts); -} - -/* This works even if 32 bit because of careful byte order choice */ -static inline int ktime_lt(const ktime_t cmp1, const ktime_t cmp2) -{ - return cmp1.tv64 < cmp2.tv64; -} - static const char version[] = "Packet Generator for packet performance testing. " "Version: " VERSION "\n"; @@ -675,7 +661,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v) seq_puts(seq, "\n"); /* not really stopped, more like last-running-at */ - stopped = pkt_dev->running ? ktime_now() : pkt_dev->stopped_at; + stopped = pkt_dev->running ? ktime_get() : pkt_dev->stopped_at; idle = pkt_dev->idle_acc; do_div(idle, NSEC_PER_USEC); @@ -2141,12 +2127,12 @@ static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until) return; } - start_time = ktime_now(); + start_time = ktime_get(); if (remaining < 100000) { /* for small delays (<100us), just loop until limit is reached */ do { - end_time = ktime_now(); - } while (ktime_lt(end_time, spin_until)); + end_time = ktime_get(); + } while (ktime_compare(end_time, spin_until) < 0); } else { /* see do_nanosleep */ hrtimer_init_sleeper(&t, current); @@ -2162,7 +2148,7 @@ static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until) hrtimer_cancel(&t.timer); } while (t.task && pkt_dev->running && !signal_pending(current)); __set_current_state(TASK_RUNNING); - end_time = ktime_now(); + end_time = ktime_get(); } pkt_dev->idle_acc += ktime_to_ns(ktime_sub(end_time, start_time)); @@ -2912,8 +2898,7 @@ static void pktgen_run(struct pktgen_thread *t) pktgen_clear_counters(pkt_dev); pkt_dev->running = 1; /* Cranke yeself! */ pkt_dev->skb = NULL; - pkt_dev->started_at = - pkt_dev->next_tx = ktime_now(); + pkt_dev->started_at = pkt_dev->next_tx = ktime_get(); set_pkt_overhead(pkt_dev); @@ -3072,7 +3057,7 @@ static int pktgen_stop_device(struct pktgen_dev *pkt_dev) kfree_skb(pkt_dev->skb); pkt_dev->skb = NULL; - pkt_dev->stopped_at = ktime_now(); + pkt_dev->stopped_at = ktime_get(); pkt_dev->running = 0; show_results(pkt_dev, nr_frags); @@ -3091,7 +3076,7 @@ static struct pktgen_dev *next_to_run(struct pktgen_thread *t) continue; if (best == NULL) best = pkt_dev; - else if (ktime_lt(pkt_dev->next_tx, best->next_tx)) + else if (ktime_compare(pkt_dev->next_tx, best->next_tx) < 0) best = pkt_dev; } if_unlock(t); @@ -3176,14 +3161,14 @@ static void pktgen_rem_thread(struct pktgen_thread *t) static void pktgen_resched(struct pktgen_dev *pkt_dev) { - ktime_t idle_start = ktime_now(); + ktime_t idle_start = ktime_get(); schedule(); - pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start)); + pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start)); } static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev) { - ktime_t idle_start = ktime_now(); + ktime_t idle_start = ktime_get(); while (atomic_read(&(pkt_dev->skb->users)) != 1) { if (signal_pending(current)) @@ -3194,7 +3179,7 @@ static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev) else cpu_relax(); } - pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start)); + pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start)); } static void pktgen_xmit(struct pktgen_dev *pkt_dev) @@ -3216,7 +3201,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) * "never transmit" */ if (unlikely(pkt_dev->delay == ULLONG_MAX)) { - pkt_dev->next_tx = ktime_add_ns(ktime_now(), ULONG_MAX); + pkt_dev->next_tx = ktime_add_ns(ktime_get(), ULONG_MAX); return; } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 64fe3cca2a4e..a810f6a61372 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2192,7 +2192,8 @@ static int nlmsg_populate_fdb(struct sk_buff *skb, goto skip; err = nlmsg_populate_fdb_fill(skb, dev, ha->addr, - portid, seq, 0, NTF_SELF); + portid, seq, + RTM_NEWNEIGH, NTF_SELF); if (err < 0) return err; skip: @@ -2252,6 +2253,211 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) return skb->len; } +int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, + struct net_device *dev, u16 mode) +{ + struct nlmsghdr *nlh; + struct ifinfomsg *ifm; + struct nlattr *br_afspec; + u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN; + + nlh = nlmsg_put(skb, pid, seq, RTM_NEWLINK, sizeof(*ifm), NLM_F_MULTI); + if (nlh == NULL) + return -EMSGSIZE; + + ifm = nlmsg_data(nlh); + ifm->ifi_family = AF_BRIDGE; + ifm->__ifi_pad = 0; + ifm->ifi_type = dev->type; + ifm->ifi_index = dev->ifindex; + ifm->ifi_flags = dev_get_flags(dev); + ifm->ifi_change = 0; + + + if (nla_put_string(skb, IFLA_IFNAME, dev->name) || + nla_put_u32(skb, IFLA_MTU, dev->mtu) || + nla_put_u8(skb, IFLA_OPERSTATE, operstate) || + (dev->master && + nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) || + (dev->addr_len && + nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) || + (dev->ifindex != dev->iflink && + nla_put_u32(skb, IFLA_LINK, dev->iflink))) + goto nla_put_failure; + + br_afspec = nla_nest_start(skb, IFLA_AF_SPEC); + if (!br_afspec) + goto nla_put_failure; + + if (nla_put_u16(skb, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF) || + nla_put_u16(skb, IFLA_BRIDGE_MODE, mode)) { + nla_nest_cancel(skb, br_afspec); + goto nla_put_failure; + } + nla_nest_end(skb, br_afspec); + + return nlmsg_end(skb, nlh); +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; +} +EXPORT_SYMBOL(ndo_dflt_bridge_getlink); + +static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + int idx = 0; + u32 portid = NETLINK_CB(cb->skb).portid; + u32 seq = cb->nlh->nlmsg_seq; + + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { + const struct net_device_ops *ops = dev->netdev_ops; + struct net_device *master = dev->master; + + if (master && master->netdev_ops->ndo_bridge_getlink) { + if (idx >= cb->args[0] && + master->netdev_ops->ndo_bridge_getlink( + skb, portid, seq, dev) < 0) + break; + idx++; + } + + if (ops->ndo_bridge_getlink) { + if (idx >= cb->args[0] && + ops->ndo_bridge_getlink(skb, portid, seq, dev) < 0) + break; + idx++; + } + } + rcu_read_unlock(); + cb->args[0] = idx; + + return skb->len; +} + +static inline size_t bridge_nlmsg_size(void) +{ + return NLMSG_ALIGN(sizeof(struct ifinfomsg)) + + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ + + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ + + nla_total_size(sizeof(u32)) /* IFLA_MASTER */ + + nla_total_size(sizeof(u32)) /* IFLA_MTU */ + + nla_total_size(sizeof(u32)) /* IFLA_LINK */ + + nla_total_size(sizeof(u32)) /* IFLA_OPERSTATE */ + + nla_total_size(sizeof(u8)) /* IFLA_PROTINFO */ + + nla_total_size(sizeof(struct nlattr)) /* IFLA_AF_SPEC */ + + nla_total_size(sizeof(u16)) /* IFLA_BRIDGE_FLAGS */ + + nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_MODE */ +} + +static int rtnl_bridge_notify(struct net_device *dev, u16 flags) +{ + struct net *net = dev_net(dev); + struct net_device *master = dev->master; + struct sk_buff *skb; + int err = -EOPNOTSUPP; + + skb = nlmsg_new(bridge_nlmsg_size(), GFP_ATOMIC); + if (!skb) { + err = -ENOMEM; + goto errout; + } + + if ((!flags || (flags & BRIDGE_FLAGS_MASTER)) && + master && master->netdev_ops->ndo_bridge_getlink) { + err = master->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); + if (err < 0) + goto errout; + } + + if ((flags & BRIDGE_FLAGS_SELF) && + dev->netdev_ops->ndo_bridge_getlink) { + err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); + if (err < 0) + goto errout; + } + + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); + return 0; +errout: + WARN_ON(err == -EMSGSIZE); + kfree_skb(skb); + rtnl_set_sk_err(net, RTNLGRP_LINK, err); + return err; +} + +static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, + void *arg) +{ + struct net *net = sock_net(skb->sk); + struct ifinfomsg *ifm; + struct net_device *dev; + struct nlattr *br_spec, *attr = NULL; + int rem, err = -EOPNOTSUPP; + u16 oflags, flags = 0; + bool have_flags = false; + + if (nlmsg_len(nlh) < sizeof(*ifm)) + return -EINVAL; + + ifm = nlmsg_data(nlh); + if (ifm->ifi_family != AF_BRIDGE) + return -EPFNOSUPPORT; + + dev = __dev_get_by_index(net, ifm->ifi_index); + if (!dev) { + pr_info("PF_BRIDGE: RTM_SETLINK with unknown ifindex\n"); + return -ENODEV; + } + + br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); + if (br_spec) { + nla_for_each_nested(attr, br_spec, rem) { + if (nla_type(attr) == IFLA_BRIDGE_FLAGS) { + have_flags = true; + flags = nla_get_u16(attr); + break; + } + } + } + + oflags = flags; + + if (!flags || (flags & BRIDGE_FLAGS_MASTER)) { + if (!dev->master || + !dev->master->netdev_ops->ndo_bridge_setlink) { + err = -EOPNOTSUPP; + goto out; + } + + err = dev->master->netdev_ops->ndo_bridge_setlink(dev, nlh); + if (err) + goto out; + + flags &= ~BRIDGE_FLAGS_MASTER; + } + + if ((flags & BRIDGE_FLAGS_SELF)) { + if (!dev->netdev_ops->ndo_bridge_setlink) + err = -EOPNOTSUPP; + else + err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh); + + if (!err) + flags &= ~BRIDGE_FLAGS_SELF; + } + + if (have_flags) + memcpy(nla_data(attr), &flags, sizeof(flags)); + /* Generate event to notify upper layer of bridge change */ + if (!err) + err = rtnl_bridge_notify(dev, oflags); +out: + return err; +} + /* Protected by RTNL sempahore. */ static struct rtattr **rta_buf; static int rtattr_max; @@ -2433,5 +2639,8 @@ void __init rtnetlink_init(void) rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, NULL); rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, NULL); rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL); + + rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL); + rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL); } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 6e04b1fa11f2..880722e22cc5 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -519,7 +519,7 @@ static void skb_release_data(struct sk_buff *skb) uarg = skb_shinfo(skb)->destructor_arg; if (uarg->callback) - uarg->callback(uarg); + uarg->callback(uarg, true); } if (skb_has_frag_list(skb)) @@ -635,6 +635,26 @@ void kfree_skb(struct sk_buff *skb) EXPORT_SYMBOL(kfree_skb); /** + * skb_tx_error - report an sk_buff xmit error + * @skb: buffer that triggered an error + * + * Report xmit error if a device callback is tracking this skb. + * skb must be freed afterwards. + */ +void skb_tx_error(struct sk_buff *skb) +{ + if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) { + struct ubuf_info *uarg; + + uarg = skb_shinfo(skb)->destructor_arg; + if (uarg->callback) + uarg->callback(uarg, false); + skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY; + } +} +EXPORT_SYMBOL(skb_tx_error); + +/** * consume_skb - free an skbuff * @skb: buffer to free * @@ -797,7 +817,7 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) for (i = 0; i < num_frags; i++) skb_frag_unref(skb, i); - uarg->callback(uarg); + uarg->callback(uarg, false); /* skb frags point to kernel buffers */ for (i = num_frags - 1; i >= 0; i--) { @@ -3379,10 +3399,12 @@ EXPORT_SYMBOL(__skb_warn_lro_forwarding); void kfree_skb_partial(struct sk_buff *skb, bool head_stolen) { - if (head_stolen) + if (head_stolen) { + skb_release_head_state(skb); kmem_cache_free(skbuff_head_cache, skb); - else + } else { __kfree_skb(skb); + } } EXPORT_SYMBOL(kfree_skb_partial); diff --git a/net/core/sock.c b/net/core/sock.c index 0a023b8daa55..06286006a2cc 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1077,6 +1077,12 @@ int sock_getsockopt(struct socket *sock, int level, int optname, case SO_BINDTODEVICE: v.val = sk->sk_bound_dev_if; break; + case SO_GET_FILTER: + len = sk_get_filter(sk, (struct sock_filter __user *)optval, len); + if (len < 0) + return len; + + goto lenout; default: return -ENOPROTOOPT; } diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index ea850ce35d4a..662071b249cc 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -174,8 +174,7 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, * To protect against Request floods, increment retrans * counter (backoff, monitored by dccp_response_timer). */ - req->retrans++; - req->rsk_ops->rtx_syn_ack(sk, req, NULL); + inet_rtx_syn_ack(sk, req); } /* Network Duplicate, discard packet */ return NULL; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 766c59658563..d5e5a054123c 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1251,7 +1251,7 @@ EXPORT_SYMBOL(inet_sk_rebuild_header); static int inet_gso_send_check(struct sk_buff *skb) { - const struct net_protocol *ops; + const struct net_offload *ops; const struct iphdr *iph; int proto; int ihl; @@ -1275,9 +1275,9 @@ static int inet_gso_send_check(struct sk_buff *skb) err = -EPROTONOSUPPORT; rcu_read_lock(); - ops = rcu_dereference(inet_protos[proto]); - if (likely(ops && ops->gso_send_check)) - err = ops->gso_send_check(skb); + ops = rcu_dereference(inet_offloads[proto]); + if (likely(ops && ops->callbacks.gso_send_check)) + err = ops->callbacks.gso_send_check(skb); rcu_read_unlock(); out: @@ -1288,7 +1288,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, netdev_features_t features) { struct sk_buff *segs = ERR_PTR(-EINVAL); - const struct net_protocol *ops; + const struct net_offload *ops; struct iphdr *iph; int proto; int ihl; @@ -1325,9 +1325,9 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, segs = ERR_PTR(-EPROTONOSUPPORT); rcu_read_lock(); - ops = rcu_dereference(inet_protos[proto]); - if (likely(ops && ops->gso_segment)) - segs = ops->gso_segment(skb, features); + ops = rcu_dereference(inet_offloads[proto]); + if (likely(ops && ops->callbacks.gso_segment)) + segs = ops->callbacks.gso_segment(skb, features); rcu_read_unlock(); if (!segs || IS_ERR(segs)) @@ -1356,7 +1356,7 @@ out: static struct sk_buff **inet_gro_receive(struct sk_buff **head, struct sk_buff *skb) { - const struct net_protocol *ops; + const struct net_offload *ops; struct sk_buff **pp = NULL; struct sk_buff *p; const struct iphdr *iph; @@ -1378,8 +1378,8 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head, proto = iph->protocol; rcu_read_lock(); - ops = rcu_dereference(inet_protos[proto]); - if (!ops || !ops->gro_receive) + ops = rcu_dereference(inet_offloads[proto]); + if (!ops || !ops->callbacks.gro_receive) goto out_unlock; if (*(u8 *)iph != 0x45) @@ -1420,7 +1420,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head, skb_gro_pull(skb, sizeof(*iph)); skb_set_transport_header(skb, skb_gro_offset(skb)); - pp = ops->gro_receive(head, skb); + pp = ops->callbacks.gro_receive(head, skb); out_unlock: rcu_read_unlock(); @@ -1435,7 +1435,7 @@ static int inet_gro_complete(struct sk_buff *skb) { __be16 newlen = htons(skb->len - skb_network_offset(skb)); struct iphdr *iph = ip_hdr(skb); - const struct net_protocol *ops; + const struct net_offload *ops; int proto = iph->protocol; int err = -ENOSYS; @@ -1443,11 +1443,11 @@ static int inet_gro_complete(struct sk_buff *skb) iph->tot_len = newlen; rcu_read_lock(); - ops = rcu_dereference(inet_protos[proto]); - if (WARN_ON(!ops || !ops->gro_complete)) + ops = rcu_dereference(inet_offloads[proto]); + if (WARN_ON(!ops || !ops->callbacks.gro_complete)) goto out_unlock; - err = ops->gro_complete(skb); + err = ops->callbacks.gro_complete(skb); out_unlock: rcu_read_unlock(); @@ -1558,23 +1558,33 @@ static const struct net_protocol tcp_protocol = { .early_demux = tcp_v4_early_demux, .handler = tcp_v4_rcv, .err_handler = tcp_v4_err, - .gso_send_check = tcp_v4_gso_send_check, - .gso_segment = tcp_tso_segment, - .gro_receive = tcp4_gro_receive, - .gro_complete = tcp4_gro_complete, .no_policy = 1, .netns_ok = 1, }; +static const struct net_offload tcp_offload = { + .callbacks = { + .gso_send_check = tcp_v4_gso_send_check, + .gso_segment = tcp_tso_segment, + .gro_receive = tcp4_gro_receive, + .gro_complete = tcp4_gro_complete, + }, +}; + static const struct net_protocol udp_protocol = { .handler = udp_rcv, .err_handler = udp_err, - .gso_send_check = udp4_ufo_send_check, - .gso_segment = udp4_ufo_fragment, .no_policy = 1, .netns_ok = 1, }; +static const struct net_offload udp_offload = { + .callbacks = { + .gso_send_check = udp4_ufo_send_check, + .gso_segment = udp4_ufo_fragment, + }, +}; + static const struct net_protocol icmp_protocol = { .handler = icmp_rcv, .err_handler = ping_err, @@ -1659,13 +1669,35 @@ static int ipv4_proc_init(void); * IP protocol layer initialiser */ +static struct packet_offload ip_packet_offload __read_mostly = { + .type = cpu_to_be16(ETH_P_IP), + .callbacks = { + .gso_send_check = inet_gso_send_check, + .gso_segment = inet_gso_segment, + .gro_receive = inet_gro_receive, + .gro_complete = inet_gro_complete, + }, +}; + +static int __init ipv4_offload_init(void) +{ + /* + * Add offloads + */ + if (inet_add_offload(&udp_offload, IPPROTO_UDP) < 0) + pr_crit("%s: Cannot add UDP protocol offload\n", __func__); + if (inet_add_offload(&tcp_offload, IPPROTO_TCP) < 0) + pr_crit("%s: Cannot add TCP protocol offlaod\n", __func__); + + dev_add_offload(&ip_packet_offload); + return 0; +} + +fs_initcall(ipv4_offload_init); + static struct packet_type ip_packet_type __read_mostly = { .type = cpu_to_be16(ETH_P_IP), .func = ip_rcv, - .gso_send_check = inet_gso_send_check, - .gso_segment = inet_gso_segment, - .gro_receive = inet_gro_receive, - .gro_complete = inet_gro_complete, }; static int __init inet_init(void) diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 2a6abc163ed2..f6db227c1fd9 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -55,6 +55,7 @@ #include <linux/sysctl.h> #endif #include <linux/kmod.h> +#include <linux/netconf.h> #include <net/arp.h> #include <net/ip.h> @@ -1442,6 +1443,149 @@ static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) return 0; } +static int inet_netconf_msgsize_devconf(int type) +{ + int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) + + nla_total_size(4); /* NETCONFA_IFINDEX */ + + /* type -1 is used for ALL */ + if (type == -1 || type == NETCONFA_FORWARDING) + size += nla_total_size(4); + if (type == -1 || type == NETCONFA_RP_FILTER) + size += nla_total_size(4); + + return size; +} + +static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, + struct ipv4_devconf *devconf, u32 portid, + u32 seq, int event, unsigned int flags, + int type) +{ + struct nlmsghdr *nlh; + struct netconfmsg *ncm; + + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), + flags); + if (nlh == NULL) + return -EMSGSIZE; + + ncm = nlmsg_data(nlh); + ncm->ncm_family = AF_INET; + + if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) + goto nla_put_failure; + + /* type -1 is used for ALL */ + if ((type == -1 || type == NETCONFA_FORWARDING) && + nla_put_s32(skb, NETCONFA_FORWARDING, + IPV4_DEVCONF(*devconf, FORWARDING)) < 0) + goto nla_put_failure; + if ((type == -1 || type == NETCONFA_RP_FILTER) && + nla_put_s32(skb, NETCONFA_RP_FILTER, + IPV4_DEVCONF(*devconf, RP_FILTER)) < 0) + goto nla_put_failure; + + return nlmsg_end(skb, nlh); + +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; +} + +static void inet_netconf_notify_devconf(struct net *net, int type, int ifindex, + struct ipv4_devconf *devconf) +{ + struct sk_buff *skb; + int err = -ENOBUFS; + + skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC); + if (skb == NULL) + goto errout; + + err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, + RTM_NEWNETCONF, 0, type); + if (err < 0) { + /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ + WARN_ON(err == -EMSGSIZE); + kfree_skb(skb); + goto errout; + } + rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC); + return; +errout: + if (err < 0) + rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); +} + +static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { + [NETCONFA_IFINDEX] = { .len = sizeof(int) }, + [NETCONFA_FORWARDING] = { .len = sizeof(int) }, + [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, +}; + +static int inet_netconf_get_devconf(struct sk_buff *in_skb, + struct nlmsghdr *nlh, + void *arg) +{ + struct net *net = sock_net(in_skb->sk); + struct nlattr *tb[NETCONFA_MAX+1]; + struct netconfmsg *ncm; + struct sk_buff *skb; + struct ipv4_devconf *devconf; + struct in_device *in_dev; + struct net_device *dev; + int ifindex; + int err; + + err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, + devconf_ipv4_policy); + if (err < 0) + goto errout; + + err = EINVAL; + if (!tb[NETCONFA_IFINDEX]) + goto errout; + + ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); + switch (ifindex) { + case NETCONFA_IFINDEX_ALL: + devconf = net->ipv4.devconf_all; + break; + case NETCONFA_IFINDEX_DEFAULT: + devconf = net->ipv4.devconf_dflt; + break; + default: + dev = __dev_get_by_index(net, ifindex); + if (dev == NULL) + goto errout; + in_dev = __in_dev_get_rtnl(dev); + if (in_dev == NULL) + goto errout; + devconf = &in_dev->cnf; + break; + } + + err = -ENOBUFS; + skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC); + if (skb == NULL) + goto errout; + + err = inet_netconf_fill_devconf(skb, ifindex, devconf, + NETLINK_CB(in_skb).portid, + nlh->nlmsg_seq, RTM_NEWNETCONF, 0, + -1); + if (err < 0) { + /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ + WARN_ON(err == -EMSGSIZE); + kfree_skb(skb); + goto errout; + } + err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); +errout: + return err; +} + #ifdef CONFIG_SYSCTL static void devinet_copy_dflt_conf(struct net *net, int i) @@ -1467,6 +1611,12 @@ static void inet_forward_change(struct net *net) IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; IPV4_DEVCONF_DFLT(net, FORWARDING) = on; + inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, + NETCONFA_IFINDEX_ALL, + net->ipv4.devconf_all); + inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, + NETCONFA_IFINDEX_DEFAULT, + net->ipv4.devconf_dflt); for_each_netdev(net, dev) { struct in_device *in_dev; @@ -1474,8 +1624,11 @@ static void inet_forward_change(struct net *net) dev_disable_lro(dev); rcu_read_lock(); in_dev = __in_dev_get_rcu(dev); - if (in_dev) + if (in_dev) { IN_DEV_CONF_SET(in_dev, FORWARDING, on); + inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, + dev->ifindex, &in_dev->cnf); + } rcu_read_unlock(); } } @@ -1501,6 +1654,23 @@ static int devinet_conf_proc(ctl_table *ctl, int write, i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) if ((new_value == 0) && (old_value != 0)) rt_cache_flush(net); + if (i == IPV4_DEVCONF_RP_FILTER - 1 && + new_value != old_value) { + int ifindex; + + if (cnf == net->ipv4.devconf_dflt) + ifindex = NETCONFA_IFINDEX_DEFAULT; + else if (cnf == net->ipv4.devconf_all) + ifindex = NETCONFA_IFINDEX_ALL; + else { + struct in_device *idev = + container_of(cnf, struct in_device, + cnf); + ifindex = idev->dev->ifindex; + } + inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER, + ifindex, cnf); + } } return ret; @@ -1527,15 +1697,23 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write, } if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { inet_forward_change(net); - } else if (*valp) { + } else { struct ipv4_devconf *cnf = ctl->extra1; struct in_device *idev = container_of(cnf, struct in_device, cnf); - dev_disable_lro(idev->dev); + if (*valp) + dev_disable_lro(idev->dev); + inet_netconf_notify_devconf(net, + NETCONFA_FORWARDING, + idev->dev->ifindex, + cnf); } rtnl_unlock(); rt_cache_flush(net); - } + } else + inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, + NETCONFA_IFINDEX_DEFAULT, + net->ipv4.devconf_dflt); } return ret; @@ -1809,5 +1987,7 @@ void __init devinet_init(void) rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL); rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL); rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL); + rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, + NULL, NULL); } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index d34ce2972c8f..2026542d6836 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -521,21 +521,31 @@ static inline void syn_ack_recalc(struct request_sock *req, const int thresh, int *expire, int *resend) { if (!rskq_defer_accept) { - *expire = req->retrans >= thresh; + *expire = req->num_timeout >= thresh; *resend = 1; return; } - *expire = req->retrans >= thresh && - (!inet_rsk(req)->acked || req->retrans >= max_retries); + *expire = req->num_timeout >= thresh && + (!inet_rsk(req)->acked || req->num_timeout >= max_retries); /* * Do not resend while waiting for data after ACK, * start to resend on end of deferring period to give * last chance for data or ACK to create established socket. */ *resend = !inet_rsk(req)->acked || - req->retrans >= rskq_defer_accept - 1; + req->num_timeout >= rskq_defer_accept - 1; } +int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req) +{ + int err = req->rsk_ops->rtx_syn_ack(parent, req, NULL); + + if (!err) + req->num_retrans++; + return err; +} +EXPORT_SYMBOL(inet_rtx_syn_ack); + void inet_csk_reqsk_queue_prune(struct sock *parent, const unsigned long interval, const unsigned long timeout, @@ -599,13 +609,14 @@ void inet_csk_reqsk_queue_prune(struct sock *parent, req->rsk_ops->syn_ack_timeout(parent, req); if (!expire && (!resend || - !req->rsk_ops->rtx_syn_ack(parent, req, NULL) || + !inet_rtx_syn_ack(parent, req) || inet_rsk(req)->acked)) { unsigned long timeo; - if (req->retrans++ == 0) + if (req->num_timeout++ == 0) lopt->qlen_young--; - timeo = min((timeout << req->retrans), max_rto); + timeo = min(timeout << req->num_timeout, + max_rto); req->expires = now + timeo; reqp = &req->dl_next; continue; diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index e5bad82d3584..cb98cbed1973 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -620,7 +620,7 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, r->idiag_family = sk->sk_family; r->idiag_state = TCP_SYN_RECV; r->idiag_timer = 1; - r->idiag_retrans = req->retrans; + r->idiag_retrans = req->num_retrans; r->id.idiag_if = sk->sk_bound_dev_if; sock_diag_save_cookie(req, r->id.idiag_cookie); @@ -895,13 +895,16 @@ static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, struct inet_diag_req_v2 *r, struct nlattr *bc) { const struct inet_diag_handler *handler; + int err = 0; handler = inet_diag_lock_handler(r->sdiag_protocol); if (!IS_ERR(handler)) handler->dump(skb, cb, r, bc); + else + err = PTR_ERR(handler); inet_diag_unlock_handler(handler); - return skb->len; + return err ? : skb->len; } static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 7240f8e2dd45..127f2a1e67f5 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -164,21 +164,6 @@ struct ipgre_net { #define tunnels_r tunnels[2] #define tunnels_l tunnels[1] #define tunnels_wc tunnels[0] -/* - * Locking : hash tables are protected by RCU and RTNL - */ - -#define for_each_ip_tunnel_rcu(start) \ - for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) - -/* often modified stats are per cpu, other are shared (netdev->stats) */ -struct pcpu_tstats { - u64 rx_packets; - u64 rx_bytes; - u64 tx_packets; - u64 tx_bytes; - struct u64_stats_sync syncp; -}; static struct rtnl_link_stats64 *ipgre_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *tot) @@ -250,7 +235,7 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev, ARPHRD_ETHER : ARPHRD_IPGRE; int score, cand_score = 4; - for_each_ip_tunnel_rcu(ign->tunnels_r_l[h0 ^ h1]) { + for_each_ip_tunnel_rcu(t, ign->tunnels_r_l[h0 ^ h1]) { if (local != t->parms.iph.saddr || remote != t->parms.iph.daddr || !(t->dev->flags & IFF_UP)) @@ -277,7 +262,7 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev, } } - for_each_ip_tunnel_rcu(ign->tunnels_r[h0 ^ h1]) { + for_each_ip_tunnel_rcu(t, ign->tunnels_r[h0 ^ h1]) { if (remote != t->parms.iph.daddr || !(t->dev->flags & IFF_UP)) continue; @@ -303,7 +288,7 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev, } } - for_each_ip_tunnel_rcu(ign->tunnels_l[h1]) { + for_each_ip_tunnel_rcu(t, ign->tunnels_l[h1]) { if ((local != t->parms.iph.saddr && (local != t->parms.iph.daddr || !ipv4_is_multicast(local))) || @@ -331,7 +316,7 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev, } } - for_each_ip_tunnel_rcu(ign->tunnels_wc[h1]) { + for_each_ip_tunnel_rcu(t, ign->tunnels_wc[h1]) { if (t->parms.i_key != key || !(t->dev->flags & IFF_UP)) continue; @@ -753,7 +738,6 @@ drop: static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip_tunnel *tunnel = netdev_priv(dev); - struct pcpu_tstats *tstats; const struct iphdr *old_iph = ip_hdr(skb); const struct iphdr *tiph; struct flowi4 fl4; @@ -977,9 +961,7 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev } } - nf_reset(skb); - tstats = this_cpu_ptr(dev->tstats); - __IPTUNNEL_XMIT(tstats, &dev->stats); + iptunnel_xmit(skb, dev); return NETDEV_TX_OK; #if IS_ENABLED(CONFIG_IPV6) diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 1831092f999f..516188b0dc1e 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -66,20 +66,6 @@ static void vti_tunnel_setup(struct net_device *dev); static void vti_dev_free(struct net_device *dev); static int vti_tunnel_bind_dev(struct net_device *dev); -/* Locking : hash tables are protected by RCU and RTNL */ - -#define for_each_ip_tunnel_rcu(start) \ - for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) - -/* often modified stats are per cpu, other are shared (netdev->stats) */ -struct pcpu_tstats { - u64 rx_packets; - u64 rx_bytes; - u64 tx_packets; - u64 tx_bytes; - struct u64_stats_sync syncp; -}; - #define VTI_XMIT(stats1, stats2) do { \ int err; \ int pkt_len = skb->len; \ @@ -142,19 +128,19 @@ static struct ip_tunnel *vti_tunnel_lookup(struct net *net, struct ip_tunnel *t; struct vti_net *ipn = net_generic(net, vti_net_id); - for_each_ip_tunnel_rcu(ipn->tunnels_r_l[h0 ^ h1]) + for_each_ip_tunnel_rcu(t, ipn->tunnels_r_l[h0 ^ h1]) if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP)) return t; - for_each_ip_tunnel_rcu(ipn->tunnels_r[h0]) + for_each_ip_tunnel_rcu(t, ipn->tunnels_r[h0]) if (remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP)) return t; - for_each_ip_tunnel_rcu(ipn->tunnels_l[h1]) + for_each_ip_tunnel_rcu(t, ipn->tunnels_l[h1]) if (local == t->parms.iph.saddr && (t->dev->flags&IFF_UP)) return t; - for_each_ip_tunnel_rcu(ipn->tunnels_wc[0]) + for_each_ip_tunnel_rcu(t, ipn->tunnels_wc[0]) if (t && (t->dev->flags&IFF_UP)) return t; return NULL; diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index 798358b10717..d763701cff1b 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -1500,8 +1500,10 @@ static int __init ip_auto_config(void) * Clue in the operator. */ pr_info("IP-Config: Complete:\n"); - pr_info(" device=%s, addr=%pI4, mask=%pI4, gw=%pI4\n", - ic_dev->name, &ic_myaddr, &ic_netmask, &ic_gateway); + + pr_info(" device=%s, hwaddr=%*phC, ipaddr=%pI4, mask=%pI4, gw=%pI4\n", + ic_dev->name, ic_dev->addr_len, ic_dev->dev_addr, + &ic_myaddr, &ic_netmask, &ic_gateway); pr_info(" host=%s, domain=%s, nis-domain=%s\n", utsname()->nodename, ic_domain, utsname()->domainname); pr_info(" bootserver=%pI4, rootserver=%pI4, rootpath=%s", diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index e15b45297c09..c26c1717c1db 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -138,22 +138,7 @@ struct ipip_net { static int ipip_tunnel_init(struct net_device *dev); static void ipip_tunnel_setup(struct net_device *dev); static void ipip_dev_free(struct net_device *dev); - -/* - * Locking : hash tables are protected by RCU and RTNL - */ - -#define for_each_ip_tunnel_rcu(start) \ - for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) - -/* often modified stats are per cpu, other are shared (netdev->stats) */ -struct pcpu_tstats { - u64 rx_packets; - u64 rx_bytes; - u64 tx_packets; - u64 tx_bytes; - struct u64_stats_sync syncp; -}; +static struct rtnl_link_ops ipip_link_ops __read_mostly; static struct rtnl_link_stats64 *ipip_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *tot) @@ -197,16 +182,16 @@ static struct ip_tunnel *ipip_tunnel_lookup(struct net *net, struct ip_tunnel *t; struct ipip_net *ipn = net_generic(net, ipip_net_id); - for_each_ip_tunnel_rcu(ipn->tunnels_r_l[h0 ^ h1]) + for_each_ip_tunnel_rcu(t, ipn->tunnels_r_l[h0 ^ h1]) if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP)) return t; - for_each_ip_tunnel_rcu(ipn->tunnels_r[h0]) + for_each_ip_tunnel_rcu(t, ipn->tunnels_r[h0]) if (remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP)) return t; - for_each_ip_tunnel_rcu(ipn->tunnels_l[h1]) + for_each_ip_tunnel_rcu(t, ipn->tunnels_l[h1]) if (local == t->parms.iph.saddr && (t->dev->flags&IFF_UP)) return t; @@ -264,6 +249,32 @@ static void ipip_tunnel_link(struct ipip_net *ipn, struct ip_tunnel *t) rcu_assign_pointer(*tp, t); } +static int ipip_tunnel_create(struct net_device *dev) +{ + struct ip_tunnel *t = netdev_priv(dev); + struct net *net = dev_net(dev); + struct ipip_net *ipn = net_generic(net, ipip_net_id); + int err; + + err = ipip_tunnel_init(dev); + if (err < 0) + goto out; + + err = register_netdevice(dev); + if (err < 0) + goto out; + + strcpy(t->parms.name, dev->name); + dev->rtnl_link_ops = &ipip_link_ops; + + dev_hold(dev); + ipip_tunnel_link(ipn, t); + return 0; + +out: + return err; +} + static struct ip_tunnel *ipip_tunnel_locate(struct net *net, struct ip_tunnel_parm *parms, int create) { @@ -298,16 +309,9 @@ static struct ip_tunnel *ipip_tunnel_locate(struct net *net, nt = netdev_priv(dev); nt->parms = *parms; - if (ipip_tunnel_init(dev) < 0) + if (ipip_tunnel_create(dev) < 0) goto failed_free; - if (register_netdevice(dev) < 0) - goto failed_free; - - strcpy(nt->parms.name, dev->name); - - dev_hold(dev); - ipip_tunnel_link(ipn, nt); return nt; failed_free: @@ -463,7 +467,6 @@ drop: static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip_tunnel *tunnel = netdev_priv(dev); - struct pcpu_tstats *tstats; const struct iphdr *tiph = &tunnel->parms.iph; u8 tos = tunnel->parms.iph.tos; __be16 df = tiph->frag_off; @@ -479,6 +482,10 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) if (skb->protocol != htons(ETH_P_IP)) goto tx_error; + if (skb->ip_summed == CHECKSUM_PARTIAL && + skb_checksum_help(skb)) + goto tx_error; + if (tos & 1) tos = old_iph->tos; @@ -586,9 +593,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) if ((iph->ttl = tiph->ttl) == 0) iph->ttl = old_iph->ttl; - nf_reset(skb); - tstats = this_cpu_ptr(dev->tstats); - __IPTUNNEL_XMIT(tstats, &dev->stats); + iptunnel_xmit(skb, dev); return NETDEV_TX_OK; tx_error_icmp: @@ -635,6 +640,28 @@ static void ipip_tunnel_bind_dev(struct net_device *dev) dev->iflink = tunnel->parms.link; } +static void ipip_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p) +{ + struct net *net = dev_net(t->dev); + struct ipip_net *ipn = net_generic(net, ipip_net_id); + + ipip_tunnel_unlink(ipn, t); + synchronize_net(); + t->parms.iph.saddr = p->iph.saddr; + t->parms.iph.daddr = p->iph.daddr; + memcpy(t->dev->dev_addr, &p->iph.saddr, 4); + memcpy(t->dev->broadcast, &p->iph.daddr, 4); + ipip_tunnel_link(ipn, t); + t->parms.iph.ttl = p->iph.ttl; + t->parms.iph.tos = p->iph.tos; + t->parms.iph.frag_off = p->iph.frag_off; + if (t->parms.link != p->link) { + t->parms.link = p->link; + ipip_tunnel_bind_dev(t->dev); + } + netdev_state_change(t->dev); +} + static int ipip_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) { @@ -693,29 +720,13 @@ ipip_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) break; } t = netdev_priv(dev); - ipip_tunnel_unlink(ipn, t); - synchronize_net(); - t->parms.iph.saddr = p.iph.saddr; - t->parms.iph.daddr = p.iph.daddr; - memcpy(dev->dev_addr, &p.iph.saddr, 4); - memcpy(dev->broadcast, &p.iph.daddr, 4); - ipip_tunnel_link(ipn, t); - netdev_state_change(dev); } + + ipip_tunnel_update(t, &p); } if (t) { err = 0; - if (cmd == SIOCCHGTUNNEL) { - t->parms.iph.ttl = p.iph.ttl; - t->parms.iph.tos = p.iph.tos; - t->parms.iph.frag_off = p.iph.frag_off; - if (t->parms.link != p.link) { - t->parms.link = p.link; - ipip_tunnel_bind_dev(dev); - netdev_state_change(dev); - } - } if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p))) err = -EFAULT; } else @@ -773,6 +784,11 @@ static void ipip_dev_free(struct net_device *dev) free_netdev(dev); } +#define IPIP_FEATURES (NETIF_F_SG | \ + NETIF_F_FRAGLIST | \ + NETIF_F_HIGHDMA | \ + NETIF_F_HW_CSUM) + static void ipip_tunnel_setup(struct net_device *dev) { dev->netdev_ops = &ipip_netdev_ops; @@ -787,6 +803,9 @@ static void ipip_tunnel_setup(struct net_device *dev) dev->features |= NETIF_F_NETNS_LOCAL; dev->features |= NETIF_F_LLTX; dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; + + dev->features |= IPIP_FEATURES; + dev->hw_features |= IPIP_FEATURES; } static int ipip_tunnel_init(struct net_device *dev) @@ -829,6 +848,142 @@ static int __net_init ipip_fb_tunnel_init(struct net_device *dev) return 0; } +static void ipip_netlink_parms(struct nlattr *data[], + struct ip_tunnel_parm *parms) +{ + memset(parms, 0, sizeof(*parms)); + + parms->iph.version = 4; + parms->iph.protocol = IPPROTO_IPIP; + parms->iph.ihl = 5; + + if (!data) + return; + + if (data[IFLA_IPTUN_LINK]) + parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]); + + if (data[IFLA_IPTUN_LOCAL]) + parms->iph.saddr = nla_get_be32(data[IFLA_IPTUN_LOCAL]); + + if (data[IFLA_IPTUN_REMOTE]) + parms->iph.daddr = nla_get_be32(data[IFLA_IPTUN_REMOTE]); + + if (data[IFLA_IPTUN_TTL]) { + parms->iph.ttl = nla_get_u8(data[IFLA_IPTUN_TTL]); + if (parms->iph.ttl) + parms->iph.frag_off = htons(IP_DF); + } + + if (data[IFLA_IPTUN_TOS]) + parms->iph.tos = nla_get_u8(data[IFLA_IPTUN_TOS]); + + if (!data[IFLA_IPTUN_PMTUDISC] || nla_get_u8(data[IFLA_IPTUN_PMTUDISC])) + parms->iph.frag_off = htons(IP_DF); +} + +static int ipip_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct net *net = dev_net(dev); + struct ip_tunnel *nt; + + nt = netdev_priv(dev); + ipip_netlink_parms(data, &nt->parms); + + if (ipip_tunnel_locate(net, &nt->parms, 0)) + return -EEXIST; + + return ipip_tunnel_create(dev); +} + +static int ipip_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[]) +{ + struct ip_tunnel *t; + struct ip_tunnel_parm p; + struct net *net = dev_net(dev); + struct ipip_net *ipn = net_generic(net, ipip_net_id); + + if (dev == ipn->fb_tunnel_dev) + return -EINVAL; + + ipip_netlink_parms(data, &p); + + if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) || + (!(dev->flags & IFF_POINTOPOINT) && p.iph.daddr)) + return -EINVAL; + + t = ipip_tunnel_locate(net, &p, 0); + + if (t) { + if (t->dev != dev) + return -EEXIST; + } else + t = netdev_priv(dev); + + ipip_tunnel_update(t, &p); + return 0; +} + +static size_t ipip_get_size(const struct net_device *dev) +{ + return + /* IFLA_IPTUN_LINK */ + nla_total_size(4) + + /* IFLA_IPTUN_LOCAL */ + nla_total_size(4) + + /* IFLA_IPTUN_REMOTE */ + nla_total_size(4) + + /* IFLA_IPTUN_TTL */ + nla_total_size(1) + + /* IFLA_IPTUN_TOS */ + nla_total_size(1) + + /* IFLA_IPTUN_PMTUDISC */ + nla_total_size(1) + + 0; +} + +static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct ip_tunnel *tunnel = netdev_priv(dev); + struct ip_tunnel_parm *parm = &tunnel->parms; + + if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) || + nla_put_be32(skb, IFLA_IPTUN_LOCAL, parm->iph.saddr) || + nla_put_be32(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) || + nla_put_u8(skb, IFLA_IPTUN_TTL, parm->iph.ttl) || + nla_put_u8(skb, IFLA_IPTUN_TOS, parm->iph.tos) || + nla_put_u8(skb, IFLA_IPTUN_PMTUDISC, + !!(parm->iph.frag_off & htons(IP_DF)))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static const struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, + [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, + [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, + [IFLA_IPTUN_TOS] = { .type = NLA_U8 }, + [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 }, +}; + +static struct rtnl_link_ops ipip_link_ops __read_mostly = { + .kind = "ipip", + .maxtype = IFLA_IPTUN_MAX, + .policy = ipip_policy, + .priv_size = sizeof(struct ip_tunnel), + .setup = ipip_tunnel_setup, + .newlink = ipip_newlink, + .changelink = ipip_changelink, + .get_size = ipip_get_size, + .fill_info = ipip_fill_info, +}; + static struct xfrm_tunnel ipip_handler __read_mostly = { .handler = ipip_rcv, .err_handler = ipip_err, @@ -925,14 +1080,26 @@ static int __init ipip_init(void) return err; err = xfrm4_tunnel_register(&ipip_handler, AF_INET); if (err < 0) { - unregister_pernet_device(&ipip_net_ops); pr_info("%s: can't register tunnel\n", __func__); + goto xfrm_tunnel_failed; } + err = rtnl_link_register(&ipip_link_ops); + if (err < 0) + goto rtnl_link_failed; + +out: return err; + +rtnl_link_failed: + xfrm4_tunnel_deregister(&ipip_handler, AF_INET); +xfrm_tunnel_failed: + unregister_pernet_device(&ipip_net_ops); + goto out; } static void __exit ipip_fini(void) { + rtnl_link_unregister(&ipip_link_ops); if (xfrm4_tunnel_deregister(&ipip_handler, AF_INET)) pr_info("%s: can't deregister tunnel\n", __func__); diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c index 8d65b74b7bb9..ac635a7b4416 100644 --- a/net/ipv4/netfilter/iptable_nat.c +++ b/net/ipv4/netfilter/iptable_nat.c @@ -184,7 +184,8 @@ nf_nat_ipv4_out(unsigned int hooknum, if ((ct->tuplehash[dir].tuple.src.u3.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) || - (ct->tuplehash[dir].tuple.src.u.all != + (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP && + ct->tuplehash[dir].tuple.src.u.all != ct->tuplehash[!dir].tuple.dst.u.all)) if (nf_xfrm_me_harder(skb, AF_INET) < 0) ret = NF_DROP; @@ -221,6 +222,7 @@ nf_nat_ipv4_local_fn(unsigned int hooknum, } #ifdef CONFIG_XFRM else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && + ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP && ct->tuplehash[dir].tuple.dst.u.all != ct->tuplehash[!dir].tuple.src.u.all) if (nf_xfrm_me_harder(skb, AF_INET) < 0) diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c index 8918eff1426d..0f9d09f54bd9 100644 --- a/net/ipv4/protocol.c +++ b/net/ipv4/protocol.c @@ -29,6 +29,7 @@ #include <net/protocol.h> const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly; +const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS] __read_mostly; /* * Add a protocol handler to the hash tables @@ -41,6 +42,13 @@ int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol) } EXPORT_SYMBOL(inet_add_protocol); +int inet_add_offload(const struct net_offload *prot, unsigned char protocol) +{ + return !cmpxchg((const struct net_offload **)&inet_offloads[protocol], + NULL, prot) ? 0 : -1; +} +EXPORT_SYMBOL(inet_add_offload); + /* * Remove a protocol from the hash tables. */ @@ -57,3 +65,16 @@ int inet_del_protocol(const struct net_protocol *prot, unsigned char protocol) return ret; } EXPORT_SYMBOL(inet_del_protocol); + +int inet_del_offload(const struct net_offload *prot, unsigned char protocol) +{ + int ret; + + ret = (cmpxchg((const struct net_offload **)&inet_offloads[protocol], + prot, NULL) == prot) ? 0 : -1; + + synchronize_net(); + + return ret; +} +EXPORT_SYMBOL(inet_del_offload); diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index ba48e799b031..b236ef04914f 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -340,7 +340,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, } req->expires = 0UL; - req->retrans = 0; + req->num_retrans = 0; /* * We need to lookup the route here to get at the correct diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index eace049da052..733f48593ec3 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2765,6 +2765,8 @@ void tcp_get_info(const struct sock *sk, struct tcp_info *info) info->tcpi_options |= TCPI_OPT_ECN; if (tp->ecn_flags & TCP_ECN_SEEN) info->tcpi_options |= TCPI_OPT_ECN_SEEN; + if (tp->syn_data_acked) + info->tcpi_options |= TCPI_OPT_SYN_DATA; info->tcpi_rto = jiffies_to_usecs(icsk->icsk_rto); info->tcpi_ato = jiffies_to_usecs(icsk->icsk_ack.ato); diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c index 813b43a76fec..834857f3c871 100644 --- a/net/ipv4/tcp_illinois.c +++ b/net/ipv4/tcp_illinois.c @@ -313,11 +313,13 @@ static void tcp_illinois_info(struct sock *sk, u32 ext, .tcpv_rttcnt = ca->cnt_rtt, .tcpv_minrtt = ca->base_rtt, }; - u64 t = ca->sum_rtt; - do_div(t, ca->cnt_rtt); - info.tcpv_rtt = t; + if (info.tcpv_rttcnt > 0) { + u64 t = ca->sum_rtt; + do_div(t, info.tcpv_rttcnt); + info.tcpv_rtt = t; + } nla_put(skb, INET_DIAG_VEGASINFO, sizeof(info), &info); } } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 60cf836120a1..7839d51fb65b 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4553,6 +4553,9 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size) struct tcphdr *th; bool fragstolen; + if (size == 0) + return 0; + skb = alloc_skb(size + sizeof(*th), sk->sk_allocation); if (!skb) goto err; @@ -5653,6 +5656,7 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, tcp_rearm_rto(sk); return true; } + tp->syn_data_acked = tp->syn_data; return false; } @@ -5970,7 +5974,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, req = tp->fastopen_rsk; if (req != NULL) { - BUG_ON(sk->sk_state != TCP_SYN_RECV && + WARN_ON_ONCE(sk->sk_state != TCP_SYN_RECV && sk->sk_state != TCP_FIN_WAIT1); if (tcp_check_req(sk, skb, req, NULL, true) == NULL) @@ -5991,7 +5995,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, */ if (req) { tcp_synack_rtt_meas(sk, req); - tp->total_retrans = req->retrans; + tp->total_retrans = req->num_retrans; reqsk_fastopen_remove(sk, req, false); } else { @@ -6059,7 +6063,15 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, * ACK we have received, this would have acknowledged * our SYNACK so stop the SYNACK timer. */ - if (acceptable && req != NULL) { + if (req != NULL) { + /* Return RST if ack_seq is invalid. + * Note that RFC793 only says to generate a + * DUPACK for it but for TCP Fast Open it seems + * better to treat this case like TCP_SYN_RECV + * above. + */ + if (!acceptable) + return 1; /* We no longer need the request sock. */ reqsk_fastopen_remove(sk, req, false); tcp_rearm_rto(sk); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 694ea4c557f8..9dd5b34eb112 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -877,10 +877,13 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, } static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req, - struct request_values *rvp) + struct request_values *rvp) { - TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); - return tcp_v4_send_synack(sk, NULL, req, rvp, 0, false); + int res = tcp_v4_send_synack(sk, NULL, req, rvp, 0, false); + + if (!res) + TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); + return res; } /* @@ -1070,7 +1073,7 @@ int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family) } EXPORT_SYMBOL(tcp_md5_do_del); -void tcp_clear_md5_list(struct sock *sk) +static void tcp_clear_md5_list(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); struct tcp_md5sig_key *key; @@ -1386,7 +1389,8 @@ static int tcp_v4_conn_req_fastopen(struct sock *sk, struct sock *child; int err; - req->retrans = 0; + req->num_retrans = 0; + req->num_timeout = 0; req->sk = NULL; child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL); @@ -1461,6 +1465,7 @@ static int tcp_v4_conn_req_fastopen(struct sock *sk, skb_set_owner_r(skb, child); __skb_queue_tail(&child->sk_receive_queue, skb); tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; + tp->syn_data_acked = 1; } sk->sk_data_ready(sk, 0); bh_unlock_sock(child); @@ -1740,7 +1745,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, tcp_initialize_rcv_mss(newsk); tcp_synack_rtt_meas(newsk, req); - newtp->total_retrans = req->retrans; + newtp->total_retrans = req->num_retrans; #ifdef CONFIG_TCP_MD5SIG /* Copy over the MD5 key from the original socket */ @@ -2638,7 +2643,7 @@ static void get_openreq4(const struct sock *sk, const struct request_sock *req, 0, 0, /* could print option size, but that is af dependent. */ 1, /* timers active (only the expire timer) */ jiffies_delta_to_clock_t(delta), - req->retrans, + req->num_timeout, from_kuid_munged(seq_user_ns(f), uid), 0, /* non standard timer */ 0, /* open_requests have no inode */ diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 4c752a6e0bcd..53bc5847bfa8 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -864,7 +864,7 @@ static int parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, } a = info->attrs[TCP_METRICS_ATTR_ADDR_IPV6]; if (a) { - if (nla_len(a) != sizeof(sizeof(struct in6_addr))) + if (nla_len(a) != sizeof(struct in6_addr)) return -EINVAL; addr->family = AF_INET6; memcpy(addr->addr.a6, nla_data(a), sizeof(addr->addr.a6)); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 27536ba16c9d..f35f2dfb6401 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -510,6 +510,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->rx_opt.mss_clamp = req->mss; TCP_ECN_openreq_child(newtp, req); newtp->fastopen_rsk = NULL; + newtp->syn_data_acked = 0; TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_PASSIVEOPENS); } @@ -552,7 +553,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, * it can be estimated (approximately) * from another data. */ - tmp_opt.ts_recent_stamp = get_seconds() - ((TCP_TIMEOUT_INIT/HZ)<<req->retrans); + tmp_opt.ts_recent_stamp = get_seconds() - ((TCP_TIMEOUT_INIT/HZ)<<req->num_timeout); paws_reject = tcp_paws_reject(&tmp_opt, th->rst); } } @@ -581,7 +582,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, * Note that even if there is new data in the SYN packet * they will be thrown away too. */ - req->rsk_ops->rtx_syn_ack(sk, req, NULL); + inet_rtx_syn_ack(sk, req); return NULL; } @@ -695,7 +696,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, /* Got ACK for our SYNACK, so update baseline for SYNACK RTT sample. */ if (tmp_opt.saw_tstamp && tmp_opt.rcv_tsecr) tcp_rsk(req)->snt_synack = tmp_opt.rcv_tsecr; - else if (req->retrans) /* don't take RTT sample if retrans && ~TS */ + else if (req->num_retrans) /* don't take RTT sample if retrans && ~TS */ tcp_rsk(req)->snt_synack = 0; /* For Fast Open no more processing is needed (sk is the @@ -705,7 +706,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, return sk; /* While TCP_DEFER_ACCEPT is active, drop bare ACK. */ - if (req->retrans < inet_csk(sk)->icsk_accept_queue.rskq_defer_accept && + if (req->num_timeout < inet_csk(sk)->icsk_accept_queue.rskq_defer_accept && TCP_SKB_CB(skb)->end_seq == tcp_rsk(req)->rcv_isn + 1) { inet_rsk(req)->acked = 1; NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDEFERACCEPTDROP); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index fc04711e80c8..b78aac30c498 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -318,7 +318,7 @@ static void tcp_fastopen_synack_timer(struct sock *sk) req = tcp_sk(sk)->fastopen_rsk; req->rsk_ops->syn_ack_timeout(sk, req); - if (req->retrans >= max_retries) { + if (req->num_timeout >= max_retries) { tcp_write_err(sk); return; } @@ -327,10 +327,10 @@ static void tcp_fastopen_synack_timer(struct sock *sk) * regular retransmit because if the child socket has been accepted * it's not good to give up too easily. */ - req->rsk_ops->rtx_syn_ack(sk, req, NULL); - req->retrans++; + inet_rtx_syn_ack(sk, req); + req->num_timeout++; inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, - TCP_TIMEOUT_INIT << req->retrans, TCP_RTO_MAX); + TCP_TIMEOUT_INIT << req->num_timeout, TCP_RTO_MAX); } /* @@ -347,8 +347,8 @@ void tcp_retransmit_timer(struct sock *sk) return; } if (tp->fastopen_rsk) { - BUG_ON(sk->sk_state != TCP_SYN_RECV && - sk->sk_state != TCP_FIN_WAIT1); + WARN_ON_ONCE(sk->sk_state != TCP_SYN_RECV && + sk->sk_state != TCP_FIN_WAIT1); tcp_fastopen_synack_timer(sk); /* Before we receive ACK to our SYN-ACK don't retransmit * anything else (e.g., data or FIN segments). diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index b6d3f79151e2..04a475df98ad 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -7,9 +7,11 @@ obj-$(CONFIG_IPV6) += ipv6.o ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ addrlabel.o \ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ - raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ + raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o +ipv6-offload := ip6_offload.o tcpv6_offload.o udp_offload.o exthdrs_offload.o + ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o @@ -38,6 +40,7 @@ obj-$(CONFIG_IPV6_SIT) += sit.o obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o obj-$(CONFIG_IPV6_GRE) += ip6_gre.o -obj-y += addrconf_core.o exthdrs_core.o +obj-y += addrconf_core.o exthdrs_core.o output_core.o protocol.o +obj-y += $(ipv6-offload) obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 0424e4e27414..cb803b7bb0d8 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -81,6 +81,7 @@ #include <net/pkt_sched.h> #include <linux/if_tunnel.h> #include <linux/rtnetlink.h> +#include <linux/netconf.h> #ifdef CONFIG_IPV6_PRIVACY #include <linux/random.h> @@ -401,7 +402,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev) if (dev->flags & (IFF_NOARP | IFF_LOOPBACK)) ndev->cnf.accept_dad = -1; -#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE) +#if IS_ENABLED(CONFIG_IPV6_SIT) if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) { pr_info("%s: Disabled Multicast RS\n", dev->name); ndev->cnf.rtr_solicits = 0; @@ -460,6 +461,141 @@ static struct inet6_dev *ipv6_find_idev(struct net_device *dev) return idev; } +static int inet6_netconf_msgsize_devconf(int type) +{ + int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) + + nla_total_size(4); /* NETCONFA_IFINDEX */ + + /* type -1 is used for ALL */ + if (type == -1 || type == NETCONFA_FORWARDING) + size += nla_total_size(4); + + return size; +} + +static int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex, + struct ipv6_devconf *devconf, u32 portid, + u32 seq, int event, unsigned int flags, + int type) +{ + struct nlmsghdr *nlh; + struct netconfmsg *ncm; + + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), + flags); + if (nlh == NULL) + return -EMSGSIZE; + + ncm = nlmsg_data(nlh); + ncm->ncm_family = AF_INET6; + + if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) + goto nla_put_failure; + + /* type -1 is used for ALL */ + if ((type == -1 || type == NETCONFA_FORWARDING) && + nla_put_s32(skb, NETCONFA_FORWARDING, devconf->forwarding) < 0) + goto nla_put_failure; + + return nlmsg_end(skb, nlh); + +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; +} + +static void inet6_netconf_notify_devconf(struct net *net, int type, int ifindex, + struct ipv6_devconf *devconf) +{ + struct sk_buff *skb; + int err = -ENOBUFS; + + skb = nlmsg_new(inet6_netconf_msgsize_devconf(type), GFP_ATOMIC); + if (skb == NULL) + goto errout; + + err = inet6_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, + RTM_NEWNETCONF, 0, type); + if (err < 0) { + /* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */ + WARN_ON(err == -EMSGSIZE); + kfree_skb(skb); + goto errout; + } + rtnl_notify(skb, net, 0, RTNLGRP_IPV6_NETCONF, NULL, GFP_ATOMIC); + return; +errout: + if (err < 0) + rtnl_set_sk_err(net, RTNLGRP_IPV6_NETCONF, err); +} + +static const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = { + [NETCONFA_IFINDEX] = { .len = sizeof(int) }, + [NETCONFA_FORWARDING] = { .len = sizeof(int) }, +}; + +static int inet6_netconf_get_devconf(struct sk_buff *in_skb, + struct nlmsghdr *nlh, + void *arg) +{ + struct net *net = sock_net(in_skb->sk); + struct nlattr *tb[NETCONFA_MAX+1]; + struct netconfmsg *ncm; + struct sk_buff *skb; + struct ipv6_devconf *devconf; + struct inet6_dev *in6_dev; + struct net_device *dev; + int ifindex; + int err; + + err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, + devconf_ipv6_policy); + if (err < 0) + goto errout; + + err = EINVAL; + if (!tb[NETCONFA_IFINDEX]) + goto errout; + + ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); + switch (ifindex) { + case NETCONFA_IFINDEX_ALL: + devconf = net->ipv6.devconf_all; + break; + case NETCONFA_IFINDEX_DEFAULT: + devconf = net->ipv6.devconf_dflt; + break; + default: + dev = __dev_get_by_index(net, ifindex); + if (dev == NULL) + goto errout; + in6_dev = __in6_dev_get(dev); + if (in6_dev == NULL) + goto errout; + devconf = &in6_dev->cnf; + break; + } + + err = -ENOBUFS; + skb = nlmsg_new(inet6_netconf_msgsize_devconf(-1), GFP_ATOMIC); + if (skb == NULL) + goto errout; + + err = inet6_netconf_fill_devconf(skb, ifindex, devconf, + NETLINK_CB(in_skb).portid, + nlh->nlmsg_seq, RTM_NEWNETCONF, 0, + -1); + if (err < 0) { + /* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */ + WARN_ON(err == -EMSGSIZE); + kfree_skb(skb); + goto errout; + } + err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); +errout: + return err; +} + #ifdef CONFIG_SYSCTL static void dev_forward_change(struct inet6_dev *idev) { @@ -471,7 +607,7 @@ static void dev_forward_change(struct inet6_dev *idev) dev = idev->dev; if (idev->cnf.forwarding) dev_disable_lro(dev); - if (dev && (dev->flags & IFF_MULTICAST)) { + if (dev->flags & IFF_MULTICAST) { if (idev->cnf.forwarding) ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); else @@ -486,6 +622,8 @@ static void dev_forward_change(struct inet6_dev *idev) else addrconf_leave_anycast(ifa); } + inet6_netconf_notify_devconf(dev_net(dev), NETCONFA_FORWARDING, + dev->ifindex, &idev->cnf); } @@ -518,6 +656,10 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf) *p = newf; if (p == &net->ipv6.devconf_dflt->forwarding) { + if ((!newf) ^ (!old)) + inet6_netconf_notify_devconf(net, NETCONFA_FORWARDING, + NETCONFA_IFINDEX_DEFAULT, + net->ipv6.devconf_dflt); rtnl_unlock(); return 0; } @@ -525,6 +667,10 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf) if (p == &net->ipv6.devconf_all->forwarding) { net->ipv6.devconf_dflt->forwarding = newf; addrconf_forward_change(net, newf); + if ((!newf) ^ (!old)) + inet6_netconf_notify_devconf(net, NETCONFA_FORWARDING, + NETCONFA_IFINDEX_ALL, + net->ipv6.devconf_all); } else if ((!newf) ^ (!old)) dev_forward_change((struct inet6_dev *)table->extra1); rtnl_unlock(); @@ -553,7 +699,7 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) pr_warn("Freeing alive inet6 address %p\n", ifp); return; } - dst_release(&ifp->rt->dst); + ip6_rt_put(ifp->rt); kfree_rcu(ifp, rcu); } @@ -805,7 +951,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) rt6_set_expires(rt, expires); } } - dst_release(&rt->dst); + ip6_rt_put(rt); } /* clean up prefsrc entries */ @@ -1692,7 +1838,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev, This thing is done here expecting that the whole class of non-broadcast devices need not cloning. */ -#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE) +#if IS_ENABLED(CONFIG_IPV6_SIT) if (dev->type == ARPHRD_SIT && (dev->flags & IFF_POINTOPOINT)) cfg.fc_flags |= RTF_NONEXTHOP; #endif @@ -1752,7 +1898,7 @@ static void addrconf_add_mroute(struct net_device *dev) ip6_route_add(&cfg); } -#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE) +#if IS_ENABLED(CONFIG_IPV6_SIT) static void sit_route_add(struct net_device *dev) { struct fib6_config cfg = { @@ -1881,8 +2027,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len, dev, expires, flags); } - if (rt) - dst_release(&rt->dst); + ip6_rt_put(rt); } /* Try to figure out our local address for this prefix */ @@ -2104,7 +2249,7 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg) if (dev == NULL) goto err_exit; -#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE) +#if IS_ENABLED(CONFIG_IPV6_SIT) if (dev->type == ARPHRD_SIT) { const struct net_device_ops *ops = dev->netdev_ops; struct ifreq ifr; @@ -2315,7 +2460,7 @@ static void add_addr(struct inet6_dev *idev, const struct in6_addr *addr, } } -#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE) +#if IS_ENABLED(CONFIG_IPV6_SIT) static void sit_add_v4_addrs(struct inet6_dev *idev) { struct in6_addr addr; @@ -2434,7 +2579,7 @@ static void addrconf_dev_config(struct net_device *dev) addrconf_add_linklocal(idev, &addr); } -#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE) +#if IS_ENABLED(CONFIG_IPV6_SIT) static void addrconf_sit_config(struct net_device *dev) { struct inet6_dev *idev; @@ -2471,7 +2616,7 @@ static void addrconf_sit_config(struct net_device *dev) } #endif -#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) +#if IS_ENABLED(CONFIG_NET_IPGRE) static void addrconf_gre_config(struct net_device *dev) { struct inet6_dev *idev; @@ -2601,12 +2746,12 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, } switch (dev->type) { -#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE) +#if IS_ENABLED(CONFIG_IPV6_SIT) case ARPHRD_SIT: addrconf_sit_config(dev); break; #endif -#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) +#if IS_ENABLED(CONFIG_NET_IPGRE) case ARPHRD_IPGRE: addrconf_gre_config(dev); break; @@ -3194,7 +3339,7 @@ void if6_proc_exit(void) } #endif /* CONFIG_PROC_FS */ -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) /* Check if address is a home address configured on any interface. */ int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr) { @@ -3892,6 +4037,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6; array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad; array[DEVCONF_FORCE_TLLAO] = cnf->force_tllao; + array[DEVCONF_NDISC_NOTIFY] = cnf->ndisc_notify; } static inline size_t inet6_ifla6_size(void) @@ -4560,6 +4706,13 @@ static struct addrconf_sysctl_table .proc_handler = proc_dointvec }, { + .procname = "ndisc_notify", + .data = &ipv6_devconf.ndisc_notify, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { /* sentinel */ } }, @@ -4784,6 +4937,8 @@ int __init addrconf_init(void) inet6_dump_ifmcaddr, NULL); __rtnl_register(PF_INET6, RTM_GETANYCAST, NULL, inet6_dump_ifacaddr, NULL); + __rtnl_register(PF_INET6, RTM_GETNETCONF, inet6_netconf_get_devconf, + NULL, NULL); ipv6_addr_label_rtnl_register(); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index a974247a9ae4..7bafc51cda11 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -699,249 +699,9 @@ bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb) } EXPORT_SYMBOL_GPL(ipv6_opt_accepted); -static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto) -{ - const struct inet6_protocol *ops = NULL; - - for (;;) { - struct ipv6_opt_hdr *opth; - int len; - - if (proto != NEXTHDR_HOP) { - ops = rcu_dereference(inet6_protos[proto]); - - if (unlikely(!ops)) - break; - - if (!(ops->flags & INET6_PROTO_GSO_EXTHDR)) - break; - } - - if (unlikely(!pskb_may_pull(skb, 8))) - break; - - opth = (void *)skb->data; - len = ipv6_optlen(opth); - - if (unlikely(!pskb_may_pull(skb, len))) - break; - - proto = opth->nexthdr; - __skb_pull(skb, len); - } - - return proto; -} - -static int ipv6_gso_send_check(struct sk_buff *skb) -{ - const struct ipv6hdr *ipv6h; - const struct inet6_protocol *ops; - int err = -EINVAL; - - if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) - goto out; - - ipv6h = ipv6_hdr(skb); - __skb_pull(skb, sizeof(*ipv6h)); - err = -EPROTONOSUPPORT; - - rcu_read_lock(); - ops = rcu_dereference(inet6_protos[ - ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]); - - if (likely(ops && ops->gso_send_check)) { - skb_reset_transport_header(skb); - err = ops->gso_send_check(skb); - } - rcu_read_unlock(); - -out: - return err; -} - -static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, - netdev_features_t features) -{ - struct sk_buff *segs = ERR_PTR(-EINVAL); - struct ipv6hdr *ipv6h; - const struct inet6_protocol *ops; - int proto; - struct frag_hdr *fptr; - unsigned int unfrag_ip6hlen; - u8 *prevhdr; - int offset = 0; - - if (!(features & NETIF_F_V6_CSUM)) - features &= ~NETIF_F_SG; - - if (unlikely(skb_shinfo(skb)->gso_type & - ~(SKB_GSO_UDP | - SKB_GSO_DODGY | - SKB_GSO_TCP_ECN | - SKB_GSO_TCPV6 | - 0))) - goto out; - - if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) - goto out; - - ipv6h = ipv6_hdr(skb); - __skb_pull(skb, sizeof(*ipv6h)); - segs = ERR_PTR(-EPROTONOSUPPORT); - - proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); - rcu_read_lock(); - ops = rcu_dereference(inet6_protos[proto]); - if (likely(ops && ops->gso_segment)) { - skb_reset_transport_header(skb); - segs = ops->gso_segment(skb, features); - } - rcu_read_unlock(); - - if (IS_ERR(segs)) - goto out; - - for (skb = segs; skb; skb = skb->next) { - ipv6h = ipv6_hdr(skb); - ipv6h->payload_len = htons(skb->len - skb->mac_len - - sizeof(*ipv6h)); - if (proto == IPPROTO_UDP) { - unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); - fptr = (struct frag_hdr *)(skb_network_header(skb) + - unfrag_ip6hlen); - fptr->frag_off = htons(offset); - if (skb->next != NULL) - fptr->frag_off |= htons(IP6_MF); - offset += (ntohs(ipv6h->payload_len) - - sizeof(struct frag_hdr)); - } - } - -out: - return segs; -} - -static struct sk_buff **ipv6_gro_receive(struct sk_buff **head, - struct sk_buff *skb) -{ - const struct inet6_protocol *ops; - struct sk_buff **pp = NULL; - struct sk_buff *p; - struct ipv6hdr *iph; - unsigned int nlen; - unsigned int hlen; - unsigned int off; - int flush = 1; - int proto; - __wsum csum; - - off = skb_gro_offset(skb); - hlen = off + sizeof(*iph); - iph = skb_gro_header_fast(skb, off); - if (skb_gro_header_hard(skb, hlen)) { - iph = skb_gro_header_slow(skb, hlen, off); - if (unlikely(!iph)) - goto out; - } - - skb_gro_pull(skb, sizeof(*iph)); - skb_set_transport_header(skb, skb_gro_offset(skb)); - - flush += ntohs(iph->payload_len) != skb_gro_len(skb); - - rcu_read_lock(); - proto = iph->nexthdr; - ops = rcu_dereference(inet6_protos[proto]); - if (!ops || !ops->gro_receive) { - __pskb_pull(skb, skb_gro_offset(skb)); - proto = ipv6_gso_pull_exthdrs(skb, proto); - skb_gro_pull(skb, -skb_transport_offset(skb)); - skb_reset_transport_header(skb); - __skb_push(skb, skb_gro_offset(skb)); - - ops = rcu_dereference(inet6_protos[proto]); - if (!ops || !ops->gro_receive) - goto out_unlock; - - iph = ipv6_hdr(skb); - } - - NAPI_GRO_CB(skb)->proto = proto; - - flush--; - nlen = skb_network_header_len(skb); - - for (p = *head; p; p = p->next) { - const struct ipv6hdr *iph2; - __be32 first_word; /* <Version:4><Traffic_Class:8><Flow_Label:20> */ - - if (!NAPI_GRO_CB(p)->same_flow) - continue; - - iph2 = ipv6_hdr(p); - first_word = *(__be32 *)iph ^ *(__be32 *)iph2 ; - - /* All fields must match except length and Traffic Class. */ - if (nlen != skb_network_header_len(p) || - (first_word & htonl(0xF00FFFFF)) || - memcmp(&iph->nexthdr, &iph2->nexthdr, - nlen - offsetof(struct ipv6hdr, nexthdr))) { - NAPI_GRO_CB(p)->same_flow = 0; - continue; - } - /* flush if Traffic Class fields are different */ - NAPI_GRO_CB(p)->flush |= !!(first_word & htonl(0x0FF00000)); - NAPI_GRO_CB(p)->flush |= flush; - } - - NAPI_GRO_CB(skb)->flush |= flush; - - csum = skb->csum; - skb_postpull_rcsum(skb, iph, skb_network_header_len(skb)); - - pp = ops->gro_receive(head, skb); - - skb->csum = csum; - -out_unlock: - rcu_read_unlock(); - -out: - NAPI_GRO_CB(skb)->flush |= flush; - - return pp; -} - -static int ipv6_gro_complete(struct sk_buff *skb) -{ - const struct inet6_protocol *ops; - struct ipv6hdr *iph = ipv6_hdr(skb); - int err = -ENOSYS; - - iph->payload_len = htons(skb->len - skb_network_offset(skb) - - sizeof(*iph)); - - rcu_read_lock(); - ops = rcu_dereference(inet6_protos[NAPI_GRO_CB(skb)->proto]); - if (WARN_ON(!ops || !ops->gro_complete)) - goto out_unlock; - - err = ops->gro_complete(skb); - -out_unlock: - rcu_read_unlock(); - - return err; -} - static struct packet_type ipv6_packet_type __read_mostly = { .type = cpu_to_be16(ETH_P_IPV6), .func = ipv6_rcv, - .gso_send_check = ipv6_gso_send_check, - .gso_segment = ipv6_gso_segment, - .gro_receive = ipv6_gro_receive, - .gro_complete = ipv6_gro_complete, }; static int __init ipv6_packet_init(void) diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 7e6139508ee7..ecc35b93314b 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -44,7 +44,7 @@ #define IPV6HDR_BASELEN 8 struct tmp_ext { -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) struct in6_addr saddr; #endif struct in6_addr daddr; @@ -152,7 +152,7 @@ bad: return false; } -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) /** * ipv6_rearrange_destopt - rearrange IPv6 destination options header * @iph: IPv6 header @@ -320,7 +320,7 @@ static void ah6_output_done(struct crypto_async_request *base, int err) memcpy(top_iph, iph_base, IPV6HDR_BASELEN); if (extlen) { -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) memcpy(&top_iph->saddr, iph_ext, extlen); #else memcpy(&top_iph->daddr, iph_ext, extlen); @@ -385,7 +385,7 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) memcpy(iph_base, top_iph, IPV6HDR_BASELEN); if (extlen) { -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) memcpy(iph_ext, &top_iph->saddr, extlen); #else memcpy(iph_ext, &top_iph->daddr, extlen); @@ -434,7 +434,7 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) memcpy(top_iph, iph_base, IPV6HDR_BASELEN); if (extlen) { -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) memcpy(&top_iph->saddr, iph_ext, extlen); #else memcpy(&top_iph->daddr, iph_ext, extlen); diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index cdf02be5f191..4963c769a13f 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -84,7 +84,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr) rt = rt6_lookup(net, addr, NULL, 0, 0); if (rt) { dev = rt->dst.dev; - dst_release(&rt->dst); + ip6_rt_put(rt); } else if (ishost) { err = -EADDRNOTAVAIL; goto error; diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index be2b67d631e5..93cbad2c0aa7 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -769,7 +769,7 @@ int datagram_send_ctl(struct net *net, struct sock *sk, rthdr = (struct ipv6_rt_hdr *)CMSG_DATA(cmsg); switch (rthdr->type) { -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) case IPV6_SRCRT_TYPE_2: if (rthdr->hdrlen != 2 || rthdr->segments_left != 1) { diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index fa3d9c328092..473f628f9f20 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -43,56 +43,12 @@ #include <net/ndisc.h> #include <net/ip6_route.h> #include <net/addrconf.h> -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) #include <net/xfrm.h> #endif #include <asm/uaccess.h> -int ipv6_find_tlv(struct sk_buff *skb, int offset, int type) -{ - const unsigned char *nh = skb_network_header(skb); - int packet_len = skb->tail - skb->network_header; - struct ipv6_opt_hdr *hdr; - int len; - - if (offset + 2 > packet_len) - goto bad; - hdr = (struct ipv6_opt_hdr *)(nh + offset); - len = ((hdr->hdrlen + 1) << 3); - - if (offset + len > packet_len) - goto bad; - - offset += 2; - len -= 2; - - while (len > 0) { - int opttype = nh[offset]; - int optlen; - - if (opttype == type) - return offset; - - switch (opttype) { - case IPV6_TLV_PAD1: - optlen = 1; - break; - default: - optlen = nh[offset + 1] + 2; - if (optlen > len) - goto bad; - break; - } - offset += optlen; - len -= optlen; - } - /* not_found */ - bad: - return -1; -} -EXPORT_SYMBOL_GPL(ipv6_find_tlv); - /* * Parsing tlv encoded headers. * @@ -224,7 +180,7 @@ bad: Destination options header. *****************************/ -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) static bool ipv6_dest_hao(struct sk_buff *skb, int optoff) { struct ipv6_destopt_hao *hao; @@ -288,7 +244,7 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff) #endif static const struct tlvtype_proc tlvprocdestopt_lst[] = { -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) { .type = IPV6_TLV_HAO, .func = ipv6_dest_hao, @@ -300,7 +256,7 @@ static const struct tlvtype_proc tlvprocdestopt_lst[] = { static int ipv6_destopt_rcv(struct sk_buff *skb) { struct inet6_skb_parm *opt = IP6CB(skb); -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) __u16 dstbuf; #endif struct dst_entry *dst = skb_dst(skb); @@ -315,14 +271,14 @@ static int ipv6_destopt_rcv(struct sk_buff *skb) } opt->lastopt = opt->dst1 = skb_network_header_len(skb); -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) dstbuf = opt->dst1; #endif if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) { skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3; opt = IP6CB(skb); -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) opt->nhoff = dstbuf; #else opt->nhoff = opt->dst1; @@ -378,7 +334,7 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb) looped_back: if (hdr->segments_left == 0) { switch (hdr->type) { -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) case IPV6_SRCRT_TYPE_2: /* Silently discard type 2 header unless it was * processed by own @@ -404,7 +360,7 @@ looped_back: } switch (hdr->type) { -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) case IPV6_SRCRT_TYPE_2: if (accept_source_route < 0) goto unknown_rh; @@ -461,7 +417,7 @@ looped_back: addr += i - 1; switch (hdr->type) { -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) case IPV6_SRCRT_TYPE_2: if (xfrm6_input_addr(skb, (xfrm_address_t *)addr, (xfrm_address_t *)&ipv6_hdr(skb)->saddr, @@ -528,12 +484,12 @@ unknown_rh: static const struct inet6_protocol rthdr_protocol = { .handler = ipv6_rthdr_rcv, - .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR, + .flags = INET6_PROTO_NOPOLICY, }; static const struct inet6_protocol destopt_protocol = { .handler = ipv6_destopt_rcv, - .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR, + .flags = INET6_PROTO_NOPOLICY, }; static const struct inet6_protocol nodata_protocol = { @@ -559,10 +515,10 @@ int __init ipv6_exthdrs_init(void) out: return ret; -out_rthdr: - inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING); out_destopt: inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS); +out_rthdr: + inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING); goto out; }; diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index f73d59a14131..e7d756e19d1d 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c @@ -111,3 +111,47 @@ int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp, return start; } EXPORT_SYMBOL(ipv6_skip_exthdr); + +int ipv6_find_tlv(struct sk_buff *skb, int offset, int type) +{ + const unsigned char *nh = skb_network_header(skb); + int packet_len = skb->tail - skb->network_header; + struct ipv6_opt_hdr *hdr; + int len; + + if (offset + 2 > packet_len) + goto bad; + hdr = (struct ipv6_opt_hdr *)(nh + offset); + len = ((hdr->hdrlen + 1) << 3); + + if (offset + len > packet_len) + goto bad; + + offset += 2; + len -= 2; + + while (len > 0) { + int opttype = nh[offset]; + int optlen; + + if (opttype == type) + return offset; + + switch (opttype) { + case IPV6_TLV_PAD1: + optlen = 1; + break; + default: + optlen = nh[offset + 1] + 2; + if (optlen > len) + goto bad; + break; + } + offset += optlen; + len -= optlen; + } + /* not_found */ + bad: + return -1; +} +EXPORT_SYMBOL_GPL(ipv6_find_tlv); diff --git a/net/ipv6/exthdrs_offload.c b/net/ipv6/exthdrs_offload.c new file mode 100644 index 000000000000..cf77f3abfd06 --- /dev/null +++ b/net/ipv6/exthdrs_offload.c @@ -0,0 +1,41 @@ +/* + * IPV6 GSO/GRO offload support + * Linux INET6 implementation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * IPV6 Extension Header GSO/GRO support + */ +#include <net/protocol.h> +#include "ip6_offload.h" + +static const struct net_offload rthdr_offload = { + .flags = INET6_PROTO_GSO_EXTHDR, +}; + +static const struct net_offload dstopt_offload = { + .flags = INET6_PROTO_GSO_EXTHDR, +}; + +int __init ipv6_exthdrs_offload_init(void) +{ + int ret; + + ret = inet6_add_offload(&rthdr_offload, IPPROTO_ROUTING); + if (!ret) + goto out; + + ret = inet6_add_offload(&dstopt_offload, IPPROTO_DSTOPTS); + if (!ret) + goto out_rt; + +out: + return ret; + +out_rt: + inet_del_offload(&rthdr_offload, IPPROTO_ROUTING); + goto out; +} diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index d9fb9110f607..2e1a432867c0 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -100,7 +100,7 @@ static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp, goto out; } again: - dst_release(&rt->dst); + ip6_rt_put(rt); rt = NULL; goto out; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 24d69dbca4d6..b4a9fd51dae7 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -280,7 +280,7 @@ static int icmpv6_getfrag(void *from, char *to, int offset, int len, int odd, st return 0; } -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) static void mip6_addr_swap(struct sk_buff *skb) { struct ipv6hdr *iph = ipv6_hdr(skb); diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 0185679c5f53..823fd64d0136 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -109,21 +109,6 @@ static u32 HASH_ADDR(const struct in6_addr *addr) #define tunnels_r tunnels[2] #define tunnels_l tunnels[1] #define tunnels_wc tunnels[0] -/* - * Locking : hash tables are protected by RCU and RTNL - */ - -#define for_each_ip_tunnel_rcu(start) \ - for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) - -/* often modified stats are per cpu, other are shared (netdev->stats) */ -struct pcpu_tstats { - u64 rx_packets; - u64 rx_bytes; - u64 tx_packets; - u64 tx_bytes; - struct u64_stats_sync syncp; -}; static struct rtnl_link_stats64 *ip6gre_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *tot) @@ -181,7 +166,7 @@ static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev, ARPHRD_ETHER : ARPHRD_IP6GRE; int score, cand_score = 4; - for_each_ip_tunnel_rcu(ign->tunnels_r_l[h0 ^ h1]) { + for_each_ip_tunnel_rcu(t, ign->tunnels_r_l[h0 ^ h1]) { if (!ipv6_addr_equal(local, &t->parms.laddr) || !ipv6_addr_equal(remote, &t->parms.raddr) || key != t->parms.i_key || @@ -206,7 +191,7 @@ static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev, } } - for_each_ip_tunnel_rcu(ign->tunnels_r[h0 ^ h1]) { + for_each_ip_tunnel_rcu(t, ign->tunnels_r[h0 ^ h1]) { if (!ipv6_addr_equal(remote, &t->parms.raddr) || key != t->parms.i_key || !(t->dev->flags & IFF_UP)) @@ -230,7 +215,7 @@ static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev, } } - for_each_ip_tunnel_rcu(ign->tunnels_l[h1]) { + for_each_ip_tunnel_rcu(t, ign->tunnels_l[h1]) { if ((!ipv6_addr_equal(local, &t->parms.laddr) && (!ipv6_addr_equal(local, &t->parms.raddr) || !ipv6_addr_is_multicast(local))) || @@ -256,7 +241,7 @@ static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev, } } - for_each_ip_tunnel_rcu(ign->tunnels_wc[h1]) { + for_each_ip_tunnel_rcu(t, ign->tunnels_wc[h1]) { if (t->parms.i_key != key || !(t->dev->flags & IFF_UP)) continue; @@ -1069,7 +1054,7 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu) dev->mtu = IPV6_MIN_MTU; } } - dst_release(&rt->dst); + ip6_rt_put(rt); } t->hlen = addend; @@ -1633,9 +1618,9 @@ static size_t ip6gre_get_size(const struct net_device *dev) /* IFLA_GRE_OKEY */ nla_total_size(4) + /* IFLA_GRE_LOCAL */ - nla_total_size(4) + + nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GRE_REMOTE */ - nla_total_size(4) + + nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GRE_TTL */ nla_total_size(1) + /* IFLA_GRE_TOS */ @@ -1659,8 +1644,8 @@ static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_be16(skb, IFLA_GRE_OFLAGS, p->o_flags) || nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) || nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) || - nla_put(skb, IFLA_GRE_LOCAL, sizeof(struct in6_addr), &p->raddr) || - nla_put(skb, IFLA_GRE_REMOTE, sizeof(struct in6_addr), &p->laddr) || + nla_put(skb, IFLA_GRE_LOCAL, sizeof(struct in6_addr), &p->laddr) || + nla_put(skb, IFLA_GRE_REMOTE, sizeof(struct in6_addr), &p->raddr) || nla_put_u8(skb, IFLA_GRE_TTL, p->hop_limit) || /*nla_put_u8(skb, IFLA_GRE_TOS, t->priority) ||*/ nla_put_u8(skb, IFLA_GRE_ENCAP_LIMIT, p->encap_limit) || diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c new file mode 100644 index 000000000000..f26f0da7f095 --- /dev/null +++ b/net/ipv6/ip6_offload.c @@ -0,0 +1,282 @@ +/* + * IPV6 GSO/GRO offload support + * Linux INET6 implementation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/socket.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/printk.h> + +#include <net/protocol.h> +#include <net/ipv6.h> + +#include "ip6_offload.h" + +static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto) +{ + const struct net_offload *ops = NULL; + + for (;;) { + struct ipv6_opt_hdr *opth; + int len; + + if (proto != NEXTHDR_HOP) { + ops = rcu_dereference(inet6_offloads[proto]); + + if (unlikely(!ops)) + break; + + if (!(ops->flags & INET6_PROTO_GSO_EXTHDR)) + break; + } + + if (unlikely(!pskb_may_pull(skb, 8))) + break; + + opth = (void *)skb->data; + len = ipv6_optlen(opth); + + if (unlikely(!pskb_may_pull(skb, len))) + break; + + proto = opth->nexthdr; + __skb_pull(skb, len); + } + + return proto; +} + +static int ipv6_gso_send_check(struct sk_buff *skb) +{ + const struct ipv6hdr *ipv6h; + const struct net_offload *ops; + int err = -EINVAL; + + if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) + goto out; + + ipv6h = ipv6_hdr(skb); + __skb_pull(skb, sizeof(*ipv6h)); + err = -EPROTONOSUPPORT; + + rcu_read_lock(); + ops = rcu_dereference(inet6_offloads[ + ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]); + + if (likely(ops && ops->callbacks.gso_send_check)) { + skb_reset_transport_header(skb); + err = ops->callbacks.gso_send_check(skb); + } + rcu_read_unlock(); + +out: + return err; +} + +static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, + netdev_features_t features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + struct ipv6hdr *ipv6h; + const struct net_offload *ops; + int proto; + struct frag_hdr *fptr; + unsigned int unfrag_ip6hlen; + u8 *prevhdr; + int offset = 0; + + if (!(features & NETIF_F_V6_CSUM)) + features &= ~NETIF_F_SG; + + if (unlikely(skb_shinfo(skb)->gso_type & + ~(SKB_GSO_UDP | + SKB_GSO_DODGY | + SKB_GSO_TCP_ECN | + SKB_GSO_TCPV6 | + 0))) + goto out; + + if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) + goto out; + + ipv6h = ipv6_hdr(skb); + __skb_pull(skb, sizeof(*ipv6h)); + segs = ERR_PTR(-EPROTONOSUPPORT); + + proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); + rcu_read_lock(); + ops = rcu_dereference(inet6_offloads[proto]); + if (likely(ops && ops->callbacks.gso_segment)) { + skb_reset_transport_header(skb); + segs = ops->callbacks.gso_segment(skb, features); + } + rcu_read_unlock(); + + if (IS_ERR(segs)) + goto out; + + for (skb = segs; skb; skb = skb->next) { + ipv6h = ipv6_hdr(skb); + ipv6h->payload_len = htons(skb->len - skb->mac_len - + sizeof(*ipv6h)); + if (proto == IPPROTO_UDP) { + unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); + fptr = (struct frag_hdr *)(skb_network_header(skb) + + unfrag_ip6hlen); + fptr->frag_off = htons(offset); + if (skb->next != NULL) + fptr->frag_off |= htons(IP6_MF); + offset += (ntohs(ipv6h->payload_len) - + sizeof(struct frag_hdr)); + } + } + +out: + return segs; +} + +static struct sk_buff **ipv6_gro_receive(struct sk_buff **head, + struct sk_buff *skb) +{ + const struct net_offload *ops; + struct sk_buff **pp = NULL; + struct sk_buff *p; + struct ipv6hdr *iph; + unsigned int nlen; + unsigned int hlen; + unsigned int off; + int flush = 1; + int proto; + __wsum csum; + + off = skb_gro_offset(skb); + hlen = off + sizeof(*iph); + iph = skb_gro_header_fast(skb, off); + if (skb_gro_header_hard(skb, hlen)) { + iph = skb_gro_header_slow(skb, hlen, off); + if (unlikely(!iph)) + goto out; + } + + skb_gro_pull(skb, sizeof(*iph)); + skb_set_transport_header(skb, skb_gro_offset(skb)); + + flush += ntohs(iph->payload_len) != skb_gro_len(skb); + + rcu_read_lock(); + proto = iph->nexthdr; + ops = rcu_dereference(inet6_offloads[proto]); + if (!ops || !ops->callbacks.gro_receive) { + __pskb_pull(skb, skb_gro_offset(skb)); + proto = ipv6_gso_pull_exthdrs(skb, proto); + skb_gro_pull(skb, -skb_transport_offset(skb)); + skb_reset_transport_header(skb); + __skb_push(skb, skb_gro_offset(skb)); + + ops = rcu_dereference(inet6_offloads[proto]); + if (!ops || !ops->callbacks.gro_receive) + goto out_unlock; + + iph = ipv6_hdr(skb); + } + + NAPI_GRO_CB(skb)->proto = proto; + + flush--; + nlen = skb_network_header_len(skb); + + for (p = *head; p; p = p->next) { + const struct ipv6hdr *iph2; + __be32 first_word; /* <Version:4><Traffic_Class:8><Flow_Label:20> */ + + if (!NAPI_GRO_CB(p)->same_flow) + continue; + + iph2 = ipv6_hdr(p); + first_word = *(__be32 *)iph ^ *(__be32 *)iph2 ; + + /* All fields must match except length and Traffic Class. */ + if (nlen != skb_network_header_len(p) || + (first_word & htonl(0xF00FFFFF)) || + memcmp(&iph->nexthdr, &iph2->nexthdr, + nlen - offsetof(struct ipv6hdr, nexthdr))) { + NAPI_GRO_CB(p)->same_flow = 0; + continue; + } + /* flush if Traffic Class fields are different */ + NAPI_GRO_CB(p)->flush |= !!(first_word & htonl(0x0FF00000)); + NAPI_GRO_CB(p)->flush |= flush; + } + + NAPI_GRO_CB(skb)->flush |= flush; + + csum = skb->csum; + skb_postpull_rcsum(skb, iph, skb_network_header_len(skb)); + + pp = ops->callbacks.gro_receive(head, skb); + + skb->csum = csum; + +out_unlock: + rcu_read_unlock(); + +out: + NAPI_GRO_CB(skb)->flush |= flush; + + return pp; +} + +static int ipv6_gro_complete(struct sk_buff *skb) +{ + const struct net_offload *ops; + struct ipv6hdr *iph = ipv6_hdr(skb); + int err = -ENOSYS; + + iph->payload_len = htons(skb->len - skb_network_offset(skb) - + sizeof(*iph)); + + rcu_read_lock(); + ops = rcu_dereference(inet6_offloads[NAPI_GRO_CB(skb)->proto]); + if (WARN_ON(!ops || !ops->callbacks.gro_complete)) + goto out_unlock; + + err = ops->callbacks.gro_complete(skb); + +out_unlock: + rcu_read_unlock(); + + return err; +} + +static struct packet_offload ipv6_packet_offload __read_mostly = { + .type = cpu_to_be16(ETH_P_IPV6), + .callbacks = { + .gso_send_check = ipv6_gso_send_check, + .gso_segment = ipv6_gso_segment, + .gro_receive = ipv6_gro_receive, + .gro_complete = ipv6_gro_complete, + }, +}; + +static int __init ipv6_offload_init(void) +{ + + if (tcpv6_offload_init() < 0) + pr_crit("%s: Cannot add TCP protocol offload\n", __func__); + if (udp_offload_init() < 0) + pr_crit("%s: Cannot add UDP protocol offload\n", __func__); + if (ipv6_exthdrs_offload_init() < 0) + pr_crit("%s: Cannot add EXTHDRS protocol offload\n", __func__); + + dev_add_offload(&ipv6_packet_offload); + return 0; +} + +fs_initcall(ipv6_offload_init); diff --git a/net/ipv6/ip6_offload.h b/net/ipv6/ip6_offload.h new file mode 100644 index 000000000000..2e155c651b35 --- /dev/null +++ b/net/ipv6/ip6_offload.h @@ -0,0 +1,18 @@ +/* + * IPV6 GSO/GRO offload support + * Linux INET6 implementation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __ip6_offload_h +#define __ip6_offload_h + +int ipv6_exthdrs_offload_init(void); +int udp_offload_init(void); +int tcpv6_offload_init(void); + +#endif diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index aece3e792f84..5552d13ae92f 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -538,78 +538,12 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from) to->tc_index = from->tc_index; #endif nf_copy(to, from); -#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \ - defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE) +#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) to->nf_trace = from->nf_trace; #endif skb_copy_secmark(to, from); } -int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) -{ - u16 offset = sizeof(struct ipv6hdr); - struct ipv6_opt_hdr *exthdr = - (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1); - unsigned int packet_len = skb->tail - skb->network_header; - int found_rhdr = 0; - *nexthdr = &ipv6_hdr(skb)->nexthdr; - - while (offset + 1 <= packet_len) { - - switch (**nexthdr) { - - case NEXTHDR_HOP: - break; - case NEXTHDR_ROUTING: - found_rhdr = 1; - break; - case NEXTHDR_DEST: -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) - if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) - break; -#endif - if (found_rhdr) - return offset; - break; - default : - return offset; - } - - offset += ipv6_optlen(exthdr); - *nexthdr = &exthdr->nexthdr; - exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) + - offset); - } - - return offset; -} - -void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt) -{ - static atomic_t ipv6_fragmentation_id; - int old, new; - - if (rt && !(rt->dst.flags & DST_NOPEER)) { - struct inet_peer *peer; - struct net *net; - - net = dev_net(rt->dst.dev); - peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1); - if (peer) { - fhdr->identification = htonl(inet_getid(peer, 0)); - inet_putpeer(peer); - return; - } - } - do { - old = atomic_read(&ipv6_fragmentation_id); - new = old + 1; - if (!new) - new = 1; - } while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old); - fhdr->identification = htonl(new); -} - int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) { struct sk_buff *frag; @@ -756,7 +690,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) if (err == 0) { IP6_INC_STATS(net, ip6_dst_idev(&rt->dst), IPSTATS_MIB_FRAGOKS); - dst_release(&rt->dst); + ip6_rt_put(rt); return 0; } @@ -768,7 +702,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) IP6_INC_STATS(net, ip6_dst_idev(&rt->dst), IPSTATS_MIB_FRAGFAILS); - dst_release(&rt->dst); + ip6_rt_put(rt); return err; slow_path_clean: diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index cb7e2ded6f08..bf3a549267d3 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -83,6 +83,7 @@ static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2) static int ip6_tnl_dev_init(struct net_device *dev); static void ip6_tnl_dev_setup(struct net_device *dev); +static struct rtnl_link_ops ip6_link_ops __read_mostly; static int ip6_tnl_net_id __read_mostly; struct ip6_tnl_net { @@ -94,14 +95,6 @@ struct ip6_tnl_net { struct ip6_tnl __rcu **tnls[2]; }; -/* often modified stats are per cpu, other are shared (netdev->stats) */ -struct pcpu_tstats { - unsigned long rx_packets; - unsigned long rx_bytes; - unsigned long tx_packets; - unsigned long tx_bytes; -} __attribute__((aligned(4*sizeof(unsigned long)))); - static struct net_device_stats *ip6_get_stats(struct net_device *dev) { struct pcpu_tstats sum = { 0 }; @@ -258,6 +251,33 @@ static void ip6_dev_free(struct net_device *dev) free_netdev(dev); } +static int ip6_tnl_create2(struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + struct net *net = dev_net(dev); + struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); + int err; + + t = netdev_priv(dev); + err = ip6_tnl_dev_init(dev); + if (err < 0) + goto out; + + err = register_netdevice(dev); + if (err < 0) + goto out; + + strcpy(t->parms.name, dev->name); + dev->rtnl_link_ops = &ip6_link_ops; + + dev_hold(dev); + ip6_tnl_link(ip6n, t); + return 0; + +out: + return err; +} + /** * ip6_tnl_create - create a new tunnel * @p: tunnel parameters @@ -276,7 +296,6 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p) struct ip6_tnl *t; char name[IFNAMSIZ]; int err; - struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); if (p->name[0]) strlcpy(name, p->name, IFNAMSIZ); @@ -291,17 +310,10 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p) t = netdev_priv(dev); t->parms = *p; - err = ip6_tnl_dev_init(dev); + err = ip6_tnl_create2(dev); if (err < 0) goto failed_free; - if ((err = register_netdevice(dev)) < 0) - goto failed_free; - - strcpy(t->parms.name, dev->name); - - dev_hold(dev); - ip6_tnl_link(ip6n, t); return t; failed_free: @@ -663,8 +675,7 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, icmpv6_send(skb2, rel_type, rel_code, rel_info); - if (rt) - dst_release(&rt->dst); + ip6_rt_put(rt); kfree_skb(skb2); } @@ -1208,7 +1219,7 @@ static void ip6_tnl_link_config(struct ip6_tnl *t) if (dev->mtu < IPV6_MIN_MTU) dev->mtu = IPV6_MIN_MTU; } - dst_release(&rt->dst); + ip6_rt_put(rt); } } @@ -1237,6 +1248,20 @@ ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p) return 0; } +static int ip6_tnl_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p) +{ + struct net *net = dev_net(t->dev); + struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); + int err; + + ip6_tnl_unlink(ip6n, t); + synchronize_net(); + err = ip6_tnl_change(t, p); + ip6_tnl_link(ip6n, t); + netdev_state_change(t->dev); + return err; +} + static void ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u) { @@ -1345,11 +1370,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) } else t = netdev_priv(dev); - ip6_tnl_unlink(ip6n, t); - synchronize_net(); - err = ip6_tnl_change(t, &p1); - ip6_tnl_link(ip6n, t); - netdev_state_change(dev); + err = ip6_tnl_update(t, &p1); } if (t) { err = 0; @@ -1505,6 +1526,164 @@ static int __net_init ip6_fb_tnl_dev_init(struct net_device *dev) return 0; } +static int ip6_tnl_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + u8 proto; + + if (!data) + return 0; + + proto = nla_get_u8(data[IFLA_IPTUN_PROTO]); + if (proto != IPPROTO_IPV6 && + proto != IPPROTO_IPIP && + proto != 0) + return -EINVAL; + + return 0; +} + +static void ip6_tnl_netlink_parms(struct nlattr *data[], + struct __ip6_tnl_parm *parms) +{ + memset(parms, 0, sizeof(*parms)); + + if (!data) + return; + + if (data[IFLA_IPTUN_LINK]) + parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]); + + if (data[IFLA_IPTUN_LOCAL]) + nla_memcpy(&parms->laddr, data[IFLA_IPTUN_LOCAL], + sizeof(struct in6_addr)); + + if (data[IFLA_IPTUN_REMOTE]) + nla_memcpy(&parms->raddr, data[IFLA_IPTUN_REMOTE], + sizeof(struct in6_addr)); + + if (data[IFLA_IPTUN_TTL]) + parms->hop_limit = nla_get_u8(data[IFLA_IPTUN_TTL]); + + if (data[IFLA_IPTUN_ENCAP_LIMIT]) + parms->encap_limit = nla_get_u8(data[IFLA_IPTUN_ENCAP_LIMIT]); + + if (data[IFLA_IPTUN_FLOWINFO]) + parms->flowinfo = nla_get_be32(data[IFLA_IPTUN_FLOWINFO]); + + if (data[IFLA_IPTUN_FLAGS]) + parms->flags = nla_get_u32(data[IFLA_IPTUN_FLAGS]); + + if (data[IFLA_IPTUN_PROTO]) + parms->proto = nla_get_u8(data[IFLA_IPTUN_PROTO]); +} + +static int ip6_tnl_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct net *net = dev_net(dev); + struct ip6_tnl *nt; + + nt = netdev_priv(dev); + ip6_tnl_netlink_parms(data, &nt->parms); + + if (ip6_tnl_locate(net, &nt->parms, 0)) + return -EEXIST; + + return ip6_tnl_create2(dev); +} + +static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[]) +{ + struct ip6_tnl *t; + struct __ip6_tnl_parm p; + struct net *net = dev_net(dev); + struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); + + if (dev == ip6n->fb_tnl_dev) + return -EINVAL; + + ip6_tnl_netlink_parms(data, &p); + + t = ip6_tnl_locate(net, &p, 0); + + if (t) { + if (t->dev != dev) + return -EEXIST; + } else + t = netdev_priv(dev); + + return ip6_tnl_update(t, &p); +} + +static size_t ip6_tnl_get_size(const struct net_device *dev) +{ + return + /* IFLA_IPTUN_LINK */ + nla_total_size(4) + + /* IFLA_IPTUN_LOCAL */ + nla_total_size(sizeof(struct in6_addr)) + + /* IFLA_IPTUN_REMOTE */ + nla_total_size(sizeof(struct in6_addr)) + + /* IFLA_IPTUN_TTL */ + nla_total_size(1) + + /* IFLA_IPTUN_ENCAP_LIMIT */ + nla_total_size(1) + + /* IFLA_IPTUN_FLOWINFO */ + nla_total_size(4) + + /* IFLA_IPTUN_FLAGS */ + nla_total_size(4) + + /* IFLA_IPTUN_PROTO */ + nla_total_size(1) + + 0; +} + +static int ip6_tnl_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct ip6_tnl *tunnel = netdev_priv(dev); + struct __ip6_tnl_parm *parm = &tunnel->parms; + + if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) || + nla_put(skb, IFLA_IPTUN_LOCAL, sizeof(struct in6_addr), + &parm->raddr) || + nla_put(skb, IFLA_IPTUN_REMOTE, sizeof(struct in6_addr), + &parm->laddr) || + nla_put_u8(skb, IFLA_IPTUN_TTL, parm->hop_limit) || + nla_put_u8(skb, IFLA_IPTUN_ENCAP_LIMIT, parm->encap_limit) || + nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) || + nla_put_u32(skb, IFLA_IPTUN_FLAGS, parm->flags) || + nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto)) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static const struct nla_policy ip6_tnl_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .len = sizeof(struct in6_addr) }, + [IFLA_IPTUN_REMOTE] = { .len = sizeof(struct in6_addr) }, + [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, + [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NLA_U8 }, + [IFLA_IPTUN_FLOWINFO] = { .type = NLA_U32 }, + [IFLA_IPTUN_FLAGS] = { .type = NLA_U32 }, + [IFLA_IPTUN_PROTO] = { .type = NLA_U8 }, +}; + +static struct rtnl_link_ops ip6_link_ops __read_mostly = { + .kind = "ip6tnl", + .maxtype = IFLA_IPTUN_MAX, + .policy = ip6_tnl_policy, + .priv_size = sizeof(struct ip6_tnl), + .setup = ip6_tnl_dev_setup, + .validate = ip6_tnl_validate, + .newlink = ip6_tnl_newlink, + .changelink = ip6_tnl_changelink, + .get_size = ip6_tnl_get_size, + .fill_info = ip6_tnl_fill_info, +}; + static struct xfrm6_tunnel ip4ip6_handler __read_mostly = { .handler = ip4ip6_rcv, .err_handler = ip4ip6_err, @@ -1613,9 +1792,14 @@ static int __init ip6_tunnel_init(void) pr_err("%s: can't register ip6ip6\n", __func__); goto out_ip6ip6; } + err = rtnl_link_register(&ip6_link_ops); + if (err < 0) + goto rtnl_link_failed; return 0; +rtnl_link_failed: + xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6); out_ip6ip6: xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET); out_ip4ip6: @@ -1630,6 +1814,7 @@ out_pernet: static void __exit ip6_tunnel_cleanup(void) { + rtnl_link_unregister(&ip6_link_ops); if (xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET)) pr_info("%s: can't deregister ip4ip6\n", __func__); diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index ba6d13d1f1e1..a7bee6a91335 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -397,7 +397,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, if (optname == IPV6_RTHDR && opt && opt->srcrt) { struct ipv6_rt_hdr *rthdr = opt->srcrt; switch (rthdr->type) { -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) case IPV6_SRCRT_TYPE_2: if (rthdr->hdrlen != 2 || rthdr->segments_left != 1) diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 92f8e48e4ba4..b19ed51a45bb 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -163,7 +163,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) rt = rt6_lookup(net, addr, NULL, 0, 0); if (rt) { dev = rt->dst.dev; - dst_release(&rt->dst); + ip6_rt_put(rt); } } else dev = dev_get_by_index_rcu(net, ifindex); @@ -260,7 +260,7 @@ static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net, if (rt) { dev = rt->dst.dev; - dst_release(&rt->dst); + ip6_rt_put(rt); } } else dev = dev_get_by_index_rcu(net, ifindex); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index ff36194a71aa..f41853bca428 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -535,7 +535,6 @@ static void ndisc_send_unsol_na(struct net_device *dev) { struct inet6_dev *idev; struct inet6_ifaddr *ifa; - struct in6_addr mcaddr; idev = in6_dev_get(dev); if (!idev) @@ -543,8 +542,7 @@ static void ndisc_send_unsol_na(struct net_device *dev) read_lock_bh(&idev->lock); list_for_each_entry(ifa, &idev->addr_list, if_list) { - addrconf_addr_solict_mult(&ifa->addr, &mcaddr); - ndisc_send_na(dev, NULL, &mcaddr, &ifa->addr, + ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &ifa->addr, /*router=*/ !!idev->cnf.forwarding, /*solicited=*/ false, /*override=*/ true, /*inc_opt=*/ true); @@ -906,7 +904,7 @@ static void ndisc_recv_na(struct sk_buff *skb) if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) && net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp && pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) { - /* XXX: idev->cnf.prixy_ndp */ + /* XXX: idev->cnf.proxy_ndp */ goto out; } @@ -1145,7 +1143,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) ND_PRINTK(0, err, "RA: %s got default router without neighbour\n", __func__); - dst_release(&rt->dst); + ip6_rt_put(rt); return; } } @@ -1170,7 +1168,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) ND_PRINTK(0, err, "RA: %s got default router without neighbour\n", __func__); - dst_release(&rt->dst); + ip6_rt_put(rt); return; } neigh->flags |= NTF_ROUTER; @@ -1326,8 +1324,7 @@ skip_routeinfo: ND_PRINTK(2, warn, "RA: invalid RA options\n"); } out: - if (rt) - dst_release(&rt->dst); + ip6_rt_put(rt); if (neigh) neigh_release(neigh); } @@ -1575,11 +1572,18 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, { struct net_device *dev = ptr; struct net *net = dev_net(dev); + struct inet6_dev *idev; switch (event) { case NETDEV_CHANGEADDR: neigh_changeaddr(&nd_tbl, dev); fib6_run_gc(~0UL, net); + idev = in6_dev_get(dev); + if (!idev) + break; + if (idev->cnf.ndisc_notify) + ndisc_send_unsol_na(dev); + in6_dev_put(idev); break; case NETDEV_DOWN: neigh_ifdown(&nd_tbl, dev); diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index d7cb04506c3d..10ce76a2cb94 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -207,8 +207,7 @@ ip6t_get_target_c(const struct ip6t_entry *e) return ip6t_get_target((struct ip6t_entry *)e); } -#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \ - defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE) +#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) /* This cries for unification! */ static const char *const hooknames[] = { [NF_INET_PRE_ROUTING] = "PREROUTING", @@ -381,8 +380,7 @@ ip6t_do_table(struct sk_buff *skb, t = ip6t_get_target_c(e); IP_NF_ASSERT(t->u.kernel.target); -#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \ - defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE) +#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) /* The packet is traced: log it */ if (unlikely(skb->nf_trace)) trace_packet(skb, hook, in, out, diff --git a/net/ipv6/netfilter/ip6t_rpfilter.c b/net/ipv6/netfilter/ip6t_rpfilter.c index 5d1d8b04d694..5060d54199ab 100644 --- a/net/ipv6/netfilter/ip6t_rpfilter.c +++ b/net/ipv6/netfilter/ip6t_rpfilter.c @@ -67,7 +67,7 @@ static bool rpfilter_lookup_reverse6(const struct sk_buff *skb, if (rt->rt6i_idev->dev == dev || (flags & XT_RPFILTER_LOOSE)) ret = true; out: - dst_release(&rt->dst); + ip6_rt_put(rt); return ret; } diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c index 4c8219e94e55..fa84cf8ec6bc 100644 --- a/net/ipv6/netfilter/ip6table_nat.c +++ b/net/ipv6/netfilter/ip6table_nat.c @@ -186,7 +186,8 @@ nf_nat_ipv6_out(unsigned int hooknum, if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3) || - (ct->tuplehash[dir].tuple.src.u.all != + (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 && + ct->tuplehash[dir].tuple.src.u.all != ct->tuplehash[!dir].tuple.dst.u.all)) if (nf_xfrm_me_harder(skb, AF_INET6) < 0) ret = NF_DROP; @@ -222,6 +223,7 @@ nf_nat_ipv6_local_fn(unsigned int hooknum, } #ifdef CONFIG_XFRM else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && + ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 && ct->tuplehash[dir].tuple.dst.u.all != ct->tuplehash[!dir].tuple.src.u.all) if (nf_xfrm_me_harder(skb, AF_INET6)) diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index e5f6cf7e0d8c..00ee17c3e893 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -345,7 +345,7 @@ ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len) return copy_to_user(user, &sin6, sizeof(sin6)) ? -EFAULT : 0; } -#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) +#if IS_ENABLED(CONFIG_NF_CT_NETLINK) #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink_conntrack.h> @@ -396,7 +396,7 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = { .invert_tuple = ipv6_invert_tuple, .print_tuple = ipv6_print_tuple, .get_l4proto = ipv6_get_l4proto, -#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) +#if IS_ENABLED(CONFIG_NF_CT_NETLINK) .tuple_to_nlattr = ipv6_tuple_to_nlattr, .nlattr_tuple_size = ipv6_nlattr_tuple_size, .nlattr_to_tuple = ipv6_nlattr_to_tuple, diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 2d54b2061d68..24df3dde0076 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -232,7 +232,7 @@ icmpv6_error(struct net *net, struct nf_conn *tmpl, return icmpv6_error_message(net, tmpl, skb, dataoff, ctinfo, hooknum); } -#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) +#if IS_ENABLED(CONFIG_NF_CT_NETLINK) #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink_conntrack.h> @@ -375,7 +375,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly = .get_timeouts = icmpv6_get_timeouts, .new = icmpv6_new, .error = icmpv6_error, -#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) +#if IS_ENABLED(CONFIG_NF_CT_NETLINK) .tuple_to_nlattr = icmpv6_tuple_to_nlattr, .nlattr_tuple_size = icmpv6_nlattr_tuple_size, .nlattr_to_tuple = icmpv6_nlattr_to_tuple, diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 18bd9bbbd1c6..22c8ea951185 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -85,7 +85,7 @@ static struct ctl_table nf_ct_frag6_sysctl_table[] = { { } }; -static int __net_init nf_ct_frag6_sysctl_register(struct net *net) +static int nf_ct_frag6_sysctl_register(struct net *net) { struct ctl_table *table; struct ctl_table_header *hdr; @@ -127,7 +127,7 @@ static void __net_exit nf_ct_frags6_sysctl_unregister(struct net *net) } #else -static int __net_init nf_ct_frag6_sysctl_register(struct net *net) +static int nf_ct_frag6_sysctl_register(struct net *net) { return 0; } diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c index cdd6d045e42e..aacd121fe8c5 100644 --- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c +++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c @@ -19,7 +19,7 @@ #include <linux/netfilter_ipv6.h> #include <linux/netfilter_bridge.h> -#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +#if IS_ENABLED(CONFIG_NF_CONNTRACK) #include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_l4proto.h> @@ -35,7 +35,7 @@ static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum, { u16 zone = NF_CT_DEFAULT_ZONE; -#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +#if IS_ENABLED(CONFIG_NF_CONNTRACK) if (skb->nfct) zone = nf_ct_zone((struct nf_conn *)skb->nfct); #endif @@ -60,7 +60,7 @@ static unsigned int ipv6_defrag(unsigned int hooknum, { struct sk_buff *reasm; -#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +#if IS_ENABLED(CONFIG_NF_CONNTRACK) /* Previously seen (loopback)? */ if (skb->nfct && !nf_ct_is_template((struct nf_conn *)skb->nfct)) return NF_ACCEPT; diff --git a/net/ipv6/netfilter/nf_nat_proto_icmpv6.c b/net/ipv6/netfilter/nf_nat_proto_icmpv6.c index 5d6da784305b..61aaf70f376e 100644 --- a/net/ipv6/netfilter/nf_nat_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_nat_proto_icmpv6.c @@ -84,7 +84,7 @@ const struct nf_nat_l4proto nf_nat_l4proto_icmpv6 = { .manip_pkt = icmpv6_manip_pkt, .in_range = icmpv6_in_range, .unique_tuple = icmpv6_unique_tuple, -#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) +#if IS_ENABLED(CONFIG_NF_CT_NETLINK) .nlattr_to_range = nf_nat_l4proto_nlattr_to_range, #endif }; diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c new file mode 100644 index 000000000000..c2e73e647e44 --- /dev/null +++ b/net/ipv6/output_core.c @@ -0,0 +1,76 @@ +/* + * IPv6 library code, needed by static components when full IPv6 support is + * not configured or static. These functions are needed by GSO/GRO implementation. + */ +#include <linux/export.h> +#include <net/ipv6.h> +#include <net/ip6_fib.h> + +void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt) +{ + static atomic_t ipv6_fragmentation_id; + int old, new; + +#if IS_ENABLED(CONFIG_IPV6) + if (rt && !(rt->dst.flags & DST_NOPEER)) { + struct inet_peer *peer; + struct net *net; + + net = dev_net(rt->dst.dev); + peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1); + if (peer) { + fhdr->identification = htonl(inet_getid(peer, 0)); + inet_putpeer(peer); + return; + } + } +#endif + do { + old = atomic_read(&ipv6_fragmentation_id); + new = old + 1; + if (!new) + new = 1; + } while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old); + fhdr->identification = htonl(new); +} +EXPORT_SYMBOL(ipv6_select_ident); + +int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) +{ + u16 offset = sizeof(struct ipv6hdr); + struct ipv6_opt_hdr *exthdr = + (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1); + unsigned int packet_len = skb->tail - skb->network_header; + int found_rhdr = 0; + *nexthdr = &ipv6_hdr(skb)->nexthdr; + + while (offset + 1 <= packet_len) { + + switch (**nexthdr) { + + case NEXTHDR_HOP: + break; + case NEXTHDR_ROUTING: + found_rhdr = 1; + break; + case NEXTHDR_DEST: +#if IS_ENABLED(CONFIG_IPV6_MIP6) + if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) + break; +#endif + if (found_rhdr) + return offset; + break; + default : + return offset; + } + + offset += ipv6_optlen(exthdr); + *nexthdr = &exthdr->nexthdr; + exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) + + offset); + } + + return offset; +} +EXPORT_SYMBOL(ip6_find_1stfragopt); diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c index 053082dfc93e..22d1bd4670da 100644 --- a/net/ipv6/protocol.c +++ b/net/ipv6/protocol.c @@ -25,7 +25,9 @@ #include <linux/spinlock.h> #include <net/protocol.h> +#if IS_ENABLED(CONFIG_IPV6) const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS] __read_mostly; +EXPORT_SYMBOL(inet6_protos); int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char protocol) { @@ -50,3 +52,26 @@ int inet6_del_protocol(const struct inet6_protocol *prot, unsigned char protocol return ret; } EXPORT_SYMBOL(inet6_del_protocol); +#endif + +const struct net_offload __rcu *inet6_offloads[MAX_INET_PROTOS] __read_mostly; + +int inet6_add_offload(const struct net_offload *prot, unsigned char protocol) +{ + return !cmpxchg((const struct net_offload **)&inet6_offloads[protocol], + NULL, prot) ? 0 : -1; +} +EXPORT_SYMBOL(inet6_add_offload); + +int inet6_del_offload(const struct net_offload *prot, unsigned char protocol) +{ + int ret; + + ret = (cmpxchg((const struct net_offload **)&inet6_offloads[protocol], + prot, NULL) == prot) ? 0 : -1; + + synchronize_net(); + + return ret; +} +EXPORT_SYMBOL(inet6_del_offload); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index d8e95c77db99..6cd29b1e8b92 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -50,7 +50,7 @@ #include <net/udp.h> #include <net/inet_common.h> #include <net/tcp_states.h> -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) #include <net/mip6.h> #endif #include <linux/mroute6.h> @@ -123,7 +123,7 @@ static int icmpv6_filter(const struct sock *sk, const struct sk_buff *skb) return 1; } -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) typedef int mh_filter_t(struct sock *sock, struct sk_buff *skb); static mh_filter_t __rcu *mh_filter __read_mostly; @@ -184,7 +184,7 @@ static bool ipv6_raw_deliver(struct sk_buff *skb, int nexthdr) filtered = icmpv6_filter(sk, skb); break; -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) case IPPROTO_MH: { /* XXX: To validate MH only once for each packet, diff --git a/net/ipv6/route.c b/net/ipv6/route.c index c42650cf8d4d..11249d211ea9 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -220,7 +220,7 @@ static struct dst_ops ip6_dst_blackhole_ops = { }; static const u32 ip6_template_metrics[RTAX_MAX] = { - [RTAX_HOPLIMIT - 1] = 255, + [RTAX_HOPLIMIT - 1] = 0, }; static const struct rt6_info ip6_null_entry_template = { @@ -321,13 +321,6 @@ static void ip6_dst_destroy(struct dst_entry *dst) } } -static atomic_t __rt6_peer_genid = ATOMIC_INIT(0); - -static u32 rt6_peer_genid(void) -{ - return atomic_read(&__rt6_peer_genid); -} - void rt6_bind_peer(struct rt6_info *rt, int create) { struct inet_peer_base *base; @@ -341,8 +334,6 @@ void rt6_bind_peer(struct rt6_info *rt, int create) if (peer) { if (!rt6_set_peer(rt, peer)) inet_putpeer(peer); - else - rt->rt6i_peer_genid = rt6_peer_genid(); } } @@ -732,7 +723,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, else rt6_set_expires(rt, jiffies + HZ * lifetime); - dst_release(&rt->dst); + ip6_rt_put(rt); } return 0; } @@ -948,7 +939,7 @@ restart: else goto out2; - dst_release(&rt->dst); + ip6_rt_put(rt); rt = nrt ? : net->ipv6.ip6_null_entry; dst_hold(&rt->dst); @@ -965,7 +956,7 @@ restart: * Race condition! In the gap, when table->tb6_lock was * released someone could insert this route. Relookup. */ - dst_release(&rt->dst); + ip6_rt_put(rt); goto relookup; out: @@ -1099,14 +1090,9 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev))) return NULL; - if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) { - if (rt->rt6i_peer_genid != rt6_peer_genid()) { - if (!rt6_has_peer(rt)) - rt6_bind_peer(rt, 0); - rt->rt6i_peer_genid = rt6_peer_genid(); - } + if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) return dst; - } + return NULL; } @@ -1301,7 +1287,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, rt->rt6i_dst.addr = fl6->daddr; rt->rt6i_dst.plen = 128; rt->rt6i_idev = idev; - dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255); + dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); spin_lock_bh(&icmp6_dst_lock); rt->dst.next = icmp6_dst_gc_list; @@ -1385,12 +1371,6 @@ out: return entries > rt_max_size; } -/* Clean host part of a prefix. Not necessary in radix tree, - but results in cleaner routing tables. - - Remove it only when all the things will work! - */ - int ip6_dst_hoplimit(struct dst_entry *dst) { int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT); @@ -1576,7 +1556,7 @@ int ip6_route_add(struct fib6_config *cfg) goto out; if (dev) { if (dev != grt->dst.dev) { - dst_release(&grt->dst); + ip6_rt_put(grt); goto out; } } else { @@ -1587,7 +1567,7 @@ int ip6_route_add(struct fib6_config *cfg) } if (!(grt->rt6i_flags & RTF_GATEWAY)) err = 0; - dst_release(&grt->dst); + ip6_rt_put(grt); if (err) goto out; @@ -1673,7 +1653,7 @@ static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) write_unlock_bh(&table->tb6_lock); out: - dst_release(&rt->dst); + ip6_rt_put(rt); return err; } @@ -2449,6 +2429,12 @@ beginning: goto beginning; } } + /* Because each route is added like a single route we remove + * this flag after the first nexthop (if there is a collision, + * we have already fail to add the first nexthop: + * fib6_add_rt2node() has reject it). + */ + cfg->fc_nlinfo.nlh->nlmsg_flags &= ~NLM_F_EXCL; rtnh = rtnh_next(rtnh, &remaining); } @@ -2726,7 +2712,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb) { - dst_release(&rt->dst); + ip6_rt_put(rt); err = -ENOBUFS; goto errout; } diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 3ed54ffd8d50..ca6c2c8e71d2 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -68,6 +68,7 @@ static int ipip6_tunnel_init(struct net_device *dev); static void ipip6_tunnel_setup(struct net_device *dev); static void ipip6_dev_free(struct net_device *dev); +static struct rtnl_link_ops sit_link_ops __read_mostly; static int sit_net_id __read_mostly; struct sit_net { @@ -80,22 +81,6 @@ struct sit_net { struct net_device *fb_tunnel_dev; }; -/* - * Locking : hash tables are protected by RCU and RTNL - */ - -#define for_each_ip_tunnel_rcu(start) \ - for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) - -/* often modified stats are per cpu, other are shared (netdev->stats) */ -struct pcpu_tstats { - u64 rx_packets; - u64 rx_bytes; - u64 tx_packets; - u64 tx_bytes; - struct u64_stats_sync syncp; -}; - static struct rtnl_link_stats64 *ipip6_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *tot) { @@ -141,20 +126,20 @@ static struct ip_tunnel *ipip6_tunnel_lookup(struct net *net, struct ip_tunnel *t; struct sit_net *sitn = net_generic(net, sit_net_id); - for_each_ip_tunnel_rcu(sitn->tunnels_r_l[h0 ^ h1]) { + for_each_ip_tunnel_rcu(t, sitn->tunnels_r_l[h0 ^ h1]) { if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr && (!dev || !t->parms.link || dev->iflink == t->parms.link) && (t->dev->flags & IFF_UP)) return t; } - for_each_ip_tunnel_rcu(sitn->tunnels_r[h0]) { + for_each_ip_tunnel_rcu(t, sitn->tunnels_r[h0]) { if (remote == t->parms.iph.daddr && (!dev || !t->parms.link || dev->iflink == t->parms.link) && (t->dev->flags & IFF_UP)) return t; } - for_each_ip_tunnel_rcu(sitn->tunnels_l[h1]) { + for_each_ip_tunnel_rcu(t, sitn->tunnels_l[h1]) { if (local == t->parms.iph.saddr && (!dev || !t->parms.link || dev->iflink == t->parms.link) && (t->dev->flags & IFF_UP)) @@ -231,6 +216,37 @@ static void ipip6_tunnel_clone_6rd(struct net_device *dev, struct sit_net *sitn) #endif } +static int ipip6_tunnel_create(struct net_device *dev) +{ + struct ip_tunnel *t = netdev_priv(dev); + struct net *net = dev_net(dev); + struct sit_net *sitn = net_generic(net, sit_net_id); + int err; + + err = ipip6_tunnel_init(dev); + if (err < 0) + goto out; + ipip6_tunnel_clone_6rd(dev, sitn); + + if ((__force u16)t->parms.i_flags & SIT_ISATAP) + dev->priv_flags |= IFF_ISATAP; + + err = register_netdevice(dev); + if (err < 0) + goto out; + + strcpy(t->parms.name, dev->name); + dev->rtnl_link_ops = &sit_link_ops; + + dev_hold(dev); + + ipip6_tunnel_link(sitn, t); + return 0; + +out: + return err; +} + static struct ip_tunnel *ipip6_tunnel_locate(struct net *net, struct ip_tunnel_parm *parms, int create) { @@ -271,21 +287,9 @@ static struct ip_tunnel *ipip6_tunnel_locate(struct net *net, nt = netdev_priv(dev); nt->parms = *parms; - if (ipip6_tunnel_init(dev) < 0) + if (ipip6_tunnel_create(dev) < 0) goto failed_free; - ipip6_tunnel_clone_6rd(dev, sitn); - - if (parms->i_flags & SIT_ISATAP) - dev->priv_flags |= IFF_ISATAP; - if (register_netdevice(dev) < 0) - goto failed_free; - - strcpy(nt->parms.name, dev->name); - - dev_hold(dev); - - ipip6_tunnel_link(sitn, nt); return nt; failed_free: @@ -683,7 +687,6 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip_tunnel *tunnel = netdev_priv(dev); - struct pcpu_tstats *tstats; const struct iphdr *tiph = &tunnel->parms.iph; const struct ipv6hdr *iph6 = ipv6_hdr(skb); u8 tos = tunnel->parms.iph.tos; @@ -864,9 +867,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, if ((iph->ttl = tiph->ttl) == 0) iph->ttl = iph6->hop_limit; - nf_reset(skb); - tstats = this_cpu_ptr(dev->tstats); - __IPTUNNEL_XMIT(tstats, &dev->stats); + iptunnel_xmit(skb, dev); return NETDEV_TX_OK; tx_error_icmp: @@ -914,6 +915,27 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev) dev->iflink = tunnel->parms.link; } +static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p) +{ + struct net *net = dev_net(t->dev); + struct sit_net *sitn = net_generic(net, sit_net_id); + + ipip6_tunnel_unlink(sitn, t); + synchronize_net(); + t->parms.iph.saddr = p->iph.saddr; + t->parms.iph.daddr = p->iph.daddr; + memcpy(t->dev->dev_addr, &p->iph.saddr, 4); + memcpy(t->dev->broadcast, &p->iph.daddr, 4); + ipip6_tunnel_link(sitn, t); + t->parms.iph.ttl = p->iph.ttl; + t->parms.iph.tos = p->iph.tos; + if (t->parms.link != p->link) { + t->parms.link = p->link; + ipip6_tunnel_bind_dev(t->dev); + } + netdev_state_change(t->dev); +} + static int ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) { @@ -995,28 +1017,13 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) break; } t = netdev_priv(dev); - ipip6_tunnel_unlink(sitn, t); - synchronize_net(); - t->parms.iph.saddr = p.iph.saddr; - t->parms.iph.daddr = p.iph.daddr; - memcpy(dev->dev_addr, &p.iph.saddr, 4); - memcpy(dev->broadcast, &p.iph.daddr, 4); - ipip6_tunnel_link(sitn, t); - netdev_state_change(dev); } + + ipip6_tunnel_update(t, &p); } if (t) { err = 0; - if (cmd == SIOCCHGTUNNEL) { - t->parms.iph.ttl = p.iph.ttl; - t->parms.iph.tos = p.iph.tos; - if (t->parms.link != p.link) { - t->parms.link = p.link; - ipip6_tunnel_bind_dev(dev); - netdev_state_change(dev); - } - } if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p))) err = -EFAULT; } else @@ -1216,6 +1223,150 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev) return 0; } +static void ipip6_netlink_parms(struct nlattr *data[], + struct ip_tunnel_parm *parms) +{ + memset(parms, 0, sizeof(*parms)); + + parms->iph.version = 4; + parms->iph.protocol = IPPROTO_IPV6; + parms->iph.ihl = 5; + parms->iph.ttl = 64; + + if (!data) + return; + + if (data[IFLA_IPTUN_LINK]) + parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]); + + if (data[IFLA_IPTUN_LOCAL]) + parms->iph.saddr = nla_get_be32(data[IFLA_IPTUN_LOCAL]); + + if (data[IFLA_IPTUN_REMOTE]) + parms->iph.daddr = nla_get_be32(data[IFLA_IPTUN_REMOTE]); + + if (data[IFLA_IPTUN_TTL]) { + parms->iph.ttl = nla_get_u8(data[IFLA_IPTUN_TTL]); + if (parms->iph.ttl) + parms->iph.frag_off = htons(IP_DF); + } + + if (data[IFLA_IPTUN_TOS]) + parms->iph.tos = nla_get_u8(data[IFLA_IPTUN_TOS]); + + if (!data[IFLA_IPTUN_PMTUDISC] || nla_get_u8(data[IFLA_IPTUN_PMTUDISC])) + parms->iph.frag_off = htons(IP_DF); + + if (data[IFLA_IPTUN_FLAGS]) + parms->i_flags = nla_get_be16(data[IFLA_IPTUN_FLAGS]); +} + +static int ipip6_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct net *net = dev_net(dev); + struct ip_tunnel *nt; + + nt = netdev_priv(dev); + ipip6_netlink_parms(data, &nt->parms); + + if (ipip6_tunnel_locate(net, &nt->parms, 0)) + return -EEXIST; + + return ipip6_tunnel_create(dev); +} + +static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[]) +{ + struct ip_tunnel *t; + struct ip_tunnel_parm p; + struct net *net = dev_net(dev); + struct sit_net *sitn = net_generic(net, sit_net_id); + + if (dev == sitn->fb_tunnel_dev) + return -EINVAL; + + ipip6_netlink_parms(data, &p); + + if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) || + (!(dev->flags & IFF_POINTOPOINT) && p.iph.daddr)) + return -EINVAL; + + t = ipip6_tunnel_locate(net, &p, 0); + + if (t) { + if (t->dev != dev) + return -EEXIST; + } else + t = netdev_priv(dev); + + ipip6_tunnel_update(t, &p); + return 0; +} + +static size_t ipip6_get_size(const struct net_device *dev) +{ + return + /* IFLA_IPTUN_LINK */ + nla_total_size(4) + + /* IFLA_IPTUN_LOCAL */ + nla_total_size(4) + + /* IFLA_IPTUN_REMOTE */ + nla_total_size(4) + + /* IFLA_IPTUN_TTL */ + nla_total_size(1) + + /* IFLA_IPTUN_TOS */ + nla_total_size(1) + + /* IFLA_IPTUN_PMTUDISC */ + nla_total_size(1) + + /* IFLA_IPTUN_FLAGS */ + nla_total_size(2) + + 0; +} + +static int ipip6_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct ip_tunnel *tunnel = netdev_priv(dev); + struct ip_tunnel_parm *parm = &tunnel->parms; + + if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) || + nla_put_be32(skb, IFLA_IPTUN_LOCAL, parm->iph.saddr) || + nla_put_be32(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) || + nla_put_u8(skb, IFLA_IPTUN_TTL, parm->iph.ttl) || + nla_put_u8(skb, IFLA_IPTUN_TOS, parm->iph.tos) || + nla_put_u8(skb, IFLA_IPTUN_PMTUDISC, + !!(parm->iph.frag_off & htons(IP_DF))) || + nla_put_be16(skb, IFLA_IPTUN_FLAGS, parm->i_flags)) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, + [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, + [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, + [IFLA_IPTUN_TOS] = { .type = NLA_U8 }, + [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 }, + [IFLA_IPTUN_FLAGS] = { .type = NLA_U16 }, +}; + +static struct rtnl_link_ops sit_link_ops __read_mostly = { + .kind = "sit", + .maxtype = IFLA_IPTUN_MAX, + .policy = ipip6_policy, + .priv_size = sizeof(struct ip_tunnel), + .setup = ipip6_tunnel_setup, + .newlink = ipip6_newlink, + .changelink = ipip6_changelink, + .get_size = ipip6_get_size, + .fill_info = ipip6_fill_info, +}; + static struct xfrm_tunnel sit_handler __read_mostly = { .handler = ipip6_rcv, .err_handler = ipip6_err, @@ -1302,6 +1453,7 @@ static struct pernet_operations sit_net_ops = { static void __exit sit_cleanup(void) { + rtnl_link_unregister(&sit_link_ops); xfrm4_tunnel_deregister(&sit_handler, AF_INET6); unregister_pernet_device(&sit_net_ops); @@ -1319,10 +1471,21 @@ static int __init sit_init(void) return err; err = xfrm4_tunnel_register(&sit_handler, AF_INET6); if (err < 0) { - unregister_pernet_device(&sit_net_ops); pr_info("%s: can't add protocol\n", __func__); + goto xfrm_tunnel_failed; } + err = rtnl_link_register(&sit_link_ops); + if (err < 0) + goto rtnl_link_failed; + +out: return err; + +rtnl_link_failed: + xfrm4_tunnel_deregister(&sit_handler, AF_INET6); +xfrm_tunnel_failed: + unregister_pernet_device(&sit_net_ops); + goto out; } module_init(sit_init); diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 182ab9a85d6c..40161977f7cf 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -214,7 +214,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ireq6->iif = inet6_iif(skb); req->expires = 0UL; - req->retrans = 0; + req->num_retrans = 0; ireq->ecn_ok = ecn_ok; ireq->snd_wscale = tcp_opt.snd_wscale; ireq->sack_ok = tcp_opt.sack_ok; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index bb6782e84996..6c0f2526f3f1 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -77,9 +77,6 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, struct request_sock *req); static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb); -static void __tcp_v6_send_check(struct sk_buff *skb, - const struct in6_addr *saddr, - const struct in6_addr *daddr); static const struct inet_connection_sock_af_ops ipv6_mapped; static const struct inet_connection_sock_af_ops ipv6_specific; @@ -119,14 +116,6 @@ static void tcp_v6_hash(struct sock *sk) } } -static __inline__ __sum16 tcp_v6_check(int len, - const struct in6_addr *saddr, - const struct in6_addr *daddr, - __wsum base) -{ - return csum_ipv6_magic(saddr, daddr, len, IPPROTO_TCP, base); -} - static __u32 tcp_v6_init_sequence(const struct sk_buff *skb) { return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32, @@ -495,9 +484,12 @@ static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req, struct request_values *rvp) { struct flowi6 fl6; + int res; - TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); - return tcp_v6_send_synack(sk, NULL, &fl6, req, rvp, 0); + res = tcp_v6_send_synack(sk, NULL, &fl6, req, rvp, 0); + if (!res) + TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); + return res; } static void tcp_v6_reqsk_destructor(struct request_sock *req) @@ -719,94 +711,6 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { }; #endif -static void __tcp_v6_send_check(struct sk_buff *skb, - const struct in6_addr *saddr, const struct in6_addr *daddr) -{ - struct tcphdr *th = tcp_hdr(skb); - - if (skb->ip_summed == CHECKSUM_PARTIAL) { - th->check = ~tcp_v6_check(skb->len, saddr, daddr, 0); - skb->csum_start = skb_transport_header(skb) - skb->head; - skb->csum_offset = offsetof(struct tcphdr, check); - } else { - th->check = tcp_v6_check(skb->len, saddr, daddr, - csum_partial(th, th->doff << 2, - skb->csum)); - } -} - -static void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb) -{ - struct ipv6_pinfo *np = inet6_sk(sk); - - __tcp_v6_send_check(skb, &np->saddr, &np->daddr); -} - -static int tcp_v6_gso_send_check(struct sk_buff *skb) -{ - const struct ipv6hdr *ipv6h; - struct tcphdr *th; - - if (!pskb_may_pull(skb, sizeof(*th))) - return -EINVAL; - - ipv6h = ipv6_hdr(skb); - th = tcp_hdr(skb); - - th->check = 0; - skb->ip_summed = CHECKSUM_PARTIAL; - __tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr); - return 0; -} - -static struct sk_buff **tcp6_gro_receive(struct sk_buff **head, - struct sk_buff *skb) -{ - const struct ipv6hdr *iph = skb_gro_network_header(skb); - __wsum wsum; - __sum16 sum; - - switch (skb->ip_summed) { - case CHECKSUM_COMPLETE: - if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr, - skb->csum)) { - skb->ip_summed = CHECKSUM_UNNECESSARY; - break; - } -flush: - NAPI_GRO_CB(skb)->flush = 1; - return NULL; - - case CHECKSUM_NONE: - wsum = ~csum_unfold(csum_ipv6_magic(&iph->saddr, &iph->daddr, - skb_gro_len(skb), - IPPROTO_TCP, 0)); - sum = csum_fold(skb_checksum(skb, - skb_gro_offset(skb), - skb_gro_len(skb), - wsum)); - if (sum) - goto flush; - - skb->ip_summed = CHECKSUM_UNNECESSARY; - break; - } - - return tcp_gro_receive(head, skb); -} - -static int tcp6_gro_complete(struct sk_buff *skb) -{ - const struct ipv6hdr *iph = ipv6_hdr(skb); - struct tcphdr *th = tcp_hdr(skb); - - th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb), - &iph->saddr, &iph->daddr, 0); - skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; - - return tcp_gro_complete(skb); -} - static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts, struct tcp_md5sig_key *key, int rst, u8 tclass) { @@ -1364,7 +1268,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, tcp_initialize_rcv_mss(newsk); tcp_synack_rtt_meas(newsk, req); - newtp->total_retrans = req->retrans; + newtp->total_retrans = req->num_retrans; newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6; newinet->inet_rcv_saddr = LOOPBACK4_IPV6; @@ -1866,7 +1770,7 @@ static void get_openreq6(struct seq_file *seq, 0,0, /* could print option size, but that is af dependent. */ 1, /* timers active (only the expire timer) */ jiffies_to_clock_t(ttd), - req->retrans, + req->num_timeout, from_kuid_munged(seq_user_ns(seq), uid), 0, /* non standard timer */ 0, /* open_requests have no inode */ @@ -2063,10 +1967,6 @@ static const struct inet6_protocol tcpv6_protocol = { .early_demux = tcp_v6_early_demux, .handler = tcp_v6_rcv, .err_handler = tcp_v6_err, - .gso_send_check = tcp_v6_gso_send_check, - .gso_segment = tcp_tso_segment, - .gro_receive = tcp6_gro_receive, - .gro_complete = tcp6_gro_complete, .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, }; @@ -2121,10 +2021,10 @@ int __init tcpv6_init(void) out: return ret; -out_tcpv6_protocol: - inet6_del_protocol(&tcpv6_protocol, IPPROTO_TCP); out_tcpv6_protosw: inet6_unregister_protosw(&tcpv6_protosw); +out_tcpv6_protocol: + inet6_del_protocol(&tcpv6_protocol, IPPROTO_TCP); goto out; } diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c new file mode 100644 index 000000000000..2ec6bf6a0aa0 --- /dev/null +++ b/net/ipv6/tcpv6_offload.c @@ -0,0 +1,95 @@ +/* + * IPV6 GSO/GRO offload support + * Linux INET6 implementation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * TCPv6 GSO/GRO support + */ +#include <linux/skbuff.h> +#include <net/protocol.h> +#include <net/tcp.h> +#include <net/ip6_checksum.h> +#include "ip6_offload.h" + +static int tcp_v6_gso_send_check(struct sk_buff *skb) +{ + const struct ipv6hdr *ipv6h; + struct tcphdr *th; + + if (!pskb_may_pull(skb, sizeof(*th))) + return -EINVAL; + + ipv6h = ipv6_hdr(skb); + th = tcp_hdr(skb); + + th->check = 0; + skb->ip_summed = CHECKSUM_PARTIAL; + __tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr); + return 0; +} + +static struct sk_buff **tcp6_gro_receive(struct sk_buff **head, + struct sk_buff *skb) +{ + const struct ipv6hdr *iph = skb_gro_network_header(skb); + __wsum wsum; + __sum16 sum; + + switch (skb->ip_summed) { + case CHECKSUM_COMPLETE: + if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr, + skb->csum)) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + break; + } +flush: + NAPI_GRO_CB(skb)->flush = 1; + return NULL; + + case CHECKSUM_NONE: + wsum = ~csum_unfold(csum_ipv6_magic(&iph->saddr, &iph->daddr, + skb_gro_len(skb), + IPPROTO_TCP, 0)); + sum = csum_fold(skb_checksum(skb, + skb_gro_offset(skb), + skb_gro_len(skb), + wsum)); + if (sum) + goto flush; + + skb->ip_summed = CHECKSUM_UNNECESSARY; + break; + } + + return tcp_gro_receive(head, skb); +} + +static int tcp6_gro_complete(struct sk_buff *skb) +{ + const struct ipv6hdr *iph = ipv6_hdr(skb); + struct tcphdr *th = tcp_hdr(skb); + + th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb), + &iph->saddr, &iph->daddr, 0); + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; + + return tcp_gro_complete(skb); +} + +static const struct net_offload tcpv6_offload = { + .callbacks = { + .gso_send_check = tcp_v6_gso_send_check, + .gso_segment = tcp_tso_segment, + .gro_receive = tcp6_gro_receive, + .gro_complete = tcp6_gro_complete, + }, +}; + +int __init tcpv6_offload_init(void) +{ + return inet6_add_offload(&tcpv6_offload, IPPROTO_TCP); +} diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index fc9997260a6b..dfaa29b8b293 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1343,103 +1343,9 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname, } #endif -static int udp6_ufo_send_check(struct sk_buff *skb) -{ - const struct ipv6hdr *ipv6h; - struct udphdr *uh; - - if (!pskb_may_pull(skb, sizeof(*uh))) - return -EINVAL; - - ipv6h = ipv6_hdr(skb); - uh = udp_hdr(skb); - - uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len, - IPPROTO_UDP, 0); - skb->csum_start = skb_transport_header(skb) - skb->head; - skb->csum_offset = offsetof(struct udphdr, check); - skb->ip_summed = CHECKSUM_PARTIAL; - return 0; -} - -static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, - netdev_features_t features) -{ - struct sk_buff *segs = ERR_PTR(-EINVAL); - unsigned int mss; - unsigned int unfrag_ip6hlen, unfrag_len; - struct frag_hdr *fptr; - u8 *mac_start, *prevhdr; - u8 nexthdr; - u8 frag_hdr_sz = sizeof(struct frag_hdr); - int offset; - __wsum csum; - - mss = skb_shinfo(skb)->gso_size; - if (unlikely(skb->len <= mss)) - goto out; - - if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { - /* Packet is from an untrusted source, reset gso_segs. */ - int type = skb_shinfo(skb)->gso_type; - - if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) || - !(type & (SKB_GSO_UDP)))) - goto out; - - skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); - - segs = NULL; - goto out; - } - - /* Do software UFO. Complete and fill in the UDP checksum as HW cannot - * do checksum of UDP packets sent as multiple IP fragments. - */ - offset = skb_checksum_start_offset(skb); - csum = skb_checksum(skb, offset, skb->len - offset, 0); - offset += skb->csum_offset; - *(__sum16 *)(skb->data + offset) = csum_fold(csum); - skb->ip_summed = CHECKSUM_NONE; - - /* Check if there is enough headroom to insert fragment header. */ - if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) && - pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC)) - goto out; - - /* Find the unfragmentable header and shift it left by frag_hdr_sz - * bytes to insert fragment header. - */ - unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); - nexthdr = *prevhdr; - *prevhdr = NEXTHDR_FRAGMENT; - unfrag_len = skb_network_header(skb) - skb_mac_header(skb) + - unfrag_ip6hlen; - mac_start = skb_mac_header(skb); - memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len); - - skb->mac_header -= frag_hdr_sz; - skb->network_header -= frag_hdr_sz; - - fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen); - fptr->nexthdr = nexthdr; - fptr->reserved = 0; - ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb)); - - /* Fragment the skb. ipv6 header and the remaining fields of the - * fragment header are updated in ipv6_gso_segment() - */ - segs = skb_segment(skb, features); - -out: - return segs; -} - static const struct inet6_protocol udpv6_protocol = { .handler = udpv6_rcv, .err_handler = udpv6_err, - .gso_send_check = udp6_ufo_send_check, - .gso_segment = udp6_ufo_fragment, .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, }; diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c new file mode 100644 index 000000000000..0c8934a317c2 --- /dev/null +++ b/net/ipv6/udp_offload.c @@ -0,0 +1,120 @@ +/* + * IPV6 GSO/GRO offload support + * Linux INET6 implementation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * UDPv6 GSO support + */ +#include <linux/skbuff.h> +#include <net/protocol.h> +#include <net/ipv6.h> +#include <net/udp.h> +#include <net/ip6_checksum.h> +#include "ip6_offload.h" + +static int udp6_ufo_send_check(struct sk_buff *skb) +{ + const struct ipv6hdr *ipv6h; + struct udphdr *uh; + + if (!pskb_may_pull(skb, sizeof(*uh))) + return -EINVAL; + + ipv6h = ipv6_hdr(skb); + uh = udp_hdr(skb); + + uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len, + IPPROTO_UDP, 0); + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + skb->ip_summed = CHECKSUM_PARTIAL; + return 0; +} + +static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, + netdev_features_t features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + unsigned int mss; + unsigned int unfrag_ip6hlen, unfrag_len; + struct frag_hdr *fptr; + u8 *mac_start, *prevhdr; + u8 nexthdr; + u8 frag_hdr_sz = sizeof(struct frag_hdr); + int offset; + __wsum csum; + + mss = skb_shinfo(skb)->gso_size; + if (unlikely(skb->len <= mss)) + goto out; + + if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { + /* Packet is from an untrusted source, reset gso_segs. */ + int type = skb_shinfo(skb)->gso_type; + + if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) || + !(type & (SKB_GSO_UDP)))) + goto out; + + skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); + + segs = NULL; + goto out; + } + + /* Do software UFO. Complete and fill in the UDP checksum as HW cannot + * do checksum of UDP packets sent as multiple IP fragments. + */ + offset = skb_checksum_start_offset(skb); + csum = skb_checksum(skb, offset, skb->len - offset, 0); + offset += skb->csum_offset; + *(__sum16 *)(skb->data + offset) = csum_fold(csum); + skb->ip_summed = CHECKSUM_NONE; + + /* Check if there is enough headroom to insert fragment header. */ + if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) && + pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC)) + goto out; + + /* Find the unfragmentable header and shift it left by frag_hdr_sz + * bytes to insert fragment header. + */ + unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); + nexthdr = *prevhdr; + *prevhdr = NEXTHDR_FRAGMENT; + unfrag_len = skb_network_header(skb) - skb_mac_header(skb) + + unfrag_ip6hlen; + mac_start = skb_mac_header(skb); + memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len); + + skb->mac_header -= frag_hdr_sz; + skb->network_header -= frag_hdr_sz; + + fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen); + fptr->nexthdr = nexthdr; + fptr->reserved = 0; + ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb)); + + /* Fragment the skb. ipv6 header and the remaining fields of the + * fragment header are updated in ipv6_gso_segment() + */ + segs = skb_segment(skb, features); + +out: + return segs; +} +static const struct net_offload udpv6_offload = { + .callbacks = { + .gso_send_check = udp6_ufo_send_check, + .gso_segment = udp6_ufo_fragment, + }, +}; + +int __init udp_offload_init(void) +{ + return inet6_add_offload(&udpv6_offload, IPPROTO_UDP); +} diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index f8c4c08ffb60..f3ed8ca59b94 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -20,7 +20,7 @@ #include <net/ip.h> #include <net/ipv6.h> #include <net/ip6_route.h> -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) #include <net/mip6.h> #endif @@ -182,7 +182,7 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse) fl6->flowi6_proto = nexthdr; return; -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) case IPPROTO_MH: if (!onlyproto && pskb_may_pull(skb, nh + offset + 3 - skb->data)) { struct ip6_mh *mh; diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c index 3f2f7c4ab721..d8c70b8efc24 100644 --- a/net/ipv6/xfrm6_state.c +++ b/net/ipv6/xfrm6_state.c @@ -101,7 +101,7 @@ static int __xfrm6_state_sort_cmp(void *p) return 1; else return 3; -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) case XFRM_MODE_ROUTEOPTIMIZATION: case XFRM_MODE_IN_TRIGGER: return 2; @@ -134,7 +134,7 @@ static int __xfrm6_tmpl_sort_cmp(void *p) switch (v->mode) { case XFRM_MODE_TRANSPORT: return 1; -#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) +#if IS_ENABLED(CONFIG_IPV6_MIP6) case XFRM_MODE_ROUTEOPTIMIZATION: case XFRM_MODE_IN_TRIGGER: return 2; diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 95a3a7a336ba..496ce2cebcd7 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -421,6 +421,8 @@ static int ircomm_tty_install(struct tty_driver *driver, struct tty_struct *tty) hashbin_insert(ircomm_tty, (irda_queue_t *) self, line, NULL); } + tty->driver_data = self; + return tty_port_install(&self->port, driver, tty); } diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index 37b8b8ba31f7..76125c57ee6d 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -291,6 +291,7 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p out_del_dev: free_netdev(dev); + spriv->dev = NULL; out_del_session: l2tp_session_delete(session); out: diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 63af25458fda..b4ecf267a34b 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -248,7 +248,7 @@ config MAC80211_MHWMP_DEBUG Do not select this option. config MAC80211_MESH_SYNC_DEBUG - bool "Verbose mesh mesh synchronization debugging" + bool "Verbose mesh synchronization debugging" depends on MAC80211_DEBUG_MENU depends on MAC80211_MESH ---help--- diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index a7dd110faafa..4911202334d9 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -8,6 +8,7 @@ mac80211-y := \ wpa.o \ scan.o offchannel.o \ ht.o agg-tx.o agg-rx.o \ + vht.o \ ibss.o \ iface.o \ rate.o \ diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c index a04752e91023..493353534a0f 100644 --- a/net/mac80211/aes_cmac.c +++ b/net/mac80211/aes_cmac.c @@ -126,3 +126,20 @@ void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm) { crypto_free_cipher(tfm); } + +void ieee80211_aes_cmac_calculate_k1_k2(struct ieee80211_key_conf *keyconf, + u8 *k1, u8 *k2) +{ + u8 l[AES_BLOCK_SIZE] = {}; + struct ieee80211_key *key = + container_of(keyconf, struct ieee80211_key, conf); + + crypto_cipher_encrypt_one(key->u.aes_cmac.tfm, l, l); + + memcpy(k1, l, AES_BLOCK_SIZE); + gf_mulx(k1); + + memcpy(k2, k1, AES_BLOCK_SIZE); + gf_mulx(k2); +} +EXPORT_SYMBOL(ieee80211_aes_cmac_calculate_k1_k2); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 05f3a313db88..5eab1325a0f6 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -372,10 +372,11 @@ static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy, static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, int idx) { + enum ieee80211_band band = ieee80211_get_sdata_band(sta->sdata); + if (!(rate->flags & RATE_INFO_FLAGS_MCS)) { struct ieee80211_supported_band *sband; - sband = sta->local->hw.wiphy->bands[ - sta->local->oper_channel->band]; + sband = sta->local->hw.wiphy->bands[band]; rate->legacy = sband->bitrates[idx].bitrate; } else rate->mcs = idx; @@ -532,6 +533,8 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy, u64 *data) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *channel; struct sta_info *sta; struct ieee80211_local *local = sdata->local; struct station_info sinfo; @@ -607,19 +610,26 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy, do_survey: i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; /* Get survey stats for current channel */ - q = 0; - while (true) { - survey.filled = 0; - if (drv_get_survey(local, q, &survey) != 0) { - survey.filled = 0; - break; - } + survey.filled = 0; - if (survey.channel && - (local->oper_channel->center_freq == - survey.channel->center_freq)) - break; - q++; + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (chanctx_conf) + channel = chanctx_conf->channel; + else + channel = NULL; + rcu_read_unlock(); + + if (channel) { + q = 0; + do { + survey.filled = 0; + if (drv_get_survey(local, q, &survey) != 0) { + survey.filled = 0; + break; + } + q++; + } while (channel != survey.channel); } if (survey.filled) @@ -724,47 +734,42 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, return ret; } -static int ieee80211_set_channel(struct wiphy *wiphy, - struct net_device *netdev, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) +static int ieee80211_set_monitor_channel(struct wiphy *wiphy, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) { struct ieee80211_local *local = wiphy_priv(wiphy); - struct ieee80211_sub_if_data *sdata = NULL; - - if (netdev) - sdata = IEEE80211_DEV_TO_SUB_IF(netdev); - - switch (ieee80211_get_channel_mode(local, NULL)) { - case CHAN_MODE_HOPPING: - return -EBUSY; - case CHAN_MODE_FIXED: - if (local->oper_channel != chan || - (!sdata && local->_oper_channel_type != channel_type)) - return -EBUSY; - if (!sdata && local->_oper_channel_type == channel_type) - return 0; - break; - case CHAN_MODE_UNDEFINED: - break; - } - - if (!ieee80211_set_channel_type(local, sdata, channel_type)) - return -EBUSY; + struct ieee80211_sub_if_data *sdata; + int ret = 0; - local->oper_channel = chan; + if (local->monitor_channel == chan && + local->monitor_channel_type == channel_type) + return 0; - /* auto-detects changes */ - ieee80211_hw_config(local, 0); + mutex_lock(&local->iflist_mtx); + if (local->use_chanctx) { + sdata = rcu_dereference_protected( + local->monitor_sdata, + lockdep_is_held(&local->iflist_mtx)); + if (sdata) { + ieee80211_vif_release_channel(sdata); + ret = ieee80211_vif_use_channel( + sdata, chan, channel_type, + IEEE80211_CHANCTX_EXCLUSIVE); + } + } else if (local->open_count == local->monitors) { + local->_oper_channel = chan; + local->_oper_channel_type = channel_type; + ieee80211_hw_config(local, 0); + } - return 0; -} + if (ret == 0) { + local->monitor_channel = chan; + local->monitor_channel_type = channel_type; + } + mutex_unlock(&local->iflist_mtx); -static int ieee80211_set_monitor_channel(struct wiphy *wiphy, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) -{ - return ieee80211_set_channel(wiphy, NULL, chan, channel_type); + return ret; } static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, @@ -879,8 +884,13 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, if (old) return -EALREADY; - err = ieee80211_set_channel(wiphy, dev, params->channel, - params->channel_type); + /* TODO: make hostapd tell us what it wants */ + sdata->smps_mode = IEEE80211_SMPS_OFF; + sdata->needed_rx_chains = sdata->local->rx_chains; + + err = ieee80211_vif_use_channel(sdata, params->channel, + params->channel_type, + IEEE80211_CHANCTX_SHARED); if (err) return err; @@ -963,6 +973,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) sta_info_flush(sdata->local, sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); + ieee80211_vif_release_channel(sdata); + return 0; } @@ -1019,9 +1031,10 @@ static int sta_apply_parameters(struct ieee80211_local *local, int i, j; struct ieee80211_supported_band *sband; struct ieee80211_sub_if_data *sdata = sta->sdata; + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); u32 mask, set; - sband = local->hw.wiphy->bands[local->oper_channel->band]; + sband = local->hw.wiphy->bands[band]; mask = params->sta_flags_mask; set = params->sta_flags_set; @@ -1136,7 +1149,7 @@ static int sta_apply_parameters(struct ieee80211_local *local, rates |= BIT(j); } } - sta->sta.supp_rates[local->oper_channel->band] = rates; + sta->sta.supp_rates[band] = rates; } if (params->ht_capa) @@ -1144,6 +1157,11 @@ static int sta_apply_parameters(struct ieee80211_local *local, params->ht_capa, &sta->sta.ht_cap); + if (params->vht_capa) + ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, + params->vht_capa, + &sta->sta.vht_cap); + if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) @@ -1664,8 +1682,13 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, if (err) return err; - err = ieee80211_set_channel(wiphy, dev, setup->channel, - setup->channel_type); + /* can mesh use other SMPS modes? */ + sdata->smps_mode = IEEE80211_SMPS_OFF; + sdata->needed_rx_chains = sdata->local->rx_chains; + + err = ieee80211_vif_use_channel(sdata, setup->channel, + setup->channel_type, + IEEE80211_CHANCTX_SHARED); if (err) return err; @@ -1679,6 +1702,7 @@ static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev) struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ieee80211_stop_mesh(sdata); + ieee80211_vif_release_channel(sdata); return 0; } @@ -1688,10 +1712,14 @@ static int ieee80211_change_bss(struct wiphy *wiphy, struct net_device *dev, struct bss_parameters *params) { - struct ieee80211_sub_if_data *sdata; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + enum ieee80211_band band; u32 changed = 0; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (!rtnl_dereference(sdata->u.ap.beacon)) + return -ENOENT; + + band = ieee80211_get_sdata_band(sdata); if (params->use_cts_prot >= 0) { sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot; @@ -1704,7 +1732,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy, } if (!sdata->vif.bss_conf.use_short_slot && - sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ) { + band == IEEE80211_BAND_5GHZ) { sdata->vif.bss_conf.use_short_slot = true; changed |= BSS_CHANGED_ERP_SLOT; } @@ -1718,9 +1746,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy, if (params->basic_rates) { int i, j; u32 rates = 0; - struct ieee80211_local *local = wiphy_priv(wiphy); - struct ieee80211_supported_band *sband = - wiphy->bands[local->oper_channel->band]; + struct ieee80211_supported_band *sband = wiphy->bands[band]; for (i = 0; i < params->basic_rates_len; i++) { int rate = (params->basic_rates[i] & 0x7f) * 5; @@ -1829,7 +1855,16 @@ static int ieee80211_scan(struct wiphy *wiphy, * beaconing hasn't been configured yet */ case NL80211_IFTYPE_AP: - if (sdata->u.ap.beacon) + /* + * If the scan has been forced (and the driver supports + * forcing), don't care about being beaconing already. + * This will create problems to the attached stations (e.g. all + * the frames sent while scanning on other channel will be + * lost) + */ + if (sdata->u.ap.beacon && + (!(wiphy->features & NL80211_FEATURE_AP_SCAN) || + !(req->flags & NL80211_SCAN_FLAG_AP))) return -EOPNOTSUPP; break; default: @@ -1872,20 +1907,6 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_assoc_request *req) { - struct ieee80211_local *local = wiphy_priv(wiphy); - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - switch (ieee80211_get_channel_mode(local, sdata)) { - case CHAN_MODE_HOPPING: - return -EBUSY; - case CHAN_MODE_FIXED: - if (local->oper_channel == req->bss->channel) - break; - return -EBUSY; - case CHAN_MODE_UNDEFINED: - break; - } - return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req); } @@ -1904,30 +1925,12 @@ static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) { - struct ieee80211_local *local = wiphy_priv(wiphy); - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - switch (ieee80211_get_channel_mode(local, sdata)) { - case CHAN_MODE_HOPPING: - return -EBUSY; - case CHAN_MODE_FIXED: - if (!params->channel_fixed) - return -EBUSY; - if (local->oper_channel == params->channel) - break; - return -EBUSY; - case CHAN_MODE_UNDEFINED: - break; - } - - return ieee80211_ibss_join(sdata, params); + return ieee80211_ibss_join(IEEE80211_DEV_TO_SUB_IF(dev), params); } static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - return ieee80211_ibss_leave(sdata); + return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev)); } static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) @@ -1971,9 +1974,13 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type, int mbm) { struct ieee80211_local *local = wiphy_priv(wiphy); - struct ieee80211_channel *chan = local->oper_channel; + struct ieee80211_channel *chan = local->_oper_channel; u32 changes = 0; + /* FIXME */ + if (local->use_chanctx) + return -EOPNOTSUPP; + switch (type) { case NL80211_TX_POWER_AUTOMATIC: local->user_power_level = -1; @@ -2067,13 +2074,12 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, /* * If not associated, or current association is not an HT - * association, there's no need to send an action frame. + * association, there's no need to do anything, just store + * the new value until we associate. */ if (!sdata->u.mgd.associated || - sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) { - ieee80211_recalc_smps(sdata->local); + sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) return 0; - } ap = sdata->u.mgd.associated->bssid; @@ -2189,6 +2195,9 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, lockdep_assert_held(&local->mtx); + if (local->use_chanctx && !local->ops->remain_on_channel) + return -EOPNOTSUPP; + roc = kzalloc(sizeof(*roc), GFP_KERNEL); if (!roc) return -ENOMEM; @@ -2515,10 +2524,20 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, /* Check if the operating channel is the requested channel */ if (!need_offchan) { - need_offchan = chan != local->oper_channel; - if (channel_type_valid && - channel_type != local->_oper_channel_type) + struct ieee80211_chanctx_conf *chanctx_conf; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + + if (chanctx_conf) { + need_offchan = chan != chanctx_conf->channel; + if (channel_type_valid && + channel_type != chanctx_conf->channel_type) + need_offchan = true; + } else { need_offchan = true; + } + rcu_read_unlock(); } if (need_offchan && !offchan) { @@ -2667,7 +2686,7 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) u16 capab; capab = 0; - if (local->oper_channel->band != IEEE80211_BAND_2GHZ) + if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ) return capab; if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) @@ -2699,7 +2718,7 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, u16 status_code, struct sk_buff *skb) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = sdata->local; + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_tdls_data *tf; tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); @@ -2719,10 +2738,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, tf->u.setup_req.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - ieee80211_add_srates_ie(sdata, skb, false, - local->oper_channel->band); - ieee80211_add_ext_srates_ie(sdata, skb, false, - local->oper_channel->band); + ieee80211_add_srates_ie(sdata, skb, false, band); + ieee80211_add_ext_srates_ie(sdata, skb, false, band); ieee80211_tdls_add_ext_capab(skb); break; case WLAN_TDLS_SETUP_RESPONSE: @@ -2735,10 +2752,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, tf->u.setup_resp.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - ieee80211_add_srates_ie(sdata, skb, false, - local->oper_channel->band); - ieee80211_add_ext_srates_ie(sdata, skb, false, - local->oper_channel->band); + ieee80211_add_srates_ie(sdata, skb, false, band); + ieee80211_add_ext_srates_ie(sdata, skb, false, band); ieee80211_tdls_add_ext_capab(skb); break; case WLAN_TDLS_SETUP_CONFIRM: @@ -2776,7 +2791,7 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, u16 status_code, struct sk_buff *skb) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = sdata->local; + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_mgmt *mgmt; mgmt = (void *)skb_put(skb, 24); @@ -2799,10 +2814,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, mgmt->u.action.u.tdls_discover_resp.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - ieee80211_add_srates_ie(sdata, skb, false, - local->oper_channel->band); - ieee80211_add_ext_srates_ie(sdata, skb, false, - local->oper_channel->band); + ieee80211_add_srates_ie(sdata, skb, false, band); + ieee80211_add_ext_srates_ie(sdata, skb, false, band); ieee80211_tdls_add_ext_capab(skb); break; default: @@ -2819,7 +2832,6 @@ static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; - struct ieee80211_tx_info *info; struct sk_buff *skb = NULL; bool send_direct; int ret; @@ -2845,7 +2857,6 @@ static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, if (!skb) return -ENOMEM; - info = IEEE80211_SKB_CB(skb); skb_reserve(skb, local->hw.extra_tx_headroom); switch (action_code) { @@ -2982,12 +2993,19 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, bool qos; struct ieee80211_tx_info *info; struct sta_info *sta; + struct ieee80211_chanctx_conf *chanctx_conf; + enum ieee80211_band band; rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (WARN_ON(!chanctx_conf)) { + rcu_read_unlock(); + return -EINVAL; + } + band = chanctx_conf->channel->band; sta = sta_info_get(sdata, peer); if (sta) { qos = test_sta_flag(sta, WLAN_STA_WME); - rcu_read_unlock(); } else { rcu_read_unlock(); return -ENOLINK; @@ -3005,8 +3023,10 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, } skb = dev_alloc_skb(local->hw.extra_tx_headroom + size); - if (!skb) + if (!skb) { + rcu_read_unlock(); return -ENOMEM; + } skb->dev = dev; @@ -3031,8 +3051,9 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, nullfunc->qos_ctrl = cpu_to_le16(7); local_bh_disable(); - ieee80211_xmit(sdata, skb); + ieee80211_xmit(sdata, skb, band); local_bh_enable(); + rcu_read_unlock(); *cookie = (unsigned long) skb; return 0; @@ -3042,10 +3063,19 @@ static struct ieee80211_channel * ieee80211_cfg_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_channel_type *type) { - struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *chan = NULL; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (chanctx_conf) { + *type = chanctx_conf->channel_type; + chan = chanctx_conf->channel; + } + rcu_read_unlock(); - *type = local->_oper_channel_type; - return local->oper_channel; + return chan; } #ifdef CONFIG_PM diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 0bfc914ddd15..f84b86028a9c 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -3,108 +3,10 @@ */ #include <linux/nl80211.h> +#include <linux/export.h> #include <net/cfg80211.h> #include "ieee80211_i.h" - -static enum ieee80211_chan_mode -__ieee80211_get_channel_mode(struct ieee80211_local *local, - struct ieee80211_sub_if_data *ignore) -{ - struct ieee80211_sub_if_data *sdata; - - lockdep_assert_held(&local->iflist_mtx); - - list_for_each_entry(sdata, &local->interfaces, list) { - if (sdata == ignore) - continue; - - if (!ieee80211_sdata_running(sdata)) - continue; - - switch (sdata->vif.type) { - case NL80211_IFTYPE_MONITOR: - continue; - case NL80211_IFTYPE_STATION: - if (!sdata->u.mgd.associated) - continue; - break; - case NL80211_IFTYPE_ADHOC: - if (!sdata->u.ibss.ssid_len) - continue; - if (!sdata->u.ibss.fixed_channel) - return CHAN_MODE_HOPPING; - break; - case NL80211_IFTYPE_AP_VLAN: - /* will also have _AP interface */ - continue; - case NL80211_IFTYPE_AP: - if (!sdata->u.ap.beacon) - continue; - break; - case NL80211_IFTYPE_MESH_POINT: - if (!sdata->wdev.mesh_id_len) - continue; - break; - default: - break; - } - - return CHAN_MODE_FIXED; - } - - return CHAN_MODE_UNDEFINED; -} - -enum ieee80211_chan_mode -ieee80211_get_channel_mode(struct ieee80211_local *local, - struct ieee80211_sub_if_data *ignore) -{ - enum ieee80211_chan_mode mode; - - mutex_lock(&local->iflist_mtx); - mode = __ieee80211_get_channel_mode(local, ignore); - mutex_unlock(&local->iflist_mtx); - - return mode; -} - -static enum nl80211_channel_type -ieee80211_get_superchan(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) -{ - enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT; - struct ieee80211_sub_if_data *tmp; - - mutex_lock(&local->iflist_mtx); - list_for_each_entry(tmp, &local->interfaces, list) { - if (tmp == sdata) - continue; - - if (!ieee80211_sdata_running(tmp)) - continue; - - switch (tmp->vif.bss_conf.channel_type) { - case NL80211_CHAN_NO_HT: - case NL80211_CHAN_HT20: - if (superchan > tmp->vif.bss_conf.channel_type) - break; - - superchan = tmp->vif.bss_conf.channel_type; - break; - case NL80211_CHAN_HT40PLUS: - WARN_ON(superchan == NL80211_CHAN_HT40MINUS); - superchan = NL80211_CHAN_HT40PLUS; - break; - case NL80211_CHAN_HT40MINUS: - WARN_ON(superchan == NL80211_CHAN_HT40PLUS); - superchan = NL80211_CHAN_HT40MINUS; - break; - } - } - mutex_unlock(&local->iflist_mtx); - - return superchan; -} +#include "driver-ops.h" static bool ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1, @@ -148,23 +50,350 @@ ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1, return true; } -bool ieee80211_set_channel_type(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - enum nl80211_channel_type chantype) +static void ieee80211_change_chantype(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx, + enum nl80211_channel_type chantype) { - enum nl80211_channel_type superchan; - enum nl80211_channel_type compatchan; + if (chantype == ctx->conf.channel_type) + return; - superchan = ieee80211_get_superchan(local, sdata); - if (!ieee80211_channel_types_are_compatible(superchan, chantype, - &compatchan)) - return false; + ctx->conf.channel_type = chantype; + drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE); - local->_oper_channel_type = compatchan; + if (!local->use_chanctx) { + local->_oper_channel_type = chantype; + ieee80211_hw_config(local, 0); + } +} - if (sdata) - sdata->vif.bss_conf.channel_type = chantype; +static struct ieee80211_chanctx * +ieee80211_find_chanctx(struct ieee80211_local *local, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type, + enum ieee80211_chanctx_mode mode) +{ + struct ieee80211_chanctx *ctx; + enum nl80211_channel_type compat_type; - return true; + lockdep_assert_held(&local->chanctx_mtx); + + if (mode == IEEE80211_CHANCTX_EXCLUSIVE) + return NULL; + if (WARN_ON(!channel)) + return NULL; + + list_for_each_entry(ctx, &local->chanctx_list, list) { + compat_type = ctx->conf.channel_type; + + if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) + continue; + if (ctx->conf.channel != channel) + continue; + if (!ieee80211_channel_types_are_compatible(ctx->conf.channel_type, + channel_type, + &compat_type)) + continue; + + ieee80211_change_chantype(local, ctx, compat_type); + + return ctx; + } + + return NULL; +} + +static struct ieee80211_chanctx * +ieee80211_new_chanctx(struct ieee80211_local *local, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type, + enum ieee80211_chanctx_mode mode) +{ + struct ieee80211_chanctx *ctx; + int err; + + lockdep_assert_held(&local->chanctx_mtx); + + ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->conf.channel = channel; + ctx->conf.channel_type = channel_type; + ctx->conf.rx_chains_static = 1; + ctx->conf.rx_chains_dynamic = 1; + ctx->mode = mode; + + if (!local->use_chanctx) { + local->_oper_channel_type = channel_type; + local->_oper_channel = channel; + ieee80211_hw_config(local, 0); + } else { + err = drv_add_chanctx(local, ctx); + if (err) { + kfree(ctx); + return ERR_PTR(err); + } + } + + list_add_rcu(&ctx->list, &local->chanctx_list); + + return ctx; +} + +static void ieee80211_free_chanctx(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx) +{ + lockdep_assert_held(&local->chanctx_mtx); + + WARN_ON_ONCE(ctx->refcount != 0); + + if (!local->use_chanctx) { + local->_oper_channel_type = NL80211_CHAN_NO_HT; + ieee80211_hw_config(local, 0); + } else { + drv_remove_chanctx(local, ctx); + } + + list_del_rcu(&ctx->list); + kfree_rcu(ctx, rcu_head); +} + +static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, + struct ieee80211_chanctx *ctx) +{ + struct ieee80211_local *local = sdata->local; + int ret; + + lockdep_assert_held(&local->chanctx_mtx); + + ret = drv_assign_vif_chanctx(local, sdata, ctx); + if (ret) + return ret; + + rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); + ctx->refcount++; + + return 0; +} + +static enum nl80211_channel_type +ieee80211_calc_chantype(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx) +{ + struct ieee80211_chanctx_conf *conf = &ctx->conf; + struct ieee80211_sub_if_data *sdata; + enum nl80211_channel_type result = NL80211_CHAN_NO_HT; + + lockdep_assert_held(&local->chanctx_mtx); + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (!ieee80211_sdata_running(sdata)) + continue; + if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) + continue; + + WARN_ON_ONCE(!ieee80211_channel_types_are_compatible( + sdata->vif.bss_conf.channel_type, + result, &result)); + } + rcu_read_unlock(); + + return result; +} + +static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx) +{ + enum nl80211_channel_type chantype; + + lockdep_assert_held(&local->chanctx_mtx); + + chantype = ieee80211_calc_chantype(local, ctx); + ieee80211_change_chantype(local, ctx, chantype); +} + +static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, + struct ieee80211_chanctx *ctx) +{ + struct ieee80211_local *local = sdata->local; + + lockdep_assert_held(&local->chanctx_mtx); + + ctx->refcount--; + rcu_assign_pointer(sdata->vif.chanctx_conf, NULL); + + drv_unassign_vif_chanctx(local, sdata, ctx); + + if (ctx->refcount > 0) { + ieee80211_recalc_chanctx_chantype(sdata->local, ctx); + ieee80211_recalc_smps_chanctx(local, ctx); + } +} + +static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *conf; + struct ieee80211_chanctx *ctx; + + lockdep_assert_held(&local->chanctx_mtx); + + conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + if (!conf) + return; + + ctx = container_of(conf, struct ieee80211_chanctx, conf); + + ieee80211_unassign_vif_chanctx(sdata, ctx); + if (ctx->refcount == 0) + ieee80211_free_chanctx(local, ctx); +} + +void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, + struct ieee80211_chanctx *chanctx) +{ + struct ieee80211_sub_if_data *sdata; + u8 rx_chains_static, rx_chains_dynamic; + + lockdep_assert_held(&local->chanctx_mtx); + + rx_chains_static = 1; + rx_chains_dynamic = 1; + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + u8 needed_static, needed_dynamic; + + if (!ieee80211_sdata_running(sdata)) + continue; + + if (rcu_access_pointer(sdata->vif.chanctx_conf) != + &chanctx->conf) + continue; + + switch (sdata->vif.type) { + case NL80211_IFTYPE_P2P_DEVICE: + continue; + case NL80211_IFTYPE_STATION: + if (!sdata->u.mgd.associated) + continue; + break; + case NL80211_IFTYPE_AP_VLAN: + continue; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MESH_POINT: + break; + default: + WARN_ON_ONCE(1); + } + + switch (sdata->smps_mode) { + default: + WARN_ONCE(1, "Invalid SMPS mode %d\n", + sdata->smps_mode); + /* fall through */ + case IEEE80211_SMPS_OFF: + needed_static = sdata->needed_rx_chains; + needed_dynamic = sdata->needed_rx_chains; + break; + case IEEE80211_SMPS_DYNAMIC: + needed_static = 1; + needed_dynamic = sdata->needed_rx_chains; + break; + case IEEE80211_SMPS_STATIC: + needed_static = 1; + needed_dynamic = 1; + break; + } + + rx_chains_static = max(rx_chains_static, needed_static); + rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); + } + rcu_read_unlock(); + + if (!local->use_chanctx) { + if (rx_chains_static > 1) + local->smps_mode = IEEE80211_SMPS_OFF; + else if (rx_chains_dynamic > 1) + local->smps_mode = IEEE80211_SMPS_DYNAMIC; + else + local->smps_mode = IEEE80211_SMPS_STATIC; + ieee80211_hw_config(local, 0); + } + + if (rx_chains_static == chanctx->conf.rx_chains_static && + rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) + return; + + chanctx->conf.rx_chains_static = rx_chains_static; + chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; + drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); +} + +int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type, + enum ieee80211_chanctx_mode mode) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx *ctx; + int ret; + + WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); + + mutex_lock(&local->chanctx_mtx); + __ieee80211_vif_release_channel(sdata); + + ctx = ieee80211_find_chanctx(local, channel, channel_type, mode); + if (!ctx) + ctx = ieee80211_new_chanctx(local, channel, channel_type, mode); + if (IS_ERR(ctx)) { + ret = PTR_ERR(ctx); + goto out; + } + + sdata->vif.bss_conf.channel_type = channel_type; + + ret = ieee80211_assign_vif_chanctx(sdata, ctx); + if (ret) { + /* if assign fails refcount stays the same */ + if (ctx->refcount == 0) + ieee80211_free_chanctx(local, ctx); + goto out; + } + + ieee80211_recalc_smps_chanctx(local, ctx); + out: + mutex_unlock(&local->chanctx_mtx); + return ret; +} + +void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) +{ + WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); + + mutex_lock(&sdata->local->chanctx_mtx); + __ieee80211_vif_release_channel(sdata); + mutex_unlock(&sdata->local->chanctx_mtx); +} + +void ieee80211_iter_chan_contexts_atomic( + struct ieee80211_hw *hw, + void (*iter)(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *chanctx_conf, + void *data), + void *iter_data) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_chanctx *ctx; + rcu_read_lock(); + list_for_each_entry_rcu(ctx, &local->chanctx_list, list) + iter(hw, &ctx->conf, iter_data); + rcu_read_unlock(); } +EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); diff --git a/net/mac80211/debugfs.h b/net/mac80211/debugfs.h index 9be4e6d71d00..214ed4ecd739 100644 --- a/net/mac80211/debugfs.h +++ b/net/mac80211/debugfs.h @@ -2,9 +2,9 @@ #define __MAC80211_DEBUGFS_H #ifdef CONFIG_MAC80211_DEBUGFS -extern void debugfs_hw_add(struct ieee80211_local *local); -extern int mac80211_format_buffer(char __user *userbuf, size_t count, - loff_t *ppos, char *fmt, ...); +void debugfs_hw_add(struct ieee80211_local *local); +int __printf(4, 5) mac80211_format_buffer(char __user *userbuf, size_t count, + loff_t *ppos, char *fmt, ...); #else static inline void debugfs_hw_add(struct ieee80211_local *local) { diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 6d5aec9418ee..3393ad5b8ab1 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -217,7 +217,7 @@ static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata, return snprintf(buf, buflen, "request: %s\nused: %s\n", smps_modes[sdata->u.mgd.req_smps], - smps_modes[sdata->u.mgd.ap_smps]); + smps_modes[sdata->smps_mode]); } static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, @@ -395,14 +395,14 @@ __IEEE80211_IF_FILE_W(uapsd_max_sp_len); /* AP attributes */ IEEE80211_IF_FILE(num_mcast_sta, u.ap.num_mcast_sta, ATOMIC); -IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); -IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); +IEEE80211_IF_FILE(num_sta_ps, u.ap.ps.num_sta_ps, ATOMIC); +IEEE80211_IF_FILE(dtim_count, u.ap.ps.dtim_count, DEC); static ssize_t ieee80211_if_fmt_num_buffered_multicast( const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) { return scnprintf(buf, buflen, "%u\n", - skb_queue_len(&sdata->u.ap.ps_bc_buf)); + skb_queue_len(&sdata->u.ap.ps.bc_buf)); } __IEEE80211_IF_FILE(num_buffered_multicast, NULL); @@ -471,7 +471,7 @@ IEEE80211_IF_FILE(dropped_frames_congestion, u.mesh.mshstats.dropped_frames_congestion, DEC); IEEE80211_IF_FILE(dropped_frames_no_route, u.mesh.mshstats.dropped_frames_no_route, DEC); -IEEE80211_IF_FILE(estab_plinks, u.mesh.mshstats.estab_plinks, ATOMIC); +IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC); /* Mesh parameters */ IEEE80211_IF_FILE(dot11MeshMaxRetries, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index da9003b20004..77407b31e1ff 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -871,4 +871,69 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local, local->ops->mgd_prepare_tx(&local->hw, &sdata->vif); trace_drv_return_void(local); } + +static inline int drv_add_chanctx(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx) +{ + int ret = -EOPNOTSUPP; + + trace_drv_add_chanctx(local, ctx); + if (local->ops->add_chanctx) + ret = local->ops->add_chanctx(&local->hw, &ctx->conf); + trace_drv_return_int(local, ret); + + return ret; +} + +static inline void drv_remove_chanctx(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx) +{ + trace_drv_remove_chanctx(local, ctx); + if (local->ops->remove_chanctx) + local->ops->remove_chanctx(&local->hw, &ctx->conf); + trace_drv_return_void(local); +} + +static inline void drv_change_chanctx(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx, + u32 changed) +{ + trace_drv_change_chanctx(local, ctx, changed); + if (local->ops->change_chanctx) + local->ops->change_chanctx(&local->hw, &ctx->conf, changed); + trace_drv_return_void(local); +} + +static inline int drv_assign_vif_chanctx(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_chanctx *ctx) +{ + int ret = 0; + + check_sdata_in_driver(sdata); + + trace_drv_assign_vif_chanctx(local, sdata, ctx); + if (local->ops->assign_vif_chanctx) + ret = local->ops->assign_vif_chanctx(&local->hw, + &sdata->vif, + &ctx->conf); + trace_drv_return_int(local, ret); + + return ret; +} + +static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_chanctx *ctx) +{ + check_sdata_in_driver(sdata); + + trace_drv_unassign_vif_chanctx(local, sdata, ctx); + if (local->ops->unassign_vif_chanctx) + local->ops->unassign_vif_chanctx(&local->hw, + &sdata->vif, + &ctx->conf); + trace_drv_return_void(local); +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 5f3620f0bc0a..c7386b2b767e 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -26,7 +26,6 @@ #include "rate.h" #define IEEE80211_SCAN_INTERVAL (2 * HZ) -#define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ) #define IEEE80211_IBSS_JOIN_TIMEOUT (7 * HZ) #define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ) @@ -39,7 +38,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, const u8 *bssid, const int beacon_int, struct ieee80211_channel *chan, const u32 basic_rates, - const u16 capability, u64 tsf) + const u16 capability, u64 tsf, + bool creator) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; @@ -72,25 +72,27 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, /* if merging, indicate to driver that we leave the old IBSS */ if (sdata->vif.bss_conf.ibss_joined) { sdata->vif.bss_conf.ibss_joined = false; + sdata->vif.bss_conf.ibss_creator = false; netif_carrier_off(sdata->dev); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS); } - memcpy(ifibss->bssid, bssid, ETH_ALEN); - sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; - local->oper_channel = chan; channel_type = ifibss->channel_type; if (!cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type)) channel_type = NL80211_CHAN_HT20; - if (!ieee80211_set_channel_type(local, sdata, channel_type)) { - /* can only fail due to HT40+/- mismatch */ - channel_type = NL80211_CHAN_HT20; - WARN_ON(!ieee80211_set_channel_type(local, sdata, - NL80211_CHAN_HT20)); + + ieee80211_vif_release_channel(sdata); + if (ieee80211_vif_use_channel(sdata, chan, channel_type, + ifibss->fixed_channel ? + IEEE80211_CHANCTX_SHARED : + IEEE80211_CHANCTX_EXCLUSIVE)) { + sdata_info(sdata, "Failed to join IBSS, no channel context\n"); + return; } - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); + + memcpy(ifibss->bssid, bssid, ETH_ALEN); sband = local->hw.wiphy->bands[chan->band]; @@ -197,6 +199,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, bss_change |= BSS_CHANGED_HT; bss_change |= BSS_CHANGED_IBSS; sdata->vif.bss_conf.ibss_joined = true; + sdata->vif.bss_conf.ibss_creator = creator; ieee80211_bss_info_change_notify(sdata, bss_change); ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates); @@ -249,7 +252,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, cbss->channel, basic_rates, cbss->capability, - cbss->tsf); + cbss->tsf, + false); } static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta, @@ -279,7 +283,7 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta, ibss_dbg(sdata, "TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n", sdata->vif.addr, addr, sdata->u.ibss.bssid); - ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, NULL, 0, + ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, 0, NULL, 0, addr, sdata->u.ibss.bssid, NULL, 0, 0); } return sta; @@ -294,7 +298,8 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; struct sta_info *sta; - int band = local->oper_channel->band; + struct ieee80211_chanctx_conf *chanctx_conf; + int band; /* * XXX: Consider removing the least recently used entry and @@ -317,6 +322,13 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, return NULL; } + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (WARN_ON_ONCE(!chanctx_conf)) + return NULL; + band = chanctx_conf->channel->band; + rcu_read_unlock(); + sta = sta_info_alloc(sdata, addr, GFP_KERNEL); if (!sta) { rcu_read_lock(); @@ -389,7 +401,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, * However, try to reply to authentication attempts if someone * has actually implemented this. */ - ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, NULL, 0, + ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, 0, NULL, 0, mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0); } @@ -517,7 +529,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, goto put_bss; /* different channel */ - if (cbss->channel != local->oper_channel) + if (sdata->u.ibss.fixed_channel && + sdata->u.ibss.channel != cbss->channel) goto put_bss; /* different SSID */ @@ -592,7 +605,8 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; struct sta_info *sta; - int band = local->oper_channel->band; + struct ieee80211_chanctx_conf *chanctx_conf; + int band; /* * XXX: Consider removing the least recently used entry and @@ -610,6 +624,15 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, if (!ether_addr_equal(bssid, sdata->u.ibss.bssid)) return; + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (WARN_ON_ONCE(!chanctx_conf)) { + rcu_read_unlock(); + return; + } + band = chanctx_conf->channel->band; + rcu_read_unlock(); + sta = sta_info_alloc(sdata, addr, GFP_ATOMIC); if (!sta) return; @@ -715,7 +738,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) __ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int, ifibss->channel, ifibss->basic_rates, - capability, 0); + capability, 0, true); } /* @@ -784,18 +807,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) int interval = IEEE80211_SCAN_INTERVAL; if (time_after(jiffies, ifibss->ibss_join_req + - IEEE80211_IBSS_JOIN_TIMEOUT)) { - if (!(local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS)) { - ieee80211_sta_create_ibss(sdata); - return; - } - sdata_info(sdata, "IBSS not allowed on %d MHz\n", - local->oper_channel->center_freq); - - /* No IBSS found - decrease scan interval and continue - * scanning. */ - interval = IEEE80211_SCAN_INTERVAL_SLOW; - } + IEEE80211_IBSS_JOIN_TIMEOUT)) + ieee80211_sta_create_ibss(sdata); mod_timer(&ifibss->timer, round_jiffies(jiffies + interval)); @@ -1086,17 +1099,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->u.ibss.channel_type = params->channel_type; sdata->u.ibss.fixed_channel = params->channel_fixed; - /* fix ourselves to that channel now already */ - if (params->channel_fixed) { - sdata->local->oper_channel = params->channel; - if (!ieee80211_set_channel_type(sdata->local, sdata, - params->channel_type)) { - mutex_unlock(&sdata->u.ibss.mtx); - kfree_skb(skb); - return -EINVAL; - } - } - if (params->ie) { sdata->u.ibss.ie = kmemdup(params->ie, params->ie_len, GFP_KERNEL); @@ -1108,7 +1110,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH; sdata->u.ibss.ibss_join_req = jiffies; - memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN); + memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len); sdata->u.ibss.ssid_len = params->ssid_len; mutex_unlock(&sdata->u.ibss.mtx); @@ -1134,6 +1136,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, changed |= BSS_CHANGED_HT; ieee80211_bss_info_change_notify(sdata, changed); + sdata->smps_mode = IEEE80211_SMPS_OFF; + sdata->needed_rx_chains = sdata->local->rx_chains; + ieee80211_queue_work(&sdata->local->hw, &sdata->work); return 0; @@ -1197,6 +1202,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) lockdep_is_held(&sdata->u.ibss.mtx)); RCU_INIT_POINTER(sdata->u.ibss.presp, NULL); sdata->vif.bss_conf.ibss_joined = false; + sdata->vif.bss_conf.ibss_creator = false; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_IBSS); synchronize_rcu(); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8c804550465b..3026519b236a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -280,23 +280,27 @@ struct probe_resp { u8 data[0]; }; -struct ieee80211_if_ap { - struct beacon_data __rcu *beacon; - struct probe_resp __rcu *probe_resp; - - struct list_head vlans; - +struct ps_data { /* yes, this looks ugly, but guarantees that we can later use * bitmap_empty :) * NB: don't touch this bitmap, use sta_info_{set,clear}_tim_bit */ u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)]; - struct sk_buff_head ps_bc_buf; + struct sk_buff_head bc_buf; atomic_t num_sta_ps; /* number of stations in PS mode */ - atomic_t num_mcast_sta; /* number of stations receiving multicast */ int dtim_count; bool dtim_bc_mc; }; +struct ieee80211_if_ap { + struct beacon_data __rcu *beacon; + struct probe_resp __rcu *probe_resp; + + struct list_head vlans; + + struct ps_data ps; + atomic_t num_mcast_sta; /* number of stations receiving multicast */ +}; + struct ieee80211_if_wds { struct sta_info *sta; u8 remote_addr[ETH_ALEN]; @@ -316,7 +320,6 @@ struct mesh_stats { __u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/ __u32 dropped_frames_no_route; /* Not transmitted, no route found */ __u32 dropped_frames_congestion;/* Not forwarded due to congestion */ - atomic_t estab_plinks; }; #define PREQ_Q_F_START 0x1 @@ -378,8 +381,9 @@ struct ieee80211_mgd_auth_data { u8 key_len, key_idx; bool done; - size_t ie_len; - u8 ie[]; + u16 sae_trans, sae_status; + size_t data_len; + u8 data[]; }; struct ieee80211_mgd_assoc_data { @@ -433,7 +437,6 @@ struct ieee80211_if_managed { bool powersave; /* powersave requested for this iface */ bool broken_ap; /* AP is broken -- turn off powersave */ enum ieee80211_smps_mode req_smps, /* requested smps mode */ - ap_smps, /* smps mode AP thinks we're in */ driver_smps_mode; /* smps mode request */ struct work_struct request_smps_work; @@ -599,6 +602,7 @@ struct ieee80211_if_mesh { int preq_queue_len; struct mesh_stats mshstats; struct mesh_config mshcfg; + atomic_t estab_plinks; u32 mesh_seqnum; bool accepting_plinks; int num_gates; @@ -610,7 +614,7 @@ struct ieee80211_if_mesh { IEEE80211_MESH_SEC_SECURED = 0x2, } security; /* Extensible Synchronization Framework */ - struct ieee80211_mesh_sync_ops *sync_ops; + const struct ieee80211_mesh_sync_ops *sync_ops; s64 sync_offset_clockdrift_max; spinlock_t sync_offset_lock; bool adjusting_tbtt; @@ -658,6 +662,30 @@ enum ieee80211_sdata_state_bits { SDATA_STATE_OFFCHANNEL, }; +/** + * enum ieee80211_chanctx_mode - channel context configuration mode + * + * @IEEE80211_CHANCTX_SHARED: channel context may be used by + * multiple interfaces + * @IEEE80211_CHANCTX_EXCLUSIVE: channel context can be used + * only by a single interface. This can be used for example for + * non-fixed channel IBSS. + */ +enum ieee80211_chanctx_mode { + IEEE80211_CHANCTX_SHARED, + IEEE80211_CHANCTX_EXCLUSIVE +}; + +struct ieee80211_chanctx { + struct list_head list; + struct rcu_head rcu_head; + + enum ieee80211_chanctx_mode mode; + int refcount; + + struct ieee80211_chanctx_conf conf; +}; + struct ieee80211_sub_if_data { struct list_head list; @@ -704,11 +732,17 @@ struct ieee80211_sub_if_data { struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; + /* used to reconfigure hardware SM PS */ + struct work_struct recalc_smps; + struct work_struct work; struct sk_buff_head skb_queue; bool arp_filter_state; + u8 needed_rx_chains; + enum ieee80211_smps_mode smps_mode; + /* * AP this belongs to: self in AP mode and * corresponding AP in VLAN mode, NULL for @@ -749,6 +783,21 @@ struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p) return container_of(p, struct ieee80211_sub_if_data, vif); } +static inline enum ieee80211_band +ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata) +{ + enum ieee80211_band band = IEEE80211_BAND_2GHZ; + struct ieee80211_chanctx_conf *chanctx_conf; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!WARN_ON(!chanctx_conf)) + band = chanctx_conf->channel->band; + rcu_read_unlock(); + + return band; +} + enum sdata_queue_type { IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0, IEEE80211_SDATA_QUEUE_AGG_START = 1, @@ -821,6 +870,7 @@ enum { * @SCAN_SUSPEND: Suspend the scan and go back to operating channel to * send out data * @SCAN_RESUME: Resume the scan and scan the next channel + * @SCAN_ABORT: Abort the scan and go back to operating channel */ enum mac80211_scan_state { SCAN_DECISION, @@ -828,6 +878,7 @@ enum mac80211_scan_state { SCAN_SEND_PROBE, SCAN_SUSPEND, SCAN_RESUME, + SCAN_ABORT, }; struct ieee80211_local { @@ -858,15 +909,14 @@ struct ieee80211_local { bool wiphy_ciphers_allocated; + bool use_chanctx; + /* protects the aggregated multicast list and filter calls */ spinlock_t filter_lock; /* used for uploading changed mc list */ struct work_struct reconfig_filter; - /* used to reconfigure hardware SM PS */ - struct work_struct recalc_smps; - /* aggregated multicast list */ struct netdev_hw_addr_list mc_list; @@ -903,6 +953,9 @@ struct ieee80211_local { /* wowlan is enabled -- don't reconfig on resume */ bool wowlan; + /* number of RX chains the hardware has */ + u8 rx_chains; + int tx_headroom; /* required headroom for hardware/radiotap */ /* Tasklet and skb queue to process calls from IRQ mode. All frames @@ -980,13 +1033,19 @@ struct ieee80211_local { enum mac80211_scan_state next_scan_state; struct delayed_work scan_work; struct ieee80211_sub_if_data __rcu *scan_sdata; + struct ieee80211_channel *csa_channel; + /* For backward compatibility only -- do not use */ + struct ieee80211_channel *_oper_channel; enum nl80211_channel_type _oper_channel_type; - struct ieee80211_channel *oper_channel, *csa_channel; /* Temporary remain-on-channel for off-channel operations */ struct ieee80211_channel *tmp_channel; enum nl80211_channel_type tmp_channel_type; + /* channel contexts */ + struct list_head chanctx_list; + struct mutex chanctx_mtx; + /* SNMP counters */ /* dot11CountersTable */ u32 dot11TransmittedFragmentCount; @@ -1091,6 +1150,8 @@ struct ieee80211_local { /* virtual monitor interface */ struct ieee80211_sub_if_data __rcu *monitor_sdata; + struct ieee80211_channel *monitor_channel; + enum nl80211_channel_type monitor_channel_type; }; static inline struct ieee80211_sub_if_data * @@ -1133,6 +1194,8 @@ struct ieee802_11_elems { u8 *wmm_param; struct ieee80211_ht_cap *ht_cap_elem; struct ieee80211_ht_operation *ht_operation; + struct ieee80211_vht_cap *vht_cap_elem; + struct ieee80211_vht_operation *vht_operation; struct ieee80211_meshconf_ie *mesh_config; u8 *mesh_id; u8 *peering; @@ -1359,6 +1422,13 @@ void ieee80211_ba_session_work(struct work_struct *work); void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid); void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid); +u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs); + +/* VHT */ +void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + struct ieee80211_vht_cap *vht_cap_ie, + struct ieee80211_sta_vht_cap *vht_cap); /* Spectrum management */ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, @@ -1393,11 +1463,42 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke gfp_t gfp); void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, bool bss_notify); -void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, + enum ieee80211_band band); + +void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, int tid, + enum ieee80211_band band); -void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, int tid); -static void inline ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, +static inline void +ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, int tid, + enum ieee80211_band band) +{ + rcu_read_lock(); + __ieee80211_tx_skb_tid_band(sdata, skb, tid, band); + rcu_read_unlock(); +} + +static inline void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, int tid) +{ + struct ieee80211_chanctx_conf *chanctx_conf; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (WARN_ON(!chanctx_conf)) { + rcu_read_unlock(); + kfree_skb(skb); + return; + } + + __ieee80211_tx_skb_tid_band(sdata, skb, tid, + chanctx_conf->channel->band); + rcu_read_unlock(); +} + +static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */ @@ -1444,7 +1545,7 @@ static inline void ieee80211_add_pending_skbs(struct ieee80211_local *local, } void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, - u16 transaction, u16 auth_alg, + u16 transaction, u16 auth_alg, u16 status, u8 *extra, size_t extra_len, const u8 *bssid, const u8 *da, const u8 *key, u8 key_len, u8 key_idx); void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, @@ -1464,7 +1565,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u32 ratemask, bool directed, bool no_cck, - struct ieee80211_channel *channel); + struct ieee80211_channel *channel, bool scan); void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, const size_t supp_rates_len, @@ -1474,7 +1575,7 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local, enum ieee80211_band band, u32 *basic_rates); int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps_mode); -void ieee80211_recalc_smps(struct ieee80211_local *local); +void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); size_t ieee80211_ie_split(const u8 *ies, size_t ielen, const u8 *ids, int n_ids, size_t offset); @@ -1495,21 +1596,19 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, enum ieee80211_band band); /* channel management */ -enum ieee80211_chan_mode { - CHAN_MODE_UNDEFINED, - CHAN_MODE_HOPPING, - CHAN_MODE_FIXED, -}; - -enum ieee80211_chan_mode -ieee80211_get_channel_mode(struct ieee80211_local *local, - struct ieee80211_sub_if_data *ignore); -bool ieee80211_set_channel_type(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - enum nl80211_channel_type chantype); enum nl80211_channel_type ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper); +int __must_check +ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type, + enum ieee80211_chanctx_mode mode); +void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); + +void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, + struct ieee80211_chanctx *chanctx); + #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline #else diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 6f8a73c64fb3..bc3e3e1db093 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -380,6 +380,15 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local) goto out_unlock; } + ret = ieee80211_vif_use_channel(sdata, local->monitor_channel, + local->monitor_channel_type, + IEEE80211_CHANCTX_EXCLUSIVE); + if (ret) { + drv_remove_interface(local, sdata); + kfree(sdata); + goto out_unlock; + } + rcu_assign_pointer(local->monitor_sdata, sdata); out_unlock: mutex_unlock(&local->iflist_mtx); @@ -403,6 +412,8 @@ static void ieee80211_del_virtual_monitor(struct ieee80211_local *local) rcu_assign_pointer(local->monitor_sdata, NULL); synchronize_net(); + ieee80211_vif_release_channel(sdata); + drv_remove_interface(local, sdata); kfree(sdata); @@ -665,7 +676,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, *tmp; u32 hw_reconf_flags = 0; int i; - enum nl80211_channel_type orig_ct; clear_bit(SDATA_STATE_RUNNING, &sdata->state); @@ -729,6 +739,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, del_timer_sync(&local->dynamic_ps_timer); cancel_work_sync(&local->dynamic_ps_enable_work); + cancel_work_sync(&sdata->recalc_smps); + /* APs need special treatment */ if (sdata->vif.type == NL80211_IFTYPE_AP) { struct ieee80211_sub_if_data *vlan, *tmpsdata; @@ -755,8 +767,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, WARN_ON(!list_empty(&sdata->u.ap.vlans)); /* free all potentially still buffered bcast frames */ - local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps_bc_buf); - skb_queue_purge(&sdata->u.ap.ps_bc_buf); + local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf); + skb_queue_purge(&sdata->u.ap.ps.bc_buf); } else if (sdata->vif.type == NL80211_IFTYPE_STATION) { ieee80211_mgd_stop(sdata); } @@ -837,14 +849,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, hw_reconf_flags = 0; } - /* Re-calculate channel-type, in case there are multiple vifs - * on different channel types. - */ - orig_ct = local->_oper_channel_type; - ieee80211_set_channel_type(local, NULL, NL80211_CHAN_NO_HT); - /* do after stop to avoid reconfiguring when we stop anyway */ - if (hw_reconf_flags || (orig_ct != local->_oper_channel_type)) + if (hw_reconf_flags) ieee80211_hw_config(local, hw_reconf_flags); spin_lock_irqsave(&local->queue_stop_reason_lock, flags); @@ -853,7 +859,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (info->control.vif == &sdata->vif) { __skb_unlink(skb, &local->pending[i]); - dev_kfree_skb_irq(skb); + ieee80211_free_txskb(&local->hw, skb); } } } @@ -1121,6 +1127,13 @@ static void ieee80211_iface_work(struct work_struct *work) } } +static void ieee80211_recalc_smps_work(struct work_struct *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, recalc_smps); + + ieee80211_recalc_smps(sdata); +} /* * Helper function to initialise an interface to a specific type. @@ -1149,6 +1162,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, skb_queue_head_init(&sdata->skb_queue); INIT_WORK(&sdata->work, ieee80211_iface_work); + INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work); switch (type) { case NL80211_IFTYPE_P2P_GO: @@ -1157,7 +1171,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->vif.p2p = true; /* fall through */ case NL80211_IFTYPE_AP: - skb_queue_head_init(&sdata->u.ap.ps_bc_buf); + skb_queue_head_init(&sdata->u.ap.ps.bc_buf); INIT_LIST_HEAD(&sdata->u.ap.vlans); break; case NL80211_IFTYPE_P2P_CLIENT: @@ -1282,11 +1296,6 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, if (type == ieee80211_vif_type_p2p(&sdata->vif)) return 0; - /* Setting ad-hoc mode on non-IBSS channel is not supported. */ - if (sdata->local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS && - type == NL80211_IFTYPE_ADHOC) - return -EOPNOTSUPP; - if (ieee80211_sdata_running(sdata)) { ret = ieee80211_runtime_change_iftype(sdata, type); if (ret) @@ -1298,9 +1307,6 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, } /* reset some values that shouldn't be kept across type changes */ - sdata->vif.bss_conf.basic_rates = - ieee80211_mandatory_rates(sdata->local, - sdata->local->oper_channel->band); sdata->drop_unencrypted = 0; if (type == NL80211_IFTYPE_STATION) sdata->u.mgd.use_4addr = false; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index c80c4490351c..fd8345c20051 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -93,23 +93,21 @@ static void ieee80211_reconfig_filter(struct work_struct *work) ieee80211_configure_filter(local); } -int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) +static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) { struct ieee80211_channel *chan; - int ret = 0; + u32 changed = 0; int power; enum nl80211_channel_type channel_type; u32 offchannel_flag; - might_sleep(); - offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; if (local->scan_channel) { chan = local->scan_channel; /* If scanning on oper channel, use whatever channel-type * is currently in use. */ - if (chan == local->oper_channel) + if (chan == local->_oper_channel) channel_type = local->_oper_channel_type; else channel_type = NL80211_CHAN_NO_HT; @@ -117,11 +115,11 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) chan = local->tmp_channel; channel_type = local->tmp_channel_type; } else { - chan = local->oper_channel; + chan = local->_oper_channel; channel_type = local->_oper_channel_type; } - if (chan != local->oper_channel || + if (chan != local->_oper_channel || channel_type != local->_oper_channel_type) local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; else @@ -164,6 +162,21 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) local->hw.conf.power_level = power; } + return changed; +} + +int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) +{ + int ret = 0; + + might_sleep(); + + if (!local->use_chanctx) + changed |= ieee80211_hw_conf_chan(local); + else + changed &= ~(IEEE80211_CONF_CHANGE_CHANNEL | + IEEE80211_CONF_CHANGE_POWER); + if (changed && local->open_count) { ret = drv_config(local, changed); /* @@ -359,14 +372,6 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw) } EXPORT_SYMBOL(ieee80211_restart_hw); -static void ieee80211_recalc_smps_work(struct work_struct *work) -{ - struct ieee80211_local *local = - container_of(work, struct ieee80211_local, recalc_smps); - - ieee80211_recalc_smps(local); -} - #ifdef CONFIG_INET static int ieee80211_ifa_changed(struct notifier_block *nb, unsigned long data, void *arg) @@ -540,6 +545,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, struct ieee80211_local *local; int priv_size, i; struct wiphy *wiphy; + bool use_chanctx; if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config || !ops->add_interface || !ops->remove_interface || @@ -549,6 +555,14 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove))) return NULL; + /* check all or no channel context operations exist */ + i = !!ops->add_chanctx + !!ops->remove_chanctx + + !!ops->change_chanctx + !!ops->assign_vif_chanctx + + !!ops->unassign_vif_chanctx; + if (WARN_ON(i != 0 && i != 5)) + return NULL; + use_chanctx = i == 5; + /* Ensure 32-byte alignment of our private data and hw private data. * We use the wiphy priv data for both our ieee80211_local and for * the driver's private data @@ -584,8 +598,14 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, if (ops->remain_on_channel) wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; - wiphy->features = NL80211_FEATURE_SK_TX_STATUS | - NL80211_FEATURE_HT_IBSS; + wiphy->features |= NL80211_FEATURE_SK_TX_STATUS | + NL80211_FEATURE_SAE | + NL80211_FEATURE_HT_IBSS; + + if (!ops->hw_scan) + wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | + NL80211_FEATURE_AP_SCAN; + if (!ops->set_key) wiphy->flags |= WIPHY_FLAG_IBSS_RSN; @@ -599,6 +619,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); local->ops = ops; + local->use_chanctx = use_chanctx; /* set up some defaults */ local->hw.queues = 1; @@ -626,6 +647,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, spin_lock_init(&local->filter_lock); spin_lock_init(&local->queue_stop_reason_lock); + INIT_LIST_HEAD(&local->chanctx_list); + mutex_init(&local->chanctx_mtx); + /* * The rx_skb_queue is only accessed from tasklets, * but other SKB queues are used from within IRQ @@ -641,7 +665,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, INIT_WORK(&local->restart_work, ieee80211_restart_work); INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); - INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work); local->smps_mode = IEEE80211_SMPS_OFF; INIT_WORK(&local->dynamic_ps_enable_work, @@ -719,6 +742,25 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if ((hw->flags & IEEE80211_HW_SCAN_WHILE_IDLE) && !local->ops->hw_scan) return -EINVAL; + if (!local->use_chanctx) { + for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) { + const struct ieee80211_iface_combination *comb; + + comb = &local->hw.wiphy->iface_combinations[i]; + + if (comb->num_different_channels > 1) + return -EINVAL; + } + } else { + /* + * WDS is currently prohibited when channel contexts are used + * because there's no clear definition of which channel WDS + * type interfaces use + */ + if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS)) + return -EINVAL; + } + /* Only HW csum features are currently compatible with mac80211 */ feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM; @@ -728,6 +770,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (hw->max_report_rates == 0) hw->max_report_rates = hw->max_rates; + local->rx_chains = 1; + /* * generic code guarantees at least one band, * set this very early because much code assumes @@ -743,18 +787,29 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) sband = local->hw.wiphy->bands[band]; if (!sband) continue; - if (!local->oper_channel) { + if (!local->use_chanctx && !local->_oper_channel) { /* init channel we're on */ local->hw.conf.channel = - local->oper_channel = &sband->channels[0]; + local->_oper_channel = &sband->channels[0]; local->hw.conf.channel_type = NL80211_CHAN_NO_HT; } + if (!local->monitor_channel) { + local->monitor_channel = &sband->channels[0]; + local->monitor_channel_type = NL80211_CHAN_NO_HT; + } channels += sband->n_channels; if (max_bitrates < sband->n_bitrates) max_bitrates = sband->n_bitrates; supp_ht = supp_ht || sband->ht_cap.ht_supported; supp_vht = supp_vht || sband->vht_cap.vht_supported; + + if (sband->ht_cap.ht_supported) + local->rx_chains = + max(ieee80211_mcs_to_chains(&sband->ht_cap.mcs), + local->rx_chains); + + /* TODO: consider VHT for RX chains, hopefully it's the same */ } local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + @@ -778,19 +833,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); - /* - * mac80211 doesn't support more than 1 channel, and also not more - * than one IBSS interface - */ + /* mac80211 doesn't support more than one IBSS interface right now */ for (i = 0; i < hw->wiphy->n_iface_combinations; i++) { const struct ieee80211_iface_combination *c; int j; c = &hw->wiphy->iface_combinations[i]; - if (c->num_different_channels > 1) - return -EINVAL; - for (j = 0; j < c->n_limits; j++) if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) && c->limits[j].max > 1) @@ -832,7 +881,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (supp_vht) local->scan_ies_len += - 2 + sizeof(struct ieee80211_vht_capabilities); + 2 + sizeof(struct ieee80211_vht_cap); if (!local->ops->hw_scan) { /* For hw_scan, driver needs to set these up. */ diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index ff0296c7bab8..a350cab4b339 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -97,7 +97,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth))) goto mismatch; - ieee80211_sta_get_rates(local, ie, local->oper_channel->band, + ieee80211_sta_get_rates(local, ie, ieee80211_get_sdata_band(sdata), &basic_rates); if (sdata->vif.bss_conf.basic_rates != basic_rates) @@ -264,7 +264,7 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) /* Authentication Protocol identifier */ *pos++ = ifmsh->mesh_auth_id; /* Mesh Formation Info - number of neighbors */ - neighbors = atomic_read(&ifmsh->mshstats.estab_plinks); + neighbors = atomic_read(&ifmsh->estab_plinks); /* Number of neighbor mesh STAs or 15 whichever is smaller */ neighbors = (neighbors > 15) ? 15 : neighbors; *pos++ = neighbors << 1; @@ -355,12 +355,22 @@ int mesh_add_ds_params_ie(struct sk_buff *skb, { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; - struct ieee80211_channel *chan = local->oper_channel; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *chan; u8 *pos; if (skb_tailroom(skb) < 3) return -ENOMEM; + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (WARN_ON(!chanctx_conf)) { + rcu_read_unlock(); + return -EINVAL; + } + chan = chanctx_conf->channel; + rcu_read_unlock(); + sband = local->hw.wiphy->bands[chan->band]; if (sband->band == IEEE80211_BAND_2GHZ) { pos = skb_put(skb, 2 + 1); @@ -376,10 +386,11 @@ int mesh_add_ht_cap_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_supported_band *sband; u8 *pos; - sband = local->hw.wiphy->bands[local->oper_channel->band]; + sband = local->hw.wiphy->bands[band]; if (!sband->ht_cap.ht_supported || sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) return 0; @@ -397,14 +408,26 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; - struct ieee80211_channel *channel = local->oper_channel; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *channel; enum nl80211_channel_type channel_type = - sdata->vif.bss_conf.channel_type; - struct ieee80211_supported_band *sband = - local->hw.wiphy->bands[channel->band]; - struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap; + sdata->vif.bss_conf.channel_type; + struct ieee80211_supported_band *sband; + struct ieee80211_sta_ht_cap *ht_cap; u8 *pos; + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (WARN_ON(!chanctx_conf)) { + rcu_read_unlock(); + return -EINVAL; + } + channel = chanctx_conf->channel; + rcu_read_unlock(); + + sband = local->hw.wiphy->bands[channel->band]; + ht_cap = &sband->ht_cap; + if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT) return 0; @@ -610,7 +633,7 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(sdata->local, - sdata->local->oper_channel->band); + ieee80211_get_sdata_band(sdata)); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_HT | @@ -680,8 +703,10 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, &elems); - /* ignore beacons from secure mesh peers if our security is off */ - if (elems.rsn_len && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) + /* ignore non-mesh or secure / unsecure mismatch */ + if ((!elems.mesh_id || !elems.mesh_config) || + (elems.rsn && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) || + (!elems.rsn && sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)) return; if (elems.ds_params && elems.ds_params_len == 1) @@ -694,8 +719,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) return; - if (elems.mesh_id && elems.mesh_config && - mesh_matches_local(sdata, &elems)) + if (mesh_matches_local(sdata, &elems)) mesh_neighbour_update(sdata, mgmt->sa, &elems); if (ifmsh->sync_ops) diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 25d0f17dec71..9285f3f67e66 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -256,7 +256,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); -struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method); +const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method); /* Mesh paths */ int mesh_nexthop_lookup(struct sk_buff *skb, @@ -324,7 +324,7 @@ extern int mesh_allocated; static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata) { return sdata->u.mesh.mshcfg.dot11MeshMaxPeerLinks - - atomic_read(&sdata->u.mesh.mshstats.estab_plinks); + atomic_read(&sdata->u.mesh.estab_plinks); } static inline bool mesh_plink_availables(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 3ab34d816897..234fe755968b 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -50,14 +50,14 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, static inline u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) { - atomic_inc(&sdata->u.mesh.mshstats.estab_plinks); + atomic_inc(&sdata->u.mesh.estab_plinks); return mesh_accept_plinks_update(sdata); } static inline u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) { - atomic_dec(&sdata->u.mesh.mshstats.estab_plinks); + atomic_dec(&sdata->u.mesh.estab_plinks); return mesh_accept_plinks_update(sdata); } @@ -252,6 +252,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, mgmt->u.action.u.self_prot.action_code = action; if (action != WLAN_SP_MESH_PEERING_CLOSE) { + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); + /* capability info */ pos = skb_put(skb, 2); memset(pos, 0, 2); @@ -260,10 +262,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, pos = skb_put(skb, 2); memcpy(pos + 2, &plid, 2); } - if (ieee80211_add_srates_ie(sdata, skb, true, - local->oper_channel->band) || - ieee80211_add_ext_srates_ie(sdata, skb, true, - local->oper_channel->band) || + if (ieee80211_add_srates_ie(sdata, skb, true, band) || + ieee80211_add_ext_srates_ie(sdata, skb, true, band) || mesh_add_rsn_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata)) @@ -343,7 +343,7 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *elems) { struct ieee80211_local *local = sdata->local; - enum ieee80211_band band = local->oper_channel->band; + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_supported_band *sband; u32 rates, basic_rates = 0; struct sta_info *sta; diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c index a16b7b4b1e02..407c8705e10d 100644 --- a/net/mac80211/mesh_sync.c +++ b/net/mac80211/mesh_sync.c @@ -234,49 +234,7 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata) spin_unlock_bh(&ifmsh->sync_offset_lock); } -static const u8 *mesh_get_vendor_oui(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - u8 offset; - - if (!ifmsh->ie || !ifmsh->ie_len) - return NULL; - - offset = ieee80211_ie_split_vendor(ifmsh->ie, - ifmsh->ie_len, 0); - - if (!offset) - return NULL; - - return ifmsh->ie + offset + 2; -} - -static void mesh_sync_vendor_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, - u16 stype, - struct ieee80211_mgmt *mgmt, - struct ieee802_11_elems *elems, - struct ieee80211_rx_status *rx_status) -{ - const u8 *oui; - - WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR); - msync_dbg(sdata, "called mesh_sync_vendor_rx_bcn_presp\n"); - oui = mesh_get_vendor_oui(sdata); - /* here you would implement the vendor offset tracking for this oui */ -} - -static void mesh_sync_vendor_adjust_tbtt(struct ieee80211_sub_if_data *sdata) -{ - const u8 *oui; - - WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR); - msync_dbg(sdata, "called mesh_sync_vendor_adjust_tbtt\n"); - oui = mesh_get_vendor_oui(sdata); - /* here you would implement the vendor tsf adjustment for this oui */ -} - -/* global variable */ -static struct sync_method sync_methods[] = { +static const struct sync_method sync_methods[] = { { .method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, .ops = { @@ -284,18 +242,11 @@ static struct sync_method sync_methods[] = { .adjust_tbtt = &mesh_sync_offset_adjust_tbtt, } }, - { - .method = IEEE80211_SYNC_METHOD_VENDOR, - .ops = { - .rx_bcn_presp = &mesh_sync_vendor_rx_bcn_presp, - .adjust_tbtt = &mesh_sync_vendor_adjust_tbtt, - } - }, }; -struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method) +const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method) { - struct ieee80211_mesh_sync_ops *ops = NULL; + const struct ieee80211_mesh_sync_ops *ops = NULL; u8 i; for (i = 0 ; i < ARRAY_SIZE(sync_methods); ++i) { diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e714ed8bb198..1d1fdf0791f0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -178,20 +178,30 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *chan; struct sta_info *sta; u32 changed = 0; u16 ht_opmode; bool disable_40 = false; - sband = local->hw.wiphy->bands[local->oper_channel->band]; + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (WARN_ON(!chanctx_conf)) { + rcu_read_unlock(); + return 0; + } + chan = chanctx_conf->channel; + rcu_read_unlock(); + sband = local->hw.wiphy->bands[chan->band]; switch (sdata->vif.bss_conf.channel_type) { case NL80211_CHAN_HT40PLUS: - if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40PLUS) + if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) disable_40 = true; break; case NL80211_CHAN_HT40MINUS: - if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40MINUS) + if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) disable_40 = true; break; default: @@ -343,7 +353,7 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, cap = vht_cap.cap; /* reserve and fill IE */ - pos = skb_put(skb, sizeof(struct ieee80211_vht_capabilities) + 2); + pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); } @@ -359,11 +369,21 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) int i, count, rates_len, supp_rates_len; u16 capab; struct ieee80211_supported_band *sband; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *chan; u32 rates = 0; lockdep_assert_held(&ifmgd->mtx); - sband = local->hw.wiphy->bands[local->oper_channel->band]; + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (WARN_ON(!chanctx_conf)) { + rcu_read_unlock(); + return; + } + chan = chanctx_conf->channel; + rcu_read_unlock(); + sband = local->hw.wiphy->bands[chan->band]; if (assoc_data->supp_rates_len) { /* @@ -392,7 +412,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) 4 + /* power capability */ 2 + 2 * sband->n_channels + /* supported channels */ 2 + sizeof(struct ieee80211_ht_cap) + /* HT */ - 2 + sizeof(struct ieee80211_vht_capabilities) + /* VHT */ + 2 + sizeof(struct ieee80211_vht_cap) + /* VHT */ assoc_data->ie_len + /* extra IEs */ 9, /* WMM */ GFP_KERNEL); @@ -485,7 +505,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) *pos++ = WLAN_EID_PWR_CAPABILITY; *pos++ = 2; *pos++ = 0; /* min tx power */ - *pos++ = local->oper_channel->max_power; /* max tx power */ + *pos++ = chan->max_power; /* max tx power */ /* 2. supported channels */ /* TODO: get this in reg domain format */ @@ -523,7 +543,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, - sband, local->oper_channel, ifmgd->ap_smps); + sband, chan, sdata->smps_mode); if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) ieee80211_add_vht_ie(sdata, skb, sband); @@ -657,18 +677,18 @@ static void ieee80211_chswitch_work(struct work_struct *work) if (!ifmgd->associated) goto out; - sdata->local->oper_channel = sdata->local->csa_channel; + sdata->local->_oper_channel = sdata->local->csa_channel; if (!sdata->local->ops->channel_switch) { /* call "hw_config" only if doing sw channel switch */ ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL); } else { /* update the device channel directly */ - sdata->local->hw.conf.channel = sdata->local->oper_channel; + sdata->local->hw.conf.channel = sdata->local->_oper_channel; } /* XXX: shouldn't really modify cfg80211-owned data! */ - ifmgd->associated->channel = sdata->local->oper_channel; + ifmgd->associated->channel = sdata->local->_oper_channel; /* XXX: wait for a beacon first? */ ieee80211_wake_queues_by_reason(&sdata->local->hw, @@ -680,11 +700,8 @@ static void ieee80211_chswitch_work(struct work_struct *work) void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) { - struct ieee80211_sub_if_data *sdata; - struct ieee80211_if_managed *ifmgd; - - sdata = vif_to_sdata(vif); - ifmgd = &sdata->u.mgd; + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; trace_api_chswitch_done(sdata, success); if (!success) { @@ -723,6 +740,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num, cbss->channel->band); + struct ieee80211_chanctx *chanctx; ASSERT_MGD_MTX(ifmgd); @@ -748,10 +766,34 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, return; } - sdata->local->csa_channel = new_ch; - ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; + if (sdata->local->use_chanctx) { + sdata_info(sdata, + "not handling channel switch with channel contexts\n"); + ieee80211_queue_work(&sdata->local->hw, + &ifmgd->csa_connection_drop_work); + } + + mutex_lock(&sdata->local->chanctx_mtx); + if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) { + mutex_unlock(&sdata->local->chanctx_mtx); + return; + } + chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf), + struct ieee80211_chanctx, conf); + if (chanctx->refcount > 1) { + sdata_info(sdata, + "channel switch with multiple interfaces on the same channel, disconnecting\n"); + ieee80211_queue_work(&sdata->local->hw, + &ifmgd->csa_connection_drop_work); + mutex_unlock(&sdata->local->chanctx_mtx); + return; + } + mutex_unlock(&sdata->local->chanctx_mtx); + + sdata->local->csa_channel = new_ch; + if (sw_elem->mode) ieee80211_stop_queues_by_reason(&sdata->local->hw, IEEE80211_QUEUE_STOP_REASON_CSA); @@ -1280,7 +1322,7 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, } use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME); - if (sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ) + if (ieee80211_get_sdata_band(sdata) == IEEE80211_BAND_5GHZ) use_short_slot = true; if (use_protection != bss_conf->use_cts_prot) { @@ -1350,7 +1392,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_ps(local, -1); mutex_unlock(&local->iflist_mtx); - ieee80211_recalc_smps(local); + ieee80211_recalc_smps(sdata); ieee80211_recalc_ps_vif(sdata); netif_tx_start_all_queues(sdata->dev); @@ -1465,9 +1507,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; ieee80211_bss_info_change_notify(sdata, changed); - /* channel(_type) changes are handled by ieee80211_hw_config */ - WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); - ieee80211_hw_config(local, 0); + ieee80211_vif_release_channel(sdata); /* disassociated - set to defaults now */ ieee80211_set_wmm_default(sdata, false); @@ -1589,7 +1629,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, 0, (u32) -1, true, false, - ifmgd->associated->channel); + ifmgd->associated->channel, false); } ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); @@ -1692,8 +1732,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, ssid_len = ssid[1]; skb = ieee80211_build_probe_req(sdata, cbss->bssid, - (u32) -1, - sdata->local->oper_channel, + (u32) -1, cbss->channel, ssid + 2, ssid_len, NULL, 0, true); @@ -1804,6 +1843,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, memset(sdata->u.mgd.bssid, 0, ETH_ALEN); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); + ieee80211_vif_release_channel(sdata); } cfg80211_put_bss(auth_data->bss); @@ -1824,7 +1864,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, return; auth_data->expected_transaction = 4; drv_mgd_prepare_tx(sdata->local, sdata); - ieee80211_send_auth(sdata, 3, auth_data->algorithm, + ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0, elems.challenge - 2, elems.challenge_len + 2, auth_data->bss->bssid, auth_data->bss->bssid, auth_data->key, auth_data->key_len, @@ -1858,8 +1898,13 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, status_code = le16_to_cpu(mgmt->u.auth.status_code); if (auth_alg != ifmgd->auth_data->algorithm || - auth_transaction != ifmgd->auth_data->expected_transaction) + auth_transaction != ifmgd->auth_data->expected_transaction) { + sdata_info(sdata, "%pM unexpected authentication state: alg %d (expected %d) transact %d (expected %d)\n", + mgmt->sa, auth_alg, ifmgd->auth_data->algorithm, + auth_transaction, + ifmgd->auth_data->expected_transaction); return RX_MGMT_NONE; + } if (status_code != WLAN_STATUS_SUCCESS) { sdata_info(sdata, "%pM denied authentication (status %d)\n", @@ -1872,6 +1917,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, case WLAN_AUTH_OPEN: case WLAN_AUTH_LEAP: case WLAN_AUTH_FT: + case WLAN_AUTH_SAE: break; case WLAN_AUTH_SHARED_KEY: if (ifmgd->auth_data->expected_transaction != 4) { @@ -1891,6 +1937,15 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; run_again(ifmgd, ifmgd->auth_data->timeout); + if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE && + ifmgd->auth_data->expected_transaction != 2) { + /* + * Report auth frame to user space for processing since another + * round of Authentication frames is still needed. + */ + return RX_MGMT_CFG80211_RX_AUTH; + } + /* move station state to auth */ mutex_lock(&sdata->local->sta_mtx); sta = sta_info_get(sdata, bssid); @@ -2030,6 +2085,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, memset(sdata->u.mgd.bssid, 0, ETH_ALEN); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); + ieee80211_vif_release_channel(sdata); } kfree(assoc_data); @@ -2091,7 +2147,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, return false; } - sband = local->hw.wiphy->bands[local->oper_channel->band]; + sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, @@ -2100,6 +2156,11 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, sta->supports_40mhz = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; + if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) + ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, + elems.vht_cap_elem, + &sta->sta.vht_cap); + rate_control_rate_init(sta); if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) @@ -2369,8 +2430,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, size_t baselen; struct ieee802_11_elems elems; struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *chan; u32 changed = 0; - bool erp_valid, directed_tim = false; + bool erp_valid; u8 erp_value = 0; u32 ncrc; u8 *bssid; @@ -2382,8 +2445,19 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (baselen > len) return; - if (rx_status->freq != local->oper_channel->center_freq) + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) { + rcu_read_unlock(); + return; + } + + if (rx_status->freq != chanctx_conf->channel->center_freq) { + rcu_read_unlock(); return; + } + chan = chanctx_conf->channel; + rcu_read_unlock(); if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon && ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) { @@ -2490,11 +2564,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, len - baselen, &elems, care_about_ies, ncrc); - if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) - directed_tim = ieee80211_check_tim(elems.tim, elems.tim_len, - ifmgd->aid); - if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) { + bool directed_tim = ieee80211_check_tim(elems.tim, + elems.tim_len, + ifmgd->aid); if (directed_tim) { if (local->hw.conf.dynamic_ps_timeout > 0) { if (local->hw.conf.flags & IEEE80211_CONF_PS) { @@ -2543,19 +2616,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param && - !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) { - struct ieee80211_supported_band *sband; - - sband = local->hw.wiphy->bands[local->oper_channel->band]; - + !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, bssid, true); - } if (elems.country_elem && elems.pwr_constr_elem && mgmt->u.probe_resp.capab_info & cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) - ieee80211_handle_pwr_constr(sdata, local->oper_channel, + ieee80211_handle_pwr_constr(sdata, chan, elems.country_elem, elems.country_elem_len, elems.pwr_constr_elem); @@ -2703,13 +2771,23 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) drv_mgd_prepare_tx(local, sdata); if (auth_data->bss->proberesp_ies) { + u16 trans = 1; + u16 status = 0; + sdata_info(sdata, "send auth to %pM (try %d/%d)\n", auth_data->bss->bssid, auth_data->tries, IEEE80211_AUTH_MAX_TRIES); auth_data->expected_transaction = 2; - ieee80211_send_auth(sdata, 1, auth_data->algorithm, - auth_data->ie, auth_data->ie_len, + + if (auth_data->algorithm == WLAN_AUTH_SAE) { + trans = auth_data->sae_trans; + status = auth_data->sae_status; + auth_data->expected_transaction = trans; + } + + ieee80211_send_auth(sdata, trans, auth_data->algorithm, status, + auth_data->data, auth_data->data_len, auth_data->bss->bssid, auth_data->bss->bssid, NULL, 0, 0); } else { @@ -2728,7 +2806,7 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) */ ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], NULL, 0, (u32) -1, true, false, - auth_data->bss->channel); + auth_data->bss->channel, false); } auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; @@ -3099,39 +3177,57 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ht_cfreq, ht_oper->primary_chan, cbss->channel->band); ht_oper = NULL; + } else { + channel_type = NL80211_CHAN_HT20; } } - if (ht_oper) { + if (ht_oper && sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { + /* + * cfg80211 already verified that the channel itself can + * be used, but it didn't check that we can do the right + * HT type, so do that here as well. If HT40 isn't allowed + * on this channel, disable 40 MHz operation. + */ + const u8 *ht_cap_ie; + const struct ieee80211_ht_cap *ht_cap; + u8 chains = 1; + channel_type = NL80211_CHAN_HT20; - if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { - switch (ht_oper->ht_param & - IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + if (cbss->channel->flags & IEEE80211_CHAN_NO_HT40PLUS) + ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ; + else channel_type = NL80211_CHAN_HT40PLUS; - break; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + if (cbss->channel->flags & IEEE80211_CHAN_NO_HT40MINUS) + ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ; + else channel_type = NL80211_CHAN_HT40MINUS; - break; - } + break; } - } - if (!ieee80211_set_channel_type(local, sdata, channel_type)) { - /* can only fail due to HT40+/- mismatch */ - channel_type = NL80211_CHAN_HT20; - sdata_info(sdata, - "disabling 40 MHz due to multi-vif mismatch\n"); - ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ; - WARN_ON(!ieee80211_set_channel_type(local, sdata, - channel_type)); + ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, + cbss->information_elements, + cbss->len_information_elements); + if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) { + ht_cap = (void *)(ht_cap_ie + 2); + chains = ieee80211_mcs_to_chains(&ht_cap->mcs); + } + sdata->needed_rx_chains = min(chains, local->rx_chains); + } else { + sdata->needed_rx_chains = 1; } - local->oper_channel = cbss->channel; - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); + /* will change later if needed */ + sdata->smps_mode = IEEE80211_SMPS_OFF; - return 0; + ieee80211_vif_release_channel(sdata); + return ieee80211_vif_use_channel(sdata, cbss->channel, channel_type, + IEEE80211_CHANCTX_SHARED); } static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, @@ -3201,7 +3297,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.basic_rates = basic_rates; /* cf. IEEE 802.11 9.2.12 */ - if (local->oper_channel->band == IEEE80211_BAND_2GHZ && + if (cbss->channel->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit) sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; else @@ -3263,19 +3359,33 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, case NL80211_AUTHTYPE_NETWORK_EAP: auth_alg = WLAN_AUTH_LEAP; break; + case NL80211_AUTHTYPE_SAE: + auth_alg = WLAN_AUTH_SAE; + break; default: return -EOPNOTSUPP; } - auth_data = kzalloc(sizeof(*auth_data) + req->ie_len, GFP_KERNEL); + auth_data = kzalloc(sizeof(*auth_data) + req->sae_data_len + + req->ie_len, GFP_KERNEL); if (!auth_data) return -ENOMEM; auth_data->bss = req->bss; + if (req->sae_data_len >= 4) { + __le16 *pos = (__le16 *) req->sae_data; + auth_data->sae_trans = le16_to_cpu(pos[0]); + auth_data->sae_status = le16_to_cpu(pos[1]); + memcpy(auth_data->data, req->sae_data + 4, + req->sae_data_len - 4); + auth_data->data_len += req->sae_data_len - 4; + } + if (req->ie && req->ie_len) { - memcpy(auth_data->ie, req->ie, req->ie_len); - auth_data->ie_len = req->ie_len; + memcpy(&auth_data->data[auth_data->data_len], + req->ie, req->ie_len); + auth_data->data_len += req->ie_len; } if (req->key && req->key_len) { @@ -3442,11 +3552,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { if (ifmgd->powersave) - ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC; + sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; else - ifmgd->ap_smps = IEEE80211_SMPS_OFF; + sdata->smps_mode = IEEE80211_SMPS_OFF; } else - ifmgd->ap_smps = ifmgd->req_smps; + sdata->smps_mode = ifmgd->req_smps; assoc_data->capability = req->bss->capability; assoc_data->wmm = bss->wmm_used && @@ -3549,6 +3659,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; + bool tx = !req->local_state_change; mutex_lock(&ifmgd->mtx); @@ -3565,12 +3676,12 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, if (ifmgd->associated && ether_addr_equal(ifmgd->associated->bssid, req->bssid)) { ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, - req->reason_code, true, frame_buf); + req->reason_code, tx, frame_buf); } else { drv_mgd_prepare_tx(sdata->local, sdata); ieee80211_send_deauth_disassoc(sdata, req->bssid, IEEE80211_STYPE_DEAUTH, - req->reason_code, true, + req->reason_code, tx, frame_buf); } diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 83608ac16780..c349f3aaf59e 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -107,6 +107,9 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, { struct ieee80211_sub_if_data *sdata; + if (WARN_ON(local->use_chanctx)) + return; + /* * notify the AP about us leaving the channel and stop all * STA interfaces. @@ -145,6 +148,9 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, { struct ieee80211_sub_if_data *sdata; + if (WARN_ON(local->use_chanctx)) + return; + mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) @@ -193,7 +199,8 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc) if (roc->mgmt_tx_cookie) { if (!WARN_ON(!roc->frame)) { - ieee80211_tx_skb(roc->sdata, roc->frame); + ieee80211_tx_skb_tid_band(roc->sdata, roc->frame, 7, + roc->chan->band); roc->frame = NULL; } } else { diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 5c572e7a1a71..9f404ac901ab 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -135,6 +135,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); + /* the interface is leaving the channel and is removed */ + ieee80211_vif_release_channel(sdata); drv_remove_interface(local, sdata); } diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index 10de668eb9f6..ec198ef6aa8a 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -52,11 +52,21 @@ static inline void rate_control_rate_init(struct sta_info *sta) struct ieee80211_sta *ista = &sta->sta; void *priv_sta = sta->rate_ctrl_priv; struct ieee80211_supported_band *sband; + struct ieee80211_chanctx_conf *chanctx_conf; if (!ref) return; - sband = local->hw.wiphy->bands[local->oper_channel->band]; + rcu_read_lock(); + + chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf); + if (WARN_ON(!chanctx_conf)) { + rcu_read_unlock(); + return; + } + + sband = local->hw.wiphy->bands[chanctx_conf->channel->band]; + rcu_read_unlock(); ref->ops->rate_init(ref->priv, sband, ista, priv_sta); set_sta_flag(sta, WLAN_STA_RATE_CONTROL); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 61c621e9273f..8c1f1527d671 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -531,6 +531,11 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) if (ieee80211_is_action(hdr->frame_control)) { u8 category; + + /* make sure category field is present */ + if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE) + return RX_DROP_MONITOR; + mgmt = (struct ieee80211_mgmt *)hdr; category = mgmt->u.action.category; if (category != WLAN_CATEGORY_MESH_ACTION && @@ -883,14 +888,16 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) */ if (rx->sta && rx->sdata->vif.type == NL80211_IFTYPE_STATION && ieee80211_is_data_present(hdr->frame_control)) { - u16 ethertype; - u8 *payload; - - payload = rx->skb->data + - ieee80211_hdrlen(hdr->frame_control); - ethertype = (payload[6] << 8) | payload[7]; - if (cpu_to_be16(ethertype) == - rx->sdata->control_port_protocol) + unsigned int hdrlen; + __be16 ethertype; + + hdrlen = ieee80211_hdrlen(hdr->frame_control); + + if (rx->skb->len < hdrlen + 8) + return RX_DROP_MONITOR; + + skb_copy_bits(rx->skb, hdrlen + 6, ðertype, 2); + if (ethertype == rx->sdata->control_port_protocol) return RX_CONTINUE; } @@ -1141,12 +1148,19 @@ ieee80211_rx_h_check_more_data(struct ieee80211_rx_data *rx) return RX_CONTINUE; } -static void ap_sta_ps_start(struct sta_info *sta) +static void sta_ps_start(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; + struct ps_data *ps; - atomic_inc(&sdata->bss->num_sta_ps); + if (sta->sdata->vif.type == NL80211_IFTYPE_AP || + sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + ps = &sdata->bss->ps; + else + return; + + atomic_inc(&ps->num_sta_ps); set_sta_flag(sta, WLAN_STA_PS_STA); if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); @@ -1154,7 +1168,7 @@ static void ap_sta_ps_start(struct sta_info *sta) sta->sta.addr, sta->sta.aid); } -static void ap_sta_ps_end(struct sta_info *sta) +static void sta_ps_end(struct sta_info *sta) { ps_dbg(sta->sdata, "STA %pM aid %d exits power save mode\n", sta->sta.addr, sta->sta.aid); @@ -1181,9 +1195,9 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start) return -EINVAL; if (start) - ap_sta_ps_start(sta_inf); + sta_ps_start(sta_inf); else - ap_sta_ps_end(sta_inf); + sta_ps_end(sta_inf); return 0; } @@ -1335,10 +1349,10 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) */ if (ieee80211_is_data(hdr->frame_control) && !ieee80211_has_pm(hdr->frame_control)) - ap_sta_ps_end(sta); + sta_ps_end(sta); } else { if (ieee80211_has_pm(hdr->frame_control)) - ap_sta_ps_start(sta); + sta_ps_start(sta); } } @@ -1384,9 +1398,7 @@ ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata, struct sk_buff **skb) { struct ieee80211_fragment_entry *entry; - int idx; - idx = sdata->fragment_next; entry = &sdata->fragments[sdata->fragment_next++]; if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX) sdata->fragment_next = 0; @@ -1462,11 +1474,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) hdr = (struct ieee80211_hdr *)rx->skb->data; fc = hdr->frame_control; + + if (ieee80211_is_ctl(fc)) + return RX_CONTINUE; + sc = le16_to_cpu(hdr->seq_ctrl); frag = sc & IEEE80211_SCTL_FRAG; if (likely((!ieee80211_has_morefrags(fc) && frag == 0) || - (rx->skb)->len < 24 || is_multicast_ether_addr(hdr->addr1))) { /* not fragmented */ goto out; @@ -1889,6 +1904,20 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) hdr = (struct ieee80211_hdr *) skb->data; hdrlen = ieee80211_hdrlen(hdr->frame_control); + + /* make sure fixed part of mesh header is there, also checks skb len */ + if (!pskb_may_pull(rx->skb, hdrlen + 6)) + return RX_DROP_MONITOR; + + mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); + + /* make sure full mesh header is there, also checks skb len */ + if (!pskb_may_pull(rx->skb, + hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr))) + return RX_DROP_MONITOR; + + /* reload pointers */ + hdr = (struct ieee80211_hdr *) skb->data; mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); /* frame is in RMC, don't forward */ @@ -1897,7 +1926,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) mesh_rmc_check(hdr->addr3, mesh_hdr, rx->sdata)) return RX_DROP_MONITOR; - if (!ieee80211_is_data(hdr->frame_control)) + if (!ieee80211_is_data(hdr->frame_control) || + !(status->rx_flags & IEEE80211_RX_RA_MATCH)) return RX_CONTINUE; if (!mesh_hdr->ttl) @@ -1911,9 +1941,12 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) if (is_multicast_ether_addr(hdr->addr1)) { mpp_addr = hdr->addr3; proxied_addr = mesh_hdr->eaddr1; - } else { + } else if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6) { + /* has_a4 already checked in ieee80211_rx_mesh_check */ mpp_addr = hdr->addr4; proxied_addr = mesh_hdr->eaddr2; + } else { + return RX_DROP_MONITOR; } rcu_read_lock(); @@ -1941,12 +1974,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) } skb_set_queue_mapping(skb, q); - if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) - goto out; - if (!--mesh_hdr->ttl) { IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl); - return RX_DROP_MONITOR; + goto out; } if (!ifmsh->mshcfg.dot11MeshForwarding) @@ -2353,6 +2383,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) } break; case WLAN_CATEGORY_SELF_PROTECTED: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.self_prot.action_code))) + break; + switch (mgmt->u.action.u.self_prot.action_code) { case WLAN_SP_MESH_PEERING_OPEN: case WLAN_SP_MESH_PEERING_CLOSE: @@ -2371,6 +2405,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) } break; case WLAN_CATEGORY_MESH_ACTION: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.mesh_action.action_code))) + break; + if (!ieee80211_vif_is_mesh(&sdata->vif)) break; if (mesh_action_is_path_sel(mgmt) && @@ -2913,10 +2951,15 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc)) local->dot11ReceivedFragmentCount++; - if (ieee80211_is_mgmt(fc)) - err = skb_linearize(skb); - else + if (ieee80211_is_mgmt(fc)) { + /* drop frame if too short for header */ + if (skb->len < ieee80211_hdrlen(fc)) + err = -ENOBUFS; + else + err = skb_linearize(skb); + } else { err = !pskb_may_pull(skb, ieee80211_hdrlen(fc)); + } if (err) { dev_kfree_skb(skb); @@ -3010,8 +3053,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) WARN_ON_ONCE(softirq_count() == 0); - if (WARN_ON(status->band < 0 || - status->band >= IEEE80211_NUM_BANDS)) + if (WARN_ON(status->band >= IEEE80211_NUM_BANDS)) goto drop; sband = local->hw.wiphy->bands[status->band]; @@ -3056,8 +3098,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) * hardware error. The driver should catch hardware * errors. */ - if (WARN((status->rate_idx < 0 || - status->rate_idx > 76), + if (WARN(status->rate_idx > 76, "Rate marked as an HT rate but passed " "status->rate_idx is not " "an MCS index [0-76]: %d (0x%02x)\n", @@ -3065,8 +3106,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) status->rate_idx)) goto drop; } else { - if (WARN_ON(status->rate_idx < 0 || - status->rate_idx >= sband->n_bitrates)) + if (WARN_ON(status->rate_idx >= sband->n_bitrates)) goto drop; rate = &sband->bitrates[status->rate_idx]; } diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index c4cdbde24fd3..13d23299e696 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -336,6 +336,10 @@ EXPORT_SYMBOL(ieee80211_scan_completed); static int ieee80211_start_sw_scan(struct ieee80211_local *local) { + /* Software scan is not supported in multi-channel cases */ + if (local->use_chanctx) + return -EOPNOTSUPP; + /* * Hardware/driver doesn't support hw_scan, so use software * scanning instead. First send a nullfunc frame with power save @@ -417,7 +421,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, local->scan_req->ie, local->scan_req->ie_len, local->scan_req->rates[band], false, local->scan_req->no_cck, - local->hw.conf.channel); + local->hw.conf.channel, true); /* * After sending probe requests, wait for probe responses @@ -462,6 +466,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, sizeof(*local->hw_scan_req) + req->n_channels * sizeof(req->channels[0]); local->hw_scan_req->ie = ies; + local->hw_scan_req->flags = req->flags; local->hw_scan_band = 0; @@ -480,7 +485,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, if (local->ops->hw_scan) { __set_bit(SCAN_HW_SCANNING, &local->scanning); } else if ((req->n_channels == 1) && - (req->channels[0] == local->oper_channel)) { + (req->channels[0] == local->_oper_channel)) { /* * If we are scanning only on the operating channel * then we do not need to stop normal activities @@ -562,6 +567,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, unsigned long min_beacon_int = 0; struct ieee80211_sub_if_data *sdata; struct ieee80211_channel *next_chan; + enum mac80211_scan_state next_scan_state; /* * check if at least one STA interface is associated, @@ -620,10 +626,18 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, usecs_to_jiffies(min_beacon_int * 1024) * local->hw.conf.listen_interval); - if (associated && (!tx_empty || bad_latency || listen_int_exceeded)) - local->next_scan_state = SCAN_SUSPEND; - else - local->next_scan_state = SCAN_SET_CHANNEL; + if (associated && !tx_empty) { + if (local->scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) + next_scan_state = SCAN_ABORT; + else + next_scan_state = SCAN_SUSPEND; + } else if (associated && (bad_latency || listen_int_exceeded)) { + next_scan_state = SCAN_SUSPEND; + } else { + next_scan_state = SCAN_SET_CHANNEL; + } + + local->next_scan_state = next_scan_state; *next_delay = 0; } @@ -794,6 +808,9 @@ void ieee80211_scan_work(struct work_struct *work) case SCAN_RESUME: ieee80211_scan_state_resume(local, &next_delay); break; + case SCAN_ABORT: + aborted = true; + goto out_complete; } } while (next_delay == 0); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 797dd36a220d..f7bb54f9ab72 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -98,6 +98,7 @@ static void free_sta_work(struct work_struct *wk) struct tid_ampdu_tx *tid_tx; struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; + struct ps_data *ps; /* * At this point, when being called as call_rcu callback, @@ -107,11 +108,15 @@ static void free_sta_work(struct work_struct *wk) */ if (test_sta_flag(sta, WLAN_STA_PS_STA)) { - BUG_ON(!sdata->bss); + if (sta->sdata->vif.type == NL80211_IFTYPE_AP || + sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + ps = &sdata->bss->ps; + else + return; clear_sta_flag(sta, WLAN_STA_PS_STA); - atomic_dec(&sdata->bss->num_sta_ps); + atomic_dec(&ps->num_sta_ps); sta_info_recalc_tim(sta); } @@ -502,22 +507,22 @@ int sta_info_insert(struct sta_info *sta) return err; } -static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid) +static inline void __bss_tim_set(u8 *tim, u16 id) { /* * This format has been mandated by the IEEE specifications, * so this line may not be changed to use the __set_bit() format. */ - bss->tim[aid / 8] |= (1 << (aid % 8)); + tim[id / 8] |= (1 << (id % 8)); } -static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid) +static inline void __bss_tim_clear(u8 *tim, u16 id) { /* * This format has been mandated by the IEEE specifications, * so this line may not be changed to use the __clear_bit() format. */ - bss->tim[aid / 8] &= ~(1 << (aid % 8)); + tim[id / 8] &= ~(1 << (id % 8)); } static unsigned long ieee80211_tids_for_ac(int ac) @@ -541,14 +546,23 @@ static unsigned long ieee80211_tids_for_ac(int ac) void sta_info_recalc_tim(struct sta_info *sta) { struct ieee80211_local *local = sta->local; - struct ieee80211_if_ap *bss = sta->sdata->bss; + struct ps_data *ps; unsigned long flags; bool indicate_tim = false; u8 ignore_for_tim = sta->sta.uapsd_queues; int ac; + u16 id; + + if (sta->sdata->vif.type == NL80211_IFTYPE_AP || + sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { + if (WARN_ON_ONCE(!sta->sdata->bss)) + return; - if (WARN_ON_ONCE(!sta->sdata->bss)) + ps = &sta->sdata->bss->ps; + id = sta->sta.aid; + } else { return; + } /* No need to do anything if the driver does all */ if (local->hw.flags & IEEE80211_HW_AP_LINK_PS) @@ -587,9 +601,9 @@ void sta_info_recalc_tim(struct sta_info *sta) spin_lock_irqsave(&local->tim_lock, flags); if (indicate_tim) - __bss_tim_set(bss, sta->sta.aid); + __bss_tim_set(ps->tim, id); else - __bss_tim_clear(bss, sta->sta.aid); + __bss_tim_clear(ps->tim, id); if (local->ops->set_tim) { local->tim_in_locked_section = true; @@ -650,7 +664,7 @@ static bool sta_info_cleanup_expire_buffered_ac(struct ieee80211_local *local, */ if (!skb) break; - dev_kfree_skb(skb); + ieee80211_free_txskb(&local->hw, skb); } /* @@ -679,7 +693,7 @@ static bool sta_info_cleanup_expire_buffered_ac(struct ieee80211_local *local, local->total_ps_buffered--; ps_dbg(sta->sdata, "Buffered frame expired (STA %pM)\n", sta->sta.addr); - dev_kfree_skb(skb); + ieee80211_free_txskb(&local->hw, skb); } /* @@ -893,8 +907,8 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, continue; if (time_after(jiffies, sta->last_rx + exp_time)) { - ibss_dbg(sdata, "expiring inactive STA %pM\n", - sta->sta.addr); + sta_dbg(sta->sdata, "expiring inactive STA %pM\n", + sta->sta.addr); WARN_ON(__sta_info_destroy(sta)); } } @@ -948,10 +962,17 @@ static void clear_sta_ps_flags(void *_sta) { struct sta_info *sta = _sta; struct ieee80211_sub_if_data *sdata = sta->sdata; + struct ps_data *ps; + + if (sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + ps = &sdata->bss->ps; + else + return; clear_sta_flag(sta, WLAN_STA_PS_DRIVER); if (test_and_clear_sta_flag(sta, WLAN_STA_PS_STA)) - atomic_dec(&sdata->bss->num_sta_ps); + atomic_dec(&ps->num_sta_ps); } /* powersave support code */ @@ -1008,6 +1029,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, __le16 fc; bool qos = test_sta_flag(sta, WLAN_STA_WME); struct ieee80211_tx_info *info; + struct ieee80211_chanctx_conf *chanctx_conf; if (qos) { fc = cpu_to_le16(IEEE80211_FTYPE_DATA | @@ -1057,7 +1079,16 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false); - ieee80211_xmit(sdata, skb); + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (WARN_ON(!chanctx_conf)) { + rcu_read_unlock(); + kfree_skb(skb); + return; + } + + ieee80211_xmit(sdata, skb, chanctx_conf->channel->band); + rcu_read_unlock(); } static void diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 3af0cc4130f1..21fa5c72ea14 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -189,30 +189,31 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) } if (ieee80211_is_action(mgmt->frame_control) && - sdata->vif.type == NL80211_IFTYPE_STATION && mgmt->u.action.category == WLAN_CATEGORY_HT && - mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) { + mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS && + sdata->vif.type == NL80211_IFTYPE_STATION && + ieee80211_sdata_running(sdata)) { /* * This update looks racy, but isn't -- if we come * here we've definitely got a station that we're * talking to, and on a managed interface that can * only be the AP. And the only other place updating - * this variable is before we're associated. + * this variable in managed mode is before association. */ switch (mgmt->u.action.u.ht_smps.smps_control) { case WLAN_HT_SMPS_CONTROL_DYNAMIC: - sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_DYNAMIC; + sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; break; case WLAN_HT_SMPS_CONTROL_STATIC: - sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_STATIC; + sdata->smps_mode = IEEE80211_SMPS_STATIC; break; case WLAN_HT_SMPS_CONTROL_DISABLED: default: /* shouldn't happen since we don't send that */ - sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_OFF; + sdata->smps_mode = IEEE80211_SMPS_OFF; break; } - ieee80211_queue_work(&local->hw, &local->recalc_smps); + ieee80211_queue_work(&local->hw, &sdata->recalc_smps); } } diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 18d9c8a52e9e..0638541b625f 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -28,6 +28,20 @@ #define VIF_PR_FMT " vif:%s(%d%s)" #define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" +#define CHANCTX_ENTRY __field(int, freq) \ + __field(int, chantype) \ + __field(u8, rx_chains_static) \ + __field(u8, rx_chains_dynamic) +#define CHANCTX_ASSIGN __entry->freq = ctx->conf.channel->center_freq; \ + __entry->chantype = ctx->conf.channel_type; \ + __entry->rx_chains_static = ctx->conf.rx_chains_static; \ + __entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic +#define CHANCTX_PR_FMT " freq:%d MHz chantype:%d chains:%d/%d" +#define CHANCTX_PR_ARG __entry->freq, __entry->chantype, \ + __entry->rx_chains_static, __entry->rx_chains_dynamic + + + /* * Tracing for driver callbacks. */ @@ -301,20 +315,33 @@ TRACE_EVENT(drv_bss_info_changed, TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY + __field(u32, changed) __field(bool, assoc) + __field(bool, ibss_joined) + __field(bool, ibss_creator) __field(u16, aid) __field(bool, cts) __field(bool, shortpre) __field(bool, shortslot) + __field(bool, enable_beacon) __field(u8, dtimper) __field(u16, bcnint) __field(u16, assoc_cap) __field(u64, sync_tsf) __field(u32, sync_device_ts) __field(u32, basic_rates) - __field(u32, changed) - __field(bool, enable_beacon) + __array(int, mcast_rate, IEEE80211_NUM_BANDS) __field(u16, ht_operation_mode) + __field(s32, cqm_rssi_thold); + __field(s32, cqm_rssi_hyst); + __field(u32, channel_type); + __dynamic_array(u32, arp_addr_list, info->arp_addr_cnt); + __field(bool, arp_filter_enabled); + __field(bool, qos); + __field(bool, idle); + __field(bool, ps); + __dynamic_array(u8, ssid, info->ssid_len); + __field(bool, hidden_ssid); ), TP_fast_assign( @@ -323,17 +350,32 @@ TRACE_EVENT(drv_bss_info_changed, __entry->changed = changed; __entry->aid = info->aid; __entry->assoc = info->assoc; + __entry->ibss_joined = info->ibss_joined; + __entry->ibss_creator = info->ibss_creator; __entry->shortpre = info->use_short_preamble; __entry->cts = info->use_cts_prot; __entry->shortslot = info->use_short_slot; + __entry->enable_beacon = info->enable_beacon; __entry->dtimper = info->dtim_period; __entry->bcnint = info->beacon_int; __entry->assoc_cap = info->assoc_capability; __entry->sync_tsf = info->sync_tsf; __entry->sync_device_ts = info->sync_device_ts; __entry->basic_rates = info->basic_rates; - __entry->enable_beacon = info->enable_beacon; + memcpy(__entry->mcast_rate, info->mcast_rate, + sizeof(__entry->mcast_rate)); __entry->ht_operation_mode = info->ht_operation_mode; + __entry->cqm_rssi_thold = info->cqm_rssi_thold; + __entry->cqm_rssi_hyst = info->cqm_rssi_hyst; + __entry->channel_type = info->channel_type; + memcpy(__get_dynamic_array(arp_addr_list), info->arp_addr_list, + sizeof(u32) * info->arp_addr_cnt); + __entry->arp_filter_enabled = info->arp_filter_enabled; + __entry->qos = info->qos; + __entry->idle = info->idle; + __entry->ps = info->ps; + memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len); + __entry->hidden_ssid = info->hidden_ssid; ), TP_printk( @@ -1256,6 +1298,104 @@ DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx, TP_ARGS(local, sdata) ); +DECLARE_EVENT_CLASS(local_chanctx, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx), + + TP_ARGS(local, ctx), + + TP_STRUCT__entry( + LOCAL_ENTRY + CHANCTX_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + CHANCTX_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT CHANCTX_PR_FMT, + LOCAL_PR_ARG, CHANCTX_PR_ARG + ) +); + +DEFINE_EVENT(local_chanctx, drv_add_chanctx, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx), + TP_ARGS(local, ctx) +); + +DEFINE_EVENT(local_chanctx, drv_remove_chanctx, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx), + TP_ARGS(local, ctx) +); + +TRACE_EVENT(drv_change_chanctx, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx, + u32 changed), + + TP_ARGS(local, ctx, changed), + + TP_STRUCT__entry( + LOCAL_ENTRY + CHANCTX_ENTRY + __field(u32, changed) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + CHANCTX_ASSIGN; + __entry->changed = changed; + ), + + TP_printk( + LOCAL_PR_FMT CHANCTX_PR_FMT " changed:%#x", + LOCAL_PR_ARG, CHANCTX_PR_ARG, __entry->changed + ) +); + +DECLARE_EVENT_CLASS(local_sdata_chanctx, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_chanctx *ctx), + + TP_ARGS(local, sdata, ctx), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + CHANCTX_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + CHANCTX_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT CHANCTX_PR_FMT, + LOCAL_PR_ARG, VIF_PR_ARG, CHANCTX_PR_ARG + ) +); + +DEFINE_EVENT(local_sdata_chanctx, drv_assign_vif_chanctx, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_chanctx *ctx), + TP_ARGS(local, sdata, ctx) +); + +DEFINE_EVENT(local_sdata_chanctx, drv_unassign_vif_chanctx, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_chanctx *ctx), + TP_ARGS(local, sdata, ctx) +); + /* * Tracing for API calls that drivers call. */ diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index c9bf83f36657..065f81cb5618 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -324,22 +324,20 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) struct ieee80211_sub_if_data *sdata; struct sta_info *sta; - /* - * virtual interfaces are protected by RCU - */ - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - struct ieee80211_if_ap *ap; - if (sdata->vif.type != NL80211_IFTYPE_AP) + struct ps_data *ps; + + if (sdata->vif.type == NL80211_IFTYPE_AP) + ps = &sdata->u.ap.ps; + else continue; - ap = &sdata->u.ap; - skb = skb_dequeue(&ap->ps_bc_buf); + + skb = skb_dequeue(&ps->bc_buf); if (skb) { purged++; dev_kfree_skb(skb); } - total += skb_queue_len(&ap->ps_bc_buf); + total += skb_queue_len(&ps->bc_buf); } /* @@ -360,8 +358,6 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) } } - rcu_read_unlock(); - local->total_ps_buffered = total; ps_dbg_hw(&local->hw, "PS buffers full - purged %d frames\n", purged); } @@ -371,6 +367,7 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; + struct ps_data *ps; /* * broadcast/multicast frame @@ -380,16 +377,24 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx) * This is done either by the hardware or us. */ - /* powersaving STAs only in AP/VLAN mode */ - if (!tx->sdata->bss) + /* powersaving STAs currently only in AP/VLAN mode */ + if (tx->sdata->vif.type == NL80211_IFTYPE_AP || + tx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { + if (!tx->sdata->bss) + return TX_CONTINUE; + + ps = &tx->sdata->bss->ps; + } else { return TX_CONTINUE; + } + /* no buffering for ordered frames */ if (ieee80211_has_order(hdr->frame_control)) return TX_CONTINUE; /* no stations in PS mode */ - if (!atomic_read(&tx->sdata->bss->num_sta_ps)) + if (!atomic_read(&ps->num_sta_ps)) return TX_CONTINUE; info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM; @@ -404,14 +409,14 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx) if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) purge_old_ps_buffers(tx->local); - if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= AP_MAX_BC_BUFFER) { + if (skb_queue_len(&ps->bc_buf) >= AP_MAX_BC_BUFFER) { ps_dbg(tx->sdata, "BC TX buffer full - dropping the oldest frame\n"); - dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf)); + dev_kfree_skb(skb_dequeue(&ps->bc_buf)); } else tx->local->total_ps_buffered++; - skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb); + skb_queue_tail(&ps->bc_buf, tx->skb); return TX_QUEUED; } @@ -951,7 +956,6 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) fragnum = 0; skb_queue_walk(&tx->skbs, skb) { - int next_len; const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS); hdr = (void *)skb->data; @@ -970,7 +974,6 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE; } else { hdr->frame_control &= ~morefrags; - next_len = 0; } hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG); fragnum++; @@ -1372,7 +1375,8 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) * Returns false if the frame couldn't be transmitted but was queued instead. */ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, bool txpending) + struct sk_buff *skb, bool txpending, + enum ieee80211_band band) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_data tx; @@ -1386,20 +1390,18 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, return true; } - rcu_read_lock(); - /* initialises tx */ led_len = skb->len; res_prepare = ieee80211_tx_prepare(sdata, &tx, skb); if (unlikely(res_prepare == TX_DROP)) { ieee80211_free_txskb(&local->hw, skb); - goto out; + return true; } else if (unlikely(res_prepare == TX_QUEUED)) { - goto out; + return true; } - info->band = local->hw.conf.channel->band; + info->band = band; /* set up hw_queue value early */ if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) || @@ -1410,8 +1412,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, if (!invoke_tx_handlers(&tx)) result = __ieee80211_tx(local, &tx.skbs, led_len, tx.sta, txpending); - out: - rcu_read_unlock(); + return result; } @@ -1446,7 +1447,8 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata, return 0; } -void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, + enum ieee80211_band band) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1454,8 +1456,6 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) int headroom; bool may_encrypt; - rcu_read_lock(); - may_encrypt = !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT); headroom = local->tx_headroom; @@ -1466,7 +1466,6 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) if (ieee80211_skb_resize(sdata, skb, headroom, may_encrypt)) { ieee80211_free_txskb(&local->hw, skb); - rcu_read_unlock(); return; } @@ -1478,13 +1477,11 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) !is_multicast_ether_addr(hdr->addr1) && mesh_nexthop_resolve(skb, sdata)) { /* skb queued: don't free */ - rcu_read_unlock(); return; } ieee80211_set_qos_hdr(sdata, skb); - ieee80211_tx(sdata, skb, false); - rcu_read_unlock(); + ieee80211_tx(sdata, skb, false, band); } static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb) @@ -1574,7 +1571,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_channel *chan = local->hw.conf.channel; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *chan; struct ieee80211_radiotap_header *prthdr = (struct ieee80211_radiotap_header *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1583,26 +1581,6 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, u16 len_rthdr; int hdrlen; - /* - * Frame injection is not allowed if beaconing is not allowed - * or if we need radar detection. Beaconing is usually not allowed when - * the mode or operation (Adhoc, AP, Mesh) does not support DFS. - * Passive scan is also used in world regulatory domains where - * your country is not known and as such it should be treated as - * NO TX unless the channel is explicitly allowed in which case - * your current regulatory domain would not have the passive scan - * flag. - * - * Since AP mode uses monitor interfaces to inject/TX management - * frames we can make AP mode the exception to this rule once it - * supports radar detection as its implementation can deal with - * radar detection by itself. We can do that later by adding a - * monitor flag interfaces used for AP support. - */ - if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_PASSIVE_SCAN))) - goto fail; - /* check for not even having the fixed radiotap header part */ if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) goto fail; /* too short to be possibly valid */ @@ -1688,11 +1666,45 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, } } - ieee80211_xmit(sdata, skb); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) { + tmp_sdata = rcu_dereference(local->monitor_sdata); + if (tmp_sdata) + chanctx_conf = + rcu_dereference(tmp_sdata->vif.chanctx_conf); + } + if (!chanctx_conf) + goto fail_rcu; + + chan = chanctx_conf->channel; + + /* + * Frame injection is not allowed if beaconing is not allowed + * or if we need radar detection. Beaconing is usually not allowed when + * the mode or operation (Adhoc, AP, Mesh) does not support DFS. + * Passive scan is also used in world regulatory domains where + * your country is not known and as such it should be treated as + * NO TX unless the channel is explicitly allowed in which case + * your current regulatory domain would not have the passive scan + * flag. + * + * Since AP mode uses monitor interfaces to inject/TX management + * frames we can make AP mode the exception to this rule once it + * supports radar detection as its implementation can deal with + * radar detection by itself. We can do that later by adding a + * monitor flag interfaces used for AP support. + */ + if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_PASSIVE_SCAN))) + goto fail_rcu; + + ieee80211_xmit(sdata, skb, chan->band); rcu_read_unlock(); return NETDEV_TX_OK; +fail_rcu: + rcu_read_unlock(); fail: dev_kfree_skb(skb); return NETDEV_TX_OK; /* meaning, we dealt with the skb */ @@ -1734,6 +1746,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, bool multicast; u32 info_flags = 0; u16 info_id = 0; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_sub_if_data *ap_sdata; + enum ieee80211_band band; if (unlikely(skb->len < ETH_HLEN)) goto fail; @@ -1743,9 +1758,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, ethertype = (skb->data[12] << 8) | skb->data[13]; fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); + rcu_read_lock(); + switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: - rcu_read_lock(); sta = rcu_dereference(sdata->u.vlan.sta); if (sta) { fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); @@ -1758,7 +1774,12 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); wme_sta = test_sta_flag(sta, WLAN_STA_WME); } - rcu_read_unlock(); + ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, + u.ap); + chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf); + if (!chanctx_conf) + goto fail_rcu; + band = chanctx_conf->channel->band; if (sta) break; /* fall through */ @@ -1769,6 +1790,11 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); hdrlen = 24; + if (sdata->vif.type == NL80211_IFTYPE_AP) + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) + goto fail_rcu; + band = chanctx_conf->channel->band; break; case NL80211_IFTYPE_WDS: fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); @@ -1778,15 +1804,20 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, memcpy(hdr.addr3, skb->data, ETH_ALEN); memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); hdrlen = 30; + /* + * This is the exception! WDS style interfaces are prohibited + * when channel contexts are in used so this must be valid + */ + band = local->hw.conf.channel->band; break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: if (!sdata->u.mesh.mshcfg.dot11MeshTTL) { /* Do not send frames with mesh_ttl == 0 */ sdata->u.mesh.mshstats.dropped_frames_ttl++; - goto fail; + goto fail_rcu; } - rcu_read_lock(); + if (!is_multicast_ether_addr(skb->data)) { mpath = mesh_path_lookup(skb->data, sdata); if (!mpath) @@ -1803,7 +1834,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, !(mppath && !ether_addr_equal(mppath->mpp, skb->data))) { hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc, skb->data, skb->data + ETH_ALEN); - rcu_read_unlock(); meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata, NULL, NULL); } else { @@ -1819,7 +1849,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, mesh_da = mppath->mpp; else if (mpath) mesh_da = mpath->dst; - rcu_read_unlock(); hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc, mesh_da, sdata->vif.addr); @@ -1839,13 +1868,16 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, skb->data + ETH_ALEN); } + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) + goto fail_rcu; + band = chanctx_conf->channel->band; break; #endif case NL80211_IFTYPE_STATION: if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { bool tdls_peer = false; - rcu_read_lock(); sta = sta_info_get(sdata, skb->data); if (sta) { authorized = test_sta_flag(sta, @@ -1856,7 +1888,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, tdls_auth = test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); } - rcu_read_unlock(); /* * If the TDLS link is enabled, send everything @@ -1871,7 +1902,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, if (tdls_direct) { /* link during setup - throw out frames to peer */ if (!tdls_auth) - goto fail; + goto fail_rcu; /* DA SA BSSID */ memcpy(hdr.addr1, skb->data, ETH_ALEN); @@ -1896,6 +1927,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, memcpy(hdr.addr3, skb->data, ETH_ALEN); hdrlen = 24; } + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) + goto fail_rcu; + band = chanctx_conf->channel->band; break; case NL80211_IFTYPE_ADHOC: /* DA SA BSSID */ @@ -1903,9 +1938,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN); hdrlen = 24; + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) + goto fail_rcu; + band = chanctx_conf->channel->band; break; default: - goto fail; + goto fail_rcu; } /* @@ -1915,13 +1954,11 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, */ multicast = is_multicast_ether_addr(hdr.addr1); if (!multicast) { - rcu_read_lock(); sta = sta_info_get(sdata, hdr.addr1); if (sta) { authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); wme_sta = test_sta_flag(sta, WLAN_STA_WME); } - rcu_read_unlock(); } /* For mesh, the use of the QoS header is mandatory */ @@ -1949,7 +1986,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, I802_DEBUG_INC(local->tx_handlers_drop_unauth_port); - goto fail; + goto fail_rcu; } if (unlikely(!multicast && skb->sk && @@ -2004,7 +2041,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, kfree_skb(tmp_skb); if (!skb) - goto fail; + goto fail_rcu; } hdr.frame_control = fc; @@ -2052,7 +2089,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, head_need = max_t(int, 0, head_need); if (ieee80211_skb_resize(sdata, skb, head_need, true)) { ieee80211_free_txskb(&local->hw, skb); - return NETDEV_TX_OK; + goto fail_rcu; } } @@ -2104,10 +2141,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, info->flags = info_flags; info->ack_frame_id = info_id; - ieee80211_xmit(sdata, skb); + ieee80211_xmit(sdata, skb, band); + rcu_read_unlock(); return NETDEV_TX_OK; + fail_rcu: + rcu_read_unlock(); fail: dev_kfree_skb(skb); return NETDEV_TX_OK; @@ -2139,11 +2179,18 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, struct sta_info *sta; struct ieee80211_hdr *hdr; bool result; + struct ieee80211_chanctx_conf *chanctx_conf; sdata = vif_to_sdata(info->control.vif); if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) { - result = ieee80211_tx(sdata, skb, true); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (unlikely(!chanctx_conf)) { + dev_kfree_skb(skb); + return true; + } + result = ieee80211_tx(sdata, skb, true, + chanctx_conf->channel->band); } else { struct sk_buff_head skbs; @@ -2211,9 +2258,8 @@ void ieee80211_tx_pending(unsigned long data) /* functions for drivers to get certain frames */ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, - struct ieee80211_if_ap *bss, - struct sk_buff *skb, - struct beacon_data *beacon) + struct ps_data *ps, + struct sk_buff *skb) { u8 *pos, *tim; int aid0 = 0; @@ -2221,27 +2267,27 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, /* Generate bitmap for TIM only if there are any STAs in power save * mode. */ - if (atomic_read(&bss->num_sta_ps) > 0) + if (atomic_read(&ps->num_sta_ps) > 0) /* in the hope that this is faster than * checking byte-for-byte */ - have_bits = !bitmap_empty((unsigned long*)bss->tim, + have_bits = !bitmap_empty((unsigned long*)ps->tim, IEEE80211_MAX_AID+1); - if (bss->dtim_count == 0) - bss->dtim_count = sdata->vif.bss_conf.dtim_period - 1; + if (ps->dtim_count == 0) + ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1; else - bss->dtim_count--; + ps->dtim_count--; tim = pos = (u8 *) skb_put(skb, 6); *pos++ = WLAN_EID_TIM; *pos++ = 4; - *pos++ = bss->dtim_count; + *pos++ = ps->dtim_count; *pos++ = sdata->vif.bss_conf.dtim_period; - if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf)) + if (ps->dtim_count == 0 && !skb_queue_empty(&ps->bc_buf)) aid0 = 1; - bss->dtim_bc_mc = aid0 == 1; + ps->dtim_bc_mc = aid0 == 1; if (have_bits) { /* Find largest even number N1 so that bits numbered 1 through @@ -2249,14 +2295,14 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, * (N2 + 1) x 8 through 2007 are 0. */ n1 = 0; for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) { - if (bss->tim[i]) { + if (ps->tim[i]) { n1 = i & 0xfe; break; } } n2 = n1; for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) { - if (bss->tim[i]) { + if (ps->tim[i]) { n2 = i; break; } @@ -2266,7 +2312,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, *pos++ = n1 | aid0; /* Part Virt Bitmap */ skb_put(skb, n2 - n1); - memcpy(pos, bss->tim + n1, n2 - n1 + 1); + memcpy(pos, ps->tim + n1, n2 - n1 + 1); tim[1] = n2 - n1 + 4; } else { @@ -2283,16 +2329,16 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, struct sk_buff *skb = NULL; struct ieee80211_tx_info *info; struct ieee80211_sub_if_data *sdata = NULL; - struct ieee80211_if_ap *ap = NULL; - struct beacon_data *beacon; - enum ieee80211_band band = local->oper_channel->band; + enum ieee80211_band band; struct ieee80211_tx_rate_control txrc; + struct ieee80211_chanctx_conf *chanctx_conf; rcu_read_lock(); sdata = vif_to_sdata(vif); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (!ieee80211_sdata_running(sdata)) + if (!ieee80211_sdata_running(sdata) || !chanctx_conf) goto out; if (tim_offset) @@ -2301,8 +2347,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, *tim_length = 0; if (sdata->vif.type == NL80211_IFTYPE_AP) { - ap = &sdata->u.ap; - beacon = rcu_dereference(ap->beacon); + struct ieee80211_if_ap *ap = &sdata->u.ap; + struct beacon_data *beacon = rcu_dereference(ap->beacon); + if (beacon) { /* * headroom, head length, @@ -2326,14 +2373,12 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, * of the tim bitmap in mac80211 and the driver. */ if (local->tim_in_locked_section) { - ieee80211_beacon_add_tim(sdata, ap, skb, - beacon); + ieee80211_beacon_add_tim(sdata, &ap->ps, skb); } else { unsigned long flags; spin_lock_irqsave(&local->tim_lock, flags); - ieee80211_beacon_add_tim(sdata, ap, skb, - beacon); + ieee80211_beacon_add_tim(sdata, &ap->ps, skb); spin_unlock_irqrestore(&local->tim_lock, flags); } @@ -2409,6 +2454,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, *pos++ = WLAN_EID_SSID; *pos++ = 0x0; + band = chanctx_conf->channel->band; + if (ieee80211_add_srates_ie(sdata, skb, true, band) || mesh_add_ds_params_ie(skb, sdata) || ieee80211_add_ext_srates_ie(sdata, skb, true, band) || @@ -2426,6 +2473,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, goto out; } + band = chanctx_conf->channel->band; + info = IEEE80211_SKB_CB(skb); info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; @@ -2653,29 +2702,40 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, struct sk_buff *skb = NULL; struct ieee80211_tx_data tx; struct ieee80211_sub_if_data *sdata; - struct ieee80211_if_ap *bss = NULL; - struct beacon_data *beacon; + struct ps_data *ps; struct ieee80211_tx_info *info; + struct ieee80211_chanctx_conf *chanctx_conf; sdata = vif_to_sdata(vif); - bss = &sdata->u.ap; rcu_read_lock(); - beacon = rcu_dereference(bss->beacon); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (sdata->vif.type != NL80211_IFTYPE_AP || !beacon || !beacon->head) + if (!chanctx_conf) goto out; - if (bss->dtim_count != 0 || !bss->dtim_bc_mc) + if (sdata->vif.type == NL80211_IFTYPE_AP) { + struct beacon_data *beacon = + rcu_dereference(sdata->u.ap.beacon); + + if (!beacon || !beacon->head) + goto out; + + ps = &sdata->u.ap.ps; + } else { + goto out; + } + + if (ps->dtim_count != 0 || !ps->dtim_bc_mc) goto out; /* send buffered bc/mc only after DTIM beacon */ while (1) { - skb = skb_dequeue(&bss->ps_bc_buf); + skb = skb_dequeue(&ps->bc_buf); if (!skb) goto out; local->total_ps_buffered--; - if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) { + if (!skb_queue_empty(&ps->bc_buf) && skb->len >= 2) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; /* more buffered multicast/broadcast frames ==> set @@ -2693,7 +2753,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, info = IEEE80211_SKB_CB(skb); tx.flags |= IEEE80211_TX_PS_BUFFERED; - info->band = local->oper_channel->band; + info->band = chanctx_conf->channel->band; if (invoke_tx_handlers(&tx)) skb = NULL; @@ -2704,8 +2764,9 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_get_buffered_bc); -void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, int tid) +void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, int tid, + enum ieee80211_band band) { int ac = ieee802_1d_to_ac[tid & 7]; @@ -2722,6 +2783,6 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, * requirements are that we do not come into tx with bhs on. */ local_bh_disable(); - ieee80211_xmit(sdata, skb); + ieee80211_xmit(sdata, skb, band); local_bh_enable(); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 22ca35054dd0..9556391b05d7 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -406,7 +406,7 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local, int queue = info->hw_queue; if (WARN_ON(!info->control.vif)) { - kfree_skb(skb); + ieee80211_free_txskb(&local->hw, skb); return; } @@ -431,7 +431,7 @@ void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (WARN_ON(!info->control.vif)) { - kfree_skb(skb); + ieee80211_free_txskb(&local->hw, skb); continue; } @@ -643,13 +643,41 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, break; } - if (id != WLAN_EID_VENDOR_SPECIFIC && - id != WLAN_EID_QUIET && - test_bit(id, seen_elems)) { - elems->parse_error = true; - left -= elen; - pos += elen; - continue; + switch (id) { + case WLAN_EID_SSID: + case WLAN_EID_SUPP_RATES: + case WLAN_EID_FH_PARAMS: + case WLAN_EID_DS_PARAMS: + case WLAN_EID_CF_PARAMS: + case WLAN_EID_TIM: + case WLAN_EID_IBSS_PARAMS: + case WLAN_EID_CHALLENGE: + case WLAN_EID_RSN: + case WLAN_EID_ERP_INFO: + case WLAN_EID_EXT_SUPP_RATES: + case WLAN_EID_HT_CAPABILITY: + case WLAN_EID_HT_OPERATION: + case WLAN_EID_VHT_CAPABILITY: + case WLAN_EID_VHT_OPERATION: + case WLAN_EID_MESH_ID: + case WLAN_EID_MESH_CONFIG: + case WLAN_EID_PEER_MGMT: + case WLAN_EID_PREQ: + case WLAN_EID_PREP: + case WLAN_EID_PERR: + case WLAN_EID_RANN: + case WLAN_EID_CHANNEL_SWITCH: + case WLAN_EID_EXT_CHANSWITCH_ANN: + case WLAN_EID_COUNTRY: + case WLAN_EID_PWR_CONSTRAINT: + case WLAN_EID_TIMEOUT_INTERVAL: + if (test_bit(id, seen_elems)) { + elems->parse_error = true; + left -= elen; + pos += elen; + continue; + } + break; } if (calc_crc && id < 64 && (filter & (1ULL << id))) @@ -741,6 +769,18 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, else elem_parse_failed = true; break; + case WLAN_EID_VHT_CAPABILITY: + if (elen >= sizeof(struct ieee80211_vht_cap)) + elems->vht_cap_elem = (void *)pos; + else + elem_parse_failed = true; + break; + case WLAN_EID_VHT_OPERATION: + if (elen >= sizeof(struct ieee80211_vht_operation)) + elems->vht_operation = (void *)pos; + else + elem_parse_failed = true; + break; case WLAN_EID_MESH_ID: elems->mesh_id = pos; elems->mesh_id_len = elen; @@ -809,7 +849,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, if (elem_parse_failed) elems->parse_error = true; else - set_bit(id, seen_elems); + __set_bit(id, seen_elems); left -= elen; pos += elen; @@ -832,6 +872,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_queue_params qparam; + struct ieee80211_chanctx_conf *chanctx_conf; int ac; bool use_11b, enable_qos; int aCWmin, aCWmax; @@ -844,8 +885,12 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, memset(&qparam, 0, sizeof(qparam)); - use_11b = (local->oper_channel->band == IEEE80211_BAND_2GHZ) && + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + use_11b = (chanctx_conf && + chanctx_conf->channel->band == IEEE80211_BAND_2GHZ) && !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE); + rcu_read_unlock(); /* * By default disable QoS in STA mode for old access points, which do @@ -924,7 +969,7 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, const size_t supp_rates_len, const u8 *supp_rates) { - struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *chanctx_conf; int i, have_higher_than_11mbit = 0; /* cf. IEEE 802.11 9.2.12 */ @@ -932,11 +977,16 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, if ((supp_rates[i] & 0x7f) * 5 > 110) have_higher_than_11mbit = 1; - if (local->oper_channel->band == IEEE80211_BAND_2GHZ && + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + + if (chanctx_conf && + chanctx_conf->channel->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit) sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; else sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; + rcu_read_unlock(); ieee80211_set_wmm_default(sdata, true); } @@ -968,7 +1018,7 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local, } void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, - u16 transaction, u16 auth_alg, + u16 transaction, u16 auth_alg, u16 status, u8 *extra, size_t extra_len, const u8 *da, const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx) { @@ -993,7 +1043,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, memcpy(mgmt->bssid, bssid, ETH_ALEN); mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); - mgmt->u.auth.status_code = cpu_to_le16(0); + mgmt->u.auth.status_code = cpu_to_le16(status); if (extra) memcpy(skb_put(skb, extra_len), extra, extra_len); @@ -1206,7 +1256,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u32 ratemask, bool directed, bool no_cck, - struct ieee80211_channel *channel) + struct ieee80211_channel *channel, bool scan) { struct sk_buff *skb; @@ -1217,7 +1267,10 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, if (no_cck) IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_NO_CCK_RATE; - ieee80211_tx_skb(sdata, skb); + if (scan) + ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band); + else + ieee80211_tx_skb(sdata, skb); } } @@ -1280,6 +1333,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) { struct ieee80211_hw *hw = &local->hw; struct ieee80211_sub_if_data *sdata; + struct ieee80211_chanctx *ctx; struct sta_info *sta; int res, i; @@ -1352,6 +1406,12 @@ int ieee80211_reconfig(struct ieee80211_local *local) res = drv_add_interface(local, sdata); } + /* add channel contexts */ + mutex_lock(&local->chanctx_mtx); + list_for_each_entry(ctx, &local->chanctx_list, list) + WARN_ON(drv_add_chanctx(local, ctx)); + mutex_unlock(&local->chanctx_mtx); + /* add STAs back */ mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { @@ -1392,11 +1452,22 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* Finally also reconfigure all the BSS information */ list_for_each_entry(sdata, &local->interfaces, list) { + struct ieee80211_chanctx_conf *ctx_conf; u32 changed; if (!ieee80211_sdata_running(sdata)) continue; + mutex_lock(&local->chanctx_mtx); + ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + if (ctx_conf) { + ctx = container_of(ctx_conf, struct ieee80211_chanctx, + conf); + drv_assign_vif_chanctx(local, sdata, ctx); + } + mutex_unlock(&local->chanctx_mtx); + /* common change flags for all interface types */ changed = BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE | @@ -1587,68 +1658,24 @@ void ieee80211_resume_disconnect(struct ieee80211_vif *vif) } EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); -static int check_mgd_smps(struct ieee80211_if_managed *ifmgd, - enum ieee80211_smps_mode *smps_mode) -{ - if (ifmgd->associated) { - *smps_mode = ifmgd->ap_smps; - - if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) { - if (ifmgd->powersave) - *smps_mode = IEEE80211_SMPS_DYNAMIC; - else - *smps_mode = IEEE80211_SMPS_OFF; - } - - return 1; - } - - return 0; -} - -void ieee80211_recalc_smps(struct ieee80211_local *local) +void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata) { - struct ieee80211_sub_if_data *sdata; - enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF; - int count = 0; - - mutex_lock(&local->iflist_mtx); - - /* - * This function could be improved to handle multiple - * interfaces better, but right now it makes any - * non-station interfaces force SM PS to be turned - * off. If there are multiple station interfaces it - * could also use the best possible mode, e.g. if - * one is in static and the other in dynamic then - * dynamic is ok. - */ - - list_for_each_entry(sdata, &local->interfaces, list) { - if (!ieee80211_sdata_running(sdata)) - continue; - if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) - continue; - if (sdata->vif.type != NL80211_IFTYPE_STATION) - goto set; + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_chanctx *chanctx; - count += check_mgd_smps(&sdata->u.mgd, &smps_mode); + mutex_lock(&local->chanctx_mtx); - if (count > 1) { - smps_mode = IEEE80211_SMPS_OFF; - break; - } - } + chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); - if (smps_mode == local->smps_mode) + if (WARN_ON_ONCE(!chanctx_conf)) goto unlock; - set: - local->smps_mode = smps_mode; - /* changed flag is auto-detected for this */ - ieee80211_hw_config(local, 0); + chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); + ieee80211_recalc_smps_chanctx(local, chanctx); unlock: - mutex_unlock(&local->iflist_mtx); + mutex_unlock(&local->chanctx_mtx); } static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) @@ -1788,8 +1815,8 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, __le32 tmp; *pos++ = WLAN_EID_VHT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_vht_capabilities); - memset(pos, 0, sizeof(struct ieee80211_vht_capabilities)); + *pos++ = sizeof(struct ieee80211_vht_cap); + memset(pos, 0, sizeof(struct ieee80211_vht_cap)); /* capability flags */ tmp = cpu_to_le32(cap); @@ -1947,3 +1974,19 @@ int ieee80211_ave_rssi(struct ieee80211_vif *vif) return ifmgd->ave_beacon_signal; } EXPORT_SYMBOL_GPL(ieee80211_ave_rssi); + +u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) +{ + if (!mcs) + return 1; + + /* TODO: consider rx_highest */ + + if (mcs->rx_mask[3]) + return 4; + if (mcs->rx_mask[2]) + return 3; + if (mcs->rx_mask[1]) + return 2; + return 1; +} diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c new file mode 100644 index 000000000000..f311388aeedf --- /dev/null +++ b/net/mac80211/vht.c @@ -0,0 +1,35 @@ +/* + * VHT handling + * + * 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. + */ + +#include <linux/ieee80211.h> +#include <linux/export.h> +#include <net/mac80211.h> +#include "ieee80211_i.h" + + +void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + struct ieee80211_vht_cap *vht_cap_ie, + struct ieee80211_sta_vht_cap *vht_cap) +{ + if (WARN_ON_ONCE(!vht_cap)) + return; + + memset(vht_cap, 0, sizeof(*vht_cap)); + + if (!vht_cap_ie || !sband->vht_cap.vht_supported) + return; + + vht_cap->vht_supported = true; + + vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info); + + /* Copy peer MCS info, the driver might need them. */ + memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, + sizeof(struct ieee80211_vht_mcs_info)); +} diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index bdb53aba888e..8bd2f5c6a56e 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -106,7 +106,8 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) if (status->flag & RX_FLAG_MMIC_ERROR) goto mic_fail; - if (!(status->flag & RX_FLAG_IV_STRIPPED) && rx->key) + if (!(status->flag & RX_FLAG_IV_STRIPPED) && rx->key && + rx->key->conf.cipher == WLAN_CIPHER_SUITE_TKIP) goto update_iv; return RX_CONTINUE; @@ -545,14 +546,19 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) static void bip_aad(struct sk_buff *skb, u8 *aad) { + __le16 mask_fc; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + /* BIP AAD: FC(masked) || A1 || A2 || A3 */ /* FC type/subtype */ - aad[0] = skb->data[0]; /* Mask FC Retry, PwrMgt, MoreData flags to zero */ - aad[1] = skb->data[1] & ~(BIT(4) | BIT(5) | BIT(6)); + mask_fc = hdr->frame_control; + mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY | IEEE80211_FCTL_PM | + IEEE80211_FCTL_MOREDATA); + put_unaligned(mask_fc, (__le16 *) &aad[0]); /* A1 || A2 || A3 */ - memcpy(aad + 2, skb->data + 4, 3 * ETH_ALEN); + memcpy(aad + 2, &hdr->addr1, 3 * ETH_ALEN); } diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 1b30b0dee708..962795e839ab 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -753,7 +753,8 @@ static int callforward_do_filter(const union nf_inet_addr *src, flowi4_to_flowi(&fl1), false)) { if (!afinfo->route(&init_net, (struct dst_entry **)&rt2, flowi4_to_flowi(&fl2), false)) { - if (rt1->rt_gateway == rt2->rt_gateway && + if (rt_nexthop(rt1, fl1.daddr) == + rt_nexthop(rt2, fl2.daddr) && rt1->dst.dev == rt2->dst.dev) ret = 1; dst_release(&rt2->dst); diff --git a/net/nfc/Kconfig b/net/nfc/Kconfig index 8d8d9bc4b6ff..60c3bbb63e8e 100644 --- a/net/nfc/Kconfig +++ b/net/nfc/Kconfig @@ -3,8 +3,8 @@ # menuconfig NFC - depends on NET && EXPERIMENTAL - tristate "NFC subsystem support (EXPERIMENTAL)" + depends on NET + tristate "NFC subsystem support" default n help Say Y here if you want to build support for NFC (Near field diff --git a/net/nfc/core.c b/net/nfc/core.c index 479bee36dc3e..aa64ea441676 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -40,6 +40,9 @@ int nfc_devlist_generation; DEFINE_MUTEX(nfc_devlist_mutex); +/* NFC device ID bitmap */ +static DEFINE_IDA(nfc_index_ida); + /** * nfc_dev_up - turn on the NFC device * @@ -181,6 +184,7 @@ int nfc_stop_poll(struct nfc_dev *dev) dev->ops->stop_poll(dev); dev->polling = false; + dev->rf_mode = NFC_RF_NONE; error: device_unlock(&dev->dev); @@ -194,7 +198,7 @@ static struct nfc_target *nfc_find_target(struct nfc_dev *dev, u32 target_idx) if (dev->n_targets == 0) return NULL; - for (i = 0; i < dev->n_targets ; i++) { + for (i = 0; i < dev->n_targets; i++) { if (dev->targets[i].idx == target_idx) return &dev->targets[i]; } @@ -274,12 +278,14 @@ int nfc_dep_link_down(struct nfc_dev *dev) if (!rc) { dev->dep_link_up = false; dev->active_target = NULL; + dev->rf_mode = NFC_RF_NONE; nfc_llcp_mac_is_down(dev); nfc_genl_dep_link_down_event(dev); } error: device_unlock(&dev->dev); + return rc; } @@ -503,6 +509,7 @@ EXPORT_SYMBOL(nfc_tm_activated); int nfc_tm_deactivated(struct nfc_dev *dev) { dev->dep_link_up = false; + dev->rf_mode = NFC_RF_NONE; return nfc_genl_tm_deactivated(dev); } @@ -697,6 +704,8 @@ static void nfc_check_pres_work(struct work_struct *work) if (dev->active_target && timer_pending(&dev->check_pres_timer) == 0) { rc = dev->ops->check_presence(dev, dev->active_target); + if (rc == -EOPNOTSUPP) + goto exit; if (!rc) { mod_timer(&dev->check_pres_timer, jiffies + msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); @@ -708,6 +717,7 @@ static void nfc_check_pres_work(struct work_struct *work) } } +exit: device_unlock(&dev->dev); } @@ -753,7 +763,6 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, u32 supported_protocols, int tx_headroom, int tx_tailroom) { - static atomic_t dev_no = ATOMIC_INIT(0); struct nfc_dev *dev; if (!ops->start_poll || !ops->stop_poll || !ops->activate_target || @@ -767,11 +776,6 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, if (!dev) return NULL; - dev->dev.class = &nfc_class; - dev->idx = atomic_inc_return(&dev_no) - 1; - dev_set_name(&dev->dev, "nfc%d", dev->idx); - device_initialize(&dev->dev); - dev->ops = ops; dev->supported_protocols = supported_protocols; dev->tx_headroom = tx_headroom; @@ -779,6 +783,7 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, nfc_genl_data_init(&dev->genl_data); + dev->rf_mode = NFC_RF_NONE; /* first generation must not be 0 */ dev->targets_generation = 1; @@ -806,6 +811,14 @@ int nfc_register_device(struct nfc_dev *dev) pr_debug("dev_name=%s\n", dev_name(&dev->dev)); + dev->idx = ida_simple_get(&nfc_index_ida, 0, 0, GFP_KERNEL); + if (dev->idx < 0) + return dev->idx; + + dev->dev.class = &nfc_class; + dev_set_name(&dev->dev, "nfc%d", dev->idx); + device_initialize(&dev->dev); + mutex_lock(&nfc_devlist_mutex); nfc_devlist_generation++; rc = device_add(&dev->dev); @@ -834,10 +847,12 @@ EXPORT_SYMBOL(nfc_register_device); */ void nfc_unregister_device(struct nfc_dev *dev) { - int rc; + int rc, id; pr_debug("dev_name=%s\n", dev_name(&dev->dev)); + id = dev->idx; + mutex_lock(&nfc_devlist_mutex); nfc_devlist_generation++; @@ -856,6 +871,8 @@ void nfc_unregister_device(struct nfc_dev *dev) pr_debug("The userspace won't be notified that the device %s was removed\n", dev_name(&dev->dev)); + ida_simple_remove(&nfc_index_ida, id); + } EXPORT_SYMBOL(nfc_unregister_device); diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c index 71c6a7086b8f..07659cfd6d7b 100644 --- a/net/nfc/hci/command.c +++ b/net/nfc/hci/command.c @@ -257,16 +257,16 @@ static u8 nfc_hci_create_pipe(struct nfc_hci_dev *hdev, u8 dest_host, *result = nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE, NFC_HCI_ADM_CREATE_PIPE, (u8 *) ¶ms, sizeof(params), &skb); - if (*result == 0) { - resp = (struct hci_create_pipe_resp *)skb->data; - pipe = resp->pipe; - kfree_skb(skb); + if (*result < 0) + return NFC_HCI_INVALID_PIPE; - pr_debug("pipe created=%d\n", pipe); + resp = (struct hci_create_pipe_resp *)skb->data; + pipe = resp->pipe; + kfree_skb(skb); - return pipe; - } else - return NFC_HCI_INVALID_PIPE; + pr_debug("pipe created=%d\n", pipe); + + return pipe; } static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe) @@ -279,8 +279,6 @@ static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe) static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev) { - int r; - u8 param[2]; /* TODO: Find out what the identity reference data is @@ -288,10 +286,8 @@ static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev) pr_debug("\n"); - r = nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE, - NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL); - - return 0; + return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE, + NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL); } int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate) diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 5fbb6e40793e..bc571b0efb92 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -65,8 +65,9 @@ static void nfc_hci_msg_tx_work(struct work_struct *work) -ETIME); kfree(hdev->cmd_pending_msg); hdev->cmd_pending_msg = NULL; - } else + } else { goto exit; + } } next_msg: @@ -182,7 +183,7 @@ static u32 nfc_hci_sak_to_protocol(u8 sak) } } -static int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate) +int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate) { struct nfc_target *targets; struct sk_buff *atqa_skb = NULL; @@ -263,7 +264,9 @@ static int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate) break; } - targets->hci_reader_gate = gate; + /* if driver set the new gate, we will skip the old one */ + if (targets->hci_reader_gate == 0x00) + targets->hci_reader_gate = gate; r = nfc_targets_found(hdev->ndev, targets, 1); @@ -275,6 +278,7 @@ exit: return r; } +EXPORT_SYMBOL(nfc_hci_target_discovered); void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, struct sk_buff *skb) @@ -307,8 +311,13 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, nfc_hci_pipe2gate(hdev, pipe)); break; default: - /* TODO: Unknown events are hardware specific - * pass them to the driver (needs a new hci_ops) */ + if (hdev->ops->event_received) { + hdev->ops->event_received(hdev, + nfc_hci_pipe2gate(hdev, pipe), + event, skb); + return; + } + break; } @@ -527,7 +536,8 @@ static int hci_start_poll(struct nfc_dev *nfc_dev, return hdev->ops->start_poll(hdev, im_protocols, tm_protocols); else return nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, - NFC_HCI_EVT_READER_REQUESTED, NULL, 0); + NFC_HCI_EVT_READER_REQUESTED, + NULL, 0); } static void hci_stop_poll(struct nfc_dev *nfc_dev) @@ -538,6 +548,28 @@ static void hci_stop_poll(struct nfc_dev *nfc_dev) NFC_HCI_EVT_END_OPERATION, NULL, 0); } +static int hci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, + __u8 comm_mode, __u8 *gb, size_t gb_len) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); + + if (hdev->ops->dep_link_up) + return hdev->ops->dep_link_up(hdev, target, comm_mode, + gb, gb_len); + + return 0; +} + +static int hci_dep_link_down(struct nfc_dev *nfc_dev) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); + + if (hdev->ops->dep_link_down) + return hdev->ops->dep_link_down(hdev); + + return 0; +} + static int hci_activate_target(struct nfc_dev *nfc_dev, struct nfc_target *target, u32 protocol) { @@ -586,8 +618,8 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, switch (target->hci_reader_gate) { case NFC_HCI_RF_READER_A_GATE: case NFC_HCI_RF_READER_B_GATE: - if (hdev->ops->data_exchange) { - r = hdev->ops->data_exchange(hdev, target, skb, cb, + if (hdev->ops->im_transceive) { + r = hdev->ops->im_transceive(hdev, target, skb, cb, cb_context); if (r <= 0) /* handled */ break; @@ -604,14 +636,14 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, skb->len, hci_transceive_cb, hdev); break; default: - if (hdev->ops->data_exchange) { - r = hdev->ops->data_exchange(hdev, target, skb, cb, + if (hdev->ops->im_transceive) { + r = hdev->ops->im_transceive(hdev, target, skb, cb, cb_context); if (r == 1) r = -ENOTSUPP; - } - else + } else { r = -ENOTSUPP; + } break; } @@ -620,6 +652,16 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, return r; } +static int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); + + if (hdev->ops->tm_send) + return hdev->ops->tm_send(hdev, skb); + else + return -ENOTSUPP; +} + static int hci_check_presence(struct nfc_dev *nfc_dev, struct nfc_target *target) { @@ -723,9 +765,12 @@ static struct nfc_ops hci_nfc_ops = { .dev_down = hci_dev_down, .start_poll = hci_start_poll, .stop_poll = hci_stop_poll, + .dep_link_up = hci_dep_link_up, + .dep_link_down = hci_dep_link_down, .activate_target = hci_activate_target, .deactivate_target = hci_deactivate_target, .im_transceive = hci_transceive, + .tm_send = hci_tm_send, .check_presence = hci_check_presence, }; @@ -848,7 +893,7 @@ void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err) } EXPORT_SYMBOL(nfc_hci_driver_failure); -void inline nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb) +void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb) { nfc_llc_rcv_from_drv(hdev->llc, skb); } diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c index ae1205ded87f..fe5e966e5b88 100644 --- a/net/nfc/hci/llc.c +++ b/net/nfc/hci/llc.c @@ -72,7 +72,7 @@ int nfc_llc_register(const char *name, struct nfc_llc_ops *ops) llc_engine->ops = ops; INIT_LIST_HEAD(&llc_engine->entry); - list_add_tail (&llc_engine->entry, &llc_engines); + list_add_tail(&llc_engine->entry, &llc_engines); return 0; } diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index 01cbc72943cd..27b313befc35 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -634,9 +634,9 @@ static void llc_shdlc_sm_work(struct work_struct *work) r = llc_shdlc_connect_initiate(shdlc); else r = -ETIME; - if (r < 0) + if (r < 0) { llc_shdlc_connect_complete(shdlc, r); - else { + } else { mod_timer(&shdlc->connect_timer, jiffies + msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS)); @@ -682,9 +682,8 @@ static void llc_shdlc_sm_work(struct work_struct *work) llc_shdlc_handle_send_queue(shdlc); } - if (shdlc->hard_fault) { + if (shdlc->hard_fault) shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault); - } break; default: break; diff --git a/net/nfc/llcp/Kconfig b/net/nfc/llcp/Kconfig index fbf5e8150908..a1a41cd68255 100644 --- a/net/nfc/llcp/Kconfig +++ b/net/nfc/llcp/Kconfig @@ -1,6 +1,6 @@ config NFC_LLCP - depends on NFC && EXPERIMENTAL - bool "NFC LLCP support (EXPERIMENTAL)" + depends on NFC + bool "NFC LLCP support" default n help Say Y here if you want to build support for a kernel NFC LLCP diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c index c45ccd6c094c..ed2d17312d61 100644 --- a/net/nfc/llcp/commands.c +++ b/net/nfc/llcp/commands.c @@ -261,7 +261,6 @@ int nfc_llcp_disconnect(struct nfc_llcp_sock *sock) struct sk_buff *skb; struct nfc_dev *dev; struct nfc_llcp_local *local; - u16 size = 0; pr_debug("Sending DISC\n"); @@ -273,17 +272,10 @@ int nfc_llcp_disconnect(struct nfc_llcp_sock *sock) if (dev == NULL) return -ENODEV; - size += LLCP_HEADER_SIZE; - size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; - - skb = alloc_skb(size, GFP_ATOMIC); + skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0); if (skb == NULL) return -ENOMEM; - skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); - - skb = llcp_add_header(skb, sock->dsap, sock->ssap, LLCP_PDU_DISC); - skb_queue_tail(&local->tx_queue, skb); return 0; @@ -324,8 +316,7 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) struct sk_buff *skb; u8 *service_name_tlv = NULL, service_name_tlv_length; u8 *miux_tlv = NULL, miux_tlv_length; - u8 *rw_tlv = NULL, rw_tlv_length, rw; - __be16 miux; + u8 *rw_tlv = NULL, rw_tlv_length; int err; u16 size = 0; @@ -343,13 +334,11 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) size += service_name_tlv_length; } - miux = cpu_to_be16(LLCP_MAX_MIUX); - miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, + miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0, &miux_tlv_length); size += miux_tlv_length; - rw = LLCP_MAX_RW; - rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length); + rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length); size += rw_tlv_length; pr_debug("SKB size %d SN length %zu\n", size, sock->service_name_len); @@ -386,8 +375,7 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock) struct nfc_llcp_local *local; struct sk_buff *skb; u8 *miux_tlv = NULL, miux_tlv_length; - u8 *rw_tlv = NULL, rw_tlv_length, rw; - __be16 miux; + u8 *rw_tlv = NULL, rw_tlv_length; int err; u16 size = 0; @@ -397,13 +385,11 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock) if (local == NULL) return -ENODEV; - miux = cpu_to_be16(LLCP_MAX_MIUX); - miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, + miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0, &miux_tlv_length); size += miux_tlv_length; - rw = LLCP_MAX_RW; - rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length); + rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length); size += rw_tlv_length; skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, size); @@ -428,6 +414,52 @@ error_tlv: return err; } +int nfc_llcp_send_snl(struct nfc_llcp_local *local, u8 tid, u8 sap) +{ + struct sk_buff *skb; + struct nfc_dev *dev; + u8 *sdres_tlv = NULL, sdres_tlv_length, sdres[2]; + u16 size = 0; + + pr_debug("Sending SNL tid 0x%x sap 0x%x\n", tid, sap); + + if (local == NULL) + return -ENODEV; + + dev = local->dev; + if (dev == NULL) + return -ENODEV; + + sdres[0] = tid; + sdres[1] = sap; + sdres_tlv = nfc_llcp_build_tlv(LLCP_TLV_SDRES, sdres, 0, + &sdres_tlv_length); + if (sdres_tlv == NULL) + return -ENOMEM; + + size += LLCP_HEADER_SIZE; + size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; + size += sdres_tlv_length; + + skb = alloc_skb(size, GFP_KERNEL); + if (skb == NULL) { + kfree(sdres_tlv); + return -ENOMEM; + } + + skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); + + skb = llcp_add_header(skb, LLCP_SAP_SDP, LLCP_SAP_SDP, LLCP_PDU_SNL); + + memcpy(skb_put(skb, sdres_tlv_length), sdres_tlv, sdres_tlv_length); + + skb_queue_tail(&local->tx_queue, skb); + + kfree(sdres_tlv); + + return 0; +} + int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason) { struct sk_buff *skb; @@ -541,6 +573,52 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, return len; } +int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap, + struct msghdr *msg, size_t len) +{ + struct sk_buff *pdu; + struct nfc_llcp_local *local; + size_t frag_len = 0, remaining_len; + u8 *msg_ptr; + int err; + + pr_debug("Send UI frame len %zd\n", len); + + local = sock->local; + if (local == NULL) + return -ENODEV; + + remaining_len = len; + msg_ptr = (u8 *) msg->msg_iov; + + while (remaining_len > 0) { + + frag_len = min_t(size_t, sock->miu, remaining_len); + + pr_debug("Fragment %zd bytes remaining %zd", + frag_len, remaining_len); + + pdu = nfc_alloc_send_skb(sock->dev, &sock->sk, MSG_DONTWAIT, + frag_len + LLCP_HEADER_SIZE, &err); + if (pdu == NULL) { + pr_err("Could not allocate PDU\n"); + continue; + } + + pdu = llcp_add_header(pdu, dsap, ssap, LLCP_PDU_UI); + + memcpy(skb_put(pdu, frag_len), msg_ptr, frag_len); + + /* No need to check for the peer RW for UI frames */ + skb_queue_tail(&local->tx_queue, pdu); + + remaining_len -= frag_len; + msg_ptr += frag_len; + } + + return len; +} + int nfc_llcp_send_rr(struct nfc_llcp_sock *sock) { struct sk_buff *skb; diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index cc10d073c338..f6804532047a 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -45,12 +45,38 @@ void nfc_llcp_sock_unlink(struct llcp_sock_list *l, struct sock *sk) write_unlock(&l->lock); } +static void nfc_llcp_socket_purge(struct nfc_llcp_sock *sock) +{ + struct nfc_llcp_local *local = sock->local; + struct sk_buff *s, *tmp; + + pr_debug("%p\n", &sock->sk); + + skb_queue_purge(&sock->tx_queue); + skb_queue_purge(&sock->tx_pending_queue); + skb_queue_purge(&sock->tx_backlog_queue); + + if (local == NULL) + return; + + /* Search for local pending SKBs that are related to this socket */ + skb_queue_walk_safe(&local->tx_queue, s, tmp) { + if (s->sk != &sock->sk) + continue; + + skb_unlink(s, &local->tx_queue); + kfree_skb(s); + } +} + static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen) { struct sock *sk; struct hlist_node *node, *tmp; struct nfc_llcp_sock *llcp_sock; + skb_queue_purge(&local->tx_queue); + write_lock(&local->sockets.lock); sk_for_each_safe(sk, node, tmp, &local->sockets.head) { @@ -58,6 +84,8 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen) bh_lock_sock(sk); + nfc_llcp_socket_purge(llcp_sock); + if (sk->sk_state == LLCP_CONNECTED) nfc_put_device(llcp_sock->dev); @@ -65,7 +93,8 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen) struct nfc_llcp_sock *lsk, *n; struct sock *accept_sk; - list_for_each_entry_safe(lsk, n, &llcp_sock->accept_queue, + list_for_each_entry_safe(lsk, n, + &llcp_sock->accept_queue, accept_queue) { accept_sk = &lsk->sk; bh_lock_sock(accept_sk); @@ -85,6 +114,16 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen) } } + /* + * If we have a connection less socket bound, we keep it alive + * if the device is still present. + */ + if (sk->sk_state == LLCP_BOUND && sk->sk_type == SOCK_DGRAM && + listen == true) { + bh_unlock_sock(sk); + continue; + } + sk->sk_state = LLCP_CLOSED; bh_unlock_sock(sk); @@ -134,7 +173,7 @@ static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local, { struct sock *sk; struct hlist_node *node; - struct nfc_llcp_sock *llcp_sock; + struct nfc_llcp_sock *llcp_sock, *tmp_sock; pr_debug("ssap dsap %d %d\n", ssap, dsap); @@ -146,10 +185,12 @@ static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local, llcp_sock = NULL; sk_for_each(sk, node, &local->sockets.head) { - llcp_sock = nfc_llcp_sock(sk); + tmp_sock = nfc_llcp_sock(sk); - if (llcp_sock->ssap == ssap && llcp_sock->dsap == dsap) + if (tmp_sock->ssap == ssap && tmp_sock->dsap == dsap) { + llcp_sock = tmp_sock; break; + } } read_unlock(&local->sockets.lock); @@ -249,7 +290,12 @@ struct nfc_llcp_sock *nfc_llcp_sock_from_sn(struct nfc_llcp_local *local, pr_debug("llcp sock %p\n", tmp_sock); - if (tmp_sock->sk.sk_state != LLCP_LISTEN) + if (tmp_sock->sk.sk_type == SOCK_STREAM && + tmp_sock->sk.sk_state != LLCP_LISTEN) + continue; + + if (tmp_sock->sk.sk_type == SOCK_DGRAM && + tmp_sock->sk.sk_state != LLCP_BOUND) continue; if (tmp_sock->service_name == NULL || @@ -421,10 +467,9 @@ static u8 nfc_llcp_reserve_sdp_ssap(struct nfc_llcp_local *local) static int nfc_llcp_build_gb(struct nfc_llcp_local *local) { u8 *gb_cur, *version_tlv, version, version_length; - u8 *lto_tlv, lto, lto_length; + u8 *lto_tlv, lto_length; u8 *wks_tlv, wks_length; u8 *miux_tlv, miux_length; - __be16 miux; u8 gb_len = 0; int ret = 0; @@ -433,9 +478,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local) 1, &version_length); gb_len += version_length; - /* 1500 ms */ - lto = 150; - lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, <o, 1, <o_length); + lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, &local->lto, 1, <o_length); gb_len += lto_length; pr_debug("Local wks 0x%lx\n", local->local_wks); @@ -443,8 +486,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local) &wks_length); gb_len += wks_length; - miux = cpu_to_be16(LLCP_MAX_MIUX); - miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, + miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0, &miux_length); gb_len += miux_length; @@ -610,7 +652,10 @@ static void nfc_llcp_tx_work(struct work_struct *work) if (skb != NULL) { sk = skb->sk; llcp_sock = nfc_llcp_sock(sk); - if (llcp_sock != NULL) { + + if (llcp_sock == NULL && nfc_llcp_ptype(skb) == LLCP_PDU_I) { + nfc_llcp_send_symm(local->dev); + } else { int ret; pr_debug("Sending pending skb\n"); @@ -629,8 +674,6 @@ static void nfc_llcp_tx_work(struct work_struct *work) skb_queue_tail(&llcp_sock->tx_pending_queue, skb); } - } else { - nfc_llcp_send_symm(local->dev); } } else { nfc_llcp_send_symm(local->dev); @@ -704,6 +747,39 @@ static u8 *nfc_llcp_connect_sn(struct sk_buff *skb, size_t *sn_len) return NULL; } +static void nfc_llcp_recv_ui(struct nfc_llcp_local *local, + struct sk_buff *skb) +{ + struct nfc_llcp_sock *llcp_sock; + struct nfc_llcp_ui_cb *ui_cb; + u8 dsap, ssap; + + dsap = nfc_llcp_dsap(skb); + ssap = nfc_llcp_ssap(skb); + + ui_cb = nfc_llcp_ui_skb_cb(skb); + ui_cb->dsap = dsap; + ui_cb->ssap = ssap; + + printk("%s %d %d\n", __func__, dsap, ssap); + + pr_debug("%d %d\n", dsap, ssap); + + /* We're looking for a bound socket, not a client one */ + llcp_sock = nfc_llcp_sock_get(local, dsap, LLCP_SAP_SDP); + if (llcp_sock == NULL || llcp_sock->sk.sk_type != SOCK_DGRAM) + return; + + /* There is no sequence with UI frames */ + skb_pull(skb, LLCP_HEADER_SIZE); + if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) { + pr_err("receive queue is full\n"); + skb_queue_head(&llcp_sock->tx_backlog_queue, skb); + } + + nfc_llcp_sock_put(llcp_sock); +} + static void nfc_llcp_recv_connect(struct nfc_llcp_local *local, struct sk_buff *skb) { @@ -823,9 +899,6 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local, fail: /* Send DM */ nfc_llcp_send_dm(local, dsap, ssap, reason); - - return; - } int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock) @@ -953,6 +1026,9 @@ static void nfc_llcp_recv_disc(struct nfc_llcp_local *local, sk = &llcp_sock->sk; lock_sock(sk); + + nfc_llcp_socket_purge(llcp_sock); + if (sk->sk_state == LLCP_CLOSED) { release_sock(sk); nfc_llcp_sock_put(llcp_sock); @@ -1027,7 +1103,7 @@ static void nfc_llcp_recv_dm(struct nfc_llcp_local *local, struct sk_buff *skb) } if (llcp_sock == NULL) { - pr_err("Invalid DM\n"); + pr_debug("Already closed\n"); return; } @@ -1038,8 +1114,100 @@ static void nfc_llcp_recv_dm(struct nfc_llcp_local *local, struct sk_buff *skb) sk->sk_state_change(sk); nfc_llcp_sock_put(llcp_sock); +} - return; +static void nfc_llcp_recv_snl(struct nfc_llcp_local *local, + struct sk_buff *skb) +{ + struct nfc_llcp_sock *llcp_sock; + u8 dsap, ssap, *tlv, type, length, tid, sap; + u16 tlv_len, offset; + char *service_name; + size_t service_name_len; + + dsap = nfc_llcp_dsap(skb); + ssap = nfc_llcp_ssap(skb); + + pr_debug("%d %d\n", dsap, ssap); + + if (dsap != LLCP_SAP_SDP || ssap != LLCP_SAP_SDP) { + pr_err("Wrong SNL SAP\n"); + return; + } + + tlv = &skb->data[LLCP_HEADER_SIZE]; + tlv_len = skb->len - LLCP_HEADER_SIZE; + offset = 0; + + while (offset < tlv_len) { + type = tlv[0]; + length = tlv[1]; + + switch (type) { + case LLCP_TLV_SDREQ: + tid = tlv[2]; + service_name = (char *) &tlv[3]; + service_name_len = length - 1; + + pr_debug("Looking for %.16s\n", service_name); + + if (service_name_len == strlen("urn:nfc:sn:sdp") && + !strncmp(service_name, "urn:nfc:sn:sdp", + service_name_len)) { + sap = 1; + goto send_snl; + } + + llcp_sock = nfc_llcp_sock_from_sn(local, service_name, + service_name_len); + if (!llcp_sock) { + sap = 0; + goto send_snl; + } + + /* + * We found a socket but its ssap has not been reserved + * yet. We need to assign it for good and send a reply. + * The ssap will be freed when the socket is closed. + */ + if (llcp_sock->ssap == LLCP_SDP_UNBOUND) { + atomic_t *client_count; + + sap = nfc_llcp_reserve_sdp_ssap(local); + + pr_debug("Reserving %d\n", sap); + + if (sap == LLCP_SAP_MAX) { + sap = 0; + goto send_snl; + } + + client_count = + &local->local_sdp_cnt[sap - + LLCP_WKS_NUM_SAP]; + + atomic_inc(client_count); + + llcp_sock->ssap = sap; + llcp_sock->reserved_ssap = sap; + } else { + sap = llcp_sock->ssap; + } + + pr_debug("%p %d\n", llcp_sock, sap); + +send_snl: + nfc_llcp_send_snl(local, tid, sap); + break; + + default: + pr_err("Invalid SNL tlv value 0x%x\n", type); + break; + } + + offset += length + 2; + tlv += length + 2; + } } static void nfc_llcp_rx_work(struct work_struct *work) @@ -1072,6 +1240,11 @@ static void nfc_llcp_rx_work(struct work_struct *work) pr_debug("SYMM\n"); break; + case LLCP_PDU_UI: + pr_debug("UI\n"); + nfc_llcp_recv_ui(local, skb); + break; + case LLCP_PDU_CONNECT: pr_debug("CONNECT\n"); nfc_llcp_recv_connect(local, skb); @@ -1092,6 +1265,11 @@ static void nfc_llcp_rx_work(struct work_struct *work) nfc_llcp_recv_dm(local, skb); break; + case LLCP_PDU_SNL: + pr_debug("SNL\n"); + nfc_llcp_recv_snl(local, skb); + break; + case LLCP_PDU_I: case LLCP_PDU_RR: case LLCP_PDU_RNR: @@ -1104,8 +1282,6 @@ static void nfc_llcp_rx_work(struct work_struct *work) schedule_work(&local->tx_work); kfree_skb(local->rx_pending); local->rx_pending = NULL; - - return; } void nfc_llcp_recv(void *data, struct sk_buff *skb, int err) @@ -1121,8 +1297,6 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err) local->rx_pending = skb_get(skb); del_timer(&local->link_timer); schedule_work(&local->rx_work); - - return; } int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb) @@ -1205,6 +1379,10 @@ int nfc_llcp_register_device(struct nfc_dev *ndev) rwlock_init(&local->connecting_sockets.lock); rwlock_init(&local->raw_sockets.lock); + local->lto = 150; /* 1500 ms */ + local->rw = LLCP_MAX_RW; + local->miux = cpu_to_be16(LLCP_MAX_MIUX); + nfc_llcp_build_gb(local); local->remote_miu = LLCP_DEFAULT_MIU; diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h index fdb2d24e60bd..0d62366f8cc3 100644 --- a/net/nfc/llcp/llcp.h +++ b/net/nfc/llcp/llcp.h @@ -64,6 +64,9 @@ struct nfc_llcp_local { u32 target_idx; u8 rf_mode; u8 comm_mode; + u8 lto; + u8 rw; + __be16 miux; unsigned long local_wks; /* Well known services */ unsigned long local_sdp; /* Local services */ unsigned long local_sap; /* Local SAPs, not available for discovery */ @@ -124,6 +127,13 @@ struct nfc_llcp_sock { struct sock *parent; }; +struct nfc_llcp_ui_cb { + __u8 dsap; + __u8 ssap; +}; + +#define nfc_llcp_ui_skb_cb(__skb) ((struct nfc_llcp_ui_cb *)&((__skb)->cb[0])) + #define nfc_llcp_sock(sk) ((struct nfc_llcp_sock *) (sk)) #define nfc_llcp_dev(sk) (nfc_llcp_sock((sk))->dev) @@ -209,10 +219,13 @@ int nfc_llcp_disconnect(struct nfc_llcp_sock *sock); int nfc_llcp_send_symm(struct nfc_dev *dev); int nfc_llcp_send_connect(struct nfc_llcp_sock *sock); int nfc_llcp_send_cc(struct nfc_llcp_sock *sock); +int nfc_llcp_send_snl(struct nfc_llcp_local *local, u8 tid, u8 sap); int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason); int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock); int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, struct msghdr *msg, size_t len); +int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap, + struct msghdr *msg, size_t len); int nfc_llcp_send_rr(struct nfc_llcp_sock *sock); /* Socket API */ diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index 63e4cdc92376..0fa1e92ceac8 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -205,8 +205,8 @@ static int llcp_sock_listen(struct socket *sock, int backlog) lock_sock(sk); - if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) - || sk->sk_state != LLCP_BOUND) { + if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) || + sk->sk_state != LLCP_BOUND) { ret = -EBADFD; goto error; } @@ -608,6 +608,25 @@ static int llcp_sock_sendmsg(struct kiocb *iocb, struct socket *sock, lock_sock(sk); + if (sk->sk_type == SOCK_DGRAM) { + struct sockaddr_nfc_llcp *addr = + (struct sockaddr_nfc_llcp *)msg->msg_name; + + if (msg->msg_namelen < sizeof(*addr)) { + release_sock(sk); + + pr_err("Invalid socket address length %d\n", + msg->msg_namelen); + + return -EINVAL; + } + + release_sock(sk); + + return nfc_llcp_send_ui_frame(llcp_sock, addr->dsap, addr->ssap, + msg, len); + } + if (sk->sk_state != LLCP_CONNECTED) { release_sock(sk); return -ENOTCONN; @@ -663,11 +682,28 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock, return -EFAULT; } + if (sk->sk_type == SOCK_DGRAM && msg->msg_name) { + struct nfc_llcp_ui_cb *ui_cb = nfc_llcp_ui_skb_cb(skb); + struct sockaddr_nfc_llcp sockaddr; + + pr_debug("Datagram socket %d %d\n", ui_cb->dsap, ui_cb->ssap); + + sockaddr.sa_family = AF_NFC; + sockaddr.nfc_protocol = NFC_PROTO_NFC_DEP; + sockaddr.dsap = ui_cb->dsap; + sockaddr.ssap = ui_cb->ssap; + + memcpy(msg->msg_name, &sockaddr, sizeof(sockaddr)); + msg->msg_namelen = sizeof(sockaddr); + } + /* Mark read part of skb as used */ if (!(flags & MSG_PEEK)) { /* SOCK_STREAM: re-queue skb if it contains unreceived data */ - if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_RAW) { + if (sk->sk_type == SOCK_STREAM || + sk->sk_type == SOCK_DGRAM || + sk->sk_type == SOCK_RAW) { skb_pull(skb, copied); if (skb->len) { skb_queue_head(&sk->sk_receive_queue, skb); diff --git a/net/nfc/nci/Kconfig b/net/nfc/nci/Kconfig index decdc49b26d8..6d69b5f0f19b 100644 --- a/net/nfc/nci/Kconfig +++ b/net/nfc/nci/Kconfig @@ -1,6 +1,6 @@ config NFC_NCI - depends on NFC && EXPERIMENTAL - tristate "NCI protocol support (EXPERIMENTAL)" + depends on NFC + tristate "NCI protocol support" default n help NCI (NFC Controller Interface) is a communication protocol between diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index acf9abb7d99b..5f98dc1bf039 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -205,10 +205,10 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) cmd.num_disc_configs = 0; if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && - (protocols & NFC_PROTO_JEWEL_MASK - || protocols & NFC_PROTO_MIFARE_MASK - || protocols & NFC_PROTO_ISO14443_MASK - || protocols & NFC_PROTO_NFC_DEP_MASK)) { + (protocols & NFC_PROTO_JEWEL_MASK || + protocols & NFC_PROTO_MIFARE_MASK || + protocols & NFC_PROTO_ISO14443_MASK || + protocols & NFC_PROTO_NFC_DEP_MASK)) { cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = NCI_NFC_A_PASSIVE_POLL_MODE; cmd.disc_configs[cmd.num_disc_configs].frequency = 1; @@ -224,8 +224,8 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) } if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && - (protocols & NFC_PROTO_FELICA_MASK - || protocols & NFC_PROTO_NFC_DEP_MASK)) { + (protocols & NFC_PROTO_FELICA_MASK || + protocols & NFC_PROTO_NFC_DEP_MASK)) { cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = NCI_NFC_F_PASSIVE_POLL_MODE; cmd.disc_configs[cmd.num_disc_configs].frequency = 1; @@ -414,13 +414,13 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); struct nci_set_config_param param; __u8 local_gb[NFC_MAX_GT_LEN]; - int i, rc = 0; + int i; param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len); if ((param.val == NULL) || (param.len == 0)) - return rc; + return 0; - if (param.len > NCI_MAX_PARAM_LEN) + if (param.len > NFC_MAX_GT_LEN) return -EINVAL; for (i = 0; i < param.len; i++) @@ -429,10 +429,8 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) param.id = NCI_PN_ATR_REQ_GEN_BYTES; param.val = local_gb; - rc = nci_request(ndev, nci_set_config_req, (unsigned long)¶m, - msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); - - return rc; + return nci_request(ndev, nci_set_config_req, (unsigned long)¶m, + msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); } static int nci_start_poll(struct nfc_dev *nfc_dev, @@ -579,7 +577,6 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev, } } - static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, __u8 comm_mode, __u8 *gb, size_t gb_len) { @@ -806,8 +803,8 @@ int nci_recv_frame(struct sk_buff *skb) pr_debug("len %d\n", skb->len); - if (!ndev || (!test_bit(NCI_UP, &ndev->flags) - && !test_bit(NCI_INIT, &ndev->flags))) { + if (!ndev || (!test_bit(NCI_UP, &ndev->flags) && + !test_bit(NCI_INIT, &ndev->flags))) { kfree_skb(skb); return -ENXIO; } diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index c1b5285cbde7..3568ae16786d 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -29,6 +29,8 @@ #include "nfc.h" +#include "llcp/llcp.h" + static struct genl_multicast_group nfc_genl_event_mcgrp = { .name = NFC_GENL_MCAST_EVENT_NAME, }; @@ -364,7 +366,8 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev, if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) || nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) || - nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up)) + nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) || + nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode)) goto nla_put_failure; return genlmsg_end(msg, hdr); @@ -590,7 +593,7 @@ static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || ((!info->attrs[NFC_ATTR_IM_PROTOCOLS] && !info->attrs[NFC_ATTR_PROTOCOLS]) && - !info->attrs[NFC_ATTR_TM_PROTOCOLS])) + !info->attrs[NFC_ATTR_TM_PROTOCOLS])) return -EINVAL; idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); @@ -715,6 +718,146 @@ static int nfc_genl_dep_link_down(struct sk_buff *skb, struct genl_info *info) return rc; } +static int nfc_genl_send_params(struct sk_buff *msg, + struct nfc_llcp_local *local, + u32 portid, u32 seq) +{ + void *hdr; + + hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, 0, + NFC_CMD_LLC_GET_PARAMS); + if (!hdr) + return -EMSGSIZE; + + if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, local->dev->idx) || + nla_put_u8(msg, NFC_ATTR_LLC_PARAM_LTO, local->lto) || + nla_put_u8(msg, NFC_ATTR_LLC_PARAM_RW, local->rw) || + nla_put_u16(msg, NFC_ATTR_LLC_PARAM_MIUX, be16_to_cpu(local->miux))) + goto nla_put_failure; + + return genlmsg_end(msg, hdr); + +nla_put_failure: + + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nfc_genl_llc_get_params(struct sk_buff *skb, struct genl_info *info) +{ + struct nfc_dev *dev; + struct nfc_llcp_local *local; + int rc = 0; + struct sk_buff *msg = NULL; + u32 idx; + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) + return -EINVAL; + + idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + + dev = nfc_get_device(idx); + if (!dev) + return -ENODEV; + + device_lock(&dev->dev); + + local = nfc_llcp_find_local(dev); + if (!local) { + rc = -ENODEV; + goto exit; + } + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) { + rc = -ENOMEM; + goto exit; + } + + rc = nfc_genl_send_params(msg, local, info->snd_portid, info->snd_seq); + +exit: + device_unlock(&dev->dev); + + nfc_put_device(dev); + + if (rc < 0) { + if (msg) + nlmsg_free(msg); + + return rc; + } + + return genlmsg_reply(msg, info); +} + +static int nfc_genl_llc_set_params(struct sk_buff *skb, struct genl_info *info) +{ + struct nfc_dev *dev; + struct nfc_llcp_local *local; + u8 rw = 0; + u16 miux = 0; + u32 idx; + int rc = 0; + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || + (!info->attrs[NFC_ATTR_LLC_PARAM_LTO] && + !info->attrs[NFC_ATTR_LLC_PARAM_RW] && + !info->attrs[NFC_ATTR_LLC_PARAM_MIUX])) + return -EINVAL; + + if (info->attrs[NFC_ATTR_LLC_PARAM_RW]) { + rw = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_RW]); + + if (rw > LLCP_MAX_RW) + return -EINVAL; + } + + if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) { + miux = nla_get_u16(info->attrs[NFC_ATTR_LLC_PARAM_MIUX]); + + if (miux > LLCP_MAX_MIUX) + return -EINVAL; + } + + idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + + dev = nfc_get_device(idx); + if (!dev) + return -ENODEV; + + device_lock(&dev->dev); + + local = nfc_llcp_find_local(dev); + if (!local) { + nfc_put_device(dev); + rc = -ENODEV; + goto exit; + } + + if (info->attrs[NFC_ATTR_LLC_PARAM_LTO]) { + if (dev->dep_link_up) { + rc = -EINPROGRESS; + goto exit; + } + + local->lto = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_LTO]); + } + + if (info->attrs[NFC_ATTR_LLC_PARAM_RW]) + local->rw = rw; + + if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) + local->miux = cpu_to_be16(miux); + +exit: + device_unlock(&dev->dev); + + nfc_put_device(dev); + + return rc; +} + static struct genl_ops nfc_genl_ops[] = { { .cmd = NFC_CMD_GET_DEVICE, @@ -759,6 +902,16 @@ static struct genl_ops nfc_genl_ops[] = { .done = nfc_genl_dump_targets_done, .policy = nfc_genl_policy, }, + { + .cmd = NFC_CMD_LLC_GET_PARAMS, + .doit = nfc_genl_llc_get_params, + .policy = nfc_genl_policy, + }, + { + .cmd = NFC_CMD_LLC_SET_PARAMS, + .doit = nfc_genl_llc_set_params, + .policy = nfc_genl_policy, + }, }; diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index c5e42b79a418..87d914d2876a 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -56,6 +56,7 @@ void nfc_llcp_unregister_device(struct nfc_dev *dev); int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len); u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len); int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb); +struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev); int __init nfc_llcp_init(void); void nfc_llcp_exit(void); @@ -97,6 +98,11 @@ static inline int nfc_llcp_data_received(struct nfc_dev *dev, return 0; } +static inline struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev) +{ + return NULL; +} + static inline int nfc_llcp_init(void) { return 0; diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index 8b8a6a2b2bad..313bf1bc848a 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -256,7 +256,6 @@ static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock, return rc ? : copied; } - static const struct proto_ops rawsock_ops = { .family = PF_NFC, .owner = THIS_MODULE, diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 9034f52659b5..f262dbfc7f06 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1881,7 +1881,35 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, skb_reserve(skb, hlen); skb_reset_network_header(skb); - data = ph.raw + po->tp_hdrlen - sizeof(struct sockaddr_ll); + if (po->tp_tx_has_off) { + int off_min, off_max, off; + off_min = po->tp_hdrlen - sizeof(struct sockaddr_ll); + off_max = po->tx_ring.frame_size - tp_len; + if (sock->type == SOCK_DGRAM) { + switch (po->tp_version) { + case TPACKET_V2: + off = ph.h2->tp_net; + break; + default: + off = ph.h1->tp_net; + break; + } + } else { + switch (po->tp_version) { + case TPACKET_V2: + off = ph.h2->tp_mac; + break; + default: + off = ph.h1->tp_mac; + break; + } + } + if (unlikely((off < off_min) || (off_max < off))) + return -EINVAL; + data = ph.raw + off; + } else { + data = ph.raw + po->tp_hdrlen - sizeof(struct sockaddr_ll); + } to_write = tp_len; if (sock->type == SOCK_DGRAM) { @@ -3109,6 +3137,19 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv return fanout_add(sk, val & 0xffff, val >> 16); } + case PACKET_TX_HAS_OFF: + { + unsigned int val; + + if (optlen != sizeof(val)) + return -EINVAL; + if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) + return -EBUSY; + if (copy_from_user(&val, optval, sizeof(val))) + return -EFAULT; + po->tp_tx_has_off = !!val; + return 0; + } default: return -ENOPROTOOPT; } @@ -3200,6 +3241,9 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, ((u32)po->fanout->type << 16)) : 0); break; + case PACKET_TX_HAS_OFF: + val = po->tp_tx_has_off; + break; default: return -ENOPROTOOPT; } diff --git a/net/packet/internal.h b/net/packet/internal.h index 44945f6b7252..e84cab8cb7a9 100644 --- a/net/packet/internal.h +++ b/net/packet/internal.h @@ -109,6 +109,7 @@ struct packet_sock { unsigned int tp_hdrlen; unsigned int tp_reserve; unsigned int tp_loss:1; + unsigned int tp_tx_has_off:1; unsigned int tp_tstamp; struct packet_type prot_hook ____cacheline_aligned_in_smp; }; diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 9d75b7761313..d2922c0ef57a 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -71,6 +71,12 @@ enum htb_cmode { HTB_CAN_SEND /* class can send */ }; +struct htb_rate_cfg { + u64 rate_bps; + u32 mult; + u32 shift; +}; + /* interior & leaf nodes; props specific to leaves are marked L: */ struct htb_class { struct Qdisc_class_common common; @@ -118,11 +124,11 @@ struct htb_class { int filter_cnt; /* token bucket parameters */ - struct qdisc_rate_table *rate; /* rate table of the class itself */ - struct qdisc_rate_table *ceil; /* ceiling rate (limits borrows too) */ - long buffer, cbuffer; /* token bucket depth/rate */ + struct htb_rate_cfg rate; + struct htb_rate_cfg ceil; + s64 buffer, cbuffer; /* token bucket depth/rate */ psched_tdiff_t mbuffer; /* max wait time */ - long tokens, ctokens; /* current number of tokens */ + s64 tokens, ctokens; /* current number of tokens */ psched_time_t t_c; /* checkpoint time */ }; @@ -162,6 +168,45 @@ struct htb_sched { struct work_struct work; }; +static u64 l2t_ns(struct htb_rate_cfg *r, unsigned int len) +{ + return ((u64)len * r->mult) >> r->shift; +} + +static void htb_precompute_ratedata(struct htb_rate_cfg *r) +{ + u64 factor; + u64 mult; + int shift; + + r->shift = 0; + r->mult = 1; + /* + * Calibrate mult, shift so that token counting is accurate + * for smallest packet size (64 bytes). Token (time in ns) is + * computed as (bytes * 8) * NSEC_PER_SEC / rate_bps. It will + * work as long as the smallest packet transfer time can be + * accurately represented in nanosec. + */ + if (r->rate_bps > 0) { + /* + * Higher shift gives better accuracy. Find the largest + * shift such that mult fits in 32 bits. + */ + for (shift = 0; shift < 16; shift++) { + r->shift = shift; + factor = 8LLU * NSEC_PER_SEC * (1 << r->shift); + mult = div64_u64(factor, r->rate_bps); + if (mult > UINT_MAX) + break; + } + + r->shift = shift - 1; + factor = 8LLU * NSEC_PER_SEC * (1 << r->shift); + r->mult = div64_u64(factor, r->rate_bps); + } +} + /* find class in global hash table using given handle */ static inline struct htb_class *htb_find(u32 handle, struct Qdisc *sch) { @@ -273,7 +318,7 @@ static void htb_add_to_id_tree(struct rb_root *root, * already in the queue. */ static void htb_add_to_wait_tree(struct htb_sched *q, - struct htb_class *cl, long delay) + struct htb_class *cl, s64 delay) { struct rb_node **p = &q->wait_pq[cl->level].rb_node, *parent = NULL; @@ -441,14 +486,14 @@ static void htb_deactivate_prios(struct htb_sched *q, struct htb_class *cl) htb_remove_class_from_row(q, cl, mask); } -static inline long htb_lowater(const struct htb_class *cl) +static inline s64 htb_lowater(const struct htb_class *cl) { if (htb_hysteresis) return cl->cmode != HTB_CANT_SEND ? -cl->cbuffer : 0; else return 0; } -static inline long htb_hiwater(const struct htb_class *cl) +static inline s64 htb_hiwater(const struct htb_class *cl) { if (htb_hysteresis) return cl->cmode == HTB_CAN_SEND ? -cl->buffer : 0; @@ -469,9 +514,9 @@ static inline long htb_hiwater(const struct htb_class *cl) * mode transitions per time unit. The speed gain is about 1/6. */ static inline enum htb_cmode -htb_class_mode(struct htb_class *cl, long *diff) +htb_class_mode(struct htb_class *cl, s64 *diff) { - long toks; + s64 toks; if ((toks = (cl->ctokens + *diff)) < htb_lowater(cl)) { *diff = -toks; @@ -495,7 +540,7 @@ htb_class_mode(struct htb_class *cl, long *diff) * to mode other than HTB_CAN_SEND (see htb_add_to_wait_tree). */ static void -htb_change_class_mode(struct htb_sched *q, struct htb_class *cl, long *diff) +htb_change_class_mode(struct htb_sched *q, struct htb_class *cl, s64 *diff) { enum htb_cmode new_mode = htb_class_mode(cl, diff); @@ -581,26 +626,26 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch) return NET_XMIT_SUCCESS; } -static inline void htb_accnt_tokens(struct htb_class *cl, int bytes, long diff) +static inline void htb_accnt_tokens(struct htb_class *cl, int bytes, s64 diff) { - long toks = diff + cl->tokens; + s64 toks = diff + cl->tokens; if (toks > cl->buffer) toks = cl->buffer; - toks -= (long) qdisc_l2t(cl->rate, bytes); + toks -= (s64) l2t_ns(&cl->rate, bytes); if (toks <= -cl->mbuffer) toks = 1 - cl->mbuffer; cl->tokens = toks; } -static inline void htb_accnt_ctokens(struct htb_class *cl, int bytes, long diff) +static inline void htb_accnt_ctokens(struct htb_class *cl, int bytes, s64 diff) { - long toks = diff + cl->ctokens; + s64 toks = diff + cl->ctokens; if (toks > cl->cbuffer) toks = cl->cbuffer; - toks -= (long) qdisc_l2t(cl->ceil, bytes); + toks -= (s64) l2t_ns(&cl->ceil, bytes); if (toks <= -cl->mbuffer) toks = 1 - cl->mbuffer; @@ -623,10 +668,10 @@ static void htb_charge_class(struct htb_sched *q, struct htb_class *cl, { int bytes = qdisc_pkt_len(skb); enum htb_cmode old_mode; - long diff; + s64 diff; while (cl) { - diff = psched_tdiff_bounded(q->now, cl->t_c, cl->mbuffer); + diff = min_t(s64, q->now - cl->t_c, cl->mbuffer); if (cl->level >= level) { if (cl->level == level) cl->xstats.lends++; @@ -673,7 +718,7 @@ static psched_time_t htb_do_events(struct htb_sched *q, int level, unsigned long stop_at = start + 2; while (time_before(jiffies, stop_at)) { struct htb_class *cl; - long diff; + s64 diff; struct rb_node *p = rb_first(&q->wait_pq[level]); if (!p) @@ -684,7 +729,7 @@ static psched_time_t htb_do_events(struct htb_sched *q, int level, return cl->pq_key; htb_safe_rb_erase(p, q->wait_pq + level); - diff = psched_tdiff_bounded(q->now, cl->t_c, cl->mbuffer); + diff = min_t(s64, q->now - cl->t_c, cl->mbuffer); htb_change_class_mode(q, cl, &diff); if (cl->cmode != HTB_CAN_SEND) htb_add_to_wait_tree(q, cl, diff); @@ -871,10 +916,10 @@ ok: if (!sch->q.qlen) goto fin; - q->now = psched_get_time(); + q->now = ktime_to_ns(ktime_get()); start_at = jiffies; - next_event = q->now + 5 * PSCHED_TICKS_PER_SEC; + next_event = q->now + 5 * NSEC_PER_SEC; for (level = 0; level < TC_HTB_MAXDEPTH; level++) { /* common case optimization - skip event handler quickly */ @@ -884,7 +929,7 @@ ok: if (q->now >= q->near_ev_cache[level]) { event = htb_do_events(q, level, start_at); if (!event) - event = q->now + PSCHED_TICKS_PER_SEC; + event = q->now + NSEC_PER_SEC; q->near_ev_cache[level] = event; } else event = q->near_ev_cache[level]; @@ -903,10 +948,17 @@ ok: } } sch->qstats.overlimits++; - if (likely(next_event > q->now)) - qdisc_watchdog_schedule(&q->watchdog, next_event); - else + if (likely(next_event > q->now)) { + if (!test_bit(__QDISC_STATE_DEACTIVATED, + &qdisc_root_sleeping(q->watchdog.qdisc)->state)) { + ktime_t time = ns_to_ktime(next_event); + qdisc_throttled(q->watchdog.qdisc); + hrtimer_start(&q->watchdog.timer, time, + HRTIMER_MODE_ABS); + } + } else { schedule_work(&q->work); + } fin: return skb; } @@ -1082,9 +1134,9 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg, memset(&opt, 0, sizeof(opt)); - opt.rate = cl->rate->rate; + opt.rate.rate = cl->rate.rate_bps >> 3; opt.buffer = cl->buffer; - opt.ceil = cl->ceil->rate; + opt.ceil.rate = cl->ceil.rate_bps >> 3; opt.cbuffer = cl->cbuffer; opt.quantum = cl->quantum; opt.prio = cl->prio; @@ -1203,9 +1255,6 @@ static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl) qdisc_destroy(cl->un.leaf.q); } gen_kill_estimator(&cl->bstats, &cl->rate_est); - qdisc_put_rtab(cl->rate); - qdisc_put_rtab(cl->ceil); - tcf_destroy_chain(&cl->filter_list); kfree(cl); } @@ -1307,7 +1356,6 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, struct htb_sched *q = qdisc_priv(sch); struct htb_class *cl = (struct htb_class *)*arg, *parent; struct nlattr *opt = tca[TCA_OPTIONS]; - struct qdisc_rate_table *rtab = NULL, *ctab = NULL; struct nlattr *tb[__TCA_HTB_MAX]; struct tc_htb_opt *hopt; @@ -1326,10 +1374,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, parent = parentid == TC_H_ROOT ? NULL : htb_find(parentid, sch); hopt = nla_data(tb[TCA_HTB_PARMS]); - - rtab = qdisc_get_rtab(&hopt->rate, tb[TCA_HTB_RTAB]); - ctab = qdisc_get_rtab(&hopt->ceil, tb[TCA_HTB_CTAB]); - if (!rtab || !ctab) + if (!hopt->rate.rate || !hopt->ceil.rate) goto failure; if (!cl) { /* new class */ @@ -1439,7 +1484,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, * is really leaf before changing cl->un.leaf ! */ if (!cl->level) { - cl->quantum = rtab->rate.rate / q->rate2quantum; + cl->quantum = hopt->rate.rate / q->rate2quantum; if (!hopt->quantum && cl->quantum < 1000) { pr_warning( "HTB: quantum of class %X is small. Consider r2q change.\n", @@ -1460,12 +1505,16 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, cl->buffer = hopt->buffer; cl->cbuffer = hopt->cbuffer; - if (cl->rate) - qdisc_put_rtab(cl->rate); - cl->rate = rtab; - if (cl->ceil) - qdisc_put_rtab(cl->ceil); - cl->ceil = ctab; + + cl->rate.rate_bps = (u64)hopt->rate.rate << 3; + cl->ceil.rate_bps = (u64)hopt->ceil.rate << 3; + + htb_precompute_ratedata(&cl->rate); + htb_precompute_ratedata(&cl->ceil); + + cl->buffer = hopt->buffer << PSCHED_SHIFT; + cl->cbuffer = hopt->buffer << PSCHED_SHIFT; + sch_tree_unlock(sch); qdisc_class_hash_grow(sch, &q->clhash); @@ -1474,10 +1523,6 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, return 0; failure: - if (rtab) - qdisc_put_rtab(rtab); - if (ctab) - qdisc_put_rtab(ctab); return err; } diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index f0dd83cff906..9687fa1c2275 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -84,18 +84,19 @@ * grp->index is the index of the group; and grp->slot_shift * is the shift for the corresponding (scaled) sigma_i. */ -#define QFQ_MAX_INDEX 19 -#define QFQ_MAX_WSHIFT 16 +#define QFQ_MAX_INDEX 24 +#define QFQ_MAX_WSHIFT 12 #define QFQ_MAX_WEIGHT (1<<QFQ_MAX_WSHIFT) -#define QFQ_MAX_WSUM (2*QFQ_MAX_WEIGHT) +#define QFQ_MAX_WSUM (16*QFQ_MAX_WEIGHT) #define FRAC_BITS 30 /* fixed point arithmetic */ #define ONE_FP (1UL << FRAC_BITS) #define IWSUM (ONE_FP/QFQ_MAX_WSUM) -#define QFQ_MTU_SHIFT 11 +#define QFQ_MTU_SHIFT 16 /* to support TSO/GSO */ #define QFQ_MIN_SLOT_SHIFT (FRAC_BITS + QFQ_MTU_SHIFT - QFQ_MAX_INDEX) +#define QFQ_MIN_LMAX 256 /* min possible lmax for a class */ /* * Possible group states. These values are used as indexes for the bitmaps @@ -231,6 +232,32 @@ static void qfq_update_class_params(struct qfq_sched *q, struct qfq_class *cl, q->wsum += delta_w; } +static void qfq_update_reactivate_class(struct qfq_sched *q, + struct qfq_class *cl, + u32 inv_w, u32 lmax, int delta_w) +{ + bool need_reactivation = false; + int i = qfq_calc_index(inv_w, lmax); + + if (&q->groups[i] != cl->grp && cl->qdisc->q.qlen > 0) { + /* + * shift cl->F back, to not charge the + * class for the not-yet-served head + * packet + */ + cl->F = cl->S; + /* remove class from its slot in the old group */ + qfq_deactivate_class(q, cl); + need_reactivation = true; + } + + qfq_update_class_params(q, cl, lmax, inv_w, delta_w); + + if (need_reactivation) /* activate in new group */ + qfq_activate_class(q, cl, qdisc_peek_len(cl->qdisc)); +} + + static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **tca, unsigned long *arg) { @@ -238,7 +265,7 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct qfq_class *cl = (struct qfq_class *)*arg; struct nlattr *tb[TCA_QFQ_MAX + 1]; u32 weight, lmax, inv_w; - int i, err; + int err; int delta_w; if (tca[TCA_OPTIONS] == NULL) { @@ -270,16 +297,14 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, if (tb[TCA_QFQ_LMAX]) { lmax = nla_get_u32(tb[TCA_QFQ_LMAX]); - if (!lmax || lmax > (1UL << QFQ_MTU_SHIFT)) { + if (lmax < QFQ_MIN_LMAX || lmax > (1UL << QFQ_MTU_SHIFT)) { pr_notice("qfq: invalid max length %u\n", lmax); return -EINVAL; } } else - lmax = 1UL << QFQ_MTU_SHIFT; + lmax = psched_mtu(qdisc_dev(sch)); if (cl != NULL) { - bool need_reactivation = false; - if (tca[TCA_RATE]) { err = gen_replace_estimator(&cl->bstats, &cl->rate_est, qdisc_root_sleeping_lock(sch), @@ -291,24 +316,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, if (lmax == cl->lmax && inv_w == cl->inv_w) return 0; /* nothing to update */ - i = qfq_calc_index(inv_w, lmax); sch_tree_lock(sch); - if (&q->groups[i] != cl->grp && cl->qdisc->q.qlen > 0) { - /* - * shift cl->F back, to not charge the - * class for the not-yet-served head - * packet - */ - cl->F = cl->S; - /* remove class from its slot in the old group */ - qfq_deactivate_class(q, cl); - need_reactivation = true; - } - - qfq_update_class_params(q, cl, lmax, inv_w, delta_w); - - if (need_reactivation) /* activate in new group */ - qfq_activate_class(q, cl, qdisc_peek_len(cl->qdisc)); + qfq_update_reactivate_class(q, cl, inv_w, lmax, delta_w); sch_tree_unlock(sch); return 0; @@ -663,15 +672,48 @@ static void qfq_make_eligible(struct qfq_sched *q, u64 old_V) /* - * XXX we should make sure that slot becomes less than 32. - * This is guaranteed by the input values. - * roundedS is always cl->S rounded on grp->slot_shift bits. + * If the weight and lmax (max_pkt_size) of the classes do not change, + * then QFQ guarantees that the slot index is never higher than + * 2 + ((1<<QFQ_MTU_SHIFT)/QFQ_MIN_LMAX) * (QFQ_MAX_WEIGHT/QFQ_MAX_WSUM). + * + * With the current values of the above constants, the index is + * then guaranteed to never be higher than 2 + 256 * (1 / 16) = 18. + * + * When the weight of a class is increased or the lmax of the class is + * decreased, a new class with smaller slot size may happen to be + * activated. The activation of this class should be properly delayed + * to when the service of the class has finished in the ideal system + * tracked by QFQ. If the activation of the class is not delayed to + * this reference time instant, then this class may be unjustly served + * before other classes waiting for service. This may cause + * (unfrequently) the above bound to the slot index to be violated for + * some of these unlucky classes. + * + * Instead of delaying the activation of the new class, which is quite + * complex, the following inaccurate but simple solution is used: if + * the slot index is higher than QFQ_MAX_SLOTS-2, then the timestamps + * of the class are shifted backward so as to let the slot index + * become equal to QFQ_MAX_SLOTS-2. This threshold is used because, if + * the slot index is above it, then the data structure implementing + * the bucket list either gets immediately corrupted or may get + * corrupted on a possible next packet arrival that causes the start + * time of the group to be shifted backward. */ static void qfq_slot_insert(struct qfq_group *grp, struct qfq_class *cl, u64 roundedS) { u64 slot = (roundedS - grp->S) >> grp->slot_shift; - unsigned int i = (grp->front + slot) % QFQ_MAX_SLOTS; + unsigned int i; /* slot index in the bucket list */ + + if (unlikely(slot > QFQ_MAX_SLOTS - 2)) { + u64 deltaS = roundedS - grp->S - + ((u64)(QFQ_MAX_SLOTS - 2)<<grp->slot_shift); + cl->S -= deltaS; + cl->F -= deltaS; + slot = QFQ_MAX_SLOTS - 2; + } + + i = (grp->front + slot) % QFQ_MAX_SLOTS; hlist_add_head(&cl->next, &grp->slots[i]); __set_bit(slot, &grp->full_slots); @@ -892,6 +934,13 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) } pr_debug("qfq_enqueue: cl = %x\n", cl->common.classid); + if (unlikely(cl->lmax < qdisc_pkt_len(skb))) { + pr_debug("qfq: increasing maxpkt from %u to %u for class %u", + cl->lmax, qdisc_pkt_len(skb), cl->common.classid); + qfq_update_reactivate_class(q, cl, cl->inv_w, + qdisc_pkt_len(skb), 0); + } + err = qdisc_enqueue(skb, cl->qdisc); if (unlikely(err != NET_XMIT_SUCCESS)) { pr_debug("qfq_enqueue: enqueue failed %d\n", err); diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 6773d7803627..6eecf7e6338d 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -1268,14 +1268,14 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, sctp_outq_uncork(&asoc->outqueue); local_cork = 0; } - asoc = cmd->obj.ptr; + asoc = cmd->obj.asoc; /* Register with the endpoint. */ sctp_endpoint_add_asoc(ep, asoc); sctp_hash_established(asoc); break; case SCTP_CMD_UPDATE_ASSOC: - sctp_assoc_update(asoc, cmd->obj.ptr); + sctp_assoc_update(asoc, cmd->obj.asoc); break; case SCTP_CMD_PURGE_OUTQUEUE: @@ -1315,7 +1315,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, break; case SCTP_CMD_PROCESS_FWDTSN: - sctp_cmd_process_fwdtsn(&asoc->ulpq, cmd->obj.ptr); + sctp_cmd_process_fwdtsn(&asoc->ulpq, cmd->obj.chunk); break; case SCTP_CMD_GEN_SACK: @@ -1331,7 +1331,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, case SCTP_CMD_PROCESS_SACK: /* Process an inbound SACK. */ error = sctp_cmd_process_sack(commands, asoc, - cmd->obj.ptr); + cmd->obj.chunk); break; case SCTP_CMD_GEN_INIT_ACK: @@ -1352,15 +1352,15 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, * layer which will bail. */ error = sctp_cmd_process_init(commands, asoc, chunk, - cmd->obj.ptr, gfp); + cmd->obj.init, gfp); break; case SCTP_CMD_GEN_COOKIE_ECHO: /* Generate a COOKIE ECHO chunk. */ new_obj = sctp_make_cookie_echo(asoc, chunk); if (!new_obj) { - if (cmd->obj.ptr) - sctp_chunk_free(cmd->obj.ptr); + if (cmd->obj.chunk) + sctp_chunk_free(cmd->obj.chunk); goto nomem; } sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, @@ -1369,9 +1369,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, /* If there is an ERROR chunk to be sent along with * the COOKIE_ECHO, send it, too. */ - if (cmd->obj.ptr) + if (cmd->obj.chunk) sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, - SCTP_CHUNK(cmd->obj.ptr)); + SCTP_CHUNK(cmd->obj.chunk)); if (new_obj->transport) { new_obj->transport->init_sent_count++; @@ -1417,18 +1417,18 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, case SCTP_CMD_CHUNK_ULP: /* Send a chunk to the sockets layer. */ SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n", - "chunk_up:", cmd->obj.ptr, + "chunk_up:", cmd->obj.chunk, "ulpq:", &asoc->ulpq); - sctp_ulpq_tail_data(&asoc->ulpq, cmd->obj.ptr, + sctp_ulpq_tail_data(&asoc->ulpq, cmd->obj.chunk, GFP_ATOMIC); break; case SCTP_CMD_EVENT_ULP: /* Send a notification to the sockets layer. */ SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n", - "event_up:",cmd->obj.ptr, + "event_up:",cmd->obj.ulpevent, "ulpq:",&asoc->ulpq); - sctp_ulpq_tail_event(&asoc->ulpq, cmd->obj.ptr); + sctp_ulpq_tail_event(&asoc->ulpq, cmd->obj.ulpevent); break; case SCTP_CMD_REPLY: @@ -1438,12 +1438,12 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, local_cork = 1; } /* Send a chunk to our peer. */ - error = sctp_outq_tail(&asoc->outqueue, cmd->obj.ptr); + error = sctp_outq_tail(&asoc->outqueue, cmd->obj.chunk); break; case SCTP_CMD_SEND_PKT: /* Send a full packet to our peer. */ - packet = cmd->obj.ptr; + packet = cmd->obj.packet; sctp_packet_transmit(packet); sctp_ootb_pkt_free(packet); break; @@ -1480,7 +1480,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, break; case SCTP_CMD_SETUP_T2: - sctp_cmd_setup_t2(commands, asoc, cmd->obj.ptr); + sctp_cmd_setup_t2(commands, asoc, cmd->obj.chunk); break; case SCTP_CMD_TIMER_START_ONCE: @@ -1514,7 +1514,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, break; case SCTP_CMD_INIT_CHOOSE_TRANSPORT: - chunk = cmd->obj.ptr; + chunk = cmd->obj.chunk; t = sctp_assoc_choose_alter_transport(asoc, asoc->init_last_sent_to); asoc->init_last_sent_to = t; @@ -1665,17 +1665,16 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, break; case SCTP_CMD_PART_DELIVER: - sctp_ulpq_partial_delivery(&asoc->ulpq, cmd->obj.ptr, - GFP_ATOMIC); + sctp_ulpq_partial_delivery(&asoc->ulpq, GFP_ATOMIC); break; case SCTP_CMD_RENEGE: - sctp_ulpq_renege(&asoc->ulpq, cmd->obj.ptr, + sctp_ulpq_renege(&asoc->ulpq, cmd->obj.chunk, GFP_ATOMIC); break; case SCTP_CMD_SETUP_T4: - sctp_cmd_setup_t4(commands, asoc, cmd->obj.ptr); + sctp_cmd_setup_t4(commands, asoc, cmd->obj.chunk); break; case SCTP_CMD_PROCESS_OPERR: @@ -1734,8 +1733,8 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, break; default: - pr_warn("Impossible command: %u, %p\n", - cmd->verb, cmd->obj.ptr); + pr_warn("Impossible command: %u\n", + cmd->verb); break; } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index fa81bdee00a5..15379acd9c08 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -973,7 +973,7 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk, void *addr_buf; struct sctp_af *af; - SCTP_DEBUG_PRINTK("sctp_setsocktopt_bindx: sk %p addrs %p" + SCTP_DEBUG_PRINTK("sctp_setsockopt_bindx: sk %p addrs %p" " addrs_size %d opt %d\n", sk, addrs, addrs_size, op); if (unlikely(addrs_size <= 0)) diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index 360d8697b95c..ada17464b65b 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -997,7 +997,6 @@ static __u16 sctp_ulpq_renege_frags(struct sctp_ulpq *ulpq, __u16 needed) /* Partial deliver the first message as there is pressure on rwnd. */ void sctp_ulpq_partial_delivery(struct sctp_ulpq *ulpq, - struct sctp_chunk *chunk, gfp_t gfp) { struct sctp_ulpevent *event; @@ -1060,7 +1059,7 @@ void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk, sctp_tsnmap_mark(&asoc->peer.tsn_map, tsn, chunk->transport); sctp_ulpq_tail_data(ulpq, chunk, gfp); - sctp_ulpq_partial_delivery(ulpq, chunk, gfp); + sctp_ulpq_partial_delivery(ulpq, gfp); } sk_mem_reclaim(asoc->base.sk); diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 2a68bb3db772..fc2f7aa4dca7 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -1409,11 +1409,11 @@ static ssize_t read_flush(struct file *file, char __user *buf, size_t count, loff_t *ppos, struct cache_detail *cd) { - char tbuf[20]; + char tbuf[22]; unsigned long p = *ppos; size_t len; - sprintf(tbuf, "%lu\n", convert_to_wallclock(cd->flush_time)); + snprintf(tbuf, sizeof(tbuf), "%lu\n", convert_to_wallclock(cd->flush_time)); len = strlen(tbuf); if (p >= len) return 0; diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index aaaadfbe36e9..75853cabf4c9 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -254,7 +254,6 @@ struct sock_xprt { void (*old_data_ready)(struct sock *, int); void (*old_state_change)(struct sock *); void (*old_write_space)(struct sock *); - void (*old_error_report)(struct sock *); }; /* @@ -737,10 +736,10 @@ static int xs_tcp_send_request(struct rpc_task *task) dprintk("RPC: sendmsg returned unrecognized error %d\n", -status); case -ECONNRESET: - case -EPIPE: xs_tcp_shutdown(xprt); case -ECONNREFUSED: case -ENOTCONN: + case -EPIPE: clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags); } @@ -781,7 +780,6 @@ static void xs_save_old_callbacks(struct sock_xprt *transport, struct sock *sk) transport->old_data_ready = sk->sk_data_ready; transport->old_state_change = sk->sk_state_change; transport->old_write_space = sk->sk_write_space; - transport->old_error_report = sk->sk_error_report; } static void xs_restore_old_callbacks(struct sock_xprt *transport, struct sock *sk) @@ -789,7 +787,6 @@ static void xs_restore_old_callbacks(struct sock_xprt *transport, struct sock *s sk->sk_data_ready = transport->old_data_ready; sk->sk_state_change = transport->old_state_change; sk->sk_write_space = transport->old_write_space; - sk->sk_error_report = transport->old_error_report; } static void xs_reset_transport(struct sock_xprt *transport) @@ -1453,7 +1450,7 @@ static void xs_tcp_cancel_linger_timeout(struct rpc_xprt *xprt) xprt_clear_connecting(xprt); } -static void xs_sock_mark_closed(struct rpc_xprt *xprt) +static void xs_sock_reset_connection_flags(struct rpc_xprt *xprt) { smp_mb__before_clear_bit(); clear_bit(XPRT_CONNECTION_ABORT, &xprt->state); @@ -1461,6 +1458,11 @@ static void xs_sock_mark_closed(struct rpc_xprt *xprt) clear_bit(XPRT_CLOSE_WAIT, &xprt->state); clear_bit(XPRT_CLOSING, &xprt->state); smp_mb__after_clear_bit(); +} + +static void xs_sock_mark_closed(struct rpc_xprt *xprt) +{ + xs_sock_reset_connection_flags(xprt); /* Mark transport as closed and wake up all pending tasks */ xprt_disconnect_done(xprt); } @@ -1516,6 +1518,7 @@ static void xs_tcp_state_change(struct sock *sk) case TCP_CLOSE_WAIT: /* The server initiated a shutdown of the socket */ xprt->connect_cookie++; + clear_bit(XPRT_CONNECTED, &xprt->state); xs_tcp_force_close(xprt); case TCP_CLOSING: /* @@ -1540,25 +1543,6 @@ static void xs_tcp_state_change(struct sock *sk) read_unlock_bh(&sk->sk_callback_lock); } -/** - * xs_error_report - callback mainly for catching socket errors - * @sk: socket - */ -static void xs_error_report(struct sock *sk) -{ - struct rpc_xprt *xprt; - - read_lock_bh(&sk->sk_callback_lock); - if (!(xprt = xprt_from_sock(sk))) - goto out; - dprintk("RPC: %s client %p...\n" - "RPC: error %d\n", - __func__, xprt, sk->sk_err); - xprt_wake_pending_tasks(xprt, -EAGAIN); -out: - read_unlock_bh(&sk->sk_callback_lock); -} - static void xs_write_space(struct sock *sk) { struct socket *sock; @@ -1858,7 +1842,6 @@ static int xs_local_finish_connecting(struct rpc_xprt *xprt, sk->sk_user_data = xprt; sk->sk_data_ready = xs_local_data_ready; sk->sk_write_space = xs_udp_write_space; - sk->sk_error_report = xs_error_report; sk->sk_allocation = GFP_ATOMIC; xprt_clear_connected(xprt); @@ -1983,7 +1966,6 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) sk->sk_user_data = xprt; sk->sk_data_ready = xs_udp_data_ready; sk->sk_write_space = xs_udp_write_space; - sk->sk_error_report = xs_error_report; sk->sk_no_check = UDP_CSUM_NORCV; sk->sk_allocation = GFP_ATOMIC; @@ -2050,10 +2032,8 @@ static void xs_abort_connection(struct sock_xprt *transport) any.sa_family = AF_UNSPEC; result = kernel_connect(transport->sock, &any, sizeof(any), 0); if (!result) - xs_sock_mark_closed(&transport->xprt); - else - dprintk("RPC: AF_UNSPEC connect return code %d\n", - result); + xs_sock_reset_connection_flags(&transport->xprt); + dprintk("RPC: AF_UNSPEC connect return code %d\n", result); } static void xs_tcp_reuse_connection(struct sock_xprt *transport) @@ -2098,7 +2078,6 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) sk->sk_data_ready = xs_tcp_data_ready; sk->sk_state_change = xs_tcp_state_change; sk->sk_write_space = xs_tcp_write_space; - sk->sk_error_report = xs_error_report; sk->sk_allocation = GFP_ATOMIC; /* socket options */ diff --git a/net/tipc/handler.c b/net/tipc/handler.c index 111ff8300ae5..b36f0fcd9bdf 100644 --- a/net/tipc/handler.c +++ b/net/tipc/handler.c @@ -116,7 +116,6 @@ void tipc_handler_stop(void) return; handler_enabled = 0; - tasklet_disable(&tipc_tasklet); tasklet_kill(&tipc_tasklet); spin_lock_bh(&qitem_lock); diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 0f7e0d621ab0..a761670af31d 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -10,11 +10,13 @@ obj-$(CONFIG_WEXT_SPY) += wext-spy.o obj-$(CONFIG_WEXT_PRIV) += wext-priv.o cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o -cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o +cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o +CFLAGS_trace.o := -I$(src) + ccflags-y += -D__CHECK_ENDIAN__ $(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk diff --git a/net/wireless/ap.c b/net/wireless/ap.c index fcc60d8dbefa..e143505f05bc 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -3,6 +3,7 @@ #include <net/cfg80211.h> #include "nl80211.h" #include "core.h" +#include "rdev-ops.h" static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, @@ -23,7 +24,7 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, if (!wdev->beacon_interval) return -ENOENT; - err = rdev->ops->stop_ap(&rdev->wiphy, dev); + err = rdev_stop_ap(rdev, dev); if (!err) { wdev->beacon_interval = 0; wdev->channel = NULL; diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 2f876b9ee344..48febd2160ba 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -9,6 +9,7 @@ #include <linux/export.h> #include <net/cfg80211.h> #include "core.h" +#include "rdev-ops.h" struct ieee80211_channel * rdev_freq_to_chan(struct cfg80211_registered_device *rdev, @@ -52,6 +53,8 @@ bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, struct ieee80211_channel *sec_chan; int diff; + trace_cfg80211_can_beacon_sec_chan(wiphy, chan, channel_type); + switch (channel_type) { case NL80211_CHAN_HT40PLUS: diff = 20; @@ -60,20 +63,25 @@ bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, diff = -20; break; default: + trace_cfg80211_return_bool(true); return true; } sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff); - if (!sec_chan) + if (!sec_chan) { + trace_cfg80211_return_bool(false); return false; + } /* we'll need a DFS capability later */ if (sec_chan->flags & (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_RADAR)) + IEEE80211_CHAN_RADAR)) { + trace_cfg80211_return_bool(false); return false; - + } + trace_cfg80211_return_bool(true); return true; } EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan); @@ -92,7 +100,7 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, if (!chan) return -EINVAL; - return rdev->ops->set_monitor_channel(&rdev->wiphy, chan, chantype); + return rdev_set_monitor_channel(rdev, chan, chantype); } void diff --git a/net/wireless/core.c b/net/wireless/core.c index 443d4d7deea2..26711f46a3be 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -26,6 +26,7 @@ #include "debugfs.h" #include "wext-compat.h" #include "ethtool.h" +#include "rdev-ops.h" /* name for sysfs, %d is appended */ #define PHY_NAME "phy" @@ -216,7 +217,7 @@ static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) { struct cfg80211_registered_device *rdev = data; - rdev->ops->rfkill_poll(&rdev->wiphy); + rdev_rfkill_poll(rdev); } static int cfg80211_rfkill_set_block(void *data, bool blocked) @@ -240,7 +241,7 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked) case NL80211_IFTYPE_P2P_DEVICE: if (!wdev->p2p_started) break; - rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); + rdev_stop_p2p_device(rdev, wdev); wdev->p2p_started = false; rdev->opencount--; break; @@ -370,6 +371,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy.rts_threshold = (u32) -1; rdev->wiphy.coverage_class = 0; + rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH; + return &rdev->wiphy; } EXPORT_SYMBOL(wiphy_new); @@ -526,8 +529,7 @@ int wiphy_register(struct wiphy *wiphy) for (i = 0; i < sband->n_channels; i++) { sband->channels[i].orig_flags = sband->channels[i].flags; - sband->channels[i].orig_mag = - sband->channels[i].max_antenna_gain; + sband->channels[i].orig_mag = INT_MAX; sband->channels[i].orig_mpwr = sband->channels[i].max_power; sband->channels[i].band = band; @@ -688,7 +690,7 @@ void wiphy_unregister(struct wiphy *wiphy) flush_work(&rdev->event_work); if (rdev->wowlan && rdev->ops->set_wakeup) - rdev->ops->set_wakeup(&rdev->wiphy, false); + rdev_set_wakeup(rdev, false); cfg80211_rdev_free_wowlan(rdev); } EXPORT_SYMBOL(wiphy_unregister); @@ -771,7 +773,7 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev) case NL80211_IFTYPE_P2P_DEVICE: if (!wdev->p2p_started) break; - rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); + rdev_stop_p2p_device(rdev, wdev); wdev->p2p_started = false; rdev->opencount--; break; @@ -962,9 +964,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, if ((wdev->iftype == NL80211_IFTYPE_STATION || wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && rdev->ops->set_power_mgmt) - if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, - wdev->ps, - wdev->ps_timeout)) { + if (rdev_set_power_mgmt(rdev, dev, wdev->ps, + wdev->ps_timeout)) { /* assume this means it's off */ wdev->ps = false; } diff --git a/net/wireless/core.h b/net/wireless/core.h index a343be4a52bd..b8eb743fe7da 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -320,13 +320,15 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx); + const u8 *key, int key_len, int key_idx, + const u8 *sae_data, int sae_data_len); int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx); + const u8 *key, int key_len, int key_idx, + const u8 *sae_data, int sae_data_len); int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, diff --git a/net/wireless/ethtool.c b/net/wireless/ethtool.c index 7eecdf40cf80..48c48ffafa1d 100644 --- a/net/wireless/ethtool.c +++ b/net/wireless/ethtool.c @@ -2,6 +2,7 @@ #include <net/cfg80211.h> #include "core.h" #include "ethtool.h" +#include "rdev-ops.h" static void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) @@ -47,9 +48,8 @@ static void cfg80211_get_ringparam(struct net_device *dev, memset(rp, 0, sizeof(*rp)); if (rdev->ops->get_ringparam) - rdev->ops->get_ringparam(wdev->wiphy, - &rp->tx_pending, &rp->tx_max_pending, - &rp->rx_pending, &rp->rx_max_pending); + rdev_get_ringparam(rdev, &rp->tx_pending, &rp->tx_max_pending, + &rp->rx_pending, &rp->rx_max_pending); } static int cfg80211_set_ringparam(struct net_device *dev, @@ -62,8 +62,7 @@ static int cfg80211_set_ringparam(struct net_device *dev, return -EINVAL; if (rdev->ops->set_ringparam) - return rdev->ops->set_ringparam(wdev->wiphy, - rp->tx_pending, rp->rx_pending); + return rdev_set_ringparam(rdev, rp->tx_pending, rp->rx_pending); return -ENOTSUPP; } @@ -73,7 +72,7 @@ static int cfg80211_get_sset_count(struct net_device *dev, int sset) struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); if (rdev->ops->get_et_sset_count) - return rdev->ops->get_et_sset_count(wdev->wiphy, dev, sset); + return rdev_get_et_sset_count(rdev, dev, sset); return -EOPNOTSUPP; } @@ -83,7 +82,7 @@ static void cfg80211_get_stats(struct net_device *dev, struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); if (rdev->ops->get_et_stats) - rdev->ops->get_et_stats(wdev->wiphy, dev, stats, data); + rdev_get_et_stats(rdev, dev, stats, data); } static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data) @@ -91,7 +90,7 @@ static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data) struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); if (rdev->ops->get_et_strings) - rdev->ops->get_et_strings(wdev->wiphy, dev, sset, data); + rdev_get_et_strings(rdev, dev, sset, data); } const struct ethtool_ops cfg80211_ethtool_ops = { diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index ca5672f6ee2f..27941d5db72b 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -11,6 +11,7 @@ #include <net/cfg80211.h> #include "wext-compat.h" #include "nl80211.h" +#include "rdev-ops.h" void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) @@ -61,6 +62,8 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) struct cfg80211_event *ev; unsigned long flags; + trace_cfg80211_ibss_joined(dev, bssid); + CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING); ev = kzalloc(sizeof(*ev), gfp); @@ -128,7 +131,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, return err; } - err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); + err = rdev_join_ibss(rdev, dev, params); if (err) { wdev->connect_keys = NULL; wdev->sme_state = CFG80211_SME_IDLE; @@ -175,7 +178,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) */ if (rdev->ops->del_key) for (i = 0; i < 6; i++) - rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL); + rdev_del_key(rdev, dev, i, false, NULL); if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); @@ -211,7 +214,7 @@ int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, if (!wdev->ssid_len) return -ENOLINK; - err = rdev->ops->leave_ibss(&rdev->wiphy, dev); + err = rdev_leave_ibss(rdev, dev); if (err) return err; diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index c384e77ff77a..966cfc4cd79d 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -3,6 +3,7 @@ #include <net/cfg80211.h> #include "nl80211.h" #include "core.h" +#include "rdev-ops.h" /* Default values, timeouts in ms */ #define MESH_TTL 31 @@ -160,7 +161,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (err) return err; - err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup); + err = rdev_join_mesh(rdev, dev, conf, setup); if (!err) { memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); wdev->mesh_id_len = setup->mesh_id_len; @@ -220,9 +221,8 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, if (err) return err; - err = rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, - wdev->netdev, - channel); + err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev, + channel); if (!err) wdev->channel = channel; @@ -242,6 +242,7 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev, { struct wireless_dev *wdev = dev->ieee80211_ptr; + trace_cfg80211_notify_new_peer_candidate(dev, macaddr); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT)) return; @@ -267,7 +268,7 @@ static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, if (!wdev->mesh_id_len) return -ENOTCONN; - err = rdev->ops->leave_mesh(&rdev->wiphy, dev); + err = rdev_leave_mesh(rdev, dev); if (!err) { wdev->mesh_id_len = 0; wdev->channel = NULL; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 8016fee0752b..4bfd14f7c592 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -15,6 +15,8 @@ #include <net/iw_handler.h> #include "core.h" #include "nl80211.h" +#include "rdev-ops.h" + void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) { @@ -22,6 +24,7 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_send_rx_auth(dev); wdev_lock(wdev); nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); @@ -42,6 +45,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, u8 *ie = mgmt->u.assoc_resp.variable; int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); + trace_cfg80211_send_rx_assoc(dev, bss); wdev_lock(wdev); status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); @@ -98,6 +102,7 @@ void __cfg80211_send_deauth(struct net_device *dev, const u8 *bssid = mgmt->bssid; bool was_current = false; + trace___cfg80211_send_deauth(dev); ASSERT_WDEV_LOCK(wdev); if (wdev->current_bss && @@ -147,6 +152,7 @@ void __cfg80211_send_disassoc(struct net_device *dev, u16 reason_code; bool from_ap; + trace___cfg80211_send_disassoc(dev); ASSERT_WDEV_LOCK(wdev); nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); @@ -188,6 +194,7 @@ void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_send_unprot_deauth(dev); nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC); } EXPORT_SYMBOL(cfg80211_send_unprot_deauth); @@ -199,6 +206,7 @@ void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_send_unprot_disassoc(dev); nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC); } EXPORT_SYMBOL(cfg80211_send_unprot_disassoc); @@ -209,6 +217,7 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_send_auth_timeout(dev, addr); wdev_lock(wdev); nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); @@ -227,6 +236,7 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_send_assoc_timeout(dev, addr); wdev_lock(wdev); nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); @@ -261,6 +271,7 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, } #endif + trace_cfg80211_michael_mic_failure(dev, addr, key_type, key_id, tsc); nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp); } EXPORT_SYMBOL(cfg80211_michael_mic_failure); @@ -273,7 +284,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx) + const u8 *key, int key_len, int key_idx, + const u8 *sae_data, int sae_data_len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_auth_request req; @@ -293,6 +305,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, req.ie = ie; req.ie_len = ie_len; + req.sae_data = sae_data; + req.sae_data_len = sae_data_len; req.auth_type = auth_type; req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); @@ -307,7 +321,7 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, if (err) goto out; - err = rdev->ops->auth(&rdev->wiphy, dev, &req); + err = rdev_auth(rdev, dev, &req); out: cfg80211_put_bss(req.bss); @@ -319,7 +333,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx) + const u8 *key, int key_len, int key_idx, + const u8 *sae_data, int sae_data_len) { int err; @@ -327,7 +342,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, wdev_lock(dev->ieee80211_ptr); err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len, - key, key_len, key_idx); + key, key_len, key_idx, + sae_data, sae_data_len); wdev_unlock(dev->ieee80211_ptr); mutex_unlock(&rdev->devlist_mtx); @@ -410,7 +426,7 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, if (err) goto out; - err = rdev->ops->assoc(&rdev->wiphy, dev, &req); + err = rdev_assoc(rdev, dev, &req); out: if (err) { @@ -457,22 +473,16 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, .reason_code = reason, .ie = ie, .ie_len = ie_len, + .local_state_change = local_state_change, }; ASSERT_WDEV_LOCK(wdev); - if (local_state_change) { - if (wdev->current_bss && - ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - wdev->current_bss = NULL; - } - + if (local_state_change && (!wdev->current_bss || + !ether_addr_equal(wdev->current_bss->pub.bssid, bssid))) return 0; - } - return rdev->ops->deauth(&rdev->wiphy, dev, &req); + return rdev_deauth(rdev, dev, &req); } int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, @@ -517,7 +527,7 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, else return -ENOTCONN; - return rdev->ops->disassoc(&rdev->wiphy, dev, &req); + return rdev_disassoc(rdev, dev, &req); } int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, @@ -558,7 +568,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); req.bssid = bssid; - rdev->ops->deauth(&rdev->wiphy, dev, &req); + rdev_deauth(rdev, dev, &req); if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); @@ -575,6 +585,8 @@ void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_ready_on_channel(wdev, cookie, chan, channel_type, + duration); nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, channel_type, duration, gfp); } @@ -588,6 +600,8 @@ void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan, + channel_type); nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan, channel_type, gfp); } @@ -599,6 +613,7 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_new_sta(dev, mac_addr, sinfo); nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp); } EXPORT_SYMBOL(cfg80211_new_sta); @@ -608,6 +623,7 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp) struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_del_sta(dev, mac_addr); nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp); } EXPORT_SYMBOL(cfg80211_del_sta); @@ -688,7 +704,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, list_add(&nreg->list, &wdev->mgmt_registrations); if (rdev->ops->mgmt_frame_register) - rdev->ops->mgmt_frame_register(wiphy, wdev, frame_type, true); + rdev_mgmt_frame_register(rdev, wdev, frame_type, true); out: spin_unlock_bh(&wdev->mgmt_registrations_lock); @@ -711,8 +727,8 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid) if (rdev->ops->mgmt_frame_register) { u16 frame_type = le16_to_cpu(reg->frame_type); - rdev->ops->mgmt_frame_register(wiphy, wdev, - frame_type, false); + rdev_mgmt_frame_register(rdev, wdev, + frame_type, false); } list_del(®->list); @@ -838,10 +854,10 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, return -EINVAL; /* Transmit the Action frame as requested by user space */ - return rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan, - channel_type, channel_type_valid, - wait, buf, len, no_cck, dont_wait_for_ack, - cookie); + return rdev_mgmt_tx(rdev, wdev, chan, offchan, + channel_type, channel_type_valid, + wait, buf, len, no_cck, dont_wait_for_ack, + cookie); } bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, @@ -860,10 +876,13 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE); u16 stype; + trace_cfg80211_rx_mgmt(wdev, freq, sig_mbm); stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4; - if (!(stypes->rx & BIT(stype))) + if (!(stypes->rx & BIT(stype))) { + trace_cfg80211_return_bool(false); return false; + } data = buf + ieee80211_hdrlen(mgmt->frame_control); data_len = len - ieee80211_hdrlen(mgmt->frame_control); @@ -894,6 +913,7 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, spin_unlock_bh(&wdev->mgmt_registrations_lock); + trace_cfg80211_return_bool(result); return result; } EXPORT_SYMBOL(cfg80211_rx_mgmt); @@ -904,6 +924,8 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_mgmt_tx_status(wdev, cookie, ack); + /* Indicate TX status of the Action frame to user space */ nl80211_send_mgmt_tx_status(rdev, wdev, cookie, buf, len, ack, gfp); } @@ -917,6 +939,8 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_cqm_rssi_notify(dev, rssi_event); + /* Indicate roaming trigger event to user space */ nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp); } @@ -929,6 +953,8 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets); + /* Indicate roaming trigger event to user space */ nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp); } @@ -954,6 +980,7 @@ void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_gtk_rekey_notify(dev, bssid); nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp); } EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); @@ -965,6 +992,7 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth); nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); } EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); @@ -977,6 +1005,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev, int freq, struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_channel *chan; + trace_cfg80211_ch_switch_notify(dev, freq, type); + wdev_lock(wdev); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && @@ -999,12 +1029,18 @@ bool cfg80211_rx_spurious_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; + bool ret; + + trace_cfg80211_rx_spurious_frame(dev, addr); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO)) + wdev->iftype != NL80211_IFTYPE_P2P_GO)) { + trace_cfg80211_return_bool(false); return false; - - return nl80211_unexpected_frame(dev, addr, gfp); + } + ret = nl80211_unexpected_frame(dev, addr, gfp); + trace_cfg80211_return_bool(ret); + return ret; } EXPORT_SYMBOL(cfg80211_rx_spurious_frame); @@ -1012,12 +1048,18 @@ bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; + bool ret; + + trace_cfg80211_rx_unexpected_4addr_frame(dev, addr); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO && - wdev->iftype != NL80211_IFTYPE_AP_VLAN)) + wdev->iftype != NL80211_IFTYPE_AP_VLAN)) { + trace_cfg80211_return_bool(false); return false; - - return nl80211_unexpected_4addr_frame(dev, addr, gfp); + } + ret = nl80211_unexpected_4addr_frame(dev, addr, gfp); + trace_cfg80211_return_bool(ret); + return ret; } EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0418a6d5c1a6..8c0857815a90 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -22,8 +22,8 @@ #include "core.h" #include "nl80211.h" #include "reg.h" +#include "rdev-ops.h" -static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type); static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, struct genl_info *info, struct cfg80211_crypto_settings *settings, @@ -355,6 +355,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, [NL80211_ATTR_WDEV] = { .type = NLA_U64 }, [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 }, + [NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, }, + [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN }, + [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 }, }; /* policy for the key attributes */ @@ -690,7 +693,7 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) static struct cfg80211_cached_keys * nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, - struct nlattr *keys) + struct nlattr *keys, bool *no_ht) { struct key_parse parse; struct nlattr *key; @@ -733,6 +736,12 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, result->params[parse.idx].key_len = parse.p.key_len; result->params[parse.idx].key = result->data[parse.idx]; memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len); + + if (parse.p.cipher == WLAN_CIPHER_SUITE_WEP40 || + parse.p.cipher == WLAN_CIPHER_SUITE_WEP104) { + if (no_ht) + *no_ht = true; + } } return result; @@ -943,7 +952,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) { u32 tx_ant = 0, rx_ant = 0; int res; - res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant); + res = rdev_get_antenna(dev, &tx_ant, &rx_ant); if (!res) { if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant) || @@ -1457,7 +1466,7 @@ static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info) return -EOPNOTSUPP; bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid); + return rdev_set_wds_peer(rdev, dev, bssid); } @@ -1562,9 +1571,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) if (result) goto bad_res; - result = rdev->ops->set_txq_params(&rdev->wiphy, - netdev, - &txq_params); + result = rdev_set_txq_params(rdev, netdev, + &txq_params); if (result) goto bad_res; } @@ -1599,7 +1607,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) mbm = nla_get_u32(info->attrs[idx]); } - result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm); + result = rdev_set_tx_power(rdev, type, mbm); if (result) goto bad_res; } @@ -1628,7 +1636,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) tx_ant = tx_ant & rdev->wiphy.available_antennas_tx; rx_ant = rx_ant & rdev->wiphy.available_antennas_rx; - result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant); + result = rdev_set_antenna(rdev, tx_ant, rx_ant); if (result) goto bad_res; } @@ -1713,7 +1721,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) if (changed & WIPHY_PARAM_COVERAGE_CLASS) rdev->wiphy.coverage_class = coverage_class; - result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); + result = rdev_set_wiphy_params(rdev, changed); if (result) { rdev->wiphy.retry_short = old_retry_short; rdev->wiphy.retry_long = old_retry_long; @@ -1765,8 +1773,7 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag struct ieee80211_channel *chan; enum nl80211_channel_type channel_type; - chan = rdev->ops->get_channel(&rdev->wiphy, wdev, - &channel_type); + chan = rdev_get_channel(rdev, wdev, &channel_type); if (chan && (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) || @@ -2014,9 +2021,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, &flags); - wdev = rdev->ops->add_virtual_intf(&rdev->wiphy, - nla_data(info->attrs[NL80211_ATTR_IFNAME]), - type, err ? NULL : &flags, ¶ms); + wdev = rdev_add_virtual_intf(rdev, + nla_data(info->attrs[NL80211_ATTR_IFNAME]), + type, err ? NULL : &flags, ¶ms); if (IS_ERR(wdev)) { nlmsg_free(msg); return PTR_ERR(wdev); @@ -2083,7 +2090,7 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) if (!wdev->netdev) info->user_ptr[1] = NULL; - return rdev->ops->del_virtual_intf(&rdev->wiphy, wdev); + return rdev_del_virtual_intf(rdev, wdev); } static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info) @@ -2100,7 +2107,7 @@ static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info) noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]); - return rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map); + return rdev_set_noack_map(rdev, dev, noack_map); } struct get_key_cookie { @@ -2210,8 +2217,8 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) return -ENOENT; - err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise, - mac_addr, &cookie, get_key_callback); + err = rdev_get_key(rdev, dev, key_idx, pairwise, mac_addr, &cookie, + get_key_callback); if (err) goto free_msg; @@ -2259,7 +2266,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) if (err) goto out; - err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx, + err = rdev_set_default_key(rdev, dev, key.idx, key.def_uni, key.def_multi); if (err) @@ -2283,8 +2290,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) if (err) goto out; - err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, - dev, key.idx); + err = rdev_set_default_mgmt_key(rdev, dev, key.idx); if (err) goto out; @@ -2340,9 +2346,9 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) wdev_lock(dev->ieee80211_ptr); err = nl80211_key_allowed(dev->ieee80211_ptr); if (!err) - err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, - key.type == NL80211_KEYTYPE_PAIRWISE, - mac_addr, &key.p); + err = rdev_add_key(rdev, dev, key.idx, + key.type == NL80211_KEYTYPE_PAIRWISE, + mac_addr, &key.p); wdev_unlock(dev->ieee80211_ptr); return err; @@ -2386,9 +2392,9 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) err = -ENOENT; if (!err) - err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, - key.type == NL80211_KEYTYPE_PAIRWISE, - mac_addr); + err = rdev_del_key(rdev, dev, key.idx, + key.type == NL80211_KEYTYPE_PAIRWISE, + mac_addr); #ifdef CONFIG_CFG80211_WEXT if (!err) { @@ -2490,6 +2496,30 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev, return ret; } +static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev, + enum nl80211_auth_type auth_type, + enum nl80211_commands cmd) +{ + if (auth_type > NL80211_AUTHTYPE_MAX) + return false; + + switch (cmd) { + case NL80211_CMD_AUTHENTICATE: + if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) && + auth_type == NL80211_AUTHTYPE_SAE) + return false; + return true; + case NL80211_CMD_CONNECT: + case NL80211_CMD_START_AP: + /* SAE not supported yet */ + if (auth_type == NL80211_AUTHTYPE_SAE) + return false; + return true; + default: + return false; + } +} + static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -2559,7 +2589,8 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { params.auth_type = nla_get_u32( info->attrs[NL80211_ATTR_AUTH_TYPE]); - if (!nl80211_valid_auth_type(params.auth_type)) + if (!nl80211_valid_auth_type(rdev, params.auth_type, + NL80211_CMD_START_AP)) return -EINVAL; } else params.auth_type = NL80211_AUTHTYPE_AUTOMATIC; @@ -2607,7 +2638,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (err) return err; - err = rdev->ops->start_ap(&rdev->wiphy, dev, ¶ms); + err = rdev_start_ap(rdev, dev, ¶ms); if (!err) { wdev->preset_chan = params.channel; wdev->preset_chantype = params.channel_type; @@ -2639,7 +2670,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) if (err) return err; - return rdev->ops->change_beacon(&rdev->wiphy, dev, ¶ms); + return rdev_change_beacon(rdev, dev, ¶ms); } static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info) @@ -2923,8 +2954,8 @@ static int nl80211_dump_station(struct sk_buff *skb, while (1) { memset(&sinfo, 0, sizeof(sinfo)); - err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx, - mac_addr, &sinfo); + err = rdev_dump_station(dev, netdev, sta_idx, + mac_addr, &sinfo); if (err == -ENOENT) break; if (err) @@ -2969,7 +3000,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->get_station) return -EOPNOTSUPP; - err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo); + err = rdev_get_station(rdev, dev, mac_addr, &sinfo); if (err) return err; @@ -3146,7 +3177,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) /* be aware of params.vlan when changing code here */ - err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, ¶ms); + err = rdev_change_station(rdev, dev, mac_addr, ¶ms); if (params.vlan) dev_put(params.vlan); @@ -3198,6 +3229,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); + if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) + params.vht_capa = + nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]); + if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) params.plink_action = nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); @@ -3275,7 +3310,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) /* be aware of params.vlan when changing code here */ - err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, ¶ms); + err = rdev_add_station(rdev, dev, mac_addr, ¶ms); if (params.vlan) dev_put(params.vlan); @@ -3300,7 +3335,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->del_station) return -EOPNOTSUPP; - return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr); + return rdev_del_station(rdev, dev, mac_addr); } static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq, @@ -3382,8 +3417,8 @@ static int nl80211_dump_mpath(struct sk_buff *skb, } while (1) { - err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx, - dst, next_hop, &pinfo); + err = rdev_dump_mpath(dev, netdev, path_idx, dst, next_hop, + &pinfo); if (err == -ENOENT) break; if (err) @@ -3430,7 +3465,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; - err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo); + err = rdev_get_mpath(rdev, dev, dst, next_hop, &pinfo); if (err) return err; @@ -3469,7 +3504,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; - return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop); + return rdev_change_mpath(rdev, dev, dst, next_hop); } static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) @@ -3494,7 +3529,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; - return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop); + return rdev_add_mpath(rdev, dev, dst, next_hop); } static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) @@ -3509,7 +3544,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->del_mpath) return -EOPNOTSUPP; - return rdev->ops->del_mpath(&rdev->wiphy, dev, dst); + return rdev_del_mpath(rdev, dev, dst); } static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) @@ -3554,7 +3589,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; - return rdev->ops->change_bss(&rdev->wiphy, dev, ¶ms); + return rdev_change_bss(rdev, dev, ¶ms); } static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { @@ -3668,8 +3703,7 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, if (!wdev->mesh_id_len) memcpy(&cur_params, &default_mesh_config, sizeof(cur_params)); else - err = rdev->ops->get_mesh_config(&rdev->wiphy, dev, - &cur_params); + err = rdev_get_mesh_config(rdev, dev, &cur_params); wdev_unlock(wdev); if (err) @@ -3971,8 +4005,7 @@ static int nl80211_update_mesh_config(struct sk_buff *skb, err = -ENOLINK; if (!err) - err = rdev->ops->update_mesh_config(&rdev->wiphy, dev, - mask, &cfg); + err = rdev_update_mesh_config(rdev, dev, mask, &cfg); wdev_unlock(wdev); @@ -4337,14 +4370,27 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) } } + if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) { + request->flags = nla_get_u32( + info->attrs[NL80211_ATTR_SCAN_FLAGS]); + if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && + !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) || + ((request->flags & NL80211_SCAN_FLAG_FLUSH) && + !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) { + err = -EOPNOTSUPP; + goto out_free; + } + } + request->no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); request->wdev = wdev; request->wiphy = &rdev->wiphy; + request->scan_start = jiffies; rdev->scan_req = request; - err = rdev->ops->scan(&rdev->wiphy, request); + err = rdev_scan(rdev, request); if (!err) { nl80211_send_scan_start(rdev, wdev); @@ -4568,11 +4614,24 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, request->ie_len); } + if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) { + request->flags = nla_get_u32( + info->attrs[NL80211_ATTR_SCAN_FLAGS]); + if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && + !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) || + ((request->flags & NL80211_SCAN_FLAG_FLUSH) && + !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) { + err = -EOPNOTSUPP; + goto out_free; + } + } + request->dev = dev; request->wiphy = &rdev->wiphy; request->interval = interval; + request->scan_start = jiffies; - err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); + err = rdev_sched_scan_start(rdev, dev, request); if (!err) { rdev->sched_scan_req = request; nl80211_send_sched_scan(rdev, dev, @@ -4815,8 +4874,7 @@ static int nl80211_dump_survey(struct sk_buff *skb, while (1) { struct ieee80211_channel *chan; - res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx, - &survey); + res = rdev_dump_survey(dev, netdev, survey_idx, &survey); if (res == -ENOENT) break; if (res) @@ -4852,11 +4910,6 @@ static int nl80211_dump_survey(struct sk_buff *skb, return res; } -static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) -{ - return auth_type <= NL80211_AUTHTYPE_MAX; -} - static bool nl80211_valid_wpa_versions(u32 wpa_versions) { return !(wpa_versions & ~(NL80211_WPA_VERSION_1 | @@ -4868,8 +4921,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct ieee80211_channel *chan; - const u8 *bssid, *ssid, *ie = NULL; - int err, ssid_len, ie_len = 0; + const u8 *bssid, *ssid, *ie = NULL, *sae_data = NULL; + int err, ssid_len, ie_len = 0, sae_data_len = 0; enum nl80211_auth_type auth_type; struct key_parse key; bool local_state_change; @@ -4945,9 +4998,23 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) } auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); - if (!nl80211_valid_auth_type(auth_type)) + if (!nl80211_valid_auth_type(rdev, auth_type, NL80211_CMD_AUTHENTICATE)) return -EINVAL; + if (auth_type == NL80211_AUTHTYPE_SAE && + !info->attrs[NL80211_ATTR_SAE_DATA]) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_SAE_DATA]) { + if (auth_type != NL80211_AUTHTYPE_SAE) + return -EINVAL; + sae_data = nla_data(info->attrs[NL80211_ATTR_SAE_DATA]); + sae_data_len = nla_len(info->attrs[NL80211_ATTR_SAE_DATA]); + /* need to include at least Auth Transaction and Status Code */ + if (sae_data_len < 4) + return -EINVAL; + } + local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; /* @@ -4959,7 +5026,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len, - key.p.key, key.p.key_len, key.idx); + key.p.key, key.p.key_len, key.idx, + sae_data, sae_data_len); } static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, @@ -5339,10 +5407,18 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) return -EINVAL; if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { + bool no_ht = false; + connkeys = nl80211_parse_connkeys(rdev, - info->attrs[NL80211_ATTR_KEYS]); + info->attrs[NL80211_ATTR_KEYS], + &no_ht); if (IS_ERR(connkeys)) return PTR_ERR(connkeys); + + if ((ibss.channel_type != NL80211_CHAN_NO_HT) && no_ht) { + kfree(connkeys); + return -EINVAL; + } } ibss.control_port = @@ -5384,7 +5460,7 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) err = -EOPNOTSUPP; if (rdev->ops->testmode_cmd) { rdev->testmode_info = info; - err = rdev->ops->testmode_cmd(&rdev->wiphy, + err = rdev_testmode_cmd(rdev, nla_data(info->attrs[NL80211_ATTR_TESTDATA]), nla_len(info->attrs[NL80211_ATTR_TESTDATA])); rdev->testmode_info = NULL; @@ -5466,8 +5542,7 @@ static int nl80211_testmode_dump(struct sk_buff *skb, genlmsg_cancel(skb, hdr); break; } - err = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb, - data, data_len); + err = rdev_testmode_dump(rdev, skb, cb, data, data_len); nla_nest_end(skb, tmdata); if (err == -ENOBUFS || err == -ENOENT) { @@ -5596,7 +5671,8 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { connect.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); - if (!nl80211_valid_auth_type(connect.auth_type)) + if (!nl80211_valid_auth_type(rdev, connect.auth_type, + NL80211_CMD_CONNECT)) return -EINVAL; } else connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; @@ -5642,7 +5718,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) { connkeys = nl80211_parse_connkeys(rdev, - info->attrs[NL80211_ATTR_KEYS]); + info->attrs[NL80211_ATTR_KEYS], NULL); if (IS_ERR(connkeys)) return PTR_ERR(connkeys); } @@ -5771,7 +5847,7 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->flush_pmksa) return -EOPNOTSUPP; - return rdev->ops->flush_pmksa(&rdev->wiphy, dev); + return rdev_flush_pmksa(rdev, dev); } static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) @@ -5798,10 +5874,10 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]); dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); - return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, - dialog_token, status_code, - nla_data(info->attrs[NL80211_ATTR_IE]), - nla_len(info->attrs[NL80211_ATTR_IE])); + return rdev_tdls_mgmt(rdev, dev, peer, action_code, + dialog_token, status_code, + nla_data(info->attrs[NL80211_ATTR_IE]), + nla_len(info->attrs[NL80211_ATTR_IE])); } static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info) @@ -5822,7 +5898,7 @@ static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info) operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]); peer = nla_data(info->attrs[NL80211_ATTR_MAC]); - return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation); + return rdev_tdls_oper(rdev, dev, peer, operation); } static int nl80211_remain_on_channel(struct sk_buff *skb, @@ -5877,8 +5953,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, goto free_msg; } - err = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan, - channel_type, duration, &cookie); + err = rdev_remain_on_channel(rdev, wdev, chan, channel_type, duration, + &cookie); if (err) goto free_msg; @@ -5912,7 +5988,7 @@ static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); - return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, wdev, cookie); + return rdev_cancel_remain_on_channel(rdev, wdev, cookie); } static u32 rateset_to_mask(struct ieee80211_supported_band *sband, @@ -6055,7 +6131,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, } } - return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask); + return rdev_set_bitrate_mask(rdev, dev, NULL, &mask); } static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) @@ -6230,7 +6306,7 @@ static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *in cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); - return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie); + return rdev_mgmt_tx_cancel_wait(rdev, wdev, cookie); } static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) @@ -6260,8 +6336,7 @@ static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) if (state == wdev->ps) return 0; - err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state, - wdev->ps_timeout); + err = rdev_set_power_mgmt(rdev, dev, state, wdev->ps_timeout); if (!err) wdev->ps = state; return err; @@ -6341,8 +6416,7 @@ static int nl80211_set_cqm_txe(struct genl_info *info, wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; - return rdev->ops->set_cqm_txe_config(wdev->wiphy, dev, - rate, pkts, intvl); + return rdev_set_cqm_txe_config(rdev, dev, rate, pkts, intvl); } static int nl80211_set_cqm_rssi(struct genl_info *info, @@ -6364,8 +6438,7 @@ static int nl80211_set_cqm_rssi(struct genl_info *info, wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; - return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev, - threshold, hysteresis); + return rdev_set_cqm_rssi_config(rdev, dev, threshold, hysteresis); } static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) @@ -6690,7 +6763,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) set_wakeup: if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan) - rdev->ops->set_wakeup(&rdev->wiphy, rdev->wowlan); + rdev_set_wakeup(rdev, rdev->wowlan); return 0; error: @@ -6746,7 +6819,7 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) goto out; } - err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data); + err = rdev_set_rekey_data(rdev, dev, &rekey_data); out: wdev_unlock(wdev); return err; @@ -6805,7 +6878,7 @@ static int nl80211_probe_client(struct sk_buff *skb, addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie); + err = rdev_probe_client(rdev, dev, addr, &cookie); if (err) goto free_msg; @@ -6859,7 +6932,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) if (err) return err; - err = rdev->ops->start_p2p_device(&rdev->wiphy, wdev); + err = rdev_start_p2p_device(rdev, wdev); if (err) return err; @@ -6885,7 +6958,7 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info) if (!wdev->p2p_started) return 0; - rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); + rdev_stop_p2p_device(rdev, wdev); wdev->p2p_started = false; mutex_lock(&rdev->devlist_mtx); @@ -7622,6 +7695,9 @@ static int nl80211_add_scan_req(struct sk_buff *msg, nla_put(msg, NL80211_ATTR_IE, req->ie_len, req->ie)) goto nla_put_failure; + if (req->flags) + nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags); + return 0; nla_put_failure: return -ENOBUFS; @@ -8800,7 +8876,10 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, void *hdr; int err; + trace_cfg80211_probe_status(dev, addr, cookie, acked); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) return; @@ -8842,6 +8921,8 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy, void *hdr; u32 nlportid = ACCESS_ONCE(rdev->ap_beacons_nlportid); + trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm); + if (!nlportid) return; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h new file mode 100644 index 000000000000..eb5f8974e148 --- /dev/null +++ b/net/wireless/rdev-ops.h @@ -0,0 +1,879 @@ +#ifndef __CFG80211_RDEV_OPS +#define __CFG80211_RDEV_OPS + +#include <linux/rtnetlink.h> +#include <net/cfg80211.h> +#include "core.h" +#include "trace.h" + +static inline int rdev_suspend(struct cfg80211_registered_device *rdev) +{ + int ret; + trace_rdev_suspend(&rdev->wiphy, rdev->wowlan); + ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_resume(struct cfg80211_registered_device *rdev) +{ + int ret; + trace_rdev_resume(&rdev->wiphy); + ret = rdev->ops->resume(&rdev->wiphy); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline void rdev_set_wakeup(struct cfg80211_registered_device *rdev, + bool enabled) +{ + trace_rdev_set_wakeup(&rdev->wiphy, enabled); + rdev->ops->set_wakeup(&rdev->wiphy, enabled); + trace_rdev_return_void(&rdev->wiphy); +} + +static inline struct wireless_dev +*rdev_add_virtual_intf(struct cfg80211_registered_device *rdev, char *name, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct wireless_dev *ret; + trace_rdev_add_virtual_intf(&rdev->wiphy, name, type); + ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, type, flags, + params); + trace_rdev_return_wdev(&rdev->wiphy, ret); + return ret; +} + +static inline int +rdev_del_virtual_intf(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + int ret; + trace_rdev_del_virtual_intf(&rdev->wiphy, wdev); + ret = rdev->ops->del_virtual_intf(&rdev->wiphy, wdev); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int +rdev_change_virtual_intf(struct cfg80211_registered_device *rdev, + struct net_device *dev, enum nl80211_iftype type, + u32 *flags, struct vif_params *params) +{ + int ret; + trace_rdev_change_virtual_intf(&rdev->wiphy, dev, type); + ret = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, type, flags, + params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_add_key(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u8 key_index, + bool pairwise, const u8 *mac_addr, + struct key_params *params) +{ + int ret; + trace_rdev_add_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr); + ret = rdev->ops->add_key(&rdev->wiphy, netdev, key_index, pairwise, + mac_addr, params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int +rdev_get_key(struct cfg80211_registered_device *rdev, struct net_device *netdev, + u8 key_index, bool pairwise, const u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, struct key_params*)) +{ + int ret; + trace_rdev_get_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr); + ret = rdev->ops->get_key(&rdev->wiphy, netdev, key_index, pairwise, + mac_addr, cookie, callback); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_del_key(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u8 key_index, + bool pairwise, const u8 *mac_addr) +{ + int ret; + trace_rdev_del_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr); + ret = rdev->ops->del_key(&rdev->wiphy, netdev, key_index, pairwise, + mac_addr); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int +rdev_set_default_key(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u8 key_index, bool unicast, + bool multicast) +{ + int ret; + trace_rdev_set_default_key(&rdev->wiphy, netdev, key_index, + unicast, multicast); + ret = rdev->ops->set_default_key(&rdev->wiphy, netdev, key_index, + unicast, multicast); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int +rdev_set_default_mgmt_key(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u8 key_index) +{ + int ret; + trace_rdev_set_default_mgmt_key(&rdev->wiphy, netdev, key_index); + ret = rdev->ops->set_default_mgmt_key(&rdev->wiphy, netdev, + key_index); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_start_ap(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ap_settings *settings) +{ + int ret; + trace_rdev_start_ap(&rdev->wiphy, dev, settings); + ret = rdev->ops->start_ap(&rdev->wiphy, dev, settings); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_change_beacon(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_beacon_data *info) +{ + int ret; + trace_rdev_change_beacon(&rdev->wiphy, dev, info); + ret = rdev->ops->change_beacon(&rdev->wiphy, dev, info); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_stop_ap(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + int ret; + trace_rdev_stop_ap(&rdev->wiphy, dev); + ret = rdev->ops->stop_ap(&rdev->wiphy, dev); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_add_station(struct cfg80211_registered_device *rdev, + struct net_device *dev, u8 *mac, + struct station_parameters *params) +{ + int ret; + trace_rdev_add_station(&rdev->wiphy, dev, mac, params); + ret = rdev->ops->add_station(&rdev->wiphy, dev, mac, params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_del_station(struct cfg80211_registered_device *rdev, + struct net_device *dev, u8 *mac) +{ + int ret; + trace_rdev_del_station(&rdev->wiphy, dev, mac); + ret = rdev->ops->del_station(&rdev->wiphy, dev, mac); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_change_station(struct cfg80211_registered_device *rdev, + struct net_device *dev, u8 *mac, + struct station_parameters *params) +{ + int ret; + trace_rdev_change_station(&rdev->wiphy, dev, mac, params); + ret = rdev->ops->change_station(&rdev->wiphy, dev, mac, params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_get_station(struct cfg80211_registered_device *rdev, + struct net_device *dev, u8 *mac, + struct station_info *sinfo) +{ + int ret; + trace_rdev_get_station(&rdev->wiphy, dev, mac); + ret = rdev->ops->get_station(&rdev->wiphy, dev, mac, sinfo); + trace_rdev_return_int_station_info(&rdev->wiphy, ret, sinfo); + return ret; +} + +static inline int rdev_dump_station(struct cfg80211_registered_device *rdev, + struct net_device *dev, int idx, u8 *mac, + struct station_info *sinfo) +{ + int ret; + trace_rdev_dump_station(&rdev->wiphy, dev, idx, mac); + ret = rdev->ops->dump_station(&rdev->wiphy, dev, idx, mac, sinfo); + trace_rdev_return_int_station_info(&rdev->wiphy, ret, sinfo); + return ret; +} + +static inline int rdev_add_mpath(struct cfg80211_registered_device *rdev, + struct net_device *dev, u8 *dst, u8 *next_hop) +{ + int ret; + trace_rdev_add_mpath(&rdev->wiphy, dev, dst, next_hop); + ret = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_del_mpath(struct cfg80211_registered_device *rdev, + struct net_device *dev, u8 *dst) +{ + int ret; + trace_rdev_del_mpath(&rdev->wiphy, dev, dst); + ret = rdev->ops->del_mpath(&rdev->wiphy, dev, dst); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_change_mpath(struct cfg80211_registered_device *rdev, + struct net_device *dev, u8 *dst, + u8 *next_hop) +{ + int ret; + trace_rdev_change_mpath(&rdev->wiphy, dev, dst, next_hop); + ret = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_get_mpath(struct cfg80211_registered_device *rdev, + struct net_device *dev, u8 *dst, u8 *next_hop, + struct mpath_info *pinfo) +{ + int ret; + trace_rdev_get_mpath(&rdev->wiphy, dev, dst, next_hop); + ret = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, pinfo); + trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo); + return ret; + +} + +static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev, + struct net_device *dev, int idx, u8 *dst, + u8 *next_hop, struct mpath_info *pinfo) + +{ + int ret; + trace_rdev_dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop); + ret = rdev->ops->dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop, + pinfo); + trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo); + return ret; +} + +static inline int +rdev_get_mesh_config(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct mesh_config *conf) +{ + int ret; + trace_rdev_get_mesh_config(&rdev->wiphy, dev); + ret = rdev->ops->get_mesh_config(&rdev->wiphy, dev, conf); + trace_rdev_return_int_mesh_config(&rdev->wiphy, ret, conf); + return ret; +} + +static inline int +rdev_update_mesh_config(struct cfg80211_registered_device *rdev, + struct net_device *dev, u32 mask, + const struct mesh_config *nconf) +{ + int ret; + trace_rdev_update_mesh_config(&rdev->wiphy, dev, mask, nconf); + ret = rdev->ops->update_mesh_config(&rdev->wiphy, dev, mask, nconf); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_join_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev, + const struct mesh_config *conf, + const struct mesh_setup *setup) +{ + int ret; + trace_rdev_join_mesh(&rdev->wiphy, dev, conf, setup); + ret = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + + +static inline int rdev_leave_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + int ret; + trace_rdev_leave_mesh(&rdev->wiphy, dev); + ret = rdev->ops->leave_mesh(&rdev->wiphy, dev); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_change_bss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct bss_parameters *params) + +{ + int ret; + trace_rdev_change_bss(&rdev->wiphy, dev, params); + ret = rdev->ops->change_bss(&rdev->wiphy, dev, params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_set_txq_params(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_txq_params *params) + +{ + int ret; + trace_rdev_set_txq_params(&rdev->wiphy, dev, params); + ret = rdev->ops->set_txq_params(&rdev->wiphy, dev, params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int +rdev_libertas_set_mesh_channel(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan) +{ + int ret; + trace_rdev_libertas_set_mesh_channel(&rdev->wiphy, dev, chan); + ret = rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, dev, chan); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int +rdev_set_monitor_channel(struct cfg80211_registered_device *rdev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + int ret; + trace_rdev_set_monitor_channel(&rdev->wiphy, chan, channel_type); + ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chan, channel_type); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_scan(struct cfg80211_registered_device *rdev, + struct cfg80211_scan_request *request) +{ + int ret; + trace_rdev_scan(&rdev->wiphy, request); + ret = rdev->ops->scan(&rdev->wiphy, request); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_auth_request *req) +{ + int ret; + trace_rdev_auth(&rdev->wiphy, dev, req); + ret = rdev->ops->auth(&rdev->wiphy, dev, req); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_assoc_request *req) +{ + int ret; + trace_rdev_assoc(&rdev->wiphy, dev, req); + ret = rdev->ops->assoc(&rdev->wiphy, dev, req); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_deauth_request *req) +{ + int ret; + trace_rdev_deauth(&rdev->wiphy, dev, req); + ret = rdev->ops->deauth(&rdev->wiphy, dev, req); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_disassoc_request *req) +{ + int ret; + trace_rdev_disassoc(&rdev->wiphy, dev, req); + ret = rdev->ops->disassoc(&rdev->wiphy, dev, req); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + int ret; + trace_rdev_connect(&rdev->wiphy, dev, sme); + ret = rdev->ops->connect(&rdev->wiphy, dev, sme); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason_code) +{ + int ret; + trace_rdev_disconnect(&rdev->wiphy, dev, reason_code); + ret = rdev->ops->disconnect(&rdev->wiphy, dev, reason_code); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + int ret; + trace_rdev_join_ibss(&rdev->wiphy, dev, params); + ret = rdev->ops->join_ibss(&rdev->wiphy, dev, params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_leave_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + int ret; + trace_rdev_leave_ibss(&rdev->wiphy, dev); + ret = rdev->ops->leave_ibss(&rdev->wiphy, dev); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int +rdev_set_wiphy_params(struct cfg80211_registered_device *rdev, u32 changed) +{ + int ret; + trace_rdev_set_wiphy_params(&rdev->wiphy, changed); + ret = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_set_tx_power(struct cfg80211_registered_device *rdev, + enum nl80211_tx_power_setting type, int mbm) +{ + int ret; + trace_rdev_set_tx_power(&rdev->wiphy, type, mbm); + ret = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_get_tx_power(struct cfg80211_registered_device *rdev, + int *dbm) +{ + int ret; + trace_rdev_get_tx_power(&rdev->wiphy); + ret = rdev->ops->get_tx_power(&rdev->wiphy, dbm); + trace_rdev_return_int_int(&rdev->wiphy, ret, *dbm); + return ret; +} + +static inline int rdev_set_wds_peer(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *addr) +{ + int ret; + trace_rdev_set_wds_peer(&rdev->wiphy, dev, addr); + ret = rdev->ops->set_wds_peer(&rdev->wiphy, dev, addr); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev) +{ + trace_rdev_rfkill_poll(&rdev->wiphy); + rdev->ops->rfkill_poll(&rdev->wiphy); + trace_rdev_return_void(&rdev->wiphy); +} + + +#ifdef CONFIG_NL80211_TESTMODE +static inline int rdev_testmode_cmd(struct cfg80211_registered_device *rdev, + void *data, int len) +{ + int ret; + trace_rdev_testmode_cmd(&rdev->wiphy); + ret = rdev->ops->testmode_cmd(&rdev->wiphy, data, len); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_testmode_dump(struct cfg80211_registered_device *rdev, + struct sk_buff *skb, + struct netlink_callback *cb, void *data, + int len) +{ + int ret; + trace_rdev_testmode_dump(&rdev->wiphy); + ret = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb, data, len); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} +#endif + +static inline int +rdev_set_bitrate_mask(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *peer, + const struct cfg80211_bitrate_mask *mask) +{ + int ret; + trace_rdev_set_bitrate_mask(&rdev->wiphy, dev, peer, mask); + ret = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, peer, mask); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_dump_survey(struct cfg80211_registered_device *rdev, + struct net_device *netdev, int idx, + struct survey_info *info) +{ + int ret; + trace_rdev_dump_survey(&rdev->wiphy, netdev, idx); + ret = rdev->ops->dump_survey(&rdev->wiphy, netdev, idx, info); + if (ret < 0) + trace_rdev_return_int(&rdev->wiphy, ret); + else + trace_rdev_return_int_survey_info(&rdev->wiphy, ret, info); + return ret; +} + +static inline int rdev_set_pmksa(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + struct cfg80211_pmksa *pmksa) +{ + int ret; + trace_rdev_set_pmksa(&rdev->wiphy, netdev, pmksa); + ret = rdev->ops->set_pmksa(&rdev->wiphy, netdev, pmksa); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_del_pmksa(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + struct cfg80211_pmksa *pmksa) +{ + int ret; + trace_rdev_del_pmksa(&rdev->wiphy, netdev, pmksa); + ret = rdev->ops->del_pmksa(&rdev->wiphy, netdev, pmksa); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_flush_pmksa(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{ + int ret; + trace_rdev_flush_pmksa(&rdev->wiphy, netdev); + ret = rdev->ops->flush_pmksa(&rdev->wiphy, netdev); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int +rdev_remain_on_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, u64 *cookie) +{ + int ret; + trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, channel_type, + duration); + ret = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan, + channel_type, duration, cookie); + trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); + return ret; +} + +static inline int +rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, u64 cookie) +{ + int ret; + trace_rdev_cancel_remain_on_channel(&rdev->wiphy, wdev, cookie); + ret = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, wdev, cookie); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, u64 *cookie) +{ + int ret; + trace_rdev_mgmt_tx(&rdev->wiphy, wdev, chan, offchan, channel_type, + channel_type_valid, wait, no_cck, dont_wait_for_ack); + ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan, + channel_type, channel_type_valid, wait, buf, + len, no_cck, dont_wait_for_ack, cookie); + trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); + return ret; +} + +static inline int +rdev_mgmt_tx_cancel_wait(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, u64 cookie) +{ + int ret; + trace_rdev_mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie); + ret = rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_set_power_mgmt(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool enabled, + int timeout) +{ + int ret; + trace_rdev_set_power_mgmt(&rdev->wiphy, dev, enabled, timeout); + ret = rdev->ops->set_power_mgmt(&rdev->wiphy, dev, enabled, timeout); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int +rdev_set_cqm_rssi_config(struct cfg80211_registered_device *rdev, + struct net_device *dev, s32 rssi_thold, u32 rssi_hyst) +{ + int ret; + trace_rdev_set_cqm_rssi_config(&rdev->wiphy, dev, rssi_thold, + rssi_hyst); + ret = rdev->ops->set_cqm_rssi_config(&rdev->wiphy, dev, rssi_thold, + rssi_hyst); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int +rdev_set_cqm_txe_config(struct cfg80211_registered_device *rdev, + struct net_device *dev, u32 rate, u32 pkts, u32 intvl) +{ + int ret; + trace_rdev_set_cqm_txe_config(&rdev->wiphy, dev, rate, pkts, intvl); + ret = rdev->ops->set_cqm_txe_config(&rdev->wiphy, dev, rate, pkts, + intvl); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline void +rdev_mgmt_frame_register(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, u16 frame_type, bool reg) +{ + trace_rdev_mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg); + rdev->ops->mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg); + trace_rdev_return_void(&rdev->wiphy); +} + +static inline int rdev_set_antenna(struct cfg80211_registered_device *rdev, + u32 tx_ant, u32 rx_ant) +{ + int ret; + trace_rdev_set_antenna(&rdev->wiphy, tx_ant, rx_ant); + ret = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_get_antenna(struct cfg80211_registered_device *rdev, + u32 *tx_ant, u32 *rx_ant) +{ + int ret; + trace_rdev_get_antenna(&rdev->wiphy); + ret = rdev->ops->get_antenna(&rdev->wiphy, tx_ant, rx_ant); + if (ret) + trace_rdev_return_int(&rdev->wiphy, ret); + else + trace_rdev_return_int_tx_rx(&rdev->wiphy, ret, *tx_ant, + *rx_ant); + return ret; +} + +static inline int rdev_set_ringparam(struct cfg80211_registered_device *rdev, + u32 tx, u32 rx) +{ + int ret; + trace_rdev_set_ringparam(&rdev->wiphy, tx, rx); + ret = rdev->ops->set_ringparam(&rdev->wiphy, tx, rx); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline void rdev_get_ringparam(struct cfg80211_registered_device *rdev, + u32 *tx, u32 *tx_max, u32 *rx, + u32 *rx_max) +{ + trace_rdev_get_ringparam(&rdev->wiphy); + rdev->ops->get_ringparam(&rdev->wiphy, tx, tx_max, rx, rx_max); + trace_rdev_return_void_tx_rx(&rdev->wiphy, *tx, *tx_max, *rx, *rx_max); +} + +static inline int +rdev_sched_scan_start(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_sched_scan_request *request) +{ + int ret; + trace_rdev_sched_scan_start(&rdev->wiphy, dev, request); + ret = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_sched_scan_stop(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + int ret; + trace_rdev_sched_scan_stop(&rdev->wiphy, dev); + ret = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_set_rekey_data(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_gtk_rekey_data *data) +{ + int ret; + trace_rdev_set_rekey_data(&rdev->wiphy, dev); + ret = rdev->ops->set_rekey_data(&rdev->wiphy, dev, data); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev, + struct net_device *dev, u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, size_t len) +{ + int ret; + trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code, + dialog_token, status_code, buf, len); + ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, + dialog_token, status_code, buf, len); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_tdls_oper(struct cfg80211_registered_device *rdev, + struct net_device *dev, u8 *peer, + enum nl80211_tdls_operation oper) +{ + int ret; + trace_rdev_tdls_oper(&rdev->wiphy, dev, peer, oper); + ret = rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, oper); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_probe_client(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *peer, + u64 *cookie) +{ + int ret; + trace_rdev_probe_client(&rdev->wiphy, dev, peer); + ret = rdev->ops->probe_client(&rdev->wiphy, dev, peer, cookie); + trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); + return ret; +} + +static inline int rdev_set_noack_map(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 noack_map) +{ + int ret; + trace_rdev_set_noack_map(&rdev->wiphy, dev, noack_map); + ret = rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int +rdev_get_et_sset_count(struct cfg80211_registered_device *rdev, + struct net_device *dev, int sset) +{ + int ret; + trace_rdev_get_et_sset_count(&rdev->wiphy, dev, sset); + ret = rdev->ops->get_et_sset_count(&rdev->wiphy, dev, sset); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline void rdev_get_et_stats(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + trace_rdev_get_et_stats(&rdev->wiphy, dev); + rdev->ops->get_et_stats(&rdev->wiphy, dev, stats, data); + trace_rdev_return_void(&rdev->wiphy); +} + +static inline void rdev_get_et_strings(struct cfg80211_registered_device *rdev, + struct net_device *dev, u32 sset, + u8 *data) +{ + trace_rdev_get_et_strings(&rdev->wiphy, dev, sset); + rdev->ops->get_et_strings(&rdev->wiphy, dev, sset, data); + trace_rdev_return_void(&rdev->wiphy); +} + +static inline struct ieee80211_channel +*rdev_get_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, enum nl80211_channel_type *type) +{ + struct ieee80211_channel *ret; + trace_rdev_get_channel(&rdev->wiphy, wdev); + ret = rdev->ops->get_channel(&rdev->wiphy, wdev, type); + trace_rdev_return_channel(&rdev->wiphy, ret, *type); + return ret; +} + +static inline int rdev_start_p2p_device(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + int ret; + + trace_rdev_start_p2p_device(&rdev->wiphy, wdev); + ret = rdev->ops->start_p2p_device(&rdev->wiphy, wdev); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline void rdev_stop_p2p_device(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + trace_rdev_stop_p2p_device(&rdev->wiphy, wdev); + rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); + trace_rdev_return_void(&rdev->wiphy); +} +#endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 3b8cbbc214db..bcc7d7ee5a51 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -908,7 +908,7 @@ static void handle_channel(struct wiphy *wiphy, map_regdom_flags(reg_rule->flags) | bw_flags; chan->max_antenna_gain = chan->orig_mag = (int) MBI_TO_DBI(power_rule->max_antenna_gain); - chan->max_power = chan->orig_mpwr = + chan->max_reg_power = chan->max_power = chan->orig_mpwr = (int) MBM_TO_DBM(power_rule->max_eirp); return; } @@ -1331,7 +1331,8 @@ static void handle_channel_custom(struct wiphy *wiphy, chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); - chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); + chan->max_reg_power = chan->max_power = + (int) MBM_TO_DBM(power_rule->max_eirp); } static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 9730c9862bdc..7f97a087f452 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -17,9 +17,58 @@ #include "core.h" #include "nl80211.h" #include "wext-compat.h" +#include "rdev-ops.h" #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) +static void bss_release(struct kref *ref) +{ + struct cfg80211_internal_bss *bss; + + bss = container_of(ref, struct cfg80211_internal_bss, ref); + if (bss->pub.free_priv) + bss->pub.free_priv(&bss->pub); + + if (bss->beacon_ies_allocated) + kfree(bss->pub.beacon_ies); + if (bss->proberesp_ies_allocated) + kfree(bss->pub.proberesp_ies); + + BUG_ON(atomic_read(&bss->hold)); + + kfree(bss); +} + +/* must hold dev->bss_lock! */ +static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *bss) +{ + list_del_init(&bss->list); + rb_erase(&bss->rbn, &dev->bss_tree); + kref_put(&bss->ref, bss_release); +} + +/* must hold dev->bss_lock! */ +static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, + unsigned long expire_time) +{ + struct cfg80211_internal_bss *bss, *tmp; + bool expired = false; + + list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { + if (atomic_read(&bss->hold)) + continue; + if (!time_after(expire_time, bss->ts)) + continue; + + __cfg80211_unlink_bss(dev, bss); + expired = true; + } + + if (expired) + dev->bss_generation++; +} + void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) { struct cfg80211_scan_request *request; @@ -45,10 +94,17 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) if (wdev->netdev) cfg80211_sme_scan_done(wdev->netdev); - if (request->aborted) + if (request->aborted) { nl80211_send_scan_aborted(rdev, wdev); - else + } else { + if (request->flags & NL80211_SCAN_FLAG_FLUSH) { + /* flush entries from previous scans */ + spin_lock_bh(&rdev->bss_lock); + __cfg80211_bss_expire(rdev, request->scan_start); + spin_unlock_bh(&rdev->bss_lock); + } nl80211_send_scan_done(rdev, wdev); + } #ifdef CONFIG_CFG80211_WEXT if (wdev->netdev && !request->aborted) { @@ -89,6 +145,7 @@ void __cfg80211_scan_done(struct work_struct *wk) void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) { + trace_cfg80211_scan_done(request, aborted); WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); request->aborted = aborted; @@ -99,22 +156,34 @@ EXPORT_SYMBOL(cfg80211_scan_done); void __cfg80211_sched_scan_results(struct work_struct *wk) { struct cfg80211_registered_device *rdev; + struct cfg80211_sched_scan_request *request; rdev = container_of(wk, struct cfg80211_registered_device, sched_scan_results_wk); + request = rdev->sched_scan_req; + mutex_lock(&rdev->sched_scan_mtx); /* we don't have sched_scan_req anymore if the scan is stopping */ - if (rdev->sched_scan_req) - nl80211_send_sched_scan_results(rdev, - rdev->sched_scan_req->dev); + if (request) { + if (request->flags & NL80211_SCAN_FLAG_FLUSH) { + /* flush entries from previous scans */ + spin_lock_bh(&rdev->bss_lock); + __cfg80211_bss_expire(rdev, request->scan_start); + spin_unlock_bh(&rdev->bss_lock); + request->scan_start = + jiffies + msecs_to_jiffies(request->interval); + } + nl80211_send_sched_scan_results(rdev, request->dev); + } mutex_unlock(&rdev->sched_scan_mtx); } void cfg80211_sched_scan_results(struct wiphy *wiphy) { + trace_cfg80211_sched_scan_results(wiphy); /* ignore if we're not scanning */ if (wiphy_to_dev(wiphy)->sched_scan_req) queue_work(cfg80211_wq, @@ -126,6 +195,8 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + trace_cfg80211_sched_scan_stopped(wiphy); + mutex_lock(&rdev->sched_scan_mtx); __cfg80211_stop_sched_scan(rdev, true); mutex_unlock(&rdev->sched_scan_mtx); @@ -145,7 +216,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, dev = rdev->sched_scan_req->dev; if (!driver_initiated) { - int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); + int err = rdev_sched_scan_stop(rdev, dev); if (err) return err; } @@ -158,24 +229,6 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, return 0; } -static void bss_release(struct kref *ref) -{ - struct cfg80211_internal_bss *bss; - - bss = container_of(ref, struct cfg80211_internal_bss, ref); - if (bss->pub.free_priv) - bss->pub.free_priv(&bss->pub); - - if (bss->beacon_ies_allocated) - kfree(bss->pub.beacon_ies); - if (bss->proberesp_ies_allocated) - kfree(bss->pub.proberesp_ies); - - BUG_ON(atomic_read(&bss->hold)); - - kfree(bss); -} - /* must hold dev->bss_lock! */ void cfg80211_bss_age(struct cfg80211_registered_device *dev, unsigned long age_secs) @@ -188,32 +241,9 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev, } } -/* must hold dev->bss_lock! */ -static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *bss) -{ - list_del_init(&bss->list); - rb_erase(&bss->rbn, &dev->bss_tree); - kref_put(&bss->ref, bss_release); -} - -/* must hold dev->bss_lock! */ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) { - struct cfg80211_internal_bss *bss, *tmp; - bool expired = false; - - list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { - if (atomic_read(&bss->hold)) - continue; - if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) - continue; - __cfg80211_unlink_bss(dev, bss); - expired = true; - } - - if (expired) - dev->bss_generation++; + __cfg80211_bss_expire(dev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE); } const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) @@ -459,6 +489,9 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct cfg80211_internal_bss *bss, *res = NULL; unsigned long now = jiffies; + trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, capa_mask, + capa_val); + spin_lock_bh(&dev->bss_lock); list_for_each_entry(bss, &dev->bss_list, list) { @@ -480,6 +513,7 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, spin_unlock_bh(&dev->bss_lock); if (!res) return NULL; + trace_cfg80211_return_bss(&res->pub); return &res->pub; } EXPORT_SYMBOL(cfg80211_get_bss); @@ -792,6 +826,7 @@ cfg80211_inform_bss(struct wiphy *wiphy, if (res->pub.capability & WLAN_CAPABILITY_ESS) regulatory_hint_found_beacon(wiphy, channel, gfp); + trace_cfg80211_return_bss(&res->pub); /* cfg80211_bss_update gives us a referenced result */ return &res->pub; } @@ -804,10 +839,13 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, s32 signal, gfp_t gfp) { struct cfg80211_internal_bss *res; + size_t ielen = len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); size_t privsz; + trace_cfg80211_inform_bss_frame(wiphy, channel, mgmt, len, signal); + if (WARN_ON(!mgmt)) return NULL; @@ -861,6 +899,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, if (res->pub.capability & WLAN_CAPABILITY_ESS) regulatory_hint_found_beacon(wiphy, channel, gfp); + trace_cfg80211_return_bss(&res->pub); /* cfg80211_bss_update gives us a referenced result */ return &res->pub; } @@ -962,6 +1001,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, creq->ssids = (void *)&creq->channels[n_channels]; creq->n_channels = n_channels; creq->n_ssids = 1; + creq->scan_start = jiffies; /* translate "Scan on frequencies" request */ i = 0; @@ -1026,7 +1066,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; rdev->scan_req = creq; - err = rdev->ops->scan(wiphy, creq); + err = rdev_scan(rdev, creq); if (err) { rdev->scan_req = NULL; /* creq will be freed below */ diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 6f39cb808302..c7490027237d 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -16,6 +16,7 @@ #include <net/rtnetlink.h> #include "nl80211.h" #include "reg.h" +#include "rdev-ops.h" struct cfg80211_conn { struct cfg80211_connect_params params; @@ -138,10 +139,11 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) request->wdev = wdev; request->wiphy = &rdev->wiphy; + request->scan_start = jiffies; rdev->scan_req = request; - err = rdev->ops->scan(wdev->wiphy, request); + err = rdev_scan(rdev, request); if (!err) { wdev->conn->state = CFG80211_CONN_SCANNING; nl80211_send_scan_start(rdev, wdev); @@ -179,7 +181,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) params->ssid, params->ssid_len, NULL, 0, params->key, params->key_len, - params->key_idx); + params->key_idx, NULL, 0); case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!rdev->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; @@ -716,7 +718,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, */ if (rdev->ops->del_key) for (i = 0; i < 6; i++) - rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL); + rdev_del_key(rdev, dev, i, false, NULL); #ifdef CONFIG_CFG80211_WEXT memset(&wrqu, 0, sizeof(wrqu)); @@ -892,7 +894,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, } else { wdev->sme_state = CFG80211_SME_CONNECTING; wdev->connect_keys = connkeys; - err = rdev->ops->connect(&rdev->wiphy, dev, connect); + err = rdev_connect(rdev, dev, connect); if (err) { wdev->connect_keys = NULL; wdev->sme_state = CFG80211_SME_IDLE; @@ -964,7 +966,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, if (err) return err; } else { - err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); + err = rdev_disconnect(rdev, dev, reason); if (err) return err; } diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index ff574597a854..9bf6d5e32166 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -16,6 +16,7 @@ #include <net/cfg80211.h> #include "sysfs.h" #include "core.h" +#include "rdev-ops.h" static inline struct cfg80211_registered_device *dev_to_rdev( struct device *dev) @@ -94,7 +95,7 @@ static int wiphy_suspend(struct device *dev, pm_message_t state) if (rdev->ops->suspend) { rtnl_lock(); if (rdev->wiphy.registered) - ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan); + ret = rdev_suspend(rdev); rtnl_unlock(); } @@ -114,7 +115,7 @@ static int wiphy_resume(struct device *dev) if (rdev->ops->resume) { rtnl_lock(); if (rdev->wiphy.registered) - ret = rdev->ops->resume(&rdev->wiphy); + ret = rdev_resume(rdev); rtnl_unlock(); } diff --git a/net/wireless/trace.c b/net/wireless/trace.c new file mode 100644 index 000000000000..95f997fad755 --- /dev/null +++ b/net/wireless/trace.c @@ -0,0 +1,7 @@ +#include <linux/module.h> + +#ifndef __CHECKER__ +#define CREATE_TRACE_POINTS +#include "trace.h" + +#endif diff --git a/net/wireless/trace.h b/net/wireless/trace.h new file mode 100644 index 000000000000..0ca71caf85fb --- /dev/null +++ b/net/wireless/trace.h @@ -0,0 +1,2296 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cfg80211 + +#if !defined(__RDEV_OPS_TRACE) || defined(TRACE_HEADER_MULTI_READ) +#define __RDEV_OPS_TRACE + +#include <linux/tracepoint.h> + +#include <linux/rtnetlink.h> +#include <net/cfg80211.h> +#include "core.h" + +#define MAC_ENTRY(entry_mac) __array(u8, entry_mac, ETH_ALEN) +#define MAC_ASSIGN(entry_mac, given_mac) do { \ + if (given_mac) \ + memcpy(__entry->entry_mac, given_mac, ETH_ALEN); \ + else \ + memset(__entry->entry_mac, 0, ETH_ALEN); \ + } while (0) +#define MAC_PR_FMT "%pM" +#define MAC_PR_ARG(entry_mac) (__entry->entry_mac) + +#define WIPHY_ENTRY MAC_ENTRY(wiphy_mac) +#define WIPHY_ASSIGN MAC_ASSIGN(wiphy_mac, wiphy->perm_addr) +#define WIPHY_PR_FMT "wiphy " MAC_PR_FMT +#define WIPHY_PR_ARG MAC_PR_ARG(wiphy_mac) + +#define WDEV_ENTRY __field(u32, id) +#define WDEV_ASSIGN (__entry->id) = (wdev->identifier) +#define WDEV_PR_FMT ", wdev id: %u" +#define WDEV_PR_ARG (__entry->id) + +#define NETDEV_ENTRY __array(char, name, IFNAMSIZ) \ + MAC_ENTRY(netdev_addr) \ + __field(int, ifindex) +#define NETDEV_ASSIGN \ + do { \ + memcpy(__entry->name, netdev->name, IFNAMSIZ); \ + MAC_ASSIGN(netdev_addr, netdev->dev_addr); \ + (__entry->ifindex) = (netdev->ifindex); \ + } while (0) +#define NETDEV_PR_FMT ", netdev - name: %s, addr: " MAC_PR_FMT \ + ", intf index: %d" +#define NETDEV_PR_ARG (__entry->name), MAC_PR_ARG(netdev_addr), \ + (__entry->ifindex) + +#define MESH_CFG_ENTRY __field(u16, dot11MeshRetryTimeout) \ + __field(u16, dot11MeshConfirmTimeout) \ + __field(u16, dot11MeshHoldingTimeout) \ + __field(u16, dot11MeshMaxPeerLinks) \ + __field(u8, dot11MeshMaxRetries) \ + __field(u8, dot11MeshTTL) \ + __field(u8, element_ttl) \ + __field(bool, auto_open_plinks) \ + __field(u32, dot11MeshNbrOffsetMaxNeighbor) \ + __field(u8, dot11MeshHWMPmaxPREQretries) \ + __field(u32, path_refresh_time) \ + __field(u32, dot11MeshHWMPactivePathTimeout) \ + __field(u16, min_discovery_timeout) \ + __field(u16, dot11MeshHWMPpreqMinInterval) \ + __field(u16, dot11MeshHWMPperrMinInterval) \ + __field(u16, dot11MeshHWMPnetDiameterTraversalTime) \ + __field(u8, dot11MeshHWMPRootMode) \ + __field(u16, dot11MeshHWMPRannInterval) \ + __field(bool, dot11MeshGateAnnouncementProtocol) \ + __field(bool, dot11MeshForwarding) \ + __field(s32, rssi_threshold) \ + __field(u16, ht_opmode) \ + __field(u32, dot11MeshHWMPactivePathToRootTimeout) \ + __field(u16, dot11MeshHWMProotInterval) \ + __field(u16, dot11MeshHWMPconfirmationInterval) +#define MESH_CFG_ASSIGN \ + do { \ + __entry->dot11MeshRetryTimeout = conf->dot11MeshRetryTimeout; \ + __entry->dot11MeshConfirmTimeout = \ + conf->dot11MeshConfirmTimeout; \ + __entry->dot11MeshHoldingTimeout = \ + conf->dot11MeshHoldingTimeout; \ + __entry->dot11MeshMaxPeerLinks = conf->dot11MeshMaxPeerLinks; \ + __entry->dot11MeshMaxRetries = conf->dot11MeshMaxRetries; \ + __entry->dot11MeshTTL = conf->dot11MeshTTL; \ + __entry->element_ttl = conf->element_ttl; \ + __entry->auto_open_plinks = conf->auto_open_plinks; \ + __entry->dot11MeshNbrOffsetMaxNeighbor = \ + conf->dot11MeshNbrOffsetMaxNeighbor; \ + __entry->dot11MeshHWMPmaxPREQretries = \ + conf->dot11MeshHWMPmaxPREQretries; \ + __entry->path_refresh_time = conf->path_refresh_time; \ + __entry->dot11MeshHWMPactivePathTimeout = \ + conf->dot11MeshHWMPactivePathTimeout; \ + __entry->min_discovery_timeout = conf->min_discovery_timeout; \ + __entry->dot11MeshHWMPpreqMinInterval = \ + conf->dot11MeshHWMPpreqMinInterval; \ + __entry->dot11MeshHWMPperrMinInterval = \ + conf->dot11MeshHWMPperrMinInterval; \ + __entry->dot11MeshHWMPnetDiameterTraversalTime = \ + conf->dot11MeshHWMPnetDiameterTraversalTime; \ + __entry->dot11MeshHWMPRootMode = conf->dot11MeshHWMPRootMode; \ + __entry->dot11MeshHWMPRannInterval = \ + conf->dot11MeshHWMPRannInterval; \ + __entry->dot11MeshGateAnnouncementProtocol = \ + conf->dot11MeshGateAnnouncementProtocol; \ + __entry->dot11MeshForwarding = conf->dot11MeshForwarding; \ + __entry->rssi_threshold = conf->rssi_threshold; \ + __entry->ht_opmode = conf->ht_opmode; \ + __entry->dot11MeshHWMPactivePathToRootTimeout = \ + conf->dot11MeshHWMPactivePathToRootTimeout; \ + __entry->dot11MeshHWMProotInterval = \ + conf->dot11MeshHWMProotInterval; \ + __entry->dot11MeshHWMPconfirmationInterval = \ + conf->dot11MeshHWMPconfirmationInterval; \ + } while (0) + +#define CHAN_ENTRY __field(enum ieee80211_band, band) \ + __field(u16, center_freq) +#define CHAN_ASSIGN(chan) \ + do { \ + if (chan) { \ + __entry->band = chan->band; \ + __entry->center_freq = chan->center_freq; \ + } else { \ + __entry->band = 0; \ + __entry->center_freq = 0; \ + } \ + } while (0) +#define CHAN_PR_FMT ", band: %d, freq: %u" +#define CHAN_PR_ARG __entry->band, __entry->center_freq + +#define SINFO_ENTRY __field(int, generation) \ + __field(u32, connected_time) \ + __field(u32, inactive_time) \ + __field(u32, rx_bytes) \ + __field(u32, tx_bytes) \ + __field(u32, rx_packets) \ + __field(u32, tx_packets) \ + __field(u32, tx_retries) \ + __field(u32, tx_failed) \ + __field(u32, rx_dropped_misc) \ + __field(u32, beacon_loss_count) \ + __field(u16, llid) \ + __field(u16, plid) \ + __field(u8, plink_state) +#define SINFO_ASSIGN \ + do { \ + __entry->generation = sinfo->generation; \ + __entry->connected_time = sinfo->connected_time; \ + __entry->inactive_time = sinfo->inactive_time; \ + __entry->rx_bytes = sinfo->rx_bytes; \ + __entry->tx_bytes = sinfo->tx_bytes; \ + __entry->rx_packets = sinfo->rx_packets; \ + __entry->tx_packets = sinfo->tx_packets; \ + __entry->tx_retries = sinfo->tx_retries; \ + __entry->tx_failed = sinfo->tx_failed; \ + __entry->rx_dropped_misc = sinfo->rx_dropped_misc; \ + __entry->beacon_loss_count = sinfo->beacon_loss_count; \ + __entry->llid = sinfo->llid; \ + __entry->plid = sinfo->plid; \ + __entry->plink_state = sinfo->plink_state; \ + } while (0) + +#define BOOL_TO_STR(bo) (bo) ? "true" : "false" + +/************************************************************* + * rdev->ops traces * + *************************************************************/ + +TRACE_EVENT(rdev_suspend, + TP_PROTO(struct wiphy *wiphy, struct cfg80211_wowlan *wow), + TP_ARGS(wiphy, wow), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(bool, any) + __field(bool, disconnect) + __field(bool, magic_pkt) + __field(bool, gtk_rekey_failure) + __field(bool, eap_identity_req) + __field(bool, four_way_handshake) + __field(bool, rfkill_release) + __field(bool, valid_wow) + ), + TP_fast_assign( + WIPHY_ASSIGN; + if (wow) { + __entry->any = wow->any; + __entry->disconnect = wow->disconnect; + __entry->magic_pkt = wow->magic_pkt; + __entry->gtk_rekey_failure = wow->gtk_rekey_failure; + __entry->eap_identity_req = wow->eap_identity_req; + __entry->four_way_handshake = wow->four_way_handshake; + __entry->rfkill_release = wow->rfkill_release; + __entry->valid_wow = true; + } else { + __entry->valid_wow = false; + } + ), + TP_printk(WIPHY_PR_FMT ", wow%s - any: %d, disconnect: %d, " + "magic pkt: %d, gtk rekey failure: %d, eap identify req: %d, " + "four way handshake: %d, rfkill release: %d.", + WIPHY_PR_ARG, __entry->valid_wow ? "" : "(Not configured!)", + __entry->any, __entry->disconnect, __entry->magic_pkt, + __entry->gtk_rekey_failure, __entry->eap_identity_req, + __entry->four_way_handshake, __entry->rfkill_release) +); + +TRACE_EVENT(rdev_return_int, + TP_PROTO(struct wiphy *wiphy, int ret), + TP_ARGS(wiphy, ret), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(int, ret) + ), + TP_fast_assign( + WIPHY_ASSIGN; + __entry->ret = ret; + ), + TP_printk(WIPHY_PR_FMT ", returned: %d", WIPHY_PR_ARG, __entry->ret) +); + +TRACE_EVENT(rdev_scan, + TP_PROTO(struct wiphy *wiphy, struct cfg80211_scan_request *request), + TP_ARGS(wiphy, request), + TP_STRUCT__entry( + WIPHY_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG) +); + +DECLARE_EVENT_CLASS(wiphy_only_evt, + TP_PROTO(struct wiphy *wiphy), + TP_ARGS(wiphy), + TP_STRUCT__entry( + WIPHY_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG) +); + +DEFINE_EVENT(wiphy_only_evt, rdev_resume, + TP_PROTO(struct wiphy *wiphy), + TP_ARGS(wiphy) +); + +DEFINE_EVENT(wiphy_only_evt, rdev_return_void, + TP_PROTO(struct wiphy *wiphy), + TP_ARGS(wiphy) +); + +DEFINE_EVENT(wiphy_only_evt, rdev_get_ringparam, + TP_PROTO(struct wiphy *wiphy), + TP_ARGS(wiphy) +); + +DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna, + TP_PROTO(struct wiphy *wiphy), + TP_ARGS(wiphy) +); + +DEFINE_EVENT(wiphy_only_evt, rdev_get_tx_power, + TP_PROTO(struct wiphy *wiphy), + TP_ARGS(wiphy) +); + +DEFINE_EVENT(wiphy_only_evt, rdev_rfkill_poll, + TP_PROTO(struct wiphy *wiphy), + TP_ARGS(wiphy) +); + +DECLARE_EVENT_CLASS(wiphy_enabled_evt, + TP_PROTO(struct wiphy *wiphy, bool enabled), + TP_ARGS(wiphy, enabled), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(bool, enabled) + ), + TP_fast_assign( + WIPHY_ASSIGN; + __entry->enabled = enabled; + ), + TP_printk(WIPHY_PR_FMT ", %senabled ", + WIPHY_PR_ARG, __entry->enabled ? "" : "not ") +); + +DEFINE_EVENT(wiphy_enabled_evt, rdev_set_wakeup, + TP_PROTO(struct wiphy *wiphy, bool enabled), + TP_ARGS(wiphy, enabled) +); + +TRACE_EVENT(rdev_add_virtual_intf, + TP_PROTO(struct wiphy *wiphy, char *name, enum nl80211_iftype type), + TP_ARGS(wiphy, name, type), + TP_STRUCT__entry( + WIPHY_ENTRY + __string(vir_intf_name, name ? name : "<noname>") + __field(enum nl80211_iftype, type) + ), + TP_fast_assign( + WIPHY_ASSIGN; + __assign_str(vir_intf_name, name ? name : "<noname>"); + __entry->type = type; + ), + TP_printk(WIPHY_PR_FMT ", virtual intf name: %s, type: %d", + WIPHY_PR_ARG, __get_str(vir_intf_name), __entry->type) +); + +DECLARE_EVENT_CLASS(wiphy_wdev_evt, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_return_wdev, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_del_virtual_intf, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev) +); + +TRACE_EVENT(rdev_change_virtual_intf, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + enum nl80211_iftype type), + TP_ARGS(wiphy, netdev, type), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(enum nl80211_iftype, type) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->type = type; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", type: %d", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->type) +); + +DECLARE_EVENT_CLASS(key_handle, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, + bool pairwise, const u8 *mac_addr), + TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(mac_addr) + __field(u8, key_index) + __field(bool, pairwise) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(mac_addr, mac_addr); + __entry->key_index = key_index; + __entry->pairwise = pairwise; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", key_index: %u, pairwise: %s, mac addr: " MAC_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index, + BOOL_TO_STR(__entry->pairwise), MAC_PR_ARG(mac_addr)) +); + +DEFINE_EVENT(key_handle, rdev_add_key, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, + bool pairwise, const u8 *mac_addr), + TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr) +); + +DEFINE_EVENT(key_handle, rdev_get_key, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, + bool pairwise, const u8 *mac_addr), + TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr) +); + +DEFINE_EVENT(key_handle, rdev_del_key, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, + bool pairwise, const u8 *mac_addr), + TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr) +); + +TRACE_EVENT(rdev_set_default_key, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, + bool unicast, bool multicast), + TP_ARGS(wiphy, netdev, key_index, unicast, multicast), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(u8, key_index) + __field(bool, unicast) + __field(bool, multicast) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->key_index = key_index; + __entry->unicast = unicast; + __entry->multicast = multicast; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", key index: %u, unicast: %s, multicast: %s", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index, + BOOL_TO_STR(__entry->unicast), + BOOL_TO_STR(__entry->multicast)) +); + +TRACE_EVENT(rdev_set_default_mgmt_key, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index), + TP_ARGS(wiphy, netdev, key_index), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(u8, key_index) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->key_index = key_index; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", key index: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index) +); + +TRACE_EVENT(rdev_start_ap, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_ap_settings *settings), + TP_ARGS(wiphy, netdev, settings), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + CHAN_ENTRY + __field(int, beacon_interval) + __field(int, dtim_period) + __array(char, ssid, IEEE80211_MAX_SSID_LEN + 1) + __field(enum nl80211_hidden_ssid, hidden_ssid) + __field(u32, wpa_ver) + __field(bool, privacy) + __field(enum nl80211_auth_type, auth_type) + __field(int, inactivity_timeout) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + CHAN_ASSIGN(settings->channel); + __entry->beacon_interval = settings->beacon_interval; + __entry->dtim_period = settings->dtim_period; + __entry->hidden_ssid = settings->hidden_ssid; + __entry->wpa_ver = settings->crypto.wpa_versions; + __entry->privacy = settings->privacy; + __entry->auth_type = settings->auth_type; + __entry->inactivity_timeout = settings->inactivity_timeout; + memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1); + memcpy(__entry->ssid, settings->ssid, settings->ssid_len); + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", AP settings - ssid: %s, " + CHAN_PR_FMT ", beacon interval: %d, dtim period: %d, " + "hidden ssid: %d, wpa versions: %u, privacy: %s, " + "auth type: %d, inactivity timeout: %d", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ssid, CHAN_PR_ARG, + __entry->beacon_interval, __entry->dtim_period, + __entry->hidden_ssid, __entry->wpa_ver, + BOOL_TO_STR(__entry->privacy), __entry->auth_type, + __entry->inactivity_timeout) +); + +TRACE_EVENT(rdev_change_beacon, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_beacon_data *info), + TP_ARGS(wiphy, netdev, info), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __dynamic_array(u8, head, info ? info->head_len : 0) + __dynamic_array(u8, tail, info ? info->tail_len : 0) + __dynamic_array(u8, beacon_ies, info ? info->beacon_ies_len : 0) + __dynamic_array(u8, proberesp_ies, + info ? info->proberesp_ies_len : 0) + __dynamic_array(u8, assocresp_ies, + info ? info->assocresp_ies_len : 0) + __dynamic_array(u8, probe_resp, info ? info->probe_resp_len : 0) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + if (info) { + if (info->head) + memcpy(__get_dynamic_array(head), info->head, + info->head_len); + if (info->tail) + memcpy(__get_dynamic_array(tail), info->tail, + info->tail_len); + if (info->beacon_ies) + memcpy(__get_dynamic_array(beacon_ies), + info->beacon_ies, info->beacon_ies_len); + if (info->proberesp_ies) + memcpy(__get_dynamic_array(proberesp_ies), + info->proberesp_ies, + info->proberesp_ies_len); + if (info->assocresp_ies) + memcpy(__get_dynamic_array(assocresp_ies), + info->assocresp_ies, + info->assocresp_ies_len); + if (info->probe_resp) + memcpy(__get_dynamic_array(probe_resp), + info->probe_resp, info->probe_resp_len); + } + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG) +); + +DECLARE_EVENT_CLASS(wiphy_netdev_evt, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), + TP_ARGS(wiphy, netdev), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_stop_ap, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), + TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_get_et_stats, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), + TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_sched_scan_stop, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), + TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_set_rekey_data, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), + TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_get_mesh_config, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), + TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_mesh, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), + TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ibss, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), + TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), + TP_ARGS(wiphy, netdev) +); + +DECLARE_EVENT_CLASS(station_add_change, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *mac, + struct station_parameters *params), + TP_ARGS(wiphy, netdev, mac, params), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(sta_mac) + __field(u32, sta_flags_mask) + __field(u32, sta_flags_set) + __field(u32, sta_modify_mask) + __field(int, listen_interval) + __field(u16, aid) + __field(u8, plink_action) + __field(u8, plink_state) + __field(u8, uapsd_queues) + __array(u8, ht_capa, (int)sizeof(struct ieee80211_ht_cap)) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(sta_mac, mac); + __entry->sta_flags_mask = params->sta_flags_mask; + __entry->sta_flags_set = params->sta_flags_set; + __entry->sta_modify_mask = params->sta_modify_mask; + __entry->listen_interval = params->listen_interval; + __entry->aid = params->aid; + __entry->plink_action = params->plink_action; + __entry->plink_state = params->plink_state; + __entry->uapsd_queues = params->uapsd_queues; + memset(__entry->ht_capa, 0, sizeof(struct ieee80211_ht_cap)); + if (params->ht_capa) + memcpy(__entry->ht_capa, params->ht_capa, + sizeof(struct ieee80211_ht_cap)); + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", station mac: " MAC_PR_FMT + ", station flags mask: %u, station flags set: %u, " + "station modify mask: %u, listen interval: %d, aid: %u, " + "plink action: %u, plink state: %u, uapsd queues: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac), + __entry->sta_flags_mask, __entry->sta_flags_set, + __entry->sta_modify_mask, __entry->listen_interval, + __entry->aid, __entry->plink_action, __entry->plink_state, + __entry->uapsd_queues) +); + +DEFINE_EVENT(station_add_change, rdev_add_station, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *mac, + struct station_parameters *params), + TP_ARGS(wiphy, netdev, mac, params) +); + +DEFINE_EVENT(station_add_change, rdev_change_station, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *mac, + struct station_parameters *params), + TP_ARGS(wiphy, netdev, mac, params) +); + +DECLARE_EVENT_CLASS(wiphy_netdev_mac_evt, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac), + TP_ARGS(wiphy, netdev, mac), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(sta_mac) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(sta_mac, mac); + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", mac: " MAC_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac)) +); + +DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_del_station, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac), + TP_ARGS(wiphy, netdev, mac) +); + +DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_get_station, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac), + TP_ARGS(wiphy, netdev, mac) +); + +DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_del_mpath, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac), + TP_ARGS(wiphy, netdev, mac) +); + +DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_set_wds_peer, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac), + TP_ARGS(wiphy, netdev, mac) +); + +TRACE_EVENT(rdev_dump_station, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx, + u8 *mac), + TP_ARGS(wiphy, netdev, idx, mac), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(sta_mac) + __field(int, idx) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(sta_mac, mac); + __entry->idx = idx; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", station mac: " MAC_PR_FMT ", idx: %d", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac), + __entry->idx) +); + +TRACE_EVENT(rdev_return_int_station_info, + TP_PROTO(struct wiphy *wiphy, int ret, struct station_info *sinfo), + TP_ARGS(wiphy, ret, sinfo), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(int, ret) + SINFO_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + __entry->ret = ret; + SINFO_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT ", returned %d" , + WIPHY_PR_ARG, __entry->ret) +); + +DECLARE_EVENT_CLASS(mpath_evt, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst, + u8 *next_hop), + TP_ARGS(wiphy, netdev, dst, next_hop), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(dst) + MAC_ENTRY(next_hop) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(dst, dst); + MAC_ASSIGN(next_hop, next_hop); + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", destination: " MAC_PR_FMT ", next hop: " MAC_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(dst), + MAC_PR_ARG(next_hop)) +); + +DEFINE_EVENT(mpath_evt, rdev_add_mpath, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst, + u8 *next_hop), + TP_ARGS(wiphy, netdev, dst, next_hop) +); + +DEFINE_EVENT(mpath_evt, rdev_change_mpath, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst, + u8 *next_hop), + TP_ARGS(wiphy, netdev, dst, next_hop) +); + +DEFINE_EVENT(mpath_evt, rdev_get_mpath, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst, + u8 *next_hop), + TP_ARGS(wiphy, netdev, dst, next_hop) +); + +TRACE_EVENT(rdev_dump_mpath, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx, + u8 *dst, u8 *next_hop), + TP_ARGS(wiphy, netdev, idx, dst, next_hop), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(dst) + MAC_ENTRY(next_hop) + __field(int, idx) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(dst, dst); + MAC_ASSIGN(next_hop, next_hop); + __entry->idx = idx; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", index: %d, destination: " + MAC_PR_FMT ", next hop: " MAC_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx, MAC_PR_ARG(dst), + MAC_PR_ARG(next_hop)) +); + +TRACE_EVENT(rdev_return_int_mpath_info, + TP_PROTO(struct wiphy *wiphy, int ret, struct mpath_info *pinfo), + TP_ARGS(wiphy, ret, pinfo), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(int, ret) + __field(int, generation) + __field(u32, filled) + __field(u32, frame_qlen) + __field(u32, sn) + __field(u32, metric) + __field(u32, exptime) + __field(u32, discovery_timeout) + __field(u8, discovery_retries) + __field(u8, flags) + ), + TP_fast_assign( + WIPHY_ASSIGN; + __entry->ret = ret; + __entry->generation = pinfo->generation; + __entry->filled = pinfo->filled; + __entry->frame_qlen = pinfo->frame_qlen; + __entry->sn = pinfo->sn; + __entry->metric = pinfo->metric; + __entry->exptime = pinfo->exptime; + __entry->discovery_timeout = pinfo->discovery_timeout; + __entry->discovery_retries = pinfo->discovery_retries; + __entry->flags = pinfo->flags; + ), + TP_printk(WIPHY_PR_FMT ", returned %d. mpath info - generation: %d, " + "filled: %u, frame qlen: %u, sn: %u, metric: %u, exptime: %u," + " discovery timeout: %u, discovery retries: %u, flags: %u", + WIPHY_PR_ARG, __entry->ret, __entry->generation, + __entry->filled, __entry->frame_qlen, __entry->sn, + __entry->metric, __entry->exptime, __entry->discovery_timeout, + __entry->discovery_retries, __entry->flags) +); + +TRACE_EVENT(rdev_return_int_mesh_config, + TP_PROTO(struct wiphy *wiphy, int ret, struct mesh_config *conf), + TP_ARGS(wiphy, ret, conf), + TP_STRUCT__entry( + WIPHY_ENTRY + MESH_CFG_ENTRY + __field(int, ret) + ), + TP_fast_assign( + WIPHY_ASSIGN; + MESH_CFG_ASSIGN; + __entry->ret = ret; + ), + TP_printk(WIPHY_PR_FMT ", returned: %d", + WIPHY_PR_ARG, __entry->ret) +); + +TRACE_EVENT(rdev_update_mesh_config, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 mask, + const struct mesh_config *conf), + TP_ARGS(wiphy, netdev, mask, conf), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MESH_CFG_ENTRY + __field(u32, mask) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MESH_CFG_ASSIGN; + __entry->mask = mask; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", mask: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->mask) +); + +TRACE_EVENT(rdev_join_mesh, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + const struct mesh_config *conf, + const struct mesh_setup *setup), + TP_ARGS(wiphy, netdev, conf, setup), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MESH_CFG_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MESH_CFG_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG) +); + +TRACE_EVENT(rdev_change_bss, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct bss_parameters *params), + TP_ARGS(wiphy, netdev, params), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(int, use_cts_prot) + __field(int, use_short_preamble) + __field(int, use_short_slot_time) + __field(int, ap_isolate) + __field(int, ht_opmode) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->use_cts_prot = params->use_cts_prot; + __entry->use_short_preamble = params->use_short_preamble; + __entry->use_short_slot_time = params->use_short_slot_time; + __entry->ap_isolate = params->ap_isolate; + __entry->ht_opmode = params->ht_opmode; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", use cts prot: %d, " + "use short preamble: %d, use short slot time: %d, " + "ap isolate: %d, ht opmode: %d", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->use_cts_prot, + __entry->use_short_preamble, __entry->use_short_slot_time, + __entry->ap_isolate, __entry->ht_opmode) +); + +TRACE_EVENT(rdev_set_txq_params, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct ieee80211_txq_params *params), + TP_ARGS(wiphy, netdev, params), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(enum nl80211_ac, ac) + __field(u16, txop) + __field(u16, cwmin) + __field(u16, cwmax) + __field(u8, aifs) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->ac = params->ac; + __entry->txop = params->txop; + __entry->cwmin = params->cwmin; + __entry->cwmax = params->cwmax; + __entry->aifs = params->aifs; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", ac: %d, txop: %u, cwmin: %u, cwmax: %u, aifs: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ac, __entry->txop, + __entry->cwmin, __entry->cwmax, __entry->aifs) +); + +TRACE_EVENT(rdev_libertas_set_mesh_channel, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct ieee80211_channel *chan), + TP_ARGS(wiphy, netdev, chan), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + CHAN_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + CHAN_ASSIGN(chan); + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT CHAN_PR_FMT, WIPHY_PR_ARG, + NETDEV_PR_ARG, CHAN_PR_ARG) +); + +TRACE_EVENT(rdev_set_monitor_channel, + TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *chan, + enum nl80211_channel_type chan_type), + TP_ARGS(wiphy, chan, chan_type), + TP_STRUCT__entry( + WIPHY_ENTRY + CHAN_ENTRY + __field(enum nl80211_channel_type, chan_type) + ), + TP_fast_assign( + WIPHY_ASSIGN; + CHAN_ASSIGN(chan); + __entry->chan_type = chan_type; + ), + TP_printk(WIPHY_PR_FMT CHAN_PR_FMT ", channel type : %d", + WIPHY_PR_ARG, CHAN_PR_ARG, __entry->chan_type) +); + +TRACE_EVENT(rdev_auth, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_auth_request *req), + TP_ARGS(wiphy, netdev, req), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(bssid) + __field(enum nl80211_auth_type, auth_type) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + if (req->bss) + MAC_ASSIGN(bssid, req->bss->bssid); + else + memset(__entry->bssid, 0, ETH_ALEN); + __entry->auth_type = req->auth_type; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", auth type: %d, bssid: " MAC_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->auth_type, + MAC_PR_ARG(bssid)) +); + +TRACE_EVENT(rdev_assoc, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_assoc_request *req), + TP_ARGS(wiphy, netdev, req), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(bssid) + MAC_ENTRY(prev_bssid) + __field(bool, use_mfp) + __field(u32, flags) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + if (req->bss) + MAC_ASSIGN(bssid, req->bss->bssid); + else + memset(__entry->bssid, 0, ETH_ALEN); + MAC_ASSIGN(prev_bssid, req->prev_bssid); + __entry->use_mfp = req->use_mfp; + __entry->flags = req->flags; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT + ", previous bssid: " MAC_PR_FMT ", use mfp: %s, flags: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), + MAC_PR_ARG(prev_bssid), BOOL_TO_STR(__entry->use_mfp), + __entry->flags) +); + +TRACE_EVENT(rdev_deauth, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_deauth_request *req), + TP_ARGS(wiphy, netdev, req), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(bssid) + __field(u16, reason_code) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(bssid, req->bssid); + __entry->reason_code = req->reason_code; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", reason: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), + __entry->reason_code) +); + +TRACE_EVENT(rdev_disassoc, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_disassoc_request *req), + TP_ARGS(wiphy, netdev, req), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(bssid) + __field(u16, reason_code) + __field(bool, local_state_change) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + if (req->bss) + MAC_ASSIGN(bssid, req->bss->bssid); + else + memset(__entry->bssid, 0, ETH_ALEN); + __entry->reason_code = req->reason_code; + __entry->local_state_change = req->local_state_change; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT + ", reason: %u, local state change: %s", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), + __entry->reason_code, + BOOL_TO_STR(__entry->local_state_change)) +); + +TRACE_EVENT(rdev_mgmt_tx_cancel_wait, + TP_PROTO(struct wiphy *wiphy, + struct wireless_dev *wdev, u64 cookie), + TP_ARGS(wiphy, wdev, cookie), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __field(u64, cookie) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + __entry->cookie = cookie; + ), + TP_printk(WIPHY_PR_FMT WDEV_PR_FMT ", cookie: %llu ", + WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie) +); + +TRACE_EVENT(rdev_set_power_mgmt, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + bool enabled, int timeout), + TP_ARGS(wiphy, netdev, enabled, timeout), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(bool, enabled) + __field(int, timeout) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->enabled = enabled; + __entry->timeout = timeout; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", %senabled, timeout: %d ", + WIPHY_PR_ARG, NETDEV_PR_ARG, + __entry->enabled ? "" : "not ", __entry->timeout) +); + +TRACE_EVENT(rdev_connect, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_connect_params *sme), + TP_ARGS(wiphy, netdev, sme), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(bssid) + __array(char, ssid, IEEE80211_MAX_SSID_LEN + 1) + __field(enum nl80211_auth_type, auth_type) + __field(bool, privacy) + __field(u32, wpa_versions) + __field(u32, flags) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(bssid, sme->bssid); + memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1); + memcpy(__entry->ssid, sme->ssid, sme->ssid_len); + __entry->auth_type = sme->auth_type; + __entry->privacy = sme->privacy; + __entry->wpa_versions = sme->crypto.wpa_versions; + __entry->flags = sme->flags; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT + ", ssid: %s, auth type: %d, privacy: %s, wpa versions: %u, " + "flags: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid, + __entry->auth_type, BOOL_TO_STR(__entry->privacy), + __entry->wpa_versions, __entry->flags) +); + +TRACE_EVENT(rdev_set_cqm_rssi_config, + TP_PROTO(struct wiphy *wiphy, + struct net_device *netdev, s32 rssi_thold, + u32 rssi_hyst), + TP_ARGS(wiphy, netdev, rssi_thold, rssi_hyst), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(s32, rssi_thold) + __field(u32, rssi_hyst) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->rssi_thold = rssi_thold; + __entry->rssi_hyst = rssi_hyst; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT + ", rssi_thold: %d, rssi_hyst: %u ", + WIPHY_PR_ARG, NETDEV_PR_ARG, + __entry->rssi_thold, __entry->rssi_hyst) +); + +TRACE_EVENT(rdev_set_cqm_txe_config, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 rate, + u32 pkts, u32 intvl), + TP_ARGS(wiphy, netdev, rate, pkts, intvl), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(u32, rate) + __field(u32, pkts) + __field(u32, intvl) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->rate = rate; + __entry->pkts = pkts; + __entry->intvl = intvl; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", rate: %u, packets: %u, interval: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->rate, __entry->pkts, + __entry->intvl) +); + +TRACE_EVENT(rdev_disconnect, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + u16 reason_code), + TP_ARGS(wiphy, netdev, reason_code), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(u16, reason_code) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->reason_code = reason_code; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", reason code: %u", WIPHY_PR_ARG, + NETDEV_PR_ARG, __entry->reason_code) +); + +TRACE_EVENT(rdev_join_ibss, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_ibss_params *params), + TP_ARGS(wiphy, netdev, params), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(bssid) + __array(char, ssid, IEEE80211_MAX_SSID_LEN + 1) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(bssid, params->bssid); + memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1); + memcpy(__entry->ssid, params->ssid, params->ssid_len); + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", ssid: %s", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid) +); + +TRACE_EVENT(rdev_set_wiphy_params, + TP_PROTO(struct wiphy *wiphy, u32 changed), + TP_ARGS(wiphy, changed), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(u32, changed) + ), + TP_fast_assign( + WIPHY_ASSIGN; + __entry->changed = changed; + ), + TP_printk(WIPHY_PR_FMT ", changed: %u", + WIPHY_PR_ARG, __entry->changed) +); + +TRACE_EVENT(rdev_set_tx_power, + TP_PROTO(struct wiphy *wiphy, enum nl80211_tx_power_setting type, + int mbm), + TP_ARGS(wiphy, type, mbm), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(enum nl80211_tx_power_setting, type) + __field(int, mbm) + ), + TP_fast_assign( + WIPHY_ASSIGN; + __entry->type = type; + __entry->mbm = mbm; + ), + TP_printk(WIPHY_PR_FMT ", type: %d, mbm: %d", + WIPHY_PR_ARG, __entry->type, __entry->mbm) +); + +TRACE_EVENT(rdev_return_int_int, + TP_PROTO(struct wiphy *wiphy, int func_ret, int func_fill), + TP_ARGS(wiphy, func_ret, func_fill), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(int, func_ret) + __field(int, func_fill) + ), + TP_fast_assign( + WIPHY_ASSIGN; + __entry->func_ret = func_ret; + __entry->func_fill = func_fill; + ), + TP_printk(WIPHY_PR_FMT ", function returns: %d, function filled: %d", + WIPHY_PR_ARG, __entry->func_ret, __entry->func_fill) +); + +#ifdef CONFIG_NL80211_TESTMODE +TRACE_EVENT(rdev_testmode_cmd, + TP_PROTO(struct wiphy *wiphy), + TP_ARGS(wiphy), + TP_STRUCT__entry( + WIPHY_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG) +); + +TRACE_EVENT(rdev_testmode_dump, + TP_PROTO(struct wiphy *wiphy), + TP_ARGS(wiphy), + TP_STRUCT__entry( + WIPHY_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG) +); +#endif /* CONFIG_NL80211_TESTMODE */ + +TRACE_EVENT(rdev_set_bitrate_mask, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + const u8 *peer, const struct cfg80211_bitrate_mask *mask), + TP_ARGS(wiphy, netdev, peer, mask), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(peer) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(peer, peer); + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", peer: " MAC_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer)) +); + +TRACE_EVENT(rdev_mgmt_frame_register, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + u16 frame_type, bool reg), + TP_ARGS(wiphy, wdev, frame_type, reg), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __field(u16, frame_type) + __field(bool, reg) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + __entry->frame_type = frame_type; + __entry->reg = reg; + ), + TP_printk(WIPHY_PR_FMT WDEV_PR_FMT ", frame_type: %u, reg: %s ", + WIPHY_PR_ARG, WDEV_PR_ARG, __entry->frame_type, + __entry->reg ? "true" : "false") +); + +TRACE_EVENT(rdev_return_int_tx_rx, + TP_PROTO(struct wiphy *wiphy, int ret, u32 tx, u32 rx), + TP_ARGS(wiphy, ret, tx, rx), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(int, ret) + __field(u32, tx) + __field(u32, rx) + ), + TP_fast_assign( + WIPHY_ASSIGN; + __entry->ret = ret; + __entry->tx = tx; + __entry->rx = rx; + ), + TP_printk(WIPHY_PR_FMT ", returned %d, tx: %u, rx: %u", + WIPHY_PR_ARG, __entry->ret, __entry->tx, __entry->rx) +); + +TRACE_EVENT(rdev_return_void_tx_rx, + TP_PROTO(struct wiphy *wiphy, u32 tx, u32 tx_max, + u32 rx, u32 rx_max), + TP_ARGS(wiphy, tx, tx_max, rx, rx_max), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(u32, tx) + __field(u32, tx_max) + __field(u32, rx) + __field(u32, rx_max) + ), + TP_fast_assign( + WIPHY_ASSIGN; + __entry->tx = tx; + __entry->tx_max = tx_max; + __entry->rx = rx; + __entry->rx_max = rx_max; + ), + TP_printk(WIPHY_PR_FMT ", tx: %u, tx_max: %u, rx: %u, rx_max: %u ", + WIPHY_PR_ARG, __entry->tx, __entry->tx_max, __entry->rx, + __entry->rx_max) +); + +DECLARE_EVENT_CLASS(tx_rx_evt, + TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx), + TP_ARGS(wiphy, rx, tx), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(u32, tx) + __field(u32, rx) + ), + TP_fast_assign( + WIPHY_ASSIGN; + __entry->tx = tx; + __entry->rx = rx; + ), + TP_printk(WIPHY_PR_FMT ", tx: %u, rx: %u ", + WIPHY_PR_ARG, __entry->tx, __entry->rx) +); + +DEFINE_EVENT(tx_rx_evt, rdev_set_ringparam, + TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx), + TP_ARGS(wiphy, rx, tx) +); + +DEFINE_EVENT(tx_rx_evt, rdev_set_antenna, + TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx), + TP_ARGS(wiphy, rx, tx) +); + +TRACE_EVENT(rdev_sched_scan_start, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_sched_scan_request *request), + TP_ARGS(wiphy, netdev, request), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG) +); + +TRACE_EVENT(rdev_tdls_mgmt, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, size_t len), + TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code, + buf, len), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(peer) + __field(u8, action_code) + __field(u8, dialog_token) + __field(u16, status_code) + __dynamic_array(u8, buf, len) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(peer, peer); + __entry->action_code = action_code; + __entry->dialog_token = dialog_token; + __entry->status_code = status_code; + memcpy(__get_dynamic_array(buf), buf, len); + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT MAC_PR_FMT ", action_code: %u, " + "dialog_token: %u, status_code: %u, buf: %#.2x ", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), + __entry->action_code, __entry->dialog_token, + __entry->status_code, ((u8 *)__get_dynamic_array(buf))[0]) +); + +TRACE_EVENT(rdev_dump_survey, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx), + TP_ARGS(wiphy, netdev, idx), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(int, idx) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->idx = idx; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", index: %d", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx) +); + +TRACE_EVENT(rdev_return_int_survey_info, + TP_PROTO(struct wiphy *wiphy, int ret, struct survey_info *info), + TP_ARGS(wiphy, ret, info), + TP_STRUCT__entry( + WIPHY_ENTRY + CHAN_ENTRY + __field(int, ret) + __field(u64, channel_time) + __field(u64, channel_time_busy) + __field(u64, channel_time_ext_busy) + __field(u64, channel_time_rx) + __field(u64, channel_time_tx) + __field(u32, filled) + __field(s8, noise) + ), + TP_fast_assign( + WIPHY_ASSIGN; + CHAN_ASSIGN(info->channel); + __entry->ret = ret; + __entry->channel_time = info->channel_time; + __entry->channel_time_busy = info->channel_time_busy; + __entry->channel_time_ext_busy = info->channel_time_ext_busy; + __entry->channel_time_rx = info->channel_time_rx; + __entry->channel_time_tx = info->channel_time_tx; + __entry->filled = info->filled; + __entry->noise = info->noise; + ), + TP_printk(WIPHY_PR_FMT ", returned: %d, " CHAN_PR_FMT + ", channel time: %llu, channel time busy: %llu, " + "channel time extension busy: %llu, channel time rx: %llu, " + "channel time tx: %llu, filled: %u, noise: %d", + WIPHY_PR_ARG, __entry->ret, CHAN_PR_ARG, + __entry->channel_time, __entry->channel_time_busy, + __entry->channel_time_ext_busy, __entry->channel_time_rx, + __entry->channel_time_tx, __entry->filled, __entry->noise) +); + +TRACE_EVENT(rdev_tdls_oper, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + u8 *peer, enum nl80211_tdls_operation oper), + TP_ARGS(wiphy, netdev, peer, oper), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(peer) + __field(enum nl80211_tdls_operation, oper) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(peer, peer); + __entry->oper = oper; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT MAC_PR_FMT ", oper: %d", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->oper) +); + +DECLARE_EVENT_CLASS(rdev_pmksa, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_pmksa *pmksa), + TP_ARGS(wiphy, netdev, pmksa), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(bssid) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(bssid, pmksa->bssid); + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid)) +); + +TRACE_EVENT(rdev_probe_client, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + const u8 *peer), + TP_ARGS(wiphy, netdev, peer), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(peer) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(peer, peer); + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT MAC_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer)) +); + +DEFINE_EVENT(rdev_pmksa, rdev_set_pmksa, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_pmksa *pmksa), + TP_ARGS(wiphy, netdev, pmksa) +); + +DEFINE_EVENT(rdev_pmksa, rdev_del_pmksa, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_pmksa *pmksa), + TP_ARGS(wiphy, netdev, pmksa) +); + +TRACE_EVENT(rdev_remain_on_channel, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, unsigned int duration), + TP_ARGS(wiphy, wdev, chan, channel_type, duration), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + CHAN_ENTRY + __field(enum nl80211_channel_type, channel_type) + __field(unsigned int, duration) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + CHAN_ASSIGN(chan); + __entry->channel_type = channel_type; + __entry->duration = duration; + ), + TP_printk(WIPHY_PR_FMT WDEV_PR_FMT CHAN_PR_FMT ", channel type: %d, duration: %u", + WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->channel_type, + __entry->duration) +); + +TRACE_EVENT(rdev_return_int_cookie, + TP_PROTO(struct wiphy *wiphy, int ret, u64 cookie), + TP_ARGS(wiphy, ret, cookie), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(int, ret) + __field(u64, cookie) + ), + TP_fast_assign( + WIPHY_ASSIGN; + __entry->ret = ret; + __entry->cookie = cookie; + ), + TP_printk(WIPHY_PR_FMT ", returned %d, cookie: %llu", + WIPHY_PR_ARG, __entry->ret, __entry->cookie) +); + +TRACE_EVENT(rdev_cancel_remain_on_channel, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie), + TP_ARGS(wiphy, wdev, cookie), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __field(u64, cookie) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + __entry->cookie = cookie; + ), + TP_printk(WIPHY_PR_FMT WDEV_PR_FMT ", cookie: %llu", + WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie) +); + +TRACE_EVENT(rdev_mgmt_tx, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + struct ieee80211_channel *chan, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, bool no_cck, + bool dont_wait_for_ack), + TP_ARGS(wiphy, wdev, chan, offchan, channel_type, channel_type_valid, + wait, no_cck, dont_wait_for_ack), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + CHAN_ENTRY + __field(bool, offchan) + __field(enum nl80211_channel_type, channel_type) + __field(bool, channel_type_valid) + __field(unsigned int, wait) + __field(bool, no_cck) + __field(bool, dont_wait_for_ack) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + CHAN_ASSIGN(chan); + __entry->offchan = offchan; + __entry->channel_type = channel_type; + __entry->channel_type_valid = channel_type_valid; + __entry->wait = wait; + __entry->no_cck = no_cck; + __entry->dont_wait_for_ack = dont_wait_for_ack; + ), + TP_printk(WIPHY_PR_FMT WDEV_PR_FMT CHAN_PR_FMT ", offchan: %s, " + "channel type: %d, channel type valid: %s, wait: %u, " + "no cck: %s, dont wait for ack: %s", + WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, + BOOL_TO_STR(__entry->offchan), __entry->channel_type, + BOOL_TO_STR(__entry->channel_type_valid), __entry->wait, + BOOL_TO_STR(__entry->no_cck), + BOOL_TO_STR(__entry->dont_wait_for_ack)) +); + +TRACE_EVENT(rdev_set_noack_map, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + u16 noack_map), + TP_ARGS(wiphy, netdev, noack_map), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(u16, noack_map) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->noack_map = noack_map; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", noack_map: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->noack_map) +); + +TRACE_EVENT(rdev_get_et_sset_count, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int sset), + TP_ARGS(wiphy, netdev, sset), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(int, sset) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->sset = sset; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", sset: %d", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset) +); + +TRACE_EVENT(rdev_get_et_strings, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 sset), + TP_ARGS(wiphy, netdev, sset), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(u32, sset) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->sset = sset; + ), + TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", sset: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_get_channel, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev) +); + +TRACE_EVENT(rdev_return_channel, + TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *chan, + enum nl80211_channel_type type), + TP_ARGS(wiphy, chan, type), + TP_STRUCT__entry( + WIPHY_ENTRY + CHAN_ENTRY + __field(enum nl80211_channel_type, type) + ), + TP_fast_assign( + WIPHY_ASSIGN; + CHAN_ASSIGN(chan); + __entry->type = type; + ), + TP_printk(WIPHY_PR_FMT CHAN_PR_FMT ", channel type: %d", + WIPHY_PR_ARG, CHAN_PR_ARG, __entry->type) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_start_p2p_device, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_device, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev) +); + +/************************************************************* + * cfg80211 exported functions traces * + *************************************************************/ + +TRACE_EVENT(cfg80211_return_bool, + TP_PROTO(bool ret), + TP_ARGS(ret), + TP_STRUCT__entry( + __field(bool, ret) + ), + TP_fast_assign( + __entry->ret = ret; + ), + TP_printk("returned %s", BOOL_TO_STR(__entry->ret)) +); + +DECLARE_EVENT_CLASS(cfg80211_netdev_mac_evt, + TP_PROTO(struct net_device *netdev, const u8 *macaddr), + TP_ARGS(netdev, macaddr), + TP_STRUCT__entry( + NETDEV_ENTRY + MAC_ENTRY(macaddr) + ), + TP_fast_assign( + NETDEV_ASSIGN; + MAC_ASSIGN(macaddr, macaddr); + ), + TP_printk(NETDEV_PR_FMT ", mac: " MAC_PR_FMT, + NETDEV_PR_ARG, MAC_PR_ARG(macaddr)) +); + +DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_notify_new_peer_candidate, + TP_PROTO(struct net_device *netdev, const u8 *macaddr), + TP_ARGS(netdev, macaddr) +); + +DECLARE_EVENT_CLASS(netdev_evt_only, + TP_PROTO(struct net_device *netdev), + TP_ARGS(netdev), + TP_STRUCT__entry( + NETDEV_ENTRY + ), + TP_fast_assign( + NETDEV_ASSIGN; + ), + TP_printk(NETDEV_PR_FMT , NETDEV_PR_ARG) +); + +DEFINE_EVENT(netdev_evt_only, cfg80211_send_rx_auth, + TP_PROTO(struct net_device *netdev), + TP_ARGS(netdev) +); + +TRACE_EVENT(cfg80211_send_rx_assoc, + TP_PROTO(struct net_device *netdev, struct cfg80211_bss *bss), + TP_ARGS(netdev, bss), + TP_STRUCT__entry( + NETDEV_ENTRY + MAC_ENTRY(bssid) + CHAN_ENTRY + ), + TP_fast_assign( + NETDEV_ASSIGN; + MAC_ASSIGN(bssid, bss->bssid); + CHAN_ASSIGN(bss->channel); + ), + TP_printk(NETDEV_PR_FMT MAC_PR_FMT CHAN_PR_FMT, + NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG) +); + +DEFINE_EVENT(netdev_evt_only, __cfg80211_send_deauth, + TP_PROTO(struct net_device *netdev), + TP_ARGS(netdev) +); + +DEFINE_EVENT(netdev_evt_only, __cfg80211_send_disassoc, + TP_PROTO(struct net_device *netdev), + TP_ARGS(netdev) +); + +DEFINE_EVENT(netdev_evt_only, cfg80211_send_unprot_deauth, + TP_PROTO(struct net_device *netdev), + TP_ARGS(netdev) +); + +DEFINE_EVENT(netdev_evt_only, cfg80211_send_unprot_disassoc, + TP_PROTO(struct net_device *netdev), + TP_ARGS(netdev) +); + +DECLARE_EVENT_CLASS(netdev_mac_evt, + TP_PROTO(struct net_device *netdev, const u8 *mac), + TP_ARGS(netdev, mac), + TP_STRUCT__entry( + NETDEV_ENTRY + MAC_ENTRY(mac) + ), + TP_fast_assign( + NETDEV_ASSIGN; + MAC_ASSIGN(mac, mac) + ), + TP_printk(NETDEV_PR_FMT ", mac: " MAC_PR_FMT, + NETDEV_PR_ARG, MAC_PR_ARG(mac)) +); + +DEFINE_EVENT(netdev_mac_evt, cfg80211_send_auth_timeout, + TP_PROTO(struct net_device *netdev, const u8 *mac), + TP_ARGS(netdev, mac) +); + +DEFINE_EVENT(netdev_mac_evt, cfg80211_send_assoc_timeout, + TP_PROTO(struct net_device *netdev, const u8 *mac), + TP_ARGS(netdev, mac) +); + +TRACE_EVENT(cfg80211_michael_mic_failure, + TP_PROTO(struct net_device *netdev, const u8 *addr, + enum nl80211_key_type key_type, int key_id, const u8 *tsc), + TP_ARGS(netdev, addr, key_type, key_id, tsc), + TP_STRUCT__entry( + NETDEV_ENTRY + MAC_ENTRY(addr) + __field(enum nl80211_key_type, key_type) + __field(int, key_id) + __array(u8, tsc, 6) + ), + TP_fast_assign( + NETDEV_ASSIGN; + MAC_ASSIGN(addr, addr); + __entry->key_type = key_type; + __entry->key_id = key_id; + memcpy(__entry->tsc, tsc, 6); + ), + TP_printk(NETDEV_PR_FMT MAC_PR_FMT ", key type: %d, key id: %d, tsc: %pm", + NETDEV_PR_ARG, MAC_PR_ARG(addr), __entry->key_type, + __entry->key_id, __entry->tsc) +); + +TRACE_EVENT(cfg80211_ready_on_channel, + TP_PROTO(struct wireless_dev *wdev, u64 cookie, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, unsigned int duration), + TP_ARGS(wdev, cookie, chan, channel_type, duration), + TP_STRUCT__entry( + WDEV_ENTRY + __field(u64, cookie) + CHAN_ENTRY + __field(enum nl80211_channel_type, channel_type) + __field(unsigned int, duration) + ), + TP_fast_assign( + WDEV_ASSIGN; + __entry->cookie = cookie; + CHAN_ASSIGN(chan); + __entry->channel_type = channel_type; + __entry->duration = duration; + ), + TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", channel type: %d, duration: %u", + WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG, + __entry->channel_type, __entry->duration) +); + +TRACE_EVENT(cfg80211_ready_on_channel_expired, + TP_PROTO(struct wireless_dev *wdev, u64 cookie, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type), + TP_ARGS(wdev, cookie, chan, channel_type), + TP_STRUCT__entry( + WDEV_ENTRY + __field(u64, cookie) + CHAN_ENTRY + __field(enum nl80211_channel_type, channel_type) + ), + TP_fast_assign( + WDEV_ASSIGN; + __entry->cookie = cookie; + CHAN_ASSIGN(chan); + __entry->channel_type = channel_type; + ), + TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", channel type: %d", + WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG, + __entry->channel_type) +); + +TRACE_EVENT(cfg80211_new_sta, + TP_PROTO(struct net_device *netdev, const u8 *mac_addr, + struct station_info *sinfo), + TP_ARGS(netdev, mac_addr, sinfo), + TP_STRUCT__entry( + NETDEV_ENTRY + MAC_ENTRY(mac_addr) + SINFO_ENTRY + ), + TP_fast_assign( + NETDEV_ASSIGN; + MAC_ASSIGN(mac_addr, mac_addr); + SINFO_ASSIGN; + ), + TP_printk(NETDEV_PR_FMT MAC_PR_FMT, + NETDEV_PR_ARG, MAC_PR_ARG(mac_addr)) +); + +DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_del_sta, + TP_PROTO(struct net_device *netdev, const u8 *macaddr), + TP_ARGS(netdev, macaddr) +); + +TRACE_EVENT(cfg80211_rx_mgmt, + TP_PROTO(struct wireless_dev *wdev, int freq, int sig_mbm), + TP_ARGS(wdev, freq, sig_mbm), + TP_STRUCT__entry( + WDEV_ENTRY + __field(int, freq) + __field(int, sig_mbm) + ), + TP_fast_assign( + WDEV_ASSIGN; + __entry->freq = freq; + __entry->sig_mbm = sig_mbm; + ), + TP_printk(WDEV_PR_FMT ", freq: %d, sig mbm: %d", + WDEV_PR_ARG, __entry->freq, __entry->sig_mbm) +); + +TRACE_EVENT(cfg80211_mgmt_tx_status, + TP_PROTO(struct wireless_dev *wdev, u64 cookie, bool ack), + TP_ARGS(wdev, cookie, ack), + TP_STRUCT__entry( + WDEV_ENTRY + __field(u64, cookie) + __field(bool, ack) + ), + TP_fast_assign( + WDEV_ASSIGN; + __entry->cookie = cookie; + __entry->ack = ack; + ), + TP_printk(WDEV_PR_FMT", cookie: %llu, ack: %s", + WDEV_PR_ARG, __entry->cookie, BOOL_TO_STR(__entry->ack)) +); + +TRACE_EVENT(cfg80211_cqm_rssi_notify, + TP_PROTO(struct net_device *netdev, + enum nl80211_cqm_rssi_threshold_event rssi_event), + TP_ARGS(netdev, rssi_event), + TP_STRUCT__entry( + NETDEV_ENTRY + __field(enum nl80211_cqm_rssi_threshold_event, rssi_event) + ), + TP_fast_assign( + NETDEV_ASSIGN; + __entry->rssi_event = rssi_event; + ), + TP_printk(NETDEV_PR_FMT ", rssi event: %d", + NETDEV_PR_ARG, __entry->rssi_event) +); + +TRACE_EVENT(cfg80211_can_beacon_sec_chan, + TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type), + TP_ARGS(wiphy, channel, channel_type), + TP_STRUCT__entry( + WIPHY_ENTRY + CHAN_ENTRY + __field(enum nl80211_channel_type, channel_type) + ), + TP_fast_assign( + WIPHY_ASSIGN; + CHAN_ASSIGN(channel); + __entry->channel_type = channel_type; + ), + TP_printk(WIPHY_PR_FMT CHAN_PR_FMT ", channel_type: %d", + WIPHY_PR_ARG, CHAN_PR_ARG, __entry->channel_type) +); + +TRACE_EVENT(cfg80211_ch_switch_notify, + TP_PROTO(struct net_device *netdev, int freq, + enum nl80211_channel_type type), + TP_ARGS(netdev, freq, type), + TP_STRUCT__entry( + NETDEV_ENTRY + __field(int, freq) + __field(enum nl80211_channel_type, type) + ), + TP_fast_assign( + NETDEV_ASSIGN; + __entry->freq = freq; + __entry->type = type; + ), + TP_printk(NETDEV_PR_FMT ", freq: %d, type: %d", NETDEV_PR_ARG, + __entry->freq, __entry->type) +); + +DECLARE_EVENT_CLASS(cfg80211_rx_evt, + TP_PROTO(struct net_device *netdev, const u8 *addr), + TP_ARGS(netdev, addr), + TP_STRUCT__entry( + NETDEV_ENTRY + MAC_ENTRY(addr) + ), + TP_fast_assign( + NETDEV_ASSIGN; + MAC_ASSIGN(addr, addr); + ), + TP_printk(NETDEV_PR_FMT MAC_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(addr)) +); + +DEFINE_EVENT(cfg80211_rx_evt, cfg80211_ibss_joined, + TP_PROTO(struct net_device *netdev, const u8 *addr), + TP_ARGS(netdev, addr) +); + +DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_spurious_frame, + TP_PROTO(struct net_device *netdev, const u8 *addr), + TP_ARGS(netdev, addr) +); + +DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_unexpected_4addr_frame, + TP_PROTO(struct net_device *netdev, const u8 *addr), + TP_ARGS(netdev, addr) +); + +TRACE_EVENT(cfg80211_probe_status, + TP_PROTO(struct net_device *netdev, const u8 *addr, u64 cookie, + bool acked), + TP_ARGS(netdev, addr, cookie, acked), + TP_STRUCT__entry( + NETDEV_ENTRY + MAC_ENTRY(addr) + __field(u64, cookie) + __field(bool, acked) + ), + TP_fast_assign( + NETDEV_ASSIGN; + MAC_ASSIGN(addr, addr); + __entry->cookie = cookie; + __entry->acked = acked; + ), + TP_printk(NETDEV_PR_FMT MAC_PR_FMT ", cookie: %llu, acked: %s", + NETDEV_PR_ARG, MAC_PR_ARG(addr), __entry->cookie, + BOOL_TO_STR(__entry->acked)) +); + +TRACE_EVENT(cfg80211_cqm_pktloss_notify, + TP_PROTO(struct net_device *netdev, const u8 *peer, u32 num_packets), + TP_ARGS(netdev, peer, num_packets), + TP_STRUCT__entry( + NETDEV_ENTRY + MAC_ENTRY(peer) + __field(u32, num_packets) + ), + TP_fast_assign( + NETDEV_ASSIGN; + MAC_ASSIGN(peer, peer); + __entry->num_packets = num_packets; + ), + TP_printk(NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", num of lost packets: %u", + NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->num_packets) +); + +DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_gtk_rekey_notify, + TP_PROTO(struct net_device *netdev, const u8 *macaddr), + TP_ARGS(netdev, macaddr) +); + +TRACE_EVENT(cfg80211_pmksa_candidate_notify, + TP_PROTO(struct net_device *netdev, int index, const u8 *bssid, + bool preauth), + TP_ARGS(netdev, index, bssid, preauth), + TP_STRUCT__entry( + NETDEV_ENTRY + __field(int, index) + MAC_ENTRY(bssid) + __field(bool, preauth) + ), + TP_fast_assign( + NETDEV_ASSIGN; + __entry->index = index; + MAC_ASSIGN(bssid, bssid); + __entry->preauth = preauth; + ), + TP_printk(NETDEV_PR_FMT ", index:%d, bssid: " MAC_PR_FMT ", pre auth: %s", + NETDEV_PR_ARG, __entry->index, MAC_PR_ARG(bssid), + BOOL_TO_STR(__entry->preauth)) +); + +TRACE_EVENT(cfg80211_report_obss_beacon, + TP_PROTO(struct wiphy *wiphy, const u8 *frame, size_t len, + int freq, int sig_dbm), + TP_ARGS(wiphy, frame, len, freq, sig_dbm), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(int, freq) + __field(int, sig_dbm) + ), + TP_fast_assign( + WIPHY_ASSIGN; + __entry->freq = freq; + __entry->sig_dbm = sig_dbm; + ), + TP_printk(WIPHY_PR_FMT ", freq: %d, sig_dbm: %d", + WIPHY_PR_ARG, __entry->freq, __entry->sig_dbm) +); + +TRACE_EVENT(cfg80211_scan_done, + TP_PROTO(struct cfg80211_scan_request *request, bool aborted), + TP_ARGS(request, aborted), + TP_STRUCT__entry( + __field(u32, n_channels) + __dynamic_array(u8, ie, request ? request->ie_len : 0) + __array(u32, rates, IEEE80211_NUM_BANDS) + __field(u32, wdev_id) + MAC_ENTRY(wiphy_mac) + __field(bool, no_cck) + __field(bool, aborted) + ), + TP_fast_assign( + if (request) { + memcpy(__get_dynamic_array(ie), request->ie, + request->ie_len); + memcpy(__entry->rates, request->rates, + IEEE80211_NUM_BANDS); + __entry->wdev_id = request->wdev ? + request->wdev->identifier : 0; + if (request->wiphy) + MAC_ASSIGN(wiphy_mac, + request->wiphy->perm_addr); + __entry->no_cck = request->no_cck; + } + __entry->aborted = aborted; + ), + TP_printk("aborted: %s", BOOL_TO_STR(__entry->aborted)) +); + +DEFINE_EVENT(wiphy_only_evt, cfg80211_sched_scan_results, + TP_PROTO(struct wiphy *wiphy), + TP_ARGS(wiphy) +); + +DEFINE_EVENT(wiphy_only_evt, cfg80211_sched_scan_stopped, + TP_PROTO(struct wiphy *wiphy), + TP_ARGS(wiphy) +); + +TRACE_EVENT(cfg80211_get_bss, + TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel, + const u8 *bssid, const u8 *ssid, size_t ssid_len, + u16 capa_mask, u16 capa_val), + TP_ARGS(wiphy, channel, bssid, ssid, ssid_len, capa_mask, capa_val), + TP_STRUCT__entry( + WIPHY_ENTRY + CHAN_ENTRY + MAC_ENTRY(bssid) + __dynamic_array(u8, ssid, ssid_len) + __field(u16, capa_mask) + __field(u16, capa_val) + ), + TP_fast_assign( + WIPHY_ASSIGN; + CHAN_ASSIGN(channel); + MAC_ASSIGN(bssid, bssid); + memcpy(__get_dynamic_array(ssid), ssid, ssid_len); + __entry->capa_mask = capa_mask; + __entry->capa_val = capa_val; + ), + TP_printk(WIPHY_PR_FMT CHAN_PR_FMT MAC_PR_FMT ", buf: %#.2x, " + "capa_mask: %d, capa_val: %u", WIPHY_PR_ARG, CHAN_PR_ARG, + MAC_PR_ARG(bssid), ((u8 *)__get_dynamic_array(ssid))[0], + __entry->capa_mask, __entry->capa_val) +); + +TRACE_EVENT(cfg80211_inform_bss_frame, + TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel, + struct ieee80211_mgmt *mgmt, size_t len, + s32 signal), + TP_ARGS(wiphy, channel, mgmt, len, signal), + TP_STRUCT__entry( + WIPHY_ENTRY + CHAN_ENTRY + __dynamic_array(u8, mgmt, len) + __field(s32, signal) + ), + TP_fast_assign( + WIPHY_ASSIGN; + CHAN_ASSIGN(channel); + if (mgmt) + memcpy(__get_dynamic_array(mgmt), mgmt, len); + __entry->signal = signal; + ), + TP_printk(WIPHY_PR_FMT CHAN_PR_FMT "signal: %d", + WIPHY_PR_ARG, CHAN_PR_ARG, __entry->signal) +); + +DECLARE_EVENT_CLASS(cfg80211_bss_evt, + TP_PROTO(struct cfg80211_bss *pub), + TP_ARGS(pub), + TP_STRUCT__entry( + MAC_ENTRY(bssid) + CHAN_ENTRY + ), + TP_fast_assign( + MAC_ASSIGN(bssid, pub->bssid); + CHAN_ASSIGN(pub->channel); + ), + TP_printk(MAC_PR_FMT CHAN_PR_FMT, MAC_PR_ARG(bssid), CHAN_PR_ARG) +); + +DEFINE_EVENT(cfg80211_bss_evt, cfg80211_return_bss, + TP_PROTO(struct cfg80211_bss *pub), + TP_ARGS(pub) +); + +TRACE_EVENT(cfg80211_return_uint, + TP_PROTO(unsigned int ret), + TP_ARGS(ret), + TP_STRUCT__entry( + __field(unsigned int, ret) + ), + TP_fast_assign( + __entry->ret = ret; + ), + TP_printk("ret: %d", __entry->ret) +); + +TRACE_EVENT(cfg80211_return_u32, + TP_PROTO(u32 ret), + TP_ARGS(ret), + TP_STRUCT__entry( + __field(u32, ret) + ), + TP_fast_assign( + __entry->ret = ret; + ), + TP_printk("ret: %u", __entry->ret) +); + +#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace +#include <trace/define_trace.h> diff --git a/net/wireless/util.c b/net/wireless/util.c index ef35f4ef2aa6..5b6c1df72f31 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -11,6 +11,8 @@ #include <net/ip.h> #include <net/dsfield.h> #include "core.h" +#include "rdev-ops.h" + struct ieee80211_rate * ieee80211_get_response_rate(struct ieee80211_supported_band *sband, @@ -309,23 +311,21 @@ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) } EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); -static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) +unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) { int ae = meshhdr->flags & MESH_FLAGS_AE; - /* 7.1.3.5a.2 */ + /* 802.11-2012, 8.2.4.7.3 */ switch (ae) { + default: case 0: return 6; case MESH_FLAGS_AE_A4: return 12; case MESH_FLAGS_AE_A5_A6: return 18; - case (MESH_FLAGS_AE_A4 | MESH_FLAGS_AE_A5_A6): - return 24; - default: - return 6; } } +EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen); int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, enum nl80211_iftype iftype) @@ -373,6 +373,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, /* make sure meshdr->flags is on the linear part */ if (!pskb_may_pull(skb, hdrlen + 1)) return -1; + if (meshdr->flags & MESH_FLAGS_AE_A4) + return -1; if (meshdr->flags & MESH_FLAGS_AE_A5_A6) { skb_copy_bits(skb, hdrlen + offsetof(struct ieee80211s_hdr, eaddr1), @@ -397,6 +399,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, /* make sure meshdr->flags is on the linear part */ if (!pskb_may_pull(skb, hdrlen + 1)) return -1; + if (meshdr->flags & MESH_FLAGS_AE_A5_A6) + return -1; if (meshdr->flags & MESH_FLAGS_AE_A4) skb_copy_bits(skb, hdrlen + offsetof(struct ieee80211s_hdr, eaddr1), @@ -703,19 +707,18 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev) for (i = 0; i < 6; i++) { if (!wdev->connect_keys->params[i].cipher) continue; - if (rdev->ops->add_key(wdev->wiphy, dev, i, false, NULL, - &wdev->connect_keys->params[i])) { + if (rdev_add_key(rdev, dev, i, false, NULL, + &wdev->connect_keys->params[i])) { netdev_err(dev, "failed to set key %d\n", i); continue; } if (wdev->connect_keys->def == i) - if (rdev->ops->set_default_key(wdev->wiphy, dev, - i, true, true)) { + if (rdev_set_default_key(rdev, dev, i, true, true)) { netdev_err(dev, "failed to set defkey %d\n", i); continue; } if (wdev->connect_keys->defmgmt == i) - if (rdev->ops->set_default_mgmt_key(wdev->wiphy, dev, i)) + if (rdev_set_default_mgmt_key(rdev, dev, i)) netdev_err(dev, "failed to set mgtdef %d\n", i); } @@ -848,8 +851,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, cfg80211_process_rdev_events(rdev); } - err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, - ntype, flags, params); + err = rdev_change_virtual_intf(rdev, dev, ntype, flags, params); WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 494379eb464f..6488d2dbc1d7 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -19,6 +19,7 @@ #include <net/cfg80211-wext.h> #include "wext-compat.h" #include "core.h" +#include "rdev-ops.h" int cfg80211_wext_giwname(struct net_device *dev, struct iw_request_info *info, @@ -301,8 +302,7 @@ int cfg80211_wext_siwrts(struct net_device *dev, else wdev->wiphy->rts_threshold = rts->value; - err = rdev->ops->set_wiphy_params(wdev->wiphy, - WIPHY_PARAM_RTS_THRESHOLD); + err = rdev_set_wiphy_params(rdev, WIPHY_PARAM_RTS_THRESHOLD); if (err) wdev->wiphy->rts_threshold = orts; @@ -342,8 +342,7 @@ int cfg80211_wext_siwfrag(struct net_device *dev, wdev->wiphy->frag_threshold = frag->value & ~0x1; } - err = rdev->ops->set_wiphy_params(wdev->wiphy, - WIPHY_PARAM_FRAG_THRESHOLD); + err = rdev_set_wiphy_params(rdev, WIPHY_PARAM_FRAG_THRESHOLD); if (err) wdev->wiphy->frag_threshold = ofrag; @@ -396,7 +395,7 @@ static int cfg80211_wext_siwretry(struct net_device *dev, if (!changed) return 0; - err = rdev->ops->set_wiphy_params(wdev->wiphy, changed); + err = rdev_set_wiphy_params(rdev, changed); if (err) { wdev->wiphy->retry_short = oshort; wdev->wiphy->retry_long = olong; @@ -490,8 +489,8 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) err = -ENOENT; else - err = rdev->ops->del_key(&rdev->wiphy, dev, idx, - pairwise, addr); + err = rdev_del_key(rdev, dev, idx, pairwise, + addr); } wdev->wext.connect.privacy = false; /* @@ -525,8 +524,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, err = 0; if (wdev->current_bss) - err = rdev->ops->add_key(&rdev->wiphy, dev, idx, - pairwise, addr, params); + err = rdev_add_key(rdev, dev, idx, pairwise, addr, params); if (err) return err; @@ -552,8 +550,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, __cfg80211_leave_ibss(rdev, wdev->netdev, true); rejoin = true; } - err = rdev->ops->set_default_key(&rdev->wiphy, dev, - idx, true, true); + err = rdev_set_default_key(rdev, dev, idx, true, true); } if (!err) { wdev->wext.default_key = idx; @@ -566,8 +563,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC && (tx_key || (!addr && wdev->wext.default_mgmt_key == -1))) { if (wdev->current_bss) - err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, - dev, idx); + err = rdev_set_default_mgmt_key(rdev, dev, idx); if (!err) wdev->wext.default_mgmt_key = idx; return err; @@ -631,8 +627,8 @@ static int cfg80211_wext_siwencode(struct net_device *dev, err = 0; wdev_lock(wdev); if (wdev->current_bss) - err = rdev->ops->set_default_key(&rdev->wiphy, dev, - idx, true, true); + err = rdev_set_default_key(rdev, dev, idx, true, + true); if (!err) wdev->wext.default_key = idx; wdev_unlock(wdev); @@ -839,7 +835,7 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, if (!rdev->ops->get_channel) return -EINVAL; - chan = rdev->ops->get_channel(wdev->wiphy, wdev, &channel_type); + chan = rdev_get_channel(rdev, wdev, &channel_type); if (!chan) return -EINVAL; freq->m = chan->center_freq; @@ -899,7 +895,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev, return 0; } - return rdev->ops->set_tx_power(wdev->wiphy, type, DBM_TO_MBM(dbm)); + return rdev_set_tx_power(rdev, type, DBM_TO_MBM(dbm)); } static int cfg80211_wext_giwtxpower(struct net_device *dev, @@ -918,7 +914,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev, if (!rdev->ops->get_tx_power) return -EOPNOTSUPP; - err = rdev->ops->get_tx_power(wdev->wiphy, &val); + err = rdev_get_tx_power(rdev, &val); if (err) return err; @@ -1158,7 +1154,7 @@ static int cfg80211_wext_siwpower(struct net_device *dev, timeout = wrq->value / 1000; } - err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, ps, timeout); + err = rdev_set_power_mgmt(rdev, dev, ps, timeout); if (err) return err; @@ -1200,7 +1196,7 @@ static int cfg80211_wds_wext_siwap(struct net_device *dev, if (!rdev->ops->set_wds_peer) return -EOPNOTSUPP; - err = rdev->ops->set_wds_peer(wdev->wiphy, dev, (u8 *) &addr->sa_data); + err = rdev_set_wds_peer(rdev, dev, (u8 *)&addr->sa_data); if (err) return err; @@ -1272,7 +1268,7 @@ static int cfg80211_wext_siwrate(struct net_device *dev, if (!match) return -EINVAL; - return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask); + return rdev_set_bitrate_mask(rdev, dev, NULL, &mask); } static int cfg80211_wext_giwrate(struct net_device *dev, @@ -1302,7 +1298,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev, if (err) return err; - err = rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo); + err = rdev_get_station(rdev, dev, addr, &sinfo); if (err) return err; @@ -1339,7 +1335,7 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); wdev_unlock(wdev); - if (rdev->ops->get_station(&rdev->wiphy, dev, bssid, &sinfo)) + if (rdev_get_station(rdev, dev, bssid, &sinfo)) return NULL; memset(&wstats, 0, sizeof(wstats)); @@ -1474,19 +1470,19 @@ static int cfg80211_wext_siwpmksa(struct net_device *dev, if (!rdev->ops->set_pmksa) return -EOPNOTSUPP; - return rdev->ops->set_pmksa(&rdev->wiphy, dev, &cfg_pmksa); + return rdev_set_pmksa(rdev, dev, &cfg_pmksa); case IW_PMKSA_REMOVE: if (!rdev->ops->del_pmksa) return -EOPNOTSUPP; - return rdev->ops->del_pmksa(&rdev->wiphy, dev, &cfg_pmksa); + return rdev_del_pmksa(rdev, dev, &cfg_pmksa); case IW_PMKSA_FLUSH: if (!rdev->ops->flush_pmksa) return -EOPNOTSUPP; - return rdev->ops->flush_pmksa(&rdev->wiphy, dev); + return rdev_flush_pmksa(rdev, dev); default: return -EOPNOTSUPP; |