summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/atm/clip.c27
-rw-r--r--net/atm/pppoatm.c2
-rw-r--r--net/batman-adv/Makefile2
-rw-r--r--net/batman-adv/bat_algo.h (renamed from net/batman-adv/bat_ogm.h)20
-rw-r--r--net/batman-adv/bat_debugfs.c24
-rw-r--r--net/batman-adv/bat_debugfs.h2
-rw-r--r--net/batman-adv/bat_iv_ogm.c304
-rw-r--r--net/batman-adv/bat_sysfs.c31
-rw-r--r--net/batman-adv/bat_sysfs.h2
-rw-r--r--net/batman-adv/bitarray.c10
-rw-r--r--net/batman-adv/bitarray.h2
-rw-r--r--net/batman-adv/gateway_client.c37
-rw-r--r--net/batman-adv/gateway_client.h2
-rw-r--r--net/batman-adv/gateway_common.c14
-rw-r--r--net/batman-adv/gateway_common.h2
-rw-r--r--net/batman-adv/hard-interface.c66
-rw-r--r--net/batman-adv/hard-interface.h2
-rw-r--r--net/batman-adv/hash.c2
-rw-r--r--net/batman-adv/hash.h2
-rw-r--r--net/batman-adv/icmp_socket.c20
-rw-r--r--net/batman-adv/icmp_socket.h2
-rw-r--r--net/batman-adv/main.c113
-rw-r--r--net/batman-adv/main.h45
-rw-r--r--net/batman-adv/originator.c33
-rw-r--r--net/batman-adv/originator.h2
-rw-r--r--net/batman-adv/packet.h40
-rw-r--r--net/batman-adv/ring_buffer.c2
-rw-r--r--net/batman-adv/ring_buffer.h2
-rw-r--r--net/batman-adv/routing.c67
-rw-r--r--net/batman-adv/routing.h2
-rw-r--r--net/batman-adv/send.c15
-rw-r--r--net/batman-adv/send.h2
-rw-r--r--net/batman-adv/soft-interface.c44
-rw-r--r--net/batman-adv/soft-interface.h2
-rw-r--r--net/batman-adv/translation-table.c251
-rw-r--r--net/batman-adv/translation-table.h2
-rw-r--r--net/batman-adv/types.h23
-rw-r--r--net/batman-adv/unicast.c22
-rw-r--r--net/batman-adv/unicast.h2
-rw-r--r--net/batman-adv/vis.c19
-rw-r--r--net/batman-adv/vis.h5
-rw-r--r--net/bluetooth/Kconfig1
-rw-r--r--net/bluetooth/bnep/sock.c6
-rw-r--r--net/bluetooth/cmtp/sock.c6
-rw-r--r--net/bluetooth/hci_conn.c73
-rw-r--r--net/bluetooth/hci_core.c645
-rw-r--r--net/bluetooth/hci_event.c631
-rw-r--r--net/bluetooth/hci_sock.c469
-rw-r--r--net/bluetooth/hci_sysfs.c53
-rw-r--r--net/bluetooth/hidp/sock.c6
-rw-r--r--net/bluetooth/l2cap_core.c641
-rw-r--r--net/bluetooth/l2cap_sock.c54
-rw-r--r--net/bluetooth/lib.c27
-rw-r--r--net/bluetooth/mgmt.c2647
-rw-r--r--net/bluetooth/rfcomm/tty.c6
-rw-r--r--net/bluetooth/smp.c108
-rw-r--r--net/bridge/br_device.c5
-rw-r--r--net/bridge/br_multicast.c7
-rw-r--r--net/bridge/br_netfilter.c32
-rw-r--r--net/bridge/br_stp.c8
-rw-r--r--net/bridge/br_stp_if.c3
-rw-r--r--net/bridge/netfilter/ebtables.c26
-rw-r--r--net/caif/caif_dev.c2
-rw-r--r--net/caif/caif_socket.c123
-rw-r--r--net/caif/cfdbgl.c4
-rw-r--r--net/caif/cfdgml.c9
-rw-r--r--net/caif/cfmuxl.c12
-rw-r--r--net/caif/cfrfml.c25
-rw-r--r--net/caif/cfsrvl.c6
-rw-r--r--net/caif/cfutill.c5
-rw-r--r--net/caif/cfvidl.c6
-rw-r--r--net/caif/chnl_net.c24
-rw-r--r--net/ceph/ceph_common.c2
-rw-r--r--net/ceph/mon_client.c13
-rw-r--r--net/compat.c2
-rw-r--r--net/core/datagram.c26
-rw-r--r--net/core/dev.c125
-rw-r--r--net/core/ethtool.c4
-rw-r--r--net/core/iovec.c2
-rw-r--r--net/core/neighbour.c92
-rw-r--r--net/core/netpoll.c73
-rw-r--r--net/core/netprio_cgroup.c15
-rw-r--r--net/core/rtnetlink.c91
-rw-r--r--net/core/skbuff.c4
-rw-r--r--net/core/sock.c28
-rw-r--r--net/dccp/ccids/ccid3.c3
-rw-r--r--net/dccp/ipv4.c8
-rw-r--r--net/dccp/ipv6.c8
-rw-r--r--net/dccp/minisocks.c18
-rw-r--r--net/dccp/output.c10
-rw-r--r--net/decnet/dn_neigh.c24
-rw-r--r--net/decnet/dn_route.c3
-rw-r--r--net/ethernet/eth.c2
-rw-r--r--net/ieee802154/6lowpan.c16
-rw-r--r--net/ipv4/Kconfig2
-rw-r--r--net/ipv4/af_inet.c28
-rw-r--r--net/ipv4/ah4.c17
-rw-r--r--net/ipv4/arp.c3
-rw-r--r--net/ipv4/esp4.c10
-rw-r--r--net/ipv4/fib_frontend.c6
-rw-r--r--net/ipv4/fib_semantics.c2
-rw-r--r--net/ipv4/fib_trie.c5
-rw-r--r--net/ipv4/gre.c6
-rw-r--r--net/ipv4/icmp.c21
-rw-r--r--net/ipv4/inet_diag.c18
-rw-r--r--net/ipv4/inetpeer.c81
-rw-r--r--net/ipv4/ip_fragment.c11
-rw-r--r--net/ipv4/ip_gre.c39
-rw-r--r--net/ipv4/ip_input.c20
-rw-r--r--net/ipv4/ip_options.c6
-rw-r--r--net/ipv4/ip_sockglue.c37
-rw-r--r--net/ipv4/ipcomp.c8
-rw-r--r--net/ipv4/ipconfig.c110
-rw-r--r--net/ipv4/ipip.c7
-rw-r--r--net/ipv4/ipmr.c4
-rw-r--r--net/ipv4/netfilter/Kconfig9
-rw-r--r--net/ipv4/netfilter/Makefile1
-rw-r--r--net/ipv4/netfilter/ipt_LOG.c516
-rw-r--r--net/ipv4/netfilter/nf_conntrack_proto_icmp.c60
-rw-r--r--net/ipv4/netfilter/nf_nat_core.c8
-rw-r--r--net/ipv4/netfilter/nf_nat_h323.c14
-rw-r--r--net/ipv4/netfilter/nf_nat_sip.c7
-rw-r--r--net/ipv4/ping.c22
-rw-r--r--net/ipv4/proc.c1
-rw-r--r--net/ipv4/raw.c10
-rw-r--r--net/ipv4/route.c64
-rw-r--r--net/ipv4/sysctl_net_ipv4.c6
-rw-r--r--net/ipv4/tcp.c38
-rw-r--r--net/ipv4/tcp_cong.c9
-rw-r--r--net/ipv4/tcp_input.c62
-rw-r--r--net/ipv4/tcp_ipv4.c317
-rw-r--r--net/ipv4/tcp_minisocks.c12
-rw-r--r--net/ipv4/tcp_output.c4
-rw-r--r--net/ipv4/tcp_probe.c4
-rw-r--r--net/ipv4/tcp_timer.c19
-rw-r--r--net/ipv4/tunnel4.c8
-rw-r--r--net/ipv4/udp.c41
-rw-r--r--net/ipv4/udplite.c7
-rw-r--r--net/ipv4/xfrm4_mode_beet.c5
-rw-r--r--net/ipv4/xfrm4_mode_tunnel.c6
-rw-r--r--net/ipv4/xfrm4_tunnel.c16
-rw-r--r--net/ipv6/addrconf.c4
-rw-r--r--net/ipv6/af_inet6.c1
-rw-r--r--net/ipv6/anycast.c29
-rw-r--r--net/ipv6/datagram.c2
-rw-r--r--net/ipv6/icmp.c4
-rw-r--r--net/ipv6/ip6_fib.c19
-rw-r--r--net/ipv6/ip6_output.c10
-rw-r--r--net/ipv6/ip6mr.c4
-rw-r--r--net/ipv6/ipv6_sockglue.c38
-rw-r--r--net/ipv6/ndisc.c35
-rw-r--r--net/ipv6/netfilter/Kconfig9
-rw-r--r--net/ipv6/netfilter/Makefile1
-rw-r--r--net/ipv6/netfilter/ip6t_LOG.c527
-rw-r--r--net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c60
-rw-r--r--net/ipv6/raw.c2
-rw-r--r--net/ipv6/reassembly.c7
-rw-r--r--net/ipv6/route.c15
-rw-r--r--net/ipv6/sit.c20
-rw-r--r--net/ipv6/tcp_ipv6.c231
-rw-r--r--net/ipv6/udp.c7
-rw-r--r--net/ipv6/xfrm6_mode_beet.c6
-rw-r--r--net/ipv6/xfrm6_mode_tunnel.c6
-rw-r--r--net/ipv6/xfrm6_output.c2
-rw-r--r--net/irda/irnet/irnet.h2
-rw-r--r--net/iucv/af_iucv.c381
-rw-r--r--net/l2tp/l2tp_eth.c2
-rw-r--r--net/l2tp/l2tp_ppp.c2
-rw-r--r--net/netfilter/Kconfig30
-rw-r--r--net/netfilter/Makefile3
-rw-r--r--net/netfilter/ipset/ip_set_bitmap_ip.c4
-rw-r--r--net/netfilter/ipset/ip_set_bitmap_ipmac.c4
-rw-r--r--net/netfilter/ipset/ip_set_bitmap_port.c4
-rw-r--r--net/netfilter/ipset/ip_set_core.c26
-rw-r--r--net/netfilter/ipset/ip_set_getport.c4
-rw-r--r--net/netfilter/ipset/ip_set_hash_ip.c18
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipport.c10
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipportip.c10
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipportnet.c147
-rw-r--r--net/netfilter/ipset/ip_set_hash_net.c89
-rw-r--r--net/netfilter/ipset/ip_set_hash_netiface.c84
-rw-r--r--net/netfilter/ipset/ip_set_hash_netport.c150
-rw-r--r--net/netfilter/ipset/ip_set_list_set.c2
-rw-r--r--net/netfilter/ipvs/ip_vs_core.c2
-rw-r--r--net/netfilter/nf_conntrack_core.c80
-rw-r--r--net/netfilter/nf_conntrack_ecache.c55
-rw-r--r--net/netfilter/nf_conntrack_helper.c54
-rw-r--r--net/netfilter/nf_conntrack_netlink.c267
-rw-r--r--net/netfilter/nf_conntrack_proto_dccp.c86
-rw-r--r--net/netfilter/nf_conntrack_proto_generic.c77
-rw-r--r--net/netfilter/nf_conntrack_proto_gre.c82
-rw-r--r--net/netfilter/nf_conntrack_proto_sctp.c83
-rw-r--r--net/netfilter/nf_conntrack_proto_tcp.c168
-rw-r--r--net/netfilter/nf_conntrack_proto_udp.c106
-rw-r--r--net/netfilter/nf_conntrack_proto_udplite.c103
-rw-r--r--net/netfilter/nf_conntrack_timeout.c60
-rw-r--r--net/netfilter/nf_queue.c40
-rw-r--r--net/netfilter/nfnetlink_acct.c6
-rw-r--r--net/netfilter/nfnetlink_cttimeout.c429
-rw-r--r--net/netfilter/xt_CT.c220
-rw-r--r--net/netfilter/xt_LOG.c925
-rw-r--r--net/netfilter/xt_TEE.c5
-rw-r--r--net/netlink/af_netlink.c30
-rw-r--r--net/netlink/genetlink.c40
-rw-r--r--net/openvswitch/actions.c44
-rw-r--r--net/openvswitch/datapath.c3
-rw-r--r--net/openvswitch/vport-internal_dev.c3
-rw-r--r--net/packet/af_packet.c32
-rw-r--r--net/rxrpc/ar-key.c4
-rw-r--r--net/sched/Kconfig26
-rw-r--r--net/sched/Makefile1
-rw-r--r--net/sched/sch_choke.c3
-rw-r--r--net/sched/sch_netem.c9
-rw-r--r--net/sched/sch_plug.c233
-rw-r--r--net/sched/sch_sfb.c3
-rw-r--r--net/sched/sch_sfq.c5
-rw-r--r--net/sctp/socket.c24
-rw-r--r--net/socket.c36
-rw-r--r--net/sunrpc/auth_generic.c17
-rw-r--r--net/tipc/bcast.c336
-rw-r--r--net/tipc/bcast.h2
-rw-r--r--net/tipc/bearer.c5
-rw-r--r--net/tipc/config.c21
-rw-r--r--net/tipc/core.c10
-rw-r--r--net/tipc/core.h42
-rw-r--r--net/tipc/discover.c79
-rw-r--r--net/tipc/link.c299
-rw-r--r--net/tipc/log.c2
-rw-r--r--net/tipc/msg.c2
-rw-r--r--net/tipc/msg.h15
-rw-r--r--net/tipc/name_distr.c8
-rw-r--r--net/tipc/name_table.c48
-rw-r--r--net/tipc/name_table.h2
-rw-r--r--net/tipc/net.c11
-rw-r--r--net/tipc/node.c84
-rw-r--r--net/tipc/node.h37
-rw-r--r--net/tipc/port.c72
-rw-r--r--net/tipc/port.h42
-rw-r--r--net/tipc/socket.c11
-rw-r--r--net/tipc/subscr.c2
-rw-r--r--net/unix/af_unix.c52
-rw-r--r--net/unix/diag.c10
-rw-r--r--net/xfrm/xfrm_user.c9
243 files changed, 10047 insertions, 5740 deletions
diff --git a/net/atm/clip.c b/net/atm/clip.c
index c12c2582457..5de42ea309b 100644
--- a/net/atm/clip.c
+++ b/net/atm/clip.c
@@ -46,8 +46,8 @@
static struct net_device *clip_devs;
static struct atm_vcc *atmarpd;
-static struct neigh_table clip_tbl;
static struct timer_list idle_timer;
+static const struct neigh_ops clip_neigh_ops;
static int to_atmarpd(enum atmarp_ctrl_type type, int itf, __be32 ip)
{
@@ -123,6 +123,8 @@ static int neigh_check_cb(struct neighbour *n)
struct atmarp_entry *entry = neighbour_priv(n);
struct clip_vcc *cv;
+ if (n->ops != &clip_neigh_ops)
+ return 0;
for (cv = entry->vccs; cv; cv = cv->next) {
unsigned long exp = cv->last_use + cv->idle_timeout;
@@ -154,10 +156,10 @@ static int neigh_check_cb(struct neighbour *n)
static void idle_timer_check(unsigned long dummy)
{
- write_lock(&clip_tbl.lock);
- __neigh_for_each_release(&clip_tbl, neigh_check_cb);
+ write_lock(&arp_tbl.lock);
+ __neigh_for_each_release(&arp_tbl, neigh_check_cb);
mod_timer(&idle_timer, jiffies + CLIP_CHECK_INTERVAL * HZ);
- write_unlock(&clip_tbl.lock);
+ write_unlock(&arp_tbl.lock);
}
static int clip_arp_rcv(struct sk_buff *skb)
@@ -328,6 +330,8 @@ static netdev_tx_t clip_start_xmit(struct sk_buff *skb,
struct atmarp_entry *entry;
struct neighbour *n;
struct atm_vcc *vcc;
+ struct rtable *rt;
+ __be32 *daddr;
int old;
unsigned long flags;
@@ -338,7 +342,12 @@ static netdev_tx_t clip_start_xmit(struct sk_buff *skb,
dev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
- n = dst_get_neighbour_noref(dst);
+ rt = (struct rtable *) dst;
+ if (rt->rt_gateway)
+ daddr = &rt->rt_gateway;
+ else
+ daddr = &ip_hdr(skb)->daddr;
+ n = dst_neigh_lookup(dst, daddr);
if (!n) {
pr_err("NO NEIGHBOUR !\n");
dev_kfree_skb(skb);
@@ -358,7 +367,7 @@ static netdev_tx_t clip_start_xmit(struct sk_buff *skb,
dev_kfree_skb(skb);
dev->stats.tx_dropped++;
}
- return NETDEV_TX_OK;
+ goto out_release_neigh;
}
pr_debug("neigh %p, vccs %p\n", entry, entry->vccs);
ATM_SKB(skb)->vcc = vcc = entry->vccs->vcc;
@@ -377,14 +386,14 @@ static netdev_tx_t clip_start_xmit(struct sk_buff *skb,
old = xchg(&entry->vccs->xoff, 1); /* assume XOFF ... */
if (old) {
pr_warning("XOFF->XOFF transition\n");
- return NETDEV_TX_OK;
+ goto out_release_neigh;
}
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
vcc->send(vcc, skb);
if (atm_may_send(vcc, 0)) {
entry->vccs->xoff = 0;
- return NETDEV_TX_OK;
+ goto out_release_neigh;
}
spin_lock_irqsave(&clip_priv->xoff_lock, flags);
netif_stop_queue(dev); /* XOFF -> throttle immediately */
@@ -396,6 +405,8 @@ static netdev_tx_t clip_start_xmit(struct sk_buff *skb,
of the brief netif_stop_queue. If this isn't true or if it
changes, use netif_wake_queue instead. */
spin_unlock_irqrestore(&clip_priv->xoff_lock, flags);
+out_release_neigh:
+ neigh_release(n);
return NETDEV_TX_OK;
}
diff --git a/net/atm/pppoatm.c b/net/atm/pppoatm.c
index df35d9a3b5f..614d3fc47ed 100644
--- a/net/atm/pppoatm.c
+++ b/net/atm/pppoatm.c
@@ -44,7 +44,7 @@
#include <linux/atmdev.h>
#include <linux/capability.h>
#include <linux/ppp_defs.h>
-#include <linux/if_ppp.h>
+#include <linux/ppp-ioctl.h>
#include <linux/ppp_channel.h>
#include <linux/atmppp.h>
diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile
index ce686116649..4e392ebedb6 100644
--- a/net/batman-adv/Makefile
+++ b/net/batman-adv/Makefile
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+# Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
#
# Marek Lindner, Simon Wunderlich
#
diff --git a/net/batman-adv/bat_ogm.h b/net/batman-adv/bat_algo.h
index 69329c107e2..9852a688ba4 100644
--- a/net/batman-adv/bat_ogm.h
+++ b/net/batman-adv/bat_algo.h
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2011-2012 B.A.T.M.A.N. contributors:
*
- * Marek Lindner, Simon Wunderlich
+ * Marek Lindner
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
@@ -19,17 +19,9 @@
*
*/
-#ifndef _NET_BATMAN_ADV_OGM_H_
-#define _NET_BATMAN_ADV_OGM_H_
+#ifndef _NET_BATMAN_ADV_BAT_ALGO_H_
+#define _NET_BATMAN_ADV_BAT_ALGO_H_
-#include "main.h"
+int bat_iv_init(void);
-void bat_ogm_init(struct hard_iface *hard_iface);
-void bat_ogm_init_primary(struct hard_iface *hard_iface);
-void bat_ogm_update_mac(struct hard_iface *hard_iface);
-void bat_ogm_schedule(struct hard_iface *hard_iface, int tt_num_changes);
-void bat_ogm_emit(struct forw_packet *forw_packet);
-void bat_ogm_receive(const struct ethhdr *ethhdr, unsigned char *packet_buff,
- int packet_len, struct hard_iface *if_incoming);
-
-#endif /* _NET_BATMAN_ADV_OGM_H_ */
+#endif /* _NET_BATMAN_ADV_BAT_ALGO_H_ */
diff --git a/net/batman-adv/bat_debugfs.c b/net/batman-adv/bat_debugfs.c
index d0af9bf69e4..c3b0548b175 100644
--- a/net/batman-adv/bat_debugfs.c
+++ b/net/batman-adv/bat_debugfs.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2010-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
@@ -221,6 +221,11 @@ static void debug_log_cleanup(struct bat_priv *bat_priv)
}
#endif
+static int bat_algorithms_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, bat_algo_seq_print_text, NULL);
+}
+
static int originators_open(struct inode *inode, struct file *file)
{
struct net_device *net_dev = (struct net_device *)inode->i_private;
@@ -274,6 +279,7 @@ struct bat_debuginfo bat_debuginfo_##_name = { \
} \
};
+static BAT_DEBUGINFO(routing_algos, S_IRUGO, bat_algorithms_open);
static BAT_DEBUGINFO(originators, S_IRUGO, originators_open);
static BAT_DEBUGINFO(gateways, S_IRUGO, gateways_open);
static BAT_DEBUGINFO(softif_neigh, S_IRUGO, softif_neigh_open);
@@ -293,9 +299,25 @@ static struct bat_debuginfo *mesh_debuginfos[] = {
void debugfs_init(void)
{
+ struct bat_debuginfo *bat_debug;
+ struct dentry *file;
+
bat_debugfs = debugfs_create_dir(DEBUGFS_BAT_SUBDIR, NULL);
if (bat_debugfs == ERR_PTR(-ENODEV))
bat_debugfs = NULL;
+
+ if (!bat_debugfs)
+ goto out;
+
+ bat_debug = &bat_debuginfo_routing_algos;
+ file = debugfs_create_file(bat_debug->attr.name,
+ S_IFREG | bat_debug->attr.mode,
+ bat_debugfs, NULL, &bat_debug->fops);
+ if (!file)
+ pr_err("Can't add debugfs file: %s\n", bat_debug->attr.name);
+
+out:
+ return;
}
void debugfs_destroy(void)
diff --git a/net/batman-adv/bat_debugfs.h b/net/batman-adv/bat_debugfs.h
index bc9cda3f01e..d605c674642 100644
--- a/net/batman-adv/bat_debugfs.h
+++ b/net/batman-adv/bat_debugfs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2010-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index 3512e251545..a6d5d63fb6a 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -20,7 +20,6 @@
*/
#include "main.h"
-#include "bat_ogm.h"
#include "translation-table.h"
#include "ring_buffer.h"
#include "originator.h"
@@ -29,8 +28,9 @@
#include "gateway_client.h"
#include "hard-interface.h"
#include "send.h"
+#include "bat_algo.h"
-void bat_ogm_init(struct hard_iface *hard_iface)
+static void bat_iv_ogm_init(struct hard_iface *hard_iface)
{
struct batman_ogm_packet *batman_ogm_packet;
@@ -38,25 +38,25 @@ void bat_ogm_init(struct hard_iface *hard_iface)
hard_iface->packet_buff = kmalloc(hard_iface->packet_len, GFP_ATOMIC);
batman_ogm_packet = (struct batman_ogm_packet *)hard_iface->packet_buff;
- batman_ogm_packet->packet_type = BAT_OGM;
- batman_ogm_packet->version = COMPAT_VERSION;
+ batman_ogm_packet->header.packet_type = BAT_OGM;
+ batman_ogm_packet->header.version = COMPAT_VERSION;
+ batman_ogm_packet->header.ttl = 2;
batman_ogm_packet->flags = NO_FLAGS;
- batman_ogm_packet->ttl = 2;
batman_ogm_packet->tq = TQ_MAX_VALUE;
batman_ogm_packet->tt_num_changes = 0;
batman_ogm_packet->ttvn = 0;
}
-void bat_ogm_init_primary(struct hard_iface *hard_iface)
+static void bat_iv_ogm_init_primary(struct hard_iface *hard_iface)
{
struct batman_ogm_packet *batman_ogm_packet;
batman_ogm_packet = (struct batman_ogm_packet *)hard_iface->packet_buff;
batman_ogm_packet->flags = PRIMARIES_FIRST_HOP;
- batman_ogm_packet->ttl = TTL;
+ batman_ogm_packet->header.ttl = TTL;
}
-void bat_ogm_update_mac(struct hard_iface *hard_iface)
+static void bat_iv_ogm_update_mac(struct hard_iface *hard_iface)
{
struct batman_ogm_packet *batman_ogm_packet;
@@ -68,7 +68,7 @@ void bat_ogm_update_mac(struct hard_iface *hard_iface)
}
/* when do we schedule our own ogm to be sent */
-static unsigned long bat_ogm_emit_send_time(const struct bat_priv *bat_priv)
+static unsigned long bat_iv_ogm_emit_send_time(const struct bat_priv *bat_priv)
{
return jiffies + msecs_to_jiffies(
atomic_read(&bat_priv->orig_interval) -
@@ -76,7 +76,7 @@ static unsigned long bat_ogm_emit_send_time(const struct bat_priv *bat_priv)
}
/* when do we schedule a ogm packet to be sent */
-static unsigned long bat_ogm_fwd_send_time(void)
+static unsigned long bat_iv_ogm_fwd_send_time(void)
{
return jiffies + msecs_to_jiffies(random32() % (JITTER/2));
}
@@ -89,8 +89,8 @@ static uint8_t hop_penalty(uint8_t tq, const struct bat_priv *bat_priv)
}
/* is there another aggregated packet here? */
-static int bat_ogm_aggr_packet(int buff_pos, int packet_len,
- int tt_num_changes)
+static int bat_iv_ogm_aggr_packet(int buff_pos, int packet_len,
+ int tt_num_changes)
{
int next_buff_pos = buff_pos + BATMAN_OGM_LEN + tt_len(tt_num_changes);
@@ -99,8 +99,8 @@ static int bat_ogm_aggr_packet(int buff_pos, int packet_len,
}
/* send a batman ogm to a given interface */
-static void bat_ogm_send_to_if(struct forw_packet *forw_packet,
- struct hard_iface *hard_iface)
+static void bat_iv_ogm_send_to_if(struct forw_packet *forw_packet,
+ struct hard_iface *hard_iface)
{
struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
char *fwd_str;
@@ -117,8 +117,8 @@ static void bat_ogm_send_to_if(struct forw_packet *forw_packet,
batman_ogm_packet = (struct batman_ogm_packet *)forw_packet->skb->data;
/* adjust all flags and log packets */
- while (bat_ogm_aggr_packet(buff_pos, forw_packet->packet_len,
- batman_ogm_packet->tt_num_changes)) {
+ while (bat_iv_ogm_aggr_packet(buff_pos, forw_packet->packet_len,
+ batman_ogm_packet->tt_num_changes)) {
/* we might have aggregated direct link packets with an
* ordinary base packet */
@@ -132,12 +132,11 @@ static void bat_ogm_send_to_if(struct forw_packet *forw_packet,
"Sending own" :
"Forwarding"));
bat_dbg(DBG_BATMAN, bat_priv,
- "%s %spacket (originator %pM, seqno %d, TQ %d, TTL %d,"
- " IDF %s, ttvn %d) on interface %s [%pM]\n",
+ "%s %spacket (originator %pM, seqno %d, TQ %d, TTL %d, IDF %s, ttvn %d) on interface %s [%pM]\n",
fwd_str, (packet_num > 0 ? "aggregated " : ""),
batman_ogm_packet->orig,
ntohl(batman_ogm_packet->seqno),
- batman_ogm_packet->tq, batman_ogm_packet->ttl,
+ batman_ogm_packet->tq, batman_ogm_packet->header.ttl,
(batman_ogm_packet->flags & DIRECTLINK ?
"on" : "off"),
batman_ogm_packet->ttvn, hard_iface->net_dev->name,
@@ -157,7 +156,7 @@ static void bat_ogm_send_to_if(struct forw_packet *forw_packet,
}
/* send a batman ogm packet */
-void bat_ogm_emit(struct forw_packet *forw_packet)
+static void bat_iv_ogm_emit(struct forw_packet *forw_packet)
{
struct hard_iface *hard_iface;
struct net_device *soft_iface;
@@ -171,8 +170,7 @@ void bat_ogm_emit(struct forw_packet *forw_packet)
directlink = (batman_ogm_packet->flags & DIRECTLINK ? 1 : 0);
if (!forw_packet->if_incoming) {
- pr_err("Error - can't forward packet: incoming iface not "
- "specified\n");
+ pr_err("Error - can't forward packet: incoming iface not specified\n");
goto out;
}
@@ -188,17 +186,16 @@ void bat_ogm_emit(struct forw_packet *forw_packet)
/* multihomed peer assumed */
/* non-primary OGMs are only broadcasted on their interface */
- if ((directlink && (batman_ogm_packet->ttl == 1)) ||
+ if ((directlink && (batman_ogm_packet->header.ttl == 1)) ||
(forw_packet->own && (forw_packet->if_incoming != primary_if))) {
/* FIXME: what about aggregated packets ? */
bat_dbg(DBG_BATMAN, bat_priv,
- "%s packet (originator %pM, seqno %d, TTL %d) "
- "on interface %s [%pM]\n",
+ "%s packet (originator %pM, seqno %d, TTL %d) on interface %s [%pM]\n",
(forw_packet->own ? "Sending own" : "Forwarding"),
batman_ogm_packet->orig,
ntohl(batman_ogm_packet->seqno),
- batman_ogm_packet->ttl,
+ batman_ogm_packet->header.ttl,
forw_packet->if_incoming->net_dev->name,
forw_packet->if_incoming->net_dev->dev_addr);
@@ -216,7 +213,7 @@ void bat_ogm_emit(struct forw_packet *forw_packet)
if (hard_iface->soft_iface != soft_iface)
continue;
- bat_ogm_send_to_if(forw_packet, hard_iface);
+ bat_iv_ogm_send_to_if(forw_packet, hard_iface);
}
rcu_read_unlock();
@@ -226,13 +223,13 @@ out:
}
/* return true if new_packet can be aggregated with forw_packet */
-static bool bat_ogm_can_aggregate(const struct batman_ogm_packet
+static bool bat_iv_ogm_can_aggregate(const struct batman_ogm_packet
*new_batman_ogm_packet,
- struct bat_priv *bat_priv,
- int packet_len, unsigned long send_time,
- bool directlink,
- const struct hard_iface *if_incoming,
- const struct forw_packet *forw_packet)
+ struct bat_priv *bat_priv,
+ int packet_len, unsigned long send_time,
+ bool directlink,
+ const struct hard_iface *if_incoming,
+ const struct forw_packet *forw_packet)
{
struct batman_ogm_packet *batman_ogm_packet;
int aggregated_bytes = forw_packet->packet_len + packet_len;
@@ -272,7 +269,7 @@ static bool bat_ogm_can_aggregate(const struct batman_ogm_packet
* are flooded through the net */
if ((!directlink) &&
(!(batman_ogm_packet->flags & DIRECTLINK)) &&
- (batman_ogm_packet->ttl != 1) &&
+ (batman_ogm_packet->header.ttl != 1) &&
/* own packets originating non-primary
* interfaces leave only that interface */
@@ -285,7 +282,7 @@ static bool bat_ogm_can_aggregate(const struct batman_ogm_packet
/* if the incoming packet is sent via this one
* interface only - we still can aggregate */
if ((directlink) &&
- (new_batman_ogm_packet->ttl == 1) &&
+ (new_batman_ogm_packet->header.ttl == 1) &&
(forw_packet->if_incoming == if_incoming) &&
/* packets from direct neighbors or
@@ -306,11 +303,11 @@ out:
}
/* create a new aggregated packet and add this packet to it */
-static void bat_ogm_aggregate_new(const unsigned char *packet_buff,
- int packet_len, unsigned long send_time,
- bool direct_link,
- struct hard_iface *if_incoming,
- int own_packet)
+static void bat_iv_ogm_aggregate_new(const 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;
@@ -385,9 +382,9 @@ out:
}
/* aggregate a new packet into the existing ogm packet */
-static void bat_ogm_aggregate(struct forw_packet *forw_packet_aggr,
- const unsigned char *packet_buff,
- int packet_len, bool direct_link)
+static void bat_iv_ogm_aggregate(struct forw_packet *forw_packet_aggr,
+ const unsigned char *packet_buff,
+ int packet_len, bool direct_link)
{
unsigned char *skb_buff;
@@ -402,10 +399,10 @@ static void bat_ogm_aggregate(struct forw_packet *forw_packet_aggr,
(1 << forw_packet_aggr->num_packets);
}
-static void bat_ogm_queue_add(struct bat_priv *bat_priv,
- unsigned char *packet_buff,
- int packet_len, struct hard_iface *if_incoming,
- int own_packet, unsigned long send_time)
+static void bat_iv_ogm_queue_add(struct bat_priv *bat_priv,
+ unsigned char *packet_buff,
+ int packet_len, struct hard_iface *if_incoming,
+ int own_packet, unsigned long send_time)
{
/**
* _aggr -> pointer to the packet we want to aggregate with
@@ -425,11 +422,11 @@ static void bat_ogm_queue_add(struct bat_priv *bat_priv,
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 (bat_ogm_can_aggregate(batman_ogm_packet,
- bat_priv, packet_len,
- send_time, direct_link,
- if_incoming,
- forw_packet_pos)) {
+ if (bat_iv_ogm_can_aggregate(batman_ogm_packet,
+ bat_priv, packet_len,
+ send_time, direct_link,
+ if_incoming,
+ forw_packet_pos)) {
forw_packet_aggr = forw_packet_pos;
break;
}
@@ -451,27 +448,27 @@ static void bat_ogm_queue_add(struct bat_priv *bat_priv,
(atomic_read(&bat_priv->aggregated_ogms)))
send_time += msecs_to_jiffies(MAX_AGGREGATION_MS);
- bat_ogm_aggregate_new(packet_buff, packet_len,
- send_time, direct_link,
- if_incoming, own_packet);
+ bat_iv_ogm_aggregate_new(packet_buff, packet_len,
+ send_time, direct_link,
+ if_incoming, own_packet);
} else {
- bat_ogm_aggregate(forw_packet_aggr, packet_buff, packet_len,
- direct_link);
+ bat_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
+ packet_len, direct_link);
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
}
}
-static void bat_ogm_forward(struct orig_node *orig_node,
- const struct ethhdr *ethhdr,
- struct batman_ogm_packet *batman_ogm_packet,
- int directlink, struct hard_iface *if_incoming)
+static void bat_iv_ogm_forward(struct orig_node *orig_node,
+ const struct ethhdr *ethhdr,
+ struct batman_ogm_packet *batman_ogm_packet,
+ int directlink, struct hard_iface *if_incoming)
{
struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
struct neigh_node *router;
uint8_t in_tq, in_ttl, tq_avg = 0;
uint8_t tt_num_changes;
- if (batman_ogm_packet->ttl <= 1) {
+ if (batman_ogm_packet->header.ttl <= 1) {
bat_dbg(DBG_BATMAN, bat_priv, "ttl exceeded\n");
return;
}
@@ -479,10 +476,10 @@ static void bat_ogm_forward(struct orig_node *orig_node,
router = orig_node_get_router(orig_node);
in_tq = batman_ogm_packet->tq;
- in_ttl = batman_ogm_packet->ttl;
+ in_ttl = batman_ogm_packet->header.ttl;
tt_num_changes = batman_ogm_packet->tt_num_changes;
- batman_ogm_packet->ttl--;
+ batman_ogm_packet->header.ttl--;
memcpy(batman_ogm_packet->prev_sender, ethhdr->h_source, ETH_ALEN);
/* rebroadcast tq of our best ranking neighbor to ensure the rebroadcast
@@ -494,7 +491,8 @@ static void bat_ogm_forward(struct orig_node *orig_node,
batman_ogm_packet->tq = router->tq_avg;
if (router->last_ttl)
- batman_ogm_packet->ttl = router->last_ttl - 1;
+ batman_ogm_packet->header.ttl =
+ router->last_ttl - 1;
}
tq_avg = router->tq_avg;
@@ -507,10 +505,9 @@ static void bat_ogm_forward(struct orig_node *orig_node,
batman_ogm_packet->tq = hop_penalty(batman_ogm_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",
+ "Forwarding packet: tq_orig: %i, tq_avg: %i, tq_forw: %i, ttl_orig: %i, ttl_forw: %i\n",
in_tq, tq_avg, batman_ogm_packet->tq, in_ttl - 1,
- batman_ogm_packet->ttl);
+ batman_ogm_packet->header.ttl);
batman_ogm_packet->seqno = htonl(batman_ogm_packet->seqno);
batman_ogm_packet->tt_crc = htons(batman_ogm_packet->tt_crc);
@@ -522,12 +519,13 @@ static void bat_ogm_forward(struct orig_node *orig_node,
else
batman_ogm_packet->flags &= ~DIRECTLINK;
- bat_ogm_queue_add(bat_priv, (unsigned char *)batman_ogm_packet,
- BATMAN_OGM_LEN + tt_len(tt_num_changes),
- if_incoming, 0, bat_ogm_fwd_send_time());
+ bat_iv_ogm_queue_add(bat_priv, (unsigned char *)batman_ogm_packet,
+ BATMAN_OGM_LEN + tt_len(tt_num_changes),
+ if_incoming, 0, bat_iv_ogm_fwd_send_time());
}
-void bat_ogm_schedule(struct hard_iface *hard_iface, int tt_num_changes)
+static void bat_iv_ogm_schedule(struct hard_iface *hard_iface,
+ int tt_num_changes)
{
struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
struct batman_ogm_packet *batman_ogm_packet;
@@ -564,21 +562,22 @@ void bat_ogm_schedule(struct hard_iface *hard_iface, int tt_num_changes)
atomic_inc(&hard_iface->seqno);
slide_own_bcast_window(hard_iface);
- bat_ogm_queue_add(bat_priv, hard_iface->packet_buff,
- hard_iface->packet_len, hard_iface, 1,
- bat_ogm_emit_send_time(bat_priv));
+ bat_iv_ogm_queue_add(bat_priv, hard_iface->packet_buff,
+ hard_iface->packet_len, hard_iface, 1,
+ bat_iv_ogm_emit_send_time(bat_priv));
if (primary_if)
hardif_free_ref(primary_if);
}
-static void bat_ogm_orig_update(struct bat_priv *bat_priv,
- struct orig_node *orig_node,
- const struct ethhdr *ethhdr,
- const struct batman_ogm_packet
+static void bat_iv_ogm_orig_update(struct bat_priv *bat_priv,
+ struct orig_node *orig_node,
+ const struct ethhdr *ethhdr,
+ const struct batman_ogm_packet
*batman_ogm_packet,
- struct hard_iface *if_incoming,
- const unsigned char *tt_buff, int is_duplicate)
+ struct hard_iface *if_incoming,
+ const unsigned char *tt_buff,
+ int is_duplicate)
{
struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL;
struct neigh_node *router = NULL;
@@ -586,8 +585,8 @@ static void bat_ogm_orig_update(struct bat_priv *bat_priv,
struct hlist_node *node;
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");
+ 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,
@@ -642,8 +641,8 @@ static void bat_ogm_orig_update(struct bat_priv *bat_priv,
spin_unlock_bh(&neigh_node->tq_lock);
if (!is_duplicate) {
- orig_node->last_ttl = batman_ogm_packet->ttl;
- neigh_node->last_ttl = batman_ogm_packet->ttl;
+ orig_node->last_ttl = batman_ogm_packet->header.ttl;
+ neigh_node->last_ttl = batman_ogm_packet->header.ttl;
}
bonding_candidate_add(orig_node, neigh_node);
@@ -683,7 +682,7 @@ update_tt:
/* I have to check for transtable changes only if the OGM has been
* sent through a primary interface */
if (((batman_ogm_packet->orig != ethhdr->h_source) &&
- (batman_ogm_packet->ttl > 2)) ||
+ (batman_ogm_packet->header.ttl > 2)) ||
(batman_ogm_packet->flags & PRIMARIES_FIRST_HOP))
tt_update_orig(bat_priv, orig_node, tt_buff,
batman_ogm_packet->tt_num_changes,
@@ -713,10 +712,10 @@ out:
neigh_node_free_ref(router);
}
-static int bat_ogm_calc_tq(struct orig_node *orig_node,
- struct orig_node *orig_neigh_node,
- struct batman_ogm_packet *batman_ogm_packet,
- struct hard_iface *if_incoming)
+static int bat_iv_ogm_calc_tq(struct orig_node *orig_node,
+ struct orig_node *orig_neigh_node,
+ struct batman_ogm_packet *batman_ogm_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;
@@ -780,8 +779,7 @@ static int bat_ogm_calc_tq(struct orig_node *orig_node,
* information */
tq_own = (TQ_MAX_VALUE * total_count) / neigh_rq_count;
- /*
- * 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE this does
+ /* 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
@@ -799,10 +797,7 @@ static int bat_ogm_calc_tq(struct orig_node *orig_node,
(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",
+ "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_ogm_packet->tq);
@@ -825,10 +820,10 @@ out:
* -1 the packet is old and has been received while the seqno window
* was protected. Caller should drop it.
*/
-static int bat_ogm_update_seqnos(const struct ethhdr *ethhdr,
- const struct batman_ogm_packet
+static int bat_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
+ const struct batman_ogm_packet
*batman_ogm_packet,
- const struct hard_iface *if_incoming)
+ const struct hard_iface *if_incoming)
{
struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
struct orig_node *orig_node;
@@ -890,10 +885,10 @@ out:
return ret;
}
-static void bat_ogm_process(const struct ethhdr *ethhdr,
- struct batman_ogm_packet *batman_ogm_packet,
- const unsigned char *tt_buff,
- struct hard_iface *if_incoming)
+static void bat_iv_ogm_process(const struct ethhdr *ethhdr,
+ struct batman_ogm_packet *batman_ogm_packet,
+ const unsigned char *tt_buff,
+ struct hard_iface *if_incoming)
{
struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
struct hard_iface *hard_iface;
@@ -918,7 +913,7 @@ static void bat_ogm_process(const struct ethhdr *ethhdr,
* packet in an aggregation. Here we expect that the padding
* is always zero (or not 0x01)
*/
- if (batman_ogm_packet->packet_type != BAT_OGM)
+ if (batman_ogm_packet->header.packet_type != BAT_OGM)
return;
/* could be changed by schedule_own_packet() */
@@ -930,16 +925,14 @@ static void bat_ogm_process(const struct ethhdr *ethhdr,
batman_ogm_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, ttvn %u, "
- "crc %u, changes %u, td %d, TTL %d, V %d, IDF %d)\n",
+ "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %d, ttvn %u, crc %u, changes %u, td %d, TTL %d, V %d, IDF %d)\n",
ethhdr->h_source, if_incoming->net_dev->name,
if_incoming->net_dev->dev_addr, batman_ogm_packet->orig,
batman_ogm_packet->prev_sender, batman_ogm_packet->seqno,
batman_ogm_packet->ttvn, batman_ogm_packet->tt_crc,
batman_ogm_packet->tt_num_changes, batman_ogm_packet->tq,
- batman_ogm_packet->ttl, batman_ogm_packet->version,
- has_directlink_flag);
+ batman_ogm_packet->header.ttl,
+ batman_ogm_packet->header.version, has_directlink_flag);
rcu_read_lock();
list_for_each_entry_rcu(hard_iface, &hardif_list, list) {
@@ -966,25 +959,24 @@ static void bat_ogm_process(const struct ethhdr *ethhdr,
}
rcu_read_unlock();
- if (batman_ogm_packet->version != COMPAT_VERSION) {
+ if (batman_ogm_packet->header.version != COMPAT_VERSION) {
bat_dbg(DBG_BATMAN, bat_priv,
"Drop packet: incompatible batman version (%i)\n",
- batman_ogm_packet->version);
+ batman_ogm_packet->header.version);
return;
}
if (is_my_addr) {
bat_dbg(DBG_BATMAN, bat_priv,
- "Drop packet: received my own broadcast (sender: %pM"
- ")\n",
+ "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);
+ bat_dbg(DBG_BATMAN, bat_priv,
+ "Drop packet: ignoring all packets with broadcast source addr (sender: %pM)\n",
+ ethhdr->h_source);
return;
}
@@ -1014,16 +1006,16 @@ static void bat_ogm_process(const struct ethhdr *ethhdr,
spin_unlock_bh(&orig_neigh_node->ogm_cnt_lock);
}
- bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: "
- "originator packet from myself (via neighbor)\n");
+ 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);
+ "Drop packet: ignoring all rebroadcast echos (sender: %pM)\n",
+ ethhdr->h_source);
return;
}
@@ -1031,13 +1023,13 @@ static void bat_ogm_process(const struct ethhdr *ethhdr,
if (!orig_node)
return;
- is_duplicate = bat_ogm_update_seqnos(ethhdr, batman_ogm_packet,
- if_incoming);
+ is_duplicate = bat_iv_ogm_update_seqnos(ethhdr, batman_ogm_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);
+ "Drop packet: packet within seqno protection time (sender: %pM)\n",
+ ethhdr->h_source);
goto out;
}
@@ -1058,8 +1050,8 @@ static void bat_ogm_process(const struct ethhdr *ethhdr,
batman_ogm_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);
+ "Drop packet: ignoring all rebroadcast packets that may make me loop (sender: %pM)\n",
+ ethhdr->h_source);
goto out;
}
@@ -1081,8 +1073,8 @@ static void bat_ogm_process(const struct ethhdr *ethhdr,
goto out_neigh;
}
- is_bidirectional = bat_ogm_calc_tq(orig_node, orig_neigh_node,
- batman_ogm_packet, if_incoming);
+ is_bidirectional = bat_iv_ogm_calc_tq(orig_node, orig_neigh_node,
+ batman_ogm_packet, if_incoming);
bonding_save_primary(orig_node, orig_neigh_node, batman_ogm_packet);
@@ -1091,20 +1083,20 @@ static void bat_ogm_process(const struct ethhdr *ethhdr,
if (is_bidirectional &&
(!is_duplicate ||
((orig_node->last_real_seqno == batman_ogm_packet->seqno) &&
- (orig_node->last_ttl - 3 <= batman_ogm_packet->ttl))))
- bat_ogm_orig_update(bat_priv, orig_node, ethhdr,
- batman_ogm_packet, if_incoming,
- tt_buff, is_duplicate);
+ (orig_node->last_ttl - 3 <= batman_ogm_packet->header.ttl))))
+ bat_iv_ogm_orig_update(bat_priv, orig_node, ethhdr,
+ batman_ogm_packet, if_incoming,
+ tt_buff, is_duplicate);
/* is single hop (direct) neighbor */
if (is_single_hop_neigh) {
/* mark direct link on incoming interface */
- bat_ogm_forward(orig_node, ethhdr, batman_ogm_packet,
- 1, if_incoming);
+ bat_iv_ogm_forward(orig_node, ethhdr, batman_ogm_packet,
+ 1, if_incoming);
- bat_dbg(DBG_BATMAN, bat_priv, "Forwarding packet: "
- "rebroadcast neighbor packet with direct link flag\n");
+ bat_dbg(DBG_BATMAN, bat_priv,
+ "Forwarding packet: rebroadcast neighbor packet with direct link flag\n");
goto out_neigh;
}
@@ -1123,7 +1115,8 @@ static void bat_ogm_process(const struct ethhdr *ethhdr,
bat_dbg(DBG_BATMAN, bat_priv,
"Forwarding packet: rebroadcast originator packet\n");
- bat_ogm_forward(orig_node, ethhdr, batman_ogm_packet, 0, if_incoming);
+ bat_iv_ogm_forward(orig_node, ethhdr, batman_ogm_packet,
+ 0, if_incoming);
out_neigh:
if ((orig_neigh_node) && (!is_single_hop_neigh))
@@ -1139,13 +1132,17 @@ out:
orig_node_free_ref(orig_node);
}
-void bat_ogm_receive(const struct ethhdr *ethhdr, unsigned char *packet_buff,
- int packet_len, struct hard_iface *if_incoming)
+static void bat_iv_ogm_receive(struct hard_iface *if_incoming,
+ struct sk_buff *skb)
{
struct batman_ogm_packet *batman_ogm_packet;
- int buff_pos = 0;
- unsigned char *tt_buff;
+ struct ethhdr *ethhdr;
+ int buff_pos = 0, packet_len;
+ unsigned char *tt_buff, *packet_buff;
+ packet_len = skb_headlen(skb);
+ ethhdr = (struct ethhdr *)skb_mac_header(skb);
+ packet_buff = skb->data;
batman_ogm_packet = (struct batman_ogm_packet *)packet_buff;
/* unpack the aggregated packets and process them one by one */
@@ -1157,14 +1154,29 @@ void bat_ogm_receive(const struct ethhdr *ethhdr, unsigned char *packet_buff,
tt_buff = packet_buff + buff_pos + BATMAN_OGM_LEN;
- bat_ogm_process(ethhdr, batman_ogm_packet,
- tt_buff, if_incoming);
+ bat_iv_ogm_process(ethhdr, batman_ogm_packet,
+ tt_buff, if_incoming);
buff_pos += BATMAN_OGM_LEN +
tt_len(batman_ogm_packet->tt_num_changes);
batman_ogm_packet = (struct batman_ogm_packet *)
(packet_buff + buff_pos);
- } while (bat_ogm_aggr_packet(buff_pos, packet_len,
- batman_ogm_packet->tt_num_changes));
+ } while (bat_iv_ogm_aggr_packet(buff_pos, packet_len,
+ batman_ogm_packet->tt_num_changes));
+}
+
+static struct bat_algo_ops batman_iv __read_mostly = {
+ .name = "BATMAN IV",
+ .bat_ogm_init = bat_iv_ogm_init,
+ .bat_ogm_init_primary = bat_iv_ogm_init_primary,
+ .bat_ogm_update_mac = bat_iv_ogm_update_mac,
+ .bat_ogm_schedule = bat_iv_ogm_schedule,
+ .bat_ogm_emit = bat_iv_ogm_emit,
+ .bat_ogm_receive = bat_iv_ogm_receive,
+};
+
+int __init bat_iv_init(void)
+{
+ return bat_algo_register(&batman_iv);
}
diff --git a/net/batman-adv/bat_sysfs.c b/net/batman-adv/bat_sysfs.c
index c25492f7d66..68ff759fc30 100644
--- a/net/batman-adv/bat_sysfs.c
+++ b/net/batman-adv/bat_sysfs.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2010-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
@@ -255,8 +255,8 @@ static ssize_t store_vis_mode(struct kobject *kobj, struct attribute *attr,
buff[count - 1] = '\0';
bat_info(net_dev,
- "Invalid parameter for 'vis mode' setting received: "
- "%s\n", buff);
+ "Invalid parameter for 'vis mode' setting received: %s\n",
+ buff);
return -EINVAL;
}
@@ -272,6 +272,13 @@ static ssize_t store_vis_mode(struct kobject *kobj, struct attribute *attr,
return count;
}
+static ssize_t show_bat_algo(struct kobject *kobj, struct attribute *attr,
+ char *buff)
+{
+ struct bat_priv *bat_priv = kobj_to_batpriv(kobj);
+ return sprintf(buff, "%s\n", bat_priv->bat_algo_ops->name);
+}
+
static void post_gw_deselect(struct net_device *net_dev)
{
struct bat_priv *bat_priv = netdev_priv(net_dev);
@@ -314,17 +321,17 @@ static ssize_t store_gw_mode(struct kobject *kobj, struct attribute *attr,
gw_mode_tmp = GW_MODE_OFF;
if (strncmp(buff, GW_MODE_CLIENT_NAME,
- strlen(GW_MODE_CLIENT_NAME)) == 0)
+ strlen(GW_MODE_CLIENT_NAME)) == 0)
gw_mode_tmp = GW_MODE_CLIENT;
if (strncmp(buff, GW_MODE_SERVER_NAME,
- strlen(GW_MODE_SERVER_NAME)) == 0)
+ strlen(GW_MODE_SERVER_NAME)) == 0)
gw_mode_tmp = GW_MODE_SERVER;
if (gw_mode_tmp < 0) {
bat_info(net_dev,
- "Invalid parameter for 'gw mode' setting received: "
- "%s\n", buff);
+ "Invalid parameter for 'gw mode' setting received: %s\n",
+ buff);
return -EINVAL;
}
@@ -382,6 +389,7 @@ 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(routing_algo, S_IRUGO, show_bat_algo, NULL);
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);
BAT_ATTR_UINT(hop_penalty, S_IRUGO | S_IWUSR, 0, TQ_MAX_VALUE, NULL);
@@ -399,6 +407,7 @@ static struct bat_attribute *mesh_attrs[] = {
&bat_attr_fragmentation,
&bat_attr_ap_isolation,
&bat_attr_vis_mode,
+ &bat_attr_routing_algo,
&bat_attr_gw_mode,
&bat_attr_orig_interval,
&bat_attr_hop_penalty,
@@ -493,8 +502,8 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr,
buff[count - 1] = '\0';
if (strlen(buff) >= IFNAMSIZ) {
- pr_err("Invalid parameter for 'mesh_iface' setting received: "
- "interface name too long '%s'\n", buff);
+ pr_err("Invalid parameter for 'mesh_iface' setting received: interface name too long '%s'\n",
+ buff);
hardif_free_ref(hard_iface);
return -EINVAL;
}
@@ -668,8 +677,8 @@ out:
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",
+ 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 a3f75a723c5..fece77ae586 100644
--- a/net/batman-adv/bat_sysfs.h
+++ b/net/batman-adv/bat_sysfs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2010-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
diff --git a/net/batman-adv/bitarray.c b/net/batman-adv/bitarray.c
index 9bc63b209b3..6d0aa216b23 100644
--- a/net/batman-adv/bitarray.c
+++ b/net/batman-adv/bitarray.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2006-2012 B.A.T.M.A.N. contributors:
*
* Simon Wunderlich, Marek Lindner
*
@@ -154,8 +154,8 @@ int bit_get_packet(void *priv, unsigned long *seq_bits,
/* sequence number is much newer, probably missed a lot of packets */
- if ((seq_num_diff >= TQ_LOCAL_WINDOW_SIZE)
- && (seq_num_diff < EXPECTED_SEQNO_RANGE)) {
+ if ((seq_num_diff >= TQ_LOCAL_WINDOW_SIZE) &&
+ (seq_num_diff < EXPECTED_SEQNO_RANGE)) {
bat_dbg(DBG_BATMAN, bat_priv,
"We missed a lot of packets (%i) !\n",
seq_num_diff - 1);
@@ -170,8 +170,8 @@ int bit_get_packet(void *priv, unsigned long *seq_bits,
* packet should be dropped without calling this function if the
* seqno window is protected. */
- if ((seq_num_diff <= -TQ_LOCAL_WINDOW_SIZE)
- || (seq_num_diff >= EXPECTED_SEQNO_RANGE)) {
+ if ((seq_num_diff <= -TQ_LOCAL_WINDOW_SIZE) ||
+ (seq_num_diff >= EXPECTED_SEQNO_RANGE)) {
bat_dbg(DBG_BATMAN, bat_priv,
"Other host probably restarted!\n");
diff --git a/net/batman-adv/bitarray.h b/net/batman-adv/bitarray.h
index 9c04422aeb0..c6135728a68 100644
--- a/net/batman-adv/bitarray.h
+++ b/net/batman-adv/bitarray.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2006-2012 B.A.T.M.A.N. contributors:
*
* Simon Wunderlich, Marek Lindner
*
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c
index 24403a7350f..6f9b9b78f77 100644
--- a/net/batman-adv/gateway_client.c
+++ b/net/batman-adv/gateway_client.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2009-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
@@ -224,16 +224,13 @@ void gw_election(struct bat_priv *bat_priv)
} 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,
+ 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,
+ "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);
}
@@ -287,8 +284,7 @@ void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node)
goto out;
bat_dbg(DBG_BATMAN, bat_priv,
- "Restarting gateway selection: better gateway found (tq curr: "
- "%i, tq new: %i)\n",
+ "Restarting gateway selection: better gateway found (tq curr: %i, tq new: %i)\n",
gw_tq_avg, orig_tq_avg);
deselect:
@@ -352,8 +348,7 @@ void gw_node_update(struct bat_priv *bat_priv,
continue;
bat_dbg(DBG_BATMAN, bat_priv,
- "Gateway class of originator %pM changed from "
- "%i to %i\n",
+ "Gateway class of originator %pM changed from %i to %i\n",
orig_node->orig, gw_node->orig_node->gw_flags,
new_gwflags);
@@ -396,7 +391,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;
+ unsigned long timeout = msecs_to_jiffies(2 * PURGE_TIMEOUT);
int do_deselect = 0;
curr_gw = gw_get_selected_gw_node(bat_priv);
@@ -474,23 +469,23 @@ int gw_client_seq_print_text(struct seq_file *seq, void *offset)
primary_if = primary_if_get_selected(bat_priv);
if (!primary_if) {
- ret = seq_printf(seq, "BATMAN mesh %s disabled - please "
- "specify interfaces to enable it\n",
+ ret = seq_printf(seq,
+ "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
net_dev->name);
goto out;
}
if (primary_if->if_status != IF_ACTIVE) {
- ret = seq_printf(seq, "BATMAN mesh %s disabled - "
- "primary interface not active\n",
+ ret = seq_printf(seq,
+ "BATMAN mesh %s disabled - primary interface not active\n",
net_dev->name);
goto out;
}
- seq_printf(seq, " %-12s (%s/%i) %17s [%10s]: gw_class ... "
- "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
- "Gateway", "#", TQ_MAX_VALUE, "Nexthop",
- "outgoingIF", SOURCE_VERSION, primary_if->net_dev->name,
+ seq_printf(seq,
+ " %-12s (%s/%i) %17s [%10s]: gw_class ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
+ "Gateway", "#", TQ_MAX_VALUE, "Nexthop", "outgoingIF",
+ SOURCE_VERSION, primary_if->net_dev->name,
primary_if->net_dev->dev_addr, net_dev->name);
rcu_read_lock();
@@ -629,7 +624,7 @@ bool gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len)
/* check for bootp port */
if ((ntohs(ethhdr->h_proto) == ETH_P_IP) &&
- (ntohs(udphdr->dest) != 67))
+ (ntohs(udphdr->dest) != 67))
return false;
if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) &&
diff --git a/net/batman-adv/gateway_client.h b/net/batman-adv/gateway_client.h
index e1edba08eb1..bf56a5aea10 100644
--- a/net/batman-adv/gateway_client.h
+++ b/net/batman-adv/gateway_client.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2009-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c
index c4ac7b0a2a6..ca57ac7d73b 100644
--- a/net/batman-adv/gateway_common.c
+++ b/net/batman-adv/gateway_common.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2009-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
@@ -93,7 +93,7 @@ static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff,
multi = 1024;
if ((strnicmp(tmp_ptr, "kbit", 4) == 0) ||
- (multi > 1))
+ (multi > 1))
*tmp_ptr = '\0';
}
@@ -118,15 +118,15 @@ static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff,
multi = 1024;
if ((strnicmp(tmp_ptr, "kbit", 4) == 0) ||
- (multi > 1))
+ (multi > 1))
*tmp_ptr = '\0';
}
ret = kstrtol(slash_ptr + 1, 10, &lup);
if (ret) {
bat_err(net_dev,
- "Upload speed of gateway mode invalid: "
- "%s\n", slash_ptr + 1);
+ "Upload speed of gateway mode invalid: %s\n",
+ slash_ptr + 1);
return false;
}
@@ -163,8 +163,8 @@ ssize_t gw_bandwidth_set(struct net_device *net_dev, char *buff, size_t count)
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: %d%s/%d%s)\n",
+ bat_info(net_dev,
+ "Changing gateway bandwidth from: '%i' to: '%ld' (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/gateway_common.h b/net/batman-adv/gateway_common.h
index 55e527a489f..b8fb11c4f92 100644
--- a/net/batman-adv/gateway_common.h
+++ b/net/batman-adv/gateway_common.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2009-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 7704df468e0..377897701a8 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -28,7 +28,6 @@
#include "bat_sysfs.h"
#include "originator.h"
#include "hash.h"
-#include "bat_ogm.h"
#include <linux/if_arp.h>
@@ -147,7 +146,7 @@ static void primary_if_select(struct bat_priv *bat_priv,
if (!new_hard_iface)
return;
- bat_ogm_init_primary(new_hard_iface);
+ bat_priv->bat_algo_ops->bat_ogm_init_primary(new_hard_iface);
primary_if_update_addr(bat_priv);
}
@@ -176,11 +175,9 @@ static void check_known_mac_addr(const struct net_device *net_dev)
net_dev->dev_addr))
continue;
- pr_warning("The newly added mac address (%pM) already exists "
- "on: %s\n", net_dev->dev_addr,
- hard_iface->net_dev->name);
- pr_warning("It is strongly recommended to keep mac addresses "
- "unique to avoid problems!\n");
+ pr_warning("The newly added mac address (%pM) already exists on: %s\n",
+ net_dev->dev_addr, hard_iface->net_dev->name);
+ pr_warning("It is strongly recommended to keep mac addresses unique to avoid problems!\n");
}
rcu_read_unlock();
}
@@ -233,7 +230,7 @@ static void hardif_activate_interface(struct hard_iface *hard_iface)
bat_priv = netdev_priv(hard_iface->soft_iface);
- bat_ogm_update_mac(hard_iface);
+ bat_priv->bat_algo_ops->bat_ogm_update_mac(hard_iface);
hard_iface->if_status = IF_TO_BE_ACTIVATED;
/**
@@ -281,6 +278,11 @@ int hardif_enable_interface(struct hard_iface *hard_iface,
if (!atomic_inc_not_zero(&hard_iface->refcount))
goto out;
+ /* hard-interface is part of a bridge */
+ if (hard_iface->net_dev->priv_flags & IFF_BRIDGE_PORT)
+ pr_err("You are about to enable batman-adv on '%s' which already is part of a bridge. Unless you know exactly what you are doing this is probably wrong and won't work the way you think it would.\n",
+ hard_iface->net_dev->name);
+
soft_iface = dev_get_by_name(&init_net, iface_name);
if (!soft_iface) {
@@ -296,8 +298,7 @@ int hardif_enable_interface(struct hard_iface *hard_iface,
}
if (!softif_is_valid(soft_iface)) {
- pr_err("Can't create batman mesh interface %s: "
- "already exists as regular interface\n",
+ pr_err("Can't create batman mesh interface %s: already exists as regular interface\n",
soft_iface->name);
dev_put(soft_iface);
ret = -EINVAL;
@@ -307,11 +308,12 @@ int hardif_enable_interface(struct hard_iface *hard_iface,
hard_iface->soft_iface = soft_iface;
bat_priv = netdev_priv(hard_iface->soft_iface);
- bat_ogm_init(hard_iface);
+ bat_priv->bat_algo_ops->bat_ogm_init(hard_iface);
if (!hard_iface->packet_buff) {
- bat_err(hard_iface->soft_iface, "Can't add interface packet "
- "(%s): out of memory\n", hard_iface->net_dev->name);
+ bat_err(hard_iface->soft_iface,
+ "Can't add interface packet (%s): out of memory\n",
+ hard_iface->net_dev->name);
ret = -ENOMEM;
goto err;
}
@@ -334,29 +336,22 @@ int hardif_enable_interface(struct hard_iface *hard_iface,
if (atomic_read(&bat_priv->fragmentation) && hard_iface->net_dev->mtu <
ETH_DATA_LEN + BAT_HEADER_LEN)
bat_info(hard_iface->soft_iface,
- "The MTU of interface %s is too small (%i) to handle "
- "the transport of batman-adv packets. Packets going "
- "over this interface will be fragmented on layer2 "
- "which could impact the performance. Setting the MTU "
- "to %zi would solve the problem.\n",
- hard_iface->net_dev->name, hard_iface->net_dev->mtu,
- ETH_DATA_LEN + BAT_HEADER_LEN);
+ "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. Packets going over this interface will be fragmented on layer2 which could impact the performance. Setting the MTU to %zi would solve the problem.\n",
+ hard_iface->net_dev->name, hard_iface->net_dev->mtu,
+ ETH_DATA_LEN + BAT_HEADER_LEN);
if (!atomic_read(&bat_priv->fragmentation) && hard_iface->net_dev->mtu <
ETH_DATA_LEN + BAT_HEADER_LEN)
bat_info(hard_iface->soft_iface,
- "The MTU of interface %s is too small (%i) to handle "
- "the transport of batman-adv packets. If you experience"
- " problems getting traffic through try increasing the "
- "MTU to %zi.\n",
- hard_iface->net_dev->name, hard_iface->net_dev->mtu,
- ETH_DATA_LEN + BAT_HEADER_LEN);
+ "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. If you experience problems getting traffic through try increasing the MTU to %zi.\n",
+ hard_iface->net_dev->name, hard_iface->net_dev->mtu,
+ ETH_DATA_LEN + BAT_HEADER_LEN);
if (hardif_is_iface_up(hard_iface))
hardif_activate_interface(hard_iface);
else
- bat_err(hard_iface->soft_iface, "Not using interface %s "
- "(retrying later): interface not active\n",
+ bat_err(hard_iface->soft_iface,
+ "Not using interface %s (retrying later): interface not active\n",
hard_iface->net_dev->name);
/* begin scheduling originator messages on that interface */
@@ -527,9 +522,10 @@ static int hard_if_event(struct notifier_block *this,
goto hardif_put;
check_known_mac_addr(hard_iface->net_dev);
- bat_ogm_update_mac(hard_iface);
bat_priv = netdev_priv(hard_iface->soft_iface);
+ bat_priv->bat_algo_ops->bat_ogm_update_mac(hard_iface);
+
primary_if = primary_if_get_selected(bat_priv);
if (!primary_if)
goto hardif_put;
@@ -572,8 +568,8 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
goto err_free;
/* expect a valid ethernet header here. */
- if (unlikely(skb->mac_len != sizeof(struct ethhdr)
- || !skb_mac_header(skb)))
+ if (unlikely(skb->mac_len != sizeof(struct ethhdr) ||
+ !skb_mac_header(skb)))
goto err_free;
if (!hard_iface->soft_iface)
@@ -590,17 +586,17 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
batman_ogm_packet = (struct batman_ogm_packet *)skb->data;
- if (batman_ogm_packet->version != COMPAT_VERSION) {
+ if (batman_ogm_packet->header.version != COMPAT_VERSION) {
bat_dbg(DBG_BATMAN, bat_priv,
"Drop packet: incompatible batman version (%i)\n",
- batman_ogm_packet->version);
+ batman_ogm_packet->header.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_ogm_packet->packet_type) {
+ switch (batman_ogm_packet->header.packet_type) {
/* batman originator packet */
case BAT_OGM:
ret = recv_bat_ogm_packet(skb, hard_iface);
diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h
index 67f78d1a63b..e68c5655e61 100644
--- a/net/batman-adv/hard-interface.h
+++ b/net/batman-adv/hard-interface.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
diff --git a/net/batman-adv/hash.c b/net/batman-adv/hash.c
index d1da29da333..117687bedf2 100644
--- a/net/batman-adv/hash.c
+++ b/net/batman-adv/hash.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2006-2012 B.A.T.M.A.N. contributors:
*
* Simon Wunderlich, Marek Lindner
*
diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h
index 4768717f07f..d4bd7862719 100644
--- a/net/batman-adv/hash.h
+++ b/net/batman-adv/hash.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2006-2012 B.A.T.M.A.N. contributors:
*
* Simon Wunderlich, Marek Lindner
*
diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c
index d9c1e7bb7fb..b87518edcef 100644
--- a/net/batman-adv/icmp_socket.c
+++ b/net/batman-adv/icmp_socket.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
@@ -59,8 +59,7 @@ static int bat_socket_open(struct inode *inode, struct file *file)
}
if (i == ARRAY_SIZE(socket_client_hash)) {
- pr_err("Error - can't add another packet client: "
- "maximum number of clients reached\n");
+ pr_err("Error - can't add another packet client: maximum number of clients reached\n");
kfree(socket_client);
return -EXFULL;
}
@@ -162,8 +161,7 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
if (len < sizeof(struct icmp_packet)) {
bat_dbg(DBG_BATMAN, bat_priv,
- "Error - can't send packet from char device: "
- "invalid packet size\n");
+ "Error - can't send packet from char device: invalid packet size\n");
return -EINVAL;
}
@@ -191,27 +189,25 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
goto free_skb;
}
- if (icmp_packet->packet_type != BAT_ICMP) {
+ if (icmp_packet->header.packet_type != BAT_ICMP) {
bat_dbg(DBG_BATMAN, bat_priv,
- "Error - can't send packet from char device: "
- "got bogus packet type (expected: BAT_ICMP)\n");
+ "Error - can't send packet from char device: got bogus packet type (expected: BAT_ICMP)\n");
len = -EINVAL;
goto free_skb;
}
if (icmp_packet->msg_type != ECHO_REQUEST) {
bat_dbg(DBG_BATMAN, bat_priv,
- "Error - can't send packet from char device: "
- "got bogus message type (expected: ECHO_REQUEST)\n");
+ "Error - can't send packet from char device: got bogus message type (expected: ECHO_REQUEST)\n");
len = -EINVAL;
goto free_skb;
}
icmp_packet->uid = socket_client->index;
- if (icmp_packet->version != COMPAT_VERSION) {
+ if (icmp_packet->header.version != COMPAT_VERSION) {
icmp_packet->msg_type = PARAMETER_PROBLEM;
- icmp_packet->version = COMPAT_VERSION;
+ icmp_packet->header.version = COMPAT_VERSION;
bat_socket_add_packet(socket_client, icmp_packet, packet_len);
goto free_skb;
}
diff --git a/net/batman-adv/icmp_socket.h b/net/batman-adv/icmp_socket.h
index 462b190fa10..380ed4c2443 100644
--- a/net/batman-adv/icmp_socket.h
+++ b/net/batman-adv/icmp_socket.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index fb87bdc2ce9..6d51caaf8ce 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -32,11 +32,14 @@
#include "gateway_client.h"
#include "vis.h"
#include "hash.h"
+#include "bat_algo.h"
/* List manipulations on hardif_list have to be rtnl_lock()'ed,
* list traversals just rcu-locked */
struct list_head hardif_list;
+char bat_routing_algo[20] = "BATMAN IV";
+static struct hlist_head bat_algo_list;
unsigned char broadcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
@@ -45,6 +48,9 @@ struct workqueue_struct *bat_event_workqueue;
static int __init batman_init(void)
{
INIT_LIST_HEAD(&hardif_list);
+ INIT_HLIST_HEAD(&bat_algo_list);
+
+ bat_iv_init();
/* the name should not be longer than 10 chars - see
* http://lwn.net/Articles/23634/ */
@@ -58,8 +64,8 @@ static int __init batman_init(void)
register_netdevice_notifier(&hard_if_notifier);
- pr_info("B.A.T.M.A.N. advanced %s (compatibility version %i) "
- "loaded\n", SOURCE_VERSION, COMPAT_VERSION);
+ pr_info("B.A.T.M.A.N. advanced %s (compatibility version %i) loaded\n",
+ SOURCE_VERSION, COMPAT_VERSION);
return 0;
}
@@ -170,9 +176,110 @@ int is_my_mac(const uint8_t *addr)
}
rcu_read_unlock();
return 0;
+}
+
+static struct bat_algo_ops *bat_algo_get(char *name)
+{
+ struct bat_algo_ops *bat_algo_ops = NULL, *bat_algo_ops_tmp;
+ struct hlist_node *node;
+
+ hlist_for_each_entry(bat_algo_ops_tmp, node, &bat_algo_list, list) {
+ if (strcmp(bat_algo_ops_tmp->name, name) != 0)
+ continue;
+
+ bat_algo_ops = bat_algo_ops_tmp;
+ break;
+ }
+
+ return bat_algo_ops;
+}
+
+int bat_algo_register(struct bat_algo_ops *bat_algo_ops)
+{
+ struct bat_algo_ops *bat_algo_ops_tmp;
+ int ret = -1;
+
+ bat_algo_ops_tmp = bat_algo_get(bat_algo_ops->name);
+ if (bat_algo_ops_tmp) {
+ pr_info("Trying to register already registered routing algorithm: %s\n",
+ bat_algo_ops->name);
+ goto out;
+ }
+
+ /* all algorithms must implement all ops (for now) */
+ if (!bat_algo_ops->bat_ogm_init ||
+ !bat_algo_ops->bat_ogm_init_primary ||
+ !bat_algo_ops->bat_ogm_update_mac ||
+ !bat_algo_ops->bat_ogm_schedule ||
+ !bat_algo_ops->bat_ogm_emit ||
+ !bat_algo_ops->bat_ogm_receive) {
+ pr_info("Routing algo '%s' does not implement required ops\n",
+ bat_algo_ops->name);
+ goto out;
+ }
+
+ INIT_HLIST_NODE(&bat_algo_ops->list);
+ hlist_add_head(&bat_algo_ops->list, &bat_algo_list);
+ ret = 0;
+
+out:
+ return ret;
+}
+
+int bat_algo_select(struct bat_priv *bat_priv, char *name)
+{
+ struct bat_algo_ops *bat_algo_ops;
+ int ret = -1;
+
+ bat_algo_ops = bat_algo_get(name);
+ if (!bat_algo_ops)
+ goto out;
+
+ bat_priv->bat_algo_ops = bat_algo_ops;
+ ret = 0;
+
+out:
+ return ret;
+}
+
+int bat_algo_seq_print_text(struct seq_file *seq, void *offset)
+{
+ struct bat_algo_ops *bat_algo_ops;
+ struct hlist_node *node;
+
+ seq_printf(seq, "Available routing algorithms:\n");
+
+ hlist_for_each_entry(bat_algo_ops, node, &bat_algo_list, list) {
+ seq_printf(seq, "%s\n", bat_algo_ops->name);
+ }
+
+ return 0;
+}
+
+static int param_set_ra(const char *val, const struct kernel_param *kp)
+{
+ struct bat_algo_ops *bat_algo_ops;
+ bat_algo_ops = bat_algo_get((char *)val);
+ if (!bat_algo_ops) {
+ pr_err("Routing algorithm '%s' is not supported\n", val);
+ return -EINVAL;
+ }
+
+ return param_set_copystring(val, kp);
}
+static const struct kernel_param_ops param_ops_ra = {
+ .set = param_set_ra,
+ .get = param_get_string,
+};
+
+static struct kparam_string __param_string_ra = {
+ .maxlen = sizeof(bat_routing_algo),
+ .string = bat_routing_algo,
+};
+
+module_param_cb(routing_algo, &param_ops_ra, &__param_string_ra, 0644);
module_init(batman_init);
module_exit(batman_exit);
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index 86354e06eb4..94fa1c2393a 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -28,7 +28,7 @@
#define DRIVER_DEVICE "batman-adv"
#ifndef SOURCE_VERSION
-#define SOURCE_VERSION "2012.0.0"
+#define SOURCE_VERSION "2012.1.0"
#endif
/* B.A.T.M.A.N. parameters */
@@ -41,13 +41,14 @@
/* purge originators after time in seconds if no valid packet comes in
* -> TODO: check influence on TQ_LOCAL_WINDOW_SIZE */
-#define PURGE_TIMEOUT 200
-#define TT_LOCAL_TIMEOUT 3600 /* in seconds */
-#define TT_CLIENT_ROAM_TIMEOUT 600
+#define PURGE_TIMEOUT 200000 /* 200 seconds */
+#define TT_LOCAL_TIMEOUT 3600000 /* in miliseconds */
+#define TT_CLIENT_ROAM_TIMEOUT 600000 /* in miliseconds */
/* 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 TT_REQUEST_TIMEOUT 3000 /* miliseconds we have to keep
+ * pending tt_req */
#define TQ_GLOBAL_WINDOW_SIZE 5
#define TQ_LOCAL_BIDRECT_SEND_MINIMUM 1
@@ -56,8 +57,8 @@
#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_TIME 20000 /* Time in which a client can roam at most
+ * ROAMING_MAX_COUNT times in miliseconds*/
#define ROAMING_MAX_COUNT 5
#define NO_FLAGS 0
@@ -106,9 +107,7 @@ enum uev_type {
#define GW_THRESHOLD 50
-/*
- * Debug Messages
- */
+/* Debug Messages */
#ifdef pr_fmt
#undef pr_fmt
#endif
@@ -123,14 +122,7 @@ enum dbg_level {
DBG_ALL = 7
};
-
-/*
- * Vis
- */
-
-/*
- * Kernel headers
- */
+/* Kernel headers */
#include <linux/mutex.h> /* mutex */
#include <linux/module.h> /* needed by all modules */
@@ -147,6 +139,7 @@ enum dbg_level {
#include <linux/seq_file.h>
#include "types.h"
+extern char bat_routing_algo[];
extern struct list_head hardif_list;
extern unsigned char broadcast_addr[];
@@ -157,6 +150,9 @@ void mesh_free(struct net_device *soft_iface);
void inc_module_count(void);
void dec_module_count(void);
int is_my_mac(const uint8_t *addr);
+int bat_algo_register(struct bat_algo_ops *bat_algo_ops);
+int bat_algo_select(struct bat_priv *bat_priv, char *name);
+int bat_algo_seq_print_text(struct seq_file *seq, void *offset);
#ifdef CONFIG_BATMAN_ADV_DEBUG
int debug_log(struct bat_priv *bat_priv, const char *fmt, ...) __printf(2, 3);
@@ -202,6 +198,17 @@ static inline int compare_eth(const void *data1, const void *data2)
return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
}
+/**
+ * has_timed_out - compares current time (jiffies) and timestamp + timeout
+ * @timestamp: base value to compare with (in jiffies)
+ * @timeout: added to base value before comparing (in milliseconds)
+ *
+ * Returns true if current time is after timestamp + timeout
+ */
+static inline bool has_timed_out(unsigned long timestamp, unsigned int timeout)
+{
+ return time_is_before_jiffies(timestamp + msecs_to_jiffies(timeout));
+}
#define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0)
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index 0bc2045a2f2..43c0a4f1399 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2009-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -143,7 +143,7 @@ static void orig_node_free_rcu(struct rcu_head *rcu)
frag_list_free(&orig_node->frag_list);
tt_global_del_orig(orig_node->bat_priv, orig_node,
- "originator timed out");
+ "originator timed out");
kfree(orig_node->tt_buff);
kfree(orig_node->bcast_own);
@@ -219,6 +219,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, const uint8_t *addr)
/* extra reference for return */
atomic_set(&orig_node->refcount, 2);
+ orig_node->tt_initialised = false;
orig_node->tt_poss_change = false;
orig_node->bat_priv = bat_priv;
memcpy(orig_node->orig, addr, ETH_ALEN);
@@ -281,8 +282,7 @@ static bool purge_orig_neighbors(struct bat_priv *bat_priv,
hlist_for_each_entry_safe(neigh_node, node, node_tmp,
&orig_node->neigh_list, list) {
- if ((time_after(jiffies,
- neigh_node->last_valid + PURGE_TIMEOUT * HZ)) ||
+ if ((has_timed_out(neigh_node->last_valid, PURGE_TIMEOUT)) ||
(neigh_node->if_incoming->if_status == IF_INACTIVE) ||
(neigh_node->if_incoming->if_status == IF_NOT_IN_USE) ||
(neigh_node->if_incoming->if_status == IF_TO_BE_REMOVED)) {
@@ -294,14 +294,12 @@ static bool purge_orig_neighbors(struct bat_priv *bat_priv,
(neigh_node->if_incoming->if_status ==
IF_TO_BE_REMOVED))
bat_dbg(DBG_BATMAN, bat_priv,
- "neighbor purge: originator %pM, "
- "neighbor: %pM, iface: %s\n",
+ "neighbor purge: originator %pM, neighbor: %pM, iface: %s\n",
orig_node->orig, neigh_node->addr,
neigh_node->if_incoming->net_dev->name);
else
bat_dbg(DBG_BATMAN, bat_priv,
- "neighbor timeout: originator %pM, "
- "neighbor: %pM, last_valid: %lu\n",
+ "neighbor timeout: originator %pM, neighbor: %pM, last_valid: %lu\n",
orig_node->orig, neigh_node->addr,
(neigh_node->last_valid / HZ));
@@ -326,18 +324,15 @@ static bool purge_orig_node(struct bat_priv *bat_priv,
{
struct neigh_node *best_neigh_node;
- if (time_after(jiffies,
- orig_node->last_valid + 2 * PURGE_TIMEOUT * HZ)) {
-
+ if (has_timed_out(orig_node->last_valid, 2 * PURGE_TIMEOUT)) {
bat_dbg(DBG_BATMAN, bat_priv,
"Originator timeout: originator %pM, last_valid %lu\n",
orig_node->orig, (orig_node->last_valid / HZ));
return true;
} else {
if (purge_orig_neighbors(bat_priv, orig_node,
- &best_neigh_node)) {
+ &best_neigh_node))
update_route(bat_priv, orig_node, best_neigh_node);
- }
}
return false;
@@ -371,8 +366,8 @@ static void _purge_orig(struct bat_priv *bat_priv)
continue;
}
- if (time_after(jiffies, orig_node->last_frag_packet +
- msecs_to_jiffies(FRAG_TIMEOUT)))
+ if (has_timed_out(orig_node->last_frag_packet,
+ FRAG_TIMEOUT))
frag_list_free(&orig_node->frag_list);
}
spin_unlock_bh(list_lock);
@@ -419,15 +414,15 @@ int orig_seq_print_text(struct seq_file *seq, void *offset)
primary_if = primary_if_get_selected(bat_priv);
if (!primary_if) {
- ret = seq_printf(seq, "BATMAN mesh %s disabled - "
- "please specify interfaces to enable it\n",
+ ret = seq_printf(seq,
+ "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
net_dev->name);
goto out;
}
if (primary_if->if_status != IF_ACTIVE) {
- ret = seq_printf(seq, "BATMAN mesh %s "
- "disabled - primary interface not active\n",
+ ret = seq_printf(seq,
+ "BATMAN mesh %s disabled - primary interface not active\n",
net_dev->name);
goto out;
}
diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h
index 67765ffef73..3fe2eda8565 100644
--- a/net/batman-adv/originator.h
+++ b/net/batman-adv/originator.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h
index 4d9e54c57a3..441f3db1bd9 100644
--- a/net/batman-adv/packet.h
+++ b/net/batman-adv/packet.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -90,10 +90,14 @@ enum tt_client_flags {
TT_CLIENT_PENDING = 1 << 10
};
-struct batman_ogm_packet {
+struct batman_header {
uint8_t packet_type;
uint8_t version; /* batman version field */
uint8_t ttl;
+} __packed;
+
+struct batman_ogm_packet {
+ struct batman_header header;
uint8_t flags; /* 0x40: DIRECTLINK flag, 0x20 VIS_SERVER flag... */
uint32_t seqno;
uint8_t orig[6];
@@ -108,9 +112,7 @@ struct batman_ogm_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 ttl;
+ struct batman_header header;
uint8_t msg_type; /* see ICMP message types above */
uint8_t dst[6];
uint8_t orig[6];
@@ -124,9 +126,7 @@ struct icmp_packet {
/* icmp_packet_rr must start with all fields from imcp_packet
* as this is assumed by code that handles ICMP packets */
struct icmp_packet_rr {
- uint8_t packet_type;
- uint8_t version; /* batman version field */
- uint8_t ttl;
+ struct batman_header header;
uint8_t msg_type; /* see ICMP message types above */
uint8_t dst[6];
uint8_t orig[6];
@@ -137,17 +137,13 @@ struct icmp_packet_rr {
} __packed;
struct unicast_packet {
- uint8_t packet_type;
- uint8_t version; /* batman version field */
- uint8_t ttl;
+ struct batman_header header;
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 ttl;
+ struct batman_header header;
uint8_t ttvn; /* destination translation table version number */
uint8_t dest[6];
uint8_t flags;
@@ -157,18 +153,14 @@ struct unicast_frag_packet {
} __packed;
struct bcast_packet {
- uint8_t packet_type;
- uint8_t version; /* batman version field */
- uint8_t ttl;
+ struct batman_header header;
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 */
+ struct batman_header header;
uint8_t vis_type; /* which type of vis-participant sent this? */
uint32_t seqno; /* sequence number */
uint8_t entries; /* number of entries behind this struct */
@@ -179,9 +171,7 @@ struct vis_packet {
} __packed;
struct tt_query_packet {
- uint8_t packet_type;
- uint8_t version; /* batman version field */
- uint8_t ttl;
+ struct batman_header header;
/* the flag field is a combination of:
* - TT_REQUEST or TT_RESPONSE
* - TT_FULL_TABLE */
@@ -202,9 +192,7 @@ struct tt_query_packet {
} __packed;
struct roam_adv_packet {
- uint8_t packet_type;
- uint8_t version;
- uint8_t ttl;
+ struct batman_header header;
uint8_t reserved;
uint8_t dst[ETH_ALEN];
uint8_t src[ETH_ALEN];
diff --git a/net/batman-adv/ring_buffer.c b/net/batman-adv/ring_buffer.c
index f1ccfa76ce8..fd63951d118 100644
--- a/net/batman-adv/ring_buffer.c
+++ b/net/batman-adv/ring_buffer.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
diff --git a/net/batman-adv/ring_buffer.h b/net/batman-adv/ring_buffer.h
index 7cdfe62b657..8b58bd82767 100644
--- a/net/batman-adv/ring_buffer.h
+++ b/net/batman-adv/ring_buffer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index 773e606f970..7f8e1589941 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -29,7 +29,6 @@
#include "originator.h"
#include "vis.h"
#include "unicast.h"
-#include "bat_ogm.h"
void slide_own_bcast_window(struct hard_iface *hard_iface)
{
@@ -73,7 +72,7 @@ static void _update_route(struct bat_priv *bat_priv,
bat_dbg(DBG_ROUTES, bat_priv, "Deleting route towards: %pM\n",
orig_node->orig);
tt_global_del_orig(bat_priv, orig_node,
- "Deleted route towards originator");
+ "Deleted route towards originator");
/* route added */
} else if ((!curr_router) && (neigh_node)) {
@@ -84,8 +83,7 @@ static void _update_route(struct bat_priv *bat_priv,
/* route changed */
} else if (neigh_node && curr_router) {
bat_dbg(DBG_ROUTES, bat_priv,
- "Changing route towards: %pM "
- "(now via %pM - was via %pM)\n",
+ "Changing route towards: %pM (now via %pM - was via %pM)\n",
orig_node->orig, neigh_node->addr,
curr_router->addr);
}
@@ -230,24 +228,25 @@ void bonding_save_primary(const struct orig_node *orig_node,
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)) {
- if (time_after(jiffies, *last_reset +
- msecs_to_jiffies(RESET_PROTECTION_MS))) {
+ if ((seq_num_diff <= -TQ_LOCAL_WINDOW_SIZE) ||
+ (seq_num_diff >= EXPECTED_SEQNO_RANGE)) {
+ if (has_timed_out(*last_reset, RESET_PROTECTION_MS)) {
*last_reset = jiffies;
bat_dbg(DBG_BATMAN, bat_priv,
"old packet received, start protection\n");
return 0;
- } else
+ } else {
return 1;
+ }
}
return 0;
}
int recv_bat_ogm_packet(struct sk_buff *skb, struct hard_iface *hard_iface)
{
+ struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
struct ethhdr *ethhdr;
/* drop packet if it has not necessary minimum size */
@@ -272,9 +271,7 @@ int recv_bat_ogm_packet(struct sk_buff *skb, struct hard_iface *hard_iface)
if (skb_linearize(skb) < 0)
return NET_RX_DROP;
- ethhdr = (struct ethhdr *)skb_mac_header(skb);
-
- bat_ogm_receive(ethhdr, skb->data, skb_headlen(skb), hard_iface);
+ bat_priv->bat_algo_ops->bat_ogm_receive(hard_iface, skb);
kfree_skb(skb);
return NET_RX_SUCCESS;
@@ -320,7 +317,7 @@ static int recv_my_icmp_packet(struct bat_priv *bat_priv,
memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
icmp_packet->msg_type = ECHO_REPLY;
- icmp_packet->ttl = TTL;
+ icmp_packet->header.ttl = TTL;
send_skb_packet(skb, router->if_incoming, router->addr);
ret = NET_RX_SUCCESS;
@@ -348,9 +345,8 @@ static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,
/* send TTL exceeded if packet is an echo request (traceroute) */
if (icmp_packet->msg_type != ECHO_REQUEST) {
- pr_debug("Warning - can't forward icmp packet from %pM to "
- "%pM: ttl exceeded\n", icmp_packet->orig,
- icmp_packet->dst);
+ pr_debug("Warning - can't forward icmp packet from %pM to %pM: ttl exceeded\n",
+ icmp_packet->orig, icmp_packet->dst);
goto out;
}
@@ -376,7 +372,7 @@ static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,
memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
icmp_packet->msg_type = TTL_EXCEEDED;
- icmp_packet->ttl = TTL;
+ icmp_packet->header.ttl = TTL;
send_skb_packet(skb, router->if_incoming, router->addr);
ret = NET_RX_SUCCESS;
@@ -432,7 +428,7 @@ int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if)
if ((hdr_size == sizeof(struct icmp_packet_rr)) &&
(icmp_packet->rr_cur < BAT_RR_LEN)) {
memcpy(&(icmp_packet->rr[icmp_packet->rr_cur]),
- ethhdr->h_dest, ETH_ALEN);
+ ethhdr->h_dest, ETH_ALEN);
icmp_packet->rr_cur++;
}
@@ -441,7 +437,7 @@ int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if)
return recv_my_icmp_packet(bat_priv, skb, hdr_size);
/* TTL exceeded */
- if (icmp_packet->ttl < 2)
+ if (icmp_packet->header.ttl < 2)
return recv_icmp_ttl_exceeded(bat_priv, skb);
/* get routing information */
@@ -460,7 +456,7 @@ int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if)
icmp_packet = (struct icmp_packet_rr *)skb->data;
/* decrement ttl */
- icmp_packet->ttl--;
+ icmp_packet->header.ttl--;
/* route it */
send_skb_packet(skb, router->if_incoming, router->addr);
@@ -677,9 +673,9 @@ int recv_roam_adv(struct sk_buff *skb, struct hard_iface *recv_if)
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);
+ 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);
@@ -815,10 +811,9 @@ int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
unicast_packet = (struct unicast_packet *)skb->data;
/* TTL exceeded */
- if (unicast_packet->ttl < 2) {
- pr_debug("Warning - can't forward unicast packet from %pM to "
- "%pM: ttl exceeded\n", ethhdr->h_source,
- unicast_packet->dest);
+ if (unicast_packet->header.ttl < 2) {
+ pr_debug("Warning - can't forward unicast packet from %pM to %pM: ttl exceeded\n",
+ ethhdr->h_source, unicast_packet->dest);
goto out;
}
@@ -840,7 +835,7 @@ int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
unicast_packet = (struct unicast_packet *)skb->data;
- if (unicast_packet->packet_type == BAT_UNICAST &&
+ if (unicast_packet->header.packet_type == BAT_UNICAST &&
atomic_read(&bat_priv->fragmentation) &&
skb->len > neigh_node->if_incoming->net_dev->mtu) {
ret = frag_send_skb(skb, bat_priv,
@@ -848,7 +843,7 @@ int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
goto out;
}
- if (unicast_packet->packet_type == BAT_UNICAST_FRAG &&
+ if (unicast_packet->header.packet_type == BAT_UNICAST_FRAG &&
frag_can_reassemble(skb, neigh_node->if_incoming->net_dev->mtu)) {
ret = frag_reassemble_skb(skb, bat_priv, &new_skb);
@@ -867,7 +862,7 @@ int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
}
/* decrement ttl */
- unicast_packet->ttl--;
+ unicast_packet->header.ttl--;
/* route it */
send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
@@ -937,10 +932,10 @@ static int check_unicast_ttvn(struct bat_priv *bat_priv,
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);
+ 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;
}
@@ -1041,7 +1036,7 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
if (is_my_mac(bcast_packet->orig))
goto out;
- if (bcast_packet->ttl < 2)
+ if (bcast_packet->header.ttl < 2)
goto out;
orig_node = orig_hash_find(bat_priv, bcast_packet->orig);
diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h
index 7aaee0fb0fd..92ac100d83d 100644
--- a/net/batman-adv/routing.h
+++ b/net/batman-adv/routing.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c
index 8a684eb738a..af7a6741a68 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -28,7 +28,6 @@
#include "vis.h"
#include "gateway_common.h"
#include "originator.h"
-#include "bat_ogm.h"
static void send_outstanding_bcast_packet(struct work_struct *work);
@@ -46,8 +45,8 @@ int send_skb_packet(struct sk_buff *skb, struct hard_iface *hard_iface,
goto send_skb_err;
if (!(hard_iface->net_dev->flags & IFF_UP)) {
- pr_warning("Interface %s is not up - can't send packet via "
- "that interface!\n", hard_iface->net_dev->name);
+ pr_warning("Interface %s is not up - can't send packet via that interface!\n",
+ hard_iface->net_dev->name);
goto send_skb_err;
}
@@ -57,7 +56,7 @@ int send_skb_packet(struct sk_buff *skb, struct hard_iface *hard_iface,
skb_reset_mac_header(skb);
- ethhdr = (struct ethhdr *) skb_mac_header(skb);
+ ethhdr = (struct ethhdr *)skb_mac_header(skb);
memcpy(ethhdr->h_source, hard_iface->net_dev->dev_addr, ETH_ALEN);
memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
ethhdr->h_proto = __constant_htons(ETH_P_BATMAN);
@@ -168,7 +167,7 @@ void schedule_bat_ogm(struct hard_iface *hard_iface)
if (primary_if)
hardif_free_ref(primary_if);
- bat_ogm_schedule(hard_iface, tt_num_changes);
+ bat_priv->bat_algo_ops->bat_ogm_schedule(hard_iface, tt_num_changes);
}
static void forw_packet_free(struct forw_packet *forw_packet)
@@ -234,7 +233,7 @@ int add_bcast_packet_to_list(struct bat_priv *bat_priv,
/* as we have a copy now, it is safe to decrease the TTL */
bcast_packet = (struct bcast_packet *)newskb->data;
- bcast_packet->ttl--;
+ bcast_packet->header.ttl--;
skb_reset_mac_header(newskb);
@@ -318,7 +317,7 @@ void send_outstanding_bat_ogm_packet(struct work_struct *work)
if (atomic_read(&bat_priv->mesh_state) == MESH_DEACTIVATING)
goto out;
- bat_ogm_emit(forw_packet);
+ bat_priv->bat_algo_ops->bat_ogm_emit(forw_packet);
/**
* we have to have at least one packet in the queue
diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h
index c8ca3ef7385..824ef06f9b0 100644
--- a/net/batman-adv/send.h
+++ b/net/batman-adv/send.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index 987c75a775f..a5590f4193f 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -252,8 +252,8 @@ static void softif_neigh_vid_select(struct bat_priv *bat_priv,
vid, curr_neigh->addr);
else if ((curr_neigh) && (new_neigh))
bat_dbg(DBG_ROUTES, bat_priv,
- "Changing mesh exit point on vid: %d from %pM "
- "to %pM.\n", vid, curr_neigh->addr, new_neigh->addr);
+ "Changing mesh exit point on vid: %d from %pM to %pM.\n",
+ vid, curr_neigh->addr, new_neigh->addr);
else if ((!curr_neigh) && (new_neigh))
bat_dbg(DBG_ROUTES, bat_priv,
"Setting mesh exit point on vid: %d to %pM.\n",
@@ -327,15 +327,15 @@ int softif_neigh_seq_print_text(struct seq_file *seq, void *offset)
primary_if = primary_if_get_selected(bat_priv);
if (!primary_if) {
- ret = seq_printf(seq, "BATMAN mesh %s disabled - "
- "please specify interfaces to enable it\n",
+ ret = seq_printf(seq,
+ "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
net_dev->name);
goto out;
}
if (primary_if->if_status != IF_ACTIVE) {
- ret = seq_printf(seq, "BATMAN mesh %s "
- "disabled - primary interface not active\n",
+ ret = seq_printf(seq,
+ "BATMAN mesh %s disabled - primary interface not active\n",
net_dev->name);
goto out;
}
@@ -396,15 +396,14 @@ void softif_neigh_purge(struct bat_priv *bat_priv)
hlist_for_each_entry_safe(softif_neigh, node_tmp, node_tmp2,
&softif_neigh_vid->softif_neigh_list,
list) {
- if ((!time_after(jiffies, softif_neigh->last_seen +
- msecs_to_jiffies(SOFTIF_NEIGH_TIMEOUT))) &&
+ if ((!has_timed_out(softif_neigh->last_seen,
+ SOFTIF_NEIGH_TIMEOUT)) &&
(atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE))
continue;
if (curr_softif_neigh == softif_neigh) {
bat_dbg(DBG_ROUTES, bat_priv,
- "Current mesh exit point on vid: %d "
- "'%pM' vanished.\n",
+ "Current mesh exit point on vid: %d '%pM' vanished.\n",
softif_neigh_vid->vid,
softif_neigh->addr);
do_deselect = 1;
@@ -457,10 +456,10 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
batman_ogm_packet = (struct batman_ogm_packet *)
(skb->data + ETH_HLEN);
- if (batman_ogm_packet->version != COMPAT_VERSION)
+ if (batman_ogm_packet->header.version != COMPAT_VERSION)
goto out;
- if (batman_ogm_packet->packet_type != BAT_OGM)
+ if (batman_ogm_packet->header.packet_type != BAT_OGM)
goto out;
if (!(batman_ogm_packet->flags & PRIMARIES_FIRST_HOP))
@@ -541,6 +540,7 @@ static int interface_set_mac_addr(struct net_device *dev, void *p)
}
memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+ dev->addr_assign_type &= ~NET_ADDR_RANDOM;
return 0;
}
@@ -632,11 +632,11 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
goto dropped;
bcast_packet = (struct bcast_packet *)skb->data;
- bcast_packet->version = COMPAT_VERSION;
- bcast_packet->ttl = TTL;
+ bcast_packet->header.version = COMPAT_VERSION;
+ bcast_packet->header.ttl = TTL;
/* batman packet type: broadcast */
- bcast_packet->packet_type = BAT_BCAST;
+ bcast_packet->header.packet_type = BAT_BCAST;
/* hw address of first interface is the orig mac because only
* this mac is known throughout the mesh */
@@ -725,8 +725,8 @@ void interface_rx(struct net_device *soft_iface,
skb_push(skb, hdr_size);
unicast_packet = (struct unicast_packet *)skb->data;
- if ((unicast_packet->packet_type != BAT_UNICAST) &&
- (unicast_packet->packet_type != BAT_UNICAST_FRAG))
+ if ((unicast_packet->header.packet_type != BAT_UNICAST) &&
+ (unicast_packet->header.packet_type != BAT_UNICAST_FRAG))
goto dropped;
skb_reset_mac_header(skb);
@@ -783,7 +783,6 @@ static const struct net_device_ops bat_netdev_ops = {
static void interface_setup(struct net_device *dev)
{
struct bat_priv *priv = netdev_priv(dev);
- char dev_addr[ETH_ALEN];
ether_setup(dev);
@@ -800,8 +799,7 @@ static void interface_setup(struct net_device *dev)
dev->hard_header_len = BAT_HEADER_LEN;
/* generate random address */
- random_ether_addr(dev_addr);
- memcpy(dev->dev_addr, dev_addr, ETH_ALEN);
+ eth_hw_addr_random(dev);
SET_ETHTOOL_OPS(dev, &bat_ethtool_ops);
@@ -855,6 +853,10 @@ struct net_device *softif_create(const char *name)
bat_priv->primary_if = NULL;
bat_priv->num_ifaces = 0;
+ ret = bat_algo_select(bat_priv, bat_routing_algo);
+ if (ret < 0)
+ goto unreg_soft_iface;
+
ret = sysfs_add_meshif(soft_iface);
if (ret < 0)
goto unreg_soft_iface;
diff --git a/net/batman-adv/soft-interface.h b/net/batman-adv/soft-interface.h
index 001546fc96f..756eab5b8dd 100644
--- a/net/batman-adv/soft-interface.h
+++ b/net/batman-adv/soft-interface.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index ab8dea8b0b2..1f869212784 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -108,14 +108,6 @@ static struct tt_global_entry *tt_global_hash_find(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->common.refcount))
@@ -218,6 +210,11 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
if (compare_eth(addr, soft_iface->dev_addr))
tt_local_entry->common.flags |= TT_CLIENT_NOPURGE;
+ /* The local entry has to be marked as NEW to avoid to send it in
+ * a full table response going out before the next ttvn increment
+ * (consistency check) */
+ tt_local_entry->common.flags |= TT_CLIENT_NEW;
+
hash_added = hash_add(bat_priv->tt_local_hash, compare_tt, choose_orig,
&tt_local_entry->common,
&tt_local_entry->common.hash_entry);
@@ -230,11 +227,6 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
tt_local_event(bat_priv, addr, tt_local_entry->common.flags);
- /* The local entry has to be marked as NEW to avoid to send it in
- * a full table response going out before the next ttvn increment
- * (consistency check) */
- tt_local_entry->common.flags |= TT_CLIENT_NEW;
-
/* remove address from global hash if present */
tt_global_entry = tt_global_hash_find(bat_priv, addr);
@@ -269,7 +261,7 @@ int tt_changes_fill_buffer(struct bat_priv *bat_priv,
atomic_set(&bat_priv->tt_local_changes, 0);
list_for_each_entry_safe(entry, safe, &bat_priv->tt_changes_list,
- list) {
+ list) {
if (count < tot_changes) {
memcpy(buff + tt_len(count),
&entry->change, sizeof(struct tt_change));
@@ -317,21 +309,21 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset)
primary_if = primary_if_get_selected(bat_priv);
if (!primary_if) {
- ret = seq_printf(seq, "BATMAN mesh %s disabled - "
- "please specify interfaces to enable it\n",
+ ret = seq_printf(seq,
+ "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
net_dev->name);
goto out;
}
if (primary_if->if_status != IF_ACTIVE) {
- ret = seq_printf(seq, "BATMAN mesh %s disabled - "
- "primary interface not active\n",
+ ret = seq_printf(seq,
+ "BATMAN mesh %s disabled - primary interface not active\n",
net_dev->name);
goto out;
}
- seq_printf(seq, "Locally retrieved addresses (from %s) "
- "announced via TT (TTVN: %u):\n",
+ seq_printf(seq,
+ "Locally retrieved addresses (from %s) announced via TT (TTVN: %u):\n",
net_dev->name, (uint8_t)atomic_read(&bat_priv->ttvn));
for (i = 0; i < hash->size; i++) {
@@ -341,17 +333,17 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset)
hlist_for_each_entry_rcu(tt_common_entry, node,
head, hash_entry) {
seq_printf(seq, " * %pM [%c%c%c%c%c]\n",
- tt_common_entry->addr,
- (tt_common_entry->flags &
- TT_CLIENT_ROAM ? 'R' : '.'),
- (tt_common_entry->flags &
- TT_CLIENT_NOPURGE ? 'P' : '.'),
- (tt_common_entry->flags &
- TT_CLIENT_NEW ? 'N' : '.'),
- (tt_common_entry->flags &
- TT_CLIENT_PENDING ? 'X' : '.'),
- (tt_common_entry->flags &
- TT_CLIENT_WIFI ? 'W' : '.'));
+ tt_common_entry->addr,
+ (tt_common_entry->flags &
+ TT_CLIENT_ROAM ? 'R' : '.'),
+ (tt_common_entry->flags &
+ TT_CLIENT_NOPURGE ? 'P' : '.'),
+ (tt_common_entry->flags &
+ TT_CLIENT_NEW ? 'N' : '.'),
+ (tt_common_entry->flags &
+ TT_CLIENT_PENDING ? 'X' : '.'),
+ (tt_common_entry->flags &
+ TT_CLIENT_WIFI ? 'W' : '.'));
}
rcu_read_unlock();
}
@@ -363,7 +355,7 @@ out:
static void tt_local_set_pending(struct bat_priv *bat_priv,
struct tt_local_entry *tt_local_entry,
- uint16_t flags)
+ uint16_t flags, const char *message)
{
tt_local_event(bat_priv, tt_local_entry->common.addr,
tt_local_entry->common.flags | flags);
@@ -372,6 +364,10 @@ static void tt_local_set_pending(struct bat_priv *bat_priv,
* 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->common.flags |= TT_CLIENT_PENDING;
+
+ bat_dbg(DBG_TT, bat_priv,
+ "Local tt entry (%pM) pending to be removed: %s\n",
+ tt_local_entry->common.addr, message);
}
void tt_local_remove(struct bat_priv *bat_priv, const uint8_t *addr,
@@ -384,10 +380,7 @@ void tt_local_remove(struct bat_priv *bat_priv, const uint8_t *addr,
goto out;
tt_local_set_pending(bat_priv, tt_local_entry, TT_CLIENT_DEL |
- (roaming ? TT_CLIENT_ROAM : NO_FLAGS));
-
- bat_dbg(DBG_TT, bat_priv, "Local tt entry (%pM) pending to be removed: "
- "%s\n", tt_local_entry->common.addr, message);
+ (roaming ? TT_CLIENT_ROAM : NO_FLAGS), message);
out:
if (tt_local_entry)
tt_local_entry_free_ref(tt_local_entry);
@@ -420,15 +413,12 @@ static void tt_local_purge(struct bat_priv *bat_priv)
if (tt_local_entry->common.flags & TT_CLIENT_PENDING)
continue;
- if (!is_out_of_time(tt_local_entry->last_seen,
- TT_LOCAL_TIMEOUT * 1000))
+ if (!has_timed_out(tt_local_entry->last_seen,
+ TT_LOCAL_TIMEOUT))
continue;
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->common.addr);
+ TT_CLIENT_DEL, "timed out");
}
spin_unlock_bh(list_lock);
}
@@ -585,15 +575,15 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset)
primary_if = primary_if_get_selected(bat_priv);
if (!primary_if) {
- ret = seq_printf(seq, "BATMAN mesh %s disabled - please "
- "specify interfaces to enable it\n",
+ ret = seq_printf(seq,
+ "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
net_dev->name);
goto out;
}
if (primary_if->if_status != IF_ACTIVE) {
- ret = seq_printf(seq, "BATMAN mesh %s disabled - "
- "primary interface not active\n",
+ ret = seq_printf(seq,
+ "BATMAN mesh %s disabled - primary interface not active\n",
net_dev->name);
goto out;
}
@@ -613,20 +603,18 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset)
tt_global_entry = container_of(tt_common_entry,
struct tt_global_entry,
common);
- seq_printf(seq, " * %pM (%3u) via %pM (%3u) "
- "[%c%c%c]\n",
- tt_global_entry->common.addr,
- tt_global_entry->ttvn,
- tt_global_entry->orig_node->orig,
- (uint8_t) atomic_read(
+ seq_printf(seq,
+ " * %pM (%3u) via %pM (%3u) [%c%c]\n",
+ tt_global_entry->common.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->common.flags &
- TT_CLIENT_ROAM ? 'R' : '.'),
- (tt_global_entry->common.flags &
- TT_CLIENT_PENDING ? 'X' : '.'),
- (tt_global_entry->common.flags &
- TT_CLIENT_WIFI ? 'W' : '.'));
+ (tt_global_entry->common.flags &
+ TT_CLIENT_ROAM ? 'R' : '.'),
+ (tt_global_entry->common.flags &
+ TT_CLIENT_WIFI ? 'W' : '.'));
}
rcu_read_unlock();
}
@@ -665,29 +653,31 @@ void tt_global_del(struct bat_priv *bat_priv,
struct tt_local_entry *tt_local_entry = NULL;
tt_global_entry = tt_global_hash_find(bat_priv, addr);
- if (!tt_global_entry)
+ if (!tt_global_entry || tt_global_entry->orig_node != orig_node)
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->common.addr);
- if (!tt_local_entry) {
- tt_global_entry->common.flags |= TT_CLIENT_ROAM;
- tt_global_entry->roam_at = jiffies;
- goto out;
- }
- }
- _tt_global_del(bat_priv, tt_global_entry, message);
+ if (!roaming)
+ goto out_del;
+
+ /* 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->common.addr);
+ if (!tt_local_entry) {
+ tt_global_entry->common.flags |= TT_CLIENT_ROAM;
+ tt_global_entry->roam_at = jiffies;
+ goto out;
}
+
+out_del:
+ _tt_global_del(bat_priv, tt_global_entry, message);
+
out:
if (tt_global_entry)
tt_global_entry_free_ref(tt_global_entry);
@@ -715,14 +705,13 @@ void tt_global_del_orig(struct bat_priv *bat_priv,
spin_lock_bh(list_lock);
hlist_for_each_entry_safe(tt_common_entry, node, safe,
- head, hash_entry) {
+ head, hash_entry) {
tt_global_entry = container_of(tt_common_entry,
struct tt_global_entry,
common);
if (tt_global_entry->orig_node == orig_node) {
bat_dbg(DBG_TT, bat_priv,
- "Deleting global tt entry %pM "
- "(via %pM): %s\n",
+ "Deleting global tt entry %pM (via %pM): %s\n",
tt_global_entry->common.addr,
tt_global_entry->orig_node->orig,
message);
@@ -733,6 +722,7 @@ void tt_global_del_orig(struct bat_priv *bat_priv,
spin_unlock_bh(list_lock);
}
atomic_set(&orig_node->tt_size, 0);
+ orig_node->tt_initialised = false;
}
static void tt_global_roam_purge(struct bat_priv *bat_priv)
@@ -757,12 +747,12 @@ static void tt_global_roam_purge(struct bat_priv *bat_priv)
common);
if (!(tt_global_entry->common.flags & TT_CLIENT_ROAM))
continue;
- if (!is_out_of_time(tt_global_entry->roam_at,
- TT_CLIENT_ROAM_TIMEOUT * 1000))
+ if (!has_timed_out(tt_global_entry->roam_at,
+ TT_CLIENT_ROAM_TIMEOUT))
continue;
- bat_dbg(DBG_TT, bat_priv, "Deleting global "
- "tt entry (%pM): Roaming timeout\n",
+ bat_dbg(DBG_TT, bat_priv,
+ "Deleting global tt entry (%pM): Roaming timeout\n",
tt_global_entry->common.addr);
atomic_dec(&tt_global_entry->orig_node->tt_size);
hlist_del_rcu(node);
@@ -846,11 +836,6 @@ struct orig_node *transtable_search(struct bat_priv *bat_priv,
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->common.flags & TT_CLIENT_PENDING)
- goto out;
-
orig_node = tt_global_entry->orig_node;
out:
@@ -977,8 +962,7 @@ static void tt_req_purge(struct bat_priv *bat_priv)
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)) {
+ if (has_timed_out(node->issued_at, TT_REQUEST_TIMEOUT)) {
list_del(&node->list);
kfree(node);
}
@@ -996,8 +980,8 @@ static struct tt_req_node *new_tt_req_node(struct bat_priv *bat_priv,
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))
+ !has_timed_out(tt_req_node_tmp->issued_at,
+ TT_REQUEST_TIMEOUT))
goto unlock;
}
@@ -1134,11 +1118,11 @@ static int send_tt_request(struct bat_priv *bat_priv,
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;
+ tt_request->header.packet_type = BAT_TT_QUERY;
+ tt_request->header.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->header.ttl = TTL;
tt_request->ttvn = ttvn;
tt_request->tt_data = tt_crc;
tt_request->flags = TT_REQUEST;
@@ -1150,8 +1134,9 @@ static int send_tt_request(struct bat_priv *bat_priv,
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,
+ 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);
@@ -1188,9 +1173,8 @@ static bool send_other_tt_response(struct bat_priv *bat_priv,
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,
+ "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 */
@@ -1264,9 +1248,9 @@ static bool send_other_tt_response(struct bat_priv *bat_priv,
tt_response = (struct tt_query_packet *)skb->data;
}
- tt_response->packet_type = BAT_TT_QUERY;
- tt_response->version = COMPAT_VERSION;
- tt_response->ttl = TTL;
+ tt_response->header.packet_type = BAT_TT_QUERY;
+ tt_response->header.version = COMPAT_VERSION;
+ tt_response->header.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;
@@ -1315,9 +1299,8 @@ static bool send_my_tt_response(struct bat_priv *bat_priv,
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,
+ "Received TT_REQUEST from %pM for ttvn: %u (me) [%c]\n",
+ tt_request->src, tt_request->ttvn,
(tt_request->flags & TT_FULL_TABLE ? 'F' : '.'));
@@ -1381,9 +1364,9 @@ static bool send_my_tt_response(struct bat_priv *bat_priv,
tt_response = (struct tt_query_packet *)skb->data;
}
- tt_response->packet_type = BAT_TT_QUERY;
- tt_response->version = COMPAT_VERSION;
- tt_response->ttl = TTL;
+ tt_response->header.packet_type = BAT_TT_QUERY;
+ tt_response->header.version = COMPAT_VERSION;
+ tt_response->header.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;
@@ -1450,6 +1433,7 @@ static void _tt_update_changes(struct bat_priv *bat_priv,
*/
return;
}
+ orig_node->tt_initialised = true;
}
static void tt_fill_gtable(struct bat_priv *bat_priv,
@@ -1519,10 +1503,9 @@ void handle_tt_response(struct bat_priv *bat_priv,
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,
+ 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);
@@ -1589,8 +1572,7 @@ static void tt_roam_purge(struct bat_priv *bat_priv)
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))
+ if (!has_timed_out(node->first_time, ROAMING_MAX_TIME))
continue;
list_del(&node->list);
@@ -1617,8 +1599,7 @@ static bool tt_check_roam_count(struct bat_priv *bat_priv,
if (!compare_eth(tt_roam_node->addr, client))
continue;
- if (is_out_of_time(tt_roam_node->first_time,
- ROAMING_MAX_TIME * 1000))
+ if (has_timed_out(tt_roam_node->first_time, ROAMING_MAX_TIME))
continue;
if (!atomic_dec_not_zero(&tt_roam_node->counter))
@@ -1669,9 +1650,9 @@ void send_roam_adv(struct bat_priv *bat_priv, uint8_t *client,
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;
+ roam_adv_packet->header.packet_type = BAT_ROAM_ADV;
+ roam_adv_packet->header.version = COMPAT_VERSION;
+ roam_adv_packet->header.ttl = TTL;
primary_if = primary_if_get_selected(bat_priv);
if (!primary_if)
goto out;
@@ -1788,8 +1769,9 @@ static void tt_local_purge_pending_clients(struct bat_priv *bat_priv)
if (!(tt_common_entry->flags & TT_CLIENT_PENDING))
continue;
- bat_dbg(DBG_TT, bat_priv, "Deleting local tt entry "
- "(%pM): pending\n", tt_common_entry->addr);
+ bat_dbg(DBG_TT, bat_priv,
+ "Deleting local tt entry (%pM): pending\n",
+ tt_common_entry->addr);
atomic_dec(&bat_priv->num_local_tt);
hlist_del_rcu(node);
@@ -1854,8 +1836,10 @@ void tt_update_orig(struct bat_priv *bat_priv, struct orig_node *orig_node,
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) {
+ /* orig table not initialised AND first diff is in the OGM OR the ttvn
+ * increased by one -> we can apply the attached changes */
+ if ((!orig_node->tt_initialised && ttvn == 1) ||
+ 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 */
@@ -1889,14 +1873,13 @@ void tt_update_orig(struct bat_priv *bat_priv, struct orig_node *orig_node,
} 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) {
+ if (!orig_node->tt_initialised || 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);
+ 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 30efd49881a..c753633b1da 100644
--- a/net/batman-adv/translation-table.h
+++ b/net/batman-adv/translation-table.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index e9eb043719a..302efb52347 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -81,6 +81,7 @@ struct orig_node {
int16_t tt_buff_len;
spinlock_t tt_buff_lock; /* protects tt_buff */
atomic_t tt_size;
+ bool tt_initialised;
/* The tt_poss_change flag is used to detect an ongoing roaming phase.
* If true, then I sent a Roaming_adv to this orig_node and I have to
* inspect every packet directed to it to check whether it is still
@@ -205,6 +206,7 @@ struct bat_priv {
atomic_t gw_reselect;
struct hard_iface __rcu *primary_if; /* rcu protected pointer */
struct vis_info *my_vis_info;
+ struct bat_algo_ops *bat_algo_ops;
};
struct socket_client {
@@ -343,4 +345,23 @@ struct softif_neigh {
struct rcu_head rcu;
};
+struct bat_algo_ops {
+ struct hlist_node list;
+ char *name;
+ /* init OGM when hard-interface is enabled */
+ void (*bat_ogm_init)(struct hard_iface *hard_iface);
+ /* init primary OGM when primary interface is selected */
+ void (*bat_ogm_init_primary)(struct hard_iface *hard_iface);
+ /* init mac addresses of the OGM belonging to this hard-interface */
+ void (*bat_ogm_update_mac)(struct hard_iface *hard_iface);
+ /* prepare a new outgoing OGM for the send queue */
+ void (*bat_ogm_schedule)(struct hard_iface *hard_iface,
+ int tt_num_changes);
+ /* send scheduled OGM */
+ void (*bat_ogm_emit)(struct forw_packet *forw_packet);
+ /* receive incoming OGM */
+ void (*bat_ogm_receive)(struct hard_iface *if_incoming,
+ struct sk_buff *skb);
+};
+
#endif /* _NET_BATMAN_ADV_TYPES_H_ */
diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c
index 07d1c1da89d..676f6a626b2 100644
--- a/net/batman-adv/unicast.c
+++ b/net/batman-adv/unicast.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2010-2012 B.A.T.M.A.N. contributors:
*
* Andreas Langer
*
@@ -66,8 +66,8 @@ static struct sk_buff *frag_merge_packet(struct list_head *head,
kfree_skb(tmp_skb);
memmove(skb->data + uni_diff, skb->data, hdr_len);
- unicast_packet = (struct unicast_packet *) skb_pull(skb, uni_diff);
- unicast_packet->packet_type = BAT_UNICAST;
+ unicast_packet = (struct unicast_packet *)skb_pull(skb, uni_diff);
+ unicast_packet->header.packet_type = BAT_UNICAST;
return skb;
@@ -238,7 +238,7 @@ int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
goto dropped;
skb_reserve(frag_skb, ucf_hdr_len);
- unicast_packet = (struct unicast_packet *) skb->data;
+ unicast_packet = (struct unicast_packet *)skb->data;
memcpy(&tmp_uc, unicast_packet, uc_hdr_len);
skb_split(skb, frag_skb, data_len / 2 + uc_hdr_len);
@@ -251,9 +251,9 @@ int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
memcpy(frag1, &tmp_uc, sizeof(tmp_uc));
- frag1->ttl--;
- frag1->version = COMPAT_VERSION;
- frag1->packet_type = BAT_UNICAST_FRAG;
+ frag1->header.ttl--;
+ frag1->header.version = COMPAT_VERSION;
+ frag1->header.packet_type = BAT_UNICAST_FRAG;
memcpy(frag1->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
memcpy(frag2, frag1, sizeof(*frag2));
@@ -320,11 +320,11 @@ find_router:
unicast_packet = (struct unicast_packet *)skb->data;
- unicast_packet->version = COMPAT_VERSION;
+ unicast_packet->header.version = COMPAT_VERSION;
/* batman packet type: unicast */
- unicast_packet->packet_type = BAT_UNICAST;
+ unicast_packet->header.packet_type = BAT_UNICAST;
/* set unicast ttl */
- unicast_packet->ttl = TTL;
+ unicast_packet->header.ttl = TTL;
/* copy the destination for faster routing */
memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN);
/* set the destination tt version number */
@@ -335,7 +335,7 @@ find_router:
data_len + sizeof(*unicast_packet) >
neigh_node->if_incoming->net_dev->mtu) {
/* send frag skb decreases ttl */
- unicast_packet->ttl++;
+ unicast_packet->header.ttl++;
ret = frag_send_skb(skb, bat_priv,
neigh_node->if_incoming, neigh_node->addr);
goto out;
diff --git a/net/batman-adv/unicast.h b/net/batman-adv/unicast.h
index 8fd5535544b..a9faf6b1db1 100644
--- a/net/batman-adv/unicast.h
+++ b/net/batman-adv/unicast.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2010-2012 B.A.T.M.A.N. contributors:
*
* Andreas Langer
*
diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
index cc3b9f2f3b5..c4a5b8cafad 100644
--- a/net/batman-adv/vis.c
+++ b/net/batman-adv/vis.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2008-2012 B.A.T.M.A.N. contributors:
*
* Simon Wunderlich
*
@@ -617,7 +617,7 @@ static int generate_vis_packet(struct bat_priv *bat_priv)
packet->vis_type = atomic_read(&bat_priv->vis_mode);
memcpy(packet->target_orig, broadcast_addr, ETH_ALEN);
- packet->ttl = TTL;
+ packet->header.ttl = TTL;
packet->seqno = htonl(ntohl(packet->seqno) + 1);
packet->entries = 0;
skb_trim(info->skb_packet, sizeof(*packet));
@@ -714,8 +714,7 @@ static void purge_vis_packets(struct bat_priv *bat_priv)
if (info == bat_priv->my_vis_info)
continue;
- if (time_after(jiffies,
- info->first_seen + VIS_TIMEOUT * HZ)) {
+ if (has_timed_out(info->first_seen, VIS_TIMEOUT)) {
hlist_del(node);
send_list_del(info);
kref_put(&info->refcount, free_info);
@@ -818,19 +817,19 @@ static void send_vis_packet(struct bat_priv *bat_priv, struct vis_info *info)
goto out;
packet = (struct vis_packet *)info->skb_packet->data;
- if (packet->ttl < 2) {
+ if (packet->header.ttl < 2) {
pr_debug("Error - can't send vis packet: ttl exceeded\n");
goto out;
}
memcpy(packet->sender_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
- packet->ttl--;
+ packet->header.ttl--;
if (is_broadcast_ether_addr(packet->target_orig))
broadcast_vis_packet(bat_priv, info);
else
unicast_vis_packet(bat_priv, info);
- packet->ttl++; /* restore TTL */
+ packet->header.ttl++; /* restore TTL */
out:
if (primary_if)
@@ -910,9 +909,9 @@ int vis_init(struct bat_priv *bat_priv)
INIT_LIST_HEAD(&bat_priv->my_vis_info->send_list);
kref_init(&bat_priv->my_vis_info->refcount);
bat_priv->my_vis_info->bat_priv = bat_priv;
- packet->version = COMPAT_VERSION;
- packet->packet_type = BAT_VIS;
- packet->ttl = TTL;
+ packet->header.version = COMPAT_VERSION;
+ packet->header.packet_type = BAT_VIS;
+ packet->header.ttl = TTL;
packet->seqno = 0;
packet->entries = 0;
diff --git a/net/batman-adv/vis.h b/net/batman-adv/vis.h
index 31b820d07f2..ee2e46e5347 100644
--- a/net/batman-adv/vis.h
+++ b/net/batman-adv/vis.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2011 B.A.T.M.A.N. contributors:
+ * Copyright (C) 2008-2012 B.A.T.M.A.N. contributors:
*
* Simon Wunderlich, Marek Lindner
*
@@ -22,7 +22,8 @@
#ifndef _NET_BATMAN_ADV_VIS_H_
#define _NET_BATMAN_ADV_VIS_H_
-#define VIS_TIMEOUT 200 /* timeout of vis packets in seconds */
+#define VIS_TIMEOUT 200000 /* timeout of vis packets
+ * in miliseconds */
int vis_seq_print_text(struct seq_file *seq, void *offset);
void receive_server_sync_packet(struct bat_priv *bat_priv,
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 9ec85eb8853..3537d385035 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -29,7 +29,6 @@ menuconfig BT
BNEP Module (Bluetooth Network Encapsulation Protocol)
CMTP Module (CAPI Message Transport Protocol)
HIDP Module (Human Interface Device Protocol)
- SMP Module (Security Manager Protocol)
Say Y here to compile Bluetooth support into the kernel or say M to
compile it as module (bluetooth).
diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c
index 17800b1d28e..9f9c8dcd8af 100644
--- a/net/bluetooth/bnep/sock.c
+++ b/net/bluetooth/bnep/sock.c
@@ -143,10 +143,10 @@ static int bnep_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne
{
if (cmd == BNEPGETCONNLIST) {
struct bnep_connlist_req cl;
- uint32_t uci;
+ u32 uci;
int err;
- if (get_user(cl.cnum, (uint32_t __user *) arg) ||
+ if (get_user(cl.cnum, (u32 __user *) arg) ||
get_user(uci, (u32 __user *) (arg + 4)))
return -EFAULT;
@@ -157,7 +157,7 @@ static int bnep_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne
err = bnep_get_connlist(&cl);
- if (!err && put_user(cl.cnum, (uint32_t __user *) arg))
+ if (!err && put_user(cl.cnum, (u32 __user *) arg))
err = -EFAULT;
return err;
diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c
index 3f2dd5c25ae..1230faaac29 100644
--- a/net/bluetooth/cmtp/sock.c
+++ b/net/bluetooth/cmtp/sock.c
@@ -137,10 +137,10 @@ static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne
{
if (cmd == CMTPGETCONNLIST) {
struct cmtp_connlist_req cl;
- uint32_t uci;
+ u32 uci;
int err;
- if (get_user(cl.cnum, (uint32_t __user *) arg) ||
+ if (get_user(cl.cnum, (u32 __user *) arg) ||
get_user(uci, (u32 __user *) (arg + 4)))
return -EFAULT;
@@ -151,7 +151,7 @@ static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne
err = cmtp_get_connlist(&cl);
- if (!err && put_user(cl.cnum, (uint32_t __user *) arg))
+ if (!err && put_user(cl.cnum, (u32 __user *) arg))
err = -EFAULT;
return err;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 07bc69ed949..947172bf162 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -35,7 +35,6 @@
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/interrupt.h>
-#include <linux/notifier.h>
#include <net/sock.h>
#include <asm/system.h>
@@ -51,7 +50,7 @@ static void hci_le_connect(struct hci_conn *conn)
struct hci_cp_le_create_conn cp;
conn->state = BT_CONNECT;
- conn->out = 1;
+ conn->out = true;
conn->link_mode |= HCI_LM_MASTER;
conn->sec_level = BT_SECURITY_LOW;
@@ -80,10 +79,10 @@ void hci_acl_connect(struct hci_conn *conn)
struct inquiry_entry *ie;
struct hci_cp_create_conn cp;
- BT_DBG("%p", conn);
+ BT_DBG("hcon %p", conn);
conn->state = BT_CONNECT;
- conn->out = 1;
+ conn->out = true;
conn->link_mode = HCI_LM_MASTER;
@@ -105,7 +104,8 @@ void hci_acl_connect(struct hci_conn *conn)
}
memcpy(conn->dev_class, ie->data.dev_class, 3);
- conn->ssp_mode = ie->data.ssp_mode;
+ if (ie->data.ssp_mode > 0)
+ set_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
}
cp.pkt_type = cpu_to_le16(conn->pkt_type);
@@ -151,7 +151,7 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle)
BT_DBG("%p", conn);
conn->state = BT_CONNECT;
- conn->out = 1;
+ conn->out = true;
conn->attempt++;
@@ -169,7 +169,7 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)
BT_DBG("%p", conn);
conn->state = BT_CONNECT;
- conn->out = 1;
+ conn->out = true;
conn->attempt++;
@@ -279,16 +279,13 @@ static void hci_conn_timeout(struct work_struct *work)
{
struct hci_conn *conn = container_of(work, struct hci_conn,
disc_work.work);
- struct hci_dev *hdev = conn->hdev;
__u8 reason;
- BT_DBG("conn %p state %d", conn, conn->state);
+ BT_DBG("conn %p state %s", conn, state_to_string(conn->state));
if (atomic_read(&conn->refcnt))
return;
- hci_dev_lock(hdev);
-
switch (conn->state) {
case BT_CONNECT:
case BT_CONNECT2:
@@ -308,8 +305,6 @@ static void hci_conn_timeout(struct work_struct *work)
conn->state = BT_CLOSED;
break;
}
-
- hci_dev_unlock(hdev);
}
/* Enter sniff mode */
@@ -337,7 +332,7 @@ static void hci_conn_enter_sniff_mode(struct hci_conn *conn)
hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp);
}
- if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
+ 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);
@@ -372,7 +367,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
BT_DBG("%s dst %s", hdev->name, batostr(dst));
- conn = kzalloc(sizeof(struct hci_conn), GFP_ATOMIC);
+ conn = kzalloc(sizeof(struct hci_conn), GFP_KERNEL);
if (!conn)
return NULL;
@@ -386,7 +381,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
conn->remote_auth = 0xff;
conn->key_type = 0xff;
- conn->power_save = 1;
+ set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
switch (type) {
@@ -407,7 +402,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
skb_queue_head_init(&conn->data_q);
- INIT_LIST_HEAD(&conn->chan_list);;
+ 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);
@@ -555,7 +550,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
if (!acl) {
acl = hci_conn_add(hdev, ACL_LINK, dst);
if (!acl)
- return NULL;
+ return ERR_PTR(-ENOMEM);
}
hci_conn_hold(acl);
@@ -575,7 +570,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
sco = hci_conn_add(hdev, type, dst);
if (!sco) {
hci_conn_put(acl);
- return NULL;
+ return ERR_PTR(-ENOMEM);
}
}
@@ -586,12 +581,12 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
if (acl->state == BT_CONNECTED &&
(sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
- acl->power_save = 1;
+ 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->pend)) {
+ 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->pend);
+ set_bit(HCI_CONN_SCO_SETUP_PEND, &acl->flags);
return sco;
}
@@ -607,8 +602,7 @@ int hci_conn_check_link_mode(struct hci_conn *conn)
{
BT_DBG("conn %p", conn);
- if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0 &&
- !(conn->link_mode & HCI_LM_ENCRYPT))
+ if (hci_conn_ssp_enabled(conn) && !(conn->link_mode & HCI_LM_ENCRYPT))
return 0;
return 1;
@@ -633,17 +627,17 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
conn->auth_type = auth_type;
- if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
+ 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->pend);
+ 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->pend);
+ set_bit(HCI_CONN_REAUTH_PEND, &conn->flags);
}
return 0;
@@ -654,7 +648,7 @@ static void hci_conn_encrypt(struct hci_conn *conn)
{
BT_DBG("conn %p", conn);
- if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
+ 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;
@@ -674,8 +668,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
/* For non 2.1 devices and low security level we don't need the link
key. */
- if (sec_level == BT_SECURITY_LOW &&
- (!conn->ssp_mode || !conn->hdev->ssp_mode))
+ if (sec_level == BT_SECURITY_LOW && !hci_conn_ssp_enabled(conn))
return 1;
/* For other security levels we need the link key. */
@@ -704,7 +697,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
goto encrypt;
auth:
- if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
return 0;
if (!hci_conn_auth(conn, sec_level, auth_type))
@@ -739,7 +732,7 @@ 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->pend)) {
+ 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,
@@ -758,7 +751,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
if (!role && conn->link_mode & HCI_LM_MASTER)
return 1;
- if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->pend)) {
+ 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;
@@ -782,10 +775,10 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
if (conn->mode != HCI_CM_SNIFF)
goto timer;
- if (!conn->power_save && !force_active)
+ if (!test_bit(HCI_CONN_POWER_SAVE, &conn->flags) && !force_active)
goto timer;
- if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
+ 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);
@@ -801,11 +794,11 @@ timer:
void hci_conn_hash_flush(struct hci_dev *hdev)
{
struct hci_conn_hash *h = &hdev->conn_hash;
- struct hci_conn *c;
+ struct hci_conn *c, *n;
BT_DBG("hdev %s", hdev->name);
- list_for_each_entry_rcu(c, &h->list, list) {
+ list_for_each_entry_safe(c, n, &h->list, list) {
c->state = BT_CLOSED;
hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
@@ -950,7 +943,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
BT_DBG("%s conn %p", hdev->name, conn);
- chan = kzalloc(sizeof(struct hci_chan), GFP_ATOMIC);
+ chan = kzalloc(sizeof(struct hci_chan), GFP_KERNEL);
if (!chan)
return NULL;
@@ -981,10 +974,10 @@ int hci_chan_del(struct hci_chan *chan)
void hci_chan_list_flush(struct hci_conn *conn)
{
- struct hci_chan *chan;
+ struct hci_chan *chan, *n;
BT_DBG("conn %p", conn);
- list_for_each_entry_rcu(chan, &conn->chan_list, list)
+ list_for_each_entry_safe(chan, n, &conn->chan_list, list)
hci_chan_del(chan);
}
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 5aeb6249119..59ec99eb739 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -40,7 +40,6 @@
#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>
@@ -55,8 +54,6 @@
#define AUTO_OFF_TIMEOUT 2000
-bool enable_hs;
-
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);
@@ -69,24 +66,11 @@ DEFINE_RWLOCK(hci_dev_list_lock);
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_sock_dev_event(hdev, event);
}
/* ---- HCI requests ---- */
@@ -98,8 +82,28 @@ void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int 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)
+ 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;
@@ -355,72 +359,209 @@ struct hci_dev *hci_dev_get(int index)
}
/* ---- Inquiry support ---- */
-static void inquiry_cache_flush(struct hci_dev *hdev)
+
+bool hci_discovery_active(struct hci_dev *hdev)
{
- struct inquiry_cache *cache = &hdev->inq_cache;
- struct inquiry_entry *next = cache->list, *e;
+ 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;
+}
- BT_DBG("cache %p", cache);
+static void inquiry_cache_flush(struct hci_dev *hdev)
+{
+ struct discovery_state *cache = &hdev->discovery;
+ struct inquiry_entry *p, *n;
- cache->list = NULL;
- while ((e = next)) {
- next = e->next;
- kfree(e);
+ 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 inquiry_cache *cache = &hdev->inq_cache;
+ struct discovery_state *cache = &hdev->discovery;
struct inquiry_entry *e;
BT_DBG("cache %p, %s", cache, batostr(bdaddr));
- for (e = cache->list; e; e = e->next)
+ 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;
- return e;
+ pos = &p->list;
+ }
+
+ list_add(&ie->list, pos);
}
-void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data)
+bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
+ bool name_known, bool *ssp)
{
- struct inquiry_cache *cache = &hdev->inq_cache;
+ 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) {
- /* Entry not in the cache. Add new one. */
- ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC);
- if (!ie)
- return;
+ 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);
+ }
- ie->next = cache->list;
- cache->list = 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 inquiry_cache *cache = &hdev->inq_cache;
+ struct discovery_state *cache = &hdev->discovery;
struct inquiry_info *info = (struct inquiry_info *) buf;
struct inquiry_entry *e;
int copied = 0;
- for (e = cache->list; e && copied < num; e = e->next, copied++) {
+ 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);
@@ -567,7 +708,7 @@ int hci_dev_open(__u16 dev)
hci_dev_hold(hdev);
set_bit(HCI_UP, &hdev->flags);
hci_notify(hdev, HCI_DEV_UP);
- if (!test_bit(HCI_SETUP, &hdev->flags)) {
+ if (!test_bit(HCI_SETUP, &hdev->dev_flags)) {
hci_dev_lock(hdev);
mgmt_powered(hdev, 1);
hci_dev_unlock(hdev);
@@ -603,6 +744,8 @@ 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);
@@ -619,14 +762,14 @@ static int hci_dev_do_close(struct hci_dev *hdev)
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_AUTO_OFF, &hdev->flags))
- cancel_delayed_work(&hdev->power_off);
-
- if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->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);
@@ -667,13 +810,18 @@ static int hci_dev_do_close(struct hci_dev *hdev)
* and no tasks are scheduled. */
hdev->close(hdev);
- hci_dev_lock(hdev);
- mgmt_powered(hdev, 0);
- hci_dev_unlock(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);
@@ -688,7 +836,12 @@ int hci_dev_close(__u16 dev)
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;
}
@@ -847,11 +1000,11 @@ int hci_get_dev_list(void __user *arg)
read_lock(&hci_dev_list_lock);
list_for_each_entry(hdev, &hci_dev_list, list) {
- if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
+ if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
cancel_delayed_work(&hdev->power_off);
- if (!test_bit(HCI_MGMT, &hdev->flags))
- set_bit(HCI_PAIRABLE, &hdev->flags);
+ 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;
@@ -883,11 +1036,11 @@ int hci_get_dev_info(void __user *arg)
if (!hdev)
return -ENODEV;
- if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
+ if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
cancel_delayed_work_sync(&hdev->power_off);
- if (!test_bit(HCI_MGMT, &hdev->flags))
- set_bit(HCI_PAIRABLE, &hdev->flags);
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+ set_bit(HCI_PAIRABLE, &hdev->dev_flags);
strcpy(di.name, hdev->name);
di.bdaddr = hdev->bdaddr;
@@ -967,11 +1120,11 @@ static void hci_power_on(struct work_struct *work)
if (hci_dev_open(hdev->id) < 0)
return;
- if (test_bit(HCI_AUTO_OFF, &hdev->flags))
+ 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->flags))
+ if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags))
mgmt_index_added(hdev);
}
@@ -982,9 +1135,7 @@ static void hci_power_off(struct work_struct *work)
BT_DBG("%s", hdev->name);
- clear_bit(HCI_AUTO_OFF, &hdev->flags);
-
- hci_dev_close(hdev->id);
+ hci_dev_do_close(hdev);
}
static void hci_discov_off(struct work_struct *work)
@@ -1037,6 +1188,18 @@ int hci_link_keys_clear(struct hci_dev *hdev)
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;
@@ -1084,44 +1247,38 @@ static int hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn,
return 0;
}
-struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8])
+struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8])
{
- struct link_key *k;
+ struct smp_ltk *k;
- list_for_each_entry(k, &hdev->link_keys, list) {
- struct key_master_id *id;
-
- if (k->type != HCI_LK_SMP_LTK)
+ list_for_each_entry(k, &hdev->long_term_keys, list) {
+ if (k->ediv != ediv ||
+ memcmp(rand, k->rand, sizeof(k->rand)))
continue;
- if (k->dlen != sizeof(*id))
- continue;
-
- id = (void *) &k->data;
- if (id->ediv == ediv &&
- (memcmp(rand, id->rand, sizeof(id->rand)) == 0))
- return k;
+ return k;
}
return NULL;
}
EXPORT_SYMBOL(hci_find_ltk);
-struct link_key *hci_find_link_key_type(struct hci_dev *hdev,
- bdaddr_t *bdaddr, u8 type)
+struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 addr_type)
{
- struct link_key *k;
+ struct smp_ltk *k;
- list_for_each_entry(k, &hdev->link_keys, list)
- if (k->type == type && bacmp(bdaddr, &k->bdaddr) == 0)
+ 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_link_key_type);
+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)
+ bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len)
{
struct link_key *key, *old_key;
u8 old_key_type, persistent;
@@ -1175,40 +1332,39 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
return 0;
}
-int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
- u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16])
+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 link_key *key, *old_key;
- struct key_master_id *id;
- u8 old_key_type;
+ struct smp_ltk *key, *old_key;
- BT_DBG("%s addr %s", hdev->name, batostr(bdaddr));
+ if (!(type & HCI_SMP_STK) && !(type & HCI_SMP_LTK))
+ return 0;
- old_key = hci_find_link_key_type(hdev, bdaddr, HCI_LK_SMP_LTK);
- if (old_key) {
+ old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type);
+ if (old_key)
key = old_key;
- old_key_type = old_key->type;
- } else {
- key = kzalloc(sizeof(*key) + sizeof(*id), GFP_ATOMIC);
+ else {
+ key = kzalloc(sizeof(*key), GFP_ATOMIC);
if (!key)
return -ENOMEM;
- list_add(&key->list, &hdev->link_keys);
- old_key_type = 0xff;
+ list_add(&key->list, &hdev->long_term_keys);
}
- key->dlen = sizeof(*id);
-
bacpy(&key->bdaddr, bdaddr);
- memcpy(key->val, ltk, sizeof(key->val));
- key->type = HCI_LK_SMP_LTK;
- key->pin_len = key_size;
+ 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));
- id = (void *) &key->data;
- id->ediv = ediv;
- memcpy(id->rand, rand, sizeof(id->rand));
+ if (!new_key)
+ return 0;
- if (new_key)
- mgmt_new_link_key(hdev, key, old_key_type);
+ if (type & HCI_SMP_LTK)
+ mgmt_new_ltk(hdev, key, 1);
return 0;
}
@@ -1229,6 +1385,23 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
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)
{
@@ -1240,7 +1413,7 @@ static void hci_cmd_timer(unsigned long arg)
}
struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
- bdaddr_t *bdaddr)
+ bdaddr_t *bdaddr)
{
struct oob_data *data;
@@ -1280,7 +1453,7 @@ int hci_remote_oob_data_clear(struct hci_dev *hdev)
}
int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
- u8 *randomizer)
+ u8 *randomizer)
{
struct oob_data *data;
@@ -1303,8 +1476,7 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
return 0;
}
-struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
- bdaddr_t *bdaddr)
+struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct bdaddr_list *b;
@@ -1331,7 +1503,7 @@ int hci_blacklist_clear(struct hci_dev *hdev)
return 0;
}
-int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
{
struct bdaddr_list *entry;
@@ -1349,10 +1521,10 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
list_add(&entry->list, &hdev->blacklist);
- return mgmt_device_blocked(hdev, bdaddr);
+ return mgmt_device_blocked(hdev, bdaddr, type);
}
-int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
{
struct bdaddr_list *entry;
@@ -1366,13 +1538,13 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
list_del(&entry->list);
kfree(entry);
- return mgmt_device_unblocked(hdev, bdaddr);
+ 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);
+ adv_work.work);
hci_dev_lock(hdev);
@@ -1415,11 +1587,7 @@ static inline int is_connectable_adv(u8 evt_type)
}
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))
+ 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
@@ -1427,7 +1595,7 @@ int hci_add_adv_entry(struct hci_dev *hdev,
if (hci_find_adv_entry(hdev, &ev->bdaddr))
return 0;
- entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
@@ -1442,16 +1610,116 @@ int hci_add_adv_entry(struct hci_dev *hdev,
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) &param,
+ 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 owner %p", hdev, hdev->name,
- hdev->bus, hdev->owner);
+ BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
- if (!hdev->open || !hdev->close || !hdev->destruct)
+ if (!hdev->open || !hdev->close)
return -EINVAL;
/* Do not allow HCI_AMP devices to register at index 0,
@@ -1472,7 +1740,6 @@ int hci_register_dev(struct hci_dev *hdev)
hdev->id = id;
list_add_tail(&hdev->list, head);
- atomic_set(&hdev->refcnt, 1);
mutex_init(&hdev->lock);
hdev->flags = 0;
@@ -1503,7 +1770,7 @@ int hci_register_dev(struct hci_dev *hdev)
init_waitqueue_head(&hdev->req_wait_q);
mutex_init(&hdev->req_lock);
- inquiry_cache_init(hdev);
+ discovery_init(hdev);
hci_conn_hash_init(hdev);
@@ -1514,6 +1781,7 @@ int hci_register_dev(struct hci_dev *hdev)
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);
@@ -1529,6 +1797,10 @@ int hci_register_dev(struct hci_dev *hdev)
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 |
@@ -1551,11 +1823,12 @@ int hci_register_dev(struct hci_dev *hdev)
}
}
- set_bit(HCI_AUTO_OFF, &hdev->flags);
- set_bit(HCI_SETUP, &hdev->flags);
+ 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;
@@ -1587,7 +1860,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
kfree_skb(hdev->reassembly[i]);
if (!test_bit(HCI_INIT, &hdev->flags) &&
- !test_bit(HCI_SETUP, &hdev->flags)) {
+ !test_bit(HCI_SETUP, &hdev->dev_flags)) {
hci_dev_lock(hdev);
mgmt_index_removed(hdev);
hci_dev_unlock(hdev);
@@ -1614,11 +1887,12 @@ void hci_unregister_dev(struct hci_dev *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);
+ hci_dev_put(hdev);
}
EXPORT_SYMBOL(hci_unregister_dev);
@@ -1706,7 +1980,7 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
while (count) {
scb = (void *) skb->cb;
- len = min(scb->expect, (__u16)count);
+ len = min_t(uint, scb->expect, count);
memcpy(skb_put(skb, len), data, len);
@@ -1862,11 +2136,15 @@ static int hci_send_frame(struct sk_buff *skb)
BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
- if (atomic_read(&hdev->promisc)) {
- /* Time stamp */
- __net_timestamp(skb);
+ /* Time stamp */
+ __net_timestamp(skb);
+
+ /* Send copy to monitor */
+ hci_send_to_monitor(hdev, skb);
- hci_send_to_sock(hdev, skb, NULL);
+ 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. */
@@ -2235,26 +2513,31 @@ static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type)
}
-static inline void hci_sched_acl(struct hci_dev *hdev)
+static inline int __get_blocks(struct hci_dev *hdev, struct sk_buff *skb)
{
- struct hci_chan *chan;
- struct sk_buff *skb;
- int quote;
- unsigned int cnt;
-
- BT_DBG("%s", hdev->name);
-
- if (!hci_conn_num(hdev, ACL_LINK))
- return;
+ /* 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 (!hdev->acl_cnt && time_after(jiffies, hdev->acl_last_tx + HZ * 45))
+ 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;
- cnt = hdev->acl_cnt;
+ __check_timeout(hdev, cnt);
while (hdev->acl_cnt &&
(chan = hci_chan_sent(hdev, ACL_LINK, &quote))) {
@@ -2270,7 +2553,7 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
skb = skb_dequeue(&chan->data_q);
hci_conn_enter_active_mode(chan->conn,
- bt_cb(skb)->force_active);
+ bt_cb(skb)->force_active);
hci_send_frame(skb);
hdev->acl_last_tx = jiffies;
@@ -2285,6 +2568,70 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
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, &quote))) {
+ 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)
{
@@ -2482,9 +2829,12 @@ static void hci_rx_work(struct work_struct *work)
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, NULL);
+ hci_send_to_sock(hdev, skb);
}
if (test_bit(HCI_RAW, &hdev->flags)) {
@@ -2568,6 +2918,8 @@ int hci_do_inquiry(struct hci_dev *hdev, u8 length)
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;
@@ -2584,6 +2936,3 @@ int hci_cancel_inquiry(struct hci_dev *hdev)
return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
}
-
-module_param(enable_hs, bool, 0644);
-MODULE_PARM_DESC(enable_hs, "Enable High Speed");
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 001307f8105..badb7851d11 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -35,7 +35,6 @@
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/interrupt.h>
-#include <linux/notifier.h>
#include <net/sock.h>
#include <asm/system.h>
@@ -45,8 +44,6 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-static bool enable_le;
-
/* Handle HCI Event packets */
static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
@@ -65,7 +62,7 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
clear_bit(HCI_INQUIRY, &hdev->flags);
hci_dev_lock(hdev);
- mgmt_discovering(hdev, 0);
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
hci_dev_unlock(hdev);
hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status);
@@ -195,7 +192,10 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, HCI_OP_RESET, status);
- hdev->dev_flags = 0;
+ /* 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)
@@ -211,13 +211,14 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_set_local_name_complete(hdev, sent, status);
-
- if (status == 0)
+ 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)
@@ -229,7 +230,8 @@ static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
if (rp->status)
return;
- memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH);
+ 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)
@@ -252,6 +254,9 @@ static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb)
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);
}
@@ -349,14 +354,19 @@ static void hci_cc_write_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
- return;
-
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_CLASS_OF_DEV);
if (!sent)
return;
- memcpy(hdev->dev_class, sent, 3);
+ 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)
@@ -419,18 +429,6 @@ static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, HCI_OP_HOST_BUFFER_SIZE, status);
}
-static void hci_cc_read_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
-{
- struct hci_rp_read_ssp_mode *rp = (void *) skb->data;
-
- BT_DBG("%s status 0x%x", hdev->name, rp->status);
-
- if (rp->status)
- return;
-
- hdev->ssp_mode = rp->mode;
-}
-
static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -438,14 +436,18 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
- return;
-
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_MODE);
if (!sent)
return;
- hdev->ssp_mode = *((__u8 *) sent);
+ 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)
@@ -540,20 +542,6 @@ static void hci_setup_event_mask(struct hci_dev *hdev)
hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
}
-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) {
- cp.le = 1;
- cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
- }
-
- hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp);
-}
-
static void hci_setup(struct hci_dev *hdev)
{
if (hdev->dev_type != HCI_BREDR)
@@ -565,8 +553,18 @@ static void hci_setup(struct hci_dev *hdev)
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
if (hdev->features[6] & LMP_SIMPLE_PAIR) {
- u8 mode = 0x01;
- hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
+ 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)
@@ -579,12 +577,15 @@ static void hci_setup(struct hci_dev *hdev)
struct hci_cp_read_local_ext_features cp;
cp.page = 0x01;
- hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES,
- sizeof(cp), &cp);
+ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp),
+ &cp);
}
- if (hdev->features[4] & LMP_LE)
- hci_set_le_support(hdev);
+ 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)
@@ -594,7 +595,7 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, rp->status);
if (rp->status)
- return;
+ goto done;
hdev->hci_ver = rp->hci_ver;
hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
@@ -608,6 +609,9 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
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)
@@ -624,8 +628,8 @@ static void hci_setup_link_policy(struct hci_dev *hdev)
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);
+ 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)
@@ -701,6 +705,22 @@ static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb
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)
{
@@ -709,7 +729,7 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
BT_DBG("%s status 0x%x", hdev->name, rp->status);
if (rp->status)
- return;
+ goto done;
switch (rp->page) {
case 0:
@@ -720,6 +740,10 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
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);
}
@@ -890,7 +914,7 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status);
if (rp->status != 0)
@@ -916,7 +940,7 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr,
rp->status);
@@ -951,9 +975,9 @@ static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr,
- rp->status);
+ 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);
}
@@ -967,9 +991,9 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr,
- rp->status);
+ ACL_LINK, 0, rp->status);
hci_dev_unlock(hdev);
}
@@ -982,9 +1006,9 @@ static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr,
- rp->status);
+ 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);
}
@@ -998,9 +1022,9 @@ static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev,
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr,
- rp->status);
+ ACL_LINK, 0, rp->status);
hci_dev_unlock(hdev);
}
@@ -1023,6 +1047,15 @@ 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,
@@ -1033,28 +1066,47 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
- return;
-
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:
@@ -1090,16 +1142,27 @@ static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
static inline void hci_cc_write_le_host_supported(struct hci_dev *hdev,
struct sk_buff *skb)
{
- struct hci_cp_read_local_ext_features cp;
+ struct hci_cp_write_le_host_supported *sent;
__u8 status = *((__u8 *) skb->data);
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
+ sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED);
+ if (!sent)
return;
- cp.page = 0x01;
- hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), &cp);
+ 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)
@@ -1110,7 +1173,7 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
hci_req_complete(hdev, HCI_OP_INQUIRY, status);
hci_conn_check_pending(hdev);
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_start_discovery_failed(hdev, status);
hci_dev_unlock(hdev);
return;
@@ -1119,7 +1182,7 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
set_bit(HCI_INQUIRY, &hdev->flags);
hci_dev_lock(hdev);
- mgmt_discovering(hdev, 1);
+ hci_discovery_set_state(hdev, DISCOVERY_FINDING);
hci_dev_unlock(hdev);
}
@@ -1153,7 +1216,7 @@ static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
if (!conn) {
conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr);
if (conn) {
- conn->out = 1;
+ conn->out = true;
conn->link_mode |= HCI_LM_MASTER;
} else
BT_ERR("No memory for new connection");
@@ -1263,7 +1326,7 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev,
/* Only request authentication for SSP connections or non-SSP
* devices with sec_level HIGH or if MITM protection is requested */
- if (!(hdev->ssp_mode > 0 && conn->ssp_mode > 0) &&
+ if (!hci_conn_ssp_enabled(conn) &&
conn->pending_sec_level != BT_SECURITY_HIGH &&
!(conn->auth_type & 0x01))
return 0;
@@ -1271,6 +1334,73 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev,
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;
@@ -1290,13 +1420,17 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
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->pend)) {
+ 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);
@@ -1413,9 +1547,9 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) {
- clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend);
+ clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags);
- if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend))
+ if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags))
hci_sco_setup(conn, status);
}
@@ -1440,15 +1574,37 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) {
- clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend);
+ clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags);
- if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend))
+ 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;
@@ -1478,7 +1634,7 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr);
if (conn) {
conn->dst_type = cp->peer_addr_type;
- conn->out = 1;
+ conn->out = true;
} else {
BT_ERR("No memory for new connection");
}
@@ -1496,6 +1652,8 @@ static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 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);
@@ -1506,8 +1664,28 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff
if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags))
return;
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+ return;
+
hci_dev_lock(hdev);
- mgmt_discovering(hdev, 0);
+
+ 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);
}
@@ -1525,6 +1703,8 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
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;
@@ -1533,9 +1713,11 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
data.clock_offset = info->clock_offset;
data.rssi = 0x00;
data.ssp_mode = 0x00;
- hci_inquiry_cache_update(hdev, &data);
+
+ name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, 0, NULL);
+ info->dev_class, 0, !name_known, ssp, NULL,
+ 0);
}
hci_dev_unlock(hdev);
@@ -1569,8 +1751,6 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->state = BT_CONFIG;
hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
- mgmt_connected(hdev, &ev->bdaddr, conn->type,
- conn->dst_type);
} else
conn->state = BT_CONNECTED;
@@ -1588,7 +1768,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
struct hci_cp_read_remote_features cp;
cp.handle = ev->handle;
hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
- sizeof(cp), &cp);
+ sizeof(cp), &cp);
}
/* Set packet type for incoming connection */
@@ -1596,14 +1776,14 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
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);
+ 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);
+ conn->dst_type, ev->status);
}
if (conn->type == ACL_LINK)
@@ -1668,8 +1848,8 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
else
cp.role = 0x01; /* Remain slave */
- hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ,
- sizeof(cp), &cp);
+ hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
+ &cp);
} else {
struct hci_cp_accept_sync_conn_req cp;
@@ -1683,7 +1863,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
cp.retrans_effort = 0xff;
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
- sizeof(cp), &cp);
+ sizeof(cp), &cp);
}
} else {
/* Connection rejected */
@@ -1711,12 +1891,14 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
if (ev->status == 0)
conn->state = BT_CLOSED;
- if (conn->type == ACL_LINK || conn->type == LE_LINK) {
+ 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, ev->status);
+ mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
+ conn->dst_type, ev->status);
else
- mgmt_disconnected(hdev, &conn->dst, conn->type,
- conn->dst_type);
+ mgmt_device_disconnected(hdev, &conn->dst, conn->type,
+ conn->dst_type);
}
if (ev->status == 0) {
@@ -1742,22 +1924,23 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
goto unlock;
if (!ev->status) {
- if (!(conn->ssp_mode > 0 && hdev->ssp_mode > 0) &&
- test_bit(HCI_CONN_REAUTH_PEND, &conn->pend)) {
+ 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, ev->status);
+ mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
+ ev->status);
}
- clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
- clear_bit(HCI_CONN_REAUTH_PEND, &conn->pend);
+ clear_bit(HCI_CONN_AUTH_PEND, &conn->flags);
+ clear_bit(HCI_CONN_REAUTH_PEND, &conn->flags);
if (conn->state == BT_CONFIG) {
- if (!ev->status && hdev->ssp_mode > 0 && conn->ssp_mode > 0) {
+ if (!ev->status && hci_conn_ssp_enabled(conn)) {
struct hci_cp_set_conn_encrypt cp;
cp.handle = ev->handle;
cp.encrypt = 0x01;
@@ -1776,7 +1959,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
hci_conn_put(conn);
}
- if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) {
if (!ev->status) {
struct hci_cp_set_conn_encrypt cp;
cp.handle = ev->handle;
@@ -1784,7 +1967,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp),
&cp);
} else {
- clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
hci_encrypt_cfm(conn, ev->status, 0x00);
}
}
@@ -1804,17 +1987,25 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb
hci_dev_lock(hdev);
- if (ev->status == 0 && test_bit(HCI_MGMT, &hdev->flags))
- mgmt_remote_name(hdev, &ev->bdaddr, ev->name);
-
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->pend)) {
+ 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);
@@ -1845,7 +2036,7 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *
conn->link_mode &= ~HCI_LM_ENCRYPT;
}
- clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
if (conn->state == BT_CONFIG) {
if (!ev->status)
@@ -1874,7 +2065,7 @@ static inline void hci_change_link_key_complete_evt(struct hci_dev *hdev, struct
if (!ev->status)
conn->link_mode |= HCI_LM_SECURE;
- clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
+ clear_bit(HCI_CONN_AUTH_PEND, &conn->flags);
hci_key_change_cfm(conn, ev->status);
}
@@ -1916,7 +2107,10 @@ static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff
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;
@@ -2024,10 +2218,6 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_host_buffer_size(hdev, skb);
break;
- case HCI_OP_READ_SSP_MODE:
- hci_cc_read_ssp_mode(hdev, skb);
- break;
-
case HCI_OP_WRITE_SSP_MODE:
hci_cc_write_ssp_mode(hdev, skb);
break;
@@ -2213,8 +2403,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
break;
case HCI_OP_DISCONNECT:
- if (ev->status != 0)
- mgmt_disconnect_failed(hdev, NULL, ev->status);
+ hci_cs_disconnect(hdev, ev->status);
break;
case HCI_OP_LE_CREATE_CONN:
@@ -2258,7 +2447,7 @@ static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb
conn->link_mode |= HCI_LM_MASTER;
}
- clear_bit(HCI_CONN_RSWITCH_PEND, &conn->pend);
+ clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
hci_role_switch_cfm(conn, ev->status, ev->role);
}
@@ -2332,6 +2521,56 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s
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;
@@ -2346,14 +2585,14 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb
conn->mode = ev->mode;
conn->interval = __le16_to_cpu(ev->interval);
- if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
+ if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) {
if (conn->mode == HCI_CM_ACTIVE)
- conn->power_save = 1;
+ set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
else
- conn->power_save = 0;
+ clear_bit(HCI_CONN_POWER_SAVE, &conn->flags);
}
- if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend))
+ if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags))
hci_sco_setup(conn, ev->status);
}
@@ -2379,10 +2618,10 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff
hci_conn_put(conn);
}
- if (!test_bit(HCI_PAIRABLE, &hdev->flags))
+ 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->flags)) {
+ else if (test_bit(HCI_MGMT, &hdev->dev_flags)) {
u8 secure;
if (conn->pending_sec_level == BT_SECURITY_HIGH)
@@ -2406,7 +2645,7 @@ static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s", hdev->name);
- if (!test_bit(HCI_LINK_KEYS, &hdev->flags))
+ if (!test_bit(HCI_LINK_KEYS, &hdev->dev_flags))
return;
hci_dev_lock(hdev);
@@ -2421,7 +2660,7 @@ static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s found key type %u for %s", hdev->name, key->type,
batostr(&ev->bdaddr));
- if (!test_bit(HCI_DEBUG_KEYS, &hdev->flags) &&
+ 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;
@@ -2483,7 +2722,7 @@ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff
hci_conn_put(conn);
}
- if (test_bit(HCI_LINK_KEYS, &hdev->flags))
+ 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);
@@ -2551,6 +2790,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
{
struct inquiry_data data;
int num_rsp = *((__u8 *) skb->data);
+ bool name_known, ssp;
BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
@@ -2572,10 +2812,12 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
data.ssp_mode = 0x00;
- hci_inquiry_cache_update(hdev, &data);
+
+ name_known = hci_inquiry_cache_update(hdev, &data,
+ false, &ssp);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, info->rssi,
- NULL);
+ info->dev_class, info->rssi,
+ !name_known, ssp, NULL, 0);
}
} else {
struct inquiry_info_with_rssi *info = (void *) (skb->data + 1);
@@ -2589,10 +2831,11 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
data.ssp_mode = 0x00;
- hci_inquiry_cache_update(hdev, &data);
+ name_known = hci_inquiry_cache_update(hdev, &data,
+ false, &ssp);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, info->rssi,
- NULL);
+ info->dev_class, info->rssi,
+ !name_known, ssp, NULL, 0);
}
}
@@ -2617,9 +2860,10 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b
ie = hci_inquiry_cache_lookup(hdev, &conn->dst);
if (ie)
- ie->data.ssp_mode = (ev->features[0] & 0x01);
+ ie->data.ssp_mode = (ev->features[0] & LMP_HOST_SSP);
- conn->ssp_mode = (ev->features[0] & 0x01);
+ if (ev->features[0] & LMP_HOST_SSP)
+ set_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
}
if (conn->state != BT_CONFIG)
@@ -2631,7 +2875,10 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b
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;
@@ -2724,6 +2971,8 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
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;
@@ -2732,9 +2981,19 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
data.ssp_mode = 0x01;
- hci_inquiry_cache_update(hdev, &data);
+
+ 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);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, info->rssi, info->data);
+ info->dev_class, info->rssi, !name_known,
+ ssp, info->data, sizeof(info->data));
}
hci_dev_unlock(hdev);
@@ -2774,19 +3033,22 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff
hci_conn_hold(conn);
- if (!test_bit(HCI_MGMT, &hdev->flags))
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags))
goto unlock;
- if (test_bit(HCI_PAIRABLE, &hdev->flags) ||
+ 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);
- cp.capability = conn->io_capability;
+ /* 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 == 0x01 || conn->remote_oob == 0x01) &&
+ if ((conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)) &&
hci_find_remote_oob_data(hdev, &conn->dst))
cp.oob_data = 0x01;
else
@@ -2822,8 +3084,9 @@ static inline void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *s
goto unlock;
conn->remote_cap = ev->capability;
- conn->remote_oob = ev->oob_data;
conn->remote_auth = ev->authentication;
+ if (ev->oob_data)
+ set_bit(HCI_CONN_REMOTE_OOB, &conn->flags);
unlock:
hci_dev_unlock(hdev);
@@ -2840,7 +3103,7 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
hci_dev_lock(hdev);
- if (!test_bit(HCI_MGMT, &hdev->flags))
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags))
goto unlock;
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
@@ -2869,7 +3132,7 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
/* 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->pend)) {
+ if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
BT_DBG("Confirming auto-accept as acceptor");
confirm_hint = 1;
goto confirm;
@@ -2890,8 +3153,8 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
}
confirm:
- mgmt_user_confirm_request(hdev, &ev->bdaddr, ev->passkey,
- confirm_hint);
+ mgmt_user_confirm_request(hdev, &ev->bdaddr, ACL_LINK, 0, ev->passkey,
+ confirm_hint);
unlock:
hci_dev_unlock(hdev);
@@ -2906,8 +3169,8 @@ static inline void hci_user_passkey_request_evt(struct hci_dev *hdev,
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_user_passkey_request(hdev, &ev->bdaddr);
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_user_passkey_request(hdev, &ev->bdaddr, ACL_LINK, 0);
hci_dev_unlock(hdev);
}
@@ -2930,8 +3193,9 @@ static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_
* 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->pend) && ev->status != 0)
- mgmt_auth_failed(hdev, &conn->dst, ev->status);
+ 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);
@@ -2950,13 +3214,13 @@ static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_
ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr);
if (ie)
- ie->data.ssp_mode = (ev->features[0] & 0x01);
+ 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 sk_buff *skb)
{
struct hci_ev_remote_oob_data_request *ev = (void *) skb->data;
struct oob_data *data;
@@ -2965,7 +3229,7 @@ static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
hci_dev_lock(hdev);
- if (!test_bit(HCI_MGMT, &hdev->flags))
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags))
goto unlock;
data = hci_find_remote_oob_data(hdev, &ev->bdaddr);
@@ -3020,7 +3284,9 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
goto unlock;
}
- mgmt_connected(hdev, &ev->bdaddr, conn->type, conn->dst_type);
+ 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);
@@ -3040,6 +3306,7 @@ static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
{
u8 num_reports = skb->data[0];
void *ptr = &skb->data[1];
+ s8 rssi;
hci_dev_lock(hdev);
@@ -3048,6 +3315,10 @@ static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
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;
}
@@ -3061,7 +3332,7 @@ static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
struct hci_cp_le_ltk_reply cp;
struct hci_cp_le_ltk_neg_reply neg;
struct hci_conn *conn;
- struct link_key *ltk;
+ struct smp_ltk *ltk;
BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle));
@@ -3077,10 +3348,17 @@ static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
memcpy(cp.ltk, ltk->val, sizeof(ltk->val));
cp.handle = cpu_to_le16(conn->handle);
- conn->pin_length = ltk->pin_len;
+
+ 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(&ltk->list);
+ kfree(ltk);
+ }
+
hci_dev_unlock(hdev);
return;
@@ -3271,6 +3549,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
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;
@@ -3279,34 +3561,3 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
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, NULL);
- kfree_skb(skb);
-}
-
-module_param(enable_le, bool, 0644);
-MODULE_PARM_DESC(enable_le, "Enable LE support");
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 0dcc9626677..63afd234283 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -48,8 +48,9 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci_mon.h>
-static bool enable_mgmt;
+static atomic_t monitor_promisc = ATOMIC_INIT(0);
/* ----- HCI socket interface ----- */
@@ -85,22 +86,20 @@ static struct bt_sock_list hci_sk_list = {
};
/* Send frame to RAW socket */
-void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb,
- struct sock *skip_sk)
+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 == skip_sk)
- continue;
-
if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev)
continue;
@@ -108,12 +107,9 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb,
if (skb->sk == sk)
continue;
- if (bt_cb(skb)->channel != hci_pi(sk)->channel)
+ if (hci_pi(sk)->channel != HCI_CHANNEL_RAW)
continue;
- if (bt_cb(skb)->channel == HCI_CHANNEL_CONTROL)
- goto clone;
-
/* Apply filter */
flt = &hci_pi(sk)->filter;
@@ -137,21 +133,303 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb,
continue;
}
-clone:
+ 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;
- /* Put type byte before the data */
- if (bt_cb(skb)->channel == HCI_CHANNEL_RAW)
- memcpy(skb_push(nskb, 1), &bt_cb(nskb)->pkt_type, 1);
+ 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);
+}
+
+/* Generate internal stack event */
+static 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);
+}
+
+void hci_sock_dev_event(struct hci_dev *hdev, int event)
+{
+ struct hci_ev_si_device ev;
+
+ BT_DBG("hdev %s event %d", hdev->name, event);
+
+ /* Send event to monitor */
+ if (atomic_read(&monitor_promisc)) {
+ struct sk_buff *skb;
+
+ skb = create_monitor_event(hdev, event);
+ if (skb) {
+ send_monitor_event(skb);
+ kfree_skb(skb);
+ }
+ }
+
+ /* 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);
+ }
+}
+
static int hci_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
@@ -164,6 +442,9 @@ static int hci_sock_release(struct socket *sock)
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) {
@@ -190,7 +471,7 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
hci_dev_lock(hdev);
- err = hci_blacklist_add(hdev, &bdaddr);
+ err = hci_blacklist_add(hdev, &bdaddr, 0);
hci_dev_unlock(hdev);
@@ -207,7 +488,7 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
hci_dev_lock(hdev);
- err = hci_blacklist_del(hdev, &bdaddr);
+ err = hci_blacklist_del(hdev, &bdaddr, 0);
hci_dev_unlock(hdev);
@@ -340,34 +621,69 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le
if (haddr.hci_family != AF_BLUETOOTH)
return -EINVAL;
- if (haddr.hci_channel > HCI_CHANNEL_CONTROL)
- return -EINVAL;
-
- if (haddr.hci_channel == HCI_CHANNEL_CONTROL) {
- if (!enable_mgmt)
- return -EINVAL;
- set_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags);
- }
-
lock_sock(sk);
- if (sk->sk_state == BT_BOUND || hci_pi(sk)->hdev) {
+ if (sk->sk_state == BT_BOUND) {
err = -EALREADY;
goto done;
}
- if (haddr.hci_dev != HCI_DEV_NONE) {
- hdev = hci_dev_get(haddr.hci_dev);
- if (!hdev) {
- err = -ENODEV;
+ switch (haddr.hci_channel) {
+ case HCI_CHANNEL_RAW:
+ if (hci_pi(sk)->hdev) {
+ err = -EALREADY;
goto done;
}
- atomic_inc(&hdev->promisc);
+ 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;
- hci_pi(sk)->hdev = hdev;
sk->sk_state = BT_BOUND;
done:
@@ -461,7 +777,15 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
skb_reset_transport_header(skb);
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
- hci_sock_cmsg(sk, msg, skb);
+ 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);
@@ -495,6 +819,9 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
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;
@@ -574,6 +901,11 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char
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)) {
@@ -636,6 +968,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char
break;
}
+done:
release_sock(sk);
return err;
}
@@ -644,11 +977,20 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char
{
struct hci_ufilter uf;
struct sock *sk = sock->sk;
- int len, opt;
+ 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)
@@ -657,7 +999,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char
opt = 0;
if (put_user(opt, optval))
- return -EFAULT;
+ err = -EFAULT;
break;
case HCI_TIME_STAMP:
@@ -667,7 +1009,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char
opt = 0;
if (put_user(opt, optval))
- return -EFAULT;
+ err = -EFAULT;
break;
case HCI_FILTER:
@@ -682,15 +1024,17 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char
len = min_t(unsigned int, len, sizeof(uf));
if (copy_to_user(optval, &uf, len))
- return -EFAULT;
+ err = -EFAULT;
break;
default:
- return -ENOPROTOOPT;
+ err = -ENOPROTOOPT;
break;
}
- return 0;
+done:
+ release_sock(sk);
+ return err;
}
static const struct proto_ops hci_sock_ops = {
@@ -748,52 +1092,12 @@ static int hci_sock_create(struct net *net, struct socket *sock, int protocol,
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;
@@ -806,8 +1110,6 @@ int __init hci_sock_init(void)
if (err < 0)
goto error;
- hci_register_notifier(&hci_sock_nblock);
-
BT_INFO("HCI socket layer initialized");
return 0;
@@ -823,10 +1125,5 @@ 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);
}
-
-module_param(enable_mgmt, bool, 0644);
-MODULE_PARM_DESC(enable_mgmt, "Enable Management interface");
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index 52109561423..bc154298979 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -33,19 +33,19 @@ static inline char *link_typetostr(int type)
static ssize_t show_link_type(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_conn *conn = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ 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],
@@ -79,8 +79,8 @@ static const struct attribute_group *bt_link_groups[] = {
static void bt_link_release(struct device *dev)
{
- void *data = dev_get_drvdata(dev);
- kfree(data);
+ struct hci_conn *conn = to_hci_conn(dev);
+ kfree(conn);
}
static struct device_type bt_link = {
@@ -120,8 +120,6 @@ void hci_conn_add_sysfs(struct hci_conn *conn)
dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle);
- dev_set_drvdata(&conn->dev, conn);
-
if (device_add(&conn->dev) < 0) {
BT_ERR("Failed to register connection device");
return;
@@ -189,19 +187,19 @@ static inline char *host_typetostr(int type)
static ssize_t show_bus(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_dev *hdev = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ struct hci_dev *hdev = to_hci_dev(dev);
char name[HCI_MAX_NAME_LENGTH + 1];
int i;
@@ -214,20 +212,20 @@ static ssize_t show_name(struct device *dev, struct device_attribute *attr, char
static ssize_t show_class(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_dev *hdev = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ 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],
@@ -238,31 +236,31 @@ static ssize_t show_features(struct device *dev, struct device_attribute *attr,
static ssize_t show_manufacturer(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_dev *hdev = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ struct hci_dev *hdev = to_hci_dev(dev);
unsigned int val;
int rv;
@@ -280,13 +278,13 @@ static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *a
static ssize_t show_sniff_max_interval(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_dev *hdev = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ struct hci_dev *hdev = to_hci_dev(dev);
u16 val;
int rv;
@@ -304,13 +302,13 @@ static ssize_t store_sniff_max_interval(struct device *dev, struct device_attrib
static ssize_t show_sniff_min_interval(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_dev *hdev = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ struct hci_dev *hdev = to_hci_dev(dev);
u16 val;
int rv;
@@ -370,8 +368,9 @@ static const struct attribute_group *bt_host_groups[] = {
static void bt_host_release(struct device *dev)
{
- void *data = dev_get_drvdata(dev);
- kfree(data);
+ struct hci_dev *hdev = to_hci_dev(dev);
+ kfree(hdev);
+ module_put(THIS_MODULE);
}
static struct device_type bt_host = {
@@ -383,12 +382,12 @@ static struct device_type bt_host = {
static int inquiry_cache_show(struct seq_file *f, void *p)
{
struct hci_dev *hdev = f->private;
- struct inquiry_cache *cache = &hdev->inq_cache;
+ struct discovery_state *cache = &hdev->discovery;
struct inquiry_entry *e;
hci_dev_lock(hdev);
- for (e = cache->list; e; e = e->next) {
+ 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),
@@ -523,7 +522,7 @@ void hci_init_sysfs(struct hci_dev *hdev)
dev->type = &bt_host;
dev->class = bt_class;
- dev_set_drvdata(dev, hdev);
+ __module_get(THIS_MODULE);
device_initialize(dev);
}
diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c
index 178ac7f127a..73a32d705c1 100644
--- a/net/bluetooth/hidp/sock.c
+++ b/net/bluetooth/hidp/sock.c
@@ -160,10 +160,10 @@ static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne
{
if (cmd == HIDPGETCONNLIST) {
struct hidp_connlist_req cl;
- uint32_t uci;
+ u32 uci;
int err;
- if (get_user(cl.cnum, (uint32_t __user *) arg) ||
+ if (get_user(cl.cnum, (u32 __user *) arg) ||
get_user(uci, (u32 __user *) (arg + 4)))
return -EFAULT;
@@ -174,7 +174,7 @@ static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne
err = hidp_get_connlist(&cl);
- if (!err && put_user(cl.cnum, (uint32_t __user *) arg))
+ if (!err && put_user(cl.cnum, (u32 __user *) arg))
err = -EFAULT;
return err;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 32d338c30e6..3e450f4a312 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -73,42 +73,28 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data);
static void l2cap_send_disconn_req(struct l2cap_conn *conn,
struct l2cap_chan *chan, int err);
-static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb);
-
/* ---- L2CAP channels ---- */
static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
{
- struct l2cap_chan *c, *r = NULL;
-
- rcu_read_lock();
+ struct l2cap_chan *c;
- list_for_each_entry_rcu(c, &conn->chan_l, list) {
- if (c->dcid == cid) {
- r = c;
- break;
- }
+ list_for_each_entry(c, &conn->chan_l, list) {
+ if (c->dcid == cid)
+ return c;
}
-
- rcu_read_unlock();
- return r;
+ return NULL;
}
static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
{
- struct l2cap_chan *c, *r = NULL;
-
- rcu_read_lock();
+ struct l2cap_chan *c;
- list_for_each_entry_rcu(c, &conn->chan_l, list) {
- if (c->scid == cid) {
- r = c;
- break;
- }
+ list_for_each_entry(c, &conn->chan_l, list) {
+ if (c->scid == cid)
+ return c;
}
-
- rcu_read_unlock();
- return r;
+ return NULL;
}
/* Find channel with given SCID.
@@ -117,36 +103,32 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 ci
{
struct l2cap_chan *c;
+ mutex_lock(&conn->chan_lock);
c = __l2cap_get_chan_by_scid(conn, cid);
- if (c)
- lock_sock(c->sk);
+ 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, *r = NULL;
-
- rcu_read_lock();
+ struct l2cap_chan *c;
- list_for_each_entry_rcu(c, &conn->chan_l, list) {
- if (c->ident == ident) {
- r = c;
- break;
- }
+ list_for_each_entry(c, &conn->chan_l, list) {
+ if (c->ident == ident)
+ return c;
}
-
- rcu_read_unlock();
- return r;
+ 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);
- if (c)
- lock_sock(c->sk);
+ mutex_unlock(&conn->chan_lock);
+
return c;
}
@@ -217,51 +199,51 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
return 0;
}
-static char *state_to_string(int state)
+static void __l2cap_state_change(struct l2cap_chan *chan, int state)
{
- switch(state) {
- case BT_CONNECTED:
- return "BT_CONNECTED";
- case BT_OPEN:
- return "BT_OPEN";
- case BT_BOUND:
- return "BT_BOUND";
- case BT_LISTEN:
- return "BT_LISTEN";
- case BT_CONNECT:
- return "BT_CONNECT";
- case BT_CONNECT2:
- return "BT_CONNECT2";
- case BT_CONFIG:
- return "BT_CONFIG";
- case BT_DISCONN:
- return "BT_DISCONN";
- case BT_CLOSED:
- return "BT_CLOSED";
- }
+ BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state),
+ state_to_string(state));
- return "invalid state";
+ chan->state = state;
+ chan->ops->state_change(chan->data, state);
}
static void l2cap_state_change(struct l2cap_chan *chan, int state)
{
- BT_DBG("%p %s -> %s", chan, state_to_string(chan->state),
- state_to_string(state));
+ struct sock *sk = chan->sk;
- chan->state = state;
- chan->ops->state_change(chan->data, state);
+ 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 sock *sk = chan->sk;
+ struct l2cap_conn *conn = chan->conn;
int reason;
- BT_DBG("chan %p state %d", chan, chan->state);
+ BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
- lock_sock(sk);
+ mutex_lock(&conn->chan_lock);
+ l2cap_chan_lock(chan);
if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
reason = ECONNREFUSED;
@@ -273,9 +255,11 @@ static void l2cap_chan_timeout(struct work_struct *work)
l2cap_chan_close(chan, reason);
- release_sock(sk);
+ l2cap_chan_unlock(chan);
chan->ops->close(chan->data);
+ mutex_unlock(&conn->chan_lock);
+
l2cap_chan_put(chan);
}
@@ -287,6 +271,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk)
if (!chan)
return NULL;
+ mutex_init(&chan->lock);
+
chan->sk = sk;
write_lock(&chan_list_lock);
@@ -313,7 +299,7 @@ void l2cap_chan_destroy(struct l2cap_chan *chan)
l2cap_chan_put(chan);
}
-static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
+void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
chan->psm, chan->dcid);
@@ -322,7 +308,8 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->conn = conn;
- if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) {
+ switch (chan->chan_type) {
+ case L2CAP_CHAN_CONN_ORIENTED:
if (conn->hcon->type == LE_LINK) {
/* LE connection */
chan->omtu = L2CAP_LE_DEFAULT_MTU;
@@ -333,12 +320,16 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->scid = l2cap_alloc_cid(conn);
chan->omtu = L2CAP_DEFAULT_MTU;
}
- } else if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
+ 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;
- } else {
+ break;
+
+ default:
/* Raw socket can send/recv signalling messages only */
chan->scid = L2CAP_CID_SIGNALING;
chan->dcid = L2CAP_CID_SIGNALING;
@@ -354,11 +345,16 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
l2cap_chan_hold(chan);
- list_add_rcu(&chan->list, &conn->chan_l);
+ 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);
}
-/* Delete channel.
- * Must be called on the locked socket. */
static void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
struct sock *sk = chan->sk;
@@ -371,8 +367,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
if (conn) {
/* Delete from channel list */
- list_del_rcu(&chan->list);
- synchronize_rcu();
+ list_del(&chan->list);
l2cap_chan_put(chan);
@@ -380,11 +375,13 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
hci_conn_put(conn->hcon);
}
- l2cap_state_change(chan, BT_CLOSED);
+ lock_sock(sk);
+
+ __l2cap_state_change(chan, BT_CLOSED);
sock_set_flag(sk, SOCK_ZAPPED);
if (err)
- sk->sk_err = err;
+ __l2cap_chan_set_err(chan, err);
if (parent) {
bt_accept_unlink(sk);
@@ -392,6 +389,8 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
} 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;
@@ -423,10 +422,12 @@ static void l2cap_chan_cleanup_listen(struct sock *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);
- lock_sock(sk);
l2cap_chan_close(chan, ECONNRESET);
- release_sock(sk);
+ l2cap_chan_unlock(chan);
+
chan->ops->close(chan->data);
}
}
@@ -436,14 +437,17 @@ 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 %d socket %p", chan, chan->state, sk->sk_socket);
+ 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);
+ __l2cap_state_change(chan, BT_CLOSED);
sock_set_flag(sk, SOCK_ZAPPED);
+ release_sock(sk);
break;
case BT_CONNECTED:
@@ -486,7 +490,9 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
break;
default:
+ lock_sock(sk);
sock_set_flag(sk, SOCK_ZAPPED);
+ release_sock(sk);
break;
}
}
@@ -661,6 +667,21 @@ 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;
@@ -670,17 +691,8 @@ static void l2cap_do_start(struct l2cap_chan *chan)
return;
if (l2cap_chan_check_security(chan) &&
- __l2cap_no_conn_pending(chan)) {
- 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);
- }
+ __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);
@@ -688,8 +700,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
conn->info_ident = l2cap_get_ident(conn);
- schedule_delayed_work(&conn->info_timer,
- msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
+ schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
l2cap_send_cmd(conn, conn->info_ident,
L2CAP_INFO_REQ, sizeof(req), &req);
@@ -714,14 +725,12 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err)
{
- struct sock *sk;
+ struct sock *sk = chan->sk;
struct l2cap_disconn_req req;
if (!conn)
return;
- sk = chan->sk;
-
if (chan->mode == L2CAP_MODE_ERTM) {
__clear_retrans_timer(chan);
__clear_monitor_timer(chan);
@@ -733,56 +742,47 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req);
- l2cap_state_change(chan, BT_DISCONN);
- sk->sk_err = err;
+ 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;
+ struct l2cap_chan *chan, *tmp;
BT_DBG("conn %p", conn);
- rcu_read_lock();
+ mutex_lock(&conn->chan_lock);
- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
+ list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
struct sock *sk = chan->sk;
- bh_lock_sock(sk);
+ l2cap_chan_lock(chan);
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}
if (chan->state == BT_CONNECT) {
- struct l2cap_conn_req req;
-
if (!l2cap_chan_check_security(chan) ||
!__l2cap_no_conn_pending(chan)) {
- bh_unlock_sock(sk);
+ 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() calls list_del(chan)
- * so release the lock */
l2cap_chan_close(chan, ECONNRESET);
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}
- 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);
+ l2cap_send_conn_req(chan);
} else if (chan->state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp;
@@ -791,6 +791,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
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);
@@ -799,10 +800,11 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
parent->sk_data_ready(parent, 0);
} else {
- l2cap_state_change(chan, BT_CONFIG);
+ __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);
@@ -813,7 +815,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
if (test_bit(CONF_REQ_SENT, &chan->conf_state) ||
rsp.result != L2CAP_CR_SUCCESS) {
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}
@@ -823,10 +825,10 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
chan->num_conf_req++;
}
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
}
- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
}
/* Find socket with cid and source bdaddr.
@@ -902,28 +904,34 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
__set_chan_timer(chan, sk->sk_sndtimeo);
- l2cap_state_change(chan, BT_CONNECTED);
+ __l2cap_state_change(chan, BT_CONNECTED);
parent->sk_data_ready(parent, 0);
clean:
release_sock(parent);
}
-static void l2cap_chan_ready(struct sock *sk)
+static void l2cap_chan_ready(struct l2cap_chan *chan)
{
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
- struct sock *parent = bt_sk(sk)->parent;
+ 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);
+ __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)
@@ -938,29 +946,31 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
if (conn->hcon->out && conn->hcon->type == LE_LINK)
smp_conn_security(conn, conn->hcon->pending_sec_level);
- rcu_read_lock();
+ mutex_lock(&conn->chan_lock);
- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
- struct sock *sk = chan->sk;
+ list_for_each_entry(chan, &conn->chan_l, list) {
- bh_lock_sock(sk);
+ l2cap_chan_lock(chan);
if (conn->hcon->type == LE_LINK) {
if (smp_conn_security(conn, chan->sec_level))
- l2cap_chan_ready(sk);
+ l2cap_chan_ready(chan);
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+ struct sock *sk = chan->sk;
__clear_chan_timer(chan);
- l2cap_state_change(chan, BT_CONNECTED);
+ 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);
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
}
- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
}
/* Notify sockets that we cannot guaranty reliability anymore */
@@ -970,16 +980,14 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
BT_DBG("conn %p", conn);
- rcu_read_lock();
-
- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
- struct sock *sk = chan->sk;
+ mutex_lock(&conn->chan_lock);
+ list_for_each_entry(chan, &conn->chan_l, list) {
if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
- sk->sk_err = err;
+ __l2cap_chan_set_err(chan, err);
}
- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
}
static void l2cap_info_timeout(struct work_struct *work)
@@ -997,7 +1005,6 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
{
struct l2cap_conn *conn = hcon->l2cap_data;
struct l2cap_chan *chan, *l;
- struct sock *sk;
if (!conn)
return;
@@ -1006,21 +1013,27 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
kfree_skb(conn->rx_skb);
+ mutex_lock(&conn->chan_lock);
+
/* Kill channels */
list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
- sk = chan->sk;
- lock_sock(sk);
+ l2cap_chan_lock(chan);
+
l2cap_chan_del(chan, err);
- release_sock(sk);
+
+ 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->pend)) {
+ if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) {
cancel_delayed_work_sync(&conn->security_timer);
smp_chan_destroy(conn);
}
@@ -1072,6 +1085,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
conn->feat_mask = 0;
spin_lock_init(&conn->lock);
+ mutex_init(&conn->chan_lock);
INIT_LIST_HEAD(&conn->chan_l);
@@ -1139,7 +1153,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d
hci_dev_lock(hdev);
- lock_sock(sk);
+ l2cap_chan_lock(chan);
/* PSM must be odd and lsb of upper byte must be 0 */
if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid &&
@@ -1166,17 +1180,21 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d
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:
@@ -1186,11 +1204,15 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d
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;
@@ -1218,7 +1240,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d
/* 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);
@@ -1235,6 +1259,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d
err = 0;
done:
+ l2cap_chan_unlock(chan);
hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -1276,14 +1301,14 @@ static void l2cap_monitor_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
monitor_timer.work);
- struct sock *sk = chan->sk;
BT_DBG("chan %p", chan);
- lock_sock(sk);
+ l2cap_chan_lock(chan);
+
if (chan->retry_count >= chan->remote_max_tx) {
l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
- release_sock(sk);
+ l2cap_chan_unlock(chan);
return;
}
@@ -1291,25 +1316,26 @@ static void l2cap_monitor_timeout(struct work_struct *work)
__set_monitor_timer(chan);
l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
- release_sock(sk);
+ l2cap_chan_unlock(chan);
}
static void l2cap_retrans_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
retrans_timer.work);
- struct sock *sk = chan->sk;
BT_DBG("chan %p", chan);
- lock_sock(sk);
+ 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);
- release_sock(sk);
+
+ l2cap_chan_unlock(chan);
}
static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
@@ -1450,17 +1476,19 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
- if (bt_cb(skb)->retries == 1)
+ 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);
-
- nsent++;
}
return nsent;
@@ -1478,7 +1506,7 @@ static int l2cap_retransmit_frames(struct l2cap_chan *chan)
return ret;
}
-static void l2cap_send_ack(struct l2cap_chan *chan)
+static void __l2cap_send_ack(struct l2cap_chan *chan)
{
u32 control = 0;
@@ -1498,6 +1526,12 @@ static void l2cap_send_ack(struct l2cap_chan *chan)
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;
@@ -1512,9 +1546,11 @@ static void l2cap_send_srejtail(struct l2cap_chan *chan)
l2cap_send_sframe(chan, control);
}
-static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, int len, int count, struct sk_buff *skb)
+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 = l2cap_pi(sk)->chan->conn;
+ struct l2cap_conn *conn = chan->conn;
struct sk_buff **frag;
int err, sent = 0;
@@ -1529,7 +1565,10 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in
while (len) {
count = min_t(unsigned int, conn->mtu, len);
- *frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err);
+ *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))
@@ -1550,17 +1589,18 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len,
u32 priority)
{
- struct sock *sk = chan->sk;
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("sk %p len %d priority %u", sk, (int)len, priority);
+ BT_DBG("chan %p len %d priority %u", chan, (int)len, priority);
count = min_t(unsigned int, (conn->mtu - hlen), len);
- skb = bt_skb_send_alloc(sk, count + hlen,
- msg->msg_flags & MSG_DONTWAIT, &err);
+
+ skb = chan->ops->alloc_skb(chan, count + hlen,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+
if (!skb)
return ERR_PTR(err);
@@ -1572,7 +1612,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
put_unaligned_le16(chan->psm, skb_put(skb, 2));
- err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
+ err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
@@ -1584,17 +1624,18 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len,
u32 priority)
{
- struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen = L2CAP_HDR_SIZE;
struct l2cap_hdr *lh;
- BT_DBG("sk %p len %d", sk, (int)len);
+ BT_DBG("chan %p len %d", chan, (int)len);
count = min_t(unsigned int, (conn->mtu - hlen), len);
- skb = bt_skb_send_alloc(sk, count + hlen,
- msg->msg_flags & MSG_DONTWAIT, &err);
+
+ skb = chan->ops->alloc_skb(chan, count + hlen,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+
if (!skb)
return ERR_PTR(err);
@@ -1605,7 +1646,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
lh->cid = cpu_to_le16(chan->dcid);
lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
- err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
+ err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
@@ -1617,13 +1658,12 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len,
u32 control, u16 sdulen)
{
- struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen;
struct l2cap_hdr *lh;
- BT_DBG("sk %p len %d", sk, (int)len);
+ BT_DBG("chan %p len %d", chan, (int)len);
if (!conn)
return ERR_PTR(-ENOTCONN);
@@ -1640,8 +1680,10 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
hlen += L2CAP_FCS_SIZE;
count = min_t(unsigned int, (conn->mtu - hlen), len);
- skb = bt_skb_send_alloc(sk, count + hlen,
- msg->msg_flags & MSG_DONTWAIT, &err);
+
+ skb = chan->ops->alloc_skb(chan, count + hlen,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+
if (!skb)
return ERR_PTR(err);
@@ -1655,7 +1697,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
if (sdulen)
put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
- err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
+ err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
@@ -1801,9 +1843,9 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("conn %p", conn);
- rcu_read_lock();
+ mutex_lock(&conn->chan_lock);
- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
+ list_for_each_entry(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk;
if (chan->chan_type != L2CAP_CHAN_RAW)
continue;
@@ -1819,7 +1861,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
kfree_skb(nskb);
}
- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
}
/* ---- L2CAP signalling commands ---- */
@@ -1987,9 +2029,13 @@ static void l2cap_ack_timeout(struct work_struct *work)
BT_DBG("chan %p", chan);
- lock_sock(chan->sk);
- l2cap_send_ack(chan);
- release_sock(chan->sk);
+ 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)
@@ -2607,6 +2653,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
parent = pchan->sk;
+ mutex_lock(&conn->chan_lock);
lock_sock(parent);
/* Check if the ACL is secure enough (if not SDP) */
@@ -2647,7 +2694,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
bt_accept_enqueue(parent, sk);
- l2cap_chan_add(conn, chan);
+ __l2cap_chan_add(conn, chan);
dcid = chan->scid;
@@ -2658,28 +2705,29 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
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);
+ __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);
+ __l2cap_state_change(chan, BT_CONFIG);
result = L2CAP_CR_SUCCESS;
status = L2CAP_CS_NO_INFO;
}
} else {
- l2cap_state_change(chan, BT_CONNECT2);
+ __l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHEN_PEND;
}
} else {
- l2cap_state_change(chan, BT_CONNECT2);
+ __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);
@@ -2695,8 +2743,7 @@ sendresp:
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
conn->info_ident = l2cap_get_ident(conn);
- schedule_delayed_work(&conn->info_timer,
- msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
+ schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
l2cap_send_cmd(conn, conn->info_ident,
L2CAP_INFO_REQ, sizeof(info), &info);
@@ -2719,27 +2766,36 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;
u16 scid, dcid, result, status;
struct l2cap_chan *chan;
- struct sock *sk;
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);
+ 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)
- return -EFAULT;
+ 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)
- return -EFAULT;
+ chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
+ if (!chan) {
+ err = -EFAULT;
+ goto unlock;
+ }
}
- sk = chan->sk;
+ err = 0;
+
+ l2cap_chan_lock(chan);
switch (result) {
case L2CAP_CR_SUCCESS:
@@ -2765,8 +2821,12 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
break;
}
- release_sock(sk);
- return 0;
+ l2cap_chan_unlock(chan);
+
+unlock:
+ mutex_unlock(&conn->chan_lock);
+
+ return err;
}
static inline void set_default_fcs(struct l2cap_chan *chan)
@@ -2786,7 +2846,6 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
u16 dcid, flags;
u8 rsp[64];
struct l2cap_chan *chan;
- struct sock *sk;
int len;
dcid = __le16_to_cpu(req->dcid);
@@ -2798,7 +2857,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (!chan)
return -ENOENT;
- sk = chan->sk;
+ l2cap_chan_lock(chan);
if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
struct l2cap_cmd_rej_cid rej;
@@ -2860,7 +2919,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (chan->mode == L2CAP_MODE_ERTM)
l2cap_ertm_init(chan);
- l2cap_chan_ready(sk);
+ l2cap_chan_ready(chan);
goto unlock;
}
@@ -2887,7 +2946,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
}
unlock:
- release_sock(sk);
+ l2cap_chan_unlock(chan);
return 0;
}
@@ -2896,7 +2955,6 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data;
u16 scid, flags, result;
struct l2cap_chan *chan;
- struct sock *sk;
int len = cmd->len - sizeof(*rsp);
scid = __le16_to_cpu(rsp->scid);
@@ -2910,7 +2968,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (!chan)
return 0;
- sk = chan->sk;
+ l2cap_chan_lock(chan);
switch (result) {
case L2CAP_CONF_SUCCESS:
@@ -2969,9 +3027,9 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
}
default:
- sk->sk_err = ECONNRESET;
- __set_chan_timer(chan,
- msecs_to_jiffies(L2CAP_DISC_REJ_TIMEOUT));
+ l2cap_chan_set_err(chan, ECONNRESET);
+
+ __set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT);
l2cap_send_disconn_req(conn, chan, ECONNRESET);
goto done;
}
@@ -2991,11 +3049,11 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (chan->mode == L2CAP_MODE_ERTM)
l2cap_ertm_init(chan);
- l2cap_chan_ready(sk);
+ l2cap_chan_ready(chan);
}
done:
- release_sock(sk);
+ l2cap_chan_unlock(chan);
return 0;
}
@@ -3012,9 +3070,15 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
- chan = l2cap_get_chan_by_scid(conn, dcid);
- if (!chan)
+ 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;
@@ -3022,12 +3086,18 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
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);
- release_sock(sk);
+
+ l2cap_chan_unlock(chan);
chan->ops->close(chan->data);
+
+ mutex_unlock(&conn->chan_lock);
+
return 0;
}
@@ -3036,23 +3106,30 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data;
u16 dcid, scid;
struct l2cap_chan *chan;
- struct sock *sk;
scid = __le16_to_cpu(rsp->scid);
dcid = __le16_to_cpu(rsp->dcid);
BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
- chan = l2cap_get_chan_by_scid(conn, scid);
- if (!chan)
+ mutex_lock(&conn->chan_lock);
+
+ chan = __l2cap_get_chan_by_scid(conn, scid);
+ if (!chan) {
+ mutex_unlock(&conn->chan_lock);
return 0;
+ }
- sk = chan->sk;
+ l2cap_chan_lock(chan);
l2cap_chan_del(chan, 0);
- release_sock(sk);
+
+ l2cap_chan_unlock(chan);
chan->ops->close(chan->data);
+
+ mutex_unlock(&conn->chan_lock);
+
return 0;
}
@@ -3132,7 +3209,8 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
return 0;
}
- if (type == L2CAP_IT_FEAT_MASK) {
+ switch (type) {
+ case L2CAP_IT_FEAT_MASK:
conn->feat_mask = get_unaligned_le32(rsp->data);
if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) {
@@ -3149,11 +3227,15 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
l2cap_conn_start(conn);
}
- } else if (type == L2CAP_IT_FIXED_CHAN) {
+ 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;
@@ -3713,19 +3795,11 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u3
static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
{
- u32 control;
-
BT_DBG("chan %p, Enter local busy", chan);
set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
- control = __set_reqseq(chan, chan->buffer_seq);
- control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
- l2cap_send_sframe(chan, control);
-
- set_bit(CONN_RNR_SENT, &chan->conn_state);
-
- __clear_ack_timer(chan);
+ __set_ack_timer(chan);
}
static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan)
@@ -3865,8 +3939,11 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
goto drop;
}
- if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state))
+ 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;
@@ -3927,15 +4004,15 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
__skb_queue_head_init(&chan->srej_q);
l2cap_add_to_srej_queue(chan, skb, tx_seq, sar);
- set_bit(CONN_SEND_PBIT, &chan->conn_state);
+ /* 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;
}
-
- __clear_ack_timer(chan);
}
return 0;
@@ -4135,9 +4212,8 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_cont
return 0;
}
-static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
+static int l2cap_ertm_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
{
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
u32 control;
u16 req_seq;
int len, next_tx_seq_offset, req_seq_offset;
@@ -4205,7 +4281,6 @@ drop:
static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb)
{
struct l2cap_chan *chan;
- struct sock *sk = NULL;
u32 control;
u16 tx_seq;
int len;
@@ -4213,10 +4288,12 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
chan = l2cap_get_chan_by_scid(conn, cid);
if (!chan) {
BT_DBG("unknown cid 0x%4.4x", cid);
- goto drop;
+ /* Drop packet and return */
+ kfree_skb(skb);
+ return 0;
}
- sk = chan->sk;
+ l2cap_chan_lock(chan);
BT_DBG("chan %p, len %d", chan, skb->len);
@@ -4238,7 +4315,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
break;
case L2CAP_MODE_ERTM:
- l2cap_ertm_data_rcv(sk, skb);
+ l2cap_ertm_data_rcv(chan, skb);
goto done;
@@ -4287,26 +4364,20 @@ drop:
kfree_skb(skb);
done:
- if (sk)
- release_sock(sk);
+ l2cap_chan_unlock(chan);
return 0;
}
static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb)
{
- struct sock *sk = NULL;
struct l2cap_chan *chan;
chan = l2cap_global_chan_by_psm(0, psm, conn->src);
if (!chan)
goto drop;
- sk = chan->sk;
-
- lock_sock(sk);
-
- BT_DBG("sk %p, len %d", sk, skb->len);
+ BT_DBG("chan %p, len %d", chan, skb->len);
if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
goto drop;
@@ -4315,31 +4386,23 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str
goto drop;
if (!chan->ops->recv(chan->data, skb))
- goto done;
+ return 0;
drop:
kfree_skb(skb);
-done:
- if (sk)
- release_sock(sk);
return 0;
}
static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct sk_buff *skb)
{
- struct sock *sk = NULL;
struct l2cap_chan *chan;
chan = l2cap_global_chan_by_scid(0, cid, conn->src);
if (!chan)
goto drop;
- sk = chan->sk;
-
- lock_sock(sk);
-
- BT_DBG("sk %p, len %d", sk, skb->len);
+ BT_DBG("chan %p, len %d", chan, skb->len);
if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
goto drop;
@@ -4348,14 +4411,11 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct
goto drop;
if (!chan->ops->recv(chan->data, skb))
- goto done;
+ return 0;
drop:
kfree_skb(skb);
-done:
- if (sk)
- release_sock(sk);
return 0;
}
@@ -4479,8 +4539,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
if (encrypt == 0x00) {
if (chan->sec_level == BT_SECURITY_MEDIUM) {
__clear_chan_timer(chan);
- __set_chan_timer(chan,
- msecs_to_jiffies(L2CAP_ENC_TIMEOUT));
+ __set_chan_timer(chan, L2CAP_ENC_TIMEOUT);
} else if (chan->sec_level == BT_SECURITY_HIGH)
l2cap_chan_close(chan, ECONNREFUSED);
} else {
@@ -4504,57 +4563,49 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
cancel_delayed_work(&conn->security_timer);
}
- rcu_read_lock();
-
- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
- struct sock *sk = chan->sk;
+ mutex_lock(&conn->chan_lock);
- bh_lock_sock(sk);
+ 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(sk);
+ l2cap_chan_ready(chan);
}
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}
if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) {
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}
if (!status && (chan->state == BT_CONNECTED ||
chan->state == BT_CONFIG)) {
l2cap_check_encryption(chan, encrypt);
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}
if (chan->state == BT_CONNECT) {
if (!status) {
- 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);
+ l2cap_send_conn_req(chan);
} else {
__clear_chan_timer(chan);
- __set_chan_timer(chan,
- msecs_to_jiffies(L2CAP_DISC_TIMEOUT));
+ __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;
@@ -4563,18 +4614,19 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (parent)
parent->sk_data_ready(parent, 0);
} else {
- l2cap_state_change(chan, BT_CONFIG);
+ __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,
- msecs_to_jiffies(L2CAP_DISC_TIMEOUT));
+ __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);
@@ -4583,10 +4635,10 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
sizeof(rsp), &rsp);
}
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
}
- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
return 0;
}
@@ -4647,6 +4699,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
if (chan && chan->sk) {
struct sock *sk = chan->sk;
+ lock_sock(sk);
if (chan->imtu < len - L2CAP_HDR_SIZE) {
BT_ERR("Frame exceeding recv MTU (len %d, "
@@ -4717,7 +4770,7 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p)
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);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 401d9428ae4..c4fe583b0af 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -125,13 +125,15 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
err = l2cap_chan_connect(chan, la.l2_psm, la.l2_cid, &la.l2_bdaddr);
if (err)
- goto done;
+ return err;
+
+ lock_sock(sk);
err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK));
-done:
- if (sock_owned_by_user(sk))
- release_sock(sk);
+
+ release_sock(sk);
+
return err;
}
@@ -783,7 +785,7 @@ static void l2cap_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);
+ BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
/* Kill poor orphan */
@@ -795,7 +797,8 @@ static void l2cap_sock_kill(struct sock *sk)
static int l2cap_sock_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ struct l2cap_chan *chan;
+ struct l2cap_conn *conn;
int err = 0;
BT_DBG("sock %p, sk %p", sock, sk);
@@ -803,13 +806,24 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
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,
@@ -820,6 +834,11 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
err = -sk->sk_err;
release_sock(sk);
+ l2cap_chan_unlock(chan);
+
+ if (conn)
+ mutex_unlock(&conn->chan_lock);
+
return err;
}
@@ -862,8 +881,12 @@ static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
struct sock *sk = data;
struct l2cap_pinfo *pi = l2cap_pi(sk);
- if (pi->rx_busy_skb)
- return -ENOMEM;
+ lock_sock(sk);
+
+ if (pi->rx_busy_skb) {
+ err = -ENOMEM;
+ goto done;
+ }
err = sock_queue_rcv_skb(sk, skb);
@@ -882,6 +905,9 @@ static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
err = 0;
}
+done:
+ release_sock(sk);
+
return err;
}
@@ -899,12 +925,22 @@ static void l2cap_sock_state_change_cb(void *data, int state)
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 sock *sk = chan->sk;
+
+ return bt_skb_send_alloc(sk, len, nb, err);
+}
+
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)
@@ -1004,7 +1040,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
sk->sk_destruct = l2cap_sock_destruct;
- sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT);
+ sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT;
sock_reset_flag(sk, SOCK_ZAPPED);
diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c
index 86a6bed229d..506628876f3 100644
--- a/net/bluetooth/lib.c
+++ b/net/bluetooth/lib.c
@@ -24,6 +24,8 @@
/* Bluetooth kernel library. */
+#define pr_fmt(fmt) "Bluetooth: " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
@@ -151,7 +153,26 @@ int bt_to_errno(__u16 code)
}
EXPORT_SYMBOL(bt_to_errno);
-int bt_printk(const char *level, const char *format, ...)
+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;
@@ -162,10 +183,10 @@ int bt_printk(const char *level, const char *format, ...)
vaf.fmt = format;
vaf.va = &args;
- r = printk("%sBluetooth: %pV\n", level, &vaf);
+ r = pr_err("%pV", &vaf);
va_end(args);
return r;
}
-EXPORT_SYMBOL(bt_printk);
+EXPORT_SYMBOL(bt_err);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index bc8e59dda78..7fcff888713 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1,6 +1,8 @@
/*
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
@@ -32,12 +34,92 @@
#include <net/bluetooth/mgmt.h>
#include <net/bluetooth/smp.h>
-#define MGMT_VERSION 0
-#define MGMT_REVISION 1
+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 INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */
+#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000)
-#define SERVICE_CACHE_TIMEOUT (5 * 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;
@@ -151,8 +233,8 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
return err;
}
-static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
- size_t rp_len)
+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;
@@ -173,6 +255,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
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);
@@ -181,10 +264,11 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
if (err < 0)
kfree_skb(skb);
- return err;;
+ return err;
}
-static int read_version(struct sock *sk)
+static int read_version(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 data_len)
{
struct mgmt_rp_read_version rp;
@@ -193,11 +277,46 @@ static int read_version(struct sock *sk)
rp.version = MGMT_VERSION;
put_unaligned_le16(MGMT_REVISION, &rp.revision);
- return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, &rp,
- sizeof(rp));
+ 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)
+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;
@@ -226,10 +345,7 @@ static int read_index_list(struct sock *sk)
i = 0;
list_for_each_entry(d, &hci_dev_list, list) {
- if (test_and_clear_bit(HCI_AUTO_OFF, &d->flags))
- cancel_delayed_work(&d->power_off);
-
- if (test_bit(HCI_SETUP, &d->flags))
+ if (test_bit(HCI_SETUP, &d->dev_flags))
continue;
put_unaligned_le16(d->id, &rp->index[i++]);
@@ -238,8 +354,8 @@ static int read_index_list(struct sock *sk)
read_unlock(&hci_dev_list_lock);
- err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, rp,
- rp_len);
+ err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 0, rp,
+ rp_len);
kfree(rp);
@@ -264,8 +380,13 @@ static u32 get_supported_settings(struct hci_dev *hdev)
settings |= MGMT_SETTING_LINK_SECURITY;
}
- if (hdev->features[4] & LMP_LE)
- settings |= MGMT_SETTING_LE;
+ if (enable_hs)
+ settings |= MGMT_SETTING_HS;
+
+ if (enable_le) {
+ if (hdev->features[4] & LMP_LE)
+ settings |= MGMT_SETTING_LE;
+ }
return settings;
}
@@ -274,47 +395,36 @@ static u32 get_current_settings(struct hci_dev *hdev)
{
u32 settings = 0;
- if (test_bit(HCI_UP, &hdev->flags))
+ if (hdev_is_powered(hdev))
settings |= MGMT_SETTING_POWERED;
- else
- return settings;
- if (test_bit(HCI_PSCAN, &hdev->flags))
+ if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
settings |= MGMT_SETTING_CONNECTABLE;
- if (test_bit(HCI_ISCAN, &hdev->flags))
+ if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
settings |= MGMT_SETTING_DISCOVERABLE;
- if (test_bit(HCI_PAIRABLE, &hdev->flags))
+ if (test_bit(HCI_PAIRABLE, &hdev->dev_flags))
settings |= MGMT_SETTING_PAIRABLE;
if (!(hdev->features[4] & LMP_NO_BREDR))
settings |= MGMT_SETTING_BREDR;
- if (hdev->host_features[0] & LMP_HOST_LE)
+ if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
settings |= MGMT_SETTING_LE;
- if (test_bit(HCI_AUTH, &hdev->flags))
+ if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
settings |= MGMT_SETTING_LINK_SECURITY;
- if (hdev->ssp_mode > 0)
+ 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 EIR_FLAGS 0x01 /* flags */
-#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */
-#define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */
-#define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */
-#define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */
-#define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */
-#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */
-#define EIR_NAME_SHORT 0x08 /* shortened local name */
-#define EIR_NAME_COMPLETE 0x09 /* complete local name */
-#define EIR_TX_POWER 0x0A /* transmit power level */
-#define EIR_DEVICE_ID 0x10 /* device ID */
-
#define PNP_INFO_SVCLASS_ID 0x1200
static u8 bluetooth_base_uuid[] = {
@@ -425,13 +535,16 @@ 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 (hdev->ssp_mode == 0)
+ if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
return 0;
- if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
+ if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
return 0;
memset(&cp, 0, sizeof(cp));
@@ -460,10 +573,14 @@ static u8 get_service_classes(struct hci_dev *hdev)
static int update_class(struct hci_dev *hdev)
{
u8 cod[3];
+ int err;
BT_DBG("%s", hdev->name);
- if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
+ if (!hdev_is_powered(hdev))
+ return 0;
+
+ if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
return 0;
cod[0] = hdev->minor_class;
@@ -473,15 +590,19 @@ static int update_class(struct hci_dev *hdev)
if (memcmp(cod, hdev->dev_class, 3) == 0)
return 0;
- return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
+ 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);
+ service_cache.work);
- if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->flags))
+ if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
return;
hci_dev_lock(hdev);
@@ -492,36 +613,30 @@ static void service_cache_off(struct work_struct *work)
hci_dev_unlock(hdev);
}
-static void mgmt_init_hdev(struct hci_dev *hdev)
+static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev)
{
- if (!test_and_set_bit(HCI_MGMT, &hdev->flags))
- INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off);
+ if (test_and_set_bit(HCI_MGMT, &hdev->dev_flags))
+ return;
+
+ INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off);
- if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->flags))
- schedule_delayed_work(&hdev->service_cache,
- msecs_to_jiffies(SERVICE_CACHE_TIMEOUT));
+ /* 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, u16 index)
+static int read_controller_info(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 data_len)
{
struct mgmt_rp_read_info rp;
- struct hci_dev *hdev;
-
- BT_DBG("sock %p hci%u", sk, index);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_READ_INFO,
- MGMT_STATUS_INVALID_PARAMS);
- if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
- cancel_delayed_work_sync(&hdev->power_off);
+ BT_DBG("sock %p %s", sk, hdev->name);
hci_dev_lock(hdev);
- if (test_and_clear_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags))
- mgmt_init_hdev(hdev);
-
memset(&rp, 0, sizeof(rp));
bacpy(&rp.bdaddr, &hdev->bdaddr);
@@ -536,11 +651,12 @@ static int read_controller_info(struct sock *sk, u16 index)
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);
- hci_dev_put(hdev);
- return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp));
+ return cmd_complete(sk, hdev->id, MGMT_OP_READ_INFO, 0, &rp,
+ sizeof(rp));
}
static void mgmt_pending_free(struct pending_cmd *cmd)
@@ -551,8 +667,8 @@ static void mgmt_pending_free(struct pending_cmd *cmd)
}
static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
- struct hci_dev *hdev,
- void *data, u16 len)
+ struct hci_dev *hdev, void *data,
+ u16 len)
{
struct pending_cmd *cmd;
@@ -581,8 +697,8 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
}
static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
- void (*cb)(struct pending_cmd *cmd, void *data),
- void *data)
+ void (*cb)(struct pending_cmd *cmd, void *data),
+ void *data)
{
struct list_head *p, *n;
@@ -620,40 +736,39 @@ 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, &settings, sizeof(settings));
+ return cmd_complete(sk, hdev->id, opcode, 0, &settings,
+ sizeof(settings));
}
-static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct mgmt_mode *cp;
- struct hci_dev *hdev;
+ struct mgmt_mode *cp = data;
struct pending_cmd *cmd;
- int err, up;
-
- cp = (void *) data;
+ int err;
- BT_DBG("request for hci%u", index);
+ BT_DBG("request for %s", hdev->name);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_POWERED,
- MGMT_STATUS_INVALID_PARAMS);
+ hci_dev_lock(hdev);
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_POWERED,
- MGMT_STATUS_INVALID_PARAMS);
+ if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
+ cancel_delayed_work(&hdev->power_off);
- hci_dev_lock(hdev);
+ if (cp->val) {
+ err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
+ mgmt_powered(hdev, 1);
+ goto failed;
+ }
+ }
- up = test_bit(HCI_UP, &hdev->flags);
- if ((cp->val && up) || (!cp->val && !up)) {
+ 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, index, MGMT_OP_SET_POWERED,
- MGMT_STATUS_BUSY);
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
+ MGMT_STATUS_BUSY);
goto failed;
}
@@ -672,49 +787,115 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
return err;
}
-static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len,
+ struct sock *skip_sk)
{
- struct mgmt_cp_set_discoverable *cp;
- struct hci_dev *hdev;
+ 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;
- cp = (void *) data;
-
- BT_DBG("request for hci%u", index);
+ BT_DBG("request for %s", hdev->name);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ 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 (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
- MGMT_STATUS_NOT_POWERED);
+ 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, index, MGMT_OP_SET_DISCOVERABLE,
- MGMT_STATUS_BUSY);
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+ MGMT_STATUS_BUSY);
goto failed;
}
- if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
- test_bit(HCI_PSCAN, &hdev->flags)) {
+ 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;
}
@@ -737,53 +918,56 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
mgmt_pending_remove(cmd);
if (cp->val)
- hdev->discov_timeout = get_unaligned_le16(&cp->timeout);
+ hdev->discov_timeout = timeout;
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct mgmt_mode *cp;
- struct hci_dev *hdev;
+ struct mgmt_mode *cp = data;
struct pending_cmd *cmd;
u8 scan;
int err;
- cp = (void *) data;
+ BT_DBG("request for %s", hdev->name);
- BT_DBG("request for hci%u", index);
+ hci_dev_lock(hdev);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ if (!hdev_is_powered(hdev)) {
+ bool changed = false;
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ if (!!cp->val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+ changed = true;
- hci_dev_lock(hdev);
+ 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);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
- 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, index, MGMT_OP_SET_CONNECTABLE,
- MGMT_STATUS_BUSY);
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
+ MGMT_STATUS_BUSY);
goto failed;
}
- if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
+ if (!!cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
goto failed;
}
@@ -794,116 +978,282 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
goto failed;
}
- if (cp->val)
+ if (cp->val) {
scan = SCAN_PAGE;
- else
+ } 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);
- hci_dev_put(hdev);
-
return err;
}
-static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
- u16 data_len, struct sock *skip_sk)
+static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct sk_buff *skb;
- struct mgmt_hdr *hdr;
+ struct mgmt_mode *cp = data;
+ int err;
- skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
- if (!skb)
- return -ENOMEM;
+ BT_DBG("request for %s", hdev->name);
- bt_cb(skb)->channel = HCI_CHANNEL_CONTROL;
+ hci_dev_lock(hdev);
- hdr = (void *) skb_put(skb, sizeof(*hdr));
- hdr->opcode = cpu_to_le16(event);
- if (hdev)
- hdr->index = cpu_to_le16(hdev->id);
+ if (cp->val)
+ set_bit(HCI_PAIRABLE, &hdev->dev_flags);
else
- hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
- hdr->len = cpu_to_le16(data_len);
+ clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
- if (data)
- memcpy(skb_put(skb, data_len), data, data_len);
+ err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
+ if (err < 0)
+ goto failed;
- hci_send_to_sock(NULL, skb, skip_sk);
- kfree_skb(skb);
+ err = new_settings(hdev, sk);
- return 0;
+failed:
+ hci_dev_unlock(hdev);
+ return err;
}
-static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct mgmt_mode *cp;
- struct hci_dev *hdev;
- __le32 ev;
+ struct mgmt_mode *cp = data;
+ struct pending_cmd *cmd;
+ u8 val;
int err;
- cp = (void *) data;
+ BT_DBG("request for %s", hdev->name);
- BT_DBG("request for hci%u", index);
+ hci_dev_lock(hdev);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ if (!hdev_is_powered(hdev)) {
+ bool changed = false;
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ if (!!cp->val != test_bit(HCI_LINK_SECURITY,
+ &hdev->dev_flags)) {
+ change_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
+ changed = true;
+ }
- hci_dev_lock(hdev);
+ err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev);
+ if (err < 0)
+ goto failed;
- if (cp->val)
- set_bit(HCI_PAIRABLE, &hdev->flags);
- else
- clear_bit(HCI_PAIRABLE, &hdev->flags);
+ if (changed)
+ err = new_settings(hdev, sk);
- err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
- if (err < 0)
goto failed;
+ }
- ev = cpu_to_le32(get_current_settings(hdev));
+ 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 = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), sk);
+ 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);
- hci_dev_put(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 add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
{
- struct mgmt_cp_add_uuid *cp;
- struct hci_dev *hdev;
- struct bt_uuid *uuid;
+ 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);
- cp = (void *) data;
+ hci_dev_lock(hdev);
- BT_DBG("request for hci%u", index);
+ if (!enable_le || !(hdev->features[4] & LMP_LE)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
+ MGMT_STATUS_NOT_SUPPORTED);
+ goto unlock;
+ }
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_ADD_UUID,
- MGMT_STATUS_INVALID_PARAMS);
+ 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;
+ }
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_ADD_UUID,
- MGMT_STATUS_INVALID_PARAMS);
+ 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;
@@ -923,41 +1273,65 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (err < 0)
goto failed;
- err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0);
+ 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);
- hci_dev_put(hdev);
-
return err;
}
-static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static bool enable_service_cache(struct hci_dev *hdev)
{
- struct list_head *p, *n;
- struct mgmt_cp_remove_uuid *cp;
- struct hci_dev *hdev;
- u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- int err, found;
+ if (!hdev_is_powered(hdev))
+ return false;
- cp = (void *) data;
+ if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
+ schedule_delayed_work(&hdev->service_cache, CACHE_TIMEOUT);
+ return true;
+ }
- BT_DBG("request for hci%u", index);
+ return false;
+}
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
- MGMT_STATUS_INVALID_PARAMS);
+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;
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
- MGMT_STATUS_INVALID_PARAMS);
+ 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);
- goto unlock;
+
+ 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;
@@ -973,11 +1347,12 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
}
if (found == 0) {
- err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
- MGMT_STATUS_INVALID_PARAMS);
+ 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;
@@ -986,41 +1361,50 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (err < 0)
goto unlock;
- err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0);
+ 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);
- hci_dev_put(hdev);
-
return err;
}
-static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_set_dev_class *cp;
+ struct mgmt_cp_set_dev_class *cp = data;
+ struct pending_cmd *cmd;
int err;
- cp = (void *) data;
-
- BT_DBG("request for hci%u", index);
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
- MGMT_STATUS_INVALID_PARAMS);
+ 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 (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->flags)) {
+ 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);
@@ -1028,30 +1412,33 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
}
err = update_class(hdev);
+ if (err < 0)
+ goto unlock;
- if (err == 0)
- err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0);
+ 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;
+ }
- hci_dev_unlock(hdev);
- hci_dev_put(hdev);
+ 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, u16 index, unsigned char *data,
+static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_load_link_keys *cp;
+ struct mgmt_cp_load_link_keys *cp = data;
u16 key_count, expected_len;
int i;
- cp = (void *) data;
-
- if (len < sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
- MGMT_STATUS_INVALID_PARAMS);
-
key_count = get_unaligned_le16(&cp->key_count);
expected_len = sizeof(*cp) + key_count *
@@ -1059,92 +1446,103 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
if (expected_len != len) {
BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
len, expected_len);
- return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
- MGMT_STATUS_INVALID_PARAMS);
+ return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+ MGMT_STATUS_INVALID_PARAMS);
}
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
- MGMT_STATUS_INVALID_PARAMS);
-
- BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
+ 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->flags);
+ set_bit(HCI_LINK_KEYS, &hdev->dev_flags);
if (cp->debug_keys)
- set_bit(HCI_DEBUG_KEYS, &hdev->flags);
+ set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
else
- clear_bit(HCI_DEBUG_KEYS, &hdev->flags);
+ 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->bdaddr, key->val, key->type,
- key->pin_len);
+ hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val,
+ key->type, key->pin_len);
}
- cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0);
+ cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0);
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
return 0;
}
-static int remove_keys(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 addr_type, struct sock *skip_sk)
{
- struct hci_dev *hdev;
- struct mgmt_cp_remove_keys *cp;
- struct mgmt_rp_remove_keys rp;
+ 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;
- cp = (void *) data;
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS,
- MGMT_STATUS_INVALID_PARAMS);
-
hci_dev_lock(hdev);
memset(&rp, 0, sizeof(rp));
- bacpy(&rp.bdaddr, &cp->bdaddr);
- rp.status = MGMT_STATUS_FAILED;
+ bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+ rp.addr.type = cp->addr.type;
- err = hci_remove_link_key(hdev, &cp->bdaddr);
- if (err < 0) {
- rp.status = MGMT_STATUS_NOT_PAIRED;
+ 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 (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) {
- err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp,
- sizeof(rp));
+ 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;
}
- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+ 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, index, MGMT_OP_REMOVE_KEYS, &rp,
- sizeof(rp));
+ 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_REMOVE_KEYS, hdev, cp, sizeof(*cp));
+ cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp,
+ sizeof(*cp));
if (!cmd) {
err = -ENOMEM;
goto unlock;
@@ -1157,19 +1555,14 @@ static int remove_keys(struct sock *sk, u16 index, unsigned char *data,
mgmt_pending_remove(cmd);
unlock:
- if (err < 0)
- err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp,
- sizeof(rp));
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_disconnect *cp;
+ struct mgmt_cp_disconnect *cp = data;
struct hci_cp_disconnect dc;
struct pending_cmd *cmd;
struct hci_conn *conn;
@@ -1177,38 +1570,28 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
BT_DBG("");
- cp = (void *) data;
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_DISCONNECT,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_DISCONNECT,
- MGMT_STATUS_INVALID_PARAMS);
-
hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
- MGMT_STATUS_NOT_POWERED);
+ 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, index, MGMT_OP_DISCONNECT,
- MGMT_STATUS_BUSY);
+ err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
+ MGMT_STATUS_BUSY);
goto failed;
}
- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
- if (!conn)
- conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
+ 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, index, MGMT_OP_DISCONNECT,
- MGMT_STATUS_NOT_CONNECTED);
+ err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
+ MGMT_STATUS_NOT_CONNECTED);
goto failed;
}
@@ -1227,8 +1610,6 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
@@ -1251,41 +1632,42 @@ static u8 link_to_mgmt(u8 link_type, u8 addr_type)
}
}
-static int get_connections(struct sock *sk, u16 index)
+static int get_connections(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 data_len)
{
struct mgmt_rp_get_connections *rp;
- struct hci_dev *hdev;
struct hci_conn *c;
- struct list_head *p;
size_t rp_len;
- u16 count;
- int i, err;
+ int err;
+ u16 i;
BT_DBG("");
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS,
- MGMT_STATUS_INVALID_PARAMS);
-
hci_dev_lock(hdev);
- count = 0;
- list_for_each(p, &hdev->conn_hash.list) {
- count++;
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_GET_CONNECTIONS,
+ MGMT_STATUS_NOT_POWERED);
+ goto unlock;
}
- rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info));
+ 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;
}
- put_unaligned_le16(count, &rp->conn_count);
-
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)
@@ -1293,85 +1675,77 @@ static int get_connections(struct sock *sk, u16 index)
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, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, 0, rp,
+ rp_len);
-unlock:
kfree(rp);
+
+unlock:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
return err;
}
-static int send_pin_code_neg_reply(struct sock *sk, u16 index,
- struct hci_dev *hdev, struct mgmt_cp_pin_code_neg_reply *cp)
+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));
+ sizeof(*cp));
if (!cmd)
return -ENOMEM;
- err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(cp->bdaddr),
- &cp->bdaddr);
+ 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, u16 index, unsigned char *data,
- u16 len)
+static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
struct hci_conn *conn;
- struct mgmt_cp_pin_code_reply *cp;
- struct mgmt_cp_pin_code_neg_reply ncp;
+ struct mgmt_cp_pin_code_reply *cp = data;
struct hci_cp_pin_code_reply reply;
struct pending_cmd *cmd;
int err;
BT_DBG("");
- cp = (void *) data;
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
-
hci_dev_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
- MGMT_STATUS_NOT_POWERED);
+ 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->bdaddr);
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
if (!conn) {
- err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
- MGMT_STATUS_NOT_CONNECTED);
+ 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) {
- bacpy(&ncp.bdaddr, &cp->bdaddr);
+ 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, index, hdev, &ncp);
+ err = send_pin_code_neg_reply(sk, hdev, &ncp);
if (err >= 0)
- err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
+ err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+ MGMT_STATUS_INVALID_PARAMS);
goto failed;
}
@@ -1382,7 +1756,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
goto failed;
}
- bacpy(&reply.bdaddr, &cp->bdaddr);
+ bacpy(&reply.bdaddr, &cp->addr.bdaddr);
reply.pin_len = cp->pin_len;
memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
@@ -1392,67 +1766,39 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_pin_code_neg_reply *cp;
+ struct mgmt_cp_pin_code_neg_reply *cp = data;
int err;
BT_DBG("");
- cp = (void *) data;
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
-
hci_dev_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
- MGMT_STATUS_NOT_POWERED);
+ 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, index, hdev, cp);
+ err = send_pin_code_neg_reply(sk, hdev, cp);
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_set_io_capability *cp;
+ struct mgmt_cp_set_io_capability *cp = data;
BT_DBG("");
- cp = (void *) data;
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
- MGMT_STATUS_INVALID_PARAMS);
-
hci_dev_lock(hdev);
hdev->io_capability = cp->io_capability;
@@ -1461,9 +1807,9 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,
hdev->io_capability);
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
- return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0);
+ 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)
@@ -1491,9 +1837,9 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
bacpy(&rp.addr.bdaddr, &conn->dst);
rp.addr.type = link_to_mgmt(conn->type, conn->dst_type);
- rp.status = status;
- cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp));
+ 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;
@@ -1515,13 +1861,13 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status)
if (!cmd)
BT_DBG("Unable to find a pending command");
else
- pairing_complete(cmd, status);
+ pairing_complete(cmd, mgmt_status(status));
}
-static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_pair_device *cp;
+ struct mgmt_cp_pair_device *cp = data;
struct mgmt_rp_pair_device rp;
struct pending_cmd *cmd;
u8 sec_level, auth_type;
@@ -1530,19 +1876,14 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
BT_DBG("");
- cp = (void *) data;
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
-
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;
@@ -1551,27 +1892,26 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (cp->addr.type == MGMT_ADDR_BREDR)
conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level,
- auth_type);
+ auth_type);
else
conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level,
- auth_type);
+ auth_type);
memset(&rp, 0, sizeof(rp));
bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
rp.addr.type = cp->addr.type;
if (IS_ERR(conn)) {
- rp.status = -PTR_ERR(conn);
- err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
- &rp, sizeof(rp));
+ 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);
- rp.status = EBUSY;
- err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
- &rp, sizeof(rp));
+ err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+ MGMT_STATUS_BUSY, &rp, sizeof(rp));
goto unlock;
}
@@ -1599,58 +1939,88 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
unlock:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
- u16 mgmt_op, u16 hci_op, __le32 passkey)
+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_dev *hdev;
struct hci_conn *conn;
int err;
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, mgmt_op,
- MGMT_STATUS_INVALID_PARAMS);
+ BT_DBG("");
hci_dev_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED);
+ 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;
}
- /*
- * Check for an existing ACL link, if present pair via
- * HCI commands.
- *
- * If no ACL link is present, check for an LE link and if
- * present, pair via the SMP engine.
- *
- * If neither ACL nor LE links are present, fail with error.
- */
- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
- if (!conn) {
+ 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, index, mgmt_op,
- MGMT_STATUS_NOT_CONNECTED);
- goto done;
- }
+ 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, index, mgmt_op,
- MGMT_STATUS_SUCCESS);
+ err = cmd_status(sk, hdev->id, mgmt_op,
+ MGMT_STATUS_SUCCESS);
else
- err = cmd_status(sk, index, mgmt_op,
- MGMT_STATUS_FAILED);
+ err = cmd_status(sk, hdev->id, mgmt_op,
+ MGMT_STATUS_FAILED);
goto done;
}
@@ -1676,94 +2046,96 @@ static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
done:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len)
+static int user_confirm_reply(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct mgmt_cp_user_confirm_reply *cp = (void *) data;
+ struct mgmt_cp_user_confirm_reply *cp = data;
BT_DBG("");
if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
+ return cmd_status(sk, hdev->id, MGMT_OP_USER_CONFIRM_REPLY,
+ MGMT_STATUS_INVALID_PARAMS);
- return user_pairing_resp(sk, index, &cp->bdaddr,
- MGMT_OP_USER_CONFIRM_REPLY,
- HCI_OP_USER_CONFIRM_REPLY, 0);
+ 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, u16 index, void *data,
- u16 len)
+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("");
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_NEG_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
-
- return user_pairing_resp(sk, index, &cp->bdaddr,
- MGMT_OP_USER_CONFIRM_NEG_REPLY,
- HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
+ 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, u16 index, void *data, u16 len)
+static int user_passkey_reply(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct mgmt_cp_user_passkey_reply *cp = (void *) data;
+ struct mgmt_cp_user_passkey_reply *cp = data;
BT_DBG("");
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_REPLY,
- EINVAL);
-
- return user_pairing_resp(sk, index, &cp->bdaddr,
- MGMT_OP_USER_PASSKEY_REPLY,
- HCI_OP_USER_PASSKEY_REPLY, cp->passkey);
+ 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, u16 index, void *data,
- u16 len)
+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 = (void *) data;
+ struct mgmt_cp_user_passkey_neg_reply *cp = data;
BT_DBG("");
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_NEG_REPLY,
- EINVAL);
+ 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;
- return user_pairing_resp(sk, index, &cp->bdaddr,
- MGMT_OP_USER_PASSKEY_NEG_REPLY,
- HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
+ 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, u16 index, unsigned char *data,
- u16 len)
+static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct mgmt_cp_set_local_name *mgmt_cp = (void *) data;
- struct hci_cp_write_local_name hci_cp;
- struct hci_dev *hdev;
+ struct mgmt_cp_set_local_name *cp = data;
struct pending_cmd *cmd;
int err;
BT_DBG("");
- if (len != sizeof(*mgmt_cp))
- return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
- MGMT_STATUS_INVALID_PARAMS);
+ hci_dev_lock(hdev);
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
- MGMT_STATUS_INVALID_PARAMS);
+ memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name));
- hci_dev_lock(hdev);
+ 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) {
@@ -1771,49 +2143,40 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
goto failed;
}
- memcpy(hci_cp.name, mgmt_cp->name, sizeof(hci_cp.name));
- err = hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(hci_cp),
- &hci_cp);
+ err = update_name(hdev, cp->name);
if (err < 0)
mgmt_pending_remove(cmd);
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int read_local_oob_data(struct sock *sk, u16 index)
+static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 data_len)
{
- struct hci_dev *hdev;
struct pending_cmd *cmd;
int err;
- BT_DBG("hci%u", index);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
- MGMT_STATUS_INVALID_PARAMS);
+ BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
- MGMT_STATUS_NOT_POWERED);
+ 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, index, MGMT_OP_READ_LOCAL_OOB_DATA,
- MGMT_STATUS_NOT_SUPPORTED);
+ 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, index, MGMT_OP_READ_LOCAL_OOB_DATA,
- MGMT_STATUS_BUSY);
+ err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+ MGMT_STATUS_BUSY);
goto unlock;
}
@@ -1829,104 +2192,112 @@ static int read_local_oob_data(struct sock *sk, u16 index)
unlock:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_add_remote_oob_data *cp = (void *) data;
+ struct mgmt_cp_add_remote_oob_data *cp = data;
+ u8 status;
int err;
- BT_DBG("hci%u ", index);
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
- MGMT_STATUS_INVALID_PARAMS);
+ BT_DBG("%s ", hdev->name);
hci_dev_lock(hdev);
- err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash,
- cp->randomizer);
+ 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)
- err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
- MGMT_STATUS_FAILED);
+ status = MGMT_STATUS_FAILED;
else
- err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
- 0);
+ status = 0;
- hci_dev_unlock(hdev);
- hci_dev_put(hdev);
+ 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, u16 index,
- unsigned char *data, u16 len)
+static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_remove_remote_oob_data *cp = (void *) data;
+ struct mgmt_cp_remove_remote_oob_data *cp = data;
+ u8 status;
int err;
- BT_DBG("hci%u ", index);
+ BT_DBG("%s", hdev->name);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
- MGMT_STATUS_INVALID_PARAMS);
+ 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;
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
- MGMT_STATUS_INVALID_PARAMS);
+ 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_remove_remote_oob_data(hdev, &cp->bdaddr);
+ err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE);
if (err < 0)
- err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
- MGMT_STATUS_INVALID_PARAMS);
- else
- err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
- NULL, 0);
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
return err;
}
-static int start_discovery(struct sock *sk, u16 index,
- unsigned char *data, u16 len)
+static int start_discovery(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
{
- struct mgmt_cp_start_discovery *cp = (void *) data;
+ struct mgmt_cp_start_discovery *cp = data;
struct pending_cmd *cmd;
- struct hci_dev *hdev;
int err;
- BT_DBG("hci%u", index);
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_INVALID_PARAMS);
+ BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_NOT_POWERED);
+ 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;
}
@@ -1936,137 +2307,217 @@ static int start_discovery(struct sock *sk, u16 index,
goto failed;
}
- err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
+ 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);
- hci_dev_put(hdev);
-
return err;
}
-static int stop_discovery(struct sock *sk, u16 index)
+static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
+ 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("hci%u", index);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
- MGMT_STATUS_INVALID_PARAMS);
+ 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 failed;
+ 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;
}
- err = hci_cancel_inquiry(hdev);
+ 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);
-failed:
+unlock:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int block_device(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_block_device *cp = (void *) data;
+ struct mgmt_cp_confirm_name *cp = data;
+ struct inquiry_entry *e;
int err;
- BT_DBG("hci%u", index);
+ BT_DBG("%s", hdev->name);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
+ hci_dev_lock(hdev);
+
+ if (!hci_discovery_active(hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
+ MGMT_STATUS_FAILED);
+ goto failed;
+ }
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
+ 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->bdaddr);
+ err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
if (err < 0)
- err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
- MGMT_STATUS_FAILED);
+ status = MGMT_STATUS_FAILED;
else
- err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
- NULL, 0);
+ status = 0;
+
+ err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status,
+ &cp->addr, sizeof(cp->addr));
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
return err;
}
-static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_unblock_device *cp = (void *) data;
+ struct mgmt_cp_unblock_device *cp = data;
+ u8 status;
int err;
- BT_DBG("hci%u", index);
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
+ BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
- err = hci_blacklist_del(hdev, &cp->bdaddr);
-
+ err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
if (err < 0)
- err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
+ status = MGMT_STATUS_INVALID_PARAMS;
else
- err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
- NULL, 0);
+ status = 0;
+
+ err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status,
+ &cp->addr, sizeof(cp->addr));
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
return err;
}
-static int set_fast_connectable(struct sock *sk, u16 index,
- unsigned char *data, u16 len)
+static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_mode *cp = (void *) data;
+ struct mgmt_mode *cp = data;
struct hci_cp_write_page_scan_activity acp;
u8 type;
int err;
- BT_DBG("hci%u", index);
+ BT_DBG("%s", hdev->name);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ if (!hdev_is_powered(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+ MGMT_STATUS_NOT_POWERED);
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ 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);
@@ -2080,35 +2531,128 @@ static int set_fast_connectable(struct sock *sk, u16 index,
acp.window = 0x0012; /* default 11.25 msec page scan window */
- err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
- sizeof(acp), &acp);
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, sizeof(acp),
+ &acp);
if (err < 0) {
- err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
- MGMT_STATUS_FAILED);
+ 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, index, MGMT_OP_SET_FAST_CONNECTABLE,
- MGMT_STATUS_FAILED);
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+ MGMT_STATUS_FAILED);
goto done;
}
- err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
- NULL, 0);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 0,
+ NULL, 0);
done:
hci_dev_unlock(hdev);
- hci_dev_put(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)
{
- unsigned char *buf;
+ 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);
@@ -2125,7 +2669,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
goto done;
}
- hdr = (struct mgmt_hdr *) buf;
+ hdr = buf;
opcode = get_unaligned_le16(&hdr->opcode);
index = get_unaligned_le16(&hdr->index);
len = get_unaligned_le16(&hdr->len);
@@ -2135,117 +2679,54 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
goto done;
}
- switch (opcode) {
- case MGMT_OP_READ_VERSION:
- err = read_version(sk);
- break;
- case MGMT_OP_READ_INDEX_LIST:
- err = read_index_list(sk);
- break;
- case MGMT_OP_READ_INFO:
- err = read_controller_info(sk, index);
- break;
- case MGMT_OP_SET_POWERED:
- err = set_powered(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_SET_DISCOVERABLE:
- err = set_discoverable(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_SET_CONNECTABLE:
- err = set_connectable(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_SET_FAST_CONNECTABLE:
- err = set_fast_connectable(sk, index, buf + sizeof(*hdr),
- len);
- break;
- case MGMT_OP_SET_PAIRABLE:
- err = set_pairable(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_ADD_UUID:
- err = add_uuid(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_REMOVE_UUID:
- err = remove_uuid(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_SET_DEV_CLASS:
- err = set_dev_class(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_LOAD_LINK_KEYS:
- err = load_link_keys(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_REMOVE_KEYS:
- err = remove_keys(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_DISCONNECT:
- err = disconnect(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_GET_CONNECTIONS:
- err = get_connections(sk, index);
- break;
- case MGMT_OP_PIN_CODE_REPLY:
- err = pin_code_reply(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_PIN_CODE_NEG_REPLY:
- err = pin_code_neg_reply(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_SET_IO_CAPABILITY:
- err = set_io_capability(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_PAIR_DEVICE:
- err = pair_device(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_USER_CONFIRM_REPLY:
- err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_USER_CONFIRM_NEG_REPLY:
- err = user_confirm_neg_reply(sk, index, buf + sizeof(*hdr),
- len);
- break;
- case MGMT_OP_USER_PASSKEY_REPLY:
- err = user_passkey_reply(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_USER_PASSKEY_NEG_REPLY:
- err = user_passkey_neg_reply(sk, index, buf + sizeof(*hdr),
- len);
- break;
- case MGMT_OP_SET_LOCAL_NAME:
- err = set_local_name(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_READ_LOCAL_OOB_DATA:
- err = read_local_oob_data(sk, index);
- break;
- case MGMT_OP_ADD_REMOTE_OOB_DATA:
- err = add_remote_oob_data(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
- err = remove_remote_oob_data(sk, index, buf + sizeof(*hdr),
- len);
- break;
- case MGMT_OP_START_DISCOVERY:
- err = start_discovery(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_STOP_DISCOVERY:
- err = stop_discovery(sk, index);
- break;
- case MGMT_OP_BLOCK_DEVICE:
- err = block_device(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_UNBLOCK_DEVICE:
- err = unblock_device(sk, index, buf + sizeof(*hdr), len);
- break;
- default:
+ 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);
- break;
+ 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;
}
@@ -2265,7 +2746,7 @@ int mgmt_index_added(struct hci_dev *hdev)
int mgmt_index_removed(struct hci_dev *hdev)
{
- u8 status = ENODEV;
+ u8 status = MGMT_STATUS_INVALID_INDEX;
mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
@@ -2273,9 +2754,9 @@ int mgmt_index_removed(struct hci_dev *hdev)
}
struct cmd_lookup {
- u8 val;
struct sock *sk;
struct hci_dev *hdev;
+ u8 mgmt_status;
};
static void settings_rsp(struct pending_cmd *cmd, void *data)
@@ -2296,63 +2777,91 @@ static void settings_rsp(struct pending_cmd *cmd, void *data)
int mgmt_powered(struct hci_dev *hdev, u8 powered)
{
- struct cmd_lookup match = { powered, NULL, hdev };
- __le32 ev;
- int ret;
+ 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 status = ENETDOWN;
+ 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);
}
- ev = cpu_to_le32(get_current_settings(hdev));
-
- ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
- match.sk);
+ err = new_settings(hdev, match.sk);
if (match.sk)
sock_put(match.sk);
- return ret;
+ return err;
}
int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
{
- struct cmd_lookup match = { discoverable, NULL, hdev };
- __le32 ev;
- int ret;
+ struct cmd_lookup match = { NULL, hdev };
+ bool changed = false;
+ int err = 0;
- mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match);
+ 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;
+ }
- ev = cpu_to_le32(get_current_settings(hdev));
+ mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp,
+ &match);
+
+ if (changed)
+ err = new_settings(hdev, match.sk);
- ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
- match.sk);
if (match.sk)
sock_put(match.sk);
- return ret;
+ return err;
}
int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
{
- __le32 ev;
- struct cmd_lookup match = { connectable, NULL, hdev };
- int ret;
+ struct cmd_lookup match = { NULL, hdev };
+ bool changed = false;
+ int err = 0;
- mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
- &match);
+ 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;
+ }
- ev = cpu_to_le32(get_current_settings(hdev));
+ mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
+ &match);
- ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
+ if (changed)
+ err = new_settings(hdev, match.sk);
if (match.sk)
sock_put(match.sk);
- return ret;
+ return err;
}
int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
@@ -2361,24 +2870,24 @@ int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
if (scan & SCAN_PAGE)
mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
- cmd_status_rsp, &mgmt_err);
+ cmd_status_rsp, &mgmt_err);
if (scan & SCAN_INQUIRY)
mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
- cmd_status_rsp, &mgmt_err);
+ cmd_status_rsp, &mgmt_err);
return 0;
}
-int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
- u8 persistent)
+int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, u8 persistent)
{
struct mgmt_ev_new_link_key ev;
memset(&ev, 0, sizeof(ev));
ev.store_hint = persistent;
- bacpy(&ev.key.bdaddr, &key->bdaddr);
+ 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;
@@ -2386,15 +2895,54 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type)
+int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
{
- struct mgmt_addr_info ev;
+ struct mgmt_ev_new_long_term_key ev;
- bacpy(&ev.bdaddr, bdaddr);
- ev.type = link_to_mgmt(link_type, addr_type);
+ memset(&ev, 0, sizeof(ev));
- return mgmt_event(MGMT_EV_CONNECTED, hdev, &ev, sizeof(ev), NULL);
+ 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_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)
@@ -2403,10 +2951,11 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data)
struct sock **sk = data;
struct mgmt_rp_disconnect rp;
- bacpy(&rp.bdaddr, &cp->bdaddr);
- rp.status = 0;
+ bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+ rp.addr.type = cp->addr.type;
- cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp));
+ cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp,
+ sizeof(rp));
*sk = cmd->sk;
sock_hold(*sk);
@@ -2414,25 +2963,25 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data)
mgmt_pending_remove(cmd);
}
-static void remove_keys_rsp(struct pending_cmd *cmd, void *data)
+static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
{
- u8 *status = data;
- struct mgmt_cp_remove_keys *cp = cmd->param;
- struct mgmt_rp_remove_keys rp;
+ 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.bdaddr, &cp->bdaddr);
- if (status != NULL)
- rp.status = *status;
+ bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+ rp.addr.type = cp->addr.type;
- cmd_complete(cmd->sk, cmd->index, MGMT_OP_REMOVE_KEYS, &rp,
- sizeof(rp));
+ 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_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type)
+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;
@@ -2443,45 +2992,44 @@ int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
bacpy(&ev.bdaddr, bdaddr);
ev.type = link_to_mgmt(link_type, addr_type);
- err = mgmt_event(MGMT_EV_DISCONNECTED, hdev, &ev, sizeof(ev), sk);
+ err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
+ sk);
if (sk)
- sock_put(sk);
+ sock_put(sk);
- mgmt_pending_foreach(MGMT_OP_REMOVE_KEYS, hdev, remove_keys_rsp, NULL);
+ 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 status)
+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;
- u8 mgmt_err = mgmt_status(status);
int err;
cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
if (!cmd)
return -ENOENT;
- if (bdaddr) {
- struct mgmt_rp_disconnect rp;
+ bacpy(&rp.addr.bdaddr, bdaddr);
+ rp.addr.type = link_to_mgmt(link_type, addr_type);
- bacpy(&rp.bdaddr, bdaddr);
- rp.status = status;
-
- err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
- &rp, sizeof(rp));
- } else
- err = cmd_status(cmd->sk, hdev->id, MGMT_OP_DISCONNECT,
- mgmt_err);
+ 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)
+ u8 addr_type, u8 status)
{
struct mgmt_ev_connect_failed ev;
@@ -2496,15 +3044,16 @@ int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
{
struct mgmt_ev_pin_code_request ev;
- bacpy(&ev.bdaddr, bdaddr);
+ 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);
+ NULL);
}
int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 status)
+ u8 status)
{
struct pending_cmd *cmd;
struct mgmt_rp_pin_code_reply rp;
@@ -2514,11 +3063,11 @@ int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
if (!cmd)
return -ENOENT;
- bacpy(&rp.bdaddr, bdaddr);
- rp.status = mgmt_status(status);
+ bacpy(&rp.addr.bdaddr, bdaddr);
+ rp.addr.type = MGMT_ADDR_BREDR;
- err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp,
- sizeof(rp));
+ err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+ mgmt_status(status), &rp, sizeof(rp));
mgmt_pending_remove(cmd);
@@ -2526,7 +3075,7 @@ int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
}
int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 status)
+ u8 status)
{
struct pending_cmd *cmd;
struct mgmt_rp_pin_code_reply rp;
@@ -2536,11 +3085,11 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
if (!cmd)
return -ENOENT;
- bacpy(&rp.bdaddr, bdaddr);
- rp.status = mgmt_status(status);
+ bacpy(&rp.addr.bdaddr, bdaddr);
+ rp.addr.type = MGMT_ADDR_BREDR;
- err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp,
- sizeof(rp));
+ err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
+ mgmt_status(status), &rp, sizeof(rp));
mgmt_pending_remove(cmd);
@@ -2548,34 +3097,39 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
}
int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
- __le32 value, u8 confirm_hint)
+ 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.bdaddr, bdaddr);
+ 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);
+ NULL);
}
-int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr)
+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.bdaddr, bdaddr);
+ 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);
+ NULL);
}
static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 status, u8 opcode)
+ u8 link_type, u8 addr_type, u8 status,
+ u8 opcode)
{
struct pending_cmd *cmd;
struct mgmt_rp_user_confirm_reply rp;
@@ -2585,9 +3139,10 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
if (!cmd)
return -ENOENT;
- bacpy(&rp.bdaddr, bdaddr);
- rp.status = mgmt_status(status);
- err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp));
+ 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);
@@ -2595,72 +3150,215 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
}
int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 status)
+ u8 link_type, u8 addr_type, u8 status)
{
- return user_pairing_resp_complete(hdev, bdaddr, status,
- MGMT_OP_USER_CONFIRM_REPLY);
+ 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 status)
+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, status,
- MGMT_OP_USER_CONFIRM_NEG_REPLY);
+ 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 status)
+ u8 link_type, u8 addr_type, u8 status)
{
- return user_pairing_resp_complete(hdev, bdaddr, status,
- MGMT_OP_USER_PASSKEY_REPLY);
+ 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 status)
+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, status,
- MGMT_OP_USER_PASSKEY_NEG_REPLY);
+ 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 status)
+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.bdaddr, bdaddr);
+ 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;
- int err;
+ 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));
+ mgmt_status(status));
goto failed;
}
- update_eir(hdev);
-
- err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, &ev,
- sizeof(ev));
+ err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev,
+ sizeof(ev));
if (err < 0)
goto failed;
send_event:
- err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
- cmd ? cmd->sk : NULL);
+ if (changed)
+ err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev,
+ sizeof(ev), cmd ? cmd->sk : NULL);
+
+ update_eir(hdev);
failed:
if (cmd)
@@ -2669,7 +3367,7 @@ failed:
}
int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
- u8 *randomizer, u8 status)
+ u8 *randomizer, u8 status)
{
struct pending_cmd *cmd;
int err;
@@ -2681,9 +3379,8 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
return -ENOENT;
if (status) {
- err = cmd_status(cmd->sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_DATA,
- mgmt_status(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;
@@ -2691,8 +3388,8 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
err = cmd_complete(cmd->sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_DATA,
- &rp, sizeof(rp));
+ MGMT_OP_READ_LOCAL_OOB_DATA, 0, &rp,
+ sizeof(rp));
}
mgmt_pending_remove(cmd);
@@ -2700,48 +3397,120 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
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 *eir)
+ u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8
+ ssp, u8 *eir, u16 eir_len)
{
- struct mgmt_ev_device_found ev;
+ char buf[512];
+ struct mgmt_ev_device_found *ev = (void *) buf;
+ size_t ev_size;
- memset(&ev, 0, sizeof(ev));
+ /* Leave 5 bytes for a potential CoD field */
+ if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
+ return -EINVAL;
- bacpy(&ev.addr.bdaddr, bdaddr);
- ev.addr.type = link_to_mgmt(link_type, addr_type);
- ev.rssi = rssi;
+ 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 (eir)
- memcpy(ev.eir, eir, sizeof(ev.eir));
+ 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);
- if (dev_class)
- memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class));
+ put_unaligned_le16(eir_len, &ev->eir_len);
- return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, &ev, sizeof(ev), NULL);
+ 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 *name)
+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_remote_name ev;
+ struct mgmt_ev_device_found *ev;
+ char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2];
+ u16 eir_len;
- memset(&ev, 0, sizeof(ev));
+ ev = (struct mgmt_ev_device_found *) buf;
- bacpy(&ev.bdaddr, bdaddr);
- memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
+ 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_REMOTE_NAME, hdev, &ev, sizeof(ev), NULL);
+ 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;
- err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status));
+ 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;
@@ -2756,7 +3525,8 @@ int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
if (!cmd)
return -ENOENT;
- err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status));
+ 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;
@@ -2764,44 +3534,61 @@ int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
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) {
- cmd_complete(cmd->sk, hdev->id, cmd->opcode, NULL, 0);
+ u8 type = hdev->discovery.type;
+
+ cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type,
+ sizeof(type));
mgmt_pending_remove(cmd);
}
- return mgmt_event(MGMT_EV_DISCOVERING, hdev, &discovering,
- sizeof(discovering), NULL);
+ 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)
+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.bdaddr, bdaddr);
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = type;
return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev),
- cmd ? cmd->sk : NULL);
+ cmd ? cmd->sk : NULL);
}
-int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr)
+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.bdaddr, bdaddr);
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = type;
return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
- cmd ? cmd->sk : NULL);
+ 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/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index a2d4f5122a6..c179734f143 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -196,7 +196,7 @@ 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, *p;
+ struct list_head *head = &rfcomm_dev_list;
int err = 0;
BT_DBG("id %d channel %d", req->dev_id, req->channel);
@@ -215,7 +215,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
break;
dev->id++;
- head = p;
+ head = &entry->list;
}
} else {
dev->id = req->dev_id;
@@ -229,7 +229,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
if (entry->id > dev->id - 1)
break;
- head = p;
+ head = &entry->list;
}
}
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 32c47de3034..deb119875fd 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -29,7 +29,7 @@
#include <linux/scatterlist.h>
#include <crypto/b128ops.h>
-#define SMP_TIMEOUT 30000 /* 30 seconds */
+#define SMP_TIMEOUT msecs_to_jiffies(30000)
static inline void swap128(u8 src[16], u8 dst[16])
{
@@ -186,8 +186,7 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
hci_send_acl(conn->hchan, skb, 0);
cancel_delayed_work_sync(&conn->security_timer);
- schedule_delayed_work(&conn->security_timer,
- msecs_to_jiffies(SMP_TIMEOUT));
+ schedule_delayed_work(&conn->security_timer, SMP_TIMEOUT);
}
static __u8 authreq_to_seclevel(__u8 authreq)
@@ -217,7 +216,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
{
u8 dist_keys = 0;
- if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) {
+ if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->dev_flags)) {
dist_keys = SMP_DIST_ENC_KEY;
authreq |= SMP_AUTH_BONDING;
} else {
@@ -250,21 +249,27 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
(max_key_size < SMP_MIN_ENC_KEY_SIZE))
return SMP_ENC_KEY_SIZE;
- smp->smp_key_size = max_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->pend);
- mgmt_auth_failed(conn->hcon->hdev, conn->dst, reason);
- cancel_delayed_work_sync(&conn->security_timer);
- smp_chan_destroy(conn);
+ 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
@@ -305,7 +310,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
remote_io > SMP_IO_KEYBOARD_DISPLAY)
method = JUST_WORKS;
else
- method = gen_method[local_io][remote_io];
+ 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)
@@ -346,9 +351,11 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
hci_dev_lock(hcon->hdev);
if (method == REQ_PASSKEY)
- ret = mgmt_user_passkey_request(hcon->hdev, conn->dst);
+ 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);
@@ -377,12 +384,11 @@ static void confirm_work(struct work_struct *work)
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);
+ 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);
+ conn->hcon->dst_type, conn->dst, 0, conn->src,
+ res);
if (ret) {
reason = SMP_UNSPECIFIED;
goto error;
@@ -417,12 +423,10 @@ static void random_work(struct work_struct *work)
if (hcon->out)
ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0,
- conn->src, hcon->dst_type, conn->dst,
- res);
+ 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);
+ hcon->dst_type, conn->dst, 0, conn->src, res);
if (ret) {
reason = SMP_UNSPECIFIED;
goto error;
@@ -446,16 +450,16 @@ static void random_work(struct work_struct *work)
smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, key);
swap128(key, stk);
- memset(stk + smp->smp_key_size, 0,
- SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size);
+ 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->pend)) {
+ 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->smp_key_size;
+ hcon->enc_key_size = smp->enc_key_size;
} else {
u8 stk[16], r[16], rand[8];
__le16 ediv;
@@ -469,11 +473,12 @@ static void random_work(struct work_struct *work)
smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, key);
swap128(key, stk);
- memset(stk + smp->smp_key_size, 0,
- SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size);
+ memset(stk + smp->enc_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
- hci_add_ltk(hcon->hdev, 0, conn->dst, smp->smp_key_size,
- ediv, rand, stk);
+ hci_add_ltk(hcon->hdev, conn->dst, hcon->dst_type,
+ HCI_SMP_STK_SLAVE, 0, 0, stk, smp->enc_key_size,
+ ediv, rand);
}
return;
@@ -506,7 +511,7 @@ void smp_chan_destroy(struct l2cap_conn *conn)
{
struct smp_chan *smp = conn->smp_chan;
- clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend);
+ BUG_ON(!smp);
if (smp->tfm)
crypto_free_blkcipher(smp->tfm);
@@ -571,7 +576,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (conn->hcon->link_mode & HCI_LM_MASTER)
return SMP_CMD_NOTSUPP;
- if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend))
+ if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
smp = smp_chan_create(conn);
smp = conn->smp_chan;
@@ -584,6 +589,8 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
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);
@@ -698,23 +705,18 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
static u8 smp_ltk_encrypt(struct l2cap_conn *conn)
{
- struct link_key *key;
- struct key_master_id *master;
+ struct smp_ltk *key;
struct hci_conn *hcon = conn->hcon;
- key = hci_find_link_key_type(hcon->hdev, conn->dst,
- HCI_LK_SMP_LTK);
+ 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->pend))
+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
return 1;
- master = (void *) key->data;
- hci_le_start_enc(hcon, master->ediv, master->rand,
- key->val);
- hcon->enc_key_size = key->pin_len;
+ hci_le_start_enc(hcon, key->ediv, key->rand, key->val);
+ hcon->enc_key_size = key->enc_size;
return 1;
@@ -733,7 +735,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (smp_ltk_encrypt(conn))
return 0;
- if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend))
+ if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
return 0;
smp = smp_chan_create(conn);
@@ -772,7 +774,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
if (smp_ltk_encrypt(conn))
goto done;
- if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend))
+ if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
return 0;
smp = smp_chan_create(conn);
@@ -817,13 +819,19 @@ 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_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->smp_key_size,
- rp->ediv, rp->rand, smp->tk);
-
+ 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;
}
@@ -908,7 +916,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
BT_DBG("conn %p force %d", conn, force);
- if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend))
+ if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
return 0;
rsp = (void *) &smp->prsp[1];
@@ -933,6 +941,8 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
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));
@@ -941,8 +951,10 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
- hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->smp_key_size,
- ediv, ident.rand, enc.ltk);
+ 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);
@@ -982,7 +994,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
}
if (conn->hcon->out || force) {
- clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend);
+ clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags);
cancel_delayed_work_sync(&conn->security_timer);
smp_chan_destroy(conn);
}
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 71773b014e0..ba829de8442 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -167,10 +167,11 @@ static int br_set_mac_address(struct net_device *dev, void *p)
struct sockaddr *addr = p;
if (!is_valid_ether_addr(addr->sa_data))
- return -EINVAL;
+ return -EADDRNOTAVAIL;
spin_lock_bh(&br->lock);
if (compare_ether_addr(dev->dev_addr, addr->sa_data)) {
+ dev->addr_assign_type &= ~NET_ADDR_RANDOM;
memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
br_fdb_change_mac_address(br, addr->sa_data);
br_stp_change_bridge_id(br, addr->sa_data);
@@ -334,7 +335,7 @@ void br_dev_setup(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
- random_ether_addr(dev->dev_addr);
+ eth_hw_addr_random(dev);
ether_setup(dev);
dev->netdev_ops = &br_netdev_ops;
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 568d5bf1753..702a1ae9220 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -446,8 +446,11 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
ip6h->nexthdr = IPPROTO_HOPOPTS;
ip6h->hop_limit = 1;
ipv6_addr_set(&ip6h->daddr, htonl(0xff020000), 0, 0, htonl(1));
- ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6h->daddr, 0,
- &ip6h->saddr);
+ if (ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6h->daddr, 0,
+ &ip6h->saddr)) {
+ kfree_skb(skb);
+ return NULL;
+ }
ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
hopopt = (u8 *)(ip6h + 1);
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index 84122472656..dec4f381713 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -62,6 +62,15 @@ static int brnf_filter_pppoe_tagged __read_mostly = 0;
#define brnf_filter_pppoe_tagged 0
#endif
+#define IS_IP(skb) \
+ (!vlan_tx_tag_present(skb) && skb->protocol == htons(ETH_P_IP))
+
+#define IS_IPV6(skb) \
+ (!vlan_tx_tag_present(skb) && skb->protocol == htons(ETH_P_IPV6))
+
+#define IS_ARP(skb) \
+ (!vlan_tx_tag_present(skb) && skb->protocol == htons(ETH_P_ARP))
+
static inline __be16 vlan_proto(const struct sk_buff *skb)
{
if (vlan_tx_tag_present(skb))
@@ -639,8 +648,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
return NF_DROP;
br = p->br;
- if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb) ||
- IS_PPPOE_IPV6(skb)) {
+ if (IS_IPV6(skb) || IS_VLAN_IPV6(skb) || IS_PPPOE_IPV6(skb)) {
if (!brnf_call_ip6tables && !br->nf_call_ip6tables)
return NF_ACCEPT;
@@ -651,8 +659,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
if (!brnf_call_iptables && !br->nf_call_iptables)
return NF_ACCEPT;
- if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb) &&
- !IS_PPPOE_IP(skb))
+ if (!IS_IP(skb) && !IS_VLAN_IP(skb) && !IS_PPPOE_IP(skb))
return NF_ACCEPT;
nf_bridge_pull_encap_header_rcsum(skb);
@@ -701,7 +708,7 @@ static int br_nf_forward_finish(struct sk_buff *skb)
struct nf_bridge_info *nf_bridge = skb->nf_bridge;
struct net_device *in;
- if (skb->protocol != htons(ETH_P_ARP) && !IS_VLAN_ARP(skb)) {
+ if (!IS_ARP(skb) && !IS_VLAN_ARP(skb)) {
in = nf_bridge->physindev;
if (nf_bridge->mask & BRNF_PKT_TYPE) {
skb->pkt_type = PACKET_OTHERHOST;
@@ -718,6 +725,7 @@ static int br_nf_forward_finish(struct sk_buff *skb)
return 0;
}
+
/* This is the 'purely bridged' case. For IP, we pass the packet to
* netfilter with indev and outdev set to the bridge device,
* but we are still able to filter on the 'real' indev/outdev
@@ -744,11 +752,9 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb,
if (!parent)
return NF_DROP;
- if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb) ||
- IS_PPPOE_IP(skb))
+ if (IS_IP(skb) || IS_VLAN_IP(skb) || IS_PPPOE_IP(skb))
pf = PF_INET;
- else if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb) ||
- IS_PPPOE_IPV6(skb))
+ else if (IS_IPV6(skb) || IS_VLAN_IPV6(skb) || IS_PPPOE_IPV6(skb))
pf = PF_INET6;
else
return NF_ACCEPT;
@@ -795,7 +801,7 @@ static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff *skb,
if (!brnf_call_arptables && !br->nf_call_arptables)
return NF_ACCEPT;
- if (skb->protocol != htons(ETH_P_ARP)) {
+ if (!IS_ARP(skb)) {
if (!IS_VLAN_ARP(skb))
return NF_ACCEPT;
nf_bridge_pull_encap_header(skb);
@@ -853,11 +859,9 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb,
if (!realoutdev)
return NF_DROP;
- if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb) ||
- IS_PPPOE_IP(skb))
+ if (IS_IP(skb) || IS_VLAN_IP(skb) || IS_PPPOE_IP(skb))
pf = PF_INET;
- else if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb) ||
- IS_PPPOE_IPV6(skb))
+ else if (IS_IPV6(skb) || IS_VLAN_IPV6(skb) || IS_PPPOE_IPV6(skb))
pf = PF_INET6;
else
return NF_ACCEPT;
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index dd147d78a58..8c836d96ba7 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -17,9 +17,9 @@
#include "br_private_stp.h"
/* since time values in bpdu are in jiffies and then scaled (1/256)
- * before sending, make sure that is at least one.
+ * before sending, make sure that is at least one STP tick.
*/
-#define MESSAGE_AGE_INCR ((HZ < 256) ? 1 : (HZ/256))
+#define MESSAGE_AGE_INCR ((HZ / 256) + 1)
static const char *const br_port_state_names[] = {
[BR_STATE_DISABLED] = "disabled",
@@ -31,7 +31,7 @@ static const char *const br_port_state_names[] = {
void br_log_state(const struct net_bridge_port *p)
{
- br_info(p->br, "port %u(%s) entering %s state\n",
+ br_info(p->br, "port %u(%s) entered %s state\n",
(unsigned) p->port_no, p->dev->name,
br_port_state_names[p->state]);
}
@@ -186,7 +186,7 @@ static void br_record_config_information(struct net_bridge_port *p,
p->designated_cost = bpdu->root_path_cost;
p->designated_bridge = bpdu->bridge_id;
p->designated_port = bpdu->port_id;
- p->designated_age = jiffies + bpdu->message_age;
+ p->designated_age = jiffies - bpdu->message_age;
mod_timer(&p->message_age_timer, jiffies
+ (p->br->max_age - bpdu->message_age));
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index 19308e305d8..f494496373d 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -98,14 +98,13 @@ void br_stp_disable_port(struct net_bridge_port *p)
struct net_bridge *br = p->br;
int wasroot;
- br_log_state(p);
-
wasroot = br_is_root_bridge(br);
br_become_designated_port(p);
p->state = BR_STATE_DISABLED;
p->topology_change_ack = 0;
p->config_pending = 0;
+ br_log_state(p);
br_ifinfo_notify(RTM_NEWLINK, p);
del_timer(&p->message_age_timer);
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index 5864cc49136..5fe2ff3b01e 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -1335,7 +1335,12 @@ static inline int ebt_make_matchname(const struct ebt_entry_match *m,
const char *base, char __user *ubase)
{
char __user *hlp = ubase + ((char *)m - base);
- if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
+ char name[EBT_FUNCTION_MAXNAMELEN] = {};
+
+ /* ebtables expects 32 bytes long names but xt_match names are 29 bytes
+ long. Copy 29 bytes and fill remaining bytes with zeroes. */
+ strncpy(name, m->u.match->name, sizeof(name));
+ if (copy_to_user(hlp, name, EBT_FUNCTION_MAXNAMELEN))
return -EFAULT;
return 0;
}
@@ -1344,7 +1349,10 @@ static inline int ebt_make_watchername(const struct ebt_entry_watcher *w,
const char *base, char __user *ubase)
{
char __user *hlp = ubase + ((char *)w - base);
- if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
+ char name[EBT_FUNCTION_MAXNAMELEN] = {};
+
+ strncpy(name, w->u.watcher->name, sizeof(name));
+ if (copy_to_user(hlp , name, EBT_FUNCTION_MAXNAMELEN))
return -EFAULT;
return 0;
}
@@ -1355,6 +1363,7 @@ ebt_make_names(struct ebt_entry *e, const char *base, char __user *ubase)
int ret;
char __user *hlp;
const struct ebt_entry_target *t;
+ char name[EBT_FUNCTION_MAXNAMELEN] = {};
if (e->bitmask == 0)
return 0;
@@ -1368,7 +1377,8 @@ ebt_make_names(struct ebt_entry *e, const char *base, char __user *ubase)
ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
if (ret != 0)
return ret;
- if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
+ strncpy(name, t->u.target->name, sizeof(name));
+ if (copy_to_user(hlp, name, EBT_FUNCTION_MAXNAMELEN))
return -EFAULT;
return 0;
}
@@ -1893,10 +1903,7 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt,
switch (compat_mwt) {
case EBT_COMPAT_MATCH:
- match = try_then_request_module(xt_find_match(NFPROTO_BRIDGE,
- name, 0), "ebt_%s", name);
- if (match == NULL)
- return -ENOENT;
+ match = xt_request_find_match(NFPROTO_BRIDGE, name, 0);
if (IS_ERR(match))
return PTR_ERR(match);
@@ -1915,10 +1922,7 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt,
break;
case EBT_COMPAT_WATCHER: /* fallthrough */
case EBT_COMPAT_TARGET:
- wt = try_then_request_module(xt_find_target(NFPROTO_BRIDGE,
- name, 0), "ebt_%s", name);
- if (wt == NULL)
- return -ENOENT;
+ wt = xt_request_find_target(NFPROTO_BRIDGE, name, 0);
if (IS_ERR(wt))
return PTR_ERR(wt);
off = xt_compat_target_offset(wt);
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
index 82c57069415..aa6f716524f 100644
--- a/net/caif/caif_dev.c
+++ b/net/caif/caif_dev.c
@@ -162,7 +162,6 @@ void caif_flow_cb(struct sk_buff *skb)
static int transmit(struct cflayer *layer, struct cfpkt *pkt)
{
int err, high = 0, qlen = 0;
- struct caif_dev_common *caifdev;
struct caif_device_entry *caifd =
container_of(layer, struct caif_device_entry, layer);
struct sk_buff *skb;
@@ -174,7 +173,6 @@ static int transmit(struct cflayer *layer, struct cfpkt *pkt)
skb->dev = caifd->netdev;
skb_reset_network_header(skb);
skb->protocol = htons(ETH_P_CAIF);
- caifdev = netdev_priv(caifd->netdev);
/* Check if we need to handle xoff */
if (likely(caifd->netdev->tx_queue_len == 0))
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
index a9862808645..5016fa57b62 100644
--- a/net/caif/caif_socket.c
+++ b/net/caif/caif_socket.c
@@ -43,34 +43,9 @@ enum caif_states {
#define TX_FLOW_ON_BIT 1
#define RX_FLOW_ON_BIT 2
-static struct dentry *debugfsdir;
-
-#ifdef CONFIG_DEBUG_FS
-struct debug_fs_counter {
- atomic_t caif_nr_socks;
- atomic_t caif_sock_create;
- atomic_t num_connect_req;
- atomic_t num_connect_resp;
- atomic_t num_connect_fail_resp;
- atomic_t num_disconnect;
- atomic_t num_remote_shutdown_ind;
- atomic_t num_tx_flow_off_ind;
- atomic_t num_tx_flow_on_ind;
- atomic_t num_rx_flow_off;
- atomic_t num_rx_flow_on;
-};
-static struct debug_fs_counter cnt;
-#define dbfs_atomic_inc(v) atomic_inc_return(v)
-#define dbfs_atomic_dec(v) atomic_dec_return(v)
-#else
-#define dbfs_atomic_inc(v) 0
-#define dbfs_atomic_dec(v) 0
-#endif
-
struct caifsock {
struct sock sk; /* must be first member */
struct cflayer layer;
- char name[CAIF_LAYER_NAME_SZ]; /* Used for debugging */
u32 flow_state;
struct caif_connect_request conn_req;
struct mutex readlock;
@@ -161,7 +136,6 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
atomic_read(&cf_sk->sk.sk_rmem_alloc),
sk_rcvbuf_lowwater(cf_sk));
set_rx_flow_off(cf_sk);
- dbfs_atomic_inc(&cnt.num_rx_flow_off);
caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ);
}
@@ -172,7 +146,6 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
set_rx_flow_off(cf_sk);
if (net_ratelimit())
pr_debug("sending flow OFF due to rmem_schedule\n");
- dbfs_atomic_inc(&cnt.num_rx_flow_off);
caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ);
}
skb->dev = NULL;
@@ -233,14 +206,12 @@ static void caif_ctrl_cb(struct cflayer *layr,
switch (flow) {
case CAIF_CTRLCMD_FLOW_ON_IND:
/* OK from modem to start sending again */
- dbfs_atomic_inc(&cnt.num_tx_flow_on_ind);
set_tx_flow_on(cf_sk);
cf_sk->sk.sk_state_change(&cf_sk->sk);
break;
case CAIF_CTRLCMD_FLOW_OFF_IND:
/* Modem asks us to shut up */
- dbfs_atomic_inc(&cnt.num_tx_flow_off_ind);
set_tx_flow_off(cf_sk);
cf_sk->sk.sk_state_change(&cf_sk->sk);
break;
@@ -249,7 +220,6 @@ static void caif_ctrl_cb(struct cflayer *layr,
/* We're now connected */
caif_client_register_refcnt(&cf_sk->layer,
cfsk_hold, cfsk_put);
- dbfs_atomic_inc(&cnt.num_connect_resp);
cf_sk->sk.sk_state = CAIF_CONNECTED;
set_tx_flow_on(cf_sk);
cf_sk->sk.sk_state_change(&cf_sk->sk);
@@ -263,7 +233,6 @@ static void caif_ctrl_cb(struct cflayer *layr,
case CAIF_CTRLCMD_INIT_FAIL_RSP:
/* Connect request failed */
- dbfs_atomic_inc(&cnt.num_connect_fail_resp);
cf_sk->sk.sk_err = ECONNREFUSED;
cf_sk->sk.sk_state = CAIF_DISCONNECTED;
cf_sk->sk.sk_shutdown = SHUTDOWN_MASK;
@@ -277,7 +246,6 @@ static void caif_ctrl_cb(struct cflayer *layr,
case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
/* Modem has closed this connection, or device is down. */
- dbfs_atomic_inc(&cnt.num_remote_shutdown_ind);
cf_sk->sk.sk_shutdown = SHUTDOWN_MASK;
cf_sk->sk.sk_err = ECONNRESET;
set_rx_flow_on(cf_sk);
@@ -297,7 +265,6 @@ static void caif_check_flow_release(struct sock *sk)
return;
if (atomic_read(&sk->sk_rmem_alloc) <= sk_rcvbuf_lowwater(cf_sk)) {
- dbfs_atomic_inc(&cnt.num_rx_flow_on);
set_rx_flow_on(cf_sk);
caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_ON_REQ);
}
@@ -539,8 +506,10 @@ static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk,
pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb);
memset(skb->cb, 0, sizeof(struct caif_payload_info));
- if (cf_sk->layer.dn == NULL)
+ if (cf_sk->layer.dn == NULL) {
+ kfree_skb(skb);
return -EINVAL;
+ }
return cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt);
}
@@ -683,10 +652,10 @@ static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
}
err = transmit_skb(skb, cf_sk,
msg->msg_flags&MSG_DONTWAIT, timeo);
- if (err < 0) {
- kfree_skb(skb);
+ if (err < 0)
+ /* skb is already freed */
goto pipe_err;
- }
+
sent += size;
}
@@ -854,7 +823,6 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr,
/*ifindex = id of the interface.*/
cf_sk->conn_req.ifindex = cf_sk->sk.sk_bound_dev_if;
- dbfs_atomic_inc(&cnt.num_connect_req);
cf_sk->layer.receive = caif_sktrecv_cb;
err = caif_connect_client(sock_net(sk), &cf_sk->conn_req,
@@ -943,8 +911,6 @@ static int caif_release(struct socket *sock)
spin_unlock_bh(&sk->sk_receive_queue.lock);
sock->sk = NULL;
- dbfs_atomic_inc(&cnt.num_disconnect);
-
WARN_ON(IS_ERR(cf_sk->debugfs_socket_dir));
if (cf_sk->debugfs_socket_dir != NULL)
debugfs_remove_recursive(cf_sk->debugfs_socket_dir);
@@ -1052,14 +1018,12 @@ static void caif_sock_destructor(struct sock *sk)
return;
}
sk_stream_kill_queues(&cf_sk->sk);
- dbfs_atomic_dec(&cnt.caif_nr_socks);
caif_free_client(&cf_sk->layer);
}
static int caif_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
- int num;
struct sock *sk = NULL;
struct caifsock *cf_sk = NULL;
static struct proto prot = {.name = "PF_CAIF",
@@ -1120,34 +1084,6 @@ static int caif_create(struct net *net, struct socket *sock, int protocol,
cf_sk->sk.sk_priority = CAIF_PRIO_NORMAL;
cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY;
cf_sk->conn_req.protocol = protocol;
- /* Increase the number of sockets created. */
- dbfs_atomic_inc(&cnt.caif_nr_socks);
- num = dbfs_atomic_inc(&cnt.caif_sock_create);
-#ifdef CONFIG_DEBUG_FS
- if (!IS_ERR(debugfsdir)) {
-
- /* Fill in some information concerning the misc socket. */
- snprintf(cf_sk->name, sizeof(cf_sk->name), "cfsk%d", num);
-
- cf_sk->debugfs_socket_dir =
- debugfs_create_dir(cf_sk->name, debugfsdir);
-
- debugfs_create_u32("sk_state", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir,
- (u32 *) &cf_sk->sk.sk_state);
- debugfs_create_u32("flow_state", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir, &cf_sk->flow_state);
- debugfs_create_u32("sk_rmem_alloc", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir,
- (u32 *) &cf_sk->sk.sk_rmem_alloc);
- debugfs_create_u32("sk_wmem_alloc", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir,
- (u32 *) &cf_sk->sk.sk_wmem_alloc);
- debugfs_create_u32("identity", S_IRUSR | S_IWUSR,
- cf_sk->debugfs_socket_dir,
- (u32 *) &cf_sk->layer.id);
- }
-#endif
release_sock(&cf_sk->sk);
return 0;
}
@@ -1159,7 +1095,7 @@ static struct net_proto_family caif_family_ops = {
.owner = THIS_MODULE,
};
-static int af_caif_init(void)
+static int __init caif_sktinit_module(void)
{
int err = sock_register(&caif_family_ops);
if (!err)
@@ -1167,54 +1103,9 @@ static int af_caif_init(void)
return 0;
}
-static int __init caif_sktinit_module(void)
-{
-#ifdef CONFIG_DEBUG_FS
- debugfsdir = debugfs_create_dir("caif_sk", NULL);
- if (!IS_ERR(debugfsdir)) {
- debugfs_create_u32("num_sockets", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.caif_nr_socks);
- debugfs_create_u32("num_create", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.caif_sock_create);
- debugfs_create_u32("num_connect_req", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_connect_req);
- debugfs_create_u32("num_connect_resp", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_connect_resp);
- debugfs_create_u32("num_connect_fail_resp", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_connect_fail_resp);
- debugfs_create_u32("num_disconnect", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_disconnect);
- debugfs_create_u32("num_remote_shutdown_ind",
- S_IRUSR | S_IWUSR, debugfsdir,
- (u32 *) &cnt.num_remote_shutdown_ind);
- debugfs_create_u32("num_tx_flow_off_ind", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_tx_flow_off_ind);
- debugfs_create_u32("num_tx_flow_on_ind", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_tx_flow_on_ind);
- debugfs_create_u32("num_rx_flow_off", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_rx_flow_off);
- debugfs_create_u32("num_rx_flow_on", S_IRUSR | S_IWUSR,
- debugfsdir,
- (u32 *) &cnt.num_rx_flow_on);
- }
-#endif
- return af_caif_init();
-}
-
static void __exit caif_sktexit_module(void)
{
sock_unregister(PF_CAIF);
- if (debugfsdir != NULL)
- debugfs_remove_recursive(debugfsdir);
}
module_init(caif_sktinit_module);
module_exit(caif_sktexit_module);
diff --git a/net/caif/cfdbgl.c b/net/caif/cfdbgl.c
index 65d6ef3cf9a..2914659eb9b 100644
--- a/net/caif/cfdbgl.c
+++ b/net/caif/cfdbgl.c
@@ -41,8 +41,10 @@ static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt)
struct caif_payload_info *info;
int ret;
- if (!cfsrvl_ready(service, &ret))
+ if (!cfsrvl_ready(service, &ret)) {
+ cfpkt_destroy(pkt);
return ret;
+ }
/* Add info for MUX-layer to route the packet out */
info = cfpkt_info(pkt);
diff --git a/net/caif/cfdgml.c b/net/caif/cfdgml.c
index 0f5ff27aa41..a63f4a5f5af 100644
--- a/net/caif/cfdgml.c
+++ b/net/caif/cfdgml.c
@@ -86,12 +86,17 @@ static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt)
struct caif_payload_info *info;
struct cfsrvl *service = container_obj(layr);
int ret;
- if (!cfsrvl_ready(service, &ret))
+
+ if (!cfsrvl_ready(service, &ret)) {
+ cfpkt_destroy(pkt);
return ret;
+ }
/* STE Modem cannot handle more than 1500 bytes datagrams */
- if (cfpkt_getlen(pkt) > DGM_MTU)
+ if (cfpkt_getlen(pkt) > DGM_MTU) {
+ cfpkt_destroy(pkt);
return -EMSGSIZE;
+ }
cfpkt_add_head(pkt, &zero, 3);
packet_type = 0x08; /* B9 set - UNCLASSIFIED */
diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c
index b36f24a4c8e..94b08612a4d 100644
--- a/net/caif/cfmuxl.c
+++ b/net/caif/cfmuxl.c
@@ -248,7 +248,6 @@ static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
{
struct cfmuxl *muxl = container_obj(layr);
struct cflayer *layer;
- int idx;
rcu_read_lock();
list_for_each_entry_rcu(layer, &muxl->srvl_list, node) {
@@ -257,14 +256,9 @@ static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
if ((ctrl == _CAIF_CTRLCMD_PHYIF_DOWN_IND ||
ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) &&
- layer->id != 0) {
-
- idx = layer->id % UP_CACHE_SIZE;
- spin_lock_bh(&muxl->receive_lock);
- RCU_INIT_POINTER(muxl->up_cache[idx], NULL);
- list_del_rcu(&layer->node);
- spin_unlock_bh(&muxl->receive_lock);
- }
+ layer->id != 0)
+ cfmuxl_remove_uplayer(layr, layer->id);
+
/* NOTE: ctrlcmd is not allowed to block */
layer->ctrlcmd(layer, ctrl, phyid);
}
diff --git a/net/caif/cfrfml.c b/net/caif/cfrfml.c
index 6dc75d4f8d9..2b563ad0459 100644
--- a/net/caif/cfrfml.c
+++ b/net/caif/cfrfml.c
@@ -184,6 +184,11 @@ out:
rfml->serv.dev_info.id);
}
spin_unlock(&rfml->sync);
+
+ if (unlikely(err == -EAGAIN))
+ /* It is not possible to recover after drop of a fragment */
+ err = -EIO;
+
return err;
}
@@ -218,7 +223,7 @@ static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt)
caif_assert(layr->dn->transmit != NULL);
if (!cfsrvl_ready(&rfml->serv, &err))
- return err;
+ goto out;
err = -EPROTO;
if (cfpkt_getlen(pkt) <= RFM_HEAD_SIZE-1)
@@ -251,8 +256,11 @@ static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt)
err = cfrfml_transmit_segment(rfml, frontpkt);
- if (err != 0)
+ if (err != 0) {
+ frontpkt = NULL;
goto out;
+ }
+
frontpkt = rearpkt;
rearpkt = NULL;
@@ -286,19 +294,8 @@ out:
if (rearpkt)
cfpkt_destroy(rearpkt);
- if (frontpkt && frontpkt != pkt) {
-
+ if (frontpkt)
cfpkt_destroy(frontpkt);
- /*
- * Socket layer will free the original packet,
- * but this packet may already be sent and
- * freed. So we have to return 0 in this case
- * to avoid socket layer to re-free this packet.
- * The return of shutdown indication will
- * cause connection to be invalidated anyhow.
- */
- err = 0;
- }
}
return err;
diff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c
index b99f5b22689..4aa33d4496b 100644
--- a/net/caif/cfsrvl.c
+++ b/net/caif/cfsrvl.c
@@ -174,15 +174,11 @@ void cfsrvl_init(struct cfsrvl *service,
bool cfsrvl_ready(struct cfsrvl *service, int *err)
{
- if (service->open && service->modem_flow_on && service->phy_flow_on)
- return true;
if (!service->open) {
*err = -ENOTCONN;
return false;
}
- caif_assert(!(service->modem_flow_on && service->phy_flow_on));
- *err = -EAGAIN;
- return false;
+ return true;
}
u8 cfsrvl_getphyid(struct cflayer *layer)
diff --git a/net/caif/cfutill.c b/net/caif/cfutill.c
index 53e49f3e3af..86d2dadb4b7 100644
--- a/net/caif/cfutill.c
+++ b/net/caif/cfutill.c
@@ -84,8 +84,11 @@ static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt)
caif_assert(layr != NULL);
caif_assert(layr->dn != NULL);
caif_assert(layr->dn->transmit != NULL);
- if (!cfsrvl_ready(service, &ret))
+
+ if (!cfsrvl_ready(service, &ret)) {
+ cfpkt_destroy(pkt);
return ret;
+ }
cfpkt_add_head(pkt, &zero, 1);
/* Add info for MUX-layer to route the packet out. */
diff --git a/net/caif/cfvidl.c b/net/caif/cfvidl.c
index e3f37db40ac..a8e2a2d758a 100644
--- a/net/caif/cfvidl.c
+++ b/net/caif/cfvidl.c
@@ -50,8 +50,12 @@ static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt)
struct caif_payload_info *info;
u32 videoheader = 0;
int ret;
- if (!cfsrvl_ready(service, &ret))
+
+ if (!cfsrvl_ready(service, &ret)) {
+ cfpkt_destroy(pkt);
return ret;
+ }
+
cfpkt_add_head(pkt, &videoheader, 4);
/* Add info for MUX-layer to route the packet out */
info = cfpkt_info(pkt);
diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c
index 865690948bb..20618dd3088 100644
--- a/net/caif/chnl_net.c
+++ b/net/caif/chnl_net.c
@@ -28,6 +28,7 @@
/* 5 sec. connect timeout */
#define CONNECT_TIMEOUT (5 * HZ)
#define CAIF_NET_DEFAULT_QUEUE_LEN 500
+#define UNDEF_CONNID 0xffffffff
/*This list is protected by the rtnl lock. */
static LIST_HEAD(chnl_net_list);
@@ -72,14 +73,12 @@ static void robust_list_del(struct list_head *delete_node)
static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt)
{
struct sk_buff *skb;
- struct chnl_net *priv = container_of(layr, struct chnl_net, chnl);
+ struct chnl_net *priv;
int pktlen;
- int err = 0;
const u8 *ip_version;
u8 buf;
priv = container_of(layr, struct chnl_net, chnl);
-
if (!priv)
return -EINVAL;
@@ -95,8 +94,7 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt)
/* check the version of IP */
ip_version = skb_header_pointer(skb, 0, 1, &buf);
- if (!ip_version)
- return -EINVAL;
+
switch (*ip_version >> 4) {
case 4:
skb->protocol = htons(ETH_P_IP);
@@ -105,6 +103,7 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt)
skb->protocol = htons(ETH_P_IPV6);
break;
default:
+ priv->netdev->stats.rx_errors++;
return -EINVAL;
}
@@ -123,7 +122,7 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt)
priv->netdev->stats.rx_packets++;
priv->netdev->stats.rx_bytes += pktlen;
- return err;
+ return 0;
}
static int delete_device(struct chnl_net *dev)
@@ -221,11 +220,13 @@ static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (skb->len > priv->netdev->mtu) {
pr_warn("Size of skb exceeded MTU\n");
+ dev->stats.tx_errors++;
return -ENOSPC;
}
if (!priv->flowenabled) {
pr_debug("dropping packets flow off\n");
+ dev->stats.tx_dropped++;
return NETDEV_TX_BUSY;
}
@@ -240,8 +241,7 @@ static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Send the packet down the stack. */
result = priv->chnl.dn->transmit(priv->chnl.dn, pkt);
if (result) {
- if (result == -EAGAIN)
- result = NETDEV_TX_BUSY;
+ dev->stats.tx_dropped++;
return result;
}
@@ -409,7 +409,7 @@ static void ipcaif_net_setup(struct net_device *dev)
priv->conn_req.link_selector = CAIF_LINK_HIGH_BANDW;
priv->conn_req.priority = CAIF_PRIO_LOW;
/* Insert illegal value */
- priv->conn_req.sockaddr.u.dgm.connection_id = 0;
+ priv->conn_req.sockaddr.u.dgm.connection_id = UNDEF_CONNID;
priv->flowenabled = false;
init_waitqueue_head(&priv->netmgmt_wq);
@@ -472,9 +472,11 @@ static int ipcaif_newlink(struct net *src_net, struct net_device *dev,
else
list_add(&caifdev->list_field, &chnl_net_list);
- /* Take ifindex as connection-id if null */
- if (caifdev->conn_req.sockaddr.u.dgm.connection_id == 0)
+ /* Use ifindex as connection id, and use loopback channel default. */
+ if (caifdev->conn_req.sockaddr.u.dgm.connection_id == UNDEF_CONNID) {
caifdev->conn_req.sockaddr.u.dgm.connection_id = dev->ifindex;
+ caifdev->conn_req.protocol = CAIFPROTO_DATAGRAM_LOOP;
+ }
return ret;
}
diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c
index 97f70e50ad3..761ad9d6cc3 100644
--- a/net/ceph/ceph_common.c
+++ b/net/ceph/ceph_common.c
@@ -85,8 +85,6 @@ int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid)
} else {
pr_info("client%lld fsid %pU\n", ceph_client_id(client), fsid);
memcpy(&client->fsid, fsid, sizeof(*fsid));
- ceph_debugfs_client_init(client);
- client->have_fsid = true;
}
return 0;
}
diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c
index 0b62deae42b..1845cde2622 100644
--- a/net/ceph/mon_client.c
+++ b/net/ceph/mon_client.c
@@ -8,8 +8,8 @@
#include <linux/ceph/mon_client.h>
#include <linux/ceph/libceph.h>
+#include <linux/ceph/debugfs.h>
#include <linux/ceph/decode.h>
-
#include <linux/ceph/auth.h>
/*
@@ -340,8 +340,19 @@ static void ceph_monc_handle_map(struct ceph_mon_client *monc,
client->monc.monmap = monmap;
kfree(old);
+ if (!client->have_fsid) {
+ client->have_fsid = true;
+ mutex_unlock(&monc->mutex);
+ /*
+ * do debugfs initialization without mutex to avoid
+ * creating a locking dependency
+ */
+ ceph_debugfs_client_init(client);
+ goto out_unlocked;
+ }
out:
mutex_unlock(&monc->mutex);
+out_unlocked:
wake_up_all(&client->auth_wq);
}
diff --git a/net/compat.c b/net/compat.c
index 6def90e0a11..64b4515a64e 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -79,7 +79,7 @@ int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg)
/* I've named the args so it is easy to tell whose space the pointers are in. */
int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov,
- struct sockaddr *kern_address, int mode)
+ struct sockaddr_storage *kern_address, int mode)
{
int tot_len;
diff --git a/net/core/datagram.c b/net/core/datagram.c
index 68bbf9f65cb..d3cf12f62c8 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -132,6 +132,8 @@ out_noerr:
* __skb_recv_datagram - Receive a datagram skbuff
* @sk: socket
* @flags: MSG_ flags
+ * @off: an offset in bytes to peek skb from. Returns an offset
+ * within an skb where data actually starts
* @peeked: returns non-zero if this packet has been seen before
* @err: error code returned
*
@@ -158,7 +160,7 @@ out_noerr:
* the standard around please.
*/
struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags,
- int *peeked, int *err)
+ int *peeked, int *off, int *err)
{
struct sk_buff *skb;
long timeo;
@@ -180,21 +182,25 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags,
* However, this function was correct in any case. 8)
*/
unsigned long cpu_flags;
+ struct sk_buff_head *queue = &sk->sk_receive_queue;
- spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
- skb = skb_peek(&sk->sk_receive_queue);
- if (skb) {
+ spin_lock_irqsave(&queue->lock, cpu_flags);
+ skb_queue_walk(queue, skb) {
*peeked = skb->peeked;
if (flags & MSG_PEEK) {
+ if (*off >= skb->len) {
+ *off -= skb->len;
+ continue;
+ }
skb->peeked = 1;
atomic_inc(&skb->users);
} else
- __skb_unlink(skb, &sk->sk_receive_queue);
- }
- spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
+ __skb_unlink(skb, queue);
- if (skb)
+ spin_unlock_irqrestore(&queue->lock, cpu_flags);
return skb;
+ }
+ spin_unlock_irqrestore(&queue->lock, cpu_flags);
/* User doesn't want to wait */
error = -EAGAIN;
@@ -214,10 +220,10 @@ EXPORT_SYMBOL(__skb_recv_datagram);
struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
int noblock, int *err)
{
- int peeked;
+ int peeked, off = 0;
return __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
- &peeked, err);
+ &peeked, &off, err);
}
EXPORT_SYMBOL(skb_recv_datagram);
diff --git a/net/core/dev.c b/net/core/dev.c
index 115dee1d985..0090809af7b 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -446,7 +446,7 @@ void __dev_remove_pack(struct packet_type *pt)
}
}
- printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt);
+ pr_warn("dev_remove_pack: %p not found\n", pt);
out:
spin_unlock(&ptype_lock);
}
@@ -848,21 +848,21 @@ EXPORT_SYMBOL(dev_get_by_flags_rcu);
* to allow sysfs to work. We also disallow any kind of
* whitespace.
*/
-int dev_valid_name(const char *name)
+bool dev_valid_name(const char *name)
{
if (*name == '\0')
- return 0;
+ return false;
if (strlen(name) >= IFNAMSIZ)
- return 0;
+ return false;
if (!strcmp(name, ".") || !strcmp(name, ".."))
- return 0;
+ return false;
while (*name) {
if (*name == '/' || isspace(*name))
- return 0;
+ return false;
name++;
}
- return 1;
+ return true;
}
EXPORT_SYMBOL(dev_valid_name);
@@ -1039,8 +1039,7 @@ rollback:
memcpy(dev->name, oldname, IFNAMSIZ);
goto rollback;
} else {
- printk(KERN_ERR
- "%s: name change rollback failed: %d.\n",
+ pr_err("%s: name change rollback failed: %d\n",
dev->name, ret);
}
}
@@ -1139,9 +1138,8 @@ void dev_load(struct net *net, const char *name)
no_module = request_module("netdev-%s", name);
if (no_module && capable(CAP_SYS_MODULE)) {
if (!request_module("%s", name))
- pr_err("Loading kernel module for a network device "
-"with CAP_SYS_MODULE (deprecated). Use CAP_NET_ADMIN and alias netdev-%s "
-"instead\n", name);
+ pr_err("Loading kernel module for a network device with CAP_SYS_MODULE (deprecated). Use CAP_NET_ADMIN and alias netdev-%s instead.\n",
+ name);
}
}
EXPORT_SYMBOL(dev_load);
@@ -1655,10 +1653,9 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
if (skb_network_header(skb2) < skb2->data ||
skb2->network_header > skb2->tail) {
if (net_ratelimit())
- printk(KERN_CRIT "protocol %04x is "
- "buggy, dev %s\n",
- ntohs(skb2->protocol),
- dev->name);
+ pr_crit("protocol %04x is buggy, dev %s\n",
+ ntohs(skb2->protocol),
+ dev->name);
skb_reset_network_header(skb2);
}
@@ -1691,9 +1688,7 @@ static void netif_setup_tc(struct net_device *dev, unsigned int txq)
/* If TC0 is invalidated disable TC mapping */
if (tc->offset + tc->count > txq) {
- pr_warning("Number of in use tx queues changed "
- "invalidating tc mappings. Priority "
- "traffic classification disabled!\n");
+ pr_warn("Number of in use tx queues changed invalidating tc mappings. Priority traffic classification disabled!\n");
dev->num_tc = 0;
return;
}
@@ -1704,11 +1699,8 @@ static void netif_setup_tc(struct net_device *dev, unsigned int txq)
tc = &dev->tc_to_txq[q];
if (tc->offset + tc->count > txq) {
- pr_warning("Number of in use tx queues "
- "changed. Priority %i to tc "
- "mapping %i is no longer valid "
- "setting map to 0\n",
- i, q);
+ pr_warn("Number of in use tx queues changed. Priority %i to tc mapping %i is no longer valid. Setting map to 0\n",
+ i, q);
netdev_set_prio_tc_map(dev, i, 0);
}
}
@@ -2014,8 +2006,7 @@ EXPORT_SYMBOL(skb_gso_segment);
void netdev_rx_csum_fault(struct net_device *dev)
{
if (net_ratelimit()) {
- printk(KERN_ERR "%s: hw csum failure.\n",
- dev ? dev->name : "<unknown>");
+ pr_err("%s: hw csum failure\n", dev ? dev->name : "<unknown>");
dump_stack();
}
}
@@ -2332,9 +2323,9 @@ static inline u16 dev_cap_txqueue(struct net_device *dev, u16 queue_index)
{
if (unlikely(queue_index >= dev->real_num_tx_queues)) {
if (net_ratelimit()) {
- pr_warning("%s selects TX queue %d, but "
- "real number of TX queues is %d\n",
- dev->name, queue_index, dev->real_num_tx_queues);
+ pr_warn("%s selects TX queue %d, but real number of TX queues is %d\n",
+ dev->name, queue_index,
+ dev->real_num_tx_queues);
}
return 0;
}
@@ -2578,16 +2569,16 @@ int dev_queue_xmit(struct sk_buff *skb)
}
HARD_TX_UNLOCK(dev, txq);
if (net_ratelimit())
- printk(KERN_CRIT "Virtual device %s asks to "
- "queue packet!\n", dev->name);
+ pr_crit("Virtual device %s asks to queue packet!\n",
+ dev->name);
} else {
/* Recursion is detected! It is possible,
* unfortunately
*/
recursion_alert:
if (net_ratelimit())
- printk(KERN_CRIT "Dead loop on virtual device "
- "%s, fix it urgently!\n", dev->name);
+ pr_crit("Dead loop on virtual device %s, fix it urgently!\n",
+ dev->name);
}
}
@@ -3069,8 +3060,8 @@ static int ing_filter(struct sk_buff *skb, struct netdev_queue *rxq)
if (unlikely(MAX_RED_LOOP < ttl++)) {
if (net_ratelimit())
- pr_warning( "Redir loop detected Dropping packet (%d->%d)\n",
- skb->skb_iif, dev->ifindex);
+ pr_warn("Redir loop detected Dropping packet (%d->%d)\n",
+ skb->skb_iif, dev->ifindex);
return TC_ACT_SHOT;
}
@@ -3500,14 +3491,20 @@ static inline gro_result_t
__napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{
struct sk_buff *p;
+ unsigned int maclen = skb->dev->hard_header_len;
for (p = napi->gro_list; p; p = p->next) {
unsigned long diffs;
diffs = (unsigned long)p->dev ^ (unsigned long)skb->dev;
diffs |= p->vlan_tci ^ skb->vlan_tci;
- diffs |= compare_ether_header(skb_mac_header(p),
- skb_gro_mac_header(skb));
+ if (maclen == ETH_HLEN)
+ diffs |= compare_ether_header(skb_mac_header(p),
+ skb_gro_mac_header(skb));
+ else if (!diffs)
+ diffs = memcmp(skb_mac_header(p),
+ skb_gro_mac_header(skb),
+ maclen);
NAPI_GRO_CB(p)->same_flow = !diffs;
NAPI_GRO_CB(p)->flush = 0;
}
@@ -4491,16 +4488,15 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc)
dev->flags &= ~IFF_PROMISC;
else {
dev->promiscuity -= inc;
- printk(KERN_WARNING "%s: promiscuity touches roof, "
- "set promiscuity failed, promiscuity feature "
- "of device might be broken.\n", dev->name);
+ pr_warn("%s: promiscuity touches roof, set promiscuity failed. promiscuity feature of device might be broken.\n",
+ dev->name);
return -EOVERFLOW;
}
}
if (dev->flags != old_flags) {
- printk(KERN_INFO "device %s %s promiscuous mode\n",
- dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
- "left");
+ pr_info("device %s %s promiscuous mode\n",
+ dev->name,
+ dev->flags & IFF_PROMISC ? "entered" : "left");
if (audit_enabled) {
current_uid_gid(&uid, &gid);
audit_log(current->audit_context, GFP_ATOMIC,
@@ -4573,9 +4569,8 @@ int dev_set_allmulti(struct net_device *dev, int inc)
dev->flags &= ~IFF_ALLMULTI;
else {
dev->allmulti -= inc;
- printk(KERN_WARNING "%s: allmulti touches roof, "
- "set allmulti failed, allmulti feature of "
- "device might be broken.\n", dev->name);
+ pr_warn("%s: allmulti touches roof, set allmulti failed. allmulti feature of device might be broken.\n",
+ dev->name);
return -EOVERFLOW;
}
}
@@ -5232,8 +5227,8 @@ static void rollback_registered_many(struct list_head *head)
* devices and proceed with the remaining.
*/
if (dev->reg_state == NETREG_UNINITIALIZED) {
- pr_debug("unregister_netdevice: device %s/%p never "
- "was registered\n", dev->name, dev);
+ pr_debug("unregister_netdevice: device %s/%p never was registered\n",
+ dev->name, dev);
WARN_ON(1);
list_del(&dev->unreg_list);
@@ -5465,7 +5460,7 @@ static int netif_alloc_rx_queues(struct net_device *dev)
rx = kcalloc(count, sizeof(struct netdev_rx_queue), GFP_KERNEL);
if (!rx) {
- pr_err("netdev: Unable to allocate %u rx queues.\n", count);
+ pr_err("netdev: Unable to allocate %u rx queues\n", count);
return -ENOMEM;
}
dev->_rx = rx;
@@ -5499,8 +5494,7 @@ static int netif_alloc_netdev_queues(struct net_device *dev)
tx = kcalloc(count, sizeof(struct netdev_queue), GFP_KERNEL);
if (!tx) {
- pr_err("netdev: Unable to allocate %u tx queues.\n",
- count);
+ pr_err("netdev: Unable to allocate %u tx queues\n", count);
return -ENOMEM;
}
dev->_tx = tx;
@@ -5759,10 +5753,8 @@ static void netdev_wait_allrefs(struct net_device *dev)
refcnt = netdev_refcnt_read(dev);
if (time_after(jiffies, warning_time + 10 * HZ)) {
- printk(KERN_EMERG "unregister_netdevice: "
- "waiting for %s to become free. Usage "
- "count = %d\n",
- dev->name, refcnt);
+ pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n",
+ dev->name, refcnt);
warning_time = jiffies;
}
}
@@ -5813,7 +5805,7 @@ void netdev_run_todo(void)
list_del(&dev->todo_list);
if (unlikely(dev->reg_state != NETREG_UNREGISTERING)) {
- printk(KERN_ERR "network todo '%s' but state %d\n",
+ pr_err("network todo '%s' but state %d\n",
dev->name, dev->reg_state);
dump_stack();
continue;
@@ -5842,12 +5834,12 @@ void netdev_run_todo(void)
/* Convert net_device_stats to rtnl_link_stats64. They have the same
* fields in the same order, with only the type differing.
*/
-static void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64,
- const struct net_device_stats *netdev_stats)
+void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64,
+ const struct net_device_stats *netdev_stats)
{
#if BITS_PER_LONG == 64
- BUILD_BUG_ON(sizeof(*stats64) != sizeof(*netdev_stats));
- memcpy(stats64, netdev_stats, sizeof(*stats64));
+ BUILD_BUG_ON(sizeof(*stats64) != sizeof(*netdev_stats));
+ memcpy(stats64, netdev_stats, sizeof(*stats64));
#else
size_t i, n = sizeof(*stats64) / sizeof(u64);
const unsigned long *src = (const unsigned long *)netdev_stats;
@@ -5859,6 +5851,7 @@ static void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64,
dst[i] = src[i];
#endif
}
+EXPORT_SYMBOL(netdev_stats_to_stats64);
/**
* dev_get_stats - get network device statistics
@@ -5929,15 +5922,13 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
BUG_ON(strlen(name) >= sizeof(dev->name));
if (txqs < 1) {
- pr_err("alloc_netdev: Unable to allocate device "
- "with zero queues.\n");
+ pr_err("alloc_netdev: Unable to allocate device with zero queues\n");
return NULL;
}
#ifdef CONFIG_RPS
if (rxqs < 1) {
- pr_err("alloc_netdev: Unable to allocate device "
- "with zero RX queues.\n");
+ pr_err("alloc_netdev: Unable to allocate device with zero RX queues\n");
return NULL;
}
#endif
@@ -5953,7 +5944,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
p = kzalloc(alloc_size, GFP_KERNEL);
if (!p) {
- printk(KERN_ERR "alloc_netdev: Unable to allocate device.\n");
+ pr_err("alloc_netdev: Unable to allocate device\n");
return NULL;
}
@@ -6486,8 +6477,8 @@ static void __net_exit default_device_exit(struct net *net)
snprintf(fb_name, IFNAMSIZ, "dev%d", dev->ifindex);
err = dev_change_net_namespace(dev, &init_net, fb_name);
if (err) {
- printk(KERN_EMERG "%s: failed to move %s to init_net: %d\n",
- __func__, dev->name, err);
+ pr_emerg("%s: failed to move %s to init_net: %d\n",
+ __func__, dev->name, err);
BUG();
}
}
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 369b4189452..6d6d7d25caa 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -73,6 +73,8 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
[NETIF_F_RXCSUM_BIT] = "rx-checksum",
[NETIF_F_NOCACHE_COPY_BIT] = "tx-nocache-copy",
[NETIF_F_LOOPBACK_BIT] = "loopback",
+ [NETIF_F_RXFCS_BIT] = "rx-fcs",
+ [NETIF_F_RXALL_BIT] = "rx-all",
};
static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
@@ -1190,6 +1192,8 @@ static noinline_for_stack int ethtool_flash_device(struct net_device *dev,
if (!dev->ethtool_ops->flash_device)
return -EOPNOTSUPP;
+ efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0;
+
return dev->ethtool_ops->flash_device(dev, &efl);
}
diff --git a/net/core/iovec.c b/net/core/iovec.c
index c40f27e7d20..7e7aeb01de4 100644
--- a/net/core/iovec.c
+++ b/net/core/iovec.c
@@ -35,7 +35,7 @@
* in any case.
*/
-int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode)
+int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode)
{
int size, ct, err;
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index e287346e093..0a68045782d 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -826,6 +826,8 @@ next_elt:
write_unlock_bh(&tbl->lock);
cond_resched();
write_lock_bh(&tbl->lock);
+ nht = rcu_dereference_protected(tbl->nht,
+ lockdep_is_held(&tbl->lock));
}
/* Cycle through all hash buckets every base_reachable_time/2 ticks.
* ARP entry timeouts range from 1/2 base_reachable_time to 3/2
@@ -2165,6 +2167,35 @@ nla_put_failure:
return -EMSGSIZE;
}
+static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn,
+ u32 pid, u32 seq, int type, unsigned int flags,
+ struct neigh_table *tbl)
+{
+ struct nlmsghdr *nlh;
+ struct ndmsg *ndm;
+
+ nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags);
+ if (nlh == NULL)
+ return -EMSGSIZE;
+
+ ndm = nlmsg_data(nlh);
+ ndm->ndm_family = tbl->family;
+ ndm->ndm_pad1 = 0;
+ ndm->ndm_pad2 = 0;
+ ndm->ndm_flags = pn->flags | NTF_PROXY;
+ ndm->ndm_type = NDA_DST;
+ ndm->ndm_ifindex = pn->dev->ifindex;
+ ndm->ndm_state = NUD_NONE;
+
+ NLA_PUT(skb, NDA_DST, tbl->key_len, pn->key);
+
+ return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+}
+
static void neigh_update_notify(struct neighbour *neigh)
{
call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh);
@@ -2214,23 +2245,78 @@ out:
return rc;
}
+static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct pneigh_entry *n;
+ struct net *net = sock_net(skb->sk);
+ int rc, h, s_h = cb->args[3];
+ int idx, s_idx = idx = cb->args[4];
+
+ read_lock_bh(&tbl->lock);
+
+ for (h = 0; h <= PNEIGH_HASHMASK; h++) {
+ if (h < s_h)
+ continue;
+ if (h > s_h)
+ s_idx = 0;
+ for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) {
+ if (dev_net(n->dev) != net)
+ continue;
+ if (idx < s_idx)
+ goto next;
+ if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq,
+ RTM_NEWNEIGH,
+ NLM_F_MULTI, tbl) <= 0) {
+ read_unlock_bh(&tbl->lock);
+ rc = -1;
+ goto out;
+ }
+ next:
+ idx++;
+ }
+ }
+
+ read_unlock_bh(&tbl->lock);
+ rc = skb->len;
+out:
+ cb->args[3] = h;
+ cb->args[4] = idx;
+ return rc;
+
+}
+
static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
{
struct neigh_table *tbl;
int t, family, s_t;
+ int proxy = 0;
+ int err = 0;
read_lock(&neigh_tbl_lock);
family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family;
+
+ /* check for full ndmsg structure presence, family member is
+ * the same for both structures
+ */
+ if (nlmsg_len(cb->nlh) >= sizeof(struct ndmsg) &&
+ ((struct ndmsg *) nlmsg_data(cb->nlh))->ndm_flags == NTF_PROXY)
+ proxy = 1;
+
s_t = cb->args[0];
- for (tbl = neigh_tables, t = 0; tbl; tbl = tbl->next, t++) {
+ for (tbl = neigh_tables, t = 0; tbl && (err >= 0);
+ tbl = tbl->next, t++) {
if (t < s_t || (family && tbl->family != family))
continue;
if (t > s_t)
memset(&cb->args[1], 0, sizeof(cb->args) -
sizeof(cb->args[0]));
- if (neigh_dump_table(tbl, skb, cb) < 0)
- break;
+ if (proxy)
+ err = pneigh_dump_table(tbl, skb, cb);
+ else
+ err = neigh_dump_table(tbl, skb, cb);
}
read_unlock(&neigh_tbl_lock);
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 556b0829866..3d84fb9d887 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -9,6 +9,8 @@
* Copyright (C) 2002 Red Hat, Inc.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/moduleparam.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -45,9 +47,11 @@ static atomic_t trapped;
#define NETPOLL_RX_ENABLED 1
#define NETPOLL_RX_DROP 2
-#define MAX_SKB_SIZE \
- (MAX_UDP_CHUNK + sizeof(struct udphdr) + \
- sizeof(struct iphdr) + sizeof(struct ethhdr))
+#define MAX_SKB_SIZE \
+ (sizeof(struct ethhdr) + \
+ sizeof(struct iphdr) + \
+ sizeof(struct udphdr) + \
+ MAX_UDP_CHUNK)
static void zap_completion_queue(void);
static void arp_reply(struct sk_buff *skb);
@@ -55,6 +59,13 @@ static void arp_reply(struct sk_buff *skb);
static unsigned int carrier_timeout = 4;
module_param(carrier_timeout, uint, 0644);
+#define np_info(np, fmt, ...) \
+ pr_info("%s: " fmt, np->name, ##__VA_ARGS__)
+#define np_err(np, fmt, ...) \
+ pr_err("%s: " fmt, np->name, ##__VA_ARGS__)
+#define np_notice(np, fmt, ...) \
+ pr_notice("%s: " fmt, np->name, ##__VA_ARGS__)
+
static void queue_process(struct work_struct *work)
{
struct netpoll_info *npinfo =
@@ -194,7 +205,7 @@ static void netpoll_poll_dev(struct net_device *dev)
poll_napi(dev);
- if (dev->priv_flags & IFF_SLAVE) {
+ if (dev->flags & IFF_SLAVE) {
if (dev->npinfo) {
struct net_device *bond_dev = dev->master;
struct sk_buff *skb;
@@ -627,18 +638,12 @@ out:
void netpoll_print_options(struct netpoll *np)
{
- printk(KERN_INFO "%s: local port %d\n",
- np->name, np->local_port);
- printk(KERN_INFO "%s: local IP %pI4\n",
- np->name, &np->local_ip);
- printk(KERN_INFO "%s: interface '%s'\n",
- np->name, np->dev_name);
- printk(KERN_INFO "%s: remote port %d\n",
- np->name, np->remote_port);
- printk(KERN_INFO "%s: remote IP %pI4\n",
- np->name, &np->remote_ip);
- printk(KERN_INFO "%s: remote ethernet address %pM\n",
- np->name, np->remote_mac);
+ np_info(np, "local port %d\n", np->local_port);
+ np_info(np, "local IP %pI4\n", &np->local_ip);
+ np_info(np, "interface '%s'\n", np->dev_name);
+ np_info(np, "remote port %d\n", np->remote_port);
+ np_info(np, "remote IP %pI4\n", &np->remote_ip);
+ np_info(np, "remote ethernet address %pM\n", np->remote_mac);
}
EXPORT_SYMBOL(netpoll_print_options);
@@ -680,8 +685,7 @@ int netpoll_parse_options(struct netpoll *np, char *opt)
goto parse_failed;
*delim = 0;
if (*cur == ' ' || *cur == '\t')
- printk(KERN_INFO "%s: warning: whitespace"
- "is not allowed\n", np->name);
+ np_info(np, "warning: whitespace is not allowed\n");
np->remote_port = simple_strtol(cur, NULL, 10);
cur = delim;
}
@@ -705,8 +709,7 @@ int netpoll_parse_options(struct netpoll *np, char *opt)
return 0;
parse_failed:
- printk(KERN_INFO "%s: couldn't parse config at '%s'!\n",
- np->name, cur);
+ np_info(np, "couldn't parse config at '%s'!\n", cur);
return -1;
}
EXPORT_SYMBOL(netpoll_parse_options);
@@ -721,8 +724,8 @@ int __netpoll_setup(struct netpoll *np)
if ((ndev->priv_flags & IFF_DISABLE_NETPOLL) ||
!ndev->netdev_ops->ndo_poll_controller) {
- printk(KERN_ERR "%s: %s doesn't support polling, aborting.\n",
- np->name, np->dev_name);
+ np_err(np, "%s doesn't support polling, aborting\n",
+ np->dev_name);
err = -ENOTSUPP;
goto out;
}
@@ -785,14 +788,12 @@ int netpoll_setup(struct netpoll *np)
if (np->dev_name)
ndev = dev_get_by_name(&init_net, np->dev_name);
if (!ndev) {
- printk(KERN_ERR "%s: %s doesn't exist, aborting.\n",
- np->name, np->dev_name);
+ np_err(np, "%s doesn't exist, aborting\n", np->dev_name);
return -ENODEV;
}
if (ndev->master) {
- printk(KERN_ERR "%s: %s is a slave device, aborting.\n",
- np->name, np->dev_name);
+ np_err(np, "%s is a slave device, aborting\n", np->dev_name);
err = -EBUSY;
goto put;
}
@@ -800,16 +801,14 @@ int netpoll_setup(struct netpoll *np)
if (!netif_running(ndev)) {
unsigned long atmost, atleast;
- printk(KERN_INFO "%s: device %s not up yet, forcing it\n",
- np->name, np->dev_name);
+ np_info(np, "device %s not up yet, forcing it\n", np->dev_name);
rtnl_lock();
err = dev_open(ndev);
rtnl_unlock();
if (err) {
- printk(KERN_ERR "%s: failed to open %s\n",
- np->name, ndev->name);
+ np_err(np, "failed to open %s\n", ndev->name);
goto put;
}
@@ -817,9 +816,7 @@ int netpoll_setup(struct netpoll *np)
atmost = jiffies + carrier_timeout * HZ;
while (!netif_carrier_ok(ndev)) {
if (time_after(jiffies, atmost)) {
- printk(KERN_NOTICE
- "%s: timeout waiting for carrier\n",
- np->name);
+ np_notice(np, "timeout waiting for carrier\n");
break;
}
msleep(1);
@@ -831,9 +828,7 @@ int netpoll_setup(struct netpoll *np)
*/
if (time_before(jiffies, atleast)) {
- printk(KERN_NOTICE "%s: carrier detect appears"
- " untrustworthy, waiting 4 seconds\n",
- np->name);
+ np_notice(np, "carrier detect appears untrustworthy, waiting 4 seconds\n");
msleep(4000);
}
}
@@ -844,15 +839,15 @@ int netpoll_setup(struct netpoll *np)
if (!in_dev || !in_dev->ifa_list) {
rcu_read_unlock();
- printk(KERN_ERR "%s: no IP address for %s, aborting\n",
- np->name, np->dev_name);
+ np_err(np, "no IP address for %s, aborting\n",
+ np->dev_name);
err = -EDESTADDRREQ;
goto put;
}
np->local_ip = in_dev->ifa_list->ifa_local;
rcu_read_unlock();
- printk(KERN_INFO "%s: local IP %pI4\n", np->name, &np->local_ip);
+ np_info(np, "local IP %pI4\n", &np->local_ip);
}
np->dev = ndev;
diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c
index 3a9fd4826b7..4dacc44637e 100644
--- a/net/core/netprio_cgroup.c
+++ b/net/core/netprio_cgroup.c
@@ -58,11 +58,12 @@ static int get_prioidx(u32 *prio)
spin_lock_irqsave(&prioidx_map_lock, flags);
prioidx = find_first_zero_bit(prioidx_map, sizeof(unsigned long) * PRIOIDX_SZ);
+ if (prioidx == sizeof(unsigned long) * PRIOIDX_SZ) {
+ spin_unlock_irqrestore(&prioidx_map_lock, flags);
+ return -ENOSPC;
+ }
set_bit(prioidx, prioidx_map);
spin_unlock_irqrestore(&prioidx_map_lock, flags);
- if (prioidx == sizeof(unsigned long) * PRIOIDX_SZ)
- return -ENOSPC;
-
atomic_set(&max_prioidx, prioidx);
*prio = prioidx;
return 0;
@@ -107,7 +108,7 @@ static void extend_netdev_table(struct net_device *dev, u32 new_len)
static void update_netdev_tables(void)
{
struct net_device *dev;
- u32 max_len = atomic_read(&max_prioidx);
+ u32 max_len = atomic_read(&max_prioidx) + 1;
struct netprio_map *map;
rtnl_lock();
@@ -270,7 +271,6 @@ static int netprio_device_event(struct notifier_block *unused,
{
struct net_device *dev = ptr;
struct netprio_map *old;
- u32 max_len = atomic_read(&max_prioidx);
/*
* Note this is called with rtnl_lock held so we have update side
@@ -278,11 +278,6 @@ static int netprio_device_event(struct notifier_block *unused,
*/
switch (event) {
-
- case NETDEV_REGISTER:
- if (max_len)
- extend_netdev_table(dev, max_len);
- break;
case NETDEV_UNREGISTER:
old = rtnl_dereference(dev->priomap);
RCU_INIT_POINTER(dev->priomap, NULL);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 65aebd45002..1a63c6efd2e 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -60,7 +60,6 @@ struct rtnl_link {
};
static DEFINE_MUTEX(rtnl_mutex);
-static u16 min_ifinfo_dump_size;
void rtnl_lock(void)
{
@@ -724,10 +723,11 @@ static void copy_rtnl_link_stats64(void *v, const struct rtnl_link_stats64 *b)
}
/* All VF info */
-static inline int rtnl_vfinfo_size(const struct net_device *dev)
+static inline int rtnl_vfinfo_size(const struct net_device *dev,
+ u32 ext_filter_mask)
{
- if (dev->dev.parent && dev_is_pci(dev->dev.parent)) {
-
+ if (dev->dev.parent && dev_is_pci(dev->dev.parent) &&
+ (ext_filter_mask & RTEXT_FILTER_VF)) {
int num_vfs = dev_num_vf(dev->dev.parent);
size_t size = nla_total_size(sizeof(struct nlattr));
size += nla_total_size(num_vfs * sizeof(struct nlattr));
@@ -766,7 +766,8 @@ static size_t rtnl_port_size(const struct net_device *dev)
return port_self_size;
}
-static noinline size_t if_nlmsg_size(const struct net_device *dev)
+static noinline size_t if_nlmsg_size(const struct net_device *dev,
+ u32 ext_filter_mask)
{
return NLMSG_ALIGN(sizeof(struct ifinfomsg))
+ nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
@@ -784,8 +785,9 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev)
+ nla_total_size(4) /* IFLA_MASTER */
+ nla_total_size(1) /* IFLA_OPERSTATE */
+ nla_total_size(1) /* IFLA_LINKMODE */
- + nla_total_size(4) /* IFLA_NUM_VF */
- + rtnl_vfinfo_size(dev) /* IFLA_VFINFO_LIST */
+ + nla_total_size(ext_filter_mask
+ & RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */
+ + rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */
+ rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */
+ rtnl_link_get_size(dev) /* IFLA_LINKINFO */
+ rtnl_link_get_af_size(dev); /* IFLA_AF_SPEC */
@@ -868,7 +870,7 @@ static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev)
static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
int type, u32 pid, u32 seq, u32 change,
- unsigned int flags)
+ unsigned int flags, u32 ext_filter_mask)
{
struct ifinfomsg *ifm;
struct nlmsghdr *nlh;
@@ -941,10 +943,11 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
goto nla_put_failure;
copy_rtnl_link_stats64(nla_data(attr), stats);
- if (dev->dev.parent)
+ if (dev->dev.parent && (ext_filter_mask & RTEXT_FILTER_VF))
NLA_PUT_U32(skb, IFLA_NUM_VF, dev_num_vf(dev->dev.parent));
- if (dev->netdev_ops->ndo_get_vf_config && dev->dev.parent) {
+ if (dev->netdev_ops->ndo_get_vf_config && dev->dev.parent
+ && (ext_filter_mask & RTEXT_FILTER_VF)) {
int i;
struct nlattr *vfinfo, *vf;
@@ -1048,6 +1051,8 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
struct net_device *dev;
struct hlist_head *head;
struct hlist_node *node;
+ struct nlattr *tb[IFLA_MAX+1];
+ u32 ext_filter_mask = 0;
s_h = cb->args[0];
s_idx = cb->args[1];
@@ -1055,6 +1060,13 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
rcu_read_lock();
cb->seq = net->dev_base_seq;
+ if (nlmsg_parse(cb->nlh, sizeof(struct rtgenmsg), tb, IFLA_MAX,
+ ifla_policy) >= 0) {
+
+ if (tb[IFLA_EXT_MASK])
+ ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
+ }
+
for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
idx = 0;
head = &net->dev_index_head[h];
@@ -1064,7 +1076,8 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, 0,
- NLM_F_MULTI) <= 0)
+ NLM_F_MULTI,
+ ext_filter_mask) <= 0)
goto out;
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
@@ -1100,6 +1113,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = {
[IFLA_VF_PORTS] = { .type = NLA_NESTED },
[IFLA_PORT_SELF] = { .type = NLA_NESTED },
[IFLA_AF_SPEC] = { .type = NLA_NESTED },
+ [IFLA_EXT_MASK] = { .type = NLA_U32 },
};
EXPORT_SYMBOL(ifla_policy);
@@ -1119,6 +1133,8 @@ static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
.len = sizeof(struct ifla_vf_vlan) },
[IFLA_VF_TX_RATE] = { .type = NLA_BINARY,
.len = sizeof(struct ifla_vf_tx_rate) },
+ [IFLA_VF_SPOOFCHK] = { .type = NLA_BINARY,
+ .len = sizeof(struct ifla_vf_spoofchk) },
};
static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
@@ -1509,8 +1525,6 @@ errout:
if (send_addr_notify)
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
- min_ifinfo_dump_size = max_t(u16, if_nlmsg_size(dev),
- min_ifinfo_dump_size);
return err;
}
@@ -1842,6 +1856,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
struct net_device *dev = NULL;
struct sk_buff *nskb;
int err;
+ u32 ext_filter_mask = 0;
err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy);
if (err < 0)
@@ -1850,6 +1865,9 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
if (tb[IFLA_IFNAME])
nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
+ if (tb[IFLA_EXT_MASK])
+ ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
+
ifm = nlmsg_data(nlh);
if (ifm->ifi_index > 0)
dev = __dev_get_by_index(net, ifm->ifi_index);
@@ -1861,12 +1879,12 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
if (dev == NULL)
return -ENODEV;
- nskb = nlmsg_new(if_nlmsg_size(dev), GFP_KERNEL);
+ nskb = nlmsg_new(if_nlmsg_size(dev, ext_filter_mask), GFP_KERNEL);
if (nskb == NULL)
return -ENOBUFS;
err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).pid,
- nlh->nlmsg_seq, 0, 0);
+ nlh->nlmsg_seq, 0, 0, ext_filter_mask);
if (err < 0) {
/* -EMSGSIZE implies BUG in if_nlmsg_size */
WARN_ON(err == -EMSGSIZE);
@@ -1877,8 +1895,32 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
return err;
}
-static u16 rtnl_calcit(struct sk_buff *skb)
+static u16 rtnl_calcit(struct sk_buff *skb, struct nlmsghdr *nlh)
{
+ struct net *net = sock_net(skb->sk);
+ struct net_device *dev;
+ struct nlattr *tb[IFLA_MAX+1];
+ u32 ext_filter_mask = 0;
+ u16 min_ifinfo_dump_size = 0;
+
+ if (nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, IFLA_MAX,
+ ifla_policy) >= 0) {
+ if (tb[IFLA_EXT_MASK])
+ ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
+ }
+
+ if (!ext_filter_mask)
+ return NLMSG_GOODSIZE;
+ /*
+ * traverse the list of net devices and compute the minimum
+ * buffer size based upon the filter mask.
+ */
+ list_for_each_entry(dev, &net->dev_base_head, dev_list) {
+ min_ifinfo_dump_size = max_t(u16, min_ifinfo_dump_size,
+ if_nlmsg_size(dev,
+ ext_filter_mask));
+ }
+
return min_ifinfo_dump_size;
}
@@ -1913,13 +1955,11 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)
int err = -ENOBUFS;
size_t if_info_size;
- skb = nlmsg_new((if_info_size = if_nlmsg_size(dev)), GFP_KERNEL);
+ skb = nlmsg_new((if_info_size = if_nlmsg_size(dev, 0)), GFP_KERNEL);
if (skb == NULL)
goto errout;
- min_ifinfo_dump_size = max_t(u16, if_info_size, min_ifinfo_dump_size);
-
- err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0);
+ err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0, 0);
if (err < 0) {
/* -EMSGSIZE implies BUG in if_nlmsg_size() */
WARN_ON(err == -EMSGSIZE);
@@ -1977,12 +2017,17 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
return -EOPNOTSUPP;
calcit = rtnl_get_calcit(family, type);
if (calcit)
- min_dump_alloc = calcit(skb);
+ min_dump_alloc = calcit(skb, nlh);
__rtnl_unlock();
rtnl = net->rtnl;
- err = netlink_dump_start(rtnl, skb, nlh, dumpit,
- NULL, min_dump_alloc);
+ {
+ struct netlink_dump_control c = {
+ .dump = dumpit,
+ .min_dump_alloc = min_dump_alloc,
+ };
+ err = netlink_dump_start(rtnl, skb, nlh, &c);
+ }
rtnl_lock();
return err;
}
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index da0c97f2fab..6eb656acdfe 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -592,6 +592,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
new->rxhash = old->rxhash;
new->ooo_okay = old->ooo_okay;
new->l4_rxhash = old->l4_rxhash;
+ new->no_fcs = old->no_fcs;
#ifdef CONFIG_XFRM
new->sp = secpath_get(old->sp);
#endif
@@ -2906,7 +2907,7 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
nskb->prev = p;
nskb->data_len += p->len;
- nskb->truesize += p->len;
+ nskb->truesize += p->truesize;
nskb->len += p->len;
*head = nskb;
@@ -2916,6 +2917,7 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
p = nskb;
merge:
+ p->truesize += skb->truesize - len;
if (offset > headlen) {
unsigned int eat = offset - headlen;
diff --git a/net/core/sock.c b/net/core/sock.c
index 3e81fd2e3c7..216719cb5c7 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -793,6 +793,17 @@ set_rcvbuf:
sock_valbool_flag(sk, SOCK_WIFI_STATUS, valbool);
break;
+ case SO_PEEK_OFF:
+ if (sock->ops->set_peek_off)
+ sock->ops->set_peek_off(sk, val);
+ else
+ ret = -EOPNOTSUPP;
+ break;
+
+ case SO_NOFCS:
+ sock_valbool_flag(sk, SOCK_NOFCS, valbool);
+ break;
+
default:
ret = -ENOPROTOOPT;
break;
@@ -1018,6 +1029,15 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
v.val = !!sock_flag(sk, SOCK_WIFI_STATUS);
break;
+ case SO_PEEK_OFF:
+ if (!sock->ops->set_peek_off)
+ return -EOPNOTSUPP;
+
+ v.val = sk->sk_peek_off;
+ break;
+ case SO_NOFCS:
+ v.val = !!sock_flag(sk, SOCK_NOFCS);
+ break;
default:
return -ENOPROTOOPT;
}
@@ -1171,13 +1191,10 @@ EXPORT_SYMBOL(sock_update_classid);
void sock_update_netprioidx(struct sock *sk)
{
- struct cgroup_netprio_state *state;
if (in_interrupt())
return;
- rcu_read_lock();
- state = task_netprio_state(current);
- sk->sk_cgrp_prioidx = state ? state->prioidx : 0;
- rcu_read_unlock();
+
+ sk->sk_cgrp_prioidx = task_netprioidx(current);
}
EXPORT_SYMBOL_GPL(sock_update_netprioidx);
#endif
@@ -2095,6 +2112,7 @@ void sock_init_data(struct socket *sock, struct sock *sk)
sk->sk_sndmsg_page = NULL;
sk->sk_sndmsg_off = 0;
+ sk->sk_peek_off = -1;
sk->sk_peer_pid = NULL;
sk->sk_peer_cred = NULL;
diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c
index 56062730720..70bfaf2d196 100644
--- a/net/dccp/ccids/ccid3.c
+++ b/net/dccp/ccids/ccid3.c
@@ -98,6 +98,7 @@ static void ccid3_update_send_interval(struct ccid3_hc_tx_sock *hc)
{
hc->tx_t_ipi = scaled_div32(((u64)hc->tx_s) << 6, hc->tx_x);
+ DCCP_BUG_ON(hc->tx_t_ipi == 0);
ccid3_pr_debug("t_ipi=%u, s=%u, X=%u\n", hc->tx_t_ipi,
hc->tx_s, (unsigned)(hc->tx_x >> 6));
}
@@ -236,8 +237,6 @@ static void ccid3_hc_tx_no_feedback_timer(unsigned long data)
*
* Note that X_recv is scaled by 2^6 while X_calc is not
*/
- BUG_ON(hc->tx_p && !hc->tx_x_calc);
-
if (hc->tx_x_calc > (hc->tx_x_recv >> 5))
hc->tx_x_recv =
max(hc->tx_x_recv / 2,
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 1c67fe8ff90..caf6e1734b6 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -300,7 +300,8 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
*/
WARN_ON(req->sk);
- if (seq != dccp_rsk(req)->dreq_iss) {
+ if (!between48(seq, dccp_rsk(req)->dreq_iss,
+ dccp_rsk(req)->dreq_gss)) {
NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
goto out;
}
@@ -639,11 +640,12 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
*
* Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie
*
- * In fact we defer setting S.GSR, S.SWL, S.SWH to
- * dccp_create_openreq_child.
+ * Setting S.SWL/S.SWH to is deferred to dccp_create_openreq_child().
*/
dreq->dreq_isr = dcb->dccpd_seq;
+ dreq->dreq_gsr = dreq->dreq_isr;
dreq->dreq_iss = dccp_v4_init_sequence(skb);
+ dreq->dreq_gss = dreq->dreq_iss;
dreq->dreq_service = service;
if (dccp_v4_send_response(sk, req, NULL))
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index ce903f747e6..4dc588f520e 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -193,7 +193,8 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
*/
WARN_ON(req->sk != NULL);
- if (seq != dccp_rsk(req)->dreq_iss) {
+ if (!between48(seq, dccp_rsk(req)->dreq_iss,
+ dccp_rsk(req)->dreq_gss)) {
NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
goto out;
}
@@ -440,11 +441,12 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
*
* Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie
*
- * In fact we defer setting S.GSR, S.SWL, S.SWH to
- * dccp_create_openreq_child.
+ * Setting S.SWL/S.SWH to is deferred to dccp_create_openreq_child().
*/
dreq->dreq_isr = dcb->dccpd_seq;
+ dreq->dreq_gsr = dreq->dreq_isr;
dreq->dreq_iss = dccp_v6_init_sequence(skb);
+ dreq->dreq_gss = dreq->dreq_iss;
dreq->dreq_service = service;
if (dccp_v6_send_response(sk, req, NULL))
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index 5a7f90bbffa..ea850ce35d4 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -127,9 +127,11 @@ struct sock *dccp_create_openreq_child(struct sock *sk,
* activation below, as these windows all depend on the local
* and remote Sequence Window feature values (7.5.2).
*/
- newdp->dccps_gss = newdp->dccps_iss = dreq->dreq_iss;
+ newdp->dccps_iss = dreq->dreq_iss;
+ newdp->dccps_gss = dreq->dreq_gss;
newdp->dccps_gar = newdp->dccps_iss;
- newdp->dccps_gsr = newdp->dccps_isr = dreq->dreq_isr;
+ newdp->dccps_isr = dreq->dreq_isr;
+ newdp->dccps_gsr = dreq->dreq_gsr;
/*
* Activate features: initialise CCIDs, sequence windows etc.
@@ -164,9 +166,9 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
/* Check for retransmitted REQUEST */
if (dccp_hdr(skb)->dccph_type == DCCP_PKT_REQUEST) {
- if (after48(DCCP_SKB_CB(skb)->dccpd_seq, dreq->dreq_isr)) {
+ if (after48(DCCP_SKB_CB(skb)->dccpd_seq, dreq->dreq_gsr)) {
dccp_pr_debug("Retransmitted REQUEST\n");
- dreq->dreq_isr = DCCP_SKB_CB(skb)->dccpd_seq;
+ dreq->dreq_gsr = DCCP_SKB_CB(skb)->dccpd_seq;
/*
* Send another RESPONSE packet
* To protect against Request floods, increment retrans
@@ -186,12 +188,14 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
goto drop;
/* Invalid ACK */
- if (DCCP_SKB_CB(skb)->dccpd_ack_seq != dreq->dreq_iss) {
+ if (!between48(DCCP_SKB_CB(skb)->dccpd_ack_seq,
+ dreq->dreq_iss, dreq->dreq_gss)) {
dccp_pr_debug("Invalid ACK number: ack_seq=%llu, "
- "dreq_iss=%llu\n",
+ "dreq_iss=%llu, dreq_gss=%llu\n",
(unsigned long long)
DCCP_SKB_CB(skb)->dccpd_ack_seq,
- (unsigned long long) dreq->dreq_iss);
+ (unsigned long long) dreq->dreq_iss,
+ (unsigned long long) dreq->dreq_gss);
goto drop;
}
diff --git a/net/dccp/output.c b/net/dccp/output.c
index dede3edb884..78736730879 100644
--- a/net/dccp/output.c
+++ b/net/dccp/output.c
@@ -408,10 +408,10 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
skb_dst_set(skb, dst_clone(dst));
dreq = dccp_rsk(req);
- if (inet_rsk(req)->acked) /* increase ISS upon retransmission */
- dccp_inc_seqno(&dreq->dreq_iss);
+ if (inet_rsk(req)->acked) /* increase GSS upon retransmission */
+ dccp_inc_seqno(&dreq->dreq_gss);
DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_RESPONSE;
- DCCP_SKB_CB(skb)->dccpd_seq = dreq->dreq_iss;
+ DCCP_SKB_CB(skb)->dccpd_seq = dreq->dreq_gss;
/* Resolve feature dependencies resulting from choice of CCID */
if (dccp_feat_server_ccid_dependencies(dreq))
@@ -429,8 +429,8 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
DCCP_SKB_CB(skb)->dccpd_opt_len) / 4;
dh->dccph_type = DCCP_PKT_RESPONSE;
dh->dccph_x = 1;
- dccp_hdr_set_seq(dh, dreq->dreq_iss);
- dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dreq->dreq_isr);
+ dccp_hdr_set_seq(dh, dreq->dreq_gss);
+ dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dreq->dreq_gsr);
dccp_hdr_response(skb)->dccph_resp_service = dreq->dreq_service;
dccp_csum_outgoing(skb);
diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c
index befe426491b..ee7013f24fc 100644
--- a/net/decnet/dn_neigh.c
+++ b/net/decnet/dn_neigh.c
@@ -205,17 +205,23 @@ static int dn_neigh_output_packet(struct sk_buff *skb)
struct neighbour *neigh = dst_get_neighbour_noref(dst);
struct net_device *dev = neigh->dev;
char mac_addr[ETH_ALEN];
+ unsigned int seq;
+ int err;
dn_dn2eth(mac_addr, rt->rt_local_src);
- if (dev_hard_header(skb, dev, ntohs(skb->protocol), neigh->ha,
- mac_addr, skb->len) >= 0)
- return dev_queue_xmit(skb);
-
- if (net_ratelimit())
- printk(KERN_DEBUG "dn_neigh_output_packet: oops, can't send packet\n");
-
- kfree_skb(skb);
- return -EINVAL;
+ do {
+ seq = read_seqbegin(&neigh->ha_lock);
+ err = dev_hard_header(skb, dev, ntohs(skb->protocol),
+ neigh->ha, mac_addr, skb->len);
+ } while (read_seqretry(&neigh->ha_lock, seq));
+
+ if (err >= 0)
+ err = dev_queue_xmit(skb);
+ else {
+ kfree_skb(skb);
+ err = -EINVAL;
+ }
+ return err;
}
static int dn_long_output(struct neighbour *neigh, struct sk_buff *skb)
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
index f31ce72dca6..80a3de4906d 100644
--- a/net/decnet/dn_route.c
+++ b/net/decnet/dn_route.c
@@ -724,11 +724,10 @@ static int dn_output(struct sk_buff *skb)
struct dn_route *rt = (struct dn_route *)dst;
struct net_device *dev = dst->dev;
struct dn_skb_cb *cb = DN_SKB_CB(skb);
- struct neighbour *neigh;
int err = -EINVAL;
- if ((neigh = dst_get_neighbour_noref(dst)) == NULL)
+ if (dst_get_neighbour_noref(dst) == NULL)
goto error;
skb->dev = dev;
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index a2468363978..a93af86b847 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -288,6 +288,8 @@ int eth_mac_addr(struct net_device *dev, void *p)
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+ /* if device marked as NET_ADDR_RANDOM, reset it */
+ dev->addr_assign_type &= ~NET_ADDR_RANDOM;
return 0;
}
EXPORT_SYMBOL(eth_mac_addr);
diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c
index e4ecc1eef98..36851588536 100644
--- a/net/ieee802154/6lowpan.c
+++ b/net/ieee802154/6lowpan.c
@@ -55,6 +55,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
#include <net/af_ieee802154.h>
#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
@@ -924,19 +925,6 @@ drop:
return -EINVAL;
}
-static int lowpan_set_address(struct net_device *dev, void *p)
-{
- struct sockaddr *sa = p;
-
- if (netif_running(dev))
- return -EBUSY;
-
- /* TODO: validate addr */
- memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
-
- return 0;
-}
-
static int lowpan_get_mac_header_length(struct sk_buff *skb)
{
/*
@@ -1062,7 +1050,7 @@ static struct header_ops lowpan_header_ops = {
static const struct net_device_ops lowpan_netdev_ops = {
.ndo_start_xmit = lowpan_xmit,
- .ndo_set_mac_address = lowpan_set_address,
+ .ndo_set_mac_address = eth_mac_addr,
};
static void lowpan_setup(struct net_device *dev)
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index aa2a2c79776..d183262943d 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -409,7 +409,7 @@ config INET_TCP_DIAG
config INET_UDP_DIAG
tristate "UDP: socket monitoring interface"
- depends on INET_DIAG
+ depends on INET_DIAG && (IPV6 || IPV6=n)
default n
---help---
Support for UDP socket monitoring interface used by the ss tool.
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index f7b5670744f..fdf49fd44bb 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -65,6 +65,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) "IPv4: " fmt
+
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/types.h>
@@ -381,6 +383,7 @@ lookup_protocol:
inet->mc_all = 1;
inet->mc_index = 0;
inet->mc_list = NULL;
+ inet->rcv_tos = 0;
sk_refcnt_debug_inc(sk);
@@ -1084,13 +1087,11 @@ out:
return;
out_permanent:
- printk(KERN_ERR "Attempt to override permanent protocol %d.\n",
- protocol);
+ pr_err("Attempt to override permanent protocol %d\n", protocol);
goto out;
out_illegal:
- printk(KERN_ERR
- "Ignoring attempt to register invalid socket type %d.\n",
+ pr_err("Ignoring attempt to register invalid socket type %d\n",
p->type);
goto out;
}
@@ -1099,8 +1100,7 @@ EXPORT_SYMBOL(inet_register_protosw);
void inet_unregister_protosw(struct inet_protosw *p)
{
if (INET_PROTOSW_PERMANENT & p->flags) {
- printk(KERN_ERR
- "Attempt to unregister permanent protocol %d.\n",
+ pr_err("Attempt to unregister permanent protocol %d\n",
p->protocol);
} else {
spin_lock_bh(&inetsw_lock);
@@ -1149,8 +1149,8 @@ static int inet_sk_reselect_saddr(struct sock *sk)
return 0;
if (sysctl_ip_dynaddr > 1) {
- printk(KERN_INFO "%s(): shifting inet->saddr from %pI4 to %pI4\n",
- __func__, &old_saddr, &new_saddr);
+ pr_info("%s(): shifting inet->saddr from %pI4 to %pI4\n",
+ __func__, &old_saddr, &new_saddr);
}
inet->inet_saddr = inet->inet_rcv_saddr = new_saddr;
@@ -1679,14 +1679,14 @@ static int __init inet_init(void)
*/
if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
- printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n");
+ pr_crit("%s: Cannot add ICMP protocol\n", __func__);
if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
- printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");
+ pr_crit("%s: Cannot add UDP protocol\n", __func__);
if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
- printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");
+ pr_crit("%s: Cannot add TCP protocol\n", __func__);
#ifdef CONFIG_IP_MULTICAST
if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
- printk(KERN_CRIT "inet_init: Cannot add IGMP protocol\n");
+ pr_crit("%s: Cannot add IGMP protocol\n", __func__);
#endif
/* Register the socket-side information for inet_create. */
@@ -1733,14 +1733,14 @@ static int __init inet_init(void)
*/
#if defined(CONFIG_IP_MROUTE)
if (ip_mr_init())
- printk(KERN_CRIT "inet_init: Cannot init ipv4 mroute\n");
+ pr_crit("%s: Cannot init ipv4 mroute\n", __func__);
#endif
/*
* Initialise per-cpu ipv4 mibs
*/
if (init_ipv4_mibs())
- printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n");
+ pr_crit("%s: Cannot init ipv4 mibs\n", __func__);
ipv4_proc_init();
diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c
index 36d14406261..fd508b52601 100644
--- a/net/ipv4/ah4.c
+++ b/net/ipv4/ah4.c
@@ -1,3 +1,5 @@
+#define pr_fmt(fmt) "IPsec: " fmt
+
#include <crypto/hash.h>
#include <linux/err.h>
#include <linux/module.h>
@@ -445,9 +447,10 @@ static int ah_init_state(struct xfrm_state *x)
if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
crypto_ahash_digestsize(ahash)) {
- printk(KERN_INFO "AH: %s digestsize %u != %hu\n",
- x->aalg->alg_name, crypto_ahash_digestsize(ahash),
- aalg_desc->uinfo.auth.icv_fullbits/8);
+ pr_info("%s: %s digestsize %u != %hu\n",
+ __func__, x->aalg->alg_name,
+ crypto_ahash_digestsize(ahash),
+ aalg_desc->uinfo.auth.icv_fullbits / 8);
goto error;
}
@@ -510,11 +513,11 @@ static const struct net_protocol ah4_protocol = {
static int __init ah4_init(void)
{
if (xfrm_register_type(&ah_type, AF_INET) < 0) {
- printk(KERN_INFO "ip ah init: can't add xfrm type\n");
+ pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN;
}
if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) < 0) {
- printk(KERN_INFO "ip ah init: can't add protocol\n");
+ pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&ah_type, AF_INET);
return -EAGAIN;
}
@@ -524,9 +527,9 @@ static int __init ah4_init(void)
static void __exit ah4_fini(void)
{
if (inet_del_protocol(&ah4_protocol, IPPROTO_AH) < 0)
- printk(KERN_INFO "ip ah close: can't remove protocol\n");
+ pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&ah_type, AF_INET) < 0)
- printk(KERN_INFO "ip ah close: can't remove xfrm type\n");
+ pr_info("%s: can't remove xfrm type\n", __func__);
}
module_init(ah4_init);
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 59402be133f..63e49890ad3 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -863,7 +863,8 @@ static int arp_process(struct sk_buff *skb)
if (addr_type == RTN_UNICAST &&
(arp_fwd_proxy(in_dev, dev, rt) ||
arp_fwd_pvlan(in_dev, dev, rt, sip, tip) ||
- pneigh_lookup(&arp_tbl, net, &tip, dev, 0))) {
+ (rt->dst.dev != dev &&
+ pneigh_lookup(&arp_tbl, net, &tip, dev, 0)))) {
n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
if (n)
neigh_release(n);
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index a5b413416da..89a47b35905 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -1,3 +1,5 @@
+#define pr_fmt(fmt) "IPsec: " fmt
+
#include <crypto/aead.h>
#include <crypto/authenc.h>
#include <linux/err.h>
@@ -706,11 +708,11 @@ static const struct net_protocol esp4_protocol = {
static int __init esp4_init(void)
{
if (xfrm_register_type(&esp_type, AF_INET) < 0) {
- printk(KERN_INFO "ip esp init: can't add xfrm type\n");
+ pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN;
}
if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) {
- printk(KERN_INFO "ip esp init: can't add protocol\n");
+ pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&esp_type, AF_INET);
return -EAGAIN;
}
@@ -720,9 +722,9 @@ static int __init esp4_init(void)
static void __exit esp4_fini(void)
{
if (inet_del_protocol(&esp4_protocol, IPPROTO_ESP) < 0)
- printk(KERN_INFO "ip esp close: can't remove protocol\n");
+ pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&esp_type, AF_INET) < 0)
- printk(KERN_INFO "ip esp close: can't remove xfrm type\n");
+ pr_info("%s: can't remove xfrm type\n", __func__);
}
module_init(esp4_init);
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 92fc5f69f5d..76e72bacc21 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -695,7 +695,7 @@ void fib_add_ifaddr(struct in_ifaddr *ifa)
if (ifa->ifa_flags & IFA_F_SECONDARY) {
prim = inet_ifa_byprefix(in_dev, prefix, mask);
if (prim == NULL) {
- printk(KERN_WARNING "fib_add_ifaddr: bug: prim == NULL\n");
+ pr_warn("%s: bug: prim == NULL\n", __func__);
return;
}
}
@@ -749,11 +749,11 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
if (ifa->ifa_flags & IFA_F_SECONDARY) {
prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask);
if (prim == NULL) {
- printk(KERN_WARNING "fib_del_ifaddr: bug: prim == NULL\n");
+ pr_warn("%s: bug: prim == NULL\n", __func__);
return;
}
if (iprim && iprim != prim) {
- printk(KERN_WARNING "fib_del_ifaddr: bug: iprim != prim\n");
+ pr_warn("%s: bug: iprim != prim\n", __func__);
return;
}
} else if (!ipv4_is_zeronet(any) &&
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 80106d89d54..a8c5c1d6715 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -154,7 +154,7 @@ static void free_fib_info_rcu(struct rcu_head *head)
void free_fib_info(struct fib_info *fi)
{
if (fi->fib_dead == 0) {
- pr_warning("Freeing alive fib_info %p\n", fi);
+ pr_warn("Freeing alive fib_info %p\n", fi);
return;
}
change_nexthops(fi) {
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 2b555a5521e..da9b9cb2282 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1170,9 +1170,8 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen)
}
if (tp && tp->pos + tp->bits > 32)
- pr_warning("fib_trie"
- " tp=%p pos=%d, bits=%d, key=%0x plen=%d\n",
- tp, tp->pos, tp->bits, key, plen);
+ pr_warn("fib_trie tp=%p pos=%d, bits=%d, key=%0x plen=%d\n",
+ tp, tp->pos, tp->bits, key, plen);
/* Rebalance the trie */
diff --git a/net/ipv4/gre.c b/net/ipv4/gre.c
index 8cb1ebb7cd7..42a491055c7 100644
--- a/net/ipv4/gre.c
+++ b/net/ipv4/gre.c
@@ -10,6 +10,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
@@ -118,10 +120,10 @@ static const struct net_protocol net_gre_protocol = {
static int __init gre_init(void)
{
- pr_info("GRE over IPv4 demultiplexor driver");
+ pr_info("GRE over IPv4 demultiplexor driver\n");
if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
- pr_err("gre: can't add protocol\n");
+ pr_err("can't add protocol\n");
return -EAGAIN;
}
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index ab188ae12fd..9664d353ccd 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -62,6 +62,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/types.h>
#include <linux/jiffies.h>
@@ -670,7 +672,7 @@ static void icmp_unreach(struct sk_buff *skb)
break;
case ICMP_FRAG_NEEDED:
if (ipv4_config.no_pmtu_disc) {
- LIMIT_NETDEBUG(KERN_INFO "ICMP: %pI4: fragmentation needed and DF set.\n",
+ LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: fragmentation needed and DF set\n"),
&iph->daddr);
} else {
info = ip_rt_frag_needed(net, iph,
@@ -681,7 +683,7 @@ static void icmp_unreach(struct sk_buff *skb)
}
break;
case ICMP_SR_FAILED:
- LIMIT_NETDEBUG(KERN_INFO "ICMP: %pI4: Source Route Failed.\n",
+ LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: Source Route Failed\n"),
&iph->daddr);
break;
default:
@@ -713,13 +715,10 @@ static void icmp_unreach(struct sk_buff *skb)
if (!net->ipv4.sysctl_icmp_ignore_bogus_error_responses &&
inet_addr_type(net, iph->daddr) == RTN_BROADCAST) {
if (net_ratelimit())
- printk(KERN_WARNING "%pI4 sent an invalid ICMP "
- "type %u, code %u "
- "error to a broadcast: %pI4 on %s\n",
- &ip_hdr(skb)->saddr,
- icmph->type, icmph->code,
- &iph->daddr,
- skb->dev->name);
+ pr_warn("%pI4 sent an invalid ICMP type %u, code %u error to a broadcast: %pI4 on %s\n",
+ &ip_hdr(skb)->saddr,
+ icmph->type, icmph->code,
+ &iph->daddr, skb->dev->name);
goto out;
}
@@ -946,8 +945,8 @@ static void icmp_address_reply(struct sk_buff *skb)
break;
}
if (!ifa && net_ratelimit()) {
- printk(KERN_INFO "Wrong address mask %pI4 from %s/%pI4\n",
- mp, dev->name, &ip_hdr(skb)->saddr);
+ pr_info("Wrong address mask %pI4 from %s/%pI4\n",
+ mp, dev->name, &ip_hdr(skb)->saddr);
}
}
}
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index fcf281819cd..8d25a1c557e 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -960,9 +960,12 @@ static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
inet_diag_bc_audit(nla_data(attr), nla_len(attr)))
return -EINVAL;
}
-
- return netlink_dump_start(sock_diag_nlsk, skb, nlh,
- inet_diag_dump_compat, NULL, 0);
+ {
+ struct netlink_dump_control c = {
+ .dump = inet_diag_dump_compat,
+ };
+ return netlink_dump_start(sock_diag_nlsk, skb, nlh, &c);
+ }
}
return inet_diag_get_exact_compat(skb, nlh);
@@ -985,9 +988,12 @@ static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
inet_diag_bc_audit(nla_data(attr), nla_len(attr)))
return -EINVAL;
}
-
- return netlink_dump_start(sock_diag_nlsk, skb, h,
- inet_diag_dump, NULL, 0);
+ {
+ struct netlink_dump_control c = {
+ .dump = inet_diag_dump,
+ };
+ return netlink_dump_start(sock_diag_nlsk, skb, h, &c);
+ }
}
return inet_diag_get_exact(skb, h, (struct inet_diag_req_v2 *)NLMSG_DATA(h));
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index bf4a9c4808e..d4d61b694fa 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/net.h>
+#include <linux/workqueue.h>
#include <net/ip.h>
#include <net/inetpeer.h>
#include <net/secure_seq.h>
@@ -66,6 +67,11 @@
static struct kmem_cache *peer_cachep __read_mostly;
+static LIST_HEAD(gc_list);
+static const int gc_delay = 60 * HZ;
+static struct delayed_work gc_work;
+static DEFINE_SPINLOCK(gc_lock);
+
#define node_height(x) x->avl_height
#define peer_avl_empty ((struct inet_peer *)&peer_fake_node)
@@ -102,6 +108,50 @@ int inet_peer_threshold __read_mostly = 65536 + 128; /* start to throw entries m
int inet_peer_minttl __read_mostly = 120 * HZ; /* TTL under high load: 120 sec */
int inet_peer_maxttl __read_mostly = 10 * 60 * HZ; /* usual time to live: 10 min */
+static void inetpeer_gc_worker(struct work_struct *work)
+{
+ struct inet_peer *p, *n;
+ LIST_HEAD(list);
+
+ spin_lock_bh(&gc_lock);
+ list_replace_init(&gc_list, &list);
+ spin_unlock_bh(&gc_lock);
+
+ if (list_empty(&list))
+ return;
+
+ list_for_each_entry_safe(p, n, &list, gc_list) {
+
+ if(need_resched())
+ cond_resched();
+
+ if (p->avl_left != peer_avl_empty) {
+ list_add_tail(&p->avl_left->gc_list, &list);
+ p->avl_left = peer_avl_empty;
+ }
+
+ if (p->avl_right != peer_avl_empty) {
+ list_add_tail(&p->avl_right->gc_list, &list);
+ p->avl_right = peer_avl_empty;
+ }
+
+ n = list_entry(p->gc_list.next, struct inet_peer, gc_list);
+
+ if (!atomic_read(&p->refcnt)) {
+ list_del(&p->gc_list);
+ kmem_cache_free(peer_cachep, p);
+ }
+ }
+
+ if (list_empty(&list))
+ return;
+
+ spin_lock_bh(&gc_lock);
+ list_splice(&list, &gc_list);
+ spin_unlock_bh(&gc_lock);
+
+ schedule_delayed_work(&gc_work, gc_delay);
+}
/* Called from ip_output.c:ip_init */
void __init inet_initpeers(void)
@@ -126,6 +176,7 @@ void __init inet_initpeers(void)
0, SLAB_HWCACHE_ALIGN | SLAB_PANIC,
NULL);
+ INIT_DELAYED_WORK_DEFERRABLE(&gc_work, inetpeer_gc_worker);
}
static int addr_compare(const struct inetpeer_addr *a,
@@ -447,9 +498,8 @@ relookup:
p->rate_last = 0;
p->pmtu_expires = 0;
p->pmtu_orig = 0;
- p->redirect_genid = 0;
memset(&p->redirect_learned, 0, sizeof(p->redirect_learned));
-
+ INIT_LIST_HEAD(&p->gc_list);
/* Link the node. */
link_to_pool(p, base);
@@ -509,3 +559,30 @@ bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout)
return rc;
}
EXPORT_SYMBOL(inet_peer_xrlim_allow);
+
+void inetpeer_invalidate_tree(int family)
+{
+ struct inet_peer *old, *new, *prev;
+ struct inet_peer_base *base = family_to_base(family);
+
+ write_seqlock_bh(&base->lock);
+
+ old = base->root;
+ if (old == peer_avl_empty_rcu)
+ goto out;
+
+ new = peer_avl_empty_rcu;
+
+ prev = cmpxchg(&base->root, old, new);
+ if (prev == old) {
+ base->total = 0;
+ spin_lock(&gc_lock);
+ list_add_tail(&prev->gc_list, &gc_list);
+ spin_unlock(&gc_lock);
+ schedule_delayed_work(&gc_work, gc_delay);
+ }
+
+out:
+ write_sequnlock_bh(&base->lock);
+}
+EXPORT_SYMBOL(inetpeer_invalidate_tree);
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 1f23a57aa9e..3727e234c88 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -20,6 +20,8 @@
* Patrick McHardy : LRU queue of frag heads for evictor.
*/
+#define pr_fmt(fmt) "IPv4: " fmt
+
#include <linux/compiler.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -299,7 +301,7 @@ static inline struct ipq *ip_find(struct net *net, struct iphdr *iph, u32 user)
return container_of(q, struct ipq, q);
out_nomem:
- LIMIT_NETDEBUG(KERN_ERR "ip_frag_create: no memory left !\n");
+ LIMIT_NETDEBUG(KERN_ERR pr_fmt("ip_frag_create: no memory left !\n"));
return NULL;
}
@@ -637,14 +639,13 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
return 0;
out_nomem:
- LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing "
- "queue %p\n", qp);
+ LIMIT_NETDEBUG(KERN_ERR pr_fmt("queue_glue: no memory for gluing queue %p\n"),
+ qp);
err = -ENOMEM;
goto out_fail;
out_oversize:
if (net_ratelimit())
- printk(KERN_INFO "Oversized IP packet from %pI4.\n",
- &qp->saddr);
+ pr_info("Oversized IP packet from %pI4\n", &qp->saddr);
out_fail:
IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS);
return err;
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 6b3ca5ba445..b57532d4742 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -10,6 +10,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/capability.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -65,7 +67,7 @@
it is infeasible task. The most general solutions would be
to keep skb->encapsulation counter (sort of local ttl),
and silently drop packet when it expires. It is a good
- solution, but it supposes maintaing new variable in ALL
+ solution, but it supposes maintaining new variable in ALL
skb, even if no tunneling is used.
Current solution: xmit_recursion breaks dead loops. This is a percpu
@@ -91,14 +93,14 @@
One of them is to parse packet trying to detect inner encapsulation
made by our node. It is difficult or even impossible, especially,
- taking into account fragmentation. TO be short, tt is not solution at all.
+ taking into account fragmentation. TO be short, ttl is not solution at all.
Current solution: The solution was UNEXPECTEDLY SIMPLE.
We force DF flag on tunnels with preconfigured hop limit,
that is ALL. :-) Well, it does not remove the problem completely,
but exponential growth of network traffic is changed to linear
(branches, that exceed pmtu are pruned) and tunnel mtu
- fastly degrades to value <68, where looping stops.
+ rapidly degrades to value <68, where looping stops.
Yes, it is not good if there exists a router in the loop,
which does not force DF, even when encapsulating packets have DF set.
But it is not our problem! Nobody could accuse us, we made
@@ -457,8 +459,8 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
GRE tunnels with enabled checksum. Tell them "thank you".
Well, I wonder, rfc1812 was written by Cisco employee,
- what the hell these idiots break standrads established
- by themself???
+ what the hell these idiots break standards established
+ by themselves???
*/
const struct iphdr *iph = (const struct iphdr *)skb->data;
@@ -730,15 +732,16 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
if (skb->protocol == htons(ETH_P_IP)) {
rt = skb_rtable(skb);
- if ((dst = rt->rt_gateway) == 0)
- goto tx_error_icmp;
+ dst = rt->rt_gateway;
}
#if IS_ENABLED(CONFIG_IPV6)
else if (skb->protocol == htons(ETH_P_IPV6)) {
- struct neighbour *neigh = dst_get_neighbour_noref(skb_dst(skb));
const struct in6_addr *addr6;
+ struct neighbour *neigh;
+ bool do_tx_error_icmp;
int addr_type;
+ neigh = dst_neigh_lookup(skb_dst(skb), &ipv6_hdr(skb)->daddr);
if (neigh == NULL)
goto tx_error;
@@ -751,9 +754,14 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
}
if ((addr_type & IPV6_ADDR_COMPATv4) == 0)
+ do_tx_error_icmp = true;
+ else {
+ do_tx_error_icmp = false;
+ dst = addr6->s6_addr32[3];
+ }
+ neigh_release(neigh);
+ if (do_tx_error_icmp)
goto tx_error_icmp;
-
- dst = addr6->s6_addr32[3];
}
#endif
else
@@ -914,9 +922,10 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
__IPTUNNEL_XMIT(tstats, &dev->stats);
return NETDEV_TX_OK;
+#if IS_ENABLED(CONFIG_IPV6)
tx_error_icmp:
dst_link_failure(skb);
-
+#endif
tx_error:
dev->stats.tx_errors++;
dev_kfree_skb(skb);
@@ -1529,7 +1538,7 @@ static int ipgre_newlink(struct net *src_net, struct net_device *dev, struct nla
return -EEXIST;
if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS])
- random_ether_addr(dev->dev_addr);
+ eth_hw_addr_random(dev);
mtu = ipgre_tunnel_bind_dev(dev);
if (!tb[IFLA_MTU])
@@ -1709,7 +1718,7 @@ static int __init ipgre_init(void)
{
int err;
- printk(KERN_INFO "GRE over IPv4 tunneling driver\n");
+ pr_info("GRE over IPv4 tunneling driver\n");
err = register_pernet_device(&ipgre_net_ops);
if (err < 0)
@@ -1717,7 +1726,7 @@ static int __init ipgre_init(void)
err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO);
if (err < 0) {
- printk(KERN_INFO "ipgre init: can't add protocol\n");
+ pr_info("%s: can't add protocol\n", __func__);
goto add_proto_failed;
}
@@ -1746,7 +1755,7 @@ static void __exit ipgre_fini(void)
rtnl_link_unregister(&ipgre_tap_ops);
rtnl_link_unregister(&ipgre_link_ops);
if (gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO) < 0)
- printk(KERN_INFO "ipgre close: can't remove protocol\n");
+ pr_info("%s: can't remove protocol\n", __func__);
unregister_pernet_device(&ipgre_net_ops);
}
diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
index 073a9b01c40..f3f1108940f 100644
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -113,6 +113,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) "IPv4: " fmt
+
#include <asm/system.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -148,7 +150,7 @@
/*
* Process Router Attention IP option (RFC 2113)
*/
-int ip_call_ra_chain(struct sk_buff *skb)
+bool ip_call_ra_chain(struct sk_buff *skb)
{
struct ip_ra_chain *ra;
u8 protocol = ip_hdr(skb)->protocol;
@@ -167,7 +169,7 @@ int ip_call_ra_chain(struct sk_buff *skb)
net_eq(sock_net(sk), dev_net(dev))) {
if (ip_is_fragment(ip_hdr(skb))) {
if (ip_defrag(skb, IP_DEFRAG_CALL_RA_CHAIN))
- return 1;
+ return true;
}
if (last) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
@@ -180,9 +182,9 @@ int ip_call_ra_chain(struct sk_buff *skb)
if (last) {
raw_rcv(last, skb);
- return 1;
+ return true;
}
- return 0;
+ return false;
}
static int ip_local_deliver_finish(struct sk_buff *skb)
@@ -265,7 +267,7 @@ int ip_local_deliver(struct sk_buff *skb)
ip_local_deliver_finish);
}
-static inline int ip_rcv_options(struct sk_buff *skb)
+static inline bool ip_rcv_options(struct sk_buff *skb)
{
struct ip_options *opt;
const struct iphdr *iph;
@@ -299,8 +301,8 @@ static inline int ip_rcv_options(struct sk_buff *skb)
if (!IN_DEV_SOURCE_ROUTE(in_dev)) {
if (IN_DEV_LOG_MARTIANS(in_dev) &&
net_ratelimit())
- printk(KERN_INFO "source route option %pI4 -> %pI4\n",
- &iph->saddr, &iph->daddr);
+ pr_info("source route option %pI4 -> %pI4\n",
+ &iph->saddr, &iph->daddr);
goto drop;
}
}
@@ -309,9 +311,9 @@ static inline int ip_rcv_options(struct sk_buff *skb)
goto drop;
}
- return 0;
+ return false;
drop:
- return -1;
+ return true;
}
static int ip_rcv_finish(struct sk_buff *skb)
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c
index 1e60f767907..a0d0d9d9b87 100644
--- a/net/ipv4/ip_options.c
+++ b/net/ipv4/ip_options.c
@@ -9,6 +9,8 @@
*
*/
+#define pr_fmt(fmt) "IPv4: " fmt
+
#include <linux/capability.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -573,11 +575,11 @@ void ip_forward_options(struct sk_buff *skb)
}
if (srrptr + 3 <= srrspace) {
opt->is_changed = 1;
- ip_rt_get_source(&optptr[srrptr-1], skb, rt);
ip_hdr(skb)->daddr = opt->nexthop;
+ ip_rt_get_source(&optptr[srrptr-1], skb, rt);
optptr[2] = srrptr+4;
} else if (net_ratelimit())
- printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n");
+ pr_crit("%s(): Argh! Destination lost!\n", __func__);
if (opt->ts_needaddr) {
optptr = raw + opt->ts;
ip_rt_get_source(&optptr[optptr[2]-9], skb, rt);
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 8aa87c19fa0..ca50d9f9f8c 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -469,6 +469,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
(1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) |
(1<<IP_PASSSEC) | (1<<IP_TRANSPARENT) |
(1<<IP_MINTTL) | (1<<IP_NODEFRAG))) ||
+ optname == IP_UNICAST_IF ||
optname == IP_MULTICAST_TTL ||
optname == IP_MULTICAST_ALL ||
optname == IP_MULTICAST_LOOP ||
@@ -628,6 +629,35 @@ static int do_ip_setsockopt(struct sock *sk, int level,
goto e_inval;
inet->mc_loop = !!val;
break;
+ case IP_UNICAST_IF:
+ {
+ struct net_device *dev = NULL;
+ int ifindex;
+
+ if (optlen != sizeof(int))
+ goto e_inval;
+
+ ifindex = (__force int)ntohl((__force __be32)val);
+ if (ifindex == 0) {
+ inet->uc_index = 0;
+ err = 0;
+ break;
+ }
+
+ dev = dev_get_by_index(sock_net(sk), ifindex);
+ err = -EADDRNOTAVAIL;
+ if (!dev)
+ break;
+ dev_put(dev);
+
+ err = -EINVAL;
+ if (sk->sk_bound_dev_if)
+ break;
+
+ inet->uc_index = ifindex;
+ err = 0;
+ break;
+ }
case IP_MULTICAST_IF:
{
struct ip_mreqn mreq;
@@ -1178,6 +1208,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
case IP_MULTICAST_LOOP:
val = inet->mc_loop;
break;
+ case IP_UNICAST_IF:
+ val = (__force int)htonl((__u32) inet->uc_index);
+ break;
case IP_MULTICAST_IF:
{
struct in_addr addr;
@@ -1256,6 +1289,10 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
int hlim = inet->mc_ttl;
put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim);
}
+ if (inet->cmsg_flags & IP_CMSG_TOS) {
+ int tos = inet->rcv_tos;
+ put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos);
+ }
len -= msg.msg_controllen;
return put_user(len, optlen);
}
diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
index c857f6f49b0..63b64c45a82 100644
--- a/net/ipv4/ipcomp.c
+++ b/net/ipv4/ipcomp.c
@@ -156,11 +156,11 @@ static const struct net_protocol ipcomp4_protocol = {
static int __init ipcomp4_init(void)
{
if (xfrm_register_type(&ipcomp_type, AF_INET) < 0) {
- printk(KERN_INFO "ipcomp init: can't add xfrm type\n");
+ pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN;
}
if (inet_add_protocol(&ipcomp4_protocol, IPPROTO_COMP) < 0) {
- printk(KERN_INFO "ipcomp init: can't add protocol\n");
+ pr_info("%s: can't add protocol\n", __func__);
xfrm_unregister_type(&ipcomp_type, AF_INET);
return -EAGAIN;
}
@@ -170,9 +170,9 @@ static int __init ipcomp4_init(void)
static void __exit ipcomp4_fini(void)
{
if (inet_del_protocol(&ipcomp4_protocol, IPPROTO_COMP) < 0)
- printk(KERN_INFO "ip ipcomp close: can't remove protocol\n");
+ pr_info("%s: can't remove protocol\n", __func__);
if (xfrm_unregister_type(&ipcomp_type, AF_INET) < 0)
- printk(KERN_INFO "ip ipcomp close: can't remove xfrm type\n");
+ pr_info("%s: can't remove xfrm type\n", __func__);
}
module_init(ipcomp4_init);
diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c
index 6e412a60a91..92ac7e7363a 100644
--- a/net/ipv4/ipconfig.c
+++ b/net/ipv4/ipconfig.c
@@ -214,7 +214,7 @@ static int __init ic_open_devs(void)
if (!(dev->flags & IFF_LOOPBACK))
continue;
if (dev_change_flags(dev, dev->flags | IFF_UP) < 0)
- printk(KERN_ERR "IP-Config: Failed to open %s\n", dev->name);
+ pr_err("IP-Config: Failed to open %s\n", dev->name);
}
for_each_netdev(&init_net, dev) {
@@ -223,7 +223,8 @@ static int __init ic_open_devs(void)
if (dev->mtu >= 364)
able |= IC_BOOTP;
else
- printk(KERN_WARNING "DHCP/BOOTP: Ignoring device %s, MTU %d too small", dev->name, dev->mtu);
+ pr_warn("DHCP/BOOTP: Ignoring device %s, MTU %d too small",
+ dev->name, dev->mtu);
if (!(dev->flags & IFF_NOARP))
able |= IC_RARP;
able &= ic_proto_enabled;
@@ -231,7 +232,8 @@ static int __init ic_open_devs(void)
continue;
oflags = dev->flags;
if (dev_change_flags(dev, oflags | IFF_UP) < 0) {
- printk(KERN_ERR "IP-Config: Failed to open %s\n", dev->name);
+ pr_err("IP-Config: Failed to open %s\n",
+ dev->name);
continue;
}
if (!(d = kmalloc(sizeof(struct ic_device), GFP_KERNEL))) {
@@ -273,9 +275,10 @@ have_carrier:
if (!ic_first_dev) {
if (user_dev_name[0])
- printk(KERN_ERR "IP-Config: Device `%s' not found.\n", user_dev_name);
+ pr_err("IP-Config: Device `%s' not found\n",
+ user_dev_name);
else
- printk(KERN_ERR "IP-Config: No network devices available.\n");
+ pr_err("IP-Config: No network devices available\n");
return -ENODEV;
}
return 0;
@@ -359,17 +362,20 @@ static int __init ic_setup_if(void)
strcpy(ir.ifr_ifrn.ifrn_name, ic_dev->name);
set_sockaddr(sin, ic_myaddr, 0);
if ((err = ic_devinet_ioctl(SIOCSIFADDR, &ir)) < 0) {
- printk(KERN_ERR "IP-Config: Unable to set interface address (%d).\n", err);
+ pr_err("IP-Config: Unable to set interface address (%d)\n",
+ err);
return -1;
}
set_sockaddr(sin, ic_netmask, 0);
if ((err = ic_devinet_ioctl(SIOCSIFNETMASK, &ir)) < 0) {
- printk(KERN_ERR "IP-Config: Unable to set interface netmask (%d).\n", err);
+ pr_err("IP-Config: Unable to set interface netmask (%d)\n",
+ err);
return -1;
}
set_sockaddr(sin, ic_myaddr | ~ic_netmask, 0);
if ((err = ic_devinet_ioctl(SIOCSIFBRDADDR, &ir)) < 0) {
- printk(KERN_ERR "IP-Config: Unable to set interface broadcast address (%d).\n", err);
+ pr_err("IP-Config: Unable to set interface broadcast address (%d)\n",
+ err);
return -1;
}
/* Handle the case where we need non-standard MTU on the boot link (a network
@@ -380,8 +386,8 @@ static int __init ic_setup_if(void)
strcpy(ir.ifr_name, ic_dev->name);
ir.ifr_mtu = ic_dev_mtu;
if ((err = ic_dev_ioctl(SIOCSIFMTU, &ir)) < 0)
- printk(KERN_ERR "IP-Config: Unable to set interface mtu to %d (%d).\n",
- ic_dev_mtu, err);
+ pr_err("IP-Config: Unable to set interface mtu to %d (%d)\n",
+ ic_dev_mtu, err);
}
return 0;
}
@@ -396,7 +402,7 @@ static int __init ic_setup_routes(void)
memset(&rm, 0, sizeof(rm));
if ((ic_gateway ^ ic_myaddr) & ic_netmask) {
- printk(KERN_ERR "IP-Config: Gateway not on directly connected network.\n");
+ pr_err("IP-Config: Gateway not on directly connected network\n");
return -1;
}
set_sockaddr((struct sockaddr_in *) &rm.rt_dst, 0, 0);
@@ -404,7 +410,8 @@ static int __init ic_setup_routes(void)
set_sockaddr((struct sockaddr_in *) &rm.rt_gateway, ic_gateway, 0);
rm.rt_flags = RTF_UP | RTF_GATEWAY;
if ((err = ic_route_ioctl(SIOCADDRT, &rm)) < 0) {
- printk(KERN_ERR "IP-Config: Cannot add default route (%d).\n", err);
+ pr_err("IP-Config: Cannot add default route (%d)\n",
+ err);
return -1;
}
}
@@ -437,8 +444,8 @@ static int __init ic_defaults(void)
else if (IN_CLASSC(ntohl(ic_myaddr)))
ic_netmask = htonl(IN_CLASSC_NET);
else {
- printk(KERN_ERR "IP-Config: Unable to guess netmask for address %pI4\n",
- &ic_myaddr);
+ pr_err("IP-Config: Unable to guess netmask for address %pI4\n",
+ &ic_myaddr);
return -1;
}
printk("IP-Config: Guessing netmask %pI4\n", &ic_netmask);
@@ -688,8 +695,8 @@ ic_dhcp_init_options(u8 *options)
e += len;
}
if (*vendor_class_identifier) {
- printk(KERN_INFO "DHCP: sending class identifier \"%s\"\n",
- vendor_class_identifier);
+ pr_info("DHCP: sending class identifier \"%s\"\n",
+ vendor_class_identifier);
*e++ = 60; /* Class-identifier */
len = strlen(vendor_class_identifier);
*e++ = len;
@@ -949,8 +956,7 @@ static int __init ic_bootp_recv(struct sk_buff *skb, struct net_device *dev, str
/* Fragments are not supported */
if (ip_is_fragment(h)) {
if (net_ratelimit())
- printk(KERN_ERR "DHCP/BOOTP: Ignoring fragmented "
- "reply.\n");
+ pr_err("DHCP/BOOTP: Ignoring fragmented reply\n");
goto drop;
}
@@ -999,8 +1005,7 @@ static int __init ic_bootp_recv(struct sk_buff *skb, struct net_device *dev, str
if (b->op != BOOTP_REPLY ||
b->xid != d->xid) {
if (net_ratelimit())
- printk(KERN_ERR "DHCP/BOOTP: Reply not for us, "
- "op[%x] xid[%x]\n",
+ pr_err("DHCP/BOOTP: Reply not for us, op[%x] xid[%x]\n",
b->op, b->xid);
goto drop_unlock;
}
@@ -1008,7 +1013,7 @@ static int __init ic_bootp_recv(struct sk_buff *skb, struct net_device *dev, str
/* Is it a reply for the device we are configuring? */
if (b->xid != ic_dev_xid) {
if (net_ratelimit())
- printk(KERN_ERR "DHCP/BOOTP: Ignoring delayed packet\n");
+ pr_err("DHCP/BOOTP: Ignoring delayed packet\n");
goto drop_unlock;
}
@@ -1146,17 +1151,17 @@ static int __init ic_dynamic(void)
* are missing, and without DHCP/BOOTP/RARP we are unable to get it.
*/
if (!ic_proto_enabled) {
- printk(KERN_ERR "IP-Config: Incomplete network configuration information.\n");
+ pr_err("IP-Config: Incomplete network configuration information\n");
return -1;
}
#ifdef IPCONFIG_BOOTP
if ((ic_proto_enabled ^ ic_proto_have_if) & IC_BOOTP)
- printk(KERN_ERR "DHCP/BOOTP: No suitable device found.\n");
+ pr_err("DHCP/BOOTP: No suitable device found\n");
#endif
#ifdef IPCONFIG_RARP
if ((ic_proto_enabled ^ ic_proto_have_if) & IC_RARP)
- printk(KERN_ERR "RARP: No suitable device found.\n");
+ pr_err("RARP: No suitable device found\n");
#endif
if (!ic_proto_have_if)
@@ -1183,11 +1188,11 @@ static int __init ic_dynamic(void)
* [Actually we could now, but the nothing else running note still
* applies.. - AC]
*/
- printk(KERN_NOTICE "Sending %s%s%s requests .",
- do_bootp
- ? ((ic_proto_enabled & IC_USE_DHCP) ? "DHCP" : "BOOTP") : "",
- (do_bootp && do_rarp) ? " and " : "",
- do_rarp ? "RARP" : "");
+ pr_notice("Sending %s%s%s requests .",
+ do_bootp
+ ? ((ic_proto_enabled & IC_USE_DHCP) ? "DHCP" : "BOOTP") : "",
+ (do_bootp && do_rarp) ? " and " : "",
+ do_rarp ? "RARP" : "");
start_jiffies = jiffies;
d = ic_first_dev;
@@ -1216,13 +1221,13 @@ static int __init ic_dynamic(void)
(ic_proto_enabled & IC_USE_DHCP) &&
ic_dhcp_msgtype != DHCPACK) {
ic_got_reply = 0;
- printk(KERN_CONT ",");
+ pr_cont(",");
continue;
}
#endif /* IPCONFIG_DHCP */
if (ic_got_reply) {
- printk(KERN_CONT " OK\n");
+ pr_cont(" OK\n");
break;
}
@@ -1230,7 +1235,7 @@ static int __init ic_dynamic(void)
continue;
if (! --retries) {
- printk(KERN_CONT " timed out!\n");
+ pr_cont(" timed out!\n");
break;
}
@@ -1240,7 +1245,7 @@ static int __init ic_dynamic(void)
if (timeout > CONF_TIMEOUT_MAX)
timeout = CONF_TIMEOUT_MAX;
- printk(KERN_CONT ".");
+ pr_cont(".");
}
#ifdef IPCONFIG_BOOTP
@@ -1260,8 +1265,8 @@ static int __init ic_dynamic(void)
printk("IP-Config: Got %s answer from %pI4, ",
((ic_got_reply & IC_RARP) ? "RARP"
: (ic_proto_enabled & IC_USE_DHCP) ? "DHCP" : "BOOTP"),
- &ic_servaddr);
- printk(KERN_CONT "my address is %pI4\n", &ic_myaddr);
+ &ic_servaddr);
+ pr_cont("my address is %pI4\n", &ic_myaddr);
return 0;
}
@@ -1437,24 +1442,22 @@ static int __init ip_auto_config(void)
*/
#ifdef CONFIG_ROOT_NFS
if (ROOT_DEV == Root_NFS) {
- printk(KERN_ERR
- "IP-Config: Retrying forever (NFS root)...\n");
+ pr_err("IP-Config: Retrying forever (NFS root)...\n");
goto try_try_again;
}
#endif
if (--retries) {
- printk(KERN_ERR
- "IP-Config: Reopening network devices...\n");
+ pr_err("IP-Config: Reopening network devices...\n");
goto try_try_again;
}
/* Oh, well. At least we tried. */
- printk(KERN_ERR "IP-Config: Auto-configuration of network failed.\n");
+ pr_err("IP-Config: Auto-configuration of network failed\n");
return -1;
}
#else /* !DYNAMIC */
- printk(KERN_ERR "IP-Config: Incomplete network configuration information.\n");
+ pr_err("IP-Config: Incomplete network configuration information\n");
ic_close_devs();
return -1;
#endif /* IPCONFIG_DYNAMIC */
@@ -1492,19 +1495,16 @@ static int __init ip_auto_config(void)
/*
* Clue in the operator.
*/
- printk("IP-Config: Complete:\n");
- printk(" device=%s", ic_dev->name);
- printk(KERN_CONT ", addr=%pI4", &ic_myaddr);
- printk(KERN_CONT ", mask=%pI4", &ic_netmask);
- printk(KERN_CONT ", gw=%pI4", &ic_gateway);
- printk(KERN_CONT ",\n host=%s, domain=%s, nis-domain=%s",
- utsname()->nodename, ic_domain, utsname()->domainname);
- printk(KERN_CONT ",\n bootserver=%pI4", &ic_servaddr);
- printk(KERN_CONT ", rootserver=%pI4", &root_server_addr);
- printk(KERN_CONT ", rootpath=%s", root_server_path);
+ pr_info("IP-Config: Complete:\n");
+ pr_info(" device=%s, addr=%pI4, mask=%pI4, gw=%pI4\n",
+ ic_dev->name, &ic_myaddr, &ic_netmask, &ic_gateway);
+ pr_info(" host=%s, domain=%s, nis-domain=%s\n",
+ utsname()->nodename, ic_domain, utsname()->domainname);
+ pr_info(" bootserver=%pI4, rootserver=%pI4, rootpath=%s",
+ &ic_servaddr, &root_server_addr, root_server_path);
if (ic_dev_mtu)
- printk(KERN_CONT ", mtu=%d", ic_dev_mtu);
- printk(KERN_CONT "\n");
+ pr_cont(", mtu=%d", ic_dev_mtu);
+ pr_cont("\n");
#endif /* !SILENT */
return 0;
@@ -1637,8 +1637,8 @@ static int __init vendor_class_identifier_setup(char *addrs)
if (strlcpy(vendor_class_identifier, addrs,
sizeof(vendor_class_identifier))
>= sizeof(vendor_class_identifier))
- printk(KERN_WARNING "DHCP: vendorclass too long, truncated to \"%s\"",
- vendor_class_identifier);
+ pr_warn("DHCP: vendorclass too long, truncated to \"%s\"",
+ vendor_class_identifier);
return 1;
}
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 22a19931530..ae1413e3f2f 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -454,8 +454,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
dev->stats.tx_fifo_errors++;
goto tx_error;
}
- if ((dst = rt->rt_gateway) == 0)
- goto tx_error_icmp;
+ dst = rt->rt_gateway;
}
rt = ip_route_output_ports(dev_net(dev), &fl4, NULL,
@@ -893,7 +892,7 @@ static int __init ipip_init(void)
err = xfrm4_tunnel_register(&ipip_handler, AF_INET);
if (err < 0) {
unregister_pernet_device(&ipip_net_ops);
- printk(KERN_INFO "ipip init: can't register tunnel\n");
+ pr_info("%s: can't register tunnel\n", __func__);
}
return err;
}
@@ -901,7 +900,7 @@ static int __init ipip_init(void)
static void __exit ipip_fini(void)
{
if (xfrm4_tunnel_deregister(&ipip_handler, AF_INET))
- printk(KERN_INFO "ipip close: can't deregister tunnel\n");
+ pr_info("%s: can't deregister tunnel\n", __func__);
unregister_pernet_device(&ipip_net_ops);
}
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 7bc2db6db8d..0518a4fb177 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -951,7 +951,7 @@ static int ipmr_cache_report(struct mr_table *mrt,
rcu_read_unlock();
if (ret < 0) {
if (net_ratelimit())
- printk(KERN_WARNING "mroute: pending queue full, dropping entries.\n");
+ pr_warn("mroute: pending queue full, dropping entries\n");
kfree_skb(skb);
}
@@ -2538,7 +2538,7 @@ int __init ip_mr_init(void)
goto reg_notif_fail;
#ifdef CONFIG_IP_PIMSM_V2
if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) {
- printk(KERN_ERR "ip_mr_init: can't add PIM protocol\n");
+ pr_err("%s: can't add PIM protocol\n", __func__);
err = -EAGAIN;
goto add_proto_fail;
}
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index 74dfc9e5211..fcc543cd987 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -123,15 +123,6 @@ config IP_NF_TARGET_REJECT
To compile it as a module, choose M here. If unsure, say N.
-config IP_NF_TARGET_LOG
- tristate "LOG target support"
- default m if NETFILTER_ADVANCED=n
- help
- This option adds a `LOG' target, which allows you to create rules in
- any iptables table which records the packet header to the syslog.
-
- To compile it as a module, choose M here. If unsure, say N.
-
config IP_NF_TARGET_ULOG
tristate "ULOG target support"
default m if NETFILTER_ADVANCED=n
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index 213a462b739..240b68469a7 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -54,7 +54,6 @@ obj-$(CONFIG_IP_NF_MATCH_RPFILTER) += ipt_rpfilter.o
# targets
obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o
-obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o
obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o
obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c
deleted file mode 100644
index d76d6c9ed94..00000000000
--- a/net/ipv4/netfilter/ipt_LOG.c
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * This is a module which is used for logging packets.
- */
-
-/* (C) 1999-2001 Paul `Rusty' Russell
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ip.h>
-#include <net/icmp.h>
-#include <net/udp.h>
-#include <net/tcp.h>
-#include <net/route.h>
-
-#include <linux/netfilter.h>
-#include <linux/netfilter/x_tables.h>
-#include <linux/netfilter_ipv4/ipt_LOG.h>
-#include <net/netfilter/nf_log.h>
-#include <net/netfilter/xt_log.h>
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
-MODULE_DESCRIPTION("Xtables: IPv4 packet logging to syslog");
-
-/* One level of recursion won't kill us */
-static void dump_packet(struct sbuff *m,
- const struct nf_loginfo *info,
- const struct sk_buff *skb,
- unsigned int iphoff)
-{
- struct iphdr _iph;
- const struct iphdr *ih;
- unsigned int logflags;
-
- if (info->type == NF_LOG_TYPE_LOG)
- logflags = info->u.log.logflags;
- else
- logflags = NF_LOG_MASK;
-
- ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph);
- if (ih == NULL) {
- sb_add(m, "TRUNCATED");
- return;
- }
-
- /* Important fields:
- * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */
- /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */
- sb_add(m, "SRC=%pI4 DST=%pI4 ",
- &ih->saddr, &ih->daddr);
-
- /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */
- sb_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
- ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK,
- ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id));
-
- /* Max length: 6 "CE DF MF " */
- if (ntohs(ih->frag_off) & IP_CE)
- sb_add(m, "CE ");
- if (ntohs(ih->frag_off) & IP_DF)
- sb_add(m, "DF ");
- if (ntohs(ih->frag_off) & IP_MF)
- sb_add(m, "MF ");
-
- /* Max length: 11 "FRAG:65535 " */
- if (ntohs(ih->frag_off) & IP_OFFSET)
- sb_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET);
-
- if ((logflags & IPT_LOG_IPOPT) &&
- ih->ihl * 4 > sizeof(struct iphdr)) {
- const unsigned char *op;
- unsigned char _opt[4 * 15 - sizeof(struct iphdr)];
- unsigned int i, optsize;
-
- optsize = ih->ihl * 4 - sizeof(struct iphdr);
- op = skb_header_pointer(skb, iphoff+sizeof(_iph),
- optsize, _opt);
- if (op == NULL) {
- sb_add(m, "TRUNCATED");
- return;
- }
-
- /* Max length: 127 "OPT (" 15*4*2chars ") " */
- sb_add(m, "OPT (");
- for (i = 0; i < optsize; i++)
- sb_add(m, "%02X", op[i]);
- sb_add(m, ") ");
- }
-
- switch (ih->protocol) {
- case IPPROTO_TCP: {
- struct tcphdr _tcph;
- const struct tcphdr *th;
-
- /* Max length: 10 "PROTO=TCP " */
- sb_add(m, "PROTO=TCP ");
-
- if (ntohs(ih->frag_off) & IP_OFFSET)
- break;
-
- /* Max length: 25 "INCOMPLETE [65535 bytes] " */
- th = skb_header_pointer(skb, iphoff + ih->ihl * 4,
- sizeof(_tcph), &_tcph);
- if (th == NULL) {
- sb_add(m, "INCOMPLETE [%u bytes] ",
- skb->len - iphoff - ih->ihl*4);
- break;
- }
-
- /* Max length: 20 "SPT=65535 DPT=65535 " */
- sb_add(m, "SPT=%u DPT=%u ",
- ntohs(th->source), ntohs(th->dest));
- /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */
- if (logflags & IPT_LOG_TCPSEQ)
- sb_add(m, "SEQ=%u ACK=%u ",
- ntohl(th->seq), ntohl(th->ack_seq));
- /* Max length: 13 "WINDOW=65535 " */
- sb_add(m, "WINDOW=%u ", ntohs(th->window));
- /* Max length: 9 "RES=0x3F " */
- sb_add(m, "RES=0x%02x ", (u8)(ntohl(tcp_flag_word(th) & TCP_RESERVED_BITS) >> 22));
- /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */
- if (th->cwr)
- sb_add(m, "CWR ");
- if (th->ece)
- sb_add(m, "ECE ");
- if (th->urg)
- sb_add(m, "URG ");
- if (th->ack)
- sb_add(m, "ACK ");
- if (th->psh)
- sb_add(m, "PSH ");
- if (th->rst)
- sb_add(m, "RST ");
- if (th->syn)
- sb_add(m, "SYN ");
- if (th->fin)
- sb_add(m, "FIN ");
- /* Max length: 11 "URGP=65535 " */
- sb_add(m, "URGP=%u ", ntohs(th->urg_ptr));
-
- if ((logflags & IPT_LOG_TCPOPT) &&
- th->doff * 4 > sizeof(struct tcphdr)) {
- unsigned char _opt[4 * 15 - sizeof(struct tcphdr)];
- const unsigned char *op;
- unsigned int i, optsize;
-
- optsize = th->doff * 4 - sizeof(struct tcphdr);
- op = skb_header_pointer(skb,
- iphoff+ih->ihl*4+sizeof(_tcph),
- optsize, _opt);
- if (op == NULL) {
- sb_add(m, "TRUNCATED");
- return;
- }
-
- /* Max length: 127 "OPT (" 15*4*2chars ") " */
- sb_add(m, "OPT (");
- for (i = 0; i < optsize; i++)
- sb_add(m, "%02X", op[i]);
- sb_add(m, ") ");
- }
- break;
- }
- case IPPROTO_UDP:
- case IPPROTO_UDPLITE: {
- struct udphdr _udph;
- const struct udphdr *uh;
-
- if (ih->protocol == IPPROTO_UDP)
- /* Max length: 10 "PROTO=UDP " */
- sb_add(m, "PROTO=UDP " );
- else /* Max length: 14 "PROTO=UDPLITE " */
- sb_add(m, "PROTO=UDPLITE ");
-
- if (ntohs(ih->frag_off) & IP_OFFSET)
- break;
-
- /* Max length: 25 "INCOMPLETE [65535 bytes] " */
- uh = skb_header_pointer(skb, iphoff+ih->ihl*4,
- sizeof(_udph), &_udph);
- if (uh == NULL) {
- sb_add(m, "INCOMPLETE [%u bytes] ",
- skb->len - iphoff - ih->ihl*4);
- break;
- }
-
- /* Max length: 20 "SPT=65535 DPT=65535 " */
- sb_add(m, "SPT=%u DPT=%u LEN=%u ",
- ntohs(uh->source), ntohs(uh->dest),
- ntohs(uh->len));
- break;
- }
- case IPPROTO_ICMP: {
- struct icmphdr _icmph;
- const struct icmphdr *ich;
- static const size_t required_len[NR_ICMP_TYPES+1]
- = { [ICMP_ECHOREPLY] = 4,
- [ICMP_DEST_UNREACH]
- = 8 + sizeof(struct iphdr),
- [ICMP_SOURCE_QUENCH]
- = 8 + sizeof(struct iphdr),
- [ICMP_REDIRECT]
- = 8 + sizeof(struct iphdr),
- [ICMP_ECHO] = 4,
- [ICMP_TIME_EXCEEDED]
- = 8 + sizeof(struct iphdr),
- [ICMP_PARAMETERPROB]
- = 8 + sizeof(struct iphdr),
- [ICMP_TIMESTAMP] = 20,
- [ICMP_TIMESTAMPREPLY] = 20,
- [ICMP_ADDRESS] = 12,
- [ICMP_ADDRESSREPLY] = 12 };
-
- /* Max length: 11 "PROTO=ICMP " */
- sb_add(m, "PROTO=ICMP ");
-
- if (ntohs(ih->frag_off) & IP_OFFSET)
- break;
-
- /* Max length: 25 "INCOMPLETE [65535 bytes] " */
- ich = skb_header_pointer(skb, iphoff + ih->ihl * 4,
- sizeof(_icmph), &_icmph);
- if (ich == NULL) {
- sb_add(m, "INCOMPLETE [%u bytes] ",
- skb->len - iphoff - ih->ihl*4);
- break;
- }
-
- /* Max length: 18 "TYPE=255 CODE=255 " */
- sb_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code);
-
- /* Max length: 25 "INCOMPLETE [65535 bytes] " */
- if (ich->type <= NR_ICMP_TYPES &&
- required_len[ich->type] &&
- skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) {
- sb_add(m, "INCOMPLETE [%u bytes] ",
- skb->len - iphoff - ih->ihl*4);
- break;
- }
-
- switch (ich->type) {
- case ICMP_ECHOREPLY:
- case ICMP_ECHO:
- /* Max length: 19 "ID=65535 SEQ=65535 " */
- sb_add(m, "ID=%u SEQ=%u ",
- ntohs(ich->un.echo.id),
- ntohs(ich->un.echo.sequence));
- break;
-
- case ICMP_PARAMETERPROB:
- /* Max length: 14 "PARAMETER=255 " */
- sb_add(m, "PARAMETER=%u ",
- ntohl(ich->un.gateway) >> 24);
- break;
- case ICMP_REDIRECT:
- /* Max length: 24 "GATEWAY=255.255.255.255 " */
- sb_add(m, "GATEWAY=%pI4 ", &ich->un.gateway);
- /* Fall through */
- case ICMP_DEST_UNREACH:
- case ICMP_SOURCE_QUENCH:
- case ICMP_TIME_EXCEEDED:
- /* Max length: 3+maxlen */
- if (!iphoff) { /* Only recurse once. */
- sb_add(m, "[");
- dump_packet(m, info, skb,
- iphoff + ih->ihl*4+sizeof(_icmph));
- sb_add(m, "] ");
- }
-
- /* Max length: 10 "MTU=65535 " */
- if (ich->type == ICMP_DEST_UNREACH &&
- ich->code == ICMP_FRAG_NEEDED)
- sb_add(m, "MTU=%u ", ntohs(ich->un.frag.mtu));
- }
- break;
- }
- /* Max Length */
- case IPPROTO_AH: {
- struct ip_auth_hdr _ahdr;
- const struct ip_auth_hdr *ah;
-
- if (ntohs(ih->frag_off) & IP_OFFSET)
- break;
-
- /* Max length: 9 "PROTO=AH " */
- sb_add(m, "PROTO=AH ");
-
- /* Max length: 25 "INCOMPLETE [65535 bytes] " */
- ah = skb_header_pointer(skb, iphoff+ih->ihl*4,
- sizeof(_ahdr), &_ahdr);
- if (ah == NULL) {
- sb_add(m, "INCOMPLETE [%u bytes] ",
- skb->len - iphoff - ih->ihl*4);
- break;
- }
-
- /* Length: 15 "SPI=0xF1234567 " */
- sb_add(m, "SPI=0x%x ", ntohl(ah->spi));
- break;
- }
- case IPPROTO_ESP: {
- struct ip_esp_hdr _esph;
- const struct ip_esp_hdr *eh;
-
- /* Max length: 10 "PROTO=ESP " */
- sb_add(m, "PROTO=ESP ");
-
- if (ntohs(ih->frag_off) & IP_OFFSET)
- break;
-
- /* Max length: 25 "INCOMPLETE [65535 bytes] " */
- eh = skb_header_pointer(skb, iphoff+ih->ihl*4,
- sizeof(_esph), &_esph);
- if (eh == NULL) {
- sb_add(m, "INCOMPLETE [%u bytes] ",
- skb->len - iphoff - ih->ihl*4);
- break;
- }
-
- /* Length: 15 "SPI=0xF1234567 " */
- sb_add(m, "SPI=0x%x ", ntohl(eh->spi));
- break;
- }
- /* Max length: 10 "PROTO 255 " */
- default:
- sb_add(m, "PROTO=%u ", ih->protocol);
- }
-
- /* Max length: 15 "UID=4294967295 " */
- if ((logflags & IPT_LOG_UID) && !iphoff && skb->sk) {
- read_lock_bh(&skb->sk->sk_callback_lock);
- if (skb->sk->sk_socket && skb->sk->sk_socket->file)
- sb_add(m, "UID=%u GID=%u ",
- skb->sk->sk_socket->file->f_cred->fsuid,
- skb->sk->sk_socket->file->f_cred->fsgid);
- read_unlock_bh(&skb->sk->sk_callback_lock);
- }
-
- /* Max length: 16 "MARK=0xFFFFFFFF " */
- if (!iphoff && skb->mark)
- sb_add(m, "MARK=0x%x ", skb->mark);
-
- /* Proto Max log string length */
- /* IP: 40+46+6+11+127 = 230 */
- /* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */
- /* UDP: 10+max(25,20) = 35 */
- /* UDPLITE: 14+max(25,20) = 39 */
- /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */
- /* ESP: 10+max(25)+15 = 50 */
- /* AH: 9+max(25)+15 = 49 */
- /* unknown: 10 */
-
- /* (ICMP allows recursion one level deep) */
- /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */
- /* maxlen = 230+ 91 + 230 + 252 = 803 */
-}
-
-static void dump_mac_header(struct sbuff *m,
- const struct nf_loginfo *info,
- const struct sk_buff *skb)
-{
- struct net_device *dev = skb->dev;
- unsigned int logflags = 0;
-
- if (info->type == NF_LOG_TYPE_LOG)
- logflags = info->u.log.logflags;
-
- if (!(logflags & IPT_LOG_MACDECODE))
- goto fallback;
-
- switch (dev->type) {
- case ARPHRD_ETHER:
- sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ",
- eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
- ntohs(eth_hdr(skb)->h_proto));
- return;
- default:
- break;
- }
-
-fallback:
- sb_add(m, "MAC=");
- if (dev->hard_header_len &&
- skb->mac_header != skb->network_header) {
- const unsigned char *p = skb_mac_header(skb);
- unsigned int i;
-
- sb_add(m, "%02x", *p++);
- for (i = 1; i < dev->hard_header_len; i++, p++)
- sb_add(m, ":%02x", *p);
- }
- sb_add(m, " ");
-}
-
-static struct nf_loginfo default_loginfo = {
- .type = NF_LOG_TYPE_LOG,
- .u = {
- .log = {
- .level = 5,
- .logflags = NF_LOG_MASK,
- },
- },
-};
-
-static void
-ipt_log_packet(u_int8_t pf,
- unsigned int hooknum,
- const struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- const struct nf_loginfo *loginfo,
- const char *prefix)
-{
- struct sbuff *m = sb_open();
-
- if (!loginfo)
- loginfo = &default_loginfo;
-
- sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level,
- prefix,
- in ? in->name : "",
- out ? out->name : "");
-#ifdef CONFIG_BRIDGE_NETFILTER
- if (skb->nf_bridge) {
- const struct net_device *physindev;
- const struct net_device *physoutdev;
-
- physindev = skb->nf_bridge->physindev;
- if (physindev && in != physindev)
- sb_add(m, "PHYSIN=%s ", physindev->name);
- physoutdev = skb->nf_bridge->physoutdev;
- if (physoutdev && out != physoutdev)
- sb_add(m, "PHYSOUT=%s ", physoutdev->name);
- }
-#endif
-
- if (in != NULL)
- dump_mac_header(m, loginfo, skb);
-
- dump_packet(m, loginfo, skb, 0);
-
- sb_close(m);
-}
-
-static unsigned int
-log_tg(struct sk_buff *skb, const struct xt_action_param *par)
-{
- const struct ipt_log_info *loginfo = par->targinfo;
- struct nf_loginfo li;
-
- li.type = NF_LOG_TYPE_LOG;
- li.u.log.level = loginfo->level;
- li.u.log.logflags = loginfo->logflags;
-
- ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, par->out, &li,
- loginfo->prefix);
- return XT_CONTINUE;
-}
-
-static int log_tg_check(const struct xt_tgchk_param *par)
-{
- const struct ipt_log_info *loginfo = par->targinfo;
-
- if (loginfo->level >= 8) {
- pr_debug("level %u >= 8\n", loginfo->level);
- return -EINVAL;
- }
- if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') {
- pr_debug("prefix is not null-terminated\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static struct xt_target log_tg_reg __read_mostly = {
- .name = "LOG",
- .family = NFPROTO_IPV4,
- .target = log_tg,
- .targetsize = sizeof(struct ipt_log_info),
- .checkentry = log_tg_check,
- .me = THIS_MODULE,
-};
-
-static struct nf_logger ipt_log_logger __read_mostly = {
- .name = "ipt_LOG",
- .logfn = &ipt_log_packet,
- .me = THIS_MODULE,
-};
-
-static int __init log_tg_init(void)
-{
- int ret;
-
- ret = xt_register_target(&log_tg_reg);
- if (ret < 0)
- return ret;
- nf_log_register(NFPROTO_IPV4, &ipt_log_logger);
- return 0;
-}
-
-static void __exit log_tg_exit(void)
-{
- nf_log_unregister(&ipt_log_logger);
- xt_unregister_target(&log_tg_reg);
-}
-
-module_init(log_tg_init);
-module_exit(log_tg_exit);
diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
index ab5b27a2916..7cbe9cb261c 100644
--- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
+++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
@@ -75,25 +75,31 @@ static int icmp_print_tuple(struct seq_file *s,
ntohs(tuple->src.u.icmp.id));
}
+static unsigned int *icmp_get_timeouts(struct net *net)
+{
+ return &nf_ct_icmp_timeout;
+}
+
/* Returns verdict for packet, or -1 for invalid. */
static int icmp_packet(struct nf_conn *ct,
const struct sk_buff *skb,
unsigned int dataoff,
enum ip_conntrack_info ctinfo,
u_int8_t pf,
- unsigned int hooknum)
+ unsigned int hooknum,
+ unsigned int *timeout)
{
/* Do not immediately delete the connection after the first
successful reply to avoid excessive conntrackd traffic
and also to handle correctly ICMP echo reply duplicates. */
- nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmp_timeout);
+ nf_ct_refresh_acct(ct, ctinfo, skb, *timeout);
return NF_ACCEPT;
}
/* Called when a new connection for this protocol found. */
static bool icmp_new(struct nf_conn *ct, const struct sk_buff *skb,
- unsigned int dataoff)
+ unsigned int dataoff, unsigned int *timeouts)
{
static const u_int8_t valid_new[] = {
[ICMP_ECHO] = 1,
@@ -263,6 +269,44 @@ static int icmp_nlattr_tuple_size(void)
}
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_cttimeout.h>
+
+static int icmp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data)
+{
+ unsigned int *timeout = data;
+
+ if (tb[CTA_TIMEOUT_ICMP_TIMEOUT]) {
+ *timeout =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_ICMP_TIMEOUT])) * HZ;
+ } else {
+ /* Set default ICMP timeout. */
+ *timeout = nf_ct_icmp_timeout;
+ }
+ return 0;
+}
+
+static int
+icmp_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
+{
+ const unsigned int *timeout = data;
+
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_ICMP_TIMEOUT, htonl(*timeout / HZ));
+
+ return 0;
+
+nla_put_failure:
+ return -ENOSPC;
+}
+
+static const struct nla_policy
+icmp_timeout_nla_policy[CTA_TIMEOUT_ICMP_MAX+1] = {
+ [CTA_TIMEOUT_ICMP_TIMEOUT] = { .type = NLA_U32 },
+};
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
+
#ifdef CONFIG_SYSCTL
static struct ctl_table_header *icmp_sysctl_header;
static struct ctl_table icmp_sysctl_table[] = {
@@ -298,6 +342,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly =
.invert_tuple = icmp_invert_tuple,
.print_tuple = icmp_print_tuple,
.packet = icmp_packet,
+ .get_timeouts = icmp_get_timeouts,
.new = icmp_new,
.error = icmp_error,
.destroy = NULL,
@@ -308,6 +353,15 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly =
.nlattr_to_tuple = icmp_nlattr_to_tuple,
.nla_policy = icmp_nla_policy,
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = icmp_timeout_nlattr_to_obj,
+ .obj_to_nlattr = icmp_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_ICMP_MAX,
+ .obj_size = sizeof(unsigned int),
+ .nla_policy = icmp_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
#ifdef CONFIG_SYSCTL
.ctl_table_header = &icmp_sysctl_header,
.ctl_table = icmp_sysctl_table,
diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c
index a708933dc23..abb52adf5ac 100644
--- a/net/ipv4/netfilter/nf_nat_core.c
+++ b/net/ipv4/netfilter/nf_nat_core.c
@@ -686,6 +686,11 @@ static struct pernet_operations nf_nat_net_ops = {
.exit = nf_nat_net_exit,
};
+static struct nf_ct_helper_expectfn follow_master_nat = {
+ .name = "nat-follow-master",
+ .expectfn = nf_nat_follow_master,
+};
+
static int __init nf_nat_init(void)
{
size_t i;
@@ -717,6 +722,8 @@ static int __init nf_nat_init(void)
l3proto = nf_ct_l3proto_find_get((u_int16_t)AF_INET);
+ nf_ct_helper_expectfn_register(&follow_master_nat);
+
BUG_ON(nf_nat_seq_adjust_hook != NULL);
RCU_INIT_POINTER(nf_nat_seq_adjust_hook, nf_nat_seq_adjust);
BUG_ON(nfnetlink_parse_nat_setup_hook != NULL);
@@ -736,6 +743,7 @@ static void __exit nf_nat_cleanup(void)
unregister_pernet_subsys(&nf_nat_net_ops);
nf_ct_l3proto_put(l3proto);
nf_ct_extend_unregister(&nat_extend);
+ nf_ct_helper_expectfn_unregister(&follow_master_nat);
RCU_INIT_POINTER(nf_nat_seq_adjust_hook, NULL);
RCU_INIT_POINTER(nfnetlink_parse_nat_setup_hook, NULL);
RCU_INIT_POINTER(nf_ct_nat_offset, NULL);
diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c
index dc1dd912baf..82536701e3a 100644
--- a/net/ipv4/netfilter/nf_nat_h323.c
+++ b/net/ipv4/netfilter/nf_nat_h323.c
@@ -568,6 +568,16 @@ static int nat_callforwarding(struct sk_buff *skb, struct nf_conn *ct,
return 0;
}
+static struct nf_ct_helper_expectfn q931_nat = {
+ .name = "Q.931",
+ .expectfn = ip_nat_q931_expect,
+};
+
+static struct nf_ct_helper_expectfn callforwarding_nat = {
+ .name = "callforwarding",
+ .expectfn = ip_nat_callforwarding_expect,
+};
+
/****************************************************************************/
static int __init init(void)
{
@@ -590,6 +600,8 @@ static int __init init(void)
RCU_INIT_POINTER(nat_h245_hook, nat_h245);
RCU_INIT_POINTER(nat_callforwarding_hook, nat_callforwarding);
RCU_INIT_POINTER(nat_q931_hook, nat_q931);
+ nf_ct_helper_expectfn_register(&q931_nat);
+ nf_ct_helper_expectfn_register(&callforwarding_nat);
return 0;
}
@@ -605,6 +617,8 @@ static void __exit fini(void)
RCU_INIT_POINTER(nat_h245_hook, NULL);
RCU_INIT_POINTER(nat_callforwarding_hook, NULL);
RCU_INIT_POINTER(nat_q931_hook, NULL);
+ nf_ct_helper_expectfn_unregister(&q931_nat);
+ nf_ct_helper_expectfn_unregister(&callforwarding_nat);
synchronize_rcu();
}
diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c
index d0319f96269..57932c43960 100644
--- a/net/ipv4/netfilter/nf_nat_sip.c
+++ b/net/ipv4/netfilter/nf_nat_sip.c
@@ -526,6 +526,11 @@ err1:
return NF_DROP;
}
+static struct nf_ct_helper_expectfn sip_nat = {
+ .name = "sip",
+ .expectfn = ip_nat_sip_expected,
+};
+
static void __exit nf_nat_sip_fini(void)
{
RCU_INIT_POINTER(nf_nat_sip_hook, NULL);
@@ -535,6 +540,7 @@ static void __exit nf_nat_sip_fini(void)
RCU_INIT_POINTER(nf_nat_sdp_port_hook, NULL);
RCU_INIT_POINTER(nf_nat_sdp_session_hook, NULL);
RCU_INIT_POINTER(nf_nat_sdp_media_hook, NULL);
+ nf_ct_helper_expectfn_unregister(&sip_nat);
synchronize_rcu();
}
@@ -554,6 +560,7 @@ static int __init nf_nat_sip_init(void)
RCU_INIT_POINTER(nf_nat_sdp_port_hook, ip_nat_sdp_port);
RCU_INIT_POINTER(nf_nat_sdp_session_hook, ip_nat_sdp_session);
RCU_INIT_POINTER(nf_nat_sdp_media_hook, ip_nat_sdp_media);
+ nf_ct_helper_expectfn_register(&sip_nat);
return 0;
}
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index aea5a199c37..ab6b36e6da1 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -156,7 +156,7 @@ static struct sock *ping_v4_lookup(struct net *net, __be32 saddr, __be32 daddr,
struct hlist_nulls_node *hnode;
pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n",
- (int)ident, &daddr, dif);
+ (int)ident, &daddr, dif);
read_lock_bh(&ping_table.lock);
ping_portaddr_for_each_entry(sk, hnode, hslot) {
@@ -229,7 +229,7 @@ static int ping_init_sock(struct sock *sk)
static void ping_close(struct sock *sk, long timeout)
{
pr_debug("ping_close(sk=%p,sk->num=%u)\n",
- inet_sk(sk), inet_sk(sk)->inet_num);
+ inet_sk(sk), inet_sk(sk)->inet_num);
pr_debug("isk->refcnt = %d\n", sk->sk_refcnt.counter);
sk_common_release(sk);
@@ -252,7 +252,7 @@ static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
return -EINVAL;
pr_debug("ping_v4_bind(sk=%p,sa_addr=%08x,sa_port=%d)\n",
- sk, addr->sin_addr.s_addr, ntohs(addr->sin_port));
+ sk, addr->sin_addr.s_addr, ntohs(addr->sin_port));
chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);
if (addr->sin_addr.s_addr == htonl(INADDR_ANY))
@@ -280,9 +280,9 @@ static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
}
pr_debug("after bind(): num = %d, daddr = %pI4, dif = %d\n",
- (int)isk->inet_num,
- &isk->inet_rcv_saddr,
- (int)sk->sk_bound_dev_if);
+ (int)isk->inet_num,
+ &isk->inet_rcv_saddr,
+ (int)sk->sk_bound_dev_if);
err = 0;
if (isk->inet_rcv_saddr)
@@ -335,7 +335,7 @@ void ping_err(struct sk_buff *skb, u32 info)
return;
pr_debug("ping_err(type=%04x,code=%04x,id=%04x,seq=%04x)\n", type,
- code, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence));
+ code, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence));
sk = ping_v4_lookup(net, iph->daddr, iph->saddr,
ntohs(icmph->un.echo.id), skb->dev->ifindex);
@@ -556,7 +556,8 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
ipc.oif = inet->mc_index;
if (!saddr)
saddr = inet->mc_addr;
- }
+ } else if (!ipc.oif)
+ ipc.oif = inet->uc_index;
flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
RT_SCOPE_UNIVERSE, sk->sk_protocol,
@@ -630,6 +631,7 @@ static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
pr_debug("ping_recvmsg(sk=%p,sk->num=%u)\n", isk, isk->inet_num);
+ err = -EOPNOTSUPP;
if (flags & MSG_OOB)
goto out;
@@ -677,7 +679,7 @@ out:
static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n",
- inet_sk(sk), inet_sk(sk)->inet_num, skb);
+ inet_sk(sk), inet_sk(sk)->inet_num, skb);
if (sock_queue_rcv_skb(sk, skb) < 0) {
kfree_skb(skb);
pr_debug("ping_queue_rcv_skb -> failed\n");
@@ -703,7 +705,7 @@ void ping_rcv(struct sk_buff *skb)
/* We assume the packet has already been checked by icmp_rcv */
pr_debug("ping_rcv(skb=%p,id=%04x,seq=%04x)\n",
- skb, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence));
+ skb, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence));
/* Push ICMP header back */
skb_push(skb, skb->data - (u8 *)icmph);
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index 6afc807ee2a..02d61079f08 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -256,6 +256,7 @@ static const struct snmp_mib snmp4_net_list[] = {
SNMP_MIB_ITEM("TCPTimeWaitOverflow", LINUX_MIB_TCPTIMEWAITOVERFLOW),
SNMP_MIB_ITEM("TCPReqQFullDoCookies", LINUX_MIB_TCPREQQFULLDOCOOKIES),
SNMP_MIB_ITEM("TCPReqQFullDrop", LINUX_MIB_TCPREQQFULLDROP),
+ SNMP_MIB_ITEM("TCPRetransFail", LINUX_MIB_TCPRETRANSFAIL),
SNMP_MIB_SENTINEL
};
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 3ccda5ae8a2..bbd604c68e6 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -491,11 +491,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
if (msg->msg_namelen < sizeof(*usin))
goto out;
if (usin->sin_family != AF_INET) {
- static int complained;
- if (!complained++)
- printk(KERN_INFO "%s forgot to set AF_INET in "
- "raw sendmsg. Fix it!\n",
- current->comm);
+ pr_info_once("%s: %s forgot to set AF_INET. Fix it!\n",
+ __func__, current->comm);
err = -EAFNOSUPPORT;
if (usin->sin_family)
goto out;
@@ -563,7 +560,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
ipc.oif = inet->mc_index;
if (!saddr)
saddr = inet->mc_addr;
- }
+ } else if (!ipc.oif)
+ ipc.oif = inet->uc_index;
flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
RT_SCOPE_UNIVERSE,
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index bcacf54e541..12ccf880eb8 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -62,6 +62,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) "IPv4: " fmt
+
#include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -132,7 +134,6 @@ static int ip_rt_mtu_expires __read_mostly = 10 * 60 * HZ;
static int ip_rt_min_pmtu __read_mostly = 512 + 20 + 20;
static int ip_rt_min_advmss __read_mostly = 256;
static int rt_chain_length_max __read_mostly = 20;
-static int redirect_genid;
static struct delayed_work expires_work;
static unsigned long expires_ljiffies;
@@ -937,7 +938,7 @@ static void rt_cache_invalidate(struct net *net)
get_random_bytes(&shuffle, sizeof(shuffle));
atomic_add(shuffle + 1U, &net->ipv4.rt_genid);
- redirect_genid++;
+ inetpeer_invalidate_tree(AF_INET);
}
/*
@@ -960,7 +961,7 @@ void rt_cache_flush_batch(struct net *net)
static void rt_emergency_hash_rebuild(struct net *net)
{
if (net_ratelimit())
- printk(KERN_WARNING "Route hash chain too long!\n");
+ pr_warn("Route hash chain too long!\n");
rt_cache_invalidate(net);
}
@@ -1084,7 +1085,7 @@ static int rt_garbage_collect(struct dst_ops *ops)
if (dst_entries_get_slow(&ipv4_dst_ops) < ip_rt_max_size)
goto out;
if (net_ratelimit())
- printk(KERN_WARNING "dst cache overflow\n");
+ pr_warn("dst cache overflow\n");
RT_CACHE_STAT_INC(gc_dst_overflow);
return 1;
@@ -1117,12 +1118,17 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const vo
static const __be32 inaddr_any = 0;
struct net_device *dev = dst->dev;
const __be32 *pkey = daddr;
+ const struct rtable *rt;
struct neighbour *n;
+ rt = (const struct rtable *) dst;
+
if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
pkey = &inaddr_any;
+ else if (rt->rt_gateway)
+ pkey = (const __be32 *) &rt->rt_gateway;
- n = __ipv4_neigh_lookup(&arp_tbl, dev, *(__force u32 *)pkey);
+ n = __ipv4_neigh_lookup(dev, *(__force u32 *)pkey);
if (n)
return n;
return neigh_create(&arp_tbl, pkey, dev);
@@ -1177,8 +1183,7 @@ restart:
int err = rt_bind_neighbour(rt);
if (err) {
if (net_ratelimit())
- printk(KERN_WARNING
- "Neighbour table failure & not caching routes.\n");
+ pr_warn("Neighbour table failure & not caching routes\n");
ip_rt_put(rt);
return ERR_PTR(err);
}
@@ -1254,7 +1259,7 @@ restart:
struct net *net = dev_net(rt->dst.dev);
int num = ++net->ipv4.current_rt_cache_rebuild_count;
if (!rt_caching(net)) {
- printk(KERN_WARNING "%s: %d rebuilds is over limit, route caching disabled\n",
+ pr_warn("%s: %d rebuilds is over limit, route caching disabled\n",
rt->dst.dev->name, num);
}
rt_emergency_hash_rebuild(net);
@@ -1295,7 +1300,7 @@ restart:
}
if (net_ratelimit())
- printk(KERN_WARNING "ipv4: Neighbour table overflow.\n");
+ pr_warn("Neighbour table overflow\n");
rt_drop(rt);
return ERR_PTR(-ENOBUFS);
}
@@ -1485,10 +1490,8 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
peer = rt->peer;
if (peer) {
- if (peer->redirect_learned.a4 != new_gw ||
- peer->redirect_genid != redirect_genid) {
+ if (peer->redirect_learned.a4 != new_gw) {
peer->redirect_learned.a4 = new_gw;
- peer->redirect_genid = redirect_genid;
atomic_inc(&__rt_peer_genid);
}
check_peer_redir(&rt->dst, peer);
@@ -1501,10 +1504,10 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
reject_redirect:
#ifdef CONFIG_IP_ROUTE_VERBOSE
if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
- printk(KERN_INFO "Redirect from %pI4 on %s about %pI4 ignored.\n"
+ pr_info("Redirect from %pI4 on %s about %pI4 ignored\n"
" Advised path = %pI4 -> %pI4\n",
- &old_gw, dev->name, &new_gw,
- &saddr, &daddr);
+ &old_gw, dev->name, &new_gw,
+ &saddr, &daddr);
#endif
;
}
@@ -1616,8 +1619,8 @@ void ip_rt_send_redirect(struct sk_buff *skb)
if (log_martians &&
peer->rate_tokens == ip_rt_redirect_number &&
net_ratelimit())
- printk(KERN_WARNING "host %pI4/if%d ignores redirects for %pI4 to %pI4.\n",
- &ip_hdr(skb)->saddr, rt->rt_iif,
+ pr_warn("host %pI4/if%d ignores redirects for %pI4 to %pI4\n",
+ &ip_hdr(skb)->saddr, rt->rt_iif,
&rt->rt_dst, &rt->rt_gateway);
#endif
}
@@ -1793,8 +1796,6 @@ static void ipv4_validate_peer(struct rtable *rt)
if (peer) {
check_peer_pmtu(&rt->dst, peer);
- if (peer->redirect_genid != redirect_genid)
- peer->redirect_learned.a4 = 0;
if (peer->redirect_learned.a4 &&
peer->redirect_learned.a4 != rt->rt_gateway)
check_peer_redir(&rt->dst, peer);
@@ -1958,8 +1959,7 @@ static void rt_init_metrics(struct rtable *rt, const struct flowi4 *fl4,
dst_init_metrics(&rt->dst, peer->metrics, false);
check_peer_pmtu(&rt->dst, peer);
- if (peer->redirect_genid != redirect_genid)
- peer->redirect_learned.a4 = 0;
+
if (peer->redirect_learned.a4 &&
peer->redirect_learned.a4 != rt->rt_gateway) {
rt->rt_gateway = peer->redirect_learned.a4;
@@ -2106,18 +2106,13 @@ static void ip_handle_martian_source(struct net_device *dev,
* RFC1812 recommendation, if source is martian,
* the only hint is MAC header.
*/
- printk(KERN_WARNING "martian source %pI4 from %pI4, on dev %s\n",
+ pr_warn("martian source %pI4 from %pI4, on dev %s\n",
&daddr, &saddr, dev->name);
if (dev->hard_header_len && skb_mac_header_was_set(skb)) {
- int i;
- const unsigned char *p = skb_mac_header(skb);
- printk(KERN_WARNING "ll header: ");
- for (i = 0; i < dev->hard_header_len; i++, p++) {
- printk("%02x", *p);
- if (i < (dev->hard_header_len - 1))
- printk(":");
- }
- printk("\n");
+ print_hex_dump(KERN_WARNING, "ll header: ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ skb_mac_header(skb),
+ dev->hard_header_len, true);
}
}
#endif
@@ -2141,8 +2136,7 @@ static int __mkroute_input(struct sk_buff *skb,
out_dev = __in_dev_get_rcu(FIB_RES_DEV(*res));
if (out_dev == NULL) {
if (net_ratelimit())
- printk(KERN_CRIT "Bug in ip_route_input" \
- "_slow(). Please, report\n");
+ pr_crit("Bug in ip_route_input_slow(). Please report.\n");
return -EINVAL;
}
@@ -2414,7 +2408,7 @@ martian_destination:
RT_CACHE_STAT_INC(in_martian_dst);
#ifdef CONFIG_IP_ROUTE_VERBOSE
if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
- printk(KERN_WARNING "martian destination %pI4 from %pI4, dev %s\n",
+ pr_warn("martian destination %pI4 from %pI4, dev %s\n",
&daddr, &saddr, dev->name);
#endif
@@ -3491,7 +3485,7 @@ int __init ip_rt_init(void)
net_random() % ip_rt_gc_interval + ip_rt_gc_interval);
if (ip_rt_proc_init())
- printk(KERN_ERR "Unable to create route proc files\n");
+ pr_err("Unable to create route proc files\n");
#ifdef CONFIG_XFRM
xfrm_init();
xfrm4_init(ip_rt_max_size);
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 4cb9cd2f2c3..7a7724da9bf 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -778,7 +778,6 @@ EXPORT_SYMBOL_GPL(net_ipv4_ctl_path);
static __net_init int ipv4_sysctl_init_net(struct net *net)
{
struct ctl_table *table;
- unsigned long limit;
table = ipv4_net_table;
if (!net_eq(net, &init_net)) {
@@ -815,11 +814,6 @@ static __net_init int ipv4_sysctl_init_net(struct net *net)
net->ipv4.sysctl_rt_cache_rebuild_count = 4;
tcp_init_mem(net);
- limit = nr_free_buffer_pages() / 8;
- limit = max(limit, 128UL);
- net->ipv4.sysctl_tcp_mem[0] = limit / 4 * 3;
- net->ipv4.sysctl_tcp_mem[1] = limit;
- net->ipv4.sysctl_tcp_mem[2] = net->ipv4.sysctl_tcp_mem[0] * 2;
net->ipv4.ipv4_hdr = register_net_sysctl_table(net,
net_ipv4_ctl_path, table);
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 06373b4a449..cfd7edda0a8 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -245,6 +245,8 @@
* TCP_CLOSE socket is finished
*/
+#define pr_fmt(fmt) "TCP: " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -1675,7 +1677,8 @@ do_prequeue:
if (tp->ucopy.dma_cookie < 0) {
- printk(KERN_ALERT "dma_cookie < 0\n");
+ pr_alert("%s: dma_cookie < 0\n",
+ __func__);
/* Exception. Bailout! */
if (!copied)
@@ -1876,6 +1879,20 @@ void tcp_shutdown(struct sock *sk, int how)
}
EXPORT_SYMBOL(tcp_shutdown);
+bool tcp_check_oom(struct sock *sk, int shift)
+{
+ bool too_many_orphans, out_of_socket_memory;
+
+ too_many_orphans = tcp_too_many_orphans(sk, shift);
+ out_of_socket_memory = tcp_out_of_memory(sk);
+
+ if (too_many_orphans && net_ratelimit())
+ pr_info("too many orphaned sockets\n");
+ if (out_of_socket_memory && net_ratelimit())
+ pr_info("out of memory -- consider tuning tcp_mem\n");
+ return too_many_orphans || out_of_socket_memory;
+}
+
void tcp_close(struct sock *sk, long timeout)
{
struct sk_buff *skb;
@@ -2015,10 +2032,7 @@ adjudge_to_death:
}
if (sk->sk_state != TCP_CLOSE) {
sk_mem_reclaim(sk);
- if (tcp_too_many_orphans(sk, 0)) {
- if (net_ratelimit())
- printk(KERN_INFO "TCP: too many of orphaned "
- "sockets\n");
+ if (tcp_check_oom(sk, 0)) {
tcp_set_state(sk, TCP_CLOSE);
tcp_send_active_reset(sk, GFP_ATOMIC);
NET_INC_STATS_BH(sock_net(sk),
@@ -3218,7 +3232,6 @@ __setup("thash_entries=", set_thash_entries);
void tcp_init_mem(struct net *net)
{
- /* Set per-socket limits to no more than 1/128 the pressure threshold */
unsigned long limit = nr_free_buffer_pages() / 8;
limit = max(limit, 128UL);
net->ipv4.sysctl_tcp_mem[0] = limit / 4 * 3;
@@ -3230,7 +3243,8 @@ void __init tcp_init(void)
{
struct sk_buff *skb = NULL;
unsigned long limit;
- int i, max_share, cnt;
+ int max_share, cnt;
+ unsigned int i;
unsigned long jiffy = jiffies;
BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb));
@@ -3273,7 +3287,7 @@ void __init tcp_init(void)
&tcp_hashinfo.bhash_size,
NULL,
64 * 1024);
- tcp_hashinfo.bhash_size = 1 << tcp_hashinfo.bhash_size;
+ tcp_hashinfo.bhash_size = 1U << tcp_hashinfo.bhash_size;
for (i = 0; i < tcp_hashinfo.bhash_size; i++) {
spin_lock_init(&tcp_hashinfo.bhash[i].lock);
INIT_HLIST_HEAD(&tcp_hashinfo.bhash[i].chain);
@@ -3287,7 +3301,8 @@ void __init tcp_init(void)
sysctl_max_syn_backlog = max(128, cnt / 256);
tcp_init_mem(&init_net);
- limit = nr_free_buffer_pages() / 8;
+ /* Set per-socket limits to no more than 1/128 the pressure threshold */
+ limit = nr_free_buffer_pages() << (PAGE_SHIFT - 10);
limit = max(limit, 128UL);
max_share = min(4UL*1024*1024, limit);
@@ -3299,9 +3314,8 @@ void __init tcp_init(void)
sysctl_tcp_rmem[1] = 87380;
sysctl_tcp_rmem[2] = max(87380, max_share);
- printk(KERN_INFO "TCP: Hash tables configured "
- "(established %u bind %u)\n",
- tcp_hashinfo.ehash_mask + 1, tcp_hashinfo.bhash_size);
+ pr_info("Hash tables configured (established %u bind %u)\n",
+ tcp_hashinfo.ehash_mask + 1, tcp_hashinfo.bhash_size);
tcp_register_congestion_control(&tcp_reno);
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index fc6d475f488..272a84593c8 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -6,6 +6,8 @@
* Copyright (C) 2005 Stephen Hemminger <shemminger@osdl.org>
*/
+#define pr_fmt(fmt) "TCP: " fmt
+
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/types.h>
@@ -41,18 +43,17 @@ int tcp_register_congestion_control(struct tcp_congestion_ops *ca)
/* all algorithms must implement ssthresh and cong_avoid ops */
if (!ca->ssthresh || !ca->cong_avoid) {
- printk(KERN_ERR "TCP %s does not implement required ops\n",
- ca->name);
+ pr_err("%s does not implement required ops\n", ca->name);
return -EINVAL;
}
spin_lock(&tcp_cong_list_lock);
if (tcp_ca_find(ca->name)) {
- printk(KERN_NOTICE "TCP %s already registered\n", ca->name);
+ pr_notice("%s already registered\n", ca->name);
ret = -EEXIST;
} else {
list_add_tail_rcu(&ca->list, &tcp_cong_list);
- printk(KERN_INFO "TCP %s registered\n", ca->name);
+ pr_info("%s registered\n", ca->name);
}
spin_unlock(&tcp_cong_list_lock);
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 976034f8232..68d4057cba0 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -61,6 +61,8 @@
* Pasi Sarolahti: F-RTO for dealing with spurious RTOs
*/
+#define pr_fmt(fmt) "TCP: " fmt
+
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -1307,25 +1309,26 @@ static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb,
return in_sack;
}
-static u8 tcp_sacktag_one(const struct sk_buff *skb, struct sock *sk,
- struct tcp_sacktag_state *state,
+/* Mark the given newly-SACKed range as such, adjusting counters and hints. */
+static u8 tcp_sacktag_one(struct sock *sk,
+ struct tcp_sacktag_state *state, u8 sacked,
+ u32 start_seq, u32 end_seq,
int dup_sack, int pcount)
{
struct tcp_sock *tp = tcp_sk(sk);
- u8 sacked = TCP_SKB_CB(skb)->sacked;
int fack_count = state->fack_count;
/* Account D-SACK for retransmitted packet. */
if (dup_sack && (sacked & TCPCB_RETRANS)) {
if (tp->undo_marker && tp->undo_retrans &&
- after(TCP_SKB_CB(skb)->end_seq, tp->undo_marker))
+ after(end_seq, tp->undo_marker))
tp->undo_retrans--;
if (sacked & TCPCB_SACKED_ACKED)
state->reord = min(fack_count, state->reord);
}
/* Nothing to do; acked frame is about to be dropped (was ACKed). */
- if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una))
+ if (!after(end_seq, tp->snd_una))
return sacked;
if (!(sacked & TCPCB_SACKED_ACKED)) {
@@ -1344,13 +1347,13 @@ static u8 tcp_sacktag_one(const struct sk_buff *skb, struct sock *sk,
/* New sack for not retransmitted frame,
* which was in hole. It is reordering.
*/
- if (before(TCP_SKB_CB(skb)->seq,
+ if (before(start_seq,
tcp_highest_sack_seq(tp)))
state->reord = min(fack_count,
state->reord);
/* SACK enhanced F-RTO (RFC4138; Appendix B) */
- if (!after(TCP_SKB_CB(skb)->end_seq, tp->frto_highmark))
+ if (!after(end_seq, tp->frto_highmark))
state->flag |= FLAG_ONLY_ORIG_SACKED;
}
@@ -1368,8 +1371,7 @@ static u8 tcp_sacktag_one(const struct sk_buff *skb, struct sock *sk,
/* Lost marker hint past SACKed? Tweak RFC3517 cnt */
if (!tcp_is_fack(tp) && (tp->lost_skb_hint != NULL) &&
- before(TCP_SKB_CB(skb)->seq,
- TCP_SKB_CB(tp->lost_skb_hint)->seq))
+ before(start_seq, TCP_SKB_CB(tp->lost_skb_hint)->seq))
tp->lost_cnt_hint += pcount;
if (fack_count > tp->fackets_out)
@@ -1388,6 +1390,9 @@ static u8 tcp_sacktag_one(const struct sk_buff *skb, struct sock *sk,
return sacked;
}
+/* Shift newly-SACKed bytes from this skb to the immediately previous
+ * already-SACKed sk_buff. Mark the newly-SACKed bytes as such.
+ */
static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb,
struct tcp_sacktag_state *state,
unsigned int pcount, int shifted, int mss,
@@ -1395,9 +1400,20 @@ static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb,
{
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *prev = tcp_write_queue_prev(sk, skb);
+ u32 start_seq = TCP_SKB_CB(skb)->seq; /* start of newly-SACKed */
+ u32 end_seq = start_seq + shifted; /* end of newly-SACKed */
BUG_ON(!pcount);
+ /* Adjust counters and hints for the newly sacked sequence
+ * range but discard the return value since prev is already
+ * marked. We must tag the range first because the seq
+ * advancement below implicitly advances
+ * tcp_highest_sack_seq() when skb is highest_sack.
+ */
+ tcp_sacktag_one(sk, state, TCP_SKB_CB(skb)->sacked,
+ start_seq, end_seq, dup_sack, pcount);
+
if (skb == tp->lost_skb_hint)
tp->lost_cnt_hint += pcount;
@@ -1424,9 +1440,6 @@ static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb,
skb_shinfo(skb)->gso_type = 0;
}
- /* We discard results */
- tcp_sacktag_one(skb, sk, state, dup_sack, pcount);
-
/* Difference in this won't matter, both ACKed by the same cumul. ACK */
TCP_SKB_CB(prev)->sacked |= (TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS);
@@ -1574,6 +1587,10 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb,
}
}
+ /* tcp_sacktag_one() won't SACK-tag ranges below snd_una */
+ if (!after(TCP_SKB_CB(skb)->seq + len, tp->snd_una))
+ goto fallback;
+
if (!skb_shift(prev, skb, len))
goto fallback;
if (!tcp_shifted_skb(sk, skb, state, pcount, len, mss, dup_sack))
@@ -1664,10 +1681,14 @@ static struct sk_buff *tcp_sacktag_walk(struct sk_buff *skb, struct sock *sk,
break;
if (in_sack) {
- TCP_SKB_CB(skb)->sacked = tcp_sacktag_one(skb, sk,
- state,
- dup_sack,
- tcp_skb_pcount(skb));
+ TCP_SKB_CB(skb)->sacked =
+ tcp_sacktag_one(sk,
+ state,
+ TCP_SKB_CB(skb)->sacked,
+ TCP_SKB_CB(skb)->seq,
+ TCP_SKB_CB(skb)->end_seq,
+ dup_sack,
+ tcp_skb_pcount(skb));
if (!before(TCP_SKB_CB(skb)->seq,
tcp_highest_sack_seq(tp)))
@@ -2554,6 +2575,7 @@ static void tcp_mark_head_lost(struct sock *sk, int packets, int mark_head)
if (cnt > packets) {
if ((tcp_is_sack(tp) && !tcp_is_fack(tp)) ||
+ (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED) ||
(oldcnt >= packets))
break;
@@ -3847,9 +3869,9 @@ void tcp_parse_options(const struct sk_buff *skb, struct tcp_options_received *o
opt_rx->wscale_ok = 1;
if (snd_wscale > 14) {
if (net_ratelimit())
- printk(KERN_INFO "tcp_parse_options: Illegal window "
- "scaling value %d >14 received.\n",
- snd_wscale);
+ pr_info("%s: Illegal window scaling value %d >14 received\n",
+ __func__,
+ snd_wscale);
snd_wscale = 14;
}
opt_rx->snd_wscale = snd_wscale;
@@ -4171,7 +4193,7 @@ static void tcp_fin(struct sock *sk)
/* Only TCP_LISTEN and TCP_CLOSE are left, in these
* cases we should never reach this piece of code.
*/
- printk(KERN_ERR "%s: Impossible, sk->sk_state=%d\n",
+ pr_err("%s: Impossible, sk->sk_state=%d\n",
__func__, sk->sk_state);
break;
}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 337ba4cca05..fe9f604ed1e 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -50,6 +50,7 @@
* a single port at the same time.
*/
+#define pr_fmt(fmt) "TCP: " fmt
#include <linux/bottom_half.h>
#include <linux/types.h>
@@ -90,16 +91,8 @@ EXPORT_SYMBOL(sysctl_tcp_low_latency);
#ifdef CONFIG_TCP_MD5SIG
-static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk,
- __be32 addr);
-static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
+static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
__be32 daddr, __be32 saddr, const struct tcphdr *th);
-#else
-static inline
-struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
-{
- return NULL;
-}
#endif
struct inet_hashinfo tcp_hashinfo;
@@ -601,6 +594,10 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
struct ip_reply_arg arg;
#ifdef CONFIG_TCP_MD5SIG
struct tcp_md5sig_key *key;
+ const __u8 *hash_location = NULL;
+ unsigned char newhash[16];
+ int genhash;
+ struct sock *sk1 = NULL;
#endif
struct net *net;
@@ -631,7 +628,36 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
arg.iov[0].iov_len = sizeof(rep.th);
#ifdef CONFIG_TCP_MD5SIG
- key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->saddr) : NULL;
+ hash_location = tcp_parse_md5sig_option(th);
+ if (!sk && hash_location) {
+ /*
+ * active side is lost. Try to find listening socket through
+ * source port, and then find md5 key through listening socket.
+ * we are not loose security here:
+ * Incoming packet is checked with md5 hash with finding key,
+ * no RST generated if md5 hash doesn't match.
+ */
+ sk1 = __inet_lookup_listener(dev_net(skb_dst(skb)->dev),
+ &tcp_hashinfo, ip_hdr(skb)->daddr,
+ ntohs(th->source), inet_iif(skb));
+ /* don't send rst if it can't find key */
+ if (!sk1)
+ return;
+ rcu_read_lock();
+ key = tcp_md5_do_lookup(sk1, (union tcp_md5_addr *)
+ &ip_hdr(skb)->saddr, AF_INET);
+ if (!key)
+ goto release_sk1;
+
+ genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, NULL, skb);
+ if (genhash || memcmp(hash_location, newhash, 16) != 0)
+ goto release_sk1;
+ } else {
+ key = sk ? tcp_md5_do_lookup(sk, (union tcp_md5_addr *)
+ &ip_hdr(skb)->saddr,
+ AF_INET) : NULL;
+ }
+
if (key) {
rep.opt[0] = htonl((TCPOPT_NOP << 24) |
(TCPOPT_NOP << 16) |
@@ -651,6 +677,11 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
arg.iov[0].iov_len, IPPROTO_TCP, 0);
arg.csumoffset = offsetof(struct tcphdr, check) / 2;
arg.flags = (sk && inet_sk(sk)->transparent) ? IP_REPLY_ARG_NOSRCCHECK : 0;
+ /* When socket is gone, all binding information is lost.
+ * routing might fail in this case. using iif for oif to
+ * make sure we can deliver it
+ */
+ arg.bound_dev_if = sk ? sk->sk_bound_dev_if : inet_iif(skb);
net = dev_net(skb_dst(skb)->dev);
arg.tos = ip_hdr(skb)->tos;
@@ -659,6 +690,14 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);
TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS);
+
+#ifdef CONFIG_TCP_MD5SIG
+release_sk1:
+ if (sk1) {
+ rcu_read_unlock();
+ sock_put(sk1);
+ }
+#endif
}
/* The code following below sending ACKs in SYN-RECV and TIME-WAIT states
@@ -759,7 +798,8 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd,
req->ts_recent,
0,
- tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr),
+ tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->daddr,
+ AF_INET),
inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0,
ip_hdr(skb)->tos);
}
@@ -837,8 +877,7 @@ int tcp_syn_flood_action(struct sock *sk,
lopt = inet_csk(sk)->icsk_accept_queue.listen_opt;
if (!lopt->synflood_warned) {
lopt->synflood_warned = 1;
- pr_info("%s: Possible SYN flooding on port %d. %s. "
- " Check SNMP counters.\n",
+ pr_info("%s: Possible SYN flooding on port %d. %s. Check SNMP counters.\n",
proto, ntohs(tcp_hdr(skb)->dest), msg);
}
return want_cookie;
@@ -876,153 +915,138 @@ static struct ip_options_rcu *tcp_v4_save_options(struct sock *sk,
*/
/* Find the Key structure for an address. */
-static struct tcp_md5sig_key *
- tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
+struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
+ const union tcp_md5_addr *addr,
+ int family)
{
struct tcp_sock *tp = tcp_sk(sk);
- int i;
-
- if (!tp->md5sig_info || !tp->md5sig_info->entries4)
+ struct tcp_md5sig_key *key;
+ struct hlist_node *pos;
+ unsigned int size = sizeof(struct in_addr);
+ struct tcp_md5sig_info *md5sig;
+
+ /* caller either holds rcu_read_lock() or socket lock */
+ md5sig = rcu_dereference_check(tp->md5sig_info,
+ sock_owned_by_user(sk) ||
+ lockdep_is_held(&sk->sk_lock.slock));
+ if (!md5sig)
return NULL;
- for (i = 0; i < tp->md5sig_info->entries4; i++) {
- if (tp->md5sig_info->keys4[i].addr == addr)
- return &tp->md5sig_info->keys4[i].base;
+#if IS_ENABLED(CONFIG_IPV6)
+ if (family == AF_INET6)
+ size = sizeof(struct in6_addr);
+#endif
+ hlist_for_each_entry_rcu(key, pos, &md5sig->head, node) {
+ if (key->family != family)
+ continue;
+ if (!memcmp(&key->addr, addr, size))
+ return key;
}
return NULL;
}
+EXPORT_SYMBOL(tcp_md5_do_lookup);
struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
struct sock *addr_sk)
{
- return tcp_v4_md5_do_lookup(sk, inet_sk(addr_sk)->inet_daddr);
+ union tcp_md5_addr *addr;
+
+ addr = (union tcp_md5_addr *)&inet_sk(addr_sk)->inet_daddr;
+ return tcp_md5_do_lookup(sk, addr, AF_INET);
}
EXPORT_SYMBOL(tcp_v4_md5_lookup);
static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk,
struct request_sock *req)
{
- return tcp_v4_md5_do_lookup(sk, inet_rsk(req)->rmt_addr);
+ union tcp_md5_addr *addr;
+
+ addr = (union tcp_md5_addr *)&inet_rsk(req)->rmt_addr;
+ return tcp_md5_do_lookup(sk, addr, AF_INET);
}
/* This can be called on a newly created socket, from other files */
-int tcp_v4_md5_do_add(struct sock *sk, __be32 addr,
- u8 *newkey, u8 newkeylen)
+int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
+ int family, const u8 *newkey, u8 newkeylen, gfp_t gfp)
{
/* Add Key to the list */
struct tcp_md5sig_key *key;
struct tcp_sock *tp = tcp_sk(sk);
- struct tcp4_md5sig_key *keys;
+ struct tcp_md5sig_info *md5sig;
- key = tcp_v4_md5_do_lookup(sk, addr);
+ key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET);
if (key) {
/* Pre-existing entry - just update that one. */
- kfree(key->key);
- key->key = newkey;
+ memcpy(key->key, newkey, newkeylen);
key->keylen = newkeylen;
- } else {
- struct tcp_md5sig_info *md5sig;
-
- if (!tp->md5sig_info) {
- tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info),
- GFP_ATOMIC);
- if (!tp->md5sig_info) {
- kfree(newkey);
- return -ENOMEM;
- }
- sk_nocaps_add(sk, NETIF_F_GSO_MASK);
- }
+ return 0;
+ }
- md5sig = tp->md5sig_info;
- if (md5sig->entries4 == 0 &&
- tcp_alloc_md5sig_pool(sk) == NULL) {
- kfree(newkey);
+ md5sig = rcu_dereference_protected(tp->md5sig_info,
+ sock_owned_by_user(sk));
+ if (!md5sig) {
+ md5sig = kmalloc(sizeof(*md5sig), gfp);
+ if (!md5sig)
return -ENOMEM;
- }
-
- if (md5sig->alloced4 == md5sig->entries4) {
- keys = kmalloc((sizeof(*keys) *
- (md5sig->entries4 + 1)), GFP_ATOMIC);
- if (!keys) {
- kfree(newkey);
- if (md5sig->entries4 == 0)
- tcp_free_md5sig_pool();
- return -ENOMEM;
- }
- if (md5sig->entries4)
- memcpy(keys, md5sig->keys4,
- sizeof(*keys) * md5sig->entries4);
+ sk_nocaps_add(sk, NETIF_F_GSO_MASK);
+ INIT_HLIST_HEAD(&md5sig->head);
+ rcu_assign_pointer(tp->md5sig_info, md5sig);
+ }
- /* Free old key list, and reference new one */
- kfree(md5sig->keys4);
- md5sig->keys4 = keys;
- md5sig->alloced4++;
- }
- md5sig->entries4++;
- md5sig->keys4[md5sig->entries4 - 1].addr = addr;
- md5sig->keys4[md5sig->entries4 - 1].base.key = newkey;
- md5sig->keys4[md5sig->entries4 - 1].base.keylen = newkeylen;
+ key = sock_kmalloc(sk, sizeof(*key), gfp);
+ if (!key)
+ return -ENOMEM;
+ if (hlist_empty(&md5sig->head) && !tcp_alloc_md5sig_pool(sk)) {
+ sock_kfree_s(sk, key, sizeof(*key));
+ return -ENOMEM;
}
- return 0;
-}
-EXPORT_SYMBOL(tcp_v4_md5_do_add);
-static int tcp_v4_md5_add_func(struct sock *sk, struct sock *addr_sk,
- u8 *newkey, u8 newkeylen)
-{
- return tcp_v4_md5_do_add(sk, inet_sk(addr_sk)->inet_daddr,
- newkey, newkeylen);
+ memcpy(key->key, newkey, newkeylen);
+ key->keylen = newkeylen;
+ key->family = family;
+ memcpy(&key->addr, addr,
+ (family == AF_INET6) ? sizeof(struct in6_addr) :
+ sizeof(struct in_addr));
+ hlist_add_head_rcu(&key->node, &md5sig->head);
+ return 0;
}
+EXPORT_SYMBOL(tcp_md5_do_add);
-int tcp_v4_md5_do_del(struct sock *sk, __be32 addr)
+int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family)
{
struct tcp_sock *tp = tcp_sk(sk);
- int i;
-
- for (i = 0; i < tp->md5sig_info->entries4; i++) {
- if (tp->md5sig_info->keys4[i].addr == addr) {
- /* Free the key */
- kfree(tp->md5sig_info->keys4[i].base.key);
- tp->md5sig_info->entries4--;
-
- if (tp->md5sig_info->entries4 == 0) {
- kfree(tp->md5sig_info->keys4);
- tp->md5sig_info->keys4 = NULL;
- tp->md5sig_info->alloced4 = 0;
- tcp_free_md5sig_pool();
- } else if (tp->md5sig_info->entries4 != i) {
- /* Need to do some manipulation */
- memmove(&tp->md5sig_info->keys4[i],
- &tp->md5sig_info->keys4[i+1],
- (tp->md5sig_info->entries4 - i) *
- sizeof(struct tcp4_md5sig_key));
- }
- return 0;
- }
- }
- return -ENOENT;
+ struct tcp_md5sig_key *key;
+ struct tcp_md5sig_info *md5sig;
+
+ key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET);
+ if (!key)
+ return -ENOENT;
+ hlist_del_rcu(&key->node);
+ atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
+ kfree_rcu(key, rcu);
+ md5sig = rcu_dereference_protected(tp->md5sig_info,
+ sock_owned_by_user(sk));
+ if (hlist_empty(&md5sig->head))
+ tcp_free_md5sig_pool();
+ return 0;
}
-EXPORT_SYMBOL(tcp_v4_md5_do_del);
+EXPORT_SYMBOL(tcp_md5_do_del);
-static void tcp_v4_clear_md5_list(struct sock *sk)
+void tcp_clear_md5_list(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
+ struct tcp_md5sig_key *key;
+ struct hlist_node *pos, *n;
+ struct tcp_md5sig_info *md5sig;
- /* Free each key, then the set of key keys,
- * the crypto element, and then decrement our
- * hold on the last resort crypto.
- */
- if (tp->md5sig_info->entries4) {
- int i;
- for (i = 0; i < tp->md5sig_info->entries4; i++)
- kfree(tp->md5sig_info->keys4[i].base.key);
- tp->md5sig_info->entries4 = 0;
+ md5sig = rcu_dereference_protected(tp->md5sig_info, 1);
+
+ if (!hlist_empty(&md5sig->head))
tcp_free_md5sig_pool();
- }
- if (tp->md5sig_info->keys4) {
- kfree(tp->md5sig_info->keys4);
- tp->md5sig_info->keys4 = NULL;
- tp->md5sig_info->alloced4 = 0;
+ hlist_for_each_entry_safe(key, pos, n, &md5sig->head, node) {
+ hlist_del_rcu(&key->node);
+ atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
+ kfree_rcu(key, rcu);
}
}
@@ -1031,7 +1055,6 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
{
struct tcp_md5sig cmd;
struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpm_addr;
- u8 *newkey;
if (optlen < sizeof(cmd))
return -EINVAL;
@@ -1042,32 +1065,16 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
if (sin->sin_family != AF_INET)
return -EINVAL;
- if (!cmd.tcpm_key || !cmd.tcpm_keylen) {
- if (!tcp_sk(sk)->md5sig_info)
- return -ENOENT;
- return tcp_v4_md5_do_del(sk, sin->sin_addr.s_addr);
- }
+ if (!cmd.tcpm_key || !cmd.tcpm_keylen)
+ return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr,
+ AF_INET);
if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
return -EINVAL;
- if (!tcp_sk(sk)->md5sig_info) {
- struct tcp_sock *tp = tcp_sk(sk);
- struct tcp_md5sig_info *p;
-
- p = kzalloc(sizeof(*p), sk->sk_allocation);
- if (!p)
- return -EINVAL;
-
- tp->md5sig_info = p;
- sk_nocaps_add(sk, NETIF_F_GSO_MASK);
- }
-
- newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, sk->sk_allocation);
- if (!newkey)
- return -ENOMEM;
- return tcp_v4_md5_do_add(sk, sin->sin_addr.s_addr,
- newkey, cmd.tcpm_keylen);
+ return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr,
+ AF_INET, cmd.tcpm_key, cmd.tcpm_keylen,
+ GFP_KERNEL);
}
static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
@@ -1093,7 +1100,7 @@ static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
return crypto_hash_update(&hp->md5_desc, &sg, sizeof(*bp));
}
-static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
+static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
__be32 daddr, __be32 saddr, const struct tcphdr *th)
{
struct tcp_md5sig_pool *hp;
@@ -1193,7 +1200,8 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
int genhash;
unsigned char newhash[16];
- hash_expected = tcp_v4_md5_do_lookup(sk, iph->saddr);
+ hash_expected = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&iph->saddr,
+ AF_INET);
hash_location = tcp_parse_md5sig_option(th);
/* We've parsed the options - do we have a hash? */
@@ -1219,10 +1227,10 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
if (genhash || memcmp(hash_location, newhash, 16) != 0) {
if (net_ratelimit()) {
- printk(KERN_INFO "MD5 Hash failed for (%pI4, %d)->(%pI4, %d)%s\n",
- &iph->saddr, ntohs(th->source),
- &iph->daddr, ntohs(th->dest),
- genhash ? " tcp_v4_calc_md5_hash failed" : "");
+ pr_info("MD5 Hash failed for (%pI4, %d)->(%pI4, %d)%s\n",
+ &iph->saddr, ntohs(th->source),
+ &iph->daddr, ntohs(th->dest),
+ genhash ? " tcp_v4_calc_md5_hash failed" : "");
}
return 1;
}
@@ -1391,7 +1399,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
* to destinations, already remembered
* to the moment of synflood.
*/
- LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open request from %pI4/%u\n",
+ LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"),
&saddr, ntohs(tcp_hdr(skb)->source));
goto drop_and_release;
}
@@ -1456,6 +1464,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
ireq->opt = NULL;
newinet->mc_index = inet_iif(skb);
newinet->mc_ttl = ip_hdr(skb)->ttl;
+ newinet->rcv_tos = ip_hdr(skb)->tos;
inet_csk(newsk)->icsk_ext_hdr_len = 0;
if (inet_opt)
inet_csk(newsk)->icsk_ext_hdr_len = inet_opt->opt.optlen;
@@ -1481,7 +1490,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
#ifdef CONFIG_TCP_MD5SIG
/* Copy over the MD5 key from the original socket */
- key = tcp_v4_md5_do_lookup(sk, newinet->inet_daddr);
+ key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&newinet->inet_daddr,
+ AF_INET);
if (key != NULL) {
/*
* We're using one, so create a matching key
@@ -1489,10 +1499,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
* memory, then we end up not copying the key
* across. Shucks.
*/
- char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC);
- if (newkey != NULL)
- tcp_v4_md5_do_add(newsk, newinet->inet_daddr,
- newkey, key->keylen);
+ tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newinet->inet_daddr,
+ AF_INET, key->key, key->keylen, GFP_ATOMIC);
sk_nocaps_add(newsk, NETIF_F_GSO_MASK);
}
#endif
@@ -1853,7 +1861,6 @@ EXPORT_SYMBOL(ipv4_specific);
static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
.md5_lookup = tcp_v4_md5_lookup,
.calc_md5_hash = tcp_v4_md5_hash_skb,
- .md5_add = tcp_v4_md5_add_func,
.md5_parse = tcp_v4_parse_md5_keys,
};
#endif
@@ -1942,8 +1949,8 @@ void tcp_v4_destroy_sock(struct sock *sk)
#ifdef CONFIG_TCP_MD5SIG
/* Clean up the MD5 key list, if any */
if (tp->md5sig_info) {
- tcp_v4_clear_md5_list(sk);
- kfree(tp->md5sig_info);
+ tcp_clear_md5_list(sk);
+ kfree_rcu(tp->md5sig_info, rcu);
tp->md5sig_info = NULL;
}
#endif
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 550e755747e..3cabafb5cdd 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -359,13 +359,11 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
*/
do {
struct tcp_md5sig_key *key;
- memset(tcptw->tw_md5_key, 0, sizeof(tcptw->tw_md5_key));
- tcptw->tw_md5_keylen = 0;
+ tcptw->tw_md5_key = NULL;
key = tp->af_specific->md5_lookup(sk, sk);
if (key != NULL) {
- memcpy(&tcptw->tw_md5_key, key->key, key->keylen);
- tcptw->tw_md5_keylen = key->keylen;
- if (tcp_alloc_md5sig_pool(sk) == NULL)
+ tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC);
+ if (tcptw->tw_md5_key && tcp_alloc_md5sig_pool(sk) == NULL)
BUG();
}
} while (0);
@@ -405,8 +403,10 @@ void tcp_twsk_destructor(struct sock *sk)
{
#ifdef CONFIG_TCP_MD5SIG
struct tcp_timewait_sock *twsk = tcp_twsk(sk);
- if (twsk->tw_md5_keylen)
+ if (twsk->tw_md5_key) {
tcp_free_md5sig_pool();
+ kfree_rcu(twsk->tw_md5_key, rcu);
+ }
#endif
}
EXPORT_SYMBOL_GPL(tcp_twsk_destructor);
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 4ff3b6dc74f..364784a9193 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2306,8 +2306,10 @@ begin_fwd:
if (sacked & (TCPCB_SACKED_ACKED|TCPCB_SACKED_RETRANS))
continue;
- if (tcp_retransmit_skb(sk, skb))
+ if (tcp_retransmit_skb(sk, skb)) {
+ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL);
return;
+ }
NET_INC_STATS_BH(sock_net(sk), mib_idx);
if (inet_csk(sk)->icsk_ca_state == TCP_CA_Recovery)
diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c
index 85ee7eb7e38..a981cdc0a6e 100644
--- a/net/ipv4/tcp_probe.c
+++ b/net/ipv4/tcp_probe.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/socket.h>
@@ -239,7 +241,7 @@ static __init int tcpprobe_init(void)
if (ret)
goto err1;
- pr_info("TCP probe registered (port=%d) bufsize=%u\n", port, bufsize);
+ pr_info("probe registered (port=%d) bufsize=%u\n", port, bufsize);
return 0;
err1:
proc_net_remove(&init_net, procname);
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index a516d1e399d..34d4a02c2f1 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -77,10 +77,7 @@ static int tcp_out_of_resources(struct sock *sk, int do_reset)
if (sk->sk_err_soft)
shift++;
- if (tcp_too_many_orphans(sk, shift)) {
- if (net_ratelimit())
- printk(KERN_INFO "Out of socket memory\n");
-
+ if (tcp_check_oom(sk, shift)) {
/* Catch exceptional cases, when connection requires reset.
* 1. Last segment was sent recently. */
if ((s32)(tcp_time_stamp - tp->lsndtime) <= TCP_TIMEWAIT_LEN ||
@@ -336,16 +333,18 @@ void tcp_retransmit_timer(struct sock *sk)
*/
struct inet_sock *inet = inet_sk(sk);
if (sk->sk_family == AF_INET) {
- LIMIT_NETDEBUG(KERN_DEBUG "TCP: Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n",
- &inet->inet_daddr, ntohs(inet->inet_dport),
- inet->inet_num, tp->snd_una, tp->snd_nxt);
+ LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n"),
+ &inet->inet_daddr,
+ ntohs(inet->inet_dport), inet->inet_num,
+ tp->snd_una, tp->snd_nxt);
}
#if IS_ENABLED(CONFIG_IPV6)
else if (sk->sk_family == AF_INET6) {
struct ipv6_pinfo *np = inet6_sk(sk);
- LIMIT_NETDEBUG(KERN_DEBUG "TCP: Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n",
- &np->daddr, ntohs(inet->inet_dport),
- inet->inet_num, tp->snd_una, tp->snd_nxt);
+ LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n"),
+ &np->daddr,
+ ntohs(inet->inet_dport), inet->inet_num,
+ tp->snd_una, tp->snd_nxt);
}
#endif
if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) {
diff --git a/net/ipv4/tunnel4.c b/net/ipv4/tunnel4.c
index 01775983b99..0d017183062 100644
--- a/net/ipv4/tunnel4.c
+++ b/net/ipv4/tunnel4.c
@@ -164,12 +164,12 @@ static const struct net_protocol tunnel64_protocol = {
static int __init tunnel4_init(void)
{
if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP)) {
- printk(KERN_ERR "tunnel4 init: can't add protocol\n");
+ pr_err("%s: can't add protocol\n", __func__);
return -EAGAIN;
}
#if IS_ENABLED(CONFIG_IPV6)
if (inet_add_protocol(&tunnel64_protocol, IPPROTO_IPV6)) {
- printk(KERN_ERR "tunnel64 init: can't add protocol\n");
+ pr_err("tunnel64 init: can't add protocol\n");
inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP);
return -EAGAIN;
}
@@ -181,10 +181,10 @@ static void __exit tunnel4_fini(void)
{
#if IS_ENABLED(CONFIG_IPV6)
if (inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6))
- printk(KERN_ERR "tunnel64 close: can't remove protocol\n");
+ pr_err("tunnel64 close: can't remove protocol\n");
#endif
if (inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP))
- printk(KERN_ERR "tunnel4 close: can't remove protocol\n");
+ pr_err("tunnel4 close: can't remove protocol\n");
}
module_init(tunnel4_init);
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 5d075b5f70f..d6f5feeb3ea 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -77,6 +77,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) "UDP: " fmt
+
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
@@ -917,7 +919,8 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
if (!saddr)
saddr = inet->mc_addr;
connected = 0;
- }
+ } else if (!ipc.oif)
+ ipc.oif = inet->uc_index;
if (connected)
rt = (struct rtable *)sk_dst_check(sk, 0);
@@ -974,7 +977,7 @@ back_from_confirm:
/* ... which is an evident application bug. --ANK */
release_sock(sk);
- LIMIT_NETDEBUG(KERN_DEBUG "udp cork app bug 2\n");
+ LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("cork app bug 2\n"));
err = -EINVAL;
goto out;
}
@@ -1053,7 +1056,7 @@ int udp_sendpage(struct sock *sk, struct page *page, int offset,
if (unlikely(!up->pending)) {
release_sock(sk);
- LIMIT_NETDEBUG(KERN_DEBUG "udp cork app bug 3\n");
+ LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("udp cork app bug 3\n"));
return -EINVAL;
}
@@ -1166,7 +1169,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
struct sk_buff *skb;
unsigned int ulen, copied;
- int peeked;
+ int peeked, off = 0;
int err;
int is_udplite = IS_UDPLITE(sk);
bool slow;
@@ -1182,7 +1185,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
try_again:
skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
- &peeked, &err);
+ &peeked, &off, &err);
if (!skb)
goto out;
@@ -1446,9 +1449,8 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
* provided by the application."
*/
if (up->pcrlen == 0) { /* full coverage was set */
- LIMIT_NETDEBUG(KERN_WARNING "UDPLITE: partial coverage "
- "%d while full coverage %d requested\n",
- UDP_SKB_CB(skb)->cscov, skb->len);
+ LIMIT_NETDEBUG(KERN_WARNING "UDPLite: partial coverage %d while full coverage %d requested\n",
+ UDP_SKB_CB(skb)->cscov, skb->len);
goto drop;
}
/* The next case involves violating the min. coverage requested
@@ -1458,9 +1460,8 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
* Therefore the above ...()->partial_cov statement is essential.
*/
if (UDP_SKB_CB(skb)->cscov < up->pcrlen) {
- LIMIT_NETDEBUG(KERN_WARNING
- "UDPLITE: coverage %d too small, need min %d\n",
- UDP_SKB_CB(skb)->cscov, up->pcrlen);
+ LIMIT_NETDEBUG(KERN_WARNING "UDPLite: coverage %d too small, need min %d\n",
+ UDP_SKB_CB(skb)->cscov, up->pcrlen);
goto drop;
}
}
@@ -1688,13 +1689,10 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
short_packet:
LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From %pI4:%u %d/%d to %pI4:%u\n",
- proto == IPPROTO_UDPLITE ? "-Lite" : "",
- &saddr,
- ntohs(uh->source),
- ulen,
- skb->len,
- &daddr,
- ntohs(uh->dest));
+ proto == IPPROTO_UDPLITE ? "Lite" : "",
+ &saddr, ntohs(uh->source),
+ ulen, skb->len,
+ &daddr, ntohs(uh->dest));
goto drop;
csum_error:
@@ -1703,11 +1701,8 @@ csum_error:
* the network is concerned, anyway) as per 4.1.3.4 (MUST).
*/
LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From %pI4:%u to %pI4:%u ulen %d\n",
- proto == IPPROTO_UDPLITE ? "-Lite" : "",
- &saddr,
- ntohs(uh->source),
- &daddr,
- ntohs(uh->dest),
+ proto == IPPROTO_UDPLITE ? "Lite" : "",
+ &saddr, ntohs(uh->source), &daddr, ntohs(uh->dest),
ulen);
drop:
UDP_INC_STATS_BH(net, UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE);
diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c
index 12e9499a1a6..2c46acd4cc3 100644
--- a/net/ipv4/udplite.c
+++ b/net/ipv4/udplite.c
@@ -10,6 +10,9 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
+
+#define pr_fmt(fmt) "UDPLite: " fmt
+
#include <linux/export.h>
#include "udp_impl.h"
@@ -129,11 +132,11 @@ void __init udplite4_register(void)
inet_register_protosw(&udplite4_protosw);
if (udplite4_proc_init())
- printk(KERN_ERR "%s: Cannot register /proc!\n", __func__);
+ pr_err("%s: Cannot register /proc!\n", __func__);
return;
out_unregister_proto:
proto_unregister(&udplite_prot);
out_register_err:
- printk(KERN_CRIT "%s: Cannot add UDP-Lite protocol.\n", __func__);
+ pr_crit("%s: Cannot add UDP-Lite protocol\n", __func__);
}
diff --git a/net/ipv4/xfrm4_mode_beet.c b/net/ipv4/xfrm4_mode_beet.c
index 63418185f52..e3db3f91511 100644
--- a/net/ipv4/xfrm4_mode_beet.c
+++ b/net/ipv4/xfrm4_mode_beet.c
@@ -110,10 +110,7 @@ static int xfrm4_beet_input(struct xfrm_state *x, struct sk_buff *skb)
skb_push(skb, sizeof(*iph));
skb_reset_network_header(skb);
-
- memmove(skb->data - skb->mac_len, skb_mac_header(skb),
- skb->mac_len);
- skb_set_mac_header(skb, -skb->mac_len);
+ skb_mac_header_rebuild(skb);
xfrm4_beet_make_header(skb);
diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c
index 534972e114a..ed4bf11ef9f 100644
--- a/net/ipv4/xfrm4_mode_tunnel.c
+++ b/net/ipv4/xfrm4_mode_tunnel.c
@@ -66,7 +66,6 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
{
- const unsigned char *old_mac;
int err = -EINVAL;
if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP)
@@ -84,10 +83,9 @@ static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
if (!(x->props.flags & XFRM_STATE_NOECN))
ipip_ecn_decapsulate(skb);
- old_mac = skb_mac_header(skb);
- skb_set_mac_header(skb, -skb->mac_len);
- memmove(skb_mac_header(skb), old_mac, skb->mac_len);
skb_reset_network_header(skb);
+ skb_mac_header_rebuild(skb);
+
err = 0;
out:
diff --git a/net/ipv4/xfrm4_tunnel.c b/net/ipv4/xfrm4_tunnel.c
index 9247d9d70e9..05a5df2febc 100644
--- a/net/ipv4/xfrm4_tunnel.c
+++ b/net/ipv4/xfrm4_tunnel.c
@@ -3,6 +3,8 @@
* Copyright (C) 2003 David S. Miller (davem@redhat.com)
*/
+#define pr_fmt(fmt) "IPsec: " fmt
+
#include <linux/skbuff.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -75,18 +77,18 @@ static struct xfrm_tunnel xfrm64_tunnel_handler __read_mostly = {
static int __init ipip_init(void)
{
if (xfrm_register_type(&ipip_type, AF_INET) < 0) {
- printk(KERN_INFO "ipip init: can't add xfrm type\n");
+ pr_info("%s: can't add xfrm type\n", __func__);
return -EAGAIN;
}
if (xfrm4_tunnel_register(&xfrm_tunnel_handler, AF_INET)) {
- printk(KERN_INFO "ipip init: can't add xfrm handler for AF_INET\n");
+ pr_info("%s: can't add xfrm handler for AF_INET\n", __func__);
xfrm_unregister_type(&ipip_type, AF_INET);
return -EAGAIN;
}
#if IS_ENABLED(CONFIG_IPV6)
if (xfrm4_tunnel_register(&xfrm64_tunnel_handler, AF_INET6)) {
- printk(KERN_INFO "ipip init: can't add xfrm handler for AF_INET6\n");
+ pr_info("%s: can't add xfrm handler for AF_INET6\n", __func__);
xfrm4_tunnel_deregister(&xfrm_tunnel_handler, AF_INET);
xfrm_unregister_type(&ipip_type, AF_INET);
return -EAGAIN;
@@ -99,12 +101,14 @@ static void __exit ipip_fini(void)
{
#if IS_ENABLED(CONFIG_IPV6)
if (xfrm4_tunnel_deregister(&xfrm64_tunnel_handler, AF_INET6))
- printk(KERN_INFO "ipip close: can't remove xfrm handler for AF_INET6\n");
+ pr_info("%s: can't remove xfrm handler for AF_INET6\n",
+ __func__);
#endif
if (xfrm4_tunnel_deregister(&xfrm_tunnel_handler, AF_INET))
- printk(KERN_INFO "ipip close: can't remove xfrm handler for AF_INET\n");
+ pr_info("%s: can't remove xfrm handler for AF_INET\n",
+ __func__);
if (xfrm_unregister_type(&ipip_type, AF_INET) < 0)
- printk(KERN_INFO "ipip close: can't remove xfrm type\n");
+ pr_info("%s: can't remove xfrm type\n", __func__);
}
module_init(ipip_init);
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index c02280a4d12..6a3bb6077e1 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -434,6 +434,10 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
/* Join all-node multicast group */
ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes);
+ /* Join all-router multicast group if forwarding is set */
+ if (ndev->cnf.forwarding && (dev->flags & IFF_MULTICAST))
+ ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
+
return ndev;
}
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 273f48d1df2..5605f9dca87 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -214,6 +214,7 @@ lookup_protocol:
inet->mc_ttl = 1;
inet->mc_index = 0;
inet->mc_list = NULL;
+ inet->rcv_tos = 0;
if (ipv4_config.no_pmtu_disc)
inet->pmtudisc = IP_PMTUDISC_DONT;
diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c
index 59402b4637f..db00d27ffb1 100644
--- a/net/ipv6/anycast.c
+++ b/net/ipv6/anycast.c
@@ -211,35 +211,6 @@ void ipv6_sock_ac_close(struct sock *sk)
rcu_read_unlock();
}
-#if 0
-/* The function is not used, which is funny. Apparently, author
- * supposed to use it to filter out datagrams inside udp/raw but forgot.
- *
- * It is OK, anycasts are not special comparing to delivery to unicasts.
- */
-
-int inet6_ac_check(struct sock *sk, struct in6_addr *addr, int ifindex)
-{
- struct ipv6_ac_socklist *pac;
- struct ipv6_pinfo *np = inet6_sk(sk);
- int found;
-
- found = 0;
- read_lock(&ipv6_sk_ac_lock);
- for (pac=np->ipv6_ac_list; pac; pac=pac->acl_next) {
- if (ifindex && pac->acl_ifindex != ifindex)
- continue;
- found = ipv6_addr_equal(&pac->acl_addr, addr);
- if (found)
- break;
- }
- read_unlock(&ipv6_sk_ac_lock);
-
- return found;
-}
-
-#endif
-
static void aca_put(struct ifacaddr6 *ac)
{
if (atomic_dec_and_test(&ac->aca_refcnt)) {
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 251e7cd75e8..76832c8dc89 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -485,7 +485,7 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
}
if (np->rxopt.bits.rxtclass) {
- int tclass = (ntohl(*(__be32 *)ipv6_hdr(skb)) >> 20) & 0xff;
+ int tclass = ipv6_tclass(ipv6_hdr(skb));
put_cmsg(msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
}
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 01d46bff63c..af88934e4d7 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -468,6 +468,8 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
fl6.flowi6_oif = np->mcast_oif;
+ else if (!fl6.flowi6_oif)
+ fl6.flowi6_oif = np->ucast_oif;
dst = icmpv6_route_lookup(net, skb, sk, &fl6);
if (IS_ERR(dst))
@@ -553,6 +555,8 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
fl6.flowi6_oif = np->mcast_oif;
+ else if (!fl6.flowi6_oif)
+ fl6.flowi6_oif = np->ucast_oif;
err = ip6_dst_lookup(sk, &dst, &fl6);
if (err)
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index b82bcde53f7..5b27fbcae34 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -1552,11 +1552,20 @@ static int fib6_age(struct rt6_info *rt, void *arg)
time_after_eq(now, rt->dst.lastuse + gc_args.timeout)) {
RT6_TRACE("aging clone %p\n", rt);
return -1;
- } else if ((rt->rt6i_flags & RTF_GATEWAY) &&
- (!(dst_get_neighbour_noref_raw(&rt->dst)->flags & NTF_ROUTER))) {
- RT6_TRACE("purging route %p via non-router but gateway\n",
- rt);
- return -1;
+ } else if (rt->rt6i_flags & RTF_GATEWAY) {
+ struct neighbour *neigh;
+ __u8 neigh_flags = 0;
+
+ neigh = dst_neigh_lookup(&rt->dst, &rt->rt6i_gateway);
+ if (neigh) {
+ neigh_flags = neigh->flags;
+ neigh_release(neigh);
+ }
+ if (neigh_flags & NTF_ROUTER) {
+ RT6_TRACE("purging route %p via non-router but gateway\n",
+ rt);
+ return -1;
+ }
}
gc_args.more++;
}
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index d97e07183ce..7a98fc2a5d9 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -388,7 +388,6 @@ int ip6_forward(struct sk_buff *skb)
struct ipv6hdr *hdr = ipv6_hdr(skb);
struct inet6_skb_parm *opt = IP6CB(skb);
struct net *net = dev_net(dst->dev);
- struct neighbour *n;
u32 mtu;
if (net->ipv6.devconf_all->forwarding == 0)
@@ -463,8 +462,7 @@ int ip6_forward(struct sk_buff *skb)
send redirects to source routed frames.
We don't send redirects to frames decapsulated from IPsec.
*/
- n = dst_get_neighbour_noref(dst);
- if (skb->dev == dst->dev && n && opt->srcrt == 0 && !skb_sec_path(skb)) {
+ if (skb->dev == dst->dev && opt->srcrt == 0 && !skb_sec_path(skb)) {
struct in6_addr *target = NULL;
struct rt6_info *rt;
@@ -474,8 +472,8 @@ int ip6_forward(struct sk_buff *skb)
*/
rt = (struct rt6_info *) dst;
- if ((rt->rt6i_flags & RTF_GATEWAY))
- target = (struct in6_addr*)&n->primary_key;
+ if (rt->rt6i_flags & RTF_GATEWAY)
+ target = &rt->rt6i_gateway;
else
target = &hdr->daddr;
@@ -486,7 +484,7 @@ int ip6_forward(struct sk_buff *skb)
and by source (inside ndisc_send_redirect)
*/
if (inet_peer_xrlim_allow(rt->rt6i_peer, 1*HZ))
- ndisc_send_redirect(skb, n, target);
+ ndisc_send_redirect(skb, target);
} else {
int addrtype = ipv6_addr_type(&hdr->saddr);
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index c7e95c8c579..5aa3981a392 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -1926,8 +1926,10 @@ static int ip6mr_forward2(struct net *net, struct mr6_table *mrt,
};
dst = ip6_route_output(net, NULL, &fl6);
- if (!dst)
+ if (dst->error) {
+ dst_release(dst);
goto out_free;
+ }
skb_dst_drop(skb);
skb_dst_set(skb, dst);
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 18a2719003c..63dd1f89ed7 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -516,6 +516,36 @@ done:
retv = 0;
break;
+ case IPV6_UNICAST_IF:
+ {
+ struct net_device *dev = NULL;
+ int ifindex;
+
+ if (optlen != sizeof(int))
+ goto e_inval;
+
+ ifindex = (__force int)ntohl((__force __be32)val);
+ if (ifindex == 0) {
+ np->ucast_oif = 0;
+ retv = 0;
+ break;
+ }
+
+ dev = dev_get_by_index(net, ifindex);
+ retv = -EADDRNOTAVAIL;
+ if (!dev)
+ break;
+ dev_put(dev);
+
+ retv = -EINVAL;
+ if (sk->sk_bound_dev_if)
+ break;
+
+ np->ucast_oif = ifindex;
+ retv = 0;
+ break;
+ }
+
case IPV6_MULTICAST_IF:
if (sk->sk_type == SOCK_STREAM)
break;
@@ -987,6 +1017,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
int hlim = np->mcast_hops;
put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
}
+ if (np->rxopt.bits.rxtclass) {
+ int tclass = np->rcv_tclass;
+ put_cmsg(&msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
+ }
if (np->rxopt.bits.rxoinfo) {
struct in6_pktinfo src_info;
src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
@@ -1160,6 +1194,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
val = np->mcast_oif;
break;
+ case IPV6_UNICAST_IF:
+ val = (__force int)htonl((__u32) np->ucast_oif);
+ break;
+
case IPV6_MTU_DISCOVER:
val = np->pmtudisc;
break;
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index d8f02ef88e5..3dcdb81ec3e 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1223,11 +1223,17 @@ static void ndisc_router_discovery(struct sk_buff *skb)
rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
- if (rt)
- neigh = dst_get_neighbour_noref(&rt->dst);
-
+ if (rt) {
+ neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
+ if (!neigh) {
+ ND_PRINTK0(KERN_ERR
+ "ICMPv6 RA: %s() got default router without neighbour.\n",
+ __func__);
+ dst_release(&rt->dst);
+ return;
+ }
+ }
if (rt && lifetime == 0) {
- neigh_clone(neigh);
ip6_del_rt(rt);
rt = NULL;
}
@@ -1244,7 +1250,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
return;
}
- neigh = dst_get_neighbour_noref(&rt->dst);
+ neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
if (neigh == NULL) {
ND_PRINTK0(KERN_ERR
"ICMPv6 RA: %s() got default router without neighbour.\n",
@@ -1411,7 +1417,7 @@ skip_routeinfo:
out:
if (rt)
dst_release(&rt->dst);
- else if (neigh)
+ if (neigh)
neigh_release(neigh);
}
@@ -1506,8 +1512,7 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
}
}
-void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
- const struct in6_addr *target)
+void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
{
struct net_device *dev = skb->dev;
struct net *net = dev_net(dev);
@@ -1545,9 +1550,10 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
&saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
dst = ip6_route_output(net, NULL, &fl6);
- if (dst == NULL)
+ if (dst->error) {
+ dst_release(dst);
return;
-
+ }
dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
if (IS_ERR(dst))
return;
@@ -1565,6 +1571,13 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
goto release;
if (dev->addr_len) {
+ struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
+ if (!neigh) {
+ ND_PRINTK2(KERN_WARNING
+ "ICMPv6 Redirect: no neigh for target address\n");
+ goto release;
+ }
+
read_lock_bh(&neigh->lock);
if (neigh->nud_state & NUD_VALID) {
memcpy(ha_buf, neigh->ha, dev->addr_len);
@@ -1573,6 +1586,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
len += ndisc_opt_addr_space(dev);
} else
read_unlock_bh(&neigh->lock);
+
+ neigh_release(neigh);
}
rd_len = min_t(unsigned int,
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index 9a68fb5b9e7..d33cddd16fb 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -154,15 +154,6 @@ config IP6_NF_TARGET_HL
(e.g. when running oldconfig). It selects
CONFIG_NETFILTER_XT_TARGET_HL.
-config IP6_NF_TARGET_LOG
- tristate "LOG target support"
- default m if NETFILTER_ADVANCED=n
- help
- This option adds a `LOG' target, which allows you to create rules in
- any iptables table which records the packet header to the syslog.
-
- To compile it as a module, choose M here. If unsure, say N.
-
config IP6_NF_FILTER
tristate "Packet filtering"
default m if NETFILTER_ADVANCED=n
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile
index 2eaed96db02..d4dfd0a2109 100644
--- a/net/ipv6/netfilter/Makefile
+++ b/net/ipv6/netfilter/Makefile
@@ -31,5 +31,4 @@ obj-$(CONFIG_IP6_NF_MATCH_RPFILTER) += ip6t_rpfilter.o
obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o
# targets
-obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o
obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o
diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c
deleted file mode 100644
index e6af8d72f26..00000000000
--- a/net/ipv6/netfilter/ip6t_LOG.c
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * This is a module which is used for logging packets.
- */
-
-/* (C) 2001 Jan Rekorajski <baggins@pld.org.pl>
- * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
- */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/module.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ip.h>
-#include <linux/spinlock.h>
-#include <linux/icmpv6.h>
-#include <net/udp.h>
-#include <net/tcp.h>
-#include <net/ipv6.h>
-#include <linux/netfilter.h>
-#include <linux/netfilter/x_tables.h>
-#include <linux/netfilter_ipv6/ip6_tables.h>
-#include <net/netfilter/nf_log.h>
-#include <net/netfilter/xt_log.h>
-
-MODULE_AUTHOR("Jan Rekorajski <baggins@pld.org.pl>");
-MODULE_DESCRIPTION("Xtables: IPv6 packet logging to syslog");
-MODULE_LICENSE("GPL");
-
-struct in_device;
-#include <net/route.h>
-#include <linux/netfilter_ipv6/ip6t_LOG.h>
-
-/* One level of recursion won't kill us */
-static void dump_packet(struct sbuff *m,
- const struct nf_loginfo *info,
- const struct sk_buff *skb, unsigned int ip6hoff,
- int recurse)
-{
- u_int8_t currenthdr;
- int fragment;
- struct ipv6hdr _ip6h;
- const struct ipv6hdr *ih;
- unsigned int ptr;
- unsigned int hdrlen = 0;
- unsigned int logflags;
-
- if (info->type == NF_LOG_TYPE_LOG)
- logflags = info->u.log.logflags;
- else
- logflags = NF_LOG_MASK;
-
- ih = skb_header_pointer(skb, ip6hoff, sizeof(_ip6h), &_ip6h);
- if (ih == NULL) {
- sb_add(m, "TRUNCATED");
- return;
- }
-
- /* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000 " */
- sb_add(m, "SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr);
-
- /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */
- sb_add(m, "LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ",
- ntohs(ih->payload_len) + sizeof(struct ipv6hdr),
- (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20,
- ih->hop_limit,
- (ntohl(*(__be32 *)ih) & 0x000fffff));
-
- fragment = 0;
- ptr = ip6hoff + sizeof(struct ipv6hdr);
- currenthdr = ih->nexthdr;
- while (currenthdr != NEXTHDR_NONE && ip6t_ext_hdr(currenthdr)) {
- struct ipv6_opt_hdr _hdr;
- const struct ipv6_opt_hdr *hp;
-
- hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
- if (hp == NULL) {
- sb_add(m, "TRUNCATED");
- return;
- }
-
- /* Max length: 48 "OPT (...) " */
- if (logflags & IP6T_LOG_IPOPT)
- sb_add(m, "OPT ( ");
-
- switch (currenthdr) {
- case IPPROTO_FRAGMENT: {
- struct frag_hdr _fhdr;
- const struct frag_hdr *fh;
-
- sb_add(m, "FRAG:");
- fh = skb_header_pointer(skb, ptr, sizeof(_fhdr),
- &_fhdr);
- if (fh == NULL) {
- sb_add(m, "TRUNCATED ");
- return;
- }
-
- /* Max length: 6 "65535 " */
- sb_add(m, "%u ", ntohs(fh->frag_off) & 0xFFF8);
-
- /* Max length: 11 "INCOMPLETE " */
- if (fh->frag_off & htons(0x0001))
- sb_add(m, "INCOMPLETE ");
-
- sb_add(m, "ID:%08x ", ntohl(fh->identification));
-
- if (ntohs(fh->frag_off) & 0xFFF8)
- fragment = 1;
-
- hdrlen = 8;
-
- break;
- }
- case IPPROTO_DSTOPTS:
- case IPPROTO_ROUTING:
- case IPPROTO_HOPOPTS:
- if (fragment) {
- if (logflags & IP6T_LOG_IPOPT)
- sb_add(m, ")");
- return;
- }
- hdrlen = ipv6_optlen(hp);
- break;
- /* Max Length */
- case IPPROTO_AH:
- if (logflags & IP6T_LOG_IPOPT) {
- struct ip_auth_hdr _ahdr;
- const struct ip_auth_hdr *ah;
-
- /* Max length: 3 "AH " */
- sb_add(m, "AH ");
-
- if (fragment) {
- sb_add(m, ")");
- return;
- }
-
- ah = skb_header_pointer(skb, ptr, sizeof(_ahdr),
- &_ahdr);
- if (ah == NULL) {
- /*
- * Max length: 26 "INCOMPLETE [65535
- * bytes] )"
- */
- sb_add(m, "INCOMPLETE [%u bytes] )",
- skb->len - ptr);
- return;
- }
-
- /* Length: 15 "SPI=0xF1234567 */
- sb_add(m, "SPI=0x%x ", ntohl(ah->spi));
-
- }
-
- hdrlen = (hp->hdrlen+2)<<2;
- break;
- case IPPROTO_ESP:
- if (logflags & IP6T_LOG_IPOPT) {
- struct ip_esp_hdr _esph;
- const struct ip_esp_hdr *eh;
-
- /* Max length: 4 "ESP " */
- sb_add(m, "ESP ");
-
- if (fragment) {
- sb_add(m, ")");
- return;
- }
-
- /*
- * Max length: 26 "INCOMPLETE [65535 bytes] )"
- */
- eh = skb_header_pointer(skb, ptr, sizeof(_esph),
- &_esph);
- if (eh == NULL) {
- sb_add(m, "INCOMPLETE [%u bytes] )",
- skb->len - ptr);
- return;
- }
-
- /* Length: 16 "SPI=0xF1234567 )" */
- sb_add(m, "SPI=0x%x )", ntohl(eh->spi) );
-
- }
- return;
- default:
- /* Max length: 20 "Unknown Ext Hdr 255" */
- sb_add(m, "Unknown Ext Hdr %u", currenthdr);
- return;
- }
- if (logflags & IP6T_LOG_IPOPT)
- sb_add(m, ") ");
-
- currenthdr = hp->nexthdr;
- ptr += hdrlen;
- }
-
- switch (currenthdr) {
- case IPPROTO_TCP: {
- struct tcphdr _tcph;
- const struct tcphdr *th;
-
- /* Max length: 10 "PROTO=TCP " */
- sb_add(m, "PROTO=TCP ");
-
- if (fragment)
- break;
-
- /* Max length: 25 "INCOMPLETE [65535 bytes] " */
- th = skb_header_pointer(skb, ptr, sizeof(_tcph), &_tcph);
- if (th == NULL) {
- sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr);
- return;
- }
-
- /* Max length: 20 "SPT=65535 DPT=65535 " */
- sb_add(m, "SPT=%u DPT=%u ",
- ntohs(th->source), ntohs(th->dest));
- /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */
- if (logflags & IP6T_LOG_TCPSEQ)
- sb_add(m, "SEQ=%u ACK=%u ",
- ntohl(th->seq), ntohl(th->ack_seq));
- /* Max length: 13 "WINDOW=65535 " */
- sb_add(m, "WINDOW=%u ", ntohs(th->window));
- /* Max length: 9 "RES=0x3C " */
- sb_add(m, "RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(th) & TCP_RESERVED_BITS) >> 22));
- /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */
- if (th->cwr)
- sb_add(m, "CWR ");
- if (th->ece)
- sb_add(m, "ECE ");
- if (th->urg)
- sb_add(m, "URG ");
- if (th->ack)
- sb_add(m, "ACK ");
- if (th->psh)
- sb_add(m, "PSH ");
- if (th->rst)
- sb_add(m, "RST ");
- if (th->syn)
- sb_add(m, "SYN ");
- if (th->fin)
- sb_add(m, "FIN ");
- /* Max length: 11 "URGP=65535 " */
- sb_add(m, "URGP=%u ", ntohs(th->urg_ptr));
-
- if ((logflags & IP6T_LOG_TCPOPT) &&
- th->doff * 4 > sizeof(struct tcphdr)) {
- u_int8_t _opt[60 - sizeof(struct tcphdr)];
- const u_int8_t *op;
- unsigned int i;
- unsigned int optsize = th->doff * 4
- - sizeof(struct tcphdr);
-
- op = skb_header_pointer(skb,
- ptr + sizeof(struct tcphdr),
- optsize, _opt);
- if (op == NULL) {
- sb_add(m, "OPT (TRUNCATED)");
- return;
- }
-
- /* Max length: 127 "OPT (" 15*4*2chars ") " */
- sb_add(m, "OPT (");
- for (i =0; i < optsize; i++)
- sb_add(m, "%02X", op[i]);
- sb_add(m, ") ");
- }
- break;
- }
- case IPPROTO_UDP:
- case IPPROTO_UDPLITE: {
- struct udphdr _udph;
- const struct udphdr *uh;
-
- if (currenthdr == IPPROTO_UDP)
- /* Max length: 10 "PROTO=UDP " */
- sb_add(m, "PROTO=UDP " );
- else /* Max length: 14 "PROTO=UDPLITE " */
- sb_add(m, "PROTO=UDPLITE ");
-
- if (fragment)
- break;
-
- /* Max length: 25 "INCOMPLETE [65535 bytes] " */
- uh = skb_header_pointer(skb, ptr, sizeof(_udph), &_udph);
- if (uh == NULL) {
- sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr);
- return;
- }
-
- /* Max length: 20 "SPT=65535 DPT=65535 " */
- sb_add(m, "SPT=%u DPT=%u LEN=%u ",
- ntohs(uh->source), ntohs(uh->dest),
- ntohs(uh->len));
- break;
- }
- case IPPROTO_ICMPV6: {
- struct icmp6hdr _icmp6h;
- const struct icmp6hdr *ic;
-
- /* Max length: 13 "PROTO=ICMPv6 " */
- sb_add(m, "PROTO=ICMPv6 ");
-
- if (fragment)
- break;
-
- /* Max length: 25 "INCOMPLETE [65535 bytes] " */
- ic = skb_header_pointer(skb, ptr, sizeof(_icmp6h), &_icmp6h);
- if (ic == NULL) {
- sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr);
- return;
- }
-
- /* Max length: 18 "TYPE=255 CODE=255 " */
- sb_add(m, "TYPE=%u CODE=%u ", ic->icmp6_type, ic->icmp6_code);
-
- switch (ic->icmp6_type) {
- case ICMPV6_ECHO_REQUEST:
- case ICMPV6_ECHO_REPLY:
- /* Max length: 19 "ID=65535 SEQ=65535 " */
- sb_add(m, "ID=%u SEQ=%u ",
- ntohs(ic->icmp6_identifier),
- ntohs(ic->icmp6_sequence));
- break;
- case ICMPV6_MGM_QUERY:
- case ICMPV6_MGM_REPORT:
- case ICMPV6_MGM_REDUCTION:
- break;
-
- case ICMPV6_PARAMPROB:
- /* Max length: 17 "POINTER=ffffffff " */
- sb_add(m, "POINTER=%08x ", ntohl(ic->icmp6_pointer));
- /* Fall through */
- case ICMPV6_DEST_UNREACH:
- case ICMPV6_PKT_TOOBIG:
- case ICMPV6_TIME_EXCEED:
- /* Max length: 3+maxlen */
- if (recurse) {
- sb_add(m, "[");
- dump_packet(m, info, skb,
- ptr + sizeof(_icmp6h), 0);
- sb_add(m, "] ");
- }
-
- /* Max length: 10 "MTU=65535 " */
- if (ic->icmp6_type == ICMPV6_PKT_TOOBIG)
- sb_add(m, "MTU=%u ", ntohl(ic->icmp6_mtu));
- }
- break;
- }
- /* Max length: 10 "PROTO=255 " */
- default:
- sb_add(m, "PROTO=%u ", currenthdr);
- }
-
- /* Max length: 15 "UID=4294967295 " */
- if ((logflags & IP6T_LOG_UID) && recurse && skb->sk) {
- read_lock_bh(&skb->sk->sk_callback_lock);
- if (skb->sk->sk_socket && skb->sk->sk_socket->file)
- sb_add(m, "UID=%u GID=%u ",
- skb->sk->sk_socket->file->f_cred->fsuid,
- skb->sk->sk_socket->file->f_cred->fsgid);
- read_unlock_bh(&skb->sk->sk_callback_lock);
- }
-
- /* Max length: 16 "MARK=0xFFFFFFFF " */
- if (!recurse && skb->mark)
- sb_add(m, "MARK=0x%x ", skb->mark);
-}
-
-static void dump_mac_header(struct sbuff *m,
- const struct nf_loginfo *info,
- const struct sk_buff *skb)
-{
- struct net_device *dev = skb->dev;
- unsigned int logflags = 0;
-
- if (info->type == NF_LOG_TYPE_LOG)
- logflags = info->u.log.logflags;
-
- if (!(logflags & IP6T_LOG_MACDECODE))
- goto fallback;
-
- switch (dev->type) {
- case ARPHRD_ETHER:
- sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ",
- eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
- ntohs(eth_hdr(skb)->h_proto));
- return;
- default:
- break;
- }
-
-fallback:
- sb_add(m, "MAC=");
- if (dev->hard_header_len &&
- skb->mac_header != skb->network_header) {
- const unsigned char *p = skb_mac_header(skb);
- unsigned int len = dev->hard_header_len;
- unsigned int i;
-
- if (dev->type == ARPHRD_SIT &&
- (p -= ETH_HLEN) < skb->head)
- p = NULL;
-
- if (p != NULL) {
- sb_add(m, "%02x", *p++);
- for (i = 1; i < len; i++)
- sb_add(m, ":%02x", *p++);
- }
- sb_add(m, " ");
-
- if (dev->type == ARPHRD_SIT) {
- const struct iphdr *iph =
- (struct iphdr *)skb_mac_header(skb);
- sb_add(m, "TUNNEL=%pI4->%pI4 ", &iph->saddr, &iph->daddr);
- }
- } else
- sb_add(m, " ");
-}
-
-static struct nf_loginfo default_loginfo = {
- .type = NF_LOG_TYPE_LOG,
- .u = {
- .log = {
- .level = 5,
- .logflags = NF_LOG_MASK,
- },
- },
-};
-
-static void
-ip6t_log_packet(u_int8_t pf,
- unsigned int hooknum,
- const struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- const struct nf_loginfo *loginfo,
- const char *prefix)
-{
- struct sbuff *m = sb_open();
-
- if (!loginfo)
- loginfo = &default_loginfo;
-
- sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level,
- prefix,
- in ? in->name : "",
- out ? out->name : "");
-
- if (in != NULL)
- dump_mac_header(m, loginfo, skb);
-
- dump_packet(m, loginfo, skb, skb_network_offset(skb), 1);
-
- sb_close(m);
-}
-
-static unsigned int
-log_tg6(struct sk_buff *skb, const struct xt_action_param *par)
-{
- const struct ip6t_log_info *loginfo = par->targinfo;
- struct nf_loginfo li;
-
- li.type = NF_LOG_TYPE_LOG;
- li.u.log.level = loginfo->level;
- li.u.log.logflags = loginfo->logflags;
-
- ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, par->out,
- &li, loginfo->prefix);
- return XT_CONTINUE;
-}
-
-
-static int log_tg6_check(const struct xt_tgchk_param *par)
-{
- const struct ip6t_log_info *loginfo = par->targinfo;
-
- if (loginfo->level >= 8) {
- pr_debug("level %u >= 8\n", loginfo->level);
- return -EINVAL;
- }
- if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') {
- pr_debug("prefix not null-terminated\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static struct xt_target log_tg6_reg __read_mostly = {
- .name = "LOG",
- .family = NFPROTO_IPV6,
- .target = log_tg6,
- .targetsize = sizeof(struct ip6t_log_info),
- .checkentry = log_tg6_check,
- .me = THIS_MODULE,
-};
-
-static struct nf_logger ip6t_logger __read_mostly = {
- .name = "ip6t_LOG",
- .logfn = &ip6t_log_packet,
- .me = THIS_MODULE,
-};
-
-static int __init log_tg6_init(void)
-{
- int ret;
-
- ret = xt_register_target(&log_tg6_reg);
- if (ret < 0)
- return ret;
- nf_log_register(NFPROTO_IPV6, &ip6t_logger);
- return 0;
-}
-
-static void __exit log_tg6_exit(void)
-{
- nf_log_unregister(&ip6t_logger);
- xt_unregister_target(&log_tg6_reg);
-}
-
-module_init(log_tg6_init);
-module_exit(log_tg6_exit);
diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
index 7c05e7eacbc..92cc9f2931a 100644
--- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
@@ -88,25 +88,31 @@ static int icmpv6_print_tuple(struct seq_file *s,
ntohs(tuple->src.u.icmp.id));
}
+static unsigned int *icmpv6_get_timeouts(struct net *net)
+{
+ return &nf_ct_icmpv6_timeout;
+}
+
/* Returns verdict for packet, or -1 for invalid. */
static int icmpv6_packet(struct nf_conn *ct,
const struct sk_buff *skb,
unsigned int dataoff,
enum ip_conntrack_info ctinfo,
u_int8_t pf,
- unsigned int hooknum)
+ unsigned int hooknum,
+ unsigned int *timeout)
{
/* Do not immediately delete the connection after the first
successful reply to avoid excessive conntrackd traffic
and also to handle correctly ICMP echo reply duplicates. */
- nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmpv6_timeout);
+ nf_ct_refresh_acct(ct, ctinfo, skb, *timeout);
return NF_ACCEPT;
}
/* Called when a new connection for this protocol found. */
static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb,
- unsigned int dataoff)
+ unsigned int dataoff, unsigned int *timeouts)
{
static const u_int8_t valid_new[] = {
[ICMPV6_ECHO_REQUEST - 128] = 1,
@@ -270,6 +276,44 @@ static int icmpv6_nlattr_tuple_size(void)
}
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_cttimeout.h>
+
+static int icmpv6_timeout_nlattr_to_obj(struct nlattr *tb[], void *data)
+{
+ unsigned int *timeout = data;
+
+ if (tb[CTA_TIMEOUT_ICMPV6_TIMEOUT]) {
+ *timeout =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_ICMPV6_TIMEOUT])) * HZ;
+ } else {
+ /* Set default ICMPv6 timeout. */
+ *timeout = nf_ct_icmpv6_timeout;
+ }
+ return 0;
+}
+
+static int
+icmpv6_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
+{
+ const unsigned int *timeout = data;
+
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_ICMPV6_TIMEOUT, htonl(*timeout / HZ));
+
+ return 0;
+
+nla_put_failure:
+ return -ENOSPC;
+}
+
+static const struct nla_policy
+icmpv6_timeout_nla_policy[CTA_TIMEOUT_ICMPV6_MAX+1] = {
+ [CTA_TIMEOUT_ICMPV6_TIMEOUT] = { .type = NLA_U32 },
+};
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
+
#ifdef CONFIG_SYSCTL
static struct ctl_table_header *icmpv6_sysctl_header;
static struct ctl_table icmpv6_sysctl_table[] = {
@@ -293,6 +337,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly =
.invert_tuple = icmpv6_invert_tuple,
.print_tuple = icmpv6_print_tuple,
.packet = icmpv6_packet,
+ .get_timeouts = icmpv6_get_timeouts,
.new = icmpv6_new,
.error = icmpv6_error,
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
@@ -301,6 +346,15 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly =
.nlattr_to_tuple = icmpv6_nlattr_to_tuple,
.nla_policy = icmpv6_nla_policy,
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = icmpv6_timeout_nlattr_to_obj,
+ .obj_to_nlattr = icmpv6_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_ICMP_MAX,
+ .obj_size = sizeof(unsigned int),
+ .nla_policy = icmpv6_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
#ifdef CONFIG_SYSCTL
.ctl_table_header = &icmpv6_sysctl_header,
.ctl_table = icmpv6_sysctl_table,
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index d02f7e4dd61..5bddea77884 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -856,6 +856,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
fl6.flowi6_oif = np->mcast_oif;
+ else if (!fl6.flowi6_oif)
+ fl6.flowi6_oif = np->ucast_oif;
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true);
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index b69fae76a6f..9447bd69873 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -336,12 +336,11 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
}
found:
- /* RFC5722, Section 4:
- * When reassembling an IPv6 datagram, if
+ /* RFC5722, Section 4, amended by Errata ID : 3089
+ * When reassembling an IPv6 datagram, if
* one or more its constituent fragments is determined to be an
* overlapping fragment, the entire datagram (and any constituent
- * fragments, including those not yet received) MUST be silently
- * discarded.
+ * fragments) MUST be silently discarded.
*/
/* Check for overlap with preceding fragment. */
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 8c2e3ab58f2..92be12bb8d2 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -121,9 +121,22 @@ static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old)
return p;
}
+static inline const void *choose_neigh_daddr(struct rt6_info *rt, const void *daddr)
+{
+ struct in6_addr *p = &rt->rt6i_gateway;
+
+ if (!ipv6_addr_any(p))
+ return (const void *) p;
+ return daddr;
+}
+
static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, const void *daddr)
{
- struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr);
+ struct rt6_info *rt = (struct rt6_info *) dst;
+ struct neighbour *n;
+
+ daddr = choose_neigh_daddr(rt, daddr);
+ n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr);
if (n)
return n;
return neigh_create(&nd_tbl, daddr, dst->dev);
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 133768e5291..c4ffd174352 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -680,9 +680,10 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
/* ISATAP (RFC4214) - must come before 6to4 */
if (dev->priv_flags & IFF_ISATAP) {
struct neighbour *neigh = NULL;
+ bool do_tx_error = false;
if (skb_dst(skb))
- neigh = dst_get_neighbour_noref(skb_dst(skb));
+ neigh = dst_neigh_lookup(skb_dst(skb), &iph6->daddr);
if (neigh == NULL) {
if (net_ratelimit())
@@ -697,6 +698,10 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
ipv6_addr_is_isatap(addr6))
dst = addr6->s6_addr32[3];
else
+ do_tx_error = true;
+
+ neigh_release(neigh);
+ if (do_tx_error)
goto tx_error;
}
@@ -705,9 +710,10 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
if (!dst) {
struct neighbour *neigh = NULL;
+ bool do_tx_error = false;
if (skb_dst(skb))
- neigh = dst_get_neighbour_noref(skb_dst(skb));
+ neigh = dst_neigh_lookup(skb_dst(skb), &iph6->daddr);
if (neigh == NULL) {
if (net_ratelimit())
@@ -723,10 +729,14 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
addr_type = ipv6_addr_type(addr6);
}
- if ((addr_type & IPV6_ADDR_COMPATv4) == 0)
- goto tx_error_icmp;
+ if ((addr_type & IPV6_ADDR_COMPATv4) != 0)
+ dst = addr6->s6_addr32[3];
+ else
+ do_tx_error = true;
- dst = addr6->s6_addr32[3];
+ neigh_release(neigh);
+ if (do_tx_error)
+ goto tx_error;
}
rt = ip_route_output_ports(dev_net(dev), &fl4, NULL,
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 3edd05ae438..12c6ece67f3 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -540,19 +540,7 @@ static void tcp_v6_reqsk_destructor(struct request_sock *req)
static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk,
const struct in6_addr *addr)
{
- struct tcp_sock *tp = tcp_sk(sk);
- int i;
-
- BUG_ON(tp == NULL);
-
- if (!tp->md5sig_info || !tp->md5sig_info->entries6)
- return NULL;
-
- for (i = 0; i < tp->md5sig_info->entries6; i++) {
- if (ipv6_addr_equal(&tp->md5sig_info->keys6[i].addr, addr))
- return &tp->md5sig_info->keys6[i].base;
- }
- return NULL;
+ return tcp_md5_do_lookup(sk, (union tcp_md5_addr *)addr, AF_INET6);
}
static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk,
@@ -567,136 +555,11 @@ static struct tcp_md5sig_key *tcp_v6_reqsk_md5_lookup(struct sock *sk,
return tcp_v6_md5_do_lookup(sk, &inet6_rsk(req)->rmt_addr);
}
-static int tcp_v6_md5_do_add(struct sock *sk, const struct in6_addr *peer,
- char *newkey, u8 newkeylen)
-{
- /* Add key to the list */
- struct tcp_md5sig_key *key;
- struct tcp_sock *tp = tcp_sk(sk);
- struct tcp6_md5sig_key *keys;
-
- key = tcp_v6_md5_do_lookup(sk, peer);
- if (key) {
- /* modify existing entry - just update that one */
- kfree(key->key);
- key->key = newkey;
- key->keylen = newkeylen;
- } else {
- /* reallocate new list if current one is full. */
- if (!tp->md5sig_info) {
- tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info), GFP_ATOMIC);
- if (!tp->md5sig_info) {
- kfree(newkey);
- return -ENOMEM;
- }
- sk_nocaps_add(sk, NETIF_F_GSO_MASK);
- }
- if (tp->md5sig_info->entries6 == 0 &&
- tcp_alloc_md5sig_pool(sk) == NULL) {
- kfree(newkey);
- return -ENOMEM;
- }
- if (tp->md5sig_info->alloced6 == tp->md5sig_info->entries6) {
- keys = kmalloc((sizeof (tp->md5sig_info->keys6[0]) *
- (tp->md5sig_info->entries6 + 1)), GFP_ATOMIC);
-
- if (!keys) {
- kfree(newkey);
- if (tp->md5sig_info->entries6 == 0)
- tcp_free_md5sig_pool();
- return -ENOMEM;
- }
-
- if (tp->md5sig_info->entries6)
- memmove(keys, tp->md5sig_info->keys6,
- (sizeof (tp->md5sig_info->keys6[0]) *
- tp->md5sig_info->entries6));
-
- kfree(tp->md5sig_info->keys6);
- tp->md5sig_info->keys6 = keys;
- tp->md5sig_info->alloced6++;
- }
-
- tp->md5sig_info->keys6[tp->md5sig_info->entries6].addr = *peer;
- tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.key = newkey;
- tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.keylen = newkeylen;
-
- tp->md5sig_info->entries6++;
- }
- return 0;
-}
-
-static int tcp_v6_md5_add_func(struct sock *sk, struct sock *addr_sk,
- u8 *newkey, __u8 newkeylen)
-{
- return tcp_v6_md5_do_add(sk, &inet6_sk(addr_sk)->daddr,
- newkey, newkeylen);
-}
-
-static int tcp_v6_md5_do_del(struct sock *sk, const struct in6_addr *peer)
-{
- struct tcp_sock *tp = tcp_sk(sk);
- int i;
-
- for (i = 0; i < tp->md5sig_info->entries6; i++) {
- if (ipv6_addr_equal(&tp->md5sig_info->keys6[i].addr, peer)) {
- /* Free the key */
- kfree(tp->md5sig_info->keys6[i].base.key);
- tp->md5sig_info->entries6--;
-
- if (tp->md5sig_info->entries6 == 0) {
- kfree(tp->md5sig_info->keys6);
- tp->md5sig_info->keys6 = NULL;
- tp->md5sig_info->alloced6 = 0;
- tcp_free_md5sig_pool();
- } else {
- /* shrink the database */
- if (tp->md5sig_info->entries6 != i)
- memmove(&tp->md5sig_info->keys6[i],
- &tp->md5sig_info->keys6[i+1],
- (tp->md5sig_info->entries6 - i)
- * sizeof (tp->md5sig_info->keys6[0]));
- }
- return 0;
- }
- }
- return -ENOENT;
-}
-
-static void tcp_v6_clear_md5_list (struct sock *sk)
-{
- struct tcp_sock *tp = tcp_sk(sk);
- int i;
-
- if (tp->md5sig_info->entries6) {
- for (i = 0; i < tp->md5sig_info->entries6; i++)
- kfree(tp->md5sig_info->keys6[i].base.key);
- tp->md5sig_info->entries6 = 0;
- tcp_free_md5sig_pool();
- }
-
- kfree(tp->md5sig_info->keys6);
- tp->md5sig_info->keys6 = NULL;
- tp->md5sig_info->alloced6 = 0;
-
- if (tp->md5sig_info->entries4) {
- for (i = 0; i < tp->md5sig_info->entries4; i++)
- kfree(tp->md5sig_info->keys4[i].base.key);
- tp->md5sig_info->entries4 = 0;
- tcp_free_md5sig_pool();
- }
-
- kfree(tp->md5sig_info->keys4);
- tp->md5sig_info->keys4 = NULL;
- tp->md5sig_info->alloced4 = 0;
-}
-
static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
int optlen)
{
struct tcp_md5sig cmd;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr;
- u8 *newkey;
if (optlen < sizeof(cmd))
return -EINVAL;
@@ -708,36 +571,22 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
return -EINVAL;
if (!cmd.tcpm_keylen) {
- if (!tcp_sk(sk)->md5sig_info)
- return -ENOENT;
if (ipv6_addr_v4mapped(&sin6->sin6_addr))
- return tcp_v4_md5_do_del(sk, sin6->sin6_addr.s6_addr32[3]);
- return tcp_v6_md5_do_del(sk, &sin6->sin6_addr);
+ return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3],
+ AF_INET);
+ return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr,
+ AF_INET6);
}
if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
return -EINVAL;
- if (!tcp_sk(sk)->md5sig_info) {
- struct tcp_sock *tp = tcp_sk(sk);
- struct tcp_md5sig_info *p;
-
- p = kzalloc(sizeof(struct tcp_md5sig_info), GFP_KERNEL);
- if (!p)
- return -ENOMEM;
+ if (ipv6_addr_v4mapped(&sin6->sin6_addr))
+ return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3],
+ AF_INET, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
- tp->md5sig_info = p;
- sk_nocaps_add(sk, NETIF_F_GSO_MASK);
- }
-
- newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
- if (!newkey)
- return -ENOMEM;
- if (ipv6_addr_v4mapped(&sin6->sin6_addr)) {
- return tcp_v4_md5_do_add(sk, sin6->sin6_addr.s6_addr32[3],
- newkey, cmd.tcpm_keylen);
- }
- return tcp_v6_md5_do_add(sk, &sin6->sin6_addr, newkey, cmd.tcpm_keylen);
+ return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr,
+ AF_INET6, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
}
static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
@@ -1074,6 +923,13 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
const struct tcphdr *th = tcp_hdr(skb);
u32 seq = 0, ack_seq = 0;
struct tcp_md5sig_key *key = NULL;
+#ifdef CONFIG_TCP_MD5SIG
+ const __u8 *hash_location = NULL;
+ struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+ unsigned char newhash[16];
+ int genhash;
+ struct sock *sk1 = NULL;
+#endif
if (th->rst)
return;
@@ -1082,8 +938,32 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
return;
#ifdef CONFIG_TCP_MD5SIG
- if (sk)
- key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr);
+ hash_location = tcp_parse_md5sig_option(th);
+ if (!sk && hash_location) {
+ /*
+ * active side is lost. Try to find listening socket through
+ * source port, and then find md5 key through listening socket.
+ * we are not loose security here:
+ * Incoming packet is checked with md5 hash with finding key,
+ * no RST generated if md5 hash doesn't match.
+ */
+ sk1 = inet6_lookup_listener(dev_net(skb_dst(skb)->dev),
+ &tcp_hashinfo, &ipv6h->daddr,
+ ntohs(th->source), inet6_iif(skb));
+ if (!sk1)
+ return;
+
+ rcu_read_lock();
+ key = tcp_v6_md5_do_lookup(sk1, &ipv6h->saddr);
+ if (!key)
+ goto release_sk1;
+
+ genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, NULL, skb);
+ if (genhash || memcmp(hash_location, newhash, 16) != 0)
+ goto release_sk1;
+ } else {
+ key = sk ? tcp_v6_md5_do_lookup(sk, &ipv6h->saddr) : NULL;
+ }
#endif
if (th->ack)
@@ -1093,6 +973,14 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
(th->doff << 2);
tcp_v6_send_response(skb, seq, ack_seq, 0, 0, key, 1, 0);
+
+#ifdef CONFIG_TCP_MD5SIG
+release_sk1:
+ if (sk1) {
+ rcu_read_unlock();
+ sock_put(sk1);
+ }
+#endif
}
static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts,
@@ -1394,6 +1282,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newnp->opt = NULL;
newnp->mcast_oif = inet6_iif(skb);
newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
+ newnp->rcv_tclass = ipv6_tclass(ipv6_hdr(skb));
/*
* No need to charge this sock to the relevant IPv6 refcnt debug socks count
@@ -1472,6 +1361,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newnp->opt = NULL;
newnp->mcast_oif = inet6_iif(skb);
newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
+ newnp->rcv_tclass = ipv6_tclass(ipv6_hdr(skb));
/* Clone native IPv6 options from listening socket (if any)
@@ -1510,10 +1400,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
* memory, then we end up not copying the key
* across. Shucks.
*/
- char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC);
- if (newkey != NULL)
- tcp_v6_md5_do_add(newsk, &newnp->daddr,
- newkey, key->keylen);
+ tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newnp->daddr,
+ AF_INET6, key->key, key->keylen, GFP_ATOMIC);
}
#endif
@@ -1676,6 +1564,8 @@ ipv6_pktoptions:
np->mcast_oif = inet6_iif(opt_skb);
if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)
np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit;
+ if (np->rxopt.bits.rxtclass)
+ np->rcv_tclass = ipv6_tclass(ipv6_hdr(skb));
if (ipv6_opt_accepted(sk, opt_skb)) {
skb_set_owner_r(opt_skb, sk);
opt_skb = xchg(&np->pktoptions, opt_skb);
@@ -1898,7 +1788,6 @@ static const struct inet_connection_sock_af_ops ipv6_specific = {
static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
.md5_lookup = tcp_v6_md5_lookup,
.calc_md5_hash = tcp_v6_md5_hash_skb,
- .md5_add = tcp_v6_md5_add_func,
.md5_parse = tcp_v6_parse_md5_keys,
};
#endif
@@ -1930,7 +1819,6 @@ static const struct inet_connection_sock_af_ops ipv6_mapped = {
static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = {
.md5_lookup = tcp_v4_md5_lookup,
.calc_md5_hash = tcp_v4_md5_hash_skb,
- .md5_add = tcp_v6_md5_add_func,
.md5_parse = tcp_v6_parse_md5_keys,
};
#endif
@@ -2004,11 +1892,6 @@ static int tcp_v6_init_sock(struct sock *sk)
static void tcp_v6_destroy_sock(struct sock *sk)
{
-#ifdef CONFIG_TCP_MD5SIG
- /* Clean up the MD5 key list */
- if (tcp_sk(sk)->md5sig_info)
- tcp_v6_clear_md5_list(sk);
-#endif
tcp_v4_destroy_sock(sk);
inet6_destroy_sock(sk);
}
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 4f96b5c6368..37b0699e95e 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -342,7 +342,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
struct inet_sock *inet = inet_sk(sk);
struct sk_buff *skb;
unsigned int ulen, copied;
- int peeked;
+ int peeked, off = 0;
int err;
int is_udplite = IS_UDPLITE(sk);
int is_udp4;
@@ -359,7 +359,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
try_again:
skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
- &peeked, &err);
+ &peeked, &off, &err);
if (!skb)
goto out;
@@ -1130,7 +1130,8 @@ do_udp_sendmsg:
if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) {
fl6.flowi6_oif = np->mcast_oif;
connected = 0;
- }
+ } else if (!fl6.flowi6_oif)
+ fl6.flowi6_oif = np->ucast_oif;
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
diff --git a/net/ipv6/xfrm6_mode_beet.c b/net/ipv6/xfrm6_mode_beet.c
index a81ce945075..9949a356d62 100644
--- a/net/ipv6/xfrm6_mode_beet.c
+++ b/net/ipv6/xfrm6_mode_beet.c
@@ -80,7 +80,6 @@ static int xfrm6_beet_output(struct xfrm_state *x, struct sk_buff *skb)
static int xfrm6_beet_input(struct xfrm_state *x, struct sk_buff *skb)
{
struct ipv6hdr *ip6h;
- const unsigned char *old_mac;
int size = sizeof(struct ipv6hdr);
int err;
@@ -90,10 +89,7 @@ static int xfrm6_beet_input(struct xfrm_state *x, struct sk_buff *skb)
__skb_push(skb, size);
skb_reset_network_header(skb);
-
- old_mac = skb_mac_header(skb);
- skb_set_mac_header(skb, -skb->mac_len);
- memmove(skb_mac_header(skb), old_mac, skb->mac_len);
+ skb_mac_header_rebuild(skb);
xfrm6_beet_make_header(skb);
diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c
index 261e6e6f487..9f2095b19ad 100644
--- a/net/ipv6/xfrm6_mode_tunnel.c
+++ b/net/ipv6/xfrm6_mode_tunnel.c
@@ -63,7 +63,6 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
{
int err = -EINVAL;
- const unsigned char *old_mac;
if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPV6)
goto out;
@@ -80,10 +79,9 @@ static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
if (!(x->props.flags & XFRM_STATE_NOECN))
ipip6_ecn_decapsulate(skb);
- old_mac = skb_mac_header(skb);
- skb_set_mac_header(skb, -skb->mac_len);
- memmove(skb_mac_header(skb), old_mac, skb->mac_len);
skb_reset_network_header(skb);
+ skb_mac_header_rebuild(skb);
+
err = 0;
out:
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index 4eeff89c1aa..8755a3079d0 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -146,7 +146,7 @@ static int __xfrm6_output(struct sk_buff *skb)
return -EMSGSIZE;
}
- if ((x && x->props.mode == XFRM_MODE_TUNNEL) &&
+ if (x->props.mode == XFRM_MODE_TUNNEL &&
((skb->len > mtu && !skb_is_gso(skb)) ||
dst_allfrag(skb_dst(skb)))) {
return ip6_fragment(skb, x->outer_mode->afinfo->output_finish);
diff --git a/net/irda/irnet/irnet.h b/net/irda/irnet/irnet.h
index 979ecb2435a..564eb0b8afa 100644
--- a/net/irda/irnet/irnet.h
+++ b/net/irda/irnet/irnet.h
@@ -254,7 +254,7 @@
#include <linux/init.h>
#include <linux/ppp_defs.h>
-#include <linux/if_ppp.h>
+#include <linux/ppp-ioctl.h>
#include <linux/ppp_channel.h>
#include <net/irda/irda.h>
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index d5c5b8fd1d0..07d7d55a1b9 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -90,6 +90,7 @@ do { \
static void iucv_sock_kill(struct sock *sk);
static void iucv_sock_close(struct sock *sk);
+static void iucv_sever_path(struct sock *, int);
static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev);
@@ -130,17 +131,6 @@ static inline void low_nmcpy(unsigned char *dst, char *src)
memcpy(&dst[8], src, 8);
}
-static void iucv_skb_queue_purge(struct sk_buff_head *list)
-{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(list)) != NULL) {
- if (skb->dev)
- dev_put(skb->dev);
- kfree_skb(skb);
- }
-}
-
static int afiucv_pm_prepare(struct device *dev)
{
#ifdef CONFIG_PM_DEBUG
@@ -175,17 +165,11 @@ static int afiucv_pm_freeze(struct device *dev)
read_lock(&iucv_sk_list.lock);
sk_for_each(sk, node, &iucv_sk_list.head) {
iucv = iucv_sk(sk);
- iucv_skb_queue_purge(&iucv->send_skb_q);
- skb_queue_purge(&iucv->backlog_skb_q);
switch (sk->sk_state) {
case IUCV_DISCONN:
case IUCV_CLOSING:
case IUCV_CONNECTED:
- if (iucv->path) {
- err = pr_iucv->path_sever(iucv->path, NULL);
- iucv_path_free(iucv->path);
- iucv->path = NULL;
- }
+ iucv_sever_path(sk, 0);
break;
case IUCV_OPEN:
case IUCV_BOUND:
@@ -194,6 +178,8 @@ static int afiucv_pm_freeze(struct device *dev)
default:
break;
}
+ skb_queue_purge(&iucv->send_skb_q);
+ skb_queue_purge(&iucv->backlog_skb_q);
}
read_unlock(&iucv_sk_list.lock);
return err;
@@ -338,7 +324,6 @@ static void iucv_sock_wake_msglim(struct sock *sk)
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;
@@ -375,10 +360,10 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
if (imsg)
memcpy(&phs_hdr->iucv_hdr, imsg, sizeof(struct iucv_message));
- skb->dev = dev_get_by_index(net, sock->sk_bound_dev_if);
+ skb->dev = iucv->hs_dev;
if (!skb->dev)
return -ENODEV;
- if (!(skb->dev->flags & IFF_UP))
+ if (!(skb->dev->flags & IFF_UP) || !netif_carrier_ok(skb->dev))
return -ENETDOWN;
if (skb->len > skb->dev->mtu) {
if (sock->sk_type == SOCK_SEQPACKET)
@@ -393,15 +378,14 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
return -ENOMEM;
skb_queue_tail(&iucv->send_skb_q, nskb);
err = dev_queue_xmit(skb);
- if (err) {
+ if (net_xmit_eval(err)) {
skb_unlink(nskb, &iucv->send_skb_q);
- dev_put(nskb->dev);
kfree_skb(nskb);
} else {
atomic_sub(confirm_recv, &iucv->msg_recv);
WARN_ON(atomic_read(&iucv->msg_recv) < 0);
}
- return err;
+ return net_xmit_eval(err);
}
static struct sock *__iucv_get_sock_by_name(char *nm)
@@ -419,7 +403,19 @@ static struct sock *__iucv_get_sock_by_name(char *nm)
static void iucv_sock_destruct(struct sock *sk)
{
skb_queue_purge(&sk->sk_receive_queue);
- skb_queue_purge(&sk->sk_write_queue);
+ skb_queue_purge(&sk->sk_error_queue);
+
+ sk_mem_reclaim(sk);
+
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ pr_err("Attempt to release alive iucv socket %p\n", sk);
+ return;
+ }
+
+ WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+ WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+ WARN_ON(sk->sk_wmem_queued);
+ WARN_ON(sk->sk_forward_alloc);
}
/* Cleanup Listen */
@@ -447,14 +443,48 @@ static void iucv_sock_kill(struct sock *sk)
sock_put(sk);
}
+/* Terminate an IUCV path */
+static void iucv_sever_path(struct sock *sk, int with_user_data)
+{
+ unsigned char user_data[16];
+ struct iucv_sock *iucv = iucv_sk(sk);
+ struct iucv_path *path = iucv->path;
+
+ if (iucv->path) {
+ iucv->path = NULL;
+ if (with_user_data) {
+ low_nmcpy(user_data, iucv->src_name);
+ high_nmcpy(user_data, iucv->dst_name);
+ ASCEBC(user_data, sizeof(user_data));
+ pr_iucv->path_sever(path, user_data);
+ } else
+ pr_iucv->path_sever(path, NULL);
+ iucv_path_free(path);
+ }
+}
+
+/* Send FIN through an IUCV socket for HIPER transport */
+static int iucv_send_ctrl(struct sock *sk, u8 flags)
+{
+ int err = 0;
+ int blen;
+ struct sk_buff *skb;
+
+ blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
+ skb = sock_alloc_send_skb(sk, blen, 1, &err);
+ if (skb) {
+ skb_reserve(skb, blen);
+ err = afiucv_hs_send(NULL, sk, skb, flags);
+ }
+ return err;
+}
+
/* Close an IUCV socket */
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;
+ int err = 0;
lock_sock(sk);
@@ -465,14 +495,7 @@ static void iucv_sock_close(struct sock *sk)
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, blen);
- err = afiucv_hs_send(NULL, sk, skb,
- AF_IUCV_FLAG_FIN);
- }
+ err = iucv_send_ctrl(sk, AF_IUCV_FLAG_FIN);
sk->sk_state = IUCV_DISCONN;
sk->sk_state_change(sk);
}
@@ -480,7 +503,7 @@ static void iucv_sock_close(struct sock *sk)
sk->sk_state = IUCV_CLOSING;
sk->sk_state_change(sk);
- if (!skb_queue_empty(&iucv->send_skb_q)) {
+ if (!err && !skb_queue_empty(&iucv->send_skb_q)) {
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
timeo = sk->sk_lingertime;
else
@@ -494,25 +517,20 @@ static void iucv_sock_close(struct sock *sk)
sk->sk_state = IUCV_CLOSED;
sk->sk_state_change(sk);
- if (iucv->path) {
- low_nmcpy(user_data, iucv->src_name);
- high_nmcpy(user_data, iucv->dst_name);
- ASCEBC(user_data, sizeof(user_data));
- pr_iucv->path_sever(iucv->path, user_data);
- iucv_path_free(iucv->path);
- iucv->path = NULL;
- }
-
sk->sk_err = ECONNRESET;
sk->sk_state_change(sk);
- iucv_skb_queue_purge(&iucv->send_skb_q);
+ skb_queue_purge(&iucv->send_skb_q);
skb_queue_purge(&iucv->backlog_skb_q);
- break;
- default:
- /* nothing to do here */
- break;
+ default: /* fall through */
+ iucv_sever_path(sk, 1);
+ }
+
+ if (iucv->hs_dev) {
+ dev_put(iucv->hs_dev);
+ iucv->hs_dev = NULL;
+ sk->sk_bound_dev_if = 0;
}
/* mark socket for deletion by iucv_sock_kill() */
@@ -706,7 +724,6 @@ static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr,
goto done_unlock;
/* Bind the socket */
-
if (pr_iucv)
if (!memcmp(sa->siucv_user_id, iucv_userid, 8))
goto vm_bind; /* VM IUCV transport */
@@ -720,6 +737,8 @@ static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr,
memcpy(iucv->src_name, sa->siucv_name, 8);
memcpy(iucv->src_user_id, sa->siucv_user_id, 8);
sk->sk_bound_dev_if = dev->ifindex;
+ iucv->hs_dev = dev;
+ dev_hold(dev);
sk->sk_state = IUCV_BOUND;
iucv->transport = AF_IUCV_TRANS_HIPER;
if (!iucv->msglimit)
@@ -780,26 +799,6 @@ static int iucv_sock_autobind(struct sock *sk)
return err;
}
-static int afiucv_hs_connect(struct socket *sock)
-{
- struct sock *sk = sock->sk;
- struct sk_buff *skb;
- int blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
- int err = 0;
-
- /* 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;
-}
-
static int afiucv_path_connect(struct socket *sock, struct sockaddr *addr)
{
struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr;
@@ -880,7 +879,7 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,
memcpy(iucv->dst_name, sa->siucv_name, 8);
if (iucv->transport == AF_IUCV_TRANS_HIPER)
- err = afiucv_hs_connect(sock);
+ err = iucv_send_ctrl(sock->sk, AF_IUCV_FLAG_SYN);
else
err = afiucv_path_connect(sock, addr);
if (err)
@@ -894,11 +893,8 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,
if (sk->sk_state == IUCV_DISCONN || sk->sk_state == IUCV_CLOSED)
err = -ECONNREFUSED;
- if (err && iucv->transport == AF_IUCV_TRANS_IUCV) {
- pr_iucv->path_sever(iucv->path, NULL);
- iucv_path_free(iucv->path);
- iucv->path = NULL;
- }
+ if (err && iucv->transport == AF_IUCV_TRANS_IUCV)
+ iucv_sever_path(sk, 0);
done:
release_sock(sk);
@@ -1124,8 +1120,10 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
noblock, &err);
else
skb = sock_alloc_send_skb(sk, len, noblock, &err);
- if (!skb)
+ if (!skb) {
+ err = -ENOMEM;
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)) {
@@ -1148,6 +1146,7 @@ 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);
@@ -1202,8 +1201,6 @@ release:
return len;
fail:
- if (skb->dev)
- dev_put(skb->dev);
kfree_skb(skb);
out:
release_sock(sk);
@@ -1332,8 +1329,7 @@ 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, *sskb;
- int blen;
+ struct sk_buff *skb, *rskb, *cskb;
int err = 0;
if ((sk->sk_state == IUCV_DISCONN) &&
@@ -1356,6 +1352,8 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
rlen = skb->len; /* real length of skb */
copied = min_t(unsigned int, rlen, len);
+ if (!rlen)
+ sk->sk_shutdown = sk->sk_shutdown | RCV_SHUTDOWN;
cskb = skb;
if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) {
@@ -1396,7 +1394,14 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
}
kfree_skb(skb);
- atomic_inc(&iucv->msg_recv);
+ if (iucv->transport == AF_IUCV_TRANS_HIPER) {
+ atomic_inc(&iucv->msg_recv);
+ if (atomic_read(&iucv->msg_recv) > iucv->msglimit) {
+ WARN_ON(1);
+ iucv_sock_close(sk);
+ return -EFAULT;
+ }
+ }
/* Queue backlog skbs */
spin_lock_bh(&iucv->message_q.lock);
@@ -1415,15 +1420,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
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, blen);
- err = afiucv_hs_send(NULL, sk, sskb,
- AF_IUCV_FLAG_WIN);
- }
+ err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
if (err) {
sk->sk_state = IUCV_DISCONN;
sk->sk_state_change(sk);
@@ -1486,7 +1483,7 @@ unsigned int iucv_sock_poll(struct file *file, struct socket *sock,
if (sk->sk_state == IUCV_DISCONN)
mask |= POLLIN;
- if (sock_writeable(sk))
+ if (sock_writeable(sk) && iucv_below_msglim(sk))
mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
else
set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
@@ -1508,42 +1505,47 @@ static int iucv_sock_shutdown(struct socket *sock, int how)
lock_sock(sk);
switch (sk->sk_state) {
+ case IUCV_LISTEN:
case IUCV_DISCONN:
case IUCV_CLOSING:
case IUCV_CLOSED:
err = -ENOTCONN;
goto fail;
-
default:
- sk->sk_shutdown |= how;
break;
}
if (how == SEND_SHUTDOWN || how == SHUTDOWN_MASK) {
- txmsg.class = 0;
- txmsg.tag = 0;
- err = pr_iucv->message_send(iucv->path, &txmsg, IUCV_IPRMDATA,
- 0, (void *) iprm_shutdown, 8);
- if (err) {
- switch (err) {
- case 1:
- err = -ENOTCONN;
- break;
- case 2:
- err = -ECONNRESET;
- break;
- default:
- err = -ENOTCONN;
- break;
+ if (iucv->transport == AF_IUCV_TRANS_IUCV) {
+ txmsg.class = 0;
+ txmsg.tag = 0;
+ err = pr_iucv->message_send(iucv->path, &txmsg,
+ IUCV_IPRMDATA, 0, (void *) iprm_shutdown, 8);
+ if (err) {
+ switch (err) {
+ case 1:
+ err = -ENOTCONN;
+ break;
+ case 2:
+ err = -ECONNRESET;
+ break;
+ default:
+ err = -ENOTCONN;
+ break;
+ }
}
- }
+ } else
+ iucv_send_ctrl(sk, AF_IUCV_FLAG_SHT);
}
+ sk->sk_shutdown |= how;
if (how == RCV_SHUTDOWN || how == SHUTDOWN_MASK) {
- err = pr_iucv->path_quiesce(iucv->path, NULL);
- if (err)
- err = -ENOTCONN;
-
+ if (iucv->transport == AF_IUCV_TRANS_IUCV) {
+ err = pr_iucv->path_quiesce(iucv->path, NULL);
+ if (err)
+ err = -ENOTCONN;
+/* skb_queue_purge(&sk->sk_receive_queue); */
+ }
skb_queue_purge(&sk->sk_receive_queue);
}
@@ -1565,13 +1567,6 @@ static int iucv_sock_release(struct socket *sock)
iucv_sock_close(sk);
- /* Unregister with IUCV base support */
- if (iucv_sk(sk)->path) {
- pr_iucv->path_sever(iucv_sk(sk)->path, NULL);
- iucv_path_free(iucv_sk(sk)->path);
- iucv_sk(sk)->path = NULL;
- }
-
sock_orphan(sk);
iucv_sock_kill(sk);
return err;
@@ -1633,7 +1628,8 @@ static int iucv_sock_getsockopt(struct socket *sock, int level, int optname,
{
struct sock *sk = sock->sk;
struct iucv_sock *iucv = iucv_sk(sk);
- int val, len;
+ unsigned int val;
+ int len;
if (level != SOL_IUCV)
return -ENOPROTOOPT;
@@ -1656,6 +1652,13 @@ static int iucv_sock_getsockopt(struct socket *sock, int level, int optname,
: iucv->msglimit; /* default */
release_sock(sk);
break;
+ case SO_MSGSIZE:
+ if (sk->sk_state == IUCV_OPEN)
+ return -EBADFD;
+ val = (iucv->hs_dev) ? iucv->hs_dev->mtu -
+ sizeof(struct af_iucv_trans_hdr) - ETH_HLEN :
+ 0x7fffffff;
+ break;
default:
return -ENOPROTOOPT;
}
@@ -1750,8 +1753,7 @@ static int iucv_callback_connreq(struct iucv_path *path,
path->msglim = iucv->msglimit;
err = pr_iucv->path_accept(path, &af_iucv_handler, nuser_data, nsk);
if (err) {
- err = pr_iucv->path_sever(path, user_data);
- iucv_path_free(path);
+ iucv_sever_path(nsk, 1);
iucv_sock_kill(nsk);
goto fail;
}
@@ -1828,6 +1830,7 @@ static void iucv_callback_txdone(struct iucv_path *path,
struct sk_buff *list_skb = list->next;
unsigned long flags;
+ bh_lock_sock(sk);
if (!skb_queue_empty(list)) {
spin_lock_irqsave(&list->lock, flags);
@@ -1849,7 +1852,6 @@ static void iucv_callback_txdone(struct iucv_path *path,
iucv_sock_wake_msglim(sk);
}
}
- BUG_ON(!this);
if (sk->sk_state == IUCV_CLOSING) {
if (skb_queue_empty(&iucv_sk(sk)->send_skb_q)) {
@@ -1857,6 +1859,7 @@ static void iucv_callback_txdone(struct iucv_path *path,
sk->sk_state_change(sk);
}
}
+ bh_unlock_sock(sk);
}
@@ -1864,9 +1867,15 @@ static void iucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
{
struct sock *sk = path->private;
+ if (sk->sk_state == IUCV_CLOSED)
+ return;
+
+ bh_lock_sock(sk);
+ iucv_sever_path(sk, 1);
sk->sk_state = IUCV_DISCONN;
sk->sk_state_change(sk);
+ bh_unlock_sock(sk);
}
/* called if the other communication side shuts down its RECV direction;
@@ -1954,6 +1963,8 @@ static int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb)
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;
+ niucv->hs_dev = iucv->hs_dev;
+ dev_hold(niucv->hs_dev);
afiucv_swap_src_dest(skb);
trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_ACK;
trans_hdr->window = niucv->msglimit;
@@ -2022,12 +2033,15 @@ 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 (!iucv)
+ goto out;
+ bh_lock_sock(sk);
+ if (sk->sk_state == IUCV_CONNECTED) {
sk->sk_state = IUCV_DISCONN;
sk->sk_state_change(sk);
- bh_unlock_sock(sk);
}
+ bh_unlock_sock(sk);
+out:
kfree_skb(skb);
return NET_RX_SUCCESS;
}
@@ -2069,8 +2083,13 @@ static int afiucv_hs_callback_rx(struct sock *sk, struct sk_buff *skb)
return NET_RX_SUCCESS;
}
+ if (sk->sk_shutdown & RCV_SHUTDOWN) {
+ kfree_skb(skb);
+ return NET_RX_SUCCESS;
+ }
+
/* write stuff from iucv_msg to skb cb */
- if (skb->len <= sizeof(struct af_iucv_trans_hdr)) {
+ if (skb->len < sizeof(struct af_iucv_trans_hdr)) {
kfree_skb(skb);
return NET_RX_SUCCESS;
}
@@ -2172,11 +2191,14 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
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;
+ if (skb->len == sizeof(struct af_iucv_trans_hdr)) {
+ kfree_skb(skb);
+ break;
+ }
+ /* fall through and receive non-zero length data */
+ case (AF_IUCV_FLAG_SHT):
+ /* shutdown request */
+ /* fall through and receive zero length data */
case 0:
/* plain data frame */
memcpy(CB_TRGCLS(skb), &trans_hdr->iucv_hdr.class,
@@ -2202,65 +2224,64 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb,
struct iucv_sock *iucv = NULL;
struct sk_buff_head *list;
struct sk_buff *list_skb;
- struct sk_buff *this = NULL;
+ struct sk_buff *nskb;
unsigned long flags;
struct hlist_node *node;
- read_lock(&iucv_sk_list.lock);
+ read_lock_irqsave(&iucv_sk_list.lock, flags);
sk_for_each(sk, node, &iucv_sk_list.head)
if (sk == isk) {
iucv = iucv_sk(sk);
break;
}
- read_unlock(&iucv_sk_list.lock);
+ read_unlock_irqrestore(&iucv_sk_list.lock, flags);
- if (!iucv)
+ if (!iucv || sock_flag(sk, SOCK_ZAPPED))
return;
- bh_lock_sock(sk);
list = &iucv->send_skb_q;
- list_skb = list->next;
+ spin_lock_irqsave(&list->lock, flags);
if (skb_queue_empty(list))
goto out_unlock;
-
- spin_lock_irqsave(&list->lock, flags);
+ list_skb = list->next;
+ nskb = list_skb->next;
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);
+ __skb_unlink(list_skb, list);
+ kfree_skb(list_skb);
iucv_sock_wake_msglim(sk);
- dev_put(this->dev);
- kfree_skb(this);
break;
case TX_NOTIFY_PENDING:
atomic_inc(&iucv->pendings);
break;
case TX_NOTIFY_DELAYED_OK:
- __skb_unlink(this, list);
+ __skb_unlink(list_skb, list);
atomic_dec(&iucv->pendings);
if (atomic_read(&iucv->pendings) <= 0)
iucv_sock_wake_msglim(sk);
- dev_put(this->dev);
- kfree_skb(this);
+ kfree_skb(list_skb);
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);
- dev_put(this->dev);
- kfree_skb(this);
- sk->sk_state = IUCV_DISCONN;
- sk->sk_state_change(sk);
+ __skb_unlink(list_skb, list);
+ kfree_skb(list_skb);
+ if (sk->sk_state == IUCV_CONNECTED) {
+ sk->sk_state = IUCV_DISCONN;
+ sk->sk_state_change(sk);
+ }
break;
}
break;
}
- list_skb = list_skb->next;
+ list_skb = nskb;
+ nskb = nskb->next;
}
+out_unlock:
spin_unlock_irqrestore(&list->lock, flags);
if (sk->sk_state == IUCV_CLOSING) {
@@ -2270,9 +2291,45 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb,
}
}
-out_unlock:
- bh_unlock_sock(sk);
}
+
+/*
+ * afiucv_netdev_event: handle netdev notifier chain events
+ */
+static int afiucv_netdev_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct net_device *event_dev = (struct net_device *)ptr;
+ struct hlist_node *node;
+ struct sock *sk;
+ struct iucv_sock *iucv;
+
+ switch (event) {
+ case NETDEV_REBOOT:
+ case NETDEV_GOING_DOWN:
+ sk_for_each(sk, node, &iucv_sk_list.head) {
+ iucv = iucv_sk(sk);
+ if ((iucv->hs_dev == event_dev) &&
+ (sk->sk_state == IUCV_CONNECTED)) {
+ if (event == NETDEV_GOING_DOWN)
+ iucv_send_ctrl(sk, AF_IUCV_FLAG_FIN);
+ sk->sk_state = IUCV_DISCONN;
+ sk->sk_state_change(sk);
+ }
+ }
+ break;
+ case NETDEV_DOWN:
+ case NETDEV_UNREGISTER:
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block afiucv_netdev_notifier = {
+ .notifier_call = afiucv_netdev_event,
+};
+
static const struct proto_ops iucv_sock_ops = {
.family = PF_IUCV,
.owner = THIS_MODULE,
@@ -2372,7 +2429,8 @@ static int __init afiucv_init(void)
err = afiucv_iucv_init();
if (err)
goto out_sock;
- }
+ } else
+ register_netdevice_notifier(&afiucv_netdev_notifier);
dev_add_pack(&iucv_packet_type);
return 0;
@@ -2393,7 +2451,8 @@ static void __exit afiucv_exit(void)
driver_unregister(&af_iucv_driver);
pr_iucv->iucv_unregister(&af_iucv_handler, 0);
symbol_put(iucv_if);
- }
+ } else
+ unregister_netdevice_notifier(&afiucv_netdev_notifier);
dev_remove_pack(&iucv_packet_type);
sock_unregister(PF_IUCV);
proto_unregister(&iucv_proto);
diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c
index d2726a74597..63fe5f353f0 100644
--- a/net/l2tp/l2tp_eth.c
+++ b/net/l2tp/l2tp_eth.c
@@ -64,7 +64,7 @@ static int l2tp_eth_dev_init(struct net_device *dev)
struct l2tp_eth *priv = netdev_priv(dev);
priv->dev = dev;
- random_ether_addr(dev->dev_addr);
+ eth_hw_addr_random(dev);
memset(&dev->broadcast[0], 0xff, 6);
return 0;
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 8a90d756c90..96bc7a67585 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -82,7 +82,7 @@
#include <net/sock.h>
#include <linux/ppp_channel.h>
#include <linux/ppp_defs.h>
-#include <linux/if_ppp.h>
+#include <linux/ppp-ioctl.h>
#include <linux/file.h>
#include <linux/hash.h>
#include <linux/sort.h>
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index f8ac4ef0b79..0c6f67e8f2e 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -103,6 +103,16 @@ config NF_CONNTRACK_EVENTS
If unsure, say `N'.
+config NF_CONNTRACK_TIMEOUT
+ bool 'Connection tracking timeout'
+ depends on NETFILTER_ADVANCED
+ help
+ This option enables support for connection tracking timeout
+ extension. This allows you to attach timeout policies to flow
+ via the CT target.
+
+ If unsure, say `N'.
+
config NF_CONNTRACK_TIMESTAMP
bool 'Connection tracking timestamping'
depends on NETFILTER_ADVANCED
@@ -314,6 +324,17 @@ config NF_CT_NETLINK
help
This option enables support for a netlink-based userspace interface
+config NF_CT_NETLINK_TIMEOUT
+ tristate 'Connection tracking timeout tuning via Netlink'
+ select NETFILTER_NETLINK
+ depends on NETFILTER_ADVANCED
+ help
+ This option enables support for connection tracking timeout
+ fine-grain tuning. This allows you to attach specific timeout
+ policies to flows, instead of using the global timeout policy.
+
+ If unsure, say `N'.
+
endif # NF_CONNTRACK
# transparent proxy support
@@ -524,6 +545,15 @@ config NETFILTER_XT_TARGET_LED
For more information on the LEDs available on your system, see
Documentation/leds/leds-class.txt
+config NETFILTER_XT_TARGET_LOG
+ tristate "LOG target support"
+ default m if NETFILTER_ADVANCED=n
+ help
+ This option adds a `LOG' target, which allows you to create rules in
+ any iptables table which records the packet header to the syslog.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
config NETFILTER_XT_TARGET_MARK
tristate '"MARK" target support'
depends on NETFILTER_ADVANCED
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 40f4c3d636c..ca3676586f5 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -1,6 +1,7 @@
netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o
nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o
+nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o
nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o
nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o
@@ -22,6 +23,7 @@ obj-$(CONFIG_NF_CT_PROTO_UDPLITE) += nf_conntrack_proto_udplite.o
# netlink interface for nf_conntrack
obj-$(CONFIG_NF_CT_NETLINK) += nf_conntrack_netlink.o
+obj-$(CONFIG_NF_CT_NETLINK_TIMEOUT) += nfnetlink_cttimeout.o
# connection tracking helpers
nf_conntrack_h323-objs := nf_conntrack_h323_main.o nf_conntrack_h323_asn1.o
@@ -58,6 +60,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
+obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o
obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o
obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o
obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o
diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c
index e3e73997c3b..a72a4dff003 100644
--- a/net/netfilter/ipset/ip_set_bitmap_ip.c
+++ b/net/netfilter/ipset/ip_set_bitmap_ip.c
@@ -442,7 +442,7 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
map->timeout = IPSET_NO_TIMEOUT;
set->data = map;
- set->family = AF_INET;
+ set->family = NFPROTO_IPV4;
return true;
}
@@ -550,7 +550,7 @@ static struct ip_set_type bitmap_ip_type __read_mostly = {
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE,
- .family = AF_INET,
+ .family = NFPROTO_IPV4,
.revision_min = 0,
.revision_max = 0,
.create = bitmap_ip_create,
diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c
index 56096f54497..81324c12c5b 100644
--- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c
+++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c
@@ -543,7 +543,7 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
map->timeout = IPSET_NO_TIMEOUT;
set->data = map;
- set->family = AF_INET;
+ set->family = NFPROTO_IPV4;
return true;
}
@@ -623,7 +623,7 @@ static struct ip_set_type bitmap_ipmac_type = {
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_MAC,
.dimension = IPSET_DIM_TWO,
- .family = AF_INET,
+ .family = NFPROTO_IPV4,
.revision_min = 0,
.revision_max = 0,
.create = bitmap_ipmac_create,
diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c
index 29ba93bb94b..382ec28ba72 100644
--- a/net/netfilter/ipset/ip_set_bitmap_port.c
+++ b/net/netfilter/ipset/ip_set_bitmap_port.c
@@ -422,7 +422,7 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
map->timeout = IPSET_NO_TIMEOUT;
set->data = map;
- set->family = AF_UNSPEC;
+ set->family = NFPROTO_UNSPEC;
return true;
}
@@ -483,7 +483,7 @@ static struct ip_set_type bitmap_port_type = {
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_PORT,
.dimension = IPSET_DIM_ONE,
- .family = AF_UNSPEC,
+ .family = NFPROTO_UNSPEC,
.revision_min = 0,
.revision_max = 0,
.create = bitmap_port_create,
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index 32dbf0fa89d..e6c1c9605a5 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -69,7 +69,7 @@ find_set_type(const char *name, u8 family, u8 revision)
list_for_each_entry_rcu(type, &ip_set_type_list, list)
if (STREQ(type->name, name) &&
- (type->family == family || type->family == AF_UNSPEC) &&
+ (type->family == family || type->family == NFPROTO_UNSPEC) &&
revision >= type->revision_min &&
revision <= type->revision_max)
return type;
@@ -149,7 +149,7 @@ __find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max,
rcu_read_lock();
list_for_each_entry_rcu(type, &ip_set_type_list, list)
if (STREQ(type->name, name) &&
- (type->family == family || type->family == AF_UNSPEC)) {
+ (type->family == family || type->family == NFPROTO_UNSPEC)) {
found = true;
if (type->revision_min < *min)
*min = type->revision_min;
@@ -164,8 +164,8 @@ __find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max,
__find_set_type_minmax(name, family, min, max, true);
}
-#define family_name(f) ((f) == AF_INET ? "inet" : \
- (f) == AF_INET6 ? "inet6" : "any")
+#define family_name(f) ((f) == NFPROTO_IPV4 ? "inet" : \
+ (f) == NFPROTO_IPV6 ? "inet6" : "any")
/* Register a set type structure. The type is identified by
* the unique triple of name, family and revision.
@@ -354,7 +354,7 @@ ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
pr_debug("set %s, index %u\n", set->name, index);
if (opt->dim < set->type->dimension ||
- !(opt->family == set->family || set->family == AF_UNSPEC))
+ !(opt->family == set->family || set->family == NFPROTO_UNSPEC))
return 0;
read_lock_bh(&set->lock);
@@ -387,7 +387,7 @@ ip_set_add(ip_set_id_t index, const struct sk_buff *skb,
pr_debug("set %s, index %u\n", set->name, index);
if (opt->dim < set->type->dimension ||
- !(opt->family == set->family || set->family == AF_UNSPEC))
+ !(opt->family == set->family || set->family == NFPROTO_UNSPEC))
return 0;
write_lock_bh(&set->lock);
@@ -410,7 +410,7 @@ ip_set_del(ip_set_id_t index, const struct sk_buff *skb,
pr_debug("set %s, index %u\n", set->name, index);
if (opt->dim < set->type->dimension ||
- !(opt->family == set->family || set->family == AF_UNSPEC))
+ !(opt->family == set->family || set->family == NFPROTO_UNSPEC))
return 0;
write_lock_bh(&set->lock);
@@ -575,7 +575,7 @@ start_msg(struct sk_buff *skb, u32 pid, u32 seq, unsigned int flags,
return NULL;
nfmsg = nlmsg_data(nlh);
- nfmsg->nfgen_family = AF_INET;
+ nfmsg->nfgen_family = NFPROTO_IPV4;
nfmsg->version = NFNETLINK_V0;
nfmsg->res_id = 0;
@@ -1162,9 +1162,13 @@ ip_set_dump(struct sock *ctnl, struct sk_buff *skb,
if (unlikely(protocol_failed(attr)))
return -IPSET_ERR_PROTOCOL;
- return netlink_dump_start(ctnl, skb, nlh,
- ip_set_dump_start,
- ip_set_dump_done, 0);
+ {
+ struct netlink_dump_control c = {
+ .dump = ip_set_dump_start,
+ .done = ip_set_dump_done,
+ };
+ return netlink_dump_start(ctnl, skb, nlh, &c);
+ }
}
/* Add, del and test */
diff --git a/net/netfilter/ipset/ip_set_getport.c b/net/netfilter/ipset/ip_set_getport.c
index 1f03556666f..6fdf88ae235 100644
--- a/net/netfilter/ipset/ip_set_getport.c
+++ b/net/netfilter/ipset/ip_set_getport.c
@@ -136,10 +136,10 @@ ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port)
u8 proto;
switch (pf) {
- case AF_INET:
+ case NFPROTO_IPV4:
ret = ip_set_get_ip4_port(skb, src, port, &proto);
break;
- case AF_INET6:
+ case NFPROTO_IPV6:
ret = ip_set_get_ip6_port(skb, src, port, &proto);
break;
default:
diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c
index 4015fcaf87b..5139dea6019 100644
--- a/net/netfilter/ipset/ip_set_hash_ip.c
+++ b/net/netfilter/ipset/ip_set_hash_ip.c
@@ -366,11 +366,11 @@ hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
u8 netmask, hbits;
struct ip_set_hash *h;
- if (!(set->family == AF_INET || set->family == AF_INET6))
+ if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
- netmask = set->family == AF_INET ? 32 : 128;
+ netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
pr_debug("Create set %s with family %s\n",
- set->name, set->family == AF_INET ? "inet" : "inet6");
+ set->name, set->family == NFPROTO_IPV4 ? "inet" : "inet6");
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
@@ -389,8 +389,8 @@ hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
if (tb[IPSET_ATTR_NETMASK]) {
netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
- if ((set->family == AF_INET && netmask > 32) ||
- (set->family == AF_INET6 && netmask > 128) ||
+ if ((set->family == NFPROTO_IPV4 && netmask > 32) ||
+ (set->family == NFPROTO_IPV6 && netmask > 128) ||
netmask == 0)
return -IPSET_ERR_INVALID_NETMASK;
}
@@ -419,15 +419,15 @@ hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_ip4_tvariant : &hash_ip6_tvariant;
- if (set->family == AF_INET)
+ if (set->family == NFPROTO_IPV4)
hash_ip4_gc_init(set);
else
hash_ip6_gc_init(set);
} else {
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_ip4_variant : &hash_ip6_variant;
}
@@ -443,7 +443,7 @@ static struct ip_set_type hash_ip_type __read_mostly = {
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE,
- .family = AF_UNSPEC,
+ .family = NFPROTO_UNSPEC,
.revision_min = 0,
.revision_max = 0,
.create = hash_ip_create,
diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c
index 37d667e3f6f..9c27e249c17 100644
--- a/net/netfilter/ipset/ip_set_hash_ipport.c
+++ b/net/netfilter/ipset/ip_set_hash_ipport.c
@@ -450,7 +450,7 @@ hash_ipport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
- if (!(set->family == AF_INET || set->family == AF_INET6))
+ if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
@@ -490,15 +490,15 @@ hash_ipport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_ipport4_tvariant : &hash_ipport6_tvariant;
- if (set->family == AF_INET)
+ if (set->family == NFPROTO_IPV4)
hash_ipport4_gc_init(set);
else
hash_ipport6_gc_init(set);
} else {
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_ipport4_variant : &hash_ipport6_variant;
}
@@ -514,7 +514,7 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT,
.dimension = IPSET_DIM_TWO,
- .family = AF_UNSPEC,
+ .family = NFPROTO_UNSPEC,
.revision_min = 0,
.revision_max = 1, /* SCTP and UDPLITE support added */
.create = hash_ipport_create,
diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c
index e69e2718fbe..9134057c072 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportip.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportip.c
@@ -468,7 +468,7 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
- if (!(set->family == AF_INET || set->family == AF_INET6))
+ if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
@@ -508,15 +508,15 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_ipportip4_tvariant : &hash_ipportip6_tvariant;
- if (set->family == AF_INET)
+ if (set->family == NFPROTO_IPV4)
hash_ipportip4_gc_init(set);
else
hash_ipportip6_gc_init(set);
} else {
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_ipportip4_variant : &hash_ipportip6_variant;
}
@@ -532,7 +532,7 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
.dimension = IPSET_DIM_THREE,
- .family = AF_UNSPEC,
+ .family = NFPROTO_UNSPEC,
.revision_min = 0,
.revision_max = 1, /* SCTP and UDPLITE support added */
.create = hash_ipportip_create,
diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c
index 64199b4e93c..5d05e696986 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -41,12 +41,19 @@ hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b);
/* The type variant functions: IPv4 */
+/* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0
+ * However this way we have to store internally cidr - 1,
+ * dancing back and forth.
+ */
+#define IP_SET_HASH_WITH_NETS_PACKED
+
/* Member elements without timeout */
struct hash_ipportnet4_elem {
__be32 ip;
__be32 ip2;
__be16 port;
- u8 cidr;
+ u8 cidr:7;
+ u8 nomatch:1;
u8 proto;
};
@@ -55,7 +62,8 @@ struct hash_ipportnet4_telem {
__be32 ip;
__be32 ip2;
__be16 port;
- u8 cidr;
+ u8 cidr:7;
+ u8 nomatch:1;
u8 proto;
unsigned long timeout;
};
@@ -86,10 +94,22 @@ hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst,
}
static inline void
+hash_ipportnet4_data_flags(struct hash_ipportnet4_elem *dst, u32 flags)
+{
+ dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
+}
+
+static inline bool
+hash_ipportnet4_data_match(const struct hash_ipportnet4_elem *elem)
+{
+ return !elem->nomatch;
+}
+
+static inline void
hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr)
{
elem->ip2 &= ip_set_netmask(cidr);
- elem->cidr = cidr;
+ elem->cidr = cidr - 1;
}
static inline void
@@ -102,11 +122,15 @@ static bool
hash_ipportnet4_data_list(struct sk_buff *skb,
const struct hash_ipportnet4_elem *data)
{
+ u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
- NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ if (flags)
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
nla_put_failure:
@@ -119,14 +143,17 @@ hash_ipportnet4_data_tlist(struct sk_buff *skb,
{
const struct hash_ipportnet4_telem *tdata =
(const struct hash_ipportnet4_telem *)data;
+ u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, tdata->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
- NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
+ if (flags)
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
@@ -158,13 +185,11 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data = {
- .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+ .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
};
- if (data.cidr == 0)
- return -EINVAL;
if (adt == IPSET_TEST)
- data.cidr = HOST_MASK;
+ data.cidr = HOST_MASK - 1;
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
@@ -172,7 +197,7 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2);
- data.ip2 &= ip_set_netmask(data.cidr);
+ data.ip2 &= ip_set_netmask(data.cidr + 1);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
@@ -183,17 +208,19 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportnet4_elem data = { .cidr = HOST_MASK };
+ struct hash_ipportnet4_elem data = { .cidr = HOST_MASK - 1 };
u32 ip, ip_to = 0, p = 0, port, port_to;
u32 ip2_from = 0, ip2_to, ip2_last, ip2;
u32 timeout = h->timeout;
bool with_ports = false;
+ u8 cidr;
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
- !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
@@ -208,9 +235,10 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
if (tb[IPSET_ATTR_CIDR2]) {
- data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
- if (!data.cidr)
+ cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+ if (!cidr || cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
+ data.cidr = cidr - 1;
}
if (tb[IPSET_ATTR_PORT])
@@ -236,12 +264,18 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
+ if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+ u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+ if (cadt_flags & IPSET_FLAG_NOMATCH)
+ flags |= (cadt_flags << 16);
+ }
+
with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
if (adt == IPSET_TEST ||
!(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports ||
tb[IPSET_ATTR_IP2_TO])) {
data.ip = htonl(ip);
- data.ip2 = htonl(ip2_from & ip_set_hostmask(data.cidr));
+ data.ip2 = htonl(ip2_from & ip_set_hostmask(data.cidr + 1));
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
@@ -275,7 +309,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
if (ip2_from + UINT_MAX == ip2_to)
return -IPSET_ERR_HASH_RANGE;
} else {
- ip_set_mask_from_to(ip2_from, ip2_to, data.cidr);
+ ip_set_mask_from_to(ip2_from, ip2_to, data.cidr + 1);
}
if (retried)
@@ -290,7 +324,8 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
while (!after(ip2, ip2_to)) {
data.ip2 = htonl(ip2);
ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
- &data.cidr);
+ &cidr);
+ data.cidr = cidr - 1;
ret = adtfn(set, &data, timeout, flags);
if (ret && !ip_set_eexist(ret, flags))
@@ -321,7 +356,8 @@ struct hash_ipportnet6_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
__be16 port;
- u8 cidr;
+ u8 cidr:7;
+ u8 nomatch:1;
u8 proto;
};
@@ -329,7 +365,8 @@ struct hash_ipportnet6_telem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
__be16 port;
- u8 cidr;
+ u8 cidr:7;
+ u8 nomatch:1;
u8 proto;
unsigned long timeout;
};
@@ -360,6 +397,18 @@ hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst,
}
static inline void
+hash_ipportnet6_data_flags(struct hash_ipportnet6_elem *dst, u32 flags)
+{
+ dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
+}
+
+static inline bool
+hash_ipportnet6_data_match(const struct hash_ipportnet6_elem *elem)
+{
+ return !elem->nomatch;
+}
+
+static inline void
hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem)
{
elem->proto = 0;
@@ -378,18 +427,22 @@ static inline void
hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr)
{
ip6_netmask(&elem->ip2, cidr);
- elem->cidr = cidr;
+ elem->cidr = cidr - 1;
}
static bool
hash_ipportnet6_data_list(struct sk_buff *skb,
const struct hash_ipportnet6_elem *data)
{
+ u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
- NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ if (flags)
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
nla_put_failure:
@@ -402,14 +455,17 @@ hash_ipportnet6_data_tlist(struct sk_buff *skb,
{
const struct hash_ipportnet6_telem *e =
(const struct hash_ipportnet6_telem *)data;
+ u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
- NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout)));
+ if (flags)
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
nla_put_failure:
@@ -438,13 +494,11 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem data = {
- .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+ .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
};
- if (data.cidr == 0)
- return -EINVAL;
if (adt == IPSET_TEST)
- data.cidr = HOST_MASK;
+ data.cidr = HOST_MASK - 1;
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
@@ -452,7 +506,7 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
- ip6_netmask(&data.ip2, data.cidr);
+ ip6_netmask(&data.ip2, data.cidr + 1);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
@@ -463,16 +517,18 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportnet6_elem data = { .cidr = HOST_MASK };
+ struct hash_ipportnet6_elem data = { .cidr = HOST_MASK - 1 };
u32 port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
+ u8 cidr;
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_CIDR]))
return -IPSET_ERR_PROTOCOL;
@@ -490,13 +546,14 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
if (ret)
return ret;
- if (tb[IPSET_ATTR_CIDR2])
- data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
-
- if (!data.cidr)
- return -IPSET_ERR_INVALID_CIDR;
+ if (tb[IPSET_ATTR_CIDR2]) {
+ cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+ if (!cidr || cidr > HOST_MASK)
+ return -IPSET_ERR_INVALID_CIDR;
+ data.cidr = cidr - 1;
+ }
- ip6_netmask(&data.ip2, data.cidr);
+ ip6_netmask(&data.ip2, data.cidr + 1);
if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
@@ -521,6 +578,12 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
+ if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+ u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+ if (cadt_flags & IPSET_FLAG_NOMATCH)
+ flags |= (cadt_flags << 16);
+ }
+
if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
@@ -554,7 +617,7 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
- if (!(set->family == AF_INET || set->family == AF_INET6))
+ if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
@@ -573,7 +636,7 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
h = kzalloc(sizeof(*h)
+ sizeof(struct ip_set_hash_nets)
- * (set->family == AF_INET ? 32 : 128), GFP_KERNEL);
+ * (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL);
if (!h)
return -ENOMEM;
@@ -596,16 +659,16 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_ipportnet4_tvariant
: &hash_ipportnet6_tvariant;
- if (set->family == AF_INET)
+ if (set->family == NFPROTO_IPV4)
hash_ipportnet4_gc_init(set);
else
hash_ipportnet6_gc_init(set);
} else {
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_ipportnet4_variant : &hash_ipportnet6_variant;
}
@@ -621,10 +684,11 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
.dimension = IPSET_DIM_THREE,
- .family = AF_UNSPEC,
+ .family = NFPROTO_UNSPEC,
.revision_min = 0,
/* 1 SCTP and UDPLITE support added */
- .revision_max = 2, /* Range as input support for IPv4 added */
+ /* 2 Range as input support for IPv4 added */
+ .revision_max = 3, /* nomatch flag support added */
.create = hash_ipportnet_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
@@ -643,6 +707,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_CIDR2] = { .type = NLA_U8 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
+ [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
},
diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c
index 28988196775..7c3d945517c 100644
--- a/net/netfilter/ipset/ip_set_hash_net.c
+++ b/net/netfilter/ipset/ip_set_hash_net.c
@@ -43,7 +43,7 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b);
struct hash_net4_elem {
__be32 ip;
u16 padding0;
- u8 padding1;
+ u8 nomatch;
u8 cidr;
};
@@ -51,7 +51,7 @@ struct hash_net4_elem {
struct hash_net4_telem {
__be32 ip;
u16 padding0;
- u8 padding1;
+ u8 nomatch;
u8 cidr;
unsigned long timeout;
};
@@ -61,7 +61,8 @@ hash_net4_data_equal(const struct hash_net4_elem *ip1,
const struct hash_net4_elem *ip2,
u32 *multi)
{
- return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr;
+ return ip1->ip == ip2->ip &&
+ ip1->cidr == ip2->cidr;
}
static inline bool
@@ -76,6 +77,19 @@ hash_net4_data_copy(struct hash_net4_elem *dst,
{
dst->ip = src->ip;
dst->cidr = src->cidr;
+ dst->nomatch = src->nomatch;
+}
+
+static inline void
+hash_net4_data_flags(struct hash_net4_elem *dst, u32 flags)
+{
+ dst->nomatch = flags & IPSET_FLAG_NOMATCH;
+}
+
+static inline bool
+hash_net4_data_match(const struct hash_net4_elem *elem)
+{
+ return !elem->nomatch;
}
static inline void
@@ -95,8 +109,12 @@ hash_net4_data_zero_out(struct hash_net4_elem *elem)
static bool
hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data)
{
+ u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+ if (flags)
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
nla_put_failure:
@@ -108,11 +126,14 @@ hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data)
{
const struct hash_net4_telem *tdata =
(const struct hash_net4_telem *)data;
+ u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, tdata->cidr);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
+ if (flags)
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
@@ -167,7 +188,8 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
- !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
@@ -179,7 +201,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
if (tb[IPSET_ATTR_CIDR]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
- if (!data.cidr)
+ if (!data.cidr || data.cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
}
@@ -189,6 +211,12 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
+ if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+ u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+ if (cadt_flags & IPSET_FLAG_NOMATCH)
+ flags |= (cadt_flags << 16);
+ }
+
if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
data.ip = htonl(ip & ip_set_hostmask(data.cidr));
ret = adtfn(set, &data, timeout, flags);
@@ -236,14 +264,14 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b)
struct hash_net6_elem {
union nf_inet_addr ip;
u16 padding0;
- u8 padding1;
+ u8 nomatch;
u8 cidr;
};
struct hash_net6_telem {
union nf_inet_addr ip;
u16 padding0;
- u8 padding1;
+ u8 nomatch;
u8 cidr;
unsigned long timeout;
};
@@ -269,6 +297,19 @@ hash_net6_data_copy(struct hash_net6_elem *dst,
{
dst->ip.in6 = src->ip.in6;
dst->cidr = src->cidr;
+ dst->nomatch = src->nomatch;
+}
+
+static inline void
+hash_net6_data_flags(struct hash_net6_elem *dst, u32 flags)
+{
+ dst->nomatch = flags & IPSET_FLAG_NOMATCH;
+}
+
+static inline bool
+hash_net6_data_match(const struct hash_net6_elem *elem)
+{
+ return !elem->nomatch;
}
static inline void
@@ -296,8 +337,12 @@ hash_net6_data_netmask(struct hash_net6_elem *elem, u8 cidr)
static bool
hash_net6_data_list(struct sk_buff *skb, const struct hash_net6_elem *data)
{
+ u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+ if (flags)
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
nla_put_failure:
@@ -309,11 +354,14 @@ hash_net6_data_tlist(struct sk_buff *skb, const struct hash_net6_elem *data)
{
const struct hash_net6_telem *e =
(const struct hash_net6_telem *)data;
+ u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, e->cidr);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout)));
+ if (flags)
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
nla_put_failure:
@@ -366,7 +414,8 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
- !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
return -IPSET_ERR_PROTOCOL;
if (unlikely(tb[IPSET_ATTR_IP_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
@@ -381,7 +430,7 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
if (tb[IPSET_ATTR_CIDR])
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
- if (!data.cidr)
+ if (!data.cidr || data.cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
ip6_netmask(&data.ip, data.cidr);
@@ -392,6 +441,12 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
+ if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+ u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+ if (cadt_flags & IPSET_FLAG_NOMATCH)
+ flags |= (cadt_flags << 16);
+ }
+
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
@@ -406,7 +461,7 @@ hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
struct ip_set_hash *h;
u8 hbits;
- if (!(set->family == AF_INET || set->family == AF_INET6))
+ if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
@@ -425,7 +480,7 @@ hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
h = kzalloc(sizeof(*h)
+ sizeof(struct ip_set_hash_nets)
- * (set->family == AF_INET ? 32 : 128), GFP_KERNEL);
+ * (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL);
if (!h)
return -ENOMEM;
@@ -448,15 +503,15 @@ hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_net4_tvariant : &hash_net6_tvariant;
- if (set->family == AF_INET)
+ if (set->family == NFPROTO_IPV4)
hash_net4_gc_init(set);
else
hash_net6_gc_init(set);
} else {
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_net4_variant : &hash_net6_variant;
}
@@ -472,9 +527,10 @@ static struct ip_set_type hash_net_type __read_mostly = {
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP,
.dimension = IPSET_DIM_ONE,
- .family = AF_UNSPEC,
+ .family = NFPROTO_UNSPEC,
.revision_min = 0,
- .revision_max = 1, /* Range as input support for IPv4 added */
+ /* = 1 Range as input support for IPv4 added */
+ .revision_max = 2, /* nomatch flag support added */
.create = hash_net_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
@@ -488,6 +544,7 @@ static struct ip_set_type hash_net_type __read_mostly = {
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.me = THIS_MODULE,
};
diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c
index e13095deb50..f24037ff432 100644
--- a/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -163,7 +163,8 @@ struct hash_netiface4_elem_hashed {
__be32 ip;
u8 physdev;
u8 cidr;
- u16 padding;
+ u8 nomatch;
+ u8 padding;
};
#define HKEY_DATALEN sizeof(struct hash_netiface4_elem_hashed)
@@ -173,7 +174,8 @@ struct hash_netiface4_elem {
__be32 ip;
u8 physdev;
u8 cidr;
- u16 padding;
+ u8 nomatch;
+ u8 padding;
const char *iface;
};
@@ -182,7 +184,8 @@ struct hash_netiface4_telem {
__be32 ip;
u8 physdev;
u8 cidr;
- u16 padding;
+ u8 nomatch;
+ u8 padding;
const char *iface;
unsigned long timeout;
};
@@ -207,11 +210,25 @@ hash_netiface4_data_isnull(const struct hash_netiface4_elem *elem)
static inline void
hash_netiface4_data_copy(struct hash_netiface4_elem *dst,
- const struct hash_netiface4_elem *src) {
+ const struct hash_netiface4_elem *src)
+{
dst->ip = src->ip;
dst->cidr = src->cidr;
dst->physdev = src->physdev;
dst->iface = src->iface;
+ dst->nomatch = src->nomatch;
+}
+
+static inline void
+hash_netiface4_data_flags(struct hash_netiface4_elem *dst, u32 flags)
+{
+ dst->nomatch = flags & IPSET_FLAG_NOMATCH;
+}
+
+static inline bool
+hash_netiface4_data_match(const struct hash_netiface4_elem *elem)
+{
+ return !elem->nomatch;
}
static inline void
@@ -233,11 +250,13 @@ hash_netiface4_data_list(struct sk_buff *skb,
{
u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
+ if (data->nomatch)
+ flags |= IPSET_FLAG_NOMATCH;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface);
if (flags)
- NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags);
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
nla_put_failure:
@@ -252,11 +271,13 @@ hash_netiface4_data_tlist(struct sk_buff *skb,
(const struct hash_netiface4_telem *)data;
u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
+ if (data->nomatch)
+ flags |= IPSET_FLAG_NOMATCH;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface);
if (flags)
- NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags);
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
@@ -361,7 +382,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
if (tb[IPSET_ATTR_CIDR]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
- if (!data.cidr)
+ if (!data.cidr || data.cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
}
@@ -387,6 +408,8 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_PHYSDEV)
data.physdev = 1;
+ if (adt == IPSET_ADD && (cadt_flags & IPSET_FLAG_NOMATCH))
+ flags |= (cadt_flags << 16);
}
if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
@@ -440,7 +463,8 @@ struct hash_netiface6_elem_hashed {
union nf_inet_addr ip;
u8 physdev;
u8 cidr;
- u16 padding;
+ u8 nomatch;
+ u8 padding;
};
#define HKEY_DATALEN sizeof(struct hash_netiface6_elem_hashed)
@@ -449,7 +473,8 @@ struct hash_netiface6_elem {
union nf_inet_addr ip;
u8 physdev;
u8 cidr;
- u16 padding;
+ u8 nomatch;
+ u8 padding;
const char *iface;
};
@@ -457,7 +482,8 @@ struct hash_netiface6_telem {
union nf_inet_addr ip;
u8 physdev;
u8 cidr;
- u16 padding;
+ u8 nomatch;
+ u8 padding;
const char *iface;
unsigned long timeout;
};
@@ -488,8 +514,21 @@ hash_netiface6_data_copy(struct hash_netiface6_elem *dst,
}
static inline void
+hash_netiface6_data_flags(struct hash_netiface6_elem *dst, u32 flags)
+{
+ dst->nomatch = flags & IPSET_FLAG_NOMATCH;
+}
+
+static inline bool
+hash_netiface6_data_match(const struct hash_netiface6_elem *elem)
+{
+ return !elem->nomatch;
+}
+
+static inline void
hash_netiface6_data_zero_out(struct hash_netiface6_elem *elem)
{
+ elem->cidr = 0;
}
static inline void
@@ -514,11 +553,13 @@ hash_netiface6_data_list(struct sk_buff *skb,
{
u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
+ if (data->nomatch)
+ flags |= IPSET_FLAG_NOMATCH;
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface);
if (flags)
- NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags);
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
nla_put_failure:
@@ -533,11 +574,13 @@ hash_netiface6_data_tlist(struct sk_buff *skb,
(const struct hash_netiface6_telem *)data;
u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
+ if (data->nomatch)
+ flags |= IPSET_FLAG_NOMATCH;
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface);
if (flags)
- NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags);
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout)));
return 0;
@@ -636,7 +679,7 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
if (tb[IPSET_ATTR_CIDR])
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
- if (!data.cidr)
+ if (!data.cidr || data.cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
ip6_netmask(&data.ip, data.cidr);
@@ -662,6 +705,8 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
if (cadt_flags & IPSET_FLAG_PHYSDEV)
data.physdev = 1;
+ if (adt == IPSET_ADD && (cadt_flags & IPSET_FLAG_NOMATCH))
+ flags |= (cadt_flags << 16);
}
ret = adtfn(set, &data, timeout, flags);
@@ -678,7 +723,7 @@ hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
- if (!(set->family == AF_INET || set->family == AF_INET6))
+ if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
@@ -697,7 +742,7 @@ hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
h = kzalloc(sizeof(*h)
+ sizeof(struct ip_set_hash_nets)
- * (set->family == AF_INET ? 32 : 128), GFP_KERNEL);
+ * (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL);
if (!h)
return -ENOMEM;
@@ -722,15 +767,15 @@ hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_netiface4_tvariant : &hash_netiface6_tvariant;
- if (set->family == AF_INET)
+ if (set->family == NFPROTO_IPV4)
hash_netiface4_gc_init(set);
else
hash_netiface6_gc_init(set);
} else {
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_netiface4_variant : &hash_netiface6_variant;
}
@@ -746,8 +791,9 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_IFACE,
.dimension = IPSET_DIM_TWO,
- .family = AF_UNSPEC,
+ .family = NFPROTO_UNSPEC,
.revision_min = 0,
+ .revision_max = 1, /* nomatch flag support added */
.create = hash_netiface_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c
index 8f9de7207ec..ce2e77100b6 100644
--- a/net/netfilter/ipset/ip_set_hash_netport.c
+++ b/net/netfilter/ipset/ip_set_hash_netport.c
@@ -40,12 +40,19 @@ hash_netport_same_set(const struct ip_set *a, const struct ip_set *b);
/* The type variant functions: IPv4 */
+/* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0
+ * However this way we have to store internally cidr - 1,
+ * dancing back and forth.
+ */
+#define IP_SET_HASH_WITH_NETS_PACKED
+
/* Member elements without timeout */
struct hash_netport4_elem {
__be32 ip;
__be16 port;
u8 proto;
- u8 cidr;
+ u8 cidr:7;
+ u8 nomatch:1;
};
/* Member elements with timeout support */
@@ -53,7 +60,8 @@ struct hash_netport4_telem {
__be32 ip;
__be16 port;
u8 proto;
- u8 cidr;
+ u8 cidr:7;
+ u8 nomatch:1;
unsigned long timeout;
};
@@ -82,13 +90,26 @@ hash_netport4_data_copy(struct hash_netport4_elem *dst,
dst->port = src->port;
dst->proto = src->proto;
dst->cidr = src->cidr;
+ dst->nomatch = src->nomatch;
+}
+
+static inline void
+hash_netport4_data_flags(struct hash_netport4_elem *dst, u32 flags)
+{
+ dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
+}
+
+static inline bool
+hash_netport4_data_match(const struct hash_netport4_elem *elem)
+{
+ return !elem->nomatch;
}
static inline void
hash_netport4_data_netmask(struct hash_netport4_elem *elem, u8 cidr)
{
elem->ip &= ip_set_netmask(cidr);
- elem->cidr = cidr;
+ elem->cidr = cidr - 1;
}
static inline void
@@ -101,10 +122,14 @@ static bool
hash_netport4_data_list(struct sk_buff *skb,
const struct hash_netport4_elem *data)
{
+ u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
- NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ if (flags)
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
nla_put_failure:
@@ -117,13 +142,16 @@ hash_netport4_data_tlist(struct sk_buff *skb,
{
const struct hash_netport4_telem *tdata =
(const struct hash_netport4_telem *)data;
+ u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
- NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
+ if (flags)
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
@@ -154,20 +182,18 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem data = {
- .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+ .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
};
- if (data.cidr == 0)
- return -EINVAL;
if (adt == IPSET_TEST)
- data.cidr = HOST_MASK;
+ data.cidr = HOST_MASK - 1;
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
- data.ip &= ip_set_netmask(data.cidr);
+ data.ip &= ip_set_netmask(data.cidr + 1);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
@@ -178,16 +204,18 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_netport4_elem data = { .cidr = HOST_MASK };
+ struct hash_netport4_elem data = { .cidr = HOST_MASK - 1 };
u32 port, port_to, p = 0, ip = 0, ip_to, last;
u32 timeout = h->timeout;
bool with_ports = false;
+ u8 cidr;
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
- !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
return -IPSET_ERR_PROTOCOL;
if (tb[IPSET_ATTR_LINENO])
@@ -198,9 +226,10 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
return ret;
if (tb[IPSET_ATTR_CIDR]) {
- data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
- if (!data.cidr)
+ cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+ if (!cidr || cidr > HOST_MASK)
return -IPSET_ERR_INVALID_CIDR;
+ data.cidr = cidr - 1;
}
if (tb[IPSET_ATTR_PORT])
@@ -227,8 +256,15 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
}
with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
+
+ if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+ u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+ if (cadt_flags & IPSET_FLAG_NOMATCH)
+ flags |= (cadt_flags << 16);
+ }
+
if (adt == IPSET_TEST || !(with_ports || tb[IPSET_ATTR_IP_TO])) {
- data.ip = htonl(ip & ip_set_hostmask(data.cidr));
+ data.ip = htonl(ip & ip_set_hostmask(data.cidr + 1));
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
@@ -248,14 +284,15 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
if (ip + UINT_MAX == ip_to)
return -IPSET_ERR_HASH_RANGE;
} else {
- ip_set_mask_from_to(ip, ip_to, data.cidr);
+ ip_set_mask_from_to(ip, ip_to, data.cidr + 1);
}
if (retried)
ip = h->next.ip;
while (!after(ip, ip_to)) {
data.ip = htonl(ip);
- last = ip_set_range_to_cidr(ip, ip_to, &data.cidr);
+ last = ip_set_range_to_cidr(ip, ip_to, &cidr);
+ data.cidr = cidr - 1;
p = retried && ip == h->next.ip ? h->next.port : port;
for (; p <= port_to; p++) {
data.port = htons(p);
@@ -288,14 +325,16 @@ struct hash_netport6_elem {
union nf_inet_addr ip;
__be16 port;
u8 proto;
- u8 cidr;
+ u8 cidr:7;
+ u8 nomatch:1;
};
struct hash_netport6_telem {
union nf_inet_addr ip;
__be16 port;
u8 proto;
- u8 cidr;
+ u8 cidr:7;
+ u8 nomatch:1;
unsigned long timeout;
};
@@ -324,6 +363,18 @@ hash_netport6_data_copy(struct hash_netport6_elem *dst,
}
static inline void
+hash_netport6_data_flags(struct hash_netport6_elem *dst, u32 flags)
+{
+ dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
+}
+
+static inline bool
+hash_netport6_data_match(const struct hash_netport6_elem *elem)
+{
+ return !elem->nomatch;
+}
+
+static inline void
hash_netport6_data_zero_out(struct hash_netport6_elem *elem)
{
elem->proto = 0;
@@ -342,17 +393,21 @@ static inline void
hash_netport6_data_netmask(struct hash_netport6_elem *elem, u8 cidr)
{
ip6_netmask(&elem->ip, cidr);
- elem->cidr = cidr;
+ elem->cidr = cidr - 1;
}
static bool
hash_netport6_data_list(struct sk_buff *skb,
const struct hash_netport6_elem *data)
{
+ u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
- NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+ if (flags)
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
nla_put_failure:
@@ -365,13 +420,16 @@ hash_netport6_data_tlist(struct sk_buff *skb,
{
const struct hash_netport6_telem *e =
(const struct hash_netport6_telem *)data;
+ u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
- NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+ NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1);
NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout)));
+ if (flags)
+ NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
return 0;
nla_put_failure:
@@ -400,20 +458,18 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport6_elem data = {
- .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+ .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1,
};
- if (data.cidr == 0)
- return -EINVAL;
if (adt == IPSET_TEST)
- data.cidr = HOST_MASK;
+ data.cidr = HOST_MASK - 1;
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&data.port, &data.proto))
return -EINVAL;
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
- ip6_netmask(&data.ip, data.cidr);
+ ip6_netmask(&data.ip, data.cidr + 1);
return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}
@@ -424,16 +480,18 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
{
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_netport6_elem data = { .cidr = HOST_MASK };
+ struct hash_netport6_elem data = { .cidr = HOST_MASK - 1 };
u32 port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
+ u8 cidr;
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
- !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+ !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
return -IPSET_ERR_PROTOCOL;
if (unlikely(tb[IPSET_ATTR_IP_TO]))
return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
@@ -445,11 +503,13 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
if (ret)
return ret;
- if (tb[IPSET_ATTR_CIDR])
- data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
- if (!data.cidr)
- return -IPSET_ERR_INVALID_CIDR;
- ip6_netmask(&data.ip, data.cidr);
+ if (tb[IPSET_ATTR_CIDR]) {
+ cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+ if (!cidr || cidr > HOST_MASK)
+ return -IPSET_ERR_INVALID_CIDR;
+ data.cidr = cidr - 1;
+ }
+ ip6_netmask(&data.ip, data.cidr + 1);
if (tb[IPSET_ATTR_PORT])
data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
@@ -474,6 +534,12 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
+ if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+ u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+ if (cadt_flags & IPSET_FLAG_NOMATCH)
+ flags |= (cadt_flags << 16);
+ }
+
if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
@@ -507,7 +573,7 @@ hash_netport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
u8 hbits;
- if (!(set->family == AF_INET || set->family == AF_INET6))
+ if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
return -IPSET_ERR_INVALID_FAMILY;
if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
@@ -526,7 +592,7 @@ hash_netport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
h = kzalloc(sizeof(*h)
+ sizeof(struct ip_set_hash_nets)
- * (set->family == AF_INET ? 32 : 128), GFP_KERNEL);
+ * (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL);
if (!h)
return -ENOMEM;
@@ -549,15 +615,15 @@ hash_netport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
if (tb[IPSET_ATTR_TIMEOUT]) {
h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_netport4_tvariant : &hash_netport6_tvariant;
- if (set->family == AF_INET)
+ if (set->family == NFPROTO_IPV4)
hash_netport4_gc_init(set);
else
hash_netport6_gc_init(set);
} else {
- set->variant = set->family == AF_INET
+ set->variant = set->family == NFPROTO_IPV4
? &hash_netport4_variant : &hash_netport6_variant;
}
@@ -573,10 +639,11 @@ static struct ip_set_type hash_netport_type __read_mostly = {
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_IP | IPSET_TYPE_PORT,
.dimension = IPSET_DIM_TWO,
- .family = AF_UNSPEC,
+ .family = NFPROTO_UNSPEC,
.revision_min = 0,
/* 1 SCTP and UDPLITE support added */
- .revision_max = 2, /* Range as input support for IPv4 added */
+ /* 2, Range as input support for IPv4 added */
+ .revision_max = 3, /* nomatch flag support added */
.create = hash_netport_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
@@ -595,6 +662,7 @@ static struct ip_set_type hash_netport_type __read_mostly = {
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_LINENO] = { .type = NLA_U32 },
+ [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
},
.me = THIS_MODULE,
};
diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c
index 4d10819d462..7e095f9005f 100644
--- a/net/netfilter/ipset/ip_set_list_set.c
+++ b/net/netfilter/ipset/ip_set_list_set.c
@@ -575,7 +575,7 @@ static struct ip_set_type list_set_type __read_mostly = {
.protocol = IPSET_PROTOCOL,
.features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
.dimension = IPSET_DIM_ONE,
- .family = AF_UNSPEC,
+ .family = NFPROTO_UNSPEC,
.revision_min = 0,
.revision_max = 0,
.create = list_set_create,
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index 611c3359b94..2555816e778 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -232,6 +232,7 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
__be16 dport = 0; /* destination port to forward */
unsigned int flags;
struct ip_vs_conn_param param;
+ const union nf_inet_addr fwmark = { .ip = htonl(svc->fwmark) };
union nf_inet_addr snet; /* source network of the client,
after masking */
@@ -267,7 +268,6 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
{
int protocol = iph.protocol;
const union nf_inet_addr *vaddr = &iph.daddr;
- const union nf_inet_addr fwmark = { .ip = htonl(svc->fwmark) };
__be16 vport = 0;
if (dst_port == svc->port) {
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 76613f5a55c..7b48035826e 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -44,6 +44,7 @@
#include <net/netfilter/nf_conntrack_ecache.h>
#include <net/netfilter/nf_conntrack_zones.h>
#include <net/netfilter/nf_conntrack_timestamp.h>
+#include <net/netfilter/nf_conntrack_timeout.h>
#include <net/netfilter/nf_nat.h>
#include <net/netfilter/nf_nat_core.h>
@@ -404,19 +405,49 @@ static void __nf_conntrack_hash_insert(struct nf_conn *ct,
&net->ct.hash[repl_hash]);
}
-void nf_conntrack_hash_insert(struct nf_conn *ct)
+int
+nf_conntrack_hash_check_insert(struct nf_conn *ct)
{
struct net *net = nf_ct_net(ct);
unsigned int hash, repl_hash;
+ struct nf_conntrack_tuple_hash *h;
+ struct hlist_nulls_node *n;
u16 zone;
zone = nf_ct_zone(ct);
- hash = hash_conntrack(net, zone, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
- repl_hash = hash_conntrack(net, zone, &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+ hash = hash_conntrack(net, zone,
+ &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+ repl_hash = hash_conntrack(net, zone,
+ &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+
+ spin_lock_bh(&nf_conntrack_lock);
+
+ /* See if there's one in the list already, including reverse */
+ hlist_nulls_for_each_entry(h, n, &net->ct.hash[hash], hnnode)
+ if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
+ &h->tuple) &&
+ zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h)))
+ goto out;
+ hlist_nulls_for_each_entry(h, n, &net->ct.hash[repl_hash], hnnode)
+ if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple,
+ &h->tuple) &&
+ zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h)))
+ goto out;
+ add_timer(&ct->timeout);
+ nf_conntrack_get(&ct->ct_general);
__nf_conntrack_hash_insert(ct, hash, repl_hash);
+ NF_CT_STAT_INC(net, insert);
+ spin_unlock_bh(&nf_conntrack_lock);
+
+ return 0;
+
+out:
+ NF_CT_STAT_INC(net, insert_failed);
+ spin_unlock_bh(&nf_conntrack_lock);
+ return -EEXIST;
}
-EXPORT_SYMBOL_GPL(nf_conntrack_hash_insert);
+EXPORT_SYMBOL_GPL(nf_conntrack_hash_check_insert);
/* Confirm a connection given skb; places it in hash table */
int
@@ -605,8 +636,12 @@ static noinline int early_drop(struct net *net, unsigned int hash)
if (del_timer(&ct->timeout)) {
death_by_timeout((unsigned long)ct);
- dropped = 1;
- NF_CT_STAT_INC_ATOMIC(net, early_drop);
+ /* Check if we indeed killed this entry. Reliable event
+ delivery may have inserted it into the dying list. */
+ if (test_bit(IPS_DYING_BIT, &ct->status)) {
+ dropped = 1;
+ NF_CT_STAT_INC_ATOMIC(net, early_drop);
+ }
}
nf_ct_put(ct);
return dropped;
@@ -733,7 +768,8 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
struct nf_conntrack_l3proto *l3proto,
struct nf_conntrack_l4proto *l4proto,
struct sk_buff *skb,
- unsigned int dataoff, u32 hash)
+ unsigned int dataoff, u32 hash,
+ unsigned int *timeouts)
{
struct nf_conn *ct;
struct nf_conn_help *help;
@@ -752,7 +788,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
if (IS_ERR(ct))
return (struct nf_conntrack_tuple_hash *)ct;
- if (!l4proto->new(ct, skb, dataoff)) {
+ if (!l4proto->new(ct, skb, dataoff, timeouts)) {
nf_conntrack_free(ct);
pr_debug("init conntrack: can't track with proto module\n");
return NULL;
@@ -818,7 +854,8 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl,
struct nf_conntrack_l3proto *l3proto,
struct nf_conntrack_l4proto *l4proto,
int *set_reply,
- enum ip_conntrack_info *ctinfo)
+ enum ip_conntrack_info *ctinfo,
+ unsigned int *timeouts)
{
struct nf_conntrack_tuple tuple;
struct nf_conntrack_tuple_hash *h;
@@ -838,7 +875,7 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl,
h = __nf_conntrack_find_get(net, zone, &tuple, hash);
if (!h) {
h = init_conntrack(net, tmpl, &tuple, l3proto, l4proto,
- skb, dataoff, hash);
+ skb, dataoff, hash, timeouts);
if (!h)
return NULL;
if (IS_ERR(h))
@@ -879,6 +916,8 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
enum ip_conntrack_info ctinfo;
struct nf_conntrack_l3proto *l3proto;
struct nf_conntrack_l4proto *l4proto;
+ struct nf_conn_timeout *timeout_ext;
+ unsigned int *timeouts;
unsigned int dataoff;
u_int8_t protonum;
int set_reply = 0;
@@ -925,8 +964,19 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
goto out;
}
+ /* Decide what timeout policy we want to apply to this flow. */
+ if (tmpl) {
+ timeout_ext = nf_ct_timeout_find(tmpl);
+ if (timeout_ext)
+ timeouts = NF_CT_TIMEOUT_EXT_DATA(timeout_ext);
+ else
+ timeouts = l4proto->get_timeouts(net);
+ } else
+ timeouts = l4proto->get_timeouts(net);
+
ct = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum,
- l3proto, l4proto, &set_reply, &ctinfo);
+ l3proto, l4proto, &set_reply, &ctinfo,
+ timeouts);
if (!ct) {
/* Not valid part of a connection */
NF_CT_STAT_INC_ATOMIC(net, invalid);
@@ -943,7 +993,7 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
NF_CT_ASSERT(skb->nfct);
- ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum);
+ ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum, timeouts);
if (ret <= 0) {
/* Invalid: inverse of the return code tells
* the netfilter core what to do */
@@ -1297,6 +1347,7 @@ static void nf_conntrack_cleanup_net(struct net *net)
}
nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size);
+ nf_conntrack_timeout_fini(net);
nf_conntrack_ecache_fini(net);
nf_conntrack_tstamp_fini(net);
nf_conntrack_acct_fini(net);
@@ -1528,9 +1579,14 @@ static int nf_conntrack_init_net(struct net *net)
ret = nf_conntrack_ecache_init(net);
if (ret < 0)
goto err_ecache;
+ ret = nf_conntrack_timeout_init(net);
+ if (ret < 0)
+ goto err_timeout;
return 0;
+err_timeout:
+ nf_conntrack_timeout_fini(net);
err_ecache:
nf_conntrack_tstamp_fini(net);
err_tstamp:
diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c
index 14af6329bdd..5bd3047ddee 100644
--- a/net/netfilter/nf_conntrack_ecache.c
+++ b/net/netfilter/nf_conntrack_ecache.c
@@ -32,9 +32,11 @@ static DEFINE_MUTEX(nf_ct_ecache_mutex);
void nf_ct_deliver_cached_events(struct nf_conn *ct)
{
struct net *net = nf_ct_net(ct);
- unsigned long events;
+ unsigned long events, missed;
struct nf_ct_event_notifier *notify;
struct nf_conntrack_ecache *e;
+ struct nf_ct_event item;
+ int ret;
rcu_read_lock();
notify = rcu_dereference(net->ct.nf_conntrack_event_cb);
@@ -47,31 +49,32 @@ void nf_ct_deliver_cached_events(struct nf_conn *ct)
events = xchg(&e->cache, 0);
- if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct) && events) {
- struct nf_ct_event item = {
- .ct = ct,
- .pid = 0,
- .report = 0
- };
- int ret;
- /* We make a copy of the missed event cache without taking
- * the lock, thus we may send missed events twice. However,
- * this does not harm and it happens very rarely. */
- unsigned long missed = e->missed;
-
- if (!((events | missed) & e->ctmask))
- goto out_unlock;
-
- ret = notify->fcn(events | missed, &item);
- if (unlikely(ret < 0 || missed)) {
- spin_lock_bh(&ct->lock);
- if (ret < 0)
- e->missed |= events;
- else
- e->missed &= ~missed;
- spin_unlock_bh(&ct->lock);
- }
- }
+ if (!nf_ct_is_confirmed(ct) || nf_ct_is_dying(ct) || !events)
+ goto out_unlock;
+
+ /* We make a copy of the missed event cache without taking
+ * the lock, thus we may send missed events twice. However,
+ * this does not harm and it happens very rarely. */
+ missed = e->missed;
+
+ if (!((events | missed) & e->ctmask))
+ goto out_unlock;
+
+ item.ct = ct;
+ item.pid = 0;
+ item.report = 0;
+
+ ret = notify->fcn(events | missed, &item);
+
+ if (likely(ret >= 0 && !missed))
+ goto out_unlock;
+
+ spin_lock_bh(&ct->lock);
+ if (ret < 0)
+ e->missed |= events;
+ else
+ e->missed &= ~missed;
+ spin_unlock_bh(&ct->lock);
out_unlock:
rcu_read_unlock();
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index bbe23baa19b..436b7cb79ba 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -181,6 +181,60 @@ void nf_ct_helper_destroy(struct nf_conn *ct)
}
}
+static LIST_HEAD(nf_ct_helper_expectfn_list);
+
+void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n)
+{
+ spin_lock_bh(&nf_conntrack_lock);
+ list_add_rcu(&n->head, &nf_ct_helper_expectfn_list);
+ spin_unlock_bh(&nf_conntrack_lock);
+}
+EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_register);
+
+void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n)
+{
+ spin_lock_bh(&nf_conntrack_lock);
+ list_del_rcu(&n->head);
+ spin_unlock_bh(&nf_conntrack_lock);
+}
+EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister);
+
+struct nf_ct_helper_expectfn *
+nf_ct_helper_expectfn_find_by_name(const char *name)
+{
+ struct nf_ct_helper_expectfn *cur;
+ bool found = false;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
+ if (!strcmp(cur->name, name)) {
+ found = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ return found ? cur : NULL;
+}
+EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_name);
+
+struct nf_ct_helper_expectfn *
+nf_ct_helper_expectfn_find_by_symbol(const void *symbol)
+{
+ struct nf_ct_helper_expectfn *cur;
+ bool found = false;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
+ if (cur->expectfn == symbol) {
+ found = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ return found ? cur : NULL;
+}
+EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol);
+
int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
{
unsigned int h = helper_hash(&me->tuple);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 9307b033c0c..2124977ac31 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -110,15 +110,16 @@ ctnetlink_dump_tuples(struct sk_buff *skb,
struct nf_conntrack_l3proto *l3proto;
struct nf_conntrack_l4proto *l4proto;
+ rcu_read_lock();
l3proto = __nf_ct_l3proto_find(tuple->src.l3num);
ret = ctnetlink_dump_tuples_ip(skb, tuple, l3proto);
- if (unlikely(ret < 0))
- return ret;
-
- l4proto = __nf_ct_l4proto_find(tuple->src.l3num, tuple->dst.protonum);
- ret = ctnetlink_dump_tuples_proto(skb, tuple, l4proto);
-
+ if (ret >= 0) {
+ l4proto = __nf_ct_l4proto_find(tuple->src.l3num,
+ tuple->dst.protonum);
+ ret = ctnetlink_dump_tuples_proto(skb, tuple, l4proto);
+ }
+ rcu_read_unlock();
return ret;
}
@@ -691,9 +692,18 @@ static int ctnetlink_done(struct netlink_callback *cb)
{
if (cb->args[1])
nf_ct_put((struct nf_conn *)cb->args[1]);
+ if (cb->data)
+ kfree(cb->data);
return 0;
}
+struct ctnetlink_dump_filter {
+ struct {
+ u_int32_t val;
+ u_int32_t mask;
+ } mark;
+};
+
static int
ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
{
@@ -703,6 +713,10 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
struct hlist_nulls_node *n;
struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
u_int8_t l3proto = nfmsg->nfgen_family;
+ int res;
+#ifdef CONFIG_NF_CONNTRACK_MARK
+ const struct ctnetlink_dump_filter *filter = cb->data;
+#endif
spin_lock_bh(&nf_conntrack_lock);
last = (struct nf_conn *)cb->args[1];
@@ -723,11 +737,20 @@ restart:
continue;
cb->args[1] = 0;
}
- if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
- cb->nlh->nlmsg_seq,
- NFNL_MSG_TYPE(
- cb->nlh->nlmsg_type),
- ct) < 0) {
+#ifdef CONFIG_NF_CONNTRACK_MARK
+ if (filter && !((ct->mark & filter->mark.mask) ==
+ filter->mark.val)) {
+ continue;
+ }
+#endif
+ rcu_read_lock();
+ res =
+ ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq,
+ NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
+ ct);
+ rcu_read_unlock();
+ if (res < 0) {
nf_conntrack_get(&ct->ct_general);
cb->args[1] = (unsigned long)ct;
goto out;
@@ -894,6 +917,7 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
[CTA_NAT_DST] = { .type = NLA_NESTED },
[CTA_TUPLE_MASTER] = { .type = NLA_NESTED },
[CTA_ZONE] = { .type = NLA_U16 },
+ [CTA_MARK_MASK] = { .type = NLA_U32 },
};
static int
@@ -977,9 +1001,28 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
u16 zone;
int err;
- if (nlh->nlmsg_flags & NLM_F_DUMP)
- return netlink_dump_start(ctnl, skb, nlh, ctnetlink_dump_table,
- ctnetlink_done, 0);
+ if (nlh->nlmsg_flags & NLM_F_DUMP) {
+ struct netlink_dump_control c = {
+ .dump = ctnetlink_dump_table,
+ .done = ctnetlink_done,
+ };
+#ifdef CONFIG_NF_CONNTRACK_MARK
+ if (cda[CTA_MARK] && cda[CTA_MARK_MASK]) {
+ struct ctnetlink_dump_filter *filter;
+
+ filter = kzalloc(sizeof(struct ctnetlink_dump_filter),
+ GFP_ATOMIC);
+ if (filter == NULL)
+ return -ENOMEM;
+
+ filter->mark.val = ntohl(nla_get_be32(cda[CTA_MARK]));
+ filter->mark.mask =
+ ntohl(nla_get_be32(cda[CTA_MARK_MASK]));
+ c.data = filter;
+ }
+#endif
+ return netlink_dump_start(ctnl, skb, nlh, &c);
+ }
err = ctnetlink_parse_zone(cda[CTA_ZONE], &zone);
if (err < 0)
@@ -1041,16 +1084,13 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
if (!parse_nat_setup) {
#ifdef CONFIG_MODULES
rcu_read_unlock();
- spin_unlock_bh(&nf_conntrack_lock);
nfnl_unlock();
if (request_module("nf-nat-ipv4") < 0) {
nfnl_lock();
- spin_lock_bh(&nf_conntrack_lock);
rcu_read_lock();
return -EOPNOTSUPP;
}
nfnl_lock();
- spin_lock_bh(&nf_conntrack_lock);
rcu_read_lock();
if (nfnetlink_parse_nat_setup_hook)
return -EAGAIN;
@@ -1367,15 +1407,12 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
nf_ct_protonum(ct));
if (helper == NULL) {
rcu_read_unlock();
- spin_unlock_bh(&nf_conntrack_lock);
#ifdef CONFIG_MODULES
if (request_module("nfct-helper-%s", helpname) < 0) {
- spin_lock_bh(&nf_conntrack_lock);
err = -EOPNOTSUPP;
goto err1;
}
- spin_lock_bh(&nf_conntrack_lock);
rcu_read_lock();
helper = __nf_conntrack_helper_find(helpname,
nf_ct_l3num(ct),
@@ -1468,8 +1505,10 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
if (tstamp)
tstamp->start = ktime_to_ns(ktime_get_real());
- add_timer(&ct->timeout);
- nf_conntrack_hash_insert(ct);
+ err = nf_conntrack_hash_check_insert(ct);
+ if (err < 0)
+ goto err2;
+
rcu_read_unlock();
return ct;
@@ -1490,6 +1529,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
struct nf_conntrack_tuple otuple, rtuple;
struct nf_conntrack_tuple_hash *h = NULL;
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+ struct nf_conn *ct;
u_int8_t u3 = nfmsg->nfgen_family;
u16 zone;
int err;
@@ -1510,27 +1550,22 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
return err;
}
- spin_lock_bh(&nf_conntrack_lock);
if (cda[CTA_TUPLE_ORIG])
- h = __nf_conntrack_find(net, zone, &otuple);
+ h = nf_conntrack_find_get(net, zone, &otuple);
else if (cda[CTA_TUPLE_REPLY])
- h = __nf_conntrack_find(net, zone, &rtuple);
+ h = nf_conntrack_find_get(net, zone, &rtuple);
if (h == NULL) {
err = -ENOENT;
if (nlh->nlmsg_flags & NLM_F_CREATE) {
- struct nf_conn *ct;
enum ip_conntrack_events events;
ct = ctnetlink_create_conntrack(net, zone, cda, &otuple,
&rtuple, u3);
- if (IS_ERR(ct)) {
- err = PTR_ERR(ct);
- goto out_unlock;
- }
+ if (IS_ERR(ct))
+ return PTR_ERR(ct);
+
err = 0;
- nf_conntrack_get(&ct->ct_general);
- spin_unlock_bh(&nf_conntrack_lock);
if (test_bit(IPS_EXPECTED_BIT, &ct->status))
events = IPCT_RELATED;
else
@@ -1545,23 +1580,19 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
ct, NETLINK_CB(skb).pid,
nlmsg_report(nlh));
nf_ct_put(ct);
- } else
- spin_unlock_bh(&nf_conntrack_lock);
+ }
return err;
}
/* implicit 'else' */
- /* We manipulate the conntrack inside the global conntrack table lock,
- * so there's no need to increase the refcount */
err = -EEXIST;
+ ct = nf_ct_tuplehash_to_ctrack(h);
if (!(nlh->nlmsg_flags & NLM_F_EXCL)) {
- struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
-
+ spin_lock_bh(&nf_conntrack_lock);
err = ctnetlink_change_conntrack(ct, cda);
+ spin_unlock_bh(&nf_conntrack_lock);
if (err == 0) {
- nf_conntrack_get(&ct->ct_general);
- spin_unlock_bh(&nf_conntrack_lock);
nf_conntrack_eventmask_report((1 << IPCT_REPLY) |
(1 << IPCT_ASSURED) |
(1 << IPCT_HELPER) |
@@ -1570,15 +1601,10 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
(1 << IPCT_MARK),
ct, NETLINK_CB(skb).pid,
nlmsg_report(nlh));
- nf_ct_put(ct);
- } else
- spin_unlock_bh(&nf_conntrack_lock);
-
- return err;
+ }
}
-out_unlock:
- spin_unlock_bh(&nf_conntrack_lock);
+ nf_ct_put(ct);
return err;
}
@@ -1626,14 +1652,16 @@ ctnetlink_exp_dump_mask(struct sk_buff *skb,
if (!nest_parms)
goto nla_put_failure;
+ rcu_read_lock();
l3proto = __nf_ct_l3proto_find(tuple->src.l3num);
ret = ctnetlink_dump_tuples_ip(skb, &m, l3proto);
-
- if (unlikely(ret < 0))
- goto nla_put_failure;
-
- l4proto = __nf_ct_l4proto_find(tuple->src.l3num, tuple->dst.protonum);
+ if (ret >= 0) {
+ l4proto = __nf_ct_l4proto_find(tuple->src.l3num,
+ tuple->dst.protonum);
ret = ctnetlink_dump_tuples_proto(skb, &m, l4proto);
+ }
+ rcu_read_unlock();
+
if (unlikely(ret < 0))
goto nla_put_failure;
@@ -1652,6 +1680,11 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
struct nf_conn *master = exp->master;
long timeout = ((long)exp->timeout.expires - (long)jiffies) / HZ;
struct nf_conn_help *help;
+#ifdef CONFIG_NF_NAT_NEEDED
+ struct nlattr *nest_parms;
+ struct nf_conntrack_tuple nat_tuple = {};
+#endif
+ struct nf_ct_helper_expectfn *expfn;
if (timeout < 0)
timeout = 0;
@@ -1665,9 +1698,29 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
CTA_EXPECT_MASTER) < 0)
goto nla_put_failure;
+#ifdef CONFIG_NF_NAT_NEEDED
+ if (exp->saved_ip || exp->saved_proto.all) {
+ nest_parms = nla_nest_start(skb, CTA_EXPECT_NAT | NLA_F_NESTED);
+ if (!nest_parms)
+ goto nla_put_failure;
+
+ NLA_PUT_BE32(skb, CTA_EXPECT_NAT_DIR, htonl(exp->dir));
+
+ nat_tuple.src.l3num = nf_ct_l3num(master);
+ nat_tuple.src.u3.ip = exp->saved_ip;
+ nat_tuple.dst.protonum = nf_ct_protonum(master);
+ nat_tuple.src.u = exp->saved_proto;
+
+ if (ctnetlink_exp_dump_tuple(skb, &nat_tuple,
+ CTA_EXPECT_NAT_TUPLE) < 0)
+ goto nla_put_failure;
+ nla_nest_end(skb, nest_parms);
+ }
+#endif
NLA_PUT_BE32(skb, CTA_EXPECT_TIMEOUT, htonl(timeout));
NLA_PUT_BE32(skb, CTA_EXPECT_ID, htonl((unsigned long)exp));
NLA_PUT_BE32(skb, CTA_EXPECT_FLAGS, htonl(exp->flags));
+ NLA_PUT_BE32(skb, CTA_EXPECT_CLASS, htonl(exp->class));
help = nfct_help(master);
if (help) {
struct nf_conntrack_helper *helper;
@@ -1676,6 +1729,9 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
if (helper)
NLA_PUT_STRING(skb, CTA_EXPECT_HELP_NAME, helper->name);
}
+ expfn = nf_ct_helper_expectfn_find_by_symbol(exp->expectfn);
+ if (expfn != NULL)
+ NLA_PUT_STRING(skb, CTA_EXPECT_FN, expfn->name);
return 0;
@@ -1833,6 +1889,9 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
[CTA_EXPECT_HELP_NAME] = { .type = NLA_NUL_STRING },
[CTA_EXPECT_ZONE] = { .type = NLA_U16 },
[CTA_EXPECT_FLAGS] = { .type = NLA_U32 },
+ [CTA_EXPECT_CLASS] = { .type = NLA_U32 },
+ [CTA_EXPECT_NAT] = { .type = NLA_NESTED },
+ [CTA_EXPECT_FN] = { .type = NLA_NUL_STRING },
};
static int
@@ -1850,9 +1909,11 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
int err;
if (nlh->nlmsg_flags & NLM_F_DUMP) {
- return netlink_dump_start(ctnl, skb, nlh,
- ctnetlink_exp_dump_table,
- ctnetlink_exp_done, 0);
+ struct netlink_dump_control c = {
+ .dump = ctnetlink_exp_dump_table,
+ .done = ctnetlink_exp_done,
+ };
+ return netlink_dump_start(ctnl, skb, nlh, &c);
}
err = ctnetlink_parse_zone(cda[CTA_EXPECT_ZONE], &zone);
@@ -2006,6 +2067,41 @@ ctnetlink_change_expect(struct nf_conntrack_expect *x,
return -EOPNOTSUPP;
}
+static const struct nla_policy exp_nat_nla_policy[CTA_EXPECT_NAT_MAX+1] = {
+ [CTA_EXPECT_NAT_DIR] = { .type = NLA_U32 },
+ [CTA_EXPECT_NAT_TUPLE] = { .type = NLA_NESTED },
+};
+
+static int
+ctnetlink_parse_expect_nat(const struct nlattr *attr,
+ struct nf_conntrack_expect *exp,
+ u_int8_t u3)
+{
+#ifdef CONFIG_NF_NAT_NEEDED
+ struct nlattr *tb[CTA_EXPECT_NAT_MAX+1];
+ struct nf_conntrack_tuple nat_tuple = {};
+ int err;
+
+ nla_parse_nested(tb, CTA_EXPECT_NAT_MAX, attr, exp_nat_nla_policy);
+
+ if (!tb[CTA_EXPECT_NAT_DIR] || !tb[CTA_EXPECT_NAT_TUPLE])
+ return -EINVAL;
+
+ err = ctnetlink_parse_tuple((const struct nlattr * const *)tb,
+ &nat_tuple, CTA_EXPECT_NAT_TUPLE, u3);
+ if (err < 0)
+ return err;
+
+ exp->saved_ip = nat_tuple.src.u3.ip;
+ exp->saved_proto = nat_tuple.src.u;
+ exp->dir = ntohl(nla_get_be32(tb[CTA_EXPECT_NAT_DIR]));
+
+ return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
static int
ctnetlink_create_expect(struct net *net, u16 zone,
const struct nlattr * const cda[],
@@ -2017,6 +2113,8 @@ ctnetlink_create_expect(struct net *net, u16 zone,
struct nf_conntrack_expect *exp;
struct nf_conn *ct;
struct nf_conn_help *help;
+ struct nf_conntrack_helper *helper = NULL;
+ u_int32_t class = 0;
int err = 0;
/* caller guarantees that those three CTA_EXPECT_* exist */
@@ -2035,6 +2133,40 @@ ctnetlink_create_expect(struct net *net, u16 zone,
if (!h)
return -ENOENT;
ct = nf_ct_tuplehash_to_ctrack(h);
+
+ /* Look for helper of this expectation */
+ if (cda[CTA_EXPECT_HELP_NAME]) {
+ const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]);
+
+ helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
+ nf_ct_protonum(ct));
+ if (helper == NULL) {
+#ifdef CONFIG_MODULES
+ if (request_module("nfct-helper-%s", helpname) < 0) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ helper = __nf_conntrack_helper_find(helpname,
+ nf_ct_l3num(ct),
+ nf_ct_protonum(ct));
+ if (helper) {
+ err = -EAGAIN;
+ goto out;
+ }
+#endif
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+ }
+
+ if (cda[CTA_EXPECT_CLASS] && helper) {
+ class = ntohl(nla_get_be32(cda[CTA_EXPECT_CLASS]));
+ if (class > helper->expect_class_max) {
+ err = -EINVAL;
+ goto out;
+ }
+ }
exp = nf_ct_expect_alloc(ct);
if (!exp) {
err = -ENOMEM;
@@ -2061,18 +2193,35 @@ ctnetlink_create_expect(struct net *net, u16 zone,
} else
exp->flags = 0;
}
+ if (cda[CTA_EXPECT_FN]) {
+ const char *name = nla_data(cda[CTA_EXPECT_FN]);
+ struct nf_ct_helper_expectfn *expfn;
- exp->class = 0;
- exp->expectfn = NULL;
+ expfn = nf_ct_helper_expectfn_find_by_name(name);
+ if (expfn == NULL) {
+ err = -EINVAL;
+ goto err_out;
+ }
+ exp->expectfn = expfn->expectfn;
+ } else
+ exp->expectfn = NULL;
+
+ exp->class = class;
exp->master = ct;
- exp->helper = NULL;
+ exp->helper = helper;
memcpy(&exp->tuple, &tuple, sizeof(struct nf_conntrack_tuple));
memcpy(&exp->mask.src.u3, &mask.src.u3, sizeof(exp->mask.src.u3));
exp->mask.src.u.all = mask.src.u.all;
+ if (cda[CTA_EXPECT_NAT]) {
+ err = ctnetlink_parse_expect_nat(cda[CTA_EXPECT_NAT],
+ exp, u3);
+ if (err < 0)
+ goto err_out;
+ }
err = nf_ct_expect_related_report(exp, pid, report);
+err_out:
nf_ct_expect_put(exp);
-
out:
nf_ct_put(nf_ct_tuplehash_to_ctrack(h));
return err;
diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c
index d6dde6dc09e..24fdce256cb 100644
--- a/net/netfilter/nf_conntrack_proto_dccp.c
+++ b/net/netfilter/nf_conntrack_proto_dccp.c
@@ -423,7 +423,7 @@ static bool dccp_invert_tuple(struct nf_conntrack_tuple *inv,
}
static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb,
- unsigned int dataoff)
+ unsigned int dataoff, unsigned int *timeouts)
{
struct net *net = nf_ct_net(ct);
struct dccp_net *dn;
@@ -472,12 +472,17 @@ static u64 dccp_ack_seq(const struct dccp_hdr *dh)
ntohl(dhack->dccph_ack_nr_low);
}
+static unsigned int *dccp_get_timeouts(struct net *net)
+{
+ return dccp_pernet(net)->dccp_timeout;
+}
+
static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,
unsigned int dataoff, enum ip_conntrack_info ctinfo,
- u_int8_t pf, unsigned int hooknum)
+ u_int8_t pf, unsigned int hooknum,
+ unsigned int *timeouts)
{
struct net *net = nf_ct_net(ct);
- struct dccp_net *dn;
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
struct dccp_hdr _dh, *dh;
u_int8_t type, old_state, new_state;
@@ -559,8 +564,7 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,
if (new_state != old_state)
nf_conntrack_event_cache(IPCT_PROTOINFO, ct);
- dn = dccp_pernet(net);
- nf_ct_refresh_acct(ct, ctinfo, skb, dn->dccp_timeout[new_state]);
+ nf_ct_refresh_acct(ct, ctinfo, skb, timeouts[new_state]);
return NF_ACCEPT;
}
@@ -702,8 +706,60 @@ static int dccp_nlattr_size(void)
return nla_total_size(0) /* CTA_PROTOINFO_DCCP */
+ nla_policy_len(dccp_nla_policy, CTA_PROTOINFO_DCCP_MAX + 1);
}
+
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_cttimeout.h>
+
+static int dccp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data)
+{
+ struct dccp_net *dn = dccp_pernet(&init_net);
+ unsigned int *timeouts = data;
+ int i;
+
+ /* set default DCCP timeouts. */
+ for (i=0; i<CT_DCCP_MAX; i++)
+ timeouts[i] = dn->dccp_timeout[i];
+
+ /* there's a 1:1 mapping between attributes and protocol states. */
+ for (i=CTA_TIMEOUT_DCCP_UNSPEC+1; i<CTA_TIMEOUT_DCCP_MAX+1; i++) {
+ if (tb[i]) {
+ timeouts[i] = ntohl(nla_get_be32(tb[i])) * HZ;
+ }
+ }
+ return 0;
+}
+
+static int
+dccp_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
+{
+ const unsigned int *timeouts = data;
+ int i;
+
+ for (i=CTA_TIMEOUT_DCCP_UNSPEC+1; i<CTA_TIMEOUT_DCCP_MAX+1; i++)
+ NLA_PUT_BE32(skb, i, htonl(timeouts[i] / HZ));
+
+ return 0;
+
+nla_put_failure:
+ return -ENOSPC;
+}
+
+static const struct nla_policy
+dccp_timeout_nla_policy[CTA_TIMEOUT_DCCP_MAX+1] = {
+ [CTA_TIMEOUT_DCCP_REQUEST] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_DCCP_RESPOND] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_DCCP_PARTOPEN] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_DCCP_OPEN] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_DCCP_CLOSEREQ] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_DCCP_CLOSING] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_DCCP_TIMEWAIT] = { .type = NLA_U32 },
+};
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
+
#ifdef CONFIG_SYSCTL
/* template, data assigned later */
static struct ctl_table dccp_sysctl_table[] = {
@@ -767,6 +823,7 @@ static struct nf_conntrack_l4proto dccp_proto4 __read_mostly = {
.invert_tuple = dccp_invert_tuple,
.new = dccp_new,
.packet = dccp_packet,
+ .get_timeouts = dccp_get_timeouts,
.error = dccp_error,
.print_tuple = dccp_print_tuple,
.print_conntrack = dccp_print_conntrack,
@@ -779,6 +836,15 @@ static struct nf_conntrack_l4proto dccp_proto4 __read_mostly = {
.nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
.nla_policy = nf_ct_port_nla_policy,
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = dccp_timeout_nlattr_to_obj,
+ .obj_to_nlattr = dccp_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_DCCP_MAX,
+ .obj_size = sizeof(unsigned int) * CT_DCCP_MAX,
+ .nla_policy = dccp_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
};
static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = {
@@ -789,6 +855,7 @@ static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = {
.invert_tuple = dccp_invert_tuple,
.new = dccp_new,
.packet = dccp_packet,
+ .get_timeouts = dccp_get_timeouts,
.error = dccp_error,
.print_tuple = dccp_print_tuple,
.print_conntrack = dccp_print_conntrack,
@@ -801,6 +868,15 @@ static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = {
.nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
.nla_policy = nf_ct_port_nla_policy,
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = dccp_timeout_nlattr_to_obj,
+ .obj_to_nlattr = dccp_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_DCCP_MAX,
+ .obj_size = sizeof(unsigned int) * CT_DCCP_MAX,
+ .nla_policy = dccp_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
};
static __net_init int dccp_net_init(struct net *net)
diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c
index e2091d0c7a2..835e24c58f0 100644
--- a/net/netfilter/nf_conntrack_proto_generic.c
+++ b/net/netfilter/nf_conntrack_proto_generic.c
@@ -40,25 +40,70 @@ static int generic_print_tuple(struct seq_file *s,
return 0;
}
+static unsigned int *generic_get_timeouts(struct net *net)
+{
+ return &nf_ct_generic_timeout;
+}
+
/* Returns verdict for packet, or -1 for invalid. */
-static int packet(struct nf_conn *ct,
- const struct sk_buff *skb,
- unsigned int dataoff,
- enum ip_conntrack_info ctinfo,
- u_int8_t pf,
- unsigned int hooknum)
+static int generic_packet(struct nf_conn *ct,
+ const struct sk_buff *skb,
+ unsigned int dataoff,
+ enum ip_conntrack_info ctinfo,
+ u_int8_t pf,
+ unsigned int hooknum,
+ unsigned int *timeout)
{
- nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_generic_timeout);
+ nf_ct_refresh_acct(ct, ctinfo, skb, *timeout);
return NF_ACCEPT;
}
/* Called when a new connection for this protocol found. */
-static bool new(struct nf_conn *ct, const struct sk_buff *skb,
- unsigned int dataoff)
+static bool generic_new(struct nf_conn *ct, const struct sk_buff *skb,
+ unsigned int dataoff, unsigned int *timeouts)
{
return true;
}
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_cttimeout.h>
+
+static int generic_timeout_nlattr_to_obj(struct nlattr *tb[], void *data)
+{
+ unsigned int *timeout = data;
+
+ if (tb[CTA_TIMEOUT_GENERIC_TIMEOUT])
+ *timeout =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_GENERIC_TIMEOUT])) * HZ;
+ else {
+ /* Set default generic timeout. */
+ *timeout = nf_ct_generic_timeout;
+ }
+
+ return 0;
+}
+
+static int
+generic_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
+{
+ const unsigned int *timeout = data;
+
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_GENERIC_TIMEOUT, htonl(*timeout / HZ));
+
+ return 0;
+
+nla_put_failure:
+ return -ENOSPC;
+}
+
+static const struct nla_policy
+generic_timeout_nla_policy[CTA_TIMEOUT_GENERIC_MAX+1] = {
+ [CTA_TIMEOUT_GENERIC_TIMEOUT] = { .type = NLA_U32 },
+};
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
+
#ifdef CONFIG_SYSCTL
static struct ctl_table_header *generic_sysctl_header;
static struct ctl_table generic_sysctl_table[] = {
@@ -93,8 +138,18 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_generic __read_mostly =
.pkt_to_tuple = generic_pkt_to_tuple,
.invert_tuple = generic_invert_tuple,
.print_tuple = generic_print_tuple,
- .packet = packet,
- .new = new,
+ .packet = generic_packet,
+ .get_timeouts = generic_get_timeouts,
+ .new = generic_new,
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = generic_timeout_nlattr_to_obj,
+ .obj_to_nlattr = generic_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_GENERIC_MAX,
+ .obj_size = sizeof(unsigned int),
+ .nla_policy = generic_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
#ifdef CONFIG_SYSCTL
.ctl_table_header = &generic_sysctl_header,
.ctl_table = generic_sysctl_table,
diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c
index f0338791b82..659648c4b14 100644
--- a/net/netfilter/nf_conntrack_proto_gre.c
+++ b/net/netfilter/nf_conntrack_proto_gre.c
@@ -41,8 +41,16 @@
#include <linux/netfilter/nf_conntrack_proto_gre.h>
#include <linux/netfilter/nf_conntrack_pptp.h>
-#define GRE_TIMEOUT (30 * HZ)
-#define GRE_STREAM_TIMEOUT (180 * HZ)
+enum grep_conntrack {
+ GRE_CT_UNREPLIED,
+ GRE_CT_REPLIED,
+ GRE_CT_MAX
+};
+
+static unsigned int gre_timeouts[GRE_CT_MAX] = {
+ [GRE_CT_UNREPLIED] = 30*HZ,
+ [GRE_CT_REPLIED] = 180*HZ,
+};
static int proto_gre_net_id __read_mostly;
struct netns_proto_gre {
@@ -227,13 +235,19 @@ static int gre_print_conntrack(struct seq_file *s, struct nf_conn *ct)
(ct->proto.gre.stream_timeout / HZ));
}
+static unsigned int *gre_get_timeouts(struct net *net)
+{
+ return gre_timeouts;
+}
+
/* Returns verdict for packet, and may modify conntrack */
static int gre_packet(struct nf_conn *ct,
const struct sk_buff *skb,
unsigned int dataoff,
enum ip_conntrack_info ctinfo,
u_int8_t pf,
- unsigned int hooknum)
+ unsigned int hooknum,
+ unsigned int *timeouts)
{
/* If we've seen traffic both ways, this is a GRE connection.
* Extend timeout. */
@@ -252,15 +266,15 @@ static int gre_packet(struct nf_conn *ct,
/* Called when a new connection for this protocol found. */
static bool gre_new(struct nf_conn *ct, const struct sk_buff *skb,
- unsigned int dataoff)
+ unsigned int dataoff, unsigned int *timeouts)
{
pr_debug(": ");
nf_ct_dump_tuple(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
/* initialize to sane value. Ideally a conntrack helper
* (e.g. in case of pptp) is increasing them */
- ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT;
- ct->proto.gre.timeout = GRE_TIMEOUT;
+ ct->proto.gre.stream_timeout = timeouts[GRE_CT_REPLIED];
+ ct->proto.gre.timeout = timeouts[GRE_CT_UNREPLIED];
return true;
}
@@ -278,6 +292,52 @@ static void gre_destroy(struct nf_conn *ct)
nf_ct_gre_keymap_destroy(master);
}
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_cttimeout.h>
+
+static int gre_timeout_nlattr_to_obj(struct nlattr *tb[], void *data)
+{
+ unsigned int *timeouts = data;
+
+ /* set default timeouts for GRE. */
+ timeouts[GRE_CT_UNREPLIED] = gre_timeouts[GRE_CT_UNREPLIED];
+ timeouts[GRE_CT_REPLIED] = gre_timeouts[GRE_CT_REPLIED];
+
+ if (tb[CTA_TIMEOUT_GRE_UNREPLIED]) {
+ timeouts[GRE_CT_UNREPLIED] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_GRE_UNREPLIED])) * HZ;
+ }
+ if (tb[CTA_TIMEOUT_GRE_REPLIED]) {
+ timeouts[GRE_CT_REPLIED] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_GRE_REPLIED])) * HZ;
+ }
+ return 0;
+}
+
+static int
+gre_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
+{
+ const unsigned int *timeouts = data;
+
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_GRE_UNREPLIED,
+ htonl(timeouts[GRE_CT_UNREPLIED] / HZ));
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_GRE_REPLIED,
+ htonl(timeouts[GRE_CT_REPLIED] / HZ));
+ return 0;
+
+nla_put_failure:
+ return -ENOSPC;
+}
+
+static const struct nla_policy
+gre_timeout_nla_policy[CTA_TIMEOUT_GRE_MAX+1] = {
+ [CTA_TIMEOUT_GRE_UNREPLIED] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_GRE_REPLIED] = { .type = NLA_U32 },
+};
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
+
/* protocol helper struct */
static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = {
.l3proto = AF_INET,
@@ -287,6 +347,7 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = {
.invert_tuple = gre_invert_tuple,
.print_tuple = gre_print_tuple,
.print_conntrack = gre_print_conntrack,
+ .get_timeouts = gre_get_timeouts,
.packet = gre_packet,
.new = gre_new,
.destroy = gre_destroy,
@@ -297,6 +358,15 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = {
.nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
.nla_policy = nf_ct_port_nla_policy,
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = gre_timeout_nlattr_to_obj,
+ .obj_to_nlattr = gre_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_GRE_MAX,
+ .obj_size = sizeof(unsigned int) * GRE_CT_MAX,
+ .nla_policy = gre_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
};
static int proto_gre_net_init(struct net *net)
diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c
index afa69136061..72b5088592d 100644
--- a/net/netfilter/nf_conntrack_proto_sctp.c
+++ b/net/netfilter/nf_conntrack_proto_sctp.c
@@ -279,13 +279,19 @@ static int sctp_new_state(enum ip_conntrack_dir dir,
return sctp_conntracks[dir][i][cur_state];
}
+static unsigned int *sctp_get_timeouts(struct net *net)
+{
+ return sctp_timeouts;
+}
+
/* Returns verdict for packet, or -NF_ACCEPT for invalid. */
static int sctp_packet(struct nf_conn *ct,
const struct sk_buff *skb,
unsigned int dataoff,
enum ip_conntrack_info ctinfo,
u_int8_t pf,
- unsigned int hooknum)
+ unsigned int hooknum,
+ unsigned int *timeouts)
{
enum sctp_conntrack new_state, old_state;
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
@@ -370,7 +376,7 @@ static int sctp_packet(struct nf_conn *ct,
}
spin_unlock_bh(&ct->lock);
- nf_ct_refresh_acct(ct, ctinfo, skb, sctp_timeouts[new_state]);
+ nf_ct_refresh_acct(ct, ctinfo, skb, timeouts[new_state]);
if (old_state == SCTP_CONNTRACK_COOKIE_ECHOED &&
dir == IP_CT_DIR_REPLY &&
@@ -390,7 +396,7 @@ out:
/* Called when a new connection for this protocol found. */
static bool sctp_new(struct nf_conn *ct, const struct sk_buff *skb,
- unsigned int dataoff)
+ unsigned int dataoff, unsigned int *timeouts)
{
enum sctp_conntrack new_state;
const struct sctphdr *sh;
@@ -543,6 +549,57 @@ static int sctp_nlattr_size(void)
}
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_cttimeout.h>
+
+static int sctp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data)
+{
+ unsigned int *timeouts = data;
+ int i;
+
+ /* set default SCTP timeouts. */
+ for (i=0; i<SCTP_CONNTRACK_MAX; i++)
+ timeouts[i] = sctp_timeouts[i];
+
+ /* there's a 1:1 mapping between attributes and protocol states. */
+ for (i=CTA_TIMEOUT_SCTP_UNSPEC+1; i<CTA_TIMEOUT_SCTP_MAX+1; i++) {
+ if (tb[i]) {
+ timeouts[i] = ntohl(nla_get_be32(tb[i])) * HZ;
+ }
+ }
+ return 0;
+}
+
+static int
+sctp_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
+{
+ const unsigned int *timeouts = data;
+ int i;
+
+ for (i=CTA_TIMEOUT_SCTP_UNSPEC+1; i<CTA_TIMEOUT_SCTP_MAX+1; i++)
+ NLA_PUT_BE32(skb, i, htonl(timeouts[i] / HZ));
+
+ return 0;
+
+nla_put_failure:
+ return -ENOSPC;
+}
+
+static const struct nla_policy
+sctp_timeout_nla_policy[CTA_TIMEOUT_SCTP_MAX+1] = {
+ [CTA_TIMEOUT_SCTP_CLOSED] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_SCTP_COOKIE_WAIT] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_SCTP_COOKIE_ECHOED] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_SCTP_ESTABLISHED] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_SCTP_SHUTDOWN_SENT] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_SCTP_SHUTDOWN_RECD] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT] = { .type = NLA_U32 },
+};
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
+
+
#ifdef CONFIG_SYSCTL
static unsigned int sctp_sysctl_table_users;
static struct ctl_table_header *sctp_sysctl_header;
@@ -664,6 +721,7 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = {
.print_tuple = sctp_print_tuple,
.print_conntrack = sctp_print_conntrack,
.packet = sctp_packet,
+ .get_timeouts = sctp_get_timeouts,
.new = sctp_new,
.me = THIS_MODULE,
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
@@ -675,6 +733,15 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = {
.nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
.nla_policy = nf_ct_port_nla_policy,
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = sctp_timeout_nlattr_to_obj,
+ .obj_to_nlattr = sctp_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_SCTP_MAX,
+ .obj_size = sizeof(unsigned int) * SCTP_CONNTRACK_MAX,
+ .nla_policy = sctp_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
#ifdef CONFIG_SYSCTL
.ctl_table_users = &sctp_sysctl_table_users,
.ctl_table_header = &sctp_sysctl_header,
@@ -694,6 +761,7 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = {
.print_tuple = sctp_print_tuple,
.print_conntrack = sctp_print_conntrack,
.packet = sctp_packet,
+ .get_timeouts = sctp_get_timeouts,
.new = sctp_new,
.me = THIS_MODULE,
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
@@ -704,6 +772,15 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = {
.nlattr_tuple_size = nf_ct_port_nlattr_tuple_size,
.nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
.nla_policy = nf_ct_port_nla_policy,
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = sctp_timeout_nlattr_to_obj,
+ .obj_to_nlattr = sctp_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_SCTP_MAX,
+ .obj_size = sizeof(unsigned int) * SCTP_CONNTRACK_MAX,
+ .nla_policy = sctp_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
#endif
#ifdef CONFIG_SYSCTL
.ctl_table_users = &sctp_sysctl_table_users,
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index 97b9f3ebf28..361eade62a0 100644
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -64,13 +64,7 @@ static const char *const tcp_conntrack_names[] = {
#define HOURS * 60 MINS
#define DAYS * 24 HOURS
-/* RFC1122 says the R2 limit should be at least 100 seconds.
- Linux uses 15 packets as limit, which corresponds
- to ~13-30min depending on RTO. */
-static unsigned int nf_ct_tcp_timeout_max_retrans __read_mostly = 5 MINS;
-static unsigned int nf_ct_tcp_timeout_unacknowledged __read_mostly = 5 MINS;
-
-static unsigned int tcp_timeouts[TCP_CONNTRACK_MAX] __read_mostly = {
+static unsigned int tcp_timeouts[TCP_CONNTRACK_TIMEOUT_MAX] __read_mostly = {
[TCP_CONNTRACK_SYN_SENT] = 2 MINS,
[TCP_CONNTRACK_SYN_RECV] = 60 SECS,
[TCP_CONNTRACK_ESTABLISHED] = 5 DAYS,
@@ -80,6 +74,11 @@ static unsigned int tcp_timeouts[TCP_CONNTRACK_MAX] __read_mostly = {
[TCP_CONNTRACK_TIME_WAIT] = 2 MINS,
[TCP_CONNTRACK_CLOSE] = 10 SECS,
[TCP_CONNTRACK_SYN_SENT2] = 2 MINS,
+/* RFC1122 says the R2 limit should be at least 100 seconds.
+ Linux uses 15 packets as limit, which corresponds
+ to ~13-30min depending on RTO. */
+ [TCP_CONNTRACK_RETRANS] = 5 MINS,
+ [TCP_CONNTRACK_UNACK] = 5 MINS,
};
#define sNO TCP_CONNTRACK_NONE
@@ -814,13 +813,19 @@ static int tcp_error(struct net *net, struct nf_conn *tmpl,
return NF_ACCEPT;
}
+static unsigned int *tcp_get_timeouts(struct net *net)
+{
+ return tcp_timeouts;
+}
+
/* Returns verdict for packet, or -1 for invalid. */
static int tcp_packet(struct nf_conn *ct,
const struct sk_buff *skb,
unsigned int dataoff,
enum ip_conntrack_info ctinfo,
u_int8_t pf,
- unsigned int hooknum)
+ unsigned int hooknum,
+ unsigned int *timeouts)
{
struct net *net = nf_ct_net(ct);
struct nf_conntrack_tuple *tuple;
@@ -1015,14 +1020,14 @@ static int tcp_packet(struct nf_conn *ct,
ct->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT;
if (ct->proto.tcp.retrans >= nf_ct_tcp_max_retrans &&
- tcp_timeouts[new_state] > nf_ct_tcp_timeout_max_retrans)
- timeout = nf_ct_tcp_timeout_max_retrans;
+ timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS])
+ timeout = timeouts[TCP_CONNTRACK_RETRANS];
else if ((ct->proto.tcp.seen[0].flags | ct->proto.tcp.seen[1].flags) &
IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED &&
- tcp_timeouts[new_state] > nf_ct_tcp_timeout_unacknowledged)
- timeout = nf_ct_tcp_timeout_unacknowledged;
+ timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK])
+ timeout = timeouts[TCP_CONNTRACK_UNACK];
else
- timeout = tcp_timeouts[new_state];
+ timeout = timeouts[new_state];
spin_unlock_bh(&ct->lock);
if (new_state != old_state)
@@ -1054,7 +1059,7 @@ static int tcp_packet(struct nf_conn *ct,
/* Called when a new connection for this protocol found. */
static bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb,
- unsigned int dataoff)
+ unsigned int dataoff, unsigned int *timeouts)
{
enum tcp_conntrack new_state;
const struct tcphdr *th;
@@ -1239,6 +1244,113 @@ static int tcp_nlattr_tuple_size(void)
}
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_cttimeout.h>
+
+static int tcp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data)
+{
+ unsigned int *timeouts = data;
+ int i;
+
+ /* set default TCP timeouts. */
+ for (i=0; i<TCP_CONNTRACK_TIMEOUT_MAX; i++)
+ timeouts[i] = tcp_timeouts[i];
+
+ if (tb[CTA_TIMEOUT_TCP_SYN_SENT]) {
+ timeouts[TCP_CONNTRACK_SYN_SENT] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_TCP_SYN_SENT]))*HZ;
+ }
+ if (tb[CTA_TIMEOUT_TCP_SYN_RECV]) {
+ timeouts[TCP_CONNTRACK_SYN_RECV] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_TCP_SYN_RECV]))*HZ;
+ }
+ if (tb[CTA_TIMEOUT_TCP_ESTABLISHED]) {
+ timeouts[TCP_CONNTRACK_ESTABLISHED] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_TCP_ESTABLISHED]))*HZ;
+ }
+ if (tb[CTA_TIMEOUT_TCP_FIN_WAIT]) {
+ timeouts[TCP_CONNTRACK_FIN_WAIT] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_TCP_FIN_WAIT]))*HZ;
+ }
+ if (tb[CTA_TIMEOUT_TCP_CLOSE_WAIT]) {
+ timeouts[TCP_CONNTRACK_CLOSE_WAIT] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_TCP_CLOSE_WAIT]))*HZ;
+ }
+ if (tb[CTA_TIMEOUT_TCP_LAST_ACK]) {
+ timeouts[TCP_CONNTRACK_LAST_ACK] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_TCP_LAST_ACK]))*HZ;
+ }
+ if (tb[CTA_TIMEOUT_TCP_TIME_WAIT]) {
+ timeouts[TCP_CONNTRACK_TIME_WAIT] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_TCP_TIME_WAIT]))*HZ;
+ }
+ if (tb[CTA_TIMEOUT_TCP_CLOSE]) {
+ timeouts[TCP_CONNTRACK_CLOSE] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_TCP_CLOSE]))*HZ;
+ }
+ if (tb[CTA_TIMEOUT_TCP_SYN_SENT2]) {
+ timeouts[TCP_CONNTRACK_SYN_SENT2] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_TCP_SYN_SENT2]))*HZ;
+ }
+ if (tb[CTA_TIMEOUT_TCP_RETRANS]) {
+ timeouts[TCP_CONNTRACK_RETRANS] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_TCP_RETRANS]))*HZ;
+ }
+ if (tb[CTA_TIMEOUT_TCP_UNACK]) {
+ timeouts[TCP_CONNTRACK_UNACK] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_TCP_UNACK]))*HZ;
+ }
+ return 0;
+}
+
+static int
+tcp_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
+{
+ const unsigned int *timeouts = data;
+
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_TCP_SYN_SENT,
+ htonl(timeouts[TCP_CONNTRACK_SYN_SENT] / HZ));
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_TCP_SYN_RECV,
+ htonl(timeouts[TCP_CONNTRACK_SYN_RECV] / HZ));
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_TCP_ESTABLISHED,
+ htonl(timeouts[TCP_CONNTRACK_ESTABLISHED] / HZ));
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_TCP_FIN_WAIT,
+ htonl(timeouts[TCP_CONNTRACK_FIN_WAIT] / HZ));
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_TCP_CLOSE_WAIT,
+ htonl(timeouts[TCP_CONNTRACK_CLOSE_WAIT] / HZ));
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_TCP_LAST_ACK,
+ htonl(timeouts[TCP_CONNTRACK_LAST_ACK] / HZ));
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_TCP_TIME_WAIT,
+ htonl(timeouts[TCP_CONNTRACK_TIME_WAIT] / HZ));
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_TCP_CLOSE,
+ htonl(timeouts[TCP_CONNTRACK_CLOSE] / HZ));
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_TCP_SYN_SENT2,
+ htonl(timeouts[TCP_CONNTRACK_SYN_SENT2] / HZ));
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_TCP_RETRANS,
+ htonl(timeouts[TCP_CONNTRACK_RETRANS] / HZ));
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_TCP_UNACK,
+ htonl(timeouts[TCP_CONNTRACK_UNACK] / HZ));
+ return 0;
+
+nla_put_failure:
+ return -ENOSPC;
+}
+
+static const struct nla_policy tcp_timeout_nla_policy[CTA_TIMEOUT_TCP_MAX+1] = {
+ [CTA_TIMEOUT_TCP_SYN_SENT] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_TCP_SYN_RECV] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_TCP_ESTABLISHED] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_TCP_FIN_WAIT] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_TCP_CLOSE_WAIT] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_TCP_LAST_ACK] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_TCP_TIME_WAIT] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_TCP_CLOSE] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_TCP_SYN_SENT2] = { .type = NLA_U32 },
+};
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
+
#ifdef CONFIG_SYSCTL
static unsigned int tcp_sysctl_table_users;
static struct ctl_table_header *tcp_sysctl_header;
@@ -1301,14 +1413,14 @@ static struct ctl_table tcp_sysctl_table[] = {
},
{
.procname = "nf_conntrack_tcp_timeout_max_retrans",
- .data = &nf_ct_tcp_timeout_max_retrans,
+ .data = &tcp_timeouts[TCP_CONNTRACK_RETRANS],
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_tcp_timeout_unacknowledged",
- .data = &nf_ct_tcp_timeout_unacknowledged,
+ .data = &tcp_timeouts[TCP_CONNTRACK_UNACK],
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
@@ -1404,7 +1516,7 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
},
{
.procname = "ip_conntrack_tcp_timeout_max_retrans",
- .data = &nf_ct_tcp_timeout_max_retrans,
+ .data = &tcp_timeouts[TCP_CONNTRACK_RETRANS],
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
@@ -1445,6 +1557,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly =
.print_tuple = tcp_print_tuple,
.print_conntrack = tcp_print_conntrack,
.packet = tcp_packet,
+ .get_timeouts = tcp_get_timeouts,
.new = tcp_new,
.error = tcp_error,
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
@@ -1456,6 +1569,16 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly =
.nlattr_tuple_size = tcp_nlattr_tuple_size,
.nla_policy = nf_ct_port_nla_policy,
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = tcp_timeout_nlattr_to_obj,
+ .obj_to_nlattr = tcp_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_TCP_MAX,
+ .obj_size = sizeof(unsigned int) *
+ TCP_CONNTRACK_TIMEOUT_MAX,
+ .nla_policy = tcp_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
#ifdef CONFIG_SYSCTL
.ctl_table_users = &tcp_sysctl_table_users,
.ctl_table_header = &tcp_sysctl_header,
@@ -1477,6 +1600,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 __read_mostly =
.print_tuple = tcp_print_tuple,
.print_conntrack = tcp_print_conntrack,
.packet = tcp_packet,
+ .get_timeouts = tcp_get_timeouts,
.new = tcp_new,
.error = tcp_error,
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
@@ -1488,6 +1612,16 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 __read_mostly =
.nlattr_tuple_size = tcp_nlattr_tuple_size,
.nla_policy = nf_ct_port_nla_policy,
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = tcp_timeout_nlattr_to_obj,
+ .obj_to_nlattr = tcp_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_TCP_MAX,
+ .obj_size = sizeof(unsigned int) *
+ TCP_CONNTRACK_TIMEOUT_MAX,
+ .nla_policy = tcp_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
#ifdef CONFIG_SYSCTL
.ctl_table_users = &tcp_sysctl_table_users,
.ctl_table_header = &tcp_sysctl_header,
diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c
index 5f35757fbff..a9073dc1548 100644
--- a/net/netfilter/nf_conntrack_proto_udp.c
+++ b/net/netfilter/nf_conntrack_proto_udp.c
@@ -25,8 +25,16 @@
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
#include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
-static unsigned int nf_ct_udp_timeout __read_mostly = 30*HZ;
-static unsigned int nf_ct_udp_timeout_stream __read_mostly = 180*HZ;
+enum udp_conntrack {
+ UDP_CT_UNREPLIED,
+ UDP_CT_REPLIED,
+ UDP_CT_MAX
+};
+
+static unsigned int udp_timeouts[UDP_CT_MAX] = {
+ [UDP_CT_UNREPLIED] = 30*HZ,
+ [UDP_CT_REPLIED] = 180*HZ,
+};
static bool udp_pkt_to_tuple(const struct sk_buff *skb,
unsigned int dataoff,
@@ -63,30 +71,38 @@ static int udp_print_tuple(struct seq_file *s,
ntohs(tuple->dst.u.udp.port));
}
+static unsigned int *udp_get_timeouts(struct net *net)
+{
+ return udp_timeouts;
+}
+
/* Returns verdict for packet, and may modify conntracktype */
static int udp_packet(struct nf_conn *ct,
const struct sk_buff *skb,
unsigned int dataoff,
enum ip_conntrack_info ctinfo,
u_int8_t pf,
- unsigned int hooknum)
+ unsigned int hooknum,
+ unsigned int *timeouts)
{
/* If we've seen traffic both ways, this is some kind of UDP
stream. Extend timeout. */
if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
- nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout_stream);
+ nf_ct_refresh_acct(ct, ctinfo, skb,
+ timeouts[UDP_CT_REPLIED]);
/* Also, more likely to be important, and not a probe */
if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
nf_conntrack_event_cache(IPCT_ASSURED, ct);
- } else
- nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout);
-
+ } else {
+ nf_ct_refresh_acct(ct, ctinfo, skb,
+ timeouts[UDP_CT_UNREPLIED]);
+ }
return NF_ACCEPT;
}
/* Called when a new connection for this protocol found. */
static bool udp_new(struct nf_conn *ct, const struct sk_buff *skb,
- unsigned int dataoff)
+ unsigned int dataoff, unsigned int *timeouts)
{
return true;
}
@@ -136,20 +152,66 @@ static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
return NF_ACCEPT;
}
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_cttimeout.h>
+
+static int udp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data)
+{
+ unsigned int *timeouts = data;
+
+ /* set default timeouts for UDP. */
+ timeouts[UDP_CT_UNREPLIED] = udp_timeouts[UDP_CT_UNREPLIED];
+ timeouts[UDP_CT_REPLIED] = udp_timeouts[UDP_CT_REPLIED];
+
+ if (tb[CTA_TIMEOUT_UDP_UNREPLIED]) {
+ timeouts[UDP_CT_UNREPLIED] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_UDP_UNREPLIED])) * HZ;
+ }
+ if (tb[CTA_TIMEOUT_UDP_REPLIED]) {
+ timeouts[UDP_CT_REPLIED] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_UDP_REPLIED])) * HZ;
+ }
+ return 0;
+}
+
+static int
+udp_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
+{
+ const unsigned int *timeouts = data;
+
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_UDP_UNREPLIED,
+ htonl(timeouts[UDP_CT_UNREPLIED] / HZ));
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_UDP_REPLIED,
+ htonl(timeouts[UDP_CT_REPLIED] / HZ));
+ return 0;
+
+nla_put_failure:
+ return -ENOSPC;
+}
+
+static const struct nla_policy
+udp_timeout_nla_policy[CTA_TIMEOUT_UDP_MAX+1] = {
+ [CTA_TIMEOUT_UDP_UNREPLIED] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_UDP_REPLIED] = { .type = NLA_U32 },
+};
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
+
#ifdef CONFIG_SYSCTL
static unsigned int udp_sysctl_table_users;
static struct ctl_table_header *udp_sysctl_header;
static struct ctl_table udp_sysctl_table[] = {
{
.procname = "nf_conntrack_udp_timeout",
- .data = &nf_ct_udp_timeout,
+ .data = &udp_timeouts[UDP_CT_UNREPLIED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_udp_timeout_stream",
- .data = &nf_ct_udp_timeout_stream,
+ .data = &udp_timeouts[UDP_CT_REPLIED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
@@ -160,14 +222,14 @@ static struct ctl_table udp_sysctl_table[] = {
static struct ctl_table udp_compat_sysctl_table[] = {
{
.procname = "ip_conntrack_udp_timeout",
- .data = &nf_ct_udp_timeout,
+ .data = &udp_timeouts[UDP_CT_UNREPLIED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_udp_timeout_stream",
- .data = &nf_ct_udp_timeout_stream,
+ .data = &udp_timeouts[UDP_CT_REPLIED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
@@ -186,6 +248,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly =
.invert_tuple = udp_invert_tuple,
.print_tuple = udp_print_tuple,
.packet = udp_packet,
+ .get_timeouts = udp_get_timeouts,
.new = udp_new,
.error = udp_error,
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
@@ -194,6 +257,15 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly =
.nlattr_tuple_size = nf_ct_port_nlattr_tuple_size,
.nla_policy = nf_ct_port_nla_policy,
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = udp_timeout_nlattr_to_obj,
+ .obj_to_nlattr = udp_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_UDP_MAX,
+ .obj_size = sizeof(unsigned int) * CTA_TIMEOUT_UDP_MAX,
+ .nla_policy = udp_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
#ifdef CONFIG_SYSCTL
.ctl_table_users = &udp_sysctl_table_users,
.ctl_table_header = &udp_sysctl_header,
@@ -214,6 +286,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 __read_mostly =
.invert_tuple = udp_invert_tuple,
.print_tuple = udp_print_tuple,
.packet = udp_packet,
+ .get_timeouts = udp_get_timeouts,
.new = udp_new,
.error = udp_error,
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
@@ -222,6 +295,15 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 __read_mostly =
.nlattr_tuple_size = nf_ct_port_nlattr_tuple_size,
.nla_policy = nf_ct_port_nla_policy,
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = udp_timeout_nlattr_to_obj,
+ .obj_to_nlattr = udp_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_UDP_MAX,
+ .obj_size = sizeof(unsigned int) * CTA_TIMEOUT_UDP_MAX,
+ .nla_policy = udp_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
#ifdef CONFIG_SYSCTL
.ctl_table_users = &udp_sysctl_table_users,
.ctl_table_header = &udp_sysctl_header,
diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c
index f52ca118101..e0606392cda 100644
--- a/net/netfilter/nf_conntrack_proto_udplite.c
+++ b/net/netfilter/nf_conntrack_proto_udplite.c
@@ -24,8 +24,16 @@
#include <net/netfilter/nf_conntrack_ecache.h>
#include <net/netfilter/nf_log.h>
-static unsigned int nf_ct_udplite_timeout __read_mostly = 30*HZ;
-static unsigned int nf_ct_udplite_timeout_stream __read_mostly = 180*HZ;
+enum udplite_conntrack {
+ UDPLITE_CT_UNREPLIED,
+ UDPLITE_CT_REPLIED,
+ UDPLITE_CT_MAX
+};
+
+static unsigned int udplite_timeouts[UDPLITE_CT_MAX] = {
+ [UDPLITE_CT_UNREPLIED] = 30*HZ,
+ [UDPLITE_CT_REPLIED] = 180*HZ,
+};
static bool udplite_pkt_to_tuple(const struct sk_buff *skb,
unsigned int dataoff,
@@ -60,31 +68,38 @@ static int udplite_print_tuple(struct seq_file *s,
ntohs(tuple->dst.u.udp.port));
}
+static unsigned int *udplite_get_timeouts(struct net *net)
+{
+ return udplite_timeouts;
+}
+
/* Returns verdict for packet, and may modify conntracktype */
static int udplite_packet(struct nf_conn *ct,
const struct sk_buff *skb,
unsigned int dataoff,
enum ip_conntrack_info ctinfo,
u_int8_t pf,
- unsigned int hooknum)
+ unsigned int hooknum,
+ unsigned int *timeouts)
{
/* If we've seen traffic both ways, this is some kind of UDP
stream. Extend timeout. */
if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
nf_ct_refresh_acct(ct, ctinfo, skb,
- nf_ct_udplite_timeout_stream);
+ timeouts[UDPLITE_CT_REPLIED]);
/* Also, more likely to be important, and not a probe */
if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
nf_conntrack_event_cache(IPCT_ASSURED, ct);
- } else
- nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udplite_timeout);
-
+ } else {
+ nf_ct_refresh_acct(ct, ctinfo, skb,
+ timeouts[UDPLITE_CT_UNREPLIED]);
+ }
return NF_ACCEPT;
}
/* Called when a new connection for this protocol found. */
static bool udplite_new(struct nf_conn *ct, const struct sk_buff *skb,
- unsigned int dataoff)
+ unsigned int dataoff, unsigned int *timeouts)
{
return true;
}
@@ -141,20 +156,66 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl,
return NF_ACCEPT;
}
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_cttimeout.h>
+
+static int udplite_timeout_nlattr_to_obj(struct nlattr *tb[], void *data)
+{
+ unsigned int *timeouts = data;
+
+ /* set default timeouts for UDPlite. */
+ timeouts[UDPLITE_CT_UNREPLIED] = udplite_timeouts[UDPLITE_CT_UNREPLIED];
+ timeouts[UDPLITE_CT_REPLIED] = udplite_timeouts[UDPLITE_CT_REPLIED];
+
+ if (tb[CTA_TIMEOUT_UDPLITE_UNREPLIED]) {
+ timeouts[UDPLITE_CT_UNREPLIED] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_UDPLITE_UNREPLIED])) * HZ;
+ }
+ if (tb[CTA_TIMEOUT_UDPLITE_REPLIED]) {
+ timeouts[UDPLITE_CT_REPLIED] =
+ ntohl(nla_get_be32(tb[CTA_TIMEOUT_UDPLITE_REPLIED])) * HZ;
+ }
+ return 0;
+}
+
+static int
+udplite_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
+{
+ const unsigned int *timeouts = data;
+
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_UDPLITE_UNREPLIED,
+ htonl(timeouts[UDPLITE_CT_UNREPLIED] / HZ));
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_UDPLITE_REPLIED,
+ htonl(timeouts[UDPLITE_CT_REPLIED] / HZ));
+ return 0;
+
+nla_put_failure:
+ return -ENOSPC;
+}
+
+static const struct nla_policy
+udplite_timeout_nla_policy[CTA_TIMEOUT_UDPLITE_MAX+1] = {
+ [CTA_TIMEOUT_UDPLITE_UNREPLIED] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_UDPLITE_REPLIED] = { .type = NLA_U32 },
+};
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
+
#ifdef CONFIG_SYSCTL
static unsigned int udplite_sysctl_table_users;
static struct ctl_table_header *udplite_sysctl_header;
static struct ctl_table udplite_sysctl_table[] = {
{
.procname = "nf_conntrack_udplite_timeout",
- .data = &nf_ct_udplite_timeout,
+ .data = &udplite_timeouts[UDPLITE_CT_UNREPLIED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_udplite_timeout_stream",
- .data = &nf_ct_udplite_timeout_stream,
+ .data = &udplite_timeouts[UDPLITE_CT_REPLIED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
@@ -172,6 +233,7 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 __read_mostly =
.invert_tuple = udplite_invert_tuple,
.print_tuple = udplite_print_tuple,
.packet = udplite_packet,
+ .get_timeouts = udplite_get_timeouts,
.new = udplite_new,
.error = udplite_error,
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
@@ -180,6 +242,16 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 __read_mostly =
.nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
.nla_policy = nf_ct_port_nla_policy,
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = udplite_timeout_nlattr_to_obj,
+ .obj_to_nlattr = udplite_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_UDPLITE_MAX,
+ .obj_size = sizeof(unsigned int) *
+ CTA_TIMEOUT_UDPLITE_MAX,
+ .nla_policy = udplite_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
#ifdef CONFIG_SYSCTL
.ctl_table_users = &udplite_sysctl_table_users,
.ctl_table_header = &udplite_sysctl_header,
@@ -196,6 +268,7 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 __read_mostly =
.invert_tuple = udplite_invert_tuple,
.print_tuple = udplite_print_tuple,
.packet = udplite_packet,
+ .get_timeouts = udplite_get_timeouts,
.new = udplite_new,
.error = udplite_error,
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
@@ -204,6 +277,16 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 __read_mostly =
.nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
.nla_policy = nf_ct_port_nla_policy,
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ .ctnl_timeout = {
+ .nlattr_to_obj = udplite_timeout_nlattr_to_obj,
+ .obj_to_nlattr = udplite_timeout_obj_to_nlattr,
+ .nlattr_max = CTA_TIMEOUT_UDPLITE_MAX,
+ .obj_size = sizeof(unsigned int) *
+ CTA_TIMEOUT_UDPLITE_MAX,
+ .nla_policy = udplite_timeout_nla_policy,
+ },
+#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
#ifdef CONFIG_SYSCTL
.ctl_table_users = &udplite_sysctl_table_users,
.ctl_table_header = &udplite_sysctl_header,
diff --git a/net/netfilter/nf_conntrack_timeout.c b/net/netfilter/nf_conntrack_timeout.c
new file mode 100644
index 00000000000..a878ce5b252
--- /dev/null
+++ b/net/netfilter/nf_conntrack_timeout.c
@@ -0,0 +1,60 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2012 by Vyatta Inc. <http://www.vyatta.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 (or any later at your option).
+ */
+
+#include <linux/types.h>
+#include <linux/netfilter.h>
+#include <linux/skbuff.h>
+#include <linux/vmalloc.h>
+#include <linux/stddef.h>
+#include <linux/err.h>
+#include <linux/percpu.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_core.h>
+#include <net/netfilter/nf_conntrack_extend.h>
+#include <net/netfilter/nf_conntrack_timeout.h>
+
+struct ctnl_timeout *
+(*nf_ct_timeout_find_get_hook)(const char *name) __read_mostly;
+EXPORT_SYMBOL_GPL(nf_ct_timeout_find_get_hook);
+
+void (*nf_ct_timeout_put_hook)(struct ctnl_timeout *timeout) __read_mostly;
+EXPORT_SYMBOL_GPL(nf_ct_timeout_put_hook);
+
+static struct nf_ct_ext_type timeout_extend __read_mostly = {
+ .len = sizeof(struct nf_conn_timeout),
+ .align = __alignof__(struct nf_conn_timeout),
+ .id = NF_CT_EXT_TIMEOUT,
+};
+
+int nf_conntrack_timeout_init(struct net *net)
+{
+ int ret = 0;
+
+ if (net_eq(net, &init_net)) {
+ ret = nf_ct_extend_register(&timeout_extend);
+ if (ret < 0) {
+ printk(KERN_ERR "nf_ct_timeout: Unable to register "
+ "timeout extension.\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void nf_conntrack_timeout_fini(struct net *net)
+{
+ if (net_eq(net, &init_net))
+ nf_ct_extend_unregister(&timeout_extend);
+}
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
index b3a7db678b8..ce60cf0f6c1 100644
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
@@ -203,6 +203,27 @@ err:
return status;
}
+#ifdef CONFIG_BRIDGE_NETFILTER
+/* When called from bridge netfilter, skb->data must point to MAC header
+ * before calling skb_gso_segment(). Else, original MAC header is lost
+ * and segmented skbs will be sent to wrong destination.
+ */
+static void nf_bridge_adjust_skb_data(struct sk_buff *skb)
+{
+ if (skb->nf_bridge)
+ __skb_push(skb, skb->network_header - skb->mac_header);
+}
+
+static void nf_bridge_adjust_segmented_data(struct sk_buff *skb)
+{
+ if (skb->nf_bridge)
+ __skb_pull(skb, skb->network_header - skb->mac_header);
+}
+#else
+#define nf_bridge_adjust_skb_data(s) do {} while (0)
+#define nf_bridge_adjust_segmented_data(s) do {} while (0)
+#endif
+
int nf_queue(struct sk_buff *skb,
struct list_head *elem,
u_int8_t pf, unsigned int hook,
@@ -212,7 +233,7 @@ int nf_queue(struct sk_buff *skb,
unsigned int queuenum)
{
struct sk_buff *segs;
- int err;
+ int err = -EINVAL;
unsigned int queued;
if (!skb_is_gso(skb))
@@ -228,23 +249,25 @@ int nf_queue(struct sk_buff *skb,
break;
}
+ nf_bridge_adjust_skb_data(skb);
segs = skb_gso_segment(skb, 0);
/* Does not use PTR_ERR to limit the number of error codes that can be
* returned by nf_queue. For instance, callers rely on -ECANCELED to mean
* 'ignore this hook'.
*/
if (IS_ERR(segs))
- return -EINVAL;
-
+ goto out_err;
queued = 0;
err = 0;
do {
struct sk_buff *nskb = segs->next;
segs->next = NULL;
- if (err == 0)
+ if (err == 0) {
+ nf_bridge_adjust_segmented_data(segs);
err = __nf_queue(segs, elem, pf, hook, indev,
outdev, okfn, queuenum);
+ }
if (err == 0)
queued++;
else
@@ -252,11 +275,12 @@ int nf_queue(struct sk_buff *skb,
segs = nskb;
} while (segs);
- /* also free orig skb if only some segments were queued */
- if (unlikely(err && queued))
- err = 0;
- if (err == 0)
+ if (queued) {
kfree_skb(skb);
+ return 0;
+ }
+ out_err:
+ nf_bridge_adjust_segmented_data(skb);
return err;
}
diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c
index 11ba013e47f..3eb348bfc4f 100644
--- a/net/netfilter/nfnetlink_acct.c
+++ b/net/netfilter/nfnetlink_acct.c
@@ -171,8 +171,10 @@ nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb,
char *acct_name;
if (nlh->nlmsg_flags & NLM_F_DUMP) {
- return netlink_dump_start(nfnl, skb, nlh, nfnl_acct_dump,
- NULL, 0);
+ struct netlink_dump_control c = {
+ .dump = nfnl_acct_dump,
+ };
+ return netlink_dump_start(nfnl, skb, nlh, &c);
}
if (!tb[NFACCT_NAME])
diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c
new file mode 100644
index 00000000000..fec29a43de4
--- /dev/null
+++ b/net/netfilter/nfnetlink_cttimeout.c
@@ -0,0 +1,429 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2012 by Vyatta Inc. <http://www.vyatta.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 (or any later at your option).
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/rculist.h>
+#include <linux/rculist_nulls.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/security.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/netlink.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include <linux/netfilter.h>
+#include <net/netlink.h>
+#include <net/sock.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_core.h>
+#include <net/netfilter/nf_conntrack_l3proto.h>
+#include <net/netfilter/nf_conntrack_l4proto.h>
+#include <net/netfilter/nf_conntrack_tuple.h>
+#include <net/netfilter/nf_conntrack_timeout.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_cttimeout.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
+MODULE_DESCRIPTION("cttimeout: Extended Netfilter Connection Tracking timeout tuning");
+
+static LIST_HEAD(cttimeout_list);
+
+static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = {
+ [CTA_TIMEOUT_NAME] = { .type = NLA_NUL_STRING },
+ [CTA_TIMEOUT_L3PROTO] = { .type = NLA_U16 },
+ [CTA_TIMEOUT_L4PROTO] = { .type = NLA_U8 },
+ [CTA_TIMEOUT_DATA] = { .type = NLA_NESTED },
+};
+
+static int
+ctnl_timeout_parse_policy(struct ctnl_timeout *timeout,
+ struct nf_conntrack_l4proto *l4proto,
+ const struct nlattr *attr)
+{
+ int ret = 0;
+
+ if (likely(l4proto->ctnl_timeout.nlattr_to_obj)) {
+ struct nlattr *tb[l4proto->ctnl_timeout.nlattr_max+1];
+
+ nla_parse_nested(tb, l4proto->ctnl_timeout.nlattr_max,
+ attr, l4proto->ctnl_timeout.nla_policy);
+
+ ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, &timeout->data);
+ }
+ return ret;
+}
+
+static int
+cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
+{
+ __u16 l3num;
+ __u8 l4num;
+ struct nf_conntrack_l4proto *l4proto;
+ struct ctnl_timeout *timeout, *matching = NULL;
+ char *name;
+ int ret;
+
+ if (!cda[CTA_TIMEOUT_NAME] ||
+ !cda[CTA_TIMEOUT_L3PROTO] ||
+ !cda[CTA_TIMEOUT_L4PROTO] ||
+ !cda[CTA_TIMEOUT_DATA])
+ return -EINVAL;
+
+ name = nla_data(cda[CTA_TIMEOUT_NAME]);
+ l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
+ l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
+
+ list_for_each_entry(timeout, &cttimeout_list, head) {
+ if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
+ continue;
+
+ if (nlh->nlmsg_flags & NLM_F_EXCL)
+ return -EEXIST;
+
+ matching = timeout;
+ break;
+ }
+
+ l4proto = __nf_ct_l4proto_find(l3num, l4num);
+
+ /* This protocol is not supportted, skip. */
+ if (l4proto->l4proto != l4num)
+ return -EOPNOTSUPP;
+
+ if (matching) {
+ if (nlh->nlmsg_flags & NLM_F_REPLACE) {
+ /* You cannot replace one timeout policy by another of
+ * different kind, sorry.
+ */
+ if (matching->l3num != l3num ||
+ matching->l4num != l4num)
+ return -EINVAL;
+
+ ret = ctnl_timeout_parse_policy(matching, l4proto,
+ cda[CTA_TIMEOUT_DATA]);
+ return ret;
+ }
+ return -EBUSY;
+ }
+
+ timeout = kzalloc(sizeof(struct ctnl_timeout) +
+ l4proto->ctnl_timeout.obj_size, GFP_KERNEL);
+ if (timeout == NULL)
+ return -ENOMEM;
+
+ ret = ctnl_timeout_parse_policy(timeout, l4proto,
+ cda[CTA_TIMEOUT_DATA]);
+ if (ret < 0)
+ goto err;
+
+ strcpy(timeout->name, nla_data(cda[CTA_TIMEOUT_NAME]));
+ timeout->l3num = l3num;
+ timeout->l4num = l4num;
+ atomic_set(&timeout->refcnt, 1);
+ list_add_tail_rcu(&timeout->head, &cttimeout_list);
+
+ return 0;
+err:
+ kfree(timeout);
+ return ret;
+}
+
+static int
+ctnl_timeout_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type,
+ int event, struct ctnl_timeout *timeout)
+{
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfmsg;
+ unsigned int flags = pid ? NLM_F_MULTI : 0;
+ struct nf_conntrack_l4proto *l4proto;
+
+ event |= NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8;
+ nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags);
+ if (nlh == NULL)
+ goto nlmsg_failure;
+
+ nfmsg = nlmsg_data(nlh);
+ nfmsg->nfgen_family = AF_UNSPEC;
+ nfmsg->version = NFNETLINK_V0;
+ nfmsg->res_id = 0;
+
+ NLA_PUT_STRING(skb, CTA_TIMEOUT_NAME, timeout->name);
+ NLA_PUT_BE16(skb, CTA_TIMEOUT_L3PROTO, htons(timeout->l3num));
+ NLA_PUT_U8(skb, CTA_TIMEOUT_L4PROTO, timeout->l4num);
+ NLA_PUT_BE32(skb, CTA_TIMEOUT_USE,
+ htonl(atomic_read(&timeout->refcnt)));
+
+ l4proto = __nf_ct_l4proto_find(timeout->l3num, timeout->l4num);
+
+ /* If the timeout object does not match the layer 4 protocol tracker,
+ * then skip dumping the data part since we don't know how to
+ * interpret it. This may happen for UPDlite, SCTP and DCCP since
+ * you can unload the module.
+ */
+ if (timeout->l4num != l4proto->l4proto)
+ goto out;
+
+ if (likely(l4proto->ctnl_timeout.obj_to_nlattr)) {
+ struct nlattr *nest_parms;
+ int ret;
+
+ nest_parms = nla_nest_start(skb,
+ CTA_TIMEOUT_DATA | NLA_F_NESTED);
+ if (!nest_parms)
+ goto nla_put_failure;
+
+ ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, &timeout->data);
+ if (ret < 0)
+ goto nla_put_failure;
+
+ nla_nest_end(skb, nest_parms);
+ }
+out:
+ nlmsg_end(skb, nlh);
+ return skb->len;
+
+nlmsg_failure:
+nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+ return -1;
+}
+
+static int
+ctnl_timeout_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct ctnl_timeout *cur, *last;
+
+ if (cb->args[2])
+ return 0;
+
+ last = (struct ctnl_timeout *)cb->args[1];
+ if (cb->args[1])
+ cb->args[1] = 0;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(cur, &cttimeout_list, head) {
+ if (last && cur != last)
+ continue;
+
+ if (ctnl_timeout_fill_info(skb, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq,
+ NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
+ IPCTNL_MSG_TIMEOUT_NEW, cur) < 0) {
+ cb->args[1] = (unsigned long)cur;
+ break;
+ }
+ }
+ if (!cb->args[1])
+ cb->args[2] = 1;
+ rcu_read_unlock();
+ return skb->len;
+}
+
+static int
+cttimeout_get_timeout(struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
+{
+ int ret = -ENOENT;
+ char *name;
+ struct ctnl_timeout *cur;
+
+ if (nlh->nlmsg_flags & NLM_F_DUMP) {
+ struct netlink_dump_control c = {
+ .dump = ctnl_timeout_dump,
+ };
+ return netlink_dump_start(ctnl, skb, nlh, &c);
+ }
+
+ if (!cda[CTA_TIMEOUT_NAME])
+ return -EINVAL;
+ name = nla_data(cda[CTA_TIMEOUT_NAME]);
+
+ list_for_each_entry(cur, &cttimeout_list, head) {
+ struct sk_buff *skb2;
+
+ if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
+ continue;
+
+ skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (skb2 == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ ret = ctnl_timeout_fill_info(skb2, NETLINK_CB(skb).pid,
+ nlh->nlmsg_seq,
+ NFNL_MSG_TYPE(nlh->nlmsg_type),
+ IPCTNL_MSG_TIMEOUT_NEW, cur);
+ if (ret <= 0) {
+ kfree_skb(skb2);
+ break;
+ }
+ ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid,
+ MSG_DONTWAIT);
+ if (ret > 0)
+ ret = 0;
+
+ /* this avoids a loop in nfnetlink. */
+ return ret == -EAGAIN ? -ENOBUFS : ret;
+ }
+ return ret;
+}
+
+/* try to delete object, fail if it is still in use. */
+static int ctnl_timeout_try_del(struct ctnl_timeout *timeout)
+{
+ int ret = 0;
+
+ /* we want to avoid races with nf_ct_timeout_find_get. */
+ if (atomic_dec_and_test(&timeout->refcnt)) {
+ /* We are protected by nfnl mutex. */
+ list_del_rcu(&timeout->head);
+ kfree_rcu(timeout, rcu_head);
+ } else {
+ /* still in use, restore reference counter. */
+ atomic_inc(&timeout->refcnt);
+ ret = -EBUSY;
+ }
+ return ret;
+}
+
+static int
+cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
+{
+ char *name;
+ struct ctnl_timeout *cur;
+ int ret = -ENOENT;
+
+ if (!cda[CTA_TIMEOUT_NAME]) {
+ list_for_each_entry(cur, &cttimeout_list, head)
+ ctnl_timeout_try_del(cur);
+
+ return 0;
+ }
+ name = nla_data(cda[CTA_TIMEOUT_NAME]);
+
+ list_for_each_entry(cur, &cttimeout_list, head) {
+ if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
+ continue;
+
+ ret = ctnl_timeout_try_del(cur);
+ if (ret < 0)
+ return ret;
+
+ break;
+ }
+ return ret;
+}
+
+#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
+static struct ctnl_timeout *ctnl_timeout_find_get(const char *name)
+{
+ struct ctnl_timeout *timeout, *matching = NULL;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(timeout, &cttimeout_list, head) {
+ if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
+ continue;
+
+ if (!try_module_get(THIS_MODULE))
+ goto err;
+
+ if (!atomic_inc_not_zero(&timeout->refcnt)) {
+ module_put(THIS_MODULE);
+ goto err;
+ }
+ matching = timeout;
+ break;
+ }
+err:
+ rcu_read_unlock();
+ return matching;
+}
+
+static void ctnl_timeout_put(struct ctnl_timeout *timeout)
+{
+ atomic_dec(&timeout->refcnt);
+ module_put(THIS_MODULE);
+}
+#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
+
+static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = {
+ [IPCTNL_MSG_TIMEOUT_NEW] = { .call = cttimeout_new_timeout,
+ .attr_count = CTA_TIMEOUT_MAX,
+ .policy = cttimeout_nla_policy },
+ [IPCTNL_MSG_TIMEOUT_GET] = { .call = cttimeout_get_timeout,
+ .attr_count = CTA_TIMEOUT_MAX,
+ .policy = cttimeout_nla_policy },
+ [IPCTNL_MSG_TIMEOUT_DELETE] = { .call = cttimeout_del_timeout,
+ .attr_count = CTA_TIMEOUT_MAX,
+ .policy = cttimeout_nla_policy },
+};
+
+static const struct nfnetlink_subsystem cttimeout_subsys = {
+ .name = "conntrack_timeout",
+ .subsys_id = NFNL_SUBSYS_CTNETLINK_TIMEOUT,
+ .cb_count = IPCTNL_MSG_TIMEOUT_MAX,
+ .cb = cttimeout_cb,
+};
+
+MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_TIMEOUT);
+
+static int __init cttimeout_init(void)
+{
+ int ret;
+
+ ret = nfnetlink_subsys_register(&cttimeout_subsys);
+ if (ret < 0) {
+ pr_err("cttimeout_init: cannot register cttimeout with "
+ "nfnetlink.\n");
+ goto err_out;
+ }
+#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
+ RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, ctnl_timeout_find_get);
+ RCU_INIT_POINTER(nf_ct_timeout_put_hook, ctnl_timeout_put);
+#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
+ return 0;
+
+err_out:
+ return ret;
+}
+
+static void __exit cttimeout_exit(void)
+{
+ struct ctnl_timeout *cur, *tmp;
+
+ pr_info("cttimeout: unregistering from nfnetlink.\n");
+
+ nfnetlink_subsys_unregister(&cttimeout_subsys);
+ list_for_each_entry_safe(cur, tmp, &cttimeout_list, head) {
+ list_del_rcu(&cur->head);
+ /* We are sure that our objects have no clients at this point,
+ * it's safe to release them all without checking refcnt.
+ */
+ kfree_rcu(cur, rcu_head);
+ }
+#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
+ RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL);
+ RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL);
+#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
+}
+
+module_init(cttimeout_init);
+module_exit(cttimeout_exit);
diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
index 0221d10de75..b873445df44 100644
--- a/net/netfilter/xt_CT.c
+++ b/net/netfilter/xt_CT.c
@@ -16,10 +16,11 @@
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_ecache.h>
+#include <net/netfilter/nf_conntrack_timeout.h>
#include <net/netfilter/nf_conntrack_zones.h>
-static unsigned int xt_ct_target(struct sk_buff *skb,
- const struct xt_action_param *par)
+static unsigned int xt_ct_target_v0(struct sk_buff *skb,
+ const struct xt_action_param *par)
{
const struct xt_ct_target_info *info = par->targinfo;
struct nf_conn *ct = info->ct;
@@ -35,6 +36,23 @@ static unsigned int xt_ct_target(struct sk_buff *skb,
return XT_CONTINUE;
}
+static unsigned int xt_ct_target_v1(struct sk_buff *skb,
+ const struct xt_action_param *par)
+{
+ const struct xt_ct_target_info_v1 *info = par->targinfo;
+ struct nf_conn *ct = info->ct;
+
+ /* Previously seen (loopback)? Ignore. */
+ if (skb->nfct != NULL)
+ return XT_CONTINUE;
+
+ atomic_inc(&ct->ct_general.use);
+ skb->nfct = &ct->ct_general;
+ skb->nfctinfo = IP_CT_NEW;
+
+ return XT_CONTINUE;
+}
+
static u8 xt_ct_find_proto(const struct xt_tgchk_param *par)
{
if (par->family == NFPROTO_IPV4) {
@@ -53,7 +71,7 @@ static u8 xt_ct_find_proto(const struct xt_tgchk_param *par)
return 0;
}
-static int xt_ct_tg_check(const struct xt_tgchk_param *par)
+static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
{
struct xt_ct_target_info *info = par->targinfo;
struct nf_conntrack_tuple t;
@@ -130,7 +148,137 @@ err1:
return ret;
}
-static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par)
+static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
+{
+ struct xt_ct_target_info_v1 *info = par->targinfo;
+ struct nf_conntrack_tuple t;
+ struct nf_conn_help *help;
+ struct nf_conn *ct;
+ int ret = 0;
+ u8 proto;
+
+ if (info->flags & ~XT_CT_NOTRACK)
+ return -EINVAL;
+
+ if (info->flags & XT_CT_NOTRACK) {
+ ct = nf_ct_untracked_get();
+ atomic_inc(&ct->ct_general.use);
+ goto out;
+ }
+
+#ifndef CONFIG_NF_CONNTRACK_ZONES
+ if (info->zone)
+ goto err1;
+#endif
+
+ ret = nf_ct_l3proto_try_module_get(par->family);
+ if (ret < 0)
+ goto err1;
+
+ memset(&t, 0, sizeof(t));
+ ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL);
+ ret = PTR_ERR(ct);
+ if (IS_ERR(ct))
+ goto err2;
+
+ ret = 0;
+ if ((info->ct_events || info->exp_events) &&
+ !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
+ GFP_KERNEL))
+ goto err3;
+
+ if (info->helper[0]) {
+ ret = -ENOENT;
+ proto = xt_ct_find_proto(par);
+ if (!proto) {
+ pr_info("You must specify a L4 protocol, "
+ "and not use inversions on it.\n");
+ goto err3;
+ }
+
+ ret = -ENOMEM;
+ help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
+ if (help == NULL)
+ goto err3;
+
+ ret = -ENOENT;
+ help->helper = nf_conntrack_helper_try_module_get(info->helper,
+ par->family,
+ proto);
+ if (help->helper == NULL) {
+ pr_info("No such helper \"%s\"\n", info->helper);
+ goto err3;
+ }
+ }
+
+#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
+ if (info->timeout) {
+ typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
+ struct ctnl_timeout *timeout;
+ struct nf_conn_timeout *timeout_ext;
+
+ timeout_find_get =
+ rcu_dereference(nf_ct_timeout_find_get_hook);
+
+ if (timeout_find_get) {
+ const struct ipt_entry *e = par->entryinfo;
+
+ if (e->ip.invflags & IPT_INV_PROTO) {
+ ret = -EINVAL;
+ pr_info("You cannot use inversion on "
+ "L4 protocol\n");
+ goto err3;
+ }
+ timeout = timeout_find_get(info->timeout);
+ if (timeout == NULL) {
+ ret = -ENOENT;
+ pr_info("No such timeout policy \"%s\"\n",
+ info->timeout);
+ goto err3;
+ }
+ if (timeout->l3num != par->family) {
+ ret = -EINVAL;
+ pr_info("Timeout policy `%s' can only be "
+ "used by L3 protocol number %d\n",
+ info->timeout, timeout->l3num);
+ goto err3;
+ }
+ if (timeout->l4num != e->ip.proto) {
+ ret = -EINVAL;
+ pr_info("Timeout policy `%s' can only be "
+ "used by L4 protocol number %d\n",
+ info->timeout, timeout->l4num);
+ goto err3;
+ }
+ timeout_ext = nf_ct_timeout_ext_add(ct, timeout,
+ GFP_KERNEL);
+ if (timeout_ext == NULL) {
+ ret = -ENOMEM;
+ goto err3;
+ }
+ } else {
+ ret = -ENOENT;
+ pr_info("Timeout policy base is empty\n");
+ goto err3;
+ }
+ }
+#endif
+
+ __set_bit(IPS_TEMPLATE_BIT, &ct->status);
+ __set_bit(IPS_CONFIRMED_BIT, &ct->status);
+out:
+ info->ct = ct;
+ return 0;
+
+err3:
+ nf_conntrack_free(ct);
+err2:
+ nf_ct_l3proto_module_put(par->family);
+err1:
+ return ret;
+}
+
+static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
{
struct xt_ct_target_info *info = par->targinfo;
struct nf_conn *ct = info->ct;
@@ -146,25 +294,67 @@ static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par)
nf_ct_put(info->ct);
}
-static struct xt_target xt_ct_tg __read_mostly = {
- .name = "CT",
- .family = NFPROTO_UNSPEC,
- .targetsize = sizeof(struct xt_ct_target_info),
- .checkentry = xt_ct_tg_check,
- .destroy = xt_ct_tg_destroy,
- .target = xt_ct_target,
- .table = "raw",
- .me = THIS_MODULE,
+static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
+{
+ struct xt_ct_target_info_v1 *info = par->targinfo;
+ struct nf_conn *ct = info->ct;
+ struct nf_conn_help *help;
+#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
+ struct nf_conn_timeout *timeout_ext;
+ typeof(nf_ct_timeout_put_hook) timeout_put;
+#endif
+ if (!nf_ct_is_untracked(ct)) {
+ help = nfct_help(ct);
+ if (help)
+ module_put(help->helper->me);
+
+ nf_ct_l3proto_module_put(par->family);
+
+#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
+ timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
+
+ if (timeout_put) {
+ timeout_ext = nf_ct_timeout_find(ct);
+ if (timeout_ext)
+ timeout_put(timeout_ext->timeout);
+ }
+#endif
+ }
+ nf_ct_put(info->ct);
+}
+
+static struct xt_target xt_ct_tg_reg[] __read_mostly = {
+ {
+ .name = "CT",
+ .family = NFPROTO_UNSPEC,
+ .targetsize = sizeof(struct xt_ct_target_info),
+ .checkentry = xt_ct_tg_check_v0,
+ .destroy = xt_ct_tg_destroy_v0,
+ .target = xt_ct_target_v0,
+ .table = "raw",
+ .me = THIS_MODULE,
+ },
+ {
+ .name = "CT",
+ .family = NFPROTO_UNSPEC,
+ .revision = 1,
+ .targetsize = sizeof(struct xt_ct_target_info_v1),
+ .checkentry = xt_ct_tg_check_v1,
+ .destroy = xt_ct_tg_destroy_v1,
+ .target = xt_ct_target_v1,
+ .table = "raw",
+ .me = THIS_MODULE,
+ },
};
static int __init xt_ct_tg_init(void)
{
- return xt_register_target(&xt_ct_tg);
+ return xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
}
static void __exit xt_ct_tg_exit(void)
{
- xt_unregister_target(&xt_ct_tg);
+ xt_unregister_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
}
module_init(xt_ct_tg_init);
diff --git a/net/netfilter/xt_LOG.c b/net/netfilter/xt_LOG.c
new file mode 100644
index 00000000000..f99f8dee238
--- /dev/null
+++ b/net/netfilter/xt_LOG.c
@@ -0,0 +1,925 @@
+/*
+ * This is a module which is used for logging packets.
+ */
+
+/* (C) 1999-2001 Paul `Rusty' Russell
+ * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ip.h>
+#include <net/ipv6.h>
+#include <net/icmp.h>
+#include <net/udp.h>
+#include <net/tcp.h>
+#include <net/route.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_LOG.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <net/netfilter/nf_log.h>
+#include <net/netfilter/xt_log.h>
+
+static struct nf_loginfo default_loginfo = {
+ .type = NF_LOG_TYPE_LOG,
+ .u = {
+ .log = {
+ .level = 5,
+ .logflags = NF_LOG_MASK,
+ },
+ },
+};
+
+static int dump_udp_header(struct sbuff *m, const struct sk_buff *skb,
+ u8 proto, int fragment, unsigned int offset)
+{
+ struct udphdr _udph;
+ const struct udphdr *uh;
+
+ if (proto == IPPROTO_UDP)
+ /* Max length: 10 "PROTO=UDP " */
+ sb_add(m, "PROTO=UDP ");
+ else /* Max length: 14 "PROTO=UDPLITE " */
+ sb_add(m, "PROTO=UDPLITE ");
+
+ if (fragment)
+ goto out;
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
+ if (uh == NULL) {
+ sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset);
+
+ return 1;
+ }
+
+ /* Max length: 20 "SPT=65535 DPT=65535 " */
+ sb_add(m, "SPT=%u DPT=%u LEN=%u ", ntohs(uh->source), ntohs(uh->dest),
+ ntohs(uh->len));
+
+out:
+ return 0;
+}
+
+static int dump_tcp_header(struct sbuff *m, const struct sk_buff *skb,
+ u8 proto, int fragment, unsigned int offset,
+ unsigned int logflags)
+{
+ struct tcphdr _tcph;
+ const struct tcphdr *th;
+
+ /* Max length: 10 "PROTO=TCP " */
+ sb_add(m, "PROTO=TCP ");
+
+ if (fragment)
+ return 0;
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
+ if (th == NULL) {
+ sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset);
+ return 1;
+ }
+
+ /* Max length: 20 "SPT=65535 DPT=65535 " */
+ sb_add(m, "SPT=%u DPT=%u ", ntohs(th->source), ntohs(th->dest));
+ /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */
+ if (logflags & XT_LOG_TCPSEQ)
+ sb_add(m, "SEQ=%u ACK=%u ", ntohl(th->seq), ntohl(th->ack_seq));
+
+ /* Max length: 13 "WINDOW=65535 " */
+ sb_add(m, "WINDOW=%u ", ntohs(th->window));
+ /* Max length: 9 "RES=0x3C " */
+ sb_add(m, "RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(th) &
+ TCP_RESERVED_BITS) >> 22));
+ /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */
+ if (th->cwr)
+ sb_add(m, "CWR ");
+ if (th->ece)
+ sb_add(m, "ECE ");
+ if (th->urg)
+ sb_add(m, "URG ");
+ if (th->ack)
+ sb_add(m, "ACK ");
+ if (th->psh)
+ sb_add(m, "PSH ");
+ if (th->rst)
+ sb_add(m, "RST ");
+ if (th->syn)
+ sb_add(m, "SYN ");
+ if (th->fin)
+ sb_add(m, "FIN ");
+ /* Max length: 11 "URGP=65535 " */
+ sb_add(m, "URGP=%u ", ntohs(th->urg_ptr));
+
+ if ((logflags & XT_LOG_TCPOPT) && th->doff*4 > sizeof(struct tcphdr)) {
+ u_int8_t _opt[60 - sizeof(struct tcphdr)];
+ const u_int8_t *op;
+ unsigned int i;
+ unsigned int optsize = th->doff*4 - sizeof(struct tcphdr);
+
+ op = skb_header_pointer(skb, offset + sizeof(struct tcphdr),
+ optsize, _opt);
+ if (op == NULL) {
+ sb_add(m, "OPT (TRUNCATED)");
+ return 1;
+ }
+
+ /* Max length: 127 "OPT (" 15*4*2chars ") " */
+ sb_add(m, "OPT (");
+ for (i = 0; i < optsize; i++)
+ sb_add(m, "%02X", op[i]);
+
+ sb_add(m, ") ");
+ }
+
+ return 0;
+}
+
+/* One level of recursion won't kill us */
+static void dump_ipv4_packet(struct sbuff *m,
+ const struct nf_loginfo *info,
+ const struct sk_buff *skb,
+ unsigned int iphoff)
+{
+ struct iphdr _iph;
+ const struct iphdr *ih;
+ unsigned int logflags;
+
+ if (info->type == NF_LOG_TYPE_LOG)
+ logflags = info->u.log.logflags;
+ else
+ logflags = NF_LOG_MASK;
+
+ ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph);
+ if (ih == NULL) {
+ sb_add(m, "TRUNCATED");
+ return;
+ }
+
+ /* Important fields:
+ * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */
+ /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */
+ sb_add(m, "SRC=%pI4 DST=%pI4 ",
+ &ih->saddr, &ih->daddr);
+
+ /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */
+ sb_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
+ ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK,
+ ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id));
+
+ /* Max length: 6 "CE DF MF " */
+ if (ntohs(ih->frag_off) & IP_CE)
+ sb_add(m, "CE ");
+ if (ntohs(ih->frag_off) & IP_DF)
+ sb_add(m, "DF ");
+ if (ntohs(ih->frag_off) & IP_MF)
+ sb_add(m, "MF ");
+
+ /* Max length: 11 "FRAG:65535 " */
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ sb_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET);
+
+ if ((logflags & XT_LOG_IPOPT) &&
+ ih->ihl * 4 > sizeof(struct iphdr)) {
+ const unsigned char *op;
+ unsigned char _opt[4 * 15 - sizeof(struct iphdr)];
+ unsigned int i, optsize;
+
+ optsize = ih->ihl * 4 - sizeof(struct iphdr);
+ op = skb_header_pointer(skb, iphoff+sizeof(_iph),
+ optsize, _opt);
+ if (op == NULL) {
+ sb_add(m, "TRUNCATED");
+ return;
+ }
+
+ /* Max length: 127 "OPT (" 15*4*2chars ") " */
+ sb_add(m, "OPT (");
+ for (i = 0; i < optsize; i++)
+ sb_add(m, "%02X", op[i]);
+ sb_add(m, ") ");
+ }
+
+ switch (ih->protocol) {
+ case IPPROTO_TCP:
+ if (dump_tcp_header(m, skb, ih->protocol,
+ ntohs(ih->frag_off) & IP_OFFSET,
+ iphoff+ih->ihl*4, logflags))
+ return;
+ break;
+ case IPPROTO_UDP:
+ case IPPROTO_UDPLITE:
+ if (dump_udp_header(m, skb, ih->protocol,
+ ntohs(ih->frag_off) & IP_OFFSET,
+ iphoff+ih->ihl*4))
+ return;
+ break;
+ case IPPROTO_ICMP: {
+ struct icmphdr _icmph;
+ const struct icmphdr *ich;
+ static const size_t required_len[NR_ICMP_TYPES+1]
+ = { [ICMP_ECHOREPLY] = 4,
+ [ICMP_DEST_UNREACH]
+ = 8 + sizeof(struct iphdr),
+ [ICMP_SOURCE_QUENCH]
+ = 8 + sizeof(struct iphdr),
+ [ICMP_REDIRECT]
+ = 8 + sizeof(struct iphdr),
+ [ICMP_ECHO] = 4,
+ [ICMP_TIME_EXCEEDED]
+ = 8 + sizeof(struct iphdr),
+ [ICMP_PARAMETERPROB]
+ = 8 + sizeof(struct iphdr),
+ [ICMP_TIMESTAMP] = 20,
+ [ICMP_TIMESTAMPREPLY] = 20,
+ [ICMP_ADDRESS] = 12,
+ [ICMP_ADDRESSREPLY] = 12 };
+
+ /* Max length: 11 "PROTO=ICMP " */
+ sb_add(m, "PROTO=ICMP ");
+
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ break;
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ ich = skb_header_pointer(skb, iphoff + ih->ihl * 4,
+ sizeof(_icmph), &_icmph);
+ if (ich == NULL) {
+ sb_add(m, "INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - ih->ihl*4);
+ break;
+ }
+
+ /* Max length: 18 "TYPE=255 CODE=255 " */
+ sb_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code);
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ if (ich->type <= NR_ICMP_TYPES &&
+ required_len[ich->type] &&
+ skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) {
+ sb_add(m, "INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - ih->ihl*4);
+ break;
+ }
+
+ switch (ich->type) {
+ case ICMP_ECHOREPLY:
+ case ICMP_ECHO:
+ /* Max length: 19 "ID=65535 SEQ=65535 " */
+ sb_add(m, "ID=%u SEQ=%u ",
+ ntohs(ich->un.echo.id),
+ ntohs(ich->un.echo.sequence));
+ break;
+
+ case ICMP_PARAMETERPROB:
+ /* Max length: 14 "PARAMETER=255 " */
+ sb_add(m, "PARAMETER=%u ",
+ ntohl(ich->un.gateway) >> 24);
+ break;
+ case ICMP_REDIRECT:
+ /* Max length: 24 "GATEWAY=255.255.255.255 " */
+ sb_add(m, "GATEWAY=%pI4 ", &ich->un.gateway);
+ /* Fall through */
+ case ICMP_DEST_UNREACH:
+ case ICMP_SOURCE_QUENCH:
+ case ICMP_TIME_EXCEEDED:
+ /* Max length: 3+maxlen */
+ if (!iphoff) { /* Only recurse once. */
+ sb_add(m, "[");
+ dump_ipv4_packet(m, info, skb,
+ iphoff + ih->ihl*4+sizeof(_icmph));
+ sb_add(m, "] ");
+ }
+
+ /* Max length: 10 "MTU=65535 " */
+ if (ich->type == ICMP_DEST_UNREACH &&
+ ich->code == ICMP_FRAG_NEEDED)
+ sb_add(m, "MTU=%u ", ntohs(ich->un.frag.mtu));
+ }
+ break;
+ }
+ /* Max Length */
+ case IPPROTO_AH: {
+ struct ip_auth_hdr _ahdr;
+ const struct ip_auth_hdr *ah;
+
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ break;
+
+ /* Max length: 9 "PROTO=AH " */
+ sb_add(m, "PROTO=AH ");
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ ah = skb_header_pointer(skb, iphoff+ih->ihl*4,
+ sizeof(_ahdr), &_ahdr);
+ if (ah == NULL) {
+ sb_add(m, "INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - ih->ihl*4);
+ break;
+ }
+
+ /* Length: 15 "SPI=0xF1234567 " */
+ sb_add(m, "SPI=0x%x ", ntohl(ah->spi));
+ break;
+ }
+ case IPPROTO_ESP: {
+ struct ip_esp_hdr _esph;
+ const struct ip_esp_hdr *eh;
+
+ /* Max length: 10 "PROTO=ESP " */
+ sb_add(m, "PROTO=ESP ");
+
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ break;
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ eh = skb_header_pointer(skb, iphoff+ih->ihl*4,
+ sizeof(_esph), &_esph);
+ if (eh == NULL) {
+ sb_add(m, "INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - ih->ihl*4);
+ break;
+ }
+
+ /* Length: 15 "SPI=0xF1234567 " */
+ sb_add(m, "SPI=0x%x ", ntohl(eh->spi));
+ break;
+ }
+ /* Max length: 10 "PROTO 255 " */
+ default:
+ sb_add(m, "PROTO=%u ", ih->protocol);
+ }
+
+ /* Max length: 15 "UID=4294967295 " */
+ if ((logflags & XT_LOG_UID) && !iphoff && skb->sk) {
+ read_lock_bh(&skb->sk->sk_callback_lock);
+ if (skb->sk->sk_socket && skb->sk->sk_socket->file)
+ sb_add(m, "UID=%u GID=%u ",
+ skb->sk->sk_socket->file->f_cred->fsuid,
+ skb->sk->sk_socket->file->f_cred->fsgid);
+ read_unlock_bh(&skb->sk->sk_callback_lock);
+ }
+
+ /* Max length: 16 "MARK=0xFFFFFFFF " */
+ if (!iphoff && skb->mark)
+ sb_add(m, "MARK=0x%x ", skb->mark);
+
+ /* Proto Max log string length */
+ /* IP: 40+46+6+11+127 = 230 */
+ /* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */
+ /* UDP: 10+max(25,20) = 35 */
+ /* UDPLITE: 14+max(25,20) = 39 */
+ /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */
+ /* ESP: 10+max(25)+15 = 50 */
+ /* AH: 9+max(25)+15 = 49 */
+ /* unknown: 10 */
+
+ /* (ICMP allows recursion one level deep) */
+ /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */
+ /* maxlen = 230+ 91 + 230 + 252 = 803 */
+}
+
+static void dump_ipv4_mac_header(struct sbuff *m,
+ const struct nf_loginfo *info,
+ const struct sk_buff *skb)
+{
+ struct net_device *dev = skb->dev;
+ unsigned int logflags = 0;
+
+ if (info->type == NF_LOG_TYPE_LOG)
+ logflags = info->u.log.logflags;
+
+ if (!(logflags & XT_LOG_MACDECODE))
+ goto fallback;
+
+ switch (dev->type) {
+ case ARPHRD_ETHER:
+ sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ",
+ eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
+ ntohs(eth_hdr(skb)->h_proto));
+ return;
+ default:
+ break;
+ }
+
+fallback:
+ sb_add(m, "MAC=");
+ if (dev->hard_header_len &&
+ skb->mac_header != skb->network_header) {
+ const unsigned char *p = skb_mac_header(skb);
+ unsigned int i;
+
+ sb_add(m, "%02x", *p++);
+ for (i = 1; i < dev->hard_header_len; i++, p++)
+ sb_add(m, ":%02x", *p);
+ }
+ sb_add(m, " ");
+}
+
+static void
+log_packet_common(struct sbuff *m,
+ u_int8_t pf,
+ unsigned int hooknum,
+ const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const struct nf_loginfo *loginfo,
+ const char *prefix)
+{
+ sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level,
+ prefix,
+ in ? in->name : "",
+ out ? out->name : "");
+#ifdef CONFIG_BRIDGE_NETFILTER
+ if (skb->nf_bridge) {
+ const struct net_device *physindev;
+ const struct net_device *physoutdev;
+
+ physindev = skb->nf_bridge->physindev;
+ if (physindev && in != physindev)
+ sb_add(m, "PHYSIN=%s ", physindev->name);
+ physoutdev = skb->nf_bridge->physoutdev;
+ if (physoutdev && out != physoutdev)
+ sb_add(m, "PHYSOUT=%s ", physoutdev->name);
+ }
+#endif
+}
+
+
+static void
+ipt_log_packet(u_int8_t pf,
+ unsigned int hooknum,
+ const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const struct nf_loginfo *loginfo,
+ const char *prefix)
+{
+ struct sbuff *m = sb_open();
+
+ if (!loginfo)
+ loginfo = &default_loginfo;
+
+ log_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix);
+
+ if (in != NULL)
+ dump_ipv4_mac_header(m, loginfo, skb);
+
+ dump_ipv4_packet(m, loginfo, skb, 0);
+
+ sb_close(m);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+/* One level of recursion won't kill us */
+static void dump_ipv6_packet(struct sbuff *m,
+ const struct nf_loginfo *info,
+ const struct sk_buff *skb, unsigned int ip6hoff,
+ int recurse)
+{
+ u_int8_t currenthdr;
+ int fragment;
+ struct ipv6hdr _ip6h;
+ const struct ipv6hdr *ih;
+ unsigned int ptr;
+ unsigned int hdrlen = 0;
+ unsigned int logflags;
+
+ if (info->type == NF_LOG_TYPE_LOG)
+ logflags = info->u.log.logflags;
+ else
+ logflags = NF_LOG_MASK;
+
+ ih = skb_header_pointer(skb, ip6hoff, sizeof(_ip6h), &_ip6h);
+ if (ih == NULL) {
+ sb_add(m, "TRUNCATED");
+ return;
+ }
+
+ /* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000 " */
+ sb_add(m, "SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr);
+
+ /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */
+ sb_add(m, "LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ",
+ ntohs(ih->payload_len) + sizeof(struct ipv6hdr),
+ (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20,
+ ih->hop_limit,
+ (ntohl(*(__be32 *)ih) & 0x000fffff));
+
+ fragment = 0;
+ ptr = ip6hoff + sizeof(struct ipv6hdr);
+ currenthdr = ih->nexthdr;
+ while (currenthdr != NEXTHDR_NONE && ip6t_ext_hdr(currenthdr)) {
+ struct ipv6_opt_hdr _hdr;
+ const struct ipv6_opt_hdr *hp;
+
+ hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
+ if (hp == NULL) {
+ sb_add(m, "TRUNCATED");
+ return;
+ }
+
+ /* Max length: 48 "OPT (...) " */
+ if (logflags & XT_LOG_IPOPT)
+ sb_add(m, "OPT ( ");
+
+ switch (currenthdr) {
+ case IPPROTO_FRAGMENT: {
+ struct frag_hdr _fhdr;
+ const struct frag_hdr *fh;
+
+ sb_add(m, "FRAG:");
+ fh = skb_header_pointer(skb, ptr, sizeof(_fhdr),
+ &_fhdr);
+ if (fh == NULL) {
+ sb_add(m, "TRUNCATED ");
+ return;
+ }
+
+ /* Max length: 6 "65535 " */
+ sb_add(m, "%u ", ntohs(fh->frag_off) & 0xFFF8);
+
+ /* Max length: 11 "INCOMPLETE " */
+ if (fh->frag_off & htons(0x0001))
+ sb_add(m, "INCOMPLETE ");
+
+ sb_add(m, "ID:%08x ", ntohl(fh->identification));
+
+ if (ntohs(fh->frag_off) & 0xFFF8)
+ fragment = 1;
+
+ hdrlen = 8;
+
+ break;
+ }
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_HOPOPTS:
+ if (fragment) {
+ if (logflags & XT_LOG_IPOPT)
+ sb_add(m, ")");
+ return;
+ }
+ hdrlen = ipv6_optlen(hp);
+ break;
+ /* Max Length */
+ case IPPROTO_AH:
+ if (logflags & XT_LOG_IPOPT) {
+ struct ip_auth_hdr _ahdr;
+ const struct ip_auth_hdr *ah;
+
+ /* Max length: 3 "AH " */
+ sb_add(m, "AH ");
+
+ if (fragment) {
+ sb_add(m, ")");
+ return;
+ }
+
+ ah = skb_header_pointer(skb, ptr, sizeof(_ahdr),
+ &_ahdr);
+ if (ah == NULL) {
+ /*
+ * Max length: 26 "INCOMPLETE [65535
+ * bytes] )"
+ */
+ sb_add(m, "INCOMPLETE [%u bytes] )",
+ skb->len - ptr);
+ return;
+ }
+
+ /* Length: 15 "SPI=0xF1234567 */
+ sb_add(m, "SPI=0x%x ", ntohl(ah->spi));
+
+ }
+
+ hdrlen = (hp->hdrlen+2)<<2;
+ break;
+ case IPPROTO_ESP:
+ if (logflags & XT_LOG_IPOPT) {
+ struct ip_esp_hdr _esph;
+ const struct ip_esp_hdr *eh;
+
+ /* Max length: 4 "ESP " */
+ sb_add(m, "ESP ");
+
+ if (fragment) {
+ sb_add(m, ")");
+ return;
+ }
+
+ /*
+ * Max length: 26 "INCOMPLETE [65535 bytes] )"
+ */
+ eh = skb_header_pointer(skb, ptr, sizeof(_esph),
+ &_esph);
+ if (eh == NULL) {
+ sb_add(m, "INCOMPLETE [%u bytes] )",
+ skb->len - ptr);
+ return;
+ }
+
+ /* Length: 16 "SPI=0xF1234567 )" */
+ sb_add(m, "SPI=0x%x )", ntohl(eh->spi));
+
+ }
+ return;
+ default:
+ /* Max length: 20 "Unknown Ext Hdr 255" */
+ sb_add(m, "Unknown Ext Hdr %u", currenthdr);
+ return;
+ }
+ if (logflags & XT_LOG_IPOPT)
+ sb_add(m, ") ");
+
+ currenthdr = hp->nexthdr;
+ ptr += hdrlen;
+ }
+
+ switch (currenthdr) {
+ case IPPROTO_TCP:
+ if (dump_tcp_header(m, skb, currenthdr, fragment, ptr,
+ logflags))
+ return;
+ break;
+ case IPPROTO_UDP:
+ case IPPROTO_UDPLITE:
+ if (dump_udp_header(m, skb, currenthdr, fragment, ptr))
+ return;
+ break;
+ case IPPROTO_ICMPV6: {
+ struct icmp6hdr _icmp6h;
+ const struct icmp6hdr *ic;
+
+ /* Max length: 13 "PROTO=ICMPv6 " */
+ sb_add(m, "PROTO=ICMPv6 ");
+
+ if (fragment)
+ break;
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ ic = skb_header_pointer(skb, ptr, sizeof(_icmp6h), &_icmp6h);
+ if (ic == NULL) {
+ sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr);
+ return;
+ }
+
+ /* Max length: 18 "TYPE=255 CODE=255 " */
+ sb_add(m, "TYPE=%u CODE=%u ", ic->icmp6_type, ic->icmp6_code);
+
+ switch (ic->icmp6_type) {
+ case ICMPV6_ECHO_REQUEST:
+ case ICMPV6_ECHO_REPLY:
+ /* Max length: 19 "ID=65535 SEQ=65535 " */
+ sb_add(m, "ID=%u SEQ=%u ",
+ ntohs(ic->icmp6_identifier),
+ ntohs(ic->icmp6_sequence));
+ break;
+ case ICMPV6_MGM_QUERY:
+ case ICMPV6_MGM_REPORT:
+ case ICMPV6_MGM_REDUCTION:
+ break;
+
+ case ICMPV6_PARAMPROB:
+ /* Max length: 17 "POINTER=ffffffff " */
+ sb_add(m, "POINTER=%08x ", ntohl(ic->icmp6_pointer));
+ /* Fall through */
+ case ICMPV6_DEST_UNREACH:
+ case ICMPV6_PKT_TOOBIG:
+ case ICMPV6_TIME_EXCEED:
+ /* Max length: 3+maxlen */
+ if (recurse) {
+ sb_add(m, "[");
+ dump_ipv6_packet(m, info, skb,
+ ptr + sizeof(_icmp6h), 0);
+ sb_add(m, "] ");
+ }
+
+ /* Max length: 10 "MTU=65535 " */
+ if (ic->icmp6_type == ICMPV6_PKT_TOOBIG)
+ sb_add(m, "MTU=%u ", ntohl(ic->icmp6_mtu));
+ }
+ break;
+ }
+ /* Max length: 10 "PROTO=255 " */
+ default:
+ sb_add(m, "PROTO=%u ", currenthdr);
+ }
+
+ /* Max length: 15 "UID=4294967295 " */
+ if ((logflags & XT_LOG_UID) && recurse && skb->sk) {
+ read_lock_bh(&skb->sk->sk_callback_lock);
+ if (skb->sk->sk_socket && skb->sk->sk_socket->file)
+ sb_add(m, "UID=%u GID=%u ",
+ skb->sk->sk_socket->file->f_cred->fsuid,
+ skb->sk->sk_socket->file->f_cred->fsgid);
+ read_unlock_bh(&skb->sk->sk_callback_lock);
+ }
+
+ /* Max length: 16 "MARK=0xFFFFFFFF " */
+ if (!recurse && skb->mark)
+ sb_add(m, "MARK=0x%x ", skb->mark);
+}
+
+static void dump_ipv6_mac_header(struct sbuff *m,
+ const struct nf_loginfo *info,
+ const struct sk_buff *skb)
+{
+ struct net_device *dev = skb->dev;
+ unsigned int logflags = 0;
+
+ if (info->type == NF_LOG_TYPE_LOG)
+ logflags = info->u.log.logflags;
+
+ if (!(logflags & XT_LOG_MACDECODE))
+ goto fallback;
+
+ switch (dev->type) {
+ case ARPHRD_ETHER:
+ sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ",
+ eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
+ ntohs(eth_hdr(skb)->h_proto));
+ return;
+ default:
+ break;
+ }
+
+fallback:
+ sb_add(m, "MAC=");
+ if (dev->hard_header_len &&
+ skb->mac_header != skb->network_header) {
+ const unsigned char *p = skb_mac_header(skb);
+ unsigned int len = dev->hard_header_len;
+ unsigned int i;
+
+ if (dev->type == ARPHRD_SIT) {
+ p -= ETH_HLEN;
+
+ if (p < skb->head)
+ p = NULL;
+ }
+
+ if (p != NULL) {
+ sb_add(m, "%02x", *p++);
+ for (i = 1; i < len; i++)
+ sb_add(m, ":%02x", *p++);
+ }
+ sb_add(m, " ");
+
+ if (dev->type == ARPHRD_SIT) {
+ const struct iphdr *iph =
+ (struct iphdr *)skb_mac_header(skb);
+ sb_add(m, "TUNNEL=%pI4->%pI4 ", &iph->saddr,
+ &iph->daddr);
+ }
+ } else
+ sb_add(m, " ");
+}
+
+static void
+ip6t_log_packet(u_int8_t pf,
+ unsigned int hooknum,
+ const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const struct nf_loginfo *loginfo,
+ const char *prefix)
+{
+ struct sbuff *m = sb_open();
+
+ if (!loginfo)
+ loginfo = &default_loginfo;
+
+ log_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix);
+
+ if (in != NULL)
+ dump_ipv6_mac_header(m, loginfo, skb);
+
+ dump_ipv6_packet(m, loginfo, skb, skb_network_offset(skb), 1);
+
+ sb_close(m);
+}
+#endif
+
+static unsigned int
+log_tg(struct sk_buff *skb, const struct xt_action_param *par)
+{
+ const struct xt_log_info *loginfo = par->targinfo;
+ struct nf_loginfo li;
+
+ li.type = NF_LOG_TYPE_LOG;
+ li.u.log.level = loginfo->level;
+ li.u.log.logflags = loginfo->logflags;
+
+ if (par->family == NFPROTO_IPV4)
+ ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in,
+ par->out, &li, loginfo->prefix);
+#if IS_ENABLED(CONFIG_IPV6)
+ else if (par->family == NFPROTO_IPV6)
+ ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in,
+ par->out, &li, loginfo->prefix);
+#endif
+ else
+ WARN_ON_ONCE(1);
+
+ return XT_CONTINUE;
+}
+
+static int log_tg_check(const struct xt_tgchk_param *par)
+{
+ const struct xt_log_info *loginfo = par->targinfo;
+
+ if (par->family != NFPROTO_IPV4 && par->family != NFPROTO_IPV6)
+ return -EINVAL;
+
+ if (loginfo->level >= 8) {
+ pr_debug("level %u >= 8\n", loginfo->level);
+ return -EINVAL;
+ }
+
+ if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') {
+ pr_debug("prefix is not null-terminated\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct xt_target log_tg_regs[] __read_mostly = {
+ {
+ .name = "LOG",
+ .family = NFPROTO_IPV4,
+ .target = log_tg,
+ .targetsize = sizeof(struct xt_log_info),
+ .checkentry = log_tg_check,
+ .me = THIS_MODULE,
+ },
+#if IS_ENABLED(CONFIG_IPV6)
+ {
+ .name = "LOG",
+ .family = NFPROTO_IPV6,
+ .target = log_tg,
+ .targetsize = sizeof(struct xt_log_info),
+ .checkentry = log_tg_check,
+ .me = THIS_MODULE,
+ },
+#endif
+};
+
+static struct nf_logger ipt_log_logger __read_mostly = {
+ .name = "ipt_LOG",
+ .logfn = &ipt_log_packet,
+ .me = THIS_MODULE,
+};
+
+#if IS_ENABLED(CONFIG_IPV6)
+static struct nf_logger ip6t_log_logger __read_mostly = {
+ .name = "ip6t_LOG",
+ .logfn = &ip6t_log_packet,
+ .me = THIS_MODULE,
+};
+#endif
+
+static int __init log_tg_init(void)
+{
+ int ret;
+
+ ret = xt_register_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs));
+ if (ret < 0)
+ return ret;
+
+ nf_log_register(NFPROTO_IPV4, &ipt_log_logger);
+#if IS_ENABLED(CONFIG_IPV6)
+ nf_log_register(NFPROTO_IPV6, &ip6t_log_logger);
+#endif
+ return 0;
+}
+
+static void __exit log_tg_exit(void)
+{
+ nf_log_unregister(&ipt_log_logger);
+#if IS_ENABLED(CONFIG_IPV6)
+ nf_log_unregister(&ip6t_log_logger);
+#endif
+ xt_unregister_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs));
+}
+
+module_init(log_tg_init);
+module_exit(log_tg_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
+MODULE_AUTHOR("Jan Rekorajski <baggins@pld.org.pl>");
+MODULE_DESCRIPTION("Xtables: IPv4/IPv6 packet logging");
+MODULE_ALIAS("ipt_LOG");
+MODULE_ALIAS("ip6t_LOG");
diff --git a/net/netfilter/xt_TEE.c b/net/netfilter/xt_TEE.c
index 3aae66facf9..4d505790283 100644
--- a/net/netfilter/xt_TEE.c
+++ b/net/netfilter/xt_TEE.c
@@ -152,9 +152,10 @@ tee_tg_route6(struct sk_buff *skb, const struct xt_tee_tginfo *info)
fl6.flowlabel = ((iph->flow_lbl[0] & 0xF) << 16) |
(iph->flow_lbl[1] << 8) | iph->flow_lbl[2];
dst = ip6_route_output(net, NULL, &fl6);
- if (dst == NULL)
+ if (dst->error) {
+ dst_release(dst);
return false;
-
+ }
skb_dst_drop(skb);
skb_dst_set(skb, dst);
skb->dev = dst->dev;
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 629b06182f3..32bb75324e7 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1645,6 +1645,24 @@ static void netlink_destroy_callback(struct netlink_callback *cb)
kfree(cb);
}
+struct nlmsghdr *
+__nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags)
+{
+ struct nlmsghdr *nlh;
+ int size = NLMSG_LENGTH(len);
+
+ nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size));
+ nlh->nlmsg_type = type;
+ nlh->nlmsg_len = size;
+ nlh->nlmsg_flags = flags;
+ nlh->nlmsg_pid = pid;
+ nlh->nlmsg_seq = seq;
+ if (!__builtin_constant_p(size) || NLMSG_ALIGN(size) - size != 0)
+ memset(NLMSG_DATA(nlh) + len, 0, NLMSG_ALIGN(size) - size);
+ return nlh;
+}
+EXPORT_SYMBOL(__nlmsg_put);
+
/*
* It looks a bit ugly.
* It would be better to create kernel thread.
@@ -1718,10 +1736,7 @@ errout_skb:
int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
const struct nlmsghdr *nlh,
- int (*dump)(struct sk_buff *skb,
- struct netlink_callback *),
- int (*done)(struct netlink_callback *),
- u16 min_dump_alloc)
+ struct netlink_dump_control *control)
{
struct netlink_callback *cb;
struct sock *sk;
@@ -1732,10 +1747,11 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
if (cb == NULL)
return -ENOBUFS;
- cb->dump = dump;
- cb->done = done;
+ cb->dump = control->dump;
+ cb->done = control->done;
cb->nlh = nlh;
- cb->min_dump_alloc = min_dump_alloc;
+ cb->data = control->data;
+ cb->min_dump_alloc = control->min_dump_alloc;
atomic_inc(&skb->users);
cb->skb = skb;
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index c29d2568c9e..9f40441d7a7 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -498,6 +498,37 @@ int genl_unregister_family(struct genl_family *family)
}
EXPORT_SYMBOL(genl_unregister_family);
+/**
+ * genlmsg_put - Add generic netlink header to netlink message
+ * @skb: socket buffer holding the message
+ * @pid: netlink pid the message is addressed to
+ * @seq: sequence number (usually the one of the sender)
+ * @family: generic netlink family
+ * @flags netlink message flags
+ * @cmd: generic netlink command
+ *
+ * Returns pointer to user specific header
+ */
+void *genlmsg_put(struct sk_buff *skb, u32 pid, u32 seq,
+ struct genl_family *family, int flags, u8 cmd)
+{
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *hdr;
+
+ nlh = nlmsg_put(skb, pid, seq, family->id, GENL_HDRLEN +
+ family->hdrsize, flags);
+ if (nlh == NULL)
+ return NULL;
+
+ hdr = nlmsg_data(nlh);
+ hdr->cmd = cmd;
+ hdr->version = family->version;
+ hdr->reserved = 0;
+
+ return (char *) hdr + GENL_HDRLEN;
+}
+EXPORT_SYMBOL(genlmsg_put);
+
static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
struct genl_ops *ops;
@@ -532,8 +563,13 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
return -EOPNOTSUPP;
genl_unlock();
- err = netlink_dump_start(net->genl_sock, skb, nlh,
- ops->dumpit, ops->done, 0);
+ {
+ struct netlink_dump_control c = {
+ .dump = ops->dumpit,
+ .done = ops->done,
+ };
+ err = netlink_dump_start(net->genl_sock, skb, nlh, &c);
+ }
genl_lock();
return err;
}
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index 2725d1bdf29..48badffaafc 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
@@ -145,9 +145,16 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb,
*addr, new_addr, 1);
} else if (nh->protocol == IPPROTO_UDP) {
- if (likely(transport_len >= sizeof(struct udphdr)))
- inet_proto_csum_replace4(&udp_hdr(skb)->check, skb,
- *addr, new_addr, 1);
+ if (likely(transport_len >= sizeof(struct udphdr))) {
+ struct udphdr *uh = udp_hdr(skb);
+
+ if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+ inet_proto_csum_replace4(&uh->check, skb,
+ *addr, new_addr, 1);
+ if (!uh->check)
+ uh->check = CSUM_MANGLED_0;
+ }
+ }
}
csum_replace4(&nh->check, *addr, new_addr);
@@ -197,8 +204,22 @@ static void set_tp_port(struct sk_buff *skb, __be16 *port,
skb->rxhash = 0;
}
-static int set_udp_port(struct sk_buff *skb,
- const struct ovs_key_udp *udp_port_key)
+static void set_udp_port(struct sk_buff *skb, __be16 *port, __be16 new_port)
+{
+ struct udphdr *uh = udp_hdr(skb);
+
+ if (uh->check && skb->ip_summed != CHECKSUM_PARTIAL) {
+ set_tp_port(skb, port, new_port, &uh->check);
+
+ if (!uh->check)
+ uh->check = CSUM_MANGLED_0;
+ } else {
+ *port = new_port;
+ skb->rxhash = 0;
+ }
+}
+
+static int set_udp(struct sk_buff *skb, const struct ovs_key_udp *udp_port_key)
{
struct udphdr *uh;
int err;
@@ -210,16 +231,15 @@ static int set_udp_port(struct sk_buff *skb,
uh = udp_hdr(skb);
if (udp_port_key->udp_src != uh->source)
- set_tp_port(skb, &uh->source, udp_port_key->udp_src, &uh->check);
+ set_udp_port(skb, &uh->source, udp_port_key->udp_src);
if (udp_port_key->udp_dst != uh->dest)
- set_tp_port(skb, &uh->dest, udp_port_key->udp_dst, &uh->check);
+ set_udp_port(skb, &uh->dest, udp_port_key->udp_dst);
return 0;
}
-static int set_tcp_port(struct sk_buff *skb,
- const struct ovs_key_tcp *tcp_port_key)
+static int set_tcp(struct sk_buff *skb, const struct ovs_key_tcp *tcp_port_key)
{
struct tcphdr *th;
int err;
@@ -328,11 +348,11 @@ static int execute_set_action(struct sk_buff *skb,
break;
case OVS_KEY_ATTR_TCP:
- err = set_tcp_port(skb, nla_data(nested_attr));
+ err = set_tcp(skb, nla_data(nested_attr));
break;
case OVS_KEY_ATTR_UDP:
- err = set_udp_port(skb, nla_data(nested_attr));
+ err = set_udp(skb, nla_data(nested_attr));
break;
}
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index ce64c18b8c7..2c030505b33 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -1521,6 +1521,9 @@ static struct vport *lookup_vport(struct ovs_header *ovs_header,
vport = ovs_vport_locate(nla_data(a[OVS_VPORT_ATTR_NAME]));
if (!vport)
return ERR_PTR(-ENODEV);
+ if (ovs_header->dp_ifindex &&
+ ovs_header->dp_ifindex != get_dpifindex(vport->dp))
+ return ERR_PTR(-ENODEV);
return vport;
} else if (a[OVS_VPORT_ATTR_PORT_NO]) {
u32 port_no = nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]);
diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c
index 322b8d20669..b6b1d7daa3c 100644
--- a/net/openvswitch/vport-internal_dev.c
+++ b/net/openvswitch/vport-internal_dev.c
@@ -66,6 +66,7 @@ static int internal_dev_mac_addr(struct net_device *dev, void *p)
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
+ dev->addr_assign_type &= ~NET_ADDR_RANDOM;
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
return 0;
}
@@ -145,7 +146,7 @@ static void do_setup(struct net_device *netdev)
netdev->vlan_features = netdev->features;
netdev->features |= NETIF_F_HW_VLAN_TX;
netdev->hw_features = netdev->features & ~NETIF_F_LLTX;
- random_ether_addr(netdev->dev_addr);
+ eth_hw_addr_random(netdev);
}
static struct vport *internal_dev_create(const struct vport_parms *parms)
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 2dbb32b988c..ae2d484416d 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1459,6 +1459,7 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock,
struct net_device *dev;
__be16 proto = 0;
int err;
+ int extra_len = 0;
/*
* Get and verify the address.
@@ -1493,8 +1494,16 @@ retry:
* raw protocol and you must do your own fragmentation at this level.
*/
+ if (unlikely(sock_flag(sk, SOCK_NOFCS))) {
+ if (!netif_supports_nofcs(dev)) {
+ err = -EPROTONOSUPPORT;
+ goto out_unlock;
+ }
+ extra_len = 4; /* We're doing our own CRC */
+ }
+
err = -EMSGSIZE;
- if (len > dev->mtu + dev->hard_header_len + VLAN_HLEN)
+ if (len > dev->mtu + dev->hard_header_len + VLAN_HLEN + extra_len)
goto out_unlock;
if (!skb) {
@@ -1526,7 +1535,7 @@ retry:
goto retry;
}
- if (len > (dev->mtu + dev->hard_header_len)) {
+ if (len > (dev->mtu + dev->hard_header_len + extra_len)) {
/* Earlier code assumed this would be a VLAN pkt,
* double-check this now that we have the actual
* packet in hand.
@@ -1548,6 +1557,9 @@ retry:
if (err < 0)
goto out_unlock;
+ if (unlikely(extra_len == 4))
+ skb->no_fcs = 1;
+
dev_queue_xmit(skb);
rcu_read_unlock();
return len;
@@ -2209,6 +2221,7 @@ static int packet_snd(struct socket *sock,
struct packet_sock *po = pkt_sk(sk);
unsigned short gso_type = 0;
int hlen, tlen;
+ int extra_len = 0;
/*
* Get and verify the address.
@@ -2288,8 +2301,16 @@ static int packet_snd(struct socket *sock,
}
}
+ if (unlikely(sock_flag(sk, SOCK_NOFCS))) {
+ if (!netif_supports_nofcs(dev)) {
+ err = -EPROTONOSUPPORT;
+ goto out_unlock;
+ }
+ extra_len = 4; /* We're doing our own CRC */
+ }
+
err = -EMSGSIZE;
- if (!gso_type && (len > dev->mtu + reserve + VLAN_HLEN))
+ if (!gso_type && (len > dev->mtu + reserve + VLAN_HLEN + extra_len))
goto out_unlock;
err = -ENOBUFS;
@@ -2315,7 +2336,7 @@ static int packet_snd(struct socket *sock,
if (err < 0)
goto out_free;
- if (!gso_type && (len > dev->mtu + reserve)) {
+ if (!gso_type && (len > dev->mtu + reserve + extra_len)) {
/* Earlier code assumed this would be a VLAN pkt,
* double-check this now that we have the actual
* packet in hand.
@@ -2353,6 +2374,9 @@ static int packet_snd(struct socket *sock,
len += vnet_hdr_len;
}
+ if (unlikely(extra_len == 4))
+ skb->no_fcs = 1;
+
/*
* Now send it
*/
diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c
index 4cba13e46ff..ae3a035f539 100644
--- a/net/rxrpc/ar-key.c
+++ b/net/rxrpc/ar-key.c
@@ -232,7 +232,7 @@ static int rxrpc_krb5_decode_principal(struct krb5_principal *princ,
if (toklen <= (n_parts + 1) * 4)
return -EINVAL;
- princ->name_parts = kcalloc(sizeof(char *), n_parts, GFP_KERNEL);
+ princ->name_parts = kcalloc(n_parts, sizeof(char *), GFP_KERNEL);
if (!princ->name_parts)
return -ENOMEM;
@@ -355,7 +355,7 @@ static int rxrpc_krb5_decode_tagged_array(struct krb5_tagged_data **_td,
_debug("n_elem %d", n_elem);
- td = kcalloc(sizeof(struct krb5_tagged_data), n_elem,
+ td = kcalloc(n_elem, sizeof(struct krb5_tagged_data),
GFP_KERNEL);
if (!td)
return -ENOMEM;
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 2590e91b328..75b58f81d53 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -260,6 +260,32 @@ config NET_SCH_INGRESS
To compile this code as a module, choose M here: the
module will be called sch_ingress.
+config NET_SCH_PLUG
+ tristate "Plug network traffic until release (PLUG)"
+ ---help---
+
+ This queuing discipline allows userspace to plug/unplug a network
+ output queue, using the netlink interface. When it receives an
+ enqueue command it inserts a plug into the outbound queue that
+ causes following packets to enqueue until a dequeue command arrives
+ over netlink, causing the plug to be removed and resuming the normal
+ packet flow.
+
+ This module also provides a generic "network output buffering"
+ functionality (aka output commit), wherein upon arrival of a dequeue
+ command, only packets up to the first plug are released for delivery.
+ The Remus HA project uses this module to enable speculative execution
+ of virtual machines by allowing the generated network output to be rolled
+ back if needed.
+
+ For more information, please refer to http://wiki.xensource.com/xenwiki/Remus
+
+ Say Y here if you are using this kernel for Xen dom0 and
+ want to protect Xen guests with Remus.
+
+ To compile this code as a module, choose M here: the
+ module will be called sch_plug.
+
comment "Classification"
config NET_CLS
diff --git a/net/sched/Makefile b/net/sched/Makefile
index dc5889c0a15..8cdf4e2b51d 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_NET_SCH_MULTIQ) += sch_multiq.o
obj-$(CONFIG_NET_SCH_ATM) += sch_atm.o
obj-$(CONFIG_NET_SCH_NETEM) += sch_netem.o
obj-$(CONFIG_NET_SCH_DRR) += sch_drr.o
+obj-$(CONFIG_NET_SCH_PLUG) += sch_plug.o
obj-$(CONFIG_NET_SCH_MQPRIO) += sch_mqprio.o
obj-$(CONFIG_NET_SCH_CHOKE) += sch_choke.o
obj-$(CONFIG_NET_SCH_QFQ) += sch_qfq.o
diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c
index e465064d39a..7e267d7b9c7 100644
--- a/net/sched/sch_choke.c
+++ b/net/sched/sch_choke.c
@@ -148,8 +148,7 @@ struct choke_skb_cb {
static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb)
{
- BUILD_BUG_ON(sizeof(skb->cb) <
- sizeof(struct qdisc_skb_cb) + sizeof(struct choke_skb_cb));
+ qdisc_cb_private_validate(skb, sizeof(struct choke_skb_cb));
return (struct choke_skb_cb *)qdisc_skb_cb(skb)->data;
}
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 2776012132e..5da548fa7ae 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -130,8 +130,7 @@ struct netem_skb_cb {
static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb)
{
- BUILD_BUG_ON(sizeof(skb->cb) <
- sizeof(struct qdisc_skb_cb) + sizeof(struct netem_skb_cb));
+ qdisc_cb_private_validate(skb, sizeof(struct netem_skb_cb));
return (struct netem_skb_cb *)qdisc_skb_cb(skb)->data;
}
@@ -502,9 +501,8 @@ tfifo_dequeue:
/* if more time remaining? */
if (cb->time_to_send <= psched_get_time()) {
- skb = qdisc_dequeue_tail(sch);
- if (unlikely(!skb))
- goto qdisc_dequeue;
+ __skb_unlink(skb, &sch->q);
+ sch->qstats.backlog -= qdisc_pkt_len(skb);
#ifdef CONFIG_NET_CLS_ACT
/*
@@ -540,7 +538,6 @@ deliver:
qdisc_watchdog_schedule(&q->watchdog, cb->time_to_send);
}
-qdisc_dequeue:
if (q->qdisc) {
skb = q->qdisc->ops->dequeue(q->qdisc);
if (skb)
diff --git a/net/sched/sch_plug.c b/net/sched/sch_plug.c
new file mode 100644
index 00000000000..89f8fcf73f1
--- /dev/null
+++ b/net/sched/sch_plug.c
@@ -0,0 +1,233 @@
+/*
+ * sch_plug.c Queue traffic until an explicit release command
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * There are two ways to use this qdisc:
+ * 1. A simple "instantaneous" plug/unplug operation, by issuing an alternating
+ * sequence of TCQ_PLUG_BUFFER & TCQ_PLUG_RELEASE_INDEFINITE commands.
+ *
+ * 2. For network output buffering (a.k.a output commit) functionality.
+ * Output commit property is commonly used by applications using checkpoint
+ * based fault-tolerance to ensure that the checkpoint from which a system
+ * is being restored is consistent w.r.t outside world.
+ *
+ * Consider for e.g. Remus - a Virtual Machine checkpointing system,
+ * wherein a VM is checkpointed, say every 50ms. The checkpoint is replicated
+ * asynchronously to the backup host, while the VM continues executing the
+ * next epoch speculatively.
+ *
+ * The following is a typical sequence of output buffer operations:
+ * 1.At epoch i, start_buffer(i)
+ * 2. At end of epoch i (i.e. after 50ms):
+ * 2.1 Stop VM and take checkpoint(i).
+ * 2.2 start_buffer(i+1) and Resume VM
+ * 3. While speculatively executing epoch(i+1), asynchronously replicate
+ * checkpoint(i) to backup host.
+ * 4. When checkpoint_ack(i) is received from backup, release_buffer(i)
+ * Thus, this Qdisc would receive the following sequence of commands:
+ * TCQ_PLUG_BUFFER (epoch i)
+ * .. TCQ_PLUG_BUFFER (epoch i+1)
+ * ....TCQ_PLUG_RELEASE_ONE (epoch i)
+ * ......TCQ_PLUG_BUFFER (epoch i+2)
+ * ........
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/pkt_sched.h>
+
+/*
+ * State of the queue, when used for network output buffering:
+ *
+ * plug(i+1) plug(i) head
+ * ------------------+--------------------+---------------->
+ * | |
+ * | |
+ * pkts_current_epoch| pkts_last_epoch |pkts_to_release
+ * ----------------->|<--------+--------->|+--------------->
+ * v v
+ *
+ */
+
+struct plug_sched_data {
+ /* If true, the dequeue function releases all packets
+ * from head to end of the queue. The queue turns into
+ * a pass-through queue for newly arriving packets.
+ */
+ bool unplug_indefinite;
+
+ /* Queue Limit in bytes */
+ u32 limit;
+
+ /* Number of packets (output) from the current speculatively
+ * executing epoch.
+ */
+ u32 pkts_current_epoch;
+
+ /* Number of packets corresponding to the recently finished
+ * epoch. These will be released when we receive a
+ * TCQ_PLUG_RELEASE_ONE command. This command is typically
+ * issued after committing a checkpoint at the target.
+ */
+ u32 pkts_last_epoch;
+
+ /*
+ * Number of packets from the head of the queue, that can
+ * be released (committed checkpoint).
+ */
+ u32 pkts_to_release;
+};
+
+static int plug_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+{
+ struct plug_sched_data *q = qdisc_priv(sch);
+
+ if (likely(sch->qstats.backlog + skb->len <= q->limit)) {
+ if (!q->unplug_indefinite)
+ q->pkts_current_epoch++;
+ return qdisc_enqueue_tail(skb, sch);
+ }
+
+ return qdisc_reshape_fail(skb, sch);
+}
+
+static struct sk_buff *plug_dequeue(struct Qdisc *sch)
+{
+ struct plug_sched_data *q = qdisc_priv(sch);
+
+ if (qdisc_is_throttled(sch))
+ return NULL;
+
+ if (!q->unplug_indefinite) {
+ if (!q->pkts_to_release) {
+ /* No more packets to dequeue. Block the queue
+ * and wait for the next release command.
+ */
+ qdisc_throttled(sch);
+ return NULL;
+ }
+ q->pkts_to_release--;
+ }
+
+ return qdisc_dequeue_head(sch);
+}
+
+static int plug_init(struct Qdisc *sch, struct nlattr *opt)
+{
+ struct plug_sched_data *q = qdisc_priv(sch);
+
+ q->pkts_current_epoch = 0;
+ q->pkts_last_epoch = 0;
+ q->pkts_to_release = 0;
+ q->unplug_indefinite = false;
+
+ if (opt == NULL) {
+ /* We will set a default limit of 100 pkts (~150kB)
+ * in case tx_queue_len is not available. The
+ * default value is completely arbitrary.
+ */
+ u32 pkt_limit = qdisc_dev(sch)->tx_queue_len ? : 100;
+ q->limit = pkt_limit * psched_mtu(qdisc_dev(sch));
+ } else {
+ struct tc_plug_qopt *ctl = nla_data(opt);
+
+ if (nla_len(opt) < sizeof(*ctl))
+ return -EINVAL;
+
+ q->limit = ctl->limit;
+ }
+
+ qdisc_throttled(sch);
+ return 0;
+}
+
+/* Receives 4 types of messages:
+ * TCQ_PLUG_BUFFER: Inset a plug into the queue and
+ * buffer any incoming packets
+ * TCQ_PLUG_RELEASE_ONE: Dequeue packets from queue head
+ * to beginning of the next plug.
+ * TCQ_PLUG_RELEASE_INDEFINITE: Dequeue all packets from queue.
+ * Stop buffering packets until the next TCQ_PLUG_BUFFER
+ * command is received (just act as a pass-thru queue).
+ * TCQ_PLUG_LIMIT: Increase/decrease queue size
+ */
+static int plug_change(struct Qdisc *sch, struct nlattr *opt)
+{
+ struct plug_sched_data *q = qdisc_priv(sch);
+ struct tc_plug_qopt *msg;
+
+ if (opt == NULL)
+ return -EINVAL;
+
+ msg = nla_data(opt);
+ if (nla_len(opt) < sizeof(*msg))
+ return -EINVAL;
+
+ switch (msg->action) {
+ case TCQ_PLUG_BUFFER:
+ /* Save size of the current buffer */
+ q->pkts_last_epoch = q->pkts_current_epoch;
+ q->pkts_current_epoch = 0;
+ if (q->unplug_indefinite)
+ qdisc_throttled(sch);
+ q->unplug_indefinite = false;
+ break;
+ case TCQ_PLUG_RELEASE_ONE:
+ /* Add packets from the last complete buffer to the
+ * packets to be released set.
+ */
+ q->pkts_to_release += q->pkts_last_epoch;
+ q->pkts_last_epoch = 0;
+ qdisc_unthrottled(sch);
+ netif_schedule_queue(sch->dev_queue);
+ break;
+ case TCQ_PLUG_RELEASE_INDEFINITE:
+ q->unplug_indefinite = true;
+ q->pkts_to_release = 0;
+ q->pkts_last_epoch = 0;
+ q->pkts_current_epoch = 0;
+ qdisc_unthrottled(sch);
+ netif_schedule_queue(sch->dev_queue);
+ break;
+ case TCQ_PLUG_LIMIT:
+ /* Limit is supplied in bytes */
+ q->limit = msg->limit;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct Qdisc_ops plug_qdisc_ops __read_mostly = {
+ .id = "plug",
+ .priv_size = sizeof(struct plug_sched_data),
+ .enqueue = plug_enqueue,
+ .dequeue = plug_dequeue,
+ .peek = qdisc_peek_head,
+ .init = plug_init,
+ .change = plug_change,
+ .owner = THIS_MODULE,
+};
+
+static int __init plug_module_init(void)
+{
+ return register_qdisc(&plug_qdisc_ops);
+}
+
+static void __exit plug_module_exit(void)
+{
+ unregister_qdisc(&plug_qdisc_ops);
+}
+module_init(plug_module_init)
+module_exit(plug_module_exit)
+MODULE_LICENSE("GPL");
diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c
index 96e42cae4c7..d7eea99333e 100644
--- a/net/sched/sch_sfb.c
+++ b/net/sched/sch_sfb.c
@@ -94,8 +94,7 @@ struct sfb_skb_cb {
static inline struct sfb_skb_cb *sfb_skb_cb(const struct sk_buff *skb)
{
- BUILD_BUG_ON(sizeof(skb->cb) <
- sizeof(struct qdisc_skb_cb) + sizeof(struct sfb_skb_cb));
+ qdisc_cb_private_validate(skb, sizeof(struct sfb_skb_cb));
return (struct sfb_skb_cb *)qdisc_skb_cb(skb)->data;
}
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index 67494aef9ac..60d47180f04 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -166,9 +166,8 @@ struct sfq_skb_cb {
static inline struct sfq_skb_cb *sfq_skb_cb(const struct sk_buff *skb)
{
- BUILD_BUG_ON(sizeof(skb->cb) <
- sizeof(struct qdisc_skb_cb) + sizeof(struct sfq_skb_cb));
- return (struct sfq_skb_cb *)qdisc_skb_cb(skb)->data;
+ qdisc_cb_private_validate(skb, sizeof(struct sfq_skb_cb));
+ return (struct sfq_skb_cb *)qdisc_skb_cb(skb)->data;
}
static unsigned int sfq_hash(const struct sfq_sched_data *q,
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 408ebd0e733..06b42b7f5a0 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -4170,14 +4170,16 @@ static int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optv
}
/* Helper routine to branch off an association to a new socket. */
-SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
- struct socket **sockp)
+int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp)
{
- struct sock *sk = asoc->base.sk;
+ struct sctp_association *asoc = sctp_id2assoc(sk, id);
struct socket *sock;
struct sctp_af *af;
int err = 0;
+ if (!asoc)
+ return -EINVAL;
+
/* An association cannot be branched off from an already peeled-off
* socket, nor is this supported for tcp style sockets.
*/
@@ -4206,13 +4208,13 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
return err;
}
+EXPORT_SYMBOL(sctp_do_peeloff);
static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval, int __user *optlen)
{
sctp_peeloff_arg_t peeloff;
struct socket *newsock;
int retval = 0;
- struct sctp_association *asoc;
if (len < sizeof(sctp_peeloff_arg_t))
return -EINVAL;
@@ -4220,15 +4222,7 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
if (copy_from_user(&peeloff, optval, len))
return -EFAULT;
- asoc = sctp_id2assoc(sk, peeloff.associd);
- if (!asoc) {
- retval = -EINVAL;
- goto out;
- }
-
- SCTP_DEBUG_PRINTK("%s: sk: %p asoc: %p\n", __func__, sk, asoc);
-
- retval = sctp_do_peeloff(asoc, &newsock);
+ retval = sctp_do_peeloff(sk, peeloff.associd, &newsock);
if (retval < 0)
goto out;
@@ -4239,8 +4233,8 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
goto out;
}
- SCTP_DEBUG_PRINTK("%s: sk: %p asoc: %p newsk: %p sd: %d\n",
- __func__, sk, asoc, newsock->sk, retval);
+ SCTP_DEBUG_PRINTK("%s: sk: %p newsk: %p sd: %d\n",
+ __func__, sk, newsock->sk, retval);
/* Return the fd mapped to the new socket. */
peeloff.sd = retval;
diff --git a/net/socket.c b/net/socket.c
index 28a96af484b..12a48d84622 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -181,7 +181,7 @@ static DEFINE_PER_CPU(int, sockets_in_use);
* invalid addresses -EFAULT is returned. On a success 0 is returned.
*/
-int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr)
+int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr)
{
if (ulen < 0 || ulen > sizeof(struct sockaddr_storage))
return -EINVAL;
@@ -209,7 +209,7 @@ int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr)
* specified. Zero is returned for a success.
*/
-static int move_addr_to_user(struct sockaddr *kaddr, int klen,
+static int move_addr_to_user(struct sockaddr_storage *kaddr, int klen,
void __user *uaddr, int __user *ulen)
{
int err;
@@ -1449,7 +1449,7 @@ SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
- err = move_addr_to_kernel(umyaddr, addrlen, (struct sockaddr *)&address);
+ err = move_addr_to_kernel(umyaddr, addrlen, &address);
if (err >= 0) {
err = security_socket_bind(sock,
(struct sockaddr *)&address,
@@ -1556,7 +1556,7 @@ SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
err = -ECONNABORTED;
goto out_fd;
}
- err = move_addr_to_user((struct sockaddr *)&address,
+ err = move_addr_to_user(&address,
len, upeer_sockaddr, upeer_addrlen);
if (err < 0)
goto out_fd;
@@ -1605,7 +1605,7 @@ SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
- err = move_addr_to_kernel(uservaddr, addrlen, (struct sockaddr *)&address);
+ err = move_addr_to_kernel(uservaddr, addrlen, &address);
if (err < 0)
goto out_put;
@@ -1645,7 +1645,7 @@ SYSCALL_DEFINE3(getsockname, int, fd, struct sockaddr __user *, usockaddr,
err = sock->ops->getname(sock, (struct sockaddr *)&address, &len, 0);
if (err)
goto out_put;
- err = move_addr_to_user((struct sockaddr *)&address, len, usockaddr, usockaddr_len);
+ err = move_addr_to_user(&address, len, usockaddr, usockaddr_len);
out_put:
fput_light(sock->file, fput_needed);
@@ -1677,7 +1677,7 @@ SYSCALL_DEFINE3(getpeername, int, fd, struct sockaddr __user *, usockaddr,
sock->ops->getname(sock, (struct sockaddr *)&address, &len,
1);
if (!err)
- err = move_addr_to_user((struct sockaddr *)&address, len, usockaddr,
+ err = move_addr_to_user(&address, len, usockaddr,
usockaddr_len);
fput_light(sock->file, fput_needed);
}
@@ -1716,7 +1716,7 @@ SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len,
msg.msg_controllen = 0;
msg.msg_namelen = 0;
if (addr) {
- err = move_addr_to_kernel(addr, addr_len, (struct sockaddr *)&address);
+ err = move_addr_to_kernel(addr, addr_len, &address);
if (err < 0)
goto out_put;
msg.msg_name = (struct sockaddr *)&address;
@@ -1779,7 +1779,7 @@ SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
err = sock_recvmsg(sock, &msg, size, flags);
if (err >= 0 && addr != NULL) {
- err2 = move_addr_to_user((struct sockaddr *)&address,
+ err2 = move_addr_to_user(&address,
msg.msg_namelen, addr, addr_len);
if (err2 < 0)
err = err2;
@@ -1933,13 +1933,9 @@ static int __sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
/* This will also move the address data into kernel space */
if (MSG_CMSG_COMPAT & flags) {
- err = verify_compat_iovec(msg_sys, iov,
- (struct sockaddr *)&address,
- VERIFY_READ);
+ err = verify_compat_iovec(msg_sys, iov, &address, VERIFY_READ);
} else
- err = verify_iovec(msg_sys, iov,
- (struct sockaddr *)&address,
- VERIFY_READ);
+ err = verify_iovec(msg_sys, iov, &address, VERIFY_READ);
if (err < 0)
goto out_freeiov;
total_len = err;
@@ -2143,13 +2139,9 @@ static int __sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
uaddr = (__force void __user *)msg_sys->msg_name;
uaddr_len = COMPAT_NAMELEN(msg);
if (MSG_CMSG_COMPAT & flags) {
- err = verify_compat_iovec(msg_sys, iov,
- (struct sockaddr *)&addr,
- VERIFY_WRITE);
+ err = verify_compat_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
} else
- err = verify_iovec(msg_sys, iov,
- (struct sockaddr *)&addr,
- VERIFY_WRITE);
+ err = verify_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
if (err < 0)
goto out_freeiov;
total_len = err;
@@ -2166,7 +2158,7 @@ static int __sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
len = err;
if (uaddr != NULL) {
- err = move_addr_to_user((struct sockaddr *)&addr,
+ err = move_addr_to_user(&addr,
msg_sys->msg_namelen, uaddr,
uaddr_len);
if (err < 0)
diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c
index 1426ec3d0a5..75762f34697 100644
--- a/net/sunrpc/auth_generic.c
+++ b/net/sunrpc/auth_generic.c
@@ -92,6 +92,7 @@ generic_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
if (gcred->acred.group_info != NULL)
get_group_info(gcred->acred.group_info);
gcred->acred.machine_cred = acred->machine_cred;
+ gcred->acred.principal = acred->principal;
dprintk("RPC: allocated %s cred %p for uid %d gid %d\n",
gcred->acred.machine_cred ? "machine" : "generic",
@@ -123,6 +124,17 @@ generic_destroy_cred(struct rpc_cred *cred)
call_rcu(&cred->cr_rcu, generic_free_cred_callback);
}
+static int
+machine_cred_match(struct auth_cred *acred, struct generic_cred *gcred, int flags)
+{
+ if (!gcred->acred.machine_cred ||
+ gcred->acred.principal != acred->principal ||
+ gcred->acred.uid != acred->uid ||
+ gcred->acred.gid != acred->gid)
+ return 0;
+ return 1;
+}
+
/*
* Match credentials against current process creds.
*/
@@ -132,9 +144,12 @@ generic_match(struct auth_cred *acred, struct rpc_cred *cred, int flags)
struct generic_cred *gcred = container_of(cred, struct generic_cred, gc_base);
int i;
+ if (acred->machine_cred)
+ return machine_cred_match(acred, gcred, flags);
+
if (gcred->acred.uid != acred->uid ||
gcred->acred.gid != acred->gid ||
- gcred->acred.machine_cred != acred->machine_cred)
+ gcred->acred.machine_cred != 0)
goto out_nomatch;
/* Optimisation in the case where pointers are identical... */
diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c
index 8eb87b11d10..e00441a2092 100644
--- a/net/tipc/bcast.c
+++ b/net/tipc/bcast.c
@@ -157,39 +157,14 @@ u32 tipc_bclink_get_last_sent(void)
return bcl->fsm_msg_cnt;
}
-/**
- * bclink_set_gap - set gap according to contents of current deferred pkt queue
- *
- * Called with 'node' locked, bc_lock unlocked
- */
-
-static void bclink_set_gap(struct tipc_node *n_ptr)
-{
- struct sk_buff *buf = n_ptr->bclink.deferred_head;
-
- n_ptr->bclink.gap_after = n_ptr->bclink.gap_to =
- mod(n_ptr->bclink.last_in);
- if (unlikely(buf != NULL))
- n_ptr->bclink.gap_to = mod(buf_seqno(buf) - 1);
-}
-
-/**
- * bclink_ack_allowed - test if ACK or NACK message can be sent at this moment
- *
- * This mechanism endeavours to prevent all nodes in network from trying
- * to ACK or NACK at the same time.
- *
- * Note: TIPC uses a different trigger to distribute ACKs than it does to
- * distribute NACKs, but tries to use the same spacing (divide by 16).
- */
-
-static int bclink_ack_allowed(u32 n)
+static void bclink_update_last_sent(struct tipc_node *node, u32 seqno)
{
- return (n % TIPC_MIN_LINK_WIN) == tipc_own_tag;
+ node->bclink.last_sent = less_eq(node->bclink.last_sent, seqno) ?
+ seqno : node->bclink.last_sent;
}
-/**
+/*
* tipc_bclink_retransmit_to - get most recent node to request retransmission
*
* Called with bc_lock locked
@@ -281,7 +256,7 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)
if (bcbuf_acks(crs) == 0) {
bcl->first_out = next;
bcl->out_queue_size--;
- buf_discard(crs);
+ kfree_skb(crs);
released = 1;
}
crs = next;
@@ -300,140 +275,94 @@ exit:
spin_unlock_bh(&bc_lock);
}
-/**
- * bclink_send_ack - unicast an ACK msg
+/*
+ * tipc_bclink_update_link_state - update broadcast link state
*
* tipc_net_lock and node lock set
*/
-static void bclink_send_ack(struct tipc_node *n_ptr)
+void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)
{
- struct tipc_link *l_ptr = n_ptr->active_links[n_ptr->addr & 1];
+ struct sk_buff *buf;
- if (l_ptr != NULL)
- tipc_link_send_proto_msg(l_ptr, STATE_MSG, 0, 0, 0, 0, 0);
-}
+ /* Ignore "stale" link state info */
-/**
- * bclink_send_nack- broadcast a NACK msg
- *
- * tipc_net_lock and node lock set
- */
+ if (less_eq(last_sent, n_ptr->bclink.last_in))
+ return;
-static void bclink_send_nack(struct tipc_node *n_ptr)
-{
- struct sk_buff *buf;
- struct tipc_msg *msg;
+ /* Update link synchronization state; quit if in sync */
+
+ bclink_update_last_sent(n_ptr, last_sent);
+
+ if (n_ptr->bclink.last_sent == n_ptr->bclink.last_in)
+ return;
+
+ /* Update out-of-sync state; quit if loss is still unconfirmed */
+
+ if ((++n_ptr->bclink.oos_state) == 1) {
+ if (n_ptr->bclink.deferred_size < (TIPC_MIN_LINK_WIN / 2))
+ return;
+ n_ptr->bclink.oos_state++;
+ }
- if (!less(n_ptr->bclink.gap_after, n_ptr->bclink.gap_to))
+ /* Don't NACK if one has been recently sent (or seen) */
+
+ if (n_ptr->bclink.oos_state & 0x1)
return;
+ /* Send NACK */
+
buf = tipc_buf_acquire(INT_H_SIZE);
if (buf) {
- msg = buf_msg(buf);
+ struct tipc_msg *msg = buf_msg(buf);
+
tipc_msg_init(msg, BCAST_PROTOCOL, STATE_MSG,
- INT_H_SIZE, n_ptr->addr);
+ INT_H_SIZE, n_ptr->addr);
msg_set_non_seq(msg, 1);
msg_set_mc_netid(msg, tipc_net_id);
- msg_set_bcast_ack(msg, mod(n_ptr->bclink.last_in));
- msg_set_bcgap_after(msg, n_ptr->bclink.gap_after);
- msg_set_bcgap_to(msg, n_ptr->bclink.gap_to);
- msg_set_bcast_tag(msg, tipc_own_tag);
+ msg_set_bcast_ack(msg, n_ptr->bclink.last_in);
+ msg_set_bcgap_after(msg, n_ptr->bclink.last_in);
+ msg_set_bcgap_to(msg, n_ptr->bclink.deferred_head
+ ? buf_seqno(n_ptr->bclink.deferred_head) - 1
+ : n_ptr->bclink.last_sent);
+ spin_lock_bh(&bc_lock);
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
- * until 16 more deferred messages arrive from it
- * (i.e. helps prevent all nodes from NACK'ing at same time)
- */
+ spin_unlock_bh(&bc_lock);
+ kfree_skb(buf);
- n_ptr->bclink.nack_sync = tipc_own_tag;
+ n_ptr->bclink.oos_state++;
}
}
-/**
- * tipc_bclink_check_gap - send a NACK if a sequence gap exists
+/*
+ * bclink_peek_nack - monitor retransmission requests sent by other nodes
*
- * tipc_net_lock and node lock set
- */
-
-void tipc_bclink_check_gap(struct tipc_node *n_ptr, u32 last_sent)
-{
- if (!n_ptr->bclink.supported ||
- less_eq(last_sent, mod(n_ptr->bclink.last_in)))
- return;
-
- bclink_set_gap(n_ptr);
- if (n_ptr->bclink.gap_after == n_ptr->bclink.gap_to)
- n_ptr->bclink.gap_to = last_sent;
- bclink_send_nack(n_ptr);
-}
-
-/**
- * tipc_bclink_peek_nack - process a NACK msg meant for another node
+ * Delay any upcoming NACK by this node if another node has already
+ * requested the first message this node is going to ask for.
*
* Only tipc_net_lock set.
*/
-static void tipc_bclink_peek_nack(u32 dest, u32 sender_tag, u32 gap_after, u32 gap_to)
+static void bclink_peek_nack(struct tipc_msg *msg)
{
- struct tipc_node *n_ptr = tipc_node_find(dest);
- u32 my_after, my_to;
+ struct tipc_node *n_ptr = tipc_node_find(msg_destnode(msg));
- if (unlikely(!n_ptr || !tipc_node_is_up(n_ptr)))
+ if (unlikely(!n_ptr))
return;
+
tipc_node_lock(n_ptr);
- /*
- * Modify gap to suppress unnecessary NACKs from this node
- */
- my_after = n_ptr->bclink.gap_after;
- my_to = n_ptr->bclink.gap_to;
-
- if (less_eq(gap_after, my_after)) {
- if (less(my_after, gap_to) && less(gap_to, my_to))
- n_ptr->bclink.gap_after = gap_to;
- else if (less_eq(my_to, gap_to))
- n_ptr->bclink.gap_to = n_ptr->bclink.gap_after;
- } else if (less_eq(gap_after, my_to)) {
- if (less_eq(my_to, gap_to))
- n_ptr->bclink.gap_to = gap_after;
- } else {
- /*
- * Expand gap if missing bufs not in deferred queue:
- */
- struct sk_buff *buf = n_ptr->bclink.deferred_head;
- u32 prev = n_ptr->bclink.gap_to;
- for (; buf; buf = buf->next) {
- u32 seqno = buf_seqno(buf);
+ if (n_ptr->bclink.supported &&
+ (n_ptr->bclink.last_in != n_ptr->bclink.last_sent) &&
+ (n_ptr->bclink.last_in == msg_bcgap_after(msg)))
+ n_ptr->bclink.oos_state = 2;
- if (mod(seqno - prev) != 1) {
- buf = NULL;
- break;
- }
- if (seqno == gap_after)
- break;
- prev = seqno;
- }
- if (buf == NULL)
- n_ptr->bclink.gap_to = gap_after;
- }
- /*
- * Some nodes may send a complementary NACK now:
- */
- if (bclink_ack_allowed(sender_tag + 1)) {
- if (n_ptr->bclink.gap_to != n_ptr->bclink.gap_after) {
- bclink_send_nack(n_ptr);
- bclink_set_gap(n_ptr);
- }
- }
tipc_node_unlock(n_ptr);
}
-/**
+/*
* tipc_bclink_send_msg - broadcast a packet to all nodes in cluster
*/
@@ -445,7 +374,7 @@ int tipc_bclink_send_msg(struct sk_buff *buf)
if (!bclink->bcast_nodes.count) {
res = msg_data_sz(buf_msg(buf));
- buf_discard(buf);
+ kfree_skb(buf);
goto exit;
}
@@ -460,7 +389,33 @@ exit:
return res;
}
-/**
+/*
+ * bclink_accept_pkt - accept an incoming, in-sequence broadcast packet
+ *
+ * Called with both sending node's lock and bc_lock taken.
+ */
+
+static void bclink_accept_pkt(struct tipc_node *node, u32 seqno)
+{
+ bclink_update_last_sent(node, seqno);
+ node->bclink.last_in = seqno;
+ node->bclink.oos_state = 0;
+ bcl->stats.recv_info++;
+
+ /*
+ * Unicast an ACK periodically, ensuring that
+ * all nodes in the cluster don't ACK at the same time
+ */
+
+ if (((seqno - tipc_own_addr) % TIPC_MIN_LINK_WIN) == 0) {
+ tipc_link_send_proto_msg(
+ node->active_links[node->addr & 1],
+ STATE_MSG, 0, 0, 0, 0, 0);
+ bcl->stats.sent_acks++;
+ }
+}
+
+/*
* tipc_bclink_recv_pkt - receive a broadcast packet, and deliver upwards
*
* tipc_net_lock is read_locked, no other locks set
@@ -472,7 +427,7 @@ void tipc_bclink_recv_pkt(struct sk_buff *buf)
struct tipc_node *node;
u32 next_in;
u32 seqno;
- struct sk_buff *deferred;
+ int deferred;
/* Screen out unwanted broadcast messages */
@@ -487,6 +442,8 @@ void tipc_bclink_recv_pkt(struct sk_buff *buf)
if (unlikely(!node->bclink.supported))
goto unlock;
+ /* Handle broadcast protocol message */
+
if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) {
if (msg_type(msg) != STATE_MSG)
goto unlock;
@@ -501,89 +458,118 @@ void tipc_bclink_recv_pkt(struct sk_buff *buf)
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));
+ bclink_peek_nack(msg);
}
goto exit;
}
/* Handle in-sequence broadcast message */
-receive:
- next_in = mod(node->bclink.last_in + 1);
seqno = msg_seqno(msg);
+ next_in = mod(node->bclink.last_in + 1);
if (likely(seqno == next_in)) {
- bcl->stats.recv_info++;
- node->bclink.last_in++;
- bclink_set_gap(node);
- if (unlikely(bclink_ack_allowed(seqno))) {
- bclink_send_ack(node);
- bcl->stats.sent_acks++;
- }
+receive:
+ /* Deliver message to destination */
+
if (likely(msg_isdata(msg))) {
+ spin_lock_bh(&bc_lock);
+ bclink_accept_pkt(node, seqno);
+ spin_unlock_bh(&bc_lock);
tipc_node_unlock(node);
if (likely(msg_mcast(msg)))
tipc_port_recv_mcast(buf, NULL);
else
- buf_discard(buf);
+ kfree_skb(buf);
} else if (msg_user(msg) == MSG_BUNDLER) {
+ spin_lock_bh(&bc_lock);
+ bclink_accept_pkt(node, seqno);
bcl->stats.recv_bundles++;
bcl->stats.recv_bundled += msg_msgcnt(msg);
+ spin_unlock_bh(&bc_lock);
tipc_node_unlock(node);
tipc_link_recv_bundle(buf);
} else if (msg_user(msg) == MSG_FRAGMENTER) {
+ int ret = tipc_link_recv_fragment(&node->bclink.defragm,
+ &buf, &msg);
+ if (ret < 0)
+ goto unlock;
+ spin_lock_bh(&bc_lock);
+ bclink_accept_pkt(node, seqno);
bcl->stats.recv_fragments++;
- if (tipc_link_recv_fragment(&node->bclink.defragm,
- &buf, &msg))
+ if (ret > 0)
bcl->stats.recv_fragmented++;
+ spin_unlock_bh(&bc_lock);
tipc_node_unlock(node);
tipc_net_route_msg(buf);
} else if (msg_user(msg) == NAME_DISTRIBUTOR) {
+ spin_lock_bh(&bc_lock);
+ bclink_accept_pkt(node, seqno);
+ spin_unlock_bh(&bc_lock);
tipc_node_unlock(node);
tipc_named_recv(buf);
} else {
+ spin_lock_bh(&bc_lock);
+ bclink_accept_pkt(node, seqno);
+ spin_unlock_bh(&bc_lock);
tipc_node_unlock(node);
- buf_discard(buf);
+ kfree_skb(buf);
}
buf = NULL;
+
+ /* Determine new synchronization state */
+
tipc_node_lock(node);
- deferred = node->bclink.deferred_head;
- if (deferred && (buf_seqno(deferred) == mod(next_in + 1))) {
- buf = deferred;
- msg = buf_msg(buf);
- node->bclink.deferred_head = deferred->next;
- goto receive;
- }
- } else if (less(next_in, seqno)) {
- u32 gap_after = node->bclink.gap_after;
- u32 gap_to = node->bclink.gap_to;
-
- if (tipc_link_defer_pkt(&node->bclink.deferred_head,
- &node->bclink.deferred_tail,
- buf)) {
- node->bclink.nack_sync++;
- bcl->stats.deferred_recv++;
- if (seqno == mod(gap_after + 1))
- node->bclink.gap_after = seqno;
- else if (less(gap_after, seqno) && less(seqno, gap_to))
- node->bclink.gap_to = seqno;
+ if (unlikely(!tipc_node_is_up(node)))
+ goto unlock;
+
+ if (node->bclink.last_in == node->bclink.last_sent)
+ goto unlock;
+
+ if (!node->bclink.deferred_head) {
+ node->bclink.oos_state = 1;
+ goto unlock;
}
+
+ msg = buf_msg(node->bclink.deferred_head);
+ seqno = msg_seqno(msg);
+ next_in = mod(next_in + 1);
+ if (seqno != next_in)
+ goto unlock;
+
+ /* Take in-sequence message from deferred queue & deliver it */
+
+ buf = node->bclink.deferred_head;
+ node->bclink.deferred_head = buf->next;
+ node->bclink.deferred_size--;
+ goto receive;
+ }
+
+ /* Handle out-of-sequence broadcast message */
+
+ if (less(next_in, seqno)) {
+ deferred = tipc_link_defer_pkt(&node->bclink.deferred_head,
+ &node->bclink.deferred_tail,
+ buf);
+ node->bclink.deferred_size += deferred;
+ bclink_update_last_sent(node, seqno);
buf = NULL;
- if (bclink_ack_allowed(node->bclink.nack_sync)) {
- if (gap_to != gap_after)
- bclink_send_nack(node);
- bclink_set_gap(node);
- }
- } else {
+ } else
+ deferred = 0;
+
+ spin_lock_bh(&bc_lock);
+
+ if (deferred)
+ bcl->stats.deferred_recv++;
+ else
bcl->stats.duplicates++;
- }
+
+ spin_unlock_bh(&bc_lock);
+
unlock:
tipc_node_unlock(node);
exit:
- buf_discard(buf);
+ kfree_skb(buf);
}
u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr)
diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h
index b009666c60b..5571394098f 100644
--- a/net/tipc/bcast.h
+++ b/net/tipc/bcast.h
@@ -96,7 +96,7 @@ int tipc_bclink_send_msg(struct sk_buff *buf);
void tipc_bclink_recv_pkt(struct sk_buff *buf);
u32 tipc_bclink_get_last_sent(void);
u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr);
-void tipc_bclink_check_gap(struct tipc_node *n_ptr, u32 seqno);
+void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent);
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);
diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 329fb659fae..5dfd89c4042 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -435,7 +435,7 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
u32 i;
int res = -EINVAL;
- if (tipc_mode != TIPC_NET_MODE) {
+ if (!tipc_own_addr) {
warn("Bearer <%s> rejected, not supported in standalone mode\n",
name);
return -ENOPROTOOPT;
@@ -456,8 +456,7 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
warn("Bearer <%s> rejected, illegal discovery domain\n", name);
return -EINVAL;
}
- if ((priority < TIPC_MIN_LINK_PRI ||
- priority > TIPC_MAX_LINK_PRI) &&
+ if ((priority > TIPC_MAX_LINK_PRI) &&
(priority != TIPC_MEDIA_LINK_PRI)) {
warn("Bearer <%s> rejected, illegal priority\n", name);
return -EINVAL;
diff --git a/net/tipc/config.c b/net/tipc/config.c
index 4785bf26cdf..f76d3b15e4e 100644
--- a/net/tipc/config.c
+++ b/net/tipc/config.c
@@ -179,7 +179,7 @@ static struct sk_buff *cfg_set_own_addr(void)
if (!tipc_addr_node_valid(addr))
return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
" (node address)");
- if (tipc_mode == TIPC_NET_MODE)
+ if (tipc_own_addr)
return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
" (cannot change node address once assigned)");
@@ -218,7 +218,7 @@ static struct sk_buff *cfg_set_max_publications(void)
return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
- if (value != delimit(value, 1, 65535))
+ if (value < 1 || value > 65535)
return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
" (max publications must be 1-65535)");
tipc_max_publications = value;
@@ -233,7 +233,7 @@ static struct sk_buff *cfg_set_max_subscriptions(void)
return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
- if (value != delimit(value, 1, 65535))
+ if (value < 1 || value > 65535)
return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
" (max subscriptions must be 1-65535");
tipc_max_subscriptions = value;
@@ -249,14 +249,11 @@ static struct sk_buff *cfg_set_max_ports(void)
value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
if (value == tipc_max_ports)
return tipc_cfg_reply_none();
- if (value != delimit(value, 127, 65535))
+ if (value < 127 || value > 65535)
return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
" (max ports must be 127-65535)");
- if (tipc_mode != TIPC_NOT_RUNNING)
- return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
- " (cannot change max ports while TIPC is active)");
- tipc_max_ports = value;
- return tipc_cfg_reply_none();
+ return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
+ " (cannot change max ports while TIPC is active)");
}
static struct sk_buff *cfg_set_netid(void)
@@ -268,10 +265,10 @@ static struct sk_buff *cfg_set_netid(void)
value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
if (value == tipc_net_id)
return tipc_cfg_reply_none();
- if (value != delimit(value, 1, 9999))
+ if (value < 1 || value > 9999)
return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
" (network id must be 1-9999)");
- if (tipc_mode == TIPC_NET_MODE)
+ if (tipc_own_addr)
return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
" (cannot change network id once TIPC has joined a network)");
tipc_net_id = value;
@@ -481,7 +478,7 @@ int tipc_cfg_init(void)
seq.type = TIPC_CFG_SRV;
seq.lower = seq.upper = tipc_own_addr;
- res = tipc_nametbl_publish_rsv(config_port_ref, TIPC_ZONE_SCOPE, &seq);
+ res = tipc_publish(config_port_ref, TIPC_ZONE_SCOPE, &seq);
if (res)
goto failed;
diff --git a/net/tipc/core.c b/net/tipc/core.c
index 2691cd57b8a..68eba03e795 100644
--- a/net/tipc/core.c
+++ b/net/tipc/core.c
@@ -53,7 +53,6 @@
/* global variables used by multiple sub-systems within TIPC */
-int tipc_mode = TIPC_NOT_RUNNING;
int tipc_random;
const char tipc_alphabet[] =
@@ -125,11 +124,6 @@ int tipc_core_start_net(unsigned long addr)
static void tipc_core_stop(void)
{
- if (tipc_mode != TIPC_NODE_MODE)
- return;
-
- tipc_mode = TIPC_NOT_RUNNING;
-
tipc_netlink_stop();
tipc_handler_stop();
tipc_cfg_stop();
@@ -148,11 +142,7 @@ static int tipc_core_start(void)
{
int res;
- if (tipc_mode != TIPC_NOT_RUNNING)
- return -ENOPROTOOPT;
-
get_random_bytes(&tipc_random, sizeof(tipc_random));
- tipc_mode = TIPC_NODE_MODE;
res = tipc_handler_start();
if (!res)
diff --git a/net/tipc/core.h b/net/tipc/core.h
index 2761af36d14..13837e0e56b 100644
--- a/net/tipc/core.h
+++ b/net/tipc/core.h
@@ -130,13 +130,6 @@ void tipc_msg_dbg(struct print_buf *, struct tipc_msg *, const char *);
#define ELINKCONG EAGAIN /* link congestion <=> resource unavailable */
/*
- * TIPC operating mode routines
- */
-#define TIPC_NOT_RUNNING 0
-#define TIPC_NODE_MODE 1
-#define TIPC_NET_MODE 2
-
-/*
* Global configuration variables
*/
@@ -151,7 +144,6 @@ extern int tipc_remote_management;
* Other global variables
*/
-extern int tipc_mode;
extern int tipc_random;
extern const char tipc_alphabet[];
@@ -168,16 +160,6 @@ extern void tipc_netlink_stop(void);
extern int tipc_socket_init(void);
extern void tipc_socket_stop(void);
-static inline int delimit(int val, int min, int max)
-{
- if (val > max)
- return max;
- if (val < min)
- return min;
- return val;
-}
-
-
/*
* TIPC timer and signal code
*/
@@ -279,28 +261,4 @@ static inline struct tipc_msg *buf_msg(struct sk_buff *skb)
extern struct sk_buff *tipc_buf_acquire(u32 size);
-/**
- * buf_discard - frees a TIPC message buffer
- * @skb: message buffer
- *
- * Frees a message buffer. If passed NULL, just returns.
- */
-
-static inline void buf_discard(struct sk_buff *skb)
-{
- kfree_skb(skb);
-}
-
-/**
- * buf_linearize - convert a TIPC message buffer into a single contiguous piece
- * @skb: message buffer
- *
- * Returns 0 on success.
- */
-
-static inline int buf_linearize(struct sk_buff *skb)
-{
- return skb_linearize(skb);
-}
-
#endif
diff --git a/net/tipc/discover.c b/net/tipc/discover.c
index a00e5f81156..c630a21b2be 100644
--- a/net/tipc/discover.c
+++ b/net/tipc/discover.c
@@ -82,6 +82,7 @@ static struct sk_buff *tipc_disc_init_msg(u32 type,
msg = buf_msg(buf);
tipc_msg_init(msg, LINK_CONFIG, type, INT_H_SIZE, dest_domain);
msg_set_non_seq(msg, 1);
+ msg_set_node_sig(msg, tipc_random);
msg_set_dest_domain(msg, dest_domain);
msg_set_bc_netid(msg, tipc_net_id);
b_ptr->media->addr2msg(&b_ptr->addr, msg_media_addr(msg));
@@ -121,20 +122,22 @@ void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr)
{
struct tipc_node *n_ptr;
struct tipc_link *link;
- struct tipc_media_addr media_addr, *addr;
+ struct tipc_media_addr media_addr;
struct sk_buff *rbuf;
struct tipc_msg *msg = buf_msg(buf);
u32 dest = msg_dest_domain(msg);
u32 orig = msg_prevnode(msg);
u32 net_id = msg_bc_netid(msg);
u32 type = msg_type(msg);
+ u32 signature = msg_node_sig(msg);
+ int addr_mismatch;
int link_fully_up;
media_addr.broadcast = 1;
b_ptr->media->msg2addr(&media_addr, msg_media_addr(msg));
- buf_discard(buf);
+ kfree_skb(buf);
- /* Validate discovery message from requesting node */
+ /* Ensure message from node is valid and communication is permitted */
if (net_id != tipc_net_id)
return;
if (media_addr.broadcast)
@@ -162,15 +165,50 @@ void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr)
}
tipc_node_lock(n_ptr);
+ /* Prepare to validate requesting node's signature and media address */
link = n_ptr->links[b_ptr->identity];
+ addr_mismatch = (link != NULL) &&
+ memcmp(&link->media_addr, &media_addr, sizeof(media_addr));
- /* Create a link endpoint for this bearer, if necessary */
- if (!link) {
- link = tipc_link_create(n_ptr, b_ptr, &media_addr);
- if (!link) {
+ /*
+ * Ensure discovery message's signature is correct
+ *
+ * If signature is incorrect and there is no working link to the node,
+ * accept the new signature but invalidate all existing links to the
+ * node so they won't re-activate without a new discovery message.
+ *
+ * If signature is incorrect and the requested link to the node is
+ * working, accept the new signature. (This is an instance of delayed
+ * rediscovery, where a link endpoint was able to re-establish contact
+ * with its peer endpoint on a node that rebooted before receiving a
+ * discovery message from that node.)
+ *
+ * If signature is incorrect and there is a working link to the node
+ * that is not the requested link, reject the request (must be from
+ * a duplicate node).
+ */
+ if (signature != n_ptr->signature) {
+ if (n_ptr->working_links == 0) {
+ struct tipc_link *curr_link;
+ int i;
+
+ for (i = 0; i < MAX_BEARERS; i++) {
+ curr_link = n_ptr->links[i];
+ if (curr_link) {
+ memset(&curr_link->media_addr, 0,
+ sizeof(media_addr));
+ tipc_link_reset(curr_link);
+ }
+ }
+ addr_mismatch = (link != NULL);
+ } else if (tipc_link_is_up(link) && !addr_mismatch) {
+ /* delayed rediscovery */
+ } else {
+ disc_dupl_alert(b_ptr, orig, &media_addr);
tipc_node_unlock(n_ptr);
return;
}
+ n_ptr->signature = signature;
}
/*
@@ -183,17 +221,26 @@ void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr)
* the new media address and reset the link to ensure it starts up
* cleanly.
*/
- addr = &link->media_addr;
- if (memcmp(addr, &media_addr, sizeof(*addr))) {
- if (tipc_link_is_up(link) || (!link->started)) {
+
+ if (addr_mismatch) {
+ if (tipc_link_is_up(link)) {
disc_dupl_alert(b_ptr, orig, &media_addr);
tipc_node_unlock(n_ptr);
return;
+ } else {
+ memcpy(&link->media_addr, &media_addr,
+ sizeof(media_addr));
+ tipc_link_reset(link);
+ }
+ }
+
+ /* Create a link endpoint for this bearer, if necessary */
+ if (!link) {
+ link = tipc_link_create(n_ptr, b_ptr, &media_addr);
+ if (!link) {
+ tipc_node_unlock(n_ptr);
+ return;
}
- warn("Resetting link <%s>, peer interface address changed\n",
- link->name);
- memcpy(addr, &media_addr, sizeof(*addr));
- tipc_link_reset(link);
}
/* Accept discovery message & send response, if necessary */
@@ -203,7 +250,7 @@ void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr)
rbuf = tipc_disc_init_msg(DSC_RESP_MSG, orig, b_ptr);
if (rbuf) {
b_ptr->media->send_msg(rbuf, b_ptr, &media_addr);
- buf_discard(rbuf);
+ kfree_skb(rbuf);
}
}
@@ -349,7 +396,7 @@ void tipc_disc_delete(struct tipc_link_req *req)
{
k_cancel_timer(&req->timer);
k_term_timer(&req->timer);
- buf_discard(req->buf);
+ kfree_skb(req->buf);
kfree(req);
}
diff --git a/net/tipc/link.c b/net/tipc/link.c
index ac1832a66f8..b4b9b30167a 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -484,7 +484,7 @@ static void link_release_outqueue(struct tipc_link *l_ptr)
while (buf) {
next = buf->next;
- buf_discard(buf);
+ kfree_skb(buf);
buf = next;
}
l_ptr->first_out = NULL;
@@ -503,7 +503,7 @@ void tipc_link_reset_fragments(struct tipc_link *l_ptr)
while (buf) {
next = buf->next;
- buf_discard(buf);
+ kfree_skb(buf);
buf = next;
}
l_ptr->defragm_buf = NULL;
@@ -522,20 +522,20 @@ void tipc_link_stop(struct tipc_link *l_ptr)
buf = l_ptr->oldest_deferred_in;
while (buf) {
next = buf->next;
- buf_discard(buf);
+ kfree_skb(buf);
buf = next;
}
buf = l_ptr->first_out;
while (buf) {
next = buf->next;
- buf_discard(buf);
+ kfree_skb(buf);
buf = next;
}
tipc_link_reset_fragments(l_ptr);
- buf_discard(l_ptr->proto_msg_queue);
+ kfree_skb(l_ptr->proto_msg_queue);
l_ptr->proto_msg_queue = NULL;
}
@@ -571,12 +571,12 @@ void tipc_link_reset(struct tipc_link *l_ptr)
/* Clean up all queues: */
link_release_outqueue(l_ptr);
- buf_discard(l_ptr->proto_msg_queue);
+ kfree_skb(l_ptr->proto_msg_queue);
l_ptr->proto_msg_queue = NULL;
buf = l_ptr->oldest_deferred_in;
while (buf) {
struct sk_buff *next = buf->next;
- buf_discard(buf);
+ kfree_skb(buf);
buf = next;
}
if (!list_empty(&l_ptr->waiting_ports))
@@ -810,7 +810,7 @@ static int link_bundle_buf(struct tipc_link *l_ptr,
skb_copy_to_linear_data_offset(bundler, to_pos, buf->data, size);
msg_set_size(bundler_msg, to_pos + size);
msg_set_msgcnt(bundler_msg, msg_msgcnt(bundler_msg) + 1);
- buf_discard(buf);
+ kfree_skb(buf);
l_ptr->stats.sent_bundled++;
return 1;
}
@@ -871,17 +871,15 @@ int tipc_link_send_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
u32 queue_limit = l_ptr->queue_limit[imp];
u32 max_packet = l_ptr->max_pkt;
- msg_set_prevnode(msg, tipc_own_addr); /* If routed message */
-
/* Match msg importance against queue limits: */
if (unlikely(queue_size >= queue_limit)) {
if (imp <= TIPC_CRITICAL_IMPORTANCE) {
link_schedule_port(l_ptr, msg_origport(msg), size);
- buf_discard(buf);
+ kfree_skb(buf);
return -ELINKCONG;
}
- buf_discard(buf);
+ kfree_skb(buf);
if (imp > CONN_MANAGER) {
warn("Resetting link <%s>, send queue full", l_ptr->name);
tipc_link_reset(l_ptr);
@@ -968,10 +966,10 @@ int tipc_link_send(struct sk_buff *buf, u32 dest, u32 selector)
if (l_ptr)
res = tipc_link_send_buf(l_ptr, buf);
else
- buf_discard(buf);
+ kfree_skb(buf);
tipc_node_unlock(n_ptr);
} else {
- buf_discard(buf);
+ kfree_skb(buf);
}
read_unlock_bh(&tipc_net_lock);
return res;
@@ -1018,7 +1016,7 @@ void tipc_link_send_names(struct list_head *message_list, u32 dest)
list_for_each_safe(buf, temp_buf, ((struct sk_buff *)message_list)) {
list_del((struct list_head *)buf);
- buf_discard(buf);
+ kfree_skb(buf);
}
}
@@ -1262,7 +1260,7 @@ again:
error:
for (; buf_chain; buf_chain = buf) {
buf = buf_chain->next;
- buf_discard(buf_chain);
+ kfree_skb(buf_chain);
}
return -EFAULT;
}
@@ -1316,7 +1314,7 @@ error:
tipc_node_unlock(node);
for (; buf_chain; buf_chain = buf) {
buf = buf_chain->next;
- buf_discard(buf_chain);
+ kfree_skb(buf_chain);
}
goto again;
}
@@ -1324,7 +1322,7 @@ error:
reject:
for (; buf_chain; buf_chain = buf) {
buf = buf_chain->next;
- buf_discard(buf_chain);
+ kfree_skb(buf_chain);
}
return tipc_port_reject_sections(sender, hdr, msg_sect, num_sect,
total_len, TIPC_ERR_NO_NODE);
@@ -1390,7 +1388,7 @@ u32 tipc_link_push_packet(struct tipc_link *l_ptr)
msg_set_bcast_ack(buf_msg(buf), l_ptr->owner->bclink.last_in);
if (tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) {
l_ptr->unacked_window = 0;
- buf_discard(buf);
+ kfree_skb(buf);
l_ptr->proto_msg_queue = NULL;
return 0;
} else {
@@ -1501,13 +1499,13 @@ static void link_retransmit_failure(struct tipc_link *l_ptr,
tipc_node_lock(n_ptr);
tipc_addr_string_fill(addr_string, n_ptr->addr);
- info("Multicast link info for %s\n", addr_string);
+ info("Broadcast link info for %s\n", addr_string);
+ info("Supportable: %d, ", n_ptr->bclink.supportable);
info("Supported: %d, ", n_ptr->bclink.supported);
info("Acked: %u\n", n_ptr->bclink.acked);
info("Last in: %u, ", n_ptr->bclink.last_in);
- info("Gap after: %u, ", n_ptr->bclink.gap_after);
- info("Gap to: %u\n", n_ptr->bclink.gap_to);
- info("Nack sync: %u\n\n", n_ptr->bclink.nack_sync);
+ info("Oos state: %u, ", n_ptr->bclink.oos_state);
+ info("Last sent: %u\n", n_ptr->bclink.last_sent);
tipc_k_signal((Handler)link_reset_all, (unsigned long)n_ptr->addr);
@@ -1679,7 +1677,7 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr)
/* Ensure message data is a single contiguous unit */
- if (unlikely(buf_linearize(buf)))
+ if (unlikely(skb_linearize(buf)))
goto cont;
/* Handle arrival of a non-unicast link message */
@@ -1736,7 +1734,7 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr)
/* Release acked messages */
- if (tipc_node_is_up(n_ptr) && n_ptr->bclink.supported)
+ if (n_ptr->bclink.supported)
tipc_bclink_acknowledge(n_ptr, msg_bcast_ack(msg));
crs = l_ptr->first_out;
@@ -1744,7 +1742,7 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr)
less_eq(buf_seqno(crs), ackd)) {
struct sk_buff *next = crs->next;
- buf_discard(crs);
+ kfree_skb(crs);
crs = next;
released++;
}
@@ -1773,52 +1771,56 @@ protocol_check:
if (unlikely(l_ptr->oldest_deferred_in))
head = link_insert_deferred_queue(l_ptr,
head);
- if (likely(msg_is_dest(msg, tipc_own_addr))) {
deliver:
- if (likely(msg_isdata(msg))) {
- tipc_node_unlock(n_ptr);
- tipc_port_recv_msg(buf);
- continue;
+ if (likely(msg_isdata(msg))) {
+ tipc_node_unlock(n_ptr);
+ tipc_port_recv_msg(buf);
+ continue;
+ }
+ switch (msg_user(msg)) {
+ int ret;
+ case MSG_BUNDLER:
+ l_ptr->stats.recv_bundles++;
+ l_ptr->stats.recv_bundled +=
+ msg_msgcnt(msg);
+ tipc_node_unlock(n_ptr);
+ tipc_link_recv_bundle(buf);
+ continue;
+ case NAME_DISTRIBUTOR:
+ tipc_node_unlock(n_ptr);
+ tipc_named_recv(buf);
+ continue;
+ case CONN_MANAGER:
+ tipc_node_unlock(n_ptr);
+ tipc_port_recv_proto_msg(buf);
+ continue;
+ case MSG_FRAGMENTER:
+ l_ptr->stats.recv_fragments++;
+ ret = tipc_link_recv_fragment(
+ &l_ptr->defragm_buf,
+ &buf, &msg);
+ if (ret == 1) {
+ l_ptr->stats.recv_fragmented++;
+ goto deliver;
}
- switch (msg_user(msg)) {
- case MSG_BUNDLER:
- l_ptr->stats.recv_bundles++;
- l_ptr->stats.recv_bundled +=
- msg_msgcnt(msg);
- tipc_node_unlock(n_ptr);
- tipc_link_recv_bundle(buf);
- continue;
- case NAME_DISTRIBUTOR:
- tipc_node_unlock(n_ptr);
- tipc_named_recv(buf);
- continue;
- case CONN_MANAGER:
- tipc_node_unlock(n_ptr);
- tipc_port_recv_proto_msg(buf);
- continue;
- case MSG_FRAGMENTER:
- l_ptr->stats.recv_fragments++;
- if (tipc_link_recv_fragment(&l_ptr->defragm_buf,
- &buf, &msg)) {
- l_ptr->stats.recv_fragmented++;
+ if (ret == -1)
+ l_ptr->next_in_no--;
+ break;
+ case CHANGEOVER_PROTOCOL:
+ type = msg_type(msg);
+ if (link_recv_changeover_msg(&l_ptr,
+ &buf)) {
+ msg = buf_msg(buf);
+ seq_no = msg_seqno(msg);
+ if (type == ORIGINAL_MSG)
goto deliver;
- }
- break;
- case CHANGEOVER_PROTOCOL:
- type = msg_type(msg);
- if (link_recv_changeover_msg(&l_ptr, &buf)) {
- msg = buf_msg(buf);
- seq_no = msg_seqno(msg);
- if (type == ORIGINAL_MSG)
- goto deliver;
- goto protocol_check;
- }
- break;
- default:
- buf_discard(buf);
- buf = NULL;
- break;
+ goto protocol_check;
}
+ break;
+ default:
+ kfree_skb(buf);
+ buf = NULL;
+ break;
}
tipc_node_unlock(n_ptr);
tipc_net_route_msg(buf);
@@ -1847,23 +1849,22 @@ deliver:
}
tipc_node_unlock(n_ptr);
cont:
- buf_discard(buf);
+ kfree_skb(buf);
}
read_unlock_bh(&tipc_net_lock);
}
/*
- * link_defer_buf(): Sort a received out-of-sequence packet
- * into the deferred reception queue.
- * Returns the increase of the queue length,i.e. 0 or 1
+ * tipc_link_defer_pkt - Add out-of-sequence message to deferred reception queue
+ *
+ * Returns increase in queue length (i.e. 0 or 1)
*/
-u32 tipc_link_defer_pkt(struct sk_buff **head,
- struct sk_buff **tail,
+u32 tipc_link_defer_pkt(struct sk_buff **head, struct sk_buff **tail,
struct sk_buff *buf)
{
- struct sk_buff *prev = NULL;
- struct sk_buff *crs = *head;
+ struct sk_buff *queue_buf;
+ struct sk_buff **prev;
u32 seq_no = buf_seqno(buf);
buf->next = NULL;
@@ -1881,31 +1882,30 @@ u32 tipc_link_defer_pkt(struct sk_buff **head,
return 1;
}
- /* Scan through queue and sort it in */
- do {
- struct tipc_msg *msg = buf_msg(crs);
+ /* Locate insertion point in queue, then insert; discard if duplicate */
+ prev = head;
+ queue_buf = *head;
+ for (;;) {
+ u32 curr_seqno = buf_seqno(queue_buf);
- if (less(seq_no, msg_seqno(msg))) {
- buf->next = crs;
- if (prev)
- prev->next = buf;
- else
- *head = buf;
- return 1;
+ if (seq_no == curr_seqno) {
+ kfree_skb(buf);
+ return 0;
}
- if (seq_no == msg_seqno(msg))
+
+ if (less(seq_no, curr_seqno))
break;
- prev = crs;
- crs = crs->next;
- } while (crs);
- /* Message is a duplicate of an existing message */
+ prev = &queue_buf->next;
+ queue_buf = queue_buf->next;
+ }
- buf_discard(buf);
- return 0;
+ buf->next = queue_buf;
+ *prev = buf;
+ return 1;
}
-/**
+/*
* link_handle_out_of_seq_msg - handle arrival of out-of-sequence packet
*/
@@ -1930,7 +1930,7 @@ static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr,
if (less(seq_no, mod(l_ptr->next_in_no))) {
l_ptr->stats.duplicates++;
- buf_discard(buf);
+ kfree_skb(buf);
return;
}
@@ -1956,6 +1956,13 @@ void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ,
u32 msg_size = sizeof(l_ptr->proto_msg);
int r_flag;
+ /* Discard any previous message that was deferred due to congestion */
+
+ if (l_ptr->proto_msg_queue) {
+ kfree_skb(l_ptr->proto_msg_queue);
+ l_ptr->proto_msg_queue = NULL;
+ }
+
if (link_blocked(l_ptr))
return;
@@ -1964,9 +1971,11 @@ void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ,
if ((l_ptr->owner->block_setup) && (msg_typ != RESET_MSG))
return;
+ /* Create protocol message with "out-of-sequence" sequence number */
+
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));
+ msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
msg_set_last_bcast(msg, tipc_bclink_get_last_sent());
if (msg_typ == STATE_MSG) {
@@ -2020,44 +2029,36 @@ void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ,
r_flag = (l_ptr->owner->working_links > tipc_link_is_up(l_ptr));
msg_set_redundant_link(msg, r_flag);
msg_set_linkprio(msg, l_ptr->priority);
-
- /* Ensure sequence number will not fit : */
+ msg_set_size(msg, msg_size);
msg_set_seqno(msg, mod(l_ptr->next_out_no + (0xffff/2)));
- /* Congestion? */
-
- if (tipc_bearer_congested(l_ptr->b_ptr, l_ptr)) {
- if (!l_ptr->proto_msg_queue) {
- l_ptr->proto_msg_queue =
- tipc_buf_acquire(sizeof(l_ptr->proto_msg));
- }
- buf = l_ptr->proto_msg_queue;
- if (!buf)
- return;
- skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg));
- return;
- }
-
- /* Message can be sent */
-
buf = tipc_buf_acquire(msg_size);
if (!buf)
return;
skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg));
- msg_set_size(buf_msg(buf), msg_size);
- if (tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) {
- l_ptr->unacked_window = 0;
- buf_discard(buf);
+ /* Defer message if bearer is already congested */
+
+ if (tipc_bearer_congested(l_ptr->b_ptr, l_ptr)) {
+ l_ptr->proto_msg_queue = buf;
return;
}
- /* New congestion */
- tipc_bearer_schedule(l_ptr->b_ptr, l_ptr);
- l_ptr->proto_msg_queue = buf;
- l_ptr->stats.bearer_congs++;
+ /* Defer message if attempting to send results in bearer congestion */
+
+ if (!tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) {
+ tipc_bearer_schedule(l_ptr->b_ptr, l_ptr);
+ l_ptr->proto_msg_queue = buf;
+ l_ptr->stats.bearer_congs++;
+ return;
+ }
+
+ /* Discard message if it was sent successfully */
+
+ l_ptr->unacked_window = 0;
+ kfree_skb(buf);
}
/*
@@ -2105,6 +2106,8 @@ static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf)
l_ptr->owner->block_setup = WAIT_NODE_DOWN;
}
+ link_state_event(l_ptr, RESET_MSG);
+
/* fall thru' */
case ACTIVATE_MSG:
/* Update link settings according other endpoint's values */
@@ -2127,16 +2130,22 @@ static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf)
} else {
l_ptr->max_pkt = l_ptr->max_pkt_target;
}
- l_ptr->owner->bclink.supported = (max_pkt_info != 0);
+ l_ptr->owner->bclink.supportable = (max_pkt_info != 0);
- link_state_event(l_ptr, msg_type(msg));
+ /* Synchronize broadcast link info, if not done previously */
+
+ if (!tipc_node_is_up(l_ptr->owner)) {
+ l_ptr->owner->bclink.last_sent =
+ l_ptr->owner->bclink.last_in =
+ msg_last_bcast(msg);
+ l_ptr->owner->bclink.oos_state = 0;
+ }
l_ptr->peer_session = msg_session(msg);
l_ptr->peer_bearer_id = msg_bearer_id(msg);
- /* Synchronize broadcast sequence numbers */
- if (!tipc_node_redundant_links(l_ptr->owner))
- l_ptr->owner->bclink.last_in = mod(msg_last_bcast(msg));
+ if (msg_type(msg) == ACTIVATE_MSG)
+ link_state_event(l_ptr, ACTIVATE_MSG);
break;
case STATE_MSG:
@@ -2177,7 +2186,9 @@ static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf)
/* Protocol message before retransmits, reduce loss risk */
- tipc_bclink_check_gap(l_ptr->owner, msg_last_bcast(msg));
+ if (l_ptr->owner->bclink.supported)
+ tipc_bclink_update_link_state(l_ptr->owner,
+ msg_last_bcast(msg));
if (rec_gap || (msg_probe(msg))) {
tipc_link_send_proto_msg(l_ptr, STATE_MSG,
@@ -2191,7 +2202,7 @@ static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf)
break;
}
exit:
- buf_discard(buf);
+ kfree_skb(buf);
}
@@ -2389,7 +2400,7 @@ static int link_recv_changeover_msg(struct tipc_link **l_ptr,
warn("Link changeover error, duplicate msg dropped\n");
goto exit;
}
- buf_discard(tunnel_buf);
+ kfree_skb(tunnel_buf);
return 1;
}
@@ -2421,7 +2432,7 @@ static int link_recv_changeover_msg(struct tipc_link **l_ptr,
} else {
*buf = buf_extract(tunnel_buf, INT_H_SIZE);
if (*buf != NULL) {
- buf_discard(tunnel_buf);
+ kfree_skb(tunnel_buf);
return 1;
} else {
warn("Link changeover error, original msg dropped\n");
@@ -2429,7 +2440,7 @@ static int link_recv_changeover_msg(struct tipc_link **l_ptr,
}
exit:
*buf = NULL;
- buf_discard(tunnel_buf);
+ kfree_skb(tunnel_buf);
return 0;
}
@@ -2451,7 +2462,7 @@ void tipc_link_recv_bundle(struct sk_buff *buf)
pos += align(msg_size(buf_msg(obuf)));
tipc_net_route_msg(obuf);
}
- buf_discard(buf);
+ kfree_skb(buf);
}
/*
@@ -2500,11 +2511,11 @@ static int link_send_long_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
}
fragm = tipc_buf_acquire(fragm_sz + INT_H_SIZE);
if (fragm == NULL) {
- buf_discard(buf);
+ kfree_skb(buf);
while (buf_chain) {
buf = buf_chain;
buf_chain = buf_chain->next;
- buf_discard(buf);
+ kfree_skb(buf);
}
return -ENOMEM;
}
@@ -2521,7 +2532,7 @@ static int link_send_long_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
crs += fragm_sz;
msg_set_type(&fragm_hdr, FRAGMENT);
}
- buf_discard(buf);
+ kfree_skb(buf);
/* Append chain of fragments to send queue & send them */
@@ -2608,7 +2619,7 @@ int tipc_link_recv_fragment(struct sk_buff **pending, struct sk_buff **fb,
if (msg_type(imsg) == TIPC_MCAST_MSG)
max = TIPC_MAX_USER_MSG_SIZE + MCAST_H_SIZE;
if (msg_size(imsg) > max) {
- buf_discard(fbuf);
+ kfree_skb(fbuf);
return 0;
}
pbuf = tipc_buf_acquire(msg_size(imsg));
@@ -2623,9 +2634,11 @@ int tipc_link_recv_fragment(struct sk_buff **pending, struct sk_buff **fb,
set_fragm_size(pbuf, fragm_sz);
set_expected_frags(pbuf, exp_fragm_cnt - 1);
} else {
- warn("Link unable to reassemble fragmented message\n");
+ dbg("Link unable to reassemble fragmented message\n");
+ kfree_skb(fbuf);
+ return -1;
}
- buf_discard(fbuf);
+ kfree_skb(fbuf);
return 0;
} else if (pbuf && (msg_type(fragm) != FIRST_FRAGMENT)) {
u32 dsz = msg_data_sz(fragm);
@@ -2634,7 +2647,7 @@ int tipc_link_recv_fragment(struct sk_buff **pending, struct sk_buff **fb,
u32 exp_frags = get_expected_frags(pbuf) - 1;
skb_copy_to_linear_data_offset(pbuf, crs,
msg_data(fragm), dsz);
- buf_discard(fbuf);
+ kfree_skb(fbuf);
/* Is message complete? */
@@ -2651,7 +2664,7 @@ int tipc_link_recv_fragment(struct sk_buff **pending, struct sk_buff **fb,
set_expected_frags(pbuf, exp_frags);
return 0;
}
- buf_discard(fbuf);
+ kfree_skb(fbuf);
return 0;
}
@@ -2682,7 +2695,7 @@ static void link_check_defragm_bufs(struct tipc_link *l_ptr)
prev->next = buf->next;
else
l_ptr->defragm_buf = buf->next;
- buf_discard(buf);
+ kfree_skb(buf);
}
buf = next;
}
@@ -3057,7 +3070,7 @@ struct sk_buff *tipc_link_cmd_show_stats(const void *req_tlv_area, int req_tlv_s
str_len = tipc_link_stats((char *)TLV_DATA(req_tlv_area),
(char *)TLV_DATA(rep_tlv), MAX_LINK_STATS_INFO);
if (!str_len) {
- buf_discard(buf);
+ kfree_skb(buf);
return tipc_cfg_reply_error_string("link not found");
}
diff --git a/net/tipc/log.c b/net/tipc/log.c
index 952c39f643e..895c6e530b0 100644
--- a/net/tipc/log.c
+++ b/net/tipc/log.c
@@ -304,7 +304,7 @@ struct sk_buff *tipc_log_resize_cmd(const void *req_tlv_area, int req_tlv_space)
return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR);
value = ntohl(*(__be32 *)TLV_DATA(req_tlv_area));
- if (value != delimit(value, 0, 32768))
+ if (value > 32768)
return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
" (log size must be 0-32768)");
if (tipc_log_resize(value))
diff --git a/net/tipc/msg.c b/net/tipc/msg.c
index 3e4d3e29be6..e3afe162c0a 100644
--- a/net/tipc/msg.c
+++ b/net/tipc/msg.c
@@ -106,7 +106,7 @@ int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect,
if (likely(res))
return dsz;
- buf_discard(*buf);
+ kfree_skb(*buf);
*buf = NULL;
return -EFAULT;
}
diff --git a/net/tipc/msg.h b/net/tipc/msg.h
index 7b0cda16710..eba524e34a6 100644
--- a/net/tipc/msg.h
+++ b/net/tipc/msg.h
@@ -384,11 +384,6 @@ static inline void msg_set_destnode(struct tipc_msg *m, u32 a)
msg_set_word(m, 7, a);
}
-static inline int msg_is_dest(struct tipc_msg *m, u32 d)
-{
- return msg_short(m) || (msg_destnode(m) == d);
-}
-
static inline u32 msg_nametype(struct tipc_msg *m)
{
return msg_word(m, 8);
@@ -517,6 +512,16 @@ static inline void msg_set_seq_gap(struct tipc_msg *m, u32 n)
msg_set_bits(m, 1, 16, 0x1fff, n);
}
+static inline u32 msg_node_sig(struct tipc_msg *m)
+{
+ return msg_bits(m, 1, 0, 0xffff);
+}
+
+static inline void msg_set_node_sig(struct tipc_msg *m, u32 n)
+{
+ msg_set_bits(m, 1, 0, 0xffff, n);
+}
+
/*
* Word 2
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index 98ebb37f180..d57da615961 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -120,7 +120,7 @@ static void named_cluster_distribute(struct sk_buff *buf)
}
}
- buf_discard(buf);
+ kfree_skb(buf);
}
/**
@@ -239,9 +239,6 @@ exit:
*
* Invoked for each publication issued by a newly failed node.
* Removes publication structure from name table & deletes it.
- * In rare cases the link may have come back up again when this
- * function is called, and we have two items representing the same
- * publication. Nudge this item's key to distinguish it from the other.
*/
static void named_purge_publ(struct publication *publ)
@@ -249,7 +246,6 @@ static void named_purge_publ(struct publication *publ)
struct publication *p;
write_lock_bh(&tipc_nametbl_lock);
- publ->key += 1222345;
p = tipc_nametbl_remove_publ(publ->type, publ->lower,
publ->node, publ->ref, publ->key);
if (p)
@@ -316,7 +312,7 @@ void tipc_named_recv(struct sk_buff *buf)
item++;
}
write_unlock_bh(&tipc_nametbl_lock);
- buf_discard(buf);
+ kfree_skb(buf);
}
/**
diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c
index 89eb5621ebb..c6a1ae36952 100644
--- a/net/tipc/name_table.c
+++ b/net/tipc/name_table.c
@@ -114,10 +114,8 @@ struct name_table {
};
static struct name_table table;
-static atomic_t rsv_publ_ok = ATOMIC_INIT(0);
DEFINE_RWLOCK(tipc_nametbl_lock);
-
static int hash(int x)
{
return x & (tipc_nametbl_size - 1);
@@ -270,6 +268,13 @@ static struct publication *tipc_nameseq_insert_publ(struct name_seq *nseq,
}
info = sseq->info;
+
+ /* Check if an identical publication already exists */
+ list_for_each_entry(publ, &info->zone_list, zone_list) {
+ if ((publ->ref == port) && (publ->key == key) &&
+ (!publ->node || (publ->node == node)))
+ return NULL;
+ }
} else {
u32 inspos;
struct sub_seq *freesseq;
@@ -534,10 +539,17 @@ struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower,
}
/*
- * tipc_nametbl_translate - translate name to port id
+ * tipc_nametbl_translate - perform name translation
+ *
+ * On entry, 'destnode' is the search domain used during translation.
*
- * Note: on entry 'destnode' is the search domain used during translation;
- * on exit it passes back the node address of the matching port (if any)
+ * On exit:
+ * - if name translation is deferred to another node/cluster/zone,
+ * leaves 'destnode' unchanged (will be non-zero) and returns 0
+ * - if name translation is attempted and succeeds, sets 'destnode'
+ * to publishing node and returns port reference (will be non-zero)
+ * - if name translation is attempted and fails, sets 'destnode' to 0
+ * and returns 0
*/
u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode)
@@ -547,6 +559,7 @@ u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode)
struct publication *publ;
struct name_seq *seq;
u32 ref = 0;
+ u32 node = 0;
if (!tipc_in_scope(*destnode, tipc_own_addr))
return 0;
@@ -604,11 +617,12 @@ u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode)
}
ref = publ->ref;
- *destnode = publ->node;
+ node = publ->node;
no_match:
spin_unlock_bh(&seq->lock);
not_found:
read_unlock_bh(&tipc_nametbl_lock);
+ *destnode = node;
return ref;
}
@@ -665,22 +679,7 @@ exit:
return res;
}
-/**
- * tipc_nametbl_publish_rsv - publish port name using a reserved name type
- */
-
-int tipc_nametbl_publish_rsv(u32 ref, unsigned int scope,
- struct tipc_name_seq const *seq)
-{
- int res;
-
- atomic_inc(&rsv_publ_ok);
- res = tipc_publish(ref, scope, seq);
- atomic_dec(&rsv_publ_ok);
- return res;
-}
-
-/**
+/*
* tipc_nametbl_publish - add name publication to network name tables
*/
@@ -694,11 +693,6 @@ struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper,
tipc_max_publications);
return NULL;
}
- if ((type < TIPC_RESERVED_TYPES) && !atomic_read(&rsv_publ_ok)) {
- warn("Publication failed, reserved name {%u,%u,%u}\n",
- type, lower, upper);
- return NULL;
- }
write_lock_bh(&tipc_nametbl_lock);
table.local_publ_count++;
diff --git a/net/tipc/name_table.h b/net/tipc/name_table.h
index 8086b42f92a..207d59ebf84 100644
--- a/net/tipc/name_table.h
+++ b/net/tipc/name_table.h
@@ -91,8 +91,6 @@ struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space);
u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *node);
int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit,
struct tipc_port_list *dports);
-int tipc_nametbl_publish_rsv(u32 ref, unsigned int scope,
- struct tipc_name_seq const *seq);
struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper,
u32 scope, u32 port_ref, u32 key);
int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key);
diff --git a/net/tipc/net.c b/net/tipc/net.c
index 61afee7e829..d4531b07076 100644
--- a/net/tipc/net.c
+++ b/net/tipc/net.c
@@ -117,7 +117,7 @@ static void net_route_named_msg(struct sk_buff *buf)
u32 dport;
if (!msg_named(msg)) {
- buf_discard(buf);
+ kfree_skb(buf);
return;
}
@@ -161,7 +161,7 @@ void tipc_net_route_msg(struct sk_buff *buf)
tipc_port_recv_proto_msg(buf);
break;
default:
- buf_discard(buf);
+ kfree_skb(buf);
}
return;
}
@@ -175,14 +175,10 @@ int tipc_net_start(u32 addr)
{
char addr_string[16];
- if (tipc_mode != TIPC_NODE_MODE)
- return -ENOPROTOOPT;
-
tipc_subscr_stop();
tipc_cfg_stop();
tipc_own_addr = addr;
- tipc_mode = TIPC_NET_MODE;
tipc_named_reinit();
tipc_port_reinit();
@@ -201,10 +197,9 @@ void tipc_net_stop(void)
{
struct tipc_node *node, *t_node;
- if (tipc_mode != TIPC_NET_MODE)
+ if (!tipc_own_addr)
return;
write_lock_bh(&tipc_net_lock);
- tipc_mode = TIPC_NODE_MODE;
tipc_bearer_stop();
tipc_bclink_stop();
list_for_each_entry_safe(node, t_node, &tipc_node_list, list)
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 6b226faad89..a34cabc2c43 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -39,6 +39,8 @@
#include "node.h"
#include "name_distr.h"
+#define NODE_HTABLE_SIZE 512
+
static void node_lost_contact(struct tipc_node *n_ptr);
static void node_established_contact(struct tipc_node *n_ptr);
@@ -49,9 +51,19 @@ LIST_HEAD(tipc_node_list);
static u32 tipc_num_nodes;
static atomic_t tipc_num_links = ATOMIC_INIT(0);
-u32 tipc_own_tag;
-/**
+/*
+ * A trivial power-of-two bitmask technique is used for speed, since this
+ * operation is done for every incoming TIPC packet. The number of hash table
+ * entries has been chosen so that no hash chain exceeds 8 nodes and will
+ * usually be much smaller (typically only a single node).
+ */
+static inline unsigned int tipc_hashfn(u32 addr)
+{
+ return addr & (NODE_HTABLE_SIZE - 1);
+}
+
+/*
* tipc_node_find - locate specified node object, if it exists
*/
@@ -113,6 +125,7 @@ struct tipc_node *tipc_node_create(u32 addr)
}
list_add_tail(&n_ptr->list, &temp_node->list);
n_ptr->block_setup = WAIT_PEER_DOWN;
+ n_ptr->signature = INVALID_NODE_SIG;
tipc_num_nodes++;
@@ -253,63 +266,14 @@ void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
n_ptr->link_cnt--;
}
-/*
- * Routing table management - five cases to handle:
- *
- * 1: A link towards a zone/cluster external node comes up.
- * => Send a multicast message updating routing tables of all
- * system nodes within own cluster that the new destination
- * can be reached via this node.
- * (node.establishedContact()=>cluster.multicastNewRoute())
- *
- * 2: A link towards a slave node comes up.
- * => Send a multicast message updating routing tables of all
- * system nodes within own cluster that the new destination
- * can be reached via this node.
- * (node.establishedContact()=>cluster.multicastNewRoute())
- * => Send a message to the slave node about existence
- * of all system nodes within cluster:
- * (node.establishedContact()=>cluster.sendLocalRoutes())
- *
- * 3: A new cluster local system node becomes available.
- * => Send message(s) to this particular node containing
- * information about all cluster external and slave
- * nodes which can be reached via this node.
- * (node.establishedContact()==>network.sendExternalRoutes())
- * (node.establishedContact()==>network.sendSlaveRoutes())
- * => Send messages to all directly connected slave nodes
- * containing information about the existence of the new node
- * (node.establishedContact()=>cluster.multicastNewRoute())
- *
- * 4: The link towards a zone/cluster external node or slave
- * node goes down.
- * => Send a multcast message updating routing tables of all
- * nodes within cluster that the new destination can not any
- * longer be reached via this node.
- * (node.lostAllLinks()=>cluster.bcastLostRoute())
- *
- * 5: A cluster local system node becomes unavailable.
- * => Remove all references to this node from the local
- * routing tables. Note: This is a completely node
- * local operation.
- * (node.lostAllLinks()=>network.removeAsRouter())
- * => Send messages to all directly connected slave nodes
- * containing information about loss of the node
- * (node.establishedContact()=>cluster.multicastLostRoute())
- *
- */
-
static void node_established_contact(struct tipc_node *n_ptr)
{
tipc_k_signal((Handler)tipc_named_node_up, n_ptr->addr);
- /* Syncronize broadcast acks */
- n_ptr->bclink.acked = tipc_bclink_get_last_sent();
-
- if (n_ptr->bclink.supported) {
+ if (n_ptr->bclink.supportable) {
+ n_ptr->bclink.acked = tipc_bclink_get_last_sent();
tipc_bclink_add_node(n_ptr->addr);
- if (n_ptr->addr < tipc_own_addr)
- tipc_own_tag++;
+ n_ptr->bclink.supported = 1;
}
}
@@ -338,22 +302,20 @@ static void node_lost_contact(struct tipc_node *n_ptr)
/* 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);
+ kfree_skb(buf);
}
+ n_ptr->bclink.deferred_size = 0;
if (n_ptr->bclink.defragm) {
- buf_discard(n_ptr->bclink.defragm);
+ kfree_skb(n_ptr->bclink.defragm);
n_ptr->bclink.defragm = NULL;
}
tipc_bclink_remove_node(n_ptr->addr);
tipc_bclink_acknowledge(n_ptr, INVALID_LINK_SEQ);
- if (n_ptr->addr < tipc_own_addr)
- tipc_own_tag--;
n_ptr->bclink.supported = 0;
}
@@ -444,12 +406,12 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space)
return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE
" (network address)");
- if (tipc_mode != TIPC_NET_MODE)
+ if (!tipc_own_addr)
return tipc_cfg_reply_none();
read_lock_bh(&tipc_net_lock);
- /* Get space for all unicast links + multicast link */
+ /* Get space for all unicast links + broadcast link */
payload_size = TLV_SPACE(sizeof(link_info)) *
(atomic_read(&tipc_num_links) + 1);
diff --git a/net/tipc/node.h b/net/tipc/node.h
index 0b1c5f8b699..72561c971d6 100644
--- a/net/tipc/node.h
+++ b/net/tipc/node.h
@@ -42,6 +42,11 @@
#include "net.h"
#include "bearer.h"
+/*
+ * Out-of-range value for node signature
+ */
+#define INVALID_NODE_SIG 0x10000
+
/* 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 */
@@ -61,13 +66,15 @@
* @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
+ * @signature: node instance identifier
* @bclink: broadcast-related info
+ * @supportable: non-zero if node supports TIPC b'cast link capability
* @supported: non-zero if node supports TIPC b'cast capability
* @acked: sequence # of last outbound b'cast message acknowledged by node
* @last_in: sequence # of last in-sequence b'cast message received from node
- * @gap_after: sequence # of last message not requiring a NAK request
- * @gap_to: sequence # of last message requiring a NAK request
- * @nack_sync: counter that determines when NAK requests should be sent
+ * @last_sent: sequence # of last b'cast message sent by node
+ * @oos_state: state tracker for handling OOS b'cast messages
+ * @deferred_size: number of OOS b'cast messages in deferred queue
* @deferred_head: oldest OOS b'cast message received from node
* @deferred_tail: newest OOS b'cast message received from node
* @defragm: list of partially reassembled b'cast message fragments from node
@@ -85,35 +92,23 @@ struct tipc_node {
int working_links;
int block_setup;
int permit_changeover;
+ u32 signature;
struct {
- int supported;
+ u8 supportable;
+ u8 supported;
u32 acked;
u32 last_in;
- u32 gap_after;
- u32 gap_to;
- u32 nack_sync;
+ u32 last_sent;
+ u32 oos_state;
+ u32 deferred_size;
struct sk_buff *deferred_head;
struct sk_buff *deferred_tail;
struct sk_buff *defragm;
} bclink;
};
-#define NODE_HTABLE_SIZE 512
extern struct list_head tipc_node_list;
-/*
- * A trivial power-of-two bitmask technique is used for speed, since this
- * operation is done for every incoming TIPC packet. The number of hash table
- * entries has been chosen so that no hash chain exceeds 8 nodes and will
- * usually be much smaller (typically only a single node).
- */
-static inline unsigned int tipc_hashfn(u32 addr)
-{
- return addr & (NODE_HTABLE_SIZE - 1);
-}
-
-extern u32 tipc_own_tag;
-
struct tipc_node *tipc_node_find(u32 addr);
struct tipc_node *tipc_node_create(u32 addr);
void tipc_node_delete(struct tipc_node *n_ptr);
diff --git a/net/tipc/port.c b/net/tipc/port.c
index d91efc69e6f..94d2904cce6 100644
--- a/net/tipc/port.c
+++ b/net/tipc/port.c
@@ -116,13 +116,13 @@ int tipc_multicast(u32 ref, struct tipc_name_seq const *seq,
ibuf = skb_copy(buf, GFP_ATOMIC);
if (ibuf == NULL) {
tipc_port_list_free(&dports);
- buf_discard(buf);
+ kfree_skb(buf);
return -ENOMEM;
}
}
res = tipc_bclink_send_msg(buf);
if ((res < 0) && (dports.count != 0))
- buf_discard(ibuf);
+ kfree_skb(ibuf);
} else {
ibuf = buf;
}
@@ -187,7 +187,7 @@ void tipc_port_recv_mcast(struct sk_buff *buf, struct tipc_port_list *dp)
}
}
exit:
- buf_discard(buf);
+ kfree_skb(buf);
tipc_port_list_free(dp);
}
@@ -400,15 +400,16 @@ int tipc_reject_msg(struct sk_buff *buf, u32 err)
/* send self-abort message when rejecting on a connected port */
if (msg_connected(msg)) {
- struct sk_buff *abuf = NULL;
struct tipc_port *p_ptr = tipc_port_lock(msg_destport(msg));
if (p_ptr) {
+ struct sk_buff *abuf = NULL;
+
if (p_ptr->connected)
abuf = port_build_self_abort_msg(p_ptr, err);
tipc_port_unlock(p_ptr);
+ tipc_net_route_msg(abuf);
}
- tipc_net_route_msg(abuf);
}
/* send returned message & dispose of rejected message */
@@ -419,7 +420,7 @@ int tipc_reject_msg(struct sk_buff *buf, u32 err)
else
tipc_link_send(rbuf, src_node, msg_link_selector(rmsg));
exit:
- buf_discard(buf);
+ kfree_skb(buf);
return data_sz;
}
@@ -567,7 +568,7 @@ void tipc_port_recv_proto_msg(struct sk_buff *buf)
tipc_port_unlock(p_ptr);
exit:
tipc_net_route_msg(r_buf);
- buf_discard(buf);
+ kfree_skb(buf);
}
static void port_print(struct tipc_port *p_ptr, struct print_buf *buf, int full_id)
@@ -758,7 +759,7 @@ static void port_dispatcher_sigh(void *dummy)
}
}
if (buf)
- buf_discard(buf);
+ kfree_skb(buf);
buf = next;
continue;
err:
@@ -812,7 +813,7 @@ err:
}
}
if (buf)
- buf_discard(buf);
+ kfree_skb(buf);
buf = next;
continue;
reject:
@@ -1053,8 +1054,6 @@ int tipc_connect2port(u32 ref, struct tipc_portid const *peer)
msg = &p_ptr->phdr;
msg_set_destnode(msg, peer->node);
msg_set_destport(msg, peer->ref);
- msg_set_orignode(msg, tipc_own_addr);
- msg_set_origport(msg, p_ptr->ref);
msg_set_type(msg, TIPC_CONN_MSG);
msg_set_lookup_scope(msg, 0);
msg_set_hdr_sz(msg, SHORT_H_SIZE);
@@ -1132,6 +1131,49 @@ int tipc_shutdown(u32 ref)
return tipc_disconnect(ref);
}
+/**
+ * tipc_port_recv_msg - receive message from lower layer and deliver to port user
+ */
+
+int tipc_port_recv_msg(struct sk_buff *buf)
+{
+ struct tipc_port *p_ptr;
+ struct tipc_msg *msg = buf_msg(buf);
+ u32 destport = msg_destport(msg);
+ u32 dsz = msg_data_sz(msg);
+ u32 err;
+
+ /* forward unresolved named message */
+ if (unlikely(!destport)) {
+ tipc_net_route_msg(buf);
+ return dsz;
+ }
+
+ /* validate destination & pass to port, otherwise reject message */
+ p_ptr = tipc_port_lock(destport);
+ if (likely(p_ptr)) {
+ if (likely(p_ptr->connected)) {
+ if ((unlikely(msg_origport(msg) !=
+ tipc_peer_port(p_ptr))) ||
+ (unlikely(msg_orignode(msg) !=
+ tipc_peer_node(p_ptr))) ||
+ (unlikely(!msg_connected(msg)))) {
+ err = TIPC_ERR_NO_PORT;
+ tipc_port_unlock(p_ptr);
+ goto reject;
+ }
+ }
+ err = p_ptr->dispatcher(p_ptr, buf);
+ tipc_port_unlock(p_ptr);
+ if (likely(!err))
+ return dsz;
+ } else {
+ err = TIPC_ERR_NO_PORT;
+ }
+reject:
+ return tipc_reject_msg(buf, err);
+}
+
/*
* tipc_port_recv_sections(): Concatenate and deliver sectioned
* message for this node.
@@ -1210,8 +1252,6 @@ int tipc_send2name(u32 ref, struct tipc_name const *name, unsigned int domain,
msg = &p_ptr->phdr;
msg_set_type(msg, TIPC_NAMED_MSG);
- msg_set_orignode(msg, tipc_own_addr);
- msg_set_origport(msg, ref);
msg_set_hdr_sz(msg, NAMED_H_SIZE);
msg_set_nametype(msg, name->type);
msg_set_nameinst(msg, name->instance);
@@ -1220,7 +1260,7 @@ int tipc_send2name(u32 ref, struct tipc_name const *name, unsigned int domain,
msg_set_destnode(msg, destnode);
msg_set_destport(msg, destport);
- if (likely(destport)) {
+ if (likely(destport || destnode)) {
if (likely(destnode == tipc_own_addr))
res = tipc_port_recv_sections(p_ptr, num_sect,
msg_sect, total_len);
@@ -1261,8 +1301,6 @@ int tipc_send2port(u32 ref, struct tipc_portid const *dest,
msg = &p_ptr->phdr;
msg_set_type(msg, TIPC_DIRECT_MSG);
msg_set_lookup_scope(msg, 0);
- msg_set_orignode(msg, tipc_own_addr);
- msg_set_origport(msg, ref);
msg_set_destnode(msg, dest->node);
msg_set_destport(msg, dest->ref);
msg_set_hdr_sz(msg, BASIC_H_SIZE);
@@ -1301,8 +1339,6 @@ int tipc_send_buf2port(u32 ref, struct tipc_portid const *dest,
msg = &p_ptr->phdr;
msg_set_type(msg, TIPC_DIRECT_MSG);
- msg_set_orignode(msg, tipc_own_addr);
- msg_set_origport(msg, ref);
msg_set_destnode(msg, dest->node);
msg_set_destport(msg, dest->ref);
msg_set_hdr_sz(msg, BASIC_H_SIZE);
diff --git a/net/tipc/port.h b/net/tipc/port.h
index f751807e2a9..9b88531e5a6 100644
--- a/net/tipc/port.h
+++ b/net/tipc/port.h
@@ -205,6 +205,7 @@ int tipc_disconnect_port(struct tipc_port *tp_ptr);
/*
* TIPC messaging routines
*/
+int tipc_port_recv_msg(struct sk_buff *buf);
int tipc_send(u32 portref, unsigned int num_sect, struct iovec const *msg_sect,
unsigned int total_len);
@@ -271,45 +272,4 @@ static inline int tipc_port_congested(struct tipc_port *p_ptr)
return (p_ptr->sent - p_ptr->acked) >= (TIPC_FLOW_CONTROL_WIN * 2);
}
-/**
- * tipc_port_recv_msg - receive message from lower layer and deliver to port user
- */
-
-static inline int tipc_port_recv_msg(struct sk_buff *buf)
-{
- struct tipc_port *p_ptr;
- struct tipc_msg *msg = buf_msg(buf);
- u32 destport = msg_destport(msg);
- u32 dsz = msg_data_sz(msg);
- u32 err;
-
- /* forward unresolved named message */
- if (unlikely(!destport)) {
- tipc_net_route_msg(buf);
- return dsz;
- }
-
- /* validate destination & pass to port, otherwise reject message */
- p_ptr = tipc_port_lock(destport);
- if (likely(p_ptr)) {
- if (likely(p_ptr->connected)) {
- if ((unlikely(msg_origport(msg) != tipc_peer_port(p_ptr))) ||
- (unlikely(msg_orignode(msg) != tipc_peer_node(p_ptr))) ||
- (unlikely(!msg_connected(msg)))) {
- err = TIPC_ERR_NO_PORT;
- tipc_port_unlock(p_ptr);
- goto reject;
- }
- }
- err = p_ptr->dispatcher(p_ptr, buf);
- tipc_port_unlock(p_ptr);
- if (likely(!err))
- return dsz;
- } else {
- err = TIPC_ERR_NO_PORT;
- }
-reject:
- return tipc_reject_msg(buf, err);
-}
-
#endif
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index e2f7c5d370b..29e957f6445 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -126,7 +126,7 @@ static atomic_t tipc_queue_size = ATOMIC_INIT(0);
static void advance_rx_queue(struct sock *sk)
{
- buf_discard(__skb_dequeue(&sk->sk_receive_queue));
+ kfree_skb(__skb_dequeue(&sk->sk_receive_queue));
atomic_dec(&tipc_queue_size);
}
@@ -142,7 +142,7 @@ static void discard_rx_queue(struct sock *sk)
while ((buf = __skb_dequeue(&sk->sk_receive_queue))) {
atomic_dec(&tipc_queue_size);
- buf_discard(buf);
+ kfree_skb(buf);
}
}
@@ -288,7 +288,7 @@ static int release(struct socket *sock)
break;
atomic_dec(&tipc_queue_size);
if (TIPC_SKB_CB(buf)->handle != 0)
- buf_discard(buf);
+ kfree_skb(buf);
else {
if ((sock->state == SS_CONNECTING) ||
(sock->state == SS_CONNECTED)) {
@@ -355,6 +355,9 @@ static int bind(struct socket *sock, struct sockaddr *uaddr, int uaddr_len)
else if (addr->addrtype != TIPC_ADDR_NAMESEQ)
return -EAFNOSUPPORT;
+ if (addr->addr.nameseq.type < TIPC_RESERVED_TYPES)
+ return -EACCES;
+
return (addr->scope > 0) ?
tipc_publish(portref, addr->scope, &addr->addr.nameseq) :
tipc_withdraw(portref, -addr->scope, &addr->addr.nameseq);
@@ -1612,7 +1615,7 @@ restart:
if (buf) {
atomic_dec(&tipc_queue_size);
if (TIPC_SKB_CB(buf)->handle != 0) {
- buf_discard(buf);
+ kfree_skb(buf);
goto restart;
}
tipc_disconnect(tport->ref);
diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c
index 8c49566da8f..b2964e9895d 100644
--- a/net/tipc/subscr.c
+++ b/net/tipc/subscr.c
@@ -552,7 +552,7 @@ int tipc_subscr_start(void)
if (res)
goto failed;
- res = tipc_nametbl_publish_rsv(topsrv.setup_port, TIPC_NODE_SCOPE, &seq);
+ res = tipc_publish(topsrv.setup_port, TIPC_NODE_SCOPE, &seq);
if (res) {
tipc_deleteport(topsrv.setup_port);
topsrv.setup_port = 0;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 85d3bb7490a..8ee85aa79fa 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -530,6 +530,16 @@ static int unix_seqpacket_sendmsg(struct kiocb *, struct socket *,
static int unix_seqpacket_recvmsg(struct kiocb *, struct socket *,
struct msghdr *, size_t, int);
+static void unix_set_peek_off(struct sock *sk, int val)
+{
+ struct unix_sock *u = unix_sk(sk);
+
+ mutex_lock(&u->readlock);
+ sk->sk_peek_off = val;
+ mutex_unlock(&u->readlock);
+}
+
+
static const struct proto_ops unix_stream_ops = {
.family = PF_UNIX,
.owner = THIS_MODULE,
@@ -549,6 +559,7 @@ static const struct proto_ops unix_stream_ops = {
.recvmsg = unix_stream_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
+ .set_peek_off = unix_set_peek_off,
};
static const struct proto_ops unix_dgram_ops = {
@@ -570,6 +581,7 @@ static const struct proto_ops unix_dgram_ops = {
.recvmsg = unix_dgram_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
+ .set_peek_off = unix_set_peek_off,
};
static const struct proto_ops unix_seqpacket_ops = {
@@ -591,6 +603,7 @@ static const struct proto_ops unix_seqpacket_ops = {
.recvmsg = unix_seqpacket_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
+ .set_peek_off = unix_set_peek_off,
};
static struct proto unix_proto = {
@@ -1756,6 +1769,7 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,
int noblock = flags & MSG_DONTWAIT;
struct sk_buff *skb;
int err;
+ int peeked, skip;
err = -EOPNOTSUPP;
if (flags&MSG_OOB)
@@ -1769,7 +1783,9 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,
goto out;
}
- skb = skb_recv_datagram(sk, flags, noblock, &err);
+ skip = sk_peek_offset(sk, flags);
+
+ skb = __skb_recv_datagram(sk, flags, &peeked, &skip, &err);
if (!skb) {
unix_state_lock(sk);
/* Signal EOF on disconnected non-blocking SEQPACKET socket. */
@@ -1786,12 +1802,12 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,
if (msg->msg_name)
unix_copy_addr(msg, skb->sk);
- if (size > skb->len)
- size = skb->len;
- else if (size < skb->len)
+ if (size > skb->len - skip)
+ size = skb->len - skip;
+ else if (size < skb->len - skip)
msg->msg_flags |= MSG_TRUNC;
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size);
+ err = skb_copy_datagram_iovec(skb, skip, msg->msg_iov, size);
if (err)
goto out_free;
@@ -1808,6 +1824,8 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,
if (!(flags & MSG_PEEK)) {
if (UNIXCB(skb).fp)
unix_detach_fds(siocb->scm, skb);
+
+ sk_peek_offset_bwd(sk, skb->len);
} else {
/* It is questionable: on PEEK we could:
- do not return fds - good, but too simple 8)
@@ -1821,10 +1839,13 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,
clearly however!
*/
+
+ sk_peek_offset_fwd(sk, size);
+
if (UNIXCB(skb).fp)
siocb->scm->fp = scm_fp_dup(UNIXCB(skb).fp);
}
- err = size;
+ err = (flags & MSG_TRUNC) ? skb->len - skip : size;
scm_recv(sock, msg, siocb->scm, flags);
@@ -1884,6 +1905,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
int target;
int err = 0;
long timeo;
+ int skip;
err = -EINVAL;
if (sk->sk_state != TCP_ESTABLISHED)
@@ -1913,12 +1935,15 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
goto out;
}
+ skip = sk_peek_offset(sk, flags);
+
do {
int chunk;
struct sk_buff *skb;
unix_state_lock(sk);
skb = skb_peek(&sk->sk_receive_queue);
+again:
if (skb == NULL) {
unix_sk(sk)->recursion_level = 0;
if (copied >= target)
@@ -1953,6 +1978,13 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
unix_state_unlock(sk);
break;
}
+
+ if (skip >= skb->len) {
+ skip -= skb->len;
+ skb = skb_peek_next(skb, &sk->sk_receive_queue);
+ goto again;
+ }
+
unix_state_unlock(sk);
if (check_creds) {
@@ -1972,8 +2004,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
sunaddr = NULL;
}
- chunk = min_t(unsigned int, skb->len, size);
- if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+ chunk = min_t(unsigned int, skb->len - skip, size);
+ if (memcpy_toiovec(msg->msg_iov, skb->data + skip, chunk)) {
if (copied == 0)
copied = -EFAULT;
break;
@@ -1985,6 +2017,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
if (!(flags & MSG_PEEK)) {
skb_pull(skb, chunk);
+ sk_peek_offset_bwd(sk, chunk);
+
if (UNIXCB(skb).fp)
unix_detach_fds(siocb->scm, skb);
@@ -2002,6 +2036,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
if (UNIXCB(skb).fp)
siocb->scm->fp = scm_fp_dup(UNIXCB(skb).fp);
+ sk_peek_offset_fwd(sk, chunk);
+
break;
}
} while (size);
diff --git a/net/unix/diag.c b/net/unix/diag.c
index 6b7697fd911..4195555aea6 100644
--- a/net/unix/diag.c
+++ b/net/unix/diag.c
@@ -301,10 +301,12 @@ static int unix_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
if (nlmsg_len(h) < hdrlen)
return -EINVAL;
- if (h->nlmsg_flags & NLM_F_DUMP)
- return netlink_dump_start(sock_diag_nlsk, skb, h,
- unix_diag_dump, NULL, 0);
- else
+ if (h->nlmsg_flags & NLM_F_DUMP) {
+ struct netlink_dump_control c = {
+ .dump = unix_diag_dump,
+ };
+ return netlink_dump_start(sock_diag_nlsk, skb, h, &c);
+ } else
return unix_diag_get_exact(skb, h, (struct unix_diag_req *)NLMSG_DATA(h));
}
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 66b84fbf274..7128dde0fe1 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -2299,8 +2299,13 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
if (link->dump == NULL)
return -EINVAL;
- return netlink_dump_start(net->xfrm.nlsk, skb, nlh,
- link->dump, link->done, 0);
+ {
+ struct netlink_dump_control c = {
+ .dump = link->dump,
+ .done = link->done,
+ };
+ return netlink_dump_start(net->xfrm.nlsk, skb, nlh, &c);
+ }
}
err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX,