aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/atm/clip.c17
-rw-r--r--net/caif/caif_dev.c2
-rw-r--r--net/caif/caif_socket.c113
-rw-r--r--net/caif/chnl_net.c15
-rw-r--r--net/core/dev.c94
-rw-r--r--net/core/neighbour.c90
-rw-r--r--net/core/netpoll.c71
-rw-r--r--net/decnet/dn_neigh.c24
-rw-r--r--net/decnet/dn_route.c3
-rw-r--r--net/ipv4/ip_gre.c19
-rw-r--r--net/ipv4/ip_sockglue.c33
-rw-r--r--net/ipv4/ipip.c3
-rw-r--r--net/ipv4/ping.c3
-rw-r--r--net/ipv4/proc.c1
-rw-r--r--net/ipv4/raw.c3
-rw-r--r--net/ipv4/route.c5
-rw-r--r--net/ipv4/tcp_ipv4.c296
-rw-r--r--net/ipv4/tcp_minisocks.c12
-rw-r--r--net/ipv4/tcp_output.c4
-rw-r--r--net/ipv4/udp.c3
-rw-r--r--net/ipv6/anycast.c29
-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/ipv6_sockglue.c34
-rw-r--r--net/ipv6/ndisc.c30
-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.c227
-rw-r--r--net/ipv6/udp.c3
-rw-r--r--net/ipv6/xfrm6_output.c2
-rw-r--r--net/iucv/af_iucv.c202
-rw-r--r--net/mac80211/cfg.c46
-rw-r--r--net/mac80211/chan.c28
-rw-r--r--net/mac80211/debugfs.c4
-rw-r--r--net/mac80211/debugfs_netdev.c35
-rw-r--r--net/mac80211/debugfs_sta.c5
-rw-r--r--net/mac80211/ibss.c94
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/iface.c13
-rw-r--r--net/mac80211/main.c3
-rw-r--r--net/mac80211/mesh_hwmp.c2
-rw-r--r--net/mac80211/mesh_plink.c6
-rw-r--r--net/mac80211/mlme.c25
-rw-r--r--net/mac80211/rate.c151
-rw-r--r--net/mac80211/rate.h4
-rw-r--r--net/mac80211/rx.c11
-rw-r--r--net/mac80211/sta_info.c84
-rw-r--r--net/mac80211/sta_info.h17
-rw-r--r--net/mac80211/status.c6
-rw-r--r--net/mac80211/tx.c5
-rw-r--r--net/mac80211/util.c21
-rw-r--r--net/mac80211/work.c5
-rw-r--r--net/netlink/af_netlink.c18
-rw-r--r--net/netlink/genetlink.c31
-rw-r--r--net/nfc/core.c5
-rw-r--r--net/nfc/nci/core.c118
-rw-r--r--net/nfc/nci/data.c4
-rw-r--r--net/nfc/nci/ntf.c337
-rw-r--r--net/nfc/nci/rsp.c28
-rw-r--r--net/nfc/netlink.c6
-rw-r--r--net/nfc/rawsock.c12
-rw-r--r--net/sched/Kconfig26
-rw-r--r--net/sched/Makefile1
-rw-r--r--net/sched/sch_plug.c233
-rw-r--r--net/tipc/bcast.c324
-rw-r--r--net/tipc/bcast.h2
-rw-r--r--net/tipc/link.c148
-rw-r--r--net/tipc/name_distr.c4
-rw-r--r--net/tipc/node.c14
-rw-r--r--net/tipc/node.h18
-rw-r--r--net/tipc/port.c5
-rw-r--r--net/wireless/mesh.c1
-rw-r--r--net/wireless/nl80211.c74
-rw-r--r--net/wireless/reg.c19
77 files changed, 2121 insertions, 1290 deletions
diff --git a/net/atm/clip.c b/net/atm/clip.c
index c12c258..ef95a30 100644
--- a/net/atm/clip.c
+++ b/net/atm/clip.c
@@ -328,6 +328,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 +340,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 +365,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 +384,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 +403,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/caif/caif_dev.c b/net/caif/caif_dev.c
index 82c5706..aa6f716 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 a97d97a..5016fa5 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);
}
@@ -856,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,
@@ -945,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);
@@ -1054,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",
@@ -1122,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;
}
@@ -1161,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)
@@ -1169,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/chnl_net.c b/net/caif/chnl_net.c
index 8656909..e866234 100644
--- a/net/caif/chnl_net.c
+++ b/net/caif/chnl_net.c
@@ -72,14 +72,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 +93,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 +102,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 +121,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 +219,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 +240,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;
}
diff --git a/net/core/dev.c b/net/core/dev.c
index 6ca32f6..763a0ed 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);
}
@@ -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;
}
@@ -4497,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,
@@ -4579,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;
}
}
@@ -5238,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);
@@ -5471,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;
@@ -5505,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;
@@ -5765,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;
}
}
@@ -5819,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;
@@ -5935,15 +5921,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
@@ -5959,7 +5943,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;
}
@@ -6492,8 +6476,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/neighbour.c b/net/core/neighbour.c
index e287346..f98ec44 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -2165,6 +2165,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 +2243,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 556b082..4ce473e 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 =
@@ -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/decnet/dn_neigh.c b/net/decnet/dn_neigh.c
index befe426..ee7013f 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 f31ce72..80a3de4 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/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 6b3ca5b..0286d78 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -730,15 +730,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 +752,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 +920,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);
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 8aa87c1..9125529 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;
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 22a1993..f84ebff 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,
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index aea5a19..cfc82cf 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -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,
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index 6afc807..02d6107 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 3ccda5a..ab46630 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -563,7 +563,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 bcacf54..4eeb8ce 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1117,10 +1117,15 @@ 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);
if (n)
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 94d683a..4d6f81c 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -90,16 +90,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 +593,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 +627,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) |
@@ -664,6 +689,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
@@ -764,7 +797,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);
}
@@ -881,153 +915,137 @@ 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));
+ 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);
}
}
@@ -1036,7 +1054,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;
@@ -1047,32 +1064,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,
@@ -1098,7 +1099,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;
@@ -1198,7 +1199,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? */
@@ -1486,7 +1488,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
@@ -1494,10 +1497,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
@@ -1858,7 +1859,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
@@ -1947,8 +1947,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 550e755..3cabafb 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 4ff3b6d..364784a 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/udp.c b/net/ipv4/udp.c
index 5d075b5..cd99f1a 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -917,7 +917,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);
diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c
index 59402b4..db00d27 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/icmp.c b/net/ipv6/icmp.c
index 01d46bf..af88934 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 b82bcde..5b27fbc 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 d97e071..7a98fc2 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/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 18a2719..6d6b65f 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;
@@ -1160,6 +1190,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 d8f02ef..8d81701 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);
@@ -1565,6 +1570,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 +1585,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/raw.c b/net/ipv6/raw.c
index d02f7e4..5bddea7 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 b69fae7..9447bd6 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 8c2e3ab..92be12b 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 133768e..c4ffd17 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 3edd05a..d16414c 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,
@@ -1510,10 +1398,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
@@ -1898,7 +1784,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 +1815,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 +1888,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 4f96b5c..8aebf8f 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -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_output.c b/net/ipv6/xfrm6_output.c
index 4eeff89..8755a30 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/iucv/af_iucv.c b/net/iucv/af_iucv.c
index d5c5b8f..98d1f0b 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,13 @@ 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->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 +180,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 +326,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 +362,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 +380,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)
@@ -447,13 +433,33 @@ 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);
+ }
+}
+
/* 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;
+ int err = 0;
+ int blen;
struct sk_buff *skb;
lock_sock(sk);
@@ -480,7 +486,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 +500,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 +707,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 +720,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)
@@ -894,11 +896,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 +1123,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 +1149,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 +1204,6 @@ release:
return len;
fail:
- if (skb->dev)
- dev_put(skb->dev);
kfree_skb(skb);
out:
release_sock(sk);
@@ -1396,7 +1396,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);
@@ -1486,7 +1493,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);
@@ -1565,13 +1572,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 +1633,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 +1657,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 +1758,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 +1835,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 +1857,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 +1864,7 @@ static void iucv_callback_txdone(struct iucv_path *path,
sk->sk_state_change(sk);
}
}
+ bh_unlock_sock(sk);
}
@@ -1864,9 +1872,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 +1968,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 +2038,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;
}
@@ -2172,11 +2191,11 @@ 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 */
case 0:
/* plain data frame */
memcpy(CB_TRGCLS(skb), &trans_hdr->iucv_hdr.class,
@@ -2202,65 +2221,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,8 +2288,6 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb,
}
}
-out_unlock:
- bh_unlock_sock(sk);
}
static const struct proto_ops iucv_sock_ops = {
.family = PF_IUCV,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 296620d..d15ba0d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -776,12 +776,10 @@ static int sta_apply_parameters(struct ieee80211_local *local,
if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
!test_sta_flag(sta, WLAN_STA_AUTH)) {
- ret = sta_info_move_state_checked(sta,
- IEEE80211_STA_AUTH);
+ ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
if (ret)
return ret;
- ret = sta_info_move_state_checked(sta,
- IEEE80211_STA_ASSOC);
+ ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
if (ret)
return ret;
}
@@ -789,11 +787,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
if (set & BIT(NL80211_STA_FLAG_AUTHORIZED))
- ret = sta_info_move_state_checked(sta,
- IEEE80211_STA_AUTHORIZED);
+ ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
- ret = sta_info_move_state_checked(sta,
- IEEE80211_STA_ASSOC);
+ ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
if (ret)
return ret;
}
@@ -805,12 +801,10 @@ static int sta_apply_parameters(struct ieee80211_local *local,
if (!(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) &&
test_sta_flag(sta, WLAN_STA_AUTH)) {
- ret = sta_info_move_state_checked(sta,
- IEEE80211_STA_AUTH);
+ ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
if (ret)
return ret;
- ret = sta_info_move_state_checked(sta,
- IEEE80211_STA_NONE);
+ ret = sta_info_move_state(sta, IEEE80211_STA_NONE);
if (ret)
return ret;
}
@@ -944,8 +938,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
return -ENOMEM;
- sta_info_move_state(sta, IEEE80211_STA_AUTH);
- sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+ sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+ sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
err = sta_apply_parameters(local, sta, params);
if (err) {
@@ -1001,6 +995,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
struct ieee80211_local *local = wiphy_priv(wiphy);
struct sta_info *sta;
struct ieee80211_sub_if_data *vlansdata;
+ int err;
mutex_lock(&local->sta_mtx);
@@ -1040,7 +1035,11 @@ static int ieee80211_change_station(struct wiphy *wiphy,
ieee80211_send_layer2_update(sta);
}
- sta_apply_parameters(local, sta, params);
+ err = sta_apply_parameters(local, sta, params);
+ if (err) {
+ mutex_unlock(&local->sta_mtx);
+ return err;
+ }
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && params->supported_rates)
rate_control_rate_init(sta);
@@ -1341,6 +1340,8 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
conf->dot11MeshHWMPRannInterval =
nconf->dot11MeshHWMPRannInterval;
}
+ if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask))
+ conf->dot11MeshForwarding = nconf->dot11MeshForwarding;
return 0;
}
@@ -1868,7 +1869,6 @@ static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,
s32 rssi_thold, u32 rssi_hyst)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_vif *vif = &sdata->vif;
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
@@ -1879,14 +1879,9 @@ static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,
bss_conf->cqm_rssi_thold = rssi_thold;
bss_conf->cqm_rssi_hyst = rssi_hyst;
- if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) {
- if (sdata->vif.type != NL80211_IFTYPE_STATION)
- return -EOPNOTSUPP;
- return 0;
- }
-
/* tell the driver upon association, unless already associated */
- if (sdata->u.mgd.associated)
+ if (sdata->u.mgd.associated &&
+ sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM);
return 0;
@@ -1907,8 +1902,11 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
return ret;
}
- for (i = 0; i < IEEE80211_NUM_BANDS; i++)
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
+ memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].mcs,
+ sizeof(mask->control[i].mcs));
+ }
return 0;
}
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 889c3e9..d1f7abd 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -20,23 +20,29 @@ __ieee80211_get_channel_mode(struct ieee80211_local *local,
if (!ieee80211_sdata_running(sdata))
continue;
- if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_MONITOR:
continue;
-
- if (sdata->vif.type == NL80211_IFTYPE_STATION &&
- !sdata->u.mgd.associated)
- continue;
-
- if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ case NL80211_IFTYPE_STATION:
+ if (!sdata->u.mgd.associated)
+ continue;
+ break;
+ case NL80211_IFTYPE_ADHOC:
if (!sdata->u.ibss.ssid_len)
continue;
if (!sdata->u.ibss.fixed_channel)
return CHAN_MODE_HOPPING;
- }
-
- if (sdata->vif.type == NL80211_IFTYPE_AP &&
- !sdata->u.ap.beacon)
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ /* will also have _AP interface */
continue;
+ case NL80211_IFTYPE_AP:
+ if (!sdata->u.ap.beacon)
+ continue;
+ break;
+ default:
+ break;
+ }
return CHAN_MODE_FIXED;
}
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 90baea5..affe64b 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -247,8 +247,6 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE)
sf += snprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
- if (local->hw.flags & IEEE80211_HW_BEACON_FILTER)
- sf += snprintf(buf + sf, mxln - sf, "BEACON_FILTER\n");
if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS)
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n");
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
@@ -259,8 +257,6 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
sf += snprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n");
if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
sf += snprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n");
- if (local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)
- sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_CQM_RSSI\n");
if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK)
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n");
if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 176c08f..510ed1d 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -87,6 +87,21 @@ static ssize_t ieee80211_if_fmt_##name( \
#define IEEE80211_IF_FMT_SIZE(name, field) \
IEEE80211_IF_FMT(name, field, "%zd\n")
+#define IEEE80211_IF_FMT_HEXARRAY(name, field) \
+static ssize_t ieee80211_if_fmt_##name( \
+ const struct ieee80211_sub_if_data *sdata, \
+ char *buf, int buflen) \
+{ \
+ char *p = buf; \
+ int i; \
+ for (i = 0; i < sizeof(sdata->field); i++) { \
+ p += scnprintf(p, buflen + buf - p, "%.2x ", \
+ sdata->field[i]); \
+ } \
+ p += scnprintf(p, buflen + buf - p, "\n"); \
+ return p - buf; \
+}
+
#define IEEE80211_IF_FMT_ATOMIC(name, field) \
static ssize_t ieee80211_if_fmt_##name( \
const struct ieee80211_sub_if_data *sdata, \
@@ -148,6 +163,11 @@ IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[IEEE80211_BAND_2GHZ],
HEX);
IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ],
HEX);
+IEEE80211_IF_FILE(rc_rateidx_mcs_mask_2ghz,
+ rc_rateidx_mcs_mask[IEEE80211_BAND_2GHZ], HEXARRAY);
+IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz,
+ rc_rateidx_mcs_mask[IEEE80211_BAND_5GHZ], HEXARRAY);
+
IEEE80211_IF_FILE(flags, flags, HEX);
IEEE80211_IF_FILE(state, state, LHEX);
IEEE80211_IF_FILE(channel_type, vif.bss_conf.channel_type, DEC);
@@ -422,6 +442,7 @@ IEEE80211_IF_FILE(dot11MeshGateAnnouncementProtocol,
u.mesh.mshcfg.dot11MeshGateAnnouncementProtocol, DEC);
IEEE80211_IF_FILE(dot11MeshHWMPRannInterval,
u.mesh.mshcfg.dot11MeshHWMPRannInterval, DEC);
+IEEE80211_IF_FILE(dot11MeshForwarding, u.mesh.mshcfg.dot11MeshForwarding, DEC);
#endif
@@ -441,6 +462,8 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(channel_type);
DEBUGFS_ADD(rc_rateidx_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
+ DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
+ DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
DEBUGFS_ADD(bssid);
DEBUGFS_ADD(aid);
@@ -458,6 +481,8 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(channel_type);
DEBUGFS_ADD(rc_rateidx_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
+ DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
+ DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
DEBUGFS_ADD(num_sta_authorized);
DEBUGFS_ADD(num_sta_ps);
@@ -468,6 +493,12 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata)
static void add_ibss_files(struct ieee80211_sub_if_data *sdata)
{
+ DEBUGFS_ADD(channel_type);
+ DEBUGFS_ADD(rc_rateidx_mask_2ghz);
+ DEBUGFS_ADD(rc_rateidx_mask_5ghz);
+ DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
+ DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
+
DEBUGFS_ADD_MODE(tsf, 0600);
}
@@ -479,6 +510,8 @@ static void add_wds_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(channel_type);
DEBUGFS_ADD(rc_rateidx_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
+ DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
+ DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
DEBUGFS_ADD(peer);
}
@@ -491,6 +524,8 @@ static void add_vlan_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(channel_type);
DEBUGFS_ADD(rc_rateidx_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
+ DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
+ DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
}
static void add_monitor_files(struct ieee80211_sub_if_data *sdata)
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 2406b3e..c838371 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -63,14 +63,15 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""
int res = scnprintf(buf, sizeof(buf),
- "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
TEST(PS_DRIVER), TEST(AUTHORIZED),
TEST(SHORT_PREAMBLE),
TEST(WME), TEST(WDS), TEST(CLEAR_PS_FILT),
TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL),
TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
- TEST(TDLS_PEER_AUTH));
+ TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT),
+ TEST(INSERTED));
#undef TEST
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index a464396..7b3a0b0 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -36,31 +36,6 @@
#define IEEE80211_IBSS_MAX_STA_ENTRIES 128
-static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len)
-{
- u16 auth_alg, auth_transaction;
-
- lockdep_assert_held(&sdata->u.ibss.mtx);
-
- if (len < 24 + 6)
- return;
-
- auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
- auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
-
- /*
- * IEEE 802.11 standard does not require authentication in IBSS
- * networks and most implementations do not seem to use it.
- * However, try to reply to authentication attempts if someone
- * has actually implemented this.
- */
- if (auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1)
- ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, NULL, 0,
- sdata->u.ibss.bssid, NULL, 0, 0);
-}
-
static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, const int beacon_int,
struct ieee80211_channel *chan,
@@ -276,7 +251,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
cbss->tsf);
}
-static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
+static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
+ bool auth)
__acquires(RCU)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
@@ -290,22 +266,31 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
addr, sdata->name);
#endif
- sta_info_move_state(sta, IEEE80211_STA_AUTH);
- sta_info_move_state(sta, IEEE80211_STA_ASSOC);
- sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+ sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+ sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+ sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
rate_control_rate_init(sta);
/* If it fails, maybe we raced another insertion? */
if (sta_info_insert_rcu(sta))
return sta_info_get(sdata, addr);
+ if (auth) {
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ printk(KERN_DEBUG "TX Auth SA=%pM DA=%pM BSSID=%pM"
+ "(auth_transaction=1)\n", sdata->vif.addr,
+ sdata->u.ibss.bssid, addr);
+#endif
+ ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, NULL, 0,
+ addr, sdata->u.ibss.bssid, NULL, 0, 0);
+ }
return sta;
}
static struct sta_info *
ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, const u8 *addr,
- u32 supp_rates)
+ u32 supp_rates, bool auth)
__acquires(RCU)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
@@ -347,7 +332,42 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
sta->sta.supp_rates[band] = supp_rates |
ieee80211_mandatory_rates(local, band);
- return ieee80211_ibss_finish_sta(sta);
+ return ieee80211_ibss_finish_sta(sta, auth);
+}
+
+static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ u16 auth_alg, auth_transaction;
+
+ lockdep_assert_held(&sdata->u.ibss.mtx);
+
+ if (len < 24 + 6)
+ return;
+
+ auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
+ auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
+
+ if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1)
+ return;
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ printk(KERN_DEBUG "%s: RX Auth SA=%pM DA=%pM BSSID=%pM."
+ "(auth_transaction=%d)\n",
+ sdata->name, mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction);
+#endif
+ sta_info_destroy_addr(sdata, mgmt->sa);
+ ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
+ rcu_read_unlock();
+
+ /*
+ * IEEE 802.11 standard does not require authentication in IBSS
+ * networks and most implementations do not seem to use it.
+ * However, try to reply to authentication attempts if someone
+ * has actually implemented this.
+ */
+ ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, NULL, 0,
+ mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0);
}
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
@@ -412,7 +432,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
} else {
rcu_read_unlock();
sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
- mgmt->sa, supp_rates);
+ mgmt->sa, supp_rates, true);
}
}
@@ -540,7 +560,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_join_ibss(sdata, bss);
supp_rates = ieee80211_sta_get_rates(local, elems, band);
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
- supp_rates);
+ supp_rates, true);
rcu_read_unlock();
}
@@ -643,8 +663,7 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
"IBSS networks with same SSID (merge)\n", sdata->name);
ieee80211_request_internal_scan(sdata,
- ifibss->ssid, ifibss->ssid_len,
- ifibss->fixed_channel ? ifibss->channel : NULL);
+ ifibss->ssid, ifibss->ssid_len, NULL);
}
static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@@ -855,9 +874,6 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
size_t baselen;
struct ieee802_11_elems elems;
- if (memcmp(mgmt->da, sdata->vif.addr, ETH_ALEN))
- return; /* ignore ProbeResp to foreign address */
-
baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
if (baselen > len)
return;
@@ -945,7 +961,7 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
list_del(&sta->list);
spin_unlock_bh(&ifibss->incomplete_lock);
- ieee80211_ibss_finish_sta(sta);
+ ieee80211_ibss_finish_sta(sta, true);
rcu_read_unlock();
spin_lock_bh(&ifibss->incomplete_lock);
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 2f0642d..d47e8c1 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -646,6 +646,7 @@ struct ieee80211_sub_if_data {
/* bitmap of allowed (non-MCS) rate indexes for rate control */
u32 rc_rateidx_mask[IEEE80211_NUM_BANDS];
+ u8 rc_rateidx_mcs_mask[IEEE80211_NUM_BANDS][IEEE80211_HT_MCS_MASK_LEN];
union {
struct ieee80211_if_ap ap;
@@ -1396,7 +1397,7 @@ void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local,
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg,
u8 *extra, size_t extra_len, const u8 *bssid,
- const u8 *key, u8 key_len, u8 key_idx);
+ const u8 *da, const u8 *key, u8 key_len, u8 key_idx);
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 01a21c2..2efd595 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -318,9 +318,9 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
goto err_del_interface;
}
- sta_info_move_state(sta, IEEE80211_STA_AUTH);
- sta_info_move_state(sta, IEEE80211_STA_ASSOC);
- sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+ sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+ sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+ sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
res = sta_info_insert(sta);
if (res) {
@@ -1181,6 +1181,13 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
sband = local->hw.wiphy->bands[i];
sdata->rc_rateidx_mask[i] =
sband ? (1 << sband->n_bitrates) - 1 : 0;
+ if (sband)
+ memcpy(sdata->rc_rateidx_mcs_mask[i],
+ sband->ht_cap.mcs.rx_mask,
+ sizeof(sdata->rc_rateidx_mcs_mask[i]));
+ else
+ memset(sdata->rc_rateidx_mcs_mask[i], 0,
+ sizeof(sdata->rc_rateidx_mcs_mask[i]));
}
/* setup type-dependent data */
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 0a0d94a..6192caa 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -155,7 +155,8 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
power = chan->max_power;
else
power = local->power_constr_level ?
- (chan->max_power - local->power_constr_level) :
+ min(chan->max_power,
+ (chan->max_reg_power - local->power_constr_level)) :
chan->max_power;
if (local->user_power_level >= 0)
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 54df1b2..c27dec9 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -575,7 +575,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
ifmsh->mshstats.dropped_frames_ttl++;
}
- if (forward) {
+ if (forward && ifmsh->mshcfg.dot11MeshForwarding) {
u32 preq_id;
u8 hopcount, flags;
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index a172517..8806e5e 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -96,9 +96,9 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
if (!sta)
return NULL;
- sta_info_move_state(sta, IEEE80211_STA_AUTH);
- sta_info_move_state(sta, IEEE80211_STA_ASSOC);
- sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+ sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+ sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+ sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
set_sta_flag(sta, WLAN_STA_WME);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 295be92..49fd1ac 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -127,7 +127,7 @@ static void run_again(struct ieee80211_if_managed *ifmgd,
void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata)
{
- if (sdata->local->hw.flags & IEEE80211_HW_BEACON_FILTER)
+ if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)
return;
mod_timer(&sdata->u.mgd.bcn_mon_timer,
@@ -547,7 +547,7 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
if (pwr_constr_elem_len != 1)
return;
- if ((*pwr_constr_elem <= conf->channel->max_power) &&
+ if ((*pwr_constr_elem <= conf->channel->max_reg_power) &&
(*pwr_constr_elem != sdata->local->power_constr_level)) {
sdata->local->power_constr_level = *pwr_constr_elem;
ieee80211_hw_config(sdata->local, 0);
@@ -1043,7 +1043,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
bss_info_changed |= BSS_CHANGED_BSSID;
/* Tell the driver to monitor connection quality (if supported) */
- if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) &&
+ if (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI &&
bss_conf->cqm_rssi_thold)
bss_info_changed |= BSS_CHANGED_CQM;
@@ -1587,10 +1587,19 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
return false;
}
- sta_info_move_state(sta, IEEE80211_STA_AUTH);
- sta_info_move_state(sta, IEEE80211_STA_ASSOC);
- if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
- sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+ err = sta_info_move_state(sta, IEEE80211_STA_AUTH);
+ if (!err)
+ err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+ if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
+ err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+ if (err) {
+ printk(KERN_DEBUG
+ "%s: failed to move station %pM to desired state\n",
+ sdata->name, sta->sta.addr);
+ WARN_ON(__sta_info_destroy(sta));
+ mutex_unlock(&sdata->local->sta_mtx);
+ return false;
+ }
rates = 0;
basic_rates = 0;
@@ -1882,7 +1891,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (bss_conf->cqm_rssi_thold &&
ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT &&
- !(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) {
+ !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) {
int sig = ifmgd->ave_beacon_signal / 16;
int last_event = ifmgd->last_cqm_event_signal;
int thold = bss_conf->cqm_rssi_thold;
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 5a5a776..3fef26d 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -159,7 +159,6 @@ static struct rate_control_ref *rate_control_alloc(const char *name,
ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL);
if (!ref)
goto fail_ref;
- kref_init(&ref->kref);
ref->local = local;
ref->ops = ieee80211_rate_control_ops_get(name);
if (!ref->ops)
@@ -184,11 +183,8 @@ fail_ref:
return NULL;
}
-static void rate_control_release(struct kref *kref)
+static void rate_control_free(struct rate_control_ref *ctrl_ref)
{
- struct rate_control_ref *ctrl_ref;
-
- ctrl_ref = container_of(kref, struct rate_control_ref, kref);
ctrl_ref->ops->free(ctrl_ref->priv);
#ifdef CONFIG_MAC80211_DEBUGFS
@@ -293,8 +289,8 @@ bool rate_control_send_low(struct ieee80211_sta *sta,
}
EXPORT_SYMBOL(rate_control_send_low);
-static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
- int n_bitrates, u32 mask)
+static bool rate_idx_match_legacy_mask(struct ieee80211_tx_rate *rate,
+ int n_bitrates, u32 mask)
{
int j;
@@ -303,7 +299,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
if (mask & (1 << j)) {
/* Okay, found a suitable rate. Use it. */
rate->idx = j;
- return;
+ return true;
}
}
@@ -312,6 +308,112 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
if (mask & (1 << j)) {
/* Okay, found a suitable rate. Use it. */
rate->idx = j;
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,
+ u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
+{
+ int i, j;
+ int ridx, rbit;
+
+ ridx = rate->idx / 8;
+ rbit = rate->idx % 8;
+
+ /* sanity check */
+ if (ridx < 0 || ridx > IEEE80211_HT_MCS_MASK_LEN)
+ return false;
+
+ /* See whether the selected rate or anything below it is allowed. */
+ for (i = ridx; i >= 0; i--) {
+ for (j = rbit; j >= 0; j--)
+ if (mcs_mask[i] & BIT(j)) {
+ rate->idx = i * 8 + j;
+ return true;
+ }
+ rbit = 7;
+ }
+
+ /* Try to find a higher rate that would be allowed */
+ ridx = (rate->idx + 1) / 8;
+ rbit = (rate->idx + 1) % 8;
+
+ for (i = ridx; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+ for (j = rbit; j < 8; j++)
+ if (mcs_mask[i] & BIT(j)) {
+ rate->idx = i * 8 + j;
+ return true;
+ }
+ rbit = 0;
+ }
+ return false;
+}
+
+
+
+static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
+ struct ieee80211_tx_rate_control *txrc,
+ u32 mask,
+ u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
+{
+ struct ieee80211_tx_rate alt_rate;
+
+ /* handle HT rates */
+ if (rate->flags & IEEE80211_TX_RC_MCS) {
+ if (rate_idx_match_mcs_mask(rate, mcs_mask))
+ return;
+
+ /* also try the legacy rates. */
+ alt_rate.idx = 0;
+ /* keep protection flags */
+ alt_rate.flags = rate->flags &
+ (IEEE80211_TX_RC_USE_RTS_CTS |
+ IEEE80211_TX_RC_USE_CTS_PROTECT |
+ IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
+ alt_rate.count = rate->count;
+ if (rate_idx_match_legacy_mask(&alt_rate,
+ txrc->sband->n_bitrates,
+ mask)) {
+ *rate = alt_rate;
+ return;
+ }
+ } else {
+ struct sk_buff *skb = txrc->skb;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ __le16 fc;
+
+ /* handle legacy rates */
+ if (rate_idx_match_legacy_mask(rate, txrc->sband->n_bitrates,
+ mask))
+ return;
+
+ /* if HT BSS, and we handle a data frame, also try HT rates */
+ if (txrc->bss_conf->channel_type == NL80211_CHAN_NO_HT)
+ return;
+
+ fc = hdr->frame_control;
+ if (!ieee80211_is_data(fc))
+ return;
+
+ alt_rate.idx = 0;
+ /* keep protection flags */
+ alt_rate.flags = rate->flags &
+ (IEEE80211_TX_RC_USE_RTS_CTS |
+ IEEE80211_TX_RC_USE_CTS_PROTECT |
+ IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
+ alt_rate.count = rate->count;
+
+ alt_rate.flags |= IEEE80211_TX_RC_MCS;
+
+ if ((txrc->bss_conf->channel_type == NL80211_CHAN_HT40MINUS) ||
+ (txrc->bss_conf->channel_type == NL80211_CHAN_HT40PLUS))
+ alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+
+ if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) {
+ *rate = alt_rate;
return;
}
}
@@ -335,6 +437,7 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
int i;
u32 mask;
+ u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
if (sta) {
ista = &sta->sta;
@@ -358,10 +461,14 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
* the common case.
*/
mask = sdata->rc_rateidx_mask[info->band];
+ memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[info->band],
+ sizeof(mcs_mask));
if (mask != (1 << txrc->sband->n_bitrates) - 1) {
if (sta) {
/* Filter out rates that the STA does not support */
mask &= sta->sta.supp_rates[info->band];
+ for (i = 0; i < sizeof(mcs_mask); i++)
+ mcs_mask[i] &= sta->sta.ht_cap.mcs.rx_mask[i];
}
/*
* Make sure the rate index selected for each TX rate is
@@ -372,32 +479,18 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
/* Skip invalid rates */
if (info->control.rates[i].idx < 0)
break;
- /* Rate masking supports only legacy rates for now */
- if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS)
- continue;
- rate_idx_match_mask(&info->control.rates[i],
- txrc->sband->n_bitrates, mask);
+ rate_idx_match_mask(&info->control.rates[i], txrc,
+ mask, mcs_mask);
}
}
BUG_ON(info->control.rates[0].idx < 0);
}
-struct rate_control_ref *rate_control_get(struct rate_control_ref *ref)
-{
- kref_get(&ref->kref);
- return ref;
-}
-
-void rate_control_put(struct rate_control_ref *ref)
-{
- kref_put(&ref->kref, rate_control_release);
-}
-
int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
const char *name)
{
- struct rate_control_ref *ref, *old;
+ struct rate_control_ref *ref;
ASSERT_RTNL();
@@ -417,12 +510,8 @@ int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
return -ENOENT;
}
- old = local->rate_ctrl;
+ WARN_ON(local->rate_ctrl);
local->rate_ctrl = ref;
- if (old) {
- rate_control_put(old);
- sta_info_flush(local, NULL);
- }
wiphy_debug(local->hw.wiphy, "Selected rate control algorithm '%s'\n",
ref->ops->name);
@@ -440,6 +529,6 @@ void rate_control_deinitialize(struct ieee80211_local *local)
return;
local->rate_ctrl = NULL;
- rate_control_put(ref);
+ rate_control_free(ref);
}
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index 168427b..8268457 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -14,7 +14,6 @@
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/types.h>
-#include <linux/kref.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "sta_info.h"
@@ -23,14 +22,11 @@ struct rate_control_ref {
struct ieee80211_local *local;
struct rate_control_ops *ops;
void *priv;
- struct kref kref;
};
void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_tx_rate_control *txrc);
-struct rate_control_ref *rate_control_get(struct rate_control_ref *ref);
-void rate_control_put(struct rate_control_ref *ref);
static inline void rate_control_tx_status(struct ieee80211_local *local,
struct ieee80211_supported_band *sband,
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 5a5e504..b5ee084 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1145,19 +1145,15 @@ static void ap_sta_ps_start(struct sta_info *sta)
static void ap_sta_ps_end(struct sta_info *sta)
{
- struct ieee80211_sub_if_data *sdata = sta->sdata;
-
- atomic_dec(&sdata->bss->num_sta_ps);
-
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "%s: STA %pM aid %d exits power save mode\n",
- sdata->name, sta->sta.addr, sta->sta.aid);
+ sta->sdata->name, sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "%s: STA %pM aid %d driver-ps-blocked\n",
- sdata->name, sta->sta.addr, sta->sta.aid);
+ sta->sdata->name, sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
return;
}
@@ -2180,9 +2176,6 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
if (rx->sdata->vif.type == NL80211_IFTYPE_AP &&
ieee80211_is_beacon(mgmt->frame_control) &&
!(rx->flags & IEEE80211_RX_BEACON_REPORTED)) {
- struct ieee80211_rx_status *status;
-
- status = IEEE80211_SKB_RXCB(rx->skb);
cfg80211_report_obss_beacon(rx->local->hw.wiphy,
rx->skb->data, rx->skb->len,
status->freq, GFP_ATOMIC);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index ff11f6b..fa08238 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -208,10 +208,8 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
*/
void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
{
- if (sta->rate_ctrl) {
+ if (sta->rate_ctrl)
rate_control_free_sta(sta);
- rate_control_put(sta->rate_ctrl);
- }
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
wiphy_debug(local->hw.wiphy, "Destroyed STA %pM\n", sta->sta.addr);
@@ -264,13 +262,11 @@ static int sta_prepare_rate_control(struct ieee80211_local *local,
if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
return 0;
- sta->rate_ctrl = rate_control_get(local->rate_ctrl);
+ sta->rate_ctrl = local->rate_ctrl;
sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl,
&sta->sta, gfp);
- if (!sta->rate_ctrl_priv) {
- rate_control_put(sta->rate_ctrl);
+ if (!sta->rate_ctrl_priv)
return -ENOMEM;
- }
return 0;
}
@@ -407,6 +403,8 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
sta_info_hash_add(local, sta);
list_add(&sta->list, &local->sta_list);
+
+ set_sta_flag(sta, WLAN_STA_INSERTED);
} else {
sta->dummy = false;
}
@@ -711,7 +709,7 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
return have_buffered;
}
-static int __must_check __sta_info_destroy(struct sta_info *sta)
+int __must_check __sta_info_destroy(struct sta_info *sta)
{
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
@@ -726,6 +724,8 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
local = sta->local;
sdata = sta->sdata;
+ lockdep_assert_held(&local->sta_mtx);
+
/*
* Before removing the station from the driver and
* rate control, it might still start new aggregation
@@ -750,25 +750,19 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
sta->dead = true;
- if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
- test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
- BUG_ON(!sdata->bss);
-
- clear_sta_flag(sta, WLAN_STA_PS_STA);
- clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
-
- atomic_dec(&sdata->bss->num_sta_ps);
- sta_info_recalc_tim(sta);
- }
-
local->num_sta--;
local->sta_generation++;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
- while (sta->sta_state > IEEE80211_STA_NONE)
- sta_info_move_state(sta, sta->sta_state - 1);
+ while (sta->sta_state > IEEE80211_STA_NONE) {
+ int err = sta_info_move_state(sta, sta->sta_state - 1);
+ if (err) {
+ WARN_ON_ONCE(1);
+ break;
+ }
+ }
if (sta->uploaded) {
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -787,6 +781,15 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
*/
synchronize_rcu();
+ if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
+ BUG_ON(!sdata->bss);
+
+ clear_sta_flag(sta, WLAN_STA_PS_STA);
+
+ atomic_dec(&sdata->bss->num_sta_ps);
+ sta_info_recalc_tim(sta);
+ }
+
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
__skb_queue_purge(&sta->ps_tx_buf[ac]);
@@ -815,35 +818,20 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
}
#endif
- /* There could be some memory leaks because of ampdu tx pending queue
- * not being freed before destroying the station info.
- *
- * Make sure that such queues are purged before freeing the station
- * info.
- * TODO: We have to somehow postpone the full destruction
- * until the aggregation stop completes. Refer
- * http://thread.gmane.org/gmane.linux.kernel.wireless.general/81936
+ /*
+ * Destroy aggregation state here. It would be nice to wait for the
+ * driver to finish aggregation stop and then clean up, but for now
+ * drivers have to handle aggregation stop being requested, followed
+ * directly by station destruction.
*/
-
- mutex_lock(&sta->ampdu_mlme.mtx);
-
for (i = 0; i < STA_TID_NUM; i++) {
- tid_tx = rcu_dereference_protected_tid_tx(sta, i);
+ tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
if (!tid_tx)
continue;
- if (skb_queue_len(&tid_tx->pending)) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
- wiphy_debug(local->hw.wiphy, "TX A-MPDU purging %d "
- "packets for tid=%d\n",
- skb_queue_len(&tid_tx->pending), i);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
- __skb_queue_purge(&tid_tx->pending);
- }
- kfree_rcu(tid_tx, rcu_head);
+ __skb_queue_purge(&tid_tx->pending);
+ kfree(tid_tx);
}
- mutex_unlock(&sta->ampdu_mlme.mtx);
-
sta_info_free(local, sta);
return 0;
@@ -1009,9 +997,11 @@ EXPORT_SYMBOL(ieee80211_find_sta);
static void clear_sta_ps_flags(void *_sta)
{
struct sta_info *sta = _sta;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
- clear_sta_flag(sta, WLAN_STA_PS_STA);
+ if (test_and_clear_sta_flag(sta, WLAN_STA_PS_STA))
+ atomic_dec(&sdata->bss->num_sta_ps);
}
/* powersave support code */
@@ -1410,8 +1400,8 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
}
EXPORT_SYMBOL(ieee80211_sta_set_buffered);
-int sta_info_move_state_checked(struct sta_info *sta,
- enum ieee80211_sta_state new_state)
+int sta_info_move_state(struct sta_info *sta,
+ enum ieee80211_sta_state new_state)
{
might_sleep();
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 6f77f12..381de37 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -52,6 +52,7 @@
* @WLAN_STA_SP: Station is in a service period, so don't try to
* reply to other uAPSD trigger frames or PS-Poll.
* @WLAN_STA_4ADDR_EVENT: 4-addr event was already sent for this frame.
+ * @WLAN_STA_INSERTED: This station is inserted into the hash table.
*/
enum ieee80211_sta_info_flags {
WLAN_STA_AUTH,
@@ -71,6 +72,7 @@ enum ieee80211_sta_info_flags {
WLAN_STA_UAPSD,
WLAN_STA_SP,
WLAN_STA_4ADDR_EVENT,
+ WLAN_STA_INSERTED,
};
enum ieee80211_sta_state {
@@ -427,13 +429,17 @@ static inline int test_and_set_sta_flag(struct sta_info *sta,
return test_and_set_bit(flag, &sta->_flags);
}
-int sta_info_move_state_checked(struct sta_info *sta,
- enum ieee80211_sta_state new_state);
+int sta_info_move_state(struct sta_info *sta,
+ enum ieee80211_sta_state new_state);
-static inline void sta_info_move_state(struct sta_info *sta,
- enum ieee80211_sta_state new_state)
+static inline void sta_info_pre_move_state(struct sta_info *sta,
+ enum ieee80211_sta_state new_state)
{
- int ret = sta_info_move_state_checked(sta, new_state);
+ int ret;
+
+ WARN_ON_ONCE(test_sta_flag(sta, WLAN_STA_INSERTED));
+
+ ret = sta_info_move_state(sta, new_state);
WARN_ON_ONCE(ret);
}
@@ -544,6 +550,7 @@ int sta_info_insert(struct sta_info *sta);
int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU);
int sta_info_reinsert(struct sta_info *sta);
+int __must_check __sta_info_destroy(struct sta_info *sta);
int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata,
const u8 *addr);
int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 30c265c..d67f0b9 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -350,7 +350,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
bool send_to_cooked;
bool acked;
struct ieee80211_bar *bar;
- u16 tid;
int rtap_len;
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
@@ -412,7 +411,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
}
if (!acked && ieee80211_is_back_req(fc)) {
- u16 control;
+ u16 tid, control;
/*
* BAR failed, store the last SSN and retry sending
@@ -516,7 +515,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
if (ieee80211_is_nullfunc(hdr->frame_control) ||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
- bool acked = info->flags & IEEE80211_TX_STAT_ACK;
+ acked = info->flags & IEEE80211_TX_STAT_ACK;
+
cfg80211_probe_status(skb->dev, hdr->addr1,
cookie, acked, GFP_ATOMIC);
} else {
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index e05667c..1be0ca2 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -635,6 +635,9 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
txrc.max_rate_idx = -1;
else
txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1;
+ memcpy(txrc.rate_idx_mcs_mask,
+ tx->sdata->rc_rateidx_mcs_mask[tx->channel->band],
+ sizeof(txrc.rate_idx_mcs_mask));
txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP ||
tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
tx->sdata->vif.type == NL80211_IFTYPE_ADHOC);
@@ -2431,6 +2434,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
txrc.max_rate_idx = -1;
else
txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1;
+ memcpy(txrc.rate_idx_mcs_mask, sdata->rc_rateidx_mcs_mask[band],
+ sizeof(txrc.rate_idx_mcs_mask));
txrc.bss = true;
rate_control_get_rate(sdata, NULL, &txrc);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 9919892..d82d886 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -862,8 +862,8 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg,
- u8 *extra, size_t extra_len, const u8 *bssid,
- const u8 *key, u8 key_len, u8 key_idx)
+ u8 *extra, size_t extra_len, const u8 *da,
+ const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
@@ -881,7 +881,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
memset(mgmt, 0, 24 + 6);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_AUTH);
- memcpy(mgmt->da, bssid, ETH_ALEN);
+ memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
memcpy(mgmt->bssid, bssid, ETH_ALEN);
mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
@@ -1272,6 +1272,21 @@ int ieee80211_reconfig(struct ieee80211_local *local)
ieee80211_recalc_ps(local, -1);
/*
+ * The sta might be in psm against the ap (e.g. because
+ * this was the state before a hw restart), so we
+ * explicitly send a null packet in order to make sure
+ * it'll sync against the ap (and get out of psm).
+ */
+ if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) {
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ continue;
+
+ ieee80211_send_nullfunc(local, sdata, 0);
+ }
+ }
+
+ /*
* Clear the WLAN_STA_BLOCK_BA flag so new aggregation
* sessions can be established after a resume.
*
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index c6dd01a..0a1a176 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -480,7 +480,8 @@ ieee80211_authenticate(struct ieee80211_work *wk)
sdata->name, wk->filter_ta, wk->probe_auth.tries);
ieee80211_send_auth(sdata, 1, wk->probe_auth.algorithm, wk->ie,
- wk->ie_len, wk->filter_ta, NULL, 0, 0);
+ wk->ie_len, wk->filter_ta, wk->filter_ta, NULL, 0,
+ 0);
wk->probe_auth.transaction = 2;
wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
@@ -598,7 +599,7 @@ static void ieee80211_auth_challenge(struct ieee80211_work *wk,
return;
ieee80211_send_auth(sdata, 3, wk->probe_auth.algorithm,
elems.challenge - 2, elems.challenge_len + 2,
- wk->filter_ta, wk->probe_auth.key,
+ wk->filter_ta, wk->filter_ta, wk->probe_auth.key,
wk->probe_auth.key_len, wk->probe_auth.key_idx);
wk->probe_auth.transaction = 4;
}
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 629b061..4d751e3 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.
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index c29d256..a115471 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;
diff --git a/net/nfc/core.c b/net/nfc/core.c
index 3ddf6e6..6089aca 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -431,15 +431,10 @@ EXPORT_SYMBOL(nfc_alloc_recv_skb);
int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets,
int n_targets)
{
- int i;
-
pr_debug("dev_name=%s n_targets=%d\n", dev_name(&dev->dev), n_targets);
dev->polling = false;
- for (i = 0; i < n_targets; i++)
- targets[i].idx = dev->target_idx++;
-
spin_lock_bh(&dev->targets_lock);
dev->targets_generation++;
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 7650139..a47e90c 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -216,6 +216,39 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
&cmd);
}
+struct nci_rf_discover_select_param {
+ __u8 rf_discovery_id;
+ __u8 rf_protocol;
+};
+
+static void nci_rf_discover_select_req(struct nci_dev *ndev, unsigned long opt)
+{
+ struct nci_rf_discover_select_param *param =
+ (struct nci_rf_discover_select_param *)opt;
+ struct nci_rf_discover_select_cmd cmd;
+
+ cmd.rf_discovery_id = param->rf_discovery_id;
+ cmd.rf_protocol = param->rf_protocol;
+
+ switch (cmd.rf_protocol) {
+ case NCI_RF_PROTOCOL_ISO_DEP:
+ cmd.rf_interface = NCI_RF_INTERFACE_ISO_DEP;
+ break;
+
+ case NCI_RF_PROTOCOL_NFC_DEP:
+ cmd.rf_interface = NCI_RF_INTERFACE_NFC_DEP;
+ break;
+
+ default:
+ cmd.rf_interface = NCI_RF_INTERFACE_FRAME;
+ break;
+ }
+
+ nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_SELECT_CMD,
+ sizeof(struct nci_rf_discover_select_cmd),
+ &cmd);
+}
+
static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt)
{
struct nci_rf_deactivate_cmd cmd;
@@ -264,6 +297,8 @@ static int nci_open_device(struct nci_dev *ndev)
if (!rc) {
set_bit(NCI_UP, &ndev->flags);
+ nci_clear_target_list(ndev);
+ atomic_set(&ndev->state, NCI_IDLE);
} else {
/* Init failed, cleanup */
skb_queue_purge(&ndev->cmd_q);
@@ -286,6 +321,7 @@ static int nci_close_device(struct nci_dev *ndev)
if (!test_and_clear_bit(NCI_UP, &ndev->flags)) {
del_timer_sync(&ndev->cmd_timer);
+ del_timer_sync(&ndev->data_timer);
mutex_unlock(&ndev->req_lock);
return 0;
}
@@ -331,6 +367,15 @@ static void nci_cmd_timer(unsigned long arg)
queue_work(ndev->cmd_wq, &ndev->cmd_work);
}
+/* NCI data exchange timer function */
+static void nci_data_timer(unsigned long arg)
+{
+ struct nci_dev *ndev = (void *) arg;
+
+ set_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
+ queue_work(ndev->rx_wq, &ndev->rx_work);
+}
+
static int nci_dev_up(struct nfc_dev *nfc_dev)
{
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
@@ -350,7 +395,8 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols)
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
int rc;
- if (test_bit(NCI_DISCOVERY, &ndev->flags)) {
+ if ((atomic_read(&ndev->state) == NCI_DISCOVERY) ||
+ (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) {
pr_err("unable to start poll, since poll is already active\n");
return -EBUSY;
}
@@ -360,8 +406,9 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols)
return -EBUSY;
}
- if (test_bit(NCI_POLL_ACTIVE, &ndev->flags)) {
- pr_debug("target is active, implicitly deactivate...\n");
+ if ((atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) ||
+ (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) {
+ pr_debug("target active or w4 select, implicitly deactivate\n");
rc = nci_request(ndev, nci_rf_deactivate_req, 0,
msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
@@ -382,7 +429,8 @@ static void nci_stop_poll(struct nfc_dev *nfc_dev)
{
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
- if (!test_bit(NCI_DISCOVERY, &ndev->flags)) {
+ if ((atomic_read(&ndev->state) != NCI_DISCOVERY) &&
+ (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) {
pr_err("unable to stop poll, since poll is not active\n");
return;
}
@@ -395,10 +443,15 @@ static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx,
__u32 protocol)
{
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+ struct nci_rf_discover_select_param param;
+ struct nfc_target *target = NULL;
+ int i;
+ int rc = 0;
pr_debug("target_idx %d, protocol 0x%x\n", target_idx, protocol);
- if (!test_bit(NCI_POLL_ACTIVE, &ndev->flags)) {
+ if ((atomic_read(&ndev->state) != NCI_W4_HOST_SELECT) &&
+ (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) {
pr_err("there is no available target to activate\n");
return -EINVAL;
}
@@ -408,16 +461,47 @@ static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx,
return -EBUSY;
}
- if (!(ndev->target_available_prots & (1 << protocol))) {
+ for (i = 0; i < ndev->n_targets; i++) {
+ if (ndev->targets[i].idx == target_idx) {
+ target = &ndev->targets[i];
+ break;
+ }
+ }
+
+ if (!target) {
+ pr_err("unable to find the selected target\n");
+ return -EINVAL;
+ }
+
+ if (!(target->supported_protocols & (1 << protocol))) {
pr_err("target does not support the requested protocol 0x%x\n",
protocol);
return -EINVAL;
}
- ndev->target_active_prot = protocol;
- ndev->target_available_prots = 0;
+ if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) {
+ param.rf_discovery_id = target->idx;
- return 0;
+ if (protocol == NFC_PROTO_JEWEL)
+ param.rf_protocol = NCI_RF_PROTOCOL_T1T;
+ else if (protocol == NFC_PROTO_MIFARE)
+ param.rf_protocol = NCI_RF_PROTOCOL_T2T;
+ else if (protocol == NFC_PROTO_FELICA)
+ param.rf_protocol = NCI_RF_PROTOCOL_T3T;
+ else if (protocol == NFC_PROTO_ISO14443)
+ param.rf_protocol = NCI_RF_PROTOCOL_ISO_DEP;
+ else
+ param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP;
+
+ rc = nci_request(ndev, nci_rf_discover_select_req,
+ (unsigned long)&param,
+ msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT));
+ }
+
+ if (!rc)
+ ndev->target_active_prot = protocol;
+
+ return rc;
}
static void nci_deactivate_target(struct nfc_dev *nfc_dev, __u32 target_idx)
@@ -433,7 +517,7 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev, __u32 target_idx)
ndev->target_active_prot = 0;
- if (test_bit(NCI_POLL_ACTIVE, &ndev->flags)) {
+ if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) {
nci_request(ndev, nci_rf_deactivate_req, 0,
msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
}
@@ -585,6 +669,8 @@ int nci_register_device(struct nci_dev *ndev)
setup_timer(&ndev->cmd_timer, nci_cmd_timer,
(unsigned long) ndev);
+ setup_timer(&ndev->data_timer, nci_data_timer,
+ (unsigned long) ndev);
mutex_init(&ndev->req_lock);
@@ -722,6 +808,9 @@ static void nci_tx_work(struct work_struct *work)
nci_plen(skb->data));
nci_send_frame(skb);
+
+ mod_timer(&ndev->data_timer,
+ jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT));
}
}
@@ -753,6 +842,15 @@ static void nci_rx_work(struct work_struct *work)
break;
}
}
+
+ /* check if a data exchange timout has occurred */
+ if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) {
+ /* complete the data exchange transaction, if exists */
+ if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
+ nci_data_exchange_complete(ndev, NULL, -ETIMEDOUT);
+
+ clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
+ }
}
/* ----- NCI TX CMD worker thread ----- */
diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c
index e5756b3..7880ae9 100644
--- a/net/nfc/nci/data.c
+++ b/net/nfc/nci/data.c
@@ -44,6 +44,10 @@ void nci_data_exchange_complete(struct nci_dev *ndev,
pr_debug("len %d, err %d\n", skb ? skb->len : 0, err);
+ /* data exchange is complete, stop the data timer */
+ del_timer_sync(&ndev->data_timer);
+ clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
+
if (cb) {
ndev->data_exchange_cb = NULL;
ndev->data_exchange_cb_context = 0;
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index b16a8dc..03e7b46 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -71,6 +71,20 @@ static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev,
queue_work(ndev->tx_wq, &ndev->tx_work);
}
+static void nci_core_generic_error_ntf_packet(struct nci_dev *ndev,
+ struct sk_buff *skb)
+{
+ __u8 status = skb->data[0];
+
+ pr_debug("status 0x%x\n", status);
+
+ if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) {
+ /* Activation failed, so complete the request
+ (the state remains the same) */
+ nci_req_complete(ndev, status);
+ }
+}
+
static void nci_core_conn_intf_error_ntf_packet(struct nci_dev *ndev,
struct sk_buff *skb)
{
@@ -86,12 +100,9 @@ static void nci_core_conn_intf_error_ntf_packet(struct nci_dev *ndev,
}
static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev,
- struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
+ struct rf_tech_specific_params_nfca_poll *nfca_poll,
+ __u8 *data)
{
- struct rf_tech_specific_params_nfca_poll *nfca_poll;
-
- nfca_poll = &ntf->rf_tech_specific_params.nfca_poll;
-
nfca_poll->sens_res = __le16_to_cpu(*((__u16 *)data));
data += 2;
@@ -115,15 +126,213 @@ static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev,
return data;
}
+static __u8 *nci_extract_rf_params_nfcb_passive_poll(struct nci_dev *ndev,
+ struct rf_tech_specific_params_nfcb_poll *nfcb_poll,
+ __u8 *data)
+{
+ nfcb_poll->sensb_res_len = *data++;
+
+ pr_debug("sensb_res_len %d\n", nfcb_poll->sensb_res_len);
+
+ memcpy(nfcb_poll->sensb_res, data, nfcb_poll->sensb_res_len);
+ data += nfcb_poll->sensb_res_len;
+
+ return data;
+}
+
+static __u8 *nci_extract_rf_params_nfcf_passive_poll(struct nci_dev *ndev,
+ struct rf_tech_specific_params_nfcf_poll *nfcf_poll,
+ __u8 *data)
+{
+ nfcf_poll->bit_rate = *data++;
+ nfcf_poll->sensf_res_len = *data++;
+
+ pr_debug("bit_rate %d, sensf_res_len %d\n",
+ nfcf_poll->bit_rate, nfcf_poll->sensf_res_len);
+
+ memcpy(nfcf_poll->sensf_res, data, nfcf_poll->sensf_res_len);
+ data += nfcf_poll->sensf_res_len;
+
+ return data;
+}
+
+static int nci_add_new_protocol(struct nci_dev *ndev,
+ struct nfc_target *target,
+ __u8 rf_protocol,
+ __u8 rf_tech_and_mode,
+ void *params)
+{
+ struct rf_tech_specific_params_nfca_poll *nfca_poll;
+ struct rf_tech_specific_params_nfcb_poll *nfcb_poll;
+ struct rf_tech_specific_params_nfcf_poll *nfcf_poll;
+ __u32 protocol;
+
+ if (rf_protocol == NCI_RF_PROTOCOL_T2T)
+ protocol = NFC_PROTO_MIFARE_MASK;
+ else if (rf_protocol == NCI_RF_PROTOCOL_ISO_DEP)
+ protocol = NFC_PROTO_ISO14443_MASK;
+ else if (rf_protocol == NCI_RF_PROTOCOL_T3T)
+ protocol = NFC_PROTO_FELICA_MASK;
+ else
+ protocol = 0;
+
+ if (!(protocol & ndev->poll_prots)) {
+ pr_err("the target found does not have the desired protocol\n");
+ return -EPROTO;
+ }
+
+ if (rf_tech_and_mode == NCI_NFC_A_PASSIVE_POLL_MODE) {
+ nfca_poll = (struct rf_tech_specific_params_nfca_poll *)params;
+
+ target->sens_res = nfca_poll->sens_res;
+ target->sel_res = nfca_poll->sel_res;
+ target->nfcid1_len = nfca_poll->nfcid1_len;
+ if (target->nfcid1_len > 0) {
+ memcpy(target->nfcid1, nfca_poll->nfcid1,
+ target->nfcid1_len);
+ }
+ } else if (rf_tech_and_mode == NCI_NFC_B_PASSIVE_POLL_MODE) {
+ nfcb_poll = (struct rf_tech_specific_params_nfcb_poll *)params;
+
+ target->sensb_res_len = nfcb_poll->sensb_res_len;
+ if (target->sensb_res_len > 0) {
+ memcpy(target->sensb_res, nfcb_poll->sensb_res,
+ target->sensb_res_len);
+ }
+ } else if (rf_tech_and_mode == NCI_NFC_F_PASSIVE_POLL_MODE) {
+ nfcf_poll = (struct rf_tech_specific_params_nfcf_poll *)params;
+
+ target->sensf_res_len = nfcf_poll->sensf_res_len;
+ if (target->sensf_res_len > 0) {
+ memcpy(target->sensf_res, nfcf_poll->sensf_res,
+ target->sensf_res_len);
+ }
+ } else {
+ pr_err("unsupported rf_tech_and_mode 0x%x\n", rf_tech_and_mode);
+ return -EPROTO;
+ }
+
+ target->supported_protocols |= protocol;
+
+ pr_debug("protocol 0x%x\n", protocol);
+
+ return 0;
+}
+
+static void nci_add_new_target(struct nci_dev *ndev,
+ struct nci_rf_discover_ntf *ntf)
+{
+ struct nfc_target *target;
+ int i, rc;
+
+ for (i = 0; i < ndev->n_targets; i++) {
+ target = &ndev->targets[i];
+ if (target->idx == ntf->rf_discovery_id) {
+ /* This target already exists, add the new protocol */
+ nci_add_new_protocol(ndev, target, ntf->rf_protocol,
+ ntf->rf_tech_and_mode,
+ &ntf->rf_tech_specific_params);
+ return;
+ }
+ }
+
+ /* This is a new target, check if we've enough room */
+ if (ndev->n_targets == NCI_MAX_DISCOVERED_TARGETS) {
+ pr_debug("not enough room, ignoring new target...\n");
+ return;
+ }
+
+ target = &ndev->targets[ndev->n_targets];
+
+ rc = nci_add_new_protocol(ndev, target, ntf->rf_protocol,
+ ntf->rf_tech_and_mode,
+ &ntf->rf_tech_specific_params);
+ if (!rc) {
+ target->idx = ntf->rf_discovery_id;
+ ndev->n_targets++;
+
+ pr_debug("target_idx %d, n_targets %d\n", target->idx,
+ ndev->n_targets);
+ }
+}
+
+void nci_clear_target_list(struct nci_dev *ndev)
+{
+ memset(ndev->targets, 0,
+ (sizeof(struct nfc_target)*NCI_MAX_DISCOVERED_TARGETS));
+
+ ndev->n_targets = 0;
+}
+
+static void nci_rf_discover_ntf_packet(struct nci_dev *ndev,
+ struct sk_buff *skb)
+{
+ struct nci_rf_discover_ntf ntf;
+ __u8 *data = skb->data;
+ bool add_target = true;
+
+ ntf.rf_discovery_id = *data++;
+ ntf.rf_protocol = *data++;
+ ntf.rf_tech_and_mode = *data++;
+ ntf.rf_tech_specific_params_len = *data++;
+
+ pr_debug("rf_discovery_id %d\n", ntf.rf_discovery_id);
+ pr_debug("rf_protocol 0x%x\n", ntf.rf_protocol);
+ pr_debug("rf_tech_and_mode 0x%x\n", ntf.rf_tech_and_mode);
+ pr_debug("rf_tech_specific_params_len %d\n",
+ ntf.rf_tech_specific_params_len);
+
+ if (ntf.rf_tech_specific_params_len > 0) {
+ switch (ntf.rf_tech_and_mode) {
+ case NCI_NFC_A_PASSIVE_POLL_MODE:
+ data = nci_extract_rf_params_nfca_passive_poll(ndev,
+ &(ntf.rf_tech_specific_params.nfca_poll), data);
+ break;
+
+ case NCI_NFC_B_PASSIVE_POLL_MODE:
+ data = nci_extract_rf_params_nfcb_passive_poll(ndev,
+ &(ntf.rf_tech_specific_params.nfcb_poll), data);
+ break;
+
+ case NCI_NFC_F_PASSIVE_POLL_MODE:
+ data = nci_extract_rf_params_nfcf_passive_poll(ndev,
+ &(ntf.rf_tech_specific_params.nfcf_poll), data);
+ break;
+
+ default:
+ pr_err("unsupported rf_tech_and_mode 0x%x\n",
+ ntf.rf_tech_and_mode);
+ data += ntf.rf_tech_specific_params_len;
+ add_target = false;
+ }
+ }
+
+ ntf.ntf_type = *data++;
+ pr_debug("ntf_type %d\n", ntf.ntf_type);
+
+ if (add_target == true)
+ nci_add_new_target(ndev, &ntf);
+
+ if (ntf.ntf_type == NCI_DISCOVER_NTF_TYPE_MORE) {
+ atomic_set(&ndev->state, NCI_W4_ALL_DISCOVERIES);
+ } else {
+ atomic_set(&ndev->state, NCI_W4_HOST_SELECT);
+ nfc_targets_found(ndev->nfc_dev, ndev->targets,
+ ndev->n_targets);
+ }
+}
+
static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
{
struct activation_params_nfca_poll_iso_dep *nfca_poll;
+ struct activation_params_nfcb_poll_iso_dep *nfcb_poll;
switch (ntf->activation_rf_tech_and_mode) {
case NCI_NFC_A_PASSIVE_POLL_MODE:
nfca_poll = &ntf->activation_params.nfca_poll_iso_dep;
nfca_poll->rats_res_len = *data++;
+ pr_debug("rats_res_len %d\n", nfca_poll->rats_res_len);
if (nfca_poll->rats_res_len > 0) {
memcpy(nfca_poll->rats_res,
data,
@@ -131,52 +340,47 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
}
break;
+ case NCI_NFC_B_PASSIVE_POLL_MODE:
+ nfcb_poll = &ntf->activation_params.nfcb_poll_iso_dep;
+ nfcb_poll->attrib_res_len = *data++;
+ pr_debug("attrib_res_len %d\n",
+ nfcb_poll->attrib_res_len);
+ if (nfcb_poll->attrib_res_len > 0) {
+ memcpy(nfcb_poll->attrib_res,
+ data,
+ nfcb_poll->attrib_res_len);
+ }
+ break;
+
default:
pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
ntf->activation_rf_tech_and_mode);
- return -EPROTO;
+ return NCI_STATUS_RF_PROTOCOL_ERROR;
}
- return 0;
+ return NCI_STATUS_OK;
}
-static void nci_target_found(struct nci_dev *ndev,
- struct nci_rf_intf_activated_ntf *ntf)
+static void nci_target_auto_activated(struct nci_dev *ndev,
+ struct nci_rf_intf_activated_ntf *ntf)
{
- struct nfc_target nfc_tgt;
+ struct nfc_target *target;
+ int rc;
- if (ntf->rf_protocol == NCI_RF_PROTOCOL_T2T) /* T2T MifareUL */
- nfc_tgt.supported_protocols = NFC_PROTO_MIFARE_MASK;
- else if (ntf->rf_protocol == NCI_RF_PROTOCOL_ISO_DEP) /* 4A */
- nfc_tgt.supported_protocols = NFC_PROTO_ISO14443_MASK;
- else
- nfc_tgt.supported_protocols = 0;
-
- nfc_tgt.sens_res = ntf->rf_tech_specific_params.nfca_poll.sens_res;
- nfc_tgt.sel_res = ntf->rf_tech_specific_params.nfca_poll.sel_res;
- nfc_tgt.nfcid1_len = ntf->rf_tech_specific_params.nfca_poll.nfcid1_len;
- if (nfc_tgt.nfcid1_len > 0) {
- memcpy(nfc_tgt.nfcid1,
- ntf->rf_tech_specific_params.nfca_poll.nfcid1,
- nfc_tgt.nfcid1_len);
- }
+ target = &ndev->targets[ndev->n_targets];
- if (!(nfc_tgt.supported_protocols & ndev->poll_prots)) {
- pr_debug("the target found does not have the desired protocol\n");
+ rc = nci_add_new_protocol(ndev, target, ntf->rf_protocol,
+ ntf->activation_rf_tech_and_mode,
+ &ntf->rf_tech_specific_params);
+ if (rc)
return;
- }
- pr_debug("new target found, supported_protocols 0x%x\n",
- nfc_tgt.supported_protocols);
+ target->idx = ntf->rf_discovery_id;
+ ndev->n_targets++;
- ndev->target_available_prots = nfc_tgt.supported_protocols;
- ndev->max_data_pkt_payload_size = ntf->max_data_pkt_payload_size;
- ndev->initial_num_credits = ntf->initial_num_credits;
+ pr_debug("target_idx %d, n_targets %d\n", target->idx, ndev->n_targets);
- /* set the available credits to initial value */
- atomic_set(&ndev->credits_cnt, ndev->initial_num_credits);
-
- nfc_targets_found(ndev->nfc_dev, &nfc_tgt, 1);
+ nfc_targets_found(ndev->nfc_dev, ndev->targets, ndev->n_targets);
}
static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
@@ -184,10 +388,7 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
{
struct nci_rf_intf_activated_ntf ntf;
__u8 *data = skb->data;
- int err = 0;
-
- clear_bit(NCI_DISCOVERY, &ndev->flags);
- set_bit(NCI_POLL_ACTIVE, &ndev->flags);
+ int err = NCI_STATUS_OK;
ntf.rf_discovery_id = *data++;
ntf.rf_interface = *data++;
@@ -212,13 +413,24 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
switch (ntf.activation_rf_tech_and_mode) {
case NCI_NFC_A_PASSIVE_POLL_MODE:
data = nci_extract_rf_params_nfca_passive_poll(ndev,
- &ntf, data);
+ &(ntf.rf_tech_specific_params.nfca_poll), data);
+ break;
+
+ case NCI_NFC_B_PASSIVE_POLL_MODE:
+ data = nci_extract_rf_params_nfcb_passive_poll(ndev,
+ &(ntf.rf_tech_specific_params.nfcb_poll), data);
+ break;
+
+ case NCI_NFC_F_PASSIVE_POLL_MODE:
+ data = nci_extract_rf_params_nfcf_passive_poll(ndev,
+ &(ntf.rf_tech_specific_params.nfcf_poll), data);
break;
default:
pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
ntf.activation_rf_tech_and_mode);
- return;
+ err = NCI_STATUS_RF_PROTOCOL_ERROR;
+ goto exit;
}
}
@@ -250,12 +462,30 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
default:
pr_err("unsupported rf_interface 0x%x\n",
ntf.rf_interface);
- return;
+ err = NCI_STATUS_RF_PROTOCOL_ERROR;
+ break;
}
}
- if (!err)
- nci_target_found(ndev, &ntf);
+exit:
+ if (err == NCI_STATUS_OK) {
+ ndev->max_data_pkt_payload_size = ntf.max_data_pkt_payload_size;
+ ndev->initial_num_credits = ntf.initial_num_credits;
+
+ /* set the available credits to initial value */
+ atomic_set(&ndev->credits_cnt, ndev->initial_num_credits);
+ }
+
+ if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
+ /* A single target was found and activated automatically */
+ atomic_set(&ndev->state, NCI_POLL_ACTIVE);
+ if (err == NCI_STATUS_OK)
+ nci_target_auto_activated(ndev, &ntf);
+ } else { /* ndev->state == NCI_W4_HOST_SELECT */
+ /* A selected target was activated, so complete the request */
+ atomic_set(&ndev->state, NCI_POLL_ACTIVE);
+ nci_req_complete(ndev, err);
+ }
}
static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev,
@@ -265,9 +495,6 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev,
pr_debug("entry, type 0x%x, reason 0x%x\n", ntf->type, ntf->reason);
- clear_bit(NCI_POLL_ACTIVE, &ndev->flags);
- ndev->target_active_prot = 0;
-
/* drop tx data queue */
skb_queue_purge(&ndev->tx_q);
@@ -280,6 +507,10 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev,
/* complete the data exchange transaction, if exists */
if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
nci_data_exchange_complete(ndev, NULL, -EIO);
+
+ nci_clear_target_list(ndev);
+ atomic_set(&ndev->state, NCI_IDLE);
+ nci_req_complete(ndev, NCI_STATUS_OK);
}
void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
@@ -300,10 +531,18 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
nci_core_conn_credits_ntf_packet(ndev, skb);
break;
+ case NCI_OP_CORE_GENERIC_ERROR_NTF:
+ nci_core_generic_error_ntf_packet(ndev, skb);
+ break;
+
case NCI_OP_CORE_INTF_ERROR_NTF:
nci_core_conn_intf_error_ntf_packet(ndev, skb);
break;
+ case NCI_OP_RF_DISCOVER_NTF:
+ nci_rf_discover_ntf_packet(ndev, skb);
+ break;
+
case NCI_OP_RF_INTF_ACTIVATED_NTF:
nci_rf_intf_activated_ntf_packet(ndev, skb);
break;
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index 2840ae2..aa63b1e 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -137,11 +137,23 @@ static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
pr_debug("status 0x%x\n", status);
if (status == NCI_STATUS_OK)
- set_bit(NCI_DISCOVERY, &ndev->flags);
+ atomic_set(&ndev->state, NCI_DISCOVERY);
nci_req_complete(ndev, status);
}
+static void nci_rf_disc_select_rsp_packet(struct nci_dev *ndev,
+ struct sk_buff *skb)
+{
+ __u8 status = skb->data[0];
+
+ pr_debug("status 0x%x\n", status);
+
+ /* Complete the request on intf_activated_ntf or generic_error_ntf */
+ if (status != NCI_STATUS_OK)
+ nci_req_complete(ndev, status);
+}
+
static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev,
struct sk_buff *skb)
{
@@ -149,9 +161,13 @@ static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev,
pr_debug("status 0x%x\n", status);
- clear_bit(NCI_DISCOVERY, &ndev->flags);
-
- nci_req_complete(ndev, status);
+ /* If target was active, complete the request only in deactivate_ntf */
+ if ((status != NCI_STATUS_OK) ||
+ (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) {
+ nci_clear_target_list(ndev);
+ atomic_set(&ndev->state, NCI_IDLE);
+ nci_req_complete(ndev, status);
+ }
}
void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
@@ -187,6 +203,10 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
nci_rf_disc_rsp_packet(ndev, skb);
break;
+ case NCI_OP_RF_DISCOVER_SELECT_RSP:
+ nci_rf_disc_select_rsp_packet(ndev, skb);
+ break;
+
case NCI_OP_RF_DEACTIVATE_RSP:
nci_rf_deactivate_rsp_packet(ndev, skb);
break;
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index 6989dfa..07f0348 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -70,6 +70,12 @@ static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
if (target->nfcid1_len > 0)
NLA_PUT(msg, NFC_ATTR_TARGET_NFCID1, target->nfcid1_len,
target->nfcid1);
+ if (target->sensb_res_len > 0)
+ NLA_PUT(msg, NFC_ATTR_TARGET_SENSB_RES, target->sensb_res_len,
+ target->sensb_res);
+ if (target->sensf_res_len > 0)
+ NLA_PUT(msg, NFC_ATTR_TARGET_SENSF_RES, target->sensf_res_len,
+ target->sensf_res);
return genlmsg_end(msg, hdr);
diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c
index 2e2f8c6..5325439 100644
--- a/net/nfc/rawsock.c
+++ b/net/nfc/rawsock.c
@@ -92,18 +92,6 @@ static int rawsock_connect(struct socket *sock, struct sockaddr *_addr,
goto error;
}
- if (addr->target_idx > dev->target_idx - 1 ||
- addr->target_idx < dev->target_idx - dev->n_targets) {
- rc = -EINVAL;
- goto error;
- }
-
- if (addr->target_idx > dev->target_idx - 1 ||
- addr->target_idx < dev->target_idx - dev->n_targets) {
- rc = -EINVAL;
- goto error;
- }
-
rc = nfc_activate_target(dev, addr->target_idx, addr->nfc_protocol);
if (rc)
goto put_dev;
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 2590e91..75b58f8 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 dc5889c..8cdf4e2 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_plug.c b/net/sched/sch_plug.c
new file mode 100644
index 0000000..ba7b737
--- /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;
+}
+
+struct Qdisc_ops plug_qdisc_ops = {
+ .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/tipc/bcast.c b/net/tipc/bcast.c
index 8eb87b1..41ecf31 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
@@ -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++;
+ spin_unlock_bh(&bc_lock);
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)
- */
-
- 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
*/
@@ -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,85 +458,114 @@ 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);
} 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);
}
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:
diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h
index b009666..5571394 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/link.c b/net/tipc/link.c
index ac1832a..d8b0a22 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -1501,13 +1501,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);
@@ -1736,7 +1736,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;
@@ -1774,6 +1774,7 @@ protocol_check:
head = link_insert_deferred_queue(l_ptr,
head);
if (likely(msg_is_dest(msg, tipc_own_addr))) {
+ int ret;
deliver:
if (likely(msg_isdata(msg))) {
tipc_node_unlock(n_ptr);
@@ -1798,11 +1799,15 @@ deliver:
continue;
case MSG_FRAGMENTER:
l_ptr->stats.recv_fragments++;
- if (tipc_link_recv_fragment(&l_ptr->defragm_buf,
- &buf, &msg)) {
+ ret = tipc_link_recv_fragment(
+ &l_ptr->defragm_buf,
+ &buf, &msg);
+ if (ret == 1) {
l_ptr->stats.recv_fragmented++;
goto deliver;
}
+ if (ret == -1)
+ l_ptr->next_in_no--;
break;
case CHANGEOVER_PROTOCOL:
type = msg_type(msg);
@@ -1853,17 +1858,16 @@ cont:
}
/*
- * 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 +1885,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) {
+ buf_discard(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
*/
@@ -1956,6 +1959,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) {
+ buf_discard(l_ptr->proto_msg_queue);
+ l_ptr->proto_msg_queue = NULL;
+ }
+
if (link_blocked(l_ptr))
return;
@@ -1964,9 +1974,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 +2032,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;
+ buf_discard(buf);
}
/*
@@ -2105,6 +2109,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 +2133,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 +2189,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,
@@ -2623,7 +2637,9 @@ 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");
+ buf_discard(fbuf);
+ return -1;
}
buf_discard(fbuf);
return 0;
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index 98ebb37..acecfda 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -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)
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 6b226fa..7bc45e1 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -49,9 +49,8 @@ LIST_HEAD(tipc_node_list);
static u32 tipc_num_nodes;
static atomic_t tipc_num_links = ATOMIC_INIT(0);
-u32 tipc_own_tag;
-/**
+/*
* tipc_node_find - locate specified node object, if it exists
*/
@@ -306,10 +305,9 @@ static void node_established_contact(struct tipc_node *n_ptr)
/* Syncronize broadcast acks */
n_ptr->bclink.acked = tipc_bclink_get_last_sent();
- if (n_ptr->bclink.supported) {
+ if (n_ptr->bclink.supportable) {
tipc_bclink_add_node(n_ptr->addr);
- if (n_ptr->addr < tipc_own_addr)
- tipc_own_tag++;
+ n_ptr->bclink.supported = 1;
}
}
@@ -338,12 +336,12 @@ 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);
}
+ n_ptr->bclink.deferred_size = 0;
if (n_ptr->bclink.defragm) {
buf_discard(n_ptr->bclink.defragm);
@@ -352,8 +350,6 @@ static void node_lost_contact(struct tipc_node *n_ptr)
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;
}
@@ -449,7 +445,7 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space)
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 0b1c5f8..e1b78a2 100644
--- a/net/tipc/node.h
+++ b/net/tipc/node.h
@@ -62,12 +62,13 @@
* @link_cnt: number of links to node
* @permit_changeover: non-zero if node has redundant links to this system
* @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
@@ -86,12 +87,13 @@ struct tipc_node {
int block_setup;
int permit_changeover;
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;
@@ -112,8 +114,6 @@ 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 d91efc6..ba3268b 100644
--- a/net/tipc/port.c
+++ b/net/tipc/port.c
@@ -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 */
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 8c550df..9d3e3b6 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -55,6 +55,7 @@ const struct mesh_config default_mesh_config = {
.min_discovery_timeout = MESH_MIN_DISCOVERY_TIMEOUT,
.dot11MeshHWMPRannInterval = MESH_RANN_INTERVAL,
.dot11MeshGateAnnouncementProtocol = false,
+ .dot11MeshForwarding = true,
};
const struct mesh_setup default_mesh_setup = {
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index afeea32..c910b07 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -427,10 +427,9 @@ static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
if (tb[NL80211_KEY_DEFAULT_TYPES]) {
struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
- int err = nla_parse_nested(kdt,
- NUM_NL80211_KEY_DEFAULT_TYPES - 1,
- tb[NL80211_KEY_DEFAULT_TYPES],
- nl80211_key_default_policy);
+ err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
+ tb[NL80211_KEY_DEFAULT_TYPES],
+ nl80211_key_default_policy);
if (err)
return err;
@@ -3259,6 +3258,8 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
cur_params.dot11MeshHWMPRannInterval);
NLA_PUT_U8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
cur_params.dot11MeshGateAnnouncementProtocol);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_FORWARDING,
+ cur_params.dot11MeshForwarding);
nla_nest_end(msg, pinfoattr);
genlmsg_end(msg, hdr);
return genlmsg_reply(msg, info);
@@ -3290,6 +3291,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
[NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 },
[NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 },
[NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 },
};
static const struct nla_policy
@@ -3379,6 +3381,8 @@ do {\
dot11MeshGateAnnouncementProtocol, mask,
NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding,
+ mask, NL80211_MESHCONF_FORWARDING, nla_get_u8);
if (mask_out)
*mask_out = mask;
@@ -4781,7 +4785,6 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
struct ieee80211_supported_band *sband =
wiphy->bands[ibss.channel->band];
- int err;
err = ieee80211_get_ratemask(sband, rates, n_rates,
&ibss.basic_rates);
@@ -5390,9 +5393,39 @@ static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
return mask;
}
+static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
+ u8 *rates, u8 rates_len,
+ u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
+{
+ u8 i;
+
+ memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
+
+ for (i = 0; i < rates_len; i++) {
+ int ridx, rbit;
+
+ ridx = rates[i] / 8;
+ rbit = BIT(rates[i] % 8);
+
+ /* check validity */
+ if ((ridx < 0) || (ridx > IEEE80211_HT_MCS_MASK_LEN))
+ return false;
+
+ /* check availability */
+ if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
+ mcs[ridx] |= rbit;
+ else
+ return false;
+ }
+
+ return true;
+}
+
static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
[NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
.len = NL80211_MAX_SUPP_RATES },
+ [NL80211_TXRATE_MCS] = { .type = NLA_BINARY,
+ .len = NL80211_MAX_SUPP_HT_RATES },
};
static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
@@ -5418,12 +5451,20 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
sband = rdev->wiphy.bands[i];
mask.control[i].legacy =
sband ? (1 << sband->n_bitrates) - 1 : 0;
+ if (sband)
+ memcpy(mask.control[i].mcs,
+ sband->ht_cap.mcs.rx_mask,
+ sizeof(mask.control[i].mcs));
+ else
+ memset(mask.control[i].mcs, 0,
+ sizeof(mask.control[i].mcs));
}
/*
* The nested attribute uses enum nl80211_band as the index. This maps
* directly to the enum ieee80211_band values used in cfg80211.
*/
+ BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
{
enum ieee80211_band band = nla_type(tx_rates);
@@ -5439,7 +5480,28 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
sband,
nla_data(tb[NL80211_TXRATE_LEGACY]),
nla_len(tb[NL80211_TXRATE_LEGACY]));
- if (mask.control[band].legacy == 0)
+ }
+ if (tb[NL80211_TXRATE_MCS]) {
+ if (!ht_rateset_to_mask(
+ sband,
+ nla_data(tb[NL80211_TXRATE_MCS]),
+ nla_len(tb[NL80211_TXRATE_MCS]),
+ mask.control[band].mcs))
+ return -EINVAL;
+ }
+
+ if (mask.control[band].legacy == 0) {
+ /* don't allow empty legacy rates if HT
+ * is not even supported. */
+ if (!rdev->wiphy.bands[band]->ht_cap.ht_supported)
+ return -EINVAL;
+
+ for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+ if (mask.control[band].mcs[i])
+ break;
+
+ /* legacy and mcs rates may not be both empty */
+ if (i == IEEE80211_HT_MCS_MASK_LEN)
return -EINVAL;
}
}
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index f65feaa..e9a0ac8 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -882,23 +882,8 @@ static void handle_channel(struct wiphy *wiphy,
chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
chan->max_antenna_gain = min(chan->orig_mag,
(int) MBI_TO_DBI(power_rule->max_antenna_gain));
- if (chan->orig_mpwr) {
- /*
- * Devices that have their own custom regulatory domain
- * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the
- * passed country IE power settings.
- */
- if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
- wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
- chan->max_power =
- MBM_TO_DBM(power_rule->max_eirp);
- } else {
- chan->max_power = min(chan->orig_mpwr,
- (int) MBM_TO_DBM(power_rule->max_eirp));
- }
- } else
- chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp);
+ chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
+ chan->max_power = min(chan->max_power, chan->max_reg_power);
}
static void handle_band(struct wiphy *wiphy,