diff options
272 files changed, 6129 insertions, 60487 deletions
diff --git a/net/Kconfig b/net/Kconfig index b74076a..c0a7305 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -231,7 +231,7 @@ source "net/dns_resolver/Kconfig" source "net/batman-adv/Kconfig" config RPS - boolean "RPS" + boolean depends on SMP && SYSFS && USE_GENERIC_SMP_HELPERS default y @@ -264,7 +264,7 @@ menu "Network testing" config NET_PKTGEN tristate "Packet Generator (USE WITH CAUTION)" - depends on PROC_FS + depends on INET && PROC_FS ---help--- This module will inject preconfigured packets, at a configurable rate, out of a given interface. It is used for network interface @@ -314,7 +314,6 @@ source "net/can/Kconfig" source "net/irda/Kconfig" source "net/bluetooth/Kconfig" source "net/bluetooth_mgmt/Kconfig" -source "net/bluetooth_tizen/Kconfig" source "net/rxrpc/Kconfig" config FIB_RULES @@ -338,6 +337,7 @@ source "net/rfkill/Kconfig" source "net/9p/Kconfig" source "net/caif/Kconfig" source "net/ceph/Kconfig" +source "net/nfc/Kconfig" endif # if NET diff --git a/net/Makefile b/net/Makefile index 11fee99..da0fdf5 100644 --- a/net/Makefile +++ b/net/Makefile @@ -9,7 +9,7 @@ obj-y := nonet.o obj-$(CONFIG_NET) := socket.o core/ -tmp-$(CONFIG_COMPAT) := compat.o +tmp-$(CONFIG_COMPAT) := compat.o obj-$(CONFIG_NET) += $(tmp-y) # LLC has to be linked before the files in net/802/ @@ -37,12 +37,8 @@ obj-$(CONFIG_IRDA) += irda/ ifeq ($(CONFIG_BT_MGMT),y) obj-$(CONFIG_BT) += bluetooth_mgmt/ else -ifeq ($(CONFIG_BT_TIZEN),y) -obj-$(CONFIG_BT) += bluetooth_tizen/ -else obj-$(CONFIG_BT) += bluetooth/ endif -endif obj-$(CONFIG_SUNRPC) += sunrpc/ obj-$(CONFIG_AF_RXRPC) += rxrpc/ obj-$(CONFIG_ATM) += atm/ @@ -56,11 +52,7 @@ endif obj-$(CONFIG_IP_DCCP) += dccp/ obj-$(CONFIG_IP_SCTP) += sctp/ obj-$(CONFIG_RDS) += rds/ -ifeq ($(CONFIG_MACH_PX),y) -obj-$(CONFIG_WIRELESS) += wireless_ath/ -else obj-$(CONFIG_WIRELESS) += wireless/ -endif obj-$(CONFIG_MAC80211) += mac80211/ obj-$(CONFIG_TIPC) += tipc/ obj-$(CONFIG_NETLABEL) += netlabel/ @@ -81,3 +73,4 @@ obj-$(CONFIG_DNS_RESOLVER) += dns_resolver/ obj-$(CONFIG_CEPH_LIB) += ceph/ obj-$(CONFIG_BATMAN_ADV) += batman-adv/ obj-$(CONFIG_NET_ACTIVITY_STATS) += activity_stats.o +obj-$(CONFIG_NFC) += nfc/ diff --git a/net/TUNABLE b/net/TUNABLE deleted file mode 100644 index 9913211..0000000 --- a/net/TUNABLE +++ /dev/null @@ -1,50 +0,0 @@ -The following parameters should be tunable at compile time. Some of them -exist as sysctls too. - -This is far from complete - -Item Description ----------------------------------------------------------------------------- -MAX_LINKS Maximum number of netlink minor devices. (1-32) -RIF_TABLE_SIZE Token ring RIF cache size (tunable) -AARP_HASH_SIZE Size of Appletalk hash table (tunable) -AX25_DEF_T1 AX.25 parameters. These are all tunable via -AX25_DEF_T2 SIOCAX25SETPARMS -AX25_DEF_T3 T1-T3,N2 have the meanings in the specification -AX25_DEF_N2 -AX25_DEF_AXDEFMODE 8 = normal 128 is PE1CHL extended -AX25_DEF_IPDEFMODE 'D' - datagram 'V' - virtual connection -AX25_DEF_BACKOFF 'E'xponential 'L'inear -AX25_DEF_NETROM Allow netrom 1=Y -AX25_DF_TEXT Allow PID=Text 1=Y -AX25_DEF_WINDOW Window for normal mode -AX25_DEF_EWINDOW Window for PE1CHL mode -AX25_DEF_DIGI 1 for inband 2 for cross band 3 for both -AX25_DEF_CONMODE Allow connected modes 1=Yes -AX25_ROUTE_MAX AX.25 route cache size - no currently tunable -Unnamed (16) Number of protocol hash slots (tunable) -DEV_NUMBUFFS Number of priority levels (not easily tunable) -Unnamed (300) Maximum packet backlog queue (tunable) -MAX_IOVEC Maximum number of iovecs in a message (tunable) -MIN_WINDOW Offered minimum window (tunable) -MAX_WINDOW Offered maximum window (tunable) -MAX_HEADER Largest physical header (tunable) -MAX_ADDR_LEN Largest physical address (tunable) -SOCK_ARRAY_SIZE IP socket array hash size (tunable) -IP_MAX_MEMBERSHIPS Largest number of groups per socket (BSD style) (tunable) -16 Hard coded constant for amount of room allowed for - cache align and faster forwarding (tunable) -IP_FRAG_TIME Time we hold a fragment for. (tunable) -PORT_MASQ_BEGIN First port reserved for masquerade (tunable) -PORT_MASQ_END Last port used for masquerade (tunable) -MASQUERADE_EXPIRE_TCP_FIN Time we keep a masquerade for after a FIN -MASQUERADE_EXPIRE_UDP Time we keep a UDP masquerade for (tunable) -MAXVIFS Maximum mrouted vifs (1-32) -MFC_LINES Lines in the multicast router cache (tunable) - -NetROM parameters are tunable via an ioctl passing a struct - -4000 Size a Unix domain socket malloc falls back to - (tunable) should be 8K - a bit for 8K machines like - the ALPHA - diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig index 6c051ad..2b68d06 100644 --- a/net/batman-adv/Kconfig +++ b/net/batman-adv/Kconfig @@ -5,6 +5,7 @@ config BATMAN_ADV tristate "B.A.T.M.A.N. Advanced Meshing Protocol" depends on NET + select CRC16 default n ---help--- diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile index 2de93d0..ce68611 100644 --- a/net/batman-adv/Makefile +++ b/net/batman-adv/Makefile @@ -19,8 +19,8 @@ # obj-$(CONFIG_BATMAN_ADV) += batman-adv.o -batman-adv-y += aggregation.o batman-adv-y += bat_debugfs.o +batman-adv-y += bat_iv_ogm.o batman-adv-y += bat_sysfs.o batman-adv-y += bitarray.o batman-adv-y += gateway_client.o diff --git a/net/batman-adv/aggregation.c b/net/batman-adv/aggregation.c deleted file mode 100644 index a8c3203..0000000 --- a/net/batman-adv/aggregation.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors: - * - * Marek Lindner, Simon Wunderlich - * - * 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 "main.h" -#include "aggregation.h" -#include "send.h" -#include "routing.h" -#include "hard-interface.h" - -/* calculate the size of the tt information for a given packet */ -static int tt_len(struct batman_packet *batman_packet) -{ - return batman_packet->num_tt * ETH_ALEN; -} - -/* return true if new_packet can be aggregated with forw_packet */ -static bool can_aggregate_with(struct batman_packet *new_batman_packet, - int packet_len, - unsigned long send_time, - bool directlink, - struct hard_iface *if_incoming, - struct forw_packet *forw_packet) -{ - struct batman_packet *batman_packet = - (struct batman_packet *)forw_packet->skb->data; - int aggregated_bytes = forw_packet->packet_len + packet_len; - - /** - * we can aggregate the current packet to this aggregated packet - * if: - * - * - the send time is within our MAX_AGGREGATION_MS time - * - the resulting packet wont be bigger than - * MAX_AGGREGATION_BYTES - */ - - if (time_before(send_time, forw_packet->send_time) && - time_after_eq(send_time + msecs_to_jiffies(MAX_AGGREGATION_MS), - forw_packet->send_time) && - (aggregated_bytes <= MAX_AGGREGATION_BYTES)) { - - /** - * check aggregation compatibility - * -> direct link packets are broadcasted on - * their interface only - * -> aggregate packet if the current packet is - * a "global" packet as well as the base - * packet - */ - - /* packets without direct link flag and high TTL - * are flooded through the net */ - if ((!directlink) && - (!(batman_packet->flags & DIRECTLINK)) && - (batman_packet->ttl != 1) && - - /* own packets originating non-primary - * interfaces leave only that interface */ - ((!forw_packet->own) || - (forw_packet->if_incoming->if_num == 0))) - return true; - - /* if the incoming packet is sent via this one - * interface only - we still can aggregate */ - if ((directlink) && - (new_batman_packet->ttl == 1) && - (forw_packet->if_incoming == if_incoming) && - - /* packets from direct neighbors or - * own secondary interface packets - * (= secondary interface packets in general) */ - (batman_packet->flags & DIRECTLINK || - (forw_packet->own && - forw_packet->if_incoming->if_num != 0))) - return true; - } - - return false; -} - -/* create a new aggregated packet and add this packet to it */ -static void new_aggregated_packet(unsigned char *packet_buff, int packet_len, - unsigned long send_time, bool direct_link, - struct hard_iface *if_incoming, - int own_packet) -{ - struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); - struct forw_packet *forw_packet_aggr; - unsigned char *skb_buff; - - if (!atomic_inc_not_zero(&if_incoming->refcount)) - return; - - /* own packet should always be scheduled */ - if (!own_packet) { - if (!atomic_dec_not_zero(&bat_priv->batman_queue_left)) { - bat_dbg(DBG_BATMAN, bat_priv, - "batman packet queue full\n"); - goto out; - } - } - - forw_packet_aggr = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC); - if (!forw_packet_aggr) { - if (!own_packet) - atomic_inc(&bat_priv->batman_queue_left); - goto out; - } - - if ((atomic_read(&bat_priv->aggregated_ogms)) && - (packet_len < MAX_AGGREGATION_BYTES)) - forw_packet_aggr->skb = dev_alloc_skb(MAX_AGGREGATION_BYTES + - sizeof(struct ethhdr)); - else - forw_packet_aggr->skb = dev_alloc_skb(packet_len + - sizeof(struct ethhdr)); - - if (!forw_packet_aggr->skb) { - if (!own_packet) - atomic_inc(&bat_priv->batman_queue_left); - kfree(forw_packet_aggr); - goto out; - } - skb_reserve(forw_packet_aggr->skb, sizeof(struct ethhdr)); - - INIT_HLIST_NODE(&forw_packet_aggr->list); - - skb_buff = skb_put(forw_packet_aggr->skb, packet_len); - forw_packet_aggr->packet_len = packet_len; - memcpy(skb_buff, packet_buff, packet_len); - - forw_packet_aggr->own = own_packet; - forw_packet_aggr->if_incoming = if_incoming; - forw_packet_aggr->num_packets = 0; - forw_packet_aggr->direct_link_flags = 0; - forw_packet_aggr->send_time = send_time; - - /* save packet direct link flag status */ - if (direct_link) - forw_packet_aggr->direct_link_flags |= 1; - - /* add new packet to packet list */ - spin_lock_bh(&bat_priv->forw_bat_list_lock); - hlist_add_head(&forw_packet_aggr->list, &bat_priv->forw_bat_list); - spin_unlock_bh(&bat_priv->forw_bat_list_lock); - - /* start timer for this packet */ - INIT_DELAYED_WORK(&forw_packet_aggr->delayed_work, - send_outstanding_bat_packet); - queue_delayed_work(bat_event_workqueue, - &forw_packet_aggr->delayed_work, - send_time - jiffies); - - return; -out: - hardif_free_ref(if_incoming); -} - -/* aggregate a new packet into the existing aggregation */ -static void aggregate(struct forw_packet *forw_packet_aggr, - unsigned char *packet_buff, - int packet_len, - bool direct_link) -{ - unsigned char *skb_buff; - - skb_buff = skb_put(forw_packet_aggr->skb, packet_len); - memcpy(skb_buff, packet_buff, packet_len); - forw_packet_aggr->packet_len += packet_len; - forw_packet_aggr->num_packets++; - - /* save packet direct link flag status */ - if (direct_link) - forw_packet_aggr->direct_link_flags |= - (1 << forw_packet_aggr->num_packets); -} - -void add_bat_packet_to_list(struct bat_priv *bat_priv, - unsigned char *packet_buff, int packet_len, - struct hard_iface *if_incoming, char own_packet, - unsigned long send_time) -{ - /** - * _aggr -> pointer to the packet we want to aggregate with - * _pos -> pointer to the position in the queue - */ - struct forw_packet *forw_packet_aggr = NULL, *forw_packet_pos = NULL; - struct hlist_node *tmp_node; - struct batman_packet *batman_packet = - (struct batman_packet *)packet_buff; - bool direct_link = batman_packet->flags & DIRECTLINK ? 1 : 0; - - /* find position for the packet in the forward queue */ - spin_lock_bh(&bat_priv->forw_bat_list_lock); - /* own packets are not to be aggregated */ - if ((atomic_read(&bat_priv->aggregated_ogms)) && (!own_packet)) { - hlist_for_each_entry(forw_packet_pos, tmp_node, - &bat_priv->forw_bat_list, list) { - if (can_aggregate_with(batman_packet, - packet_len, - send_time, - direct_link, - if_incoming, - forw_packet_pos)) { - forw_packet_aggr = forw_packet_pos; - break; - } - } - } - - /* nothing to aggregate with - either aggregation disabled or no - * suitable aggregation packet found */ - if (!forw_packet_aggr) { - /* the following section can run without the lock */ - spin_unlock_bh(&bat_priv->forw_bat_list_lock); - - /** - * if we could not aggregate this packet with one of the others - * we hold it back for a while, so that it might be aggregated - * later on - */ - if ((!own_packet) && - (atomic_read(&bat_priv->aggregated_ogms))) - send_time += msecs_to_jiffies(MAX_AGGREGATION_MS); - - new_aggregated_packet(packet_buff, packet_len, - send_time, direct_link, - if_incoming, own_packet); - } else { - aggregate(forw_packet_aggr, - packet_buff, packet_len, - direct_link); - spin_unlock_bh(&bat_priv->forw_bat_list_lock); - } -} - -/* unpack the aggregated packets and process them one by one */ -void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, - int packet_len, struct hard_iface *if_incoming) -{ - struct batman_packet *batman_packet; - int buff_pos = 0; - unsigned char *tt_buff; - - batman_packet = (struct batman_packet *)packet_buff; - - do { - /* network to host order for our 32bit seqno, and the - orig_interval. */ - batman_packet->seqno = ntohl(batman_packet->seqno); - - tt_buff = packet_buff + buff_pos + BAT_PACKET_LEN; - receive_bat_packet(ethhdr, batman_packet, - tt_buff, tt_len(batman_packet), - if_incoming); - - buff_pos += BAT_PACKET_LEN + tt_len(batman_packet); - batman_packet = (struct batman_packet *) - (packet_buff + buff_pos); - } while (aggregated_packet(buff_pos, packet_len, - batman_packet->num_tt)); -} diff --git a/net/batman-adv/aggregation.h b/net/batman-adv/aggregation.h deleted file mode 100644 index 7e6d72f..0000000 --- a/net/batman-adv/aggregation.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors: - * - * Marek Lindner, Simon Wunderlich - * - * 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_AGGREGATION_H_ -#define _NET_BATMAN_ADV_AGGREGATION_H_ - -#include "main.h" - -/* is there another aggregated packet here? */ -static inline int aggregated_packet(int buff_pos, int packet_len, int num_tt) -{ - int next_buff_pos = buff_pos + BAT_PACKET_LEN + (num_tt * ETH_ALEN); - - return (next_buff_pos <= packet_len) && - (next_buff_pos <= MAX_AGGREGATION_BYTES); -} - -void add_bat_packet_to_list(struct bat_priv *bat_priv, - unsigned char *packet_buff, int packet_len, - struct hard_iface *if_incoming, char own_packet, - unsigned long send_time); -void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, - int packet_len, struct hard_iface *if_incoming); - -#endif /* _NET_BATMAN_ADV_AGGREGATION_H_ */ diff --git a/net/batman-adv/bat_debugfs.c b/net/batman-adv/bat_debugfs.c index abaeec5..d0af9bf 100644 --- a/net/batman-adv/bat_debugfs.c +++ b/net/batman-adv/bat_debugfs.c @@ -50,7 +50,8 @@ static void emit_log_char(struct debug_log *debug_log, char c) debug_log->log_start = debug_log->log_end - log_buff_len; } -static int fdebug_log(struct debug_log *debug_log, char *fmt, ...) +__printf(2, 3) +static int fdebug_log(struct debug_log *debug_log, const char *fmt, ...) { va_list args; static char debug_log_buf[256]; @@ -74,14 +75,14 @@ static int fdebug_log(struct debug_log *debug_log, char *fmt, ...) return 0; } -int debug_log(struct bat_priv *bat_priv, char *fmt, ...) +int debug_log(struct bat_priv *bat_priv, const char *fmt, ...) { va_list args; char tmp_log_buf[256]; va_start(args, fmt); vscnprintf(tmp_log_buf, sizeof(tmp_log_buf), fmt, args); - fdebug_log(bat_priv->debug_log, "[%10u] %s", + fdebug_log(bat_priv->debug_log, "[%10lu] %s", (jiffies / HZ), tmp_log_buf); va_end(args); @@ -114,7 +115,7 @@ static ssize_t log_read(struct file *file, char __user *buf, !(debug_log->log_end - debug_log->log_start)) return -EAGAIN; - if ((!buf) || (count < 0)) + if (!buf) return -EINVAL; if (count == 0) @@ -184,7 +185,7 @@ static int debug_log_setup(struct bat_priv *bat_priv) if (!bat_priv->debug_dir) goto err; - bat_priv->debug_log = kzalloc(sizeof(struct debug_log), GFP_ATOMIC); + bat_priv->debug_log = kzalloc(sizeof(*bat_priv->debug_log), GFP_ATOMIC); if (!bat_priv->debug_log) goto err; diff --git a/net/batman-adv/bat_sysfs.c b/net/batman-adv/bat_sysfs.c index 497a070..b8a7414 100644 --- a/net/batman-adv/bat_sysfs.c +++ b/net/batman-adv/bat_sysfs.c @@ -28,9 +28,31 @@ #include "gateway_client.h" #include "vis.h" -#define to_dev(obj) container_of(obj, struct device, kobj) -#define kobj_to_netdev(obj) to_net_dev(to_dev(obj->parent)) -#define kobj_to_batpriv(obj) netdev_priv(kobj_to_netdev(obj)) +static struct net_device *kobj_to_netdev(struct kobject *obj) +{ + struct device *dev = container_of(obj->parent, struct device, kobj); + return to_net_dev(dev); +} + +static struct bat_priv *kobj_to_batpriv(struct kobject *obj) +{ + struct net_device *net_dev = kobj_to_netdev(obj); + return netdev_priv(net_dev); +} + +#define UEV_TYPE_VAR "BATTYPE=" +#define UEV_ACTION_VAR "BATACTION=" +#define UEV_DATA_VAR "BATDATA=" + +static char *uev_action_str[] = { + "add", + "del", + "change" +}; + +static char *uev_type_str[] = { + "gw" +}; /* Use this, if you have customized show and store functions */ #define BAT_ATTR(_name, _mode, _show, _store) \ @@ -96,7 +118,7 @@ ssize_t show_##_name(struct kobject *kobj, struct attribute *attr, \ static int store_bool_attr(char *buff, size_t count, struct net_device *net_dev, - char *attr_name, atomic_t *attr) + const char *attr_name, atomic_t *attr) { int enabled = -1; @@ -138,16 +160,15 @@ static inline ssize_t __store_bool_attr(char *buff, size_t count, { int ret; - ret = store_bool_attr(buff, count, net_dev, (char *)attr->name, - attr_store); + ret = store_bool_attr(buff, count, net_dev, attr->name, attr_store); if (post_func && ret) post_func(net_dev); return ret; } -static int store_uint_attr(char *buff, size_t count, - struct net_device *net_dev, char *attr_name, +static int store_uint_attr(const char *buff, size_t count, + struct net_device *net_dev, const char *attr_name, unsigned int min, unsigned int max, atomic_t *attr) { unsigned long uint_val; @@ -183,15 +204,15 @@ static int store_uint_attr(char *buff, size_t count, return count; } -static inline ssize_t __store_uint_attr(char *buff, size_t count, +static inline ssize_t __store_uint_attr(const char *buff, size_t count, int min, int max, void (*post_func)(struct net_device *), - struct attribute *attr, + const struct attribute *attr, atomic_t *attr_store, struct net_device *net_dev) { int ret; - ret = store_uint_attr(buff, count, net_dev, (char *)attr->name, + ret = store_uint_attr(buff, count, net_dev, attr->name, min, max, attr_store); if (post_func && ret) post_func(net_dev); @@ -359,6 +380,7 @@ static ssize_t store_gw_bwidth(struct kobject *kobj, struct attribute *attr, BAT_ATTR_BOOL(aggregated_ogms, S_IRUGO | S_IWUSR, NULL); BAT_ATTR_BOOL(bonding, S_IRUGO | S_IWUSR, NULL); BAT_ATTR_BOOL(fragmentation, S_IRUGO | S_IWUSR, update_min_mtu); +BAT_ATTR_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL); static BAT_ATTR(vis_mode, S_IRUGO | S_IWUSR, show_vis_mode, store_vis_mode); static BAT_ATTR(gw_mode, S_IRUGO | S_IWUSR, show_gw_mode, store_gw_mode); BAT_ATTR_UINT(orig_interval, S_IRUGO | S_IWUSR, 2 * JITTER, INT_MAX, NULL); @@ -368,13 +390,14 @@ BAT_ATTR_UINT(gw_sel_class, S_IRUGO | S_IWUSR, 1, TQ_MAX_VALUE, static BAT_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, show_gw_bwidth, store_gw_bwidth); #ifdef CONFIG_BATMAN_ADV_DEBUG -BAT_ATTR_UINT(log_level, S_IRUGO | S_IWUSR, 0, 3, NULL); +BAT_ATTR_UINT(log_level, S_IRUGO | S_IWUSR, 0, 7, NULL); #endif static struct bat_attribute *mesh_attrs[] = { &bat_attr_aggregated_ogms, &bat_attr_bonding, &bat_attr_fragmentation, + &bat_attr_ap_isolation, &bat_attr_vis_mode, &bat_attr_gw_mode, &bat_attr_orig_interval, @@ -594,3 +617,60 @@ void sysfs_del_hardif(struct kobject **hardif_obj) kobject_put(*hardif_obj); *hardif_obj = NULL; } + +int throw_uevent(struct bat_priv *bat_priv, enum uev_type type, + enum uev_action action, const char *data) +{ + int ret = -1; + struct hard_iface *primary_if = NULL; + struct kobject *bat_kobj; + char *uevent_env[4] = { NULL, NULL, NULL, NULL }; + + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + bat_kobj = &primary_if->soft_iface->dev.kobj; + + uevent_env[0] = kmalloc(strlen(UEV_TYPE_VAR) + + strlen(uev_type_str[type]) + 1, + GFP_ATOMIC); + if (!uevent_env[0]) + goto out; + + sprintf(uevent_env[0], "%s%s", UEV_TYPE_VAR, uev_type_str[type]); + + uevent_env[1] = kmalloc(strlen(UEV_ACTION_VAR) + + strlen(uev_action_str[action]) + 1, + GFP_ATOMIC); + if (!uevent_env[1]) + goto out; + + sprintf(uevent_env[1], "%s%s", UEV_ACTION_VAR, uev_action_str[action]); + + /* If the event is DEL, ignore the data field */ + if (action != UEV_DEL) { + uevent_env[2] = kmalloc(strlen(UEV_DATA_VAR) + + strlen(data) + 1, GFP_ATOMIC); + if (!uevent_env[2]) + goto out; + + sprintf(uevent_env[2], "%s%s", UEV_DATA_VAR, data); + } + + ret = kobject_uevent_env(bat_kobj, KOBJ_CHANGE, uevent_env); +out: + kfree(uevent_env[0]); + kfree(uevent_env[1]); + kfree(uevent_env[2]); + + if (primary_if) + hardif_free_ref(primary_if); + + if (ret) + bat_dbg(DBG_BATMAN, bat_priv, "Impossible to send " + "uevent for (%s,%s,%s) event (err: %d)\n", + uev_type_str[type], uev_action_str[action], + (action == UEV_DEL ? "NULL" : data), ret); + return ret; +} diff --git a/net/batman-adv/bat_sysfs.h b/net/batman-adv/bat_sysfs.h index 02f1fa7..a3f75a7 100644 --- a/net/batman-adv/bat_sysfs.h +++ b/net/batman-adv/bat_sysfs.h @@ -38,5 +38,7 @@ int sysfs_add_meshif(struct net_device *dev); void sysfs_del_meshif(struct net_device *dev); int sysfs_add_hardif(struct kobject **hardif_obj, struct net_device *dev); void sysfs_del_hardif(struct kobject **hardif_obj); +int throw_uevent(struct bat_priv *bat_priv, enum uev_type type, + enum uev_action action, const char *data); #endif /* _NET_BATMAN_ADV_SYSFS_H_ */ diff --git a/net/batman-adv/bitarray.c b/net/batman-adv/bitarray.c index ad2ca92..0be9ff3 100644 --- a/net/batman-adv/bitarray.c +++ b/net/batman-adv/bitarray.c @@ -26,8 +26,8 @@ /* returns true if the corresponding bit in the given seq_bits indicates true * and curr_seqno is within range of last_seqno */ -uint8_t get_bit_status(unsigned long *seq_bits, uint32_t last_seqno, - uint32_t curr_seqno) +int get_bit_status(const unsigned long *seq_bits, uint32_t last_seqno, + uint32_t curr_seqno) { int32_t diff, word_offset, word_num; @@ -97,12 +97,12 @@ static void bit_shift(unsigned long *seq_bits, int32_t n) (seq_bits[i - word_num - 1] >> (WORD_BIT_SIZE-word_offset)); /* and the upper part of the right half and shift it left to - * it's position */ + * its position */ /* for our example that would be: word[0] = 9800 + 0076 = * 9876 */ } - /* now for our last word, i==word_num, we only have the it's "left" - * half. that's the 1000 word in our example.*/ + /* now for our last word, i==word_num, we only have its "left" half. + * that's the 1000 word in our example.*/ seq_bits[i] = (seq_bits[i - word_num] << word_offset); @@ -127,10 +127,10 @@ static void bit_reset_window(unsigned long *seq_bits) * 1 if the window was moved (either new or very old) * 0 if the window was not moved/shifted. */ -char bit_get_packet(void *priv, unsigned long *seq_bits, - int32_t seq_num_diff, int8_t set_mark) +int bit_get_packet(void *priv, unsigned long *seq_bits, + int32_t seq_num_diff, int set_mark) { - struct bat_priv *bat_priv = (struct bat_priv *)priv; + struct bat_priv *bat_priv = priv; /* sequence number is slightly older. We already got a sequence number * higher than this one, so we just mark it. */ @@ -190,7 +190,7 @@ char bit_get_packet(void *priv, unsigned long *seq_bits, /* count the hamming weight, how many good packets did we receive? just count * the 1's. */ -int bit_packet_count(unsigned long *seq_bits) +int bit_packet_count(const unsigned long *seq_bits) { int i, hamming = 0; diff --git a/net/batman-adv/bitarray.h b/net/batman-adv/bitarray.h index 769c246..9c04422 100644 --- a/net/batman-adv/bitarray.h +++ b/net/batman-adv/bitarray.h @@ -26,8 +26,8 @@ /* returns true if the corresponding bit in the given seq_bits indicates true * and curr_seqno is within range of last_seqno */ -uint8_t get_bit_status(unsigned long *seq_bits, uint32_t last_seqno, - uint32_t curr_seqno); +int get_bit_status(const unsigned long *seq_bits, uint32_t last_seqno, + uint32_t curr_seqno); /* turn corresponding bit on, so we can remember that we got the packet */ void bit_mark(unsigned long *seq_bits, int32_t n); @@ -35,10 +35,10 @@ void bit_mark(unsigned long *seq_bits, int32_t n); /* receive and process one packet, returns 1 if received seq_num is considered * new, 0 if old */ -char bit_get_packet(void *priv, unsigned long *seq_bits, - int32_t seq_num_diff, int8_t set_mark); +int bit_get_packet(void *priv, unsigned long *seq_bits, + int32_t seq_num_diff, int set_mark); /* count the hamming weight, how many good packets did we receive? */ -int bit_packet_count(unsigned long *seq_bits); +int bit_packet_count(const unsigned long *seq_bits); #endif /* _NET_BATMAN_ADV_BITARRAY_H_ */ diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 61605a0..619fb73 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -20,15 +20,22 @@ */ #include "main.h" +#include "bat_sysfs.h" #include "gateway_client.h" #include "gateway_common.h" #include "hard-interface.h" #include "originator.h" +#include "routing.h" #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/udp.h> #include <linux/if_vlan.h> +/* This is the offset of the options field in a dhcp packet starting at + * the beginning of the dhcp header */ +#define DHCP_OPTIONS_OFFSET 240 +#define DHCP_REQUEST 3 + static void gw_node_free_ref(struct gw_node *gw_node) { if (atomic_dec_and_test(&gw_node->refcount)) @@ -86,7 +93,7 @@ static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount)) new_gw_node = NULL; - curr_gw_node = bat_priv->curr_gw; + curr_gw_node = rcu_dereference_protected(bat_priv->curr_gw, 1); rcu_assign_pointer(bat_priv->curr_gw, new_gw_node); if (curr_gw_node) @@ -97,40 +104,19 @@ static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) void gw_deselect(struct bat_priv *bat_priv) { - gw_select(bat_priv, NULL); + atomic_set(&bat_priv->gw_reselect, 1); } -void gw_election(struct bat_priv *bat_priv) +static struct gw_node *gw_get_best_gw_node(struct bat_priv *bat_priv) { - struct hlist_node *node; - struct gw_node *gw_node, *curr_gw = NULL, *curr_gw_tmp = NULL; struct neigh_node *router; - uint8_t max_tq = 0; + struct hlist_node *node; + struct gw_node *gw_node, *curr_gw = NULL; uint32_t max_gw_factor = 0, tmp_gw_factor = 0; + uint8_t max_tq = 0; int down, up; - /** - * The batman daemon checks here if we already passed a full originator - * cycle in order to make sure we don't choose the first gateway we - * hear about. This check is based on the daemon's uptime which we - * don't have. - **/ - if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) - return; - - curr_gw = gw_get_selected_gw_node(bat_priv); - if (curr_gw) - goto out; - rcu_read_lock(); - if (hlist_empty(&bat_priv->gw_list)) { - bat_dbg(DBG_BATMAN, bat_priv, - "Removing selected gateway - " - "no gateway in range\n"); - gw_deselect(bat_priv); - goto unlock; - } - hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { if (gw_node->deleted) continue; @@ -139,6 +125,9 @@ void gw_election(struct bat_priv *bat_priv) if (!router) continue; + if (!atomic_inc_not_zero(&gw_node->refcount)) + goto next; + switch (atomic_read(&bat_priv->gw_sel_class)) { case 1: /* fast connection */ gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, @@ -151,8 +140,12 @@ void gw_election(struct bat_priv *bat_priv) if ((tmp_gw_factor > max_gw_factor) || ((tmp_gw_factor == max_gw_factor) && - (router->tq_avg > max_tq))) - curr_gw_tmp = gw_node; + (router->tq_avg > max_tq))) { + if (curr_gw) + gw_node_free_ref(curr_gw); + curr_gw = gw_node; + atomic_inc(&curr_gw->refcount); + } break; default: /** @@ -163,8 +156,12 @@ void gw_election(struct bat_priv *bat_priv) * soon as a better gateway appears which has * $routing_class more tq points) **/ - if (router->tq_avg > max_tq) - curr_gw_tmp = gw_node; + if (router->tq_avg > max_tq) { + if (curr_gw) + gw_node_free_ref(curr_gw); + curr_gw = gw_node; + atomic_inc(&curr_gw->refcount); + } break; } @@ -174,42 +171,81 @@ void gw_election(struct bat_priv *bat_priv) if (tmp_gw_factor > max_gw_factor) max_gw_factor = tmp_gw_factor; + gw_node_free_ref(gw_node); + +next: neigh_node_free_ref(router); } + rcu_read_unlock(); - if (curr_gw != curr_gw_tmp) { - router = orig_node_get_router(curr_gw_tmp->orig_node); - if (!router) - goto unlock; + return curr_gw; +} - if ((curr_gw) && (!curr_gw_tmp)) - bat_dbg(DBG_BATMAN, bat_priv, - "Removing selected gateway - " - "no gateway in range\n"); - else if ((!curr_gw) && (curr_gw_tmp)) - bat_dbg(DBG_BATMAN, bat_priv, - "Adding route to gateway %pM " - "(gw_flags: %i, tq: %i)\n", - curr_gw_tmp->orig_node->orig, - curr_gw_tmp->orig_node->gw_flags, - router->tq_avg); - else - bat_dbg(DBG_BATMAN, bat_priv, - "Changing route to gateway %pM " - "(gw_flags: %i, tq: %i)\n", - curr_gw_tmp->orig_node->orig, - curr_gw_tmp->orig_node->gw_flags, - router->tq_avg); +void gw_election(struct bat_priv *bat_priv) +{ + struct gw_node *curr_gw = NULL, *next_gw = NULL; + struct neigh_node *router = NULL; + char gw_addr[18] = { '\0' }; - neigh_node_free_ref(router); - gw_select(bat_priv, curr_gw_tmp); + /** + * The batman daemon checks here if we already passed a full originator + * cycle in order to make sure we don't choose the first gateway we + * hear about. This check is based on the daemon's uptime which we + * don't have. + **/ + if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) + goto out; + + if (!atomic_dec_not_zero(&bat_priv->gw_reselect)) + goto out; + + curr_gw = gw_get_selected_gw_node(bat_priv); + + next_gw = gw_get_best_gw_node(bat_priv); + + if (curr_gw == next_gw) + goto out; + + if (next_gw) { + sprintf(gw_addr, "%pM", next_gw->orig_node->orig); + + router = orig_node_get_router(next_gw->orig_node); + if (!router) { + gw_deselect(bat_priv); + goto out; + } } -unlock: - rcu_read_unlock(); + if ((curr_gw) && (!next_gw)) { + bat_dbg(DBG_BATMAN, bat_priv, + "Removing selected gateway - no gateway in range\n"); + throw_uevent(bat_priv, UEV_GW, UEV_DEL, NULL); + } else if ((!curr_gw) && (next_gw)) { + bat_dbg(DBG_BATMAN, bat_priv, + "Adding route to gateway %pM (gw_flags: %i, tq: %i)\n", + next_gw->orig_node->orig, + next_gw->orig_node->gw_flags, + router->tq_avg); + throw_uevent(bat_priv, UEV_GW, UEV_ADD, gw_addr); + } else { + bat_dbg(DBG_BATMAN, bat_priv, + "Changing route to gateway %pM " + "(gw_flags: %i, tq: %i)\n", + next_gw->orig_node->orig, + next_gw->orig_node->gw_flags, + router->tq_avg); + throw_uevent(bat_priv, UEV_GW, UEV_CHANGE, gw_addr); + } + + gw_select(bat_priv, next_gw); + out: if (curr_gw) gw_node_free_ref(curr_gw); + if (next_gw) + gw_node_free_ref(next_gw); + if (router) + neigh_node_free_ref(router); } void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) @@ -273,11 +309,10 @@ static void gw_node_add(struct bat_priv *bat_priv, struct gw_node *gw_node; int down, up; - gw_node = kmalloc(sizeof(struct gw_node), GFP_ATOMIC); + gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC); if (!gw_node) return; - memset(gw_node, 0, sizeof(struct gw_node)); INIT_HLIST_NODE(&gw_node->list); gw_node->orig_node = orig_node; atomic_set(&gw_node->refcount, 1); @@ -323,7 +358,7 @@ void gw_node_update(struct bat_priv *bat_priv, gw_node->deleted = 0; - if (new_gwflags == 0) { + if (new_gwflags == NO_FLAGS) { gw_node->deleted = jiffies; bat_dbg(DBG_BATMAN, bat_priv, "Gateway %pM removed from gateway list\n", @@ -336,7 +371,7 @@ void gw_node_update(struct bat_priv *bat_priv, goto unlock; } - if (new_gwflags == 0) + if (new_gwflags == NO_FLAGS) goto unlock; gw_node_add(bat_priv, orig_node, new_gwflags); @@ -353,7 +388,7 @@ unlock: void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node) { - return gw_node_update(bat_priv, orig_node, 0); + gw_node_update(bat_priv, orig_node, 0); } void gw_node_purge(struct bat_priv *bat_priv) @@ -361,7 +396,7 @@ void gw_node_purge(struct bat_priv *bat_priv) struct gw_node *gw_node, *curr_gw; struct hlist_node *node, *node_tmp; unsigned long timeout = 2 * PURGE_TIMEOUT * HZ; - char do_deselect = 0; + int do_deselect = 0; curr_gw = gw_get_selected_gw_node(bat_priv); @@ -394,8 +429,8 @@ void gw_node_purge(struct bat_priv *bat_priv) /** * fails if orig_node has no router */ -static int _write_buffer_text(struct bat_priv *bat_priv, - struct seq_file *seq, struct gw_node *gw_node) +static int _write_buffer_text(struct bat_priv *bat_priv, struct seq_file *seq, + const struct gw_node *gw_node) { struct gw_node *curr_gw; struct neigh_node *router; @@ -452,10 +487,9 @@ int gw_client_seq_print_text(struct seq_file *seq, void *offset) } seq_printf(seq, " %-12s (%s/%i) %17s [%10s]: gw_class ... " - "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n", + "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n", "Gateway", "#", TQ_MAX_VALUE, "Nexthop", - "outgoingIF", SOURCE_VERSION, REVISION_VERSION_STR, - primary_if->net_dev->name, + "outgoingIF", SOURCE_VERSION, primary_if->net_dev->name, primary_if->net_dev->dev_addr, net_dev->name); rcu_read_lock(); @@ -480,14 +514,75 @@ out: return ret; } -int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) +static bool is_type_dhcprequest(struct sk_buff *skb, int header_len) +{ + int ret = false; + unsigned char *p; + int pkt_len; + + if (skb_linearize(skb) < 0) + goto out; + + pkt_len = skb_headlen(skb); + + if (pkt_len < header_len + DHCP_OPTIONS_OFFSET + 1) + goto out; + + p = skb->data + header_len + DHCP_OPTIONS_OFFSET; + pkt_len -= header_len + DHCP_OPTIONS_OFFSET + 1; + + /* Access the dhcp option lists. Each entry is made up by: + * - octet 1: option type + * - octet 2: option data len (only if type != 255 and 0) + * - octet 3: option data */ + while (*p != 255 && !ret) { + /* p now points to the first octet: option type */ + if (*p == 53) { + /* type 53 is the message type option. + * Jump the len octet and go to the data octet */ + if (pkt_len < 2) + goto out; + p += 2; + + /* check if the message type is what we need */ + if (*p == DHCP_REQUEST) + ret = true; + break; + } else if (*p == 0) { + /* option type 0 (padding), just go forward */ + if (pkt_len < 1) + goto out; + pkt_len--; + p++; + } else { + /* This is any other option. So we get the length... */ + if (pkt_len < 1) + goto out; + pkt_len--; + p++; + + /* ...and then we jump over the data */ + if (pkt_len < *p) + goto out; + pkt_len -= *p; + p += (*p); + } + } +out: + return ret; +} + +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw) { struct ethhdr *ethhdr; struct iphdr *iphdr; struct ipv6hdr *ipv6hdr; struct udphdr *udphdr; struct gw_node *curr_gw; + struct neigh_node *neigh_curr = NULL, *neigh_old = NULL; unsigned int header_len = 0; + int ret = 1; if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) return 0; @@ -509,7 +604,7 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) /* check for ip header */ switch (ntohs(ethhdr->h_proto)) { case ETH_P_IP: - if (!pskb_may_pull(skb, header_len + sizeof(struct iphdr))) + if (!pskb_may_pull(skb, header_len + sizeof(*iphdr))) return 0; iphdr = (struct iphdr *)(skb->data + header_len); header_len += iphdr->ihl * 4; @@ -520,10 +615,10 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) break; case ETH_P_IPV6: - if (!pskb_may_pull(skb, header_len + sizeof(struct ipv6hdr))) + if (!pskb_may_pull(skb, header_len + sizeof(*ipv6hdr))) return 0; ipv6hdr = (struct ipv6hdr *)(skb->data + header_len); - header_len += sizeof(struct ipv6hdr); + header_len += sizeof(*ipv6hdr); /* check for udp header */ if (ipv6hdr->nexthdr != IPPROTO_UDP) @@ -534,10 +629,10 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) return 0; } - if (!pskb_may_pull(skb, header_len + sizeof(struct udphdr))) + if (!pskb_may_pull(skb, header_len + sizeof(*udphdr))) return 0; udphdr = (struct udphdr *)(skb->data + header_len); - header_len += sizeof(struct udphdr); + header_len += sizeof(*udphdr); /* check for bootp port */ if ((ntohs(ethhdr->h_proto) == ETH_P_IP) && @@ -555,7 +650,30 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) if (!curr_gw) return 0; + /* If old_gw != NULL then this packet is unicast. + * So, at this point we have to check the message type: if it is a + * DHCPREQUEST we have to decide whether to drop it or not */ + if (old_gw && curr_gw->orig_node != old_gw) { + if (is_type_dhcprequest(skb, header_len)) { + /* If the dhcp packet has been sent to a different gw, + * we have to evaluate whether the old gw is still + * reliable enough */ + neigh_curr = find_router(bat_priv, curr_gw->orig_node, + NULL); + neigh_old = find_router(bat_priv, old_gw, NULL); + if (!neigh_curr || !neigh_old) + goto free_neigh; + if (neigh_curr->tq_avg - neigh_old->tq_avg < + GW_THRESHOLD) + ret = -1; + } + } +free_neigh: + if (neigh_old) + neigh_node_free_ref(neigh_old); + if (neigh_curr) + neigh_node_free_ref(neigh_curr); if (curr_gw) gw_node_free_ref(curr_gw); - return 1; + return ret; } diff --git a/net/batman-adv/gateway_client.h b/net/batman-adv/gateway_client.h index 1ce8c60..b9b983c 100644 --- a/net/batman-adv/gateway_client.h +++ b/net/batman-adv/gateway_client.h @@ -31,6 +31,7 @@ void gw_node_update(struct bat_priv *bat_priv, void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node); void gw_node_purge(struct bat_priv *bat_priv); int gw_client_seq_print_text(struct seq_file *seq, void *offset); -int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb); +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw); #endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */ diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c index 50d3a59..18661af 100644 --- a/net/batman-adv/gateway_common.c +++ b/net/batman-adv/gateway_common.c @@ -61,9 +61,9 @@ static void kbit_to_gw_bandwidth(int down, int up, long *gw_srv_class) /* returns the up and downspeeds in kbit, calculated from the class */ void gw_bandwidth_to_kbit(uint8_t gw_srv_class, int *down, int *up) { - char sbit = (gw_srv_class & 0x80) >> 7; - char dpart = (gw_srv_class & 0x78) >> 3; - char upart = (gw_srv_class & 0x07); + int sbit = (gw_srv_class & 0x80) >> 7; + int dpart = (gw_srv_class & 0x78) >> 3; + int upart = (gw_srv_class & 0x07); if (!gw_srv_class) { *down = 0; @@ -76,10 +76,11 @@ void gw_bandwidth_to_kbit(uint8_t gw_srv_class, int *down, int *up) } static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff, - long *up, long *down) + int *up, int *down) { int ret, multi = 1; char *slash_ptr, *tmp_ptr; + long ldown, lup; slash_ptr = strchr(buff, '/'); if (slash_ptr) @@ -96,7 +97,7 @@ static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff, *tmp_ptr = '\0'; } - ret = strict_strtoul(buff, 10, down); + ret = strict_strtol(buff, 10, &ldown); if (ret) { bat_err(net_dev, "Download speed of gateway mode invalid: %s\n", @@ -104,7 +105,7 @@ static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff, return false; } - *down *= multi; + *down = ldown * multi; /* we also got some upload info */ if (slash_ptr) { @@ -121,7 +122,7 @@ static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff, *tmp_ptr = '\0'; } - ret = strict_strtoul(slash_ptr + 1, 10, up); + ret = strict_strtol(slash_ptr + 1, 10, &lup); if (ret) { bat_err(net_dev, "Upload speed of gateway mode invalid: " @@ -129,7 +130,7 @@ static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff, return false; } - *up *= multi; + *up = lup * multi; } return true; @@ -138,7 +139,8 @@ static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff, ssize_t gw_bandwidth_set(struct net_device *net_dev, char *buff, size_t count) { struct bat_priv *bat_priv = netdev_priv(net_dev); - long gw_bandwidth_tmp = 0, up = 0, down = 0; + long gw_bandwidth_tmp = 0; + int up = 0, down = 0; bool ret; ret = parse_gw_bandwidth(net_dev, buff, &up, &down); @@ -158,12 +160,11 @@ ssize_t gw_bandwidth_set(struct net_device *net_dev, char *buff, size_t count) * speeds, hence we need to calculate it back to show the number * that is going to be propagated **/ - gw_bandwidth_to_kbit((uint8_t)gw_bandwidth_tmp, - (int *)&down, (int *)&up); + gw_bandwidth_to_kbit((uint8_t)gw_bandwidth_tmp, &down, &up); gw_deselect(bat_priv); bat_info(net_dev, "Changing gateway bandwidth from: '%i' to: '%ld' " - "(propagating: %ld%s/%ld%s)\n", + "(propagating: %d%s/%d%s)\n", atomic_read(&bat_priv->gw_bandwidth), gw_bandwidth_tmp, (down > 2048 ? down / 1024 : down), (down > 2048 ? "MBit" : "KBit"), diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index dfbfccc..7704df4 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -28,6 +28,7 @@ #include "bat_sysfs.h" #include "originator.h" #include "hash.h" +#include "bat_ogm.h" #include <linux/if_arp.h> @@ -46,7 +47,7 @@ void hardif_free_rcu(struct rcu_head *rcu) kfree(hard_iface); } -struct hard_iface *hardif_get_by_netdev(struct net_device *net_dev) +struct hard_iface *hardif_get_by_netdev(const struct net_device *net_dev) { struct hard_iface *hard_iface; @@ -64,7 +65,7 @@ out: return hard_iface; } -static int is_valid_iface(struct net_device *net_dev) +static int is_valid_iface(const struct net_device *net_dev) { if (net_dev->flags & IFF_LOOPBACK) return 0; @@ -86,7 +87,7 @@ static int is_valid_iface(struct net_device *net_dev) return 1; } -static struct hard_iface *hardif_get_active(struct net_device *soft_iface) +static struct hard_iface *hardif_get_active(const struct net_device *soft_iface) { struct hard_iface *hard_iface; @@ -131,14 +132,13 @@ static void primary_if_select(struct bat_priv *bat_priv, struct hard_iface *new_hard_iface) { struct hard_iface *curr_hard_iface; - struct batman_packet *batman_packet; ASSERT_RTNL(); if (new_hard_iface && !atomic_inc_not_zero(&new_hard_iface->refcount)) new_hard_iface = NULL; - curr_hard_iface = bat_priv->primary_if; + curr_hard_iface = rcu_dereference_protected(bat_priv->primary_if, 1); rcu_assign_pointer(bat_priv->primary_if, new_hard_iface); if (curr_hard_iface) @@ -147,20 +147,11 @@ static void primary_if_select(struct bat_priv *bat_priv, if (!new_hard_iface) return; - batman_packet = (struct batman_packet *)(new_hard_iface->packet_buff); - batman_packet->flags = PRIMARIES_FIRST_HOP; - batman_packet->ttl = TTL; - + bat_ogm_init_primary(new_hard_iface); primary_if_update_addr(bat_priv); - - /*** - * hacky trick to make sure that we send the TT information via - * our new primary interface - */ - atomic_set(&bat_priv->tt_local_changed, 1); } -static bool hardif_is_iface_up(struct hard_iface *hard_iface) +static bool hardif_is_iface_up(const struct hard_iface *hard_iface) { if (hard_iface->net_dev->flags & IFF_UP) return true; @@ -168,17 +159,9 @@ static bool hardif_is_iface_up(struct hard_iface *hard_iface) return false; } -static void update_mac_addresses(struct hard_iface *hard_iface) +static void check_known_mac_addr(const struct net_device *net_dev) { - memcpy(((struct batman_packet *)(hard_iface->packet_buff))->orig, - hard_iface->net_dev->dev_addr, ETH_ALEN); - memcpy(((struct batman_packet *)(hard_iface->packet_buff))->prev_sender, - hard_iface->net_dev->dev_addr, ETH_ALEN); -} - -static void check_known_mac_addr(struct net_device *net_dev) -{ - struct hard_iface *hard_iface; + const struct hard_iface *hard_iface; rcu_read_lock(); list_for_each_entry_rcu(hard_iface, &hardif_list, list) { @@ -204,8 +187,8 @@ static void check_known_mac_addr(struct net_device *net_dev) int hardif_min_mtu(struct net_device *soft_iface) { - struct bat_priv *bat_priv = netdev_priv(soft_iface); - struct hard_iface *hard_iface; + const struct bat_priv *bat_priv = netdev_priv(soft_iface); + const struct hard_iface *hard_iface; /* allow big frames if all devices are capable to do so * (have MTU > 1500 + BAT_HEADER_LEN) */ int min_mtu = ETH_DATA_LEN; @@ -250,12 +233,12 @@ static void hardif_activate_interface(struct hard_iface *hard_iface) bat_priv = netdev_priv(hard_iface->soft_iface); - update_mac_addresses(hard_iface); + bat_ogm_update_mac(hard_iface); hard_iface->if_status = IF_TO_BE_ACTIVATED; /** * the first active interface becomes our primary interface or - * the next active interface after the old primay interface was removed + * the next active interface after the old primary interface was removed */ primary_if = primary_if_get_selected(bat_priv); if (!primary_if) @@ -285,10 +268,10 @@ static void hardif_deactivate_interface(struct hard_iface *hard_iface) update_min_mtu(hard_iface->soft_iface); } -int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name) +int hardif_enable_interface(struct hard_iface *hard_iface, + const char *iface_name) { struct bat_priv *bat_priv; - struct batman_packet *batman_packet; struct net_device *soft_iface; int ret; @@ -323,8 +306,8 @@ int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name) hard_iface->soft_iface = soft_iface; bat_priv = netdev_priv(hard_iface->soft_iface); - hard_iface->packet_len = BAT_PACKET_LEN; - hard_iface->packet_buff = kmalloc(hard_iface->packet_len, GFP_ATOMIC); + + bat_ogm_init(hard_iface); if (!hard_iface->packet_buff) { bat_err(hard_iface->soft_iface, "Can't add interface packet " @@ -333,14 +316,6 @@ int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name) goto err; } - batman_packet = (struct batman_packet *)(hard_iface->packet_buff); - batman_packet->packet_type = BAT_PACKET; - batman_packet->version = COMPAT_VERSION; - batman_packet->flags = 0; - batman_packet->ttl = 2; - batman_packet->tq = TQ_MAX_VALUE; - batman_packet->num_tt = 0; - hard_iface->if_num = bat_priv->num_ifaces; bat_priv->num_ifaces++; hard_iface->if_status = IF_INACTIVE; @@ -385,7 +360,7 @@ int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name) hard_iface->net_dev->name); /* begin scheduling originator messages on that interface */ - schedule_own_packet(hard_iface); + schedule_bat_ogm(hard_iface); out: return 0; @@ -458,12 +433,9 @@ static struct hard_iface *hardif_add_interface(struct net_device *net_dev) dev_hold(net_dev); - hard_iface = kmalloc(sizeof(struct hard_iface), GFP_ATOMIC); - if (!hard_iface) { - pr_err("Can't add interface (%s): out of memory\n", - net_dev->name); + hard_iface = kmalloc(sizeof(*hard_iface), GFP_ATOMIC); + if (!hard_iface) goto release_dev; - } ret = sysfs_add_hardif(&hard_iface->hardif_obj, net_dev); if (ret) @@ -522,7 +494,7 @@ void hardif_remove_interfaces(void) static int hard_if_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *net_dev = (struct net_device *)ptr; + struct net_device *net_dev = ptr; struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev); struct hard_iface *primary_if = NULL; struct bat_priv *bat_priv; @@ -555,7 +527,7 @@ static int hard_if_event(struct notifier_block *this, goto hardif_put; check_known_mac_addr(hard_iface->net_dev); - update_mac_addresses(hard_iface); + bat_ogm_update_mac(hard_iface); bat_priv = netdev_priv(hard_iface->soft_iface); primary_if = primary_if_get_selected(bat_priv); @@ -567,7 +539,7 @@ static int hard_if_event(struct notifier_block *this, break; default: break; - }; + } hardif_put: hardif_free_ref(hard_iface); @@ -577,14 +549,14 @@ out: return NOTIFY_DONE; } -/* receive a packet with the batman ethertype coming on a hard +/* incoming packets with the batman ethertype received on any active hard * interface */ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev) { struct bat_priv *bat_priv; - struct batman_packet *batman_packet; + struct batman_ogm_packet *batman_ogm_packet; struct hard_iface *hard_iface; int ret; @@ -616,22 +588,22 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, if (hard_iface->if_status != IF_ACTIVE) goto err_free; - batman_packet = (struct batman_packet *)skb->data; + batman_ogm_packet = (struct batman_ogm_packet *)skb->data; - if (batman_packet->version != COMPAT_VERSION) { + if (batman_ogm_packet->version != COMPAT_VERSION) { bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: incompatible batman version (%i)\n", - batman_packet->version); + batman_ogm_packet->version); goto err_free; } /* all receive handlers return whether they received or reused * the supplied skb. if not, we have to free the skb. */ - switch (batman_packet->packet_type) { + switch (batman_ogm_packet->packet_type) { /* batman originator packet */ - case BAT_PACKET: - ret = recv_bat_packet(skb, hard_iface); + case BAT_OGM: + ret = recv_bat_ogm_packet(skb, hard_iface); break; /* batman icmp packet */ @@ -658,6 +630,14 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, case BAT_VIS: ret = recv_vis_packet(skb, hard_iface); break; + /* Translation table query (request or response) */ + case BAT_TT_QUERY: + ret = recv_tt_query(skb, hard_iface); + break; + /* Roaming advertisement */ + case BAT_ROAM_ADV: + ret = recv_roam_adv(skb, hard_iface); + break; default: ret = NET_RX_DROP; } @@ -677,6 +657,36 @@ err_out: return NET_RX_DROP; } +/* This function returns true if the interface represented by ifindex is a + * 802.11 wireless device */ +bool is_wifi_iface(int ifindex) +{ + struct net_device *net_device = NULL; + bool ret = false; + + if (ifindex == NULL_IFINDEX) + goto out; + + net_device = dev_get_by_index(&init_net, ifindex); + if (!net_device) + goto out; + +#ifdef CONFIG_WIRELESS_EXT + /* pre-cfg80211 drivers have to implement WEXT, so it is possible to + * check for wireless_handlers != NULL */ + if (net_device->wireless_handlers) + ret = true; + else +#endif + /* cfg80211 drivers have to set ieee80211_ptr */ + if (net_device->ieee80211_ptr) + ret = true; +out: + if (net_device) + dev_put(net_device); + return ret; +} + struct notifier_block hard_if_notifier = { .notifier_call = hard_if_event, }; diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index 6426599..67f78d1 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -22,22 +22,27 @@ #ifndef _NET_BATMAN_ADV_HARD_INTERFACE_H_ #define _NET_BATMAN_ADV_HARD_INTERFACE_H_ -#define IF_NOT_IN_USE 0 -#define IF_TO_BE_REMOVED 1 -#define IF_INACTIVE 2 -#define IF_ACTIVE 3 -#define IF_TO_BE_ACTIVATED 4 -#define IF_I_WANT_YOU 5 +enum hard_if_state { + IF_NOT_IN_USE, + IF_TO_BE_REMOVED, + IF_INACTIVE, + IF_ACTIVE, + IF_TO_BE_ACTIVATED, + IF_I_WANT_YOU +}; extern struct notifier_block hard_if_notifier; -struct hard_iface *hardif_get_by_netdev(struct net_device *net_dev); -int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name); +struct hard_iface* +hardif_get_by_netdev(const struct net_device *net_dev); +int hardif_enable_interface(struct hard_iface *hard_iface, + const char *iface_name); void hardif_disable_interface(struct hard_iface *hard_iface); void hardif_remove_interfaces(void); int hardif_min_mtu(struct net_device *soft_iface); void update_min_mtu(struct net_device *soft_iface); void hardif_free_rcu(struct rcu_head *rcu); +bool is_wifi_iface(int ifindex); static inline void hardif_free_ref(struct hard_iface *hard_iface) { diff --git a/net/batman-adv/hash.c b/net/batman-adv/hash.c index c5213d8..2a17250 100644 --- a/net/batman-adv/hash.c +++ b/net/batman-adv/hash.c @@ -46,15 +46,16 @@ struct hashtable_t *hash_new(int size) { struct hashtable_t *hash; - hash = kmalloc(sizeof(struct hashtable_t), GFP_ATOMIC); + hash = kmalloc(sizeof(*hash), GFP_ATOMIC); if (!hash) return NULL; - hash->table = kmalloc(sizeof(struct element_t *) * size, GFP_ATOMIC); + hash->table = kmalloc(sizeof(*hash->table) * size, GFP_ATOMIC); if (!hash->table) goto free_hash; - hash->list_locks = kmalloc(sizeof(spinlock_t) * size, GFP_ATOMIC); + hash->list_locks = kmalloc(sizeof(*hash->list_locks) * size, + GFP_ATOMIC); if (!hash->list_locks) goto free_table; diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h index 434822b..d20aa71 100644 --- a/net/batman-adv/hash.h +++ b/net/batman-adv/hash.h @@ -28,12 +28,12 @@ * compare 2 element datas for their keys, * return 0 if same and not 0 if not * same */ -typedef int (*hashdata_compare_cb)(struct hlist_node *, void *); +typedef int (*hashdata_compare_cb)(const struct hlist_node *, const void *); /* the hashfunction, should return an index * based on the key in the data of the first * argument and the size the second */ -typedef int (*hashdata_choose_cb)(void *, int); +typedef int (*hashdata_choose_cb)(const void *, int); typedef void (*hashdata_free_cb)(struct hlist_node *, void *); struct hashtable_t { @@ -76,19 +76,30 @@ static inline void hash_delete(struct hashtable_t *hash, hash_destroy(hash); } -/* adds data to the hashtable. returns 0 on success, -1 on error */ +/** + * hash_add - adds data to the hashtable + * @hash: storage hash table + * @compare: callback to determine if 2 hash elements are identical + * @choose: callback calculating the hash index + * @data: data passed to the aforementioned callbacks as argument + * @data_node: to be added element + * + * Returns 0 on success, 1 if the element already is in the hash + * and -1 on error. + */ + static inline int hash_add(struct hashtable_t *hash, hashdata_compare_cb compare, hashdata_choose_cb choose, - void *data, struct hlist_node *data_node) + const void *data, struct hlist_node *data_node) { - int index; + int index, ret = -1; struct hlist_head *head; struct hlist_node *node; spinlock_t *list_lock; /* spinlock to protect write access */ if (!hash) - goto err; + goto out; index = choose(data, hash->size); head = &hash->table[index]; @@ -99,6 +110,7 @@ static inline int hash_add(struct hashtable_t *hash, if (!compare(node, data)) continue; + ret = 1; goto err_unlock; } rcu_read_unlock(); @@ -108,12 +120,13 @@ static inline int hash_add(struct hashtable_t *hash, hlist_add_head_rcu(data_node, head); spin_unlock_bh(list_lock); - return 0; + ret = 0; + goto out; err_unlock: rcu_read_unlock(); -err: - return -1; +out: + return ret; } /* removes data from hash, if found. returns pointer do data on success, so you diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c index ad7d8b2..0c82ce3 100644 --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c @@ -46,7 +46,7 @@ static int bat_socket_open(struct inode *inode, struct file *file) nonseekable_open(inode, file); - socket_client = kmalloc(sizeof(struct socket_client), GFP_KERNEL); + socket_client = kmalloc(sizeof(*socket_client), GFP_KERNEL); if (!socket_client) return -ENOMEM; @@ -309,7 +309,7 @@ static void bat_socket_add_packet(struct socket_client *socket_client, { struct socket_packet *socket_packet; - socket_packet = kmalloc(sizeof(struct socket_packet), GFP_ATOMIC); + socket_packet = kmalloc(sizeof(*socket_packet), GFP_ATOMIC); if (!socket_packet) return; diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 0a7cee0..fb87bdc 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -58,9 +58,8 @@ static int __init batman_init(void) register_netdevice_notifier(&hard_if_notifier); - pr_info("B.A.T.M.A.N. advanced %s%s (compatibility version %i) " - "loaded\n", SOURCE_VERSION, REVISION_VERSION_STR, - COMPAT_VERSION); + pr_info("B.A.T.M.A.N. advanced %s (compatibility version %i) " + "loaded\n", SOURCE_VERSION, COMPAT_VERSION); return 0; } @@ -84,8 +83,10 @@ int mesh_init(struct net_device *soft_iface) spin_lock_init(&bat_priv->forw_bat_list_lock); spin_lock_init(&bat_priv->forw_bcast_list_lock); - spin_lock_init(&bat_priv->tt_lhash_lock); - spin_lock_init(&bat_priv->tt_ghash_lock); + spin_lock_init(&bat_priv->tt_changes_list_lock); + spin_lock_init(&bat_priv->tt_req_list_lock); + spin_lock_init(&bat_priv->tt_roam_list_lock); + spin_lock_init(&bat_priv->tt_buff_lock); spin_lock_init(&bat_priv->gw_list_lock); spin_lock_init(&bat_priv->vis_hash_lock); spin_lock_init(&bat_priv->vis_list_lock); @@ -96,27 +97,26 @@ int mesh_init(struct net_device *soft_iface) INIT_HLIST_HEAD(&bat_priv->forw_bcast_list); INIT_HLIST_HEAD(&bat_priv->gw_list); INIT_HLIST_HEAD(&bat_priv->softif_neigh_vids); + INIT_LIST_HEAD(&bat_priv->tt_changes_list); + INIT_LIST_HEAD(&bat_priv->tt_req_list); + INIT_LIST_HEAD(&bat_priv->tt_roam_list); if (originator_init(bat_priv) < 1) goto err; - if (tt_local_init(bat_priv) < 1) + if (tt_init(bat_priv) < 1) goto err; - if (tt_global_init(bat_priv) < 1) - goto err; - - tt_local_add(soft_iface, soft_iface->dev_addr); + tt_local_add(soft_iface, soft_iface->dev_addr, NULL_IFINDEX); if (vis_init(bat_priv) < 1) goto err; + atomic_set(&bat_priv->gw_reselect, 0); atomic_set(&bat_priv->mesh_state, MESH_ACTIVE); goto end; err: - pr_err("Unable to allocate memory for mesh information structures: " - "out of mem ?\n"); mesh_free(soft_iface); return -1; @@ -137,8 +137,7 @@ void mesh_free(struct net_device *soft_iface) gw_node_purge(bat_priv); originator_free(bat_priv); - tt_local_free(bat_priv); - tt_global_free(bat_priv); + tt_free(bat_priv); softif_neigh_purge(bat_priv); @@ -155,9 +154,9 @@ void dec_module_count(void) module_put(THIS_MODULE); } -int is_my_mac(uint8_t *addr) +int is_my_mac(const uint8_t *addr) { - struct hard_iface *hard_iface; + const struct hard_iface *hard_iface; rcu_read_lock(); list_for_each_entry_rcu(hard_iface, &hardif_list, list) { @@ -182,8 +181,4 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_SUPPORTED_DEVICE(DRIVER_DEVICE); -#ifdef REVISION_VERSION -MODULE_VERSION(SOURCE_VERSION "-" REVISION_VERSION); -#else MODULE_VERSION(SOURCE_VERSION); -#endif diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 148b49e..964ad4d 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -27,8 +27,9 @@ #define DRIVER_DESC "B.A.T.M.A.N. advanced" #define DRIVER_DEVICE "batman-adv" -#define SOURCE_VERSION "next" - +#ifndef SOURCE_VERSION +#define SOURCE_VERSION "2011.4.0" +#endif /* B.A.T.M.A.N. parameters */ @@ -42,15 +43,27 @@ * -> TODO: check influence on TQ_LOCAL_WINDOW_SIZE */ #define PURGE_TIMEOUT 200 #define TT_LOCAL_TIMEOUT 3600 /* in seconds */ - -/* sliding packet range of received originator messages in squence numbers +#define TT_CLIENT_ROAM_TIMEOUT 600 +/* sliding packet range of received originator messages in sequence numbers * (should be a multiple of our word size) */ #define TQ_LOCAL_WINDOW_SIZE 64 +#define TT_REQUEST_TIMEOUT 3 /* seconds we have to keep pending tt_req */ + #define TQ_GLOBAL_WINDOW_SIZE 5 #define TQ_LOCAL_BIDRECT_SEND_MINIMUM 1 #define TQ_LOCAL_BIDRECT_RECV_MINIMUM 1 #define TQ_TOTAL_BIDRECT_LIMIT 1 +#define TT_OGM_APPEND_MAX 3 /* number of OGMs sent with the last tt diff */ + +#define ROAMING_MAX_TIME 20 /* Time in which a client can roam at most + * ROAMING_MAX_COUNT times */ +#define ROAMING_MAX_COUNT 5 + +#define NO_FLAGS 0 + +#define NULL_IFINDEX 0 /* dummy ifindex used to avoid iface checks */ + #define NUM_WORDS (TQ_LOCAL_WINDOW_SIZE / WORD_BIT_SIZE) #define LOG_BUF_LEN 8192 /* has to be a power of 2 */ @@ -72,13 +85,27 @@ #define RESET_PROTECTION_MS 30000 #define EXPECTED_SEQNO_RANGE 65536 -#define MESH_INACTIVE 0 -#define MESH_ACTIVE 1 -#define MESH_DEACTIVATING 2 +enum mesh_state { + MESH_INACTIVE, + MESH_ACTIVE, + MESH_DEACTIVATING +}; #define BCAST_QUEUE_LEN 256 #define BATMAN_QUEUE_LEN 256 +enum uev_action { + UEV_ADD = 0, + UEV_DEL, + UEV_CHANGE +}; + +enum uev_type { + UEV_GW = 0 +}; + +#define GW_THRESHOLD 50 + /* * Debug Messages */ @@ -89,10 +116,12 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt /* all messages related to routing / flooding / broadcasting / etc */ -#define DBG_BATMAN 1 -/* route or tt entry added / changed / deleted */ -#define DBG_ROUTES 2 -#define DBG_ALL 3 +enum dbg_level { + DBG_BATMAN = 1 << 0, + DBG_ROUTES = 1 << 1, /* route added / changed / deleted */ + DBG_TT = 1 << 2, /* translation table operations */ + DBG_ALL = 7 +}; /* @@ -106,7 +135,7 @@ #include <linux/mutex.h> /* mutex */ #include <linux/module.h> /* needed by all modules */ #include <linux/netdevice.h> /* netdevice */ -#include <linux/etherdevice.h> /* ethernet address classifaction */ +#include <linux/etherdevice.h> /* ethernet address classification */ #include <linux/if_ether.h> /* ethernet header */ #include <linux/poll.h> /* poll_table */ #include <linux/kthread.h> /* kernel threads */ @@ -118,12 +147,6 @@ #include <linux/seq_file.h> #include "types.h" -#ifndef REVISION_VERSION -#define REVISION_VERSION_STR "" -#else -#define REVISION_VERSION_STR " "REVISION_VERSION -#endif - extern struct list_head hardif_list; extern unsigned char broadcast_addr[]; @@ -133,10 +156,10 @@ int mesh_init(struct net_device *soft_iface); void mesh_free(struct net_device *soft_iface); void inc_module_count(void); void dec_module_count(void); -int is_my_mac(uint8_t *addr); +int is_my_mac(const uint8_t *addr); #ifdef CONFIG_BATMAN_ADV_DEBUG -int debug_log(struct bat_priv *bat_priv, char *fmt, ...); +int debug_log(struct bat_priv *bat_priv, const char *fmt, ...) __printf(2, 3); #define bat_dbg(type, bat_priv, fmt, arg...) \ do { \ @@ -145,9 +168,10 @@ int debug_log(struct bat_priv *bat_priv, char *fmt, ...); } \ while (0) #else /* !CONFIG_BATMAN_ADV_DEBUG */ -static inline void bat_dbg(char type __always_unused, +__printf(3, 4) +static inline void bat_dbg(int type __always_unused, struct bat_priv *bat_priv __always_unused, - char *fmt __always_unused, ...) + const char *fmt __always_unused, ...) { } #endif @@ -172,11 +196,32 @@ static inline void bat_dbg(char type __always_unused, * * note: can't use compare_ether_addr() as it requires aligned memory */ -static inline int compare_eth(void *data1, void *data2) + +static inline int compare_eth(const void *data1, const void *data2) { return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); } + #define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) +/* Returns the smallest signed integer in two's complement with the sizeof x */ +#define smallest_signed_int(x) (1u << (7u + 8u * (sizeof(x) - 1u))) + +/* Checks if a sequence number x is a predecessor/successor of y. + * they handle overflows/underflows and can correctly check for a + * predecessor/successor unless the variable sequence number has grown by + * more then 2**(bitwidth(x)-1)-1. + * This means that for a uint8_t with the maximum value 255, it would think: + * - when adding nothing - it is neither a predecessor nor a successor + * - before adding more than 127 to the starting value - it is a predecessor, + * - when adding 128 - it is neither a predecessor nor a successor, + * - after adding more than 127 to the starting value - it is a successor */ +#define seq_before(x, y) ({typeof(x) _d1 = (x); \ + typeof(y) _d2 = (y); \ + typeof(x) _dummy = (_d1 - _d2); \ + (void) (&_d1 == &_d2); \ + _dummy > smallest_signed_int(_dummy); }) +#define seq_after(x, y) seq_before(y, x) + #endif /* _NET_BATMAN_ADV_MAIN_H_ */ diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 40a30bb..0e5b772 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -37,6 +37,14 @@ static void start_purge_timer(struct bat_priv *bat_priv) queue_delayed_work(bat_event_workqueue, &bat_priv->orig_work, 1 * HZ); } +/* returns 1 if they are the same originator */ +static int compare_orig(const struct hlist_node *node, const void *data2) +{ + const void *data1 = container_of(node, struct orig_node, hash_entry); + + return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); +} + int originator_init(struct bat_priv *bat_priv) { if (bat_priv->orig_hash) @@ -77,7 +85,7 @@ struct neigh_node *orig_node_get_router(struct orig_node *orig_node) struct neigh_node *create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, - uint8_t *neigh, + const uint8_t *neigh, struct hard_iface *if_incoming) { struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); @@ -86,7 +94,7 @@ struct neigh_node *create_neighbor(struct orig_node *orig_node, bat_dbg(DBG_BATMAN, bat_priv, "Creating new last-hop neighbor of originator\n"); - neigh_node = kzalloc(sizeof(struct neigh_node), GFP_ATOMIC); + neigh_node = kzalloc(sizeof(*neigh_node), GFP_ATOMIC); if (!neigh_node) return NULL; @@ -137,6 +145,7 @@ static void orig_node_free_rcu(struct rcu_head *rcu) tt_global_del_orig(orig_node->bat_priv, orig_node, "originator timed out"); + kfree(orig_node->tt_buff); kfree(orig_node->bcast_own); kfree(orig_node->bcast_own_sum); kfree(orig_node); @@ -183,7 +192,7 @@ void originator_free(struct bat_priv *bat_priv) /* this function finds or creates an originator entry for the given * address if it does not exits */ -struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) +struct orig_node *get_orig_node(struct bat_priv *bat_priv, const uint8_t *addr) { struct orig_node *orig_node; int size; @@ -196,7 +205,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) bat_dbg(DBG_BATMAN, bat_priv, "Creating new originator: %pM\n", addr); - orig_node = kzalloc(sizeof(struct orig_node), GFP_ATOMIC); + orig_node = kzalloc(sizeof(*orig_node), GFP_ATOMIC); if (!orig_node) return NULL; @@ -205,14 +214,20 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) spin_lock_init(&orig_node->ogm_cnt_lock); spin_lock_init(&orig_node->bcast_seqno_lock); spin_lock_init(&orig_node->neigh_list_lock); + spin_lock_init(&orig_node->tt_buff_lock); /* extra reference for return */ atomic_set(&orig_node->refcount, 2); + orig_node->tt_poss_change = false; orig_node->bat_priv = bat_priv; memcpy(orig_node->orig, addr, ETH_ALEN); orig_node->router = NULL; + orig_node->tt_crc = 0; + atomic_set(&orig_node->last_ttvn, 0); orig_node->tt_buff = NULL; + orig_node->tt_buff_len = 0; + atomic_set(&orig_node->tt_size, 0); orig_node->bcast_seqno_reset = jiffies - 1 - msecs_to_jiffies(RESET_PROTECTION_MS); orig_node->batman_seqno_reset = jiffies - 1 @@ -237,7 +252,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) hash_added = hash_add(bat_priv->orig_hash, compare_orig, choose_orig, orig_node, &orig_node->hash_entry); - if (hash_added < 0) + if (hash_added != 0) goto free_bcast_own_sum; return orig_node; @@ -321,10 +336,7 @@ static bool purge_orig_node(struct bat_priv *bat_priv, } else { if (purge_orig_neighbors(bat_priv, orig_node, &best_neigh_node)) { - update_routes(bat_priv, orig_node, - best_neigh_node, - orig_node->tt_buff, - orig_node->tt_buff_len); + update_route(bat_priv, orig_node, best_neigh_node); } } @@ -419,9 +431,8 @@ int orig_seq_print_text(struct seq_file *seq, void *offset) goto out; } - seq_printf(seq, "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n", - SOURCE_VERSION, REVISION_VERSION_STR, - primary_if->net_dev->name, + seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n", + SOURCE_VERSION, primary_if->net_dev->name, primary_if->net_dev->dev_addr, net_dev->name); seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n", "Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop", @@ -481,10 +492,8 @@ static int orig_node_add_if(struct orig_node *orig_node, int max_if_num) data_ptr = kmalloc(max_if_num * sizeof(unsigned long) * NUM_WORDS, GFP_ATOMIC); - if (!data_ptr) { - pr_err("Can't resize orig: out of memory\n"); + if (!data_ptr) return -1; - } memcpy(data_ptr, orig_node->bcast_own, (max_if_num - 1) * sizeof(unsigned long) * NUM_WORDS); @@ -492,10 +501,8 @@ static int orig_node_add_if(struct orig_node *orig_node, int max_if_num) orig_node->bcast_own = data_ptr; data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC); - if (!data_ptr) { - pr_err("Can't resize orig: out of memory\n"); + if (!data_ptr) return -1; - } memcpy(data_ptr, orig_node->bcast_own_sum, (max_if_num - 1) * sizeof(uint8_t)); @@ -550,16 +557,14 @@ static int orig_node_del_if(struct orig_node *orig_node, chunk_size = sizeof(unsigned long) * NUM_WORDS; data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC); - if (!data_ptr) { - pr_err("Can't resize orig: out of memory\n"); + if (!data_ptr) return -1; - } /* copy first part */ memcpy(data_ptr, orig_node->bcast_own, del_if_num * chunk_size); /* copy second part */ - memcpy(data_ptr + del_if_num * chunk_size, + memcpy((char *)data_ptr + del_if_num * chunk_size, orig_node->bcast_own + ((del_if_num + 1) * chunk_size), (max_if_num - del_if_num) * chunk_size); @@ -571,15 +576,13 @@ free_bcast_own: goto free_own_sum; data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC); - if (!data_ptr) { - pr_err("Can't resize orig: out of memory\n"); + if (!data_ptr) return -1; - } memcpy(data_ptr, orig_node->bcast_own_sum, del_if_num * sizeof(uint8_t)); - memcpy(data_ptr + del_if_num * sizeof(uint8_t), + memcpy((char *)data_ptr + del_if_num * sizeof(uint8_t), orig_node->bcast_own_sum + ((del_if_num + 1) * sizeof(uint8_t)), (max_if_num - del_if_num) * sizeof(uint8_t)); diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index e1d641f..cfc1f60 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h @@ -28,10 +28,10 @@ int originator_init(struct bat_priv *bat_priv); void originator_free(struct bat_priv *bat_priv); void purge_orig_ref(struct bat_priv *bat_priv); void orig_node_free_ref(struct orig_node *orig_node); -struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr); +struct orig_node *get_orig_node(struct bat_priv *bat_priv, const uint8_t *addr); struct neigh_node *create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, - uint8_t *neigh, + const uint8_t *neigh, struct hard_iface *if_incoming); void neigh_node_free_ref(struct neigh_node *neigh_node); struct neigh_node *orig_node_get_router(struct orig_node *orig_node); @@ -40,19 +40,11 @@ int orig_hash_add_if(struct hard_iface *hard_iface, int max_if_num); int orig_hash_del_if(struct hard_iface *hard_iface, int max_if_num); -/* returns 1 if they are the same originator */ -static inline int compare_orig(struct hlist_node *node, void *data2) -{ - void *data1 = container_of(node, struct orig_node, hash_entry); - - return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); -} - /* hashfunction to choose an entry in a hash table of given size */ /* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ -static inline int choose_orig(void *data, int32_t size) +static inline int choose_orig(const void *data, int32_t size) { - unsigned char *key = data; + const unsigned char *key = data; uint32_t hash = 0; size_t i; @@ -70,7 +62,7 @@ static inline int choose_orig(void *data, int32_t size) } static inline struct orig_node *orig_hash_find(struct bat_priv *bat_priv, - void *data) + const void *data) { struct hashtable_t *hash = bat_priv->orig_hash; struct hlist_head *head; diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index eda9965..4d9e54c 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -24,59 +24,99 @@ #define ETH_P_BATMAN 0x4305 /* unofficial/not registered Ethertype */ -#define BAT_PACKET 0x01 -#define BAT_ICMP 0x02 -#define BAT_UNICAST 0x03 -#define BAT_BCAST 0x04 -#define BAT_VIS 0x05 -#define BAT_UNICAST_FRAG 0x06 +enum bat_packettype { + BAT_OGM = 0x01, + BAT_ICMP = 0x02, + BAT_UNICAST = 0x03, + BAT_BCAST = 0x04, + BAT_VIS = 0x05, + BAT_UNICAST_FRAG = 0x06, + BAT_TT_QUERY = 0x07, + BAT_ROAM_ADV = 0x08 +}; /* this file is included by batctl which needs these defines */ -#define COMPAT_VERSION 12 -#define DIRECTLINK 0x40 -#define VIS_SERVER 0x20 -#define PRIMARIES_FIRST_HOP 0x10 +#define COMPAT_VERSION 14 + +enum batman_flags { + PRIMARIES_FIRST_HOP = 1 << 4, + VIS_SERVER = 1 << 5, + DIRECTLINK = 1 << 6 +}; /* ICMP message types */ -#define ECHO_REPLY 0 -#define DESTINATION_UNREACHABLE 3 -#define ECHO_REQUEST 8 -#define TTL_EXCEEDED 11 -#define PARAMETER_PROBLEM 12 +enum icmp_packettype { + ECHO_REPLY = 0, + DESTINATION_UNREACHABLE = 3, + ECHO_REQUEST = 8, + TTL_EXCEEDED = 11, + PARAMETER_PROBLEM = 12 +}; /* vis defines */ -#define VIS_TYPE_SERVER_SYNC 0 -#define VIS_TYPE_CLIENT_UPDATE 1 +enum vis_packettype { + VIS_TYPE_SERVER_SYNC = 0, + VIS_TYPE_CLIENT_UPDATE = 1 +}; /* fragmentation defines */ -#define UNI_FRAG_HEAD 0x01 -#define UNI_FRAG_LARGETAIL 0x02 - -struct batman_packet { +enum unicast_frag_flags { + UNI_FRAG_HEAD = 1 << 0, + UNI_FRAG_LARGETAIL = 1 << 1 +}; + +/* TT_QUERY subtypes */ +#define TT_QUERY_TYPE_MASK 0x3 + +enum tt_query_packettype { + TT_REQUEST = 0, + TT_RESPONSE = 1 +}; + +/* TT_QUERY flags */ +enum tt_query_flags { + TT_FULL_TABLE = 1 << 2 +}; + +/* TT_CLIENT flags. + * Flags from 1 to 1 << 7 are sent on the wire, while flags from 1 << 8 to + * 1 << 15 are used for local computation only */ +enum tt_client_flags { + TT_CLIENT_DEL = 1 << 0, + TT_CLIENT_ROAM = 1 << 1, + TT_CLIENT_WIFI = 1 << 2, + TT_CLIENT_NOPURGE = 1 << 8, + TT_CLIENT_NEW = 1 << 9, + TT_CLIENT_PENDING = 1 << 10 +}; + +struct batman_ogm_packet { uint8_t packet_type; uint8_t version; /* batman version field */ + uint8_t ttl; uint8_t flags; /* 0x40: DIRECTLINK flag, 0x20 VIS_SERVER flag... */ - uint8_t tq; uint32_t seqno; uint8_t orig[6]; uint8_t prev_sender[6]; - uint8_t ttl; - uint8_t num_tt; uint8_t gw_flags; /* flags related to gateway class */ - uint8_t align; + uint8_t tq; + uint8_t tt_num_changes; + uint8_t ttvn; /* translation table version number */ + uint16_t tt_crc; } __packed; -#define BAT_PACKET_LEN sizeof(struct batman_packet) +#define BATMAN_OGM_LEN sizeof(struct batman_ogm_packet) struct icmp_packet { uint8_t packet_type; uint8_t version; /* batman version field */ - uint8_t msg_type; /* see ICMP message types above */ uint8_t ttl; + uint8_t msg_type; /* see ICMP message types above */ uint8_t dst[6]; uint8_t orig[6]; uint16_t seqno; uint8_t uid; + uint8_t reserved; } __packed; #define BAT_RR_LEN 16 @@ -86,8 +126,8 @@ struct icmp_packet { struct icmp_packet_rr { uint8_t packet_type; uint8_t version; /* batman version field */ - uint8_t msg_type; /* see ICMP message types above */ uint8_t ttl; + uint8_t msg_type; /* see ICMP message types above */ uint8_t dst[6]; uint8_t orig[6]; uint16_t seqno; @@ -99,16 +139,19 @@ struct icmp_packet_rr { struct unicast_packet { uint8_t packet_type; uint8_t version; /* batman version field */ - uint8_t dest[6]; uint8_t ttl; + uint8_t ttvn; /* destination translation table version number */ + uint8_t dest[6]; } __packed; struct unicast_frag_packet { uint8_t packet_type; uint8_t version; /* batman version field */ - uint8_t dest[6]; uint8_t ttl; + uint8_t ttvn; /* destination translation table version number */ + uint8_t dest[6]; uint8_t flags; + uint8_t align; uint8_t orig[6]; uint16_t seqno; } __packed; @@ -116,21 +159,61 @@ struct unicast_frag_packet { struct bcast_packet { uint8_t packet_type; uint8_t version; /* batman version field */ - uint8_t orig[6]; uint8_t ttl; + uint8_t reserved; uint32_t seqno; + uint8_t orig[6]; } __packed; struct vis_packet { uint8_t packet_type; uint8_t version; /* batman version field */ + uint8_t ttl; /* TTL */ uint8_t vis_type; /* which type of vis-participant sent this? */ - uint8_t entries; /* number of entries behind this struct */ uint32_t seqno; /* sequence number */ - uint8_t ttl; /* TTL */ + uint8_t entries; /* number of entries behind this struct */ + uint8_t reserved; uint8_t vis_orig[6]; /* originator that announces its neighbors */ uint8_t target_orig[6]; /* who should receive this packet */ uint8_t sender_orig[6]; /* who sent or rebroadcasted this packet */ } __packed; +struct tt_query_packet { + uint8_t packet_type; + uint8_t version; /* batman version field */ + uint8_t ttl; + /* the flag field is a combination of: + * - TT_REQUEST or TT_RESPONSE + * - TT_FULL_TABLE */ + uint8_t flags; + uint8_t dst[ETH_ALEN]; + uint8_t src[ETH_ALEN]; + /* the ttvn field is: + * if TT_REQUEST: ttvn that triggered the + * request + * if TT_RESPONSE: new ttvn for the src + * orig_node */ + uint8_t ttvn; + /* tt_data field is: + * if TT_REQUEST: crc associated with the + * ttvn + * if TT_RESPONSE: table_size */ + uint16_t tt_data; +} __packed; + +struct roam_adv_packet { + uint8_t packet_type; + uint8_t version; + uint8_t ttl; + uint8_t reserved; + uint8_t dst[ETH_ALEN]; + uint8_t src[ETH_ALEN]; + uint8_t client[ETH_ALEN]; +} __packed; + +struct tt_change { + uint8_t flags; + uint8_t addr[ETH_ALEN]; +} __packed; + #endif /* _NET_BATMAN_ADV_PACKET_H_ */ diff --git a/net/batman-adv/ring_buffer.c b/net/batman-adv/ring_buffer.c index 5bb6a61..f1ccfa7 100644 --- a/net/batman-adv/ring_buffer.c +++ b/net/batman-adv/ring_buffer.c @@ -28,9 +28,9 @@ void ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, uint8_t value) *lq_index = (*lq_index + 1) % TQ_GLOBAL_WINDOW_SIZE; } -uint8_t ring_buffer_avg(uint8_t lq_recv[]) +uint8_t ring_buffer_avg(const uint8_t lq_recv[]) { - uint8_t *ptr; + const uint8_t *ptr; uint16_t count = 0, i = 0, sum = 0; ptr = lq_recv; diff --git a/net/batman-adv/ring_buffer.h b/net/batman-adv/ring_buffer.h index 0395b27..7cdfe62 100644 --- a/net/batman-adv/ring_buffer.h +++ b/net/batman-adv/ring_buffer.h @@ -23,6 +23,6 @@ #define _NET_BATMAN_ADV_RING_BUFFER_H_ void ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, uint8_t value); -uint8_t ring_buffer_avg(uint8_t lq_recv[]); +uint8_t ring_buffer_avg(const uint8_t lq_recv[]); #endif /* _NET_BATMAN_ADV_RING_BUFFER_H_ */ diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index bb1c3ec..da587ad 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -22,18 +22,14 @@ #include "main.h" #include "routing.h" #include "send.h" -#include "hash.h" #include "soft-interface.h" #include "hard-interface.h" #include "icmp_socket.h" #include "translation-table.h" #include "originator.h" -#include "ring_buffer.h" #include "vis.h" -#include "aggregation.h" -#include "gateway_common.h" -#include "gateway_client.h" #include "unicast.h" +#include "bat_ogm.h" void slide_own_bcast_window(struct hard_iface *hard_iface) { @@ -64,28 +60,9 @@ void slide_own_bcast_window(struct hard_iface *hard_iface) } } -static void update_TT(struct bat_priv *bat_priv, struct orig_node *orig_node, - unsigned char *tt_buff, int tt_buff_len) -{ - if ((tt_buff_len != orig_node->tt_buff_len) || - ((tt_buff_len > 0) && - (orig_node->tt_buff_len > 0) && - (memcmp(orig_node->tt_buff, tt_buff, tt_buff_len) != 0))) { - - if (orig_node->tt_buff_len > 0) - tt_global_del_orig(bat_priv, orig_node, - "originator changed tt"); - - if ((tt_buff_len > 0) && (tt_buff)) - tt_global_add_orig(bat_priv, orig_node, - tt_buff, tt_buff_len); - } -} - -static void update_route(struct bat_priv *bat_priv, - struct orig_node *orig_node, - struct neigh_node *neigh_node, - unsigned char *tt_buff, int tt_buff_len) +static void _update_route(struct bat_priv *bat_priv, + struct orig_node *orig_node, + struct neigh_node *neigh_node) { struct neigh_node *curr_router; @@ -93,11 +70,10 @@ static void update_route(struct bat_priv *bat_priv, /* route deleted */ if ((curr_router) && (!neigh_node)) { - bat_dbg(DBG_ROUTES, bat_priv, "Deleting route towards: %pM\n", orig_node->orig); tt_global_del_orig(bat_priv, orig_node, - "originator timed out"); + "Deleted route towards originator"); /* route added */ } else if ((!curr_router) && (neigh_node)) { @@ -105,11 +81,8 @@ static void update_route(struct bat_priv *bat_priv, bat_dbg(DBG_ROUTES, bat_priv, "Adding route towards: %pM (via %pM)\n", orig_node->orig, neigh_node->addr); - tt_global_add_orig(bat_priv, orig_node, - tt_buff, tt_buff_len); - /* route changed */ - } else { + } else if (neigh_node && curr_router) { bat_dbg(DBG_ROUTES, bat_priv, "Changing route towards: %pM " "(now via %pM - was via %pM)\n", @@ -133,10 +106,8 @@ static void update_route(struct bat_priv *bat_priv, neigh_node_free_ref(curr_router); } - -void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, - struct neigh_node *neigh_node, unsigned char *tt_buff, - int tt_buff_len) +void update_route(struct bat_priv *bat_priv, struct orig_node *orig_node, + struct neigh_node *neigh_node) { struct neigh_node *router = NULL; @@ -146,120 +117,13 @@ void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, router = orig_node_get_router(orig_node); if (router != neigh_node) - update_route(bat_priv, orig_node, neigh_node, - tt_buff, tt_buff_len); - /* may be just TT changed */ - else - update_TT(bat_priv, orig_node, tt_buff, tt_buff_len); + _update_route(bat_priv, orig_node, neigh_node); out: if (router) neigh_node_free_ref(router); } -static int is_bidirectional_neigh(struct orig_node *orig_node, - struct orig_node *orig_neigh_node, - struct batman_packet *batman_packet, - struct hard_iface *if_incoming) -{ - struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); - struct neigh_node *neigh_node = NULL, *tmp_neigh_node; - struct hlist_node *node; - unsigned char total_count; - uint8_t orig_eq_count, neigh_rq_count, tq_own; - int tq_asym_penalty, ret = 0; - - /* find corresponding one hop neighbor */ - rcu_read_lock(); - hlist_for_each_entry_rcu(tmp_neigh_node, node, - &orig_neigh_node->neigh_list, list) { - - if (!compare_eth(tmp_neigh_node->addr, orig_neigh_node->orig)) - continue; - - if (tmp_neigh_node->if_incoming != if_incoming) - continue; - - if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) - continue; - - neigh_node = tmp_neigh_node; - break; - } - rcu_read_unlock(); - - if (!neigh_node) - neigh_node = create_neighbor(orig_neigh_node, - orig_neigh_node, - orig_neigh_node->orig, - if_incoming); - - if (!neigh_node) - goto out; - - /* if orig_node is direct neighbour update neigh_node last_valid */ - if (orig_node == orig_neigh_node) - neigh_node->last_valid = jiffies; - - orig_node->last_valid = jiffies; - - /* find packet count of corresponding one hop neighbor */ - spin_lock_bh(&orig_node->ogm_cnt_lock); - orig_eq_count = orig_neigh_node->bcast_own_sum[if_incoming->if_num]; - neigh_rq_count = neigh_node->real_packet_count; - spin_unlock_bh(&orig_node->ogm_cnt_lock); - - /* pay attention to not get a value bigger than 100 % */ - total_count = (orig_eq_count > neigh_rq_count ? - neigh_rq_count : orig_eq_count); - - /* if we have too few packets (too less data) we set tq_own to zero */ - /* if we receive too few packets it is not considered bidirectional */ - if ((total_count < TQ_LOCAL_BIDRECT_SEND_MINIMUM) || - (neigh_rq_count < TQ_LOCAL_BIDRECT_RECV_MINIMUM)) - tq_own = 0; - else - /* neigh_node->real_packet_count is never zero as we - * only purge old information when getting new - * information */ - tq_own = (TQ_MAX_VALUE * total_count) / neigh_rq_count; - - /* - * 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE this does - * affect the nearly-symmetric links only a little, but - * punishes asymmetric links more. This will give a value - * between 0 and TQ_MAX_VALUE - */ - tq_asym_penalty = TQ_MAX_VALUE - (TQ_MAX_VALUE * - (TQ_LOCAL_WINDOW_SIZE - neigh_rq_count) * - (TQ_LOCAL_WINDOW_SIZE - neigh_rq_count) * - (TQ_LOCAL_WINDOW_SIZE - neigh_rq_count)) / - (TQ_LOCAL_WINDOW_SIZE * - TQ_LOCAL_WINDOW_SIZE * - TQ_LOCAL_WINDOW_SIZE); - - batman_packet->tq = ((batman_packet->tq * tq_own * tq_asym_penalty) / - (TQ_MAX_VALUE * TQ_MAX_VALUE)); - - bat_dbg(DBG_BATMAN, bat_priv, - "bidirectional: " - "orig = %-15pM neigh = %-15pM => own_bcast = %2i, " - "real recv = %2i, local tq: %3i, asym_penalty: %3i, " - "total tq: %3i\n", - orig_node->orig, orig_neigh_node->orig, total_count, - neigh_rq_count, tq_own, tq_asym_penalty, batman_packet->tq); - - /* if link has the minimum required transmission quality - * consider it bidirectional */ - if (batman_packet->tq >= TQ_TOTAL_BIDRECT_LIMIT) - ret = 1; - -out: - if (neigh_node) - neigh_node_free_ref(neigh_node); - return ret; -} - /* caller must hold the neigh_list_lock */ void bonding_candidate_del(struct orig_node *orig_node, struct neigh_node *neigh_node) @@ -277,8 +141,8 @@ out: return; } -static void bonding_candidate_add(struct orig_node *orig_node, - struct neigh_node *neigh_node) +void bonding_candidate_add(struct orig_node *orig_node, + struct neigh_node *neigh_node) { struct hlist_node *node; struct neigh_node *tmp_neigh_node, *router = NULL; @@ -348,164 +212,23 @@ out: } /* copy primary address for bonding */ -static void bonding_save_primary(struct orig_node *orig_node, - struct orig_node *orig_neigh_node, - struct batman_packet *batman_packet) +void bonding_save_primary(const struct orig_node *orig_node, + struct orig_node *orig_neigh_node, + const struct batman_ogm_packet *batman_ogm_packet) { - if (!(batman_packet->flags & PRIMARIES_FIRST_HOP)) + if (!(batman_ogm_packet->flags & PRIMARIES_FIRST_HOP)) return; memcpy(orig_neigh_node->primary_addr, orig_node->orig, ETH_ALEN); } -static void update_orig(struct bat_priv *bat_priv, - struct orig_node *orig_node, - struct ethhdr *ethhdr, - struct batman_packet *batman_packet, - struct hard_iface *if_incoming, - unsigned char *tt_buff, int tt_buff_len, - char is_duplicate) -{ - struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; - struct neigh_node *router = NULL; - struct orig_node *orig_node_tmp; - struct hlist_node *node; - int tmp_tt_buff_len; - uint8_t bcast_own_sum_orig, bcast_own_sum_neigh; - - bat_dbg(DBG_BATMAN, bat_priv, "update_originator(): " - "Searching and updating originator entry of received packet\n"); - - rcu_read_lock(); - hlist_for_each_entry_rcu(tmp_neigh_node, node, - &orig_node->neigh_list, list) { - if (compare_eth(tmp_neigh_node->addr, ethhdr->h_source) && - (tmp_neigh_node->if_incoming == if_incoming) && - atomic_inc_not_zero(&tmp_neigh_node->refcount)) { - if (neigh_node) - neigh_node_free_ref(neigh_node); - neigh_node = tmp_neigh_node; - continue; - } - - if (is_duplicate) - continue; - - spin_lock_bh(&tmp_neigh_node->tq_lock); - ring_buffer_set(tmp_neigh_node->tq_recv, - &tmp_neigh_node->tq_index, 0); - tmp_neigh_node->tq_avg = - ring_buffer_avg(tmp_neigh_node->tq_recv); - spin_unlock_bh(&tmp_neigh_node->tq_lock); - } - - if (!neigh_node) { - struct orig_node *orig_tmp; - - orig_tmp = get_orig_node(bat_priv, ethhdr->h_source); - if (!orig_tmp) - goto unlock; - - neigh_node = create_neighbor(orig_node, orig_tmp, - ethhdr->h_source, if_incoming); - - orig_node_free_ref(orig_tmp); - if (!neigh_node) - goto unlock; - } else - bat_dbg(DBG_BATMAN, bat_priv, - "Updating existing last-hop neighbor of originator\n"); - - rcu_read_unlock(); - - orig_node->flags = batman_packet->flags; - neigh_node->last_valid = jiffies; - - spin_lock_bh(&neigh_node->tq_lock); - ring_buffer_set(neigh_node->tq_recv, - &neigh_node->tq_index, - batman_packet->tq); - neigh_node->tq_avg = ring_buffer_avg(neigh_node->tq_recv); - spin_unlock_bh(&neigh_node->tq_lock); - - if (!is_duplicate) { - orig_node->last_ttl = batman_packet->ttl; - neigh_node->last_ttl = batman_packet->ttl; - } - - bonding_candidate_add(orig_node, neigh_node); - - tmp_tt_buff_len = (tt_buff_len > batman_packet->num_tt * ETH_ALEN ? - batman_packet->num_tt * ETH_ALEN : tt_buff_len); - - /* if this neighbor already is our next hop there is nothing - * to change */ - router = orig_node_get_router(orig_node); - if (router == neigh_node) - goto update_tt; - - /* if this neighbor does not offer a better TQ we won't consider it */ - if (router && (router->tq_avg > neigh_node->tq_avg)) - goto update_tt; - - /* if the TQ is the same and the link not more symetric we - * won't consider it either */ - if (router && (neigh_node->tq_avg == router->tq_avg)) { - orig_node_tmp = router->orig_node; - spin_lock_bh(&orig_node_tmp->ogm_cnt_lock); - bcast_own_sum_orig = - orig_node_tmp->bcast_own_sum[if_incoming->if_num]; - spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock); - - orig_node_tmp = neigh_node->orig_node; - spin_lock_bh(&orig_node_tmp->ogm_cnt_lock); - bcast_own_sum_neigh = - orig_node_tmp->bcast_own_sum[if_incoming->if_num]; - spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock); - - if (bcast_own_sum_orig >= bcast_own_sum_neigh) - goto update_tt; - } - - update_routes(bat_priv, orig_node, neigh_node, - tt_buff, tmp_tt_buff_len); - goto update_gw; - -update_tt: - update_routes(bat_priv, orig_node, router, - tt_buff, tmp_tt_buff_len); - -update_gw: - if (orig_node->gw_flags != batman_packet->gw_flags) - gw_node_update(bat_priv, orig_node, batman_packet->gw_flags); - - orig_node->gw_flags = batman_packet->gw_flags; - - /* restart gateway selection if fast or late switching was enabled */ - if ((orig_node->gw_flags) && - (atomic_read(&bat_priv->gw_mode) == GW_MODE_CLIENT) && - (atomic_read(&bat_priv->gw_sel_class) > 2)) - gw_check_election(bat_priv, orig_node); - - goto out; - -unlock: - rcu_read_unlock(); -out: - if (neigh_node) - neigh_node_free_ref(neigh_node); - if (router) - neigh_node_free_ref(router); -} - /* checks whether the host restarted and is in the protection time. * returns: * 0 if the packet is to be accepted * 1 if the packet is to be ignored. */ -static int window_protected(struct bat_priv *bat_priv, - int32_t seq_num_diff, - unsigned long *last_reset) +int window_protected(struct bat_priv *bat_priv, int32_t seq_num_diff, + unsigned long *last_reset) { if ((seq_num_diff <= -TQ_LOCAL_WINDOW_SIZE) || (seq_num_diff >= EXPECTED_SEQNO_RANGE)) { @@ -523,329 +246,12 @@ static int window_protected(struct bat_priv *bat_priv, return 0; } -/* processes a batman packet for all interfaces, adjusts the sequence number and - * finds out whether it is a duplicate. - * returns: - * 1 the packet is a duplicate - * 0 the packet has not yet been received - * -1 the packet is old and has been received while the seqno window - * was protected. Caller should drop it. - */ -static char count_real_packets(struct ethhdr *ethhdr, - struct batman_packet *batman_packet, - struct hard_iface *if_incoming) -{ - struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); - struct orig_node *orig_node; - struct neigh_node *tmp_neigh_node; - struct hlist_node *node; - char is_duplicate = 0; - int32_t seq_diff; - int need_update = 0; - int set_mark, ret = -1; - - orig_node = get_orig_node(bat_priv, batman_packet->orig); - if (!orig_node) - return 0; - - spin_lock_bh(&orig_node->ogm_cnt_lock); - seq_diff = batman_packet->seqno - orig_node->last_real_seqno; - - /* signalize caller that the packet is to be dropped. */ - if (window_protected(bat_priv, seq_diff, - &orig_node->batman_seqno_reset)) - goto out; - - rcu_read_lock(); - hlist_for_each_entry_rcu(tmp_neigh_node, node, - &orig_node->neigh_list, list) { - - is_duplicate |= get_bit_status(tmp_neigh_node->real_bits, - orig_node->last_real_seqno, - batman_packet->seqno); - - if (compare_eth(tmp_neigh_node->addr, ethhdr->h_source) && - (tmp_neigh_node->if_incoming == if_incoming)) - set_mark = 1; - else - set_mark = 0; - - /* if the window moved, set the update flag. */ - need_update |= bit_get_packet(bat_priv, - tmp_neigh_node->real_bits, - seq_diff, set_mark); - - tmp_neigh_node->real_packet_count = - bit_packet_count(tmp_neigh_node->real_bits); - } - rcu_read_unlock(); - - if (need_update) { - bat_dbg(DBG_BATMAN, bat_priv, - "updating last_seqno: old %d, new %d\n", - orig_node->last_real_seqno, batman_packet->seqno); - orig_node->last_real_seqno = batman_packet->seqno; - } - - ret = is_duplicate; - -out: - spin_unlock_bh(&orig_node->ogm_cnt_lock); - orig_node_free_ref(orig_node); - return ret; -} - -void receive_bat_packet(struct ethhdr *ethhdr, - struct batman_packet *batman_packet, - unsigned char *tt_buff, int tt_buff_len, - struct hard_iface *if_incoming) -{ - struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); - struct hard_iface *hard_iface; - struct orig_node *orig_neigh_node, *orig_node; - struct neigh_node *router = NULL, *router_router = NULL; - struct neigh_node *orig_neigh_router = NULL; - char has_directlink_flag; - char is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0; - char is_broadcast = 0, is_bidirectional, is_single_hop_neigh; - char is_duplicate; - uint32_t if_incoming_seqno; - - /* Silently drop when the batman packet is actually not a - * correct packet. - * - * This might happen if a packet is padded (e.g. Ethernet has a - * minimum frame length of 64 byte) and the aggregation interprets - * it as an additional length. - * - * TODO: A more sane solution would be to have a bit in the - * batman_packet to detect whether the packet is the last - * packet in an aggregation. Here we expect that the padding - * is always zero (or not 0x01) - */ - if (batman_packet->packet_type != BAT_PACKET) - return; - - /* could be changed by schedule_own_packet() */ - if_incoming_seqno = atomic_read(&if_incoming->seqno); - - has_directlink_flag = (batman_packet->flags & DIRECTLINK ? 1 : 0); - - is_single_hop_neigh = (compare_eth(ethhdr->h_source, - batman_packet->orig) ? 1 : 0); - - bat_dbg(DBG_BATMAN, bat_priv, - "Received BATMAN packet via NB: %pM, IF: %s [%pM] " - "(from OG: %pM, via prev OG: %pM, seqno %d, tq %d, " - "TTL %d, V %d, IDF %d)\n", - ethhdr->h_source, if_incoming->net_dev->name, - if_incoming->net_dev->dev_addr, batman_packet->orig, - batman_packet->prev_sender, batman_packet->seqno, - batman_packet->tq, batman_packet->ttl, batman_packet->version, - has_directlink_flag); - - rcu_read_lock(); - list_for_each_entry_rcu(hard_iface, &hardif_list, list) { - if (hard_iface->if_status != IF_ACTIVE) - continue; - - if (hard_iface->soft_iface != if_incoming->soft_iface) - continue; - - if (compare_eth(ethhdr->h_source, - hard_iface->net_dev->dev_addr)) - is_my_addr = 1; - - if (compare_eth(batman_packet->orig, - hard_iface->net_dev->dev_addr)) - is_my_orig = 1; - - if (compare_eth(batman_packet->prev_sender, - hard_iface->net_dev->dev_addr)) - is_my_oldorig = 1; - - if (compare_eth(ethhdr->h_source, broadcast_addr)) - is_broadcast = 1; - } - rcu_read_unlock(); - - if (batman_packet->version != COMPAT_VERSION) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: incompatible batman version (%i)\n", - batman_packet->version); - return; - } - - if (is_my_addr) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: received my own broadcast (sender: %pM" - ")\n", - ethhdr->h_source); - return; - } - - if (is_broadcast) { - bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: " - "ignoring all packets with broadcast source addr (sender: %pM" - ")\n", ethhdr->h_source); - return; - } - - if (is_my_orig) { - unsigned long *word; - int offset; - - orig_neigh_node = get_orig_node(bat_priv, ethhdr->h_source); - if (!orig_neigh_node) - return; - - /* neighbor has to indicate direct link and it has to - * come via the corresponding interface */ - /* if received seqno equals last send seqno save new - * seqno for bidirectional check */ - if (has_directlink_flag && - compare_eth(if_incoming->net_dev->dev_addr, - batman_packet->orig) && - (batman_packet->seqno - if_incoming_seqno + 2 == 0)) { - offset = if_incoming->if_num * NUM_WORDS; - - spin_lock_bh(&orig_neigh_node->ogm_cnt_lock); - word = &(orig_neigh_node->bcast_own[offset]); - bit_mark(word, 0); - orig_neigh_node->bcast_own_sum[if_incoming->if_num] = - bit_packet_count(word); - spin_unlock_bh(&orig_neigh_node->ogm_cnt_lock); - } - - bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: " - "originator packet from myself (via neighbor)\n"); - orig_node_free_ref(orig_neigh_node); - return; - } - - if (is_my_oldorig) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: ignoring all rebroadcast echos (sender: " - "%pM)\n", ethhdr->h_source); - return; - } - - orig_node = get_orig_node(bat_priv, batman_packet->orig); - if (!orig_node) - return; - - is_duplicate = count_real_packets(ethhdr, batman_packet, if_incoming); - - if (is_duplicate == -1) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: packet within seqno protection time " - "(sender: %pM)\n", ethhdr->h_source); - goto out; - } - - if (batman_packet->tq == 0) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: originator packet with tq equal 0\n"); - goto out; - } - - router = orig_node_get_router(orig_node); - if (router) - router_router = orig_node_get_router(router->orig_node); - - /* avoid temporary routing loops */ - if (router && router_router && - (compare_eth(router->addr, batman_packet->prev_sender)) && - !(compare_eth(batman_packet->orig, batman_packet->prev_sender)) && - (compare_eth(router->addr, router_router->addr))) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: ignoring all rebroadcast packets that " - "may make me loop (sender: %pM)\n", ethhdr->h_source); - goto out; - } - - /* if sender is a direct neighbor the sender mac equals - * originator mac */ - orig_neigh_node = (is_single_hop_neigh ? - orig_node : - get_orig_node(bat_priv, ethhdr->h_source)); - if (!orig_neigh_node) - goto out; - - orig_neigh_router = orig_node_get_router(orig_neigh_node); - - /* drop packet if sender is not a direct neighbor and if we - * don't route towards it */ - if (!is_single_hop_neigh && (!orig_neigh_router)) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: OGM via unknown neighbor!\n"); - goto out_neigh; - } - - is_bidirectional = is_bidirectional_neigh(orig_node, orig_neigh_node, - batman_packet, if_incoming); - - bonding_save_primary(orig_node, orig_neigh_node, batman_packet); - - /* update ranking if it is not a duplicate or has the same - * seqno and similar ttl as the non-duplicate */ - if (is_bidirectional && - (!is_duplicate || - ((orig_node->last_real_seqno == batman_packet->seqno) && - (orig_node->last_ttl - 3 <= batman_packet->ttl)))) - update_orig(bat_priv, orig_node, ethhdr, batman_packet, - if_incoming, tt_buff, tt_buff_len, is_duplicate); - - /* is single hop (direct) neighbor */ - if (is_single_hop_neigh) { - - /* mark direct link on incoming interface */ - schedule_forward_packet(orig_node, ethhdr, batman_packet, - 1, tt_buff_len, if_incoming); - - bat_dbg(DBG_BATMAN, bat_priv, "Forwarding packet: " - "rebroadcast neighbor packet with direct link flag\n"); - goto out_neigh; - } - - /* multihop originator */ - if (!is_bidirectional) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: not received via bidirectional link\n"); - goto out_neigh; - } - - if (is_duplicate) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: duplicate packet received\n"); - goto out_neigh; - } - - bat_dbg(DBG_BATMAN, bat_priv, - "Forwarding packet: rebroadcast originator packet\n"); - schedule_forward_packet(orig_node, ethhdr, batman_packet, - 0, tt_buff_len, if_incoming); - -out_neigh: - if ((orig_neigh_node) && (!is_single_hop_neigh)) - orig_node_free_ref(orig_neigh_node); -out: - if (router) - neigh_node_free_ref(router); - if (router_router) - neigh_node_free_ref(router_router); - if (orig_neigh_router) - neigh_node_free_ref(orig_neigh_router); - - orig_node_free_ref(orig_node); -} - -int recv_bat_packet(struct sk_buff *skb, struct hard_iface *hard_iface) +int recv_bat_ogm_packet(struct sk_buff *skb, struct hard_iface *hard_iface) { struct ethhdr *ethhdr; /* drop packet if it has not necessary minimum size */ - if (unlikely(!pskb_may_pull(skb, sizeof(struct batman_packet)))) + if (unlikely(!pskb_may_pull(skb, BATMAN_OGM_LEN))) return NET_RX_DROP; ethhdr = (struct ethhdr *)skb_mac_header(skb); @@ -868,10 +274,7 @@ int recv_bat_packet(struct sk_buff *skb, struct hard_iface *hard_iface) ethhdr = (struct ethhdr *)skb_mac_header(skb); - receive_aggr_bat_packet(ethhdr, - skb->data, - skb_headlen(skb), - hard_iface); + bat_ogm_receive(ethhdr, skb->data, skb_headlen(skb), hard_iface); kfree_skb(skb); return NET_RX_SUCCESS; @@ -1077,7 +480,7 @@ out: * This method rotates the bonding list and increases the * returned router's refcount. */ static struct neigh_node *find_bond_router(struct orig_node *primary_orig, - struct hard_iface *recv_if) + const struct hard_iface *recv_if) { struct neigh_node *tmp_neigh_node; struct neigh_node *router = NULL, *first_candidate = NULL; @@ -1128,7 +531,7 @@ out: * * Increases the returned router's refcount */ static struct neigh_node *find_ifalter_router(struct orig_node *primary_orig, - struct hard_iface *recv_if) + const struct hard_iface *recv_if) { struct neigh_node *tmp_neigh_node; struct neigh_node *router = NULL, *first_candidate = NULL; @@ -1171,12 +574,126 @@ static struct neigh_node *find_ifalter_router(struct orig_node *primary_orig, return router; } +int recv_tt_query(struct sk_buff *skb, struct hard_iface *recv_if) +{ + struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct tt_query_packet *tt_query; + struct ethhdr *ethhdr; + + /* drop packet if it has not necessary minimum size */ + if (unlikely(!pskb_may_pull(skb, sizeof(struct tt_query_packet)))) + goto out; + + /* I could need to modify it */ + if (skb_cow(skb, sizeof(struct tt_query_packet)) < 0) + 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)) + goto out; + + tt_query = (struct tt_query_packet *)skb->data; + + tt_query->tt_data = ntohs(tt_query->tt_data); + + switch (tt_query->flags & TT_QUERY_TYPE_MASK) { + case TT_REQUEST: + /* If we cannot provide an answer the tt_request is + * forwarded */ + if (!send_tt_response(bat_priv, tt_query)) { + bat_dbg(DBG_TT, bat_priv, + "Routing TT_REQUEST to %pM [%c]\n", + tt_query->dst, + (tt_query->flags & TT_FULL_TABLE ? 'F' : '.')); + tt_query->tt_data = htons(tt_query->tt_data); + return route_unicast_packet(skb, recv_if); + } + break; + case TT_RESPONSE: + /* packet needs to be linearized to access the TT changes */ + if (skb_linearize(skb) < 0) + goto out; + /* skb_linearize() possibly changed skb->data */ + tt_query = (struct tt_query_packet *)skb->data; + + if (is_my_mac(tt_query->dst)) + handle_tt_response(bat_priv, tt_query); + else { + bat_dbg(DBG_TT, bat_priv, + "Routing TT_RESPONSE to %pM [%c]\n", + tt_query->dst, + (tt_query->flags & TT_FULL_TABLE ? 'F' : '.')); + tt_query->tt_data = htons(tt_query->tt_data); + return route_unicast_packet(skb, recv_if); + } + break; + } + +out: + /* returning NET_RX_DROP will make the caller function kfree the skb */ + return NET_RX_DROP; +} + +int recv_roam_adv(struct sk_buff *skb, struct hard_iface *recv_if) +{ + struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct roam_adv_packet *roam_adv_packet; + struct orig_node *orig_node; + struct ethhdr *ethhdr; + + /* drop packet if it has not necessary minimum size */ + if (unlikely(!pskb_may_pull(skb, sizeof(struct 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)) + goto out; + + roam_adv_packet = (struct roam_adv_packet *)skb->data; + + if (!is_my_mac(roam_adv_packet->dst)) + return route_unicast_packet(skb, recv_if); + + orig_node = orig_hash_find(bat_priv, roam_adv_packet->src); + if (!orig_node) + goto out; + + bat_dbg(DBG_TT, bat_priv, "Received ROAMING_ADV from %pM " + "(client %pM)\n", roam_adv_packet->src, + roam_adv_packet->client); + + tt_global_add(bat_priv, orig_node, roam_adv_packet->client, + atomic_read(&orig_node->last_ttvn) + 1, true, false); + + /* 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; + + orig_node_free_ref(orig_node); +out: + /* returning NET_RX_DROP will make the caller function kfree the skb */ + return NET_RX_DROP; +} + /* find a suitable router for this originator, and use * bonding if possible. increases the found neighbors * refcount.*/ struct neigh_node *find_router(struct bat_priv *bat_priv, struct orig_node *orig_node, - struct hard_iface *recv_if) + const struct hard_iface *recv_if) { struct orig_node *primary_orig_node; struct orig_node *router_orig; @@ -1240,6 +757,9 @@ struct neigh_node *find_router(struct bat_priv *bat_priv, router = find_ifalter_router(primary_orig_node, recv_if); return_router: + if (router && router->if_incoming->if_status != IF_ACTIVE) + goto err_unlock; + rcu_read_unlock(); return router; err_unlock: @@ -1354,14 +874,84 @@ out: return ret; } +static int check_unicast_ttvn(struct bat_priv *bat_priv, + struct sk_buff *skb) { + uint8_t curr_ttvn; + struct orig_node *orig_node; + struct ethhdr *ethhdr; + struct hard_iface *primary_if; + struct unicast_packet *unicast_packet; + bool tt_poss_change; + + /* I could need to modify it */ + if (skb_cow(skb, sizeof(struct unicast_packet)) < 0) + return 0; + + unicast_packet = (struct unicast_packet *)skb->data; + + if (is_my_mac(unicast_packet->dest)) { + tt_poss_change = bat_priv->tt_poss_change; + curr_ttvn = (uint8_t)atomic_read(&bat_priv->ttvn); + } else { + orig_node = orig_hash_find(bat_priv, unicast_packet->dest); + + if (!orig_node) + return 0; + + curr_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); + tt_poss_change = orig_node->tt_poss_change; + orig_node_free_ref(orig_node); + } + + /* Check whether I have to reroute the packet */ + if (seq_before(unicast_packet->ttvn, curr_ttvn) || tt_poss_change) { + /* Linearize the skb before accessing it */ + if (skb_linearize(skb) < 0) + return 0; + + ethhdr = (struct ethhdr *)(skb->data + + sizeof(struct unicast_packet)); + orig_node = transtable_search(bat_priv, NULL, ethhdr->h_dest); + + if (!orig_node) { + if (!is_my_client(bat_priv, ethhdr->h_dest)) + return 0; + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + return 0; + memcpy(unicast_packet->dest, + primary_if->net_dev->dev_addr, ETH_ALEN); + 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); + orig_node_free_ref(orig_node); + } + + bat_dbg(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); + + unicast_packet->ttvn = curr_ttvn; + } + return 1; +} + int recv_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if) { + struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct unicast_packet *unicast_packet; - int hdr_size = sizeof(struct unicast_packet); + int hdr_size = sizeof(*unicast_packet); if (check_unicast_packet(skb, hdr_size) < 0) return NET_RX_DROP; + if (!check_unicast_ttvn(bat_priv, skb)) + return NET_RX_DROP; + unicast_packet = (struct unicast_packet *)skb->data; /* packet for me */ @@ -1377,13 +967,16 @@ int recv_ucast_frag_packet(struct sk_buff *skb, struct hard_iface *recv_if) { struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct unicast_frag_packet *unicast_packet; - int hdr_size = sizeof(struct unicast_frag_packet); + int hdr_size = sizeof(*unicast_packet); struct sk_buff *new_skb = NULL; int ret; if (check_unicast_packet(skb, hdr_size) < 0) return NET_RX_DROP; + if (!check_unicast_ttvn(bat_priv, skb)) + return NET_RX_DROP; + unicast_packet = (struct unicast_frag_packet *)skb->data; /* packet for me */ @@ -1413,7 +1006,7 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if) struct orig_node *orig_node = NULL; struct bcast_packet *bcast_packet; struct ethhdr *ethhdr; - int hdr_size = sizeof(struct bcast_packet); + int hdr_size = sizeof(*bcast_packet); int ret = NET_RX_DROP; int32_t seq_diff; @@ -1471,7 +1064,7 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if) spin_unlock_bh(&orig_node->bcast_seqno_lock); /* rebroadcast packet */ - add_bcast_packet_to_list(bat_priv, skb); + add_bcast_packet_to_list(bat_priv, skb, 1); /* broadcast for me */ interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size); @@ -1491,7 +1084,7 @@ int recv_vis_packet(struct sk_buff *skb, struct hard_iface *recv_if) struct vis_packet *vis_packet; struct ethhdr *ethhdr; struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); - int hdr_size = sizeof(struct vis_packet); + int hdr_size = sizeof(*vis_packet); /* keep skb linear */ if (skb_linearize(skb) < 0) diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h index 870f298..7aaee0f 100644 --- a/net/batman-adv/routing.h +++ b/net/batman-adv/routing.h @@ -23,24 +23,28 @@ #define _NET_BATMAN_ADV_ROUTING_H_ void slide_own_bcast_window(struct hard_iface *hard_iface); -void receive_bat_packet(struct ethhdr *ethhdr, - struct batman_packet *batman_packet, - unsigned char *tt_buff, int tt_buff_len, - struct hard_iface *if_incoming); -void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, - struct neigh_node *neigh_node, unsigned char *tt_buff, - int tt_buff_len); +void update_route(struct bat_priv *bat_priv, struct orig_node *orig_node, + struct neigh_node *neigh_node); int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if); int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if); int recv_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if); int recv_ucast_frag_packet(struct sk_buff *skb, struct hard_iface *recv_if); int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if); int recv_vis_packet(struct sk_buff *skb, struct hard_iface *recv_if); -int recv_bat_packet(struct sk_buff *skb, struct hard_iface *recv_if); +int recv_bat_ogm_packet(struct sk_buff *skb, struct hard_iface *recv_if); +int recv_tt_query(struct sk_buff *skb, struct hard_iface *recv_if); +int recv_roam_adv(struct sk_buff *skb, struct hard_iface *recv_if); struct neigh_node *find_router(struct bat_priv *bat_priv, struct orig_node *orig_node, - struct hard_iface *recv_if); + const struct hard_iface *recv_if); void bonding_candidate_del(struct orig_node *orig_node, struct neigh_node *neigh_node); +void bonding_candidate_add(struct orig_node *orig_node, + struct neigh_node *neigh_node); +void bonding_save_primary(const struct orig_node *orig_node, + struct orig_node *orig_neigh_node, + const struct batman_ogm_packet *batman_ogm_packet); +int window_protected(struct bat_priv *bat_priv, int32_t seq_num_diff, + unsigned long *last_reset); #endif /* _NET_BATMAN_ADV_ROUTING_H_ */ diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 3377927..8a684eb 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -26,38 +26,16 @@ #include "soft-interface.h" #include "hard-interface.h" #include "vis.h" -#include "aggregation.h" #include "gateway_common.h" #include "originator.h" +#include "bat_ogm.h" static void send_outstanding_bcast_packet(struct work_struct *work); -/* apply hop penalty for a normal link */ -static uint8_t hop_penalty(const uint8_t tq, struct bat_priv *bat_priv) -{ - int hop_penalty = atomic_read(&bat_priv->hop_penalty); - return (tq * (TQ_MAX_VALUE - hop_penalty)) / (TQ_MAX_VALUE); -} - -/* when do we schedule our own packet to be sent */ -static unsigned long own_send_time(struct bat_priv *bat_priv) -{ - return jiffies + msecs_to_jiffies( - atomic_read(&bat_priv->orig_interval) - - JITTER + (random32() % 2*JITTER)); -} - -/* when do we schedule a forwarded packet to be sent */ -static unsigned long forward_send_time(void) -{ - return jiffies + msecs_to_jiffies(random32() % (JITTER/2)); -} - /* send out an already prepared packet to the given address via the * specified batman interface */ -int send_skb_packet(struct sk_buff *skb, - struct hard_iface *hard_iface, - uint8_t *dst_addr) +int send_skb_packet(struct sk_buff *skb, struct hard_iface *hard_iface, + const uint8_t *dst_addr) { struct ethhdr *ethhdr; @@ -74,7 +52,7 @@ int send_skb_packet(struct sk_buff *skb, } /* push to the ethernet header. */ - if (my_skb_head_push(skb, sizeof(struct ethhdr)) < 0) + if (my_skb_head_push(skb, sizeof(*ethhdr)) < 0) goto send_skb_err; skb_reset_mac_header(skb); @@ -100,162 +78,67 @@ send_skb_err: return NET_XMIT_DROP; } -/* Send a packet to a given interface */ -static void send_packet_to_if(struct forw_packet *forw_packet, - struct hard_iface *hard_iface) +static void realloc_packet_buffer(struct hard_iface *hard_iface, + int new_len) { - struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface); - char *fwd_str; - uint8_t packet_num; - int16_t buff_pos; - struct batman_packet *batman_packet; - struct sk_buff *skb; + unsigned char *new_buff; - if (hard_iface->if_status != IF_ACTIVE) - return; + new_buff = kmalloc(new_len, GFP_ATOMIC); - packet_num = 0; - buff_pos = 0; - batman_packet = (struct batman_packet *)forw_packet->skb->data; - - /* adjust all flags and log packets */ - while (aggregated_packet(buff_pos, - forw_packet->packet_len, - batman_packet->num_tt)) { - - /* we might have aggregated direct link packets with an - * ordinary base packet */ - if ((forw_packet->direct_link_flags & (1 << packet_num)) && - (forw_packet->if_incoming == hard_iface)) - batman_packet->flags |= DIRECTLINK; - else - batman_packet->flags &= ~DIRECTLINK; - - fwd_str = (packet_num > 0 ? "Forwarding" : (forw_packet->own ? - "Sending own" : - "Forwarding")); - bat_dbg(DBG_BATMAN, bat_priv, - "%s %spacket (originator %pM, seqno %d, TQ %d, TTL %d," - " IDF %s) on interface %s [%pM]\n", - fwd_str, (packet_num > 0 ? "aggregated " : ""), - batman_packet->orig, ntohl(batman_packet->seqno), - batman_packet->tq, batman_packet->ttl, - (batman_packet->flags & DIRECTLINK ? - "on" : "off"), - hard_iface->net_dev->name, - hard_iface->net_dev->dev_addr); - - buff_pos += sizeof(struct batman_packet) + - (batman_packet->num_tt * ETH_ALEN); - packet_num++; - batman_packet = (struct batman_packet *) - (forw_packet->skb->data + buff_pos); - } + /* keep old buffer if kmalloc should fail */ + if (new_buff) { + memcpy(new_buff, hard_iface->packet_buff, + BATMAN_OGM_LEN); - /* create clone because function is called more than once */ - skb = skb_clone(forw_packet->skb, GFP_ATOMIC); - if (skb) - send_skb_packet(skb, hard_iface, broadcast_addr); + kfree(hard_iface->packet_buff); + hard_iface->packet_buff = new_buff; + hard_iface->packet_len = new_len; + } } -/* send a batman packet */ -static void send_packet(struct forw_packet *forw_packet) +/* when calling this function (hard_iface == primary_if) has to be true */ +static int prepare_packet_buffer(struct bat_priv *bat_priv, + struct hard_iface *hard_iface) { - struct hard_iface *hard_iface; - struct net_device *soft_iface; - struct bat_priv *bat_priv; - struct batman_packet *batman_packet = - (struct batman_packet *)(forw_packet->skb->data); - unsigned char directlink = (batman_packet->flags & DIRECTLINK ? 1 : 0); - - if (!forw_packet->if_incoming) { - pr_err("Error - can't forward packet: incoming iface not " - "specified\n"); - return; - } + int new_len; - soft_iface = forw_packet->if_incoming->soft_iface; - bat_priv = netdev_priv(soft_iface); + new_len = BATMAN_OGM_LEN + + tt_len((uint8_t)atomic_read(&bat_priv->tt_local_changes)); - if (forw_packet->if_incoming->if_status != IF_ACTIVE) - return; + /* if we have too many changes for one packet don't send any + * and wait for the tt table request which will be fragmented */ + if (new_len > hard_iface->soft_iface->mtu) + new_len = BATMAN_OGM_LEN; - /* multihomed peer assumed */ - /* non-primary OGMs are only broadcasted on their interface */ - if ((directlink && (batman_packet->ttl == 1)) || - (forw_packet->own && (forw_packet->if_incoming->if_num > 0))) { + realloc_packet_buffer(hard_iface, new_len); - /* FIXME: what about aggregated packets ? */ - bat_dbg(DBG_BATMAN, bat_priv, - "%s packet (originator %pM, seqno %d, TTL %d) " - "on interface %s [%pM]\n", - (forw_packet->own ? "Sending own" : "Forwarding"), - batman_packet->orig, ntohl(batman_packet->seqno), - batman_packet->ttl, - forw_packet->if_incoming->net_dev->name, - forw_packet->if_incoming->net_dev->dev_addr); - - /* skb is only used once and than forw_packet is free'd */ - send_skb_packet(forw_packet->skb, forw_packet->if_incoming, - broadcast_addr); - forw_packet->skb = NULL; + atomic_set(&bat_priv->tt_crc, tt_local_crc(bat_priv)); - return; - } + /* reset the sending counter */ + atomic_set(&bat_priv->tt_ogm_append_cnt, TT_OGM_APPEND_MAX); - /* broadcast on every interface */ - rcu_read_lock(); - list_for_each_entry_rcu(hard_iface, &hardif_list, list) { - if (hard_iface->soft_iface != soft_iface) - continue; - - send_packet_to_if(forw_packet, hard_iface); - } - rcu_read_unlock(); + return tt_changes_fill_buffer(bat_priv, + hard_iface->packet_buff + BATMAN_OGM_LEN, + hard_iface->packet_len - BATMAN_OGM_LEN); } -static void rebuild_batman_packet(struct bat_priv *bat_priv, - struct hard_iface *hard_iface) +static int reset_packet_buffer(struct bat_priv *bat_priv, + struct hard_iface *hard_iface) { - int new_len; - unsigned char *new_buff; - struct batman_packet *batman_packet; - - new_len = sizeof(struct batman_packet) + - (bat_priv->num_local_tt * ETH_ALEN); - new_buff = kmalloc(new_len, GFP_ATOMIC); - - /* keep old buffer if kmalloc should fail */ - if (new_buff) { - memcpy(new_buff, hard_iface->packet_buff, - sizeof(struct batman_packet)); - batman_packet = (struct batman_packet *)new_buff; - - batman_packet->num_tt = tt_local_fill_buffer(bat_priv, - new_buff + sizeof(struct batman_packet), - new_len - sizeof(struct batman_packet)); - - kfree(hard_iface->packet_buff); - hard_iface->packet_buff = new_buff; - hard_iface->packet_len = new_len; - } + realloc_packet_buffer(hard_iface, BATMAN_OGM_LEN); + return 0; } -void schedule_own_packet(struct hard_iface *hard_iface) +void schedule_bat_ogm(struct hard_iface *hard_iface) { struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface); struct hard_iface *primary_if; - unsigned long send_time; - struct batman_packet *batman_packet; - int vis_server; + int tt_num_changes = -1; if ((hard_iface->if_status == IF_NOT_IN_USE) || (hard_iface->if_status == IF_TO_BE_REMOVED)) return; - vis_server = atomic_read(&bat_priv->vis_mode); - primary_if = primary_if_get_selected(bat_priv); - /** * the interface gets activated here to avoid race conditions between * the moment of activating the interface in @@ -266,111 +149,26 @@ void schedule_own_packet(struct hard_iface *hard_iface) if (hard_iface->if_status == IF_TO_BE_ACTIVATED) hard_iface->if_status = IF_ACTIVE; - /* if local tt has changed and interface is a primary interface */ - if ((atomic_read(&bat_priv->tt_local_changed)) && - (hard_iface == primary_if)) - rebuild_batman_packet(bat_priv, hard_iface); - - /** - * NOTE: packet_buff might just have been re-allocated in - * rebuild_batman_packet() - */ - batman_packet = (struct batman_packet *)hard_iface->packet_buff; - - /* change sequence number to network order */ - batman_packet->seqno = - htonl((uint32_t)atomic_read(&hard_iface->seqno)); - - if (vis_server == VIS_TYPE_SERVER_SYNC) - batman_packet->flags |= VIS_SERVER; - else - batman_packet->flags &= ~VIS_SERVER; - - if ((hard_iface == primary_if) && - (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER)) - batman_packet->gw_flags = - (uint8_t)atomic_read(&bat_priv->gw_bandwidth); - else - batman_packet->gw_flags = 0; - - atomic_inc(&hard_iface->seqno); - - slide_own_bcast_window(hard_iface); - send_time = own_send_time(bat_priv); - add_bat_packet_to_list(bat_priv, - hard_iface->packet_buff, - hard_iface->packet_len, - hard_iface, 1, send_time); - - if (primary_if) - hardif_free_ref(primary_if); -} - -void schedule_forward_packet(struct orig_node *orig_node, - struct ethhdr *ethhdr, - struct batman_packet *batman_packet, - uint8_t directlink, int tt_buff_len, - struct hard_iface *if_incoming) -{ - struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); - struct neigh_node *router; - unsigned char in_tq, in_ttl, tq_avg = 0; - unsigned long send_time; - - if (batman_packet->ttl <= 1) { - bat_dbg(DBG_BATMAN, bat_priv, "ttl exceeded\n"); - return; - } - - router = orig_node_get_router(orig_node); - - in_tq = batman_packet->tq; - in_ttl = batman_packet->ttl; - - batman_packet->ttl--; - memcpy(batman_packet->prev_sender, ethhdr->h_source, ETH_ALEN); - - /* rebroadcast tq of our best ranking neighbor to ensure the rebroadcast - * of our best tq value */ - if (router && router->tq_avg != 0) { - - /* rebroadcast ogm of best ranking neighbor as is */ - if (!compare_eth(router->addr, ethhdr->h_source)) { - batman_packet->tq = router->tq_avg; + primary_if = primary_if_get_selected(bat_priv); - if (router->last_ttl) - batman_packet->ttl = router->last_ttl - 1; + if (hard_iface == primary_if) { + /* if at least one change happened */ + if (atomic_read(&bat_priv->tt_local_changes) > 0) { + tt_commit_changes(bat_priv); + tt_num_changes = prepare_packet_buffer(bat_priv, + hard_iface); } - tq_avg = router->tq_avg; + /* if the changes have been sent often enough */ + if (!atomic_dec_not_zero(&bat_priv->tt_ogm_append_cnt)) + tt_num_changes = reset_packet_buffer(bat_priv, + hard_iface); } - if (router) - neigh_node_free_ref(router); - - /* apply hop penalty */ - batman_packet->tq = hop_penalty(batman_packet->tq, bat_priv); - - bat_dbg(DBG_BATMAN, bat_priv, - "Forwarding packet: tq_orig: %i, tq_avg: %i, " - "tq_forw: %i, ttl_orig: %i, ttl_forw: %i\n", - in_tq, tq_avg, batman_packet->tq, in_ttl - 1, - batman_packet->ttl); - - batman_packet->seqno = htonl(batman_packet->seqno); - - /* switch of primaries first hop flag when forwarding */ - batman_packet->flags &= ~PRIMARIES_FIRST_HOP; - if (directlink) - batman_packet->flags |= DIRECTLINK; - else - batman_packet->flags &= ~DIRECTLINK; + if (primary_if) + hardif_free_ref(primary_if); - send_time = forward_send_time(); - add_bat_packet_to_list(bat_priv, - (unsigned char *)batman_packet, - sizeof(struct batman_packet) + tt_buff_len, - if_incoming, 0, send_time); + bat_ogm_schedule(hard_iface, tt_num_changes); } static void forw_packet_free(struct forw_packet *forw_packet) @@ -401,18 +199,20 @@ static void _add_bcast_packet_to_list(struct bat_priv *bat_priv, } /* add a broadcast packet to the queue and setup timers. broadcast packets - * are sent multiple times to increase probability for beeing received. + * are sent multiple times to increase probability for being received. * * This function returns NETDEV_TX_OK on success and NETDEV_TX_BUSY on * errors. * * The skb is not consumed, so the caller should make sure that the * skb is freed. */ -int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb) +int add_bcast_packet_to_list(struct bat_priv *bat_priv, + const struct sk_buff *skb, unsigned long delay) { struct hard_iface *primary_if = NULL; struct forw_packet *forw_packet; struct bcast_packet *bcast_packet; + struct sk_buff *newskb; if (!atomic_dec_not_zero(&bat_priv->bcast_queue_left)) { bat_dbg(DBG_BATMAN, bat_priv, "bcast packet queue full\n"); @@ -423,28 +223,28 @@ int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb) if (!primary_if) goto out_and_inc; - forw_packet = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC); + forw_packet = kmalloc(sizeof(*forw_packet), GFP_ATOMIC); if (!forw_packet) goto out_and_inc; - skb = skb_copy(skb, GFP_ATOMIC); - if (!skb) + newskb = skb_copy(skb, GFP_ATOMIC); + if (!newskb) goto packet_free; /* as we have a copy now, it is safe to decrease the TTL */ - bcast_packet = (struct bcast_packet *)skb->data; + bcast_packet = (struct bcast_packet *)newskb->data; bcast_packet->ttl--; - skb_reset_mac_header(skb); + skb_reset_mac_header(newskb); - forw_packet->skb = skb; + forw_packet->skb = newskb; forw_packet->if_incoming = primary_if; /* how often did we send the bcast packet ? */ forw_packet->num_packets = 0; - _add_bcast_packet_to_list(bat_priv, forw_packet, 1); + _add_bcast_packet_to_list(bat_priv, forw_packet, delay); return NETDEV_TX_OK; packet_free: @@ -502,7 +302,7 @@ out: atomic_inc(&bat_priv->bcast_queue_left); } -void send_outstanding_bat_packet(struct work_struct *work) +void send_outstanding_bat_ogm_packet(struct work_struct *work) { struct delayed_work *delayed_work = container_of(work, struct delayed_work, work); @@ -518,7 +318,7 @@ void send_outstanding_bat_packet(struct work_struct *work) if (atomic_read(&bat_priv->mesh_state) == MESH_DEACTIVATING) goto out; - send_packet(forw_packet); + bat_ogm_emit(forw_packet); /** * we have to have at least one packet in the queue @@ -526,7 +326,7 @@ void send_outstanding_bat_packet(struct work_struct *work) * shutting down */ if (forw_packet->own) - schedule_own_packet(forw_packet->if_incoming); + schedule_bat_ogm(forw_packet->if_incoming); out: /* don't count own packet */ @@ -537,7 +337,7 @@ out: } void purge_outstanding_packets(struct bat_priv *bat_priv, - struct hard_iface *hard_iface) + const struct hard_iface *hard_iface) { struct forw_packet *forw_packet; struct hlist_node *tmp_node, *safe_tmp_node; @@ -557,7 +357,7 @@ void purge_outstanding_packets(struct bat_priv *bat_priv, &bat_priv->forw_bcast_list, list) { /** - * if purge_outstanding_packets() was called with an argmument + * if purge_outstanding_packets() was called with an argument * we delete only packets belonging to the given interface */ if ((hard_iface) && @@ -586,7 +386,7 @@ void purge_outstanding_packets(struct bat_priv *bat_priv, &bat_priv->forw_bat_list, list) { /** - * if purge_outstanding_packets() was called with an argmument + * if purge_outstanding_packets() was called with an argument * we delete only packets belonging to the given interface */ if ((hard_iface) && diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index 247172d..c8ca3ef 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -22,18 +22,13 @@ #ifndef _NET_BATMAN_ADV_SEND_H_ #define _NET_BATMAN_ADV_SEND_H_ -int send_skb_packet(struct sk_buff *skb, - struct hard_iface *hard_iface, - uint8_t *dst_addr); -void schedule_own_packet(struct hard_iface *hard_iface); -void schedule_forward_packet(struct orig_node *orig_node, - struct ethhdr *ethhdr, - struct batman_packet *batman_packet, - uint8_t directlink, int tt_buff_len, - struct hard_iface *if_outgoing); -int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb); -void send_outstanding_bat_packet(struct work_struct *work); +int send_skb_packet(struct sk_buff *skb, struct hard_iface *hard_iface, + const uint8_t *dst_addr); +void schedule_bat_ogm(struct hard_iface *hard_iface); +int add_bcast_packet_to_list(struct bat_priv *bat_priv, + const struct sk_buff *skb, unsigned long delay); +void send_outstanding_bat_ogm_packet(struct work_struct *work); void purge_outstanding_packets(struct bat_priv *bat_priv, - struct hard_iface *hard_iface); + const struct hard_iface *hard_iface); #endif /* _NET_BATMAN_ADV_SEND_H_ */ diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index d5aa609..f9cc957 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -30,6 +30,7 @@ #include "gateway_common.h" #include "gateway_client.h" #include "bat_sysfs.h" +#include "originator.h" #include <linux/slab.h> #include <linux/ethtool.h> #include <linux/etherdevice.h> @@ -123,8 +124,7 @@ static struct softif_neigh_vid *softif_neigh_vid_get(struct bat_priv *bat_priv, goto out; } - softif_neigh_vid = kzalloc(sizeof(struct softif_neigh_vid), - GFP_ATOMIC); + softif_neigh_vid = kzalloc(sizeof(*softif_neigh_vid), GFP_ATOMIC); if (!softif_neigh_vid) goto out; @@ -146,7 +146,7 @@ out: } static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, - uint8_t *addr, short vid) + const uint8_t *addr, short vid) { struct softif_neigh_vid *softif_neigh_vid; struct softif_neigh *softif_neigh = NULL; @@ -170,7 +170,7 @@ static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, goto unlock; } - softif_neigh = kzalloc(sizeof(struct softif_neigh), GFP_ATOMIC); + softif_neigh = kzalloc(sizeof(*softif_neigh), GFP_ATOMIC); if (!softif_neigh) goto unlock; @@ -242,7 +242,8 @@ static void softif_neigh_vid_select(struct bat_priv *bat_priv, if (new_neigh && !atomic_inc_not_zero(&new_neigh->refcount)) new_neigh = NULL; - curr_neigh = softif_neigh_vid->softif_neigh; + curr_neigh = rcu_dereference_protected(softif_neigh_vid->softif_neigh, + 1); rcu_assign_pointer(softif_neigh_vid->softif_neigh, new_neigh); if ((curr_neigh) && (!new_neigh)) @@ -380,7 +381,7 @@ void softif_neigh_purge(struct bat_priv *bat_priv) struct softif_neigh *softif_neigh, *curr_softif_neigh; struct softif_neigh_vid *softif_neigh_vid; struct hlist_node *node, *node_tmp, *node_tmp2; - char do_deselect; + int do_deselect; rcu_read_lock(); hlist_for_each_entry_rcu(softif_neigh_vid, node, @@ -444,30 +445,31 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, { struct bat_priv *bat_priv = netdev_priv(dev); struct ethhdr *ethhdr = (struct ethhdr *)skb->data; - struct batman_packet *batman_packet; + struct batman_ogm_packet *batman_ogm_packet; struct softif_neigh *softif_neigh = NULL; struct hard_iface *primary_if = NULL; struct softif_neigh *curr_softif_neigh = NULL; if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) - batman_packet = (struct batman_packet *) + batman_ogm_packet = (struct batman_ogm_packet *) (skb->data + ETH_HLEN + VLAN_HLEN); else - batman_packet = (struct batman_packet *)(skb->data + ETH_HLEN); + batman_ogm_packet = (struct batman_ogm_packet *) + (skb->data + ETH_HLEN); - if (batman_packet->version != COMPAT_VERSION) + if (batman_ogm_packet->version != COMPAT_VERSION) goto out; - if (batman_packet->packet_type != BAT_PACKET) + if (batman_ogm_packet->packet_type != BAT_OGM) goto out; - if (!(batman_packet->flags & PRIMARIES_FIRST_HOP)) + if (!(batman_ogm_packet->flags & PRIMARIES_FIRST_HOP)) goto out; - if (is_my_mac(batman_packet->orig)) + if (is_my_mac(batman_ogm_packet->orig)) goto out; - softif_neigh = softif_neigh_get(bat_priv, batman_packet->orig, vid); + softif_neigh = softif_neigh_get(bat_priv, batman_ogm_packet->orig, vid); if (!softif_neigh) goto out; @@ -531,11 +533,11 @@ static int interface_set_mac_addr(struct net_device *dev, void *p) if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - /* only modify transtable if it has been initialised before */ + /* only modify transtable if it has been initialized before */ if (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) { tt_local_remove(bat_priv, dev->dev_addr, - "mac address changed"); - tt_local_add(dev, addr->sa_data); + "mac address changed", false); + tt_local_add(dev, addr->sa_data, NULL_IFINDEX); } memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); @@ -553,7 +555,7 @@ static int interface_change_mtu(struct net_device *dev, int new_mtu) return 0; } -int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) +static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) { struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct bat_priv *bat_priv = netdev_priv(soft_iface); @@ -561,9 +563,10 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) struct bcast_packet *bcast_packet; struct vlan_ethhdr *vhdr; struct softif_neigh *curr_softif_neigh = NULL; + struct orig_node *orig_node = NULL; int data_len = skb->len, ret; short vid = -1; - bool do_bcast = false; + bool do_bcast; if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) goto dropped; @@ -592,17 +595,20 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) if (curr_softif_neigh) goto dropped; - /* TODO: check this for locks */ - tt_local_add(soft_iface, ethhdr->h_source); + /* Register the client MAC in the transtable */ + tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif); - if (is_multicast_ether_addr(ethhdr->h_dest)) { - ret = gw_is_target(bat_priv, skb); + orig_node = transtable_search(bat_priv, ethhdr->h_source, + ethhdr->h_dest); + do_bcast = is_multicast_ether_addr(ethhdr->h_dest); + if (do_bcast || (orig_node && orig_node->gw_flags)) { + ret = gw_is_target(bat_priv, skb, orig_node); if (ret < 0) goto dropped; - if (ret == 0) - do_bcast = true; + if (ret) + do_bcast = false; } /* ethernet packet should be broadcasted */ @@ -611,7 +617,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) if (!primary_if) goto dropped; - if (my_skb_head_push(skb, sizeof(struct bcast_packet)) < 0) + if (my_skb_head_push(skb, sizeof(*bcast_packet)) < 0) goto dropped; bcast_packet = (struct bcast_packet *)skb->data; @@ -630,7 +636,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) bcast_packet->seqno = htonl(atomic_inc_return(&bat_priv->bcast_seqno)); - add_bcast_packet_to_list(bat_priv, skb); + add_bcast_packet_to_list(bat_priv, skb, 1); /* a copy is stored in the bcast list, therefore removing * the original skb. */ @@ -656,6 +662,8 @@ end: softif_neigh_free_ref(curr_softif_neigh); if (primary_if) hardif_free_ref(primary_if); + if (orig_node) + orig_node_free_ref(orig_node); return NETDEV_TX_OK; } @@ -733,6 +741,9 @@ void interface_rx(struct net_device *soft_iface, soft_iface->last_rx = jiffies; + if (is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest)) + goto dropped; + netif_rx(skb); goto out; @@ -744,7 +755,6 @@ out: return; } -#ifdef HAVE_NET_DEVICE_OPS static const struct net_device_ops bat_netdev_ops = { .ndo_open = interface_open, .ndo_stop = interface_release, @@ -754,7 +764,6 @@ static const struct net_device_ops bat_netdev_ops = { .ndo_start_xmit = interface_tx, .ndo_validate_addr = eth_validate_addr }; -#endif static void interface_setup(struct net_device *dev) { @@ -763,16 +772,7 @@ static void interface_setup(struct net_device *dev) ether_setup(dev); -#ifdef HAVE_NET_DEVICE_OPS dev->netdev_ops = &bat_netdev_ops; -#else - dev->open = interface_open; - dev->stop = interface_release; - dev->get_stats = interface_stats; - dev->set_mac_address = interface_set_mac_addr; - dev->change_mtu = interface_change_mtu; - dev->hard_start_xmit = interface_tx; -#endif dev->destructor = free_netdev; dev->tx_queue_len = 0; @@ -790,22 +790,19 @@ static void interface_setup(struct net_device *dev) SET_ETHTOOL_OPS(dev, &bat_ethtool_ops); - memset(priv, 0, sizeof(struct bat_priv)); + memset(priv, 0, sizeof(*priv)); } -struct net_device *softif_create(char *name) +struct net_device *softif_create(const char *name) { struct net_device *soft_iface; struct bat_priv *bat_priv; int ret; - soft_iface = alloc_netdev(sizeof(struct bat_priv) , name, - interface_setup); + soft_iface = alloc_netdev(sizeof(*bat_priv), name, interface_setup); - if (!soft_iface) { - pr_err("Unable to allocate the batman interface: %s\n", name); + if (!soft_iface) goto out; - } ret = register_netdevice(soft_iface); if (ret < 0) { @@ -818,6 +815,7 @@ struct net_device *softif_create(char *name) atomic_set(&bat_priv->aggregated_ogms, 1); atomic_set(&bat_priv->bonding, 0); + atomic_set(&bat_priv->ap_isolation, 0); atomic_set(&bat_priv->vis_mode, VIS_TYPE_CLIENT_UPDATE); atomic_set(&bat_priv->gw_mode, GW_MODE_OFF); atomic_set(&bat_priv->gw_sel_class, 20); @@ -831,7 +829,13 @@ struct net_device *softif_create(char *name) atomic_set(&bat_priv->mesh_state, MESH_INACTIVE); atomic_set(&bat_priv->bcast_seqno, 1); - atomic_set(&bat_priv->tt_local_changed, 0); + atomic_set(&bat_priv->ttvn, 0); + atomic_set(&bat_priv->tt_local_changes, 0); + atomic_set(&bat_priv->tt_ogm_append_cnt, 0); + + bat_priv->tt_buff = NULL; + bat_priv->tt_buff_len = 0; + bat_priv->tt_poss_change = false; bat_priv->primary_if = NULL; bat_priv->num_ifaces = 0; @@ -872,15 +876,10 @@ void softif_destroy(struct net_device *soft_iface) unregister_netdevice(soft_iface); } -int softif_is_valid(struct net_device *net_dev) +int softif_is_valid(const struct net_device *net_dev) { -#ifdef HAVE_NET_DEVICE_OPS if (net_dev->netdev_ops->ndo_start_xmit == interface_tx) return 1; -#else - if (net_dev->hard_start_xmit == interface_tx) - return 1; -#endif return 0; } @@ -924,4 +923,3 @@ static u32 bat_get_link(struct net_device *dev) { return 1; } - diff --git a/net/batman-adv/soft-interface.h b/net/batman-adv/soft-interface.h index 4789b6f..001546f 100644 --- a/net/batman-adv/soft-interface.h +++ b/net/batman-adv/soft-interface.h @@ -25,12 +25,11 @@ int my_skb_head_push(struct sk_buff *skb, unsigned int len); int softif_neigh_seq_print_text(struct seq_file *seq, void *offset); void softif_neigh_purge(struct bat_priv *bat_priv); -int interface_tx(struct sk_buff *skb, struct net_device *soft_iface); void interface_rx(struct net_device *soft_iface, struct sk_buff *skb, struct hard_iface *recv_if, int hdr_size); -struct net_device *softif_create(char *name); +struct net_device *softif_create(const char *name); void softif_destroy(struct net_device *soft_iface); -int softif_is_valid(struct net_device *net_dev); +int softif_is_valid(const struct net_device *net_dev); #endif /* _NET_BATMAN_ADV_SOFT_INTERFACE_H_ */ diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 7b72966..088af45 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -23,38 +23,45 @@ #include "translation-table.h" #include "soft-interface.h" #include "hard-interface.h" +#include "send.h" #include "hash.h" #include "originator.h" +#include "routing.h" -static void tt_local_purge(struct work_struct *work); -static void _tt_global_del_orig(struct bat_priv *bat_priv, - struct tt_global_entry *tt_global_entry, - char *message); +#include <linux/crc16.h> + +static void _tt_global_del(struct bat_priv *bat_priv, + struct tt_global_entry *tt_global_entry, + const char *message); +static void tt_purge(struct work_struct *work); /* returns 1 if they are the same mac addr */ -static int compare_ltt(struct hlist_node *node, void *data2) +static int compare_ltt(const struct hlist_node *node, const void *data2) { - void *data1 = container_of(node, struct tt_local_entry, hash_entry); + const void *data1 = container_of(node, struct tt_local_entry, + hash_entry); return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); } /* returns 1 if they are the same mac addr */ -static int compare_gtt(struct hlist_node *node, void *data2) +static int compare_gtt(const struct hlist_node *node, const void *data2) { - void *data1 = container_of(node, struct tt_global_entry, hash_entry); + const void *data1 = container_of(node, struct tt_global_entry, + hash_entry); return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); } -static void tt_local_start_timer(struct bat_priv *bat_priv) +static void tt_start_timer(struct bat_priv *bat_priv) { - INIT_DELAYED_WORK(&bat_priv->tt_work, tt_local_purge); - queue_delayed_work(bat_event_workqueue, &bat_priv->tt_work, 10 * HZ); + INIT_DELAYED_WORK(&bat_priv->tt_work, tt_purge); + queue_delayed_work(bat_event_workqueue, &bat_priv->tt_work, + msecs_to_jiffies(5000)); } static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv, - void *data) + const void *data) { struct hashtable_t *hash = bat_priv->tt_local_hash; struct hlist_head *head; @@ -73,6 +80,9 @@ static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv, if (!compare_eth(tt_local_entry, data)) continue; + if (!atomic_inc_not_zero(&tt_local_entry->refcount)) + continue; + tt_local_entry_tmp = tt_local_entry; break; } @@ -82,7 +92,7 @@ static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv, } static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv, - void *data) + const void *data) { struct hashtable_t *hash = bat_priv->tt_global_hash; struct hlist_head *head; @@ -102,6 +112,9 @@ static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv, if (!compare_eth(tt_global_entry, data)) continue; + if (!atomic_inc_not_zero(&tt_global_entry->refcount)) + continue; + tt_global_entry_tmp = tt_global_entry; break; } @@ -110,7 +123,66 @@ static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv, return tt_global_entry_tmp; } -int tt_local_init(struct bat_priv *bat_priv) +static bool is_out_of_time(unsigned long starting_time, unsigned long timeout) +{ + unsigned long deadline; + deadline = starting_time + msecs_to_jiffies(timeout); + + return time_after(jiffies, deadline); +} + +static void tt_local_entry_free_ref(struct tt_local_entry *tt_local_entry) +{ + if (atomic_dec_and_test(&tt_local_entry->refcount)) + kfree_rcu(tt_local_entry, rcu); +} + +static void tt_global_entry_free_rcu(struct rcu_head *rcu) +{ + struct tt_global_entry *tt_global_entry; + + tt_global_entry = container_of(rcu, struct tt_global_entry, rcu); + + if (tt_global_entry->orig_node) + orig_node_free_ref(tt_global_entry->orig_node); + + kfree(tt_global_entry); +} + +static void tt_global_entry_free_ref(struct tt_global_entry *tt_global_entry) +{ + if (atomic_dec_and_test(&tt_global_entry->refcount)) + call_rcu(&tt_global_entry->rcu, tt_global_entry_free_rcu); +} + +static void tt_local_event(struct bat_priv *bat_priv, const uint8_t *addr, + uint8_t flags) +{ + struct tt_change_node *tt_change_node; + + tt_change_node = kmalloc(sizeof(*tt_change_node), GFP_ATOMIC); + + if (!tt_change_node) + return; + + tt_change_node->change.flags = flags; + memcpy(tt_change_node->change.addr, addr, ETH_ALEN); + + spin_lock_bh(&bat_priv->tt_changes_list_lock); + /* track the change in the OGMinterval list */ + list_add_tail(&tt_change_node->list, &bat_priv->tt_changes_list); + atomic_inc(&bat_priv->tt_local_changes); + spin_unlock_bh(&bat_priv->tt_changes_list_lock); + + atomic_set(&bat_priv->tt_ogm_append_cnt, 0); +} + +int tt_len(int changes_num) +{ + return changes_num * sizeof(struct tt_change); +} + +static int tt_local_init(struct bat_priv *bat_priv) { if (bat_priv->tt_local_hash) return 1; @@ -120,116 +192,119 @@ int tt_local_init(struct bat_priv *bat_priv) if (!bat_priv->tt_local_hash) return 0; - atomic_set(&bat_priv->tt_local_changed, 0); - tt_local_start_timer(bat_priv); - return 1; } -void tt_local_add(struct net_device *soft_iface, uint8_t *addr) +void tt_local_add(struct net_device *soft_iface, const uint8_t *addr, + int ifindex) { struct bat_priv *bat_priv = netdev_priv(soft_iface); - struct tt_local_entry *tt_local_entry; - struct tt_global_entry *tt_global_entry; - int required_bytes; + struct tt_local_entry *tt_local_entry = NULL; + struct tt_global_entry *tt_global_entry = NULL; - spin_lock_bh(&bat_priv->tt_lhash_lock); tt_local_entry = tt_local_hash_find(bat_priv, addr); - spin_unlock_bh(&bat_priv->tt_lhash_lock); if (tt_local_entry) { tt_local_entry->last_seen = jiffies; - return; - } - - /* only announce as many hosts as possible in the batman-packet and - space in batman_packet->num_tt That also should give a limit to - MAC-flooding. */ - required_bytes = (bat_priv->num_local_tt + 1) * ETH_ALEN; - required_bytes += BAT_PACKET_LEN; - - if ((required_bytes > ETH_DATA_LEN) || - (atomic_read(&bat_priv->aggregated_ogms) && - required_bytes > MAX_AGGREGATION_BYTES) || - (bat_priv->num_local_tt + 1 > 255)) { - bat_dbg(DBG_ROUTES, bat_priv, - "Can't add new local tt entry (%pM): " - "number of local tt entries exceeds packet size\n", - addr); - return; + goto out; } - bat_dbg(DBG_ROUTES, bat_priv, - "Creating new local tt entry: %pM\n", addr); - - tt_local_entry = kmalloc(sizeof(struct tt_local_entry), GFP_ATOMIC); + tt_local_entry = kmalloc(sizeof(*tt_local_entry), GFP_ATOMIC); if (!tt_local_entry) - return; + goto out; + + bat_dbg(DBG_TT, bat_priv, + "Creating new local tt entry: %pM (ttvn: %d)\n", addr, + (uint8_t)atomic_read(&bat_priv->ttvn)); memcpy(tt_local_entry->addr, addr, ETH_ALEN); tt_local_entry->last_seen = jiffies; + tt_local_entry->flags = NO_FLAGS; + if (is_wifi_iface(ifindex)) + tt_local_entry->flags |= TT_CLIENT_WIFI; + atomic_set(&tt_local_entry->refcount, 2); /* the batman interface mac address should never be purged */ if (compare_eth(addr, soft_iface->dev_addr)) - tt_local_entry->never_purge = 1; - else - tt_local_entry->never_purge = 0; + tt_local_entry->flags |= TT_CLIENT_NOPURGE; + + tt_local_event(bat_priv, addr, tt_local_entry->flags); - spin_lock_bh(&bat_priv->tt_lhash_lock); + /* 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->flags |= TT_CLIENT_NEW; hash_add(bat_priv->tt_local_hash, compare_ltt, choose_orig, tt_local_entry, &tt_local_entry->hash_entry); - bat_priv->num_local_tt++; - atomic_set(&bat_priv->tt_local_changed, 1); - - spin_unlock_bh(&bat_priv->tt_lhash_lock); /* remove address from global hash if present */ - spin_lock_bh(&bat_priv->tt_ghash_lock); - tt_global_entry = tt_global_hash_find(bat_priv, addr); + /* Check whether it is a roaming! */ + if (tt_global_entry) { + /* This node is probably going to update its tt table */ + tt_global_entry->orig_node->tt_poss_change = true; + /* The global entry has to be marked as ROAMING and has to be + * kept for consistency purpose */ + tt_global_entry->flags |= TT_CLIENT_ROAM; + tt_global_entry->roam_at = jiffies; + + send_roam_adv(bat_priv, tt_global_entry->addr, + tt_global_entry->orig_node); + } +out: + if (tt_local_entry) + tt_local_entry_free_ref(tt_local_entry); if (tt_global_entry) - _tt_global_del_orig(bat_priv, tt_global_entry, - "local tt received"); - - spin_unlock_bh(&bat_priv->tt_ghash_lock); + tt_global_entry_free_ref(tt_global_entry); } -int tt_local_fill_buffer(struct bat_priv *bat_priv, - unsigned char *buff, int buff_len) +int tt_changes_fill_buffer(struct bat_priv *bat_priv, + unsigned char *buff, int buff_len) { - struct hashtable_t *hash = bat_priv->tt_local_hash; - struct tt_local_entry *tt_local_entry; - struct hlist_node *node; - struct hlist_head *head; - int i, count = 0; - - spin_lock_bh(&bat_priv->tt_lhash_lock); - - for (i = 0; i < hash->size; i++) { - head = &hash->table[i]; + int count = 0, tot_changes = 0; + struct tt_change_node *entry, *safe; - rcu_read_lock(); - hlist_for_each_entry_rcu(tt_local_entry, node, - head, hash_entry) { - if (buff_len < (count + 1) * ETH_ALEN) - break; + if (buff_len > 0) + tot_changes = buff_len / tt_len(1); - memcpy(buff + (count * ETH_ALEN), tt_local_entry->addr, - ETH_ALEN); + spin_lock_bh(&bat_priv->tt_changes_list_lock); + atomic_set(&bat_priv->tt_local_changes, 0); + list_for_each_entry_safe(entry, safe, &bat_priv->tt_changes_list, + list) { + if (count < tot_changes) { + memcpy(buff + tt_len(count), + &entry->change, sizeof(struct tt_change)); count++; } - rcu_read_unlock(); + list_del(&entry->list); + kfree(entry); } + spin_unlock_bh(&bat_priv->tt_changes_list_lock); + + /* Keep the buffer for possible tt_request */ + spin_lock_bh(&bat_priv->tt_buff_lock); + kfree(bat_priv->tt_buff); + bat_priv->tt_buff_len = 0; + bat_priv->tt_buff = NULL; + /* We check whether this new OGM has no changes due to size + * problems */ + if (buff_len > 0) { + /** + * if kmalloc() fails we will reply with the full table + * instead of providing the diff + */ + bat_priv->tt_buff = kmalloc(buff_len, GFP_ATOMIC); + if (bat_priv->tt_buff) { + memcpy(bat_priv->tt_buff, buff, buff_len); + bat_priv->tt_buff_len = buff_len; + } + } + spin_unlock_bh(&bat_priv->tt_buff_lock); - /* if we did not get all new local tts see you next time ;-) */ - if (count == bat_priv->num_local_tt) - atomic_set(&bat_priv->tt_local_changed, 0); - - spin_unlock_bh(&bat_priv->tt_lhash_lock); - return count; + return tot_changes; } int tt_local_seq_print_text(struct seq_file *seq, void *offset) @@ -261,10 +336,8 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset) } seq_printf(seq, "Locally retrieved addresses (from %s) " - "announced via TT:\n", - net_dev->name); - - spin_lock_bh(&bat_priv->tt_lhash_lock); + "announced via TT (TTVN: %u):\n", + net_dev->name, (uint8_t)atomic_read(&bat_priv->ttvn)); buf_size = 1; /* Estimate length for: " * xx:xx:xx:xx:xx:xx\n" */ @@ -273,13 +346,12 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset) rcu_read_lock(); __hlist_for_each_rcu(node, head) - buf_size += 21; + buf_size += 29; rcu_read_unlock(); } buff = kmalloc(buf_size, GFP_ATOMIC); if (!buff) { - spin_unlock_bh(&bat_priv->tt_lhash_lock); ret = -ENOMEM; goto out; } @@ -293,14 +365,23 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset) rcu_read_lock(); hlist_for_each_entry_rcu(tt_local_entry, node, head, hash_entry) { - pos += snprintf(buff + pos, 22, " * %pM\n", - tt_local_entry->addr); + pos += snprintf(buff + pos, 30, " * %pM " + "[%c%c%c%c%c]\n", + tt_local_entry->addr, + (tt_local_entry->flags & + TT_CLIENT_ROAM ? 'R' : '.'), + (tt_local_entry->flags & + TT_CLIENT_NOPURGE ? 'P' : '.'), + (tt_local_entry->flags & + TT_CLIENT_NEW ? 'N' : '.'), + (tt_local_entry->flags & + TT_CLIENT_PENDING ? 'X' : '.'), + (tt_local_entry->flags & + TT_CLIENT_WIFI ? 'W' : '.')); } rcu_read_unlock(); } - spin_unlock_bh(&bat_priv->tt_lhash_lock); - seq_printf(seq, "%s", buff); kfree(buff); out: @@ -309,92 +390,109 @@ out: return ret; } -static void _tt_local_del(struct hlist_node *node, void *arg) +static void tt_local_set_pending(struct bat_priv *bat_priv, + struct tt_local_entry *tt_local_entry, + uint16_t flags) { - struct bat_priv *bat_priv = (struct bat_priv *)arg; - void *data = container_of(node, struct tt_local_entry, hash_entry); + tt_local_event(bat_priv, tt_local_entry->addr, + tt_local_entry->flags | flags); - kfree(data); - bat_priv->num_local_tt--; - atomic_set(&bat_priv->tt_local_changed, 1); + /* The local client has to be marked as "pending to be removed" but has + * to be kept in the table in order to send it in a full table + * response issued before the net ttvn increment (consistency check) */ + tt_local_entry->flags |= TT_CLIENT_PENDING; } -static void tt_local_del(struct bat_priv *bat_priv, - struct tt_local_entry *tt_local_entry, - char *message) +void tt_local_remove(struct bat_priv *bat_priv, const uint8_t *addr, + const char *message, bool roaming) { - bat_dbg(DBG_ROUTES, bat_priv, "Deleting local tt entry (%pM): %s\n", - tt_local_entry->addr, message); - - hash_remove(bat_priv->tt_local_hash, compare_ltt, choose_orig, - tt_local_entry->addr); - _tt_local_del(&tt_local_entry->hash_entry, bat_priv); -} - -void tt_local_remove(struct bat_priv *bat_priv, - uint8_t *addr, char *message) -{ - struct tt_local_entry *tt_local_entry; - - spin_lock_bh(&bat_priv->tt_lhash_lock); + struct tt_local_entry *tt_local_entry = NULL; tt_local_entry = tt_local_hash_find(bat_priv, addr); + if (!tt_local_entry) + goto out; - if (tt_local_entry) - tt_local_del(bat_priv, tt_local_entry, message); + tt_local_set_pending(bat_priv, tt_local_entry, TT_CLIENT_DEL | + (roaming ? TT_CLIENT_ROAM : NO_FLAGS)); - spin_unlock_bh(&bat_priv->tt_lhash_lock); + bat_dbg(DBG_TT, bat_priv, "Local tt entry (%pM) pending to be removed: " + "%s\n", tt_local_entry->addr, message); +out: + if (tt_local_entry) + tt_local_entry_free_ref(tt_local_entry); } -static void tt_local_purge(struct work_struct *work) +static void tt_local_purge(struct bat_priv *bat_priv) { - struct delayed_work *delayed_work = - container_of(work, struct delayed_work, work); - struct bat_priv *bat_priv = - container_of(delayed_work, struct bat_priv, tt_work); struct hashtable_t *hash = bat_priv->tt_local_hash; struct tt_local_entry *tt_local_entry; struct hlist_node *node, *node_tmp; struct hlist_head *head; - unsigned long timeout; + spinlock_t *list_lock; /* protects write access to the hash lists */ int i; - spin_lock_bh(&bat_priv->tt_lhash_lock); - for (i = 0; i < hash->size; i++) { head = &hash->table[i]; + list_lock = &hash->list_locks[i]; + spin_lock_bh(list_lock); hlist_for_each_entry_safe(tt_local_entry, node, node_tmp, head, hash_entry) { - if (tt_local_entry->never_purge) + if (tt_local_entry->flags & TT_CLIENT_NOPURGE) continue; - timeout = tt_local_entry->last_seen; - timeout += TT_LOCAL_TIMEOUT * HZ; + /* entry already marked for deletion */ + if (tt_local_entry->flags & TT_CLIENT_PENDING) + continue; - if (time_before(jiffies, timeout)) + if (!is_out_of_time(tt_local_entry->last_seen, + TT_LOCAL_TIMEOUT * 1000)) continue; - tt_local_del(bat_priv, tt_local_entry, - "address timed out"); + tt_local_set_pending(bat_priv, tt_local_entry, + TT_CLIENT_DEL); + bat_dbg(DBG_TT, bat_priv, "Local tt entry (%pM) " + "pending to be removed: timed out\n", + tt_local_entry->addr); } + spin_unlock_bh(list_lock); } - spin_unlock_bh(&bat_priv->tt_lhash_lock); - tt_local_start_timer(bat_priv); } -void tt_local_free(struct bat_priv *bat_priv) +static void tt_local_table_free(struct bat_priv *bat_priv) { + struct hashtable_t *hash; + spinlock_t *list_lock; /* protects write access to the hash lists */ + struct tt_local_entry *tt_local_entry; + struct hlist_node *node, *node_tmp; + struct hlist_head *head; + int i; + if (!bat_priv->tt_local_hash) return; - cancel_delayed_work_sync(&bat_priv->tt_work); - hash_delete(bat_priv->tt_local_hash, _tt_local_del, bat_priv); + hash = bat_priv->tt_local_hash; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + list_lock = &hash->list_locks[i]; + + spin_lock_bh(list_lock); + hlist_for_each_entry_safe(tt_local_entry, node, node_tmp, + head, hash_entry) { + hlist_del_rcu(node); + tt_local_entry_free_ref(tt_local_entry); + } + spin_unlock_bh(list_lock); + } + + hash_destroy(hash); + bat_priv->tt_local_hash = NULL; } -int tt_global_init(struct bat_priv *bat_priv) +static int tt_global_init(struct bat_priv *bat_priv) { if (bat_priv->tt_global_hash) return 1; @@ -407,74 +505,82 @@ int tt_global_init(struct bat_priv *bat_priv) return 1; } -void tt_global_add_orig(struct bat_priv *bat_priv, - struct orig_node *orig_node, - unsigned char *tt_buff, int tt_buff_len) +static void tt_changes_list_free(struct bat_priv *bat_priv) { - struct tt_global_entry *tt_global_entry; - struct tt_local_entry *tt_local_entry; - int tt_buff_count = 0; - unsigned char *tt_ptr; - - while ((tt_buff_count + 1) * ETH_ALEN <= tt_buff_len) { - spin_lock_bh(&bat_priv->tt_ghash_lock); - - tt_ptr = tt_buff + (tt_buff_count * ETH_ALEN); - tt_global_entry = tt_global_hash_find(bat_priv, tt_ptr); + struct tt_change_node *entry, *safe; - if (!tt_global_entry) { - spin_unlock_bh(&bat_priv->tt_ghash_lock); + spin_lock_bh(&bat_priv->tt_changes_list_lock); - tt_global_entry = - kmalloc(sizeof(struct tt_global_entry), - GFP_ATOMIC); - - if (!tt_global_entry) - break; + list_for_each_entry_safe(entry, safe, &bat_priv->tt_changes_list, + list) { + list_del(&entry->list); + kfree(entry); + } - memcpy(tt_global_entry->addr, tt_ptr, ETH_ALEN); + atomic_set(&bat_priv->tt_local_changes, 0); + spin_unlock_bh(&bat_priv->tt_changes_list_lock); +} - bat_dbg(DBG_ROUTES, bat_priv, - "Creating new global tt entry: " - "%pM (via %pM)\n", - tt_global_entry->addr, orig_node->orig); +/* caller must hold orig_node refcount */ +int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node, + const unsigned char *tt_addr, uint8_t ttvn, bool roaming, + bool wifi) +{ + struct tt_global_entry *tt_global_entry; + struct orig_node *orig_node_tmp; + int ret = 0; - spin_lock_bh(&bat_priv->tt_ghash_lock); - hash_add(bat_priv->tt_global_hash, compare_gtt, - choose_orig, tt_global_entry, - &tt_global_entry->hash_entry); + tt_global_entry = tt_global_hash_find(bat_priv, tt_addr); - } + if (!tt_global_entry) { + tt_global_entry = + kmalloc(sizeof(*tt_global_entry), + GFP_ATOMIC); + if (!tt_global_entry) + goto out; + memcpy(tt_global_entry->addr, tt_addr, ETH_ALEN); + /* Assign the new orig_node */ + atomic_inc(&orig_node->refcount); tt_global_entry->orig_node = orig_node; - spin_unlock_bh(&bat_priv->tt_ghash_lock); - - /* remove address from local hash if present */ - spin_lock_bh(&bat_priv->tt_lhash_lock); - - tt_ptr = tt_buff + (tt_buff_count * ETH_ALEN); - tt_local_entry = tt_local_hash_find(bat_priv, tt_ptr); - - if (tt_local_entry) - tt_local_del(bat_priv, tt_local_entry, - "global tt received"); - - spin_unlock_bh(&bat_priv->tt_lhash_lock); - - tt_buff_count++; + tt_global_entry->ttvn = ttvn; + tt_global_entry->flags = NO_FLAGS; + tt_global_entry->roam_at = 0; + atomic_set(&tt_global_entry->refcount, 2); + + hash_add(bat_priv->tt_global_hash, compare_gtt, + choose_orig, tt_global_entry, + &tt_global_entry->hash_entry); + atomic_inc(&orig_node->tt_size); + } else { + if (tt_global_entry->orig_node != orig_node) { + atomic_dec(&tt_global_entry->orig_node->tt_size); + orig_node_tmp = tt_global_entry->orig_node; + atomic_inc(&orig_node->refcount); + tt_global_entry->orig_node = orig_node; + orig_node_free_ref(orig_node_tmp); + atomic_inc(&orig_node->tt_size); + } + tt_global_entry->ttvn = ttvn; + tt_global_entry->flags = NO_FLAGS; + tt_global_entry->roam_at = 0; } - /* initialize, and overwrite if malloc succeeds */ - orig_node->tt_buff = NULL; - orig_node->tt_buff_len = 0; + if (wifi) + tt_global_entry->flags |= TT_CLIENT_WIFI; - if (tt_buff_len > 0) { - orig_node->tt_buff = kmalloc(tt_buff_len, GFP_ATOMIC); - if (orig_node->tt_buff) { - memcpy(orig_node->tt_buff, tt_buff, tt_buff_len); - orig_node->tt_buff_len = tt_buff_len; - } - } + bat_dbg(DBG_TT, bat_priv, + "Creating new global tt entry: %pM (via %pM)\n", + tt_global_entry->addr, orig_node->orig); + + /* remove address from local hash if present */ + tt_local_remove(bat_priv, tt_global_entry->addr, + "global tt received", roaming); + ret = 1; +out: + if (tt_global_entry) + tt_global_entry_free_ref(tt_global_entry); + return ret; } int tt_global_seq_print_text(struct seq_file *seq, void *offset) @@ -508,26 +614,27 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset) seq_printf(seq, "Globally announced TT entries received via the mesh %s\n", net_dev->name); - - spin_lock_bh(&bat_priv->tt_ghash_lock); + seq_printf(seq, " %-13s %s %-15s %s %s\n", + "Client", "(TTVN)", "Originator", "(Curr TTVN)", "Flags"); buf_size = 1; - /* Estimate length for: " * xx:xx:xx:xx:xx:xx via xx:xx:xx:xx:xx:xx\n"*/ + /* Estimate length for: " * xx:xx:xx:xx:xx:xx (ttvn) via + * xx:xx:xx:xx:xx:xx (cur_ttvn)\n"*/ for (i = 0; i < hash->size; i++) { head = &hash->table[i]; rcu_read_lock(); __hlist_for_each_rcu(node, head) - buf_size += 43; + buf_size += 67; rcu_read_unlock(); } buff = kmalloc(buf_size, GFP_ATOMIC); if (!buff) { - spin_unlock_bh(&bat_priv->tt_ghash_lock); ret = -ENOMEM; goto out; } + buff[0] = '\0'; pos = 0; @@ -537,16 +644,24 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset) rcu_read_lock(); hlist_for_each_entry_rcu(tt_global_entry, node, head, hash_entry) { - pos += snprintf(buff + pos, 44, - " * %pM via %pM\n", - tt_global_entry->addr, - tt_global_entry->orig_node->orig); + pos += snprintf(buff + pos, 69, + " * %pM (%3u) via %pM (%3u) " + "[%c%c%c]\n", tt_global_entry->addr, + tt_global_entry->ttvn, + tt_global_entry->orig_node->orig, + (uint8_t) atomic_read( + &tt_global_entry->orig_node-> + last_ttvn), + (tt_global_entry->flags & + TT_CLIENT_ROAM ? 'R' : '.'), + (tt_global_entry->flags & + TT_CLIENT_PENDING ? 'X' : '.'), + (tt_global_entry->flags & + TT_CLIENT_WIFI ? 'W' : '.')); } rcu_read_unlock(); } - spin_unlock_bh(&bat_priv->tt_ghash_lock); - seq_printf(seq, "%s", buff); kfree(buff); out: @@ -555,84 +670,1231 @@ out: return ret; } -static void _tt_global_del_orig(struct bat_priv *bat_priv, - struct tt_global_entry *tt_global_entry, - char *message) +static void _tt_global_del(struct bat_priv *bat_priv, + struct tt_global_entry *tt_global_entry, + const char *message) { - bat_dbg(DBG_ROUTES, bat_priv, + if (!tt_global_entry) + goto out; + + bat_dbg(DBG_TT, bat_priv, "Deleting global tt entry %pM (via %pM): %s\n", tt_global_entry->addr, tt_global_entry->orig_node->orig, message); + atomic_dec(&tt_global_entry->orig_node->tt_size); + hash_remove(bat_priv->tt_global_hash, compare_gtt, choose_orig, tt_global_entry->addr); - kfree(tt_global_entry); +out: + if (tt_global_entry) + tt_global_entry_free_ref(tt_global_entry); +} + +void tt_global_del(struct bat_priv *bat_priv, + struct orig_node *orig_node, const unsigned char *addr, + const char *message, bool roaming) +{ + struct tt_global_entry *tt_global_entry = NULL; + struct tt_local_entry *tt_local_entry = NULL; + + tt_global_entry = tt_global_hash_find(bat_priv, addr); + if (!tt_global_entry) + goto out; + + if (tt_global_entry->orig_node == orig_node) { + if (roaming) { + /* if we are deleting a global entry due to a roam + * event, there are two possibilities: + * 1) the client roamed from node A to node B => we mark + * it with TT_CLIENT_ROAM, we start a timer and we + * wait for node B to claim it. In case of timeout + * the entry is purged. + * 2) the client roamed to us => we can directly delete + * the global entry, since it is useless now. */ + tt_local_entry = tt_local_hash_find(bat_priv, + tt_global_entry->addr); + if (!tt_local_entry) { + tt_global_entry->flags |= TT_CLIENT_ROAM; + tt_global_entry->roam_at = jiffies; + goto out; + } + } + _tt_global_del(bat_priv, tt_global_entry, message); + } +out: + if (tt_global_entry) + tt_global_entry_free_ref(tt_global_entry); + if (tt_local_entry) + tt_local_entry_free_ref(tt_local_entry); } void tt_global_del_orig(struct bat_priv *bat_priv, - struct orig_node *orig_node, char *message) + struct orig_node *orig_node, const char *message) { struct tt_global_entry *tt_global_entry; - int tt_buff_count = 0; - unsigned char *tt_ptr; + int i; + struct hashtable_t *hash = bat_priv->tt_global_hash; + struct hlist_node *node, *safe; + struct hlist_head *head; + spinlock_t *list_lock; /* protects write access to the hash lists */ - if (orig_node->tt_buff_len == 0) + if (!hash) return; - spin_lock_bh(&bat_priv->tt_ghash_lock); - - while ((tt_buff_count + 1) * ETH_ALEN <= orig_node->tt_buff_len) { - tt_ptr = orig_node->tt_buff + (tt_buff_count * ETH_ALEN); - tt_global_entry = tt_global_hash_find(bat_priv, tt_ptr); - - if ((tt_global_entry) && - (tt_global_entry->orig_node == orig_node)) - _tt_global_del_orig(bat_priv, tt_global_entry, - message); + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + list_lock = &hash->list_locks[i]; - tt_buff_count++; + spin_lock_bh(list_lock); + hlist_for_each_entry_safe(tt_global_entry, node, safe, + head, hash_entry) { + if (tt_global_entry->orig_node == orig_node) { + bat_dbg(DBG_TT, bat_priv, + "Deleting global tt entry %pM " + "(via %pM): originator time out\n", + tt_global_entry->addr, + tt_global_entry->orig_node->orig); + hlist_del_rcu(node); + tt_global_entry_free_ref(tt_global_entry); + } + } + spin_unlock_bh(list_lock); } - - spin_unlock_bh(&bat_priv->tt_ghash_lock); - - orig_node->tt_buff_len = 0; - kfree(orig_node->tt_buff); - orig_node->tt_buff = NULL; + atomic_set(&orig_node->tt_size, 0); } -static void tt_global_del(struct hlist_node *node, void *arg) +static void tt_global_roam_purge(struct bat_priv *bat_priv) { - void *data = container_of(node, struct tt_global_entry, hash_entry); + struct hashtable_t *hash = bat_priv->tt_global_hash; + struct tt_global_entry *tt_global_entry; + struct hlist_node *node, *node_tmp; + struct hlist_head *head; + spinlock_t *list_lock; /* protects write access to the hash lists */ + int i; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + list_lock = &hash->list_locks[i]; + + spin_lock_bh(list_lock); + hlist_for_each_entry_safe(tt_global_entry, node, node_tmp, + head, hash_entry) { + if (!(tt_global_entry->flags & TT_CLIENT_ROAM)) + continue; + if (!is_out_of_time(tt_global_entry->roam_at, + TT_CLIENT_ROAM_TIMEOUT * 1000)) + continue; + + bat_dbg(DBG_TT, bat_priv, "Deleting global " + "tt entry (%pM): Roaming timeout\n", + tt_global_entry->addr); + atomic_dec(&tt_global_entry->orig_node->tt_size); + hlist_del_rcu(node); + tt_global_entry_free_ref(tt_global_entry); + } + spin_unlock_bh(list_lock); + } - kfree(data); } -void tt_global_free(struct bat_priv *bat_priv) +static void tt_global_table_free(struct bat_priv *bat_priv) { + struct hashtable_t *hash; + spinlock_t *list_lock; /* protects write access to the hash lists */ + struct tt_global_entry *tt_global_entry; + struct hlist_node *node, *node_tmp; + struct hlist_head *head; + int i; + if (!bat_priv->tt_global_hash) return; - hash_delete(bat_priv->tt_global_hash, tt_global_del, NULL); + hash = bat_priv->tt_global_hash; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + list_lock = &hash->list_locks[i]; + + spin_lock_bh(list_lock); + hlist_for_each_entry_safe(tt_global_entry, node, node_tmp, + head, hash_entry) { + hlist_del_rcu(node); + tt_global_entry_free_ref(tt_global_entry); + } + spin_unlock_bh(list_lock); + } + + hash_destroy(hash); + bat_priv->tt_global_hash = NULL; } -struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr) +static bool _is_ap_isolated(struct tt_local_entry *tt_local_entry, + struct tt_global_entry *tt_global_entry) { - struct tt_global_entry *tt_global_entry; + bool ret = false; + + if (tt_local_entry->flags & TT_CLIENT_WIFI && + tt_global_entry->flags & TT_CLIENT_WIFI) + ret = true; + + return ret; +} + +struct orig_node *transtable_search(struct bat_priv *bat_priv, + const uint8_t *src, const uint8_t *addr) +{ + struct tt_local_entry *tt_local_entry = NULL; + struct tt_global_entry *tt_global_entry = NULL; struct orig_node *orig_node = NULL; - spin_lock_bh(&bat_priv->tt_ghash_lock); - tt_global_entry = tt_global_hash_find(bat_priv, addr); + if (src && atomic_read(&bat_priv->ap_isolation)) { + tt_local_entry = tt_local_hash_find(bat_priv, src); + if (!tt_local_entry) + goto out; + } + tt_global_entry = tt_global_hash_find(bat_priv, addr); if (!tt_global_entry) goto out; + /* check whether the clients should not communicate due to AP + * isolation */ + if (tt_local_entry && _is_ap_isolated(tt_local_entry, tt_global_entry)) + goto out; + if (!atomic_inc_not_zero(&tt_global_entry->orig_node->refcount)) goto out; + /* A global client marked as PENDING has already moved from that + * originator */ + if (tt_global_entry->flags & TT_CLIENT_PENDING) + goto out; + orig_node = tt_global_entry->orig_node; out: - spin_unlock_bh(&bat_priv->tt_ghash_lock); + if (tt_global_entry) + tt_global_entry_free_ref(tt_global_entry); + if (tt_local_entry) + tt_local_entry_free_ref(tt_local_entry); + return orig_node; } + +/* Calculates the checksum of the local table of a given orig_node */ +uint16_t tt_global_crc(struct bat_priv *bat_priv, struct orig_node *orig_node) +{ + uint16_t total = 0, total_one; + struct hashtable_t *hash = bat_priv->tt_global_hash; + struct tt_global_entry *tt_global_entry; + struct hlist_node *node; + struct hlist_head *head; + int i, j; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tt_global_entry, node, + head, hash_entry) { + if (compare_eth(tt_global_entry->orig_node, + orig_node)) { + /* Roaming clients are in the global table for + * consistency only. They don't have to be + * taken into account while computing the + * global crc */ + if (tt_global_entry->flags & TT_CLIENT_ROAM) + continue; + total_one = 0; + for (j = 0; j < ETH_ALEN; j++) + total_one = crc16_byte(total_one, + tt_global_entry->addr[j]); + total ^= total_one; + } + } + rcu_read_unlock(); + } + + return total; +} + +/* Calculates the checksum of the local table */ +uint16_t tt_local_crc(struct bat_priv *bat_priv) +{ + uint16_t total = 0, total_one; + struct hashtable_t *hash = bat_priv->tt_local_hash; + struct tt_local_entry *tt_local_entry; + struct hlist_node *node; + struct hlist_head *head; + int i, j; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tt_local_entry, node, + head, hash_entry) { + /* not yet committed clients have not to be taken into + * account while computing the CRC */ + if (tt_local_entry->flags & TT_CLIENT_NEW) + continue; + total_one = 0; + for (j = 0; j < ETH_ALEN; j++) + total_one = crc16_byte(total_one, + tt_local_entry->addr[j]); + total ^= total_one; + } + rcu_read_unlock(); + } + + return total; +} + +static void tt_req_list_free(struct bat_priv *bat_priv) +{ + struct tt_req_node *node, *safe; + + spin_lock_bh(&bat_priv->tt_req_list_lock); + + list_for_each_entry_safe(node, safe, &bat_priv->tt_req_list, list) { + list_del(&node->list); + kfree(node); + } + + spin_unlock_bh(&bat_priv->tt_req_list_lock); +} + +void tt_save_orig_buffer(struct bat_priv *bat_priv, struct orig_node *orig_node, + const unsigned char *tt_buff, uint8_t tt_num_changes) +{ + uint16_t tt_buff_len = tt_len(tt_num_changes); + + /* Replace the old buffer only if I received something in the + * last OGM (the OGM could carry no changes) */ + spin_lock_bh(&orig_node->tt_buff_lock); + if (tt_buff_len > 0) { + kfree(orig_node->tt_buff); + orig_node->tt_buff_len = 0; + orig_node->tt_buff = kmalloc(tt_buff_len, GFP_ATOMIC); + if (orig_node->tt_buff) { + memcpy(orig_node->tt_buff, tt_buff, tt_buff_len); + orig_node->tt_buff_len = tt_buff_len; + } + } + spin_unlock_bh(&orig_node->tt_buff_lock); +} + +static void tt_req_purge(struct bat_priv *bat_priv) +{ + struct tt_req_node *node, *safe; + + spin_lock_bh(&bat_priv->tt_req_list_lock); + list_for_each_entry_safe(node, safe, &bat_priv->tt_req_list, list) { + if (is_out_of_time(node->issued_at, + TT_REQUEST_TIMEOUT * 1000)) { + list_del(&node->list); + kfree(node); + } + } + spin_unlock_bh(&bat_priv->tt_req_list_lock); +} + +/* returns the pointer to the new tt_req_node struct if no request + * has already been issued for this orig_node, NULL otherwise */ +static struct tt_req_node *new_tt_req_node(struct bat_priv *bat_priv, + struct orig_node *orig_node) +{ + struct tt_req_node *tt_req_node_tmp, *tt_req_node = NULL; + + spin_lock_bh(&bat_priv->tt_req_list_lock); + list_for_each_entry(tt_req_node_tmp, &bat_priv->tt_req_list, list) { + if (compare_eth(tt_req_node_tmp, orig_node) && + !is_out_of_time(tt_req_node_tmp->issued_at, + TT_REQUEST_TIMEOUT * 1000)) + goto unlock; + } + + tt_req_node = kmalloc(sizeof(*tt_req_node), GFP_ATOMIC); + if (!tt_req_node) + goto unlock; + + memcpy(tt_req_node->addr, orig_node->orig, ETH_ALEN); + tt_req_node->issued_at = jiffies; + + list_add(&tt_req_node->list, &bat_priv->tt_req_list); +unlock: + spin_unlock_bh(&bat_priv->tt_req_list_lock); + return tt_req_node; +} + +/* data_ptr is useless here, but has to be kept to respect the prototype */ +static int tt_local_valid_entry(const void *entry_ptr, const void *data_ptr) +{ + const struct tt_local_entry *tt_local_entry = entry_ptr; + + if (tt_local_entry->flags & TT_CLIENT_NEW) + return 0; + return 1; +} + +static int tt_global_valid_entry(const void *entry_ptr, const void *data_ptr) +{ + const struct tt_global_entry *tt_global_entry = entry_ptr; + const struct orig_node *orig_node = data_ptr; + + if (tt_global_entry->flags & TT_CLIENT_ROAM) + return 0; + + return (tt_global_entry->orig_node == orig_node); +} + +static struct sk_buff *tt_response_fill_table(uint16_t tt_len, uint8_t ttvn, + struct hashtable_t *hash, + struct hard_iface *primary_if, + int (*valid_cb)(const void *, + const void *), + void *cb_data) +{ + struct tt_local_entry *tt_local_entry; + struct tt_query_packet *tt_response; + struct tt_change *tt_change; + struct hlist_node *node; + struct hlist_head *head; + struct sk_buff *skb = NULL; + uint16_t tt_tot, tt_count; + ssize_t tt_query_size = sizeof(struct tt_query_packet); + int i; + + if (tt_query_size + tt_len > primary_if->soft_iface->mtu) { + tt_len = primary_if->soft_iface->mtu - tt_query_size; + tt_len -= tt_len % sizeof(struct tt_change); + } + tt_tot = tt_len / sizeof(struct tt_change); + + skb = dev_alloc_skb(tt_query_size + tt_len + ETH_HLEN); + if (!skb) + goto out; + + skb_reserve(skb, ETH_HLEN); + tt_response = (struct tt_query_packet *)skb_put(skb, + tt_query_size + tt_len); + tt_response->ttvn = ttvn; + + tt_change = (struct tt_change *)(skb->data + tt_query_size); + tt_count = 0; + + rcu_read_lock(); + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry_rcu(tt_local_entry, node, + head, hash_entry) { + if (tt_count == tt_tot) + break; + + if ((valid_cb) && (!valid_cb(tt_local_entry, cb_data))) + continue; + + memcpy(tt_change->addr, tt_local_entry->addr, ETH_ALEN); + tt_change->flags = NO_FLAGS; + + tt_count++; + tt_change++; + } + } + rcu_read_unlock(); + + /* store in the message the number of entries we have successfully + * copied */ + tt_response->tt_data = htons(tt_count); + +out: + return skb; +} + +static int send_tt_request(struct bat_priv *bat_priv, + struct orig_node *dst_orig_node, + uint8_t ttvn, uint16_t tt_crc, bool full_table) +{ + struct sk_buff *skb = NULL; + struct tt_query_packet *tt_request; + struct neigh_node *neigh_node = NULL; + struct hard_iface *primary_if; + struct tt_req_node *tt_req_node = NULL; + int ret = 1; + + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + /* The new tt_req will be issued only if I'm not waiting for a + * reply from the same orig_node yet */ + tt_req_node = new_tt_req_node(bat_priv, dst_orig_node); + if (!tt_req_node) + goto out; + + skb = dev_alloc_skb(sizeof(struct tt_query_packet) + ETH_HLEN); + if (!skb) + goto out; + + skb_reserve(skb, ETH_HLEN); + + tt_request = (struct tt_query_packet *)skb_put(skb, + sizeof(struct tt_query_packet)); + + tt_request->packet_type = BAT_TT_QUERY; + tt_request->version = COMPAT_VERSION; + memcpy(tt_request->src, primary_if->net_dev->dev_addr, ETH_ALEN); + memcpy(tt_request->dst, dst_orig_node->orig, ETH_ALEN); + tt_request->ttl = TTL; + tt_request->ttvn = ttvn; + tt_request->tt_data = tt_crc; + tt_request->flags = TT_REQUEST; + + if (full_table) + tt_request->flags |= TT_FULL_TABLE; + + neigh_node = orig_node_get_router(dst_orig_node); + if (!neigh_node) + goto out; + + bat_dbg(DBG_TT, bat_priv, "Sending TT_REQUEST to %pM via %pM " + "[%c]\n", dst_orig_node->orig, neigh_node->addr, + (full_table ? 'F' : '.')); + + send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + ret = 0; + +out: + if (neigh_node) + neigh_node_free_ref(neigh_node); + if (primary_if) + hardif_free_ref(primary_if); + if (ret) + kfree_skb(skb); + if (ret && tt_req_node) { + spin_lock_bh(&bat_priv->tt_req_list_lock); + list_del(&tt_req_node->list); + spin_unlock_bh(&bat_priv->tt_req_list_lock); + kfree(tt_req_node); + } + return ret; +} + +static bool send_other_tt_response(struct bat_priv *bat_priv, + struct tt_query_packet *tt_request) +{ + struct orig_node *req_dst_orig_node = NULL, *res_dst_orig_node = NULL; + struct neigh_node *neigh_node = NULL; + struct hard_iface *primary_if = NULL; + uint8_t orig_ttvn, req_ttvn, ttvn; + int ret = false; + unsigned char *tt_buff; + bool full_table; + uint16_t tt_len, tt_tot; + struct sk_buff *skb = NULL; + struct tt_query_packet *tt_response; + + bat_dbg(DBG_TT, bat_priv, + "Received TT_REQUEST from %pM for " + "ttvn: %u (%pM) [%c]\n", tt_request->src, + tt_request->ttvn, tt_request->dst, + (tt_request->flags & TT_FULL_TABLE ? 'F' : '.')); + + /* Let's get the orig node of the REAL destination */ + req_dst_orig_node = get_orig_node(bat_priv, tt_request->dst); + if (!req_dst_orig_node) + goto out; + + res_dst_orig_node = get_orig_node(bat_priv, tt_request->src); + if (!res_dst_orig_node) + goto out; + + neigh_node = orig_node_get_router(res_dst_orig_node); + if (!neigh_node) + goto out; + + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + orig_ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn); + req_ttvn = tt_request->ttvn; + + /* I don't have the requested data */ + if (orig_ttvn != req_ttvn || + tt_request->tt_data != req_dst_orig_node->tt_crc) + goto out; + + /* If the full table has been explicitly requested */ + if (tt_request->flags & TT_FULL_TABLE || + !req_dst_orig_node->tt_buff) + full_table = true; + else + full_table = false; + + /* In this version, fragmentation is not implemented, then + * I'll send only one packet with as much TT entries as I can */ + if (!full_table) { + spin_lock_bh(&req_dst_orig_node->tt_buff_lock); + tt_len = req_dst_orig_node->tt_buff_len; + tt_tot = tt_len / sizeof(struct tt_change); + + skb = dev_alloc_skb(sizeof(struct tt_query_packet) + + tt_len + ETH_HLEN); + if (!skb) + goto unlock; + + skb_reserve(skb, ETH_HLEN); + tt_response = (struct tt_query_packet *)skb_put(skb, + sizeof(struct tt_query_packet) + tt_len); + tt_response->ttvn = req_ttvn; + tt_response->tt_data = htons(tt_tot); + + tt_buff = skb->data + sizeof(struct tt_query_packet); + /* Copy the last orig_node's OGM buffer */ + memcpy(tt_buff, req_dst_orig_node->tt_buff, + req_dst_orig_node->tt_buff_len); + + spin_unlock_bh(&req_dst_orig_node->tt_buff_lock); + } else { + tt_len = (uint16_t)atomic_read(&req_dst_orig_node->tt_size) * + sizeof(struct tt_change); + ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn); + + skb = tt_response_fill_table(tt_len, ttvn, + bat_priv->tt_global_hash, + primary_if, tt_global_valid_entry, + req_dst_orig_node); + if (!skb) + goto out; + + tt_response = (struct tt_query_packet *)skb->data; + } + + tt_response->packet_type = BAT_TT_QUERY; + tt_response->version = COMPAT_VERSION; + tt_response->ttl = TTL; + memcpy(tt_response->src, req_dst_orig_node->orig, ETH_ALEN); + memcpy(tt_response->dst, tt_request->src, ETH_ALEN); + tt_response->flags = TT_RESPONSE; + + if (full_table) + tt_response->flags |= TT_FULL_TABLE; + + bat_dbg(DBG_TT, bat_priv, + "Sending TT_RESPONSE %pM via %pM for %pM (ttvn: %u)\n", + res_dst_orig_node->orig, neigh_node->addr, + req_dst_orig_node->orig, req_ttvn); + + send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + ret = true; + goto out; + +unlock: + spin_unlock_bh(&req_dst_orig_node->tt_buff_lock); + +out: + if (res_dst_orig_node) + orig_node_free_ref(res_dst_orig_node); + if (req_dst_orig_node) + orig_node_free_ref(req_dst_orig_node); + if (neigh_node) + neigh_node_free_ref(neigh_node); + if (primary_if) + hardif_free_ref(primary_if); + if (!ret) + kfree_skb(skb); + return ret; + +} +static bool send_my_tt_response(struct bat_priv *bat_priv, + struct tt_query_packet *tt_request) +{ + struct orig_node *orig_node = NULL; + struct neigh_node *neigh_node = NULL; + struct hard_iface *primary_if = NULL; + uint8_t my_ttvn, req_ttvn, ttvn; + int ret = false; + unsigned char *tt_buff; + bool full_table; + uint16_t tt_len, tt_tot; + struct sk_buff *skb = NULL; + struct tt_query_packet *tt_response; + + bat_dbg(DBG_TT, bat_priv, + "Received TT_REQUEST from %pM for " + "ttvn: %u (me) [%c]\n", tt_request->src, + tt_request->ttvn, + (tt_request->flags & TT_FULL_TABLE ? 'F' : '.')); + + + my_ttvn = (uint8_t)atomic_read(&bat_priv->ttvn); + req_ttvn = tt_request->ttvn; + + orig_node = get_orig_node(bat_priv, tt_request->src); + if (!orig_node) + goto out; + + neigh_node = orig_node_get_router(orig_node); + if (!neigh_node) + goto out; + + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + /* If the full table has been explicitly requested or the gap + * is too big send the whole local translation table */ + if (tt_request->flags & TT_FULL_TABLE || my_ttvn != req_ttvn || + !bat_priv->tt_buff) + full_table = true; + else + full_table = false; + + /* In this version, fragmentation is not implemented, then + * I'll send only one packet with as much TT entries as I can */ + if (!full_table) { + spin_lock_bh(&bat_priv->tt_buff_lock); + tt_len = bat_priv->tt_buff_len; + tt_tot = tt_len / sizeof(struct tt_change); + + skb = dev_alloc_skb(sizeof(struct tt_query_packet) + + tt_len + ETH_HLEN); + if (!skb) + goto unlock; + + skb_reserve(skb, ETH_HLEN); + tt_response = (struct tt_query_packet *)skb_put(skb, + sizeof(struct tt_query_packet) + tt_len); + tt_response->ttvn = req_ttvn; + tt_response->tt_data = htons(tt_tot); + + tt_buff = skb->data + sizeof(struct tt_query_packet); + memcpy(tt_buff, bat_priv->tt_buff, + bat_priv->tt_buff_len); + spin_unlock_bh(&bat_priv->tt_buff_lock); + } else { + tt_len = (uint16_t)atomic_read(&bat_priv->num_local_tt) * + sizeof(struct tt_change); + ttvn = (uint8_t)atomic_read(&bat_priv->ttvn); + + skb = tt_response_fill_table(tt_len, ttvn, + bat_priv->tt_local_hash, + primary_if, tt_local_valid_entry, + NULL); + if (!skb) + goto out; + + tt_response = (struct tt_query_packet *)skb->data; + } + + tt_response->packet_type = BAT_TT_QUERY; + tt_response->version = COMPAT_VERSION; + tt_response->ttl = TTL; + memcpy(tt_response->src, primary_if->net_dev->dev_addr, ETH_ALEN); + memcpy(tt_response->dst, tt_request->src, ETH_ALEN); + tt_response->flags = TT_RESPONSE; + + if (full_table) + tt_response->flags |= TT_FULL_TABLE; + + bat_dbg(DBG_TT, bat_priv, + "Sending TT_RESPONSE to %pM via %pM [%c]\n", + orig_node->orig, neigh_node->addr, + (tt_response->flags & TT_FULL_TABLE ? 'F' : '.')); + + send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + ret = true; + goto out; + +unlock: + spin_unlock_bh(&bat_priv->tt_buff_lock); +out: + if (orig_node) + orig_node_free_ref(orig_node); + if (neigh_node) + neigh_node_free_ref(neigh_node); + if (primary_if) + hardif_free_ref(primary_if); + if (!ret) + kfree_skb(skb); + /* This packet was for me, so it doesn't need to be re-routed */ + return true; +} + +bool send_tt_response(struct bat_priv *bat_priv, + struct tt_query_packet *tt_request) +{ + if (is_my_mac(tt_request->dst)) + return send_my_tt_response(bat_priv, tt_request); + else + return send_other_tt_response(bat_priv, tt_request); +} + +static void _tt_update_changes(struct bat_priv *bat_priv, + struct orig_node *orig_node, + struct tt_change *tt_change, + uint16_t tt_num_changes, uint8_t ttvn) +{ + int i; + + for (i = 0; i < tt_num_changes; i++) { + if ((tt_change + i)->flags & TT_CLIENT_DEL) + tt_global_del(bat_priv, orig_node, + (tt_change + i)->addr, + "tt removed by changes", + (tt_change + i)->flags & TT_CLIENT_ROAM); + else + if (!tt_global_add(bat_priv, orig_node, + (tt_change + i)->addr, ttvn, false, + (tt_change + i)->flags & + TT_CLIENT_WIFI)) + /* In case of problem while storing a + * global_entry, we stop the updating + * procedure without committing the + * ttvn change. This will avoid to send + * corrupted data on tt_request + */ + return; + } +} + +static void tt_fill_gtable(struct bat_priv *bat_priv, + struct tt_query_packet *tt_response) +{ + struct orig_node *orig_node = NULL; + + orig_node = orig_hash_find(bat_priv, tt_response->src); + if (!orig_node) + goto out; + + /* Purge the old table first.. */ + tt_global_del_orig(bat_priv, orig_node, "Received full table"); + + _tt_update_changes(bat_priv, orig_node, + (struct tt_change *)(tt_response + 1), + tt_response->tt_data, tt_response->ttvn); + + spin_lock_bh(&orig_node->tt_buff_lock); + kfree(orig_node->tt_buff); + orig_node->tt_buff_len = 0; + orig_node->tt_buff = NULL; + spin_unlock_bh(&orig_node->tt_buff_lock); + + atomic_set(&orig_node->last_ttvn, tt_response->ttvn); + +out: + if (orig_node) + orig_node_free_ref(orig_node); +} + +static void tt_update_changes(struct bat_priv *bat_priv, + struct orig_node *orig_node, + uint16_t tt_num_changes, uint8_t ttvn, + struct tt_change *tt_change) +{ + _tt_update_changes(bat_priv, orig_node, tt_change, tt_num_changes, + ttvn); + + tt_save_orig_buffer(bat_priv, orig_node, (unsigned char *)tt_change, + tt_num_changes); + atomic_set(&orig_node->last_ttvn, ttvn); +} + +bool is_my_client(struct bat_priv *bat_priv, const uint8_t *addr) +{ + struct tt_local_entry *tt_local_entry = NULL; + bool ret = false; + + tt_local_entry = tt_local_hash_find(bat_priv, addr); + if (!tt_local_entry) + goto out; + /* Check if the client has been logically deleted (but is kept for + * consistency purpose) */ + if (tt_local_entry->flags & TT_CLIENT_PENDING) + goto out; + ret = true; +out: + if (tt_local_entry) + tt_local_entry_free_ref(tt_local_entry); + return ret; +} + +void handle_tt_response(struct bat_priv *bat_priv, + struct tt_query_packet *tt_response) +{ + struct tt_req_node *node, *safe; + struct orig_node *orig_node = NULL; + + bat_dbg(DBG_TT, bat_priv, "Received TT_RESPONSE from %pM for " + "ttvn %d t_size: %d [%c]\n", + tt_response->src, tt_response->ttvn, + tt_response->tt_data, + (tt_response->flags & TT_FULL_TABLE ? 'F' : '.')); + + orig_node = orig_hash_find(bat_priv, tt_response->src); + if (!orig_node) + goto out; + + if (tt_response->flags & TT_FULL_TABLE) + tt_fill_gtable(bat_priv, tt_response); + else + tt_update_changes(bat_priv, orig_node, tt_response->tt_data, + tt_response->ttvn, + (struct tt_change *)(tt_response + 1)); + + /* Delete the tt_req_node from pending tt_requests list */ + spin_lock_bh(&bat_priv->tt_req_list_lock); + list_for_each_entry_safe(node, safe, &bat_priv->tt_req_list, list) { + if (!compare_eth(node->addr, tt_response->src)) + continue; + list_del(&node->list); + kfree(node); + } + spin_unlock_bh(&bat_priv->tt_req_list_lock); + + /* Recalculate the CRC for this orig_node and store it */ + orig_node->tt_crc = 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) + orig_node_free_ref(orig_node); +} + +int tt_init(struct bat_priv *bat_priv) +{ + if (!tt_local_init(bat_priv)) + return 0; + + if (!tt_global_init(bat_priv)) + return 0; + + tt_start_timer(bat_priv); + + return 1; +} + +static void tt_roam_list_free(struct bat_priv *bat_priv) +{ + struct tt_roam_node *node, *safe; + + spin_lock_bh(&bat_priv->tt_roam_list_lock); + + list_for_each_entry_safe(node, safe, &bat_priv->tt_roam_list, list) { + list_del(&node->list); + kfree(node); + } + + spin_unlock_bh(&bat_priv->tt_roam_list_lock); +} + +static void tt_roam_purge(struct bat_priv *bat_priv) +{ + struct tt_roam_node *node, *safe; + + spin_lock_bh(&bat_priv->tt_roam_list_lock); + list_for_each_entry_safe(node, safe, &bat_priv->tt_roam_list, list) { + if (!is_out_of_time(node->first_time, + ROAMING_MAX_TIME * 1000)) + continue; + + list_del(&node->list); + kfree(node); + } + spin_unlock_bh(&bat_priv->tt_roam_list_lock); +} + +/* This function checks whether the client already reached the + * maximum number of possible roaming phases. In this case the ROAMING_ADV + * will not be sent. + * + * returns true if the ROAMING_ADV can be sent, false otherwise */ +static bool tt_check_roam_count(struct bat_priv *bat_priv, + uint8_t *client) +{ + struct tt_roam_node *tt_roam_node; + bool ret = false; + + spin_lock_bh(&bat_priv->tt_roam_list_lock); + /* The new tt_req will be issued only if I'm not waiting for a + * reply from the same orig_node yet */ + list_for_each_entry(tt_roam_node, &bat_priv->tt_roam_list, list) { + if (!compare_eth(tt_roam_node->addr, client)) + continue; + + if (is_out_of_time(tt_roam_node->first_time, + ROAMING_MAX_TIME * 1000)) + continue; + + if (!atomic_dec_not_zero(&tt_roam_node->counter)) + /* Sorry, you roamed too many times! */ + goto unlock; + ret = true; + break; + } + + if (!ret) { + tt_roam_node = kmalloc(sizeof(*tt_roam_node), GFP_ATOMIC); + if (!tt_roam_node) + goto unlock; + + tt_roam_node->first_time = jiffies; + atomic_set(&tt_roam_node->counter, ROAMING_MAX_COUNT - 1); + memcpy(tt_roam_node->addr, client, ETH_ALEN); + + list_add(&tt_roam_node->list, &bat_priv->tt_roam_list); + ret = true; + } + +unlock: + spin_unlock_bh(&bat_priv->tt_roam_list_lock); + return ret; +} + +void send_roam_adv(struct bat_priv *bat_priv, uint8_t *client, + struct orig_node *orig_node) +{ + struct neigh_node *neigh_node = NULL; + struct sk_buff *skb = NULL; + struct roam_adv_packet *roam_adv_packet; + int ret = 1; + struct hard_iface *primary_if; + + /* before going on we have to check whether the client has + * already roamed to us too many times */ + if (!tt_check_roam_count(bat_priv, client)) + goto out; + + skb = dev_alloc_skb(sizeof(struct roam_adv_packet) + ETH_HLEN); + if (!skb) + goto out; + + skb_reserve(skb, ETH_HLEN); + + roam_adv_packet = (struct roam_adv_packet *)skb_put(skb, + sizeof(struct roam_adv_packet)); + + roam_adv_packet->packet_type = BAT_ROAM_ADV; + roam_adv_packet->version = COMPAT_VERSION; + roam_adv_packet->ttl = TTL; + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + memcpy(roam_adv_packet->src, primary_if->net_dev->dev_addr, ETH_ALEN); + hardif_free_ref(primary_if); + memcpy(roam_adv_packet->dst, orig_node->orig, ETH_ALEN); + memcpy(roam_adv_packet->client, client, ETH_ALEN); + + neigh_node = orig_node_get_router(orig_node); + if (!neigh_node) + goto out; + + bat_dbg(DBG_TT, bat_priv, + "Sending ROAMING_ADV to %pM (client %pM) via %pM\n", + orig_node->orig, client, neigh_node->addr); + + send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + ret = 0; + +out: + if (neigh_node) + neigh_node_free_ref(neigh_node); + if (ret) + kfree_skb(skb); + return; +} + +static void tt_purge(struct work_struct *work) +{ + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct bat_priv *bat_priv = + container_of(delayed_work, struct bat_priv, tt_work); + + tt_local_purge(bat_priv); + tt_global_roam_purge(bat_priv); + tt_req_purge(bat_priv); + tt_roam_purge(bat_priv); + + tt_start_timer(bat_priv); +} + +void tt_free(struct bat_priv *bat_priv) +{ + cancel_delayed_work_sync(&bat_priv->tt_work); + + tt_local_table_free(bat_priv); + tt_global_table_free(bat_priv); + tt_req_list_free(bat_priv); + tt_changes_list_free(bat_priv); + tt_roam_list_free(bat_priv); + + kfree(bat_priv->tt_buff); +} + +/* This function will reset the specified flags from all the entries in + * the given hash table and will increment num_local_tt for each involved + * entry */ +static void tt_local_reset_flags(struct bat_priv *bat_priv, uint16_t flags) +{ + int i; + struct hashtable_t *hash = bat_priv->tt_local_hash; + struct hlist_head *head; + struct hlist_node *node; + struct tt_local_entry *tt_local_entry; + + if (!hash) + return; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tt_local_entry, node, + head, hash_entry) { + if (!(tt_local_entry->flags & flags)) + continue; + tt_local_entry->flags &= ~flags; + atomic_inc(&bat_priv->num_local_tt); + } + rcu_read_unlock(); + } + +} + +/* Purge out all the tt local entries marked with TT_CLIENT_PENDING */ +static void tt_local_purge_pending_clients(struct bat_priv *bat_priv) +{ + struct hashtable_t *hash = bat_priv->tt_local_hash; + struct tt_local_entry *tt_local_entry; + struct hlist_node *node, *node_tmp; + struct hlist_head *head; + spinlock_t *list_lock; /* protects write access to the hash lists */ + int i; + + if (!hash) + return; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + list_lock = &hash->list_locks[i]; + + spin_lock_bh(list_lock); + hlist_for_each_entry_safe(tt_local_entry, node, node_tmp, + head, hash_entry) { + if (!(tt_local_entry->flags & TT_CLIENT_PENDING)) + continue; + + bat_dbg(DBG_TT, bat_priv, "Deleting local tt entry " + "(%pM): pending\n", tt_local_entry->addr); + + atomic_dec(&bat_priv->num_local_tt); + hlist_del_rcu(node); + tt_local_entry_free_ref(tt_local_entry); + } + spin_unlock_bh(list_lock); + } + +} + +void tt_commit_changes(struct bat_priv *bat_priv) +{ + tt_local_reset_flags(bat_priv, TT_CLIENT_NEW); + tt_local_purge_pending_clients(bat_priv); + + /* Increment the TTVN only once per OGM interval */ + atomic_inc(&bat_priv->ttvn); + bat_priv->tt_poss_change = false; +} + +bool is_ap_isolated(struct bat_priv *bat_priv, uint8_t *src, uint8_t *dst) +{ + struct tt_local_entry *tt_local_entry = NULL; + struct tt_global_entry *tt_global_entry = NULL; + bool ret = false; + + if (!atomic_read(&bat_priv->ap_isolation)) + goto out; + + tt_local_entry = tt_local_hash_find(bat_priv, dst); + if (!tt_local_entry) + goto out; + + tt_global_entry = tt_global_hash_find(bat_priv, src); + if (!tt_global_entry) + goto out; + + if (!_is_ap_isolated(tt_local_entry, tt_global_entry)) + goto out; + + ret = true; + +out: + if (tt_global_entry) + tt_global_entry_free_ref(tt_global_entry); + if (tt_local_entry) + tt_local_entry_free_ref(tt_local_entry); + return ret; +} + +void tt_update_orig(struct bat_priv *bat_priv, struct orig_node *orig_node, + const unsigned char *tt_buff, uint8_t tt_num_changes, + uint8_t ttvn, uint16_t tt_crc) +{ + uint8_t orig_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); + bool full_table = true; + + /* the ttvn increased by one -> we can apply the attached changes */ + if (ttvn - orig_ttvn == 1) { + /* the OGM could not contain the changes due to their size or + * because they have already been sent TT_OGM_APPEND_MAX times. + * In this case send a tt request */ + if (!tt_num_changes) { + full_table = false; + goto request_table; + } + + tt_update_changes(bat_priv, orig_node, tt_num_changes, ttvn, + (struct tt_change *)tt_buff); + + /* Even if we received the precomputed crc with the OGM, we + * prefer to recompute it to spot any possible inconsistency + * in the global table */ + orig_node->tt_crc = tt_global_crc(bat_priv, orig_node); + + /* The ttvn alone is not enough to guarantee consistency + * because a single value could represent different states + * (due to the wrap around). Thus a node has to check whether + * the resulting table (after applying the changes) is still + * consistent or not. E.g. a node could disconnect while its + * ttvn is X and reconnect on ttvn = X + TTVN_MAX: in this case + * checking the CRC value is mandatory to detect the + * inconsistency */ + 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 */ + if (ttvn != orig_ttvn || orig_node->tt_crc != tt_crc) { +request_table: + bat_dbg(DBG_TT, bat_priv, "TT inconsistency for %pM. " + "Need to retrieve the correct information " + "(ttvn: %u last_ttvn: %u crc: %u last_crc: " + "%u num_changes: %u)\n", orig_node->orig, ttvn, + orig_ttvn, tt_crc, orig_node->tt_crc, + tt_num_changes); + send_tt_request(bat_priv, orig_node, ttvn, tt_crc, + full_table); + return; + } + } +} diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h index 46152c3..30efd49 100644 --- a/net/batman-adv/translation-table.h +++ b/net/batman-adv/translation-table.h @@ -22,22 +22,44 @@ #ifndef _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ #define _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ -int tt_local_init(struct bat_priv *bat_priv); -void tt_local_add(struct net_device *soft_iface, uint8_t *addr); +int tt_len(int changes_num); +int tt_changes_fill_buffer(struct bat_priv *bat_priv, + unsigned char *buff, int buff_len); +int tt_init(struct bat_priv *bat_priv); +void tt_local_add(struct net_device *soft_iface, const uint8_t *addr, + int ifindex); void tt_local_remove(struct bat_priv *bat_priv, - uint8_t *addr, char *message); -int tt_local_fill_buffer(struct bat_priv *bat_priv, - unsigned char *buff, int buff_len); + const uint8_t *addr, const char *message, bool roaming); int tt_local_seq_print_text(struct seq_file *seq, void *offset); -void tt_local_free(struct bat_priv *bat_priv); -int tt_global_init(struct bat_priv *bat_priv); -void tt_global_add_orig(struct bat_priv *bat_priv, - struct orig_node *orig_node, - unsigned char *tt_buff, int tt_buff_len); +void tt_global_add_orig(struct bat_priv *bat_priv, struct orig_node *orig_node, + const unsigned char *tt_buff, int tt_buff_len); +int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node, + const unsigned char *addr, uint8_t ttvn, bool roaming, + bool wifi); int tt_global_seq_print_text(struct seq_file *seq, void *offset); void tt_global_del_orig(struct bat_priv *bat_priv, - struct orig_node *orig_node, char *message); -void tt_global_free(struct bat_priv *bat_priv); -struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr); + struct orig_node *orig_node, const char *message); +void tt_global_del(struct bat_priv *bat_priv, + struct orig_node *orig_node, const unsigned char *addr, + const char *message, bool roaming); +struct orig_node *transtable_search(struct bat_priv *bat_priv, + const uint8_t *src, const uint8_t *addr); +void tt_save_orig_buffer(struct bat_priv *bat_priv, struct orig_node *orig_node, + const unsigned char *tt_buff, uint8_t tt_num_changes); +uint16_t tt_local_crc(struct bat_priv *bat_priv); +uint16_t tt_global_crc(struct bat_priv *bat_priv, struct orig_node *orig_node); +void tt_free(struct bat_priv *bat_priv); +bool send_tt_response(struct bat_priv *bat_priv, + struct tt_query_packet *tt_request); +bool is_my_client(struct bat_priv *bat_priv, const uint8_t *addr); +void handle_tt_response(struct bat_priv *bat_priv, + struct tt_query_packet *tt_response); +void send_roam_adv(struct bat_priv *bat_priv, uint8_t *client, + struct orig_node *orig_node); +void tt_commit_changes(struct bat_priv *bat_priv); +bool is_ap_isolated(struct bat_priv *bat_priv, uint8_t *src, uint8_t *dst); +void tt_update_orig(struct bat_priv *bat_priv, struct orig_node *orig_node, + const unsigned char *tt_buff, uint8_t tt_num_changes, + uint8_t ttvn, uint16_t tt_crc); #endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */ diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index fab70e8..ab8d0fe 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -57,7 +57,7 @@ struct hard_iface { * @batman_seqno_reset: time when the batman seqno window was reset * @gw_flags: flags related to gateway class * @flags: for now only VIS_SERVER flag - * @last_real_seqno: last and best known squence number + * @last_real_seqno: last and best known sequence number * @last_ttl: ttl of last received packet * @last_bcast_seqno: last broadcast sequence number received by this host * @@ -75,8 +75,18 @@ struct orig_node { unsigned long batman_seqno_reset; uint8_t gw_flags; uint8_t flags; + atomic_t last_ttvn; /* last seen translation table version number */ + uint16_t tt_crc; unsigned char *tt_buff; int16_t tt_buff_len; + spinlock_t tt_buff_lock; /* protects tt_buff */ + atomic_t tt_size; + /* 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; unsigned long bcast_bits[NUM_WORDS]; @@ -94,6 +104,7 @@ struct orig_node { spinlock_t ogm_cnt_lock; /* bcast_seqno_lock protects bcast_bits, last_bcast_seqno */ spinlock_t bcast_seqno_lock; + spinlock_t tt_list_lock; /* protects tt_list */ atomic_t bond_candidates; struct list_head bond_list; }; @@ -135,6 +146,7 @@ struct bat_priv { atomic_t aggregated_ogms; /* boolean */ atomic_t bonding; /* boolean */ atomic_t fragmentation; /* boolean */ + atomic_t ap_isolation; /* boolean */ atomic_t vis_mode; /* VIS_TYPE_* */ atomic_t gw_mode; /* GW_MODE_* */ atomic_t gw_sel_class; /* uint */ @@ -145,6 +157,15 @@ struct bat_priv { atomic_t bcast_seqno; atomic_t bcast_queue_left; atomic_t batman_queue_left; + atomic_t ttvn; /* translation table version number */ + atomic_t tt_ogm_append_cnt; + atomic_t tt_local_changes; /* changes registered in a OGM interval */ + /* The tt_poss_change flag is used to detect an ongoing roaming phase. + * If true, then I received a Roaming_adv and I have to inspect every + * packet directed to me to check whether I am still the true + * destination or not. This flag will be reset to false as soon as I + * increase my TTVN */ + bool tt_poss_change; char num_ifaces; struct debug_log *debug_log; struct kobject *mesh_obj; @@ -153,26 +174,35 @@ struct bat_priv { struct hlist_head forw_bcast_list; struct hlist_head gw_list; struct hlist_head softif_neigh_vids; + struct list_head tt_changes_list; /* tracks changes in a OGM int */ struct list_head vis_send_list; struct hashtable_t *orig_hash; struct hashtable_t *tt_local_hash; struct hashtable_t *tt_global_hash; + struct list_head tt_req_list; /* list of pending tt_requests */ + struct list_head tt_roam_list; struct hashtable_t *vis_hash; spinlock_t forw_bat_list_lock; /* protects forw_bat_list */ spinlock_t forw_bcast_list_lock; /* protects */ - spinlock_t tt_lhash_lock; /* protects tt_local_hash */ - spinlock_t tt_ghash_lock; /* protects tt_global_hash */ + spinlock_t tt_changes_list_lock; /* protects tt_changes */ + spinlock_t tt_req_list_lock; /* protects tt_req_list */ + spinlock_t tt_roam_list_lock; /* protects tt_roam_list */ spinlock_t gw_list_lock; /* protects gw_list and curr_gw */ spinlock_t vis_hash_lock; /* protects vis_hash */ spinlock_t vis_list_lock; /* protects vis_info::recv_list */ spinlock_t softif_neigh_lock; /* protects soft-interface neigh list */ spinlock_t softif_neigh_vid_lock; /* protects soft-interface vid list */ - int16_t num_local_tt; - atomic_t tt_local_changed; + atomic_t num_local_tt; + /* Checksum of the local table, recomputed before sending a new OGM */ + atomic_t tt_crc; + unsigned char *tt_buff; + int16_t tt_buff_len; + spinlock_t tt_buff_lock; /* protects tt_buff */ struct delayed_work tt_work; struct delayed_work orig_work; struct delayed_work vis_work; struct gw_node __rcu *curr_gw; /* rcu protected pointer */ + atomic_t gw_reselect; struct hard_iface __rcu *primary_if; /* rcu protected pointer */ struct vis_info *my_vis_info; }; @@ -194,15 +224,40 @@ struct socket_packet { struct tt_local_entry { uint8_t addr[ETH_ALEN]; - unsigned long last_seen; - char never_purge; struct hlist_node hash_entry; + unsigned long last_seen; + uint16_t flags; + atomic_t refcount; + struct rcu_head rcu; }; struct tt_global_entry { uint8_t addr[ETH_ALEN]; + struct hlist_node hash_entry; /* entry in the global table */ struct orig_node *orig_node; - struct hlist_node hash_entry; + uint8_t ttvn; + uint16_t flags; /* only TT_GLOBAL_ROAM is used */ + unsigned long roam_at; /* time at which TT_GLOBAL_ROAM was set */ + atomic_t refcount; + struct rcu_head rcu; +}; + +struct tt_change_node { + struct list_head list; + struct tt_change change; +}; + +struct tt_req_node { + uint8_t addr[ETH_ALEN]; + unsigned long issued_at; + struct list_head list; +}; + +struct tt_roam_node { + uint8_t addr[ETH_ALEN]; + atomic_t counter; + unsigned long first_time; + struct list_head list; }; /** @@ -246,10 +301,10 @@ struct frag_packet_list_entry { }; struct vis_info { - unsigned long first_seen; - struct list_head recv_list; - /* list of server-neighbors we received a vis-packet - * from. we should not reply to them. */ + unsigned long first_seen; + /* list of server-neighbors we received a vis-packet + * from. we should not reply to them. */ + struct list_head recv_list; struct list_head send_list; struct kref refcount; struct hlist_node hash_entry; diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c index 19c3daf..07d1c1d 100644 --- a/net/batman-adv/unicast.c +++ b/net/batman-adv/unicast.c @@ -39,8 +39,8 @@ static struct sk_buff *frag_merge_packet(struct list_head *head, (struct unicast_frag_packet *)skb->data; struct sk_buff *tmp_skb; struct unicast_packet *unicast_packet; - int hdr_len = sizeof(struct unicast_packet); - int uni_diff = sizeof(struct unicast_frag_packet) - hdr_len; + int hdr_len = sizeof(*unicast_packet); + int uni_diff = sizeof(*up) - hdr_len; /* set skb to the first part and tmp_skb to the second part */ if (up->flags & UNI_FRAG_HEAD) { @@ -53,7 +53,7 @@ static struct sk_buff *frag_merge_packet(struct list_head *head, if (skb_linearize(skb) < 0 || skb_linearize(tmp_skb) < 0) goto err; - skb_pull(tmp_skb, sizeof(struct unicast_frag_packet)); + skb_pull(tmp_skb, sizeof(*up)); if (pskb_expand_head(skb, 0, tmp_skb->len, GFP_ATOMIC) < 0) goto err; @@ -99,8 +99,7 @@ static int frag_create_buffer(struct list_head *head) struct frag_packet_list_entry *tfp; for (i = 0; i < FRAG_BUFFER_SIZE; i++) { - tfp = kmalloc(sizeof(struct frag_packet_list_entry), - GFP_ATOMIC); + tfp = kmalloc(sizeof(*tfp), GFP_ATOMIC); if (!tfp) { frag_list_free(head); return -ENOMEM; @@ -115,7 +114,7 @@ static int frag_create_buffer(struct list_head *head) } static struct frag_packet_list_entry *frag_search_packet(struct list_head *head, - struct unicast_frag_packet *up) + const struct unicast_frag_packet *up) { struct frag_packet_list_entry *tfp; struct unicast_frag_packet *tmp_up = NULL; @@ -218,14 +217,14 @@ out: } int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, - struct hard_iface *hard_iface, uint8_t dstaddr[]) + struct hard_iface *hard_iface, const uint8_t dstaddr[]) { struct unicast_packet tmp_uc, *unicast_packet; struct hard_iface *primary_if; struct sk_buff *frag_skb; struct unicast_frag_packet *frag1, *frag2; - int uc_hdr_len = sizeof(struct unicast_packet); - int ucf_hdr_len = sizeof(struct unicast_frag_packet); + int uc_hdr_len = sizeof(*unicast_packet); + int ucf_hdr_len = sizeof(*frag1); int data_len = skb->len - uc_hdr_len; int large_tail = 0, ret = NET_RX_DROP; uint16_t seqno; @@ -250,14 +249,14 @@ int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, frag1 = (struct unicast_frag_packet *)skb->data; frag2 = (struct unicast_frag_packet *)frag_skb->data; - memcpy(frag1, &tmp_uc, sizeof(struct unicast_packet)); + memcpy(frag1, &tmp_uc, sizeof(tmp_uc)); frag1->ttl--; frag1->version = COMPAT_VERSION; frag1->packet_type = BAT_UNICAST_FRAG; memcpy(frag1->orig, primary_if->net_dev->dev_addr, ETH_ALEN); - memcpy(frag2, frag1, sizeof(struct unicast_frag_packet)); + memcpy(frag2, frag1, sizeof(*frag2)); if (data_len & 1) large_tail = UNI_FRAG_LARGETAIL; @@ -295,13 +294,15 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) /* get routing information */ if (is_multicast_ether_addr(ethhdr->h_dest)) { - orig_node = (struct orig_node *)gw_get_selected_orig(bat_priv); + orig_node = gw_get_selected_orig(bat_priv); if (orig_node) goto find_router; } - /* check for tt host - increases orig_node refcount */ - orig_node = transtable_search(bat_priv, ethhdr->h_dest); + /* check for tt host - increases orig_node refcount. + * returns NULL in case of AP isolation */ + orig_node = transtable_search(bat_priv, ethhdr->h_source, + ethhdr->h_dest); find_router: /** @@ -314,10 +315,7 @@ find_router: if (!neigh_node) goto out; - if (neigh_node->if_incoming->if_status != IF_ACTIVE) - goto out; - - if (my_skb_head_push(skb, sizeof(struct unicast_packet)) < 0) + if (my_skb_head_push(skb, sizeof(*unicast_packet)) < 0) goto out; unicast_packet = (struct unicast_packet *)skb->data; @@ -329,9 +327,12 @@ find_router: unicast_packet->ttl = 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); if (atomic_read(&bat_priv->fragmentation) && - data_len + sizeof(struct unicast_packet) > + data_len + sizeof(*unicast_packet) > neigh_node->if_incoming->net_dev->mtu) { /* send frag skb decreases ttl */ unicast_packet->ttl++; diff --git a/net/batman-adv/unicast.h b/net/batman-adv/unicast.h index 16ad7a9..8fd5535 100644 --- a/net/batman-adv/unicast.h +++ b/net/batman-adv/unicast.h @@ -24,7 +24,7 @@ #include "packet.h" -#define FRAG_TIMEOUT 10000 /* purge frag list entrys after time in ms */ +#define FRAG_TIMEOUT 10000 /* purge frag list entries after time in ms */ #define FRAG_BUFFER_SIZE 6 /* number of list elements in buffer */ int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv, @@ -32,11 +32,11 @@ int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv, void frag_list_free(struct list_head *head); int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv); int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, - struct hard_iface *hard_iface, uint8_t dstaddr[]); + struct hard_iface *hard_iface, const uint8_t dstaddr[]); -static inline int frag_can_reassemble(struct sk_buff *skb, int mtu) +static inline int frag_can_reassemble(const struct sk_buff *skb, int mtu) { - struct unicast_frag_packet *unicast_packet; + const struct unicast_frag_packet *unicast_packet; int uneven_correction = 0; unsigned int merged_size; @@ -49,7 +49,7 @@ static inline int frag_can_reassemble(struct sk_buff *skb, int mtu) uneven_correction = -1; } - merged_size = (skb->len - sizeof(struct unicast_frag_packet)) * 2; + merged_size = (skb->len - sizeof(*unicast_packet)) * 2; merged_size += sizeof(struct unicast_packet) + uneven_correction; return merged_size <= mtu; diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index c39f20c..f81a6b6 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@ -30,22 +30,6 @@ #define MAX_VIS_PACKET_SIZE 1000 -/* Returns the smallest signed integer in two's complement with the sizeof x */ -#define smallest_signed_int(x) (1u << (7u + 8u * (sizeof(x) - 1u))) - -/* Checks if a sequence number x is a predecessor/successor of y. - * they handle overflows/underflows and can correctly check for a - * predecessor/successor unless the variable sequence number has grown by - * more then 2**(bitwidth(x)-1)-1. - * This means that for a uint8_t with the maximum value 255, it would think: - * - when adding nothing - it is neither a predecessor nor a successor - * - before adding more than 127 to the starting value - it is a predecessor, - * - when adding 128 - it is neither a predecessor nor a successor, - * - after adding more than 127 to the starting value - it is a successor */ -#define seq_before(x, y) ({typeof(x) _dummy = (x - y); \ - _dummy > smallest_signed_int(_dummy); }) -#define seq_after(x, y) seq_before(y, x) - static void start_vis_timer(struct bat_priv *bat_priv); /* free the info */ @@ -68,10 +52,10 @@ static void free_info(struct kref *ref) } /* Compare two vis packets, used by the hashing algorithm */ -static int vis_info_cmp(struct hlist_node *node, void *data2) +static int vis_info_cmp(const struct hlist_node *node, const void *data2) { - struct vis_info *d1, *d2; - struct vis_packet *p1, *p2; + const struct vis_info *d1, *d2; + const struct vis_packet *p1, *p2; d1 = container_of(node, struct vis_info, hash_entry); d2 = data2; @@ -82,11 +66,11 @@ static int vis_info_cmp(struct hlist_node *node, void *data2) /* hash function to choose an entry in a hash table of given size */ /* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ -static int vis_info_choose(void *data, int size) +static int vis_info_choose(const void *data, int size) { - struct vis_info *vis_info = data; - struct vis_packet *packet; - unsigned char *key; + const struct vis_info *vis_info = data; + const struct vis_packet *packet; + const unsigned char *key; uint32_t hash = 0; size_t i; @@ -106,7 +90,7 @@ static int vis_info_choose(void *data, int size) } static struct vis_info *vis_hash_find(struct bat_priv *bat_priv, - void *data) + const void *data) { struct hashtable_t *hash = bat_priv->vis_hash; struct hlist_head *head; @@ -143,11 +127,11 @@ static void vis_data_insert_interface(const uint8_t *interface, struct hlist_node *pos; hlist_for_each_entry(entry, pos, if_list, list) { - if (compare_eth(entry->addr, (void *)interface)) + if (compare_eth(entry->addr, interface)) return; } - /* its a new address, add it to the list */ + /* it's a new address, add it to the list */ entry = kmalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) return; @@ -156,7 +140,8 @@ static void vis_data_insert_interface(const uint8_t *interface, hlist_add_head(&entry->list, if_list); } -static ssize_t vis_data_read_prim_sec(char *buff, struct hlist_head *if_list) +static ssize_t vis_data_read_prim_sec(char *buff, + const struct hlist_head *if_list) { struct if_list_entry *entry; struct hlist_node *pos; @@ -189,8 +174,9 @@ static size_t vis_data_count_prim_sec(struct hlist_head *if_list) } /* read an entry */ -static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry, - uint8_t *src, bool primary) +static ssize_t vis_data_read_entry(char *buff, + const struct vis_info_entry *entry, + const uint8_t *src, bool primary) { /* maximal length: max(4+17+2, 3+17+1+3+2) == 26 */ if (primary && entry->quality == 0) @@ -239,7 +225,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) hlist_for_each_entry_rcu(info, node, head, hash_entry) { packet = (struct vis_packet *)info->skb_packet->data; entries = (struct vis_info_entry *) - ((char *)packet + sizeof(struct vis_packet)); + ((char *)packet + sizeof(*packet)); for (j = 0; j < packet->entries; j++) { if (entries[j].quality == 0) @@ -287,7 +273,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) hlist_for_each_entry_rcu(info, node, head, hash_entry) { packet = (struct vis_packet *)info->skb_packet->data; entries = (struct vis_info_entry *) - ((char *)packet + sizeof(struct vis_packet)); + ((char *)packet + sizeof(*packet)); for (j = 0; j < packet->entries; j++) { if (entries[j].quality == 0) @@ -361,11 +347,11 @@ static void send_list_del(struct vis_info *info) /* tries to add one entry to the receive list. */ static void recv_list_add(struct bat_priv *bat_priv, - struct list_head *recv_list, char *mac) + struct list_head *recv_list, const char *mac) { struct recvlist_node *entry; - entry = kmalloc(sizeof(struct recvlist_node), GFP_ATOMIC); + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) return; @@ -377,9 +363,9 @@ static void recv_list_add(struct bat_priv *bat_priv, /* returns 1 if this mac is in the recv_list */ static int recv_list_is_in(struct bat_priv *bat_priv, - struct list_head *recv_list, char *mac) + const struct list_head *recv_list, const char *mac) { - struct recvlist_node *entry; + const struct recvlist_node *entry; spin_lock_bh(&bat_priv->vis_list_lock); list_for_each_entry(entry, recv_list, list) { @@ -412,11 +398,11 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv, return NULL; /* see if the packet is already in vis_hash */ - search_elem.skb_packet = dev_alloc_skb(sizeof(struct vis_packet)); + search_elem.skb_packet = dev_alloc_skb(sizeof(*search_packet)); if (!search_elem.skb_packet) return NULL; search_packet = (struct vis_packet *)skb_put(search_elem.skb_packet, - sizeof(struct vis_packet)); + sizeof(*search_packet)); memcpy(search_packet->vis_orig, vis_packet->vis_orig, ETH_ALEN); old_info = vis_hash_find(bat_priv, &search_elem); @@ -442,27 +428,26 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv, kref_put(&old_info->refcount, free_info); } - info = kmalloc(sizeof(struct vis_info), GFP_ATOMIC); + info = kmalloc(sizeof(*info), GFP_ATOMIC); if (!info) return NULL; - info->skb_packet = dev_alloc_skb(sizeof(struct vis_packet) + - vis_info_len + sizeof(struct ethhdr)); + info->skb_packet = dev_alloc_skb(sizeof(*packet) + vis_info_len + + sizeof(struct ethhdr)); if (!info->skb_packet) { kfree(info); return NULL; } skb_reserve(info->skb_packet, sizeof(struct ethhdr)); - packet = (struct vis_packet *)skb_put(info->skb_packet, - sizeof(struct vis_packet) + - vis_info_len); + packet = (struct vis_packet *)skb_put(info->skb_packet, sizeof(*packet) + + vis_info_len); kref_init(&info->refcount); INIT_LIST_HEAD(&info->send_list); INIT_LIST_HEAD(&info->recv_list); info->first_seen = jiffies; info->bat_priv = bat_priv; - memcpy(packet, vis_packet, sizeof(struct vis_packet) + vis_info_len); + memcpy(packet, vis_packet, sizeof(*packet) + vis_info_len); /* initialize and add new packet. */ *is_new = 1; @@ -480,7 +465,7 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv, /* try to add it */ hash_added = hash_add(bat_priv->vis_hash, vis_info_cmp, vis_info_choose, info, &info->hash_entry); - if (hash_added < 0) { + if (hash_added != 0) { /* did not work (for some reason) */ kref_put(&info->refcount, free_info); info = NULL; @@ -599,9 +584,9 @@ static int find_best_vis_server(struct bat_priv *bat_priv, } /* Return true if the vis packet is full. */ -static bool vis_packet_full(struct vis_info *info) +static bool vis_packet_full(const struct vis_info *info) { - struct vis_packet *packet; + const struct vis_packet *packet; packet = (struct vis_packet *)info->skb_packet->data; if (MAX_VIS_PACKET_SIZE / sizeof(struct vis_info_entry) @@ -619,7 +604,7 @@ static int generate_vis_packet(struct bat_priv *bat_priv) struct hlist_head *head; struct orig_node *orig_node; struct neigh_node *router; - struct vis_info *info = (struct vis_info *)bat_priv->my_vis_info; + struct vis_info *info = bat_priv->my_vis_info; struct vis_packet *packet = (struct vis_packet *)info->skb_packet->data; struct vis_info_entry *entry; struct tt_local_entry *tt_local_entry; @@ -632,7 +617,7 @@ static int generate_vis_packet(struct bat_priv *bat_priv) packet->ttl = TTL; packet->seqno = htonl(ntohl(packet->seqno) + 1); packet->entries = 0; - skb_trim(info->skb_packet, sizeof(struct vis_packet)); + skb_trim(info->skb_packet, sizeof(*packet)); if (packet->vis_type == VIS_TYPE_CLIENT_UPDATE) { best_tq = find_best_vis_server(bat_priv, info); @@ -680,11 +665,12 @@ next: hash = bat_priv->tt_local_hash; - spin_lock_bh(&bat_priv->tt_lhash_lock); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(tt_local_entry, node, head, hash_entry) { + rcu_read_lock(); + hlist_for_each_entry_rcu(tt_local_entry, node, head, + hash_entry) { entry = (struct vis_info_entry *) skb_put(info->skb_packet, sizeof(*entry)); @@ -693,14 +679,12 @@ next: entry->quality = 0; /* 0 means TT */ packet->entries++; - if (vis_packet_full(info)) { - spin_unlock_bh(&bat_priv->tt_lhash_lock); - return 0; - } + if (vis_packet_full(info)) + goto unlock; } + rcu_read_unlock(); } - spin_unlock_bh(&bat_priv->tt_lhash_lock); return 0; unlock: @@ -903,22 +887,18 @@ int vis_init(struct bat_priv *bat_priv) } bat_priv->my_vis_info = kmalloc(MAX_VIS_PACKET_SIZE, GFP_ATOMIC); - if (!bat_priv->my_vis_info) { - pr_err("Can't initialize vis packet\n"); + if (!bat_priv->my_vis_info) goto err; - } - bat_priv->my_vis_info->skb_packet = dev_alloc_skb( - sizeof(struct vis_packet) + - MAX_VIS_PACKET_SIZE + - sizeof(struct ethhdr)); + bat_priv->my_vis_info->skb_packet = dev_alloc_skb(sizeof(*packet) + + MAX_VIS_PACKET_SIZE + + sizeof(struct ethhdr)); if (!bat_priv->my_vis_info->skb_packet) goto free_info; skb_reserve(bat_priv->my_vis_info->skb_packet, sizeof(struct ethhdr)); - packet = (struct vis_packet *)skb_put( - bat_priv->my_vis_info->skb_packet, - sizeof(struct vis_packet)); + packet = (struct vis_packet *)skb_put(bat_priv->my_vis_info->skb_packet, + sizeof(*packet)); /* prefill the vis info */ bat_priv->my_vis_info->first_seen = jiffies - @@ -938,7 +918,7 @@ int vis_init(struct bat_priv *bat_priv) hash_added = hash_add(bat_priv->vis_hash, vis_info_cmp, vis_info_choose, bat_priv->my_vis_info, &bat_priv->my_vis_info->hash_entry); - if (hash_added < 0) { + if (hash_added != 0) { pr_err("Can't add own vis packet into hash\n"); /* not in hash, need to remove it manually. */ kref_put(&bat_priv->my_vis_info->refcount, free_info); diff --git a/net/bluetooth_tizen/Kconfig b/net/bluetooth_tizen/Kconfig deleted file mode 100644 index 6d17deb..0000000 --- a/net/bluetooth_tizen/Kconfig +++ /dev/null @@ -1,49 +0,0 @@ -# -# Bluetooth subsystem configuration -# - -menuconfig BT - tristate "Bluetooth subsystem support(Tizen)" - depends on NET && !S390 - depends on RFKILL || !RFKILL - select CRC16 - select CRYPTO - select CRYPTO_BLKCIPHER - select CRYPTO_AES - select CRYPTO_ECB - help - Bluetooth is low-cost, low-power, short-range wireless technology. - It was designed as a replacement for cables and other short-range - technologies like IrDA. Bluetooth operates in personal area range - that typically extends up to 10 meters. More information about - Bluetooth can be found at <http://www.bluetooth.com/>. - - Linux Bluetooth subsystem consist of several layers: - Bluetooth Core - HCI device and connection manager, scheduler - SCO audio links - L2CAP (Logical Link Control and Adaptation Protocol) - SMP (Security Manager Protocol) on LE (Low Energy) links - HCI Device drivers (Interface to the hardware) - RFCOMM Module (RFCOMM Protocol) - BNEP Module (Bluetooth Network Encapsulation Protocol) - CMTP Module (CAPI Message Transport Protocol) - HIDP Module (Human Interface Device Protocol) - - Say Y here to compile Bluetooth support into the kernel or say M to - compile it as module (bluetooth). - - To use Linux Bluetooth subsystem, you will need several user-space - utilities like hciconfig and bluetoothd. These utilities and updates - to Bluetooth kernel modules are provided in the BlueZ packages. For - more information, see <http://www.bluez.org/>. - -source "net/bluetooth_tizen/rfcomm/Kconfig" - -source "net/bluetooth_tizen/bnep/Kconfig" - -source "net/bluetooth_tizen/cmtp/Kconfig" - -source "net/bluetooth_tizen/hidp/Kconfig" - -source "drivers/bluetooth_tizen/Kconfig" diff --git a/net/bluetooth_tizen/Makefile b/net/bluetooth_tizen/Makefile deleted file mode 100644 index 2dc5a57..0000000 --- a/net/bluetooth_tizen/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# -# Makefile for the Linux Bluetooth subsystem. -# - -obj-$(CONFIG_BT) += bluetooth.o -obj-$(CONFIG_BT_RFCOMM) += rfcomm/ -obj-$(CONFIG_BT_BNEP) += bnep/ -obj-$(CONFIG_BT_CMTP) += cmtp/ -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 diff --git a/net/bluetooth_tizen/af_bluetooth.c b/net/bluetooth_tizen/af_bluetooth.c deleted file mode 100644 index 6fb68a9..0000000 --- a/net/bluetooth_tizen/af_bluetooth.c +++ /dev/null @@ -1,616 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth address family and sockets. */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/list.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/skbuff.h> -#include <linux/init.h> -#include <linux/poll.h> -#include <net/sock.h> -#include <asm/ioctls.h> -#include <linux/kmod.h> - -#include <net/bluetooth/bluetooth.h> - -#define VERSION "2.16" - -/* Bluetooth sockets */ -#define BT_MAX_PROTO 8 -static const struct net_proto_family *bt_proto[BT_MAX_PROTO]; -static DEFINE_RWLOCK(bt_proto_lock); - -static struct lock_class_key bt_lock_key[BT_MAX_PROTO]; -static const char *const bt_key_strings[BT_MAX_PROTO] = { - "sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP", - "sk_lock-AF_BLUETOOTH-BTPROTO_HCI", - "sk_lock-AF_BLUETOOTH-BTPROTO_SCO", - "sk_lock-AF_BLUETOOTH-BTPROTO_RFCOMM", - "sk_lock-AF_BLUETOOTH-BTPROTO_BNEP", - "sk_lock-AF_BLUETOOTH-BTPROTO_CMTP", - "sk_lock-AF_BLUETOOTH-BTPROTO_HIDP", - "sk_lock-AF_BLUETOOTH-BTPROTO_AVDTP", -}; - -static struct lock_class_key bt_slock_key[BT_MAX_PROTO]; -static const char *const bt_slock_key_strings[BT_MAX_PROTO] = { - "slock-AF_BLUETOOTH-BTPROTO_L2CAP", - "slock-AF_BLUETOOTH-BTPROTO_HCI", - "slock-AF_BLUETOOTH-BTPROTO_SCO", - "slock-AF_BLUETOOTH-BTPROTO_RFCOMM", - "slock-AF_BLUETOOTH-BTPROTO_BNEP", - "slock-AF_BLUETOOTH-BTPROTO_CMTP", - "slock-AF_BLUETOOTH-BTPROTO_HIDP", - "slock-AF_BLUETOOTH-BTPROTO_AVDTP", -}; - -void bt_sock_reclassify_lock(struct sock *sk, int proto) -{ - BUG_ON(!sk); - BUG_ON(sock_owned_by_user(sk)); - - sock_lock_init_class_and_name(sk, - bt_slock_key_strings[proto], &bt_slock_key[proto], - bt_key_strings[proto], &bt_lock_key[proto]); -} -EXPORT_SYMBOL(bt_sock_reclassify_lock); - -int bt_sock_register(int proto, const struct net_proto_family *ops) -{ - int err = 0; - - if (proto < 0 || proto >= BT_MAX_PROTO) - return -EINVAL; - - write_lock(&bt_proto_lock); - - if (bt_proto[proto]) - err = -EEXIST; - else - bt_proto[proto] = ops; - - write_unlock(&bt_proto_lock); - - return err; -} -EXPORT_SYMBOL(bt_sock_register); - -int bt_sock_unregister(int proto) -{ - int err = 0; - - if (proto < 0 || proto >= BT_MAX_PROTO) - return -EINVAL; - - write_lock(&bt_proto_lock); - - if (!bt_proto[proto]) - err = -ENOENT; - else - bt_proto[proto] = NULL; - - write_unlock(&bt_proto_lock); - - return err; -} -EXPORT_SYMBOL(bt_sock_unregister); - -static int bt_sock_create(struct net *net, struct socket *sock, int proto, - int kern) -{ - int err; - - if (net != &init_net) - return -EAFNOSUPPORT; - - if (proto < 0 || proto >= BT_MAX_PROTO) - return -EINVAL; - - if (!bt_proto[proto]) - request_module("bt-proto-%d", proto); - - err = -EPROTONOSUPPORT; - - read_lock(&bt_proto_lock); - - if (bt_proto[proto] && try_module_get(bt_proto[proto]->owner)) { - err = bt_proto[proto]->create(net, sock, proto, kern); - if (!err) - bt_sock_reclassify_lock(sock->sk, proto); - module_put(bt_proto[proto]->owner); - } - - read_unlock(&bt_proto_lock); - - return err; -} - -void bt_sock_link(struct bt_sock_list *l, struct sock *sk) -{ - write_lock(&l->lock); - sk_add_node(sk, &l->head); - write_unlock(&l->lock); -} -EXPORT_SYMBOL(bt_sock_link); - -void bt_sock_unlink(struct bt_sock_list *l, struct sock *sk) -{ - write_lock(&l->lock); - sk_del_node_init(sk); - write_unlock(&l->lock); -} -EXPORT_SYMBOL(bt_sock_unlink); - -void bt_accept_enqueue(struct sock *parent, struct sock *sk) -{ - BT_DBG("parent %p, sk %p", parent, sk); - - sock_hold(sk); - list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q); - bt_sk(sk)->parent = parent; - parent->sk_ack_backlog++; -} -EXPORT_SYMBOL(bt_accept_enqueue); - -void bt_accept_unlink(struct sock *sk) -{ - BT_DBG("sk %p state %d", sk, sk->sk_state); - - list_del_init(&bt_sk(sk)->accept_q); - bt_sk(sk)->parent->sk_ack_backlog--; - bt_sk(sk)->parent = NULL; - sock_put(sk); -} -EXPORT_SYMBOL(bt_accept_unlink); - -struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) -{ - struct list_head *p, *n; - struct sock *sk; - - BT_DBG("parent %p", parent); - - list_for_each_safe(p, n, &bt_sk(parent)->accept_q) { - sk = (struct sock *) list_entry(p, struct bt_sock, accept_q); - - lock_sock(sk); - - /* FIXME: Is this check still needed */ - if (sk->sk_state == BT_CLOSED) { - release_sock(sk); - bt_accept_unlink(sk); - continue; - } - - if (sk->sk_state == BT_CONNECTED || !newsock || - bt_sk(parent)->defer_setup) { - bt_accept_unlink(sk); - if (newsock) - sock_graft(sk, newsock); - - release_sock(sk); - return sk; - } - - release_sock(sk); - } - - return NULL; -} -EXPORT_SYMBOL(bt_accept_dequeue); - -int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) -{ - int noblock = flags & MSG_DONTWAIT; - struct sock *sk = sock->sk; - struct sk_buff *skb; - size_t copied; - int err; - - BT_DBG("sock %p sk %p len %zu", sock, sk, len); - - if (flags & (MSG_OOB)) - return -EOPNOTSUPP; - - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) { - if (sk->sk_shutdown & RCV_SHUTDOWN) - return 0; - return err; - } - - msg->msg_namelen = 0; - - copied = skb->len; - if (len < copied) { - msg->msg_flags |= MSG_TRUNC; - copied = len; - } - - skb_reset_transport_header(skb); - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); - if (err == 0) - sock_recv_ts_and_drops(msg, sk, skb); - - skb_free_datagram(sk, skb); - - return err ? : copied; -} -EXPORT_SYMBOL(bt_sock_recvmsg); - -static long bt_sock_data_wait(struct sock *sk, long timeo) -{ - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(sk_sleep(sk), &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - - if (!skb_queue_empty(&sk->sk_receive_queue)) - break; - - if (sk->sk_err || (sk->sk_shutdown & RCV_SHUTDOWN)) - break; - - if (signal_pending(current) || !timeo) - break; - - set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); - } - - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - return timeo; -} - -int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) -{ - struct sock *sk = sock->sk; - int err = 0; - size_t target, copied = 0; - long timeo; - - if (flags & MSG_OOB) - return -EOPNOTSUPP; - - msg->msg_namelen = 0; - - BT_DBG("sk %p size %zu", sk, size); - - lock_sock(sk); - - target = sock_rcvlowat(sk, flags & MSG_WAITALL, size); - timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); - - do { - struct sk_buff *skb; - int chunk; - - skb = skb_dequeue(&sk->sk_receive_queue); - if (!skb) { - if (copied >= target) - break; - - err = sock_error(sk); - if (err) - break; - if (sk->sk_shutdown & RCV_SHUTDOWN) - break; - - err = -EAGAIN; - if (!timeo) - break; - - timeo = bt_sock_data_wait(sk, timeo); - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - goto out; - } - continue; - } - - chunk = min_t(unsigned int, skb->len, size); - if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, chunk)) { - skb_queue_head(&sk->sk_receive_queue, skb); - if (!copied) - copied = -EFAULT; - break; - } - copied += chunk; - size -= chunk; - - sock_recv_ts_and_drops(msg, sk, skb); - - if (!(flags & MSG_PEEK)) { - int skb_len = skb_headlen(skb); - - if (chunk <= skb_len) { - __skb_pull(skb, chunk); - } else { - struct sk_buff *frag; - - __skb_pull(skb, skb_len); - chunk -= skb_len; - - skb_walk_frags(skb, frag) { - if (chunk <= frag->len) { - /* Pulling partial data */ - skb->len -= chunk; - skb->data_len -= chunk; - __skb_pull(frag, chunk); - break; - } else if (frag->len) { - /* Pulling all frag data */ - chunk -= frag->len; - skb->len -= frag->len; - skb->data_len -= frag->len; - __skb_pull(frag, frag->len); - } - } - } - - if (skb->len) { - skb_queue_head(&sk->sk_receive_queue, skb); - break; - } - kfree_skb(skb); - - } else { - /* put message back and return */ - skb_queue_head(&sk->sk_receive_queue, skb); - break; - } - } while (size); - -out: - release_sock(sk); - return copied ? : err; -} -EXPORT_SYMBOL(bt_sock_stream_recvmsg); - -static inline unsigned int bt_accept_poll(struct sock *parent) -{ - struct list_head *p, *n; - struct sock *sk; - - list_for_each_safe(p, n, &bt_sk(parent)->accept_q) { - sk = (struct sock *) list_entry(p, struct bt_sock, accept_q); - if (sk->sk_state == BT_CONNECTED || - (bt_sk(parent)->defer_setup && - sk->sk_state == BT_CONNECT2)) - return POLLIN | POLLRDNORM; - } - - return 0; -} - -unsigned int bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait) -{ - struct sock *sk = sock->sk; - unsigned int mask = 0; - - BT_DBG("sock %p, sk %p", sock, sk); - - poll_wait(file, sk_sleep(sk), wait); - - if (sk->sk_state == BT_LISTEN) - return bt_accept_poll(sk); - - if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) - mask |= POLLERR; - - if (sk->sk_shutdown & RCV_SHUTDOWN) - mask |= POLLRDHUP | POLLIN | POLLRDNORM; - - if (sk->sk_shutdown == SHUTDOWN_MASK) - mask |= POLLHUP; - - if (!skb_queue_empty(&sk->sk_receive_queue)) - mask |= POLLIN | POLLRDNORM; - - if (sk->sk_state == BT_CLOSED) - mask |= POLLHUP; - - if (sk->sk_state == BT_CONNECT || - sk->sk_state == BT_CONNECT2 || - sk->sk_state == BT_CONFIG) - return mask; - - if (!bt_sk(sk)->suspended && sock_writeable(sk)) - mask |= POLLOUT | POLLWRNORM | POLLWRBAND; - else - set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); - - return mask; -} -EXPORT_SYMBOL(bt_sock_poll); - -int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk = sock->sk; - struct sk_buff *skb; - long amount; - int err; - - BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg); - - switch (cmd) { - case TIOCOUTQ: - if (sk->sk_state == BT_LISTEN) - return -EINVAL; - - amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); - if (amount < 0) - amount = 0; - err = put_user(amount, (int __user *) arg); - break; - - case TIOCINQ: - if (sk->sk_state == BT_LISTEN) - return -EINVAL; - - lock_sock(sk); - skb = skb_peek(&sk->sk_receive_queue); - amount = skb ? skb->len : 0; - release_sock(sk); - err = put_user(amount, (int __user *) arg); - break; - - case SIOCGSTAMP: - err = sock_get_timestamp(sk, (struct timeval __user *) arg); - break; - - case SIOCGSTAMPNS: - err = sock_get_timestampns(sk, (struct timespec __user *) arg); - break; - - default: - err = -ENOIOCTLCMD; - break; - } - - return err; -} -EXPORT_SYMBOL(bt_sock_ioctl); - -int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo) -{ - DECLARE_WAITQUEUE(wait, current); - int err = 0; - - BT_DBG("sk %p", sk); - - add_wait_queue(sk_sleep(sk), &wait); - set_current_state(TASK_INTERRUPTIBLE); - while (sk->sk_state != state) { - if (!timeo) { - err = -EINPROGRESS; - break; - } - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - set_current_state(TASK_INTERRUPTIBLE); - - err = sock_error(sk); - if (err) - break; - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - return err; -} -EXPORT_SYMBOL(bt_sock_wait_state); - -static struct net_proto_family bt_sock_family_ops = { - .owner = THIS_MODULE, - .family = PF_BLUETOOTH, - .create = bt_sock_create, -}; - -static int __init bt_init(void) -{ - int err; - - BT_INFO("Core ver %s", VERSION); - - err = bt_sysfs_init(); - if (err < 0) - return err; - - err = sock_register(&bt_sock_family_ops); - if (err < 0) { - bt_sysfs_cleanup(); - return err; - } - - BT_INFO("HCI device and connection manager initialized"); - - err = hci_sock_init(); - if (err < 0) - goto error; - - err = l2cap_init(); - if (err < 0) - goto sock_err; - - err = sco_init(); - if (err < 0) { - l2cap_exit(); - goto sock_err; - } - - return 0; - -sock_err: - hci_sock_cleanup(); - -error: - sock_unregister(PF_BLUETOOTH); - bt_sysfs_cleanup(); - - return err; -} - -static void __exit bt_exit(void) -{ - - sco_exit(); - - l2cap_exit(); - - hci_sock_cleanup(); - - sock_unregister(PF_BLUETOOTH); - - bt_sysfs_cleanup(); -} - -subsys_initcall(bt_init); -module_exit(bt_exit); - -MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -MODULE_DESCRIPTION("Bluetooth Core ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(PF_BLUETOOTH); diff --git a/net/bluetooth_tizen/bnep/Kconfig b/net/bluetooth_tizen/bnep/Kconfig deleted file mode 100644 index 71791fc..0000000 --- a/net/bluetooth_tizen/bnep/Kconfig +++ /dev/null @@ -1,24 +0,0 @@ -config BT_BNEP - tristate "BNEP protocol support" - depends on BT - select CRC32 - help - BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet - emulation layer on top of Bluetooth. BNEP is required for - Bluetooth PAN (Personal Area Network). - - Say Y here to compile BNEP support into the kernel or say M to - compile it as module (bnep). - -config BT_BNEP_MC_FILTER - bool "Multicast filter support" - depends on BT_BNEP - help - This option enables the multicast filter support for BNEP. - -config BT_BNEP_PROTO_FILTER - bool "Protocol filter support" - depends on BT_BNEP - help - This option enables the protocol filter support for BNEP. - diff --git a/net/bluetooth_tizen/bnep/Makefile b/net/bluetooth_tizen/bnep/Makefile deleted file mode 100644 index c7821e7..0000000 --- a/net/bluetooth_tizen/bnep/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the Linux Bluetooth BNEP layer. -# - -obj-$(CONFIG_BT_BNEP) += bnep.o - -bnep-objs := core.o sock.o netdev.o diff --git a/net/bluetooth_tizen/bnep/bnep.h b/net/bluetooth_tizen/bnep/bnep.h deleted file mode 100644 index e7ee531..0000000 --- a/net/bluetooth_tizen/bnep/bnep.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - BNEP protocol definition for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License, version 2, as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef _BNEP_H -#define _BNEP_H - -#include <linux/types.h> -#include <linux/crc32.h> -#include <net/bluetooth/bluetooth.h> - -/* Limits */ -#define BNEP_MAX_PROTO_FILTERS 5 -#define BNEP_MAX_MULTICAST_FILTERS 20 - -/* UUIDs */ -#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB -#define BNEP_UUID16 0x02 -#define BNEP_UUID32 0x04 -#define BNEP_UUID128 0x16 - -#define BNEP_SVC_PANU 0x1115 -#define BNEP_SVC_NAP 0x1116 -#define BNEP_SVC_GN 0x1117 - -/* Packet types */ -#define BNEP_GENERAL 0x00 -#define BNEP_CONTROL 0x01 -#define BNEP_COMPRESSED 0x02 -#define BNEP_COMPRESSED_SRC_ONLY 0x03 -#define BNEP_COMPRESSED_DST_ONLY 0x04 - -/* Control types */ -#define BNEP_CMD_NOT_UNDERSTOOD 0x00 -#define BNEP_SETUP_CONN_REQ 0x01 -#define BNEP_SETUP_CONN_RSP 0x02 -#define BNEP_FILTER_NET_TYPE_SET 0x03 -#define BNEP_FILTER_NET_TYPE_RSP 0x04 -#define BNEP_FILTER_MULTI_ADDR_SET 0x05 -#define BNEP_FILTER_MULTI_ADDR_RSP 0x06 - -/* Extension types */ -#define BNEP_EXT_CONTROL 0x00 - -/* Response messages */ -#define BNEP_SUCCESS 0x00 - -#define BNEP_CONN_INVALID_DST 0x01 -#define BNEP_CONN_INVALID_SRC 0x02 -#define BNEP_CONN_INVALID_SVC 0x03 -#define BNEP_CONN_NOT_ALLOWED 0x04 - -#define BNEP_FILTER_UNSUPPORTED_REQ 0x01 -#define BNEP_FILTER_INVALID_RANGE 0x02 -#define BNEP_FILTER_INVALID_MCADDR 0x02 -#define BNEP_FILTER_LIMIT_REACHED 0x03 -#define BNEP_FILTER_DENIED_SECURITY 0x04 - -/* L2CAP settings */ -#define BNEP_MTU 1691 -#define BNEP_PSM 0x0f -#define BNEP_FLUSH_TO 0xffff -#define BNEP_CONNECT_TO 15 -#define BNEP_FILTER_TO 15 - -/* Headers */ -#define BNEP_TYPE_MASK 0x7f -#define BNEP_EXT_HEADER 0x80 - -struct bnep_setup_conn_req { - __u8 type; - __u8 ctrl; - __u8 uuid_size; - __u8 service[0]; -} __packed; - -struct bnep_set_filter_req { - __u8 type; - __u8 ctrl; - __be16 len; - __u8 list[0]; -} __packed; - -struct bnep_control_rsp { - __u8 type; - __u8 ctrl; - __be16 resp; -} __packed; - -struct bnep_ext_hdr { - __u8 type; - __u8 len; - __u8 data[0]; -} __packed; - -/* BNEP ioctl defines */ -#define BNEPCONNADD _IOW('B', 200, int) -#define BNEPCONNDEL _IOW('B', 201, int) -#define BNEPGETCONNLIST _IOR('B', 210, int) -#define BNEPGETCONNINFO _IOR('B', 211, int) - -struct bnep_connadd_req { - int sock; /* Connected socket */ - __u32 flags; - __u16 role; - char device[16]; /* Name of the Ethernet device */ -}; - -struct bnep_conndel_req { - __u32 flags; - __u8 dst[ETH_ALEN]; -}; - -struct bnep_conninfo { - __u32 flags; - __u16 role; - __u16 state; - __u8 dst[ETH_ALEN]; - char device[16]; -}; - -struct bnep_connlist_req { - __u32 cnum; - struct bnep_conninfo __user *ci; -}; - -struct bnep_proto_filter { - __u16 start; - __u16 end; -}; - -int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock); -int bnep_del_connection(struct bnep_conndel_req *req); -int bnep_get_connlist(struct bnep_connlist_req *req); -int bnep_get_conninfo(struct bnep_conninfo *ci); - -/* BNEP sessions */ -struct bnep_session { - struct list_head list; - - unsigned int role; - unsigned long state; - unsigned long flags; - atomic_t terminate; - struct task_struct *task; - - struct ethhdr eh; - struct msghdr msg; - - struct bnep_proto_filter proto_filter[BNEP_MAX_PROTO_FILTERS]; - unsigned long long mc_filter; - - struct socket *sock; - struct net_device *dev; -}; - -void bnep_net_setup(struct net_device *dev); -int bnep_sock_init(void); -void bnep_sock_cleanup(void); - -static inline int bnep_mc_hash(__u8 *addr) -{ - return crc32_be(~0, addr, ETH_ALEN) >> 26; -} - -#endif diff --git a/net/bluetooth_tizen/bnep/core.c b/net/bluetooth_tizen/bnep/core.c deleted file mode 100644 index a779ec7..0000000 --- a/net/bluetooth_tizen/bnep/core.c +++ /dev/null @@ -1,747 +0,0 @@ -/* - BNEP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2001-2002 Inventel Systemes - Written 2001-2002 by - Clément Moreau <clement.moreau@inventel.fr> - David Libault <david.libault@inventel.fr> - - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> - -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/signal.h> -#include <linux/init.h> -#include <linux/wait.h> -#include <linux/freezer.h> -#include <linux/errno.h> -#include <linux/net.h> -#include <linux/slab.h> -#include <linux/kthread.h> -#include <net/sock.h> - -#include <linux/socket.h> -#include <linux/file.h> - -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> - -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> - -#include "bnep.h" - -#define VERSION "1.3" - -static bool compress_src = true; -static bool compress_dst = true; - -static LIST_HEAD(bnep_session_list); -static DECLARE_RWSEM(bnep_session_sem); - -static struct bnep_session *__bnep_get_session(u8 *dst) -{ - struct bnep_session *s; - - BT_DBG(""); - - list_for_each_entry(s, &bnep_session_list, list) - if (!compare_ether_addr(dst, s->eh.h_source)) - return s; - - return NULL; -} - -static void __bnep_link_session(struct bnep_session *s) -{ - list_add(&s->list, &bnep_session_list); -} - -static void __bnep_unlink_session(struct bnep_session *s) -{ - list_del(&s->list); -} - -static int bnep_send(struct bnep_session *s, void *data, size_t len) -{ - struct socket *sock = s->sock; - struct kvec iv = { data, len }; - - return kernel_sendmsg(sock, &s->msg, &iv, 1, len); -} - -static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp) -{ - struct bnep_control_rsp rsp; - rsp.type = BNEP_CONTROL; - rsp.ctrl = ctrl; - rsp.resp = htons(resp); - return bnep_send(s, &rsp, sizeof(rsp)); -} - -#ifdef CONFIG_BT_BNEP_PROTO_FILTER -static inline void bnep_set_default_proto_filter(struct bnep_session *s) -{ - /* (IPv4, ARP) */ - s->proto_filter[0].start = ETH_P_IP; - s->proto_filter[0].end = ETH_P_ARP; - /* (RARP, AppleTalk) */ - s->proto_filter[1].start = ETH_P_RARP; - s->proto_filter[1].end = ETH_P_AARP; - /* (IPX, IPv6) */ - s->proto_filter[2].start = ETH_P_IPX; - s->proto_filter[2].end = ETH_P_IPV6; -} -#endif - -static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len) -{ - int n; - - if (len < 2) - return -EILSEQ; - - n = get_unaligned_be16(data); - data++; - len -= 2; - - if (len < n) - return -EILSEQ; - - BT_DBG("filter len %d", n); - -#ifdef CONFIG_BT_BNEP_PROTO_FILTER - n /= 4; - if (n <= BNEP_MAX_PROTO_FILTERS) { - struct bnep_proto_filter *f = s->proto_filter; - int i; - - for (i = 0; i < n; i++) { - f[i].start = get_unaligned_be16(data++); - f[i].end = get_unaligned_be16(data++); - - BT_DBG("proto filter start %d end %d", - f[i].start, f[i].end); - } - - if (i < BNEP_MAX_PROTO_FILTERS) - memset(f + i, 0, sizeof(*f)); - - if (n == 0) - bnep_set_default_proto_filter(s); - - bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS); - } else { - bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED); - } -#else - bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ); -#endif - return 0; -} - -static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len) -{ - int n; - - if (len < 2) - return -EILSEQ; - - n = get_unaligned_be16(data); - data += 2; - len -= 2; - - if (len < n) - return -EILSEQ; - - BT_DBG("filter len %d", n); - -#ifdef CONFIG_BT_BNEP_MC_FILTER - n /= (ETH_ALEN * 2); - - if (n > 0) { - int i; - - s->mc_filter = 0; - - /* Always send broadcast */ - set_bit(bnep_mc_hash(s->dev->broadcast), (ulong *) &s->mc_filter); - - /* Add address ranges to the multicast hash */ - for (; n > 0; n--) { - u8 a1[6], *a2; - - memcpy(a1, data, ETH_ALEN); - data += ETH_ALEN; - a2 = data; - data += ETH_ALEN; - - BT_DBG("mc filter %s -> %s", - batostr((void *) a1), batostr((void *) a2)); - - /* Iterate from a1 to a2 */ - set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter); - while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) { - /* Increment a1 */ - i = 5; - while (i >= 0 && ++a1[i--] == 0) - ; - - set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter); - } - } - } - - BT_DBG("mc filter hash 0x%llx", s->mc_filter); - - bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS); -#else - bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ); -#endif - return 0; -} - -static int bnep_rx_control(struct bnep_session *s, void *data, int len) -{ - u8 cmd = *(u8 *)data; - int err = 0; - - data++; - len--; - - switch (cmd) { - case BNEP_CMD_NOT_UNDERSTOOD: - case BNEP_SETUP_CONN_RSP: - case BNEP_FILTER_NET_TYPE_RSP: - case BNEP_FILTER_MULTI_ADDR_RSP: - /* Ignore these for now */ - break; - - case BNEP_FILTER_NET_TYPE_SET: - err = bnep_ctrl_set_netfilter(s, data, len); - break; - - case BNEP_FILTER_MULTI_ADDR_SET: - err = bnep_ctrl_set_mcfilter(s, data, len); - break; - - case BNEP_SETUP_CONN_REQ: - err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, BNEP_CONN_NOT_ALLOWED); - break; - - default: { - u8 pkt[3]; - pkt[0] = BNEP_CONTROL; - pkt[1] = BNEP_CMD_NOT_UNDERSTOOD; - pkt[2] = cmd; - bnep_send(s, pkt, sizeof(pkt)); - } - break; - } - - return err; -} - -static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb) -{ - struct bnep_ext_hdr *h; - int err = 0; - - do { - h = (void *) skb->data; - if (!skb_pull(skb, sizeof(*h))) { - err = -EILSEQ; - break; - } - - BT_DBG("type 0x%x len %d", h->type, h->len); - - switch (h->type & BNEP_TYPE_MASK) { - case BNEP_EXT_CONTROL: - bnep_rx_control(s, skb->data, skb->len); - break; - - default: - /* Unknown extension, skip it. */ - break; - } - - if (!skb_pull(skb, h->len)) { - err = -EILSEQ; - break; - } - } while (!err && (h->type & BNEP_EXT_HEADER)); - - return err; -} - -static u8 __bnep_rx_hlen[] = { - ETH_HLEN, /* BNEP_GENERAL */ - 0, /* BNEP_CONTROL */ - 2, /* BNEP_COMPRESSED */ - ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */ - ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */ -}; - -static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb) -{ - struct net_device *dev = s->dev; - struct sk_buff *nskb; - u8 type; - - dev->stats.rx_bytes += skb->len; - - type = *(u8 *) skb->data; - skb_pull(skb, 1); - - if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen)) - goto badframe; - - if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) { - bnep_rx_control(s, skb->data, skb->len); - kfree_skb(skb); - return 0; - } - - skb_reset_mac_header(skb); - - /* Verify and pull out header */ - if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK])) - goto badframe; - - s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2)); - - if (type & BNEP_EXT_HEADER) { - if (bnep_rx_extension(s, skb) < 0) - goto badframe; - } - - /* Strip 802.1p header */ - if (ntohs(s->eh.h_proto) == 0x8100) { - if (!skb_pull(skb, 4)) - goto badframe; - s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2)); - } - - /* We have to alloc new skb and copy data here :(. Because original skb - * may not be modified and because of the alignment requirements. */ - nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL); - if (!nskb) { - dev->stats.rx_dropped++; - kfree_skb(skb); - return -ENOMEM; - } - skb_reserve(nskb, 2); - - /* Decompress header and construct ether frame */ - switch (type & BNEP_TYPE_MASK) { - case BNEP_COMPRESSED: - memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN); - break; - - case BNEP_COMPRESSED_SRC_ONLY: - memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN); - memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), ETH_ALEN); - put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2)); - break; - - case BNEP_COMPRESSED_DST_ONLY: - memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), - ETH_ALEN); - memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source, - ETH_ALEN + 2); - break; - - case BNEP_GENERAL: - memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb), - ETH_ALEN * 2); - put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2)); - break; - } - - skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len); - kfree_skb(skb); - - dev->stats.rx_packets++; - nskb->ip_summed = CHECKSUM_NONE; - nskb->protocol = eth_type_trans(nskb, dev); - netif_rx_ni(nskb); - return 0; - -badframe: - dev->stats.rx_errors++; - kfree_skb(skb); - return 0; -} - -static u8 __bnep_tx_types[] = { - BNEP_GENERAL, - BNEP_COMPRESSED_SRC_ONLY, - BNEP_COMPRESSED_DST_ONLY, - BNEP_COMPRESSED -}; - -static inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb) -{ - struct ethhdr *eh = (void *) skb->data; - struct socket *sock = s->sock; - struct kvec iv[3]; - int len = 0, il = 0; - u8 type = 0; - - BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type); - - if (!skb->dev) { - /* Control frame sent by us */ - goto send; - } - - iv[il++] = (struct kvec) { &type, 1 }; - len++; - - if (compress_src && !compare_ether_addr(eh->h_dest, s->eh.h_source)) - type |= 0x01; - - if (compress_dst && !compare_ether_addr(eh->h_source, s->eh.h_dest)) - type |= 0x02; - - if (type) - skb_pull(skb, ETH_ALEN * 2); - - type = __bnep_tx_types[type]; - switch (type) { - case BNEP_COMPRESSED_SRC_ONLY: - iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN }; - len += ETH_ALEN; - break; - - case BNEP_COMPRESSED_DST_ONLY: - iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN }; - len += ETH_ALEN; - break; - } - -send: - iv[il++] = (struct kvec) { skb->data, skb->len }; - len += skb->len; - - /* FIXME: linearize skb */ - { - len = kernel_sendmsg(sock, &s->msg, iv, il, len); - } - kfree_skb(skb); - - if (len > 0) { - s->dev->stats.tx_bytes += len; - s->dev->stats.tx_packets++; - return 0; - } - - return len; -} - -static int bnep_session(void *arg) -{ - struct bnep_session *s = arg; - struct net_device *dev = s->dev; - struct sock *sk = s->sock->sk; - struct sk_buff *skb; - wait_queue_t wait; - - BT_DBG(""); - - set_user_nice(current, -15); - - init_waitqueue_entry(&wait, current); - add_wait_queue(sk_sleep(sk), &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (atomic_read(&s->terminate)) - break; - /* RX */ - while ((skb = skb_dequeue(&sk->sk_receive_queue))) { - skb_orphan(skb); - if (!skb_linearize(skb)) - bnep_rx_frame(s, skb); - else - kfree_skb(skb); - } - - if (sk->sk_state != BT_CONNECTED) - break; - - /* TX */ - while ((skb = skb_dequeue(&sk->sk_write_queue))) - if (bnep_tx_frame(s, skb)) - break; - netif_wake_queue(dev); - - schedule(); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - /* Cleanup session */ - down_write(&bnep_session_sem); - - /* Delete network device */ - unregister_netdev(dev); - - /* Wakeup user-space polling for socket errors */ - s->sock->sk->sk_err = EUNATCH; - - wake_up_interruptible(sk_sleep(s->sock->sk)); - - /* Release the socket */ - fput(s->sock->file); - - __bnep_unlink_session(s); - - up_write(&bnep_session_sem); - free_netdev(dev); - module_put_and_exit(0); - return 0; -} - -static struct device *bnep_get_device(struct bnep_session *session) -{ - bdaddr_t *src = &bt_sk(session->sock->sk)->src; - bdaddr_t *dst = &bt_sk(session->sock->sk)->dst; - struct hci_dev *hdev; - struct hci_conn *conn; - - hdev = hci_get_route(dst, src); - if (!hdev) - return NULL; - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); - - hci_dev_put(hdev); - - return conn ? &conn->dev : NULL; -} - -static struct device_type bnep_type = { - .name = "bluetooth", -}; - -int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) -{ - struct net_device *dev; - struct bnep_session *s, *ss; - u8 dst[ETH_ALEN], src[ETH_ALEN]; - int err; - - BT_DBG(""); - - baswap((void *) dst, &bt_sk(sock->sk)->dst); - baswap((void *) src, &bt_sk(sock->sk)->src); - - /* session struct allocated as private part of net_device */ - dev = alloc_netdev(sizeof(struct bnep_session), - (*req->device) ? req->device : "bnep%d", - bnep_net_setup); - if (!dev) - return -ENOMEM; - - down_write(&bnep_session_sem); - - ss = __bnep_get_session(dst); - if (ss && ss->state == BT_CONNECTED) { - err = -EEXIST; - goto failed; - } - - s = netdev_priv(dev); - - /* This is rx header therefore addresses are swapped. - * ie. eh.h_dest is our local address. */ - memcpy(s->eh.h_dest, &src, ETH_ALEN); - memcpy(s->eh.h_source, &dst, ETH_ALEN); - memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN); - - s->dev = dev; - s->sock = sock; - s->role = req->role; - s->state = BT_CONNECTED; - - s->msg.msg_flags = MSG_NOSIGNAL; - -#ifdef CONFIG_BT_BNEP_MC_FILTER - /* Set default mc filter */ - set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter); -#endif - -#ifdef CONFIG_BT_BNEP_PROTO_FILTER - /* Set default protocol filter */ - bnep_set_default_proto_filter(s); -#endif - - SET_NETDEV_DEV(dev, bnep_get_device(s)); - SET_NETDEV_DEVTYPE(dev, &bnep_type); - - err = register_netdev(dev); - if (err) - goto failed; - - __bnep_link_session(s); - - __module_get(THIS_MODULE); - s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name); - if (IS_ERR(s->task)) { - /* Session thread start failed, gotta cleanup. */ - module_put(THIS_MODULE); - unregister_netdev(dev); - __bnep_unlink_session(s); - err = PTR_ERR(s->task); - goto failed; - } - - up_write(&bnep_session_sem); - strcpy(req->device, dev->name); - return 0; - -failed: - up_write(&bnep_session_sem); - free_netdev(dev); - return err; -} - -int bnep_del_connection(struct bnep_conndel_req *req) -{ - struct bnep_session *s; - int err = 0; - - BT_DBG(""); - - down_read(&bnep_session_sem); - - s = __bnep_get_session(req->dst); - if (s) { - atomic_inc(&s->terminate); - wake_up_process(s->task); - } else - err = -ENOENT; - - up_read(&bnep_session_sem); - return err; -} - -static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s) -{ - memset(ci, 0, sizeof(*ci)); - memcpy(ci->dst, s->eh.h_source, ETH_ALEN); - strcpy(ci->device, s->dev->name); - ci->flags = s->flags; - ci->state = s->state; - ci->role = s->role; -} - -int bnep_get_connlist(struct bnep_connlist_req *req) -{ - struct bnep_session *s; - int err = 0, n = 0; - - down_read(&bnep_session_sem); - - list_for_each_entry(s, &bnep_session_list, list) { - struct bnep_conninfo ci; - - __bnep_copy_ci(&ci, s); - - if (copy_to_user(req->ci, &ci, sizeof(ci))) { - err = -EFAULT; - break; - } - - if (++n >= req->cnum) - break; - - req->ci++; - } - req->cnum = n; - - up_read(&bnep_session_sem); - return err; -} - -int bnep_get_conninfo(struct bnep_conninfo *ci) -{ - struct bnep_session *s; - int err = 0; - - down_read(&bnep_session_sem); - - s = __bnep_get_session(ci->dst); - if (s) - __bnep_copy_ci(ci, s); - else - err = -ENOENT; - - up_read(&bnep_session_sem); - return err; -} - -static int __init bnep_init(void) -{ - char flt[50] = ""; - -#ifdef CONFIG_BT_BNEP_PROTO_FILTER - strcat(flt, "protocol "); -#endif - -#ifdef CONFIG_BT_BNEP_MC_FILTER - strcat(flt, "multicast"); -#endif - - BT_INFO("BNEP (Ethernet Emulation) ver %s", VERSION); - if (flt[0]) - BT_INFO("BNEP filters: %s", flt); - - bnep_sock_init(); - return 0; -} - -static void __exit bnep_exit(void) -{ - bnep_sock_cleanup(); -} - -module_init(bnep_init); -module_exit(bnep_exit); - -module_param(compress_src, bool, 0644); -MODULE_PARM_DESC(compress_src, "Compress sources headers"); - -module_param(compress_dst, bool, 0644); -MODULE_PARM_DESC(compress_dst, "Compress destination headers"); - -MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("bt-proto-4"); diff --git a/net/bluetooth_tizen/bnep/netdev.c b/net/bluetooth_tizen/bnep/netdev.c deleted file mode 100644 index bc40864..0000000 --- a/net/bluetooth_tizen/bnep/netdev.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - BNEP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2001-2002 Inventel Systemes - Written 2001-2002 by - Clément Moreau <clement.moreau@inventel.fr> - David Libault <david.libault@inventel.fr> - - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> -#include <linux/slab.h> - -#include <linux/socket.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/wait.h> - -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> - -#include "bnep.h" - -#define BNEP_TX_QUEUE_LEN 20 - -static int bnep_net_open(struct net_device *dev) -{ - netif_start_queue(dev); - return 0; -} - -static int bnep_net_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - -static void bnep_net_set_mc_list(struct net_device *dev) -{ -#ifdef CONFIG_BT_BNEP_MC_FILTER - struct bnep_session *s = netdev_priv(dev); - struct sock *sk = s->sock->sk; - struct bnep_set_filter_req *r; - struct sk_buff *skb; - int size; - - BT_DBG("%s mc_count %d", dev->name, netdev_mc_count(dev)); - - size = sizeof(*r) + (BNEP_MAX_MULTICAST_FILTERS + 1) * ETH_ALEN * 2; - skb = alloc_skb(size, GFP_ATOMIC); - if (!skb) { - BT_ERR("%s Multicast list allocation failed", dev->name); - return; - } - - r = (void *) skb->data; - __skb_put(skb, sizeof(*r)); - - r->type = BNEP_CONTROL; - r->ctrl = BNEP_FILTER_MULTI_ADDR_SET; - - if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { - u8 start[ETH_ALEN] = { 0x01 }; - - /* Request all addresses */ - memcpy(__skb_put(skb, ETH_ALEN), start, ETH_ALEN); - memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN); - r->len = htons(ETH_ALEN * 2); - } else { - struct netdev_hw_addr *ha; - int i, len = skb->len; - - if (dev->flags & IFF_BROADCAST) { - memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN); - memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN); - } - - /* FIXME: We should group addresses here. */ - - i = 0; - netdev_for_each_mc_addr(ha, dev) { - if (i == BNEP_MAX_MULTICAST_FILTERS) - break; - memcpy(__skb_put(skb, ETH_ALEN), ha->addr, ETH_ALEN); - memcpy(__skb_put(skb, ETH_ALEN), ha->addr, ETH_ALEN); - - i++; - } - r->len = htons(skb->len - len); - } - - skb_queue_tail(&sk->sk_write_queue, skb); - wake_up_interruptible(sk_sleep(sk)); -#endif -} - -static int bnep_net_set_mac_addr(struct net_device *dev, void *arg) -{ - BT_DBG("%s", dev->name); - return 0; -} - -static void bnep_net_timeout(struct net_device *dev) -{ - BT_DBG("net_timeout"); - netif_wake_queue(dev); -} - -#ifdef CONFIG_BT_BNEP_MC_FILTER -static inline int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s) -{ - struct ethhdr *eh = (void *) skb->data; - - if ((eh->h_dest[0] & 1) && !test_bit(bnep_mc_hash(eh->h_dest), (ulong *) &s->mc_filter)) - return 1; - return 0; -} -#endif - -#ifdef CONFIG_BT_BNEP_PROTO_FILTER -/* Determine ether protocol. Based on eth_type_trans. */ -static inline u16 bnep_net_eth_proto(struct sk_buff *skb) -{ - struct ethhdr *eh = (void *) skb->data; - u16 proto = ntohs(eh->h_proto); - - if (proto >= 1536) - return proto; - - if (get_unaligned((__be16 *) skb->data) == htons(0xFFFF)) - return ETH_P_802_3; - - return ETH_P_802_2; -} - -static inline int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session *s) -{ - u16 proto = bnep_net_eth_proto(skb); - struct bnep_proto_filter *f = s->proto_filter; - int i; - - for (i = 0; i < BNEP_MAX_PROTO_FILTERS && f[i].end; i++) { - if (proto >= f[i].start && proto <= f[i].end) - return 0; - } - - BT_DBG("BNEP: filtered skb %p, proto 0x%.4x", skb, proto); - return 1; -} -#endif - -static netdev_tx_t bnep_net_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct bnep_session *s = netdev_priv(dev); - struct sock *sk = s->sock->sk; - - BT_DBG("skb %p, dev %p", skb, dev); - -#ifdef CONFIG_BT_BNEP_MC_FILTER - if (bnep_net_mc_filter(skb, s)) { - kfree_skb(skb); - return NETDEV_TX_OK; - } -#endif - -#ifdef CONFIG_BT_BNEP_PROTO_FILTER - if (bnep_net_proto_filter(skb, s)) { - kfree_skb(skb); - return NETDEV_TX_OK; - } -#endif - - /* - * We cannot send L2CAP packets from here as we are potentially in a bh. - * So we have to queue them and wake up session thread which is sleeping - * on the sk_sleep(sk). - */ - dev->trans_start = jiffies; - skb_queue_tail(&sk->sk_write_queue, skb); - wake_up_interruptible(sk_sleep(sk)); - - if (skb_queue_len(&sk->sk_write_queue) >= BNEP_TX_QUEUE_LEN) { - BT_DBG("tx queue is full"); - - /* Stop queuing. - * Session thread will do netif_wake_queue() */ - netif_stop_queue(dev); - } - - return NETDEV_TX_OK; -} - -static const struct net_device_ops bnep_netdev_ops = { - .ndo_open = bnep_net_open, - .ndo_stop = bnep_net_close, - .ndo_start_xmit = bnep_net_xmit, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = bnep_net_set_mc_list, - .ndo_set_mac_address = bnep_net_set_mac_addr, - .ndo_tx_timeout = bnep_net_timeout, - .ndo_change_mtu = eth_change_mtu, - -}; - -void bnep_net_setup(struct net_device *dev) -{ - - memset(dev->broadcast, 0xff, ETH_ALEN); - dev->addr_len = ETH_ALEN; - - ether_setup(dev); - dev->priv_flags &= ~IFF_TX_SKB_SHARING; - dev->netdev_ops = &bnep_netdev_ops; - - dev->watchdog_timeo = HZ * 2; -} diff --git a/net/bluetooth_tizen/bnep/sock.c b/net/bluetooth_tizen/bnep/sock.c deleted file mode 100644 index 9f9c8dc..0000000 --- a/net/bluetooth_tizen/bnep/sock.c +++ /dev/null @@ -1,259 +0,0 @@ -/* - BNEP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2001-2002 Inventel Systemes - Written 2001-2002 by - David Libault <david.libault@inventel.fr> - - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/capability.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/skbuff.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <linux/file.h> -#include <linux/init.h> -#include <linux/compat.h> -#include <linux/gfp.h> -#include <linux/uaccess.h> -#include <net/sock.h> - -#include <asm/system.h> - -#include "bnep.h" - -static int bnep_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!sk) - return 0; - - sock_orphan(sk); - sock_put(sk); - return 0; -} - -static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct bnep_connlist_req cl; - struct bnep_connadd_req ca; - struct bnep_conndel_req cd; - struct bnep_conninfo ci; - struct socket *nsock; - void __user *argp = (void __user *)arg; - int err; - - BT_DBG("cmd %x arg %lx", cmd, arg); - - switch (cmd) { - case BNEPCONNADD: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (copy_from_user(&ca, argp, sizeof(ca))) - return -EFAULT; - - nsock = sockfd_lookup(ca.sock, &err); - if (!nsock) - return err; - - if (nsock->sk->sk_state != BT_CONNECTED) { - sockfd_put(nsock); - return -EBADFD; - } - ca.device[sizeof(ca.device)-1] = 0; - - err = bnep_add_connection(&ca, nsock); - if (!err) { - if (copy_to_user(argp, &ca, sizeof(ca))) - err = -EFAULT; - } else - sockfd_put(nsock); - - return err; - - case BNEPCONNDEL: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (copy_from_user(&cd, argp, sizeof(cd))) - return -EFAULT; - - return bnep_del_connection(&cd); - - case BNEPGETCONNLIST: - if (copy_from_user(&cl, argp, sizeof(cl))) - return -EFAULT; - - if (cl.cnum <= 0) - return -EINVAL; - - err = bnep_get_connlist(&cl); - if (!err && copy_to_user(argp, &cl, sizeof(cl))) - return -EFAULT; - - return err; - - case BNEPGETCONNINFO: - if (copy_from_user(&ci, argp, sizeof(ci))) - return -EFAULT; - - err = bnep_get_conninfo(&ci); - if (!err && copy_to_user(argp, &ci, sizeof(ci))) - return -EFAULT; - - return err; - - default: - return -EINVAL; - } - - return 0; -} - -#ifdef CONFIG_COMPAT -static int bnep_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - if (cmd == BNEPGETCONNLIST) { - struct bnep_connlist_req cl; - u32 uci; - int err; - - if (get_user(cl.cnum, (u32 __user *) arg) || - get_user(uci, (u32 __user *) (arg + 4))) - return -EFAULT; - - cl.ci = compat_ptr(uci); - - if (cl.cnum <= 0) - return -EINVAL; - - err = bnep_get_connlist(&cl); - - if (!err && put_user(cl.cnum, (u32 __user *) arg)) - err = -EFAULT; - - return err; - } - - return bnep_sock_ioctl(sock, cmd, arg); -} -#endif - -static const struct proto_ops bnep_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = bnep_sock_release, - .ioctl = bnep_sock_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = bnep_sock_compat_ioctl, -#endif - .bind = sock_no_bind, - .getname = sock_no_getname, - .sendmsg = sock_no_sendmsg, - .recvmsg = sock_no_recvmsg, - .poll = sock_no_poll, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = sock_no_setsockopt, - .getsockopt = sock_no_getsockopt, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - -static struct proto bnep_proto = { - .name = "BNEP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct bt_sock) -}; - -static int bnep_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - if (sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &bnep_proto); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - - sock->ops = &bnep_sock_ops; - - sock->state = SS_UNCONNECTED; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = protocol; - sk->sk_state = BT_OPEN; - - return 0; -} - -static const struct net_proto_family bnep_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = bnep_sock_create -}; - -int __init bnep_sock_init(void) -{ - int err; - - err = proto_register(&bnep_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops); - if (err < 0) - goto error; - - return 0; - -error: - BT_ERR("Can't register BNEP socket"); - proto_unregister(&bnep_proto); - return err; -} - -void __exit bnep_sock_cleanup(void) -{ - if (bt_sock_unregister(BTPROTO_BNEP) < 0) - BT_ERR("Can't unregister BNEP socket"); - - proto_unregister(&bnep_proto); -} diff --git a/net/bluetooth_tizen/cmtp/Kconfig b/net/bluetooth_tizen/cmtp/Kconfig deleted file mode 100644 index 94cbf42..0000000 --- a/net/bluetooth_tizen/cmtp/Kconfig +++ /dev/null @@ -1,11 +0,0 @@ -config BT_CMTP - tristate "CMTP protocol support" - depends on BT && ISDN_CAPI - help - CMTP (CAPI Message Transport Protocol) is a transport layer - for CAPI messages. CMTP is required for the Bluetooth Common - ISDN Access Profile. - - Say Y here to compile CMTP support into the kernel or say M to - compile it as module (cmtp). - diff --git a/net/bluetooth_tizen/cmtp/Makefile b/net/bluetooth_tizen/cmtp/Makefile deleted file mode 100644 index 890a9a5..0000000 --- a/net/bluetooth_tizen/cmtp/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the Linux Bluetooth CMTP layer -# - -obj-$(CONFIG_BT_CMTP) += cmtp.o - -cmtp-objs := core.o sock.o capi.o diff --git a/net/bluetooth_tizen/cmtp/capi.c b/net/bluetooth_tizen/cmtp/capi.c deleted file mode 100644 index 50f0d13..0000000 --- a/net/bluetooth_tizen/cmtp/capi.c +++ /dev/null @@ -1,624 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/skbuff.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <linux/file.h> -#include <linux/wait.h> -#include <linux/kthread.h> -#include <net/sock.h> - -#include <linux/isdn/capilli.h> -#include <linux/isdn/capicmd.h> -#include <linux/isdn/capiutil.h> - -#include "cmtp.h" - -#define CAPI_INTEROPERABILITY 0x20 - -#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ) -#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF) -#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND) -#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP) - -#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2) -#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4) -#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2) -#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2) - -#define CAPI_FUNCTION_REGISTER 0 -#define CAPI_FUNCTION_RELEASE 1 -#define CAPI_FUNCTION_GET_PROFILE 2 -#define CAPI_FUNCTION_GET_MANUFACTURER 3 -#define CAPI_FUNCTION_GET_VERSION 4 -#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5 -#define CAPI_FUNCTION_MANUFACTURER 6 -#define CAPI_FUNCTION_LOOPBACK 7 - - -#define CMTP_MSGNUM 1 -#define CMTP_APPLID 2 -#define CMTP_MAPPING 3 - -static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl) -{ - struct cmtp_application *app = kzalloc(sizeof(*app), GFP_KERNEL); - - BT_DBG("session %p application %p appl %d", session, app, appl); - - if (!app) - return NULL; - - app->state = BT_OPEN; - app->appl = appl; - - list_add_tail(&app->list, &session->applications); - - return app; -} - -static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app) -{ - BT_DBG("session %p application %p", session, app); - - if (app) { - list_del(&app->list); - kfree(app); - } -} - -static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value) -{ - struct cmtp_application *app; - struct list_head *p, *n; - - list_for_each_safe(p, n, &session->applications) { - app = list_entry(p, struct cmtp_application, list); - switch (pattern) { - case CMTP_MSGNUM: - if (app->msgnum == value) - return app; - break; - case CMTP_APPLID: - if (app->appl == value) - return app; - break; - case CMTP_MAPPING: - if (app->mapping == value) - return app; - break; - } - } - - return NULL; -} - -static int cmtp_msgnum_get(struct cmtp_session *session) -{ - session->msgnum++; - - if ((session->msgnum & 0xff) > 200) - session->msgnum = CMTP_INITIAL_MSGNUM + 1; - - return session->msgnum; -} - -static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb) -{ - struct cmtp_scb *scb = (void *) skb->cb; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - scb->id = -1; - scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3); - - skb_queue_tail(&session->transmit, skb); - - wake_up_interruptible(sk_sleep(session->sock->sk)); -} - -static void cmtp_send_interopmsg(struct cmtp_session *session, - __u8 subcmd, __u16 appl, __u16 msgnum, - __u16 function, unsigned char *buf, int len) -{ - struct sk_buff *skb; - unsigned char *s; - - BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum); - - skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC); - if (!skb) { - BT_ERR("Can't allocate memory for interoperability packet"); - return; - } - - s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len); - - capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len); - capimsg_setu16(s, 2, appl); - capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY); - capimsg_setu8 (s, 5, subcmd); - capimsg_setu16(s, 6, msgnum); - - /* Interoperability selector (Bluetooth Device Management) */ - capimsg_setu16(s, 8, 0x0001); - - capimsg_setu8 (s, 10, 3 + len); - capimsg_setu16(s, 11, function); - capimsg_setu8 (s, 13, len); - - if (len > 0) - memcpy(s + 14, buf, len); - - cmtp_send_capimsg(session, skb); -} - -static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb) -{ - struct capi_ctr *ctrl = &session->ctrl; - struct cmtp_application *application; - __u16 appl, msgnum, func, info; - __u32 controller; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - switch (CAPIMSG_SUBCOMMAND(skb->data)) { - case CAPI_CONF: - if (skb->len < CAPI_MSG_BASELEN + 10) - break; - - func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5); - info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8); - - switch (func) { - case CAPI_FUNCTION_REGISTER: - msgnum = CAPIMSG_MSGID(skb->data); - - application = cmtp_application_get(session, CMTP_MSGNUM, msgnum); - if (application) { - application->state = BT_CONNECTED; - application->msgnum = 0; - application->mapping = CAPIMSG_APPID(skb->data); - wake_up_interruptible(&session->wait); - } - - break; - - case CAPI_FUNCTION_RELEASE: - appl = CAPIMSG_APPID(skb->data); - - application = cmtp_application_get(session, CMTP_MAPPING, appl); - if (application) { - application->state = BT_CLOSED; - application->msgnum = 0; - wake_up_interruptible(&session->wait); - } - - break; - - case CAPI_FUNCTION_GET_PROFILE: - if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile)) - break; - - controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11); - msgnum = CAPIMSG_MSGID(skb->data); - - if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) { - session->ncontroller = controller; - wake_up_interruptible(&session->wait); - break; - } - - if (!info && ctrl) { - memcpy(&ctrl->profile, - skb->data + CAPI_MSG_BASELEN + 11, - sizeof(capi_profile)); - session->state = BT_CONNECTED; - capi_ctr_ready(ctrl); - } - - break; - - case CAPI_FUNCTION_GET_MANUFACTURER: - if (skb->len < CAPI_MSG_BASELEN + 15) - break; - - controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10); - - if (!info && ctrl) { - int len = min_t(uint, CAPI_MANUFACTURER_LEN, - skb->data[CAPI_MSG_BASELEN + 14]); - - memset(ctrl->manu, 0, CAPI_MANUFACTURER_LEN); - strncpy(ctrl->manu, - skb->data + CAPI_MSG_BASELEN + 15, len); - } - - break; - - case CAPI_FUNCTION_GET_VERSION: - if (skb->len < CAPI_MSG_BASELEN + 32) - break; - - controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); - - if (!info && ctrl) { - ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16); - ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20); - ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24); - ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28); - } - - break; - - case CAPI_FUNCTION_GET_SERIAL_NUMBER: - if (skb->len < CAPI_MSG_BASELEN + 17) - break; - - controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); - - if (!info && ctrl) { - int len = min_t(uint, CAPI_SERIAL_LEN, - skb->data[CAPI_MSG_BASELEN + 16]); - - memset(ctrl->serial, 0, CAPI_SERIAL_LEN); - strncpy(ctrl->serial, - skb->data + CAPI_MSG_BASELEN + 17, len); - } - - break; - } - - break; - - case CAPI_IND: - if (skb->len < CAPI_MSG_BASELEN + 6) - break; - - func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3); - - if (func == CAPI_FUNCTION_LOOPBACK) { - int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6, - skb->data[CAPI_MSG_BASELEN + 5]); - appl = CAPIMSG_APPID(skb->data); - msgnum = CAPIMSG_MSGID(skb->data); - cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func, - skb->data + CAPI_MSG_BASELEN + 6, len); - } - - break; - } - - kfree_skb(skb); -} - -void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) -{ - struct capi_ctr *ctrl = &session->ctrl; - struct cmtp_application *application; - __u16 appl; - __u32 contr; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - if (skb->len < CAPI_MSG_BASELEN) - return; - - if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) { - cmtp_recv_interopmsg(session, skb); - return; - } - - if (session->flags & (1 << CMTP_LOOPBACK)) { - kfree_skb(skb); - return; - } - - appl = CAPIMSG_APPID(skb->data); - contr = CAPIMSG_CONTROL(skb->data); - - application = cmtp_application_get(session, CMTP_MAPPING, appl); - if (application) { - appl = application->appl; - CAPIMSG_SETAPPID(skb->data, appl); - } else { - BT_ERR("Can't find application with id %d", appl); - kfree_skb(skb); - return; - } - - if ((contr & 0x7f) == 0x01) { - contr = (contr & 0xffffff80) | session->num; - CAPIMSG_SETCONTROL(skb->data, contr); - } - - if (!ctrl) { - BT_ERR("Can't find controller %d for message", session->num); - kfree_skb(skb); - return; - } - - capi_ctr_handle_message(ctrl, appl, skb); -} - -static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) -{ - BT_DBG("ctrl %p data %p", ctrl, data); - - return 0; -} - -static void cmtp_reset_ctr(struct capi_ctr *ctrl) -{ - struct cmtp_session *session = ctrl->driverdata; - - BT_DBG("ctrl %p", ctrl); - - capi_ctr_down(ctrl); - - atomic_inc(&session->terminate); - wake_up_process(session->task); -} - -static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp) -{ - DECLARE_WAITQUEUE(wait, current); - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *application; - unsigned long timeo = CMTP_INTEROP_TIMEOUT; - unsigned char buf[8]; - int err = 0, nconn, want = rp->level3cnt; - - BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d", - ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen); - - application = cmtp_application_add(session, appl); - if (!application) { - BT_ERR("Can't allocate memory for new application"); - return; - } - - if (want < 0) - nconn = ctrl->profile.nbchannel * -want; - else - nconn = want; - - if (nconn == 0) - nconn = ctrl->profile.nbchannel; - - capimsg_setu16(buf, 0, nconn); - capimsg_setu16(buf, 2, rp->datablkcnt); - capimsg_setu16(buf, 4, rp->datablklen); - - application->state = BT_CONFIG; - application->msgnum = cmtp_msgnum_get(session); - - cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum, - CAPI_FUNCTION_REGISTER, buf, 6); - - add_wait_queue(&session->wait, &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (!timeo) { - err = -EAGAIN; - break; - } - - if (application->state == BT_CLOSED) { - err = -application->err; - break; - } - - if (application->state == BT_CONNECTED) - break; - - if (signal_pending(current)) { - err = -EINTR; - break; - } - - timeo = schedule_timeout(timeo); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&session->wait, &wait); - - if (err) { - cmtp_application_del(session, application); - return; - } -} - -static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl) -{ - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *application; - - BT_DBG("ctrl %p appl %d", ctrl, appl); - - application = cmtp_application_get(session, CMTP_APPLID, appl); - if (!application) { - BT_ERR("Can't find application"); - return; - } - - application->msgnum = cmtp_msgnum_get(session); - - cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum, - CAPI_FUNCTION_RELEASE, NULL, 0); - - wait_event_interruptible_timeout(session->wait, - (application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT); - - cmtp_application_del(session, application); -} - -static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) -{ - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *application; - __u16 appl; - __u32 contr; - - BT_DBG("ctrl %p skb %p", ctrl, skb); - - appl = CAPIMSG_APPID(skb->data); - contr = CAPIMSG_CONTROL(skb->data); - - application = cmtp_application_get(session, CMTP_APPLID, appl); - if ((!application) || (application->state != BT_CONNECTED)) { - BT_ERR("Can't find application with id %d", appl); - return CAPI_ILLAPPNR; - } - - CAPIMSG_SETAPPID(skb->data, application->mapping); - - if ((contr & 0x7f) == session->num) { - contr = (contr & 0xffffff80) | 0x01; - CAPIMSG_SETCONTROL(skb->data, contr); - } - - cmtp_send_capimsg(session, skb); - - return CAPI_NOERROR; -} - -static char *cmtp_procinfo(struct capi_ctr *ctrl) -{ - return "CAPI Message Transport Protocol"; -} - -static int cmtp_proc_show(struct seq_file *m, void *v) -{ - struct capi_ctr *ctrl = m->private; - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *app; - struct list_head *p, *n; - - seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl)); - seq_printf(m, "addr %s\n", session->name); - seq_printf(m, "ctrl %d\n", session->num); - - list_for_each_safe(p, n, &session->applications) { - app = list_entry(p, struct cmtp_application, list); - seq_printf(m, "appl %d -> %d\n", app->appl, app->mapping); - } - - return 0; -} - -static int cmtp_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cmtp_proc_show, PDE(inode)->data); -} - -static const struct file_operations cmtp_proc_fops = { - .owner = THIS_MODULE, - .open = cmtp_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -int cmtp_attach_device(struct cmtp_session *session) -{ - unsigned char buf[4]; - long ret; - - BT_DBG("session %p", session); - - capimsg_setu32(buf, 0, 0); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM, - CAPI_FUNCTION_GET_PROFILE, buf, 4); - - ret = wait_event_interruptible_timeout(session->wait, - session->ncontroller, CMTP_INTEROP_TIMEOUT); - - BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name); - - if (!ret) - return -ETIMEDOUT; - - if (!session->ncontroller) - return -ENODEV; - - if (session->ncontroller > 1) - BT_INFO("Setting up only CAPI controller 1"); - - session->ctrl.owner = THIS_MODULE; - session->ctrl.driverdata = session; - strcpy(session->ctrl.name, session->name); - - session->ctrl.driver_name = "cmtp"; - session->ctrl.load_firmware = cmtp_load_firmware; - session->ctrl.reset_ctr = cmtp_reset_ctr; - session->ctrl.register_appl = cmtp_register_appl; - session->ctrl.release_appl = cmtp_release_appl; - session->ctrl.send_message = cmtp_send_message; - - session->ctrl.procinfo = cmtp_procinfo; - session->ctrl.proc_fops = &cmtp_proc_fops; - - if (attach_capi_ctr(&session->ctrl) < 0) { - BT_ERR("Can't attach new controller"); - return -EBUSY; - } - - session->num = session->ctrl.cnr; - - BT_DBG("session %p num %d", session, session->num); - - capimsg_setu32(buf, 0, 1); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_MANUFACTURER, buf, 4); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_VERSION, buf, 4); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_PROFILE, buf, 4); - - return 0; -} - -void cmtp_detach_device(struct cmtp_session *session) -{ - BT_DBG("session %p", session); - - detach_capi_ctr(&session->ctrl); -} diff --git a/net/bluetooth_tizen/cmtp/cmtp.h b/net/bluetooth_tizen/cmtp/cmtp.h deleted file mode 100644 index c32638d..0000000 --- a/net/bluetooth_tizen/cmtp/cmtp.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#ifndef __CMTP_H -#define __CMTP_H - -#include <linux/types.h> -#include <net/bluetooth/bluetooth.h> - -#define BTNAMSIZ 18 - -/* CMTP ioctl defines */ -#define CMTPCONNADD _IOW('C', 200, int) -#define CMTPCONNDEL _IOW('C', 201, int) -#define CMTPGETCONNLIST _IOR('C', 210, int) -#define CMTPGETCONNINFO _IOR('C', 211, int) - -#define CMTP_LOOPBACK 0 - -struct cmtp_connadd_req { - int sock; /* Connected socket */ - __u32 flags; -}; - -struct cmtp_conndel_req { - bdaddr_t bdaddr; - __u32 flags; -}; - -struct cmtp_conninfo { - bdaddr_t bdaddr; - __u32 flags; - __u16 state; - int num; -}; - -struct cmtp_connlist_req { - __u32 cnum; - struct cmtp_conninfo __user *ci; -}; - -int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock); -int cmtp_del_connection(struct cmtp_conndel_req *req); -int cmtp_get_connlist(struct cmtp_connlist_req *req); -int cmtp_get_conninfo(struct cmtp_conninfo *ci); - -/* CMTP session defines */ -#define CMTP_INTEROP_TIMEOUT (HZ * 5) -#define CMTP_INITIAL_MSGNUM 0xff00 - -struct cmtp_session { - struct list_head list; - - struct socket *sock; - - bdaddr_t bdaddr; - - unsigned long state; - unsigned long flags; - - uint mtu; - - char name[BTNAMSIZ]; - - atomic_t terminate; - struct task_struct *task; - - wait_queue_head_t wait; - - int ncontroller; - int num; - struct capi_ctr ctrl; - - struct list_head applications; - - unsigned long blockids; - int msgnum; - - struct sk_buff_head transmit; - - struct sk_buff *reassembly[16]; -}; - -struct cmtp_application { - struct list_head list; - - unsigned long state; - int err; - - __u16 appl; - __u16 mapping; - - __u16 msgnum; -}; - -struct cmtp_scb { - int id; - int data; -}; - -int cmtp_attach_device(struct cmtp_session *session); -void cmtp_detach_device(struct cmtp_session *session); - -void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb); - -/* CMTP init defines */ -int cmtp_init_sockets(void); -void cmtp_cleanup_sockets(void); - -#endif /* __CMTP_H */ diff --git a/net/bluetooth_tizen/cmtp/core.c b/net/bluetooth_tizen/cmtp/core.c deleted file mode 100644 index 6c9c1fd..0000000 --- a/net/bluetooth_tizen/cmtp/core.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/freezer.h> -#include <linux/skbuff.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <linux/file.h> -#include <linux/init.h> -#include <linux/kthread.h> -#include <net/sock.h> - -#include <linux/isdn/capilli.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/l2cap.h> - -#include "cmtp.h" - -#define VERSION "1.0" - -static DECLARE_RWSEM(cmtp_session_sem); -static LIST_HEAD(cmtp_session_list); - -static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr) -{ - struct cmtp_session *session; - - BT_DBG(""); - - list_for_each_entry(session, &cmtp_session_list, list) - if (!bacmp(bdaddr, &session->bdaddr)) - return session; - - return NULL; -} - -static void __cmtp_link_session(struct cmtp_session *session) -{ - list_add(&session->list, &cmtp_session_list); -} - -static void __cmtp_unlink_session(struct cmtp_session *session) -{ - list_del(&session->list); -} - -static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci) -{ - memset(ci, 0, sizeof(*ci)); - bacpy(&ci->bdaddr, &session->bdaddr); - - ci->flags = session->flags; - ci->state = session->state; - - ci->num = session->num; -} - - -static inline int cmtp_alloc_block_id(struct cmtp_session *session) -{ - int i, id = -1; - - for (i = 0; i < 16; i++) - if (!test_and_set_bit(i, &session->blockids)) { - id = i; - break; - } - - return id; -} - -static inline void cmtp_free_block_id(struct cmtp_session *session, int id) -{ - clear_bit(id, &session->blockids); -} - -static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count) -{ - struct sk_buff *skb = session->reassembly[id], *nskb; - int size; - - BT_DBG("session %p buf %p count %d", session, buf, count); - - size = (skb) ? skb->len + count : count; - - nskb = alloc_skb(size, GFP_ATOMIC); - if (!nskb) { - BT_ERR("Can't allocate memory for CAPI message"); - return; - } - - if (skb && (skb->len > 0)) - skb_copy_from_linear_data(skb, skb_put(nskb, skb->len), skb->len); - - memcpy(skb_put(nskb, count), buf, count); - - session->reassembly[id] = nskb; - - kfree_skb(skb); -} - -static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb) -{ - __u8 hdr, hdrlen, id; - __u16 len; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - while (skb->len > 0) { - hdr = skb->data[0]; - - switch (hdr & 0xc0) { - case 0x40: - hdrlen = 2; - len = skb->data[1]; - break; - case 0x80: - hdrlen = 3; - len = skb->data[1] | (skb->data[2] << 8); - break; - default: - hdrlen = 1; - len = 0; - break; - } - - id = (hdr & 0x3c) >> 2; - - BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id); - - if (hdrlen + len > skb->len) { - BT_ERR("Wrong size or header information in CMTP frame"); - break; - } - - if (len == 0) { - skb_pull(skb, hdrlen); - continue; - } - - switch (hdr & 0x03) { - case 0x00: - cmtp_add_msgpart(session, id, skb->data + hdrlen, len); - cmtp_recv_capimsg(session, session->reassembly[id]); - session->reassembly[id] = NULL; - break; - case 0x01: - cmtp_add_msgpart(session, id, skb->data + hdrlen, len); - break; - default: - if (session->reassembly[id] != NULL) - kfree_skb(session->reassembly[id]); - session->reassembly[id] = NULL; - break; - } - - skb_pull(skb, hdrlen + len); - } - - kfree_skb(skb); - return 0; -} - -static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len) -{ - struct socket *sock = session->sock; - struct kvec iv = { data, len }; - struct msghdr msg; - - BT_DBG("session %p data %p len %d", session, data, len); - - if (!len) - return 0; - - memset(&msg, 0, sizeof(msg)); - - return kernel_sendmsg(sock, &msg, &iv, 1, len); -} - -static void cmtp_process_transmit(struct cmtp_session *session) -{ - struct sk_buff *skb, *nskb; - unsigned char *hdr; - unsigned int size, tail; - - BT_DBG("session %p", session); - - nskb = alloc_skb(session->mtu, GFP_ATOMIC); - if (!nskb) { - BT_ERR("Can't allocate memory for new frame"); - return; - } - - while ((skb = skb_dequeue(&session->transmit))) { - struct cmtp_scb *scb = (void *) skb->cb; - - tail = session->mtu - nskb->len; - if (tail < 5) { - cmtp_send_frame(session, nskb->data, nskb->len); - skb_trim(nskb, 0); - tail = session->mtu; - } - - size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len); - - if (scb->id < 0) { - scb->id = cmtp_alloc_block_id(session); - if (scb->id < 0) { - skb_queue_head(&session->transmit, skb); - break; - } - } - - if (size < 256) { - hdr = skb_put(nskb, 2); - hdr[0] = 0x40 - | ((scb->id << 2) & 0x3c) - | ((skb->len == size) ? 0x00 : 0x01); - hdr[1] = size; - } else { - hdr = skb_put(nskb, 3); - hdr[0] = 0x80 - | ((scb->id << 2) & 0x3c) - | ((skb->len == size) ? 0x00 : 0x01); - hdr[1] = size & 0xff; - hdr[2] = size >> 8; - } - - skb_copy_from_linear_data(skb, skb_put(nskb, size), size); - skb_pull(skb, size); - - if (skb->len > 0) { - skb_queue_head(&session->transmit, skb); - } else { - cmtp_free_block_id(session, scb->id); - if (scb->data) { - cmtp_send_frame(session, nskb->data, nskb->len); - skb_trim(nskb, 0); - } - kfree_skb(skb); - } - } - - cmtp_send_frame(session, nskb->data, nskb->len); - - kfree_skb(nskb); -} - -static int cmtp_session(void *arg) -{ - struct cmtp_session *session = arg; - struct sock *sk = session->sock->sk; - struct sk_buff *skb; - wait_queue_t wait; - - BT_DBG("session %p", session); - - set_user_nice(current, -15); - - init_waitqueue_entry(&wait, current); - add_wait_queue(sk_sleep(sk), &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (atomic_read(&session->terminate)) - break; - if (sk->sk_state != BT_CONNECTED) - break; - - while ((skb = skb_dequeue(&sk->sk_receive_queue))) { - skb_orphan(skb); - if (!skb_linearize(skb)) - cmtp_recv_frame(session, skb); - else - kfree_skb(skb); - } - - cmtp_process_transmit(session); - - schedule(); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - down_write(&cmtp_session_sem); - - if (!(session->flags & (1 << CMTP_LOOPBACK))) - cmtp_detach_device(session); - - fput(session->sock->file); - - __cmtp_unlink_session(session); - - up_write(&cmtp_session_sem); - - kfree(session); - module_put_and_exit(0); - return 0; -} - -int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) -{ - struct cmtp_session *session, *s; - int i, err; - - BT_DBG(""); - - session = kzalloc(sizeof(struct cmtp_session), GFP_KERNEL); - if (!session) - return -ENOMEM; - - down_write(&cmtp_session_sem); - - s = __cmtp_get_session(&bt_sk(sock->sk)->dst); - if (s && s->state == BT_CONNECTED) { - err = -EEXIST; - goto failed; - } - - bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst); - - session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu, - l2cap_pi(sock->sk)->chan->imtu); - - BT_DBG("mtu %d", session->mtu); - - sprintf(session->name, "%s", batostr(&bt_sk(sock->sk)->dst)); - - session->sock = sock; - session->state = BT_CONFIG; - - init_waitqueue_head(&session->wait); - - session->msgnum = CMTP_INITIAL_MSGNUM; - - INIT_LIST_HEAD(&session->applications); - - skb_queue_head_init(&session->transmit); - - for (i = 0; i < 16; i++) - session->reassembly[i] = NULL; - - session->flags = req->flags; - - __cmtp_link_session(session); - - __module_get(THIS_MODULE); - session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d", - session->num); - if (IS_ERR(session->task)) { - module_put(THIS_MODULE); - err = PTR_ERR(session->task); - goto unlink; - } - - if (!(session->flags & (1 << CMTP_LOOPBACK))) { - err = cmtp_attach_device(session); - if (err < 0) { - atomic_inc(&session->terminate); - wake_up_process(session->task); - up_write(&cmtp_session_sem); - return err; - } - } - - up_write(&cmtp_session_sem); - return 0; - -unlink: - __cmtp_unlink_session(session); - -failed: - up_write(&cmtp_session_sem); - kfree(session); - return err; -} - -int cmtp_del_connection(struct cmtp_conndel_req *req) -{ - struct cmtp_session *session; - int err = 0; - - BT_DBG(""); - - down_read(&cmtp_session_sem); - - session = __cmtp_get_session(&req->bdaddr); - if (session) { - /* Flush the transmit queue */ - skb_queue_purge(&session->transmit); - - /* Stop session thread */ - atomic_inc(&session->terminate); - wake_up_process(session->task); - } else - err = -ENOENT; - - up_read(&cmtp_session_sem); - return err; -} - -int cmtp_get_connlist(struct cmtp_connlist_req *req) -{ - struct cmtp_session *session; - int err = 0, n = 0; - - BT_DBG(""); - - down_read(&cmtp_session_sem); - - list_for_each_entry(session, &cmtp_session_list, list) { - struct cmtp_conninfo ci; - - __cmtp_copy_session(session, &ci); - - if (copy_to_user(req->ci, &ci, sizeof(ci))) { - err = -EFAULT; - break; - } - - if (++n >= req->cnum) - break; - - req->ci++; - } - req->cnum = n; - - up_read(&cmtp_session_sem); - return err; -} - -int cmtp_get_conninfo(struct cmtp_conninfo *ci) -{ - struct cmtp_session *session; - int err = 0; - - down_read(&cmtp_session_sem); - - session = __cmtp_get_session(&ci->bdaddr); - if (session) - __cmtp_copy_session(session, ci); - else - err = -ENOENT; - - up_read(&cmtp_session_sem); - return err; -} - - -static int __init cmtp_init(void) -{ - BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION); - - cmtp_init_sockets(); - - return 0; -} - -static void __exit cmtp_exit(void) -{ - cmtp_cleanup_sockets(); -} - -module_init(cmtp_init); -module_exit(cmtp_exit); - -MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -MODULE_DESCRIPTION("Bluetooth CMTP ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("bt-proto-5"); diff --git a/net/bluetooth_tizen/cmtp/sock.c b/net/bluetooth_tizen/cmtp/sock.c deleted file mode 100644 index 1230faa..0000000 --- a/net/bluetooth_tizen/cmtp/sock.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/capability.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/skbuff.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <linux/file.h> -#include <linux/compat.h> -#include <linux/gfp.h> -#include <linux/uaccess.h> -#include <net/sock.h> - -#include <linux/isdn/capilli.h> - -#include <asm/system.h> - -#include "cmtp.h" - -static int cmtp_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!sk) - return 0; - - sock_orphan(sk); - sock_put(sk); - - return 0; -} - -static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct cmtp_connadd_req ca; - struct cmtp_conndel_req cd; - struct cmtp_connlist_req cl; - struct cmtp_conninfo ci; - struct socket *nsock; - void __user *argp = (void __user *)arg; - int err; - - BT_DBG("cmd %x arg %lx", cmd, arg); - - switch (cmd) { - case CMTPCONNADD: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (copy_from_user(&ca, argp, sizeof(ca))) - return -EFAULT; - - nsock = sockfd_lookup(ca.sock, &err); - if (!nsock) - return err; - - if (nsock->sk->sk_state != BT_CONNECTED) { - sockfd_put(nsock); - return -EBADFD; - } - - err = cmtp_add_connection(&ca, nsock); - if (!err) { - if (copy_to_user(argp, &ca, sizeof(ca))) - err = -EFAULT; - } else - sockfd_put(nsock); - - return err; - - case CMTPCONNDEL: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (copy_from_user(&cd, argp, sizeof(cd))) - return -EFAULT; - - return cmtp_del_connection(&cd); - - case CMTPGETCONNLIST: - if (copy_from_user(&cl, argp, sizeof(cl))) - return -EFAULT; - - if (cl.cnum <= 0) - return -EINVAL; - - err = cmtp_get_connlist(&cl); - if (!err && copy_to_user(argp, &cl, sizeof(cl))) - return -EFAULT; - - return err; - - case CMTPGETCONNINFO: - if (copy_from_user(&ci, argp, sizeof(ci))) - return -EFAULT; - - err = cmtp_get_conninfo(&ci); - if (!err && copy_to_user(argp, &ci, sizeof(ci))) - return -EFAULT; - - return err; - } - - return -EINVAL; -} - -#ifdef CONFIG_COMPAT -static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - if (cmd == CMTPGETCONNLIST) { - struct cmtp_connlist_req cl; - u32 uci; - int err; - - if (get_user(cl.cnum, (u32 __user *) arg) || - get_user(uci, (u32 __user *) (arg + 4))) - return -EFAULT; - - cl.ci = compat_ptr(uci); - - if (cl.cnum <= 0) - return -EINVAL; - - err = cmtp_get_connlist(&cl); - - if (!err && put_user(cl.cnum, (u32 __user *) arg)) - err = -EFAULT; - - return err; - } - - return cmtp_sock_ioctl(sock, cmd, arg); -} -#endif - -static const struct proto_ops cmtp_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = cmtp_sock_release, - .ioctl = cmtp_sock_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = cmtp_sock_compat_ioctl, -#endif - .bind = sock_no_bind, - .getname = sock_no_getname, - .sendmsg = sock_no_sendmsg, - .recvmsg = sock_no_recvmsg, - .poll = sock_no_poll, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = sock_no_setsockopt, - .getsockopt = sock_no_getsockopt, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - -static struct proto cmtp_proto = { - .name = "CMTP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct bt_sock) -}; - -static int cmtp_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - if (sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &cmtp_proto); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - - sock->ops = &cmtp_sock_ops; - - sock->state = SS_UNCONNECTED; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = protocol; - sk->sk_state = BT_OPEN; - - return 0; -} - -static const struct net_proto_family cmtp_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = cmtp_sock_create -}; - -int cmtp_init_sockets(void) -{ - int err; - - err = proto_register(&cmtp_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops); - if (err < 0) - goto error; - - return 0; - -error: - BT_ERR("Can't register CMTP socket"); - proto_unregister(&cmtp_proto); - return err; -} - -void cmtp_cleanup_sockets(void) -{ - if (bt_sock_unregister(BTPROTO_CMTP) < 0) - BT_ERR("Can't unregister CMTP socket"); - - proto_unregister(&cmtp_proto); -} diff --git a/net/bluetooth_tizen/hci_conn.c b/net/bluetooth_tizen/hci_conn.c deleted file mode 100644 index 459227d..0000000 --- a/net/bluetooth_tizen/hci_conn.c +++ /dev/null @@ -1,984 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth HCI connection handling. */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/skbuff.h> -#include <linux/interrupt.h> -#include <linux/notifier.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <linux/uaccess.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> - -static void hci_le_connect(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_cp_le_create_conn cp; - - conn->state = BT_CONNECT; - conn->out = true; - conn->link_mode |= HCI_LM_MASTER; - conn->sec_level = BT_SECURITY_LOW; - - memset(&cp, 0, sizeof(cp)); - cp.scan_interval = cpu_to_le16(0x0060); - cp.scan_window = cpu_to_le16(0x0030); - bacpy(&cp.peer_addr, &conn->dst); - cp.peer_addr_type = conn->dst_type; - cp.conn_interval_min = cpu_to_le16(0x0028); - cp.conn_interval_max = cpu_to_le16(0x0038); - cp.supervision_timeout = cpu_to_le16(0x002a); - cp.min_ce_len = cpu_to_le16(0x0000); - cp.max_ce_len = cpu_to_le16(0x0000); - - hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp); -} - -static void hci_le_connect_cancel(struct hci_conn *conn) -{ - hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL); -} - -void hci_acl_connect(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - struct inquiry_entry *ie; - struct hci_cp_create_conn cp; - - BT_DBG("hcon %p", conn); - - conn->state = BT_CONNECT; - conn->out = true; - - conn->link_mode = HCI_LM_MASTER; - - conn->attempt++; - - conn->link_policy = hdev->link_policy; - - memset(&cp, 0, sizeof(cp)); - bacpy(&cp.bdaddr, &conn->dst); - cp.pscan_rep_mode = 0x02; - - ie = hci_inquiry_cache_lookup(hdev, &conn->dst); - if (ie) { - if (inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) { - cp.pscan_rep_mode = ie->data.pscan_rep_mode; - cp.pscan_mode = ie->data.pscan_mode; - cp.clock_offset = ie->data.clock_offset | - cpu_to_le16(0x8000); - } - - memcpy(conn->dev_class, ie->data.dev_class, 3); - if (ie->data.ssp_mode > 0) - set_bit(HCI_CONN_SSP_ENABLED, &conn->flags); - } - - cp.pkt_type = cpu_to_le16(conn->pkt_type); - if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER)) - cp.role_switch = 0x01; - else - cp.role_switch = 0x00; - - hci_send_cmd(hdev, HCI_OP_CREATE_CONN, sizeof(cp), &cp); -} - -static void hci_acl_connect_cancel(struct hci_conn *conn) -{ - struct hci_cp_create_conn_cancel cp; - - BT_DBG("%p", conn); - - if (conn->hdev->hci_ver < BLUETOOTH_VER_1_2) - return; - - bacpy(&cp.bdaddr, &conn->dst); - hci_send_cmd(conn->hdev, HCI_OP_CREATE_CONN_CANCEL, sizeof(cp), &cp); -} - -void hci_acl_disconn(struct hci_conn *conn, __u8 reason) -{ - struct hci_cp_disconnect cp; - - BT_DBG("%p", conn); - - conn->state = BT_DISCONN; - - cp.handle = cpu_to_le16(conn->handle); - cp.reason = reason; - hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp); -} - -void hci_add_sco(struct hci_conn *conn, __u16 handle) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_cp_add_sco cp; - - BT_DBG("%p", conn); - - conn->state = BT_CONNECT; - conn->out = true; - - conn->attempt++; - - cp.handle = cpu_to_le16(handle); - cp.pkt_type = cpu_to_le16(conn->pkt_type); - - hci_send_cmd(hdev, HCI_OP_ADD_SCO, sizeof(cp), &cp); -} - -void hci_setup_sync(struct hci_conn *conn, __u16 handle) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_cp_setup_sync_conn cp; - - BT_DBG("%p", conn); - - conn->state = BT_CONNECT; - conn->out = true; - - conn->attempt++; - - cp.handle = cpu_to_le16(handle); - cp.pkt_type = cpu_to_le16(conn->pkt_type); - - cp.tx_bandwidth = cpu_to_le32(0x00001f40); - cp.rx_bandwidth = cpu_to_le32(0x00001f40); - cp.max_latency = cpu_to_le16(0xffff); - cp.voice_setting = cpu_to_le16(hdev->voice_setting); - cp.retrans_effort = 0xff; - - hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp); -} - -void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, - u16 latency, u16 to_multiplier) -{ - struct hci_cp_le_conn_update cp; - struct hci_dev *hdev = conn->hdev; - - memset(&cp, 0, sizeof(cp)); - - cp.handle = cpu_to_le16(conn->handle); - cp.conn_interval_min = cpu_to_le16(min); - cp.conn_interval_max = cpu_to_le16(max); - cp.conn_latency = cpu_to_le16(latency); - cp.supervision_timeout = cpu_to_le16(to_multiplier); - cp.min_ce_len = cpu_to_le16(0x0001); - cp.max_ce_len = cpu_to_le16(0x0001); - - hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp); -} -EXPORT_SYMBOL(hci_le_conn_update); - -void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], - __u8 ltk[16]) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_cp_le_start_enc cp; - - BT_DBG("%p", conn); - - memset(&cp, 0, sizeof(cp)); - - cp.handle = cpu_to_le16(conn->handle); - memcpy(cp.ltk, ltk, sizeof(cp.ltk)); - cp.ediv = ediv; - memcpy(cp.rand, rand, sizeof(cp.rand)); - - hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp); -} -EXPORT_SYMBOL(hci_le_start_enc); - -void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_cp_le_ltk_reply cp; - - BT_DBG("%p", conn); - - memset(&cp, 0, sizeof(cp)); - - cp.handle = cpu_to_le16(conn->handle); - memcpy(cp.ltk, ltk, sizeof(ltk)); - - hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); -} -EXPORT_SYMBOL(hci_le_ltk_reply); - -void hci_le_ltk_neg_reply(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_cp_le_ltk_neg_reply cp; - - BT_DBG("%p", conn); - - memset(&cp, 0, sizeof(cp)); - - cp.handle = cpu_to_le16(conn->handle); - - hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(cp), &cp); -} - -/* Device _must_ be locked */ -void hci_sco_setup(struct hci_conn *conn, __u8 status) -{ - struct hci_conn *sco = conn->link; - - BT_DBG("%p", conn); - - if (!sco) - return; - - if (!status) { - if (lmp_esco_capable(conn->hdev)) - hci_setup_sync(sco, conn->handle); - else - hci_add_sco(sco, conn->handle); - } else { - hci_proto_connect_cfm(sco, status); - hci_conn_del(sco); - } -} - -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("conn %p state %s", conn, state_to_string(conn->state)); - - if (atomic_read(&conn->refcnt)) - return; - - switch (conn->state) { - case BT_CONNECT: - case BT_CONNECT2: - if (conn->out) { - if (conn->type == ACL_LINK) - hci_acl_connect_cancel(conn); - else if (conn->type == LE_LINK) - hci_le_connect_cancel(conn); - } - break; - case BT_CONFIG: - case BT_CONNECTED: - reason = hci_proto_disconn_ind(conn); - hci_acl_disconn(conn, reason); - break; - default: - conn->state = BT_CLOSED; - break; - } -} - -/* Enter sniff mode */ -static void hci_conn_enter_sniff_mode(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - - BT_DBG("conn %p mode %d", conn, conn->mode); - - if (test_bit(HCI_RAW, &hdev->flags)) - return; - - if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn)) - return; - - if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF)) - return; - - if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) { - struct hci_cp_sniff_subrate cp; - cp.handle = cpu_to_le16(conn->handle); - cp.max_latency = cpu_to_le16(0); - cp.min_remote_timeout = cpu_to_le16(0); - cp.min_local_timeout = cpu_to_le16(0); - hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp); - } - - if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { - struct hci_cp_sniff_mode cp; - cp.handle = cpu_to_le16(conn->handle); - cp.max_interval = cpu_to_le16(hdev->sniff_max_interval); - cp.min_interval = cpu_to_le16(hdev->sniff_min_interval); - cp.attempt = cpu_to_le16(4); - cp.timeout = cpu_to_le16(1); - hci_send_cmd(hdev, HCI_OP_SNIFF_MODE, sizeof(cp), &cp); - } -} - -static void hci_conn_idle(unsigned long arg) -{ - struct hci_conn *conn = (void *) arg; - - BT_DBG("conn %p mode %d", conn, conn->mode); - - hci_conn_enter_sniff_mode(conn); -} - -static void hci_conn_auto_accept(unsigned long arg) -{ - struct hci_conn *conn = (void *) arg; - struct hci_dev *hdev = conn->hdev; - - hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst), - &conn->dst); -} - -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)); - - conn = kzalloc(sizeof(struct hci_conn), GFP_KERNEL); - if (!conn) - return NULL; - - bacpy(&conn->dst, dst); - conn->hdev = hdev; - conn->type = type; - conn->mode = HCI_CM_ACTIVE; - conn->state = BT_OPEN; - conn->auth_type = HCI_AT_GENERAL_BONDING; - conn->io_capability = hdev->io_capability; - conn->remote_auth = 0xff; - conn->key_type = 0xff; - - set_bit(HCI_CONN_POWER_SAVE, &conn->flags); - conn->disc_timeout = HCI_DISCONN_TIMEOUT; - - switch (type) { - case ACL_LINK: - conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK; - break; - case SCO_LINK: - if (lmp_esco_capable(hdev)) - conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | - (hdev->esco_type & EDR_ESCO_MASK); - else - conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK; - break; - case ESCO_LINK: - conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK; - break; - } - - skb_queue_head_init(&conn->data_q); - - INIT_LIST_HEAD(&conn->chan_list); - - INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout); - setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn); - setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept, - (unsigned long) conn); - - atomic_set(&conn->refcnt, 0); - - hci_dev_hold(hdev); - - hci_conn_hash_add(hdev, conn); - if (hdev->notify) - hdev->notify(hdev, HCI_NOTIFY_CONN_ADD); - - atomic_set(&conn->devref, 0); - - hci_conn_init_sysfs(conn); - - return conn; -} - -int hci_conn_del(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - - BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle); - - del_timer(&conn->idle_timer); - - cancel_delayed_work_sync(&conn->disc_work); - - del_timer(&conn->auto_accept_timer); - - if (conn->type == ACL_LINK) { - struct hci_conn *sco = conn->link; - if (sco) - sco->link = NULL; - - /* Unacked frames */ - hdev->acl_cnt += conn->sent; - } else if (conn->type == LE_LINK) { - if (hdev->le_pkts) - hdev->le_cnt += conn->sent; - else - hdev->acl_cnt += conn->sent; - } else { - struct hci_conn *acl = conn->link; - if (acl) { - acl->link = NULL; - hci_conn_put(acl); - } - } - - - hci_chan_list_flush(conn); - - hci_conn_hash_del(hdev, conn); - if (hdev->notify) - hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); - - skb_queue_purge(&conn->data_q); - - hci_conn_put_device(conn); - - hci_dev_put(hdev); - - if (conn->handle == 0) - kfree(conn); - - return 0; -} - -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)); - - read_lock(&hci_dev_list_lock); - - list_for_each_entry(d, &hci_dev_list, list) { - if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags)) - continue; - - /* Simple routing: - * No source address - find interface with bdaddr != dst - * Source address - find interface with bdaddr == src - */ - - if (use_src) { - if (!bacmp(&d->bdaddr, src)) { - hdev = d; break; - } - } else { - if (bacmp(&d->bdaddr, dst)) { - hdev = d; break; - } - } - } - - if (hdev) - hdev = hci_dev_hold(hdev); - - read_unlock(&hci_dev_list_lock); - return hdev; -} -EXPORT_SYMBOL(hci_get_route); - -/* Create SCO, ACL or LE connection. - * Device _must_ be locked */ -struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type) -{ - struct hci_conn *acl; - struct hci_conn *sco; - struct hci_conn *le; - - BT_DBG("%s dst %s", hdev->name, batostr(dst)); - - if (type == LE_LINK) { - struct adv_entry *entry; - - le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); - if (le) - return ERR_PTR(-EBUSY); - - entry = hci_find_adv_entry(hdev, dst); - if (!entry) - return ERR_PTR(-EHOSTUNREACH); - - le = hci_conn_add(hdev, LE_LINK, dst); - if (!le) - return ERR_PTR(-ENOMEM); - - le->dst_type = entry->bdaddr_type; - - hci_le_connect(le); - - hci_conn_hold(le); - - return le; - } - - acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); - if (!acl) { - acl = hci_conn_add(hdev, ACL_LINK, dst); - if (!acl) - return ERR_PTR(-ENOMEM); - } - - hci_conn_hold(acl); - - if (acl->state == BT_OPEN || acl->state == BT_CLOSED) { - acl->sec_level = BT_SECURITY_LOW; - acl->pending_sec_level = sec_level; - acl->auth_type = auth_type; - hci_acl_connect(acl); - } - - if (type == ACL_LINK) - return acl; - - sco = hci_conn_hash_lookup_ba(hdev, type, dst); - if (!sco) { - sco = hci_conn_add(hdev, type, dst); - if (!sco) { - hci_conn_put(acl); - return ERR_PTR(-ENOMEM); - } - } - - acl->link = sco; - sco->link = acl; - - hci_conn_hold(sco); - - if (acl->state == BT_CONNECTED && - (sco->state == BT_OPEN || sco->state == BT_CLOSED)) { - set_bit(HCI_CONN_POWER_SAVE, &acl->flags); - hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON); - - if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->flags)) { - /* defer SCO setup until mode change completed */ - set_bit(HCI_CONN_SCO_SETUP_PEND, &acl->flags); - return sco; - } - - hci_sco_setup(acl, 0x00); - } - - return sco; -} -EXPORT_SYMBOL(hci_connect); - -/* Check link security requirement */ -int hci_conn_check_link_mode(struct hci_conn *conn) -{ - BT_DBG("conn %p", conn); - - if (hci_conn_ssp_enabled(conn) && !(conn->link_mode & HCI_LM_ENCRYPT)) - return 0; - - return 1; -} -EXPORT_SYMBOL(hci_conn_check_link_mode); - -/* Authenticate remote device */ -static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) -{ - BT_DBG("conn %p", conn); - - if (conn->pending_sec_level > sec_level) - sec_level = conn->pending_sec_level; - - if (sec_level > conn->sec_level) - conn->pending_sec_level = sec_level; - else if (conn->link_mode & HCI_LM_AUTH) - return 1; - - /* Make sure we preserve an existing MITM requirement*/ - auth_type |= (conn->auth_type & 0x01); - - conn->auth_type = auth_type; - - if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { - struct hci_cp_auth_requested cp; - - /* encrypt must be pending if auth is also pending */ - set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); - - cp.handle = cpu_to_le16(conn->handle); - hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED, - sizeof(cp), &cp); - if (conn->key_type != 0xff) - set_bit(HCI_CONN_REAUTH_PEND, &conn->flags); - } - - return 0; -} - -/* Encrypt the the link */ -static void hci_conn_encrypt(struct hci_conn *conn) -{ - BT_DBG("conn %p", conn); - - if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) { - struct hci_cp_set_conn_encrypt cp; - cp.handle = cpu_to_le16(conn->handle); - cp.encrypt = 0x01; - hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), - &cp); - } -} - -/* Enable security */ -int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) -{ - BT_DBG("conn %p", conn); - - /* For sdp we don't need the link key. */ - if (sec_level == BT_SECURITY_SDP) - return 1; - - /* For non 2.1 devices and low security level we don't need the link - key. */ - if (sec_level == BT_SECURITY_LOW && !hci_conn_ssp_enabled(conn)) - return 1; - - /* For other security levels we need the link key. */ - if (!(conn->link_mode & HCI_LM_AUTH)) - goto auth; - - /* An authenticated combination key has sufficient security for any - security level. */ - if (conn->key_type == HCI_LK_AUTH_COMBINATION) - goto encrypt; - - /* An unauthenticated combination key has sufficient security for - security level 1 and 2. */ - if (conn->key_type == HCI_LK_UNAUTH_COMBINATION && - (sec_level == BT_SECURITY_MEDIUM || - sec_level == BT_SECURITY_LOW)) - goto encrypt; - - /* A combination key has always sufficient security for the security - levels 1 or 2. High security level requires the combination key - is generated using maximum PIN code length (16). - For pre 2.1 units. */ - if (conn->key_type == HCI_LK_COMBINATION && - (sec_level != BT_SECURITY_HIGH || - conn->pin_length == 16)) - goto encrypt; - -auth: - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) - return 0; - - if (!hci_conn_auth(conn, sec_level, auth_type)) - return 0; - -encrypt: - if (conn->link_mode & HCI_LM_ENCRYPT) - return 1; - - hci_conn_encrypt(conn); - return 0; -} -EXPORT_SYMBOL(hci_conn_security); - -/* Check secure link requirement */ -int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level) -{ - BT_DBG("conn %p", conn); - - if (sec_level != BT_SECURITY_HIGH) - return 1; /* Accept if non-secure is required */ - - if (conn->sec_level == BT_SECURITY_HIGH) - return 1; - - return 0; /* Reject not secure link */ -} -EXPORT_SYMBOL(hci_conn_check_secure); - -/* Change link key */ -int hci_conn_change_link_key(struct hci_conn *conn) -{ - BT_DBG("conn %p", conn); - - if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { - struct hci_cp_change_conn_link_key cp; - cp.handle = cpu_to_le16(conn->handle); - hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY, - sizeof(cp), &cp); - } - - return 0; -} -EXPORT_SYMBOL(hci_conn_change_link_key); - -/* Switch role */ -int hci_conn_switch_role(struct hci_conn *conn, __u8 role) -{ - BT_DBG("conn %p", conn); - - if (!role && conn->link_mode & HCI_LM_MASTER) - return 1; - - if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) { - struct hci_cp_switch_role cp; - bacpy(&cp.bdaddr, &conn->dst); - cp.role = role; - hci_send_cmd(conn->hdev, HCI_OP_SWITCH_ROLE, sizeof(cp), &cp); - } - - return 0; -} -EXPORT_SYMBOL(hci_conn_switch_role); - -/* Enter active mode */ -void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) -{ - struct hci_dev *hdev = conn->hdev; - - BT_DBG("conn %p mode %d", conn, conn->mode); - - if (test_bit(HCI_RAW, &hdev->flags)) - return; - - if (conn->mode != HCI_CM_SNIFF) - goto timer; - - if (!test_bit(HCI_CONN_POWER_SAVE, &conn->flags) && !force_active) - goto timer; - - if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { - struct hci_cp_exit_sniff_mode cp; - cp.handle = cpu_to_le16(conn->handle); - hci_send_cmd(hdev, HCI_OP_EXIT_SNIFF_MODE, sizeof(cp), &cp); - } - -timer: - if (hdev->idle_timeout > 0) - mod_timer(&conn->idle_timer, - jiffies + msecs_to_jiffies(hdev->idle_timeout)); -} - -/* Drop all connection on the device */ -void hci_conn_hash_flush(struct hci_dev *hdev) -{ - struct hci_conn_hash *h = &hdev->conn_hash; - struct hci_conn *c, *n; - - BT_DBG("hdev %s", hdev->name); - - list_for_each_entry_safe(c, n, &h->list, list) { - c->state = BT_CLOSED; - - hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM); - hci_conn_del(c); - } -} - -/* Check pending connect attempts */ -void hci_conn_check_pending(struct hci_dev *hdev) -{ - struct hci_conn *conn; - - BT_DBG("hdev %s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECT2); - if (conn) - hci_acl_connect(conn); - - hci_dev_unlock(hdev); -} - -void hci_conn_hold_device(struct hci_conn *conn) -{ - atomic_inc(&conn->devref); -} -EXPORT_SYMBOL(hci_conn_hold_device); - -void hci_conn_put_device(struct hci_conn *conn) -{ - if (atomic_dec_and_test(&conn->devref)) - hci_conn_del_sysfs(conn); -} -EXPORT_SYMBOL(hci_conn_put_device); - -int hci_get_conn_list(void __user *arg) -{ - register struct hci_conn *c; - struct hci_conn_list_req req, *cl; - struct hci_conn_info *ci; - struct hci_dev *hdev; - int n = 0, size, err; - - if (copy_from_user(&req, arg, sizeof(req))) - return -EFAULT; - - if (!req.conn_num || req.conn_num > (PAGE_SIZE * 2) / sizeof(*ci)) - return -EINVAL; - - size = sizeof(req) + req.conn_num * sizeof(*ci); - - cl = kmalloc(size, GFP_KERNEL); - if (!cl) - return -ENOMEM; - - hdev = hci_dev_get(req.dev_id); - if (!hdev) { - kfree(cl); - return -ENODEV; - } - - ci = cl->conn_info; - - hci_dev_lock(hdev); - list_for_each_entry(c, &hdev->conn_hash.list, list) { - bacpy(&(ci + n)->bdaddr, &c->dst); - (ci + n)->handle = c->handle; - (ci + n)->type = c->type; - (ci + n)->out = c->out; - (ci + n)->state = c->state; - (ci + n)->link_mode = c->link_mode; - if (++n >= req.conn_num) - break; - } - hci_dev_unlock(hdev); - - cl->dev_id = hdev->id; - cl->conn_num = n; - size = sizeof(req) + n * sizeof(*ci); - - hci_dev_put(hdev); - - err = copy_to_user(arg, cl, size); - kfree(cl); - - return err ? -EFAULT : 0; -} - -int hci_get_conn_info(struct hci_dev *hdev, void __user *arg) -{ - struct hci_conn_info_req req; - struct hci_conn_info ci; - struct hci_conn *conn; - char __user *ptr = arg + sizeof(req); - - if (copy_from_user(&req, arg, sizeof(req))) - return -EFAULT; - - hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_ba(hdev, req.type, &req.bdaddr); - if (conn) { - bacpy(&ci.bdaddr, &conn->dst); - ci.handle = conn->handle; - ci.type = conn->type; - ci.out = conn->out; - ci.state = conn->state; - ci.link_mode = conn->link_mode; - } - hci_dev_unlock(hdev); - - if (!conn) - return -ENOENT; - - return copy_to_user(ptr, &ci, sizeof(ci)) ? -EFAULT : 0; -} - -int hci_get_auth_info(struct hci_dev *hdev, void __user *arg) -{ - struct hci_auth_info_req req; - struct hci_conn *conn; - - if (copy_from_user(&req, arg, sizeof(req))) - return -EFAULT; - - hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &req.bdaddr); - if (conn) - req.type = conn->auth_type; - hci_dev_unlock(hdev); - - if (!conn) - return -ENOENT; - - return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0; -} - -struct hci_chan *hci_chan_create(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_chan *chan; - - BT_DBG("%s conn %p", hdev->name, conn); - - chan = kzalloc(sizeof(struct hci_chan), GFP_KERNEL); - if (!chan) - return NULL; - - chan->conn = conn; - skb_queue_head_init(&chan->data_q); - - list_add_rcu(&chan->list, &conn->chan_list); - - return chan; -} - -int hci_chan_del(struct hci_chan *chan) -{ - struct hci_conn *conn = chan->conn; - struct hci_dev *hdev = conn->hdev; - - BT_DBG("%s conn %p chan %p", hdev->name, conn, chan); - - list_del_rcu(&chan->list); - - synchronize_rcu(); - - skb_queue_purge(&chan->data_q); - kfree(chan); - - return 0; -} - -void hci_chan_list_flush(struct hci_conn *conn) -{ - struct hci_chan *chan, *n; - - BT_DBG("conn %p", conn); - - list_for_each_entry_safe(chan, n, &conn->chan_list, list) - hci_chan_del(chan); -} diff --git a/net/bluetooth_tizen/hci_core.c b/net/bluetooth_tizen/hci_core.c deleted file mode 100644 index 1c69c54..0000000 --- a/net/bluetooth_tizen/hci_core.c +++ /dev/null @@ -1,2966 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - Copyright (C) 2011 ProFUSION Embedded Systems - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth HCI core. */ - -#include <linux/jiffies.h> -#include <linux/module.h> -#include <linux/kmod.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/skbuff.h> -#include <linux/workqueue.h> -#include <linux/interrupt.h> -#include <linux/notifier.h> -#include <linux/rfkill.h> -#include <linux/timer.h> -#include <linux/crypto.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <linux/uaccess.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> - -#define AUTO_OFF_TIMEOUT 2000 - -static void hci_rx_work(struct work_struct *work); -static void hci_cmd_work(struct work_struct *work); -static void hci_tx_work(struct work_struct *work); - -/* HCI device list */ -LIST_HEAD(hci_dev_list); -DEFINE_RWLOCK(hci_dev_list_lock); - -/* HCI callback list */ -LIST_HEAD(hci_cb_list); -DEFINE_RWLOCK(hci_cb_list_lock); - -/* HCI notifiers list */ -static ATOMIC_NOTIFIER_HEAD(hci_notifier); - -/* ---- HCI notifications ---- */ - -int hci_register_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&hci_notifier, nb); -} - -int hci_unregister_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&hci_notifier, nb); -} - -static void hci_notify(struct hci_dev *hdev, int event) -{ - atomic_notifier_call_chain(&hci_notifier, event, hdev); -} - -/* ---- HCI requests ---- */ - -void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result) -{ - BT_DBG("%s command 0x%04x result 0x%2.2x", hdev->name, cmd, result); - - /* If this is the init phase check if the completed command matches - * the last init command, and if not just return. - */ - if (test_bit(HCI_INIT, &hdev->flags) && hdev->init_last_cmd != cmd) { - struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data; - struct sk_buff *skb; - - /* Some CSR based controllers generate a spontaneous - * reset complete event during init and any pending - * command will never be completed. In such a case we - * need to resend whatever was the last sent - * command. - */ - - if (cmd != HCI_OP_RESET || sent->opcode == HCI_OP_RESET) - return; - - skb = skb_clone(hdev->sent_cmd, GFP_ATOMIC); - if (skb) { - skb_queue_head(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); - } - - return; - } - - if (hdev->req_status == HCI_REQ_PEND) { - hdev->req_result = result; - hdev->req_status = HCI_REQ_DONE; - wake_up_interruptible(&hdev->req_wait_q); - } -} - -static void hci_req_cancel(struct hci_dev *hdev, int err) -{ - BT_DBG("%s err 0x%2.2x", hdev->name, err); - - if (hdev->req_status == HCI_REQ_PEND) { - hdev->req_result = err; - hdev->req_status = HCI_REQ_CANCELED; - wake_up_interruptible(&hdev->req_wait_q); - } -} - -/* Execute request and wait for completion. */ -static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt), - unsigned long opt, __u32 timeout) -{ - DECLARE_WAITQUEUE(wait, current); - int err = 0; - - BT_DBG("%s start", hdev->name); - - hdev->req_status = HCI_REQ_PEND; - - add_wait_queue(&hdev->req_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - req(hdev, opt); - schedule_timeout(timeout); - - remove_wait_queue(&hdev->req_wait_q, &wait); - - if (signal_pending(current)) - return -EINTR; - - switch (hdev->req_status) { - case HCI_REQ_DONE: - err = -bt_to_errno(hdev->req_result); - break; - - case HCI_REQ_CANCELED: - err = -hdev->req_result; - break; - - default: - err = -ETIMEDOUT; - break; - } - - hdev->req_status = hdev->req_result = 0; - - BT_DBG("%s end: err %d", hdev->name, err); - - return err; -} - -static inline int hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt), - unsigned long opt, __u32 timeout) -{ - int ret; - - if (!test_bit(HCI_UP, &hdev->flags)) - return -ENETDOWN; - - /* Serialize all requests */ - hci_req_lock(hdev); - ret = __hci_request(hdev, req, opt, timeout); - hci_req_unlock(hdev); - - return ret; -} - -static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) -{ - BT_DBG("%s %ld", hdev->name, opt); - - /* Reset device */ - set_bit(HCI_RESET, &hdev->flags); - hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); -} - -static void bredr_init(struct hci_dev *hdev) -{ - struct hci_cp_delete_stored_link_key cp; - __le16 param; - __u8 flt_type; - - hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED; - - /* Mandatory initialization */ - - /* Reset */ - if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) { - set_bit(HCI_RESET, &hdev->flags); - hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); - } - - /* Read Local Supported Features */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); - - /* Read Local Version */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); - - /* Read Buffer Size (ACL mtu, max pkt, etc.) */ - hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); - - /* Read BD Address */ - hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); - - /* Read Class of Device */ - hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); - - /* Read Local Name */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL); - - /* Read Voice Setting */ - hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL); - - /* Optional initialization */ - - /* Clear Event Filters */ - flt_type = HCI_FLT_CLEAR_ALL; - hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type); - - /* Connection accept timeout ~20 secs */ - param = cpu_to_le16(0x7d00); - hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); - - bacpy(&cp.bdaddr, BDADDR_ANY); - cp.delete_all = 1; - hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); -} - -static void amp_init(struct hci_dev *hdev) -{ - hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED; - - /* Reset */ - hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); - - /* Read Local Version */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); -} - -static void hci_init_req(struct hci_dev *hdev, unsigned long opt) -{ - struct sk_buff *skb; - - BT_DBG("%s %ld", hdev->name, opt); - - /* Driver initialization */ - - /* Special commands */ - while ((skb = skb_dequeue(&hdev->driver_init))) { - bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; - skb->dev = (void *) hdev; - - skb_queue_tail(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); - } - skb_queue_purge(&hdev->driver_init); - - switch (hdev->dev_type) { - case HCI_BREDR: - bredr_init(hdev); - break; - - case HCI_AMP: - amp_init(hdev); - break; - - default: - BT_ERR("Unknown device type %d", hdev->dev_type); - break; - } - -} - -static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt) -{ - BT_DBG("%s", hdev->name); - - /* Read LE buffer size */ - hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); -} - -static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) -{ - __u8 scan = opt; - - BT_DBG("%s %x", hdev->name, scan); - - /* Inquiry and Page scans */ - hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); -} - -static void hci_auth_req(struct hci_dev *hdev, unsigned long opt) -{ - __u8 auth = opt; - - BT_DBG("%s %x", hdev->name, auth); - - /* Authentication */ - hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth); -} - -static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt) -{ - __u8 encrypt = opt; - - BT_DBG("%s %x", hdev->name, encrypt); - - /* Encryption */ - hci_send_cmd(hdev, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt); -} - -static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt) -{ - __le16 policy = cpu_to_le16(opt); - - BT_DBG("%s %x", hdev->name, policy); - - /* Default link policy */ - hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy); -} - -/* Get HCI device by index. - * Device is held on return. */ -struct hci_dev *hci_dev_get(int index) -{ - struct hci_dev *hdev = NULL, *d; - - BT_DBG("%d", index); - - if (index < 0) - return NULL; - - read_lock(&hci_dev_list_lock); - list_for_each_entry(d, &hci_dev_list, list) { - if (d->id == index) { - hdev = hci_dev_hold(d); - break; - } - } - read_unlock(&hci_dev_list_lock); - return hdev; -} - -/* ---- Inquiry support ---- */ - -bool hci_discovery_active(struct hci_dev *hdev) -{ - struct discovery_state *discov = &hdev->discovery; - - switch (discov->state) { - case DISCOVERY_FINDING: - case DISCOVERY_RESOLVING: - return true; - - default: - return false; - } -} - -void hci_discovery_set_state(struct hci_dev *hdev, int state) -{ - BT_DBG("%s state %u -> %u", hdev->name, hdev->discovery.state, state); - - if (hdev->discovery.state == state) - return; - - switch (state) { - case DISCOVERY_STOPPED: - if (hdev->discovery.state != DISCOVERY_STARTING) - mgmt_discovering(hdev, 0); - hdev->discovery.type = 0; - break; - case DISCOVERY_STARTING: - break; - case DISCOVERY_FINDING: - mgmt_discovering(hdev, 1); - break; - case DISCOVERY_RESOLVING: - break; - case DISCOVERY_STOPPING: - break; - } - - hdev->discovery.state = state; -} - -static void inquiry_cache_flush(struct hci_dev *hdev) -{ - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *p, *n; - - list_for_each_entry_safe(p, n, &cache->all, all) { - list_del(&p->all); - kfree(p); - } - - INIT_LIST_HEAD(&cache->unknown); - INIT_LIST_HEAD(&cache->resolve); -} - -struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *e; - - BT_DBG("cache %p, %s", cache, batostr(bdaddr)); - - list_for_each_entry(e, &cache->all, all) { - if (!bacmp(&e->data.bdaddr, bdaddr)) - return e; - } - - return NULL; -} - -struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, - bdaddr_t *bdaddr) -{ - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *e; - - BT_DBG("cache %p, %s", cache, batostr(bdaddr)); - - list_for_each_entry(e, &cache->unknown, list) { - if (!bacmp(&e->data.bdaddr, bdaddr)) - return e; - } - - return NULL; -} - -struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, - bdaddr_t *bdaddr, - int state) -{ - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *e; - - BT_DBG("cache %p bdaddr %s state %d", cache, batostr(bdaddr), state); - - list_for_each_entry(e, &cache->resolve, list) { - if (!bacmp(bdaddr, BDADDR_ANY) && e->name_state == state) - return e; - if (!bacmp(&e->data.bdaddr, bdaddr)) - return e; - } - - return NULL; -} - -void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, - struct inquiry_entry *ie) -{ - struct discovery_state *cache = &hdev->discovery; - struct list_head *pos = &cache->resolve; - struct inquiry_entry *p; - - list_del(&ie->list); - - list_for_each_entry(p, &cache->resolve, list) { - if (p->name_state != NAME_PENDING && - abs(p->data.rssi) >= abs(ie->data.rssi)) - break; - pos = &p->list; - } - - list_add(&ie->list, pos); -} - -bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, - bool name_known, bool *ssp) -{ - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *ie; - - BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr)); - - if (ssp) - *ssp = data->ssp_mode; - - ie = hci_inquiry_cache_lookup(hdev, &data->bdaddr); - if (ie) { - if (ie->data.ssp_mode && ssp) - *ssp = true; - - if (ie->name_state == NAME_NEEDED && - data->rssi != ie->data.rssi) { - ie->data.rssi = data->rssi; - hci_inquiry_cache_update_resolve(hdev, ie); - } - - goto update; - } - - /* Entry not in the cache. Add new one. */ - ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC); - if (!ie) - return false; - - list_add(&ie->all, &cache->all); - - if (name_known) { - ie->name_state = NAME_KNOWN; - } else { - ie->name_state = NAME_NOT_KNOWN; - list_add(&ie->list, &cache->unknown); - } - -update: - if (name_known && ie->name_state != NAME_KNOWN && - ie->name_state != NAME_PENDING) { - ie->name_state = NAME_KNOWN; - list_del(&ie->list); - } - - memcpy(&ie->data, data, sizeof(*data)); - ie->timestamp = jiffies; - cache->timestamp = jiffies; - - if (ie->name_state == NAME_NOT_KNOWN) - return false; - - return true; -} - -static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf) -{ - struct discovery_state *cache = &hdev->discovery; - struct inquiry_info *info = (struct inquiry_info *) buf; - struct inquiry_entry *e; - int copied = 0; - - list_for_each_entry(e, &cache->all, all) { - struct inquiry_data *data = &e->data; - - if (copied >= num) - break; - - bacpy(&info->bdaddr, &data->bdaddr); - info->pscan_rep_mode = data->pscan_rep_mode; - info->pscan_period_mode = data->pscan_period_mode; - info->pscan_mode = data->pscan_mode; - memcpy(info->dev_class, data->dev_class, 3); - info->clock_offset = data->clock_offset; - - info++; - copied++; - } - - BT_DBG("cache %p, copied %d", cache, copied); - return copied; -} - -static void hci_inq_req(struct hci_dev *hdev, unsigned long opt) -{ - struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt; - struct hci_cp_inquiry cp; - - BT_DBG("%s", hdev->name); - - if (test_bit(HCI_INQUIRY, &hdev->flags)) - return; - - /* Start Inquiry */ - memcpy(&cp.lap, &ir->lap, 3); - cp.length = ir->length; - cp.num_rsp = ir->num_rsp; - hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); -} - -int hci_inquiry(void __user *arg) -{ - __u8 __user *ptr = arg; - struct hci_inquiry_req ir; - struct hci_dev *hdev; - int err = 0, do_inquiry = 0, max_rsp; - long timeo; - __u8 *buf; - - if (copy_from_user(&ir, ptr, sizeof(ir))) - return -EFAULT; - - hdev = hci_dev_get(ir.dev_id); - if (!hdev) - return -ENODEV; - - hci_dev_lock(hdev); - if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX || - inquiry_cache_empty(hdev) || - ir.flags & IREQ_CACHE_FLUSH) { - inquiry_cache_flush(hdev); - do_inquiry = 1; - } - hci_dev_unlock(hdev); - - timeo = ir.length * msecs_to_jiffies(2000); - - if (do_inquiry) { - err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo); - if (err < 0) - goto done; - } - - /* for unlimited number of responses we will use buffer with 255 entries */ - max_rsp = (ir.num_rsp == 0) ? 255 : ir.num_rsp; - - /* cache_dump can't sleep. Therefore we allocate temp buffer and then - * copy it to the user space. - */ - buf = kmalloc(sizeof(struct inquiry_info) * max_rsp, GFP_KERNEL); - if (!buf) { - err = -ENOMEM; - goto done; - } - - hci_dev_lock(hdev); - ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf); - hci_dev_unlock(hdev); - - BT_DBG("num_rsp %d", ir.num_rsp); - - if (!copy_to_user(ptr, &ir, sizeof(ir))) { - ptr += sizeof(ir); - if (copy_to_user(ptr, buf, sizeof(struct inquiry_info) * - ir.num_rsp)) - err = -EFAULT; - } else - err = -EFAULT; - - kfree(buf); - -done: - hci_dev_put(hdev); - return err; -} - -/* ---- HCI ioctl helpers ---- */ - -int hci_dev_open(__u16 dev) -{ - struct hci_dev *hdev; - int ret = 0; - - hdev = hci_dev_get(dev); - if (!hdev) - return -ENODEV; - - BT_DBG("%s %p", hdev->name, hdev); - - hci_req_lock(hdev); - - if (test_bit(HCI_UNREGISTER, &hdev->dev_flags)) { - ret = -ENODEV; - goto done; - } - - if (hdev->rfkill && rfkill_blocked(hdev->rfkill)) { - ret = -ERFKILL; - goto done; - } - - if (test_bit(HCI_UP, &hdev->flags)) { - ret = -EALREADY; - goto done; - } - - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) - set_bit(HCI_RAW, &hdev->flags); - - /* Treat all non BR/EDR controllers as raw devices if - enable_hs is not set */ - if (hdev->dev_type != HCI_BREDR && !enable_hs) - set_bit(HCI_RAW, &hdev->flags); - - if (hdev->open(hdev)) { - ret = -EIO; - goto done; - } - - if (!test_bit(HCI_RAW, &hdev->flags)) { - atomic_set(&hdev->cmd_cnt, 1); - set_bit(HCI_INIT, &hdev->flags); - hdev->init_last_cmd = 0; - - ret = __hci_request(hdev, hci_init_req, 0, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - - if (lmp_host_le_capable(hdev)) - ret = __hci_request(hdev, hci_le_init_req, 0, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - - clear_bit(HCI_INIT, &hdev->flags); - } - - if (!ret) { - hci_dev_hold(hdev); - set_bit(HCI_UP, &hdev->flags); - hci_notify(hdev, HCI_DEV_UP); - if (!test_bit(HCI_SETUP, &hdev->dev_flags)) { - hci_dev_lock(hdev); - mgmt_powered(hdev, 1); - hci_dev_unlock(hdev); - } - } else { - /* Init failed, cleanup */ - flush_work(&hdev->tx_work); - flush_work(&hdev->cmd_work); - flush_work(&hdev->rx_work); - - skb_queue_purge(&hdev->cmd_q); - skb_queue_purge(&hdev->rx_q); - - if (hdev->flush) - hdev->flush(hdev); - - if (hdev->sent_cmd) { - kfree_skb(hdev->sent_cmd); - hdev->sent_cmd = NULL; - } - - hdev->close(hdev); - hdev->flags = 0; - } - -done: - hci_req_unlock(hdev); - hci_dev_put(hdev); - return ret; -} - -static int hci_dev_do_close(struct hci_dev *hdev) -{ - BT_DBG("%s %p", hdev->name, hdev); - - cancel_work_sync(&hdev->le_scan); - - hci_req_cancel(hdev, ENODEV); - hci_req_lock(hdev); - - if (!test_and_clear_bit(HCI_UP, &hdev->flags)) { - del_timer_sync(&hdev->cmd_timer); - hci_req_unlock(hdev); - return 0; - } - - /* Flush RX and TX works */ - flush_work(&hdev->tx_work); - flush_work(&hdev->rx_work); - - if (hdev->discov_timeout > 0) { - cancel_delayed_work(&hdev->discov_off); - hdev->discov_timeout = 0; - clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); - } - - if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) - cancel_delayed_work(&hdev->service_cache); - - cancel_delayed_work_sync(&hdev->le_scan_disable); - - hci_dev_lock(hdev); - inquiry_cache_flush(hdev); - hci_conn_hash_flush(hdev); - hci_dev_unlock(hdev); - - hci_notify(hdev, HCI_DEV_DOWN); - - if (hdev->flush) - hdev->flush(hdev); - - /* Reset device */ - skb_queue_purge(&hdev->cmd_q); - atomic_set(&hdev->cmd_cnt, 1); - if (!test_bit(HCI_RAW, &hdev->flags) && - test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) { - set_bit(HCI_INIT, &hdev->flags); - __hci_request(hdev, hci_reset_req, 0, - msecs_to_jiffies(250)); - clear_bit(HCI_INIT, &hdev->flags); - } - - /* flush cmd work */ - flush_work(&hdev->cmd_work); - - /* Drop queues */ - skb_queue_purge(&hdev->rx_q); - skb_queue_purge(&hdev->cmd_q); - skb_queue_purge(&hdev->raw_q); - - /* Drop last sent command */ - if (hdev->sent_cmd) { - del_timer_sync(&hdev->cmd_timer); - kfree_skb(hdev->sent_cmd); - hdev->sent_cmd = NULL; - } - - /* After this point our queues are empty - * and no tasks are scheduled. */ - hdev->close(hdev); - - if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { - hci_dev_lock(hdev); - mgmt_powered(hdev, 0); - hci_dev_unlock(hdev); - } - - /* Clear flags */ - hdev->flags = 0; - - memset(hdev->eir, 0, sizeof(hdev->eir)); - memset(hdev->dev_class, 0, sizeof(hdev->dev_class)); - - hci_req_unlock(hdev); - - hci_dev_put(hdev); - return 0; -} - -int hci_dev_close(__u16 dev) -{ - struct hci_dev *hdev; - int err; - - hdev = hci_dev_get(dev); - if (!hdev) - return -ENODEV; - - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - cancel_delayed_work(&hdev->power_off); - - err = hci_dev_do_close(hdev); - - hci_dev_put(hdev); - return err; -} - -int hci_dev_reset(__u16 dev) -{ - struct hci_dev *hdev; - int ret = 0; - - hdev = hci_dev_get(dev); - if (!hdev) - return -ENODEV; - - hci_req_lock(hdev); - - if (!test_bit(HCI_UP, &hdev->flags)) - goto done; - - /* Drop queues */ - skb_queue_purge(&hdev->rx_q); - skb_queue_purge(&hdev->cmd_q); - - hci_dev_lock(hdev); - inquiry_cache_flush(hdev); - hci_conn_hash_flush(hdev); - hci_dev_unlock(hdev); - - if (hdev->flush) - hdev->flush(hdev); - - atomic_set(&hdev->cmd_cnt, 1); - hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0; - - if (!test_bit(HCI_RAW, &hdev->flags)) - ret = __hci_request(hdev, hci_reset_req, 0, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - -done: - hci_req_unlock(hdev); - hci_dev_put(hdev); - return ret; -} - -int hci_dev_reset_stat(__u16 dev) -{ - struct hci_dev *hdev; - int ret = 0; - - hdev = hci_dev_get(dev); - if (!hdev) - return -ENODEV; - - memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); - - hci_dev_put(hdev); - - return ret; -} - -int hci_dev_cmd(unsigned int cmd, void __user *arg) -{ - struct hci_dev *hdev; - struct hci_dev_req dr; - int err = 0; - - if (copy_from_user(&dr, arg, sizeof(dr))) - return -EFAULT; - - hdev = hci_dev_get(dr.dev_id); - if (!hdev) - return -ENODEV; - - switch (cmd) { - case HCISETAUTH: - err = hci_request(hdev, hci_auth_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - break; - - case HCISETENCRYPT: - if (!lmp_encrypt_capable(hdev)) { - err = -EOPNOTSUPP; - break; - } - - if (!test_bit(HCI_AUTH, &hdev->flags)) { - /* Auth must be enabled first */ - err = hci_request(hdev, hci_auth_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - if (err) - break; - } - - err = hci_request(hdev, hci_encrypt_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - break; - - case HCISETSCAN: - err = hci_request(hdev, hci_scan_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - break; - - case HCISETLINKPOL: - err = hci_request(hdev, hci_linkpol_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - break; - - case HCISETLINKMODE: - hdev->link_mode = ((__u16) dr.dev_opt) & - (HCI_LM_MASTER | HCI_LM_ACCEPT); - break; - - case HCISETPTYPE: - hdev->pkt_type = (__u16) dr.dev_opt; - break; - - case HCISETACLMTU: - hdev->acl_mtu = *((__u16 *) &dr.dev_opt + 1); - hdev->acl_pkts = *((__u16 *) &dr.dev_opt + 0); - break; - - case HCISETSCOMTU: - hdev->sco_mtu = *((__u16 *) &dr.dev_opt + 1); - hdev->sco_pkts = *((__u16 *) &dr.dev_opt + 0); - break; - - default: - err = -EINVAL; - break; - } - - hci_dev_put(hdev); - return err; -} - -int hci_get_dev_list(void __user *arg) -{ - struct hci_dev *hdev; - struct hci_dev_list_req *dl; - struct hci_dev_req *dr; - int n = 0, size, err; - __u16 dev_num; - - if (get_user(dev_num, (__u16 __user *) arg)) - return -EFAULT; - - if (!dev_num || dev_num > (PAGE_SIZE * 2) / sizeof(*dr)) - return -EINVAL; - - size = sizeof(*dl) + dev_num * sizeof(*dr); - - dl = kzalloc(size, GFP_KERNEL); - if (!dl) - return -ENOMEM; - - dr = dl->dev_req; - - read_lock(&hci_dev_list_lock); - list_for_each_entry(hdev, &hci_dev_list, list) { - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - cancel_delayed_work(&hdev->power_off); - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - set_bit(HCI_PAIRABLE, &hdev->dev_flags); - - (dr + n)->dev_id = hdev->id; - (dr + n)->dev_opt = hdev->flags; - - if (++n >= dev_num) - break; - } - read_unlock(&hci_dev_list_lock); - - dl->dev_num = n; - size = sizeof(*dl) + n * sizeof(*dr); - - err = copy_to_user(arg, dl, size); - kfree(dl); - - return err ? -EFAULT : 0; -} - -int hci_get_dev_info(void __user *arg) -{ - struct hci_dev *hdev; - struct hci_dev_info di; - int err = 0; - - if (copy_from_user(&di, arg, sizeof(di))) - return -EFAULT; - - hdev = hci_dev_get(di.dev_id); - if (!hdev) - return -ENODEV; - - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - cancel_delayed_work_sync(&hdev->power_off); - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - set_bit(HCI_PAIRABLE, &hdev->dev_flags); - - strcpy(di.name, hdev->name); - di.bdaddr = hdev->bdaddr; - di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4); - di.flags = hdev->flags; - di.pkt_type = hdev->pkt_type; - di.acl_mtu = hdev->acl_mtu; - di.acl_pkts = hdev->acl_pkts; - di.sco_mtu = hdev->sco_mtu; - di.sco_pkts = hdev->sco_pkts; - di.link_policy = hdev->link_policy; - di.link_mode = hdev->link_mode; - - memcpy(&di.stat, &hdev->stat, sizeof(di.stat)); - memcpy(&di.features, &hdev->features, sizeof(di.features)); - - if (copy_to_user(arg, &di, sizeof(di))) - err = -EFAULT; - - hci_dev_put(hdev); - - return err; -} - -/* ---- Interface to HCI drivers ---- */ - -static int hci_rfkill_set_block(void *data, bool blocked) -{ - struct hci_dev *hdev = data; - - BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked); - - if (!blocked) - return 0; - - hci_dev_do_close(hdev); - - return 0; -} - -static const struct rfkill_ops hci_rfkill_ops = { - .set_block = hci_rfkill_set_block, -}; - -/* Alloc HCI device */ -struct hci_dev *hci_alloc_dev(void) -{ - struct hci_dev *hdev; - - hdev = kzalloc(sizeof(struct hci_dev), GFP_KERNEL); - if (!hdev) - return NULL; - - hci_init_sysfs(hdev); - skb_queue_head_init(&hdev->driver_init); - - return hdev; -} -EXPORT_SYMBOL(hci_alloc_dev); - -/* Free HCI device */ -void hci_free_dev(struct hci_dev *hdev) -{ - skb_queue_purge(&hdev->driver_init); - - /* will free via device release */ - put_device(&hdev->dev); -} -EXPORT_SYMBOL(hci_free_dev); - -static void hci_power_on(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, power_on); - - BT_DBG("%s", hdev->name); - - if (hci_dev_open(hdev->id) < 0) - return; - - if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - schedule_delayed_work(&hdev->power_off, - msecs_to_jiffies(AUTO_OFF_TIMEOUT)); - - if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) - mgmt_index_added(hdev); -} - -static void hci_power_off(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, - power_off.work); - - BT_DBG("%s", hdev->name); - - hci_dev_do_close(hdev); -} - -static void hci_discov_off(struct work_struct *work) -{ - struct hci_dev *hdev; - u8 scan = SCAN_PAGE; - - hdev = container_of(work, struct hci_dev, discov_off.work); - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan); - - hdev->discov_timeout = 0; - - hci_dev_unlock(hdev); -} - -int hci_uuids_clear(struct hci_dev *hdev) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->uuids) { - struct bt_uuid *uuid; - - uuid = list_entry(p, struct bt_uuid, list); - - list_del(p); - kfree(uuid); - } - - return 0; -} - -int hci_link_keys_clear(struct hci_dev *hdev) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->link_keys) { - struct link_key *key; - - key = list_entry(p, struct link_key, list); - - list_del(p); - kfree(key); - } - - return 0; -} - -int hci_smp_ltks_clear(struct hci_dev *hdev) -{ - struct smp_ltk *k, *tmp; - - list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) { - list_del(&k->list); - kfree(k); - } - - return 0; -} - -struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct link_key *k; - - list_for_each_entry(k, &hdev->link_keys, list) - if (bacmp(bdaddr, &k->bdaddr) == 0) - return k; - - return NULL; -} - -static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, - u8 key_type, u8 old_key_type) -{ - /* Legacy key */ - if (key_type < 0x03) - return true; - - /* Debug keys are insecure so don't store them persistently */ - if (key_type == HCI_LK_DEBUG_COMBINATION) - return false; - - /* Changed combination key and there's no previous one */ - if (key_type == HCI_LK_CHANGED_COMBINATION && old_key_type == 0xff) - return false; - - /* Security mode 3 case */ - if (!conn) - return true; - - /* Neither local nor remote side had no-bonding as requirement */ - if (conn->auth_type > 0x01 && conn->remote_auth > 0x01) - return true; - - /* Local side had dedicated bonding as requirement */ - if (conn->auth_type == 0x02 || conn->auth_type == 0x03) - return true; - - /* Remote side had dedicated bonding as requirement */ - if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) - return true; - - /* If none of the above criteria match, then don't store the key - * persistently */ - return false; -} - -struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]) -{ - struct smp_ltk *k; - - list_for_each_entry(k, &hdev->long_term_keys, list) { - if (k->ediv != ediv || - memcmp(rand, k->rand, sizeof(k->rand))) - continue; - - return k; - } - - return NULL; -} -EXPORT_SYMBOL(hci_find_ltk); - -struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type) -{ - struct smp_ltk *k; - - list_for_each_entry(k, &hdev->long_term_keys, list) - if (addr_type == k->bdaddr_type && - bacmp(bdaddr, &k->bdaddr) == 0) - return k; - - return NULL; -} -EXPORT_SYMBOL(hci_find_ltk_by_addr); - -int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, - bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) -{ - struct link_key *key, *old_key; - u8 old_key_type; - bool persistent; - - old_key = hci_find_link_key(hdev, bdaddr); - if (old_key) { - old_key_type = old_key->type; - key = old_key; - } else { - old_key_type = conn ? conn->key_type : 0xff; - key = kzalloc(sizeof(*key), GFP_ATOMIC); - if (!key) - return -ENOMEM; - list_add(&key->list, &hdev->link_keys); - } - - BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type); - - /* Some buggy controller combinations generate a changed - * combination key for legacy pairing even when there's no - * previous key */ - if (type == HCI_LK_CHANGED_COMBINATION && - (!conn || conn->remote_auth == 0xff) && - old_key_type == 0xff) { - type = HCI_LK_COMBINATION; - if (conn) - conn->key_type = type; - } - - bacpy(&key->bdaddr, bdaddr); - memcpy(key->val, val, 16); - key->pin_len = pin_len; - - if (type == HCI_LK_CHANGED_COMBINATION) - key->type = old_key_type; - else - key->type = type; - - if (!new_key) - return 0; - - persistent = hci_persistent_key(hdev, conn, type, old_key_type); - - mgmt_new_link_key(hdev, key, persistent); - - if (conn) - conn->flush_key = !persistent; - - return 0; -} - -int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, - int new_key, u8 authenticated, u8 tk[16], u8 enc_size, u16 - ediv, u8 rand[8]) -{ - struct smp_ltk *key, *old_key; - - if (!(type & HCI_SMP_STK) && !(type & HCI_SMP_LTK)) - return 0; - - old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type); - if (old_key) - key = old_key; - else { - key = kzalloc(sizeof(*key), GFP_ATOMIC); - if (!key) - return -ENOMEM; - list_add(&key->list, &hdev->long_term_keys); - } - - bacpy(&key->bdaddr, bdaddr); - key->bdaddr_type = addr_type; - memcpy(key->val, tk, sizeof(key->val)); - key->authenticated = authenticated; - key->ediv = ediv; - key->enc_size = enc_size; - key->type = type; - memcpy(key->rand, rand, sizeof(key->rand)); - - if (!new_key) - return 0; - - if (type & HCI_SMP_LTK) - mgmt_new_ltk(hdev, key, 1); - - return 0; -} - -int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct link_key *key; - - key = hci_find_link_key(hdev, bdaddr); - if (!key) - return -ENOENT; - - BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); - - list_del(&key->list); - kfree(key); - - return 0; -} - -int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct smp_ltk *k, *tmp; - - list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) { - if (bacmp(bdaddr, &k->bdaddr)) - continue; - - BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); - - list_del(&k->list); - kfree(k); - } - - return 0; -} - -/* HCI command timer function */ -static void hci_cmd_timer(unsigned long arg) -{ - struct hci_dev *hdev = (void *) arg; - - BT_ERR("%s command tx timeout", hdev->name); - atomic_set(&hdev->cmd_cnt, 1); - queue_work(hdev->workqueue, &hdev->cmd_work); -} - -struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, - bdaddr_t *bdaddr) -{ - struct oob_data *data; - - list_for_each_entry(data, &hdev->remote_oob_data, list) - if (bacmp(bdaddr, &data->bdaddr) == 0) - return data; - - return NULL; -} - -int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct oob_data *data; - - data = hci_find_remote_oob_data(hdev, bdaddr); - if (!data) - return -ENOENT; - - BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); - - list_del(&data->list); - kfree(data); - - return 0; -} - -int hci_remote_oob_data_clear(struct hci_dev *hdev) -{ - struct oob_data *data, *n; - - list_for_each_entry_safe(data, n, &hdev->remote_oob_data, list) { - list_del(&data->list); - kfree(data); - } - - return 0; -} - -int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, - u8 *randomizer) -{ - struct oob_data *data; - - data = hci_find_remote_oob_data(hdev, bdaddr); - - if (!data) { - data = kmalloc(sizeof(*data), GFP_ATOMIC); - if (!data) - return -ENOMEM; - - bacpy(&data->bdaddr, bdaddr); - list_add(&data->list, &hdev->remote_oob_data); - } - - memcpy(data->hash, hash, sizeof(data->hash)); - memcpy(data->randomizer, randomizer, sizeof(data->randomizer)); - - BT_DBG("%s for %s", hdev->name, batostr(bdaddr)); - - return 0; -} - -struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct bdaddr_list *b; - - list_for_each_entry(b, &hdev->blacklist, list) - if (bacmp(bdaddr, &b->bdaddr) == 0) - return b; - - return NULL; -} - -int hci_blacklist_clear(struct hci_dev *hdev) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->blacklist) { - struct bdaddr_list *b; - - b = list_entry(p, struct bdaddr_list, list); - - list_del(p); - kfree(b); - } - - return 0; -} - -int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct bdaddr_list *entry; - - if (bacmp(bdaddr, BDADDR_ANY) == 0) - return -EBADF; - - if (hci_blacklist_lookup(hdev, bdaddr)) - return -EEXIST; - - entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - bacpy(&entry->bdaddr, bdaddr); - - list_add(&entry->list, &hdev->blacklist); - - return mgmt_device_blocked(hdev, bdaddr, type); -} - -int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct bdaddr_list *entry; - - if (bacmp(bdaddr, BDADDR_ANY) == 0) - return hci_blacklist_clear(hdev); - - entry = hci_blacklist_lookup(hdev, bdaddr); - if (!entry) - return -ENOENT; - - list_del(&entry->list); - kfree(entry); - - return mgmt_device_unblocked(hdev, bdaddr, type); -} - -static void hci_clear_adv_cache(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, - adv_work.work); - - hci_dev_lock(hdev); - - hci_adv_entries_clear(hdev); - - hci_dev_unlock(hdev); -} - -int hci_adv_entries_clear(struct hci_dev *hdev) -{ - struct adv_entry *entry, *tmp; - - list_for_each_entry_safe(entry, tmp, &hdev->adv_entries, list) { - list_del(&entry->list); - kfree(entry); - } - - BT_DBG("%s adv cache cleared", hdev->name); - - return 0; -} - -struct adv_entry *hci_find_adv_entry(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct adv_entry *entry; - - list_for_each_entry(entry, &hdev->adv_entries, list) - if (bacmp(bdaddr, &entry->bdaddr) == 0) - return entry; - - return NULL; -} - -static inline int is_connectable_adv(u8 evt_type) -{ - if (evt_type == ADV_IND || evt_type == ADV_DIRECT_IND) - return 1; - - return 0; -} - -int hci_add_adv_entry(struct hci_dev *hdev, - struct hci_ev_le_advertising_info *ev) { struct adv_entry *entry; if (!is_connectable_adv(ev->evt_type)) - return -EINVAL; - - /* Only new entries should be added to adv_entries. So, if - * bdaddr was found, don't add it. */ - if (hci_find_adv_entry(hdev, &ev->bdaddr)) - return 0; - - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - bacpy(&entry->bdaddr, &ev->bdaddr); - entry->bdaddr_type = ev->bdaddr_type; - - list_add(&entry->list, &hdev->adv_entries); - - BT_DBG("%s adv entry added: address %s type %u", hdev->name, - batostr(&entry->bdaddr), entry->bdaddr_type); - - return 0; -} - -static void le_scan_param_req(struct hci_dev *hdev, unsigned long opt) -{ - struct le_scan_params *param = (struct le_scan_params *) opt; - struct hci_cp_le_set_scan_param cp; - - memset(&cp, 0, sizeof(cp)); - cp.type = param->type; - cp.interval = cpu_to_le16(param->interval); - cp.window = cpu_to_le16(param->window); - - hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_PARAM, sizeof(cp), &cp); -} - -static void le_scan_enable_req(struct hci_dev *hdev, unsigned long opt) -{ - struct hci_cp_le_set_scan_enable cp; - - memset(&cp, 0, sizeof(cp)); - cp.enable = 1; - - hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); -} - -static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval, - u16 window, int timeout) -{ - long timeo = msecs_to_jiffies(3000); - struct le_scan_params param; - int err; - - BT_DBG("%s", hdev->name); - - if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) - return -EINPROGRESS; - - param.type = type; - param.interval = interval; - param.window = window; - - hci_req_lock(hdev); - - err = __hci_request(hdev, le_scan_param_req, (unsigned long) ¶m, - timeo); - if (!err) - err = __hci_request(hdev, le_scan_enable_req, 0, timeo); - - hci_req_unlock(hdev); - - if (err < 0) - return err; - - schedule_delayed_work(&hdev->le_scan_disable, - msecs_to_jiffies(timeout)); - - return 0; -} - -static void le_scan_disable_work(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, - le_scan_disable.work); - struct hci_cp_le_set_scan_enable cp; - - BT_DBG("%s", hdev->name); - - memset(&cp, 0, sizeof(cp)); - - hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); -} - -static void le_scan_work(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, le_scan); - struct le_scan_params *param = &hdev->le_scan_params; - - BT_DBG("%s", hdev->name); - - hci_do_le_scan(hdev, param->type, param->interval, param->window, - param->timeout); -} - -int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window, - int timeout) -{ - struct le_scan_params *param = &hdev->le_scan_params; - - BT_DBG("%s", hdev->name); - - if (work_busy(&hdev->le_scan)) - return -EINPROGRESS; - - param->type = type; - param->interval = interval; - param->window = window; - param->timeout = timeout; - - queue_work(system_long_wq, &hdev->le_scan); - - return 0; -} - -/* Register HCI device */ -int hci_register_dev(struct hci_dev *hdev) -{ - struct list_head *head = &hci_dev_list, *p; - int i, id, error; - - BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - - if (!hdev->open || !hdev->close) - return -EINVAL; - - /* Do not allow HCI_AMP devices to register at index 0, - * so the index can be used as the AMP controller ID. - */ - id = (hdev->dev_type == HCI_BREDR) ? 0 : 1; - - write_lock(&hci_dev_list_lock); - - /* Find first available device id */ - list_for_each(p, &hci_dev_list) { - if (list_entry(p, struct hci_dev, list)->id != id) - break; - head = p; id++; - } - - sprintf(hdev->name, "hci%d", id); - hdev->id = id; - list_add_tail(&hdev->list, head); - - mutex_init(&hdev->lock); - - hdev->flags = 0; - hdev->dev_flags = 0; - hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); - hdev->esco_type = (ESCO_HV1); - hdev->link_mode = (HCI_LM_ACCEPT); - hdev->io_capability = 0x03; /* No Input No Output */ - - hdev->idle_timeout = 0; - hdev->sniff_max_interval = 800; - hdev->sniff_min_interval = 80; - - INIT_WORK(&hdev->rx_work, hci_rx_work); - INIT_WORK(&hdev->cmd_work, hci_cmd_work); - INIT_WORK(&hdev->tx_work, hci_tx_work); - - - skb_queue_head_init(&hdev->rx_q); - skb_queue_head_init(&hdev->cmd_q); - skb_queue_head_init(&hdev->raw_q); - - setup_timer(&hdev->cmd_timer, hci_cmd_timer, (unsigned long) hdev); - - for (i = 0; i < NUM_REASSEMBLY; i++) - hdev->reassembly[i] = NULL; - - init_waitqueue_head(&hdev->req_wait_q); - mutex_init(&hdev->req_lock); - - discovery_init(hdev); - - hci_conn_hash_init(hdev); - - INIT_LIST_HEAD(&hdev->mgmt_pending); - - INIT_LIST_HEAD(&hdev->blacklist); - - INIT_LIST_HEAD(&hdev->uuids); - - INIT_LIST_HEAD(&hdev->link_keys); - INIT_LIST_HEAD(&hdev->long_term_keys); - - INIT_LIST_HEAD(&hdev->remote_oob_data); - - INIT_LIST_HEAD(&hdev->adv_entries); - - INIT_DELAYED_WORK(&hdev->adv_work, hci_clear_adv_cache); - INIT_WORK(&hdev->power_on, hci_power_on); - INIT_DELAYED_WORK(&hdev->power_off, hci_power_off); - - INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off); - - memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); - - atomic_set(&hdev->promisc, 0); - - INIT_WORK(&hdev->le_scan, le_scan_work); - - INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work); - - write_unlock(&hci_dev_list_lock); - - hdev->workqueue = alloc_workqueue(hdev->name, WQ_HIGHPRI | WQ_UNBOUND | - WQ_MEM_RECLAIM, 1); - if (!hdev->workqueue) { - error = -ENOMEM; - goto err; - } - - error = hci_add_sysfs(hdev); - if (error < 0) - goto err_wqueue; - - hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, - RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, hdev); - if (hdev->rfkill) { - if (rfkill_register(hdev->rfkill) < 0) { - rfkill_destroy(hdev->rfkill); - hdev->rfkill = NULL; - } - } - - set_bit(HCI_AUTO_OFF, &hdev->dev_flags); - set_bit(HCI_SETUP, &hdev->dev_flags); - schedule_work(&hdev->power_on); - - hci_notify(hdev, HCI_DEV_REG); - hci_dev_hold(hdev); - - return id; - -err_wqueue: - destroy_workqueue(hdev->workqueue); -err: - write_lock(&hci_dev_list_lock); - list_del(&hdev->list); - write_unlock(&hci_dev_list_lock); - - return error; -} -EXPORT_SYMBOL(hci_register_dev); - -/* Unregister HCI device */ -void hci_unregister_dev(struct hci_dev *hdev) -{ - int i; - - BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - - set_bit(HCI_UNREGISTER, &hdev->dev_flags); - - write_lock(&hci_dev_list_lock); - list_del(&hdev->list); - write_unlock(&hci_dev_list_lock); - - hci_dev_do_close(hdev); - - for (i = 0; i < NUM_REASSEMBLY; i++) - kfree_skb(hdev->reassembly[i]); - - if (!test_bit(HCI_INIT, &hdev->flags) && - !test_bit(HCI_SETUP, &hdev->dev_flags)) { - hci_dev_lock(hdev); - mgmt_index_removed(hdev); - hci_dev_unlock(hdev); - } - - /* mgmt_index_removed should take care of emptying the - * pending list */ - BUG_ON(!list_empty(&hdev->mgmt_pending)); - - hci_notify(hdev, HCI_DEV_UNREG); - - if (hdev->rfkill) { - rfkill_unregister(hdev->rfkill); - rfkill_destroy(hdev->rfkill); - } - - hci_del_sysfs(hdev); - - cancel_delayed_work_sync(&hdev->adv_work); - - destroy_workqueue(hdev->workqueue); - - hci_dev_lock(hdev); - hci_blacklist_clear(hdev); - hci_uuids_clear(hdev); - hci_link_keys_clear(hdev); - hci_smp_ltks_clear(hdev); - hci_remote_oob_data_clear(hdev); - hci_adv_entries_clear(hdev); - hci_dev_unlock(hdev); - - hci_dev_put(hdev); -} -EXPORT_SYMBOL(hci_unregister_dev); - -/* Suspend HCI device */ -int hci_suspend_dev(struct hci_dev *hdev) -{ - hci_notify(hdev, HCI_DEV_SUSPEND); - return 0; -} -EXPORT_SYMBOL(hci_suspend_dev); - -/* Resume HCI device */ -int hci_resume_dev(struct hci_dev *hdev) -{ - hci_notify(hdev, HCI_DEV_RESUME); - return 0; -} -EXPORT_SYMBOL(hci_resume_dev); - -/* Receive frame from HCI drivers */ -int hci_recv_frame(struct sk_buff *skb) -{ - struct hci_dev *hdev = (struct hci_dev *) skb->dev; - if (!hdev || (!test_bit(HCI_UP, &hdev->flags) - && !test_bit(HCI_INIT, &hdev->flags))) { - kfree_skb(skb); - return -ENXIO; - } - - /* Incomming skb */ - bt_cb(skb)->incoming = 1; - - /* Time stamp */ - __net_timestamp(skb); - - skb_queue_tail(&hdev->rx_q, skb); - queue_work(hdev->workqueue, &hdev->rx_work); - - return 0; -} -EXPORT_SYMBOL(hci_recv_frame); - -static int hci_reassembly(struct hci_dev *hdev, int type, void *data, - int count, __u8 index) -{ - int len = 0; - int hlen = 0; - int remain = count; - struct sk_buff *skb; - struct bt_skb_cb *scb; - - if ((type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) || - index >= NUM_REASSEMBLY) - return -EILSEQ; - - skb = hdev->reassembly[index]; - - if (!skb) { - switch (type) { - case HCI_ACLDATA_PKT: - len = HCI_MAX_FRAME_SIZE; - hlen = HCI_ACL_HDR_SIZE; - break; - case HCI_EVENT_PKT: - len = HCI_MAX_EVENT_SIZE; - hlen = HCI_EVENT_HDR_SIZE; - break; - case HCI_SCODATA_PKT: - len = HCI_MAX_SCO_SIZE; - hlen = HCI_SCO_HDR_SIZE; - break; - } - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - scb = (void *) skb->cb; - scb->expect = hlen; - scb->pkt_type = type; - - skb->dev = (void *) hdev; - hdev->reassembly[index] = skb; - } - - while (count) { - scb = (void *) skb->cb; - len = min_t(uint, scb->expect, count); - - memcpy(skb_put(skb, len), data, len); - - count -= len; - data += len; - scb->expect -= len; - remain = count; - - switch (type) { - case HCI_EVENT_PKT: - if (skb->len == HCI_EVENT_HDR_SIZE) { - struct hci_event_hdr *h = hci_event_hdr(skb); - scb->expect = h->plen; - - if (skb_tailroom(skb) < scb->expect) { - kfree_skb(skb); - hdev->reassembly[index] = NULL; - return -ENOMEM; - } - } - break; - - case HCI_ACLDATA_PKT: - if (skb->len == HCI_ACL_HDR_SIZE) { - struct hci_acl_hdr *h = hci_acl_hdr(skb); - scb->expect = __le16_to_cpu(h->dlen); - - if (skb_tailroom(skb) < scb->expect) { - kfree_skb(skb); - hdev->reassembly[index] = NULL; - return -ENOMEM; - } - } - break; - - case HCI_SCODATA_PKT: - if (skb->len == HCI_SCO_HDR_SIZE) { - struct hci_sco_hdr *h = hci_sco_hdr(skb); - scb->expect = h->dlen; - - if (skb_tailroom(skb) < scb->expect) { - kfree_skb(skb); - hdev->reassembly[index] = NULL; - return -ENOMEM; - } - } - break; - } - - if (scb->expect == 0) { - /* Complete frame */ - - bt_cb(skb)->pkt_type = type; - hci_recv_frame(skb); - - hdev->reassembly[index] = NULL; - return remain; - } - } - - return remain; -} - -int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count) -{ - int rem = 0; - - if (type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) - return -EILSEQ; - - while (count) { - rem = hci_reassembly(hdev, type, data, count, type - 1); - if (rem < 0) - return rem; - - data += (count - rem); - count = rem; - } - - return rem; -} -EXPORT_SYMBOL(hci_recv_fragment); - -#define STREAM_REASSEMBLY 0 - -int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count) -{ - int type; - int rem = 0; - - while (count) { - struct sk_buff *skb = hdev->reassembly[STREAM_REASSEMBLY]; - - if (!skb) { - struct { char type; } *pkt; - - /* Start of the frame */ - pkt = data; - type = pkt->type; - - data++; - count--; - } else - type = bt_cb(skb)->pkt_type; - - rem = hci_reassembly(hdev, type, data, count, - STREAM_REASSEMBLY); - if (rem < 0) - return rem; - - data += (count - rem); - count = rem; - } - - return rem; -} -EXPORT_SYMBOL(hci_recv_stream_fragment); - -/* ---- Interface to upper protocols ---- */ - -int hci_register_cb(struct hci_cb *cb) -{ - BT_DBG("%p name %s", cb, cb->name); - - write_lock(&hci_cb_list_lock); - list_add(&cb->list, &hci_cb_list); - write_unlock(&hci_cb_list_lock); - - return 0; -} -EXPORT_SYMBOL(hci_register_cb); - -int hci_unregister_cb(struct hci_cb *cb) -{ - BT_DBG("%p name %s", cb, cb->name); - - write_lock(&hci_cb_list_lock); - list_del(&cb->list); - write_unlock(&hci_cb_list_lock); - - return 0; -} -EXPORT_SYMBOL(hci_unregister_cb); - -static int hci_send_frame(struct sk_buff *skb) -{ - struct hci_dev *hdev = (struct hci_dev *) skb->dev; - - if (!hdev) { - kfree_skb(skb); - return -ENODEV; - } - - BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); - - /* Time stamp */ - __net_timestamp(skb); - - /* Send copy to monitor */ - hci_send_to_monitor(hdev, skb); - - if (atomic_read(&hdev->promisc)) { - /* Send copy to the sockets */ - hci_send_to_sock(hdev, skb); - } - - /* Get rid of skb owner, prior to sending to the driver. */ - skb_orphan(skb); - - return hdev->send(skb); -} - -/* Send HCI command */ -int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) -{ - int len = HCI_COMMAND_HDR_SIZE + plen; - struct hci_command_hdr *hdr; - struct sk_buff *skb; - - BT_DBG("%s opcode 0x%x plen %d", hdev->name, opcode, plen); - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) { - BT_ERR("%s no memory for command", hdev->name); - return -ENOMEM; - } - - hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE); - hdr->opcode = cpu_to_le16(opcode); - hdr->plen = plen; - - if (plen) - memcpy(skb_put(skb, plen), param, plen); - - BT_DBG("skb len %d", skb->len); - - bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; - skb->dev = (void *) hdev; - - if (test_bit(HCI_INIT, &hdev->flags)) - hdev->init_last_cmd = opcode; - - skb_queue_tail(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); - - return 0; -} - -/* Get data from the previously sent command */ -void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode) -{ - struct hci_command_hdr *hdr; - - if (!hdev->sent_cmd) - return NULL; - - hdr = (void *) hdev->sent_cmd->data; - - if (hdr->opcode != cpu_to_le16(opcode)) - return NULL; - - BT_DBG("%s opcode 0x%x", hdev->name, opcode); - - return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE; -} - -/* Send ACL data */ -static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags) -{ - struct hci_acl_hdr *hdr; - int len = skb->len; - - skb_push(skb, HCI_ACL_HDR_SIZE); - skb_reset_transport_header(skb); - hdr = (struct hci_acl_hdr *)skb_transport_header(skb); - hdr->handle = cpu_to_le16(hci_handle_pack(handle, flags)); - hdr->dlen = cpu_to_le16(len); -} - -static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue, - struct sk_buff *skb, __u16 flags) -{ - struct hci_dev *hdev = conn->hdev; - struct sk_buff *list; - - list = skb_shinfo(skb)->frag_list; - if (!list) { - /* Non fragmented */ - BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len); - - skb_queue_tail(queue, skb); - } else { - /* Fragmented */ - BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); - - skb_shinfo(skb)->frag_list = NULL; - - /* Queue all fragments atomically */ - spin_lock(&queue->lock); - - __skb_queue_tail(queue, skb); - - flags &= ~ACL_START; - flags |= ACL_CONT; - do { - skb = list; list = list->next; - - skb->dev = (void *) hdev; - bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_add_acl_hdr(skb, conn->handle, flags); - - BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); - - __skb_queue_tail(queue, skb); - } while (list); - - spin_unlock(&queue->lock); - } -} - -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; - - BT_DBG("%s chan %p flags 0x%x", hdev->name, chan, flags); - - skb->dev = (void *) hdev; - bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_add_acl_hdr(skb, conn->handle, flags); - - hci_queue_acl(conn, &chan->data_q, skb, flags); - - queue_work(hdev->workqueue, &hdev->tx_work); -} -EXPORT_SYMBOL(hci_send_acl); - -/* Send SCO data */ -void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_sco_hdr hdr; - - BT_DBG("%s len %d", hdev->name, skb->len); - - hdr.handle = cpu_to_le16(conn->handle); - hdr.dlen = skb->len; - - skb_push(skb, HCI_SCO_HDR_SIZE); - skb_reset_transport_header(skb); - memcpy(skb_transport_header(skb), &hdr, HCI_SCO_HDR_SIZE); - - skb->dev = (void *) hdev; - bt_cb(skb)->pkt_type = HCI_SCODATA_PKT; - - skb_queue_tail(&conn->data_q, skb); - queue_work(hdev->workqueue, &hdev->tx_work); -} -EXPORT_SYMBOL(hci_send_sco); - -/* ---- HCI TX task (outgoing data) ---- */ - -/* HCI Connection scheduler */ -static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote) -{ - struct hci_conn_hash *h = &hdev->conn_hash; - struct hci_conn *conn = NULL, *c; - int num = 0, min = ~0; - - /* We don't have to lock device here. Connections are always - * added and removed with TX task disabled. */ - - rcu_read_lock(); - - list_for_each_entry_rcu(c, &h->list, list) { - if (c->type != type || skb_queue_empty(&c->data_q)) - continue; - - if (c->state != BT_CONNECTED && c->state != BT_CONFIG) - continue; - - num++; - - if (c->sent < min) { - min = c->sent; - conn = c; - } - - if (hci_conn_num(hdev, type) == num) - break; - } - - rcu_read_unlock(); - - if (conn) { - int cnt, q; - - switch (conn->type) { - case ACL_LINK: - cnt = hdev->acl_cnt; - break; - case SCO_LINK: - case ESCO_LINK: - cnt = hdev->sco_cnt; - break; - case LE_LINK: - cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt; - break; - default: - cnt = 0; - BT_ERR("Unknown link type"); - } - - q = cnt / num; - *quote = q ? q : 1; - } else - *quote = 0; - - BT_DBG("conn %p quote %d", conn, *quote); - return conn; -} - -static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type) -{ - struct hci_conn_hash *h = &hdev->conn_hash; - struct hci_conn *c; - - BT_ERR("%s link tx timeout", hdev->name); - - rcu_read_lock(); - - /* 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)); - hci_acl_disconn(c, 0x13); - } - } - - rcu_read_unlock(); -} - -static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, - int *quote) -{ - struct hci_conn_hash *h = &hdev->conn_hash; - struct hci_chan *chan = NULL; - int num = 0, min = ~0, cur_prio = 0; - struct hci_conn *conn; - int cnt, q, conn_num = 0; - - BT_DBG("%s", hdev->name); - - rcu_read_lock(); - - list_for_each_entry_rcu(conn, &h->list, list) { - struct hci_chan *tmp; - - if (conn->type != type) - continue; - - if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG) - continue; - - conn_num++; - - list_for_each_entry_rcu(tmp, &conn->chan_list, list) { - struct sk_buff *skb; - - if (skb_queue_empty(&tmp->data_q)) - continue; - - skb = skb_peek(&tmp->data_q); - if (skb->priority < cur_prio) - continue; - - if (skb->priority > cur_prio) { - num = 0; - min = ~0; - cur_prio = skb->priority; - } - - num++; - - if (conn->sent < min) { - min = conn->sent; - chan = tmp; - } - } - - if (hci_conn_num(hdev, type) == conn_num) - break; - } - - rcu_read_unlock(); - - if (!chan) - return NULL; - - switch (chan->conn->type) { - case ACL_LINK: - cnt = hdev->acl_cnt; - break; - case SCO_LINK: - case ESCO_LINK: - cnt = hdev->sco_cnt; - break; - case LE_LINK: - cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt; - break; - default: - cnt = 0; - BT_ERR("Unknown link type"); - } - - q = cnt / num; - *quote = q ? q : 1; - BT_DBG("chan %p quote %d", chan, *quote); - return chan; -} - -static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type) -{ - struct hci_conn_hash *h = &hdev->conn_hash; - struct hci_conn *conn; - int num = 0; - - BT_DBG("%s", hdev->name); - - rcu_read_lock(); - - list_for_each_entry_rcu(conn, &h->list, list) { - struct hci_chan *chan; - - if (conn->type != type) - continue; - - if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG) - continue; - - num++; - - list_for_each_entry_rcu(chan, &conn->chan_list, list) { - struct sk_buff *skb; - - if (chan->sent) { - chan->sent = 0; - continue; - } - - if (skb_queue_empty(&chan->data_q)) - continue; - - skb = skb_peek(&chan->data_q); - if (skb->priority >= HCI_PRIO_MAX - 1) - continue; - - skb->priority = HCI_PRIO_MAX - 1; - - BT_DBG("chan %p skb %p promoted to %d", chan, skb, - skb->priority); - } - - if (hci_conn_num(hdev, type) == num) - break; - } - - rcu_read_unlock(); - -} - -static inline int __get_blocks(struct hci_dev *hdev, struct sk_buff *skb) -{ - /* Calculate count of blocks used by this packet */ - return DIV_ROUND_UP(skb->len - HCI_ACL_HDR_SIZE, hdev->block_len); -} - -static inline void __check_timeout(struct hci_dev *hdev, unsigned int cnt) -{ - if (!test_bit(HCI_RAW, &hdev->flags)) { - /* ACL tx timeout must be longer than maximum - * link supervision timeout (40.9 seconds) */ - if (!cnt && time_after(jiffies, hdev->acl_last_tx + - msecs_to_jiffies(HCI_ACL_TX_TIMEOUT))) - hci_link_tx_to(hdev, ACL_LINK); - } -} - -static inline void hci_sched_acl_pkt(struct hci_dev *hdev) -{ - unsigned int cnt = hdev->acl_cnt; - struct hci_chan *chan; - struct sk_buff *skb; - int quote; - - __check_timeout(hdev, cnt); - - while (hdev->acl_cnt && - (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { - u32 priority = (skb_peek(&chan->data_q))->priority; - while (quote-- && (skb = skb_peek(&chan->data_q))) { - BT_DBG("chan %p skb %p len %d priority %u", chan, skb, - skb->len, skb->priority); - - /* Stop if priority has changed */ - if (skb->priority < priority) - break; - - skb = skb_dequeue(&chan->data_q); - - hci_conn_enter_active_mode(chan->conn, - bt_cb(skb)->force_active); - - hci_send_frame(skb); - hdev->acl_last_tx = jiffies; - - hdev->acl_cnt--; - chan->sent++; - chan->conn->sent++; - } - } - - if (cnt != hdev->acl_cnt) - hci_prio_recalculate(hdev, ACL_LINK); -} - -static inline void hci_sched_acl_blk(struct hci_dev *hdev) -{ - unsigned int cnt = hdev->block_cnt; - struct hci_chan *chan; - struct sk_buff *skb; - int quote; - - __check_timeout(hdev, cnt); - - while (hdev->block_cnt > 0 && - (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { - u32 priority = (skb_peek(&chan->data_q))->priority; - while (quote > 0 && (skb = skb_peek(&chan->data_q))) { - int blocks; - - BT_DBG("chan %p skb %p len %d priority %u", chan, skb, - skb->len, skb->priority); - - /* Stop if priority has changed */ - if (skb->priority < priority) - break; - - skb = skb_dequeue(&chan->data_q); - - blocks = __get_blocks(hdev, skb); - if (blocks > hdev->block_cnt) - return; - - hci_conn_enter_active_mode(chan->conn, - bt_cb(skb)->force_active); - - hci_send_frame(skb); - hdev->acl_last_tx = jiffies; - - hdev->block_cnt -= blocks; - quote -= blocks; - - chan->sent += blocks; - chan->conn->sent += blocks; - } - } - - if (cnt != hdev->block_cnt) - hci_prio_recalculate(hdev, ACL_LINK); -} - -static inline void hci_sched_acl(struct hci_dev *hdev) -{ - BT_DBG("%s", hdev->name); - - if (!hci_conn_num(hdev, ACL_LINK)) - return; - - switch (hdev->flow_ctl_mode) { - case HCI_FLOW_CTL_MODE_PACKET_BASED: - hci_sched_acl_pkt(hdev); - break; - - case HCI_FLOW_CTL_MODE_BLOCK_BASED: - hci_sched_acl_blk(hdev); - break; - } -} - -/* Schedule SCO */ -static inline void hci_sched_sco(struct hci_dev *hdev) -{ - struct hci_conn *conn; - struct sk_buff *skb; - int quote; - - BT_DBG("%s", hdev->name); - - if (!hci_conn_num(hdev, SCO_LINK)) - return; - - while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) { - while (quote-- && (skb = skb_dequeue(&conn->data_q))) { - BT_DBG("skb %p len %d", skb, skb->len); - hci_send_frame(skb); - - conn->sent++; - if (conn->sent == ~0) - conn->sent = 0; - } - } -} - -static inline void hci_sched_esco(struct hci_dev *hdev) -{ - struct hci_conn *conn; - struct sk_buff *skb; - int quote; - - BT_DBG("%s", hdev->name); - - if (!hci_conn_num(hdev, ESCO_LINK)) - return; - - while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK, "e))) { - while (quote-- && (skb = skb_dequeue(&conn->data_q))) { - BT_DBG("skb %p len %d", skb, skb->len); - hci_send_frame(skb); - - conn->sent++; - if (conn->sent == ~0) - conn->sent = 0; - } - } -} - -static inline void hci_sched_le(struct hci_dev *hdev) -{ - struct hci_chan *chan; - struct sk_buff *skb; - int quote, cnt, tmp; - - BT_DBG("%s", hdev->name); - - if (!hci_conn_num(hdev, LE_LINK)) - return; - - if (!test_bit(HCI_RAW, &hdev->flags)) { - /* LE tx timeout must be longer than maximum - * link supervision timeout (40.9 seconds) */ - if (!hdev->le_cnt && hdev->le_pkts && - time_after(jiffies, hdev->le_last_tx + HZ * 45)) - hci_link_tx_to(hdev, LE_LINK); - } - - cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt; - tmp = cnt; - while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) { - u32 priority = (skb_peek(&chan->data_q))->priority; - while (quote-- && (skb = skb_peek(&chan->data_q))) { - BT_DBG("chan %p skb %p len %d priority %u", chan, skb, - skb->len, skb->priority); - - /* Stop if priority has changed */ - if (skb->priority < priority) - break; - - skb = skb_dequeue(&chan->data_q); - - hci_send_frame(skb); - hdev->le_last_tx = jiffies; - - cnt--; - chan->sent++; - chan->conn->sent++; - } - } - - if (hdev->le_pkts) - hdev->le_cnt = cnt; - else - hdev->acl_cnt = cnt; - - if (cnt != tmp) - hci_prio_recalculate(hdev, LE_LINK); -} - -static void hci_tx_work(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, tx_work); - struct sk_buff *skb; - - BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt, - hdev->sco_cnt, hdev->le_cnt); - - /* Schedule queues and send stuff to HCI driver */ - - hci_sched_acl(hdev); - - hci_sched_sco(hdev); - - hci_sched_esco(hdev); - - hci_sched_le(hdev); - - /* Send next queued raw (unknown type) packet */ - while ((skb = skb_dequeue(&hdev->raw_q))) - hci_send_frame(skb); -} - -/* ----- HCI RX task (incoming data processing) ----- */ - -/* ACL data packet */ -static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_acl_hdr *hdr = (void *) skb->data; - struct hci_conn *conn; - __u16 handle, flags; - - skb_pull(skb, HCI_ACL_HDR_SIZE); - - handle = __le16_to_cpu(hdr->handle); - flags = hci_flags(handle); - handle = hci_handle(handle); - - BT_DBG("%s len %d handle 0x%x flags 0x%x", hdev->name, skb->len, handle, flags); - - hdev->stat.acl_rx++; - - hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_handle(hdev, handle); - hci_dev_unlock(hdev); - - if (conn) { - hci_conn_enter_active_mode(conn, BT_POWER_FORCE_ACTIVE_OFF); - - hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags) && - !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, &conn->dst, conn->type, - conn->dst_type, 0, NULL, 0, - conn->dev_class); - hci_dev_unlock(hdev); - - /* Send to upper protocol */ - l2cap_recv_acldata(conn, skb, flags); - return; - } else { - BT_ERR("%s ACL packet for unknown connection handle %d", - hdev->name, handle); - } - - kfree_skb(skb); -} - -/* SCO data packet */ -static inline void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_sco_hdr *hdr = (void *) skb->data; - struct hci_conn *conn; - __u16 handle; - - skb_pull(skb, HCI_SCO_HDR_SIZE); - - handle = __le16_to_cpu(hdr->handle); - - BT_DBG("%s len %d handle 0x%x", hdev->name, skb->len, handle); - - hdev->stat.sco_rx++; - - hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_handle(hdev, handle); - hci_dev_unlock(hdev); - - if (conn) { - /* Send to upper protocol */ - sco_recv_scodata(conn, skb); - return; - } else { - BT_ERR("%s SCO packet for unknown connection handle %d", - hdev->name, handle); - } - - kfree_skb(skb); -} - -static void hci_rx_work(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, rx_work); - struct sk_buff *skb; - - BT_DBG("%s", hdev->name); - - while ((skb = skb_dequeue(&hdev->rx_q))) { - /* Send copy to monitor */ - hci_send_to_monitor(hdev, skb); - - if (atomic_read(&hdev->promisc)) { - /* Send copy to the sockets */ - hci_send_to_sock(hdev, skb); - } - - if (test_bit(HCI_RAW, &hdev->flags)) { - kfree_skb(skb); - continue; - } - - if (test_bit(HCI_INIT, &hdev->flags)) { - /* Don't process data packets in this states. */ - switch (bt_cb(skb)->pkt_type) { - case HCI_ACLDATA_PKT: - case HCI_SCODATA_PKT: - kfree_skb(skb); - continue; - } - } - - /* Process frame */ - switch (bt_cb(skb)->pkt_type) { - case HCI_EVENT_PKT: - BT_DBG("%s Event packet", hdev->name); - hci_event_packet(hdev, skb); - break; - - case HCI_ACLDATA_PKT: - BT_DBG("%s ACL data packet", hdev->name); - hci_acldata_packet(hdev, skb); - break; - - case HCI_SCODATA_PKT: - BT_DBG("%s SCO data packet", hdev->name); - hci_scodata_packet(hdev, skb); - break; - - default: - kfree_skb(skb); - break; - } - } -} - -static void hci_cmd_work(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, cmd_work); - struct sk_buff *skb; - - BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt)); - - /* Send queued commands */ - if (atomic_read(&hdev->cmd_cnt)) { - skb = skb_dequeue(&hdev->cmd_q); - if (!skb) - return; - - kfree_skb(hdev->sent_cmd); - - hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC); - if (hdev->sent_cmd) { - atomic_dec(&hdev->cmd_cnt); - hci_send_frame(skb); - if (test_bit(HCI_RESET, &hdev->flags)) - del_timer(&hdev->cmd_timer); - else - mod_timer(&hdev->cmd_timer, - jiffies + msecs_to_jiffies(HCI_CMD_TIMEOUT)); - } else { - skb_queue_head(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); - } - } -} - -int hci_do_inquiry(struct hci_dev *hdev, u8 length) -{ - /* General inquiry access code (GIAC) */ - u8 lap[3] = { 0x33, 0x8b, 0x9e }; - struct hci_cp_inquiry cp; - - BT_DBG("%s", hdev->name); - - if (test_bit(HCI_INQUIRY, &hdev->flags)) - return -EINPROGRESS; - - inquiry_cache_flush(hdev); - - memset(&cp, 0, sizeof(cp)); - memcpy(&cp.lap, lap, sizeof(cp.lap)); - cp.length = length; - - return hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); -} - -int hci_cancel_inquiry(struct hci_dev *hdev) -{ - BT_DBG("%s", hdev->name); - - if (!test_bit(HCI_INQUIRY, &hdev->flags)) - return -EPERM; - - return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL); -} diff --git a/net/bluetooth_tizen/hci_event.c b/net/bluetooth_tizen/hci_event.c deleted file mode 100644 index 17aaa11..0000000 --- a/net/bluetooth_tizen/hci_event.c +++ /dev/null @@ -1,3604 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth HCI event handling. */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/skbuff.h> -#include <linux/interrupt.h> -#include <linux/notifier.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <linux/uaccess.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> - -/* Handle HCI Event packets */ - -static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (status) { - hci_dev_lock(hdev); - mgmt_stop_discovery_failed(hdev, status); - hci_dev_unlock(hdev); - return; - } - - clear_bit(HCI_INQUIRY, &hdev->flags); - - hci_dev_lock(hdev); - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - hci_dev_unlock(hdev); - - hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); - - hci_conn_check_pending(hdev); -} - -static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (status) - return; - - hci_conn_check_pending(hdev); -} - -static void hci_cc_remote_name_req_cancel(struct hci_dev *hdev, struct sk_buff *skb) -{ - BT_DBG("%s", hdev->name); -} - -static void hci_cc_role_discovery(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_role_discovery *rp = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); - if (conn) { - if (rp->role) - conn->link_mode &= ~HCI_LM_MASTER; - else - conn->link_mode |= HCI_LM_MASTER; - } - - hci_dev_unlock(hdev); -} - -static void hci_cc_read_link_policy(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_link_policy *rp = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); - if (conn) - conn->link_policy = __le16_to_cpu(rp->policy); - - hci_dev_unlock(hdev); -} - -static void hci_cc_write_link_policy(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_write_link_policy *rp = (void *) skb->data; - struct hci_conn *conn; - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LINK_POLICY); - if (!sent) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); - if (conn) - conn->link_policy = get_unaligned_le16(sent + 2); - - hci_dev_unlock(hdev); -} - -static void hci_cc_read_def_link_policy(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_def_link_policy *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hdev->link_policy = __le16_to_cpu(rp->policy); -} - -static void hci_cc_write_def_link_policy(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_DEF_LINK_POLICY); - if (!sent) - return; - - if (!status) - hdev->link_policy = get_unaligned_le16(sent); - - hci_req_complete(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, status); -} - -static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - clear_bit(HCI_RESET, &hdev->flags); - - hci_req_complete(hdev, HCI_OP_RESET, status); - - /* Reset all non-persistent flags */ - hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PENDING_CLASS)); - - hdev->discovery.state = DISCOVERY_STOPPED; -} - -static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LOCAL_NAME); - if (!sent) - return; - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_set_local_name_complete(hdev, sent, status); - else if (!status) - memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH); - - hci_dev_unlock(hdev); - - hci_req_complete(hdev, HCI_OP_WRITE_LOCAL_NAME, status); -} - -static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_local_name *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - if (test_bit(HCI_SETUP, &hdev->dev_flags)) - memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH); -} - -static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_AUTH_ENABLE); - if (!sent) - return; - - if (!status) { - __u8 param = *((__u8 *) sent); - - if (param == AUTH_ENABLED) - set_bit(HCI_AUTH, &hdev->flags); - else - clear_bit(HCI_AUTH, &hdev->flags); - } - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_auth_enable_complete(hdev, status); - - hci_req_complete(hdev, HCI_OP_WRITE_AUTH_ENABLE, status); -} - -static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_ENCRYPT_MODE); - if (!sent) - return; - - if (!status) { - __u8 param = *((__u8 *) sent); - - if (param) - set_bit(HCI_ENCRYPT, &hdev->flags); - else - clear_bit(HCI_ENCRYPT, &hdev->flags); - } - - hci_req_complete(hdev, HCI_OP_WRITE_ENCRYPT_MODE, status); -} - -static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 param, status = *((__u8 *) skb->data); - int old_pscan, old_iscan; - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SCAN_ENABLE); - if (!sent) - return; - - param = *((__u8 *) sent); - - hci_dev_lock(hdev); - - if (status != 0) { - mgmt_write_scan_failed(hdev, param, status); - hdev->discov_timeout = 0; - goto done; - } - - old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); - old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); - - if (param & SCAN_INQUIRY) { - set_bit(HCI_ISCAN, &hdev->flags); - if (!old_iscan) - mgmt_discoverable(hdev, 1); - if (hdev->discov_timeout > 0) { - int to = msecs_to_jiffies(hdev->discov_timeout * 1000); - queue_delayed_work(hdev->workqueue, &hdev->discov_off, - to); - } - } else if (old_iscan) - mgmt_discoverable(hdev, 0); - - if (param & SCAN_PAGE) { - set_bit(HCI_PSCAN, &hdev->flags); - if (!old_pscan) - mgmt_connectable(hdev, 1); - } else if (old_pscan) - mgmt_connectable(hdev, 0); - -done: - hci_dev_unlock(hdev); - hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); -} - -static void hci_cc_read_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_class_of_dev *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - memcpy(hdev->dev_class, rp->dev_class, 3); - - BT_DBG("%s class 0x%.2x%.2x%.2x", hdev->name, - hdev->dev_class[2], hdev->dev_class[1], hdev->dev_class[0]); -} - -static void hci_cc_write_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_CLASS_OF_DEV); - if (!sent) - return; - - hci_dev_lock(hdev); - - if (status == 0) - memcpy(hdev->dev_class, sent, 3); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_set_class_of_dev_complete(hdev, sent, status); - - hci_dev_unlock(hdev); -} - -static void hci_cc_read_voice_setting(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_voice_setting *rp = (void *) skb->data; - __u16 setting; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - setting = __le16_to_cpu(rp->voice_setting); - - if (hdev->voice_setting == setting) - return; - - hdev->voice_setting = setting; - - BT_DBG("%s voice setting 0x%04x", hdev->name, setting); - - if (hdev->notify) - hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); -} - -static void hci_cc_write_voice_setting(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - __u16 setting; - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (status) - return; - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_VOICE_SETTING); - if (!sent) - return; - - setting = get_unaligned_le16(sent); - - if (hdev->voice_setting == setting) - return; - - hdev->voice_setting = setting; - - BT_DBG("%s voice setting 0x%04x", hdev->name, setting); - - if (hdev->notify) - hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); -} - -static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_HOST_BUFFER_SIZE, status); -} - -static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_MODE); - if (!sent) - return; - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_ssp_enable_complete(hdev, *((u8 *) sent), status); - else if (!status) { - if (*((u8 *) sent)) - set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); - else - clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); - } -} - -static u8 hci_get_inquiry_mode(struct hci_dev *hdev) -{ - if (hdev->features[6] & LMP_EXT_INQ) - return 2; - - if (hdev->features[3] & LMP_RSSI_INQ) - return 1; - - if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 && - hdev->lmp_subver == 0x0757) - return 1; - - if (hdev->manufacturer == 15) { - if (hdev->hci_rev == 0x03 && hdev->lmp_subver == 0x6963) - return 1; - if (hdev->hci_rev == 0x09 && hdev->lmp_subver == 0x6963) - return 1; - if (hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x6965) - return 1; - } - - if (hdev->manufacturer == 31 && hdev->hci_rev == 0x2005 && - hdev->lmp_subver == 0x1805) - return 1; - - return 0; -} - -static void hci_setup_inquiry_mode(struct hci_dev *hdev) -{ - u8 mode; - - mode = hci_get_inquiry_mode(hdev); - - hci_send_cmd(hdev, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode); -} - -static void hci_setup_event_mask(struct hci_dev *hdev) -{ - /* The second byte is 0xff instead of 0x9f (two reserved bits - * disabled) since a Broadcom 1.2 dongle doesn't respond to the - * command otherwise */ - u8 events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 }; - - /* CSR 1.1 dongles does not accept any bitfield so don't try to set - * any event mask for pre 1.2 devices */ - if (hdev->hci_ver < BLUETOOTH_VER_1_2) - return; - - events[4] |= 0x01; /* Flow Specification Complete */ - events[4] |= 0x02; /* Inquiry Result with RSSI */ - events[4] |= 0x04; /* Read Remote Extended Features Complete */ - events[5] |= 0x08; /* Synchronous Connection Complete */ - events[5] |= 0x10; /* Synchronous Connection Changed */ - - if (hdev->features[3] & LMP_RSSI_INQ) - events[4] |= 0x02; /* Inquiry Result with RSSI */ - - if (hdev->features[5] & LMP_SNIFF_SUBR) - events[5] |= 0x20; /* Sniff Subrating */ - - if (hdev->features[5] & LMP_PAUSE_ENC) - events[5] |= 0x80; /* Encryption Key Refresh Complete */ - - if (hdev->features[6] & LMP_EXT_INQ) - events[5] |= 0x40; /* Extended Inquiry Result */ - - if (hdev->features[6] & LMP_NO_FLUSH) - events[7] |= 0x01; /* Enhanced Flush Complete */ - - if (hdev->features[7] & LMP_LSTO) - events[6] |= 0x80; /* Link Supervision Timeout Changed */ - - if (hdev->features[6] & LMP_SIMPLE_PAIR) { - events[6] |= 0x01; /* IO Capability Request */ - events[6] |= 0x02; /* IO Capability Response */ - events[6] |= 0x04; /* User Confirmation Request */ - events[6] |= 0x08; /* User Passkey Request */ - events[6] |= 0x10; /* Remote OOB Data Request */ - events[6] |= 0x20; /* Simple Pairing Complete */ - events[7] |= 0x04; /* User Passkey Notification */ - events[7] |= 0x08; /* Keypress Notification */ - events[7] |= 0x10; /* Remote Host Supported - * Features Notification */ - } - - if (hdev->features[4] & LMP_LE) - events[7] |= 0x20; /* LE Meta-Event */ - - hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); -} - -static void hci_setup(struct hci_dev *hdev) -{ - if (hdev->dev_type != HCI_BREDR) - return; - - hci_setup_event_mask(hdev); - - if (hdev->hci_ver > BLUETOOTH_VER_1_1) - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); - - if (hdev->features[6] & LMP_SIMPLE_PAIR) { - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { - u8 mode = 0x01; - hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, - sizeof(mode), &mode); - } else { - struct hci_cp_write_eir cp; - - memset(hdev->eir, 0, sizeof(hdev->eir)); - memset(&cp, 0, sizeof(cp)); - - hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); - } - } - - if (hdev->features[3] & LMP_RSSI_INQ) - hci_setup_inquiry_mode(hdev); - - if (hdev->features[7] & LMP_INQ_TX_PWR) - hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); - - if (hdev->features[7] & LMP_EXTFEATURES) { - struct hci_cp_read_local_ext_features cp; - - cp.page = 0x01; - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), - &cp); - } - - if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) { - u8 enable = 1; - hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable), - &enable); - } -} - -static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_local_version *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - goto done; - - hdev->hci_ver = rp->hci_ver; - hdev->hci_rev = __le16_to_cpu(rp->hci_rev); - hdev->lmp_ver = rp->lmp_ver; - hdev->manufacturer = __le16_to_cpu(rp->manufacturer); - hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver); - - BT_DBG("%s manufacturer %d hci ver %d:%d", hdev->name, - hdev->manufacturer, - hdev->hci_ver, hdev->hci_rev); - - if (test_bit(HCI_INIT, &hdev->flags)) - hci_setup(hdev); - -done: - hci_req_complete(hdev, HCI_OP_READ_LOCAL_VERSION, rp->status); -} - -static void hci_setup_link_policy(struct hci_dev *hdev) -{ - u16 link_policy = 0; - - if (hdev->features[0] & LMP_RSWITCH) - link_policy |= HCI_LP_RSWITCH; - if (hdev->features[0] & LMP_HOLD) - link_policy |= HCI_LP_HOLD; - if (hdev->features[0] & LMP_SNIFF) - link_policy |= HCI_LP_SNIFF; - if (hdev->features[1] & LMP_PARK) - link_policy |= HCI_LP_PARK; - - link_policy = cpu_to_le16(link_policy); - hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(link_policy), - &link_policy); -} - -static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_local_commands *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - goto done; - - memcpy(hdev->commands, rp->commands, sizeof(hdev->commands)); - - if (test_bit(HCI_INIT, &hdev->flags) && (hdev->commands[5] & 0x10)) - hci_setup_link_policy(hdev); - -done: - hci_req_complete(hdev, HCI_OP_READ_LOCAL_COMMANDS, rp->status); -} - -static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_local_features *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - memcpy(hdev->features, rp->features, 8); - - /* Adjust default settings according to features - * supported by device. */ - - if (hdev->features[0] & LMP_3SLOT) - hdev->pkt_type |= (HCI_DM3 | HCI_DH3); - - if (hdev->features[0] & LMP_5SLOT) - hdev->pkt_type |= (HCI_DM5 | HCI_DH5); - - if (hdev->features[1] & LMP_HV2) { - hdev->pkt_type |= (HCI_HV2); - hdev->esco_type |= (ESCO_HV2); - } - - if (hdev->features[1] & LMP_HV3) { - hdev->pkt_type |= (HCI_HV3); - hdev->esco_type |= (ESCO_HV3); - } - - if (hdev->features[3] & LMP_ESCO) - hdev->esco_type |= (ESCO_EV3); - - if (hdev->features[4] & LMP_EV4) - hdev->esco_type |= (ESCO_EV4); - - if (hdev->features[4] & LMP_EV5) - hdev->esco_type |= (ESCO_EV5); - - if (hdev->features[5] & LMP_EDR_ESCO_2M) - hdev->esco_type |= (ESCO_2EV3); - - if (hdev->features[5] & LMP_EDR_ESCO_3M) - hdev->esco_type |= (ESCO_3EV3); - - if (hdev->features[5] & LMP_EDR_3S_ESCO) - hdev->esco_type |= (ESCO_2EV5 | ESCO_3EV5); - - BT_DBG("%s features 0x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", hdev->name, - hdev->features[0], hdev->features[1], - hdev->features[2], hdev->features[3], - hdev->features[4], hdev->features[5], - hdev->features[6], hdev->features[7]); -} - -static void hci_set_le_support(struct hci_dev *hdev) -{ - struct hci_cp_write_le_host_supported cp; - - memset(&cp, 0, sizeof(cp)); - - if (enable_le && test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { - cp.le = 1; - cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); - } - - if (cp.le != !!(hdev->host_features[0] & LMP_HOST_LE)) - hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), - &cp); -} - -static void hci_cc_read_local_ext_features(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_read_local_ext_features *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - goto done; - - switch (rp->page) { - case 0: - memcpy(hdev->features, rp->features, 8); - break; - case 1: - memcpy(hdev->host_features, rp->features, 8); - break; - } - - if (test_bit(HCI_INIT, &hdev->flags) && hdev->features[4] & LMP_LE) - hci_set_le_support(hdev); - -done: - hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status); -} - -static void hci_cc_read_flow_control_mode(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_read_flow_control_mode *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hdev->flow_ctl_mode = rp->mode; - - hci_req_complete(hdev, HCI_OP_READ_FLOW_CONTROL_MODE, rp->status); -} - -static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_buffer_size *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hdev->acl_mtu = __le16_to_cpu(rp->acl_mtu); - hdev->sco_mtu = rp->sco_mtu; - hdev->acl_pkts = __le16_to_cpu(rp->acl_max_pkt); - hdev->sco_pkts = __le16_to_cpu(rp->sco_max_pkt); - - if (test_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks)) { - hdev->sco_mtu = 64; - hdev->sco_pkts = 8; - } - - hdev->acl_cnt = hdev->acl_pkts; - hdev->sco_cnt = hdev->sco_pkts; - - BT_DBG("%s acl mtu %d:%d sco mtu %d:%d", hdev->name, - hdev->acl_mtu, hdev->acl_pkts, - hdev->sco_mtu, hdev->sco_pkts); -} - -static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_bd_addr *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (!rp->status) - bacpy(&hdev->bdaddr, &rp->bdaddr); - - hci_req_complete(hdev, HCI_OP_READ_BD_ADDR, rp->status); -} - -static void hci_cc_read_data_block_size(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_read_data_block_size *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hdev->block_mtu = __le16_to_cpu(rp->max_acl_len); - hdev->block_len = __le16_to_cpu(rp->block_len); - hdev->num_blocks = __le16_to_cpu(rp->num_blocks); - - hdev->block_cnt = hdev->num_blocks; - - BT_DBG("%s blk mtu %d cnt %d len %d", hdev->name, hdev->block_mtu, - hdev->block_cnt, hdev->block_len); - - hci_req_complete(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, rp->status); -} - -static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status); -} - -static void hci_cc_read_local_amp_info(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_read_local_amp_info *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hdev->amp_status = rp->amp_status; - hdev->amp_total_bw = __le32_to_cpu(rp->total_bw); - hdev->amp_max_bw = __le32_to_cpu(rp->max_bw); - hdev->amp_min_latency = __le32_to_cpu(rp->min_latency); - hdev->amp_max_pdu = __le32_to_cpu(rp->max_pdu); - hdev->amp_type = rp->amp_type; - hdev->amp_pal_cap = __le16_to_cpu(rp->pal_cap); - hdev->amp_assoc_size = __le16_to_cpu(rp->max_assoc_size); - hdev->amp_be_flush_to = __le32_to_cpu(rp->be_flush_to); - hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to); - - hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status); -} - -static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, - struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_DELETE_STORED_LINK_KEY, status); -} - -static void hci_cc_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_SET_EVENT_MASK, status); -} - -static void hci_cc_write_inquiry_mode(struct hci_dev *hdev, - struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_WRITE_INQUIRY_MODE, status); -} - -static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev, - struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, status); -} - -static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_SET_EVENT_FLT, status); -} - -static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_pin_code_reply *rp = (void *) skb->data; - struct hci_cp_pin_code_reply *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status); - - if (rp->status != 0) - goto unlock; - - cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY); - if (!cp) - goto unlock; - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - if (conn) - conn->pin_length = cp->pin_len; - -unlock: - hci_dev_unlock(hdev); -} - -static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_pin_code_neg_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr, - rp->status); - - hci_dev_unlock(hdev); -} - -static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_le_read_buffer_size *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hdev->le_mtu = __le16_to_cpu(rp->le_mtu); - hdev->le_pkts = rp->le_max_pkt; - - hdev->le_cnt = hdev->le_pkts; - - BT_DBG("%s le mtu %d:%d", hdev->name, hdev->le_mtu, hdev->le_pkts); - - hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status); -} - -static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_user_confirm_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0, - rp->status); - - hci_dev_unlock(hdev); -} - -static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_user_confirm_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr, - ACL_LINK, 0, rp->status); - - hci_dev_unlock(hdev); -} - -static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_user_confirm_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr, ACL_LINK, - 0, rp->status); - - hci_dev_unlock(hdev); -} - -static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_user_confirm_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr, - ACL_LINK, 0, rp->status); - - hci_dev_unlock(hdev); -} - -static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_read_local_oob_data *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - mgmt_read_local_oob_data_reply_complete(hdev, rp->hash, - rp->randomizer, rp->status); - hci_dev_unlock(hdev); -} - -static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_PARAM, status); - - if (status) { - hci_dev_lock(hdev); - mgmt_start_discovery_failed(hdev, status); - hci_dev_unlock(hdev); - return; - } -} - -static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_cp_le_set_scan_enable *cp; - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE); - if (!cp) - return; - - switch (cp->enable) { - case LE_SCANNING_ENABLED: - hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_ENABLE, status); - - if (status) { - hci_dev_lock(hdev); - mgmt_start_discovery_failed(hdev, status); - hci_dev_unlock(hdev); - return; - } - - set_bit(HCI_LE_SCAN, &hdev->dev_flags); - - cancel_delayed_work_sync(&hdev->adv_work); - - hci_dev_lock(hdev); - hci_adv_entries_clear(hdev); - hci_discovery_set_state(hdev, DISCOVERY_FINDING); - hci_dev_unlock(hdev); - break; - - case LE_SCANNING_DISABLED: - if (status) - return; - - clear_bit(HCI_LE_SCAN, &hdev->dev_flags); - - schedule_delayed_work(&hdev->adv_work, ADV_CLEAR_TIMEOUT); - - if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED) { - mgmt_interleaved_discovery(hdev); - } else { - hci_dev_lock(hdev); - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - hci_dev_unlock(hdev); - } - - break; - - default: - BT_ERR("Used reserved LE_Scan_Enable param %d", cp->enable); - break; - } -} - -static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_le_ltk_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status); -} - -static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_le_ltk_neg_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status); -} - -static inline void hci_cc_write_le_host_supported(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_cp_write_le_host_supported *sent; - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED); - if (!sent) - return; - - if (!status) { - if (sent->le) - hdev->host_features[0] |= LMP_HOST_LE; - else - hdev->host_features[0] &= ~LMP_HOST_LE; - } - - if (test_bit(HCI_MGMT, &hdev->dev_flags) && - !test_bit(HCI_INIT, &hdev->flags)) - mgmt_le_enable_complete(hdev, sent->le, status); - - hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status); -} - -static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) -{ - BT_DBG("%s status 0x%x", hdev->name, status); - - if (status) { - hci_req_complete(hdev, HCI_OP_INQUIRY, status); - hci_conn_check_pending(hdev); - hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_start_discovery_failed(hdev, status); - hci_dev_unlock(hdev); - return; - } - - set_bit(HCI_INQUIRY, &hdev->flags); - - hci_dev_lock(hdev); - hci_discovery_set_state(hdev, DISCOVERY_FINDING); - hci_dev_unlock(hdev); -} - -static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_create_conn *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_CONN); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - - BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->bdaddr), conn); - - if (status) { - if (conn && conn->state == BT_CONNECT) { - if (status != 0x0c || conn->attempt > 2) { - conn->state = BT_CLOSED; - hci_proto_connect_cfm(conn, status); - hci_conn_del(conn); - } else - conn->state = BT_CONNECT2; - } - } else { - if (!conn) { - conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr); - if (conn) { - conn->out = true; - conn->link_mode |= HCI_LM_MASTER; - } else - BT_ERR("No memory for new connection"); - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_add_sco *cp; - struct hci_conn *acl, *sco; - __u16 handle; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_ADD_SCO); - if (!cp) - return; - - handle = __le16_to_cpu(cp->handle); - - BT_DBG("%s handle %d", hdev->name, handle); - - hci_dev_lock(hdev); - - acl = hci_conn_hash_lookup_handle(hdev, handle); - if (acl) { - sco = acl->link; - if (sco) { - sco->state = BT_CLOSED; - - hci_proto_connect_cfm(sco, status); - hci_conn_del(sco); - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_auth_requested(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_auth_requested *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_AUTH_REQUESTED); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - if (conn->state == BT_CONFIG) { - hci_proto_connect_cfm(conn, status); - hci_conn_put(conn); - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_set_conn_encrypt(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_set_conn_encrypt *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_SET_CONN_ENCRYPT); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - if (conn->state == BT_CONFIG) { - hci_proto_connect_cfm(conn, status); - hci_conn_put(conn); - } - } - - hci_dev_unlock(hdev); -} - -static int hci_outgoing_auth_needed(struct hci_dev *hdev, - struct hci_conn *conn) -{ - if (conn->state != BT_CONFIG || !conn->out) - return 0; - - if (conn->pending_sec_level == BT_SECURITY_SDP) - return 0; - - /* Only request authentication for SSP connections or non-SSP - * devices with sec_level HIGH or if MITM protection is requested */ - if (!hci_conn_ssp_enabled(conn) && - conn->pending_sec_level != BT_SECURITY_HIGH && - !(conn->auth_type & 0x01)) - return 0; - - return 1; -} - -static inline int hci_resolve_name(struct hci_dev *hdev, - struct inquiry_entry *e) -{ - struct hci_cp_remote_name_req cp; - - memset(&cp, 0, sizeof(cp)); - - bacpy(&cp.bdaddr, &e->data.bdaddr); - cp.pscan_rep_mode = e->data.pscan_rep_mode; - cp.pscan_mode = e->data.pscan_mode; - cp.clock_offset = e->data.clock_offset; - - return hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); -} - -static bool hci_resolve_next_name(struct hci_dev *hdev) -{ - struct discovery_state *discov = &hdev->discovery; - struct inquiry_entry *e; - - if (list_empty(&discov->resolve)) - return false; - - e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED); - if (hci_resolve_name(hdev, e) == 0) { - e->name_state = NAME_PENDING; - return true; - } - - return false; -} - -static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn, - bdaddr_t *bdaddr, u8 *name, u8 name_len) -{ - struct discovery_state *discov = &hdev->discovery; - struct inquiry_entry *e; - - if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, 0, name, - name_len, conn->dev_class); - - if (discov->state == DISCOVERY_STOPPED) - return; - - if (discov->state == DISCOVERY_STOPPING) - goto discov_complete; - - if (discov->state != DISCOVERY_RESOLVING) - return; - - e = hci_inquiry_cache_lookup_resolve(hdev, bdaddr, NAME_PENDING); - if (e) { - e->name_state = NAME_KNOWN; - list_del(&e->list); - if (name) - mgmt_remote_name(hdev, bdaddr, ACL_LINK, 0x00, - e->data.rssi, name, name_len); - } - - if (hci_resolve_next_name(hdev)) - return; - -discov_complete: - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); -} - -static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_remote_name_req *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - /* If successful wait for the name req complete event before - * checking for the need to do authentication */ - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_REMOTE_NAME_REQ); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - hci_check_pending_name(hdev, conn, &cp->bdaddr, NULL, 0); - - if (!conn) - goto unlock; - - if (!hci_outgoing_auth_needed(hdev, conn)) - goto unlock; - - if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { - struct hci_cp_auth_requested cp; - cp.handle = __cpu_to_le16(conn->handle); - hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); - } - -unlock: - hci_dev_unlock(hdev); -} - -static void hci_cs_read_remote_features(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_read_remote_features *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_FEATURES); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - if (conn->state == BT_CONFIG) { - hci_proto_connect_cfm(conn, status); - hci_conn_put(conn); - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_read_remote_ext_features(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_read_remote_ext_features *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_EXT_FEATURES); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - if (conn->state == BT_CONFIG) { - hci_proto_connect_cfm(conn, status); - hci_conn_put(conn); - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_setup_sync_conn *cp; - struct hci_conn *acl, *sco; - __u16 handle; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_SETUP_SYNC_CONN); - if (!cp) - return; - - handle = __le16_to_cpu(cp->handle); - - BT_DBG("%s handle %d", hdev->name, handle); - - hci_dev_lock(hdev); - - acl = hci_conn_hash_lookup_handle(hdev, handle); - if (acl) { - sco = acl->link; - if (sco) { - sco->state = BT_CLOSED; - - hci_proto_connect_cfm(sco, status); - hci_conn_del(sco); - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_sniff_mode *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_SNIFF_MODE); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags); - - if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags)) - hci_sco_setup(conn, status); - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_exit_sniff_mode *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_EXIT_SNIFF_MODE); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags); - - if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags)) - hci_sco_setup(conn, status); - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_disconnect(struct hci_dev *hdev, u8 status) -{ - struct hci_cp_disconnect *cp; - struct hci_conn *conn; - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_DISCONNECT); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) - mgmt_disconnect_failed(hdev, &conn->dst, conn->type, - conn->dst_type, status); - - hci_dev_unlock(hdev); -} - -static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_le_create_conn *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CONN); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr); - - BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->peer_addr), - conn); - - if (status) { - if (conn && conn->state == BT_CONNECT) { - conn->state = BT_CLOSED; - hci_proto_connect_cfm(conn, status); - hci_conn_del(conn); - } - } else { - if (!conn) { - conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr); - if (conn) { - conn->dst_type = cp->peer_addr_type; - conn->out = true; - } else { - BT_ERR("No memory for new connection"); - } - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) -{ - BT_DBG("%s status 0x%x", hdev->name, status); -} - -static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - struct discovery_state *discov = &hdev->discovery; - struct inquiry_entry *e; - - BT_DBG("%s status %d", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_INQUIRY, status); - - hci_conn_check_pending(hdev); - - if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags)) - return; - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - return; - - hci_dev_lock(hdev); - - if (discov->state != DISCOVERY_FINDING) - goto unlock; - - if (list_empty(&discov->resolve)) { - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - goto unlock; - } - - e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED); - if (e && hci_resolve_name(hdev, e) == 0) { - e->name_state = NAME_PENDING; - hci_discovery_set_state(hdev, DISCOVERY_RESOLVING); - } else { - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct inquiry_data data; - struct inquiry_info *info = (void *) (skb->data + 1); - int num_rsp = *((__u8 *) skb->data); - - BT_DBG("%s num_rsp %d", hdev->name, num_rsp); - - if (!num_rsp) - return; - - hci_dev_lock(hdev); - - for (; num_rsp; num_rsp--, info++) { - bool name_known, ssp; - - bacpy(&data.bdaddr, &info->bdaddr); - data.pscan_rep_mode = info->pscan_rep_mode; - data.pscan_period_mode = info->pscan_period_mode; - data.pscan_mode = info->pscan_mode; - memcpy(data.dev_class, info->dev_class, 3); - data.clock_offset = info->clock_offset; - data.rssi = 0x00; - data.ssp_mode = 0x00; - - name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp); - mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, 0, !name_known, ssp, NULL, - 0); - } - - hci_dev_unlock(hdev); -} - -static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_conn_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); - if (!conn) { - if (ev->link_type != SCO_LINK) - goto unlock; - - conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - conn->type = SCO_LINK; - } - - if (!ev->status) { - conn->handle = __le16_to_cpu(ev->handle); - - if (conn->type == ACL_LINK) { - conn->state = BT_CONFIG; - hci_conn_hold(conn); - conn->disc_timeout = HCI_DISCONN_TIMEOUT; - } else - conn->state = BT_CONNECTED; - - hci_conn_hold_device(conn); - hci_conn_add_sysfs(conn); - - if (test_bit(HCI_AUTH, &hdev->flags)) - conn->link_mode |= HCI_LM_AUTH; - - if (test_bit(HCI_ENCRYPT, &hdev->flags)) - conn->link_mode |= HCI_LM_ENCRYPT; - - /* Get remote features */ - if (conn->type == ACL_LINK) { - struct hci_cp_read_remote_features cp; - cp.handle = ev->handle; - hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, - sizeof(cp), &cp); - } - - /* Set packet type for incoming connection */ - if (!conn->out && hdev->hci_ver < BLUETOOTH_VER_2_0) { - struct hci_cp_change_conn_ptype cp; - cp.handle = ev->handle; - cp.pkt_type = cpu_to_le16(conn->pkt_type); - hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp), - &cp); - } - } else { - conn->state = BT_CLOSED; - if (conn->type == ACL_LINK) - mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, - conn->dst_type, ev->status); - } - - if (conn->type == ACL_LINK) - hci_sco_setup(conn, ev->status); - - if (ev->status) { - hci_proto_connect_cfm(conn, ev->status); - hci_conn_del(conn); - } else if (ev->link_type != ACL_LINK) - hci_proto_connect_cfm(conn, ev->status); - -unlock: - hci_dev_unlock(hdev); - - hci_conn_check_pending(hdev); -} - -static inline 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), ev->link_type); - - mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type); - - if ((mask & HCI_LM_ACCEPT) && - !hci_blacklist_lookup(hdev, &ev->bdaddr)) { - /* Connection accepted */ - struct inquiry_entry *ie; - struct hci_conn *conn; - - hci_dev_lock(hdev); - - ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr); - if (ie) - memcpy(ie->data.dev_class, ev->dev_class, 3); - - conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); - if (!conn) { - conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr); - if (!conn) { - BT_ERR("No memory for new connection"); - hci_dev_unlock(hdev); - return; - } - } - - memcpy(conn->dev_class, ev->dev_class, 3); - conn->state = BT_CONNECT; - - hci_dev_unlock(hdev); - - if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) { - struct hci_cp_accept_conn_req cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - - if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) - cp.role = 0x00; /* Become master */ - else - cp.role = 0x01; /* Remain slave */ - - hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), - &cp); - } else { - struct hci_cp_accept_sync_conn_req cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - cp.pkt_type = cpu_to_le16(conn->pkt_type); - - cp.tx_bandwidth = cpu_to_le32(0x00001f40); - cp.rx_bandwidth = cpu_to_le32(0x00001f40); - cp.max_latency = cpu_to_le16(0xffff); - cp.content_format = cpu_to_le16(hdev->voice_setting); - cp.retrans_effort = 0xff; - - hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ, - sizeof(cp), &cp); - } - } else { - /* Connection rejected */ - struct hci_cp_reject_conn_req cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - cp.reason = HCI_ERROR_REJ_BAD_ADDR; - hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp); - } -} - -static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_disconn_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (!conn) - goto unlock; - - if (ev->status == 0) - conn->state = BT_CLOSED; - - if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) && - (conn->type == ACL_LINK || conn->type == LE_LINK)) { - if (ev->status != 0) - mgmt_disconnect_failed(hdev, &conn->dst, conn->type, - conn->dst_type, ev->status); - else - mgmt_device_disconnected(hdev, &conn->dst, conn->type, - conn->dst_type); - } - - if (ev->status == 0) { - if (conn->type == ACL_LINK && conn->flush_key) - hci_remove_link_key(hdev, &conn->dst); - hci_proto_disconn_cfm(conn, ev->reason); - hci_conn_del(conn); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_auth_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (!conn) - goto unlock; - - if (!ev->status) { - if (!hci_conn_ssp_enabled(conn) && - test_bit(HCI_CONN_REAUTH_PEND, &conn->flags)) { - BT_INFO("re-auth of legacy device is not possible."); - } else { - conn->link_mode |= HCI_LM_AUTH; - conn->sec_level = conn->pending_sec_level; - } - } else { - mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type, - ev->status); - } - - clear_bit(HCI_CONN_AUTH_PEND, &conn->flags); - clear_bit(HCI_CONN_REAUTH_PEND, &conn->flags); - - if (conn->state == BT_CONFIG) { - if (!ev->status && hci_conn_ssp_enabled(conn)) { - struct hci_cp_set_conn_encrypt cp; - cp.handle = ev->handle; - cp.encrypt = 0x01; - hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), - &cp); - } else { - conn->state = BT_CONNECTED; - hci_proto_connect_cfm(conn, ev->status); - hci_conn_put(conn); - } - } else { - hci_auth_cfm(conn, ev->status); - - hci_conn_hold(conn); - conn->disc_timeout = HCI_DISCONN_TIMEOUT; - hci_conn_put(conn); - } - - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) { - if (!ev->status) { - struct hci_cp_set_conn_encrypt cp; - cp.handle = ev->handle; - cp.encrypt = 0x01; - hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), - &cp); - } else { - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); - hci_encrypt_cfm(conn, ev->status, 0x00); - } - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_remote_name *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_conn_check_pending(hdev); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - goto check_auth; - - if (ev->status == 0) - hci_check_pending_name(hdev, conn, &ev->bdaddr, ev->name, - strnlen(ev->name, HCI_MAX_NAME_LENGTH)); - else - hci_check_pending_name(hdev, conn, &ev->bdaddr, NULL, 0); - -check_auth: - if (!conn) - goto unlock; - - if (!hci_outgoing_auth_needed(hdev, conn)) - goto unlock; - - if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { - struct hci_cp_auth_requested cp; - cp.handle = __cpu_to_le16(conn->handle); - hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_encrypt_change *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn) { - if (!ev->status) { - if (ev->encrypt) { - /* Encryption implies authentication */ - conn->link_mode |= HCI_LM_AUTH; - conn->link_mode |= HCI_LM_ENCRYPT; - conn->sec_level = conn->pending_sec_level; - } else - conn->link_mode &= ~HCI_LM_ENCRYPT; - } - - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); - - if (ev->status && conn->state == BT_CONNECTED) { - hci_acl_disconn(conn, 0x13); - hci_conn_put(conn); - goto unlock; - } - - if (conn->state == BT_CONFIG) { - if (!ev->status) - conn->state = BT_CONNECTED; - - hci_proto_connect_cfm(conn, ev->status); - hci_conn_put(conn); - } else - hci_encrypt_cfm(conn, ev->status, ev->encrypt); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_change_link_key_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_change_link_key_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn) { - if (!ev->status) - conn->link_mode |= HCI_LM_SECURE; - - clear_bit(HCI_CONN_AUTH_PEND, &conn->flags); - - hci_key_change_cfm(conn, ev->status); - } - - hci_dev_unlock(hdev); -} - -static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_remote_features *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (!conn) - goto unlock; - - if (!ev->status) - memcpy(conn->features, ev->features, 8); - - if (conn->state != BT_CONFIG) - goto unlock; - - if (!ev->status && lmp_ssp_capable(hdev) && lmp_ssp_capable(conn)) { - struct hci_cp_read_remote_ext_features cp; - cp.handle = ev->handle; - cp.page = 0x01; - hci_send_cmd(hdev, HCI_OP_READ_REMOTE_EXT_FEATURES, - sizeof(cp), &cp); - goto unlock; - } - - if (!ev->status && !test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) { - struct hci_cp_remote_name_req cp; - memset(&cp, 0, sizeof(cp)); - bacpy(&cp.bdaddr, &conn->dst); - cp.pscan_rep_mode = 0x02; - hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); - } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, &conn->dst, conn->type, - conn->dst_type, 0, NULL, 0, - conn->dev_class); - - if (!hci_outgoing_auth_needed(hdev, conn)) { - conn->state = BT_CONNECTED; - hci_proto_connect_cfm(conn, ev->status); - hci_conn_put(conn); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_remote_version_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - BT_DBG("%s", hdev->name); -} - -static inline void hci_qos_setup_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - BT_DBG("%s", hdev->name); -} - -static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_cmd_complete *ev = (void *) skb->data; - __u16 opcode; - - skb_pull(skb, sizeof(*ev)); - - opcode = __le16_to_cpu(ev->opcode); - - switch (opcode) { - case HCI_OP_INQUIRY_CANCEL: - hci_cc_inquiry_cancel(hdev, skb); - break; - - case HCI_OP_EXIT_PERIODIC_INQ: - hci_cc_exit_periodic_inq(hdev, skb); - break; - - case HCI_OP_REMOTE_NAME_REQ_CANCEL: - hci_cc_remote_name_req_cancel(hdev, skb); - break; - - case HCI_OP_ROLE_DISCOVERY: - hci_cc_role_discovery(hdev, skb); - break; - - case HCI_OP_READ_LINK_POLICY: - hci_cc_read_link_policy(hdev, skb); - break; - - case HCI_OP_WRITE_LINK_POLICY: - hci_cc_write_link_policy(hdev, skb); - break; - - case HCI_OP_READ_DEF_LINK_POLICY: - hci_cc_read_def_link_policy(hdev, skb); - break; - - case HCI_OP_WRITE_DEF_LINK_POLICY: - hci_cc_write_def_link_policy(hdev, skb); - break; - - case HCI_OP_RESET: - hci_cc_reset(hdev, skb); - break; - - case HCI_OP_WRITE_LOCAL_NAME: - hci_cc_write_local_name(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_NAME: - hci_cc_read_local_name(hdev, skb); - break; - - case HCI_OP_WRITE_AUTH_ENABLE: - hci_cc_write_auth_enable(hdev, skb); - break; - - case HCI_OP_WRITE_ENCRYPT_MODE: - hci_cc_write_encrypt_mode(hdev, skb); - break; - - case HCI_OP_WRITE_SCAN_ENABLE: - hci_cc_write_scan_enable(hdev, skb); - break; - - case HCI_OP_READ_CLASS_OF_DEV: - hci_cc_read_class_of_dev(hdev, skb); - break; - - case HCI_OP_WRITE_CLASS_OF_DEV: - hci_cc_write_class_of_dev(hdev, skb); - break; - - case HCI_OP_READ_VOICE_SETTING: - hci_cc_read_voice_setting(hdev, skb); - break; - - case HCI_OP_WRITE_VOICE_SETTING: - hci_cc_write_voice_setting(hdev, skb); - break; - - case HCI_OP_HOST_BUFFER_SIZE: - hci_cc_host_buffer_size(hdev, skb); - break; - - case HCI_OP_WRITE_SSP_MODE: - hci_cc_write_ssp_mode(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_VERSION: - hci_cc_read_local_version(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_COMMANDS: - hci_cc_read_local_commands(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_FEATURES: - hci_cc_read_local_features(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_EXT_FEATURES: - hci_cc_read_local_ext_features(hdev, skb); - break; - - case HCI_OP_READ_BUFFER_SIZE: - hci_cc_read_buffer_size(hdev, skb); - break; - - case HCI_OP_READ_BD_ADDR: - hci_cc_read_bd_addr(hdev, skb); - break; - - case HCI_OP_READ_DATA_BLOCK_SIZE: - hci_cc_read_data_block_size(hdev, skb); - break; - - case HCI_OP_WRITE_CA_TIMEOUT: - hci_cc_write_ca_timeout(hdev, skb); - break; - - case HCI_OP_READ_FLOW_CONTROL_MODE: - hci_cc_read_flow_control_mode(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_AMP_INFO: - hci_cc_read_local_amp_info(hdev, skb); - break; - - case HCI_OP_DELETE_STORED_LINK_KEY: - hci_cc_delete_stored_link_key(hdev, skb); - break; - - case HCI_OP_SET_EVENT_MASK: - hci_cc_set_event_mask(hdev, skb); - break; - - case HCI_OP_WRITE_INQUIRY_MODE: - hci_cc_write_inquiry_mode(hdev, skb); - break; - - case HCI_OP_READ_INQ_RSP_TX_POWER: - hci_cc_read_inq_rsp_tx_power(hdev, skb); - break; - - case HCI_OP_SET_EVENT_FLT: - hci_cc_set_event_flt(hdev, skb); - break; - - case HCI_OP_PIN_CODE_REPLY: - hci_cc_pin_code_reply(hdev, skb); - break; - - case HCI_OP_PIN_CODE_NEG_REPLY: - hci_cc_pin_code_neg_reply(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_OOB_DATA: - hci_cc_read_local_oob_data_reply(hdev, skb); - break; - - case HCI_OP_LE_READ_BUFFER_SIZE: - hci_cc_le_read_buffer_size(hdev, skb); - break; - - case HCI_OP_USER_CONFIRM_REPLY: - hci_cc_user_confirm_reply(hdev, skb); - break; - - case HCI_OP_USER_CONFIRM_NEG_REPLY: - hci_cc_user_confirm_neg_reply(hdev, skb); - break; - - case HCI_OP_USER_PASSKEY_REPLY: - hci_cc_user_passkey_reply(hdev, skb); - break; - - case HCI_OP_USER_PASSKEY_NEG_REPLY: - hci_cc_user_passkey_neg_reply(hdev, skb); - break; - - case HCI_OP_LE_SET_SCAN_PARAM: - hci_cc_le_set_scan_param(hdev, skb); - break; - - case HCI_OP_LE_SET_SCAN_ENABLE: - hci_cc_le_set_scan_enable(hdev, skb); - break; - - case HCI_OP_LE_LTK_REPLY: - hci_cc_le_ltk_reply(hdev, skb); - break; - - case HCI_OP_LE_LTK_NEG_REPLY: - hci_cc_le_ltk_neg_reply(hdev, skb); - break; - - case HCI_OP_WRITE_LE_HOST_SUPPORTED: - hci_cc_write_le_host_supported(hdev, skb); - break; - - default: - BT_DBG("%s opcode 0x%x", hdev->name, opcode); - break; - } - - if (ev->opcode != HCI_OP_NOP) - del_timer(&hdev->cmd_timer); - - if (ev->ncmd) { - atomic_set(&hdev->cmd_cnt, 1); - if (!skb_queue_empty(&hdev->cmd_q)) - queue_work(hdev->workqueue, &hdev->cmd_work); - } -} - -static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_cmd_status *ev = (void *) skb->data; - __u16 opcode; - - skb_pull(skb, sizeof(*ev)); - - opcode = __le16_to_cpu(ev->opcode); - - switch (opcode) { - case HCI_OP_INQUIRY: - hci_cs_inquiry(hdev, ev->status); - break; - - case HCI_OP_CREATE_CONN: - hci_cs_create_conn(hdev, ev->status); - break; - - case HCI_OP_ADD_SCO: - hci_cs_add_sco(hdev, ev->status); - break; - - case HCI_OP_AUTH_REQUESTED: - hci_cs_auth_requested(hdev, ev->status); - break; - - case HCI_OP_SET_CONN_ENCRYPT: - hci_cs_set_conn_encrypt(hdev, ev->status); - break; - - case HCI_OP_REMOTE_NAME_REQ: - hci_cs_remote_name_req(hdev, ev->status); - break; - - case HCI_OP_READ_REMOTE_FEATURES: - hci_cs_read_remote_features(hdev, ev->status); - break; - - case HCI_OP_READ_REMOTE_EXT_FEATURES: - hci_cs_read_remote_ext_features(hdev, ev->status); - break; - - case HCI_OP_SETUP_SYNC_CONN: - hci_cs_setup_sync_conn(hdev, ev->status); - break; - - case HCI_OP_SNIFF_MODE: - hci_cs_sniff_mode(hdev, ev->status); - break; - - case HCI_OP_EXIT_SNIFF_MODE: - hci_cs_exit_sniff_mode(hdev, ev->status); - break; - - case HCI_OP_DISCONNECT: - hci_cs_disconnect(hdev, ev->status); - break; - - case HCI_OP_LE_CREATE_CONN: - hci_cs_le_create_conn(hdev, ev->status); - break; - - case HCI_OP_LE_START_ENC: - hci_cs_le_start_enc(hdev, ev->status); - break; - - default: - BT_DBG("%s opcode 0x%x", hdev->name, opcode); - break; - } - - if (ev->opcode != HCI_OP_NOP) - del_timer(&hdev->cmd_timer); - - if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { - atomic_set(&hdev->cmd_cnt, 1); - if (!skb_queue_empty(&hdev->cmd_q)) - queue_work(hdev->workqueue, &hdev->cmd_work); - } -} - -static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_role_change *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (conn) { - if (!ev->status) { - if (ev->role) - conn->link_mode &= ~HCI_LM_MASTER; - else - conn->link_mode |= HCI_LM_MASTER; - } - - clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags); - - hci_role_switch_cfm(conn, ev->status, ev->role); - } - - hci_dev_unlock(hdev); -} - -static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_num_comp_pkts *ev = (void *) skb->data; - int i; - - if (hdev->flow_ctl_mode != HCI_FLOW_CTL_MODE_PACKET_BASED) { - BT_ERR("Wrong event for mode %d", hdev->flow_ctl_mode); - return; - } - - if (skb->len < sizeof(*ev) || skb->len < sizeof(*ev) + - ev->num_hndl * sizeof(struct hci_comp_pkts_info)) { - BT_DBG("%s bad parameters", hdev->name); - return; - } - - BT_DBG("%s num_hndl %d", hdev->name, ev->num_hndl); - - for (i = 0; i < ev->num_hndl; i++) { - struct hci_comp_pkts_info *info = &ev->handles[i]; - struct hci_conn *conn; - __u16 handle, count; - - handle = __le16_to_cpu(info->handle); - count = __le16_to_cpu(info->count); - - conn = hci_conn_hash_lookup_handle(hdev, handle); - if (!conn) - continue; - - conn->sent -= count; - - switch (conn->type) { - case ACL_LINK: - hdev->acl_cnt += count; - if (hdev->acl_cnt > hdev->acl_pkts) - hdev->acl_cnt = hdev->acl_pkts; - break; - - case LE_LINK: - if (hdev->le_pkts) { - hdev->le_cnt += count; - if (hdev->le_cnt > hdev->le_pkts) - hdev->le_cnt = hdev->le_pkts; - } else { - hdev->acl_cnt += count; - if (hdev->acl_cnt > hdev->acl_pkts) - hdev->acl_cnt = hdev->acl_pkts; - } - break; - - case SCO_LINK: - hdev->sco_cnt += count; - if (hdev->sco_cnt > hdev->sco_pkts) - hdev->sco_cnt = hdev->sco_pkts; - break; - - default: - BT_ERR("Unknown type %d conn %p", conn->type, conn); - break; - } - } - - queue_work(hdev->workqueue, &hdev->tx_work); -} - -static inline void hci_num_comp_blocks_evt(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_ev_num_comp_blocks *ev = (void *) skb->data; - int i; - - if (hdev->flow_ctl_mode != HCI_FLOW_CTL_MODE_BLOCK_BASED) { - BT_ERR("Wrong event for mode %d", hdev->flow_ctl_mode); - return; - } - - if (skb->len < sizeof(*ev) || skb->len < sizeof(*ev) + - ev->num_hndl * sizeof(struct hci_comp_blocks_info)) { - BT_DBG("%s bad parameters", hdev->name); - return; - } - - BT_DBG("%s num_blocks %d num_hndl %d", hdev->name, ev->num_blocks, - ev->num_hndl); - - for (i = 0; i < ev->num_hndl; i++) { - struct hci_comp_blocks_info *info = &ev->handles[i]; - struct hci_conn *conn; - __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); - if (!conn) - continue; - - conn->sent -= block_count; - - switch (conn->type) { - case ACL_LINK: - hdev->block_cnt += block_count; - if (hdev->block_cnt > hdev->num_blocks) - hdev->block_cnt = hdev->num_blocks; - break; - - default: - BT_ERR("Unknown type %d conn %p", conn->type, conn); - break; - } - } - - queue_work(hdev->workqueue, &hdev->tx_work); -} - -static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_mode_change *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn) { - conn->mode = ev->mode; - conn->interval = __le16_to_cpu(ev->interval); - - if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { - if (conn->mode == HCI_CM_ACTIVE) - set_bit(HCI_CONN_POWER_SAVE, &conn->flags); - else - clear_bit(HCI_CONN_POWER_SAVE, &conn->flags); - } - - if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags)) - hci_sco_setup(conn, ev->status); - } - - hci_dev_unlock(hdev); -} - -static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_pin_code_req *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - if (conn->state == BT_CONNECTED) { - hci_conn_hold(conn); - conn->disc_timeout = HCI_PAIRING_TIMEOUT; - hci_conn_put(conn); - } - - if (!test_bit(HCI_PAIRABLE, &hdev->dev_flags)) - hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, - sizeof(ev->bdaddr), &ev->bdaddr); - else if (test_bit(HCI_MGMT, &hdev->dev_flags)) { - u8 secure; - - if (conn->pending_sec_level == BT_SECURITY_HIGH) - secure = 1; - else - secure = 0; - - mgmt_pin_code_request(hdev, &ev->bdaddr, secure); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_link_key_req *ev = (void *) skb->data; - struct hci_cp_link_key_reply cp; - struct hci_conn *conn; - struct link_key *key; - - BT_DBG("%s", hdev->name); - - if (!test_bit(HCI_LINK_KEYS, &hdev->dev_flags)) - return; - - hci_dev_lock(hdev); - - key = hci_find_link_key(hdev, &ev->bdaddr); - if (!key) { - BT_DBG("%s link key not found for %s", hdev->name, - batostr(&ev->bdaddr)); - goto not_found; - } - - BT_DBG("%s found key type %u for %s", hdev->name, key->type, - batostr(&ev->bdaddr)); - - if (!test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) && - key->type == HCI_LK_DEBUG_COMBINATION) { - BT_DBG("%s ignoring debug key", hdev->name); - goto not_found; - } - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (conn) { - if (key->type == HCI_LK_UNAUTH_COMBINATION && - conn->auth_type != 0xff && - (conn->auth_type & 0x01)) { - BT_DBG("%s ignoring unauthenticated key", hdev->name); - goto not_found; - } - - if (key->type == HCI_LK_COMBINATION && key->pin_len < 16 && - conn->pending_sec_level == BT_SECURITY_HIGH) { - BT_DBG("%s ignoring key unauthenticated for high \ - security", hdev->name); - goto not_found; - } - - conn->key_type = key->type; - conn->pin_length = key->pin_len; - } - - bacpy(&cp.bdaddr, &ev->bdaddr); - memcpy(cp.link_key, key->val, 16); - - hci_send_cmd(hdev, HCI_OP_LINK_KEY_REPLY, sizeof(cp), &cp); - - hci_dev_unlock(hdev); - - return; - -not_found: - hci_send_cmd(hdev, HCI_OP_LINK_KEY_NEG_REPLY, 6, &ev->bdaddr); - hci_dev_unlock(hdev); -} - -static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_link_key_notify *ev = (void *) skb->data; - struct hci_conn *conn; - u8 pin_len = 0; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (conn) { - hci_conn_hold(conn); - conn->disc_timeout = HCI_DISCONN_TIMEOUT; - pin_len = conn->pin_length; - - if (ev->key_type != HCI_LK_CHANGED_COMBINATION) - conn->key_type = ev->key_type; - - hci_conn_put(conn); - } - - if (test_bit(HCI_LINK_KEYS, &hdev->dev_flags)) - hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key, - ev->key_type, pin_len); - - hci_dev_unlock(hdev); -} - -static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_clock_offset *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn && !ev->status) { - struct inquiry_entry *ie; - - ie = hci_inquiry_cache_lookup(hdev, &conn->dst); - if (ie) { - ie->data.clock_offset = ev->clock_offset; - ie->timestamp = jiffies; - } - } - - hci_dev_unlock(hdev); -} - -static inline void hci_pkt_type_change_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_pkt_type_change *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn && !ev->status) - conn->pkt_type = __le16_to_cpu(ev->pkt_type); - - hci_dev_unlock(hdev); -} - -static inline void hci_pscan_rep_mode_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_pscan_rep_mode *ev = (void *) skb->data; - struct inquiry_entry *ie; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr); - if (ie) { - ie->data.pscan_rep_mode = ev->pscan_rep_mode; - ie->timestamp = jiffies; - } - - hci_dev_unlock(hdev); -} - -static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct inquiry_data data; - int num_rsp = *((__u8 *) skb->data); - bool name_known, ssp; - - BT_DBG("%s num_rsp %d", hdev->name, num_rsp); - - if (!num_rsp) - return; - - hci_dev_lock(hdev); - - if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) { - struct inquiry_info_with_rssi_and_pscan_mode *info; - info = (void *) (skb->data + 1); - - for (; num_rsp; num_rsp--, info++) { - bacpy(&data.bdaddr, &info->bdaddr); - data.pscan_rep_mode = info->pscan_rep_mode; - data.pscan_period_mode = info->pscan_period_mode; - data.pscan_mode = info->pscan_mode; - memcpy(data.dev_class, info->dev_class, 3); - data.clock_offset = info->clock_offset; - data.rssi = info->rssi; - data.ssp_mode = 0x00; - - name_known = hci_inquiry_cache_update(hdev, &data, - false, &ssp); - mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, info->rssi, - !name_known, ssp, NULL, 0); - } - } else { - struct inquiry_info_with_rssi *info = (void *) (skb->data + 1); - - for (; num_rsp; num_rsp--, info++) { - bacpy(&data.bdaddr, &info->bdaddr); - data.pscan_rep_mode = info->pscan_rep_mode; - data.pscan_period_mode = info->pscan_period_mode; - data.pscan_mode = 0x00; - memcpy(data.dev_class, info->dev_class, 3); - data.clock_offset = info->clock_offset; - data.rssi = info->rssi; - data.ssp_mode = 0x00; - name_known = hci_inquiry_cache_update(hdev, &data, - false, &ssp); - mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, info->rssi, - !name_known, ssp, NULL, 0); - } - } - - hci_dev_unlock(hdev); -} - -static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_remote_ext_features *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (!conn) - goto unlock; - - if (!ev->status && ev->page == 0x01) { - struct inquiry_entry *ie; - - ie = hci_inquiry_cache_lookup(hdev, &conn->dst); - if (ie) - ie->data.ssp_mode = (ev->features[0] & LMP_HOST_SSP); - - if (ev->features[0] & LMP_HOST_SSP) - set_bit(HCI_CONN_SSP_ENABLED, &conn->flags); - } - - if (conn->state != BT_CONFIG) - goto unlock; - - if (!ev->status && !test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) { - struct hci_cp_remote_name_req cp; - memset(&cp, 0, sizeof(cp)); - bacpy(&cp.bdaddr, &conn->dst); - cp.pscan_rep_mode = 0x02; - hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); - } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, &conn->dst, conn->type, - conn->dst_type, 0, NULL, 0, - conn->dev_class); - - if (!hci_outgoing_auth_needed(hdev, conn)) { - conn->state = BT_CONNECTED; - hci_proto_connect_cfm(conn, ev->status); - hci_conn_put(conn); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_sync_conn_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); - if (!conn) { - if (ev->link_type == ESCO_LINK) - goto unlock; - - conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - conn->type = SCO_LINK; - } - - switch (ev->status) { - case 0x00: - conn->handle = __le16_to_cpu(ev->handle); - conn->state = BT_CONNECTED; - - hci_conn_hold_device(conn); - hci_conn_add_sysfs(conn); - break; - - case 0x11: /* Unsupported Feature or Parameter Value */ - case 0x1c: /* SCO interval rejected */ - case 0x1a: /* Unsupported Remote Feature */ - case 0x1f: /* Unspecified error */ - if (conn->out && conn->attempt < 2) { - conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | - (hdev->esco_type & EDR_ESCO_MASK); - hci_setup_sync(conn, conn->link->handle); - goto unlock; - } - /* fall through */ - - default: - conn->state = BT_CLOSED; - break; - } - - hci_proto_connect_cfm(conn, ev->status); - if (ev->status) - hci_conn_del(conn); - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_sync_conn_changed_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - BT_DBG("%s", hdev->name); -} - -static inline void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_sniff_subrate *ev = (void *) skb->data; - - BT_DBG("%s status %d", hdev->name, ev->status); -} - -static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct inquiry_data data; - struct extended_inquiry_info *info = (void *) (skb->data + 1); - int num_rsp = *((__u8 *) skb->data); - size_t eir_len; - - BT_DBG("%s num_rsp %d", hdev->name, num_rsp); - - if (!num_rsp) - return; - - hci_dev_lock(hdev); - - for (; num_rsp; num_rsp--, info++) { - bool name_known, ssp; - - bacpy(&data.bdaddr, &info->bdaddr); - data.pscan_rep_mode = info->pscan_rep_mode; - data.pscan_period_mode = info->pscan_period_mode; - data.pscan_mode = 0x00; - memcpy(data.dev_class, info->dev_class, 3); - data.clock_offset = info->clock_offset; - data.rssi = info->rssi; - data.ssp_mode = 0x01; - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - name_known = eir_has_data_type(info->data, - sizeof(info->data), - EIR_NAME_COMPLETE); - else - name_known = true; - - name_known = hci_inquiry_cache_update(hdev, &data, name_known, - &ssp); - eir_len = eir_get_length(info->data, sizeof(info->data)); - mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, info->rssi, !name_known, - ssp, info->data, eir_len); - } - - hci_dev_unlock(hdev); -} - -static inline u8 hci_get_auth_req(struct hci_conn *conn) -{ - /* If remote requests dedicated bonding follow that lead */ - if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) { - /* If both remote and local IO capabilities allow MITM - * protection then require it, otherwise don't */ - if (conn->remote_cap == 0x03 || conn->io_capability == 0x03) - return 0x02; - else - return 0x03; - } - - /* If remote requests no-bonding follow that lead */ - if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01) - return conn->remote_auth | (conn->auth_type & 0x01); - - return conn->auth_type; -} - -static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_io_capa_request *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - hci_conn_hold(conn); - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - goto unlock; - - if (test_bit(HCI_PAIRABLE, &hdev->dev_flags) || - (conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) { - struct hci_cp_io_capability_reply cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - /* Change the IO capability from KeyboardDisplay - * to DisplayYesNo as it is not supported by BT spec. */ - cp.capability = (conn->io_capability == 0x04) ? - 0x01 : conn->io_capability; - conn->auth_type = hci_get_auth_req(conn); - cp.authentication = conn->auth_type; - - if ((conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)) && - hci_find_remote_oob_data(hdev, &conn->dst)) - cp.oob_data = 0x01; - else - cp.oob_data = 0x00; - - hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_REPLY, - sizeof(cp), &cp); - } else { - struct hci_cp_io_capability_neg_reply cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - cp.reason = HCI_ERROR_PAIRING_NOT_ALLOWED; - - hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY, - sizeof(cp), &cp); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_io_capa_reply *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - conn->remote_cap = ev->capability; - conn->remote_auth = ev->authentication; - if (ev->oob_data) - set_bit(HCI_CONN_REMOTE_OOB, &conn->flags); - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_user_confirm_request_evt(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_ev_user_confirm_req *ev = (void *) skb->data; - int loc_mitm, rem_mitm, confirm_hint = 0; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - goto unlock; - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - loc_mitm = (conn->auth_type & 0x01); - rem_mitm = (conn->remote_auth & 0x01); - - /* If we require MITM but the remote device can't provide that - * (it has NoInputNoOutput) then reject the confirmation - * request. The only exception is when we're dedicated bonding - * initiators (connect_cfm_cb set) since then we always have the MITM - * bit set. */ - if (!conn->connect_cfm_cb && loc_mitm && conn->remote_cap == 0x03) { - BT_DBG("Rejecting request: remote device can't provide MITM"); - hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY, - sizeof(ev->bdaddr), &ev->bdaddr); - goto unlock; - } - - /* If no side requires MITM protection; auto-accept */ - if ((!loc_mitm || conn->remote_cap == 0x03) && - (!rem_mitm || conn->io_capability == 0x03)) { - - /* If we're not the initiators request authorization to - * proceed from user space (mgmt_user_confirm with - * confirm_hint set to 1). */ - if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { - BT_DBG("Confirming auto-accept as acceptor"); - confirm_hint = 1; - goto confirm; - } - - BT_DBG("Auto-accept of user confirmation with %ums delay", - hdev->auto_accept_delay); - - if (hdev->auto_accept_delay > 0) { - int delay = msecs_to_jiffies(hdev->auto_accept_delay); - mod_timer(&conn->auto_accept_timer, jiffies + delay); - goto unlock; - } - - hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, - sizeof(ev->bdaddr), &ev->bdaddr); - goto unlock; - } - -confirm: - mgmt_user_confirm_request(hdev, &ev->bdaddr, ACL_LINK, 0, ev->passkey, - confirm_hint); - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_user_passkey_request_evt(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_ev_user_passkey_req *ev = (void *) skb->data; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_passkey_request(hdev, &ev->bdaddr, ACL_LINK, 0); - - hci_dev_unlock(hdev); -} - -static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_simple_pair_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - /* To avoid duplicate auth_failed events to user space we check - * the HCI_CONN_AUTH_PEND flag which will be set if we - * initiated the authentication. A traditional auth_complete - * event gets always produced as initiator and is also mapped to - * the mgmt_auth_failed event */ - if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status != 0) - mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type, - ev->status); - - hci_conn_put(conn); - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_remote_host_features *ev = (void *) skb->data; - struct inquiry_entry *ie; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr); - if (ie) - ie->data.ssp_mode = (ev->features[0] & LMP_HOST_SSP); - - hci_dev_unlock(hdev); -} - -static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_ev_remote_oob_data_request *ev = (void *) skb->data; - struct oob_data *data; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - goto unlock; - - data = hci_find_remote_oob_data(hdev, &ev->bdaddr); - if (data) { - struct hci_cp_remote_oob_data_reply cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - memcpy(cp.hash, data->hash, sizeof(cp.hash)); - memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer)); - - hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY, sizeof(cp), - &cp); - } else { - struct hci_cp_remote_oob_data_neg_reply cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY, sizeof(cp), - &cp); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_le_conn_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr); - if (!conn) { - conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr); - if (!conn) { - BT_ERR("No memory for new connection"); - hci_dev_unlock(hdev); - return; - } - - conn->dst_type = ev->bdaddr_type; - } - - if (ev->status) { - mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, - conn->dst_type, ev->status); - hci_proto_connect_cfm(conn, ev->status); - conn->state = BT_CLOSED; - hci_conn_del(conn); - goto unlock; - } - - if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, &ev->bdaddr, conn->type, - conn->dst_type, 0, NULL, 0, NULL); - - conn->sec_level = BT_SECURITY_LOW; - conn->handle = __le16_to_cpu(ev->handle); - conn->state = BT_CONNECTED; - - hci_conn_hold_device(conn); - hci_conn_add_sysfs(conn); - - hci_proto_connect_cfm(conn, ev->status); - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_le_adv_report_evt(struct hci_dev *hdev, - struct sk_buff *skb) -{ - u8 num_reports = skb->data[0]; - void *ptr = &skb->data[1]; - s8 rssi; - - hci_dev_lock(hdev); - - while (num_reports--) { - struct hci_ev_le_advertising_info *ev = ptr; - - hci_add_adv_entry(hdev, ev); - - rssi = ev->data[ev->length]; - mgmt_device_found(hdev, &ev->bdaddr, LE_LINK, ev->bdaddr_type, - NULL, rssi, 0, 1, ev->data, ev->length); - - ptr += sizeof(*ev) + ev->length + 1; - } - - hci_dev_unlock(hdev); -} - -static inline void hci_le_ltk_request_evt(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_ev_le_ltk_req *ev = (void *) skb->data; - struct hci_cp_le_ltk_reply cp; - struct hci_cp_le_ltk_neg_reply neg; - struct hci_conn *conn; - struct smp_ltk *ltk; - - BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle)); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn == NULL) - goto not_found; - - ltk = hci_find_ltk(hdev, ev->ediv, ev->random); - if (ltk == NULL) - goto not_found; - - memcpy(cp.ltk, ltk->val, sizeof(ltk->val)); - cp.handle = cpu_to_le16(conn->handle); - - if (ltk->authenticated) - conn->sec_level = BT_SECURITY_HIGH; - - hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); - - if (ltk->type & HCI_SMP_STK) { - list_del(<k->list); - kfree(ltk); - } - - hci_dev_unlock(hdev); - - return; - -not_found: - neg.handle = ev->handle; - hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(neg), &neg); - hci_dev_unlock(hdev); -} - -static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_le_meta *le_ev = (void *) skb->data; - - skb_pull(skb, sizeof(*le_ev)); - - switch (le_ev->subevent) { - case HCI_EV_LE_CONN_COMPLETE: - hci_le_conn_complete_evt(hdev, skb); - break; - - case HCI_EV_LE_ADVERTISING_REPORT: - hci_le_adv_report_evt(hdev, skb); - break; - - case HCI_EV_LE_LTK_REQ: - hci_le_ltk_request_evt(hdev, skb); - break; - - default: - break; - } -} - -void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_event_hdr *hdr = (void *) skb->data; - __u8 event = hdr->evt; - - skb_pull(skb, HCI_EVENT_HDR_SIZE); - - switch (event) { - case HCI_EV_INQUIRY_COMPLETE: - hci_inquiry_complete_evt(hdev, skb); - break; - - case HCI_EV_INQUIRY_RESULT: - hci_inquiry_result_evt(hdev, skb); - break; - - case HCI_EV_CONN_COMPLETE: - hci_conn_complete_evt(hdev, skb); - break; - - case HCI_EV_CONN_REQUEST: - hci_conn_request_evt(hdev, skb); - break; - - case HCI_EV_DISCONN_COMPLETE: - hci_disconn_complete_evt(hdev, skb); - break; - - case HCI_EV_AUTH_COMPLETE: - hci_auth_complete_evt(hdev, skb); - break; - - case HCI_EV_REMOTE_NAME: - hci_remote_name_evt(hdev, skb); - break; - - case HCI_EV_ENCRYPT_CHANGE: - hci_encrypt_change_evt(hdev, skb); - break; - - case HCI_EV_CHANGE_LINK_KEY_COMPLETE: - hci_change_link_key_complete_evt(hdev, skb); - break; - - case HCI_EV_REMOTE_FEATURES: - hci_remote_features_evt(hdev, skb); - break; - - case HCI_EV_REMOTE_VERSION: - hci_remote_version_evt(hdev, skb); - break; - - case HCI_EV_QOS_SETUP_COMPLETE: - hci_qos_setup_complete_evt(hdev, skb); - break; - - case HCI_EV_CMD_COMPLETE: - hci_cmd_complete_evt(hdev, skb); - break; - - case HCI_EV_CMD_STATUS: - hci_cmd_status_evt(hdev, skb); - break; - - case HCI_EV_ROLE_CHANGE: - hci_role_change_evt(hdev, skb); - break; - - case HCI_EV_NUM_COMP_PKTS: - hci_num_comp_pkts_evt(hdev, skb); - break; - - case HCI_EV_MODE_CHANGE: - hci_mode_change_evt(hdev, skb); - break; - - case HCI_EV_PIN_CODE_REQ: - hci_pin_code_request_evt(hdev, skb); - break; - - case HCI_EV_LINK_KEY_REQ: - hci_link_key_request_evt(hdev, skb); - break; - - case HCI_EV_LINK_KEY_NOTIFY: - hci_link_key_notify_evt(hdev, skb); - break; - - case HCI_EV_CLOCK_OFFSET: - hci_clock_offset_evt(hdev, skb); - break; - - case HCI_EV_PKT_TYPE_CHANGE: - hci_pkt_type_change_evt(hdev, skb); - break; - - case HCI_EV_PSCAN_REP_MODE: - hci_pscan_rep_mode_evt(hdev, skb); - break; - - case HCI_EV_INQUIRY_RESULT_WITH_RSSI: - hci_inquiry_result_with_rssi_evt(hdev, skb); - break; - - case HCI_EV_REMOTE_EXT_FEATURES: - hci_remote_ext_features_evt(hdev, skb); - break; - - case HCI_EV_SYNC_CONN_COMPLETE: - hci_sync_conn_complete_evt(hdev, skb); - break; - - case HCI_EV_SYNC_CONN_CHANGED: - hci_sync_conn_changed_evt(hdev, skb); - break; - - case HCI_EV_SNIFF_SUBRATE: - hci_sniff_subrate_evt(hdev, skb); - break; - - case HCI_EV_EXTENDED_INQUIRY_RESULT: - hci_extended_inquiry_result_evt(hdev, skb); - break; - - case HCI_EV_IO_CAPA_REQUEST: - hci_io_capa_request_evt(hdev, skb); - break; - - case HCI_EV_IO_CAPA_REPLY: - hci_io_capa_reply_evt(hdev, skb); - break; - - case HCI_EV_USER_CONFIRM_REQUEST: - hci_user_confirm_request_evt(hdev, skb); - break; - - case HCI_EV_USER_PASSKEY_REQUEST: - hci_user_passkey_request_evt(hdev, skb); - break; - - case HCI_EV_SIMPLE_PAIR_COMPLETE: - hci_simple_pair_complete_evt(hdev, skb); - break; - - case HCI_EV_REMOTE_HOST_FEATURES: - hci_remote_host_features_evt(hdev, skb); - break; - - case HCI_EV_LE_META: - hci_le_meta_evt(hdev, skb); - break; - - case HCI_EV_REMOTE_OOB_DATA_REQUEST: - hci_remote_oob_data_request_evt(hdev, skb); - break; - - case HCI_EV_NUM_COMP_BLOCKS: - hci_num_comp_blocks_evt(hdev, skb); - break; - - default: - BT_DBG("%s event 0x%x", hdev->name, event); - break; - } - - kfree_skb(skb); - hdev->stat.evt_rx++; -} - -/* Generate internal stack event */ -void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) -{ - struct hci_event_hdr *hdr; - struct hci_ev_stack_internal *ev; - struct sk_buff *skb; - - skb = bt_skb_alloc(HCI_EVENT_HDR_SIZE + sizeof(*ev) + dlen, GFP_ATOMIC); - if (!skb) - return; - - hdr = (void *) skb_put(skb, HCI_EVENT_HDR_SIZE); - hdr->evt = HCI_EV_STACK_INTERNAL; - hdr->plen = sizeof(*ev) + dlen; - - ev = (void *) skb_put(skb, sizeof(*ev) + dlen); - ev->type = type; - memcpy(ev->data, data, dlen); - - bt_cb(skb)->incoming = 1; - __net_timestamp(skb); - - bt_cb(skb)->pkt_type = HCI_EVENT_PKT; - skb->dev = (void *) hdev; - hci_send_to_sock(hdev, skb); - kfree_skb(skb); -} diff --git a/net/bluetooth_tizen/hci_sock.c b/net/bluetooth_tizen/hci_sock.c deleted file mode 100644 index 63c1516..0000000 --- a/net/bluetooth_tizen/hci_sock.c +++ /dev/null @@ -1,1102 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth HCI sockets. */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/capability.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/skbuff.h> -#include <linux/workqueue.h> -#include <linux/interrupt.h> -#include <linux/compat.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <linux/uaccess.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/hci_mon.h> - -static atomic_t monitor_promisc = ATOMIC_INIT(0); - -/* ----- HCI socket interface ----- */ - -static inline int hci_test_bit(int nr, void *addr) -{ - return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); -} - -/* Security filter */ -static struct hci_sec_filter hci_sec_filter = { - /* Packet types */ - 0x10, - /* Events */ - { 0x1000d9fe, 0x0000b00c }, - /* Commands */ - { - { 0x0 }, - /* OGF_LINK_CTL */ - { 0xbe000006, 0x00000001, 0x00000000, 0x00 }, - /* OGF_LINK_POLICY */ - { 0x00005200, 0x00000000, 0x00000000, 0x00 }, - /* OGF_HOST_CTL */ - { 0xaab00200, 0x2b402aaa, 0x05220154, 0x00 }, - /* OGF_INFO_PARAM */ - { 0x000002be, 0x00000000, 0x00000000, 0x00 }, - /* OGF_STATUS_PARAM */ - { 0x000000ea, 0x00000000, 0x00000000, 0x00 } - } -}; - -static struct bt_sock_list hci_sk_list = { - .lock = __RW_LOCK_UNLOCKED(hci_sk_list.lock) -}; - -/* Send frame to RAW socket */ -void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct sock *sk; - struct hlist_node *node; - struct sk_buff *skb_copy = NULL; - - BT_DBG("hdev %p len %d", hdev, skb->len); - - read_lock(&hci_sk_list.lock); - - sk_for_each(sk, node, &hci_sk_list.head) { - struct hci_filter *flt; - struct sk_buff *nskb; - - if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev) - continue; - - /* Don't send frame to the socket it came from */ - if (skb->sk == sk) - continue; - - if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) - continue; - - /* Apply filter */ - flt = &hci_pi(sk)->filter; - - if (!test_bit((bt_cb(skb)->pkt_type == HCI_VENDOR_PKT) ? - 0 : (bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask)) - continue; - - if (bt_cb(skb)->pkt_type == HCI_EVENT_PKT) { - register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS); - - if (!hci_test_bit(evt, &flt->event_mask)) - continue; - - if (flt->opcode && - ((evt == HCI_EV_CMD_COMPLETE && - flt->opcode != - get_unaligned((__le16 *)(skb->data + 3))) || - (evt == HCI_EV_CMD_STATUS && - flt->opcode != - get_unaligned((__le16 *)(skb->data + 4))))) - continue; - } - - if (!skb_copy) { - /* Create a private copy with headroom */ - skb_copy = __pskb_copy(skb, 1, GFP_ATOMIC); - if (!skb_copy) - continue; - - /* Put type byte before the data */ - memcpy(skb_push(skb_copy, 1), &bt_cb(skb)->pkt_type, 1); - } - - nskb = skb_clone(skb_copy, GFP_ATOMIC); - if (!nskb) - continue; - - if (sock_queue_rcv_skb(sk, nskb)) - kfree_skb(nskb); - } - - read_unlock(&hci_sk_list.lock); - - kfree_skb(skb_copy); -} - -/* Send frame to control socket */ -void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk) -{ - struct sock *sk; - struct hlist_node *node; - - BT_DBG("len %d", skb->len); - - read_lock(&hci_sk_list.lock); - - sk_for_each(sk, node, &hci_sk_list.head) { - struct sk_buff *nskb; - - /* Skip the original socket */ - if (sk == skip_sk) - continue; - - if (sk->sk_state != BT_BOUND) - continue; - - if (hci_pi(sk)->channel != HCI_CHANNEL_CONTROL) - continue; - - nskb = skb_clone(skb, GFP_ATOMIC); - if (!nskb) - continue; - - if (sock_queue_rcv_skb(sk, nskb)) - kfree_skb(nskb); - } - - read_unlock(&hci_sk_list.lock); -} - -/* Send frame to monitor socket */ -void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct sock *sk; - struct hlist_node *node; - struct sk_buff *skb_copy = NULL; - __le16 opcode; - - if (!atomic_read(&monitor_promisc)) - return; - - BT_DBG("hdev %p len %d", hdev, skb->len); - - switch (bt_cb(skb)->pkt_type) { - case HCI_COMMAND_PKT: - opcode = __constant_cpu_to_le16(HCI_MON_COMMAND_PKT); - break; - case HCI_EVENT_PKT: - opcode = __constant_cpu_to_le16(HCI_MON_EVENT_PKT); - break; - case HCI_ACLDATA_PKT: - if (bt_cb(skb)->incoming) - opcode = __constant_cpu_to_le16(HCI_MON_ACL_RX_PKT); - else - opcode = __constant_cpu_to_le16(HCI_MON_ACL_TX_PKT); - break; - case HCI_SCODATA_PKT: - if (bt_cb(skb)->incoming) - opcode = __constant_cpu_to_le16(HCI_MON_SCO_RX_PKT); - else - opcode = __constant_cpu_to_le16(HCI_MON_SCO_TX_PKT); - break; - default: - return; - } - - read_lock(&hci_sk_list.lock); - - sk_for_each(sk, node, &hci_sk_list.head) { - struct sk_buff *nskb; - - if (sk->sk_state != BT_BOUND) - continue; - - if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR) - continue; - - if (!skb_copy) { - struct hci_mon_hdr *hdr; - - /* Create a private copy with headroom */ - skb_copy = __pskb_copy(skb, HCI_MON_HDR_SIZE, GFP_ATOMIC); - if (!skb_copy) - continue; - - /* Put header before the data */ - hdr = (void *) skb_push(skb_copy, HCI_MON_HDR_SIZE); - hdr->opcode = opcode; - hdr->index = cpu_to_le16(hdev->id); - hdr->len = cpu_to_le16(skb->len); - } - - nskb = skb_clone(skb_copy, GFP_ATOMIC); - if (!nskb) - continue; - - if (sock_queue_rcv_skb(sk, nskb)) - kfree_skb(nskb); - } - - read_unlock(&hci_sk_list.lock); - - kfree_skb(skb_copy); -} - -static void send_monitor_event(struct sk_buff *skb) -{ - struct sock *sk; - struct hlist_node *node; - - BT_DBG("len %d", skb->len); - - read_lock(&hci_sk_list.lock); - - sk_for_each(sk, node, &hci_sk_list.head) { - struct sk_buff *nskb; - - if (sk->sk_state != BT_BOUND) - continue; - - if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR) - continue; - - nskb = skb_clone(skb, GFP_ATOMIC); - if (!nskb) - continue; - - if (sock_queue_rcv_skb(sk, nskb)) - kfree_skb(nskb); - } - - read_unlock(&hci_sk_list.lock); -} - -static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event) -{ - struct hci_mon_hdr *hdr; - struct hci_mon_new_index *ni; - struct sk_buff *skb; - __le16 opcode; - - switch (event) { - case HCI_DEV_REG: - skb = bt_skb_alloc(HCI_MON_NEW_INDEX_SIZE, GFP_ATOMIC); - if (!skb) - return NULL; - - ni = (void *) skb_put(skb, HCI_MON_NEW_INDEX_SIZE); - ni->type = hdev->dev_type; - ni->bus = hdev->bus; - bacpy(&ni->bdaddr, &hdev->bdaddr); - memcpy(ni->name, hdev->name, 8); - - opcode = __constant_cpu_to_le16(HCI_MON_NEW_INDEX); - break; - - case HCI_DEV_UNREG: - skb = bt_skb_alloc(0, GFP_ATOMIC); - if (!skb) - return NULL; - - opcode = __constant_cpu_to_le16(HCI_MON_DEL_INDEX); - break; - - default: - return NULL; - } - - __net_timestamp(skb); - - hdr = (void *) skb_push(skb, HCI_MON_HDR_SIZE); - hdr->opcode = opcode; - hdr->index = cpu_to_le16(hdev->id); - hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE); - - return skb; -} - -static void send_monitor_replay(struct sock *sk) -{ - struct hci_dev *hdev; - - read_lock(&hci_dev_list_lock); - - list_for_each_entry(hdev, &hci_dev_list, list) { - struct sk_buff *skb; - - skb = create_monitor_event(hdev, HCI_DEV_REG); - if (!skb) - continue; - - if (sock_queue_rcv_skb(sk, skb)) - kfree_skb(skb); - } - - read_unlock(&hci_dev_list_lock); -} - -static int hci_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - struct hci_dev *hdev; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!sk) - return 0; - - hdev = hci_pi(sk)->hdev; - - if (hci_pi(sk)->channel == HCI_CHANNEL_MONITOR) - atomic_dec(&monitor_promisc); - - bt_sock_unlink(&hci_sk_list, sk); - - if (hdev) { - atomic_dec(&hdev->promisc); - hci_dev_put(hdev); - } - - sock_orphan(sk); - - skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); - - sock_put(sk); - return 0; -} - -static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) -{ - bdaddr_t bdaddr; - int err; - - if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) - return -EFAULT; - - hci_dev_lock(hdev); - - err = hci_blacklist_add(hdev, &bdaddr, 0); - - hci_dev_unlock(hdev); - - return err; -} - -static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg) -{ - bdaddr_t bdaddr; - int err; - - if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) - return -EFAULT; - - hci_dev_lock(hdev); - - err = hci_blacklist_del(hdev, &bdaddr, 0); - - hci_dev_unlock(hdev); - - return err; -} - -/* Ioctls that require bound socket */ -static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg) -{ - struct hci_dev *hdev = hci_pi(sk)->hdev; - - if (!hdev) - return -EBADFD; - - switch (cmd) { - case HCISETRAW: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) - return -EPERM; - - if (arg) - set_bit(HCI_RAW, &hdev->flags); - else - clear_bit(HCI_RAW, &hdev->flags); - - return 0; - - case HCIGETCONNINFO: - return hci_get_conn_info(hdev, (void __user *) arg); - - case HCIGETAUTHINFO: - return hci_get_auth_info(hdev, (void __user *) arg); - - case HCIBLOCKADDR: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_sock_blacklist_add(hdev, (void __user *) arg); - - case HCIUNBLOCKADDR: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_sock_blacklist_del(hdev, (void __user *) arg); - - default: - if (hdev->ioctl) - return hdev->ioctl(hdev, cmd, arg); - return -EINVAL; - } -} - -static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk = sock->sk; - void __user *argp = (void __user *) arg; - int err; - - BT_DBG("cmd %x arg %lx", cmd, arg); - - switch (cmd) { - case HCIGETDEVLIST: - return hci_get_dev_list(argp); - - case HCIGETDEVINFO: - return hci_get_dev_info(argp); - - case HCIGETCONNLIST: - return hci_get_conn_list(argp); - - case HCIDEVUP: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_dev_open(arg); - - case HCIDEVDOWN: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_dev_close(arg); - - case HCIDEVRESET: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_dev_reset(arg); - - case HCIDEVRESTAT: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_dev_reset_stat(arg); - - case HCISETSCAN: - case HCISETAUTH: - case HCISETENCRYPT: - case HCISETPTYPE: - case HCISETLINKPOL: - case HCISETLINKMODE: - case HCISETACLMTU: - case HCISETSCOMTU: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_dev_cmd(cmd, argp); - - case HCIINQUIRY: - return hci_inquiry(argp); - - default: - lock_sock(sk); - err = hci_sock_bound_ioctl(sk, cmd, arg); - release_sock(sk); - return err; - } -} - -static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) -{ - struct sockaddr_hci haddr; - struct sock *sk = sock->sk; - struct hci_dev *hdev = NULL; - int len, err = 0; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!addr) - return -EINVAL; - - memset(&haddr, 0, sizeof(haddr)); - len = min_t(unsigned int, sizeof(haddr), addr_len); - memcpy(&haddr, addr, len); - - if (haddr.hci_family != AF_BLUETOOTH) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state == BT_BOUND) { - err = -EALREADY; - goto done; - } - - switch (haddr.hci_channel) { - case HCI_CHANNEL_RAW: - if (hci_pi(sk)->hdev) { - err = -EALREADY; - goto done; - } - - if (haddr.hci_dev != HCI_DEV_NONE) { - hdev = hci_dev_get(haddr.hci_dev); - if (!hdev) { - err = -ENODEV; - goto done; - } - - atomic_inc(&hdev->promisc); - } - - hci_pi(sk)->hdev = hdev; - break; - - case HCI_CHANNEL_CONTROL: - if (haddr.hci_dev != HCI_DEV_NONE) { - err = -EINVAL; - goto done; - } - - if (!capable(CAP_NET_ADMIN)) { - err = -EPERM; - goto done; - } - - break; - - case HCI_CHANNEL_MONITOR: - if (haddr.hci_dev != HCI_DEV_NONE) { - err = -EINVAL; - goto done; - } - - if (!capable(CAP_NET_RAW)) { - err = -EPERM; - goto done; - } - - send_monitor_replay(sk); - - atomic_inc(&monitor_promisc); - break; - - default: - err = -EINVAL; - goto done; - } - - - hci_pi(sk)->channel = haddr.hci_channel; - sk->sk_state = BT_BOUND; - -done: - release_sock(sk); - return err; -} - -static int hci_sock_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer) -{ - struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr; - struct sock *sk = sock->sk; - struct hci_dev *hdev = hci_pi(sk)->hdev; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!hdev) - return -EBADFD; - - lock_sock(sk); - - *addr_len = sizeof(*haddr); - haddr->hci_family = AF_BLUETOOTH; - haddr->hci_dev = hdev->id; - - release_sock(sk); - return 0; -} - -static inline void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) -{ - __u32 mask = hci_pi(sk)->cmsg_mask; - - if (mask & HCI_CMSG_DIR) { - int incoming = bt_cb(skb)->incoming; - put_cmsg(msg, SOL_HCI, HCI_CMSG_DIR, sizeof(incoming), &incoming); - } - - if (mask & HCI_CMSG_TSTAMP) { -#ifdef CONFIG_COMPAT - struct compat_timeval ctv; -#endif - struct timeval tv; - void *data; - int len; - - skb_get_timestamp(skb, &tv); - - data = &tv; - len = sizeof(tv); -#ifdef CONFIG_COMPAT - if (msg->msg_flags & MSG_CMSG_COMPAT) { - ctv.tv_sec = tv.tv_sec; - ctv.tv_usec = tv.tv_usec; - data = &ctv; - len = sizeof(ctv); - } -#endif - - put_cmsg(msg, SOL_HCI, HCI_CMSG_TSTAMP, len, data); - } -} - -static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) -{ - int noblock = flags & MSG_DONTWAIT; - struct sock *sk = sock->sk; - struct sk_buff *skb; - int copied, err; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (flags & (MSG_OOB)) - return -EOPNOTSUPP; - - if (sk->sk_state == BT_CLOSED) - return 0; - - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) - return err; - - msg->msg_namelen = 0; - - copied = skb->len; - if (len < copied) { - msg->msg_flags |= MSG_TRUNC; - copied = len; - } - - skb_reset_transport_header(skb); - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); - - switch (hci_pi(sk)->channel) { - case HCI_CHANNEL_RAW: - hci_sock_cmsg(sk, msg, skb); - break; - case HCI_CHANNEL_CONTROL: - case HCI_CHANNEL_MONITOR: - sock_recv_timestamp(msg, sk, skb); - break; - } - - skb_free_datagram(sk, skb); - - return err ? : copied; -} - -static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct hci_dev *hdev; - struct sk_buff *skb; - int err; - - BT_DBG("sock %p sk %p", sock, sk); - - if (msg->msg_flags & MSG_OOB) - return -EOPNOTSUPP; - - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE)) - return -EINVAL; - - if (len < 4 || len > HCI_MAX_FRAME_SIZE) - return -EINVAL; - - lock_sock(sk); - - switch (hci_pi(sk)->channel) { - case HCI_CHANNEL_RAW: - break; - case HCI_CHANNEL_CONTROL: - err = mgmt_control(sk, msg, len); - goto done; - case HCI_CHANNEL_MONITOR: - err = -EOPNOTSUPP; - goto done; - default: - err = -EINVAL; - goto done; - } - - hdev = hci_pi(sk)->hdev; - if (!hdev) { - err = -EBADFD; - goto done; - } - - if (!test_bit(HCI_UP, &hdev->flags)) { - err = -ENETDOWN; - goto done; - } - - skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) - goto done; - - if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { - err = -EFAULT; - goto drop; - } - - bt_cb(skb)->pkt_type = *((unsigned char *) skb->data); - skb_pull(skb, 1); - skb->dev = (void *) hdev; - - if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) { - u16 opcode = get_unaligned_le16(skb->data); - u16 ogf = hci_opcode_ogf(opcode); - u16 ocf = hci_opcode_ocf(opcode); - - if (((ogf > HCI_SFLT_MAX_OGF) || - !hci_test_bit(ocf & HCI_FLT_OCF_BITS, &hci_sec_filter.ocf_mask[ogf])) && - !capable(CAP_NET_RAW)) { - err = -EPERM; - goto drop; - } - - if (test_bit(HCI_RAW, &hdev->flags) || (ogf == 0x3f)) { - skb_queue_tail(&hdev->raw_q, skb); - queue_work(hdev->workqueue, &hdev->tx_work); - } else { - skb_queue_tail(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); - } - } else { - if (!capable(CAP_NET_RAW)) { - err = -EPERM; - goto drop; - } - - skb_queue_tail(&hdev->raw_q, skb); - queue_work(hdev->workqueue, &hdev->tx_work); - } - - err = len; - -done: - release_sock(sk); - return err; - -drop: - kfree_skb(skb); - goto done; -} - -static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int len) -{ - struct hci_ufilter uf = { .opcode = 0 }; - struct sock *sk = sock->sk; - int err = 0, opt = 0; - - BT_DBG("sk %p, opt %d", sk, optname); - - lock_sock(sk); - - if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) { - err = -EINVAL; - goto done; - } - - switch (optname) { - case HCI_DATA_DIR: - if (get_user(opt, (int __user *)optval)) { - err = -EFAULT; - break; - } - - if (opt) - hci_pi(sk)->cmsg_mask |= HCI_CMSG_DIR; - else - hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_DIR; - break; - - case HCI_TIME_STAMP: - if (get_user(opt, (int __user *)optval)) { - err = -EFAULT; - break; - } - - if (opt) - hci_pi(sk)->cmsg_mask |= HCI_CMSG_TSTAMP; - else - hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_TSTAMP; - break; - - case HCI_FILTER: - { - struct hci_filter *f = &hci_pi(sk)->filter; - - uf.type_mask = f->type_mask; - uf.opcode = f->opcode; - uf.event_mask[0] = *((u32 *) f->event_mask + 0); - uf.event_mask[1] = *((u32 *) f->event_mask + 1); - } - - len = min_t(unsigned int, len, sizeof(uf)); - if (copy_from_user(&uf, optval, len)) { - err = -EFAULT; - break; - } - - if (!capable(CAP_NET_RAW)) { - uf.type_mask &= hci_sec_filter.type_mask; - uf.event_mask[0] &= *((u32 *) hci_sec_filter.event_mask + 0); - uf.event_mask[1] &= *((u32 *) hci_sec_filter.event_mask + 1); - } - - { - struct hci_filter *f = &hci_pi(sk)->filter; - - f->type_mask = uf.type_mask; - f->opcode = uf.opcode; - *((u32 *) f->event_mask + 0) = uf.event_mask[0]; - *((u32 *) f->event_mask + 1) = uf.event_mask[1]; - } - break; - - default: - err = -ENOPROTOOPT; - break; - } - -done: - release_sock(sk); - return err; -} - -static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) -{ - struct hci_ufilter uf; - struct sock *sk = sock->sk; - int len, opt, err = 0; - - BT_DBG("sk %p, opt %d", sk, optname); - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) { - err = -EINVAL; - goto done; - } - - switch (optname) { - case HCI_DATA_DIR: - if (hci_pi(sk)->cmsg_mask & HCI_CMSG_DIR) - opt = 1; - else - opt = 0; - - if (put_user(opt, optval)) - err = -EFAULT; - break; - - case HCI_TIME_STAMP: - if (hci_pi(sk)->cmsg_mask & HCI_CMSG_TSTAMP) - opt = 1; - else - opt = 0; - - if (put_user(opt, optval)) - err = -EFAULT; - break; - - case HCI_FILTER: - { - struct hci_filter *f = &hci_pi(sk)->filter; - - uf.type_mask = f->type_mask; - uf.opcode = f->opcode; - uf.event_mask[0] = *((u32 *) f->event_mask + 0); - uf.event_mask[1] = *((u32 *) f->event_mask + 1); - } - - len = min_t(unsigned int, len, sizeof(uf)); - if (copy_to_user(optval, &uf, len)) - err = -EFAULT; - break; - - default: - err = -ENOPROTOOPT; - break; - } - -done: - release_sock(sk); - return err; -} - -static const struct proto_ops hci_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = hci_sock_release, - .bind = hci_sock_bind, - .getname = hci_sock_getname, - .sendmsg = hci_sock_sendmsg, - .recvmsg = hci_sock_recvmsg, - .ioctl = hci_sock_ioctl, - .poll = datagram_poll, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = hci_sock_setsockopt, - .getsockopt = hci_sock_getsockopt, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - -static struct proto hci_sk_proto = { - .name = "HCI", - .owner = THIS_MODULE, - .obj_size = sizeof(struct hci_pinfo) -}; - -static int hci_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - if (sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - sock->ops = &hci_sock_ops; - - sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hci_sk_proto); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = protocol; - - sock->state = SS_UNCONNECTED; - sk->sk_state = BT_OPEN; - - bt_sock_link(&hci_sk_list, sk); - return 0; -} - -static int hci_sock_dev_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct hci_dev *hdev = (struct hci_dev *) ptr; - struct hci_ev_si_device ev; - - BT_DBG("hdev %s event %ld", hdev->name, event); - - /* Send event to sockets */ - ev.event = event; - ev.dev_id = hdev->id; - hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev); - - if (event == HCI_DEV_UNREG) { - struct sock *sk; - struct hlist_node *node; - - /* Detach sockets from device */ - read_lock(&hci_sk_list.lock); - sk_for_each(sk, node, &hci_sk_list.head) { - bh_lock_sock_nested(sk); - if (hci_pi(sk)->hdev == hdev) { - hci_pi(sk)->hdev = NULL; - sk->sk_err = EPIPE; - sk->sk_state = BT_OPEN; - sk->sk_state_change(sk); - - hci_dev_put(hdev); - } - bh_unlock_sock(sk); - } - read_unlock(&hci_sk_list.lock); - } - - return NOTIFY_DONE; -} - -static const struct net_proto_family hci_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = hci_sock_create, -}; - -static struct notifier_block hci_sock_nblock = { - .notifier_call = hci_sock_dev_event -}; - -int __init hci_sock_init(void) -{ - int err; - - err = proto_register(&hci_sk_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_HCI, &hci_sock_family_ops); - if (err < 0) - goto error; - - hci_register_notifier(&hci_sock_nblock); - - BT_INFO("HCI socket layer initialized"); - - return 0; - -error: - BT_ERR("HCI socket registration failed"); - proto_unregister(&hci_sk_proto); - return err; -} - -void hci_sock_cleanup(void) -{ - if (bt_sock_unregister(BTPROTO_HCI) < 0) - BT_ERR("HCI socket unregistration failed"); - - hci_unregister_notifier(&hci_sock_nblock); - - proto_unregister(&hci_sk_proto); -} diff --git a/net/bluetooth_tizen/hci_sysfs.c b/net/bluetooth_tizen/hci_sysfs.c deleted file mode 100644 index bc15429..0000000 --- a/net/bluetooth_tizen/hci_sysfs.c +++ /dev/null @@ -1,588 +0,0 @@ -/* Bluetooth HCI driver model support. */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/module.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> - -static struct class *bt_class; - -struct dentry *bt_debugfs; -EXPORT_SYMBOL_GPL(bt_debugfs); - -static inline char *link_typetostr(int type) -{ - switch (type) { - case ACL_LINK: - return "ACL"; - case SCO_LINK: - return "SCO"; - case ESCO_LINK: - return "eSCO"; - case LE_LINK: - return "LE"; - default: - return "UNKNOWN"; - } -} - -static ssize_t show_link_type(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_conn *conn = to_hci_conn(dev); - return sprintf(buf, "%s\n", link_typetostr(conn->type)); -} - -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)); -} - -static ssize_t show_link_features(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_conn *conn = to_hci_conn(dev); - - return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", - conn->features[0], conn->features[1], - conn->features[2], conn->features[3], - conn->features[4], conn->features[5], - conn->features[6], conn->features[7]); -} - -#define LINK_ATTR(_name, _mode, _show, _store) \ -struct device_attribute link_attr_##_name = __ATTR(_name, _mode, _show, _store) - -static LINK_ATTR(type, S_IRUGO, show_link_type, NULL); -static LINK_ATTR(address, S_IRUGO, show_link_address, NULL); -static LINK_ATTR(features, S_IRUGO, show_link_features, NULL); - -static struct attribute *bt_link_attrs[] = { - &link_attr_type.attr, - &link_attr_address.attr, - &link_attr_features.attr, - NULL -}; - -static struct attribute_group bt_link_group = { - .attrs = bt_link_attrs, -}; - -static const struct attribute_group *bt_link_groups[] = { - &bt_link_group, - NULL -}; - -static void bt_link_release(struct device *dev) -{ - struct hci_conn *conn = to_hci_conn(dev); - kfree(conn); -} - -static struct device_type bt_link = { - .name = "link", - .groups = bt_link_groups, - .release = bt_link_release, -}; - -/* - * The rfcomm tty device will possibly retain even when conn - * is down, and sysfs doesn't support move zombie device, - * so we should move the device before conn device is destroyed. - */ -static int __match_tty(struct device *dev, void *data) -{ - return !strncmp(dev_name(dev), "rfcomm", 6); -} - -void hci_conn_init_sysfs(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - - BT_DBG("conn %p", conn); - - conn->dev.type = &bt_link; - conn->dev.class = bt_class; - conn->dev.parent = &hdev->dev; - - device_initialize(&conn->dev); -} - -void hci_conn_add_sysfs(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - - BT_DBG("conn %p", conn); - - dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle); - - if (device_add(&conn->dev) < 0) { - BT_ERR("Failed to register connection device"); - return; - } - - hci_dev_hold(hdev); -} - -void hci_conn_del_sysfs(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - - if (!device_is_registered(&conn->dev)) - return; - - while (1) { - struct device *dev; - - dev = device_find_child(&conn->dev, NULL, __match_tty); - if (!dev) - break; - device_move(dev, NULL, DPM_ORDER_DEV_LAST); - put_device(dev); - } - - device_del(&conn->dev); - put_device(&conn->dev); - - hci_dev_put(hdev); -} - -static inline char *host_bustostr(int bus) -{ - switch (bus) { - case HCI_VIRTUAL: - return "VIRTUAL"; - case HCI_USB: - return "USB"; - case HCI_PCCARD: - return "PCCARD"; - case HCI_UART: - return "UART"; - case HCI_RS232: - return "RS232"; - case HCI_PCI: - return "PCI"; - case HCI_SDIO: - return "SDIO"; - default: - return "UNKNOWN"; - } -} - -static inline char *host_typetostr(int type) -{ - switch (type) { - case HCI_BREDR: - return "BR/EDR"; - case HCI_AMP: - return "AMP"; - default: - return "UNKNOWN"; - } -} - -static ssize_t show_bus(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%s\n", host_bustostr(hdev->bus)); -} - -static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%s\n", host_typetostr(hdev->dev_type)); -} - -static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - char name[HCI_MAX_NAME_LENGTH + 1]; - int i; - - for (i = 0; i < HCI_MAX_NAME_LENGTH; i++) - name[i] = hdev->dev_name[i]; - - name[HCI_MAX_NAME_LENGTH] = '\0'; - return sprintf(buf, "%s\n", name); -} - -static ssize_t show_class(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "0x%.2x%.2x%.2x\n", - hdev->dev_class[2], hdev->dev_class[1], hdev->dev_class[0]); -} - -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)); -} - -static ssize_t show_features(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - - return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", - hdev->features[0], hdev->features[1], - hdev->features[2], hdev->features[3], - hdev->features[4], hdev->features[5], - hdev->features[6], hdev->features[7]); -} - -static ssize_t show_manufacturer(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->manufacturer); -} - -static ssize_t show_hci_version(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->hci_ver); -} - -static ssize_t show_hci_revision(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->hci_rev); -} - -static ssize_t show_idle_timeout(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->idle_timeout); -} - -static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct hci_dev *hdev = to_hci_dev(dev); - unsigned int val; - int rv; - - rv = kstrtouint(buf, 0, &val); - if (rv < 0) - return rv; - - if (val != 0 && (val < 500 || val > 3600000)) - return -EINVAL; - - hdev->idle_timeout = val; - - return count; -} - -static ssize_t show_sniff_max_interval(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->sniff_max_interval); -} - -static ssize_t store_sniff_max_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct hci_dev *hdev = to_hci_dev(dev); - u16 val; - int rv; - - rv = kstrtou16(buf, 0, &val); - if (rv < 0) - return rv; - - if (val == 0 || val % 2 || val < hdev->sniff_min_interval) - return -EINVAL; - - hdev->sniff_max_interval = val; - - return count; -} - -static ssize_t show_sniff_min_interval(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->sniff_min_interval); -} - -static ssize_t store_sniff_min_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct hci_dev *hdev = to_hci_dev(dev); - u16 val; - int rv; - - rv = kstrtou16(buf, 0, &val); - if (rv < 0) - return rv; - - if (val == 0 || val % 2 || val > hdev->sniff_max_interval) - return -EINVAL; - - hdev->sniff_min_interval = val; - - return count; -} - -static DEVICE_ATTR(bus, S_IRUGO, show_bus, NULL); -static DEVICE_ATTR(type, S_IRUGO, show_type, NULL); -static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -static DEVICE_ATTR(class, S_IRUGO, show_class, NULL); -static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); -static DEVICE_ATTR(features, S_IRUGO, show_features, NULL); -static DEVICE_ATTR(manufacturer, S_IRUGO, show_manufacturer, NULL); -static DEVICE_ATTR(hci_version, S_IRUGO, show_hci_version, NULL); -static DEVICE_ATTR(hci_revision, S_IRUGO, show_hci_revision, NULL); - -static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR, - show_idle_timeout, store_idle_timeout); -static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR, - show_sniff_max_interval, store_sniff_max_interval); -static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR, - show_sniff_min_interval, store_sniff_min_interval); - -static struct attribute *bt_host_attrs[] = { - &dev_attr_bus.attr, - &dev_attr_type.attr, - &dev_attr_name.attr, - &dev_attr_class.attr, - &dev_attr_address.attr, - &dev_attr_features.attr, - &dev_attr_manufacturer.attr, - &dev_attr_hci_version.attr, - &dev_attr_hci_revision.attr, - &dev_attr_idle_timeout.attr, - &dev_attr_sniff_max_interval.attr, - &dev_attr_sniff_min_interval.attr, - NULL -}; - -static struct attribute_group bt_host_group = { - .attrs = bt_host_attrs, -}; - -static const struct attribute_group *bt_host_groups[] = { - &bt_host_group, - NULL -}; - -static void bt_host_release(struct device *dev) -{ - struct hci_dev *hdev = to_hci_dev(dev); - kfree(hdev); - module_put(THIS_MODULE); -} - -static struct device_type bt_host = { - .name = "host", - .groups = bt_host_groups, - .release = bt_host_release, -}; - -static int inquiry_cache_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *e; - - hci_dev_lock(hdev); - - 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), - data->pscan_rep_mode, data->pscan_period_mode, - data->pscan_mode, data->dev_class[2], - data->dev_class[1], data->dev_class[0], - __le16_to_cpu(data->clock_offset), - data->rssi, data->ssp_mode, e->timestamp); - } - - hci_dev_unlock(hdev); - - return 0; -} - -static int inquiry_cache_open(struct inode *inode, struct file *file) -{ - return single_open(file, inquiry_cache_show, inode->i_private); -} - -static const struct file_operations inquiry_cache_fops = { - .open = inquiry_cache_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int blacklist_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct bdaddr_list *b; - - hci_dev_lock(hdev); - - list_for_each_entry(b, &hdev->blacklist, list) - seq_printf(f, "%s\n", batostr(&b->bdaddr)); - - hci_dev_unlock(hdev); - - return 0; -} - -static int blacklist_open(struct inode *inode, struct file *file) -{ - return single_open(file, blacklist_show, inode->i_private); -} - -static const struct file_operations blacklist_fops = { - .open = blacklist_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void print_bt_uuid(struct seq_file *f, u8 *uuid) -{ - u32 data0, data4; - u16 data1, data2, data3, data5; - - memcpy(&data0, &uuid[0], 4); - memcpy(&data1, &uuid[4], 2); - memcpy(&data2, &uuid[6], 2); - memcpy(&data3, &uuid[8], 2); - memcpy(&data4, &uuid[10], 4); - memcpy(&data5, &uuid[14], 2); - - seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x\n", - ntohl(data0), ntohs(data1), ntohs(data2), - ntohs(data3), ntohl(data4), ntohs(data5)); -} - -static int uuids_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct bt_uuid *uuid; - - hci_dev_lock(hdev); - - list_for_each_entry(uuid, &hdev->uuids, list) - print_bt_uuid(f, uuid->uuid); - - hci_dev_unlock(hdev); - - return 0; -} - -static int uuids_open(struct inode *inode, struct file *file) -{ - return single_open(file, uuids_show, inode->i_private); -} - -static const struct file_operations uuids_fops = { - .open = uuids_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int auto_accept_delay_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - - hdev->auto_accept_delay = val; - - hci_dev_unlock(hdev); - - return 0; -} - -static int auto_accept_delay_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - - *val = hdev->auto_accept_delay; - - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get, - auto_accept_delay_set, "%llu\n"); - -void hci_init_sysfs(struct hci_dev *hdev) -{ - struct device *dev = &hdev->dev; - - dev->type = &bt_host; - dev->class = bt_class; - - __module_get(THIS_MODULE); - device_initialize(dev); -} - -int hci_add_sysfs(struct hci_dev *hdev) -{ - struct device *dev = &hdev->dev; - int err; - - BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - - dev->parent = hdev->parent; - dev_set_name(dev, "%s", hdev->name); - - err = device_add(dev); - if (err < 0) - return err; - - if (!bt_debugfs) - return 0; - - hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs); - if (!hdev->debugfs) - return 0; - - debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, - hdev, &inquiry_cache_fops); - - debugfs_create_file("blacklist", 0444, hdev->debugfs, - hdev, &blacklist_fops); - - debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); - - debugfs_create_file("auto_accept_delay", 0444, hdev->debugfs, hdev, - &auto_accept_delay_fops); - return 0; -} - -void hci_del_sysfs(struct hci_dev *hdev) -{ - BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - - debugfs_remove_recursive(hdev->debugfs); - - device_del(&hdev->dev); -} - -int __init bt_sysfs_init(void) -{ - bt_debugfs = debugfs_create_dir("bluetooth", NULL); - - bt_class = class_create(THIS_MODULE, "bluetooth"); - if (IS_ERR(bt_class)) - return PTR_ERR(bt_class); - - return 0; -} - -void bt_sysfs_cleanup(void) -{ - class_destroy(bt_class); - - debugfs_remove_recursive(bt_debugfs); -} diff --git a/net/bluetooth_tizen/hidp/Kconfig b/net/bluetooth_tizen/hidp/Kconfig deleted file mode 100644 index 4deaca7..0000000 --- a/net/bluetooth_tizen/hidp/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -config BT_HIDP - tristate "HIDP protocol support" - depends on BT && INPUT && HID_SUPPORT - select HID - help - HIDP (Human Interface Device Protocol) is a transport layer - for HID reports. HIDP is required for the Bluetooth Human - Interface Device Profile. - - Say Y here to compile HIDP support into the kernel or say M to - compile it as module (hidp). - diff --git a/net/bluetooth_tizen/hidp/Makefile b/net/bluetooth_tizen/hidp/Makefile deleted file mode 100644 index a9ee115..0000000 --- a/net/bluetooth_tizen/hidp/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the Linux Bluetooth HIDP layer -# - -obj-$(CONFIG_BT_HIDP) += hidp.o - -hidp-objs := core.o sock.o diff --git a/net/bluetooth_tizen/hidp/core.c b/net/bluetooth_tizen/hidp/core.c deleted file mode 100644 index d478be1..0000000 --- a/net/bluetooth_tizen/hidp/core.c +++ /dev/null @@ -1,1242 +0,0 @@ -/* - HIDP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/freezer.h> -#include <linux/fcntl.h> -#include <linux/skbuff.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <linux/file.h> -#include <linux/init.h> -#include <linux/wait.h> -#include <linux/mutex.h> -#include <linux/kthread.h> -#include <net/sock.h> - -#include <linux/input.h> -#include <linux/hid.h> -#include <linux/hidraw.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> - -#include "hidp.h" - -#define VERSION "1.2" - -static DECLARE_RWSEM(hidp_session_sem); -static LIST_HEAD(hidp_session_list); - -static unsigned char hidp_keycode[256] = { - 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, - 37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, - 21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, - 14, 15, 57, 12, 13, 26, 27, 43, 43, 39, 40, 41, 51, 52, - 53, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88, - 99, 70, 119, 110, 102, 104, 111, 107, 109, 106, 105, 108, 103, 69, - 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73, - 82, 83, 86, 127, 116, 117, 183, 184, 185, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 134, 138, 130, 132, 128, 129, 131, 137, 133, 135, - 136, 113, 115, 114, 0, 0, 0, 121, 0, 89, 93, 124, 92, 94, - 95, 0, 0, 0, 122, 123, 90, 91, 85, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 29, 42, 56, 125, 97, 54, 100, 126, 164, 166, 165, 163, 161, 115, - 114, 113, 150, 158, 159, 128, 136, 177, 178, 176, 142, 152, 173, 140 -}; - -static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; - -static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr) -{ - struct hidp_session *session; - - BT_DBG(""); - - list_for_each_entry(session, &hidp_session_list, list) { - if (!bacmp(bdaddr, &session->bdaddr)) - return session; - } - - return NULL; -} - -static void __hidp_link_session(struct hidp_session *session) -{ - list_add(&session->list, &hidp_session_list); -} - -static void __hidp_unlink_session(struct hidp_session *session) -{ - hci_conn_put_device(session->conn); - - list_del(&session->list); -} - -static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci) -{ - memset(ci, 0, sizeof(*ci)); - bacpy(&ci->bdaddr, &session->bdaddr); - - ci->flags = session->flags; - ci->state = session->state; - - ci->vendor = 0x0000; - ci->product = 0x0000; - ci->version = 0x0000; - - if (session->input) { - ci->vendor = session->input->id.vendor; - ci->product = session->input->id.product; - ci->version = session->input->id.version; - if (session->input->name) - strncpy(ci->name, session->input->name, 128); - else - strncpy(ci->name, "HID Boot Device", 128); - } - - if (session->hid) { - ci->vendor = session->hid->vendor; - ci->product = session->hid->product; - ci->version = session->hid->version; - strncpy(ci->name, session->hid->name, 128); - } -} - -static int hidp_queue_event(struct hidp_session *session, struct input_dev *dev, - unsigned int type, unsigned int code, int value) -{ - unsigned char newleds; - struct sk_buff *skb; - - BT_DBG("session %p type %d code %d value %d", session, type, code, value); - - if (type != EV_LED) - return -1; - - newleds = (!!test_bit(LED_KANA, dev->led) << 3) | - (!!test_bit(LED_COMPOSE, dev->led) << 3) | - (!!test_bit(LED_SCROLLL, dev->led) << 2) | - (!!test_bit(LED_CAPSL, dev->led) << 1) | - (!!test_bit(LED_NUML, dev->led)); - - if (session->leds == newleds) - return 0; - - session->leds = newleds; - - skb = alloc_skb(3, GFP_ATOMIC); - if (!skb) { - BT_ERR("Can't allocate memory for new frame"); - return -ENOMEM; - } - - *skb_put(skb, 1) = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; - *skb_put(skb, 1) = 0x01; - *skb_put(skb, 1) = newleds; - - skb_queue_tail(&session->intr_transmit, skb); - - hidp_schedule(session); - - return 0; -} - -static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct hidp_session *session = hid->driver_data; - - return hidp_queue_event(session, dev, type, code, value); -} - -static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) -{ - struct hidp_session *session = input_get_drvdata(dev); - - return hidp_queue_event(session, dev, type, code, value); -} - -static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) -{ - struct input_dev *dev = session->input; - unsigned char *keys = session->keys; - unsigned char *udata = skb->data + 1; - signed char *sdata = skb->data + 1; - int i, size = skb->len - 1; - - switch (skb->data[0]) { - case 0x01: /* Keyboard report */ - for (i = 0; i < 8; i++) - input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1); - - /* If all the key codes have been set to 0x01, it means - * too many keys were pressed at the same time. */ - if (!memcmp(udata + 2, hidp_mkeyspat, 6)) - break; - - for (i = 2; i < 8; i++) { - if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) { - if (hidp_keycode[keys[i]]) - input_report_key(dev, hidp_keycode[keys[i]], 0); - else - BT_ERR("Unknown key (scancode %#x) released.", keys[i]); - } - - if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) { - if (hidp_keycode[udata[i]]) - input_report_key(dev, hidp_keycode[udata[i]], 1); - else - BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]); - } - } - - memcpy(keys, udata, 8); - break; - - case 0x02: /* Mouse report */ - input_report_key(dev, BTN_LEFT, sdata[0] & 0x01); - input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02); - input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04); - input_report_key(dev, BTN_SIDE, sdata[0] & 0x08); - input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10); - - input_report_rel(dev, REL_X, sdata[1]); - input_report_rel(dev, REL_Y, sdata[2]); - - if (size > 3) - input_report_rel(dev, REL_WHEEL, sdata[3]); - break; - } - - input_sync(dev); -} - -static int __hidp_send_ctrl_message(struct hidp_session *session, - unsigned char hdr, unsigned char *data, int size) -{ - struct sk_buff *skb; - - BT_DBG("session %p data %p size %d", session, data, size); - - if (atomic_read(&session->terminate)) - return -EIO; - - skb = alloc_skb(size + 1, GFP_ATOMIC); - if (!skb) { - BT_ERR("Can't allocate memory for new frame"); - return -ENOMEM; - } - - *skb_put(skb, 1) = hdr; - if (data && size > 0) - memcpy(skb_put(skb, size), data, size); - - skb_queue_tail(&session->ctrl_transmit, skb); - - return 0; -} - -static inline int hidp_send_ctrl_message(struct hidp_session *session, - unsigned char hdr, unsigned char *data, int size) -{ - int err; - - err = __hidp_send_ctrl_message(session, hdr, data, size); - - hidp_schedule(session); - - return err; -} - -static int hidp_queue_report(struct hidp_session *session, - unsigned char *data, int size) -{ - struct sk_buff *skb; - - BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size); - - skb = alloc_skb(size + 1, GFP_ATOMIC); - if (!skb) { - BT_ERR("Can't allocate memory for new frame"); - return -ENOMEM; - } - - *skb_put(skb, 1) = 0xa2; - if (size > 0) - memcpy(skb_put(skb, size), data, size); - - skb_queue_tail(&session->intr_transmit, skb); - - hidp_schedule(session); - - return 0; -} - -static int hidp_send_report(struct hidp_session *session, struct hid_report *report) -{ - unsigned char buf[32]; - int rsize; - - rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0); - if (rsize > sizeof(buf)) - return -EIO; - - hid_output_report(report, buf); - - return hidp_queue_report(session, buf, rsize); -} - -static int hidp_get_raw_report(struct hid_device *hid, - unsigned char report_number, - unsigned char *data, size_t count, - unsigned char report_type) -{ - struct hidp_session *session = hid->driver_data; - struct sk_buff *skb; - size_t len; - int numbered_reports = hid->report_enum[report_type].numbered; - int ret; - - switch (report_type) { - case HID_FEATURE_REPORT: - report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE; - break; - case HID_INPUT_REPORT: - report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT; - break; - case HID_OUTPUT_REPORT: - report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT; - break; - default: - return -EINVAL; - } - - if (mutex_lock_interruptible(&session->report_mutex)) - return -ERESTARTSYS; - - /* Set up our wait, and send the report request to the device. */ - session->waiting_report_type = report_type & HIDP_DATA_RTYPE_MASK; - session->waiting_report_number = numbered_reports ? report_number : -1; - set_bit(HIDP_WAITING_FOR_RETURN, &session->flags); - data[0] = report_number; - ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, 1); - if (ret) - goto err; - - /* Wait for the return of the report. The returned report - gets put in session->report_return. */ - while (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) { - int res; - - res = wait_event_interruptible_timeout(session->report_queue, - !test_bit(HIDP_WAITING_FOR_RETURN, &session->flags), - 5*HZ); - if (res == 0) { - /* timeout */ - ret = -EIO; - goto err; - } - if (res < 0) { - /* signal */ - ret = -ERESTARTSYS; - goto err; - } - } - - skb = session->report_return; - if (skb) { - len = skb->len < count ? skb->len : count; - memcpy(data, skb->data, len); - - kfree_skb(skb); - session->report_return = NULL; - } else { - /* Device returned a HANDSHAKE, indicating protocol error. */ - len = -EIO; - } - - clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); - mutex_unlock(&session->report_mutex); - - return len; - -err: - clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); - mutex_unlock(&session->report_mutex); - return ret; -} - -static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, - unsigned char report_type) -{ - struct hidp_session *session = hid->driver_data; - int ret; - - switch (report_type) { - case HID_FEATURE_REPORT: - report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; - break; - case HID_OUTPUT_REPORT: - report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT; - break; - default: - return -EINVAL; - } - - if (mutex_lock_interruptible(&session->report_mutex)) - return -ERESTARTSYS; - - /* Set up our wait, and send the report request to the device. */ - set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); - ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, - count); - if (ret) - goto err; - - /* Wait for the ACK from the device. */ - while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { - int res; - - res = wait_event_interruptible_timeout(session->report_queue, - !test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags), - 10*HZ); - if (res == 0) { - /* timeout */ - ret = -EIO; - goto err; - } - if (res < 0) { - /* signal */ - ret = -ERESTARTSYS; - goto err; - } - } - - if (!session->output_report_success) { - ret = -EIO; - goto err; - } - - ret = count; - -err: - clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); - mutex_unlock(&session->report_mutex); - return ret; -} - -static void hidp_idle_timeout(unsigned long arg) -{ - struct hidp_session *session = (struct hidp_session *) arg; - - atomic_inc(&session->terminate); - wake_up_process(session->task); -} - -static void hidp_set_timer(struct hidp_session *session) -{ - if (session->idle_to > 0) - mod_timer(&session->timer, jiffies + HZ * session->idle_to); -} - -static inline void hidp_del_timer(struct hidp_session *session) -{ - if (session->idle_to > 0) - del_timer(&session->timer); -} - -static void hidp_process_handshake(struct hidp_session *session, - unsigned char param) -{ - BT_DBG("session %p param 0x%02x", session, param); - session->output_report_success = 0; /* default condition */ - - switch (param) { - case HIDP_HSHK_SUCCESSFUL: - /* FIXME: Call into SET_ GET_ handlers here */ - session->output_report_success = 1; - break; - - case HIDP_HSHK_NOT_READY: - case HIDP_HSHK_ERR_INVALID_REPORT_ID: - case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: - case HIDP_HSHK_ERR_INVALID_PARAMETER: - if (test_and_clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) - wake_up_interruptible(&session->report_queue); - - /* FIXME: Call into SET_ GET_ handlers here */ - break; - - case HIDP_HSHK_ERR_UNKNOWN: - break; - - case HIDP_HSHK_ERR_FATAL: - /* Device requests a reboot, as this is the only way this error - * can be recovered. */ - __hidp_send_ctrl_message(session, - HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET, NULL, 0); - break; - - default: - __hidp_send_ctrl_message(session, - HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); - break; - } - - /* Wake up the waiting thread. */ - if (test_and_clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) - wake_up_interruptible(&session->report_queue); -} - -static void hidp_process_hid_control(struct hidp_session *session, - unsigned char param) -{ - BT_DBG("session %p param 0x%02x", session, param); - - if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG) { - /* Flush the transmit queues */ - skb_queue_purge(&session->ctrl_transmit); - skb_queue_purge(&session->intr_transmit); - - atomic_inc(&session->terminate); - wake_up_process(current); - } -} - -/* Returns true if the passed-in skb should be freed by the caller. */ -static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb, - unsigned char param) -{ - int done_with_skb = 1; - BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param); - - switch (param) { - case HIDP_DATA_RTYPE_INPUT: - hidp_set_timer(session); - - if (session->input) - hidp_input_report(session, skb); - - if (session->hid) - hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0); - break; - - case HIDP_DATA_RTYPE_OTHER: - case HIDP_DATA_RTYPE_OUPUT: - case HIDP_DATA_RTYPE_FEATURE: - break; - - default: - __hidp_send_ctrl_message(session, - HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); - } - - if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) && - param == session->waiting_report_type) { - if (session->waiting_report_number < 0 || - session->waiting_report_number == skb->data[0]) { - /* hidp_get_raw_report() is waiting on this report. */ - session->report_return = skb; - done_with_skb = 0; - clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); - wake_up_interruptible(&session->report_queue); - } - } - - return done_with_skb; -} - -static void hidp_recv_ctrl_frame(struct hidp_session *session, - struct sk_buff *skb) -{ - unsigned char hdr, type, param; - int free_skb = 1; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - hdr = skb->data[0]; - skb_pull(skb, 1); - - type = hdr & HIDP_HEADER_TRANS_MASK; - param = hdr & HIDP_HEADER_PARAM_MASK; - - switch (type) { - case HIDP_TRANS_HANDSHAKE: - hidp_process_handshake(session, param); - break; - - case HIDP_TRANS_HID_CONTROL: - hidp_process_hid_control(session, param); - break; - - case HIDP_TRANS_DATA: - free_skb = hidp_process_data(session, skb, param); - break; - - default: - __hidp_send_ctrl_message(session, - HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0); - break; - } - - if (free_skb) - kfree_skb(skb); -} - -static void hidp_recv_intr_frame(struct hidp_session *session, - struct sk_buff *skb) -{ - unsigned char hdr; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - hdr = skb->data[0]; - skb_pull(skb, 1); - - if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { - hidp_set_timer(session); - - if (session->input) - hidp_input_report(session, skb); - - if (session->hid) { - hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1); - BT_DBG("report len %d", skb->len); - } - } else { - BT_DBG("Unsupported protocol header 0x%02x", hdr); - } - - kfree_skb(skb); -} - -static int hidp_send_frame(struct socket *sock, unsigned char *data, int len) -{ - struct kvec iv = { data, len }; - struct msghdr msg; - - BT_DBG("sock %p data %p len %d", sock, data, len); - - if (!len) - return 0; - - memset(&msg, 0, sizeof(msg)); - - return kernel_sendmsg(sock, &msg, &iv, 1, len); -} - -static void hidp_process_intr_transmit(struct hidp_session *session) -{ - struct sk_buff *skb; - - BT_DBG("session %p", session); - - while ((skb = skb_dequeue(&session->intr_transmit))) { - if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) { - skb_queue_head(&session->intr_transmit, skb); - break; - } - - hidp_set_timer(session); - kfree_skb(skb); - } -} - -static void hidp_process_ctrl_transmit(struct hidp_session *session) -{ - struct sk_buff *skb; - - BT_DBG("session %p", session); - - while ((skb = skb_dequeue(&session->ctrl_transmit))) { - if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) { - skb_queue_head(&session->ctrl_transmit, skb); - break; - } - - hidp_set_timer(session); - kfree_skb(skb); - } -} - -static int hidp_session(void *arg) -{ - struct hidp_session *session = arg; - struct sock *ctrl_sk = session->ctrl_sock->sk; - struct sock *intr_sk = session->intr_sock->sk; - struct sk_buff *skb; - wait_queue_t ctrl_wait, intr_wait; - - BT_DBG("session %p", session); - - __module_get(THIS_MODULE); - set_user_nice(current, -15); - - init_waitqueue_entry(&ctrl_wait, current); - init_waitqueue_entry(&intr_wait, current); - add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); - add_wait_queue(sk_sleep(intr_sk), &intr_wait); - session->waiting_for_startup = 0; - wake_up_interruptible(&session->startup_queue); - set_current_state(TASK_INTERRUPTIBLE); - while (!atomic_read(&session->terminate)) { - if (ctrl_sk->sk_state != BT_CONNECTED || - intr_sk->sk_state != BT_CONNECTED) - break; - - while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) { - skb_orphan(skb); - if (!skb_linearize(skb)) - hidp_recv_intr_frame(session, skb); - else - kfree_skb(skb); - } - - hidp_process_intr_transmit(session); - - while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) { - skb_orphan(skb); - if (!skb_linearize(skb)) - hidp_recv_ctrl_frame(session, skb); - else - kfree_skb(skb); - } - - hidp_process_ctrl_transmit(session); - - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(intr_sk), &intr_wait); - remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); - - clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); - clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); - wake_up_interruptible(&session->report_queue); - - down_write(&hidp_session_sem); - - hidp_del_timer(session); - - if (session->input) { - input_unregister_device(session->input); - session->input = NULL; - } - - if (session->hid) { - hid_destroy_device(session->hid); - session->hid = NULL; - } - - /* Wakeup user-space polling for socket errors */ - session->intr_sock->sk->sk_err = EUNATCH; - session->ctrl_sock->sk->sk_err = EUNATCH; - - hidp_schedule(session); - - fput(session->intr_sock->file); - - wait_event_timeout(*(sk_sleep(ctrl_sk)), - (ctrl_sk->sk_state == BT_CLOSED), msecs_to_jiffies(500)); - - fput(session->ctrl_sock->file); - - __hidp_unlink_session(session); - - up_write(&hidp_session_sem); - - kfree(session->rd_data); - kfree(session); - module_put_and_exit(0); - return 0; -} - -static struct hci_conn *hidp_get_connection(struct hidp_session *session) -{ - bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src; - bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst; - struct hci_conn *conn; - struct hci_dev *hdev; - - hdev = hci_get_route(dst, src); - if (!hdev) - return NULL; - - hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); - if (conn) - hci_conn_hold_device(conn); - hci_dev_unlock(hdev); - - hci_dev_put(hdev); - - return conn; -} - -static int hidp_setup_input(struct hidp_session *session, - struct hidp_connadd_req *req) -{ - struct input_dev *input; - int i; - - input = input_allocate_device(); - if (!input) - return -ENOMEM; - - session->input = input; - - input_set_drvdata(input, session); - - input->name = "Bluetooth HID Boot Protocol Device"; - - input->id.bustype = BUS_BLUETOOTH; - input->id.vendor = req->vendor; - input->id.product = req->product; - input->id.version = req->version; - - if (req->subclass & 0x40) { - set_bit(EV_KEY, input->evbit); - set_bit(EV_LED, input->evbit); - set_bit(EV_REP, input->evbit); - - set_bit(LED_NUML, input->ledbit); - set_bit(LED_CAPSL, input->ledbit); - set_bit(LED_SCROLLL, input->ledbit); - set_bit(LED_COMPOSE, input->ledbit); - set_bit(LED_KANA, input->ledbit); - - for (i = 0; i < sizeof(hidp_keycode); i++) - set_bit(hidp_keycode[i], input->keybit); - clear_bit(0, input->keybit); - } - - if (req->subclass & 0x80) { - input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); - input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); - input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | - BIT_MASK(BTN_EXTRA); - input->relbit[0] |= BIT_MASK(REL_WHEEL); - } - - input->dev.parent = &session->conn->dev; - - input->event = hidp_input_event; - - return 0; -} - -static int hidp_open(struct hid_device *hid) -{ - return 0; -} - -static void hidp_close(struct hid_device *hid) -{ -} - -static int hidp_parse(struct hid_device *hid) -{ - struct hidp_session *session = hid->driver_data; - - return hid_parse_report(session->hid, session->rd_data, - session->rd_size); -} - -static int hidp_start(struct hid_device *hid) -{ - struct hidp_session *session = hid->driver_data; - struct hid_report *report; - - if (hid->quirks & HID_QUIRK_NO_INIT_REPORTS) - return 0; - - list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT]. - report_list, list) - hidp_send_report(session, report); - - list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT]. - report_list, list) - hidp_send_report(session, report); - - return 0; -} - -static void hidp_stop(struct hid_device *hid) -{ - struct hidp_session *session = hid->driver_data; - - skb_queue_purge(&session->ctrl_transmit); - skb_queue_purge(&session->intr_transmit); - - hid->claimed = 0; -} - -static struct hid_ll_driver hidp_hid_driver = { - .parse = hidp_parse, - .start = hidp_start, - .stop = hidp_stop, - .open = hidp_open, - .close = hidp_close, - .hidinput_input_event = hidp_hidinput_event, -}; - -/* This function sets up the hid device. It does not add it - to the HID system. That is done in hidp_add_connection(). */ -static int hidp_setup_hid(struct hidp_session *session, - struct hidp_connadd_req *req) -{ - struct hid_device *hid; - int err; - - session->rd_data = kzalloc(req->rd_size, GFP_KERNEL); - if (!session->rd_data) - return -ENOMEM; - - if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) { - err = -EFAULT; - goto fault; - } - session->rd_size = req->rd_size; - - hid = hid_allocate_device(); - if (IS_ERR(hid)) { - err = PTR_ERR(hid); - goto fault; - } - - session->hid = hid; - - hid->driver_data = session; - - hid->bus = BUS_BLUETOOTH; - hid->vendor = req->vendor; - hid->product = req->product; - hid->version = req->version; - 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); - - hid->dev.parent = &session->conn->dev; - hid->ll_driver = &hidp_hid_driver; - - hid->hid_get_raw_report = hidp_get_raw_report; - hid->hid_output_raw_report = hidp_output_raw_report; - - return 0; - -fault: - kfree(session->rd_data); - session->rd_data = NULL; - - return err; -} - -int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock) -{ - struct hidp_session *session, *s; - int vendor, product; - int err; - - BT_DBG(""); - - if (bacmp(&bt_sk(ctrl_sock->sk)->src, &bt_sk(intr_sock->sk)->src) || - bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst)) - return -ENOTUNIQ; - - BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); - - down_write(&hidp_session_sem); - - s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); - if (s && s->state == BT_CONNECTED) { - up_write(&hidp_session_sem); - return -EEXIST; - } - - session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL); - if (!session) { - up_write(&hidp_session_sem); - return -ENOMEM; - } - - bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst); - - session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->chan->omtu, - l2cap_pi(ctrl_sock->sk)->chan->imtu); - session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->chan->omtu, - l2cap_pi(intr_sock->sk)->chan->imtu); - - BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu); - - session->ctrl_sock = ctrl_sock; - session->intr_sock = intr_sock; - session->state = BT_CONNECTED; - - session->conn = hidp_get_connection(session); - if (!session->conn) { - err = -ENOTCONN; - goto failed; - } - - setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session); - - skb_queue_head_init(&session->ctrl_transmit); - skb_queue_head_init(&session->intr_transmit); - - mutex_init(&session->report_mutex); - init_waitqueue_head(&session->report_queue); - init_waitqueue_head(&session->startup_queue); - session->waiting_for_startup = 1; - session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); - session->idle_to = req->idle_to; - - __hidp_link_session(session); - - if (req->rd_size > 0) { - err = hidp_setup_hid(session, req); - if (err) - goto purge; - } - - if (!session->hid) { - err = hidp_setup_input(session, req); - if (err < 0) - goto purge; - } - - hidp_set_timer(session); - - if (session->hid) { - vendor = session->hid->vendor; - product = session->hid->product; - } else if (session->input) { - vendor = session->input->id.vendor; - product = session->input->id.product; - } else { - vendor = 0x0000; - product = 0x0000; - } - - session->task = kthread_run(hidp_session, session, "khidpd_%04x%04x", - vendor, product); - if (IS_ERR(session->task)) { - err = PTR_ERR(session->task); - goto unlink; - } - - while (session->waiting_for_startup) { - wait_event_interruptible(session->startup_queue, - !session->waiting_for_startup); - } - - if (session->hid) - err = hid_add_device(session->hid); - else - err = input_register_device(session->input); - - if (err < 0) { - atomic_inc(&session->terminate); - wake_up_process(session->task); - up_write(&hidp_session_sem); - return err; - } - - if (session->input) { - hidp_send_ctrl_message(session, - HIDP_TRANS_SET_PROTOCOL | HIDP_PROTO_BOOT, NULL, 0); - session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE); - - session->leds = 0xff; - hidp_input_event(session->input, EV_LED, 0, 0); - } - - up_write(&hidp_session_sem); - return 0; - -unlink: - hidp_del_timer(session); - - if (session->input) { - input_unregister_device(session->input); - session->input = NULL; - } - - if (session->hid) { - hid_destroy_device(session->hid); - session->hid = NULL; - } - - kfree(session->rd_data); - session->rd_data = NULL; - -purge: - __hidp_unlink_session(session); - - skb_queue_purge(&session->ctrl_transmit); - skb_queue_purge(&session->intr_transmit); - -failed: - up_write(&hidp_session_sem); - - kfree(session); - return err; -} - -int hidp_del_connection(struct hidp_conndel_req *req) -{ - struct hidp_session *session; - int err = 0; - - BT_DBG(""); - - down_read(&hidp_session_sem); - - session = __hidp_get_session(&req->bdaddr); - if (session) { - if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) { - hidp_send_ctrl_message(session, - HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG, NULL, 0); - } else { - /* Flush the transmit queues */ - skb_queue_purge(&session->ctrl_transmit); - skb_queue_purge(&session->intr_transmit); - - atomic_inc(&session->terminate); - wake_up_process(session->task); - } - } else - err = -ENOENT; - - up_read(&hidp_session_sem); - return err; -} - -int hidp_get_connlist(struct hidp_connlist_req *req) -{ - struct hidp_session *session; - int err = 0, n = 0; - - BT_DBG(""); - - down_read(&hidp_session_sem); - - list_for_each_entry(session, &hidp_session_list, list) { - struct hidp_conninfo ci; - - __hidp_copy_session(session, &ci); - - if (copy_to_user(req->ci, &ci, sizeof(ci))) { - err = -EFAULT; - break; - } - - if (++n >= req->cnum) - break; - - req->ci++; - } - req->cnum = n; - - up_read(&hidp_session_sem); - return err; -} - -int hidp_get_conninfo(struct hidp_conninfo *ci) -{ - struct hidp_session *session; - int err = 0; - - down_read(&hidp_session_sem); - - session = __hidp_get_session(&ci->bdaddr); - if (session) - __hidp_copy_session(session, ci); - else - err = -ENOENT; - - up_read(&hidp_session_sem); - return err; -} - -static const struct hid_device_id hidp_table[] = { - { HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) }, - { } -}; - -static struct hid_driver hidp_driver = { - .name = "generic-bluetooth", - .id_table = hidp_table, -}; - -static int __init hidp_init(void) -{ - int ret; - - BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION); - - ret = hid_register_driver(&hidp_driver); - if (ret) - goto err; - - ret = hidp_init_sockets(); - if (ret) - goto err_drv; - - return 0; -err_drv: - hid_unregister_driver(&hidp_driver); -err: - return ret; -} - -static void __exit hidp_exit(void) -{ - hidp_cleanup_sockets(); - hid_unregister_driver(&hidp_driver); -} - -module_init(hidp_init); -module_exit(hidp_exit); - -MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("bt-proto-6"); diff --git a/net/bluetooth_tizen/hidp/hidp.h b/net/bluetooth_tizen/hidp/hidp.h deleted file mode 100644 index af1bcc8..0000000 --- a/net/bluetooth_tizen/hidp/hidp.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - HIDP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#ifndef __HIDP_H -#define __HIDP_H - -#include <linux/types.h> -#include <net/bluetooth/bluetooth.h> - -/* HIDP header masks */ -#define HIDP_HEADER_TRANS_MASK 0xf0 -#define HIDP_HEADER_PARAM_MASK 0x0f - -/* HIDP transaction types */ -#define HIDP_TRANS_HANDSHAKE 0x00 -#define HIDP_TRANS_HID_CONTROL 0x10 -#define HIDP_TRANS_GET_REPORT 0x40 -#define HIDP_TRANS_SET_REPORT 0x50 -#define HIDP_TRANS_GET_PROTOCOL 0x60 -#define HIDP_TRANS_SET_PROTOCOL 0x70 -#define HIDP_TRANS_GET_IDLE 0x80 -#define HIDP_TRANS_SET_IDLE 0x90 -#define HIDP_TRANS_DATA 0xa0 -#define HIDP_TRANS_DATC 0xb0 - -/* HIDP handshake results */ -#define HIDP_HSHK_SUCCESSFUL 0x00 -#define HIDP_HSHK_NOT_READY 0x01 -#define HIDP_HSHK_ERR_INVALID_REPORT_ID 0x02 -#define HIDP_HSHK_ERR_UNSUPPORTED_REQUEST 0x03 -#define HIDP_HSHK_ERR_INVALID_PARAMETER 0x04 -#define HIDP_HSHK_ERR_UNKNOWN 0x0e -#define HIDP_HSHK_ERR_FATAL 0x0f - -/* HIDP control operation parameters */ -#define HIDP_CTRL_NOP 0x00 -#define HIDP_CTRL_HARD_RESET 0x01 -#define HIDP_CTRL_SOFT_RESET 0x02 -#define HIDP_CTRL_SUSPEND 0x03 -#define HIDP_CTRL_EXIT_SUSPEND 0x04 -#define HIDP_CTRL_VIRTUAL_CABLE_UNPLUG 0x05 - -/* HIDP data transaction headers */ -#define HIDP_DATA_RTYPE_MASK 0x03 -#define HIDP_DATA_RSRVD_MASK 0x0c -#define HIDP_DATA_RTYPE_OTHER 0x00 -#define HIDP_DATA_RTYPE_INPUT 0x01 -#define HIDP_DATA_RTYPE_OUPUT 0x02 -#define HIDP_DATA_RTYPE_FEATURE 0x03 - -/* HIDP protocol header parameters */ -#define HIDP_PROTO_BOOT 0x00 -#define HIDP_PROTO_REPORT 0x01 - -/* HIDP ioctl defines */ -#define HIDPCONNADD _IOW('H', 200, int) -#define HIDPCONNDEL _IOW('H', 201, int) -#define HIDPGETCONNLIST _IOR('H', 210, int) -#define HIDPGETCONNINFO _IOR('H', 211, int) - -#define HIDP_VIRTUAL_CABLE_UNPLUG 0 -#define HIDP_BOOT_PROTOCOL_MODE 1 -#define HIDP_BLUETOOTH_VENDOR_ID 9 -#define HIDP_WAITING_FOR_RETURN 10 -#define HIDP_WAITING_FOR_SEND_ACK 11 - -struct hidp_connadd_req { - int ctrl_sock; /* Connected control socket */ - int intr_sock; /* Connected interrupt socket */ - __u16 parser; - __u16 rd_size; - __u8 __user *rd_data; - __u8 country; - __u8 subclass; - __u16 vendor; - __u16 product; - __u16 version; - __u32 flags; - __u32 idle_to; - char name[128]; -}; - -struct hidp_conndel_req { - bdaddr_t bdaddr; - __u32 flags; -}; - -struct hidp_conninfo { - bdaddr_t bdaddr; - __u32 flags; - __u16 state; - __u16 vendor; - __u16 product; - __u16 version; - char name[128]; -}; - -struct hidp_connlist_req { - __u32 cnum; - struct hidp_conninfo __user *ci; -}; - -int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock); -int hidp_del_connection(struct hidp_conndel_req *req); -int hidp_get_connlist(struct hidp_connlist_req *req); -int hidp_get_conninfo(struct hidp_conninfo *ci); - -/* HIDP session defines */ -struct hidp_session { - struct list_head list; - - struct hci_conn *conn; - - struct socket *ctrl_sock; - struct socket *intr_sock; - - bdaddr_t bdaddr; - - unsigned long state; - unsigned long flags; - unsigned long idle_to; - - uint ctrl_mtu; - uint intr_mtu; - - atomic_t terminate; - struct task_struct *task; - - unsigned char keys[8]; - unsigned char leds; - - struct input_dev *input; - - struct hid_device *hid; - - struct timer_list timer; - - struct sk_buff_head ctrl_transmit; - struct sk_buff_head intr_transmit; - - /* Used in hidp_get_raw_report() */ - int waiting_report_type; /* HIDP_DATA_RTYPE_* */ - int waiting_report_number; /* -1 for not numbered */ - struct mutex report_mutex; - struct sk_buff *report_return; - wait_queue_head_t report_queue; - - /* Used in hidp_output_raw_report() */ - int output_report_success; /* boolean */ - - /* Report descriptor */ - __u8 *rd_data; - uint rd_size; - - wait_queue_head_t startup_queue; - int waiting_for_startup; -}; - -static inline void hidp_schedule(struct hidp_session *session) -{ - struct sock *ctrl_sk = session->ctrl_sock->sk; - struct sock *intr_sk = session->intr_sock->sk; - - wake_up_interruptible(sk_sleep(ctrl_sk)); - wake_up_interruptible(sk_sleep(intr_sk)); -} - -/* HIDP init defines */ -extern int __init hidp_init_sockets(void); -extern void __exit hidp_cleanup_sockets(void); - -#endif /* __HIDP_H */ diff --git a/net/bluetooth_tizen/hidp/sock.c b/net/bluetooth_tizen/hidp/sock.c deleted file mode 100644 index 73a32d7..0000000 --- a/net/bluetooth_tizen/hidp/sock.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - HIDP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/capability.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/skbuff.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <linux/file.h> -#include <linux/init.h> -#include <linux/compat.h> -#include <linux/gfp.h> -#include <net/sock.h> - -#include "hidp.h" - -static int hidp_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!sk) - return 0; - - sock_orphan(sk); - sock_put(sk); - - return 0; -} - -static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *) arg; - struct hidp_connadd_req ca; - struct hidp_conndel_req cd; - struct hidp_connlist_req cl; - struct hidp_conninfo ci; - struct socket *csock; - struct socket *isock; - int err; - - BT_DBG("cmd %x arg %lx", cmd, arg); - - switch (cmd) { - case HIDPCONNADD: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (copy_from_user(&ca, argp, sizeof(ca))) - return -EFAULT; - - csock = sockfd_lookup(ca.ctrl_sock, &err); - if (!csock) - return err; - - isock = sockfd_lookup(ca.intr_sock, &err); - if (!isock) { - sockfd_put(csock); - return err; - } - - if (csock->sk->sk_state != BT_CONNECTED || - isock->sk->sk_state != BT_CONNECTED) { - sockfd_put(csock); - sockfd_put(isock); - return -EBADFD; - } - - err = hidp_add_connection(&ca, csock, isock); - if (!err) { - if (copy_to_user(argp, &ca, sizeof(ca))) - err = -EFAULT; - } else { - sockfd_put(csock); - sockfd_put(isock); - } - - return err; - - case HIDPCONNDEL: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (copy_from_user(&cd, argp, sizeof(cd))) - return -EFAULT; - - return hidp_del_connection(&cd); - - case HIDPGETCONNLIST: - if (copy_from_user(&cl, argp, sizeof(cl))) - return -EFAULT; - - if (cl.cnum <= 0) - return -EINVAL; - - err = hidp_get_connlist(&cl); - if (!err && copy_to_user(argp, &cl, sizeof(cl))) - return -EFAULT; - - return err; - - case HIDPGETCONNINFO: - if (copy_from_user(&ci, argp, sizeof(ci))) - return -EFAULT; - - err = hidp_get_conninfo(&ci); - if (!err && copy_to_user(argp, &ci, sizeof(ci))) - return -EFAULT; - - return err; - } - - return -EINVAL; -} - -#ifdef CONFIG_COMPAT -struct compat_hidp_connadd_req { - int ctrl_sock; /* Connected control socket */ - int intr_sock; /* Connected interrupt socket */ - __u16 parser; - __u16 rd_size; - compat_uptr_t rd_data; - __u8 country; - __u8 subclass; - __u16 vendor; - __u16 product; - __u16 version; - __u32 flags; - __u32 idle_to; - char name[128]; -}; - -static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - if (cmd == HIDPGETCONNLIST) { - struct hidp_connlist_req cl; - u32 uci; - int err; - - if (get_user(cl.cnum, (u32 __user *) arg) || - get_user(uci, (u32 __user *) (arg + 4))) - return -EFAULT; - - cl.ci = compat_ptr(uci); - - if (cl.cnum <= 0) - return -EINVAL; - - err = hidp_get_connlist(&cl); - - if (!err && put_user(cl.cnum, (u32 __user *) arg)) - err = -EFAULT; - - return err; - } else if (cmd == HIDPCONNADD) { - struct compat_hidp_connadd_req ca; - struct hidp_connadd_req __user *uca; - - uca = compat_alloc_user_space(sizeof(*uca)); - - if (copy_from_user(&ca, (void __user *) arg, sizeof(ca))) - return -EFAULT; - - if (put_user(ca.ctrl_sock, &uca->ctrl_sock) || - put_user(ca.intr_sock, &uca->intr_sock) || - put_user(ca.parser, &uca->parser) || - put_user(ca.rd_size, &uca->rd_size) || - put_user(compat_ptr(ca.rd_data), &uca->rd_data) || - put_user(ca.country, &uca->country) || - put_user(ca.subclass, &uca->subclass) || - put_user(ca.vendor, &uca->vendor) || - put_user(ca.product, &uca->product) || - put_user(ca.version, &uca->version) || - put_user(ca.flags, &uca->flags) || - put_user(ca.idle_to, &uca->idle_to) || - copy_to_user(&uca->name[0], &ca.name[0], 128)) - return -EFAULT; - - arg = (unsigned long) uca; - - /* Fall through. We don't actually write back any _changes_ - to the structure anyway, so there's no need to copy back - into the original compat version */ - } - - return hidp_sock_ioctl(sock, cmd, arg); -} -#endif - -static const struct proto_ops hidp_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = hidp_sock_release, - .ioctl = hidp_sock_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = hidp_sock_compat_ioctl, -#endif - .bind = sock_no_bind, - .getname = sock_no_getname, - .sendmsg = sock_no_sendmsg, - .recvmsg = sock_no_recvmsg, - .poll = sock_no_poll, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = sock_no_setsockopt, - .getsockopt = sock_no_getsockopt, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - -static struct proto hidp_proto = { - .name = "HIDP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct bt_sock) -}; - -static int hidp_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - if (sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hidp_proto); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - - sock->ops = &hidp_sock_ops; - - sock->state = SS_UNCONNECTED; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = protocol; - sk->sk_state = BT_OPEN; - - return 0; -} - -static const struct net_proto_family hidp_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = hidp_sock_create -}; - -int __init hidp_init_sockets(void) -{ - int err; - - err = proto_register(&hidp_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops); - if (err < 0) - goto error; - - return 0; - -error: - BT_ERR("Can't register HIDP socket"); - proto_unregister(&hidp_proto); - return err; -} - -void __exit hidp_cleanup_sockets(void) -{ - if (bt_sock_unregister(BTPROTO_HIDP) < 0) - BT_ERR("Can't unregister HIDP socket"); - - proto_unregister(&hidp_proto); -} diff --git a/net/bluetooth_tizen/l2cap_core.c b/net/bluetooth_tizen/l2cap_core.c deleted file mode 100644 index 48268ab..0000000 --- a/net/bluetooth_tizen/l2cap_core.c +++ /dev/null @@ -1,4803 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org> - Copyright (C) 2010 Google Inc. - Copyright (C) 2011 ProFUSION Embedded Systems - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth L2CAP core. */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/capability.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/socket.h> -#include <linux/skbuff.h> -#include <linux/list.h> -#include <linux/device.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/uaccess.h> -#include <linux/crc16.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> -#include <net/bluetooth/smp.h> - -bool disable_ertm; - -static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; -static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, }; - -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); -static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, - 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); - -/* ---- L2CAP channels ---- */ - -static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid) -{ - struct l2cap_chan *c; - - list_for_each_entry(c, &conn->chan_l, list) { - if (c->dcid == cid) - return c; - } - return NULL; -} - -static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) -{ - struct l2cap_chan *c; - - list_for_each_entry(c, &conn->chan_l, list) { - if (c->scid == cid) - return c; - } - return NULL; -} - -/* Find channel with given SCID. - * Returns locked channel. */ -static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) -{ - struct l2cap_chan *c; - - mutex_lock(&conn->chan_lock); - c = __l2cap_get_chan_by_scid(conn, cid); - if (c) - l2cap_chan_lock(c); - mutex_unlock(&conn->chan_lock); - - return c; -} - -static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) -{ - struct l2cap_chan *c; - - list_for_each_entry(c, &conn->chan_l, list) { - if (c->ident == ident) - return c; - } - return NULL; -} - -static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) -{ - struct l2cap_chan *c; - - mutex_lock(&conn->chan_lock); - c = __l2cap_get_chan_by_ident(conn, ident); - mutex_unlock(&conn->chan_lock); - - return c; -} - -static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src) -{ - struct l2cap_chan *c; - - list_for_each_entry(c, &chan_list, global_l) { - if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src)) - return c; - } - return NULL; -} - -int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm) -{ - int err; - - write_lock(&chan_list_lock); - - if (psm && __l2cap_global_chan_by_addr(psm, src)) { - err = -EADDRINUSE; - goto done; - } - - if (psm) { - chan->psm = psm; - chan->sport = psm; - err = 0; - } else { - u16 p; - - err = -EINVAL; - for (p = 0x1001; p < 0x1100; p += 2) - if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src)) { - chan->psm = cpu_to_le16(p); - chan->sport = cpu_to_le16(p); - err = 0; - break; - } - } - -done: - write_unlock(&chan_list_lock); - return err; -} - -int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid) -{ - write_lock(&chan_list_lock); - - chan->scid = scid; - - write_unlock(&chan_list_lock); - - return 0; -} - -static u16 l2cap_alloc_cid(struct l2cap_conn *conn) -{ - u16 cid = L2CAP_CID_DYN_START; - - for (; cid < L2CAP_CID_DYN_END; cid++) { - if (!__l2cap_get_chan_by_scid(conn, cid)) - return cid; - } - - return 0; -} - -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)); - - chan->state = state; - chan->ops->state_change(chan->data, state); -} - -static void l2cap_state_change(struct l2cap_chan *chan, int state) -{ - struct sock *sk = chan->sk; - - lock_sock(sk); - __l2cap_state_change(chan, state); - release_sock(sk); -} - -static inline void __l2cap_chan_set_err(struct l2cap_chan *chan, int err) -{ - struct sock *sk = chan->sk; - - sk->sk_err = err; -} - -static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err) -{ - struct sock *sk = chan->sk; - - lock_sock(sk); - __l2cap_chan_set_err(chan, err); - release_sock(sk); -} - -static void l2cap_chan_timeout(struct work_struct *work) -{ - struct l2cap_chan *chan = container_of(work, struct l2cap_chan, - chan_timer.work); - struct l2cap_conn *conn = chan->conn; - int reason; - - BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); - - mutex_lock(&conn->chan_lock); - l2cap_chan_lock(chan); - - if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG) - reason = ECONNREFUSED; - else if (chan->state == BT_CONNECT && - chan->sec_level != BT_SECURITY_SDP) - reason = ECONNREFUSED; - else - reason = ETIMEDOUT; - - l2cap_chan_close(chan, reason); - - l2cap_chan_unlock(chan); - - chan->ops->close(chan->data); - mutex_unlock(&conn->chan_lock); - - l2cap_chan_put(chan); -} - -struct l2cap_chan *l2cap_chan_create(struct sock *sk) -{ - struct l2cap_chan *chan; - - chan = kzalloc(sizeof(*chan), GFP_ATOMIC); - if (!chan) - return NULL; - - mutex_init(&chan->lock); - - chan->sk = sk; - - write_lock(&chan_list_lock); - list_add(&chan->global_l, &chan_list); - write_unlock(&chan_list_lock); - - INIT_DELAYED_WORK(&chan->chan_timer, l2cap_chan_timeout); - - chan->state = BT_OPEN; - - atomic_set(&chan->refcnt, 1); - - BT_DBG("sk %p chan %p", sk, chan); - - return chan; -} - -void l2cap_chan_destroy(struct l2cap_chan *chan) -{ - write_lock(&chan_list_lock); - list_del(&chan->global_l); - write_unlock(&chan_list_lock); - - l2cap_chan_put(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, - chan->psm, chan->dcid); - - conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; - - chan->conn = conn; - - switch (chan->chan_type) { - case L2CAP_CHAN_CONN_ORIENTED: - if (conn->hcon->type == LE_LINK) { - /* LE connection */ - chan->omtu = L2CAP_LE_DEFAULT_MTU; - chan->scid = L2CAP_CID_LE_DATA; - chan->dcid = L2CAP_CID_LE_DATA; - } else { - /* Alloc CID for connection-oriented socket */ - chan->scid = l2cap_alloc_cid(conn); - chan->omtu = L2CAP_DEFAULT_MTU; - } - break; - - case L2CAP_CHAN_CONN_LESS: - /* Connectionless socket */ - chan->scid = L2CAP_CID_CONN_LESS; - chan->dcid = L2CAP_CID_CONN_LESS; - chan->omtu = L2CAP_DEFAULT_MTU; - break; - - default: - /* Raw socket can send/recv signalling messages only */ - chan->scid = L2CAP_CID_SIGNALING; - chan->dcid = L2CAP_CID_SIGNALING; - chan->omtu = L2CAP_DEFAULT_MTU; - } - - chan->local_id = L2CAP_BESTEFFORT_ID; - chan->local_stype = L2CAP_SERV_BESTEFFORT; - 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; - - l2cap_chan_hold(chan); - - list_add(&chan->list, &conn->chan_l); -} - -void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) -{ - mutex_lock(&conn->chan_lock); - __l2cap_chan_add(conn, chan); - mutex_unlock(&conn->chan_lock); -} - -static void l2cap_chan_del(struct l2cap_chan *chan, int err) -{ - struct sock *sk = chan->sk; - struct l2cap_conn *conn = chan->conn; - struct sock *parent = bt_sk(sk)->parent; - - __clear_chan_timer(chan); - - BT_DBG("chan %p, conn %p, err %d", chan, conn, err); - - if (conn) { - /* Delete from channel list */ - list_del(&chan->list); - - l2cap_chan_put(chan); - - chan->conn = NULL; - hci_conn_put(conn->hcon); - } - - lock_sock(sk); - - __l2cap_state_change(chan, BT_CLOSED); - sock_set_flag(sk, SOCK_ZAPPED); - - if (err) - __l2cap_chan_set_err(chan, err); - - if (parent) { - bt_accept_unlink(sk); - parent->sk_data_ready(parent, 0); - } else - sk->sk_state_change(sk); - - release_sock(sk); - - if (!(test_bit(CONF_OUTPUT_DONE, &chan->conf_state) && - test_bit(CONF_INPUT_DONE, &chan->conf_state))) - return; - - skb_queue_purge(&chan->tx_q); - - if (chan->mode == L2CAP_MODE_ERTM) { - struct srej_list *l, *tmp; - - __clear_retrans_timer(chan); - __clear_monitor_timer(chan); - __clear_ack_timer(chan); - - skb_queue_purge(&chan->srej_q); - - list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { - list_del(&l->list); - kfree(l); - } - } -} - -static void l2cap_chan_cleanup_listen(struct sock *parent) -{ - struct sock *sk; - - BT_DBG("parent %p", parent); - - /* Close not yet accepted channels */ - while ((sk = bt_accept_dequeue(parent, NULL))) { - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - - l2cap_chan_lock(chan); - __clear_chan_timer(chan); - l2cap_chan_close(chan, ECONNRESET); - l2cap_chan_unlock(chan); - - chan->ops->close(chan->data); - } -} - -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); - - switch (chan->state) { - case BT_LISTEN: - lock_sock(sk); - l2cap_chan_cleanup_listen(sk); - - __l2cap_state_change(chan, BT_CLOSED); - sock_set_flag(sk, SOCK_ZAPPED); - release_sock(sk); - break; - - case BT_CONNECTED: - case BT_CONFIG: - if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && - conn->hcon->type == ACL_LINK) { - __clear_chan_timer(chan); - __set_chan_timer(chan, sk->sk_sndtimeo); - l2cap_send_disconn_req(conn, chan, reason); - } else - l2cap_chan_del(chan, reason); - break; - - case BT_CONNECT2: - if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && - conn->hcon->type == ACL_LINK) { - struct l2cap_conn_rsp rsp; - __u16 result; - - if (bt_sk(sk)->defer_setup) - result = L2CAP_CR_SEC_BLOCK; - else - result = L2CAP_CR_BAD_PSM; - l2cap_state_change(chan, BT_DISCONN); - - rsp.scid = cpu_to_le16(chan->dcid); - rsp.dcid = cpu_to_le16(chan->scid); - rsp.result = cpu_to_le16(result); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); - } - - l2cap_chan_del(chan, reason); - break; - - case BT_CONNECT: - case BT_DISCONN: - l2cap_chan_del(chan, reason); - break; - - default: - lock_sock(sk); - sock_set_flag(sk, SOCK_ZAPPED); - release_sock(sk); - break; - } -} - -static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) -{ - if (chan->chan_type == L2CAP_CHAN_RAW) { - switch (chan->sec_level) { - case BT_SECURITY_HIGH: - return HCI_AT_DEDICATED_BONDING_MITM; - case BT_SECURITY_MEDIUM: - return HCI_AT_DEDICATED_BONDING; - default: - return HCI_AT_NO_BONDING; - } - } else if (chan->psm == cpu_to_le16(0x0001)) { - if (chan->sec_level == BT_SECURITY_LOW) - chan->sec_level = BT_SECURITY_SDP; - - if (chan->sec_level == BT_SECURITY_HIGH) - return HCI_AT_NO_BONDING_MITM; - else - return HCI_AT_NO_BONDING; - } else { - switch (chan->sec_level) { - case BT_SECURITY_HIGH: - return HCI_AT_GENERAL_BONDING_MITM; - case BT_SECURITY_MEDIUM: - return HCI_AT_GENERAL_BONDING; - default: - return HCI_AT_NO_BONDING; - } - } -} - -/* Service level security */ -int l2cap_chan_check_security(struct l2cap_chan *chan) -{ - struct l2cap_conn *conn = chan->conn; - __u8 auth_type; - - auth_type = l2cap_get_auth_type(chan); - - return hci_conn_security(conn->hcon, chan->sec_level, auth_type); -} - -static u8 l2cap_get_ident(struct l2cap_conn *conn) -{ - u8 id; - - /* Get next available identificator. - * 1 - 128 are used by kernel. - * 129 - 199 are reserved. - * 200 - 254 are used by utilities like l2ping, etc. - */ - - spin_lock(&conn->lock); - - if (++conn->tx_ident > 128) - conn->tx_ident = 1; - - id = conn->tx_ident; - - spin_unlock(&conn->lock); - - return id; -} - -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; - - BT_DBG("code 0x%2.2x", code); - - if (!skb) - return; - - if (lmp_no_flush_capable(conn->hcon->hdev)) - flags = ACL_START_NO_FLUSH; - else - flags = ACL_START; - - bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON; - skb->priority = HCI_PRIO_MAX; - - hci_send_acl(conn->hchan, skb, flags); -} - -static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) -{ - struct hci_conn *hcon = chan->conn->hcon; - u16 flags; - - BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len, - skb->priority); - - if (!test_bit(FLAG_FLUSHABLE, &chan->flags) && - lmp_no_flush_capable(hcon->hdev)) - flags = ACL_START_NO_FLUSH; - else - flags = ACL_START; - - bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); - hci_send_acl(chan->conn->hchan, skb, flags); -} - -static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control) -{ - struct sk_buff *skb; - struct l2cap_hdr *lh; - struct l2cap_conn *conn = chan->conn; - int count, hlen; - - if (chan->state != BT_CONNECTED) - return; - - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - hlen = L2CAP_EXT_HDR_SIZE; - else - hlen = L2CAP_ENH_HDR_SIZE; - - if (chan->fcs == L2CAP_FCS_CRC16) - hlen += L2CAP_FCS_SIZE; - - BT_DBG("chan %p, control 0x%8.8x", chan, control); - - count = min_t(unsigned int, conn->mtu, hlen); - - control |= __set_sframe(chan); - - if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) - control |= __set_ctrl_final(chan); - - if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state)) - control |= __set_ctrl_poll(chan); - - skb = bt_skb_alloc(count, GFP_ATOMIC); - if (!skb) - return; - - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(chan->dcid); - - __put_control(chan, control, skb_put(skb, __ctrl_size(chan))); - - if (chan->fcs == L2CAP_FCS_CRC16) { - u16 fcs = crc16(0, (u8 *)lh, count - L2CAP_FCS_SIZE); - put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE)); - } - - skb->priority = HCI_PRIO_MAX; - l2cap_do_send(chan, skb); -} - -static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u32 control) -{ - if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); - set_bit(CONN_RNR_SENT, &chan->conn_state); - } else - control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); - - control |= __set_reqseq(chan, chan->buffer_seq); - - l2cap_send_sframe(chan, control); -} - -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) -{ - struct l2cap_conn *conn = chan->conn; - struct l2cap_conn_req req; - - req.scid = cpu_to_le16(chan->scid); - req.psm = chan->psm; - - chan->ident = l2cap_get_ident(conn); - - set_bit(CONF_CONNECT_PEND, &chan->conf_state); - - l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); -} - -static void l2cap_do_start(struct l2cap_chan *chan) -{ - struct l2cap_conn *conn = chan->conn; - - if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { - if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)) - return; - - if (l2cap_chan_check_security(chan) && - __l2cap_no_conn_pending(chan)) - l2cap_send_conn_req(chan); - } else { - struct l2cap_info_req req; - req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK); - - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT; - conn->info_ident = l2cap_get_ident(conn); - - schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT); - - l2cap_send_cmd(conn, conn->info_ident, - L2CAP_INFO_REQ, sizeof(req), &req); - } -} - -static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) -{ - u32 local_feat_mask = l2cap_feat_mask; - if (!disable_ertm) - local_feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING; - - switch (mode) { - case L2CAP_MODE_ERTM: - return L2CAP_FEAT_ERTM & feat_mask & local_feat_mask; - case L2CAP_MODE_STREAMING: - return L2CAP_FEAT_STREAMING & feat_mask & local_feat_mask; - default: - return 0x00; - } -} - -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; - - if (!conn) - return; - - if (chan->mode == L2CAP_MODE_ERTM) { - __clear_retrans_timer(chan); - __clear_monitor_timer(chan); - __clear_ack_timer(chan); - } - - 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); - - lock_sock(sk); - __l2cap_state_change(chan, BT_DISCONN); - __l2cap_chan_set_err(chan, err); - release_sock(sk); -} - -/* ---- L2CAP connections ---- */ -static void l2cap_conn_start(struct l2cap_conn *conn) -{ - struct l2cap_chan *chan, *tmp; - - BT_DBG("conn %p", conn); - - mutex_lock(&conn->chan_lock); - - list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) { - struct sock *sk = chan->sk; - - l2cap_chan_lock(chan); - - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - l2cap_chan_unlock(chan); - continue; - } - - if (chan->state == BT_CONNECT) { - if (!l2cap_chan_check_security(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, - &chan->conf_state)) { - l2cap_chan_close(chan, ECONNRESET); - l2cap_chan_unlock(chan); - continue; - } - - l2cap_send_conn_req(chan); - - } else if (chan->state == BT_CONNECT2) { - struct l2cap_conn_rsp rsp; - char buf[128]; - rsp.scid = cpu_to_le16(chan->dcid); - rsp.dcid = cpu_to_le16(chan->scid); - - if (l2cap_chan_check_security(chan)) { - lock_sock(sk); - if (bt_sk(sk)->defer_setup) { - struct sock *parent = bt_sk(sk)->parent; - rsp.result = cpu_to_le16(L2CAP_CR_PEND); - rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND); - if (parent) - parent->sk_data_ready(parent, 0); - - } else { - __l2cap_state_change(chan, BT_CONFIG); - rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - } - release_sock(sk); - } else { - rsp.result = cpu_to_le16(L2CAP_CR_PEND); - rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND); - } - - l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); - - if (test_bit(CONF_REQ_SENT, &chan->conf_state) || - 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); - chan->num_conf_req++; - } - - l2cap_chan_unlock(chan); - } - - mutex_unlock(&conn->chan_lock); -} - -/* Find socket with cid and source bdaddr. - * Returns closest match, locked. - */ -static struct l2cap_chan *l2cap_global_chan_by_scid(int state, __le16 cid, bdaddr_t *src) -{ - struct l2cap_chan *c, *c1 = NULL; - - read_lock(&chan_list_lock); - - list_for_each_entry(c, &chan_list, global_l) { - struct sock *sk = c->sk; - - if (state && c->state != state) - continue; - - if (c->scid == cid) { - /* Exact match. */ - if (!bacmp(&bt_sk(sk)->src, src)) { - read_unlock(&chan_list_lock); - return c; - } - - /* Closest match */ - if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) - c1 = c; - } - } - - read_unlock(&chan_list_lock); - - return c1; -} - -static void l2cap_le_conn_ready(struct l2cap_conn *conn) -{ - struct sock *parent, *sk; - struct l2cap_chan *chan, *pchan; - - BT_DBG(""); - - /* Check if we have socket listening on cid */ - pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA, - conn->src); - if (!pchan) - return; - - parent = pchan->sk; - - lock_sock(parent); - - /* Check for backlog size */ - if (sk_acceptq_is_full(parent)) { - BT_DBG("backlog full %d", parent->sk_ack_backlog); - goto clean; - } - - chan = pchan->ops->new_connection(pchan->data); - if (!chan) - goto clean; - - sk = chan->sk; - - hci_conn_hold(conn->hcon); - - bacpy(&bt_sk(sk)->src, conn->src); - bacpy(&bt_sk(sk)->dst, conn->dst); - - bt_accept_enqueue(parent, sk); - - l2cap_chan_add(conn, chan); - - __set_chan_timer(chan, sk->sk_sndtimeo); - - __l2cap_state_change(chan, BT_CONNECTED); - parent->sk_data_ready(parent, 0); - -clean: - release_sock(parent); -} - -static void l2cap_chan_ready(struct l2cap_chan *chan) -{ - struct sock *sk = chan->sk; - struct sock *parent; - - lock_sock(sk); - - parent = bt_sk(sk)->parent; - - BT_DBG("sk %p, parent %p", sk, parent); - - chan->conf_state = 0; - __clear_chan_timer(chan); - - __l2cap_state_change(chan, BT_CONNECTED); - sk->sk_state_change(sk); - - if (parent) - parent->sk_data_ready(parent, 0); - - release_sock(sk); -} - -static void l2cap_conn_ready(struct l2cap_conn *conn) -{ - struct l2cap_chan *chan; - - BT_DBG("conn %p", conn); - - if (!conn->hcon->out && conn->hcon->type == LE_LINK) - l2cap_le_conn_ready(conn); - - if (conn->hcon->out && conn->hcon->type == LE_LINK) - smp_conn_security(conn, conn->hcon->pending_sec_level); - - mutex_lock(&conn->chan_lock); - - list_for_each_entry(chan, &conn->chan_l, list) { - - l2cap_chan_lock(chan); - - if (conn->hcon->type == LE_LINK) { - if (smp_conn_security(conn, chan->sec_level)) - l2cap_chan_ready(chan); - - } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - struct sock *sk = chan->sk; - __clear_chan_timer(chan); - lock_sock(sk); - __l2cap_state_change(chan, BT_CONNECTED); - sk->sk_state_change(sk); - release_sock(sk); - - } else if (chan->state == BT_CONNECT) - l2cap_do_start(chan); - - l2cap_chan_unlock(chan); - } - - mutex_unlock(&conn->chan_lock); -} - -/* Notify sockets that we cannot guaranty reliability anymore */ -static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) -{ - struct l2cap_chan *chan; - - BT_DBG("conn %p", conn); - - mutex_lock(&conn->chan_lock); - - list_for_each_entry(chan, &conn->chan_l, list) { - if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) - __l2cap_chan_set_err(chan, err); - } - - mutex_unlock(&conn->chan_lock); -} - -static void l2cap_info_timeout(struct work_struct *work) -{ - struct l2cap_conn *conn = container_of(work, struct l2cap_conn, - info_timer.work); - - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; - conn->info_ident = 0; - - l2cap_conn_start(conn); -} - -static void l2cap_conn_del(struct hci_conn *hcon, int err) -{ - struct l2cap_conn *conn = hcon->l2cap_data; - struct l2cap_chan *chan, *l; - - if (!conn) - return; - - BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); - - kfree_skb(conn->rx_skb); - - mutex_lock(&conn->chan_lock); - - /* Kill channels */ - list_for_each_entry_safe(chan, l, &conn->chan_l, list) { - l2cap_chan_lock(chan); - - l2cap_chan_del(chan, err); - - l2cap_chan_unlock(chan); - - chan->ops->close(chan->data); - } - - mutex_unlock(&conn->chan_lock); - - hci_chan_del(conn->hchan); - - if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) - cancel_delayed_work_sync(&conn->info_timer); - - if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) { - cancel_delayed_work_sync(&conn->security_timer); - smp_chan_destroy(conn); - } - - hcon->l2cap_data = NULL; - kfree(conn); -} - -static void security_timeout(struct work_struct *work) -{ - struct l2cap_conn *conn = container_of(work, struct l2cap_conn, - security_timer.work); - - l2cap_conn_del(conn->hcon, ETIMEDOUT); -} - -static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) -{ - struct l2cap_conn *conn = hcon->l2cap_data; - struct hci_chan *hchan; - - if (conn || status) - return conn; - - hchan = hci_chan_create(hcon); - if (!hchan) - return NULL; - - conn = kzalloc(sizeof(struct l2cap_conn), GFP_ATOMIC); - if (!conn) { - hci_chan_del(hchan); - return NULL; - } - - hcon->l2cap_data = conn; - conn->hcon = hcon; - conn->hchan = hchan; - - 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 - conn->mtu = hcon->hdev->acl_mtu; - - conn->src = &hcon->hdev->bdaddr; - conn->dst = &hcon->dst; - - conn->feat_mask = 0; - - spin_lock_init(&conn->lock); - mutex_init(&conn->chan_lock); - - INIT_LIST_HEAD(&conn->chan_l); - - if (hcon->type == LE_LINK) - INIT_DELAYED_WORK(&conn->security_timer, security_timeout); - else - INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout); - - conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; - - return conn; -} - -/* ---- Socket interface ---- */ - -/* Find socket with psm and source bdaddr. - * Returns closest match. - */ -static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr_t *src) -{ - struct l2cap_chan *c, *c1 = NULL; - - read_lock(&chan_list_lock); - - list_for_each_entry(c, &chan_list, global_l) { - struct sock *sk = c->sk; - - if (state && c->state != state) - continue; - - if (c->psm == psm) { - /* Exact match. */ - if (!bacmp(&bt_sk(sk)->src, src)) { - read_unlock(&chan_list_lock); - return c; - } - - /* Closest match */ - if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) - c1 = c; - } - } - - read_unlock(&chan_list_lock); - - return c1; -} - -int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst) -{ - struct sock *sk = chan->sk; - bdaddr_t *src = &bt_sk(sk)->src; - struct l2cap_conn *conn; - struct hci_conn *hcon; - struct hci_dev *hdev; - __u8 auth_type; - int err; - - BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), - chan->psm); - - hdev = hci_get_route(dst, src); - if (!hdev) - return -EHOSTUNREACH; - - hci_dev_lock(hdev); - - l2cap_chan_lock(chan); - - /* 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) { - err = -EINVAL; - goto done; - } - - if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(psm || cid)) { - err = -EINVAL; - goto done; - } - - switch (chan->mode) { - case L2CAP_MODE_BASIC: - break; - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - if (!disable_ertm) - break; - /* fall through */ - default: - err = -ENOTSUPP; - goto done; - } - - lock_sock(sk); - - switch (sk->sk_state) { - case BT_CONNECT: - case BT_CONNECT2: - case BT_CONFIG: - /* Already connecting */ - err = 0; - release_sock(sk); - goto done; - - case BT_CONNECTED: - /* Already connected */ - err = -EISCONN; - release_sock(sk); - goto done; - - case BT_OPEN: - case BT_BOUND: - /* Can connect */ - break; - - default: - err = -EBADFD; - release_sock(sk); - goto done; - } - - /* Set destination address and psm */ - bacpy(&bt_sk(sk)->dst, dst); - - release_sock(sk); - - chan->psm = psm; - chan->dcid = cid; - - auth_type = l2cap_get_auth_type(chan); - - if (chan->dcid == L2CAP_CID_LE_DATA) - hcon = hci_connect(hdev, LE_LINK, dst, - chan->sec_level, auth_type); - else - hcon = hci_connect(hdev, ACL_LINK, dst, - chan->sec_level, auth_type); - - if (IS_ERR(hcon)) { - err = PTR_ERR(hcon); - goto done; - } - - conn = l2cap_conn_add(hcon, 0); - if (!conn) { - hci_conn_put(hcon); - err = -ENOMEM; - goto done; - } - - /* Update source addr of the socket */ - bacpy(src, conn->src); - - l2cap_chan_unlock(chan); - l2cap_chan_add(conn, chan); - l2cap_chan_lock(chan); - - l2cap_state_change(chan, BT_CONNECT); - __set_chan_timer(chan, sk->sk_sndtimeo); - - if (hcon->state == BT_CONNECTED) { - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - __clear_chan_timer(chan); - if (l2cap_chan_check_security(chan)) - l2cap_state_change(chan, BT_CONNECTED); - } else - l2cap_do_start(chan); - } - - err = 0; - -done: - l2cap_chan_unlock(chan); - hci_dev_unlock(hdev); - hci_dev_put(hdev); - return err; -} - -int __l2cap_wait_ack(struct sock *sk) -{ - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - DECLARE_WAITQUEUE(wait, current); - int err = 0; - int timeo = HZ/5; - - add_wait_queue(sk_sleep(sk), &wait); - set_current_state(TASK_INTERRUPTIBLE); - while (chan->unacked_frames > 0 && chan->conn) { - if (!timeo) - timeo = HZ/5; - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - set_current_state(TASK_INTERRUPTIBLE); - - err = sock_error(sk); - if (err) - break; - } - set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - return err; -} - -static void l2cap_monitor_timeout(struct work_struct *work) -{ - struct l2cap_chan *chan = container_of(work, struct l2cap_chan, - monitor_timer.work); - - BT_DBG("chan %p", chan); - - l2cap_chan_lock(chan); - - if (chan->retry_count >= chan->remote_max_tx) { - l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); - l2cap_chan_unlock(chan); - l2cap_chan_put(chan); - return; - } - - chan->retry_count++; - __set_monitor_timer(chan); - - l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); - l2cap_chan_unlock(chan); - l2cap_chan_put(chan); -} - -static void l2cap_retrans_timeout(struct work_struct *work) -{ - struct l2cap_chan *chan = container_of(work, struct l2cap_chan, - retrans_timer.work); - - BT_DBG("chan %p", chan); - - l2cap_chan_lock(chan); - - chan->retry_count = 1; - __set_monitor_timer(chan); - - set_bit(CONN_WAIT_F, &chan->conn_state); - - l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); - - l2cap_chan_unlock(chan); - l2cap_chan_put(chan); -} - -static void l2cap_drop_acked_frames(struct l2cap_chan *chan) -{ - struct sk_buff *skb; - - while ((skb = skb_peek(&chan->tx_q)) && - chan->unacked_frames) { - if (bt_cb(skb)->tx_seq == chan->expected_ack_seq) - break; - - skb = skb_dequeue(&chan->tx_q); - kfree_skb(skb); - - chan->unacked_frames--; - } - - if (!chan->unacked_frames) - __clear_retrans_timer(chan); -} - -static void l2cap_streaming_send(struct l2cap_chan *chan) -{ - struct sk_buff *skb; - u32 control; - u16 fcs; - - while ((skb = skb_dequeue(&chan->tx_q))) { - control = __get_control(chan, skb->data + L2CAP_HDR_SIZE); - control |= __set_txseq(chan, chan->next_tx_seq); - __put_control(chan, control, skb->data + L2CAP_HDR_SIZE); - - if (chan->fcs == L2CAP_FCS_CRC16) { - fcs = crc16(0, (u8 *)skb->data, - skb->len - L2CAP_FCS_SIZE); - put_unaligned_le16(fcs, - skb->data + skb->len - L2CAP_FCS_SIZE); - } - - l2cap_do_send(chan, skb); - - chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq); - } -} - -static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq) -{ - struct sk_buff *skb, *tx_skb; - u16 fcs; - u32 control; - - skb = skb_peek(&chan->tx_q); - if (!skb) - return; - - while (bt_cb(skb)->tx_seq != tx_seq) { - if (skb_queue_is_last(&chan->tx_q, skb)) - return; - - skb = skb_queue_next(&chan->tx_q, skb); - } - - if (chan->remote_max_tx && - bt_cb(skb)->retries == chan->remote_max_tx) { - l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); - return; - } - - tx_skb = skb_clone(skb, GFP_ATOMIC); - bt_cb(skb)->retries++; - - control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE); - control &= __get_sar_mask(chan); - - if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) - control |= __set_ctrl_final(chan); - - control |= __set_reqseq(chan, chan->buffer_seq); - control |= __set_txseq(chan, tx_seq); - - __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE); - - if (chan->fcs == L2CAP_FCS_CRC16) { - fcs = crc16(0, (u8 *)tx_skb->data, - tx_skb->len - L2CAP_FCS_SIZE); - put_unaligned_le16(fcs, - tx_skb->data + tx_skb->len - L2CAP_FCS_SIZE); - } - - l2cap_do_send(chan, tx_skb); -} - -static int l2cap_ertm_send(struct l2cap_chan *chan) -{ - struct sk_buff *skb, *tx_skb; - u16 fcs; - u32 control; - int nsent = 0; - - if (chan->state != BT_CONNECTED) - return -ENOTCONN; - - while ((skb = chan->tx_send_head) && (!l2cap_tx_window_full(chan))) { - - if (chan->remote_max_tx && - bt_cb(skb)->retries == chan->remote_max_tx) { - l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); - break; - } - - tx_skb = skb_clone(skb, GFP_ATOMIC); - - bt_cb(skb)->retries++; - - control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE); - control &= __get_sar_mask(chan); - - if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) - control |= __set_ctrl_final(chan); - - control |= __set_reqseq(chan, chan->buffer_seq); - control |= __set_txseq(chan, chan->next_tx_seq); - - __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE); - - if (chan->fcs == L2CAP_FCS_CRC16) { - fcs = crc16(0, (u8 *)skb->data, - tx_skb->len - L2CAP_FCS_SIZE); - put_unaligned_le16(fcs, skb->data + - tx_skb->len - L2CAP_FCS_SIZE); - } - - l2cap_do_send(chan, tx_skb); - - __set_retrans_timer(chan); - - bt_cb(skb)->tx_seq = chan->next_tx_seq; - - chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq); - - if (bt_cb(skb)->retries == 1) { - chan->unacked_frames++; - - if (!nsent++) - __clear_ack_timer(chan); - } - - chan->frames_sent++; - - if (skb_queue_is_last(&chan->tx_q, skb)) - chan->tx_send_head = NULL; - else - chan->tx_send_head = skb_queue_next(&chan->tx_q, skb); - } - - return nsent; -} - -static int l2cap_retransmit_frames(struct l2cap_chan *chan) -{ - int ret; - - if (!skb_queue_empty(&chan->tx_q)) - chan->tx_send_head = chan->tx_q.next; - - chan->next_tx_seq = chan->expected_ack_seq; - ret = l2cap_ertm_send(chan); - return ret; -} - -static void __l2cap_send_ack(struct l2cap_chan *chan) -{ - u32 control = 0; - - control |= __set_reqseq(chan, chan->buffer_seq); - - if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); - set_bit(CONN_RNR_SENT, &chan->conn_state); - l2cap_send_sframe(chan, control); - return; - } - - if (l2cap_ertm_send(chan) > 0) - return; - - control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); - l2cap_send_sframe(chan, control); -} - -static void l2cap_send_ack(struct l2cap_chan *chan) -{ - __clear_ack_timer(chan); - __l2cap_send_ack(chan); -} - -static void l2cap_send_srejtail(struct l2cap_chan *chan) -{ - struct srej_list *tail; - u32 control; - - control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); - control |= __set_ctrl_final(chan); - - tail = list_entry((&chan->srej_l)->prev, struct srej_list, list); - control |= __set_reqseq(chan, tail->tx_seq); - - l2cap_send_sframe(chan, control); -} - -static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, - struct msghdr *msg, int len, - int count, struct sk_buff *skb) -{ - struct l2cap_conn *conn = chan->conn; - struct sk_buff **frag; - int err, sent = 0; - - if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) - return -EFAULT; - - sent += count; - len -= count; - - /* Continuation fragments (no L2CAP header) */ - frag = &skb_shinfo(skb)->frag_list; - while (len) { - count = min_t(unsigned int, conn->mtu, len); - - *frag = chan->ops->alloc_skb(chan, count, - msg->msg_flags & MSG_DONTWAIT, - &err); - - if (!*frag) - return err; - if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) - return -EFAULT; - - (*frag)->priority = skb->priority; - - sent += count; - len -= count; - - frag = &(*frag)->next; - } - - return sent; -} - -static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, - struct msghdr *msg, size_t len, - u32 priority) -{ - struct l2cap_conn *conn = chan->conn; - struct sk_buff *skb; - int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE; - struct l2cap_hdr *lh; - - BT_DBG("chan %p len %d priority %u", chan, (int)len, priority); - - count = min_t(unsigned int, (conn->mtu - hlen), len); - - skb = chan->ops->alloc_skb(chan, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); - - if (!skb) - return ERR_PTR(err); - - skb->priority = priority; - - /* Create L2CAP header */ - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(chan->dcid); - lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); - put_unaligned_le16(chan->psm, skb_put(skb, 2)); - - err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb); - if (unlikely(err < 0)) { - kfree_skb(skb); - return ERR_PTR(err); - } - return skb; -} - -static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, - struct msghdr *msg, size_t len, - u32 priority) -{ - struct l2cap_conn *conn = chan->conn; - struct sk_buff *skb; - int err, count, hlen = L2CAP_HDR_SIZE; - struct l2cap_hdr *lh; - - BT_DBG("chan %p len %d", chan, (int)len); - - count = min_t(unsigned int, (conn->mtu - hlen), len); - - skb = chan->ops->alloc_skb(chan, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); - - if (!skb) - return ERR_PTR(err); - - skb->priority = priority; - - /* Create L2CAP header */ - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(chan->dcid); - lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); - - err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb); - if (unlikely(err < 0)) { - kfree_skb(skb); - return ERR_PTR(err); - } - return skb; -} - -static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, - struct msghdr *msg, size_t len, - u32 control, u16 sdulen) -{ - struct l2cap_conn *conn = chan->conn; - struct sk_buff *skb; - int err, count, hlen; - struct l2cap_hdr *lh; - - BT_DBG("chan %p len %d", chan, (int)len); - - if (!conn) - return ERR_PTR(-ENOTCONN); - - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - hlen = L2CAP_EXT_HDR_SIZE; - else - hlen = L2CAP_ENH_HDR_SIZE; - - if (sdulen) - hlen += L2CAP_SDULEN_SIZE; - - if (chan->fcs == L2CAP_FCS_CRC16) - hlen += L2CAP_FCS_SIZE; - - count = min_t(unsigned int, (conn->mtu - hlen), len); - - skb = chan->ops->alloc_skb(chan, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); - - if (!skb) - return ERR_PTR(err); - - /* Create L2CAP header */ - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(chan->dcid); - lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); - - __put_control(chan, control, skb_put(skb, __ctrl_size(chan))); - - if (sdulen) - put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE)); - - err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb); - if (unlikely(err < 0)) { - kfree_skb(skb); - return ERR_PTR(err); - } - - if (chan->fcs == L2CAP_FCS_CRC16) - put_unaligned_le16(0, skb_put(skb, L2CAP_FCS_SIZE)); - - bt_cb(skb)->retries = 0; - return skb; -} - -static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) -{ - struct sk_buff *skb; - struct sk_buff_head sar_queue; - u32 control; - size_t size = 0; - - skb_queue_head_init(&sar_queue); - control = __set_ctrl_sar(chan, L2CAP_SAR_START); - skb = l2cap_create_iframe_pdu(chan, msg, chan->remote_mps, control, len); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - __skb_queue_tail(&sar_queue, skb); - len -= chan->remote_mps; - size += chan->remote_mps; - - while (len > 0) { - size_t buflen; - - if (len > chan->remote_mps) { - control = __set_ctrl_sar(chan, L2CAP_SAR_CONTINUE); - buflen = chan->remote_mps; - } else { - control = __set_ctrl_sar(chan, L2CAP_SAR_END); - buflen = len; - } - - skb = l2cap_create_iframe_pdu(chan, msg, buflen, control, 0); - if (IS_ERR(skb)) { - skb_queue_purge(&sar_queue); - return PTR_ERR(skb); - } - - __skb_queue_tail(&sar_queue, skb); - len -= buflen; - size += buflen; - } - skb_queue_splice_tail(&sar_queue, &chan->tx_q); - if (chan->tx_send_head == NULL) - chan->tx_send_head = sar_queue.next; - - return size; -} - -int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, - u32 priority) -{ - struct sk_buff *skb; - u32 control; - int err; - - /* Connectionless channel */ - if (chan->chan_type == L2CAP_CHAN_CONN_LESS) { - skb = l2cap_create_connless_pdu(chan, msg, len, priority); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - l2cap_do_send(chan, skb); - return len; - } - - switch (chan->mode) { - case L2CAP_MODE_BASIC: - /* Check outgoing MTU */ - if (len > chan->omtu) - return -EMSGSIZE; - - /* Create a basic PDU */ - skb = l2cap_create_basic_pdu(chan, msg, len, priority); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - l2cap_do_send(chan, skb); - err = len; - break; - - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - /* Entire SDU fits into one PDU */ - if (len <= chan->remote_mps) { - control = __set_ctrl_sar(chan, L2CAP_SAR_UNSEGMENTED); - skb = l2cap_create_iframe_pdu(chan, msg, len, control, - 0); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - __skb_queue_tail(&chan->tx_q, skb); - - if (chan->tx_send_head == NULL) - chan->tx_send_head = skb; - - } else { - /* Segment SDU into multiples PDUs */ - err = l2cap_sar_segment_sdu(chan, msg, len); - if (err < 0) - return err; - } - - if (chan->mode == L2CAP_MODE_STREAMING) { - l2cap_streaming_send(chan); - err = len; - break; - } - - if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && - test_bit(CONN_WAIT_F, &chan->conn_state)) { - err = len; - break; - } - - err = l2cap_ertm_send(chan); - if (err >= 0) - err = len; - - break; - - default: - BT_DBG("bad state %1.1x", chan->mode); - err = -EBADFD; - } - - return err; -} - -/* Copy frame to all raw sockets on that connection */ -static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct sk_buff *nskb; - struct l2cap_chan *chan; - - BT_DBG("conn %p", conn); - - mutex_lock(&conn->chan_lock); - - list_for_each_entry(chan, &conn->chan_l, list) { - struct sock *sk = chan->sk; - if (chan->chan_type != L2CAP_CHAN_RAW) - continue; - - /* Don't send frame to the socket it came from */ - if (skb->sk == sk) - continue; - nskb = skb_clone(skb, GFP_ATOMIC); - if (!nskb) - continue; - - if (chan->ops->recv(chan->data, nskb)) - kfree_skb(nskb); - } - - mutex_unlock(&conn->chan_lock); -} - -/* ---- L2CAP signalling commands ---- */ -static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, - u8 code, u8 ident, u16 dlen, void *data) -{ - struct sk_buff *skb, **frag; - struct l2cap_cmd_hdr *cmd; - struct l2cap_hdr *lh; - int len, count; - - BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d", - conn, code, ident, dlen); - - len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen; - count = min_t(unsigned int, conn->mtu, len); - - skb = bt_skb_alloc(count, GFP_ATOMIC); - if (!skb) - return NULL; - - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->len = cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen); - - if (conn->hcon->type == LE_LINK) - lh->cid = cpu_to_le16(L2CAP_CID_LE_SIGNALING); - else - lh->cid = cpu_to_le16(L2CAP_CID_SIGNALING); - - cmd = (struct l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE); - cmd->code = code; - cmd->ident = ident; - cmd->len = cpu_to_le16(dlen); - - if (dlen) { - count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE; - memcpy(skb_put(skb, count), data, count); - data += count; - } - - len -= skb->len; - - /* Continuation fragments (no L2CAP header) */ - frag = &skb_shinfo(skb)->frag_list; - while (len) { - count = min_t(unsigned int, conn->mtu, len); - - *frag = bt_skb_alloc(count, GFP_ATOMIC); - if (!*frag) - goto fail; - - memcpy(skb_put(*frag, count), data, count); - - len -= count; - data += count; - - frag = &(*frag)->next; - } - - return skb; - -fail: - kfree_skb(skb); - return NULL; -} - -static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val) -{ - struct l2cap_conf_opt *opt = *ptr; - int len; - - len = L2CAP_CONF_OPT_SIZE + opt->len; - *ptr += len; - - *type = opt->type; - *olen = opt->len; - - switch (opt->len) { - case 1: - *val = *((u8 *) opt->val); - break; - - case 2: - *val = get_unaligned_le16(opt->val); - break; - - case 4: - *val = get_unaligned_le32(opt->val); - break; - - default: - *val = (unsigned long) opt->val; - break; - } - - BT_DBG("type 0x%2.2x len %d val 0x%lx", *type, opt->len, *val); - return len; -} - -static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val) -{ - struct l2cap_conf_opt *opt = *ptr; - - BT_DBG("type 0x%2.2x len %d val 0x%lx", type, len, val); - - opt->type = type; - opt->len = len; - - switch (len) { - case 1: - *((u8 *) opt->val) = val; - break; - - case 2: - put_unaligned_le16(val, opt->val); - break; - - case 4: - put_unaligned_le32(val, opt->val); - break; - - default: - memcpy(opt->val, (void *) val, len); - break; - } - - *ptr += L2CAP_CONF_OPT_SIZE + len; -} - -static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan) -{ - struct l2cap_conf_efs efs; - - switch (chan->mode) { - case L2CAP_MODE_ERTM: - efs.id = chan->local_id; - efs.stype = chan->local_stype; - efs.msdu = cpu_to_le16(chan->local_msdu); - efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime); - efs.acc_lat = cpu_to_le32(L2CAP_DEFAULT_ACC_LAT); - efs.flush_to = cpu_to_le32(L2CAP_DEFAULT_FLUSH_TO); - break; - - case L2CAP_MODE_STREAMING: - efs.id = 1; - efs.stype = L2CAP_SERV_BESTEFFORT; - efs.msdu = cpu_to_le16(chan->local_msdu); - efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime); - efs.acc_lat = 0; - efs.flush_to = 0; - break; - - default: - return; - } - - l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs), - (unsigned long) &efs); -} - -static void l2cap_ack_timeout(struct work_struct *work) -{ - struct l2cap_chan *chan = container_of(work, struct l2cap_chan, - ack_timer.work); - - BT_DBG("chan %p", chan); - - l2cap_chan_lock(chan); - - __l2cap_send_ack(chan); - - l2cap_chan_unlock(chan); - - l2cap_chan_put(chan); -} - -static inline void l2cap_ertm_init(struct l2cap_chan *chan) -{ - chan->expected_ack_seq = 0; - chan->unacked_frames = 0; - chan->buffer_seq = 0; - chan->num_acked = 0; - chan->frames_sent = 0; - - INIT_DELAYED_WORK(&chan->retrans_timer, l2cap_retrans_timeout); - INIT_DELAYED_WORK(&chan->monitor_timer, l2cap_monitor_timeout); - INIT_DELAYED_WORK(&chan->ack_timer, l2cap_ack_timeout); - - skb_queue_head_init(&chan->srej_q); - - INIT_LIST_HEAD(&chan->srej_l); -} - -static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) -{ - switch (mode) { - case L2CAP_MODE_STREAMING: - case L2CAP_MODE_ERTM: - if (l2cap_mode_supported(mode, remote_feat_mask)) - return mode; - /* fall through */ - default: - return L2CAP_MODE_BASIC; - } -} - -static inline bool __l2cap_ews_supported(struct l2cap_chan *chan) -{ - return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW; -} - -static inline bool __l2cap_efs_supported(struct l2cap_chan *chan) -{ - return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW; -} - -static inline void l2cap_txwin_setup(struct l2cap_chan *chan) -{ - if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && - __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); - chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW; - } -} - -static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) -{ - struct l2cap_conf_req *req = data; - struct l2cap_conf_rfc rfc = { .mode = chan->mode }; - void *ptr = req->data; - u16 size; - - BT_DBG("chan %p", chan); - - if (chan->num_conf_req || chan->num_conf_rsp) - goto done; - - switch (chan->mode) { - case L2CAP_MODE_STREAMING: - case L2CAP_MODE_ERTM: - if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) - break; - - if (__l2cap_efs_supported(chan)) - set_bit(FLAG_EFS_ENABLE, &chan->flags); - - /* fall through */ - default: - chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask); - break; - } - -done: - if (chan->imtu != L2CAP_DEFAULT_MTU) - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu); - - switch (chan->mode) { - case L2CAP_MODE_BASIC: - if (!(chan->conn->feat_mask & L2CAP_FEAT_ERTM) && - !(chan->conn->feat_mask & L2CAP_FEAT_STREAMING)) - break; - - rfc.mode = L2CAP_MODE_BASIC; - rfc.txwin_size = 0; - rfc.max_transmit = 0; - rfc.retrans_timeout = 0; - rfc.monitor_timeout = 0; - rfc.max_pdu_size = 0; - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) &rfc); - break; - - case L2CAP_MODE_ERTM: - rfc.mode = L2CAP_MODE_ERTM; - rfc.max_transmit = chan->max_tx; - rfc.retrans_timeout = 0; - 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); - 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_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) &rfc); - - if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) - l2cap_add_opt_efs(&ptr, chan); - - if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) - break; - - if (chan->fcs == L2CAP_FCS_NONE || - 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); - break; - - case L2CAP_MODE_STREAMING: - rfc.mode = L2CAP_MODE_STREAMING; - rfc.txwin_size = 0; - rfc.max_transmit = 0; - rfc.retrans_timeout = 0; - 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); - rfc.max_pdu_size = cpu_to_le16(size); - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) &rfc); - - if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) - l2cap_add_opt_efs(&ptr, chan); - - if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) - break; - - if (chan->fcs == L2CAP_FCS_NONE || - 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); - } - break; - } - - req->dcid = cpu_to_le16(chan->dcid); - req->flags = cpu_to_le16(0); - - return ptr - data; -} - -static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) -{ - struct l2cap_conf_rsp *rsp = data; - void *ptr = rsp->data; - void *req = chan->conf_req; - int len = chan->conf_len; - int type, hint, olen; - unsigned long val; - struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; - struct l2cap_conf_efs efs; - u8 remote_efs = 0; - u16 mtu = L2CAP_DEFAULT_MTU; - u16 result = L2CAP_CONF_SUCCESS; - u16 size; - - BT_DBG("chan %p", chan); - - while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&req, &type, &olen, &val); - - hint = type & L2CAP_CONF_HINT; - type &= L2CAP_CONF_MASK; - - switch (type) { - case L2CAP_CONF_MTU: - mtu = val; - break; - - case L2CAP_CONF_FLUSH_TO: - chan->flush_to = val; - break; - - case L2CAP_CONF_QOS: - break; - - case L2CAP_CONF_RFC: - if (olen == sizeof(rfc)) - memcpy(&rfc, (void *) val, olen); - break; - - case L2CAP_CONF_FCS: - if (val == L2CAP_FCS_NONE) - set_bit(CONF_NO_FCS_RECV, &chan->conf_state); - break; - - case L2CAP_CONF_EFS: - remote_efs = 1; - if (olen == sizeof(efs)) - memcpy(&efs, (void *) val, olen); - break; - - case L2CAP_CONF_EWS: - if (!enable_hs) - return -ECONNREFUSED; - - set_bit(FLAG_EXT_CTRL, &chan->flags); - set_bit(CONF_EWS_RECV, &chan->conf_state); - chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; - chan->remote_tx_win = val; - break; - - default: - if (hint) - break; - - result = L2CAP_CONF_UNKNOWN; - *((u8 *) ptr++) = type; - break; - } - } - - if (chan->num_conf_rsp || chan->num_conf_req > 1) - goto done; - - switch (chan->mode) { - case L2CAP_MODE_STREAMING: - case L2CAP_MODE_ERTM: - if (!test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) { - chan->mode = l2cap_select_mode(rfc.mode, - chan->conn->feat_mask); - break; - } - - if (remote_efs) { - if (__l2cap_efs_supported(chan)) - set_bit(FLAG_EFS_ENABLE, &chan->flags); - else - return -ECONNREFUSED; - } - - if (chan->mode != rfc.mode) - return -ECONNREFUSED; - - break; - } - -done: - if (chan->mode != rfc.mode) { - result = L2CAP_CONF_UNACCEPT; - rfc.mode = chan->mode; - - if (chan->num_conf_rsp == 1) - return -ECONNREFUSED; - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); - } - - if (result == L2CAP_CONF_SUCCESS) { - /* Configure output options and let the other side know - * which ones we don't like. */ - - if (mtu < L2CAP_DEFAULT_MIN_MTU) - result = L2CAP_CONF_UNACCEPT; - else { - chan->omtu = mtu; - set_bit(CONF_MTU_DONE, &chan->conf_state); - } - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu); - - if (remote_efs) { - if (chan->local_stype != L2CAP_SERV_NOTRAFIC && - efs.stype != L2CAP_SERV_NOTRAFIC && - efs.stype != chan->local_stype) { - - result = L2CAP_CONF_UNACCEPT; - - if (chan->num_conf_req >= 1) - return -ECONNREFUSED; - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, - sizeof(efs), - (unsigned long) &efs); - } else { - /* Send PENDING Conf Rsp */ - result = L2CAP_CONF_PENDING; - set_bit(CONF_LOC_CONF_PEND, &chan->conf_state); - } - } - - switch (rfc.mode) { - case L2CAP_MODE_BASIC: - chan->fcs = L2CAP_FCS_NONE; - set_bit(CONF_MODE_DONE, &chan->conf_state); - break; - - case L2CAP_MODE_ERTM: - if (!test_bit(CONF_EWS_RECV, &chan->conf_state)) - chan->remote_tx_win = rfc.txwin_size; - else - rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW; - - 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); - rfc.max_pdu_size = cpu_to_le16(size); - chan->remote_mps = size; - - rfc.retrans_timeout = - le16_to_cpu(L2CAP_DEFAULT_RETRANS_TO); - rfc.monitor_timeout = - le16_to_cpu(L2CAP_DEFAULT_MONITOR_TO); - - set_bit(CONF_MODE_DONE, &chan->conf_state); - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_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); - chan->remote_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); - } - 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); - 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); - - break; - - default: - result = L2CAP_CONF_UNACCEPT; - - memset(&rfc, 0, sizeof(rfc)); - rfc.mode = chan->mode; - } - - if (result == L2CAP_CONF_SUCCESS) - set_bit(CONF_OUTPUT_DONE, &chan->conf_state); - } - rsp->scid = cpu_to_le16(chan->dcid); - rsp->result = cpu_to_le16(result); - rsp->flags = cpu_to_le16(0x0000); - - return ptr - data; -} - -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; - int type, olen; - unsigned long val; - struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; - struct l2cap_conf_efs efs; - - BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data); - - while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); - - switch (type) { - case L2CAP_CONF_MTU: - if (val < L2CAP_DEFAULT_MIN_MTU) { - *result = L2CAP_CONF_UNACCEPT; - chan->imtu = L2CAP_DEFAULT_MIN_MTU; - } else - chan->imtu = val; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu); - break; - - case L2CAP_CONF_FLUSH_TO: - chan->flush_to = val; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, - 2, chan->flush_to); - break; - - case L2CAP_CONF_RFC: - if (olen == sizeof(rfc)) - memcpy(&rfc, (void *)val, olen); - - if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) && - rfc.mode != chan->mode) - return -ECONNREFUSED; - - chan->fcs = 0; - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); - break; - - case L2CAP_CONF_EWS: - chan->tx_win = min_t(u16, val, - L2CAP_DEFAULT_EXT_WINDOW); - l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, - chan->tx_win); - break; - - case L2CAP_CONF_EFS: - if (olen == sizeof(efs)) - memcpy(&efs, (void *)val, olen); - - if (chan->local_stype != L2CAP_SERV_NOTRAFIC && - 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); - break; - } - } - - if (chan->mode == L2CAP_MODE_BASIC && chan->mode != rfc.mode) - return -ECONNREFUSED; - - chan->mode = rfc.mode; - - if (*result == L2CAP_CONF_SUCCESS || *result == L2CAP_CONF_PENDING) { - switch (rfc.mode) { - case L2CAP_MODE_ERTM: - chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); - chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); - chan->mps = le16_to_cpu(rfc.max_pdu_size); - - 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); - chan->local_acc_lat = le32_to_cpu(efs.acc_lat); - chan->local_flush_to = - le32_to_cpu(efs.flush_to); - } - break; - - case L2CAP_MODE_STREAMING: - chan->mps = le16_to_cpu(rfc.max_pdu_size); - } - } - - req->dcid = cpu_to_le16(chan->dcid); - req->flags = cpu_to_le16(0x0000); - - return ptr - data; -} - -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; - - BT_DBG("chan %p", chan); - - rsp->scid = cpu_to_le16(chan->dcid); - rsp->result = cpu_to_le16(result); - rsp->flags = cpu_to_le16(flags); - - return ptr - data; -} - -void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) -{ - struct l2cap_conn_rsp rsp; - struct l2cap_conn *conn = chan->conn; - u8 buf[128]; - - rsp.scid = cpu_to_le16(chan->dcid); - rsp.dcid = cpu_to_le16(chan->scid); - rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - 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); - chan->num_conf_req++; -} - -static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) -{ - int type, olen; - unsigned long val; - struct l2cap_conf_rfc rfc; - - BT_DBG("chan %p, rsp %p, len %d", chan, rsp, len); - - if ((chan->mode != L2CAP_MODE_ERTM) && (chan->mode != L2CAP_MODE_STREAMING)) - return; - - while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); - - switch (type) { - case L2CAP_CONF_RFC: - if (olen == sizeof(rfc)) - memcpy(&rfc, (void *)val, olen); - goto done; - } - } - - /* Use sane default values in case a misbehaving remote device - * did not send an RFC option. - */ - rfc.mode = chan->mode; - rfc.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); - rfc.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); - rfc.max_pdu_size = cpu_to_le16(chan->imtu); - - BT_ERR("Expected RFC option was not found, using defaults"); - -done: - switch (rfc.mode) { - case L2CAP_MODE_ERTM: - chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); - chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); - chan->mps = le16_to_cpu(rfc.max_pdu_size); - break; - case L2CAP_MODE_STREAMING: - chan->mps = le16_to_cpu(rfc.max_pdu_size); - } -} - -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; - - if (rej->reason != L2CAP_REJ_NOT_UNDERSTOOD) - return 0; - - if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) && - cmd->ident == conn->info_ident) { - cancel_delayed_work(&conn->info_timer); - - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; - conn->info_ident = 0; - - l2cap_conn_start(conn); - } - - return 0; -} - -static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) -{ - struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; - struct l2cap_conn_rsp rsp; - struct l2cap_chan *chan = NULL, *pchan; - struct sock *parent, *sk = NULL; - int result, status = L2CAP_CS_NO_INFO; - - u16 dcid = 0, scid = __le16_to_cpu(req->scid); - __le16 psm = req->psm; - - BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid); - - /* Check if we have socket listening on psm */ - pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src); - if (!pchan) { - result = L2CAP_CR_BAD_PSM; - goto sendresp; - } - - parent = pchan->sk; - - mutex_lock(&conn->chan_lock); - lock_sock(parent); - - /* Check if the ACL is secure enough (if not SDP) */ - if (psm != cpu_to_le16(0x0001) && - !hci_conn_check_link_mode(conn->hcon)) { - conn->disc_reason = HCI_ERROR_AUTH_FAILURE; - result = L2CAP_CR_SEC_BLOCK; - goto response; - } - - result = L2CAP_CR_NO_MEM; - - /* Check for backlog size */ - if (sk_acceptq_is_full(parent)) { - BT_DBG("backlog full %d", parent->sk_ack_backlog); - goto response; - } - - chan = pchan->ops->new_connection(pchan->data); - if (!chan) - goto response; - - sk = chan->sk; - - /* Check if we already have channel with that dcid */ - if (__l2cap_get_chan_by_dcid(conn, scid)) { - sock_set_flag(sk, SOCK_ZAPPED); - chan->ops->close(chan->data); - goto response; - } - - hci_conn_hold(conn->hcon); - - bacpy(&bt_sk(sk)->src, conn->src); - bacpy(&bt_sk(sk)->dst, conn->dst); - chan->psm = psm; - chan->dcid = scid; - - bt_accept_enqueue(parent, sk); - - __l2cap_chan_add(conn, chan); - - dcid = chan->scid; - - __set_chan_timer(chan, sk->sk_sndtimeo); - - chan->ident = cmd->ident; - - if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) { - if (l2cap_chan_check_security(chan)) { - if (bt_sk(sk)->defer_setup) { - __l2cap_state_change(chan, BT_CONNECT2); - result = L2CAP_CR_PEND; - status = L2CAP_CS_AUTHOR_PEND; - parent->sk_data_ready(parent, 0); - } else { - __l2cap_state_change(chan, BT_CONFIG); - result = L2CAP_CR_SUCCESS; - status = L2CAP_CS_NO_INFO; - } - } else { - __l2cap_state_change(chan, BT_CONNECT2); - result = L2CAP_CR_PEND; - status = L2CAP_CS_AUTHEN_PEND; - } - } else { - __l2cap_state_change(chan, BT_CONNECT2); - result = L2CAP_CR_PEND; - status = L2CAP_CS_NO_INFO; - } - -response: - release_sock(parent); - mutex_unlock(&conn->chan_lock); - -sendresp: - rsp.scid = cpu_to_le16(scid); - 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); - - if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) { - struct l2cap_info_req info; - info.type = cpu_to_le16(L2CAP_IT_FEAT_MASK); - - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT; - conn->info_ident = l2cap_get_ident(conn); - - schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT); - - 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) { - 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); - chan->num_conf_req++; - } - - return 0; -} - -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; - struct l2cap_chan *chan; - u8 req[128]; - int err; - - scid = __le16_to_cpu(rsp->scid); - dcid = __le16_to_cpu(rsp->dcid); - result = __le16_to_cpu(rsp->result); - 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); - - mutex_lock(&conn->chan_lock); - - if (scid) { - chan = __l2cap_get_chan_by_scid(conn, scid); - if (!chan) { - err = -EFAULT; - goto unlock; - } - } else { - chan = __l2cap_get_chan_by_ident(conn, cmd->ident); - if (!chan) { - err = -EFAULT; - goto unlock; - } - } - - err = 0; - - l2cap_chan_lock(chan); - - switch (result) { - case L2CAP_CR_SUCCESS: - l2cap_state_change(chan, BT_CONFIG); - chan->ident = 0; - chan->dcid = dcid; - clear_bit(CONF_CONNECT_PEND, &chan->conf_state); - - if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) - break; - - l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, req), req); - chan->num_conf_req++; - break; - - case L2CAP_CR_PEND: - set_bit(CONF_CONNECT_PEND, &chan->conf_state); - break; - - default: - l2cap_chan_del(chan, ECONNREFUSED); - break; - } - - l2cap_chan_unlock(chan); - -unlock: - mutex_unlock(&conn->chan_lock); - - return err; -} - -static inline void set_default_fcs(struct l2cap_chan *chan) -{ - /* FCS is enabled only in ERTM or streaming mode, if one or both - * sides request it. - */ - if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING) - chan->fcs = L2CAP_FCS_NONE; - else if (!test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) - 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) -{ - struct l2cap_conf_req *req = (struct l2cap_conf_req *) data; - u16 dcid, flags; - u8 rsp[64]; - struct l2cap_chan *chan; - int len; - - dcid = __le16_to_cpu(req->dcid); - flags = __le16_to_cpu(req->flags); - - BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags); - - chan = l2cap_get_chan_by_scid(conn, dcid); - if (!chan) - return -ENOENT; - - if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) { - struct l2cap_cmd_rej_cid rej; - - rej.reason = cpu_to_le16(L2CAP_REJ_INVALID_CID); - rej.scid = cpu_to_le16(chan->scid); - rej.dcid = cpu_to_le16(chan->dcid); - - l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ, - sizeof(rej), &rej); - goto unlock; - } - - /* Reject if config buffer is too small. */ - 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); - goto unlock; - } - - /* Store config. */ - memcpy(chan->conf_req + chan->conf_len, req->data, len); - chan->conf_len += len; - - if (flags & 0x0001) { - /* Incomplete config. Send empty response. */ - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, rsp, - L2CAP_CONF_SUCCESS, 0x0001), rsp); - goto unlock; - } - - /* Complete config. */ - len = l2cap_parse_conf_req(chan, rsp); - if (len < 0) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); - goto unlock; - } - - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp); - chan->num_conf_rsp++; - - /* Reset config buffer. */ - chan->conf_len = 0; - - if (!test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) - goto unlock; - - if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) { - set_default_fcs(chan); - - l2cap_state_change(chan, BT_CONNECTED); - - chan->next_tx_seq = 0; - chan->expected_tx_seq = 0; - skb_queue_head_init(&chan->tx_q); - if (chan->mode == L2CAP_MODE_ERTM) - l2cap_ertm_init(chan); - - l2cap_chan_ready(chan); - goto unlock; - } - - 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); - 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)) { - - /* 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, 0x0000), rsp); - } - -unlock: - l2cap_chan_unlock(chan); - return 0; -} - -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; - struct l2cap_chan *chan; - int len = cmd->len - sizeof(*rsp); - - scid = __le16_to_cpu(rsp->scid); - flags = __le16_to_cpu(rsp->flags); - result = __le16_to_cpu(rsp->result); - - BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", - scid, flags, result); - - chan = l2cap_get_chan_by_scid(conn, scid); - if (!chan) - return 0; - - switch (result) { - case L2CAP_CONF_SUCCESS: - l2cap_conf_rfc_get(chan, rsp->data, len); - clear_bit(CONF_REM_CONF_PEND, &chan->conf_state); - break; - - case L2CAP_CONF_PENDING: - set_bit(CONF_REM_CONF_PEND, &chan->conf_state); - - if (test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { - char buf[64]; - - len = l2cap_parse_conf_rsp(chan, rsp->data, len, - buf, &result); - if (len < 0) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); - goto done; - } - - /* 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); - } - goto done; - - case L2CAP_CONF_UNACCEPT: - if (chan->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) { - char req[64]; - - if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); - goto done; - } - - /* throw out any old stored conf requests */ - result = L2CAP_CONF_SUCCESS; - len = l2cap_parse_conf_rsp(chan, rsp->data, len, - 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); - chan->num_conf_req++; - if (result != L2CAP_CONF_SUCCESS) - goto done; - break; - } - - default: - l2cap_chan_set_err(chan, ECONNRESET); - - __set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT); - l2cap_send_disconn_req(conn, chan, ECONNRESET); - goto done; - } - - if (flags & 0x01) - goto done; - - set_bit(CONF_INPUT_DONE, &chan->conf_state); - - if (test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) { - set_default_fcs(chan); - - l2cap_state_change(chan, BT_CONNECTED); - chan->next_tx_seq = 0; - chan->expected_tx_seq = 0; - skb_queue_head_init(&chan->tx_q); - if (chan->mode == L2CAP_MODE_ERTM) - l2cap_ertm_init(chan); - - l2cap_chan_ready(chan); - } - -done: - l2cap_chan_unlock(chan); - return 0; -} - -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; - u16 dcid, scid; - struct l2cap_chan *chan; - struct sock *sk; - - scid = __le16_to_cpu(req->scid); - dcid = __le16_to_cpu(req->dcid); - - BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid); - - mutex_lock(&conn->chan_lock); - - chan = __l2cap_get_chan_by_scid(conn, dcid); - if (!chan) { - mutex_unlock(&conn->chan_lock); - return 0; - } - - l2cap_chan_lock(chan); - - sk = chan->sk; - - rsp.dcid = cpu_to_le16(chan->scid); - rsp.scid = cpu_to_le16(chan->dcid); - l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp); - - lock_sock(sk); - sk->sk_shutdown = SHUTDOWN_MASK; - release_sock(sk); - - l2cap_chan_del(chan, ECONNRESET); - - l2cap_chan_unlock(chan); - - chan->ops->close(chan->data); - - mutex_unlock(&conn->chan_lock); - - return 0; -} - -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; - struct l2cap_chan *chan; - - scid = __le16_to_cpu(rsp->scid); - dcid = __le16_to_cpu(rsp->dcid); - - BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid); - - mutex_lock(&conn->chan_lock); - - chan = __l2cap_get_chan_by_scid(conn, scid); - if (!chan) { - mutex_unlock(&conn->chan_lock); - return 0; - } - - l2cap_chan_lock(chan); - - l2cap_chan_del(chan, 0); - - l2cap_chan_unlock(chan); - - chan->ops->close(chan->data); - - mutex_unlock(&conn->chan_lock); - - return 0; -} - -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; - - type = __le16_to_cpu(req->type); - - BT_DBG("type 0x%4.4x", type); - - if (type == L2CAP_IT_FEAT_MASK) { - u8 buf[8]; - u32 feat_mask = l2cap_feat_mask; - struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; - rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK); - rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); - if (!disable_ertm) - feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING - | L2CAP_FEAT_FCS; - if (enable_hs) - feat_mask |= L2CAP_FEAT_EXT_FLOW - | L2CAP_FEAT_EXT_WINDOW; - - put_unaligned_le32(feat_mask, rsp->data); - 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; - - if (enable_hs) - l2cap_fixed_chan[0] |= L2CAP_FC_A2MP; - else - l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP; - - rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); - rsp->result = 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); - } else { - struct l2cap_info_rsp rsp; - rsp.type = cpu_to_le16(type); - rsp.result = cpu_to_le16(L2CAP_IR_NOTSUPP); - 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) -{ - struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) data; - u16 type, result; - - type = __le16_to_cpu(rsp->type); - result = __le16_to_cpu(rsp->result); - - BT_DBG("type 0x%4.4x result 0x%2.2x", type, result); - - /* 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) - return 0; - - cancel_delayed_work(&conn->info_timer); - - if (result != L2CAP_IR_SUCCESS) { - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; - conn->info_ident = 0; - - l2cap_conn_start(conn); - - return 0; - } - - switch (type) { - case L2CAP_IT_FEAT_MASK: - conn->feat_mask = get_unaligned_le32(rsp->data); - - if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) { - struct l2cap_info_req req; - req.type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); - - conn->info_ident = l2cap_get_ident(conn); - - l2cap_send_cmd(conn, conn->info_ident, - L2CAP_INFO_REQ, sizeof(req), &req); - } else { - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; - conn->info_ident = 0; - - l2cap_conn_start(conn); - } - break; - - case L2CAP_IT_FIXED_CHAN: - conn->fixed_chan_mask = rsp->data[0]; - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; - conn->info_ident = 0; - - l2cap_conn_start(conn); - break; - } - - return 0; -} - -static inline int l2cap_create_channel_req(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, - void *data) -{ - struct l2cap_create_chan_req *req = data; - struct l2cap_create_chan_rsp rsp; - u16 psm, scid; - - if (cmd_len != sizeof(*req)) - return -EPROTO; - - if (!enable_hs) - return -EINVAL; - - psm = le16_to_cpu(req->psm); - scid = le16_to_cpu(req->scid); - - BT_DBG("psm %d, scid %d, amp_id %d", psm, scid, req->amp_id); - - /* Placeholder: Always reject */ - rsp.dcid = 0; - rsp.scid = cpu_to_le16(scid); - rsp.result = L2CAP_CR_NO_MEM; - rsp.status = L2CAP_CS_NO_INFO; - - l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP, - sizeof(rsp), &rsp); - - return 0; -} - -static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, void *data) -{ - BT_DBG("conn %p", conn); - - return l2cap_connect_rsp(conn, cmd, data); -} - -static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, - u16 icid, u16 result) -{ - struct l2cap_move_chan_rsp rsp; - - BT_DBG("icid %d, result %d", icid, result); - - rsp.icid = cpu_to_le16(icid); - rsp.result = cpu_to_le16(result); - - l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp); -} - -static void l2cap_send_move_chan_cfm(struct l2cap_conn *conn, - struct l2cap_chan *chan, u16 icid, u16 result) -{ - struct l2cap_move_chan_cfm cfm; - u8 ident; - - BT_DBG("icid %d, result %d", icid, result); - - ident = l2cap_get_ident(conn); - if (chan) - chan->ident = ident; - - cfm.icid = cpu_to_le16(icid); - cfm.result = cpu_to_le16(result); - - l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM, sizeof(cfm), &cfm); -} - -static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident, - u16 icid) -{ - struct l2cap_move_chan_cfm_rsp rsp; - - BT_DBG("icid %d", icid); - - rsp.icid = cpu_to_le16(icid); - l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp); -} - -static inline int l2cap_move_channel_req(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) -{ - struct l2cap_move_chan_req *req = data; - u16 icid = 0; - u16 result = L2CAP_MR_NOT_ALLOWED; - - if (cmd_len != sizeof(*req)) - return -EPROTO; - - icid = le16_to_cpu(req->icid); - - BT_DBG("icid %d, dest_amp_id %d", icid, req->dest_amp_id); - - if (!enable_hs) - return -EINVAL; - - /* Placeholder: Always refuse */ - l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result); - - return 0; -} - -static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) -{ - struct l2cap_move_chan_rsp *rsp = data; - u16 icid, result; - - if (cmd_len != sizeof(*rsp)) - return -EPROTO; - - icid = le16_to_cpu(rsp->icid); - result = le16_to_cpu(rsp->result); - - BT_DBG("icid %d, result %d", icid, result); - - /* Placeholder: Always unconfirmed */ - l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED); - - return 0; -} - -static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) -{ - struct l2cap_move_chan_cfm *cfm = data; - u16 icid, result; - - if (cmd_len != sizeof(*cfm)) - return -EPROTO; - - icid = le16_to_cpu(cfm->icid); - result = le16_to_cpu(cfm->result); - - BT_DBG("icid %d, result %d", icid, result); - - l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid); - - return 0; -} - -static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) -{ - struct l2cap_move_chan_cfm_rsp *rsp = data; - u16 icid; - - if (cmd_len != sizeof(*rsp)) - return -EPROTO; - - icid = le16_to_cpu(rsp->icid); - - BT_DBG("icid %d", icid); - - return 0; -} - -static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency, - u16 to_multiplier) -{ - u16 max_latency; - - if (min > max || min < 6 || max > 3200) - return -EINVAL; - - if (to_multiplier < 10 || to_multiplier > 3200) - return -EINVAL; - - if (max >= to_multiplier * 8) - return -EINVAL; - - max_latency = (to_multiplier * 8 / max) - 1; - if (latency > 499 || latency > max_latency) - return -EINVAL; - - return 0; -} - -static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u8 *data) -{ - struct hci_conn *hcon = conn->hcon; - struct l2cap_conn_param_update_req *req; - struct l2cap_conn_param_update_rsp rsp; - u16 min, max, latency, to_multiplier, cmd_len; - int err; - - if (!(hcon->link_mode & HCI_LM_MASTER)) - return -EINVAL; - - cmd_len = __le16_to_cpu(cmd->len); - if (cmd_len != sizeof(struct l2cap_conn_param_update_req)) - return -EPROTO; - - req = (struct l2cap_conn_param_update_req *) data; - min = __le16_to_cpu(req->min); - max = __le16_to_cpu(req->max); - latency = __le16_to_cpu(req->latency); - 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); - - memset(&rsp, 0, sizeof(rsp)); - - err = l2cap_check_conn_param(min, max, latency, to_multiplier); - if (err) - rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED); - else - rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED); - - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP, - sizeof(rsp), &rsp); - - if (!err) - hci_le_conn_update(hcon, min, max, latency, to_multiplier); - - return 0; -} - -static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) -{ - int err = 0; - - switch (cmd->code) { - case L2CAP_COMMAND_REJ: - l2cap_command_rej(conn, cmd, data); - break; - - case L2CAP_CONN_REQ: - err = l2cap_connect_req(conn, cmd, data); - break; - - case L2CAP_CONN_RSP: - err = l2cap_connect_rsp(conn, cmd, data); - break; - - case L2CAP_CONF_REQ: - err = l2cap_config_req(conn, cmd, cmd_len, data); - break; - - case L2CAP_CONF_RSP: - err = l2cap_config_rsp(conn, cmd, data); - break; - - case L2CAP_DISCONN_REQ: - err = l2cap_disconnect_req(conn, cmd, data); - break; - - case L2CAP_DISCONN_RSP: - err = l2cap_disconnect_rsp(conn, cmd, data); - break; - - case L2CAP_ECHO_REQ: - l2cap_send_cmd(conn, cmd->ident, L2CAP_ECHO_RSP, cmd_len, data); - break; - - case L2CAP_ECHO_RSP: - break; - - case L2CAP_INFO_REQ: - err = l2cap_information_req(conn, cmd, data); - break; - - case L2CAP_INFO_RSP: - err = l2cap_information_rsp(conn, cmd, data); - break; - - case L2CAP_CREATE_CHAN_REQ: - 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; - - case L2CAP_MOVE_CHAN_RSP: - err = l2cap_move_channel_rsp(conn, cmd, cmd_len, data); - break; - - case L2CAP_MOVE_CHAN_CFM: - err = l2cap_move_channel_confirm(conn, cmd, cmd_len, data); - break; - - case L2CAP_MOVE_CHAN_CFM_RSP: - err = l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data); - break; - - default: - BT_ERR("Unknown BR/EDR signaling command 0x%2.2x", cmd->code); - err = -EINVAL; - break; - } - - return err; -} - -static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u8 *data) -{ - switch (cmd->code) { - case L2CAP_COMMAND_REJ: - return 0; - - case L2CAP_CONN_PARAM_UPDATE_REQ: - return l2cap_conn_param_update_req(conn, cmd, data); - - case L2CAP_CONN_PARAM_UPDATE_RSP: - return 0; - - default: - BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code); - return -EINVAL; - } -} - -static inline void l2cap_sig_channel(struct l2cap_conn *conn, - struct sk_buff *skb) -{ - u8 *data = skb->data; - int len = skb->len; - struct l2cap_cmd_hdr cmd; - int err; - - l2cap_raw_recv(conn, skb); - - while (len >= L2CAP_CMD_HDR_SIZE) { - u16 cmd_len; - memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE); - data += L2CAP_CMD_HDR_SIZE; - len -= L2CAP_CMD_HDR_SIZE; - - 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); - - if (cmd_len > len || !cmd.ident) { - BT_DBG("corrupted command"); - break; - } - - if (conn->hcon->type == LE_LINK) - err = l2cap_le_sig_cmd(conn, &cmd, data); - else - err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data); - - if (err) { - struct l2cap_cmd_rej_unk rej; - - BT_ERR("Wrong link type (%d)", err); - - /* FIXME: Map err to a valid reason */ - rej.reason = cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD); - l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); - } - - data += cmd_len; - len -= cmd_len; - } - - kfree_skb(skb); -} - -static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb) -{ - u16 our_fcs, rcv_fcs; - int hdr_size; - - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - hdr_size = L2CAP_EXT_HDR_SIZE; - else - hdr_size = L2CAP_ENH_HDR_SIZE; - - if (chan->fcs == L2CAP_FCS_CRC16) { - skb_trim(skb, skb->len - L2CAP_FCS_SIZE); - rcv_fcs = get_unaligned_le16(skb->data + skb->len); - our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size); - - if (our_fcs != rcv_fcs) - return -EBADMSG; - } - return 0; -} - -static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) -{ - u32 control = 0; - - chan->frames_sent = 0; - - control |= __set_reqseq(chan, chan->buffer_seq); - - if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); - l2cap_send_sframe(chan, control); - set_bit(CONN_RNR_SENT, &chan->conn_state); - } - - if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) - l2cap_retransmit_frames(chan); - - l2cap_ertm_send(chan); - - if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) && - chan->frames_sent == 0) { - control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); - l2cap_send_sframe(chan, control); - } -} - -static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u16 tx_seq, u8 sar) -{ - struct sk_buff *next_skb; - int tx_seq_offset, next_tx_seq_offset; - - bt_cb(skb)->tx_seq = tx_seq; - bt_cb(skb)->sar = sar; - - next_skb = skb_peek(&chan->srej_q); - - tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq); - - while (next_skb) { - if (bt_cb(next_skb)->tx_seq == tx_seq) - return -EINVAL; - - next_tx_seq_offset = __seq_offset(chan, - bt_cb(next_skb)->tx_seq, chan->buffer_seq); - - if (next_tx_seq_offset > tx_seq_offset) { - __skb_queue_before(&chan->srej_q, next_skb, skb); - return 0; - } - - if (skb_queue_is_last(&chan->srej_q, next_skb)) - next_skb = NULL; - else - next_skb = skb_queue_next(&chan->srej_q, next_skb); - } - - __skb_queue_tail(&chan->srej_q, skb); - - return 0; -} - -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 - */ - if (!skb_has_frag_list(skb)) - skb_shinfo(skb)->frag_list = new_frag; - - new_frag->next = NULL; - - (*last_frag)->next = new_frag; - *last_frag = new_frag; - - skb->len += new_frag->len; - skb->data_len += new_frag->len; - skb->truesize += new_frag->truesize; -} - -static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u32 control) -{ - int err = -EINVAL; - - switch (__get_ctrl_sar(chan, control)) { - case L2CAP_SAR_UNSEGMENTED: - if (chan->sdu) - break; - - err = chan->ops->recv(chan->data, skb); - break; - - case L2CAP_SAR_START: - if (chan->sdu) - break; - - chan->sdu_len = get_unaligned_le16(skb->data); - skb_pull(skb, L2CAP_SDULEN_SIZE); - - if (chan->sdu_len > chan->imtu) { - err = -EMSGSIZE; - break; - } - - if (skb->len >= chan->sdu_len) - break; - - chan->sdu = skb; - chan->sdu_last_frag = skb; - - skb = NULL; - err = 0; - break; - - case L2CAP_SAR_CONTINUE: - if (!chan->sdu) - break; - - append_skb_frag(chan->sdu, skb, - &chan->sdu_last_frag); - skb = NULL; - - if (chan->sdu->len >= chan->sdu_len) - break; - - err = 0; - break; - - case L2CAP_SAR_END: - if (!chan->sdu) - break; - - append_skb_frag(chan->sdu, skb, - &chan->sdu_last_frag); - skb = NULL; - - if (chan->sdu->len != chan->sdu_len) - break; - - err = chan->ops->recv(chan->data, chan->sdu); - - if (!err) { - /* Reassembly complete */ - chan->sdu = NULL; - chan->sdu_last_frag = NULL; - chan->sdu_len = 0; - } - break; - } - - if (err) { - kfree_skb(skb); - kfree_skb(chan->sdu); - chan->sdu = NULL; - chan->sdu_last_frag = NULL; - chan->sdu_len = 0; - } - - return err; -} - -static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) -{ - BT_DBG("chan %p, Enter local busy", chan); - - set_bit(CONN_LOCAL_BUSY, &chan->conn_state); - - __set_ack_timer(chan); -} - -static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan) -{ - u32 control; - - if (!test_bit(CONN_RNR_SENT, &chan->conn_state)) - goto done; - - control = __set_reqseq(chan, chan->buffer_seq); - control |= __set_ctrl_poll(chan); - control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); - l2cap_send_sframe(chan, control); - chan->retry_count = 1; - - __clear_retrans_timer(chan); - __set_monitor_timer(chan); - - set_bit(CONN_WAIT_F, &chan->conn_state); - -done: - clear_bit(CONN_LOCAL_BUSY, &chan->conn_state); - clear_bit(CONN_RNR_SENT, &chan->conn_state); - - BT_DBG("chan %p, Exit local busy", chan); -} - -void l2cap_chan_busy(struct l2cap_chan *chan, int busy) -{ - if (chan->mode == L2CAP_MODE_ERTM) { - if (busy) - l2cap_ertm_enter_local_busy(chan); - else - l2cap_ertm_exit_local_busy(chan); - } -} - -static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq) -{ - struct sk_buff *skb; - u32 control; - - while ((skb = skb_peek(&chan->srej_q)) && - !test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - int err; - - if (bt_cb(skb)->tx_seq != tx_seq) - break; - - skb = skb_dequeue(&chan->srej_q); - control = __set_ctrl_sar(chan, bt_cb(skb)->sar); - err = l2cap_reassemble_sdu(chan, skb, control); - - if (err < 0) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - break; - } - - chan->buffer_seq_srej = __next_seq(chan, chan->buffer_seq_srej); - tx_seq = __next_seq(chan, tx_seq); - } -} - -static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq) -{ - struct srej_list *l, *tmp; - u32 control; - - list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { - if (l->tx_seq == tx_seq) { - list_del(&l->list); - kfree(l); - return; - } - control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); - control |= __set_reqseq(chan, l->tx_seq); - l2cap_send_sframe(chan, control); - list_del(&l->list); - list_add_tail(&l->list, &chan->srej_l); - } -} - -static int l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq) -{ - struct srej_list *new; - u32 control; - - while (tx_seq != chan->expected_tx_seq) { - control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); - control |= __set_reqseq(chan, chan->expected_tx_seq); - l2cap_send_sframe(chan, control); - - new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); - if (!new) - return -ENOMEM; - - new->tx_seq = chan->expected_tx_seq; - - chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); - - list_add_tail(&new->list, &chan->srej_l); - } - - chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); - - return 0; -} - -static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb) -{ - u16 tx_seq = __get_txseq(chan, rx_control); - u16 req_seq = __get_reqseq(chan, rx_control); - u8 sar = __get_ctrl_sar(chan, rx_control); - int tx_seq_offset, expected_tx_seq_offset; - int num_to_ack = (chan->tx_win/6) + 1; - int err = 0; - - BT_DBG("chan %p len %d tx_seq %d rx_control 0x%8.8x", chan, skb->len, - tx_seq, rx_control); - - if (__is_ctrl_final(chan, rx_control) && - test_bit(CONN_WAIT_F, &chan->conn_state)) { - __clear_monitor_timer(chan); - if (chan->unacked_frames > 0) - __set_retrans_timer(chan); - clear_bit(CONN_WAIT_F, &chan->conn_state); - } - - chan->expected_ack_seq = req_seq; - l2cap_drop_acked_frames(chan); - - tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq); - - /* invalid tx_seq */ - if (tx_seq_offset >= chan->tx_win) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - goto drop; - } - - if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - if (!test_bit(CONN_RNR_SENT, &chan->conn_state)) - l2cap_send_ack(chan); - goto drop; - } - - if (tx_seq == chan->expected_tx_seq) - goto expected; - - if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { - struct srej_list *first; - - first = list_first_entry(&chan->srej_l, - struct srej_list, list); - if (tx_seq == first->tx_seq) { - l2cap_add_to_srej_queue(chan, skb, tx_seq, sar); - l2cap_check_srej_gap(chan, tx_seq); - - list_del(&first->list); - kfree(first); - - if (list_empty(&chan->srej_l)) { - chan->buffer_seq = chan->buffer_seq_srej; - clear_bit(CONN_SREJ_SENT, &chan->conn_state); - l2cap_send_ack(chan); - BT_DBG("chan %p, Exit SREJ_SENT", chan); - } - } else { - struct srej_list *l; - - /* duplicated tx_seq */ - if (l2cap_add_to_srej_queue(chan, skb, tx_seq, sar) < 0) - goto drop; - - list_for_each_entry(l, &chan->srej_l, list) { - if (l->tx_seq == tx_seq) { - l2cap_resend_srejframe(chan, tx_seq); - return 0; - } - } - - err = l2cap_send_srejframe(chan, tx_seq); - if (err < 0) { - l2cap_send_disconn_req(chan->conn, chan, -err); - return err; - } - } - } else { - expected_tx_seq_offset = __seq_offset(chan, - chan->expected_tx_seq, chan->buffer_seq); - - /* duplicated tx_seq */ - if (tx_seq_offset < expected_tx_seq_offset) - goto drop; - - set_bit(CONN_SREJ_SENT, &chan->conn_state); - - BT_DBG("chan %p, Enter SREJ", chan); - - INIT_LIST_HEAD(&chan->srej_l); - chan->buffer_seq_srej = chan->buffer_seq; - - __skb_queue_head_init(&chan->srej_q); - l2cap_add_to_srej_queue(chan, skb, tx_seq, sar); - - /* Set P-bit only if there are some I-frames to ack. */ - if (__clear_ack_timer(chan)) - set_bit(CONN_SEND_PBIT, &chan->conn_state); - - err = l2cap_send_srejframe(chan, tx_seq); - if (err < 0) { - l2cap_send_disconn_req(chan->conn, chan, -err); - return err; - } - } - return 0; - -expected: - chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); - - if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { - bt_cb(skb)->tx_seq = tx_seq; - bt_cb(skb)->sar = sar; - __skb_queue_tail(&chan->srej_q, skb); - return 0; - } - - err = l2cap_reassemble_sdu(chan, skb, rx_control); - chan->buffer_seq = __next_seq(chan, chan->buffer_seq); - - if (err < 0) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - return err; - } - - if (__is_ctrl_final(chan, rx_control)) { - if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) - l2cap_retransmit_frames(chan); - } - - - chan->num_acked = (chan->num_acked + 1) % num_to_ack; - if (chan->num_acked == num_to_ack - 1) - l2cap_send_ack(chan); - else - __set_ack_timer(chan); - - return 0; - -drop: - kfree_skb(skb); - return 0; -} - -static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u32 rx_control) -{ - BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, - __get_reqseq(chan, rx_control), rx_control); - - chan->expected_ack_seq = __get_reqseq(chan, rx_control); - l2cap_drop_acked_frames(chan); - - if (__is_ctrl_poll(chan, rx_control)) { - set_bit(CONN_SEND_FBIT, &chan->conn_state); - if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { - if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && - (chan->unacked_frames > 0)) - __set_retrans_timer(chan); - - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - l2cap_send_srejtail(chan); - } else { - l2cap_send_i_or_rr_or_rnr(chan); - } - - } else if (__is_ctrl_final(chan, rx_control)) { - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - - if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) - l2cap_retransmit_frames(chan); - - } else { - if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && - (chan->unacked_frames > 0)) - __set_retrans_timer(chan); - - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) - l2cap_send_ack(chan); - else - l2cap_ertm_send(chan); - } -} - -static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u32 rx_control) -{ - u16 tx_seq = __get_reqseq(chan, rx_control); - - BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); - - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - - chan->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(chan); - - if (__is_ctrl_final(chan, rx_control)) { - if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) - l2cap_retransmit_frames(chan); - } else { - l2cap_retransmit_frames(chan); - - if (test_bit(CONN_WAIT_F, &chan->conn_state)) - set_bit(CONN_REJ_ACT, &chan->conn_state); - } -} -static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u32 rx_control) -{ - u16 tx_seq = __get_reqseq(chan, rx_control); - - BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); - - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - - if (__is_ctrl_poll(chan, rx_control)) { - chan->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(chan); - - set_bit(CONN_SEND_FBIT, &chan->conn_state); - l2cap_retransmit_one_frame(chan, tx_seq); - - l2cap_ertm_send(chan); - - if (test_bit(CONN_WAIT_F, &chan->conn_state)) { - chan->srej_save_reqseq = tx_seq; - set_bit(CONN_SREJ_ACT, &chan->conn_state); - } - } else if (__is_ctrl_final(chan, rx_control)) { - if (test_bit(CONN_SREJ_ACT, &chan->conn_state) && - chan->srej_save_reqseq == tx_seq) - clear_bit(CONN_SREJ_ACT, &chan->conn_state); - else - l2cap_retransmit_one_frame(chan, tx_seq); - } else { - l2cap_retransmit_one_frame(chan, tx_seq); - if (test_bit(CONN_WAIT_F, &chan->conn_state)) { - chan->srej_save_reqseq = tx_seq; - set_bit(CONN_SREJ_ACT, &chan->conn_state); - } - } -} - -static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u32 rx_control) -{ - u16 tx_seq = __get_reqseq(chan, rx_control); - - BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); - - set_bit(CONN_REMOTE_BUSY, &chan->conn_state); - chan->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(chan); - - if (__is_ctrl_poll(chan, rx_control)) - set_bit(CONN_SEND_FBIT, &chan->conn_state); - - if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) { - __clear_retrans_timer(chan); - if (__is_ctrl_poll(chan, rx_control)) - l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL); - return; - } - - if (__is_ctrl_poll(chan, rx_control)) { - l2cap_send_srejtail(chan); - } else { - rx_control = __set_ctrl_super(chan, L2CAP_SUPER_RR); - l2cap_send_sframe(chan, rx_control); - } -} - -static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb) -{ - BT_DBG("chan %p rx_control 0x%8.8x len %d", chan, rx_control, skb->len); - - if (__is_ctrl_final(chan, rx_control) && - test_bit(CONN_WAIT_F, &chan->conn_state)) { - __clear_monitor_timer(chan); - if (chan->unacked_frames > 0) - __set_retrans_timer(chan); - clear_bit(CONN_WAIT_F, &chan->conn_state); - } - - switch (__get_ctrl_super(chan, rx_control)) { - case L2CAP_SUPER_RR: - l2cap_data_channel_rrframe(chan, rx_control); - break; - - case L2CAP_SUPER_REJ: - l2cap_data_channel_rejframe(chan, rx_control); - break; - - case L2CAP_SUPER_SREJ: - l2cap_data_channel_srejframe(chan, rx_control); - break; - - case L2CAP_SUPER_RNR: - l2cap_data_channel_rnrframe(chan, rx_control); - break; - } - - kfree_skb(skb); - return 0; -} - -static int l2cap_ertm_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) -{ - u32 control; - u16 req_seq; - int len, next_tx_seq_offset, req_seq_offset; - - control = __get_control(chan, skb->data); - skb_pull(skb, __ctrl_size(chan)); - len = skb->len; - - /* - * We can just drop the corrupted I-frame here. - * Receiver will miss it and start proper recovery - * procedures and ask retransmission. - */ - if (l2cap_check_fcs(chan, skb)) - goto drop; - - if (__is_sar_start(chan, control) && !__is_sframe(chan, control)) - len -= L2CAP_SDULEN_SIZE; - - if (chan->fcs == L2CAP_FCS_CRC16) - len -= L2CAP_FCS_SIZE; - - if (len > chan->mps) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - goto drop; - } - - req_seq = __get_reqseq(chan, control); - - req_seq_offset = __seq_offset(chan, req_seq, chan->expected_ack_seq); - - next_tx_seq_offset = __seq_offset(chan, chan->next_tx_seq, - chan->expected_ack_seq); - - /* check for invalid req-seq */ - if (req_seq_offset > next_tx_seq_offset) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - goto drop; - } - - if (!__is_sframe(chan, control)) { - if (len < 0) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - goto drop; - } - - l2cap_data_channel_iframe(chan, control, skb); - } else { - if (len != 0) { - BT_ERR("%d", len); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - goto drop; - } - - l2cap_data_channel_sframe(chan, control, skb); - } - - return 0; - -drop: - kfree_skb(skb); - return 0; -} - -static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) -{ - struct l2cap_chan *chan; - u32 control; - u16 tx_seq; - int len; - - chan = l2cap_get_chan_by_scid(conn, cid); - if (!chan) { - BT_DBG("unknown cid 0x%4.4x", cid); - /* Drop packet and return */ - kfree_skb(skb); - return 0; - } - - BT_DBG("chan %p, len %d", chan, skb->len); - - if (chan->state != BT_CONNECTED) - goto drop; - - switch (chan->mode) { - case L2CAP_MODE_BASIC: - /* If socket recv buffers overflows we drop data here - * which is *bad* because L2CAP has to be reliable. - * But we don't have any other choice. L2CAP doesn't - * provide flow control mechanism. */ - - if (chan->imtu < skb->len) - goto drop; - - if (!chan->ops->recv(chan->data, skb)) - goto done; - break; - - case L2CAP_MODE_ERTM: - l2cap_ertm_data_rcv(chan, skb); - - goto done; - - case L2CAP_MODE_STREAMING: - control = __get_control(chan, skb->data); - skb_pull(skb, __ctrl_size(chan)); - len = skb->len; - - if (l2cap_check_fcs(chan, skb)) - goto drop; - - if (__is_sar_start(chan, control)) - len -= L2CAP_SDULEN_SIZE; - - if (chan->fcs == L2CAP_FCS_CRC16) - len -= L2CAP_FCS_SIZE; - - if (len > chan->mps || len < 0 || __is_sframe(chan, control)) - goto drop; - - tx_seq = __get_txseq(chan, control); - - if (chan->expected_tx_seq != tx_seq) { - /* Frame(s) missing - must discard partial SDU */ - kfree_skb(chan->sdu); - chan->sdu = NULL; - chan->sdu_last_frag = NULL; - chan->sdu_len = 0; - - /* TODO: Notify userland of missing data */ - } - - chan->expected_tx_seq = __next_seq(chan, tx_seq); - - if (l2cap_reassemble_sdu(chan, skb, control) == -EMSGSIZE) - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - - goto done; - - default: - BT_DBG("chan %p: bad mode 0x%2.2x", chan, chan->mode); - break; - } - -drop: - kfree_skb(skb); - -done: - l2cap_chan_unlock(chan); - - return 0; -} - -static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb) -{ - struct l2cap_chan *chan; - - chan = l2cap_global_chan_by_psm(0, psm, conn->src); - if (!chan) - goto drop; - - BT_DBG("chan %p, len %d", chan, skb->len); - - if (chan->state != BT_BOUND && chan->state != BT_CONNECTED) - goto drop; - - if (chan->imtu < skb->len) - goto drop; - - if (!chan->ops->recv(chan->data, skb)) - return 0; - -drop: - kfree_skb(skb); - - return 0; -} - -static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct sk_buff *skb) -{ - struct l2cap_chan *chan; - - chan = l2cap_global_chan_by_scid(0, cid, conn->src); - if (!chan) - goto drop; - - BT_DBG("chan %p, len %d", chan, skb->len); - - if (chan->state != BT_BOUND && chan->state != BT_CONNECTED) - goto drop; - - if (chan->imtu < skb->len) - goto drop; - - if (!chan->ops->recv(chan->data, skb)) - return 0; - -drop: - kfree_skb(skb); - - return 0; -} - -static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct l2cap_hdr *lh = (void *) skb->data; - u16 cid, len; - __le16 psm; - - skb_pull(skb, L2CAP_HDR_SIZE); - cid = __le16_to_cpu(lh->cid); - len = __le16_to_cpu(lh->len); - - if (len != skb->len) { - kfree_skb(skb); - return; - } - - BT_DBG("len %d, cid 0x%4.4x", len, cid); - - switch (cid) { - case L2CAP_CID_LE_SIGNALING: - case L2CAP_CID_SIGNALING: - l2cap_sig_channel(conn, skb); - break; - - case L2CAP_CID_CONN_LESS: - psm = get_unaligned_le16(skb->data); - skb_pull(skb, 2); - l2cap_conless_channel(conn, psm, skb); - break; - - case L2CAP_CID_LE_DATA: - l2cap_att_channel(conn, cid, skb); - break; - - case L2CAP_CID_SMP: - if (smp_sig_channel(conn, skb)) - l2cap_conn_del(conn->hcon, EACCES); - break; - - default: - l2cap_data_channel(conn, cid, skb); - break; - } -} - -/* ---- L2CAP interface with lower layer (HCI) ---- */ - -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)); - - /* Find listening sockets and check their link_mode */ - read_lock(&chan_list_lock); - list_for_each_entry(c, &chan_list, global_l) { - struct sock *sk = c->sk; - - if (c->state != BT_LISTEN) - continue; - - if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { - lm1 |= HCI_LM_ACCEPT; - if (test_bit(FLAG_ROLE_SWITCH, &c->flags)) - lm1 |= HCI_LM_MASTER; - exact++; - } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { - lm2 |= HCI_LM_ACCEPT; - if (test_bit(FLAG_ROLE_SWITCH, &c->flags)) - lm2 |= HCI_LM_MASTER; - } - } - read_unlock(&chan_list_lock); - - return exact ? lm1 : lm2; -} - -int 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); - - if (!status) { - conn = l2cap_conn_add(hcon, status); - if (conn) - l2cap_conn_ready(conn); - } else - l2cap_conn_del(hcon, bt_to_errno(status)); - - return 0; -} - -int l2cap_disconn_ind(struct hci_conn *hcon) -{ - struct l2cap_conn *conn = hcon->l2cap_data; - - BT_DBG("hcon %p", hcon); - - if (!conn) - return HCI_ERROR_REMOTE_USER_TERM; - return conn->disc_reason; -} - -int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) -{ - BT_DBG("hcon %p reason %d", hcon, reason); - - l2cap_conn_del(hcon, bt_to_errno(reason)); - return 0; -} - -static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) -{ - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) - return; - - if (encrypt == 0x00) { - if (chan->sec_level == BT_SECURITY_MEDIUM) { - __clear_chan_timer(chan); - __set_chan_timer(chan, L2CAP_ENC_TIMEOUT); - } else if (chan->sec_level == BT_SECURITY_HIGH) - l2cap_chan_close(chan, ECONNREFUSED); - } else { - if (chan->sec_level == BT_SECURITY_MEDIUM) - __clear_chan_timer(chan); - } -} - -int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) -{ - struct l2cap_conn *conn = hcon->l2cap_data; - struct l2cap_chan *chan; - - if (!conn) - return 0; - - BT_DBG("conn %p", conn); - - if (hcon->type == LE_LINK) { - smp_distribute_keys(conn, 0); - cancel_delayed_work(&conn->security_timer); - } - - mutex_lock(&conn->chan_lock); - - list_for_each_entry(chan, &conn->chan_l, list) { - l2cap_chan_lock(chan); - - BT_DBG("chan->scid %d", chan->scid); - - if (chan->scid == L2CAP_CID_LE_DATA) { - if (!status && encrypt) { - chan->sec_level = hcon->sec_level; - l2cap_chan_ready(chan); - } - - l2cap_chan_unlock(chan); - continue; - } - - if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) { - l2cap_chan_unlock(chan); - continue; - } - - if (!status && (chan->state == BT_CONNECTED || - chan->state == BT_CONFIG)) { - struct sock *sk = chan->sk; - - bt_sk(sk)->suspended = false; - sk->sk_state_change(sk); - - l2cap_check_encryption(chan, encrypt); - l2cap_chan_unlock(chan); - continue; - } - - if (chan->state == BT_CONNECT) { - if (!status) { - l2cap_send_conn_req(chan); - } else { - __clear_chan_timer(chan); - __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); - } - } else if (chan->state == BT_CONNECT2) { - struct sock *sk = chan->sk; - struct l2cap_conn_rsp rsp; - __u16 res, stat; - - lock_sock(sk); - - if (!status) { - if (bt_sk(sk)->defer_setup) { - struct sock *parent = bt_sk(sk)->parent; - res = L2CAP_CR_PEND; - stat = L2CAP_CS_AUTHOR_PEND; - if (parent) - parent->sk_data_ready(parent, 0); - } else { - __l2cap_state_change(chan, BT_CONFIG); - res = L2CAP_CR_SUCCESS; - stat = L2CAP_CS_NO_INFO; - } - } else { - __l2cap_state_change(chan, BT_DISCONN); - __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); - res = L2CAP_CR_SEC_BLOCK; - stat = L2CAP_CS_NO_INFO; - } - - release_sock(sk); - - rsp.scid = cpu_to_le16(chan->dcid); - rsp.dcid = cpu_to_le16(chan->scid); - rsp.result = cpu_to_le16(res); - rsp.status = cpu_to_le16(stat); - l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); - } - - l2cap_chan_unlock(chan); - } - - mutex_unlock(&conn->chan_lock); - - return 0; -} - -int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) -{ - struct l2cap_conn *conn = hcon->l2cap_data; - - if (!conn) - conn = l2cap_conn_add(hcon, 0); - - if (!conn) - goto drop; - - BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags); - - if (!(flags & ACL_CONT)) { - struct l2cap_hdr *hdr; - int len; - - if (conn->rx_len) { - BT_ERR("Unexpected start frame (len %d)", skb->len); - kfree_skb(conn->rx_skb); - conn->rx_skb = NULL; - conn->rx_len = 0; - l2cap_conn_unreliable(conn, ECOMM); - } - - /* Start fragment always begin with Basic L2CAP header */ - if (skb->len < L2CAP_HDR_SIZE) { - BT_ERR("Frame is too short (len %d)", skb->len); - l2cap_conn_unreliable(conn, ECOMM); - goto drop; - } - - hdr = (struct l2cap_hdr *) skb->data; - len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE; - - if (len == skb->len) { - /* Complete frame received */ - l2cap_recv_frame(conn, skb); - return 0; - } - - BT_DBG("Start: total len %d, frag len %d", len, skb->len); - - if (skb->len > len) { - BT_ERR("Frame is too long (len %d, expected len %d)", - 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); - if (!conn->rx_skb) - goto drop; - - skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); - conn->rx_len = len - skb->len; - } else { - BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); - - if (!conn->rx_len) { - BT_ERR("Unexpected continuation frame (len %d)", skb->len); - l2cap_conn_unreliable(conn, ECOMM); - goto drop; - } - - if (skb->len > conn->rx_len) { - BT_ERR("Fragment is too long (len %d, expected %d)", - skb->len, conn->rx_len); - kfree_skb(conn->rx_skb); - conn->rx_skb = NULL; - conn->rx_len = 0; - l2cap_conn_unreliable(conn, ECOMM); - goto drop; - } - - skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); - conn->rx_len -= skb->len; - - if (!conn->rx_len) { - /* Complete frame received */ - l2cap_recv_frame(conn, conn->rx_skb); - conn->rx_skb = NULL; - } - } - -drop: - kfree_skb(skb); - return 0; -} - -static int l2cap_debugfs_show(struct seq_file *f, void *p) -{ - struct l2cap_chan *c; - - read_lock(&chan_list_lock); - - 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); - } - - read_unlock(&chan_list_lock); - - return 0; -} - -static int l2cap_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, l2cap_debugfs_show, inode->i_private); -} - -static const struct file_operations l2cap_debugfs_fops = { - .open = l2cap_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *l2cap_debugfs; - -int __init l2cap_init(void) -{ - int err; - - err = l2cap_init_sockets(); - if (err < 0) - return err; - - if (bt_debugfs) { - l2cap_debugfs = debugfs_create_file("l2cap", 0444, - bt_debugfs, NULL, &l2cap_debugfs_fops); - if (!l2cap_debugfs) - BT_ERR("Failed to create L2CAP debug file"); - } - - return 0; -} - -void l2cap_exit(void) -{ - debugfs_remove(l2cap_debugfs); - l2cap_cleanup_sockets(); -} - -module_param(disable_ertm, bool, 0644); -MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode"); diff --git a/net/bluetooth_tizen/l2cap_sock.c b/net/bluetooth_tizen/l2cap_sock.c deleted file mode 100644 index 2cae7af..0000000 --- a/net/bluetooth_tizen/l2cap_sock.c +++ /dev/null @@ -1,1156 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org> - Copyright (C) 2010 Google Inc. - Copyright (C) 2011 ProFUSION Embedded Systems - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth L2CAP sockets. */ - -#include <linux/security.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> -#include <net/bluetooth/smp.h> - -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 int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) -{ - struct sock *sk = sock->sk; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - struct sockaddr_l2 la; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (!addr || addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - memset(&la, 0, sizeof(la)); - len = min_t(unsigned int, sizeof(la), alen); - memcpy(&la, addr, len); - - if (la.l2_cid && la.l2_psm) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state != BT_OPEN) { - err = -EBADFD; - goto done; - } - - if (la.l2_psm) { - __u16 psm = __le16_to_cpu(la.l2_psm); - - /* PSM must be odd and lsb of upper byte must be 0 */ - if ((psm & 0x0101) != 0x0001) { - err = -EINVAL; - goto done; - } - - /* Restrict usage of well-known PSMs */ - if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) { - err = -EACCES; - goto done; - } - } - - if (la.l2_cid) - err = l2cap_add_scid(chan, __le16_to_cpu(la.l2_cid)); - else - err = l2cap_add_psm(chan, &la.l2_bdaddr, la.l2_psm); - - if (err < 0) - goto done; - - if (__le16_to_cpu(la.l2_psm) == 0x0001 || - __le16_to_cpu(la.l2_psm) == 0x0003) - chan->sec_level = BT_SECURITY_SDP; - - bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); - - chan->state = BT_BOUND; - sk->sk_state = BT_BOUND; - -done: - release_sock(sk); - return err; -} - -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; - struct sockaddr_l2 la; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (!addr || alen < sizeof(addr->sa_family) || - addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - memset(&la, 0, sizeof(la)); - len = min_t(unsigned int, sizeof(la), alen); - memcpy(&la, addr, len); - - if (la.l2_cid && la.l2_psm) - return -EINVAL; - - err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid), - &la.l2_bdaddr); - if (err) - return err; - - lock_sock(sk); - - err = bt_sock_wait_state(sk, BT_CONNECTED, - sock_sndtimeo(sk, flags & O_NONBLOCK)); - - release_sock(sk); - - return err; -} - -static int l2cap_sock_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - int err = 0; - - BT_DBG("sk %p backlog %d", sk, backlog); - - lock_sock(sk); - - if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) - || sk->sk_state != BT_BOUND) { - err = -EBADFD; - goto done; - } - - switch (chan->mode) { - case L2CAP_MODE_BASIC: - break; - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - if (!disable_ertm) - break; - /* fall through */ - default: - err = -ENOTSUPP; - goto done; - } - - sk->sk_max_ack_backlog = backlog; - sk->sk_ack_backlog = 0; - - chan->state = BT_LISTEN; - sk->sk_state = BT_LISTEN; - -done: - release_sock(sk); - return err; -} - -static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) -{ - DECLARE_WAITQUEUE(wait, current); - struct sock *sk = sock->sk, *nsk; - long timeo; - int err = 0; - - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); - - timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); - - BT_DBG("sk %p timeo %ld", sk, timeo); - - /* Wait for an incoming connection. (wake-one). */ - add_wait_queue_exclusive(sk_sleep(sk), &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; - break; - } - - nsk = bt_accept_dequeue(sk, newsock); - if (nsk) - break; - - if (!timeo) { - err = -EAGAIN; - break; - } - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - if (err) - goto done; - - newsock->state = SS_CONNECTED; - - BT_DBG("new socket %p", nsk); - -done: - release_sock(sk); - return err; -} - -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; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - - BT_DBG("sock %p, sk %p", sock, sk); - - addr->sa_family = AF_BLUETOOTH; - *len = sizeof(struct sockaddr_l2); - - if (peer) { - la->l2_psm = chan->psm; - bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); - la->l2_cid = cpu_to_le16(chan->dcid); - } else { - la->l2_psm = chan->sport; - bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); - la->l2_cid = cpu_to_le16(chan->scid); - } - - return 0; -} - -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; - struct l2cap_options opts; - struct l2cap_conninfo cinfo; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case L2CAP_OPTIONS: - memset(&opts, 0, sizeof(opts)); - opts.imtu = chan->imtu; - opts.omtu = chan->omtu; - opts.flush_to = chan->flush_to; - opts.mode = chan->mode; - opts.fcs = chan->fcs; - opts.max_tx = chan->max_tx; - opts.txwin_size = chan->tx_win; - - len = min_t(unsigned int, len, sizeof(opts)); - if (copy_to_user(optval, (char *) &opts, len)) - err = -EFAULT; - - break; - - case L2CAP_LM: - switch (chan->sec_level) { - case BT_SECURITY_LOW: - opt = L2CAP_LM_AUTH; - break; - case BT_SECURITY_MEDIUM: - opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT; - break; - case BT_SECURITY_HIGH: - opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | - L2CAP_LM_SECURE; - break; - default: - opt = 0; - break; - } - - if (test_bit(FLAG_ROLE_SWITCH, &chan->flags)) - opt |= L2CAP_LM_MASTER; - - if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) - opt |= L2CAP_LM_RELIABLE; - - if (put_user(opt, (u32 __user *) optval)) - err = -EFAULT; - break; - - case L2CAP_CONNINFO: - if (sk->sk_state != BT_CONNECTED && - !(sk->sk_state == BT_CONNECT2 && - bt_sk(sk)->defer_setup)) { - err = -ENOTCONN; - break; - } - - memset(&cinfo, 0, sizeof(cinfo)); - cinfo.hci_handle = chan->conn->hcon->handle; - memcpy(cinfo.dev_class, chan->conn->hcon->dev_class, 3); - - len = min_t(unsigned int, len, sizeof(cinfo)); - if (copy_to_user(optval, (char *) &cinfo, len)) - err = -EFAULT; - - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -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; - struct bt_security sec; - struct bt_power pwr; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (level == SOL_L2CAP) - return l2cap_sock_getsockopt_old(sock, optname, optval, optlen); - - if (level != SOL_BLUETOOTH) - return -ENOPROTOOPT; - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case BT_SECURITY: - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && - chan->chan_type != L2CAP_CHAN_RAW) { - err = -EINVAL; - break; - } - - memset(&sec, 0, sizeof(sec)); - 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; - - len = min_t(unsigned int, len, sizeof(sec)); - if (copy_to_user(optval, (char *) &sec, len)) - err = -EFAULT; - - break; - - case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { - err = -EINVAL; - break; - } - - if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval)) - err = -EFAULT; - - break; - - case BT_FLUSHABLE: - if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags), - (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) { - err = -EINVAL; - break; - } - - pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); - - len = min_t(unsigned int, len, sizeof(pwr)); - if (copy_to_user(optval, (char *) &pwr, len)) - err = -EFAULT; - - break; - - case BT_CHANNEL_POLICY: - if (!enable_hs) { - err = -ENOPROTOOPT; - break; - } - - if (put_user(chan->chan_policy, (u32 __user *) optval)) - err = -EFAULT; - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -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; - struct l2cap_options opts; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - lock_sock(sk); - - switch (optname) { - case L2CAP_OPTIONS: - if (sk->sk_state == BT_CONNECTED) { - err = -EINVAL; - break; - } - - opts.imtu = chan->imtu; - opts.omtu = chan->omtu; - opts.flush_to = chan->flush_to; - opts.mode = chan->mode; - opts.fcs = chan->fcs; - opts.max_tx = chan->max_tx; - opts.txwin_size = chan->tx_win; - - len = min_t(unsigned int, sizeof(opts), optlen); - if (copy_from_user((char *) &opts, optval, len)) { - err = -EFAULT; - break; - } - - if (opts.txwin_size > L2CAP_DEFAULT_EXT_WINDOW) { - err = -EINVAL; - break; - } - - chan->mode = opts.mode; - switch (chan->mode) { - case L2CAP_MODE_BASIC: - clear_bit(CONF_STATE2_DEVICE, &chan->conf_state); - break; - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - if (!disable_ertm) - break; - /* fall through */ - default: - err = -EINVAL; - break; - } - - chan->imtu = opts.imtu; - chan->omtu = opts.omtu; - chan->fcs = opts.fcs; - chan->max_tx = opts.max_tx; - chan->tx_win = opts.txwin_size; - break; - - case L2CAP_LM: - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - if (opt & L2CAP_LM_AUTH) - chan->sec_level = BT_SECURITY_LOW; - if (opt & L2CAP_LM_ENCRYPT) - chan->sec_level = BT_SECURITY_MEDIUM; - if (opt & L2CAP_LM_SECURE) - chan->sec_level = BT_SECURITY_HIGH; - - if (opt & L2CAP_LM_MASTER) - set_bit(FLAG_ROLE_SWITCH, &chan->flags); - else - clear_bit(FLAG_ROLE_SWITCH, &chan->flags); - - if (opt & L2CAP_LM_RELIABLE) - set_bit(FLAG_FORCE_RELIABLE, &chan->flags); - else - clear_bit(FLAG_FORCE_RELIABLE, &chan->flags); - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -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; - struct bt_security sec; - struct bt_power pwr; - struct l2cap_conn *conn; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - if (level == SOL_L2CAP) - return l2cap_sock_setsockopt_old(sock, optname, optval, optlen); - - if (level != SOL_BLUETOOTH) - return -ENOPROTOOPT; - - lock_sock(sk); - - switch (optname) { - case BT_SECURITY: - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && - chan->chan_type != L2CAP_CHAN_RAW) { - err = -EINVAL; - break; - } - - sec.level = BT_SECURITY_LOW; - - len = min_t(unsigned int, sizeof(sec), optlen); - if (copy_from_user((char *) &sec, optval, len)) { - err = -EFAULT; - break; - } - - if (sec.level < BT_SECURITY_LOW || - sec.level > BT_SECURITY_HIGH) { - err = -EINVAL; - break; - } - - chan->sec_level = sec.level; - - if (!chan->conn) - break; - - conn = chan->conn; - - /*change security for LE channels */ - if (chan->scid == L2CAP_CID_LE_DATA) { - if (!conn->hcon->out) { - err = -EINVAL; - break; - } - - if (smp_conn_security(conn, sec.level)) - break; - sk->sk_state = BT_CONFIG; - chan->state = BT_CONFIG; - - /* or for ACL link */ - } else if ((sk->sk_state == BT_CONNECT2 && - bt_sk(sk)->defer_setup) || - sk->sk_state == BT_CONNECTED) { - if (!l2cap_chan_check_security(chan)) - bt_sk(sk)->suspended = true; - else - sk->sk_state_change(sk); - } - else { - err = -EINVAL; - } - break; - - case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { - err = -EINVAL; - break; - } - - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - bt_sk(sk)->defer_setup = opt; - break; - - case BT_FLUSHABLE: - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - if (opt > BT_FLUSHABLE_ON) { - err = -EINVAL; - break; - } - - if (opt == BT_FLUSHABLE_OFF) { - struct l2cap_conn *conn = chan->conn; - /* proceed further only when we have l2cap_conn and - No Flush support in the LM */ - if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) { - err = -EINVAL; - break; - } - } - - if (opt) - set_bit(FLAG_FLUSHABLE, &chan->flags); - else - clear_bit(FLAG_FLUSHABLE, &chan->flags); - break; - - case BT_POWER: - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && - chan->chan_type != L2CAP_CHAN_RAW) { - err = -EINVAL; - break; - } - - pwr.force_active = BT_POWER_FORCE_ACTIVE_ON; - - len = min_t(unsigned int, sizeof(pwr), optlen); - if (copy_from_user((char *) &pwr, optval, len)) { - err = -EFAULT; - break; - } - - if (pwr.force_active) - set_bit(FLAG_FORCE_ACTIVE, &chan->flags); - else - clear_bit(FLAG_FORCE_ACTIVE, &chan->flags); - break; - - case BT_CHANNEL_POLICY: - if (!enable_hs) { - err = -ENOPROTOOPT; - break; - } - - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - if (opt > BT_CHANNEL_POLICY_AMP_PREFERRED) { - err = -EINVAL; - break; - } - - if (chan->mode != L2CAP_MODE_ERTM && - chan->mode != L2CAP_MODE_STREAMING) { - err = -EOPNOTSUPP; - break; - } - - chan->chan_policy = (u8) opt; - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -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; - int err; - - BT_DBG("sock %p, sk %p", sock, sk); - - err = sock_error(sk); - if (err) - return err; - - if (msg->msg_flags & MSG_OOB) - return -EOPNOTSUPP; - - l2cap_chan_lock(chan); - - if (sk->sk_state != BT_CONNECTED) { - l2cap_chan_unlock(chan); - return -ENOTCONN; - } - - err = l2cap_chan_send(chan, msg, len, sk->sk_priority); - l2cap_chan_unlock(chan); - - return err; -} - -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); - int err; - - lock_sock(sk); - - if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) { - sk->sk_state = BT_CONFIG; - pi->chan->state = BT_CONFIG; - - __l2cap_connect_rsp_defer(pi->chan); - release_sock(sk); - return 0; - } - - release_sock(sk); - - if (sock->type == SOCK_STREAM) - err = bt_sock_stream_recvmsg(iocb, sock, msg, len, flags); - else - err = bt_sock_recvmsg(iocb, sock, msg, len, flags); - - if (pi->chan->mode != L2CAP_MODE_ERTM) - return err; - - /* Attempt to put pending rx data in the socket buffer */ - - lock_sock(sk); - - if (!test_bit(CONN_LOCAL_BUSY, &pi->chan->conn_state)) - goto done; - - if (pi->rx_busy_skb) { - if (!sock_queue_rcv_skb(sk, pi->rx_busy_skb)) - pi->rx_busy_skb = NULL; - else - goto done; - } - - /* Restore data flow when half of the receive buffer is - * available. This avoids resending large numbers of - * frames. - */ - if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf >> 1) - l2cap_chan_busy(pi->chan, 0); - -done: - release_sock(sk); - return err; -} - -/* Kill socket (only if zapped and orphan) - * Must be called on unlocked socket. - */ -static void l2cap_sock_kill(struct sock *sk) -{ - if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || - sock_flag(sk, SOCK_DEAD)) - return; - - BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state)); - - /* Kill poor orphan */ - - l2cap_chan_destroy(l2cap_pi(sk)->chan); - sock_set_flag(sk, SOCK_DEAD); - sock_put(sk); -} - -static int l2cap_sock_shutdown(struct socket *sock, int how) -{ - struct sock *sk = sock->sk; - struct l2cap_chan *chan; - struct l2cap_conn *conn; - int err = 0; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - chan = l2cap_pi(sk)->chan; - conn = chan->conn; - - if (conn) - mutex_lock(&conn->chan_lock); - - l2cap_chan_lock(chan); - lock_sock(sk); - - if (!sk->sk_shutdown) { - if (chan->mode == L2CAP_MODE_ERTM) - err = __l2cap_wait_ack(sk); - - sk->sk_shutdown = SHUTDOWN_MASK; - - release_sock(sk); - l2cap_chan_close(chan, 0); - lock_sock(sk); - - if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) - err = bt_sock_wait_state(sk, BT_CLOSED, - sk->sk_lingertime); - } - - if (!err && sk->sk_err) - err = -sk->sk_err; - - release_sock(sk); - l2cap_chan_unlock(chan); - - if (conn) - mutex_unlock(&conn->chan_lock); - - return err; -} - -static int l2cap_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - int err; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - err = l2cap_sock_shutdown(sock, 2); - - sock_orphan(sk); - l2cap_sock_kill(sk); - return err; -} - -static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data) -{ - struct sock *sk, *parent = data; - - sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, - GFP_ATOMIC); - if (!sk) - return NULL; - - bt_sock_reclassify_lock(sk, BTPROTO_L2CAP); - - l2cap_sock_init(sk, parent); - - return l2cap_pi(sk)->chan; -} - -static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb) -{ - int err; - struct sock *sk = data; - struct l2cap_pinfo *pi = l2cap_pi(sk); - - lock_sock(sk); - - if (pi->rx_busy_skb) { - err = -ENOMEM; - goto done; - } - - err = sock_queue_rcv_skb(sk, skb); - - /* For ERTM, handle one skb that doesn't fit into the recv - * buffer. This is important to do because the data frames - * have already been acked, so the skb cannot be discarded. - * - * Notify the l2cap core that the buffer is full, so the - * LOCAL_BUSY state is entered and no more frames are - * acked and reassembled until there is buffer space - * available. - */ - if (err < 0 && pi->chan->mode == L2CAP_MODE_ERTM) { - pi->rx_busy_skb = skb; - l2cap_chan_busy(pi->chan, 1); - err = 0; - } - -done: - release_sock(sk); - - return err; -} - -static void l2cap_sock_close_cb(void *data) -{ - struct sock *sk = data; - - l2cap_sock_kill(sk); -} - -static void l2cap_sock_state_change_cb(void *data, int state) -{ - struct sock *sk = data; - - sk->sk_state = state; -} - -static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, - unsigned long len, int nb, - int *err) -{ - struct sk_buff *skb; - - l2cap_chan_unlock(chan); - - skb = bt_skb_send_alloc(chan->sk, len, nb, err); - - l2cap_chan_lock(chan); - - return skb; -} - -static struct l2cap_ops l2cap_chan_ops = { - .name = "L2CAP Socket Interface", - .new_connection = l2cap_sock_new_connection_cb, - .recv = l2cap_sock_recv_cb, - .close = l2cap_sock_close_cb, - .state_change = l2cap_sock_state_change_cb, - .alloc_skb = l2cap_sock_alloc_skb_cb, -}; - -static void l2cap_sock_destruct(struct sock *sk) -{ - BT_DBG("sk %p", sk); - - if (l2cap_pi(sk)->rx_busy_skb) { - kfree_skb(l2cap_pi(sk)->rx_busy_skb); - l2cap_pi(sk)->rx_busy_skb = NULL; - } - - skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); -} - -static void l2cap_sock_init(struct sock *sk, struct sock *parent) -{ - struct l2cap_pinfo *pi = l2cap_pi(sk); - struct l2cap_chan *chan = pi->chan; - - BT_DBG("sk %p", sk); - - if (parent) { - struct l2cap_chan *pchan = l2cap_pi(parent)->chan; - - sk->sk_type = parent->sk_type; - bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup; - - chan->chan_type = pchan->chan_type; - chan->imtu = pchan->imtu; - chan->omtu = pchan->omtu; - chan->conf_state = pchan->conf_state; - chan->mode = pchan->mode; - chan->fcs = pchan->fcs; - chan->max_tx = pchan->max_tx; - chan->tx_win = pchan->tx_win; - chan->tx_win_max = pchan->tx_win_max; - chan->sec_level = pchan->sec_level; - chan->flags = pchan->flags; - - security_sk_clone(parent, sk); - } else { - - switch (sk->sk_type) { - case SOCK_RAW: - chan->chan_type = L2CAP_CHAN_RAW; - break; - case SOCK_DGRAM: - chan->chan_type = L2CAP_CHAN_CONN_LESS; - break; - case SOCK_SEQPACKET: - case SOCK_STREAM: - chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; - break; - } - - chan->imtu = L2CAP_DEFAULT_MTU; - chan->omtu = 0; - if (!disable_ertm && sk->sk_type == SOCK_STREAM) { - chan->mode = L2CAP_MODE_ERTM; - set_bit(CONF_STATE2_DEVICE, &chan->conf_state); - } else { - chan->mode = L2CAP_MODE_BASIC; - } - chan->max_tx = L2CAP_DEFAULT_MAX_TX; - chan->fcs = L2CAP_FCS_CRC16; - chan->tx_win = L2CAP_DEFAULT_TX_WINDOW; - chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW; - chan->sec_level = BT_SECURITY_LOW; - chan->flags = 0; - set_bit(FLAG_FORCE_ACTIVE, &chan->flags); - } - - /* Default config options */ - chan->flush_to = L2CAP_DEFAULT_FLUSH_TO; - - chan->data = sk; - chan->ops = &l2cap_chan_ops; -} - -static struct proto l2cap_proto = { - .name = "L2CAP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct l2cap_pinfo) -}; - -static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) -{ - struct sock *sk; - struct l2cap_chan *chan; - - sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto); - if (!sk) - return NULL; - - sock_init_data(sock, sk); - INIT_LIST_HEAD(&bt_sk(sk)->accept_q); - - sk->sk_destruct = l2cap_sock_destruct; - sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = proto; - sk->sk_state = BT_OPEN; - - chan = l2cap_chan_create(sk); - if (!chan) { - l2cap_sock_kill(sk); - return NULL; - } - - l2cap_pi(sk)->chan = chan; - - return sk; -} - -static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - sock->state = SS_UNCONNECTED; - - if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM && - sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) - return -EPERM; - - sock->ops = &l2cap_sock_ops; - - sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC); - if (!sk) - return -ENOMEM; - - l2cap_sock_init(sk, NULL); - return 0; -} - -static const struct proto_ops l2cap_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = l2cap_sock_release, - .bind = l2cap_sock_bind, - .connect = l2cap_sock_connect, - .listen = l2cap_sock_listen, - .accept = l2cap_sock_accept, - .getname = l2cap_sock_getname, - .sendmsg = l2cap_sock_sendmsg, - .recvmsg = l2cap_sock_recvmsg, - .poll = bt_sock_poll, - .ioctl = bt_sock_ioctl, - .mmap = sock_no_mmap, - .socketpair = sock_no_socketpair, - .shutdown = l2cap_sock_shutdown, - .setsockopt = l2cap_sock_setsockopt, - .getsockopt = l2cap_sock_getsockopt -}; - -static const struct net_proto_family l2cap_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = l2cap_sock_create, -}; - -int __init l2cap_init_sockets(void) -{ - int err; - - err = proto_register(&l2cap_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops); - if (err < 0) - goto error; - - BT_INFO("L2CAP socket layer initialized"); - - return 0; - -error: - BT_ERR("L2CAP socket registration failed"); - proto_unregister(&l2cap_proto); - return err; -} - -void l2cap_cleanup_sockets(void) -{ - if (bt_sock_unregister(BTPROTO_L2CAP) < 0) - BT_ERR("L2CAP socket unregistration failed"); - - proto_unregister(&l2cap_proto); -} diff --git a/net/bluetooth_tizen/lib.c b/net/bluetooth_tizen/lib.c deleted file mode 100644 index 5066288..0000000 --- a/net/bluetooth_tizen/lib.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth kernel library. */ - -#define pr_fmt(fmt) "Bluetooth: " fmt - -#include <linux/module.h> - -#include <linux/kernel.h> -#include <linux/stddef.h> -#include <linux/string.h> -#include <asm/errno.h> - -#include <net/bluetooth/bluetooth.h> - -void baswap(bdaddr_t *dst, bdaddr_t *src) -{ - unsigned char *d = (unsigned char *) dst; - unsigned char *s = (unsigned char *) src; - unsigned int i; - - for (i = 0; i < 6; i++) - d[i] = s[5 - i]; -} -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) -{ - switch (code) { - case 0: - return 0; - - case 0x01: - return EBADRQC; - - case 0x02: - return ENOTCONN; - - case 0x03: - return EIO; - - case 0x04: - return EHOSTDOWN; - - case 0x05: - return EACCES; - - case 0x06: - return EBADE; - - case 0x07: - return ENOMEM; - - case 0x08: - return ETIMEDOUT; - - case 0x09: - return EMLINK; - - case 0x0a: - return EMLINK; - - case 0x0b: - return EALREADY; - - case 0x0c: - return EBUSY; - - case 0x0d: - case 0x0e: - case 0x0f: - return ECONNREFUSED; - - case 0x10: - return ETIMEDOUT; - - case 0x11: - case 0x27: - case 0x29: - case 0x20: - return EOPNOTSUPP; - - case 0x12: - return EINVAL; - - case 0x13: - case 0x14: - case 0x15: - return ECONNRESET; - - case 0x16: - return ECONNABORTED; - - case 0x17: - return ELOOP; - - case 0x18: - return EACCES; - - case 0x1a: - return EPROTONOSUPPORT; - - case 0x1b: - return ECONNREFUSED; - - case 0x19: - case 0x1e: - case 0x23: - case 0x24: - case 0x25: - return EPROTO; - - default: - return ENOSYS; - } -} -EXPORT_SYMBOL(bt_to_errno); - -int bt_info(const char *format, ...) -{ - struct va_format vaf; - va_list args; - int r; - - va_start(args, format); - - vaf.fmt = format; - vaf.va = &args; - - r = pr_info("%pV", &vaf); - - va_end(args); - - return r; -} -EXPORT_SYMBOL(bt_info); - -int bt_err(const char *format, ...) -{ - struct va_format vaf; - va_list args; - int r; - - va_start(args, format); - - vaf.fmt = format; - vaf.va = &args; - - r = pr_err("%pV", &vaf); - - va_end(args); - - return r; -} -EXPORT_SYMBOL(bt_err); diff --git a/net/bluetooth_tizen/mgmt.c b/net/bluetooth_tizen/mgmt.c deleted file mode 100644 index 4bb03b1..0000000 --- a/net/bluetooth_tizen/mgmt.c +++ /dev/null @@ -1,3599 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - - Copyright (C) 2010 Nokia Corporation - Copyright (C) 2011-2012 Intel Corporation - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth HCI Management interface */ - -#include <linux/kernel.h> -#include <linux/uaccess.h> -#include <linux/module.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/mgmt.h> -#include <net/bluetooth/smp.h> - -bool enable_hs; -bool enable_le; - -#define MGMT_VERSION 1 -#define MGMT_REVISION 0 - -static const u16 mgmt_commands[] = { - MGMT_OP_READ_INDEX_LIST, - MGMT_OP_READ_INFO, - MGMT_OP_SET_POWERED, - MGMT_OP_SET_DISCOVERABLE, - MGMT_OP_SET_CONNECTABLE, - MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_OP_SET_PAIRABLE, - MGMT_OP_SET_LINK_SECURITY, - MGMT_OP_SET_SSP, - MGMT_OP_SET_HS, - MGMT_OP_SET_LE, - MGMT_OP_SET_DEV_CLASS, - MGMT_OP_SET_LOCAL_NAME, - MGMT_OP_ADD_UUID, - MGMT_OP_REMOVE_UUID, - MGMT_OP_LOAD_LINK_KEYS, - MGMT_OP_LOAD_LONG_TERM_KEYS, - MGMT_OP_DISCONNECT, - MGMT_OP_GET_CONNECTIONS, - MGMT_OP_PIN_CODE_REPLY, - MGMT_OP_PIN_CODE_NEG_REPLY, - MGMT_OP_SET_IO_CAPABILITY, - MGMT_OP_PAIR_DEVICE, - MGMT_OP_CANCEL_PAIR_DEVICE, - MGMT_OP_UNPAIR_DEVICE, - MGMT_OP_USER_CONFIRM_REPLY, - MGMT_OP_USER_CONFIRM_NEG_REPLY, - MGMT_OP_USER_PASSKEY_REPLY, - MGMT_OP_USER_PASSKEY_NEG_REPLY, - MGMT_OP_READ_LOCAL_OOB_DATA, - MGMT_OP_ADD_REMOTE_OOB_DATA, - MGMT_OP_REMOVE_REMOTE_OOB_DATA, - MGMT_OP_START_DISCOVERY, - MGMT_OP_STOP_DISCOVERY, - MGMT_OP_CONFIRM_NAME, - MGMT_OP_BLOCK_DEVICE, - MGMT_OP_UNBLOCK_DEVICE, -}; - -static const u16 mgmt_events[] = { - MGMT_EV_CONTROLLER_ERROR, - MGMT_EV_INDEX_ADDED, - MGMT_EV_INDEX_REMOVED, - MGMT_EV_NEW_SETTINGS, - MGMT_EV_CLASS_OF_DEV_CHANGED, - MGMT_EV_LOCAL_NAME_CHANGED, - MGMT_EV_NEW_LINK_KEY, - MGMT_EV_NEW_LONG_TERM_KEY, - MGMT_EV_DEVICE_CONNECTED, - MGMT_EV_DEVICE_DISCONNECTED, - MGMT_EV_CONNECT_FAILED, - MGMT_EV_PIN_CODE_REQUEST, - MGMT_EV_USER_CONFIRM_REQUEST, - MGMT_EV_USER_PASSKEY_REQUEST, - MGMT_EV_AUTH_FAILED, - MGMT_EV_DEVICE_FOUND, - MGMT_EV_DISCOVERING, - MGMT_EV_DEVICE_BLOCKED, - MGMT_EV_DEVICE_UNBLOCKED, - MGMT_EV_DEVICE_UNPAIRED, -}; - -/* - * These LE scan and inquiry parameters were chosen according to LE General - * Discovery Procedure specification. - */ -#define LE_SCAN_TYPE 0x01 -#define LE_SCAN_WIN 0x12 -#define LE_SCAN_INT 0x12 -#define LE_SCAN_TIMEOUT_LE_ONLY 10240 /* TGAP(gen_disc_scan_min) */ -#define LE_SCAN_TIMEOUT_BREDR_LE 5120 /* TGAP(100)/2 */ - -#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */ -#define INQUIRY_LEN_BREDR_LE 0x04 /* TGAP(100)/2 */ - -#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) - -#define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \ - !test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - -struct pending_cmd { - struct list_head list; - u16 opcode; - int index; - void *param; - struct sock *sk; - void *user_data; -}; - -/* HCI to MGMT error code conversion table */ -static u8 mgmt_status_table[] = { - MGMT_STATUS_SUCCESS, - MGMT_STATUS_UNKNOWN_COMMAND, /* Unknown Command */ - MGMT_STATUS_NOT_CONNECTED, /* No Connection */ - MGMT_STATUS_FAILED, /* Hardware Failure */ - MGMT_STATUS_CONNECT_FAILED, /* Page Timeout */ - MGMT_STATUS_AUTH_FAILED, /* Authentication Failed */ - MGMT_STATUS_NOT_PAIRED, /* PIN or Key Missing */ - MGMT_STATUS_NO_RESOURCES, /* Memory Full */ - MGMT_STATUS_TIMEOUT, /* Connection Timeout */ - MGMT_STATUS_NO_RESOURCES, /* Max Number of Connections */ - MGMT_STATUS_NO_RESOURCES, /* Max Number of SCO Connections */ - MGMT_STATUS_ALREADY_CONNECTED, /* ACL Connection Exists */ - MGMT_STATUS_BUSY, /* Command Disallowed */ - MGMT_STATUS_NO_RESOURCES, /* Rejected Limited Resources */ - MGMT_STATUS_REJECTED, /* Rejected Security */ - MGMT_STATUS_REJECTED, /* Rejected Personal */ - MGMT_STATUS_TIMEOUT, /* Host Timeout */ - MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Feature */ - MGMT_STATUS_INVALID_PARAMS, /* Invalid Parameters */ - MGMT_STATUS_DISCONNECTED, /* OE User Ended Connection */ - MGMT_STATUS_NO_RESOURCES, /* OE Low Resources */ - MGMT_STATUS_DISCONNECTED, /* OE Power Off */ - MGMT_STATUS_DISCONNECTED, /* Connection Terminated */ - MGMT_STATUS_BUSY, /* Repeated Attempts */ - MGMT_STATUS_REJECTED, /* Pairing Not Allowed */ - MGMT_STATUS_FAILED, /* Unknown LMP PDU */ - MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Remote Feature */ - MGMT_STATUS_REJECTED, /* SCO Offset Rejected */ - MGMT_STATUS_REJECTED, /* SCO Interval Rejected */ - MGMT_STATUS_REJECTED, /* Air Mode Rejected */ - MGMT_STATUS_INVALID_PARAMS, /* Invalid LMP Parameters */ - MGMT_STATUS_FAILED, /* Unspecified Error */ - MGMT_STATUS_NOT_SUPPORTED, /* Unsupported LMP Parameter Value */ - MGMT_STATUS_FAILED, /* Role Change Not Allowed */ - MGMT_STATUS_TIMEOUT, /* LMP Response Timeout */ - MGMT_STATUS_FAILED, /* LMP Error Transaction Collision */ - MGMT_STATUS_FAILED, /* LMP PDU Not Allowed */ - MGMT_STATUS_REJECTED, /* Encryption Mode Not Accepted */ - MGMT_STATUS_FAILED, /* Unit Link Key Used */ - MGMT_STATUS_NOT_SUPPORTED, /* QoS Not Supported */ - MGMT_STATUS_TIMEOUT, /* Instant Passed */ - MGMT_STATUS_NOT_SUPPORTED, /* Pairing Not Supported */ - MGMT_STATUS_FAILED, /* Transaction Collision */ - MGMT_STATUS_INVALID_PARAMS, /* Unacceptable Parameter */ - MGMT_STATUS_REJECTED, /* QoS Rejected */ - MGMT_STATUS_NOT_SUPPORTED, /* Classification Not Supported */ - MGMT_STATUS_REJECTED, /* Insufficient Security */ - MGMT_STATUS_INVALID_PARAMS, /* Parameter Out Of Range */ - MGMT_STATUS_BUSY, /* Role Switch Pending */ - MGMT_STATUS_FAILED, /* Slot Violation */ - MGMT_STATUS_FAILED, /* Role Switch Failed */ - MGMT_STATUS_INVALID_PARAMS, /* EIR Too Large */ - MGMT_STATUS_NOT_SUPPORTED, /* Simple Pairing Not Supported */ - MGMT_STATUS_BUSY, /* Host Busy Pairing */ - MGMT_STATUS_REJECTED, /* Rejected, No Suitable Channel */ - MGMT_STATUS_BUSY, /* Controller Busy */ - MGMT_STATUS_INVALID_PARAMS, /* Unsuitable Connection Interval */ - MGMT_STATUS_TIMEOUT, /* Directed Advertising Timeout */ - MGMT_STATUS_AUTH_FAILED, /* Terminated Due to MIC Failure */ - MGMT_STATUS_CONNECT_FAILED, /* Connection Establishment Failed */ - MGMT_STATUS_CONNECT_FAILED, /* MAC Connection Failed */ -}; - -static u8 mgmt_status(u8 hci_status) -{ - if (hci_status < ARRAY_SIZE(mgmt_status_table)) - return mgmt_status_table[hci_status]; - - return MGMT_STATUS_FAILED; -} - -static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) -{ - struct sk_buff *skb; - struct mgmt_hdr *hdr; - struct mgmt_ev_cmd_status *ev; - int err; - - BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status); - - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); - hdr->index = cpu_to_le16(index); - hdr->len = cpu_to_le16(sizeof(*ev)); - - ev = (void *) skb_put(skb, sizeof(*ev)); - ev->status = status; - put_unaligned_le16(cmd, &ev->opcode); - - err = sock_queue_rcv_skb(sk, skb); - if (err < 0) - kfree_skb(skb); - - return err; -} - -static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, - void *rp, size_t rp_len) -{ - struct sk_buff *skb; - struct mgmt_hdr *hdr; - struct mgmt_ev_cmd_complete *ev; - int err; - - BT_DBG("sock %p", sk); - - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); - hdr->index = cpu_to_le16(index); - hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); - - ev = (void *) skb_put(skb, sizeof(*ev) + rp_len); - put_unaligned_le16(cmd, &ev->opcode); - ev->status = status; - - if (rp) - memcpy(ev->data, rp, rp_len); - - err = sock_queue_rcv_skb(sk, skb); - if (err < 0) - kfree_skb(skb); - - return err; -} - -static int read_version(struct sock *sk, struct hci_dev *hdev, void *data, - u16 data_len) -{ - struct mgmt_rp_read_version rp; - - BT_DBG("sock %p", sk); - - rp.version = MGMT_VERSION; - put_unaligned_le16(MGMT_REVISION, &rp.revision); - - return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0, &rp, - sizeof(rp)); -} - -static int read_commands(struct sock *sk, struct hci_dev *hdev, void *data, - u16 data_len) -{ - struct mgmt_rp_read_commands *rp; - u16 num_commands = ARRAY_SIZE(mgmt_commands); - u16 num_events = ARRAY_SIZE(mgmt_events); - u16 *opcode; - size_t rp_size; - int i, err; - - BT_DBG("sock %p", sk); - - rp_size = sizeof(*rp) + ((num_commands + num_events) * sizeof(u16)); - - rp = kmalloc(rp_size, GFP_KERNEL); - if (!rp) - return -ENOMEM; - - put_unaligned_le16(num_commands, &rp->num_commands); - put_unaligned_le16(num_events, &rp->num_events); - - for (i = 0, opcode = rp->opcodes; i < num_commands; i++, opcode++) - put_unaligned_le16(mgmt_commands[i], opcode); - - for (i = 0; i < num_events; i++, opcode++) - put_unaligned_le16(mgmt_events[i], opcode); - - err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, 0, rp, - rp_size); - kfree(rp); - - return err; -} - -static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, - u16 data_len) -{ - struct mgmt_rp_read_index_list *rp; - struct list_head *p; - struct hci_dev *d; - size_t rp_len; - u16 count; - int i, err; - - BT_DBG("sock %p", sk); - - read_lock(&hci_dev_list_lock); - - count = 0; - list_for_each(p, &hci_dev_list) { - count++; - } - - rp_len = sizeof(*rp) + (2 * count); - rp = kmalloc(rp_len, GFP_ATOMIC); - if (!rp) { - read_unlock(&hci_dev_list_lock); - return -ENOMEM; - } - - put_unaligned_le16(count, &rp->num_controllers); - - i = 0; - list_for_each_entry(d, &hci_dev_list, list) { - if (test_bit(HCI_SETUP, &d->dev_flags)) - continue; - - put_unaligned_le16(d->id, &rp->index[i++]); - BT_DBG("Added hci%u", d->id); - } - - read_unlock(&hci_dev_list_lock); - - err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 0, rp, - rp_len); - - kfree(rp); - - return err; -} - -static u32 get_supported_settings(struct hci_dev *hdev) -{ - u32 settings = 0; - - settings |= MGMT_SETTING_POWERED; - settings |= MGMT_SETTING_CONNECTABLE; - settings |= MGMT_SETTING_FAST_CONNECTABLE; - settings |= MGMT_SETTING_DISCOVERABLE; - settings |= MGMT_SETTING_PAIRABLE; - - if (hdev->features[6] & LMP_SIMPLE_PAIR) - settings |= MGMT_SETTING_SSP; - - if (!(hdev->features[4] & LMP_NO_BREDR)) { - settings |= MGMT_SETTING_BREDR; - settings |= MGMT_SETTING_LINK_SECURITY; - } - - if (enable_hs) - settings |= MGMT_SETTING_HS; - - if (enable_le) { - if (hdev->features[4] & LMP_LE) - settings |= MGMT_SETTING_LE; - } - - return settings; -} - -static u32 get_current_settings(struct hci_dev *hdev) -{ - u32 settings = 0; - - if (hdev_is_powered(hdev)) - settings |= MGMT_SETTING_POWERED; - - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - settings |= MGMT_SETTING_CONNECTABLE; - - if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - settings |= MGMT_SETTING_DISCOVERABLE; - - if (test_bit(HCI_PAIRABLE, &hdev->dev_flags)) - settings |= MGMT_SETTING_PAIRABLE; - - if (!(hdev->features[4] & LMP_NO_BREDR)) - settings |= MGMT_SETTING_BREDR; - - if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) - settings |= MGMT_SETTING_LE; - - if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) - settings |= MGMT_SETTING_LINK_SECURITY; - - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - settings |= MGMT_SETTING_SSP; - - if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags)) - settings |= MGMT_SETTING_HS; - - return settings; -} - -#define PNP_INFO_SVCLASS_ID 0x1200 - -static u8 bluetooth_base_uuid[] = { - 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static u16 get_uuid16(u8 *uuid128) -{ - u32 val; - int i; - - for (i = 0; i < 12; i++) { - if (bluetooth_base_uuid[i] != uuid128[i]) - return 0; - } - - memcpy(&val, &uuid128[12], 4); - - val = le32_to_cpu(val); - if (val > 0xffff) - return 0; - - return (u16) val; -} - -static void create_eir(struct hci_dev *hdev, u8 *data) -{ - u8 *ptr = data; - u16 eir_len = 0; - u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)]; - int i, truncated = 0; - struct bt_uuid *uuid; - size_t name_len; - - name_len = strlen(hdev->dev_name); - - if (name_len > 0) { - /* EIR Data type */ - if (name_len > 48) { - name_len = 48; - ptr[1] = EIR_NAME_SHORT; - } else - ptr[1] = EIR_NAME_COMPLETE; - - /* EIR Data length */ - ptr[0] = name_len + 1; - - memcpy(ptr + 2, hdev->dev_name, name_len); - - eir_len += (name_len + 2); - ptr += (name_len + 2); - } - - memset(uuid16_list, 0, sizeof(uuid16_list)); - - /* Group all UUID16 types */ - list_for_each_entry(uuid, &hdev->uuids, list) { - u16 uuid16; - - uuid16 = get_uuid16(uuid->uuid); - if (uuid16 == 0) - return; - - if (uuid16 < 0x1100) - continue; - - if (uuid16 == PNP_INFO_SVCLASS_ID) - continue; - - /* Stop if not enough space to put next UUID */ - if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) { - truncated = 1; - break; - } - - /* Check for duplicates */ - for (i = 0; uuid16_list[i] != 0; i++) - if (uuid16_list[i] == uuid16) - break; - - if (uuid16_list[i] == 0) { - uuid16_list[i] = uuid16; - eir_len += sizeof(u16); - } - } - - if (uuid16_list[0] != 0) { - u8 *length = ptr; - - /* EIR Data type */ - ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL; - - ptr += 2; - eir_len += 2; - - for (i = 0; uuid16_list[i] != 0; i++) { - *ptr++ = (uuid16_list[i] & 0x00ff); - *ptr++ = (uuid16_list[i] & 0xff00) >> 8; - } - - /* EIR Data length */ - *length = (i * sizeof(u16)) + 1; - } -} - -static int update_eir(struct hci_dev *hdev) -{ - struct hci_cp_write_eir cp; - - if (!hdev_is_powered(hdev)) - return 0; - - if (!(hdev->features[6] & LMP_EXT_INQ)) - return 0; - - if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - return 0; - - if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) - return 0; - - memset(&cp, 0, sizeof(cp)); - - create_eir(hdev, cp.data); - - if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) - return 0; - - memcpy(hdev->eir, cp.data, sizeof(cp.data)); - - return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); -} - -static u8 get_service_classes(struct hci_dev *hdev) -{ - struct bt_uuid *uuid; - u8 val = 0; - - list_for_each_entry(uuid, &hdev->uuids, list) - val |= uuid->svc_hint; - - return val; -} - -static int update_class(struct hci_dev *hdev) -{ - u8 cod[3]; - int err; - - BT_DBG("%s", hdev->name); - - if (!hdev_is_powered(hdev)) - return 0; - - if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) - return 0; - - cod[0] = hdev->minor_class; - cod[1] = hdev->major_class; - cod[2] = get_service_classes(hdev); - - if (memcmp(cod, hdev->dev_class, 3) == 0) - return 0; - - err = hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); - if (err == 0) - set_bit(HCI_PENDING_CLASS, &hdev->dev_flags); - - return err; -} - -static void service_cache_off(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, - service_cache.work); - - if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) - return; - - hci_dev_lock(hdev); - - update_eir(hdev); - update_class(hdev); - - hci_dev_unlock(hdev); -} - -static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev) -{ - if (test_and_set_bit(HCI_MGMT, &hdev->dev_flags)) - return; - - INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off); - - /* Non-mgmt controlled devices get this bit set - * implicitly so that pairing works for them, however - * for mgmt we require user-space to explicitly enable - * it - */ - clear_bit(HCI_PAIRABLE, &hdev->dev_flags); -} - -static int read_controller_info(struct sock *sk, struct hci_dev *hdev, - void *data, u16 data_len) -{ - struct mgmt_rp_read_info rp; - - BT_DBG("sock %p %s", sk, hdev->name); - - hci_dev_lock(hdev); - - memset(&rp, 0, sizeof(rp)); - - bacpy(&rp.bdaddr, &hdev->bdaddr); - - rp.version = hdev->hci_ver; - - put_unaligned_le16(hdev->manufacturer, &rp.manufacturer); - - rp.supported_settings = cpu_to_le32(get_supported_settings(hdev)); - rp.current_settings = cpu_to_le32(get_current_settings(hdev)); - - memcpy(rp.dev_class, hdev->dev_class, 3); - - memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name)); - memcpy(rp.short_name, hdev->short_name, sizeof(hdev->short_name)); - - hci_dev_unlock(hdev); - - return cmd_complete(sk, hdev->id, MGMT_OP_READ_INFO, 0, &rp, - sizeof(rp)); -} - -static void mgmt_pending_free(struct pending_cmd *cmd) -{ - sock_put(cmd->sk); - kfree(cmd->param); - kfree(cmd); -} - -static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, - struct hci_dev *hdev, void *data, - u16 len) -{ - struct pending_cmd *cmd; - - cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC); - if (!cmd) - return NULL; - - cmd->opcode = opcode; - cmd->index = hdev->id; - - cmd->param = kmalloc(len, GFP_ATOMIC); - if (!cmd->param) { - kfree(cmd); - return NULL; - } - - if (data) - memcpy(cmd->param, data, len); - - cmd->sk = sk; - sock_hold(sk); - - list_add(&cmd->list, &hdev->mgmt_pending); - - return cmd; -} - -static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, - void (*cb)(struct pending_cmd *cmd, void *data), - void *data) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->mgmt_pending) { - struct pending_cmd *cmd; - - cmd = list_entry(p, struct pending_cmd, list); - - if (opcode > 0 && cmd->opcode != opcode) - continue; - - cb(cmd, data); - } -} - -static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev) -{ - struct pending_cmd *cmd; - - list_for_each_entry(cmd, &hdev->mgmt_pending, list) { - if (cmd->opcode == opcode) - return cmd; - } - - return NULL; -} - -static void mgmt_pending_remove(struct pending_cmd *cmd) -{ - list_del(&cmd->list); - mgmt_pending_free(cmd); -} - -static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) -{ - __le32 settings = cpu_to_le32(get_current_settings(hdev)); - - return cmd_complete(sk, hdev->id, opcode, 0, &settings, - sizeof(settings)); -} - -static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_mode *cp = data; - struct pending_cmd *cmd; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { - cancel_delayed_work(&hdev->power_off); - - if (cp->val) { - err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev); - mgmt_powered(hdev, 1); - goto failed; - } - } - - if (!!cp->val == hdev_is_powered(hdev)) { - err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev); - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED, - MGMT_STATUS_BUSY); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - if (cp->val) - schedule_work(&hdev->power_on); - else - schedule_work(&hdev->power_off.work); - - err = 0; - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len, - struct sock *skip_sk) -{ - struct sk_buff *skb; - struct mgmt_hdr *hdr; - - skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(event); - if (hdev) - hdr->index = cpu_to_le16(hdev->id); - else - hdr->index = cpu_to_le16(MGMT_INDEX_NONE); - hdr->len = cpu_to_le16(data_len); - - if (data) - memcpy(skb_put(skb, data_len), data, data_len); - - /* Time stamp */ - __net_timestamp(skb); - - hci_send_to_control(skb, skip_sk); - kfree_skb(skb); - - return 0; -} - -static int new_settings(struct hci_dev *hdev, struct sock *skip) -{ - __le32 ev; - - ev = cpu_to_le32(get_current_settings(hdev)); - - return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip); -} - -static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_set_discoverable *cp = data; - struct pending_cmd *cmd; - u16 timeout; - u8 scan; - int err; - - BT_DBG("request for %s", hdev->name); - - timeout = get_unaligned_le16(&cp->timeout); - if (!cp->val && timeout > 0) - return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, - MGMT_STATUS_INVALID_PARAMS); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev) && timeout > 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, - MGMT_STATUS_NOT_POWERED); - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, - MGMT_STATUS_BUSY); - goto failed; - } - - if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, - MGMT_STATUS_REJECTED); - goto failed; - } - - if (!hdev_is_powered(hdev)) { - bool changed = false; - - if (!!cp->val != test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) { - change_bit(HCI_DISCOVERABLE, &hdev->dev_flags); - changed = true; - } - - err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); - if (err < 0) - goto failed; - - if (changed) - err = new_settings(hdev, sk); - - goto failed; - } - - if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) { - if (hdev->discov_timeout > 0) { - cancel_delayed_work(&hdev->discov_off); - hdev->discov_timeout = 0; - } - - if (cp->val && timeout > 0) { - hdev->discov_timeout = timeout; - queue_delayed_work(hdev->workqueue, &hdev->discov_off, - msecs_to_jiffies(hdev->discov_timeout * 1000)); - } - - err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - scan = SCAN_PAGE; - - if (cp->val) - scan |= SCAN_INQUIRY; - else - cancel_delayed_work(&hdev->discov_off); - - err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); - if (err < 0) - mgmt_pending_remove(cmd); - - if (cp->val) - hdev->discov_timeout = timeout; - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_mode *cp = data; - struct pending_cmd *cmd; - u8 scan; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - bool changed = false; - - if (!!cp->val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - changed = true; - - if (cp->val) { - set_bit(HCI_CONNECTABLE, &hdev->dev_flags); - } else { - clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); - clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); - } - - err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); - if (err < 0) - goto failed; - - if (changed) - err = new_settings(hdev, sk); - - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, - MGMT_STATUS_BUSY); - goto failed; - } - - if (!!cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { - err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - if (cp->val) { - scan = SCAN_PAGE; - } else { - scan = 0; - - if (test_bit(HCI_ISCAN, &hdev->flags) && - hdev->discov_timeout > 0) - cancel_delayed_work(&hdev->discov_off); - } - - err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); - if (err < 0) - mgmt_pending_remove(cmd); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_mode *cp = data; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (cp->val) - set_bit(HCI_PAIRABLE, &hdev->dev_flags); - else - clear_bit(HCI_PAIRABLE, &hdev->dev_flags); - - err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev); - if (err < 0) - goto failed; - - err = new_settings(hdev, sk); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_mode *cp = data; - struct pending_cmd *cmd; - u8 val; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - bool changed = false; - - if (!!cp->val != test_bit(HCI_LINK_SECURITY, - &hdev->dev_flags)) { - change_bit(HCI_LINK_SECURITY, &hdev->dev_flags); - changed = true; - } - - err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev); - if (err < 0) - goto failed; - - if (changed) - err = new_settings(hdev, sk); - - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, - MGMT_STATUS_BUSY); - goto failed; - } - - val = !!cp->val; - - if (test_bit(HCI_AUTH, &hdev->flags) == val) { - err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_LINK_SECURITY, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - err = hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(val), &val); - if (err < 0) { - mgmt_pending_remove(cmd); - goto failed; - } - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) -{ - struct mgmt_mode *cp = data; - struct pending_cmd *cmd; - u8 val; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, - MGMT_STATUS_NOT_SUPPORTED); - goto failed; - } - - val = !!cp->val; - - if (!hdev_is_powered(hdev)) { - bool changed = false; - - if (val != test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { - change_bit(HCI_SSP_ENABLED, &hdev->dev_flags); - changed = true; - } - - err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); - if (err < 0) - goto failed; - - if (changed) - err = new_settings(hdev, sk); - - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, - MGMT_STATUS_BUSY); - goto failed; - } - - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) { - err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_SSP, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val); - if (err < 0) { - mgmt_pending_remove(cmd); - goto failed; - } - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) -{ - struct mgmt_mode *cp = data; - - BT_DBG("request for %s", hdev->name); - - if (!enable_hs) - return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, - MGMT_STATUS_NOT_SUPPORTED); - - if (cp->val) - set_bit(HCI_HS_ENABLED, &hdev->dev_flags); - else - clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); - - return send_settings_rsp(sk, MGMT_OP_SET_HS, hdev); -} - -static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) -{ - struct mgmt_mode *cp = data; - struct hci_cp_write_le_host_supported hci_cp; - struct pending_cmd *cmd; - int err; - u8 val, enabled; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (!enable_le || !(hdev->features[4] & LMP_LE)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE, - MGMT_STATUS_NOT_SUPPORTED); - goto unlock; - } - - val = !!cp->val; - enabled = !!(hdev->host_features[0] & LMP_HOST_LE); - - if (!hdev_is_powered(hdev) || val == enabled) { - bool changed = false; - - if (val != test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { - change_bit(HCI_LE_ENABLED, &hdev->dev_flags); - changed = true; - } - - err = send_settings_rsp(sk, MGMT_OP_SET_LE, hdev); - if (err < 0) - goto unlock; - - if (changed) - err = new_settings(hdev, sk); - - goto unlock; - } - - if (mgmt_pending_find(MGMT_OP_SET_LE, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE, - MGMT_STATUS_BUSY); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_LE, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto unlock; - } - - memset(&hci_cp, 0, sizeof(hci_cp)); - - if (val) { - hci_cp.le = val; - hci_cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); - } - - err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp), - &hci_cp); - if (err < 0) { - mgmt_pending_remove(cmd); - goto unlock; - } - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) -{ - struct mgmt_cp_add_uuid *cp = data; - struct pending_cmd *cmd; - struct bt_uuid *uuid; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_ADD_UUID, - MGMT_STATUS_BUSY); - goto failed; - } - - uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC); - if (!uuid) { - err = -ENOMEM; - goto failed; - } - - memcpy(uuid->uuid, cp->uuid, 16); - uuid->svc_hint = cp->svc_hint; - - list_add(&uuid->list, &hdev->uuids); - - err = update_class(hdev); - if (err < 0) - goto failed; - - err = update_eir(hdev); - if (err < 0) - goto failed; - - if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0, - hdev->dev_class, 3); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_ADD_UUID, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - -failed: - hci_dev_unlock(hdev); - return err; -} - -static bool enable_service_cache(struct hci_dev *hdev) -{ - if (!hdev_is_powered(hdev)) - return false; - - if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) { - schedule_delayed_work(&hdev->service_cache, CACHE_TIMEOUT); - return true; - } - - return false; -} - -static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_remove_uuid *cp = data; - struct pending_cmd *cmd; - struct list_head *p, *n; - u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - int err, found; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID, - MGMT_STATUS_BUSY); - goto unlock; - } - - if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) { - err = hci_uuids_clear(hdev); - - if (enable_service_cache(hdev)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, - 0, hdev->dev_class, 3); - goto unlock; - } - - goto update_class; - } - - found = 0; - - list_for_each_safe(p, n, &hdev->uuids) { - struct bt_uuid *match = list_entry(p, struct bt_uuid, list); - - if (memcmp(match->uuid, cp->uuid, 16) != 0) - continue; - - list_del(&match->list); - found++; - } - - if (found == 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID, - MGMT_STATUS_INVALID_PARAMS); - goto unlock; - } - -update_class: - err = update_class(hdev); - if (err < 0) - goto unlock; - - err = update_eir(hdev); - if (err < 0) - goto unlock; - - if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0, - hdev->dev_class, 3); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_UUID, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto unlock; - } - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_set_dev_class *cp = data; - struct pending_cmd *cmd; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, - MGMT_STATUS_BUSY); - goto unlock; - } - - hdev->major_class = cp->major; - hdev->minor_class = cp->minor; - - if (!hdev_is_powered(hdev)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, - hdev->dev_class, 3); - goto unlock; - } - - if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) { - hci_dev_unlock(hdev); - cancel_delayed_work_sync(&hdev->service_cache); - hci_dev_lock(hdev); - update_eir(hdev); - } - - err = update_class(hdev); - if (err < 0) - goto unlock; - - if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, - hdev->dev_class, 3); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_DEV_CLASS, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto unlock; - } - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_load_link_keys *cp = data; - u16 key_count, expected_len; - int i; - - key_count = get_unaligned_le16(&cp->key_count); - - expected_len = sizeof(*cp) + key_count * - sizeof(struct mgmt_link_key_info); - if (expected_len != len) { - BT_ERR("load_link_keys: expected %u bytes, got %u bytes", - len, expected_len); - return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, - MGMT_STATUS_INVALID_PARAMS); - } - - BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys, - key_count); - - hci_dev_lock(hdev); - - hci_link_keys_clear(hdev); - - set_bit(HCI_LINK_KEYS, &hdev->dev_flags); - - if (cp->debug_keys) - set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); - else - clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); - - for (i = 0; i < key_count; i++) { - struct mgmt_link_key_info *key = &cp->keys[i]; - - hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val, - key->type, key->pin_len); - } - - cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0); - - hci_dev_unlock(hdev); - - return 0; -} - -static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type, struct sock *skip_sk) -{ - struct mgmt_ev_device_unpaired ev; - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = addr_type; - - return mgmt_event(MGMT_EV_DEVICE_UNPAIRED, hdev, &ev, sizeof(ev), - skip_sk); -} - -static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_unpair_device *cp = data; - struct mgmt_rp_unpair_device rp; - struct hci_cp_disconnect dc; - struct pending_cmd *cmd; - struct hci_conn *conn; - int err; - - hci_dev_lock(hdev); - - memset(&rp, 0, sizeof(rp)); - bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); - rp.addr.type = cp->addr.type; - - if (!hdev_is_powered(hdev)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, - MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); - goto unlock; - } - - if (cp->addr.type == MGMT_ADDR_BREDR) - err = hci_remove_link_key(hdev, &cp->addr.bdaddr); - else - err = hci_remove_ltk(hdev, &cp->addr.bdaddr); - - if (err < 0) { - err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, - MGMT_STATUS_NOT_PAIRED, &rp, sizeof(rp)); - goto unlock; - } - - if (cp->disconnect) { - if (cp->addr.type == MGMT_ADDR_BREDR) - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, - &cp->addr.bdaddr); - else - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, - &cp->addr.bdaddr); - } else { - conn = NULL; - } - - if (!conn) { - err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0, - &rp, sizeof(rp)); - device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp, - sizeof(*cp)); - if (!cmd) { - err = -ENOMEM; - goto unlock; - } - - put_unaligned_le16(conn->handle, &dc.handle); - dc.reason = 0x13; /* Remote User Terminated Connection */ - err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); - if (err < 0) - mgmt_pending_remove(cmd); - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_disconnect *cp = data; - struct hci_cp_disconnect dc; - struct pending_cmd *cmd; - struct hci_conn *conn; - int err; - - BT_DBG(""); - - hci_dev_lock(hdev); - - if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_NOT_POWERED); - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_BUSY); - goto failed; - } - - if (cp->addr.type == MGMT_ADDR_BREDR) - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); - else - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr); - - if (!conn) { - err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_NOT_CONNECTED); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - put_unaligned_le16(conn->handle, &dc.handle); - dc.reason = 0x13; /* Remote User Terminated Connection */ - - err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); - if (err < 0) - mgmt_pending_remove(cmd); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static u8 link_to_mgmt(u8 link_type, u8 addr_type) -{ - switch (link_type) { - case LE_LINK: - switch (addr_type) { - case ADDR_LE_DEV_PUBLIC: - return MGMT_ADDR_LE_PUBLIC; - case ADDR_LE_DEV_RANDOM: - return MGMT_ADDR_LE_RANDOM; - default: - return MGMT_ADDR_INVALID; - } - case ACL_LINK: - return MGMT_ADDR_BREDR; - default: - return MGMT_ADDR_INVALID; - } -} - -static int get_connections(struct sock *sk, struct hci_dev *hdev, void *data, - u16 data_len) -{ - struct mgmt_rp_get_connections *rp; - struct hci_conn *c; - size_t rp_len; - int err; - u16 i; - - BT_DBG(""); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, - MGMT_STATUS_NOT_POWERED); - goto unlock; - } - - i = 0; - list_for_each_entry(c, &hdev->conn_hash.list, list) { - if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags)) - i++; - } - - rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info)); - rp = kmalloc(rp_len, GFP_ATOMIC); - if (!rp) { - err = -ENOMEM; - goto unlock; - } - - i = 0; - list_for_each_entry(c, &hdev->conn_hash.list, list) { - if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags)) - continue; - bacpy(&rp->addr[i].bdaddr, &c->dst); - rp->addr[i].type = link_to_mgmt(c->type, c->dst_type); - if (rp->addr[i].type == MGMT_ADDR_INVALID) - continue; - i++; - } - - put_unaligned_le16(i, &rp->conn_count); - - /* Recalculate length in case of filtered SCO connections, etc */ - rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info)); - - err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, 0, rp, - rp_len); - - kfree(rp); - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int send_pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev, - struct mgmt_cp_pin_code_neg_reply *cp) -{ - struct pending_cmd *cmd; - int err; - - cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp, - sizeof(*cp)); - if (!cmd) - return -ENOMEM; - - err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, - sizeof(cp->addr.bdaddr), &cp->addr.bdaddr); - if (err < 0) - mgmt_pending_remove(cmd); - - return err; -} - -static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct hci_conn *conn; - struct mgmt_cp_pin_code_reply *cp = data; - struct hci_cp_pin_code_reply reply; - struct pending_cmd *cmd; - int err; - - BT_DBG(""); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - MGMT_STATUS_NOT_POWERED); - goto failed; - } - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); - if (!conn) { - err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - MGMT_STATUS_NOT_CONNECTED); - goto failed; - } - - if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) { - struct mgmt_cp_pin_code_neg_reply ncp; - - memcpy(&ncp.addr, &cp->addr, sizeof(ncp.addr)); - - BT_ERR("PIN code is not 16 bytes long"); - - err = send_pin_code_neg_reply(sk, hdev, &ncp); - if (err >= 0) - err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - MGMT_STATUS_INVALID_PARAMS); - - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - bacpy(&reply.bdaddr, &cp->addr.bdaddr); - reply.pin_len = cp->pin_len; - memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code)); - - err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply); - if (err < 0) - mgmt_pending_remove(cmd); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_cp_pin_code_neg_reply *cp = data; - int err; - - BT_DBG(""); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, - MGMT_STATUS_NOT_POWERED); - goto failed; - } - - err = send_pin_code_neg_reply(sk, hdev, cp); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_set_io_capability *cp = data; - - BT_DBG(""); - - hci_dev_lock(hdev); - - hdev->io_capability = cp->io_capability; - - BT_DBG("%s IO capability set to 0x%02x", hdev->name, - hdev->io_capability); - - hci_dev_unlock(hdev); - - return cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY, 0, NULL, - 0); -} - -static inline struct pending_cmd *find_pairing(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - struct pending_cmd *cmd; - - list_for_each_entry(cmd, &hdev->mgmt_pending, list) { - if (cmd->opcode != MGMT_OP_PAIR_DEVICE) - continue; - - if (cmd->user_data != conn) - continue; - - return cmd; - } - - return NULL; -} - -static void pairing_complete(struct pending_cmd *cmd, u8 status) -{ - struct mgmt_rp_pair_device rp; - struct hci_conn *conn = cmd->user_data; - - bacpy(&rp.addr.bdaddr, &conn->dst); - rp.addr.type = link_to_mgmt(conn->type, conn->dst_type); - - cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status, - &rp, sizeof(rp)); - - /* So we don't get further callbacks for this connection */ - conn->connect_cfm_cb = NULL; - conn->security_cfm_cb = NULL; - conn->disconn_cfm_cb = NULL; - - hci_conn_put(conn); - - mgmt_pending_remove(cmd); -} - -static void pairing_complete_cb(struct hci_conn *conn, u8 status) -{ - struct pending_cmd *cmd; - - BT_DBG("status %u", status); - - cmd = find_pairing(conn); - if (!cmd) - BT_DBG("Unable to find a pending command"); - else - pairing_complete(cmd, mgmt_status(status)); -} - -static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_pair_device *cp = data; - struct mgmt_rp_pair_device rp; - struct pending_cmd *cmd; - u8 sec_level, auth_type; - struct hci_conn *conn; - int err; - - BT_DBG(""); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_PAIR_DEVICE, - MGMT_STATUS_NOT_POWERED); - goto unlock; - } - - sec_level = BT_SECURITY_MEDIUM; - if (cp->io_cap == 0x03) - auth_type = HCI_AT_DEDICATED_BONDING; - else - auth_type = HCI_AT_DEDICATED_BONDING_MITM; - - if (cp->addr.type == MGMT_ADDR_BREDR) - conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level, - auth_type); - else - conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level, - auth_type); - - memset(&rp, 0, sizeof(rp)); - bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); - rp.addr.type = cp->addr.type; - - if (IS_ERR(conn)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, - MGMT_STATUS_CONNECT_FAILED, &rp, - sizeof(rp)); - goto unlock; - } - - if (conn->connect_cfm_cb) { - hci_conn_put(conn); - err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, - MGMT_STATUS_BUSY, &rp, sizeof(rp)); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - hci_conn_put(conn); - goto unlock; - } - - /* For LE, just connecting isn't a proof that the pairing finished */ - if (cp->addr.type == MGMT_ADDR_BREDR) - conn->connect_cfm_cb = pairing_complete_cb; - - conn->security_cfm_cb = pairing_complete_cb; - conn->disconn_cfm_cb = pairing_complete_cb; - conn->io_capability = cp->io_cap; - cmd->user_data = conn; - - if (conn->state == BT_CONNECTED && - hci_conn_security(conn, sec_level, auth_type)) - pairing_complete(cmd, 0); - - err = 0; - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_addr_info *addr = data; - struct pending_cmd *cmd; - struct hci_conn *conn; - int err; - - BT_DBG(""); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, - MGMT_STATUS_NOT_POWERED); - goto unlock; - } - - cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev); - if (!cmd) { - err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, - MGMT_STATUS_INVALID_PARAMS); - goto unlock; - } - - conn = cmd->user_data; - - if (bacmp(&addr->bdaddr, &conn->dst) != 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, - MGMT_STATUS_INVALID_PARAMS); - goto unlock; - } - - pairing_complete(cmd, MGMT_STATUS_CANCELLED); - - err = cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0, - addr, sizeof(*addr)); -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 type, u16 mgmt_op, - u16 hci_op, __le32 passkey) -{ - struct pending_cmd *cmd; - struct hci_conn *conn; - int err; - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_NOT_POWERED); - goto done; - } - - if (type == MGMT_ADDR_BREDR) - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr); - else - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr); - - if (!conn) { - err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_NOT_CONNECTED); - goto done; - } - - if (type == MGMT_ADDR_LE_PUBLIC || type == MGMT_ADDR_LE_RANDOM) { - /* Continue with pairing via SMP */ - err = smp_user_confirm_reply(conn, mgmt_op, passkey); - - if (!err) - err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_SUCCESS); - else - err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_FAILED); - - goto done; - } - - cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr)); - if (!cmd) { - err = -ENOMEM; - goto done; - } - - /* Continue with pairing via HCI */ - if (hci_op == HCI_OP_USER_PASSKEY_REPLY) { - struct hci_cp_user_passkey_reply cp; - - bacpy(&cp.bdaddr, bdaddr); - cp.passkey = passkey; - err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp); - } else - err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr); - - if (err < 0) - mgmt_pending_remove(cmd); - -done: - hci_dev_unlock(hdev); - return err; -} - -static int user_confirm_reply(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_user_confirm_reply *cp = data; - - BT_DBG(""); - - if (len != sizeof(*cp)) - return cmd_status(sk, hdev->id, MGMT_OP_USER_CONFIRM_REPLY, - MGMT_STATUS_INVALID_PARAMS); - - return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, - MGMT_OP_USER_CONFIRM_REPLY, - HCI_OP_USER_CONFIRM_REPLY, 0); -} - -static int user_confirm_neg_reply(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_cp_user_confirm_neg_reply *cp = data; - - BT_DBG(""); - - return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, - MGMT_OP_USER_CONFIRM_NEG_REPLY, - HCI_OP_USER_CONFIRM_NEG_REPLY, 0); -} - -static int user_passkey_reply(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_user_passkey_reply *cp = data; - - BT_DBG(""); - - return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, - MGMT_OP_USER_PASSKEY_REPLY, - HCI_OP_USER_PASSKEY_REPLY, cp->passkey); -} - -static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_cp_user_passkey_neg_reply *cp = data; - - BT_DBG(""); - - return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, - MGMT_OP_USER_PASSKEY_NEG_REPLY, - HCI_OP_USER_PASSKEY_NEG_REPLY, 0); -} - -static int update_name(struct hci_dev *hdev, const char *name) -{ - struct hci_cp_write_local_name cp; - - memcpy(cp.name, name, sizeof(cp.name)); - - return hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp); -} - -static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_set_local_name *cp = data; - struct pending_cmd *cmd; - int err; - - BT_DBG(""); - - hci_dev_lock(hdev); - - memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name)); - - if (!hdev_is_powered(hdev)) { - memcpy(hdev->dev_name, cp->name, sizeof(hdev->dev_name)); - - err = cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, - data, len); - if (err < 0) - goto failed; - - err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, data, len, - sk); - - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - err = update_name(hdev, cp->name); - if (err < 0) - mgmt_pending_remove(cmd); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev, - void *data, u16 data_len) -{ - struct pending_cmd *cmd; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - MGMT_STATUS_NOT_POWERED); - goto unlock; - } - - if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) { - err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - MGMT_STATUS_NOT_SUPPORTED); - goto unlock; - } - - if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - MGMT_STATUS_BUSY); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0); - if (!cmd) { - err = -ENOMEM; - goto unlock; - } - - err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL); - if (err < 0) - mgmt_pending_remove(cmd); - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_cp_add_remote_oob_data *cp = data; - u8 status; - int err; - - BT_DBG("%s ", hdev->name); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, - MGMT_STATUS_NOT_POWERED, &cp->addr, - sizeof(cp->addr)); - goto unlock; - } - - err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash, - cp->randomizer); - if (err < 0) - status = MGMT_STATUS_FAILED; - else - status = 0; - - err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status, - &cp->addr, sizeof(cp->addr)); - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_cp_remove_remote_oob_data *cp = data; - u8 status; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_complete(sk, hdev->id, - MGMT_OP_REMOVE_REMOTE_OOB_DATA, - MGMT_STATUS_NOT_POWERED, &cp->addr, - sizeof(cp->addr)); - goto unlock; - } - - err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr); - if (err < 0) - status = MGMT_STATUS_INVALID_PARAMS; - else - status = 0; - - err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA, - status, &cp->addr, sizeof(cp->addr)); - -unlock: - hci_dev_unlock(hdev); - return err; -} - -int mgmt_interleaved_discovery(struct hci_dev *hdev) -{ - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE); - if (err < 0) - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - - hci_dev_unlock(hdev); - - return err; -} - -static int start_discovery(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_cp_start_discovery *cp = data; - struct pending_cmd *cmd; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_NOT_POWERED); - goto failed; - } - - if (hdev->discovery.state != DISCOVERY_STOPPED) { - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_BUSY); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - hdev->discovery.type = cp->type; - - switch (hdev->discovery.type) { - case DISCOV_TYPE_BREDR: - if (lmp_bredr_capable(hdev)) - err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); - else - err = -ENOTSUPP; - break; - - case DISCOV_TYPE_LE: - if (lmp_host_le_capable(hdev)) - err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, - LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY); - else - err = -ENOTSUPP; - break; - - case DISCOV_TYPE_INTERLEAVED: - if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev)) - err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, - LE_SCAN_WIN, - LE_SCAN_TIMEOUT_BREDR_LE); - else - err = -ENOTSUPP; - break; - - default: - err = -EINVAL; - } - - if (err < 0) - mgmt_pending_remove(cmd); - else - hci_discovery_set_state(hdev, DISCOVERY_STARTING); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_stop_discovery *mgmt_cp = data; - struct pending_cmd *cmd; - struct hci_cp_remote_name_req_cancel cp; - struct inquiry_entry *e; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!hci_discovery_active(hdev)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, - MGMT_STATUS_REJECTED, &mgmt_cp->type, - sizeof(mgmt_cp->type)); - goto unlock; - } - - if (hdev->discovery.type != mgmt_cp->type) { - err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, - MGMT_STATUS_INVALID_PARAMS, &mgmt_cp->type, - sizeof(mgmt_cp->type)); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0); - if (!cmd) { - err = -ENOMEM; - goto unlock; - } - - if (hdev->discovery.state == DISCOVERY_FINDING) { - err = hci_cancel_inquiry(hdev); - if (err < 0) - mgmt_pending_remove(cmd); - else - hci_discovery_set_state(hdev, DISCOVERY_STOPPING); - goto unlock; - } - - e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING); - if (!e) { - mgmt_pending_remove(cmd); - err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 0, - &mgmt_cp->type, sizeof(mgmt_cp->type)); - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - goto unlock; - } - - bacpy(&cp.bdaddr, &e->data.bdaddr); - err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp), - &cp); - if (err < 0) - mgmt_pending_remove(cmd); - else - hci_discovery_set_state(hdev, DISCOVERY_STOPPING); - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_confirm_name *cp = data; - struct inquiry_entry *e; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!hci_discovery_active(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME, - MGMT_STATUS_FAILED); - goto failed; - } - - e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr); - if (!e) { - err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME, - MGMT_STATUS_INVALID_PARAMS); - goto failed; - } - - if (cp->name_known) { - e->name_state = NAME_KNOWN; - list_del(&e->list); - } else { - e->name_state = NAME_NEEDED; - hci_inquiry_cache_update_resolve(hdev, e); - } - - err = 0; - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_block_device *cp = data; - u8 status; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type); - if (err < 0) - status = MGMT_STATUS_FAILED; - else - status = 0; - - err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status, - &cp->addr, sizeof(cp->addr)); - - hci_dev_unlock(hdev); - - return err; -} - -static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_unblock_device *cp = data; - u8 status; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type); - if (err < 0) - status = MGMT_STATUS_INVALID_PARAMS; - else - status = 0; - - err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status, - &cp->addr, sizeof(cp->addr)); - - hci_dev_unlock(hdev); - - return err; -} - -static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_mode *cp = data; - struct hci_cp_write_page_scan_activity acp; - u8 type; - int err; - - BT_DBG("%s", hdev->name); - - if (!hdev_is_powered(hdev)) - return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_NOT_POWERED); - - if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_REJECTED); - - hci_dev_lock(hdev); - - if (cp->val) { - type = PAGE_SCAN_TYPE_INTERLACED; - - /* 22.5 msec page scan interval */ - acp.interval = __constant_cpu_to_le16(0x0024); - } else { - type = PAGE_SCAN_TYPE_STANDARD; /* default */ - - /* default 1.28 sec page scan */ - acp.interval = __constant_cpu_to_le16(0x0800); - } - - /* default 11.25 msec page scan window */ - acp.window = __constant_cpu_to_le16(0x0012); - - err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, sizeof(acp), - &acp); - if (err < 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_FAILED); - goto done; - } - - err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); - if (err < 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_FAILED); - goto done; - } - - err = cmd_complete(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 0, - NULL, 0); -done: - hci_dev_unlock(hdev); - return err; -} - -static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, - void *cp_data, u16 len) -{ - struct mgmt_cp_load_long_term_keys *cp = cp_data; - u16 key_count, expected_len; - int i; - - key_count = get_unaligned_le16(&cp->key_count); - - expected_len = sizeof(*cp) + key_count * - sizeof(struct mgmt_ltk_info); - if (expected_len != len) { - BT_ERR("load_keys: expected %u bytes, got %u bytes", - len, expected_len); - return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, - EINVAL); - } - - BT_DBG("%s key_count %u", hdev->name, key_count); - - hci_dev_lock(hdev); - - hci_smp_ltks_clear(hdev); - - for (i = 0; i < key_count; i++) { - struct mgmt_ltk_info *key = &cp->keys[i]; - u8 type; - - if (key->master) - type = HCI_SMP_LTK; - else - type = HCI_SMP_LTK_SLAVE; - - hci_add_ltk(hdev, &key->addr.bdaddr, key->addr.type, - type, 0, key->authenticated, key->val, - key->enc_size, key->ediv, key->rand); - } - - hci_dev_unlock(hdev); - - return 0; -} - -struct mgmt_handler { - int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, - u16 data_len); - bool var_len; - size_t data_len; -} mgmt_handlers[] = { - { NULL }, /* 0x0000 (no command) */ - { read_version, false, MGMT_READ_VERSION_SIZE }, - { read_commands, false, MGMT_READ_COMMANDS_SIZE }, - { read_index_list, false, MGMT_READ_INDEX_LIST_SIZE }, - { read_controller_info, false, MGMT_READ_INFO_SIZE }, - { set_powered, false, MGMT_SETTING_SIZE }, - { set_discoverable, false, MGMT_SET_DISCOVERABLE_SIZE }, - { set_connectable, false, MGMT_SETTING_SIZE }, - { set_fast_connectable, false, MGMT_SETTING_SIZE }, - { set_pairable, false, MGMT_SETTING_SIZE }, - { set_link_security, false, MGMT_SETTING_SIZE }, - { set_ssp, false, MGMT_SETTING_SIZE }, - { set_hs, false, MGMT_SETTING_SIZE }, - { set_le, false, MGMT_SETTING_SIZE }, - { set_dev_class, false, MGMT_SET_DEV_CLASS_SIZE }, - { set_local_name, false, MGMT_SET_LOCAL_NAME_SIZE }, - { add_uuid, false, MGMT_ADD_UUID_SIZE }, - { remove_uuid, false, MGMT_REMOVE_UUID_SIZE }, - { load_link_keys, true, MGMT_LOAD_LINK_KEYS_SIZE }, - { load_long_term_keys, true, MGMT_LOAD_LONG_TERM_KEYS_SIZE }, - { disconnect, false, MGMT_DISCONNECT_SIZE }, - { get_connections, false, MGMT_GET_CONNECTIONS_SIZE }, - { pin_code_reply, false, MGMT_PIN_CODE_REPLY_SIZE }, - { pin_code_neg_reply, false, MGMT_PIN_CODE_NEG_REPLY_SIZE }, - { set_io_capability, false, MGMT_SET_IO_CAPABILITY_SIZE }, - { pair_device, false, MGMT_PAIR_DEVICE_SIZE }, - { cancel_pair_device, false, MGMT_CANCEL_PAIR_DEVICE_SIZE }, - { unpair_device, false, MGMT_UNPAIR_DEVICE_SIZE }, - { user_confirm_reply, false, MGMT_USER_CONFIRM_REPLY_SIZE }, - { user_confirm_neg_reply, false, MGMT_USER_CONFIRM_NEG_REPLY_SIZE }, - { user_passkey_reply, false, MGMT_USER_PASSKEY_REPLY_SIZE }, - { user_passkey_neg_reply, false, MGMT_USER_PASSKEY_NEG_REPLY_SIZE }, - { read_local_oob_data, false, MGMT_READ_LOCAL_OOB_DATA_SIZE }, - { add_remote_oob_data, false, MGMT_ADD_REMOTE_OOB_DATA_SIZE }, - { remove_remote_oob_data, false, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE }, - { start_discovery, false, MGMT_START_DISCOVERY_SIZE }, - { stop_discovery, false, MGMT_STOP_DISCOVERY_SIZE }, - { confirm_name, false, MGMT_CONFIRM_NAME_SIZE }, - { block_device, false, MGMT_BLOCK_DEVICE_SIZE }, - { unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE }, -}; - - -int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) -{ - void *buf; - u8 *cp; - struct mgmt_hdr *hdr; - u16 opcode, index, len; - struct hci_dev *hdev = NULL; - struct mgmt_handler *handler; - int err; - - BT_DBG("got %zu bytes", msglen); - - if (msglen < sizeof(*hdr)) - return -EINVAL; - - buf = kmalloc(msglen, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) { - err = -EFAULT; - goto done; - } - - hdr = buf; - opcode = get_unaligned_le16(&hdr->opcode); - index = get_unaligned_le16(&hdr->index); - len = get_unaligned_le16(&hdr->len); - - if (len != msglen - sizeof(*hdr)) { - err = -EINVAL; - goto done; - } - - if (index != MGMT_INDEX_NONE) { - hdev = hci_dev_get(index); - if (!hdev) { - err = cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); - goto done; - } - } - - if (opcode >= ARRAY_SIZE(mgmt_handlers) || - mgmt_handlers[opcode].func == NULL) { - BT_DBG("Unknown op %u", opcode); - err = cmd_status(sk, index, opcode, - MGMT_STATUS_UNKNOWN_COMMAND); - goto done; - } - - if ((hdev && opcode < MGMT_OP_READ_INFO) || - (!hdev && opcode >= MGMT_OP_READ_INFO)) { - err = cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); - goto done; - } - - handler = &mgmt_handlers[opcode]; - - if ((handler->var_len && len < handler->data_len) || - (!handler->var_len && len != handler->data_len)) { - err = cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_PARAMS); - goto done; - } - - if (hdev) - mgmt_init_hdev(sk, hdev); - - cp = buf + sizeof(*hdr); - - err = handler->func(sk, hdev, cp, len); - if (err < 0) - goto done; - - err = msglen; - -done: - if (hdev) - hci_dev_put(hdev); - - kfree(buf); - return err; -} - -static void cmd_status_rsp(struct pending_cmd *cmd, void *data) -{ - u8 *status = data; - - cmd_status(cmd->sk, cmd->index, cmd->opcode, *status); - mgmt_pending_remove(cmd); -} - -int mgmt_index_added(struct hci_dev *hdev) -{ - return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); -} - -int mgmt_index_removed(struct hci_dev *hdev) -{ - u8 status = MGMT_STATUS_INVALID_INDEX; - - mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); - - return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); -} - -struct cmd_lookup { - struct sock *sk; - struct hci_dev *hdev; - u8 mgmt_status; -}; - -static void settings_rsp(struct pending_cmd *cmd, void *data) -{ - struct cmd_lookup *match = data; - - send_settings_rsp(cmd->sk, cmd->opcode, match->hdev); - - list_del(&cmd->list); - - if (match->sk == NULL) { - match->sk = cmd->sk; - sock_hold(match->sk); - } - - mgmt_pending_free(cmd); -} - -int mgmt_powered(struct hci_dev *hdev, u8 powered) -{ - struct cmd_lookup match = { NULL, hdev }; - int err; - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - return 0; - - mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); - - if (powered) { - u8 scan = 0; - - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - scan |= SCAN_PAGE; - if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - scan |= SCAN_INQUIRY; - - if (scan) - hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); - - update_class(hdev); - update_name(hdev, hdev->dev_name); - update_eir(hdev); - } else { - u8 status = MGMT_STATUS_NOT_POWERED; - mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); - } - - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); - - return err; -} - -int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) -{ - struct cmd_lookup match = { NULL, hdev }; - bool changed = false; - int err = 0; - - if (discoverable) { - if (!test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - changed = true; - } else { - if (test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - changed = true; - } - - mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, - &match); - - if (changed) - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); - - return err; -} - -int mgmt_connectable(struct hci_dev *hdev, u8 connectable) -{ - struct cmd_lookup match = { NULL, hdev }; - bool changed = false; - int err = 0; - - if (connectable) { - if (!test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - changed = true; - } else { - if (test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - changed = true; - } - - mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp, - &match); - - if (changed) - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); - - return err; -} - -int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) -{ - u8 mgmt_err = mgmt_status(status); - - if (scan & SCAN_PAGE) - mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, - cmd_status_rsp, &mgmt_err); - - if (scan & SCAN_INQUIRY) - mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, - cmd_status_rsp, &mgmt_err); - - return 0; -} - -int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent) -{ - struct mgmt_ev_new_link_key ev; - - memset(&ev, 0, sizeof(ev)); - - ev.store_hint = persistent; - bacpy(&ev.key.addr.bdaddr, &key->bdaddr); - ev.key.addr.type = MGMT_ADDR_BREDR; - ev.key.type = key->type; - memcpy(ev.key.val, key->val, 16); - ev.key.pin_len = key->pin_len; - - return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); -} - -int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent) -{ - struct mgmt_ev_new_long_term_key ev; - - memset(&ev, 0, sizeof(ev)); - - ev.store_hint = persistent; - bacpy(&ev.key.addr.bdaddr, &key->bdaddr); - ev.key.addr.type = key->bdaddr_type; - ev.key.authenticated = key->authenticated; - ev.key.enc_size = key->enc_size; - ev.key.ediv = key->ediv; - - if (key->type == HCI_SMP_LTK) - ev.key.master = 1; - - memcpy(ev.key.rand, key->rand, sizeof(key->rand)); - memcpy(ev.key.val, key->val, sizeof(key->val)); - - return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), - NULL); -} - -int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u32 flags, u8 *name, u8 name_len, - u8 *dev_class) -{ - char buf[512]; - struct mgmt_ev_device_connected *ev = (void *) buf; - u16 eir_len = 0; - - bacpy(&ev->addr.bdaddr, bdaddr); - ev->addr.type = link_to_mgmt(link_type, addr_type); - - ev->flags = __cpu_to_le32(flags); - - if (name_len > 0) - eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, - name, name_len); - - if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0) - eir_len = eir_append_data(ev->eir, eir_len, - EIR_CLASS_OF_DEV, dev_class, 3); - - put_unaligned_le16(eir_len, &ev->eir_len); - - return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf, - sizeof(*ev) + eir_len, NULL); -} - -static void disconnect_rsp(struct pending_cmd *cmd, void *data) -{ - struct mgmt_cp_disconnect *cp = cmd->param; - struct sock **sk = data; - struct mgmt_rp_disconnect rp; - - bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); - rp.addr.type = cp->addr.type; - - cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp, - sizeof(rp)); - - *sk = cmd->sk; - sock_hold(*sk); - - mgmt_pending_remove(cmd); -} - -static void unpair_device_rsp(struct pending_cmd *cmd, void *data) -{ - struct hci_dev *hdev = data; - struct mgmt_cp_unpair_device *cp = cmd->param; - struct mgmt_rp_unpair_device rp; - - memset(&rp, 0, sizeof(rp)); - bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); - rp.addr.type = cp->addr.type; - - device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk); - - cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp)); - - mgmt_pending_remove(cmd); -} - -int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type) -{ - struct mgmt_addr_info ev; - struct sock *sk = NULL; - int err; - - mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk); - - bacpy(&ev.bdaddr, bdaddr); - ev.type = link_to_mgmt(link_type, addr_type); - - err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev), - sk); - - if (sk) - sock_put(sk); - - mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, - hdev); - - return err; -} - -int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) -{ - struct mgmt_rp_disconnect rp; - struct pending_cmd *cmd; - int err; - - cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev); - if (!cmd) - return -ENOENT; - - bacpy(&rp.addr.bdaddr, bdaddr); - rp.addr.type = link_to_mgmt(link_type, addr_type); - - err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, - mgmt_status(status), &rp, sizeof(rp)); - - mgmt_pending_remove(cmd); - - mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, - hdev); - return err; -} - -int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 status) -{ - struct mgmt_ev_connect_failed ev; - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = link_to_mgmt(link_type, addr_type); - ev.status = mgmt_status(status); - - return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL); -} - -int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure) -{ - struct mgmt_ev_pin_code_request ev; - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = MGMT_ADDR_BREDR; - ev.secure = secure; - - return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), - NULL); -} - -int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status) -{ - struct pending_cmd *cmd; - struct mgmt_rp_pin_code_reply rp; - int err; - - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev); - if (!cmd) - return -ENOENT; - - bacpy(&rp.addr.bdaddr, bdaddr); - rp.addr.type = MGMT_ADDR_BREDR; - - err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - mgmt_status(status), &rp, sizeof(rp)); - - mgmt_pending_remove(cmd); - - return err; -} - -int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status) -{ - struct pending_cmd *cmd; - struct mgmt_rp_pin_code_reply rp; - int err; - - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev); - if (!cmd) - return -ENOENT; - - bacpy(&rp.addr.bdaddr, bdaddr); - rp.addr.type = MGMT_ADDR_BREDR; - - err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, - mgmt_status(status), &rp, sizeof(rp)); - - mgmt_pending_remove(cmd); - - return err; -} - -int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, __le32 value, - u8 confirm_hint) -{ - struct mgmt_ev_user_confirm_request ev; - - BT_DBG("%s", hdev->name); - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = link_to_mgmt(link_type, addr_type); - ev.confirm_hint = confirm_hint; - put_unaligned_le32(value, &ev.value); - - return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev), - NULL); -} - -int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type) -{ - struct mgmt_ev_user_passkey_request ev; - - BT_DBG("%s", hdev->name); - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = link_to_mgmt(link_type, addr_type); - - return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev), - NULL); -} - -static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status, - u8 opcode) -{ - struct pending_cmd *cmd; - struct mgmt_rp_user_confirm_reply rp; - int err; - - cmd = mgmt_pending_find(opcode, hdev); - if (!cmd) - return -ENOENT; - - bacpy(&rp.addr.bdaddr, bdaddr); - rp.addr.type = link_to_mgmt(link_type, addr_type); - err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status), - &rp, sizeof(rp)); - - mgmt_pending_remove(cmd); - - return err; -} - -int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) -{ - return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, - status, MGMT_OP_USER_CONFIRM_REPLY); -} - -int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) -{ - return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, - status, MGMT_OP_USER_CONFIRM_NEG_REPLY); -} - -int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) -{ - return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, - status, MGMT_OP_USER_PASSKEY_REPLY); -} - -int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) -{ - return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, - status, MGMT_OP_USER_PASSKEY_NEG_REPLY); -} - -int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 status) -{ - struct mgmt_ev_auth_failed ev; - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = link_to_mgmt(link_type, addr_type); - ev.status = mgmt_status(status); - - return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL); -} - -int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) -{ - struct cmd_lookup match = { NULL, hdev }; - bool changed = false; - int err = 0; - - if (status) { - u8 mgmt_err = mgmt_status(status); - mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, - cmd_status_rsp, &mgmt_err); - return 0; - } - - if (test_bit(HCI_AUTH, &hdev->flags)) { - if (!test_and_set_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) - changed = true; - } else { - if (test_and_clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) - changed = true; - } - - mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp, - &match); - - if (changed) - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); - - return err; -} - -static int clear_eir(struct hci_dev *hdev) -{ - struct hci_cp_write_eir cp; - - if (!(hdev->features[6] & LMP_EXT_INQ)) - return 0; - - memset(hdev->eir, 0, sizeof(hdev->eir)); - - memset(&cp, 0, sizeof(cp)); - - return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); -} - -int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) -{ - struct cmd_lookup match = { NULL, hdev }; - bool changed = false; - int err = 0; - - if (status) { - u8 mgmt_err = mgmt_status(status); - - if (enable && test_and_clear_bit(HCI_SSP_ENABLED, - &hdev->dev_flags)) - err = new_settings(hdev, NULL); - - mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, cmd_status_rsp, - &mgmt_err); - - return err; - } - - if (enable) { - if (!test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - changed = true; - } else { - if (test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - changed = true; - } - - mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match); - - if (changed) - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); - - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - update_eir(hdev); - else - clear_eir(hdev); - - return err; -} - -static void class_rsp(struct pending_cmd *cmd, void *data) -{ - struct cmd_lookup *match = data; - - cmd_complete(cmd->sk, cmd->index, cmd->opcode, match->mgmt_status, - match->hdev->dev_class, 3); - - list_del(&cmd->list); - - if (match->sk == NULL) { - match->sk = cmd->sk; - sock_hold(match->sk); - } - - mgmt_pending_free(cmd); -} - -int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, - u8 status) -{ - struct cmd_lookup match = { NULL, hdev, mgmt_status(status) }; - int err = 0; - - clear_bit(HCI_PENDING_CLASS, &hdev->dev_flags); - - mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, class_rsp, &match); - mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, class_rsp, &match); - mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, class_rsp, &match); - - if (!status) - err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, - 3, NULL); - - if (match.sk) - sock_put(match.sk); - - return err; -} - -int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) -{ - struct pending_cmd *cmd; - struct mgmt_cp_set_local_name ev; - bool changed = false; - int err = 0; - - if (memcmp(name, hdev->dev_name, sizeof(hdev->dev_name)) != 0) { - memcpy(hdev->dev_name, name, sizeof(hdev->dev_name)); - changed = true; - } - - memset(&ev, 0, sizeof(ev)); - memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); - memcpy(ev.short_name, hdev->short_name, HCI_MAX_SHORT_NAME_LENGTH); - - cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); - if (!cmd) - goto send_event; - - /* Always assume that either the short or the complete name has - * changed if there was a pending mgmt command */ - changed = true; - - if (status) { - err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, - mgmt_status(status)); - goto failed; - } - - err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev, - sizeof(ev)); - if (err < 0) - goto failed; - -send_event: - if (changed) - err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, - sizeof(ev), cmd ? cmd->sk : NULL); - - update_eir(hdev); - -failed: - if (cmd) - mgmt_pending_remove(cmd); - return err; -} - -int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, - u8 *randomizer, u8 status) -{ - struct pending_cmd *cmd; - int err; - - BT_DBG("%s status %u", hdev->name, status); - - cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev); - if (!cmd) - return -ENOENT; - - if (status) { - err = cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - mgmt_status(status)); - } else { - struct mgmt_rp_read_local_oob_data rp; - - memcpy(rp.hash, hash, sizeof(rp.hash)); - memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer)); - - err = cmd_complete(cmd->sk, hdev->id, - MGMT_OP_READ_LOCAL_OOB_DATA, 0, &rp, - sizeof(rp)); - } - - mgmt_pending_remove(cmd); - - return err; -} - -int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) -{ - struct cmd_lookup match = { NULL, hdev }; - bool changed = false; - int err = 0; - - if (status) { - u8 mgmt_err = mgmt_status(status); - - if (enable && test_and_clear_bit(HCI_LE_ENABLED, - &hdev->dev_flags)) - err = new_settings(hdev, NULL); - - mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, - cmd_status_rsp, &mgmt_err); - - return err; - } - - if (enable) { - if (!test_and_set_bit(HCI_LE_ENABLED, &hdev->dev_flags)) - changed = true; - } else { - if (test_and_clear_bit(HCI_LE_ENABLED, &hdev->dev_flags)) - changed = true; - } - - mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match); - - if (changed) - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); - - return err; -} - -int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 - ssp, u8 *eir, u16 eir_len) -{ - char buf[512]; - struct mgmt_ev_device_found *ev = (void *) buf; - size_t ev_size; - - /* Leave 5 bytes for a potential CoD field */ - if (sizeof(*ev) + eir_len + 5 > sizeof(buf)) - return -EINVAL; - - memset(buf, 0, sizeof(buf)); - - bacpy(&ev->addr.bdaddr, bdaddr); - ev->addr.type = link_to_mgmt(link_type, addr_type); - ev->rssi = rssi; - if (cfm_name) - ev->flags[0] |= MGMT_DEV_FOUND_CONFIRM_NAME; - if (!ssp) - ev->flags[0] |= MGMT_DEV_FOUND_LEGACY_PAIRING; - - if (eir_len > 0) - memcpy(ev->eir, eir, eir_len); - - if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV)) - eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, - dev_class, 3); - - put_unaligned_le16(eir_len, &ev->eir_len); - - ev_size = sizeof(*ev) + eir_len; - - return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL); -} - -int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, s8 rssi, u8 *name, u8 name_len) -{ - struct mgmt_ev_device_found *ev; - char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2]; - u16 eir_len; - - ev = (struct mgmt_ev_device_found *) buf; - - memset(buf, 0, sizeof(buf)); - - bacpy(&ev->addr.bdaddr, bdaddr); - ev->addr.type = link_to_mgmt(link_type, addr_type); - ev->rssi = rssi; - - eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name, - name_len); - - put_unaligned_le16(eir_len, &ev->eir_len); - - return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, - sizeof(*ev) + eir_len, NULL); -} - -int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status) -{ - struct pending_cmd *cmd; - u8 type; - int err; - - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - - cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); - if (!cmd) - return -ENOENT; - - type = hdev->discovery.type; - - err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status), - &type, sizeof(type)); - mgmt_pending_remove(cmd); - - return err; -} - -int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status) -{ - struct pending_cmd *cmd; - int err; - - cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); - if (!cmd) - return -ENOENT; - - err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status), - &hdev->discovery.type, sizeof(hdev->discovery.type)); - mgmt_pending_remove(cmd); - - return err; -} - -int mgmt_discovering(struct hci_dev *hdev, u8 discovering) -{ - struct mgmt_ev_discovering ev; - struct pending_cmd *cmd; - - BT_DBG("%s discovering %u", hdev->name, discovering); - - if (discovering) - cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); - else - cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); - - if (cmd != NULL) { - u8 type = hdev->discovery.type; - - cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type, - sizeof(type)); - mgmt_pending_remove(cmd); - } - - memset(&ev, 0, sizeof(ev)); - ev.type = hdev->discovery.type; - ev.discovering = discovering; - - return mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); -} - -int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct pending_cmd *cmd; - struct mgmt_ev_device_blocked ev; - - cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev); - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = type; - - return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); -} - -int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct pending_cmd *cmd; - struct mgmt_ev_device_unblocked ev; - - cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev); - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = type; - - return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); -} - -module_param(enable_hs, bool, 0644); -MODULE_PARM_DESC(enable_hs, "Enable High Speed support"); - -module_param(enable_le, bool, 0644); -MODULE_PARM_DESC(enable_le, "Enable Low Energy support"); diff --git a/net/bluetooth_tizen/rfcomm/Kconfig b/net/bluetooth_tizen/rfcomm/Kconfig deleted file mode 100644 index 22e718b..0000000 --- a/net/bluetooth_tizen/rfcomm/Kconfig +++ /dev/null @@ -1,17 +0,0 @@ -config BT_RFCOMM - tristate "RFCOMM protocol support" - depends on BT - help - RFCOMM provides connection oriented stream transport. RFCOMM - support is required for Dialup Networking, OBEX and other Bluetooth - applications. - - Say Y here to compile RFCOMM support into the kernel or say M to - compile it as module (rfcomm). - -config BT_RFCOMM_TTY - bool "RFCOMM TTY support" - depends on BT_RFCOMM - help - This option enables TTY emulation support for RFCOMM channels. - diff --git a/net/bluetooth_tizen/rfcomm/Makefile b/net/bluetooth_tizen/rfcomm/Makefile deleted file mode 100644 index fe07988..0000000 --- a/net/bluetooth_tizen/rfcomm/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# Makefile for the Linux Bluetooth RFCOMM layer. -# - -obj-$(CONFIG_BT_RFCOMM) += rfcomm.o - -rfcomm-y := core.o sock.o -rfcomm-$(CONFIG_BT_RFCOMM_TTY) += tty.o diff --git a/net/bluetooth_tizen/rfcomm/core.c b/net/bluetooth_tizen/rfcomm/core.c deleted file mode 100644 index 8a60238..0000000 --- a/net/bluetooth_tizen/rfcomm/core.c +++ /dev/null @@ -1,2245 +0,0 @@ -/* - RFCOMM implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> - Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* - * Bluetooth RFCOMM core. - */ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/signal.h> -#include <linux/init.h> -#include <linux/wait.h> -#include <linux/device.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/net.h> -#include <linux/mutex.h> -#include <linux/kthread.h> -#include <linux/slab.h> - -#include <net/sock.h> -#include <linux/uaccess.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> -#include <net/bluetooth/rfcomm.h> - -#define VERSION "1.11" - -static bool disable_cfc; -static bool l2cap_ertm; -static int channel_mtu = -1; -static unsigned int l2cap_mtu = RFCOMM_MAX_L2CAP_MTU; - -static struct task_struct *rfcomm_thread; - -static DEFINE_MUTEX(rfcomm_mutex); -#define rfcomm_lock() mutex_lock(&rfcomm_mutex) -#define rfcomm_unlock() mutex_unlock(&rfcomm_mutex) - - -static LIST_HEAD(session_list); - -static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len); -static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci); -static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci); -static int rfcomm_queue_disc(struct rfcomm_dlc *d); -static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type); -static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d); -static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig); -static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len); -static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits); -static void rfcomm_make_uih(struct sk_buff *skb, u8 addr); - -static void rfcomm_process_connect(struct rfcomm_session *s); - -static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, - bdaddr_t *dst, - u8 sec_level, - int *err); -static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst); -static void rfcomm_session_del(struct rfcomm_session *s); - -/* ---- RFCOMM frame parsing macros ---- */ -#define __get_dlci(b) ((b & 0xfc) >> 2) -#define __get_channel(b) ((b & 0xf8) >> 3) -#define __get_dir(b) ((b & 0x04) >> 2) -#define __get_type(b) ((b & 0xef)) - -#define __test_ea(b) ((b & 0x01)) -#define __test_cr(b) ((b & 0x02)) -#define __test_pf(b) ((b & 0x10)) - -#define __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01) -#define __ctrl(type, pf) (((type & 0xef) | (pf << 4))) -#define __dlci(dir, chn) (((chn & 0x1f) << 1) | dir) -#define __srv_channel(dlci) (dlci >> 1) -#define __dir(dlci) (dlci & 0x01) - -#define __len8(len) (((len) << 1) | 1) -#define __len16(len) ((len) << 1) - -/* MCC macros */ -#define __mcc_type(cr, type) (((type << 2) | (cr << 1) | 0x01)) -#define __get_mcc_type(b) ((b & 0xfc) >> 2) -#define __get_mcc_len(b) ((b & 0xfe) >> 1) - -/* RPN macros */ -#define __rpn_line_settings(data, stop, parity) ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x7) << 3)) -#define __get_rpn_data_bits(line) ((line) & 0x3) -#define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1) -#define __get_rpn_parity(line) (((line) >> 3) & 0x7) - -static inline void rfcomm_schedule(void) -{ - if (!rfcomm_thread) - return; - wake_up_process(rfcomm_thread); -} - -static inline void rfcomm_session_put(struct rfcomm_session *s) -{ - if (atomic_dec_and_test(&s->refcnt)) - rfcomm_session_del(s); -} - -/* ---- RFCOMM FCS computation ---- */ - -/* reversed, 8-bit, poly=0x07 */ -static unsigned char rfcomm_crc_table[256] = { - 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, - 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, - 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, - 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, - - 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, - 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, - 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, - 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, - - 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, - 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, - 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, - 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, - - 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, - 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, - 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, - 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, - - 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, - 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, - 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, - 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, - - 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, - 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, - 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, - 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, - - 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, - 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, - 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, - 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, - - 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, - 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, - 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, - 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf -}; - -/* CRC on 2 bytes */ -#define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]]) - -/* FCS on 2 bytes */ -static inline u8 __fcs(u8 *data) -{ - return 0xff - __crc(data); -} - -/* FCS on 3 bytes */ -static inline u8 __fcs2(u8 *data) -{ - return 0xff - rfcomm_crc_table[__crc(data) ^ data[2]]; -} - -/* Check FCS */ -static inline int __check_fcs(u8 *data, int type, u8 fcs) -{ - u8 f = __crc(data); - - if (type != RFCOMM_UIH) - f = rfcomm_crc_table[f ^ data[2]]; - - return rfcomm_crc_table[f ^ fcs] != 0xcf; -} - -/* ---- L2CAP callbacks ---- */ -static void rfcomm_l2state_change(struct sock *sk) -{ - BT_DBG("%p state %d", sk, sk->sk_state); - rfcomm_schedule(); -} - -static void rfcomm_l2data_ready(struct sock *sk, int bytes) -{ - BT_DBG("%p bytes %d", sk, bytes); - rfcomm_schedule(); -} - -static int rfcomm_l2sock_create(struct socket **sock) -{ - int err; - - BT_DBG(""); - - err = sock_create_kern(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock); - if (!err) { - struct sock *sk = (*sock)->sk; - sk->sk_data_ready = rfcomm_l2data_ready; - sk->sk_state_change = rfcomm_l2state_change; - } - return err; -} - -static inline int rfcomm_check_security(struct rfcomm_dlc *d) -{ - struct sock *sk = d->session->sock->sk; - struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; - - __u8 auth_type; - - switch (d->sec_level) { - case BT_SECURITY_HIGH: - auth_type = HCI_AT_GENERAL_BONDING_MITM; - break; - case BT_SECURITY_MEDIUM: - auth_type = HCI_AT_GENERAL_BONDING; - break; - default: - auth_type = HCI_AT_NO_BONDING; - break; - } - - return hci_conn_security(conn->hcon, d->sec_level, auth_type); -} - -static void rfcomm_session_timeout(unsigned long arg) -{ - struct rfcomm_session *s = (void *) arg; - - BT_DBG("session %p state %ld", s, s->state); - - set_bit(RFCOMM_TIMED_OUT, &s->flags); - rfcomm_schedule(); -} - -static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout) -{ - BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout); - - if (!mod_timer(&s->timer, jiffies + timeout)) - rfcomm_session_hold(s); -} - -static void rfcomm_session_clear_timer(struct rfcomm_session *s) -{ - BT_DBG("session %p state %ld", s, s->state); - - if (timer_pending(&s->timer) && del_timer(&s->timer)) - rfcomm_session_put(s); -} - -/* ---- RFCOMM DLCs ---- */ -static void rfcomm_dlc_timeout(unsigned long arg) -{ - struct rfcomm_dlc *d = (void *) arg; - - BT_DBG("dlc %p state %ld", d, d->state); - - set_bit(RFCOMM_TIMED_OUT, &d->flags); - rfcomm_dlc_put(d); - rfcomm_schedule(); -} - -static void rfcomm_dlc_set_timer(struct rfcomm_dlc *d, long timeout) -{ - BT_DBG("dlc %p state %ld timeout %ld", d, d->state, timeout); - - if (!mod_timer(&d->timer, jiffies + timeout)) - rfcomm_dlc_hold(d); -} - -static void rfcomm_dlc_clear_timer(struct rfcomm_dlc *d) -{ - BT_DBG("dlc %p state %ld", d, d->state); - - if (timer_pending(&d->timer) && del_timer(&d->timer)) - rfcomm_dlc_put(d); -} - -static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d) -{ - BT_DBG("%p", d); - - d->state = BT_OPEN; - d->flags = 0; - d->mscex = 0; - d->sec_level = BT_SECURITY_LOW; - d->mtu = RFCOMM_DEFAULT_MTU; - d->v24_sig = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV; - - d->cfc = RFCOMM_CFC_DISABLED; - d->rx_credits = RFCOMM_DEFAULT_CREDITS; -} - -struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio) -{ - struct rfcomm_dlc *d = kzalloc(sizeof(*d), prio); - - if (!d) - return NULL; - - setup_timer(&d->timer, rfcomm_dlc_timeout, (unsigned long)d); - - skb_queue_head_init(&d->tx_queue); - spin_lock_init(&d->lock); - atomic_set(&d->refcnt, 1); - - rfcomm_dlc_clear_state(d); - - BT_DBG("%p", d); - - return d; -} - -void rfcomm_dlc_free(struct rfcomm_dlc *d) -{ - BT_DBG("%p", d); - - skb_queue_purge(&d->tx_queue); - kfree(d); -} - -static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d) -{ - BT_DBG("dlc %p session %p", d, s); - - rfcomm_session_hold(s); - - rfcomm_session_clear_timer(s); - rfcomm_dlc_hold(d); - list_add(&d->list, &s->dlcs); - d->session = s; -} - -static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) -{ - struct rfcomm_session *s = d->session; - - BT_DBG("dlc %p refcnt %d session %p", d, atomic_read(&d->refcnt), s); - - list_del(&d->list); - d->session = NULL; - rfcomm_dlc_put(d); - - if (list_empty(&s->dlcs)) - rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT); - - rfcomm_session_put(s); -} - -static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci) -{ - struct rfcomm_dlc *d; - - list_for_each_entry(d, &s->dlcs, list) - if (d->dlci == dlci) - return d; - - return NULL; -} - -static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) -{ - struct rfcomm_session *s; - int err = 0; - u8 dlci; - - BT_DBG("dlc %p state %ld %s %s channel %d", - d, d->state, batostr(src), batostr(dst), channel); - - if (channel < 1 || channel > 30) - return -EINVAL; - - if (d->state != BT_OPEN && d->state != BT_CLOSED) - return 0; - - s = rfcomm_session_get(src, dst); - if (!s) { - s = rfcomm_session_create(src, dst, d->sec_level, &err); - if (!s) - return err; - } - - dlci = __dlci(!s->initiator, channel); - - /* Check if DLCI already exists */ - if (rfcomm_dlc_get(s, dlci)) - return -EBUSY; - - rfcomm_dlc_clear_state(d); - - d->dlci = dlci; - d->addr = __addr(s->initiator, dlci); - d->priority = 7; - - d->state = BT_CONFIG; - rfcomm_dlc_link(s, d); - - d->out = 1; - - d->mtu = s->mtu; - d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc; - - if (s->state == BT_CONNECTED) { - if (rfcomm_check_security(d)) - rfcomm_send_pn(s, 1, d); - else - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - } - - rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); - - return 0; -} - -int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) -{ - int r; - - rfcomm_lock(); - - r = __rfcomm_dlc_open(d, src, dst, channel); - - rfcomm_unlock(); - return r; -} - -static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) -{ - struct rfcomm_session *s = d->session; - if (!s) - return 0; - - BT_DBG("dlc %p state %ld dlci %d err %d session %p", - d, d->state, d->dlci, err, s); - - switch (d->state) { - case BT_CONNECT: - case BT_CONFIG: - if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { - set_bit(RFCOMM_AUTH_REJECT, &d->flags); - rfcomm_schedule(); - break; - } - /* Fall through */ - - case BT_CONNECTED: - d->state = BT_DISCONN; - if (skb_queue_empty(&d->tx_queue)) { - rfcomm_send_disc(s, d->dlci); - rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT); - } else { - rfcomm_queue_disc(d); - rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2); - } - break; - - case BT_OPEN: - case BT_CONNECT2: - if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { - set_bit(RFCOMM_AUTH_REJECT, &d->flags); - rfcomm_schedule(); - break; - } - /* Fall through */ - - default: - rfcomm_dlc_clear_timer(d); - - rfcomm_dlc_lock(d); - d->state = BT_CLOSED; - d->state_change(d, err); - rfcomm_dlc_unlock(d); - - skb_queue_purge(&d->tx_queue); - rfcomm_dlc_unlink(d); - } - - return 0; -} - -int rfcomm_dlc_close(struct rfcomm_dlc *d, int err) -{ - int r; - - rfcomm_lock(); - - r = __rfcomm_dlc_close(d, err); - - rfcomm_unlock(); - return r; -} - -int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb) -{ - int len = skb->len; - - if (d->state != BT_CONNECTED) - return -ENOTCONN; - - BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len); - - if (len > d->mtu) - return -EINVAL; - - rfcomm_make_uih(skb, d->addr); - skb_queue_tail(&d->tx_queue, skb); - - if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags)) - rfcomm_schedule(); - return len; -} - -void __rfcomm_dlc_throttle(struct rfcomm_dlc *d) -{ - BT_DBG("dlc %p state %ld", d, d->state); - - if (!d->cfc) { - d->v24_sig |= RFCOMM_V24_FC; - set_bit(RFCOMM_MSC_PENDING, &d->flags); - } - rfcomm_schedule(); -} - -void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) -{ - BT_DBG("dlc %p state %ld", d, d->state); - - if (!d->cfc) { - d->v24_sig &= ~RFCOMM_V24_FC; - set_bit(RFCOMM_MSC_PENDING, &d->flags); - } - rfcomm_schedule(); -} - -/* - Set/get modem status functions use _local_ status i.e. what we report - to the other side. - Remote status is provided by dlc->modem_status() callback. - */ -int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig) -{ - BT_DBG("dlc %p state %ld v24_sig 0x%x", - d, d->state, v24_sig); - - if (test_bit(RFCOMM_RX_THROTTLED, &d->flags)) - v24_sig |= RFCOMM_V24_FC; - else - v24_sig &= ~RFCOMM_V24_FC; - - d->v24_sig = v24_sig; - - if (!test_and_set_bit(RFCOMM_MSC_PENDING, &d->flags)) - rfcomm_schedule(); - - return 0; -} - -int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig) -{ - BT_DBG("dlc %p state %ld v24_sig 0x%x", - d, d->state, d->v24_sig); - - *v24_sig = d->v24_sig; - return 0; -} - -/* ---- RFCOMM sessions ---- */ -static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) -{ - struct rfcomm_session *s = kzalloc(sizeof(*s), GFP_KERNEL); - - if (!s) - return NULL; - - BT_DBG("session %p sock %p", s, sock); - - setup_timer(&s->timer, rfcomm_session_timeout, (unsigned long) s); - - INIT_LIST_HEAD(&s->dlcs); - s->state = state; - s->sock = sock; - - s->mtu = RFCOMM_DEFAULT_MTU; - s->cfc = disable_cfc ? RFCOMM_CFC_DISABLED : RFCOMM_CFC_UNKNOWN; - - /* Do not increment module usage count for listening sessions. - * Otherwise we won't be able to unload the module. */ - if (state != BT_LISTEN) - if (!try_module_get(THIS_MODULE)) { - kfree(s); - return NULL; - } - - list_add(&s->list, &session_list); - - return s; -} - -static void rfcomm_session_del(struct rfcomm_session *s) -{ - int state = s->state; - - BT_DBG("session %p state %ld", s, s->state); - - list_del(&s->list); - - if (state == BT_CONNECTED) - rfcomm_send_disc(s, 0); - - rfcomm_session_clear_timer(s); - sock_release(s->sock); - kfree(s); - - if (state != BT_LISTEN) - module_put(THIS_MODULE); -} - -static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst) -{ - struct rfcomm_session *s; - struct list_head *p, *n; - struct bt_sock *sk; - list_for_each_safe(p, n, &session_list) { - s = list_entry(p, struct rfcomm_session, list); - sk = bt_sk(s->sock->sk); - - if ((!bacmp(src, BDADDR_ANY) || !bacmp(&sk->src, src)) && - !bacmp(&sk->dst, dst)) - return s; - } - return NULL; -} - -static void rfcomm_session_close(struct rfcomm_session *s, int err) -{ - struct rfcomm_dlc *d; - struct list_head *p, *n; - - BT_DBG("session %p state %ld err %d", s, s->state, err); - - rfcomm_session_hold(s); - - s->state = BT_CLOSED; - - /* Close all dlcs */ - list_for_each_safe(p, n, &s->dlcs) { - d = list_entry(p, struct rfcomm_dlc, list); - d->state = BT_CLOSED; - __rfcomm_dlc_close(d, err); - } - - rfcomm_session_clear_timer(s); - rfcomm_session_put(s); -} - -static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, - bdaddr_t *dst, - u8 sec_level, - int *err) -{ - struct rfcomm_session *s = NULL; - struct sockaddr_l2 addr; - struct socket *sock; - struct sock *sk; - - BT_DBG("%s %s", batostr(src), batostr(dst)); - - *err = rfcomm_l2sock_create(&sock); - if (*err < 0) - return NULL; - - bacpy(&addr.l2_bdaddr, src); - addr.l2_family = AF_BLUETOOTH; - addr.l2_psm = 0; - addr.l2_cid = 0; - *err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr)); - if (*err < 0) - goto failed; - - /* Set L2CAP options */ - sk = sock->sk; - lock_sock(sk); - l2cap_pi(sk)->chan->imtu = l2cap_mtu; - l2cap_pi(sk)->chan->sec_level = sec_level; - if (l2cap_ertm) - l2cap_pi(sk)->chan->mode = L2CAP_MODE_ERTM; - release_sock(sk); - - s = rfcomm_session_add(sock, BT_BOUND); - if (!s) { - *err = -ENOMEM; - goto failed; - } - - s->initiator = 1; - - bacpy(&addr.l2_bdaddr, dst); - addr.l2_family = AF_BLUETOOTH; - addr.l2_psm = 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) - return s; - - rfcomm_session_del(s); - return NULL; - -failed: - sock_release(sock); - return NULL; -} - -void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst) -{ - struct sock *sk = s->sock->sk; - if (src) - bacpy(src, &bt_sk(sk)->src); - if (dst) - bacpy(dst, &bt_sk(sk)->dst); -} - -/* ---- RFCOMM frame sending ---- */ -static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len) -{ - struct kvec iv = { data, len }; - struct msghdr msg; - - BT_DBG("session %p len %d", s, len); - - memset(&msg, 0, sizeof(msg)); - - return kernel_sendmsg(s->sock, &msg, &iv, 1, len); -} - -static int rfcomm_send_cmd(struct rfcomm_session *s, struct rfcomm_cmd *cmd) -{ - BT_DBG("%p cmd %u", s, cmd->ctrl); - - return rfcomm_send_frame(s, (void *) cmd, sizeof(*cmd)); -} - -static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci) -{ - struct rfcomm_cmd cmd; - - BT_DBG("%p dlci %d", s, dlci); - - cmd.addr = __addr(s->initiator, dlci); - cmd.ctrl = __ctrl(RFCOMM_SABM, 1); - cmd.len = __len8(0); - cmd.fcs = __fcs2((u8 *) &cmd); - - return rfcomm_send_cmd(s, &cmd); -} - -static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci) -{ - struct rfcomm_cmd cmd; - - BT_DBG("%p dlci %d", s, dlci); - - cmd.addr = __addr(!s->initiator, dlci); - cmd.ctrl = __ctrl(RFCOMM_UA, 1); - cmd.len = __len8(0); - cmd.fcs = __fcs2((u8 *) &cmd); - - return rfcomm_send_cmd(s, &cmd); -} - -static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci) -{ - struct rfcomm_cmd cmd; - - BT_DBG("%p dlci %d", s, dlci); - - cmd.addr = __addr(s->initiator, dlci); - cmd.ctrl = __ctrl(RFCOMM_DISC, 1); - cmd.len = __len8(0); - cmd.fcs = __fcs2((u8 *) &cmd); - - return rfcomm_send_cmd(s, &cmd); -} - -static int rfcomm_queue_disc(struct rfcomm_dlc *d) -{ - struct rfcomm_cmd *cmd; - struct sk_buff *skb; - - BT_DBG("dlc %p dlci %d", d, d->dlci); - - skb = alloc_skb(sizeof(*cmd), GFP_KERNEL); - if (!skb) - return -ENOMEM; - - cmd = (void *) __skb_put(skb, sizeof(*cmd)); - cmd->addr = d->addr; - cmd->ctrl = __ctrl(RFCOMM_DISC, 1); - cmd->len = __len8(0); - cmd->fcs = __fcs2((u8 *) cmd); - - skb_queue_tail(&d->tx_queue, skb); - rfcomm_schedule(); - return 0; -} - -static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci) -{ - struct rfcomm_cmd cmd; - - BT_DBG("%p dlci %d", s, dlci); - - cmd.addr = __addr(!s->initiator, dlci); - cmd.ctrl = __ctrl(RFCOMM_DM, 1); - cmd.len = __len8(0); - cmd.fcs = __fcs2((u8 *) &cmd); - - return rfcomm_send_cmd(s, &cmd); -} - -static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d type %d", s, cr, type); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc) + 1); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_NSC); - mcc->len = __len8(1); - - /* Type that we didn't like */ - *ptr = __mcc_type(cr, type); ptr++; - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - struct rfcomm_pn *pn; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d dlci %d mtu %d", s, cr, d->dlci, d->mtu); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc) + sizeof(*pn)); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_PN); - mcc->len = __len8(sizeof(*pn)); - - pn = (void *) ptr; ptr += sizeof(*pn); - pn->dlci = d->dlci; - pn->priority = d->priority; - pn->ack_timer = 0; - pn->max_retrans = 0; - - if (s->cfc) { - pn->flow_ctrl = cr ? 0xf0 : 0xe0; - pn->credits = RFCOMM_DEFAULT_CREDITS; - } else { - pn->flow_ctrl = 0; - pn->credits = 0; - } - - if (cr && channel_mtu >= 0) - pn->mtu = cpu_to_le16(channel_mtu); - else - pn->mtu = cpu_to_le16(d->mtu); - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, - u8 bit_rate, u8 data_bits, u8 stop_bits, - u8 parity, u8 flow_ctrl_settings, - u8 xon_char, u8 xoff_char, u16 param_mask) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - struct rfcomm_rpn *rpn; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d dlci %d bit_r 0x%x data_b 0x%x stop_b 0x%x parity 0x%x" - " flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x", - s, cr, dlci, bit_rate, data_bits, stop_bits, parity, - flow_ctrl_settings, xon_char, xoff_char, param_mask); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc) + sizeof(*rpn)); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_RPN); - mcc->len = __len8(sizeof(*rpn)); - - rpn = (void *) ptr; ptr += sizeof(*rpn); - rpn->dlci = __addr(1, dlci); - rpn->bit_rate = bit_rate; - rpn->line_settings = __rpn_line_settings(data_bits, stop_bits, parity); - rpn->flow_ctrl = flow_ctrl_settings; - rpn->xon_char = xon_char; - rpn->xoff_char = xoff_char; - rpn->param_mask = cpu_to_le16(param_mask); - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - struct rfcomm_rls *rls; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d status 0x%x", s, cr, status); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc) + sizeof(*rls)); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_RLS); - mcc->len = __len8(sizeof(*rls)); - - rls = (void *) ptr; ptr += sizeof(*rls); - rls->dlci = __addr(1, dlci); - rls->status = status; - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - struct rfcomm_msc *msc; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d v24 0x%x", s, cr, v24_sig); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc) + sizeof(*msc)); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_MSC); - mcc->len = __len8(sizeof(*msc)); - - msc = (void *) ptr; ptr += sizeof(*msc); - msc->dlci = __addr(1, dlci); - msc->v24_sig = v24_sig | 0x01; - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d", s, cr); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc)); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_FCOFF); - mcc->len = __len8(0); - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static int rfcomm_send_fcon(struct rfcomm_session *s, int cr) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d", s, cr); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc)); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_FCON); - mcc->len = __len8(0); - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len) -{ - struct socket *sock = s->sock; - struct kvec iv[3]; - struct msghdr msg; - unsigned char hdr[5], crc[1]; - - if (len > 125) - return -EINVAL; - - BT_DBG("%p cr %d", s, cr); - - hdr[0] = __addr(s->initiator, 0); - hdr[1] = __ctrl(RFCOMM_UIH, 0); - hdr[2] = 0x01 | ((len + 2) << 1); - hdr[3] = 0x01 | ((cr & 0x01) << 1) | (RFCOMM_TEST << 2); - hdr[4] = 0x01 | (len << 1); - - crc[0] = __fcs(hdr); - - iv[0].iov_base = hdr; - iv[0].iov_len = 5; - iv[1].iov_base = pattern; - iv[1].iov_len = len; - iv[2].iov_base = crc; - iv[2].iov_len = 1; - - memset(&msg, 0, sizeof(msg)); - - return kernel_sendmsg(sock, &msg, iv, 3, 6 + len); -} - -static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits) -{ - struct rfcomm_hdr *hdr; - u8 buf[16], *ptr = buf; - - BT_DBG("%p addr %d credits %d", s, addr, credits); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = addr; - hdr->ctrl = __ctrl(RFCOMM_UIH, 1); - hdr->len = __len8(0); - - *ptr = credits; ptr++; - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static void rfcomm_make_uih(struct sk_buff *skb, u8 addr) -{ - struct rfcomm_hdr *hdr; - int len = skb->len; - u8 *crc; - - if (len > 127) { - hdr = (void *) skb_push(skb, 4); - put_unaligned(cpu_to_le16(__len16(len)), (__le16 *) &hdr->len); - } else { - hdr = (void *) skb_push(skb, 3); - hdr->len = __len8(len); - } - hdr->addr = addr; - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - - crc = skb_put(skb, 1); - *crc = __fcs((void *) hdr); -} - -/* ---- RFCOMM frame reception ---- */ -static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) -{ - BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); - - if (dlci) { - /* Data channel */ - struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); - if (!d) { - rfcomm_send_dm(s, dlci); - return 0; - } - - switch (d->state) { - case BT_CONNECT: - rfcomm_dlc_clear_timer(d); - - rfcomm_dlc_lock(d); - d->state = BT_CONNECTED; - d->state_change(d, 0); - rfcomm_dlc_unlock(d); - - rfcomm_send_msc(s, 1, dlci, d->v24_sig); - break; - - case BT_DISCONN: - d->state = BT_CLOSED; - __rfcomm_dlc_close(d, 0); - - if (list_empty(&s->dlcs)) { - s->state = BT_DISCONN; - rfcomm_send_disc(s, 0); - rfcomm_session_clear_timer(s); - } - - break; - } - } else { - /* Control channel */ - switch (s->state) { - case BT_CONNECT: - s->state = BT_CONNECTED; - rfcomm_process_connect(s); - break; - - case BT_DISCONN: - /* rfcomm_session_put is called later so don't do - * anything here otherwise we will mess up the session - * reference counter: - * - * (a) when we are the initiator dlc_unlink will drive - * the reference counter to 0 (there is no initial put - * after session_add) - * - * (b) when we are not the initiator rfcomm_rx_process - * will explicitly call put to balance the initial hold - * done after session add. - */ - break; - } - } - return 0; -} - -static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci) -{ - int err = 0; - - BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); - - if (dlci) { - /* Data DLC */ - struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); - if (d) { - if (d->state == BT_CONNECT || d->state == BT_CONFIG) - err = ECONNREFUSED; - else - err = ECONNRESET; - - d->state = BT_CLOSED; - __rfcomm_dlc_close(d, err); - } - } else { - if (s->state == BT_CONNECT) - err = ECONNREFUSED; - else - err = ECONNRESET; - - s->state = BT_CLOSED; - rfcomm_session_close(s, err); - } - return 0; -} - -static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci) -{ - int err = 0; - - BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); - - if (dlci) { - struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); - if (d) { - rfcomm_send_ua(s, dlci); - - if (d->state == BT_CONNECT || d->state == BT_CONFIG) - err = ECONNREFUSED; - else - err = ECONNRESET; - - d->state = BT_CLOSED; - __rfcomm_dlc_close(d, err); - } else - rfcomm_send_dm(s, dlci); - - } else { - rfcomm_send_ua(s, 0); - - if (s->state == BT_CONNECT) - err = ECONNREFUSED; - else - err = ECONNRESET; - - s->state = BT_CLOSED; - rfcomm_session_close(s, err); - } - - return 0; -} - -void rfcomm_dlc_accept(struct rfcomm_dlc *d) -{ - struct sock *sk = d->session->sock->sk; - struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; - - BT_DBG("dlc %p", d); - - rfcomm_send_ua(d->session, d->dlci); - - rfcomm_dlc_clear_timer(d); - - rfcomm_dlc_lock(d); - d->state = BT_CONNECTED; - d->state_change(d, 0); - rfcomm_dlc_unlock(d); - - if (d->role_switch) - hci_conn_switch_role(conn->hcon, 0x00); - - rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); -} - -static void rfcomm_check_accept(struct rfcomm_dlc *d) -{ - if (rfcomm_check_security(d)) { - if (d->defer_setup) { - set_bit(RFCOMM_DEFER_SETUP, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - - rfcomm_dlc_lock(d); - d->state = BT_CONNECT2; - d->state_change(d, 0); - rfcomm_dlc_unlock(d); - } else - rfcomm_dlc_accept(d); - } else { - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - } -} - -static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) -{ - struct rfcomm_dlc *d; - u8 channel; - - BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); - - if (!dlci) { - rfcomm_send_ua(s, 0); - - if (s->state == BT_OPEN) { - s->state = BT_CONNECTED; - rfcomm_process_connect(s); - } - return 0; - } - - /* Check if DLC exists */ - d = rfcomm_dlc_get(s, dlci); - if (d) { - if (d->state == BT_OPEN) { - /* DLC was previously opened by PN request */ - rfcomm_check_accept(d); - } - return 0; - } - - /* Notify socket layer about incoming connection */ - channel = __srv_channel(dlci); - if (rfcomm_connect_ind(s, channel, &d)) { - d->dlci = dlci; - d->addr = __addr(s->initiator, dlci); - rfcomm_dlc_link(s, d); - - rfcomm_check_accept(d); - } else { - rfcomm_send_dm(s, dlci); - } - - return 0; -} - -static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn) -{ - struct rfcomm_session *s = d->session; - - BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d", - d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits); - - if ((pn->flow_ctrl == 0xf0 && s->cfc != RFCOMM_CFC_DISABLED) || - pn->flow_ctrl == 0xe0) { - d->cfc = RFCOMM_CFC_ENABLED; - d->tx_credits = pn->credits; - } else { - d->cfc = RFCOMM_CFC_DISABLED; - set_bit(RFCOMM_TX_THROTTLED, &d->flags); - } - - if (s->cfc == RFCOMM_CFC_UNKNOWN) - s->cfc = d->cfc; - - d->priority = pn->priority; - - d->mtu = __le16_to_cpu(pn->mtu); - - if (cr && d->mtu > s->mtu) - d->mtu = s->mtu; - - return 0; -} - -static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb) -{ - struct rfcomm_pn *pn = (void *) skb->data; - struct rfcomm_dlc *d; - u8 dlci = pn->dlci; - - BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); - - if (!dlci) - return 0; - - d = rfcomm_dlc_get(s, dlci); - if (d) { - if (cr) { - /* PN request */ - rfcomm_apply_pn(d, cr, pn); - rfcomm_send_pn(s, 0, d); - } else { - /* PN response */ - switch (d->state) { - case BT_CONFIG: - rfcomm_apply_pn(d, cr, pn); - - d->state = BT_CONNECT; - rfcomm_send_sabm(s, d->dlci); - break; - } - } - } else { - u8 channel = __srv_channel(dlci); - - if (!cr) - return 0; - - /* PN request for non existing DLC. - * Assume incoming connection. */ - if (rfcomm_connect_ind(s, channel, &d)) { - d->dlci = dlci; - d->addr = __addr(s->initiator, dlci); - rfcomm_dlc_link(s, d); - - rfcomm_apply_pn(d, cr, pn); - - d->state = BT_OPEN; - rfcomm_send_pn(s, 0, d); - } else { - rfcomm_send_dm(s, dlci); - } - } - return 0; -} - -static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb) -{ - struct rfcomm_rpn *rpn = (void *) skb->data; - u8 dlci = __get_dlci(rpn->dlci); - - u8 bit_rate = 0; - u8 data_bits = 0; - u8 stop_bits = 0; - u8 parity = 0; - u8 flow_ctrl = 0; - u8 xon_char = 0; - u8 xoff_char = 0; - u16 rpn_mask = RFCOMM_RPN_PM_ALL; - - BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x", - dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl, - rpn->xon_char, rpn->xoff_char, rpn->param_mask); - - if (!cr) - return 0; - - if (len == 1) { - /* This is a request, return default (according to ETSI TS 07.10) settings */ - bit_rate = RFCOMM_RPN_BR_9600; - data_bits = RFCOMM_RPN_DATA_8; - stop_bits = RFCOMM_RPN_STOP_1; - parity = RFCOMM_RPN_PARITY_NONE; - flow_ctrl = RFCOMM_RPN_FLOW_NONE; - xon_char = RFCOMM_RPN_XON_CHAR; - xoff_char = RFCOMM_RPN_XOFF_CHAR; - goto rpn_out; - } - - /* Check for sane values, ignore/accept bit_rate, 8 bits, 1 stop bit, - * no parity, no flow control lines, normal XON/XOFF chars */ - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_BITRATE)) { - bit_rate = rpn->bit_rate; - if (bit_rate > RFCOMM_RPN_BR_230400) { - BT_DBG("RPN bit rate mismatch 0x%x", bit_rate); - bit_rate = RFCOMM_RPN_BR_9600; - rpn_mask ^= RFCOMM_RPN_PM_BITRATE; - } - } - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_DATA)) { - data_bits = __get_rpn_data_bits(rpn->line_settings); - if (data_bits != RFCOMM_RPN_DATA_8) { - BT_DBG("RPN data bits mismatch 0x%x", data_bits); - data_bits = RFCOMM_RPN_DATA_8; - rpn_mask ^= RFCOMM_RPN_PM_DATA; - } - } - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_STOP)) { - stop_bits = __get_rpn_stop_bits(rpn->line_settings); - if (stop_bits != RFCOMM_RPN_STOP_1) { - BT_DBG("RPN stop bits mismatch 0x%x", stop_bits); - stop_bits = RFCOMM_RPN_STOP_1; - rpn_mask ^= RFCOMM_RPN_PM_STOP; - } - } - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_PARITY)) { - parity = __get_rpn_parity(rpn->line_settings); - if (parity != RFCOMM_RPN_PARITY_NONE) { - BT_DBG("RPN parity mismatch 0x%x", parity); - parity = RFCOMM_RPN_PARITY_NONE; - rpn_mask ^= RFCOMM_RPN_PM_PARITY; - } - } - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_FLOW)) { - flow_ctrl = rpn->flow_ctrl; - if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) { - BT_DBG("RPN flow ctrl mismatch 0x%x", flow_ctrl); - flow_ctrl = RFCOMM_RPN_FLOW_NONE; - rpn_mask ^= RFCOMM_RPN_PM_FLOW; - } - } - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_XON)) { - xon_char = rpn->xon_char; - if (xon_char != RFCOMM_RPN_XON_CHAR) { - BT_DBG("RPN XON char mismatch 0x%x", xon_char); - xon_char = RFCOMM_RPN_XON_CHAR; - rpn_mask ^= RFCOMM_RPN_PM_XON; - } - } - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_XOFF)) { - xoff_char = rpn->xoff_char; - if (xoff_char != RFCOMM_RPN_XOFF_CHAR) { - BT_DBG("RPN XOFF char mismatch 0x%x", xoff_char); - xoff_char = RFCOMM_RPN_XOFF_CHAR; - rpn_mask ^= RFCOMM_RPN_PM_XOFF; - } - } - -rpn_out: - rfcomm_send_rpn(s, 0, dlci, bit_rate, data_bits, stop_bits, - parity, flow_ctrl, xon_char, xoff_char, rpn_mask); - - return 0; -} - -static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb) -{ - struct rfcomm_rls *rls = (void *) skb->data; - u8 dlci = __get_dlci(rls->dlci); - - BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status); - - if (!cr) - return 0; - - /* We should probably do something with this information here. But - * for now it's sufficient just to reply -- Bluetooth 1.1 says it's - * mandatory to recognise and respond to RLS */ - - rfcomm_send_rls(s, 0, dlci, rls->status); - - return 0; -} - -static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb) -{ - struct rfcomm_msc *msc = (void *) skb->data; - struct rfcomm_dlc *d; - u8 dlci = __get_dlci(msc->dlci); - - BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig); - - d = rfcomm_dlc_get(s, dlci); - if (!d) - return 0; - - if (cr) { - if (msc->v24_sig & RFCOMM_V24_FC && !d->cfc) - set_bit(RFCOMM_TX_THROTTLED, &d->flags); - else - clear_bit(RFCOMM_TX_THROTTLED, &d->flags); - - rfcomm_dlc_lock(d); - - d->remote_v24_sig = msc->v24_sig; - - if (d->modem_status) - d->modem_status(d, msc->v24_sig); - - rfcomm_dlc_unlock(d); - - rfcomm_send_msc(s, 0, dlci, msc->v24_sig); - - d->mscex |= RFCOMM_MSCEX_RX; - } else - d->mscex |= RFCOMM_MSCEX_TX; - - return 0; -} - -static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb) -{ - struct rfcomm_mcc *mcc = (void *) skb->data; - u8 type, cr, len; - - cr = __test_cr(mcc->type); - type = __get_mcc_type(mcc->type); - len = __get_mcc_len(mcc->len); - - BT_DBG("%p type 0x%x cr %d", s, type, cr); - - skb_pull(skb, 2); - - switch (type) { - case RFCOMM_PN: - rfcomm_recv_pn(s, cr, skb); - break; - - case RFCOMM_RPN: - rfcomm_recv_rpn(s, cr, len, skb); - break; - - case RFCOMM_RLS: - rfcomm_recv_rls(s, cr, skb); - break; - - case RFCOMM_MSC: - rfcomm_recv_msc(s, cr, skb); - break; - - case RFCOMM_FCOFF: - if (cr) { - set_bit(RFCOMM_TX_THROTTLED, &s->flags); - rfcomm_send_fcoff(s, 0); - } - break; - - case RFCOMM_FCON: - if (cr) { - clear_bit(RFCOMM_TX_THROTTLED, &s->flags); - rfcomm_send_fcon(s, 0); - } - break; - - case RFCOMM_TEST: - if (cr) - rfcomm_send_test(s, 0, skb->data, skb->len); - break; - - case RFCOMM_NSC: - break; - - default: - BT_ERR("Unknown control type 0x%02x", type); - rfcomm_send_nsc(s, cr, type); - break; - } - return 0; -} - -static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk_buff *skb) -{ - struct rfcomm_dlc *d; - - BT_DBG("session %p state %ld dlci %d pf %d", s, s->state, dlci, pf); - - d = rfcomm_dlc_get(s, dlci); - if (!d) { - rfcomm_send_dm(s, dlci); - goto drop; - } - - if (pf && d->cfc) { - u8 credits = *(u8 *) skb->data; skb_pull(skb, 1); - - d->tx_credits += credits; - if (d->tx_credits) - clear_bit(RFCOMM_TX_THROTTLED, &d->flags); - } - - if (skb->len && d->state == BT_CONNECTED) { - rfcomm_dlc_lock(d); - d->rx_credits--; - d->data_ready(d, skb); - rfcomm_dlc_unlock(d); - return 0; - } - -drop: - kfree_skb(skb); - return 0; -} - -static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) -{ - struct rfcomm_hdr *hdr = (void *) skb->data; - u8 type, dlci, fcs; - - dlci = __get_dlci(hdr->addr); - type = __get_type(hdr->ctrl); - - /* Trim FCS */ - skb->len--; skb->tail--; - fcs = *(u8 *)skb_tail_pointer(skb); - - if (__check_fcs(skb->data, type, fcs)) { - BT_ERR("bad checksum in packet"); - kfree_skb(skb); - return -EILSEQ; - } - - if (__test_ea(hdr->len)) - skb_pull(skb, 3); - else - skb_pull(skb, 4); - - switch (type) { - case RFCOMM_SABM: - if (__test_pf(hdr->ctrl)) - rfcomm_recv_sabm(s, dlci); - break; - - case RFCOMM_DISC: - if (__test_pf(hdr->ctrl)) - rfcomm_recv_disc(s, dlci); - break; - - case RFCOMM_UA: - if (__test_pf(hdr->ctrl)) - rfcomm_recv_ua(s, dlci); - break; - - case RFCOMM_DM: - rfcomm_recv_dm(s, dlci); - break; - - case RFCOMM_UIH: - if (dlci) - return rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb); - - rfcomm_recv_mcc(s, skb); - break; - - default: - BT_ERR("Unknown packet type 0x%02x", type); - break; - } - kfree_skb(skb); - return 0; -} - -/* ---- Connection and data processing ---- */ - -static void rfcomm_process_connect(struct rfcomm_session *s) -{ - struct rfcomm_dlc *d; - struct list_head *p, *n; - - BT_DBG("session %p state %ld", s, s->state); - - list_for_each_safe(p, n, &s->dlcs) { - d = list_entry(p, struct rfcomm_dlc, list); - if (d->state == BT_CONFIG) { - d->mtu = s->mtu; - if (rfcomm_check_security(d)) { - rfcomm_send_pn(s, 1, d); - } else { - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - } - } - } -} - -/* Send data queued for the DLC. - * Return number of frames left in the queue. - */ -static inline int rfcomm_process_tx(struct rfcomm_dlc *d) -{ - struct sk_buff *skb; - int err; - - BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d", - d, d->state, d->cfc, d->rx_credits, d->tx_credits); - - /* Send pending MSC */ - if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags)) - rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); - - if (d->cfc) { - /* CFC enabled. - * Give them some credits */ - if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) && - d->rx_credits <= (d->cfc >> 2)) { - rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits); - d->rx_credits = d->cfc; - } - } else { - /* CFC disabled. - * Give ourselves some credits */ - d->tx_credits = 5; - } - - if (test_bit(RFCOMM_TX_THROTTLED, &d->flags)) - return skb_queue_len(&d->tx_queue); - - while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) { - err = rfcomm_send_frame(d->session, skb->data, skb->len); - if (err < 0) { - skb_queue_head(&d->tx_queue, skb); - break; - } - kfree_skb(skb); - d->tx_credits--; - } - - if (d->cfc && !d->tx_credits) { - /* We're out of TX credits. - * Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */ - set_bit(RFCOMM_TX_THROTTLED, &d->flags); - } - - return skb_queue_len(&d->tx_queue); -} - -static inline void rfcomm_process_dlcs(struct rfcomm_session *s) -{ - struct rfcomm_dlc *d; - struct list_head *p, *n; - - BT_DBG("session %p state %ld", s, s->state); - - list_for_each_safe(p, n, &s->dlcs) { - d = list_entry(p, struct rfcomm_dlc, list); - - if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) { - __rfcomm_dlc_close(d, ETIMEDOUT); - continue; - } - - if (test_bit(RFCOMM_ENC_DROP, &d->flags)) { - __rfcomm_dlc_close(d, ECONNREFUSED); - continue; - } - - if (test_and_clear_bit(RFCOMM_AUTH_ACCEPT, &d->flags)) { - rfcomm_dlc_clear_timer(d); - if (d->out) { - rfcomm_send_pn(s, 1, d); - rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); - } else { - if (d->defer_setup) { - set_bit(RFCOMM_DEFER_SETUP, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - - rfcomm_dlc_lock(d); - d->state = BT_CONNECT2; - d->state_change(d, 0); - rfcomm_dlc_unlock(d); - } else - rfcomm_dlc_accept(d); - } - continue; - } else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) { - rfcomm_dlc_clear_timer(d); - if (!d->out) - rfcomm_send_dm(s, d->dlci); - else - d->state = BT_CLOSED; - __rfcomm_dlc_close(d, ECONNREFUSED); - continue; - } - - if (test_bit(RFCOMM_SEC_PENDING, &d->flags)) - continue; - - if (test_bit(RFCOMM_TX_THROTTLED, &s->flags)) - continue; - - if ((d->state == BT_CONNECTED || d->state == BT_DISCONN) && - d->mscex == RFCOMM_MSCEX_OK) - rfcomm_process_tx(d); - } -} - -static inline void rfcomm_process_rx(struct rfcomm_session *s) -{ - struct socket *sock = s->sock; - struct sock *sk = sock->sk; - struct sk_buff *skb; - - BT_DBG("session %p state %ld qlen %d", s, s->state, skb_queue_len(&sk->sk_receive_queue)); - - /* Get data directly from socket receive queue without copying it. */ - while ((skb = skb_dequeue(&sk->sk_receive_queue))) { - skb_orphan(skb); - if (!skb_linearize(skb)) - rfcomm_recv_frame(s, skb); - else - kfree_skb(skb); - } - - if (sk->sk_state == BT_CLOSED) { - if (!s->initiator) - rfcomm_session_put(s); - - rfcomm_session_close(s, sk->sk_err); - } -} - -static inline void rfcomm_accept_connection(struct rfcomm_session *s) -{ - struct socket *sock = s->sock, *nsock; - int err; - - /* Fast check for a new connection. - * Avoids unnesesary socket allocations. */ - if (list_empty(&bt_sk(sock->sk)->accept_q)) - return; - - BT_DBG("session %p", s); - - err = kernel_accept(sock, &nsock, O_NONBLOCK); - if (err < 0) - return; - - /* Set our callbacks */ - nsock->sk->sk_data_ready = rfcomm_l2data_ready; - nsock->sk->sk_state_change = rfcomm_l2state_change; - - s = rfcomm_session_add(nsock, BT_OPEN); - if (s) { - rfcomm_session_hold(s); - - /* We should adjust MTU on incoming sessions. - * L2CAP MTU minus UIH header and FCS. */ - s->mtu = min(l2cap_pi(nsock->sk)->chan->omtu, - l2cap_pi(nsock->sk)->chan->imtu) - 5; - - rfcomm_schedule(); - } else - sock_release(nsock); -} - -static inline void rfcomm_check_connection(struct rfcomm_session *s) -{ - struct sock *sk = s->sock->sk; - - BT_DBG("%p state %ld", s, s->state); - - switch (sk->sk_state) { - case BT_CONNECTED: - s->state = BT_CONNECT; - - /* We can adjust MTU on outgoing sessions. - * L2CAP MTU minus UIH header and FCS. */ - s->mtu = min(l2cap_pi(sk)->chan->omtu, l2cap_pi(sk)->chan->imtu) - 5; - - rfcomm_send_sabm(s, 0); - break; - - case BT_CLOSED: - s->state = BT_CLOSED; - rfcomm_session_close(s, sk->sk_err); - break; - } -} - -static inline void rfcomm_process_sessions(void) -{ - struct list_head *p, *n; - - rfcomm_lock(); - - list_for_each_safe(p, n, &session_list) { - struct rfcomm_session *s; - s = list_entry(p, struct rfcomm_session, list); - - if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) { - s->state = BT_DISCONN; - rfcomm_send_disc(s, 0); - rfcomm_session_put(s); - continue; - } - - if (s->state == BT_LISTEN) { - rfcomm_accept_connection(s); - continue; - } - - rfcomm_session_hold(s); - - switch (s->state) { - case BT_BOUND: - rfcomm_check_connection(s); - break; - - default: - rfcomm_process_rx(s); - break; - } - - rfcomm_process_dlcs(s); - - rfcomm_session_put(s); - } - - rfcomm_unlock(); -} - -static int rfcomm_add_listener(bdaddr_t *ba) -{ - struct sockaddr_l2 addr; - struct socket *sock; - struct sock *sk; - struct rfcomm_session *s; - int err = 0; - - /* Create socket */ - err = rfcomm_l2sock_create(&sock); - if (err < 0) { - BT_ERR("Create socket failed %d", err); - return err; - } - - /* Bind socket */ - bacpy(&addr.l2_bdaddr, ba); - addr.l2_family = AF_BLUETOOTH; - addr.l2_psm = cpu_to_le16(RFCOMM_PSM); - addr.l2_cid = 0; - err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr)); - if (err < 0) { - BT_ERR("Bind failed %d", err); - goto failed; - } - - /* Set L2CAP options */ - sk = sock->sk; - lock_sock(sk); - l2cap_pi(sk)->chan->imtu = l2cap_mtu; - release_sock(sk); - - /* Start listening on the socket */ - err = kernel_listen(sock, 10); - if (err) { - BT_ERR("Listen failed %d", err); - goto failed; - } - - /* Add listening session */ - s = rfcomm_session_add(sock, BT_LISTEN); - if (!s) - goto failed; - - rfcomm_session_hold(s); - return 0; -failed: - sock_release(sock); - return err; -} - -static void rfcomm_kill_listener(void) -{ - struct rfcomm_session *s; - struct list_head *p, *n; - - BT_DBG(""); - - list_for_each_safe(p, n, &session_list) { - s = list_entry(p, struct rfcomm_session, list); - rfcomm_session_del(s); - } -} - -static int rfcomm_run(void *unused) -{ - BT_DBG(""); - - set_user_nice(current, -10); - - rfcomm_add_listener(BDADDR_ANY); - - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (kthread_should_stop()) - break; - - /* Process stuff */ - rfcomm_process_sessions(); - - schedule(); - } - __set_current_state(TASK_RUNNING); - - rfcomm_kill_listener(); - - return 0; -} - -static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) -{ - struct rfcomm_session *s; - struct rfcomm_dlc *d; - struct list_head *p, *n; - - BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt); - - s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst); - if (!s) - return; - - rfcomm_session_hold(s); - - list_for_each_safe(p, n, &s->dlcs) { - d = list_entry(p, struct rfcomm_dlc, list); - - if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) { - rfcomm_dlc_clear_timer(d); - if (status || encrypt == 0x00) { - set_bit(RFCOMM_ENC_DROP, &d->flags); - continue; - } - } - - if (d->state == BT_CONNECTED && !status && encrypt == 0x00) { - if (d->sec_level == BT_SECURITY_MEDIUM) { - set_bit(RFCOMM_SEC_PENDING, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - continue; - } else if (d->sec_level == BT_SECURITY_HIGH) { - set_bit(RFCOMM_ENC_DROP, &d->flags); - continue; - } - } - - if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags)) - continue; - - if (!status && hci_conn_check_secure(conn, d->sec_level)) - set_bit(RFCOMM_AUTH_ACCEPT, &d->flags); - else - set_bit(RFCOMM_AUTH_REJECT, &d->flags); - } - - rfcomm_session_put(s); - - rfcomm_schedule(); -} - -static struct hci_cb rfcomm_cb = { - .name = "RFCOMM", - .security_cfm = rfcomm_security_cfm -}; - -static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x) -{ - struct rfcomm_session *s; - - rfcomm_lock(); - - list_for_each_entry(s, &session_list, list) { - struct rfcomm_dlc *d; - 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); - } - } - - rfcomm_unlock(); - - return 0; -} - -static int rfcomm_dlc_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, rfcomm_dlc_debugfs_show, inode->i_private); -} - -static const struct file_operations rfcomm_dlc_debugfs_fops = { - .open = rfcomm_dlc_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *rfcomm_dlc_debugfs; - -/* ---- Initialization ---- */ -static int __init rfcomm_init(void) -{ - int err; - - hci_register_cb(&rfcomm_cb); - - rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd"); - if (IS_ERR(rfcomm_thread)) { - err = PTR_ERR(rfcomm_thread); - goto unregister; - } - - if (bt_debugfs) { - rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444, - bt_debugfs, NULL, &rfcomm_dlc_debugfs_fops); - if (!rfcomm_dlc_debugfs) - BT_ERR("Failed to create RFCOMM debug file"); - } - - err = rfcomm_init_ttys(); - if (err < 0) - goto stop; - - err = rfcomm_init_sockets(); - if (err < 0) - goto cleanup; - - BT_INFO("RFCOMM ver %s", VERSION); - - return 0; - -cleanup: - rfcomm_cleanup_ttys(); - -stop: - kthread_stop(rfcomm_thread); - -unregister: - hci_unregister_cb(&rfcomm_cb); - - return err; -} - -static void __exit rfcomm_exit(void) -{ - debugfs_remove(rfcomm_dlc_debugfs); - - hci_unregister_cb(&rfcomm_cb); - - kthread_stop(rfcomm_thread); - - rfcomm_cleanup_ttys(); - - rfcomm_cleanup_sockets(); -} - -module_init(rfcomm_init); -module_exit(rfcomm_exit); - -module_param(disable_cfc, bool, 0644); -MODULE_PARM_DESC(disable_cfc, "Disable credit based flow control"); - -module_param(channel_mtu, int, 0644); -MODULE_PARM_DESC(channel_mtu, "Default MTU for the RFCOMM channel"); - -module_param(l2cap_mtu, uint, 0644); -MODULE_PARM_DESC(l2cap_mtu, "Default MTU for the L2CAP connection"); - -module_param(l2cap_ertm, bool, 0644); -MODULE_PARM_DESC(l2cap_ertm, "Use L2CAP ERTM mode for connection"); - -MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -MODULE_DESCRIPTION("Bluetooth RFCOMM ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("bt-proto-3"); diff --git a/net/bluetooth_tizen/rfcomm/sock.c b/net/bluetooth_tizen/rfcomm/sock.c deleted file mode 100644 index 22169c3..0000000 --- a/net/bluetooth_tizen/rfcomm/sock.c +++ /dev/null @@ -1,1078 +0,0 @@ -/* - RFCOMM implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> - Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* - * RFCOMM sockets. - */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/socket.h> -#include <linux/skbuff.h> -#include <linux/list.h> -#include <linux/device.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/security.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <linux/uaccess.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> -#include <net/bluetooth/rfcomm.h> - -static const struct proto_ops rfcomm_sock_ops; - -static struct bt_sock_list rfcomm_sk_list = { - .lock = __RW_LOCK_UNLOCKED(rfcomm_sk_list.lock) -}; - -static void rfcomm_sock_close(struct sock *sk); -static void rfcomm_sock_kill(struct sock *sk); - -/* ---- DLC callbacks ---- - * - * called under rfcomm_dlc_lock() - */ -static void rfcomm_sk_data_ready(struct rfcomm_dlc *d, struct sk_buff *skb) -{ - struct sock *sk = d->owner; - if (!sk) - return; - - atomic_add(skb->len, &sk->sk_rmem_alloc); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, skb->len); - - if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf) - rfcomm_dlc_throttle(d); -} - -static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err) -{ - struct sock *sk = d->owner, *parent; - unsigned long flags; - - if (!sk) - return; - - BT_DBG("dlc %p state %ld err %d", d, d->state, err); - - local_irq_save(flags); - bh_lock_sock(sk); - - if (err) - sk->sk_err = err; - - sk->sk_state = d->state; - - parent = bt_sk(sk)->parent; - if (parent) { - if (d->state == BT_CLOSED) { - sock_set_flag(sk, SOCK_ZAPPED); - bt_accept_unlink(sk); - } - parent->sk_data_ready(parent, 0); - } else { - if (d->state == BT_CONNECTED) - rfcomm_session_getaddr(d->session, &bt_sk(sk)->src, NULL); - sk->sk_state_change(sk); - } - - bh_unlock_sock(sk); - local_irq_restore(flags); - - if (parent && sock_flag(sk, SOCK_ZAPPED)) { - /* We have to drop DLC lock here, otherwise - * rfcomm_sock_destruct() will dead lock. */ - rfcomm_dlc_unlock(d); - rfcomm_sock_kill(sk); - rfcomm_dlc_lock(d); - } -} - -/* ---- Socket functions ---- */ -static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src) -{ - struct sock *sk = NULL; - struct hlist_node *node; - - sk_for_each(sk, node, &rfcomm_sk_list.head) { - if (rfcomm_pi(sk)->channel == channel && - !bacmp(&bt_sk(sk)->src, src)) - break; - } - - return node ? sk : NULL; -} - -/* Find socket with channel and source bdaddr. - * Returns closest match. - */ -static struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src) -{ - struct sock *sk = NULL, *sk1 = NULL; - struct hlist_node *node; - - read_lock(&rfcomm_sk_list.lock); - - sk_for_each(sk, node, &rfcomm_sk_list.head) { - if (state && sk->sk_state != state) - continue; - - if (rfcomm_pi(sk)->channel == channel) { - /* Exact match. */ - if (!bacmp(&bt_sk(sk)->src, src)) - break; - - /* Closest match */ - if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) - sk1 = sk; - } - } - - read_unlock(&rfcomm_sk_list.lock); - - return node ? sk : sk1; -} - -static void rfcomm_sock_destruct(struct sock *sk) -{ - struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; - - BT_DBG("sk %p dlc %p", sk, d); - - skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); - - rfcomm_dlc_lock(d); - rfcomm_pi(sk)->dlc = NULL; - - /* Detach DLC if it's owned by this socket */ - if (d->owner == sk) - d->owner = NULL; - rfcomm_dlc_unlock(d); - - rfcomm_dlc_put(d); -} - -static void rfcomm_sock_cleanup_listen(struct sock *parent) -{ - struct sock *sk; - - BT_DBG("parent %p", parent); - - /* Close not yet accepted dlcs */ - while ((sk = bt_accept_dequeue(parent, NULL))) { - rfcomm_sock_close(sk); - rfcomm_sock_kill(sk); - } - - parent->sk_state = BT_CLOSED; - sock_set_flag(parent, SOCK_ZAPPED); -} - -/* Kill socket (only if zapped and orphan) - * Must be called on unlocked socket. - */ -static void rfcomm_sock_kill(struct sock *sk) -{ - if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) - return; - - BT_DBG("sk %p state %d refcnt %d", sk, sk->sk_state, atomic_read(&sk->sk_refcnt)); - - /* Kill poor orphan */ - bt_sock_unlink(&rfcomm_sk_list, sk); - sock_set_flag(sk, SOCK_DEAD); - sock_put(sk); -} - -static void __rfcomm_sock_close(struct sock *sk) -{ - struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; - - BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); - - switch (sk->sk_state) { - case BT_LISTEN: - rfcomm_sock_cleanup_listen(sk); - break; - - case BT_CONNECT: - case BT_CONNECT2: - case BT_CONFIG: - case BT_CONNECTED: - rfcomm_dlc_close(d, 0); - - default: - sock_set_flag(sk, SOCK_ZAPPED); - break; - } -} - -/* Close socket. - * Must be called on unlocked socket. - */ -static void rfcomm_sock_close(struct sock *sk) -{ - lock_sock(sk); - __rfcomm_sock_close(sk); - release_sock(sk); -} - -static void rfcomm_sock_init(struct sock *sk, struct sock *parent) -{ - struct rfcomm_pinfo *pi = rfcomm_pi(sk); - - BT_DBG("sk %p", sk); - - if (parent) { - sk->sk_type = parent->sk_type; - pi->dlc->defer_setup = bt_sk(parent)->defer_setup; - - pi->sec_level = rfcomm_pi(parent)->sec_level; - pi->role_switch = rfcomm_pi(parent)->role_switch; - - security_sk_clone(parent, sk); - } else { - pi->dlc->defer_setup = 0; - - pi->sec_level = BT_SECURITY_LOW; - pi->role_switch = 0; - } - - pi->dlc->sec_level = pi->sec_level; - pi->dlc->role_switch = pi->role_switch; -} - -static struct proto rfcomm_proto = { - .name = "RFCOMM", - .owner = THIS_MODULE, - .obj_size = sizeof(struct rfcomm_pinfo) -}; - -static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) -{ - struct rfcomm_dlc *d; - struct sock *sk; - - sk = sk_alloc(net, PF_BLUETOOTH, prio, &rfcomm_proto); - if (!sk) - return NULL; - - sock_init_data(sock, sk); - INIT_LIST_HEAD(&bt_sk(sk)->accept_q); - - d = rfcomm_dlc_alloc(prio); - if (!d) { - sk_free(sk); - return NULL; - } - - d->data_ready = rfcomm_sk_data_ready; - d->state_change = rfcomm_sk_state_change; - - rfcomm_pi(sk)->dlc = d; - d->owner = sk; - - sk->sk_destruct = rfcomm_sock_destruct; - sk->sk_sndtimeo = RFCOMM_CONN_TIMEOUT; - - sk->sk_sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10; - sk->sk_rcvbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = proto; - sk->sk_state = BT_OPEN; - - bt_sock_link(&rfcomm_sk_list, sk); - - BT_DBG("sk %p", sk); - return sk; -} - -static int rfcomm_sock_create(struct net *net, struct socket *sock, - int protocol, int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - sock->state = SS_UNCONNECTED; - - if (sock->type != SOCK_STREAM && sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - sock->ops = &rfcomm_sock_ops; - - sk = rfcomm_sock_alloc(net, sock, protocol, GFP_ATOMIC); - if (!sk) - return -ENOMEM; - - rfcomm_sock_init(sk, NULL); - return 0; -} - -static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) -{ - struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr)); - - if (!addr || addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state != BT_OPEN) { - err = -EBADFD; - goto done; - } - - if (sk->sk_type != SOCK_STREAM) { - err = -EINVAL; - goto done; - } - - write_lock(&rfcomm_sk_list.lock); - - if (sa->rc_channel && __rfcomm_get_sock_by_addr(sa->rc_channel, &sa->rc_bdaddr)) { - err = -EADDRINUSE; - } else { - /* Save source address */ - bacpy(&bt_sk(sk)->src, &sa->rc_bdaddr); - rfcomm_pi(sk)->channel = sa->rc_channel; - sk->sk_state = BT_BOUND; - } - - write_unlock(&rfcomm_sk_list.lock); - -done: - release_sock(sk); - return err; -} - -static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) -{ - struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; - struct sock *sk = sock->sk; - struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; - int err = 0; - - BT_DBG("sk %p", sk); - - if (alen < sizeof(struct sockaddr_rc) || - addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) { - err = -EBADFD; - goto done; - } - - if (sk->sk_type != SOCK_STREAM) { - err = -EINVAL; - goto done; - } - - sk->sk_state = BT_CONNECT; - bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr); - rfcomm_pi(sk)->channel = sa->rc_channel; - - d->sec_level = rfcomm_pi(sk)->sec_level; - d->role_switch = rfcomm_pi(sk)->role_switch; - - err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel); - if (!err) - err = bt_sock_wait_state(sk, BT_CONNECTED, - sock_sndtimeo(sk, flags & O_NONBLOCK)); - -done: - release_sock(sk); - return err; -} - -static int rfcomm_sock_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sk %p backlog %d", sk, backlog); - - lock_sock(sk); - - if (sk->sk_state != BT_BOUND) { - err = -EBADFD; - goto done; - } - - if (sk->sk_type != SOCK_STREAM) { - err = -EINVAL; - goto done; - } - - if (!rfcomm_pi(sk)->channel) { - bdaddr_t *src = &bt_sk(sk)->src; - u8 channel; - - err = -EINVAL; - - write_lock(&rfcomm_sk_list.lock); - - for (channel = 1; channel < 31; channel++) - if (!__rfcomm_get_sock_by_addr(channel, src)) { - rfcomm_pi(sk)->channel = channel; - err = 0; - break; - } - - write_unlock(&rfcomm_sk_list.lock); - - if (err < 0) - goto done; - } - - sk->sk_max_ack_backlog = backlog; - sk->sk_ack_backlog = 0; - sk->sk_state = BT_LISTEN; - -done: - release_sock(sk); - return err; -} - -static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int flags) -{ - DECLARE_WAITQUEUE(wait, current); - struct sock *sk = sock->sk, *nsk; - long timeo; - int err = 0; - - lock_sock(sk); - - if (sk->sk_type != SOCK_STREAM) { - err = -EINVAL; - goto done; - } - - timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); - - BT_DBG("sk %p timeo %ld", sk, timeo); - - /* Wait for an incoming connection. (wake-one). */ - add_wait_queue_exclusive(sk_sleep(sk), &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; - break; - } - - nsk = bt_accept_dequeue(sk, newsock); - if (nsk) - break; - - if (!timeo) { - err = -EAGAIN; - break; - } - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - if (err) - goto done; - - newsock->state = SS_CONNECTED; - - BT_DBG("new socket %p", nsk); - -done: - release_sock(sk); - return err; -} - -static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) -{ - struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; - struct sock *sk = sock->sk; - - BT_DBG("sock %p, sk %p", sock, sk); - - sa->rc_family = AF_BLUETOOTH; - sa->rc_channel = rfcomm_pi(sk)->channel; - if (peer) - bacpy(&sa->rc_bdaddr, &bt_sk(sk)->dst); - else - bacpy(&sa->rc_bdaddr, &bt_sk(sk)->src); - - *len = sizeof(struct sockaddr_rc); - return 0; -} - -static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; - struct sk_buff *skb; - int sent = 0; - - if (test_bit(RFCOMM_DEFER_SETUP, &d->flags)) - return -ENOTCONN; - - if (msg->msg_flags & MSG_OOB) - return -EOPNOTSUPP; - - if (sk->sk_shutdown & SEND_SHUTDOWN) - return -EPIPE; - - BT_DBG("sock %p, sk %p", sock, sk); - - lock_sock(sk); - - while (len) { - size_t size = min_t(size_t, len, d->mtu); - int err; - - skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE, - msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) { - if (sent == 0) - sent = err; - break; - } - skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); - - err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); - if (err) { - kfree_skb(skb); - if (sent == 0) - sent = err; - break; - } - - skb->priority = sk->sk_priority; - - err = rfcomm_dlc_send(d, skb); - if (err < 0) { - kfree_skb(skb); - if (sent == 0) - sent = err; - break; - } - - sent += size; - len -= size; - } - - release_sock(sk); - - return sent; -} - -static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) -{ - struct sock *sk = sock->sk; - struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; - int len; - - if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { - rfcomm_dlc_accept(d); - return 0; - } - - len = bt_sock_stream_recvmsg(iocb, sock, msg, size, flags); - - lock_sock(sk); - if (!(flags & MSG_PEEK) && len > 0) - atomic_sub(len, &sk->sk_rmem_alloc); - - if (atomic_read(&sk->sk_rmem_alloc) <= (sk->sk_rcvbuf >> 2)) - rfcomm_dlc_unthrottle(rfcomm_pi(sk)->dlc); - release_sock(sk); - - return len; -} - -static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - int err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - lock_sock(sk); - - switch (optname) { - case RFCOMM_LM: - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - if (opt & RFCOMM_LM_AUTH) - rfcomm_pi(sk)->sec_level = BT_SECURITY_LOW; - if (opt & RFCOMM_LM_ENCRYPT) - rfcomm_pi(sk)->sec_level = BT_SECURITY_MEDIUM; - if (opt & RFCOMM_LM_SECURE) - rfcomm_pi(sk)->sec_level = BT_SECURITY_HIGH; - - rfcomm_pi(sk)->role_switch = (opt & RFCOMM_LM_MASTER); - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct bt_security sec; - int err = 0; - size_t len; - u32 opt; - - BT_DBG("sk %p", sk); - - if (level == SOL_RFCOMM) - return rfcomm_sock_setsockopt_old(sock, optname, optval, optlen); - - if (level != SOL_BLUETOOTH) - return -ENOPROTOOPT; - - lock_sock(sk); - - switch (optname) { - case BT_SECURITY: - if (sk->sk_type != SOCK_STREAM) { - err = -EINVAL; - break; - } - - sec.level = BT_SECURITY_LOW; - - len = min_t(unsigned int, sizeof(sec), optlen); - if (copy_from_user((char *) &sec, optval, len)) { - err = -EFAULT; - break; - } - - if (sec.level > BT_SECURITY_HIGH) { - err = -EINVAL; - break; - } - - rfcomm_pi(sk)->sec_level = sec.level; - break; - - case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { - err = -EINVAL; - break; - } - - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - bt_sk(sk)->defer_setup = opt; - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct rfcomm_conninfo cinfo; - struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case RFCOMM_LM: - switch (rfcomm_pi(sk)->sec_level) { - case BT_SECURITY_LOW: - opt = RFCOMM_LM_AUTH; - break; - case BT_SECURITY_MEDIUM: - opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT; - break; - case BT_SECURITY_HIGH: - opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | - RFCOMM_LM_SECURE; - break; - default: - opt = 0; - break; - } - - if (rfcomm_pi(sk)->role_switch) - opt |= RFCOMM_LM_MASTER; - - if (put_user(opt, (u32 __user *) optval)) - err = -EFAULT; - break; - - case RFCOMM_CONNINFO: - if (sk->sk_state != BT_CONNECTED && - !rfcomm_pi(sk)->dlc->defer_setup) { - err = -ENOTCONN; - break; - } - - memset(&cinfo, 0, sizeof(cinfo)); - cinfo.hci_handle = conn->hcon->handle; - memcpy(cinfo.dev_class, conn->hcon->dev_class, 3); - - len = min_t(unsigned int, len, sizeof(cinfo)); - if (copy_to_user(optval, (char *) &cinfo, len)) - err = -EFAULT; - - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct bt_security sec; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (level == SOL_RFCOMM) - return rfcomm_sock_getsockopt_old(sock, optname, optval, optlen); - - if (level != SOL_BLUETOOTH) - return -ENOPROTOOPT; - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case BT_SECURITY: - if (sk->sk_type != SOCK_STREAM) { - err = -EINVAL; - break; - } - - sec.level = rfcomm_pi(sk)->sec_level; - - len = min_t(unsigned int, len, sizeof(sec)); - if (copy_to_user(optval, (char *) &sec, len)) - err = -EFAULT; - - break; - - case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { - err = -EINVAL; - break; - } - - if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval)) - err = -EFAULT; - - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk __maybe_unused = sock->sk; - int err; - - BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg); - - err = bt_sock_ioctl(sock, cmd, arg); - - if (err == -ENOIOCTLCMD) { -#ifdef CONFIG_BT_RFCOMM_TTY - lock_sock(sk); - err = rfcomm_dev_ioctl(sk, cmd, (void __user *) arg); - release_sock(sk); -#else - err = -EOPNOTSUPP; -#endif - } - - return err; -} - -static int rfcomm_sock_shutdown(struct socket *sock, int how) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - lock_sock(sk); - if (!sk->sk_shutdown) { - sk->sk_shutdown = SHUTDOWN_MASK; - __rfcomm_sock_close(sk); - - if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) - err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime); - } - release_sock(sk); - return err; -} - -static int rfcomm_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - int err; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - err = rfcomm_sock_shutdown(sock, 2); - - sock_orphan(sk); - rfcomm_sock_kill(sk); - return err; -} - -/* ---- RFCOMM core layer callbacks ---- - * - * called under rfcomm_lock() - */ -int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d) -{ - struct sock *sk, *parent; - bdaddr_t src, dst; - int result = 0; - - BT_DBG("session %p channel %d", s, channel); - - rfcomm_session_getaddr(s, &src, &dst); - - /* Check if we have socket listening on channel */ - parent = rfcomm_get_sock_by_channel(BT_LISTEN, channel, &src); - if (!parent) - return 0; - - bh_lock_sock(parent); - - /* Check for backlog size */ - if (sk_acceptq_is_full(parent)) { - BT_DBG("backlog full %d", parent->sk_ack_backlog); - goto done; - } - - sk = rfcomm_sock_alloc(sock_net(parent), NULL, BTPROTO_RFCOMM, GFP_ATOMIC); - if (!sk) - goto done; - - bt_sock_reclassify_lock(sk, BTPROTO_RFCOMM); - - rfcomm_sock_init(sk, parent); - bacpy(&bt_sk(sk)->src, &src); - bacpy(&bt_sk(sk)->dst, &dst); - rfcomm_pi(sk)->channel = channel; - - sk->sk_state = BT_CONFIG; - bt_accept_enqueue(parent, sk); - - /* Accept connection and return socket DLC */ - *d = rfcomm_pi(sk)->dlc; - result = 1; - -done: - bh_unlock_sock(parent); - - if (bt_sk(parent)->defer_setup) - parent->sk_state_change(parent); - - return result; -} - -static int rfcomm_sock_debugfs_show(struct seq_file *f, void *p) -{ - struct sock *sk; - struct hlist_node *node; - - 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); - } - - read_unlock(&rfcomm_sk_list.lock); - - return 0; -} - -static int rfcomm_sock_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, rfcomm_sock_debugfs_show, inode->i_private); -} - -static const struct file_operations rfcomm_sock_debugfs_fops = { - .open = rfcomm_sock_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *rfcomm_sock_debugfs; - -static const struct proto_ops rfcomm_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = rfcomm_sock_release, - .bind = rfcomm_sock_bind, - .connect = rfcomm_sock_connect, - .listen = rfcomm_sock_listen, - .accept = rfcomm_sock_accept, - .getname = rfcomm_sock_getname, - .sendmsg = rfcomm_sock_sendmsg, - .recvmsg = rfcomm_sock_recvmsg, - .shutdown = rfcomm_sock_shutdown, - .setsockopt = rfcomm_sock_setsockopt, - .getsockopt = rfcomm_sock_getsockopt, - .ioctl = rfcomm_sock_ioctl, - .poll = bt_sock_poll, - .socketpair = sock_no_socketpair, - .mmap = sock_no_mmap -}; - -static const struct net_proto_family rfcomm_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = rfcomm_sock_create -}; - -int __init rfcomm_init_sockets(void) -{ - int err; - - err = proto_register(&rfcomm_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_RFCOMM, &rfcomm_sock_family_ops); - if (err < 0) - goto error; - - if (bt_debugfs) { - rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444, - bt_debugfs, NULL, &rfcomm_sock_debugfs_fops); - if (!rfcomm_sock_debugfs) - BT_ERR("Failed to create RFCOMM debug file"); - } - - BT_INFO("RFCOMM socket layer initialized"); - - return 0; - -error: - BT_ERR("RFCOMM socket layer registration failed"); - proto_unregister(&rfcomm_proto); - return err; -} - -void __exit rfcomm_cleanup_sockets(void) -{ - debugfs_remove(rfcomm_sock_debugfs); - - if (bt_sock_unregister(BTPROTO_RFCOMM) < 0) - BT_ERR("RFCOMM socket layer unregistration failed"); - - proto_unregister(&rfcomm_proto); -} diff --git a/net/bluetooth_tizen/rfcomm/tty.c b/net/bluetooth_tizen/rfcomm/tty.c deleted file mode 100644 index 4bf54b3..0000000 --- a/net/bluetooth_tizen/rfcomm/tty.c +++ /dev/null @@ -1,1188 +0,0 @@ -/* - RFCOMM implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> - Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* - * RFCOMM TTY. - */ - -#include <linux/module.h> - -#include <linux/tty.h> -#include <linux/tty_driver.h> -#include <linux/tty_flip.h> - -#include <linux/capability.h> -#include <linux/slab.h> -#include <linux/skbuff.h> -#include <linux/workqueue.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/rfcomm.h> - -#define RFCOMM_TTY_MAGIC 0x6d02 /* magic number for rfcomm struct */ -#define RFCOMM_TTY_PORTS RFCOMM_MAX_DEV /* whole lotta rfcomm devices */ -#define RFCOMM_TTY_MAJOR 216 /* device node major id of the usb/bluetooth.c driver */ -#define RFCOMM_TTY_MINOR 0 - -static struct tty_driver *rfcomm_tty_driver; - -struct rfcomm_dev { - struct list_head list; - atomic_t refcnt; - - char name[12]; - int id; - unsigned long flags; - atomic_t opened; - int err; - - bdaddr_t src; - bdaddr_t dst; - u8 channel; - - uint modem_status; - - struct rfcomm_dlc *dlc; - struct tty_struct *tty; - wait_queue_head_t wait; - struct work_struct wakeup_task; - - struct device *tty_dev; - - atomic_t wmem_alloc; - - struct sk_buff_head pending; -}; - -static LIST_HEAD(rfcomm_dev_list); -static DEFINE_SPINLOCK(rfcomm_dev_lock); - -static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb); -static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err); -static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig); - -static void rfcomm_tty_wakeup(struct work_struct *work); - -/* ---- Device functions ---- */ -static void rfcomm_dev_destruct(struct rfcomm_dev *dev) -{ - struct rfcomm_dlc *dlc = dev->dlc; - - BT_DBG("dev %p dlc %p", dev, dlc); - - /* Refcount should only hit zero when called from rfcomm_dev_del() - which will have taken us off the list. Everything else are - refcounting bugs. */ - BUG_ON(!list_empty(&dev->list)); - - rfcomm_dlc_lock(dlc); - /* Detach DLC if it's owned by this dev */ - if (dlc->owner == dev) - dlc->owner = NULL; - rfcomm_dlc_unlock(dlc); - - rfcomm_dlc_put(dlc); - - tty_unregister_device(rfcomm_tty_driver, dev->id); - - kfree(dev); - - /* It's safe to call module_put() here because socket still - holds reference to this module. */ - module_put(THIS_MODULE); -} - -static inline void rfcomm_dev_hold(struct rfcomm_dev *dev) -{ - atomic_inc(&dev->refcnt); -} - -static inline void rfcomm_dev_put(struct rfcomm_dev *dev) -{ - /* The reason this isn't actually a race, as you no - doubt have a little voice screaming at you in your - head, is that the refcount should never actually - reach zero unless the device has already been taken - off the list, in rfcomm_dev_del(). And if that's not - true, we'll hit the BUG() in rfcomm_dev_destruct() - anyway. */ - if (atomic_dec_and_test(&dev->refcnt)) - rfcomm_dev_destruct(dev); -} - -static struct rfcomm_dev *__rfcomm_dev_get(int id) -{ - struct rfcomm_dev *dev; - - list_for_each_entry(dev, &rfcomm_dev_list, list) - if (dev->id == id) - return dev; - - return NULL; -} - -static inline struct rfcomm_dev *rfcomm_dev_get(int id) -{ - struct rfcomm_dev *dev; - - spin_lock(&rfcomm_dev_lock); - - dev = __rfcomm_dev_get(id); - - if (dev) { - if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) - dev = NULL; - else - rfcomm_dev_hold(dev); - } - - spin_unlock(&rfcomm_dev_lock); - - return dev; -} - -static struct device *rfcomm_get_device(struct rfcomm_dev *dev) -{ - struct hci_dev *hdev; - struct hci_conn *conn; - - hdev = hci_get_route(&dev->dst, &dev->src); - if (!hdev) - return NULL; - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &dev->dst); - - hci_dev_put(hdev); - - return conn ? &conn->dev : NULL; -} - -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)); -} - -static ssize_t show_channel(struct device *tty_dev, struct device_attribute *attr, char *buf) -{ - struct rfcomm_dev *dev = dev_get_drvdata(tty_dev); - return sprintf(buf, "%d\n", dev->channel); -} - -static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); -static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL); - -static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) -{ - struct rfcomm_dev *dev, *entry; - struct list_head *head = &rfcomm_dev_list; - int err = 0; - - BT_DBG("id %d channel %d", req->dev_id, req->channel); - - dev = kzalloc(sizeof(struct rfcomm_dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - spin_lock(&rfcomm_dev_lock); - - if (req->dev_id < 0) { - dev->id = 0; - - list_for_each_entry(entry, &rfcomm_dev_list, list) { - if (entry->id != dev->id) - break; - - dev->id++; - head = &entry->list; - } - } else { - dev->id = req->dev_id; - - list_for_each_entry(entry, &rfcomm_dev_list, list) { - if (entry->id == dev->id) { - err = -EADDRINUSE; - goto out; - } - - if (entry->id > dev->id - 1) - break; - - head = &entry->list; - } - } - - if ((dev->id < 0) || (dev->id > RFCOMM_MAX_DEV - 1)) { - err = -ENFILE; - goto out; - } - - sprintf(dev->name, "rfcomm%d", dev->id); - - list_add(&dev->list, head); - atomic_set(&dev->refcnt, 1); - - bacpy(&dev->src, &req->src); - bacpy(&dev->dst, &req->dst); - dev->channel = req->channel; - - dev->flags = req->flags & - ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC)); - - atomic_set(&dev->opened, 0); - - init_waitqueue_head(&dev->wait); - INIT_WORK(&dev->wakeup_task, rfcomm_tty_wakeup); - - skb_queue_head_init(&dev->pending); - - rfcomm_dlc_lock(dlc); - - if (req->flags & (1 << RFCOMM_REUSE_DLC)) { - struct sock *sk = dlc->owner; - struct sk_buff *skb; - - BUG_ON(!sk); - - rfcomm_dlc_throttle(dlc); - - while ((skb = skb_dequeue(&sk->sk_receive_queue))) { - skb_orphan(skb); - skb_queue_tail(&dev->pending, skb); - atomic_sub(skb->len, &sk->sk_rmem_alloc); - } - } - - dlc->data_ready = rfcomm_dev_data_ready; - dlc->state_change = rfcomm_dev_state_change; - dlc->modem_status = rfcomm_dev_modem_status; - - dlc->owner = dev; - dev->dlc = dlc; - - rfcomm_dev_modem_status(dlc, dlc->remote_v24_sig); - - rfcomm_dlc_unlock(dlc); - - /* It's safe to call __module_get() here because socket already - holds reference to this module. */ - __module_get(THIS_MODULE); - -out: - spin_unlock(&rfcomm_dev_lock); - - if (err < 0) - goto free; - - dev->tty_dev = tty_register_device(rfcomm_tty_driver, dev->id, NULL); - - if (IS_ERR(dev->tty_dev)) { - err = PTR_ERR(dev->tty_dev); - list_del(&dev->list); - goto free; - } - - dev_set_drvdata(dev->tty_dev, dev); - - if (device_create_file(dev->tty_dev, &dev_attr_address) < 0) - BT_ERR("Failed to create address attribute"); - - if (device_create_file(dev->tty_dev, &dev_attr_channel) < 0) - BT_ERR("Failed to create channel attribute"); - - return dev->id; - -free: - kfree(dev); - return err; -} - -static void rfcomm_dev_del(struct rfcomm_dev *dev) -{ - BT_DBG("dev %p", dev); - - BUG_ON(test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags)); - - if (atomic_read(&dev->opened) > 0) - return; - - spin_lock(&rfcomm_dev_lock); - list_del_init(&dev->list); - spin_unlock(&rfcomm_dev_lock); - - rfcomm_dev_put(dev); -} - -/* ---- Send buffer ---- */ -static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc) -{ - /* We can't let it be zero, because we don't get a callback - when tx_credits becomes nonzero, hence we'd never wake up */ - return dlc->mtu * (dlc->tx_credits?:1); -} - -static void rfcomm_wfree(struct sk_buff *skb) -{ - struct rfcomm_dev *dev = (void *) skb->sk; - atomic_sub(skb->truesize, &dev->wmem_alloc); - if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags)) - queue_work(system_nrt_wq, &dev->wakeup_task); - rfcomm_dev_put(dev); -} - -static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev) -{ - rfcomm_dev_hold(dev); - atomic_add(skb->truesize, &dev->wmem_alloc); - skb->sk = (void *) dev; - skb->destructor = rfcomm_wfree; -} - -static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, gfp_t priority) -{ - if (atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) { - struct sk_buff *skb = alloc_skb(size, priority); - if (skb) { - rfcomm_set_owner_w(skb, dev); - return skb; - } - } - return NULL; -} - -/* ---- Device IOCTLs ---- */ - -#define NOCAP_FLAGS ((1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP)) - -static int rfcomm_create_dev(struct sock *sk, void __user *arg) -{ - struct rfcomm_dev_req req; - struct rfcomm_dlc *dlc; - int id; - - if (copy_from_user(&req, arg, sizeof(req))) - return -EFAULT; - - BT_DBG("sk %p dev_id %d flags 0x%x", sk, req.dev_id, req.flags); - - if (req.flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) - return -EPERM; - - if (req.flags & (1 << RFCOMM_REUSE_DLC)) { - /* Socket must be connected */ - if (sk->sk_state != BT_CONNECTED) - return -EBADFD; - - dlc = rfcomm_pi(sk)->dlc; - rfcomm_dlc_hold(dlc); - } else { - dlc = rfcomm_dlc_alloc(GFP_KERNEL); - if (!dlc) - return -ENOMEM; - } - - id = rfcomm_dev_add(&req, dlc); - if (id < 0) { - rfcomm_dlc_put(dlc); - return id; - } - - if (req.flags & (1 << RFCOMM_REUSE_DLC)) { - /* DLC is now used by device. - * Socket must be disconnected */ - sk->sk_state = BT_CLOSED; - } - - return id; -} - -static int rfcomm_release_dev(void __user *arg) -{ - struct rfcomm_dev_req req; - struct rfcomm_dev *dev; - - if (copy_from_user(&req, arg, sizeof(req))) - return -EFAULT; - - BT_DBG("dev_id %d flags 0x%x", req.dev_id, req.flags); - - dev = rfcomm_dev_get(req.dev_id); - if (!dev) - return -ENODEV; - - if (dev->flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) { - rfcomm_dev_put(dev); - return -EPERM; - } - - if (req.flags & (1 << RFCOMM_HANGUP_NOW)) - rfcomm_dlc_close(dev->dlc, 0); - - /* Shut down TTY synchronously before freeing rfcomm_dev */ - if (dev->tty) - tty_vhangup(dev->tty); - - if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) - rfcomm_dev_del(dev); - rfcomm_dev_put(dev); - return 0; -} - -static int rfcomm_get_dev_list(void __user *arg) -{ - struct rfcomm_dev *dev; - struct rfcomm_dev_list_req *dl; - struct rfcomm_dev_info *di; - int n = 0, size, err; - u16 dev_num; - - BT_DBG(""); - - if (get_user(dev_num, (u16 __user *) arg)) - return -EFAULT; - - if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di)) - return -EINVAL; - - size = sizeof(*dl) + dev_num * sizeof(*di); - - dl = kmalloc(size, GFP_KERNEL); - if (!dl) - return -ENOMEM; - - di = dl->dev_info; - - spin_lock(&rfcomm_dev_lock); - - list_for_each_entry(dev, &rfcomm_dev_list, list) { - if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) - continue; - (di + n)->id = dev->id; - (di + n)->flags = dev->flags; - (di + n)->state = dev->dlc->state; - (di + n)->channel = dev->channel; - bacpy(&(di + n)->src, &dev->src); - bacpy(&(di + n)->dst, &dev->dst); - if (++n >= dev_num) - break; - } - - spin_unlock(&rfcomm_dev_lock); - - dl->dev_num = n; - size = sizeof(*dl) + n * sizeof(*di); - - err = copy_to_user(arg, dl, size); - kfree(dl); - - return err ? -EFAULT : 0; -} - -static int rfcomm_get_dev_info(void __user *arg) -{ - struct rfcomm_dev *dev; - struct rfcomm_dev_info di; - int err = 0; - - BT_DBG(""); - - if (copy_from_user(&di, arg, sizeof(di))) - return -EFAULT; - - dev = rfcomm_dev_get(di.id); - if (!dev) - return -ENODEV; - - di.flags = dev->flags; - di.channel = dev->channel; - di.state = dev->dlc->state; - bacpy(&di.src, &dev->src); - bacpy(&di.dst, &dev->dst); - - if (copy_to_user(arg, &di, sizeof(di))) - err = -EFAULT; - - rfcomm_dev_put(dev); - return err; -} - -int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) -{ - BT_DBG("cmd %d arg %p", cmd, arg); - - switch (cmd) { - case RFCOMMCREATEDEV: - return rfcomm_create_dev(sk, arg); - - case RFCOMMRELEASEDEV: - return rfcomm_release_dev(arg); - - case RFCOMMGETDEVLIST: - return rfcomm_get_dev_list(arg); - - case RFCOMMGETDEVINFO: - return rfcomm_get_dev_info(arg); - } - - return -EINVAL; -} - -/* ---- DLC callbacks ---- */ -static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb) -{ - struct rfcomm_dev *dev = dlc->owner; - struct tty_struct *tty; - - if (!dev) { - kfree_skb(skb); - return; - } - - tty = dev->tty; - if (!tty || !skb_queue_empty(&dev->pending)) { - skb_queue_tail(&dev->pending, skb); - return; - } - - BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len); - - tty_insert_flip_string(tty, skb->data, skb->len); - tty_flip_buffer_push(tty); - - kfree_skb(skb); -} - -static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) -{ - struct rfcomm_dev *dev = dlc->owner; - if (!dev) - return; - - BT_DBG("dlc %p dev %p err %d", dlc, dev, err); - - dev->err = err; - wake_up_interruptible(&dev->wait); - - if (dlc->state == BT_CLOSED) { - if (!dev->tty) { - if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { - /* Drop DLC lock here to avoid deadlock - * 1. rfcomm_dev_get will take rfcomm_dev_lock - * but in rfcomm_dev_add there's lock order: - * rfcomm_dev_lock -> dlc lock - * 2. rfcomm_dev_put will deadlock if it's - * the last reference - */ - rfcomm_dlc_unlock(dlc); - if (rfcomm_dev_get(dev->id) == NULL) { - rfcomm_dlc_lock(dlc); - return; - } - - rfcomm_dev_del(dev); - rfcomm_dev_put(dev); - rfcomm_dlc_lock(dlc); - } - } else - tty_hangup(dev->tty); - } -} - -static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig) -{ - struct rfcomm_dev *dev = dlc->owner; - if (!dev) - return; - - BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig); - - if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV)) { - if (dev->tty && !C_CLOCAL(dev->tty)) - tty_hangup(dev->tty); - } - - dev->modem_status = - ((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) | - ((v24_sig & RFCOMM_V24_RTR) ? (TIOCM_RTS | TIOCM_CTS) : 0) | - ((v24_sig & RFCOMM_V24_IC) ? TIOCM_RI : 0) | - ((v24_sig & RFCOMM_V24_DV) ? TIOCM_CD : 0); -} - -/* ---- TTY functions ---- */ -static void rfcomm_tty_wakeup(struct work_struct *work) -{ - struct rfcomm_dev *dev = container_of(work, struct rfcomm_dev, - wakeup_task); - struct tty_struct *tty = dev->tty; - if (!tty) - return; - - BT_DBG("dev %p tty %p", dev, tty); - tty_wakeup(tty); -} - -static void rfcomm_tty_copy_pending(struct rfcomm_dev *dev) -{ - struct tty_struct *tty = dev->tty; - struct sk_buff *skb; - int inserted = 0; - - if (!tty) - return; - - BT_DBG("dev %p tty %p", dev, tty); - - rfcomm_dlc_lock(dev->dlc); - - while ((skb = skb_dequeue(&dev->pending))) { - inserted += tty_insert_flip_string(tty, skb->data, skb->len); - kfree_skb(skb); - } - - rfcomm_dlc_unlock(dev->dlc); - - if (inserted > 0) - tty_flip_buffer_push(tty); -} - -static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) -{ - DECLARE_WAITQUEUE(wait, current); - struct rfcomm_dev *dev; - struct rfcomm_dlc *dlc; - int err, id; - - id = tty->index; - - BT_DBG("tty %p id %d", tty, id); - - /* We don't leak this refcount. For reasons which are not entirely - clear, the TTY layer will call our ->close() method even if the - open fails. We decrease the refcount there, and decreasing it - here too would cause breakage. */ - dev = rfcomm_dev_get(id); - if (!dev) - return -ENODEV; - - BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), - dev->channel, atomic_read(&dev->opened)); - - if (atomic_inc_return(&dev->opened) > 1) - return 0; - - dlc = dev->dlc; - - /* Attach TTY and open DLC */ - - rfcomm_dlc_lock(dlc); - tty->driver_data = dev; - dev->tty = tty; - rfcomm_dlc_unlock(dlc); - set_bit(RFCOMM_TTY_ATTACHED, &dev->flags); - - err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel); - if (err < 0) - return err; - - /* Wait for DLC to connect */ - add_wait_queue(&dev->wait, &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (dlc->state == BT_CLOSED) { - err = -dev->err; - break; - } - - if (dlc->state == BT_CONNECTED) - break; - - if (signal_pending(current)) { - err = -EINTR; - break; - } - - tty_unlock(); - schedule(); - tty_lock(); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&dev->wait, &wait); - - if (err == 0) - device_move(dev->tty_dev, rfcomm_get_device(dev), - DPM_ORDER_DEV_AFTER_PARENT); - - rfcomm_tty_copy_pending(dev); - - rfcomm_dlc_unthrottle(dev->dlc); - - return err; -} - -static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - if (!dev) - return; - - BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, - atomic_read(&dev->opened)); - - if (atomic_dec_and_test(&dev->opened)) { - if (dev->tty_dev->parent) - device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST); - - /* Close DLC and dettach TTY */ - rfcomm_dlc_close(dev->dlc, 0); - - clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); - cancel_work_sync(&dev->wakeup_task); - - rfcomm_dlc_lock(dev->dlc); - tty->driver_data = NULL; - dev->tty = NULL; - rfcomm_dlc_unlock(dev->dlc); - - if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) { - spin_lock(&rfcomm_dev_lock); - list_del_init(&dev->list); - spin_unlock(&rfcomm_dev_lock); - - rfcomm_dev_put(dev); - } - } - - rfcomm_dev_put(dev); -} - -static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - struct rfcomm_dlc *dlc = dev->dlc; - struct sk_buff *skb; - int err = 0, sent = 0, size; - - BT_DBG("tty %p count %d", tty, count); - - while (count) { - size = min_t(uint, count, dlc->mtu); - - skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC); - - if (!skb) - break; - - skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); - - memcpy(skb_put(skb, size), buf + sent, size); - - err = rfcomm_dlc_send(dlc, skb); - if (err < 0) { - kfree_skb(skb); - break; - } - - sent += size; - count -= size; - } - - return sent ? sent : err; -} - -static int rfcomm_tty_write_room(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - int room; - - BT_DBG("tty %p", tty); - - if (!dev || !dev->dlc) - return 0; - - room = rfcomm_room(dev->dlc) - atomic_read(&dev->wmem_alloc); - if (room < 0) - room = 0; - - return room; -} - -static int rfcomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) -{ - BT_DBG("tty %p cmd 0x%02x", tty, cmd); - - switch (cmd) { - case TCGETS: - BT_DBG("TCGETS is not supported"); - return -ENOIOCTLCMD; - - case TCSETS: - BT_DBG("TCSETS is not supported"); - return -ENOIOCTLCMD; - - case TIOCMIWAIT: - BT_DBG("TIOCMIWAIT"); - break; - - case TIOCGSERIAL: - BT_ERR("TIOCGSERIAL is not supported"); - return -ENOIOCTLCMD; - - case TIOCSSERIAL: - BT_ERR("TIOCSSERIAL is not supported"); - return -ENOIOCTLCMD; - - case TIOCSERGSTRUCT: - BT_ERR("TIOCSERGSTRUCT is not supported"); - return -ENOIOCTLCMD; - - case TIOCSERGETLSR: - BT_ERR("TIOCSERGETLSR is not supported"); - return -ENOIOCTLCMD; - - case TIOCSERCONFIG: - BT_ERR("TIOCSERCONFIG is not supported"); - return -ENOIOCTLCMD; - - default: - return -ENOIOCTLCMD; /* ioctls which we must ignore */ - - } - - return -ENOIOCTLCMD; -} - -static void rfcomm_tty_set_termios(struct tty_struct *tty, struct ktermios *old) -{ - struct ktermios *new = tty->termios; - int old_baud_rate = tty_termios_baud_rate(old); - int new_baud_rate = tty_termios_baud_rate(new); - - u8 baud, data_bits, stop_bits, parity, x_on, x_off; - u16 changes = 0; - - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p termios %p", tty, old); - - if (!dev || !dev->dlc || !dev->dlc->session) - return; - - /* Handle turning off CRTSCTS */ - if ((old->c_cflag & CRTSCTS) && !(new->c_cflag & CRTSCTS)) - BT_DBG("Turning off CRTSCTS unsupported"); - - /* Parity on/off and when on, odd/even */ - if (((old->c_cflag & PARENB) != (new->c_cflag & PARENB)) || - ((old->c_cflag & PARODD) != (new->c_cflag & PARODD))) { - changes |= RFCOMM_RPN_PM_PARITY; - BT_DBG("Parity change detected."); - } - - /* Mark and space parity are not supported! */ - if (new->c_cflag & PARENB) { - if (new->c_cflag & PARODD) { - BT_DBG("Parity is ODD"); - parity = RFCOMM_RPN_PARITY_ODD; - } else { - BT_DBG("Parity is EVEN"); - parity = RFCOMM_RPN_PARITY_EVEN; - } - } else { - BT_DBG("Parity is OFF"); - parity = RFCOMM_RPN_PARITY_NONE; - } - - /* Setting the x_on / x_off characters */ - if (old->c_cc[VSTOP] != new->c_cc[VSTOP]) { - BT_DBG("XOFF custom"); - x_on = new->c_cc[VSTOP]; - changes |= RFCOMM_RPN_PM_XON; - } else { - BT_DBG("XOFF default"); - x_on = RFCOMM_RPN_XON_CHAR; - } - - if (old->c_cc[VSTART] != new->c_cc[VSTART]) { - BT_DBG("XON custom"); - x_off = new->c_cc[VSTART]; - changes |= RFCOMM_RPN_PM_XOFF; - } else { - BT_DBG("XON default"); - x_off = RFCOMM_RPN_XOFF_CHAR; - } - - /* Handle setting of stop bits */ - if ((old->c_cflag & CSTOPB) != (new->c_cflag & CSTOPB)) - changes |= RFCOMM_RPN_PM_STOP; - - /* POSIX does not support 1.5 stop bits and RFCOMM does not - * support 2 stop bits. So a request for 2 stop bits gets - * translated to 1.5 stop bits */ - if (new->c_cflag & CSTOPB) - stop_bits = RFCOMM_RPN_STOP_15; - else - stop_bits = RFCOMM_RPN_STOP_1; - - /* Handle number of data bits [5-8] */ - if ((old->c_cflag & CSIZE) != (new->c_cflag & CSIZE)) - changes |= RFCOMM_RPN_PM_DATA; - - switch (new->c_cflag & CSIZE) { - case CS5: - data_bits = RFCOMM_RPN_DATA_5; - break; - case CS6: - data_bits = RFCOMM_RPN_DATA_6; - break; - case CS7: - data_bits = RFCOMM_RPN_DATA_7; - break; - case CS8: - data_bits = RFCOMM_RPN_DATA_8; - break; - default: - data_bits = RFCOMM_RPN_DATA_8; - break; - } - - /* Handle baudrate settings */ - if (old_baud_rate != new_baud_rate) - changes |= RFCOMM_RPN_PM_BITRATE; - - switch (new_baud_rate) { - case 2400: - baud = RFCOMM_RPN_BR_2400; - break; - case 4800: - baud = RFCOMM_RPN_BR_4800; - break; - case 7200: - baud = RFCOMM_RPN_BR_7200; - break; - case 9600: - baud = RFCOMM_RPN_BR_9600; - break; - case 19200: - baud = RFCOMM_RPN_BR_19200; - break; - case 38400: - baud = RFCOMM_RPN_BR_38400; - break; - case 57600: - baud = RFCOMM_RPN_BR_57600; - break; - case 115200: - baud = RFCOMM_RPN_BR_115200; - break; - case 230400: - baud = RFCOMM_RPN_BR_230400; - break; - default: - /* 9600 is standard accordinag to the RFCOMM specification */ - baud = RFCOMM_RPN_BR_9600; - break; - - } - - if (changes) - rfcomm_send_rpn(dev->dlc->session, 1, dev->dlc->dlci, baud, - data_bits, stop_bits, parity, - RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes); -} - -static void rfcomm_tty_throttle(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p dev %p", tty, dev); - - rfcomm_dlc_throttle(dev->dlc); -} - -static void rfcomm_tty_unthrottle(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p dev %p", tty, dev); - - rfcomm_dlc_unthrottle(dev->dlc); -} - -static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p dev %p", tty, dev); - - if (!dev || !dev->dlc) - return 0; - - if (!skb_queue_empty(&dev->dlc->tx_queue)) - return dev->dlc->mtu; - - return 0; -} - -static void rfcomm_tty_flush_buffer(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p dev %p", tty, dev); - - if (!dev || !dev->dlc) - return; - - skb_queue_purge(&dev->dlc->tx_queue); - tty_wakeup(tty); -} - -static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch) -{ - BT_DBG("tty %p ch %c", tty, ch); -} - -static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout) -{ - BT_DBG("tty %p timeout %d", tty, timeout); -} - -static void rfcomm_tty_hangup(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p dev %p", tty, dev); - - if (!dev) - return; - - rfcomm_tty_flush_buffer(tty); - - if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { - if (rfcomm_dev_get(dev->id) == NULL) - return; - rfcomm_dev_del(dev); - rfcomm_dev_put(dev); - } -} - -static int rfcomm_tty_tiocmget(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p dev %p", tty, dev); - - return dev->modem_status; -} - -static int rfcomm_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - struct rfcomm_dlc *dlc = dev->dlc; - u8 v24_sig; - - BT_DBG("tty %p dev %p set 0x%02x clear 0x%02x", tty, dev, set, clear); - - rfcomm_dlc_get_modem_status(dlc, &v24_sig); - - if (set & TIOCM_DSR || set & TIOCM_DTR) - v24_sig |= RFCOMM_V24_RTC; - if (set & TIOCM_RTS || set & TIOCM_CTS) - v24_sig |= RFCOMM_V24_RTR; - if (set & TIOCM_RI) - v24_sig |= RFCOMM_V24_IC; - if (set & TIOCM_CD) - v24_sig |= RFCOMM_V24_DV; - - if (clear & TIOCM_DSR || clear & TIOCM_DTR) - v24_sig &= ~RFCOMM_V24_RTC; - if (clear & TIOCM_RTS || clear & TIOCM_CTS) - v24_sig &= ~RFCOMM_V24_RTR; - if (clear & TIOCM_RI) - v24_sig &= ~RFCOMM_V24_IC; - if (clear & TIOCM_CD) - v24_sig &= ~RFCOMM_V24_DV; - - rfcomm_dlc_set_modem_status(dlc, v24_sig); - - return 0; -} - -/* ---- TTY structure ---- */ - -static const struct tty_operations rfcomm_ops = { - .open = rfcomm_tty_open, - .close = rfcomm_tty_close, - .write = rfcomm_tty_write, - .write_room = rfcomm_tty_write_room, - .chars_in_buffer = rfcomm_tty_chars_in_buffer, - .flush_buffer = rfcomm_tty_flush_buffer, - .ioctl = rfcomm_tty_ioctl, - .throttle = rfcomm_tty_throttle, - .unthrottle = rfcomm_tty_unthrottle, - .set_termios = rfcomm_tty_set_termios, - .send_xchar = rfcomm_tty_send_xchar, - .hangup = rfcomm_tty_hangup, - .wait_until_sent = rfcomm_tty_wait_until_sent, - .tiocmget = rfcomm_tty_tiocmget, - .tiocmset = rfcomm_tty_tiocmset, -}; - -int __init rfcomm_init_ttys(void) -{ - int error; - - rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS); - if (!rfcomm_tty_driver) - return -ENOMEM; - - rfcomm_tty_driver->driver_name = "rfcomm"; - rfcomm_tty_driver->name = "rfcomm"; - rfcomm_tty_driver->major = RFCOMM_TTY_MAJOR; - rfcomm_tty_driver->minor_start = RFCOMM_TTY_MINOR; - rfcomm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - rfcomm_tty_driver->subtype = SERIAL_TYPE_NORMAL; - rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - rfcomm_tty_driver->init_termios = tty_std_termios; - rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON; - tty_set_operations(rfcomm_tty_driver, &rfcomm_ops); - - error = tty_register_driver(rfcomm_tty_driver); - if (error) { - BT_ERR("Can't register RFCOMM TTY driver"); - put_tty_driver(rfcomm_tty_driver); - return error; - } - - BT_INFO("RFCOMM TTY layer initialized"); - - return 0; -} - -void rfcomm_cleanup_ttys(void) -{ - tty_unregister_driver(rfcomm_tty_driver); - put_tty_driver(rfcomm_tty_driver); -} diff --git a/net/bluetooth_tizen/sco.c b/net/bluetooth_tizen/sco.c deleted file mode 100644 index 8bf26d1..0000000 --- a/net/bluetooth_tizen/sco.c +++ /dev/null @@ -1,1061 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth SCO sockets. */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/socket.h> -#include <linux/skbuff.h> -#include <linux/device.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/list.h> -#include <linux/security.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <linux/uaccess.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/sco.h> - -static bool disable_esco; - -static const struct proto_ops sco_sock_ops; - -static struct bt_sock_list sco_sk_list = { - .lock = __RW_LOCK_UNLOCKED(sco_sk_list.lock) -}; - -static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent); -static void sco_chan_del(struct sock *sk, int err); - -static int sco_conn_del(struct hci_conn *conn, int err); - -static void sco_sock_close(struct sock *sk); -static void sco_sock_kill(struct sock *sk); - -/* ---- SCO timers ---- */ -static void sco_sock_timeout(unsigned long arg) -{ - struct sock *sk = (struct sock *) arg; - - BT_DBG("sock %p state %d", sk, sk->sk_state); - - bh_lock_sock(sk); - sk->sk_err = ETIMEDOUT; - sk->sk_state_change(sk); - bh_unlock_sock(sk); - - sco_sock_kill(sk); - sock_put(sk); -} - -static void sco_sock_set_timer(struct sock *sk, long timeout) -{ - BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout); - sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout); -} - -static void sco_sock_clear_timer(struct sock *sk) -{ - BT_DBG("sock %p state %d", sk, sk->sk_state); - sk_stop_timer(sk, &sk->sk_timer); -} - -/* ---- SCO connections ---- */ -static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status) -{ - struct hci_dev *hdev = hcon->hdev; - struct sco_conn *conn = hcon->sco_data; - - if (conn || status) - return conn; - - conn = kzalloc(sizeof(struct sco_conn), GFP_ATOMIC); - if (!conn) - return NULL; - - spin_lock_init(&conn->lock); - - hcon->sco_data = conn; - conn->hcon = hcon; - - conn->src = &hdev->bdaddr; - conn->dst = &hcon->dst; - - if (hdev->sco_mtu > 0) - conn->mtu = hdev->sco_mtu; - else - conn->mtu = 60; - - BT_DBG("hcon %p conn %p", hcon, conn); - - return conn; -} - -static inline struct sock *sco_chan_get(struct sco_conn *conn) -{ - struct sock *sk = NULL; - sco_conn_lock(conn); - sk = conn->sk; - sco_conn_unlock(conn); - return sk; -} - -static int sco_conn_del(struct hci_conn *hcon, int err) -{ - struct sco_conn *conn = hcon->sco_data; - struct sock *sk; - - if (!conn) - return 0; - - BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); - - /* Kill socket */ - sk = sco_chan_get(conn); - if (sk) { - bh_lock_sock(sk); - sco_sock_clear_timer(sk); - sco_chan_del(sk, err); - bh_unlock_sock(sk); - sco_sock_kill(sk); - } - - hcon->sco_data = NULL; - kfree(conn); - return 0; -} - -static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) -{ - int err = 0; - - sco_conn_lock(conn); - if (conn->sk) - err = -EBUSY; - else - __sco_chan_add(conn, sk, parent); - - sco_conn_unlock(conn); - return err; -} - -static int sco_connect(struct sock *sk) -{ - bdaddr_t *src = &bt_sk(sk)->src; - bdaddr_t *dst = &bt_sk(sk)->dst; - struct sco_conn *conn; - struct hci_conn *hcon; - struct hci_dev *hdev; - int err, type; - - BT_DBG("%s -> %s", batostr(src), batostr(dst)); - - hdev = hci_get_route(dst, src); - if (!hdev) - return -EHOSTUNREACH; - - hci_dev_lock(hdev); - - if (lmp_esco_capable(hdev) && !disable_esco) - type = ESCO_LINK; - else - type = SCO_LINK; - - hcon = hci_connect(hdev, type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING); - if (IS_ERR(hcon)) { - err = PTR_ERR(hcon); - goto done; - } - - conn = sco_conn_add(hcon, 0); - if (!conn) { - hci_conn_put(hcon); - err = -ENOMEM; - goto done; - } - - /* Update source addr of the socket */ - bacpy(src, conn->src); - - err = sco_chan_add(conn, sk, NULL); - if (err) - goto done; - - if (hcon->state == BT_CONNECTED) { - sco_sock_clear_timer(sk); - sk->sk_state = BT_CONNECTED; - } else { - sk->sk_state = BT_CONNECT; - sco_sock_set_timer(sk, sk->sk_sndtimeo); - } - -done: - hci_dev_unlock(hdev); - hci_dev_put(hdev); - return err; -} - -static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len) -{ - struct sco_conn *conn = sco_pi(sk)->conn; - struct sk_buff *skb; - int err, count; - - /* Check outgoing MTU */ - if (len > conn->mtu) - return -EINVAL; - - BT_DBG("sk %p len %d", sk, len); - - count = min_t(unsigned int, conn->mtu, len); - skb = bt_skb_send_alloc(sk, count, - msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) - return err; - - if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { - kfree_skb(skb); - return -EFAULT; - } - - hci_send_sco(conn->hcon, skb); - - return count; -} - -static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb) -{ - struct sock *sk = sco_chan_get(conn); - - if (!sk) - goto drop; - - BT_DBG("sk %p len %d", sk, skb->len); - - if (sk->sk_state != BT_CONNECTED) - goto drop; - - if (!sock_queue_rcv_skb(sk, skb)) - return; - -drop: - kfree_skb(skb); -} - -/* -------- Socket interface ---------- */ -static struct sock *__sco_get_sock_by_addr(bdaddr_t *ba) -{ - struct sock *sk; - struct hlist_node *node; - - sk_for_each(sk, node, &sco_sk_list.head) - if (!bacmp(&bt_sk(sk)->src, ba)) - goto found; - sk = NULL; -found: - return sk; -} - -/* Find socket listening on source bdaddr. - * Returns closest match. - */ -static struct sock *sco_get_sock_listen(bdaddr_t *src) -{ - struct sock *sk = NULL, *sk1 = NULL; - struct hlist_node *node; - - read_lock(&sco_sk_list.lock); - - sk_for_each(sk, node, &sco_sk_list.head) { - if (sk->sk_state != BT_LISTEN) - continue; - - /* Exact match. */ - if (!bacmp(&bt_sk(sk)->src, src)) - break; - - /* Closest match */ - if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) - sk1 = sk; - } - - read_unlock(&sco_sk_list.lock); - - return node ? sk : sk1; -} - -static void sco_sock_destruct(struct sock *sk) -{ - BT_DBG("sk %p", sk); - - skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); -} - -static void sco_sock_cleanup_listen(struct sock *parent) -{ - struct sock *sk; - - BT_DBG("parent %p", parent); - - /* Close not yet accepted channels */ - while ((sk = bt_accept_dequeue(parent, NULL))) { - sco_sock_close(sk); - sco_sock_kill(sk); - } - - parent->sk_state = BT_CLOSED; - sock_set_flag(parent, SOCK_ZAPPED); -} - -/* Kill socket (only if zapped and orphan) - * Must be called on unlocked socket. - */ -static void sco_sock_kill(struct sock *sk) -{ - if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) - return; - - BT_DBG("sk %p state %d", sk, sk->sk_state); - - /* Kill poor orphan */ - bt_sock_unlink(&sco_sk_list, sk); - sock_set_flag(sk, SOCK_DEAD); - sock_put(sk); -} - -static void __sco_sock_close(struct sock *sk) -{ - BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); - - switch (sk->sk_state) { - case BT_LISTEN: - sco_sock_cleanup_listen(sk); - break; - - case BT_CONNECTED: - case BT_CONFIG: - if (sco_pi(sk)->conn) { - sk->sk_state = BT_DISCONN; - sco_sock_set_timer(sk, SCO_DISCONN_TIMEOUT); - hci_conn_put(sco_pi(sk)->conn->hcon); - sco_pi(sk)->conn->hcon = NULL; - } else - sco_chan_del(sk, ECONNRESET); - break; - - case BT_CONNECT: - case BT_DISCONN: - sco_chan_del(sk, ECONNRESET); - break; - - default: - sock_set_flag(sk, SOCK_ZAPPED); - break; - } -} - -/* Must be called on unlocked socket. */ -static void sco_sock_close(struct sock *sk) -{ - sco_sock_clear_timer(sk); - lock_sock(sk); - __sco_sock_close(sk); - release_sock(sk); - sco_sock_kill(sk); -} - -static void sco_sock_init(struct sock *sk, struct sock *parent) -{ - BT_DBG("sk %p", sk); - - if (parent) { - sk->sk_type = parent->sk_type; - security_sk_clone(parent, sk); - } -} - -static struct proto sco_proto = { - .name = "SCO", - .owner = THIS_MODULE, - .obj_size = sizeof(struct sco_pinfo) -}; - -static struct sock *sco_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) -{ - struct sock *sk; - - sk = sk_alloc(net, PF_BLUETOOTH, prio, &sco_proto); - if (!sk) - return NULL; - - sock_init_data(sock, sk); - INIT_LIST_HEAD(&bt_sk(sk)->accept_q); - - sk->sk_destruct = sco_sock_destruct; - sk->sk_sndtimeo = SCO_CONN_TIMEOUT; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = proto; - sk->sk_state = BT_OPEN; - - setup_timer(&sk->sk_timer, sco_sock_timeout, (unsigned long)sk); - - bt_sock_link(&sco_sk_list, sk); - return sk; -} - -static int sco_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - sock->state = SS_UNCONNECTED; - - if (sock->type != SOCK_SEQPACKET) - return -ESOCKTNOSUPPORT; - - sock->ops = &sco_sock_ops; - - sk = sco_sock_alloc(net, sock, protocol, GFP_ATOMIC); - if (!sk) - return -ENOMEM; - - sco_sock_init(sk, NULL); - return 0; -} - -static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) -{ - struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; - struct sock *sk = sock->sk; - bdaddr_t *src = &sa->sco_bdaddr; - int err = 0; - - BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr)); - - if (!addr || addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state != BT_OPEN) { - err = -EBADFD; - goto done; - } - - write_lock(&sco_sk_list.lock); - - if (bacmp(src, BDADDR_ANY) && __sco_get_sock_by_addr(src)) { - err = -EADDRINUSE; - } else { - /* Save source address */ - bacpy(&bt_sk(sk)->src, &sa->sco_bdaddr); - sk->sk_state = BT_BOUND; - } - - write_unlock(&sco_sk_list.lock); - -done: - release_sock(sk); - return err; -} - -static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) -{ - struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; - struct sock *sk = sock->sk; - int err = 0; - - - BT_DBG("sk %p", sk); - - if (alen < sizeof(struct sockaddr_sco) || - addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) - return -EBADFD; - - if (sk->sk_type != SOCK_SEQPACKET) - return -EINVAL; - - lock_sock(sk); - - /* Set destination address and psm */ - bacpy(&bt_sk(sk)->dst, &sa->sco_bdaddr); - - err = sco_connect(sk); - if (err) - goto done; - - err = bt_sock_wait_state(sk, BT_CONNECTED, - sock_sndtimeo(sk, flags & O_NONBLOCK)); - -done: - release_sock(sk); - return err; -} - -static int sco_sock_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sk %p backlog %d", sk, backlog); - - lock_sock(sk); - - if (sk->sk_state != BT_BOUND || sock->type != SOCK_SEQPACKET) { - err = -EBADFD; - goto done; - } - - sk->sk_max_ack_backlog = backlog; - sk->sk_ack_backlog = 0; - sk->sk_state = BT_LISTEN; - -done: - release_sock(sk); - return err; -} - -static int sco_sock_accept(struct socket *sock, struct socket *newsock, int flags) -{ - DECLARE_WAITQUEUE(wait, current); - struct sock *sk = sock->sk, *ch; - long timeo; - int err = 0; - - lock_sock(sk); - - timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); - - BT_DBG("sk %p timeo %ld", sk, timeo); - - /* Wait for an incoming connection. (wake-one). */ - add_wait_queue_exclusive(sk_sleep(sk), &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; - break; - } - - ch = bt_accept_dequeue(sk, newsock); - if (ch) - break; - - if (!timeo) { - err = -EAGAIN; - break; - } - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - if (err) - goto done; - - newsock->state = SS_CONNECTED; - - BT_DBG("new socket %p", ch); - -done: - release_sock(sk); - return err; -} - -static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) -{ - struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; - struct sock *sk = sock->sk; - - BT_DBG("sock %p, sk %p", sock, sk); - - addr->sa_family = AF_BLUETOOTH; - *len = sizeof(struct sockaddr_sco); - - if (peer) - bacpy(&sa->sco_bdaddr, &bt_sk(sk)->dst); - else - bacpy(&sa->sco_bdaddr, &bt_sk(sk)->src); - - return 0; -} - -static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - int err; - - BT_DBG("sock %p, sk %p", sock, sk); - - err = sock_error(sk); - if (err) - return err; - - if (msg->msg_flags & MSG_OOB) - return -EOPNOTSUPP; - - lock_sock(sk); - - if (sk->sk_state == BT_CONNECTED) - err = sco_send_frame(sk, msg, len); - else - err = -ENOTCONN; - - release_sock(sk); - return err; -} - -static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sk %p", sk); - - lock_sock(sk); - - switch (optname) { - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct sco_options opts; - struct sco_conninfo cinfo; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case SCO_OPTIONS: - if (sk->sk_state != BT_CONNECTED) { - err = -ENOTCONN; - break; - } - - opts.mtu = sco_pi(sk)->conn->mtu; - - BT_DBG("mtu %d", opts.mtu); - - len = min_t(unsigned int, len, sizeof(opts)); - if (copy_to_user(optval, (char *)&opts, len)) - err = -EFAULT; - - break; - - case SCO_CONNINFO: - if (sk->sk_state != BT_CONNECTED) { - err = -ENOTCONN; - break; - } - - memset(&cinfo, 0, sizeof(cinfo)); - cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle; - memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3); - - len = min_t(unsigned int, len, sizeof(cinfo)); - if (copy_to_user(optval, (char *)&cinfo, len)) - err = -EFAULT; - - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (level == SOL_SCO) - return sco_sock_getsockopt_old(sock, optname, optval, optlen); - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int sco_sock_shutdown(struct socket *sock, int how) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - lock_sock(sk); - if (!sk->sk_shutdown) { - sk->sk_shutdown = SHUTDOWN_MASK; - sco_sock_clear_timer(sk); - __sco_sock_close(sk); - - if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) - err = bt_sock_wait_state(sk, BT_CLOSED, - sk->sk_lingertime); - } - release_sock(sk); - return err; -} - -static int sco_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - sco_sock_close(sk); - - if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) { - lock_sock(sk); - err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime); - release_sock(sk); - } - - sock_orphan(sk); - sco_sock_kill(sk); - return err; -} - -static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) -{ - BT_DBG("conn %p", conn); - - sco_pi(sk)->conn = conn; - conn->sk = sk; - - if (parent) - bt_accept_enqueue(parent, sk); -} - -/* Delete channel. - * Must be called on the locked socket. */ -static void sco_chan_del(struct sock *sk, int err) -{ - struct sco_conn *conn; - - conn = sco_pi(sk)->conn; - - BT_DBG("sk %p, conn %p, err %d", sk, conn, err); - - if (conn) { - sco_conn_lock(conn); - conn->sk = NULL; - sco_pi(sk)->conn = NULL; - sco_conn_unlock(conn); - - if (conn->hcon) - hci_conn_put(conn->hcon); - } - - sk->sk_state = BT_CLOSED; - sk->sk_err = err; - sk->sk_state_change(sk); - - sock_set_flag(sk, SOCK_ZAPPED); -} - -static void sco_conn_ready(struct sco_conn *conn) -{ - struct sock *parent; - struct sock *sk = conn->sk; - - BT_DBG("conn %p", conn); - - sco_conn_lock(conn); - - if (sk) { - sco_sock_clear_timer(sk); - bh_lock_sock(sk); - sk->sk_state = BT_CONNECTED; - sk->sk_state_change(sk); - bh_unlock_sock(sk); - } else { - parent = sco_get_sock_listen(conn->src); - if (!parent) - goto done; - - bh_lock_sock(parent); - - sk = sco_sock_alloc(sock_net(parent), NULL, - BTPROTO_SCO, GFP_ATOMIC); - if (!sk) { - bh_unlock_sock(parent); - goto done; - } - - sco_sock_init(sk, parent); - - bacpy(&bt_sk(sk)->src, conn->src); - bacpy(&bt_sk(sk)->dst, conn->dst); - - hci_conn_hold(conn->hcon); - __sco_chan_add(conn, sk, parent); - - sk->sk_state = BT_CONNECTED; - - /* Wake up parent */ - parent->sk_data_ready(parent, 1); - - bh_unlock_sock(parent); - } - -done: - sco_conn_unlock(conn); -} - -/* ----- SCO interface with lower layer (HCI) ----- */ -int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - register struct sock *sk; - struct hlist_node *node; - int lm = 0; - - BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); - - /* Find listening sockets */ - read_lock(&sco_sk_list.lock); - sk_for_each(sk, node, &sco_sk_list.head) { - if (sk->sk_state != BT_LISTEN) - continue; - - if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) || - !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { - lm |= HCI_LM_ACCEPT; - break; - } - } - read_unlock(&sco_sk_list.lock); - - return lm; -} - -int sco_connect_cfm(struct hci_conn *hcon, __u8 status) -{ - BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); - if (!status) { - struct sco_conn *conn; - - conn = sco_conn_add(hcon, status); - if (conn) - sco_conn_ready(conn); - } else - sco_conn_del(hcon, bt_to_errno(status)); - - return 0; -} - -int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason) -{ - BT_DBG("hcon %p reason %d", hcon, reason); - - sco_conn_del(hcon, bt_to_errno(reason)); - return 0; -} - -int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb) -{ - struct sco_conn *conn = hcon->sco_data; - - if (!conn) - goto drop; - - BT_DBG("conn %p len %d", conn, skb->len); - - if (skb->len) { - sco_recv_frame(conn, skb); - return 0; - } - -drop: - kfree_skb(skb); - return 0; -} - -static int sco_debugfs_show(struct seq_file *f, void *p) -{ - struct sock *sk; - struct hlist_node *node; - - 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); - } - - read_unlock(&sco_sk_list.lock); - - return 0; -} - -static int sco_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, sco_debugfs_show, inode->i_private); -} - -static const struct file_operations sco_debugfs_fops = { - .open = sco_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *sco_debugfs; - -static const struct proto_ops sco_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = sco_sock_release, - .bind = sco_sock_bind, - .connect = sco_sock_connect, - .listen = sco_sock_listen, - .accept = sco_sock_accept, - .getname = sco_sock_getname, - .sendmsg = sco_sock_sendmsg, - .recvmsg = bt_sock_recvmsg, - .poll = bt_sock_poll, - .ioctl = bt_sock_ioctl, - .mmap = sock_no_mmap, - .socketpair = sock_no_socketpair, - .shutdown = sco_sock_shutdown, - .setsockopt = sco_sock_setsockopt, - .getsockopt = sco_sock_getsockopt -}; - -static const struct net_proto_family sco_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = sco_sock_create, -}; - -int __init sco_init(void) -{ - int err; - - err = proto_register(&sco_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_SCO, &sco_sock_family_ops); - if (err < 0) { - BT_ERR("SCO socket registration failed"); - goto error; - } - - if (bt_debugfs) { - sco_debugfs = debugfs_create_file("sco", 0444, - bt_debugfs, NULL, &sco_debugfs_fops); - if (!sco_debugfs) - BT_ERR("Failed to create SCO debug file"); - } - - BT_INFO("SCO socket layer initialized"); - - return 0; - -error: - proto_unregister(&sco_proto); - return err; -} - -void __exit sco_exit(void) -{ - debugfs_remove(sco_debugfs); - - if (bt_sock_unregister(BTPROTO_SCO) < 0) - BT_ERR("SCO socket unregistration failed"); - - proto_unregister(&sco_proto); -} - -module_param(disable_esco, bool, 0644); -MODULE_PARM_DESC(disable_esco, "Disable eSCO connection creation"); diff --git a/net/bluetooth_tizen/smp.c b/net/bluetooth_tizen/smp.c deleted file mode 100644 index deb1198..0000000 --- a/net/bluetooth_tizen/smp.c +++ /dev/null @@ -1,1003 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> -#include <net/bluetooth/mgmt.h> -#include <net/bluetooth/smp.h> -#include <linux/crypto.h> -#include <linux/scatterlist.h> -#include <crypto/b128ops.h> - -#define SMP_TIMEOUT msecs_to_jiffies(30000) - -static inline void swap128(u8 src[16], u8 dst[16]) -{ - int i; - for (i = 0; i < 16; i++) - dst[15 - i] = src[i]; -} - -static inline void swap56(u8 src[7], u8 dst[7]) -{ - int i; - for (i = 0; i < 7; i++) - dst[6 - i] = src[i]; -} - -static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) -{ - struct blkcipher_desc desc; - struct scatterlist sg; - int err, iv_len; - unsigned char iv[128]; - - if (tfm == NULL) { - BT_ERR("tfm %p", tfm); - return -EINVAL; - } - - desc.tfm = tfm; - desc.flags = 0; - - err = crypto_blkcipher_setkey(tfm, k, 16); - if (err) { - BT_ERR("cipher setkey failed: %d", err); - return err; - } - - sg_init_one(&sg, r, 16); - - iv_len = crypto_blkcipher_ivsize(tfm); - if (iv_len) { - memset(&iv, 0xff, iv_len); - crypto_blkcipher_set_iv(tfm, iv, iv_len); - } - - err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16); - if (err) - BT_ERR("Encrypt data error %d", err); - - return err; -} - -static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16], - u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia, - u8 _rat, bdaddr_t *ra, u8 res[16]) -{ - u8 p1[16], p2[16]; - int err; - - memset(p1, 0, 16); - - /* p1 = pres || preq || _rat || _iat */ - swap56(pres, p1); - swap56(preq, p1 + 7); - p1[14] = _rat; - p1[15] = _iat; - - memset(p2, 0, 16); - - /* p2 = padding || ia || ra */ - baswap((bdaddr_t *) (p2 + 4), ia); - baswap((bdaddr_t *) (p2 + 10), ra); - - /* res = r XOR p1 */ - u128_xor((u128 *) res, (u128 *) r, (u128 *) p1); - - /* res = e(k, res) */ - err = smp_e(tfm, k, res); - if (err) { - BT_ERR("Encrypt data error"); - return err; - } - - /* res = res XOR p2 */ - u128_xor((u128 *) res, (u128 *) res, (u128 *) p2); - - /* res = e(k, res) */ - err = smp_e(tfm, k, res); - if (err) - BT_ERR("Encrypt data error"); - - return err; -} - -static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], - u8 r1[16], u8 r2[16], u8 _r[16]) -{ - int err; - - /* Just least significant octets from r1 and r2 are considered */ - memcpy(_r, r1 + 8, 8); - memcpy(_r + 8, r2 + 8, 8); - - err = smp_e(tfm, k, _r); - if (err) - BT_ERR("Encrypt data error"); - - return err; -} - -static int smp_rand(u8 *buf) -{ - get_random_bytes(buf, 16); - - return 0; -} - -static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code, - u16 dlen, void *data) -{ - struct sk_buff *skb; - struct l2cap_hdr *lh; - int len; - - len = L2CAP_HDR_SIZE + sizeof(code) + dlen; - - if (len > conn->mtu) - return NULL; - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) - return NULL; - - 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); - - memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code)); - - memcpy(skb_put(skb, dlen), data, dlen); - - return skb; -} - -static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) -{ - struct sk_buff *skb = smp_build_cmd(conn, code, len, data); - - BT_DBG("code 0x%2.2x", code); - - if (!skb) - return; - - skb->priority = HCI_PRIO_MAX; - hci_send_acl(conn->hchan, skb, 0); - - cancel_delayed_work_sync(&conn->security_timer); - schedule_delayed_work(&conn->security_timer, SMP_TIMEOUT); -} - -static __u8 authreq_to_seclevel(__u8 authreq) -{ - if (authreq & SMP_AUTH_MITM) - return BT_SECURITY_HIGH; - else - return BT_SECURITY_MEDIUM; -} - -static __u8 seclevel_to_authreq(__u8 sec_level) -{ - switch (sec_level) { - case BT_SECURITY_HIGH: - return SMP_AUTH_MITM | SMP_AUTH_BONDING; - case BT_SECURITY_MEDIUM: - return SMP_AUTH_BONDING; - default: - return SMP_AUTH_NONE; - } -} - -static void build_pairing_cmd(struct l2cap_conn *conn, - struct smp_cmd_pairing *req, - struct smp_cmd_pairing *rsp, - __u8 authreq) -{ - u8 dist_keys = 0; - - if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->dev_flags)) { - dist_keys = SMP_DIST_ENC_KEY; - authreq |= SMP_AUTH_BONDING; - } else { - authreq &= ~SMP_AUTH_BONDING; - } - - if (rsp == NULL) { - req->io_capability = conn->hcon->io_capability; - req->oob_flag = SMP_OOB_NOT_PRESENT; - req->max_key_size = SMP_MAX_ENC_KEY_SIZE; - req->init_key_dist = 0; - req->resp_key_dist = dist_keys; - req->auth_req = authreq; - return; - } - - rsp->io_capability = conn->hcon->io_capability; - rsp->oob_flag = SMP_OOB_NOT_PRESENT; - 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; -} - -static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) -{ - struct smp_chan *smp = conn->smp_chan; - - if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) || - (max_key_size < SMP_MIN_ENC_KEY_SIZE)) - return SMP_ENC_KEY_SIZE; - - smp->enc_key_size = max_key_size; - - return 0; -} - -static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send) -{ - struct hci_conn *hcon = conn->hcon; - - if (send) - smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), - &reason); - - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags); - mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type, - hcon->dst_type, reason); - - if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) { - cancel_delayed_work_sync(&conn->security_timer); - smp_chan_destroy(conn); - } -} - -#define JUST_WORKS 0x00 -#define JUST_CFM 0x01 -#define REQ_PASSKEY 0x02 -#define CFM_PASSKEY 0x03 -#define REQ_OOB 0x04 -#define OVERLAP 0xFF - -static const u8 gen_method[5][5] = { - { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY }, - { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY }, - { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY }, - { JUST_WORKS, JUST_CFM, JUST_WORKS, JUST_WORKS, JUST_CFM }, - { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP }, -}; - -static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, - u8 local_io, u8 remote_io) -{ - struct hci_conn *hcon = conn->hcon; - struct smp_chan *smp = conn->smp_chan; - u8 method; - u32 passkey = 0; - int ret = 0; - - /* Initialize key for JUST WORKS */ - memset(smp->tk, 0, sizeof(smp->tk)); - clear_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); - - BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io); - - /* If neither side wants MITM, use JUST WORKS */ - /* If either side has unknown io_caps, use JUST WORKS */ - /* Otherwise, look up method from the table */ - if (!(auth & SMP_AUTH_MITM) || - local_io > SMP_IO_KEYBOARD_DISPLAY || - remote_io > SMP_IO_KEYBOARD_DISPLAY) - method = JUST_WORKS; - else - method = gen_method[remote_io][local_io]; - - /* If not bonding, don't ask user to confirm a Zero TK */ - if (!(auth & SMP_AUTH_BONDING) && method == JUST_CFM) - method = JUST_WORKS; - - /* If Just Works, Continue with Zero TK */ - if (method == JUST_WORKS) { - set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); - return 0; - } - - /* Not Just Works/Confirm results in MITM Authentication */ - if (method != JUST_CFM) - set_bit(SMP_FLAG_MITM_AUTH, &smp->smp_flags); - - /* If both devices have Keyoard-Display I/O, the master - * Confirms and the slave Enters the passkey. - */ - if (method == OVERLAP) { - if (hcon->link_mode & HCI_LM_MASTER) - method = CFM_PASSKEY; - else - method = REQ_PASSKEY; - } - - /* Generate random passkey. Not valid until confirmed. */ - if (method == CFM_PASSKEY) { - u8 key[16]; - - memset(key, 0, sizeof(key)); - get_random_bytes(&passkey, sizeof(passkey)); - passkey %= 1000000; - put_unaligned_le32(passkey, key); - swap128(key, smp->tk); - BT_DBG("PassKey: %d", passkey); - } - - hci_dev_lock(hcon->hdev); - - if (method == REQ_PASSKEY) - ret = mgmt_user_passkey_request(hcon->hdev, conn->dst, - hcon->type, hcon->dst_type); - else - ret = mgmt_user_confirm_request(hcon->hdev, conn->dst, - hcon->type, hcon->dst_type, - cpu_to_le32(passkey), 0); - - hci_dev_unlock(hcon->hdev); - - return ret; -} - -static void confirm_work(struct work_struct *work) -{ - struct smp_chan *smp = container_of(work, struct smp_chan, confirm); - struct l2cap_conn *conn = smp->conn; - struct crypto_blkcipher *tfm; - struct smp_cmd_pairing_confirm cp; - int ret; - u8 res[16], reason; - - BT_DBG("conn %p", conn); - - tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(tfm)) { - reason = SMP_UNSPECIFIED; - goto error; - } - - smp->tfm = tfm; - - if (conn->hcon->out) - ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0, - conn->src, conn->hcon->dst_type, conn->dst, res); - else - ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, - conn->hcon->dst_type, conn->dst, 0, conn->src, - res); - if (ret) { - reason = SMP_UNSPECIFIED; - goto error; - } - - clear_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags); - - swap128(res, cp.confirm_val); - smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); - - return; - -error: - smp_failure(conn, reason, 1); -} - -static void random_work(struct work_struct *work) -{ - struct smp_chan *smp = container_of(work, struct smp_chan, random); - struct l2cap_conn *conn = smp->conn; - struct hci_conn *hcon = conn->hcon; - struct crypto_blkcipher *tfm = smp->tfm; - u8 reason, confirm[16], res[16], key[16]; - int ret; - - if (IS_ERR_OR_NULL(tfm)) { - reason = SMP_UNSPECIFIED; - goto error; - } - - BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - - if (hcon->out) - ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0, - conn->src, hcon->dst_type, conn->dst, res); - else - ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, - hcon->dst_type, conn->dst, 0, conn->src, res); - if (ret) { - reason = SMP_UNSPECIFIED; - goto error; - } - - swap128(res, confirm); - - if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) { - BT_ERR("Pairing failed (confirmation values mismatch)"); - reason = SMP_CONFIRM_FAILED; - goto error; - } - - if (hcon->out) { - u8 stk[16], rand[8]; - __le16 ediv; - - memset(rand, 0, sizeof(rand)); - ediv = 0; - - smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, key); - swap128(key, stk); - - memset(stk + smp->enc_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); - - if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) { - reason = SMP_UNSPECIFIED; - goto error; - } - - hci_le_start_enc(hcon, ediv, rand, stk); - hcon->enc_key_size = smp->enc_key_size; - } else { - u8 stk[16], r[16], rand[8]; - __le16 ediv; - - memset(rand, 0, sizeof(rand)); - ediv = 0; - - swap128(smp->prnd, r); - smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); - - smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, key); - swap128(key, stk); - - memset(stk + smp->enc_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); - - hci_add_ltk(hcon->hdev, conn->dst, hcon->dst_type, - HCI_SMP_STK_SLAVE, 0, 0, stk, smp->enc_key_size, - ediv, rand); - } - - return; - -error: - smp_failure(conn, reason, 1); -} - -static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) -{ - struct smp_chan *smp; - - smp = kzalloc(sizeof(struct smp_chan), GFP_ATOMIC); - if (!smp) - return NULL; - - INIT_WORK(&smp->confirm, confirm_work); - INIT_WORK(&smp->random, random_work); - - smp->conn = conn; - conn->smp_chan = smp; - conn->hcon->smp_conn = conn; - - hci_conn_hold(conn->hcon); - - return smp; -} - -void smp_chan_destroy(struct l2cap_conn *conn) -{ - struct smp_chan *smp = conn->smp_chan; - - BUG_ON(!smp); - - if (smp->tfm) - crypto_free_blkcipher(smp->tfm); - - kfree(smp); - conn->smp_chan = NULL; - conn->hcon->smp_conn = NULL; - hci_conn_put(conn->hcon); -} - -int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey) -{ - struct l2cap_conn *conn = hcon->smp_conn; - struct smp_chan *smp; - u32 value; - u8 key[16]; - - BT_DBG(""); - - if (!conn) - return -ENOTCONN; - - smp = conn->smp_chan; - - switch (mgmt_op) { - case MGMT_OP_USER_PASSKEY_REPLY: - value = le32_to_cpu(passkey); - memset(key, 0, sizeof(key)); - BT_DBG("PassKey: %d", value); - put_unaligned_le32(value, key); - swap128(key, smp->tk); - /* Fall Through */ - case MGMT_OP_USER_CONFIRM_REPLY: - set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); - break; - case MGMT_OP_USER_PASSKEY_NEG_REPLY: - case MGMT_OP_USER_CONFIRM_NEG_REPLY: - smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED, 1); - return 0; - default: - smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED, 1); - return -EOPNOTSUPP; - } - - /* If it is our turn to send Pairing Confirm, do so now */ - if (test_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags)) - queue_work(hcon->hdev->workqueue, &smp->confirm); - - return 0; -} - -static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_cmd_pairing rsp, *req = (void *) skb->data; - struct smp_chan *smp; - u8 key_size; - u8 auth = SMP_AUTH_NONE; - int ret; - - BT_DBG("conn %p", conn); - - if (conn->hcon->link_mode & HCI_LM_MASTER) - return SMP_CMD_NOTSUPP; - - if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) - smp = smp_chan_create(conn); - - smp = conn->smp_chan; - - smp->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&smp->preq[1], req, sizeof(*req)); - skb_pull(skb, sizeof(*req)); - - /* We didn't start the pairing, so match remote */ - if (req->auth_req & SMP_AUTH_BONDING) - auth = req->auth_req; - - conn->hcon->pending_sec_level = authreq_to_seclevel(auth); - - build_pairing_cmd(conn, req, &rsp, auth); - - key_size = min(req->max_key_size, rsp.max_key_size); - if (check_enc_key_size(conn, key_size)) - return SMP_ENC_KEY_SIZE; - - ret = smp_rand(smp->prnd); - if (ret) - return SMP_UNSPECIFIED; - - smp->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&smp->prsp[1], &rsp, sizeof(rsp)); - - smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp); - - /* Request setup of TK */ - ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability); - if (ret) - return SMP_UNSPECIFIED; - - return 0; -} - -static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_cmd_pairing *req, *rsp = (void *) skb->data; - struct smp_chan *smp = conn->smp_chan; - struct hci_dev *hdev = conn->hcon->hdev; - u8 key_size, auth = SMP_AUTH_NONE; - int ret; - - BT_DBG("conn %p", conn); - - if (!(conn->hcon->link_mode & HCI_LM_MASTER)) - return SMP_CMD_NOTSUPP; - - skb_pull(skb, sizeof(*rsp)); - - req = (void *) &smp->preq[1]; - - key_size = min(req->max_key_size, rsp->max_key_size); - if (check_enc_key_size(conn, key_size)) - return SMP_ENC_KEY_SIZE; - - ret = smp_rand(smp->prnd); - if (ret) - return SMP_UNSPECIFIED; - - smp->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&smp->prsp[1], rsp, sizeof(*rsp)); - - if ((req->auth_req & SMP_AUTH_BONDING) && - (rsp->auth_req & SMP_AUTH_BONDING)) - auth = SMP_AUTH_BONDING; - - auth |= (req->auth_req | rsp->auth_req) & SMP_AUTH_MITM; - - ret = tk_request(conn, 0, auth, rsp->io_capability, req->io_capability); - if (ret) - return SMP_UNSPECIFIED; - - set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags); - - /* Can't compose response until we have been confirmed */ - if (!test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) - return 0; - - queue_work(hdev->workqueue, &smp->confirm); - - return 0; -} - -static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_chan *smp = conn->smp_chan; - struct hci_dev *hdev = conn->hcon->hdev; - - BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - - memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf)); - skb_pull(skb, sizeof(smp->pcnf)); - - if (conn->hcon->out) { - u8 random[16]; - - swap128(smp->prnd, random); - smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random), - random); - } else if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) { - queue_work(hdev->workqueue, &smp->confirm); - } else { - set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags); - } - - return 0; -} - -static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_chan *smp = conn->smp_chan; - struct hci_dev *hdev = conn->hcon->hdev; - - BT_DBG("conn %p", conn); - - swap128(skb->data, smp->rrnd); - skb_pull(skb, sizeof(smp->rrnd)); - - queue_work(hdev->workqueue, &smp->random); - - return 0; -} - -static u8 smp_ltk_encrypt(struct l2cap_conn *conn) -{ - struct smp_ltk *key; - struct hci_conn *hcon = conn->hcon; - - key = hci_find_ltk_by_addr(hcon->hdev, conn->dst, hcon->dst_type); - if (!key) - return 0; - - if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) - return 1; - - hci_le_start_enc(hcon, key->ediv, key->rand, key->val); - hcon->enc_key_size = key->enc_size; - - return 1; - -} -static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_cmd_security_req *rp = (void *) skb->data; - struct smp_cmd_pairing cp; - struct hci_conn *hcon = conn->hcon; - struct smp_chan *smp; - - BT_DBG("conn %p", conn); - - hcon->pending_sec_level = authreq_to_seclevel(rp->auth_req); - - if (smp_ltk_encrypt(conn)) - return 0; - - if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) - return 0; - - smp = smp_chan_create(conn); - - skb_pull(skb, sizeof(*rp)); - - memset(&cp, 0, sizeof(cp)); - build_pairing_cmd(conn, &cp, NULL, rp->auth_req); - - smp->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&smp->preq[1], &cp, sizeof(cp)); - - smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); - - return 0; -} - -int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) -{ - struct hci_conn *hcon = conn->hcon; - struct smp_chan *smp = conn->smp_chan; - __u8 authreq; - - BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level); - - if (!lmp_host_le_capable(hcon->hdev)) - return 1; - - if (sec_level == BT_SECURITY_LOW) - return 1; - - if (hcon->sec_level >= sec_level) - return 1; - - if (hcon->link_mode & HCI_LM_MASTER) - if (smp_ltk_encrypt(conn)) - goto done; - - if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) - return 0; - - smp = smp_chan_create(conn); - if (!smp) - return 1; - - authreq = seclevel_to_authreq(sec_level); - - if (hcon->link_mode & HCI_LM_MASTER) { - struct smp_cmd_pairing cp; - - build_pairing_cmd(conn, &cp, NULL, authreq); - smp->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&smp->preq[1], &cp, sizeof(cp)); - - smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); - } else { - struct smp_cmd_security_req cp; - cp.auth_req = authreq; - smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp); - } - -done: - hcon->pending_sec_level = sec_level; - - return 0; -} - -static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_cmd_encrypt_info *rp = (void *) skb->data; - struct smp_chan *smp = conn->smp_chan; - - skb_pull(skb, sizeof(*rp)); - - memcpy(smp->tk, rp->ltk, sizeof(smp->tk)); - - return 0; -} - -static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_cmd_master_ident *rp = (void *) skb->data; - struct smp_chan *smp = conn->smp_chan; - struct hci_dev *hdev = conn->hcon->hdev; - struct hci_conn *hcon = conn->hcon; - u8 authenticated; - - skb_pull(skb, sizeof(*rp)); - - hci_dev_lock(hdev); - authenticated = (conn->hcon->sec_level == BT_SECURITY_HIGH); - hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type, - HCI_SMP_LTK, 1, authenticated, smp->tk, smp->enc_key_size, - rp->ediv, rp->rand); - smp_distribute_keys(conn, 1); - hci_dev_unlock(hdev); - - return 0; -} - -int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) -{ - __u8 code = skb->data[0]; - __u8 reason; - int err = 0; - - if (!lmp_host_le_capable(conn->hcon->hdev)) { - err = -ENOTSUPP; - reason = SMP_PAIRING_NOTSUPP; - goto done; - } - - skb_pull(skb, sizeof(code)); - - switch (code) { - case SMP_CMD_PAIRING_REQ: - reason = smp_cmd_pairing_req(conn, skb); - break; - - case SMP_CMD_PAIRING_FAIL: - smp_failure(conn, skb->data[0], 0); - reason = 0; - err = -EPERM; - break; - - case SMP_CMD_PAIRING_RSP: - reason = smp_cmd_pairing_rsp(conn, skb); - break; - - case SMP_CMD_SECURITY_REQ: - reason = smp_cmd_security_req(conn, skb); - break; - - case SMP_CMD_PAIRING_CONFIRM: - reason = smp_cmd_pairing_confirm(conn, skb); - break; - - case SMP_CMD_PAIRING_RANDOM: - reason = smp_cmd_pairing_random(conn, skb); - break; - - case SMP_CMD_ENCRYPT_INFO: - reason = smp_cmd_encrypt_info(conn, skb); - break; - - case SMP_CMD_MASTER_IDENT: - reason = smp_cmd_master_ident(conn, skb); - break; - - case SMP_CMD_IDENT_INFO: - case SMP_CMD_IDENT_ADDR_INFO: - case SMP_CMD_SIGN_INFO: - /* Just ignored */ - reason = 0; - break; - - default: - BT_DBG("Unknown command code 0x%2.2x", code); - - reason = SMP_CMD_NOTSUPP; - err = -EOPNOTSUPP; - goto done; - } - -done: - if (reason) - smp_failure(conn, reason, 1); - - kfree_skb(skb); - return err; -} - -int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) -{ - struct smp_cmd_pairing *req, *rsp; - struct smp_chan *smp = conn->smp_chan; - __u8 *keydist; - - BT_DBG("conn %p force %d", conn, force); - - if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) - return 0; - - rsp = (void *) &smp->prsp[1]; - - /* The responder sends its keys first */ - if (!force && conn->hcon->out && (rsp->resp_key_dist & 0x07)) - return 0; - - req = (void *) &smp->preq[1]; - - if (conn->hcon->out) { - keydist = &rsp->init_key_dist; - *keydist &= req->init_key_dist; - } else { - keydist = &rsp->resp_key_dist; - *keydist &= req->resp_key_dist; - } - - - BT_DBG("keydist 0x%x", *keydist); - - if (*keydist & SMP_DIST_ENC_KEY) { - struct smp_cmd_encrypt_info enc; - struct smp_cmd_master_ident ident; - struct hci_conn *hcon = conn->hcon; - u8 authenticated; - __le16 ediv; - - get_random_bytes(enc.ltk, sizeof(enc.ltk)); - get_random_bytes(&ediv, sizeof(ediv)); - get_random_bytes(ident.rand, sizeof(ident.rand)); - - smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc); - - authenticated = hcon->sec_level == BT_SECURITY_HIGH; - hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type, - HCI_SMP_LTK_SLAVE, 1, authenticated, - enc.ltk, smp->enc_key_size, ediv, ident.rand); - - ident.ediv = cpu_to_le16(ediv); - - smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident); - - *keydist &= ~SMP_DIST_ENC_KEY; - } - - if (*keydist & SMP_DIST_ID_KEY) { - struct smp_cmd_ident_addr_info addrinfo; - struct smp_cmd_ident_info idinfo; - - /* Send a dummy key */ - get_random_bytes(idinfo.irk, sizeof(idinfo.irk)); - - smp_send_cmd(conn, SMP_CMD_IDENT_INFO, sizeof(idinfo), &idinfo); - - /* Just public address */ - memset(&addrinfo, 0, sizeof(addrinfo)); - bacpy(&addrinfo.bdaddr, conn->src); - - smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo), - &addrinfo); - - *keydist &= ~SMP_DIST_ID_KEY; - } - - if (*keydist & SMP_DIST_SIGN) { - struct smp_cmd_sign_info sign; - - /* Send a dummy key */ - get_random_bytes(sign.csrk, sizeof(sign.csrk)); - - smp_send_cmd(conn, SMP_CMD_SIGN_INFO, sizeof(sign), &sign); - - *keydist &= ~SMP_DIST_SIGN; - } - - if (conn->hcon->out || force) { - clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags); - cancel_delayed_work_sync(&conn->security_timer); - smp_chan_destroy(conn); - } - - return 0; -} diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index dac6a21..feb77ea 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -38,17 +38,16 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) } #endif - BR_INPUT_SKB_CB(skb)->brdev = dev; - - skb_reset_mac_header(skb); - skb_pull(skb, ETH_HLEN); - u64_stats_update_begin(&brstats->syncp); brstats->tx_packets++; - /* Exclude ETH_HLEN from byte stats for consistency with Rx chain */ brstats->tx_bytes += skb->len; u64_stats_update_end(&brstats->syncp); + BR_INPUT_SKB_CB(skb)->brdev = dev; + + skb_reset_mac_header(skb); + skb_pull(skb, ETH_HLEN); + rcu_read_lock(); if (is_broadcast_ether_addr(dest)) br_flood_deliver(br, skb); @@ -302,7 +301,7 @@ static const struct net_device_ops br_netdev_ops = { .ndo_start_xmit = br_dev_xmit, .ndo_get_stats64 = br_get_stats64, .ndo_set_mac_address = br_set_mac_address, - .ndo_set_multicast_list = br_dev_set_multicast_list, + .ndo_set_rx_mode = br_dev_set_multicast_list, .ndo_change_mtu = br_change_mtu, .ndo_do_ioctl = br_dev_ioctl, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -359,6 +358,8 @@ void br_dev_setup(struct net_device *dev) memcpy(br->group_addr, br_group_address, ETH_ALEN); br->stp_enabled = BR_NO_STP; + br->group_fwd_mask = BR_GROUPFWD_DEFAULT; + br->designated_root = br->bridge_id; br->bridge_max_age = br->max_age = 20 * HZ; br->bridge_hello_time = br->hello_time = 2 * HZ; diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index e0dfbc1..c8e7861 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -21,7 +21,7 @@ #include <linux/jhash.h> #include <linux/random.h> #include <linux/slab.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/unaligned.h> #include "br_private.h" @@ -558,19 +558,28 @@ skip: /* Create new static fdb entry */ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, - __u16 state) + __u16 state, __u16 flags) { struct net_bridge *br = source->br; struct hlist_head *head = &br->hash[br_mac_hash(addr)]; struct net_bridge_fdb_entry *fdb; fdb = fdb_find(head, addr); - if (fdb) - return -EEXIST; + if (fdb == NULL) { + if (!(flags & NLM_F_CREATE)) + return -ENOENT; - fdb = fdb_create(head, source, addr); - if (!fdb) - return -ENOMEM; + fdb = fdb_create(head, source, addr); + if (!fdb) + return -ENOMEM; + } else { + if (flags & NLM_F_EXCL) + return -EEXIST; + + if (flags & NLM_F_REPLACE) + fdb->updated = fdb->used = jiffies; + fdb->is_local = fdb->is_static = 0; + } if (state & NUD_PERMANENT) fdb->is_local = fdb->is_static = 1; @@ -626,7 +635,7 @@ int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) } spin_lock_bh(&p->br->hash_lock); - err = fdb_add_entry(p, addr, ndm->ndm_state); + err = fdb_add_entry(p, addr, ndm->ndm_state, nlh->nlmsg_flags); spin_unlock_bh(&p->br->hash_lock); return err; diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index ee64287..e221f88 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -47,6 +47,7 @@ int br_dev_queue_push_xmit(struct sk_buff *skb) kfree_skb(skb); } else { skb_push(skb, ETH_HLEN); + br_drop_fake_rtable(skb); dev_queue_xmit(skb); } diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index eae6a4e..56693c3 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/netdevice.h> +#include <linux/etherdevice.h> #include <linux/netpoll.h> #include <linux/ethtool.h> #include <linux/if_arp.h> @@ -33,20 +34,18 @@ */ static int port_cost(struct net_device *dev) { - if (dev->ethtool_ops && dev->ethtool_ops->get_settings) { - struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET, }; - - if (!dev_ethtool_get_settings(dev, &ecmd)) { - switch (ethtool_cmd_speed(&ecmd)) { - case SPEED_10000: - return 2; - case SPEED_1000: - return 4; - case SPEED_100: - return 19; - case SPEED_10: - return 100; - } + struct ethtool_cmd ecmd; + + if (!__ethtool_get_settings(dev, &ecmd)) { + switch (ethtool_cmd_speed(&ecmd)) { + case SPEED_10000: + return 2; + case SPEED_1000: + return 4; + case SPEED_100: + return 19; + case SPEED_10: + return 100; } } @@ -171,6 +170,8 @@ void br_dev_delete(struct net_device *dev, struct list_head *head) del_nbp(p); } + br_fdb_delete_by_port(br, NULL, 1); + del_timer_sync(&br->gc_timer); br_sysfs_delbr(br->dev); @@ -326,7 +327,8 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) /* Don't allow bridging non-ethernet like devices */ if ((dev->flags & IFF_LOOPBACK) || - dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN) + dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN || + !is_valid_ether_addr(dev->dev_addr)) return -EINVAL; /* No bridging of bridges */ @@ -354,10 +356,6 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) err = kobject_init_and_add(&p->kobj, &brport_ktype, &(dev->dev.kobj), SYSFS_BRIDGE_PORT_ATTR); if (err) - goto err0; - - err = br_fdb_insert(br, p, dev->dev_addr); - if (err) goto err1; err = br_sysfs_addif(p); @@ -394,10 +392,13 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) br_ifinfo_notify(RTM_NEWLINK, p); if (changed_addr) - call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); + call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); dev_set_mtu(br->dev, br_min_mtu(br)); + if (br_fdb_insert(br, p, dev->dev_addr)) + netdev_err(dev, "failed insert local address bridge forwarding table\n"); + kobject_uevent(&p->kobj, KOBJ_ADD); return 0; @@ -407,11 +408,9 @@ err4: err3: sysfs_remove_link(br->ifobj, p->dev->name); err2: - br_fdb_delete_by_port(br, p, 1); -err1: kobject_put(&p->kobj); p = NULL; /* kobject_put frees */ -err0: +err1: dev_set_promiscuity(dev, -1); put_back: dev_put(dev); @@ -423,6 +422,7 @@ put_back: int br_del_if(struct net_bridge *br, struct net_device *dev) { struct net_bridge_port *p; + bool changed_addr; p = br_port_get_rtnl(dev); if (!p || p->br != br) @@ -431,9 +431,12 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) del_nbp(p); spin_lock_bh(&br->lock); - br_stp_recalculate_bridge_id(br); + changed_addr = br_stp_recalculate_bridge_id(br); spin_unlock_bh(&br->lock); + if (changed_addr) + call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); + netdev_update_features(br->dev); return 0; diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index f06ee39..5a31731 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -16,6 +16,7 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/netfilter_bridge.h> +#include <linux/export.h> #include "br_private.h" /* Bridge group multicast address 802.1d (pg 51). */ @@ -162,14 +163,37 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb) p = br_port_get_rcu(skb->dev); if (unlikely(is_link_local(dest))) { - /* Pause frames shouldn't be passed up by driver anyway */ - if (skb->protocol == htons(ETH_P_PAUSE)) + /* + * See IEEE 802.1D Table 7-10 Reserved addresses + * + * Assignment Value + * Bridge Group Address 01-80-C2-00-00-00 + * (MAC Control) 802.3 01-80-C2-00-00-01 + * (Link Aggregation) 802.3 01-80-C2-00-00-02 + * 802.1X PAE address 01-80-C2-00-00-03 + * + * 802.1AB LLDP 01-80-C2-00-00-0E + * + * Others reserved for future standardization + */ + switch (dest[5]) { + case 0x00: /* Bridge Group Address */ + /* If STP is turned off, + then must forward to keep loop detection */ + if (p->br->stp_enabled == BR_NO_STP) + goto forward; + break; + + case 0x01: /* IEEE MAC (Pause) */ goto drop; - /* If STP is turned off, then forward */ - if (p->br->stp_enabled == BR_NO_STP && dest[5] == 0) - goto forward; + default: + /* Allow selective forwarding for most other protocols */ + if (p->br->group_fwd_mask & (1u << dest[5])) + goto forward; + } + /* Deliver packet to local host only */ if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, NULL, br_handle_local_finish)) { return RX_HANDLER_CONSUMED; /* consumed by filter */ diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c index 7222fe1..ea0e15c 100644 --- a/net/bridge/br_ioctl.c +++ b/net/bridge/br_ioctl.c @@ -246,9 +246,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) if (!capable(CAP_NET_ADMIN)) return -EPERM; - spin_lock_bh(&br->lock); br_stp_set_bridge_priority(br, args[1]); - spin_unlock_bh(&br->lock); return 0; case BRCTL_SET_PORT_PRIORITY: diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index c637a66..5f21e53 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -36,6 +36,9 @@ #define mlock_dereference(X, br) \ rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock)) +static void br_multicast_add_router(struct net_bridge *br, + struct net_bridge_port *port); + #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) static inline int ipv6_is_transient_multicast(const struct in6_addr *addr) { @@ -842,6 +845,8 @@ void br_multicast_enable_port(struct net_bridge_port *port) goto out; __br_multicast_enable_port(port); + if (port->multicast_router == 2 && hlist_unhashed(&port->rlist)) + br_multicast_add_router(br, port); out: spin_unlock(&br->multicast_lock); @@ -972,7 +977,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br, } err = br_ip6_multicast_add_group(br, port, &grec->grec_mca); - if (!err) + if (err) break; } @@ -991,6 +996,9 @@ static void br_multicast_add_router(struct net_bridge *br, struct net_bridge_port *p; struct hlist_node *n, *slot = NULL; + if (!hlist_unhashed(&port->rlist)) + return; + hlist_for_each_entry(p, n, &br->router_list, rlist) { if ((unsigned long) port >= (unsigned long) p) break; @@ -1018,12 +1026,8 @@ static void br_multicast_mark_router(struct net_bridge *br, if (port->multicast_router != 1) return; - if (!hlist_unhashed(&port->rlist)) - goto timer; - br_multicast_add_router(br, port); -timer: mod_timer(&port->multicast_router_timer, now + br->multicast_querier_interval); } @@ -1138,6 +1142,12 @@ static int br_ip6_multicast_query(struct net_bridge *br, br_multicast_query_received(br, port, !ipv6_addr_any(&ip6h->saddr)); + /* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */ + if (!(ipv6_addr_type(&ip6h->saddr) & IPV6_ADDR_LINKLOCAL)) { + err = -EINVAL; + goto out; + } + if (skb->len == sizeof(*mld)) { if (!pskb_may_pull(skb, sizeof(*mld))) { err = -EINVAL; @@ -1426,6 +1436,8 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, __skb_pull(skb2, offset); skb_reset_transport_header(skb2); + skb_postpull_rcsum(skb2, skb_network_header(skb2), + skb_network_header_len(skb2)); icmp6_type = icmp6_hdr(skb2)->icmp6_type; @@ -1694,7 +1706,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val) int err = 0; struct net_bridge_mdb_htable *mdb; - spin_lock(&br->multicast_lock); + spin_lock_bh(&br->multicast_lock); if (br->multicast_disabled == !val) goto unlock; @@ -1730,7 +1742,7 @@ rollback: } unlock: - spin_unlock(&br->multicast_lock); + spin_unlock_bh(&br->multicast_lock); return err; } @@ -1741,7 +1753,7 @@ int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val) u32 old; struct net_bridge_mdb_htable *mdb; - spin_lock(&br->multicast_lock); + spin_lock_bh(&br->multicast_lock); if (!netif_running(br->dev)) goto unlock; @@ -1773,7 +1785,7 @@ rollback: } unlock: - spin_unlock(&br->multicast_lock); + spin_unlock_bh(&br->multicast_lock); return err; } diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index c3d0729..6cdd3af 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -109,11 +109,23 @@ static u32 *fake_cow_metrics(struct dst_entry *dst, unsigned long old) return NULL; } +static struct neighbour *fake_neigh_lookup(const struct dst_entry *dst, const void *daddr) +{ + return NULL; +} + +static unsigned int fake_mtu(const struct dst_entry *dst) +{ + return dst->dev->mtu; +} + static struct dst_ops fake_dst_ops = { .family = AF_INET, .protocol = cpu_to_be16(ETH_P_IP), .update_pmtu = fake_update_pmtu, .cow_metrics = fake_cow_metrics, + .neigh_lookup = fake_neigh_lookup, + .mtu = fake_mtu, }; /* @@ -135,7 +147,7 @@ void br_netfilter_rtable_init(struct net_bridge *br) rt->dst.dev = br->dev; rt->dst.path = &rt->dst; dst_init_metrics(&rt->dst, br_dst_default_metrics, true); - rt->dst.flags = DST_NOXFRM; + rt->dst.flags = DST_NOXFRM | DST_NOPEER | DST_FAKE_RTABLE; rt->dst.ops = &fake_dst_ops; } @@ -354,18 +366,18 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) goto free_skb; dst = skb_dst(skb); neigh = dst_get_neighbour(dst); - if (dst->hh) { - neigh_hh_bridge(dst->hh, skb); + if (neigh->hh.hh_len) { + neigh_hh_bridge(&neigh->hh, skb); skb->dev = nf_bridge->physindev; return br_handle_frame_finish(skb); - } else if (neigh) { + } else { /* the neighbour function below overwrites the complete * MAC header, so we save the Ethernet source address and * protocol number. */ skb_copy_from_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN), skb->nf_bridge->data, ETH_HLEN-ETH_ALEN); /* tell br_dev_xmit to continue with forwarding */ nf_bridge->mask |= BRNF_BRIDGED_DNAT; - return neigh->output(skb); + return neigh->output(neigh, skb); } free_skb: kfree_skb(skb); @@ -678,11 +690,7 @@ static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff *skb, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - struct rtable *rt = skb_rtable(skb); - - if (rt && rt == bridge_parent_rtable(in)) - skb_dst_drop(skb); - + br_drop_fake_rtable(skb); return NF_ACCEPT; } @@ -814,12 +822,15 @@ static int br_nf_dev_queue_xmit(struct sk_buff *skb) !skb_is_gso(skb)) { if (br_parse_ip_options(skb)) /* Drop invalid packet */ - return NF_DROP; + goto drop; ret = ip_fragment(skb, br_dev_queue_push_xmit); } else ret = br_dev_queue_push_xmit(skb); return ret; + drop: + kfree_skb(skb); + return 0; } #else static int br_nf_dev_queue_xmit(struct sk_buff *skb) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index d372df2..99a48a3 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -18,6 +18,7 @@ #include <net/sock.h> #include "br_private.h" +#include "br_private_stp.h" static inline size_t br_nlmsg_size(void) { @@ -188,6 +189,13 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) p->state = new_state; br_log_state(p); + + spin_lock_bh(&p->br->lock); + br_port_state_selection(p->br); + spin_unlock_bh(&p->br->lock); + + br_ifinfo_notify(RTM_NEWLINK, p); + return 0; } @@ -203,11 +211,26 @@ static int br_validate(struct nlattr *tb[], struct nlattr *data[]) return 0; } +static int br_dev_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct net_bridge *br = netdev_priv(dev); + + if (tb[IFLA_ADDRESS]) { + spin_lock_bh(&br->lock); + br_stp_change_bridge_id(br, nla_data(tb[IFLA_ADDRESS])); + spin_unlock_bh(&br->lock); + } + + return register_netdevice(dev); +} + struct rtnl_link_ops br_link_ops __read_mostly = { .kind = "bridge", .priv_size = sizeof(struct net_bridge), .setup = br_dev_setup, .validate = br_validate, + .newlink = br_dev_newlink, .dellink = br_dev_delete, }; diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c index 6545ee9..a76b621 100644 --- a/net/bridge/br_notify.c +++ b/net/bridge/br_notify.c @@ -34,6 +34,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v struct net_device *dev = ptr; struct net_bridge_port *p; struct net_bridge *br; + bool changed_addr; int err; /* register of bridge completed, add sysfs entries */ @@ -57,8 +58,12 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v case NETDEV_CHANGEADDR: spin_lock_bh(&br->lock); br_fdb_changeaddr(p, dev->dev_addr); - br_stp_recalculate_bridge_id(br); + changed_addr = br_stp_recalculate_bridge_id(br); spin_unlock_bh(&br->lock); + + if (changed_addr) + call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); + break; case NETDEV_CHANGE: diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index fe1e299..b9bba8f 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -29,6 +29,11 @@ #define BR_VERSION "2.3" +/* Control of forwarding link local multicast */ +#define BR_GROUPFWD_DEFAULT 0 +/* Don't allow forwarding control protocols like STP and LLDP */ +#define BR_GROUPFWD_RESTRICTED 0x4007u + /* Path to usermode spanning tree program */ #define BR_STP_PROG "/sbin/bridge-stp" @@ -189,6 +194,8 @@ struct net_bridge unsigned long flags; #define BR_SET_MAC_ADDR 0x00000001 + u16 group_fwd_mask; + /* STP */ bridge_id designated_root; bridge_id bridge_id; @@ -492,7 +499,6 @@ extern struct net_bridge_port *br_get_port(struct net_bridge *br, extern void br_init_port(struct net_bridge_port *p); extern void br_become_designated_port(struct net_bridge_port *p); -extern void __br_set_forward_delay(struct net_bridge *br, unsigned long t); extern int br_set_forward_delay(struct net_bridge *br, unsigned long x); extern int br_set_hello_time(struct net_bridge *br, unsigned long x); extern int br_set_max_age(struct net_bridge *br, unsigned long x); diff --git a/net/bridge/br_private_stp.h b/net/bridge/br_private_stp.h index 642ef47..05ed9bc 100644 --- a/net/bridge/br_private_stp.h +++ b/net/bridge/br_private_stp.h @@ -56,7 +56,8 @@ extern void br_become_root_bridge(struct net_bridge *br); extern void br_config_bpdu_generation(struct net_bridge *); extern void br_configuration_update(struct net_bridge *); extern void br_port_state_selection(struct net_bridge *); -extern void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu); +extern void br_received_config_bpdu(struct net_bridge_port *p, + const struct br_config_bpdu *bpdu); extern void br_received_tcn_bpdu(struct net_bridge_port *p); extern void br_transmit_config(struct net_bridge_port *p); extern void br_transmit_tcn(struct net_bridge *br); diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 81fb35a..8ac946f 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -109,7 +109,6 @@ static void br_root_selection(struct net_bridge *br) list_for_each_entry(p, &br->port_list, list) { if (br_should_become_root_port(p, root_port)) root_port = p->port_no; - } br->root_port = root_port; @@ -145,7 +144,6 @@ void br_transmit_config(struct net_bridge_port *p) struct br_config_bpdu bpdu; struct net_bridge *br; - if (timer_pending(&p->hold_timer)) { p->config_pending = 1; return; @@ -181,8 +179,8 @@ void br_transmit_config(struct net_bridge_port *p) } /* called under bridge lock */ -static inline void br_record_config_information(struct net_bridge_port *p, - const struct br_config_bpdu *bpdu) +static void br_record_config_information(struct net_bridge_port *p, + const struct br_config_bpdu *bpdu) { p->designated_root = bpdu->root; p->designated_cost = bpdu->root_path_cost; @@ -195,7 +193,7 @@ static inline void br_record_config_information(struct net_bridge_port *p, } /* called under bridge lock */ -static inline void br_record_config_timeout_values(struct net_bridge *br, +static void br_record_config_timeout_values(struct net_bridge *br, const struct br_config_bpdu *bpdu) { br->max_age = bpdu->max_age; @@ -254,7 +252,8 @@ static void br_designated_port_selection(struct net_bridge *br) } /* called under bridge lock */ -static int br_supersedes_port_info(struct net_bridge_port *p, struct br_config_bpdu *bpdu) +static int br_supersedes_port_info(const struct net_bridge_port *p, + const struct br_config_bpdu *bpdu) { int t; @@ -285,7 +284,7 @@ static int br_supersedes_port_info(struct net_bridge_port *p, struct br_config_b } /* called under bridge lock */ -static inline void br_topology_change_acknowledged(struct net_bridge *br) +static void br_topology_change_acknowledged(struct net_bridge *br) { br->topology_change_detected = 0; del_timer(&br->tcn_timer); @@ -327,7 +326,7 @@ void br_config_bpdu_generation(struct net_bridge *br) } /* called under bridge lock */ -static inline void br_reply(struct net_bridge_port *p) +static void br_reply(struct net_bridge_port *p) { br_transmit_config(p); } @@ -363,6 +362,8 @@ static void br_make_blocking(struct net_bridge_port *p) p->state = BR_STATE_BLOCKING; br_log_state(p); + br_ifinfo_notify(RTM_NEWLINK, p); + del_timer(&p->forward_delay_timer); } } @@ -379,15 +380,14 @@ static void br_make_forwarding(struct net_bridge_port *p) p->state = BR_STATE_FORWARDING; br_topology_change_detection(br); del_timer(&p->forward_delay_timer); - } - else if (br->stp_enabled == BR_KERNEL_STP) + } else if (br->stp_enabled == BR_KERNEL_STP) p->state = BR_STATE_LISTENING; else p->state = BR_STATE_LEARNING; br_multicast_enable_port(p); - 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); @@ -399,25 +399,24 @@ void br_port_state_selection(struct net_bridge *br) struct net_bridge_port *p; unsigned int liveports = 0; - /* Don't change port states if userspace is handling STP */ - if (br->stp_enabled == BR_USER_STP) - return; - list_for_each_entry(p, &br->port_list, list) { if (p->state == BR_STATE_DISABLED) continue; - if (p->port_no == br->root_port) { - p->config_pending = 0; - p->topology_change_ack = 0; - br_make_forwarding(p); - } else if (br_is_designated_port(p)) { - del_timer(&p->message_age_timer); - br_make_forwarding(p); - } else { - p->config_pending = 0; - p->topology_change_ack = 0; - br_make_blocking(p); + /* Don't change port states if userspace is handling STP */ + if (br->stp_enabled != BR_USER_STP) { + if (p->port_no == br->root_port) { + p->config_pending = 0; + p->topology_change_ack = 0; + br_make_forwarding(p); + } else if (br_is_designated_port(p)) { + del_timer(&p->message_age_timer); + br_make_forwarding(p); + } else { + p->config_pending = 0; + p->topology_change_ack = 0; + br_make_blocking(p); + } } if (p->state == BR_STATE_FORWARDING) @@ -431,14 +430,15 @@ void br_port_state_selection(struct net_bridge *br) } /* called under bridge lock */ -static inline void br_topology_change_acknowledge(struct net_bridge_port *p) +static void br_topology_change_acknowledge(struct net_bridge_port *p) { p->topology_change_ack = 1; br_transmit_config(p); } /* called under bridge lock */ -void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) +void br_received_config_bpdu(struct net_bridge_port *p, + const struct br_config_bpdu *bpdu) { struct net_bridge *br; int was_root; @@ -517,27 +517,18 @@ int br_set_max_age(struct net_bridge *br, unsigned long val) } -void __br_set_forward_delay(struct net_bridge *br, unsigned long t) -{ - br->bridge_forward_delay = t; - if (br_is_root_bridge(br)) - br->forward_delay = br->bridge_forward_delay; -} - int br_set_forward_delay(struct net_bridge *br, unsigned long val) { unsigned long t = clock_t_to_jiffies(val); - int err = -ERANGE; - spin_lock_bh(&br->lock); if (br->stp_enabled != BR_NO_STP && (t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY)) - goto unlock; - - __br_set_forward_delay(br, t); - err = 0; + return -ERANGE; -unlock: + spin_lock_bh(&br->lock); + br->bridge_forward_delay = t; + if (br_is_root_bridge(br)) + br->forward_delay = br->bridge_forward_delay; spin_unlock_bh(&br->lock); - return err; + return 0; } diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index f26516a..718cbe8 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -212,10 +212,19 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb, bpdu.hello_time = br_get_ticks(buf+28); bpdu.forward_delay = br_get_ticks(buf+30); - br_received_config_bpdu(p, &bpdu); - } + if (bpdu.message_age > bpdu.max_age) { + if (net_ratelimit()) + br_notice(p->br, + "port %u config from %pM" + " (message_age %ul > max_age %ul)\n", + p->port_no, + eth_hdr(skb)->h_source, + bpdu.message_age, bpdu.max_age); + goto out; + } - else if (buf[0] == BPDU_TYPE_TCN) { + br_received_config_bpdu(p, &bpdu); + } else if (buf[0] == BPDU_TYPE_TCN) { br_received_tcn_bpdu(p); } out: diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index d07e521..0f7dc60 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -12,6 +12,7 @@ */ #include <linux/kernel.h> +#include <linux/kmod.h> #include <linux/etherdevice.h> #include <linux/rtnetlink.h> @@ -88,6 +89,7 @@ void br_stp_enable_port(struct net_bridge_port *p) br_init_port(p); br_port_state_selection(p->br); br_log_state(p); + br_ifinfo_notify(RTM_NEWLINK, p); } /* called under bridge lock */ @@ -104,6 +106,8 @@ void br_stp_disable_port(struct net_bridge_port *p) p->topology_change_ack = 0; p->config_pending = 0; + br_ifinfo_notify(RTM_NEWLINK, p); + del_timer(&p->message_age_timer); del_timer(&p->forward_delay_timer); del_timer(&p->hold_timer); @@ -126,14 +130,6 @@ static void br_stp_start(struct net_bridge *br) char *envp[] = { NULL }; r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC); - - spin_lock_bh(&br->lock); - - if (br->bridge_forward_delay < BR_MIN_FORWARD_DELAY) - __br_set_forward_delay(br, BR_MIN_FORWARD_DELAY); - else if (br->bridge_forward_delay < BR_MAX_FORWARD_DELAY) - __br_set_forward_delay(br, BR_MAX_FORWARD_DELAY); - if (r == 0) { br->stp_enabled = BR_USER_STP; br_debug(br, "userspace STP started\n"); @@ -142,10 +138,10 @@ static void br_stp_start(struct net_bridge *br) br_debug(br, "using kernel STP\n"); /* To start timers on any ports left in blocking */ + spin_lock_bh(&br->lock); br_port_state_selection(br); + spin_unlock_bh(&br->lock); } - - spin_unlock_bh(&br->lock); } static void br_stp_stop(struct net_bridge *br) @@ -239,12 +235,13 @@ bool br_stp_recalculate_bridge_id(struct net_bridge *br) return true; } -/* called under bridge lock */ +/* Acquires and releases bridge lock */ void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio) { struct net_bridge_port *p; int wasroot; + spin_lock_bh(&br->lock); wasroot = br_is_root_bridge(br); list_for_each_entry(p, &br->port_list, list) { @@ -262,6 +259,7 @@ void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio) br_port_state_selection(br); if (br_is_root_bridge(br) && !wasroot) br_become_root_bridge(br); + spin_unlock_bh(&br->lock); } /* called under bridge lock */ diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c index 0abc6b1..c83ee79 100644 --- a/net/bridge/br_stp_timer.c +++ b/net/bridge/br_stp_timer.c @@ -97,6 +97,7 @@ static void br_forward_delay_timer_expired(unsigned long arg) netif_carrier_on(br->dev); } br_log_state(p); + br_ifinfo_notify(RTM_NEWLINK, p); spin_unlock(&br->lock); } diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index 68b893e..c236c0e 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -149,6 +149,39 @@ static ssize_t store_stp_state(struct device *d, static DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state, store_stp_state); +static ssize_t show_group_fwd_mask(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct net_bridge *br = to_bridge(d); + return sprintf(buf, "%#x\n", br->group_fwd_mask); +} + + +static ssize_t store_group_fwd_mask(struct device *d, + struct device_attribute *attr, const char *buf, + size_t len) +{ + struct net_bridge *br = to_bridge(d); + char *endp; + unsigned long val; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + val = simple_strtoul(buf, &endp, 0); + if (endp == buf) + return -EINVAL; + + if (val & BR_GROUPFWD_RESTRICTED) + return -EINVAL; + + br->group_fwd_mask = val; + + return len; +} +static DEVICE_ATTR(group_fwd_mask, S_IRUGO | S_IWUSR, show_group_fwd_mask, + store_group_fwd_mask); + static ssize_t show_priority(struct device *d, struct device_attribute *attr, char *buf) { @@ -652,6 +685,7 @@ static struct attribute *bridge_attrs[] = { &dev_attr_max_age.attr, &dev_attr_ageing_time.attr, &dev_attr_stp_state.attr, + &dev_attr_group_fwd_mask.attr, &dev_attr_priority.attr, &dev_attr_bridge_id.attr, &dev_attr_root_id.attr, diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index ba6f73e..a9aff9c 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -4,7 +4,7 @@ menuconfig BRIDGE_NF_EBTABLES tristate "Ethernet Bridge tables (ebtables) support" - depends on BRIDGE && BRIDGE_NETFILTER + depends on BRIDGE && NETFILTER select NETFILTER_XTABLES help ebtables is a general, extensible frame/packet identification diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 26377e9..5449294 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -102,16 +102,15 @@ static struct sk_buff *ulog_alloc_skb(unsigned int size) unsigned int n; n = max(size, nlbufsiz); - skb = alloc_skb(n, GFP_ATOMIC); + skb = alloc_skb(n, GFP_ATOMIC | __GFP_NOWARN); if (!skb) { - pr_debug("cannot alloc whole buffer of size %ub!\n", n); if (n > size) { /* try to allocate only as much as we need for * current packet */ skb = alloc_skb(size, GFP_ATOMIC); if (!skb) - pr_debug("cannot even allocate " - "buffer of size %ub\n", size); + pr_debug("cannot even allocate buffer of size %ub\n", + size); } } @@ -216,7 +215,6 @@ unlock: nlmsg_failure: pr_debug("error during NLMSG_PUT. This should " "not happen, please report to author.\n"); - goto unlock; alloc_failure: goto unlock; } diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c index 1bcaf36..40d8258 100644 --- a/net/bridge/netfilter/ebtable_broute.c +++ b/net/bridge/netfilter/ebtable_broute.c @@ -87,14 +87,14 @@ static int __init ebtable_broute_init(void) if (ret < 0) return ret; /* see br_input.c */ - rcu_assign_pointer(br_should_route_hook, + RCU_INIT_POINTER(br_should_route_hook, (br_should_route_hook_t *)ebt_broute); return 0; } static void __exit ebtable_broute_fini(void) { - rcu_assign_pointer(br_should_route_hook, NULL); + RCU_INIT_POINTER(br_should_route_hook, NULL); synchronize_net(); unregister_pernet_subsys(&broute_net_ops); } diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 2b5ca1a..45f93f8 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1044,10 +1044,9 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl, if (repl->num_counters && copy_to_user(repl->counters, counterstmp, repl->num_counters * sizeof(struct ebt_counter))) { - ret = -EFAULT; + /* Silent error, can't fail, new table is already in place */ + net_warn_ratelimited("ebtables: counters copy to user failed while replacing table\n"); } - else - ret = 0; /* decrease module count and free resources */ EBT_ENTRY_ITERATE(table->entries, table->entries_size, @@ -1198,7 +1197,8 @@ ebt_register_table(struct net *net, const struct ebt_table *input_table) if (table->check && table->check(newinfo, table->valid_hooks)) { BUGPRINT("The table doesn't like its own initial data, lol\n"); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto free_chainstack; } table->private = newinfo; diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index 804e50f..4e9115d 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -11,12 +11,12 @@ #define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ -#include <linux/version.h> #include <linux/kernel.h> #include <linux/if_arp.h> #include <linux/net.h> #include <linux/netdevice.h> #include <linux/mutex.h> +#include <linux/module.h> #include <net/netns/generic.h> #include <net/net_namespace.h> #include <net/pkt_sched.h> @@ -91,10 +91,14 @@ static struct caif_device_entry *caif_device_alloc(struct net_device *dev) caifdevs = caif_device_list(dev_net(dev)); - caifd = kzalloc(sizeof(*caifd), GFP_ATOMIC); + caifd = kzalloc(sizeof(*caifd), GFP_KERNEL); if (!caifd) return NULL; caifd->pcpu_refcnt = alloc_percpu(int); + if (!caifd->pcpu_refcnt) { + kfree(caifd); + return NULL; + } caifd->netdev = dev; dev_hold(dev); return caifd; diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index 53a8e37..7e4b4b4 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -317,11 +317,9 @@ static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock, int copylen; ret = -EOPNOTSUPP; - if (m->msg_flags&MSG_OOB) + if (flags & MSG_OOB) goto read_error; - m->msg_namelen = 0; - skb = skb_recv_datagram(sk, flags, 0 , &ret); if (!skb) goto read_error; @@ -368,6 +366,10 @@ static long caif_stream_data_wait(struct sock *sk, long timeo) release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); + + if (sock_flag(sk, SOCK_DEAD)) + break; + clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); } @@ -395,8 +397,6 @@ static int caif_stream_recvmsg(struct kiocb *iocb, struct socket *sock, if (flags&MSG_OOB) goto out; - msg->msg_namelen = 0; - /* * Lock the socket to prevent queue disordering * while sleeps in memcpy_tomsg @@ -414,6 +414,10 @@ static int caif_stream_recvmsg(struct kiocb *iocb, struct socket *sock, struct sk_buff *skb; lock_sock(sk); + if (sock_flag(sk, SOCK_DEAD)) { + err = -ECONNRESET; + goto unlock; + } skb = skb_dequeue(&sk->sk_receive_queue); caif_check_flow_release(sk); diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c index bca32d7..86ff37c 100644 --- a/net/caif/cfcnfg.c +++ b/net/caif/cfcnfg.c @@ -78,10 +78,8 @@ struct cfcnfg *cfcnfg_create(void) /* Initiate this layer */ this = kzalloc(sizeof(struct cfcnfg), GFP_ATOMIC); - if (!this) { - pr_warn("Out of memory\n"); + if (!this) return NULL; - } this->mux = cfmuxl_create(); if (!this->mux) goto out_of_mem; @@ -108,8 +106,6 @@ struct cfcnfg *cfcnfg_create(void) return this; out_of_mem: - pr_warn("Out of memory\n"); - synchronize_rcu(); kfree(this->mux); @@ -447,10 +443,8 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, "- unknown channel type\n"); goto unlock; } - if (!servicel) { - pr_warn("Out of memory\n"); + if (!servicel) goto unlock; - } layer_set_dn(servicel, cnfg->mux); cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id); layer_set_up(servicel, adapt_layer); @@ -472,7 +466,7 @@ cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type, { struct cflayer *frml; struct cflayer *phy_driver = NULL; - struct cfcnfg_phyinfo *phyinfo; + struct cfcnfg_phyinfo *phyinfo = NULL; int i; u8 phyid; @@ -487,25 +481,25 @@ cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type, goto got_phyid; } pr_warn("Too many CAIF Link Layers (max 6)\n"); - goto out; + goto out_err; got_phyid: phyinfo = kzalloc(sizeof(struct cfcnfg_phyinfo), GFP_ATOMIC); + if (!phyinfo) + goto out_err; switch (phy_type) { case CFPHYTYPE_FRAG: phy_driver = cfserl_create(CFPHYTYPE_FRAG, phyid, stx); - if (!phy_driver) { - pr_warn("Out of memory\n"); - goto out; - } + if (!phy_driver) + goto out_err; break; case CFPHYTYPE_CAIF: phy_driver = NULL; break; default: - goto out; + goto out_err; } phy_layer->id = phyid; phyinfo->pref = pref; @@ -519,11 +513,8 @@ got_phyid: frml = cffrml_create(phyid, fcs); - if (!frml) { - pr_warn("Out of memory\n"); - kfree(phyinfo); - goto out; - } + if (!frml) + goto out_err; phyinfo->frm_layer = frml; layer_set_up(frml, cnfg->mux); @@ -539,7 +530,12 @@ got_phyid: } list_add_rcu(&phyinfo->node, &cnfg->phys); -out: + mutex_unlock(&cnfg->lock); + return; + +out_err: + kfree(phy_driver); + kfree(phyinfo); mutex_unlock(&cnfg->lock); } EXPORT_SYMBOL(cfcnfg_add_phy_layer); diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c index a80d94a..84efbe4 100644 --- a/net/caif/cfctrl.c +++ b/net/caif/cfctrl.c @@ -35,15 +35,12 @@ struct cflayer *cfctrl_create(void) { struct dev_info dev_info; struct cfctrl *this = - kmalloc(sizeof(struct cfctrl), GFP_ATOMIC); - if (!this) { - pr_warn("Out of memory\n"); + kzalloc(sizeof(struct cfctrl), GFP_ATOMIC); + if (!this) return NULL; - } caif_assert(offsetof(struct cfctrl, serv.layer) == 0); memset(&dev_info, 0, sizeof(dev_info)); dev_info.id = 0xff; - memset(this, 0, sizeof(*this)); cfsrvl_init(&this->serv, 0, &dev_info, false); atomic_set(&this->req_seq_no, 1); atomic_set(&this->rsp_seq_no, 1); @@ -180,10 +177,8 @@ void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid) struct cfctrl *cfctrl = container_obj(layer); struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); struct cflayer *dn = cfctrl->serv.layer.dn; - if (!pkt) { - pr_warn("Out of memory\n"); + if (!pkt) return; - } if (!dn) { pr_debug("not able to send enum request\n"); return; @@ -224,10 +219,8 @@ int cfctrl_linkup_request(struct cflayer *layer, } pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); - if (!pkt) { - pr_warn("Out of memory\n"); + if (!pkt) return -ENOMEM; - } cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP); cfpkt_addbdy(pkt, (param->chtype << 4) | param->linktype); cfpkt_addbdy(pkt, (param->priority << 3) | param->phyid); @@ -275,10 +268,8 @@ int cfctrl_linkup_request(struct cflayer *layer, return -EINVAL; } req = kzalloc(sizeof(*req), GFP_KERNEL); - if (!req) { - pr_warn("Out of memory\n"); + if (!req) return -ENOMEM; - } req->client_layer = user_layer; req->cmd = CFCTRL_CMD_LINK_SETUP; req->param = *param; @@ -313,10 +304,8 @@ int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid, struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); struct cflayer *dn = cfctrl->serv.layer.dn; - if (!pkt) { - pr_warn("Out of memory\n"); + if (!pkt) return -ENOMEM; - } if (!dn) { pr_debug("not able to send link-down request\n"); diff --git a/net/caif/cfdbgl.c b/net/caif/cfdbgl.c index 11a2af4..65d6ef3 100644 --- a/net/caif/cfdbgl.c +++ b/net/caif/cfdbgl.c @@ -19,13 +19,10 @@ static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt); struct cflayer *cfdbgl_create(u8 channel_id, struct dev_info *dev_info) { - struct cfsrvl *dbg = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC); - if (!dbg) { - pr_warn("Out of memory\n"); + struct cfsrvl *dbg = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC); + if (!dbg) return NULL; - } caif_assert(offsetof(struct cfsrvl, layer) == 0); - memset(dbg, 0, sizeof(struct cfsrvl)); cfsrvl_init(dbg, channel_id, dev_info, false); dbg->layer.receive = cfdbgl_receive; dbg->layer.transmit = cfdbgl_transmit; diff --git a/net/caif/cfdgml.c b/net/caif/cfdgml.c index 0382dec..0f5ff27 100644 --- a/net/caif/cfdgml.c +++ b/net/caif/cfdgml.c @@ -26,13 +26,10 @@ static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt); struct cflayer *cfdgml_create(u8 channel_id, struct dev_info *dev_info) { - struct cfsrvl *dgm = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC); - if (!dgm) { - pr_warn("Out of memory\n"); + struct cfsrvl *dgm = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC); + if (!dgm) return NULL; - } caif_assert(offsetof(struct cfsrvl, layer) == 0); - memset(dgm, 0, sizeof(struct cfsrvl)); cfsrvl_init(dgm, channel_id, dev_info, true); dgm->layer.receive = cfdgml_receive; dgm->layer.transmit = cfdgml_transmit; diff --git a/net/caif/cffrml.c b/net/caif/cffrml.c index 04204b2..d3ca87b 100644 --- a/net/caif/cffrml.c +++ b/net/caif/cffrml.c @@ -34,11 +34,9 @@ static u32 cffrml_rcv_error; static u32 cffrml_rcv_checsum_error; struct cflayer *cffrml_create(u16 phyid, bool use_fcs) { - struct cffrml *this = kmalloc(sizeof(struct cffrml), GFP_ATOMIC); - if (!this) { - pr_warn("Out of memory\n"); + struct cffrml *this = kzalloc(sizeof(struct cffrml), GFP_ATOMIC); + if (!this) return NULL; - } this->pcpu_refcnt = alloc_percpu(int); if (this->pcpu_refcnt == NULL) { kfree(this); @@ -47,7 +45,6 @@ struct cflayer *cffrml_create(u16 phyid, bool use_fcs) caif_assert(offsetof(struct cffrml, layer) == 0); - memset(this, 0, sizeof(struct cflayer)); this->layer.receive = cffrml_receive; this->layer.transmit = cffrml_transmit; this->layer.ctrlcmd = cffrml_ctrlcmd; @@ -139,20 +136,21 @@ static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt) static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt) { - int tmp; u16 chks; u16 len; + __le16 data; + struct cffrml *this = container_obj(layr); if (this->dofcs) { chks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff); - tmp = cpu_to_le16(chks); - cfpkt_add_trail(pkt, &tmp, 2); + data = cpu_to_le16(chks); + cfpkt_add_trail(pkt, &data, 2); } else { cfpkt_pad_trail(pkt, 2); } len = cfpkt_getlen(pkt); - tmp = cpu_to_le16(len); - cfpkt_add_head(pkt, &tmp, 2); + data = cpu_to_le16(len); + cfpkt_add_head(pkt, &data, 2); cfpkt_info(pkt)->hdr_len += 2; if (cfpkt_erroneous(pkt)) { pr_err("Packet is erroneous!\n"); diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c index c23979e..b36f24a 100644 --- a/net/caif/cfmuxl.c +++ b/net/caif/cfmuxl.c @@ -108,7 +108,7 @@ struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid) int idx = phyid % DN_CACHE_SIZE; spin_lock_bh(&muxl->transmit_lock); - rcu_assign_pointer(muxl->dn_cache[idx], NULL); + RCU_INIT_POINTER(muxl->dn_cache[idx], NULL); dn = get_from_id(&muxl->frml_list, phyid); if (dn == NULL) goto out; @@ -164,7 +164,7 @@ struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id) if (up == NULL) goto out; - rcu_assign_pointer(muxl->up_cache[idx], NULL); + RCU_INIT_POINTER(muxl->up_cache[idx], NULL); list_del_rcu(&up->node); out: spin_unlock_bh(&muxl->receive_lock); @@ -261,7 +261,7 @@ static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, idx = layer->id % UP_CACHE_SIZE; spin_lock_bh(&muxl->receive_lock); - rcu_assign_pointer(muxl->up_cache[idx], NULL); + RCU_INIT_POINTER(muxl->up_cache[idx], NULL); list_del_rcu(&layer->node); spin_unlock_bh(&muxl->receive_lock); } diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c index 75d4bfa..df08c47 100644 --- a/net/caif/cfpkt_skbuff.c +++ b/net/caif/cfpkt_skbuff.c @@ -9,6 +9,7 @@ #include <linux/string.h> #include <linux/skbuff.h> #include <linux/hardirq.h> +#include <linux/export.h> #include <net/caif/cfpkt.h> #define PKT_PREFIX 48 diff --git a/net/caif/cfrfml.c b/net/caif/cfrfml.c index 0deabb4..81660f8 100644 --- a/net/caif/cfrfml.c +++ b/net/caif/cfrfml.c @@ -46,13 +46,10 @@ struct cflayer *cfrfml_create(u8 channel_id, struct dev_info *dev_info, int mtu_size) { int tmp; - struct cfrfml *this = - kzalloc(sizeof(struct cfrfml), GFP_ATOMIC); + struct cfrfml *this = kzalloc(sizeof(struct cfrfml), GFP_ATOMIC); - if (!this) { - pr_warn("Out of memory\n"); + if (!this) return NULL; - } cfsrvl_init(&this->serv, channel_id, dev_info, false); this->serv.release = cfrfml_release; diff --git a/net/caif/cfserl.c b/net/caif/cfserl.c index 2715c84..797c8d1 100644 --- a/net/caif/cfserl.c +++ b/net/caif/cfserl.c @@ -33,13 +33,10 @@ static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, struct cflayer *cfserl_create(int type, int instance, bool use_stx) { - struct cfserl *this = kmalloc(sizeof(struct cfserl), GFP_ATOMIC); - if (!this) { - pr_warn("Out of memory\n"); + struct cfserl *this = kzalloc(sizeof(struct cfserl), GFP_ATOMIC); + if (!this) return NULL; - } caif_assert(offsetof(struct cfserl, layer) == 0); - memset(this, 0, sizeof(struct cfserl)); this->layer.receive = cfserl_receive; this->layer.transmit = cfserl_transmit; this->layer.ctrlcmd = cfserl_ctrlcmd; diff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index 535a1e7..b99f5b2 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -108,10 +108,8 @@ static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl) struct caif_payload_info *info; u8 flow_on = SRVL_FLOW_ON; pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE); - if (!pkt) { - pr_warn("Out of memory\n"); + if (!pkt) return -ENOMEM; - } if (cfpkt_add_head(pkt, &flow_on, 1) < 0) { pr_err("Packet is erroneous!\n"); @@ -130,10 +128,8 @@ static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl) struct caif_payload_info *info; u8 flow_off = SRVL_FLOW_OFF; pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE); - if (!pkt) { - pr_warn("Out of memory\n"); + if (!pkt) return -ENOMEM; - } if (cfpkt_add_head(pkt, &flow_off, 1) < 0) { pr_err("Packet is erroneous!\n"); diff --git a/net/caif/cfutill.c b/net/caif/cfutill.c index 98e027d..53e49f3 100644 --- a/net/caif/cfutill.c +++ b/net/caif/cfutill.c @@ -26,13 +26,10 @@ static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt); struct cflayer *cfutill_create(u8 channel_id, struct dev_info *dev_info) { - struct cfsrvl *util = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC); - if (!util) { - pr_warn("Out of memory\n"); + struct cfsrvl *util = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC); + if (!util) return NULL; - } caif_assert(offsetof(struct cfsrvl, layer) == 0); - memset(util, 0, sizeof(struct cfsrvl)); cfsrvl_init(util, channel_id, dev_info, true); util->layer.receive = cfutill_receive; util->layer.transmit = cfutill_transmit; diff --git a/net/caif/cfveil.c b/net/caif/cfveil.c index 3ec83fb..910ab06 100644 --- a/net/caif/cfveil.c +++ b/net/caif/cfveil.c @@ -25,13 +25,10 @@ static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt); struct cflayer *cfvei_create(u8 channel_id, struct dev_info *dev_info) { - struct cfsrvl *vei = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC); - if (!vei) { - pr_warn("Out of memory\n"); + struct cfsrvl *vei = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC); + if (!vei) return NULL; - } caif_assert(offsetof(struct cfsrvl, layer) == 0); - memset(vei, 0, sizeof(struct cfsrvl)); cfsrvl_init(vei, channel_id, dev_info, true); vei->layer.receive = cfvei_receive; vei->layer.transmit = cfvei_transmit; diff --git a/net/caif/cfvidl.c b/net/caif/cfvidl.c index b2f5989..e3f37db 100644 --- a/net/caif/cfvidl.c +++ b/net/caif/cfvidl.c @@ -21,14 +21,11 @@ static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt); struct cflayer *cfvidl_create(u8 channel_id, struct dev_info *dev_info) { - struct cfsrvl *vid = kmalloc(sizeof(struct cfsrvl), GFP_ATOMIC); - if (!vid) { - pr_warn("Out of memory\n"); + struct cfsrvl *vid = kzalloc(sizeof(struct cfsrvl), GFP_ATOMIC); + if (!vid) return NULL; - } caif_assert(offsetof(struct cfsrvl, layer) == 0); - memset(vid, 0, sizeof(struct cfsrvl)); cfsrvl_init(vid, channel_id, dev_info, false); vid->layer.receive = cfvidl_receive; vid->layer.transmit = cfvidl_transmit; diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c index adbb424..b525aec 100644 --- a/net/caif/chnl_net.c +++ b/net/caif/chnl_net.c @@ -7,8 +7,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ -#include <linux/version.h> #include <linux/fs.h> +#include <linux/hardirq.h> #include <linux/init.h> #include <linux/module.h> #include <linux/netdevice.h> @@ -464,7 +464,6 @@ static int ipcaif_newlink(struct net *src_net, struct net_device *dev, ASSERT_RTNL(); caifdev = netdev_priv(dev); caif_netlink_parms(data, &caifdev->conn_req); - dev_net_set(caifdev->netdev, src_net); ret = register_netdevice(dev); if (ret) diff --git a/net/can/Kconfig b/net/can/Kconfig index 89395b2..0320069 100644 --- a/net/can/Kconfig +++ b/net/can/Kconfig @@ -40,5 +40,16 @@ config CAN_BCM CAN messages are used on the bus (e.g. in automotive environments). To use the Broadcast Manager, use AF_CAN with protocol CAN_BCM. +config CAN_GW + tristate "CAN Gateway/Router (with netlink configuration)" + depends on CAN + default N + ---help--- + The CAN Gateway/Router is used to route (and modify) CAN frames. + It is based on the PF_CAN core infrastructure for msg filtering and + msg sending and can optionally modify routed CAN frames on the fly. + CAN frames can be routed between CAN network interfaces (one hop). + They can be modified with AND/OR/XOR/SET operations as configured + by the netlink configuration interface known e.g. from iptables. source "drivers/net/can/Kconfig" diff --git a/net/can/Makefile b/net/can/Makefile index 2d3894b..cef49eb 100644 --- a/net/can/Makefile +++ b/net/can/Makefile @@ -10,3 +10,6 @@ can-raw-y := raw.o obj-$(CONFIG_CAN_BCM) += can-bcm.o can-bcm-y := bcm.o + +obj-$(CONFIG_CAN_GW) += can-gw.o +can-gw-y := gw.o diff --git a/net/can/af_can.c b/net/can/af_can.c index 094fc53..7d9dff222 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -38,8 +38,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * - * Send feedback to <socketcan-users@lists.berlios.de> - * */ #include <linux/module.h> @@ -58,6 +56,7 @@ #include <linux/skbuff.h> #include <linux/can.h> #include <linux/can/core.h> +#include <linux/ratelimit.h> #include <net/net_namespace.h> #include <net/sock.h> @@ -161,8 +160,8 @@ static int can_create(struct net *net, struct socket *sock, int protocol, * return the error code immediately. Below we will * return -EPROTONOSUPPORT */ - if (err && printk_ratelimit()) - printk(KERN_ERR "can: request_module " + if (err) + printk_ratelimited(KERN_ERR "can: request_module " "(can-proto-%d) failed.\n", protocol); cp = can_get_proto(protocol); @@ -245,6 +244,9 @@ int can_send(struct sk_buff *skb, int loop) } skb->protocol = htons(ETH_P_CAN); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + skb_reset_mac_header(skb); skb_reset_network_header(skb); skb_reset_transport_header(skb); @@ -718,7 +720,7 @@ int can_proto_register(const struct can_proto *cp) proto); err = -EBUSY; } else - rcu_assign_pointer(proto_tab[proto], cp); + RCU_INIT_POINTER(proto_tab[proto], cp); mutex_unlock(&proto_tab_lock); @@ -739,7 +741,7 @@ void can_proto_unregister(const struct can_proto *cp) mutex_lock(&proto_tab_lock); BUG_ON(proto_tab[proto] != cp); - rcu_assign_pointer(proto_tab[proto], NULL); + RCU_INIT_POINTER(proto_tab[proto], NULL); mutex_unlock(&proto_tab_lock); synchronize_rcu(); @@ -856,7 +858,7 @@ static __exit void can_exit(void) struct net_device *dev; if (stats_timer) - del_timer(&can_stattimer); + del_timer_sync(&can_stattimer); can_remove_proc(); diff --git a/net/can/af_can.h b/net/can/af_can.h index 34253b8..fd882db 100644 --- a/net/can/af_can.h +++ b/net/can/af_can.h @@ -35,8 +35,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * - * Send feedback to <socketcan-users@lists.berlios.de> - * */ #ifndef AF_CAN_H diff --git a/net/can/bcm.c b/net/can/bcm.c index b117bfa..3910c1f 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -37,12 +37,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * - * Send feedback to <socketcan-users@lists.berlios.de> - * */ #include <linux/module.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/hrtimer.h> #include <linux/list.h> #include <linux/proc_fs.h> diff --git a/net/can/proc.c b/net/can/proc.c index 0016f73..ba873c3 100644 --- a/net/can/proc.c +++ b/net/can/proc.c @@ -37,8 +37,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * - * Send feedback to <socketcan-users@lists.berlios.de> - * */ #include <linux/module.h> diff --git a/net/can/raw.c b/net/can/raw.c index dea99a6..46cca3a 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -37,8 +37,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * - * Send feedback to <socketcan-users@lists.berlios.de> - * */ #include <linux/module.h> @@ -683,9 +681,6 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock, if (err < 0) goto free_skb; - /* to be able to check the received tx sock reference in raw_rcv() */ - skb_shinfo(skb)->tx_flags |= SKBTX_DRV_NEEDS_SK_REF; - skb->dev = dev; skb->sk = sk; diff --git a/net/ceph/Kconfig b/net/ceph/Kconfig index be683f2..cc04dd6 100644 --- a/net/ceph/Kconfig +++ b/net/ceph/Kconfig @@ -27,3 +27,17 @@ config CEPH_LIB_PRETTYDEBUG If unsure, say N. +config CEPH_LIB_USE_DNS_RESOLVER + bool "Use in-kernel support for DNS lookup" + depends on CEPH_LIB + select DNS_RESOLVER + default n + help + If you say Y here, hostnames (e.g. monitor addresses) will + be resolved using the CONFIG_DNS_RESOLVER facility. + + For information on how to use CONFIG_DNS_RESOLVER consult + Documentation/networking/dns_resolver.txt + + If unsure, say N. + diff --git a/net/ceph/auth_x.c b/net/ceph/auth_x.c index 1587dc6..9898d1f 100644 --- a/net/ceph/auth_x.c +++ b/net/ceph/auth_x.c @@ -13,8 +13,6 @@ #include "auth_x.h" #include "auth_x_protocol.h" -#define TEMP_TICKET_BUF_LEN 256 - static void ceph_x_validate_tickets(struct ceph_auth_client *ac, int *pneed); static int ceph_x_is_authenticated(struct ceph_auth_client *ac) @@ -64,7 +62,7 @@ static int ceph_x_encrypt(struct ceph_crypto_key *secret, } static int ceph_x_decrypt(struct ceph_crypto_key *secret, - void **p, void *end, void *obuf, size_t olen) + void **p, void *end, void **obuf, size_t olen) { struct ceph_x_encrypt_header head; size_t head_len = sizeof(head); @@ -75,8 +73,14 @@ static int ceph_x_decrypt(struct ceph_crypto_key *secret, return -EINVAL; dout("ceph_x_decrypt len %d\n", len); - ret = ceph_decrypt2(secret, &head, &head_len, obuf, &olen, - *p, len); + if (*obuf == NULL) { + *obuf = kmalloc(len, GFP_NOFS); + if (!*obuf) + return -ENOMEM; + olen = len; + } + + ret = ceph_decrypt2(secret, &head, &head_len, *obuf, &olen, *p, len); if (ret) return ret; if (head.struct_v != 1 || le64_to_cpu(head.magic) != CEPHX_ENC_MAGIC) @@ -129,139 +133,120 @@ static void remove_ticket_handler(struct ceph_auth_client *ac, kfree(th); } -static int ceph_x_proc_ticket_reply(struct ceph_auth_client *ac, - struct ceph_crypto_key *secret, - void *buf, void *end) +static int process_one_ticket(struct ceph_auth_client *ac, + struct ceph_crypto_key *secret, + void **p, void *end) { struct ceph_x_info *xi = ac->private; - int num; - void *p = buf; + int type; + u8 tkt_struct_v, blob_struct_v; + struct ceph_x_ticket_handler *th; + void *dbuf = NULL; + void *dp, *dend; + int dlen; + char is_enc; + struct timespec validity; + struct ceph_crypto_key old_key; + void *ticket_buf = NULL; + void *tp, *tpend; + struct ceph_timespec new_validity; + struct ceph_crypto_key new_session_key; + struct ceph_buffer *new_ticket_blob; + unsigned long new_expires, new_renew_after; + u64 new_secret_id; int ret; - char *dbuf; - char *ticket_buf; - u8 reply_struct_v; - dbuf = kmalloc(TEMP_TICKET_BUF_LEN, GFP_NOFS); - if (!dbuf) - return -ENOMEM; + ceph_decode_need(p, end, sizeof(u32) + 1, bad); - ret = -ENOMEM; - ticket_buf = kmalloc(TEMP_TICKET_BUF_LEN, GFP_NOFS); - if (!ticket_buf) - goto out_dbuf; + type = ceph_decode_32(p); + dout(" ticket type %d %s\n", type, ceph_entity_type_name(type)); - ceph_decode_need(&p, end, 1 + sizeof(u32), bad); - reply_struct_v = ceph_decode_8(&p); - if (reply_struct_v != 1) + tkt_struct_v = ceph_decode_8(p); + if (tkt_struct_v != 1) goto bad; - num = ceph_decode_32(&p); - dout("%d tickets\n", num); - while (num--) { - int type; - u8 tkt_struct_v, blob_struct_v; - struct ceph_x_ticket_handler *th; - void *dp, *dend; - int dlen; - char is_enc; - struct timespec validity; - struct ceph_crypto_key old_key; - void *tp, *tpend; - struct ceph_timespec new_validity; - struct ceph_crypto_key new_session_key; - struct ceph_buffer *new_ticket_blob; - unsigned long new_expires, new_renew_after; - u64 new_secret_id; - - ceph_decode_need(&p, end, sizeof(u32) + 1, bad); - - type = ceph_decode_32(&p); - dout(" ticket type %d %s\n", type, ceph_entity_type_name(type)); - - tkt_struct_v = ceph_decode_8(&p); - if (tkt_struct_v != 1) - goto bad; - - th = get_ticket_handler(ac, type); - if (IS_ERR(th)) { - ret = PTR_ERR(th); - goto out; - } - /* blob for me */ - dlen = ceph_x_decrypt(secret, &p, end, dbuf, - TEMP_TICKET_BUF_LEN); - if (dlen <= 0) { - ret = dlen; - goto out; - } - dout(" decrypted %d bytes\n", dlen); - dend = dbuf + dlen; - dp = dbuf; + th = get_ticket_handler(ac, type); + if (IS_ERR(th)) { + ret = PTR_ERR(th); + goto out; + } - tkt_struct_v = ceph_decode_8(&dp); - if (tkt_struct_v != 1) - goto bad; + /* blob for me */ + dlen = ceph_x_decrypt(secret, p, end, &dbuf, 0); + if (dlen <= 0) { + ret = dlen; + goto out; + } + dout(" decrypted %d bytes\n", dlen); + dp = dbuf; + dend = dp + dlen; - memcpy(&old_key, &th->session_key, sizeof(old_key)); - ret = ceph_crypto_key_decode(&new_session_key, &dp, dend); - if (ret) - goto out; + tkt_struct_v = ceph_decode_8(&dp); + if (tkt_struct_v != 1) + goto bad; - ceph_decode_copy(&dp, &new_validity, sizeof(new_validity)); - ceph_decode_timespec(&validity, &new_validity); - new_expires = get_seconds() + validity.tv_sec; - new_renew_after = new_expires - (validity.tv_sec / 4); - dout(" expires=%lu renew_after=%lu\n", new_expires, - new_renew_after); + memcpy(&old_key, &th->session_key, sizeof(old_key)); + ret = ceph_crypto_key_decode(&new_session_key, &dp, dend); + if (ret) + goto out; - /* ticket blob for service */ - ceph_decode_8_safe(&p, end, is_enc, bad); - tp = ticket_buf; - if (is_enc) { - /* encrypted */ - dout(" encrypted ticket\n"); - dlen = ceph_x_decrypt(&old_key, &p, end, ticket_buf, - TEMP_TICKET_BUF_LEN); - if (dlen < 0) { - ret = dlen; - goto out; - } - dlen = ceph_decode_32(&tp); - } else { - /* unencrypted */ - ceph_decode_32_safe(&p, end, dlen, bad); - ceph_decode_need(&p, end, dlen, bad); - ceph_decode_copy(&p, ticket_buf, dlen); + ceph_decode_copy(&dp, &new_validity, sizeof(new_validity)); + ceph_decode_timespec(&validity, &new_validity); + new_expires = get_seconds() + validity.tv_sec; + new_renew_after = new_expires - (validity.tv_sec / 4); + dout(" expires=%lu renew_after=%lu\n", new_expires, + new_renew_after); + + /* ticket blob for service */ + ceph_decode_8_safe(p, end, is_enc, bad); + if (is_enc) { + /* encrypted */ + dout(" encrypted ticket\n"); + dlen = ceph_x_decrypt(&old_key, p, end, &ticket_buf, 0); + if (dlen < 0) { + ret = dlen; + goto out; } - tpend = tp + dlen; - dout(" ticket blob is %d bytes\n", dlen); - ceph_decode_need(&tp, tpend, 1 + sizeof(u64), bad); - blob_struct_v = ceph_decode_8(&tp); - new_secret_id = ceph_decode_64(&tp); - ret = ceph_decode_buffer(&new_ticket_blob, &tp, tpend); - if (ret) + tp = ticket_buf; + dlen = ceph_decode_32(&tp); + } else { + /* unencrypted */ + ceph_decode_32_safe(p, end, dlen, bad); + ticket_buf = kmalloc(dlen, GFP_NOFS); + if (!ticket_buf) { + ret = -ENOMEM; goto out; - - /* all is well, update our ticket */ - ceph_crypto_key_destroy(&th->session_key); - if (th->ticket_blob) - ceph_buffer_put(th->ticket_blob); - th->session_key = new_session_key; - th->ticket_blob = new_ticket_blob; - th->validity = new_validity; - th->secret_id = new_secret_id; - th->expires = new_expires; - th->renew_after = new_renew_after; - dout(" got ticket service %d (%s) secret_id %lld len %d\n", - type, ceph_entity_type_name(type), th->secret_id, - (int)th->ticket_blob->vec.iov_len); - xi->have_keys |= th->service; + } + tp = ticket_buf; + ceph_decode_need(p, end, dlen, bad); + ceph_decode_copy(p, ticket_buf, dlen); } + tpend = tp + dlen; + dout(" ticket blob is %d bytes\n", dlen); + ceph_decode_need(&tp, tpend, 1 + sizeof(u64), bad); + blob_struct_v = ceph_decode_8(&tp); + new_secret_id = ceph_decode_64(&tp); + ret = ceph_decode_buffer(&new_ticket_blob, &tp, tpend); + if (ret) + goto out; + + /* all is well, update our ticket */ + ceph_crypto_key_destroy(&th->session_key); + if (th->ticket_blob) + ceph_buffer_put(th->ticket_blob); + th->session_key = new_session_key; + th->ticket_blob = new_ticket_blob; + th->validity = new_validity; + th->secret_id = new_secret_id; + th->expires = new_expires; + th->renew_after = new_renew_after; + dout(" got ticket service %d (%s) secret_id %lld len %d\n", + type, ceph_entity_type_name(type), th->secret_id, + (int)th->ticket_blob->vec.iov_len); + xi->have_keys |= th->service; - ret = 0; out: kfree(ticket_buf); -out_dbuf: kfree(dbuf); return ret; @@ -270,6 +255,34 @@ bad: goto out; } +static int ceph_x_proc_ticket_reply(struct ceph_auth_client *ac, + struct ceph_crypto_key *secret, + void *buf, void *end) +{ + void *p = buf; + u8 reply_struct_v; + u32 num; + int ret; + + ceph_decode_8_safe(&p, end, reply_struct_v, bad); + if (reply_struct_v != 1) + return -EINVAL; + + ceph_decode_32_safe(&p, end, num, bad); + dout("%d tickets\n", num); + + while (num--) { + ret = process_one_ticket(ac, secret, &p, end); + if (ret) + return ret; + } + + return 0; + +bad: + return -EINVAL; +} + static int ceph_x_build_authorizer(struct ceph_auth_client *ac, struct ceph_x_ticket_handler *th, struct ceph_x_authorizer *au) @@ -563,13 +576,14 @@ static int ceph_x_verify_authorizer_reply(struct ceph_auth_client *ac, struct ceph_x_ticket_handler *th; int ret = 0; struct ceph_x_authorize_reply reply; + void *preply = &reply; void *p = au->reply_buf; void *end = p + sizeof(au->reply_buf); th = get_ticket_handler(ac, au->service); if (IS_ERR(th)) return PTR_ERR(th); - ret = ceph_x_decrypt(&th->session_key, &p, end, &reply, sizeof(reply)); + ret = ceph_x_decrypt(&th->session_key, &p, end, &preply, sizeof(reply)); if (ret < 0) return ret; if (ret != sizeof(reply)) diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index 132963a..97f70e5 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -232,6 +232,7 @@ void ceph_destroy_options(struct ceph_options *opt) ceph_crypto_key_destroy(opt->key); kfree(opt->key); } + kfree(opt->mon_addr); kfree(opt); } EXPORT_SYMBOL(ceph_destroy_options); @@ -431,9 +432,12 @@ EXPORT_SYMBOL(ceph_client_id); /* * create a fresh client instance */ -struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private) +struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private, + unsigned supported_features, + unsigned required_features) { struct ceph_client *client; + struct ceph_entity_addr *myaddr = NULL; int err = -ENOMEM; client = kzalloc(sizeof(*client), GFP_KERNEL); @@ -448,15 +452,27 @@ struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private) client->auth_err = 0; client->extra_mon_dispatch = NULL; - client->supported_features = CEPH_FEATURE_SUPPORTED_DEFAULT; - client->required_features = CEPH_FEATURE_REQUIRED_DEFAULT; - - client->msgr = NULL; + client->supported_features = CEPH_FEATURE_SUPPORTED_DEFAULT | + supported_features; + client->required_features = CEPH_FEATURE_REQUIRED_DEFAULT | + required_features; + + /* msgr */ + if (ceph_test_opt(client, MYIP)) + myaddr = &client->options->my_addr; + client->msgr = ceph_messenger_create(myaddr, + client->supported_features, + client->required_features); + if (IS_ERR(client->msgr)) { + err = PTR_ERR(client->msgr); + goto fail; + } + client->msgr->nocrc = ceph_test_opt(client, NOCRC); /* subsystems */ err = ceph_monc_init(&client->monc, client); if (err < 0) - goto fail; + goto fail_msgr; err = ceph_osdc_init(&client->osdc, client); if (err < 0) goto fail_monc; @@ -465,6 +481,8 @@ struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private) fail_monc: ceph_monc_stop(&client->monc); +fail_msgr: + ceph_messenger_destroy(client->msgr); fail: kfree(client); return ERR_PTR(err); @@ -489,8 +507,7 @@ void ceph_destroy_client(struct ceph_client *client) ceph_debugfs_client_cleanup(client); - if (client->msgr) - ceph_messenger_destroy(client->msgr); + ceph_messenger_destroy(client->msgr); ceph_destroy_options(client->options); @@ -513,24 +530,9 @@ static int have_mon_and_osd_map(struct ceph_client *client) */ int __ceph_open_session(struct ceph_client *client, unsigned long started) { - struct ceph_entity_addr *myaddr = NULL; int err; unsigned long timeout = client->options->mount_timeout * HZ; - /* initialize the messenger */ - if (client->msgr == NULL) { - if (ceph_test_opt(client, MYIP)) - myaddr = &client->options->my_addr; - client->msgr = ceph_messenger_create(myaddr, - client->supported_features, - client->required_features); - if (IS_ERR(client->msgr)) { - client->msgr = NULL; - return PTR_ERR(client->msgr); - } - client->msgr->nocrc = ceph_test_opt(client, NOCRC); - } - /* open session, and wait for mon and osd maps */ err = ceph_monc_open_session(&client->monc); if (err < 0) diff --git a/net/ceph/crush/mapper.c b/net/ceph/crush/mapper.c index 42599e3..3a94eae 100644 --- a/net/ceph/crush/mapper.c +++ b/net/ceph/crush/mapper.c @@ -477,7 +477,6 @@ int crush_do_rule(struct crush_map *map, int i, j; int numrep; int firstn; - int rc = -1; BUG_ON(ruleno >= map->max_rules); @@ -491,23 +490,18 @@ int crush_do_rule(struct crush_map *map, * that this may or may not correspond to the specific types * referenced by the crush rule. */ - if (force >= 0) { - if (force >= map->max_devices || - map->device_parents[force] == 0) { - /*dprintk("CRUSH: forcefed device dne\n");*/ - rc = -1; /* force fed device dne */ - goto out; - } - if (!is_out(map, weight, force, x)) { - while (1) { - force_context[++force_pos] = force; - if (force >= 0) - force = map->device_parents[force]; - else - force = map->bucket_parents[-1-force]; - if (force == 0) - break; - } + if (force >= 0 && + force < map->max_devices && + map->device_parents[force] != 0 && + !is_out(map, weight, force, x)) { + while (1) { + force_context[++force_pos] = force; + if (force >= 0) + force = map->device_parents[force]; + else + force = map->bucket_parents[-1-force]; + if (force == 0) + break; } } @@ -600,10 +594,7 @@ int crush_do_rule(struct crush_map *map, BUG_ON(1); } } - rc = result_len; - -out: - return rc; + return result_len; } diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c index 5a8009c..21e777b 100644 --- a/net/ceph/crypto.c +++ b/net/ceph/crypto.c @@ -90,11 +90,82 @@ static struct crypto_blkcipher *ceph_crypto_alloc_cipher(void) static const u8 *aes_iv = (u8 *)CEPH_AES_IV; +/* + * Should be used for buffers allocated with ceph_kvmalloc(). + * Currently these are encrypt out-buffer (ceph_buffer) and decrypt + * in-buffer (msg front). + * + * Dispose of @sgt with teardown_sgtable(). + * + * @prealloc_sg is to avoid memory allocation inside sg_alloc_table() + * in cases where a single sg is sufficient. No attempt to reduce the + * number of sgs by squeezing physically contiguous pages together is + * made though, for simplicity. + */ +static int setup_sgtable(struct sg_table *sgt, struct scatterlist *prealloc_sg, + const void *buf, unsigned int buf_len) +{ + struct scatterlist *sg; + const bool is_vmalloc = is_vmalloc_addr(buf); + unsigned int off = offset_in_page(buf); + unsigned int chunk_cnt = 1; + unsigned int chunk_len = PAGE_ALIGN(off + buf_len); + int i; + int ret; + + if (buf_len == 0) { + memset(sgt, 0, sizeof(*sgt)); + return -EINVAL; + } + + if (is_vmalloc) { + chunk_cnt = chunk_len >> PAGE_SHIFT; + chunk_len = PAGE_SIZE; + } + + if (chunk_cnt > 1) { + ret = sg_alloc_table(sgt, chunk_cnt, GFP_NOFS); + if (ret) + return ret; + } else { + WARN_ON(chunk_cnt != 1); + sg_init_table(prealloc_sg, 1); + sgt->sgl = prealloc_sg; + sgt->nents = sgt->orig_nents = 1; + } + + for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) { + struct page *page; + unsigned int len = min(chunk_len - off, buf_len); + + if (is_vmalloc) + page = vmalloc_to_page(buf); + else + page = virt_to_page(buf); + + sg_set_page(sg, page, len, off); + + off = 0; + buf += len; + buf_len -= len; + } + WARN_ON(buf_len != 0); + + return 0; +} + +static void teardown_sgtable(struct sg_table *sgt) +{ + if (sgt->orig_nents > 1) + sg_free_table(sgt); +} + static int ceph_aes_encrypt(const void *key, int key_len, void *dst, size_t *dst_len, const void *src, size_t src_len) { - struct scatterlist sg_in[2], sg_out[1]; + struct scatterlist sg_in[2], prealloc_sg; + struct sg_table sg_out; struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 }; int ret; @@ -110,16 +181,18 @@ static int ceph_aes_encrypt(const void *key, int key_len, *dst_len = src_len + zero_padding; - crypto_blkcipher_setkey((void *)tfm, key, key_len); sg_init_table(sg_in, 2); sg_set_buf(&sg_in[0], src, src_len); sg_set_buf(&sg_in[1], pad, zero_padding); - sg_init_table(sg_out, 1); - sg_set_buf(sg_out, dst, *dst_len); + ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len); + if (ret) + goto out_tfm; + + crypto_blkcipher_setkey((void *)tfm, key, key_len); iv = crypto_blkcipher_crt(tfm)->iv; ivsize = crypto_blkcipher_ivsize(tfm); - memcpy(iv, aes_iv, ivsize); + /* print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1, key, key_len, 1); @@ -128,16 +201,22 @@ static int ceph_aes_encrypt(const void *key, int key_len, print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1, pad, zero_padding, 1); */ - ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, + ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in, src_len + zero_padding); - crypto_free_blkcipher(tfm); - if (ret < 0) + if (ret < 0) { pr_err("ceph_aes_crypt failed %d\n", ret); + goto out_sg; + } /* print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1, dst, *dst_len, 1); */ - return 0; + +out_sg: + teardown_sgtable(&sg_out); +out_tfm: + crypto_free_blkcipher(tfm); + return ret; } static int ceph_aes_encrypt2(const void *key, int key_len, void *dst, @@ -145,7 +224,8 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst, const void *src1, size_t src1_len, const void *src2, size_t src2_len) { - struct scatterlist sg_in[3], sg_out[1]; + struct scatterlist sg_in[3], prealloc_sg; + struct sg_table sg_out; struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 }; int ret; @@ -161,17 +241,19 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst, *dst_len = src1_len + src2_len + zero_padding; - crypto_blkcipher_setkey((void *)tfm, key, key_len); sg_init_table(sg_in, 3); sg_set_buf(&sg_in[0], src1, src1_len); sg_set_buf(&sg_in[1], src2, src2_len); sg_set_buf(&sg_in[2], pad, zero_padding); - sg_init_table(sg_out, 1); - sg_set_buf(sg_out, dst, *dst_len); + ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len); + if (ret) + goto out_tfm; + + crypto_blkcipher_setkey((void *)tfm, key, key_len); iv = crypto_blkcipher_crt(tfm)->iv; ivsize = crypto_blkcipher_ivsize(tfm); - memcpy(iv, aes_iv, ivsize); + /* print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1, key, key_len, 1); @@ -182,23 +264,30 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst, print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1, pad, zero_padding, 1); */ - ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, + ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in, src1_len + src2_len + zero_padding); - crypto_free_blkcipher(tfm); - if (ret < 0) + if (ret < 0) { pr_err("ceph_aes_crypt2 failed %d\n", ret); + goto out_sg; + } /* print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1, dst, *dst_len, 1); */ - return 0; + +out_sg: + teardown_sgtable(&sg_out); +out_tfm: + crypto_free_blkcipher(tfm); + return ret; } static int ceph_aes_decrypt(const void *key, int key_len, void *dst, size_t *dst_len, const void *src, size_t src_len) { - struct scatterlist sg_in[1], sg_out[2]; + struct sg_table sg_in; + struct scatterlist sg_out[2], prealloc_sg; struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); struct blkcipher_desc desc = { .tfm = tfm }; char pad[16]; @@ -210,16 +299,16 @@ static int ceph_aes_decrypt(const void *key, int key_len, if (IS_ERR(tfm)) return PTR_ERR(tfm); - crypto_blkcipher_setkey((void *)tfm, key, key_len); - sg_init_table(sg_in, 1); sg_init_table(sg_out, 2); - sg_set_buf(sg_in, src, src_len); sg_set_buf(&sg_out[0], dst, *dst_len); sg_set_buf(&sg_out[1], pad, sizeof(pad)); + ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len); + if (ret) + goto out_tfm; + crypto_blkcipher_setkey((void *)tfm, key, key_len); iv = crypto_blkcipher_crt(tfm)->iv; ivsize = crypto_blkcipher_ivsize(tfm); - memcpy(iv, aes_iv, ivsize); /* @@ -228,12 +317,10 @@ static int ceph_aes_decrypt(const void *key, int key_len, print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1, src, src_len, 1); */ - - ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len); - crypto_free_blkcipher(tfm); + ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len); if (ret < 0) { pr_err("ceph_aes_decrypt failed %d\n", ret); - return ret; + goto out_sg; } if (src_len <= *dst_len) @@ -251,7 +338,12 @@ static int ceph_aes_decrypt(const void *key, int key_len, print_hex_dump(KERN_ERR, "dec out: ", DUMP_PREFIX_NONE, 16, 1, dst, *dst_len, 1); */ - return 0; + +out_sg: + teardown_sgtable(&sg_in); +out_tfm: + crypto_free_blkcipher(tfm); + return ret; } static int ceph_aes_decrypt2(const void *key, int key_len, @@ -259,7 +351,8 @@ static int ceph_aes_decrypt2(const void *key, int key_len, void *dst2, size_t *dst2_len, const void *src, size_t src_len) { - struct scatterlist sg_in[1], sg_out[3]; + struct sg_table sg_in; + struct scatterlist sg_out[3], prealloc_sg; struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); struct blkcipher_desc desc = { .tfm = tfm }; char pad[16]; @@ -271,17 +364,17 @@ static int ceph_aes_decrypt2(const void *key, int key_len, if (IS_ERR(tfm)) return PTR_ERR(tfm); - sg_init_table(sg_in, 1); - sg_set_buf(sg_in, src, src_len); sg_init_table(sg_out, 3); sg_set_buf(&sg_out[0], dst1, *dst1_len); sg_set_buf(&sg_out[1], dst2, *dst2_len); sg_set_buf(&sg_out[2], pad, sizeof(pad)); + ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len); + if (ret) + goto out_tfm; crypto_blkcipher_setkey((void *)tfm, key, key_len); iv = crypto_blkcipher_crt(tfm)->iv; ivsize = crypto_blkcipher_ivsize(tfm); - memcpy(iv, aes_iv, ivsize); /* @@ -290,12 +383,10 @@ static int ceph_aes_decrypt2(const void *key, int key_len, print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1, src, src_len, 1); */ - - ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len); - crypto_free_blkcipher(tfm); + ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len); if (ret < 0) { pr_err("ceph_aes_decrypt failed %d\n", ret); - return ret; + goto out_sg; } if (src_len <= *dst1_len) @@ -325,7 +416,11 @@ static int ceph_aes_decrypt2(const void *key, int key_len, dst2, *dst2_len, 1); */ - return 0; +out_sg: + teardown_sgtable(&sg_in); +out_tfm: + crypto_free_blkcipher(tfm); + return ret; } @@ -444,7 +539,7 @@ int ceph_key_instantiate(struct key *key, const void *data, size_t datalen) goto err; /* TODO ceph_crypto_key_decode should really take const input */ - p = (void*)data; + p = (void *)data; ret = ceph_crypto_key_decode(ckey, &p, (char*)data+datalen); if (ret < 0) goto err_ckey; diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 78b55f4..e85a8d2 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -11,12 +11,14 @@ #include <linux/string.h> #include <linux/bio.h> #include <linux/blkdev.h> +#include <linux/dns_resolver.h> #include <net/tcp.h> #include <linux/ceph/libceph.h> #include <linux/ceph/messenger.h> #include <linux/ceph/decode.h> #include <linux/ceph/pagelist.h> +#include <linux/export.h> /* * Ceph uses the messenger to exchange ceph_msg messages with other @@ -97,7 +99,12 @@ struct workqueue_struct *ceph_msgr_wq; int ceph_msgr_init(void) { - ceph_msgr_wq = alloc_workqueue("ceph-msgr", WQ_NON_REENTRANT, 0); + /* + * The number of active work items is limited by the number of + * connections, so leave @max_active at default. + */ + ceph_msgr_wq = alloc_workqueue("ceph-msgr", + WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0); if (!ceph_msgr_wq) { pr_err("msgr_init failed to create workqueue\n"); return -ENOMEM; @@ -282,6 +289,37 @@ static int ceph_tcp_sendmsg(struct socket *sock, struct kvec *iov, return r; } +static int __ceph_tcp_sendpage(struct socket *sock, struct page *page, + int offset, size_t size, bool more) +{ + int flags = MSG_DONTWAIT | MSG_NOSIGNAL | (more ? MSG_MORE : MSG_EOR); + int ret; + + ret = kernel_sendpage(sock, page, offset, size, flags); + if (ret == -EAGAIN) + ret = 0; + + return ret; +} + +static int ceph_tcp_sendpage(struct socket *sock, struct page *page, + int offset, size_t size, bool more) +{ + int ret; + struct kvec iov; + + /* sendpage cannot properly handle pages with page_count == 0, + * we need to fallback to sendmsg if that's the case */ + if (page_count(page) >= 1) + return __ceph_tcp_sendpage(sock, page, offset, size, more); + + iov.iov_base = kmap(page) + offset; + iov.iov_len = size; + ret = ceph_tcp_sendmsg(sock, &iov, 1, size, more); + kunmap(page); + + return ret; +} /* * Shutdown/close the socket for the given connection. @@ -486,13 +524,10 @@ static void prepare_write_message(struct ceph_connection *con) m = list_first_entry(&con->out_queue, struct ceph_msg, list_head); con->out_msg = m; - if (test_bit(LOSSYTX, &con->state)) { - list_del_init(&m->list_head); - } else { - /* put message on sent list */ - ceph_msg_get(m); - list_move_tail(&m->list_head, &con->out_sent); - } + + /* put message on sent list */ + ceph_msg_get(m); + list_move_tail(&m->list_head, &con->out_sent); /* * only assign outgoing seq # if we haven't sent this message @@ -852,18 +887,14 @@ static int write_partial_msg_pages(struct ceph_connection *con) cpu_to_le32(crc32c(tmpcrc, base, len)); con->out_msg_pos.did_page_crc = 1; } - ret = kernel_sendpage(con->sock, page, + ret = ceph_tcp_sendpage(con->sock, page, con->out_msg_pos.page_pos + page_shift, - len, - MSG_DONTWAIT | MSG_NOSIGNAL | - MSG_MORE); + len, 1); if (crc && (msg->pages || msg->pagelist || msg->bio || in_trail)) kunmap(page); - if (ret == -EAGAIN) - ret = 0; if (ret <= 0) goto out; @@ -1081,6 +1112,101 @@ static void addr_set_port(struct sockaddr_storage *ss, int p) } /* + * Unlike other *_pton function semantics, zero indicates success. + */ +static int ceph_pton(const char *str, size_t len, struct sockaddr_storage *ss, + char delim, const char **ipend) +{ + struct sockaddr_in *in4 = (void *)ss; + struct sockaddr_in6 *in6 = (void *)ss; + + memset(ss, 0, sizeof(*ss)); + + if (in4_pton(str, len, (u8 *)&in4->sin_addr.s_addr, delim, ipend)) { + ss->ss_family = AF_INET; + return 0; + } + + if (in6_pton(str, len, (u8 *)&in6->sin6_addr.s6_addr, delim, ipend)) { + ss->ss_family = AF_INET6; + return 0; + } + + return -EINVAL; +} + +/* + * Extract hostname string and resolve using kernel DNS facility. + */ +#ifdef CONFIG_CEPH_LIB_USE_DNS_RESOLVER +static int ceph_dns_resolve_name(const char *name, size_t namelen, + struct sockaddr_storage *ss, char delim, const char **ipend) +{ + const char *end, *delim_p; + char *colon_p, *ip_addr = NULL; + int ip_len, ret; + + /* + * The end of the hostname occurs immediately preceding the delimiter or + * the port marker (':') where the delimiter takes precedence. + */ + delim_p = memchr(name, delim, namelen); + colon_p = memchr(name, ':', namelen); + + if (delim_p && colon_p) + end = delim_p < colon_p ? delim_p : colon_p; + else if (!delim_p && colon_p) + end = colon_p; + else { + end = delim_p; + if (!end) /* case: hostname:/ */ + end = name + namelen; + } + + if (end <= name) + return -EINVAL; + + /* do dns_resolve upcall */ + ip_len = dns_query(NULL, name, end - name, NULL, &ip_addr, NULL); + if (ip_len > 0) + ret = ceph_pton(ip_addr, ip_len, ss, -1, NULL); + else + ret = -ESRCH; + + kfree(ip_addr); + + *ipend = end; + + pr_info("resolve '%.*s' (ret=%d): %s\n", (int)(end - name), name, + ret, ret ? "failed" : ceph_pr_addr(ss)); + + return ret; +} +#else +static inline int ceph_dns_resolve_name(const char *name, size_t namelen, + struct sockaddr_storage *ss, char delim, const char **ipend) +{ + return -EINVAL; +} +#endif + +/* + * Parse a server name (IP or hostname). If a valid IP address is not found + * then try to extract a hostname to resolve using userspace DNS upcall. + */ +static int ceph_parse_server_name(const char *name, size_t namelen, + struct sockaddr_storage *ss, char delim, const char **ipend) +{ + int ret; + + ret = ceph_pton(name, namelen, ss, delim, ipend); + if (ret) + ret = ceph_dns_resolve_name(name, namelen, ss, delim, ipend); + + return ret; +} + +/* * Parse an ip[:port] list into an addr array. Use the default * monitor port if a port isn't specified. */ @@ -1088,15 +1214,13 @@ int ceph_parse_ips(const char *c, const char *end, struct ceph_entity_addr *addr, int max_count, int *count) { - int i; + int i, ret = -EINVAL; const char *p = c; dout("parse_ips on '%.*s'\n", (int)(end-c), c); for (i = 0; i < max_count; i++) { const char *ipend; struct sockaddr_storage *ss = &addr[i].in_addr; - struct sockaddr_in *in4 = (void *)ss; - struct sockaddr_in6 *in6 = (void *)ss; int port; char delim = ','; @@ -1105,15 +1229,11 @@ int ceph_parse_ips(const char *c, const char *end, p++; } - memset(ss, 0, sizeof(*ss)); - if (in4_pton(p, end - p, (u8 *)&in4->sin_addr.s_addr, - delim, &ipend)) - ss->ss_family = AF_INET; - else if (in6_pton(p, end - p, (u8 *)&in6->sin6_addr.s6_addr, - delim, &ipend)) - ss->ss_family = AF_INET6; - else + ret = ceph_parse_server_name(p, end - p, ss, delim, &ipend); + if (ret) goto bad; + ret = -EINVAL; + p = ipend; if (delim == ']') { @@ -1158,7 +1278,7 @@ int ceph_parse_ips(const char *c, const char *end, bad: pr_err("parse_ips bad ip '%.*s'\n", (int)(end - c), c); - return -EINVAL; + return ret; } EXPORT_SYMBOL(ceph_parse_ips); @@ -1399,6 +1519,7 @@ static void process_ack(struct ceph_connection *con) break; dout("got ack for seq %llu type %d at %p\n", seq, le16_to_cpu(m->hdr.type), m); + m->ack_stamp = jiffies; ceph_msg_remove(m); } prepare_read_tag(con); @@ -2283,7 +2404,8 @@ EXPORT_SYMBOL(ceph_con_keepalive); * construct a new message with given type, size * the new msg has a ref count of 1. */ -struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags) +struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags, + bool can_fail) { struct ceph_msg *m; @@ -2306,9 +2428,10 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags) m->footer.middle_crc = 0; m->footer.data_crc = 0; m->footer.flags = 0; - m->front_max = front_len; + m->front_alloc_len = front_len; m->front_is_vmalloc = false; m->more_to_follow = false; + m->ack_stamp = 0; m->pool = NULL; /* middle */ @@ -2334,7 +2457,7 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags) m->front.iov_base = kmalloc(front_len, flags); } if (m->front.iov_base == NULL) { - pr_err("msg_new can't allocate %d bytes\n", + dout("ceph_msg_new can't allocate %d bytes\n", front_len); goto out2; } @@ -2349,7 +2472,14 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags) out2: ceph_msg_put(m); out: - pr_err("msg_new can't create type %d front %d\n", type, front_len); + if (!can_fail) { + pr_err("msg_new can't create type %d front %d\n", type, + front_len); + WARN_ON(1); + } else { + dout("msg_new can't create type %d front %d\n", type, + front_len); + } return NULL; } EXPORT_SYMBOL(ceph_msg_new); @@ -2399,7 +2529,7 @@ static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, } if (!msg) { *skip = 0; - msg = ceph_msg_new(type, front_len, GFP_NOFS); + msg = ceph_msg_new(type, front_len, GFP_NOFS, false); if (!msg) { pr_err("unable to allocate msg type %d len %d\n", type, front_len); @@ -2469,8 +2599,8 @@ EXPORT_SYMBOL(ceph_msg_last_put); void ceph_msg_dump(struct ceph_msg *msg) { - pr_debug("msg_dump %p (front_max %d nr_pages %d)\n", msg, - msg->front_max, msg->nr_pages); + pr_debug("msg_dump %p (front_alloc_len %d nr_pages %d)\n", msg, + msg->front_alloc_len, msg->nr_pages); print_hex_dump(KERN_DEBUG, "header: ", DUMP_PREFIX_OFFSET, 16, 1, &msg->hdr, sizeof(msg->hdr), true); diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index cbe31fa..0c0859b 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -116,14 +116,12 @@ static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len) */ static void __close_session(struct ceph_mon_client *monc) { - if (monc->con) { - dout("__close_session closing mon%d\n", monc->cur_mon); - ceph_con_revoke(monc->con, monc->m_auth); - ceph_con_close(monc->con); - monc->cur_mon = -1; - monc->pending_auth = 0; - ceph_auth_reset(monc->auth); - } + dout("__close_session closing mon%d\n", monc->cur_mon); + ceph_con_revoke(monc->con, monc->m_auth); + ceph_con_close(monc->con); + monc->cur_mon = -1; + monc->pending_auth = 0; + ceph_auth_reset(monc->auth); } /* @@ -152,7 +150,7 @@ static int __open_session(struct ceph_mon_client *monc) /* initiatiate authentication handshake */ ret = ceph_auth_build_hello(monc->auth, monc->m_auth->front.iov_base, - monc->m_auth->front_max); + monc->m_auth->front_alloc_len); __send_prepared_auth_request(monc, ret); } else { dout("open_session mon%d already open\n", monc->cur_mon); @@ -196,7 +194,7 @@ static void __send_subscribe(struct ceph_mon_client *monc) int num; p = msg->front.iov_base; - end = p + msg->front_max; + end = p + msg->front_alloc_len; num = 1 + !!monc->want_next_osdmap + !!monc->want_mdsmap; ceph_encode_32(&p, num); @@ -302,15 +300,6 @@ void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc) */ int ceph_monc_open_session(struct ceph_mon_client *monc) { - if (!monc->con) { - monc->con = kmalloc(sizeof(*monc->con), GFP_KERNEL); - if (!monc->con) - return -ENOMEM; - ceph_con_init(monc->client->msgr, monc->con); - monc->con->private = monc; - monc->con->ops = &mon_con_ops; - } - mutex_lock(&monc->mutex); __open_session(monc); __schedule_delayed(monc); @@ -528,10 +517,12 @@ int ceph_monc_do_statfs(struct ceph_mon_client *monc, struct ceph_statfs *buf) init_completion(&req->completion); err = -ENOMEM; - req->request = ceph_msg_new(CEPH_MSG_STATFS, sizeof(*h), GFP_NOFS); + req->request = ceph_msg_new(CEPH_MSG_STATFS, sizeof(*h), GFP_NOFS, + true); if (!req->request) goto out; - req->reply = ceph_msg_new(CEPH_MSG_STATFS_REPLY, 1024, GFP_NOFS); + req->reply = ceph_msg_new(CEPH_MSG_STATFS_REPLY, 1024, GFP_NOFS, + true); if (!req->reply) goto out; @@ -626,10 +617,12 @@ int ceph_monc_do_poolop(struct ceph_mon_client *monc, u32 op, init_completion(&req->completion); err = -ENOMEM; - req->request = ceph_msg_new(CEPH_MSG_POOLOP, sizeof(*h), GFP_NOFS); + req->request = ceph_msg_new(CEPH_MSG_POOLOP, sizeof(*h), GFP_NOFS, + true); if (!req->request) goto out; - req->reply = ceph_msg_new(CEPH_MSG_POOLOP_REPLY, 1024, GFP_NOFS); + req->reply = ceph_msg_new(CEPH_MSG_POOLOP_REPLY, 1024, GFP_NOFS, + true); if (!req->reply) goto out; @@ -755,13 +748,21 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) if (err) goto out; - monc->con = NULL; + /* connection */ + monc->con = kmalloc(sizeof(*monc->con), GFP_KERNEL); + if (!monc->con) + goto out_monmap; + ceph_con_init(monc->client->msgr, monc->con); + monc->con->private = monc; + monc->con->ops = &mon_con_ops; /* authentication */ monc->auth = ceph_auth_init(cl->options->name, cl->options->key); - if (IS_ERR(monc->auth)) - return PTR_ERR(monc->auth); + if (IS_ERR(monc->auth)) { + err = PTR_ERR(monc->auth); + goto out_con; + } monc->auth->want_keys = CEPH_ENTITY_TYPE_AUTH | CEPH_ENTITY_TYPE_MON | CEPH_ENTITY_TYPE_OSD | CEPH_ENTITY_TYPE_MDS; @@ -770,19 +771,21 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) err = -ENOMEM; monc->m_subscribe_ack = ceph_msg_new(CEPH_MSG_MON_SUBSCRIBE_ACK, sizeof(struct ceph_mon_subscribe_ack), - GFP_NOFS); + GFP_NOFS, true); if (!monc->m_subscribe_ack) - goto out_monmap; + goto out_auth; - monc->m_subscribe = ceph_msg_new(CEPH_MSG_MON_SUBSCRIBE, 96, GFP_NOFS); + monc->m_subscribe = ceph_msg_new(CEPH_MSG_MON_SUBSCRIBE, 96, GFP_NOFS, + true); if (!monc->m_subscribe) goto out_subscribe_ack; - monc->m_auth_reply = ceph_msg_new(CEPH_MSG_AUTH_REPLY, 4096, GFP_NOFS); + monc->m_auth_reply = ceph_msg_new(CEPH_MSG_AUTH_REPLY, 4096, GFP_NOFS, + true); if (!monc->m_auth_reply) goto out_subscribe; - monc->m_auth = ceph_msg_new(CEPH_MSG_AUTH, 4096, GFP_NOFS); + monc->m_auth = ceph_msg_new(CEPH_MSG_AUTH, 4096, GFP_NOFS, true); monc->pending_auth = 0; if (!monc->m_auth) goto out_auth_reply; @@ -808,6 +811,10 @@ out_subscribe: ceph_msg_put(monc->m_subscribe); out_subscribe_ack: ceph_msg_put(monc->m_subscribe_ack); +out_auth: + ceph_auth_destroy(monc->auth); +out_con: + monc->con->ops->put(monc->con); out_monmap: kfree(monc->monmap); out: @@ -822,11 +829,11 @@ void ceph_monc_stop(struct ceph_mon_client *monc) mutex_lock(&monc->mutex); __close_session(monc); - if (monc->con) { - monc->con->private = NULL; - monc->con->ops->put(monc->con); - monc->con = NULL; - } + + monc->con->private = NULL; + monc->con->ops->put(monc->con); + monc->con = NULL; + mutex_unlock(&monc->mutex); ceph_auth_destroy(monc->auth); @@ -853,7 +860,7 @@ static void handle_auth_reply(struct ceph_mon_client *monc, ret = ceph_handle_auth_reply(monc->auth, msg->front.iov_base, msg->front.iov_len, monc->m_auth->front.iov_base, - monc->m_auth->front_max); + monc->m_auth->front_alloc_len); if (ret < 0) { monc->client->auth_err = ret; wake_up_all(&monc->client->auth_wq); @@ -880,7 +887,7 @@ static int __validate_auth(struct ceph_mon_client *monc) return 0; ret = ceph_build_auth(monc->auth, monc->m_auth->front.iov_base, - monc->m_auth->front_max); + monc->m_auth->front_alloc_len); if (ret <= 0) return ret; /* either an error, or no need to authenticate */ __send_prepared_auth_request(monc, ret); @@ -973,14 +980,22 @@ static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con, case CEPH_MSG_MON_MAP: case CEPH_MSG_MDS_MAP: case CEPH_MSG_OSD_MAP: - m = ceph_msg_new(type, front_len, GFP_NOFS); + m = ceph_msg_new(type, front_len, GFP_NOFS, false); break; } if (!m) { pr_info("alloc_msg unknown type %d\n", type); *skip = 1; + } else if (front_len > m->front_alloc_len) { + pr_warning("mon_alloc_msg front %d > prealloc %d (%u#%llu)\n", + front_len, m->front_alloc_len, + (unsigned int)con->peer_name.type, + le64_to_cpu(con->peer_name.num)); + ceph_msg_put(m); + m = ceph_msg_new(type, front_len, GFP_NOFS, false); } + return m; } @@ -1000,7 +1015,7 @@ static void mon_fault(struct ceph_connection *con) if (!con->private) goto out; - if (monc->con && !monc->hunting) + if (!monc->hunting) pr_info("mon%d %s session lost, " "hunting for new mon\n", monc->cur_mon, ceph_pr_addr(&monc->con->peer_addr.in_addr)); diff --git a/net/ceph/msgpool.c b/net/ceph/msgpool.c index d5f2d97..11d5f41 100644 --- a/net/ceph/msgpool.c +++ b/net/ceph/msgpool.c @@ -7,27 +7,37 @@ #include <linux/ceph/msgpool.h> -static void *alloc_fn(gfp_t gfp_mask, void *arg) +static void *msgpool_alloc(gfp_t gfp_mask, void *arg) { struct ceph_msgpool *pool = arg; - void *p; + struct ceph_msg *msg; - p = ceph_msg_new(0, pool->front_len, gfp_mask); - if (!p) - pr_err("msgpool %s alloc failed\n", pool->name); - return p; + msg = ceph_msg_new(0, pool->front_len, gfp_mask, true); + if (!msg) { + dout("msgpool_alloc %s failed\n", pool->name); + } else { + dout("msgpool_alloc %s %p\n", pool->name, msg); + msg->pool = pool; + } + return msg; } -static void free_fn(void *element, void *arg) +static void msgpool_free(void *element, void *arg) { - ceph_msg_put(element); + struct ceph_msgpool *pool = arg; + struct ceph_msg *msg = element; + + dout("msgpool_release %s %p\n", pool->name, msg); + msg->pool = NULL; + ceph_msg_put(msg); } int ceph_msgpool_init(struct ceph_msgpool *pool, int front_len, int size, bool blocking, const char *name) { + dout("msgpool %s init\n", name); pool->front_len = front_len; - pool->pool = mempool_create(size, alloc_fn, free_fn, pool); + pool->pool = mempool_create(size, msgpool_alloc, msgpool_free, pool); if (!pool->pool) return -ENOMEM; pool->name = name; @@ -36,29 +46,37 @@ int ceph_msgpool_init(struct ceph_msgpool *pool, void ceph_msgpool_destroy(struct ceph_msgpool *pool) { + dout("msgpool %s destroy\n", pool->name); mempool_destroy(pool->pool); } struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *pool, int front_len) { + struct ceph_msg *msg; + if (front_len > pool->front_len) { - pr_err("msgpool_get pool %s need front %d, pool size is %d\n", + dout("msgpool_get %s need front %d, pool size is %d\n", pool->name, front_len, pool->front_len); WARN_ON(1); /* try to alloc a fresh message */ - return ceph_msg_new(0, front_len, GFP_NOFS); + return ceph_msg_new(0, front_len, GFP_NOFS, false); } - return mempool_alloc(pool->pool, GFP_NOFS); + msg = mempool_alloc(pool->pool, GFP_NOFS); + dout("msgpool_get %s %p\n", pool->name, msg); + return msg; } void ceph_msgpool_put(struct ceph_msgpool *pool, struct ceph_msg *msg) { + dout("msgpool_put %s %p\n", pool->name, msg); + /* reset msg front_len; user may have changed it */ msg->front.iov_len = pool->front_len; msg->hdr.front_len = cpu_to_le32(pool->front_len); kref_init(&msg->kref); /* retake single ref */ + mempool_free(msg, pool->pool); } diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 7330c27..2df98a6 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -217,6 +217,7 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, INIT_LIST_HEAD(&req->r_unsafe_item); INIT_LIST_HEAD(&req->r_linger_item); INIT_LIST_HEAD(&req->r_linger_osd); + INIT_LIST_HEAD(&req->r_req_lru_item); req->r_flags = flags; WARN_ON((flags & (CEPH_OSD_FLAG_READ|CEPH_OSD_FLAG_WRITE)) == 0); @@ -226,7 +227,7 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, msg = ceph_msgpool_get(&osdc->msgpool_op_reply, 0); else msg = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, - OSD_OPREPLY_FRONT_LEN, gfp_flags); + OSD_OPREPLY_FRONT_LEN, gfp_flags, true); if (!msg) { ceph_osdc_put_request(req); return NULL; @@ -243,13 +244,13 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, ceph_pagelist_init(req->r_trail); } /* create request message; allow space for oid */ - msg_size += 40; + msg_size += MAX_OBJ_NAME_SIZE; if (snapc) msg_size += sizeof(u64) * snapc->num_snaps; if (use_mempool) msg = ceph_msgpool_get(&osdc->msgpool_op, 0); else - msg = ceph_msg_new(CEPH_MSG_OSD_OP, msg_size, gfp_flags); + msg = ceph_msg_new(CEPH_MSG_OSD_OP, msg_size, gfp_flags, true); if (!msg) { ceph_osdc_put_request(req); return NULL; @@ -677,12 +678,34 @@ static void put_osd(struct ceph_osd *osd) */ static void __remove_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) { - dout("__remove_osd %p\n", osd); + dout("%s %p osd%d\n", __func__, osd, osd->o_osd); BUG_ON(!list_empty(&osd->o_requests)); - rb_erase(&osd->o_node, &osdc->osds); list_del_init(&osd->o_osd_lru); - ceph_con_close(&osd->o_con); - put_osd(osd); + rb_erase(&osd->o_node, &osdc->osds); + RB_CLEAR_NODE(&osd->o_node); +} + +static void remove_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) +{ + dout("%s %p osd%d\n", __func__, osd, osd->o_osd); + + if (!RB_EMPTY_NODE(&osd->o_node)) { + ceph_con_close(&osd->o_con); + __remove_osd(osdc, osd); + put_osd(osd); + } +} + +static void remove_all_osds(struct ceph_osd_client *osdc) +{ + dout("__remove_old_osds %p\n", osdc); + mutex_lock(&osdc->request_mutex); + while (!RB_EMPTY_ROOT(&osdc->osds)) { + struct ceph_osd *osd = rb_entry(rb_first(&osdc->osds), + struct ceph_osd, o_node); + remove_osd(osdc, osd); + } + mutex_unlock(&osdc->request_mutex); } static void __move_osd_to_lru(struct ceph_osd_client *osdc, @@ -701,16 +724,16 @@ static void __remove_osd_from_lru(struct ceph_osd *osd) list_del_init(&osd->o_osd_lru); } -static void remove_old_osds(struct ceph_osd_client *osdc, int remove_all) +static void remove_old_osds(struct ceph_osd_client *osdc) { struct ceph_osd *osd, *nosd; dout("__remove_old_osds %p\n", osdc); mutex_lock(&osdc->request_mutex); list_for_each_entry_safe(osd, nosd, &osdc->osd_lru, o_osd_lru) { - if (!remove_all && time_before(jiffies, osd->lru_ttl)) + if (time_before(jiffies, osd->lru_ttl)) break; - __remove_osd(osdc, osd); + remove_osd(osdc, osd); } mutex_unlock(&osdc->request_mutex); } @@ -726,7 +749,7 @@ static int __reset_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) dout("__reset_osd %p osd%d\n", osd, osd->o_osd); if (list_empty(&osd->o_requests) && list_empty(&osd->o_linger_requests)) { - __remove_osd(osdc, osd); + remove_osd(osdc, osd); } else if (memcmp(&osdc->osdmap->osd_addr[osd->o_osd], &osd->o_con.peer_addr, sizeof(osd->o_con.peer_addr)) == 0 && @@ -751,6 +774,7 @@ static void __insert_osd(struct ceph_osd_client *osdc, struct ceph_osd *new) struct rb_node *parent = NULL; struct ceph_osd *osd = NULL; + dout("__insert_osd %p osd%d\n", new, new->o_osd); while (*p) { parent = *p; osd = rb_entry(parent, struct ceph_osd, o_node); @@ -803,13 +827,10 @@ static void __register_request(struct ceph_osd_client *osdc, { req->r_tid = ++osdc->last_tid; req->r_request->hdr.tid = cpu_to_le64(req->r_tid); - INIT_LIST_HEAD(&req->r_req_lru_item); - dout("__register_request %p tid %lld\n", req, req->r_tid); __insert_request(osdc, req); ceph_osdc_get_request(req); osdc->num_requests++; - if (osdc->num_requests == 1) { dout(" first request, scheduling timeout\n"); __schedule_osd_timeout(osdc); @@ -932,7 +953,7 @@ EXPORT_SYMBOL(ceph_osdc_set_request_linger); * Caller should hold map_sem for read and request_mutex. */ static int __map_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req) + struct ceph_osd_request *req, int force_resend) { struct ceph_osd_request_head *reqhead = req->r_request->front.iov_base; struct ceph_pg pgid; @@ -956,7 +977,8 @@ static int __map_request(struct ceph_osd_client *osdc, num = err; } - if ((req->r_osd && req->r_osd->o_osd == o && + if ((!force_resend && + req->r_osd && req->r_osd->o_osd == o && req->r_sent >= req->r_osd->o_incarnation && req->r_num_pg_osds == num && memcmp(req->r_pg_osds, acting, sizeof(acting[0])*num) == 0) || @@ -1085,9 +1107,15 @@ static void handle_timeout(struct work_struct *work) req = list_entry(osdc->req_lru.next, struct ceph_osd_request, r_req_lru_item); + /* hasn't been long enough since we sent it? */ if (time_before(jiffies, req->r_stamp + timeout)) break; + /* hasn't been long enough since it was acked? */ + if (req->r_request->ack_stamp == 0 || + time_before(jiffies, req->r_request->ack_stamp + timeout)) + break; + BUG_ON(req == last_req && req->r_stamp == last_stamp); last_req = req; last_stamp = req->r_stamp; @@ -1138,7 +1166,7 @@ static void handle_osds_timeout(struct work_struct *work) dout("osds timeout\n"); down_read(&osdc->map_sem); - remove_old_osds(osdc, 0); + remove_old_osds(osdc); up_read(&osdc->map_sem); schedule_delayed_work(&osdc->osds_timeout_work, @@ -1253,6 +1281,7 @@ static void reset_changed_osds(struct ceph_osd_client *osdc) { struct rb_node *p, *n; + dout("%s %p\n", __func__, osdc); for (p = rb_first(&osdc->osds); p; p = n) { struct ceph_osd *osd = rb_entry(p, struct ceph_osd, o_node); @@ -1272,18 +1301,18 @@ static void reset_changed_osds(struct ceph_osd_client *osdc) * * Caller should hold map_sem for read and request_mutex. */ -static void kick_requests(struct ceph_osd_client *osdc) +static void kick_requests(struct ceph_osd_client *osdc, int force_resend) { struct ceph_osd_request *req, *nreq; struct rb_node *p; int needmap = 0; int err; - dout("kick_requests\n"); + dout("kick_requests %s\n", force_resend ? " (force resend)" : ""); mutex_lock(&osdc->request_mutex); for (p = rb_first(&osdc->requests); p; p = rb_next(p)) { req = rb_entry(p, struct ceph_osd_request, r_node); - err = __map_request(osdc, req); + err = __map_request(osdc, req, force_resend); if (err < 0) continue; /* error */ if (req->r_osd == NULL) { @@ -1301,7 +1330,7 @@ static void kick_requests(struct ceph_osd_client *osdc) r_linger_item) { dout("linger req=%p req->r_osd=%p\n", req, req->r_osd); - err = __map_request(osdc, req); + err = __map_request(osdc, req, force_resend); if (err == 0) continue; /* no change and no osd was specified */ if (err < 0) @@ -1378,7 +1407,7 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) ceph_osdmap_destroy(osdc->osdmap); osdc->osdmap = newmap; } - kick_requests(osdc); + kick_requests(osdc, 0); reset_changed_osds(osdc); } else { dout("ignoring incremental map %u len %d\n", @@ -1406,6 +1435,8 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) "older than our %u\n", epoch, maplen, osdc->osdmap->epoch); } else { + int skipped_map = 0; + dout("taking full map %u len %d\n", epoch, maplen); newmap = osdmap_decode(&p, p+maplen); if (IS_ERR(newmap)) { @@ -1415,9 +1446,12 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) BUG_ON(!newmap); oldmap = osdc->osdmap; osdc->osdmap = newmap; - if (oldmap) + if (oldmap) { + if (oldmap->epoch + 1 < newmap->epoch) + skipped_map = 1; ceph_osdmap_destroy(oldmap); - kick_requests(osdc); + } + kick_requests(osdc, skipped_map); } p += maplen; nr_maps--; @@ -1690,12 +1724,14 @@ int ceph_osdc_start_request(struct ceph_osd_client *osdc, * the request still han't been touched yet. */ if (req->r_sent == 0) { - rc = __map_request(osdc, req); + rc = __map_request(osdc, req, 0); if (rc < 0) { if (nofail) { dout("osdc_start_request failed map, " " will retry %lld\n", req->r_tid); rc = 0; + } else { + __unregister_request(osdc, req); } goto out_unlock; } @@ -1856,8 +1892,7 @@ void ceph_osdc_stop(struct ceph_osd_client *osdc) ceph_osdmap_destroy(osdc->osdmap); osdc->osdmap = NULL; } - remove_old_osds(osdc, 1); - WARN_ON(!RB_EMPTY_ROOT(&osdc->osds)); + remove_all_osds(osdc); mempool_destroy(osdc->req_mempool); ceph_msgpool_destroy(&osdc->msgpool_op); ceph_msgpool_destroy(&osdc->msgpool_op_reply); @@ -2016,7 +2051,7 @@ static struct ceph_msg *get_reply(struct ceph_connection *con, if (front > req->r_reply->front.iov_len) { pr_warning("get_reply front %d > preallocated %d\n", front, (int)req->r_reply->front.iov_len); - m = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, front, GFP_NOFS); + m = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, front, GFP_NOFS, false); if (!m) goto out; ceph_msg_put(req->r_reply); @@ -2064,7 +2099,7 @@ static struct ceph_msg *alloc_msg(struct ceph_connection *con, switch (type) { case CEPH_MSG_OSD_MAP: case CEPH_MSG_WATCH_NOTIFY: - return ceph_msg_new(type, front, GFP_NOFS); + return ceph_msg_new(type, front, GFP_NOFS, false); case CEPH_MSG_OSD_OPREPLY: return get_reply(con, hdr, skip); default: diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index e97c358..bb38a3c 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -102,7 +102,7 @@ static int crush_decode_tree_bucket(void **p, void *end, { int j; dout("crush_decode_tree_bucket %p to %p\n", *p, end); - ceph_decode_32_safe(p, end, b->num_nodes, bad); + ceph_decode_8_safe(p, end, b->num_nodes, bad); b->node_weights = kcalloc(b->num_nodes, sizeof(u32), GFP_NOFS); if (b->node_weights == NULL) return -ENOMEM; @@ -339,6 +339,7 @@ static int __insert_pg_mapping(struct ceph_pg_mapping *new, struct ceph_pg_mapping *pg = NULL; int c; + dout("__insert_pg_mapping %llx %p\n", *(u64 *)&new->pgid, new); while (*p) { parent = *p; pg = rb_entry(parent, struct ceph_pg_mapping, node); @@ -366,16 +367,33 @@ static struct ceph_pg_mapping *__lookup_pg_mapping(struct rb_root *root, while (n) { pg = rb_entry(n, struct ceph_pg_mapping, node); c = pgid_cmp(pgid, pg->pgid); - if (c < 0) + if (c < 0) { n = n->rb_left; - else if (c > 0) + } else if (c > 0) { n = n->rb_right; - else + } else { + dout("__lookup_pg_mapping %llx got %p\n", + *(u64 *)&pgid, pg); return pg; + } } return NULL; } +static int __remove_pg_mapping(struct rb_root *root, struct ceph_pg pgid) +{ + struct ceph_pg_mapping *pg = __lookup_pg_mapping(root, pgid); + + if (pg) { + dout("__remove_pg_mapping %llx %p\n", *(u64 *)&pgid, pg); + rb_erase(&pg->node, root); + kfree(pg); + return 0; + } + dout("__remove_pg_mapping %llx dne\n", *(u64 *)&pgid); + return -ENOENT; +} + /* * rbtree of pg pool info */ @@ -711,7 +729,6 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, void *start = *p; int err = -EINVAL; u16 version; - struct rb_node *rbp; ceph_decode_16_safe(p, end, version, bad); if (version > CEPH_OSDMAP_INC_VERSION) { @@ -861,7 +878,6 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, } /* new_pg_temp */ - rbp = rb_first(&map->pg_temp); ceph_decode_32_safe(p, end, len, bad); while (len--) { struct ceph_pg_mapping *pg; @@ -872,18 +888,6 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, ceph_decode_copy(p, &pgid, sizeof(pgid)); pglen = ceph_decode_32(p); - /* remove any? */ - while (rbp && pgid_cmp(rb_entry(rbp, struct ceph_pg_mapping, - node)->pgid, pgid) <= 0) { - struct ceph_pg_mapping *cur = - rb_entry(rbp, struct ceph_pg_mapping, node); - - rbp = rb_next(rbp); - dout(" removed pg_temp %llx\n", *(u64 *)&cur->pgid); - rb_erase(&cur->node, &map->pg_temp); - kfree(cur); - } - if (pglen) { /* insert */ ceph_decode_need(p, end, pglen*sizeof(u32), bad); @@ -903,17 +907,11 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, } dout(" added pg_temp %llx len %d\n", *(u64 *)&pgid, pglen); + } else { + /* remove */ + __remove_pg_mapping(&map->pg_temp, pgid); } } - while (rbp) { - struct ceph_pg_mapping *cur = - rb_entry(rbp, struct ceph_pg_mapping, node); - - rbp = rb_next(rbp); - dout(" removed pg_temp %llx\n", *(u64 *)&cur->pgid); - rb_erase(&cur->node, &map->pg_temp); - kfree(cur); - } /* ignore the rest */ *p = end; @@ -1046,10 +1044,25 @@ static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid, struct ceph_pg_mapping *pg; struct ceph_pg_pool_info *pool; int ruleno; - unsigned poolid, ps, pps; + unsigned poolid, ps, pps, t; int preferred; + poolid = le32_to_cpu(pgid.pool); + ps = le16_to_cpu(pgid.ps); + preferred = (s16)le16_to_cpu(pgid.preferred); + + pool = __lookup_pg_pool(&osdmap->pg_pools, poolid); + if (!pool) + return NULL; + /* pg_temp? */ + if (preferred >= 0) + t = ceph_stable_mod(ps, le32_to_cpu(pool->v.lpg_num), + pool->lpgp_num_mask); + else + t = ceph_stable_mod(ps, le32_to_cpu(pool->v.pg_num), + pool->pgp_num_mask); + pgid.ps = cpu_to_le16(t); pg = __lookup_pg_mapping(&osdmap->pg_temp, pgid); if (pg) { *num = pg->len; @@ -1057,18 +1070,6 @@ static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid, } /* crush */ - poolid = le32_to_cpu(pgid.pool); - ps = le16_to_cpu(pgid.ps); - preferred = (s16)le16_to_cpu(pgid.preferred); - - /* don't forcefeed bad device ids to crush */ - if (preferred >= osdmap->max_osd || - preferred >= osdmap->crush->max_devices) - preferred = -1; - - pool = __lookup_pg_pool(&osdmap->pg_pools, poolid); - if (!pool) - return NULL; ruleno = crush_find_rule(osdmap->crush, pool->v.crush_ruleset, pool->v.type, pool->v.size); if (ruleno < 0) { @@ -1078,6 +1079,11 @@ static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid, return NULL; } + /* don't forcefeed bad device ids to crush */ + if (preferred >= osdmap->max_osd || + preferred >= osdmap->crush->max_devices) + preferred = -1; + if (preferred >= 0) pps = ceph_stable_mod(ps, le32_to_cpu(pool->v.lpgp_num), diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index 9680226..8c06a50 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -1835,8 +1835,6 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock, if (skb->tstamp.tv64) sk->sk_stamp = skb->tstamp; - msg->msg_namelen = sizeof(*sipx); - if (sipx) { sipx->sipx_family = AF_IPX; sipx->sipx_port = ipx->ipx_source.sock; @@ -1844,6 +1842,7 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock, sipx->sipx_network = IPX_SKB_CB(skb)->ipx_source_net; sipx->sipx_type = ipx->ipx_type; sipx->sipx_zero = 0; + msg->msg_namelen = sizeof(*sipx); } rc = copied; diff --git a/net/ipx/ipx_proc.c b/net/ipx/ipx_proc.c index 26b5bfc..f8ba30d 100644 --- a/net/ipx/ipx_proc.c +++ b/net/ipx/ipx_proc.c @@ -9,6 +9,7 @@ #include <linux/proc_fs.h> #include <linux/spinlock.h> #include <linux/seq_file.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/tcp_states.h> #include <net/ipx.h> diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index 402af94..f5d011a 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -369,7 +369,7 @@ static void irda_getvalue_confirm(int result, __u16 obj_id, { struct irda_sock *self; - self = (struct irda_sock *) priv; + self = priv; if (!self) { IRDA_WARNING("%s: lost myself!\n", __func__); return; @@ -418,7 +418,7 @@ static void irda_selective_discovery_indication(discinfo_t *discovery, IRDA_DEBUG(2, "%s()\n", __func__); - self = (struct irda_sock *) priv; + self = priv; if (!self) { IRDA_WARNING("%s: lost myself!\n", __func__); return; @@ -1386,8 +1386,6 @@ static int irda_recvmsg_dgram(struct kiocb *iocb, struct socket *sock, IRDA_DEBUG(4, "%s()\n", __func__); - msg->msg_namelen = 0; - skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err); if (!skb) @@ -1452,8 +1450,6 @@ static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock, target = sock_rcvlowat(sk, flags & MSG_WAITALL, size); timeo = sock_rcvtimeo(sk, noblock); - msg->msg_namelen = 0; - do { int chunk; struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue); diff --git a/net/irda/discovery.c b/net/irda/discovery.c index 36c3f03..b0b56a3 100644 --- a/net/irda/discovery.c +++ b/net/irda/discovery.c @@ -35,6 +35,7 @@ #include <linux/fs.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/irda/irda.h> #include <net/irda/irlmp.h> diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index b3cc8b3..cf368dd 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -551,7 +551,7 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) */ tty->closing = 1; if (self->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, self->closing_wait); + tty_wait_until_sent_from_close(tty, self->closing_wait); ircomm_tty_shutdown(self); @@ -848,7 +848,9 @@ static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout) orig_jiffies = jiffies; /* Set poll time to 200 ms */ - poll_time = IRDA_MIN(timeout, msecs_to_jiffies(200)); + poll_time = msecs_to_jiffies(200); + if (timeout) + poll_time = min_t(unsigned long, timeout, poll_time); spin_lock_irqsave(&self->spinlock, flags); while (self->tx_skb && self->tx_skb->len) { diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c index 3c17540..b65d66e 100644 --- a/net/irda/ircomm/ircomm_tty_attach.c +++ b/net/irda/ircomm/ircomm_tty_attach.c @@ -382,7 +382,7 @@ static void ircomm_tty_discovery_indication(discinfo_t *discovery, info.daddr = discovery->daddr; info.saddr = discovery->saddr; - self = (struct ircomm_tty_cb *) priv; + self = priv; ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION, NULL, &info); } diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c index 25cc2e6..14653b8 100644 --- a/net/irda/irda_device.c +++ b/net/irda/irda_device.c @@ -42,6 +42,7 @@ #include <linux/kmod.h> #include <linux/spinlock.h> #include <linux/slab.h> +#include <linux/export.h> #include <asm/ioctls.h> #include <asm/uaccess.h> @@ -262,7 +263,7 @@ static void irda_task_timer_expired(void *data) IRDA_DEBUG(2, "%s()\n", __func__); - task = (struct irda_task *) data; + task = data; irda_task_kick(task); } diff --git a/net/irda/iriap.c b/net/irda/iriap.c index f876eed..e71e85b 100644 --- a/net/irda/iriap.c +++ b/net/irda/iriap.c @@ -305,7 +305,7 @@ static void iriap_disconnect_indication(void *instance, void *sap, IRDA_DEBUG(4, "%s(), reason=%s\n", __func__, irlmp_reasons[reason]); - self = (struct iriap_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IAS_MAGIC, return;); @@ -759,7 +759,7 @@ static void iriap_connect_confirm(void *instance, void *sap, { struct iriap_cb *self; - self = (struct iriap_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IAS_MAGIC, return;); @@ -791,7 +791,7 @@ static void iriap_connect_indication(void *instance, void *sap, IRDA_DEBUG(1, "%s()\n", __func__); - self = (struct iriap_cb *) instance; + self = instance; IRDA_ASSERT(skb != NULL, return;); IRDA_ASSERT(self != NULL, goto out;); @@ -839,7 +839,7 @@ static int iriap_data_indication(void *instance, void *sap, IRDA_DEBUG(3, "%s()\n", __func__); - self = (struct iriap_cb *) instance; + self = instance; IRDA_ASSERT(skb != NULL, return 0;); IRDA_ASSERT(self != NULL, goto out;); diff --git a/net/irda/irlan/irlan_client.c b/net/irda/irlan/irlan_client.c index 7ed3af9..ba1a3fc 100644 --- a/net/irda/irlan/irlan_client.c +++ b/net/irda/irlan/irlan_client.c @@ -198,7 +198,7 @@ static int irlan_client_ctrl_data_indication(void *instance, void *sap, IRDA_DEBUG(2, "%s()\n", __func__ ); - self = (struct irlan_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); @@ -226,8 +226,8 @@ static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap, IRDA_DEBUG(4, "%s(), reason=%d\n", __func__ , reason); - self = (struct irlan_cb *) instance; - tsap = (struct tsap_cb *) sap; + self = instance; + tsap = sap; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -298,7 +298,7 @@ static void irlan_client_ctrl_connect_confirm(void *instance, void *sap, IRDA_DEBUG(4, "%s()\n", __func__ ); - self = (struct irlan_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -542,7 +542,7 @@ void irlan_client_get_value_confirm(int result, __u16 obj_id, IRDA_ASSERT(priv != NULL, return;); - self = (struct irlan_cb *) priv; + self = priv; IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); /* We probably don't need to make any more queries */ diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c index 6130f9d..7791176 100644 --- a/net/irda/irlan/irlan_common.c +++ b/net/irda/irlan/irlan_common.c @@ -317,8 +317,8 @@ static void irlan_connect_indication(void *instance, void *sap, IRDA_DEBUG(2, "%s()\n", __func__ ); - self = (struct irlan_cb *) instance; - tsap = (struct tsap_cb *) sap; + self = instance; + tsap = sap; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -361,7 +361,7 @@ static void irlan_connect_confirm(void *instance, void *sap, { struct irlan_cb *self; - self = (struct irlan_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -406,8 +406,8 @@ static void irlan_disconnect_indication(void *instance, IRDA_DEBUG(0, "%s(), reason=%d\n", __func__ , reason); - self = (struct irlan_cb *) instance; - tsap = (struct tsap_cb *) sap; + self = instance; + tsap = sap; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index 8ee1ff6..d14152e 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -50,7 +50,7 @@ static const struct net_device_ops irlan_eth_netdev_ops = { .ndo_open = irlan_eth_open, .ndo_stop = irlan_eth_close, .ndo_start_xmit = irlan_eth_xmit, - .ndo_set_multicast_list = irlan_eth_set_multicast_list, + .ndo_set_rx_mode = irlan_eth_set_multicast_list, .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; @@ -272,7 +272,7 @@ void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) struct irlan_cb *self; struct net_device *dev; - self = (struct irlan_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); diff --git a/net/irda/irlan/irlan_provider.c b/net/irda/irlan/irlan_provider.c index b8af74a..8b61cf0 100644 --- a/net/irda/irlan/irlan_provider.c +++ b/net/irda/irlan/irlan_provider.c @@ -73,7 +73,7 @@ static int irlan_provider_data_indication(void *instance, void *sap, IRDA_DEBUG(4, "%s()\n", __func__ ); - self = (struct irlan_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); @@ -131,8 +131,8 @@ static void irlan_provider_connect_indication(void *instance, void *sap, IRDA_DEBUG(0, "%s()\n", __func__ ); - self = (struct irlan_cb *) instance; - tsap = (struct tsap_cb *) sap; + self = instance; + tsap = sap; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -182,8 +182,8 @@ static void irlan_provider_disconnect_indication(void *instance, void *sap, IRDA_DEBUG(4, "%s(), reason=%d\n", __func__ , reason); - self = (struct irlan_cb *) instance; - tsap = (struct tsap_cb *) sap; + self = instance; + tsap = sap; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); diff --git a/net/irda/irqueue.c b/net/irda/irqueue.c index 9715e6e..f06947c 100644 --- a/net/irda/irqueue.c +++ b/net/irda/irqueue.c @@ -780,7 +780,7 @@ void* hashbin_lock_find( hashbin_t* hashbin, long hashv, const char* name ) /* * Search for entry */ - entry = (irda_queue_t* ) hashbin_find( hashbin, hashv, name ); + entry = hashbin_find(hashbin, hashv, name); /* Release lock */ spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); @@ -813,7 +813,7 @@ void* hashbin_find_next( hashbin_t* hashbin, long hashv, const char* name, * This allow to check if the current item is still in the * hashbin or has been removed. */ - entry = (irda_queue_t* ) hashbin_find( hashbin, hashv, name ); + entry = hashbin_find(hashbin, hashv, name); /* * Trick hashbin_get_next() to return what we want diff --git a/net/irda/irsysctl.c b/net/irda/irsysctl.c index d0b70da..2615ffc 100644 --- a/net/irda/irsysctl.c +++ b/net/irda/irsysctl.c @@ -40,9 +40,9 @@ extern int sysctl_slot_timeout; extern int sysctl_fast_poll_increase; extern char sysctl_devname[]; extern int sysctl_max_baud_rate; -extern int sysctl_min_tx_turn_time; -extern int sysctl_max_tx_data_size; -extern int sysctl_max_tx_window; +extern unsigned int sysctl_min_tx_turn_time; +extern unsigned int sysctl_max_tx_data_size; +extern unsigned int sysctl_max_tx_window; extern int sysctl_max_noreply_time; extern int sysctl_warn_noreply_time; extern int sysctl_lap_keepalive_time; diff --git a/net/irda/irttp.c b/net/irda/irttp.c index 9d9af46..32e3bb0 100644 --- a/net/irda/irttp.c +++ b/net/irda/irttp.c @@ -29,6 +29,7 @@ #include <linux/fs.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/export.h> #include <asm/byteorder.h> #include <asm/unaligned.h> @@ -350,7 +351,7 @@ static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, { struct tsap_cb *self; - self = (struct tsap_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); @@ -879,7 +880,7 @@ static int irttp_udata_indication(void *instance, void *sap, IRDA_DEBUG(4, "%s()\n", __func__); - self = (struct tsap_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); @@ -914,7 +915,7 @@ static int irttp_data_indication(void *instance, void *sap, unsigned long flags; int n; - self = (struct tsap_cb *) instance; + self = instance; n = skb->data[0] & 0x7f; /* Extract the credits */ @@ -996,7 +997,7 @@ static void irttp_status_indication(void *instance, IRDA_DEBUG(4, "%s()\n", __func__); - self = (struct tsap_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); @@ -1025,7 +1026,7 @@ static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) { struct tsap_cb *self; - self = (struct tsap_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); @@ -1208,7 +1209,7 @@ static void irttp_connect_confirm(void *instance, void *sap, IRDA_DEBUG(4, "%s()\n", __func__); - self = (struct tsap_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); @@ -1292,13 +1293,13 @@ static void irttp_connect_indication(void *instance, void *sap, __u8 plen; __u8 n; - self = (struct tsap_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); - lsap = (struct lsap_cb *) sap; + lsap = sap; self->max_seg_size = max_seg_size - TTP_HEADER; self->max_header_size = max_header_size+TTP_HEADER; @@ -1602,7 +1603,7 @@ static void irttp_disconnect_indication(void *instance, void *sap, IRDA_DEBUG(4, "%s()\n", __func__); - self = (struct tsap_cb *) instance; + self = instance; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); diff --git a/net/irda/qos.c b/net/irda/qos.c index 1b51bcf..798ffd9 100644 --- a/net/irda/qos.c +++ b/net/irda/qos.c @@ -30,6 +30,8 @@ * ********************************************************************/ +#include <linux/export.h> + #include <asm/byteorder.h> #include <net/irda/irda.h> @@ -60,7 +62,7 @@ int sysctl_max_noreply_time = 12; * Default is 10us which means using the unmodified value given by the * peer except if it's 0 (0 is likely a bug in the other stack). */ -unsigned sysctl_min_tx_turn_time = 10; +unsigned int sysctl_min_tx_turn_time = 10; /* * Maximum data size to be used in transmission in payload of LAP frame. * There is a bit of confusion in the IrDA spec : @@ -75,13 +77,13 @@ unsigned sysctl_min_tx_turn_time = 10; * bytes frames or all negotiated frame sizes, but you can use the sysctl * to play with this value anyway. * Jean II */ -unsigned sysctl_max_tx_data_size = 2042; +unsigned int sysctl_max_tx_data_size = 2042; /* * Maximum transmit window, i.e. number of LAP frames between turn-around. * This allow to override what the peer told us. Some peers are buggy and * don't always support what they tell us. * Jean II */ -unsigned sysctl_max_tx_window = 7; +unsigned int sysctl_max_tx_window = 7; static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get); static int irlap_param_link_disconnect(void *instance, irda_param_t *parm, diff --git a/net/iucv/Kconfig b/net/iucv/Kconfig index 16ce9cd..497fbe7 100644 --- a/net/iucv/Kconfig +++ b/net/iucv/Kconfig @@ -1,15 +1,17 @@ config IUCV - tristate "IUCV support (S390 - z/VM only)" depends on S390 + def_tristate y if S390 + prompt "IUCV support (S390 - z/VM only)" help Select this option if you want to use inter-user communication under VM or VIF. If you run on z/VM, say "Y" to enable a fast communication link between VM guests. config AFIUCV - tristate "AF_IUCV support (S390 - z/VM only)" - depends on IUCV + depends on S390 + def_tristate m if QETH_L3 || IUCV + prompt "AF_IUCV Socket support (S390 - z/VM and HiperSockets transport)" help - Select this option if you want to use inter-user communication under - VM or VIF sockets. If you run on z/VM, say "Y" to enable a fast - communication link between VM guests. + Select this option if you want to use AF_IUCV socket applications + based on z/VM inter-user communication vehicle or based on + HiperSockets. diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 794601e..cf98d62 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -27,10 +27,9 @@ #include <asm/cpcmd.h> #include <linux/kmod.h> -#include <net/iucv/iucv.h> #include <net/iucv/af_iucv.h> -#define VERSION "1.1" +#define VERSION "1.2" static char iucv_userid[80]; @@ -42,6 +41,8 @@ static struct proto iucv_proto = { .obj_size = sizeof(struct iucv_sock), }; +static struct iucv_interface *pr_iucv; + /* special AF_IUCV IPRM messages */ static const u8 iprm_shutdown[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; @@ -90,6 +91,12 @@ do { \ static void iucv_sock_kill(struct sock *sk); static void iucv_sock_close(struct sock *sk); +static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev); +static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock, + struct sk_buff *skb, u8 flags); +static void afiucv_hs_callback_txnotify(struct sk_buff *, enum iucv_tx_notify); + /* Call Back functions */ static void iucv_callback_rx(struct iucv_path *, struct iucv_message *); static void iucv_callback_txdone(struct iucv_path *, struct iucv_message *); @@ -165,7 +172,7 @@ static int afiucv_pm_freeze(struct device *dev) case IUCV_CLOSING: case IUCV_CONNECTED: if (iucv->path) { - err = iucv_path_sever(iucv->path, NULL); + err = pr_iucv->path_sever(iucv->path, NULL); iucv_path_free(iucv->path); iucv->path = NULL; } @@ -229,7 +236,7 @@ static const struct dev_pm_ops afiucv_pm_ops = { static struct device_driver af_iucv_driver = { .owner = THIS_MODULE, .name = "afiucv", - .bus = &iucv_bus, + .bus = NULL, .pm = &afiucv_pm_ops, }; @@ -294,7 +301,11 @@ static inline int iucv_below_msglim(struct sock *sk) if (sk->sk_state != IUCV_CONNECTED) return 1; - return (skb_queue_len(&iucv->send_skb_q) < iucv->path->msglim); + if (iucv->transport == AF_IUCV_TRANS_IUCV) + return (skb_queue_len(&iucv->send_skb_q) < iucv->path->msglim); + else + return ((atomic_read(&iucv->msg_sent) < iucv->msglimit_peer) && + (atomic_read(&iucv->pendings) <= 0)); } /** @@ -312,6 +323,78 @@ static void iucv_sock_wake_msglim(struct sock *sk) rcu_read_unlock(); } +/** + * afiucv_hs_send() - send a message through HiperSockets transport + */ +static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock, + struct sk_buff *skb, u8 flags) +{ + struct net *net = sock_net(sock); + struct iucv_sock *iucv = iucv_sk(sock); + struct af_iucv_trans_hdr *phs_hdr; + struct sk_buff *nskb; + int err, confirm_recv = 0; + + memset(skb->head, 0, ETH_HLEN); + phs_hdr = (struct af_iucv_trans_hdr *)skb_push(skb, + sizeof(struct af_iucv_trans_hdr)); + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_push(skb, ETH_HLEN); + skb_reset_mac_header(skb); + memset(phs_hdr, 0, sizeof(struct af_iucv_trans_hdr)); + + phs_hdr->magic = ETH_P_AF_IUCV; + phs_hdr->version = 1; + phs_hdr->flags = flags; + if (flags == AF_IUCV_FLAG_SYN) + phs_hdr->window = iucv->msglimit; + else if ((flags == AF_IUCV_FLAG_WIN) || !flags) { + confirm_recv = atomic_read(&iucv->msg_recv); + phs_hdr->window = confirm_recv; + if (confirm_recv) + phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN; + } + memcpy(phs_hdr->destUserID, iucv->dst_user_id, 8); + memcpy(phs_hdr->destAppName, iucv->dst_name, 8); + memcpy(phs_hdr->srcUserID, iucv->src_user_id, 8); + memcpy(phs_hdr->srcAppName, iucv->src_name, 8); + ASCEBC(phs_hdr->destUserID, sizeof(phs_hdr->destUserID)); + ASCEBC(phs_hdr->destAppName, sizeof(phs_hdr->destAppName)); + ASCEBC(phs_hdr->srcUserID, sizeof(phs_hdr->srcUserID)); + ASCEBC(phs_hdr->srcAppName, sizeof(phs_hdr->srcAppName)); + if (imsg) + memcpy(&phs_hdr->iucv_hdr, imsg, sizeof(struct iucv_message)); + + rcu_read_lock(); + skb->dev = dev_get_by_index_rcu(net, sock->sk_bound_dev_if); + rcu_read_unlock(); + if (!skb->dev) + return -ENODEV; + if (!(skb->dev->flags & IFF_UP)) + return -ENETDOWN; + if (skb->len > skb->dev->mtu) { + if (sock->sk_type == SOCK_SEQPACKET) + return -EMSGSIZE; + else + skb_trim(skb, skb->dev->mtu); + } + skb->protocol = ETH_P_AF_IUCV; + nskb = skb_clone(skb, GFP_ATOMIC); + if (!nskb) + return -ENOMEM; + skb_queue_tail(&iucv->send_skb_q, nskb); + err = dev_queue_xmit(skb); + if (err) { + skb_unlink(nskb, &iucv->send_skb_q); + kfree_skb(nskb); + } else { + atomic_sub(confirm_recv, &iucv->msg_recv); + WARN_ON(atomic_read(&iucv->msg_recv) < 0); + } + return err; +} + /* Timers */ static void iucv_sock_timeout(unsigned long arg) { @@ -380,6 +463,8 @@ static void iucv_sock_close(struct sock *sk) unsigned char user_data[16]; struct iucv_sock *iucv = iucv_sk(sk); unsigned long timeo; + int err, blen; + struct sk_buff *skb; iucv_sock_clear_timer(sk); lock_sock(sk); @@ -390,6 +475,20 @@ static void iucv_sock_close(struct sock *sk) break; case IUCV_CONNECTED: + if (iucv->transport == AF_IUCV_TRANS_HIPER) { + /* send fin */ + blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN; + skb = sock_alloc_send_skb(sk, blen, 1, &err); + if (skb) { + skb_reserve(skb, + sizeof(struct af_iucv_trans_hdr) + + ETH_HLEN); + err = afiucv_hs_send(NULL, sk, skb, + AF_IUCV_FLAG_FIN); + } + sk->sk_state = IUCV_DISCONN; + sk->sk_state_change(sk); + } case IUCV_DISCONN: sk->sk_state = IUCV_CLOSING; sk->sk_state_change(sk); @@ -412,7 +511,7 @@ static void iucv_sock_close(struct sock *sk) low_nmcpy(user_data, iucv->src_name); high_nmcpy(user_data, iucv->dst_name); ASCEBC(user_data, sizeof(user_data)); - iucv_path_sever(iucv->path, user_data); + pr_iucv->path_sever(iucv->path, user_data); iucv_path_free(iucv->path); iucv->path = NULL; } @@ -444,23 +543,33 @@ static void iucv_sock_init(struct sock *sk, struct sock *parent) static struct sock *iucv_sock_alloc(struct socket *sock, int proto, gfp_t prio) { struct sock *sk; + struct iucv_sock *iucv; sk = sk_alloc(&init_net, PF_IUCV, prio, &iucv_proto); if (!sk) return NULL; + iucv = iucv_sk(sk); sock_init_data(sock, sk); - INIT_LIST_HEAD(&iucv_sk(sk)->accept_q); - spin_lock_init(&iucv_sk(sk)->accept_q_lock); - skb_queue_head_init(&iucv_sk(sk)->send_skb_q); - INIT_LIST_HEAD(&iucv_sk(sk)->message_q.list); - spin_lock_init(&iucv_sk(sk)->message_q.lock); - skb_queue_head_init(&iucv_sk(sk)->backlog_skb_q); - iucv_sk(sk)->send_tag = 0; - iucv_sk(sk)->flags = 0; - iucv_sk(sk)->msglimit = IUCV_QUEUELEN_DEFAULT; - iucv_sk(sk)->path = NULL; - memset(&iucv_sk(sk)->src_user_id , 0, 32); + INIT_LIST_HEAD(&iucv->accept_q); + spin_lock_init(&iucv->accept_q_lock); + skb_queue_head_init(&iucv->send_skb_q); + INIT_LIST_HEAD(&iucv->message_q.list); + spin_lock_init(&iucv->message_q.lock); + skb_queue_head_init(&iucv->backlog_skb_q); + iucv->send_tag = 0; + atomic_set(&iucv->pendings, 0); + iucv->flags = 0; + iucv->msglimit = 0; + atomic_set(&iucv->msg_sent, 0); + atomic_set(&iucv->msg_recv, 0); + iucv->path = NULL; + iucv->sk_txnotify = afiucv_hs_callback_txnotify; + memset(&iucv->src_user_id , 0, 32); + if (pr_iucv) + iucv->transport = AF_IUCV_TRANS_IUCV; + else + iucv->transport = AF_IUCV_TRANS_HIPER; sk->sk_destruct = iucv_sock_destruct; sk->sk_sndtimeo = IUCV_CONN_TIMEOUT; @@ -591,7 +700,9 @@ static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr, struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr; struct sock *sk = sock->sk; struct iucv_sock *iucv; - int err; + int err = 0; + struct net_device *dev; + char uid[9]; /* Verify the input sockaddr */ if (!addr || addr->sa_family != AF_IUCV) @@ -610,19 +721,46 @@ static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr, err = -EADDRINUSE; goto done_unlock; } - if (iucv->path) { - err = 0; + if (iucv->path) goto done_unlock; - } /* Bind the socket */ - memcpy(iucv->src_name, sa->siucv_name, 8); - /* Copy the user id */ - memcpy(iucv->src_user_id, iucv_userid, 8); - sk->sk_state = IUCV_BOUND; - err = 0; + if (pr_iucv) + if (!memcmp(sa->siucv_user_id, iucv_userid, 8)) + goto vm_bind; /* VM IUCV transport */ + /* try hiper transport */ + memcpy(uid, sa->siucv_user_id, sizeof(uid)); + ASCEBC(uid, 8); + rcu_read_lock(); + for_each_netdev_rcu(&init_net, dev) { + if (!memcmp(dev->perm_addr, uid, 8)) { + memcpy(iucv->src_name, sa->siucv_name, 8); + memcpy(iucv->src_user_id, sa->siucv_user_id, 8); + sock->sk->sk_bound_dev_if = dev->ifindex; + sk->sk_state = IUCV_BOUND; + iucv->transport = AF_IUCV_TRANS_HIPER; + if (!iucv->msglimit) + iucv->msglimit = IUCV_HIPER_MSGLIM_DEFAULT; + rcu_read_unlock(); + goto done_unlock; + } + } + rcu_read_unlock(); +vm_bind: + if (pr_iucv) { + /* use local userid for backward compat */ + memcpy(iucv->src_name, sa->siucv_name, 8); + memcpy(iucv->src_user_id, iucv_userid, 8); + sk->sk_state = IUCV_BOUND; + iucv->transport = AF_IUCV_TRANS_IUCV; + if (!iucv->msglimit) + iucv->msglimit = IUCV_QUEUELEN_DEFAULT; + goto done_unlock; + } + /* found no dev to bind */ + err = -ENODEV; done_unlock: /* Release the socket list lock */ write_unlock_bh(&iucv_sk_list.lock); @@ -658,45 +796,44 @@ static int iucv_sock_autobind(struct sock *sk) memcpy(&iucv->src_name, name, 8); + if (!iucv->msglimit) + iucv->msglimit = IUCV_QUEUELEN_DEFAULT; + return err; } -/* Connect an unconnected socket */ -static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr, - int alen, int flags) +static int afiucv_hs_connect(struct socket *sock) { - struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr; struct sock *sk = sock->sk; - struct iucv_sock *iucv; - unsigned char user_data[16]; - int err; - - if (addr->sa_family != AF_IUCV || alen < sizeof(struct sockaddr_iucv)) - return -EINVAL; - - if (sk->sk_state != IUCV_OPEN && sk->sk_state != IUCV_BOUND) - return -EBADFD; - - if (sk->sk_type != SOCK_STREAM && sk->sk_type != SOCK_SEQPACKET) - return -EINVAL; + struct sk_buff *skb; + int blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN; + int err = 0; - if (sk->sk_state == IUCV_OPEN) { - err = iucv_sock_autobind(sk); - if (unlikely(err)) - return err; + /* send syn */ + skb = sock_alloc_send_skb(sk, blen, 1, &err); + if (!skb) { + err = -ENOMEM; + goto done; } + skb->dev = NULL; + skb_reserve(skb, blen); + err = afiucv_hs_send(NULL, sk, skb, AF_IUCV_FLAG_SYN); +done: + return err; +} - lock_sock(sk); - - /* Set the destination information */ - memcpy(iucv_sk(sk)->dst_user_id, sa->siucv_user_id, 8); - memcpy(iucv_sk(sk)->dst_name, sa->siucv_name, 8); +static int afiucv_path_connect(struct socket *sock, struct sockaddr *addr) +{ + struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr; + struct sock *sk = sock->sk; + struct iucv_sock *iucv = iucv_sk(sk); + unsigned char user_data[16]; + int err; high_nmcpy(user_data, sa->siucv_name); - low_nmcpy(user_data, iucv_sk(sk)->src_name); + low_nmcpy(user_data, iucv->src_name); ASCEBC(user_data, sizeof(user_data)); - iucv = iucv_sk(sk); /* Create path. */ iucv->path = iucv_path_alloc(iucv->msglimit, IUCV_IPRMDATA, GFP_KERNEL); @@ -704,8 +841,9 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr, err = -ENOMEM; goto done; } - err = iucv_path_connect(iucv->path, &af_iucv_handler, - sa->siucv_user_id, NULL, user_data, sk); + err = pr_iucv->path_connect(iucv->path, &af_iucv_handler, + sa->siucv_user_id, NULL, user_data, + sk); if (err) { iucv_path_free(iucv->path); iucv->path = NULL; @@ -724,21 +862,62 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr, err = -ECONNREFUSED; break; } - goto done; } +done: + return err; +} - if (sk->sk_state != IUCV_CONNECTED) { +/* Connect an unconnected socket */ +static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr, + int alen, int flags) +{ + struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr; + struct sock *sk = sock->sk; + struct iucv_sock *iucv = iucv_sk(sk); + int err; + + if (addr->sa_family != AF_IUCV || alen < sizeof(struct sockaddr_iucv)) + return -EINVAL; + + if (sk->sk_state != IUCV_OPEN && sk->sk_state != IUCV_BOUND) + return -EBADFD; + + if (sk->sk_state == IUCV_OPEN && + iucv->transport == AF_IUCV_TRANS_HIPER) + return -EBADFD; /* explicit bind required */ + + if (sk->sk_type != SOCK_STREAM && sk->sk_type != SOCK_SEQPACKET) + return -EINVAL; + + if (sk->sk_state == IUCV_OPEN) { + err = iucv_sock_autobind(sk); + if (unlikely(err)) + return err; + } + + lock_sock(sk); + + /* Set the destination information */ + memcpy(iucv->dst_user_id, sa->siucv_user_id, 8); + memcpy(iucv->dst_name, sa->siucv_name, 8); + + if (iucv->transport == AF_IUCV_TRANS_HIPER) + err = afiucv_hs_connect(sock); + else + err = afiucv_path_connect(sock, addr); + if (err) + goto done; + + if (sk->sk_state != IUCV_CONNECTED) err = iucv_sock_wait(sk, iucv_sock_in_state(sk, IUCV_CONNECTED, IUCV_DISCONN), sock_sndtimeo(sk, flags & O_NONBLOCK)); - } - if (sk->sk_state == IUCV_DISCONN) { + if (sk->sk_state == IUCV_DISCONN || sk->sk_state == IUCV_CLOSED) err = -ECONNREFUSED; - } - if (err) { - iucv_path_sever(iucv->path, NULL); + if (err && iucv->transport == AF_IUCV_TRANS_IUCV) { + pr_iucv->path_sever(iucv->path, NULL); iucv_path_free(iucv->path); iucv->path = NULL; } @@ -833,20 +1012,21 @@ static int iucv_sock_getname(struct socket *sock, struct sockaddr *addr, { struct sockaddr_iucv *siucv = (struct sockaddr_iucv *) addr; struct sock *sk = sock->sk; + struct iucv_sock *iucv = iucv_sk(sk); addr->sa_family = AF_IUCV; *len = sizeof(struct sockaddr_iucv); if (peer) { - memcpy(siucv->siucv_user_id, iucv_sk(sk)->dst_user_id, 8); - memcpy(siucv->siucv_name, &iucv_sk(sk)->dst_name, 8); + memcpy(siucv->siucv_user_id, iucv->dst_user_id, 8); + memcpy(siucv->siucv_name, iucv->dst_name, 8); } else { - memcpy(siucv->siucv_user_id, iucv_sk(sk)->src_user_id, 8); - memcpy(siucv->siucv_name, iucv_sk(sk)->src_name, 8); + memcpy(siucv->siucv_user_id, iucv->src_user_id, 8); + memcpy(siucv->siucv_name, iucv->src_name, 8); } memset(&siucv->siucv_port, 0, sizeof(siucv->siucv_port)); memset(&siucv->siucv_addr, 0, sizeof(siucv->siucv_addr)); - memset(siucv->siucv_nodeid, 0, sizeof(siucv->siucv_nodeid)); + memset(&siucv->siucv_nodeid, 0, sizeof(siucv->siucv_nodeid)); return 0; } @@ -871,7 +1051,7 @@ static int iucv_send_iprm(struct iucv_path *path, struct iucv_message *msg, memcpy(prmdata, (void *) skb->data, skb->len); prmdata[7] = 0xff - (u8) skb->len; - return iucv_message_send(path, msg, IUCV_IPRMDATA, 0, + return pr_iucv->message_send(path, msg, IUCV_IPRMDATA, 0, (void *) prmdata, 8); } @@ -960,9 +1140,16 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock, * this is fine for SOCK_SEQPACKET (unless we want to support * segmented records using the MSG_EOR flag), but * for SOCK_STREAM we might want to improve it in future */ - skb = sock_alloc_send_skb(sk, len, noblock, &err); + if (iucv->transport == AF_IUCV_TRANS_HIPER) + skb = sock_alloc_send_skb(sk, + len + sizeof(struct af_iucv_trans_hdr) + ETH_HLEN, + noblock, &err); + else + skb = sock_alloc_send_skb(sk, len, noblock, &err); if (!skb) goto out; + if (iucv->transport == AF_IUCV_TRANS_HIPER) + skb_reserve(skb, sizeof(struct af_iucv_trans_hdr) + ETH_HLEN); if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { err = -EFAULT; goto fail; @@ -983,6 +1170,15 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock, /* increment and save iucv message tag for msg_completion cbk */ txmsg.tag = iucv->send_tag++; memcpy(CB_TAG(skb), &txmsg.tag, CB_TAG_LEN); + if (iucv->transport == AF_IUCV_TRANS_HIPER) { + atomic_inc(&iucv->msg_sent); + err = afiucv_hs_send(&txmsg, sk, skb, 0); + if (err) { + atomic_dec(&iucv->msg_sent); + goto fail; + } + goto release; + } skb_queue_tail(&iucv->send_skb_q, skb); if (((iucv->path->flags & IUCV_IPRMDATA) & iucv->flags) @@ -999,13 +1195,13 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock, /* this error should never happen since the * IUCV_IPRMDATA path flag is set... sever path */ if (err == 0x15) { - iucv_path_sever(iucv->path, NULL); + pr_iucv->path_sever(iucv->path, NULL); skb_unlink(skb, &iucv->send_skb_q); err = -EPIPE; goto fail; } } else - err = iucv_message_send(iucv->path, &txmsg, 0, 0, + err = pr_iucv->message_send(iucv->path, &txmsg, 0, 0, (void *) skb->data, skb->len); if (err) { if (err == 3) { @@ -1023,6 +1219,7 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock, goto fail; } +release: release_sock(sk); return len; @@ -1095,8 +1292,9 @@ static void iucv_process_message(struct sock *sk, struct sk_buff *skb, skb->len = 0; } } else { - rc = iucv_message_receive(path, msg, msg->flags & IUCV_IPRMDATA, - skb->data, len, NULL); + rc = pr_iucv->message_receive(path, msg, + msg->flags & IUCV_IPRMDATA, + skb->data, len, NULL); if (rc) { kfree_skb(skb); return; @@ -1110,7 +1308,7 @@ static void iucv_process_message(struct sock *sk, struct sk_buff *skb, kfree_skb(skb); skb = NULL; if (rc) { - iucv_path_sever(path, NULL); + pr_iucv->path_sever(path, NULL); return; } skb = skb_dequeue(&iucv_sk(sk)->backlog_skb_q); @@ -1154,11 +1352,10 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct sock *sk = sock->sk; struct iucv_sock *iucv = iucv_sk(sk); unsigned int copied, rlen; - struct sk_buff *skb, *rskb, *cskb; + struct sk_buff *skb, *rskb, *cskb, *sskb; + int blen; int err = 0; - msg->msg_namelen = 0; - if ((sk->sk_state == IUCV_DISCONN || sk->sk_state == IUCV_SEVERED) && skb_queue_empty(&iucv->backlog_skb_q) && skb_queue_empty(&sk->sk_receive_queue) && @@ -1181,7 +1378,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock, copied = min_t(unsigned int, rlen, len); cskb = skb; - if (memcpy_toiovec(msg->msg_iov, cskb->data, copied)) { + if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) { if (!(flags & MSG_PEEK)) skb_queue_head(&sk->sk_receive_queue, skb); return -EFAULT; @@ -1219,6 +1416,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock, } kfree_skb(skb); + atomic_inc(&iucv->msg_recv); /* Queue backlog skbs */ spin_lock_bh(&iucv->message_q.lock); @@ -1235,6 +1433,24 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock, if (skb_queue_empty(&iucv->backlog_skb_q)) { if (!list_empty(&iucv->message_q.list)) iucv_process_message_q(sk); + if (atomic_read(&iucv->msg_recv) >= + iucv->msglimit / 2) { + /* send WIN to peer */ + blen = sizeof(struct af_iucv_trans_hdr) + + ETH_HLEN; + sskb = sock_alloc_send_skb(sk, blen, 1, &err); + if (sskb) { + skb_reserve(sskb, + sizeof(struct af_iucv_trans_hdr) + + ETH_HLEN); + err = afiucv_hs_send(NULL, sk, sskb, + AF_IUCV_FLAG_WIN); + } + if (err) { + sk->sk_state = IUCV_DISCONN; + sk->sk_state_change(sk); + } + } } spin_unlock_bh(&iucv->message_q.lock); } @@ -1329,8 +1545,8 @@ static int iucv_sock_shutdown(struct socket *sock, int how) if (how == SEND_SHUTDOWN || how == SHUTDOWN_MASK) { txmsg.class = 0; txmsg.tag = 0; - err = iucv_message_send(iucv->path, &txmsg, IUCV_IPRMDATA, 0, - (void *) iprm_shutdown, 8); + err = pr_iucv->message_send(iucv->path, &txmsg, IUCV_IPRMDATA, + 0, (void *) iprm_shutdown, 8); if (err) { switch (err) { case 1: @@ -1347,7 +1563,7 @@ static int iucv_sock_shutdown(struct socket *sock, int how) } if (how == RCV_SHUTDOWN || how == SHUTDOWN_MASK) { - err = iucv_path_quiesce(iucv_sk(sk)->path, NULL); + err = pr_iucv->path_quiesce(iucv->path, NULL); if (err) err = -ENOTCONN; @@ -1374,7 +1590,7 @@ static int iucv_sock_release(struct socket *sock) /* Unregister with IUCV base support */ if (iucv_sk(sk)->path) { - iucv_path_sever(iucv_sk(sk)->path, NULL); + pr_iucv->path_sever(iucv_sk(sk)->path, NULL); iucv_path_free(iucv_sk(sk)->path); iucv_sk(sk)->path = NULL; } @@ -1516,14 +1732,14 @@ static int iucv_callback_connreq(struct iucv_path *path, high_nmcpy(user_data, iucv->dst_name); ASCEBC(user_data, sizeof(user_data)); if (sk->sk_state != IUCV_LISTEN) { - err = iucv_path_sever(path, user_data); + err = pr_iucv->path_sever(path, user_data); iucv_path_free(path); goto fail; } /* Check for backlog size */ if (sk_acceptq_is_full(sk)) { - err = iucv_path_sever(path, user_data); + err = pr_iucv->path_sever(path, user_data); iucv_path_free(path); goto fail; } @@ -1531,7 +1747,7 @@ static int iucv_callback_connreq(struct iucv_path *path, /* Create the new socket */ nsk = iucv_sock_alloc(NULL, sk->sk_type, GFP_ATOMIC); if (!nsk) { - err = iucv_path_sever(path, user_data); + err = pr_iucv->path_sever(path, user_data); iucv_path_free(path); goto fail; } @@ -1555,9 +1771,9 @@ static int iucv_callback_connreq(struct iucv_path *path, /* set message limit for path based on msglimit of accepting socket */ niucv->msglimit = iucv->msglimit; path->msglim = iucv->msglimit; - err = iucv_path_accept(path, &af_iucv_handler, nuser_data, nsk); + err = pr_iucv->path_accept(path, &af_iucv_handler, nuser_data, nsk); if (err) { - err = iucv_path_sever(path, user_data); + err = pr_iucv->path_sever(path, user_data); iucv_path_free(path); iucv_sock_kill(nsk); goto fail; @@ -1591,7 +1807,7 @@ static void iucv_callback_rx(struct iucv_path *path, struct iucv_message *msg) int len; if (sk->sk_shutdown & RCV_SHUTDOWN) { - iucv_message_reject(path, msg); + pr_iucv->message_reject(path, msg); return; } @@ -1602,7 +1818,7 @@ static void iucv_callback_rx(struct iucv_path *path, struct iucv_message *msg) goto save_message; len = atomic_read(&sk->sk_rmem_alloc); - len += iucv_msg_length(msg) + sizeof(struct sk_buff); + len += SKB_TRUESIZE(iucv_msg_length(msg)); if (len > sk->sk_rcvbuf) goto save_message; @@ -1694,6 +1910,389 @@ static void iucv_callback_shutdown(struct iucv_path *path, u8 ipuser[16]) bh_unlock_sock(sk); } +/***************** HiperSockets transport callbacks ********************/ +static void afiucv_swap_src_dest(struct sk_buff *skb) +{ + struct af_iucv_trans_hdr *trans_hdr = + (struct af_iucv_trans_hdr *)skb->data; + char tmpID[8]; + char tmpName[8]; + + ASCEBC(trans_hdr->destUserID, sizeof(trans_hdr->destUserID)); + ASCEBC(trans_hdr->destAppName, sizeof(trans_hdr->destAppName)); + ASCEBC(trans_hdr->srcUserID, sizeof(trans_hdr->srcUserID)); + ASCEBC(trans_hdr->srcAppName, sizeof(trans_hdr->srcAppName)); + memcpy(tmpID, trans_hdr->srcUserID, 8); + memcpy(tmpName, trans_hdr->srcAppName, 8); + memcpy(trans_hdr->srcUserID, trans_hdr->destUserID, 8); + memcpy(trans_hdr->srcAppName, trans_hdr->destAppName, 8); + memcpy(trans_hdr->destUserID, tmpID, 8); + memcpy(trans_hdr->destAppName, tmpName, 8); + skb_push(skb, ETH_HLEN); + memset(skb->data, 0, ETH_HLEN); +} + +/** + * afiucv_hs_callback_syn - react on received SYN + **/ +static int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb) +{ + struct sock *nsk; + struct iucv_sock *iucv, *niucv; + struct af_iucv_trans_hdr *trans_hdr; + int err; + + iucv = iucv_sk(sk); + trans_hdr = (struct af_iucv_trans_hdr *)skb->data; + if (!iucv) { + /* no sock - connection refused */ + afiucv_swap_src_dest(skb); + trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN; + err = dev_queue_xmit(skb); + goto out; + } + + nsk = iucv_sock_alloc(NULL, sk->sk_type, GFP_ATOMIC); + bh_lock_sock(sk); + if ((sk->sk_state != IUCV_LISTEN) || + sk_acceptq_is_full(sk) || + !nsk) { + /* error on server socket - connection refused */ + if (nsk) + sk_free(nsk); + afiucv_swap_src_dest(skb); + trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN; + err = dev_queue_xmit(skb); + bh_unlock_sock(sk); + goto out; + } + + niucv = iucv_sk(nsk); + iucv_sock_init(nsk, sk); + niucv->transport = AF_IUCV_TRANS_HIPER; + niucv->msglimit = iucv->msglimit; + if (!trans_hdr->window) + niucv->msglimit_peer = IUCV_HIPER_MSGLIM_DEFAULT; + else + niucv->msglimit_peer = trans_hdr->window; + memcpy(niucv->dst_name, trans_hdr->srcAppName, 8); + memcpy(niucv->dst_user_id, trans_hdr->srcUserID, 8); + memcpy(niucv->src_name, iucv->src_name, 8); + memcpy(niucv->src_user_id, iucv->src_user_id, 8); + nsk->sk_bound_dev_if = sk->sk_bound_dev_if; + afiucv_swap_src_dest(skb); + trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_ACK; + trans_hdr->window = niucv->msglimit; + /* if receiver acks the xmit connection is established */ + err = dev_queue_xmit(skb); + if (!err) { + iucv_accept_enqueue(sk, nsk); + nsk->sk_state = IUCV_CONNECTED; + sk->sk_data_ready(sk, 1); + } else + iucv_sock_kill(nsk); + bh_unlock_sock(sk); + +out: + return NET_RX_SUCCESS; +} + +/** + * afiucv_hs_callback_synack() - react on received SYN-ACK + **/ +static int afiucv_hs_callback_synack(struct sock *sk, struct sk_buff *skb) +{ + struct iucv_sock *iucv = iucv_sk(sk); + struct af_iucv_trans_hdr *trans_hdr = + (struct af_iucv_trans_hdr *)skb->data; + + if (!iucv) + goto out; + if (sk->sk_state != IUCV_BOUND) + goto out; + bh_lock_sock(sk); + iucv->msglimit_peer = trans_hdr->window; + sk->sk_state = IUCV_CONNECTED; + sk->sk_state_change(sk); + bh_unlock_sock(sk); +out: + kfree_skb(skb); + return NET_RX_SUCCESS; +} + +/** + * afiucv_hs_callback_synfin() - react on received SYN_FIN + **/ +static int afiucv_hs_callback_synfin(struct sock *sk, struct sk_buff *skb) +{ + struct iucv_sock *iucv = iucv_sk(sk); + + if (!iucv) + goto out; + if (sk->sk_state != IUCV_BOUND) + goto out; + bh_lock_sock(sk); + sk->sk_state = IUCV_DISCONN; + sk->sk_state_change(sk); + bh_unlock_sock(sk); +out: + kfree_skb(skb); + return NET_RX_SUCCESS; +} + +/** + * afiucv_hs_callback_fin() - react on received FIN + **/ +static int afiucv_hs_callback_fin(struct sock *sk, struct sk_buff *skb) +{ + struct iucv_sock *iucv = iucv_sk(sk); + + /* other end of connection closed */ + if (iucv) { + bh_lock_sock(sk); + if (!list_empty(&iucv->accept_q)) + sk->sk_state = IUCV_SEVERED; + else + sk->sk_state = IUCV_DISCONN; + sk->sk_state_change(sk); + bh_unlock_sock(sk); + } + kfree_skb(skb); + return NET_RX_SUCCESS; +} + +/** + * afiucv_hs_callback_win() - react on received WIN + **/ +static int afiucv_hs_callback_win(struct sock *sk, struct sk_buff *skb) +{ + struct iucv_sock *iucv = iucv_sk(sk); + struct af_iucv_trans_hdr *trans_hdr = + (struct af_iucv_trans_hdr *)skb->data; + + if (!iucv) + return NET_RX_SUCCESS; + + if (sk->sk_state != IUCV_CONNECTED) + return NET_RX_SUCCESS; + + atomic_sub(trans_hdr->window, &iucv->msg_sent); + iucv_sock_wake_msglim(sk); + return NET_RX_SUCCESS; +} + +/** + * afiucv_hs_callback_rx() - react on received data + **/ +static int afiucv_hs_callback_rx(struct sock *sk, struct sk_buff *skb) +{ + struct iucv_sock *iucv = iucv_sk(sk); + + if (!iucv) { + kfree_skb(skb); + return NET_RX_SUCCESS; + } + + if (sk->sk_state != IUCV_CONNECTED) { + kfree_skb(skb); + return NET_RX_SUCCESS; + } + + /* write stuff from iucv_msg to skb cb */ + if (skb->len <= sizeof(struct af_iucv_trans_hdr)) { + kfree_skb(skb); + return NET_RX_SUCCESS; + } + skb_pull(skb, sizeof(struct af_iucv_trans_hdr)); + skb_reset_transport_header(skb); + skb_reset_network_header(skb); + spin_lock(&iucv->message_q.lock); + if (skb_queue_empty(&iucv->backlog_skb_q)) { + if (sock_queue_rcv_skb(sk, skb)) { + /* handle rcv queue full */ + skb_queue_tail(&iucv->backlog_skb_q, skb); + } + } else + skb_queue_tail(&iucv_sk(sk)->backlog_skb_q, skb); + spin_unlock(&iucv->message_q.lock); + return NET_RX_SUCCESS; +} + +/** + * afiucv_hs_rcv() - base function for arriving data through HiperSockets + * transport + * called from netif RX softirq + **/ +static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct hlist_node *node; + struct sock *sk; + struct iucv_sock *iucv; + struct af_iucv_trans_hdr *trans_hdr; + char nullstring[8]; + int err = 0; + + skb_pull(skb, ETH_HLEN); + trans_hdr = (struct af_iucv_trans_hdr *)skb->data; + EBCASC(trans_hdr->destAppName, sizeof(trans_hdr->destAppName)); + EBCASC(trans_hdr->destUserID, sizeof(trans_hdr->destUserID)); + EBCASC(trans_hdr->srcAppName, sizeof(trans_hdr->srcAppName)); + EBCASC(trans_hdr->srcUserID, sizeof(trans_hdr->srcUserID)); + memset(nullstring, 0, sizeof(nullstring)); + iucv = NULL; + sk = NULL; + read_lock(&iucv_sk_list.lock); + sk_for_each(sk, node, &iucv_sk_list.head) { + if (trans_hdr->flags == AF_IUCV_FLAG_SYN) { + if ((!memcmp(&iucv_sk(sk)->src_name, + trans_hdr->destAppName, 8)) && + (!memcmp(&iucv_sk(sk)->src_user_id, + trans_hdr->destUserID, 8)) && + (!memcmp(&iucv_sk(sk)->dst_name, nullstring, 8)) && + (!memcmp(&iucv_sk(sk)->dst_user_id, + nullstring, 8))) { + iucv = iucv_sk(sk); + break; + } + } else { + if ((!memcmp(&iucv_sk(sk)->src_name, + trans_hdr->destAppName, 8)) && + (!memcmp(&iucv_sk(sk)->src_user_id, + trans_hdr->destUserID, 8)) && + (!memcmp(&iucv_sk(sk)->dst_name, + trans_hdr->srcAppName, 8)) && + (!memcmp(&iucv_sk(sk)->dst_user_id, + trans_hdr->srcUserID, 8))) { + iucv = iucv_sk(sk); + break; + } + } + } + read_unlock(&iucv_sk_list.lock); + if (!iucv) + sk = NULL; + + /* no sock + how should we send with no sock + 1) send without sock no send rc checking? + 2) introduce default sock to handle this cases + + SYN -> send SYN|ACK in good case, send SYN|FIN in bad case + data -> send FIN + SYN|ACK, SYN|FIN, FIN -> no action? */ + + switch (trans_hdr->flags) { + case AF_IUCV_FLAG_SYN: + /* connect request */ + err = afiucv_hs_callback_syn(sk, skb); + break; + case (AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_ACK): + /* connect request confirmed */ + err = afiucv_hs_callback_synack(sk, skb); + break; + case (AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN): + /* connect request refused */ + err = afiucv_hs_callback_synfin(sk, skb); + break; + case (AF_IUCV_FLAG_FIN): + /* close request */ + err = afiucv_hs_callback_fin(sk, skb); + break; + case (AF_IUCV_FLAG_WIN): + err = afiucv_hs_callback_win(sk, skb); + if (skb->len > sizeof(struct af_iucv_trans_hdr)) + err = afiucv_hs_callback_rx(sk, skb); + else + kfree(skb); + break; + case 0: + /* plain data frame */ + err = afiucv_hs_callback_rx(sk, skb); + break; + default: + ; + } + + return err; +} + +/** + * afiucv_hs_callback_txnotify() - handle send notifcations from HiperSockets + * transport + **/ +static void afiucv_hs_callback_txnotify(struct sk_buff *skb, + enum iucv_tx_notify n) +{ + struct sock *isk = skb->sk; + struct sock *sk = NULL; + struct iucv_sock *iucv = NULL; + struct sk_buff_head *list; + struct sk_buff *list_skb; + struct sk_buff *this = NULL; + unsigned long flags; + struct hlist_node *node; + + read_lock(&iucv_sk_list.lock); + sk_for_each(sk, node, &iucv_sk_list.head) + if (sk == isk) { + iucv = iucv_sk(sk); + break; + } + read_unlock(&iucv_sk_list.lock); + + if (!iucv) + return; + + bh_lock_sock(sk); + list = &iucv->send_skb_q; + list_skb = list->next; + if (skb_queue_empty(list)) + goto out_unlock; + + spin_lock_irqsave(&list->lock, flags); + while (list_skb != (struct sk_buff *)list) { + if (skb_shinfo(list_skb) == skb_shinfo(skb)) { + this = list_skb; + switch (n) { + case TX_NOTIFY_OK: + __skb_unlink(this, list); + iucv_sock_wake_msglim(sk); + kfree_skb(this); + break; + case TX_NOTIFY_PENDING: + atomic_inc(&iucv->pendings); + break; + case TX_NOTIFY_DELAYED_OK: + __skb_unlink(this, list); + atomic_dec(&iucv->pendings); + if (atomic_read(&iucv->pendings) <= 0) + iucv_sock_wake_msglim(sk); + kfree_skb(this); + break; + case TX_NOTIFY_UNREACHABLE: + case TX_NOTIFY_DELAYED_UNREACHABLE: + case TX_NOTIFY_TPQFULL: /* not yet used */ + case TX_NOTIFY_GENERALERROR: + case TX_NOTIFY_DELAYED_GENERALERROR: + __skb_unlink(this, list); + kfree_skb(this); + if (!list_empty(&iucv->accept_q)) + sk->sk_state = IUCV_SEVERED; + else + sk->sk_state = IUCV_DISCONN; + sk->sk_state_change(sk); + break; + } + break; + } + list_skb = list_skb->next; + } + spin_unlock_irqrestore(&list->lock, flags); + +out_unlock: + bh_unlock_sock(sk); +} static const struct proto_ops iucv_sock_ops = { .family = PF_IUCV, .owner = THIS_MODULE, @@ -1720,71 +2319,104 @@ static const struct net_proto_family iucv_sock_family_ops = { .create = iucv_sock_create, }; -static int __init afiucv_init(void) +static struct packet_type iucv_packet_type = { + .type = cpu_to_be16(ETH_P_AF_IUCV), + .func = afiucv_hs_rcv, +}; + +static int afiucv_iucv_init(void) { int err; - if (!MACHINE_IS_VM) { - pr_err("The af_iucv module cannot be loaded" - " without z/VM\n"); - err = -EPROTONOSUPPORT; - goto out; - } - cpcmd("QUERY USERID", iucv_userid, sizeof(iucv_userid), &err); - if (unlikely(err)) { - WARN_ON(err); - err = -EPROTONOSUPPORT; - goto out; - } - - err = iucv_register(&af_iucv_handler, 0); + err = pr_iucv->iucv_register(&af_iucv_handler, 0); if (err) goto out; - err = proto_register(&iucv_proto, 0); - if (err) - goto out_iucv; - err = sock_register(&iucv_sock_family_ops); - if (err) - goto out_proto; /* establish dummy device */ + af_iucv_driver.bus = pr_iucv->bus; err = driver_register(&af_iucv_driver); if (err) - goto out_sock; + goto out_iucv; af_iucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL); if (!af_iucv_dev) { err = -ENOMEM; goto out_driver; } dev_set_name(af_iucv_dev, "af_iucv"); - af_iucv_dev->bus = &iucv_bus; - af_iucv_dev->parent = iucv_root; + af_iucv_dev->bus = pr_iucv->bus; + af_iucv_dev->parent = pr_iucv->root; af_iucv_dev->release = (void (*)(struct device *))kfree; af_iucv_dev->driver = &af_iucv_driver; err = device_register(af_iucv_dev); if (err) goto out_driver; - return 0; out_driver: driver_unregister(&af_iucv_driver); +out_iucv: + pr_iucv->iucv_unregister(&af_iucv_handler, 0); +out: + return err; +} + +static int __init afiucv_init(void) +{ + int err; + + if (MACHINE_IS_VM) { + cpcmd("QUERY USERID", iucv_userid, sizeof(iucv_userid), &err); + if (unlikely(err)) { + WARN_ON(err); + err = -EPROTONOSUPPORT; + goto out; + } + + pr_iucv = try_then_request_module(symbol_get(iucv_if), "iucv"); + if (!pr_iucv) { + printk(KERN_WARNING "iucv_if lookup failed\n"); + memset(&iucv_userid, 0, sizeof(iucv_userid)); + } + } else { + memset(&iucv_userid, 0, sizeof(iucv_userid)); + pr_iucv = NULL; + } + + err = proto_register(&iucv_proto, 0); + if (err) + goto out; + err = sock_register(&iucv_sock_family_ops); + if (err) + goto out_proto; + + if (pr_iucv) { + err = afiucv_iucv_init(); + if (err) + goto out_sock; + } + dev_add_pack(&iucv_packet_type); + return 0; + out_sock: sock_unregister(PF_IUCV); out_proto: proto_unregister(&iucv_proto); -out_iucv: - iucv_unregister(&af_iucv_handler, 0); out: + if (pr_iucv) + symbol_put(iucv_if); return err; } static void __exit afiucv_exit(void) { - device_unregister(af_iucv_dev); - driver_unregister(&af_iucv_driver); + if (pr_iucv) { + device_unregister(af_iucv_dev); + driver_unregister(&af_iucv_driver); + pr_iucv->iucv_unregister(&af_iucv_handler, 0); + symbol_put(iucv_if); + } + dev_remove_pack(&iucv_packet_type); sock_unregister(PF_IUCV); proto_unregister(&iucv_proto); - iucv_unregister(&af_iucv_handler, 0); } module_init(afiucv_init); @@ -1795,3 +2427,4 @@ MODULE_DESCRIPTION("IUCV Sockets ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); MODULE_ALIAS_NETPROTO(PF_IUCV); + diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 7f91249..403be43 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -51,7 +51,7 @@ #include <linux/cpu.h> #include <linux/reboot.h> #include <net/iucv/iucv.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/ebcdic.h> #include <asm/io.h> #include <asm/irq.h> @@ -1974,6 +1974,27 @@ out: return rc; } +struct iucv_interface iucv_if = { + .message_receive = iucv_message_receive, + .__message_receive = __iucv_message_receive, + .message_reply = iucv_message_reply, + .message_reject = iucv_message_reject, + .message_send = iucv_message_send, + .__message_send = __iucv_message_send, + .message_send2way = iucv_message_send2way, + .message_purge = iucv_message_purge, + .path_accept = iucv_path_accept, + .path_connect = iucv_path_connect, + .path_quiesce = iucv_path_quiesce, + .path_resume = iucv_path_resume, + .path_sever = iucv_path_sever, + .iucv_register = iucv_register, + .iucv_unregister = iucv_unregister, + .bus = NULL, + .root = NULL, +}; +EXPORT_SYMBOL(iucv_if); + /** * iucv_init * @@ -1988,12 +2009,13 @@ static int __init iucv_init(void) rc = -EPROTONOSUPPORT; goto out; } + ctl_set_bit(0, 1); rc = iucv_query_maxconn(); if (rc) - goto out; + goto out_ctl; rc = register_external_interrupt(0x4000, iucv_external_interrupt); if (rc) - goto out; + goto out_ctl; iucv_root = root_device_register("iucv"); if (IS_ERR(iucv_root)) { rc = PTR_ERR(iucv_root); @@ -2037,6 +2059,8 @@ static int __init iucv_init(void) rc = bus_register(&iucv_bus); if (rc) goto out_reboot; + iucv_if.root = iucv_root; + iucv_if.bus = &iucv_bus; return 0; out_reboot: @@ -2055,6 +2079,8 @@ out_free: root_device_unregister(iucv_root); out_int: unregister_external_interrupt(0x4000, iucv_external_interrupt); +out_ctl: + ctl_clear_bit(0, 1); out: return rc; } diff --git a/net/key/af_key.c b/net/key/af_key.c index 020a602..8636f10 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -220,7 +220,7 @@ static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2, #define BROADCAST_ONE 1 #define BROADCAST_REGISTERED 2 #define BROADCAST_PROMISC_ONLY 4 -static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation, +static int pfkey_broadcast(struct sk_buff *skb, int broadcast_flags, struct sock *one_sk, struct net *net) { @@ -246,7 +246,7 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation, * socket. */ if (pfk->promisc) - pfkey_broadcast_one(skb, &skb2, allocation, sk); + pfkey_broadcast_one(skb, &skb2, GFP_ATOMIC, sk); /* the exact target will be processed later */ if (sk == one_sk) @@ -261,7 +261,7 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation, continue; } - err2 = pfkey_broadcast_one(skb, &skb2, allocation, sk); + err2 = pfkey_broadcast_one(skb, &skb2, GFP_ATOMIC, sk); /* Error is cleare after succecful sending to at least one * registered KM */ @@ -271,7 +271,7 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation, rcu_read_unlock(); if (one_sk != NULL) - err = pfkey_broadcast_one(skb, &skb2, allocation, one_sk); + err = pfkey_broadcast_one(skb, &skb2, GFP_KERNEL, one_sk); kfree_skb(skb2); kfree_skb(skb); @@ -294,7 +294,7 @@ static int pfkey_do_dump(struct pfkey_sock *pfk) hdr = (struct sadb_msg *) pfk->dump.skb->data; hdr->sadb_msg_seq = 0; hdr->sadb_msg_errno = rc; - pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE, + pfkey_broadcast(pfk->dump.skb, BROADCAST_ONE, &pfk->sk, sock_net(&pfk->sk)); pfk->dump.skb = NULL; } @@ -335,7 +335,7 @@ static int pfkey_error(const struct sadb_msg *orig, int err, struct sock *sk) hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t)); - pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ONE, sk, sock_net(sk)); + pfkey_broadcast(skb, BROADCAST_ONE, sk, sock_net(sk)); return 0; } @@ -621,7 +621,7 @@ static struct xfrm_state *pfkey_xfrm_state_lookup(struct net *net, const struct unsigned short family; xfrm_address_t *xaddr; - sa = (const struct sadb_sa *) ext_hdrs[SADB_EXT_SA-1]; + sa = ext_hdrs[SADB_EXT_SA - 1]; if (sa == NULL) return NULL; @@ -630,7 +630,7 @@ static struct xfrm_state *pfkey_xfrm_state_lookup(struct net *net, const struct return NULL; /* sadb_address_len should be checked by caller */ - addr = (const struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_DST-1]; + addr = ext_hdrs[SADB_EXT_ADDRESS_DST - 1]; if (addr == NULL) return NULL; @@ -1039,7 +1039,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, int err; - sa = (const struct sadb_sa *) ext_hdrs[SADB_EXT_SA-1]; + sa = ext_hdrs[SADB_EXT_SA - 1]; if (!sa || !present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1], ext_hdrs[SADB_EXT_ADDRESS_DST-1])) @@ -1078,7 +1078,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, sa->sadb_sa_encrypt > SADB_X_CALG_MAX) || sa->sadb_sa_encrypt > SADB_EALG_MAX) return ERR_PTR(-EINVAL); - key = (const struct sadb_key*) ext_hdrs[SADB_EXT_KEY_AUTH-1]; + key = ext_hdrs[SADB_EXT_KEY_AUTH - 1]; if (key != NULL && sa->sadb_sa_auth != SADB_X_AALG_NULL && ((key->sadb_key_bits+7) / 8 == 0 || @@ -1105,14 +1105,14 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, if (sa->sadb_sa_flags & SADB_SAFLAGS_NOPMTUDISC) x->props.flags |= XFRM_STATE_NOPMTUDISC; - lifetime = (const struct sadb_lifetime*) ext_hdrs[SADB_EXT_LIFETIME_HARD-1]; + lifetime = ext_hdrs[SADB_EXT_LIFETIME_HARD - 1]; if (lifetime != NULL) { x->lft.hard_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations); x->lft.hard_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes); x->lft.hard_add_expires_seconds = lifetime->sadb_lifetime_addtime; x->lft.hard_use_expires_seconds = lifetime->sadb_lifetime_usetime; } - lifetime = (const struct sadb_lifetime*) ext_hdrs[SADB_EXT_LIFETIME_SOFT-1]; + lifetime = ext_hdrs[SADB_EXT_LIFETIME_SOFT - 1]; if (lifetime != NULL) { x->lft.soft_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations); x->lft.soft_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes); @@ -1120,7 +1120,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, x->lft.soft_use_expires_seconds = lifetime->sadb_lifetime_usetime; } - sec_ctx = (const struct sadb_x_sec_ctx *) ext_hdrs[SADB_X_EXT_SEC_CTX-1]; + sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1]; if (sec_ctx != NULL) { struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx); @@ -1134,7 +1134,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, goto out; } - key = (const struct sadb_key*) ext_hdrs[SADB_EXT_KEY_AUTH-1]; + key = ext_hdrs[SADB_EXT_KEY_AUTH - 1]; if (sa->sadb_sa_auth) { int keysize = 0; struct xfrm_algo_desc *a = xfrm_aalg_get_byid(sa->sadb_sa_auth); @@ -1361,7 +1361,7 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_ xfrm_state_put(x); - pfkey_broadcast(resp_skb, GFP_KERNEL, BROADCAST_ONE, sk, net); + pfkey_broadcast(resp_skb, BROADCAST_ONE, sk, net); return 0; } @@ -1449,7 +1449,7 @@ static int key_notify_sa(struct xfrm_state *x, const struct km_event *c) hdr->sadb_msg_seq = c->seq; hdr->sadb_msg_pid = c->pid; - pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xs_net(x)); + pfkey_broadcast(skb, BROADCAST_ALL, NULL, xs_net(x)); return 0; } @@ -1566,7 +1566,7 @@ static int pfkey_get(struct sock *sk, struct sk_buff *skb, const struct sadb_msg out_hdr->sadb_msg_reserved = 0; out_hdr->sadb_msg_seq = hdr->sadb_msg_seq; out_hdr->sadb_msg_pid = hdr->sadb_msg_pid; - pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk)); + pfkey_broadcast(out_skb, BROADCAST_ONE, sk, sock_net(sk)); return 0; } @@ -1667,7 +1667,7 @@ static int pfkey_register(struct sock *sk, struct sk_buff *skb, const struct sad return -ENOBUFS; } - pfkey_broadcast(supp_skb, GFP_KERNEL, BROADCAST_REGISTERED, sk, sock_net(sk)); + pfkey_broadcast(supp_skb, BROADCAST_REGISTERED, sk, sock_net(sk)); return 0; } @@ -1686,7 +1686,7 @@ static int unicast_flush_resp(struct sock *sk, const struct sadb_msg *ihdr) hdr->sadb_msg_errno = (uint8_t) 0; hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t)); - return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk)); + return pfkey_broadcast(skb, BROADCAST_ONE, sk, sock_net(sk)); } static int key_notify_sa_flush(const struct km_event *c) @@ -1707,7 +1707,7 @@ static int key_notify_sa_flush(const struct km_event *c) hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t)); hdr->sadb_msg_reserved = 0; - pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net); + pfkey_broadcast(skb, BROADCAST_ALL, NULL, c->net); return 0; } @@ -1768,7 +1768,7 @@ static int dump_sa(struct xfrm_state *x, int count, void *ptr) out_hdr->sadb_msg_pid = pfk->dump.msg_pid; if (pfk->dump.skb) - pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE, + pfkey_broadcast(pfk->dump.skb, BROADCAST_ONE, &pfk->sk, sock_net(&pfk->sk)); pfk->dump.skb = out_skb; @@ -1829,7 +1829,7 @@ static int pfkey_promisc(struct sock *sk, struct sk_buff *skb, const struct sadb new_hdr->sadb_msg_errno = 0; } - pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ALL, NULL, sock_net(sk)); + pfkey_broadcast(skb, BROADCAST_ALL, NULL, sock_net(sk)); return 0; } @@ -2160,7 +2160,7 @@ static int key_notify_policy(struct xfrm_policy *xp, int dir, const struct km_ev out_hdr->sadb_msg_errno = 0; out_hdr->sadb_msg_seq = c->seq; out_hdr->sadb_msg_pid = c->pid; - pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xp_net(xp)); + pfkey_broadcast(out_skb, BROADCAST_ALL, NULL, xp_net(xp)); return 0; } @@ -2221,7 +2221,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, const struct sadb_ if (xp->selector.dport) xp->selector.dport_mask = htons(0xffff); - sec_ctx = (struct sadb_x_sec_ctx *) ext_hdrs[SADB_X_EXT_SEC_CTX-1]; + sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1]; if (sec_ctx != NULL) { struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx); @@ -2325,7 +2325,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sa if (sel.dport) sel.dport_mask = htons(0xffff); - sec_ctx = (struct sadb_x_sec_ctx *) ext_hdrs[SADB_X_EXT_SEC_CTX-1]; + sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1]; if (sec_ctx != NULL) { struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx); @@ -2386,7 +2386,7 @@ static int key_pol_get_resp(struct sock *sk, struct xfrm_policy *xp, const struc out_hdr->sadb_msg_errno = 0; out_hdr->sadb_msg_seq = hdr->sadb_msg_seq; out_hdr->sadb_msg_pid = hdr->sadb_msg_pid; - pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, xp_net(xp)); + pfkey_broadcast(out_skb, BROADCAST_ONE, sk, xp_net(xp)); err = 0; out: @@ -2639,7 +2639,7 @@ static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr) out_hdr->sadb_msg_pid = pfk->dump.msg_pid; if (pfk->dump.skb) - pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE, + pfkey_broadcast(pfk->dump.skb, BROADCAST_ONE, &pfk->sk, sock_net(&pfk->sk)); pfk->dump.skb = out_skb; @@ -2690,7 +2690,7 @@ static int key_notify_policy_flush(const struct km_event *c) hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC; hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t)); hdr->sadb_msg_reserved = 0; - pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net); + pfkey_broadcast(skb_out, BROADCAST_ALL, NULL, c->net); return 0; } @@ -2756,7 +2756,7 @@ static int pfkey_process(struct sock *sk, struct sk_buff *skb, const struct sadb void *ext_hdrs[SADB_EXT_MAX]; int err; - pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL, + pfkey_broadcast(skb_clone(skb, GFP_KERNEL), BROADCAST_PROMISC_ONLY, NULL, sock_net(sk)); memset(ext_hdrs, 0, sizeof(ext_hdrs)); @@ -2962,7 +2962,7 @@ static int key_notify_sa_expire(struct xfrm_state *x, const struct km_event *c) out_hdr->sadb_msg_seq = 0; out_hdr->sadb_msg_pid = 0; - pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x)); + pfkey_broadcast(out_skb, BROADCAST_REGISTERED, NULL, xs_net(x)); return 0; } @@ -3134,7 +3134,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_ctx->ctx_len); } - return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x)); + return pfkey_broadcast(skb, BROADCAST_REGISTERED, NULL, xs_net(x)); } static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt, @@ -3332,7 +3332,7 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, n_port->sadb_x_nat_t_port_port = sport; n_port->sadb_x_nat_t_port_reserved = 0; - return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x)); + return pfkey_broadcast(skb, BROADCAST_REGISTERED, NULL, xs_net(x)); } #ifdef CONFIG_NET_KEY_MIGRATE @@ -3524,7 +3524,7 @@ static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, } /* broadcast migrate message to sockets */ - pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, &init_net); + pfkey_broadcast(skb, BROADCAST_ALL, NULL, &init_net); return 0; @@ -3595,7 +3595,6 @@ static int pfkey_recvmsg(struct kiocb *kiocb, if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT)) goto out; - msg->msg_namelen = 0; skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err); if (skb == NULL) goto out; diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 6a3d680..7501b22 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -55,7 +55,7 @@ #include <net/protocol.h> #include <asm/byteorder.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "l2tp_core.h" @@ -397,6 +397,7 @@ static void l2tp_recv_dequeue(struct l2tp_session *session) * expect to send up next, dequeue it and any other * in-sequence packets behind it. */ +start: spin_lock_bh(&session->reorder_q.lock); skb_queue_walk_safe(&session->reorder_q, skb, tmp) { if (time_after(jiffies, L2TP_SKB_CB(skb)->expires)) { @@ -433,7 +434,7 @@ static void l2tp_recv_dequeue(struct l2tp_session *session) */ spin_unlock_bh(&session->reorder_q.lock); l2tp_recv_dequeue_skb(session, skb); - spin_lock_bh(&session->reorder_q.lock); + goto start; } out: @@ -755,9 +756,6 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb, goto error; } - /* Point to L2TP header */ - optr = ptr = skb->data; - /* Trace packet contents, if enabled */ if (tunnel->debug & L2TP_MSG_DATA) { length = min(32u, skb->len); @@ -768,12 +766,15 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb, offset = 0; do { - printk(" %02X", ptr[offset]); + printk(" %02X", skb->data[offset]); } while (++offset < length); printk("\n"); } + /* Point to L2TP header */ + optr = ptr = skb->data; + /* Get L2TP header flags */ hdrflags = ntohs(*(__be16 *) ptr); @@ -1071,7 +1072,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len /* Get routing info from the tunnel socket */ skb_dst_drop(skb); - skb_dst_set(skb, dst_clone(__sk_dst_get(sk))); + skb_dst_set(skb, dst_clone(__sk_dst_check(sk, 0))); inet = inet_sk(sk); fl = &inet->cork.fl; diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 78bc442..334a93d 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -494,18 +494,16 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m if (connected) rt = (struct rtable *) __sk_dst_check(sk, 0); + rcu_read_lock(); if (rt == NULL) { - struct ip_options_rcu *inet_opt; + const struct ip_options_rcu *inet_opt; - rcu_read_lock(); inet_opt = rcu_dereference(inet->inet_opt); /* Use correct destination address if we have options. */ if (inet_opt && inet_opt->opt.srr) daddr = inet_opt->opt.faddr; - rcu_read_unlock(); - /* If this fails, retransmit mechanism of transport layer will * keep trying until route appears or the connection times * itself out. @@ -517,12 +515,23 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m sk->sk_bound_dev_if); if (IS_ERR(rt)) goto no_route; - sk_setup_caps(sk, &rt->dst); + if (connected) { + sk_setup_caps(sk, &rt->dst); + } else { + skb_dst_set(skb, &rt->dst); + goto xmit; + } } - skb_dst_set(skb, dst_clone(&rt->dst)); + /* We dont need to clone dst here, it is guaranteed to not disappear. + * __dev_xmit_skb() might force a refcount if needed. + */ + skb_dst_set_noref(skb, &rt->dst); + +xmit: /* Queue the packet to IP for output */ rc = ip_queue_xmit(skb, &inet->cork.fl); + rcu_read_unlock(); error: /* Update stats */ @@ -539,6 +548,7 @@ out: return rc; no_route: + rcu_read_unlock(); IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); kfree_skb(skb); rc = -EHOSTUNREACH; @@ -558,9 +568,6 @@ static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m if (flags & MSG_OOB) goto out; - if (addr_len) - *addr_len = sizeof(*sin); - skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) goto out; @@ -583,6 +590,7 @@ static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m sin->sin_addr.s_addr = ip_hdr(skb)->saddr; sin->sin_port = 0; memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); + *addr_len = sizeof(*sin); } if (inet->cmsg_flags) ip_cmsg_recv(msg, skb); diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 2366914..767bf4a 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -97,7 +97,7 @@ #include <net/xfrm.h> #include <asm/byteorder.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include "l2tp_core.h" @@ -200,8 +200,6 @@ static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock, if (sk->sk_state & PPPOX_BOUND) goto end; - msg->msg_namelen = 0; - err = 0; skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err); @@ -357,7 +355,9 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh goto error_put_sess_tun; } + local_bh_disable(); l2tp_xmit_skb(session, skb, session->hdr_len); + local_bh_enable(); sock_put(ps->tunnel_sock); sock_put(sk); @@ -396,6 +396,7 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) struct pppol2tp_session *ps; int old_headroom; int new_headroom; + int uhlen, headroom; if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) goto abort; @@ -414,7 +415,13 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) goto abort_put_sess; old_headroom = skb_headroom(skb); - if (skb_cow_head(skb, sizeof(ppph))) + uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0; + headroom = NET_SKB_PAD + + sizeof(struct iphdr) + /* IP header */ + uhlen + /* UDP header (if L2TP_ENCAPTYPE_UDP) */ + session->hdr_len + /* L2TP header */ + sizeof(ppph); /* PPP header */ + if (skb_cow_head(skb, headroom)) goto abort_put_sess_tun; new_headroom = skb_headroom(skb); @@ -425,7 +432,9 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) skb->data[0] = ppph[0]; skb->data[1] = ppph[1]; + local_bh_disable(); l2tp_xmit_skb(session, skb, session->hdr_len); + local_bh_enable(); sock_put(sk_tun); sock_put(sk); @@ -763,9 +772,10 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, session->deref = pppol2tp_session_sock_put; /* If PMTU discovery was enabled, use the MTU that was discovered */ - dst = sk_dst_get(sk); + dst = sk_dst_get(tunnel->sock); if (dst != NULL) { - u32 pmtu = dst_mtu(__sk_dst_get(sk)); + u32 pmtu = dst_mtu(dst); + if (pmtu != 0) session->mtu = session->mru = pmtu - PPPOL2TP_HEADER_OVERHEAD; @@ -1342,7 +1352,7 @@ static int pppol2tp_setsockopt(struct socket *sock, int level, int optname, int err; if (level != SOL_PPPOL2TP) - return udp_prot.setsockopt(sk, level, optname, optval, optlen); + return -EINVAL; if (optlen < sizeof(int)) return -EINVAL; @@ -1468,7 +1478,7 @@ static int pppol2tp_getsockopt(struct socket *sock, int level, struct pppol2tp_session *ps; if (level != SOL_PPPOL2TP) - return udp_prot.getsockopt(sk, level, optname, optval, optlen); + return -EINVAL; if (get_user(len, (int __user *) optlen)) return -EFAULT; diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c index d5d8d55..8d0324b 100644 --- a/net/lapb/lapb_iface.c +++ b/net/lapb/lapb_iface.c @@ -139,7 +139,8 @@ out: return lapb; } -int lapb_register(struct net_device *dev, struct lapb_register_struct *callbacks) +int lapb_register(struct net_device *dev, + const struct lapb_register_struct *callbacks) { struct lapb_cb *lapb; int rc = LAPB_BADTOKEN; @@ -158,7 +159,7 @@ int lapb_register(struct net_device *dev, struct lapb_register_struct *callbacks goto out; lapb->dev = dev; - lapb->callbacks = *callbacks; + lapb->callbacks = callbacks; __lapb_insert_cb(lapb); @@ -300,26 +301,26 @@ int lapb_disconnect_request(struct net_device *dev) goto out; switch (lapb->state) { - case LAPB_STATE_0: - rc = LAPB_NOTCONNECTED; - goto out_put; + case LAPB_STATE_0: + rc = LAPB_NOTCONNECTED; + goto out_put; - case LAPB_STATE_1: + case LAPB_STATE_1: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S1 TX DISC(1)\n", lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S1 TX DISC(1)\n", lapb->dev); #endif #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->dev); #endif - lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); - lapb->state = LAPB_STATE_0; - lapb_start_t1timer(lapb); - rc = LAPB_NOTCONNECTED; - goto out_put; - - case LAPB_STATE_2: - rc = LAPB_OK; - goto out_put; + lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); + rc = LAPB_NOTCONNECTED; + goto out_put; + + case LAPB_STATE_2: + rc = LAPB_OK; + goto out_put; } lapb_clear_queues(lapb); @@ -380,32 +381,32 @@ int lapb_data_received(struct net_device *dev, struct sk_buff *skb) void lapb_connect_confirmation(struct lapb_cb *lapb, int reason) { - if (lapb->callbacks.connect_confirmation) - lapb->callbacks.connect_confirmation(lapb->dev, reason); + if (lapb->callbacks->connect_confirmation) + lapb->callbacks->connect_confirmation(lapb->dev, reason); } void lapb_connect_indication(struct lapb_cb *lapb, int reason) { - if (lapb->callbacks.connect_indication) - lapb->callbacks.connect_indication(lapb->dev, reason); + if (lapb->callbacks->connect_indication) + lapb->callbacks->connect_indication(lapb->dev, reason); } void lapb_disconnect_confirmation(struct lapb_cb *lapb, int reason) { - if (lapb->callbacks.disconnect_confirmation) - lapb->callbacks.disconnect_confirmation(lapb->dev, reason); + if (lapb->callbacks->disconnect_confirmation) + lapb->callbacks->disconnect_confirmation(lapb->dev, reason); } void lapb_disconnect_indication(struct lapb_cb *lapb, int reason) { - if (lapb->callbacks.disconnect_indication) - lapb->callbacks.disconnect_indication(lapb->dev, reason); + if (lapb->callbacks->disconnect_indication) + lapb->callbacks->disconnect_indication(lapb->dev, reason); } int lapb_data_indication(struct lapb_cb *lapb, struct sk_buff *skb) { - if (lapb->callbacks.data_indication) - return lapb->callbacks.data_indication(lapb->dev, skb); + if (lapb->callbacks->data_indication) + return lapb->callbacks->data_indication(lapb->dev, skb); kfree_skb(skb); return NET_RX_SUCCESS; /* For now; must be != NET_RX_DROP */ @@ -415,8 +416,8 @@ int lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *skb) { int used = 0; - if (lapb->callbacks.data_transmit) { - lapb->callbacks.data_transmit(lapb->dev, skb); + if (lapb->callbacks->data_transmit) { + lapb->callbacks->data_transmit(lapb->dev, skb); used = 1; } diff --git a/net/lapb/lapb_in.c b/net/lapb/lapb_in.c index 21904a0..2ec1af5 100644 --- a/net/lapb/lapb_in.c +++ b/net/lapb/lapb_in.c @@ -44,89 +44,86 @@ static void lapb_state0_machine(struct lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame) { switch (frame->type) { - case LAPB_SABM: + case LAPB_SABM: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S0 RX SABM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S0 RX SABM(%d)\n", + lapb->dev, frame->pf); #endif - if (lapb->mode & LAPB_EXTENDED) { + if (lapb->mode & LAPB_EXTENDED) { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_DM, frame->pf, - LAPB_RESPONSE); - } else { + lapb_send_control(lapb, LAPB_DM, frame->pf, + LAPB_RESPONSE); + } else { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", + lapb->dev, frame->pf); #endif #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", - lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->dev); #endif - lapb_send_control(lapb, LAPB_UA, frame->pf, - LAPB_RESPONSE); - lapb_stop_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb->state = LAPB_STATE_3; - lapb->condition = 0x00; - lapb->n2count = 0; - lapb->vs = 0; - lapb->vr = 0; - lapb->va = 0; - lapb_connect_indication(lapb, LAPB_OK); - } - break; + lapb_send_control(lapb, LAPB_UA, frame->pf, + LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_3; + lapb->condition = 0x00; + lapb->n2count = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + lapb_connect_indication(lapb, LAPB_OK); + } + break; - case LAPB_SABME: + case LAPB_SABME: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S0 RX SABME(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S0 RX SABME(%d)\n", + lapb->dev, frame->pf); #endif - if (lapb->mode & LAPB_EXTENDED) { + if (lapb->mode & LAPB_EXTENDED) { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", + lapb->dev, frame->pf); #endif #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", - lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->dev); #endif - lapb_send_control(lapb, LAPB_UA, frame->pf, - LAPB_RESPONSE); - lapb_stop_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb->state = LAPB_STATE_3; - lapb->condition = 0x00; - lapb->n2count = 0; - lapb->vs = 0; - lapb->vr = 0; - lapb->va = 0; - lapb_connect_indication(lapb, LAPB_OK); - } else { -#if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", - lapb->dev, frame->pf); + lapb_send_control(lapb, LAPB_UA, frame->pf, + LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_3; + lapb->condition = 0x00; + lapb->n2count = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + lapb_connect_indication(lapb, LAPB_OK); + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_DM, frame->pf, - LAPB_RESPONSE); - } - break; + lapb_send_control(lapb, LAPB_DM, frame->pf, + LAPB_RESPONSE); + } + break; - case LAPB_DISC: + case LAPB_DISC: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S0 RX DISC(%d)\n", - lapb->dev, frame->pf); - printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S0 RX DISC(%d)\n", + lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_UA, frame->pf, - LAPB_RESPONSE); - break; + lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + break; - default: - break; + default: + break; } kfree_skb(skb); @@ -140,100 +137,97 @@ static void lapb_state1_machine(struct lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame) { switch (frame->type) { - case LAPB_SABM: + case LAPB_SABM: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S1 RX SABM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S1 RX SABM(%d)\n", + lapb->dev, frame->pf); #endif - if (lapb->mode & LAPB_EXTENDED) { + if (lapb->mode & LAPB_EXTENDED) { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", - lapb->dev, frame->pf); -#endif - lapb_send_control(lapb, LAPB_DM, frame->pf, - LAPB_RESPONSE); - } else { -#if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_UA, frame->pf, - LAPB_RESPONSE); - } - break; - - case LAPB_SABME: + lapb_send_control(lapb, LAPB_DM, frame->pf, + LAPB_RESPONSE); + } else { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S1 RX SABME(%d)\n", + printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", lapb->dev, frame->pf); #endif - if (lapb->mode & LAPB_EXTENDED) { + lapb_send_control(lapb, LAPB_UA, frame->pf, + LAPB_RESPONSE); + } + break; + + case LAPB_SABME: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S1 RX SABME(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_UA, frame->pf, - LAPB_RESPONSE); - } else { + if (lapb->mode & LAPB_EXTENDED) { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_DM, frame->pf, - LAPB_RESPONSE); - } - break; - - case LAPB_DISC: + lapb_send_control(lapb, LAPB_UA, frame->pf, + LAPB_RESPONSE); + } else { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S1 RX DISC(%d)\n", - lapb->dev, frame->pf); printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->dev, frame->pf); #endif lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE); - break; + } + break; - case LAPB_UA: + case LAPB_DISC: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S1 RX UA(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S1 RX DISC(%d)\n", + lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", + lapb->dev, frame->pf); #endif - if (frame->pf) { -#if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S1 -> S3\n", - lapb->dev); -#endif - lapb_stop_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb->state = LAPB_STATE_3; - lapb->condition = 0x00; - lapb->n2count = 0; - lapb->vs = 0; - lapb->vr = 0; - lapb->va = 0; - lapb_connect_confirmation(lapb, LAPB_OK); - } - break; + lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE); + break; - case LAPB_DM: + case LAPB_UA: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S1 RX DM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S1 RX UA(%d)\n", + lapb->dev, frame->pf); #endif - if (frame->pf) { + if (frame->pf) { #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", - lapb->dev); -#endif - lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb_start_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb_disconnect_indication(lapb, LAPB_REFUSED); - } - break; + printk(KERN_DEBUG "lapb: (%p) S1 -> S3\n", lapb->dev); +#endif + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_3; + lapb->condition = 0x00; + lapb->n2count = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + lapb_connect_confirmation(lapb, LAPB_OK); + } + break; + + case LAPB_DM: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S1 RX DM(%d)\n", + lapb->dev, frame->pf); +#endif + if (frame->pf) { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->dev); +#endif + lapb_clear_queues(lapb); + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb_disconnect_indication(lapb, LAPB_REFUSED); + } + break; } kfree_skb(skb); @@ -247,78 +241,73 @@ static void lapb_state2_machine(struct lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame) { switch (frame->type) { - case LAPB_SABM: - case LAPB_SABME: + case LAPB_SABM: + case LAPB_SABME: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S2 RX {SABM,SABME}(%d)\n", - lapb->dev, frame->pf); - printk(KERN_DEBUG "lapb: (%p) S2 TX DM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S2 RX {SABM,SABME}(%d)\n", + lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S2 TX DM(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_DM, frame->pf, - LAPB_RESPONSE); - break; + lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE); + break; - case LAPB_DISC: + case LAPB_DISC: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S2 RX DISC(%d)\n", - lapb->dev, frame->pf); - printk(KERN_DEBUG "lapb: (%p) S2 TX UA(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S2 RX DISC(%d)\n", + lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S2 TX UA(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_UA, frame->pf, - LAPB_RESPONSE); - break; + lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + break; - case LAPB_UA: + case LAPB_UA: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S2 RX UA(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S2 RX UA(%d)\n", + lapb->dev, frame->pf); #endif - if (frame->pf) { + if (frame->pf) { #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", - lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->dev); #endif - lapb->state = LAPB_STATE_0; - lapb_start_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb_disconnect_confirmation(lapb, LAPB_OK); - } - break; + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb_disconnect_confirmation(lapb, LAPB_OK); + } + break; - case LAPB_DM: + case LAPB_DM: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", + lapb->dev, frame->pf); #endif - if (frame->pf) { + if (frame->pf) { #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", - lapb->dev); -#endif - lapb->state = LAPB_STATE_0; - lapb_start_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb_disconnect_confirmation(lapb, - LAPB_NOTCONNECTED); - } - break; + printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->dev); +#endif + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb_disconnect_confirmation(lapb, LAPB_NOTCONNECTED); + } + break; - case LAPB_I: - case LAPB_REJ: - case LAPB_RNR: - case LAPB_RR: + case LAPB_I: + case LAPB_REJ: + case LAPB_RNR: + case LAPB_RR: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S2 RX {I,REJ,RNR,RR}" - "(%d)\n", lapb->dev, frame->pf); - printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S2 RX {I,REJ,RNR,RR}(%d)\n", + lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", + lapb->dev, frame->pf); #endif - if (frame->pf) - lapb_send_control(lapb, LAPB_DM, frame->pf, - LAPB_RESPONSE); - break; + if (frame->pf) + lapb_send_control(lapb, LAPB_DM, frame->pf, + LAPB_RESPONSE); + break; } kfree_skb(skb); @@ -336,277 +325,267 @@ static void lapb_state3_machine(struct lapb_cb *lapb, struct sk_buff *skb, LAPB_SMODULUS; switch (frame->type) { - case LAPB_SABM: + case LAPB_SABM: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 RX SABM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S3 RX SABM(%d)\n", + lapb->dev, frame->pf); #endif - if (lapb->mode & LAPB_EXTENDED) { + if (lapb->mode & LAPB_EXTENDED) { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_DM, frame->pf, - LAPB_RESPONSE); - } else { + lapb_send_control(lapb, LAPB_DM, frame->pf, + LAPB_RESPONSE); + } else { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_UA, frame->pf, - LAPB_RESPONSE); - lapb_stop_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb->condition = 0x00; - lapb->n2count = 0; - lapb->vs = 0; - lapb->vr = 0; - lapb->va = 0; - lapb_requeue_frames(lapb); - } - break; + lapb_send_control(lapb, LAPB_UA, frame->pf, + LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->condition = 0x00; + lapb->n2count = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + lapb_requeue_frames(lapb); + } + break; - case LAPB_SABME: + case LAPB_SABME: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 RX SABME(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S3 RX SABME(%d)\n", + lapb->dev, frame->pf); #endif - if (lapb->mode & LAPB_EXTENDED) { + if (lapb->mode & LAPB_EXTENDED) { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_UA, frame->pf, - LAPB_RESPONSE); - lapb_stop_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb->condition = 0x00; - lapb->n2count = 0; - lapb->vs = 0; - lapb->vr = 0; - lapb->va = 0; - lapb_requeue_frames(lapb); - } else { + lapb_send_control(lapb, LAPB_UA, frame->pf, + LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->condition = 0x00; + lapb->n2count = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + lapb_requeue_frames(lapb); + } else { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_DM, frame->pf, - LAPB_RESPONSE); - } - break; + lapb_send_control(lapb, LAPB_DM, frame->pf, + LAPB_RESPONSE); + } + break; - case LAPB_DISC: + case LAPB_DISC: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 RX DISC(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S3 RX DISC(%d)\n", + lapb->dev, frame->pf); #endif #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", - lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->dev); #endif - lapb_clear_queues(lapb); - lapb_send_control(lapb, LAPB_UA, frame->pf, - LAPB_RESPONSE); - lapb_start_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb->state = LAPB_STATE_0; - lapb_disconnect_indication(lapb, LAPB_OK); - break; + lapb_clear_queues(lapb); + lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_0; + lapb_disconnect_indication(lapb, LAPB_OK); + break; - case LAPB_DM: + case LAPB_DM: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 RX DM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S3 RX DM(%d)\n", + lapb->dev, frame->pf); #endif #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", - lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->dev); #endif - lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb_start_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb_disconnect_indication(lapb, LAPB_NOTCONNECTED); - break; + lapb_clear_queues(lapb); + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb_disconnect_indication(lapb, LAPB_NOTCONNECTED); + break; - case LAPB_RNR: + case LAPB_RNR: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 RX RNR(%d) R%d\n", - lapb->dev, frame->pf, frame->nr); + printk(KERN_DEBUG "lapb: (%p) S3 RX RNR(%d) R%d\n", + lapb->dev, frame->pf, frame->nr); #endif - lapb->condition |= LAPB_PEER_RX_BUSY_CONDITION; - lapb_check_need_response(lapb, frame->cr, frame->pf); - if (lapb_validate_nr(lapb, frame->nr)) { - lapb_check_iframes_acked(lapb, frame->nr); - } else { - lapb->frmr_data = *frame; - lapb->frmr_type = LAPB_FRMR_Z; - lapb_transmit_frmr(lapb); + lapb->condition |= LAPB_PEER_RX_BUSY_CONDITION; + lapb_check_need_response(lapb, frame->cr, frame->pf); + if (lapb_validate_nr(lapb, frame->nr)) { + lapb_check_iframes_acked(lapb, frame->nr); + } else { + lapb->frmr_data = *frame; + lapb->frmr_type = LAPB_FRMR_Z; + lapb_transmit_frmr(lapb); #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", - lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->dev); #endif - lapb_start_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb->state = LAPB_STATE_4; - lapb->n2count = 0; - } - break; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; + } + break; - case LAPB_RR: + case LAPB_RR: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 RX RR(%d) R%d\n", - lapb->dev, frame->pf, frame->nr); + printk(KERN_DEBUG "lapb: (%p) S3 RX RR(%d) R%d\n", + lapb->dev, frame->pf, frame->nr); #endif - lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION; - lapb_check_need_response(lapb, frame->cr, frame->pf); - if (lapb_validate_nr(lapb, frame->nr)) { - lapb_check_iframes_acked(lapb, frame->nr); - } else { - lapb->frmr_data = *frame; - lapb->frmr_type = LAPB_FRMR_Z; - lapb_transmit_frmr(lapb); + lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION; + lapb_check_need_response(lapb, frame->cr, frame->pf); + if (lapb_validate_nr(lapb, frame->nr)) { + lapb_check_iframes_acked(lapb, frame->nr); + } else { + lapb->frmr_data = *frame; + lapb->frmr_type = LAPB_FRMR_Z; + lapb_transmit_frmr(lapb); #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", - lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->dev); #endif - lapb_start_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb->state = LAPB_STATE_4; - lapb->n2count = 0; - } - break; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; + } + break; - case LAPB_REJ: + case LAPB_REJ: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 RX REJ(%d) R%d\n", - lapb->dev, frame->pf, frame->nr); + printk(KERN_DEBUG "lapb: (%p) S3 RX REJ(%d) R%d\n", + lapb->dev, frame->pf, frame->nr); #endif - lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION; - lapb_check_need_response(lapb, frame->cr, frame->pf); - if (lapb_validate_nr(lapb, frame->nr)) { - lapb_frames_acked(lapb, frame->nr); - lapb_stop_t1timer(lapb); - lapb->n2count = 0; - lapb_requeue_frames(lapb); - } else { - lapb->frmr_data = *frame; - lapb->frmr_type = LAPB_FRMR_Z; - lapb_transmit_frmr(lapb); + lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION; + lapb_check_need_response(lapb, frame->cr, frame->pf); + if (lapb_validate_nr(lapb, frame->nr)) { + lapb_frames_acked(lapb, frame->nr); + lapb_stop_t1timer(lapb); + lapb->n2count = 0; + lapb_requeue_frames(lapb); + } else { + lapb->frmr_data = *frame; + lapb->frmr_type = LAPB_FRMR_Z; + lapb_transmit_frmr(lapb); #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", - lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->dev); #endif - lapb_start_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb->state = LAPB_STATE_4; - lapb->n2count = 0; - } - break; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; + } + break; - case LAPB_I: + case LAPB_I: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 RX I(%d) S%d R%d\n", - lapb->dev, frame->pf, frame->ns, frame->nr); + printk(KERN_DEBUG "lapb: (%p) S3 RX I(%d) S%d R%d\n", + lapb->dev, frame->pf, frame->ns, frame->nr); #endif - if (!lapb_validate_nr(lapb, frame->nr)) { - lapb->frmr_data = *frame; - lapb->frmr_type = LAPB_FRMR_Z; - lapb_transmit_frmr(lapb); + if (!lapb_validate_nr(lapb, frame->nr)) { + lapb->frmr_data = *frame; + lapb->frmr_type = LAPB_FRMR_Z; + lapb_transmit_frmr(lapb); #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", - lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->dev); #endif - lapb_start_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb->state = LAPB_STATE_4; - lapb->n2count = 0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; + break; + } + if (lapb->condition & LAPB_PEER_RX_BUSY_CONDITION) + lapb_frames_acked(lapb, frame->nr); + else + lapb_check_iframes_acked(lapb, frame->nr); + + if (frame->ns == lapb->vr) { + int cn; + cn = lapb_data_indication(lapb, skb); + queued = 1; + /* + * If upper layer has dropped the frame, we + * basically ignore any further protocol + * processing. This will cause the peer + * to re-transmit the frame later like + * a frame lost on the wire. + */ + if (cn == NET_RX_DROP) { + printk(KERN_DEBUG "LAPB: rx congestion\n"); break; } - if (lapb->condition & LAPB_PEER_RX_BUSY_CONDITION) - lapb_frames_acked(lapb, frame->nr); - else - lapb_check_iframes_acked(lapb, frame->nr); - - if (frame->ns == lapb->vr) { - int cn; - cn = lapb_data_indication(lapb, skb); - queued = 1; - /* - * If upper layer has dropped the frame, we - * basically ignore any further protocol - * processing. This will cause the peer - * to re-transmit the frame later like - * a frame lost on the wire. - */ - if (cn == NET_RX_DROP) { - printk(KERN_DEBUG - "LAPB: rx congestion\n"); - break; + lapb->vr = (lapb->vr + 1) % modulus; + lapb->condition &= ~LAPB_REJECT_CONDITION; + if (frame->pf) + lapb_enquiry_response(lapb); + else { + if (!(lapb->condition & + LAPB_ACK_PENDING_CONDITION)) { + lapb->condition |= LAPB_ACK_PENDING_CONDITION; + lapb_start_t2timer(lapb); } - lapb->vr = (lapb->vr + 1) % modulus; - lapb->condition &= ~LAPB_REJECT_CONDITION; + } + } else { + if (lapb->condition & LAPB_REJECT_CONDITION) { if (frame->pf) lapb_enquiry_response(lapb); - else { - if (!(lapb->condition & - LAPB_ACK_PENDING_CONDITION)) { - lapb->condition |= LAPB_ACK_PENDING_CONDITION; - lapb_start_t2timer(lapb); - } - } } else { - if (lapb->condition & LAPB_REJECT_CONDITION) { - if (frame->pf) - lapb_enquiry_response(lapb); - } else { -#if LAPB_DEBUG > 1 - printk(KERN_DEBUG - "lapb: (%p) S3 TX REJ(%d) R%d\n", - lapb->dev, frame->pf, lapb->vr); -#endif - lapb->condition |= LAPB_REJECT_CONDITION; - lapb_send_control(lapb, LAPB_REJ, - frame->pf, - LAPB_RESPONSE); - lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; - } +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG + "lapb: (%p) S3 TX REJ(%d) R%d\n", + lapb->dev, frame->pf, lapb->vr); +#endif + lapb->condition |= LAPB_REJECT_CONDITION; + lapb_send_control(lapb, LAPB_REJ, frame->pf, + LAPB_RESPONSE); + lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; } - break; + } + break; - case LAPB_FRMR: + case LAPB_FRMR: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 RX FRMR(%d) %02X " - "%02X %02X %02X %02X\n", lapb->dev, frame->pf, - skb->data[0], skb->data[1], skb->data[2], - skb->data[3], skb->data[4]); + printk(KERN_DEBUG "lapb: (%p) S3 RX FRMR(%d) %02X " + "%02X %02X %02X %02X\n", lapb->dev, frame->pf, + skb->data[0], skb->data[1], skb->data[2], + skb->data[3], skb->data[4]); #endif - lapb_establish_data_link(lapb); + lapb_establish_data_link(lapb); #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", - lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->dev); #endif - lapb_requeue_frames(lapb); - lapb->state = LAPB_STATE_1; - break; + lapb_requeue_frames(lapb); + lapb->state = LAPB_STATE_1; + break; - case LAPB_ILLEGAL: + case LAPB_ILLEGAL: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S3 RX ILLEGAL(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S3 RX ILLEGAL(%d)\n", + lapb->dev, frame->pf); #endif - lapb->frmr_data = *frame; - lapb->frmr_type = LAPB_FRMR_W; - lapb_transmit_frmr(lapb); + lapb->frmr_data = *frame; + lapb->frmr_type = LAPB_FRMR_W; + lapb_transmit_frmr(lapb); #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->dev); #endif - lapb_start_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb->state = LAPB_STATE_4; - lapb->n2count = 0; - break; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; + break; } if (!queued) @@ -621,75 +600,73 @@ static void lapb_state4_machine(struct lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame) { switch (frame->type) { - case LAPB_SABM: + case LAPB_SABM: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S4 RX SABM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S4 RX SABM(%d)\n", + lapb->dev, frame->pf); #endif - if (lapb->mode & LAPB_EXTENDED) { + if (lapb->mode & LAPB_EXTENDED) { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S4 TX DM(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S4 TX DM(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_DM, frame->pf, - LAPB_RESPONSE); - } else { + lapb_send_control(lapb, LAPB_DM, frame->pf, + LAPB_RESPONSE); + } else { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S4 TX UA(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S4 TX UA(%d)\n", + lapb->dev, frame->pf); #endif #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", - lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->dev); #endif - lapb_send_control(lapb, LAPB_UA, frame->pf, - LAPB_RESPONSE); - lapb_stop_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb->state = LAPB_STATE_3; - lapb->condition = 0x00; - lapb->n2count = 0; - lapb->vs = 0; - lapb->vr = 0; - lapb->va = 0; - lapb_connect_indication(lapb, LAPB_OK); - } - break; + lapb_send_control(lapb, LAPB_UA, frame->pf, + LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_3; + lapb->condition = 0x00; + lapb->n2count = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + lapb_connect_indication(lapb, LAPB_OK); + } + break; - case LAPB_SABME: + case LAPB_SABME: #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S4 RX SABME(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S4 RX SABME(%d)\n", + lapb->dev, frame->pf); #endif - if (lapb->mode & LAPB_EXTENDED) { + if (lapb->mode & LAPB_EXTENDED) { #if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S4 TX UA(%d)\n", - lapb->dev, frame->pf); + printk(KERN_DEBUG "lapb: (%p) S4 TX UA(%d)\n", + lapb->dev, frame->pf); #endif #if LAPB_DEBUG > 0 - printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", - lapb->dev); + printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->dev); #endif - lapb_send_control(lapb, LAPB_UA, frame->pf, - LAPB_RESPONSE); - lapb_stop_t1timer(lapb); - lapb_stop_t2timer(lapb); - lapb->state = LAPB_STATE_3; - lapb->condition = 0x00; - lapb->n2count = 0; - lapb->vs = 0; - lapb->vr = 0; - lapb->va = 0; - lapb_connect_indication(lapb, LAPB_OK); - } else { -#if LAPB_DEBUG > 1 - printk(KERN_DEBUG "lapb: (%p) S4 TX DM(%d)\n", - lapb->dev, frame->pf); + lapb_send_control(lapb, LAPB_UA, frame->pf, + LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_3; + lapb->condition = 0x00; + lapb->n2count = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + lapb_connect_indication(lapb, LAPB_OK); + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 TX DM(%d)\n", + lapb->dev, frame->pf); #endif - lapb_send_control(lapb, LAPB_DM, frame->pf, - LAPB_RESPONSE); - } - break; + lapb_send_control(lapb, LAPB_DM, frame->pf, + LAPB_RESPONSE); + } + break; } kfree_skb(skb); diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index e5565c7..f432d7b 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -715,13 +715,11 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock, struct llc_sock *llc = llc_sk(sk); size_t copied = 0; u32 peek_seq = 0; - u32 *seq; + u32 *seq, skb_len; unsigned long used; int target; /* Read at least this many bytes */ long timeo; - msg->msg_namelen = 0; - lock_sock(sk); copied = -ENOTCONN; if (unlikely(sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN)) @@ -815,6 +813,7 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock, } continue; found_ok_skb: + skb_len = skb->len; /* Ok so how much can we use? */ used = skb->len - offset; if (len < used) @@ -845,7 +844,7 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock, } /* Partial read */ - if (used + offset < skb->len) + if (used + offset < skb_len) continue; } while (len > 0); diff --git a/net/llc/llc_input.c b/net/llc/llc_input.c index 9032421..e32cab4 100644 --- a/net/llc/llc_input.c +++ b/net/llc/llc_input.c @@ -13,6 +13,7 @@ */ #include <linux/netdevice.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/llc.h> #include <net/llc_pdu.h> diff --git a/net/llc/llc_output.c b/net/llc/llc_output.c index b38a107..b658cba 100644 --- a/net/llc/llc_output.c +++ b/net/llc/llc_output.c @@ -18,6 +18,7 @@ #include <linux/netdevice.h> #include <linux/trdevice.h> #include <linux/skbuff.h> +#include <linux/export.h> #include <net/llc.h> #include <net/llc_pdu.h> diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c index 7af1ff2..a1839c0 100644 --- a/net/llc/llc_proc.c +++ b/net/llc/llc_proc.c @@ -17,6 +17,7 @@ #include <linux/proc_fs.h> #include <linux/errno.h> #include <linux/seq_file.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/sock.h> #include <net/llc.h> diff --git a/net/llc/sysctl_net_llc.c b/net/llc/sysctl_net_llc.c index e2ebe35..be078ec 100644 --- a/net/llc/sysctl_net_llc.c +++ b/net/llc/sysctl_net_llc.c @@ -17,28 +17,28 @@ static struct ctl_table llc2_timeout_table[] = { { .procname = "ack", .data = &sysctl_llc2_ack_timeout, - .maxlen = sizeof(long), + .maxlen = sizeof(sysctl_llc2_ack_timeout), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "busy", .data = &sysctl_llc2_busy_timeout, - .maxlen = sizeof(long), + .maxlen = sizeof(sysctl_llc2_busy_timeout), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "p", .data = &sysctl_llc2_p_timeout, - .maxlen = sizeof(long), + .maxlen = sizeof(sysctl_llc2_p_timeout), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "rej", .data = &sysctl_llc2_rej_timeout, - .maxlen = sizeof(long), + .maxlen = sizeof(sysctl_llc2_rej_timeout), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index 059af31..fe6cb43 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -576,7 +576,7 @@ static const struct file_operations ip_vs_app_fops = { }; #endif -int __net_init __ip_vs_app_init(struct net *net) +int __net_init ip_vs_app_net_init(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -585,17 +585,7 @@ int __net_init __ip_vs_app_init(struct net *net) return 0; } -void __net_exit __ip_vs_app_cleanup(struct net *net) +void __net_exit ip_vs_app_net_cleanup(struct net *net) { proc_net_remove(net, "ip_vs_app"); } - -int __init ip_vs_app_init(void) -{ - return 0; -} - - -void ip_vs_app_cleanup(void) -{ -} diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 782db27..6422845 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -616,7 +616,7 @@ struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp) if ((cp) && (!cp->dest)) { dest = ip_vs_find_dest(ip_vs_conn_net(cp), cp->af, &cp->daddr, cp->dport, &cp->vaddr, cp->vport, - cp->protocol, cp->fwmark); + cp->protocol, cp->fwmark, cp->flags); ip_vs_bind_dest(cp, dest); return dest; } else @@ -777,7 +777,6 @@ static void ip_vs_conn_expire(unsigned long data) ip_vs_control_del(cp); if (cp->flags & IP_VS_CONN_F_NFCT) { - ip_vs_conn_drop_conntrack(cp); /* Do not access conntracks during subsys cleanup * because nf_conntrack_find_get can not be used after * conntrack cleanup for the net. @@ -1255,7 +1254,7 @@ flush_again: /* * per netns init and exit */ -int __net_init __ip_vs_conn_init(struct net *net) +int __net_init ip_vs_conn_net_init(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -1266,7 +1265,7 @@ int __net_init __ip_vs_conn_init(struct net *net) return 0; } -void __net_exit __ip_vs_conn_cleanup(struct net *net) +void __net_exit ip_vs_conn_net_cleanup(struct net *net) { /* flush all the connection entries first */ ip_vs_conn_flush(net); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 0787bed..197ed93 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -188,14 +188,13 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) } -static inline int +static inline void ip_vs_set_state(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, struct ip_vs_proto_data *pd) { - if (unlikely(!pd->pp->state_transition)) - return 0; - return pd->pp->state_transition(cp, direction, skb, pd); + if (likely(pd->pp->state_transition)) + pd->pp->state_transition(cp, direction, skb, pd); } static inline int @@ -530,7 +529,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, a cache_bypass connection entry */ ipvs = net_ipvs(net); if (ipvs->sysctl_cache_bypass && svc->fwmark && unicast) { - int ret, cs; + int ret; struct ip_vs_conn *cp; unsigned int flags = (svc->flags & IP_VS_SVC_F_ONEPACKET && iph.protocol == IPPROTO_UDP)? @@ -557,7 +556,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, ip_vs_in_stats(cp, skb); /* set state */ - cs = ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); /* transmit the first SYN packet */ ret = cp->packet_xmit(skb, cp, pd->pp); @@ -663,16 +662,24 @@ static inline int ip_vs_gather_frags_v6(struct sk_buff *skb, u_int32_t user) } #endif -static int ip_vs_route_me_harder(int af, struct sk_buff *skb) +static int ip_vs_route_me_harder(int af, struct sk_buff *skb, + unsigned int hooknum) { + if (!sysctl_snat_reroute(skb)) + return 0; + /* Reroute replies only to remote clients (FORWARD and LOCAL_OUT) */ + if (NF_INET_LOCAL_IN == hooknum) + return 0; #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) { - if (sysctl_snat_reroute(skb) && ip6_route_me_harder(skb) != 0) + struct dst_entry *dst = skb_dst(skb); + + if (dst->dev && !(dst->dev->flags & IFF_LOOPBACK) && + ip6_route_me_harder(skb) != 0) return 1; } else #endif - if ((sysctl_snat_reroute(skb) || - skb_rtable(skb)->rt_flags & RTCF_LOCAL) && + if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL) && ip_route_me_harder(skb, RTN_LOCAL) != 0) return 1; @@ -783,7 +790,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb, union nf_inet_addr *snet, __u8 protocol, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, - unsigned int offset, unsigned int ihl) + unsigned int offset, unsigned int ihl, + unsigned int hooknum) { unsigned int verdict = NF_DROP; @@ -813,7 +821,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb, #endif ip_vs_nat_icmp(skb, pp, cp, 1); - if (ip_vs_route_me_harder(af, skb)) + if (ip_vs_route_me_harder(af, skb, hooknum)) goto out; /* do the statistics and put it back */ @@ -852,7 +860,7 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related, *related = 1; /* reassemble IP fragments */ - if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) { + if (ip_is_fragment(ip_hdr(skb))) { if (ip_vs_gather_frags(skb, ip_vs_defrag_user(hooknum))) return NF_STOLEN; } @@ -909,7 +917,7 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related, snet.ip = iph->saddr; return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp, - pp, offset, ihl); + pp, offset, ihl, hooknum); } #ifdef CONFIG_IP_VS_IPV6 @@ -986,7 +994,8 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related, ipv6_addr_copy(&snet.in6, &iph->saddr); return handle_response_icmp(AF_INET6, skb, &snet, cih->nexthdr, cp, - pp, offset, sizeof(struct ipv6hdr)); + pp, offset, sizeof(struct ipv6hdr), + hooknum); } #endif @@ -1019,7 +1028,7 @@ static inline int is_tcp_reset(const struct sk_buff *skb, int nh_len) */ static unsigned int handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, - struct ip_vs_conn *cp, int ihl) + struct ip_vs_conn *cp, int ihl, unsigned int hooknum) { struct ip_vs_protocol *pp = pd->pp; @@ -1057,7 +1066,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, * if it came from this machine itself. So re-compute * the routing information. */ - if (ip_vs_route_me_harder(af, skb)) + if (ip_vs_route_me_harder(af, skb, hooknum)) goto drop; IP_VS_DBG_PKT(10, af, pp, skb, 0, "After SNAT"); @@ -1156,8 +1165,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); } else #endif - if (unlikely(ip_hdr(skb)->frag_off & htons(IP_MF|IP_OFFSET) && - !pp->dont_defrag)) { + if (unlikely(ip_is_fragment(ip_hdr(skb)) && !pp->dont_defrag)) { if (ip_vs_gather_frags(skb, ip_vs_defrag_user(hooknum))) return NF_STOLEN; @@ -1171,7 +1179,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) cp = pp->conn_out_get(af, skb, &iph, iph.len, 0); if (likely(cp)) - return handle_response(af, skb, pd, cp, iph.len); + return handle_response(af, skb, pd, cp, iph.len, hooknum); if (sysctl_nat_icmp_send(net) && (pp->protocol == IPPROTO_TCP || pp->protocol == IPPROTO_UDP || @@ -1310,7 +1318,7 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) *related = 1; /* reassemble IP fragments */ - if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) { + if (ip_is_fragment(ip_hdr(skb))) { if (ip_vs_gather_frags(skb, ip_vs_defrag_user(hooknum))) return NF_STOLEN; } @@ -1384,7 +1392,7 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) offset += 2 * sizeof(__u16); verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum); - out: +out: __ip_vs_conn_put(cp); return verdict; @@ -1491,7 +1499,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) struct ip_vs_protocol *pp; struct ip_vs_proto_data *pd; struct ip_vs_conn *cp; - int ret, restart, pkts; + int ret, pkts; struct netns_ipvs *ipvs; /* Already marked as IPVS request or reply? */ @@ -1592,7 +1600,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) } ip_vs_in_stats(cp, skb); - restart = ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); if (cp->packet_xmit) ret = cp->packet_xmit(skb, cp, pp); /* do not touch skb anymore */ @@ -1879,10 +1887,9 @@ static int __net_init __ip_vs_init(struct net *net) struct netns_ipvs *ipvs; ipvs = net_generic(net, ip_vs_net_id); - if (ipvs == NULL) { - pr_err("%s(): no memory.\n", __func__); + if (ipvs == NULL) return -ENOMEM; - } + /* Hold the beast until a service is registerd */ ipvs->enable = 0; ipvs->net = net; @@ -1891,22 +1898,22 @@ static int __net_init __ip_vs_init(struct net *net) atomic_inc(&ipvs_netns_cnt); net->ipvs = ipvs; - if (__ip_vs_estimator_init(net) < 0) + if (ip_vs_estimator_net_init(net) < 0) goto estimator_fail; - if (__ip_vs_control_init(net) < 0) + if (ip_vs_control_net_init(net) < 0) goto control_fail; - if (__ip_vs_protocol_init(net) < 0) + if (ip_vs_protocol_net_init(net) < 0) goto protocol_fail; - if (__ip_vs_app_init(net) < 0) + if (ip_vs_app_net_init(net) < 0) goto app_fail; - if (__ip_vs_conn_init(net) < 0) + if (ip_vs_conn_net_init(net) < 0) goto conn_fail; - if (__ip_vs_sync_init(net) < 0) + if (ip_vs_sync_net_init(net) < 0) goto sync_fail; printk(KERN_INFO "IPVS: Creating netns size=%zu id=%d\n", @@ -1917,27 +1924,27 @@ static int __net_init __ip_vs_init(struct net *net) */ sync_fail: - __ip_vs_conn_cleanup(net); + ip_vs_conn_net_cleanup(net); conn_fail: - __ip_vs_app_cleanup(net); + ip_vs_app_net_cleanup(net); app_fail: - __ip_vs_protocol_cleanup(net); + ip_vs_protocol_net_cleanup(net); protocol_fail: - __ip_vs_control_cleanup(net); + ip_vs_control_net_cleanup(net); control_fail: - __ip_vs_estimator_cleanup(net); + ip_vs_estimator_net_cleanup(net); estimator_fail: return -ENOMEM; } static void __net_exit __ip_vs_cleanup(struct net *net) { - __ip_vs_service_cleanup(net); /* ip_vs_flush() with locks */ - __ip_vs_conn_cleanup(net); - __ip_vs_app_cleanup(net); - __ip_vs_protocol_cleanup(net); - __ip_vs_control_cleanup(net); - __ip_vs_estimator_cleanup(net); + ip_vs_service_net_cleanup(net); /* ip_vs_flush() with locks */ + ip_vs_conn_net_cleanup(net); + ip_vs_app_net_cleanup(net); + ip_vs_protocol_net_cleanup(net); + ip_vs_control_net_cleanup(net); + ip_vs_estimator_net_cleanup(net); IP_VS_DBG(2, "ipvs netns %d released\n", net_ipvs(net)->gen); } @@ -1946,7 +1953,7 @@ static void __net_exit __ip_vs_dev_cleanup(struct net *net) EnterFunction(2); net_ipvs(net)->enable = 0; /* Disable packet reception */ smp_wmb(); - __ip_vs_sync_cleanup(net); + ip_vs_sync_net_cleanup(net); LeaveFunction(2); } @@ -1968,36 +1975,23 @@ static int __init ip_vs_init(void) { int ret; - ip_vs_estimator_init(); ret = ip_vs_control_init(); if (ret < 0) { pr_err("can't setup control.\n"); - goto cleanup_estimator; + goto exit; } ip_vs_protocol_init(); - ret = ip_vs_app_init(); - if (ret < 0) { - pr_err("can't setup application helper.\n"); - goto cleanup_protocol; - } - ret = ip_vs_conn_init(); if (ret < 0) { pr_err("can't setup connection table.\n"); - goto cleanup_app; - } - - ret = ip_vs_sync_init(); - if (ret < 0) { - pr_err("can't setup sync data.\n"); - goto cleanup_conn; + goto cleanup_protocol; } ret = register_pernet_subsys(&ipvs_core_ops); /* Alloc ip_vs struct */ if (ret < 0) - goto cleanup_sync; + goto cleanup_conn; ret = register_pernet_device(&ipvs_core_dev_ops); if (ret < 0) @@ -2009,39 +2003,40 @@ static int __init ip_vs_init(void) goto cleanup_dev; } + ret = ip_vs_register_nl_ioctl(); + if (ret < 0) { + pr_err("can't register netlink/ioctl.\n"); + goto cleanup_hooks; + } + pr_info("ipvs loaded.\n"); return ret; +cleanup_hooks: + nf_unregister_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); cleanup_dev: unregister_pernet_device(&ipvs_core_dev_ops); cleanup_sub: unregister_pernet_subsys(&ipvs_core_ops); -cleanup_sync: - ip_vs_sync_cleanup(); - cleanup_conn: +cleanup_conn: ip_vs_conn_cleanup(); - cleanup_app: - ip_vs_app_cleanup(); - cleanup_protocol: +cleanup_protocol: ip_vs_protocol_cleanup(); ip_vs_control_cleanup(); - cleanup_estimator: - ip_vs_estimator_cleanup(); +exit: return ret; } static void __exit ip_vs_cleanup(void) { + ip_vs_unregister_nl_ioctl(); nf_unregister_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); unregister_pernet_device(&ipvs_core_dev_ops); unregister_pernet_subsys(&ipvs_core_ops); /* free ip_vs struct */ - ip_vs_sync_cleanup(); ip_vs_conn_cleanup(); - ip_vs_app_cleanup(); ip_vs_protocol_cleanup(); ip_vs_control_cleanup(); - ip_vs_estimator_cleanup(); pr_info("ipvs unloaded.\n"); } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index d75eb39..1e27a1f 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -619,15 +619,21 @@ struct ip_vs_dest *ip_vs_find_dest(struct net *net, int af, const union nf_inet_addr *daddr, __be16 dport, const union nf_inet_addr *vaddr, - __be16 vport, __u16 protocol, __u32 fwmark) + __be16 vport, __u16 protocol, __u32 fwmark, + __u32 flags) { struct ip_vs_dest *dest; struct ip_vs_service *svc; + __be16 port = dport; svc = ip_vs_service_get(net, af, fwmark, protocol, vaddr, vport); if (!svc) return NULL; - dest = ip_vs_lookup_dest(svc, daddr, dport); + if (fwmark && (flags & IP_VS_CONN_F_FWD_MASK) != IP_VS_CONN_F_MASQ) + port = 0; + dest = ip_vs_lookup_dest(svc, daddr, port); + if (!dest) + dest = ip_vs_lookup_dest(svc, daddr, port ^ dport); if (dest) atomic_inc(&dest->refcnt); ip_vs_service_put(svc); @@ -856,15 +862,12 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest, } dest = kzalloc(sizeof(struct ip_vs_dest), GFP_KERNEL); - if (dest == NULL) { - pr_err("%s(): no memory.\n", __func__); + if (dest == NULL) return -ENOMEM; - } + dest->stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats); - if (!dest->stats.cpustats) { - pr_err("%s() alloc_percpu failed\n", __func__); + if (!dest->stats.cpustats) goto err_alloc; - } dest->af = svc->af; dest->protocol = svc->protocol; @@ -1168,10 +1171,8 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u, goto out_err; } svc->stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats); - if (!svc->stats.cpustats) { - pr_err("%s() alloc_percpu failed\n", __func__); + if (!svc->stats.cpustats) goto out_err; - } /* I'm the first user of the service */ atomic_set(&svc->usecnt, 0); @@ -1334,9 +1335,9 @@ ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u) ip_vs_bind_pe(svc, pe); } - out_unlock: +out_unlock: write_unlock_bh(&__ip_vs_svc_lock); - out: +out: ip_vs_scheduler_put(old_sched); ip_vs_pe_put(old_pe); return ret; @@ -1483,7 +1484,7 @@ static int ip_vs_flush(struct net *net) * Delete service by {netns} in the service table. * Called by __ip_vs_cleanup() */ -void __ip_vs_service_cleanup(struct net *net) +void ip_vs_service_net_cleanup(struct net *net) { EnterFunction(2); /* Check for "full" addressed entries */ @@ -1520,12 +1521,11 @@ static int ip_vs_dst_event(struct notifier_block *this, unsigned long event, { struct net_device *dev = ptr; struct net *net = dev_net(dev); - struct netns_ipvs *ipvs = net_ipvs(net); struct ip_vs_service *svc; struct ip_vs_dest *dest; unsigned int idx; - if (event != NETDEV_UNREGISTER || !ipvs) + if (event != NETDEV_UNREGISTER) return NOTIFY_DONE; IP_VS_DBG(3, "%s() dev=%s\n", __func__, dev->name); EnterFunction(2); @@ -1551,7 +1551,7 @@ static int ip_vs_dst_event(struct notifier_block *this, unsigned long event, } } - list_for_each_entry(dest, &ipvs->dest_trash, n_list) { + list_for_each_entry(dest, &net_ipvs(net)->dest_trash, n_list) { __ip_vs_dev_reset(dest, dev); } mutex_unlock(&__ip_vs_mutex); @@ -1663,7 +1663,7 @@ proc_do_sync_mode(ctl_table *table, int write, /* * IPVS sysctl table (under the /proc/sys/net/ipv4/vs/) * Do not change order or insert new entries without - * align with netns init in __ip_vs_control_init() + * align with netns init in ip_vs_control_net_init() */ static struct ctl_table vs_vars[] = { @@ -2284,6 +2284,7 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) struct ip_vs_service *svc; struct ip_vs_dest_user *udest_compat; struct ip_vs_dest_user_kern udest; + struct netns_ipvs *ipvs = net_ipvs(net); if (!capable(CAP_NET_ADMIN)) return -EPERM; @@ -2304,6 +2305,24 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) /* increase the module use count */ ip_vs_use_count_inc(); + /* Handle daemons since they have another lock */ + if (cmd == IP_VS_SO_SET_STARTDAEMON || + cmd == IP_VS_SO_SET_STOPDAEMON) { + struct ip_vs_daemon_user *dm = (struct ip_vs_daemon_user *)arg; + + if (mutex_lock_interruptible(&ipvs->sync_mutex)) { + ret = -ERESTARTSYS; + goto out_dec; + } + if (cmd == IP_VS_SO_SET_STARTDAEMON) + ret = start_sync_thread(net, dm->state, dm->mcast_ifn, + dm->syncid); + else + ret = stop_sync_thread(net, dm->state); + mutex_unlock(&ipvs->sync_mutex); + goto out_dec; + } + if (mutex_lock_interruptible(&__ip_vs_mutex)) { ret = -ERESTARTSYS; goto out_dec; @@ -2317,15 +2336,6 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) /* Set timeout values for (tcp tcpfin udp) */ ret = ip_vs_set_timeout(net, (struct ip_vs_timeout_user *)arg); goto out_unlock; - } else if (cmd == IP_VS_SO_SET_STARTDAEMON) { - struct ip_vs_daemon_user *dm = (struct ip_vs_daemon_user *)arg; - ret = start_sync_thread(net, dm->state, dm->mcast_ifn, - dm->syncid); - goto out_unlock; - } else if (cmd == IP_VS_SO_SET_STOPDAEMON) { - struct ip_vs_daemon_user *dm = (struct ip_vs_daemon_user *)arg; - ret = stop_sync_thread(net, dm->state); - goto out_unlock; } usvc_compat = (struct ip_vs_service_user *)arg; @@ -2470,7 +2480,7 @@ __ip_vs_get_service_entries(struct net *net, count++; } } - out: +out: return ret; } @@ -2585,6 +2595,33 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) if (copy_from_user(arg, user, copylen) != 0) return -EFAULT; + /* + * Handle daemons first since it has its own locking + */ + if (cmd == IP_VS_SO_GET_DAEMON) { + struct ip_vs_daemon_user d[2]; + + memset(&d, 0, sizeof(d)); + if (mutex_lock_interruptible(&ipvs->sync_mutex)) + return -ERESTARTSYS; + + if (ipvs->sync_state & IP_VS_STATE_MASTER) { + d[0].state = IP_VS_STATE_MASTER; + strlcpy(d[0].mcast_ifn, ipvs->master_mcast_ifn, + sizeof(d[0].mcast_ifn)); + d[0].syncid = ipvs->master_syncid; + } + if (ipvs->sync_state & IP_VS_STATE_BACKUP) { + d[1].state = IP_VS_STATE_BACKUP; + strlcpy(d[1].mcast_ifn, ipvs->backup_mcast_ifn, + sizeof(d[1].mcast_ifn)); + d[1].syncid = ipvs->backup_syncid; + } + if (copy_to_user(user, &d, sizeof(d)) != 0) + ret = -EFAULT; + mutex_unlock(&ipvs->sync_mutex); + return ret; + } if (mutex_lock_interruptible(&__ip_vs_mutex)) return -ERESTARTSYS; @@ -2683,33 +2720,11 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) } break; - case IP_VS_SO_GET_DAEMON: - { - struct ip_vs_daemon_user d[2]; - - memset(&d, 0, sizeof(d)); - if (ipvs->sync_state & IP_VS_STATE_MASTER) { - d[0].state = IP_VS_STATE_MASTER; - strlcpy(d[0].mcast_ifn, ipvs->master_mcast_ifn, - sizeof(d[0].mcast_ifn)); - d[0].syncid = ipvs->master_syncid; - } - if (ipvs->sync_state & IP_VS_STATE_BACKUP) { - d[1].state = IP_VS_STATE_BACKUP; - strlcpy(d[1].mcast_ifn, ipvs->backup_mcast_ifn, - sizeof(d[1].mcast_ifn)); - d[1].syncid = ipvs->backup_syncid; - } - if (copy_to_user(user, &d, sizeof(d)) != 0) - ret = -EFAULT; - } - break; - default: ret = -EINVAL; } - out: +out: mutex_unlock(&__ip_vs_mutex); return ret; } @@ -3207,7 +3222,7 @@ static int ip_vs_genl_dump_daemons(struct sk_buff *skb, struct net *net = skb_sknet(skb); struct netns_ipvs *ipvs = net_ipvs(net); - mutex_lock(&__ip_vs_mutex); + mutex_lock(&ipvs->sync_mutex); if ((ipvs->sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) { if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_MASTER, ipvs->master_mcast_ifn, @@ -3227,7 +3242,7 @@ static int ip_vs_genl_dump_daemons(struct sk_buff *skb, } nla_put_failure: - mutex_unlock(&__ip_vs_mutex); + mutex_unlock(&ipvs->sync_mutex); return skb->len; } @@ -3273,13 +3288,9 @@ static int ip_vs_genl_set_config(struct net *net, struct nlattr **attrs) return ip_vs_set_timeout(net, &t); } -static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) +static int ip_vs_genl_set_daemon(struct sk_buff *skb, struct genl_info *info) { - struct ip_vs_service *svc = NULL; - struct ip_vs_service_user_kern usvc; - struct ip_vs_dest_user_kern udest; int ret = 0, cmd; - int need_full_svc = 0, need_full_dest = 0; struct net *net; struct netns_ipvs *ipvs; @@ -3287,19 +3298,10 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) ipvs = net_ipvs(net); cmd = info->genlhdr->cmd; - mutex_lock(&__ip_vs_mutex); - - if (cmd == IPVS_CMD_FLUSH) { - ret = ip_vs_flush(net); - goto out; - } else if (cmd == IPVS_CMD_SET_CONFIG) { - ret = ip_vs_genl_set_config(net, info->attrs); - goto out; - } else if (cmd == IPVS_CMD_NEW_DAEMON || - cmd == IPVS_CMD_DEL_DAEMON) { - + if (cmd == IPVS_CMD_NEW_DAEMON || cmd == IPVS_CMD_DEL_DAEMON) { struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1]; + mutex_lock(&ipvs->sync_mutex); if (!info->attrs[IPVS_CMD_ATTR_DAEMON] || nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX, info->attrs[IPVS_CMD_ATTR_DAEMON], @@ -3312,6 +3314,31 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) ret = ip_vs_genl_new_daemon(net, daemon_attrs); else ret = ip_vs_genl_del_daemon(net, daemon_attrs); +out: + mutex_unlock(&ipvs->sync_mutex); + } + return ret; +} + +static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) +{ + struct ip_vs_service *svc = NULL; + struct ip_vs_service_user_kern usvc; + struct ip_vs_dest_user_kern udest; + int ret = 0, cmd; + int need_full_svc = 0, need_full_dest = 0; + struct net *net; + + net = skb_sknet(skb); + cmd = info->genlhdr->cmd; + + mutex_lock(&__ip_vs_mutex); + + if (cmd == IPVS_CMD_FLUSH) { + ret = ip_vs_flush(net); + goto out; + } else if (cmd == IPVS_CMD_SET_CONFIG) { + ret = ip_vs_genl_set_config(net, info->attrs); goto out; } else if (cmd == IPVS_CMD_ZERO && !info->attrs[IPVS_CMD_ATTR_SERVICE]) { @@ -3394,10 +3421,8 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info) void *reply; int ret, cmd, reply_cmd; struct net *net; - struct netns_ipvs *ipvs; net = skb_sknet(skb); - ipvs = net_ipvs(net); cmd = info->genlhdr->cmd; if (cmd == IPVS_CMD_GET_SERVICE) @@ -3538,13 +3563,13 @@ static struct genl_ops ip_vs_genl_ops[] __read_mostly = { .cmd = IPVS_CMD_NEW_DAEMON, .flags = GENL_ADMIN_PERM, .policy = ip_vs_cmd_policy, - .doit = ip_vs_genl_set_cmd, + .doit = ip_vs_genl_set_daemon, }, { .cmd = IPVS_CMD_DEL_DAEMON, .flags = GENL_ADMIN_PERM, .policy = ip_vs_cmd_policy, - .doit = ip_vs_genl_set_cmd, + .doit = ip_vs_genl_set_daemon, }, { .cmd = IPVS_CMD_GET_DAEMON, @@ -3597,7 +3622,7 @@ static void ip_vs_genl_unregister(void) * per netns intit/exit func. */ #ifdef CONFIG_SYSCTL -int __net_init __ip_vs_control_init_sysctl(struct net *net) +int __net_init ip_vs_control_net_init_sysctl(struct net *net) { int idx; struct netns_ipvs *ipvs = net_ipvs(net); @@ -3656,19 +3681,23 @@ int __net_init __ip_vs_control_init_sysctl(struct net *net) return 0; } -void __net_init __ip_vs_control_cleanup_sysctl(struct net *net) +void __net_init ip_vs_control_net_cleanup_sysctl(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); cancel_delayed_work_sync(&ipvs->defense_work); cancel_work_sync(&ipvs->defense_work.work); unregister_net_sysctl_table(ipvs->sysctl_hdr); + ip_vs_stop_estimator(net, &ipvs->tot_stats); + + if (!net_eq(net, &init_net)) + kfree(ipvs->sysctl_tbl); } #else -int __net_init __ip_vs_control_init_sysctl(struct net *net) { return 0; } -void __net_init __ip_vs_control_cleanup_sysctl(struct net *net) { } +int __net_init ip_vs_control_net_init_sysctl(struct net *net) { return 0; } +void __net_init ip_vs_control_net_cleanup_sysctl(struct net *net) { } #endif @@ -3676,12 +3705,12 @@ static struct notifier_block ip_vs_dst_notifier = { .notifier_call = ip_vs_dst_event, }; -int __net_init __ip_vs_control_init(struct net *net) +int __net_init ip_vs_control_net_init(struct net *net) { int idx; struct netns_ipvs *ipvs = net_ipvs(net); - ipvs->rs_lock = __RW_LOCK_UNLOCKED(ipvs->rs_lock); + rwlock_init(&ipvs->rs_lock); /* Initialize rs_table */ for (idx = 0; idx < IP_VS_RTAB_SIZE; idx++) @@ -3693,10 +3722,9 @@ int __net_init __ip_vs_control_init(struct net *net) /* procfs stats */ ipvs->tot_stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats); - if (!ipvs->tot_stats.cpustats) { - pr_err("%s(): alloc_percpu.\n", __func__); + if (!ipvs->tot_stats.cpustats) return -ENOMEM; - } + spin_lock_init(&ipvs->tot_stats.lock); proc_net_fops_create(net, "ip_vs", 0, &ip_vs_info_fops); @@ -3704,7 +3732,7 @@ int __net_init __ip_vs_control_init(struct net *net) proc_net_fops_create(net, "ip_vs_stats_percpu", 0, &ip_vs_stats_percpu_fops); - if (__ip_vs_control_init_sysctl(net)) + if (ip_vs_control_net_init_sysctl(net)) goto err; return 0; @@ -3714,34 +3742,22 @@ err: return -ENOMEM; } -void __net_exit __ip_vs_control_cleanup(struct net *net) +void __net_exit ip_vs_control_net_cleanup(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); ip_vs_trash_cleanup(net); - ip_vs_stop_estimator(net, &ipvs->tot_stats); - __ip_vs_control_cleanup_sysctl(net); + ip_vs_control_net_cleanup_sysctl(net); proc_net_remove(net, "ip_vs_stats_percpu"); proc_net_remove(net, "ip_vs_stats"); proc_net_remove(net, "ip_vs"); free_percpu(ipvs->tot_stats.cpustats); } -int __init ip_vs_control_init(void) +int __init ip_vs_register_nl_ioctl(void) { - int idx; int ret; - EnterFunction(2); - - /* Initialize svc_table, ip_vs_svc_fwm_table, rs_table */ - for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { - INIT_LIST_HEAD(&ip_vs_svc_table[idx]); - INIT_LIST_HEAD(&ip_vs_svc_fwm_table[idx]); - } - - smp_wmb(); /* Do we really need it now ? */ - ret = nf_register_sockopt(&ip_vs_sockopts); if (ret) { pr_err("cannot register sockopt.\n"); @@ -3753,28 +3769,47 @@ int __init ip_vs_control_init(void) pr_err("cannot register Generic Netlink interface.\n"); goto err_genl; } - - ret = register_netdevice_notifier(&ip_vs_dst_notifier); - if (ret < 0) - goto err_notf; - - LeaveFunction(2); return 0; -err_notf: - ip_vs_genl_unregister(); err_genl: nf_unregister_sockopt(&ip_vs_sockopts); err_sock: return ret; } +void ip_vs_unregister_nl_ioctl(void) +{ + ip_vs_genl_unregister(); + nf_unregister_sockopt(&ip_vs_sockopts); +} + +int __init ip_vs_control_init(void) +{ + int idx; + int ret; + + EnterFunction(2); + + /* Initialize svc_table, ip_vs_svc_fwm_table, rs_table */ + for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { + INIT_LIST_HEAD(&ip_vs_svc_table[idx]); + INIT_LIST_HEAD(&ip_vs_svc_fwm_table[idx]); + } + + smp_wmb(); /* Do we really need it now ? */ + + ret = register_netdevice_notifier(&ip_vs_dst_notifier); + if (ret < 0) + return ret; + + LeaveFunction(2); + return 0; +} + void ip_vs_control_cleanup(void) { EnterFunction(2); unregister_netdevice_notifier(&ip_vs_dst_notifier); - ip_vs_genl_unregister(); - nf_unregister_sockopt(&ip_vs_sockopts); LeaveFunction(2); } diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c index 95fd0d1..1c269e5 100644 --- a/net/netfilter/ipvs/ip_vs_dh.c +++ b/net/netfilter/ipvs/ip_vs_dh.c @@ -150,10 +150,9 @@ static int ip_vs_dh_init_svc(struct ip_vs_service *svc) /* allocate the DH table for this service */ tbl = kmalloc(sizeof(struct ip_vs_dh_bucket)*IP_VS_DH_TAB_SIZE, GFP_ATOMIC); - if (tbl == NULL) { - pr_err("%s(): no memory\n", __func__); + if (tbl == NULL) return -ENOMEM; - } + svc->sched_data = tbl; IP_VS_DBG(6, "DH hash table (memory=%Zdbytes) allocated for " "current service\n", diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index 508cce9..0fac601 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -192,7 +192,7 @@ void ip_vs_read_estimator(struct ip_vs_stats_user *dst, dst->outbps = (e->outbps + 0xF) >> 5; } -int __net_init __ip_vs_estimator_init(struct net *net) +int __net_init ip_vs_estimator_net_init(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -203,16 +203,7 @@ int __net_init __ip_vs_estimator_init(struct net *net) return 0; } -void __net_exit __ip_vs_estimator_cleanup(struct net *net) +void __net_exit ip_vs_estimator_net_cleanup(struct net *net) { del_timer_sync(&net_ipvs(net)->est_timer); } - -int __init ip_vs_estimator_init(void) -{ - return 0; -} - -void ip_vs_estimator_cleanup(void) -{ -} diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index af63553..365163f 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -44,16 +44,17 @@ #include <net/ip_vs.h> -#define SERVER_STRING "227 Entering Passive Mode (" -#define CLIENT_STRING "PORT " +#define SERVER_STRING "227 " +#define CLIENT_STRING "PORT" /* * List of ports (up to IP_VS_APP_MAX_PORTS) to be handled by helper * First port is set to the default port. */ +static unsigned int ports_count = 1; static unsigned short ports[IP_VS_APP_MAX_PORTS] = {21, 0}; -module_param_array(ports, ushort, NULL, 0); +module_param_array(ports, ushort, &ports_count, 0444); MODULE_PARM_DESC(ports, "Ports to monitor for FTP control commands"); @@ -79,14 +80,17 @@ ip_vs_ftp_done_conn(struct ip_vs_app *app, struct ip_vs_conn *cp) /* * Get <addr,port> from the string "xxx.xxx.xxx.xxx,ppp,ppp", started - * with the "pattern" and terminated with the "term" character. + * with the "pattern", ignoring before "skip" and terminated with + * the "term" character. * <addr,port> is in network order. */ static int ip_vs_ftp_get_addrport(char *data, char *data_limit, - const char *pattern, size_t plen, char term, + const char *pattern, size_t plen, + char skip, char term, __be32 *addr, __be16 *port, char **start, char **end) { + char *s, c; unsigned char p[6]; int i = 0; @@ -101,19 +105,38 @@ static int ip_vs_ftp_get_addrport(char *data, char *data_limit, if (strnicmp(data, pattern, plen) != 0) { return 0; } - *start = data + plen; + s = data + plen; + if (skip) { + int found = 0; + + for (;; s++) { + if (s == data_limit) + return -1; + if (!found) { + if (*s == skip) + found = 1; + } else if (*s != skip) { + break; + } + } + } - for (data = *start; *data != term; data++) { + for (data = s; ; data++) { if (data == data_limit) return -1; + if (*data == term) + break; } *end = data; memset(p, 0, sizeof(p)); - for (data = *start; data != *end; data++) { - if (*data >= '0' && *data <= '9') { - p[i] = p[i]*10 + *data - '0'; - } else if (*data == ',' && i < 5) { + for (data = s; ; data++) { + c = *data; + if (c == term) + break; + if (c >= '0' && c <= '9') { + p[i] = p[i]*10 + c - '0'; + } else if (c == ',' && i < 5) { i++; } else { /* unexpected character */ @@ -124,8 +147,9 @@ static int ip_vs_ftp_get_addrport(char *data, char *data_limit, if (i != 5) return -1; - *addr = get_unaligned((__be32 *)p); - *port = get_unaligned((__be16 *)(p + 4)); + *start = s; + *addr = get_unaligned((__be32 *) p); + *port = get_unaligned((__be16 *) (p + 4)); return 1; } @@ -159,6 +183,8 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, struct nf_conn *ct; struct net *net; + *diff = 0; + #ifdef CONFIG_IP_VS_IPV6 /* This application helper doesn't work with IPv6 yet, * so turn this into a no-op for IPv6 packets @@ -167,8 +193,6 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, return 1; #endif - *diff = 0; - /* Only useful for established sessions */ if (cp->state != IP_VS_TCP_S_ESTABLISHED) return 1; @@ -185,7 +209,8 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, if (ip_vs_ftp_get_addrport(data, data_limit, SERVER_STRING, - sizeof(SERVER_STRING)-1, ')', + sizeof(SERVER_STRING)-1, + '(', ')', &from.ip, &port, &start, &end) != 1) return 1; @@ -293,6 +318,9 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, struct ip_vs_conn *n_cp; struct net *net; + /* no diff required for incoming packets */ + *diff = 0; + #ifdef CONFIG_IP_VS_IPV6 /* This application helper doesn't work with IPv6 yet, * so turn this into a no-op for IPv6 packets @@ -301,9 +329,6 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, return 1; #endif - /* no diff required for incoming packets */ - *diff = 0; - /* Only useful for established sessions */ if (cp->state != IP_VS_TCP_S_ESTABLISHED) return 1; @@ -345,7 +370,7 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, */ if (ip_vs_ftp_get_addrport(data_start, data_limit, CLIENT_STRING, sizeof(CLIENT_STRING)-1, - '\r', &to.ip, &port, + ' ', '\r', &to.ip, &port, &start, &end) != 1) return 1; @@ -425,7 +450,7 @@ static int __net_init __ip_vs_ftp_init(struct net *net) if (ret) goto err_exit; - for (i=0; i<IP_VS_APP_MAX_PORTS; i++) { + for (i = 0; i < ports_count; i++) { if (!ports[i]) continue; ret = register_ip_vs_app_inc(net, app, app->protocol, ports[i]); diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 87e40ea..0f16283 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -202,10 +202,8 @@ ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, const union nf_inet_addr *daddr, en = ip_vs_lblc_get(dest->af, tbl, daddr); if (!en) { en = kmalloc(sizeof(*en), GFP_ATOMIC); - if (!en) { - pr_err("%s(): no memory\n", __func__); + if (!en) return NULL; - } en->af = dest->af; ip_vs_addr_copy(dest->af, &en->addr, daddr); @@ -345,10 +343,9 @@ static int ip_vs_lblc_init_svc(struct ip_vs_service *svc) * Allocate the ip_vs_lblc_table for this service */ tbl = kmalloc(sizeof(*tbl), GFP_ATOMIC); - if (tbl == NULL) { - pr_err("%s(): no memory\n", __func__); + if (tbl == NULL) return -ENOMEM; - } + svc->sched_data = tbl; IP_VS_DBG(6, "LBLC hash table (memory=%Zdbytes) allocated for " "current service\n", sizeof(*tbl)); diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index 90f618a..eec797f 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -112,10 +112,8 @@ ip_vs_dest_set_insert(struct ip_vs_dest_set *set, struct ip_vs_dest *dest) } e = kmalloc(sizeof(*e), GFP_ATOMIC); - if (e == NULL) { - pr_err("%s(): no memory\n", __func__); + if (e == NULL) return NULL; - } atomic_inc(&dest->refcnt); e->dest = dest; @@ -373,10 +371,8 @@ ip_vs_lblcr_new(struct ip_vs_lblcr_table *tbl, const union nf_inet_addr *daddr, en = ip_vs_lblcr_get(dest->af, tbl, daddr); if (!en) { en = kmalloc(sizeof(*en), GFP_ATOMIC); - if (!en) { - pr_err("%s(): no memory\n", __func__); + if (!en) return NULL; - } en->af = dest->af; ip_vs_addr_copy(dest->af, &en->addr, daddr); @@ -516,10 +512,9 @@ static int ip_vs_lblcr_init_svc(struct ip_vs_service *svc) * Allocate the ip_vs_lblcr_table for this service */ tbl = kmalloc(sizeof(*tbl), GFP_ATOMIC); - if (tbl == NULL) { - pr_err("%s(): no memory\n", __func__); + if (tbl == NULL) return -ENOMEM; - } + svc->sched_data = tbl; IP_VS_DBG(6, "LBLCR hash table (memory=%Zdbytes) allocated for " "current service\n", sizeof(*tbl)); diff --git a/net/netfilter/ipvs/ip_vs_nfct.c b/net/netfilter/ipvs/ip_vs_nfct.c index f454c80..022e77e 100644 --- a/net/netfilter/ipvs/ip_vs_nfct.c +++ b/net/netfilter/ipvs/ip_vs_nfct.c @@ -127,7 +127,7 @@ ip_vs_update_conntrack(struct sk_buff *skb, struct ip_vs_conn *cp, int outin) nf_conntrack_alter_reply(ct, &new_tuple); } -int ip_vs_confirm_conntrack(struct sk_buff *skb, struct ip_vs_conn *cp) +int ip_vs_confirm_conntrack(struct sk_buff *skb) { return nf_conntrack_confirm(skb); } diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index eb86028..8531293 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -74,10 +74,9 @@ register_ip_vs_proto_netns(struct net *net, struct ip_vs_protocol *pp) struct ip_vs_proto_data *pd = kzalloc(sizeof(struct ip_vs_proto_data), GFP_ATOMIC); - if (!pd) { - pr_err("%s(): no memory.\n", __func__); + if (!pd) return -ENOMEM; - } + pd->pp = pp; /* For speed issues */ pd->next = ipvs->proto_data_table[hash]; ipvs->proto_data_table[hash] = pd; @@ -316,7 +315,7 @@ ip_vs_tcpudp_debug_packet(int af, struct ip_vs_protocol *pp, /* * per network name-space init */ -int __net_init __ip_vs_protocol_init(struct net *net) +int __net_init ip_vs_protocol_net_init(struct net *net) { #ifdef CONFIG_IP_VS_PROTO_TCP register_ip_vs_proto_netns(net, &ip_vs_protocol_tcp); @@ -336,7 +335,7 @@ int __net_init __ip_vs_protocol_init(struct net *net) return 0; } -void __net_exit __ip_vs_protocol_cleanup(struct net *net) +void __net_exit ip_vs_protocol_net_cleanup(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); struct ip_vs_proto_data *pd; diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index d12ed53..1fbf7a2 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -906,7 +906,7 @@ static const char *sctp_state_name(int state) return "?"; } -static inline int +static inline void set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, int direction, const struct sk_buff *skb) { @@ -924,7 +924,7 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, sch = skb_header_pointer(skb, ihl + sizeof(sctp_sctphdr_t), sizeof(_sctpch), &_sctpch); if (sch == NULL) - return 0; + return; chunk_type = sch->type; /* @@ -993,21 +993,15 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, cp->timeout = pd->timeout_table[cp->state = next_state]; else /* What to do ? */ cp->timeout = sctp_timeouts[cp->state = next_state]; - - return 1; } -static int +static void sctp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, struct ip_vs_proto_data *pd) { - int ret = 0; - spin_lock(&cp->lock); - ret = set_sctp_state(pd, cp, direction, skb); + set_sctp_state(pd, cp, direction, skb); spin_unlock(&cp->lock); - - return ret; } static inline __u16 sctp_app_hashkey(__be16 port) diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index c0cc341..ef8641f 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -546,7 +546,7 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, /* * Handle state transitions */ -static int +static void tcp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, struct ip_vs_proto_data *pd) @@ -561,13 +561,11 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction, th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph); if (th == NULL) - return 0; + return; spin_lock(&cp->lock); set_tcp_state(pd, cp, direction, th); spin_unlock(&cp->lock); - - return 1; } static inline __u16 tcp_app_hashkey(__be16 port) diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index f1282cb..f4b7262 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -454,18 +454,17 @@ static const char * udp_state_name(int state) return udp_state_name_table[state] ? udp_state_name_table[state] : "?"; } -static int +static void udp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, struct ip_vs_proto_data *pd) { if (unlikely(!pd)) { pr_err("UDP no ns data\n"); - return 0; + return; } cp->timeout = pd->timeout_table[IP_VS_UDP_S_NORMAL]; - return 1; } static void __udp_init(struct net *net, struct ip_vs_proto_data *pd) diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c index b5e2556..33815f4 100644 --- a/net/netfilter/ipvs/ip_vs_sh.c +++ b/net/netfilter/ipvs/ip_vs_sh.c @@ -147,10 +147,9 @@ static int ip_vs_sh_init_svc(struct ip_vs_service *svc) /* allocate the SH table for this service */ tbl = kmalloc(sizeof(struct ip_vs_sh_bucket)*IP_VS_SH_TAB_SIZE, GFP_ATOMIC); - if (tbl == NULL) { - pr_err("%s(): no memory\n", __func__); + if (tbl == NULL) return -ENOMEM; - } + svc->sched_data = tbl; IP_VS_DBG(6, "SH hash table (memory=%Zdbytes) allocated for " "current service\n", diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index e292e5b..2cbcc83 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -61,6 +61,7 @@ #define SYNC_PROTO_VER 1 /* Protocol version in header */ +static struct lock_class_key __ipvs_sync_key; /* * IPVS sync connection entry * Version 0, i.e. original version. @@ -739,7 +740,7 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param, * but still handled. */ dest = ip_vs_find_dest(net, type, daddr, dport, param->vaddr, - param->vport, protocol, fwmark); + param->vport, protocol, fwmark, flags); /* Set the approprite ativity flag */ if (protocol == IPPROTO_TCP) { @@ -762,6 +763,8 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param, IP_VS_DBG(2, "BACKUP, add new conn. failed\n"); return; } + if (!(flags & IP_VS_CONN_F_TEMPLATE)) + kfree(param->pe_data); } else if (!cp->dest) { dest = ip_vs_try_bind_dest(cp); if (dest) @@ -1063,6 +1066,7 @@ static inline int ip_vs_proc_sync_conn(struct net *net, __u8 *p, __u8 *msg_end) (opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL) ); #endif + ip_vs_pe_put(param.pe); return 0; /* Error exit */ out: @@ -1545,6 +1549,7 @@ int start_sync_thread(struct net *net, int state, char *mcast_ifn, __u8 syncid) IP_VS_DBG(7, "Each ip_vs_sync_conn entry needs %Zd bytes\n", sizeof(struct ip_vs_sync_conn_v0)); + if (state == IP_VS_STATE_MASTER) { if (ipvs->master_thread) return -EEXIST; @@ -1663,10 +1668,11 @@ int stop_sync_thread(struct net *net, int state) /* * Initialize data struct for each netns */ -int __net_init __ip_vs_sync_init(struct net *net) +int __net_init ip_vs_sync_net_init(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); + __mutex_init(&ipvs->sync_mutex, "ipvs->sync_mutex", &__ipvs_sync_key); INIT_LIST_HEAD(&ipvs->sync_queue); spin_lock_init(&ipvs->sync_lock); spin_lock_init(&ipvs->sync_buff_lock); @@ -1677,10 +1683,12 @@ int __net_init __ip_vs_sync_init(struct net *net) return 0; } -void __ip_vs_sync_cleanup(struct net *net) +void ip_vs_sync_net_cleanup(struct net *net) { int retc; + struct netns_ipvs *ipvs = net_ipvs(net); + mutex_lock(&ipvs->sync_mutex); retc = stop_sync_thread(net, IP_VS_STATE_MASTER); if (retc && retc != -ESRCH) pr_err("Failed to stop Master Daemon\n"); @@ -1688,13 +1696,5 @@ void __ip_vs_sync_cleanup(struct net *net) retc = stop_sync_thread(net, IP_VS_STATE_BACKUP); if (retc && retc != -ESRCH) pr_err("Failed to stop Backup Daemon\n"); -} - -int __init ip_vs_sync_init(void) -{ - return 0; -} - -void ip_vs_sync_cleanup(void) -{ + mutex_unlock(&ipvs->sync_mutex); } diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c index 1ef41f5..fd0d4e0 100644 --- a/net/netfilter/ipvs/ip_vs_wrr.c +++ b/net/netfilter/ipvs/ip_vs_wrr.c @@ -85,10 +85,9 @@ static int ip_vs_wrr_init_svc(struct ip_vs_service *svc) * Allocate the mark variable for WRR scheduling */ mark = kmalloc(sizeof(struct ip_vs_wrr_mark), GFP_ATOMIC); - if (mark == NULL) { - pr_err("%s(): no memory\n", __func__); + if (mark == NULL) return -ENOMEM; - } + mark->cl = &svc->destinations; mark->cw = 0; mark->mw = ip_vs_wrr_max_weight(svc); diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index ab67dd1..cc8f8b4 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -339,7 +339,7 @@ ip_vs_dst_reset(struct ip_vs_dest *dest) \ (skb)->ipvs_property = 1; \ if (unlikely((cp)->flags & IP_VS_CONN_F_NFCT)) \ - __ret = ip_vs_confirm_conntrack(skb, cp); \ + __ret = ip_vs_confirm_conntrack(skb); \ if (__ret == NF_ACCEPT) { \ nf_reset(skb); \ skb_forward_csum(skb); \ @@ -853,7 +853,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, iph->daddr = cp->daddr.ip; iph->saddr = saddr; iph->ttl = old_iph->ttl; - ip_select_ident(skb, &rt->dst, NULL); + ip_select_ident(skb, NULL); /* Another hack: avoid icmp_send in ip_fragment */ skb->local_df = 1; diff --git a/net/rds/bind.c b/net/rds/bind.c index 2f6b3fc..637bde5 100644 --- a/net/rds/bind.c +++ b/net/rds/bind.c @@ -35,6 +35,7 @@ #include <linux/in.h> #include <linux/if_arp.h> #include <linux/jhash.h> +#include <linux/ratelimit.h> #include "rds.h" #define BIND_HASH_SIZE 1024 @@ -185,8 +186,7 @@ int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if (!trans) { ret = -EADDRNOTAVAIL; rds_remove_bound(rs); - if (printk_ratelimit()) - printk(KERN_INFO "RDS: rds_bind() could not find a transport, " + printk_ratelimited(KERN_INFO "RDS: rds_bind() could not find a transport, " "load rds_tcp or rds_rdma?\n"); goto out; } diff --git a/net/rds/cong.c b/net/rds/cong.c index 6daaa49..e5b65ac 100644 --- a/net/rds/cong.c +++ b/net/rds/cong.c @@ -34,6 +34,7 @@ #include <linux/types.h> #include <linux/rbtree.h> #include <linux/bitops.h> +#include <linux/export.h> #include "rds.h" diff --git a/net/rds/connection.c b/net/rds/connection.c index 9334d89..be3eecd 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -33,6 +33,7 @@ #include <linux/kernel.h> #include <linux/list.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/inet_hashtables.h> #include "rds.h" @@ -177,6 +178,12 @@ static struct rds_connection *__rds_conn_create(__be32 laddr, __be32 faddr, } } + if (trans == NULL) { + kmem_cache_free(rds_conn_slab, conn); + conn = ERR_PTR(-ENODEV); + goto out; + } + conn->c_trans = trans; ret = trans->conn_alloc(conn, gfp); diff --git a/net/rds/ib.c b/net/rds/ib.c index 3b83086..ba2dffe 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -38,6 +38,7 @@ #include <linux/if_arp.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/module.h> #include "rds.h" #include "ib.h" @@ -337,7 +338,8 @@ static int rds_ib_laddr_check(__be32 addr) ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); /* due to this, we will claim to support iWARP devices unless we check node_type. */ - if (ret || cm_id->device->node_type != RDMA_NODE_IB_CA) + if (ret || !cm_id->device || + cm_id->device->node_type != RDMA_NODE_IB_CA) ret = -EADDRNOTAVAIL; rdsdebug("addr %pI4 ret %d node type %d\n", diff --git a/net/rds/ib.h b/net/rds/ib.h index 4297d92..edfaaaf 100644 --- a/net/rds/ib.h +++ b/net/rds/ib.h @@ -3,6 +3,7 @@ #include <rdma/ib_verbs.h> #include <rdma/rdma_cm.h> +#include <linux/interrupt.h> #include <linux/pci.h> #include <linux/slab.h> #include "rds.h" diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index fd453dd..51c8689 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -34,6 +34,7 @@ #include <linux/in.h> #include <linux/slab.h> #include <linux/vmalloc.h> +#include <linux/ratelimit.h> #include "rds.h" #include "ib.h" @@ -374,23 +375,21 @@ static int rds_ib_setup_qp(struct rds_connection *conn) goto out; } - ic->i_sends = vmalloc_node(ic->i_send_ring.w_nr * sizeof(struct rds_ib_send_work), + ic->i_sends = vzalloc_node(ic->i_send_ring.w_nr * sizeof(struct rds_ib_send_work), ibdev_to_node(dev)); if (!ic->i_sends) { ret = -ENOMEM; rdsdebug("send allocation failed\n"); goto out; } - memset(ic->i_sends, 0, ic->i_send_ring.w_nr * sizeof(struct rds_ib_send_work)); - ic->i_recvs = vmalloc_node(ic->i_recv_ring.w_nr * sizeof(struct rds_ib_recv_work), + ic->i_recvs = vzalloc_node(ic->i_recv_ring.w_nr * sizeof(struct rds_ib_recv_work), ibdev_to_node(dev)); if (!ic->i_recvs) { ret = -ENOMEM; rdsdebug("recv allocation failed\n"); goto out; } - memset(ic->i_recvs, 0, ic->i_recv_ring.w_nr * sizeof(struct rds_ib_recv_work)); rds_ib_recv_init_ack(ic); @@ -435,13 +434,12 @@ static u32 rds_ib_protocol_compatible(struct rdma_cm_event *event) version = RDS_PROTOCOL_3_0; while ((common >>= 1) != 0) version++; - } else if (printk_ratelimit()) { - printk(KERN_NOTICE "RDS: Connection from %pI4 using " + } + printk_ratelimited(KERN_NOTICE "RDS: Connection from %pI4 using " "incompatible protocol version %u.%u\n", &dp->dp_saddr, dp->dp_protocol_major, dp->dp_protocol_minor); - } return version; } diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c index 819c35a..a985158 100644 --- a/net/rds/ib_rdma.c +++ b/net/rds/ib_rdma.c @@ -33,10 +33,10 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/rculist.h> +#include <linux/llist.h> #include "rds.h" #include "ib.h" -#include "xlist.h" static DEFINE_PER_CPU(unsigned long, clean_list_grace); #define CLEAN_LIST_BUSY_BIT 0 @@ -49,7 +49,7 @@ struct rds_ib_mr { struct rds_ib_mr_pool *pool; struct ib_fmr *fmr; - struct xlist_head xlist; + struct llist_node llnode; /* unmap_list is for freeing */ struct list_head unmap_list; @@ -71,9 +71,9 @@ struct rds_ib_mr_pool { atomic_t item_count; /* total # of MRs */ atomic_t dirty_count; /* # dirty of MRs */ - struct xlist_head drop_list; /* MRs that have reached their max_maps limit */ - struct xlist_head free_list; /* unused MRs */ - struct xlist_head clean_list; /* global unused & unamapped MRs */ + struct llist_head drop_list; /* MRs that have reached their max_maps limit */ + struct llist_head free_list; /* unused MRs */ + struct llist_head clean_list; /* global unused & unamapped MRs */ wait_queue_head_t flush_wait; atomic_t free_pinned; /* memory pinned by free MRs */ @@ -220,9 +220,9 @@ struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_ibdev) if (!pool) return ERR_PTR(-ENOMEM); - INIT_XLIST_HEAD(&pool->free_list); - INIT_XLIST_HEAD(&pool->drop_list); - INIT_XLIST_HEAD(&pool->clean_list); + init_llist_head(&pool->free_list); + init_llist_head(&pool->drop_list); + init_llist_head(&pool->clean_list); mutex_init(&pool->flush_lock); init_waitqueue_head(&pool->flush_wait); INIT_DELAYED_WORK(&pool->flush_worker, rds_ib_mr_pool_flush_worker); @@ -260,26 +260,18 @@ void rds_ib_destroy_mr_pool(struct rds_ib_mr_pool *pool) kfree(pool); } -static void refill_local(struct rds_ib_mr_pool *pool, struct xlist_head *xl, - struct rds_ib_mr **ibmr_ret) -{ - struct xlist_head *ibmr_xl; - ibmr_xl = xlist_del_head_fast(xl); - *ibmr_ret = list_entry(ibmr_xl, struct rds_ib_mr, xlist); -} - static inline struct rds_ib_mr *rds_ib_reuse_fmr(struct rds_ib_mr_pool *pool) { struct rds_ib_mr *ibmr = NULL; - struct xlist_head *ret; + struct llist_node *ret; unsigned long *flag; preempt_disable(); flag = &__get_cpu_var(clean_list_grace); set_bit(CLEAN_LIST_BUSY_BIT, flag); - ret = xlist_del_head(&pool->clean_list); + ret = llist_del_first(&pool->clean_list); if (ret) - ibmr = list_entry(ret, struct rds_ib_mr, xlist); + ibmr = llist_entry(ret, struct rds_ib_mr, llnode); clear_bit(CLEAN_LIST_BUSY_BIT, flag); preempt_enable(); @@ -529,46 +521,44 @@ static inline unsigned int rds_ib_flush_goal(struct rds_ib_mr_pool *pool, int fr } /* - * given an xlist of mrs, put them all into the list_head for more processing + * given an llist of mrs, put them all into the list_head for more processing */ -static void xlist_append_to_list(struct xlist_head *xlist, struct list_head *list) +static void llist_append_to_list(struct llist_head *llist, struct list_head *list) { struct rds_ib_mr *ibmr; - struct xlist_head splice; - struct xlist_head *cur; - struct xlist_head *next; - - splice.next = NULL; - xlist_splice(xlist, &splice); - cur = splice.next; - while (cur) { - next = cur->next; - ibmr = list_entry(cur, struct rds_ib_mr, xlist); + struct llist_node *node; + struct llist_node *next; + + node = llist_del_all(llist); + while (node) { + next = node->next; + ibmr = llist_entry(node, struct rds_ib_mr, llnode); list_add_tail(&ibmr->unmap_list, list); - cur = next; + node = next; } } /* - * this takes a list head of mrs and turns it into an xlist of clusters. - * each cluster has an xlist of MR_CLUSTER_SIZE mrs that are ready for - * reuse. + * this takes a list head of mrs and turns it into linked llist nodes + * of clusters. Each cluster has linked llist nodes of + * MR_CLUSTER_SIZE mrs that are ready for reuse. */ -static void list_append_to_xlist(struct rds_ib_mr_pool *pool, - struct list_head *list, struct xlist_head *xlist, - struct xlist_head **tail_ret) +static void list_to_llist_nodes(struct rds_ib_mr_pool *pool, + struct list_head *list, + struct llist_node **nodes_head, + struct llist_node **nodes_tail) { struct rds_ib_mr *ibmr; - struct xlist_head *cur_mr = xlist; - struct xlist_head *tail_mr = NULL; + struct llist_node *cur = NULL; + struct llist_node **next = nodes_head; list_for_each_entry(ibmr, list, unmap_list) { - tail_mr = &ibmr->xlist; - tail_mr->next = NULL; - cur_mr->next = tail_mr; - cur_mr = tail_mr; + cur = &ibmr->llnode; + *next = cur; + next = &cur->next; } - *tail_ret = tail_mr; + *next = NULL; + *nodes_tail = cur; } /* @@ -581,8 +571,8 @@ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool, int free_all, struct rds_ib_mr **ibmr_ret) { struct rds_ib_mr *ibmr, *next; - struct xlist_head clean_xlist; - struct xlist_head *clean_tail; + struct llist_node *clean_nodes; + struct llist_node *clean_tail; LIST_HEAD(unmap_list); LIST_HEAD(fmr_list); unsigned long unpinned = 0; @@ -603,7 +593,7 @@ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool, prepare_to_wait(&pool->flush_wait, &wait, TASK_UNINTERRUPTIBLE); - if (xlist_empty(&pool->clean_list)) + if (llist_empty(&pool->clean_list)) schedule(); ibmr = rds_ib_reuse_fmr(pool); @@ -628,10 +618,10 @@ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool, /* Get the list of all MRs to be dropped. Ordering matters - * we want to put drop_list ahead of free_list. */ - xlist_append_to_list(&pool->drop_list, &unmap_list); - xlist_append_to_list(&pool->free_list, &unmap_list); + llist_append_to_list(&pool->drop_list, &unmap_list); + llist_append_to_list(&pool->free_list, &unmap_list); if (free_all) - xlist_append_to_list(&pool->clean_list, &unmap_list); + llist_append_to_list(&pool->clean_list, &unmap_list); free_goal = rds_ib_flush_goal(pool, free_all); @@ -663,22 +653,22 @@ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool, if (!list_empty(&unmap_list)) { /* we have to make sure that none of the things we're about * to put on the clean list would race with other cpus trying - * to pull items off. The xlist would explode if we managed to + * to pull items off. The llist would explode if we managed to * remove something from the clean list and then add it back again - * while another CPU was spinning on that same item in xlist_del_head. + * while another CPU was spinning on that same item in llist_del_first. * - * This is pretty unlikely, but just in case wait for an xlist grace period + * This is pretty unlikely, but just in case wait for an llist grace period * here before adding anything back into the clean list. */ wait_clean_list_grace(); - list_append_to_xlist(pool, &unmap_list, &clean_xlist, &clean_tail); + list_to_llist_nodes(pool, &unmap_list, &clean_nodes, &clean_tail); if (ibmr_ret) - refill_local(pool, &clean_xlist, ibmr_ret); + *ibmr_ret = llist_entry(clean_nodes, struct rds_ib_mr, llnode); - /* refill_local may have emptied our list */ - if (!xlist_empty(&clean_xlist)) - xlist_add(clean_xlist.next, clean_tail, &pool->clean_list); + /* more than one entry in llist nodes */ + if (clean_nodes->next) + llist_add_batch(clean_nodes->next, clean_tail, &pool->clean_list); } @@ -711,9 +701,9 @@ void rds_ib_free_mr(void *trans_private, int invalidate) /* Return it to the pool's free list */ if (ibmr->remap_count >= pool->fmr_attr.max_maps) - xlist_add(&ibmr->xlist, &ibmr->xlist, &pool->drop_list); + llist_add(&ibmr->llnode, &pool->drop_list); else - xlist_add(&ibmr->xlist, &ibmr->xlist, &pool->free_list); + llist_add(&ibmr->llnode, &pool->free_list); atomic_add(ibmr->sg_len, &pool->free_pinned); atomic_inc(&pool->dirty_count); @@ -769,8 +759,10 @@ void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents, } ibmr = rds_ib_alloc_fmr(rds_ibdev); - if (IS_ERR(ibmr)) + if (IS_ERR(ibmr)) { + rds_ib_dev_put(rds_ibdev); return ibmr; + } ret = rds_ib_map_fmr(rds_ibdev, ibmr, sg, nents); if (ret == 0) diff --git a/net/rds/ib_send.c b/net/rds/ib_send.c index 7c4dce8..37be6e2 100644 --- a/net/rds/ib_send.c +++ b/net/rds/ib_send.c @@ -34,6 +34,7 @@ #include <linux/in.h> #include <linux/device.h> #include <linux/dmapool.h> +#include <linux/ratelimit.h> #include "rds.h" #include "ib.h" @@ -207,8 +208,7 @@ static struct rds_message *rds_ib_send_unmap_op(struct rds_ib_connection *ic, } break; default: - if (printk_ratelimit()) - printk(KERN_NOTICE + printk_ratelimited(KERN_NOTICE "RDS/IB: %s: unexpected opcode 0x%x in WR!\n", __func__, send->s_wr.opcode); break; @@ -552,9 +552,8 @@ int rds_ib_xmit(struct rds_connection *conn, struct rds_message *rm, && rm->m_inc.i_hdr.h_flags & RDS_FLAG_CONG_BITMAP) { rds_cong_map_updated(conn->c_fcong, ~(u64) 0); scat = &rm->data.op_sg[sg]; - ret = sizeof(struct rds_header) + RDS_CONG_MAP_BYTES; - ret = min_t(int, ret, scat->length - conn->c_xmit_data_off); - return ret; + ret = max_t(int, RDS_CONG_MAP_BYTES, scat->length); + return sizeof(struct rds_header) + ret; } /* FIXME we may overallocate here */ diff --git a/net/rds/info.c b/net/rds/info.c index 4fdf1b6..a4adb39 100644 --- a/net/rds/info.c +++ b/net/rds/info.c @@ -34,6 +34,7 @@ #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/proc_fs.h> +#include <linux/export.h> #include "rds.h" @@ -175,7 +176,7 @@ int rds_info_getsockopt(struct socket *sock, int optname, char __user *optval, /* check for all kinds of wrapping and the like */ start = (unsigned long)optval; - if (len < 0 || len + PAGE_SIZE - 1 < len || start + len < start) { + if (len < 0 || len > INT_MAX - PAGE_SIZE + 1 || start + len < start) { ret = -EINVAL; goto out; } diff --git a/net/rds/iw.c b/net/rds/iw.c index f747484..5899356 100644 --- a/net/rds/iw.c +++ b/net/rds/iw.c @@ -38,6 +38,7 @@ #include <linux/if_arp.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/module.h> #include "rds.h" #include "iw.h" @@ -238,7 +239,8 @@ static int rds_iw_laddr_check(__be32 addr) ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); /* due to this, we will claim to support IB devices unless we check node_type. */ - if (ret || cm_id->device->node_type != RDMA_NODE_RNIC) + if (ret || !cm_id->device || + cm_id->device->node_type != RDMA_NODE_RNIC) ret = -EADDRNOTAVAIL; rdsdebug("addr %pI4 ret %d node type %d\n", diff --git a/net/rds/iw.h b/net/rds/iw.h index 9015192..04ce3b1 100644 --- a/net/rds/iw.h +++ b/net/rds/iw.h @@ -1,6 +1,7 @@ #ifndef _RDS_IW_H #define _RDS_IW_H +#include <linux/interrupt.h> #include <rdma/ib_verbs.h> #include <rdma/rdma_cm.h> #include "rds.h" diff --git a/net/rds/iw_cm.c b/net/rds/iw_cm.c index c12db66..9556d28 100644 --- a/net/rds/iw_cm.c +++ b/net/rds/iw_cm.c @@ -34,6 +34,7 @@ #include <linux/in.h> #include <linux/slab.h> #include <linux/vmalloc.h> +#include <linux/ratelimit.h> #include "rds.h" #include "iw.h" @@ -258,8 +259,7 @@ static int rds_iw_setup_qp(struct rds_connection *conn) */ rds_iwdev = ib_get_client_data(dev, &rds_iw_client); if (!rds_iwdev) { - if (printk_ratelimit()) - printk(KERN_NOTICE "RDS/IW: No client_data for device %s\n", + printk_ratelimited(KERN_NOTICE "RDS/IW: No client_data for device %s\n", dev->name); return -EOPNOTSUPP; } @@ -365,13 +365,12 @@ static u32 rds_iw_protocol_compatible(const struct rds_iw_connect_private *dp) version = RDS_PROTOCOL_3_0; while ((common >>= 1) != 0) version++; - } else if (printk_ratelimit()) { - printk(KERN_NOTICE "RDS: Connection from %pI4 using " + } + printk_ratelimited(KERN_NOTICE "RDS: Connection from %pI4 using " "incompatible protocol version %u.%u\n", &dp->dp_saddr, dp->dp_protocol_major, dp->dp_protocol_minor); - } return version; } diff --git a/net/rds/iw_rdma.c b/net/rds/iw_rdma.c index 6deaa77..83725f3 100644 --- a/net/rds/iw_rdma.c +++ b/net/rds/iw_rdma.c @@ -32,6 +32,7 @@ */ #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/ratelimit.h> #include "rds.h" #include "iw.h" @@ -83,10 +84,13 @@ static int rds_iw_map_fastreg(struct rds_iw_mr_pool *pool, static void rds_iw_free_fastreg(struct rds_iw_mr_pool *pool, struct rds_iw_mr *ibmr); static unsigned int rds_iw_unmap_fastreg_list(struct rds_iw_mr_pool *pool, struct list_head *unmap_list, - struct list_head *kill_list); + struct list_head *kill_list, + int *unpinned); static void rds_iw_destroy_fastreg(struct rds_iw_mr_pool *pool, struct rds_iw_mr *ibmr); -static int rds_iw_get_device(struct rds_sock *rs, struct rds_iw_device **rds_iwdev, struct rdma_cm_id **cm_id) +static int rds_iw_get_device(struct sockaddr_in *src, struct sockaddr_in *dst, + struct rds_iw_device **rds_iwdev, + struct rdma_cm_id **cm_id) { struct rds_iw_device *iwdev; struct rds_iw_cm_id *i_cm_id; @@ -110,15 +114,15 @@ static int rds_iw_get_device(struct rds_sock *rs, struct rds_iw_device **rds_iwd src_addr->sin_port, dst_addr->sin_addr.s_addr, dst_addr->sin_port, - rs->rs_bound_addr, - rs->rs_bound_port, - rs->rs_conn_addr, - rs->rs_conn_port); + src->sin_addr.s_addr, + src->sin_port, + dst->sin_addr.s_addr, + dst->sin_port); #ifdef WORKING_TUPLE_DETECTION - if (src_addr->sin_addr.s_addr == rs->rs_bound_addr && - src_addr->sin_port == rs->rs_bound_port && - dst_addr->sin_addr.s_addr == rs->rs_conn_addr && - dst_addr->sin_port == rs->rs_conn_port) { + if (src_addr->sin_addr.s_addr == src->sin_addr.s_addr && + src_addr->sin_port == src->sin_port && + dst_addr->sin_addr.s_addr == dst->sin_addr.s_addr && + dst_addr->sin_port == dst->sin_port) { #else /* FIXME - needs to compare the local and remote * ipaddr/port tuple, but the ipaddr is the only @@ -126,7 +130,7 @@ static int rds_iw_get_device(struct rds_sock *rs, struct rds_iw_device **rds_iwd * zero'ed. It doesn't appear to be properly populated * during connection setup... */ - if (src_addr->sin_addr.s_addr == rs->rs_bound_addr) { + if (src_addr->sin_addr.s_addr == src->sin_addr.s_addr) { #endif spin_unlock_irq(&iwdev->spinlock); *rds_iwdev = iwdev; @@ -178,19 +182,13 @@ int rds_iw_update_cm_id(struct rds_iw_device *rds_iwdev, struct rdma_cm_id *cm_i { struct sockaddr_in *src_addr, *dst_addr; struct rds_iw_device *rds_iwdev_old; - struct rds_sock rs; struct rdma_cm_id *pcm_id; int rc; src_addr = (struct sockaddr_in *)&cm_id->route.addr.src_addr; dst_addr = (struct sockaddr_in *)&cm_id->route.addr.dst_addr; - rs.rs_bound_addr = src_addr->sin_addr.s_addr; - rs.rs_bound_port = src_addr->sin_port; - rs.rs_conn_addr = dst_addr->sin_addr.s_addr; - rs.rs_conn_port = dst_addr->sin_port; - - rc = rds_iw_get_device(&rs, &rds_iwdev_old, &pcm_id); + rc = rds_iw_get_device(src_addr, dst_addr, &rds_iwdev_old, &pcm_id); if (rc) rds_iw_remove_cm_id(rds_iwdev, cm_id); @@ -498,7 +496,7 @@ static int rds_iw_flush_mr_pool(struct rds_iw_mr_pool *pool, int free_all) LIST_HEAD(unmap_list); LIST_HEAD(kill_list); unsigned long flags; - unsigned int nfreed = 0, ncleaned = 0, free_goal; + unsigned int nfreed = 0, ncleaned = 0, unpinned = 0, free_goal; int ret = 0; rds_iw_stats_inc(s_iw_rdma_mr_pool_flush); @@ -523,7 +521,8 @@ static int rds_iw_flush_mr_pool(struct rds_iw_mr_pool *pool, int free_all) * will be destroyed by the unmap function. */ if (!list_empty(&unmap_list)) { - ncleaned = rds_iw_unmap_fastreg_list(pool, &unmap_list, &kill_list); + ncleaned = rds_iw_unmap_fastreg_list(pool, &unmap_list, + &kill_list, &unpinned); /* If we've been asked to destroy all MRs, move those * that were simply cleaned to the kill list */ if (free_all) @@ -547,6 +546,7 @@ static int rds_iw_flush_mr_pool(struct rds_iw_mr_pool *pool, int free_all) spin_unlock_irqrestore(&pool->list_lock, flags); } + atomic_sub(unpinned, &pool->free_pinned); atomic_sub(ncleaned, &pool->dirty_count); atomic_sub(nfreed, &pool->item_count); @@ -607,9 +607,17 @@ void *rds_iw_get_mr(struct scatterlist *sg, unsigned long nents, struct rds_iw_device *rds_iwdev; struct rds_iw_mr *ibmr = NULL; struct rdma_cm_id *cm_id; + struct sockaddr_in src = { + .sin_addr.s_addr = rs->rs_bound_addr, + .sin_port = rs->rs_bound_port, + }; + struct sockaddr_in dst = { + .sin_addr.s_addr = rs->rs_conn_addr, + .sin_port = rs->rs_conn_port, + }; int ret; - ret = rds_iw_get_device(rs, &rds_iwdev, &cm_id); + ret = rds_iw_get_device(&src, &dst, &rds_iwdev, &cm_id); if (ret || !cm_id) { ret = -ENODEV; goto out; @@ -729,8 +737,8 @@ static int rds_iw_rdma_build_fastreg(struct rds_iw_mapping *mapping) failed_wr = &f_wr; ret = ib_post_send(ibmr->cm_id->qp, &f_wr, &failed_wr); BUG_ON(failed_wr != &f_wr); - if (ret && printk_ratelimit()) - printk(KERN_WARNING "RDS/IW: %s:%d ib_post_send returned %d\n", + if (ret) + printk_ratelimited(KERN_WARNING "RDS/IW: %s:%d ib_post_send returned %d\n", __func__, __LINE__, ret); return ret; } @@ -751,8 +759,8 @@ static int rds_iw_rdma_fastreg_inv(struct rds_iw_mr *ibmr) failed_wr = &s_wr; ret = ib_post_send(ibmr->cm_id->qp, &s_wr, &failed_wr); - if (ret && printk_ratelimit()) { - printk(KERN_WARNING "RDS/IW: %s:%d ib_post_send returned %d\n", + if (ret) { + printk_ratelimited(KERN_WARNING "RDS/IW: %s:%d ib_post_send returned %d\n", __func__, __LINE__, ret); goto out; } @@ -827,7 +835,8 @@ static void rds_iw_free_fastreg(struct rds_iw_mr_pool *pool, static unsigned int rds_iw_unmap_fastreg_list(struct rds_iw_mr_pool *pool, struct list_head *unmap_list, - struct list_head *kill_list) + struct list_head *kill_list, + int *unpinned) { struct rds_iw_mapping *mapping, *next; unsigned int ncleaned = 0; @@ -854,6 +863,7 @@ static unsigned int rds_iw_unmap_fastreg_list(struct rds_iw_mr_pool *pool, spin_lock_irqsave(&pool->list_lock, flags); list_for_each_entry_safe(mapping, next, unmap_list, m_list) { + *unpinned += mapping->m_sg.len; list_move(&mapping->m_list, &laundered); ncleaned++; } diff --git a/net/rds/iw_send.c b/net/rds/iw_send.c index 545d8ee..e40c3c5 100644 --- a/net/rds/iw_send.c +++ b/net/rds/iw_send.c @@ -34,6 +34,7 @@ #include <linux/in.h> #include <linux/device.h> #include <linux/dmapool.h> +#include <linux/ratelimit.h> #include "rds.h" #include "iw.h" @@ -258,8 +259,7 @@ void rds_iw_send_cq_comp_handler(struct ib_cq *cq, void *context) * when the SEND completes. */ break; default: - if (printk_ratelimit()) - printk(KERN_NOTICE + printk_ratelimited(KERN_NOTICE "RDS/IW: %s: unexpected opcode 0x%x in WR!\n", __func__, send->s_wr.opcode); break; diff --git a/net/rds/message.c b/net/rds/message.c index b48c4be..aff589c 100644 --- a/net/rds/message.c +++ b/net/rds/message.c @@ -32,6 +32,7 @@ */ #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/export.h> #include "rds.h" diff --git a/net/rds/page.c b/net/rds/page.c index d8acdeb..2499cd1 100644 --- a/net/rds/page.c +++ b/net/rds/page.c @@ -32,6 +32,8 @@ */ #include <linux/highmem.h> #include <linux/gfp.h> +#include <linux/cpu.h> +#include <linux/export.h> #include "rds.h" diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c index f8760e1..c2be901 100644 --- a/net/rds/rdma_transport.c +++ b/net/rds/rdma_transport.c @@ -30,6 +30,7 @@ * SOFTWARE. * */ +#include <linux/module.h> #include <rdma/rdma_cm.h> #include "rdma_transport.h" diff --git a/net/rds/rds.h b/net/rds/rds.h index da8adac..7eaba18 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -36,8 +36,8 @@ #define rdsdebug(fmt, args...) pr_debug("%s(): " fmt, __func__ , ##args) #else /* sigh, pr_debug() causes unused variable warnings */ -static inline void __attribute__ ((format (printf, 1, 2))) -rdsdebug(char *fmt, ...) +static inline __printf(1, 2) +void rdsdebug(char *fmt, ...) { } #endif @@ -625,8 +625,8 @@ void rds_for_each_conn_info(struct socket *sock, unsigned int len, struct rds_info_lengths *lens, int (*visitor)(struct rds_connection *, void *), size_t item_len); -void __rds_conn_error(struct rds_connection *conn, const char *, ...) - __attribute__ ((format (printf, 2, 3))); +__printf(2, 3) +void __rds_conn_error(struct rds_connection *conn, const char *, ...); #define rds_conn_error(conn, fmt...) \ __rds_conn_error(conn, KERN_WARNING "RDS: " fmt) diff --git a/net/rds/recv.c b/net/rds/recv.c index 51a8f8e..96a1239 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -34,6 +34,7 @@ #include <linux/slab.h> #include <net/sock.h> #include <linux/in.h> +#include <linux/export.h> #include "rds.h" @@ -409,8 +410,6 @@ int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, rdsdebug("size %zu flags 0x%x timeo %ld\n", size, msg_flags, timeo); - msg->msg_namelen = 0; - if (msg_flags & MSG_OOB) goto out; diff --git a/net/rds/send.c b/net/rds/send.c index f6bdfb0..88eace5 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -31,10 +31,13 @@ * */ #include <linux/kernel.h> +#include <linux/moduleparam.h> #include <linux/gfp.h> #include <net/sock.h> #include <linux/in.h> #include <linux/list.h> +#include <linux/ratelimit.h> +#include <linux/export.h> #include "rds.h" @@ -1005,16 +1008,14 @@ int rds_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, goto out; if (rm->rdma.op_active && !conn->c_trans->xmit_rdma) { - if (printk_ratelimit()) - printk(KERN_NOTICE "rdma_op %p conn xmit_rdma %p\n", + printk_ratelimited(KERN_NOTICE "rdma_op %p conn xmit_rdma %p\n", &rm->rdma, conn->c_trans->xmit_rdma); ret = -EOPNOTSUPP; goto out; } if (rm->atomic.op_active && !conn->c_trans->xmit_atomic) { - if (printk_ratelimit()) - printk(KERN_NOTICE "atomic_op %p conn xmit_atomic %p\n", + printk_ratelimited(KERN_NOTICE "atomic_op %p conn xmit_atomic %p\n", &rm->atomic, conn->c_trans->xmit_atomic); ret = -EOPNOTSUPP; goto out; diff --git a/net/rds/stats.c b/net/rds/stats.c index 10c759c..7be790d 100644 --- a/net/rds/stats.c +++ b/net/rds/stats.c @@ -33,6 +33,7 @@ #include <linux/percpu.h> #include <linux/seq_file.h> #include <linux/proc_fs.h> +#include <linux/export.h> #include "rds.h" diff --git a/net/rds/sysctl.c b/net/rds/sysctl.c index 25ad0c7..065026f 100644 --- a/net/rds/sysctl.c +++ b/net/rds/sysctl.c @@ -71,14 +71,14 @@ static ctl_table rds_sysctl_rds_table[] = { { .procname = "max_unacked_packets", .data = &rds_sysctl_max_unacked_packets, - .maxlen = sizeof(unsigned long), + .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec, }, { .procname = "max_unacked_bytes", .data = &rds_sysctl_max_unacked_bytes, - .maxlen = sizeof(unsigned long), + .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec, }, diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 8e0a320..edac9ef 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -33,6 +33,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/in.h> +#include <linux/module.h> #include <net/tcp.h> #include "rds.h" diff --git a/net/rds/tcp_stats.c b/net/rds/tcp_stats.c index d5898d0..f8a7954 100644 --- a/net/rds/tcp_stats.c +++ b/net/rds/tcp_stats.c @@ -40,7 +40,7 @@ DEFINE_PER_CPU(struct rds_tcp_statistics, rds_tcp_stats) ____cacheline_aligned; -static const char const *rds_tcp_stat_names[] = { +static const char * const rds_tcp_stat_names[] = { "tcp_data_ready_calls", "tcp_write_space_calls", "tcp_sndbuf_full", diff --git a/net/rds/threads.c b/net/rds/threads.c index 0fd90f8..65eaefc 100644 --- a/net/rds/threads.c +++ b/net/rds/threads.c @@ -32,6 +32,7 @@ */ #include <linux/kernel.h> #include <linux/random.h> +#include <linux/export.h> #include "rds.h" diff --git a/net/rds/xlist.h b/net/rds/xlist.h deleted file mode 100644 index e6b5190..0000000 --- a/net/rds/xlist.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef _LINUX_XLIST_H -#define _LINUX_XLIST_H - -#include <linux/stddef.h> -#include <linux/poison.h> -#include <linux/prefetch.h> -#include <asm/system.h> - -struct xlist_head { - struct xlist_head *next; -}; - -static inline void INIT_XLIST_HEAD(struct xlist_head *list) -{ - list->next = NULL; -} - -static inline int xlist_empty(struct xlist_head *head) -{ - return head->next == NULL; -} - -static inline void xlist_add(struct xlist_head *new, struct xlist_head *tail, - struct xlist_head *head) -{ - struct xlist_head *cur; - struct xlist_head *check; - - while (1) { - cur = head->next; - tail->next = cur; - check = cmpxchg(&head->next, cur, new); - if (check == cur) - break; - } -} - -static inline struct xlist_head *xlist_del_head(struct xlist_head *head) -{ - struct xlist_head *cur; - struct xlist_head *check; - struct xlist_head *next; - - while (1) { - cur = head->next; - if (!cur) - goto out; - - next = cur->next; - check = cmpxchg(&head->next, cur, next); - if (check == cur) - goto out; - } -out: - return cur; -} - -static inline struct xlist_head *xlist_del_head_fast(struct xlist_head *head) -{ - struct xlist_head *cur; - - cur = head->next; - if (!cur) - return NULL; - - head->next = cur->next; - return cur; -} - -static inline void xlist_splice(struct xlist_head *list, - struct xlist_head *head) -{ - struct xlist_head *cur; - - WARN_ON(head->next); - cur = xchg(&list->next, NULL); - head->next = cur; -} - -#endif diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 554d081..1776e57 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -338,9 +338,9 @@ rpcrdma_inline_pullup(struct rpc_rqst *rqst, int pad) curlen = copy_len; dprintk("RPC: %s: page %d destp 0x%p len %d curlen %d\n", __func__, i, destp, copy_len, curlen); - srcp = kmap_atomic(ppages[i], KM_SKB_SUNRPC_DATA); + srcp = kmap_atomic(ppages[i]); memcpy(destp, srcp+page_base, curlen); - kunmap_atomic(srcp, KM_SKB_SUNRPC_DATA); + kunmap_atomic(srcp); rqst->rq_svec[0].iov_len += curlen; destp += curlen; copy_len -= curlen; @@ -639,10 +639,10 @@ rpcrdma_inline_fixup(struct rpc_rqst *rqst, char *srcp, int copy_len, int pad) dprintk("RPC: %s: page %d" " srcp 0x%p len %d curlen %d\n", __func__, i, srcp, copy_len, curlen); - destp = kmap_atomic(ppages[i], KM_SKB_SUNRPC_DATA); + destp = kmap_atomic(ppages[i]); memcpy(destp + page_base, srcp, curlen); flush_dcache_page(ppages[i]); - kunmap_atomic(destp, KM_SKB_SUNRPC_DATA); + kunmap_atomic(destp); srcp += curlen; copy_len -= curlen; if (copy_len == 0) diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index c3c232a..ba1296d 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -42,6 +42,7 @@ #include <linux/sunrpc/svc_xprt.h> #include <linux/sunrpc/debug.h> #include <linux/sunrpc/rpc_rdma.h> +#include <linux/interrupt.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/spinlock.h> @@ -49,6 +50,7 @@ #include <rdma/ib_verbs.h> #include <rdma/rdma_cm.h> #include <linux/sunrpc/svc_rdma.h> +#include <linux/export.h> #define RPCDBG_FACILITY RPCDBG_SVCXPRT diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index d0b5210..5d9202d 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -284,6 +284,7 @@ xprt_setup_rdma(struct xprt_create *args) } xprt = xprt_alloc(args->net, sizeof(struct rpcrdma_xprt), + xprt_rdma_slot_table_entries, xprt_rdma_slot_table_entries); if (xprt == NULL) { dprintk("RPC: %s: couldn't allocate rpcrdma_xprt\n", @@ -453,9 +454,8 @@ xprt_rdma_connect(struct rpc_task *task) } static int -xprt_rdma_reserve_xprt(struct rpc_task *task) +xprt_rdma_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task) { - struct rpc_xprt *xprt = task->tk_xprt; struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt); int credits = atomic_read(&r_xprt->rx_buf.rb_credits); @@ -467,7 +467,7 @@ xprt_rdma_reserve_xprt(struct rpc_task *task) BUG_ON(r_xprt->rx_buf.rb_cwndscale <= 0); } xprt->cwnd = credits * r_xprt->rx_buf.rb_cwndscale; - return xprt_reserve_xprt_cong(task); + return xprt_reserve_xprt_cong(xprt, task); } /* @@ -713,6 +713,7 @@ static void xprt_rdma_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) static struct rpc_xprt_ops xprt_rdma_procs = { .reserve_xprt = xprt_rdma_reserve_xprt, .release_xprt = xprt_release_xprt_cong, /* sunrpc/xprt.c */ + .alloc_slot = xprt_alloc_slot, .release_request = xprt_release_rqst_cong, /* ditto */ .set_retrans_timeout = xprt_set_retrans_timeout_def, /* ditto */ .rpcbind = rpcb_getport_async, /* sunrpc/rpcb_clnt.c */ diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 80f8da3..37e4484 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -47,6 +47,7 @@ * o buffer memory */ +#include <linux/interrupt.h> #include <linux/pci.h> /* for Tavor hack below */ #include <linux/slab.h> @@ -484,7 +485,7 @@ rpcrdma_ia_open(struct rpcrdma_xprt *xprt, struct sockaddr *addr, int memreg) if (rc) { dprintk("RPC: %s: ib_query_device failed %d\n", __func__, rc); - goto out2; + goto out3; } if (devattr.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY) { @@ -586,7 +587,7 @@ rpcrdma_ia_open(struct rpcrdma_xprt *xprt, struct sockaddr *addr, int memreg) printk(KERN_ERR "%s: invalid memory registration mode %d\n", __func__, memreg); rc = -EINVAL; - goto out2; + goto out3; } dprintk("RPC: %s: memory registration strategy is %d\n", __func__, memreg); @@ -595,6 +596,10 @@ rpcrdma_ia_open(struct rpcrdma_xprt *xprt, struct sockaddr *addr, int memreg) ia->ri_memreg_strategy = memreg; return 0; + +out3: + ib_dealloc_pd(ia->ri_pd); + ia->ri_pd = NULL; out2: rdma_destroy_id(ia->ri_id); ia->ri_id = NULL; diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index cae761a..08c5d5a 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -42,7 +42,7 @@ #include <linux/wait.h> /* wait_queue_head_t, etc */ #include <linux/spinlock.h> /* spinlock_t, etc */ -#include <asm/atomic.h> /* atomic_t, etc */ +#include <linux/atomic.h> /* atomic_t, etc */ #include <rdma/rdma_cm.h> /* RDMA connection api */ #include <rdma/ib_verbs.h> /* RDMA verbs api */ @@ -109,7 +109,7 @@ struct rpcrdma_ep { */ /* temporary static scatter/gather max */ -#define RPCRDMA_MAX_DATA_SEGS (8) /* max scatter/gather */ +#define RPCRDMA_MAX_DATA_SEGS (64) /* max scatter/gather */ #define RPCRDMA_MAX_SEGS (RPCRDMA_MAX_DATA_SEGS + 2) /* head+tail = 2 */ #define MAX_RPCRDMAHDR (\ /* max supported RPC/RDMA header */ \ diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index fa68d1e..28908f5 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -39,6 +39,7 @@ #include "link.h" #include "port.h" #include "bcast.h" +#include "name_distr.h" #define MAX_PKT_DEFAULT_MCAST 1500 /* bcast link max packet size (fixed) */ @@ -298,14 +299,9 @@ static void bclink_send_nack(struct tipc_node *n_ptr) msg_set_bcgap_to(msg, n_ptr->bclink.gap_to); msg_set_bcast_tag(msg, tipc_own_tag); - if (tipc_bearer_send(&bcbearer->bearer, buf, NULL)) { - bcl->stats.sent_nacks++; - buf_discard(buf); - } else { - tipc_bearer_schedule(bcl->b_ptr, bcl); - bcl->proto_msg_queue = buf; - bcl->stats.bearer_congs++; - } + tipc_bearer_send(&bcbearer->bearer, buf, NULL); + bcl->stats.sent_nacks++; + buf_discard(buf); /* * Ensure we doesn't send another NACK msg to the node @@ -426,20 +422,28 @@ int tipc_bclink_send_msg(struct sk_buff *buf) void tipc_bclink_recv_pkt(struct sk_buff *buf) { struct tipc_msg *msg = buf_msg(buf); - struct tipc_node *node = tipc_node_find(msg_prevnode(msg)); + struct tipc_node *node; u32 next_in; u32 seqno; struct sk_buff *deferred; - if (unlikely(!node || !tipc_node_is_up(node) || !node->bclink.supported || - (msg_mc_netid(msg) != tipc_net_id))) { - buf_discard(buf); - return; - } + /* Screen out unwanted broadcast messages */ + + if (msg_mc_netid(msg) != tipc_net_id) + goto exit; + + node = tipc_node_find(msg_prevnode(msg)); + if (unlikely(!node)) + goto exit; + + tipc_node_lock(node); + if (unlikely(!node->bclink.supported)) + goto unlock; if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) { + if (msg_type(msg) != STATE_MSG) + goto unlock; if (msg_destnode(msg) == tipc_own_addr) { - tipc_node_lock(node); tipc_bclink_acknowledge(node, msg_bcast_ack(msg)); tipc_node_unlock(node); spin_lock_bh(&bc_lock); @@ -449,18 +453,18 @@ void tipc_bclink_recv_pkt(struct sk_buff *buf) msg_bcgap_to(msg)); spin_unlock_bh(&bc_lock); } else { + tipc_node_unlock(node); tipc_bclink_peek_nack(msg_destnode(msg), msg_bcast_tag(msg), msg_bcgap_after(msg), msg_bcgap_to(msg)); } - buf_discard(buf); - return; + goto exit; } - tipc_node_lock(node); + /* Handle in-sequence broadcast message */ + receive: - deferred = node->bclink.deferred_head; next_in = mod(node->bclink.last_in + 1); seqno = msg_seqno(msg); @@ -474,7 +478,10 @@ receive: } if (likely(msg_isdata(msg))) { tipc_node_unlock(node); - tipc_port_recv_mcast(buf, NULL); + if (likely(msg_mcast(msg))) + tipc_port_recv_mcast(buf, NULL); + else + buf_discard(buf); } else if (msg_user(msg) == MSG_BUNDLER) { bcl->stats.recv_bundles++; bcl->stats.recv_bundled += msg_msgcnt(msg); @@ -487,18 +494,22 @@ receive: bcl->stats.recv_fragmented++; tipc_node_unlock(node); tipc_net_route_msg(buf); + } else if (msg_user(msg) == NAME_DISTRIBUTOR) { + tipc_node_unlock(node); + tipc_named_recv(buf); } else { tipc_node_unlock(node); - tipc_net_route_msg(buf); + buf_discard(buf); } + buf = NULL; + tipc_node_lock(node); + deferred = node->bclink.deferred_head; if (deferred && (buf_seqno(deferred) == mod(next_in + 1))) { - tipc_node_lock(node); buf = deferred; msg = buf_msg(buf); node->bclink.deferred_head = deferred->next; goto receive; } - return; } else if (less(next_in, seqno)) { u32 gap_after = node->bclink.gap_after; u32 gap_to = node->bclink.gap_to; @@ -513,6 +524,7 @@ receive: else if (less(gap_after, seqno) && less(seqno, gap_to)) node->bclink.gap_to = seqno; } + buf = NULL; if (bclink_ack_allowed(node->bclink.nack_sync)) { if (gap_to != gap_after) bclink_send_nack(node); @@ -520,9 +532,11 @@ receive: } } else { bcl->stats.duplicates++; - buf_discard(buf); } +unlock: tipc_node_unlock(node); +exit: + buf_discard(buf); } u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr) @@ -535,10 +549,11 @@ u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr) /** * tipc_bcbearer_send - send a packet through the broadcast pseudo-bearer * - * Send through as many bearers as necessary to reach all nodes - * that support TIPC multicasting. + * Send packet over as many bearers as necessary to reach all nodes + * that have joined the broadcast link. * - * Returns 0 if packet sent successfully, non-zero if not + * Returns 0 (packet sent successfully) under all circumstances, + * since the broadcast link's pseudo-bearer never blocks */ static int tipc_bcbearer_send(struct sk_buff *buf, @@ -547,17 +562,26 @@ static int tipc_bcbearer_send(struct sk_buff *buf, { int bp_index; - /* Prepare buffer for broadcasting (if first time trying to send it) */ + /* + * Prepare broadcast link message for reliable transmission, + * if first time trying to send it; + * preparation is skipped for broadcast link protocol messages + * since they are sent in an unreliable manner and don't need it + */ if (likely(!msg_non_seq(buf_msg(buf)))) { struct tipc_msg *msg; - assert(tipc_bcast_nmap.count != 0); bcbuf_set_acks(buf, tipc_bcast_nmap.count); msg = buf_msg(buf); msg_set_non_seq(msg, 1); msg_set_mc_netid(msg, tipc_net_id); bcl->stats.sent_info++; + + if (WARN_ON(!tipc_bcast_nmap.count)) { + dump_stack(); + return 0; + } } /* Send buffer over bearers until all targets reached */ @@ -592,18 +616,12 @@ static int tipc_bcbearer_send(struct sk_buff *buf, } if (bcbearer->remains_new.count == 0) - return 0; + break; /* all targets reached */ bcbearer->remains = bcbearer->remains_new; } - /* - * Unable to reach all targets (indicate success, since currently - * there isn't code in place to properly block & unblock the - * pseudo-bearer used by the broadcast link) - */ - - return TIPC_OK; + return 0; } /** @@ -663,27 +681,6 @@ void tipc_bcbearer_sort(void) spin_unlock_bh(&bc_lock); } -/** - * tipc_bcbearer_push - resolve bearer congestion - * - * Forces bclink to push out any unsent packets, until all packets are gone - * or congestion reoccurs. - * No locks set when function called - */ - -void tipc_bcbearer_push(void) -{ - struct tipc_bearer *b_ptr; - - spin_lock_bh(&bc_lock); - b_ptr = &bcbearer->bearer; - if (b_ptr->blocked) { - b_ptr->blocked = 0; - tipc_bearer_lock_push(b_ptr); - } - spin_unlock_bh(&bc_lock); -} - int tipc_bclink_stats(char *buf, const u32 buf_size) { @@ -760,7 +757,7 @@ int tipc_bclink_init(void) bcbearer = kzalloc(sizeof(*bcbearer), GFP_ATOMIC); bclink = kzalloc(sizeof(*bclink), GFP_ATOMIC); if (!bcbearer || !bclink) { - warn("Multicast link creation failed, no memory\n"); + warn("Broadcast link creation failed, no memory\n"); kfree(bcbearer); bcbearer = NULL; kfree(bclink); @@ -771,7 +768,7 @@ int tipc_bclink_init(void) INIT_LIST_HEAD(&bcbearer->bearer.cong_links); bcbearer->bearer.media = &bcbearer->media; bcbearer->media.send_msg = tipc_bcbearer_send; - sprintf(bcbearer->media.name, "tipc-multicast"); + sprintf(bcbearer->media.name, "tipc-broadcast"); bcl = &bclink->link; INIT_LIST_HEAD(&bcl->waiting_ports); diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index 500c97f..06740da 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -101,6 +101,5 @@ int tipc_bclink_stats(char *stats_buf, const u32 buf_size); int tipc_bclink_reset_stats(void); int tipc_bclink_set_queue_limits(u32 limit); void tipc_bcbearer_sort(void); -void tipc_bcbearer_push(void); #endif diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 85209ea..e2202de 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -385,13 +385,9 @@ static int bearer_push(struct tipc_bearer *b_ptr) void tipc_bearer_lock_push(struct tipc_bearer *b_ptr) { - int res; - spin_lock_bh(&b_ptr->lock); - res = bearer_push(b_ptr); + bearer_push(b_ptr); spin_unlock_bh(&b_ptr->lock); - if (res) - tipc_bcbearer_push(); } @@ -402,7 +398,6 @@ void tipc_bearer_lock_push(struct tipc_bearer *b_ptr) void tipc_continue(struct tipc_bearer *b_ptr) { spin_lock_bh(&b_ptr->lock); - b_ptr->continue_count++; if (!list_empty(&b_ptr->cong_links)) tipc_k_signal((Handler)tipc_bearer_lock_push, (unsigned long)b_ptr); b_ptr->blocked = 0; @@ -609,6 +604,7 @@ int tipc_block_bearer(const char *name) info("Blocking bearer <%s>\n", name); spin_lock_bh(&b_ptr->lock); b_ptr->blocked = 1; + list_splice_init(&b_ptr->cong_links, &b_ptr->links); list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) { struct tipc_node *n_ptr = l_ptr->owner; @@ -636,6 +632,7 @@ static void bearer_disable(struct tipc_bearer *b_ptr) spin_lock_bh(&b_ptr->lock); b_ptr->blocked = 1; b_ptr->media->disable_bearer(b_ptr); + list_splice_init(&b_ptr->cong_links, &b_ptr->links); list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) { tipc_link_delete(l_ptr); } diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index 31d6172..d696f9e 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h @@ -39,8 +39,8 @@ #include "bcast.h" -#define MAX_BEARERS 8 -#define MAX_MEDIA 4 +#define MAX_BEARERS 2 +#define MAX_MEDIA 2 /* * Identifiers of supported TIPC media types @@ -107,7 +107,6 @@ struct media { * @link_req: ptr to (optional) structure making periodic link setup requests * @links: list of non-congested links associated with bearer * @cong_links: list of congested links associated with bearer - * @continue_count: # of times bearer has resumed after congestion or blocking * @active: non-zero if bearer structure is represents a bearer * @net_plane: network plane ('A' through 'H') currently associated with bearer * @nodes: indicates which nodes in cluster can be reached through bearer @@ -129,7 +128,6 @@ struct tipc_bearer { struct link_req *link_req; struct list_head links; struct list_head cong_links; - u32 continue_count; int active; char net_plane; struct tipc_node_map nodes; diff --git a/net/tipc/config.h b/net/tipc/config.h index 443159a..80da6eb 100644 --- a/net/tipc/config.h +++ b/net/tipc/config.h @@ -65,7 +65,6 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *req_tlv_area, int req_tlv_space, int headroom); -void tipc_cfg_link_event(u32 addr, char *name, int up); int tipc_cfg_init(void); void tipc_cfg_stop(void); diff --git a/net/tipc/core.c b/net/tipc/core.c index 943b6af..c21331d 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -34,6 +34,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <linux/module.h> + #include "core.h" #include "ref.h" #include "name_table.h" diff --git a/net/tipc/core.h b/net/tipc/core.h index 436dda1..2761af3 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -47,7 +47,7 @@ #include <linux/string.h> #include <asm/uaccess.h> #include <linux/interrupt.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/hardirq.h> #include <linux/netdevice.h> #include <linux/in.h> @@ -62,12 +62,6 @@ struct tipc_msg; /* msg.h */ struct print_buf; /* log.h */ /* - * TIPC sanity test macros - */ - -#define assert(i) BUG_ON(!(i)) - -/* * TIPC system monitoring code */ diff --git a/net/tipc/discover.c b/net/tipc/discover.c index 0987933..f2fb96e 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -159,12 +159,6 @@ void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr) } tipc_node_lock(n_ptr); - /* Don't talk to neighbor during cleanup after last session */ - if (n_ptr->cleanup_required) { - tipc_node_unlock(n_ptr); - return; - } - link = n_ptr->links[b_ptr->identity]; /* Create a link endpoint for this bearer, if necessary */ diff --git a/net/tipc/eth_media.c b/net/tipc/eth_media.c index a8c2a6b2..a224a38 100644 --- a/net/tipc/eth_media.c +++ b/net/tipc/eth_media.c @@ -2,7 +2,7 @@ * net/tipc/eth_media.c: Ethernet bearer support for TIPC * * Copyright (c) 2001-2007, Ericsson AB - * Copyright (c) 2005-2007, Wind River Systems + * Copyright (c) 2005-2008, 2011, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,7 +37,7 @@ #include "core.h" #include "bearer.h" -#define MAX_ETH_BEARERS 2 +#define MAX_ETH_BEARERS MAX_BEARERS #define ETH_LINK_PRIORITY TIPC_DEF_LINK_PRI #define ETH_LINK_TOLERANCE TIPC_DEF_LINK_TOL #define ETH_LINK_WINDOW TIPC_DEF_LINK_WIN @@ -156,32 +156,28 @@ static int enable_bearer(struct tipc_bearer *tb_ptr) /* Find device with specified name */ + read_lock(&dev_base_lock); for_each_netdev(&init_net, pdev) { if (!strncmp(pdev->name, driver_name, IFNAMSIZ)) { dev = pdev; + dev_hold(dev); break; } } + read_unlock(&dev_base_lock); if (!dev) return -ENODEV; - /* Find Ethernet bearer for device (or create one) */ - - while ((eb_ptr != stop) && eb_ptr->dev && (eb_ptr->dev != dev)) - eb_ptr++; - if (eb_ptr == stop) - return -EDQUOT; - if (!eb_ptr->dev) { - eb_ptr->dev = dev; - eb_ptr->tipc_packet_type.type = htons(ETH_P_TIPC); - eb_ptr->tipc_packet_type.dev = dev; - eb_ptr->tipc_packet_type.func = recv_msg; - eb_ptr->tipc_packet_type.af_packet_priv = eb_ptr; - INIT_LIST_HEAD(&(eb_ptr->tipc_packet_type.list)); - dev_hold(dev); - INIT_WORK(&eb_ptr->setup, setup_bearer); - schedule_work(&eb_ptr->setup); - } + /* Create Ethernet bearer for device */ + + eb_ptr->dev = dev; + eb_ptr->tipc_packet_type.type = htons(ETH_P_TIPC); + eb_ptr->tipc_packet_type.dev = dev; + eb_ptr->tipc_packet_type.func = recv_msg; + eb_ptr->tipc_packet_type.af_packet_priv = eb_ptr; + INIT_LIST_HEAD(&(eb_ptr->tipc_packet_type.list)); + INIT_WORK(&eb_ptr->setup, setup_bearer); + schedule_work(&eb_ptr->setup); /* Associate TIPC bearer with Ethernet bearer */ diff --git a/net/tipc/link.c b/net/tipc/link.c index 5ed4b4f..ae98a72 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -332,15 +332,16 @@ struct link *tipc_link_create(struct tipc_node *n_ptr, l_ptr->addr = peer; if_name = strchr(b_ptr->name, ':') + 1; - sprintf(l_ptr->name, "%u.%u.%u:%s-%u.%u.%u:", + sprintf(l_ptr->name, "%u.%u.%u:%s-%u.%u.%u:unknown", tipc_zone(tipc_own_addr), tipc_cluster(tipc_own_addr), tipc_node(tipc_own_addr), if_name, tipc_zone(peer), tipc_cluster(peer), tipc_node(peer)); - /* note: peer i/f is appended to link name by reset/activate */ + /* note: peer i/f name is updated by reset/activate message */ memcpy(&l_ptr->media_addr, media_addr, sizeof(*media_addr)); l_ptr->owner = n_ptr; l_ptr->checkpoint = 1; + l_ptr->peer_session = INVALID_SESSION; l_ptr->b_ptr = b_ptr; link_set_supervision_props(l_ptr, b_ptr->media->tolerance); l_ptr->state = RESET_UNKNOWN; @@ -536,9 +537,6 @@ void tipc_link_stop(struct link *l_ptr) l_ptr->proto_msg_queue = NULL; } -/* LINK EVENT CODE IS NOT SUPPORTED AT PRESENT */ -#define link_send_event(fcn, l_ptr, up) do { } while (0) - void tipc_link_reset(struct link *l_ptr) { struct sk_buff *buf; @@ -596,10 +594,6 @@ void tipc_link_reset(struct link *l_ptr) l_ptr->fsm_msg_cnt = 0; l_ptr->stale_count = 0; link_reset_statistics(l_ptr); - - link_send_event(tipc_cfg_link_event, l_ptr, 0); - if (!in_own_cluster(l_ptr->addr)) - link_send_event(tipc_disc_link_event, l_ptr, 0); } @@ -608,9 +602,6 @@ static void link_activate(struct link *l_ptr) l_ptr->next_in_no = l_ptr->stats.recv_info = 1; tipc_node_link_up(l_ptr->owner, l_ptr); tipc_bearer_add_dest(l_ptr->b_ptr, l_ptr->addr); - link_send_event(tipc_cfg_link_event, l_ptr, 1); - if (!in_own_cluster(l_ptr->addr)) - link_send_event(tipc_disc_link_event, l_ptr, 1); } /** @@ -985,6 +976,51 @@ int tipc_link_send(struct sk_buff *buf, u32 dest, u32 selector) } /* + * tipc_link_send_names - send name table entries to new neighbor + * + * Send routine for bulk delivery of name table messages when contact + * with a new neighbor occurs. No link congestion checking is performed + * because name table messages *must* be delivered. The messages must be + * small enough not to require fragmentation. + * Called without any locks held. + */ + +void tipc_link_send_names(struct list_head *message_list, u32 dest) +{ + struct tipc_node *n_ptr; + struct link *l_ptr; + struct sk_buff *buf; + struct sk_buff *temp_buf; + + if (list_empty(message_list)) + return; + + read_lock_bh(&tipc_net_lock); + n_ptr = tipc_node_find(dest); + if (n_ptr) { + tipc_node_lock(n_ptr); + l_ptr = n_ptr->active_links[0]; + if (l_ptr) { + /* convert circular list to linear list */ + ((struct sk_buff *)message_list->prev)->next = NULL; + link_add_chain_to_outqueue(l_ptr, + (struct sk_buff *)message_list->next, 0); + tipc_link_push_queue(l_ptr); + INIT_LIST_HEAD(message_list); + } + tipc_node_unlock(n_ptr); + } + read_unlock_bh(&tipc_net_lock); + + /* discard the messages if they couldn't be sent */ + + list_for_each_safe(buf, temp_buf, ((struct sk_buff *)message_list)) { + list_del((struct list_head *)buf); + buf_discard(buf); + } +} + +/* * link_send_buf_fast: Entry for data messages where the * destination link is known and the header is complete, * inclusive total message length. Very time critical. @@ -1031,9 +1067,6 @@ int tipc_send_buf_fast(struct sk_buff *buf, u32 destnode) u32 selector = msg_origport(buf_msg(buf)) & 1; u32 dummy; - if (destnode == tipc_own_addr) - return tipc_port_recv_msg(buf); - read_lock_bh(&tipc_net_lock); n_ptr = tipc_node_find(destnode); if (likely(n_ptr)) { @@ -1572,7 +1605,7 @@ static struct sk_buff *link_insert_deferred_queue(struct link *l_ptr, static int link_recv_buf_validate(struct sk_buff *buf) { static u32 min_data_hdr_size[8] = { - SHORT_H_SIZE, MCAST_H_SIZE, LONG_H_SIZE, DIR_MSG_H_SIZE, + SHORT_H_SIZE, MCAST_H_SIZE, NAMED_H_SIZE, BASIC_H_SIZE, MAX_H_SIZE, MAX_H_SIZE, MAX_H_SIZE, MAX_H_SIZE }; @@ -1658,19 +1691,12 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr) continue; } + /* Discard unicast link messages destined for another node */ + if (unlikely(!msg_short(msg) && (msg_destnode(msg) != tipc_own_addr))) goto cont; - /* Discard non-routeable messages destined for another node */ - - if (unlikely(!msg_isdata(msg) && - (msg_destnode(msg) != tipc_own_addr))) { - if ((msg_user(msg) != CONN_MANAGER) && - (msg_user(msg) != MSG_FRAGMENTER)) - goto cont; - } - /* Locate neighboring node that sent message */ n_ptr = tipc_node_find(msg_prevnode(msg)); @@ -1678,17 +1704,24 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr) goto cont; tipc_node_lock(n_ptr); - /* Don't talk to neighbor during cleanup after last session */ + /* Locate unicast link endpoint that should handle message */ - if (n_ptr->cleanup_required) { + l_ptr = n_ptr->links[b_ptr->identity]; + if (unlikely(!l_ptr)) { tipc_node_unlock(n_ptr); goto cont; } - /* Locate unicast link endpoint that should handle message */ + /* Verify that communication with node is currently allowed */ - l_ptr = n_ptr->links[b_ptr->identity]; - if (unlikely(!l_ptr)) { + if ((n_ptr->block_setup & WAIT_PEER_DOWN) && + msg_user(msg) == LINK_PROTOCOL && + (msg_type(msg) == RESET_MSG || + msg_type(msg) == ACTIVATE_MSG) && + !msg_redundant_link(msg)) + n_ptr->block_setup &= ~WAIT_PEER_DOWN; + + if (n_ptr->block_setup) { tipc_node_unlock(n_ptr); goto cont; } @@ -1923,6 +1956,12 @@ void tipc_link_send_proto_msg(struct link *l_ptr, u32 msg_typ, int probe_msg, if (link_blocked(l_ptr)) return; + + /* Abort non-RESET send if communication with node is prohibited */ + + if ((l_ptr->owner->block_setup) && (msg_typ != RESET_MSG)) + return; + msg_set_type(msg, msg_typ); msg_set_net_plane(msg, l_ptr->b_ptr->net_plane); msg_set_bcast_ack(msg, mod(l_ptr->owner->bclink.last_in)); @@ -2051,9 +2090,19 @@ static void link_recv_proto_msg(struct link *l_ptr, struct sk_buff *buf) case RESET_MSG: if (!link_working_unknown(l_ptr) && (l_ptr->peer_session != INVALID_SESSION)) { - if (msg_session(msg) == l_ptr->peer_session) - break; /* duplicate: ignore */ + if (less_eq(msg_session(msg), l_ptr->peer_session)) + break; /* duplicate or old reset: ignore */ + } + + if (!msg_redundant_link(msg) && (link_working_working(l_ptr) || + link_working_unknown(l_ptr))) { + /* + * peer has lost contact -- don't allow peer's links + * to reactivate before we recognize loss & clean up + */ + l_ptr->owner->block_setup = WAIT_NODE_DOWN; } + /* fall thru' */ case ACTIVATE_MSG: /* Update link settings according other endpoint's values */ @@ -2553,7 +2602,7 @@ int tipc_link_recv_fragment(struct sk_buff **pending, struct sk_buff **fb, u32 msg_sz = msg_size(imsg); u32 fragm_sz = msg_data_sz(fragm); u32 exp_fragm_cnt = msg_sz/fragm_sz + !!(msg_sz % fragm_sz); - u32 max = TIPC_MAX_USER_MSG_SIZE + LONG_H_SIZE; + u32 max = TIPC_MAX_USER_MSG_SIZE + NAMED_H_SIZE; if (msg_type(imsg) == TIPC_MCAST_MSG) max = TIPC_MAX_USER_MSG_SIZE + MCAST_H_SIZE; if (msg_size(imsg) > max) { @@ -2882,7 +2931,7 @@ static int tipc_link_stats(const char *name, char *buf, const u32 buf_size) profile_total = 1; tipc_printf(&pb, " TX profile sample:%u packets average:%u octets\n" " 0-64:%u%% -256:%u%% -1024:%u%% -4096:%u%% " - "-16354:%u%% -32768:%u%% -66000:%u%%\n", + "-16384:%u%% -32768:%u%% -66000:%u%%\n", l_ptr->stats.msg_length_counts, l_ptr->stats.msg_lengths_total / profile_total, percent(l_ptr->stats.msg_length_profile[0], profile_total), diff --git a/net/tipc/link.h b/net/tipc/link.h index 74fbeca..e56cb53 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -223,6 +223,7 @@ struct sk_buff *tipc_link_cmd_show_stats(const void *req_tlv_area, int req_tlv_s struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area, int req_tlv_space); void tipc_link_reset(struct link *l_ptr); int tipc_link_send(struct sk_buff *buf, u32 dest, u32 selector); +void tipc_link_send_names(struct list_head *message_list, u32 dest); int tipc_link_send_buf(struct link *l_ptr, struct sk_buff *buf); u32 tipc_link_get_max_pkt(u32 dest, u32 selector); int tipc_link_send_sections_fast(struct tipc_port *sender, diff --git a/net/tipc/msg.c b/net/tipc/msg.c index 03e57bf..83d5096 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -61,10 +61,8 @@ void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, msg_set_size(m, hsize); msg_set_prevnode(m, tipc_own_addr); msg_set_type(m, type); - if (!msg_short(m)) { - msg_set_orignode(m, tipc_own_addr); - msg_set_destnode(m, destnode); - } + msg_set_orignode(m, tipc_own_addr); + msg_set_destnode(m, destnode); } /** diff --git a/net/tipc/msg.h b/net/tipc/msg.h index 8452454..d93178f 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -68,10 +68,10 @@ * Message header sizes */ -#define SHORT_H_SIZE 24 /* Connected, in-cluster messages */ -#define DIR_MSG_H_SIZE 32 /* Directly addressed messages */ -#define LONG_H_SIZE 40 /* Named messages */ -#define MCAST_H_SIZE 44 /* Multicast messages */ +#define SHORT_H_SIZE 24 /* In-cluster basic payload message */ +#define BASIC_H_SIZE 32 /* Basic payload message */ +#define NAMED_H_SIZE 40 /* Named payload message */ +#define MCAST_H_SIZE 44 /* Multicast payload message */ #define INT_H_SIZE 40 /* Internal messages */ #define MIN_H_SIZE 24 /* Smallest legal TIPC header size */ #define MAX_H_SIZE 60 /* Largest possible TIPC header size */ @@ -311,26 +311,6 @@ static inline void msg_set_seqno(struct tipc_msg *m, u32 n) } /* - * TIPC may utilize the "link ack #" and "link seq #" fields of a short - * message header to hold the destination node for the message, since the - * normal "dest node" field isn't present. This cache is only referenced - * when required, so populating the cache of a longer message header is - * harmless (as long as the header has the two link sequence fields present). - * - * Note: Host byte order is OK here, since the info never goes off-card. - */ - -static inline u32 msg_destnode_cache(struct tipc_msg *m) -{ - return m->hdr[2]; -} - -static inline void msg_set_destnode_cache(struct tipc_msg *m, u32 dnode) -{ - m->hdr[2] = dnode; -} - -/* * Words 3-10 */ @@ -377,7 +357,7 @@ static inline void msg_set_mc_netid(struct tipc_msg *m, u32 p) static inline int msg_short(struct tipc_msg *m) { - return msg_hdr_sz(m) == 24; + return msg_hdr_sz(m) == SHORT_H_SIZE; } static inline u32 msg_orignode(struct tipc_msg *m) @@ -635,7 +615,7 @@ static inline u32 msg_link_selector(struct tipc_msg *m) static inline void msg_set_link_selector(struct tipc_msg *m, u32 n) { - msg_set_bits(m, 4, 0, 1, (n & 1)); + msg_set_bits(m, 4, 0, 1, n); } /* @@ -659,7 +639,7 @@ static inline u32 msg_probe(struct tipc_msg *m) static inline void msg_set_probe(struct tipc_msg *m, u32 val) { - msg_set_bits(m, 5, 0, 1, (val & 1)); + msg_set_bits(m, 5, 0, 1, val); } static inline char msg_net_plane(struct tipc_msg *m) diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index 80025a1..b7ca1bd 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -94,13 +94,13 @@ static void publ_to_item(struct distr_item *i, struct publication *p) static struct sk_buff *named_prepare_buf(u32 type, u32 size, u32 dest) { - struct sk_buff *buf = tipc_buf_acquire(LONG_H_SIZE + size); + struct sk_buff *buf = tipc_buf_acquire(INT_H_SIZE + size); struct tipc_msg *msg; if (buf != NULL) { msg = buf_msg(buf); - tipc_msg_init(msg, NAME_DISTRIBUTOR, type, LONG_H_SIZE, dest); - msg_set_size(msg, LONG_H_SIZE + size); + tipc_msg_init(msg, NAME_DISTRIBUTOR, type, INT_H_SIZE, dest); + msg_set_size(msg, INT_H_SIZE + size); } return buf; } @@ -173,18 +173,40 @@ void tipc_named_withdraw(struct publication *publ) * tipc_named_node_up - tell specified node about all publications by this node */ -void tipc_named_node_up(unsigned long node) +void tipc_named_node_up(unsigned long nodearg) { + struct tipc_node *n_ptr; + struct link *l_ptr; struct publication *publ; struct distr_item *item = NULL; struct sk_buff *buf = NULL; + struct list_head message_list; + u32 node = (u32)nodearg; u32 left = 0; u32 rest; - u32 max_item_buf; + u32 max_item_buf = 0; + + /* compute maximum amount of publication data to send per message */ + + read_lock_bh(&tipc_net_lock); + n_ptr = tipc_node_find(node); + if (n_ptr) { + tipc_node_lock(n_ptr); + l_ptr = n_ptr->active_links[0]; + if (l_ptr) + max_item_buf = ((l_ptr->max_pkt - INT_H_SIZE) / + ITEM_SIZE) * ITEM_SIZE; + tipc_node_unlock(n_ptr); + } + read_unlock_bh(&tipc_net_lock); + if (!max_item_buf) + return; + + /* create list of publication messages, then send them as a unit */ + + INIT_LIST_HEAD(&message_list); read_lock_bh(&tipc_nametbl_lock); - max_item_buf = TIPC_MAX_USER_MSG_SIZE / ITEM_SIZE; - max_item_buf *= ITEM_SIZE; rest = publ_cnt * ITEM_SIZE; list_for_each_entry(publ, &publ_root, local_list) { @@ -202,13 +224,14 @@ void tipc_named_node_up(unsigned long node) item++; left -= ITEM_SIZE; if (!left) { - msg_set_link_selector(buf_msg(buf), node); - tipc_link_send(buf, node, node); + list_add_tail((struct list_head *)buf, &message_list); buf = NULL; } } exit: read_unlock_bh(&tipc_nametbl_lock); + + tipc_link_send_names(&message_list, (u32)node); } /** diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index 205ed4a..46e6b6c 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -2,7 +2,7 @@ * net/tipc/name_table.c: TIPC name table code * * Copyright (c) 2000-2006, Ericsson AB - * Copyright (c) 2004-2008, Wind River Systems + * Copyright (c) 2004-2008, 2010-2011, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,9 +44,7 @@ static int tipc_nametbl_size = 1024; /* must be a power of 2 */ /** - * struct sub_seq - container for all published instances of a name sequence - * @lower: name sequence lower bound - * @upper: name sequence upper bound + * struct name_info - name sequence publication info * @node_list: circular list of publications made by own node * @cluster_list: circular list of publications made by own cluster * @zone_list: circular list of publications made by own zone @@ -59,18 +57,29 @@ static int tipc_nametbl_size = 1024; /* must be a power of 2 */ * (The cluster and node lists may be empty.) */ -struct sub_seq { - u32 lower; - u32 upper; - struct publication *node_list; - struct publication *cluster_list; - struct publication *zone_list; +struct name_info { + struct list_head node_list; + struct list_head cluster_list; + struct list_head zone_list; u32 node_list_size; u32 cluster_list_size; u32 zone_list_size; }; /** + * struct sub_seq - container for all published instances of a name sequence + * @lower: name sequence lower bound + * @upper: name sequence upper bound + * @info: pointer to name sequence publication info + */ + +struct sub_seq { + u32 lower; + u32 upper; + struct name_info *info; +}; + +/** * struct name_seq - container for all published instances of a name type * @type: 32 bit 'type' value for name sequence * @sseq: pointer to dynamically-sized array of sub-sequences of this 'type'; @@ -246,6 +255,7 @@ static struct publication *tipc_nameseq_insert_publ(struct name_seq *nseq, struct subscription *st; struct publication *publ; struct sub_seq *sseq; + struct name_info *info; int created_subseq = 0; sseq = nameseq_find_subseq(nseq, lower); @@ -258,6 +268,8 @@ static struct publication *tipc_nameseq_insert_publ(struct name_seq *nseq, type, lower, upper); return NULL; } + + info = sseq->info; } else { u32 inspos; struct sub_seq *freesseq; @@ -292,6 +304,17 @@ static struct publication *tipc_nameseq_insert_publ(struct name_seq *nseq, nseq->alloc *= 2; } + info = kzalloc(sizeof(*info), GFP_ATOMIC); + if (!info) { + warn("Cannot publish {%u,%u,%u}, no memory\n", + type, lower, upper); + return NULL; + } + + INIT_LIST_HEAD(&info->node_list); + INIT_LIST_HEAD(&info->cluster_list); + INIT_LIST_HEAD(&info->zone_list); + /* Insert new sub-sequence */ sseq = &nseq->sseqs[inspos]; @@ -301,6 +324,7 @@ static struct publication *tipc_nameseq_insert_publ(struct name_seq *nseq, nseq->first_free++; sseq->lower = lower; sseq->upper = upper; + sseq->info = info; created_subseq = 1; } @@ -310,33 +334,17 @@ static struct publication *tipc_nameseq_insert_publ(struct name_seq *nseq, if (!publ) return NULL; - sseq->zone_list_size++; - if (!sseq->zone_list) - sseq->zone_list = publ->zone_list_next = publ; - else { - publ->zone_list_next = sseq->zone_list->zone_list_next; - sseq->zone_list->zone_list_next = publ; - } + list_add(&publ->zone_list, &info->zone_list); + info->zone_list_size++; if (in_own_cluster(node)) { - sseq->cluster_list_size++; - if (!sseq->cluster_list) - sseq->cluster_list = publ->cluster_list_next = publ; - else { - publ->cluster_list_next = - sseq->cluster_list->cluster_list_next; - sseq->cluster_list->cluster_list_next = publ; - } + list_add(&publ->cluster_list, &info->cluster_list); + info->cluster_list_size++; } if (node == tipc_own_addr) { - sseq->node_list_size++; - if (!sseq->node_list) - sseq->node_list = publ->node_list_next = publ; - else { - publ->node_list_next = sseq->node_list->node_list_next; - sseq->node_list->node_list_next = publ; - } + list_add(&publ->node_list, &info->node_list); + info->node_list_size++; } /* @@ -370,9 +378,8 @@ static struct publication *tipc_nameseq_remove_publ(struct name_seq *nseq, u32 i u32 node, u32 ref, u32 key) { struct publication *publ; - struct publication *curr; - struct publication *prev; struct sub_seq *sseq = nameseq_find_subseq(nseq, inst); + struct name_info *info; struct sub_seq *free; struct subscription *s, *st; int removed_subseq = 0; @@ -380,96 +387,41 @@ static struct publication *tipc_nameseq_remove_publ(struct name_seq *nseq, u32 i if (!sseq) return NULL; - /* Remove publication from zone scope list */ + info = sseq->info; - prev = sseq->zone_list; - publ = sseq->zone_list->zone_list_next; - while ((publ->key != key) || (publ->ref != ref) || - (publ->node && (publ->node != node))) { - prev = publ; - publ = publ->zone_list_next; - if (prev == sseq->zone_list) { + /* Locate publication, if it exists */ - /* Prevent endless loop if publication not found */ - - return NULL; - } - } - if (publ != sseq->zone_list) - prev->zone_list_next = publ->zone_list_next; - else if (publ->zone_list_next != publ) { - prev->zone_list_next = publ->zone_list_next; - sseq->zone_list = publ->zone_list_next; - } else { - sseq->zone_list = NULL; + list_for_each_entry(publ, &info->zone_list, zone_list) { + if ((publ->key == key) && (publ->ref == ref) && + (!publ->node || (publ->node == node))) + goto found; } - sseq->zone_list_size--; + return NULL; + +found: + /* Remove publication from zone scope list */ + + list_del(&publ->zone_list); + info->zone_list_size--; /* Remove publication from cluster scope list, if present */ if (in_own_cluster(node)) { - prev = sseq->cluster_list; - curr = sseq->cluster_list->cluster_list_next; - while (curr != publ) { - prev = curr; - curr = curr->cluster_list_next; - if (prev == sseq->cluster_list) { - - /* Prevent endless loop for malformed list */ - - err("Unable to de-list cluster publication\n" - "{%u%u}, node=0x%x, ref=%u, key=%u)\n", - publ->type, publ->lower, publ->node, - publ->ref, publ->key); - goto end_cluster; - } - } - if (publ != sseq->cluster_list) - prev->cluster_list_next = publ->cluster_list_next; - else if (publ->cluster_list_next != publ) { - prev->cluster_list_next = publ->cluster_list_next; - sseq->cluster_list = publ->cluster_list_next; - } else { - sseq->cluster_list = NULL; - } - sseq->cluster_list_size--; + list_del(&publ->cluster_list); + info->cluster_list_size--; } -end_cluster: /* Remove publication from node scope list, if present */ if (node == tipc_own_addr) { - prev = sseq->node_list; - curr = sseq->node_list->node_list_next; - while (curr != publ) { - prev = curr; - curr = curr->node_list_next; - if (prev == sseq->node_list) { - - /* Prevent endless loop for malformed list */ - - err("Unable to de-list node publication\n" - "{%u%u}, node=0x%x, ref=%u, key=%u)\n", - publ->type, publ->lower, publ->node, - publ->ref, publ->key); - goto end_node; - } - } - if (publ != sseq->node_list) - prev->node_list_next = publ->node_list_next; - else if (publ->node_list_next != publ) { - prev->node_list_next = publ->node_list_next; - sseq->node_list = publ->node_list_next; - } else { - sseq->node_list = NULL; - } - sseq->node_list_size--; + list_del(&publ->node_list); + info->node_list_size--; } -end_node: /* Contract subseq list if no more publications for that subseq */ - if (!sseq->zone_list) { + if (list_empty(&info->zone_list)) { + kfree(info); free = &nseq->sseqs[nseq->first_free--]; memmove(sseq, sseq + 1, (free - (sseq + 1)) * sizeof(*sseq)); removed_subseq = 1; @@ -506,12 +458,12 @@ static void tipc_nameseq_subscribe(struct name_seq *nseq, struct subscription *s return; while (sseq != &nseq->sseqs[nseq->first_free]) { - struct publication *zl = sseq->zone_list; - if (zl && tipc_subscr_overlap(s, sseq->lower, sseq->upper)) { - struct publication *crs = zl; + if (tipc_subscr_overlap(s, sseq->lower, sseq->upper)) { + struct publication *crs; + struct name_info *info = sseq->info; int must_report = 1; - do { + list_for_each_entry(crs, &info->zone_list, zone_list) { tipc_subscr_report_overlap(s, sseq->lower, sseq->upper, @@ -520,8 +472,7 @@ static void tipc_nameseq_subscribe(struct name_seq *nseq, struct subscription *s crs->node, must_report); must_report = 0; - crs = crs->zone_list_next; - } while (crs != zl); + } } sseq++; } @@ -591,9 +542,10 @@ struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower, u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode) { struct sub_seq *sseq; - struct publication *publ = NULL; + struct name_info *info; + struct publication *publ; struct name_seq *seq; - u32 ref; + u32 ref = 0; if (!tipc_in_scope(*destnode, tipc_own_addr)) return 0; @@ -606,55 +558,57 @@ u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode) if (unlikely(!sseq)) goto not_found; spin_lock_bh(&seq->lock); + info = sseq->info; /* Closest-First Algorithm: */ if (likely(!*destnode)) { - publ = sseq->node_list; - if (publ) { - sseq->node_list = publ->node_list_next; -found: - ref = publ->ref; - *destnode = publ->node; - spin_unlock_bh(&seq->lock); - read_unlock_bh(&tipc_nametbl_lock); - return ref; - } - publ = sseq->cluster_list; - if (publ) { - sseq->cluster_list = publ->cluster_list_next; - goto found; - } - publ = sseq->zone_list; - if (publ) { - sseq->zone_list = publ->zone_list_next; - goto found; + if (!list_empty(&info->node_list)) { + publ = list_first_entry(&info->node_list, + struct publication, + node_list); + list_move_tail(&publ->node_list, + &info->node_list); + } else if (!list_empty(&info->cluster_list)) { + publ = list_first_entry(&info->cluster_list, + struct publication, + cluster_list); + list_move_tail(&publ->cluster_list, + &info->cluster_list); + } else { + publ = list_first_entry(&info->zone_list, + struct publication, + zone_list); + list_move_tail(&publ->zone_list, + &info->zone_list); } } /* Round-Robin Algorithm: */ else if (*destnode == tipc_own_addr) { - publ = sseq->node_list; - if (publ) { - sseq->node_list = publ->node_list_next; - goto found; - } + if (list_empty(&info->node_list)) + goto no_match; + publ = list_first_entry(&info->node_list, struct publication, + node_list); + list_move_tail(&publ->node_list, &info->node_list); } else if (in_own_cluster(*destnode)) { - publ = sseq->cluster_list; - if (publ) { - sseq->cluster_list = publ->cluster_list_next; - goto found; - } + if (list_empty(&info->cluster_list)) + goto no_match; + publ = list_first_entry(&info->cluster_list, struct publication, + cluster_list); + list_move_tail(&publ->cluster_list, &info->cluster_list); } else { - publ = sseq->zone_list; - if (publ) { - sseq->zone_list = publ->zone_list_next; - goto found; - } + publ = list_first_entry(&info->zone_list, struct publication, + zone_list); + list_move_tail(&publ->zone_list, &info->zone_list); } + + ref = publ->ref; + *destnode = publ->node; +no_match: spin_unlock_bh(&seq->lock); not_found: read_unlock_bh(&tipc_nametbl_lock); - return 0; + return ref; } /** @@ -676,6 +630,7 @@ int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit, struct name_seq *seq; struct sub_seq *sseq; struct sub_seq *sseq_stop; + struct name_info *info; int res = 0; read_lock_bh(&tipc_nametbl_lock); @@ -693,16 +648,13 @@ int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit, if (sseq->lower > upper) break; - publ = sseq->node_list; - if (publ) { - do { - if (publ->scope <= limit) - tipc_port_list_add(dports, publ->ref); - publ = publ->node_list_next; - } while (publ != sseq->node_list); + info = sseq->info; + list_for_each_entry(publ, &info->node_list, node_list) { + if (publ->scope <= limit) + tipc_port_list_add(dports, publ->ref); } - if (sseq->cluster_list_size != sseq->node_list_size) + if (info->cluster_list_size != info->node_list_size) res = 1; } @@ -840,16 +792,19 @@ static void subseq_list(struct sub_seq *sseq, struct print_buf *buf, u32 depth, { char portIdStr[27]; const char *scope_str[] = {"", " zone", " cluster", " node"}; - struct publication *publ = sseq->zone_list; + struct publication *publ; + struct name_info *info; tipc_printf(buf, "%-10u %-10u ", sseq->lower, sseq->upper); - if (depth == 2 || !publ) { + if (depth == 2) { tipc_printf(buf, "\n"); return; } - do { + info = sseq->info; + + list_for_each_entry(publ, &info->zone_list, zone_list) { sprintf(portIdStr, "<%u.%u.%u:%u>", tipc_zone(publ->node), tipc_cluster(publ->node), tipc_node(publ->node), publ->ref); @@ -858,13 +813,9 @@ static void subseq_list(struct sub_seq *sseq, struct print_buf *buf, u32 depth, tipc_printf(buf, "%-10u %s", publ->key, scope_str[publ->scope]); } - - publ = publ->zone_list_next; - if (publ == sseq->zone_list) - break; - - tipc_printf(buf, "\n%33s", " "); - } while (1); + if (!list_is_last(&publ->zone_list, &info->zone_list)) + tipc_printf(buf, "\n%33s", " "); + }; tipc_printf(buf, "\n"); } diff --git a/net/tipc/name_table.h b/net/tipc/name_table.h index d228bd6..62d77e5 100644 --- a/net/tipc/name_table.h +++ b/net/tipc/name_table.h @@ -2,7 +2,7 @@ * net/tipc/name_table.h: Include file for TIPC name table code * * Copyright (c) 2000-2006, Ericsson AB - * Copyright (c) 2004-2005, Wind River Systems + * Copyright (c) 2004-2005, 2010-2011, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -61,9 +61,9 @@ struct port_list; * @subscr: subscription to "node down" event (for off-node publications only) * @local_list: adjacent entries in list of publications made by this node * @pport_list: adjacent entries in list of publications made by this port - * @node_list: next matching name seq publication with >= node scope - * @cluster_list: next matching name seq publication with >= cluster scope - * @zone_list: next matching name seq publication with >= zone scope + * @node_list: adjacent matching name seq publications with >= node scope + * @cluster_list: adjacent matching name seq publications with >= cluster scope + * @zone_list: adjacent matching name seq publications with >= zone scope * * Note that the node list, cluster list, and zone list are circular lists. */ @@ -79,9 +79,9 @@ struct publication { struct tipc_node_subscr subscr; struct list_head local_list; struct list_head pport_list; - struct publication *node_list_next; - struct publication *cluster_list_next; - struct publication *zone_list_next; + struct list_head node_list; + struct list_head cluster_list; + struct list_head zone_list; }; diff --git a/net/tipc/net.c b/net/tipc/net.c index 68b3dd6..fafef6c 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -141,17 +141,6 @@ void tipc_net_route_msg(struct sk_buff *buf) return; msg = buf_msg(buf); - msg_incr_reroute_cnt(msg); - if (msg_reroute_cnt(msg) > 6) { - if (msg_errcode(msg)) { - buf_discard(buf); - } else { - tipc_reject_msg(buf, msg_destport(msg) ? - TIPC_ERR_NO_PORT : TIPC_ERR_NO_NAME); - } - return; - } - /* Handle message for this node */ dnode = msg_short(msg) ? tipc_own_addr : msg_destnode(msg); if (tipc_in_scope(dnode, tipc_own_addr)) { diff --git a/net/tipc/node.c b/net/tipc/node.c index 2d106ef..27b4bb0 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -112,6 +112,7 @@ struct tipc_node *tipc_node_create(u32 addr) break; } list_add_tail(&n_ptr->list, &temp_node->list); + n_ptr->block_setup = WAIT_PEER_DOWN; tipc_num_nodes++; @@ -312,7 +313,7 @@ static void node_established_contact(struct tipc_node *n_ptr) } } -static void node_cleanup_finished(unsigned long node_addr) +static void node_name_purge_complete(unsigned long node_addr) { struct tipc_node *n_ptr; @@ -320,7 +321,7 @@ static void node_cleanup_finished(unsigned long node_addr) n_ptr = tipc_node_find(node_addr); if (n_ptr) { tipc_node_lock(n_ptr); - n_ptr->cleanup_required = 0; + n_ptr->block_setup &= ~WAIT_NAMES_GONE; tipc_node_unlock(n_ptr); } read_unlock_bh(&tipc_net_lock); @@ -331,28 +332,32 @@ static void node_lost_contact(struct tipc_node *n_ptr) char addr_string[16]; u32 i; - /* Clean up broadcast reception remains */ - n_ptr->bclink.gap_after = n_ptr->bclink.gap_to = 0; - while (n_ptr->bclink.deferred_head) { - struct sk_buff *buf = n_ptr->bclink.deferred_head; - n_ptr->bclink.deferred_head = buf->next; - buf_discard(buf); - } - if (n_ptr->bclink.defragm) { - buf_discard(n_ptr->bclink.defragm); - n_ptr->bclink.defragm = NULL; - } + info("Lost contact with %s\n", + tipc_addr_string_fill(addr_string, n_ptr->addr)); + + /* Flush broadcast link info associated with lost node */ if (n_ptr->bclink.supported) { + n_ptr->bclink.gap_after = n_ptr->bclink.gap_to = 0; + while (n_ptr->bclink.deferred_head) { + struct sk_buff *buf = n_ptr->bclink.deferred_head; + n_ptr->bclink.deferred_head = buf->next; + buf_discard(buf); + } + + if (n_ptr->bclink.defragm) { + buf_discard(n_ptr->bclink.defragm); + n_ptr->bclink.defragm = NULL; + } + + tipc_nmap_remove(&tipc_bcast_nmap, n_ptr->addr); tipc_bclink_acknowledge(n_ptr, mod(n_ptr->bclink.acked + 10000)); - tipc_nmap_remove(&tipc_bcast_nmap, n_ptr->addr); if (n_ptr->addr < tipc_own_addr) tipc_own_tag--; - } - info("Lost contact with %s\n", - tipc_addr_string_fill(addr_string, n_ptr->addr)); + n_ptr->bclink.supported = 0; + } /* Abort link changeover */ for (i = 0; i < MAX_BEARERS; i++) { @@ -367,10 +372,10 @@ static void node_lost_contact(struct tipc_node *n_ptr) /* Notify subscribers */ tipc_nodesub_notify(n_ptr); - /* Prevent re-contact with node until all cleanup is done */ + /* Prevent re-contact with node until cleanup is done */ - n_ptr->cleanup_required = 1; - tipc_k_signal((Handler)node_cleanup_finished, n_ptr->addr); + n_ptr->block_setup = WAIT_PEER_DOWN | WAIT_NAMES_GONE; + tipc_k_signal((Handler)node_name_purge_complete, n_ptr->addr); } struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space) diff --git a/net/tipc/node.h b/net/tipc/node.h index 5c61afc..4f15cb4 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h @@ -42,6 +42,12 @@ #include "net.h" #include "bearer.h" +/* Flags used to block (re)establishment of contact with a neighboring node */ + +#define WAIT_PEER_DOWN 0x0001 /* wait to see that peer's links are down */ +#define WAIT_NAMES_GONE 0x0002 /* wait for peer's publications to be purged */ +#define WAIT_NODE_DOWN 0x0004 /* wait until peer node is declared down */ + /** * struct tipc_node - TIPC node structure * @addr: network address of node @@ -52,7 +58,7 @@ * @active_links: pointers to active links to node * @links: pointers to all links to node * @working_links: number of working links to node (both active and standby) - * @cleanup_required: non-zero if cleaning up after a prior loss of contact + * @block_setup: bit mask of conditions preventing link establishment to node * @link_cnt: number of links to node * @permit_changeover: non-zero if node has redundant links to this system * @bclink: broadcast-related info @@ -77,7 +83,7 @@ struct tipc_node { struct link *links[MAX_BEARERS]; int link_cnt; int working_links; - int cleanup_required; + int block_setup; int permit_changeover; struct { int supported; diff --git a/net/tipc/port.c b/net/tipc/port.c index c68dc95..54d812a 100644 --- a/net/tipc/port.c +++ b/net/tipc/port.c @@ -222,7 +222,7 @@ struct tipc_port *tipc_createport_raw(void *usr_handle, p_ptr->max_pkt = MAX_PKT_DEFAULT; p_ptr->ref = ref; msg = &p_ptr->phdr; - tipc_msg_init(msg, importance, TIPC_NAMED_MSG, LONG_H_SIZE, 0); + tipc_msg_init(msg, importance, TIPC_NAMED_MSG, NAMED_H_SIZE, 0); msg_set_origport(msg, ref); INIT_LIST_HEAD(&p_ptr->wait_list); INIT_LIST_HEAD(&p_ptr->subscription.nodesub_list); @@ -327,26 +327,23 @@ int tipc_set_portunreturnable(u32 ref, unsigned int isunrejectable) } /* - * port_build_proto_msg(): build a port level protocol - * or a connection abortion message. Called with - * tipc_port lock on. + * port_build_proto_msg(): create connection protocol message for port + * + * On entry the port must be locked and connected. */ -static struct sk_buff *port_build_proto_msg(u32 destport, u32 destnode, - u32 origport, u32 orignode, - u32 usr, u32 type, u32 err, - u32 ack) +static struct sk_buff *port_build_proto_msg(struct tipc_port *p_ptr, + u32 type, u32 ack) { struct sk_buff *buf; struct tipc_msg *msg; - buf = tipc_buf_acquire(LONG_H_SIZE); + buf = tipc_buf_acquire(INT_H_SIZE); if (buf) { msg = buf_msg(buf); - tipc_msg_init(msg, usr, type, LONG_H_SIZE, destnode); - msg_set_errcode(msg, err); - msg_set_destport(msg, destport); - msg_set_origport(msg, origport); - msg_set_orignode(msg, orignode); + tipc_msg_init(msg, CONN_MANAGER, type, INT_H_SIZE, + port_peernode(p_ptr)); + msg_set_destport(msg, port_peerport(p_ptr)); + msg_set_origport(msg, p_ptr->ref); msg_set_msgcnt(msg, ack); } return buf; @@ -358,45 +355,48 @@ int tipc_reject_msg(struct sk_buff *buf, u32 err) struct sk_buff *rbuf; struct tipc_msg *rmsg; int hdr_sz; - u32 imp = msg_importance(msg); + u32 imp; u32 data_sz = msg_data_sz(msg); - - if (data_sz > MAX_REJECT_SIZE) - data_sz = MAX_REJECT_SIZE; - if (msg_connected(msg) && (imp < TIPC_CRITICAL_IMPORTANCE)) - imp++; + u32 src_node; + u32 rmsg_sz; /* discard rejected message if it shouldn't be returned to sender */ - if (msg_errcode(msg) || msg_dest_droppable(msg)) { - buf_discard(buf); - return data_sz; - } - /* construct rejected message */ - if (msg_mcast(msg)) - hdr_sz = MCAST_H_SIZE; - else - hdr_sz = LONG_H_SIZE; - rbuf = tipc_buf_acquire(data_sz + hdr_sz); - if (rbuf == NULL) { - buf_discard(buf); - return data_sz; + if (WARN(!msg_isdata(msg), + "attempt to reject message with user=%u", msg_user(msg))) { + dump_stack(); + goto exit; } + if (msg_errcode(msg) || msg_dest_droppable(msg)) + goto exit; + + /* + * construct returned message by copying rejected message header and + * data (or subset), then updating header fields that need adjusting + */ + + hdr_sz = msg_hdr_sz(msg); + rmsg_sz = hdr_sz + min_t(u32, data_sz, MAX_REJECT_SIZE); + + rbuf = tipc_buf_acquire(rmsg_sz); + if (rbuf == NULL) + goto exit; + rmsg = buf_msg(rbuf); - tipc_msg_init(rmsg, imp, msg_type(msg), hdr_sz, msg_orignode(msg)); - msg_set_errcode(rmsg, err); - msg_set_destport(rmsg, msg_origport(msg)); - msg_set_origport(rmsg, msg_destport(msg)); - if (msg_short(msg)) { - msg_set_orignode(rmsg, tipc_own_addr); - /* leave name type & instance as zeroes */ - } else { - msg_set_orignode(rmsg, msg_destnode(msg)); - msg_set_nametype(rmsg, msg_nametype(msg)); - msg_set_nameinst(rmsg, msg_nameinst(msg)); + skb_copy_to_linear_data(rbuf, msg, rmsg_sz); + + if (msg_connected(rmsg)) { + imp = msg_importance(rmsg); + if (imp < TIPC_CRITICAL_IMPORTANCE) + msg_set_importance(rmsg, ++imp); } - msg_set_size(rmsg, data_sz + hdr_sz); - skb_copy_to_linear_data_offset(rbuf, hdr_sz, msg_data(msg), data_sz); + msg_set_non_seq(rmsg, 0); + msg_set_size(rmsg, rmsg_sz); + msg_set_errcode(rmsg, err); + msg_set_prevnode(rmsg, tipc_own_addr); + msg_swap_words(rmsg, 4, 5); + if (!msg_short(rmsg)) + msg_swap_words(rmsg, 6, 7); /* send self-abort message when rejecting on a connected port */ if (msg_connected(msg)) { @@ -411,9 +411,15 @@ int tipc_reject_msg(struct sk_buff *buf, u32 err) tipc_net_route_msg(abuf); } - /* send rejected message */ + /* send returned message & dispose of rejected message */ + + src_node = msg_prevnode(msg); + if (src_node == tipc_own_addr) + tipc_port_recv_msg(rbuf); + else + tipc_link_send(rbuf, src_node, msg_link_selector(rmsg)); +exit: buf_discard(buf); - tipc_net_route_msg(rbuf); return data_sz; } @@ -449,14 +455,7 @@ static void port_timeout(unsigned long ref) if (p_ptr->probing_state == PROBING) { buf = port_build_self_abort_msg(p_ptr, TIPC_ERR_NO_PORT); } else { - buf = port_build_proto_msg(port_peerport(p_ptr), - port_peernode(p_ptr), - p_ptr->ref, - tipc_own_addr, - CONN_MANAGER, - CONN_PROBE, - TIPC_OK, - 0); + buf = port_build_proto_msg(p_ptr, CONN_PROBE, 0); p_ptr->probing_state = PROBING; k_start_timer(&p_ptr->timer, p_ptr->probing_interval); } @@ -480,100 +479,94 @@ static void port_handle_node_down(unsigned long ref) static struct sk_buff *port_build_self_abort_msg(struct tipc_port *p_ptr, u32 err) { - u32 imp = msg_importance(&p_ptr->phdr); + struct sk_buff *buf = port_build_peer_abort_msg(p_ptr, err); - if (!p_ptr->connected) - return NULL; - if (imp < TIPC_CRITICAL_IMPORTANCE) - imp++; - return port_build_proto_msg(p_ptr->ref, - tipc_own_addr, - port_peerport(p_ptr), - port_peernode(p_ptr), - imp, - TIPC_CONN_MSG, - err, - 0); + if (buf) { + struct tipc_msg *msg = buf_msg(buf); + msg_swap_words(msg, 4, 5); + msg_swap_words(msg, 6, 7); + } + return buf; } static struct sk_buff *port_build_peer_abort_msg(struct tipc_port *p_ptr, u32 err) { - u32 imp = msg_importance(&p_ptr->phdr); + struct sk_buff *buf; + struct tipc_msg *msg; + u32 imp; if (!p_ptr->connected) return NULL; - if (imp < TIPC_CRITICAL_IMPORTANCE) - imp++; - return port_build_proto_msg(port_peerport(p_ptr), - port_peernode(p_ptr), - p_ptr->ref, - tipc_own_addr, - imp, - TIPC_CONN_MSG, - err, - 0); + + buf = tipc_buf_acquire(BASIC_H_SIZE); + if (buf) { + msg = buf_msg(buf); + memcpy(msg, &p_ptr->phdr, BASIC_H_SIZE); + msg_set_hdr_sz(msg, BASIC_H_SIZE); + msg_set_size(msg, BASIC_H_SIZE); + imp = msg_importance(msg); + if (imp < TIPC_CRITICAL_IMPORTANCE) + msg_set_importance(msg, ++imp); + msg_set_errcode(msg, err); + } + return buf; } void tipc_port_recv_proto_msg(struct sk_buff *buf) { struct tipc_msg *msg = buf_msg(buf); - struct tipc_port *p_ptr = tipc_port_lock(msg_destport(msg)); - u32 err = TIPC_OK; + struct tipc_port *p_ptr; struct sk_buff *r_buf = NULL; - struct sk_buff *abort_buf = NULL; - - if (!p_ptr) { - err = TIPC_ERR_NO_PORT; - } else if (p_ptr->connected) { - if ((port_peernode(p_ptr) != msg_orignode(msg)) || - (port_peerport(p_ptr) != msg_origport(msg))) { - err = TIPC_ERR_NO_PORT; - } else if (msg_type(msg) == CONN_ACK) { - int wakeup = tipc_port_congested(p_ptr) && - p_ptr->congested && - p_ptr->wakeup; - p_ptr->acked += msg_msgcnt(msg); - if (tipc_port_congested(p_ptr)) - goto exit; - p_ptr->congested = 0; - if (!wakeup) - goto exit; - p_ptr->wakeup(p_ptr); - goto exit; + u32 orignode = msg_orignode(msg); + u32 origport = msg_origport(msg); + u32 destport = msg_destport(msg); + int wakeable; + + /* Validate connection */ + + p_ptr = tipc_port_lock(destport); + if (!p_ptr || !p_ptr->connected || + (port_peernode(p_ptr) != orignode) || + (port_peerport(p_ptr) != origport)) { + r_buf = tipc_buf_acquire(BASIC_H_SIZE); + if (r_buf) { + msg = buf_msg(r_buf); + tipc_msg_init(msg, TIPC_HIGH_IMPORTANCE, TIPC_CONN_MSG, + BASIC_H_SIZE, orignode); + msg_set_errcode(msg, TIPC_ERR_NO_PORT); + msg_set_origport(msg, destport); + msg_set_destport(msg, origport); } - } else if (p_ptr->published) { - err = TIPC_ERR_NO_PORT; - } - if (err) { - r_buf = port_build_proto_msg(msg_origport(msg), - msg_orignode(msg), - msg_destport(msg), - tipc_own_addr, - TIPC_HIGH_IMPORTANCE, - TIPC_CONN_MSG, - err, - 0); + if (p_ptr) + tipc_port_unlock(p_ptr); goto exit; } - /* All is fine */ - if (msg_type(msg) == CONN_PROBE) { - r_buf = port_build_proto_msg(msg_origport(msg), - msg_orignode(msg), - msg_destport(msg), - tipc_own_addr, - CONN_MANAGER, - CONN_PROBE_REPLY, - TIPC_OK, - 0); + /* Process protocol message sent by peer */ + + switch (msg_type(msg)) { + case CONN_ACK: + wakeable = tipc_port_congested(p_ptr) && p_ptr->congested && + p_ptr->wakeup; + p_ptr->acked += msg_msgcnt(msg); + if (!tipc_port_congested(p_ptr)) { + p_ptr->congested = 0; + if (wakeable) + p_ptr->wakeup(p_ptr); + } + break; + case CONN_PROBE: + r_buf = port_build_proto_msg(p_ptr, CONN_PROBE_REPLY, 0); + break; + default: + /* CONN_PROBE_REPLY or unrecognized - no action required */ + break; } p_ptr->probing_state = CONFIRMED; + tipc_port_unlock(p_ptr); exit: - if (p_ptr) - tipc_port_unlock(p_ptr); tipc_net_route_msg(r_buf); - tipc_net_route_msg(abort_buf); buf_discard(buf); } @@ -889,14 +882,7 @@ void tipc_acknowledge(u32 ref, u32 ack) return; if (p_ptr->connected) { p_ptr->conn_unacked -= ack; - buf = port_build_proto_msg(port_peerport(p_ptr), - port_peernode(p_ptr), - ref, - tipc_own_addr, - CONN_MANAGER, - CONN_ACK, - TIPC_OK, - ack); + buf = port_build_proto_msg(p_ptr, CONN_ACK, ack); } tipc_port_unlock(p_ptr); tipc_net_route_msg(buf); @@ -1140,19 +1126,7 @@ int tipc_shutdown(u32 ref) if (!p_ptr) return -EINVAL; - if (p_ptr->connected) { - u32 imp = msg_importance(&p_ptr->phdr); - if (imp < TIPC_CRITICAL_IMPORTANCE) - imp++; - buf = port_build_proto_msg(port_peerport(p_ptr), - port_peernode(p_ptr), - ref, - tipc_own_addr, - imp, - TIPC_CONN_MSG, - TIPC_CONN_SHUTDOWN, - 0); - } + buf = port_build_peer_abort_msg(p_ptr, TIPC_CONN_SHUTDOWN); tipc_port_unlock(p_ptr); tipc_net_route_msg(buf); return tipc_disconnect(ref); @@ -1238,7 +1212,7 @@ int tipc_send2name(u32 ref, struct tipc_name const *name, unsigned int domain, msg_set_type(msg, TIPC_NAMED_MSG); msg_set_orignode(msg, tipc_own_addr); msg_set_origport(msg, ref); - msg_set_hdr_sz(msg, LONG_H_SIZE); + msg_set_hdr_sz(msg, NAMED_H_SIZE); msg_set_nametype(msg, name->type); msg_set_nameinst(msg, name->instance); msg_set_lookup_scope(msg, tipc_addr_scope(domain)); @@ -1291,7 +1265,7 @@ int tipc_send2port(u32 ref, struct tipc_portid const *dest, msg_set_origport(msg, ref); msg_set_destnode(msg, dest->node); msg_set_destport(msg, dest->ref); - msg_set_hdr_sz(msg, DIR_MSG_H_SIZE); + msg_set_hdr_sz(msg, BASIC_H_SIZE); if (dest->node == tipc_own_addr) res = tipc_port_recv_sections(p_ptr, num_sect, msg_sect, @@ -1331,13 +1305,13 @@ int tipc_send_buf2port(u32 ref, struct tipc_portid const *dest, msg_set_origport(msg, ref); msg_set_destnode(msg, dest->node); msg_set_destport(msg, dest->ref); - msg_set_hdr_sz(msg, DIR_MSG_H_SIZE); - msg_set_size(msg, DIR_MSG_H_SIZE + dsz); - if (skb_cow(buf, DIR_MSG_H_SIZE)) + msg_set_hdr_sz(msg, BASIC_H_SIZE); + msg_set_size(msg, BASIC_H_SIZE + dsz); + if (skb_cow(buf, BASIC_H_SIZE)) return -ENOMEM; - skb_push(buf, DIR_MSG_H_SIZE); - skb_copy_to_linear_data(buf, msg, DIR_MSG_H_SIZE); + skb_push(buf, BASIC_H_SIZE); + skb_copy_to_linear_data(buf, msg, BASIC_H_SIZE); if (dest->node == tipc_own_addr) res = tipc_port_recv_msg(buf); diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 3610786..580ecf2 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -34,11 +34,9 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <linux/export.h> #include <net/sock.h> -#include <linux/tipc.h> -#include <linux/tipc_config.h> - #include "core.h" #include "port.h" @@ -52,7 +50,7 @@ struct tipc_sock { struct sock sk; struct tipc_port *p; struct tipc_portid peer_name; - long conn_timeout; + unsigned int conn_timeout; }; #define tipc_sk(sk) ((struct tipc_sock *)(sk)) @@ -234,7 +232,7 @@ static int tipc_create(struct net *net, struct socket *sock, int protocol, sock_init_data(sock, sk); sk->sk_backlog_rcv = backlog_rcv; tipc_sk(sk)->p = tp_ptr; - tipc_sk(sk)->conn_timeout = msecs_to_jiffies(CONN_TIMEOUT_DEFAULT); + tipc_sk(sk)->conn_timeout = CONN_TIMEOUT_DEFAULT; spin_unlock_bh(tp_ptr->lock); @@ -528,6 +526,7 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, struct tipc_port *tport = tipc_sk_port(sk); struct sockaddr_tipc *dest = (struct sockaddr_tipc *)m->msg_name; int needs_conn; + long timeout_val; int res = -EINVAL; if (unlikely(!dest)) @@ -567,6 +566,8 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, reject_rx_queue(sk); } + timeout_val = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); + do { if (dest->addrtype == TIPC_ADDR_NAME) { res = dest_name_check(dest, m); @@ -603,16 +604,14 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, sock->state = SS_CONNECTING; break; } - if (m->msg_flags & MSG_DONTWAIT) { - res = -EWOULDBLOCK; + if (timeout_val <= 0L) { + res = timeout_val ? timeout_val : -EWOULDBLOCK; break; } release_sock(sk); - res = wait_event_interruptible(*sk_sleep(sk), - !tport->congested); + timeout_val = wait_event_interruptible_timeout(*sk_sleep(sk), + !tport->congested, timeout_val); lock_sock(sk); - if (res) - break; } while (1); exit: @@ -639,6 +638,7 @@ static int send_packet(struct kiocb *iocb, struct socket *sock, struct sock *sk = sock->sk; struct tipc_port *tport = tipc_sk_port(sk); struct sockaddr_tipc *dest = (struct sockaddr_tipc *)m->msg_name; + long timeout_val; int res; /* Handle implied connection establishment */ @@ -653,6 +653,8 @@ static int send_packet(struct kiocb *iocb, struct socket *sock, if (iocb) lock_sock(sk); + timeout_val = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); + do { if (unlikely(sock->state != SS_CONNECTED)) { if (sock->state == SS_DISCONNECTING) @@ -666,16 +668,14 @@ static int send_packet(struct kiocb *iocb, struct socket *sock, total_len); if (likely(res != -ELINKCONG)) break; - if (m->msg_flags & MSG_DONTWAIT) { - res = -EWOULDBLOCK; + if (timeout_val <= 0L) { + res = timeout_val ? timeout_val : -EWOULDBLOCK; break; } release_sock(sk); - res = wait_event_interruptible(*sk_sleep(sk), - (!tport->congested || !tport->connected)); + timeout_val = wait_event_interruptible_timeout(*sk_sleep(sk), + (!tport->congested || !tport->connected), timeout_val); lock_sock(sk); - if (res) - break; } while (1); if (iocb) @@ -949,9 +949,6 @@ static int recv_msg(struct kiocb *iocb, struct socket *sock, goto exit; } - /* will be updated in set_orig_addr() if needed */ - m->msg_namelen = 0; - timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); restart: @@ -1078,9 +1075,6 @@ static int recv_stream(struct kiocb *iocb, struct socket *sock, goto exit; } - /* will be updated in set_orig_addr() if needed */ - m->msg_namelen = 0; - target = sock_rcvlowat(sk, flags & MSG_WAITALL, buf_len); timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); restart: @@ -1379,7 +1373,7 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen, struct msghdr m = {NULL,}; struct sk_buff *buf; struct tipc_msg *msg; - long timeout; + unsigned int timeout; int res; lock_sock(sk); @@ -1444,7 +1438,8 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen, res = wait_event_interruptible_timeout(*sk_sleep(sk), (!skb_queue_empty(&sk->sk_receive_queue) || (sock->state != SS_CONNECTING)), - timeout ? timeout : MAX_SCHEDULE_TIMEOUT); + timeout ? (long)msecs_to_jiffies(timeout) + : MAX_SCHEDULE_TIMEOUT); lock_sock(sk); if (res > 0) { @@ -1490,9 +1485,7 @@ static int listen(struct socket *sock, int len) lock_sock(sk); - if (sock->state == SS_READY) - res = -EOPNOTSUPP; - else if (sock->state != SS_UNCONNECTED) + if (sock->state != SS_UNCONNECTED) res = -EINVAL; else { sock->state = SS_LISTENING; @@ -1520,10 +1513,6 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags) lock_sock(sk); - if (sock->state == SS_READY) { - res = -EOPNOTSUPP; - goto exit; - } if (sock->state != SS_LISTENING) { res = -EINVAL; goto exit; @@ -1552,6 +1541,8 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags) u32 new_ref = new_tport->ref; struct tipc_msg *msg = buf_msg(buf); + security_sk_clone(sock->sk, new_sock->sk); + lock_sock(new_sk); /* @@ -1706,7 +1697,7 @@ static int setsockopt(struct socket *sock, res = tipc_set_portunreturnable(tport->ref, value); break; case TIPC_CONN_TIMEOUT: - tipc_sk(sk)->conn_timeout = msecs_to_jiffies(value); + tipc_sk(sk)->conn_timeout = value; /* no need to set "res", since already 0 at this point */ break; default: @@ -1762,7 +1753,7 @@ static int getsockopt(struct socket *sock, res = tipc_portunreturnable(tport->ref, &value); break; case TIPC_CONN_TIMEOUT: - value = jiffies_to_msecs(tipc_sk(sk)->conn_timeout); + value = tipc_sk(sk)->conn_timeout; /* no need to set "res", since already 0 at this point */ break; case TIPC_NODE_RECVQ_DEPTH: @@ -1800,11 +1791,11 @@ static const struct proto_ops msg_ops = { .bind = bind, .connect = connect, .socketpair = sock_no_socketpair, - .accept = accept, + .accept = sock_no_accept, .getname = get_name, .poll = poll, .ioctl = sock_no_ioctl, - .listen = listen, + .listen = sock_no_listen, .shutdown = shutdown, .setsockopt = setsockopt, .getsockopt = getsockopt, diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c index 6cf7268..1983717 100644 --- a/net/tipc/subscr.c +++ b/net/tipc/subscr.c @@ -151,7 +151,7 @@ void tipc_subscr_report_overlap(struct subscription *sub, if (!must && !(sub->filter & TIPC_SUB_PORTS)) return; - sub->event_cb(sub, found_lower, found_upper, event, port_ref, node); + subscr_send_event(sub, found_lower, found_upper, event, port_ref, node); } /** @@ -365,7 +365,6 @@ static struct subscription *subscr_subscribe(struct tipc_subscr *s, subscr_terminate(subscriber); return NULL; } - sub->event_cb = subscr_send_event; INIT_LIST_HEAD(&sub->nameseq_list); list_add(&sub->subscription_list, &subscriber->subscription_list); sub->server_ref = subscriber->port_ref; diff --git a/net/tipc/subscr.h b/net/tipc/subscr.h index 45d89bf..4b06ef6 100644 --- a/net/tipc/subscr.h +++ b/net/tipc/subscr.h @@ -39,16 +39,11 @@ struct subscription; -typedef void (*tipc_subscr_event) (struct subscription *sub, - u32 found_lower, u32 found_upper, - u32 event, u32 port_ref, u32 node); - /** * struct subscription - TIPC network topology subscription object * @seq: name sequence associated with subscription * @timeout: duration of subscription (in ms) * @filter: event filtering to be done for subscription - * @event_cb: routine invoked when a subscription event is detected * @timer: timer governing subscription duration (optional) * @nameseq_list: adjacent subscriptions in name sequence's subscription list * @subscription_list: adjacent subscriptions in subscriber's subscription list @@ -61,7 +56,6 @@ struct subscription { struct tipc_name_seq seq; u32 timeout; u32 filter; - tipc_subscr_event event_cb; struct timer_list timer; struct list_head nameseq_list; struct list_head subscription_list; diff --git a/net/wanrouter/wanproc.c b/net/wanrouter/wanproc.c index f346395..c43612e 100644 --- a/net/wanrouter/wanproc.c +++ b/net/wanrouter/wanproc.c @@ -81,7 +81,6 @@ static struct proc_dir_entry *proc_router; * Iterator */ static void *r_start(struct seq_file *m, loff_t *pos) - __acquires(kernel_lock) { struct wan_device *wandev; loff_t l = *pos; @@ -103,7 +102,6 @@ static void *r_next(struct seq_file *m, void *v, loff_t *pos) } static void r_stop(struct seq_file *m, void *v) - __releases(kernel_lock) { mutex_unlock(&config_mutex); } diff --git a/net/wimax/op-msg.c b/net/wimax/op-msg.c index d5b7c37..0694d62 100644 --- a/net/wimax/op-msg.c +++ b/net/wimax/op-msg.c @@ -77,6 +77,7 @@ #include <linux/netdevice.h> #include <linux/wimax.h> #include <linux/security.h> +#include <linux/export.h> #include "wimax-internal.h" diff --git a/net/wimax/op-reset.c b/net/wimax/op-reset.c index 68bedf3..7ceffe3 100644 --- a/net/wimax/op-reset.c +++ b/net/wimax/op-reset.c @@ -32,6 +32,7 @@ #include <net/genetlink.h> #include <linux/wimax.h> #include <linux/security.h> +#include <linux/export.h> #include "wimax-internal.h" #define D_SUBMODULE op_reset diff --git a/net/wimax/op-rfkill.c b/net/wimax/op-rfkill.c index 2609e44..7ab60ba 100644 --- a/net/wimax/op-rfkill.c +++ b/net/wimax/op-rfkill.c @@ -65,6 +65,7 @@ #include <linux/wimax.h> #include <linux/security.h> #include <linux/rfkill.h> +#include <linux/export.h> #include "wimax-internal.h" #define D_SUBMODULE op_rfkill diff --git a/net/wimax/stack.c b/net/wimax/stack.c index ee99e7d..3c65eae 100644 --- a/net/wimax/stack.c +++ b/net/wimax/stack.c @@ -55,6 +55,7 @@ #include <net/genetlink.h> #include <linux/netdevice.h> #include <linux/wimax.h> +#include <linux/module.h> #include "wimax-internal.h" diff --git a/net/wireless_ath/Makefile b/net/wireless_ath/Makefile deleted file mode 100755 index 2c35c62..0000000 --- a/net/wireless_ath/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -NOSTDINC_FLAGS := -I$(srctree)/include/compat/ \ - -include $(srctree)/include/compat/linux/compat-2.6.h \ - $(CFLAGS) - -obj-$(CONFIG_CFG80211) += cfg80211.o - -obj-$(CONFIG_WEXT_CORE) += wext-core.o -obj-$(CONFIG_WEXT_PROC) += wext-proc.o -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 -cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o -cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o -cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o - -ccflags-y += -D__CHECK_ENDIAN__ - -$(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk - @$(AWK) -f $(src)/genregdb.awk < $< > $@ - -clean-files := regdb.c diff --git a/net/wireless_ath/Makefile.bk b/net/wireless_ath/Makefile.bk deleted file mode 100755 index 9b12455..0000000 --- a/net/wireless_ath/Makefile.bk +++ /dev/null @@ -1,18 +0,0 @@ -obj-$(CONFIG_CFG80211) += cfg80211.o -obj-$(CONFIG_LIB80211) += lib80211.o -obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o -obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o -obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.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 -cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o -cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o -cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o - -ccflags-y += -D__CHECK_ENDIAN__ - -$(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk - @$(AWK) -f $(src)/genregdb.awk < $< > $@ - -clean-files := regdb.c diff --git a/net/wireless_ath/chan.c b/net/wireless_ath/chan.c deleted file mode 100755 index 17cd0c0..0000000 --- a/net/wireless_ath/chan.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This file contains helper code to handle channel - * settings and keeping track of what is possible at - * any point in time. - * - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - */ - -#include <net/cfg80211.h> -#include "core.h" - -struct ieee80211_channel * -rdev_freq_to_chan(struct cfg80211_registered_device *rdev, - int freq, enum nl80211_channel_type channel_type) -{ - struct ieee80211_channel *chan; - struct ieee80211_sta_ht_cap *ht_cap; - - chan = ieee80211_get_channel(&rdev->wiphy, freq); - - /* Primary channel not allowed */ - if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) - return NULL; - - if (channel_type == NL80211_CHAN_HT40MINUS && - chan->flags & IEEE80211_CHAN_NO_HT40MINUS) - return NULL; - else if (channel_type == NL80211_CHAN_HT40PLUS && - chan->flags & IEEE80211_CHAN_NO_HT40PLUS) - return NULL; - - ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; - - if (channel_type != NL80211_CHAN_NO_HT) { - if (!ht_cap->ht_supported) - return NULL; - - if (channel_type != NL80211_CHAN_HT20 && - (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || - ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) - return NULL; - } - - return chan; -} - -static bool can_beacon_sec_chan(struct wiphy *wiphy, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) -{ - struct ieee80211_channel *sec_chan; - int diff; - - switch (channel_type) { - case NL80211_CHAN_HT40PLUS: - diff = 20; - break; - case NL80211_CHAN_HT40MINUS: - diff = -20; - break; - default: - return false; - } - - sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff); - if (!sec_chan) - 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)) - return false; - - return true; -} - -int cfg80211_set_freq(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, int freq, - enum nl80211_channel_type channel_type) -{ - struct ieee80211_channel *chan; - int result; - - if (wdev && wdev->iftype == NL80211_IFTYPE_MONITOR) - wdev = NULL; - - if (wdev) { - ASSERT_WDEV_LOCK(wdev); - - if (!netif_running(wdev->netdev)) - return -ENETDOWN; - } - - if (!rdev->ops->set_channel) - return -EOPNOTSUPP; - - chan = rdev_freq_to_chan(rdev, freq, channel_type); - if (!chan) - return -EINVAL; - - /* Both channels should be able to initiate communication */ - if (wdev && (wdev->iftype == NL80211_IFTYPE_ADHOC || - wdev->iftype == NL80211_IFTYPE_AP || - wdev->iftype == NL80211_IFTYPE_AP_VLAN || - wdev->iftype == NL80211_IFTYPE_MESH_POINT || - wdev->iftype == NL80211_IFTYPE_P2P_GO)) { - switch (channel_type) { - case NL80211_CHAN_HT40PLUS: - case NL80211_CHAN_HT40MINUS: - if (!can_beacon_sec_chan(&rdev->wiphy, chan, - channel_type)) { - printk(KERN_DEBUG - "cfg80211: Secondary channel not " - "allowed to initiate communication\n"); - return -EINVAL; - } - break; - default: - break; - } - } - - result = rdev->ops->set_channel(&rdev->wiphy, - wdev ? wdev->netdev : NULL, - chan, channel_type); - if (result) - return result; - - if (wdev) - wdev->channel = chan; - - return 0; -} diff --git a/net/wireless_ath/core.c b/net/wireless_ath/core.c deleted file mode 100755 index 2c715e6..0000000 --- a/net/wireless_ath/core.c +++ /dev/null @@ -1,1110 +0,0 @@ -/* - * This is the linux wireless configuration interface. - * - * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> - */ - -//#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/if.h> -#include <linux/module.h> -#include <linux/err.h> -#include <linux/list.h> -#include <linux/slab.h> -#include <linux/nl80211.h> -#include <linux/debugfs.h> -#include <linux/notifier.h> -#include <linux/device.h> -#include <linux/etherdevice.h> -#include <linux/rtnetlink.h> -#include <linux/sched.h> -#include <net/genetlink.h> -#include <net/cfg80211.h> -#include "nl80211.h" -#include "core.h" -#include "sysfs.h" -#include "debugfs.h" -#include "wext-compat.h" -#include "ethtool.h" - -/* name for sysfs, %d is appended */ -#define PHY_NAME "phy" - -MODULE_AUTHOR("Johannes Berg"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("wireless configuration support"); - -/* RCU-protected (and cfg80211_mutex for writers) */ -LIST_HEAD(cfg80211_rdev_list); -int cfg80211_rdev_list_generation; - -DEFINE_MUTEX(cfg80211_mutex); - -/* for debugfs */ -static struct dentry *ieee80211_debugfs_dir; - -/* for the cleanup, scan and event works */ -struct workqueue_struct *cfg80211_wq; - -static bool cfg80211_disable_40mhz_24ghz; -module_param(cfg80211_disable_40mhz_24ghz, bool, 0644); -MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz, - "Disable 40MHz support in the 2.4GHz band"); - -/* requires cfg80211_mutex to be held! */ -struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) -{ - struct cfg80211_registered_device *result = NULL, *rdev; - - if (!wiphy_idx_valid(wiphy_idx)) - return NULL; - - assert_cfg80211_lock(); - - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { - if (rdev->wiphy_idx == wiphy_idx) { - result = rdev; - break; - } - } - - return result; -} - -int get_wiphy_idx(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *rdev; - if (!wiphy) - return WIPHY_IDX_STALE; - rdev = wiphy_to_dev(wiphy); - return rdev->wiphy_idx; -} - -/* requires cfg80211_rdev_mutex to be held! */ -struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) -{ - struct cfg80211_registered_device *rdev; - - if (!wiphy_idx_valid(wiphy_idx)) - return NULL; - - assert_cfg80211_lock(); - - rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); - if (!rdev) - return NULL; - return &rdev->wiphy; -} - -/* requires cfg80211_mutex to be held! */ -struct cfg80211_registered_device * -__cfg80211_rdev_from_info(struct genl_info *info) -{ - int ifindex; - struct cfg80211_registered_device *bywiphyidx = NULL, *byifidx = NULL; - struct net_device *dev; - int err = -EINVAL; - - assert_cfg80211_lock(); - - if (info->attrs[NL80211_ATTR_WIPHY]) { - bywiphyidx = cfg80211_rdev_by_wiphy_idx( - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY])); - err = -ENODEV; - } - - if (info->attrs[NL80211_ATTR_IFINDEX]) { - ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); - dev = dev_get_by_index(genl_info_net(info), ifindex); - if (dev) { - if (dev->ieee80211_ptr) - byifidx = - wiphy_to_dev(dev->ieee80211_ptr->wiphy); - dev_put(dev); - } - err = -ENODEV; - } - - if (bywiphyidx && byifidx) { - if (bywiphyidx != byifidx) - return ERR_PTR(-EINVAL); - else - return bywiphyidx; /* == byifidx */ - } - if (bywiphyidx) - return bywiphyidx; - - if (byifidx) - return byifidx; - - return ERR_PTR(err); -} - -struct cfg80211_registered_device * -cfg80211_get_dev_from_info(struct genl_info *info) -{ - struct cfg80211_registered_device *rdev; - - mutex_lock(&cfg80211_mutex); - rdev = __cfg80211_rdev_from_info(info); - - /* if it is not an error we grab the lock on - * it to assure it won't be going away while - * we operate on it */ - if (!IS_ERR(rdev)) - mutex_lock(&rdev->mtx); - - mutex_unlock(&cfg80211_mutex); - - return rdev; -} - -struct cfg80211_registered_device * -cfg80211_get_dev_from_ifindex(struct net *net, int ifindex) -{ - struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV); - struct net_device *dev; - - mutex_lock(&cfg80211_mutex); - dev = dev_get_by_index(net, ifindex); - if (!dev) - goto out; - if (dev->ieee80211_ptr) { - rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); - mutex_lock(&rdev->mtx); - } else - rdev = ERR_PTR(-ENODEV); - dev_put(dev); - out: - mutex_unlock(&cfg80211_mutex); - return rdev; -} - -/* requires cfg80211_mutex to be held */ -int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, - char *newname) -{ - struct cfg80211_registered_device *rdev2; - int wiphy_idx, taken = -1, result, digits; - - assert_cfg80211_lock(); - - /* prohibit calling the thing phy%d when %d is not its number */ - sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken); - if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) { - /* count number of places needed to print wiphy_idx */ - digits = 1; - while (wiphy_idx /= 10) - digits++; - /* - * deny the name if it is phy<idx> where <idx> is printed - * without leading zeroes. taken == strlen(newname) here - */ - if (taken == strlen(PHY_NAME) + digits) - return -EINVAL; - } - - - /* Ignore nop renames */ - if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0) - return 0; - - /* Ensure another device does not already have this name. */ - list_for_each_entry(rdev2, &cfg80211_rdev_list, list) - if (strcmp(newname, dev_name(&rdev2->wiphy.dev)) == 0) - return -EINVAL; - - result = device_rename(&rdev->wiphy.dev, newname); - if (result) - return result; - - if (rdev->wiphy.debugfsdir && - !debugfs_rename(rdev->wiphy.debugfsdir->d_parent, - rdev->wiphy.debugfsdir, - rdev->wiphy.debugfsdir->d_parent, - newname)) - pr_err("failed to rename debugfs dir to %s!\n", newname); - - nl80211_notify_dev_rename(rdev); - - return 0; -} - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) -int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, - struct net *net) -{ - struct wireless_dev *wdev; - int err = 0; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK)) - return -EOPNOTSUPP; - - list_for_each_entry(wdev, &rdev->netdev_list, list) { - wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; - err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); - if (err) - break; - wdev->netdev->features |= NETIF_F_NETNS_LOCAL; - } - - if (err) { - /* failed -- clean up to old netns */ - net = wiphy_net(&rdev->wiphy); - - list_for_each_entry_continue_reverse(wdev, &rdev->netdev_list, - list) { - wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; - err = dev_change_net_namespace(wdev->netdev, net, - "wlan%d"); - WARN_ON(err); - wdev->netdev->features |= NETIF_F_NETNS_LOCAL; - } - - return err; - } - - wiphy_net_set(&rdev->wiphy, net); - - err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev)); - WARN_ON(err); - - return 0; -} -#endif - -static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) -{ - struct cfg80211_registered_device *rdev = data; - - rdev->ops->rfkill_poll(&rdev->wiphy); -} - -static int cfg80211_rfkill_set_block(void *data, bool blocked) -{ - struct cfg80211_registered_device *rdev = data; - struct wireless_dev *wdev; - - if (!blocked) - return 0; - - rtnl_lock(); - mutex_lock(&rdev->devlist_mtx); - - list_for_each_entry(wdev, &rdev->netdev_list, list) - dev_close(wdev->netdev); - - mutex_unlock(&rdev->devlist_mtx); - rtnl_unlock(); - - return 0; -} - -static void cfg80211_rfkill_sync_work(struct work_struct *work) -{ - struct cfg80211_registered_device *rdev; - - rdev = container_of(work, struct cfg80211_registered_device, rfkill_sync); - cfg80211_rfkill_set_block(rdev, rfkill_blocked(rdev->rfkill)); -} - -static void cfg80211_event_work(struct work_struct *work) -{ - struct cfg80211_registered_device *rdev; - - rdev = container_of(work, struct cfg80211_registered_device, - event_work); - - rtnl_lock(); - cfg80211_lock_rdev(rdev); - - cfg80211_process_rdev_events(rdev); - cfg80211_unlock_rdev(rdev); - rtnl_unlock(); -} - -/* exported functions */ - -struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) -{ - static int wiphy_counter; - - struct cfg80211_registered_device *rdev; - int alloc_size; - - /* - * Make sure the padding is >= the rest of the struct so that we - * always keep it large enough to pad out the entire original - * kernel's struct. We really only need to make sure it's larger - * than the kernel compat is compiled against, but since it'll - * only increase in size make sure it's larger than the current - * version of it. Subtract since it's included. - */ - BUILD_BUG_ON(WIPHY_COMPAT_PAD_SIZE < - sizeof(struct wiphy) - WIPHY_COMPAT_PAD_SIZE); - - WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key)); - WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc)); - WARN_ON(ops->connect && !ops->disconnect); - WARN_ON(ops->join_ibss && !ops->leave_ibss); - WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf); - WARN_ON(ops->add_station && !ops->del_station); - WARN_ON(ops->add_mpath && !ops->del_mpath); - WARN_ON(ops->join_mesh && !ops->leave_mesh); - - alloc_size = sizeof(*rdev) + sizeof_priv; - - rdev = kzalloc(alloc_size, GFP_KERNEL); - if (!rdev) - return NULL; - - rdev->ops = ops; - - mutex_lock(&cfg80211_mutex); - - rdev->wiphy_idx = wiphy_counter++; - - if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) { - wiphy_counter--; - mutex_unlock(&cfg80211_mutex); - /* ugh, wrapped! */ - kfree(rdev); - return NULL; - } - - mutex_unlock(&cfg80211_mutex); - - /* give it a proper name */ - dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); - - mutex_init(&rdev->mtx); - mutex_init(&rdev->devlist_mtx); - mutex_init(&rdev->sched_scan_mtx); - INIT_LIST_HEAD(&rdev->netdev_list); - spin_lock_init(&rdev->bss_lock); - INIT_LIST_HEAD(&rdev->bss_list); - INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); - INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); - device_initialize(&rdev->wiphy.dev); - rdev->wiphy.dev.class = &ieee80211_class; - rdev->wiphy.dev.platform_data = rdev; - -#ifdef CONFIG_CFG80211_DEFAULT_PS - rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; -#endif - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - wiphy_net_set(&rdev->wiphy, &init_net); -#endif - - rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; - rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), - &rdev->wiphy.dev, RFKILL_TYPE_WLAN, - &rdev->rfkill_ops, rdev); - - if (!rdev->rfkill) { - kfree(rdev); - return NULL; - } - - INIT_WORK(&rdev->rfkill_sync, cfg80211_rfkill_sync_work); - INIT_WORK(&rdev->conn_work, cfg80211_conn_work); - INIT_WORK(&rdev->event_work, cfg80211_event_work); - - init_waitqueue_head(&rdev->dev_wait); - - /* - * Initialize wiphy parameters to IEEE 802.11 MIB default values. - * Fragmentation and RTS threshold are disabled by default with the - * special -1 value. - */ - rdev->wiphy.retry_short = 7; - rdev->wiphy.retry_long = 4; - rdev->wiphy.frag_threshold = (u32) -1; - rdev->wiphy.rts_threshold = (u32) -1; - rdev->wiphy.coverage_class = 0; - - return &rdev->wiphy; -} -EXPORT_SYMBOL(wiphy_new); - -static int wiphy_verify_combinations(struct wiphy *wiphy) -{ - const struct ieee80211_iface_combination *c; - int i, j; - - /* If we have combinations enforce them */ - if (wiphy->n_iface_combinations) - wiphy->flags |= WIPHY_FLAG_ENFORCE_COMBINATIONS; - - for (i = 0; i < wiphy->n_iface_combinations; i++) { - u32 cnt = 0; - u16 all_iftypes = 0; - - c = &wiphy->iface_combinations[i]; - - /* Combinations with just one interface aren't real */ - if (WARN_ON(c->max_interfaces < 2)) - return -EINVAL; - - /* Need at least one channel */ - if (WARN_ON(!c->num_different_channels)) - return -EINVAL; - - if (WARN_ON(!c->n_limits)) - return -EINVAL; - - for (j = 0; j < c->n_limits; j++) { - u16 types = c->limits[j].types; - - /* - * interface types shouldn't overlap, this is - * used in cfg80211_can_change_interface() - */ - if (WARN_ON(types & all_iftypes)) - return -EINVAL; - all_iftypes |= types; - - if (WARN_ON(!c->limits[j].max)) - return -EINVAL; - - /* Shouldn't list software iftypes in combinations! */ - if (WARN_ON(wiphy->software_iftypes & types)) - return -EINVAL; - - cnt += c->limits[j].max; - /* - * Don't advertise an unsupported type - * in a combination. - */ - if (WARN_ON((wiphy->interface_modes & types) != types)) - return -EINVAL; - } - - /* You can't even choose that many! */ - if (WARN_ON(cnt < c->max_interfaces)) - return -EINVAL; - } - - return 0; -} - -int wiphy_register(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - int res; - enum ieee80211_band band; - struct ieee80211_supported_band *sband; - bool have_band = false; - int i; - u16 ifmodes = wiphy->interface_modes; - - if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && - !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) - return -EINVAL; - - if (WARN_ON(wiphy->ap_sme_capa && - !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) - return -EINVAL; - - if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) - return -EINVAL; - - if (WARN_ON(wiphy->addresses && - !is_zero_ether_addr(wiphy->perm_addr) && - memcmp(wiphy->perm_addr, wiphy->addresses[0].addr, - ETH_ALEN))) - return -EINVAL; - - if (wiphy->addresses) - memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); - - /* sanity check ifmodes */ - WARN_ON(!ifmodes); - ifmodes &= ((1 << NUM_NL80211_IFTYPES) - 1) & ~1; - if (WARN_ON(ifmodes != wiphy->interface_modes)) - wiphy->interface_modes = ifmodes; - - res = wiphy_verify_combinations(wiphy); - if (res) - return res; - - /* sanity check supported bands/channels */ - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - sband = wiphy->bands[band]; - if (!sband) - continue; - - sband->band = band; - - if (WARN_ON(!sband->n_channels || !sband->n_bitrates)) - return -EINVAL; - - /* - * Since cfg80211_disable_40mhz_24ghz is global, we can - * modify the sband's ht data even if the driver uses a - * global structure for that. - */ - if (cfg80211_disable_40mhz_24ghz && - band == IEEE80211_BAND_2GHZ && - sband->ht_cap.ht_supported) { - sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; - } - - /* - * Since we use a u32 for rate bitmaps in - * ieee80211_get_response_rate, we cannot - * have more than 32 legacy rates. - */ - if (WARN_ON(sband->n_bitrates > 32)) - return -EINVAL; - - 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_mpwr = - sband->channels[i].max_power; - sband->channels[i].band = band; - } - - have_band = true; - } - - if (!have_band) { - WARN_ON(1); - return -EINVAL; - } - - if (rdev->wiphy.wowlan.n_patterns) { - if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len || - rdev->wiphy.wowlan.pattern_min_len > - rdev->wiphy.wowlan.pattern_max_len)) - return -EINVAL; - } - - /* check and set up bitrates */ - ieee80211_set_bitrate_flags(wiphy); - - mutex_lock(&cfg80211_mutex); - - res = device_add(&rdev->wiphy.dev); - if (res) { - mutex_unlock(&cfg80211_mutex); - return res; - } - - /* set up regulatory info */ - regulatory_update(wiphy, NL80211_REGDOM_SET_BY_CORE); - - list_add_rcu(&rdev->list, &cfg80211_rdev_list); - cfg80211_rdev_list_generation++; - - /* add to debugfs */ - rdev->wiphy.debugfsdir = - debugfs_create_dir(wiphy_name(&rdev->wiphy), - ieee80211_debugfs_dir); - if (IS_ERR(rdev->wiphy.debugfsdir)) - rdev->wiphy.debugfsdir = NULL; - - if (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { - struct regulatory_request request; - - request.wiphy_idx = get_wiphy_idx(wiphy); - request.initiator = NL80211_REGDOM_SET_BY_DRIVER; - request.alpha2[0] = '9'; - request.alpha2[1] = '9'; - - nl80211_send_reg_change_event(&request); - } - - cfg80211_debugfs_rdev_add(rdev); - mutex_unlock(&cfg80211_mutex); - - /* - * due to a locking dependency this has to be outside of the - * cfg80211_mutex lock - */ - res = rfkill_register(rdev->rfkill); - if (res) - goto out_rm_dev; - - rtnl_lock(); - rdev->wiphy.registered = true; - rtnl_unlock(); - return 0; - -out_rm_dev: - device_del(&rdev->wiphy.dev); - return res; -} -EXPORT_SYMBOL(wiphy_register); - -void wiphy_rfkill_start_polling(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - if (!rdev->ops->rfkill_poll) - return; - rdev->rfkill_ops.poll = cfg80211_rfkill_poll; - rfkill_resume_polling(rdev->rfkill); -} -EXPORT_SYMBOL(wiphy_rfkill_start_polling); - -void wiphy_rfkill_stop_polling(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - rfkill_pause_polling(rdev->rfkill); -} -EXPORT_SYMBOL(wiphy_rfkill_stop_polling); - -void wiphy_unregister(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - rtnl_lock(); - rdev->wiphy.registered = false; - rtnl_unlock(); - - rfkill_unregister(rdev->rfkill); - - /* protect the device list */ - mutex_lock(&cfg80211_mutex); - - wait_event(rdev->dev_wait, ({ - int __count; - mutex_lock(&rdev->devlist_mtx); - __count = rdev->opencount; - mutex_unlock(&rdev->devlist_mtx); - __count == 0;})); - - mutex_lock(&rdev->devlist_mtx); - BUG_ON(!list_empty(&rdev->netdev_list)); - mutex_unlock(&rdev->devlist_mtx); - - /* - * First remove the hardware from everywhere, this makes - * it impossible to find from userspace. - */ - debugfs_remove_recursive(rdev->wiphy.debugfsdir); - list_del_rcu(&rdev->list); - synchronize_rcu(); - - /* - * Try to grab rdev->mtx. If a command is still in progress, - * hopefully the driver will refuse it since it's tearing - * down the device already. We wait for this command to complete - * before unlinking the item from the list. - * Note: as codified by the BUG_ON above we cannot get here if - * a virtual interface is still present. Hence, we can only get - * to lock contention here if userspace issues a command that - * identified the hardware by wiphy index. - */ - cfg80211_lock_rdev(rdev); - /* nothing */ - cfg80211_unlock_rdev(rdev); - - /* If this device got a regulatory hint tell core its - * free to listen now to a new shiny device regulatory hint */ - reg_device_remove(wiphy); - - cfg80211_rdev_list_generation++; - device_del(&rdev->wiphy.dev); - - mutex_unlock(&cfg80211_mutex); - - flush_work(&rdev->scan_done_wk); - cancel_work_sync(&rdev->conn_work); - flush_work(&rdev->event_work); -} -EXPORT_SYMBOL(wiphy_unregister); - -void cfg80211_dev_free(struct cfg80211_registered_device *rdev) -{ - struct cfg80211_internal_bss *scan, *tmp; - rfkill_destroy(rdev->rfkill); - mutex_destroy(&rdev->mtx); - mutex_destroy(&rdev->devlist_mtx); - mutex_destroy(&rdev->sched_scan_mtx); - list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) - cfg80211_put_bss(&scan->pub); - cfg80211_rdev_free_wowlan(rdev); - kfree(rdev); -} - -void wiphy_free(struct wiphy *wiphy) -{ - put_device(&wiphy->dev); -} -EXPORT_SYMBOL(wiphy_free); - -void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - if (rfkill_set_hw_state(rdev->rfkill, blocked)) - schedule_work(&rdev->rfkill_sync); -} -EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); - -static void wdev_cleanup_work(struct work_struct *work) -{ - struct wireless_dev *wdev; - struct cfg80211_registered_device *rdev; - - wdev = container_of(work, struct wireless_dev, cleanup_work); - rdev = wiphy_to_dev(wdev->wiphy); - - cfg80211_lock_rdev(rdev); - - if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == wdev->netdev)) { - rdev->scan_req->aborted = true; - ___cfg80211_scan_done(rdev, true); - } - - cfg80211_unlock_rdev(rdev); - - mutex_lock(&rdev->sched_scan_mtx); - - if (WARN_ON(rdev->sched_scan_req && - rdev->sched_scan_req->dev == wdev->netdev)) { - __cfg80211_stop_sched_scan(rdev, false); - } - - mutex_unlock(&rdev->sched_scan_mtx); - - mutex_lock(&rdev->devlist_mtx); - rdev->opencount--; - mutex_unlock(&rdev->devlist_mtx); - wake_up(&rdev->dev_wait); - - dev_put(wdev->netdev); -} - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) -static struct device_type wiphy_type = { - .name = "wlan", -}; -#endif - -static int cfg80211_netdev_notifier_call(struct notifier_block * nb, - unsigned long state, - void *ndev) -{ - struct net_device *dev = ndev; - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev; - int ret; - - if (!wdev) - return NOTIFY_DONE; - - rdev = wiphy_to_dev(wdev->wiphy); - - WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED); - - switch (state) { - case NETDEV_POST_INIT: - SET_NETDEV_DEVTYPE(dev, &wiphy_type); - break; - case NETDEV_REGISTER: - /* - * NB: cannot take rdev->mtx here because this may be - * called within code protected by it when interfaces - * are added with nl80211. - */ - mutex_init(&wdev->mtx); - INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); - INIT_LIST_HEAD(&wdev->event_list); - spin_lock_init(&wdev->event_lock); - INIT_LIST_HEAD(&wdev->mgmt_registrations); - spin_lock_init(&wdev->mgmt_registrations_lock); - - mutex_lock(&rdev->devlist_mtx); - list_add_rcu(&wdev->list, &rdev->netdev_list); - rdev->devlist_generation++; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - /* can only change netns with wiphy */ - dev->features |= NETIF_F_NETNS_LOCAL; -#endif - - if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, - "phy80211")) { - pr_err("failed to add phy80211 symlink to netdev!\n"); - } - wdev->netdev = dev; - wdev->sme_state = CFG80211_SME_IDLE; - mutex_unlock(&rdev->devlist_mtx); -#ifdef CONFIG_CFG80211_WEXT -#ifdef CONFIG_WIRELESS_EXT - if (!dev->wireless_handlers) - dev->wireless_handlers = &cfg80211_wext_handler; -#else -#ifdef CONFIG_MACH_PX - dev->ieee80211_ptr->wiphy->wext = &cfg80211_wext_handler; - printk_once(KERN_WARNING "cfg80211: wext will work even though " - "kernel was compiled with CONFIG_WIRELESS_EXT=n.\n"); -#else - printk_once(KERN_WARNING "cfg80211: wext will not work because " - "kernel was compiled with CONFIG_WIRELESS_EXT=n. " - "Tools using wext interface, like iwconfig will " - "not work.\n"); -#endif -#endif - wdev->wext.default_key = -1; - wdev->wext.default_mgmt_key = -1; - wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; -#endif - - if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) - wdev->ps = true; - else - wdev->ps = false; - /* allow mac80211 to determine the timeout */ - wdev->ps_timeout = -1; - - if (!dev->ethtool_ops) - dev->ethtool_ops = &cfg80211_ethtool_ops; - - if ((wdev->iftype == NL80211_IFTYPE_STATION || - wdev->iftype == NL80211_IFTYPE_P2P_CLIENT || - wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) - dev->priv_flags |= IFF_DONT_BRIDGE; - break; - case NETDEV_GOING_DOWN: - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - cfg80211_leave_ibss(rdev, dev, true); - break; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_STATION: - mutex_lock(&rdev->sched_scan_mtx); - __cfg80211_stop_sched_scan(rdev, false); - mutex_unlock(&rdev->sched_scan_mtx); - - wdev_lock(wdev); -#ifdef CONFIG_CFG80211_WEXT - kfree(wdev->wext.ie); - wdev->wext.ie = NULL; - wdev->wext.ie_len = 0; - wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; -#endif - __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, true); - cfg80211_mlme_down(rdev, dev); - wdev_unlock(wdev); - break; - case NL80211_IFTYPE_MESH_POINT: - cfg80211_leave_mesh(rdev, dev); - break; - default: - break; - } - wdev->beacon_interval = 0; - break; - case NETDEV_DOWN: - dev_hold(dev); - queue_work(cfg80211_wq, &wdev->cleanup_work); - break; - case NETDEV_UP: - /* - * If we have a really quick DOWN/UP succession we may - * have this work still pending ... cancel it and see - * if it was pending, in which case we need to account - * for some of the work it would have done. - */ - if (cancel_work_sync(&wdev->cleanup_work)) { - mutex_lock(&rdev->devlist_mtx); - rdev->opencount--; - mutex_unlock(&rdev->devlist_mtx); - dev_put(dev); - } - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - switch (wdev->iftype) { -#ifdef CONFIG_CFG80211_WEXT - case NL80211_IFTYPE_ADHOC: - cfg80211_ibss_wext_join(rdev, wdev); - break; - case NL80211_IFTYPE_STATION: - cfg80211_mgd_wext_connect(rdev, wdev); - break; -#endif -#ifdef CONFIG_MAC80211_MESH - case NL80211_IFTYPE_MESH_POINT: - { - /* backward compat code... */ - struct mesh_setup setup; - memcpy(&setup, &default_mesh_setup, - sizeof(setup)); - /* back compat only needed for mesh_id */ - setup.mesh_id = wdev->ssid; - setup.mesh_id_len = wdev->mesh_id_up_len; - if (wdev->mesh_id_up_len) - __cfg80211_join_mesh(rdev, dev, - &setup, - &default_mesh_config); - break; - } -#endif - default: - break; - } - wdev_unlock(wdev); - rdev->opencount++; - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); - - /* - * Configure power management to the driver here so that its - * correctly set also after interface type changes etc. - */ - 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)) { - /* assume this means it's off */ - wdev->ps = false; - } - break; - case NETDEV_UNREGISTER: - /* - * NB: cannot take rdev->mtx here because this may be - * called within code protected by it when interfaces - * are removed with nl80211. - */ - mutex_lock(&rdev->devlist_mtx); - /* - * It is possible to get NETDEV_UNREGISTER - * multiple times. To detect that, check - * that the interface is still on the list - * of registered interfaces, and only then - * remove and clean it up. - */ - if (!list_empty(&wdev->list)) { - sysfs_remove_link(&dev->dev.kobj, "phy80211"); - list_del_rcu(&wdev->list); - rdev->devlist_generation++; - cfg80211_mlme_purge_registrations(wdev); -#ifdef CONFIG_CFG80211_WEXT - kfree(wdev->wext.keys); -#endif - } - mutex_unlock(&rdev->devlist_mtx); - /* - * synchronise (so that we won't find this netdev - * from other code any more) and then clear the list - * head so that the above code can safely check for - * !list_empty() to avoid double-cleanup. - */ - synchronize_rcu(); - INIT_LIST_HEAD(&wdev->list); - break; - case NETDEV_PRE_UP: - if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) - return notifier_from_errno(-EOPNOTSUPP); - if (rfkill_blocked(rdev->rfkill)) - return notifier_from_errno(-ERFKILL); - ret = cfg80211_can_add_interface(rdev, wdev->iftype); - if (ret) - return notifier_from_errno(ret); - break; - } - - return NOTIFY_DONE; -} - -static struct notifier_block cfg80211_netdev_notifier = { - .notifier_call = cfg80211_netdev_notifier_call, -}; - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) -static void __net_exit cfg80211_pernet_exit(struct net *net) -{ - struct cfg80211_registered_device *rdev; - - rtnl_lock(); - mutex_lock(&cfg80211_mutex); - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { - if (net_eq(wiphy_net(&rdev->wiphy), net)) - WARN_ON(cfg80211_switch_netns(rdev, &init_net)); - } - mutex_unlock(&cfg80211_mutex); - rtnl_unlock(); -} - -static struct pernet_operations cfg80211_pernet_ops = { - .exit = cfg80211_pernet_exit, -}; -#endif - -static int __init cfg80211_init(void) -{ - int err; - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - err = register_pernet_device(&cfg80211_pernet_ops); - if (err) - goto out_fail_pernet; -#endif - - err = wiphy_sysfs_init(); - if (err) - goto out_fail_sysfs; - - err = register_netdevice_notifier(&cfg80211_netdev_notifier); - if (err) - goto out_fail_notifier; - - err = nl80211_init(); - if (err) - goto out_fail_nl80211; - - ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); - - err = regulatory_init(); - if (err) - goto out_fail_reg; - - cfg80211_wq = create_singlethread_workqueue("cfg80211"); - if (!cfg80211_wq) - goto out_fail_wq; - - return 0; - -out_fail_wq: - regulatory_exit(); -out_fail_reg: - debugfs_remove(ieee80211_debugfs_dir); -out_fail_nl80211: - unregister_netdevice_notifier(&cfg80211_netdev_notifier); -out_fail_notifier: - wiphy_sysfs_exit(); -out_fail_sysfs: -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - unregister_pernet_device(&cfg80211_pernet_ops); -out_fail_pernet: -#endif - return err; -} -subsys_initcall(cfg80211_init); - -static void __exit cfg80211_exit(void) -{ - debugfs_remove(ieee80211_debugfs_dir); - nl80211_exit(); - unregister_netdevice_notifier(&cfg80211_netdev_notifier); - wiphy_sysfs_exit(); - regulatory_exit(); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - unregister_pernet_device(&cfg80211_pernet_ops); -#endif - destroy_workqueue(cfg80211_wq); -} -module_exit(cfg80211_exit); diff --git a/net/wireless_ath/core.h b/net/wireless_ath/core.h deleted file mode 100755 index 11ff6bb..0000000 --- a/net/wireless_ath/core.h +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Wireless configuration interface internals. - * - * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> - */ -#ifndef __NET_WIRELESS_CORE_H -#define __NET_WIRELESS_CORE_H -#include <linux/mutex.h> -#include <linux/list.h> -#include <linux/netdevice.h> -#include <linux/kref.h> -#include <linux/rbtree.h> -#include <linux/debugfs.h> -#include <linux/rfkill.h> -#include <linux/workqueue.h> -#include <net/genetlink.h> -#include <net/cfg80211.h> -#include "reg.h" - -struct cfg80211_registered_device { - const struct cfg80211_ops *ops; - struct list_head list; - /* we hold this mutex during any call so that - * we cannot do multiple calls at once, and also - * to avoid the deregister call to proceed while - * any call is in progress */ - struct mutex mtx; - - /* rfkill support */ - struct rfkill_ops rfkill_ops; - struct rfkill *rfkill; - struct work_struct rfkill_sync; - - /* ISO / IEC 3166 alpha2 for which this device is receiving - * country IEs on, this can help disregard country IEs from APs - * on the same alpha2 quickly. The alpha2 may differ from - * cfg80211_regdomain's alpha2 when an intersection has occurred. - * If the AP is reconfigured this can also be used to tell us if - * the country on the country IE changed. */ - char country_ie_alpha2[2]; - - /* If a Country IE has been received this tells us the environment - * which its telling us its in. This defaults to ENVIRON_ANY */ - enum environment_cap env; - - /* wiphy index, internal only */ - int wiphy_idx; - - /* associate netdev list */ - struct mutex devlist_mtx; - /* protected by devlist_mtx or RCU */ - struct list_head netdev_list; - int devlist_generation; - int opencount; /* also protected by devlist_mtx */ - wait_queue_head_t dev_wait; - - u32 ap_beacons_nlpid; - - /* BSSes/scanning */ - spinlock_t bss_lock; - struct list_head bss_list; - struct rb_root bss_tree; - u32 bss_generation; - struct cfg80211_scan_request *scan_req; /* protected by RTNL */ - struct cfg80211_sched_scan_request *sched_scan_req; - unsigned long suspend_at; - struct work_struct scan_done_wk; - struct work_struct sched_scan_results_wk; - - struct mutex sched_scan_mtx; - -#ifdef CONFIG_NL80211_TESTMODE - struct genl_info *testmode_info; -#endif - - struct work_struct conn_work; - struct work_struct event_work; - - struct cfg80211_wowlan *wowlan; - - /* must be last because of the way we do wiphy_priv(), - * and it should at least be aligned to NETDEV_ALIGN */ - struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); -}; - -static inline -struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) -{ - BUG_ON(!wiphy); - return container_of(wiphy, struct cfg80211_registered_device, wiphy); -} - -/* Note 0 is valid, hence phy0 */ -static inline -bool wiphy_idx_valid(int wiphy_idx) -{ - return wiphy_idx >= 0; -} - -static inline void -cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) -{ - int i; - - if (!rdev->wowlan) - return; - for (i = 0; i < rdev->wowlan->n_patterns; i++) - kfree(rdev->wowlan->patterns[i].mask); - kfree(rdev->wowlan->patterns); - kfree(rdev->wowlan); -} - -extern struct workqueue_struct *cfg80211_wq; -extern struct mutex cfg80211_mutex; -extern struct list_head cfg80211_rdev_list; -extern int cfg80211_rdev_list_generation; - -static inline void assert_cfg80211_lock(void) -{ - lockdep_assert_held(&cfg80211_mutex); -} - -/* - * You can use this to mark a wiphy_idx as not having an associated wiphy. - * It guarantees cfg80211_rdev_by_wiphy_idx(wiphy_idx) will return NULL - */ -#define WIPHY_IDX_STALE -1 - -struct cfg80211_internal_bss { - struct list_head list; - struct rb_node rbn; - unsigned long ts; - struct kref ref; - atomic_t hold; - bool beacon_ies_allocated; - bool proberesp_ies_allocated; - - /* must be last because of priv member */ - struct cfg80211_bss pub; -}; - -static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pub) -{ - return container_of(pub, struct cfg80211_internal_bss, pub); -} - -static inline void cfg80211_ref_bss(struct cfg80211_internal_bss *bss) -{ - kref_get(&bss->ref); -} - -static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss) -{ - atomic_inc(&bss->hold); -} - -static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss) -{ - int r = atomic_dec_return(&bss->hold); - WARN_ON(r < 0); -} - - -struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx); -int get_wiphy_idx(struct wiphy *wiphy); - -struct cfg80211_registered_device * -__cfg80211_rdev_from_info(struct genl_info *info); - -/* - * This function returns a pointer to the driver - * that the genl_info item that is passed refers to. - * If successful, it returns non-NULL and also locks - * the driver's mutex! - * - * This means that you need to call cfg80211_unlock_rdev() - * before being allowed to acquire &cfg80211_mutex! - * - * This is necessary because we need to lock the global - * mutex to get an item off the list safely, and then - * we lock the rdev mutex so it doesn't go away under us. - * - * We don't want to keep cfg80211_mutex locked - * for all the time in order to allow requests on - * other interfaces to go through at the same time. - * - * The result of this can be a PTR_ERR and hence must - * be checked with IS_ERR() for errors. - */ -extern struct cfg80211_registered_device * -cfg80211_get_dev_from_info(struct genl_info *info); - -/* requires cfg80211_rdev_mutex to be held! */ -struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); - -/* identical to cfg80211_get_dev_from_info but only operate on ifindex */ -extern struct cfg80211_registered_device * -cfg80211_get_dev_from_ifindex(struct net *net, int ifindex); - -int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, - struct net *net); - -static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev) -{ - mutex_lock(&rdev->mtx); -} - -static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *rdev) -{ - BUG_ON(IS_ERR(rdev) || !rdev); - mutex_unlock(&rdev->mtx); -} - -static inline void wdev_lock(struct wireless_dev *wdev) - __acquires(wdev) -{ - mutex_lock(&wdev->mtx); - __acquire(wdev->mtx); -} - -static inline void wdev_unlock(struct wireless_dev *wdev) - __releases(wdev) -{ - __release(wdev->mtx); - mutex_unlock(&wdev->mtx); -} - -#define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx) -#define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx) - -enum cfg80211_event_type { - EVENT_CONNECT_RESULT, - EVENT_ROAMED, - EVENT_DISCONNECTED, - EVENT_IBSS_JOINED, -}; - -struct cfg80211_event { - struct list_head list; - enum cfg80211_event_type type; - - union { - struct { - u8 bssid[ETH_ALEN]; - const u8 *req_ie; - const u8 *resp_ie; - size_t req_ie_len; - size_t resp_ie_len; - u16 status; - } cr; - struct { - const u8 *req_ie; - const u8 *resp_ie; - size_t req_ie_len; - size_t resp_ie_len; - struct cfg80211_bss *bss; - } rm; - struct { - const u8 *ie; - size_t ie_len; - u16 reason; - } dc; - struct { - u8 bssid[ETH_ALEN]; - } ij; - }; -}; - -struct cfg80211_cached_keys { - struct key_params params[6]; - u8 data[6][WLAN_MAX_KEY_LEN]; - int def, defmgmt; -}; - - -/* free object */ -extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); - -extern int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, - char *newname); - -void ieee80211_set_bitrate_flags(struct wiphy *wiphy); - -void cfg80211_bss_expire(struct cfg80211_registered_device *dev); -void cfg80211_bss_age(struct cfg80211_registered_device *dev, - unsigned long age_secs); - -/* IBSS */ -int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params, - struct cfg80211_cached_keys *connkeys); -int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params, - struct cfg80211_cached_keys *connkeys); -void cfg80211_clear_ibss(struct net_device *dev, bool nowext); -int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext); -int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext); -void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); -int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev); - -/* mesh */ -extern const struct mesh_config default_mesh_config; -extern const struct mesh_setup default_mesh_setup; -int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev, - const struct mesh_setup *setup, - const struct mesh_config *conf); -int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev, - const struct mesh_setup *setup, - const struct mesh_config *conf); -int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev); - -/* MLME */ -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, - bool local_state_change); -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, - bool local_state_change); -int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, - const u8 *bssid, const u8 *prev_bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, bool use_mfp, - struct cfg80211_crypto_settings *crypt); -int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, struct ieee80211_channel *chan, - const u8 *bssid, const u8 *prev_bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, bool use_mfp, - struct cfg80211_crypto_settings *crypt); -int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change); -int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change); -int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change); -void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, - struct net_device *dev); -void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, bool wextev, - struct cfg80211_bss *bss); -int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, - u16 frame_type, const u8 *match_data, - int match_len); -void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid); -void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); -int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, - struct net_device *dev, - 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); - -/* SME */ -int __cfg80211_connect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys, - const u8 *prev_bssid); -int cfg80211_connect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys); -int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason, - bool wextev); -int cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason, - bool wextev); -void __cfg80211_roamed(struct wireless_dev *wdev, - struct cfg80211_bss *bss, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len); -int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev); - -void cfg80211_conn_work(struct work_struct *work); -void cfg80211_sme_failed_assoc(struct wireless_dev *wdev); -bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev); - -/* internal helpers */ -bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher); -int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, - struct key_params *params, int key_idx, - bool pairwise, const u8 *mac_addr); -void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, - size_t ie_len, u16 reason, bool from_ap); -void cfg80211_sme_scan_done(struct net_device *dev); -void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); -void cfg80211_sme_disassoc(struct net_device *dev, int idx); -void __cfg80211_scan_done(struct work_struct *wk); -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); -void __cfg80211_sched_scan_results(struct work_struct *wk); -int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, - bool driver_initiated); -void cfg80211_upload_connect_keys(struct wireless_dev *wdev); -int cfg80211_change_iface(struct cfg80211_registered_device *rdev, - struct net_device *dev, enum nl80211_iftype ntype, - u32 *flags, struct vif_params *params); -void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); - -int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, - enum nl80211_iftype iftype); - -static inline int -cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, - enum nl80211_iftype iftype) -{ - return cfg80211_can_change_interface(rdev, NULL, iftype); -} - -struct ieee80211_channel * -rdev_freq_to_chan(struct cfg80211_registered_device *rdev, - int freq, enum nl80211_channel_type channel_type); -int cfg80211_set_freq(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, int freq, - enum nl80211_channel_type channel_type); - -u16 cfg80211_calculate_bitrate(struct rate_info *rate); - -int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, - const u8 *rates, unsigned int n_rates, - u32 *mask); - -int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, - u32 beacon_int); - -#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS -#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) -#else -/* - * Trick to enable using it as a condition, - * and also not give a warning when it's - * not used that way. - */ -#define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; }) -#endif - -#endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless_ath/db.txt b/net/wireless_ath/db.txt deleted file mode 100755 index 23b85a3..0000000 --- a/net/wireless_ath/db.txt +++ /dev/null @@ -1,697 +0,0 @@ -# This is the world regulatory domain -country 00: - (2402 - 2472 @ 40), (3, 20) - # Channel 12 - 13. No HT40 channel fits here - (2457 - 2482 @ 20), (3, 20), PASSIVE-SCAN, NO-IBSS - # Channel 14. Only JP enables this and for 802.11b only - (2474 - 2494 @ 20), (3, 20), PASSIVE-SCAN, NO-IBSS, NO-OFDM - # Channel 36 - 48 - (5170 - 5250 @ 40), (3, 20), PASSIVE-SCAN, NO-IBSS - # NB: 5260 MHz - 5700 MHz requies DFS - # Channel 149 - 165 - (5735 - 5835 @ 40), (3, 20), PASSIVE-SCAN, NO-IBSS - - -country AE: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country AL: - (2402 - 2482 @ 20), (N/A, 20) - -country AM: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (N/A, 18) - (5250 - 5330 @ 20), (N/A, 18), DFS - -country AN: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country AR: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country AT: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country AU: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 23) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country AW: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country AZ: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 18) - (5250 - 5330 @ 40), (N/A, 18), DFS - -country BA: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country BB: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 23) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country BD: - (2402 - 2482 @ 40), (N/A, 20) - -country BE: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country BG: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 23) - (5250 - 5290 @ 40), (N/A, 23), DFS - (5490 - 5710 @ 40), (N/A, 30), DFS - -country BH: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (N/A, 20) - (5250 - 5330 @ 20), (N/A, 20), DFS - (5735 - 5835 @ 20), (N/A, 20) - -country BL: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 18) - (5250 - 5330 @ 40), (N/A, 18), DFS - -country BN: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5735 - 5835 @ 40), (N/A, 30) - -country BO: - (2402 - 2482 @ 40), (N/A, 30) - (5735 - 5835 @ 40), (N/A, 30) - -country BR: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country BY: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country BZ: - (2402 - 2482 @ 40), (N/A, 30) - (5735 - 5835 @ 40), (N/A, 30) - -country CA: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country CH: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country CL: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5735 - 5835 @ 40), (N/A, 20) - -country CN: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country CO: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country CR: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (3, 17) - (5250 - 5330 @ 20), (3, 23), DFS - (5735 - 5835 @ 20), (3, 30) - -country CS: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country CY: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -# Data from http://www.ctu.eu/164/download/VOR/VOR-12-08-2005-34.pdf -# and http://www.ctu.eu/164/download/VOR/VOR-12-05-2007-6-AN.pdf -# Power at 5250 - 5350 MHz and 5470 - 5725 MHz can be doubled if TPC is -# implemented. -country CZ: DFS-ETSI - (2400 - 2483.5 @ 40), (N/A, 100 mW) - (5150 - 5250 @ 40), (N/A, 200 mW), NO-OUTDOOR - (5250 - 5350 @ 40), (N/A, 100 mW), NO-OUTDOOR, DFS - (5470 - 5725 @ 40), (N/A, 500 mW), DFS - -# Data from "Frequenznutzungsplan" (as published in April 2008), downloaded from -# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38448/publicationFile/2659/Frequenznutzungsplan2008_Id17448pdf.pdf -# For the 5GHz range also see -# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38216/publicationFile/6579/WLAN5GHzVfg7_2010_28042010pdf.pdf -# The values have been reduced by a factor of 2 (3db) for non TPC devices -# (in other words: devices with TPC can use twice the tx power of this table). -# Note that the docs do not require TPC for 5150--5250; the reduction to -# 100mW thus is not strictly required -- however the conservative 100mW -# limit is used here as the non-interference with radar and satellite -# apps relies on the attenuation by the building walls only in the -# absence of DFS; the neighbour countries have 100mW limit here as well. - -country DE: DFS-ETSI - # entries 279004 and 280006 - (2400 - 2483.5 @ 40), (N/A, 100 mW) - # entry 303005 - (5150 - 5250 @ 40), (N/A, 100 mW), NO-OUTDOOR - # entries 304002 and 305002 - (5250 - 5350 @ 40), (N/A, 100 mW), NO-OUTDOOR, DFS - # entries 308002, 309001 and 310003 - (5470 - 5725 @ 40), (N/A, 500 mW), DFS - -country DK: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country DO: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country DZ: - (2402 - 2482 @ 40), (N/A, 20) - -country EC: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (3, 17) - (5250 - 5330 @ 20), (3, 23), DFS - (5735 - 5835 @ 20), (3, 30) - -country EE: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country EG: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (N/A, 20) - (5250 - 5330 @ 20), (N/A, 20), DFS - -country ES: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country FI: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country FR: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country GE: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 18) - (5250 - 5330 @ 40), (N/A, 18), DFS - -country GB: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country GD: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country GR: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country GL: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (N/A, 20) - (5250 - 5330 @ 20), (N/A, 20), DFS - (5490 - 5710 @ 20), (N/A, 27), DFS - -country GT: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country GU: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 20), (3, 17) - (5250 - 5330 @ 20), (3, 23), DFS - (5735 - 5835 @ 20), (3, 30) - -country HN: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country HK: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country HR: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country HT: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country HU: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country ID: - (2402 - 2482 @ 40), (N/A, 20) - -country IE: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country IL: - (2402 - 2482 @ 40), (N/A, 20) - (5150 - 5250 @ 40), (N/A, 200 mW), NO-OUTDOOR - (5250 - 5350 @ 40), (N/A, 200 mW), NO-OUTDOOR, DFS - -country IN: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5735 - 5835 @ 40), (N/A, 20) - -country IS: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country IR: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country IT: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country JM: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country JP: - (2402 - 2472 @ 40), (N/A, 20) - (2457 - 2482 @ 20), (N/A, 20) - (2474 - 2494 @ 20), (N/A, 20), NO-OFDM - (4910 - 4930 @ 10), (N/A, 23) - (4910 - 4990 @ 40), (N/A, 23) - (4930 - 4950 @ 10), (N/A, 23) - (5030 - 5045 @ 10), (N/A, 23) - (5030 - 5090 @ 40), (N/A, 23) - (5050 - 5060 @ 10), (N/A, 23) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 23), DFS - -country JO: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 18) - -country KE: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country KH: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country KP: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5330 @ 40), (3, 20) - (5160 - 5250 @ 40), (3, 20), DFS - (5490 - 5630 @ 40), (3, 30), DFS - (5735 - 5815 @ 40), (3, 30) - -country KR: - (2402 - 2482 @ 20), (N/A, 20) - (5170 - 5250 @ 20), (3, 20) - (5250 - 5330 @ 20), (3, 20), DFS - (5490 - 5630 @ 20), (3, 30), DFS - (5735 - 5815 @ 20), (3, 30) - -country KW: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - -country KZ: - (2402 - 2482 @ 40), (N/A, 20) - -country LB: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country LI: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country LK: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (3, 17) - (5250 - 5330 @ 20), (3, 20), DFS - (5490 - 5710 @ 20), (3, 20), DFS - (5735 - 5835 @ 20), (3, 30) - -country LT: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country LU: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country LV: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country MC: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 18) - (5250 - 5330 @ 40), (N/A, 18), DFS - -country MA: - (2402 - 2482 @ 40), (N/A, 20) - -country MO: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 23) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country MK: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country MT: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country MY: - (2402 - 2482 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 30), DFS - (5735 - 5835 @ 40), (N/A, 30) - -country MX: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country NL: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20), NO-OUTDOOR - (5250 - 5330 @ 40), (N/A, 20), NO-OUTDOOR, DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country NO: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country NP: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country NZ: - (2402 - 2482 @ 40), (N/A, 30) - (5170 - 5250 @ 20), (3, 23) - (5250 - 5330 @ 20), (3, 23), DFS - (5735 - 5835 @ 20), (3, 30) - -country OM: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country PA: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country PE: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country PG: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country PH: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country PK: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country PL: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country PT: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country PR: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country QA: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country RO: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country RU: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 20), (N/A, 30) - -country SA: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (3, 23) - (5250 - 5330 @ 20), (3, 23), DFS - (5735 - 5835 @ 20), (3, 30) - -country SE: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country SG: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5735 - 5835 @ 40), (N/A, 20) - -country SI: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country SK: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country SV: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (3, 17) - (5250 - 5330 @ 20), (3, 23), DFS - (5735 - 5835 @ 20), (3, 30) - -country SY: - (2402 - 2482 @ 40), (N/A, 20) - -country TW: - (2402 - 2472 @ 40), (3, 27) - (5270 - 5330 @ 40), (3, 17), DFS - (5735 - 5815 @ 40), (3, 30) - -country TH: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country TT: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country TN: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (N/A, 20) - (5250 - 5330 @ 20), (N/A, 20), DFS - -country TR: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (N/A, 20) - (5250 - 5330 @ 20), (N/A, 20), DFS - -country UA: - (2402 - 2482 @ 40), (N/A, 20) - -country US: DFS-FCC - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5600 @ 40), (3, 20), DFS - (5650 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country UY: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country UZ: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country VE: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5815 @ 40), (N/A, 23) - -country VN: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - -country YE: - (2402 - 2482 @ 40), (N/A, 20) - -country ZA: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country ZW: - (2402 - 2482 @ 40), (N/A, 20) - diff --git a/net/wireless_ath/debugfs.c b/net/wireless_ath/debugfs.c deleted file mode 100755 index 39765bc..0000000 --- a/net/wireless_ath/debugfs.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * cfg80211 debugfs - * - * Copyright 2009 Luis R. Rodriguez <lrodriguez@atheros.com> - * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> - * - * 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/slab.h> -#include "core.h" -#include "debugfs.h" - -static int cfg80211_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -#define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \ -static ssize_t name## _read(struct file *file, char __user *userbuf, \ - size_t count, loff_t *ppos) \ -{ \ - struct wiphy *wiphy= file->private_data; \ - char buf[buflen]; \ - int res; \ - \ - res = scnprintf(buf, buflen, fmt "\n", ##value); \ - return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ -} \ - \ -static const struct file_operations name## _ops = { \ - .read = name## _read, \ - .open = cfg80211_open_file_generic, \ - .llseek = generic_file_llseek, \ -}; - -DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d", - wiphy->rts_threshold) -DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d", - wiphy->frag_threshold); -DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d", - wiphy->retry_short) -DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d", - wiphy->retry_long); - -static int ht_print_chan(struct ieee80211_channel *chan, - char *buf, int buf_size, int offset) -{ - if (WARN_ON(offset > buf_size)) - return 0; - - if (chan->flags & IEEE80211_CHAN_DISABLED) - return snprintf(buf + offset, - buf_size - offset, - "%d Disabled\n", - chan->center_freq); - - return snprintf(buf + offset, - buf_size - offset, - "%d HT40 %c%c\n", - chan->center_freq, - (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-', - (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? ' ' : '+'); -} - -static ssize_t ht40allow_map_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct wiphy *wiphy = file->private_data; - char *buf; - unsigned int offset = 0, buf_size = PAGE_SIZE, i, r; - enum ieee80211_band band; - struct ieee80211_supported_band *sband; - - buf = kzalloc(buf_size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - mutex_lock(&cfg80211_mutex); - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - sband = wiphy->bands[band]; - if (!sband) - continue; - for (i = 0; i < sband->n_channels; i++) - offset += ht_print_chan(&sband->channels[i], - buf, buf_size, offset); - } - - mutex_unlock(&cfg80211_mutex); - - r = simple_read_from_buffer(user_buf, count, ppos, buf, offset); - - kfree(buf); - - return r; -} - -static const struct file_operations ht40allow_map_ops = { - .read = ht40allow_map_read, - .open = cfg80211_open_file_generic, - .llseek = default_llseek, -}; - -#define DEBUGFS_ADD(name) \ - debugfs_create_file(#name, S_IRUGO, phyd, &rdev->wiphy, &name## _ops); - -void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) -{ - struct dentry *phyd = rdev->wiphy.debugfsdir; - - DEBUGFS_ADD(rts_threshold); - DEBUGFS_ADD(fragmentation_threshold); - DEBUGFS_ADD(short_retry_limit); - DEBUGFS_ADD(long_retry_limit); - DEBUGFS_ADD(ht40allow_map); -} diff --git a/net/wireless_ath/debugfs.h b/net/wireless_ath/debugfs.h deleted file mode 100755 index 74fdd38..0000000 --- a/net/wireless_ath/debugfs.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __CFG80211_DEBUGFS_H -#define __CFG80211_DEBUGFS_H - -#ifdef CONFIG_CFG80211_DEBUGFS -void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev); -#else -static inline -void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) {} -#endif - -#endif /* __CFG80211_DEBUGFS_H */ diff --git a/net/wireless_ath/ethtool.c b/net/wireless_ath/ethtool.c deleted file mode 100755 index 9bde4d1..0000000 --- a/net/wireless_ath/ethtool.c +++ /dev/null @@ -1,78 +0,0 @@ -#include <linux/utsname.h> -#include <net/cfg80211.h> -#include "core.h" -#include "ethtool.h" - -static void cfg80211_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - strlcpy(info->driver, wiphy_dev(wdev->wiphy)->driver->name, - sizeof(info->driver)); - - strlcpy(info->version, init_utsname()->release, sizeof(info->version)); - - if (wdev->wiphy->fw_version[0]) - strncpy(info->fw_version, wdev->wiphy->fw_version, - sizeof(info->fw_version)); - else - strncpy(info->fw_version, "N/A", sizeof(info->fw_version)); - - strlcpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)), - sizeof(info->bus_info)); -} - -static int cfg80211_get_regs_len(struct net_device *dev) -{ - /* For now, return 0... */ - return 0; -} - -static void cfg80211_get_regs(struct net_device *dev, struct ethtool_regs *regs, - void *data) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - regs->version = wdev->wiphy->hw_version; - regs->len = 0; -} - -static void cfg80211_get_ringparam(struct net_device *dev, - struct ethtool_ringparam *rp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - - 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); -} - -static int cfg80211_set_ringparam(struct net_device *dev, - struct ethtool_ringparam *rp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - - if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0) - return -EINVAL; - - if (rdev->ops->set_ringparam) - return rdev->ops->set_ringparam(wdev->wiphy, - rp->tx_pending, rp->rx_pending); - - return -ENOTSUPP; -} - -const struct ethtool_ops cfg80211_ethtool_ops = { - .get_drvinfo = cfg80211_get_drvinfo, - .get_regs_len = cfg80211_get_regs_len, - .get_regs = cfg80211_get_regs, - .get_link = ethtool_op_get_link, - .get_ringparam = cfg80211_get_ringparam, - .set_ringparam = cfg80211_set_ringparam, -}; diff --git a/net/wireless_ath/ethtool.h b/net/wireless_ath/ethtool.h deleted file mode 100755 index 695ecad..0000000 --- a/net/wireless_ath/ethtool.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __CFG80211_ETHTOOL__ -#define __CFG80211_ETHTOOL__ - -extern const struct ethtool_ops cfg80211_ethtool_ops; - -#endif /* __CFG80211_ETHTOOL__ */ diff --git a/net/wireless_ath/genregdb.awk b/net/wireless_ath/genregdb.awk deleted file mode 100755 index 53c143f..0000000 --- a/net/wireless_ath/genregdb.awk +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/awk -f -# -# genregdb.awk -- generate regdb.c from db.txt -# -# Actually, it reads from stdin (presumed to be db.txt) and writes -# to stdout (presumed to be regdb.c), but close enough... -# -# Copyright 2009 John W. Linville <linville@tuxdriver.com> -# -# 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. -# - -BEGIN { - active = 0 - rules = 0; - print "/*" - print " * DO NOT EDIT -- file generated from data in db.txt" - print " */" - print "" - print "#include <linux/nl80211.h>" - print "#include <net/cfg80211.h>" - print "#include \"regdb.h\"" - print "" - regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n" -} - -/^[ \t]*#/ { - # Ignore -} - -!active && /^[ \t]*$/ { - # Ignore -} - -!active && /country/ { - country=$2 - sub(/:/, "", country) - printf "static const struct ieee80211_regdomain regdom_%s = {\n", country - printf "\t.alpha2 = \"%s\",\n", country - printf "\t.reg_rules = {\n" - active = 1 - regdb = regdb "\t®dom_" country ",\n" -} - -active && /^[ \t]*\(/ { - start = $1 - sub(/\(/, "", start) - end = $3 - bw = $5 - sub(/\),/, "", bw) - gain = $6 - sub(/\(/, "", gain) - sub(/,/, "", gain) - power = $7 - sub(/\)/, "", power) - sub(/,/, "", power) - # power might be in mW... - units = $8 - sub(/\)/, "", units) - sub(/,/, "", units) - if (units == "mW") { - if (power == 100) { - power = 20 - } else if (power == 200) { - power = 23 - } else if (power == 500) { - power = 27 - } else if (power == 1000) { - power = 30 - } else { - print "Unknown power value in database!" - } - } - flagstr = "" - for (i=8; i<=NF; i++) - flagstr = flagstr $i - split(flagstr, flagarray, ",") - flags = "" - for (arg in flagarray) { - if (flagarray[arg] == "NO-OFDM") { - flags = flags "\n\t\t\tNL80211_RRF_NO_OFDM | " - } else if (flagarray[arg] == "NO-CCK") { - flags = flags "\n\t\t\tNL80211_RRF_NO_CCK | " - } else if (flagarray[arg] == "NO-INDOOR") { - flags = flags "\n\t\t\tNL80211_RRF_NO_INDOOR | " - } else if (flagarray[arg] == "NO-OUTDOOR") { - flags = flags "\n\t\t\tNL80211_RRF_NO_OUTDOOR | " - } else if (flagarray[arg] == "DFS") { - flags = flags "\n\t\t\tNL80211_RRF_DFS | " - } else if (flagarray[arg] == "PTP-ONLY") { - flags = flags "\n\t\t\tNL80211_RRF_PTP_ONLY | " - } else if (flagarray[arg] == "PTMP-ONLY") { - flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | " - } else if (flagarray[arg] == "PASSIVE-SCAN") { - flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | " - } else if (flagarray[arg] == "NO-IBSS") { - flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | " - } - } - flags = flags "0" - printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags - rules++ -} - -active && /^[ \t]*$/ { - active = 0 - printf "\t},\n" - printf "\t.n_reg_rules = %d\n", rules - printf "};\n\n" - rules = 0; -} - -END { - print regdb "};" - print "" - print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);" -} diff --git a/net/wireless_ath/ibss.c b/net/wireless_ath/ibss.c deleted file mode 100755 index 30f20fe..0000000 --- a/net/wireless_ath/ibss.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Some IBSS support code for cfg80211. - * - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - */ - -#include <linux/etherdevice.h> -#include <linux/if_arp.h> -#include <linux/slab.h> -#include <linux/export.h> -#include <net/cfg80211.h> -#include "wext-compat.h" -#include "nl80211.h" - - -void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_bss *bss; -#ifdef CONFIG_CFG80211_WEXT - union iwreq_data wrqu; -#endif - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return; - - if (!wdev->ssid_len) - return; - - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, - wdev->ssid, wdev->ssid_len, - WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); - - if (WARN_ON(!bss)) - return; - - if (wdev->current_bss) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - } - - cfg80211_hold_bss(bss_from_pub(bss)); - wdev->current_bss = bss_from_pub(bss); - - cfg80211_upload_connect_keys(wdev); - - nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, - GFP_KERNEL); -#ifdef CONFIG_CFG80211_WEXT - memset(&wrqu, 0, sizeof(wrqu)); - memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); -#endif -} - -void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_event *ev; - unsigned long flags; - - CFG80211_DEV_WARN_ON(!wdev->ssid_len); - - ev = kzalloc(sizeof(*ev), gfp); - if (!ev) - return; - - ev->type = EVENT_IBSS_JOINED; - memcpy(ev->cr.bssid, bssid, ETH_ALEN); - - spin_lock_irqsave(&wdev->event_lock, flags); - list_add_tail(&ev->list, &wdev->event_list); - spin_unlock_irqrestore(&wdev->event_lock, flags); - queue_work(cfg80211_wq, &rdev->event_work); -} -EXPORT_SYMBOL(cfg80211_ibss_joined); - -int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params, - struct cfg80211_cached_keys *connkeys) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->ssid_len) - return -EALREADY; - - if (!params->basic_rates) { - /* - * If no rates were explicitly configured, - * use the mandatory rate set for 11b or - * 11a for maximum compatibility. - */ - struct ieee80211_supported_band *sband = - rdev->wiphy.bands[params->channel->band]; - int j; - u32 flag = params->channel->band == IEEE80211_BAND_5GHZ ? - IEEE80211_RATE_MANDATORY_A : - IEEE80211_RATE_MANDATORY_B; - - for (j = 0; j < sband->n_bitrates; j++) { - if (sband->bitrates[j].flags & flag) - params->basic_rates |= BIT(j); - } - } - - if (WARN_ON(wdev->connect_keys)) - kfree(wdev->connect_keys); - wdev->connect_keys = connkeys; - -#ifdef CONFIG_CFG80211_WEXT - wdev->wext.ibss.channel = params->channel; -#endif - err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); - if (err) { - wdev->connect_keys = NULL; - return err; - } - - memcpy(wdev->ssid, params->ssid, params->ssid_len); - wdev->ssid_len = params->ssid_len; - - return 0; -} - -int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params, - struct cfg80211_cached_keys *connkeys) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - err = __cfg80211_join_ibss(rdev, dev, params, connkeys); - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - - return err; -} - -static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int i; - - ASSERT_WDEV_LOCK(wdev); - - kfree(wdev->connect_keys); - wdev->connect_keys = NULL; - - /* - * Delete all the keys ... pairwise keys can't really - * exist any more anyway, but default keys might. - */ - if (rdev->ops->del_key) - for (i = 0; i < 6; i++) - rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL); - - if (wdev->current_bss) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - } - - wdev->current_bss = NULL; - wdev->ssid_len = 0; -#ifdef CONFIG_CFG80211_WEXT - if (!nowext) - wdev->wext.ibss.ssid_len = 0; -#endif -} - -void cfg80211_clear_ibss(struct net_device *dev, bool nowext) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - __cfg80211_clear_ibss(dev, nowext); - wdev_unlock(wdev); -} - -int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - ASSERT_WDEV_LOCK(wdev); - - if (!wdev->ssid_len) - return -ENOLINK; - - err = rdev->ops->leave_ibss(&rdev->wiphy, dev); - - if (err) - return err; - - __cfg80211_clear_ibss(dev, nowext); - - return 0; -} - -int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_leave_ibss(rdev, dev, nowext); - wdev_unlock(wdev); - - return err; -} - -#ifdef CONFIG_CFG80211_WEXT -int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) -{ - struct cfg80211_cached_keys *ck = NULL; - enum ieee80211_band band; - int i, err; - - ASSERT_WDEV_LOCK(wdev); - - if (!wdev->wext.ibss.beacon_interval) - wdev->wext.ibss.beacon_interval = 100; - - /* try to find an IBSS channel if none requested ... */ - if (!wdev->wext.ibss.channel) { - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - struct ieee80211_supported_band *sband; - struct ieee80211_channel *chan; - - sband = rdev->wiphy.bands[band]; - if (!sband) - continue; - - for (i = 0; i < sband->n_channels; i++) { - chan = &sband->channels[i]; - if (chan->flags & IEEE80211_CHAN_NO_IBSS) - continue; - if (chan->flags & IEEE80211_CHAN_DISABLED) - continue; - wdev->wext.ibss.channel = chan; - break; - } - - if (wdev->wext.ibss.channel) - break; - } - - if (!wdev->wext.ibss.channel) - return -EINVAL; - } - - /* don't join -- SSID is not there */ - if (!wdev->wext.ibss.ssid_len) - return 0; - - if (!netif_running(wdev->netdev)) - return 0; - - if (wdev->wext.keys) { - wdev->wext.keys->def = wdev->wext.default_key; - wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key; - } - - wdev->wext.ibss.privacy = wdev->wext.default_key != -1; - - if (wdev->wext.keys) { - ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); - if (!ck) - return -ENOMEM; - for (i = 0; i < 6; i++) - ck->params[i].key = ck->data[i]; - } - err = __cfg80211_join_ibss(rdev, wdev->netdev, - &wdev->wext.ibss, ck); - if (err) - kfree(ck); - - return err; -} - -int cfg80211_ibss_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *wextfreq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan = NULL; - int err, freq; - - /* call only for ibss! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return -EINVAL; - - if (!rdev->ops->join_ibss) - return -EOPNOTSUPP; - - freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); - if (freq < 0) - return freq; - - if (freq) { - chan = ieee80211_get_channel(wdev->wiphy, freq); - if (!chan) - return -EINVAL; - if (chan->flags & IEEE80211_CHAN_NO_IBSS || - chan->flags & IEEE80211_CHAN_DISABLED) - return -EINVAL; - } - - if (wdev->wext.ibss.channel == chan) - return 0; - - wdev_lock(wdev); - err = 0; - if (wdev->ssid_len) - err = __cfg80211_leave_ibss(rdev, dev, true); - wdev_unlock(wdev); - - if (err) - return err; - - if (chan) { - wdev->wext.ibss.channel = chan; - wdev->wext.ibss.channel_fixed = true; - } else { - /* cfg80211_ibss_wext_join will pick one if needed */ - wdev->wext.ibss.channel_fixed = false; - } - - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - err = cfg80211_ibss_wext_join(rdev, wdev); - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - - return err; -} - -int cfg80211_ibss_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct ieee80211_channel *chan = NULL; - - /* call only for ibss! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return -EINVAL; - - wdev_lock(wdev); - if (wdev->current_bss) - chan = wdev->current_bss->pub.channel; - else if (wdev->wext.ibss.channel) - chan = wdev->wext.ibss.channel; - wdev_unlock(wdev); - - if (chan) { - freq->m = chan->center_freq; - freq->e = 6; - return 0; - } - - /* no channel if not joining */ - return -EINVAL; -} - -int cfg80211_ibss_wext_siwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - size_t len = data->length; - int err; - - /* call only for ibss! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return -EINVAL; - - if (!rdev->ops->join_ibss) - return -EOPNOTSUPP; - - wdev_lock(wdev); - err = 0; - if (wdev->ssid_len) - err = __cfg80211_leave_ibss(rdev, dev, true); - wdev_unlock(wdev); - - if (err) - return err; - - /* iwconfig uses nul termination in SSID.. */ - if (len > 0 && ssid[len - 1] == '\0') - len--; - - wdev->wext.ibss.ssid = wdev->ssid; - memcpy(wdev->wext.ibss.ssid, ssid, len); - wdev->wext.ibss.ssid_len = len; - - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - err = cfg80211_ibss_wext_join(rdev, wdev); - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - - return err; -} - -int cfg80211_ibss_wext_giwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - /* call only for ibss! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return -EINVAL; - - data->flags = 0; - - wdev_lock(wdev); - if (wdev->ssid_len) { - data->flags = 1; - data->length = wdev->ssid_len; - memcpy(ssid, wdev->ssid, data->length); - } else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) { - data->flags = 1; - data->length = wdev->wext.ibss.ssid_len; - memcpy(ssid, wdev->wext.ibss.ssid, data->length); - } - wdev_unlock(wdev); - - return 0; -} - -int cfg80211_ibss_wext_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u8 *bssid = ap_addr->sa_data; - int err; - - /* call only for ibss! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return -EINVAL; - - if (!rdev->ops->join_ibss) - return -EOPNOTSUPP; - - if (ap_addr->sa_family != ARPHRD_ETHER) - return -EINVAL; - - /* automatic mode */ - if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) - bssid = NULL; - - /* both automatic */ - if (!bssid && !wdev->wext.ibss.bssid) - return 0; - - /* fixed already - and no change */ - if (wdev->wext.ibss.bssid && bssid && - compare_ether_addr(bssid, wdev->wext.ibss.bssid) == 0) - return 0; - - wdev_lock(wdev); - err = 0; - if (wdev->ssid_len) - err = __cfg80211_leave_ibss(rdev, dev, true); - wdev_unlock(wdev); - - if (err) - return err; - - if (bssid) { - memcpy(wdev->wext.bssid, bssid, ETH_ALEN); - wdev->wext.ibss.bssid = wdev->wext.bssid; - } else - wdev->wext.ibss.bssid = NULL; - - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - err = cfg80211_ibss_wext_join(rdev, wdev); - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - - return err; -} - -int cfg80211_ibss_wext_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - /* call only for ibss! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return -EINVAL; - - ap_addr->sa_family = ARPHRD_ETHER; - - wdev_lock(wdev); - if (wdev->current_bss) - memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); - else if (wdev->wext.ibss.bssid) - memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); - else - memset(ap_addr->sa_data, 0, ETH_ALEN); - - wdev_unlock(wdev); - - return 0; -} -#endif diff --git a/net/wireless_ath/lib80211.c b/net/wireless_ath/lib80211.c deleted file mode 100755 index a55c27b..0000000 --- a/net/wireless_ath/lib80211.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * lib80211 -- common bits for IEEE802.11 drivers - * - * Copyright(c) 2008 John W. Linville <linville@tuxdriver.com> - * - * Portions copied from old ieee80211 component, w/ original copyright - * notices below: - * - * Host AP crypto routines - * - * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> - * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com> - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> -#include <linux/ctype.h> -#include <linux/ieee80211.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/string.h> - -#include <net/lib80211.h> - -#define DRV_NAME "lib80211" - -#define DRV_DESCRIPTION "common routines for IEEE802.11 drivers" - -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_AUTHOR("John W. Linville <linville@tuxdriver.com>"); -MODULE_LICENSE("GPL"); - -struct lib80211_crypto_alg { - struct list_head list; - struct lib80211_crypto_ops *ops; -}; - -static LIST_HEAD(lib80211_crypto_algs); -static DEFINE_SPINLOCK(lib80211_crypto_lock); - -static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, - int force); -static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info); -static void lib80211_crypt_deinit_handler(unsigned long data); - -const char *print_ssid(char *buf, const char *ssid, u8 ssid_len) -{ - const char *s = ssid; - char *d = buf; - - ssid_len = min_t(u8, ssid_len, IEEE80211_MAX_SSID_LEN); - while (ssid_len--) { - if (isprint(*s)) { - *d++ = *s++; - continue; - } - - *d++ = '\\'; - if (*s == '\0') - *d++ = '0'; - else if (*s == '\n') - *d++ = 'n'; - else if (*s == '\r') - *d++ = 'r'; - else if (*s == '\t') - *d++ = 't'; - else if (*s == '\\') - *d++ = '\\'; - else - d += snprintf(d, 3, "%03o", *s); - s++; - } - *d = '\0'; - return buf; -} -EXPORT_SYMBOL(print_ssid); - -int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name, - spinlock_t *lock) -{ - memset(info, 0, sizeof(*info)); - - info->name = name; - info->lock = lock; - - INIT_LIST_HEAD(&info->crypt_deinit_list); - setup_timer(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler, - (unsigned long)info); - - return 0; -} -EXPORT_SYMBOL(lib80211_crypt_info_init); - -void lib80211_crypt_info_free(struct lib80211_crypt_info *info) -{ - int i; - - lib80211_crypt_quiescing(info); - del_timer_sync(&info->crypt_deinit_timer); - lib80211_crypt_deinit_entries(info, 1); - - for (i = 0; i < NUM_WEP_KEYS; i++) { - struct lib80211_crypt_data *crypt = info->crypt[i]; - if (crypt) { - if (crypt->ops) { - crypt->ops->deinit(crypt->priv); - module_put(crypt->ops->owner); - } - kfree(crypt); - info->crypt[i] = NULL; - } - } -} -EXPORT_SYMBOL(lib80211_crypt_info_free); - -static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, - int force) -{ - struct lib80211_crypt_data *entry, *next; - unsigned long flags; - - spin_lock_irqsave(info->lock, flags); - list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) { - if (atomic_read(&entry->refcnt) != 0 && !force) - continue; - - list_del(&entry->list); - - if (entry->ops) { - entry->ops->deinit(entry->priv); - module_put(entry->ops->owner); - } - kfree(entry); - } - spin_unlock_irqrestore(info->lock, flags); -} - -/* After this, crypt_deinit_list won't accept new members */ -static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info) -{ - unsigned long flags; - - spin_lock_irqsave(info->lock, flags); - info->crypt_quiesced = 1; - spin_unlock_irqrestore(info->lock, flags); -} - -static void lib80211_crypt_deinit_handler(unsigned long data) -{ - struct lib80211_crypt_info *info = (struct lib80211_crypt_info *)data; - unsigned long flags; - - lib80211_crypt_deinit_entries(info, 0); - - spin_lock_irqsave(info->lock, flags); - if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) { - printk(KERN_DEBUG "%s: entries remaining in delayed crypt " - "deletion list\n", info->name); - info->crypt_deinit_timer.expires = jiffies + HZ; - add_timer(&info->crypt_deinit_timer); - } - spin_unlock_irqrestore(info->lock, flags); -} - -void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info, - struct lib80211_crypt_data **crypt) -{ - struct lib80211_crypt_data *tmp; - unsigned long flags; - - if (*crypt == NULL) - return; - - tmp = *crypt; - *crypt = NULL; - - /* must not run ops->deinit() while there may be pending encrypt or - * decrypt operations. Use a list of delayed deinits to avoid needing - * locking. */ - - spin_lock_irqsave(info->lock, flags); - if (!info->crypt_quiesced) { - list_add(&tmp->list, &info->crypt_deinit_list); - if (!timer_pending(&info->crypt_deinit_timer)) { - info->crypt_deinit_timer.expires = jiffies + HZ; - add_timer(&info->crypt_deinit_timer); - } - } - spin_unlock_irqrestore(info->lock, flags); -} -EXPORT_SYMBOL(lib80211_crypt_delayed_deinit); - -int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops) -{ - unsigned long flags; - struct lib80211_crypto_alg *alg; - - alg = kzalloc(sizeof(*alg), GFP_KERNEL); - if (alg == NULL) - return -ENOMEM; - - alg->ops = ops; - - spin_lock_irqsave(&lib80211_crypto_lock, flags); - list_add(&alg->list, &lib80211_crypto_algs); - spin_unlock_irqrestore(&lib80211_crypto_lock, flags); - - printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'\n", - ops->name); - - return 0; -} -EXPORT_SYMBOL(lib80211_register_crypto_ops); - -int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops) -{ - struct lib80211_crypto_alg *alg; - unsigned long flags; - - spin_lock_irqsave(&lib80211_crypto_lock, flags); - list_for_each_entry(alg, &lib80211_crypto_algs, list) { - if (alg->ops == ops) - goto found; - } - spin_unlock_irqrestore(&lib80211_crypto_lock, flags); - return -EINVAL; - - found: - printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm '%s'\n", - ops->name); - list_del(&alg->list); - spin_unlock_irqrestore(&lib80211_crypto_lock, flags); - kfree(alg); - return 0; -} -EXPORT_SYMBOL(lib80211_unregister_crypto_ops); - -struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name) -{ - struct lib80211_crypto_alg *alg; - unsigned long flags; - - spin_lock_irqsave(&lib80211_crypto_lock, flags); - list_for_each_entry(alg, &lib80211_crypto_algs, list) { - if (strcmp(alg->ops->name, name) == 0) - goto found; - } - spin_unlock_irqrestore(&lib80211_crypto_lock, flags); - return NULL; - - found: - spin_unlock_irqrestore(&lib80211_crypto_lock, flags); - return alg->ops; -} -EXPORT_SYMBOL(lib80211_get_crypto_ops); - -static void *lib80211_crypt_null_init(int keyidx) -{ - return (void *)1; -} - -static void lib80211_crypt_null_deinit(void *priv) -{ -} - -static struct lib80211_crypto_ops lib80211_crypt_null = { - .name = "NULL", - .init = lib80211_crypt_null_init, - .deinit = lib80211_crypt_null_deinit, - .owner = THIS_MODULE, -}; - -static int __init lib80211_init(void) -{ - pr_info(DRV_DESCRIPTION "\n"); - return lib80211_register_crypto_ops(&lib80211_crypt_null); -} - -static void __exit lib80211_exit(void) -{ - lib80211_unregister_crypto_ops(&lib80211_crypt_null); - BUG_ON(!list_empty(&lib80211_crypto_algs)); -} - -module_init(lib80211_init); -module_exit(lib80211_exit); diff --git a/net/wireless_ath/lib80211_crypt_ccmp.c b/net/wireless_ath/lib80211_crypt_ccmp.c deleted file mode 100755 index 755738d..0000000 --- a/net/wireless_ath/lib80211_crypt_ccmp.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - * lib80211 crypt: host-based CCMP encryption implementation for lib80211 - * - * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi> - * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com> - * - * 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. See README and COPYING for - * more details. - */ - -#include <linux/kernel.h> -#include <linux/err.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/random.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> -#include <linux/if_ether.h> -#include <linux/if_arp.h> -#include <asm/string.h> -#include <linux/wireless.h> - -#include <linux/ieee80211.h> - -#include <linux/crypto.h> - -#include <net/lib80211.h> - -MODULE_AUTHOR("Jouni Malinen"); -MODULE_DESCRIPTION("Host AP crypt: CCMP"); -MODULE_LICENSE("GPL"); - -#define AES_BLOCK_LEN 16 -#define CCMP_HDR_LEN 8 -#define CCMP_MIC_LEN 8 -#define CCMP_TK_LEN 16 -#define CCMP_PN_LEN 6 - -struct lib80211_ccmp_data { - u8 key[CCMP_TK_LEN]; - int key_set; - - u8 tx_pn[CCMP_PN_LEN]; - u8 rx_pn[CCMP_PN_LEN]; - - u32 dot11RSNAStatsCCMPFormatErrors; - u32 dot11RSNAStatsCCMPReplays; - u32 dot11RSNAStatsCCMPDecryptErrors; - - int key_idx; - - struct crypto_cipher *tfm; - - /* scratch buffers for virt_to_page() (crypto API) */ - u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN], - tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN]; - u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN]; -}; - -static inline void lib80211_ccmp_aes_encrypt(struct crypto_cipher *tfm, - const u8 pt[16], u8 ct[16]) -{ - crypto_cipher_encrypt_one(tfm, ct, pt); -} - -static void *lib80211_ccmp_init(int key_idx) -{ - struct lib80211_ccmp_data *priv; - - priv = kzalloc(sizeof(*priv), GFP_ATOMIC); - if (priv == NULL) - goto fail; - priv->key_idx = key_idx; - - priv->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->tfm)) { - priv->tfm = NULL; - goto fail; - } - - return priv; - - fail: - if (priv) { - if (priv->tfm) - crypto_free_cipher(priv->tfm); - kfree(priv); - } - - return NULL; -} - -static void lib80211_ccmp_deinit(void *priv) -{ - struct lib80211_ccmp_data *_priv = priv; - if (_priv && _priv->tfm) - crypto_free_cipher(_priv->tfm); - kfree(priv); -} - -static inline void xor_block(u8 * b, u8 * a, size_t len) -{ - int i; - for (i = 0; i < len; i++) - b[i] ^= a[i]; -} - -static void ccmp_init_blocks(struct crypto_cipher *tfm, - struct ieee80211_hdr *hdr, - u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0) -{ - u8 *pos, qc = 0; - size_t aad_len; - int a4_included, qc_included; - u8 aad[2 * AES_BLOCK_LEN]; - - a4_included = ieee80211_has_a4(hdr->frame_control); - qc_included = ieee80211_is_data_qos(hdr->frame_control); - - aad_len = 22; - if (a4_included) - aad_len += 6; - if (qc_included) { - pos = (u8 *) & hdr->addr4; - if (a4_included) - pos += 6; - qc = *pos & 0x0f; - aad_len += 2; - } - - /* CCM Initial Block: - * Flag (Include authentication header, M=3 (8-octet MIC), - * L=1 (2-octet Dlen)) - * Nonce: 0x00 | A2 | PN - * Dlen */ - b0[0] = 0x59; - b0[1] = qc; - memcpy(b0 + 2, hdr->addr2, ETH_ALEN); - memcpy(b0 + 8, pn, CCMP_PN_LEN); - b0[14] = (dlen >> 8) & 0xff; - b0[15] = dlen & 0xff; - - /* AAD: - * FC with bits 4..6 and 11..13 masked to zero; 14 is always one - * A1 | A2 | A3 - * SC with bits 4..15 (seq#) masked to zero - * A4 (if present) - * QC (if present) - */ - pos = (u8 *) hdr; - aad[0] = 0; /* aad_len >> 8 */ - aad[1] = aad_len & 0xff; - aad[2] = pos[0] & 0x8f; - aad[3] = pos[1] & 0xc7; - memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN); - pos = (u8 *) & hdr->seq_ctrl; - aad[22] = pos[0] & 0x0f; - aad[23] = 0; /* all bits masked */ - memset(aad + 24, 0, 8); - if (a4_included) - memcpy(aad + 24, hdr->addr4, ETH_ALEN); - if (qc_included) { - aad[a4_included ? 30 : 24] = qc; - /* rest of QC masked */ - } - - /* Start with the first block and AAD */ - lib80211_ccmp_aes_encrypt(tfm, b0, auth); - xor_block(auth, aad, AES_BLOCK_LEN); - lib80211_ccmp_aes_encrypt(tfm, auth, auth); - xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); - lib80211_ccmp_aes_encrypt(tfm, auth, auth); - b0[0] &= 0x07; - b0[14] = b0[15] = 0; - lib80211_ccmp_aes_encrypt(tfm, b0, s0); -} - -static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len, - u8 *aeskey, int keylen, void *priv) -{ - struct lib80211_ccmp_data *key = priv; - int i; - u8 *pos; - - if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len) - return -1; - - if (aeskey != NULL && keylen >= CCMP_TK_LEN) - memcpy(aeskey, key->key, CCMP_TK_LEN); - - pos = skb_push(skb, CCMP_HDR_LEN); - memmove(pos, pos + CCMP_HDR_LEN, hdr_len); - pos += hdr_len; - - i = CCMP_PN_LEN - 1; - while (i >= 0) { - key->tx_pn[i]++; - if (key->tx_pn[i] != 0) - break; - i--; - } - - *pos++ = key->tx_pn[5]; - *pos++ = key->tx_pn[4]; - *pos++ = 0; - *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ; - *pos++ = key->tx_pn[3]; - *pos++ = key->tx_pn[2]; - *pos++ = key->tx_pn[1]; - *pos++ = key->tx_pn[0]; - - return CCMP_HDR_LEN; -} - -static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) -{ - struct lib80211_ccmp_data *key = priv; - int data_len, i, blocks, last, len; - u8 *pos, *mic; - struct ieee80211_hdr *hdr; - u8 *b0 = key->tx_b0; - u8 *b = key->tx_b; - u8 *e = key->tx_e; - u8 *s0 = key->tx_s0; - - if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len) - return -1; - - data_len = skb->len - hdr_len; - len = lib80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv); - if (len < 0) - return -1; - - pos = skb->data + hdr_len + CCMP_HDR_LEN; - hdr = (struct ieee80211_hdr *)skb->data; - ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); - - blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); - last = data_len % AES_BLOCK_LEN; - - for (i = 1; i <= blocks; i++) { - len = (i == blocks && last) ? last : AES_BLOCK_LEN; - /* Authentication */ - xor_block(b, pos, len); - lib80211_ccmp_aes_encrypt(key->tfm, b, b); - /* Encryption, with counter */ - b0[14] = (i >> 8) & 0xff; - b0[15] = i & 0xff; - lib80211_ccmp_aes_encrypt(key->tfm, b0, e); - xor_block(pos, e, len); - pos += len; - } - - mic = skb_put(skb, CCMP_MIC_LEN); - for (i = 0; i < CCMP_MIC_LEN; i++) - mic[i] = b[i] ^ s0[i]; - - return 0; -} - -/* - * deal with seq counter wrapping correctly. - * refer to timer_after() for jiffies wrapping handling - */ -static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o) -{ - u32 iv32_n, iv16_n; - u32 iv32_o, iv16_o; - - iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3]; - iv16_n = (pn_n[4] << 8) | pn_n[5]; - - iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3]; - iv16_o = (pn_o[4] << 8) | pn_o[5]; - - if ((s32)iv32_n - (s32)iv32_o < 0 || - (iv32_n == iv32_o && iv16_n <= iv16_o)) - return 1; - return 0; -} - -static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) -{ - struct lib80211_ccmp_data *key = priv; - u8 keyidx, *pos; - struct ieee80211_hdr *hdr; - u8 *b0 = key->rx_b0; - u8 *b = key->rx_b; - u8 *a = key->rx_a; - u8 pn[6]; - int i, blocks, last, len; - size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN; - u8 *mic = skb->data + skb->len - CCMP_MIC_LEN; - - if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) { - key->dot11RSNAStatsCCMPFormatErrors++; - return -1; - } - - hdr = (struct ieee80211_hdr *)skb->data; - pos = skb->data + hdr_len; - keyidx = pos[3]; - if (!(keyidx & (1 << 5))) { - if (net_ratelimit()) { - printk(KERN_DEBUG "CCMP: received packet without ExtIV" - " flag from %pM\n", hdr->addr2); - } - key->dot11RSNAStatsCCMPFormatErrors++; - return -2; - } - keyidx >>= 6; - if (key->key_idx != keyidx) { - printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame " - "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv); - return -6; - } - if (!key->key_set) { - if (net_ratelimit()) { - printk(KERN_DEBUG "CCMP: received packet from %pM" - " with keyid=%d that does not have a configured" - " key\n", hdr->addr2, keyidx); - } - return -3; - } - - pn[0] = pos[7]; - pn[1] = pos[6]; - pn[2] = pos[5]; - pn[3] = pos[4]; - pn[4] = pos[1]; - pn[5] = pos[0]; - pos += 8; - - if (ccmp_replay_check(pn, key->rx_pn)) { -#ifdef CONFIG_LIB80211_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "CCMP: replay detected: STA=%pM " - "previous PN %02x%02x%02x%02x%02x%02x " - "received PN %02x%02x%02x%02x%02x%02x\n", - hdr->addr2, - key->rx_pn[0], key->rx_pn[1], key->rx_pn[2], - key->rx_pn[3], key->rx_pn[4], key->rx_pn[5], - pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]); - } -#endif - key->dot11RSNAStatsCCMPReplays++; - return -4; - } - - ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b); - xor_block(mic, b, CCMP_MIC_LEN); - - blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); - last = data_len % AES_BLOCK_LEN; - - for (i = 1; i <= blocks; i++) { - len = (i == blocks && last) ? last : AES_BLOCK_LEN; - /* Decrypt, with counter */ - b0[14] = (i >> 8) & 0xff; - b0[15] = i & 0xff; - lib80211_ccmp_aes_encrypt(key->tfm, b0, b); - xor_block(pos, b, len); - /* Authentication */ - xor_block(a, pos, len); - lib80211_ccmp_aes_encrypt(key->tfm, a, a); - pos += len; - } - - if (memcmp(mic, a, CCMP_MIC_LEN) != 0) { - if (net_ratelimit()) { - printk(KERN_DEBUG "CCMP: decrypt failed: STA=" - "%pM\n", hdr->addr2); - } - key->dot11RSNAStatsCCMPDecryptErrors++; - return -5; - } - - memcpy(key->rx_pn, pn, CCMP_PN_LEN); - - /* Remove hdr and MIC */ - memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len); - skb_pull(skb, CCMP_HDR_LEN); - skb_trim(skb, skb->len - CCMP_MIC_LEN); - - return keyidx; -} - -static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv) -{ - struct lib80211_ccmp_data *data = priv; - int keyidx; - struct crypto_cipher *tfm = data->tfm; - - keyidx = data->key_idx; - memset(data, 0, sizeof(*data)); - data->key_idx = keyidx; - data->tfm = tfm; - if (len == CCMP_TK_LEN) { - memcpy(data->key, key, CCMP_TK_LEN); - data->key_set = 1; - if (seq) { - data->rx_pn[0] = seq[5]; - data->rx_pn[1] = seq[4]; - data->rx_pn[2] = seq[3]; - data->rx_pn[3] = seq[2]; - data->rx_pn[4] = seq[1]; - data->rx_pn[5] = seq[0]; - } - crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN); - } else if (len == 0) - data->key_set = 0; - else - return -1; - - return 0; -} - -static int lib80211_ccmp_get_key(void *key, int len, u8 * seq, void *priv) -{ - struct lib80211_ccmp_data *data = priv; - - if (len < CCMP_TK_LEN) - return -1; - - if (!data->key_set) - return 0; - memcpy(key, data->key, CCMP_TK_LEN); - - if (seq) { - seq[0] = data->tx_pn[5]; - seq[1] = data->tx_pn[4]; - seq[2] = data->tx_pn[3]; - seq[3] = data->tx_pn[2]; - seq[4] = data->tx_pn[1]; - seq[5] = data->tx_pn[0]; - } - - return CCMP_TK_LEN; -} - -static char *lib80211_ccmp_print_stats(char *p, void *priv) -{ - struct lib80211_ccmp_data *ccmp = priv; - - p += sprintf(p, "key[%d] alg=CCMP key_set=%d " - "tx_pn=%02x%02x%02x%02x%02x%02x " - "rx_pn=%02x%02x%02x%02x%02x%02x " - "format_errors=%d replays=%d decrypt_errors=%d\n", - ccmp->key_idx, ccmp->key_set, - ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2], - ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5], - ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2], - ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5], - ccmp->dot11RSNAStatsCCMPFormatErrors, - ccmp->dot11RSNAStatsCCMPReplays, - ccmp->dot11RSNAStatsCCMPDecryptErrors); - - return p; -} - -static struct lib80211_crypto_ops lib80211_crypt_ccmp = { - .name = "CCMP", - .init = lib80211_ccmp_init, - .deinit = lib80211_ccmp_deinit, - .encrypt_mpdu = lib80211_ccmp_encrypt, - .decrypt_mpdu = lib80211_ccmp_decrypt, - .encrypt_msdu = NULL, - .decrypt_msdu = NULL, - .set_key = lib80211_ccmp_set_key, - .get_key = lib80211_ccmp_get_key, - .print_stats = lib80211_ccmp_print_stats, - .extra_mpdu_prefix_len = CCMP_HDR_LEN, - .extra_mpdu_postfix_len = CCMP_MIC_LEN, - .owner = THIS_MODULE, -}; - -static int __init lib80211_crypto_ccmp_init(void) -{ - return lib80211_register_crypto_ops(&lib80211_crypt_ccmp); -} - -static void __exit lib80211_crypto_ccmp_exit(void) -{ - lib80211_unregister_crypto_ops(&lib80211_crypt_ccmp); -} - -module_init(lib80211_crypto_ccmp_init); -module_exit(lib80211_crypto_ccmp_exit); diff --git a/net/wireless_ath/lib80211_crypt_tkip.c b/net/wireless_ath/lib80211_crypt_tkip.c deleted file mode 100755 index 3873484..0000000 --- a/net/wireless_ath/lib80211_crypt_tkip.c +++ /dev/null @@ -1,780 +0,0 @@ -/* - * lib80211 crypt: host-based TKIP encryption implementation for lib80211 - * - * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi> - * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com> - * - * 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. See README and COPYING for - * more details. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/err.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/random.h> -#include <linux/scatterlist.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> -#include <linux/mm.h> -#include <linux/if_ether.h> -#include <linux/if_arp.h> -#include <asm/string.h> - -#include <linux/wireless.h> -#include <linux/ieee80211.h> -#include <net/iw_handler.h> - -#include <linux/crypto.h> -#include <linux/crc32.h> - -#include <net/lib80211.h> - -MODULE_AUTHOR("Jouni Malinen"); -MODULE_DESCRIPTION("lib80211 crypt: TKIP"); -MODULE_LICENSE("GPL"); - -#define TKIP_HDR_LEN 8 - -struct lib80211_tkip_data { -#define TKIP_KEY_LEN 32 - u8 key[TKIP_KEY_LEN]; - int key_set; - - u32 tx_iv32; - u16 tx_iv16; - u16 tx_ttak[5]; - int tx_phase1_done; - - u32 rx_iv32; - u16 rx_iv16; - u16 rx_ttak[5]; - int rx_phase1_done; - u32 rx_iv32_new; - u16 rx_iv16_new; - - u32 dot11RSNAStatsTKIPReplays; - u32 dot11RSNAStatsTKIPICVErrors; - u32 dot11RSNAStatsTKIPLocalMICFailures; - - int key_idx; - - struct crypto_blkcipher *rx_tfm_arc4; - struct crypto_hash *rx_tfm_michael; - struct crypto_blkcipher *tx_tfm_arc4; - struct crypto_hash *tx_tfm_michael; - - /* scratch buffers for virt_to_page() (crypto API) */ - u8 rx_hdr[16], tx_hdr[16]; - - unsigned long flags; -}; - -static unsigned long lib80211_tkip_set_flags(unsigned long flags, void *priv) -{ - struct lib80211_tkip_data *_priv = priv; - unsigned long old_flags = _priv->flags; - _priv->flags = flags; - return old_flags; -} - -static unsigned long lib80211_tkip_get_flags(void *priv) -{ - struct lib80211_tkip_data *_priv = priv; - return _priv->flags; -} - -static void *lib80211_tkip_init(int key_idx) -{ - struct lib80211_tkip_data *priv; - - priv = kzalloc(sizeof(*priv), GFP_ATOMIC); - if (priv == NULL) - goto fail; - - priv->key_idx = key_idx; - - priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, - CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->tx_tfm_arc4)) { - priv->tx_tfm_arc4 = NULL; - goto fail; - } - - priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0, - CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->tx_tfm_michael)) { - priv->tx_tfm_michael = NULL; - goto fail; - } - - priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, - CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->rx_tfm_arc4)) { - priv->rx_tfm_arc4 = NULL; - goto fail; - } - - priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0, - CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->rx_tfm_michael)) { - priv->rx_tfm_michael = NULL; - goto fail; - } - - return priv; - - fail: - if (priv) { - if (priv->tx_tfm_michael) - crypto_free_hash(priv->tx_tfm_michael); - if (priv->tx_tfm_arc4) - crypto_free_blkcipher(priv->tx_tfm_arc4); - if (priv->rx_tfm_michael) - crypto_free_hash(priv->rx_tfm_michael); - if (priv->rx_tfm_arc4) - crypto_free_blkcipher(priv->rx_tfm_arc4); - kfree(priv); - } - - return NULL; -} - -static void lib80211_tkip_deinit(void *priv) -{ - struct lib80211_tkip_data *_priv = priv; - if (_priv) { - if (_priv->tx_tfm_michael) - crypto_free_hash(_priv->tx_tfm_michael); - if (_priv->tx_tfm_arc4) - crypto_free_blkcipher(_priv->tx_tfm_arc4); - if (_priv->rx_tfm_michael) - crypto_free_hash(_priv->rx_tfm_michael); - if (_priv->rx_tfm_arc4) - crypto_free_blkcipher(_priv->rx_tfm_arc4); - } - kfree(priv); -} - -static inline u16 RotR1(u16 val) -{ - return (val >> 1) | (val << 15); -} - -static inline u8 Lo8(u16 val) -{ - return val & 0xff; -} - -static inline u8 Hi8(u16 val) -{ - return val >> 8; -} - -static inline u16 Lo16(u32 val) -{ - return val & 0xffff; -} - -static inline u16 Hi16(u32 val) -{ - return val >> 16; -} - -static inline u16 Mk16(u8 hi, u8 lo) -{ - return lo | (((u16) hi) << 8); -} - -static inline u16 Mk16_le(__le16 * v) -{ - return le16_to_cpu(*v); -} - -static const u16 Sbox[256] = { - 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, - 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, - 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, - 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, - 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, - 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, - 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, - 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, - 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, - 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, - 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, - 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, - 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, - 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, - 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, - 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, - 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, - 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, - 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, - 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, - 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, - 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, - 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, - 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, - 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, - 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, - 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, - 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, - 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, - 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, - 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, - 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, -}; - -static inline u16 _S_(u16 v) -{ - u16 t = Sbox[Hi8(v)]; - return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8)); -} - -#define PHASE1_LOOP_COUNT 8 - -static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA, - u32 IV32) -{ - int i, j; - - /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */ - TTAK[0] = Lo16(IV32); - TTAK[1] = Hi16(IV32); - TTAK[2] = Mk16(TA[1], TA[0]); - TTAK[3] = Mk16(TA[3], TA[2]); - TTAK[4] = Mk16(TA[5], TA[4]); - - for (i = 0; i < PHASE1_LOOP_COUNT; i++) { - j = 2 * (i & 1); - TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j])); - TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j])); - TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j])); - TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j])); - TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i; - } -} - -static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK, - u16 IV16) -{ - /* Make temporary area overlap WEP seed so that the final copy can be - * avoided on little endian hosts. */ - u16 *PPK = (u16 *) & WEPSeed[4]; - - /* Step 1 - make copy of TTAK and bring in TSC */ - PPK[0] = TTAK[0]; - PPK[1] = TTAK[1]; - PPK[2] = TTAK[2]; - PPK[3] = TTAK[3]; - PPK[4] = TTAK[4]; - PPK[5] = TTAK[4] + IV16; - - /* Step 2 - 96-bit bijective mixing using S-box */ - PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0])); - PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2])); - PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4])); - PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6])); - PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8])); - PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10])); - - PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12])); - PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14])); - PPK[2] += RotR1(PPK[1]); - PPK[3] += RotR1(PPK[2]); - PPK[4] += RotR1(PPK[3]); - PPK[5] += RotR1(PPK[4]); - - /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value - * WEPSeed[0..2] is transmitted as WEP IV */ - WEPSeed[0] = Hi8(IV16); - WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F; - WEPSeed[2] = Lo8(IV16); - WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1); - -#ifdef __BIG_ENDIAN - { - int i; - for (i = 0; i < 6; i++) - PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8); - } -#endif -} - -static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len, - u8 * rc4key, int keylen, void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - u8 *pos; - struct ieee80211_hdr *hdr; - - hdr = (struct ieee80211_hdr *)skb->data; - - if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len) - return -1; - - if (rc4key == NULL || keylen < 16) - return -1; - - if (!tkey->tx_phase1_done) { - tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2, - tkey->tx_iv32); - tkey->tx_phase1_done = 1; - } - tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16); - - pos = skb_push(skb, TKIP_HDR_LEN); - memmove(pos, pos + TKIP_HDR_LEN, hdr_len); - pos += hdr_len; - - *pos++ = *rc4key; - *pos++ = *(rc4key + 1); - *pos++ = *(rc4key + 2); - *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ; - *pos++ = tkey->tx_iv32 & 0xff; - *pos++ = (tkey->tx_iv32 >> 8) & 0xff; - *pos++ = (tkey->tx_iv32 >> 16) & 0xff; - *pos++ = (tkey->tx_iv32 >> 24) & 0xff; - - tkey->tx_iv16++; - if (tkey->tx_iv16 == 0) { - tkey->tx_phase1_done = 0; - tkey->tx_iv32++; - } - - return TKIP_HDR_LEN; -} - -static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - struct blkcipher_desc desc = { .tfm = tkey->tx_tfm_arc4 }; - int len; - u8 rc4key[16], *pos, *icv; - u32 crc; - struct scatterlist sg; - - if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) { - if (net_ratelimit()) { - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *)skb->data; - printk(KERN_DEBUG ": TKIP countermeasures: dropped " - "TX packet to %pM\n", hdr->addr1); - } - return -1; - } - - if (skb_tailroom(skb) < 4 || skb->len < hdr_len) - return -1; - - len = skb->len - hdr_len; - pos = skb->data + hdr_len; - - if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0) - return -1; - - crc = ~crc32_le(~0, pos, len); - icv = skb_put(skb, 4); - icv[0] = crc; - icv[1] = crc >> 8; - icv[2] = crc >> 16; - icv[3] = crc >> 24; - - crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16); - sg_init_one(&sg, pos, len + 4); - return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); -} - -/* - * deal with seq counter wrapping correctly. - * refer to timer_after() for jiffies wrapping handling - */ -static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n, - u32 iv32_o, u16 iv16_o) -{ - if ((s32)iv32_n - (s32)iv32_o < 0 || - (iv32_n == iv32_o && iv16_n <= iv16_o)) - return 1; - return 0; -} - -static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - struct blkcipher_desc desc = { .tfm = tkey->rx_tfm_arc4 }; - u8 rc4key[16]; - u8 keyidx, *pos; - u32 iv32; - u16 iv16; - struct ieee80211_hdr *hdr; - u8 icv[4]; - u32 crc; - struct scatterlist sg; - int plen; - - hdr = (struct ieee80211_hdr *)skb->data; - - if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) { - if (net_ratelimit()) { - printk(KERN_DEBUG ": TKIP countermeasures: dropped " - "received packet from %pM\n", hdr->addr2); - } - return -1; - } - - if (skb->len < hdr_len + TKIP_HDR_LEN + 4) - return -1; - - pos = skb->data + hdr_len; - keyidx = pos[3]; - if (!(keyidx & (1 << 5))) { - if (net_ratelimit()) { - printk(KERN_DEBUG "TKIP: received packet without ExtIV" - " flag from %pM\n", hdr->addr2); - } - return -2; - } - keyidx >>= 6; - if (tkey->key_idx != keyidx) { - printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame " - "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv); - return -6; - } - if (!tkey->key_set) { - if (net_ratelimit()) { - printk(KERN_DEBUG "TKIP: received packet from %pM" - " with keyid=%d that does not have a configured" - " key\n", hdr->addr2, keyidx); - } - return -3; - } - iv16 = (pos[0] << 8) | pos[2]; - iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); - pos += TKIP_HDR_LEN; - - if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) { -#ifdef CONFIG_LIB80211_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "TKIP: replay detected: STA=%pM" - " previous TSC %08x%04x received TSC " - "%08x%04x\n", hdr->addr2, - tkey->rx_iv32, tkey->rx_iv16, iv32, iv16); - } -#endif - tkey->dot11RSNAStatsTKIPReplays++; - return -4; - } - - if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) { - tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32); - tkey->rx_phase1_done = 1; - } - tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16); - - plen = skb->len - hdr_len - 12; - - crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16); - sg_init_one(&sg, pos, plen + 4); - if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) { - if (net_ratelimit()) { - printk(KERN_DEBUG ": TKIP: failed to decrypt " - "received packet from %pM\n", - hdr->addr2); - } - return -7; - } - - crc = ~crc32_le(~0, pos, plen); - icv[0] = crc; - icv[1] = crc >> 8; - icv[2] = crc >> 16; - icv[3] = crc >> 24; - if (memcmp(icv, pos + plen, 4) != 0) { - if (iv32 != tkey->rx_iv32) { - /* Previously cached Phase1 result was already lost, so - * it needs to be recalculated for the next packet. */ - tkey->rx_phase1_done = 0; - } -#ifdef CONFIG_LIB80211_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "TKIP: ICV error detected: STA=" - "%pM\n", hdr->addr2); - } -#endif - tkey->dot11RSNAStatsTKIPICVErrors++; - return -5; - } - - /* Update real counters only after Michael MIC verification has - * completed */ - tkey->rx_iv32_new = iv32; - tkey->rx_iv16_new = iv16; - - /* Remove IV and ICV */ - memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len); - skb_pull(skb, TKIP_HDR_LEN); - skb_trim(skb, skb->len - 4); - - return keyidx; -} - -static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr, - u8 * data, size_t data_len, u8 * mic) -{ - struct hash_desc desc; - struct scatterlist sg[2]; - - if (tfm_michael == NULL) { - pr_warn("%s(): tfm_michael == NULL\n", __func__); - return -1; - } - sg_init_table(sg, 2); - sg_set_buf(&sg[0], hdr, 16); - sg_set_buf(&sg[1], data, data_len); - - if (crypto_hash_setkey(tfm_michael, key, 8)) - return -1; - - desc.tfm = tfm_michael; - desc.flags = 0; - return crypto_hash_digest(&desc, sg, data_len + 16, mic); -} - -static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr) -{ - struct ieee80211_hdr *hdr11; - - hdr11 = (struct ieee80211_hdr *)skb->data; - - switch (le16_to_cpu(hdr11->frame_control) & - (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { - case IEEE80211_FCTL_TODS: - memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ - memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ - break; - case IEEE80211_FCTL_FROMDS: - memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ - memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */ - break; - case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: - memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ - memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */ - break; - case 0: - memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ - memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ - break; - } - - if (ieee80211_is_data_qos(hdr11->frame_control)) { - hdr[12] = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(hdr11))) - & IEEE80211_QOS_CTL_TID_MASK; - } else - hdr[12] = 0; /* priority */ - - hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ -} - -static int lib80211_michael_mic_add(struct sk_buff *skb, int hdr_len, - void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - u8 *pos; - - if (skb_tailroom(skb) < 8 || skb->len < hdr_len) { - printk(KERN_DEBUG "Invalid packet for Michael MIC add " - "(tailroom=%d hdr_len=%d skb->len=%d)\n", - skb_tailroom(skb), hdr_len, skb->len); - return -1; - } - - michael_mic_hdr(skb, tkey->tx_hdr); - pos = skb_put(skb, 8); - if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr, - skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) - return -1; - - return 0; -} - -static void lib80211_michael_mic_failure(struct net_device *dev, - struct ieee80211_hdr *hdr, - int keyidx) -{ - union iwreq_data wrqu; - struct iw_michaelmicfailure ev; - - /* TODO: needed parameters: count, keyid, key type, TSC */ - memset(&ev, 0, sizeof(ev)); - ev.flags = keyidx & IW_MICFAILURE_KEY_ID; - if (hdr->addr1[0] & 0x01) - ev.flags |= IW_MICFAILURE_GROUP; - else - ev.flags |= IW_MICFAILURE_PAIRWISE; - ev.src_addr.sa_family = ARPHRD_ETHER; - memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN); - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = sizeof(ev); - wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev); -} - -static int lib80211_michael_mic_verify(struct sk_buff *skb, int keyidx, - int hdr_len, void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - u8 mic[8]; - - if (!tkey->key_set) - return -1; - - michael_mic_hdr(skb, tkey->rx_hdr); - if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr, - skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) - return -1; - if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) { - struct ieee80211_hdr *hdr; - hdr = (struct ieee80211_hdr *)skb->data; - printk(KERN_DEBUG "%s: Michael MIC verification failed for " - "MSDU from %pM keyidx=%d\n", - skb->dev ? skb->dev->name : "N/A", hdr->addr2, - keyidx); - if (skb->dev) - lib80211_michael_mic_failure(skb->dev, hdr, keyidx); - tkey->dot11RSNAStatsTKIPLocalMICFailures++; - return -1; - } - - /* Update TSC counters for RX now that the packet verification has - * completed. */ - tkey->rx_iv32 = tkey->rx_iv32_new; - tkey->rx_iv16 = tkey->rx_iv16_new; - - skb_trim(skb, skb->len - 8); - - return 0; -} - -static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - int keyidx; - struct crypto_hash *tfm = tkey->tx_tfm_michael; - struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4; - struct crypto_hash *tfm3 = tkey->rx_tfm_michael; - struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4; - - keyidx = tkey->key_idx; - memset(tkey, 0, sizeof(*tkey)); - tkey->key_idx = keyidx; - tkey->tx_tfm_michael = tfm; - tkey->tx_tfm_arc4 = tfm2; - tkey->rx_tfm_michael = tfm3; - tkey->rx_tfm_arc4 = tfm4; - if (len == TKIP_KEY_LEN) { - memcpy(tkey->key, key, TKIP_KEY_LEN); - tkey->key_set = 1; - tkey->tx_iv16 = 1; /* TSC is initialized to 1 */ - if (seq) { - tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) | - (seq[3] << 8) | seq[2]; - tkey->rx_iv16 = (seq[1] << 8) | seq[0]; - } - } else if (len == 0) - tkey->key_set = 0; - else - return -1; - - return 0; -} - -static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - - if (len < TKIP_KEY_LEN) - return -1; - - if (!tkey->key_set) - return 0; - memcpy(key, tkey->key, TKIP_KEY_LEN); - - if (seq) { - /* Return the sequence number of the last transmitted frame. */ - u16 iv16 = tkey->tx_iv16; - u32 iv32 = tkey->tx_iv32; - if (iv16 == 0) - iv32--; - iv16--; - seq[0] = tkey->tx_iv16; - seq[1] = tkey->tx_iv16 >> 8; - seq[2] = tkey->tx_iv32; - seq[3] = tkey->tx_iv32 >> 8; - seq[4] = tkey->tx_iv32 >> 16; - seq[5] = tkey->tx_iv32 >> 24; - } - - return TKIP_KEY_LEN; -} - -static char *lib80211_tkip_print_stats(char *p, void *priv) -{ - struct lib80211_tkip_data *tkip = priv; - p += sprintf(p, "key[%d] alg=TKIP key_set=%d " - "tx_pn=%02x%02x%02x%02x%02x%02x " - "rx_pn=%02x%02x%02x%02x%02x%02x " - "replays=%d icv_errors=%d local_mic_failures=%d\n", - tkip->key_idx, tkip->key_set, - (tkip->tx_iv32 >> 24) & 0xff, - (tkip->tx_iv32 >> 16) & 0xff, - (tkip->tx_iv32 >> 8) & 0xff, - tkip->tx_iv32 & 0xff, - (tkip->tx_iv16 >> 8) & 0xff, - tkip->tx_iv16 & 0xff, - (tkip->rx_iv32 >> 24) & 0xff, - (tkip->rx_iv32 >> 16) & 0xff, - (tkip->rx_iv32 >> 8) & 0xff, - tkip->rx_iv32 & 0xff, - (tkip->rx_iv16 >> 8) & 0xff, - tkip->rx_iv16 & 0xff, - tkip->dot11RSNAStatsTKIPReplays, - tkip->dot11RSNAStatsTKIPICVErrors, - tkip->dot11RSNAStatsTKIPLocalMICFailures); - return p; -} - -static struct lib80211_crypto_ops lib80211_crypt_tkip = { - .name = "TKIP", - .init = lib80211_tkip_init, - .deinit = lib80211_tkip_deinit, - .encrypt_mpdu = lib80211_tkip_encrypt, - .decrypt_mpdu = lib80211_tkip_decrypt, - .encrypt_msdu = lib80211_michael_mic_add, - .decrypt_msdu = lib80211_michael_mic_verify, - .set_key = lib80211_tkip_set_key, - .get_key = lib80211_tkip_get_key, - .print_stats = lib80211_tkip_print_stats, - .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */ - .extra_mpdu_postfix_len = 4, /* ICV */ - .extra_msdu_postfix_len = 8, /* MIC */ - .get_flags = lib80211_tkip_get_flags, - .set_flags = lib80211_tkip_set_flags, - .owner = THIS_MODULE, -}; - -static int __init lib80211_crypto_tkip_init(void) -{ - return lib80211_register_crypto_ops(&lib80211_crypt_tkip); -} - -static void __exit lib80211_crypto_tkip_exit(void) -{ - lib80211_unregister_crypto_ops(&lib80211_crypt_tkip); -} - -module_init(lib80211_crypto_tkip_init); -module_exit(lib80211_crypto_tkip_exit); diff --git a/net/wireless_ath/lib80211_crypt_wep.c b/net/wireless_ath/lib80211_crypt_wep.c deleted file mode 100755 index c130401..0000000 --- a/net/wireless_ath/lib80211_crypt_wep.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * lib80211 crypt: host-based WEP encryption implementation for lib80211 - * - * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi> - * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com> - * - * 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. See README and COPYING for - * more details. - */ - -#include <linux/err.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/random.h> -#include <linux/scatterlist.h> -#include <linux/skbuff.h> -#include <linux/mm.h> -#include <asm/string.h> - -#include <net/lib80211.h> - -#include <linux/crypto.h> -#include <linux/crc32.h> - -MODULE_AUTHOR("Jouni Malinen"); -MODULE_DESCRIPTION("lib80211 crypt: WEP"); -MODULE_LICENSE("GPL"); - -struct lib80211_wep_data { - u32 iv; -#define WEP_KEY_LEN 13 - u8 key[WEP_KEY_LEN + 1]; - u8 key_len; - u8 key_idx; - struct crypto_blkcipher *tx_tfm; - struct crypto_blkcipher *rx_tfm; -}; - -static void *lib80211_wep_init(int keyidx) -{ - struct lib80211_wep_data *priv; - - priv = kzalloc(sizeof(*priv), GFP_ATOMIC); - if (priv == NULL) - goto fail; - priv->key_idx = keyidx; - - priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->tx_tfm)) { - priv->tx_tfm = NULL; - goto fail; - } - - priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->rx_tfm)) { - priv->rx_tfm = NULL; - goto fail; - } - /* start WEP IV from a random value */ - get_random_bytes(&priv->iv, 4); - - return priv; - - fail: - if (priv) { - if (priv->tx_tfm) - crypto_free_blkcipher(priv->tx_tfm); - if (priv->rx_tfm) - crypto_free_blkcipher(priv->rx_tfm); - kfree(priv); - } - return NULL; -} - -static void lib80211_wep_deinit(void *priv) -{ - struct lib80211_wep_data *_priv = priv; - if (_priv) { - if (_priv->tx_tfm) - crypto_free_blkcipher(_priv->tx_tfm); - if (_priv->rx_tfm) - crypto_free_blkcipher(_priv->rx_tfm); - } - kfree(priv); -} - -/* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */ -static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len, - u8 *key, int keylen, void *priv) -{ - struct lib80211_wep_data *wep = priv; - u32 klen; - u8 *pos; - - if (skb_headroom(skb) < 4 || skb->len < hdr_len) - return -1; - - pos = skb_push(skb, 4); - memmove(pos, pos + 4, hdr_len); - pos += hdr_len; - - klen = 3 + wep->key_len; - - wep->iv++; - - /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key - * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N) - * can be used to speedup attacks, so avoid using them. */ - if ((wep->iv & 0xff00) == 0xff00) { - u8 B = (wep->iv >> 16) & 0xff; - if (B >= 3 && B < klen) - wep->iv += 0x0100; - } - - /* Prepend 24-bit IV to RC4 key and TX frame */ - *pos++ = (wep->iv >> 16) & 0xff; - *pos++ = (wep->iv >> 8) & 0xff; - *pos++ = wep->iv & 0xff; - *pos++ = wep->key_idx << 6; - - return 0; -} - -/* Perform WEP encryption on given skb that has at least 4 bytes of headroom - * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted, - * so the payload length increases with 8 bytes. - * - * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) - */ -static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) -{ - struct lib80211_wep_data *wep = priv; - struct blkcipher_desc desc = { .tfm = wep->tx_tfm }; - u32 crc, klen, len; - u8 *pos, *icv; - struct scatterlist sg; - u8 key[WEP_KEY_LEN + 3]; - - /* other checks are in lib80211_wep_build_iv */ - if (skb_tailroom(skb) < 4) - return -1; - - /* add the IV to the frame */ - if (lib80211_wep_build_iv(skb, hdr_len, NULL, 0, priv)) - return -1; - - /* Copy the IV into the first 3 bytes of the key */ - skb_copy_from_linear_data_offset(skb, hdr_len, key, 3); - - /* Copy rest of the WEP key (the secret part) */ - memcpy(key + 3, wep->key, wep->key_len); - - len = skb->len - hdr_len - 4; - pos = skb->data + hdr_len + 4; - klen = 3 + wep->key_len; - - /* Append little-endian CRC32 over only the data and encrypt it to produce ICV */ - crc = ~crc32_le(~0, pos, len); - icv = skb_put(skb, 4); - icv[0] = crc; - icv[1] = crc >> 8; - icv[2] = crc >> 16; - icv[3] = crc >> 24; - - crypto_blkcipher_setkey(wep->tx_tfm, key, klen); - sg_init_one(&sg, pos, len + 4); - return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); -} - -/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of - * the frame: IV (4 bytes), encrypted payload (including SNAP header), - * ICV (4 bytes). len includes both IV and ICV. - * - * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on - * failure. If frame is OK, IV and ICV will be removed. - */ -static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) -{ - struct lib80211_wep_data *wep = priv; - struct blkcipher_desc desc = { .tfm = wep->rx_tfm }; - u32 crc, klen, plen; - u8 key[WEP_KEY_LEN + 3]; - u8 keyidx, *pos, icv[4]; - struct scatterlist sg; - - if (skb->len < hdr_len + 8) - return -1; - - pos = skb->data + hdr_len; - key[0] = *pos++; - key[1] = *pos++; - key[2] = *pos++; - keyidx = *pos++ >> 6; - if (keyidx != wep->key_idx) - return -1; - - klen = 3 + wep->key_len; - - /* Copy rest of the WEP key (the secret part) */ - memcpy(key + 3, wep->key, wep->key_len); - - /* Apply RC4 to data and compute CRC32 over decrypted data */ - plen = skb->len - hdr_len - 8; - - crypto_blkcipher_setkey(wep->rx_tfm, key, klen); - sg_init_one(&sg, pos, plen + 4); - if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) - return -7; - - crc = ~crc32_le(~0, pos, plen); - icv[0] = crc; - icv[1] = crc >> 8; - icv[2] = crc >> 16; - icv[3] = crc >> 24; - if (memcmp(icv, pos + plen, 4) != 0) { - /* ICV mismatch - drop frame */ - return -2; - } - - /* Remove IV and ICV */ - memmove(skb->data + 4, skb->data, hdr_len); - skb_pull(skb, 4); - skb_trim(skb, skb->len - 4); - - return 0; -} - -static int lib80211_wep_set_key(void *key, int len, u8 * seq, void *priv) -{ - struct lib80211_wep_data *wep = priv; - - if (len < 0 || len > WEP_KEY_LEN) - return -1; - - memcpy(wep->key, key, len); - wep->key_len = len; - - return 0; -} - -static int lib80211_wep_get_key(void *key, int len, u8 * seq, void *priv) -{ - struct lib80211_wep_data *wep = priv; - - if (len < wep->key_len) - return -1; - - memcpy(key, wep->key, wep->key_len); - - return wep->key_len; -} - -static char *lib80211_wep_print_stats(char *p, void *priv) -{ - struct lib80211_wep_data *wep = priv; - p += sprintf(p, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len); - return p; -} - -static struct lib80211_crypto_ops lib80211_crypt_wep = { - .name = "WEP", - .init = lib80211_wep_init, - .deinit = lib80211_wep_deinit, - .encrypt_mpdu = lib80211_wep_encrypt, - .decrypt_mpdu = lib80211_wep_decrypt, - .encrypt_msdu = NULL, - .decrypt_msdu = NULL, - .set_key = lib80211_wep_set_key, - .get_key = lib80211_wep_get_key, - .print_stats = lib80211_wep_print_stats, - .extra_mpdu_prefix_len = 4, /* IV */ - .extra_mpdu_postfix_len = 4, /* ICV */ - .owner = THIS_MODULE, -}; - -static int __init lib80211_crypto_wep_init(void) -{ - return lib80211_register_crypto_ops(&lib80211_crypt_wep); -} - -static void __exit lib80211_crypto_wep_exit(void) -{ - lib80211_unregister_crypto_ops(&lib80211_crypt_wep); -} - -module_init(lib80211_crypto_wep_init); -module_exit(lib80211_crypto_wep_exit); diff --git a/net/wireless_ath/mesh.c b/net/wireless_ath/mesh.c deleted file mode 100755 index ba5337d..0000000 --- a/net/wireless_ath/mesh.c +++ /dev/null @@ -1,165 +0,0 @@ -#include <linux/ieee80211.h> -#include <linux/export.h> -#include <net/cfg80211.h> -#include "nl80211.h" -#include "core.h" - -/* Default values, timeouts in ms */ -#define MESH_TTL 31 -#define MESH_DEFAULT_ELEMENT_TTL 31 -#define MESH_MAX_RETR 3 -#define MESH_RET_T 100 -#define MESH_CONF_T 100 -#define MESH_HOLD_T 100 - -#define MESH_PATH_TIMEOUT 5000 -#define MESH_RANN_INTERVAL 5000 - -/* - * Minimum interval between two consecutive PREQs originated by the same - * interface - */ -#define MESH_PREQ_MIN_INT 10 -#define MESH_DIAM_TRAVERSAL_TIME 50 - -/* - * A path will be refreshed if it is used PATH_REFRESH_TIME milliseconds - * before timing out. This way it will remain ACTIVE and no data frames - * will be unnecessarily held in the pending queue. - */ -#define MESH_PATH_REFRESH_TIME 1000 -#define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME) - -/* Default maximum number of established plinks per interface */ -#define MESH_MAX_ESTAB_PLINKS 32 - -#define MESH_MAX_PREQ_RETRIES 4 - - -const struct mesh_config default_mesh_config = { - .dot11MeshRetryTimeout = MESH_RET_T, - .dot11MeshConfirmTimeout = MESH_CONF_T, - .dot11MeshHoldingTimeout = MESH_HOLD_T, - .dot11MeshMaxRetries = MESH_MAX_RETR, - .dot11MeshTTL = MESH_TTL, - .element_ttl = MESH_DEFAULT_ELEMENT_TTL, - .auto_open_plinks = true, - .dot11MeshMaxPeerLinks = MESH_MAX_ESTAB_PLINKS, - .dot11MeshHWMPactivePathTimeout = MESH_PATH_TIMEOUT, - .dot11MeshHWMPpreqMinInterval = MESH_PREQ_MIN_INT, - .dot11MeshHWMPnetDiameterTraversalTime = MESH_DIAM_TRAVERSAL_TIME, - .dot11MeshHWMPmaxPREQretries = MESH_MAX_PREQ_RETRIES, - .path_refresh_time = MESH_PATH_REFRESH_TIME, - .min_discovery_timeout = MESH_MIN_DISCOVERY_TIMEOUT, - .dot11MeshHWMPRannInterval = MESH_RANN_INTERVAL, - .dot11MeshGateAnnouncementProtocol = false, -}; - -const struct mesh_setup default_mesh_setup = { - .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, - .path_metric = IEEE80211_PATH_METRIC_AIRTIME, - .ie = NULL, - .ie_len = 0, - .is_secure = false, -}; - -int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev, - const struct mesh_setup *setup, - const struct mesh_config *conf) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); - - ASSERT_WDEV_LOCK(wdev); - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) && - setup->is_secure) - return -EOPNOTSUPP; - - if (wdev->mesh_id_len) - return -EALREADY; - - if (!setup->mesh_id_len) - return -EINVAL; - - if (!rdev->ops->join_mesh) - return -EOPNOTSUPP; - - err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup); - if (!err) { - memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); - wdev->mesh_id_len = setup->mesh_id_len; - } - - return err; -} - -int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev, - const struct mesh_setup *setup, - const struct mesh_config *conf) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_join_mesh(rdev, dev, setup, conf); - wdev_unlock(wdev); - - return err; -} - -void cfg80211_notify_new_peer_candidate(struct net_device *dev, - const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT)) - return; - - nl80211_send_new_peer_candidate(wiphy_to_dev(wdev->wiphy), dev, - macaddr, ie, ie_len, gfp); -} -EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate); - -static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - ASSERT_WDEV_LOCK(wdev); - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - if (!rdev->ops->leave_mesh) - return -EOPNOTSUPP; - - if (!wdev->mesh_id_len) - return -ENOTCONN; - - err = rdev->ops->leave_mesh(&rdev->wiphy, dev); - if (!err) - wdev->mesh_id_len = 0; - return err; -} - -int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_leave_mesh(rdev, dev); - wdev_unlock(wdev); - - return err; -} diff --git a/net/wireless_ath/mlme.c b/net/wireless_ath/mlme.c deleted file mode 100755 index 6c1bafd..0000000 --- a/net/wireless_ath/mlme.c +++ /dev/null @@ -1,1140 +0,0 @@ -/* - * cfg80211 MLME SAP interface - * - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/nl80211.h> -#include <linux/slab.h> -#include <linux/wireless.h> -#include <net/cfg80211.h> -#include <net/iw_handler.h> -#include "core.h" -#include "nl80211.h" - -void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - u8 *bssid = mgmt->bssid; - int i; - u16 status = le16_to_cpu(mgmt->u.auth.status_code); - bool done = false; - - wdev_lock(wdev); - - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->authtry_bsses[i] && - memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, - ETH_ALEN) == 0) { - if (status == WLAN_STATUS_SUCCESS) { - wdev->auth_bsses[i] = wdev->authtry_bsses[i]; - } else { - cfg80211_unhold_bss(wdev->authtry_bsses[i]); - cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); - } - wdev->authtry_bsses[i] = NULL; - done = true; - break; - } - } - - if (done) { - nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); - cfg80211_sme_rx_auth(dev, buf, len); - } - - wdev_unlock(wdev); -} -EXPORT_SYMBOL(cfg80211_send_rx_auth); - -void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) -{ - u16 status_code; - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - u8 *ie = mgmt->u.assoc_resp.variable; - int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); - struct cfg80211_internal_bss *bss = NULL; - - wdev_lock(wdev); - - status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); - - /* - * This is a bit of a hack, we don't notify userspace of - * a (re-)association reply if we tried to send a reassoc - * and got a reject -- we only try again with an assoc - * frame instead of reassoc. - */ - if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && - cfg80211_sme_failed_reassoc(wdev)) - goto out; - - nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); - - if (status_code == WLAN_STATUS_SUCCESS) { - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (!wdev->auth_bsses[i]) - continue; - if (memcmp(wdev->auth_bsses[i]->pub.bssid, mgmt->bssid, - ETH_ALEN) == 0) { - bss = wdev->auth_bsses[i]; - wdev->auth_bsses[i] = NULL; - /* additional reference to drop hold */ - cfg80211_ref_bss(bss); - break; - } - } - - /* - * We might be coming here because the driver reported - * a successful association at the same time as the - * user requested a deauth. In that case, we will have - * removed the BSS from the auth_bsses list due to the - * deauth request when the assoc response makes it. If - * the two code paths acquire the lock the other way - * around, that's just the standard situation of a - * deauth being requested while connected. - */ - if (!bss) - goto out; - } else if (wdev->conn) { - cfg80211_sme_failed_assoc(wdev); - /* - * do not call connect_result() now because the - * sme will schedule work that does it later. - */ - goto out; - } - - if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) { - /* - * This is for the userspace SME, the CONNECTING - * state will be changed to CONNECTED by - * __cfg80211_connect_result() below. - */ - wdev->sme_state = CFG80211_SME_CONNECTING; - } - - /* this consumes one bss reference (unless bss is NULL) */ - __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, - status_code, - status_code == WLAN_STATUS_SUCCESS, - bss ? &bss->pub : NULL); - /* drop hold now, and also reference acquired above */ - if (bss) { - cfg80211_unhold_bss(bss); - cfg80211_put_bss(&bss->pub); - } - - out: - wdev_unlock(wdev); -} -EXPORT_SYMBOL(cfg80211_send_rx_assoc); - -void __cfg80211_send_deauth(struct net_device *dev, - const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - const u8 *bssid = mgmt->bssid; - int i; - bool found = false, was_current = false; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->current_bss && - memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - wdev->current_bss = NULL; - found = true; - was_current = true; - } else for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i] && - memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->auth_bsses[i]); - cfg80211_put_bss(&wdev->auth_bsses[i]->pub); - wdev->auth_bsses[i] = NULL; - found = true; - break; - } - if (wdev->authtry_bsses[i] && - memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, - ETH_ALEN) == 0 && - memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->authtry_bsses[i]); - cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); - wdev->authtry_bsses[i] = NULL; - found = true; - break; - } - } - - if (!found) - return; - - nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); - - if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) { - u16 reason_code; - bool from_ap; - - reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); - - from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0; - __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); - } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { - __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - false, NULL); - } -} -EXPORT_SYMBOL(__cfg80211_send_deauth); - -void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - __cfg80211_send_deauth(dev, buf, len); - wdev_unlock(wdev); -} -EXPORT_SYMBOL(cfg80211_send_deauth); - -void __cfg80211_send_disassoc(struct net_device *dev, - const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - const u8 *bssid = mgmt->bssid; - int i; - u16 reason_code; - bool from_ap; - bool done = false; - - ASSERT_WDEV_LOCK(wdev); - - nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); - - if (wdev->sme_state != CFG80211_SME_CONNECTED) - return; - - if (wdev->current_bss && - memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->authtry_bsses[i] || wdev->auth_bsses[i]) - continue; - wdev->auth_bsses[i] = wdev->current_bss; - wdev->current_bss = NULL; - done = true; - cfg80211_sme_disassoc(dev, i); - break; - } - WARN_ON(!done); - } else - WARN_ON(1); - - - reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); - - from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0; - __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); -} -EXPORT_SYMBOL(__cfg80211_send_disassoc); - -void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - __cfg80211_send_disassoc(dev, buf, len); - wdev_unlock(wdev); -} -EXPORT_SYMBOL(cfg80211_send_disassoc); - -void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf, - size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC); -} -EXPORT_SYMBOL(cfg80211_send_unprot_deauth); - -void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf, - size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC); -} -EXPORT_SYMBOL(cfg80211_send_unprot_disassoc); - -static void __cfg80211_auth_remove(struct wireless_dev *wdev, const u8 *addr) -{ - int i; - bool done = false; - - ASSERT_WDEV_LOCK(wdev); - - for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { - if (wdev->authtry_bsses[i] && - memcmp(wdev->authtry_bsses[i]->pub.bssid, - addr, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->authtry_bsses[i]); - cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); - wdev->authtry_bsses[i] = NULL; - done = true; - break; - } - } - - WARN_ON(!done); -} - -void __cfg80211_auth_canceled(struct net_device *dev, const u8 *addr) -{ - __cfg80211_auth_remove(dev->ieee80211_ptr, addr); -} -EXPORT_SYMBOL(__cfg80211_auth_canceled); - -void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - wdev_lock(wdev); - - nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); - if (wdev->sme_state == CFG80211_SME_CONNECTING) - __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - false, NULL); - - __cfg80211_auth_remove(wdev, addr); - - wdev_unlock(wdev); -} -EXPORT_SYMBOL(cfg80211_send_auth_timeout); - -void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - int i; - bool done = false; - - wdev_lock(wdev); - - nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); - if (wdev->sme_state == CFG80211_SME_CONNECTING) - __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - false, NULL); - - for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i] && - memcmp(wdev->auth_bsses[i]->pub.bssid, - addr, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->auth_bsses[i]); - cfg80211_put_bss(&wdev->auth_bsses[i]->pub); - wdev->auth_bsses[i] = NULL; - done = true; - break; - } - } - - WARN_ON(!done); - - wdev_unlock(wdev); -} -EXPORT_SYMBOL(cfg80211_send_assoc_timeout); - -void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, - enum nl80211_key_type key_type, int key_id, - const u8 *tsc, gfp_t gfp) -{ - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); -#ifdef CONFIG_CFG80211_WEXT - union iwreq_data wrqu; - char *buf = kmalloc(128, gfp); - - if (buf) { - sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" - "keyid=%d %scast addr=%pM)", key_id, - key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni", - addr); - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = strlen(buf); - wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); - kfree(buf); - } -#endif - - nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp); -} -EXPORT_SYMBOL(cfg80211_michael_mic_failure); - -/* some MLME handling for userspace SME */ -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, - bool local_state_change) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_auth_request req; - struct cfg80211_internal_bss *bss; - int i, err, slot = -1, nfree = 0; - - ASSERT_WDEV_LOCK(wdev); - - if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) - if (!key || !key_len || key_idx < 0 || key_idx > 4) - return -EINVAL; - - if (wdev->current_bss && - memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0) - return -EALREADY; - - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->authtry_bsses[i] && - memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, - ETH_ALEN) == 0) - return -EALREADY; - if (wdev->auth_bsses[i] && - memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, - ETH_ALEN) == 0) - return -EALREADY; - } - - memset(&req, 0, sizeof(req)); - - req.local_state_change = local_state_change; - req.ie = ie; - req.ie_len = ie_len; - req.auth_type = auth_type; - req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, - WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); - req.key = key; - req.key_len = key_len; - req.key_idx = key_idx; - if (!req.bss) - return -ENOENT; - - bss = bss_from_pub(req.bss); - - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) { - slot = i; - nfree++; - } - } - - /* we need one free slot for disassoc and one for this auth */ - if (nfree < 2) { - err = -ENOSPC; - goto out; - } - - if (local_state_change) - wdev->auth_bsses[slot] = bss; - else - wdev->authtry_bsses[slot] = bss; - cfg80211_hold_bss(bss); - - err = rdev->ops->auth(&rdev->wiphy, dev, &req); - if (err) { - if (local_state_change) - wdev->auth_bsses[slot] = NULL; - else - wdev->authtry_bsses[slot] = NULL; - cfg80211_unhold_bss(bss); - } - - out: - if (err) - cfg80211_put_bss(req.bss); - return err; -} - -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, - bool local_state_change) -{ - int err; - - 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, local_state_change); - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - -int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, - const u8 *bssid, const u8 *prev_bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, bool use_mfp, - struct cfg80211_crypto_settings *crypt) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_assoc_request req; - struct cfg80211_internal_bss *bss; - int i, err, slot = -1; - bool was_connected = false; - - ASSERT_WDEV_LOCK(wdev); - - memset(&req, 0, sizeof(req)); - - if (wdev->current_bss && prev_bssid && - memcmp(wdev->current_bss->pub.bssid, prev_bssid, ETH_ALEN) == 0) { - /* - * Trying to reassociate: Allow this to proceed and let the old - * association to be dropped when the new one is completed. - */ - if (wdev->sme_state == CFG80211_SME_CONNECTED) { - was_connected = true; - wdev->sme_state = CFG80211_SME_CONNECTING; - } - } else if (wdev->current_bss) - return -EALREADY; - - req.ie = ie; - req.ie_len = ie_len; - memcpy(&req.crypto, crypt, sizeof(req.crypto)); - req.use_mfp = use_mfp; - req.prev_bssid = prev_bssid; - req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, - WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); - if (!req.bss) { - if (was_connected) - wdev->sme_state = CFG80211_SME_CONNECTED; - return -ENOENT; - } - - bss = bss_from_pub(req.bss); - - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (bss == wdev->auth_bsses[i]) { - slot = i; - break; - } - } - - if (slot < 0) { - err = -ENOTCONN; - goto out; - } - - err = rdev->ops->assoc(&rdev->wiphy, dev, &req); - out: - if (err && was_connected) - wdev->sme_state = CFG80211_SME_CONNECTED; - /* still a reference in wdev->auth_bsses[slot] */ - cfg80211_put_bss(req.bss); - return err; -} - -int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, - const u8 *bssid, const u8 *prev_bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, bool use_mfp, - struct cfg80211_crypto_settings *crypt) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, - ssid, ssid_len, ie, ie_len, use_mfp, crypt); - wdev_unlock(wdev); - - return err; -} - -int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_deauth_request req; - int i; - - ASSERT_WDEV_LOCK(wdev); - - memset(&req, 0, sizeof(req)); - req.reason_code = reason; - req.local_state_change = local_state_change; - req.ie = ie; - req.ie_len = ie_len; - if (wdev->current_bss && - memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { - req.bss = &wdev->current_bss->pub; - } else for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i] && - memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) { - req.bss = &wdev->auth_bsses[i]->pub; - break; - } - if (wdev->authtry_bsses[i] && - memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) { - req.bss = &wdev->authtry_bsses[i]->pub; - break; - } - } - - if (!req.bss) - return -ENOTCONN; - - return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); -} - -int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason, - local_state_change); - wdev_unlock(wdev); - - return err; -} - -static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_disassoc_request req; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->sme_state != CFG80211_SME_CONNECTED) - return -ENOTCONN; - - if (WARN_ON(!wdev->current_bss)) - return -ENOTCONN; - - memset(&req, 0, sizeof(req)); - req.reason_code = reason; - req.local_state_change = local_state_change; - req.ie = ie; - req.ie_len = ie_len; - if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) - req.bss = &wdev->current_bss->pub; - else - return -ENOTCONN; - - return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev); -} - -int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason, - local_state_change); - wdev_unlock(wdev); - - return err; -} - -void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, - struct net_device *dev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_deauth_request req; - int i; - - ASSERT_WDEV_LOCK(wdev); - - if (!rdev->ops->deauth) - return; - - memset(&req, 0, sizeof(req)); - req.reason_code = WLAN_REASON_DEAUTH_LEAVING; - req.ie = NULL; - req.ie_len = 0; - - if (wdev->current_bss) { - req.bss = &wdev->current_bss->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); - if (wdev->current_bss) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - wdev->current_bss = NULL; - } - } - - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i]) { - req.bss = &wdev->auth_bsses[i]->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); - if (wdev->auth_bsses[i]) { - cfg80211_unhold_bss(wdev->auth_bsses[i]); - cfg80211_put_bss(&wdev->auth_bsses[i]->pub); - wdev->auth_bsses[i] = NULL; - } - } - if (wdev->authtry_bsses[i]) { - req.bss = &wdev->authtry_bsses[i]->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); - if (wdev->authtry_bsses[i]) { - cfg80211_unhold_bss(wdev->authtry_bsses[i]); - cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); - wdev->authtry_bsses[i] = NULL; - } - } - } -} - -void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - unsigned int duration, gfp_t gfp) -{ - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type, - duration, gfp); -} -EXPORT_SYMBOL(cfg80211_ready_on_channel); - -void cfg80211_remain_on_channel_expired(struct net_device *dev, - u64 cookie, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - gfp_t gfp) -{ - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan, - channel_type, gfp); -} -EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); - -void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, - struct station_info *sinfo, gfp_t gfp) -{ - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp); -} -EXPORT_SYMBOL(cfg80211_new_sta); - -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); - - nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp); -} -EXPORT_SYMBOL(cfg80211_del_sta); - -struct cfg80211_mgmt_registration { - struct list_head list; - - u32 nlpid; - - int match_len; - - __le16 frame_type; - - u8 match[]; -}; - -int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, - u16 frame_type, const u8 *match_data, - int match_len) -{ - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct cfg80211_mgmt_registration *reg, *nreg; - int err = 0; - u16 mgmt_type; - - if (!wdev->wiphy->mgmt_stypes) - return -EOPNOTSUPP; - - if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT) - return -EINVAL; - - if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) - return -EINVAL; - - mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; - if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type))) - return -EINVAL; - - nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL); - if (!nreg) - return -ENOMEM; - - spin_lock_bh(&wdev->mgmt_registrations_lock); - - list_for_each_entry(reg, &wdev->mgmt_registrations, list) { - int mlen = min(match_len, reg->match_len); - - if (frame_type != le16_to_cpu(reg->frame_type)) - continue; - - if (memcmp(reg->match, match_data, mlen) == 0) { - err = -EALREADY; - break; - } - } - - if (err) { - kfree(nreg); - goto out; - } - - memcpy(nreg->match, match_data, match_len); - nreg->match_len = match_len; - nreg->nlpid = snd_pid; - nreg->frame_type = cpu_to_le16(frame_type); - list_add(&nreg->list, &wdev->mgmt_registrations); - - if (rdev->ops->mgmt_frame_register) - rdev->ops->mgmt_frame_register(wiphy, wdev->netdev, - frame_type, true); - - out: - spin_unlock_bh(&wdev->mgmt_registrations_lock); - - return err; -} - -void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid) -{ - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct cfg80211_mgmt_registration *reg, *tmp; - - spin_lock_bh(&wdev->mgmt_registrations_lock); - - list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { - if (reg->nlpid != nlpid) - continue; - - if (rdev->ops->mgmt_frame_register) { - u16 frame_type = le16_to_cpu(reg->frame_type); - - rdev->ops->mgmt_frame_register(wiphy, wdev->netdev, - frame_type, false); - } - - list_del(®->list); - kfree(reg); - } - - spin_unlock_bh(&wdev->mgmt_registrations_lock); - - if (nlpid == wdev->ap_unexpected_nlpid) - wdev->ap_unexpected_nlpid = 0; -} - -void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) -{ - struct cfg80211_mgmt_registration *reg, *tmp; - - spin_lock_bh(&wdev->mgmt_registrations_lock); - - list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { - list_del(®->list); - kfree(reg); - } - - spin_unlock_bh(&wdev->mgmt_registrations_lock); -} - -int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, - struct net_device *dev, - 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) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - const struct ieee80211_mgmt *mgmt; - u16 stype; - - if (!wdev->wiphy->mgmt_stypes) - return -EOPNOTSUPP; - - if (!rdev->ops->mgmt_tx) - return -EOPNOTSUPP; - - if (len < 24 + 1) - return -EINVAL; - - mgmt = (const struct ieee80211_mgmt *) buf; - - if (!ieee80211_is_mgmt(mgmt->frame_control)) - return -EINVAL; - - stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; - if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4))) - return -EINVAL; - - if (ieee80211_is_action(mgmt->frame_control) && - mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) { - int err = 0; - - wdev_lock(wdev); - - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - if (!wdev->current_bss) { - err = -ENOTCONN; - break; - } - - if (memcmp(wdev->current_bss->pub.bssid, - mgmt->bssid, ETH_ALEN)) { - err = -ENOTCONN; - break; - } - - /* - * check for IBSS DA must be done by driver as - * cfg80211 doesn't track the stations - */ - if (wdev->iftype == NL80211_IFTYPE_ADHOC) - break; - - /* for station, check that DA is the AP */ - if (memcmp(wdev->current_bss->pub.bssid, - mgmt->da, ETH_ALEN)) { - err = -ENOTCONN; - break; - } - break; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_AP_VLAN: - if (memcmp(mgmt->bssid, dev->dev_addr, ETH_ALEN)) - err = -EINVAL; - break; - case NL80211_IFTYPE_MESH_POINT: - if (memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN)) { - err = -EINVAL; - break; - } - /* - * check for mesh DA must be done by driver as - * cfg80211 doesn't track the stations - */ - break; - default: - err = -EOPNOTSUPP; - break; - } - wdev_unlock(wdev); - - if (err) - return err; - } - - if (memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0) - return -EINVAL; - - /* Transmit the Action frame as requested by user space */ - return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, offchan, - channel_type, channel_type_valid, - wait, buf, len, no_cck, dont_wait_for_ack, - cookie); -} - -bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, - size_t len, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct cfg80211_mgmt_registration *reg; - const struct ieee80211_txrx_stypes *stypes = - &wiphy->mgmt_stypes[wdev->iftype]; - struct ieee80211_mgmt *mgmt = (void *)buf; - const u8 *data; - int data_len; - bool result = false; - __le16 ftype = mgmt->frame_control & - cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE); - u16 stype; - - stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4; - - if (!(stypes->rx & BIT(stype))) - return false; - - data = buf + ieee80211_hdrlen(mgmt->frame_control); - data_len = len - ieee80211_hdrlen(mgmt->frame_control); - - spin_lock_bh(&wdev->mgmt_registrations_lock); - - list_for_each_entry(reg, &wdev->mgmt_registrations, list) { - if (reg->frame_type != ftype) - continue; - - if (reg->match_len > data_len) - continue; - - if (memcmp(reg->match, data, reg->match_len)) - continue; - - /* found match! */ - - /* Indicate the received Action frame to user space */ - if (nl80211_send_mgmt(rdev, dev, reg->nlpid, freq, - buf, len, gfp)) - continue; - - result = true; - break; - } - - spin_unlock_bh(&wdev->mgmt_registrations_lock); - - return result; -} -EXPORT_SYMBOL(cfg80211_rx_mgmt); - -void cfg80211_mgmt_tx_status(struct net_device *dev, u64 cookie, - const u8 *buf, size_t len, bool ack, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - /* Indicate TX status of the Action frame to user space */ - nl80211_send_mgmt_tx_status(rdev, dev, cookie, buf, len, ack, gfp); -} -EXPORT_SYMBOL(cfg80211_mgmt_tx_status); - -void cfg80211_cqm_rssi_notify(struct net_device *dev, - enum nl80211_cqm_rssi_threshold_event rssi_event, - gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - /* Indicate roaming trigger event to user space */ - nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp); -} -EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); - -void cfg80211_cqm_pktloss_notify(struct net_device *dev, - const u8 *peer, u32 num_packets, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - /* Indicate roaming trigger event to user space */ - nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp); -} -EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); - -void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, - const u8 *replay_ctr, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp); -} -EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); - -void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, - const u8 *bssid, bool preauth, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); -} -EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); - -bool cfg80211_rx_spurious_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO)) - return false; - - return nl80211_unexpected_frame(dev, addr, gfp); -} -EXPORT_SYMBOL(cfg80211_rx_spurious_frame); - -bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO && - wdev->iftype != NL80211_IFTYPE_AP_VLAN)) - return false; - - return nl80211_unexpected_4addr_frame(dev, addr, gfp); -} -EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); diff --git a/net/wireless_ath/nl80211.c b/net/wireless_ath/nl80211.c deleted file mode 100755 index b11cabe..0000000 --- a/net/wireless_ath/nl80211.c +++ /dev/null @@ -1,8092 +0,0 @@ -/* - * This is the new netlink-based wireless configuration interface. - * - * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> - */ - -#include <linux/if.h> -#include <linux/module.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <linux/list.h> -#include <linux/if_ether.h> -#include <linux/ieee80211.h> -#include <linux/nl80211.h> -#include <linux/rtnetlink.h> -#include <linux/netlink.h> -#include <linux/etherdevice.h> -#include <net/net_namespace.h> -#include <net/genetlink.h> -#include <net/cfg80211.h> -#include <net/sock.h> -#include "core.h" -#include "nl80211.h" -#include "reg.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, - int cipher_limit); - -static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info); -static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info); - -/* the netlink family */ -static struct genl_family nl80211_fam = { - .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ - .name = "nl80211", /* have users key off the name instead */ - .hdrsize = 0, /* no private header */ - .version = 1, /* no particular meaning now */ - .maxattr = NL80211_ATTR_MAX, - .netnsok = true, - .pre_doit = nl80211_pre_doit, - .post_doit = nl80211_post_doit, -}; - -/* internal helper: get rdev and dev */ -static int get_rdev_dev_by_info_ifindex(struct genl_info *info, - struct cfg80211_registered_device **rdev, - struct net_device **dev) -{ - struct nlattr **attrs = info->attrs; - int ifindex; - - if (!attrs[NL80211_ATTR_IFINDEX]) - return -EINVAL; - - ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); - *dev = dev_get_by_index(genl_info_net(info), ifindex); - if (!*dev) - return -ENODEV; - - *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex); - if (IS_ERR(*rdev)) { - dev_put(*dev); - return PTR_ERR(*rdev); - } - - return 0; -} - -/* policy for the attributes */ -static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { - [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, - .len = 20-1 }, - [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, - [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 }, - [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, - [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 }, - - [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, - [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, - [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, - - [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN }, - [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN }, - - [NL80211_ATTR_KEY] = { .type = NLA_NESTED, }, - [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, - .len = WLAN_MAX_KEY_LEN }, - [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 }, - [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 }, - [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG }, - [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, - [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 }, - - [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 }, - [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 }, - [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_STA_AID] = { .type = NLA_U16 }, - [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED }, - [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 }, - [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY, - .len = NL80211_MAX_SUPP_RATES }, - [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 }, - [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, - [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ }, - [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_MESH_ID_LEN }, - [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, - - [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 }, - [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED }, - - [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, - [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, - [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, - [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY, - .len = NL80211_MAX_SUPP_RATES }, - [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 }, - - [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED }, - [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG }, - - [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN }, - - [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, - [NL80211_ATTR_IE] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED }, - [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED }, - - [NL80211_ATTR_SSID] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_SSID_LEN }, - [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 }, - [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 }, - [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG }, - [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG }, - [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 }, - [NL80211_ATTR_STA_FLAGS2] = { - .len = sizeof(struct nl80211_sta_flag_update), - }, - [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG }, - [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 }, - [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG }, - [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, - [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, - [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, - [NL80211_ATTR_PID] = { .type = NLA_U32 }, - [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, - [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, - .len = WLAN_PMKID_LEN }, - [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, - [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, - [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, - [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, - [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, - [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, - [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG }, - [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 }, - [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 }, - [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 }, - [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 }, - [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, - [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, - [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, - [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, - [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 }, - [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 }, - [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED }, - [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED }, - [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 }, - [NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG }, - [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED }, - [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG }, - [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 }, - [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 }, - [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 }, - [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG }, - [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG }, - [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG }, - [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - - [NL80211_ATTR_BTCOEX_INQ_STATUS] = { .type = NLA_FLAG }, - [NL80211_ATTR_BTCOEX_SCO_STATUS] = { .type = NLA_FLAG }, - [NL80211_ATTR_BTCOEX_TYPE_ESCO] = { .type = NLA_FLAG }, - [NL80211_ATTR_BTCOEX_ESCO_TX_INTERVAL] = { .type = NLA_U32 }, - [NL80211_ATTR_BTCOEX_ESCO_TX_PKT_LEN] = { .type = NLA_U32 }, - [NL80211_ATTR_BTCOEX_A2DP_STATUS] = { .type = NLA_FLAG }, - [NL80211_ATTR_BTCOEX_ACL_ROLE] = { .type = NLA_U32 }, - [NL80211_ATTR_BTCOEX_REMOTE_LMP_VER] = { .type = NLA_U32 }, - - [NL80211_ATTR_BTCOEX_ANTENNA_CONFIG] = { .type = NLA_U32 }, - [NL80211_ATTR_BT_VENDOR_ID] = { .type = NLA_U32 }, - [NL80211_ATTR_BTCOEX_DATA] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_PRIV_CMD] = { .type = NLA_NUL_STRING, .len = 128 }, - [NL80211_ATTR_PRIV_EVENT] = { .type = NLA_NUL_STRING, .len = 128 }, - -}; - -/* policy for the key attributes */ -static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { - [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, - [NL80211_KEY_IDX] = { .type = NLA_U8 }, - [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, - [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, - [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG }, - [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, - [NL80211_KEY_TYPE] = { .type = NLA_U32 }, - [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, -}; - -/* policy for the key default flags */ -static const struct nla_policy -nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = { - [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG }, - [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG }, -}; - -/* policy for WoWLAN attributes */ -static const struct nla_policy -nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { - [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG }, - [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG }, - [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG }, - [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, - [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG }, - [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG }, - [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, - [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, -}; - -/* policy for GTK rekey offload attributes */ -static const struct nla_policy -nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { - [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN }, - [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN }, - [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN }, -}; - -static const struct nla_policy -nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = { - [NL80211_ATTR_SCHED_SCAN_MATCH_SSID] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_SSID_LEN }, -}; - -/* ifidx get helper */ -static int nl80211_get_ifidx(struct netlink_callback *cb) -{ - int res; - - res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, - nl80211_fam.attrbuf, nl80211_fam.maxattr, - nl80211_policy); - if (res) - return res; - - if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) - return -EINVAL; - - res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); - if (!res) - return -EINVAL; - return res; -} - -static int nl80211_prepare_netdev_dump(struct sk_buff *skb, - struct netlink_callback *cb, - struct cfg80211_registered_device **rdev, - struct net_device **dev) -{ - int ifidx = cb->args[0]; - int err; - - if (!ifidx) - ifidx = nl80211_get_ifidx(cb); - if (ifidx < 0) - return ifidx; - - cb->args[0] = ifidx; - - rtnl_lock(); - - *dev = __dev_get_by_index(sock_net(skb->sk), ifidx); - if (!*dev) { - err = -ENODEV; - goto out_rtnl; - } - - *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); - if (IS_ERR(*rdev)) { - err = PTR_ERR(*rdev); - goto out_rtnl; - } - - return 0; - out_rtnl: - rtnl_unlock(); - return err; -} - -static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev) -{ - cfg80211_unlock_rdev(rdev); - rtnl_unlock(); -} - -/* IE validation */ -static bool is_valid_ie_attr(const struct nlattr *attr) -{ - const u8 *pos; - int len; - - if (!attr) - return true; - - pos = nla_data(attr); - len = nla_len(attr); - - while (len) { - u8 elemlen; - - if (len < 2) - return false; - len -= 2; - - elemlen = pos[1]; - if (elemlen > len) - return false; - - len -= elemlen; - pos += 2 + elemlen; - } - - return true; -} - -/* message building helper */ -static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq, - int flags, u8 cmd) -{ - /* since there is no private header just add the generic one */ - return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd); -} - -static int nl80211_msg_put_channel(struct sk_buff *msg, - struct ieee80211_channel *chan) -{ - NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ, - chan->center_freq); - - if (chan->flags & IEEE80211_CHAN_DISABLED) - NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED); - if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) - NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN); - if (chan->flags & IEEE80211_CHAN_NO_IBSS) - NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS); - if (chan->flags & IEEE80211_CHAN_RADAR) - NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR); - - NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, - DBM_TO_MBM(chan->max_power)); - - return 0; - - nla_put_failure: - return -ENOBUFS; -} - -/* netlink command implementations */ - -struct key_parse { - struct key_params p; - int idx; - int type; - bool def, defmgmt; - bool def_uni, def_multi; -}; - -static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k) -{ - struct nlattr *tb[NL80211_KEY_MAX + 1]; - int err = nla_parse_nested(tb, NL80211_KEY_MAX, key, - nl80211_key_policy); - if (err) - return err; - - k->def = !!tb[NL80211_KEY_DEFAULT]; - k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT]; - - if (k->def) { - k->def_uni = true; - k->def_multi = true; - } - if (k->defmgmt) - k->def_multi = true; - - if (tb[NL80211_KEY_IDX]) - k->idx = nla_get_u8(tb[NL80211_KEY_IDX]); - - if (tb[NL80211_KEY_DATA]) { - k->p.key = nla_data(tb[NL80211_KEY_DATA]); - k->p.key_len = nla_len(tb[NL80211_KEY_DATA]); - } - - if (tb[NL80211_KEY_SEQ]) { - k->p.seq = nla_data(tb[NL80211_KEY_SEQ]); - k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]); - } - - if (tb[NL80211_KEY_CIPHER]) - k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]); - - if (tb[NL80211_KEY_TYPE]) { - k->type = nla_get_u32(tb[NL80211_KEY_TYPE]); - if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES) - return -EINVAL; - } - - if (tb[NL80211_KEY_DEFAULT_TYPES]) { - struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; - int err = nla_parse_nested(kdt, - NUM_NL80211_KEY_DEFAULT_TYPES - 1, - tb[NL80211_KEY_DEFAULT_TYPES], - nl80211_key_default_policy); - if (err) - return err; - - k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; - k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; - } - - return 0; -} - -static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k) -{ - if (info->attrs[NL80211_ATTR_KEY_DATA]) { - k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]); - k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]); - } - - if (info->attrs[NL80211_ATTR_KEY_SEQ]) { - k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]); - k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]); - } - - if (info->attrs[NL80211_ATTR_KEY_IDX]) - k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - - if (info->attrs[NL80211_ATTR_KEY_CIPHER]) - k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]); - - k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT]; - k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]; - - if (k->def) { - k->def_uni = true; - k->def_multi = true; - } - if (k->defmgmt) - k->def_multi = true; - - if (info->attrs[NL80211_ATTR_KEY_TYPE]) { - k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]); - if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) { - struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; - int err = nla_parse_nested( - kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1, - info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES], - nl80211_key_default_policy); - if (err) - return err; - - k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; - k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; - } - - return 0; -} - -static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) -{ - int err; - - memset(k, 0, sizeof(*k)); - k->idx = -1; - k->type = -1; - - if (info->attrs[NL80211_ATTR_KEY]) - err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k); - else - err = nl80211_parse_key_old(info, k); - - if (err) - return err; - - if (k->def && k->defmgmt) - return -EINVAL; - - if (k->defmgmt) { - if (k->def_uni || !k->def_multi) - return -EINVAL; - } - - if (k->idx != -1) { - if (k->defmgmt) { - if (k->idx < 4 || k->idx > 5) - return -EINVAL; - } else if (k->def) { - if (k->idx < 0 || k->idx > 3) - return -EINVAL; - } else { - if (k->idx < 0 || k->idx > 5) - return -EINVAL; - } - } - - return 0; -} - -static struct cfg80211_cached_keys * -nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, - struct nlattr *keys) -{ - struct key_parse parse; - struct nlattr *key; - struct cfg80211_cached_keys *result; - int rem, err, def = 0; - - result = kzalloc(sizeof(*result), GFP_KERNEL); - if (!result) - return ERR_PTR(-ENOMEM); - - result->def = -1; - result->defmgmt = -1; - - nla_for_each_nested(key, keys, rem) { - memset(&parse, 0, sizeof(parse)); - parse.idx = -1; - - err = nl80211_parse_key_new(key, &parse); - if (err) - goto error; - err = -EINVAL; - if (!parse.p.key) - goto error; - if (parse.idx < 0 || parse.idx > 4) - goto error; - if (parse.def) { - if (def) - goto error; - def = 1; - result->def = parse.idx; - if (!parse.def_uni || !parse.def_multi) - goto error; - } else if (parse.defmgmt) - goto error; - err = cfg80211_validate_key_settings(rdev, &parse.p, - parse.idx, false, NULL); - if (err) - goto error; - result->params[parse.idx].cipher = parse.p.cipher; - 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); - } - - return result; - error: - kfree(result); - return ERR_PTR(err); -} - -static int nl80211_key_allowed(struct wireless_dev *wdev) -{ - ASSERT_WDEV_LOCK(wdev); - - switch (wdev->iftype) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_MESH_POINT: - break; - case NL80211_IFTYPE_ADHOC: - if (!wdev->current_bss) - return -ENOLINK; - break; - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - if (wdev->sme_state != CFG80211_SME_CONNECTED) - return -ENOLINK; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes) -{ - struct nlattr *nl_modes = nla_nest_start(msg, attr); - int i; - - if (!nl_modes) - goto nla_put_failure; - - i = 0; - while (ifmodes) { - if (ifmodes & 1) - NLA_PUT_FLAG(msg, i); - ifmodes >>= 1; - i++; - } - - nla_nest_end(msg, nl_modes); - return 0; - -nla_put_failure: - return -ENOBUFS; -} - -static int nl80211_put_iface_combinations(struct wiphy *wiphy, - struct sk_buff *msg) -{ - struct nlattr *nl_combis; - int i, j; - - nl_combis = nla_nest_start(msg, - NL80211_ATTR_INTERFACE_COMBINATIONS); - if (!nl_combis) - goto nla_put_failure; - - for (i = 0; i < wiphy->n_iface_combinations; i++) { - const struct ieee80211_iface_combination *c; - struct nlattr *nl_combi, *nl_limits; - - c = &wiphy->iface_combinations[i]; - - nl_combi = nla_nest_start(msg, i + 1); - if (!nl_combi) - goto nla_put_failure; - - nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS); - if (!nl_limits) - goto nla_put_failure; - - for (j = 0; j < c->n_limits; j++) { - struct nlattr *nl_limit; - - nl_limit = nla_nest_start(msg, j + 1); - if (!nl_limit) - goto nla_put_failure; - NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX, - c->limits[j].max); - if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES, - c->limits[j].types)) - goto nla_put_failure; - nla_nest_end(msg, nl_limit); - } - - nla_nest_end(msg, nl_limits); - - if (c->beacon_int_infra_match) - NLA_PUT_FLAG(msg, - NL80211_IFACE_COMB_STA_AP_BI_MATCH); - NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS, - c->num_different_channels); - NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM, - c->max_interfaces); - - nla_nest_end(msg, nl_combi); - } - - nla_nest_end(msg, nl_combis); - - return 0; -nla_put_failure: - return -ENOBUFS; -} - -static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, - struct cfg80211_registered_device *dev) -{ - void *hdr; - struct nlattr *nl_bands, *nl_band; - struct nlattr *nl_freqs, *nl_freq; - struct nlattr *nl_rates, *nl_rate; - struct nlattr *nl_cmds; - enum ieee80211_band band; - struct ieee80211_channel *chan; - struct ieee80211_rate *rate; - int i; - const struct ieee80211_txrx_stypes *mgmt_stypes = - dev->wiphy.mgmt_stypes; - - hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY); - if (!hdr) - return -1; - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx); - NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); - - NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, - cfg80211_rdev_list_generation); - - NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, - dev->wiphy.retry_short); - NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, - dev->wiphy.retry_long); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, - dev->wiphy.frag_threshold); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, - dev->wiphy.rts_threshold); - NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, - dev->wiphy.coverage_class); - NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, - dev->wiphy.max_scan_ssids); - NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, - dev->wiphy.max_sched_scan_ssids); - NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, - dev->wiphy.max_scan_ie_len); - NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, - dev->wiphy.max_sched_scan_ie_len); - NLA_PUT_U8(msg, NL80211_ATTR_MAX_MATCH_SETS, - dev->wiphy.max_match_sets); - - if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) - NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN); - if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) - NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH); - if (dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) - NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_AP_UAPSD); - if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) - NLA_PUT_FLAG(msg, NL80211_ATTR_ROAM_SUPPORT); - if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) - NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_SUPPORT); - if (dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) - NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP); - - NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES, - sizeof(u32) * dev->wiphy.n_cipher_suites, - dev->wiphy.cipher_suites); - - NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS, - dev->wiphy.max_num_pmkids); - - if (dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) - NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE); - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, - dev->wiphy.available_antennas_tx); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, - dev->wiphy.available_antennas_rx); - - if (dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) - NLA_PUT_U32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD, - dev->wiphy.probe_resp_offload); - - if ((dev->wiphy.available_antennas_tx || - 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); - if (!res) { - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant); - } - } - - if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, - dev->wiphy.interface_modes)) - goto nla_put_failure; - - nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); - if (!nl_bands) - goto nla_put_failure; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!dev->wiphy.bands[band]) - continue; - - nl_band = nla_nest_start(msg, band); - if (!nl_band) - goto nla_put_failure; - - /* add HT info */ - if (dev->wiphy.bands[band]->ht_cap.ht_supported) { - NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET, - sizeof(dev->wiphy.bands[band]->ht_cap.mcs), - &dev->wiphy.bands[band]->ht_cap.mcs); - NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA, - dev->wiphy.bands[band]->ht_cap.cap); - NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR, - dev->wiphy.bands[band]->ht_cap.ampdu_factor); - NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, - dev->wiphy.bands[band]->ht_cap.ampdu_density); - } - - /* add frequencies */ - nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS); - if (!nl_freqs) - goto nla_put_failure; - - for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) { - nl_freq = nla_nest_start(msg, i); - if (!nl_freq) - goto nla_put_failure; - - chan = &dev->wiphy.bands[band]->channels[i]; - - if (nl80211_msg_put_channel(msg, chan)) - goto nla_put_failure; - - nla_nest_end(msg, nl_freq); - } - - nla_nest_end(msg, nl_freqs); - - /* add bitrates */ - nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES); - if (!nl_rates) - goto nla_put_failure; - - for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) { - nl_rate = nla_nest_start(msg, i); - if (!nl_rate) - goto nla_put_failure; - - rate = &dev->wiphy.bands[band]->bitrates[i]; - NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE, - rate->bitrate); - if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) - NLA_PUT_FLAG(msg, - NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE); - - nla_nest_end(msg, nl_rate); - } - - nla_nest_end(msg, nl_rates); - - nla_nest_end(msg, nl_band); - } - nla_nest_end(msg, nl_bands); - - nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS); - if (!nl_cmds) - goto nla_put_failure; - - i = 0; -#define CMD(op, n) \ - do { \ - if (dev->ops->op) { \ - i++; \ - NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \ - } \ - } while (0) - - CMD(add_virtual_intf, NEW_INTERFACE); - CMD(change_virtual_intf, SET_INTERFACE); - CMD(add_key, NEW_KEY); - CMD(add_beacon, NEW_BEACON); - CMD(add_station, NEW_STATION); - CMD(add_mpath, NEW_MPATH); - CMD(update_mesh_config, SET_MESH_CONFIG); - CMD(change_bss, SET_BSS); - CMD(auth, AUTHENTICATE); - CMD(assoc, ASSOCIATE); - CMD(deauth, DEAUTHENTICATE); - CMD(disassoc, DISASSOCIATE); - CMD(join_ibss, JOIN_IBSS); - CMD(join_mesh, JOIN_MESH); - CMD(set_pmksa, SET_PMKSA); - CMD(del_pmksa, DEL_PMKSA); - CMD(flush_pmksa, FLUSH_PMKSA); - CMD(remain_on_channel, REMAIN_ON_CHANNEL); - CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); - CMD(mgmt_tx, FRAME); - CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); - if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { - i++; - NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); - } - CMD(set_channel, SET_CHANNEL); - CMD(set_wds_peer, SET_WDS_PEER); - if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { - CMD(tdls_mgmt, TDLS_MGMT); - CMD(tdls_oper, TDLS_OPER); - } - if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) - CMD(sched_scan_start, START_SCHED_SCAN); - CMD(probe_client, PROBE_CLIENT); - if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { - i++; - NLA_PUT_U32(msg, i, NL80211_CMD_REGISTER_BEACONS); - } - -#undef CMD - - if (dev->ops->connect || dev->ops->auth) { - i++; - NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT); - } - - if (dev->ops->disconnect || dev->ops->deauth) { - i++; - NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT); - } - - nla_nest_end(msg, nl_cmds); - - if (dev->ops->remain_on_channel) - NLA_PUT_U32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, - dev->wiphy.max_remain_on_channel_duration); - - if (dev->ops->mgmt_tx_cancel_wait) - NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK); - - if (mgmt_stypes) { - u16 stypes; - struct nlattr *nl_ftypes, *nl_ifs; - enum nl80211_iftype ift; - - nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES); - if (!nl_ifs) - goto nla_put_failure; - - for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { - nl_ftypes = nla_nest_start(msg, ift); - if (!nl_ftypes) - goto nla_put_failure; - i = 0; - stypes = mgmt_stypes[ift].tx; - while (stypes) { - if (stypes & 1) - NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, - (i << 4) | IEEE80211_FTYPE_MGMT); - stypes >>= 1; - i++; - } - nla_nest_end(msg, nl_ftypes); - } - - nla_nest_end(msg, nl_ifs); - - nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES); - if (!nl_ifs) - goto nla_put_failure; - - for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { - nl_ftypes = nla_nest_start(msg, ift); - if (!nl_ftypes) - goto nla_put_failure; - i = 0; - stypes = mgmt_stypes[ift].rx; - while (stypes) { - if (stypes & 1) - NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, - (i << 4) | IEEE80211_FTYPE_MGMT); - stypes >>= 1; - i++; - } - nla_nest_end(msg, nl_ftypes); - } - nla_nest_end(msg, nl_ifs); - } - - if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) { - struct nlattr *nl_wowlan; - - nl_wowlan = nla_nest_start(msg, - NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED); - if (!nl_wowlan) - goto nla_put_failure; - - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); - if (dev->wiphy.wowlan.n_patterns) { - struct nl80211_wowlan_pattern_support pat = { - .max_patterns = dev->wiphy.wowlan.n_patterns, - .min_pattern_len = - dev->wiphy.wowlan.pattern_min_len, - .max_pattern_len = - dev->wiphy.wowlan.pattern_max_len, - }; - NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, - sizeof(pat), &pat); - } - - nla_nest_end(msg, nl_wowlan); - } - - if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, - dev->wiphy.software_iftypes)) - goto nla_put_failure; - - if (nl80211_put_iface_combinations(&dev->wiphy, msg)) - goto nla_put_failure; - - if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) - NLA_PUT_U32(msg, NL80211_ATTR_DEVICE_AP_SME, - dev->wiphy.ap_sme_capa); - - NLA_PUT_U32(msg, NL80211_ATTR_FEATURE_FLAGS, dev->wiphy.features); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) -{ - int idx = 0; - int start = cb->args[0]; - struct cfg80211_registered_device *dev; - - mutex_lock(&cfg80211_mutex); - list_for_each_entry(dev, &cfg80211_rdev_list, list) { - if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) - continue; - if (++idx <= start) - continue; - if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - dev) < 0) { - idx--; - break; - } - } - mutex_unlock(&cfg80211_mutex); - - cb->args[0] = idx; - - return skb->len; -} - -static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) -{ - struct sk_buff *msg; - struct cfg80211_registered_device *dev = info->user_ptr[0]; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) { - nlmsg_free(msg); - return -ENOBUFS; - } - - return genlmsg_reply(msg, info); -} - -static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = { - [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 }, - [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 }, - [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 }, - [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 }, - [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 }, -}; - -static int parse_txq_params(struct nlattr *tb[], - struct ieee80211_txq_params *txq_params) -{ - if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] || - !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] || - !tb[NL80211_TXQ_ATTR_AIFS]) - return -EINVAL; - - txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]); - txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]); - txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]); - txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]); - txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]); - - return 0; -} - -static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) -{ - /* - * You can only set the channel explicitly for AP, mesh - * and WDS type interfaces; all others have their channel - * managed via their respective "establish a connection" - * command (connect, join, ...) - * - * Monitors are special as they are normally slaved to - * whatever else is going on, so they behave as though - * you tried setting the wiphy channel itself. - */ - return !wdev || - wdev->iftype == NL80211_IFTYPE_AP || - wdev->iftype == NL80211_IFTYPE_WDS || - wdev->iftype == NL80211_IFTYPE_MESH_POINT || - wdev->iftype == NL80211_IFTYPE_MONITOR || - wdev->iftype == NL80211_IFTYPE_P2P_GO; -} - -static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, - struct genl_info *info) -{ - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - u32 freq; - int result; - - if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) - return -EINVAL; - - if (!nl80211_can_set_dev_channel(wdev)) - return -EOPNOTSUPP; - - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { - channel_type = nla_get_u32(info->attrs[ - NL80211_ATTR_WIPHY_CHANNEL_TYPE]); - if (channel_type != NL80211_CHAN_NO_HT && - channel_type != NL80211_CHAN_HT20 && - channel_type != NL80211_CHAN_HT40PLUS && - channel_type != NL80211_CHAN_HT40MINUS) - return -EINVAL; - } - - freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - - mutex_lock(&rdev->devlist_mtx); - if (wdev) { - wdev_lock(wdev); - result = cfg80211_set_freq(rdev, wdev, freq, channel_type); - wdev_unlock(wdev); - } else { - result = cfg80211_set_freq(rdev, NULL, freq, channel_type); - } - mutex_unlock(&rdev->devlist_mtx); - - return result; -} - -static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *netdev = info->user_ptr[1]; - - return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info); -} - -static int nl80211_set_wds_peer(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 wireless_dev *wdev = dev->ieee80211_ptr; - const u8 *bssid; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (netif_running(dev)) - return -EBUSY; - - if (!rdev->ops->set_wds_peer) - return -EOPNOTSUPP; - - if (wdev->iftype != NL80211_IFTYPE_WDS) - return -EOPNOTSUPP; - - bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid); -} - - -static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev; - struct net_device *netdev = NULL; - struct wireless_dev *wdev; - int result = 0, rem_txq_params = 0; - struct nlattr *nl_txq_params; - u32 changed; - u8 retry_short = 0, retry_long = 0; - u32 frag_threshold = 0, rts_threshold = 0; - u8 coverage_class = 0; - - /* - * Try to find the wiphy and netdev. Normally this - * function shouldn't need the netdev, but this is - * done for backward compatibility -- previously - * setting the channel was done per wiphy, but now - * it is per netdev. Previous userland like hostapd - * also passed a netdev to set_wiphy, so that it is - * possible to let that go to the right netdev! - */ - mutex_lock(&cfg80211_mutex); - - if (info->attrs[NL80211_ATTR_IFINDEX]) { - int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); - - netdev = dev_get_by_index(genl_info_net(info), ifindex); - if (netdev && netdev->ieee80211_ptr) { - rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy); - mutex_lock(&rdev->mtx); - } else - netdev = NULL; - } - - if (!netdev) { - rdev = __cfg80211_rdev_from_info(info); - if (IS_ERR(rdev)) { - mutex_unlock(&cfg80211_mutex); - return PTR_ERR(rdev); - } - wdev = NULL; - netdev = NULL; - result = 0; - - mutex_lock(&rdev->mtx); - } else if (netif_running(netdev) && - nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) - wdev = netdev->ieee80211_ptr; - else - wdev = NULL; - - /* - * end workaround code, by now the rdev is available - * and locked, and wdev may or may not be NULL. - */ - - if (info->attrs[NL80211_ATTR_WIPHY_NAME]) - result = cfg80211_dev_rename( - rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); - - mutex_unlock(&cfg80211_mutex); - - if (result) - goto bad_res; - - if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) { - struct ieee80211_txq_params txq_params; - struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1]; - - if (!rdev->ops->set_txq_params) { - result = -EOPNOTSUPP; - goto bad_res; - } - - if (!netdev) { - result = -EINVAL; - goto bad_res; - } - - if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { - result = -EINVAL; - goto bad_res; - } - - nla_for_each_nested(nl_txq_params, - info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], - rem_txq_params) { - nla_parse(tb, NL80211_TXQ_ATTR_MAX, - nla_data(nl_txq_params), - nla_len(nl_txq_params), - txq_params_policy); - result = parse_txq_params(tb, &txq_params); - if (result) - goto bad_res; - - result = rdev->ops->set_txq_params(&rdev->wiphy, - netdev, - &txq_params); - if (result) - goto bad_res; - } - } - - if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - result = __nl80211_set_channel(rdev, wdev, info); - if (result) - goto bad_res; - } - - if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) { - enum nl80211_tx_power_setting type; - int idx, mbm = 0; - - if (!rdev->ops->set_tx_power) { - result = -EOPNOTSUPP; - goto bad_res; - } - - idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING; - type = nla_get_u32(info->attrs[idx]); - - if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] && - (type != NL80211_TX_POWER_AUTOMATIC)) { - result = -EINVAL; - goto bad_res; - } - - if (type != NL80211_TX_POWER_AUTOMATIC) { - idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL; - mbm = nla_get_u32(info->attrs[idx]); - } - - result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm); - if (result) - goto bad_res; - } - - if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] && - info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) { - u32 tx_ant, rx_ant; - if ((!rdev->wiphy.available_antennas_tx && - !rdev->wiphy.available_antennas_rx) || - !rdev->ops->set_antenna) { - result = -EOPNOTSUPP; - goto bad_res; - } - - tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]); - rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]); - - /* reject antenna configurations which don't match the - * available antenna masks, except for the "all" mask */ - if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) || - (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) { - result = -EINVAL; - goto bad_res; - } - - 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); - if (result) - goto bad_res; - } - - changed = 0; - - if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) { - retry_short = nla_get_u8( - info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]); - if (retry_short == 0) { - result = -EINVAL; - goto bad_res; - } - changed |= WIPHY_PARAM_RETRY_SHORT; - } - - if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) { - retry_long = nla_get_u8( - info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]); - if (retry_long == 0) { - result = -EINVAL; - goto bad_res; - } - changed |= WIPHY_PARAM_RETRY_LONG; - } - - if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) { - frag_threshold = nla_get_u32( - info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]); - if (frag_threshold < 256) { - result = -EINVAL; - goto bad_res; - } - if (frag_threshold != (u32) -1) { - /* - * Fragments (apart from the last one) are required to - * have even length. Make the fragmentation code - * simpler by stripping LSB should someone try to use - * odd threshold value. - */ - frag_threshold &= ~0x1; - } - changed |= WIPHY_PARAM_FRAG_THRESHOLD; - } - - if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) { - rts_threshold = nla_get_u32( - info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]); - changed |= WIPHY_PARAM_RTS_THRESHOLD; - } - - if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) { - coverage_class = nla_get_u8( - info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]); - changed |= WIPHY_PARAM_COVERAGE_CLASS; - } - - if (changed) { - u8 old_retry_short, old_retry_long; - u32 old_frag_threshold, old_rts_threshold; - u8 old_coverage_class; - - if (!rdev->ops->set_wiphy_params) { - result = -EOPNOTSUPP; - goto bad_res; - } - - old_retry_short = rdev->wiphy.retry_short; - old_retry_long = rdev->wiphy.retry_long; - old_frag_threshold = rdev->wiphy.frag_threshold; - old_rts_threshold = rdev->wiphy.rts_threshold; - old_coverage_class = rdev->wiphy.coverage_class; - - if (changed & WIPHY_PARAM_RETRY_SHORT) - rdev->wiphy.retry_short = retry_short; - if (changed & WIPHY_PARAM_RETRY_LONG) - rdev->wiphy.retry_long = retry_long; - if (changed & WIPHY_PARAM_FRAG_THRESHOLD) - rdev->wiphy.frag_threshold = frag_threshold; - if (changed & WIPHY_PARAM_RTS_THRESHOLD) - rdev->wiphy.rts_threshold = rts_threshold; - if (changed & WIPHY_PARAM_COVERAGE_CLASS) - rdev->wiphy.coverage_class = coverage_class; - - result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); - if (result) { - rdev->wiphy.retry_short = old_retry_short; - rdev->wiphy.retry_long = old_retry_long; - rdev->wiphy.frag_threshold = old_frag_threshold; - rdev->wiphy.rts_threshold = old_rts_threshold; - rdev->wiphy.coverage_class = old_coverage_class; - } - } - - bad_res: - mutex_unlock(&rdev->mtx); - if (netdev) - dev_put(netdev); - return result; -} - - -static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, - struct cfg80211_registered_device *rdev, - struct net_device *dev) -{ - void *hdr; - - hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE); - if (!hdr) - return -1; - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name); - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype); - - NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, - rdev->devlist_generation ^ - (cfg80211_rdev_list_generation << 2)); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb) -{ - int wp_idx = 0; - int if_idx = 0; - int wp_start = cb->args[0]; - int if_start = cb->args[1]; - struct cfg80211_registered_device *rdev; - struct wireless_dev *wdev; - - mutex_lock(&cfg80211_mutex); - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { - if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) - continue; - if (wp_idx < wp_start) { - wp_idx++; - continue; - } - if_idx = 0; - - mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->netdev_list, list) { - if (if_idx < if_start) { - if_idx++; - continue; - } - if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - rdev, wdev->netdev) < 0) { - mutex_unlock(&rdev->devlist_mtx); - goto out; - } - if_idx++; - } - mutex_unlock(&rdev->devlist_mtx); - - wp_idx++; - } - out: - mutex_unlock(&cfg80211_mutex); - - cb->args[0] = wp_idx; - cb->args[1] = if_idx; - - return skb->len; -} - -static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) -{ - struct sk_buff *msg; - struct cfg80211_registered_device *dev = info->user_ptr[0]; - struct net_device *netdev = info->user_ptr[1]; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, - dev, netdev) < 0) { - nlmsg_free(msg); - return -ENOBUFS; - } - - return genlmsg_reply(msg, info); -} - -static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = { - [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG }, - [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG }, - [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG }, - [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG }, - [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG }, -}; - -static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) -{ - struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1]; - int flag; - - *mntrflags = 0; - - if (!nla) - return -EINVAL; - - if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX, - nla, mntr_flags_policy)) - return -EINVAL; - - for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++) - if (flags[flag]) - *mntrflags |= (1<<flag); - - return 0; -} - -static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u8 use_4addr, - enum nl80211_iftype iftype) -{ - if (!use_4addr) { - if (netdev && br_port_exists(netdev)) - return -EBUSY; - return 0; - } - - switch (iftype) { - case NL80211_IFTYPE_AP_VLAN: - if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP) - return 0; - break; - case NL80211_IFTYPE_STATION: - if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION) - return 0; - break; - default: - break; - } - - return -EOPNOTSUPP; -} - -static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct vif_params params; - int err; - enum nl80211_iftype otype, ntype; - struct net_device *dev = info->user_ptr[1]; - u32 _flags, *flags = NULL; - bool change = false; - - memset(¶ms, 0, sizeof(params)); - - otype = ntype = dev->ieee80211_ptr->iftype; - - if (info->attrs[NL80211_ATTR_IFTYPE]) { - ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); - if (otype != ntype) - change = true; - if (ntype > NL80211_IFTYPE_MAX) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_MESH_ID]) { - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (ntype != NL80211_IFTYPE_MESH_POINT) - return -EINVAL; - if (netif_running(dev)) - return -EBUSY; - - wdev_lock(wdev); - BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != - IEEE80211_MAX_MESH_ID_LEN); - wdev->mesh_id_up_len = - nla_len(info->attrs[NL80211_ATTR_MESH_ID]); - memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), - wdev->mesh_id_up_len); - wdev_unlock(wdev); - } - - if (info->attrs[NL80211_ATTR_4ADDR]) { - params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); - change = true; - err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype); - if (err) - return err; - } else { - params.use_4addr = -1; - } - - if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { - if (ntype != NL80211_IFTYPE_MONITOR) - return -EINVAL; - err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], - &_flags); - if (err) - return err; - - flags = &_flags; - change = true; - } - - if (change) - err = cfg80211_change_iface(rdev, dev, ntype, flags, ¶ms); - else - err = 0; - - if (!err && params.use_4addr != -1) - dev->ieee80211_ptr->use_4addr = params.use_4addr; - - return err; -} - -static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct vif_params params; - struct net_device *dev; - int err; - enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; - u32 flags; - - memset(¶ms, 0, sizeof(params)); - - if (!info->attrs[NL80211_ATTR_IFNAME]) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_IFTYPE]) { - type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); - if (type > NL80211_IFTYPE_MAX) - return -EINVAL; - } - - if (!rdev->ops->add_virtual_intf || - !(rdev->wiphy.interface_modes & (1 << type))) - return -EOPNOTSUPP; - - if (info->attrs[NL80211_ATTR_4ADDR]) { - params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); - err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type); - if (err) - return err; - } - - err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? - info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, - &flags); - dev = rdev->ops->add_virtual_intf(&rdev->wiphy, - nla_data(info->attrs[NL80211_ATTR_IFNAME]), - type, err ? NULL : &flags, ¶ms); - if (IS_ERR(dev)) - return PTR_ERR(dev); - - if (type == NL80211_IFTYPE_MESH_POINT && - info->attrs[NL80211_ATTR_MESH_ID]) { - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != - IEEE80211_MAX_MESH_ID_LEN); - wdev->mesh_id_up_len = - nla_len(info->attrs[NL80211_ATTR_MESH_ID]); - memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), - wdev->mesh_id_up_len); - wdev_unlock(wdev); - } - - return 0; -} - -static int nl80211_del_interface(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]; - - if (!rdev->ops->del_virtual_intf) - return -EOPNOTSUPP; - - return rdev->ops->del_virtual_intf(&rdev->wiphy, dev); -} - -struct get_key_cookie { - struct sk_buff *msg; - int error; - int idx; -}; - -static void get_key_callback(void *c, struct key_params *params) -{ - struct nlattr *key; - struct get_key_cookie *cookie = c; - - if (params->key) - NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA, - params->key_len, params->key); - - if (params->seq) - NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ, - params->seq_len, params->seq); - - if (params->cipher) - NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER, - params->cipher); - - key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY); - if (!key) - goto nla_put_failure; - - if (params->key) - NLA_PUT(cookie->msg, NL80211_KEY_DATA, - params->key_len, params->key); - - if (params->seq) - NLA_PUT(cookie->msg, NL80211_KEY_SEQ, - params->seq_len, params->seq); - - if (params->cipher) - NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER, - params->cipher); - - NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx); - - nla_nest_end(cookie->msg, key); - - return; - nla_put_failure: - cookie->error = 1; -} - -static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int err; - struct net_device *dev = info->user_ptr[1]; - u8 key_idx = 0; - const u8 *mac_addr = NULL; - bool pairwise; - struct get_key_cookie cookie = { - .error = 0, - }; - void *hdr; - struct sk_buff *msg; - - if (info->attrs[NL80211_ATTR_KEY_IDX]) - key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - - if (key_idx > 5) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_MAC]) - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - pairwise = !!mac_addr; - if (info->attrs[NL80211_ATTR_KEY_TYPE]) { - u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]); - if (kt >= NUM_NL80211_KEYTYPES) - return -EINVAL; - if (kt != NL80211_KEYTYPE_GROUP && - kt != NL80211_KEYTYPE_PAIRWISE) - return -EINVAL; - pairwise = kt == NL80211_KEYTYPE_PAIRWISE; - } - - if (!rdev->ops->get_key) - return -EOPNOTSUPP; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_NEW_KEY); - if (IS_ERR(hdr)) { - nlmsg_free(msg); - return PTR_ERR(hdr); - } - - cookie.msg = msg; - cookie.idx = key_idx; - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); - if (mac_addr) - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - - if (pairwise && mac_addr && - !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) { - nlmsg_free(msg); - return -ENOENT; - } - - err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise, - mac_addr, &cookie, get_key_callback); - - if (err) - goto free_msg; - - if (cookie.error) - goto nla_put_failure; - - genlmsg_end(msg, hdr); - return genlmsg_reply(msg, info); - - nla_put_failure: - err = -ENOBUFS; - free_msg: - nlmsg_free(msg); - return err; -} - -static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct key_parse key; - int err; - struct net_device *dev = info->user_ptr[1]; - - err = nl80211_parse_key(info, &key); - if (err) - return err; - - if (key.idx < 0) - return -EINVAL; - - /* only support setting default key */ - if (!key.def && !key.defmgmt) - return -EINVAL; - - wdev_lock(dev->ieee80211_ptr); - - if (key.def) { - if (!rdev->ops->set_default_key) { - err = -EOPNOTSUPP; - goto out; - } - - err = nl80211_key_allowed(dev->ieee80211_ptr); - if (err) - goto out; - - err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx, - key.def_uni, key.def_multi); - - if (err) - goto out; - -#ifdef CONFIG_CFG80211_WEXT - dev->ieee80211_ptr->wext.default_key = key.idx; -#endif - } else { - if (key.def_uni || !key.def_multi) { - err = -EINVAL; - goto out; - } - - if (!rdev->ops->set_default_mgmt_key) { - err = -EOPNOTSUPP; - goto out; - } - - err = nl80211_key_allowed(dev->ieee80211_ptr); - if (err) - goto out; - - err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, - dev, key.idx); - if (err) - goto out; - -#ifdef CONFIG_CFG80211_WEXT - dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; -#endif - } - - out: - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - -static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int err; - struct net_device *dev = info->user_ptr[1]; - struct key_parse key; - const u8 *mac_addr = NULL; - - err = nl80211_parse_key(info, &key); - if (err) - return err; - - if (!key.p.key) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_MAC]) - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (key.type == -1) { - if (mac_addr) - key.type = NL80211_KEYTYPE_PAIRWISE; - else - key.type = NL80211_KEYTYPE_GROUP; - } - - /* for now */ - if (key.type != NL80211_KEYTYPE_PAIRWISE && - key.type != NL80211_KEYTYPE_GROUP) - return -EINVAL; - - if (!rdev->ops->add_key) - return -EOPNOTSUPP; - - if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, - key.type == NL80211_KEYTYPE_PAIRWISE, - mac_addr)) - return -EINVAL; - - 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); - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - -static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int err; - struct net_device *dev = info->user_ptr[1]; - u8 *mac_addr = NULL; - struct key_parse key; - - err = nl80211_parse_key(info, &key); - if (err) - return err; - - if (info->attrs[NL80211_ATTR_MAC]) - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (key.type == -1) { - if (mac_addr) - key.type = NL80211_KEYTYPE_PAIRWISE; - else - key.type = NL80211_KEYTYPE_GROUP; - } - - /* for now */ - if (key.type != NL80211_KEYTYPE_PAIRWISE && - key.type != NL80211_KEYTYPE_GROUP) - return -EINVAL; - - if (!rdev->ops->del_key) - return -EOPNOTSUPP; - - wdev_lock(dev->ieee80211_ptr); - err = nl80211_key_allowed(dev->ieee80211_ptr); - - if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr && - !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) - err = -ENOENT; - - if (!err) - err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, - key.type == NL80211_KEYTYPE_PAIRWISE, - mac_addr); - -#ifdef CONFIG_CFG80211_WEXT - if (!err) { - if (key.idx == dev->ieee80211_ptr->wext.default_key) - dev->ieee80211_ptr->wext.default_key = -1; - else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key) - dev->ieee80211_ptr->wext.default_mgmt_key = -1; - } -#endif - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - -static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) -{ - int (*call)(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info); - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *dev = info->user_ptr[1]; - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct beacon_parameters params; - int haveinfo = 0, err; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) || - !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) || - !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) || - !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP])) - return -EINVAL; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - memset(¶ms, 0, sizeof(params)); - - switch (info->genlhdr->cmd) { - case NL80211_CMD_NEW_BEACON: - /* these are required for NEW_BEACON */ - if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] || - !info->attrs[NL80211_ATTR_DTIM_PERIOD] || - !info->attrs[NL80211_ATTR_BEACON_HEAD]) - return -EINVAL; - - params.interval = - nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); - params.dtim_period = - nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); - - err = cfg80211_validate_beacon_int(rdev, params.interval); - if (err) - return err; - - /* - * In theory, some of these attributes could be required for - * NEW_BEACON, but since they were not used when the command was - * originally added, keep them optional for old user space - * programs to work with drivers that do not need the additional - * information. - */ - if (info->attrs[NL80211_ATTR_SSID]) { - params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - params.ssid_len = - nla_len(info->attrs[NL80211_ATTR_SSID]); - if (params.ssid_len == 0 || - params.ssid_len > IEEE80211_MAX_SSID_LEN) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) { - params.hidden_ssid = nla_get_u32( - info->attrs[NL80211_ATTR_HIDDEN_SSID]); - if (params.hidden_ssid != - NL80211_HIDDEN_SSID_NOT_IN_USE && - params.hidden_ssid != - NL80211_HIDDEN_SSID_ZERO_LEN && - params.hidden_ssid != - NL80211_HIDDEN_SSID_ZERO_CONTENTS) - return -EINVAL; - } - - params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; - - 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)) - return -EINVAL; - } else - params.auth_type = NL80211_AUTHTYPE_AUTOMATIC; - - err = nl80211_crypto_settings(rdev, info, ¶ms.crypto, - NL80211_MAX_NR_CIPHER_SUITES); - if (err) - return err; - - call = rdev->ops->add_beacon; - break; - case NL80211_CMD_SET_BEACON: - call = rdev->ops->set_beacon; - break; - default: - WARN_ON(1); - return -EOPNOTSUPP; - } - - if (!call) - return -EOPNOTSUPP; - - if (info->attrs[NL80211_ATTR_BEACON_HEAD]) { - params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); - params.head_len = - nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]); - haveinfo = 1; - } - - if (info->attrs[NL80211_ATTR_BEACON_TAIL]) { - params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]); - params.tail_len = - nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]); - haveinfo = 1; - } - - if (!haveinfo) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_IE]) { - params.beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]); - params.beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) { - params.proberesp_ies = - nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); - params.proberesp_ies_len = - nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); - } - - if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) { - params.assocresp_ies = - nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); - params.assocresp_ies_len = - nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); - } - - if (info->attrs[NL80211_ATTR_PROBE_RESP]) { - params.probe_resp = - nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]); - params.probe_resp_len = - nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]); - } - - err = call(&rdev->wiphy, dev, ¶ms); - if (!err && params.interval) - wdev->beacon_interval = params.interval; - return err; -} - -static int nl80211_del_beacon(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 wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - if (!rdev->ops->del_beacon) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - err = rdev->ops->del_beacon(&rdev->wiphy, dev); - if (!err) - wdev->beacon_interval = 0; - return err; -} - -static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { - [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG }, - [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG }, - [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG }, - [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG }, - [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG }, -}; - -static int parse_station_flags(struct genl_info *info, - struct station_parameters *params) -{ - struct nlattr *flags[NL80211_STA_FLAG_MAX + 1]; - struct nlattr *nla; - int flag; - - /* - * Try parsing the new attribute first so userspace - * can specify both for older kernels. - */ - nla = info->attrs[NL80211_ATTR_STA_FLAGS2]; - if (nla) { - struct nl80211_sta_flag_update *sta_flags; - - sta_flags = nla_data(nla); - params->sta_flags_mask = sta_flags->mask; - params->sta_flags_set = sta_flags->set; - if ((params->sta_flags_mask | - params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID)) - return -EINVAL; - return 0; - } - - /* if present, parse the old attribute */ - - nla = info->attrs[NL80211_ATTR_STA_FLAGS]; - if (!nla) - return 0; - - if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX, - nla, sta_flags_policy)) - return -EINVAL; - - params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1; - params->sta_flags_mask &= ~1; - - for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) - if (flags[flag]) - params->sta_flags_set |= (1<<flag); - - return 0; -} - -static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, - int attr) -{ - struct nlattr *rate; - u16 bitrate; - - rate = nla_nest_start(msg, attr); - if (!rate) - goto nla_put_failure; - - /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ - bitrate = cfg80211_calculate_bitrate(info); - if (bitrate > 0) - NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); - - if (info->flags & RATE_INFO_FLAGS_MCS) - NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, info->mcs); - if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) - NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH); - if (info->flags & RATE_INFO_FLAGS_SHORT_GI) - NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI); - - nla_nest_end(msg, rate); - return true; - -nla_put_failure: - return false; -} - -static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, - int flags, struct net_device *dev, - const u8 *mac_addr, struct station_info *sinfo) -{ - void *hdr; - struct nlattr *sinfoattr, *bss_param; - - hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); - if (!hdr) - return -1; - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - - NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation); - - sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); - if (!sinfoattr) - goto nla_put_failure; - if (sinfo->filled & STATION_INFO_CONNECTED_TIME) - NLA_PUT_U32(msg, NL80211_STA_INFO_CONNECTED_TIME, - sinfo->connected_time); - if (sinfo->filled & STATION_INFO_INACTIVE_TIME) - NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME, - sinfo->inactive_time); - if (sinfo->filled & STATION_INFO_RX_BYTES) - NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES, - sinfo->rx_bytes); - if (sinfo->filled & STATION_INFO_TX_BYTES) - NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES, - sinfo->tx_bytes); - if (sinfo->filled & STATION_INFO_LLID) - NLA_PUT_U16(msg, NL80211_STA_INFO_LLID, - sinfo->llid); - if (sinfo->filled & STATION_INFO_PLID) - NLA_PUT_U16(msg, NL80211_STA_INFO_PLID, - sinfo->plid); - if (sinfo->filled & STATION_INFO_PLINK_STATE) - NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE, - sinfo->plink_state); - if (sinfo->filled & STATION_INFO_SIGNAL) - NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL, - sinfo->signal); - if (sinfo->filled & STATION_INFO_SIGNAL_AVG) - NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG, - sinfo->signal_avg); - if (sinfo->filled & STATION_INFO_TX_BITRATE) { - if (!nl80211_put_sta_rate(msg, &sinfo->txrate, - NL80211_STA_INFO_TX_BITRATE)) - goto nla_put_failure; - } - if (sinfo->filled & STATION_INFO_RX_BITRATE) { - if (!nl80211_put_sta_rate(msg, &sinfo->rxrate, - NL80211_STA_INFO_RX_BITRATE)) - goto nla_put_failure; - } - if (sinfo->filled & STATION_INFO_RX_PACKETS) - NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS, - sinfo->rx_packets); - if (sinfo->filled & STATION_INFO_TX_PACKETS) - NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS, - sinfo->tx_packets); - if (sinfo->filled & STATION_INFO_TX_RETRIES) - NLA_PUT_U32(msg, NL80211_STA_INFO_TX_RETRIES, - sinfo->tx_retries); - if (sinfo->filled & STATION_INFO_TX_FAILED) - NLA_PUT_U32(msg, NL80211_STA_INFO_TX_FAILED, - sinfo->tx_failed); - if (sinfo->filled & STATION_INFO_BSS_PARAM) { - bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM); - if (!bss_param) - goto nla_put_failure; - - if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) - NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_CTS_PROT); - if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) - NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE); - if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) - NLA_PUT_FLAG(msg, - NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME); - NLA_PUT_U8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD, - sinfo->bss_param.dtim_period); - NLA_PUT_U16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL, - sinfo->bss_param.beacon_interval); - - nla_nest_end(msg, bss_param); - } - if (sinfo->filled & STATION_INFO_STA_FLAGS) - NLA_PUT(msg, NL80211_STA_INFO_STA_FLAGS, - sizeof(struct nl80211_sta_flag_update), - &sinfo->sta_flags); - nla_nest_end(msg, sinfoattr); - - if (sinfo->filled & STATION_INFO_ASSOC_REQ_IES) - NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len, - sinfo->assoc_req_ies); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int nl80211_dump_station(struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct station_info sinfo; - struct cfg80211_registered_device *dev; - struct net_device *netdev; - u8 mac_addr[ETH_ALEN]; - int sta_idx = cb->args[1]; - int err; - - err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev); - if (err) - return err; - - if (!dev->ops->dump_station) { - err = -EOPNOTSUPP; - goto out_err; - } - - while (1) { - memset(&sinfo, 0, sizeof(sinfo)); - err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx, - mac_addr, &sinfo); - if (err == -ENOENT) - break; - if (err) - goto out_err; - - if (nl80211_send_station(skb, - NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - netdev, mac_addr, - &sinfo) < 0) - goto out; - - sta_idx++; - } - - - out: - cb->args[1] = sta_idx; - err = skb->len; - out_err: - nl80211_finish_netdev_dump(dev); - - return err; -} - -static int nl80211_get_station(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 station_info sinfo; - struct sk_buff *msg; - u8 *mac_addr = NULL; - int err; - - memset(&sinfo, 0, sizeof(sinfo)); - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (!rdev->ops->get_station) - return -EOPNOTSUPP; - - err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo); - if (err) - return err; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0, - dev, mac_addr, &sinfo) < 0) { - nlmsg_free(msg); - return -ENOBUFS; - } - - return genlmsg_reply(msg, info); -} - -/* - * Get vlan interface making sure it is running and on the right wiphy. - */ -static int get_vlan(struct genl_info *info, - struct cfg80211_registered_device *rdev, - struct net_device **vlan) -{ - struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN]; - *vlan = NULL; - - if (vlanattr) { - *vlan = dev_get_by_index(genl_info_net(info), - nla_get_u32(vlanattr)); - if (!*vlan) - return -ENODEV; - if (!(*vlan)->ieee80211_ptr) - return -EINVAL; - if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy) - return -EINVAL; - if (!netif_running(*vlan)) - return -ENETDOWN; - } - return 0; -} - -static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int err; - struct net_device *dev = info->user_ptr[1]; - struct station_parameters params; - u8 *mac_addr = NULL; - - memset(¶ms, 0, sizeof(params)); - - params.listen_interval = -1; - params.plink_state = -1; - - if (info->attrs[NL80211_ATTR_STA_AID]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) { - params.supported_rates = - nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); - params.supported_rates_len = - nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); - } - - if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) - params.listen_interval = - nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); - - if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) - params.ht_capa = - nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); - - if (parse_station_flags(info, ¶ms)) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) - params.plink_action = - nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); - - if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) - params.plink_state = - nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); - - err = get_vlan(info, rdev, ¶ms.vlan); - if (err) - goto out; - - /* validate settings */ - err = 0; - - switch (dev->ieee80211_ptr->iftype) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_P2P_GO: - /* disallow mesh-specific things */ - if (params.plink_action) - err = -EINVAL; - break; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_STATION: - /* disallow things sta doesn't support */ - if (params.plink_action) - err = -EINVAL; - if (params.vlan) - err = -EINVAL; - if (params.supported_rates && - !(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) - err = -EINVAL; - if (params.ht_capa) - err = -EINVAL; - if (params.listen_interval >= 0) - err = -EINVAL; - if (params.sta_flags_mask & - ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | - BIT(NL80211_STA_FLAG_TDLS_PEER))) - err = -EINVAL; - /* can't change the TDLS bit */ - if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && - (params.sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER))) - err = -EINVAL; - break; - case NL80211_IFTYPE_MESH_POINT: - /* disallow things mesh doesn't support */ - if (params.vlan) - err = -EINVAL; - if (params.ht_capa) - err = -EINVAL; - if (params.listen_interval >= 0) - err = -EINVAL; - if (params.sta_flags_mask & - ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | - BIT(NL80211_STA_FLAG_MFP) | - BIT(NL80211_STA_FLAG_AUTHORIZED))) - err = -EINVAL; - break; - default: - err = -EINVAL; - } - - if (err) - goto out; - - if (!rdev->ops->change_station) { - err = -EOPNOTSUPP; - goto out; - } - - err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, ¶ms); - - out: - if (params.vlan) - dev_put(params.vlan); - - return err; -} - -static struct nla_policy -nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = { - [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, - [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 }, -}; - -static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int err; - struct net_device *dev = info->user_ptr[1]; - struct station_parameters params; - u8 *mac_addr = NULL; - - memset(¶ms, 0, sizeof(params)); - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_STA_AID]) - return -EINVAL; - - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - params.supported_rates = - nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); - params.supported_rates_len = - nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); - params.listen_interval = - nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); - - params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); - if (!params.aid || params.aid > IEEE80211_MAX_AID) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) - params.ht_capa = - nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); - - if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) - params.plink_action = - nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); - - if (parse_station_flags(info, ¶ms)) - return -EINVAL; - - /* parse WME attributes if sta is WME capable */ - if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) && - (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) && - info->attrs[NL80211_ATTR_STA_WME]) { - struct nlattr *tb[NL80211_STA_WME_MAX + 1]; - struct nlattr *nla; - - nla = info->attrs[NL80211_ATTR_STA_WME]; - err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla, - nl80211_sta_wme_policy); - if (err) - return err; - - if (tb[NL80211_STA_WME_UAPSD_QUEUES]) - params.uapsd_queues = - nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]); - if (params.uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) - return -EINVAL; - - if (tb[NL80211_STA_WME_MAX_SP]) - params.max_sp = - nla_get_u8(tb[NL80211_STA_WME_MAX_SP]); - - if (params.max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) - return -EINVAL; - - params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD; - } - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) - return -EINVAL; - - /* - * Only managed stations can add TDLS peers, and only when the - * wiphy supports external TDLS setup. - */ - if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION && - !((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && - (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) && - (rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))) - return -EINVAL; - - err = get_vlan(info, rdev, ¶ms.vlan); - if (err) - goto out; - - /* validate settings */ - err = 0; - - if (!rdev->ops->add_station) { - err = -EOPNOTSUPP; - goto out; - } - - err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, ¶ms); - - out: - if (params.vlan) - dev_put(params.vlan); - return err; -} - -static int nl80211_del_station(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]; - u8 *mac_addr = NULL; - - if (info->attrs[NL80211_ATTR_MAC]) - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EINVAL; - - if (!rdev->ops->del_station) - return -EOPNOTSUPP; - - return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr); -} - -static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq, - int flags, struct net_device *dev, - u8 *dst, u8 *next_hop, - struct mpath_info *pinfo) -{ - void *hdr; - struct nlattr *pinfoattr; - - hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); - if (!hdr) - return -1; - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); - NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop); - - NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation); - - pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO); - if (!pinfoattr) - goto nla_put_failure; - if (pinfo->filled & MPATH_INFO_FRAME_QLEN) - NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN, - pinfo->frame_qlen); - if (pinfo->filled & MPATH_INFO_SN) - NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN, - pinfo->sn); - if (pinfo->filled & MPATH_INFO_METRIC) - NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC, - pinfo->metric); - if (pinfo->filled & MPATH_INFO_EXPTIME) - NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME, - pinfo->exptime); - if (pinfo->filled & MPATH_INFO_FLAGS) - NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS, - pinfo->flags); - if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) - NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, - pinfo->discovery_timeout); - if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) - NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES, - pinfo->discovery_retries); - - nla_nest_end(msg, pinfoattr); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int nl80211_dump_mpath(struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct mpath_info pinfo; - struct cfg80211_registered_device *dev; - struct net_device *netdev; - u8 dst[ETH_ALEN]; - u8 next_hop[ETH_ALEN]; - int path_idx = cb->args[1]; - int err; - - err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev); - if (err) - return err; - - if (!dev->ops->dump_mpath) { - err = -EOPNOTSUPP; - goto out_err; - } - - if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { - err = -EOPNOTSUPP; - goto out_err; - } - - while (1) { - err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx, - dst, next_hop, &pinfo); - if (err == -ENOENT) - break; - if (err) - goto out_err; - - if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - netdev, dst, next_hop, - &pinfo) < 0) - goto out; - - path_idx++; - } - - - out: - cb->args[1] = path_idx; - err = skb->len; - out_err: - nl80211_finish_netdev_dump(dev); - return err; -} - -static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int err; - struct net_device *dev = info->user_ptr[1]; - struct mpath_info pinfo; - struct sk_buff *msg; - u8 *dst = NULL; - u8 next_hop[ETH_ALEN]; - - memset(&pinfo, 0, sizeof(pinfo)); - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - dst = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (!rdev->ops->get_mpath) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo); - if (err) - return err; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0, - dev, dst, next_hop, &pinfo) < 0) { - nlmsg_free(msg); - return -ENOBUFS; - } - - return genlmsg_reply(msg, info); -} - -static int nl80211_set_mpath(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]; - u8 *dst = NULL; - u8 *next_hop = NULL; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) - return -EINVAL; - - dst = nla_data(info->attrs[NL80211_ATTR_MAC]); - next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); - - if (!rdev->ops->change_mpath) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop); -} - -static int nl80211_new_mpath(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]; - u8 *dst = NULL; - u8 *next_hop = NULL; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) - return -EINVAL; - - dst = nla_data(info->attrs[NL80211_ATTR_MAC]); - next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); - - if (!rdev->ops->add_mpath) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop); -} - -static int nl80211_del_mpath(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]; - u8 *dst = NULL; - - if (info->attrs[NL80211_ATTR_MAC]) - dst = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (!rdev->ops->del_mpath) - return -EOPNOTSUPP; - - return rdev->ops->del_mpath(&rdev->wiphy, dev, dst); -} - -static int nl80211_set_bss(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 bss_parameters params; - - memset(¶ms, 0, sizeof(params)); - /* default to not changing parameters */ - params.use_cts_prot = -1; - params.use_short_preamble = -1; - params.use_short_slot_time = -1; - params.ap_isolate = -1; - params.ht_opmode = -1; - - if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) - params.use_cts_prot = - nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]); - if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]) - params.use_short_preamble = - nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]); - if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) - params.use_short_slot_time = - nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); - if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { - params.basic_rates = - nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); - params.basic_rates_len = - nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); - } - if (info->attrs[NL80211_ATTR_AP_ISOLATE]) - params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]); - if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE]) - params.ht_opmode = - nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]); - - if (!rdev->ops->change_bss) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - return rdev->ops->change_bss(&rdev->wiphy, dev, ¶ms); -} - -static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { - [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, - [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, - [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, - [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, - [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, - [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, -}; - -static int parse_reg_rule(struct nlattr *tb[], - struct ieee80211_reg_rule *reg_rule) -{ - struct ieee80211_freq_range *freq_range = ®_rule->freq_range; - struct ieee80211_power_rule *power_rule = ®_rule->power_rule; - - if (!tb[NL80211_ATTR_REG_RULE_FLAGS]) - return -EINVAL; - if (!tb[NL80211_ATTR_FREQ_RANGE_START]) - return -EINVAL; - if (!tb[NL80211_ATTR_FREQ_RANGE_END]) - return -EINVAL; - if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) - return -EINVAL; - if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]) - return -EINVAL; - - reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]); - - freq_range->start_freq_khz = - nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]); - freq_range->end_freq_khz = - nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]); - freq_range->max_bandwidth_khz = - nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]); - - power_rule->max_eirp = - nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]); - - if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]) - power_rule->max_antenna_gain = - nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]); - - return 0; -} - -static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) -{ - int r; - char *data = NULL; - - /* - * You should only get this when cfg80211 hasn't yet initialized - * completely when built-in to the kernel right between the time - * window between nl80211_init() and regulatory_init(), if that is - * even possible. - */ - mutex_lock(&cfg80211_mutex); - if (unlikely(!cfg80211_regdomain)) { - mutex_unlock(&cfg80211_mutex); - return -EINPROGRESS; - } - mutex_unlock(&cfg80211_mutex); - - if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) - return -EINVAL; - - data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); - - r = regulatory_hint_user(data); - - return r; -} - -static int nl80211_get_mesh_config(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 wireless_dev *wdev = dev->ieee80211_ptr; - struct mesh_config cur_params; - int err = 0; - void *hdr; - struct nlattr *pinfoattr; - struct sk_buff *msg; - - if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - if (!rdev->ops->get_mesh_config) - return -EOPNOTSUPP; - - wdev_lock(wdev); - /* If not connected, get default parameters */ - 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); - wdev_unlock(wdev); - - if (err) - return err; - - /* Draw up a netlink message to send back */ - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_GET_MESH_CONFIG); - if (!hdr) - goto out; - pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG); - if (!pinfoattr) - goto nla_put_failure; - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT, - cur_params.dot11MeshRetryTimeout); - NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT, - cur_params.dot11MeshConfirmTimeout); - NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT, - cur_params.dot11MeshHoldingTimeout); - NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, - cur_params.dot11MeshMaxPeerLinks); - NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES, - cur_params.dot11MeshMaxRetries); - NLA_PUT_U8(msg, NL80211_MESHCONF_TTL, - cur_params.dot11MeshTTL); - NLA_PUT_U8(msg, NL80211_MESHCONF_ELEMENT_TTL, - cur_params.element_ttl); - NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, - cur_params.auto_open_plinks); - NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, - cur_params.dot11MeshHWMPmaxPREQretries); - NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME, - cur_params.path_refresh_time); - NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, - cur_params.min_discovery_timeout); - NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, - cur_params.dot11MeshHWMPactivePathTimeout); - NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, - cur_params.dot11MeshHWMPpreqMinInterval); - NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, - cur_params.dot11MeshHWMPnetDiameterTraversalTime); - NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE, - cur_params.dot11MeshHWMPRootMode); - NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL, - cur_params.dot11MeshHWMPRannInterval); - NLA_PUT_U8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, - cur_params.dot11MeshGateAnnouncementProtocol); - nla_nest_end(msg, pinfoattr); - genlmsg_end(msg, hdr); - return genlmsg_reply(msg, info); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - out: - nlmsg_free(msg); - return -ENOBUFS; -} - -static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = { - [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, - [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, - [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, - [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 }, - [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 }, - [NL80211_MESHCONF_TTL] = { .type = NLA_U8 }, - [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 }, - [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 }, - - [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 }, - [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 }, - [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 }, - [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 }, - [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 }, - [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 }, - [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 }, - [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 }, - [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 }, -}; - -static const struct nla_policy - nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = { - [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 }, - [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 }, - [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, - [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG }, -}; - -static int nl80211_parse_mesh_config(struct genl_info *info, - struct mesh_config *cfg, - u32 *mask_out) -{ - struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; - u32 mask = 0; - -#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \ -do {\ - if (table[attr_num]) {\ - cfg->param = nla_fn(table[attr_num]); \ - mask |= (1 << (attr_num - 1)); \ - } \ -} while (0);\ - - - if (!info->attrs[NL80211_ATTR_MESH_CONFIG]) - return -EINVAL; - if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX, - info->attrs[NL80211_ATTR_MESH_CONFIG], - nl80211_meshconf_params_policy)) - return -EINVAL; - - /* This makes sure that there aren't more than 32 mesh config - * parameters (otherwise our bitfield scheme would not work.) */ - BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32); - - /* Fill in the params struct */ - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, - mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, - mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, - mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, - mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, - mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, - mask, NL80211_MESHCONF_TTL, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, - mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, - mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, - mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, - nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, - mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, - mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, - nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, - mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, - nla_get_u32); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, - mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, - nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshHWMPnetDiameterTraversalTime, - mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, - nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshHWMPRootMode, mask, - NL80211_MESHCONF_HWMP_ROOTMODE, - nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshHWMPRannInterval, mask, - NL80211_MESHCONF_HWMP_RANN_INTERVAL, - nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshGateAnnouncementProtocol, mask, - NL80211_MESHCONF_GATE_ANNOUNCEMENTS, - nla_get_u8); - if (mask_out) - *mask_out = mask; - - return 0; - -#undef FILL_IN_MESH_PARAM_IF_SET -} - -static int nl80211_parse_mesh_setup(struct genl_info *info, - struct mesh_setup *setup) -{ - struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1]; - - if (!info->attrs[NL80211_ATTR_MESH_SETUP]) - return -EINVAL; - if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX, - info->attrs[NL80211_ATTR_MESH_SETUP], - nl80211_mesh_setup_params_policy)) - return -EINVAL; - - if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL]) - setup->path_sel_proto = - (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ? - IEEE80211_PATH_PROTOCOL_VENDOR : - IEEE80211_PATH_PROTOCOL_HWMP; - - if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC]) - setup->path_metric = - (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ? - IEEE80211_PATH_METRIC_VENDOR : - IEEE80211_PATH_METRIC_AIRTIME; - - - if (tb[NL80211_MESH_SETUP_IE]) { - struct nlattr *ieattr = - tb[NL80211_MESH_SETUP_IE]; - if (!is_valid_ie_attr(ieattr)) - return -EINVAL; - setup->ie = nla_data(ieattr); - setup->ie_len = nla_len(ieattr); - } - setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]); - setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]); - - return 0; -} - -static int nl80211_update_mesh_config(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 wireless_dev *wdev = dev->ieee80211_ptr; - struct mesh_config cfg; - u32 mask; - int err; - - if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - if (!rdev->ops->update_mesh_config) - return -EOPNOTSUPP; - - err = nl80211_parse_mesh_config(info, &cfg, &mask); - if (err) - return err; - - wdev_lock(wdev); - if (!wdev->mesh_id_len) - err = -ENOLINK; - - if (!err) - err = rdev->ops->update_mesh_config(&rdev->wiphy, dev, - mask, &cfg); - - wdev_unlock(wdev); - - return err; -} - -static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) -{ - struct sk_buff *msg; - void *hdr = NULL; - struct nlattr *nl_reg_rules; - unsigned int i; - int err = -EINVAL; - - mutex_lock(&cfg80211_mutex); - - if (!cfg80211_regdomain) - goto out; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) { - err = -ENOBUFS; - goto out; - } - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_GET_REG); - if (!hdr) - goto put_failure; - - NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, - cfg80211_regdomain->alpha2); - - nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); - if (!nl_reg_rules) - goto nla_put_failure; - - for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { - struct nlattr *nl_reg_rule; - const struct ieee80211_reg_rule *reg_rule; - const struct ieee80211_freq_range *freq_range; - const struct ieee80211_power_rule *power_rule; - - reg_rule = &cfg80211_regdomain->reg_rules[i]; - freq_range = ®_rule->freq_range; - power_rule = ®_rule->power_rule; - - nl_reg_rule = nla_nest_start(msg, i); - if (!nl_reg_rule) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS, - reg_rule->flags); - NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START, - freq_range->start_freq_khz); - NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END, - freq_range->end_freq_khz); - NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW, - freq_range->max_bandwidth_khz); - NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, - power_rule->max_antenna_gain); - NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, - power_rule->max_eirp); - - nla_nest_end(msg, nl_reg_rule); - } - - nla_nest_end(msg, nl_reg_rules); - - genlmsg_end(msg, hdr); - err = genlmsg_reply(msg, info); - goto out; - -nla_put_failure: - genlmsg_cancel(msg, hdr); -put_failure: - nlmsg_free(msg); - err = -EMSGSIZE; -out: - mutex_unlock(&cfg80211_mutex); - return err; -} - -static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) -{ - struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; - struct nlattr *nl_reg_rule; - char *alpha2 = NULL; - int rem_reg_rules = 0, r = 0; - u32 num_rules = 0, rule_idx = 0, size_of_regd; - struct ieee80211_regdomain *rd = NULL; - - if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_REG_RULES]) - return -EINVAL; - - alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); - - nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], - rem_reg_rules) { - num_rules++; - if (num_rules > NL80211_MAX_SUPP_REG_RULES) - return -EINVAL; - } - - mutex_lock(&cfg80211_mutex); - - if (!reg_is_valid_request(alpha2)) { - r = -EINVAL; - goto bad_reg; - } - - size_of_regd = sizeof(struct ieee80211_regdomain) + - (num_rules * sizeof(struct ieee80211_reg_rule)); - - rd = kzalloc(size_of_regd, GFP_KERNEL); - if (!rd) { - r = -ENOMEM; - goto bad_reg; - } - - rd->n_reg_rules = num_rules; - rd->alpha2[0] = alpha2[0]; - rd->alpha2[1] = alpha2[1]; - - nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], - rem_reg_rules) { - nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, - nla_data(nl_reg_rule), nla_len(nl_reg_rule), - reg_rule_policy); - r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); - if (r) - goto bad_reg; - - rule_idx++; - - if (rule_idx > NL80211_MAX_SUPP_REG_RULES) { - r = -EINVAL; - goto bad_reg; - } - } - - BUG_ON(rule_idx != num_rules); - - r = set_regdom(rd); - - mutex_unlock(&cfg80211_mutex); - - return r; - - bad_reg: - mutex_unlock(&cfg80211_mutex); - kfree(rd); - return r; -} - -static int validate_scan_freqs(struct nlattr *freqs) -{ - struct nlattr *attr1, *attr2; - int n_channels = 0, tmp1, tmp2; - - nla_for_each_nested(attr1, freqs, tmp1) { - n_channels++; - /* - * Some hardware has a limited channel list for - * scanning, and it is pretty much nonsensical - * to scan for a channel twice, so disallow that - * and don't require drivers to check that the - * channel list they get isn't longer than what - * they can scan, as long as they can scan all - * the channels they registered at once. - */ - nla_for_each_nested(attr2, freqs, tmp2) - if (attr1 != attr2 && - nla_get_u32(attr1) == nla_get_u32(attr2)) - return 0; - } - - return n_channels; -} - -static int nl80211_trigger_scan(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 cfg80211_scan_request *request; - struct nlattr *attr; - struct wiphy *wiphy; - int err, tmp, n_ssids = 0, n_channels, i; - size_t ie_len; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - wiphy = &rdev->wiphy; - - if (!rdev->ops->scan) - return -EOPNOTSUPP; - - if (rdev->scan_req) - return -EBUSY; - - if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { - n_channels = validate_scan_freqs( - info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); - if (!n_channels) - return -EINVAL; - } else { - enum ieee80211_band band; - n_channels = 0; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; - } - - if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) - n_ssids++; - - if (n_ssids > wiphy->max_scan_ssids) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_IE]) - ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - else - ie_len = 0; - - if (ie_len > wiphy->max_scan_ie_len) - return -EINVAL; - - request = kzalloc(sizeof(*request) - + sizeof(*request->ssids) * n_ssids - + sizeof(*request->channels) * n_channels - + ie_len, GFP_KERNEL); - if (!request) - return -ENOMEM; - - if (n_ssids) - request->ssids = (void *)&request->channels[n_channels]; - request->n_ssids = n_ssids; - if (ie_len) { - if (request->ssids) - request->ie = (void *)(request->ssids + n_ssids); - else - request->ie = (void *)(request->channels + n_channels); - } - - i = 0; - if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { - /* user specified, bail out if channel not found */ - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { - struct ieee80211_channel *chan; - - chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); - - if (!chan) { - err = -EINVAL; - goto out_free; - } - - /* ignore disabled channels */ - if (chan->flags & IEEE80211_CHAN_DISABLED) - continue; - - request->channels[i] = chan; - i++; - } - } else { - enum ieee80211_band band; - - /* all channels */ - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - int j; - if (!wiphy->bands[band]) - continue; - for (j = 0; j < wiphy->bands[band]->n_channels; j++) { - struct ieee80211_channel *chan; - - chan = &wiphy->bands[band]->channels[j]; - - if (chan->flags & IEEE80211_CHAN_DISABLED) - continue; - - request->channels[i] = chan; - i++; - } - } - } - - if (!i) { - err = -EINVAL; - goto out_free; - } - - request->n_channels = i; - - i = 0; - if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { - if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) { - err = -EINVAL; - goto out_free; - } - request->ssids[i].ssid_len = nla_len(attr); - memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr)); - i++; - } - } - - if (info->attrs[NL80211_ATTR_IE]) { - request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - memcpy((void *)request->ie, - nla_data(info->attrs[NL80211_ATTR_IE]), - request->ie_len); - } - - for (i = 0; i < IEEE80211_NUM_BANDS; i++) - if (wiphy->bands[i]) - request->rates[i] = - (1 << wiphy->bands[i]->n_bitrates) - 1; - - if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) { - nla_for_each_nested(attr, - info->attrs[NL80211_ATTR_SCAN_SUPP_RATES], - tmp) { - enum ieee80211_band band = nla_type(attr); - - if (band < 0 || band >= IEEE80211_NUM_BANDS) { - err = -EINVAL; - goto out_free; - } - err = ieee80211_get_ratemask(wiphy->bands[band], - nla_data(attr), - nla_len(attr), - &request->rates[band]); - if (err) - goto out_free; - } - } - - request->no_cck = - nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); - - request->dev = dev; - request->wiphy = &rdev->wiphy; - - rdev->scan_req = request; - err = rdev->ops->scan(&rdev->wiphy, dev, request); - - if (!err) { - nl80211_send_scan_start(rdev, dev); - dev_hold(dev); - } else { - out_free: - rdev->scan_req = NULL; - kfree(request); - } - - return err; -} - -static int nl80211_start_sched_scan(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_sched_scan_request *request; - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *dev = info->user_ptr[1]; - struct nlattr *attr; - struct wiphy *wiphy; - int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i; - u32 interval; - enum ieee80211_band band; - size_t ie_len; - struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1]; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || - !rdev->ops->sched_scan_start) - return -EOPNOTSUPP; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]) - return -EINVAL; - - interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]); - if (interval == 0) - return -EINVAL; - - wiphy = &rdev->wiphy; - - if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { - n_channels = validate_scan_freqs( - info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); - if (!n_channels) - return -EINVAL; - } else { - n_channels = 0; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; - } - - if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], - tmp) - n_ssids++; - - if (n_ssids > wiphy->max_sched_scan_ssids) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) - nla_for_each_nested(attr, - info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], - tmp) - n_match_sets++; - - if (n_match_sets > wiphy->max_match_sets) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_IE]) - ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - else - ie_len = 0; - - if (ie_len > wiphy->max_sched_scan_ie_len) - return -EINVAL; - - mutex_lock(&rdev->sched_scan_mtx); - - if (rdev->sched_scan_req) { - err = -EINPROGRESS; - goto out; - } - - request = kzalloc(sizeof(*request) - + sizeof(*request->ssids) * n_ssids - + sizeof(*request->match_sets) * n_match_sets - + sizeof(*request->channels) * n_channels - + ie_len, GFP_KERNEL); - if (!request) { - err = -ENOMEM; - goto out; - } - - if (n_ssids) - request->ssids = (void *)&request->channels[n_channels]; - request->n_ssids = n_ssids; - if (ie_len) { - if (request->ssids) - request->ie = (void *)(request->ssids + n_ssids); - else - request->ie = (void *)(request->channels + n_channels); - } - - if (n_match_sets) { - if (request->ie) - request->match_sets = (void *)(request->ie + ie_len); - else if (request->ssids) - request->match_sets = - (void *)(request->ssids + n_ssids); - else - request->match_sets = - (void *)(request->channels + n_channels); - } - request->n_match_sets = n_match_sets; - - i = 0; - if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { - /* user specified, bail out if channel not found */ - nla_for_each_nested(attr, - info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], - tmp) { - struct ieee80211_channel *chan; - - chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); - - if (!chan) { - err = -EINVAL; - goto out_free; - } - - /* ignore disabled channels */ - if (chan->flags & IEEE80211_CHAN_DISABLED) - continue; - - request->channels[i] = chan; - i++; - } - } else { - /* all channels */ - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - int j; - if (!wiphy->bands[band]) - continue; - for (j = 0; j < wiphy->bands[band]->n_channels; j++) { - struct ieee80211_channel *chan; - - chan = &wiphy->bands[band]->channels[j]; - - if (chan->flags & IEEE80211_CHAN_DISABLED) - continue; - - request->channels[i] = chan; - i++; - } - } - } - - if (!i) { - err = -EINVAL; - goto out_free; - } - - request->n_channels = i; - - i = 0; - if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], - tmp) { - if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) { - err = -EINVAL; - goto out_free; - } - request->ssids[i].ssid_len = nla_len(attr); - memcpy(request->ssids[i].ssid, nla_data(attr), - nla_len(attr)); - i++; - } - } - - i = 0; - if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { - nla_for_each_nested(attr, - info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], - tmp) { - struct nlattr *ssid; - - nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, - nla_data(attr), nla_len(attr), - nl80211_match_policy); - ssid = tb[NL80211_ATTR_SCHED_SCAN_MATCH_SSID]; - if (ssid) { - if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) { - err = -EINVAL; - goto out_free; - } - memcpy(request->match_sets[i].ssid.ssid, - nla_data(ssid), nla_len(ssid)); - request->match_sets[i].ssid.ssid_len = - nla_len(ssid); - } - i++; - } - } - - if (info->attrs[NL80211_ATTR_IE]) { - request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - memcpy((void *)request->ie, - nla_data(info->attrs[NL80211_ATTR_IE]), - request->ie_len); - } - - request->dev = dev; - request->wiphy = &rdev->wiphy; - request->interval = interval; - - err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); - if (!err) { - rdev->sched_scan_req = request; - nl80211_send_sched_scan(rdev, dev, - NL80211_CMD_START_SCHED_SCAN); - goto out; - } - -out_free: - kfree(request); -out: - mutex_unlock(&rdev->sched_scan_mtx); - return err; -} - -static int nl80211_stop_sched_scan(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int err; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || - !rdev->ops->sched_scan_stop) - return -EOPNOTSUPP; - - mutex_lock(&rdev->sched_scan_mtx); - err = __cfg80211_stop_sched_scan(rdev, false); - mutex_unlock(&rdev->sched_scan_mtx); - - return err; -} - -static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, - u32 seq, int flags, - struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, - struct cfg80211_internal_bss *intbss) -{ - struct cfg80211_bss *res = &intbss->pub; - void *hdr; - struct nlattr *bss; - int i; - - ASSERT_WDEV_LOCK(wdev); - - hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags, - NL80211_CMD_NEW_SCAN_RESULTS); - if (!hdr) - return -1; - - genl_dump_check_consistent(cb, hdr, &nl80211_fam); - - NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex); - - bss = nla_nest_start(msg, NL80211_ATTR_BSS); - if (!bss) - goto nla_put_failure; - if (!is_zero_ether_addr(res->bssid)) - NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid); - if (res->information_elements && res->len_information_elements) - NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, - res->len_information_elements, - res->information_elements); - if (res->beacon_ies && res->len_beacon_ies && - res->beacon_ies != res->information_elements) - NLA_PUT(msg, NL80211_BSS_BEACON_IES, - res->len_beacon_ies, res->beacon_ies); - if (res->tsf) - NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); - if (res->beacon_interval) - NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval); - NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); - NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq); - NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO, - jiffies_to_msecs(jiffies - intbss->ts)); - - switch (rdev->wiphy.signal_type) { - case CFG80211_SIGNAL_TYPE_MBM: - NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal); - break; - case CFG80211_SIGNAL_TYPE_UNSPEC: - NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal); - break; - default: - break; - } - - switch (wdev->iftype) { - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_STATION: - if (intbss == wdev->current_bss) - NLA_PUT_U32(msg, NL80211_BSS_STATUS, - NL80211_BSS_STATUS_ASSOCIATED); - else for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (intbss != wdev->auth_bsses[i]) - continue; - NLA_PUT_U32(msg, NL80211_BSS_STATUS, - NL80211_BSS_STATUS_AUTHENTICATED); - break; - } - break; - case NL80211_IFTYPE_ADHOC: - if (intbss == wdev->current_bss) - NLA_PUT_U32(msg, NL80211_BSS_STATUS, - NL80211_BSS_STATUS_IBSS_JOINED); - break; - default: - break; - } - - nla_nest_end(msg, bss); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int nl80211_dump_scan(struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct cfg80211_registered_device *rdev; - struct net_device *dev; - struct cfg80211_internal_bss *scan; - struct wireless_dev *wdev; - int start = cb->args[1], idx = 0; - int err; - - err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev); - if (err) - return err; - - wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - spin_lock_bh(&rdev->bss_lock); - cfg80211_bss_expire(rdev); - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)) - cb->seq = rdev->bss_generation; -#endif - - list_for_each_entry(scan, &rdev->bss_list, list) { - if (++idx <= start) - continue; - if (nl80211_send_bss(skb, cb, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - rdev, wdev, scan) < 0) { - idx--; - break; - } - } - - spin_unlock_bh(&rdev->bss_lock); - wdev_unlock(wdev); - - cb->args[1] = idx; - nl80211_finish_netdev_dump(rdev); - - return skb->len; -} - -static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq, - int flags, struct net_device *dev, - struct survey_info *survey) -{ - void *hdr; - struct nlattr *infoattr; - - hdr = nl80211hdr_put(msg, pid, seq, flags, - NL80211_CMD_NEW_SURVEY_RESULTS); - if (!hdr) - return -ENOMEM; - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - - infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO); - if (!infoattr) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY, - survey->channel->center_freq); - if (survey->filled & SURVEY_INFO_NOISE_DBM) - NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE, - survey->noise); - if (survey->filled & SURVEY_INFO_IN_USE) - NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE); - if (survey->filled & SURVEY_INFO_CHANNEL_TIME) - NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME, - survey->channel_time); - if (survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) - NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, - survey->channel_time_busy); - if (survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) - NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, - survey->channel_time_ext_busy); - if (survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) - NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX, - survey->channel_time_rx); - if (survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) - NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX, - survey->channel_time_tx); - - nla_nest_end(msg, infoattr); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int nl80211_dump_survey(struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct survey_info survey; - struct cfg80211_registered_device *dev; - struct net_device *netdev; - int survey_idx = cb->args[1]; - int res; - - res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev); - if (res) - return res; - - if (!dev->ops->dump_survey) { - res = -EOPNOTSUPP; - goto out_err; - } - - while (1) { - struct ieee80211_channel *chan; - - res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx, - &survey); - if (res == -ENOENT) - break; - if (res) - goto out_err; - - /* Survey without a channel doesn't make sense */ - if (!survey.channel) { - res = -EINVAL; - goto out; - } - - chan = ieee80211_get_channel(&dev->wiphy, - survey.channel->center_freq); - if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) { - survey_idx++; - continue; - } - - if (nl80211_send_survey(skb, - NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - netdev, - &survey) < 0) - goto out; - survey_idx++; - } - - out: - cb->args[1] = survey_idx; - res = skb->len; - out_err: - nl80211_finish_netdev_dump(dev); - 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 | - NL80211_WPA_VERSION_2)); -} - -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; - enum nl80211_auth_type auth_type; - struct key_parse key; - bool local_state_change; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_AUTH_TYPE]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_SSID]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) - return -EINVAL; - - err = nl80211_parse_key(info, &key); - if (err) - return err; - - if (key.idx >= 0) { - if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP) - return -EINVAL; - if (!key.p.key || !key.p.key_len) - return -EINVAL; - if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 || - key.p.key_len != WLAN_KEY_LEN_WEP40) && - (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 || - key.p.key_len != WLAN_KEY_LEN_WEP104)) - return -EINVAL; - if (key.idx > 4) - return -EINVAL; - } else { - key.p.key_len = 0; - key.p.key = NULL; - } - - if (key.idx >= 0) { - int i; - bool ok = false; - for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) { - if (key.p.cipher == rdev->wiphy.cipher_suites[i]) { - ok = true; - break; - } - } - if (!ok) - return -EINVAL; - } - - if (!rdev->ops->auth) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - chan = ieee80211_get_channel(&rdev->wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) - return -EINVAL; - - ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); - - if (info->attrs[NL80211_ATTR_IE]) { - ie = nla_data(info->attrs[NL80211_ATTR_IE]); - ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); - if (!nl80211_valid_auth_type(auth_type)) - return -EINVAL; - - local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; - - 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, - local_state_change); -} - -static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, - struct genl_info *info, - struct cfg80211_crypto_settings *settings, - int cipher_limit) -{ - memset(settings, 0, sizeof(*settings)); - - settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; - - if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) { - u16 proto; - proto = nla_get_u16( - info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]); - settings->control_port_ethertype = cpu_to_be16(proto); - if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) && - proto != ETH_P_PAE) - return -EINVAL; - if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]) - settings->control_port_no_encrypt = true; - } else - settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE); - - if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) { - void *data; - int len, i; - - data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); - len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); - settings->n_ciphers_pairwise = len / sizeof(u32); - - if (len % sizeof(u32)) - return -EINVAL; - - if (settings->n_ciphers_pairwise > cipher_limit) - return -EINVAL; - - memcpy(settings->ciphers_pairwise, data, len); - - for (i = 0; i < settings->n_ciphers_pairwise; i++) - if (!cfg80211_supported_cipher_suite( - &rdev->wiphy, - settings->ciphers_pairwise[i])) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) { - settings->cipher_group = - nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]); - if (!cfg80211_supported_cipher_suite(&rdev->wiphy, - settings->cipher_group)) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) { - settings->wpa_versions = - nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]); - if (!nl80211_valid_wpa_versions(settings->wpa_versions)) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_AKM_SUITES]) { - void *data; - int len; - - data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]); - len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]); - settings->n_akm_suites = len / sizeof(u32); - - if (len % sizeof(u32)) - return -EINVAL; - - if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES) - return -EINVAL; - - memcpy(settings->akm_suites, data, len); - } - - return 0; -} - -static int nl80211_associate(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 cfg80211_crypto_settings crypto; - struct ieee80211_channel *chan; - const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; - int err, ssid_len, ie_len = 0; - bool use_mfp = false; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MAC] || - !info->attrs[NL80211_ATTR_SSID] || - !info->attrs[NL80211_ATTR_WIPHY_FREQ]) - return -EINVAL; - - if (!rdev->ops->assoc) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - - chan = ieee80211_get_channel(&rdev->wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) - return -EINVAL; - - ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); - - if (info->attrs[NL80211_ATTR_IE]) { - ie = nla_data(info->attrs[NL80211_ATTR_IE]); - ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - if (info->attrs[NL80211_ATTR_USE_MFP]) { - enum nl80211_mfp mfp = - nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]); - if (mfp == NL80211_MFP_REQUIRED) - use_mfp = true; - else if (mfp != NL80211_MFP_NO) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_PREV_BSSID]) - prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]); - - err = nl80211_crypto_settings(rdev, info, &crypto, 1); - if (!err) - err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, - ssid, ssid_len, ie, ie_len, use_mfp, - &crypto); - - return err; -} - -static int nl80211_deauthenticate(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]; - const u8 *ie = NULL, *bssid; - int ie_len = 0; - u16 reason_code; - bool local_state_change; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_REASON_CODE]) - return -EINVAL; - - if (!rdev->ops->deauth) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - - reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); - if (reason_code == 0) { - /* Reason Code 0 is reserved */ - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_IE]) { - ie = nla_data(info->attrs[NL80211_ATTR_IE]); - ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; - - return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, - local_state_change); -} - -static int nl80211_disassociate(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]; - const u8 *ie = NULL, *bssid; - int ie_len = 0; - u16 reason_code; - bool local_state_change; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_REASON_CODE]) - return -EINVAL; - - if (!rdev->ops->disassoc) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - - reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); - if (reason_code == 0) { - /* Reason Code 0 is reserved */ - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_IE]) { - ie = nla_data(info->attrs[NL80211_ATTR_IE]); - ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; - - return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, - local_state_change); -} - -static bool -nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev, - int mcast_rate[IEEE80211_NUM_BANDS], - int rateval) -{ - struct wiphy *wiphy = &rdev->wiphy; - bool found = false; - int band, i; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - struct ieee80211_supported_band *sband; - - sband = wiphy->bands[band]; - if (!sband) - continue; - - for (i = 0; i < sband->n_bitrates; i++) { - if (sband->bitrates[i].bitrate == rateval) { - mcast_rate[band] = i + 1; - found = true; - break; - } - } - } - - return found; -} - -static int nl80211_join_ibss(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 cfg80211_ibss_params ibss; - struct wiphy *wiphy; - struct cfg80211_cached_keys *connkeys = NULL; - int err; - - memset(&ibss, 0, sizeof(ibss)); - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || - !info->attrs[NL80211_ATTR_SSID] || - !nla_len(info->attrs[NL80211_ATTR_SSID])) - return -EINVAL; - - ibss.beacon_interval = 100; - - if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { - ibss.beacon_interval = - nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); - if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000) - return -EINVAL; - } - - if (!rdev->ops->join_ibss) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) - return -EOPNOTSUPP; - - wiphy = &rdev->wiphy; - - if (info->attrs[NL80211_ATTR_MAC]) { - ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (!is_valid_ether_addr(ibss.bssid)) - return -EINVAL; - } - ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); - - if (info->attrs[NL80211_ATTR_IE]) { - ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]); - ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - ibss.channel = ieee80211_get_channel(wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!ibss.channel || - ibss.channel->flags & IEEE80211_CHAN_NO_IBSS || - ibss.channel->flags & IEEE80211_CHAN_DISABLED) - return -EINVAL; - - ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; - ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; - - if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { - u8 *rates = - nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); - int n_rates = - nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); - struct ieee80211_supported_band *sband = - wiphy->bands[ibss.channel->band]; - int err; - - err = ieee80211_get_ratemask(sband, rates, n_rates, - &ibss.basic_rates); - if (err) - return err; - } - - if (info->attrs[NL80211_ATTR_MCAST_RATE] && - !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate, - nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) - return -EINVAL; - - if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { - connkeys = nl80211_parse_connkeys(rdev, - info->attrs[NL80211_ATTR_KEYS]); - if (IS_ERR(connkeys)) - return PTR_ERR(connkeys); - } - - err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); - if (err) - kfree(connkeys); - return err; -} - -static int nl80211_leave_ibss(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]; - - if (!rdev->ops->leave_ibss) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) - return -EOPNOTSUPP; - - return cfg80211_leave_ibss(rdev, dev, false); -} - -#ifdef CONFIG_NL80211_TESTMODE -static struct genl_multicast_group nl80211_testmode_mcgrp = { - .name = "testmode", -}; - -static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int err; - - if (!info->attrs[NL80211_ATTR_TESTDATA]) - return -EINVAL; - - err = -EOPNOTSUPP; - if (rdev->ops->testmode_cmd) { - rdev->testmode_info = info; - err = rdev->ops->testmode_cmd(&rdev->wiphy, - nla_data(info->attrs[NL80211_ATTR_TESTDATA]), - nla_len(info->attrs[NL80211_ATTR_TESTDATA])); - rdev->testmode_info = NULL; - } - - return err; -} - -static int nl80211_testmode_dump(struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct cfg80211_registered_device *dev; - int err; - long phy_idx; - void *data = NULL; - int data_len = 0; - - if (cb->args[0]) { - /* - * 0 is a valid index, but not valid for args[0], - * so we need to offset by 1. - */ - phy_idx = cb->args[0] - 1; - } else { - err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, - nl80211_fam.attrbuf, nl80211_fam.maxattr, - nl80211_policy); - if (err) - return err; - if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]) - return -EINVAL; - phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]); - if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]) - cb->args[1] = - (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]; - } - - if (cb->args[1]) { - data = nla_data((void *)cb->args[1]); - data_len = nla_len((void *)cb->args[1]); - } - - mutex_lock(&cfg80211_mutex); - dev = cfg80211_rdev_by_wiphy_idx(phy_idx); - if (!dev) { - mutex_unlock(&cfg80211_mutex); - return -ENOENT; - } - cfg80211_lock_rdev(dev); - mutex_unlock(&cfg80211_mutex); - - if (!dev->ops->testmode_dump) { - err = -EOPNOTSUPP; - goto out_err; - } - - while (1) { - void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - NL80211_CMD_TESTMODE); - struct nlattr *tmdata; - - if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) { - genlmsg_cancel(skb, hdr); - break; - } - - tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA); - if (!tmdata) { - genlmsg_cancel(skb, hdr); - break; - } - err = dev->ops->testmode_dump(&dev->wiphy, skb, cb, - data, data_len); - nla_nest_end(skb, tmdata); - - if (err == -ENOBUFS || err == -ENOENT) { - genlmsg_cancel(skb, hdr); - break; - } else if (err) { - genlmsg_cancel(skb, hdr); - goto out_err; - } - - genlmsg_end(skb, hdr); - } - - err = skb->len; - /* see above */ - cb->args[0] = phy_idx + 1; - out_err: - cfg80211_unlock_rdev(dev); - return err; -} - -static struct sk_buff * -__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, - int approxlen, u32 pid, u32 seq, gfp_t gfp) -{ - struct sk_buff *skb; - void *hdr; - struct nlattr *data; - - skb = nlmsg_new(approxlen + 100, gfp); - if (!skb) - return NULL; - - hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE); - if (!hdr) { - kfree_skb(skb); - return NULL; - } - - NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - data = nla_nest_start(skb, NL80211_ATTR_TESTDATA); - - ((void **)skb->cb)[0] = rdev; - ((void **)skb->cb)[1] = hdr; - ((void **)skb->cb)[2] = data; - - return skb; - - nla_put_failure: - kfree_skb(skb); - return NULL; -} - -struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, - int approxlen) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - if (WARN_ON(!rdev->testmode_info)) - return NULL; - - return __cfg80211_testmode_alloc_skb(rdev, approxlen, - rdev->testmode_info->snd_pid, - rdev->testmode_info->snd_seq, - GFP_KERNEL); -} -EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb); - -int cfg80211_testmode_reply(struct sk_buff *skb) -{ - struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; - void *hdr = ((void **)skb->cb)[1]; - struct nlattr *data = ((void **)skb->cb)[2]; - - if (WARN_ON(!rdev->testmode_info)) { - kfree_skb(skb); - return -EINVAL; - } - - nla_nest_end(skb, data); - genlmsg_end(skb, hdr); - return genlmsg_reply(skb, rdev->testmode_info); -} -EXPORT_SYMBOL(cfg80211_testmode_reply); - -struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, - int approxlen, gfp_t gfp) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp); -} -EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); - -void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) -{ - void *hdr = ((void **)skb->cb)[1]; - struct nlattr *data = ((void **)skb->cb)[2]; - - nla_nest_end(skb, data); - genlmsg_end(skb, hdr); - genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp); -} -EXPORT_SYMBOL(cfg80211_testmode_event); -#endif - -static int nl80211_connect(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 cfg80211_connect_params connect; - struct wiphy *wiphy; - struct cfg80211_cached_keys *connkeys = NULL; - int err; - - memset(&connect, 0, sizeof(connect)); - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_SSID] || - !nla_len(info->attrs[NL80211_ATTR_SSID])) - return -EINVAL; - - 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)) - return -EINVAL; - } else - connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; - - connect.privacy = info->attrs[NL80211_ATTR_PRIVACY]; - - err = nl80211_crypto_settings(rdev, info, &connect.crypto, - NL80211_MAX_NR_CIPHER_SUITES); - if (err) - return err; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - wiphy = &rdev->wiphy; - - if (info->attrs[NL80211_ATTR_MAC]) - connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); - - if (info->attrs[NL80211_ATTR_IE]) { - connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]); - connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - connect.channel = - ieee80211_get_channel(wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!connect.channel || - connect.channel->flags & IEEE80211_CHAN_DISABLED) - return -EINVAL; - } - - if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) { - connkeys = nl80211_parse_connkeys(rdev, - info->attrs[NL80211_ATTR_KEYS]); - if (IS_ERR(connkeys)) - return PTR_ERR(connkeys); - } - - err = cfg80211_connect(rdev, dev, &connect, connkeys); - if (err) - kfree(connkeys); - return err; -} - -static int nl80211_disconnect(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]; - u16 reason; - - if (!info->attrs[NL80211_ATTR_REASON_CODE]) - reason = WLAN_REASON_DEAUTH_LEAVING; - else - reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); - - if (reason == 0) - return -EINVAL; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - return cfg80211_disconnect(rdev, dev, reason, true); -} - -static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net *net; - int err; - u32 pid; - - if (!info->attrs[NL80211_ATTR_PID]) - return -EINVAL; - - pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]); - - net = get_net_ns_by_pid(pid); - if (IS_ERR(net)) - return PTR_ERR(net); - - err = 0; - - /* check if anything to do */ - if (!net_eq(wiphy_net(&rdev->wiphy), net)) - err = cfg80211_switch_netns(rdev, net); - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - put_net(net); -#endif - return err; -} - -static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_pmksa *pmksa) = NULL; - struct net_device *dev = info->user_ptr[1]; - struct cfg80211_pmksa pmksa; - - memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_PMKID]) - return -EINVAL; - - pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]); - pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - switch (info->genlhdr->cmd) { - case NL80211_CMD_SET_PMKSA: - rdev_ops = rdev->ops->set_pmksa; - break; - case NL80211_CMD_DEL_PMKSA: - rdev_ops = rdev->ops->del_pmksa; - break; - default: - WARN_ON(1); - break; - } - - if (!rdev_ops) - return -EOPNOTSUPP; - - return rdev_ops(&rdev->wiphy, dev, &pmksa); -} - -static int nl80211_flush_pmksa(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]; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - if (!rdev->ops->flush_pmksa) - return -EOPNOTSUPP; - - return rdev->ops->flush_pmksa(&rdev->wiphy, dev); -} - -static int nl80211_tdls_mgmt(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]; - u8 action_code, dialog_token; - u16 status_code; - u8 *peer; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || - !rdev->ops->tdls_mgmt) - return -EOPNOTSUPP; - - if (!info->attrs[NL80211_ATTR_TDLS_ACTION] || - !info->attrs[NL80211_ATTR_STATUS_CODE] || - !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] || - !info->attrs[NL80211_ATTR_IE] || - !info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - peer = nla_data(info->attrs[NL80211_ATTR_MAC]); - action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]); - 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])); -} - -static int nl80211_tdls_oper(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]; - enum nl80211_tdls_operation operation; - u8 *peer; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || - !rdev->ops->tdls_oper) - return -EOPNOTSUPP; - - if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] || - !info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - 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); -} - -static int nl80211_remain_on_channel(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; - struct sk_buff *msg; - void *hdr; - u64 cookie; - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - u32 freq, duration; - int err; - - if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || - !info->attrs[NL80211_ATTR_DURATION]) - return -EINVAL; - - duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); - - /* - * We should be on that channel for at least one jiffie, - * and more than 5 seconds seems excessive. - */ - if (!duration || !msecs_to_jiffies(duration) || - duration > rdev->wiphy.max_remain_on_channel_duration) - return -EINVAL; - - if (!rdev->ops->remain_on_channel) - return -EOPNOTSUPP; - - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { - channel_type = nla_get_u32( - info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); - if (channel_type != NL80211_CHAN_NO_HT && - channel_type != NL80211_CHAN_HT20 && - channel_type != NL80211_CHAN_HT40PLUS && - channel_type != NL80211_CHAN_HT40MINUS) - return -EINVAL; - } - - freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - chan = rdev_freq_to_chan(rdev, freq, channel_type); - if (chan == NULL) - return -EINVAL; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_REMAIN_ON_CHANNEL); - - if (IS_ERR(hdr)) { - err = PTR_ERR(hdr); - goto free_msg; - } - - err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan, - channel_type, duration, &cookie); - - if (err) - goto free_msg; - - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); - - genlmsg_end(msg, hdr); - - return genlmsg_reply(msg, info); - - nla_put_failure: - err = -ENOBUFS; - free_msg: - nlmsg_free(msg); - return err; -} - -static int nl80211_cancel_remain_on_channel(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]; - u64 cookie; - - if (!info->attrs[NL80211_ATTR_COOKIE]) - return -EINVAL; - - if (!rdev->ops->cancel_remain_on_channel) - return -EOPNOTSUPP; - - cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); - - return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie); -} - -static u32 rateset_to_mask(struct ieee80211_supported_band *sband, - u8 *rates, u8 rates_len) -{ - u8 i; - u32 mask = 0; - - for (i = 0; i < rates_len; i++) { - int rate = (rates[i] & 0x7f) * 5; - int ridx; - for (ridx = 0; ridx < sband->n_bitrates; ridx++) { - struct ieee80211_rate *srate = - &sband->bitrates[ridx]; - if (rate == srate->bitrate) { - mask |= 1 << ridx; - break; - } - } - if (ridx == sband->n_bitrates) - return 0; /* rate not found */ - } - - return mask; -} - -static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { - [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, - .len = NL80211_MAX_SUPP_RATES }, -}; - -static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, - struct genl_info *info) -{ - struct nlattr *tb[NL80211_TXRATE_MAX + 1]; - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct cfg80211_bitrate_mask mask; - int rem, i; - struct net_device *dev = info->user_ptr[1]; - struct nlattr *tx_rates; - struct ieee80211_supported_band *sband; - - if (info->attrs[NL80211_ATTR_TX_RATES] == NULL) - return -EINVAL; - - if (!rdev->ops->set_bitrate_mask) - return -EOPNOTSUPP; - - memset(&mask, 0, sizeof(mask)); - /* Default to all rates enabled */ - for (i = 0; i < IEEE80211_NUM_BANDS; i++) { - sband = rdev->wiphy.bands[i]; - mask.control[i].legacy = - sband ? (1 << sband->n_bitrates) - 1 : 0; - } - - /* - * The nested attribute uses enum nl80211_band as the index. This maps - * directly to the enum ieee80211_band values used in cfg80211. - */ - nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) - { - enum ieee80211_band band = nla_type(tx_rates); - if (band < 0 || band >= IEEE80211_NUM_BANDS) - return -EINVAL; - sband = rdev->wiphy.bands[band]; - if (sband == NULL) - return -EINVAL; - nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), - nla_len(tx_rates), nl80211_txattr_policy); - if (tb[NL80211_TXRATE_LEGACY]) { - mask.control[band].legacy = rateset_to_mask( - sband, - nla_data(tb[NL80211_TXRATE_LEGACY]), - nla_len(tb[NL80211_TXRATE_LEGACY])); - if (mask.control[band].legacy == 0) - return -EINVAL; - } - } - - return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask); -} - -static int nl80211_register_mgmt(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]; - u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION; - - if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_FRAME_TYPE]) - frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]); - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - /* not much point in registering if we can't reply */ - if (!rdev->ops->mgmt_tx) - return -EOPNOTSUPP; - - return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid, - frame_type, - nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), - nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); -} - -static int nl80211_tx_mgmt(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; - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - bool channel_type_valid = false; - u32 freq; - int err; - void *hdr = NULL; - u64 cookie; - struct sk_buff *msg = NULL; - unsigned int wait = 0; - bool offchan, no_cck, dont_wait_for_ack; - - dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK]; - - if (!info->attrs[NL80211_ATTR_FRAME] || - !info->attrs[NL80211_ATTR_WIPHY_FREQ]) - return -EINVAL; - - if (!rdev->ops->mgmt_tx) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - if (info->attrs[NL80211_ATTR_DURATION]) { - if (!rdev->ops->mgmt_tx_cancel_wait) - return -EINVAL; - wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); - } - - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { - channel_type = nla_get_u32( - info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); - if (channel_type != NL80211_CHAN_NO_HT && - channel_type != NL80211_CHAN_HT20 && - channel_type != NL80211_CHAN_HT40PLUS && - channel_type != NL80211_CHAN_HT40MINUS) - return -EINVAL; - channel_type_valid = true; - } - - offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; - - no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); - - freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - chan = rdev_freq_to_chan(rdev, freq, channel_type); - if (chan == NULL) - return -EINVAL; - - if (!dont_wait_for_ack) { - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_FRAME); - - if (IS_ERR(hdr)) { - err = PTR_ERR(hdr); - goto free_msg; - } - } - - err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type, - channel_type_valid, wait, - nla_data(info->attrs[NL80211_ATTR_FRAME]), - nla_len(info->attrs[NL80211_ATTR_FRAME]), - no_cck, dont_wait_for_ack, &cookie); - if (err) - goto free_msg; - - if (msg) { - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); - - genlmsg_end(msg, hdr); - return genlmsg_reply(msg, info); - } - - return 0; - - nla_put_failure: - err = -ENOBUFS; - free_msg: - nlmsg_free(msg); - return err; -} - -static int nl80211_tx_mgmt_cancel_wait(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]; - u64 cookie; - - if (!info->attrs[NL80211_ATTR_COOKIE]) - return -EINVAL; - - if (!rdev->ops->mgmt_tx_cancel_wait) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); - - return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie); -} - -static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct wireless_dev *wdev; - struct net_device *dev = info->user_ptr[1]; - u8 ps_state; - bool state; - int err; - - if (!info->attrs[NL80211_ATTR_PS_STATE]) - return -EINVAL; - - ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]); - - if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) - return -EINVAL; - - wdev = dev->ieee80211_ptr; - - if (!rdev->ops->set_power_mgmt) - return -EOPNOTSUPP; - - state = (ps_state == NL80211_PS_ENABLED) ? true : false; - - if (state == wdev->ps) - return 0; - - err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state, - wdev->ps_timeout); - if (!err) - wdev->ps = state; - return err; -} - -static int nl80211_priv_cmd(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct wireless_dev *wdev; - struct net_device *dev = info->user_ptr[1]; - int err; - - if (!info->attrs[NL80211_ATTR_PRIV_CMD]) - return -EINVAL; - - wdev = dev->ieee80211_ptr; - - if (!rdev->ops->priv_cmd) - return -EOPNOTSUPP; - - err = rdev->ops->priv_cmd(wdev->wiphy, dev, - nla_data(info->attrs[NL80211_ATTR_PRIV_CMD])); - - return err; -} - -static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - enum nl80211_ps_state ps_state; - struct wireless_dev *wdev; - struct net_device *dev = info->user_ptr[1]; - struct sk_buff *msg; - void *hdr; - int err; - - wdev = dev->ieee80211_ptr; - - if (!rdev->ops->set_power_mgmt) - return -EOPNOTSUPP; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_GET_POWER_SAVE); - if (!hdr) { - err = -ENOBUFS; - goto free_msg; - } - - if (wdev->ps) - ps_state = NL80211_PS_ENABLED; - else - ps_state = NL80211_PS_DISABLED; - - NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state); - - genlmsg_end(msg, hdr); - return genlmsg_reply(msg, info); - - nla_put_failure: - err = -ENOBUFS; - free_msg: - nlmsg_free(msg); - return err; -} - -static struct nla_policy -nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { - [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, - [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, - [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, -}; - -static int nl80211_set_cqm_rssi(struct genl_info *info, - s32 threshold, u32 hysteresis) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct wireless_dev *wdev; - struct net_device *dev = info->user_ptr[1]; - - if (threshold > 0) - return -EINVAL; - - wdev = dev->ieee80211_ptr; - - if (!rdev->ops->set_cqm_rssi_config) - return -EOPNOTSUPP; - - if (wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev, - threshold, hysteresis); -} - -static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) -{ - struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1]; - struct nlattr *cqm; - int err; - - cqm = info->attrs[NL80211_ATTR_CQM]; - if (!cqm) { - err = -EINVAL; - goto out; - } - - err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm, - nl80211_attr_cqm_policy); - if (err) - goto out; - - if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && - attrs[NL80211_ATTR_CQM_RSSI_HYST]) { - s32 threshold; - u32 hysteresis; - threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); - hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); - err = nl80211_set_cqm_rssi(info, threshold, hysteresis); - } else - err = -EINVAL; - -out: - return err; -} - -static int nl80211_join_mesh(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 mesh_config cfg; - struct mesh_setup setup; - int err; - - /* start with default */ - memcpy(&cfg, &default_mesh_config, sizeof(cfg)); - memcpy(&setup, &default_mesh_setup, sizeof(setup)); - - if (info->attrs[NL80211_ATTR_MESH_CONFIG]) { - /* and parse parameters if given */ - err = nl80211_parse_mesh_config(info, &cfg, NULL); - if (err) - return err; - } - - if (!info->attrs[NL80211_ATTR_MESH_ID] || - !nla_len(info->attrs[NL80211_ATTR_MESH_ID])) - return -EINVAL; - - setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); - setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); - - if (info->attrs[NL80211_ATTR_MESH_SETUP]) { - /* parse additional setup parameters if given */ - err = nl80211_parse_mesh_setup(info, &setup); - if (err) - return err; - } - - return cfg80211_join_mesh(rdev, dev, &setup, &cfg); -} - -static int nl80211_leave_mesh(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]; - - return cfg80211_leave_mesh(rdev, dev); -} - -static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct sk_buff *msg; - void *hdr; - - if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) - return -EOPNOTSUPP; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_GET_WOWLAN); - if (!hdr) - goto nla_put_failure; - - if (rdev->wowlan) { - struct nlattr *nl_wowlan; - - nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); - if (!nl_wowlan) - goto nla_put_failure; - - if (rdev->wowlan->any) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); - if (rdev->wowlan->disconnect) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); - if (rdev->wowlan->magic_pkt) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); - if (rdev->wowlan->gtk_rekey_failure) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); - if (rdev->wowlan->eap_identity_req) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); - if (rdev->wowlan->four_way_handshake) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); - if (rdev->wowlan->rfkill_release) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); - if (rdev->wowlan->n_patterns) { - struct nlattr *nl_pats, *nl_pat; - int i, pat_len; - - nl_pats = nla_nest_start(msg, - NL80211_WOWLAN_TRIG_PKT_PATTERN); - if (!nl_pats) - goto nla_put_failure; - - for (i = 0; i < rdev->wowlan->n_patterns; i++) { - nl_pat = nla_nest_start(msg, i + 1); - if (!nl_pat) - goto nla_put_failure; - pat_len = rdev->wowlan->patterns[i].pattern_len; - NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK, - DIV_ROUND_UP(pat_len, 8), - rdev->wowlan->patterns[i].mask); - NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN, - pat_len, - rdev->wowlan->patterns[i].pattern); - nla_nest_end(msg, nl_pat); - } - nla_nest_end(msg, nl_pats); - } - - nla_nest_end(msg, nl_wowlan); - } - - genlmsg_end(msg, hdr); - return genlmsg_reply(msg, info); - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; -} - -static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG]; - struct cfg80211_wowlan no_triggers = {}; - struct cfg80211_wowlan new_triggers = {}; - struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan; - int err, i; - - if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) - return -EOPNOTSUPP; - - if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) - goto no_triggers; - - err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG, - nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), - nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), - nl80211_wowlan_policy); - if (err) - return err; - - if (tb[NL80211_WOWLAN_TRIG_ANY]) { - if (!(wowlan->flags & WIPHY_WOWLAN_ANY)) - return -EINVAL; - new_triggers.any = true; - } - - if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) { - if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT)) - return -EINVAL; - new_triggers.disconnect = true; - } - - if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) { - if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT)) - return -EINVAL; - new_triggers.magic_pkt = true; - } - - if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED]) - return -EINVAL; - - if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) { - if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)) - return -EINVAL; - new_triggers.gtk_rekey_failure = true; - } - - if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) { - if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)) - return -EINVAL; - new_triggers.eap_identity_req = true; - } - - if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) { - if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)) - return -EINVAL; - new_triggers.four_way_handshake = true; - } - - if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) { - if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE)) - return -EINVAL; - new_triggers.rfkill_release = true; - } - - if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { - struct nlattr *pat; - int n_patterns = 0; - int rem, pat_len, mask_len; - struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT]; - - nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], - rem) - n_patterns++; - if (n_patterns > wowlan->n_patterns) - return -EINVAL; - - new_triggers.patterns = kcalloc(n_patterns, - sizeof(new_triggers.patterns[0]), - GFP_KERNEL); - if (!new_triggers.patterns) - return -ENOMEM; - - new_triggers.n_patterns = n_patterns; - i = 0; - - nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], - rem) { - nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT, - nla_data(pat), nla_len(pat), NULL); - err = -EINVAL; - if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] || - !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]) - goto error; - pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]); - mask_len = DIV_ROUND_UP(pat_len, 8); - if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) != - mask_len) - goto error; - if (pat_len > wowlan->pattern_max_len || - pat_len < wowlan->pattern_min_len) - goto error; - - new_triggers.patterns[i].mask = - kmalloc(mask_len + pat_len, GFP_KERNEL); - if (!new_triggers.patterns[i].mask) { - err = -ENOMEM; - goto error; - } - new_triggers.patterns[i].pattern = - new_triggers.patterns[i].mask + mask_len; - memcpy(new_triggers.patterns[i].mask, - nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]), - mask_len); - new_triggers.patterns[i].pattern_len = pat_len; - memcpy(new_triggers.patterns[i].pattern, - nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]), - pat_len); - i++; - } - } - - if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) { - struct cfg80211_wowlan *ntrig; - ntrig = kmemdup(&new_triggers, sizeof(new_triggers), - GFP_KERNEL); - if (!ntrig) { - err = -ENOMEM; - goto error; - } - cfg80211_rdev_free_wowlan(rdev); - rdev->wowlan = ntrig; - } else { - no_triggers: - cfg80211_rdev_free_wowlan(rdev); - rdev->wowlan = NULL; - } - - return 0; - error: - for (i = 0; i < new_triggers.n_patterns; i++) - kfree(new_triggers.patterns[i].mask); - kfree(new_triggers.patterns); - return err; -} - -static int nl80211_set_rekey_data(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 wireless_dev *wdev = dev->ieee80211_ptr; - struct nlattr *tb[NUM_NL80211_REKEY_DATA]; - struct cfg80211_gtk_rekey_data rekey_data; - int err; - - if (!info->attrs[NL80211_ATTR_REKEY_DATA]) - return -EINVAL; - - err = nla_parse(tb, MAX_NL80211_REKEY_DATA, - nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]), - nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]), - nl80211_rekey_policy); - if (err) - return err; - - if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN) - return -ERANGE; - if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN) - return -ERANGE; - if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN) - return -ERANGE; - - memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]), - NL80211_KEK_LEN); - memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]), - NL80211_KCK_LEN); - memcpy(rekey_data.replay_ctr, - nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]), - NL80211_REPLAY_CTR_LEN); - - wdev_lock(wdev); - if (!wdev->current_bss) { - err = -ENOTCONN; - goto out; - } - - if (!rdev->ops->set_rekey_data) { - err = -EOPNOTSUPP; - goto out; - } - - err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data); - out: - wdev_unlock(wdev); - return err; -} - -static int nl80211_register_unexpected_frame(struct sk_buff *skb, - struct genl_info *info) -{ - struct net_device *dev = info->user_ptr[1]; - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO) - return -EINVAL; - - if (wdev->ap_unexpected_nlpid) - return -EBUSY; - - wdev->ap_unexpected_nlpid = info->snd_pid; - return 0; -} - -static int nl80211_probe_client(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 wireless_dev *wdev = dev->ieee80211_ptr; - struct sk_buff *msg; - void *hdr; - const u8 *addr; - u64 cookie; - int err; - - if (wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!rdev->ops->probe_client) - return -EOPNOTSUPP; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_PROBE_CLIENT); - - if (IS_ERR(hdr)) { - err = PTR_ERR(hdr); - goto free_msg; - } - - addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie); - if (err) - goto free_msg; - - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); - - genlmsg_end(msg, hdr); - - return genlmsg_reply(msg, info); - - nla_put_failure: - err = -ENOBUFS; - free_msg: - nlmsg_free(msg); - return err; -} - -static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS)) - return -EOPNOTSUPP; - - if (rdev->ap_beacons_nlpid) - return -EBUSY; - - rdev->ap_beacons_nlpid = info->snd_pid; - - return 0; -} - -static int nl80211_btcoex_notify_inq(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - bool scan_status; - - if (!dev) - return -ENODEV; - - scan_status = !!info->attrs[NL80211_ATTR_BTCOEX_INQ_STATUS]; - - if (!dev->ops->notify_btcoex_inq_status) - return -EOPNOTSUPP; - - return dev->ops->notify_btcoex_inq_status(&dev->wiphy, scan_status); -} - -static int nl80211_btcoex_notify_sco(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - int tx_interval = 0; - int tx_pkt_len = 0; - bool sco_status; - bool esco; - - if (!dev) - return -ENODEV; - - sco_status = !!info->attrs[NL80211_ATTR_BTCOEX_SCO_STATUS]; - esco = !!info->attrs[NL80211_ATTR_BTCOEX_TYPE_ESCO]; - - if (esco) { - if (info->attrs[NL80211_ATTR_BTCOEX_ESCO_TX_INTERVAL]) - tx_interval = nla_get_u32(info->attrs - [NL80211_ATTR_BTCOEX_ESCO_TX_INTERVAL]); - - if (info->attrs[NL80211_ATTR_BTCOEX_ESCO_TX_PKT_LEN]) - tx_pkt_len = nla_get_u32(info->attrs - [NL80211_ATTR_BTCOEX_ESCO_TX_PKT_LEN]); - } - if (!dev->ops->notify_btcoex_sco_status) - return -EOPNOTSUPP; - - return dev->ops->notify_btcoex_sco_status(&dev->wiphy, sco_status, - esco, tx_interval, - tx_pkt_len); -} - -static int nl80211_btcoex_notify_a2dp(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - bool a2dp_status; - - if (!dev) - return -ENODEV; - - a2dp_status = !!info->attrs[NL80211_ATTR_BTCOEX_A2DP_STATUS]; - - if (!dev->ops->notify_btcoex_a2dp_status) - return -EOPNOTSUPP; - - return dev->ops->notify_btcoex_a2dp_status(&dev->wiphy, a2dp_status); -} - -static int nl80211_btcoex_notify_acl_info(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - enum nl80211_btcoex_acl_role role = NL80211_BTCOEX_ACL_ROLE_UNKNOWN; - u32 remote_lmp_ver = 0; - - if (!dev) - return -ENODEV; - - if (info->attrs[NL80211_ATTR_BTCOEX_ACL_ROLE]) - role = nla_get_u32(info->attrs[NL80211_ATTR_BTCOEX_ACL_ROLE]); - - if (info->attrs[NL80211_ATTR_BTCOEX_REMOTE_LMP_VER]) - remote_lmp_ver = nla_get_u32(info->attrs - [NL80211_ATTR_BTCOEX_REMOTE_LMP_VER]); - - if (!dev->ops->notify_btcoex_acl_info) - return -EOPNOTSUPP; - - return dev->ops->notify_btcoex_acl_info(&dev->wiphy, role, - remote_lmp_ver); -} -static int nl80211_btcoex_notify_antenna_config(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - enum nl80211_btcoex_antenna_config config = NL80211_BTCOEX_ANTENNA_DA; - - if (!dev) - return -ENODEV; - - if (!info->attrs[NL80211_ATTR_BTCOEX_ANTENNA_CONFIG]) - return -EINVAL; - - config = nla_get_u32(info->attrs[NL80211_ATTR_BTCOEX_ANTENNA_CONFIG]); - - if (!dev->ops->notify_btcoex_antenna_config) - return -EOPNOTSUPP; - - return dev->ops->notify_btcoex_antenna_config(&dev->wiphy, config); -} - -static int nl80211_btcoex_notify(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - u8 *buf; - int len; - - if (!dev) - return -ENODEV; - - if (!info->attrs[NL80211_ATTR_BTCOEX_DATA]) - return -EINVAL; - - if (!dev->ops->notify_btcoex) - return -EOPNOTSUPP; - - buf = (char *)nla_data(info->attrs[NL80211_ATTR_BTCOEX_DATA]); - len = nla_len(info->attrs[NL80211_ATTR_BTCOEX_DATA]); - - return dev->ops->notify_btcoex(&dev->wiphy, buf, len); -} - -static int nl80211_p2p_flush_notify(struct sk_buff *skb, - struct genl_info *info) -{ - - struct cfg80211_registered_device *dev = info->user_ptr[0]; - printk("%s() enter steven\n", __func__); - - if (!dev) - return -ENODEV; - - if (!dev->ops->notify_p2p_flush) - return -EOPNOTSUPP; - - return dev->ops->notify_p2p_flush(&dev->wiphy); -} - -static int nl80211_btcoex_notify_bt_vendor(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - enum nl80211_btcoex_antenna_config config = - NL80211_BTCOEX_VENDOR_DEFAULT; - - if (!dev) - return -ENODEV; - - if (!info->attrs[NL80211_ATTR_BT_VENDOR_ID]) - return -EINVAL; - - config = nla_get_u32(info->attrs[NL80211_ATTR_BT_VENDOR_ID]); - - if (!dev->ops->notify_btcoex_bt_vendor) - return -EOPNOTSUPP; - - return dev->ops->notify_btcoex_bt_vendor(&dev->wiphy, config); -} -#define NL80211_FLAG_NEED_WIPHY 0x01 -#define NL80211_FLAG_NEED_NETDEV 0x02 -#define NL80211_FLAG_NEED_RTNL 0x04 -#define NL80211_FLAG_CHECK_NETDEV_UP 0x08 -#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\ - NL80211_FLAG_CHECK_NETDEV_UP) - -static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *rdev; - struct net_device *dev; - int err; - bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL; - - if (rtnl) - rtnl_lock(); - - if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) { - rdev = cfg80211_get_dev_from_info(info); - if (IS_ERR(rdev)) { - if (rtnl) - rtnl_unlock(); - return PTR_ERR(rdev); - } - info->user_ptr[0] = rdev; - } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) { - err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); - if (err) { - if (rtnl) - rtnl_unlock(); - return err; - } - if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && - !netif_running(dev)) { - cfg80211_unlock_rdev(rdev); - dev_put(dev); - if (rtnl) - rtnl_unlock(); - return -ENETDOWN; - } - info->user_ptr[0] = rdev; - info->user_ptr[1] = dev; - } - - return 0; -} - -static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info) -{ - if (info->user_ptr[0]) - cfg80211_unlock_rdev(info->user_ptr[0]); - if (info->user_ptr[1]) - dev_put(info->user_ptr[1]); - if (ops->internal_flags & NL80211_FLAG_NEED_RTNL) - rtnl_unlock(); -} - -static struct genl_ops nl80211_ops[] = { - { - .cmd = NL80211_CMD_GET_WIPHY, - .doit = nl80211_get_wiphy, - .dumpit = nl80211_dump_wiphy, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_WIPHY, - }, - { - .cmd = NL80211_CMD_SET_WIPHY, - .doit = nl80211_set_wiphy, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_INTERFACE, - .doit = nl80211_get_interface, - .dumpit = nl80211_dump_interface, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_NETDEV, - }, - { - .cmd = NL80211_CMD_SET_INTERFACE, - .doit = nl80211_set_interface, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_NEW_INTERFACE, - .doit = nl80211_new_interface, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_WIPHY | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEL_INTERFACE, - .doit = nl80211_del_interface, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_KEY, - .doit = nl80211_get_key, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_KEY, - .doit = nl80211_set_key, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_NEW_KEY, - .doit = nl80211_new_key, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEL_KEY, - .doit = nl80211_del_key, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_BEACON, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .doit = nl80211_addset_beacon, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_NEW_BEACON, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .doit = nl80211_addset_beacon, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEL_BEACON, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .doit = nl80211_del_beacon, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_STATION, - .doit = nl80211_get_station, - .dumpit = nl80211_dump_station, - .policy = nl80211_policy, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_STATION, - .doit = nl80211_set_station, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_NEW_STATION, - .doit = nl80211_new_station, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEL_STATION, - .doit = nl80211_del_station, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_MPATH, - .doit = nl80211_get_mpath, - .dumpit = nl80211_dump_mpath, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_MPATH, - .doit = nl80211_set_mpath, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_NEW_MPATH, - .doit = nl80211_new_mpath, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEL_MPATH, - .doit = nl80211_del_mpath, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_BSS, - .doit = nl80211_set_bss, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_REG, - .doit = nl80211_get_reg, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - }, - { - .cmd = NL80211_CMD_SET_REG, - .doit = nl80211_set_reg, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - }, - { - .cmd = NL80211_CMD_REQ_SET_REG, - .doit = nl80211_req_set_reg, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - }, - { - .cmd = NL80211_CMD_GET_MESH_CONFIG, - .doit = nl80211_get_mesh_config, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_MESH_CONFIG, - .doit = nl80211_update_mesh_config, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_TRIGGER_SCAN, - .doit = nl80211_trigger_scan, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_SCAN, - .policy = nl80211_policy, - .dumpit = nl80211_dump_scan, - }, - { - .cmd = NL80211_CMD_START_SCHED_SCAN, - .doit = nl80211_start_sched_scan, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_STOP_SCHED_SCAN, - .doit = nl80211_stop_sched_scan, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_AUTHENTICATE, - .doit = nl80211_authenticate, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_ASSOCIATE, - .doit = nl80211_associate, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEAUTHENTICATE, - .doit = nl80211_deauthenticate, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DISASSOCIATE, - .doit = nl80211_disassociate, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_JOIN_IBSS, - .doit = nl80211_join_ibss, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_LEAVE_IBSS, - .doit = nl80211_leave_ibss, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, -#ifdef CONFIG_NL80211_TESTMODE - { - .cmd = NL80211_CMD_TESTMODE, - .doit = nl80211_testmode_do, - .dumpit = nl80211_testmode_dump, - .policy = nl80211_policy, -#ifdef CONFIG_MACH_PX -#else - .flags = GENL_ADMIN_PERM, -#endif - .internal_flags = NL80211_FLAG_NEED_WIPHY | - NL80211_FLAG_NEED_RTNL, - }, -#endif - { - .cmd = NL80211_CMD_CONNECT, - .doit = nl80211_connect, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DISCONNECT, - .doit = nl80211_disconnect, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_WIPHY_NETNS, - .doit = nl80211_wiphy_netns, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_WIPHY | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_SURVEY, - .policy = nl80211_policy, - .dumpit = nl80211_dump_survey, - }, - { - .cmd = NL80211_CMD_SET_PMKSA, - .doit = nl80211_setdel_pmksa, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEL_PMKSA, - .doit = nl80211_setdel_pmksa, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_FLUSH_PMKSA, - .doit = nl80211_flush_pmksa, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_REMAIN_ON_CHANNEL, - .doit = nl80211_remain_on_channel, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, - .doit = nl80211_cancel_remain_on_channel, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_TX_BITRATE_MASK, - .doit = nl80211_set_tx_bitrate_mask, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_REGISTER_FRAME, - .doit = nl80211_register_mgmt, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_FRAME, - .doit = nl80211_tx_mgmt, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_FRAME_WAIT_CANCEL, - .doit = nl80211_tx_mgmt_cancel_wait, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_POWER_SAVE, - .doit = nl80211_set_power_save, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_POWER_SAVE, - .doit = nl80211_get_power_save, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_CQM, - .doit = nl80211_set_cqm, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_CHANNEL, - .doit = nl80211_set_channel, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_WDS_PEER, - .doit = nl80211_set_wds_peer, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_JOIN_MESH, - .doit = nl80211_join_mesh, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_LEAVE_MESH, - .doit = nl80211_leave_mesh, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_WOWLAN, - .doit = nl80211_get_wowlan, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_WIPHY | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_WOWLAN, - .doit = nl80211_set_wowlan, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_WIPHY | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_REKEY_OFFLOAD, - .doit = nl80211_set_rekey_data, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_TDLS_MGMT, - .doit = nl80211_tdls_mgmt, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_TDLS_OPER, - .doit = nl80211_tdls_oper, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_UNEXPECTED_FRAME, - .doit = nl80211_register_unexpected_frame, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_PROBE_CLIENT, - .doit = nl80211_probe_client, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_REGISTER_BEACONS, - .doit = nl80211_register_beacons, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_WIPHY | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX_INQ, - .doit = nl80211_btcoex_notify_inq, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX_SCO, - .doit = nl80211_btcoex_notify_sco, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX_A2DP, - .doit = nl80211_btcoex_notify_a2dp, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX_ACL_INFO, - .doit = nl80211_btcoex_notify_acl_info, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX_ANTENNA_CONFIG, - .doit = nl80211_btcoex_notify_antenna_config, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX_BT_VENDOR, - .doit = nl80211_btcoex_notify_bt_vendor, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX, - .doit = nl80211_btcoex_notify, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_PRIV, - .doit = nl80211_priv_cmd, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_P2P_FLUSH, - .doit = nl80211_p2p_flush_notify, - .policy = nl80211_policy, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, -}; - -static struct genl_multicast_group nl80211_mlme_mcgrp = { - .name = "mlme", -}; - -/* multicast groups */ -static struct genl_multicast_group nl80211_config_mcgrp = { - .name = "config", -}; -static struct genl_multicast_group nl80211_scan_mcgrp = { - .name = "scan", -}; -static struct genl_multicast_group nl80211_regulatory_mcgrp = { - .name = "regulatory", -}; - -/* notification functions */ - -void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return; - - if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_config_mcgrp.id, GFP_KERNEL); -} - -static int nl80211_add_scan_req(struct sk_buff *msg, - struct cfg80211_registered_device *rdev) -{ - struct cfg80211_scan_request *req = rdev->scan_req; - struct nlattr *nest; - int i; - - ASSERT_RDEV_LOCK(rdev); - - if (WARN_ON(!req)) - return 0; - - nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS); - if (!nest) - goto nla_put_failure; - for (i = 0; i < req->n_ssids; i++) - NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid); - nla_nest_end(msg, nest); - - nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); - if (!nest) - goto nla_put_failure; - for (i = 0; i < req->n_channels; i++) - NLA_PUT_U32(msg, i, req->channels[i]->center_freq); - nla_nest_end(msg, nest); - - if (req->ie) - NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie); - - return 0; - nla_put_failure: - return -ENOBUFS; -} - -static int nl80211_send_scan_msg(struct sk_buff *msg, - struct cfg80211_registered_device *rdev, - struct net_device *netdev, - u32 pid, u32 seq, int flags, - u32 cmd) -{ - void *hdr; - - hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); - if (!hdr) - return -1; - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - - /* ignore errors and send incomplete event anyway */ - nl80211_add_scan_req(msg, rdev); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int -nl80211_send_sched_scan_msg(struct sk_buff *msg, - struct cfg80211_registered_device *rdev, - struct net_device *netdev, - u32 pid, u32 seq, int flags, u32 cmd) -{ - void *hdr; - - hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); - if (!hdr) - return -1; - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, - struct net_device *netdev) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); - if (!msg) - return; - - if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, - NL80211_CMD_TRIGGER_SCAN) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_scan_mcgrp.id, GFP_KERNEL); -} - -void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, - struct net_device *netdev) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return; - - if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, - NL80211_CMD_NEW_SCAN_RESULTS) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_scan_mcgrp.id, GFP_KERNEL); -} - -void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, - struct net_device *netdev) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return; - - if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, - NL80211_CMD_SCAN_ABORTED) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_scan_mcgrp.id, GFP_KERNEL); -} - -void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, - struct net_device *netdev) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return; - - if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, - NL80211_CMD_SCHED_SCAN_RESULTS) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_scan_mcgrp.id, GFP_KERNEL); -} - -void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 cmd) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); - if (!msg) - return; - - if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_scan_mcgrp.id, GFP_KERNEL); -} - -/* - * This can happen on global regulatory changes or device specific settings - * based on custom world regulatory domains. - */ -void nl80211_send_reg_change_event(struct regulatory_request *request) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE); - if (!hdr) { - nlmsg_free(msg); - return; - } - - /* Userspace can always count this one always being set */ - NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator); - - if (request->alpha2[0] == '0' && request->alpha2[1] == '0') - NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, - NL80211_REGDOM_TYPE_WORLD); - else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') - NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, - NL80211_REGDOM_TYPE_CUSTOM_WORLD); - else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') || - request->intersect) - NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, - NL80211_REGDOM_TYPE_INTERSECTION); - else { - NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, - NL80211_REGDOM_TYPE_COUNTRY); - NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2); - } - - if (wiphy_idx_valid(request->wiphy_idx)) - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx); - - genlmsg_end(msg, hdr); - - rcu_read_lock(); - genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, - GFP_ATOMIC); - rcu_read_unlock(); - - return; - -nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, - enum nl80211_commands cmd, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp) -{ - nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_AUTHENTICATE, gfp); -} - -void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp) -{ - nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_ASSOCIATE, gfp); -} - -void nl80211_send_deauth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp) -{ - nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_DEAUTHENTICATE, gfp); -} - -void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp) -{ - nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_DISASSOCIATE, gfp); -} - -void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp) -{ - nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp); -} - -void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp) -{ - nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_UNPROT_DISASSOCIATE, gfp); -} - -static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, int cmd, - const u8 *addr, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *addr, - gfp_t gfp) -{ - nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE, - addr, gfp); -} - -void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *addr, - gfp_t gfp) -{ - nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, - addr, gfp); -} - -void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - if (bssid) - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); - NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status); - if (req_ie) - NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); - if (resp_ie) - NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); - -} - -void nl80211_send_roamed(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); - if (req_ie) - NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); - if (resp_ie) - NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); - -} - -void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u16 reason, - const u8 *ie, size_t ie_len, bool from_ap) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - if (from_ap && reason) - NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason); - if (from_ap) - NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP); - if (ie) - NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, GFP_KERNEL); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); - -} - -void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *macaddr, const u8* ie, u8 ie_len, - gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr); - if (ie_len && ie) - NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *addr, - enum nl80211_key_type key_type, int key_id, - const u8 *tsc, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - if (addr) - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type); - if (key_id != -1) - NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id); - if (tsc) - NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_send_beacon_hint_event(struct wiphy *wiphy, - struct ieee80211_channel *channel_before, - struct ieee80211_channel *channel_after) -{ - struct sk_buff *msg; - void *hdr; - struct nlattr *nl_freq; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT); - if (!hdr) { - nlmsg_free(msg); - return; - } - - /* - * Since we are applying the beacon hint to a wiphy we know its - * wiphy_idx is valid - */ - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)); - - /* Before */ - nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE); - if (!nl_freq) - goto nla_put_failure; - if (nl80211_msg_put_channel(msg, channel_before)) - goto nla_put_failure; - nla_nest_end(msg, nl_freq); - - /* After */ - nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER); - if (!nl_freq) - goto nla_put_failure; - if (nl80211_msg_put_channel(msg, channel_after)) - goto nla_put_failure; - nla_nest_end(msg, nl_freq); - - genlmsg_end(msg, hdr); - - rcu_read_lock(); - genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, - GFP_ATOMIC); - rcu_read_unlock(); - - return; - -nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -static void nl80211_send_remain_on_chan_event( - int cmd, struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - unsigned int duration, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type); - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); - - if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL) - NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - unsigned int duration, gfp_t gfp) -{ - nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, - rdev, netdev, cookie, chan, - channel_type, duration, gfp); -} - -void nl80211_send_remain_on_channel_cancel( - struct cfg80211_registered_device *rdev, struct net_device *netdev, - u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, gfp_t gfp) -{ - nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, - rdev, netdev, cookie, chan, - channel_type, 0, gfp); -} - -void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *mac_addr, - struct station_info *sinfo, gfp_t gfp) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); -} - -void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *mac_addr, - gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd, - const u8 *addr, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct sk_buff *msg; - void *hdr; - int err; - u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid); - - if (!nlpid) - return false; - - msg = nlmsg_new(100, gfp); - if (!msg) - return true; - - hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); - if (!hdr) { - nlmsg_free(msg); - return true; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - - err = genlmsg_end(msg, hdr); - if (err < 0) { - nlmsg_free(msg); - return true; - } - - genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); - return true; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); - return true; -} - -bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) -{ - return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME, - addr, gfp); -} - -bool nl80211_unexpected_4addr_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp) -{ - return __nl80211_unexpected_frame(dev, - NL80211_CMD_UNEXPECTED_4ADDR_FRAME, - addr, gfp); -} - -int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 nlpid, - int freq, const u8 *buf, size_t len, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); - if (!hdr) { - nlmsg_free(msg); - return -ENOMEM; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); - - genlmsg_end(msg, hdr); - - return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); - return -ENOBUFS; -} - -void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, - const u8 *buf, size_t len, bool ack, - gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); - if (ack) - NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); - - genlmsg_end(msg, hdr); - - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void -nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - enum nl80211_cqm_rssi_threshold_event rssi_event, - gfp_t gfp) -{ - struct sk_buff *msg; - struct nlattr *pinfoattr; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - - pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); - if (!pinfoattr) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, - rssi_event); - - nla_nest_end(msg, pinfoattr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *replay_ctr, gfp_t gfp) -{ - struct sk_buff *msg; - struct nlattr *rekey_attr; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); - - rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); - if (!rekey_attr) - goto nla_put_failure; - - NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, - NL80211_REPLAY_CTR_LEN, replay_ctr); - - nla_nest_end(msg, rekey_attr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, int index, - const u8 *bssid, bool preauth, gfp_t gfp) -{ - struct sk_buff *msg; - struct nlattr *attr; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - - attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE); - if (!attr) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index); - NLA_PUT(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid); - if (preauth) - NLA_PUT_FLAG(msg, NL80211_PMKSA_CANDIDATE_PREAUTH); - - nla_nest_end(msg, attr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void -nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *peer, - u32 num_packets, gfp_t gfp) -{ - struct sk_buff *msg; - struct nlattr *pinfoattr; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer); - - pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); - if (!pinfoattr) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets); - - nla_nest_end(msg, pinfoattr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void cfg80211_probe_status(struct net_device *dev, const u8 *addr, - u64 cookie, bool acked, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct sk_buff *msg; - void *hdr; - int err; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); - if (acked) - NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); - - err = genlmsg_end(msg, hdr); - if (err < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} -EXPORT_SYMBOL(cfg80211_probe_status); - -void cfg80211_report_obss_beacon(struct wiphy *wiphy, - const u8 *frame, size_t len, - int freq, gfp_t gfp) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct sk_buff *msg; - void *hdr; - u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid); - - if (!nlpid) - return; - - msg = nlmsg_new(len + 100, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - if (freq) - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - NLA_PUT(msg, NL80211_ATTR_FRAME, len, frame); - - genlmsg_end(msg, hdr); - - genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} -EXPORT_SYMBOL(cfg80211_report_obss_beacon); - -void cfg80211_priv_event(struct net_device *dev, - const char *priv_event, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct sk_buff *msg; - void *hdr; - int err; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PRIV_EVENT); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT_STRING(msg, NL80211_ATTR_PRIV_EVENT, priv_event); - - err = genlmsg_end(msg, hdr); - if (err < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); - -} -EXPORT_SYMBOL(cfg80211_priv_event); - - -static int nl80211_netlink_notify(struct notifier_block * nb, - unsigned long state, - void *_notify) -{ - struct netlink_notify *notify = _notify; - struct cfg80211_registered_device *rdev; - struct wireless_dev *wdev; - - if (state != NETLINK_URELEASE) - return NOTIFY_DONE; - - rcu_read_lock(); - - list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { - list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) - cfg80211_mlme_unregister_socket(wdev, notify->pid); - if (rdev->ap_beacons_nlpid == notify->pid) - rdev->ap_beacons_nlpid = 0; - } - - rcu_read_unlock(); - - return NOTIFY_DONE; -} - -static struct notifier_block nl80211_netlink_notifier = { - .notifier_call = nl80211_netlink_notify, -}; - -/* initialisation/exit functions */ - -int nl80211_init(void) -{ - int err; - - err = genl_register_family_with_ops(&nl80211_fam, - nl80211_ops, ARRAY_SIZE(nl80211_ops)); - if (err) - return err; - - err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp); - if (err) - goto err_out; - - err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp); - if (err) - goto err_out; - - err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp); - if (err) - goto err_out; - - err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp); - if (err) - goto err_out; - -#ifdef CONFIG_NL80211_TESTMODE - err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp); - if (err) - goto err_out; -#endif - - err = netlink_register_notifier(&nl80211_netlink_notifier); - if (err) - goto err_out; - - return 0; - err_out: - genl_unregister_family(&nl80211_fam); - return err; -} - -void nl80211_exit(void) -{ - netlink_unregister_notifier(&nl80211_netlink_notifier); - genl_unregister_family(&nl80211_fam); -} diff --git a/net/wireless_ath/nl80211.h b/net/wireless_ath/nl80211.h deleted file mode 100755 index 12bf4d1..0000000 --- a/net/wireless_ath/nl80211.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef __NET_WIRELESS_NL80211_H -#define __NET_WIRELESS_NL80211_H - -#include "core.h" - -int nl80211_init(void); -void nl80211_exit(void); -void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); -void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, - struct net_device *netdev); -void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, - struct net_device *netdev); -void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, - struct net_device *netdev); -void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 cmd); -void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, - struct net_device *netdev); -void nl80211_send_reg_change_event(struct regulatory_request *request); -void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_deauth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *addr, gfp_t gfp); -void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *addr, gfp_t gfp); -void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, gfp_t gfp); -void nl80211_send_roamed(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); -void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u16 reason, - const u8 *ie, size_t ie_len, bool from_ap); - -void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *macaddr, const u8* ie, u8 ie_len, - gfp_t gfp); -void -nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *addr, - enum nl80211_key_type key_type, - int key_id, const u8 *tsc, gfp_t gfp); - -void -nl80211_send_beacon_hint_event(struct wiphy *wiphy, - struct ieee80211_channel *channel_before, - struct ieee80211_channel *channel_after); - -void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - gfp_t gfp); - -void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - u64 cookie, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - unsigned int duration, gfp_t gfp); -void nl80211_send_remain_on_channel_cancel( - struct cfg80211_registered_device *rdev, struct net_device *netdev, - u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, gfp_t gfp); - -void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *mac_addr, - struct station_info *sinfo, gfp_t gfp); -void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *mac_addr, - gfp_t gfp); - -int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 nlpid, int freq, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, - const u8 *buf, size_t len, bool ack, - gfp_t gfp); - -void -nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - enum nl80211_cqm_rssi_threshold_event rssi_event, - gfp_t gfp); -void -nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *peer, - u32 num_packets, gfp_t gfp); - -void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *replay_ctr, gfp_t gfp); - -void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, int index, - const u8 *bssid, bool preauth, gfp_t gfp); - -bool nl80211_unexpected_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp); -bool nl80211_unexpected_4addr_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp); - -#endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless_ath/radiotap.c b/net/wireless_ath/radiotap.c deleted file mode 100755 index c4ad795..0000000 --- a/net/wireless_ath/radiotap.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Radiotap parser - * - * Copyright 2007 Andy Green <andy@warmcat.com> - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - * - * 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. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See COPYING for more details. - */ - -#include <linux/kernel.h> -#include <linux/export.h> -#include <net/cfg80211.h> -#include <net/ieee80211_radiotap.h> -#include <asm/unaligned.h> - -/* function prototypes and related defs are in include/net/cfg80211.h */ - -static const struct radiotap_align_size rtap_namespace_sizes[] = { - [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, }, - [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, }, - [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, }, - [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, }, - [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, }, - [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, }, - [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, }, - [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, }, - [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, - /* - * add more here as they are defined in radiotap.h - */ -}; - -static const struct ieee80211_radiotap_namespace radiotap_ns = { - .n_bits = ARRAY_SIZE(rtap_namespace_sizes), - .align_size = rtap_namespace_sizes, -}; - -/** - * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization - * @iterator: radiotap_iterator to initialize - * @radiotap_header: radiotap header to parse - * @max_length: total length we can parse into (eg, whole packet length) - * - * Returns: 0 or a negative error code if there is a problem. - * - * This function initializes an opaque iterator struct which can then - * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap - * argument which is present in the header. It knows about extended - * present headers and handles them. - * - * How to use: - * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator - * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) - * checking for a good 0 return code. Then loop calling - * __ieee80211_radiotap_iterator_next()... it returns either 0, - * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. - * The iterator's @this_arg member points to the start of the argument - * associated with the current argument index that is present, which can be - * found in the iterator's @this_arg_index member. This arg index corresponds - * to the IEEE80211_RADIOTAP_... defines. - * - * Radiotap header length: - * You can find the CPU-endian total radiotap header length in - * iterator->max_length after executing ieee80211_radiotap_iterator_init() - * successfully. - * - * Alignment Gotcha: - * You must take care when dereferencing iterator.this_arg - * for multibyte types... the pointer is not aligned. Use - * get_unaligned((type *)iterator.this_arg) to dereference - * iterator.this_arg for type "type" safely on all arches. - * - * Example code: - * See Documentation/networking/radiotap-headers.txt - */ - -int ieee80211_radiotap_iterator_init( - struct ieee80211_radiotap_iterator *iterator, - struct ieee80211_radiotap_header *radiotap_header, - int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns) -{ - /* Linux only supports version 0 radiotap format */ - if (radiotap_header->it_version) - return -EINVAL; - - /* sanity check for allowed length and radiotap length field */ - if (max_length < get_unaligned_le16(&radiotap_header->it_len)) - return -EINVAL; - - iterator->_rtheader = radiotap_header; - iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len); - iterator->_arg_index = 0; - iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); - iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header); - iterator->_reset_on_ext = 0; - iterator->_next_bitmap = &radiotap_header->it_present; - iterator->_next_bitmap++; - iterator->_vns = vns; - iterator->current_namespace = &radiotap_ns; - iterator->is_radiotap_ns = 1; - - /* find payload start allowing for extended bitmap(s) */ - - if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) { - while (get_unaligned_le32(iterator->_arg) & - (1 << IEEE80211_RADIOTAP_EXT)) { - iterator->_arg += sizeof(uint32_t); - - /* - * check for insanity where the present bitmaps - * keep claiming to extend up to or even beyond the - * stated radiotap header length - */ - - if ((unsigned long)iterator->_arg - - (unsigned long)iterator->_rtheader > - (unsigned long)iterator->_max_length) - return -EINVAL; - } - - iterator->_arg += sizeof(uint32_t); - - /* - * no need to check again for blowing past stated radiotap - * header length, because ieee80211_radiotap_iterator_next - * checks it before it is dereferenced - */ - } - - iterator->this_arg = iterator->_arg; - - /* we are all initialized happily */ - - return 0; -} -EXPORT_SYMBOL(ieee80211_radiotap_iterator_init); - -static void find_ns(struct ieee80211_radiotap_iterator *iterator, - uint32_t oui, uint8_t subns) -{ - int i; - - iterator->current_namespace = NULL; - - if (!iterator->_vns) - return; - - for (i = 0; i < iterator->_vns->n_ns; i++) { - if (iterator->_vns->ns[i].oui != oui) - continue; - if (iterator->_vns->ns[i].subns != subns) - continue; - - iterator->current_namespace = &iterator->_vns->ns[i]; - break; - } -} - - - -/** - * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg - * @iterator: radiotap_iterator to move to next arg (if any) - * - * Returns: 0 if there is an argument to handle, - * -ENOENT if there are no more args or -EINVAL - * if there is something else wrong. - * - * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) - * in @this_arg_index and sets @this_arg to point to the - * payload for the field. It takes care of alignment handling and extended - * present fields. @this_arg can be changed by the caller (eg, - * incremented to move inside a compound argument like - * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in - * little-endian format whatever the endianess of your CPU. - * - * Alignment Gotcha: - * You must take care when dereferencing iterator.this_arg - * for multibyte types... the pointer is not aligned. Use - * get_unaligned((type *)iterator.this_arg) to dereference - * iterator.this_arg for type "type" safely on all arches. - */ - -int ieee80211_radiotap_iterator_next( - struct ieee80211_radiotap_iterator *iterator) -{ - while (1) { - int hit = 0; - int pad, align, size, subns; - uint32_t oui; - - /* if no more EXT bits, that's it */ - if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT && - !(iterator->_bitmap_shifter & 1)) - return -ENOENT; - - if (!(iterator->_bitmap_shifter & 1)) - goto next_entry; /* arg not present */ - - /* get alignment/size of data */ - switch (iterator->_arg_index % 32) { - case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: - case IEEE80211_RADIOTAP_EXT: - align = 1; - size = 0; - break; - case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: - align = 2; - size = 6; - break; - default: - if (!iterator->current_namespace || - iterator->_arg_index >= iterator->current_namespace->n_bits) { - if (iterator->current_namespace == &radiotap_ns) - return -ENOENT; - align = 0; - } else { - align = iterator->current_namespace->align_size[iterator->_arg_index].align; - size = iterator->current_namespace->align_size[iterator->_arg_index].size; - } - if (!align) { - /* skip all subsequent data */ - iterator->_arg = iterator->_next_ns_data; - /* give up on this namespace */ - iterator->current_namespace = NULL; - goto next_entry; - } - break; - } - - /* - * arg is present, account for alignment padding - * - * Note that these alignments are relative to the start - * of the radiotap header. There is no guarantee - * that the radiotap header itself is aligned on any - * kind of boundary. - * - * The above is why get_unaligned() is used to dereference - * multibyte elements from the radiotap area. - */ - - pad = ((unsigned long)iterator->_arg - - (unsigned long)iterator->_rtheader) & (align - 1); - - if (pad) - iterator->_arg += align - pad; - - if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) { - int vnslen; - - if ((unsigned long)iterator->_arg + size - - (unsigned long)iterator->_rtheader > - (unsigned long)iterator->_max_length) - return -EINVAL; - - oui = (*iterator->_arg << 16) | - (*(iterator->_arg + 1) << 8) | - *(iterator->_arg + 2); - subns = *(iterator->_arg + 3); - - find_ns(iterator, oui, subns); - - vnslen = get_unaligned_le16(iterator->_arg + 4); - iterator->_next_ns_data = iterator->_arg + size + vnslen; - if (!iterator->current_namespace) - size += vnslen; - } - - /* - * this is what we will return to user, but we need to - * move on first so next call has something fresh to test - */ - iterator->this_arg_index = iterator->_arg_index; - iterator->this_arg = iterator->_arg; - iterator->this_arg_size = size; - - /* internally move on the size of this arg */ - iterator->_arg += size; - - /* - * check for insanity where we are given a bitmap that - * claims to have more arg content than the length of the - * radiotap section. We will normally end up equalling this - * max_length on the last arg, never exceeding it. - */ - - if ((unsigned long)iterator->_arg - - (unsigned long)iterator->_rtheader > - (unsigned long)iterator->_max_length) - return -EINVAL; - - /* these special ones are valid in each bitmap word */ - switch (iterator->_arg_index % 32) { - case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: - iterator->_reset_on_ext = 1; - - iterator->is_radiotap_ns = 0; - /* - * If parser didn't register this vendor - * namespace with us, allow it to show it - * as 'raw. Do do that, set argument index - * to vendor namespace. - */ - iterator->this_arg_index = - IEEE80211_RADIOTAP_VENDOR_NAMESPACE; - if (!iterator->current_namespace) - hit = 1; - goto next_entry; - case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: - iterator->_reset_on_ext = 1; - iterator->current_namespace = &radiotap_ns; - iterator->is_radiotap_ns = 1; - goto next_entry; - case IEEE80211_RADIOTAP_EXT: - /* - * bit 31 was set, there is more - * -- move to next u32 bitmap - */ - iterator->_bitmap_shifter = - get_unaligned_le32(iterator->_next_bitmap); - iterator->_next_bitmap++; - if (iterator->_reset_on_ext) - iterator->_arg_index = 0; - else - iterator->_arg_index++; - iterator->_reset_on_ext = 0; - break; - default: - /* we've got a hit! */ - hit = 1; - next_entry: - iterator->_bitmap_shifter >>= 1; - iterator->_arg_index++; - } - - /* if we found a valid arg earlier, return it now */ - if (hit) - return 0; - } -} -EXPORT_SYMBOL(ieee80211_radiotap_iterator_next); diff --git a/net/wireless_ath/reg.c b/net/wireless_ath/reg.c deleted file mode 100755 index a797919..0000000 --- a/net/wireless_ath/reg.c +++ /dev/null @@ -1,2307 +0,0 @@ -/* - * Copyright 2002-2005, Instant802 Networks, Inc. - * Copyright 2005-2006, Devicescape Software, Inc. - * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> - * Copyright 2008 Luis R. Rodriguez <lrodriguz@atheros.com> - * - * 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. - */ - -/** - * DOC: Wireless regulatory infrastructure - * - * The usual implementation is for a driver to read a device EEPROM to - * determine which regulatory domain it should be operating under, then - * looking up the allowable channels in a driver-local table and finally - * registering those channels in the wiphy structure. - * - * Another set of compliance enforcement is for drivers to use their - * own compliance limits which can be stored on the EEPROM. The host - * driver or firmware may ensure these are used. - * - * In addition to all this we provide an extra layer of regulatory - * conformance. For drivers which do not have any regulatory - * information CRDA provides the complete regulatory solution. - * For others it provides a community effort on further restrictions - * to enhance compliance. - * - * Note: When number of rules --> infinity we will not be able to - * index on alpha2 any more, instead we'll probably have to - * rely on some SHA1 checksum of the regdomain for example. - * - */ - -//#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/kernel.h> -#include <linux/export.h> -#include <linux/slab.h> -#include <linux/list.h> -#include <linux/random.h> -#include <linux/ctype.h> -#include <linux/nl80211.h> -#include <linux/platform_device.h> -#include <linux/moduleparam.h> -#include <net/cfg80211.h> -#include "core.h" -#include "reg.h" -#include "regdb.h" -#include "nl80211.h" - -#ifdef CONFIG_CFG80211_REG_DEBUG -#define REG_DBG_PRINT(format, args...) \ - printk(KERN_DEBUG pr_fmt(format), ##args) -#else -#define REG_DBG_PRINT(args...) -#endif - -/* Receipt of information from last regulatory request */ -static struct regulatory_request *last_request; - -/* To trigger userspace events */ -static struct platform_device *reg_pdev; - -static struct device_type reg_device_type = { - .uevent = reg_device_uevent, -}; - -/* - * Central wireless core regulatory domains, we only need two, - * the current one and a world regulatory domain in case we have no - * information to give us an alpha2 - */ -const struct ieee80211_regdomain *cfg80211_regdomain; - -/* - * Protects static reg.c components: - * - cfg80211_world_regdom - * - cfg80211_regdom - * - last_request - */ -static DEFINE_MUTEX(reg_mutex); - -static inline void assert_reg_lock(void) -{ - lockdep_assert_held(®_mutex); -} - -/* Used to queue up regulatory hints */ -static LIST_HEAD(reg_requests_list); -static spinlock_t reg_requests_lock; - -/* Used to queue up beacon hints for review */ -static LIST_HEAD(reg_pending_beacons); -static spinlock_t reg_pending_beacons_lock; - -/* Used to keep track of processed beacon hints */ -static LIST_HEAD(reg_beacon_list); - -struct reg_beacon { - struct list_head list; - struct ieee80211_channel chan; -}; - -static void reg_todo(struct work_struct *work); -static DECLARE_WORK(reg_work, reg_todo); - -static void reg_timeout_work(struct work_struct *work); -static DECLARE_DELAYED_WORK(reg_timeout, reg_timeout_work); - -/* We keep a static world regulatory domain in case of the absence of CRDA */ -static const struct ieee80211_regdomain world_regdom = { - .n_reg_rules = 5, - .alpha2 = "00", - .reg_rules = { - /* IEEE 802.11b/g, channels 1..11 */ - REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), - /* IEEE 802.11b/g, channels 12..13. No HT40 - * channel fits here. */ - REG_RULE(2467-10, 2472+10, 20, 6, 20, - NL80211_RRF_PASSIVE_SCAN | - NL80211_RRF_NO_IBSS), - /* IEEE 802.11 channel 14 - Only JP enables - * this and for 802.11b only */ - REG_RULE(2484-10, 2484+10, 20, 6, 20, - NL80211_RRF_PASSIVE_SCAN | - NL80211_RRF_NO_IBSS | - NL80211_RRF_NO_OFDM), - /* IEEE 802.11a, channel 36..48 */ - REG_RULE(5180-10, 5240+10, 40, 6, 20, - NL80211_RRF_PASSIVE_SCAN | - NL80211_RRF_NO_IBSS), - - /* NB: 5260 MHz - 5700 MHz requies DFS */ - - /* IEEE 802.11a, channel 149..165 */ - REG_RULE(5745-10, 5825+10, 40, 6, 20, - NL80211_RRF_PASSIVE_SCAN | - NL80211_RRF_NO_IBSS), - } -}; - -static const struct ieee80211_regdomain *cfg80211_world_regdom = - &world_regdom; - -static char *ieee80211_regdom = "00"; -static char user_alpha2[2]; - -module_param(ieee80211_regdom, charp, 0444); -MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); - -static void reset_regdomains(void) -{ - /* avoid freeing static information or freeing something twice */ - if (cfg80211_regdomain == cfg80211_world_regdom) - cfg80211_regdomain = NULL; - if (cfg80211_world_regdom == &world_regdom) - cfg80211_world_regdom = NULL; - if (cfg80211_regdomain == &world_regdom) - cfg80211_regdomain = NULL; - - kfree(cfg80211_regdomain); - kfree(cfg80211_world_regdom); - - cfg80211_world_regdom = &world_regdom; - cfg80211_regdomain = NULL; -} - -/* - * Dynamic world regulatory domain requested by the wireless - * core upon initialization - */ -static void update_world_regdomain(const struct ieee80211_regdomain *rd) -{ - BUG_ON(!last_request); - - reset_regdomains(); - - cfg80211_world_regdom = rd; - cfg80211_regdomain = rd; -} - -bool is_world_regdom(const char *alpha2) -{ - if (!alpha2) - return false; - if (alpha2[0] == '0' && alpha2[1] == '0') - return true; - return false; -} - -static bool is_alpha2_set(const char *alpha2) -{ - if (!alpha2) - return false; - if (alpha2[0] != 0 && alpha2[1] != 0) - return true; - return false; -} - -static bool is_unknown_alpha2(const char *alpha2) -{ - if (!alpha2) - return false; - /* - * Special case where regulatory domain was built by driver - * but a specific alpha2 cannot be determined - */ - if (alpha2[0] == '9' && alpha2[1] == '9') - return true; - return false; -} - -static bool is_intersected_alpha2(const char *alpha2) -{ - if (!alpha2) - return false; - /* - * Special case where regulatory domain is the - * result of an intersection between two regulatory domain - * structures - */ - if (alpha2[0] == '9' && alpha2[1] == '8') - return true; - return false; -} - -static bool is_an_alpha2(const char *alpha2) -{ - if (!alpha2) - return false; - if (isalpha(alpha2[0]) && isalpha(alpha2[1])) - return true; - return false; -} - -static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) -{ - if (!alpha2_x || !alpha2_y) - return false; - if (alpha2_x[0] == alpha2_y[0] && - alpha2_x[1] == alpha2_y[1]) - return true; - return false; -} - -static bool regdom_changes(const char *alpha2) -{ - assert_cfg80211_lock(); - - if (!cfg80211_regdomain) - return true; - if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) - return false; - return true; -} - -/* - * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets - * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER - * has ever been issued. - */ -static bool is_user_regdom_saved(void) -{ - if (user_alpha2[0] == '9' && user_alpha2[1] == '7') - return false; - - /* This would indicate a mistake on the design */ - if (WARN((!is_world_regdom(user_alpha2) && - !is_an_alpha2(user_alpha2)), - "Unexpected user alpha2: %c%c\n", - user_alpha2[0], - user_alpha2[1])) - return false; - - return true; -} - -static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, - const struct ieee80211_regdomain *src_regd) -{ - struct ieee80211_regdomain *regd; - int size_of_regd = 0; - unsigned int i; - - size_of_regd = sizeof(struct ieee80211_regdomain) + - ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); - - regd = kzalloc(size_of_regd, GFP_KERNEL); - if (!regd) - return -ENOMEM; - - memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); - - for (i = 0; i < src_regd->n_reg_rules; i++) - memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], - sizeof(struct ieee80211_reg_rule)); - - *dst_regd = regd; - return 0; -} - -#ifdef CONFIG_CFG80211_INTERNAL_REGDB -struct reg_regdb_search_request { - char alpha2[2]; - struct list_head list; -}; - -static LIST_HEAD(reg_regdb_search_list); -static DEFINE_MUTEX(reg_regdb_search_mutex); - -static void reg_regdb_search(struct work_struct *work) -{ - struct reg_regdb_search_request *request; - const struct ieee80211_regdomain *curdom, *regdom; - int i, r; - - mutex_lock(®_regdb_search_mutex); - while (!list_empty(®_regdb_search_list)) { - request = list_first_entry(®_regdb_search_list, - struct reg_regdb_search_request, - list); - list_del(&request->list); - - for (i=0; i<reg_regdb_size; i++) { - curdom = reg_regdb[i]; - - if (!memcmp(request->alpha2, curdom->alpha2, 2)) { - r = reg_copy_regd(®dom, curdom); - if (r) - break; - mutex_lock(&cfg80211_mutex); - set_regdom(regdom); - mutex_unlock(&cfg80211_mutex); - break; - } - } - - kfree(request); - } - mutex_unlock(®_regdb_search_mutex); -} - -static DECLARE_WORK(reg_regdb_work, reg_regdb_search); - -static void reg_regdb_query(const char *alpha2) -{ - struct reg_regdb_search_request *request; - - if (!alpha2) - return; - - request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL); - if (!request) - return; - - memcpy(request->alpha2, alpha2, 2); - - mutex_lock(®_regdb_search_mutex); - list_add_tail(&request->list, ®_regdb_search_list); - mutex_unlock(®_regdb_search_mutex); - - schedule_work(®_regdb_work); -} -#else -static inline void reg_regdb_query(const char *alpha2) {} -#endif /* CONFIG_CFG80211_INTERNAL_REGDB */ - -/* - * This lets us keep regulatory code which is updated on a regulatory - * basis in userspace. Country information is filled in by - * reg_device_uevent - */ -static int call_crda(const char *alpha2) -{ - if (!is_world_regdom((char *) alpha2)) - pr_info("Calling CRDA for country: %c%c\n", - alpha2[0], alpha2[1]); - else - pr_info("Calling CRDA to update world regulatory domain\n"); - - /* query internal regulatory database (if it exists) */ - reg_regdb_query(alpha2); - - return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); -} - -/* Used by nl80211 before kmalloc'ing our regulatory domain */ -bool reg_is_valid_request(const char *alpha2) -{ - assert_cfg80211_lock(); - - if (!last_request) - return false; - - return alpha2_equal(last_request->alpha2, alpha2); -} - -/* Sanity check on a regulatory rule */ -static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) -{ - const struct ieee80211_freq_range *freq_range = &rule->freq_range; - u32 freq_diff; - - if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0) - return false; - - if (freq_range->start_freq_khz > freq_range->end_freq_khz) - return false; - - freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; - - if (freq_range->end_freq_khz <= freq_range->start_freq_khz || - freq_range->max_bandwidth_khz > freq_diff) - return false; - - return true; -} - -static bool is_valid_rd(const struct ieee80211_regdomain *rd) -{ - const struct ieee80211_reg_rule *reg_rule = NULL; - unsigned int i; - - if (!rd->n_reg_rules) - return false; - - if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) - return false; - - for (i = 0; i < rd->n_reg_rules; i++) { - reg_rule = &rd->reg_rules[i]; - if (!is_valid_reg_rule(reg_rule)) - return false; - } - - return true; -} - -static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, - u32 center_freq_khz, - u32 bw_khz) -{ - u32 start_freq_khz, end_freq_khz; - - start_freq_khz = center_freq_khz - (bw_khz/2); - end_freq_khz = center_freq_khz + (bw_khz/2); - - if (start_freq_khz >= freq_range->start_freq_khz && - end_freq_khz <= freq_range->end_freq_khz) - return true; - - return false; -} - -/** - * freq_in_rule_band - tells us if a frequency is in a frequency band - * @freq_range: frequency rule we want to query - * @freq_khz: frequency we are inquiring about - * - * This lets us know if a specific frequency rule is or is not relevant to - * a specific frequency's band. Bands are device specific and artificial - * definitions (the "2.4 GHz band" and the "5 GHz band"), however it is - * safe for now to assume that a frequency rule should not be part of a - * frequency's band if the start freq or end freq are off by more than 2 GHz. - * This resolution can be lowered and should be considered as we add - * regulatory rule support for other "bands". - **/ -static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, - u32 freq_khz) -{ -#define ONE_GHZ_IN_KHZ 1000000 - if (abs(freq_khz - freq_range->start_freq_khz) <= (2 * ONE_GHZ_IN_KHZ)) - return true; - if (abs(freq_khz - freq_range->end_freq_khz) <= (2 * ONE_GHZ_IN_KHZ)) - return true; - return false; -#undef ONE_GHZ_IN_KHZ -} - -/* - * Helper for regdom_intersect(), this does the real - * mathematical intersection fun - */ -static int reg_rules_intersect( - const struct ieee80211_reg_rule *rule1, - const struct ieee80211_reg_rule *rule2, - struct ieee80211_reg_rule *intersected_rule) -{ - const struct ieee80211_freq_range *freq_range1, *freq_range2; - struct ieee80211_freq_range *freq_range; - const struct ieee80211_power_rule *power_rule1, *power_rule2; - struct ieee80211_power_rule *power_rule; - u32 freq_diff; - - freq_range1 = &rule1->freq_range; - freq_range2 = &rule2->freq_range; - freq_range = &intersected_rule->freq_range; - - power_rule1 = &rule1->power_rule; - power_rule2 = &rule2->power_rule; - power_rule = &intersected_rule->power_rule; - - freq_range->start_freq_khz = max(freq_range1->start_freq_khz, - freq_range2->start_freq_khz); - freq_range->end_freq_khz = min(freq_range1->end_freq_khz, - freq_range2->end_freq_khz); - freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, - freq_range2->max_bandwidth_khz); - - freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; - if (freq_range->max_bandwidth_khz > freq_diff) - freq_range->max_bandwidth_khz = freq_diff; - - power_rule->max_eirp = min(power_rule1->max_eirp, - power_rule2->max_eirp); - power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, - power_rule2->max_antenna_gain); - - intersected_rule->flags = (rule1->flags | rule2->flags); - - if (!is_valid_reg_rule(intersected_rule)) - return -EINVAL; - - return 0; -} - -/** - * regdom_intersect - do the intersection between two regulatory domains - * @rd1: first regulatory domain - * @rd2: second regulatory domain - * - * Use this function to get the intersection between two regulatory domains. - * Once completed we will mark the alpha2 for the rd as intersected, "98", - * as no one single alpha2 can represent this regulatory domain. - * - * Returns a pointer to the regulatory domain structure which will hold the - * resulting intersection of rules between rd1 and rd2. We will - * kzalloc() this structure for you. - */ -static struct ieee80211_regdomain *regdom_intersect( - const struct ieee80211_regdomain *rd1, - const struct ieee80211_regdomain *rd2) -{ - int r, size_of_regd; - unsigned int x, y; - unsigned int num_rules = 0, rule_idx = 0; - const struct ieee80211_reg_rule *rule1, *rule2; - struct ieee80211_reg_rule *intersected_rule; - struct ieee80211_regdomain *rd; - /* This is just a dummy holder to help us count */ - struct ieee80211_reg_rule irule; - - /* Uses the stack temporarily for counter arithmetic */ - intersected_rule = &irule; - - memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); - - if (!rd1 || !rd2) - return NULL; - - /* - * First we get a count of the rules we'll need, then we actually - * build them. This is to so we can malloc() and free() a - * regdomain once. The reason we use reg_rules_intersect() here - * is it will return -EINVAL if the rule computed makes no sense. - * All rules that do check out OK are valid. - */ - - for (x = 0; x < rd1->n_reg_rules; x++) { - rule1 = &rd1->reg_rules[x]; - for (y = 0; y < rd2->n_reg_rules; y++) { - rule2 = &rd2->reg_rules[y]; - if (!reg_rules_intersect(rule1, rule2, - intersected_rule)) - num_rules++; - memset(intersected_rule, 0, - sizeof(struct ieee80211_reg_rule)); - } - } - - if (!num_rules) - return NULL; - - size_of_regd = sizeof(struct ieee80211_regdomain) + - ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); - - rd = kzalloc(size_of_regd, GFP_KERNEL); - if (!rd) - return NULL; - - for (x = 0; x < rd1->n_reg_rules; x++) { - rule1 = &rd1->reg_rules[x]; - for (y = 0; y < rd2->n_reg_rules; y++) { - rule2 = &rd2->reg_rules[y]; - /* - * This time around instead of using the stack lets - * write to the target rule directly saving ourselves - * a memcpy() - */ - intersected_rule = &rd->reg_rules[rule_idx]; - r = reg_rules_intersect(rule1, rule2, - intersected_rule); - /* - * No need to memset here the intersected rule here as - * we're not using the stack anymore - */ - if (r) - continue; - rule_idx++; - } - } - - if (rule_idx != num_rules) { - kfree(rd); - return NULL; - } - - rd->n_reg_rules = num_rules; - rd->alpha2[0] = '9'; - rd->alpha2[1] = '8'; - - return rd; -} - -/* - * XXX: add support for the rest of enum nl80211_reg_rule_flags, we may - * want to just have the channel structure use these - */ -static u32 map_regdom_flags(u32 rd_flags) -{ - u32 channel_flags = 0; - if (rd_flags & NL80211_RRF_PASSIVE_SCAN) - channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN; - if (rd_flags & NL80211_RRF_NO_IBSS) - channel_flags |= IEEE80211_CHAN_NO_IBSS; - if (rd_flags & NL80211_RRF_DFS) - channel_flags |= IEEE80211_CHAN_RADAR; - return channel_flags; -} - -static int freq_reg_info_regd(struct wiphy *wiphy, - u32 center_freq, - u32 desired_bw_khz, - const struct ieee80211_reg_rule **reg_rule, - const struct ieee80211_regdomain *custom_regd) -{ - int i; - bool band_rule_found = false; - const struct ieee80211_regdomain *regd; - bool bw_fits = false; - - if (!desired_bw_khz) - desired_bw_khz = MHZ_TO_KHZ(20); - - regd = custom_regd ? custom_regd : cfg80211_regdomain; - - /* - * Follow the driver's regulatory domain, if present, unless a country - * IE has been processed or a user wants to help complaince further - */ - if (!custom_regd && - last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - last_request->initiator != NL80211_REGDOM_SET_BY_USER && - wiphy->regd) - regd = wiphy->regd; - - if (!regd) - return -EINVAL; - - for (i = 0; i < regd->n_reg_rules; i++) { - const struct ieee80211_reg_rule *rr; - const struct ieee80211_freq_range *fr = NULL; - - rr = ®d->reg_rules[i]; - fr = &rr->freq_range; - - /* - * We only need to know if one frequency rule was - * was in center_freq's band, that's enough, so lets - * not overwrite it once found - */ - if (!band_rule_found) - band_rule_found = freq_in_rule_band(fr, center_freq); - - bw_fits = reg_does_bw_fit(fr, - center_freq, - desired_bw_khz); - - if (band_rule_found && bw_fits) { - *reg_rule = rr; - return 0; - } - } - - if (!band_rule_found) - return -ERANGE; - - return -EINVAL; -} - -int freq_reg_info(struct wiphy *wiphy, - u32 center_freq, - u32 desired_bw_khz, - const struct ieee80211_reg_rule **reg_rule) -{ - assert_cfg80211_lock(); - return freq_reg_info_regd(wiphy, - center_freq, - desired_bw_khz, - reg_rule, - NULL); -} -EXPORT_SYMBOL(freq_reg_info); - -#ifdef CONFIG_CFG80211_REG_DEBUG -static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) -{ - switch (initiator) { - case NL80211_REGDOM_SET_BY_CORE: - return "Set by core"; - case NL80211_REGDOM_SET_BY_USER: - return "Set by user"; - case NL80211_REGDOM_SET_BY_DRIVER: - return "Set by driver"; - case NL80211_REGDOM_SET_BY_COUNTRY_IE: - return "Set by country IE"; - default: - WARN_ON(1); - return "Set by bug"; - } -} - -static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, - u32 desired_bw_khz, - const struct ieee80211_reg_rule *reg_rule) -{ - const struct ieee80211_power_rule *power_rule; - const struct ieee80211_freq_range *freq_range; - char max_antenna_gain[32]; - - power_rule = ®_rule->power_rule; - freq_range = ®_rule->freq_range; - - if (!power_rule->max_antenna_gain) - snprintf(max_antenna_gain, 32, "N/A"); - else - snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); - - REG_DBG_PRINT("Updating information on frequency %d MHz " - "for a %d MHz width channel with regulatory rule:\n", - chan->center_freq, - KHZ_TO_MHZ(desired_bw_khz)); - - REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n", - freq_range->start_freq_khz, - freq_range->end_freq_khz, - freq_range->max_bandwidth_khz, - max_antenna_gain, - power_rule->max_eirp); -} -#else -static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, - u32 desired_bw_khz, - const struct ieee80211_reg_rule *reg_rule) -{ - return; -} -#endif - -/* - * Note that right now we assume the desired channel bandwidth - * is always 20 MHz for each individual channel (HT40 uses 20 MHz - * per channel, the primary and the extension channel). To support - * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a - * new ieee80211_channel.target_bw and re run the regulatory check - * on the wiphy with the target_bw specified. Then we can simply use - * that below for the desired_bw_khz below. - */ -static void handle_channel(struct wiphy *wiphy, - enum nl80211_reg_initiator initiator, - enum ieee80211_band band, - unsigned int chan_idx) -{ - int r; - u32 flags, bw_flags = 0; - u32 desired_bw_khz = MHZ_TO_KHZ(20); - const struct ieee80211_reg_rule *reg_rule = NULL; - const struct ieee80211_power_rule *power_rule = NULL; - const struct ieee80211_freq_range *freq_range = NULL; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *chan; - struct wiphy *request_wiphy = NULL; - - assert_cfg80211_lock(); - - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); - - sband = wiphy->bands[band]; - BUG_ON(chan_idx >= sband->n_channels); - chan = &sband->channels[chan_idx]; - - flags = chan->orig_flags; - - r = freq_reg_info(wiphy, - MHZ_TO_KHZ(chan->center_freq), - desired_bw_khz, - ®_rule); - - if (r) { - /* - * We will disable all channels that do not match our - * received regulatory rule unless the hint is coming - * from a Country IE and the Country IE had no information - * about a band. The IEEE 802.11 spec allows for an AP - * to send only a subset of the regulatory rules allowed, - * so an AP in the US that only supports 2.4 GHz may only send - * a country IE with information for the 2.4 GHz band - * while 5 GHz is still supported. - */ - if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && - r == -ERANGE) - return; - - REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); - chan->flags = IEEE80211_CHAN_DISABLED; - return; - } - - chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); - - power_rule = ®_rule->power_rule; - freq_range = ®_rule->freq_range; - - if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) - bw_flags = IEEE80211_CHAN_NO_HT40; - - if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && - request_wiphy && request_wiphy == wiphy && - request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { - /* - * This guarantees the driver's requested regulatory domain - * will always be used as a base for further regulatory - * settings - */ - chan->flags = chan->orig_flags = - 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 = - (int) MBM_TO_DBM(power_rule->max_eirp); - return; - } - - chan->beacon_found = false; - chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); - chan->max_antenna_gain = min(chan->orig_mag, - (int) MBI_TO_DBI(power_rule->max_antenna_gain)); - if (chan->orig_mpwr) - chan->max_power = min(chan->orig_mpwr, - (int) MBM_TO_DBM(power_rule->max_eirp)); - else - chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); -} - -static void handle_band(struct wiphy *wiphy, - enum ieee80211_band band, - enum nl80211_reg_initiator initiator) -{ - unsigned int i; - struct ieee80211_supported_band *sband; - - BUG_ON(!wiphy->bands[band]); - sband = wiphy->bands[band]; - - for (i = 0; i < sband->n_channels; i++) - handle_channel(wiphy, initiator, band, i); -} - -static bool ignore_reg_update(struct wiphy *wiphy, - enum nl80211_reg_initiator initiator) -{ - if (!last_request) { - REG_DBG_PRINT("Ignoring regulatory request %s since " - "last_request is not set\n", - reg_initiator_name(initiator)); - return true; - } - - if (initiator == NL80211_REGDOM_SET_BY_CORE && - wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { - REG_DBG_PRINT("Ignoring regulatory request %s " - "since the driver uses its own custom " - "regulatory domain\n", - reg_initiator_name(initiator)); - return true; - } - - /* - * wiphy->regd will be set once the device has its own - * desired regulatory domain set - */ - if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && - initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - !is_world_regdom(last_request->alpha2)) { - REG_DBG_PRINT("Ignoring regulatory request %s " - "since the driver requires its own regulatory " - "domain to be set first\n", - reg_initiator_name(initiator)); - return true; - } - - return false; -} - -static void handle_reg_beacon(struct wiphy *wiphy, - unsigned int chan_idx, - struct reg_beacon *reg_beacon) -{ - struct ieee80211_supported_band *sband; - struct ieee80211_channel *chan; - bool channel_changed = false; - struct ieee80211_channel chan_before; - - assert_cfg80211_lock(); - - sband = wiphy->bands[reg_beacon->chan.band]; - chan = &sband->channels[chan_idx]; - - if (likely(chan->center_freq != reg_beacon->chan.center_freq)) - return; - - if (chan->beacon_found) - return; - - chan->beacon_found = true; - - if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) - return; - - chan_before.center_freq = chan->center_freq; - chan_before.flags = chan->flags; - - if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) { - chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; - channel_changed = true; - } - - if (chan->flags & IEEE80211_CHAN_NO_IBSS) { - chan->flags &= ~IEEE80211_CHAN_NO_IBSS; - channel_changed = true; - } - - if (channel_changed) - nl80211_send_beacon_hint_event(wiphy, &chan_before, chan); -} - -/* - * Called when a scan on a wiphy finds a beacon on - * new channel - */ -static void wiphy_update_new_beacon(struct wiphy *wiphy, - struct reg_beacon *reg_beacon) -{ - unsigned int i; - struct ieee80211_supported_band *sband; - - assert_cfg80211_lock(); - - if (!wiphy->bands[reg_beacon->chan.band]) - return; - - sband = wiphy->bands[reg_beacon->chan.band]; - - for (i = 0; i < sband->n_channels; i++) - handle_reg_beacon(wiphy, i, reg_beacon); -} - -/* - * Called upon reg changes or a new wiphy is added - */ -static void wiphy_update_beacon_reg(struct wiphy *wiphy) -{ - unsigned int i; - struct ieee80211_supported_band *sband; - struct reg_beacon *reg_beacon; - - assert_cfg80211_lock(); - - if (list_empty(®_beacon_list)) - return; - - list_for_each_entry(reg_beacon, ®_beacon_list, list) { - if (!wiphy->bands[reg_beacon->chan.band]) - continue; - sband = wiphy->bands[reg_beacon->chan.band]; - for (i = 0; i < sband->n_channels; i++) - handle_reg_beacon(wiphy, i, reg_beacon); - } -} - -static bool reg_is_world_roaming(struct wiphy *wiphy) -{ - if (is_world_regdom(cfg80211_regdomain->alpha2) || - (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) - return true; - if (last_request && - last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) - return true; - return false; -} - -/* Reap the advantages of previously found beacons */ -static void reg_process_beacons(struct wiphy *wiphy) -{ - /* - * Means we are just firing up cfg80211, so no beacons would - * have been processed yet. - */ - if (!last_request) - return; - if (!reg_is_world_roaming(wiphy)) - return; - wiphy_update_beacon_reg(wiphy); -} - -static bool is_ht40_not_allowed(struct ieee80211_channel *chan) -{ - if (!chan) - return true; - if (chan->flags & IEEE80211_CHAN_DISABLED) - return true; - /* This would happen when regulatory rules disallow HT40 completely */ - if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) - return true; - return false; -} - -static void reg_process_ht_flags_channel(struct wiphy *wiphy, - enum ieee80211_band band, - unsigned int chan_idx) -{ - struct ieee80211_supported_band *sband; - struct ieee80211_channel *channel; - struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; - unsigned int i; - - assert_cfg80211_lock(); - - sband = wiphy->bands[band]; - BUG_ON(chan_idx >= sband->n_channels); - channel = &sband->channels[chan_idx]; - - if (is_ht40_not_allowed(channel)) { - channel->flags |= IEEE80211_CHAN_NO_HT40; - return; - } - - /* - * We need to ensure the extension channels exist to - * be able to use HT40- or HT40+, this finds them (or not) - */ - for (i = 0; i < sband->n_channels; i++) { - struct ieee80211_channel *c = &sband->channels[i]; - if (c->center_freq == (channel->center_freq - 20)) - channel_before = c; - if (c->center_freq == (channel->center_freq + 20)) - channel_after = c; - } - - /* - * Please note that this assumes target bandwidth is 20 MHz, - * if that ever changes we also need to change the below logic - * to include that as well. - */ - if (is_ht40_not_allowed(channel_before)) - channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; - else - channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; - - if (is_ht40_not_allowed(channel_after)) - channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; - else - channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; -} - -static void reg_process_ht_flags_band(struct wiphy *wiphy, - enum ieee80211_band band) -{ - unsigned int i; - struct ieee80211_supported_band *sband; - - BUG_ON(!wiphy->bands[band]); - sband = wiphy->bands[band]; - - for (i = 0; i < sband->n_channels; i++) - reg_process_ht_flags_channel(wiphy, band, i); -} - -static void reg_process_ht_flags(struct wiphy *wiphy) -{ - enum ieee80211_band band; - - if (!wiphy) - return; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (wiphy->bands[band]) - reg_process_ht_flags_band(wiphy, band); - } - -} - -static void wiphy_update_regulatory(struct wiphy *wiphy, - enum nl80211_reg_initiator initiator) -{ - enum ieee80211_band band; - - assert_reg_lock(); - - if (ignore_reg_update(wiphy, initiator)) - return; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (wiphy->bands[band]) - handle_band(wiphy, band, initiator); - } - -#ifdef CONFIG_MACH_PX - last_request->processed = true; -#endif - - reg_process_beacons(wiphy); - reg_process_ht_flags(wiphy); - if (wiphy->reg_notifier) - wiphy->reg_notifier(wiphy, last_request); -} - -void regulatory_update(struct wiphy *wiphy, - enum nl80211_reg_initiator setby) -{ - mutex_lock(®_mutex); - wiphy_update_regulatory(wiphy, setby); - mutex_unlock(®_mutex); -} - -static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) -{ - struct cfg80211_registered_device *rdev; - - list_for_each_entry(rdev, &cfg80211_rdev_list, list) - wiphy_update_regulatory(&rdev->wiphy, initiator); -} - -static void handle_channel_custom(struct wiphy *wiphy, - enum ieee80211_band band, - unsigned int chan_idx, - const struct ieee80211_regdomain *regd) -{ - int r; - u32 desired_bw_khz = MHZ_TO_KHZ(20); - u32 bw_flags = 0; - const struct ieee80211_reg_rule *reg_rule = NULL; - const struct ieee80211_power_rule *power_rule = NULL; - const struct ieee80211_freq_range *freq_range = NULL; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *chan; - - assert_reg_lock(); - - sband = wiphy->bands[band]; - BUG_ON(chan_idx >= sband->n_channels); - chan = &sband->channels[chan_idx]; - - r = freq_reg_info_regd(wiphy, - MHZ_TO_KHZ(chan->center_freq), - desired_bw_khz, - ®_rule, - regd); - - if (r) { - REG_DBG_PRINT("Disabling freq %d MHz as custom " - "regd has no rule that fits a %d MHz " - "wide channel\n", - chan->center_freq, - KHZ_TO_MHZ(desired_bw_khz)); - chan->flags = IEEE80211_CHAN_DISABLED; - return; - } - - chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); - - power_rule = ®_rule->power_rule; - freq_range = ®_rule->freq_range; - - if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) - bw_flags = IEEE80211_CHAN_NO_HT40; - - 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); -} - -static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, - const struct ieee80211_regdomain *regd) -{ - unsigned int i; - struct ieee80211_supported_band *sband; - - BUG_ON(!wiphy->bands[band]); - sband = wiphy->bands[band]; - - for (i = 0; i < sband->n_channels; i++) - handle_channel_custom(wiphy, band, i, regd); -} - -/* Used by drivers prior to wiphy registration */ -void wiphy_apply_custom_regulatory(struct wiphy *wiphy, - const struct ieee80211_regdomain *regd) -{ - enum ieee80211_band band; - unsigned int bands_set = 0; - - mutex_lock(®_mutex); - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!wiphy->bands[band]) - continue; - handle_band_custom(wiphy, band, regd); - bands_set++; - } - mutex_unlock(®_mutex); - - /* - * no point in calling this if it won't have any effect - * on your device's supportd bands. - */ - WARN_ON(!bands_set); -} -EXPORT_SYMBOL(wiphy_apply_custom_regulatory); - -/* - * Return value which can be used by ignore_request() to indicate - * it has been determined we should intersect two regulatory domains - */ -#define REG_INTERSECT 1 - -/* This has the logic which determines when a new request - * should be ignored. */ -static int ignore_request(struct wiphy *wiphy, - struct regulatory_request *pending_request) -{ - struct wiphy *last_wiphy = NULL; - - assert_cfg80211_lock(); - - /* All initial requests are respected */ - if (!last_request) - return 0; - - switch (pending_request->initiator) { - case NL80211_REGDOM_SET_BY_CORE: - return 0; - case NL80211_REGDOM_SET_BY_COUNTRY_IE: - - last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); - - if (unlikely(!is_an_alpha2(pending_request->alpha2))) - return -EINVAL; - if (last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE) { - if (last_wiphy != wiphy) { - /* - * Two cards with two APs claiming different - * Country IE alpha2s. We could - * intersect them, but that seems unlikely - * to be correct. Reject second one for now. - */ - if (regdom_changes(pending_request->alpha2)) - return -EOPNOTSUPP; - return -EALREADY; - } - /* - * Two consecutive Country IE hints on the same wiphy. - * This should be picked up early by the driver/stack - */ - if (WARN_ON(regdom_changes(pending_request->alpha2))) - return 0; - return -EALREADY; - } - return 0; - case NL80211_REGDOM_SET_BY_DRIVER: - if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { - if (regdom_changes(pending_request->alpha2)) - return 0; - return -EALREADY; - } - - /* - * This would happen if you unplug and plug your card - * back in or if you add a new device for which the previously - * loaded card also agrees on the regulatory domain. - */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && - !regdom_changes(pending_request->alpha2)) - return -EALREADY; - - return REG_INTERSECT; - case NL80211_REGDOM_SET_BY_USER: - if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) - return REG_INTERSECT; - /* - * If the user knows better the user should set the regdom - * to their country before the IE is picked up - */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && - last_request->intersect) - return -EOPNOTSUPP; - /* - * Process user requests only after previous user/driver/core - * requests have been processed - */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE || - last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || - last_request->initiator == NL80211_REGDOM_SET_BY_USER) { - if (regdom_changes(last_request->alpha2)) - return -EAGAIN; - } - - if (!regdom_changes(pending_request->alpha2)) - return -EALREADY; - - return 0; - } - - return -EINVAL; -} - -static void reg_set_request_processed(void) -{ - bool need_more_processing = false; - - last_request->processed = true; - - spin_lock(®_requests_lock); - if (!list_empty(®_requests_list)) - need_more_processing = true; - spin_unlock(®_requests_lock); - - if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) - cancel_delayed_work_sync(®_timeout); - - if (need_more_processing) - schedule_work(®_work); -} - -/** - * __regulatory_hint - hint to the wireless core a regulatory domain - * @wiphy: if the hint comes from country information from an AP, this - * is required to be set to the wiphy that received the information - * @pending_request: the regulatory request currently being processed - * - * The Wireless subsystem can use this function to hint to the wireless core - * what it believes should be the current regulatory domain. - * - * Returns zero if all went fine, %-EALREADY if a regulatory domain had - * already been set or other standard error codes. - * - * Caller must hold &cfg80211_mutex and ®_mutex - */ -static int __regulatory_hint(struct wiphy *wiphy, - struct regulatory_request *pending_request) -{ - bool intersect = false; - int r = 0; - - assert_cfg80211_lock(); - - r = ignore_request(wiphy, pending_request); - - if (r == REG_INTERSECT) { - if (pending_request->initiator == - NL80211_REGDOM_SET_BY_DRIVER) { - r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); - if (r) { - kfree(pending_request); - return r; - } - } - intersect = true; - } else if (r) { - /* - * If the regulatory domain being requested by the - * driver has already been set just copy it to the - * wiphy - */ - if (r == -EALREADY && - pending_request->initiator == - NL80211_REGDOM_SET_BY_DRIVER) { - r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); - if (r) { - kfree(pending_request); - return r; - } - r = -EALREADY; - goto new_request; - } - kfree(pending_request); - return r; - } - -new_request: - kfree(last_request); - - last_request = pending_request; - last_request->intersect = intersect; - - pending_request = NULL; - - if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { - user_alpha2[0] = last_request->alpha2[0]; - user_alpha2[1] = last_request->alpha2[1]; - } - - /* When r == REG_INTERSECT we do need to call CRDA */ - if (r < 0) { - /* - * Since CRDA will not be called in this case as we already - * have applied the requested regulatory domain before we just - * inform userspace we have processed the request - */ - if (r == -EALREADY) { - nl80211_send_reg_change_event(last_request); - reg_set_request_processed(); - } - return r; - } - - return call_crda(last_request->alpha2); -} - -/* This processes *all* regulatory hints */ -static void reg_process_hint(struct regulatory_request *reg_request) -{ - int r = 0; - struct wiphy *wiphy = NULL; - enum nl80211_reg_initiator initiator = reg_request->initiator; - - BUG_ON(!reg_request->alpha2); - - if (wiphy_idx_valid(reg_request->wiphy_idx)) - wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); - - if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && - !wiphy) { - kfree(reg_request); - return; - } - - r = __regulatory_hint(wiphy, reg_request); - /* This is required so that the orig_* parameters are saved */ - if (r == -EALREADY && wiphy && - wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { - wiphy_update_regulatory(wiphy, initiator); - return; - } - - /* - * We only time out user hints, given that they should be the only - * source of bogus requests. - */ - if (r != -EALREADY && - reg_request->initiator == NL80211_REGDOM_SET_BY_USER) - schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); -} - -/* - * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* - * Regulatory hints come on a first come first serve basis and we - * must process each one atomically. - */ -static void reg_process_pending_hints(void) -{ - struct regulatory_request *reg_request; - - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); - - /* When last_request->processed becomes true this will be rescheduled */ - if (last_request && !last_request->processed) { - REG_DBG_PRINT("Pending regulatory request, waiting " - "for it to be processed...\n"); - goto out; - } - - spin_lock(®_requests_lock); - - if (list_empty(®_requests_list)) { - spin_unlock(®_requests_lock); - goto out; - } - - reg_request = list_first_entry(®_requests_list, - struct regulatory_request, - list); - list_del_init(®_request->list); - - spin_unlock(®_requests_lock); - - reg_process_hint(reg_request); - -out: - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); -} - -/* Processes beacon hints -- this has nothing to do with country IEs */ -static void reg_process_pending_beacon_hints(void) -{ - struct cfg80211_registered_device *rdev; - struct reg_beacon *pending_beacon, *tmp; - - /* - * No need to hold the reg_mutex here as we just touch wiphys - * and do not read or access regulatory variables. - */ - mutex_lock(&cfg80211_mutex); - - /* This goes through the _pending_ beacon list */ - spin_lock_bh(®_pending_beacons_lock); - - if (list_empty(®_pending_beacons)) { - spin_unlock_bh(®_pending_beacons_lock); - goto out; - } - - list_for_each_entry_safe(pending_beacon, tmp, - ®_pending_beacons, list) { - - list_del_init(&pending_beacon->list); - - /* Applies the beacon hint to current wiphys */ - list_for_each_entry(rdev, &cfg80211_rdev_list, list) - wiphy_update_new_beacon(&rdev->wiphy, pending_beacon); - - /* Remembers the beacon hint for new wiphys or reg changes */ - list_add_tail(&pending_beacon->list, ®_beacon_list); - } - - spin_unlock_bh(®_pending_beacons_lock); -out: - mutex_unlock(&cfg80211_mutex); -} - -static void reg_todo(struct work_struct *work) -{ - reg_process_pending_hints(); - reg_process_pending_beacon_hints(); -} - -static void queue_regulatory_request(struct regulatory_request *request) -{ - if (isalpha(request->alpha2[0])) - request->alpha2[0] = toupper(request->alpha2[0]); - if (isalpha(request->alpha2[1])) - request->alpha2[1] = toupper(request->alpha2[1]); - - spin_lock(®_requests_lock); - list_add_tail(&request->list, ®_requests_list); - spin_unlock(®_requests_lock); - - schedule_work(®_work); -} - -/* - * Core regulatory hint -- happens during cfg80211_init() - * and when we restore regulatory settings. - */ -static int regulatory_hint_core(const char *alpha2) -{ - struct regulatory_request *request; - - kfree(last_request); - last_request = NULL; - - request = kzalloc(sizeof(struct regulatory_request), - GFP_KERNEL); - if (!request) - return -ENOMEM; - - request->alpha2[0] = alpha2[0]; - request->alpha2[1] = alpha2[1]; - request->initiator = NL80211_REGDOM_SET_BY_CORE; - - queue_regulatory_request(request); - - return 0; -} - -/* User hints */ -int regulatory_hint_user(const char *alpha2) -{ - struct regulatory_request *request; - - BUG_ON(!alpha2); - - request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); - if (!request) - return -ENOMEM; - - request->wiphy_idx = WIPHY_IDX_STALE; - request->alpha2[0] = alpha2[0]; - request->alpha2[1] = alpha2[1]; - request->initiator = NL80211_REGDOM_SET_BY_USER; - - queue_regulatory_request(request); - - return 0; -} - -/* Driver hints */ -int regulatory_hint(struct wiphy *wiphy, const char *alpha2) -{ - struct regulatory_request *request; - - BUG_ON(!alpha2); - BUG_ON(!wiphy); - - request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); - if (!request) - return -ENOMEM; - - request->wiphy_idx = get_wiphy_idx(wiphy); - - /* Must have registered wiphy first */ - BUG_ON(!wiphy_idx_valid(request->wiphy_idx)); - - request->alpha2[0] = alpha2[0]; - request->alpha2[1] = alpha2[1]; - request->initiator = NL80211_REGDOM_SET_BY_DRIVER; - - queue_regulatory_request(request); - - return 0; -} -EXPORT_SYMBOL(regulatory_hint); - -/* - * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and - * therefore cannot iterate over the rdev list here. - */ -void regulatory_hint_11d(struct wiphy *wiphy, - enum ieee80211_band band, - u8 *country_ie, - u8 country_ie_len) -{ - char alpha2[2]; - enum environment_cap env = ENVIRON_ANY; - struct regulatory_request *request; - - mutex_lock(®_mutex); - - if (unlikely(!last_request)) - goto out; - - /* IE len must be evenly divisible by 2 */ - if (country_ie_len & 0x01) - goto out; - - if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) - goto out; - - alpha2[0] = country_ie[0]; - alpha2[1] = country_ie[1]; - - if (country_ie[2] == 'I') - env = ENVIRON_INDOOR; - else if (country_ie[2] == 'O') - env = ENVIRON_OUTDOOR; - - /* - * We will run this only upon a successful connection on cfg80211. - * We leave conflict resolution to the workqueue, where can hold - * cfg80211_mutex. - */ - if (likely(last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy_idx_valid(last_request->wiphy_idx))) - goto out; - - request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); - if (!request) - goto out; - - request->wiphy_idx = get_wiphy_idx(wiphy); - request->alpha2[0] = alpha2[0]; - request->alpha2[1] = alpha2[1]; - request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; - request->country_ie_env = env; - - mutex_unlock(®_mutex); - - queue_regulatory_request(request); - - return; - -out: - mutex_unlock(®_mutex); -} - -static void restore_alpha2(char *alpha2, bool reset_user) -{ - /* indicates there is no alpha2 to consider for restoration */ - alpha2[0] = '9'; - alpha2[1] = '7'; - - /* The user setting has precedence over the module parameter */ - if (is_user_regdom_saved()) { - /* Unless we're asked to ignore it and reset it */ - if (reset_user) { - REG_DBG_PRINT("Restoring regulatory settings " - "including user preference\n"); - user_alpha2[0] = '9'; - user_alpha2[1] = '7'; - - /* - * If we're ignoring user settings, we still need to - * check the module parameter to ensure we put things - * back as they were for a full restore. - */ - if (!is_world_regdom(ieee80211_regdom)) { - REG_DBG_PRINT("Keeping preference on " - "module parameter ieee80211_regdom: %c%c\n", - ieee80211_regdom[0], - ieee80211_regdom[1]); - alpha2[0] = ieee80211_regdom[0]; - alpha2[1] = ieee80211_regdom[1]; - } - } else { - REG_DBG_PRINT("Restoring regulatory settings " - "while preserving user preference for: %c%c\n", - user_alpha2[0], - user_alpha2[1]); - alpha2[0] = user_alpha2[0]; - alpha2[1] = user_alpha2[1]; - } - } else if (!is_world_regdom(ieee80211_regdom)) { - REG_DBG_PRINT("Keeping preference on " - "module parameter ieee80211_regdom: %c%c\n", - ieee80211_regdom[0], - ieee80211_regdom[1]); - alpha2[0] = ieee80211_regdom[0]; - alpha2[1] = ieee80211_regdom[1]; - } else - REG_DBG_PRINT("Restoring regulatory settings\n"); -} - -/* - * Restoring regulatory settings involves ingoring any - * possibly stale country IE information and user regulatory - * settings if so desired, this includes any beacon hints - * learned as we could have traveled outside to another country - * after disconnection. To restore regulatory settings we do - * exactly what we did at bootup: - * - * - send a core regulatory hint - * - send a user regulatory hint if applicable - * - * Device drivers that send a regulatory hint for a specific country - * keep their own regulatory domain on wiphy->regd so that does does - * not need to be remembered. - */ -static void restore_regulatory_settings(bool reset_user) -{ - char alpha2[2]; - struct reg_beacon *reg_beacon, *btmp; - struct regulatory_request *reg_request, *tmp; - LIST_HEAD(tmp_reg_req_list); - - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); - - reset_regdomains(); - restore_alpha2(alpha2, reset_user); - - /* - * If there's any pending requests we simply - * stash them to a temporary pending queue and - * add then after we've restored regulatory - * settings. - */ - spin_lock(®_requests_lock); - if (!list_empty(®_requests_list)) { - list_for_each_entry_safe(reg_request, tmp, - ®_requests_list, list) { - if (reg_request->initiator != - NL80211_REGDOM_SET_BY_USER) - continue; - list_del(®_request->list); - list_add_tail(®_request->list, &tmp_reg_req_list); - } - } - spin_unlock(®_requests_lock); - - /* Clear beacon hints */ - spin_lock_bh(®_pending_beacons_lock); - if (!list_empty(®_pending_beacons)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_pending_beacons, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } - } - spin_unlock_bh(®_pending_beacons_lock); - - if (!list_empty(®_beacon_list)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_beacon_list, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } - } - - /* First restore to the basic regulatory settings */ - cfg80211_regdomain = cfg80211_world_regdom; - - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); - - regulatory_hint_core(cfg80211_regdomain->alpha2); - - /* - * This restores the ieee80211_regdom module parameter - * preference or the last user requested regulatory - * settings, user regulatory settings takes precedence. - */ - if (is_an_alpha2(alpha2)) - regulatory_hint_user(user_alpha2); - - if (list_empty(&tmp_reg_req_list)) - return; - - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); - - spin_lock(®_requests_lock); - list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) { - REG_DBG_PRINT("Adding request for country %c%c back " - "into the queue\n", - reg_request->alpha2[0], - reg_request->alpha2[1]); - list_del(®_request->list); - list_add_tail(®_request->list, ®_requests_list); - } - spin_unlock(®_requests_lock); - - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); - - REG_DBG_PRINT("Kicking the queue\n"); - - schedule_work(®_work); -} - -void regulatory_hint_disconnect(void) -{ - REG_DBG_PRINT("All devices are disconnected, going to " - "restore regulatory settings\n"); - restore_regulatory_settings(false); -} - -static bool freq_is_chan_12_13_14(u16 freq) -{ - if (freq == ieee80211_channel_to_frequency(12, IEEE80211_BAND_2GHZ) || - freq == ieee80211_channel_to_frequency(13, IEEE80211_BAND_2GHZ) || - freq == ieee80211_channel_to_frequency(14, IEEE80211_BAND_2GHZ)) - return true; - return false; -} - -int regulatory_hint_found_beacon(struct wiphy *wiphy, - struct ieee80211_channel *beacon_chan, - gfp_t gfp) -{ - struct reg_beacon *reg_beacon; - - if (likely((beacon_chan->beacon_found || - (beacon_chan->flags & IEEE80211_CHAN_RADAR) || - (beacon_chan->band == IEEE80211_BAND_2GHZ && - !freq_is_chan_12_13_14(beacon_chan->center_freq))))) - return 0; - - reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); - if (!reg_beacon) - return -ENOMEM; - - REG_DBG_PRINT("Found new beacon on " - "frequency: %d MHz (Ch %d) on %s\n", - beacon_chan->center_freq, - ieee80211_frequency_to_channel(beacon_chan->center_freq), - wiphy_name(wiphy)); - - memcpy(®_beacon->chan, beacon_chan, - sizeof(struct ieee80211_channel)); - - - /* - * Since we can be called from BH or and non-BH context - * we must use spin_lock_bh() - */ - spin_lock_bh(®_pending_beacons_lock); - list_add_tail(®_beacon->list, ®_pending_beacons); - spin_unlock_bh(®_pending_beacons_lock); - - schedule_work(®_work); - - return 0; -} - -static void print_rd_rules(const struct ieee80211_regdomain *rd) -{ - unsigned int i; - const struct ieee80211_reg_rule *reg_rule = NULL; - const struct ieee80211_freq_range *freq_range = NULL; - const struct ieee80211_power_rule *power_rule = NULL; - - pr_info(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n"); - - for (i = 0; i < rd->n_reg_rules; i++) { - reg_rule = &rd->reg_rules[i]; - freq_range = ®_rule->freq_range; - power_rule = ®_rule->power_rule; - - /* - * There may not be documentation for max antenna gain - * in certain regions - */ - if (power_rule->max_antenna_gain) - pr_info(" (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm)\n", - freq_range->start_freq_khz, - freq_range->end_freq_khz, - freq_range->max_bandwidth_khz, - power_rule->max_antenna_gain, - power_rule->max_eirp); - else - pr_info(" (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm)\n", - freq_range->start_freq_khz, - freq_range->end_freq_khz, - freq_range->max_bandwidth_khz, - power_rule->max_eirp); - } -} - -static void print_regdomain(const struct ieee80211_regdomain *rd) -{ - - if (is_intersected_alpha2(rd->alpha2)) { - - if (last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE) { - struct cfg80211_registered_device *rdev; - rdev = cfg80211_rdev_by_wiphy_idx( - last_request->wiphy_idx); - if (rdev) { - pr_info("Current regulatory domain updated by AP to: %c%c\n", - rdev->country_ie_alpha2[0], - rdev->country_ie_alpha2[1]); - } else - pr_info("Current regulatory domain intersected:\n"); - } else - pr_info("Current regulatory domain intersected:\n"); - } else if (is_world_regdom(rd->alpha2)) - pr_info("World regulatory domain updated:\n"); - else { - if (is_unknown_alpha2(rd->alpha2)) - pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); - else - pr_info("Regulatory domain changed to country: %c%c\n", - rd->alpha2[0], rd->alpha2[1]); - } - print_rd_rules(rd); -} - -static void print_regdomain_info(const struct ieee80211_regdomain *rd) -{ - pr_info("Regulatory domain: %c%c\n", rd->alpha2[0], rd->alpha2[1]); - print_rd_rules(rd); -} - -/* Takes ownership of rd only if it doesn't fail */ -static int __set_regdom(const struct ieee80211_regdomain *rd) -{ - const struct ieee80211_regdomain *intersected_rd = NULL; - struct cfg80211_registered_device *rdev = NULL; - struct wiphy *request_wiphy; - /* Some basic sanity checks first */ - - if (is_world_regdom(rd->alpha2)) { - if (WARN_ON(!reg_is_valid_request(rd->alpha2))) - return -EINVAL; - update_world_regdomain(rd); - return 0; - } - - if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && - !is_unknown_alpha2(rd->alpha2)) - return -EINVAL; - - if (!last_request) - return -EINVAL; - - /* - * Lets only bother proceeding on the same alpha2 if the current - * rd is non static (it means CRDA was present and was used last) - * and the pending request came in from a country IE - */ - if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { - /* - * If someone else asked us to change the rd lets only bother - * checking if the alpha2 changes if CRDA was already called - */ - if (!regdom_changes(rd->alpha2)) - return -EINVAL; - } - - /* - * Now lets set the regulatory domain, update all driver channels - * and finally inform them of what we have done, in case they want - * to review or adjust their own settings based on their own - * internal EEPROM data - */ - - if (WARN_ON(!reg_is_valid_request(rd->alpha2))) - return -EINVAL; - - if (!is_valid_rd(rd)) { - pr_err("Invalid regulatory domain detected:\n"); - print_regdomain_info(rd); - return -EINVAL; - } - - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); - - if (!last_request->intersect) { - int r; - - if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { - reset_regdomains(); - cfg80211_regdomain = rd; - return 0; - } - - /* - * For a driver hint, lets copy the regulatory domain the - * driver wanted to the wiphy to deal with conflicts - */ - - /* - * Userspace could have sent two replies with only - * one kernel request. - */ - if (request_wiphy->regd) - return -EALREADY; - - r = reg_copy_regd(&request_wiphy->regd, rd); - if (r) - return r; - - reset_regdomains(); - cfg80211_regdomain = rd; - return 0; - } - - /* Intersection requires a bit more work */ - - if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { - - intersected_rd = regdom_intersect(rd, cfg80211_regdomain); - if (!intersected_rd) - return -EINVAL; - - /* - * We can trash what CRDA provided now. - * However if a driver requested this specific regulatory - * domain we keep it for its private use - */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) - request_wiphy->regd = rd; - else - kfree(rd); - - rd = NULL; - - reset_regdomains(); - cfg80211_regdomain = intersected_rd; - - return 0; - } - - if (!intersected_rd) - return -EINVAL; - - rdev = wiphy_to_dev(request_wiphy); - - rdev->country_ie_alpha2[0] = rd->alpha2[0]; - rdev->country_ie_alpha2[1] = rd->alpha2[1]; - rdev->env = last_request->country_ie_env; - - BUG_ON(intersected_rd == rd); - - kfree(rd); - rd = NULL; - - reset_regdomains(); - cfg80211_regdomain = intersected_rd; - - return 0; -} - - -/* - * Use this call to set the current regulatory domain. Conflicts with - * multiple drivers can be ironed out later. Caller must've already - * kmalloc'd the rd structure. Caller must hold cfg80211_mutex - */ -int set_regdom(const struct ieee80211_regdomain *rd) -{ - int r; - - assert_cfg80211_lock(); - - mutex_lock(®_mutex); - - /* Note that this doesn't update the wiphys, this is done below */ - r = __set_regdom(rd); - if (r) { - kfree(rd); - mutex_unlock(®_mutex); - return r; - } - - /* This would make this whole thing pointless */ - if (!last_request->intersect) - BUG_ON(rd != cfg80211_regdomain); - - /* update all wiphys now with the new established regulatory domain */ - update_all_wiphy_regulatory(last_request->initiator); - - print_regdomain(cfg80211_regdomain); - - nl80211_send_reg_change_event(last_request); - - reg_set_request_processed(); - - mutex_unlock(®_mutex); - - return r; -} - -#ifdef CONFIG_HOTPLUG -int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - if (last_request && !last_request->processed) { - if (add_uevent_var(env, "COUNTRY=%c%c", - last_request->alpha2[0], - last_request->alpha2[1])) - return -ENOMEM; - } - - return 0; -} -#else -int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - return -ENODEV; -} -#endif /* CONFIG_HOTPLUG */ - -/* Caller must hold cfg80211_mutex */ -void reg_device_remove(struct wiphy *wiphy) -{ - struct wiphy *request_wiphy = NULL; - - assert_cfg80211_lock(); - - mutex_lock(®_mutex); - - kfree(wiphy->regd); - - if (last_request) - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); - - if (!request_wiphy || request_wiphy != wiphy) - goto out; - - last_request->wiphy_idx = WIPHY_IDX_STALE; - last_request->country_ie_env = ENVIRON_ANY; -out: - mutex_unlock(®_mutex); -} - -static void reg_timeout_work(struct work_struct *work) -{ - REG_DBG_PRINT("Timeout while waiting for CRDA to reply, " - "restoring regulatory settings\n"); - restore_regulatory_settings(true); -} - -int __init regulatory_init(void) -{ - int err = 0; - - reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0); - if (IS_ERR(reg_pdev)) - return PTR_ERR(reg_pdev); - - reg_pdev->dev.type = ®_device_type; - - spin_lock_init(®_requests_lock); - spin_lock_init(®_pending_beacons_lock); - - cfg80211_regdomain = cfg80211_world_regdom; - - user_alpha2[0] = '9'; - user_alpha2[1] = '7'; - - /* We always try to get an update for the static regdomain */ - err = regulatory_hint_core(cfg80211_regdomain->alpha2); - if (err) { - if (err == -ENOMEM) - return err; - /* - * N.B. kobject_uevent_env() can fail mainly for when we're out - * memory which is handled and propagated appropriately above - * but it can also fail during a netlink_broadcast() or during - * early boot for call_usermodehelper(). For now treat these - * errors as non-fatal. - */ - pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); -#ifdef CONFIG_CFG80211_REG_DEBUG - /* We want to find out exactly why when debugging */ - WARN_ON(err); -#endif - } - - /* - * Finally, if the user set the module parameter treat it - * as a user hint. - */ - if (!is_world_regdom(ieee80211_regdom)) - regulatory_hint_user(ieee80211_regdom); - - return 0; -} - -void /* __init_or_exit */ regulatory_exit(void) -{ - struct regulatory_request *reg_request, *tmp; - struct reg_beacon *reg_beacon, *btmp; - - cancel_work_sync(®_work); - cancel_delayed_work_sync(®_timeout); - - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); - - reset_regdomains(); - - kfree(last_request); - - last_request = NULL; - dev_set_uevent_suppress(®_pdev->dev, true); - - platform_device_unregister(reg_pdev); - - spin_lock_bh(®_pending_beacons_lock); - if (!list_empty(®_pending_beacons)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_pending_beacons, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } - } - spin_unlock_bh(®_pending_beacons_lock); - - if (!list_empty(®_beacon_list)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_beacon_list, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } - } - - spin_lock(®_requests_lock); - if (!list_empty(®_requests_list)) { - list_for_each_entry_safe(reg_request, tmp, - ®_requests_list, list) { - list_del(®_request->list); - kfree(reg_request); - } - } - spin_unlock(®_requests_lock); - - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); -} diff --git a/net/wireless_ath/reg.h b/net/wireless_ath/reg.h deleted file mode 100755 index 4a56799..0000000 --- a/net/wireless_ath/reg.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef __NET_WIRELESS_REG_H -#define __NET_WIRELESS_REG_H - -extern const struct ieee80211_regdomain *cfg80211_regdomain; - -bool is_world_regdom(const char *alpha2); -bool reg_is_valid_request(const char *alpha2); - -int regulatory_hint_user(const char *alpha2); - -int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env); -void reg_device_remove(struct wiphy *wiphy); - -int __init regulatory_init(void); -void regulatory_exit(void); - -int set_regdom(const struct ieee80211_regdomain *rd); - -void regulatory_update(struct wiphy *wiphy, enum nl80211_reg_initiator setby); - -/** - * regulatory_hint_found_beacon - hints a beacon was found on a channel - * @wiphy: the wireless device where the beacon was found on - * @beacon_chan: the channel on which the beacon was found on - * @gfp: context flags - * - * This informs the wireless core that a beacon from an AP was found on - * the channel provided. This allows the wireless core to make educated - * guesses on regulatory to help with world roaming. This is only used for - * world roaming -- when we do not know our current location. This is - * only useful on channels 12, 13 and 14 on the 2 GHz band as channels - * 1-11 are already enabled by the world regulatory domain; and on - * non-radar 5 GHz channels. - * - * Drivers do not need to call this, cfg80211 will do it for after a scan - * on a newly found BSS. If you cannot make use of this feature you can - * set the wiphy->disable_beacon_hints to true. - */ -int regulatory_hint_found_beacon(struct wiphy *wiphy, - struct ieee80211_channel *beacon_chan, - gfp_t gfp); - -/** - * regulatory_hint_11d - hints a country IE as a regulatory domain - * @wiphy: the wireless device giving the hint (used only for reporting - * conflicts) - * @band: the band on which the country IE was received on. This determines - * the band we'll process the country IE channel triplets for. - * @country_ie: pointer to the country IE - * @country_ie_len: length of the country IE - * - * We will intersect the rd with the what CRDA tells us should apply - * for the alpha2 this country IE belongs to, this prevents APs from - * sending us incorrect or outdated information against a country. - * - * The AP is expected to provide Country IE channel triplets for the - * band it is on. It is technically possible for APs to send channel - * country IE triplets even for channels outside of the band they are - * in but for that they would have to use the regulatory extension - * in combination with a triplet but this behaviour is currently - * not observed. For this reason if a triplet is seen with channel - * information for a band the BSS is not present in it will be ignored. - */ -void regulatory_hint_11d(struct wiphy *wiphy, - enum ieee80211_band band, - u8 *country_ie, - u8 country_ie_len); - -/** - * regulatory_hint_disconnect - informs all devices have been disconneted - * - * Regulotory rules can be enhanced further upon scanning and upon - * connection to an AP. These rules become stale if we disconnect - * and go to another country, whether or not we suspend and resume. - * If we suspend, go to another country and resume we'll automatically - * get disconnected shortly after resuming and things will be reset as well. - * This routine is a helper to restore regulatory settings to how they were - * prior to our first connect attempt. This includes ignoring country IE and - * beacon regulatory hints. The ieee80211_regdom module parameter will always - * be respected but if a user had set the regulatory domain that will take - * precedence. - * - * Must be called from process context. - */ -void regulatory_hint_disconnect(void); - -#endif /* __NET_WIRELESS_REG_H */ diff --git a/net/wireless_ath/regdb.h b/net/wireless_ath/regdb.h deleted file mode 100755 index 818222c..0000000 --- a/net/wireless_ath/regdb.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef __REGDB_H__ -#define __REGDB_H__ - -extern const struct ieee80211_regdomain *reg_regdb[]; -extern int reg_regdb_size; - -#endif /* __REGDB_H__ */ diff --git a/net/wireless_ath/scan.c b/net/wireless_ath/scan.c deleted file mode 100755 index 8f5fa19..0000000 --- a/net/wireless_ath/scan.c +++ /dev/null @@ -1,1381 +0,0 @@ -/* - * cfg80211 scan result handling - * - * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> - */ -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/wireless.h> -#include <linux/nl80211.h> -#include <linux/etherdevice.h> -#include <net/arp.h> -#include <net/cfg80211.h> -#include <net/cfg80211-wext.h> -#include <net/iw_handler.h> -#include "core.h" -#include "nl80211.h" -#include "wext-compat.h" - -#define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) - -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) -{ - struct cfg80211_scan_request *request; - struct net_device *dev; -#ifdef CONFIG_CFG80211_WEXT - union iwreq_data wrqu; -#endif - - ASSERT_RDEV_LOCK(rdev); - - request = rdev->scan_req; - - if (!request) - return; - - dev = request->dev; - - /* - * This must be before sending the other events! - * Otherwise, wpa_supplicant gets completely confused with - * wext events. - */ - cfg80211_sme_scan_done(dev); - - if (request->aborted) - nl80211_send_scan_aborted(rdev, dev); - else - nl80211_send_scan_done(rdev, dev); - -#ifdef CONFIG_CFG80211_WEXT - if (!request->aborted) { - memset(&wrqu, 0, sizeof(wrqu)); - - wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); - } -#endif - - dev_put(dev); - - rdev->scan_req = NULL; - - /* - * OK. If this is invoked with "leak" then we can't - * free this ... but we've cleaned it up anyway. The - * driver failed to call the scan_done callback, so - * all bets are off, it might still be trying to use - * the scan request or not ... if it accesses the dev - * in there (it shouldn't anyway) then it may crash. - */ - if (!leak) - kfree(request); -} - -void __cfg80211_scan_done(struct work_struct *wk) -{ - struct cfg80211_registered_device *rdev; - - rdev = container_of(wk, struct cfg80211_registered_device, - scan_done_wk); - - cfg80211_lock_rdev(rdev); - ___cfg80211_scan_done(rdev, false); - cfg80211_unlock_rdev(rdev); -} - -void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) -{ - WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); - - request->aborted = aborted; - queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk); -} -EXPORT_SYMBOL(cfg80211_scan_done); - -void __cfg80211_sched_scan_results(struct work_struct *wk) -{ - struct cfg80211_registered_device *rdev; - - rdev = container_of(wk, struct cfg80211_registered_device, - sched_scan_results_wk); - - 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); - - mutex_unlock(&rdev->sched_scan_mtx); -} - -void cfg80211_sched_scan_results(struct wiphy *wiphy) -{ - /* ignore if we're not scanning */ - if (wiphy_to_dev(wiphy)->sched_scan_req) - queue_work(cfg80211_wq, - &wiphy_to_dev(wiphy)->sched_scan_results_wk); -} -EXPORT_SYMBOL(cfg80211_sched_scan_results); - -void cfg80211_sched_scan_stopped(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - mutex_lock(&rdev->sched_scan_mtx); - __cfg80211_stop_sched_scan(rdev, true); - mutex_unlock(&rdev->sched_scan_mtx); -} -EXPORT_SYMBOL(cfg80211_sched_scan_stopped); - -int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, - bool driver_initiated) -{ - struct net_device *dev; - - lockdep_assert_held(&rdev->sched_scan_mtx); - - if (!rdev->sched_scan_req) - return -ENOENT; - - dev = rdev->sched_scan_req->dev; - - if (!driver_initiated) { - int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); - if (err) - return err; - } - - nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); - - kfree(rdev->sched_scan_req); - rdev->sched_scan_req = NULL; - - 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) -{ - struct cfg80211_internal_bss *bss; - unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); - - list_for_each_entry(bss, &dev->bss_list, list) { - bss->ts -= age_jiffies; - } -} - -/* 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++; -} - -/* must hold dev->bss_lock! */ -void cfg80211_bss_expire_all(struct cfg80211_registered_device *dev) -{ - struct cfg80211_internal_bss *bss, *tmp; - bool expired = false; - - printk("%s() Enter - steven", __func__); - - list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { - __cfg80211_unlink_bss(dev, bss); - expired = true; - } - - if (expired) - dev->bss_generation++; -} - -const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) -{ - while (len > 2 && ies[0] != eid) { - len -= ies[1] + 2; - ies += ies[1] + 2; - } - if (len < 2) - return NULL; - if (len < 2 + ies[1]) - return NULL; - return ies; -} -EXPORT_SYMBOL(cfg80211_find_ie); - -const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, - const u8 *ies, int len) -{ - struct ieee80211_vendor_ie *ie; - const u8 *pos = ies, *end = ies + len; - int ie_oui; - - while (pos < end) { - pos = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, pos, - end - pos); - if (!pos) - return NULL; - - if (end - pos < sizeof(*ie)) - return NULL; - - ie = (struct ieee80211_vendor_ie *)pos; - ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2]; - if (ie_oui == oui && ie->oui_type == oui_type) - return pos; - - pos += 2 + ie->len; - } - return NULL; -} -EXPORT_SYMBOL(cfg80211_find_vendor_ie); - -static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) -{ - const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); - const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); - - /* equal if both missing */ - if (!ie1 && !ie2) - return 0; - /* sort missing IE before (left of) present IE */ - if (!ie1) - return -1; - if (!ie2) - return 1; - - /* sort by length first, then by contents */ - if (ie1[1] != ie2[1]) - return ie2[1] - ie1[1]; - return memcmp(ie1 + 2, ie2 + 2, ie1[1]); -} - -static bool is_bss(struct cfg80211_bss *a, - const u8 *bssid, - const u8 *ssid, size_t ssid_len) -{ - const u8 *ssidie; - - if (bssid && compare_ether_addr(a->bssid, bssid)) - return false; - - if (!ssid) - return true; - - ssidie = cfg80211_find_ie(WLAN_EID_SSID, - a->information_elements, - a->len_information_elements); - if (!ssidie) - return false; - if (ssidie[1] != ssid_len) - return false; - return memcmp(ssidie + 2, ssid, ssid_len) == 0; -} - -static bool is_mesh_bss(struct cfg80211_bss *a) -{ - const u8 *ie; - - if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) - return false; - - ie = cfg80211_find_ie(WLAN_EID_MESH_ID, - a->information_elements, - a->len_information_elements); - if (!ie) - return false; - - ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, - a->information_elements, - a->len_information_elements); - if (!ie) - return false; - - return true; -} - -static bool is_mesh(struct cfg80211_bss *a, - const u8 *meshid, size_t meshidlen, - const u8 *meshcfg) -{ - const u8 *ie; - - if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) - return false; - - ie = cfg80211_find_ie(WLAN_EID_MESH_ID, - a->information_elements, - a->len_information_elements); - if (!ie) - return false; - if (ie[1] != meshidlen) - return false; - if (memcmp(ie + 2, meshid, meshidlen)) - return false; - - ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, - a->information_elements, - a->len_information_elements); - if (!ie) - return false; - if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) - return false; - - /* - * Ignore mesh capability (last two bytes of the IE) when - * comparing since that may differ between stations taking - * part in the same mesh. - */ - return memcmp(ie + 2, meshcfg, - sizeof(struct ieee80211_meshconf_ie) - 2) == 0; -} - -static int cmp_bss_core(struct cfg80211_bss *a, - struct cfg80211_bss *b) -{ - int r; - - if (a->channel != b->channel) - return b->channel->center_freq - a->channel->center_freq; - - if (is_mesh_bss(a) && is_mesh_bss(b)) { - r = cmp_ies(WLAN_EID_MESH_ID, - a->information_elements, - a->len_information_elements, - b->information_elements, - b->len_information_elements); - if (r) - return r; - return cmp_ies(WLAN_EID_MESH_CONFIG, - a->information_elements, - a->len_information_elements, - b->information_elements, - b->len_information_elements); - } - - return memcmp(a->bssid, b->bssid, ETH_ALEN); -} - -static int cmp_bss(struct cfg80211_bss *a, - struct cfg80211_bss *b) -{ - int r; - - r = cmp_bss_core(a, b); - if (r) - return r; - - return cmp_ies(WLAN_EID_SSID, - a->information_elements, - a->len_information_elements, - b->information_elements, - b->len_information_elements); -} - -static int cmp_hidden_bss(struct cfg80211_bss *a, - struct cfg80211_bss *b) -{ - const u8 *ie1; - const u8 *ie2; - int i; - int r; - - r = cmp_bss_core(a, b); - if (r) - return r; - - ie1 = cfg80211_find_ie(WLAN_EID_SSID, - a->information_elements, - a->len_information_elements); - ie2 = cfg80211_find_ie(WLAN_EID_SSID, - b->information_elements, - b->len_information_elements); - - /* Key comparator must use same algorithm in any rb-tree - * search function (order is important), otherwise ordering - * of items in the tree is broken and search gives incorrect - * results. This code uses same order as cmp_ies() does. */ - - /* sort missing IE before (left of) present IE */ - if (!ie1) - return -1; - if (!ie2) - return 1; - - /* zero-size SSID is used as an indication of the hidden bss */ - if (!ie2[1]) - return 0; - - /* sort by length first, then by contents */ - if (ie1[1] != ie2[1]) - return ie2[1] - ie1[1]; - - /* zeroed SSID ie is another indication of a hidden bss */ - for (i = 0; i < ie2[1]; i++) - if (ie2[i + 2]) - return -1; - - return 0; -} - -struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, - struct ieee80211_channel *channel, - const u8 *bssid, - const u8 *ssid, size_t ssid_len, - u16 capa_mask, u16 capa_val) -{ - struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); - struct cfg80211_internal_bss *bss, *res = NULL; - unsigned long now = jiffies; - - spin_lock_bh(&dev->bss_lock); - - list_for_each_entry(bss, &dev->bss_list, list) { - if ((bss->pub.capability & capa_mask) != capa_val) - continue; - if (channel && bss->pub.channel != channel) - continue; - /* Don't get expired BSS structs */ - if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) && - !atomic_read(&bss->hold)) - continue; - if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { - res = bss; - kref_get(&res->ref); - break; - } - } - - spin_unlock_bh(&dev->bss_lock); - if (!res) - return NULL; - return &res->pub; -} -EXPORT_SYMBOL(cfg80211_get_bss); - -struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, - struct ieee80211_channel *channel, - const u8 *meshid, size_t meshidlen, - const u8 *meshcfg) -{ - struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); - struct cfg80211_internal_bss *bss, *res = NULL; - - spin_lock_bh(&dev->bss_lock); - - list_for_each_entry(bss, &dev->bss_list, list) { - if (channel && bss->pub.channel != channel) - continue; - if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) { - res = bss; - kref_get(&res->ref); - break; - } - } - - spin_unlock_bh(&dev->bss_lock); - if (!res) - return NULL; - return &res->pub; -} -EXPORT_SYMBOL(cfg80211_get_mesh); - - -static void rb_insert_bss(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *bss) -{ - struct rb_node **p = &dev->bss_tree.rb_node; - struct rb_node *parent = NULL; - struct cfg80211_internal_bss *tbss; - int cmp; - - while (*p) { - parent = *p; - tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); - - cmp = cmp_bss(&bss->pub, &tbss->pub); - - if (WARN_ON(!cmp)) { - /* will sort of leak this BSS */ - return; - } - - if (cmp < 0) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - - rb_link_node(&bss->rbn, parent, p); - rb_insert_color(&bss->rbn, &dev->bss_tree); -} - -static struct cfg80211_internal_bss * -rb_find_bss(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *res) -{ - struct rb_node *n = dev->bss_tree.rb_node; - struct cfg80211_internal_bss *bss; - int r; - - while (n) { - bss = rb_entry(n, struct cfg80211_internal_bss, rbn); - r = cmp_bss(&res->pub, &bss->pub); - - if (r == 0) - return bss; - else if (r < 0) - n = n->rb_left; - else - n = n->rb_right; - } - - return NULL; -} - -static struct cfg80211_internal_bss * -rb_find_hidden_bss(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *res) -{ - struct rb_node *n = dev->bss_tree.rb_node; - struct cfg80211_internal_bss *bss; - int r; - - while (n) { - bss = rb_entry(n, struct cfg80211_internal_bss, rbn); - r = cmp_hidden_bss(&res->pub, &bss->pub); - - if (r == 0) - return bss; - else if (r < 0) - n = n->rb_left; - else - n = n->rb_right; - } - - return NULL; -} - -static void -copy_hidden_ies(struct cfg80211_internal_bss *res, - struct cfg80211_internal_bss *hidden) -{ - if (unlikely(res->pub.beacon_ies)) - return; - if (WARN_ON(!hidden->pub.beacon_ies)) - return; - - res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC); - if (unlikely(!res->pub.beacon_ies)) - return; - - res->beacon_ies_allocated = true; - res->pub.len_beacon_ies = hidden->pub.len_beacon_ies; - memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies, - res->pub.len_beacon_ies); -} - -static struct cfg80211_internal_bss * -cfg80211_bss_update(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *res) -{ - struct cfg80211_internal_bss *found = NULL; - - /* - * The reference to "res" is donated to this function. - */ - - if (WARN_ON(!res->pub.channel)) { - kref_put(&res->ref, bss_release); - return NULL; - } - - res->ts = jiffies; - - spin_lock_bh(&dev->bss_lock); - - found = rb_find_bss(dev, res); - - if (found) { - found->pub.beacon_interval = res->pub.beacon_interval; - found->pub.tsf = res->pub.tsf; - found->pub.signal = res->pub.signal; - found->pub.capability = res->pub.capability; - found->ts = res->ts; - - /* Update IEs */ - if (res->pub.proberesp_ies) { - size_t used = dev->wiphy.bss_priv_size + sizeof(*res); - size_t ielen = res->pub.len_proberesp_ies; - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) - if (0) { - used = 0; /* just to shut up the compiler */ -#else - if (found->pub.proberesp_ies && - !found->proberesp_ies_allocated && - ksize(found) >= used + ielen) { -#endif - memcpy(found->pub.proberesp_ies, - res->pub.proberesp_ies, ielen); - found->pub.len_proberesp_ies = ielen; - } else { - u8 *ies = found->pub.proberesp_ies; - - if (found->proberesp_ies_allocated) - ies = krealloc(ies, ielen, GFP_ATOMIC); - else - ies = kmalloc(ielen, GFP_ATOMIC); - - if (ies) { - memcpy(ies, res->pub.proberesp_ies, - ielen); - found->proberesp_ies_allocated = true; - found->pub.proberesp_ies = ies; - found->pub.len_proberesp_ies = ielen; - } - } - - /* Override possible earlier Beacon frame IEs */ - found->pub.information_elements = - found->pub.proberesp_ies; - found->pub.len_information_elements = - found->pub.len_proberesp_ies; - } - if (res->pub.beacon_ies) { - size_t used = dev->wiphy.bss_priv_size + sizeof(*res); - size_t ielen = res->pub.len_beacon_ies; - bool information_elements_is_beacon_ies = - (found->pub.information_elements == - found->pub.beacon_ies); - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) - if (0) { - used = 0; /* just to shut up the compiler */ -#else - if (found->pub.beacon_ies && - !found->beacon_ies_allocated && - ksize(found) >= used + ielen) { -#endif - memcpy(found->pub.beacon_ies, - res->pub.beacon_ies, ielen); - found->pub.len_beacon_ies = ielen; - } else { - u8 *ies = found->pub.beacon_ies; - - if (found->beacon_ies_allocated) - ies = krealloc(ies, ielen, GFP_ATOMIC); - else - ies = kmalloc(ielen, GFP_ATOMIC); - - if (ies) { - memcpy(ies, res->pub.beacon_ies, - ielen); - found->beacon_ies_allocated = true; - found->pub.beacon_ies = ies; - found->pub.len_beacon_ies = ielen; - } - } - - /* Override IEs if they were from a beacon before */ - if (information_elements_is_beacon_ies) { - found->pub.information_elements = - found->pub.beacon_ies; - found->pub.len_information_elements = - found->pub.len_beacon_ies; - } - } - - kref_put(&res->ref, bss_release); - } else { - struct cfg80211_internal_bss *hidden; - - /* First check if the beacon is a probe response from - * a hidden bss. If so, copy beacon ies (with nullified - * ssid) into the probe response bss entry (with real ssid). - * It is required basically for PSM implementation - * (probe responses do not contain tim ie) */ - - /* TODO: The code is not trying to update existing probe - * response bss entries when beacon ies are - * getting changed. */ - hidden = rb_find_hidden_bss(dev, res); - if (hidden) - copy_hidden_ies(res, hidden); - - /* this "consumes" the reference */ - list_add_tail(&res->list, &dev->bss_list); - rb_insert_bss(dev, res); - found = res; - } - - dev->bss_generation++; - spin_unlock_bh(&dev->bss_lock); - - kref_get(&found->ref); - return found; -} - -struct cfg80211_bss* -cfg80211_inform_bss(struct wiphy *wiphy, - struct ieee80211_channel *channel, - const u8 *bssid, - u64 timestamp, u16 capability, u16 beacon_interval, - const u8 *ie, size_t ielen, - s32 signal, gfp_t gfp) -{ - struct cfg80211_internal_bss *res; - size_t privsz; - - if (WARN_ON(!wiphy)) - return NULL; - - privsz = wiphy->bss_priv_size; - - if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC && - (signal < 0 || signal > 100))) - return NULL; - - res = kzalloc(sizeof(*res) + privsz + ielen, gfp); - if (!res) - return NULL; - - memcpy(res->pub.bssid, bssid, ETH_ALEN); - res->pub.channel = channel; - res->pub.signal = signal; - res->pub.tsf = timestamp; - res->pub.beacon_interval = beacon_interval; - res->pub.capability = capability; - /* - * Since we do not know here whether the IEs are from a Beacon or Probe - * Response frame, we need to pick one of the options and only use it - * with the driver that does not provide the full Beacon/Probe Response - * frame. Use Beacon frame pointer to avoid indicating that this should - * override the information_elements pointer should we have received an - * earlier indication of Probe Response data. - * - * The initial buffer for the IEs is allocated with the BSS entry and - * is located after the private area. - */ - res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz; - memcpy(res->pub.beacon_ies, ie, ielen); - res->pub.len_beacon_ies = ielen; - res->pub.information_elements = res->pub.beacon_ies; - res->pub.len_information_elements = res->pub.len_beacon_ies; - - kref_init(&res->ref); - - res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); - if (!res) - return NULL; - - if (res->pub.capability & WLAN_CAPABILITY_ESS) - regulatory_hint_found_beacon(wiphy, channel, gfp); - - /* cfg80211_bss_update gives us a referenced result */ - return &res->pub; -} -EXPORT_SYMBOL(cfg80211_inform_bss); - -struct cfg80211_bss * -cfg80211_inform_bss_frame(struct wiphy *wiphy, - struct ieee80211_channel *channel, - struct ieee80211_mgmt *mgmt, size_t len, - 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; - - if (WARN_ON(!mgmt)) - return NULL; - - if (WARN_ON(!wiphy)) - return NULL; - - if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC && - (signal < 0 || signal > 100))) - return NULL; - - if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable))) - return NULL; - - privsz = wiphy->bss_priv_size; - - res = kzalloc(sizeof(*res) + privsz + ielen, gfp); - if (!res) - return NULL; - - memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN); - res->pub.channel = channel; - res->pub.signal = signal; - res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); - res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); - res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); - /* - * The initial buffer for the IEs is allocated with the BSS entry and - * is located after the private area. - */ - if (ieee80211_is_probe_resp(mgmt->frame_control)) { - res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz; - memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable, - ielen); - res->pub.len_proberesp_ies = ielen; - res->pub.information_elements = res->pub.proberesp_ies; - res->pub.len_information_elements = res->pub.len_proberesp_ies; - } else { - res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz; - memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen); - res->pub.len_beacon_ies = ielen; - res->pub.information_elements = res->pub.beacon_ies; - res->pub.len_information_elements = res->pub.len_beacon_ies; - } - - kref_init(&res->ref); - - res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); - if (!res) - return NULL; - - if (res->pub.capability & WLAN_CAPABILITY_ESS) - regulatory_hint_found_beacon(wiphy, channel, gfp); - - /* cfg80211_bss_update gives us a referenced result */ - return &res->pub; -} -EXPORT_SYMBOL(cfg80211_inform_bss_frame); - -void cfg80211_put_bss(struct cfg80211_bss *pub) -{ - struct cfg80211_internal_bss *bss; - - if (!pub) - return; - - bss = container_of(pub, struct cfg80211_internal_bss, pub); - kref_put(&bss->ref, bss_release); -} -EXPORT_SYMBOL(cfg80211_put_bss); - -void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) -{ - struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); - struct cfg80211_internal_bss *bss; - - if (WARN_ON(!pub)) - return; - - bss = container_of(pub, struct cfg80211_internal_bss, pub); - - spin_lock_bh(&dev->bss_lock); - if (!list_empty(&bss->list)) { - __cfg80211_unlink_bss(dev, bss); - dev->bss_generation++; - } - spin_unlock_bh(&dev->bss_lock); -} -EXPORT_SYMBOL(cfg80211_unlink_bss); - -void cfg80211_unlink_allbss(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); - - printk("%s() Enter - steven", __func__); - - spin_lock_bh(&dev->bss_lock); - cfg80211_bss_expire_all(dev); - spin_unlock_bh(&dev->bss_lock); -} -EXPORT_SYMBOL(cfg80211_unlink_allbss); - -#ifdef CONFIG_CFG80211_WEXT -int cfg80211_wext_siwscan(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct cfg80211_registered_device *rdev; - struct wiphy *wiphy; - struct iw_scan_req *wreq = NULL; - struct cfg80211_scan_request *creq = NULL; - int i, err, n_channels = 0; - enum ieee80211_band band; - - if (!netif_running(dev)) - return -ENETDOWN; - - if (wrqu->data.length == sizeof(struct iw_scan_req)) - wreq = (struct iw_scan_req *)extra; - - rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); - - if (IS_ERR(rdev)) - return PTR_ERR(rdev); - - if (rdev->scan_req) { - err = -EBUSY; - goto out; - } - - wiphy = &rdev->wiphy; - - /* Determine number of channels, needed to allocate creq */ - if (wreq && wreq->num_channels) - n_channels = wreq->num_channels; - else { - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; - } - - creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + - n_channels * sizeof(void *), - GFP_ATOMIC); - if (!creq) { - err = -ENOMEM; - goto out; - } - - creq->wiphy = wiphy; - creq->dev = dev; - /* SSIDs come after channels */ - creq->ssids = (void *)&creq->channels[n_channels]; - creq->n_channels = n_channels; - creq->n_ssids = 1; - - /* translate "Scan on frequencies" request */ - i = 0; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - int j; - - if (!wiphy->bands[band]) - continue; - - for (j = 0; j < wiphy->bands[band]->n_channels; j++) { - /* ignore disabled channels */ - if (wiphy->bands[band]->channels[j].flags & - IEEE80211_CHAN_DISABLED) - continue; - - /* If we have a wireless request structure and the - * wireless request specifies frequencies, then search - * for the matching hardware channel. - */ - if (wreq && wreq->num_channels) { - int k; - int wiphy_freq = wiphy->bands[band]->channels[j].center_freq; - for (k = 0; k < wreq->num_channels; k++) { - int wext_freq = cfg80211_wext_freq(wiphy, &wreq->channel_list[k]); - if (wext_freq == wiphy_freq) - goto wext_freq_found; - } - goto wext_freq_not_found; - } - - wext_freq_found: - creq->channels[i] = &wiphy->bands[band]->channels[j]; - i++; - wext_freq_not_found: ; - } - } - /* No channels found? */ - if (!i) { - err = -EINVAL; - goto out; - } - - /* Set real number of channels specified in creq->channels[] */ - creq->n_channels = i; - - /* translate "Scan for SSID" request */ - if (wreq) { - if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { - if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) { - err = -EINVAL; - goto out; - } - memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len); - creq->ssids[0].ssid_len = wreq->essid_len; - } - if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE) - creq->n_ssids = 0; - } - - for (i = 0; i < IEEE80211_NUM_BANDS; i++) - if (wiphy->bands[i]) - creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; - - rdev->scan_req = creq; - err = rdev->ops->scan(wiphy, dev, creq); - if (err) { - rdev->scan_req = NULL; - /* creq will be freed below */ - } else { - nl80211_send_scan_start(rdev, dev); - /* creq now owned by driver */ - creq = NULL; - dev_hold(dev); - } - out: - kfree(creq); - cfg80211_unlock_rdev(rdev); - return err; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); - -static void ieee80211_scan_add_ies(struct iw_request_info *info, - struct cfg80211_bss *bss, - char **current_ev, char *end_buf) -{ - u8 *pos, *end, *next; - struct iw_event iwe; - - if (!bss->information_elements || - !bss->len_information_elements) - return; - - /* - * If needed, fragment the IEs buffer (at IE boundaries) into short - * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. - */ - pos = bss->information_elements; - end = pos + bss->len_information_elements; - - while (end - pos > IW_GENERIC_IE_MAX) { - next = pos + 2 + pos[1]; - while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX) - next = next + 2 + next[1]; - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = next - pos; - *current_ev = iwe_stream_add_point(info, *current_ev, - end_buf, &iwe, pos); - - pos = next; - } - - if (end > pos) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = end - pos; - *current_ev = iwe_stream_add_point(info, *current_ev, - end_buf, &iwe, pos); - } -} - -static inline unsigned int elapsed_jiffies_msecs(unsigned long start) -{ - unsigned long end = jiffies; - - if (end >= start) - return jiffies_to_msecs(end - start); - - return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); -} - -static char * -ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, - struct cfg80211_internal_bss *bss, char *current_ev, - char *end_buf) -{ - struct iw_event iwe; - u8 *buf, *cfg, *p; - u8 *ie = bss->pub.information_elements; - int rem = bss->pub.len_information_elements, i, sig; - bool ismesh = false; - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_ADDR_LEN); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq); - iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_FREQ_LEN); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = bss->pub.channel->center_freq; - iwe.u.freq.e = 6; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_FREQ_LEN); - - if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVQUAL; - iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED | - IW_QUAL_NOISE_INVALID | - IW_QUAL_QUAL_UPDATED; - switch (wiphy->signal_type) { - case CFG80211_SIGNAL_TYPE_MBM: - sig = bss->pub.signal / 100; - iwe.u.qual.level = sig; - iwe.u.qual.updated |= IW_QUAL_DBM; - if (sig < -110) /* rather bad */ - sig = -110; - else if (sig > -40) /* perfect */ - sig = -40; - /* will give a range of 0 .. 70 */ - iwe.u.qual.qual = sig + 110; - break; - case CFG80211_SIGNAL_TYPE_UNSPEC: - iwe.u.qual.level = bss->pub.signal; - /* will give range 0 .. 100 */ - iwe.u.qual.qual = bss->pub.signal; - break; - default: - /* not reached */ - break; - } - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_QUAL_LEN); - } - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWENCODE; - if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY) - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, ""); - - while (rem >= 2) { - /* invalid data */ - if (ie[1] > rem - 2) - break; - - switch (ie[0]) { - case WLAN_EID_SSID: - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWESSID; - iwe.u.data.length = ie[1]; - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, ie + 2); - break; - case WLAN_EID_MESH_ID: - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWESSID; - iwe.u.data.length = ie[1]; - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, ie + 2); - break; - case WLAN_EID_MESH_CONFIG: - ismesh = true; - if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) - break; - buf = kmalloc(50, GFP_ATOMIC); - if (!buf) - break; - cfg = ie + 2; - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "Mesh Network Path Selection Protocol ID: " - "0x%02X", cfg[0]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Path Selection Metric ID: 0x%02X", - cfg[1]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Congestion Control Mode ID: 0x%02X", - cfg[2]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Authentication ID: 0x%02X", cfg[4]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Formation Info: 0x%02X", cfg[5]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Capabilities: 0x%02X", cfg[6]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - kfree(buf); - break; - case WLAN_EID_SUPP_RATES: - case WLAN_EID_EXT_SUPP_RATES: - /* display all supported rates in readable format */ - p = current_ev + iwe_stream_lcp_len(info); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWRATE; - /* Those two flags are ignored... */ - iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; - - for (i = 0; i < ie[1]; i++) { - iwe.u.bitrate.value = - ((ie[i + 2] & 0x7f) * 500000); - p = iwe_stream_add_value(info, current_ev, p, - end_buf, &iwe, IW_EV_PARAM_LEN); - } - current_ev = p; - break; - } - rem -= ie[1] + 2; - ie += ie[1] + 2; - } - - if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) || - ismesh) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWMODE; - if (ismesh) - iwe.u.mode = IW_MODE_MESH; - else if (bss->pub.capability & WLAN_CAPABILITY_ESS) - iwe.u.mode = IW_MODE_MASTER; - else - iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_UINT_LEN); - } - - buf = kmalloc(30, GFP_ATOMIC); - if (buf) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf)); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, buf); - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, " Last beacon: %ums ago", - elapsed_jiffies_msecs(bss->ts)); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, &iwe, buf); - kfree(buf); - } - - ieee80211_scan_add_ies(info, &bss->pub, ¤t_ev, end_buf); - - return current_ev; -} - - -static int ieee80211_scan_results(struct cfg80211_registered_device *dev, - struct iw_request_info *info, - char *buf, size_t len) -{ - char *current_ev = buf; - char *end_buf = buf + len; - struct cfg80211_internal_bss *bss; - - spin_lock_bh(&dev->bss_lock); - cfg80211_bss_expire(dev); - - list_for_each_entry(bss, &dev->bss_list, list) { - if (buf + len - current_ev <= IW_EV_ADDR_LEN) { - spin_unlock_bh(&dev->bss_lock); - return -E2BIG; - } - current_ev = ieee80211_bss(&dev->wiphy, info, bss, - current_ev, end_buf); - } - spin_unlock_bh(&dev->bss_lock); - return current_ev - buf; -} - - -int cfg80211_wext_giwscan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct cfg80211_registered_device *rdev; - int res; - - if (!netif_running(dev)) - return -ENETDOWN; - - rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); - - if (IS_ERR(rdev)) - return PTR_ERR(rdev); - - if (rdev->scan_req) { - res = -EAGAIN; - goto out; - } - - res = ieee80211_scan_results(rdev, info, extra, data->length); - data->length = 0; - if (res >= 0) { - data->length = res; - res = 0; - } - - out: - cfg80211_unlock_rdev(rdev); - return res; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan); -#endif diff --git a/net/wireless_ath/sme.c b/net/wireless_ath/sme.c deleted file mode 100755 index 38d3248..0000000 --- a/net/wireless_ath/sme.c +++ /dev/null @@ -1,1041 +0,0 @@ -/* - * SME code for cfg80211's connect emulation. - * - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - * Copyright (C) 2009 Intel Corporation. All rights reserved. - */ - -#include <linux/etherdevice.h> -#include <linux/if_arp.h> -#include <linux/slab.h> -#include <linux/workqueue.h> -#include <linux/wireless.h> -#include <linux/export.h> -#include <net/iw_handler.h> -#include <net/cfg80211.h> -#include <net/rtnetlink.h> -#include "nl80211.h" -#include "reg.h" - -struct cfg80211_conn { - struct cfg80211_connect_params params; - /* these are sub-states of the _CONNECTING sme_state */ - enum { - CFG80211_CONN_IDLE, - CFG80211_CONN_SCANNING, - CFG80211_CONN_SCAN_AGAIN, - CFG80211_CONN_AUTHENTICATE_NEXT, - CFG80211_CONN_AUTHENTICATING, - CFG80211_CONN_ASSOCIATE_NEXT, - CFG80211_CONN_ASSOCIATING, - CFG80211_CONN_DEAUTH_ASSOC_FAIL, - } state; - u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; - u8 *ie; - size_t ie_len; - bool auto_auth, prev_bssid_valid; -}; - -static bool cfg80211_is_all_idle(void) -{ - struct cfg80211_registered_device *rdev; - struct wireless_dev *wdev; - bool is_all_idle = true; - - mutex_lock(&cfg80211_mutex); - - /* - * All devices must be idle as otherwise if you are actively - * scanning some new beacon hints could be learned and would - * count as new regulatory hints. - */ - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { - cfg80211_lock_rdev(rdev); - list_for_each_entry(wdev, &rdev->netdev_list, list) { - wdev_lock(wdev); - if (wdev->sme_state != CFG80211_SME_IDLE) - is_all_idle = false; - wdev_unlock(wdev); - } - cfg80211_unlock_rdev(rdev); - } - - mutex_unlock(&cfg80211_mutex); - - return is_all_idle; -} - -static void disconnect_work(struct work_struct *work) -{ - if (!cfg80211_is_all_idle()) - return; - - regulatory_hint_disconnect(); -} - -static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); - -static int cfg80211_conn_scan(struct wireless_dev *wdev) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_scan_request *request; - int n_channels, err; - - ASSERT_RTNL(); - ASSERT_RDEV_LOCK(rdev); - ASSERT_WDEV_LOCK(wdev); - - if (rdev->scan_req) - return -EBUSY; - - if (wdev->conn->params.channel) { - n_channels = 1; - } else { - enum ieee80211_band band; - n_channels = 0; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!wdev->wiphy->bands[band]) - continue; - n_channels += wdev->wiphy->bands[band]->n_channels; - } - } - request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + - sizeof(request->channels[0]) * n_channels, - GFP_KERNEL); - if (!request) - return -ENOMEM; - - if (wdev->conn->params.channel) - request->channels[0] = wdev->conn->params.channel; - else { - int i = 0, j; - enum ieee80211_band band; - struct ieee80211_supported_band *bands; - struct ieee80211_channel *channel; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - bands = wdev->wiphy->bands[band]; - if (!bands) - continue; - for (j = 0; j < bands->n_channels; j++) { - channel = &bands->channels[j]; - if (channel->flags & IEEE80211_CHAN_DISABLED) - continue; - request->channels[i++] = channel; - } - request->rates[band] = (1 << bands->n_bitrates) - 1; - } - n_channels = i; - } - request->n_channels = n_channels; - request->ssids = (void *)&request->channels[n_channels]; - request->n_ssids = 1; - - memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, - wdev->conn->params.ssid_len); - request->ssids[0].ssid_len = wdev->conn->params.ssid_len; - - request->dev = wdev->netdev; - request->wiphy = &rdev->wiphy; - - rdev->scan_req = request; - - err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request); - if (!err) { - wdev->conn->state = CFG80211_CONN_SCANNING; - nl80211_send_scan_start(rdev, wdev->netdev); - dev_hold(wdev->netdev); - } else { - rdev->scan_req = NULL; - kfree(request); - } - return err; -} - -static int cfg80211_conn_do_work(struct wireless_dev *wdev) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_connect_params *params; - const u8 *prev_bssid = NULL; - int err; - - ASSERT_WDEV_LOCK(wdev); - - if (!wdev->conn) - return 0; - - params = &wdev->conn->params; - - switch (wdev->conn->state) { - case CFG80211_CONN_SCAN_AGAIN: - return cfg80211_conn_scan(wdev); - case CFG80211_CONN_AUTHENTICATE_NEXT: - BUG_ON(!rdev->ops->auth); - wdev->conn->state = CFG80211_CONN_AUTHENTICATING; - return __cfg80211_mlme_auth(rdev, wdev->netdev, - params->channel, params->auth_type, - params->bssid, - params->ssid, params->ssid_len, - NULL, 0, - params->key, params->key_len, - params->key_idx, false); - case CFG80211_CONN_ASSOCIATE_NEXT: - BUG_ON(!rdev->ops->assoc); - wdev->conn->state = CFG80211_CONN_ASSOCIATING; - if (wdev->conn->prev_bssid_valid) - prev_bssid = wdev->conn->prev_bssid; - err = __cfg80211_mlme_assoc(rdev, wdev->netdev, - params->channel, params->bssid, - prev_bssid, - params->ssid, params->ssid_len, - params->ie, params->ie_len, - false, ¶ms->crypto); - if (err) - __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, - NULL, 0, - WLAN_REASON_DEAUTH_LEAVING, - false); - return err; - case CFG80211_CONN_DEAUTH_ASSOC_FAIL: - __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, - NULL, 0, - WLAN_REASON_DEAUTH_LEAVING, false); - /* return an error so that we call __cfg80211_connect_result() */ - return -EINVAL; - default: - return 0; - } -} - -void cfg80211_conn_work(struct work_struct *work) -{ - struct cfg80211_registered_device *rdev = - container_of(work, struct cfg80211_registered_device, conn_work); - struct wireless_dev *wdev; - u8 bssid_buf[ETH_ALEN], *bssid = NULL; - - rtnl_lock(); - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - - list_for_each_entry(wdev, &rdev->netdev_list, list) { - wdev_lock(wdev); - if (!netif_running(wdev->netdev)) { - wdev_unlock(wdev); - continue; - } - if (wdev->sme_state != CFG80211_SME_CONNECTING) { - wdev_unlock(wdev); - continue; - } - if (wdev->conn->params.bssid) { - memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN); - bssid = bssid_buf; - } - if (cfg80211_conn_do_work(wdev)) - __cfg80211_connect_result( - wdev->netdev, bssid, - NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - false, NULL); - wdev_unlock(wdev); - } - - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); - rtnl_unlock(); -} - -static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_bss *bss; - u16 capa = WLAN_CAPABILITY_ESS; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->conn->params.privacy) - capa |= WLAN_CAPABILITY_PRIVACY; - - bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, - wdev->conn->params.bssid, - wdev->conn->params.ssid, - wdev->conn->params.ssid_len, - WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, - capa); - if (!bss) - return NULL; - - memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); - wdev->conn->params.bssid = wdev->conn->bssid; - wdev->conn->params.channel = bss->channel; - wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; - schedule_work(&rdev->conn_work); - - return bss; -} - -static void __cfg80211_sme_scan_done(struct net_device *dev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_bss *bss; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->sme_state != CFG80211_SME_CONNECTING) - return; - - if (!wdev->conn) - return; - - if (wdev->conn->state != CFG80211_CONN_SCANNING && - wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) - return; - - bss = cfg80211_get_conn_bss(wdev); - if (bss) { - cfg80211_put_bss(bss); - } else { - /* not found */ - if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) - schedule_work(&rdev->conn_work); - else - __cfg80211_connect_result( - wdev->netdev, - wdev->conn->params.bssid, - NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - false, NULL); - } -} - -void cfg80211_sme_scan_done(struct net_device *dev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); - wdev_lock(wdev); - __cfg80211_sme_scan_done(dev); - wdev_unlock(wdev); - mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); -} - -void cfg80211_sme_rx_auth(struct net_device *dev, - const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); - - ASSERT_WDEV_LOCK(wdev); - - /* should only RX auth frames when connecting */ - if (wdev->sme_state != CFG80211_SME_CONNECTING) - return; - - if (WARN_ON(!wdev->conn)) - return; - - if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && - wdev->conn->auto_auth && - wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { - /* select automatically between only open, shared, leap */ - switch (wdev->conn->params.auth_type) { - case NL80211_AUTHTYPE_OPEN_SYSTEM: - if (wdev->connect_keys) - wdev->conn->params.auth_type = - NL80211_AUTHTYPE_SHARED_KEY; - else - wdev->conn->params.auth_type = - NL80211_AUTHTYPE_NETWORK_EAP; - break; - case NL80211_AUTHTYPE_SHARED_KEY: - wdev->conn->params.auth_type = - NL80211_AUTHTYPE_NETWORK_EAP; - break; - default: - /* huh? */ - wdev->conn->params.auth_type = - NL80211_AUTHTYPE_OPEN_SYSTEM; - break; - } - wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; - schedule_work(&rdev->conn_work); - } else if (status_code != WLAN_STATUS_SUCCESS) { - __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, - status_code, false, NULL); - } else if (wdev->sme_state == CFG80211_SME_CONNECTING && - wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { - wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; - schedule_work(&rdev->conn_work); - } -} - -bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev) -{ - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - if (WARN_ON(!wdev->conn)) - return false; - - if (!wdev->conn->prev_bssid_valid) - return false; - - /* - * Some stupid APs don't accept reassoc, so we - * need to fall back to trying regular assoc. - */ - wdev->conn->prev_bssid_valid = false; - wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; - schedule_work(&rdev->conn_work); - - return true; -} - -void cfg80211_sme_failed_assoc(struct wireless_dev *wdev) -{ - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL; - schedule_work(&rdev->conn_work); -} - -void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, bool wextev, - struct cfg80211_bss *bss) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - u8 *country_ie; -#ifdef CONFIG_CFG80211_WEXT - union iwreq_data wrqu; -#endif - - ASSERT_WDEV_LOCK(wdev); - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) - return; - - if (wdev->sme_state != CFG80211_SME_CONNECTING) - return; - - nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, - bssid, req_ie, req_ie_len, - resp_ie, resp_ie_len, - status, GFP_KERNEL); - -#ifdef CONFIG_CFG80211_WEXT - if (wextev) { - if (req_ie && status == WLAN_STATUS_SUCCESS) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = req_ie_len; - wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie); - } - - if (resp_ie && status == WLAN_STATUS_SUCCESS) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = resp_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); - } - - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid && status == WLAN_STATUS_SUCCESS) { - memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); - memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); - wdev->wext.prev_bssid_valid = true; - } - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); - } -#endif - - if (wdev->current_bss) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - wdev->current_bss = NULL; - } - - if (wdev->conn) - wdev->conn->state = CFG80211_CONN_IDLE; - - if (status != WLAN_STATUS_SUCCESS) { - wdev->sme_state = CFG80211_SME_IDLE; - if (wdev->conn) - kfree(wdev->conn->ie); - kfree(wdev->conn); - wdev->conn = NULL; - kfree(wdev->connect_keys); - wdev->connect_keys = NULL; - wdev->ssid_len = 0; - return; - } - - if (!bss) - bss = cfg80211_get_bss(wdev->wiphy, - wdev->conn ? wdev->conn->params.channel : - NULL, - bssid, - wdev->ssid, wdev->ssid_len, - WLAN_CAPABILITY_ESS, - WLAN_CAPABILITY_ESS); - - if (WARN_ON(!bss)) - return; - - cfg80211_hold_bss(bss_from_pub(bss)); - wdev->current_bss = bss_from_pub(bss); - - wdev->sme_state = CFG80211_SME_CONNECTED; - cfg80211_upload_connect_keys(wdev); - - country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); - - if (!country_ie) - return; - - /* - * ieee80211_bss_get_ie() ensures we can access: - * - country_ie + 2, the start of the country ie data, and - * - and country_ie[1] which is the IE length - */ - regulatory_hint_11d(wdev->wiphy, - bss->channel->band, - country_ie + 2, - country_ie[1]); -} - -void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_event *ev; - unsigned long flags; - - CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING); - - ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); - if (!ev) - return; - - ev->type = EVENT_CONNECT_RESULT; - if (bssid) - memcpy(ev->cr.bssid, bssid, ETH_ALEN); - if (req_ie_len) { - ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); - ev->cr.req_ie_len = req_ie_len; - memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); - } - if (resp_ie_len) { - ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; - ev->cr.resp_ie_len = resp_ie_len; - memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); - } - ev->cr.status = status; - - spin_lock_irqsave(&wdev->event_lock, flags); - list_add_tail(&ev->list, &wdev->event_list); - spin_unlock_irqrestore(&wdev->event_lock, flags); - queue_work(cfg80211_wq, &rdev->event_work); -} -EXPORT_SYMBOL(cfg80211_connect_result); - -void __cfg80211_roamed(struct wireless_dev *wdev, - struct cfg80211_bss *bss, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len) -{ -#ifdef CONFIG_CFG80211_WEXT - union iwreq_data wrqu; -#endif - ASSERT_WDEV_LOCK(wdev); - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) - goto out; - - if (wdev->sme_state != CFG80211_SME_CONNECTED) - goto out; - - /* internal error -- how did we get to CONNECTED w/o BSS? */ - if (WARN_ON(!wdev->current_bss)) { - goto out; - } - - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - wdev->current_bss = NULL; - - cfg80211_hold_bss(bss_from_pub(bss)); - wdev->current_bss = bss_from_pub(bss); - - nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bss->bssid, - req_ie, req_ie_len, resp_ie, resp_ie_len, - GFP_KERNEL); - -#ifdef CONFIG_CFG80211_WEXT - if (req_ie) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = req_ie_len; - wireless_send_event(wdev->netdev, IWEVASSOCREQIE, - &wrqu, req_ie); - } - - if (resp_ie) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = resp_ie_len; - wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, - &wrqu, resp_ie); - } - - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN); - memcpy(wdev->wext.prev_bssid, bss->bssid, ETH_ALEN); - wdev->wext.prev_bssid_valid = true; - wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); -#endif - - return; -out: - cfg80211_put_bss(bss); -} - -void cfg80211_roamed(struct net_device *dev, - struct ieee80211_channel *channel, - const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_bss *bss; - - CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); - - bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, - wdev->ssid_len, WLAN_CAPABILITY_ESS, - WLAN_CAPABILITY_ESS); - if (WARN_ON(!bss)) - return; - - cfg80211_roamed_bss(dev, bss, req_ie, req_ie_len, resp_ie, - resp_ie_len, gfp); -} -EXPORT_SYMBOL(cfg80211_roamed); - -void cfg80211_roamed_bss(struct net_device *dev, - struct cfg80211_bss *bss, const u8 *req_ie, - size_t req_ie_len, const u8 *resp_ie, - size_t resp_ie_len, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_event *ev; - unsigned long flags; - - CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); - - if (WARN_ON(!bss)) - return; - - ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); - if (!ev) { - cfg80211_put_bss(bss); - return; - } - - ev->type = EVENT_ROAMED; - ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); - ev->rm.req_ie_len = req_ie_len; - memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); - ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; - ev->rm.resp_ie_len = resp_ie_len; - memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); - ev->rm.bss = bss; - - spin_lock_irqsave(&wdev->event_lock, flags); - list_add_tail(&ev->list, &wdev->event_list); - spin_unlock_irqrestore(&wdev->event_lock, flags); - queue_work(cfg80211_wq, &rdev->event_work); -} -EXPORT_SYMBOL(cfg80211_roamed_bss); - -void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, - size_t ie_len, u16 reason, bool from_ap) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int i; -#ifdef CONFIG_CFG80211_WEXT - union iwreq_data wrqu; -#endif - - ASSERT_WDEV_LOCK(wdev); - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) - return; - - if (wdev->sme_state != CFG80211_SME_CONNECTED) - return; - - if (wdev->current_bss) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - } - - wdev->current_bss = NULL; - wdev->sme_state = CFG80211_SME_IDLE; - wdev->ssid_len = 0; - - if (wdev->conn) { - const u8 *bssid; - int ret; - - kfree(wdev->conn->ie); - wdev->conn->ie = NULL; - kfree(wdev->conn); - wdev->conn = NULL; - - /* - * If this disconnect was due to a disassoc, we - * we might still have an auth BSS around. For - * the userspace SME that's currently expected, - * but for the kernel SME (nl80211 CONNECT or - * wireless extensions) we want to clear up all - * state. - */ - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (!wdev->auth_bsses[i]) - continue; - bssid = wdev->auth_bsses[i]->pub.bssid; - ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, - WLAN_REASON_DEAUTH_LEAVING, - false); - WARN(ret, "deauth failed: %d\n", ret); - } - } - - nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); - - /* - * Delete all the keys ... pairwise keys can't really - * exist any more anyway, but default keys might. - */ - if (rdev->ops->del_key) - for (i = 0; i < 6; i++) - rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL); - -#ifdef CONFIG_CFG80211_WEXT - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); - wdev->wext.connect.ssid_len = 0; -#endif - - schedule_work(&cfg80211_disconnect_work); -} - -void cfg80211_disconnected(struct net_device *dev, u16 reason, - u8 *ie, size_t ie_len, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_event *ev; - unsigned long flags; - - CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); - - ev = kzalloc(sizeof(*ev) + ie_len, gfp); - if (!ev) - return; - - ev->type = EVENT_DISCONNECTED; - ev->dc.ie = ((u8 *)ev) + sizeof(*ev); - ev->dc.ie_len = ie_len; - memcpy((void *)ev->dc.ie, ie, ie_len); - ev->dc.reason = reason; - - spin_lock_irqsave(&wdev->event_lock, flags); - list_add_tail(&ev->list, &wdev->event_list); - spin_unlock_irqrestore(&wdev->event_lock, flags); - queue_work(cfg80211_wq, &rdev->event_work); -} -EXPORT_SYMBOL(cfg80211_disconnected); - -int __cfg80211_connect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys, - const u8 *prev_bssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_bss *bss = NULL; - int err; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->sme_state != CFG80211_SME_IDLE) - return -EALREADY; - - if (WARN_ON(wdev->connect_keys)) { - kfree(wdev->connect_keys); - wdev->connect_keys = NULL; - } - - if (connkeys && connkeys->def >= 0) { - int idx; - u32 cipher; - - idx = connkeys->def; - cipher = connkeys->params[idx].cipher; - /* If given a WEP key we may need it for shared key auth */ - if (cipher == WLAN_CIPHER_SUITE_WEP40 || - cipher == WLAN_CIPHER_SUITE_WEP104) { - connect->key_idx = idx; - connect->key = connkeys->params[idx].key; - connect->key_len = connkeys->params[idx].key_len; - - /* - * If ciphers are not set (e.g. when going through - * iwconfig), we have to set them appropriately here. - */ - if (connect->crypto.cipher_group == 0) - connect->crypto.cipher_group = cipher; - - if (connect->crypto.n_ciphers_pairwise == 0) { - connect->crypto.n_ciphers_pairwise = 1; - connect->crypto.ciphers_pairwise[0] = cipher; - } - } - } - - if (!rdev->ops->connect) { - if (!rdev->ops->auth || !rdev->ops->assoc) - return -EOPNOTSUPP; - - if (WARN_ON(wdev->conn)) - return -EINPROGRESS; - - wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); - if (!wdev->conn) - return -ENOMEM; - - /* - * Copy all parameters, and treat explicitly IEs, BSSID, SSID. - */ - memcpy(&wdev->conn->params, connect, sizeof(*connect)); - if (connect->bssid) { - wdev->conn->params.bssid = wdev->conn->bssid; - memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); - } - - if (connect->ie) { - wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, - GFP_KERNEL); - wdev->conn->params.ie = wdev->conn->ie; - if (!wdev->conn->ie) { - kfree(wdev->conn); - wdev->conn = NULL; - return -ENOMEM; - } - } - - if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { - wdev->conn->auto_auth = true; - /* start with open system ... should mostly work */ - wdev->conn->params.auth_type = - NL80211_AUTHTYPE_OPEN_SYSTEM; - } else { - wdev->conn->auto_auth = false; - } - - memcpy(wdev->ssid, connect->ssid, connect->ssid_len); - wdev->ssid_len = connect->ssid_len; - wdev->conn->params.ssid = wdev->ssid; - wdev->conn->params.ssid_len = connect->ssid_len; - - /* see if we have the bss already */ - bss = cfg80211_get_conn_bss(wdev); - - wdev->sme_state = CFG80211_SME_CONNECTING; - wdev->connect_keys = connkeys; - - if (prev_bssid) { - memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); - wdev->conn->prev_bssid_valid = true; - } - - /* we're good if we have a matching bss struct */ - if (bss) { - wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; - err = cfg80211_conn_do_work(wdev); - cfg80211_put_bss(bss); - } else { - /* otherwise we'll need to scan for the AP first */ - err = cfg80211_conn_scan(wdev); - /* - * If we can't scan right now, then we need to scan again - * after the current scan finished, since the parameters - * changed (unless we find a good AP anyway). - */ - if (err == -EBUSY) { - err = 0; - wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; - } - } - if (err) { - kfree(wdev->conn->ie); - kfree(wdev->conn); - wdev->conn = NULL; - wdev->sme_state = CFG80211_SME_IDLE; - wdev->connect_keys = NULL; - wdev->ssid_len = 0; - } - - return err; - } else { - wdev->sme_state = CFG80211_SME_CONNECTING; - wdev->connect_keys = connkeys; - err = rdev->ops->connect(&rdev->wiphy, dev, connect); - if (err) { - wdev->connect_keys = NULL; - wdev->sme_state = CFG80211_SME_IDLE; - return err; - } - - memcpy(wdev->ssid, connect->ssid, connect->ssid_len); - wdev->ssid_len = connect->ssid_len; - - return 0; - } -} - -int cfg80211_connect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys) -{ - int err; - - mutex_lock(&rdev->devlist_mtx); - wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL); - wdev_unlock(dev->ieee80211_ptr); - mutex_unlock(&rdev->devlist_mtx); - - return err; -} - -int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason, bool wextev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->sme_state == CFG80211_SME_IDLE) - return -EINVAL; - - kfree(wdev->connect_keys); - wdev->connect_keys = NULL; - - if (!rdev->ops->disconnect) { - if (!rdev->ops->deauth) - return -EOPNOTSUPP; - - /* was it connected by userspace SME? */ - if (!wdev->conn) { - cfg80211_mlme_down(rdev, dev); - return 0; - } - - if (wdev->sme_state == CFG80211_SME_CONNECTING && - (wdev->conn->state == CFG80211_CONN_SCANNING || - wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { - wdev->sme_state = CFG80211_SME_IDLE; - kfree(wdev->conn->ie); - kfree(wdev->conn); - wdev->conn = NULL; - wdev->ssid_len = 0; - return 0; - } - - /* wdev->conn->params.bssid must be set if > SCANNING */ - err = __cfg80211_mlme_deauth(rdev, dev, - wdev->conn->params.bssid, - NULL, 0, reason, false); - if (err) - return err; - } else { - err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); - if (err) - return err; - } - - if (wdev->sme_state == CFG80211_SME_CONNECTED) - __cfg80211_disconnected(dev, NULL, 0, 0, false); - else if (wdev->sme_state == CFG80211_SME_CONNECTING) - __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - wextev, NULL); - - return 0; -} - -int cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - u16 reason, bool wextev) -{ - int err; - - wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_disconnect(rdev, dev, reason, wextev); - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - -void cfg80211_sme_disassoc(struct net_device *dev, int idx) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u8 bssid[ETH_ALEN]; - - ASSERT_WDEV_LOCK(wdev); - - if (!wdev->conn) - return; - - if (wdev->conn->state == CFG80211_CONN_IDLE) - return; - - /* - * Ok, so the association was made by this SME -- we don't - * want it any more so deauthenticate too. - */ - - if (!wdev->auth_bsses[idx]) - return; - - memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); - if (__cfg80211_mlme_deauth(rdev, dev, bssid, - NULL, 0, WLAN_REASON_DEAUTH_LEAVING, - false)) { - /* whatever -- assume gone anyway */ - cfg80211_unhold_bss(wdev->auth_bsses[idx]); - cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); - wdev->auth_bsses[idx] = NULL; - } -} diff --git a/net/wireless_ath/sysfs.c b/net/wireless_ath/sysfs.c deleted file mode 100755 index 2f8af6e..0000000 --- a/net/wireless_ath/sysfs.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * This file provides /sys/class/ieee80211/<wiphy name>/ - * and some default attributes. - * - * Copyright 2005-2006 Jiri Benc <jbenc@suse.cz> - * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> - * - * This file is GPLv2 as found in COPYING. - */ - -#include <linux/device.h> -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/nl80211.h> -#include <linux/rtnetlink.h> -#include <net/cfg80211.h> -#include "sysfs.h" -#include "core.h" - -static inline struct cfg80211_registered_device *dev_to_rdev( - struct device *dev) -{ - return container_of(dev, struct cfg80211_registered_device, wiphy.dev); -} - -#define SHOW_FMT(name, fmt, member) \ -static ssize_t name ## _show(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \ -} - -SHOW_FMT(index, "%d", wiphy_idx); -SHOW_FMT(macaddress, "%pM", wiphy.perm_addr); -SHOW_FMT(address_mask, "%pM", wiphy.addr_mask); - -static ssize_t name_show(struct device *dev, - struct device_attribute *attr, - char *buf) { - struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy; - return sprintf(buf, "%s\n", dev_name(&wiphy->dev)); -} - - -static ssize_t addresses_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy; - char *start = buf; - int i; - - if (!wiphy->addresses) - return sprintf(buf, "%pM\n", wiphy->perm_addr); - - for (i = 0; i < wiphy->n_addresses; i++) - buf += sprintf(buf, "%pM\n", &wiphy->addresses[i].addr); - - return buf - start; -} - -static struct device_attribute ieee80211_dev_attrs[] = { - __ATTR_RO(index), - __ATTR_RO(macaddress), - __ATTR_RO(address_mask), - __ATTR_RO(addresses), - __ATTR_RO(name), - {} -}; - -static void wiphy_dev_release(struct device *dev) -{ - struct cfg80211_registered_device *rdev = dev_to_rdev(dev); - - cfg80211_dev_free(rdev); -} - -#ifdef CONFIG_HOTPLUG -static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - /* TODO, we probably need stuff here */ - return 0; -} -#endif - -static int wiphy_suspend(struct device *dev, pm_message_t state) -{ - struct cfg80211_registered_device *rdev = dev_to_rdev(dev); - int ret = 0; - - rdev->suspend_at = get_seconds(); - - if (rdev->ops->suspend) { - rtnl_lock(); - if (rdev->wiphy.registered) - ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan); - rtnl_unlock(); - } - - return ret; -} - -static int wiphy_resume(struct device *dev) -{ - struct cfg80211_registered_device *rdev = dev_to_rdev(dev); - int ret = 0; - - /* Age scan results with time spent in suspend */ - spin_lock_bh(&rdev->bss_lock); - cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at); - spin_unlock_bh(&rdev->bss_lock); - - if (rdev->ops->resume) { - rtnl_lock(); - if (rdev->wiphy.registered) - ret = rdev->ops->resume(&rdev->wiphy); - rtnl_unlock(); - } - - return ret; -} - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) -static const void *wiphy_namespace(struct device *d) -{ - struct wiphy *wiphy = container_of(d, struct wiphy, dev); - - return wiphy_net(wiphy); -} -#endif - -struct class ieee80211_class = { - .name = "ieee80211", - .owner = THIS_MODULE, - .dev_release = wiphy_dev_release, - .dev_attrs = ieee80211_dev_attrs, -#ifdef CONFIG_HOTPLUG - .dev_uevent = wiphy_uevent, -#endif - .suspend = wiphy_suspend, - .resume = wiphy_resume, -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) - .ns_type = &net_ns_type_operations, - .namespace = wiphy_namespace, -#endif -}; - -int wiphy_sysfs_init(void) -{ - return class_register(&ieee80211_class); -} - -void wiphy_sysfs_exit(void) -{ - class_unregister(&ieee80211_class); -} diff --git a/net/wireless_ath/sysfs.h b/net/wireless_ath/sysfs.h deleted file mode 100755 index 65acbeb..0000000 --- a/net/wireless_ath/sysfs.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __WIRELESS_SYSFS_H -#define __WIRELESS_SYSFS_H - -extern int wiphy_sysfs_init(void); -extern void wiphy_sysfs_exit(void); - -extern struct class ieee80211_class; - -#endif /* __WIRELESS_SYSFS_H */ diff --git a/net/wireless_ath/util.c b/net/wireless_ath/util.c deleted file mode 100755 index a7c53d9..0000000 --- a/net/wireless_ath/util.c +++ /dev/null @@ -1,1219 +0,0 @@ -/* - * Wireless utility functions - * - * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net> - */ -#include <linux/export.h> -#include <linux/bitops.h> -#include <linux/etherdevice.h> -#include <linux/slab.h> -#include <linux/crc32.h> -#include <net/cfg80211.h> -#include <net/ip.h> -#include "core.h" - -struct ieee80211_rate * -ieee80211_get_response_rate(struct ieee80211_supported_band *sband, - u32 basic_rates, int bitrate) -{ - struct ieee80211_rate *result = &sband->bitrates[0]; - int i; - - for (i = 0; i < sband->n_bitrates; i++) { - if (!(basic_rates & BIT(i))) - continue; - if (sband->bitrates[i].bitrate > bitrate) - continue; - result = &sband->bitrates[i]; - } - - return result; -} -EXPORT_SYMBOL(ieee80211_get_response_rate); - -int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band) -{ - /* see 802.11 17.3.8.3.2 and Annex J - * there are overlapping channel numbers in 5GHz and 2GHz bands */ - if (band == IEEE80211_BAND_5GHZ) { - if (chan >= 182 && chan <= 196) - return 4000 + chan * 5; - else - return 5000 + chan * 5; - } else { /* IEEE80211_BAND_2GHZ */ - if (chan == 14) - return 2484; - else if (chan < 14) - return 2407 + chan * 5; - else - return 0; /* not supported */ - } -} -EXPORT_SYMBOL(ieee80211_channel_to_frequency); - -int ieee80211_frequency_to_channel(int freq) -{ - /* see 802.11 17.3.8.3.2 and Annex J */ - if (freq == 2484) - return 14; - else if (freq < 2484) - return (freq - 2407) / 5; - else if (freq >= 4910 && freq <= 4980) - return (freq - 4000) / 5; - else - return (freq - 5000) / 5; -} -EXPORT_SYMBOL(ieee80211_frequency_to_channel); - -struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy, - int freq) -{ - enum ieee80211_band band; - struct ieee80211_supported_band *sband; - int i; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - sband = wiphy->bands[band]; - - if (!sband) - continue; - - for (i = 0; i < sband->n_channels; i++) { - if (sband->channels[i].center_freq == freq) - return &sband->channels[i]; - } - } - - return NULL; -} -EXPORT_SYMBOL(__ieee80211_get_channel); - -static void set_mandatory_flags_band(struct ieee80211_supported_band *sband, - enum ieee80211_band band) -{ - int i, want; - - switch (band) { - case IEEE80211_BAND_5GHZ: - want = 3; - for (i = 0; i < sband->n_bitrates; i++) { - if (sband->bitrates[i].bitrate == 60 || - sband->bitrates[i].bitrate == 120 || - sband->bitrates[i].bitrate == 240) { - sband->bitrates[i].flags |= - IEEE80211_RATE_MANDATORY_A; - want--; - } - } - WARN_ON(want); - break; - case IEEE80211_BAND_2GHZ: - want = 7; - for (i = 0; i < sband->n_bitrates; i++) { - if (sband->bitrates[i].bitrate == 10) { - sband->bitrates[i].flags |= - IEEE80211_RATE_MANDATORY_B | - IEEE80211_RATE_MANDATORY_G; - want--; - } - - if (sband->bitrates[i].bitrate == 20 || - sband->bitrates[i].bitrate == 55 || - sband->bitrates[i].bitrate == 110 || - sband->bitrates[i].bitrate == 60 || - sband->bitrates[i].bitrate == 120 || - sband->bitrates[i].bitrate == 240) { - sband->bitrates[i].flags |= - IEEE80211_RATE_MANDATORY_G; - want--; - } - - if (sband->bitrates[i].bitrate != 10 && - sband->bitrates[i].bitrate != 20 && - sband->bitrates[i].bitrate != 55 && - sband->bitrates[i].bitrate != 110) - sband->bitrates[i].flags |= - IEEE80211_RATE_ERP_G; - } - WARN_ON(want != 0 && want != 3 && want != 6); - break; - case IEEE80211_NUM_BANDS: - WARN_ON(1); - break; - } -} - -void ieee80211_set_bitrate_flags(struct wiphy *wiphy) -{ - enum ieee80211_band band; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - set_mandatory_flags_band(wiphy->bands[band], band); -} - -bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher) -{ - int i; - for (i = 0; i < wiphy->n_cipher_suites; i++) - if (cipher == wiphy->cipher_suites[i]) - return true; - return false; -} - -int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, - struct key_params *params, int key_idx, - bool pairwise, const u8 *mac_addr) -{ - if (key_idx > 5) - return -EINVAL; - - if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) - return -EINVAL; - - if (pairwise && !mac_addr) - return -EINVAL; - - /* - * Disallow pairwise keys with non-zero index unless it's WEP - * or a vendor specific cipher (because current deployments use - * pairwise WEP keys with non-zero indices and for vendor specific - * ciphers this should be validated in the driver or hardware level - * - but 802.11i clearly specifies to use zero) - */ - if (pairwise && key_idx && - ((params->cipher == WLAN_CIPHER_SUITE_TKIP) || - (params->cipher == WLAN_CIPHER_SUITE_CCMP) || - (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC))) - return -EINVAL; - - switch (params->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - if (params->key_len != WLAN_KEY_LEN_WEP40) - return -EINVAL; - break; - case WLAN_CIPHER_SUITE_TKIP: - if (params->key_len != WLAN_KEY_LEN_TKIP) - return -EINVAL; - break; - case WLAN_CIPHER_SUITE_CCMP: - if (params->key_len != WLAN_KEY_LEN_CCMP) - return -EINVAL; - break; - case WLAN_CIPHER_SUITE_WEP104: - if (params->key_len != WLAN_KEY_LEN_WEP104) - return -EINVAL; - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - if (params->key_len != WLAN_KEY_LEN_AES_CMAC) - return -EINVAL; - break; - default: - /* - * We don't know anything about this algorithm, - * allow using it -- but the driver must check - * all parameters! We still check below whether - * or not the driver supports this algorithm, - * of course. - */ - break; - } - - if (params->seq) { - switch (params->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - /* These ciphers do not use key sequence */ - return -EINVAL; - case WLAN_CIPHER_SUITE_TKIP: - case WLAN_CIPHER_SUITE_CCMP: - case WLAN_CIPHER_SUITE_AES_CMAC: - if (params->seq_len != 6) - return -EINVAL; - break; - } - } - - if (!cfg80211_supported_cipher_suite(&rdev->wiphy, params->cipher)) - return -EINVAL; - - return 0; -} - -/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ -/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ -const unsigned char rfc1042_header[] __aligned(2) = - { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; -EXPORT_SYMBOL(rfc1042_header); - -/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ -const unsigned char bridge_tunnel_header[] __aligned(2) = - { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; -EXPORT_SYMBOL(bridge_tunnel_header); - -unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc) -{ - unsigned int hdrlen = 24; - - if (ieee80211_is_data(fc)) { - if (ieee80211_has_a4(fc)) - hdrlen = 30; - if (ieee80211_is_data_qos(fc)) { - hdrlen += IEEE80211_QOS_CTL_LEN; - if (ieee80211_has_order(fc)) - hdrlen += IEEE80211_HT_CTL_LEN; - } - goto out; - } - - if (ieee80211_is_ctl(fc)) { - /* - * ACK and CTS are 10 bytes, all others 16. To see how - * to get this condition consider - * subtype mask: 0b0000000011110000 (0x00F0) - * ACK subtype: 0b0000000011010000 (0x00D0) - * CTS subtype: 0b0000000011000000 (0x00C0) - * bits that matter: ^^^ (0x00E0) - * value of those: 0b0000000011000000 (0x00C0) - */ - if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0)) - hdrlen = 10; - else - hdrlen = 16; - } -out: - return hdrlen; -} -EXPORT_SYMBOL(ieee80211_hdrlen); - -unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) -{ - const struct ieee80211_hdr *hdr = - (const struct ieee80211_hdr *)skb->data; - unsigned int hdrlen; - - if (unlikely(skb->len < 10)) - return 0; - hdrlen = ieee80211_hdrlen(hdr->frame_control); - if (unlikely(hdrlen > skb->len)) - return 0; - return hdrlen; -} -EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); - -static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) -{ - int ae = meshhdr->flags & MESH_FLAGS_AE; - /* 7.1.3.5a.2 */ - switch (ae) { - 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; - } -} - -int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, - enum nl80211_iftype iftype) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - u16 hdrlen, ethertype; - u8 *payload; - u8 dst[ETH_ALEN]; - u8 src[ETH_ALEN] __aligned(2); - - if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) - return -1; - - hdrlen = ieee80211_hdrlen(hdr->frame_control); - - /* convert IEEE 802.11 header + possible LLC headers into Ethernet - * header - * IEEE 802.11 address fields: - * ToDS FromDS Addr1 Addr2 Addr3 Addr4 - * 0 0 DA SA BSSID n/a - * 0 1 DA BSSID SA n/a - * 1 0 BSSID SA DA n/a - * 1 1 RA TA DA SA - */ - memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN); - memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN); - - switch (hdr->frame_control & - cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { - case cpu_to_le16(IEEE80211_FCTL_TODS): - if (unlikely(iftype != NL80211_IFTYPE_AP && - iftype != NL80211_IFTYPE_AP_VLAN && - iftype != NL80211_IFTYPE_P2P_GO)) - return -1; - break; - case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS): - if (unlikely(iftype != NL80211_IFTYPE_WDS && - iftype != NL80211_IFTYPE_MESH_POINT && - iftype != NL80211_IFTYPE_AP_VLAN && - iftype != NL80211_IFTYPE_STATION)) - return -1; - if (iftype == NL80211_IFTYPE_MESH_POINT) { - struct ieee80211s_hdr *meshdr = - (struct ieee80211s_hdr *) (skb->data + hdrlen); - /* 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) { - skb_copy_bits(skb, hdrlen + - offsetof(struct ieee80211s_hdr, eaddr1), - dst, ETH_ALEN); - skb_copy_bits(skb, hdrlen + - offsetof(struct ieee80211s_hdr, eaddr2), - src, ETH_ALEN); - } - hdrlen += ieee80211_get_mesh_hdrlen(meshdr); - } - break; - case cpu_to_le16(IEEE80211_FCTL_FROMDS): - if ((iftype != NL80211_IFTYPE_STATION && - iftype != NL80211_IFTYPE_P2P_CLIENT && - iftype != NL80211_IFTYPE_MESH_POINT) || - (is_multicast_ether_addr(dst) && - !compare_ether_addr(src, addr))) - return -1; - if (iftype == NL80211_IFTYPE_MESH_POINT) { - struct ieee80211s_hdr *meshdr = - (struct ieee80211s_hdr *) (skb->data + hdrlen); - /* 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) - skb_copy_bits(skb, hdrlen + - offsetof(struct ieee80211s_hdr, eaddr1), - src, ETH_ALEN); - hdrlen += ieee80211_get_mesh_hdrlen(meshdr); - } - break; - case cpu_to_le16(0): - if (iftype != NL80211_IFTYPE_ADHOC && - iftype != NL80211_IFTYPE_STATION) - return -1; - break; - } - - if (!pskb_may_pull(skb, hdrlen + 8)) - return -1; - - payload = skb->data + hdrlen; - ethertype = (payload[6] << 8) | payload[7]; - - if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && - ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || - compare_ether_addr(payload, bridge_tunnel_header) == 0)) { - /* remove RFC1042 or Bridge-Tunnel encapsulation and - * replace EtherType */ - skb_pull(skb, hdrlen + 6); - memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); - } else { - struct ethhdr *ehdr; - __be16 len; - - skb_pull(skb, hdrlen); - len = htons(skb->len); - ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr)); - memcpy(ehdr->h_dest, dst, ETH_ALEN); - memcpy(ehdr->h_source, src, ETH_ALEN); - ehdr->h_proto = len; - } - return 0; -} -EXPORT_SYMBOL(ieee80211_data_to_8023); - -int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, - enum nl80211_iftype iftype, u8 *bssid, bool qos) -{ - struct ieee80211_hdr hdr; - u16 hdrlen, ethertype; - __le16 fc; - const u8 *encaps_data; - int encaps_len, skip_header_bytes; - int nh_pos, h_pos; - int head_need; - - if (unlikely(skb->len < ETH_HLEN)) - return -EINVAL; - - nh_pos = skb_network_header(skb) - skb->data; - h_pos = skb_transport_header(skb) - skb->data; - - /* convert Ethernet header to proper 802.11 header (based on - * operation mode) */ - ethertype = (skb->data[12] << 8) | skb->data[13]; - fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); - - switch (iftype) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_P2P_GO: - fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); - /* DA BSSID SA */ - memcpy(hdr.addr1, skb->data, ETH_ALEN); - memcpy(hdr.addr2, addr, ETH_ALEN); - memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); - hdrlen = 24; - break; - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - fc |= cpu_to_le16(IEEE80211_FCTL_TODS); - /* BSSID SA DA */ - memcpy(hdr.addr1, bssid, ETH_ALEN); - memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); - memcpy(hdr.addr3, skb->data, ETH_ALEN); - hdrlen = 24; - break; - case NL80211_IFTYPE_ADHOC: - /* DA SA BSSID */ - memcpy(hdr.addr1, skb->data, ETH_ALEN); - memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); - memcpy(hdr.addr3, bssid, ETH_ALEN); - hdrlen = 24; - break; - default: - return -EOPNOTSUPP; - } - - if (qos) { - fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); - hdrlen += 2; - } - - hdr.frame_control = fc; - hdr.duration_id = 0; - hdr.seq_ctrl = 0; - - skip_header_bytes = ETH_HLEN; - if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { - encaps_data = bridge_tunnel_header; - encaps_len = sizeof(bridge_tunnel_header); - skip_header_bytes -= 2; - } else if (ethertype > 0x600) { - encaps_data = rfc1042_header; - encaps_len = sizeof(rfc1042_header); - skip_header_bytes -= 2; - } else { - encaps_data = NULL; - encaps_len = 0; - } - - skb_pull(skb, skip_header_bytes); - nh_pos -= skip_header_bytes; - h_pos -= skip_header_bytes; - - head_need = hdrlen + encaps_len - skb_headroom(skb); - - if (head_need > 0 || skb_cloned(skb)) { - head_need = max(head_need, 0); - if (head_need) - skb_orphan(skb); - - if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) - return -ENOMEM; - - skb->truesize += head_need; - } - - if (encaps_data) { - memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); - nh_pos += encaps_len; - h_pos += encaps_len; - } - - memcpy(skb_push(skb, hdrlen), &hdr, hdrlen); - - nh_pos += hdrlen; - h_pos += hdrlen; - - /* Update skb pointers to various headers since this modified frame - * is going to go through Linux networking code that may potentially - * need things like pointer to IP header. */ - skb_set_mac_header(skb, 0); - skb_set_network_header(skb, nh_pos); - skb_set_transport_header(skb, h_pos); - - return 0; -} -EXPORT_SYMBOL(ieee80211_data_from_8023); - - -void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, - const u8 *addr, enum nl80211_iftype iftype, - const unsigned int extra_headroom, - bool has_80211_header) -{ - struct sk_buff *frame = NULL; - u16 ethertype; - u8 *payload; - const struct ethhdr *eth; - int remaining, err; - u8 dst[ETH_ALEN], src[ETH_ALEN]; - - if (has_80211_header) { - err = ieee80211_data_to_8023(skb, addr, iftype); - if (err) - goto out; - - /* skip the wrapping header */ - eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); - if (!eth) - goto out; - } else { - eth = (struct ethhdr *) skb->data; - } - - while (skb != frame) { - u8 padding; - __be16 len = eth->h_proto; - unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len); - - remaining = skb->len; - memcpy(dst, eth->h_dest, ETH_ALEN); - memcpy(src, eth->h_source, ETH_ALEN); - - padding = (4 - subframe_len) & 0x3; - /* the last MSDU has no padding */ - if (subframe_len > remaining) - goto purge; - - skb_pull(skb, sizeof(struct ethhdr)); - /* reuse skb for the last subframe */ - if (remaining <= subframe_len + padding) - frame = skb; - else { - unsigned int hlen = ALIGN(extra_headroom, 4); - /* - * Allocate and reserve two bytes more for payload - * alignment since sizeof(struct ethhdr) is 14. - */ - frame = dev_alloc_skb(hlen + subframe_len + 2); - if (!frame) - goto purge; - - skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2); - memcpy(skb_put(frame, ntohs(len)), skb->data, - ntohs(len)); - - eth = (struct ethhdr *)skb_pull(skb, ntohs(len) + - padding); - if (!eth) { - dev_kfree_skb(frame); - goto purge; - } - } - - skb_reset_network_header(frame); - frame->dev = skb->dev; - frame->priority = skb->priority; - - payload = frame->data; - ethertype = (payload[6] << 8) | payload[7]; - - if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && - ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || - compare_ether_addr(payload, - bridge_tunnel_header) == 0)) { - /* remove RFC1042 or Bridge-Tunnel - * encapsulation and replace EtherType */ - skb_pull(frame, 6); - memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); - } else { - memcpy(skb_push(frame, sizeof(__be16)), &len, - sizeof(__be16)); - memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); - } - __skb_queue_tail(list, frame); - } - - return; - - purge: - __skb_queue_purge(list); - out: - dev_kfree_skb(skb); -} -EXPORT_SYMBOL(ieee80211_amsdu_to_8023s); - -/* Given a data frame determine the 802.1p/1d tag to use. */ -unsigned int cfg80211_classify8021d(struct sk_buff *skb) -{ - unsigned int dscp; - - /* skb->priority values from 256->263 are magic values to - * directly indicate a specific 802.1d priority. This is used - * to allow 802.1d priority to be passed directly in from VLAN - * tags, etc. - */ - if (skb->priority >= 256 && skb->priority <= 263) - return skb->priority - 256; - - switch (skb->protocol) { - case htons(ETH_P_IP): - dscp = ip_hdr(skb)->tos & 0xfc; - break; - default: - return 0; - } - - return dscp >> 5; -} -EXPORT_SYMBOL(cfg80211_classify8021d); - -const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) -{ - u8 *end, *pos; - - pos = bss->information_elements; - if (pos == NULL) - return NULL; - end = pos + bss->len_information_elements; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; -} -EXPORT_SYMBOL(ieee80211_bss_get_ie); - -void cfg80211_upload_connect_keys(struct wireless_dev *wdev) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct net_device *dev = wdev->netdev; - int i; - - if (!wdev->connect_keys) - return; - - 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])) { - 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)) { - 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)) - netdev_err(dev, "failed to set mgtdef %d\n", i); - } - - kfree(wdev->connect_keys); - wdev->connect_keys = NULL; -} - -static void cfg80211_process_wdev_events(struct wireless_dev *wdev) -{ - struct cfg80211_event *ev; - unsigned long flags; - const u8 *bssid = NULL; - - spin_lock_irqsave(&wdev->event_lock, flags); - while (!list_empty(&wdev->event_list)) { - ev = list_first_entry(&wdev->event_list, - struct cfg80211_event, list); - list_del(&ev->list); - spin_unlock_irqrestore(&wdev->event_lock, flags); - - wdev_lock(wdev); - switch (ev->type) { - case EVENT_CONNECT_RESULT: - if (!is_zero_ether_addr(ev->cr.bssid)) - bssid = ev->cr.bssid; - __cfg80211_connect_result( - wdev->netdev, bssid, - ev->cr.req_ie, ev->cr.req_ie_len, - ev->cr.resp_ie, ev->cr.resp_ie_len, - ev->cr.status, - ev->cr.status == WLAN_STATUS_SUCCESS, - NULL); - break; - case EVENT_ROAMED: - __cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie, - ev->rm.req_ie_len, ev->rm.resp_ie, - ev->rm.resp_ie_len); - break; - case EVENT_DISCONNECTED: - __cfg80211_disconnected(wdev->netdev, - ev->dc.ie, ev->dc.ie_len, - ev->dc.reason, true); - break; - case EVENT_IBSS_JOINED: - __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); - break; - } - wdev_unlock(wdev); - - kfree(ev); - - spin_lock_irqsave(&wdev->event_lock, flags); - } - spin_unlock_irqrestore(&wdev->event_lock, flags); -} - -void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev) -{ - struct wireless_dev *wdev; - - ASSERT_RTNL(); - ASSERT_RDEV_LOCK(rdev); - - mutex_lock(&rdev->devlist_mtx); - - list_for_each_entry(wdev, &rdev->netdev_list, list) - cfg80211_process_wdev_events(wdev); - - mutex_unlock(&rdev->devlist_mtx); -} - -int cfg80211_change_iface(struct cfg80211_registered_device *rdev, - struct net_device *dev, enum nl80211_iftype ntype, - u32 *flags, struct vif_params *params) -{ - int err; - enum nl80211_iftype otype = dev->ieee80211_ptr->iftype; - - ASSERT_RDEV_LOCK(rdev); - - /* don't support changing VLANs, you just re-create them */ - if (otype == NL80211_IFTYPE_AP_VLAN) - return -EOPNOTSUPP; - - if (!rdev->ops->change_virtual_intf || - !(rdev->wiphy.interface_modes & (1 << ntype))) - return -EOPNOTSUPP; - - /* if it's part of a bridge, reject changing type to station/ibss */ - if (br_port_exists(dev) && - (ntype == NL80211_IFTYPE_ADHOC || - ntype == NL80211_IFTYPE_STATION || - ntype == NL80211_IFTYPE_P2P_CLIENT)) - return -EBUSY; - - if (ntype != otype) { - err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr, - ntype); - if (err) - return err; - - dev->ieee80211_ptr->use_4addr = false; - dev->ieee80211_ptr->mesh_id_up_len = 0; - - switch (otype) { - case NL80211_IFTYPE_ADHOC: - cfg80211_leave_ibss(rdev, dev, false); - break; - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, true); - break; - case NL80211_IFTYPE_MESH_POINT: - /* mesh should be handled? */ - break; - default: - break; - } - - cfg80211_process_rdev_events(rdev); - } - - err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, - ntype, flags, params); - - WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); - - if (!err && params && params->use_4addr != -1) - dev->ieee80211_ptr->use_4addr = params->use_4addr; - - if (!err) { - dev->priv_flags &= ~IFF_DONT_BRIDGE; - switch (ntype) { - case NL80211_IFTYPE_STATION: - if (dev->ieee80211_ptr->use_4addr) - break; - /* fall through */ - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_ADHOC: - dev->priv_flags |= IFF_DONT_BRIDGE; - break; - case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_MESH_POINT: - /* bridging OK */ - break; - case NL80211_IFTYPE_MONITOR: - /* monitor can't bridge anyway */ - break; - case NL80211_IFTYPE_UNSPECIFIED: - case NUM_NL80211_IFTYPES: - /* not happening */ - break; - } - } - - return err; -} - -u16 cfg80211_calculate_bitrate(struct rate_info *rate) -{ - int modulation, streams, bitrate; - - if (!(rate->flags & RATE_INFO_FLAGS_MCS)) - return rate->legacy; - - /* the formula below does only work for MCS values smaller than 32 */ - if (rate->mcs >= 32) - return 0; - - modulation = rate->mcs & 7; - streams = (rate->mcs >> 3) + 1; - - bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? - 13500000 : 6500000; - - if (modulation < 4) - bitrate *= (modulation + 1); - else if (modulation == 4) - bitrate *= (modulation + 2); - else - bitrate *= (modulation + 3); - - bitrate *= streams; - - if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) - bitrate = (bitrate / 9) * 10; - - /* do NOT round down here */ - return (bitrate + 50000) / 100000; -} - -int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, - u32 beacon_int) -{ - struct wireless_dev *wdev; - int res = 0; - - if (!beacon_int) - return -EINVAL; - - mutex_lock(&rdev->devlist_mtx); - - list_for_each_entry(wdev, &rdev->netdev_list, list) { - if (!wdev->beacon_interval) - continue; - if (wdev->beacon_interval != beacon_int) { - res = -EINVAL; - break; - } - } - - mutex_unlock(&rdev->devlist_mtx); - - return res; -} - -int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, - enum nl80211_iftype iftype) -{ - struct wireless_dev *wdev_iter; - int num[NUM_NL80211_IFTYPES]; - int total = 1; - int i, j; - - ASSERT_RTNL(); - - /* Always allow software iftypes */ - if (rdev->wiphy.software_iftypes & BIT(iftype)) - return 0; - - /* - * Drivers will gradually all set this flag, until all - * have it we only enforce for those that set it. - */ - if (!(rdev->wiphy.flags & WIPHY_FLAG_ENFORCE_COMBINATIONS)) - return 0; - - memset(num, 0, sizeof(num)); - - num[iftype] = 1; - - mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { - if (wdev_iter == wdev) - continue; - if (!netif_running(wdev_iter->netdev)) - continue; - - if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) - continue; - - num[wdev_iter->iftype]++; - total++; - } - mutex_unlock(&rdev->devlist_mtx); - - for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { - const struct ieee80211_iface_combination *c; - struct ieee80211_iface_limit *limits; - - c = &rdev->wiphy.iface_combinations[i]; - - limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, - GFP_KERNEL); - if (!limits) - return -ENOMEM; - if (total > c->max_interfaces) - goto cont; - - for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { - if (rdev->wiphy.software_iftypes & BIT(iftype)) - continue; - for (j = 0; j < c->n_limits; j++) { - if (!(limits[j].types & iftype)) - continue; - if (limits[j].max < num[iftype]) - goto cont; - limits[j].max -= num[iftype]; - } - } - /* yay, it fits */ - kfree(limits); - return 0; - cont: - kfree(limits); - } - - return -EBUSY; -} - -int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, - const u8 *rates, unsigned int n_rates, - u32 *mask) -{ - int i, j; - - if (!sband) - return -EINVAL; - - if (n_rates == 0 || n_rates > NL80211_MAX_SUPP_RATES) - return -EINVAL; - - *mask = 0; - - for (i = 0; i < n_rates; i++) { - int rate = (rates[i] & 0x7f) * 5; - bool found = false; - - for (j = 0; j < sband->n_bitrates; j++) { - if (sband->bitrates[j].bitrate == rate) { - found = true; - *mask |= BIT(j); - break; - } - } - if (!found) - return -EINVAL; - } - - /* - * mask must have at least one bit set here since we - * didn't accept a 0-length rates array nor allowed - * entries in the array that didn't exist - */ - - return 0; -} - -u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, - struct ieee802_11_elems *elems, - u64 filter, u32 crc) -{ - size_t left = len; - u8 *pos = start; - bool calc_crc = filter != 0; - - memset(elems, 0, sizeof(*elems)); - elems->ie_start = start; - elems->total_len = len; - - while (left >= 2) { - u8 id, elen; - - id = *pos++; - elen = *pos++; - left -= 2; - - if (elen > left) - break; - - if (calc_crc && id < 64 && (filter & (1ULL << id))) - crc = crc32_be(crc, pos - 2, elen + 2); - - switch (id) { - case WLAN_EID_SSID: - elems->ssid = pos; - elems->ssid_len = elen; - break; - case WLAN_EID_SUPP_RATES: - elems->supp_rates = pos; - elems->supp_rates_len = elen; - break; - case WLAN_EID_FH_PARAMS: - elems->fh_params = pos; - elems->fh_params_len = elen; - break; - case WLAN_EID_DS_PARAMS: - elems->ds_params = pos; - elems->ds_params_len = elen; - break; - case WLAN_EID_CF_PARAMS: - elems->cf_params = pos; - elems->cf_params_len = elen; - break; - case WLAN_EID_TIM: - if (elen >= sizeof(struct ieee80211_tim_ie)) { - elems->tim = (void *)pos; - elems->tim_len = elen; - } - break; - case WLAN_EID_IBSS_PARAMS: - elems->ibss_params = pos; - elems->ibss_params_len = elen; - break; - case WLAN_EID_CHALLENGE: - elems->challenge = pos; - elems->challenge_len = elen; - break; - case WLAN_EID_VENDOR_SPECIFIC: - if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && - pos[2] == 0xf2) { - /* Microsoft OUI (00:50:F2) */ - - if (calc_crc) - crc = crc32_be(crc, pos - 2, elen + 2); - - if (pos[3] == 1) { - /* OUI Type 1 - WPA IE */ - elems->wpa = pos; - elems->wpa_len = elen; - } else if (elen >= 5 && pos[3] == 2) { - /* OUI Type 2 - WMM IE */ - if (pos[4] == 0) { - elems->wmm_info = pos; - elems->wmm_info_len = elen; - } else if (pos[4] == 1) { - elems->wmm_param = pos; - elems->wmm_param_len = elen; - } - } - } - break; - case WLAN_EID_RSN: - elems->rsn = pos; - elems->rsn_len = elen; - break; - case WLAN_EID_ERP_INFO: - elems->erp_info = pos; - elems->erp_info_len = elen; - break; - case WLAN_EID_EXT_SUPP_RATES: - elems->ext_supp_rates = pos; - elems->ext_supp_rates_len = elen; - break; - case WLAN_EID_HT_CAPABILITY: - if (elen >= sizeof(struct ieee80211_ht_cap)) - elems->ht_cap_elem = (void *)pos; - break; - case WLAN_EID_HT_INFORMATION: - if (elen >= sizeof(struct ieee80211_ht_info)) - elems->ht_info_elem = (void *)pos; - break; - case WLAN_EID_MESH_ID: - elems->mesh_id = pos; - elems->mesh_id_len = elen; - break; - case WLAN_EID_MESH_CONFIG: - if (elen >= sizeof(struct ieee80211_meshconf_ie)) - elems->mesh_config = (void *)pos; - break; - case WLAN_EID_PEER_MGMT: - elems->peering = pos; - elems->peering_len = elen; - break; - case WLAN_EID_PREQ: - elems->preq = pos; - elems->preq_len = elen; - break; - case WLAN_EID_PREP: - elems->prep = pos; - elems->prep_len = elen; - break; - case WLAN_EID_PERR: - elems->perr = pos; - elems->perr_len = elen; - break; - case WLAN_EID_RANN: - if (elen >= sizeof(struct ieee80211_rann_ie)) - elems->rann = (void *)pos; - break; - case WLAN_EID_CHANNEL_SWITCH: - elems->ch_switch_elem = pos; - elems->ch_switch_elem_len = elen; - break; - case WLAN_EID_QUIET: - if (!elems->quiet_elem) { - elems->quiet_elem = pos; - elems->quiet_elem_len = elen; - } - elems->num_of_quiet_elem++; - break; - case WLAN_EID_COUNTRY: - elems->country_elem = pos; - elems->country_elem_len = elen; - break; - case WLAN_EID_PWR_CONSTRAINT: - elems->pwr_constr_elem = pos; - elems->pwr_constr_elem_len = elen; - break; - case WLAN_EID_TIMEOUT_INTERVAL: - elems->timeout_int = pos; - elems->timeout_int_len = elen; - break; - default: - break; - } - - left -= elen; - pos += elen; - } - - return crc; -} -EXPORT_SYMBOL(ieee802_11_parse_elems_crc); diff --git a/net/wireless_ath/wext-compat.c b/net/wireless_ath/wext-compat.c deleted file mode 100755 index 3c24eb9..0000000 --- a/net/wireless_ath/wext-compat.c +++ /dev/null @@ -1,1531 +0,0 @@ -/* - * cfg80211 - wext compat code - * - * This is temporary code until all wireless functionality is migrated - * into cfg80211, when that happens all the exports here go away and - * we directly assign the wireless handlers of wireless interfaces. - * - * Copyright 2008-2009 Johannes Berg <johannes@sipsolutions.net> - */ - -#include <linux/export.h> -#include <linux/wireless.h> -#include <linux/nl80211.h> -#include <linux/if_arp.h> -#include <linux/etherdevice.h> -#include <linux/slab.h> -#include <net/iw_handler.h> -#include <net/cfg80211.h> -#include <net/cfg80211-wext.h> -#include "wext-compat.h" -#include "core.h" - -int cfg80211_wext_giwname(struct net_device *dev, - struct iw_request_info *info, - char *name, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct ieee80211_supported_band *sband; - bool is_ht = false, is_a = false, is_b = false, is_g = false; - - if (!wdev) - return -EOPNOTSUPP; - - sband = wdev->wiphy->bands[IEEE80211_BAND_5GHZ]; - if (sband) { - is_a = true; - is_ht |= sband->ht_cap.ht_supported; - } - - sband = wdev->wiphy->bands[IEEE80211_BAND_2GHZ]; - if (sband) { - int i; - /* Check for mandatory rates */ - for (i = 0; i < sband->n_bitrates; i++) { - if (sband->bitrates[i].bitrate == 10) - is_b = true; - if (sband->bitrates[i].bitrate == 60) - is_g = true; - } - is_ht |= sband->ht_cap.ht_supported; - } - - strcpy(name, "IEEE 802.11"); - if (is_a) - strcat(name, "a"); - if (is_b) - strcat(name, "b"); - if (is_g) - strcat(name, "g"); - if (is_ht) - strcat(name, "n"); - - return 0; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwname); - -int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, - u32 *mode, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev; - struct vif_params vifparams; - enum nl80211_iftype type; - int ret; - - rdev = wiphy_to_dev(wdev->wiphy); - - switch (*mode) { - case IW_MODE_INFRA: - type = NL80211_IFTYPE_STATION; - break; - case IW_MODE_ADHOC: - type = NL80211_IFTYPE_ADHOC; - break; - case IW_MODE_REPEAT: - type = NL80211_IFTYPE_WDS; - break; - case IW_MODE_MONITOR: - type = NL80211_IFTYPE_MONITOR; - break; - default: - return -EINVAL; - } - - if (type == wdev->iftype) - return 0; - - memset(&vifparams, 0, sizeof(vifparams)); - - cfg80211_lock_rdev(rdev); - ret = cfg80211_change_iface(rdev, dev, type, NULL, &vifparams); - cfg80211_unlock_rdev(rdev); - - return ret; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_siwmode); - -int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, - u32 *mode, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (!wdev) - return -EOPNOTSUPP; - - switch (wdev->iftype) { - case NL80211_IFTYPE_AP: - *mode = IW_MODE_MASTER; - break; - case NL80211_IFTYPE_STATION: - *mode = IW_MODE_INFRA; - break; - case NL80211_IFTYPE_ADHOC: - *mode = IW_MODE_ADHOC; - break; - case NL80211_IFTYPE_MONITOR: - *mode = IW_MODE_MONITOR; - break; - case NL80211_IFTYPE_WDS: - *mode = IW_MODE_REPEAT; - break; - case NL80211_IFTYPE_AP_VLAN: - *mode = IW_MODE_SECOND; /* FIXME */ - break; - default: - *mode = IW_MODE_AUTO; - break; - } - return 0; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwmode); - - -int cfg80211_wext_giwrange(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct iw_range *range = (struct iw_range *) extra; - enum ieee80211_band band; - int i, c = 0; - - if (!wdev) - return -EOPNOTSUPP; - - data->length = sizeof(struct iw_range); - memset(range, 0, sizeof(struct iw_range)); - - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 21; - range->retry_capa = IW_RETRY_LIMIT; - range->retry_flags = IW_RETRY_LIMIT; - range->min_retry = 0; - range->max_retry = 255; - range->min_rts = 0; - range->max_rts = 2347; - range->min_frag = 256; - range->max_frag = 2346; - - range->max_encoding_tokens = 4; - - range->max_qual.updated = IW_QUAL_NOISE_INVALID; - - switch (wdev->wiphy->signal_type) { - case CFG80211_SIGNAL_TYPE_NONE: - break; - case CFG80211_SIGNAL_TYPE_MBM: - range->max_qual.level = -110; - range->max_qual.qual = 70; - range->avg_qual.qual = 35; - range->max_qual.updated |= IW_QUAL_DBM; - range->max_qual.updated |= IW_QUAL_QUAL_UPDATED; - range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED; - break; - case CFG80211_SIGNAL_TYPE_UNSPEC: - range->max_qual.level = 100; - range->max_qual.qual = 100; - range->avg_qual.qual = 50; - range->max_qual.updated |= IW_QUAL_QUAL_UPDATED; - range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED; - break; - } - - range->avg_qual.level = range->max_qual.level / 2; - range->avg_qual.noise = range->max_qual.noise / 2; - range->avg_qual.updated = range->max_qual.updated; - - for (i = 0; i < wdev->wiphy->n_cipher_suites; i++) { - switch (wdev->wiphy->cipher_suites[i]) { - case WLAN_CIPHER_SUITE_TKIP: - range->enc_capa |= (IW_ENC_CAPA_CIPHER_TKIP | - IW_ENC_CAPA_WPA); - break; - - case WLAN_CIPHER_SUITE_CCMP: - range->enc_capa |= (IW_ENC_CAPA_CIPHER_CCMP | - IW_ENC_CAPA_WPA2); - break; - - case WLAN_CIPHER_SUITE_WEP40: - range->encoding_size[range->num_encoding_sizes++] = - WLAN_KEY_LEN_WEP40; - break; - - case WLAN_CIPHER_SUITE_WEP104: - range->encoding_size[range->num_encoding_sizes++] = - WLAN_KEY_LEN_WEP104; - break; - } - } - - for (band = 0; band < IEEE80211_NUM_BANDS; band ++) { - struct ieee80211_supported_band *sband; - - sband = wdev->wiphy->bands[band]; - - if (!sband) - continue; - - for (i = 0; i < sband->n_channels && c < IW_MAX_FREQUENCIES; i++) { - struct ieee80211_channel *chan = &sband->channels[i]; - - if (!(chan->flags & IEEE80211_CHAN_DISABLED)) { - range->freq[c].i = - ieee80211_frequency_to_channel( - chan->center_freq); - range->freq[c].m = chan->center_freq; - range->freq[c].e = 6; - c++; - } - } - } - range->num_channels = c; - range->num_frequency = c; - - IW_EVENT_CAPA_SET_KERNEL(range->event_capa); - IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); - IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); - - if (wdev->wiphy->max_scan_ssids > 0) - range->scan_capa |= IW_SCAN_CAPA_ESSID; - - return 0; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange); - - -/** - * cfg80211_wext_freq - get wext frequency for non-"auto" - * @wiphy: the wiphy - * @freq: the wext freq encoding - * - * Returns a frequency, or a negative error code, or 0 for auto. - */ -int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq) -{ - /* - * Parse frequency - return 0 for auto and - * -EINVAL for impossible things. - */ - if (freq->e == 0) { - enum ieee80211_band band = IEEE80211_BAND_2GHZ; - if (freq->m < 0) - return 0; - if (freq->m > 14) - band = IEEE80211_BAND_5GHZ; - return ieee80211_channel_to_frequency(freq->m, band); - } else { - int i, div = 1000000; - for (i = 0; i < freq->e; i++) - div /= 10; - if (div <= 0) - return -EINVAL; - return freq->m / div; - } -} - -int cfg80211_wext_siwrts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rts, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u32 orts = wdev->wiphy->rts_threshold; - int err; - - if (rts->disabled || !rts->fixed) - wdev->wiphy->rts_threshold = (u32) -1; - else if (rts->value < 0) - return -EINVAL; - else - wdev->wiphy->rts_threshold = rts->value; - - err = rdev->ops->set_wiphy_params(wdev->wiphy, - WIPHY_PARAM_RTS_THRESHOLD); - if (err) - wdev->wiphy->rts_threshold = orts; - - return err; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_siwrts); - -int cfg80211_wext_giwrts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rts, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - rts->value = wdev->wiphy->rts_threshold; - rts->disabled = rts->value == (u32) -1; - rts->fixed = 1; - - return 0; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwrts); - -int cfg80211_wext_siwfrag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *frag, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u32 ofrag = wdev->wiphy->frag_threshold; - int err; - - if (frag->disabled || !frag->fixed) - wdev->wiphy->frag_threshold = (u32) -1; - else if (frag->value < 256) - return -EINVAL; - else { - /* Fragment length must be even, so strip LSB. */ - wdev->wiphy->frag_threshold = frag->value & ~0x1; - } - - err = rdev->ops->set_wiphy_params(wdev->wiphy, - WIPHY_PARAM_FRAG_THRESHOLD); - if (err) - wdev->wiphy->frag_threshold = ofrag; - - return err; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_siwfrag); - -int cfg80211_wext_giwfrag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *frag, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - frag->value = wdev->wiphy->frag_threshold; - frag->disabled = frag->value == (u32) -1; - frag->fixed = 1; - - return 0; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwfrag); - -static int cfg80211_wext_siwretry(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *retry, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u32 changed = 0; - u8 olong = wdev->wiphy->retry_long; - u8 oshort = wdev->wiphy->retry_short; - int err; - - if (retry->disabled || - (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) - return -EINVAL; - - if (retry->flags & IW_RETRY_LONG) { - wdev->wiphy->retry_long = retry->value; - changed |= WIPHY_PARAM_RETRY_LONG; - } else if (retry->flags & IW_RETRY_SHORT) { - wdev->wiphy->retry_short = retry->value; - changed |= WIPHY_PARAM_RETRY_SHORT; - } else { - wdev->wiphy->retry_short = retry->value; - wdev->wiphy->retry_long = retry->value; - changed |= WIPHY_PARAM_RETRY_LONG; - changed |= WIPHY_PARAM_RETRY_SHORT; - } - - if (!changed) - return 0; - - err = rdev->ops->set_wiphy_params(wdev->wiphy, changed); - if (err) { - wdev->wiphy->retry_short = oshort; - wdev->wiphy->retry_long = olong; - } - - return err; -} - -int cfg80211_wext_giwretry(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *retry, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - retry->disabled = 0; - - if (retry->flags == 0 || (retry->flags & IW_RETRY_SHORT)) { - /* - * First return short value, iwconfig will ask long value - * later if needed - */ - retry->flags |= IW_RETRY_LIMIT; - retry->value = wdev->wiphy->retry_short; - if (wdev->wiphy->retry_long != wdev->wiphy->retry_short) - retry->flags |= IW_RETRY_LONG; - - return 0; - } - - if (retry->flags & IW_RETRY_LONG) { - retry->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; - retry->value = wdev->wiphy->retry_long; - } - - return 0; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwretry); - -static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool pairwise, - const u8 *addr, bool remove, bool tx_key, - int idx, struct key_params *params) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err, i; - bool rejoin = false; - - if (pairwise && !addr) - return -EINVAL; - - if (!wdev->wext.keys) { - wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys), - GFP_KERNEL); - if (!wdev->wext.keys) - return -ENOMEM; - for (i = 0; i < 6; i++) - wdev->wext.keys->params[i].key = - wdev->wext.keys->data[i]; - } - - if (wdev->iftype != NL80211_IFTYPE_ADHOC && - wdev->iftype != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - - if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { - if (!wdev->current_bss) - return -ENOLINK; - - if (!rdev->ops->set_default_mgmt_key) - return -EOPNOTSUPP; - - if (idx < 4 || idx > 5) - return -EINVAL; - } else if (idx < 0 || idx > 3) - return -EINVAL; - - if (remove) { - err = 0; - if (wdev->current_bss) { - /* - * If removing the current TX key, we will need to - * join a new IBSS without the privacy bit clear. - */ - if (idx == wdev->wext.default_key && - wdev->iftype == NL80211_IFTYPE_ADHOC) { - __cfg80211_leave_ibss(rdev, wdev->netdev, true); - rejoin = true; - } - - if (!pairwise && addr && - !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) - err = -ENOENT; - else - err = rdev->ops->del_key(&rdev->wiphy, dev, idx, - pairwise, addr); - } - wdev->wext.connect.privacy = false; - /* - * Applications using wireless extensions expect to be - * able to delete keys that don't exist, so allow that. - */ - if (err == -ENOENT) - err = 0; - if (!err) { - if (!addr) { - wdev->wext.keys->params[idx].key_len = 0; - wdev->wext.keys->params[idx].cipher = 0; - } - if (idx == wdev->wext.default_key) - wdev->wext.default_key = -1; - else if (idx == wdev->wext.default_mgmt_key) - wdev->wext.default_mgmt_key = -1; - } - - if (!err && rejoin) - err = cfg80211_ibss_wext_join(rdev, wdev); - - return err; - } - - if (addr) - tx_key = false; - - if (cfg80211_validate_key_settings(rdev, params, idx, pairwise, addr)) - return -EINVAL; - - err = 0; - if (wdev->current_bss) - err = rdev->ops->add_key(&rdev->wiphy, dev, idx, - pairwise, addr, params); - if (err) - return err; - - if (!addr) { - wdev->wext.keys->params[idx] = *params; - memcpy(wdev->wext.keys->data[idx], - params->key, params->key_len); - wdev->wext.keys->params[idx].key = - wdev->wext.keys->data[idx]; - } - - if ((params->cipher == WLAN_CIPHER_SUITE_WEP40 || - params->cipher == WLAN_CIPHER_SUITE_WEP104) && - (tx_key || (!addr && wdev->wext.default_key == -1))) { - if (wdev->current_bss) { - /* - * If we are getting a new TX key from not having - * had one before we need to join a new IBSS with - * the privacy bit set. - */ - if (wdev->iftype == NL80211_IFTYPE_ADHOC && - wdev->wext.default_key == -1) { - __cfg80211_leave_ibss(rdev, wdev->netdev, true); - rejoin = true; - } - err = rdev->ops->set_default_key(&rdev->wiphy, dev, - idx, true, true); - } - if (!err) { - wdev->wext.default_key = idx; - if (rejoin) - err = cfg80211_ibss_wext_join(rdev, wdev); - } - return err; - } - - 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); - if (!err) - wdev->wext.default_mgmt_key = idx; - return err; - } - - return 0; -} - -static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool pairwise, - const u8 *addr, bool remove, bool tx_key, - int idx, struct key_params *params) -{ - int err; - - /* devlist mutex needed for possible IBSS re-join */ - mutex_lock(&rdev->devlist_mtx); - wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_set_encryption(rdev, dev, pairwise, addr, - remove, tx_key, idx, params); - wdev_unlock(dev->ieee80211_ptr); - mutex_unlock(&rdev->devlist_mtx); - - return err; -} - -static int cfg80211_wext_siwencode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, char *keybuf) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int idx, err; - bool remove = false; - struct key_params params; - - if (wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_ADHOC) - return -EOPNOTSUPP; - - /* no use -- only MFP (set_default_mgmt_key) is optional */ - if (!rdev->ops->del_key || - !rdev->ops->add_key || - !rdev->ops->set_default_key) - return -EOPNOTSUPP; - - idx = erq->flags & IW_ENCODE_INDEX; - if (idx == 0) { - idx = wdev->wext.default_key; - if (idx < 0) - idx = 0; - } else if (idx < 1 || idx > 4) - return -EINVAL; - else - idx--; - - if (erq->flags & IW_ENCODE_DISABLED) - remove = true; - else if (erq->length == 0) { - /* No key data - just set the default TX key index */ - err = 0; - wdev_lock(wdev); - if (wdev->current_bss) - err = rdev->ops->set_default_key(&rdev->wiphy, dev, - idx, true, true); - if (!err) - wdev->wext.default_key = idx; - wdev_unlock(wdev); - return err; - } - - memset(¶ms, 0, sizeof(params)); - params.key = keybuf; - params.key_len = erq->length; - if (erq->length == 5) - params.cipher = WLAN_CIPHER_SUITE_WEP40; - else if (erq->length == 13) - params.cipher = WLAN_CIPHER_SUITE_WEP104; - else if (!remove) - return -EINVAL; - - return cfg80211_set_encryption(rdev, dev, false, NULL, remove, - wdev->wext.default_key == -1, - idx, ¶ms); -} - -static int cfg80211_wext_siwencodeext(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; - const u8 *addr; - int idx; - bool remove = false; - struct key_params params; - u32 cipher; - - if (wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_ADHOC) - return -EOPNOTSUPP; - - /* no use -- only MFP (set_default_mgmt_key) is optional */ - if (!rdev->ops->del_key || - !rdev->ops->add_key || - !rdev->ops->set_default_key) - return -EOPNOTSUPP; - - switch (ext->alg) { - case IW_ENCODE_ALG_NONE: - remove = true; - cipher = 0; - break; - case IW_ENCODE_ALG_WEP: - if (ext->key_len == 5) - cipher = WLAN_CIPHER_SUITE_WEP40; - else if (ext->key_len == 13) - cipher = WLAN_CIPHER_SUITE_WEP104; - else - return -EINVAL; - break; - case IW_ENCODE_ALG_TKIP: - cipher = WLAN_CIPHER_SUITE_TKIP; - break; - case IW_ENCODE_ALG_CCMP: - cipher = WLAN_CIPHER_SUITE_CCMP; - break; - case IW_ENCODE_ALG_AES_CMAC: - cipher = WLAN_CIPHER_SUITE_AES_CMAC; - break; - default: - return -EOPNOTSUPP; - } - - if (erq->flags & IW_ENCODE_DISABLED) - remove = true; - - idx = erq->flags & IW_ENCODE_INDEX; - if (cipher == WLAN_CIPHER_SUITE_AES_CMAC) { - if (idx < 4 || idx > 5) { - idx = wdev->wext.default_mgmt_key; - if (idx < 0) - return -EINVAL; - } else - idx--; - } else { - if (idx < 1 || idx > 4) { - idx = wdev->wext.default_key; - if (idx < 0) - return -EINVAL; - } else - idx--; - } - - addr = ext->addr.sa_data; - if (is_broadcast_ether_addr(addr)) - addr = NULL; - - memset(¶ms, 0, sizeof(params)); - params.key = ext->key; - params.key_len = ext->key_len; - params.cipher = cipher; - - if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { - params.seq = ext->rx_seq; - params.seq_len = 6; - } - - return cfg80211_set_encryption( - rdev, dev, - !(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY), - addr, remove, - ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, - idx, ¶ms); -} - -static int cfg80211_wext_giwencode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, char *keybuf) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int idx; - - if (wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_ADHOC) - return -EOPNOTSUPP; - - idx = erq->flags & IW_ENCODE_INDEX; - if (idx == 0) { - idx = wdev->wext.default_key; - if (idx < 0) - idx = 0; - } else if (idx < 1 || idx > 4) - return -EINVAL; - else - idx--; - - erq->flags = idx + 1; - - if (!wdev->wext.keys || !wdev->wext.keys->params[idx].cipher) { - erq->flags |= IW_ENCODE_DISABLED; - erq->length = 0; - return 0; - } - - erq->length = min_t(size_t, erq->length, - wdev->wext.keys->params[idx].key_len); - memcpy(keybuf, wdev->wext.keys->params[idx].key, erq->length); - erq->flags |= IW_ENCODE_ENABLED; - - return 0; -} - -static int cfg80211_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *wextfreq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int freq, err; - - switch (wdev->iftype) { - case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra); - case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_MESH_POINT: - freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); - if (freq < 0) - return freq; - if (freq == 0) - return -EINVAL; - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - return err; - default: - return -EOPNOTSUPP; - } -} - -static int cfg80211_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan; - - switch (wdev->iftype) { - case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); - case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); - case NL80211_IFTYPE_MONITOR: - if (!rdev->ops->get_channel) - return -EINVAL; - - chan = rdev->ops->get_channel(wdev->wiphy); - if (!chan) - return -EINVAL; - freq->m = chan->center_freq; - freq->e = 6; - return 0; - default: - if (!wdev->channel) - return -EINVAL; - freq->m = wdev->channel->center_freq; - freq->e = 6; - return 0; - } -} - -static int cfg80211_wext_siwtxpower(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - enum nl80211_tx_power_setting type; - int dbm = 0; - - if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) - return -EINVAL; - if (data->txpower.flags & IW_TXPOW_RANGE) - return -EINVAL; - - if (!rdev->ops->set_tx_power) - return -EOPNOTSUPP; - - /* only change when not disabling */ - if (!data->txpower.disabled) { - rfkill_set_sw_state(rdev->rfkill, false); - - if (data->txpower.fixed) { - /* - * wext doesn't support negative values, see - * below where it's for automatic - */ - if (data->txpower.value < 0) - return -EINVAL; - dbm = data->txpower.value; - type = NL80211_TX_POWER_FIXED; - /* TODO: do regulatory check! */ - } else { - /* - * Automatic power level setting, max being the value - * passed in from userland. - */ - if (data->txpower.value < 0) { - type = NL80211_TX_POWER_AUTOMATIC; - } else { - dbm = data->txpower.value; - type = NL80211_TX_POWER_LIMITED; - } - } - } else { - rfkill_set_sw_state(rdev->rfkill, true); - schedule_work(&rdev->rfkill_sync); - return 0; - } - - return rdev->ops->set_tx_power(wdev->wiphy, type, DBM_TO_MBM(dbm)); -} - -static int cfg80211_wext_giwtxpower(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int err, val; - - if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) - return -EINVAL; - if (data->txpower.flags & IW_TXPOW_RANGE) - return -EINVAL; - - if (!rdev->ops->get_tx_power) - return -EOPNOTSUPP; - - err = rdev->ops->get_tx_power(wdev->wiphy, &val); - if (err) - return err; - - /* well... oh well */ - data->txpower.fixed = 1; - data->txpower.disabled = rfkill_blocked(rdev->rfkill); - data->txpower.value = val; - data->txpower.flags = IW_TXPOW_DBM; - - return 0; -} - -static int cfg80211_set_auth_alg(struct wireless_dev *wdev, - s32 auth_alg) -{ - int nr_alg = 0; - - if (!auth_alg) - return -EINVAL; - - if (auth_alg & ~(IW_AUTH_ALG_OPEN_SYSTEM | - IW_AUTH_ALG_SHARED_KEY | - IW_AUTH_ALG_LEAP)) - return -EINVAL; - - if (auth_alg & IW_AUTH_ALG_OPEN_SYSTEM) { - nr_alg++; - wdev->wext.connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; - } - - if (auth_alg & IW_AUTH_ALG_SHARED_KEY) { - nr_alg++; - wdev->wext.connect.auth_type = NL80211_AUTHTYPE_SHARED_KEY; - } - - if (auth_alg & IW_AUTH_ALG_LEAP) { - nr_alg++; - wdev->wext.connect.auth_type = NL80211_AUTHTYPE_NETWORK_EAP; - } - - if (nr_alg > 1) - wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; - - return 0; -} - -static int cfg80211_set_wpa_version(struct wireless_dev *wdev, u32 wpa_versions) -{ - if (wpa_versions & ~(IW_AUTH_WPA_VERSION_WPA | - IW_AUTH_WPA_VERSION_WPA2| - IW_AUTH_WPA_VERSION_DISABLED)) - return -EINVAL; - - if ((wpa_versions & IW_AUTH_WPA_VERSION_DISABLED) && - (wpa_versions & (IW_AUTH_WPA_VERSION_WPA| - IW_AUTH_WPA_VERSION_WPA2))) - return -EINVAL; - - if (wpa_versions & IW_AUTH_WPA_VERSION_DISABLED) - wdev->wext.connect.crypto.wpa_versions &= - ~(NL80211_WPA_VERSION_1|NL80211_WPA_VERSION_2); - - if (wpa_versions & IW_AUTH_WPA_VERSION_WPA) - wdev->wext.connect.crypto.wpa_versions |= - NL80211_WPA_VERSION_1; - - if (wpa_versions & IW_AUTH_WPA_VERSION_WPA2) - wdev->wext.connect.crypto.wpa_versions |= - NL80211_WPA_VERSION_2; - - return 0; -} - -static int cfg80211_set_cipher_group(struct wireless_dev *wdev, u32 cipher) -{ - if (cipher & IW_AUTH_CIPHER_WEP40) - wdev->wext.connect.crypto.cipher_group = - WLAN_CIPHER_SUITE_WEP40; - else if (cipher & IW_AUTH_CIPHER_WEP104) - wdev->wext.connect.crypto.cipher_group = - WLAN_CIPHER_SUITE_WEP104; - else if (cipher & IW_AUTH_CIPHER_TKIP) - wdev->wext.connect.crypto.cipher_group = - WLAN_CIPHER_SUITE_TKIP; - else if (cipher & IW_AUTH_CIPHER_CCMP) - wdev->wext.connect.crypto.cipher_group = - WLAN_CIPHER_SUITE_CCMP; - else if (cipher & IW_AUTH_CIPHER_AES_CMAC) - wdev->wext.connect.crypto.cipher_group = - WLAN_CIPHER_SUITE_AES_CMAC; - else if (cipher & IW_AUTH_CIPHER_NONE) - wdev->wext.connect.crypto.cipher_group = 0; - else - return -EINVAL; - - return 0; -} - -static int cfg80211_set_cipher_pairwise(struct wireless_dev *wdev, u32 cipher) -{ - int nr_ciphers = 0; - u32 *ciphers_pairwise = wdev->wext.connect.crypto.ciphers_pairwise; - - if (cipher & IW_AUTH_CIPHER_WEP40) { - ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP40; - nr_ciphers++; - } - - if (cipher & IW_AUTH_CIPHER_WEP104) { - ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP104; - nr_ciphers++; - } - - if (cipher & IW_AUTH_CIPHER_TKIP) { - ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_TKIP; - nr_ciphers++; - } - - if (cipher & IW_AUTH_CIPHER_CCMP) { - ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_CCMP; - nr_ciphers++; - } - - if (cipher & IW_AUTH_CIPHER_AES_CMAC) { - ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_AES_CMAC; - nr_ciphers++; - } - - BUILD_BUG_ON(NL80211_MAX_NR_CIPHER_SUITES < 5); - - wdev->wext.connect.crypto.n_ciphers_pairwise = nr_ciphers; - - return 0; -} - - -static int cfg80211_set_key_mgt(struct wireless_dev *wdev, u32 key_mgt) -{ - int nr_akm_suites = 0; - - if (key_mgt & ~(IW_AUTH_KEY_MGMT_802_1X | - IW_AUTH_KEY_MGMT_PSK)) - return -EINVAL; - - if (key_mgt & IW_AUTH_KEY_MGMT_802_1X) { - wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = - WLAN_AKM_SUITE_8021X; - nr_akm_suites++; - } - - if (key_mgt & IW_AUTH_KEY_MGMT_PSK) { - wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = - WLAN_AKM_SUITE_PSK; - nr_akm_suites++; - } - - wdev->wext.connect.crypto.n_akm_suites = nr_akm_suites; - - return 0; -} - -static int cfg80211_wext_siwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - - switch (data->flags & IW_AUTH_INDEX) { - case IW_AUTH_PRIVACY_INVOKED: - wdev->wext.connect.privacy = data->value; - return 0; - case IW_AUTH_WPA_VERSION: - return cfg80211_set_wpa_version(wdev, data->value); - case IW_AUTH_CIPHER_GROUP: - return cfg80211_set_cipher_group(wdev, data->value); - case IW_AUTH_KEY_MGMT: - return cfg80211_set_key_mgt(wdev, data->value); - case IW_AUTH_CIPHER_PAIRWISE: - return cfg80211_set_cipher_pairwise(wdev, data->value); - case IW_AUTH_80211_AUTH_ALG: - return cfg80211_set_auth_alg(wdev, data->value); - case IW_AUTH_WPA_ENABLED: - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - case IW_AUTH_DROP_UNENCRYPTED: - case IW_AUTH_MFP: - return 0; - default: - return -EOPNOTSUPP; - } -} - -static int cfg80211_wext_giwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - /* XXX: what do we need? */ - - return -EOPNOTSUPP; -} - -static int cfg80211_wext_siwpower(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *wrq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - bool ps = wdev->ps; - int timeout = wdev->ps_timeout; - int err; - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EINVAL; - - if (!rdev->ops->set_power_mgmt) - return -EOPNOTSUPP; - - if (wrq->disabled) { - ps = false; - } else { - switch (wrq->flags & IW_POWER_MODE) { - case IW_POWER_ON: /* If not specified */ - case IW_POWER_MODE: /* If set all mask */ - case IW_POWER_ALL_R: /* If explicitely state all */ - ps = true; - break; - default: /* Otherwise we ignore */ - return -EINVAL; - } - - if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT)) - return -EINVAL; - - if (wrq->flags & IW_POWER_TIMEOUT) - timeout = wrq->value / 1000; - } - - err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, ps, timeout); - if (err) - return err; - - wdev->ps = ps; - wdev->ps_timeout = timeout; - - return 0; - -} - -static int cfg80211_wext_giwpower(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *wrq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wrq->disabled = !wdev->ps; - - return 0; -} - -static int cfg80211_wds_wext_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int err; - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS)) - return -EINVAL; - - if (addr->sa_family != ARPHRD_ETHER) - return -EINVAL; - - if (netif_running(dev)) - return -EBUSY; - - if (!rdev->ops->set_wds_peer) - return -EOPNOTSUPP; - - err = rdev->ops->set_wds_peer(wdev->wiphy, dev, (u8 *) &addr->sa_data); - if (err) - return err; - - memcpy(&wdev->wext.bssid, (u8 *) &addr->sa_data, ETH_ALEN); - - return 0; -} - -static int cfg80211_wds_wext_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS)) - return -EINVAL; - - addr->sa_family = ARPHRD_ETHER; - memcpy(&addr->sa_data, wdev->wext.bssid, ETH_ALEN); - - return 0; -} - -static int cfg80211_wext_siwrate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rate, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_bitrate_mask mask; - u32 fixed, maxrate; - struct ieee80211_supported_band *sband; - int band, ridx; - bool match = false; - - if (!rdev->ops->set_bitrate_mask) - return -EOPNOTSUPP; - - memset(&mask, 0, sizeof(mask)); - fixed = 0; - maxrate = (u32)-1; - - if (rate->value < 0) { - /* nothing */ - } else if (rate->fixed) { - fixed = rate->value / 100000; - } else { - maxrate = rate->value / 100000; - } - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - sband = wdev->wiphy->bands[band]; - if (sband == NULL) - continue; - for (ridx = 0; ridx < sband->n_bitrates; ridx++) { - struct ieee80211_rate *srate = &sband->bitrates[ridx]; - if (fixed == srate->bitrate) { - mask.control[band].legacy = 1 << ridx; - match = true; - break; - } - if (srate->bitrate <= maxrate) { - mask.control[band].legacy |= 1 << ridx; - match = true; - } - } - } - - if (!match) - return -EINVAL; - - return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask); -} - -static int cfg80211_wext_giwrate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rate, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - /* we are under RTNL - globally locked - so can use a static struct */ - static struct station_info sinfo; - u8 addr[ETH_ALEN]; - int err; - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - - if (!rdev->ops->get_station) - return -EOPNOTSUPP; - - err = 0; - wdev_lock(wdev); - if (wdev->current_bss) - memcpy(addr, wdev->current_bss->pub.bssid, ETH_ALEN); - else - err = -EOPNOTSUPP; - wdev_unlock(wdev); - if (err) - return err; - - err = rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo); - if (err) - return err; - - if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) - return -EOPNOTSUPP; - - rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); - - return 0; -} - -/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ -static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - /* we are under RTNL - globally locked - so can use static structs */ - static struct iw_statistics wstats; - static struct station_info sinfo; - u8 bssid[ETH_ALEN]; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) - return NULL; - - if (!rdev->ops->get_station) - return NULL; - - /* Grab BSSID of current BSS, if any */ - wdev_lock(wdev); - if (!wdev->current_bss) { - wdev_unlock(wdev); - return NULL; - } - memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); - wdev_unlock(wdev); - - if (rdev->ops->get_station(&rdev->wiphy, dev, bssid, &sinfo)) - return NULL; - - memset(&wstats, 0, sizeof(wstats)); - - switch (rdev->wiphy.signal_type) { - case CFG80211_SIGNAL_TYPE_MBM: - if (sinfo.filled & STATION_INFO_SIGNAL) { - int sig = sinfo.signal; - wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; - wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; - wstats.qual.updated |= IW_QUAL_DBM; - wstats.qual.level = sig; - if (sig < -110) - sig = -110; - else if (sig > -40) - sig = -40; - wstats.qual.qual = sig + 110; - break; - } - case CFG80211_SIGNAL_TYPE_UNSPEC: - if (sinfo.filled & STATION_INFO_SIGNAL) { - wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; - wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; - wstats.qual.level = sinfo.signal; - wstats.qual.qual = sinfo.signal; - break; - } - default: - wstats.qual.updated |= IW_QUAL_LEVEL_INVALID; - wstats.qual.updated |= IW_QUAL_QUAL_INVALID; - } - - wstats.qual.updated |= IW_QUAL_NOISE_INVALID; - if (sinfo.filled & STATION_INFO_RX_DROP_MISC) - wstats.discard.misc = sinfo.rx_dropped_misc; - if (sinfo.filled & STATION_INFO_TX_FAILED) - wstats.discard.retries = sinfo.tx_failed; - - return &wstats; -} - -static int cfg80211_wext_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra); - case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra); - case NL80211_IFTYPE_WDS: - return cfg80211_wds_wext_siwap(dev, info, ap_addr, extra); - default: - return -EOPNOTSUPP; - } -} - -static int cfg80211_wext_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra); - case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra); - case NL80211_IFTYPE_WDS: - return cfg80211_wds_wext_giwap(dev, info, ap_addr, extra); - default: - return -EOPNOTSUPP; - } -} - -static int cfg80211_wext_siwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); - case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_siwessid(dev, info, data, ssid); - default: - return -EOPNOTSUPP; - } -} - -static int cfg80211_wext_giwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - data->flags = 0; - data->length = 0; - - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); - case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_giwessid(dev, info, data, ssid); - default: - return -EOPNOTSUPP; - } -} - -static int cfg80211_wext_siwpmksa(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_pmksa cfg_pmksa; - struct iw_pmksa *pmksa = (struct iw_pmksa *)extra; - - memset(&cfg_pmksa, 0, sizeof(struct cfg80211_pmksa)); - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EINVAL; - - cfg_pmksa.bssid = pmksa->bssid.sa_data; - cfg_pmksa.pmkid = pmksa->pmkid; - - switch (pmksa->cmd) { - case IW_PMKSA_ADD: - if (!rdev->ops->set_pmksa) - return -EOPNOTSUPP; - - return rdev->ops->set_pmksa(&rdev->wiphy, dev, &cfg_pmksa); - - case IW_PMKSA_REMOVE: - if (!rdev->ops->del_pmksa) - return -EOPNOTSUPP; - - return rdev->ops->del_pmksa(&rdev->wiphy, dev, &cfg_pmksa); - - case IW_PMKSA_FLUSH: - if (!rdev->ops->flush_pmksa) - return -EOPNOTSUPP; - - return rdev->ops->flush_pmksa(&rdev->wiphy, dev); - - default: - return -EOPNOTSUPP; - } -} - -static const iw_handler cfg80211_handlers[] = { - [IW_IOCTL_IDX(SIOCGIWNAME)] = (iw_handler) cfg80211_wext_giwname, - [IW_IOCTL_IDX(SIOCSIWFREQ)] = (iw_handler) cfg80211_wext_siwfreq, - [IW_IOCTL_IDX(SIOCGIWFREQ)] = (iw_handler) cfg80211_wext_giwfreq, - [IW_IOCTL_IDX(SIOCSIWMODE)] = (iw_handler) cfg80211_wext_siwmode, - [IW_IOCTL_IDX(SIOCGIWMODE)] = (iw_handler) cfg80211_wext_giwmode, - [IW_IOCTL_IDX(SIOCGIWRANGE)] = (iw_handler) cfg80211_wext_giwrange, - [IW_IOCTL_IDX(SIOCSIWAP)] = (iw_handler) cfg80211_wext_siwap, - [IW_IOCTL_IDX(SIOCGIWAP)] = (iw_handler) cfg80211_wext_giwap, - [IW_IOCTL_IDX(SIOCSIWMLME)] = (iw_handler) cfg80211_wext_siwmlme, - [IW_IOCTL_IDX(SIOCSIWSCAN)] = (iw_handler) cfg80211_wext_siwscan, - [IW_IOCTL_IDX(SIOCGIWSCAN)] = (iw_handler) cfg80211_wext_giwscan, - [IW_IOCTL_IDX(SIOCSIWESSID)] = (iw_handler) cfg80211_wext_siwessid, - [IW_IOCTL_IDX(SIOCGIWESSID)] = (iw_handler) cfg80211_wext_giwessid, - [IW_IOCTL_IDX(SIOCSIWRATE)] = (iw_handler) cfg80211_wext_siwrate, - [IW_IOCTL_IDX(SIOCGIWRATE)] = (iw_handler) cfg80211_wext_giwrate, - [IW_IOCTL_IDX(SIOCSIWRTS)] = (iw_handler) cfg80211_wext_siwrts, - [IW_IOCTL_IDX(SIOCGIWRTS)] = (iw_handler) cfg80211_wext_giwrts, - [IW_IOCTL_IDX(SIOCSIWFRAG)] = (iw_handler) cfg80211_wext_siwfrag, - [IW_IOCTL_IDX(SIOCGIWFRAG)] = (iw_handler) cfg80211_wext_giwfrag, - [IW_IOCTL_IDX(SIOCSIWTXPOW)] = (iw_handler) cfg80211_wext_siwtxpower, - [IW_IOCTL_IDX(SIOCGIWTXPOW)] = (iw_handler) cfg80211_wext_giwtxpower, - [IW_IOCTL_IDX(SIOCSIWRETRY)] = (iw_handler) cfg80211_wext_siwretry, - [IW_IOCTL_IDX(SIOCGIWRETRY)] = (iw_handler) cfg80211_wext_giwretry, - [IW_IOCTL_IDX(SIOCSIWENCODE)] = (iw_handler) cfg80211_wext_siwencode, - [IW_IOCTL_IDX(SIOCGIWENCODE)] = (iw_handler) cfg80211_wext_giwencode, - [IW_IOCTL_IDX(SIOCSIWPOWER)] = (iw_handler) cfg80211_wext_siwpower, - [IW_IOCTL_IDX(SIOCGIWPOWER)] = (iw_handler) cfg80211_wext_giwpower, - [IW_IOCTL_IDX(SIOCSIWGENIE)] = (iw_handler) cfg80211_wext_siwgenie, - [IW_IOCTL_IDX(SIOCSIWAUTH)] = (iw_handler) cfg80211_wext_siwauth, - [IW_IOCTL_IDX(SIOCGIWAUTH)] = (iw_handler) cfg80211_wext_giwauth, - [IW_IOCTL_IDX(SIOCSIWENCODEEXT)]= (iw_handler) cfg80211_wext_siwencodeext, - [IW_IOCTL_IDX(SIOCSIWPMKSA)] = (iw_handler) cfg80211_wext_siwpmksa, -}; - -const struct iw_handler_def cfg80211_wext_handler = { - .num_standard = ARRAY_SIZE(cfg80211_handlers), - .standard = cfg80211_handlers, - .get_wireless_stats = cfg80211_wireless_stats, -}; diff --git a/net/wireless_ath/wext-compat.h b/net/wireless_ath/wext-compat.h deleted file mode 100755 index 5d766b0..0000000 --- a/net/wireless_ath/wext-compat.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef __WEXT_COMPAT -#define __WEXT_COMPAT - -#include <net/iw_handler.h> -#include <linux/wireless.h> - -int cfg80211_ibss_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra); -int cfg80211_ibss_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra); -int cfg80211_ibss_wext_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra); -int cfg80211_ibss_wext_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra); -int cfg80211_ibss_wext_siwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid); -int cfg80211_ibss_wext_giwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid); - -int cfg80211_mgd_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra); -int cfg80211_mgd_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra); -int cfg80211_mgd_wext_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra); -int cfg80211_mgd_wext_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra); -int cfg80211_mgd_wext_siwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid); -int cfg80211_mgd_wext_giwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid); - -int cfg80211_wext_siwmlme(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra); -int cfg80211_wext_siwgenie(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra); - - -int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq); - - -extern const struct iw_handler_def cfg80211_wext_handler; -#endif /* __WEXT_COMPAT */ diff --git a/net/wireless_ath/wext-core.c b/net/wireless_ath/wext-core.c deleted file mode 100755 index ae45584..0000000 --- a/net/wireless_ath/wext-core.c +++ /dev/null @@ -1,1123 +0,0 @@ -/* - * This file implement the Wireless Extensions core API. - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - * - * (As all part of the Linux kernel, this file is GPL) - */ -#include <linux/kernel.h> -#include <linux/netdevice.h> -#include <linux/rtnetlink.h> -#include <linux/slab.h> -#include <linux/wireless.h> -#include <linux/uaccess.h> -#include <linux/export.h> -#include <net/cfg80211.h> -#include <net/iw_handler.h> -#include <net/netlink.h> -#include <net/wext.h> -#include <net/net_namespace.h> - -typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, - unsigned int, struct iw_request_info *, - iw_handler); - - -/* - * Meta-data about all the standard Wireless Extension request we - * know about. - */ -static const struct iw_ioctl_description standard_ioctl[] = { - [IW_IOCTL_IDX(SIOCSIWCOMMIT)] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [IW_IOCTL_IDX(SIOCGIWNAME)] = { - .header_type = IW_HEADER_TYPE_CHAR, - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWNWID)] = { - .header_type = IW_HEADER_TYPE_PARAM, - .flags = IW_DESCR_FLAG_EVENT, - }, - [IW_IOCTL_IDX(SIOCGIWNWID)] = { - .header_type = IW_HEADER_TYPE_PARAM, - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWFREQ)] = { - .header_type = IW_HEADER_TYPE_FREQ, - .flags = IW_DESCR_FLAG_EVENT, - }, - [IW_IOCTL_IDX(SIOCGIWFREQ)] = { - .header_type = IW_HEADER_TYPE_FREQ, - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWMODE)] = { - .header_type = IW_HEADER_TYPE_UINT, - .flags = IW_DESCR_FLAG_EVENT, - }, - [IW_IOCTL_IDX(SIOCGIWMODE)] = { - .header_type = IW_HEADER_TYPE_UINT, - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWSENS)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWSENS)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWRANGE)] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [IW_IOCTL_IDX(SIOCGIWRANGE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_range), - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWPRIV)] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */ - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct iw_priv_args), - .max_tokens = 16, - .flags = IW_DESCR_FLAG_NOMAX, - }, - [IW_IOCTL_IDX(SIOCSIWSTATS)] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */ - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_statistics), - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWSPY)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct sockaddr), - .max_tokens = IW_MAX_SPY, - }, - [IW_IOCTL_IDX(SIOCGIWSPY)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct sockaddr) + - sizeof(struct iw_quality), - .max_tokens = IW_MAX_SPY, - }, - [IW_IOCTL_IDX(SIOCSIWTHRSPY)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct iw_thrspy), - .min_tokens = 1, - .max_tokens = 1, - }, - [IW_IOCTL_IDX(SIOCGIWTHRSPY)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct iw_thrspy), - .min_tokens = 1, - .max_tokens = 1, - }, - [IW_IOCTL_IDX(SIOCSIWAP)] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [IW_IOCTL_IDX(SIOCGIWAP)] = { - .header_type = IW_HEADER_TYPE_ADDR, - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWMLME)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = sizeof(struct iw_mlme), - .max_tokens = sizeof(struct iw_mlme), - }, - [IW_IOCTL_IDX(SIOCGIWAPLIST)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct sockaddr) + - sizeof(struct iw_quality), - .max_tokens = IW_MAX_AP, - .flags = IW_DESCR_FLAG_NOMAX, - }, - [IW_IOCTL_IDX(SIOCSIWSCAN)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = 0, - .max_tokens = sizeof(struct iw_scan_req), - }, - [IW_IOCTL_IDX(SIOCGIWSCAN)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_SCAN_MAX_DATA, - .flags = IW_DESCR_FLAG_NOMAX, - }, - [IW_IOCTL_IDX(SIOCSIWESSID)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ESSID_MAX_SIZE, - .flags = IW_DESCR_FLAG_EVENT, - }, - [IW_IOCTL_IDX(SIOCGIWESSID)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ESSID_MAX_SIZE, - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWNICKN)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ESSID_MAX_SIZE, - }, - [IW_IOCTL_IDX(SIOCGIWNICKN)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ESSID_MAX_SIZE, - }, - [IW_IOCTL_IDX(SIOCSIWRATE)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWRATE)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWRTS)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWRTS)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWFRAG)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWFRAG)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWTXPOW)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWTXPOW)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWRETRY)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWRETRY)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWENCODE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ENCODING_TOKEN_MAX, - .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, - }, - [IW_IOCTL_IDX(SIOCGIWENCODE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ENCODING_TOKEN_MAX, - .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, - }, - [IW_IOCTL_IDX(SIOCSIWPOWER)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWPOWER)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWGENIE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [IW_IOCTL_IDX(SIOCGIWGENIE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [IW_IOCTL_IDX(SIOCSIWAUTH)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWAUTH)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = sizeof(struct iw_encode_ext), - .max_tokens = sizeof(struct iw_encode_ext) + - IW_ENCODING_TOKEN_MAX, - }, - [IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = sizeof(struct iw_encode_ext), - .max_tokens = sizeof(struct iw_encode_ext) + - IW_ENCODING_TOKEN_MAX, - }, - [IW_IOCTL_IDX(SIOCSIWPMKSA)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = sizeof(struct iw_pmksa), - .max_tokens = sizeof(struct iw_pmksa), - }, -}; -static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl); - -/* - * Meta-data about all the additional standard Wireless Extension events - * we know about. - */ -static const struct iw_ioctl_description standard_event[] = { - [IW_EVENT_IDX(IWEVTXDROP)] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [IW_EVENT_IDX(IWEVQUAL)] = { - .header_type = IW_HEADER_TYPE_QUAL, - }, - [IW_EVENT_IDX(IWEVCUSTOM)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_CUSTOM_MAX, - }, - [IW_EVENT_IDX(IWEVREGISTERED)] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [IW_EVENT_IDX(IWEVEXPIRED)] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [IW_EVENT_IDX(IWEVGENIE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_michaelmicfailure), - }, - [IW_EVENT_IDX(IWEVASSOCREQIE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [IW_EVENT_IDX(IWEVASSOCRESPIE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [IW_EVENT_IDX(IWEVPMKIDCAND)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_pmkid_cand), - }, -}; -static const unsigned standard_event_num = ARRAY_SIZE(standard_event); - -/* Size (in bytes) of various events */ -static const int event_type_size[] = { - IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ - 0, - IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ - 0, - IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ - IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ - IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ - 0, - IW_EV_POINT_LEN, /* Without variable payload */ - IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ - IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ -}; - -#ifdef CONFIG_COMPAT -static const int compat_event_type_size[] = { - IW_EV_COMPAT_LCP_LEN, /* IW_HEADER_TYPE_NULL */ - 0, - IW_EV_COMPAT_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ - 0, - IW_EV_COMPAT_UINT_LEN, /* IW_HEADER_TYPE_UINT */ - IW_EV_COMPAT_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ - IW_EV_COMPAT_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ - 0, - IW_EV_COMPAT_POINT_LEN, /* Without variable payload */ - IW_EV_COMPAT_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ - IW_EV_COMPAT_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ -}; -#endif - - -/* IW event code */ - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) -static int __net_init wext_pernet_init(struct net *net) -{ - skb_queue_head_init(&net->wext_nlevents); - return 0; -} - -static void __net_exit wext_pernet_exit(struct net *net) -{ - skb_queue_purge(&net->wext_nlevents); -} - -static struct pernet_operations wext_pernet_ops = { - .init = wext_pernet_init, - .exit = wext_pernet_exit, -}; - -static int __init wireless_nlevent_init(void) -{ - return register_pernet_subsys(&wext_pernet_ops); -} - -subsys_initcall(wireless_nlevent_init); - -/* Process events generated by the wireless layer or the driver. */ -static void wireless_nlevent_process(struct work_struct *work) -{ - struct sk_buff *skb; - struct net *net; - - rtnl_lock(); - - for_each_net(net) { - while ((skb = skb_dequeue(&net->wext_nlevents))) - rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, - GFP_KERNEL); - } - - rtnl_unlock(); -} - -static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); - -#else -/* Older kernels get the old way of doing stuff*/ -static struct sk_buff_head wireless_nlevent_queue; - -static int __init wireless_nlevent_init(void) -{ - skb_queue_head_init(&wireless_nlevent_queue); - return 0; -} - -subsys_initcall(wireless_nlevent_init); - -static void wireless_nlevent_process(unsigned long data) -{ - struct sk_buff *skb; - while ((skb = skb_dequeue(&wireless_nlevent_queue))) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); -#else - rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); -#endif -} - -static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); - -#endif - -static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev, - struct sk_buff *skb) -{ - struct ifinfomsg *r; - struct nlmsghdr *nlh; - - nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0); - if (!nlh) - return NULL; - - r = nlmsg_data(nlh); - r->ifi_family = AF_UNSPEC; - r->__ifi_pad = 0; - r->ifi_type = dev->type; - r->ifi_index = dev->ifindex; - r->ifi_flags = dev_get_flags(dev); - r->ifi_change = 0; /* Wireless changes don't affect those flags */ - - NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name); - - return nlh; - nla_put_failure: - nlmsg_cancel(skb, nlh); - return NULL; -} - - -/* - * Main event dispatcher. Called from other parts and drivers. - * Send the event on the appropriate channels. - * May be called from interrupt context. - */ -void wireless_send_event(struct net_device * dev, - unsigned int cmd, - union iwreq_data * wrqu, - const char * extra) -{ - const struct iw_ioctl_description * descr = NULL; - int extra_len = 0; - struct iw_event *event; /* Mallocated whole event */ - int event_len; /* Its size */ - int hdr_len; /* Size of the event header */ - int wrqu_off = 0; /* Offset in wrqu */ - /* Don't "optimise" the following variable, it will crash */ - unsigned cmd_index; /* *MUST* be unsigned */ - struct sk_buff *skb; - struct nlmsghdr *nlh; - struct nlattr *nla; -#ifdef CONFIG_COMPAT - struct __compat_iw_event *compat_event; - struct compat_iw_point compat_wrqu; - struct sk_buff *compskb; -#endif - - /* - * Nothing in the kernel sends scan events with data, be safe. - * This is necessary because we cannot fix up scan event data - * for compat, due to being contained in 'extra', but normally - * applications are required to retrieve the scan data anyway - * and no data is included in the event, this codifies that - * practice. - */ - if (WARN_ON(cmd == SIOCGIWSCAN && extra)) - extra = NULL; - - /* Get the description of the Event */ - if (cmd <= SIOCIWLAST) { - cmd_index = IW_IOCTL_IDX(cmd); - if (cmd_index < standard_ioctl_num) - descr = &(standard_ioctl[cmd_index]); - } else { - cmd_index = IW_EVENT_IDX(cmd); - if (cmd_index < standard_event_num) - descr = &(standard_event[cmd_index]); - } - /* Don't accept unknown events */ - if (descr == NULL) { - /* Note : we don't return an error to the driver, because - * the driver would not know what to do about it. It can't - * return an error to the user, because the event is not - * initiated by a user request. - * The best the driver could do is to log an error message. - * We will do it ourselves instead... - */ - netdev_err(dev, "(WE) : Invalid/Unknown Wireless Event (0x%04X)\n", - cmd); - return; - } - - /* Check extra parameters and set extra_len */ - if (descr->header_type == IW_HEADER_TYPE_POINT) { - /* Check if number of token fits within bounds */ - if (wrqu->data.length > descr->max_tokens) { - netdev_err(dev, "(WE) : Wireless Event too big (%d)\n", - wrqu->data.length); - return; - } - if (wrqu->data.length < descr->min_tokens) { - netdev_err(dev, "(WE) : Wireless Event too small (%d)\n", - wrqu->data.length); - return; - } - /* Calculate extra_len - extra is NULL for restricted events */ - if (extra != NULL) - extra_len = wrqu->data.length * descr->token_size; - /* Always at an offset in wrqu */ - wrqu_off = IW_EV_POINT_OFF; - } - - /* Total length of the event */ - hdr_len = event_type_size[descr->header_type]; - event_len = hdr_len + extra_len; - - /* - * The problem for 64/32 bit. - * - * On 64-bit, a regular event is laid out as follows: - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | - * | event.len | event.cmd | p a d d i n g | - * | wrqu data ... (with the correct size) | - * - * This padding exists because we manipulate event->u, - * and 'event' is not packed. - * - * An iw_point event is laid out like this instead: - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | - * | event.len | event.cmd | p a d d i n g | - * | iwpnt.len | iwpnt.flg | p a d d i n g | - * | extra data ... - * - * The second padding exists because struct iw_point is extended, - * but this depends on the platform... - * - * On 32-bit, all the padding shouldn't be there. - */ - - skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); - if (!skb) - return; - - /* Send via the RtNetlink event channel */ - nlh = rtnetlink_ifinfo_prep(dev, skb); - if (WARN_ON(!nlh)) { - kfree_skb(skb); - return; - } - - /* Add the wireless events in the netlink packet */ - nla = nla_reserve(skb, IFLA_WIRELESS, event_len); - if (!nla) { - kfree_skb(skb); - return; - } - event = nla_data(nla); - - /* Fill event - first clear to avoid data leaking */ - memset(event, 0, hdr_len); - event->len = event_len; - event->cmd = cmd; - memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); - if (extra_len) - memcpy(((char *) event) + hdr_len, extra, extra_len); - - nlmsg_end(skb, nlh); -#ifdef CONFIG_COMPAT - hdr_len = compat_event_type_size[descr->header_type]; - event_len = hdr_len + extra_len; - - compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); - if (!compskb) { - kfree_skb(skb); - return; - } - - /* Send via the RtNetlink event channel */ - nlh = rtnetlink_ifinfo_prep(dev, compskb); - if (WARN_ON(!nlh)) { - kfree_skb(skb); - kfree_skb(compskb); - return; - } - - /* Add the wireless events in the netlink packet */ - nla = nla_reserve(compskb, IFLA_WIRELESS, event_len); - if (!nla) { - kfree_skb(skb); - kfree_skb(compskb); - return; - } - compat_event = nla_data(nla); - - compat_event->len = event_len; - compat_event->cmd = cmd; - if (descr->header_type == IW_HEADER_TYPE_POINT) { - compat_wrqu.length = wrqu->data.length; - compat_wrqu.flags = wrqu->data.flags; - memcpy(&compat_event->pointer, - ((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF, - hdr_len - IW_EV_COMPAT_LCP_LEN); - if (extra_len) - memcpy(((char *) compat_event) + hdr_len, - extra, extra_len); - } else { - /* extra_len must be zero, so no if (extra) needed */ - memcpy(&compat_event->pointer, wrqu, - hdr_len - IW_EV_COMPAT_LCP_LEN); - } - - nlmsg_end(compskb, nlh); - - skb_shinfo(skb)->frag_list = compskb; -#endif -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) - skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); - schedule_work(&wireless_nlevent_work); -#else - skb_queue_tail(&wireless_nlevent_queue, skb); - tasklet_schedule(&wireless_nlevent_tasklet); -#endif -} -EXPORT_SYMBOL(wireless_send_event); - - - -/* IW handlers */ - -struct iw_statistics *get_wireless_stats(struct net_device *dev) -{ -#ifdef CONFIG_WIRELESS_EXT - if ((dev->wireless_handlers != NULL) && - (dev->wireless_handlers->get_wireless_stats != NULL)) - return dev->wireless_handlers->get_wireless_stats(dev); -#endif - -#ifdef CONFIG_CFG80211_WEXT - if (dev->ieee80211_ptr && - dev->ieee80211_ptr->wiphy && - dev->ieee80211_ptr->wiphy->wext && - dev->ieee80211_ptr->wiphy->wext->get_wireless_stats) - return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev); -#endif - - /* not found */ - return NULL; -} - -static int iw_handler_get_iwstats(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra) -{ - /* Get stats from the driver */ - struct iw_statistics *stats; - - stats = get_wireless_stats(dev); - if (stats) { - /* Copy statistics to extra */ - memcpy(extra, stats, sizeof(struct iw_statistics)); - wrqu->data.length = sizeof(struct iw_statistics); - - /* Check if we need to clear the updated flag */ - if (wrqu->data.flags != 0) - stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; - return 0; - } else - return -EOPNOTSUPP; -} - -static iw_handler get_handler(struct net_device *dev, unsigned int cmd) -{ - /* Don't "optimise" the following variable, it will crash */ - unsigned int index; /* *MUST* be unsigned */ - const struct iw_handler_def *handlers = NULL; - -#ifdef CONFIG_CFG80211_WEXT - if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) - handlers = dev->ieee80211_ptr->wiphy->wext; -#endif -#ifdef CONFIG_WIRELESS_EXT - if (dev->wireless_handlers) - handlers = dev->wireless_handlers; -#endif - - if (!handlers) - return NULL; - - /* Try as a standard command */ - index = IW_IOCTL_IDX(cmd); - if (index < handlers->num_standard) - return handlers->standard[index]; - -#ifdef CONFIG_WEXT_PRIV - /* Try as a private command */ - index = cmd - SIOCIWFIRSTPRIV; - if (index < handlers->num_private) - return handlers->private[index]; -#endif - - /* Not found */ - return NULL; -} - -static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, - const struct iw_ioctl_description *descr, - iw_handler handler, struct net_device *dev, - struct iw_request_info *info) -{ - int err, extra_size, user_length = 0, essid_compat = 0; - char *extra; - - /* Calculate space needed by arguments. Always allocate - * for max space. - */ - extra_size = descr->max_tokens * descr->token_size; - - /* Check need for ESSID compatibility for WE < 21 */ - switch (cmd) { - case SIOCSIWESSID: - case SIOCGIWESSID: - case SIOCSIWNICKN: - case SIOCGIWNICKN: - if (iwp->length == descr->max_tokens + 1) - essid_compat = 1; - else if (IW_IS_SET(cmd) && (iwp->length != 0)) { - char essid[IW_ESSID_MAX_SIZE + 1]; - unsigned int len; - len = iwp->length * descr->token_size; - - if (len > IW_ESSID_MAX_SIZE) - return -EFAULT; - - err = copy_from_user(essid, iwp->pointer, len); - if (err) - return -EFAULT; - - if (essid[iwp->length - 1] == '\0') - essid_compat = 1; - } - break; - default: - break; - } - - iwp->length -= essid_compat; - - /* Check what user space is giving us */ - if (IW_IS_SET(cmd)) { - /* Check NULL pointer */ - if (!iwp->pointer && iwp->length != 0) - return -EFAULT; - /* Check if number of token fits within bounds */ - if (iwp->length > descr->max_tokens) - return -E2BIG; - if (iwp->length < descr->min_tokens) - return -EINVAL; - } else { - /* Check NULL pointer */ - if (!iwp->pointer) - return -EFAULT; - /* Save user space buffer size for checking */ - user_length = iwp->length; - - /* Don't check if user_length > max to allow forward - * compatibility. The test user_length < min is - * implied by the test at the end. - */ - - /* Support for very large requests */ - if ((descr->flags & IW_DESCR_FLAG_NOMAX) && - (user_length > descr->max_tokens)) { - /* Allow userspace to GET more than max so - * we can support any size GET requests. - * There is still a limit : -ENOMEM. - */ - extra_size = user_length * descr->token_size; - - /* Note : user_length is originally a __u16, - * and token_size is controlled by us, - * so extra_size won't get negative and - * won't overflow... - */ - } - } - - /* kzalloc() ensures NULL-termination for essid_compat. */ - extra = kzalloc(extra_size, GFP_KERNEL); - if (!extra) - return -ENOMEM; - - /* If it is a SET, get all the extra data in here */ - if (IW_IS_SET(cmd) && (iwp->length != 0)) { - if (copy_from_user(extra, iwp->pointer, - iwp->length * - descr->token_size)) { - err = -EFAULT; - goto out; - } - - if (cmd == SIOCSIWENCODEEXT) { - struct iw_encode_ext *ee = (void *) extra; - - if (iwp->length < sizeof(*ee) + ee->key_len) - return -EFAULT; - } - } - - if (IW_IS_GET(cmd) && !(descr->flags & IW_DESCR_FLAG_NOMAX)) { - /* - * If this is a GET, but not NOMAX, it means that the extra - * data is not bounded by userspace, but by max_tokens. Thus - * set the length to max_tokens. This matches the extra data - * allocation. - * The driver should fill it with the number of tokens it - * provided, and it may check iwp->length rather than having - * knowledge of max_tokens. If the driver doesn't change the - * iwp->length, this ioctl just copies back max_token tokens - * filled with zeroes. Hopefully the driver isn't claiming - * them to be valid data. - */ - iwp->length = descr->max_tokens; - } - - err = handler(dev, info, (union iwreq_data *) iwp, extra); - - iwp->length += essid_compat; - - /* If we have something to return to the user */ - if (!err && IW_IS_GET(cmd)) { - /* Check if there is enough buffer up there */ - if (user_length < iwp->length) { - err = -E2BIG; - goto out; - } - - if (copy_to_user(iwp->pointer, extra, - iwp->length * - descr->token_size)) { - err = -EFAULT; - goto out; - } - } - - /* Generate an event to notify listeners of the change */ - if ((descr->flags & IW_DESCR_FLAG_EVENT) && - ((err == 0) || (err == -EIWCOMMIT))) { - union iwreq_data *data = (union iwreq_data *) iwp; - - if (descr->flags & IW_DESCR_FLAG_RESTRICT) - /* If the event is restricted, don't - * export the payload. - */ - wireless_send_event(dev, cmd, data, NULL); - else - wireless_send_event(dev, cmd, data, extra); - } - -out: - kfree(extra); - return err; -} - -/* - * Call the commit handler in the driver - * (if exist and if conditions are right) - * - * Note : our current commit strategy is currently pretty dumb, - * but we will be able to improve on that... - * The goal is to try to agreagate as many changes as possible - * before doing the commit. Drivers that will define a commit handler - * are usually those that need a reset after changing parameters, so - * we want to minimise the number of reset. - * A cool idea is to use a timer : at each "set" command, we re-set the - * timer, when the timer eventually fires, we call the driver. - * Hopefully, more on that later. - * - * Also, I'm waiting to see how many people will complain about the - * netif_running(dev) test. I'm open on that one... - * Hopefully, the driver will remember to do a commit in "open()" ;-) - */ -int call_commit_handler(struct net_device *dev) -{ -#ifdef CONFIG_WIRELESS_EXT - if ((netif_running(dev)) && - (dev->wireless_handlers->standard[0] != NULL)) - /* Call the commit handler on the driver */ - return dev->wireless_handlers->standard[0](dev, NULL, - NULL, NULL); - else - return 0; /* Command completed successfully */ -#else - /* cfg80211 has no commit */ - return 0; -#endif -} - -/* - * Main IOCTl dispatcher. - * Check the type of IOCTL and call the appropriate wrapper... - */ -static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, - unsigned int cmd, - struct iw_request_info *info, - wext_ioctl_func standard, - wext_ioctl_func private) -{ - struct iwreq *iwr = (struct iwreq *) ifr; - struct net_device *dev; - iw_handler handler; - - /* Permissions are already checked in dev_ioctl() before calling us. - * The copy_to/from_user() of ifr is also dealt with in there */ - - /* Make sure the device exist */ - if ((dev = __dev_get_by_name(net, ifr->ifr_name)) == NULL) - return -ENODEV; - - /* A bunch of special cases, then the generic case... - * Note that 'cmd' is already filtered in dev_ioctl() with - * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ - if (cmd == SIOCGIWSTATS) - return standard(dev, iwr, cmd, info, - &iw_handler_get_iwstats); - -#ifdef CONFIG_WEXT_PRIV - if (cmd == SIOCGIWPRIV && dev->wireless_handlers) - return standard(dev, iwr, cmd, info, - iw_handler_get_private); -#endif - - /* Basic check */ - if (!netif_device_present(dev)) - return -ENODEV; - - /* New driver API : try to find the handler */ - handler = get_handler(dev, cmd); - if (handler) { - /* Standard and private are not the same */ - if (cmd < SIOCIWFIRSTPRIV) - return standard(dev, iwr, cmd, info, handler); - else if (private) - return private(dev, iwr, cmd, info, handler); - } - /* Old driver API : call driver ioctl handler */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) - if (dev->netdev_ops->ndo_do_ioctl) - return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd); -#else - if (dev->do_ioctl) - return dev->do_ioctl(dev, ifr, cmd); -#endif - return -EOPNOTSUPP; -} - -/* If command is `set a parameter', or `get the encoding parameters', - * check if the user has the right to do it. - */ -static int wext_permission_check(unsigned int cmd) -{ - if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || - cmd == SIOCGIWENCODEEXT) && - !capable(CAP_NET_ADMIN)) - return -EPERM; - - return 0; -} - -/* entry point from dev ioctl */ -static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr, - unsigned int cmd, struct iw_request_info *info, - wext_ioctl_func standard, - wext_ioctl_func private) -{ - int ret = wext_permission_check(cmd); - - if (ret) - return ret; - - dev_load(net, ifr->ifr_name); - rtnl_lock(); - ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private); - rtnl_unlock(); - - return ret; -} - -/* - * Wrapper to call a standard Wireless Extension handler. - * We do various checks and also take care of moving data between - * user space and kernel space. - */ -static int ioctl_standard_call(struct net_device * dev, - struct iwreq *iwr, - unsigned int cmd, - struct iw_request_info *info, - iw_handler handler) -{ - const struct iw_ioctl_description * descr; - int ret = -EINVAL; - - /* Get the description of the IOCTL */ - if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num) - return -EOPNOTSUPP; - descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]); - - /* Check if we have a pointer to user space data or not */ - if (descr->header_type != IW_HEADER_TYPE_POINT) { - - /* No extra arguments. Trivial to handle */ - ret = handler(dev, info, &(iwr->u), NULL); - - /* Generate an event to notify listeners of the change */ - if ((descr->flags & IW_DESCR_FLAG_EVENT) && - ((ret == 0) || (ret == -EIWCOMMIT))) - wireless_send_event(dev, cmd, &(iwr->u), NULL); - } else { - ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr, - handler, dev, info); - } - - /* Call commit handler if needed and defined */ - if (ret == -EIWCOMMIT) - ret = call_commit_handler(dev); - - /* Here, we will generate the appropriate event if needed */ - - return ret; -} - - -int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, - void __user *arg) -{ - struct iw_request_info info = { .cmd = cmd, .flags = 0 }; - int ret; - - ret = wext_ioctl_dispatch(net, ifr, cmd, &info, - ioctl_standard_call, - ioctl_private_call); - if (ret >= 0 && - IW_IS_GET(cmd) && - copy_to_user(arg, ifr, sizeof(struct iwreq))) - return -EFAULT; - - return ret; -} - -#ifdef CONFIG_COMPAT -static int compat_standard_call(struct net_device *dev, - struct iwreq *iwr, - unsigned int cmd, - struct iw_request_info *info, - iw_handler handler) -{ - const struct iw_ioctl_description *descr; - struct compat_iw_point *iwp_compat; - struct iw_point iwp; - int err; - - descr = standard_ioctl + IW_IOCTL_IDX(cmd); - - if (descr->header_type != IW_HEADER_TYPE_POINT) - return ioctl_standard_call(dev, iwr, cmd, info, handler); - - iwp_compat = (struct compat_iw_point *) &iwr->u.data; - iwp.pointer = compat_ptr(iwp_compat->pointer); - iwp.length = iwp_compat->length; - iwp.flags = iwp_compat->flags; - - err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info); - - iwp_compat->pointer = ptr_to_compat(iwp.pointer); - iwp_compat->length = iwp.length; - iwp_compat->flags = iwp.flags; - - return err; -} - -int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, - unsigned long arg) -{ - void __user *argp = (void __user *)arg; - struct iw_request_info info; - struct iwreq iwr; - char *colon; - int ret; - - if (copy_from_user(&iwr, argp, sizeof(struct iwreq))) - return -EFAULT; - - iwr.ifr_name[IFNAMSIZ-1] = 0; - colon = strchr(iwr.ifr_name, ':'); - if (colon) - *colon = 0; - - info.cmd = cmd; - info.flags = IW_REQUEST_FLAG_COMPAT; - - ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, &info, - compat_standard_call, - compat_private_call); - - if (ret >= 0 && - IW_IS_GET(cmd) && - copy_to_user(argp, &iwr, sizeof(struct iwreq))) - return -EFAULT; - - return ret; -} -#endif diff --git a/net/wireless_ath/wext-priv.c b/net/wireless_ath/wext-priv.c deleted file mode 100755 index 674d426..0000000 --- a/net/wireless_ath/wext-priv.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * This file implement the Wireless Extensions priv API. - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - * - * (As all part of the Linux kernel, this file is GPL) - */ -#include <linux/slab.h> -#include <linux/wireless.h> -#include <linux/netdevice.h> -#include <net/iw_handler.h> -#include <net/wext.h> - -int iw_handler_get_private(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra) -{ - /* Check if the driver has something to export */ - if ((dev->wireless_handlers->num_private_args == 0) || - (dev->wireless_handlers->private_args == NULL)) - return -EOPNOTSUPP; - - /* Check if there is enough buffer up there */ - if (wrqu->data.length < dev->wireless_handlers->num_private_args) { - /* User space can't know in advance how large the buffer - * needs to be. Give it a hint, so that we can support - * any size buffer we want somewhat efficiently... */ - wrqu->data.length = dev->wireless_handlers->num_private_args; - return -E2BIG; - } - - /* Set the number of available ioctls. */ - wrqu->data.length = dev->wireless_handlers->num_private_args; - - /* Copy structure to the user buffer. */ - memcpy(extra, dev->wireless_handlers->private_args, - sizeof(struct iw_priv_args) * wrqu->data.length); - - return 0; -} - -/* Size (in bytes) of the various private data types */ -static const char iw_priv_type_size[] = { - 0, /* IW_PRIV_TYPE_NONE */ - 1, /* IW_PRIV_TYPE_BYTE */ - 1, /* IW_PRIV_TYPE_CHAR */ - 0, /* Not defined */ - sizeof(__u32), /* IW_PRIV_TYPE_INT */ - sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ - sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ - 0, /* Not defined */ -}; - -static int get_priv_size(__u16 args) -{ - int num = args & IW_PRIV_SIZE_MASK; - int type = (args & IW_PRIV_TYPE_MASK) >> 12; - - return num * iw_priv_type_size[type]; -} - -static int adjust_priv_size(__u16 args, struct iw_point *iwp) -{ - int num = iwp->length; - int max = args & IW_PRIV_SIZE_MASK; - int type = (args & IW_PRIV_TYPE_MASK) >> 12; - - /* Make sure the driver doesn't goof up */ - if (max < num) - num = max; - - return num * iw_priv_type_size[type]; -} - -/* - * Wrapper to call a private Wireless Extension handler. - * We do various checks and also take care of moving data between - * user space and kernel space. - * It's not as nice and slimline as the standard wrapper. The cause - * is struct iw_priv_args, which was not really designed for the - * job we are going here. - * - * IMPORTANT : This function prevent to set and get data on the same - * IOCTL and enforce the SET/GET convention. Not doing it would be - * far too hairy... - * If you need to set and get data at the same time, please don't use - * a iw_handler but process it in your ioctl handler (i.e. use the - * old driver API). - */ -static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd, - const struct iw_priv_args **descrp) -{ - const struct iw_priv_args *descr; - int i, extra_size; - - descr = NULL; - for (i = 0; i < dev->wireless_handlers->num_private_args; i++) { - if (cmd == dev->wireless_handlers->private_args[i].cmd) { - descr = &dev->wireless_handlers->private_args[i]; - break; - } - } - - extra_size = 0; - if (descr) { - if (IW_IS_SET(cmd)) { - int offset = 0; /* For sub-ioctls */ - /* Check for sub-ioctl handler */ - if (descr->name[0] == '\0') - /* Reserve one int for sub-ioctl index */ - offset = sizeof(__u32); - - /* Size of set arguments */ - extra_size = get_priv_size(descr->set_args); - - /* Does it fits in iwr ? */ - if ((descr->set_args & IW_PRIV_SIZE_FIXED) && - ((extra_size + offset) <= IFNAMSIZ)) - extra_size = 0; - } else { - /* Size of get arguments */ - extra_size = get_priv_size(descr->get_args); - - /* Does it fits in iwr ? */ - if ((descr->get_args & IW_PRIV_SIZE_FIXED) && - (extra_size <= IFNAMSIZ)) - extra_size = 0; - } - } - *descrp = descr; - return extra_size; -} - -static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd, - const struct iw_priv_args *descr, - iw_handler handler, struct net_device *dev, - struct iw_request_info *info, int extra_size) -{ - char *extra; - int err; - - /* Check what user space is giving us */ - if (IW_IS_SET(cmd)) { - if (!iwp->pointer && iwp->length != 0) - return -EFAULT; - - if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK)) - return -E2BIG; - } else if (!iwp->pointer) - return -EFAULT; - - extra = kzalloc(extra_size, GFP_KERNEL); - if (!extra) - return -ENOMEM; - - /* If it is a SET, get all the extra data in here */ - if (IW_IS_SET(cmd) && (iwp->length != 0)) { - if (copy_from_user(extra, iwp->pointer, extra_size)) { - err = -EFAULT; - goto out; - } - } - - /* Call the handler */ - err = handler(dev, info, (union iwreq_data *) iwp, extra); - - /* If we have something to return to the user */ - if (!err && IW_IS_GET(cmd)) { - /* Adjust for the actual length if it's variable, - * avoid leaking kernel bits outside. - */ - if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) - extra_size = adjust_priv_size(descr->get_args, iwp); - - if (copy_to_user(iwp->pointer, extra, extra_size)) - err = -EFAULT; - } - -out: - kfree(extra); - return err; -} - -int ioctl_private_call(struct net_device *dev, struct iwreq *iwr, - unsigned int cmd, struct iw_request_info *info, - iw_handler handler) -{ - int extra_size = 0, ret = -EINVAL; - const struct iw_priv_args *descr; - - extra_size = get_priv_descr_and_size(dev, cmd, &descr); - - /* Check if we have a pointer to user space data or not. */ - if (extra_size == 0) { - /* No extra arguments. Trivial to handle */ - ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); - } else { - ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr, - handler, dev, info, extra_size); - } - - /* Call commit handler if needed and defined */ - if (ret == -EIWCOMMIT) - ret = call_commit_handler(dev); - - return ret; -} - -#ifdef CONFIG_COMPAT -int compat_private_call(struct net_device *dev, struct iwreq *iwr, - unsigned int cmd, struct iw_request_info *info, - iw_handler handler) -{ - const struct iw_priv_args *descr; - int ret, extra_size; - - extra_size = get_priv_descr_and_size(dev, cmd, &descr); - - /* Check if we have a pointer to user space data or not. */ - if (extra_size == 0) { - /* No extra arguments. Trivial to handle */ - ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); - } else { - struct compat_iw_point *iwp_compat; - struct iw_point iwp; - - iwp_compat = (struct compat_iw_point *) &iwr->u.data; - iwp.pointer = compat_ptr(iwp_compat->pointer); - iwp.length = iwp_compat->length; - iwp.flags = iwp_compat->flags; - - ret = ioctl_private_iw_point(&iwp, cmd, descr, - handler, dev, info, extra_size); - - iwp_compat->pointer = ptr_to_compat(iwp.pointer); - iwp_compat->length = iwp.length; - iwp_compat->flags = iwp.flags; - } - - /* Call commit handler if needed and defined */ - if (ret == -EIWCOMMIT) - ret = call_commit_handler(dev); - - return ret; -} -#endif diff --git a/net/wireless_ath/wext-proc.c b/net/wireless_ath/wext-proc.c deleted file mode 100755 index f2fbfa6..0000000 --- a/net/wireless_ath/wext-proc.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This file implement the Wireless Extensions proc API. - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. - * - * (As all part of the Linux kernel, this file is GPL) - */ - -/* - * The /proc/net/wireless file is a human readable user-space interface - * exporting various wireless specific statistics from the wireless devices. - * This is the most popular part of the Wireless Extensions ;-) - * - * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). - * The content of the file is basically the content of "struct iw_statistics". - */ - -#include <linux/module.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/wireless.h> -#include <linux/netdevice.h> -#include <linux/rtnetlink.h> -#include <net/iw_handler.h> -#include <net/wext.h> - - -static void wireless_seq_printf_stats(struct seq_file *seq, - struct net_device *dev) -{ - /* Get stats from the driver */ - struct iw_statistics *stats = get_wireless_stats(dev); - static struct iw_statistics nullstats = {}; - - /* show device if it's wireless regardless of current stats */ - if (!stats) { -#ifdef CONFIG_WIRELESS_EXT - if (dev->wireless_handlers) - stats = &nullstats; -#endif -#ifdef CONFIG_CFG80211 - if (dev->ieee80211_ptr) - stats = &nullstats; -#endif - } - - if (stats) { - seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " - "%6d %6d %6d\n", - dev->name, stats->status, stats->qual.qual, - stats->qual.updated & IW_QUAL_QUAL_UPDATED - ? '.' : ' ', - ((__s32) stats->qual.level) - - ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), - stats->qual.updated & IW_QUAL_LEVEL_UPDATED - ? '.' : ' ', - ((__s32) stats->qual.noise) - - ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), - stats->qual.updated & IW_QUAL_NOISE_UPDATED - ? '.' : ' ', - stats->discard.nwid, stats->discard.code, - stats->discard.fragment, stats->discard.retries, - stats->discard.misc, stats->miss.beacon); - - if (stats != &nullstats) - stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; - } -} - -/* ---------------------------------------------------------------- */ -/* - * Print info for /proc/net/wireless (print all entries) - */ -static int wireless_dev_seq_show(struct seq_file *seq, void *v) -{ - might_sleep(); - - if (v == SEQ_START_TOKEN) - seq_printf(seq, "Inter-| sta-| Quality | Discarded " - "packets | Missed | WE\n" - " face | tus | link level noise | nwid " - "crypt frag retry misc | beacon | %d\n", - WIRELESS_EXT); - else - wireless_seq_printf_stats(seq, v); - return 0; -} - -static void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct net *net = seq_file_net(seq); - loff_t off; - struct net_device *dev; - - rtnl_lock(); - if (!*pos) - return SEQ_START_TOKEN; - - off = 1; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - for_each_netdev(net, dev) -#else - for_each_netdev(net) -#endif - if (off++ == *pos) - return dev; - return NULL; -} - -static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct net *net = seq_file_net(seq); - - ++*pos; - - return v == SEQ_START_TOKEN ? - first_net_device(net) : next_net_device(v); -} - -static void wireless_dev_seq_stop(struct seq_file *seq, void *v) -{ - rtnl_unlock(); -} - -static const struct seq_operations wireless_seq_ops = { - .start = wireless_dev_seq_start, - .next = wireless_dev_seq_next, - .stop = wireless_dev_seq_stop, - .show = wireless_dev_seq_show, -}; - -static int seq_open_wireless(struct inode *inode, struct file *file) -{ - return seq_open_net(inode, file, &wireless_seq_ops, - sizeof(struct seq_net_private)); -} - -static const struct file_operations wireless_seq_fops = { - .owner = THIS_MODULE, - .open = seq_open_wireless, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_net, -}; - -int __net_init wext_proc_init(struct net *net) -{ - /* Create /proc/net/wireless entry */ - if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops)) - return -ENOMEM; - - return 0; -} - -void __net_exit wext_proc_exit(struct net *net) -{ - proc_net_remove(net, "wireless"); -} diff --git a/net/wireless_ath/wext-sme.c b/net/wireless_ath/wext-sme.c deleted file mode 100755 index be681b0..0000000 --- a/net/wireless_ath/wext-sme.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * cfg80211 wext compat for managed mode. - * - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - * Copyright (C) 2009 Intel Corporation. All rights reserved. - */ - -#include <linux/export.h> -#include <linux/etherdevice.h> -#include <linux/if_arp.h> -#include <linux/slab.h> -#include <net/cfg80211.h> -#include <net/cfg80211-wext.h> -#include "wext-compat.h" -#include "nl80211.h" - -int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) -{ - struct cfg80211_cached_keys *ck = NULL; - const u8 *prev_bssid = NULL; - int err, i; - - ASSERT_RDEV_LOCK(rdev); - ASSERT_WDEV_LOCK(wdev); - -#ifdef CONFIG_MACH_PX -#else - if (!netif_running(wdev->netdev)) - return 0; -#endif - - wdev->wext.connect.ie = wdev->wext.ie; - wdev->wext.connect.ie_len = wdev->wext.ie_len; - - if (wdev->wext.keys) { - wdev->wext.keys->def = wdev->wext.default_key; - wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key; - if (wdev->wext.default_key != -1) - wdev->wext.connect.privacy = true; - } - - if (!wdev->wext.connect.ssid_len) - return 0; - - if (wdev->wext.keys) { - ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); - if (!ck) - return -ENOMEM; - for (i = 0; i < 6; i++) - ck->params[i].key = ck->data[i]; - } - - if (wdev->wext.prev_bssid_valid) - prev_bssid = wdev->wext.prev_bssid; - - err = __cfg80211_connect(rdev, wdev->netdev, - &wdev->wext.connect, ck, prev_bssid); - if (err) - kfree(ck); - - return err; -} - -int cfg80211_mgd_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *wextfreq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan = NULL; - int err, freq; - - /* call only for station! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) - return -EINVAL; - - freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); - if (freq < 0) - return freq; - - if (freq) { - chan = ieee80211_get_channel(wdev->wiphy, freq); - if (!chan) - return -EINVAL; - if (chan->flags & IEEE80211_CHAN_DISABLED) - return -EINVAL; - } - - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - - if (wdev->sme_state != CFG80211_SME_IDLE) { - bool event = true; - - if (wdev->wext.connect.channel == chan) { - err = 0; - goto out; - } - - /* if SSID set, we'll try right again, avoid event */ - if (wdev->wext.connect.ssid_len) - event = false; - err = __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, event); - if (err) - goto out; - } - - - wdev->wext.connect.channel = chan; - - /* SSID is not set, we just want to switch channel */ - if (chan && !wdev->wext.connect.ssid_len) { - err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); - goto out; - } - - err = cfg80211_mgd_wext_connect(rdev, wdev); - out: - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); - return err; -} - -int cfg80211_mgd_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct ieee80211_channel *chan = NULL; - - /* call only for station! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) - return -EINVAL; - - wdev_lock(wdev); - if (wdev->current_bss) - chan = wdev->current_bss->pub.channel; - else if (wdev->wext.connect.channel) - chan = wdev->wext.connect.channel; - wdev_unlock(wdev); - - if (chan) { - freq->m = chan->center_freq; - freq->e = 6; - return 0; - } - - /* no channel if not joining */ - return -EINVAL; -} - -int cfg80211_mgd_wext_siwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - size_t len = data->length; - int err; - - /* call only for station! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) - return -EINVAL; - - if (!data->flags) - len = 0; - - /* iwconfig uses nul termination in SSID.. */ - if (len > 0 && ssid[len - 1] == '\0') - len--; - - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - - err = 0; - - if (wdev->sme_state != CFG80211_SME_IDLE) { - bool event = true; - - if (wdev->wext.connect.ssid && len && - len == wdev->wext.connect.ssid_len && - memcmp(wdev->wext.connect.ssid, ssid, len) == 0) - goto out; - - /* if SSID set now, we'll try to connect, avoid event */ - if (len) - event = false; - err = __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, event); - if (err) - goto out; - } - - wdev->wext.prev_bssid_valid = false; - wdev->wext.connect.ssid = wdev->wext.ssid; - memcpy(wdev->wext.ssid, ssid, len); - wdev->wext.connect.ssid_len = len; - - wdev->wext.connect.crypto.control_port = false; - wdev->wext.connect.crypto.control_port_ethertype = - cpu_to_be16(ETH_P_PAE); - - err = cfg80211_mgd_wext_connect(rdev, wdev); - out: - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); - return err; -} - -int cfg80211_mgd_wext_giwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - /* call only for station! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) - return -EINVAL; - - data->flags = 0; - - wdev_lock(wdev); - if (wdev->current_bss) { - const u8 *ie = ieee80211_bss_get_ie(&wdev->current_bss->pub, - WLAN_EID_SSID); - if (ie) { - data->flags = 1; - data->length = ie[1]; - memcpy(ssid, ie + 2, data->length); - } - } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { - data->flags = 1; - data->length = wdev->wext.connect.ssid_len; - memcpy(ssid, wdev->wext.connect.ssid, data->length); - } - wdev_unlock(wdev); - - return 0; -} - -int cfg80211_mgd_wext_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u8 *bssid = ap_addr->sa_data; - int err; - - /* call only for station! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) - return -EINVAL; - - if (ap_addr->sa_family != ARPHRD_ETHER) - return -EINVAL; - - /* automatic mode */ - if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) - bssid = NULL; - - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - - if (wdev->sme_state != CFG80211_SME_IDLE) { - err = 0; - /* both automatic */ - if (!bssid && !wdev->wext.connect.bssid) - goto out; - - /* fixed already - and no change */ - if (wdev->wext.connect.bssid && bssid && - compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) - goto out; - - err = __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, false); - if (err) - goto out; - } - - if (bssid) { - memcpy(wdev->wext.bssid, bssid, ETH_ALEN); - wdev->wext.connect.bssid = wdev->wext.bssid; - } else - wdev->wext.connect.bssid = NULL; - - err = cfg80211_mgd_wext_connect(rdev, wdev); - out: - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); - return err; -} - -int cfg80211_mgd_wext_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - /* call only for station! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) - return -EINVAL; - - ap_addr->sa_family = ARPHRD_ETHER; - - wdev_lock(wdev); - if (wdev->current_bss) - memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); - else - memset(ap_addr->sa_data, 0, ETH_ALEN); - wdev_unlock(wdev); - - return 0; -} - -int cfg80211_wext_siwgenie(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u8 *ie = extra; - int ie_len = data->length, err; - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - - if (!ie_len) - ie = NULL; - - wdev_lock(wdev); - - /* no change */ - err = 0; - if (wdev->wext.ie_len == ie_len && - memcmp(wdev->wext.ie, ie, ie_len) == 0) - goto out; - - if (ie_len) { - ie = kmemdup(extra, ie_len, GFP_KERNEL); - if (!ie) { - err = -ENOMEM; - goto out; - } - } else - ie = NULL; - - kfree(wdev->wext.ie); - wdev->wext.ie = ie; - wdev->wext.ie_len = ie_len; - - if (wdev->sme_state != CFG80211_SME_IDLE) { - err = __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, false); - if (err) - goto out; - } - - /* userspace better not think we'll reconnect */ - err = 0; - out: - wdev_unlock(wdev); - return err; -} - -int cfg80211_wext_siwmlme(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct iw_mlme *mlme = (struct iw_mlme *)extra; - struct cfg80211_registered_device *rdev; - int err; - - if (!wdev) - return -EOPNOTSUPP; - - rdev = wiphy_to_dev(wdev->wiphy); - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EINVAL; - - if (mlme->addr.sa_family != ARPHRD_ETHER) - return -EINVAL; - - wdev_lock(wdev); - switch (mlme->cmd) { - case IW_MLME_DEAUTH: - case IW_MLME_DISASSOC: - err = __cfg80211_disconnect(rdev, dev, mlme->reason_code, - true); - break; - default: - err = -EOPNOTSUPP; - break; - } - wdev_unlock(wdev); - - return err; -} diff --git a/net/wireless_ath/wext-spy.c b/net/wireless_ath/wext-spy.c deleted file mode 100755 index 9e0dc1b..0000000 --- a/net/wireless_ath/wext-spy.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * This file implement the Wireless Extensions spy API. - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. - * - * (As all part of the Linux kernel, this file is GPL) - */ - -#include <linux/wireless.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/export.h> -#include <net/iw_handler.h> -#include <net/arp.h> -#include <net/wext.h> - -static inline struct iw_spy_data *get_spydata(struct net_device *dev) -{ - /* This is the new way */ - if (dev->wireless_data) - return dev->wireless_data->spy_data; - return NULL; -} - -int iw_handler_set_spy(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra) -{ - struct iw_spy_data * spydata = get_spydata(dev); - struct sockaddr * address = (struct sockaddr *) extra; - - /* Make sure driver is not buggy or using the old API */ - if (!spydata) - return -EOPNOTSUPP; - - /* Disable spy collection while we copy the addresses. - * While we copy addresses, any call to wireless_spy_update() - * will NOP. This is OK, as anyway the addresses are changing. */ - spydata->spy_number = 0; - - /* We want to operate without locking, because wireless_spy_update() - * most likely will happen in the interrupt handler, and therefore - * have its own locking constraints and needs performance. - * The rtnl_lock() make sure we don't race with the other iw_handlers. - * This make sure wireless_spy_update() "see" that the spy list - * is temporarily disabled. */ - smp_wmb(); - - /* Are there are addresses to copy? */ - if (wrqu->data.length > 0) { - int i; - - /* Copy addresses */ - for (i = 0; i < wrqu->data.length; i++) - memcpy(spydata->spy_address[i], address[i].sa_data, - ETH_ALEN); - /* Reset stats */ - memset(spydata->spy_stat, 0, - sizeof(struct iw_quality) * IW_MAX_SPY); - } - - /* Make sure above is updated before re-enabling */ - smp_wmb(); - - /* Enable addresses */ - spydata->spy_number = wrqu->data.length; - - return 0; -} -EXPORT_SYMBOL(iw_handler_set_spy); - -int iw_handler_get_spy(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra) -{ - struct iw_spy_data * spydata = get_spydata(dev); - struct sockaddr * address = (struct sockaddr *) extra; - int i; - - /* Make sure driver is not buggy or using the old API */ - if (!spydata) - return -EOPNOTSUPP; - - wrqu->data.length = spydata->spy_number; - - /* Copy addresses. */ - for (i = 0; i < spydata->spy_number; i++) { - memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); - address[i].sa_family = AF_UNIX; - } - /* Copy stats to the user buffer (just after). */ - if (spydata->spy_number > 0) - memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number), - spydata->spy_stat, - sizeof(struct iw_quality) * spydata->spy_number); - /* Reset updated flags. */ - for (i = 0; i < spydata->spy_number; i++) - spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED; - return 0; -} -EXPORT_SYMBOL(iw_handler_get_spy); - -/*------------------------------------------------------------------*/ -/* - * Standard Wireless Handler : set spy threshold - */ -int iw_handler_set_thrspy(struct net_device * dev, - struct iw_request_info *info, - union iwreq_data * wrqu, - char * extra) -{ - struct iw_spy_data * spydata = get_spydata(dev); - struct iw_thrspy * threshold = (struct iw_thrspy *) extra; - - /* Make sure driver is not buggy or using the old API */ - if (!spydata) - return -EOPNOTSUPP; - - /* Just do it */ - memcpy(&(spydata->spy_thr_low), &(threshold->low), - 2 * sizeof(struct iw_quality)); - - /* Clear flag */ - memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); - - return 0; -} -EXPORT_SYMBOL(iw_handler_set_thrspy); - -/*------------------------------------------------------------------*/ -/* - * Standard Wireless Handler : get spy threshold - */ -int iw_handler_get_thrspy(struct net_device * dev, - struct iw_request_info *info, - union iwreq_data * wrqu, - char * extra) -{ - struct iw_spy_data * spydata = get_spydata(dev); - struct iw_thrspy * threshold = (struct iw_thrspy *) extra; - - /* Make sure driver is not buggy or using the old API */ - if (!spydata) - return -EOPNOTSUPP; - - /* Just do it */ - memcpy(&(threshold->low), &(spydata->spy_thr_low), - 2 * sizeof(struct iw_quality)); - - return 0; -} -EXPORT_SYMBOL(iw_handler_get_thrspy); - -/*------------------------------------------------------------------*/ -/* - * Prepare and send a Spy Threshold event - */ -static void iw_send_thrspy_event(struct net_device * dev, - struct iw_spy_data * spydata, - unsigned char * address, - struct iw_quality * wstats) -{ - union iwreq_data wrqu; - struct iw_thrspy threshold; - - /* Init */ - wrqu.data.length = 1; - wrqu.data.flags = 0; - /* Copy address */ - memcpy(threshold.addr.sa_data, address, ETH_ALEN); - threshold.addr.sa_family = ARPHRD_ETHER; - /* Copy stats */ - memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); - /* Copy also thresholds */ - memcpy(&(threshold.low), &(spydata->spy_thr_low), - 2 * sizeof(struct iw_quality)); - - /* Send event to user space */ - wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); -} - -/* ---------------------------------------------------------------- */ -/* - * Call for the driver to update the spy data. - * For now, the spy data is a simple array. As the size of the array is - * small, this is good enough. If we wanted to support larger number of - * spy addresses, we should use something more efficient... - */ -void wireless_spy_update(struct net_device * dev, - unsigned char * address, - struct iw_quality * wstats) -{ - struct iw_spy_data * spydata = get_spydata(dev); - int i; - int match = -1; - - /* Make sure driver is not buggy or using the old API */ - if (!spydata) - return; - - /* Update all records that match */ - for (i = 0; i < spydata->spy_number; i++) - if (!compare_ether_addr(address, spydata->spy_address[i])) { - memcpy(&(spydata->spy_stat[i]), wstats, - sizeof(struct iw_quality)); - match = i; - } - - /* Generate an event if we cross the spy threshold. - * To avoid event storms, we have a simple hysteresis : we generate - * event only when we go under the low threshold or above the - * high threshold. */ - if (match >= 0) { - if (spydata->spy_thr_under[match]) { - if (wstats->level > spydata->spy_thr_high.level) { - spydata->spy_thr_under[match] = 0; - iw_send_thrspy_event(dev, spydata, - address, wstats); - } - } else { - if (wstats->level < spydata->spy_thr_low.level) { - spydata->spy_thr_under[match] = 1; - iw_send_thrspy_event(dev, spydata, - address, wstats); - } - } - } -} -EXPORT_SYMBOL(wireless_spy_update); |