Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux

This commit is contained in:
David S. Miller
2016-03-19 21:05:24 -04:00
6527 changed files with 314251 additions and 168833 deletions

View File

@@ -20,7 +20,7 @@
int lowpan_register_netdevice(struct net_device *dev,
enum lowpan_lltypes lltype)
{
int ret;
int i, ret;
dev->addr_len = EUI64_ADDR_LEN;
dev->type = ARPHRD_6LOWPAN;
@@ -29,6 +29,10 @@ int lowpan_register_netdevice(struct net_device *dev,
lowpan_priv(dev)->lltype = lltype;
spin_lock_init(&lowpan_priv(dev)->ctx.lock);
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
lowpan_priv(dev)->ctx.table[i].id = i;
ret = register_netdevice(dev);
if (ret < 0)
return ret;
@@ -68,6 +72,32 @@ void lowpan_unregister_netdev(struct net_device *dev)
}
EXPORT_SYMBOL(lowpan_unregister_netdev);
static int lowpan_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
int i;
if (dev->type != ARPHRD_6LOWPAN)
return NOTIFY_DONE;
switch (event) {
case NETDEV_DOWN:
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
&lowpan_priv(dev)->ctx.table[i].flags);
break;
default:
return NOTIFY_DONE;
}
return NOTIFY_OK;
}
static struct notifier_block lowpan_notifier = {
.notifier_call = lowpan_event,
};
static int __init lowpan_module_init(void)
{
int ret;
@@ -76,6 +106,12 @@ static int __init lowpan_module_init(void)
if (ret < 0)
return ret;
ret = register_netdevice_notifier(&lowpan_notifier);
if (ret < 0) {
lowpan_debugfs_exit();
return ret;
}
request_module_nowait("ipv6");
request_module_nowait("nhc_dest");
@@ -92,6 +128,7 @@ static int __init lowpan_module_init(void)
static void __exit lowpan_module_exit(void)
{
lowpan_debugfs_exit();
unregister_netdevice_notifier(&lowpan_notifier);
}
module_init(lowpan_module_init);

View File

@@ -16,19 +16,266 @@
#include "6lowpan_i.h"
#define LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS 8
static struct dentry *lowpan_debugfs;
static int lowpan_ctx_flag_active_set(void *data, u64 val)
{
struct lowpan_iphc_ctx *ctx = data;
if (val != 0 && val != 1)
return -EINVAL;
if (val)
set_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
else
clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
return 0;
}
static int lowpan_ctx_flag_active_get(void *data, u64 *val)
{
*val = lowpan_iphc_ctx_is_active(data);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_active_fops,
lowpan_ctx_flag_active_get,
lowpan_ctx_flag_active_set, "%llu\n");
static int lowpan_ctx_flag_c_set(void *data, u64 val)
{
struct lowpan_iphc_ctx *ctx = data;
if (val != 0 && val != 1)
return -EINVAL;
if (val)
set_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags);
else
clear_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags);
return 0;
}
static int lowpan_ctx_flag_c_get(void *data, u64 *val)
{
*val = lowpan_iphc_ctx_is_compression(data);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_c_fops, lowpan_ctx_flag_c_get,
lowpan_ctx_flag_c_set, "%llu\n");
static int lowpan_ctx_plen_set(void *data, u64 val)
{
struct lowpan_iphc_ctx *ctx = data;
struct lowpan_iphc_ctx_table *t =
container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
if (val > 128)
return -EINVAL;
spin_lock_bh(&t->lock);
ctx->plen = val;
spin_unlock_bh(&t->lock);
return 0;
}
static int lowpan_ctx_plen_get(void *data, u64 *val)
{
struct lowpan_iphc_ctx *ctx = data;
struct lowpan_iphc_ctx_table *t =
container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
spin_lock_bh(&t->lock);
*val = ctx->plen;
spin_unlock_bh(&t->lock);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_plen_fops, lowpan_ctx_plen_get,
lowpan_ctx_plen_set, "%llu\n");
static int lowpan_ctx_pfx_show(struct seq_file *file, void *offset)
{
struct lowpan_iphc_ctx *ctx = file->private;
struct lowpan_iphc_ctx_table *t =
container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
spin_lock_bh(&t->lock);
seq_printf(file, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
be16_to_cpu(ctx->pfx.s6_addr16[0]),
be16_to_cpu(ctx->pfx.s6_addr16[1]),
be16_to_cpu(ctx->pfx.s6_addr16[2]),
be16_to_cpu(ctx->pfx.s6_addr16[3]),
be16_to_cpu(ctx->pfx.s6_addr16[4]),
be16_to_cpu(ctx->pfx.s6_addr16[5]),
be16_to_cpu(ctx->pfx.s6_addr16[6]),
be16_to_cpu(ctx->pfx.s6_addr16[7]));
spin_unlock_bh(&t->lock);
return 0;
}
static int lowpan_ctx_pfx_open(struct inode *inode, struct file *file)
{
return single_open(file, lowpan_ctx_pfx_show, inode->i_private);
}
static ssize_t lowpan_ctx_pfx_write(struct file *fp,
const char __user *user_buf, size_t count,
loff_t *ppos)
{
char buf[128] = {};
struct seq_file *file = fp->private_data;
struct lowpan_iphc_ctx *ctx = file->private;
struct lowpan_iphc_ctx_table *t =
container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
int status = count, n, i;
unsigned int addr[8];
if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1,
count))) {
status = -EFAULT;
goto out;
}
n = sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
&addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
&addr[5], &addr[6], &addr[7]);
if (n != LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS) {
status = -EINVAL;
goto out;
}
spin_lock_bh(&t->lock);
for (i = 0; i < 8; i++)
ctx->pfx.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
spin_unlock_bh(&t->lock);
out:
return status;
}
static const struct file_operations lowpan_ctx_pfx_fops = {
.open = lowpan_ctx_pfx_open,
.read = seq_read,
.write = lowpan_ctx_pfx_write,
.llseek = seq_lseek,
.release = single_release,
};
static int lowpan_dev_debugfs_ctx_init(struct net_device *dev,
struct dentry *ctx, u8 id)
{
struct lowpan_priv *lpriv = lowpan_priv(dev);
struct dentry *dentry, *root;
char buf[32];
WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE);
sprintf(buf, "%d", id);
root = debugfs_create_dir(buf, ctx);
if (!root)
return -EINVAL;
dentry = debugfs_create_file("active", 0644, root,
&lpriv->ctx.table[id],
&lowpan_ctx_flag_active_fops);
if (!dentry)
return -EINVAL;
dentry = debugfs_create_file("compression", 0644, root,
&lpriv->ctx.table[id],
&lowpan_ctx_flag_c_fops);
if (!dentry)
return -EINVAL;
dentry = debugfs_create_file("prefix", 0644, root,
&lpriv->ctx.table[id],
&lowpan_ctx_pfx_fops);
if (!dentry)
return -EINVAL;
dentry = debugfs_create_file("prefix_len", 0644, root,
&lpriv->ctx.table[id],
&lowpan_ctx_plen_fops);
if (!dentry)
return -EINVAL;
return 0;
}
static int lowpan_context_show(struct seq_file *file, void *offset)
{
struct lowpan_iphc_ctx_table *t = file->private;
int i;
seq_printf(file, "%3s|%-43s|%c\n", "cid", "prefix", 'C');
seq_puts(file, "-------------------------------------------------\n");
spin_lock_bh(&t->lock);
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
if (!lowpan_iphc_ctx_is_active(&t->table[i]))
continue;
seq_printf(file, "%3d|%39pI6c/%-3d|%d\n", t->table[i].id,
&t->table[i].pfx, t->table[i].plen,
lowpan_iphc_ctx_is_compression(&t->table[i]));
}
spin_unlock_bh(&t->lock);
return 0;
}
static int lowpan_context_open(struct inode *inode, struct file *file)
{
return single_open(file, lowpan_context_show, inode->i_private);
}
static const struct file_operations lowpan_context_fops = {
.open = lowpan_context_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
int lowpan_dev_debugfs_init(struct net_device *dev)
{
struct lowpan_priv *lpriv = lowpan_priv(dev);
struct dentry *contexts, *dentry;
int ret, i;
/* creating the root */
lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
if (!lpriv->iface_debugfs)
goto fail;
contexts = debugfs_create_dir("contexts", lpriv->iface_debugfs);
if (!contexts)
goto remove_root;
dentry = debugfs_create_file("show", 0644, contexts,
&lowpan_priv(dev)->ctx,
&lowpan_context_fops);
if (!dentry)
goto remove_root;
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
ret = lowpan_dev_debugfs_ctx_init(dev, contexts, i);
if (ret < 0)
goto remove_root;
}
return 0;
remove_root:
lowpan_dev_debugfs_exit(dev);
fail:
return -EINVAL;
}

View File

@@ -56,6 +56,7 @@
/* special link-layer handling */
#include <net/mac802154.h>
#include "6lowpan_i.h"
#include "nhc.h"
/* Values of fields within the IPHC encoding first byte */
@@ -147,6 +148,9 @@
(((a)->s6_addr16[6]) == 0) && \
(((a)->s6_addr[14]) == 0))
#define LOWPAN_IPHC_CID_DCI(cid) (cid & 0x0f)
#define LOWPAN_IPHC_CID_SCI(cid) ((cid & 0xf0) >> 4)
static inline void iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr,
const void *lladdr)
{
@@ -195,6 +199,98 @@ static inline void iphc_uncompress_802154_lladdr(struct in6_addr *ipaddr,
}
}
static struct lowpan_iphc_ctx *
lowpan_iphc_ctx_get_by_id(const struct net_device *dev, u8 id)
{
struct lowpan_iphc_ctx *ret = &lowpan_priv(dev)->ctx.table[id];
if (!lowpan_iphc_ctx_is_active(ret))
return NULL;
return ret;
}
static struct lowpan_iphc_ctx *
lowpan_iphc_ctx_get_by_addr(const struct net_device *dev,
const struct in6_addr *addr)
{
struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
struct lowpan_iphc_ctx *ret = NULL;
struct in6_addr addr_pfx;
u8 addr_plen;
int i;
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
/* Check if context is valid. A context that is not valid
* MUST NOT be used for compression.
*/
if (!lowpan_iphc_ctx_is_active(&table[i]) ||
!lowpan_iphc_ctx_is_compression(&table[i]))
continue;
ipv6_addr_prefix(&addr_pfx, addr, table[i].plen);
/* if prefix len < 64, the remaining bits until 64th bit is
* zero. Otherwise we use table[i]->plen.
*/
if (table[i].plen < 64)
addr_plen = 64;
else
addr_plen = table[i].plen;
if (ipv6_prefix_equal(&addr_pfx, &table[i].pfx, addr_plen)) {
/* remember first match */
if (!ret) {
ret = &table[i];
continue;
}
/* get the context with longest prefix len */
if (table[i].plen > ret->plen)
ret = &table[i];
}
}
return ret;
}
static struct lowpan_iphc_ctx *
lowpan_iphc_ctx_get_by_mcast_addr(const struct net_device *dev,
const struct in6_addr *addr)
{
struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
struct lowpan_iphc_ctx *ret = NULL;
struct in6_addr addr_mcast, network_pfx = {};
int i;
/* init mcast address with */
memcpy(&addr_mcast, addr, sizeof(*addr));
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
/* Check if context is valid. A context that is not valid
* MUST NOT be used for compression.
*/
if (!lowpan_iphc_ctx_is_active(&table[i]) ||
!lowpan_iphc_ctx_is_compression(&table[i]))
continue;
/* setting plen */
addr_mcast.s6_addr[3] = table[i].plen;
/* get network prefix to copy into multicast address */
ipv6_addr_prefix(&network_pfx, &table[i].pfx,
table[i].plen);
/* setting network prefix */
memcpy(&addr_mcast.s6_addr[4], &network_pfx, 8);
if (ipv6_addr_equal(addr, &addr_mcast)) {
ret = &table[i];
break;
}
}
return ret;
}
/* Uncompress address function for source and
* destination address(non-multicast).
*
@@ -259,30 +355,59 @@ static int uncompress_addr(struct sk_buff *skb, const struct net_device *dev,
/* Uncompress address function for source context
* based address(non-multicast).
*/
static int uncompress_context_based_src_addr(struct sk_buff *skb,
struct in6_addr *ipaddr,
u8 address_mode)
static int uncompress_ctx_addr(struct sk_buff *skb,
const struct net_device *dev,
const struct lowpan_iphc_ctx *ctx,
struct in6_addr *ipaddr, u8 address_mode,
const void *lladdr)
{
bool fail;
switch (address_mode) {
case LOWPAN_IPHC_SAM_00:
/* unspec address ::
/* SAM and DAM are the same here */
case LOWPAN_IPHC_DAM_00:
fail = false;
/* SAM_00 -> unspec address ::
* Do nothing, address is already ::
*
* DAM 00 -> reserved should never occur.
*/
break;
case LOWPAN_IPHC_SAM_01:
/* TODO */
case LOWPAN_IPHC_DAM_01:
fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
break;
case LOWPAN_IPHC_SAM_10:
/* TODO */
case LOWPAN_IPHC_DAM_10:
ipaddr->s6_addr[11] = 0xFF;
ipaddr->s6_addr[12] = 0xFE;
fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
break;
case LOWPAN_IPHC_SAM_11:
/* TODO */
netdev_warn(skb->dev, "SAM value 0x%x not supported\n",
address_mode);
return -EINVAL;
case LOWPAN_IPHC_DAM_11:
fail = false;
switch (lowpan_priv(dev)->lltype) {
case LOWPAN_LLTYPE_IEEE802154:
iphc_uncompress_802154_lladdr(ipaddr, lladdr);
break;
default:
iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
break;
}
ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
break;
default:
pr_debug("Invalid sam value: 0x%x\n", address_mode);
return -EINVAL;
}
if (fail) {
pr_debug("Failed to fetch skb data\n");
return -EIO;
}
raw_dump_inline(NULL,
"Reconstructed context based ipv6 src addr is",
ipaddr->s6_addr, 16);
@@ -346,6 +471,30 @@ static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
return 0;
}
static int lowpan_uncompress_multicast_ctx_daddr(struct sk_buff *skb,
struct lowpan_iphc_ctx *ctx,
struct in6_addr *ipaddr,
u8 address_mode)
{
struct in6_addr network_pfx = {};
bool fail;
ipaddr->s6_addr[0] = 0xFF;
fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 2);
fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[12], 4);
if (fail)
return -EIO;
/* take prefix_len and network prefix from the context */
ipaddr->s6_addr[3] = ctx->plen;
/* get network prefix to copy into multicast address */
ipv6_addr_prefix(&network_pfx, &ctx->pfx, ctx->plen);
/* setting network prefix */
memcpy(&ipaddr->s6_addr[4], &network_pfx, 8);
return 0;
}
/* get the ecn values from iphc tf format and set it to ipv6hdr */
static inline void lowpan_iphc_tf_set_ecn(struct ipv6hdr *hdr, const u8 *tf)
{
@@ -459,7 +608,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
const void *daddr, const void *saddr)
{
struct ipv6hdr hdr = {};
u8 iphc0, iphc1;
struct lowpan_iphc_ctx *ci;
u8 iphc0, iphc1, cid = 0;
int err;
raw_dump_table(__func__, "raw skb data dump uncompressed",
@@ -469,12 +619,14 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
lowpan_fetch_skb(skb, &iphc1, sizeof(iphc1)))
return -EINVAL;
/* another if the CID flag is set */
if (iphc1 & LOWPAN_IPHC_CID)
return -ENOTSUPP;
hdr.version = 6;
/* default CID = 0, another if the CID flag is set */
if (iphc1 & LOWPAN_IPHC_CID) {
if (lowpan_fetch_skb(skb, &cid, sizeof(cid)))
return -EINVAL;
}
err = lowpan_iphc_tf_decompress(skb, &hdr,
iphc0 & LOWPAN_IPHC_TF_MASK);
if (err < 0)
@@ -500,10 +652,17 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
}
if (iphc1 & LOWPAN_IPHC_SAC) {
/* Source address context based uncompression */
spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_SCI(cid));
if (!ci) {
spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
return -EINVAL;
}
pr_debug("SAC bit is set. Handle context based source address.\n");
err = uncompress_context_based_src_addr(skb, &hdr.saddr,
iphc1 & LOWPAN_IPHC_SAM_MASK);
err = uncompress_ctx_addr(skb, dev, ci, &hdr.saddr,
iphc1 & LOWPAN_IPHC_SAM_MASK, saddr);
spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
} else {
/* Source address uncompression */
pr_debug("source address stateless compression\n");
@@ -515,27 +674,52 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
if (err)
return -EINVAL;
/* check for Multicast Compression */
if (iphc1 & LOWPAN_IPHC_M) {
if (iphc1 & LOWPAN_IPHC_DAC) {
pr_debug("dest: context-based mcast compression\n");
/* TODO: implement this */
} else {
err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
iphc1 & LOWPAN_IPHC_DAM_MASK);
if (err)
return -EINVAL;
switch (iphc1 & (LOWPAN_IPHC_M | LOWPAN_IPHC_DAC)) {
case LOWPAN_IPHC_M | LOWPAN_IPHC_DAC:
spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
if (!ci) {
spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
return -EINVAL;
}
} else {
/* multicast with context */
pr_debug("dest: context-based mcast compression\n");
err = lowpan_uncompress_multicast_ctx_daddr(skb, ci,
&hdr.daddr,
iphc1 & LOWPAN_IPHC_DAM_MASK);
spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
break;
case LOWPAN_IPHC_M:
/* multicast */
err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
iphc1 & LOWPAN_IPHC_DAM_MASK);
break;
case LOWPAN_IPHC_DAC:
spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
if (!ci) {
spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
return -EINVAL;
}
/* Destination address context based uncompression */
pr_debug("DAC bit is set. Handle context based destination address.\n");
err = uncompress_ctx_addr(skb, dev, ci, &hdr.daddr,
iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
break;
default:
err = uncompress_addr(skb, dev, &hdr.daddr,
iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
pr_debug("dest: stateless compression mode %d dest %pI6c\n",
iphc1 & LOWPAN_IPHC_DAM_MASK, &hdr.daddr);
if (err)
return -EINVAL;
break;
}
if (err)
return -EINVAL;
/* Next header data uncompression */
if (iphc0 & LOWPAN_IPHC_NH) {
err = lowpan_nhc_do_uncompression(skb, dev, &hdr);
@@ -585,6 +769,58 @@ static const u8 lowpan_iphc_dam_to_sam_value[] = {
[LOWPAN_IPHC_DAM_11] = LOWPAN_IPHC_SAM_11,
};
static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct in6_addr *ipaddr,
const struct lowpan_iphc_ctx *ctx,
const unsigned char *lladdr, bool sam)
{
struct in6_addr tmp = {};
u8 dam;
/* check for SAM/DAM = 11 */
memcpy(&tmp.s6_addr[8], lladdr, 8);
/* second bit-flip (Universe/Local) is done according RFC2464 */
tmp.s6_addr[8] ^= 0x02;
/* context information are always used */
ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
if (ipv6_addr_equal(&tmp, ipaddr)) {
dam = LOWPAN_IPHC_DAM_11;
goto out;
}
memset(&tmp, 0, sizeof(tmp));
/* check for SAM/DAM = 10 */
tmp.s6_addr[11] = 0xFF;
tmp.s6_addr[12] = 0xFE;
memcpy(&tmp.s6_addr[14], &ipaddr->s6_addr[14], 2);
/* context information are always used */
ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
if (ipv6_addr_equal(&tmp, ipaddr)) {
lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[14], 2);
dam = LOWPAN_IPHC_DAM_10;
goto out;
}
memset(&tmp, 0, sizeof(tmp));
/* check for SAM/DAM = 01, should always match */
memcpy(&tmp.s6_addr[8], &ipaddr->s6_addr[8], 8);
/* context information are always used */
ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
if (ipv6_addr_equal(&tmp, ipaddr)) {
lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[8], 8);
dam = LOWPAN_IPHC_DAM_01;
goto out;
}
WARN_ONCE(1, "context found but no address mode matched\n");
return LOWPAN_IPHC_DAM_00;
out:
if (sam)
return lowpan_iphc_dam_to_sam_value[dam];
else
return dam;
}
static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct in6_addr *ipaddr,
const unsigned char *lladdr, bool sam)
{
@@ -708,6 +944,21 @@ static u8 lowpan_iphc_tf_compress(u8 **hc_ptr, const struct ipv6hdr *hdr)
return val;
}
static u8 lowpan_iphc_mcast_ctx_addr_compress(u8 **hc_ptr,
const struct lowpan_iphc_ctx *ctx,
const struct in6_addr *ipaddr)
{
u8 data[6];
/* flags/scope, reserved (RIID) */
memcpy(data, &ipaddr->s6_addr[1], 2);
/* group ID */
memcpy(&data[1], &ipaddr->s6_addr[11], 4);
lowpan_push_hc_data(hc_ptr, data, 6);
return LOWPAN_IPHC_DAM_00;
}
static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
const struct in6_addr *ipaddr)
{
@@ -742,10 +993,11 @@ static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
const void *daddr, const void *saddr)
{
u8 iphc0, iphc1, *hc_ptr;
u8 iphc0, iphc1, *hc_ptr, cid = 0;
struct ipv6hdr *hdr;
u8 head[LOWPAN_IPHC_MAX_HC_BUF_LEN] = {};
int ret, addr_type;
struct lowpan_iphc_ctx *dci, *sci, dci_entry, sci_entry;
int ret, ipv6_daddr_type, ipv6_saddr_type;
if (skb->protocol != htons(ETH_P_IPV6))
return -EINVAL;
@@ -769,14 +1021,38 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
iphc0 = LOWPAN_DISPATCH_IPHC;
iphc1 = 0;
/* TODO: context lookup */
raw_dump_inline(__func__, "saddr", saddr, EUI64_ADDR_LEN);
raw_dump_inline(__func__, "daddr", daddr, EUI64_ADDR_LEN);
raw_dump_table(__func__, "sending raw skb network uncompressed packet",
skb->data, skb->len);
ipv6_daddr_type = ipv6_addr_type(&hdr->daddr);
spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
if (ipv6_daddr_type & IPV6_ADDR_MULTICAST)
dci = lowpan_iphc_ctx_get_by_mcast_addr(dev, &hdr->daddr);
else
dci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->daddr);
if (dci) {
memcpy(&dci_entry, dci, sizeof(*dci));
cid |= dci->id;
}
spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
sci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->saddr);
if (sci) {
memcpy(&sci_entry, sci, sizeof(*sci));
cid |= (sci->id << 4);
}
spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
/* if cid is zero it will be compressed */
if (cid) {
iphc1 |= LOWPAN_IPHC_CID;
lowpan_push_hc_data(&hc_ptr, &cid, sizeof(cid));
}
/* Traffic Class, Flow Label compression */
iphc0 |= lowpan_iphc_tf_compress(&hc_ptr, hdr);
@@ -813,39 +1089,64 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
sizeof(hdr->hop_limit));
}
addr_type = ipv6_addr_type(&hdr->saddr);
ipv6_saddr_type = ipv6_addr_type(&hdr->saddr);
/* source address compression */
if (addr_type == IPV6_ADDR_ANY) {
if (ipv6_saddr_type == IPV6_ADDR_ANY) {
pr_debug("source address is unspecified, setting SAC\n");
iphc1 |= LOWPAN_IPHC_SAC;
} else {
if (addr_type & IPV6_ADDR_LINKLOCAL) {
iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->saddr,
saddr, true);
pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
&hdr->saddr, iphc1);
if (sci) {
iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->saddr,
&sci_entry, saddr,
true);
iphc1 |= LOWPAN_IPHC_SAC;
} else {
pr_debug("send the full source address\n");
lowpan_push_hc_data(&hc_ptr, hdr->saddr.s6_addr, 16);
if (ipv6_saddr_type & IPV6_ADDR_LINKLOCAL) {
iphc1 |= lowpan_compress_addr_64(&hc_ptr,
&hdr->saddr,
saddr, true);
pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
&hdr->saddr, iphc1);
} else {
pr_debug("send the full source address\n");
lowpan_push_hc_data(&hc_ptr,
hdr->saddr.s6_addr, 16);
}
}
}
addr_type = ipv6_addr_type(&hdr->daddr);
/* destination address compression */
if (addr_type & IPV6_ADDR_MULTICAST) {
if (ipv6_daddr_type & IPV6_ADDR_MULTICAST) {
pr_debug("destination address is multicast: ");
iphc1 |= LOWPAN_IPHC_M;
iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr, &hdr->daddr);
} else {
if (addr_type & IPV6_ADDR_LINKLOCAL) {
/* TODO: context lookup */
iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->daddr,
daddr, false);
pr_debug("dest address unicast link-local %pI6c "
"iphc1 0x%02x\n", &hdr->daddr, iphc1);
if (dci) {
iphc1 |= lowpan_iphc_mcast_ctx_addr_compress(&hc_ptr,
&dci_entry,
&hdr->daddr);
iphc1 |= LOWPAN_IPHC_DAC;
} else {
pr_debug("dest address unicast %pI6c\n", &hdr->daddr);
lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr, 16);
iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr,
&hdr->daddr);
}
} else {
if (dci) {
iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->daddr,
&dci_entry, daddr,
false);
iphc1 |= LOWPAN_IPHC_DAC;
} else {
if (ipv6_daddr_type & IPV6_ADDR_LINKLOCAL) {
iphc1 |= lowpan_compress_addr_64(&hc_ptr,
&hdr->daddr,
daddr, false);
pr_debug("dest address unicast link-local %pI6c iphc1 0x%02x\n",
&hdr->daddr, iphc1);
} else {
pr_debug("dest address unicast %pI6c\n",
&hdr->daddr);
lowpan_push_hc_data(&hc_ptr,
hdr->daddr.s6_addr, 16);
}
}
}

View File

@@ -261,7 +261,6 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id)
* hope the underlying device can handle it.
*/
new_dev->mtu = real_dev->mtu;
new_dev->priv_flags |= (real_dev->priv_flags & IFF_UNICAST_FLT);
vlan = vlan_dev_priv(new_dev);
vlan->vlan_proto = htons(ETH_P_8021Q);
@@ -312,6 +311,7 @@ static void vlan_transfer_features(struct net_device *dev,
struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev);
vlandev->gso_max_size = dev->gso_max_size;
vlandev->gso_max_segs = dev->gso_max_segs;
if (vlan_hw_offload_capable(dev->features, vlan->vlan_proto))
vlandev->hard_header_len = dev->hard_header_len;

View File

@@ -551,6 +551,7 @@ static int vlan_dev_init(struct net_device *dev)
dev->features |= real_dev->vlan_features | NETIF_F_LLTX |
NETIF_F_GSO_SOFTWARE;
dev->gso_max_size = real_dev->gso_max_size;
dev->gso_max_segs = real_dev->gso_max_segs;
if (dev->features & NETIF_F_VLAN_FEATURES)
netdev_warn(real_dev, "VLAN features are set incorrectly. Q-in-Q configurations may not work correctly.\n");
@@ -621,12 +622,12 @@ static netdev_features_t vlan_dev_fix_features(struct net_device *dev,
return features;
}
static int vlan_ethtool_get_settings(struct net_device *dev,
struct ethtool_cmd *cmd)
static int vlan_ethtool_get_link_ksettings(struct net_device *dev,
struct ethtool_link_ksettings *cmd)
{
const struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
return __ethtool_get_settings(vlan->real_dev, cmd);
return __ethtool_get_link_ksettings(vlan->real_dev, cmd);
}
static void vlan_ethtool_get_drvinfo(struct net_device *dev,
@@ -741,7 +742,7 @@ static int vlan_dev_get_iflink(const struct net_device *dev)
}
static const struct ethtool_ops vlan_ethtool_ops = {
.get_settings = vlan_ethtool_get_settings,
.get_link_ksettings = vlan_ethtool_get_link_ksettings,
.get_drvinfo = vlan_ethtool_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_ts_info = vlan_ethtool_get_ts_info,
@@ -799,6 +800,7 @@ void vlan_setup(struct net_device *dev)
ether_setup(dev);
dev->priv_flags |= IFF_802_1Q_VLAN | IFF_NO_QUEUE;
dev->priv_flags |= IFF_UNICAST_FLT;
dev->priv_flags &= ~IFF_TX_SKB_SHARING;
netif_keep_dst(dev);

View File

@@ -184,12 +184,11 @@ int vlan_proc_add_dev(struct net_device *vlandev)
/*
* Delete directory entry for VLAN device.
*/
int vlan_proc_rem_dev(struct net_device *vlandev)
void vlan_proc_rem_dev(struct net_device *vlandev)
{
/** NOTE: This will consume the memory pointed to by dent, it seems. */
proc_remove(vlan_dev_priv(vlandev)->dent);
vlan_dev_priv(vlandev)->dent = NULL;
return 0;
}
/****** Proc filesystem entry points ****************************************/

View File

@@ -5,7 +5,7 @@
struct net;
int vlan_proc_init(struct net *net);
int vlan_proc_rem_dev(struct net_device *vlandev);
void vlan_proc_rem_dev(struct net_device *vlandev);
int vlan_proc_add_dev(struct net_device *vlandev);
void vlan_proc_cleanup(struct net *net);
@@ -14,7 +14,7 @@ void vlan_proc_cleanup(struct net *net);
#define vlan_proc_init(net) (0)
#define vlan_proc_cleanup(net) do {} while (0)
#define vlan_proc_add_dev(dev) ({(void)(dev), 0; })
#define vlan_proc_rem_dev(dev) ({(void)(dev), 0; })
#define vlan_proc_rem_dev(dev) do {} while (0)
#endif
#endif /* !(__BEN_VLAN_PROC_INC__) */

View File

@@ -109,14 +109,13 @@ struct p9_trans_rdma {
/**
* p9_rdma_context - Keeps track of in-process WR
*
* @wc_op: The original WR op for when the CQE completes in error.
* @busa: Bus address to unmap when the WR completes
* @req: Keeps track of requests (send)
* @rc: Keepts track of replies (receive)
*/
struct p9_rdma_req;
struct p9_rdma_context {
enum ib_wc_opcode wc_op;
struct ib_cqe cqe;
dma_addr_t busa;
union {
struct p9_req_t *req;
@@ -284,9 +283,12 @@ p9_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
}
static void
handle_recv(struct p9_client *client, struct p9_trans_rdma *rdma,
struct p9_rdma_context *c, enum ib_wc_status status, u32 byte_len)
recv_done(struct ib_cq *cq, struct ib_wc *wc)
{
struct p9_client *client = cq->cq_context;
struct p9_trans_rdma *rdma = client->trans;
struct p9_rdma_context *c =
container_of(wc->wr_cqe, struct p9_rdma_context, cqe);
struct p9_req_t *req;
int err = 0;
int16_t tag;
@@ -295,7 +297,7 @@ handle_recv(struct p9_client *client, struct p9_trans_rdma *rdma,
ib_dma_unmap_single(rdma->cm_id->device, c->busa, client->msize,
DMA_FROM_DEVICE);
if (status != IB_WC_SUCCESS)
if (wc->status != IB_WC_SUCCESS)
goto err_out;
err = p9_parse_header(c->rc, NULL, NULL, &tag, 1);
@@ -316,21 +318,32 @@ handle_recv(struct p9_client *client, struct p9_trans_rdma *rdma,
req->rc = c->rc;
p9_client_cb(client, req, REQ_STATUS_RCVD);
out:
up(&rdma->rq_sem);
kfree(c);
return;
err_out:
p9_debug(P9_DEBUG_ERROR, "req %p err %d status %d\n", req, err, status);
p9_debug(P9_DEBUG_ERROR, "req %p err %d status %d\n",
req, err, wc->status);
rdma->state = P9_RDMA_FLUSHING;
client->status = Disconnected;
goto out;
}
static void
handle_send(struct p9_client *client, struct p9_trans_rdma *rdma,
struct p9_rdma_context *c, enum ib_wc_status status, u32 byte_len)
send_done(struct ib_cq *cq, struct ib_wc *wc)
{
struct p9_client *client = cq->cq_context;
struct p9_trans_rdma *rdma = client->trans;
struct p9_rdma_context *c =
container_of(wc->wr_cqe, struct p9_rdma_context, cqe);
ib_dma_unmap_single(rdma->cm_id->device,
c->busa, c->req->tc->size,
DMA_TO_DEVICE);
up(&rdma->sq_sem);
kfree(c);
}
static void qp_event_handler(struct ib_event *event, void *context)
@@ -339,42 +352,6 @@ static void qp_event_handler(struct ib_event *event, void *context)
event->event, context);
}
static void cq_comp_handler(struct ib_cq *cq, void *cq_context)
{
struct p9_client *client = cq_context;
struct p9_trans_rdma *rdma = client->trans;
int ret;
struct ib_wc wc;
ib_req_notify_cq(rdma->cq, IB_CQ_NEXT_COMP);
while ((ret = ib_poll_cq(cq, 1, &wc)) > 0) {
struct p9_rdma_context *c = (void *) (unsigned long) wc.wr_id;
switch (c->wc_op) {
case IB_WC_RECV:
handle_recv(client, rdma, c, wc.status, wc.byte_len);
up(&rdma->rq_sem);
break;
case IB_WC_SEND:
handle_send(client, rdma, c, wc.status, wc.byte_len);
up(&rdma->sq_sem);
break;
default:
pr_err("unexpected completion type, c->wc_op=%d, wc.opcode=%d, status=%d\n",
c->wc_op, wc.opcode, wc.status);
break;
}
kfree(c);
}
}
static void cq_event_handler(struct ib_event *e, void *v)
{
p9_debug(P9_DEBUG_ERROR, "CQ event %d context %p\n", e->event, v);
}
static void rdma_destroy_trans(struct p9_trans_rdma *rdma)
{
if (!rdma)
@@ -387,7 +364,7 @@ static void rdma_destroy_trans(struct p9_trans_rdma *rdma)
ib_dealloc_pd(rdma->pd);
if (rdma->cq && !IS_ERR(rdma->cq))
ib_destroy_cq(rdma->cq);
ib_free_cq(rdma->cq);
if (rdma->cm_id && !IS_ERR(rdma->cm_id))
rdma_destroy_id(rdma->cm_id);
@@ -408,13 +385,14 @@ post_recv(struct p9_client *client, struct p9_rdma_context *c)
if (ib_dma_mapping_error(rdma->cm_id->device, c->busa))
goto error;
c->cqe.done = recv_done;
sge.addr = c->busa;
sge.length = client->msize;
sge.lkey = rdma->pd->local_dma_lkey;
wr.next = NULL;
c->wc_op = IB_WC_RECV;
wr.wr_id = (unsigned long) c;
wr.wr_cqe = &c->cqe;
wr.sg_list = &sge;
wr.num_sge = 1;
return ib_post_recv(rdma->qp, &wr, &bad_wr);
@@ -499,13 +477,14 @@ dont_need_post_recv:
goto send_error;
}
c->cqe.done = send_done;
sge.addr = c->busa;
sge.length = c->req->tc->size;
sge.lkey = rdma->pd->local_dma_lkey;
wr.next = NULL;
c->wc_op = IB_WC_SEND;
wr.wr_id = (unsigned long) c;
wr.wr_cqe = &c->cqe;
wr.opcode = IB_WR_SEND;
wr.send_flags = IB_SEND_SIGNALED;
wr.sg_list = &sge;
@@ -642,7 +621,6 @@ rdma_create_trans(struct p9_client *client, const char *addr, char *args)
struct p9_trans_rdma *rdma;
struct rdma_conn_param conn_param;
struct ib_qp_init_attr qp_attr;
struct ib_cq_init_attr cq_attr = {};
/* Parse the transport specific mount options */
err = parse_opts(args, &opts);
@@ -695,13 +673,11 @@ rdma_create_trans(struct p9_client *client, const char *addr, char *args)
goto error;
/* Create the Completion Queue */
cq_attr.cqe = opts.sq_depth + opts.rq_depth + 1;
rdma->cq = ib_create_cq(rdma->cm_id->device, cq_comp_handler,
cq_event_handler, client,
&cq_attr);
rdma->cq = ib_alloc_cq(rdma->cm_id->device, client,
opts.sq_depth + opts.rq_depth + 1,
0, IB_POLL_SOFTIRQ);
if (IS_ERR(rdma->cq))
goto error;
ib_req_notify_cq(rdma->cq, IB_CQ_NEXT_COMP);
/* Create the Protection Domain */
rdma->pd = ib_alloc_pd(rdma->cm_id->device);

View File

@@ -253,6 +253,9 @@ config XPS
depends on SMP
default y
config HWBM
bool
config SOCK_CGROUP_DATA
bool
default n
@@ -360,6 +363,7 @@ source "net/can/Kconfig"
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
source "net/kcm/Kconfig"
config FIB_RULES
bool
@@ -392,6 +396,26 @@ config LWTUNNEL
weight tunnel endpoint. Tunnel encapsulation parameters are stored
with light weight tunnel state associated with fib routes.
config DST_CACHE
bool "dst cache"
default n
config NET_DEVLINK
tristate "Network physical/parent device Netlink interface"
help
Network physical/parent device Netlink interface provides
infrastructure to support access to physical chip-wide config and
monitoring.
config MAY_USE_DEVLINK
tristate
default m if NET_DEVLINK=m
default y if NET_DEVLINK=y || NET_DEVLINK=n
help
Drivers using the devlink infrastructure should have a dependency
on MAY_USE_DEVLINK to ensure they do not cause link errors when
devlink is a loadable module and the driver using it is built-in.
endif # if NET
# Used by archs to tell that they support BPF_JIT

View File

@@ -34,6 +34,7 @@ obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
obj-$(CONFIG_AF_RXRPC) += rxrpc/
obj-$(CONFIG_AF_KCM) += kcm/
obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_L2TP) += l2tp/
obj-$(CONFIG_DECNET) += decnet/

View File

@@ -228,8 +228,23 @@ netdev_tx_t ax25_ip_xmit(struct sk_buff *skb)
}
#endif
static bool ax25_validate_header(const char *header, unsigned int len)
{
ax25_digi digi;
if (!len)
return false;
if (header[0])
return true;
return ax25_addr_parse(header + 1, len - 1, NULL, NULL, &digi, NULL,
NULL);
}
const struct header_ops ax25_header_ops = {
.create = ax25_hard_header,
.validate = ax25_validate_header,
};
EXPORT_SYMBOL(ax25_header_ops);

View File

@@ -12,9 +12,23 @@ config BATMAN_ADV
B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is
a routing protocol for multi-hop ad-hoc mesh networks. The
networks may be wired or wireless. See
http://www.open-mesh.org/ for more information and user space
https://www.open-mesh.org/ for more information and user space
tools.
config BATMAN_ADV_BATMAN_V
bool "B.A.T.M.A.N. V protocol (experimental)"
depends on BATMAN_ADV && CFG80211=y || (CFG80211=m && BATMAN_ADV=m)
default n
help
This option enables the B.A.T.M.A.N. V protocol, the successor
of the currently used B.A.T.M.A.N. IV protocol. The main
changes include splitting of the OGM protocol into a neighbor
discovery protocol (Echo Location Protocol, ELP) and a new OGM
Protocol OGMv2 for flooding protocol information through the
network, as well as a throughput based metric.
B.A.T.M.A.N. V is currently considered experimental and not
compatible to B.A.T.M.A.N. IV networks.
config BATMAN_ADV_BLA
bool "Bridge Loop Avoidance"
depends on BATMAN_ADV && INET

View File

@@ -1,5 +1,5 @@
#
# Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
# Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
#
# Marek Lindner, Simon Wunderlich
#
@@ -18,6 +18,9 @@
obj-$(CONFIG_BATMAN_ADV) += batman-adv.o
batman-adv-y += bat_iv_ogm.o
batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v.o
batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v_elp.o
batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v_ogm.o
batman-adv-y += bitarray.o
batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o
batman-adv-$(CONFIG_DEBUG_FS) += debugfs.o

View File

@@ -1,6 +1,6 @@
/* Copyright (C) 2011-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2011-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner
* Marek Lindner, Linus Lüssing
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
@@ -18,6 +18,32 @@
#ifndef _NET_BATMAN_ADV_BAT_ALGO_H_
#define _NET_BATMAN_ADV_BAT_ALGO_H_
struct batadv_priv;
int batadv_iv_init(void);
#ifdef CONFIG_BATMAN_ADV_BATMAN_V
int batadv_v_init(void);
int batadv_v_mesh_init(struct batadv_priv *bat_priv);
void batadv_v_mesh_free(struct batadv_priv *bat_priv);
#else
static inline int batadv_v_init(void)
{
return 0;
}
static inline int batadv_v_mesh_init(struct batadv_priv *bat_priv)
{
return 0;
}
static inline void batadv_v_mesh_free(struct batadv_priv *bat_priv)
{
}
#endif /* CONFIG_BATMAN_ADV_BATMAN_V */
#endif /* _NET_BATMAN_ADV_BAT_ALGO_H_ */

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -31,6 +31,7 @@
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/list.h>
#include <linux/kref.h>
#include <linux/netdevice.h>
#include <linux/pkt_sched.h>
#include <linux/printk.h>
@@ -88,7 +89,7 @@ static void batadv_ring_buffer_set(u8 lq_recv[], u8 *lq_index, u8 value)
* in the given ring buffer
* @lq_recv: pointer to the ring buffer
*
* Returns computed average value.
* Return: computed average value.
*/
static u8 batadv_ring_buffer_avg(const u8 lq_recv[])
{
@@ -132,7 +133,7 @@ static void batadv_iv_ogm_orig_free(struct batadv_orig_node *orig_node)
* @orig_node: the orig_node that has to be changed
* @max_if_num: the current amount of interfaces
*
* Returns 0 on success, a negative error code otherwise.
* Return: 0 on success, a negative error code otherwise.
*/
static int batadv_iv_ogm_orig_add_if(struct batadv_orig_node *orig_node,
int max_if_num)
@@ -180,7 +181,7 @@ unlock:
* @max_if_num: the current amount of interfaces
* @del_if_num: the index of the interface being removed
*
* Returns 0 on success, a negative error code otherwise.
* Return: 0 on success, a negative error code otherwise.
*/
static int batadv_iv_ogm_orig_del_if(struct batadv_orig_node *orig_node,
int max_if_num, int del_if_num)
@@ -246,7 +247,7 @@ unlock:
* @bat_priv: the bat priv with all the soft interface information
* @addr: mac address of the originator
*
* Returns the originator object corresponding to the passed mac address or NULL
* Return: the originator object corresponding to the passed mac address or NULL
* on failure.
* If the object does not exists it is created an initialised.
*/
@@ -286,8 +287,8 @@ batadv_iv_ogm_orig_get(struct batadv_priv *bat_priv, const u8 *addr)
free_orig_node:
/* free twice, as batadv_orig_node_new sets refcount to 2 */
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
batadv_orig_node_put(orig_node);
return NULL;
}
@@ -396,7 +397,14 @@ static u8 batadv_hop_penalty(u8 tq, const struct batadv_priv *bat_priv)
return new_tq;
}
/* is there another aggregated packet here? */
/**
* batadv_iv_ogm_aggr_packet - checks if there is another OGM attached
* @buff_pos: current position in the skb
* @packet_len: total length of the skb
* @tvlv_len: tvlv length of the previously considered OGM
*
* Return: true if there is enough space for another OGM, false otherwise.
*/
static bool batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
__be16 tvlv_len)
{
@@ -470,7 +478,7 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_TX);
batadv_add_counter(bat_priv, BATADV_CNT_MGMT_TX_BYTES,
skb->len + ETH_HLEN);
batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr);
batadv_send_broadcast_skb(skb, hard_iface);
}
}
@@ -507,7 +515,7 @@ static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet)
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
}
/**
@@ -522,7 +530,7 @@ out:
* @if_outgoing: interface for which the retransmission should be considered
* @forw_packet: the forwarded packet which should be checked
*
* Returns true if new_packet can be aggregated with forw_packet
* Return: true if new_packet can be aggregated with forw_packet
*/
static bool
batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
@@ -609,7 +617,7 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
return res;
}
@@ -636,10 +644,10 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
unsigned char *skb_buff;
unsigned int skb_size;
if (!atomic_inc_not_zero(&if_incoming->refcount))
if (!kref_get_unless_zero(&if_incoming->refcount))
return;
if (!atomic_inc_not_zero(&if_outgoing->refcount))
if (!kref_get_unless_zero(&if_outgoing->refcount))
goto out_free_incoming;
/* own packet should always be scheduled */
@@ -703,9 +711,9 @@ out_nomem:
if (!own_packet)
atomic_inc(&bat_priv->batman_queue_left);
out_free_outgoing:
batadv_hardif_free_ref(if_outgoing);
batadv_hardif_put(if_outgoing);
out_free_incoming:
batadv_hardif_free_ref(if_incoming);
batadv_hardif_put(if_incoming);
}
/* aggregate a new packet into the existing ogm packet */
@@ -950,7 +958,7 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
}
/**
@@ -995,9 +1003,9 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
neigh_addr = tmp_neigh_node->addr;
if (batadv_compare_eth(neigh_addr, ethhdr->h_source) &&
tmp_neigh_node->if_incoming == if_incoming &&
atomic_inc_not_zero(&tmp_neigh_node->refcount)) {
kref_get_unless_zero(&tmp_neigh_node->refcount)) {
if (WARN(neigh_node, "too many matching neigh_nodes"))
batadv_neigh_node_free_ref(neigh_node);
batadv_neigh_node_put(neigh_node);
neigh_node = tmp_neigh_node;
continue;
}
@@ -1018,7 +1026,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
neigh_ifinfo->bat_iv.tq_avg = tq_avg;
spin_unlock_bh(&tmp_neigh_node->ifinfo_lock);
batadv_neigh_ifinfo_free_ref(neigh_ifinfo);
batadv_neigh_ifinfo_put(neigh_ifinfo);
neigh_ifinfo = NULL;
}
@@ -1033,7 +1041,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
ethhdr->h_source,
orig_node, orig_tmp);
batadv_orig_node_free_ref(orig_tmp);
batadv_orig_node_put(orig_tmp);
if (!neigh_node)
goto unlock;
} else {
@@ -1108,13 +1116,13 @@ unlock:
rcu_read_unlock();
out:
if (neigh_node)
batadv_neigh_node_free_ref(neigh_node);
batadv_neigh_node_put(neigh_node);
if (router)
batadv_neigh_node_free_ref(router);
batadv_neigh_node_put(router);
if (neigh_ifinfo)
batadv_neigh_ifinfo_free_ref(neigh_ifinfo);
batadv_neigh_ifinfo_put(neigh_ifinfo);
if (router_ifinfo)
batadv_neigh_ifinfo_free_ref(router_ifinfo);
batadv_neigh_ifinfo_put(router_ifinfo);
}
/**
@@ -1125,7 +1133,7 @@ out:
* @if_incoming: interface where the packet was received
* @if_outgoing: interface for which the retransmission should be considered
*
* Returns 1 if the link can be considered bidirectional, 0 otherwise
* Return: 1 if the link can be considered bidirectional, 0 otherwise
*/
static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node,
struct batadv_orig_node *orig_neigh_node,
@@ -1154,7 +1162,7 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node,
if (tmp_neigh_node->if_incoming != if_incoming)
continue;
if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
if (!kref_get_unless_zero(&tmp_neigh_node->refcount))
continue;
neigh_node = tmp_neigh_node;
@@ -1184,7 +1192,7 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node,
neigh_ifinfo = batadv_neigh_ifinfo_new(neigh_node, if_outgoing);
if (neigh_ifinfo) {
neigh_rq_count = neigh_ifinfo->bat_iv.real_packet_count;
batadv_neigh_ifinfo_free_ref(neigh_ifinfo);
batadv_neigh_ifinfo_put(neigh_ifinfo);
} else {
neigh_rq_count = 0;
}
@@ -1257,7 +1265,7 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node,
out:
if (neigh_node)
batadv_neigh_node_free_ref(neigh_node);
batadv_neigh_node_put(neigh_node);
return ret;
}
@@ -1269,7 +1277,7 @@ out:
* @if_incoming: interface on which the OGM packet was received
* @if_outgoing: interface for which the retransmission should be considered
*
* Returns duplicate status as enum batadv_dup_status
* Return: duplicate status as enum batadv_dup_status
*/
static enum batadv_dup_status
batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
@@ -1298,7 +1306,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing);
if (WARN_ON(!orig_ifinfo)) {
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
return 0;
}
@@ -1308,7 +1316,8 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
/* signalize caller that the packet is to be dropped. */
if (!hlist_empty(&orig_node->neigh_list) &&
batadv_window_protected(bat_priv, seq_diff,
&orig_ifinfo->batman_seqno_reset)) {
BATADV_TQ_LOCAL_WINDOW_SIZE,
&orig_ifinfo->batman_seqno_reset, NULL)) {
ret = BATADV_PROTECTED;
goto out;
}
@@ -1344,7 +1353,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
packet_count = bitmap_weight(bitmap,
BATADV_TQ_LOCAL_WINDOW_SIZE);
neigh_ifinfo->bat_iv.real_packet_count = packet_count;
batadv_neigh_ifinfo_free_ref(neigh_ifinfo);
batadv_neigh_ifinfo_put(neigh_ifinfo);
}
rcu_read_unlock();
@@ -1358,8 +1367,8 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
out:
spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
batadv_orig_node_free_ref(orig_node);
batadv_orig_ifinfo_free_ref(orig_ifinfo);
batadv_orig_node_put(orig_node);
batadv_orig_ifinfo_put(orig_ifinfo);
return ret;
}
@@ -1505,7 +1514,7 @@ batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset,
ogm_packet, if_incoming,
if_outgoing, dup_status);
}
batadv_orig_ifinfo_free_ref(orig_ifinfo);
batadv_orig_ifinfo_put(orig_ifinfo);
/* only forward for specific interface, not for the default one. */
if (if_outgoing == BATADV_IF_DEFAULT)
@@ -1554,18 +1563,18 @@ batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset,
out_neigh:
if ((orig_neigh_node) && (!is_single_hop_neigh))
batadv_orig_node_free_ref(orig_neigh_node);
batadv_orig_node_put(orig_neigh_node);
out:
if (router_ifinfo)
batadv_neigh_ifinfo_free_ref(router_ifinfo);
batadv_neigh_ifinfo_put(router_ifinfo);
if (router)
batadv_neigh_node_free_ref(router);
batadv_neigh_node_put(router);
if (router_router)
batadv_neigh_node_free_ref(router_router);
batadv_neigh_node_put(router_router);
if (orig_neigh_router)
batadv_neigh_node_free_ref(orig_neigh_router);
batadv_neigh_node_put(orig_neigh_router);
if (hardif_neigh)
batadv_hardif_neigh_free_ref(hardif_neigh);
batadv_hardif_neigh_put(hardif_neigh);
kfree_skb(skb_priv);
}
@@ -1688,7 +1697,7 @@ static void batadv_iv_ogm_process(const struct sk_buff *skb, int ogm_offset,
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Drop packet: originator packet from myself (via neighbor)\n");
batadv_orig_node_free_ref(orig_neigh_node);
batadv_orig_node_put(orig_neigh_node);
return;
}
@@ -1726,7 +1735,7 @@ static void batadv_iv_ogm_process(const struct sk_buff *skb, int ogm_offset,
}
rcu_read_unlock();
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
}
static int batadv_iv_ogm_receive(struct sk_buff *skb,
@@ -1796,7 +1805,7 @@ batadv_iv_ogm_orig_print_neigh(struct batadv_orig_node *orig_node,
neigh_node->addr,
n_ifinfo->bat_iv.tq_avg);
batadv_neigh_ifinfo_free_ref(n_ifinfo);
batadv_neigh_ifinfo_put(n_ifinfo);
}
}
@@ -1859,9 +1868,9 @@ static void batadv_iv_ogm_orig_print(struct batadv_priv *bat_priv,
batman_count++;
next:
batadv_neigh_node_free_ref(neigh_node);
batadv_neigh_node_put(neigh_node);
if (n_ifinfo)
batadv_neigh_ifinfo_free_ref(n_ifinfo);
batadv_neigh_ifinfo_put(n_ifinfo);
}
rcu_read_unlock();
}
@@ -1929,7 +1938,7 @@ static void batadv_iv_neigh_print(struct batadv_priv *bat_priv,
* @neigh2: the second neighbor object of the comparison
* @if_outgoing2: outgoing interface for the second neighbor
*
* Returns a value less, equal to or greater than 0 if the metric via neigh1 is
* Return: a value less, equal to or greater than 0 if the metric via neigh1 is
* lower, the same as or higher than the metric via neigh2
*/
static int batadv_iv_ogm_neigh_cmp(struct batadv_neigh_node *neigh1,
@@ -1955,9 +1964,9 @@ static int batadv_iv_ogm_neigh_cmp(struct batadv_neigh_node *neigh1,
out:
if (neigh1_ifinfo)
batadv_neigh_ifinfo_free_ref(neigh1_ifinfo);
batadv_neigh_ifinfo_put(neigh1_ifinfo);
if (neigh2_ifinfo)
batadv_neigh_ifinfo_free_ref(neigh2_ifinfo);
batadv_neigh_ifinfo_put(neigh2_ifinfo);
return diff;
}
@@ -1970,7 +1979,7 @@ out:
* @neigh2: the second neighbor object of the comparison
* @if_outgoing2: outgoing interface for the second neighbor
*
* Returns true if the metric via neigh1 is equally good or better than
* Return: true if the metric via neigh1 is equally good or better than
* the metric via neigh2, false otherwise.
*/
static bool
@@ -1998,9 +2007,9 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,
out:
if (neigh1_ifinfo)
batadv_neigh_ifinfo_free_ref(neigh1_ifinfo);
batadv_neigh_ifinfo_put(neigh1_ifinfo);
if (neigh2_ifinfo)
batadv_neigh_ifinfo_free_ref(neigh2_ifinfo);
batadv_neigh_ifinfo_put(neigh2_ifinfo);
return ret;
}

347
net/batman-adv/bat_v.c Normal file
View File

@@ -0,0 +1,347 @@
/* Copyright (C) 2013-2016 B.A.T.M.A.N. contributors:
*
* Linus Lüssing, Marek Lindner
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "bat_algo.h"
#include "main.h"
#include <linux/atomic.h>
#include <linux/bug.h>
#include <linux/cache.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/netdevice.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/seq_file.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include "bat_v_elp.h"
#include "bat_v_ogm.h"
#include "hash.h"
#include "originator.h"
#include "packet.h"
static int batadv_v_iface_enable(struct batadv_hard_iface *hard_iface)
{
int ret;
ret = batadv_v_elp_iface_enable(hard_iface);
if (ret < 0)
return ret;
ret = batadv_v_ogm_iface_enable(hard_iface);
if (ret < 0)
batadv_v_elp_iface_disable(hard_iface);
/* enable link throughput auto-detection by setting the throughput
* override to zero
*/
atomic_set(&hard_iface->bat_v.throughput_override, 0);
return ret;
}
static void batadv_v_iface_disable(struct batadv_hard_iface *hard_iface)
{
batadv_v_elp_iface_disable(hard_iface);
}
static void batadv_v_iface_update_mac(struct batadv_hard_iface *hard_iface)
{
}
static void batadv_v_primary_iface_set(struct batadv_hard_iface *hard_iface)
{
batadv_v_elp_primary_iface_set(hard_iface);
batadv_v_ogm_primary_iface_set(hard_iface);
}
static void
batadv_v_hardif_neigh_init(struct batadv_hardif_neigh_node *hardif_neigh)
{
ewma_throughput_init(&hardif_neigh->bat_v.throughput);
INIT_WORK(&hardif_neigh->bat_v.metric_work,
batadv_v_elp_throughput_metric_update);
}
static void batadv_v_ogm_schedule(struct batadv_hard_iface *hard_iface)
{
}
static void batadv_v_ogm_emit(struct batadv_forw_packet *forw_packet)
{
}
/**
* batadv_v_orig_print_neigh - print neighbors for the originator table
* @orig_node: the orig_node for which the neighbors are printed
* @if_outgoing: outgoing interface for these entries
* @seq: debugfs table seq_file struct
*
* Must be called while holding an rcu lock.
*/
static void
batadv_v_orig_print_neigh(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *if_outgoing,
struct seq_file *seq)
{
struct batadv_neigh_node *neigh_node;
struct batadv_neigh_ifinfo *n_ifinfo;
hlist_for_each_entry_rcu(neigh_node, &orig_node->neigh_list, list) {
n_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
if (!n_ifinfo)
continue;
seq_printf(seq, " %pM (%9u.%1u)",
neigh_node->addr,
n_ifinfo->bat_v.throughput / 10,
n_ifinfo->bat_v.throughput % 10);
batadv_neigh_ifinfo_put(n_ifinfo);
}
}
/**
* batadv_v_hardif_neigh_print - print a single ELP neighbour node
* @seq: neighbour table seq_file struct
* @hardif_neigh: hardif neighbour information
*/
static void
batadv_v_hardif_neigh_print(struct seq_file *seq,
struct batadv_hardif_neigh_node *hardif_neigh)
{
int last_secs, last_msecs;
u32 throughput;
last_secs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen) / 1000;
last_msecs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen) % 1000;
throughput = ewma_throughput_read(&hardif_neigh->bat_v.throughput);
seq_printf(seq, "%pM %4i.%03is (%9u.%1u) [%10s]\n",
hardif_neigh->addr, last_secs, last_msecs, throughput / 10,
throughput % 10, hardif_neigh->if_incoming->net_dev->name);
}
/**
* batadv_v_neigh_print - print the single hop neighbour list
* @bat_priv: the bat priv with all the soft interface information
* @seq: neighbour table seq_file struct
*/
static void batadv_v_neigh_print(struct batadv_priv *bat_priv,
struct seq_file *seq)
{
struct net_device *net_dev = (struct net_device *)seq->private;
struct batadv_hardif_neigh_node *hardif_neigh;
struct batadv_hard_iface *hard_iface;
int batman_count = 0;
seq_printf(seq, " %-15s %s (%11s) [%10s]\n", "Neighbor",
"last-seen", "throughput", "IF");
rcu_read_lock();
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
if (hard_iface->soft_iface != net_dev)
continue;
hlist_for_each_entry_rcu(hardif_neigh,
&hard_iface->neigh_list, list) {
batadv_v_hardif_neigh_print(seq, hardif_neigh);
batman_count++;
}
}
rcu_read_unlock();
if (batman_count == 0)
seq_puts(seq, "No batman nodes in range ...\n");
}
/**
* batadv_v_orig_print - print the originator table
* @bat_priv: the bat priv with all the soft interface information
* @seq: debugfs table seq_file struct
* @if_outgoing: the outgoing interface for which this should be printed
*/
static void batadv_v_orig_print(struct batadv_priv *bat_priv,
struct seq_file *seq,
struct batadv_hard_iface *if_outgoing)
{
struct batadv_neigh_node *neigh_node;
struct batadv_hashtable *hash = bat_priv->orig_hash;
int last_seen_msecs, last_seen_secs;
struct batadv_orig_node *orig_node;
struct batadv_neigh_ifinfo *n_ifinfo;
unsigned long last_seen_jiffies;
struct hlist_head *head;
int batman_count = 0;
u32 i;
seq_printf(seq, " %-15s %s (%11s) %17s [%10s]: %20s ...\n",
"Originator", "last-seen", "throughput", "Nexthop",
"outgoingIF", "Potential nexthops");
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
neigh_node = batadv_orig_router_get(orig_node,
if_outgoing);
if (!neigh_node)
continue;
n_ifinfo = batadv_neigh_ifinfo_get(neigh_node,
if_outgoing);
if (!n_ifinfo)
goto next;
last_seen_jiffies = jiffies - orig_node->last_seen;
last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
last_seen_secs = last_seen_msecs / 1000;
last_seen_msecs = last_seen_msecs % 1000;
seq_printf(seq, "%pM %4i.%03is (%9u.%1u) %pM [%10s]:",
orig_node->orig, last_seen_secs,
last_seen_msecs,
n_ifinfo->bat_v.throughput / 10,
n_ifinfo->bat_v.throughput % 10,
neigh_node->addr,
neigh_node->if_incoming->net_dev->name);
batadv_v_orig_print_neigh(orig_node, if_outgoing, seq);
seq_puts(seq, "\n");
batman_count++;
next:
batadv_neigh_node_put(neigh_node);
if (n_ifinfo)
batadv_neigh_ifinfo_put(n_ifinfo);
}
rcu_read_unlock();
}
if (batman_count == 0)
seq_puts(seq, "No batman nodes in range ...\n");
}
static int batadv_v_neigh_cmp(struct batadv_neigh_node *neigh1,
struct batadv_hard_iface *if_outgoing1,
struct batadv_neigh_node *neigh2,
struct batadv_hard_iface *if_outgoing2)
{
struct batadv_neigh_ifinfo *ifinfo1, *ifinfo2;
ifinfo1 = batadv_neigh_ifinfo_get(neigh1, if_outgoing1);
ifinfo2 = batadv_neigh_ifinfo_get(neigh2, if_outgoing2);
if (WARN_ON(!ifinfo1 || !ifinfo2))
return 0;
return ifinfo1->bat_v.throughput - ifinfo2->bat_v.throughput;
}
static bool batadv_v_neigh_is_sob(struct batadv_neigh_node *neigh1,
struct batadv_hard_iface *if_outgoing1,
struct batadv_neigh_node *neigh2,
struct batadv_hard_iface *if_outgoing2)
{
struct batadv_neigh_ifinfo *ifinfo1, *ifinfo2;
u32 threshold;
ifinfo1 = batadv_neigh_ifinfo_get(neigh1, if_outgoing1);
ifinfo2 = batadv_neigh_ifinfo_get(neigh2, if_outgoing2);
threshold = ifinfo1->bat_v.throughput / 4;
threshold = ifinfo1->bat_v.throughput - threshold;
return ifinfo2->bat_v.throughput > threshold;
}
static struct batadv_algo_ops batadv_batman_v __read_mostly = {
.name = "BATMAN_V",
.bat_iface_enable = batadv_v_iface_enable,
.bat_iface_disable = batadv_v_iface_disable,
.bat_iface_update_mac = batadv_v_iface_update_mac,
.bat_primary_iface_set = batadv_v_primary_iface_set,
.bat_hardif_neigh_init = batadv_v_hardif_neigh_init,
.bat_ogm_emit = batadv_v_ogm_emit,
.bat_ogm_schedule = batadv_v_ogm_schedule,
.bat_orig_print = batadv_v_orig_print,
.bat_neigh_cmp = batadv_v_neigh_cmp,
.bat_neigh_is_similar_or_better = batadv_v_neigh_is_sob,
.bat_neigh_print = batadv_v_neigh_print,
};
/**
* batadv_v_mesh_init - initialize the B.A.T.M.A.N. V private resources for a
* mesh
* @bat_priv: the object representing the mesh interface to initialise
*
* Return: 0 on success or a negative error code otherwise
*/
int batadv_v_mesh_init(struct batadv_priv *bat_priv)
{
return batadv_v_ogm_init(bat_priv);
}
/**
* batadv_v_mesh_free - free the B.A.T.M.A.N. V private resources for a mesh
* @bat_priv: the object representing the mesh interface to free
*/
void batadv_v_mesh_free(struct batadv_priv *bat_priv)
{
batadv_v_ogm_free(bat_priv);
}
/**
* batadv_v_init - B.A.T.M.A.N. V initialization function
*
* Description: Takes care of initializing all the subcomponents.
* It is invoked upon module load only.
*
* Return: 0 on success or a negative error code otherwise
*/
int __init batadv_v_init(void)
{
int ret;
/* B.A.T.M.A.N. V echo location protocol packet */
ret = batadv_recv_handler_register(BATADV_ELP,
batadv_v_elp_packet_recv);
if (ret < 0)
return ret;
ret = batadv_recv_handler_register(BATADV_OGM2,
batadv_v_ogm_packet_recv);
if (ret < 0)
goto elp_unregister;
ret = batadv_algo_register(&batadv_batman_v);
if (ret < 0)
goto ogm_unregister;
return ret;
ogm_unregister:
batadv_recv_handler_unregister(BATADV_OGM2);
elp_unregister:
batadv_recv_handler_unregister(BATADV_ELP);
return ret;
}

515
net/batman-adv/bat_v_elp.c Normal file
View File

@@ -0,0 +1,515 @@
/* Copyright (C) 2011-2016 B.A.T.M.A.N. contributors:
*
* Linus Lüssing, Marek Lindner
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "bat_v_elp.h"
#include "main.h"
#include <linux/atomic.h>
#include <linux/byteorder/generic.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/fs.h>
#include <linux/if_ether.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/netdevice.h>
#include <linux/random.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/rtnetlink.h>
#include <linux/skbuff.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <net/cfg80211.h>
#include "bat_algo.h"
#include "bat_v_ogm.h"
#include "hard-interface.h"
#include "originator.h"
#include "packet.h"
#include "routing.h"
#include "send.h"
/**
* batadv_v_elp_start_timer - restart timer for ELP periodic work
* @hard_iface: the interface for which the timer has to be reset
*/
static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface)
{
unsigned int msecs;
msecs = atomic_read(&hard_iface->bat_v.elp_interval) - BATADV_JITTER;
msecs += prandom_u32() % (2 * BATADV_JITTER);
queue_delayed_work(batadv_event_workqueue, &hard_iface->bat_v.elp_wq,
msecs_to_jiffies(msecs));
}
/**
* batadv_v_elp_get_throughput - get the throughput towards a neighbour
* @neigh: the neighbour for which the throughput has to be obtained
*
* Return: The throughput towards the given neighbour in multiples of 100kpbs
* (a value of '1' equals to 0.1Mbps, '10' equals 1Mbps, etc).
*/
static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
{
struct batadv_hard_iface *hard_iface = neigh->if_incoming;
struct ethtool_link_ksettings link_settings;
struct station_info sinfo;
u32 throughput;
int ret;
/* if the user specified a customised value for this interface, then
* return it directly
*/
throughput = atomic_read(&hard_iface->bat_v.throughput_override);
if (throughput != 0)
return throughput;
/* if this is a wireless device, then ask its throughput through
* cfg80211 API
*/
if (batadv_is_wifi_netdev(hard_iface->net_dev)) {
if (hard_iface->net_dev->ieee80211_ptr) {
ret = cfg80211_get_station(hard_iface->net_dev,
neigh->addr, &sinfo);
if (ret == -ENOENT) {
/* Node is not associated anymore! It would be
* possible to delete this neighbor. For now set
* the throughput metric to 0.
*/
return 0;
}
if (!ret)
return sinfo.expected_throughput / 100;
}
/* unsupported WiFi driver version */
goto default_throughput;
}
/* if not a wifi interface, check if this device provides data via
* ethtool (e.g. an Ethernet adapter)
*/
memset(&link_settings, 0, sizeof(link_settings));
rtnl_lock();
ret = __ethtool_get_link_ksettings(hard_iface->net_dev, &link_settings);
rtnl_unlock();
if (ret == 0) {
/* link characteristics might change over time */
if (link_settings.base.duplex == DUPLEX_FULL)
hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX;
else
hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
throughput = link_settings.base.speed;
if (throughput && (throughput != SPEED_UNKNOWN))
return throughput * 10;
}
default_throughput:
if (!(hard_iface->bat_v.flags & BATADV_WARNING_DEFAULT)) {
batadv_info(hard_iface->soft_iface,
"WiFi driver or ethtool info does not provide information about link speeds on interface %s, therefore defaulting to hardcoded throughput values of %u.%1u Mbps. Consider overriding the throughput manually or checking your driver.\n",
hard_iface->net_dev->name,
BATADV_THROUGHPUT_DEFAULT_VALUE / 10,
BATADV_THROUGHPUT_DEFAULT_VALUE % 10);
hard_iface->bat_v.flags |= BATADV_WARNING_DEFAULT;
}
/* if none of the above cases apply, return the base_throughput */
return BATADV_THROUGHPUT_DEFAULT_VALUE;
}
/**
* batadv_v_elp_throughput_metric_update - worker updating the throughput metric
* of a single hop neighbour
* @work: the work queue item
*/
void batadv_v_elp_throughput_metric_update(struct work_struct *work)
{
struct batadv_hardif_neigh_node_bat_v *neigh_bat_v;
struct batadv_hardif_neigh_node *neigh;
neigh_bat_v = container_of(work, struct batadv_hardif_neigh_node_bat_v,
metric_work);
neigh = container_of(neigh_bat_v, struct batadv_hardif_neigh_node,
bat_v);
ewma_throughput_add(&neigh->bat_v.throughput,
batadv_v_elp_get_throughput(neigh));
/* decrement refcounter to balance increment performed before scheduling
* this task
*/
batadv_hardif_neigh_put(neigh);
}
/**
* batadv_v_elp_wifi_neigh_probe - send link probing packets to a neighbour
* @neigh: the neighbour to probe
*
* Sends a predefined number of unicast wifi packets to a given neighbour in
* order to trigger the throughput estimation on this link by the RC algorithm.
* Packets are sent only if there there is not enough payload unicast traffic
* towards this neighbour..
*
* Return: True on success and false in case of error during skb preparation.
*/
static bool
batadv_v_elp_wifi_neigh_probe(struct batadv_hardif_neigh_node *neigh)
{
struct batadv_hard_iface *hard_iface = neigh->if_incoming;
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
unsigned long last_tx_diff;
struct sk_buff *skb;
int probe_len, i;
int elp_skb_len;
/* this probing routine is for Wifi neighbours only */
if (!batadv_is_wifi_netdev(hard_iface->net_dev))
return true;
/* probe the neighbor only if no unicast packets have been sent
* to it in the last 100 milliseconds: this is the rate control
* algorithm sampling interval (minstrel). In this way, if not
* enough traffic has been sent to the neighbor, batman-adv can
* generate 2 probe packets and push the RC algorithm to perform
* the sampling
*/
last_tx_diff = jiffies_to_msecs(jiffies - neigh->bat_v.last_unicast_tx);
if (last_tx_diff <= BATADV_ELP_PROBE_MAX_TX_DIFF)
return true;
probe_len = max_t(int, sizeof(struct batadv_elp_packet),
BATADV_ELP_MIN_PROBE_SIZE);
for (i = 0; i < BATADV_ELP_PROBES_PER_NODE; i++) {
elp_skb_len = hard_iface->bat_v.elp_skb->len;
skb = skb_copy_expand(hard_iface->bat_v.elp_skb, 0,
probe_len - elp_skb_len,
GFP_ATOMIC);
if (!skb)
return false;
/* Tell the skb to get as big as the allocated space (we want
* the packet to be exactly of that size to make the link
* throughput estimation effective.
*/
skb_put(skb, probe_len - hard_iface->bat_v.elp_skb->len);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Sending unicast (probe) ELP packet on interface %s to %pM\n",
hard_iface->net_dev->name, neigh->addr);
batadv_send_skb_packet(skb, hard_iface, neigh->addr);
}
return true;
}
/**
* batadv_v_elp_periodic_work - ELP periodic task per interface
* @work: work queue item
*
* Emits broadcast ELP message in regular intervals.
*/
static void batadv_v_elp_periodic_work(struct work_struct *work)
{
struct batadv_hardif_neigh_node *hardif_neigh;
struct batadv_hard_iface *hard_iface;
struct batadv_hard_iface_bat_v *bat_v;
struct batadv_elp_packet *elp_packet;
struct batadv_priv *bat_priv;
struct sk_buff *skb;
u32 elp_interval;
bat_v = container_of(work, struct batadv_hard_iface_bat_v, elp_wq.work);
hard_iface = container_of(bat_v, struct batadv_hard_iface, bat_v);
bat_priv = netdev_priv(hard_iface->soft_iface);
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
goto out;
/* we are in the process of shutting this interface down */
if ((hard_iface->if_status == BATADV_IF_NOT_IN_USE) ||
(hard_iface->if_status == BATADV_IF_TO_BE_REMOVED))
goto out;
/* the interface was enabled but may not be ready yet */
if (hard_iface->if_status != BATADV_IF_ACTIVE)
goto restart_timer;
skb = skb_copy(hard_iface->bat_v.elp_skb, GFP_ATOMIC);
if (!skb)
goto restart_timer;
elp_packet = (struct batadv_elp_packet *)skb->data;
elp_packet->seqno = htonl(atomic_read(&hard_iface->bat_v.elp_seqno));
elp_interval = atomic_read(&hard_iface->bat_v.elp_interval);
elp_packet->elp_interval = htonl(elp_interval);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Sending broadcast ELP packet on interface %s, seqno %u\n",
hard_iface->net_dev->name,
atomic_read(&hard_iface->bat_v.elp_seqno));
batadv_send_broadcast_skb(skb, hard_iface);
atomic_inc(&hard_iface->bat_v.elp_seqno);
/* The throughput metric is updated on each sent packet. This way, if a
* node is dead and no longer sends packets, batman-adv is still able to
* react timely to its death.
*
* The throughput metric is updated by following these steps:
* 1) if the hard_iface is wifi => send a number of unicast ELPs for
* probing/sampling to each neighbor
* 2) update the throughput metric value of each neighbor (note that the
* value retrieved in this step might be 100ms old because the
* probing packets at point 1) could still be in the HW queue)
*/
rcu_read_lock();
hlist_for_each_entry_rcu(hardif_neigh, &hard_iface->neigh_list, list) {
if (!batadv_v_elp_wifi_neigh_probe(hardif_neigh))
/* if something goes wrong while probing, better to stop
* sending packets immediately and reschedule the task
*/
break;
if (!kref_get_unless_zero(&hardif_neigh->refcount))
continue;
/* Reading the estimated throughput from cfg80211 is a task that
* may sleep and that is not allowed in an rcu protected
* context. Therefore schedule a task for that.
*/
queue_work(batadv_event_workqueue,
&hardif_neigh->bat_v.metric_work);
}
rcu_read_unlock();
restart_timer:
batadv_v_elp_start_timer(hard_iface);
out:
return;
}
/**
* batadv_v_elp_iface_enable - setup the ELP interface private resources
* @hard_iface: interface for which the data has to be prepared
*
* Return: 0 on success or a -ENOMEM in case of failure.
*/
int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface)
{
struct batadv_elp_packet *elp_packet;
unsigned char *elp_buff;
u32 random_seqno;
size_t size;
int res = -ENOMEM;
size = ETH_HLEN + NET_IP_ALIGN + BATADV_ELP_HLEN;
hard_iface->bat_v.elp_skb = dev_alloc_skb(size);
if (!hard_iface->bat_v.elp_skb)
goto out;
skb_reserve(hard_iface->bat_v.elp_skb, ETH_HLEN + NET_IP_ALIGN);
elp_buff = skb_push(hard_iface->bat_v.elp_skb, BATADV_ELP_HLEN);
elp_packet = (struct batadv_elp_packet *)elp_buff;
memset(elp_packet, 0, BATADV_ELP_HLEN);
elp_packet->packet_type = BATADV_ELP;
elp_packet->version = BATADV_COMPAT_VERSION;
/* randomize initial seqno to avoid collision */
get_random_bytes(&random_seqno, sizeof(random_seqno));
atomic_set(&hard_iface->bat_v.elp_seqno, random_seqno);
atomic_set(&hard_iface->bat_v.elp_interval, 500);
/* assume full-duplex by default */
hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX;
/* warn the user (again) if there is no throughput data is available */
hard_iface->bat_v.flags &= ~BATADV_WARNING_DEFAULT;
if (batadv_is_wifi_netdev(hard_iface->net_dev))
hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
INIT_DELAYED_WORK(&hard_iface->bat_v.elp_wq,
batadv_v_elp_periodic_work);
batadv_v_elp_start_timer(hard_iface);
res = 0;
out:
return res;
}
/**
* batadv_v_elp_iface_disable - release ELP interface private resources
* @hard_iface: interface for which the resources have to be released
*/
void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface)
{
cancel_delayed_work_sync(&hard_iface->bat_v.elp_wq);
dev_kfree_skb(hard_iface->bat_v.elp_skb);
hard_iface->bat_v.elp_skb = NULL;
}
/**
* batadv_v_elp_primary_iface_set - change internal data to reflect the new
* primary interface
* @primary_iface: the new primary interface
*/
void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface)
{
struct batadv_hard_iface *hard_iface;
struct batadv_elp_packet *elp_packet;
struct sk_buff *skb;
/* update orig field of every elp iface belonging to this mesh */
rcu_read_lock();
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
if (primary_iface->soft_iface != hard_iface->soft_iface)
continue;
if (!hard_iface->bat_v.elp_skb)
continue;
skb = hard_iface->bat_v.elp_skb;
elp_packet = (struct batadv_elp_packet *)skb->data;
ether_addr_copy(elp_packet->orig,
primary_iface->net_dev->dev_addr);
}
rcu_read_unlock();
}
/**
* batadv_v_elp_neigh_update - update an ELP neighbour node
* @bat_priv: the bat priv with all the soft interface information
* @neigh_addr: the neighbour interface address
* @if_incoming: the interface the packet was received through
* @elp_packet: the received ELP packet
*
* Updates the ELP neighbour node state with the data received within the new
* ELP packet.
*/
static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv,
u8 *neigh_addr,
struct batadv_hard_iface *if_incoming,
struct batadv_elp_packet *elp_packet)
{
struct batadv_neigh_node *neigh;
struct batadv_orig_node *orig_neigh;
struct batadv_hardif_neigh_node *hardif_neigh;
s32 seqno_diff;
s32 elp_latest_seqno;
orig_neigh = batadv_v_ogm_orig_get(bat_priv, elp_packet->orig);
if (!orig_neigh)
return;
neigh = batadv_neigh_node_new(orig_neigh, if_incoming, neigh_addr);
if (!neigh)
goto orig_free;
hardif_neigh = batadv_hardif_neigh_get(if_incoming, neigh_addr);
if (!hardif_neigh)
goto neigh_free;
elp_latest_seqno = hardif_neigh->bat_v.elp_latest_seqno;
seqno_diff = ntohl(elp_packet->seqno) - elp_latest_seqno;
/* known or older sequence numbers are ignored. However always adopt
* if the router seems to have been restarted.
*/
if (seqno_diff < 1 && seqno_diff > -BATADV_ELP_MAX_AGE)
goto hardif_free;
neigh->last_seen = jiffies;
hardif_neigh->last_seen = jiffies;
hardif_neigh->bat_v.elp_latest_seqno = ntohl(elp_packet->seqno);
hardif_neigh->bat_v.elp_interval = ntohl(elp_packet->elp_interval);
hardif_free:
if (hardif_neigh)
batadv_hardif_neigh_put(hardif_neigh);
neigh_free:
if (neigh)
batadv_neigh_node_put(neigh);
orig_free:
if (orig_neigh)
batadv_orig_node_put(orig_neigh);
}
/**
* batadv_v_elp_packet_recv - main ELP packet handler
* @skb: the received packet
* @if_incoming: the interface this packet was received through
*
* Return: NET_RX_SUCCESS and consumes the skb if the packet was peoperly
* processed or NET_RX_DROP in case of failure.
*/
int batadv_v_elp_packet_recv(struct sk_buff *skb,
struct batadv_hard_iface *if_incoming)
{
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
struct batadv_elp_packet *elp_packet;
struct batadv_hard_iface *primary_if;
struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb);
bool ret;
ret = batadv_check_management_packet(skb, if_incoming, BATADV_ELP_HLEN);
if (!ret)
return NET_RX_DROP;
if (batadv_is_my_mac(bat_priv, ethhdr->h_source))
return NET_RX_DROP;
/* did we receive a B.A.T.M.A.N. V ELP packet on an interface
* that does not have B.A.T.M.A.N. V ELP enabled ?
*/
if (strcmp(bat_priv->bat_algo_ops->name, "BATMAN_V") != 0)
return NET_RX_DROP;
elp_packet = (struct batadv_elp_packet *)skb->data;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Received ELP packet from %pM seqno %u ORIG: %pM\n",
ethhdr->h_source, ntohl(elp_packet->seqno),
elp_packet->orig);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if)
goto out;
batadv_v_elp_neigh_update(bat_priv, ethhdr->h_source, if_incoming,
elp_packet);
out:
if (primary_if)
batadv_hardif_put(primary_if);
consume_skb(skb);
return NET_RX_SUCCESS;
}

View File

@@ -0,0 +1,33 @@
/* Copyright (C) 2013-2016 B.A.T.M.A.N. contributors:
*
* Linus Lüssing, Marek Lindner
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "main.h"
#ifndef _NET_BATMAN_ADV_BAT_V_ELP_H_
#define _NET_BATMAN_ADV_BAT_V_ELP_H_
struct sk_buff;
struct work_struct;
int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface);
void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface);
void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface);
int batadv_v_elp_packet_recv(struct sk_buff *skb,
struct batadv_hard_iface *if_incoming);
void batadv_v_elp_throughput_metric_update(struct work_struct *work);
#endif /* _NET_BATMAN_ADV_BAT_V_ELP_H_ */

833
net/batman-adv/bat_v_ogm.c Normal file
View File

@@ -0,0 +1,833 @@
/* Copyright (C) 2013-2016 B.A.T.M.A.N. contributors:
*
* Antonio Quartulli
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "bat_v_ogm.h"
#include "main.h"
#include <linux/atomic.h>
#include <linux/byteorder/generic.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/fs.h>
#include <linux/if_ether.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/random.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include "hard-interface.h"
#include "hash.h"
#include "originator.h"
#include "packet.h"
#include "routing.h"
#include "send.h"
#include "translation-table.h"
/**
* batadv_v_ogm_orig_get - retrieve and possibly create an originator node
* @bat_priv: the bat priv with all the soft interface information
* @addr: the address of the originator
*
* Return: the orig_node corresponding to the specified address. If such object
* does not exist it is allocated here. In case of allocation failure returns
* NULL.
*/
struct batadv_orig_node *batadv_v_ogm_orig_get(struct batadv_priv *bat_priv,
const u8 *addr)
{
struct batadv_orig_node *orig_node;
int hash_added;
orig_node = batadv_orig_hash_find(bat_priv, addr);
if (orig_node)
return orig_node;
orig_node = batadv_orig_node_new(bat_priv, addr);
if (!orig_node)
return NULL;
hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig,
batadv_choose_orig, orig_node,
&orig_node->hash_entry);
if (hash_added != 0) {
/* orig_node->refcounter is initialised to 2 by
* batadv_orig_node_new()
*/
batadv_orig_node_put(orig_node);
batadv_orig_node_put(orig_node);
orig_node = NULL;
}
return orig_node;
}
/**
* batadv_v_ogm_start_timer - restart the OGM sending timer
* @bat_priv: the bat priv with all the soft interface information
*/
static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
{
unsigned long msecs;
/* this function may be invoked in different contexts (ogm rescheduling
* or hard_iface activation), but the work timer should not be reset
*/
if (delayed_work_pending(&bat_priv->bat_v.ogm_wq))
return;
msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER;
msecs += prandom_u32() % (2 * BATADV_JITTER);
queue_delayed_work(batadv_event_workqueue, &bat_priv->bat_v.ogm_wq,
msecs_to_jiffies(msecs));
}
/**
* batadv_v_ogm_send_to_if - send a batman ogm using a given interface
* @skb: the OGM to send
* @hard_iface: the interface to use to send the OGM
*/
static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
if (hard_iface->if_status != BATADV_IF_ACTIVE)
return;
batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_TX);
batadv_add_counter(bat_priv, BATADV_CNT_MGMT_TX_BYTES,
skb->len + ETH_HLEN);
batadv_send_broadcast_skb(skb, hard_iface);
}
/**
* batadv_v_ogm_send - periodic worker broadcasting the own OGM
* @work: work queue item
*/
static void batadv_v_ogm_send(struct work_struct *work)
{
struct batadv_hard_iface *hard_iface;
struct batadv_priv_bat_v *bat_v;
struct batadv_priv *bat_priv;
struct batadv_ogm2_packet *ogm_packet;
struct sk_buff *skb, *skb_tmp;
unsigned char *ogm_buff, *pkt_buff;
int ogm_buff_len;
u16 tvlv_len = 0;
bat_v = container_of(work, struct batadv_priv_bat_v, ogm_wq.work);
bat_priv = container_of(bat_v, struct batadv_priv, bat_v);
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
goto out;
ogm_buff = bat_priv->bat_v.ogm_buff;
ogm_buff_len = bat_priv->bat_v.ogm_buff_len;
/* tt changes have to be committed before the tvlv data is
* appended as it may alter the tt tvlv container
*/
batadv_tt_local_commit_changes(bat_priv);
tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff,
&ogm_buff_len,
BATADV_OGM2_HLEN);
bat_priv->bat_v.ogm_buff = ogm_buff;
bat_priv->bat_v.ogm_buff_len = ogm_buff_len;
skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len);
if (!skb)
goto reschedule;
skb_reserve(skb, ETH_HLEN);
pkt_buff = skb_put(skb, ogm_buff_len);
memcpy(pkt_buff, ogm_buff, ogm_buff_len);
ogm_packet = (struct batadv_ogm2_packet *)skb->data;
ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno));
atomic_inc(&bat_priv->bat_v.ogm_seqno);
ogm_packet->tvlv_len = htons(tvlv_len);
/* broadcast on every interface */
rcu_read_lock();
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
if (hard_iface->soft_iface != bat_priv->soft_iface)
continue;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Sending own OGM2 packet (originator %pM, seqno %u, throughput %u, TTL %d) on interface %s [%pM]\n",
ogm_packet->orig, ntohl(ogm_packet->seqno),
ntohl(ogm_packet->throughput), ogm_packet->ttl,
hard_iface->net_dev->name,
hard_iface->net_dev->dev_addr);
/* this skb gets consumed by batadv_v_ogm_send_to_if() */
skb_tmp = skb_clone(skb, GFP_ATOMIC);
if (!skb_tmp)
break;
batadv_v_ogm_send_to_if(skb_tmp, hard_iface);
}
rcu_read_unlock();
consume_skb(skb);
reschedule:
batadv_v_ogm_start_timer(bat_priv);
out:
return;
}
/**
* batadv_v_ogm_iface_enable - prepare an interface for B.A.T.M.A.N. V
* @hard_iface: the interface to prepare
*
* Takes care of scheduling own OGM sending routine for this interface.
*
* Return: 0 on success or a negative error code otherwise
*/
int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
batadv_v_ogm_start_timer(bat_priv);
return 0;
}
/**
* batadv_v_ogm_primary_iface_set - set a new primary interface
* @primary_iface: the new primary interface
*/
void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *primary_iface)
{
struct batadv_priv *bat_priv = netdev_priv(primary_iface->soft_iface);
struct batadv_ogm2_packet *ogm_packet;
if (!bat_priv->bat_v.ogm_buff)
return;
ogm_packet = (struct batadv_ogm2_packet *)bat_priv->bat_v.ogm_buff;
ether_addr_copy(ogm_packet->orig, primary_iface->net_dev->dev_addr);
}
/**
* batadv_v_ogm_orig_update - update the originator status based on the received
* OGM
* @bat_priv: the bat priv with all the soft interface information
* @orig_node: the originator to update
* @neigh_node: the neighbour the OGM has been received from (to update)
* @ogm2: the received OGM
* @if_outgoing: the interface where this OGM is going to be forwarded through
*/
static void
batadv_v_ogm_orig_update(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
struct batadv_neigh_node *neigh_node,
const struct batadv_ogm2_packet *ogm2,
struct batadv_hard_iface *if_outgoing)
{
struct batadv_neigh_ifinfo *router_ifinfo = NULL, *neigh_ifinfo = NULL;
struct batadv_neigh_node *router = NULL;
s32 neigh_seq_diff;
u32 neigh_last_seqno;
u32 router_last_seqno;
u32 router_throughput, neigh_throughput;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Searching and updating originator entry of received packet\n");
/* if this neighbor already is our next hop there is nothing
* to change
*/
router = batadv_orig_router_get(orig_node, if_outgoing);
if (router == neigh_node)
goto out;
/* don't consider neighbours with worse throughput.
* also switch route if this seqno is BATADV_V_MAX_ORIGDIFF newer than
* the last received seqno from our best next hop.
*/
if (router) {
router_ifinfo = batadv_neigh_ifinfo_get(router, if_outgoing);
neigh_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
/* if these are not allocated, something is wrong. */
if (!router_ifinfo || !neigh_ifinfo)
goto out;
neigh_last_seqno = neigh_ifinfo->bat_v.last_seqno;
router_last_seqno = router_ifinfo->bat_v.last_seqno;
neigh_seq_diff = neigh_last_seqno - router_last_seqno;
router_throughput = router_ifinfo->bat_v.throughput;
neigh_throughput = neigh_ifinfo->bat_v.throughput;
if ((neigh_seq_diff < BATADV_OGM_MAX_ORIGDIFF) &&
(router_throughput >= neigh_throughput))
goto out;
}
batadv_update_route(bat_priv, orig_node, if_outgoing, neigh_node);
out:
if (router_ifinfo)
batadv_neigh_ifinfo_put(router_ifinfo);
if (neigh_ifinfo)
batadv_neigh_ifinfo_put(neigh_ifinfo);
if (router)
batadv_neigh_node_put(router);
}
/**
* batadv_v_forward_penalty - apply a penalty to the throughput metric forwarded
* with B.A.T.M.A.N. V OGMs
* @bat_priv: the bat priv with all the soft interface information
* @if_incoming: the interface where the OGM has been received
* @if_outgoing: the interface where the OGM has to be forwarded to
* @throughput: the current throughput
*
* Apply a penalty on the current throughput metric value based on the
* characteristic of the interface where the OGM has been received. The return
* value is computed as follows:
* - throughput * 50% if the incoming and outgoing interface are the
* same WiFi interface and the throughput is above
* 1MBit/s
* - throughput if the outgoing interface is the default
* interface (i.e. this OGM is processed for the
* internal table and not forwarded)
* - throughput * hop penalty otherwise
*
* Return: the penalised throughput metric.
*/
static u32 batadv_v_forward_penalty(struct batadv_priv *bat_priv,
struct batadv_hard_iface *if_incoming,
struct batadv_hard_iface *if_outgoing,
u32 throughput)
{
int hop_penalty = atomic_read(&bat_priv->hop_penalty);
int hop_penalty_max = BATADV_TQ_MAX_VALUE;
/* Don't apply hop penalty in default originator table. */
if (if_outgoing == BATADV_IF_DEFAULT)
return throughput;
/* Forwarding on the same WiFi interface cuts the throughput in half
* due to the store & forward characteristics of WIFI.
* Very low throughput values are the exception.
*/
if ((throughput > 10) &&
(if_incoming == if_outgoing) &&
!(if_incoming->bat_v.flags & BATADV_FULL_DUPLEX))
return throughput / 2;
/* hop penalty of 255 equals 100% */
return throughput * (hop_penalty_max - hop_penalty) / hop_penalty_max;
}
/**
* batadv_v_ogm_forward - forward an OGM to the given outgoing interface
* @bat_priv: the bat priv with all the soft interface information
* @ogm_received: previously received OGM to be forwarded
* @throughput: throughput to announce, may vary per outgoing interface
* @if_incoming: the interface on which this OGM was received on
* @if_outgoing: the interface to which the OGM has to be forwarded to
*
* Forward an OGM to an interface after having altered the throughput metric and
* the TTL value contained in it. The original OGM isn't modified.
*/
static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
const struct batadv_ogm2_packet *ogm_received,
u32 throughput,
struct batadv_hard_iface *if_incoming,
struct batadv_hard_iface *if_outgoing)
{
struct batadv_ogm2_packet *ogm_forward;
unsigned char *skb_buff;
struct sk_buff *skb;
size_t packet_len;
u16 tvlv_len;
if (ogm_received->ttl <= 1) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "ttl exceeded\n");
return;
}
tvlv_len = ntohs(ogm_received->tvlv_len);
packet_len = BATADV_OGM2_HLEN + tvlv_len;
skb = netdev_alloc_skb_ip_align(if_outgoing->net_dev,
ETH_HLEN + packet_len);
if (!skb)
return;
skb_reserve(skb, ETH_HLEN);
skb_buff = skb_put(skb, packet_len);
memcpy(skb_buff, ogm_received, packet_len);
/* apply forward penalty */
ogm_forward = (struct batadv_ogm2_packet *)skb_buff;
ogm_forward->throughput = htonl(throughput);
ogm_forward->ttl--;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Forwarding OGM2 packet on %s: throughput %u, ttl %u, received via %s\n",
if_outgoing->net_dev->name, throughput, ogm_forward->ttl,
if_incoming->net_dev->name);
batadv_v_ogm_send_to_if(skb, if_outgoing);
}
/**
* batadv_v_ogm_metric_update - update route metric based on OGM
* @bat_priv: the bat priv with all the soft interface information
* @ogm2: OGM2 structure
* @orig_node: Originator structure for which the OGM has been received
* @neigh_node: the neigh_node through with the OGM has been received
* @if_incoming: the interface where this packet was received
* @if_outgoing: the interface for which the packet should be considered
*
* Return:
* 1 if the OGM is new,
* 0 if it is not new but valid,
* <0 on error (e.g. old OGM)
*/
static int batadv_v_ogm_metric_update(struct batadv_priv *bat_priv,
const struct batadv_ogm2_packet *ogm2,
struct batadv_orig_node *orig_node,
struct batadv_neigh_node *neigh_node,
struct batadv_hard_iface *if_incoming,
struct batadv_hard_iface *if_outgoing)
{
struct batadv_orig_ifinfo *orig_ifinfo = NULL;
struct batadv_neigh_ifinfo *neigh_ifinfo = NULL;
bool protection_started = false;
int ret = -EINVAL;
u32 path_throughput;
s32 seq_diff;
orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing);
if (!orig_ifinfo)
goto out;
seq_diff = ntohl(ogm2->seqno) - orig_ifinfo->last_real_seqno;
if (!hlist_empty(&orig_node->neigh_list) &&
batadv_window_protected(bat_priv, seq_diff,
BATADV_OGM_MAX_AGE,
&orig_ifinfo->batman_seqno_reset,
&protection_started)) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Drop packet: packet within window protection time from %pM\n",
ogm2->orig);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Last reset: %ld, %ld\n",
orig_ifinfo->batman_seqno_reset, jiffies);
goto out;
}
/* drop packets with old seqnos, however accept the first packet after
* a host has been rebooted.
*/
if ((seq_diff < 0) && !protection_started)
goto out;
neigh_node->last_seen = jiffies;
orig_node->last_seen = jiffies;
orig_ifinfo->last_real_seqno = ntohl(ogm2->seqno);
orig_ifinfo->last_ttl = ogm2->ttl;
neigh_ifinfo = batadv_neigh_ifinfo_new(neigh_node, if_outgoing);
if (!neigh_ifinfo)
goto out;
path_throughput = batadv_v_forward_penalty(bat_priv, if_incoming,
if_outgoing,
ntohl(ogm2->throughput));
neigh_ifinfo->bat_v.throughput = path_throughput;
neigh_ifinfo->bat_v.last_seqno = ntohl(ogm2->seqno);
neigh_ifinfo->last_ttl = ogm2->ttl;
if (seq_diff > 0 || protection_started)
ret = 1;
else
ret = 0;
out:
if (orig_ifinfo)
batadv_orig_ifinfo_put(orig_ifinfo);
if (neigh_ifinfo)
batadv_neigh_ifinfo_put(neigh_ifinfo);
return ret;
}
/**
* batadv_v_ogm_route_update - update routes based on OGM
* @bat_priv: the bat priv with all the soft interface information
* @ethhdr: the Ethernet header of the OGM2
* @ogm2: OGM2 structure
* @orig_node: Originator structure for which the OGM has been received
* @neigh_node: the neigh_node through with the OGM has been received
* @if_incoming: the interface where this packet was received
* @if_outgoing: the interface for which the packet should be considered
*/
static void batadv_v_ogm_route_update(struct batadv_priv *bat_priv,
const struct ethhdr *ethhdr,
const struct batadv_ogm2_packet *ogm2,
struct batadv_orig_node *orig_node,
struct batadv_neigh_node *neigh_node,
struct batadv_hard_iface *if_incoming,
struct batadv_hard_iface *if_outgoing)
{
struct batadv_neigh_node *router = NULL;
struct batadv_neigh_ifinfo *neigh_ifinfo = NULL;
struct batadv_orig_node *orig_neigh_node = NULL;
struct batadv_orig_ifinfo *orig_ifinfo = NULL;
struct batadv_neigh_node *orig_neigh_router = NULL;
neigh_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
if (!neigh_ifinfo)
goto out;
orig_neigh_node = batadv_v_ogm_orig_get(bat_priv, ethhdr->h_source);
if (!orig_neigh_node)
goto out;
orig_neigh_router = batadv_orig_router_get(orig_neigh_node,
if_outgoing);
/* drop packet if sender is not a direct neighbor and if we
* don't route towards it
*/
router = batadv_orig_router_get(orig_node, if_outgoing);
if (router && router->orig_node != orig_node && !orig_neigh_router) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Drop packet: OGM via unknown neighbor!\n");
goto out;
}
if (router)
batadv_neigh_node_put(router);
/* Update routes, and check if the OGM is from the best next hop */
batadv_v_ogm_orig_update(bat_priv, orig_node, neigh_node, ogm2,
if_outgoing);
orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing);
if (!orig_ifinfo)
goto out;
/* don't forward the same seqno twice on one interface */
if (orig_ifinfo->last_seqno_forwarded == ntohl(ogm2->seqno))
goto out;
/* acquire possibly updated router */
router = batadv_orig_router_get(orig_node, if_outgoing);
/* strict rule: forward packets coming from the best next hop only */
if (neigh_node != router)
goto out;
/* only forward for specific interface, not for the default one. */
if (if_outgoing != BATADV_IF_DEFAULT) {
orig_ifinfo->last_seqno_forwarded = ntohl(ogm2->seqno);
batadv_v_ogm_forward(bat_priv, ogm2,
neigh_ifinfo->bat_v.throughput,
if_incoming, if_outgoing);
}
out:
if (orig_ifinfo)
batadv_orig_ifinfo_put(orig_ifinfo);
if (router)
batadv_neigh_node_put(router);
if (orig_neigh_router)
batadv_neigh_node_put(orig_neigh_router);
if (orig_neigh_node)
batadv_orig_node_put(orig_neigh_node);
if (neigh_ifinfo)
batadv_neigh_ifinfo_put(neigh_ifinfo);
}
/**
* batadv_v_ogm_process_per_outif - process a batman v OGM for an outgoing if
* @bat_priv: the bat priv with all the soft interface information
* @ethhdr: the Ethernet header of the OGM2
* @ogm2: OGM2 structure
* @orig_node: Originator structure for which the OGM has been received
* @neigh_node: the neigh_node through with the OGM has been received
* @if_incoming: the interface where this packet was received
* @if_outgoing: the interface for which the packet should be considered
*/
static void
batadv_v_ogm_process_per_outif(struct batadv_priv *bat_priv,
const struct ethhdr *ethhdr,
const struct batadv_ogm2_packet *ogm2,
struct batadv_orig_node *orig_node,
struct batadv_neigh_node *neigh_node,
struct batadv_hard_iface *if_incoming,
struct batadv_hard_iface *if_outgoing)
{
int seqno_age;
/* first, update the metric with according sanity checks */
seqno_age = batadv_v_ogm_metric_update(bat_priv, ogm2, orig_node,
neigh_node, if_incoming,
if_outgoing);
/* outdated sequence numbers are to be discarded */
if (seqno_age < 0)
return;
/* only unknown & newer OGMs contain TVLVs we are interested in */
if ((seqno_age > 0) && (if_outgoing == BATADV_IF_DEFAULT))
batadv_tvlv_containers_process(bat_priv, true, orig_node,
NULL, NULL,
(unsigned char *)(ogm2 + 1),
ntohs(ogm2->tvlv_len));
/* if the metric update went through, update routes if needed */
batadv_v_ogm_route_update(bat_priv, ethhdr, ogm2, orig_node,
neigh_node, if_incoming, if_outgoing);
}
/**
* batadv_v_ogm_aggr_packet - checks if there is another OGM aggregated
* @buff_pos: current position in the skb
* @packet_len: total length of the skb
* @tvlv_len: tvlv length of the previously considered OGM
*
* Return: true if there is enough space for another OGM, false otherwise.
*/
static bool batadv_v_ogm_aggr_packet(int buff_pos, int packet_len,
__be16 tvlv_len)
{
int next_buff_pos = 0;
next_buff_pos += buff_pos + BATADV_OGM2_HLEN;
next_buff_pos += ntohs(tvlv_len);
return (next_buff_pos <= packet_len) &&
(next_buff_pos <= BATADV_MAX_AGGREGATION_BYTES);
}
/**
* batadv_v_ogm_process - process an incoming batman v OGM
* @skb: the skb containing the OGM
* @ogm_offset: offset to the OGM which should be processed (for aggregates)
* @if_incoming: the interface where this packet was receved
*/
static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset,
struct batadv_hard_iface *if_incoming)
{
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
struct ethhdr *ethhdr;
struct batadv_orig_node *orig_node = NULL;
struct batadv_hardif_neigh_node *hardif_neigh = NULL;
struct batadv_neigh_node *neigh_node = NULL;
struct batadv_hard_iface *hard_iface;
struct batadv_ogm2_packet *ogm_packet;
u32 ogm_throughput, link_throughput, path_throughput;
ethhdr = eth_hdr(skb);
ogm_packet = (struct batadv_ogm2_packet *)(skb->data + ogm_offset);
ogm_throughput = ntohl(ogm_packet->throughput);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Received OGM2 packet via NB: %pM, IF: %s [%pM] (from OG: %pM, seqno %u, troughput %u, TTL %u, V %u, tvlv_len %u)\n",
ethhdr->h_source, if_incoming->net_dev->name,
if_incoming->net_dev->dev_addr, ogm_packet->orig,
ntohl(ogm_packet->seqno), ogm_throughput, ogm_packet->ttl,
ogm_packet->version, ntohs(ogm_packet->tvlv_len));
/* If the troughput metric is 0, immediately drop the packet. No need to
* create orig_node / neigh_node for an unusable route.
*/
if (ogm_throughput == 0) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Drop packet: originator packet with troughput metric of 0\n");
return;
}
/* require ELP packets be to received from this neighbor first */
hardif_neigh = batadv_hardif_neigh_get(if_incoming, ethhdr->h_source);
if (!hardif_neigh) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Drop packet: OGM via unknown neighbor!\n");
goto out;
}
orig_node = batadv_v_ogm_orig_get(bat_priv, ogm_packet->orig);
if (!orig_node)
return;
neigh_node = batadv_neigh_node_new(orig_node, if_incoming,
ethhdr->h_source);
if (!neigh_node)
goto out;
/* Update the received throughput metric to match the link
* characteristic:
* - If this OGM traveled one hop so far (emitted by single hop
* neighbor) the path throughput metric equals the link throughput.
* - For OGMs traversing more than hop the path throughput metric is
* the smaller of the path throughput and the link throughput.
*/
link_throughput = ewma_throughput_read(&hardif_neigh->bat_v.throughput);
path_throughput = min_t(u32, link_throughput, ogm_throughput);
ogm_packet->throughput = htonl(path_throughput);
batadv_v_ogm_process_per_outif(bat_priv, ethhdr, ogm_packet, orig_node,
neigh_node, if_incoming,
BATADV_IF_DEFAULT);
rcu_read_lock();
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
if (hard_iface->if_status != BATADV_IF_ACTIVE)
continue;
if (hard_iface->soft_iface != bat_priv->soft_iface)
continue;
batadv_v_ogm_process_per_outif(bat_priv, ethhdr, ogm_packet,
orig_node, neigh_node,
if_incoming, hard_iface);
}
rcu_read_unlock();
out:
if (orig_node)
batadv_orig_node_put(orig_node);
if (neigh_node)
batadv_neigh_node_put(neigh_node);
if (hardif_neigh)
batadv_hardif_neigh_put(hardif_neigh);
}
/**
* batadv_v_ogm_packet_recv - OGM2 receiving handler
* @skb: the received OGM
* @if_incoming: the interface where this OGM has been received
*
* Return: NET_RX_SUCCESS and consume the skb on success or returns NET_RX_DROP
* (without freeing the skb) on failure
*/
int batadv_v_ogm_packet_recv(struct sk_buff *skb,
struct batadv_hard_iface *if_incoming)
{
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
struct batadv_ogm2_packet *ogm_packet;
struct ethhdr *ethhdr = eth_hdr(skb);
int ogm_offset;
u8 *packet_pos;
int ret = NET_RX_DROP;
/* did we receive a OGM2 packet on an interface that does not have
* B.A.T.M.A.N. V enabled ?
*/
if (strcmp(bat_priv->bat_algo_ops->name, "BATMAN_V") != 0)
return NET_RX_DROP;
if (!batadv_check_management_packet(skb, if_incoming, BATADV_OGM2_HLEN))
return NET_RX_DROP;
if (batadv_is_my_mac(bat_priv, ethhdr->h_source))
return NET_RX_DROP;
ogm_packet = (struct batadv_ogm2_packet *)skb->data;
if (batadv_is_my_mac(bat_priv, ogm_packet->orig))
return NET_RX_DROP;
batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_RX);
batadv_add_counter(bat_priv, BATADV_CNT_MGMT_RX_BYTES,
skb->len + ETH_HLEN);
ogm_offset = 0;
ogm_packet = (struct batadv_ogm2_packet *)skb->data;
while (batadv_v_ogm_aggr_packet(ogm_offset, skb_headlen(skb),
ogm_packet->tvlv_len)) {
batadv_v_ogm_process(skb, ogm_offset, if_incoming);
ogm_offset += BATADV_OGM2_HLEN;
ogm_offset += ntohs(ogm_packet->tvlv_len);
packet_pos = skb->data + ogm_offset;
ogm_packet = (struct batadv_ogm2_packet *)packet_pos;
}
ret = NET_RX_SUCCESS;
consume_skb(skb);
return ret;
}
/**
* batadv_v_ogm_init - initialise the OGM2 engine
* @bat_priv: the bat priv with all the soft interface information
*
* Return: 0 on success or a negative error code in case of failure
*/
int batadv_v_ogm_init(struct batadv_priv *bat_priv)
{
struct batadv_ogm2_packet *ogm_packet;
unsigned char *ogm_buff;
u32 random_seqno;
bat_priv->bat_v.ogm_buff_len = BATADV_OGM2_HLEN;
ogm_buff = kzalloc(bat_priv->bat_v.ogm_buff_len, GFP_ATOMIC);
if (!ogm_buff)
return -ENOMEM;
bat_priv->bat_v.ogm_buff = ogm_buff;
ogm_packet = (struct batadv_ogm2_packet *)ogm_buff;
ogm_packet->packet_type = BATADV_OGM2;
ogm_packet->version = BATADV_COMPAT_VERSION;
ogm_packet->ttl = BATADV_TTL;
ogm_packet->flags = BATADV_NO_FLAGS;
ogm_packet->throughput = htonl(BATADV_THROUGHPUT_MAX_VALUE);
/* randomize initial seqno to avoid collision */
get_random_bytes(&random_seqno, sizeof(random_seqno));
atomic_set(&bat_priv->bat_v.ogm_seqno, random_seqno);
INIT_DELAYED_WORK(&bat_priv->bat_v.ogm_wq, batadv_v_ogm_send);
return 0;
}
/**
* batadv_v_ogm_free - free OGM private resources
* @bat_priv: the bat priv with all the soft interface information
*/
void batadv_v_ogm_free(struct batadv_priv *bat_priv)
{
cancel_delayed_work_sync(&bat_priv->bat_v.ogm_wq);
kfree(bat_priv->bat_v.ogm_buff);
bat_priv->bat_v.ogm_buff = NULL;
bat_priv->bat_v.ogm_buff_len = 0;
}

View File

@@ -0,0 +1,36 @@
/* Copyright (C) 2013-2016 B.A.T.M.A.N. contributors:
*
* Antonio Quartulli
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _BATMAN_ADV_BATADV_V_OGM_H_
#define _BATMAN_ADV_BATADV_V_OGM_H_
#include <linux/types.h>
struct batadv_hard_iface;
struct batadv_priv;
struct sk_buff;
int batadv_v_ogm_init(struct batadv_priv *bat_priv);
void batadv_v_ogm_free(struct batadv_priv *bat_priv);
int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface);
struct batadv_orig_node *batadv_v_ogm_orig_get(struct batadv_priv *bat_priv,
const u8 *addr);
void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *primary_iface);
int batadv_v_ogm_packet_recv(struct sk_buff *skb,
struct batadv_hard_iface *if_incoming);
#endif /* _BATMAN_ADV_BATADV_V_OGM_H_ */

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2006-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2006-2016 B.A.T.M.A.N. contributors:
*
* Simon Wunderlich, Marek Lindner
*
@@ -29,10 +29,16 @@ static void batadv_bitmap_shift_left(unsigned long *seq_bits, s32 n)
bitmap_shift_left(seq_bits, seq_bits, n, BATADV_TQ_LOCAL_WINDOW_SIZE);
}
/* receive and process one packet within the sequence number window.
/**
* batadv_bit_get_packet - receive and process one packet within the sequence
* number window
* @priv: the bat priv with all the soft interface information
* @seq_bits: pointer to the sequence number receive packet
* @seq_num_diff: difference between the current/received sequence number and
* the last sequence number
* @set_mark: whether this packet should be marked in seq_bits
*
* returns:
* 1 if the window was moved (either new or very old)
* Return: 1 if the window was moved (either new or very old),
* 0 if the window was not moved/shifted.
*/
int batadv_bit_get_packet(void *priv, unsigned long *seq_bits, s32 seq_num_diff,

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2006-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2006-2016 B.A.T.M.A.N. contributors:
*
* Simon Wunderlich, Marek Lindner
*
@@ -24,7 +24,14 @@
#include <linux/compiler.h>
#include <linux/types.h>
/* Returns 1 if the corresponding bit in the given seq_bits indicates true
/**
* batadv_test_bit - check if bit is set in the current window
*
* @seq_bits: pointer to the sequence number receive packet
* @last_seqno: latest sequence number in seq_bits
* @curr_seqno: sequence number to test for
*
* Return: 1 if the corresponding bit in the given seq_bits indicates true
* and curr_seqno is within range of last_seqno. Otherwise returns 0.
*/
static inline int batadv_test_bit(const unsigned long *seq_bits,
@@ -48,9 +55,6 @@ static inline void batadv_set_bit(unsigned long *seq_bits, s32 n)
set_bit(n, seq_bits); /* turn the position on */
}
/* receive and process one packet, returns 1 if received seq_num is considered
* new, 0 if old
*/
int batadv_bit_get_packet(void *priv, unsigned long *seq_bits, s32 seq_num_diff,
int set_mark);

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2011-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2011-2016 B.A.T.M.A.N. contributors:
*
* Simon Wunderlich
*
@@ -31,6 +31,7 @@
#include <linux/jhash.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/netdevice.h>
@@ -58,7 +59,13 @@ static void
batadv_bla_send_announce(struct batadv_priv *bat_priv,
struct batadv_bla_backbone_gw *backbone_gw);
/* return the index of the claim */
/**
* batadv_choose_claim - choose the right bucket for a claim.
* @data: data to hash
* @size: size of the hash table
*
* Return: the hash index of the claim
*/
static inline u32 batadv_choose_claim(const void *data, u32 size)
{
struct batadv_bla_claim *claim = (struct batadv_bla_claim *)data;
@@ -70,7 +77,13 @@ static inline u32 batadv_choose_claim(const void *data, u32 size)
return hash % size;
}
/* return the index of the backbone gateway */
/**
* batadv_choose_backbone_gw - choose the right bucket for a backbone gateway.
* @data: data to hash
* @size: size of the hash table
*
* Return: the hash index of the backbone gateway
*/
static inline u32 batadv_choose_backbone_gw(const void *data, u32 size)
{
const struct batadv_bla_claim *claim = (struct batadv_bla_claim *)data;
@@ -82,7 +95,13 @@ static inline u32 batadv_choose_backbone_gw(const void *data, u32 size)
return hash % size;
}
/* compares address and vid of two backbone gws */
/**
* batadv_compare_backbone_gw - compare address and vid of two backbone gws
* @node: list node of the first entry to compare
* @data2: pointer to the second backbone gateway
*
* Return: 1 if the backbones have the same data, 0 otherwise
*/
static int batadv_compare_backbone_gw(const struct hlist_node *node,
const void *data2)
{
@@ -100,7 +119,13 @@ static int batadv_compare_backbone_gw(const struct hlist_node *node,
return 1;
}
/* compares address and vid of two claims */
/**
* batadv_compare_backbone_gw - compare address and vid of two claims
* @node: list node of the first entry to compare
* @data2: pointer to the second claims
*
* Return: 1 if the claim have the same data, 0 otherwise
*/
static int batadv_compare_claim(const struct hlist_node *node,
const void *data2)
{
@@ -118,35 +143,62 @@ static int batadv_compare_claim(const struct hlist_node *node,
return 1;
}
/* free a backbone gw */
static void
batadv_backbone_gw_free_ref(struct batadv_bla_backbone_gw *backbone_gw)
/**
* batadv_backbone_gw_release - release backbone gw from lists and queue for
* free after rcu grace period
* @ref: kref pointer of the backbone gw
*/
static void batadv_backbone_gw_release(struct kref *ref)
{
if (atomic_dec_and_test(&backbone_gw->refcount))
kfree_rcu(backbone_gw, rcu);
}
struct batadv_bla_backbone_gw *backbone_gw;
/* finally deinitialize the claim */
static void batadv_claim_release(struct batadv_bla_claim *claim)
{
batadv_backbone_gw_free_ref(claim->backbone_gw);
kfree_rcu(claim, rcu);
}
backbone_gw = container_of(ref, struct batadv_bla_backbone_gw,
refcount);
/* free a claim, call claim_free_rcu if its the last reference */
static void batadv_claim_free_ref(struct batadv_bla_claim *claim)
{
if (atomic_dec_and_test(&claim->refcount))
batadv_claim_release(claim);
kfree_rcu(backbone_gw, rcu);
}
/**
* batadv_claim_hash_find
* batadv_backbone_gw_put - decrement the backbone gw refcounter and possibly
* release it
* @backbone_gw: backbone gateway to be free'd
*/
static void batadv_backbone_gw_put(struct batadv_bla_backbone_gw *backbone_gw)
{
kref_put(&backbone_gw->refcount, batadv_backbone_gw_release);
}
/**
* batadv_claim_release - release claim from lists and queue for free after rcu
* grace period
* @ref: kref pointer of the claim
*/
static void batadv_claim_release(struct kref *ref)
{
struct batadv_bla_claim *claim;
claim = container_of(ref, struct batadv_bla_claim, refcount);
batadv_backbone_gw_put(claim->backbone_gw);
kfree_rcu(claim, rcu);
}
/**
* batadv_claim_put - decrement the claim refcounter and possibly
* release it
* @claim: claim to be free'd
*/
static void batadv_claim_put(struct batadv_bla_claim *claim)
{
kref_put(&claim->refcount, batadv_claim_release);
}
/**
* batadv_claim_hash_find - looks for a claim in the claim hash
* @bat_priv: the bat priv with all the soft interface information
* @data: search data (may be local/static data)
*
* looks for a claim in the hash, and returns it if found
* or NULL otherwise.
* Return: claim if found or NULL otherwise.
*/
static struct batadv_bla_claim
*batadv_claim_hash_find(struct batadv_priv *bat_priv,
@@ -169,7 +221,7 @@ static struct batadv_bla_claim
if (!batadv_compare_claim(&claim->hash_entry, data))
continue;
if (!atomic_inc_not_zero(&claim->refcount))
if (!kref_get_unless_zero(&claim->refcount))
continue;
claim_tmp = claim;
@@ -181,12 +233,12 @@ static struct batadv_bla_claim
}
/**
* batadv_backbone_hash_find - looks for a claim in the hash
* batadv_backbone_hash_find - looks for a backbone gateway in the hash
* @bat_priv: the bat priv with all the soft interface information
* @addr: the address of the originator
* @vid: the VLAN ID
*
* Returns claim if found or NULL otherwise.
* Return: backbone gateway if found or NULL otherwise
*/
static struct batadv_bla_backbone_gw *
batadv_backbone_hash_find(struct batadv_priv *bat_priv, u8 *addr,
@@ -213,7 +265,7 @@ batadv_backbone_hash_find(struct batadv_priv *bat_priv, u8 *addr,
&search_entry))
continue;
if (!atomic_inc_not_zero(&backbone_gw->refcount))
if (!kref_get_unless_zero(&backbone_gw->refcount))
continue;
backbone_gw_tmp = backbone_gw;
@@ -224,7 +276,10 @@ batadv_backbone_hash_find(struct batadv_priv *bat_priv, u8 *addr,
return backbone_gw_tmp;
}
/* delete all claims for a backbone */
/**
* batadv_bla_del_backbone_claims - delete all claims for a backbone
* @backbone_gw: backbone gateway where the claims should be removed
*/
static void
batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw)
{
@@ -249,7 +304,7 @@ batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw)
if (claim->backbone_gw != backbone_gw)
continue;
batadv_claim_free_ref(claim);
batadv_claim_put(claim);
hlist_del_rcu(&claim->hash_entry);
}
spin_unlock_bh(list_lock);
@@ -368,18 +423,17 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, u8 *mac,
netif_rx(skb);
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
}
/**
* batadv_bla_get_backbone_gw
* batadv_bla_get_backbone_gw - finds or creates a backbone gateway
* @bat_priv: the bat priv with all the soft interface information
* @orig: the mac address of the originator
* @vid: the VLAN ID
* @own_backbone: set if the requested backbone is local
*
* searches for the backbone gw or creates a new one if it could not
* be found.
* Return: the (possibly created) backbone gateway or NULL on error
*/
static struct batadv_bla_backbone_gw *
batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig,
@@ -412,7 +466,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig,
ether_addr_copy(entry->orig, orig);
/* one for the hash, one for returning */
atomic_set(&entry->refcount, 2);
kref_init(&entry->refcount);
kref_get(&entry->refcount);
hash_added = batadv_hash_add(bat_priv->bla.backbone_hash,
batadv_compare_backbone_gw,
@@ -430,7 +485,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig,
if (orig_node) {
batadv_tt_global_del_orig(bat_priv, orig_node, vid,
"became a backbone gateway");
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
}
if (own_backbone) {
@@ -445,7 +500,13 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig,
return entry;
}
/* update or add the own backbone gw to make sure we announce
/**
* batadv_bla_update_own_backbone_gw - updates the own backbone gw for a VLAN
* @bat_priv: the bat priv with all the soft interface information
* @primary_if: the selected primary interface
* @vid: VLAN identifier
*
* update or add the own backbone gw to make sure we announce
* where we receive other backbone gws
*/
static void
@@ -462,7 +523,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv,
return;
backbone_gw->lasttime = jiffies;
batadv_backbone_gw_free_ref(backbone_gw);
batadv_backbone_gw_put(backbone_gw);
}
/**
@@ -511,7 +572,7 @@ static void batadv_bla_answer_request(struct batadv_priv *bat_priv,
/* finally, send an announcement frame */
batadv_bla_send_announce(bat_priv, backbone_gw);
batadv_backbone_gw_free_ref(backbone_gw);
batadv_backbone_gw_put(backbone_gw);
}
/**
@@ -542,12 +603,9 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw)
}
/**
* batadv_bla_send_announce
* batadv_bla_send_announce - Send an announcement frame
* @bat_priv: the bat priv with all the soft interface information
* @backbone_gw: our backbone gateway which should be announced
*
* This function sends an announcement. It is called from multiple
* places.
*/
static void batadv_bla_send_announce(struct batadv_priv *bat_priv,
struct batadv_bla_backbone_gw *backbone_gw)
@@ -595,7 +653,8 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
claim->lasttime = jiffies;
claim->backbone_gw = backbone_gw;
atomic_set(&claim->refcount, 2);
kref_init(&claim->refcount);
kref_get(&claim->refcount);
batadv_dbg(BATADV_DBG_BLA, bat_priv,
"bla_add_claim(): adding new entry %pM, vid %d to hash ...\n",
mac, BATADV_PRINT_VID(vid));
@@ -622,10 +681,10 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
spin_lock_bh(&claim->backbone_gw->crc_lock);
claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
spin_unlock_bh(&claim->backbone_gw->crc_lock);
batadv_backbone_gw_free_ref(claim->backbone_gw);
batadv_backbone_gw_put(claim->backbone_gw);
}
/* set (new) backbone gw */
atomic_inc(&backbone_gw->refcount);
kref_get(&backbone_gw->refcount);
claim->backbone_gw = backbone_gw;
spin_lock_bh(&backbone_gw->crc_lock);
@@ -634,11 +693,14 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
backbone_gw->lasttime = jiffies;
claim_free_ref:
batadv_claim_free_ref(claim);
batadv_claim_put(claim);
}
/* Delete a claim from the claim hash which has the
* given mac address and vid.
/**
* batadv_bla_del_claim - delete a claim from the claim hash
* @bat_priv: the bat priv with all the soft interface information
* @mac: mac address of the claim to be removed
* @vid: VLAN id for the claim to be removed
*/
static void batadv_bla_del_claim(struct batadv_priv *bat_priv,
const u8 *mac, const unsigned short vid)
@@ -656,17 +718,25 @@ static void batadv_bla_del_claim(struct batadv_priv *bat_priv,
batadv_hash_remove(bat_priv->bla.claim_hash, batadv_compare_claim,
batadv_choose_claim, claim);
batadv_claim_free_ref(claim); /* reference from the hash is gone */
batadv_claim_put(claim); /* reference from the hash is gone */
spin_lock_bh(&claim->backbone_gw->crc_lock);
claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
spin_unlock_bh(&claim->backbone_gw->crc_lock);
/* don't need the reference from hash_find() anymore */
batadv_claim_free_ref(claim);
batadv_claim_put(claim);
}
/* check for ANNOUNCE frame, return 1 if handled */
/**
* batadv_handle_announce - check for ANNOUNCE frame
* @bat_priv: the bat priv with all the soft interface information
* @an_addr: announcement mac address (ARP Sender HW address)
* @backbone_addr: originator address of the sender (Ethernet source MAC)
* @vid: the VLAN ID of the frame
*
* Return: 1 if handled
*/
static int batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
u8 *backbone_addr, unsigned short vid)
{
@@ -712,11 +782,20 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
}
}
batadv_backbone_gw_free_ref(backbone_gw);
batadv_backbone_gw_put(backbone_gw);
return 1;
}
/* check for REQUEST frame, return 1 if handled */
/**
* batadv_handle_request - check for REQUEST frame
* @bat_priv: the bat priv with all the soft interface information
* @primary_if: the primary hard interface of this batman soft interface
* @backbone_addr: backbone address to be requested (ARP sender HW MAC)
* @ethhdr: ethernet header of a packet
* @vid: the VLAN ID of the frame
*
* Return: 1 if handled
*/
static int batadv_handle_request(struct batadv_priv *bat_priv,
struct batadv_hard_iface *primary_if,
u8 *backbone_addr, struct ethhdr *ethhdr,
@@ -740,7 +819,16 @@ static int batadv_handle_request(struct batadv_priv *bat_priv,
return 1;
}
/* check for UNCLAIM frame, return 1 if handled */
/**
* batadv_handle_unclaim - check for UNCLAIM frame
* @bat_priv: the bat priv with all the soft interface information
* @primary_if: the primary hard interface of this batman soft interface
* @backbone_addr: originator address of the backbone (Ethernet source)
* @claim_addr: Client to be unclaimed (ARP sender HW MAC)
* @vid: the VLAN ID of the frame
*
* Return: 1 if handled
*/
static int batadv_handle_unclaim(struct batadv_priv *bat_priv,
struct batadv_hard_iface *primary_if,
u8 *backbone_addr, u8 *claim_addr,
@@ -765,11 +853,20 @@ static int batadv_handle_unclaim(struct batadv_priv *bat_priv,
claim_addr, BATADV_PRINT_VID(vid), backbone_gw->orig);
batadv_bla_del_claim(bat_priv, claim_addr, vid);
batadv_backbone_gw_free_ref(backbone_gw);
batadv_backbone_gw_put(backbone_gw);
return 1;
}
/* check for CLAIM frame, return 1 if handled */
/**
* batadv_handle_claim - check for CLAIM frame
* @bat_priv: the bat priv with all the soft interface information
* @primary_if: the primary hard interface of this batman soft interface
* @backbone_addr: originator address of the backbone (Ethernet Source)
* @claim_addr: client mac address to be claimed (ARP sender HW MAC)
* @vid: the VLAN ID of the frame
*
* Return: 1 if handled
*/
static int batadv_handle_claim(struct batadv_priv *bat_priv,
struct batadv_hard_iface *primary_if,
u8 *backbone_addr, u8 *claim_addr,
@@ -793,12 +890,12 @@ static int batadv_handle_claim(struct batadv_priv *bat_priv,
/* TODO: we could call something like tt_local_del() here. */
batadv_backbone_gw_free_ref(backbone_gw);
batadv_backbone_gw_put(backbone_gw);
return 1;
}
/**
* batadv_check_claim_group
* batadv_check_claim_group - check for claim group membership
* @bat_priv: the bat priv with all the soft interface information
* @primary_if: the primary interface of this batman interface
* @hw_src: the Hardware source in the ARP Header
@@ -809,7 +906,7 @@ static int batadv_handle_claim(struct batadv_priv *bat_priv,
* This function also applies the group ID of the sender
* if it is in the same mesh.
*
* returns:
* Return:
* 2 - if it is a claim packet and on the same group
* 1 - if is a claim packet from another group
* 0 - if it is not a claim packet
@@ -867,20 +964,18 @@ static int batadv_check_claim_group(struct batadv_priv *bat_priv,
bla_dst_own->group = bla_dst->group;
}
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
return 2;
}
/**
* batadv_bla_process_claim
* batadv_bla_process_claim - Check if this is a claim frame, and process it
* @bat_priv: the bat priv with all the soft interface information
* @primary_if: the primary hard interface of this batman soft interface
* @skb: the frame to be checked
*
* Check if this is a claim frame, and process it accordingly.
*
* returns 1 if it was a claim frame, otherwise return 0 to
* Return: 1 if it was a claim frame, otherwise return 0 to
* tell the callee that it can use the frame on its own.
*/
static int batadv_bla_process_claim(struct batadv_priv *bat_priv,
@@ -1011,7 +1106,13 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv,
return 1;
}
/* Check when we last heard from other nodes, and remove them in case of
/**
* batadv_bla_purge_backbone_gw - Remove backbone gateways after a timeout or
* immediately
* @bat_priv: the bat priv with all the soft interface information
* @now: whether the whole hash shall be wiped now
*
* Check when we last heard from other nodes, and remove them in case of
* a time out, or clean all backbone gws if now is set.
*/
static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
@@ -1052,14 +1153,14 @@ purge_now:
batadv_bla_del_backbone_claims(backbone_gw);
hlist_del_rcu(&backbone_gw->hash_entry);
batadv_backbone_gw_free_ref(backbone_gw);
batadv_backbone_gw_put(backbone_gw);
}
spin_unlock_bh(list_lock);
}
}
/**
* batadv_bla_purge_claims
* batadv_bla_purge_claims - Remove claims after a timeout or immediately
* @bat_priv: the bat priv with all the soft interface information
* @primary_if: the selected primary interface, may be NULL if now is set
* @now: whether the whole hash shall be wiped now
@@ -1108,12 +1209,11 @@ purge_now:
}
/**
* batadv_bla_update_orig_address
* batadv_bla_update_orig_address - Update the backbone gateways when the own
* originator address changes
* @bat_priv: the bat priv with all the soft interface information
* @primary_if: the new selected primary_if
* @oldif: the old primary interface, may be NULL
*
* Update the backbone gateways when the own orig address changes.
*/
void batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
struct batadv_hard_iface *primary_if,
@@ -1181,10 +1281,14 @@ void batadv_bla_status_update(struct net_device *net_dev)
* so just call that one.
*/
batadv_bla_update_orig_address(bat_priv, primary_if, primary_if);
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
}
/* periodic work to do:
/**
* batadv_bla_periodic_work - performs periodic bla work
* @work: kernel work struct
*
* periodic work to do:
* * purge structures when they are too old
* * send announcements
*/
@@ -1251,7 +1355,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
}
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
queue_delayed_work(batadv_event_workqueue, &bat_priv->bla.work,
msecs_to_jiffies(BATADV_BLA_PERIOD_LENGTH));
@@ -1265,7 +1369,12 @@ out:
static struct lock_class_key batadv_claim_hash_lock_class_key;
static struct lock_class_key batadv_backbone_hash_lock_class_key;
/* initialize all bla structures */
/**
* batadv_bla_init - initialize all bla structures
* @bat_priv: the bat priv with all the soft interface information
*
* Return: 0 on success, < 0 on error.
*/
int batadv_bla_init(struct batadv_priv *bat_priv)
{
int i;
@@ -1285,7 +1394,7 @@ int batadv_bla_init(struct batadv_priv *bat_priv)
if (primary_if) {
crc = crc16(0, primary_if->net_dev->dev_addr, ETH_ALEN);
bat_priv->bla.claim_dest.group = htons(crc);
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
} else {
bat_priv->bla.claim_dest.group = 0; /* will be set later */
}
@@ -1320,7 +1429,7 @@ int batadv_bla_init(struct batadv_priv *bat_priv)
}
/**
* batadv_bla_check_bcast_duplist
* batadv_bla_check_bcast_duplist - Check if a frame is in the broadcast dup.
* @bat_priv: the bat priv with all the soft interface information
* @skb: contains the bcast_packet to be checked
*
@@ -1332,6 +1441,8 @@ int batadv_bla_init(struct batadv_priv *bat_priv)
* with a good chance that it is the same packet. If it is furthermore
* sent by another host, drop it. We allow equal packets from
* the same host however as this might be intended.
*
* Return: 1 if a packet is in the duplicate list, 0 otherwise.
*/
int batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
struct sk_buff *skb)
@@ -1390,14 +1501,13 @@ out:
}
/**
* batadv_bla_is_backbone_gw_orig
* batadv_bla_is_backbone_gw_orig - Check if the originator is a gateway for
* the VLAN identified by vid.
* @bat_priv: the bat priv with all the soft interface information
* @orig: originator mac address
* @vid: VLAN identifier
*
* Check if the originator is a gateway for the VLAN identified by vid.
*
* Returns true if orig is a backbone for this vid, false otherwise.
* Return: true if orig is a backbone for this vid, false otherwise.
*/
bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, u8 *orig,
unsigned short vid)
@@ -1431,14 +1541,13 @@ bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, u8 *orig,
}
/**
* batadv_bla_is_backbone_gw
* batadv_bla_is_backbone_gw - check if originator is a backbone gw for a VLAN.
* @skb: the frame to be checked
* @orig_node: the orig_node of the frame
* @hdr_size: maximum length of the frame
*
* bla_is_backbone_gw inspects the skb for the VLAN ID and returns 1
* if the orig_node is also a gateway on the soft interface, otherwise it
* returns 0.
* Return: 1 if the orig_node is also a gateway on the soft interface, otherwise
* it returns 0.
*/
int batadv_bla_is_backbone_gw(struct sk_buff *skb,
struct batadv_orig_node *orig_node, int hdr_size)
@@ -1461,11 +1570,16 @@ int batadv_bla_is_backbone_gw(struct sk_buff *skb,
if (!backbone_gw)
return 0;
batadv_backbone_gw_free_ref(backbone_gw);
batadv_backbone_gw_put(backbone_gw);
return 1;
}
/* free all bla structures (for softinterface free or module unload) */
/**
* batadv_bla_init - free all bla structures
* @bat_priv: the bat priv with all the soft interface information
*
* for softinterface free or module unload
*/
void batadv_bla_free(struct batadv_priv *bat_priv)
{
struct batadv_hard_iface *primary_if;
@@ -1484,22 +1598,23 @@ void batadv_bla_free(struct batadv_priv *bat_priv)
bat_priv->bla.backbone_hash = NULL;
}
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
}
/**
* batadv_bla_rx
* batadv_bla_rx - check packets coming from the mesh.
* @bat_priv: the bat priv with all the soft interface information
* @skb: the frame to be checked
* @vid: the VLAN ID of the frame
* @is_bcast: the packet came in a broadcast packet type.
*
* bla_rx avoidance checks if:
* batadv_bla_rx avoidance checks if:
* * we have to race for a claim
* * if the frame is allowed on the LAN
*
* in these cases, the skb is further handled by this function and
* returns 1, otherwise it returns 0 and the caller shall further
* in these cases, the skb is further handled by this function
*
* Return: 1 if handled, otherwise it returns 0 and the caller shall further
* process the skb.
*/
int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
@@ -1576,27 +1691,28 @@ handled:
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
if (claim)
batadv_claim_free_ref(claim);
batadv_claim_put(claim);
return ret;
}
/**
* batadv_bla_tx
* batadv_bla_tx - check packets going into the mesh
* @bat_priv: the bat priv with all the soft interface information
* @skb: the frame to be checked
* @vid: the VLAN ID of the frame
*
* bla_tx checks if:
* batadv_bla_tx checks if:
* * a claim was received which has to be processed
* * the frame is allowed on the mesh
*
* in these cases, the skb is further handled by this function and
* returns 1, otherwise it returns 0 and the caller shall further
* process the skb.
* in these cases, the skb is further handled by this function.
*
* This call might reallocate skb data.
*
* Return: 1 if handled, otherwise it returns 0 and the caller shall further
* process the skb.
*/
int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
unsigned short vid)
@@ -1664,12 +1780,19 @@ handled:
ret = 1;
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
if (claim)
batadv_claim_free_ref(claim);
batadv_claim_put(claim);
return ret;
}
/**
* batadv_bla_claim_table_seq_print_text - print the claim table in a seq file
* @seq: seq file to print on
* @offset: not used
*
* Return: always 0
*/
int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
{
struct net_device *net_dev = (struct net_device *)seq->private;
@@ -1715,10 +1838,18 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
}
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
return 0;
}
/**
* batadv_bla_backbone_table_seq_print_text - print the backbone table in a seq
* file
* @seq: seq file to print on
* @offset: not used
*
* Return: always 0
*/
int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset)
{
struct net_device *net_dev = (struct net_device *)seq->private;
@@ -1772,6 +1903,6 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset)
}
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
return 0;
}

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2011-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2011-2016 B.A.T.M.A.N. contributors:
*
* Simon Wunderlich
*

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2010-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2010-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
@@ -281,6 +281,8 @@ static int batadv_originators_open(struct inode *inode, struct file *file)
* originator table of an hard interface
* @inode: inode pointer to debugfs file
* @file: pointer to the seq_file
*
* Return: 0 on success or negative error number in case of failure
*/
static int batadv_originators_hardif_open(struct inode *inode,
struct file *file)
@@ -329,6 +331,8 @@ static int batadv_bla_backbone_table_open(struct inode *inode,
* batadv_dat_cache_open - Prepare file handler for reads from dat_chache
* @inode: inode which was opened
* @file: file handle to be initialized
*
* Return: 0 on success or negative error number in case of failure
*/
static int batadv_dat_cache_open(struct inode *inode, struct file *file)
{
@@ -483,6 +487,8 @@ void batadv_debugfs_destroy(void)
* batadv_debugfs_add_hardif - creates the base directory for a hard interface
* in debugfs.
* @hard_iface: hard interface which should be added.
*
* Return: 0 on success or negative error number in case of failure
*/
int batadv_debugfs_add_hardif(struct batadv_hard_iface *hard_iface)
{

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2010-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2010-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2011-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2011-2016 B.A.T.M.A.N. contributors:
*
* Antonio Quartulli
*
@@ -30,6 +30,7 @@
#include <linux/in.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
@@ -62,21 +63,34 @@ static void batadv_dat_start_timer(struct batadv_priv *bat_priv)
}
/**
* batadv_dat_entry_free_ref - decrement the dat_entry refcounter and possibly
* free it
* @dat_entry: the entry to free
* batadv_dat_entry_release - release dat_entry from lists and queue for free
* after rcu grace period
* @ref: kref pointer of the dat_entry
*/
static void batadv_dat_entry_free_ref(struct batadv_dat_entry *dat_entry)
static void batadv_dat_entry_release(struct kref *ref)
{
if (atomic_dec_and_test(&dat_entry->refcount))
kfree_rcu(dat_entry, rcu);
struct batadv_dat_entry *dat_entry;
dat_entry = container_of(ref, struct batadv_dat_entry, refcount);
kfree_rcu(dat_entry, rcu);
}
/**
* batadv_dat_entry_put - decrement the dat_entry refcounter and possibly
* release it
* @dat_entry: dat_entry to be free'd
*/
static void batadv_dat_entry_put(struct batadv_dat_entry *dat_entry)
{
kref_put(&dat_entry->refcount, batadv_dat_entry_release);
}
/**
* batadv_dat_to_purge - check whether a dat_entry has to be purged or not
* @dat_entry: the entry to check
*
* Returns true if the entry has to be purged now, false otherwise.
* Return: true if the entry has to be purged now, false otherwise.
*/
static bool batadv_dat_to_purge(struct batadv_dat_entry *dat_entry)
{
@@ -121,7 +135,7 @@ static void __batadv_dat_purge(struct batadv_priv *bat_priv,
continue;
hlist_del_rcu(&dat_entry->hash_entry);
batadv_dat_entry_free_ref(dat_entry);
batadv_dat_entry_put(dat_entry);
}
spin_unlock_bh(list_lock);
}
@@ -151,7 +165,7 @@ static void batadv_dat_purge(struct work_struct *work)
* @node: node in the local table
* @data2: second object to compare the node to
*
* Returns 1 if the two entries are the same, 0 otherwise.
* Return: 1 if the two entries are the same, 0 otherwise.
*/
static int batadv_compare_dat(const struct hlist_node *node, const void *data2)
{
@@ -166,7 +180,7 @@ static int batadv_compare_dat(const struct hlist_node *node, const void *data2)
* @skb: ARP packet
* @hdr_size: size of the possible header before the ARP packet
*
* Returns the value of the hw_src field in the ARP packet.
* Return: the value of the hw_src field in the ARP packet.
*/
static u8 *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size)
{
@@ -183,7 +197,7 @@ static u8 *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size)
* @skb: ARP packet
* @hdr_size: size of the possible header before the ARP packet
*
* Returns the value of the ip_src field in the ARP packet.
* Return: the value of the ip_src field in the ARP packet.
*/
static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size)
{
@@ -195,7 +209,7 @@ static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size)
* @skb: ARP packet
* @hdr_size: size of the possible header before the ARP packet
*
* Returns the value of the hw_dst field in the ARP packet.
* Return: the value of the hw_dst field in the ARP packet.
*/
static u8 *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size)
{
@@ -207,7 +221,7 @@ static u8 *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size)
* @skb: ARP packet
* @hdr_size: size of the possible header before the ARP packet
*
* Returns the value of the ip_dst field in the ARP packet.
* Return: the value of the ip_dst field in the ARP packet.
*/
static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
{
@@ -219,7 +233,7 @@ static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
* @data: data to hash
* @size: size of the hash table
*
* Returns the selected index in the hash table for the given data.
* Return: the selected index in the hash table for the given data.
*/
static u32 batadv_hash_dat(const void *data, u32 size)
{
@@ -256,7 +270,7 @@ static u32 batadv_hash_dat(const void *data, u32 size)
* @ip: search key
* @vid: VLAN identifier
*
* Returns the dat_entry if found, NULL otherwise.
* Return: the dat_entry if found, NULL otherwise.
*/
static struct batadv_dat_entry *
batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
@@ -281,7 +295,7 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
if (dat_entry->ip != ip)
continue;
if (!atomic_inc_not_zero(&dat_entry->refcount))
if (!kref_get_unless_zero(&dat_entry->refcount))
continue;
dat_entry_tmp = dat_entry;
@@ -326,7 +340,8 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
dat_entry->vid = vid;
ether_addr_copy(dat_entry->mac_addr, mac_addr);
dat_entry->last_update = jiffies;
atomic_set(&dat_entry->refcount, 2);
kref_init(&dat_entry->refcount);
kref_get(&dat_entry->refcount);
hash_added = batadv_hash_add(bat_priv->dat.hash, batadv_compare_dat,
batadv_hash_dat, dat_entry,
@@ -334,7 +349,7 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
if (unlikely(hash_added != 0)) {
/* remove the reference for the hash */
batadv_dat_entry_free_ref(dat_entry);
batadv_dat_entry_put(dat_entry);
goto out;
}
@@ -343,7 +358,7 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
out:
if (dat_entry)
batadv_dat_entry_free_ref(dat_entry);
batadv_dat_entry_put(dat_entry);
}
#ifdef CONFIG_BATMAN_ADV_DEBUG
@@ -440,7 +455,7 @@ static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb,
* @candidate: orig_node under evaluation
* @max_orig_node: last selected candidate
*
* Returns true if the node has been elected as next candidate or false
* Return: true if the node has been elected as next candidate or false
* otherwise.
*/
static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res,
@@ -527,12 +542,12 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv,
max_orig_node))
continue;
if (!atomic_inc_not_zero(&orig_node->refcount))
if (!kref_get_unless_zero(&orig_node->refcount))
continue;
max = tmp_max;
if (max_orig_node)
batadv_orig_node_free_ref(max_orig_node);
batadv_orig_node_put(max_orig_node);
max_orig_node = orig_node;
}
rcu_read_unlock();
@@ -558,7 +573,7 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv,
* closest values (from the LEFT, with wrap around if needed) then the hash
* value of the key. ip_dst is the key.
*
* Returns the candidate array of size BATADV_DAT_CANDIDATE_NUM.
* Return: the candidate array of size BATADV_DAT_CANDIDATE_NUM.
*/
static struct batadv_dat_candidate *
batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst)
@@ -602,7 +617,7 @@ batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst)
* This function copies the skb with pskb_copy() and is sent as unicast packet
* to each of the selected candidates.
*
* Returns true if the packet is sent to at least one candidate, false
* Return: true if the packet is sent to at least one candidate, false
* otherwise.
*/
static bool batadv_dat_send_data(struct batadv_priv *bat_priv,
@@ -639,9 +654,7 @@ static bool batadv_dat_send_data(struct batadv_priv *bat_priv,
goto free_neigh;
}
send_status = batadv_send_skb_packet(tmp_skb,
neigh_node->if_incoming,
neigh_node->addr);
send_status = batadv_send_unicast_skb(tmp_skb, neigh_node);
if (send_status == NET_XMIT_SUCCESS) {
/* count the sent packet */
switch (packet_subtype) {
@@ -659,9 +672,9 @@ static bool batadv_dat_send_data(struct batadv_priv *bat_priv,
ret = true;
}
free_neigh:
batadv_neigh_node_free_ref(neigh_node);
batadv_neigh_node_put(neigh_node);
free_orig:
batadv_orig_node_free_ref(cand[i].orig_node);
batadv_orig_node_put(cand[i].orig_node);
}
out:
@@ -741,6 +754,8 @@ static void batadv_dat_hash_free(struct batadv_priv *bat_priv)
/**
* batadv_dat_init - initialise the DAT internals
* @bat_priv: the bat priv with all the soft interface information
*
* Return: 0 in case of success, a negative error code otherwise
*/
int batadv_dat_init(struct batadv_priv *bat_priv)
{
@@ -779,6 +794,8 @@ void batadv_dat_free(struct batadv_priv *bat_priv)
* batadv_dat_cache_seq_print_text - print the local DAT hash table
* @seq: seq file to print on
* @offset: not used
*
* Return: always 0
*/
int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)
{
@@ -821,7 +838,7 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
return 0;
}
@@ -831,7 +848,7 @@ out:
* @skb: packet to analyse
* @hdr_size: size of the possible header before the ARP packet in the skb
*
* Returns the ARP type if the skb contains a valid ARP packet, 0 otherwise.
* Return: the ARP type if the skb contains a valid ARP packet, 0 otherwise.
*/
static u16 batadv_arp_get_type(struct batadv_priv *bat_priv,
struct sk_buff *skb, int hdr_size)
@@ -904,8 +921,9 @@ out:
* @skb: the buffer containing the packet to extract the VID from
* @hdr_size: the size of the batman-adv header encapsulating the packet
*
* If the packet embedded in the skb is vlan tagged this function returns the
* VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS is returned.
* Return: If the packet embedded in the skb is vlan tagged this function
* returns the VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS
* is returned.
*/
static unsigned short batadv_dat_get_vid(struct sk_buff *skb, int *hdr_size)
{
@@ -930,7 +948,7 @@ static unsigned short batadv_dat_get_vid(struct sk_buff *skb, int *hdr_size)
* @bat_priv: the bat priv with all the soft interface information
* @skb: packet to check
*
* Returns true if the message has been sent to the dht candidates, false
* Return: true if the message has been sent to the dht candidates, false
* otherwise. In case of a positive return value the message has to be enqueued
* to permit the fallback.
*/
@@ -1009,7 +1027,7 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
}
out:
if (dat_entry)
batadv_dat_entry_free_ref(dat_entry);
batadv_dat_entry_put(dat_entry);
return ret;
}
@@ -1020,7 +1038,7 @@ out:
* @skb: packet to check
* @hdr_size: size of the encapsulation header
*
* Returns true if the request has been answered, false otherwise.
* Return: true if the request has been answered, false otherwise.
*/
bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
struct sk_buff *skb, int hdr_size)
@@ -1089,7 +1107,7 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
}
out:
if (dat_entry)
batadv_dat_entry_free_ref(dat_entry);
batadv_dat_entry_put(dat_entry);
if (ret)
kfree_skb(skb);
return ret;
@@ -1143,7 +1161,7 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
* @skb: packet to check
* @hdr_size: size of the encapsulation header
*
* Returns true if the packet was snooped and consumed by DAT. False if the
* Return: true if the packet was snooped and consumed by DAT. False if the
* packet has to be delivered to the interface
*/
bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
@@ -1200,7 +1218,7 @@ out:
* @bat_priv: the bat priv with all the soft interface information
* @forw_packet: the broadcast packet
*
* Returns true if the node can drop the packet, false otherwise.
* Return: true if the node can drop the packet, false otherwise.
*/
bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
struct batadv_forw_packet *forw_packet)
@@ -1242,6 +1260,6 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
out:
if (dat_entry)
batadv_dat_entry_free_ref(dat_entry);
batadv_dat_entry_put(dat_entry);
return ret;
}

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2011-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2011-2016 B.A.T.M.A.N. contributors:
*
* Antonio Quartulli
*

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2013-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2013-2016 B.A.T.M.A.N. contributors:
*
* Martin Hundebøll <martin@hundeboll.net>
*
@@ -85,7 +85,7 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig_node,
/**
* batadv_frag_size_limit - maximum possible size of packet to be fragmented
*
* Returns the maximum size of payload that can be fragmented.
* Return: the maximum size of payload that can be fragmented.
*/
static int batadv_frag_size_limit(void)
{
@@ -107,7 +107,7 @@ static int batadv_frag_size_limit(void)
*
* Caller must hold chain->lock.
*
* Returns true if chain is empty and caller can just insert the new fragment
* Return: true if chain is empty and caller can just insert the new fragment
* without searching for the right position.
*/
static bool batadv_frag_init_chain(struct batadv_frag_table_entry *chain,
@@ -136,7 +136,7 @@ static bool batadv_frag_init_chain(struct batadv_frag_table_entry *chain,
* Insert a new fragment into the reverse ordered chain in the right table
* entry. The hash table entry is cleared if "old" fragments exist in it.
*
* Returns true if skb is buffered, false on error. If the chain has all the
* Return: true if skb is buffered, false on error. If the chain has all the
* fragments needed to merge the packet, the chain is moved to the passed head
* to avoid locking the chain in the table.
*/
@@ -242,12 +242,11 @@ err:
/**
* batadv_frag_merge_packets - merge a chain of fragments
* @chain: head of chain with fragments
* @skb: packet with total size of skb after merging
*
* Expand the first skb in the chain and copy the content of the remaining
* skb's into the expanded one. After doing so, clear the chain.
*
* Returns the merged skb or NULL on error.
* Return: the merged skb or NULL on error.
*/
static struct sk_buff *
batadv_frag_merge_packets(struct hlist_head *chain)
@@ -307,6 +306,9 @@ free:
* There are three possible outcomes: 1) Packet is merged: Return true and
* set *skb to merged packet; 2) Packet is buffered: Return true and set *skb
* to NULL; 3) Error: Return false and leave skb as is.
*
* Return: true when packet is merged or buffered, false when skb is not not
* used.
*/
bool batadv_frag_skb_buffer(struct sk_buff **skb,
struct batadv_orig_node *orig_node_src)
@@ -344,7 +346,7 @@ out_err:
* will exceed the MTU towards the next-hop. If so, the fragment is forwarded
* without merging it.
*
* Returns true if the fragment is consumed/forwarded, false otherwise.
* Return: true if the fragment is consumed/forwarded, false otherwise.
*/
bool batadv_frag_skb_fwd(struct sk_buff *skb,
struct batadv_hard_iface *recv_if,
@@ -376,16 +378,15 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
skb->len + ETH_HLEN);
packet->ttl--;
batadv_send_skb_packet(skb, neigh_node->if_incoming,
neigh_node->addr);
batadv_send_unicast_skb(skb, neigh_node);
ret = true;
}
out:
if (orig_node_dst)
batadv_orig_node_free_ref(orig_node_dst);
batadv_orig_node_put(orig_node_dst);
if (neigh_node)
batadv_neigh_node_free_ref(neigh_node);
batadv_neigh_node_put(neigh_node);
return ret;
}
@@ -399,7 +400,7 @@ out:
* passed mtu and the old one with the rest. The new skb contains data from the
* tail of the old skb.
*
* Returns the new fragment, NULL on error.
* Return: the new fragment, NULL on error.
*/
static struct sk_buff *batadv_frag_create(struct sk_buff *skb,
struct batadv_frag_packet *frag_head,
@@ -433,7 +434,7 @@ err:
* @orig_node: final destination of the created fragments
* @neigh_node: next-hop of the created fragments
*
* Returns true on success, false otherwise.
* Return: true on success, false otherwise.
*/
bool batadv_frag_send_packet(struct sk_buff *skb,
struct batadv_orig_node *orig_node,
@@ -484,8 +485,7 @@ bool batadv_frag_send_packet(struct sk_buff *skb,
batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX);
batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES,
skb_fragment->len + ETH_HLEN);
batadv_send_skb_packet(skb_fragment, neigh_node->if_incoming,
neigh_node->addr);
batadv_send_unicast_skb(skb_fragment, neigh_node);
frag_header.no++;
/* The initial check in this function should cover this case */
@@ -504,13 +504,13 @@ bool batadv_frag_send_packet(struct sk_buff *skb,
batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX);
batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES,
skb->len + ETH_HLEN);
batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
batadv_send_unicast_skb(skb, neigh_node);
ret = true;
out_err:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
return ret;
}

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2013-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2013-2016 B.A.T.M.A.N. contributors:
*
* Martin Hundebøll <martin@hundeboll.net>
*
@@ -42,7 +42,7 @@ bool batadv_frag_send_packet(struct sk_buff *skb,
* batadv_frag_check_entry - check if a list of fragments has timed out
* @frags_entry: table entry to check
*
* Returns true if the frags entry has timed out, false otherwise.
* Return: true if the frags entry has timed out, false otherwise.
*/
static inline bool
batadv_frag_check_entry(struct batadv_frag_table_entry *frags_entry)

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2009-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2009-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
@@ -28,6 +28,7 @@
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/rculist.h>
@@ -59,12 +60,28 @@
*/
#define BATADV_DHCP_CHADDR_OFFSET 28
static void batadv_gw_node_free_ref(struct batadv_gw_node *gw_node)
/**
* batadv_gw_node_release - release gw_node from lists and queue for free after
* rcu grace period
* @ref: kref pointer of the gw_node
*/
static void batadv_gw_node_release(struct kref *ref)
{
if (atomic_dec_and_test(&gw_node->refcount)) {
batadv_orig_node_free_ref(gw_node->orig_node);
kfree_rcu(gw_node, rcu);
}
struct batadv_gw_node *gw_node;
gw_node = container_of(ref, struct batadv_gw_node, refcount);
batadv_orig_node_put(gw_node->orig_node);
kfree_rcu(gw_node, rcu);
}
/**
* batadv_gw_node_put - decrement the gw_node refcounter and possibly release it
* @gw_node: gateway node to free
*/
static void batadv_gw_node_put(struct batadv_gw_node *gw_node)
{
kref_put(&gw_node->refcount, batadv_gw_node_release);
}
static struct batadv_gw_node *
@@ -77,7 +94,7 @@ batadv_gw_get_selected_gw_node(struct batadv_priv *bat_priv)
if (!gw_node)
goto out;
if (!atomic_inc_not_zero(&gw_node->refcount))
if (!kref_get_unless_zero(&gw_node->refcount))
gw_node = NULL;
out:
@@ -100,14 +117,14 @@ batadv_gw_get_selected_orig(struct batadv_priv *bat_priv)
if (!orig_node)
goto unlock;
if (!atomic_inc_not_zero(&orig_node->refcount))
if (!kref_get_unless_zero(&orig_node->refcount))
orig_node = NULL;
unlock:
rcu_read_unlock();
out:
if (gw_node)
batadv_gw_node_free_ref(gw_node);
batadv_gw_node_put(gw_node);
return orig_node;
}
@@ -118,14 +135,14 @@ static void batadv_gw_select(struct batadv_priv *bat_priv,
spin_lock_bh(&bat_priv->gw.list_lock);
if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount))
if (new_gw_node && !kref_get_unless_zero(&new_gw_node->refcount))
new_gw_node = NULL;
curr_gw_node = rcu_dereference_protected(bat_priv->gw.curr_gw, 1);
rcu_assign_pointer(bat_priv->gw.curr_gw, new_gw_node);
if (curr_gw_node)
batadv_gw_node_free_ref(curr_gw_node);
batadv_gw_node_put(curr_gw_node);
spin_unlock_bh(&bat_priv->gw.list_lock);
}
@@ -170,7 +187,7 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
if (!router_ifinfo)
goto next;
if (!atomic_inc_not_zero(&gw_node->refcount))
if (!kref_get_unless_zero(&gw_node->refcount))
goto next;
tq_avg = router_ifinfo->bat_iv.tq_avg;
@@ -186,9 +203,9 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
((tmp_gw_factor == max_gw_factor) &&
(tq_avg > max_tq))) {
if (curr_gw)
batadv_gw_node_free_ref(curr_gw);
batadv_gw_node_put(curr_gw);
curr_gw = gw_node;
atomic_inc(&curr_gw->refcount);
kref_get(&curr_gw->refcount);
}
break;
@@ -201,9 +218,9 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
*/
if (tq_avg > max_tq) {
if (curr_gw)
batadv_gw_node_free_ref(curr_gw);
batadv_gw_node_put(curr_gw);
curr_gw = gw_node;
atomic_inc(&curr_gw->refcount);
kref_get(&curr_gw->refcount);
}
break;
}
@@ -214,12 +231,12 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
if (tmp_gw_factor > max_gw_factor)
max_gw_factor = tmp_gw_factor;
batadv_gw_node_free_ref(gw_node);
batadv_gw_node_put(gw_node);
next:
batadv_neigh_node_free_ref(router);
batadv_neigh_node_put(router);
if (router_ifinfo)
batadv_neigh_ifinfo_free_ref(router_ifinfo);
batadv_neigh_ifinfo_put(router_ifinfo);
}
rcu_read_unlock();
@@ -255,7 +272,7 @@ void batadv_gw_check_client_stop(struct batadv_priv *bat_priv)
*/
batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL, NULL);
batadv_gw_node_free_ref(curr_gw);
batadv_gw_node_put(curr_gw);
}
void batadv_gw_election(struct batadv_priv *bat_priv)
@@ -330,13 +347,13 @@ void batadv_gw_election(struct batadv_priv *bat_priv)
out:
if (curr_gw)
batadv_gw_node_free_ref(curr_gw);
batadv_gw_node_put(curr_gw);
if (next_gw)
batadv_gw_node_free_ref(next_gw);
batadv_gw_node_put(next_gw);
if (router)
batadv_neigh_node_free_ref(router);
batadv_neigh_node_put(router);
if (router_ifinfo)
batadv_neigh_ifinfo_free_ref(router_ifinfo);
batadv_neigh_ifinfo_put(router_ifinfo);
}
void batadv_gw_check_election(struct batadv_priv *bat_priv,
@@ -397,15 +414,15 @@ reselect:
batadv_gw_reselect(bat_priv);
out:
if (curr_gw_orig)
batadv_orig_node_free_ref(curr_gw_orig);
batadv_orig_node_put(curr_gw_orig);
if (router_gw)
batadv_neigh_node_free_ref(router_gw);
batadv_neigh_node_put(router_gw);
if (router_orig)
batadv_neigh_node_free_ref(router_orig);
batadv_neigh_node_put(router_orig);
if (router_gw_tq)
batadv_neigh_ifinfo_free_ref(router_gw_tq);
batadv_neigh_ifinfo_put(router_gw_tq);
if (router_orig_tq)
batadv_neigh_ifinfo_free_ref(router_orig_tq);
batadv_neigh_ifinfo_put(router_orig_tq);
}
/**
@@ -423,12 +440,12 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv,
if (gateway->bandwidth_down == 0)
return;
if (!atomic_inc_not_zero(&orig_node->refcount))
if (!kref_get_unless_zero(&orig_node->refcount))
return;
gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC);
if (!gw_node) {
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
return;
}
@@ -436,7 +453,7 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv,
gw_node->orig_node = orig_node;
gw_node->bandwidth_down = ntohl(gateway->bandwidth_down);
gw_node->bandwidth_up = ntohl(gateway->bandwidth_up);
atomic_set(&gw_node->refcount, 1);
kref_init(&gw_node->refcount);
spin_lock_bh(&bat_priv->gw.list_lock);
hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.list);
@@ -456,7 +473,7 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv,
* @bat_priv: the bat priv with all the soft interface information
* @orig_node: originator announcing gateway capabilities
*
* Returns gateway node if found or NULL otherwise.
* Return: gateway node if found or NULL otherwise.
*/
static struct batadv_gw_node *
batadv_gw_node_get(struct batadv_priv *bat_priv,
@@ -469,7 +486,7 @@ batadv_gw_node_get(struct batadv_priv *bat_priv,
if (gw_node_tmp->orig_node != orig_node)
continue;
if (!atomic_inc_not_zero(&gw_node_tmp->refcount))
if (!kref_get_unless_zero(&gw_node_tmp->refcount))
continue;
gw_node = gw_node_tmp;
@@ -529,7 +546,7 @@ void batadv_gw_node_update(struct batadv_priv *bat_priv,
spin_lock_bh(&bat_priv->gw.list_lock);
if (!hlist_unhashed(&gw_node->list)) {
hlist_del_init_rcu(&gw_node->list);
batadv_gw_node_free_ref(gw_node);
batadv_gw_node_put(gw_node);
}
spin_unlock_bh(&bat_priv->gw.list_lock);
@@ -538,12 +555,12 @@ void batadv_gw_node_update(struct batadv_priv *bat_priv,
batadv_gw_reselect(bat_priv);
if (curr_gw)
batadv_gw_node_free_ref(curr_gw);
batadv_gw_node_put(curr_gw);
}
out:
if (gw_node)
batadv_gw_node_free_ref(gw_node);
batadv_gw_node_put(gw_node);
}
void batadv_gw_node_delete(struct batadv_priv *bat_priv,
@@ -566,7 +583,7 @@ void batadv_gw_node_free(struct batadv_priv *bat_priv)
hlist_for_each_entry_safe(gw_node, node_tmp,
&bat_priv->gw.list, list) {
hlist_del_init_rcu(&gw_node->list);
batadv_gw_node_free_ref(gw_node);
batadv_gw_node_put(gw_node);
}
spin_unlock_bh(&bat_priv->gw.list_lock);
}
@@ -603,12 +620,12 @@ static int batadv_write_buffer_text(struct batadv_priv *bat_priv,
ret = seq_has_overflowed(seq) ? -1 : 0;
if (curr_gw)
batadv_gw_node_free_ref(curr_gw);
batadv_gw_node_put(curr_gw);
out:
if (router_ifinfo)
batadv_neigh_ifinfo_free_ref(router_ifinfo);
batadv_neigh_ifinfo_put(router_ifinfo);
if (router)
batadv_neigh_node_free_ref(router);
batadv_neigh_node_put(router);
return ret;
}
@@ -645,7 +662,7 @@ int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset)
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
return 0;
}
@@ -656,13 +673,13 @@ out:
* @chaddr: buffer where the client address will be stored. Valid
* only if the function returns BATADV_DHCP_TO_CLIENT
*
* Returns:
* This function may re-allocate the data buffer of the skb passed as argument.
*
* Return:
* - BATADV_DHCP_NO if the packet is not a dhcp message or if there was an error
* while parsing it
* - BATADV_DHCP_TO_SERVER if this is a message going to the DHCP server
* - BATADV_DHCP_TO_CLIENT if this is a message going to a DHCP client
*
* This function may re-allocate the data buffer of the skb passed as argument.
*/
enum batadv_dhcp_recipient
batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len,
@@ -777,11 +794,11 @@ batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len,
* server. Due to topology changes it may be the case that the GW server
* previously selected is not the best one anymore.
*
* Returns true if the packet destination is unicast and it is not the best gw,
* false otherwise.
*
* This call might reallocate skb data.
* Must be invoked only when the DHCP packet is going TO a DHCP SERVER.
*
* Return: true if the packet destination is unicast and it is not the best gw,
* false otherwise.
*/
bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
struct sk_buff *skb)
@@ -839,7 +856,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
goto out;
curr_tq_avg = curr_ifinfo->bat_iv.tq_avg;
batadv_neigh_ifinfo_free_ref(curr_ifinfo);
batadv_neigh_ifinfo_put(curr_ifinfo);
break;
case BATADV_GW_MODE_OFF:
@@ -857,18 +874,18 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
if ((curr_tq_avg - old_ifinfo->bat_iv.tq_avg) > BATADV_GW_THRESHOLD)
out_of_range = true;
batadv_neigh_ifinfo_free_ref(old_ifinfo);
batadv_neigh_ifinfo_put(old_ifinfo);
out:
if (orig_dst_node)
batadv_orig_node_free_ref(orig_dst_node);
batadv_orig_node_put(orig_dst_node);
if (curr_gw)
batadv_gw_node_free_ref(curr_gw);
batadv_gw_node_put(curr_gw);
if (gw_node)
batadv_gw_node_free_ref(gw_node);
batadv_gw_node_put(gw_node);
if (neigh_old)
batadv_neigh_node_free_ref(neigh_old);
batadv_neigh_node_put(neigh_old);
if (neigh_curr)
batadv_neigh_node_free_ref(neigh_curr);
batadv_neigh_node_put(neigh_curr);
return out_of_range;
}

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2009-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2009-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2009-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2009-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
@@ -38,10 +38,10 @@
* @description: text shown when throughput string cannot be parsed
* @throughput: pointer holding the returned throughput information
*
* Returns false on parse error and true otherwise.
* Return: false on parse error and true otherwise.
*/
static bool batadv_parse_throughput(struct net_device *net_dev, char *buff,
const char *description, u32 *throughput)
bool batadv_parse_throughput(struct net_device *net_dev, char *buff,
const char *description, u32 *throughput)
{
enum batadv_bandwidth_units bw_unit_type = BATADV_BW_UNIT_KBIT;
u64 lthroughput;

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2009-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2009-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
@@ -49,5 +49,7 @@ ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff,
void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv);
void batadv_gw_init(struct batadv_priv *bat_priv);
void batadv_gw_free(struct batadv_priv *bat_priv);
bool batadv_parse_throughput(struct net_device *net_dev, char *buff,
const char *description, u32 *throughput);
#endif /* _NET_BATMAN_ADV_GATEWAY_COMMON_H_ */

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -18,6 +18,7 @@
#include "hard-interface.h"
#include "main.h"
#include <linux/atomic.h>
#include <linux/bug.h>
#include <linux/byteorder/generic.h>
#include <linux/errno.h>
@@ -26,6 +27,7 @@
#include <linux/if_ether.h>
#include <linux/if.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/printk.h>
@@ -47,13 +49,19 @@
#include "sysfs.h"
#include "translation-table.h"
void batadv_hardif_free_rcu(struct rcu_head *rcu)
/**
* batadv_hardif_release - release hard interface from lists and queue for
* free after rcu grace period
* @ref: kref pointer of the hard interface
*/
void batadv_hardif_release(struct kref *ref)
{
struct batadv_hard_iface *hard_iface;
hard_iface = container_of(rcu, struct batadv_hard_iface, rcu);
hard_iface = container_of(ref, struct batadv_hard_iface, refcount);
dev_put(hard_iface->net_dev);
kfree(hard_iface);
kfree_rcu(hard_iface, rcu);
}
struct batadv_hard_iface *
@@ -64,7 +72,7 @@ batadv_hardif_get_by_netdev(const struct net_device *net_dev)
rcu_read_lock();
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
if (hard_iface->net_dev == net_dev &&
atomic_inc_not_zero(&hard_iface->refcount))
kref_get_unless_zero(&hard_iface->refcount))
goto out;
}
@@ -107,7 +115,7 @@ static bool batadv_mutual_parents(const struct net_device *dev1,
* This function recursively checks all the fathers of the device passed as
* argument looking for a batman-adv soft interface.
*
* Returns true if the device is descendant of a batman-adv mesh interface (or
* Return: true if the device is descendant of a batman-adv mesh interface (or
* if it is a batman-adv interface itself), false otherwise
*/
static bool batadv_is_on_batman_iface(const struct net_device *net_dev)
@@ -161,7 +169,7 @@ static int batadv_is_valid_iface(const struct net_device *net_dev)
* interface
* @net_device: the device to check
*
* Returns true if the net device is a 802.11 wireless device, false otherwise.
* Return: true if the net device is a 802.11 wireless device, false otherwise.
*/
bool batadv_is_wifi_netdev(struct net_device *net_device)
{
@@ -194,7 +202,7 @@ batadv_hardif_get_active(const struct net_device *soft_iface)
continue;
if (hard_iface->if_status == BATADV_IF_ACTIVE &&
atomic_inc_not_zero(&hard_iface->refcount))
kref_get_unless_zero(&hard_iface->refcount))
goto out;
}
@@ -218,7 +226,7 @@ static void batadv_primary_if_update_addr(struct batadv_priv *bat_priv,
batadv_bla_update_orig_address(bat_priv, primary_if, oldif);
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
}
static void batadv_primary_if_select(struct batadv_priv *bat_priv,
@@ -228,7 +236,7 @@ static void batadv_primary_if_select(struct batadv_priv *bat_priv,
ASSERT_RTNL();
if (new_hard_iface && !atomic_inc_not_zero(&new_hard_iface->refcount))
if (new_hard_iface && !kref_get_unless_zero(&new_hard_iface->refcount))
new_hard_iface = NULL;
curr_hard_iface = rcu_dereference_protected(bat_priv->primary_if, 1);
@@ -242,7 +250,7 @@ static void batadv_primary_if_select(struct batadv_priv *bat_priv,
out:
if (curr_hard_iface)
batadv_hardif_free_ref(curr_hard_iface);
batadv_hardif_put(curr_hard_iface);
}
static bool
@@ -401,7 +409,7 @@ batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface)
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
}
static void
@@ -426,7 +434,8 @@ batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
*
* Invoke ndo_del_slave on master passing slave as argument. In this way slave
* is free'd and master can correctly change its internal state.
* Return 0 on success, a negative value representing the error otherwise
*
* Return: 0 on success, a negative value representing the error otherwise
*/
static int batadv_master_del_slave(struct batadv_hard_iface *slave,
struct net_device *master)
@@ -455,7 +464,7 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
goto out;
if (!atomic_inc_not_zero(&hard_iface->refcount))
if (!kref_get_unless_zero(&hard_iface->refcount))
goto out;
soft_iface = dev_get_by_name(&init_net, iface_name);
@@ -553,7 +562,7 @@ err_dev:
hard_iface->soft_iface = NULL;
dev_put(soft_iface);
err:
batadv_hardif_free_ref(hard_iface);
batadv_hardif_put(hard_iface);
return ret;
}
@@ -584,7 +593,7 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface,
batadv_primary_if_select(bat_priv, new_if);
if (new_if)
batadv_hardif_free_ref(new_if);
batadv_hardif_put(new_if);
}
bat_priv->bat_algo_ops->bat_iface_disable(hard_iface);
@@ -607,11 +616,11 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface,
}
hard_iface->soft_iface = NULL;
batadv_hardif_free_ref(hard_iface);
batadv_hardif_put(hard_iface);
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
}
/**
@@ -630,7 +639,7 @@ static void batadv_hardif_remove_interface_finish(struct work_struct *work)
batadv_debugfs_del_hardif(hard_iface);
batadv_sysfs_del_hardif(&hard_iface->hardif_obj);
batadv_hardif_free_ref(hard_iface);
batadv_hardif_put(hard_iface);
}
static struct batadv_hard_iface *
@@ -676,7 +685,8 @@ batadv_hardif_add_interface(struct net_device *net_dev)
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
/* extra reference for return */
atomic_set(&hard_iface->refcount, 2);
kref_init(&hard_iface->refcount);
kref_get(&hard_iface->refcount);
batadv_check_known_mac_addr(hard_iface->net_dev);
list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
@@ -784,10 +794,10 @@ static int batadv_hard_if_event(struct notifier_block *this,
}
hardif_put:
batadv_hardif_free_ref(hard_iface);
batadv_hardif_put(hard_iface);
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
return NOTIFY_DONE;
}

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -20,8 +20,8 @@
#include "main.h"
#include <linux/atomic.h>
#include <linux/compiler.h>
#include <linux/kref.h>
#include <linux/notifier.h>
#include <linux/rcupdate.h>
#include <linux/stddef.h>
@@ -61,18 +61,16 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface,
void batadv_hardif_remove_interfaces(void);
int batadv_hardif_min_mtu(struct net_device *soft_iface);
void batadv_update_min_mtu(struct net_device *soft_iface);
void batadv_hardif_free_rcu(struct rcu_head *rcu);
void batadv_hardif_release(struct kref *ref);
/**
* batadv_hardif_free_ref - decrement the hard interface refcounter and
* possibly free it
* batadv_hardif_put - decrement the hard interface refcounter and possibly
* release it
* @hard_iface: the hard interface to free
*/
static inline void
batadv_hardif_free_ref(struct batadv_hard_iface *hard_iface)
static inline void batadv_hardif_put(struct batadv_hard_iface *hard_iface)
{
if (atomic_dec_and_test(&hard_iface->refcount))
call_rcu(&hard_iface->rcu, batadv_hardif_free_rcu);
kref_put(&hard_iface->refcount, batadv_hardif_release);
}
static inline struct batadv_hard_iface *
@@ -85,7 +83,7 @@ batadv_primary_if_get_selected(struct batadv_priv *bat_priv)
if (!hard_iface)
goto out;
if (!atomic_inc_not_zero(&hard_iface->refcount))
if (!kref_get_unless_zero(&hard_iface->refcount))
hard_iface = NULL;
out:

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2006-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2006-2016 B.A.T.M.A.N. contributors:
*
* Simon Wunderlich, Marek Lindner
*

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2006-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2006-2016 B.A.T.M.A.N. contributors:
*
* Simon Wunderlich, Marek Lindner
*
@@ -30,14 +30,17 @@
struct lock_class_key;
/* callback to a compare function. should compare 2 element datas for their
* keys, return 0 if same and not 0 if not same
* keys
*
* Return: 0 if same and not 0 if not same
*/
typedef int (*batadv_hashdata_compare_cb)(const struct hlist_node *,
const void *);
/* the hashfunction, should return an index
* based on the key in the data of the first
* argument and the size the second
/* the hashfunction
*
* Return: an index based on the key in the data of the first argument and the
* size the second
*/
typedef u32 (*batadv_hashdata_choose_cb)(const void *, u32);
typedef void (*batadv_hashdata_free_cb)(struct hlist_node *, void *);
@@ -96,7 +99,7 @@ static inline void batadv_hash_delete(struct batadv_hashtable *hash,
* @data: data passed to the aforementioned callbacks as argument
* @data_node: to be added element
*
* Returns 0 on success, 1 if the element already is in the hash
* Return: 0 on success, 1 if the element already is in the hash
* and -1 on error.
*/
static inline int batadv_hash_add(struct batadv_hashtable *hash,
@@ -139,10 +142,11 @@ out:
return ret;
}
/* removes data from hash, if found. returns pointer do data on success, so you
* can remove the used structure yourself, or NULL on error . data could be the
* structure you use with just the key filled, we just need the key for
* comparing.
/* removes data from hash, if found. data could be the structure you use with
* just the key filled, we just need the key for comparing.
*
* Return: returns pointer do data on success, so you can remove the used
* structure yourself, or NULL on error
*/
static inline void *batadv_hash_remove(struct batadv_hashtable *hash,
batadv_hashdata_compare_cb compare,

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
@@ -278,7 +278,7 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
ether_addr_copy(icmp_header->orig, primary_if->net_dev->dev_addr);
batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
batadv_send_unicast_skb(skb, neigh_node);
goto out;
dst_unreach:
@@ -288,11 +288,11 @@ free_skb:
kfree_skb(skb);
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
if (neigh_node)
batadv_neigh_node_free_ref(neigh_node);
batadv_neigh_node_put(neigh_node);
if (orig_node)
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
return len;
}

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -29,6 +29,7 @@
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/module.h>
@@ -86,6 +87,7 @@ static int __init batadv_init(void)
batadv_recv_handler_init();
batadv_v_init();
batadv_iv_init();
batadv_nc_init();
@@ -158,6 +160,10 @@ int batadv_mesh_init(struct net_device *soft_iface)
INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list);
INIT_HLIST_HEAD(&bat_priv->softif_vlan_list);
ret = batadv_v_mesh_init(bat_priv);
if (ret < 0)
goto err;
ret = batadv_originator_init(bat_priv);
if (ret < 0)
goto err;
@@ -200,6 +206,8 @@ void batadv_mesh_free(struct net_device *soft_iface)
batadv_purge_outstanding_packets(bat_priv, NULL);
batadv_gw_node_free(bat_priv);
batadv_v_mesh_free(bat_priv);
batadv_nc_mesh_free(bat_priv);
batadv_dat_free(bat_priv);
batadv_bla_free(bat_priv);
@@ -233,7 +241,7 @@ void batadv_mesh_free(struct net_device *soft_iface)
* @bat_priv: the bat priv with all the soft interface information
* @addr: the address to check
*
* Returns 'true' if the mac address was found, false otherwise.
* Return: 'true' if the mac address was found, false otherwise.
*/
bool batadv_is_my_mac(struct batadv_priv *bat_priv, const u8 *addr)
{
@@ -262,7 +270,7 @@ bool batadv_is_my_mac(struct batadv_priv *bat_priv, const u8 *addr)
* function that requires the primary interface
* @seq: debugfs table seq_file struct
*
* Returns primary interface if found or NULL otherwise.
* Return: primary interface if found or NULL otherwise.
*/
struct batadv_hard_iface *
batadv_seq_print_text_primary_if_get(struct seq_file *seq)
@@ -286,7 +294,7 @@ batadv_seq_print_text_primary_if_get(struct seq_file *seq)
seq_printf(seq,
"BATMAN mesh %s disabled - primary interface not active\n",
net_dev->name);
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
primary_if = NULL;
out:
@@ -297,7 +305,7 @@ out:
* batadv_max_header_len - calculate maximum encapsulation overhead for a
* payload packet
*
* Return the maximum encapsulation overhead in bytes.
* Return: the maximum encapsulation overhead in bytes.
*/
int batadv_max_header_len(void)
{
@@ -599,6 +607,8 @@ int batadv_algo_seq_print_text(struct seq_file *seq, void *offset)
*
* payload_ptr must always point to an address in the skb head buffer and not to
* a fragment.
*
* Return: big endian crc32c of the checksummed data
*/
__be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr)
{
@@ -622,15 +632,26 @@ __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr)
}
/**
* batadv_tvlv_handler_free_ref - decrement the tvlv handler refcounter and
* possibly free it
* batadv_tvlv_handler_release - release tvlv handler from lists and queue for
* free after rcu grace period
* @ref: kref pointer of the tvlv
*/
static void batadv_tvlv_handler_release(struct kref *ref)
{
struct batadv_tvlv_handler *tvlv_handler;
tvlv_handler = container_of(ref, struct batadv_tvlv_handler, refcount);
kfree_rcu(tvlv_handler, rcu);
}
/**
* batadv_tvlv_handler_put - decrement the tvlv container refcounter and
* possibly release it
* @tvlv_handler: the tvlv handler to free
*/
static void
batadv_tvlv_handler_free_ref(struct batadv_tvlv_handler *tvlv_handler)
static void batadv_tvlv_handler_put(struct batadv_tvlv_handler *tvlv_handler)
{
if (atomic_dec_and_test(&tvlv_handler->refcount))
kfree_rcu(tvlv_handler, rcu);
kref_put(&tvlv_handler->refcount, batadv_tvlv_handler_release);
}
/**
@@ -640,7 +661,7 @@ batadv_tvlv_handler_free_ref(struct batadv_tvlv_handler *tvlv_handler)
* @type: tvlv handler type to look for
* @version: tvlv handler version to look for
*
* Returns tvlv handler if found or NULL otherwise.
* Return: tvlv handler if found or NULL otherwise.
*/
static struct batadv_tvlv_handler
*batadv_tvlv_handler_get(struct batadv_priv *bat_priv, u8 type, u8 version)
@@ -656,7 +677,7 @@ static struct batadv_tvlv_handler
if (tvlv_handler_tmp->version != version)
continue;
if (!atomic_inc_not_zero(&tvlv_handler_tmp->refcount))
if (!kref_get_unless_zero(&tvlv_handler_tmp->refcount))
continue;
tvlv_handler = tvlv_handler_tmp;
@@ -668,14 +689,25 @@ static struct batadv_tvlv_handler
}
/**
* batadv_tvlv_container_free_ref - decrement the tvlv container refcounter and
* possibly free it
* batadv_tvlv_container_release - release tvlv from lists and free
* @ref: kref pointer of the tvlv
*/
static void batadv_tvlv_container_release(struct kref *ref)
{
struct batadv_tvlv_container *tvlv;
tvlv = container_of(ref, struct batadv_tvlv_container, refcount);
kfree(tvlv);
}
/**
* batadv_tvlv_container_put - decrement the tvlv container refcounter and
* possibly release it
* @tvlv: the tvlv container to free
*/
static void batadv_tvlv_container_free_ref(struct batadv_tvlv_container *tvlv)
static void batadv_tvlv_container_put(struct batadv_tvlv_container *tvlv)
{
if (atomic_dec_and_test(&tvlv->refcount))
kfree(tvlv);
kref_put(&tvlv->refcount, batadv_tvlv_container_release);
}
/**
@@ -688,13 +720,15 @@ static void batadv_tvlv_container_free_ref(struct batadv_tvlv_container *tvlv)
* Has to be called with the appropriate locks being acquired
* (tvlv.container_list_lock).
*
* Returns tvlv container if found or NULL otherwise.
* Return: tvlv container if found or NULL otherwise.
*/
static struct batadv_tvlv_container
*batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version)
{
struct batadv_tvlv_container *tvlv_tmp, *tvlv = NULL;
lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
hlist_for_each_entry(tvlv_tmp, &bat_priv->tvlv.container_list, list) {
if (tvlv_tmp->tvlv_hdr.type != type)
continue;
@@ -702,7 +736,7 @@ static struct batadv_tvlv_container
if (tvlv_tmp->tvlv_hdr.version != version)
continue;
if (!atomic_inc_not_zero(&tvlv_tmp->refcount))
if (!kref_get_unless_zero(&tvlv_tmp->refcount))
continue;
tvlv = tvlv_tmp;
@@ -720,13 +754,15 @@ static struct batadv_tvlv_container
* Has to be called with the appropriate locks being acquired
* (tvlv.container_list_lock).
*
* Returns size of all currently registered tvlv containers in bytes.
* Return: size of all currently registered tvlv containers in bytes.
*/
static u16 batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
{
struct batadv_tvlv_container *tvlv;
u16 tvlv_len = 0;
lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
hlist_for_each_entry(tvlv, &bat_priv->tvlv.container_list, list) {
tvlv_len += sizeof(struct batadv_tvlv_hdr);
tvlv_len += ntohs(tvlv->tvlv_hdr.len);
@@ -755,8 +791,8 @@ static void batadv_tvlv_container_remove(struct batadv_priv *bat_priv,
hlist_del(&tvlv->list);
/* first call to decrement the counter, second call to free */
batadv_tvlv_container_free_ref(tvlv);
batadv_tvlv_container_free_ref(tvlv);
batadv_tvlv_container_put(tvlv);
batadv_tvlv_container_put(tvlv);
}
/**
@@ -808,7 +844,7 @@ void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
memcpy(tvlv_new + 1, tvlv_value, ntohs(tvlv_new->tvlv_hdr.len));
INIT_HLIST_NODE(&tvlv_new->list);
atomic_set(&tvlv_new->refcount, 1);
kref_init(&tvlv_new->refcount);
spin_lock_bh(&bat_priv->tvlv.container_list_lock);
tvlv_old = batadv_tvlv_container_get(bat_priv, type, version);
@@ -826,7 +862,7 @@ void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
* @additional_packet_len: requested additional packet size on top of minimum
* size
*
* Returns true of the packet buffer could be changed to the requested size,
* Return: true of the packet buffer could be changed to the requested size,
* false otherwise.
*/
static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff,
@@ -862,7 +898,7 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff,
* The ogm packet might be enlarged or shrunk depending on the current size
* and the size of the to-be-appended tvlv containers.
*
* Returns size of all appended tvlv containers in bytes.
* Return: size of all appended tvlv containers in bytes.
*/
u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
unsigned char **packet_buff,
@@ -915,7 +951,7 @@ end:
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
*
* Returns success if handler was not found or the return value of the handler
* Return: success if handler was not found or the return value of the handler
* callback.
*/
static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
@@ -968,7 +1004,7 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
*
* Returns success when processing an OGM or the return value of all called
* Return: success when processing an OGM or the return value of all called
* handler callbacks.
*/
int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
@@ -1001,7 +1037,7 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
src, dst, tvlv_value,
tvlv_value_cont_len);
if (tvlv_handler)
batadv_tvlv_handler_free_ref(tvlv_handler);
batadv_tvlv_handler_put(tvlv_handler);
tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
tvlv_value_len -= tvlv_value_cont_len;
}
@@ -1081,7 +1117,7 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version);
if (tvlv_handler) {
batadv_tvlv_handler_free_ref(tvlv_handler);
batadv_tvlv_handler_put(tvlv_handler);
return;
}
@@ -1094,7 +1130,7 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
tvlv_handler->type = type;
tvlv_handler->version = version;
tvlv_handler->flags = flags;
atomic_set(&tvlv_handler->refcount, 1);
kref_init(&tvlv_handler->refcount);
INIT_HLIST_NODE(&tvlv_handler->list);
spin_lock_bh(&bat_priv->tvlv.handler_list_lock);
@@ -1118,11 +1154,11 @@ void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv,
if (!tvlv_handler)
return;
batadv_tvlv_handler_free_ref(tvlv_handler);
batadv_tvlv_handler_put(tvlv_handler);
spin_lock_bh(&bat_priv->tvlv.handler_list_lock);
hlist_del_rcu(&tvlv_handler->list);
spin_unlock_bh(&bat_priv->tvlv.handler_list_lock);
batadv_tvlv_handler_free_ref(tvlv_handler);
batadv_tvlv_handler_put(tvlv_handler);
}
/**
@@ -1182,7 +1218,7 @@ void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, u8 *src,
if (batadv_send_skb_to_orig(skb, orig_node, NULL) == NET_XMIT_DROP)
kfree_skb(skb);
out:
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
}
/**
@@ -1190,8 +1226,8 @@ out:
* @skb: the buffer containing the packet
* @header_len: length of the batman header preceding the ethernet header
*
* If the packet embedded in the skb is vlan tagged this function returns the
* VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS is returned.
* Return: VID with the BATADV_VLAN_HAS_TAG flag when the packet embedded in the
* skb is vlan tagged. Otherwise BATADV_NO_FLAGS.
*/
unsigned short batadv_get_vid(struct sk_buff *skb, size_t header_len)
{
@@ -1218,7 +1254,7 @@ unsigned short batadv_get_vid(struct sk_buff *skb, size_t header_len)
* @vid: the VLAN identifier for which the AP isolation attributed as to be
* looked up
*
* Returns true if AP isolation is on for the VLAN idenfied by vid, false
* Return: true if AP isolation is on for the VLAN idenfied by vid, false
* otherwise
*/
bool batadv_vlan_ap_isola_get(struct batadv_priv *bat_priv, unsigned short vid)
@@ -1232,7 +1268,7 @@ bool batadv_vlan_ap_isola_get(struct batadv_priv *bat_priv, unsigned short vid)
vlan = batadv_softif_vlan_get(bat_priv, vid);
if (vlan) {
ap_isolation_enabled = atomic_read(&vlan->ap_isolation);
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_put(vlan);
}
return ap_isolation_enabled;

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -24,17 +24,21 @@
#define BATADV_DRIVER_DEVICE "batman-adv"
#ifndef BATADV_SOURCE_VERSION
#define BATADV_SOURCE_VERSION "2016.0"
#define BATADV_SOURCE_VERSION "2016.1"
#endif
/* B.A.T.M.A.N. parameters */
#define BATADV_TQ_MAX_VALUE 255
#define BATADV_THROUGHPUT_MAX_VALUE 0xFFFFFFFF
#define BATADV_JITTER 20
/* Time To Live of broadcast messages */
#define BATADV_TTL 50
/* maximum sequence number age of broadcast messages */
#define BATADV_BCAST_MAX_AGE 64
/* purge originators after time in seconds if no valid packet comes in
* -> TODO: check influence on BATADV_TQ_LOCAL_WINDOW_SIZE
*/
@@ -57,6 +61,15 @@
#define BATADV_TQ_LOCAL_BIDRECT_RECV_MINIMUM 1
#define BATADV_TQ_TOTAL_BIDRECT_LIMIT 1
/* B.A.T.M.A.N. V */
#define BATADV_THROUGHPUT_DEFAULT_VALUE 10 /* 1 Mbps */
#define BATADV_ELP_PROBES_PER_NODE 2
#define BATADV_ELP_MIN_PROBE_SIZE 200 /* bytes */
#define BATADV_ELP_PROBE_MAX_TX_DIFF 100 /* milliseconds */
#define BATADV_ELP_MAX_AGE 64
#define BATADV_OGM_MAX_ORIGDIFF 5
#define BATADV_OGM_MAX_AGE 64
/* number of OGMs sent with the last tt diff */
#define BATADV_TT_OGM_APPEND_MAX 3
@@ -97,11 +110,6 @@
*/
#define BATADV_TQ_SIMILARITY_THRESHOLD 50
/* how much worse secondary interfaces may be to be considered as bonding
* candidates
*/
#define BATADV_BONDING_TQ_THRESHOLD 50
/* should not be bigger than 512 bytes or change the size of
* forw_packet->direct_link_flags
*/
@@ -273,9 +281,14 @@ static inline void _batadv_dbg(int type __always_unused,
pr_err("%s: " fmt, _netdev->name, ## arg); \
} while (0)
/* returns 1 if they are the same ethernet addr
/**
* batadv_compare_eth - Compare two not u16 aligned Ethernet addresses
* @data1: Pointer to a six-byte array containing the Ethernet address
* @data2: Pointer other six-byte array containing the Ethernet address
*
* note: can't use ether_addr_equal() as it requires aligned memory
*
* Return: 1 if they are the same ethernet addr
*/
static inline bool batadv_compare_eth(const void *data1, const void *data2)
{
@@ -287,7 +300,7 @@ static inline bool batadv_compare_eth(const void *data1, const void *data2)
* @timestamp: base value to compare with (in jiffies)
* @timeout: added to base value before comparing (in milliseconds)
*
* Returns true if current time is after timestamp + timeout
* Return: true if current time is after timestamp + timeout
*/
static inline bool batadv_has_timed_out(unsigned long timestamp,
unsigned int timeout)
@@ -326,7 +339,13 @@ static inline void batadv_add_counter(struct batadv_priv *bat_priv, size_t idx,
#define batadv_inc_counter(b, i) batadv_add_counter(b, i, 1)
/* Sum and return the cpu-local counters for index 'idx' */
/**
* batadv_sum_counter - Sum the cpu-local counters for index 'idx'
* @bat_priv: the bat priv with all the soft interface information
* @idx: index of counter to sum up
*
* Return: sum of all cpu-local counters
*/
static inline u64 batadv_sum_counter(struct batadv_priv *bat_priv, size_t idx)
{
u64 *counters, sum = 0;

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2014-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2014-2016 B.A.T.M.A.N. contributors:
*
* Linus Lüssing
*
@@ -30,6 +30,7 @@
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/netdevice.h>
@@ -55,7 +56,7 @@
* Collect multicast addresses of the local multicast listeners
* on the given soft interface, dev, in the given mcast_list.
*
* Returns -ENOMEM on memory allocation error or the number of
* Return: -ENOMEM on memory allocation error or the number of
* items added to the mcast_list otherwise.
*/
static int batadv_mcast_mla_softif_get(struct net_device *dev,
@@ -87,7 +88,7 @@ static int batadv_mcast_mla_softif_get(struct net_device *dev,
* @mcast_addr: the multicast address to check
* @mcast_list: the list with multicast addresses to search in
*
* Returns true if the given address is already in the given list.
* Return: true if the given address is already in the given list.
* Otherwise returns false.
*/
static bool batadv_mcast_mla_is_duplicate(u8 *mcast_addr,
@@ -195,8 +196,9 @@ static void batadv_mcast_mla_tt_add(struct batadv_priv *bat_priv,
* batadv_mcast_has_bridge - check whether the soft-iface is bridged
* @bat_priv: the bat priv with all the soft interface information
*
* Checks whether there is a bridge on top of our soft interface. Returns
* true if so, false otherwise.
* Checks whether there is a bridge on top of our soft interface.
*
* Return: true if there is a bridge, false otherwise.
*/
static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)
{
@@ -218,7 +220,7 @@ static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)
* Updates the own multicast tvlv with our current multicast related settings,
* capabilities and inabilities.
*
* Returns true if the tvlv container is registered afterwards. Otherwise
* Return: true if the tvlv container is registered afterwards. Otherwise
* returns false.
*/
static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
@@ -289,8 +291,8 @@ out:
* Checks whether the given IPv4 packet has the potential to be forwarded with a
* mode more optimal than classic flooding.
*
* If so then returns 0. Otherwise -EINVAL is returned or -ENOMEM in case of
* memory allocation failure.
* Return: If so then 0. Otherwise -EINVAL or -ENOMEM in case of memory
* allocation failure.
*/
static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv,
struct sk_buff *skb,
@@ -327,8 +329,7 @@ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv,
* Checks whether the given IPv6 packet has the potential to be forwarded with a
* mode more optimal than classic flooding.
*
* If so then returns 0. Otherwise -EINVAL is returned or -ENOMEM if we are out
* of memory.
* Return: If so then 0. Otherwise -EINVAL is or -ENOMEM if we are out of memory
*/
static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv,
struct sk_buff *skb,
@@ -366,8 +367,7 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv,
* Checks whether the given multicast ethernet frame has the potential to be
* forwarded with a mode more optimal than classic flooding.
*
* If so then returns 0. Otherwise -EINVAL is returned or -ENOMEM if we are out
* of memory.
* Return: If so then 0. Otherwise -EINVAL is or -ENOMEM if we are out of memory
*/
static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv,
struct sk_buff *skb,
@@ -398,7 +398,7 @@ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv,
* @bat_priv: the bat priv with all the soft interface information
* @ethhdr: ethernet header of a packet
*
* Returns the number of nodes which want all IPv4 multicast traffic if the
* Return: the number of nodes which want all IPv4 multicast traffic if the
* given ethhdr is from an IPv4 packet or the number of nodes which want all
* IPv6 traffic if it matches an IPv6 packet.
*/
@@ -421,7 +421,7 @@ static int batadv_mcast_forw_want_all_ip_count(struct batadv_priv *bat_priv,
* @bat_priv: the bat priv with all the soft interface information
* @ethhdr: the ether header containing the multicast destination
*
* Returns an orig_node matching the multicast address provided by ethhdr
* Return: an orig_node matching the multicast address provided by ethhdr
* via a translation table lookup. This increases the returned nodes refcount.
*/
static struct batadv_orig_node *
@@ -436,7 +436,7 @@ batadv_mcast_forw_tt_node_get(struct batadv_priv *bat_priv,
* batadv_mcast_want_forw_ipv4_node_get - get a node with an ipv4 flag
* @bat_priv: the bat priv with all the soft interface information
*
* Returns an orig_node which has the BATADV_MCAST_WANT_ALL_IPV4 flag set and
* Return: an orig_node which has the BATADV_MCAST_WANT_ALL_IPV4 flag set and
* increases its refcount.
*/
static struct batadv_orig_node *
@@ -448,7 +448,7 @@ batadv_mcast_forw_ipv4_node_get(struct batadv_priv *bat_priv)
hlist_for_each_entry_rcu(tmp_orig_node,
&bat_priv->mcast.want_all_ipv4_list,
mcast_want_all_ipv4_node) {
if (!atomic_inc_not_zero(&tmp_orig_node->refcount))
if (!kref_get_unless_zero(&tmp_orig_node->refcount))
continue;
orig_node = tmp_orig_node;
@@ -463,7 +463,7 @@ batadv_mcast_forw_ipv4_node_get(struct batadv_priv *bat_priv)
* batadv_mcast_want_forw_ipv6_node_get - get a node with an ipv6 flag
* @bat_priv: the bat priv with all the soft interface information
*
* Returns an orig_node which has the BATADV_MCAST_WANT_ALL_IPV6 flag set
* Return: an orig_node which has the BATADV_MCAST_WANT_ALL_IPV6 flag set
* and increases its refcount.
*/
static struct batadv_orig_node *
@@ -475,7 +475,7 @@ batadv_mcast_forw_ipv6_node_get(struct batadv_priv *bat_priv)
hlist_for_each_entry_rcu(tmp_orig_node,
&bat_priv->mcast.want_all_ipv6_list,
mcast_want_all_ipv6_node) {
if (!atomic_inc_not_zero(&tmp_orig_node->refcount))
if (!kref_get_unless_zero(&tmp_orig_node->refcount))
continue;
orig_node = tmp_orig_node;
@@ -491,7 +491,7 @@ batadv_mcast_forw_ipv6_node_get(struct batadv_priv *bat_priv)
* @bat_priv: the bat priv with all the soft interface information
* @ethhdr: an ethernet header to determine the protocol family from
*
* Returns an orig_node which has the BATADV_MCAST_WANT_ALL_IPV4 or
* Return: an orig_node which has the BATADV_MCAST_WANT_ALL_IPV4 or
* BATADV_MCAST_WANT_ALL_IPV6 flag, depending on the provided ethhdr, set and
* increases its refcount.
*/
@@ -514,7 +514,7 @@ batadv_mcast_forw_ip_node_get(struct batadv_priv *bat_priv,
* batadv_mcast_want_forw_unsnoop_node_get - get a node with an unsnoopable flag
* @bat_priv: the bat priv with all the soft interface information
*
* Returns an orig_node which has the BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag
* Return: an orig_node which has the BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag
* set and increases its refcount.
*/
static struct batadv_orig_node *
@@ -526,7 +526,7 @@ batadv_mcast_forw_unsnoop_node_get(struct batadv_priv *bat_priv)
hlist_for_each_entry_rcu(tmp_orig_node,
&bat_priv->mcast.want_all_unsnoopables_list,
mcast_want_all_unsnoopables_node) {
if (!atomic_inc_not_zero(&tmp_orig_node->refcount))
if (!kref_get_unless_zero(&tmp_orig_node->refcount))
continue;
orig_node = tmp_orig_node;
@@ -543,7 +543,7 @@ batadv_mcast_forw_unsnoop_node_get(struct batadv_priv *bat_priv)
* @skb: The multicast packet to check
* @orig: an originator to be set to forward the skb to
*
* Returns the forwarding mode as enum batadv_forw_mode and in case of
* Return: the forwarding mode as enum batadv_forw_mode and in case of
* BATADV_FORW_SINGLE set the orig to the single originator the skb
* should be forwarded to.
*/

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2014-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2014-2016 B.A.T.M.A.N. contributors:
*
* Linus Lüssing
*
@@ -23,7 +23,7 @@
struct sk_buff;
/**
* batadv_forw_mode - the way a packet should be forwarded as
* enum batadv_forw_mode - the way a packet should be forwarded as
* @BATADV_FORW_ALL: forward the packet to all nodes (currently via classic
* flooding)
* @BATADV_FORW_SINGLE: forward the packet to a single node (currently via the

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2012-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2012-2016 B.A.T.M.A.N. contributors:
*
* Martin Hundebøll, Jeppe Ledet-Pedersen
*
@@ -32,6 +32,7 @@
#include <linux/jhash.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/netdevice.h>
@@ -64,6 +65,8 @@ static int batadv_nc_recv_coded_packet(struct sk_buff *skb,
/**
* batadv_nc_init - one-time initialization for network coding
*
* Return: 0 on success or negative error number in case of failure
*/
int __init batadv_nc_init(void)
{
@@ -142,6 +145,8 @@ static void batadv_nc_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
/**
* batadv_nc_mesh_init - initialise coding hash table and start house keeping
* @bat_priv: the bat priv with all the soft interface information
*
* Return: 0 on success or negative error number in case of failure
*/
int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
{
@@ -205,34 +210,50 @@ void batadv_nc_init_orig(struct batadv_orig_node *orig_node)
/**
* batadv_nc_node_release - release nc_node from lists and queue for free after
* rcu grace period
* @nc_node: the nc node to free
* @ref: kref pointer of the nc_node
*/
static void batadv_nc_node_release(struct batadv_nc_node *nc_node)
static void batadv_nc_node_release(struct kref *ref)
{
batadv_orig_node_free_ref(nc_node->orig_node);
struct batadv_nc_node *nc_node;
nc_node = container_of(ref, struct batadv_nc_node, refcount);
batadv_orig_node_put(nc_node->orig_node);
kfree_rcu(nc_node, rcu);
}
/**
* batadv_nc_node_free_ref - decrement the nc node refcounter and possibly
* batadv_nc_node_put - decrement the nc_node refcounter and possibly
* release it
* @nc_node: the nc node to free
* @nc_node: nc_node to be free'd
*/
static void batadv_nc_node_free_ref(struct batadv_nc_node *nc_node)
static void batadv_nc_node_put(struct batadv_nc_node *nc_node)
{
if (atomic_dec_and_test(&nc_node->refcount))
batadv_nc_node_release(nc_node);
kref_put(&nc_node->refcount, batadv_nc_node_release);
}
/**
* batadv_nc_path_free_ref - decrements the nc path refcounter and possibly
* frees it
* @nc_path: the nc node to free
* batadv_nc_path_release - release nc_path from lists and queue for free after
* rcu grace period
* @ref: kref pointer of the nc_path
*/
static void batadv_nc_path_free_ref(struct batadv_nc_path *nc_path)
static void batadv_nc_path_release(struct kref *ref)
{
if (atomic_dec_and_test(&nc_path->refcount))
kfree_rcu(nc_path, rcu);
struct batadv_nc_path *nc_path;
nc_path = container_of(ref, struct batadv_nc_path, refcount);
kfree_rcu(nc_path, rcu);
}
/**
* batadv_nc_path_put - decrement the nc_path refcounter and possibly
* release it
* @nc_path: nc_path to be free'd
*/
static void batadv_nc_path_put(struct batadv_nc_path *nc_path)
{
kref_put(&nc_path->refcount, batadv_nc_path_release);
}
/**
@@ -242,7 +263,7 @@ static void batadv_nc_path_free_ref(struct batadv_nc_path *nc_path)
static void batadv_nc_packet_free(struct batadv_nc_packet *nc_packet)
{
kfree_skb(nc_packet->skb);
batadv_nc_path_free_ref(nc_packet->nc_path);
batadv_nc_path_put(nc_packet->nc_path);
kfree(nc_packet);
}
@@ -251,7 +272,7 @@ static void batadv_nc_packet_free(struct batadv_nc_packet *nc_packet)
* @bat_priv: the bat priv with all the soft interface information
* @nc_node: the nc node to check
*
* Returns true if the entry has to be purged now, false otherwise
* Return: true if the entry has to be purged now, false otherwise
*/
static bool batadv_nc_to_purge_nc_node(struct batadv_priv *bat_priv,
struct batadv_nc_node *nc_node)
@@ -267,7 +288,7 @@ static bool batadv_nc_to_purge_nc_node(struct batadv_priv *bat_priv,
* @bat_priv: the bat priv with all the soft interface information
* @nc_path: the nc path to check
*
* Returns true if the entry has to be purged now, false otherwise
* Return: true if the entry has to be purged now, false otherwise
*/
static bool batadv_nc_to_purge_nc_path_coding(struct batadv_priv *bat_priv,
struct batadv_nc_path *nc_path)
@@ -287,7 +308,7 @@ static bool batadv_nc_to_purge_nc_path_coding(struct batadv_priv *bat_priv,
* @bat_priv: the bat priv with all the soft interface information
* @nc_path: the nc path to check
*
* Returns true if the entry has to be purged now, false otherwise
* Return: true if the entry has to be purged now, false otherwise
*/
static bool batadv_nc_to_purge_nc_path_decoding(struct batadv_priv *bat_priv,
struct batadv_nc_path *nc_path)
@@ -335,7 +356,7 @@ batadv_nc_purge_orig_nc_nodes(struct batadv_priv *bat_priv,
"Removing nc_node %pM -> %pM\n",
nc_node->addr, nc_node->orig_node->orig);
list_del_rcu(&nc_node->list);
batadv_nc_node_free_ref(nc_node);
batadv_nc_node_put(nc_node);
}
spin_unlock_bh(lock);
}
@@ -446,7 +467,7 @@ static void batadv_nc_purge_paths(struct batadv_priv *bat_priv,
"Remove nc_path %pM -> %pM\n",
nc_path->prev_hop, nc_path->next_hop);
hlist_del_rcu(&nc_path->hash_entry);
batadv_nc_path_free_ref(nc_path);
batadv_nc_path_put(nc_path);
}
spin_unlock_bh(lock);
}
@@ -470,7 +491,7 @@ static void batadv_nc_hash_key_gen(struct batadv_nc_path *key, const char *src,
* @data: data to hash
* @size: size of the hash table
*
* Returns the selected index in the hash table for the given data.
* Return: the selected index in the hash table for the given data.
*/
static u32 batadv_nc_hash_choose(const void *data, u32 size)
{
@@ -489,7 +510,7 @@ static u32 batadv_nc_hash_choose(const void *data, u32 size)
* @node: node in the local table
* @data2: second object to compare the node to
*
* Returns 1 if the two entry are the same, 0 otherwise
* Return: 1 if the two entry are the same, 0 otherwise
*/
static int batadv_nc_hash_compare(const struct hlist_node *node,
const void *data2)
@@ -516,7 +537,7 @@ static int batadv_nc_hash_compare(const struct hlist_node *node,
* @hash: hash table containing the nc path
* @data: search key
*
* Returns the nc_path if found, NULL otherwise.
* Return: the nc_path if found, NULL otherwise.
*/
static struct batadv_nc_path *
batadv_nc_hash_find(struct batadv_hashtable *hash,
@@ -537,7 +558,7 @@ batadv_nc_hash_find(struct batadv_hashtable *hash,
if (!batadv_nc_hash_compare(&nc_path->hash_entry, data))
continue;
if (!atomic_inc_not_zero(&nc_path->refcount))
if (!kref_get_unless_zero(&nc_path->refcount))
continue;
nc_path_tmp = nc_path;
@@ -554,9 +575,7 @@ batadv_nc_hash_find(struct batadv_hashtable *hash,
*/
static void batadv_nc_send_packet(struct batadv_nc_packet *nc_packet)
{
batadv_send_skb_packet(nc_packet->skb,
nc_packet->neigh_node->if_incoming,
nc_packet->nc_path->next_hop);
batadv_send_unicast_skb(nc_packet->skb, nc_packet->neigh_node);
nc_packet->skb = NULL;
batadv_nc_packet_free(nc_packet);
}
@@ -571,7 +590,7 @@ static void batadv_nc_send_packet(struct batadv_nc_packet *nc_packet)
* timeout. If so, the packet is no longer kept and the entry deleted from the
* queue. Has to be called with the appropriate locks.
*
* Returns false as soon as the entry in the fifo queue has not been timed out
* Return: false as soon as the entry in the fifo queue has not been timed out
* yet and true otherwise.
*/
static bool batadv_nc_sniffed_purge(struct batadv_priv *bat_priv,
@@ -610,7 +629,7 @@ out:
* packet is no longer delayed, immediately sent and the entry deleted from the
* queue. Has to be called with the appropriate locks.
*
* Returns false as soon as the entry in the fifo queue has not been timed out
* Return: false as soon as the entry in the fifo queue has not been timed out
* yet and true otherwise.
*/
static bool batadv_nc_fwd_flush(struct batadv_priv *bat_priv,
@@ -731,7 +750,7 @@ static void batadv_nc_worker(struct work_struct *work)
* @orig_node: neighboring orig node which may be used as nc candidate
* @ogm_packet: incoming ogm packet also used for the checks
*
* Returns true if:
* Return: true if:
* 1) The OGM must have the most recent sequence number.
* 2) The TTL must be decremented by one and only one.
* 3) The OGM must be received from the first hop from orig_node.
@@ -751,7 +770,7 @@ static bool batadv_can_nc_with_orig(struct batadv_priv *bat_priv,
last_ttl = orig_ifinfo->last_ttl;
last_real_seqno = orig_ifinfo->last_real_seqno;
batadv_orig_ifinfo_free_ref(orig_ifinfo);
batadv_orig_ifinfo_put(orig_ifinfo);
if (last_real_seqno != ntohl(ogm_packet->seqno))
return false;
@@ -772,7 +791,7 @@ static bool batadv_can_nc_with_orig(struct batadv_priv *bat_priv,
* (can be equal to orig_node)
* @in_coding: traverse incoming or outgoing network coding list
*
* Returns the nc_node if found, NULL otherwise.
* Return: the nc_node if found, NULL otherwise.
*/
static struct batadv_nc_node
*batadv_nc_find_nc_node(struct batadv_orig_node *orig_node,
@@ -793,7 +812,7 @@ static struct batadv_nc_node
if (!batadv_compare_eth(nc_node->addr, orig_node->orig))
continue;
if (!atomic_inc_not_zero(&nc_node->refcount))
if (!kref_get_unless_zero(&nc_node->refcount))
continue;
/* Found a match */
@@ -814,7 +833,7 @@ static struct batadv_nc_node
* (can be equal to orig_node)
* @in_coding: traverse incoming or outgoing network coding list
*
* Returns the nc_node if found or created, NULL in case of an error.
* Return: the nc_node if found or created, NULL in case of an error.
*/
static struct batadv_nc_node
*batadv_nc_get_nc_node(struct batadv_priv *bat_priv,
@@ -837,14 +856,15 @@ static struct batadv_nc_node
if (!nc_node)
return NULL;
if (!atomic_inc_not_zero(&orig_neigh_node->refcount))
if (!kref_get_unless_zero(&orig_neigh_node->refcount))
goto free;
/* Initialize nc_node */
INIT_LIST_HEAD(&nc_node->list);
ether_addr_copy(nc_node->addr, orig_node->orig);
nc_node->orig_node = orig_neigh_node;
atomic_set(&nc_node->refcount, 2);
kref_init(&nc_node->refcount);
kref_get(&nc_node->refcount);
/* Select ingoing or outgoing coding node */
if (in_coding) {
@@ -920,9 +940,9 @@ void batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
out:
if (in_nc_node)
batadv_nc_node_free_ref(in_nc_node);
batadv_nc_node_put(in_nc_node);
if (out_nc_node)
batadv_nc_node_free_ref(out_nc_node);
batadv_nc_node_put(out_nc_node);
}
/**
@@ -932,7 +952,7 @@ out:
* @src: ethernet source address - first half of the nc path search key
* @dst: ethernet destination address - second half of the nc path search key
*
* Returns pointer to nc_path if the path was found or created, returns NULL
* Return: pointer to nc_path if the path was found or created, returns NULL
* on error.
*/
static struct batadv_nc_path *batadv_nc_get_path(struct batadv_priv *bat_priv,
@@ -963,7 +983,8 @@ static struct batadv_nc_path *batadv_nc_get_path(struct batadv_priv *bat_priv,
/* Initialize nc_path */
INIT_LIST_HEAD(&nc_path->packet_list);
spin_lock_init(&nc_path->packet_list_lock);
atomic_set(&nc_path->refcount, 2);
kref_init(&nc_path->refcount);
kref_get(&nc_path->refcount);
nc_path->last_valid = jiffies;
ether_addr_copy(nc_path->next_hop, dst);
ether_addr_copy(nc_path->prev_hop, src);
@@ -989,6 +1010,8 @@ static struct batadv_nc_path *batadv_nc_get_path(struct batadv_priv *bat_priv,
* batadv_nc_random_weight_tq - scale the receivers TQ-value to avoid unfair
* selection of a receiver with slightly lower TQ than the other
* @tq: to be weighted tq value
*
* Return: scaled tq value
*/
static u8 batadv_nc_random_weight_tq(u8 tq)
{
@@ -1029,7 +1052,7 @@ static void batadv_nc_memxor(char *dst, const char *src, unsigned int len)
* @nc_packet: structure containing the packet to the skb can be coded with
* @neigh_node: next hop to forward packet to
*
* Returns true if both packets are consumed, false otherwise.
* Return: true if both packets are consumed, false otherwise.
*/
static bool batadv_nc_code_packets(struct batadv_priv *bat_priv,
struct sk_buff *skb,
@@ -1042,11 +1065,11 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv,
struct batadv_unicast_packet *packet1;
struct batadv_unicast_packet *packet2;
struct batadv_coded_packet *coded_packet;
struct batadv_neigh_node *neigh_tmp, *router_neigh;
struct batadv_neigh_node *router_coding = NULL;
struct batadv_neigh_node *neigh_tmp, *router_neigh, *first_dest;
struct batadv_neigh_node *router_coding = NULL, *second_dest;
struct batadv_neigh_ifinfo *router_neigh_ifinfo = NULL;
struct batadv_neigh_ifinfo *router_coding_ifinfo = NULL;
u8 *first_source, *first_dest, *second_source, *second_dest;
u8 *first_source, *second_source;
__be32 packet_id1, packet_id2;
size_t count;
bool res = false;
@@ -1089,9 +1112,9 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv,
*/
if (tq_weighted_neigh >= tq_weighted_coding) {
/* Destination from nc_packet is selected for MAC-header */
first_dest = nc_packet->nc_path->next_hop;
first_dest = nc_packet->neigh_node;
first_source = nc_packet->nc_path->prev_hop;
second_dest = neigh_node->addr;
second_dest = neigh_node;
second_source = ethhdr->h_source;
packet1 = (struct batadv_unicast_packet *)nc_packet->skb->data;
packet2 = (struct batadv_unicast_packet *)skb->data;
@@ -1100,9 +1123,9 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv,
skb->data + sizeof(*packet2));
} else {
/* Destination for skb is selected for MAC-header */
first_dest = neigh_node->addr;
first_dest = neigh_node;
first_source = ethhdr->h_source;
second_dest = nc_packet->nc_path->next_hop;
second_dest = nc_packet->neigh_node;
second_source = nc_packet->nc_path->prev_hop;
packet1 = (struct batadv_unicast_packet *)skb->data;
packet2 = (struct batadv_unicast_packet *)nc_packet->skb->data;
@@ -1144,7 +1167,7 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv,
coded_packet->first_ttvn = packet1->ttvn;
/* Info about second unicast packet */
ether_addr_copy(coded_packet->second_dest, second_dest);
ether_addr_copy(coded_packet->second_dest, second_dest->addr);
ether_addr_copy(coded_packet->second_source, second_source);
ether_addr_copy(coded_packet->second_orig_dest, packet2->dest);
coded_packet->second_crc = packet_id2;
@@ -1199,17 +1222,17 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv,
batadv_nc_packet_free(nc_packet);
/* Send the coded packet and return true */
batadv_send_skb_packet(skb_dest, neigh_node->if_incoming, first_dest);
batadv_send_unicast_skb(skb_dest, first_dest);
res = true;
out:
if (router_neigh)
batadv_neigh_node_free_ref(router_neigh);
batadv_neigh_node_put(router_neigh);
if (router_coding)
batadv_neigh_node_free_ref(router_coding);
batadv_neigh_node_put(router_coding);
if (router_neigh_ifinfo)
batadv_neigh_ifinfo_free_ref(router_neigh_ifinfo);
batadv_neigh_ifinfo_put(router_neigh_ifinfo);
if (router_coding_ifinfo)
batadv_neigh_ifinfo_free_ref(router_coding_ifinfo);
batadv_neigh_ifinfo_put(router_coding_ifinfo);
return res;
}
@@ -1228,7 +1251,7 @@ out:
* Since the source encoded the packet we can be certain it has all necessary
* decode information.
*
* Returns true if coding of a decoded packet is allowed.
* Return: true if coding of a decoded packet is allowed.
*/
static bool batadv_nc_skb_coding_possible(struct sk_buff *skb, u8 *dst, u8 *src)
{
@@ -1246,7 +1269,7 @@ static bool batadv_nc_skb_coding_possible(struct sk_buff *skb, u8 *dst, u8 *src)
* @skb: data skb to forward
* @eth_dst: next hop mac address of skb
*
* Returns true if coding of a decoded skb is allowed.
* Return: true if coding of a decoded skb is allowed.
*/
static struct batadv_nc_packet *
batadv_nc_path_search(struct batadv_priv *bat_priv,
@@ -1314,7 +1337,7 @@ batadv_nc_path_search(struct batadv_priv *bat_priv,
* @eth_src: source mac address of skb
* @in_nc_node: pointer to skb next hop's neighbor nc node
*
* Returns an nc packet if a suitable coding packet was found, NULL otherwise.
* Return: an nc packet if a suitable coding packet was found, NULL otherwise.
*/
static struct batadv_nc_packet *
batadv_nc_skb_src_search(struct batadv_priv *bat_priv,
@@ -1347,7 +1370,7 @@ batadv_nc_skb_src_search(struct batadv_priv *bat_priv,
}
rcu_read_unlock();
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
return nc_packet;
}
@@ -1397,7 +1420,7 @@ static void batadv_nc_skb_store_before_coding(struct batadv_priv *bat_priv,
* next hop that potentially sent a packet which our next hop also received
* (overheard) and has stored for later decoding.
*
* Returns true if the skb was consumed (encoded packet sent) or false otherwise
* Return: true if the skb was consumed (encoded packet sent) or false otherwise
*/
static bool batadv_nc_skb_dst_search(struct sk_buff *skb,
struct batadv_neigh_node *neigh_node,
@@ -1451,7 +1474,7 @@ static bool batadv_nc_skb_dst_search(struct sk_buff *skb,
* @neigh_node: next hop to forward packet to
* @packet_id: checksum to identify packet
*
* Returns true if the packet was buffered or false in case of an error.
* Return: true if the packet was buffered or false in case of an error.
*/
static bool batadv_nc_skb_add_to_path(struct sk_buff *skb,
struct batadv_nc_path *nc_path,
@@ -1485,7 +1508,7 @@ static bool batadv_nc_skb_add_to_path(struct sk_buff *skb,
* @skb: data skb to forward
* @neigh_node: next hop to forward packet to
*
* Returns true if the skb was consumed (encoded packet sent) or false otherwise
* Return: true if the skb was consumed (encoded packet sent) or false otherwise
*/
bool batadv_nc_skb_forward(struct sk_buff *skb,
struct batadv_neigh_node *neigh_node)
@@ -1530,7 +1553,7 @@ bool batadv_nc_skb_forward(struct sk_buff *skb,
return true;
free_nc_path:
batadv_nc_path_free_ref(nc_path);
batadv_nc_path_put(nc_path);
out:
/* Packet is not consumed */
return false;
@@ -1592,7 +1615,7 @@ void batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv,
free_skb:
kfree_skb(skb);
free_nc_path:
batadv_nc_path_free_ref(nc_path);
batadv_nc_path_put(nc_path);
out:
return;
}
@@ -1624,7 +1647,7 @@ void batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv,
* @skb: unicast skb to decode
* @nc_packet: decode data needed to decode the skb
*
* Returns pointer to decoded unicast packet if the packet was decoded or NULL
* Return: pointer to decoded unicast packet if the packet was decoded or NULL
* in case of an error.
*/
static struct batadv_unicast_packet *
@@ -1718,7 +1741,7 @@ batadv_nc_skb_decode_packet(struct batadv_priv *bat_priv, struct sk_buff *skb,
* @ethhdr: pointer to the ethernet header inside the coded packet
* @coded: coded packet we try to find decode data for
*
* Returns pointer to nc packet if the needed data was found or NULL otherwise.
* Return: pointer to nc packet if the needed data was found or NULL otherwise.
*/
static struct batadv_nc_packet *
batadv_nc_find_decoding_packet(struct batadv_priv *bat_priv,
@@ -1781,6 +1804,9 @@ batadv_nc_find_decoding_packet(struct batadv_priv *bat_priv,
* resulting unicast packet
* @skb: incoming coded packet
* @recv_if: pointer to interface this packet was received on
*
* Return: NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
* otherwise.
*/
static int batadv_nc_recv_coded_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
@@ -1865,6 +1891,8 @@ void batadv_nc_mesh_free(struct batadv_priv *bat_priv)
* batadv_nc_nodes_seq_print_text - print the nc node information
* @seq: seq file to print on
* @offset: not used
*
* Return: always 0
*/
int batadv_nc_nodes_seq_print_text(struct seq_file *seq, void *offset)
{
@@ -1920,13 +1948,15 @@ int batadv_nc_nodes_seq_print_text(struct seq_file *seq, void *offset)
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
return 0;
}
/**
* batadv_nc_init_debugfs - create nc folder and related files in debugfs
* @bat_priv: the bat priv with all the soft interface information
*
* Return: 0 on success or negative error number in case of failure
*/
int batadv_nc_init_debugfs(struct batadv_priv *bat_priv)
{

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2012-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2012-2016 B.A.T.M.A.N. contributors:
*
* Martin Hundebøll, Jeppe Ledet-Pedersen
*

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2009-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2009-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -18,11 +18,13 @@
#include "originator.h"
#include "main.h"
#include <linux/atomic.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/fs.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/netdevice.h>
@@ -47,7 +49,13 @@ static struct lock_class_key batadv_orig_hash_lock_class_key;
static void batadv_purge_orig(struct work_struct *work);
/* returns 1 if they are the same originator */
/**
* batadv_compare_orig - comparing function used in the originator hash table
* @node: node in the local table
* @data2: second object to compare the node to
*
* Return: 1 if they are the same originator
*/
int batadv_compare_orig(const struct hlist_node *node, const void *data2)
{
const void *data1 = container_of(node, struct batadv_orig_node,
@@ -61,7 +69,7 @@ int batadv_compare_orig(const struct hlist_node *node, const void *data2)
* @orig_node: the originator serving the VLAN
* @vid: the VLAN identifier
*
* Returns the vlan object identified by vid and belonging to orig_node or NULL
* Return: the vlan object identified by vid and belonging to orig_node or NULL
* if it does not exist.
*/
struct batadv_orig_node_vlan *
@@ -75,7 +83,7 @@ batadv_orig_node_vlan_get(struct batadv_orig_node *orig_node,
if (tmp->vid != vid)
continue;
if (!atomic_inc_not_zero(&tmp->refcount))
if (!kref_get_unless_zero(&tmp->refcount))
continue;
vlan = tmp;
@@ -93,7 +101,7 @@ batadv_orig_node_vlan_get(struct batadv_orig_node *orig_node,
* @orig_node: the originator serving the VLAN
* @vid: the VLAN identifier
*
* Returns NULL in case of failure or the vlan object identified by vid and
* Return: NULL in case of failure or the vlan object identified by vid and
* belonging to orig_node otherwise. The object is created and added to the list
* if it does not exist.
*
@@ -116,7 +124,8 @@ batadv_orig_node_vlan_new(struct batadv_orig_node *orig_node,
if (!vlan)
goto out;
atomic_set(&vlan->refcount, 2);
kref_init(&vlan->refcount);
kref_get(&vlan->refcount);
vlan->vid = vid;
hlist_add_head_rcu(&vlan->list, &orig_node->vlan_list);
@@ -128,14 +137,27 @@ out:
}
/**
* batadv_orig_node_vlan_free_ref - decrement the refcounter and possibly free
* batadv_orig_node_vlan_release - release originator-vlan object from lists
* and queue for free after rcu grace period
* @ref: kref pointer of the originator-vlan object
*/
static void batadv_orig_node_vlan_release(struct kref *ref)
{
struct batadv_orig_node_vlan *orig_vlan;
orig_vlan = container_of(ref, struct batadv_orig_node_vlan, refcount);
kfree_rcu(orig_vlan, rcu);
}
/**
* batadv_orig_node_vlan_put - decrement the refcounter and possibly release
* the originator-vlan object
* @orig_vlan: the originator-vlan object to release
*/
void batadv_orig_node_vlan_free_ref(struct batadv_orig_node_vlan *orig_vlan)
void batadv_orig_node_vlan_put(struct batadv_orig_node_vlan *orig_vlan)
{
if (atomic_dec_and_test(&orig_vlan->refcount))
kfree_rcu(orig_vlan, rcu);
kref_put(&orig_vlan->refcount, batadv_orig_node_vlan_release);
}
int batadv_originator_init(struct batadv_priv *bat_priv)
@@ -165,99 +187,105 @@ err:
/**
* batadv_neigh_ifinfo_release - release neigh_ifinfo from lists and queue for
* free after rcu grace period
* @neigh_ifinfo: the neigh_ifinfo object to release
* @ref: kref pointer of the neigh_ifinfo
*/
static void
batadv_neigh_ifinfo_release(struct batadv_neigh_ifinfo *neigh_ifinfo)
static void batadv_neigh_ifinfo_release(struct kref *ref)
{
struct batadv_neigh_ifinfo *neigh_ifinfo;
neigh_ifinfo = container_of(ref, struct batadv_neigh_ifinfo, refcount);
if (neigh_ifinfo->if_outgoing != BATADV_IF_DEFAULT)
batadv_hardif_free_ref(neigh_ifinfo->if_outgoing);
batadv_hardif_put(neigh_ifinfo->if_outgoing);
kfree_rcu(neigh_ifinfo, rcu);
}
/**
* batadv_neigh_ifinfo_free_ref - decrement the refcounter and possibly release
* batadv_neigh_ifinfo_put - decrement the refcounter and possibly release
* the neigh_ifinfo
* @neigh_ifinfo: the neigh_ifinfo object to release
*/
void batadv_neigh_ifinfo_free_ref(struct batadv_neigh_ifinfo *neigh_ifinfo)
void batadv_neigh_ifinfo_put(struct batadv_neigh_ifinfo *neigh_ifinfo)
{
if (atomic_dec_and_test(&neigh_ifinfo->refcount))
batadv_neigh_ifinfo_release(neigh_ifinfo);
kref_put(&neigh_ifinfo->refcount, batadv_neigh_ifinfo_release);
}
/**
* batadv_hardif_neigh_release - release hardif neigh node from lists and
* queue for free after rcu grace period
* @hardif_neigh: hardif neigh neighbor to free
* @ref: kref pointer of the neigh_node
*/
static void
batadv_hardif_neigh_release(struct batadv_hardif_neigh_node *hardif_neigh)
static void batadv_hardif_neigh_release(struct kref *ref)
{
struct batadv_hardif_neigh_node *hardif_neigh;
hardif_neigh = container_of(ref, struct batadv_hardif_neigh_node,
refcount);
spin_lock_bh(&hardif_neigh->if_incoming->neigh_list_lock);
hlist_del_init_rcu(&hardif_neigh->list);
spin_unlock_bh(&hardif_neigh->if_incoming->neigh_list_lock);
batadv_hardif_free_ref(hardif_neigh->if_incoming);
batadv_hardif_put(hardif_neigh->if_incoming);
kfree_rcu(hardif_neigh, rcu);
}
/**
* batadv_hardif_neigh_free_ref - decrement the hardif neighbors refcounter
* batadv_hardif_neigh_put - decrement the hardif neighbors refcounter
* and possibly release it
* @hardif_neigh: hardif neigh neighbor to free
*/
void batadv_hardif_neigh_free_ref(struct batadv_hardif_neigh_node *hardif_neigh)
void batadv_hardif_neigh_put(struct batadv_hardif_neigh_node *hardif_neigh)
{
if (atomic_dec_and_test(&hardif_neigh->refcount))
batadv_hardif_neigh_release(hardif_neigh);
kref_put(&hardif_neigh->refcount, batadv_hardif_neigh_release);
}
/**
* batadv_neigh_node_release - release neigh_node from lists and queue for
* free after rcu grace period
* @neigh_node: neigh neighbor to free
* @ref: kref pointer of the neigh_node
*/
static void batadv_neigh_node_release(struct batadv_neigh_node *neigh_node)
static void batadv_neigh_node_release(struct kref *ref)
{
struct hlist_node *node_tmp;
struct batadv_neigh_node *neigh_node;
struct batadv_hardif_neigh_node *hardif_neigh;
struct batadv_neigh_ifinfo *neigh_ifinfo;
struct batadv_algo_ops *bao;
neigh_node = container_of(ref, struct batadv_neigh_node, refcount);
bao = neigh_node->orig_node->bat_priv->bat_algo_ops;
hlist_for_each_entry_safe(neigh_ifinfo, node_tmp,
&neigh_node->ifinfo_list, list) {
batadv_neigh_ifinfo_free_ref(neigh_ifinfo);
batadv_neigh_ifinfo_put(neigh_ifinfo);
}
hardif_neigh = batadv_hardif_neigh_get(neigh_node->if_incoming,
neigh_node->addr);
if (hardif_neigh) {
/* batadv_hardif_neigh_get() increases refcount too */
batadv_hardif_neigh_free_ref(hardif_neigh);
batadv_hardif_neigh_free_ref(hardif_neigh);
batadv_hardif_neigh_put(hardif_neigh);
batadv_hardif_neigh_put(hardif_neigh);
}
if (bao->bat_neigh_free)
bao->bat_neigh_free(neigh_node);
batadv_hardif_free_ref(neigh_node->if_incoming);
batadv_hardif_put(neigh_node->if_incoming);
kfree_rcu(neigh_node, rcu);
}
/**
* batadv_neigh_node_free_ref - decrement the neighbors refcounter
* and possibly release it
* batadv_neigh_node_put - decrement the neighbors refcounter and possibly
* release it
* @neigh_node: neigh neighbor to free
*/
void batadv_neigh_node_free_ref(struct batadv_neigh_node *neigh_node)
void batadv_neigh_node_put(struct batadv_neigh_node *neigh_node)
{
if (atomic_dec_and_test(&neigh_node->refcount))
batadv_neigh_node_release(neigh_node);
kref_put(&neigh_node->refcount, batadv_neigh_node_release);
}
/**
@@ -266,7 +294,7 @@ void batadv_neigh_node_free_ref(struct batadv_neigh_node *neigh_node)
* @if_outgoing: the interface where the payload packet has been received or
* the OGM should be sent to
*
* Returns the neighbor which should be router for this orig_node/iface.
* Return: the neighbor which should be router for this orig_node/iface.
*
* The object is returned with refcounter increased by 1.
*/
@@ -286,7 +314,7 @@ batadv_orig_router_get(struct batadv_orig_node *orig_node,
break;
}
if (router && !atomic_inc_not_zero(&router->refcount))
if (router && !kref_get_unless_zero(&router->refcount))
router = NULL;
rcu_read_unlock();
@@ -298,7 +326,7 @@ batadv_orig_router_get(struct batadv_orig_node *orig_node,
* @orig_node: the orig node to be queried
* @if_outgoing: the interface for which the ifinfo should be acquired
*
* Returns the requested orig_ifinfo or NULL if not found.
* Return: the requested orig_ifinfo or NULL if not found.
*
* The object is returned with refcounter increased by 1.
*/
@@ -314,7 +342,7 @@ batadv_orig_ifinfo_get(struct batadv_orig_node *orig_node,
if (tmp->if_outgoing != if_outgoing)
continue;
if (!atomic_inc_not_zero(&tmp->refcount))
if (!kref_get_unless_zero(&tmp->refcount))
continue;
orig_ifinfo = tmp;
@@ -330,7 +358,7 @@ batadv_orig_ifinfo_get(struct batadv_orig_node *orig_node,
* @orig_node: the orig node to be queried
* @if_outgoing: the interface for which the ifinfo should be acquired
*
* Returns NULL in case of failure or the orig_ifinfo object for the if_outgoing
* Return: NULL in case of failure or the orig_ifinfo object for the if_outgoing
* interface otherwise. The object is created and added to the list
* if it does not exist.
*
@@ -354,7 +382,7 @@ batadv_orig_ifinfo_new(struct batadv_orig_node *orig_node,
goto out;
if (if_outgoing != BATADV_IF_DEFAULT &&
!atomic_inc_not_zero(&if_outgoing->refcount)) {
!kref_get_unless_zero(&if_outgoing->refcount)) {
kfree(orig_ifinfo);
orig_ifinfo = NULL;
goto out;
@@ -365,7 +393,8 @@ batadv_orig_ifinfo_new(struct batadv_orig_node *orig_node,
orig_ifinfo->batman_seqno_reset = reset_time;
orig_ifinfo->if_outgoing = if_outgoing;
INIT_HLIST_NODE(&orig_ifinfo->list);
atomic_set(&orig_ifinfo->refcount, 2);
kref_init(&orig_ifinfo->refcount);
kref_get(&orig_ifinfo->refcount);
hlist_add_head_rcu(&orig_ifinfo->list,
&orig_node->ifinfo_list);
out:
@@ -375,12 +404,12 @@ out:
/**
* batadv_neigh_ifinfo_get - find the ifinfo from an neigh_node
* @neigh_node: the neigh node to be queried
* @neigh: the neigh node to be queried
* @if_outgoing: the interface for which the ifinfo should be acquired
*
* The object is returned with refcounter increased by 1.
*
* Returns the requested neigh_ifinfo or NULL if not found
* Return: the requested neigh_ifinfo or NULL if not found
*/
struct batadv_neigh_ifinfo *
batadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh,
@@ -395,7 +424,7 @@ batadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh,
if (tmp_neigh_ifinfo->if_outgoing != if_outgoing)
continue;
if (!atomic_inc_not_zero(&tmp_neigh_ifinfo->refcount))
if (!kref_get_unless_zero(&tmp_neigh_ifinfo->refcount))
continue;
neigh_ifinfo = tmp_neigh_ifinfo;
@@ -408,10 +437,10 @@ batadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh,
/**
* batadv_neigh_ifinfo_new - search and possibly create an neigh_ifinfo object
* @neigh_node: the neigh node to be queried
* @neigh: the neigh node to be queried
* @if_outgoing: the interface for which the ifinfo should be acquired
*
* Returns NULL in case of failure or the neigh_ifinfo object for the
* Return: NULL in case of failure or the neigh_ifinfo object for the
* if_outgoing interface otherwise. The object is created and added to the list
* if it does not exist.
*
@@ -433,14 +462,15 @@ batadv_neigh_ifinfo_new(struct batadv_neigh_node *neigh,
if (!neigh_ifinfo)
goto out;
if (if_outgoing && !atomic_inc_not_zero(&if_outgoing->refcount)) {
if (if_outgoing && !kref_get_unless_zero(&if_outgoing->refcount)) {
kfree(neigh_ifinfo);
neigh_ifinfo = NULL;
goto out;
}
INIT_HLIST_NODE(&neigh_ifinfo->list);
atomic_set(&neigh_ifinfo->refcount, 2);
kref_init(&neigh_ifinfo->refcount);
kref_get(&neigh_ifinfo->refcount);
neigh_ifinfo->if_outgoing = if_outgoing;
hlist_add_head_rcu(&neigh_ifinfo->list, &neigh->ifinfo_list);
@@ -459,7 +489,8 @@ out:
*
* Looks for and possibly returns a neighbour belonging to this originator list
* which is connected through the provided hard interface.
* Returns NULL if the neighbour is not found.
*
* Return: neighbor when found. Othwerwise NULL
*/
static struct batadv_neigh_node *
batadv_neigh_node_get(const struct batadv_orig_node *orig_node,
@@ -476,7 +507,7 @@ batadv_neigh_node_get(const struct batadv_orig_node *orig_node,
if (tmp_neigh_node->if_incoming != hard_iface)
continue;
if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
if (!kref_get_unless_zero(&tmp_neigh_node->refcount))
continue;
res = tmp_neigh_node;
@@ -492,7 +523,7 @@ batadv_neigh_node_get(const struct batadv_orig_node *orig_node,
* @hard_iface: the interface this neighbour is connected to
* @neigh_addr: the interface address of the neighbour to retrieve
*
* Returns the hardif neighbour node if found or created or NULL otherwise.
* Return: the hardif neighbour node if found or created or NULL otherwise.
*/
static struct batadv_hardif_neigh_node *
batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface,
@@ -508,12 +539,12 @@ batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface,
if (hardif_neigh)
goto out;
if (!atomic_inc_not_zero(&hard_iface->refcount))
if (!kref_get_unless_zero(&hard_iface->refcount))
goto out;
hardif_neigh = kzalloc(sizeof(*hardif_neigh), GFP_ATOMIC);
if (!hardif_neigh) {
batadv_hardif_free_ref(hard_iface);
batadv_hardif_put(hard_iface);
goto out;
}
@@ -522,7 +553,7 @@ batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface,
hardif_neigh->if_incoming = hard_iface;
hardif_neigh->last_seen = jiffies;
atomic_set(&hardif_neigh->refcount, 1);
kref_init(&hardif_neigh->refcount);
if (bat_priv->bat_algo_ops->bat_hardif_neigh_init)
bat_priv->bat_algo_ops->bat_hardif_neigh_init(hardif_neigh);
@@ -540,7 +571,7 @@ out:
* @hard_iface: the interface this neighbour is connected to
* @neigh_addr: the interface address of the neighbour to retrieve
*
* Returns the hardif neighbour node if found or created or NULL otherwise.
* Return: the hardif neighbour node if found or created or NULL otherwise.
*/
static struct batadv_hardif_neigh_node *
batadv_hardif_neigh_get_or_create(struct batadv_hard_iface *hard_iface,
@@ -562,7 +593,8 @@ batadv_hardif_neigh_get_or_create(struct batadv_hard_iface *hard_iface,
* @neigh_addr: the address of the neighbour
*
* Looks for and possibly returns a neighbour belonging to this hard interface.
* Returns NULL if the neighbour is not found.
*
* Return: neighbor when found. Othwerwise NULL
*/
struct batadv_hardif_neigh_node *
batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,
@@ -576,7 +608,7 @@ batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,
if (!batadv_compare_eth(tmp_hardif_neigh->addr, neigh_addr))
continue;
if (!atomic_inc_not_zero(&tmp_hardif_neigh->refcount))
if (!kref_get_unless_zero(&tmp_hardif_neigh->refcount))
continue;
hardif_neigh = tmp_hardif_neigh;
@@ -594,7 +626,8 @@ batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,
* @neigh_addr: the mac address of the neighbour interface
*
* Allocates a new neigh_node object and initialises all the generic fields.
* Returns the new object or NULL on failure.
*
* Return: neighbor when found. Othwerwise NULL
*/
struct batadv_neigh_node *
batadv_neigh_node_new(struct batadv_orig_node *orig_node,
@@ -617,7 +650,7 @@ batadv_neigh_node_new(struct batadv_orig_node *orig_node,
if (!neigh_node)
goto out;
if (!atomic_inc_not_zero(&hard_iface->refcount)) {
if (!kref_get_unless_zero(&hard_iface->refcount)) {
kfree(neigh_node);
neigh_node = NULL;
goto out;
@@ -632,14 +665,15 @@ batadv_neigh_node_new(struct batadv_orig_node *orig_node,
neigh_node->orig_node = orig_node;
/* extra reference for return */
atomic_set(&neigh_node->refcount, 2);
kref_init(&neigh_node->refcount);
kref_get(&neigh_node->refcount);
spin_lock_bh(&orig_node->neigh_list_lock);
hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
spin_unlock_bh(&orig_node->neigh_list_lock);
/* increment unique neighbor refcount */
atomic_inc(&hardif_neigh->refcount);
kref_get(&hardif_neigh->refcount);
batadv_dbg(BATADV_DBG_BATMAN, orig_node->bat_priv,
"Creating new neighbor %pM for orig_node %pM on interface %s\n",
@@ -647,7 +681,7 @@ batadv_neigh_node_new(struct batadv_orig_node *orig_node,
out:
if (hardif_neigh)
batadv_hardif_neigh_free_ref(hardif_neigh);
batadv_hardif_neigh_put(hardif_neigh);
return neigh_node;
}
@@ -656,7 +690,7 @@ out:
* @seq: neighbour table seq_file struct
* @offset: not used
*
* Always returns 0.
* Return: always 0
*/
int batadv_hardif_neigh_seq_print_text(struct seq_file *seq, void *offset)
{
@@ -673,7 +707,7 @@ int batadv_hardif_neigh_seq_print_text(struct seq_file *seq, void *offset)
primary_if->net_dev->dev_addr, net_dev->name,
bat_priv->bat_algo_ops->name);
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
if (!bat_priv->bat_algo_ops->bat_neigh_print) {
seq_puts(seq,
@@ -688,32 +722,34 @@ int batadv_hardif_neigh_seq_print_text(struct seq_file *seq, void *offset)
/**
* batadv_orig_ifinfo_release - release orig_ifinfo from lists and queue for
* free after rcu grace period
* @orig_ifinfo: the orig_ifinfo object to release
* @ref: kref pointer of the orig_ifinfo
*/
static void batadv_orig_ifinfo_release(struct batadv_orig_ifinfo *orig_ifinfo)
static void batadv_orig_ifinfo_release(struct kref *ref)
{
struct batadv_orig_ifinfo *orig_ifinfo;
struct batadv_neigh_node *router;
orig_ifinfo = container_of(ref, struct batadv_orig_ifinfo, refcount);
if (orig_ifinfo->if_outgoing != BATADV_IF_DEFAULT)
batadv_hardif_free_ref(orig_ifinfo->if_outgoing);
batadv_hardif_put(orig_ifinfo->if_outgoing);
/* this is the last reference to this object */
router = rcu_dereference_protected(orig_ifinfo->router, true);
if (router)
batadv_neigh_node_free_ref(router);
batadv_neigh_node_put(router);
kfree_rcu(orig_ifinfo, rcu);
}
/**
* batadv_orig_ifinfo_free_ref - decrement the refcounter and possibly release
* batadv_orig_ifinfo_put - decrement the refcounter and possibly release
* the orig_ifinfo
* @orig_ifinfo: the orig_ifinfo object to release
*/
void batadv_orig_ifinfo_free_ref(struct batadv_orig_ifinfo *orig_ifinfo)
void batadv_orig_ifinfo_put(struct batadv_orig_ifinfo *orig_ifinfo)
{
if (atomic_dec_and_test(&orig_ifinfo->refcount))
batadv_orig_ifinfo_release(orig_ifinfo);
kref_put(&orig_ifinfo->refcount, batadv_orig_ifinfo_release);
}
/**
@@ -740,27 +776,30 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu)
/**
* batadv_orig_node_release - release orig_node from lists and queue for
* free after rcu grace period
* @orig_node: the orig node to free
* @ref: kref pointer of the orig_node
*/
static void batadv_orig_node_release(struct batadv_orig_node *orig_node)
static void batadv_orig_node_release(struct kref *ref)
{
struct hlist_node *node_tmp;
struct batadv_neigh_node *neigh_node;
struct batadv_orig_node *orig_node;
struct batadv_orig_ifinfo *orig_ifinfo;
orig_node = container_of(ref, struct batadv_orig_node, refcount);
spin_lock_bh(&orig_node->neigh_list_lock);
/* for all neighbors towards this originator ... */
hlist_for_each_entry_safe(neigh_node, node_tmp,
&orig_node->neigh_list, list) {
hlist_del_rcu(&neigh_node->list);
batadv_neigh_node_free_ref(neigh_node);
batadv_neigh_node_put(neigh_node);
}
hlist_for_each_entry_safe(orig_ifinfo, node_tmp,
&orig_node->ifinfo_list, list) {
hlist_del_rcu(&orig_ifinfo->list);
batadv_orig_ifinfo_free_ref(orig_ifinfo);
batadv_orig_ifinfo_put(orig_ifinfo);
}
spin_unlock_bh(&orig_node->neigh_list_lock);
@@ -771,14 +810,13 @@ static void batadv_orig_node_release(struct batadv_orig_node *orig_node)
}
/**
* batadv_orig_node_free_ref - decrement the orig node refcounter and possibly
* batadv_orig_node_put - decrement the orig node refcounter and possibly
* release it
* @orig_node: the orig node to free
*/
void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node)
void batadv_orig_node_put(struct batadv_orig_node *orig_node)
{
if (atomic_dec_and_test(&orig_node->refcount))
batadv_orig_node_release(orig_node);
kref_put(&orig_node->refcount, batadv_orig_node_release);
}
void batadv_originator_free(struct batadv_priv *bat_priv)
@@ -805,7 +843,7 @@ void batadv_originator_free(struct batadv_priv *bat_priv)
hlist_for_each_entry_safe(orig_node, node_tmp,
head, hash_entry) {
hlist_del_rcu(&orig_node->hash_entry);
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
}
spin_unlock_bh(list_lock);
}
@@ -820,7 +858,8 @@ void batadv_originator_free(struct batadv_priv *bat_priv)
*
* Creates a new originator object and initialise all the generic fields.
* The new object is not added to the originator list.
* Returns the newly created object or NULL on failure.
*
* Return: the newly created object or NULL on failure.
*/
struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
const u8 *addr)
@@ -849,7 +888,8 @@ struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
batadv_nc_init_orig(orig_node);
/* extra reference for return */
atomic_set(&orig_node->refcount, 2);
kref_init(&orig_node->refcount);
kref_get(&orig_node->refcount);
orig_node->bat_priv = bat_priv;
ether_addr_copy(orig_node->orig, addr);
@@ -877,7 +917,7 @@ struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
* Immediately release vlan since it is not needed anymore in this
* context
*/
batadv_orig_node_vlan_free_ref(vlan);
batadv_orig_node_vlan_put(vlan);
for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) {
INIT_HLIST_HEAD(&orig_node->fragments[i].head);
@@ -926,7 +966,7 @@ batadv_purge_neigh_ifinfo(struct batadv_priv *bat_priv,
neigh->addr, if_outgoing->net_dev->name);
hlist_del_rcu(&neigh_ifinfo->list);
batadv_neigh_ifinfo_free_ref(neigh_ifinfo);
batadv_neigh_ifinfo_put(neigh_ifinfo);
}
spin_unlock_bh(&neigh->ifinfo_lock);
@@ -937,7 +977,7 @@ batadv_purge_neigh_ifinfo(struct batadv_priv *bat_priv,
* @bat_priv: the bat priv with all the soft interface information
* @orig_node: orig node which is to be checked
*
* Returns true if any ifinfo entry was purged, false otherwise.
* Return: true if any ifinfo entry was purged, false otherwise.
*/
static bool
batadv_purge_orig_ifinfo(struct batadv_priv *bat_priv,
@@ -972,10 +1012,10 @@ batadv_purge_orig_ifinfo(struct batadv_priv *bat_priv,
ifinfo_purged = true;
hlist_del_rcu(&orig_ifinfo->list);
batadv_orig_ifinfo_free_ref(orig_ifinfo);
batadv_orig_ifinfo_put(orig_ifinfo);
if (orig_node->last_bonding_candidate == orig_ifinfo) {
orig_node->last_bonding_candidate = NULL;
batadv_orig_ifinfo_free_ref(orig_ifinfo);
batadv_orig_ifinfo_put(orig_ifinfo);
}
}
@@ -989,7 +1029,7 @@ batadv_purge_orig_ifinfo(struct batadv_priv *bat_priv,
* @bat_priv: the bat priv with all the soft interface information
* @orig_node: orig node which is to be checked
*
* Returns true if any neighbor was purged, false otherwise
* Return: true if any neighbor was purged, false otherwise
*/
static bool
batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
@@ -1029,7 +1069,7 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
neigh_purged = true;
hlist_del_rcu(&neigh_node->list);
batadv_neigh_node_free_ref(neigh_node);
batadv_neigh_node_put(neigh_node);
} else {
/* only necessary if not the whole neighbor is to be
* deleted, but some interface has been removed.
@@ -1048,7 +1088,7 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
* @orig_node: orig node which is to be checked
* @if_outgoing: the interface for which the metric should be compared
*
* Returns the current best neighbor, with refcount increased.
* Return: the current best neighbor, with refcount increased.
*/
static struct batadv_neigh_node *
batadv_find_best_neighbor(struct batadv_priv *bat_priv,
@@ -1064,11 +1104,11 @@ batadv_find_best_neighbor(struct batadv_priv *bat_priv,
best, if_outgoing) <= 0))
continue;
if (!atomic_inc_not_zero(&neigh->refcount))
if (!kref_get_unless_zero(&neigh->refcount))
continue;
if (best)
batadv_neigh_node_free_ref(best);
batadv_neigh_node_put(best);
best = neigh;
}
@@ -1085,7 +1125,7 @@ batadv_find_best_neighbor(struct batadv_priv *bat_priv,
* This function checks if the orig_node or substructures of it have become
* obsolete, and purges this information if that's the case.
*
* Returns true if the orig_node is to be removed, false otherwise.
* Return: true if the orig_node is to be removed, false otherwise.
*/
static bool batadv_purge_orig_node(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node)
@@ -1114,7 +1154,7 @@ static bool batadv_purge_orig_node(struct batadv_priv *bat_priv,
batadv_update_route(bat_priv, orig_node, BATADV_IF_DEFAULT,
best_neigh_node);
if (best_neigh_node)
batadv_neigh_node_free_ref(best_neigh_node);
batadv_neigh_node_put(best_neigh_node);
/* ... then for all other interfaces. */
rcu_read_lock();
@@ -1131,7 +1171,7 @@ static bool batadv_purge_orig_node(struct batadv_priv *bat_priv,
batadv_update_route(bat_priv, orig_node, hard_iface,
best_neigh_node);
if (best_neigh_node)
batadv_neigh_node_free_ref(best_neigh_node);
batadv_neigh_node_put(best_neigh_node);
}
rcu_read_unlock();
@@ -1164,7 +1204,7 @@ static void _batadv_purge_orig(struct batadv_priv *bat_priv)
batadv_tt_global_del_orig(orig_node->bat_priv,
orig_node, -1,
"originator timed out");
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
continue;
}
@@ -1210,7 +1250,7 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset)
primary_if->net_dev->dev_addr, net_dev->name,
bat_priv->bat_algo_ops->name);
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
if (!bat_priv->bat_algo_ops->bat_orig_print) {
seq_puts(seq,
@@ -1230,7 +1270,7 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset)
* @seq: debugfs table seq_file struct
* @offset: not used
*
* Returns 0
* Return: 0
*/
int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset)
{
@@ -1266,7 +1306,7 @@ int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset)
out:
if (hard_iface)
batadv_hardif_free_ref(hard_iface);
batadv_hardif_put(hard_iface);
return 0;
}

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -20,10 +20,10 @@
#include "main.h"
#include <linux/atomic.h>
#include <linux/compiler.h>
#include <linux/if_ether.h>
#include <linux/jhash.h>
#include <linux/kref.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/stddef.h>
@@ -37,19 +37,19 @@ int batadv_compare_orig(const struct hlist_node *node, const void *data2);
int batadv_originator_init(struct batadv_priv *bat_priv);
void batadv_originator_free(struct batadv_priv *bat_priv);
void batadv_purge_orig_ref(struct batadv_priv *bat_priv);
void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node);
void batadv_orig_node_put(struct batadv_orig_node *orig_node);
struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
const u8 *addr);
struct batadv_hardif_neigh_node *
batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,
const u8 *neigh_addr);
void
batadv_hardif_neigh_free_ref(struct batadv_hardif_neigh_node *hardif_neigh);
batadv_hardif_neigh_put(struct batadv_hardif_neigh_node *hardif_neigh);
struct batadv_neigh_node *
batadv_neigh_node_new(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *hard_iface,
const u8 *neigh_addr);
void batadv_neigh_node_free_ref(struct batadv_neigh_node *neigh_node);
void batadv_neigh_node_put(struct batadv_neigh_node *neigh_node);
struct batadv_neigh_node *
batadv_orig_router_get(struct batadv_orig_node *orig_node,
const struct batadv_hard_iface *if_outgoing);
@@ -59,7 +59,7 @@ batadv_neigh_ifinfo_new(struct batadv_neigh_node *neigh,
struct batadv_neigh_ifinfo *
batadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh,
struct batadv_hard_iface *if_outgoing);
void batadv_neigh_ifinfo_free_ref(struct batadv_neigh_ifinfo *neigh_ifinfo);
void batadv_neigh_ifinfo_put(struct batadv_neigh_ifinfo *neigh_ifinfo);
int batadv_hardif_neigh_seq_print_text(struct seq_file *seq, void *offset);
@@ -69,7 +69,7 @@ batadv_orig_ifinfo_get(struct batadv_orig_node *orig_node,
struct batadv_orig_ifinfo *
batadv_orig_ifinfo_new(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *if_outgoing);
void batadv_orig_ifinfo_free_ref(struct batadv_orig_ifinfo *orig_ifinfo);
void batadv_orig_ifinfo_put(struct batadv_orig_ifinfo *orig_ifinfo);
int batadv_orig_seq_print_text(struct seq_file *seq, void *offset);
int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset);
@@ -83,7 +83,7 @@ batadv_orig_node_vlan_new(struct batadv_orig_node *orig_node,
struct batadv_orig_node_vlan *
batadv_orig_node_vlan_get(struct batadv_orig_node *orig_node,
unsigned short vid);
void batadv_orig_node_vlan_free_ref(struct batadv_orig_node_vlan *orig_vlan);
void batadv_orig_node_vlan_put(struct batadv_orig_node_vlan *orig_vlan);
/* hashfunction to choose an entry in a hash table of given size
* hash algorithm from http://en.wikipedia.org/wiki/Hash_table
@@ -115,7 +115,7 @@ batadv_orig_hash_find(struct batadv_priv *bat_priv, const void *data)
if (!batadv_compare_eth(orig_node, data))
continue;
if (!atomic_inc_not_zero(&orig_node->refcount))
if (!kref_get_unless_zero(&orig_node->refcount))
continue;
orig_node_tmp = orig_node;

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -26,6 +26,8 @@
* @BATADV_IV_OGM: originator messages for B.A.T.M.A.N. IV
* @BATADV_BCAST: broadcast packets carrying broadcast payload
* @BATADV_CODED: network coded packets
* @BATADV_ELP: echo location packets for B.A.T.M.A.N. V
* @BATADV_OGM2: originator messages for B.A.T.M.A.N. V
*
* @BATADV_UNICAST: unicast packets carrying unicast payload traffic
* @BATADV_UNICAST_FRAG: unicast packets carrying a fragment of the original
@@ -40,6 +42,8 @@ enum batadv_packettype {
BATADV_IV_OGM = 0x00,
BATADV_BCAST = 0x01,
BATADV_CODED = 0x02,
BATADV_ELP = 0x03,
BATADV_OGM2 = 0x04,
/* 0x40 - 0x7f: unicast */
#define BATADV_UNICAST_MIN 0x40
BATADV_UNICAST = 0x40,
@@ -158,7 +162,7 @@ enum batadv_tt_client_flags {
};
/**
* batadv_vlan_flags - flags for the four MSB of any vlan ID field
* enum batadv_vlan_flags - flags for the four MSB of any vlan ID field
* @BATADV_VLAN_HAS_TAG: whether the field contains a valid vlan tag or not
*/
enum batadv_vlan_flags {
@@ -209,6 +213,11 @@ struct batadv_bla_claim_dst {
* @version: batman-adv protocol version, part of the genereal header
* @ttl: time to live for this packet, part of the genereal header
* @flags: contains routing relevant flags - see enum batadv_iv_flags
* @seqno: sequence identification
* @orig: address of the source node
* @prev_sender: address of the previous sender
* @reserved: reserved byte for alignment
* @tq: transmission quality
* @tvlv_len: length of tvlv data following the ogm header
*/
struct batadv_ogm_packet {
@@ -230,7 +239,52 @@ struct batadv_ogm_packet {
#define BATADV_OGM_HLEN sizeof(struct batadv_ogm_packet)
/**
* batadv_icmp_header - common members among all the ICMP packets
* struct batadv_ogm2_packet - ogm2 (routing protocol) packet
* @packet_type: batman-adv packet type, part of the general header
* @version: batman-adv protocol version, part of the general header
* @ttl: time to live for this packet, part of the general header
* @flags: reseved for routing relevant flags - currently always 0
* @seqno: sequence number
* @orig: originator mac address
* @tvlv_len: length of the appended tvlv buffer (in bytes)
* @throughput: the currently flooded path throughput
*/
struct batadv_ogm2_packet {
u8 packet_type;
u8 version;
u8 ttl;
u8 flags;
__be32 seqno;
u8 orig[ETH_ALEN];
__be16 tvlv_len;
__be32 throughput;
/* __packed is not needed as the struct size is divisible by 4,
* and the largest data type in this struct has a size of 4.
*/
};
#define BATADV_OGM2_HLEN sizeof(struct batadv_ogm2_packet)
/**
* struct batadv_elp_packet - elp (neighbor discovery) packet
* @packet_type: batman-adv packet type, part of the general header
* @version: batman-adv protocol version, part of the genereal header
* @orig: originator mac address
* @seqno: sequence number
* @elp_interval: currently used ELP sending interval in ms
*/
struct batadv_elp_packet {
u8 packet_type;
u8 version;
u8 orig[ETH_ALEN];
__be32 seqno;
__be32 elp_interval;
};
#define BATADV_ELP_HLEN sizeof(struct batadv_elp_packet)
/**
* struct batadv_icmp_header - common members among all the ICMP packets
* @packet_type: batman-adv packet type, part of the general header
* @version: batman-adv protocol version, part of the genereal header
* @ttl: time to live for this packet, part of the genereal header
@@ -256,7 +310,7 @@ struct batadv_icmp_header {
};
/**
* batadv_icmp_packet - ICMP packet
* struct batadv_icmp_packet - ICMP packet
* @packet_type: batman-adv packet type, part of the general header
* @version: batman-adv protocol version, part of the genereal header
* @ttl: time to live for this packet, part of the genereal header
@@ -282,7 +336,7 @@ struct batadv_icmp_packet {
#define BATADV_RR_LEN 16
/**
* batadv_icmp_packet_rr - ICMP RouteRecord packet
* struct batadv_icmp_packet_rr - ICMP RouteRecord packet
* @packet_type: batman-adv packet type, part of the general header
* @version: batman-adv protocol version, part of the genereal header
* @ttl: time to live for this packet, part of the genereal header
@@ -345,6 +399,7 @@ struct batadv_unicast_packet {
* @u: common unicast packet header
* @src: address of the source
* @subtype: packet subtype
* @reserved: reserved byte for alignment
*/
struct batadv_unicast_4addr_packet {
struct batadv_unicast_packet u;
@@ -413,7 +468,6 @@ struct batadv_bcast_packet {
* @packet_type: batman-adv packet type, part of the general header
* @version: batman-adv protocol version, part of the genereal header
* @ttl: time to live for this packet, part of the genereal header
* @reserved: Align following fields to 2-byte boundaries
* @first_source: original source of first included packet
* @first_orig_dest: original destinal of first included packet
* @first_crc: checksum of first included packet
@@ -495,7 +549,7 @@ struct batadv_tvlv_gateway_data {
* struct batadv_tvlv_tt_data - tt data propagated through the tt tvlv container
* @flags: translation table flags (see batadv_tt_data_flags)
* @ttvn: translation table version number
* @vlan_num: number of announced VLANs. In the TVLV this struct is followed by
* @num_vlan: number of announced VLANs. In the TVLV this struct is followed by
* one batadv_tvlv_tt_vlan_data object per announced vlan
*/
struct batadv_tvlv_tt_data {

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -25,6 +25,7 @@
#include <linux/etherdevice.h>
#include <linux/if_ether.h>
#include <linux/jiffies.h>
#include <linux/kref.h>
#include <linux/netdevice.h>
#include <linux/printk.h>
#include <linux/rculist.h>
@@ -72,7 +73,7 @@ static void _batadv_update_route(struct batadv_priv *bat_priv,
rcu_read_lock();
curr_router = rcu_dereference(orig_ifinfo->router);
if (curr_router && !atomic_inc_not_zero(&curr_router->refcount))
if (curr_router && !kref_get_unless_zero(&curr_router->refcount))
curr_router = NULL;
rcu_read_unlock();
@@ -97,20 +98,20 @@ static void _batadv_update_route(struct batadv_priv *bat_priv,
}
if (curr_router)
batadv_neigh_node_free_ref(curr_router);
batadv_neigh_node_put(curr_router);
/* increase refcount of new best neighbor */
if (neigh_node && !atomic_inc_not_zero(&neigh_node->refcount))
if (neigh_node && !kref_get_unless_zero(&neigh_node->refcount))
neigh_node = NULL;
spin_lock_bh(&orig_node->neigh_list_lock);
rcu_assign_pointer(orig_ifinfo->router, neigh_node);
spin_unlock_bh(&orig_node->neigh_list_lock);
batadv_orig_ifinfo_free_ref(orig_ifinfo);
batadv_orig_ifinfo_put(orig_ifinfo);
/* decrease refcount of previous best neighbor */
if (curr_router)
batadv_neigh_node_free_ref(curr_router);
batadv_neigh_node_put(curr_router);
}
/**
@@ -137,24 +138,38 @@ void batadv_update_route(struct batadv_priv *bat_priv,
out:
if (router)
batadv_neigh_node_free_ref(router);
batadv_neigh_node_put(router);
}
/* checks whether the host restarted and is in the protection time.
* returns:
* 0 if the packet is to be accepted
/**
* batadv_window_protected - checks whether the host restarted and is in the
* protection time.
* @bat_priv: the bat priv with all the soft interface information
* @seq_num_diff: difference between the current/received sequence number and
* the last sequence number
* @seq_old_max_diff: maximum age of sequence number not considered as restart
* @last_reset: jiffies timestamp of the last reset, will be updated when reset
* is detected
* @protection_started: is set to true if the protection window was started,
* doesn't change otherwise.
*
* Return:
* 0 if the packet is to be accepted.
* 1 if the packet is to be ignored.
*/
int batadv_window_protected(struct batadv_priv *bat_priv, s32 seq_num_diff,
unsigned long *last_reset)
s32 seq_old_max_diff, unsigned long *last_reset,
bool *protection_started)
{
if (seq_num_diff <= -BATADV_TQ_LOCAL_WINDOW_SIZE ||
if (seq_num_diff <= -seq_old_max_diff ||
seq_num_diff >= BATADV_EXPECTED_SEQNO_RANGE) {
if (!batadv_has_timed_out(*last_reset,
BATADV_RESET_PROTECTION_MS))
return 1;
*last_reset = jiffies;
if (protection_started)
*protection_started = true;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"old packet received, start protection\n");
}
@@ -198,7 +213,7 @@ bool batadv_check_management_packet(struct sk_buff *skb,
* @bat_priv: the bat priv with all the soft interface information
* @skb: icmp packet to process
*
* Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
* Return: NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
* otherwise.
*/
static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
@@ -254,9 +269,9 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
}
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
if (orig_node)
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
return ret;
}
@@ -302,9 +317,9 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv,
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
if (orig_node)
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
return ret;
}
@@ -388,7 +403,7 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
out:
if (orig_node)
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
return ret;
}
@@ -398,10 +413,11 @@ out:
* @skb: packet to check
* @hdr_size: size of header to pull
*
* Check for short header and bad addresses in given packet. Returns negative
* value when check fails and 0 otherwise. The negative value depends on the
* reason: -ENODATA for bad header, -EBADR for broadcast destination or source,
* and -EREMOTE for non-local (other host) destination.
* Check for short header and bad addresses in given packet.
*
* Return: negative value when check fails and 0 otherwise. The negative value
* depends on the reason: -ENODATA for bad header, -EBADR for broadcast
* destination or source, and -EREMOTE for non-local (other host) destination.
*/
static int batadv_check_unicast_packet(struct batadv_priv *bat_priv,
struct sk_buff *skb, int hdr_size)
@@ -435,7 +451,7 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv,
* @orig_node: the destination node
* @recv_if: pointer to interface this packet was received on
*
* Returns the router which should be used for this orig_node on
* Return: the router which should be used for this orig_node on
* this interface, or NULL if not available.
*/
struct batadv_neigh_node *
@@ -482,14 +498,14 @@ batadv_find_router(struct batadv_priv *bat_priv,
hlist_for_each_entry_rcu(cand, &orig_node->ifinfo_list, list) {
/* acquire some structures and references ... */
if (!atomic_inc_not_zero(&cand->refcount))
if (!kref_get_unless_zero(&cand->refcount))
continue;
cand_router = rcu_dereference(cand->router);
if (!cand_router)
goto next;
if (!atomic_inc_not_zero(&cand_router->refcount)) {
if (!kref_get_unless_zero(&cand_router->refcount)) {
cand_router = NULL;
goto next;
}
@@ -508,8 +524,8 @@ batadv_find_router(struct batadv_priv *bat_priv,
/* mark the first possible candidate */
if (!first_candidate) {
atomic_inc(&cand_router->refcount);
atomic_inc(&cand->refcount);
kref_get(&cand_router->refcount);
kref_get(&cand->refcount);
first_candidate = cand;
first_candidate_router = cand_router;
}
@@ -529,16 +545,16 @@ batadv_find_router(struct batadv_priv *bat_priv,
next:
/* free references */
if (cand_router) {
batadv_neigh_node_free_ref(cand_router);
batadv_neigh_node_put(cand_router);
cand_router = NULL;
}
batadv_orig_ifinfo_free_ref(cand);
batadv_orig_ifinfo_put(cand);
}
rcu_read_unlock();
/* last_bonding_candidate is reset below, remove the old reference. */
if (orig_node->last_bonding_candidate)
batadv_orig_ifinfo_free_ref(orig_node->last_bonding_candidate);
batadv_orig_ifinfo_put(orig_node->last_bonding_candidate);
/* After finding candidates, handle the three cases:
* 1) there is a next candidate, use that
@@ -546,17 +562,17 @@ next:
* 3) there is no candidate at all, return the default router
*/
if (next_candidate) {
batadv_neigh_node_free_ref(router);
batadv_neigh_node_put(router);
/* remove references to first candidate, we don't need it. */
if (first_candidate) {
batadv_neigh_node_free_ref(first_candidate_router);
batadv_orig_ifinfo_free_ref(first_candidate);
batadv_neigh_node_put(first_candidate_router);
batadv_orig_ifinfo_put(first_candidate);
}
router = next_candidate_router;
orig_node->last_bonding_candidate = next_candidate;
} else if (first_candidate) {
batadv_neigh_node_free_ref(router);
batadv_neigh_node_put(router);
/* refcounting has already been done in the loop above. */
router = first_candidate_router;
@@ -633,7 +649,7 @@ static int batadv_route_unicast_packet(struct sk_buff *skb,
out:
if (orig_node)
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
return ret;
}
@@ -648,7 +664,7 @@ out:
* the new corresponding information (originator address where the destination
* client currently is and its known TTVN)
*
* Returns true if the packet header has been updated, false otherwise
* Return: true if the packet header has been updated, false otherwise
*/
static bool
batadv_reroute_unicast_packet(struct batadv_priv *bat_priv,
@@ -686,9 +702,9 @@ batadv_reroute_unicast_packet(struct batadv_priv *bat_priv,
ret = true;
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
if (orig_node)
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
return ret;
}
@@ -752,7 +768,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
return 0;
curr_ttvn = (u8)atomic_read(&orig_node->last_ttvn);
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
}
/* check if the TTVN contained in the packet is fresher than what the
@@ -792,7 +808,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
ether_addr_copy(unicast_packet->dest, primary_if->net_dev->dev_addr);
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
unicast_packet->ttvn = curr_ttvn;
@@ -805,7 +821,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
* @skb: unicast tvlv packet to process
* @recv_if: pointer to interface this packet was received on
*
* Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
* Return: NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
* otherwise.
*/
int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb,
@@ -892,7 +908,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,
rx_success:
if (orig_node)
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
return NET_RX_SUCCESS;
}
@@ -904,9 +920,8 @@ rx_success:
* batadv_recv_unicast_tvlv - receive and process unicast tvlv packets
* @skb: unicast tvlv packet to process
* @recv_if: pointer to interface this packet was received on
* @dst_addr: the payload destination
*
* Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
* Return: NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
* otherwise.
*/
int batadv_recv_unicast_tvlv(struct sk_buff *skb,
@@ -960,7 +975,7 @@ int batadv_recv_unicast_tvlv(struct sk_buff *skb,
* the assembled packet will exceed our MTU; 2) Buffer fragment, if we till
* lack further fragments; 3) Merge fragments, if we have all needed parts.
*
* Return NET_RX_DROP if the skb is not consumed, NET_RX_SUCCESS otherwise.
* Return: NET_RX_DROP if the skb is not consumed, NET_RX_SUCCESS otherwise.
*/
int batadv_recv_frag_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
@@ -1004,7 +1019,7 @@ int batadv_recv_frag_packet(struct sk_buff *skb,
out:
if (orig_node_src)
batadv_orig_node_free_ref(orig_node_src);
batadv_orig_node_put(orig_node_src);
return ret;
}
@@ -1065,7 +1080,8 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
/* check whether the packet is old and the host just restarted. */
if (batadv_window_protected(bat_priv, seq_diff,
&orig_node->bcast_seqno_reset))
BATADV_BCAST_MAX_AGE,
&orig_node->bcast_seqno_reset, NULL))
goto spin_unlock;
/* mark broadcast in flood history, update window position
@@ -1108,6 +1124,6 @@ spin_unlock:
spin_unlock_bh(&orig_node->bcast_seqno_lock);
out:
if (orig_node)
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
return ret;
}

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -52,6 +52,7 @@ batadv_find_router(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
struct batadv_hard_iface *recv_if);
int batadv_window_protected(struct batadv_priv *bat_priv, s32 seq_num_diff,
unsigned long *last_reset);
s32 seq_old_max_diff, unsigned long *last_reset,
bool *protection_started);
#endif /* _NET_BATMAN_ADV_ROUTING_H_ */

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -49,16 +49,30 @@
static void batadv_send_outstanding_bcast_packet(struct work_struct *work);
/* send out an already prepared packet to the given address via the
* specified batman interface
/**
* batadv_send_skb_packet - send an already prepared packet
* @skb: the packet to send
* @hard_iface: the interface to use to send the broadcast packet
* @dst_addr: the payload destination
*
* Send out an already prepared packet to the given neighbor or broadcast it
* using the specified interface. Either hard_iface or neigh_node must be not
* NULL.
* If neigh_node is NULL, then the packet is broadcasted using hard_iface,
* otherwise it is sent as unicast to the given neighbor.
*
* Return: NET_TX_DROP in case of error or the result of dev_queue_xmit(skb)
* otherwise
*/
int batadv_send_skb_packet(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface,
const u8 *dst_addr)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
struct batadv_priv *bat_priv;
struct ethhdr *ethhdr;
bat_priv = netdev_priv(hard_iface->soft_iface);
if (hard_iface->if_status != BATADV_IF_ACTIVE)
goto send_skb_err;
@@ -100,6 +114,35 @@ send_skb_err:
return NET_XMIT_DROP;
}
int batadv_send_broadcast_skb(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
return batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr);
}
int batadv_send_unicast_skb(struct sk_buff *skb,
struct batadv_neigh_node *neigh)
{
#ifdef CONFIG_BATMAN_ADV_BATMAN_V
struct batadv_hardif_neigh_node *hardif_neigh;
#endif
int ret;
ret = batadv_send_skb_packet(skb, neigh->if_incoming, neigh->addr);
#ifdef CONFIG_BATMAN_ADV_BATMAN_V
hardif_neigh = batadv_hardif_neigh_get(neigh->if_incoming, neigh->addr);
if ((hardif_neigh) && (ret != NET_XMIT_DROP))
hardif_neigh->bat_v.last_unicast_tx = jiffies;
if (hardif_neigh)
batadv_hardif_neigh_put(hardif_neigh);
#endif
return ret;
}
/**
* batadv_send_skb_to_orig - Lookup next-hop and transmit skb.
* @skb: Packet to be transmitted.
@@ -111,7 +154,7 @@ send_skb_err:
* host, NULL can be passed as recv_if and no interface alternating is
* attempted.
*
* Returns NET_XMIT_SUCCESS on success, NET_XMIT_DROP on failure, or
* Return: NET_XMIT_SUCCESS on success, NET_XMIT_DROP on failure, or
* NET_XMIT_POLICED if the skb is buffered for later transmit.
*/
int batadv_send_skb_to_orig(struct sk_buff *skb,
@@ -146,14 +189,13 @@ int batadv_send_skb_to_orig(struct sk_buff *skb,
if (recv_if && batadv_nc_skb_forward(skb, neigh_node)) {
ret = NET_XMIT_POLICED;
} else {
batadv_send_skb_packet(skb, neigh_node->if_incoming,
neigh_node->addr);
batadv_send_unicast_skb(skb, neigh_node);
ret = NET_XMIT_SUCCESS;
}
out:
if (neigh_node)
batadv_neigh_node_free_ref(neigh_node);
batadv_neigh_node_put(neigh_node);
return ret;
}
@@ -165,7 +207,7 @@ out:
* @hdr_size: amount of bytes to push at the beginning of the skb
* @orig_node: the destination node
*
* Returns false if the buffer extension was not possible or true otherwise.
* Return: false if the buffer extension was not possible or true otherwise.
*/
static bool
batadv_send_skb_push_fill_unicast(struct sk_buff *skb, int hdr_size,
@@ -196,7 +238,7 @@ batadv_send_skb_push_fill_unicast(struct sk_buff *skb, int hdr_size,
* @skb: the skb containing the payload to encapsulate
* @orig_node: the destination node
*
* Returns false if the payload could not be encapsulated or true otherwise.
* Return: false if the payload could not be encapsulated or true otherwise.
*/
static bool batadv_send_skb_prepare_unicast(struct sk_buff *skb,
struct batadv_orig_node *orig_node)
@@ -211,10 +253,10 @@ static bool batadv_send_skb_prepare_unicast(struct sk_buff *skb,
* unicast 4addr header
* @bat_priv: the bat priv with all the soft interface information
* @skb: the skb containing the payload to encapsulate
* @orig_node: the destination node
* @orig: the destination node
* @packet_subtype: the unicast 4addr packet subtype to use
*
* Returns false if the payload could not be encapsulated or true otherwise.
* Return: false if the payload could not be encapsulated or true otherwise.
*/
bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv,
struct sk_buff *skb,
@@ -246,7 +288,7 @@ bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv,
ret = true;
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
return ret;
}
@@ -265,7 +307,7 @@ out:
* as packet_type. Then send this frame to the given orig_node and release a
* reference to this orig_node.
*
* Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
* Return: NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
*/
int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
struct sk_buff *skb, int packet_type,
@@ -317,7 +359,7 @@ int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
out:
if (orig_node)
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
if (ret == NET_XMIT_DROP)
kfree_skb(skb);
return ret;
@@ -339,7 +381,7 @@ out:
* BATADV_UNICAST_4ADDR was supplied as packet_type. Then send this frame
* to the according destination node.
*
* Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
* Return: NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
*/
int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv,
struct sk_buff *skb, int packet_type,
@@ -373,7 +415,7 @@ int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv,
* Look up the currently selected gateway. Wrap the given skb into a batman-adv
* unicast header and send this frame to this gateway node.
*
* Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
* Return: NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
*/
int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
unsigned short vid)
@@ -409,9 +451,9 @@ static void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet)
{
kfree_skb(forw_packet->skb);
if (forw_packet->if_incoming)
batadv_hardif_free_ref(forw_packet->if_incoming);
batadv_hardif_put(forw_packet->if_incoming);
if (forw_packet->if_outgoing)
batadv_hardif_free_ref(forw_packet->if_outgoing);
batadv_hardif_put(forw_packet->if_outgoing);
kfree(forw_packet);
}
@@ -430,14 +472,19 @@ _batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
send_time);
}
/* add a broadcast packet to the queue and setup timers. broadcast packets
* are sent multiple times to increase probability for being received.
/**
* batadv_add_bcast_packet_to_list - queue broadcast packet for multiple sends
* @bat_priv: the bat priv with all the soft interface information
* @skb: broadcast packet to add
* @delay: number of jiffies to wait before sending
*
* This function returns NETDEV_TX_OK on success and NETDEV_TX_BUSY on
* errors.
* add a broadcast packet to the queue and setup timers. broadcast packets
* are sent multiple times to increase probability for being received.
*
* The skb is not consumed, so the caller should make sure that the
* skb is freed.
*
* Return: NETDEV_TX_OK on success and NETDEV_TX_BUSY on errors.
*/
int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
const struct sk_buff *skb,
@@ -492,7 +539,7 @@ out_and_inc:
atomic_inc(&bat_priv->bcast_queue_left);
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
return NETDEV_TX_BUSY;
}
@@ -533,8 +580,7 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
/* send a copy of the saved skb */
skb1 = skb_clone(forw_packet->skb, GFP_ATOMIC);
if (skb1)
batadv_send_skb_packet(skb1, hard_iface,
batadv_broadcast_addr);
batadv_send_broadcast_skb(skb1, hard_iface);
}
rcu_read_unlock();

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -28,12 +28,16 @@
struct sk_buff;
struct work_struct;
int batadv_send_skb_packet(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface,
const u8 *dst_addr);
int batadv_send_skb_to_orig(struct sk_buff *skb,
struct batadv_orig_node *orig_node,
struct batadv_hard_iface *recv_if);
int batadv_send_skb_packet(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface,
const u8 *dst_addr);
int batadv_send_broadcast_skb(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface);
int batadv_send_unicast_skb(struct sk_buff *skb,
struct batadv_neigh_node *neigh_node);
void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface);
int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
const struct sk_buff *skb,
@@ -69,7 +73,7 @@ int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
* header via the translation table. Wrap the given skb into a batman-adv
* unicast header. Then send this frame to the according destination node.
*
* Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
* Return: NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
*/
static inline int batadv_send_skb_via_tt(struct batadv_priv *bat_priv,
struct sk_buff *skb, u8 *dst_hint,
@@ -92,7 +96,7 @@ static inline int batadv_send_skb_via_tt(struct batadv_priv *bat_priv,
* unicast-4addr header. Then send this frame to the according destination
* node.
*
* Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
* Return: NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
*/
static inline int batadv_send_skb_via_tt_4addr(struct batadv_priv *bat_priv,
struct sk_buff *skb,

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -30,6 +30,7 @@
#include <linux/if_vlan.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/netdevice.h>
@@ -376,7 +377,7 @@ dropped_freed:
batadv_inc_counter(bat_priv, BATADV_CNT_TX_DROPPED);
end:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
return NETDEV_TX_OK;
}
@@ -478,22 +479,34 @@ out:
}
/**
* batadv_softif_vlan_free_ref - decrease the vlan object refcounter and
* possibly free it
* @softif_vlan: the vlan object to release
* batadv_softif_vlan_release - release vlan from lists and queue for free after
* rcu grace period
* @ref: kref pointer of the vlan object
*/
void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *vlan)
static void batadv_softif_vlan_release(struct kref *ref)
{
struct batadv_softif_vlan *vlan;
vlan = container_of(ref, struct batadv_softif_vlan, refcount);
spin_lock_bh(&vlan->bat_priv->softif_vlan_list_lock);
hlist_del_rcu(&vlan->list);
spin_unlock_bh(&vlan->bat_priv->softif_vlan_list_lock);
kfree_rcu(vlan, rcu);
}
/**
* batadv_softif_vlan_put - decrease the vlan object refcounter and
* possibly release it
* @vlan: the vlan object to release
*/
void batadv_softif_vlan_put(struct batadv_softif_vlan *vlan)
{
if (!vlan)
return;
if (atomic_dec_and_test(&vlan->refcount)) {
spin_lock_bh(&vlan->bat_priv->softif_vlan_list_lock);
hlist_del_rcu(&vlan->list);
spin_unlock_bh(&vlan->bat_priv->softif_vlan_list_lock);
kfree_rcu(vlan, rcu);
}
kref_put(&vlan->refcount, batadv_softif_vlan_release);
}
/**
@@ -501,7 +514,7 @@ void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *vlan)
* @bat_priv: the bat priv with all the soft interface information
* @vid: the identifier of the vlan object to retrieve
*
* Returns the private data of the vlan matching the vid passed as argument or
* Return: the private data of the vlan matching the vid passed as argument or
* NULL otherwise. The refcounter of the returned object is incremented by 1.
*/
struct batadv_softif_vlan *batadv_softif_vlan_get(struct batadv_priv *bat_priv,
@@ -514,7 +527,7 @@ struct batadv_softif_vlan *batadv_softif_vlan_get(struct batadv_priv *bat_priv,
if (vlan_tmp->vid != vid)
continue;
if (!atomic_inc_not_zero(&vlan_tmp->refcount))
if (!kref_get_unless_zero(&vlan_tmp->refcount))
continue;
vlan = vlan_tmp;
@@ -530,7 +543,7 @@ struct batadv_softif_vlan *batadv_softif_vlan_get(struct batadv_priv *bat_priv,
* @bat_priv: the bat priv with all the soft interface information
* @vid: the VLAN identifier
*
* Returns 0 on success, a negative error otherwise.
* Return: 0 on success, a negative error otherwise.
*/
int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
{
@@ -539,7 +552,7 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
vlan = batadv_softif_vlan_get(bat_priv, vid);
if (vlan) {
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_put(vlan);
return -EEXIST;
}
@@ -549,7 +562,7 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
vlan->bat_priv = bat_priv;
vlan->vid = vid;
atomic_set(&vlan->refcount, 1);
kref_init(&vlan->refcount);
atomic_set(&vlan->ap_isolation, 0);
@@ -588,18 +601,19 @@ static void batadv_softif_destroy_vlan(struct batadv_priv *bat_priv,
vlan->vid, "vlan interface destroyed", false);
batadv_sysfs_del_vlan(bat_priv, vlan);
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_put(vlan);
}
/**
* batadv_interface_add_vid - ndo_add_vid API implementation
* @dev: the netdev of the mesh interface
* @proto: protocol of the the vlan id
* @vid: identifier of the new vlan
*
* Set up all the internal structures for handling the new vlan on top of the
* mesh interface
*
* Returns 0 on success or a negative error code in case of failure.
* Return: 0 on success or a negative error code in case of failure.
*/
static int batadv_interface_add_vid(struct net_device *dev, __be16 proto,
unsigned short vid)
@@ -632,7 +646,7 @@ static int batadv_interface_add_vid(struct net_device *dev, __be16 proto,
if (!vlan->kobj) {
ret = batadv_sysfs_add_vlan(bat_priv->soft_iface, vlan);
if (ret) {
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_put(vlan);
return ret;
}
}
@@ -651,12 +665,13 @@ static int batadv_interface_add_vid(struct net_device *dev, __be16 proto,
/**
* batadv_interface_kill_vid - ndo_kill_vid API implementation
* @dev: the netdev of the mesh interface
* @proto: protocol of the the vlan id
* @vid: identifier of the deleted vlan
*
* Destroy all the internal structures used to handle the vlan identified by vid
* on top of the mesh interface
*
* Returns 0 on success, -EINVAL if the specified prototype is not ETH_P_8021Q
* Return: 0 on success, -EINVAL if the specified prototype is not ETH_P_8021Q
* or -ENOENT if the specified vlan id wasn't registered.
*/
static int batadv_interface_kill_vid(struct net_device *dev, __be16 proto,
@@ -678,7 +693,7 @@ static int batadv_interface_kill_vid(struct net_device *dev, __be16 proto,
batadv_softif_destroy_vlan(bat_priv, vlan);
/* finally free the vlan object */
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_put(vlan);
return 0;
}
@@ -734,7 +749,7 @@ static void batadv_softif_destroy_finish(struct work_struct *work)
vlan = batadv_softif_vlan_get(bat_priv, BATADV_NO_FLAGS);
if (vlan) {
batadv_softif_destroy_vlan(bat_priv, vlan);
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_put(vlan);
}
batadv_sysfs_del_meshif(soft_iface);
@@ -745,7 +760,7 @@ static void batadv_softif_destroy_finish(struct work_struct *work)
* batadv_softif_init_late - late stage initialization of soft interface
* @dev: registered network device to modify
*
* Returns error code on failures
* Return: error code on failures
*/
static int batadv_softif_init_late(struct net_device *dev)
{
@@ -847,7 +862,7 @@ free_bat_counters:
* @dev: batadv_soft_interface used as master interface
* @slave_dev: net_device which should become the slave interface
*
* Return 0 if successful or error otherwise.
* Return: 0 if successful or error otherwise.
*/
static int batadv_softif_slave_add(struct net_device *dev,
struct net_device *slave_dev)
@@ -863,7 +878,7 @@ static int batadv_softif_slave_add(struct net_device *dev,
out:
if (hard_iface)
batadv_hardif_free_ref(hard_iface);
batadv_hardif_put(hard_iface);
return ret;
}
@@ -872,7 +887,7 @@ out:
* @dev: batadv_soft_interface used as master interface
* @slave_dev: net_device which should be removed from the master interface
*
* Return 0 if successful or error otherwise.
* Return: 0 if successful or error otherwise.
*/
static int batadv_softif_slave_del(struct net_device *dev,
struct net_device *slave_dev)
@@ -890,7 +905,7 @@ static int batadv_softif_slave_del(struct net_device *dev,
out:
if (hard_iface)
batadv_hardif_free_ref(hard_iface);
batadv_hardif_put(hard_iface);
return ret;
}

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
@@ -34,7 +34,7 @@ void batadv_softif_destroy_sysfs(struct net_device *soft_iface);
int batadv_softif_is_valid(const struct net_device *net_dev);
extern struct rtnl_link_ops batadv_link_ops;
int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid);
void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *softif_vlan);
void batadv_softif_vlan_put(struct batadv_softif_vlan *softif_vlan);
struct batadv_softif_vlan *batadv_softif_vlan_get(struct batadv_priv *bat_priv,
unsigned short vid);

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2010-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2010-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*
@@ -25,6 +25,7 @@
#include <linux/fs.h>
#include <linux/if.h>
#include <linux/if_vlan.h>
#include <linux/kref.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/printk.h>
@@ -64,7 +65,7 @@ static struct batadv_priv *batadv_kobj_to_batpriv(struct kobject *obj)
* batadv_vlan_kobj_to_batpriv - convert a vlan kobj in the associated batpriv
* @obj: kobject to covert
*
* Returns the associated batadv_priv struct.
* Return: the associated batadv_priv struct.
*/
static struct batadv_priv *batadv_vlan_kobj_to_batpriv(struct kobject *obj)
{
@@ -82,9 +83,10 @@ static struct batadv_priv *batadv_vlan_kobj_to_batpriv(struct kobject *obj)
/**
* batadv_kobj_to_vlan - convert a kobj in the associated softif_vlan struct
* @bat_priv: the bat priv with all the soft interface information
* @obj: kobject to covert
*
* Returns the associated softif_vlan struct if found, NULL otherwise.
* Return: the associated softif_vlan struct if found, NULL otherwise.
*/
static struct batadv_softif_vlan *
batadv_kobj_to_vlan(struct batadv_priv *bat_priv, struct kobject *obj)
@@ -96,7 +98,7 @@ batadv_kobj_to_vlan(struct batadv_priv *bat_priv, struct kobject *obj)
if (vlan_tmp->kobj != obj)
continue;
if (!atomic_inc_not_zero(&vlan_tmp->refcount))
if (!kref_get_unless_zero(&vlan_tmp->refcount))
continue;
vlan = vlan_tmp;
@@ -214,7 +216,7 @@ ssize_t batadv_store_vlan_##_name(struct kobject *kobj, \
attr, &vlan->_name, \
bat_priv->soft_iface); \
\
batadv_softif_vlan_free_ref(vlan); \
batadv_softif_vlan_put(vlan); \
return res; \
}
@@ -229,7 +231,7 @@ ssize_t batadv_show_vlan_##_name(struct kobject *kobj, \
atomic_read(&vlan->_name) == 0 ? \
"disabled" : "enabled"); \
\
batadv_softif_vlan_free_ref(vlan); \
batadv_softif_vlan_put(vlan); \
return res; \
}
@@ -240,6 +242,55 @@ ssize_t batadv_show_vlan_##_name(struct kobject *kobj, \
static BATADV_ATTR_VLAN(_name, _mode, batadv_show_vlan_##_name, \
batadv_store_vlan_##_name)
#define BATADV_ATTR_HIF_STORE_UINT(_name, _var, _min, _max, _post_func) \
ssize_t batadv_store_##_name(struct kobject *kobj, \
struct attribute *attr, char *buff, \
size_t count) \
{ \
struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \
struct batadv_hard_iface *hard_iface; \
ssize_t length; \
\
hard_iface = batadv_hardif_get_by_netdev(net_dev); \
if (!hard_iface) \
return 0; \
\
length = __batadv_store_uint_attr(buff, count, _min, _max, \
_post_func, attr, \
&hard_iface->_var, net_dev); \
\
batadv_hardif_put(hard_iface); \
return length; \
}
#define BATADV_ATTR_HIF_SHOW_UINT(_name, _var) \
ssize_t batadv_show_##_name(struct kobject *kobj, \
struct attribute *attr, char *buff) \
{ \
struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \
struct batadv_hard_iface *hard_iface; \
ssize_t length; \
\
hard_iface = batadv_hardif_get_by_netdev(net_dev); \
if (!hard_iface) \
return 0; \
\
length = sprintf(buff, "%i\n", atomic_read(&hard_iface->_var)); \
\
batadv_hardif_put(hard_iface); \
return length; \
}
/* Use this, if you are going to set [name] in hard_iface to an
* unsigned integer value
*/
#define BATADV_ATTR_HIF_UINT(_name, _var, _mode, _min, _max, _post_func)\
static BATADV_ATTR_HIF_STORE_UINT(_name, _var, _min, \
_max, _post_func) \
static BATADV_ATTR_HIF_SHOW_UINT(_name, _var) \
static BATADV_ATTR(_name, _mode, batadv_show_##_name, \
batadv_store_##_name)
static int batadv_store_bool_attr(char *buff, size_t count,
struct net_device *net_dev,
const char *attr_name, atomic_t *attr,
@@ -491,7 +542,7 @@ static ssize_t batadv_store_gw_bwidth(struct kobject *kobj,
* @attr: the batman-adv attribute the user is interacting with
* @buff: the buffer that will contain the data to send back to the user
*
* Returns the number of bytes written into 'buff' on success or a negative
* Return: the number of bytes written into 'buff' on success or a negative
* error code in case of failure
*/
static ssize_t batadv_show_isolation_mark(struct kobject *kobj,
@@ -511,7 +562,7 @@ static ssize_t batadv_show_isolation_mark(struct kobject *kobj,
* @buff: the buffer containing the user data
* @count: number of bytes in the buffer
*
* Returns 'count' on success or a negative error code in case of failure
* Return: 'count' on success or a negative error code in case of failure
*/
static ssize_t batadv_store_isolation_mark(struct kobject *kobj,
struct attribute *attr, char *buff,
@@ -620,9 +671,7 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
BATADV_ATTR_VLAN_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL);
/**
* batadv_vlan_attrs - array of vlan specific sysfs attributes
*/
/* array of vlan specific sysfs attributes */
static struct batadv_attribute *batadv_vlan_attrs[] = {
&batadv_attr_vlan_ap_isolation,
NULL,
@@ -683,7 +732,7 @@ void batadv_sysfs_del_meshif(struct net_device *dev)
* @dev: netdev of the mesh interface
* @vlan: private data of the newly added VLAN interface
*
* Returns 0 on success and -ENOMEM if any of the structure allocations fails.
* Return: 0 on success and -ENOMEM if any of the structure allocations fails.
*/
int batadv_sysfs_add_vlan(struct net_device *dev,
struct batadv_softif_vlan *vlan)
@@ -771,7 +820,7 @@ static ssize_t batadv_show_mesh_iface(struct kobject *kobj,
length = sprintf(buff, "%s\n", ifname);
batadv_hardif_free_ref(hard_iface);
batadv_hardif_put(hard_iface);
return length;
}
@@ -795,7 +844,7 @@ static ssize_t batadv_store_mesh_iface(struct kobject *kobj,
if (strlen(buff) >= IFNAMSIZ) {
pr_err("Invalid parameter for 'mesh_iface' setting received: interface name too long '%s'\n",
buff);
batadv_hardif_free_ref(hard_iface);
batadv_hardif_put(hard_iface);
return -EINVAL;
}
@@ -829,7 +878,7 @@ static ssize_t batadv_store_mesh_iface(struct kobject *kobj,
unlock:
rtnl_unlock();
out:
batadv_hardif_free_ref(hard_iface);
batadv_hardif_put(hard_iface);
return ret;
}
@@ -863,18 +912,99 @@ static ssize_t batadv_show_iface_status(struct kobject *kobj,
break;
}
batadv_hardif_free_ref(hard_iface);
batadv_hardif_put(hard_iface);
return length;
}
#ifdef CONFIG_BATMAN_ADV_BATMAN_V
/**
* batadv_store_throughput_override - parse and store throughput override
* entered by the user
* @kobj: kobject representing the private mesh sysfs directory
* @attr: the batman-adv attribute the user is interacting with
* @buff: the buffer containing the user data
* @count: number of bytes in the buffer
*
* Return: 'count' on success or a negative error code in case of failure
*/
static ssize_t batadv_store_throughput_override(struct kobject *kobj,
struct attribute *attr,
char *buff, size_t count)
{
struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
struct batadv_hard_iface *hard_iface;
u32 tp_override;
u32 old_tp_override;
bool ret;
hard_iface = batadv_hardif_get_by_netdev(net_dev);
if (!hard_iface)
return -EINVAL;
if (buff[count - 1] == '\n')
buff[count - 1] = '\0';
ret = batadv_parse_throughput(net_dev, buff, "throughput_override",
&tp_override);
if (!ret)
return count;
old_tp_override = atomic_read(&hard_iface->bat_v.throughput_override);
if (old_tp_override == tp_override)
goto out;
batadv_info(net_dev, "%s: Changing from: %u.%u MBit to: %u.%u MBit\n",
"throughput_override",
old_tp_override / 10, old_tp_override % 10,
tp_override / 10, tp_override % 10);
atomic_set(&hard_iface->bat_v.throughput_override, tp_override);
out:
batadv_hardif_put(hard_iface);
return count;
}
static ssize_t batadv_show_throughput_override(struct kobject *kobj,
struct attribute *attr,
char *buff)
{
struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
struct batadv_hard_iface *hard_iface;
u32 tp_override;
hard_iface = batadv_hardif_get_by_netdev(net_dev);
if (!hard_iface)
return -EINVAL;
tp_override = atomic_read(&hard_iface->bat_v.throughput_override);
return sprintf(buff, "%u.%u MBit\n", tp_override / 10,
tp_override % 10);
}
#endif
static BATADV_ATTR(mesh_iface, S_IRUGO | S_IWUSR, batadv_show_mesh_iface,
batadv_store_mesh_iface);
static BATADV_ATTR(iface_status, S_IRUGO, batadv_show_iface_status, NULL);
#ifdef CONFIG_BATMAN_ADV_BATMAN_V
BATADV_ATTR_HIF_UINT(elp_interval, bat_v.elp_interval, S_IRUGO | S_IWUSR,
2 * BATADV_JITTER, INT_MAX, NULL);
static BATADV_ATTR(throughput_override, S_IRUGO | S_IWUSR,
batadv_show_throughput_override,
batadv_store_throughput_override);
#endif
static struct batadv_attribute *batadv_batman_attrs[] = {
&batadv_attr_mesh_iface,
&batadv_attr_iface_status,
#ifdef CONFIG_BATMAN_ADV_BATMAN_V
&batadv_attr_elp_interval,
&batadv_attr_throughput_override,
#endif
NULL,
};

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2010-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2010-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner
*

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich, Antonio Quartulli
*
@@ -31,6 +31,7 @@
#include <linux/jhash.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/netdevice.h>
@@ -68,7 +69,15 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv,
unsigned short vid, const char *message,
bool roaming);
/* returns 1 if they are the same mac addr and vid */
/**
* batadv_compare_tt - check if two TT entries are the same
* @node: the list element pointer of the first TT entry
* @data2: pointer to the tt_common_entry of the second TT entry
*
* Compare the MAC address and the VLAN ID of the two TT entries and check if
* they are the same TT client.
* Return: 1 if the two TT clients are the same, 0 otherwise
*/
static int batadv_compare_tt(const struct hlist_node *node, const void *data2)
{
const void *data1 = container_of(node, struct batadv_tt_common_entry,
@@ -84,7 +93,7 @@ static int batadv_compare_tt(const struct hlist_node *node, const void *data2)
* @data: pointer to the tt_common_entry object to map
* @size: the size of the hash table
*
* Returns the hash index where the object represented by 'data' should be
* Return: the hash index where the object represented by 'data' should be
* stored at.
*/
static inline u32 batadv_choose_tt(const void *data, u32 size)
@@ -105,7 +114,7 @@ static inline u32 batadv_choose_tt(const void *data, u32 size)
* @addr: the mac address of the client to look for
* @vid: VLAN identifier
*
* Returns a pointer to the tt_common struct belonging to the searched client if
* Return: a pointer to the tt_common struct belonging to the searched client if
* found, NULL otherwise.
*/
static struct batadv_tt_common_entry *
@@ -133,7 +142,7 @@ batadv_tt_hash_find(struct batadv_hashtable *hash, const u8 *addr,
if (tt->vid != vid)
continue;
if (!atomic_inc_not_zero(&tt->refcount))
if (!kref_get_unless_zero(&tt->refcount))
continue;
tt_tmp = tt;
@@ -150,7 +159,7 @@ batadv_tt_hash_find(struct batadv_hashtable *hash, const u8 *addr,
* @addr: the mac address of the client to look for
* @vid: VLAN identifier
*
* Returns a pointer to the corresponding tt_local_entry struct if the client is
* Return: a pointer to the corresponding tt_local_entry struct if the client is
* found, NULL otherwise.
*/
static struct batadv_tt_local_entry *
@@ -175,7 +184,7 @@ batadv_tt_local_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
* @addr: the mac address of the client to look for
* @vid: VLAN identifier
*
* Returns a pointer to the corresponding tt_global_entry struct if the client
* Return: a pointer to the corresponding tt_global_entry struct if the client
* is found, NULL otherwise.
*/
static struct batadv_tt_global_entry *
@@ -194,34 +203,68 @@ batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
return tt_global_entry;
}
static void
batadv_tt_local_entry_free_ref(struct batadv_tt_local_entry *tt_local_entry)
/**
* batadv_tt_local_entry_release - release tt_local_entry from lists and queue
* for free after rcu grace period
* @ref: kref pointer of the nc_node
*/
static void batadv_tt_local_entry_release(struct kref *ref)
{
if (atomic_dec_and_test(&tt_local_entry->common.refcount))
kfree_rcu(tt_local_entry, common.rcu);
struct batadv_tt_local_entry *tt_local_entry;
tt_local_entry = container_of(ref, struct batadv_tt_local_entry,
common.refcount);
kfree_rcu(tt_local_entry, common.rcu);
}
/**
* batadv_tt_global_entry_free_ref - decrement the refcounter for a
* tt_global_entry and possibly free it
* @tt_global_entry: the object to free
* batadv_tt_local_entry_put - decrement the tt_local_entry refcounter and
* possibly release it
* @tt_local_entry: tt_local_entry to be free'd
*/
static void
batadv_tt_global_entry_free_ref(struct batadv_tt_global_entry *tt_global_entry)
batadv_tt_local_entry_put(struct batadv_tt_local_entry *tt_local_entry)
{
if (atomic_dec_and_test(&tt_global_entry->common.refcount)) {
batadv_tt_global_del_orig_list(tt_global_entry);
kfree_rcu(tt_global_entry, common.rcu);
}
kref_put(&tt_local_entry->common.refcount,
batadv_tt_local_entry_release);
}
/**
* batadv_tt_global_entry_release - release tt_global_entry from lists and queue
* for free after rcu grace period
* @ref: kref pointer of the nc_node
*/
static void batadv_tt_global_entry_release(struct kref *ref)
{
struct batadv_tt_global_entry *tt_global_entry;
tt_global_entry = container_of(ref, struct batadv_tt_global_entry,
common.refcount);
batadv_tt_global_del_orig_list(tt_global_entry);
kfree_rcu(tt_global_entry, common.rcu);
}
/**
* batadv_tt_global_entry_put - decrement the tt_global_entry refcounter and
* possibly release it
* @tt_global_entry: tt_global_entry to be free'd
*/
static void
batadv_tt_global_entry_put(struct batadv_tt_global_entry *tt_global_entry)
{
kref_put(&tt_global_entry->common.refcount,
batadv_tt_global_entry_release);
}
/**
* batadv_tt_global_hash_count - count the number of orig entries
* @hash: hash table containing the tt entries
* @bat_priv: the bat priv with all the soft interface information
* @addr: the mac address of the client to count entries for
* @vid: VLAN identifier
*
* Return the number of originators advertising the given address/data
* Return: the number of originators advertising the given address/data
* (excluding ourself).
*/
int batadv_tt_global_hash_count(struct batadv_priv *bat_priv,
@@ -235,7 +278,7 @@ int batadv_tt_global_hash_count(struct batadv_priv *bat_priv,
return 0;
count = atomic_read(&tt_global_entry->orig_list_count);
batadv_tt_global_entry_free_ref(tt_global_entry);
batadv_tt_global_entry_put(tt_global_entry);
return count;
}
@@ -258,7 +301,7 @@ static void batadv_tt_local_size_mod(struct batadv_priv *bat_priv,
atomic_add(v, &vlan->tt.num_entries);
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_put(vlan);
}
/**
@@ -286,9 +329,9 @@ static void batadv_tt_local_size_dec(struct batadv_priv *bat_priv,
}
/**
* batadv_tt_global_size_mod - change the size by v of the local table
* identified by vid
* @bat_priv: the bat priv with all the soft interface information
* batadv_tt_global_size_mod - change the size by v of the global table
* for orig_node identified by vid
* @orig_node: the originator for which the table has to be modified
* @vid: the VLAN identifier
* @v: the amount to sum to the global table size
*/
@@ -305,12 +348,12 @@ static void batadv_tt_global_size_mod(struct batadv_orig_node *orig_node,
spin_lock_bh(&orig_node->vlan_list_lock);
if (!hlist_unhashed(&vlan->list)) {
hlist_del_init_rcu(&vlan->list);
batadv_orig_node_vlan_free_ref(vlan);
batadv_orig_node_vlan_put(vlan);
}
spin_unlock_bh(&orig_node->vlan_list_lock);
}
batadv_orig_node_vlan_free_ref(vlan);
batadv_orig_node_vlan_put(vlan);
}
/**
@@ -340,22 +383,28 @@ static void batadv_tt_global_size_dec(struct batadv_orig_node *orig_node,
/**
* batadv_tt_orig_list_entry_release - release tt orig entry from lists and
* queue for free after rcu grace period
* @orig_entry: tt orig entry to be free'd
* @ref: kref pointer of the tt orig entry
*/
static void
batadv_tt_orig_list_entry_release(struct batadv_tt_orig_list_entry *orig_entry)
static void batadv_tt_orig_list_entry_release(struct kref *ref)
{
batadv_orig_node_free_ref(orig_entry->orig_node);
struct batadv_tt_orig_list_entry *orig_entry;
orig_entry = container_of(ref, struct batadv_tt_orig_list_entry,
refcount);
batadv_orig_node_put(orig_entry->orig_node);
kfree_rcu(orig_entry, rcu);
}
/**
* batadv_tt_orig_list_entry_put - decrement the tt orig entry refcounter and
* possibly release it
* @orig_entry: tt orig entry to be free'd
*/
static void
batadv_tt_orig_list_entry_free_ref(struct batadv_tt_orig_list_entry *orig_entry)
batadv_tt_orig_list_entry_put(struct batadv_tt_orig_list_entry *orig_entry)
{
if (!atomic_dec_and_test(&orig_entry->refcount))
return;
batadv_tt_orig_list_entry_release(orig_entry);
kref_put(&orig_entry->refcount, batadv_tt_orig_list_entry_release);
}
/**
@@ -437,7 +486,7 @@ unlock:
* batadv_tt_len - compute length in bytes of given number of tt changes
* @changes_num: number of tt changes
*
* Returns computed length in bytes.
* Return: computed length in bytes.
*/
static int batadv_tt_len(int changes_num)
{
@@ -448,7 +497,7 @@ static int batadv_tt_len(int changes_num)
* batadv_tt_entries - compute the number of entries fitting in tt_len bytes
* @tt_len: available space
*
* Returns the number of entries.
* Return: the number of entries.
*/
static u16 batadv_tt_entries(u16 tt_len)
{
@@ -460,7 +509,7 @@ static u16 batadv_tt_entries(u16 tt_len)
* size when transmitted over the air
* @bat_priv: the bat priv with all the soft interface information
*
* Returns local translation table size in bytes.
* Return: local translation table size in bytes.
*/
static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv)
{
@@ -512,7 +561,7 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv,
batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt,
batadv_choose_tt, &tt_global->common);
batadv_tt_global_entry_free_ref(tt_global);
batadv_tt_global_entry_put(tt_global);
}
/**
@@ -526,7 +575,7 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv,
* @mark: the value contained in the skb->mark field of the received packet (if
* any)
*
* Returns true if the client was successfully added, false otherwise.
* Return: true if the client was successfully added, false otherwise.
*/
bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
unsigned short vid, int ifindex, u32 mark)
@@ -620,7 +669,8 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
tt_local->common.vid = vid;
if (batadv_is_wifi_netdev(in_dev))
tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
atomic_set(&tt_local->common.refcount, 2);
kref_init(&tt_local->common.refcount);
kref_get(&tt_local->common.refcount);
tt_local->last_seen = jiffies;
tt_local->common.added_at = tt_local->last_seen;
@@ -637,8 +687,8 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
if (unlikely(hash_added != 0)) {
/* remove the reference for the hash */
batadv_tt_local_entry_free_ref(tt_local);
batadv_softif_vlan_free_ref(vlan);
batadv_tt_local_entry_put(tt_local);
batadv_softif_vlan_put(vlan);
goto out;
}
@@ -704,9 +754,9 @@ out:
if (in_dev)
dev_put(in_dev);
if (tt_local)
batadv_tt_local_entry_free_ref(tt_local);
batadv_tt_local_entry_put(tt_local);
if (tt_global)
batadv_tt_global_entry_free_ref(tt_global);
batadv_tt_global_entry_put(tt_global);
return ret;
}
@@ -721,12 +771,11 @@ out:
* function reserves the amount of space needed to send the entire global TT
* table. In case of success the value is updated with the real amount of
* reserved bytes
* Allocate the needed amount of memory for the entire TT TVLV and write its
* header made up by one tvlv_tt_data object and a series of tvlv_tt_vlan_data
* objects, one per active VLAN served by the originator node.
*
* Return the size of the allocated buffer or 0 in case of failure.
* Return: the size of the allocated buffer or 0 in case of failure.
*/
static u16
batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
@@ -800,7 +849,7 @@ out:
* header made up by one tvlv_tt_data object and a series of tvlv_tt_vlan_data
* objects, one per active VLAN.
*
* Return the size of the allocated buffer or 0 in case of failure.
* Return: the size of the allocated buffer or 0 in case of failure.
*/
static u16
batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
@@ -1005,13 +1054,13 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
no_purge ? 0 : last_seen_msecs,
vlan->tt.crc);
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_put(vlan);
}
rcu_read_unlock();
}
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
return 0;
}
@@ -1042,7 +1091,7 @@ batadv_tt_local_set_pending(struct batadv_priv *bat_priv,
* @message: message to append to the log on deletion
* @roaming: true if the deletion is due to a roaming event
*
* Returns the flags assigned to the local entry before being deleted
* Return: the flags assigned to the local entry before being deleted
*/
u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,
unsigned short vid, const char *message,
@@ -1088,19 +1137,19 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,
goto out;
/* extra call to free the local tt entry */
batadv_tt_local_entry_free_ref(tt_local_entry);
batadv_tt_local_entry_put(tt_local_entry);
/* decrease the reference held for this vlan */
vlan = batadv_softif_vlan_get(bat_priv, vid);
if (!vlan)
goto out;
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_put(vlan);
batadv_softif_vlan_put(vlan);
out:
if (tt_local_entry)
batadv_tt_local_entry_free_ref(tt_local_entry);
batadv_tt_local_entry_put(tt_local_entry);
return curr_flags;
}
@@ -1196,11 +1245,11 @@ static void batadv_tt_local_table_free(struct batadv_priv *bat_priv)
vlan = batadv_softif_vlan_get(bat_priv,
tt_common_entry->vid);
if (vlan) {
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_put(vlan);
batadv_softif_vlan_put(vlan);
}
batadv_tt_local_entry_free_ref(tt_local);
batadv_tt_local_entry_put(tt_local);
}
spin_unlock_bh(list_lock);
}
@@ -1242,10 +1291,16 @@ static void batadv_tt_changes_list_free(struct batadv_priv *bat_priv)
spin_unlock_bh(&bat_priv->tt.changes_list_lock);
}
/* retrieves the orig_tt_list_entry belonging to orig_node from the
/**
* batadv_tt_global_orig_entry_find - find a TT orig_list_entry
* @entry: the TT global entry where the orig_list_entry has to be
* extracted from
* @orig_node: the originator for which the orig_list_entry has to be found
*
* retrieve the orig_tt_list_entry belonging to orig_node from the
* batadv_tt_global_entry list
*
* returns it with an increased refcounter, NULL if not found
* Return: it with an increased refcounter, NULL if not found
*/
static struct batadv_tt_orig_list_entry *
batadv_tt_global_orig_entry_find(const struct batadv_tt_global_entry *entry,
@@ -1259,7 +1314,7 @@ batadv_tt_global_orig_entry_find(const struct batadv_tt_global_entry *entry,
hlist_for_each_entry_rcu(tmp_orig_entry, head, list) {
if (tmp_orig_entry->orig_node != orig_node)
continue;
if (!atomic_inc_not_zero(&tmp_orig_entry->refcount))
if (!kref_get_unless_zero(&tmp_orig_entry->refcount))
continue;
orig_entry = tmp_orig_entry;
@@ -1270,8 +1325,15 @@ batadv_tt_global_orig_entry_find(const struct batadv_tt_global_entry *entry,
return orig_entry;
}
/* find out if an orig_node is already in the list of a tt_global_entry.
* returns true if found, false otherwise
/**
* batadv_tt_global_entry_has_orig - check if a TT global entry is also handled
* by a given originator
* @entry: the TT global entry to check
* @orig_node: the originator to search in the list
*
* find out if an orig_node is already in the list of a tt_global_entry.
*
* Return: true if found, false otherwise
*/
static bool
batadv_tt_global_entry_has_orig(const struct batadv_tt_global_entry *entry,
@@ -1283,7 +1345,7 @@ batadv_tt_global_entry_has_orig(const struct batadv_tt_global_entry *entry,
orig_entry = batadv_tt_global_orig_entry_find(entry, orig_node);
if (orig_entry) {
found = true;
batadv_tt_orig_list_entry_free_ref(orig_entry);
batadv_tt_orig_list_entry_put(orig_entry);
}
return found;
@@ -1309,11 +1371,12 @@ batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global,
goto out;
INIT_HLIST_NODE(&orig_entry->list);
atomic_inc(&orig_node->refcount);
kref_get(&orig_node->refcount);
batadv_tt_global_size_inc(orig_node, tt_global->common.vid);
orig_entry->orig_node = orig_node;
orig_entry->ttvn = ttvn;
atomic_set(&orig_entry->refcount, 2);
kref_init(&orig_entry->refcount);
kref_get(&orig_entry->refcount);
spin_lock_bh(&tt_global->list_lock);
hlist_add_head_rcu(&orig_entry->list,
@@ -1323,7 +1386,7 @@ batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global,
out:
if (orig_entry)
batadv_tt_orig_list_entry_free_ref(orig_entry);
batadv_tt_orig_list_entry_put(orig_entry);
}
/**
@@ -1343,7 +1406,7 @@ out:
*
* The caller must hold orig_node refcount.
*
* Return true if the new entry has been added, false otherwise
* Return: true if the new entry has been added, false otherwise
*/
static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
@@ -1389,7 +1452,8 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
*/
if (flags & BATADV_TT_CLIENT_ROAM)
tt_global_entry->roam_at = jiffies;
atomic_set(&common->refcount, 2);
kref_init(&common->refcount);
kref_get(&common->refcount);
common->added_at = jiffies;
INIT_HLIST_HEAD(&tt_global_entry->orig_list);
@@ -1403,7 +1467,7 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
if (unlikely(hash_added != 0)) {
/* remove the reference for the hash */
batadv_tt_global_entry_free_ref(tt_global_entry);
batadv_tt_global_entry_put(tt_global_entry);
goto out_remove;
}
} else {
@@ -1489,9 +1553,9 @@ out_remove:
out:
if (tt_global_entry)
batadv_tt_global_entry_free_ref(tt_global_entry);
batadv_tt_global_entry_put(tt_global_entry);
if (tt_local_entry)
batadv_tt_local_entry_free_ref(tt_local_entry);
batadv_tt_local_entry_put(tt_local_entry);
return ret;
}
@@ -1501,7 +1565,7 @@ out:
* @tt_global_entry: global translation table entry to be analyzed
*
* This functon assumes the caller holds rcu_read_lock().
* Returns best originator list entry or NULL on errors.
* Return: best originator list entry or NULL on errors.
*/
static struct batadv_tt_orig_list_entry *
batadv_transtable_best_orig(struct batadv_priv *bat_priv,
@@ -1522,20 +1586,20 @@ batadv_transtable_best_orig(struct batadv_priv *bat_priv,
if (best_router &&
bao->bat_neigh_cmp(router, BATADV_IF_DEFAULT,
best_router, BATADV_IF_DEFAULT) <= 0) {
batadv_neigh_node_free_ref(router);
batadv_neigh_node_put(router);
continue;
}
/* release the refcount for the "old" best */
if (best_router)
batadv_neigh_node_free_ref(best_router);
batadv_neigh_node_put(best_router);
best_entry = orig_entry;
best_router = router;
}
if (best_router)
batadv_neigh_node_free_ref(best_router);
batadv_neigh_node_put(best_router);
return best_entry;
}
@@ -1588,7 +1652,7 @@ batadv_tt_global_print_entry(struct batadv_priv *bat_priv,
((flags & BATADV_TT_CLIENT_ISOLA) ? 'I' : '.'),
((flags & BATADV_TT_CLIENT_TEMP) ? 'T' : '.'));
batadv_orig_node_vlan_free_ref(vlan);
batadv_orig_node_vlan_put(vlan);
}
print_list:
@@ -1620,7 +1684,7 @@ print_list:
((flags & BATADV_TT_CLIENT_ISOLA) ? 'I' : '.'),
((flags & BATADV_TT_CLIENT_TEMP) ? 'T' : '.'));
batadv_orig_node_vlan_free_ref(vlan);
batadv_orig_node_vlan_put(vlan);
}
}
@@ -1661,7 +1725,7 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset)
}
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
return 0;
}
@@ -1689,7 +1753,7 @@ _batadv_tt_global_del_orig_entry(struct batadv_tt_global_entry *tt_global_entry,
* being part of a list
*/
hlist_del_rcu(&orig_entry->list);
batadv_tt_orig_list_entry_free_ref(orig_entry);
batadv_tt_orig_list_entry_put(orig_entry);
}
/* deletes the orig list of a tt_global_entry */
@@ -1845,9 +1909,9 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv,
out:
if (tt_global_entry)
batadv_tt_global_entry_free_ref(tt_global_entry);
batadv_tt_global_entry_put(tt_global_entry);
if (local_entry)
batadv_tt_local_entry_free_ref(local_entry);
batadv_tt_local_entry_put(local_entry);
}
/**
@@ -1901,7 +1965,7 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
tt_global->common.addr,
BATADV_PRINT_VID(vid), message);
hlist_del_rcu(&tt_common_entry->hash_entry);
batadv_tt_global_entry_free_ref(tt_global);
batadv_tt_global_entry_put(tt_global);
}
}
spin_unlock_bh(list_lock);
@@ -1964,7 +2028,7 @@ static void batadv_tt_global_purge(struct batadv_priv *bat_priv)
hlist_del_rcu(&tt_common->hash_entry);
batadv_tt_global_entry_free_ref(tt_global);
batadv_tt_global_entry_put(tt_global);
}
spin_unlock_bh(list_lock);
}
@@ -1996,7 +2060,7 @@ static void batadv_tt_global_table_free(struct batadv_priv *bat_priv)
tt_global = container_of(tt_common_entry,
struct batadv_tt_global_entry,
common);
batadv_tt_global_entry_free_ref(tt_global);
batadv_tt_global_entry_put(tt_global);
}
spin_unlock_bh(list_lock);
}
@@ -2031,7 +2095,7 @@ _batadv_is_ap_isolated(struct batadv_tt_local_entry *tt_local_entry,
* @addr: mac address of the destination client
* @vid: VLAN identifier
*
* Returns a pointer to the originator that was selected as destination in the
* Return: a pointer to the originator that was selected as destination in the
* mesh for contacting the client 'addr', NULL otherwise.
* In case of multiple originators serving the same client, the function returns
* the best one (best in terms of metric towards the destination node).
@@ -2071,15 +2135,15 @@ struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
/* found anything? */
if (best_entry)
orig_node = best_entry->orig_node;
if (orig_node && !atomic_inc_not_zero(&orig_node->refcount))
if (orig_node && !kref_get_unless_zero(&orig_node->refcount))
orig_node = NULL;
rcu_read_unlock();
out:
if (tt_global_entry)
batadv_tt_global_entry_free_ref(tt_global_entry);
batadv_tt_global_entry_put(tt_global_entry);
if (tt_local_entry)
batadv_tt_local_entry_free_ref(tt_local_entry);
batadv_tt_local_entry_put(tt_local_entry);
return orig_node;
}
@@ -2106,7 +2170,7 @@ out:
* because the XOR operation can combine them all while trying to reduce the
* noise as much as possible.
*
* Returns the checksum of the global table of a given originator.
* Return: the checksum of the global table of a given originator.
*/
static u32 batadv_tt_global_crc(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
@@ -2183,7 +2247,7 @@ static u32 batadv_tt_global_crc(struct batadv_priv *bat_priv,
* For details about the computation, please refer to the documentation for
* batadv_tt_global_crc().
*
* Returns the checksum of the local table
* Return: the checksum of the local table
*/
static u32 batadv_tt_local_crc(struct batadv_priv *bat_priv,
unsigned short vid)
@@ -2289,7 +2353,7 @@ static void batadv_tt_req_purge(struct batadv_priv *bat_priv)
* @bat_priv: the bat priv with all the soft interface information
* @orig_node: orig node this request is being issued for
*
* Returns the pointer to the new tt_req_node struct if no request
* Return: the pointer to the new tt_req_node struct if no request
* has already been issued for this orig_node, NULL otherwise.
*/
static struct batadv_tt_req_node *
@@ -2324,7 +2388,7 @@ unlock:
* @entry_ptr: to be checked local tt entry
* @data_ptr: not used but definition required to satisfy the callback prototype
*
* Returns 1 if the entry is a valid, 0 otherwise.
* Return: 1 if the entry is a valid, 0 otherwise.
*/
static int batadv_tt_local_valid(const void *entry_ptr, const void *data_ptr)
{
@@ -2408,9 +2472,8 @@ static void batadv_tt_tvlv_generate(struct batadv_priv *bat_priv,
* @orig_node: originator for which the CRCs have to be checked
* @tt_vlan: pointer to the first tvlv VLAN entry
* @num_vlan: number of tvlv VLAN entries
* @create: if true, create VLAN objects if not found
*
* Return true if all the received CRCs match the locally stored ones, false
* Return: true if all the received CRCs match the locally stored ones, false
* otherwise
*/
static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node,
@@ -2440,7 +2503,7 @@ static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node,
return false;
crc = vlan->tt.crc;
batadv_orig_node_vlan_free_ref(vlan);
batadv_orig_node_vlan_put(vlan);
if (crc != ntohl(tt_vlan_tmp->crc))
return false;
@@ -2513,6 +2576,8 @@ static void batadv_tt_global_update_crc(struct batadv_priv *bat_priv,
* @num_vlan: number of tvlv VLAN entries
* @full_table: ask for the entire translation table if true, while only for the
* last TT diff otherwise
*
* Return: true if the TT Request was sent, false otherwise
*/
static int batadv_send_tt_request(struct batadv_priv *bat_priv,
struct batadv_orig_node *dst_orig_node,
@@ -2573,7 +2638,7 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv,
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
if (ret && tt_req_node) {
spin_lock_bh(&bat_priv->tt.req_list_lock);
/* hlist_del_init() verifies tt_req_node still is in the list */
@@ -2593,7 +2658,7 @@ out:
* @req_src: mac address of tt request sender
* @req_dst: mac address of tt request recipient
*
* Returns true if tt request reply was sent, false otherwise.
* Return: true if tt request reply was sent, false otherwise.
*/
static bool batadv_send_other_tt_response(struct batadv_priv *bat_priv,
struct batadv_tvlv_tt_data *tt_data,
@@ -2711,9 +2776,9 @@ unlock:
out:
if (res_dst_orig_node)
batadv_orig_node_free_ref(res_dst_orig_node);
batadv_orig_node_put(res_dst_orig_node);
if (req_dst_orig_node)
batadv_orig_node_free_ref(req_dst_orig_node);
batadv_orig_node_put(req_dst_orig_node);
kfree(tvlv_tt_data);
return ret;
}
@@ -2725,7 +2790,7 @@ out:
* @tt_data: tt data containing the tt request information
* @req_src: mac address of tt request sender
*
* Returns true if tt request reply was sent, false otherwise.
* Return: true if tt request reply was sent, false otherwise.
*/
static bool batadv_send_my_tt_response(struct batadv_priv *bat_priv,
struct batadv_tvlv_tt_data *tt_data,
@@ -2828,9 +2893,9 @@ unlock:
out:
spin_unlock_bh(&bat_priv->tt.commit_lock);
if (orig_node)
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
kfree(tvlv_tt_data);
/* The packet was for this host, so it doesn't need to be re-routed */
return true;
@@ -2843,7 +2908,7 @@ out:
* @req_src: mac address of tt request sender
* @req_dst: mac address of tt request recipient
*
* Returns true if tt request reply was sent, false otherwise.
* Return: true if tt request reply was sent, false otherwise.
*/
static bool batadv_send_tt_response(struct batadv_priv *bat_priv,
struct batadv_tvlv_tt_data *tt_data,
@@ -2916,7 +2981,7 @@ static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv,
out:
if (orig_node)
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
}
static void batadv_tt_update_changes(struct batadv_priv *bat_priv,
@@ -2938,7 +3003,7 @@ static void batadv_tt_update_changes(struct batadv_priv *bat_priv,
* @addr: the mac address of the client to check
* @vid: VLAN identifier
*
* Returns true if the client is served by this node, false otherwise.
* Return: true if the client is served by this node, false otherwise.
*/
bool batadv_is_my_client(struct batadv_priv *bat_priv, const u8 *addr,
unsigned short vid)
@@ -2958,7 +3023,7 @@ bool batadv_is_my_client(struct batadv_priv *bat_priv, const u8 *addr,
ret = true;
out:
if (tt_local_entry)
batadv_tt_local_entry_free_ref(tt_local_entry);
batadv_tt_local_entry_put(tt_local_entry);
return ret;
}
@@ -3022,7 +3087,7 @@ static void batadv_handle_tt_response(struct batadv_priv *bat_priv,
spin_unlock_bh(&bat_priv->tt.req_list_lock);
out:
if (orig_node)
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
}
static void batadv_tt_roam_list_free(struct batadv_priv *bat_priv)
@@ -3055,11 +3120,16 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
spin_unlock_bh(&bat_priv->tt.roam_list_lock);
}
/* This function checks whether the client already reached the
/**
* batadv_tt_check_roam_count - check if a client has roamed too frequently
* @bat_priv: the bat priv with all the soft interface information
* @client: mac address of the roaming client
*
* This function checks whether the client already reached the
* maximum number of possible roaming phases. In this case the ROAMING_ADV
* will not be sent.
*
* returns true if the ROAMING_ADV can be sent, false otherwise
* Return: true if the ROAMING_ADV can be sent, false otherwise
*/
static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
{
@@ -3148,7 +3218,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client,
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
batadv_hardif_put(primary_if);
}
static void batadv_tt_purge(struct work_struct *work)
@@ -3272,11 +3342,11 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
/* decrease the reference held for this vlan */
vlan = batadv_softif_vlan_get(bat_priv, tt_common->vid);
if (vlan) {
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_put(vlan);
batadv_softif_vlan_put(vlan);
}
batadv_tt_local_entry_free_ref(tt_local);
batadv_tt_local_entry_put(tt_local);
}
spin_unlock_bh(list_lock);
}
@@ -3359,11 +3429,11 @@ bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, u8 *src, u8 *dst,
ret = true;
out:
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_put(vlan);
if (tt_global_entry)
batadv_tt_global_entry_free_ref(tt_global_entry);
batadv_tt_global_entry_put(tt_global_entry);
if (tt_local_entry)
batadv_tt_local_entry_free_ref(tt_local_entry);
batadv_tt_local_entry_put(tt_local_entry);
return ret;
}
@@ -3371,13 +3441,12 @@ out:
* batadv_tt_update_orig - update global translation table with new tt
* information received via ogms
* @bat_priv: the bat priv with all the soft interface information
* @orig: the orig_node of the ogm
* @tt_vlan: pointer to the first tvlv VLAN entry
* @orig_node: the orig_node of the ogm
* @tt_buff: pointer to the first tvlv VLAN entry
* @tt_num_vlan: number of tvlv VLAN entries
* @tt_change: pointer to the first entry in the TT buffer
* @tt_num_changes: number of tt changes inside the tt buffer
* @ttvn: translation table version number of this changeset
* @tt_crc: crc32 checksum of orig node's translation table
*/
static void batadv_tt_update_orig(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
@@ -3459,7 +3528,7 @@ request_table:
* @addr: the mac address of the client to check
* @vid: VLAN identifier
*
* Returns true if we know that the client has moved from its old originator
* Return: true if we know that the client has moved from its old originator
* to another one. This entry is still kept for consistency purposes and will be
* deleted later by a DEL or because of timeout
*/
@@ -3474,7 +3543,7 @@ bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv,
goto out;
ret = tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM;
batadv_tt_global_entry_free_ref(tt_global_entry);
batadv_tt_global_entry_put(tt_global_entry);
out:
return ret;
}
@@ -3485,7 +3554,7 @@ out:
* @addr: the mac address of the local client to query
* @vid: VLAN identifier
*
* Returns true if the local client is known to be roaming (it is not served by
* Return: true if the local client is known to be roaming (it is not served by
* this node anymore) or not. If yes, the client is still present in the table
* to keep the latter consistent with the node TTVN
*/
@@ -3500,7 +3569,7 @@ bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
goto out;
ret = tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM;
batadv_tt_local_entry_free_ref(tt_local_entry);
batadv_tt_local_entry_put(tt_local_entry);
out:
return ret;
}
@@ -3614,7 +3683,7 @@ static void batadv_tt_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
* @tvlv_value: tvlv buffer containing the tt data
* @tvlv_value_len: tvlv buffer length
*
* Returns NET_RX_DROP if the tt tvlv is to be re-routed, NET_RX_SUCCESS
* Return: NET_RX_DROP if the tt tvlv is to be re-routed, NET_RX_SUCCESS
* otherwise.
*/
static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
@@ -3695,7 +3764,7 @@ static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
* @tvlv_value: tvlv buffer containing the tt data
* @tvlv_value_len: tvlv buffer length
*
* Returns NET_RX_DROP if the tt roam tvlv is to be re-routed, NET_RX_SUCCESS
* Return: NET_RX_DROP if the tt roam tvlv is to be re-routed, NET_RX_SUCCESS
* otherwise.
*/
static int batadv_roam_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
@@ -3733,7 +3802,7 @@ static int batadv_roam_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
out:
if (orig_node)
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_put(orig_node);
return NET_RX_SUCCESS;
}
@@ -3741,7 +3810,7 @@ out:
* batadv_tt_init - initialise the translation table internals
* @bat_priv: the bat priv with all the soft interface information
*
* Return 0 on success or negative error number in case of failure.
* Return: 0 on success or negative error number in case of failure.
*/
int batadv_tt_init(struct batadv_priv *bat_priv)
{
@@ -3779,7 +3848,7 @@ int batadv_tt_init(struct batadv_priv *bat_priv)
* @addr: the mac address of the client
* @vid: the identifier of the VLAN where this client is connected
*
* Returns true if the client is marked with the TT_CLIENT_ISOLA flag, false
* Return: true if the client is marked with the TT_CLIENT_ISOLA flag, false
* otherwise
*/
bool batadv_tt_global_is_isolated(struct batadv_priv *bat_priv,
@@ -3794,7 +3863,7 @@ bool batadv_tt_global_is_isolated(struct batadv_priv *bat_priv,
ret = tt->common.flags & BATADV_TT_CLIENT_ISOLA;
batadv_tt_global_entry_free_ref(tt);
batadv_tt_global_entry_put(tt);
return ret;
}

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich, Antonio Quartulli
*

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
/* Copyright (C) 2007-2016 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
@@ -22,9 +22,11 @@
#error only "main.h" can be included directly
#endif
#include <linux/average.h>
#include <linux/bitops.h>
#include <linux/compiler.h>
#include <linux/if_ether.h>
#include <linux/kref.h>
#include <linux/netdevice.h>
#include <linux/sched.h> /* for linux/wait.h */
#include <linux/spinlock.h>
@@ -73,7 +75,7 @@ enum batadv_dhcp_recipient {
#define BATADV_TT_SYNC_MASK 0x00F0
/**
* struct batadv_hard_iface_bat_iv - per hard interface B.A.T.M.A.N. IV data
* struct batadv_hard_iface_bat_iv - per hard-interface B.A.T.M.A.N. IV data
* @ogm_buff: buffer holding the OGM packet
* @ogm_buff_len: length of the OGM packet buffer
* @ogm_seqno: OGM sequence number - used to identify each OGM
@@ -84,6 +86,36 @@ struct batadv_hard_iface_bat_iv {
atomic_t ogm_seqno;
};
/**
* enum batadv_v_hard_iface_flags - interface flags useful to B.A.T.M.A.N. V
* @BATADV_FULL_DUPLEX: tells if the connection over this link is full-duplex
* @BATADV_WARNING_DEFAULT: tells whether we have warned the user that no
* throughput data is available for this interface and that default values are
* assumed.
*/
enum batadv_v_hard_iface_flags {
BATADV_FULL_DUPLEX = BIT(0),
BATADV_WARNING_DEFAULT = BIT(1),
};
/**
* struct batadv_hard_iface_bat_v - per hard-interface B.A.T.M.A.N. V data
* @elp_interval: time interval between two ELP transmissions
* @elp_seqno: current ELP sequence number
* @elp_skb: base skb containing the ELP message to send
* @elp_wq: workqueue used to schedule ELP transmissions
* @throughput_override: throughput override to disable link auto-detection
* @flags: interface specific flags
*/
struct batadv_hard_iface_bat_v {
atomic_t elp_interval;
atomic_t elp_seqno;
struct sk_buff *elp_skb;
struct delayed_work elp_wq;
atomic_t throughput_override;
u8 flags;
};
/**
* struct batadv_hard_iface - network device known to batman-adv
* @list: list node for batadv_hardif_list
@@ -97,8 +129,9 @@ struct batadv_hard_iface_bat_iv {
* batman-adv for this interface
* @soft_iface: the batman-adv interface which uses this network interface
* @rcu: struct used for freeing in an RCU-safe manner
* @bat_iv: BATMAN IV specific per hard interface data
* @cleanup_work: work queue callback item for hard interface deinit
* @bat_iv: per hard-interface B.A.T.M.A.N. IV data
* @bat_v: per hard-interface B.A.T.M.A.N. V data
* @cleanup_work: work queue callback item for hard-interface deinit
* @debug_dir: dentry for nc subdir in batman-adv directory in debugfs
* @neigh_list: list of unique single hop neighbors via this interface
* @neigh_list_lock: lock protecting neigh_list
@@ -110,11 +143,14 @@ struct batadv_hard_iface {
struct net_device *net_dev;
u8 num_bcasts;
struct kobject *hardif_obj;
atomic_t refcount;
struct kref refcount;
struct packet_type batman_adv_ptype;
struct net_device *soft_iface;
struct rcu_head rcu;
struct batadv_hard_iface_bat_iv bat_iv;
#ifdef CONFIG_BATMAN_ADV_BATMAN_V
struct batadv_hard_iface_bat_v bat_v;
#endif
struct work_struct cleanup_work;
struct dentry *debug_dir;
struct hlist_head neigh_list;
@@ -125,10 +161,11 @@ struct batadv_hard_iface {
/**
* struct batadv_orig_ifinfo - originator info per outgoing interface
* @list: list node for orig_node::ifinfo_list
* @if_outgoing: pointer to outgoing hard interface
* @if_outgoing: pointer to outgoing hard-interface
* @router: router that should be used to reach this originator
* @last_real_seqno: last and best known sequence number
* @last_ttl: ttl of last received packet
* @last_seqno_forwarded: seqno of the OGM which was forwarded last
* @batman_seqno_reset: time when the batman seqno window was reset
* @refcount: number of contexts the object is used
* @rcu: struct used for freeing in an RCU-safe manner
@@ -139,8 +176,9 @@ struct batadv_orig_ifinfo {
struct batadv_neigh_node __rcu *router; /* rcu protected pointer */
u32 last_real_seqno;
u8 last_ttl;
u32 last_seqno_forwarded;
unsigned long batman_seqno_reset;
atomic_t refcount;
struct kref refcount;
struct rcu_head rcu;
};
@@ -196,13 +234,13 @@ struct batadv_orig_node_vlan {
unsigned short vid;
struct batadv_vlan_tt tt;
struct hlist_node list;
atomic_t refcount;
struct kref refcount;
struct rcu_head rcu;
};
/**
* struct batadv_orig_bat_iv - B.A.T.M.A.N. IV private orig_node members
* @bcast_own: set of bitfields (one per hard interface) where each one counts
* @bcast_own: set of bitfields (one per hard-interface) where each one counts
* the number of our OGMs this orig_node rebroadcasted "back" to us (relative
* to last_real_seqno). Every bitfield is BATADV_TQ_LOCAL_WINDOW_SIZE bits long.
* @bcast_own_sum: sum of bcast_own
@@ -298,7 +336,7 @@ struct batadv_orig_node {
struct batadv_priv *bat_priv;
/* bcast_seqno_lock protects: bcast_bits & last_bcast_seqno */
spinlock_t bcast_seqno_lock;
atomic_t refcount;
struct kref refcount;
struct rcu_head rcu;
#ifdef CONFIG_BATMAN_ADV_NC
struct list_head in_coding_list;
@@ -341,15 +379,36 @@ struct batadv_gw_node {
struct batadv_orig_node *orig_node;
u32 bandwidth_down;
u32 bandwidth_up;
atomic_t refcount;
struct kref refcount;
struct rcu_head rcu;
};
DECLARE_EWMA(throughput, 1024, 8)
/**
* batadv_hardif_neigh_node - unique neighbor per hard interface
* struct batadv_hardif_neigh_node_bat_v - B.A.T.M.A.N. V private neighbor
* information
* @throughput: ewma link throughput towards this neighbor
* @elp_interval: time interval between two ELP transmissions
* @elp_latest_seqno: latest and best known ELP sequence number
* @last_unicast_tx: when the last unicast packet has been sent to this neighbor
* @metric_work: work queue callback item for metric update
*/
struct batadv_hardif_neigh_node_bat_v {
struct ewma_throughput throughput;
u32 elp_interval;
u32 elp_latest_seqno;
unsigned long last_unicast_tx;
struct work_struct metric_work;
};
/**
* struct batadv_hardif_neigh_node - unique neighbor per hard-interface
* @list: list node for batadv_hard_iface::neigh_list
* @addr: the MAC address of the neighboring interface
* @if_incoming: pointer to incoming hard interface
* @if_incoming: pointer to incoming hard-interface
* @last_seen: when last packet via this neighbor was received
* @bat_v: B.A.T.M.A.N. V private data
* @refcount: number of contexts the object is used
* @rcu: struct used for freeing in a RCU-safe manner
*/
@@ -358,7 +417,10 @@ struct batadv_hardif_neigh_node {
u8 addr[ETH_ALEN];
struct batadv_hard_iface *if_incoming;
unsigned long last_seen;
atomic_t refcount;
#ifdef CONFIG_BATMAN_ADV_BATMAN_V
struct batadv_hardif_neigh_node_bat_v bat_v;
#endif
struct kref refcount;
struct rcu_head rcu;
};
@@ -369,7 +431,7 @@ struct batadv_hardif_neigh_node {
* @addr: the MAC address of the neighboring interface
* @ifinfo_list: list for routing metrics per outgoing interface
* @ifinfo_lock: lock protecting private ifinfo members and list
* @if_incoming: pointer to incoming hard interface
* @if_incoming: pointer to incoming hard-interface
* @last_seen: when last packet via this neighbor was received
* @refcount: number of contexts the object is used
* @rcu: struct used for freeing in an RCU-safe manner
@@ -382,13 +444,13 @@ struct batadv_neigh_node {
spinlock_t ifinfo_lock; /* protects ifinfo_list and its members */
struct batadv_hard_iface *if_incoming;
unsigned long last_seen;
atomic_t refcount;
struct kref refcount;
struct rcu_head rcu;
};
/**
* struct batadv_neigh_ifinfo_bat_iv - neighbor information per outgoing
* interface for BATMAN IV
* interface for B.A.T.M.A.N. IV
* @tq_recv: ring buffer of received TQ values from this neigh node
* @tq_index: ring buffer index
* @tq_avg: averaged tq of all tq values in the ring buffer (tq_recv)
@@ -404,11 +466,23 @@ struct batadv_neigh_ifinfo_bat_iv {
u8 real_packet_count;
};
/**
* struct batadv_neigh_ifinfo_bat_v - neighbor information per outgoing
* interface for B.A.T.M.A.N. V
* @throughput: last throughput metric received from originator via this neigh
* @last_seqno: last sequence number known for this neighbor
*/
struct batadv_neigh_ifinfo_bat_v {
u32 throughput;
u32 last_seqno;
};
/**
* struct batadv_neigh_ifinfo - neighbor information per outgoing interface
* @list: list node for batadv_neigh_node::ifinfo_list
* @if_outgoing: pointer to outgoing hard interface
* @if_outgoing: pointer to outgoing hard-interface
* @bat_iv: B.A.T.M.A.N. IV private structure
* @bat_v: B.A.T.M.A.N. V private data
* @last_ttl: last received ttl from this neigh node
* @refcount: number of contexts the object is used
* @rcu: struct used for freeing in a RCU-safe manner
@@ -417,8 +491,11 @@ struct batadv_neigh_ifinfo {
struct hlist_node list;
struct batadv_hard_iface *if_outgoing;
struct batadv_neigh_ifinfo_bat_iv bat_iv;
#ifdef CONFIG_BATMAN_ADV_BATMAN_V
struct batadv_neigh_ifinfo_bat_v bat_v;
#endif
u8 last_ttl;
atomic_t refcount;
struct kref refcount;
struct rcu_head rcu;
};
@@ -744,10 +821,24 @@ struct batadv_softif_vlan {
atomic_t ap_isolation; /* boolean */
struct batadv_vlan_tt tt;
struct hlist_node list;
atomic_t refcount;
struct kref refcount;
struct rcu_head rcu;
};
/**
* struct batadv_priv_bat_v - B.A.T.M.A.N. V per soft-interface private data
* @ogm_buff: buffer holding the OGM packet
* @ogm_buff_len: length of the OGM packet buffer
* @ogm_seqno: OGM sequence number - used to identify each OGM
* @ogm_wq: workqueue used to schedule OGM transmissions
*/
struct batadv_priv_bat_v {
unsigned char *ogm_buff;
int ogm_buff_len;
atomic_t ogm_seqno;
struct delayed_work ogm_wq;
};
/**
* struct batadv_priv - per mesh interface data
* @mesh_state: current status of the mesh (inactive/active/deactivating)
@@ -771,6 +862,9 @@ struct batadv_softif_vlan {
* @orig_interval: OGM broadcast interval in milliseconds
* @hop_penalty: penalty which will be applied to an OGM's tq-field on every hop
* @log_level: configured log level (see batadv_dbg_level)
* @isolation_mark: the skb->mark value used to match packets for AP isolation
* @isolation_mark_mask: bitmask identifying the bits in skb->mark to be used
* for the isolation mark
* @bcast_seqno: last sent broadcast packet sequence number
* @bcast_queue_left: number of remaining buffered broadcast packet slots
* @batman_queue_left: number of remaining OGM packet slots
@@ -783,8 +877,8 @@ struct batadv_softif_vlan {
* @forw_bat_list_lock: lock protecting forw_bat_list
* @forw_bcast_list_lock: lock protecting forw_bcast_list
* @orig_work: work queue callback item for orig node purging
* @cleanup_work: work queue callback item for soft interface deinit
* @primary_if: one of the hard interfaces assigned to this mesh interface
* @cleanup_work: work queue callback item for soft-interface deinit
* @primary_if: one of the hard-interfaces assigned to this mesh interface
* becomes the primary interface
* @bat_algo_ops: routing algorithm used by this mesh interface
* @softif_vlan_list: a list of softif_vlan structs, one per VLAN created on top
@@ -799,6 +893,7 @@ struct batadv_softif_vlan {
* @mcast: multicast data
* @network_coding: bool indicating whether network coding is enabled
* @nc: network coding data
* @bat_v: B.A.T.M.A.N. V per soft-interface private data
*/
struct batadv_priv {
atomic_t mesh_state;
@@ -864,6 +959,9 @@ struct batadv_priv {
atomic_t network_coding;
struct batadv_priv_nc nc;
#endif /* CONFIG_BATMAN_ADV_NC */
#ifdef CONFIG_BATMAN_ADV_BATMAN_V
struct batadv_priv_bat_v bat_v;
#endif
};
/**
@@ -925,7 +1023,7 @@ struct batadv_bla_backbone_gw {
atomic_t request_sent;
u16 crc;
spinlock_t crc_lock; /* protects crc */
atomic_t refcount;
struct kref refcount;
struct rcu_head rcu;
};
@@ -946,7 +1044,7 @@ struct batadv_bla_claim {
unsigned long lasttime;
struct hlist_node hash_entry;
struct rcu_head rcu;
atomic_t refcount;
struct kref refcount;
};
#endif
@@ -967,7 +1065,7 @@ struct batadv_tt_common_entry {
struct hlist_node hash_entry;
u16 flags;
unsigned long added_at;
atomic_t refcount;
struct kref refcount;
struct rcu_head rcu;
};
@@ -1009,7 +1107,7 @@ struct batadv_tt_orig_list_entry {
struct batadv_orig_node *orig_node;
u8 ttvn;
struct hlist_node list;
atomic_t refcount;
struct kref refcount;
struct rcu_head rcu;
};
@@ -1062,7 +1160,7 @@ struct batadv_tt_roam_node {
struct batadv_nc_node {
struct list_head list;
u8 addr[ETH_ALEN];
atomic_t refcount;
struct kref refcount;
struct rcu_head rcu;
struct batadv_orig_node *orig_node;
unsigned long last_seen;
@@ -1082,7 +1180,7 @@ struct batadv_nc_node {
struct batadv_nc_path {
struct hlist_node hash_entry;
struct rcu_head rcu;
atomic_t refcount;
struct kref refcount;
struct list_head packet_list;
spinlock_t packet_list_lock; /* Protects packet_list */
u8 next_hop[ETH_ALEN];
@@ -1225,7 +1323,7 @@ struct batadv_dat_entry {
unsigned short vid;
unsigned long last_update;
struct hlist_node hash_entry;
atomic_t refcount;
struct kref refcount;
struct rcu_head rcu;
};
@@ -1261,7 +1359,7 @@ struct batadv_dat_candidate {
struct batadv_tvlv_container {
struct hlist_node list;
struct batadv_tvlv_hdr tvlv_hdr;
atomic_t refcount;
struct kref refcount;
};
/**
@@ -1288,7 +1386,7 @@ struct batadv_tvlv_handler {
u8 type;
u8 version;
u8 flags;
atomic_t refcount;
struct kref refcount;
struct rcu_head rcu;
};

View File

@@ -69,6 +69,15 @@ config BT_6LOWPAN
help
IPv6 compression over Bluetooth Low Energy.
config BT_LEDS
bool "Enable LED triggers"
depends on BT
depends on LEDS_CLASS
select LEDS_TRIGGERS
help
This option selects a few LED triggers for different
Bluetooth events.
config BT_SELFTEST
bool "Bluetooth self testing support"
depends on BT && DEBUG_KERNEL

View File

@@ -17,6 +17,7 @@ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
bluetooth-$(CONFIG_BT_BREDR) += sco.o
bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
bluetooth-$(CONFIG_BT_LEDS) += leds.o
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o

View File

@@ -719,6 +719,13 @@ done:
hci_dev_unlock(hdev);
}
static bool conn_use_rpa(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
return hci_dev_test_flag(hdev, HCI_PRIVACY);
}
static void hci_req_add_le_create_conn(struct hci_request *req,
struct hci_conn *conn)
{
@@ -726,14 +733,15 @@ static void hci_req_add_le_create_conn(struct hci_request *req,
struct hci_dev *hdev = conn->hdev;
u8 own_addr_type;
memset(&cp, 0, sizeof(cp));
/* Update random address, but set require_privacy to false so
* that we never connect with an non-resolvable address.
*/
if (hci_update_random_address(req, false, &own_addr_type))
if (hci_update_random_address(req, false, conn_use_rpa(conn),
&own_addr_type))
return;
memset(&cp, 0, sizeof(cp));
/* Set window to be the same value as the interval to enable
* continuous scanning.
*/
@@ -774,7 +782,8 @@ static void hci_req_directed_advertising(struct hci_request *req,
/* Set require_privacy to false so that the remote device has a
* chance of identifying us.
*/
if (hci_update_random_address(req, false, &own_addr_type) < 0)
if (hci_update_random_address(req, false, conn_use_rpa(conn),
&own_addr_type) < 0)
return;
memset(&cp, 0, sizeof(cp));

View File

@@ -40,6 +40,7 @@
#include "hci_request.h"
#include "hci_debugfs.h"
#include "smp.h"
#include "leds.h"
static void hci_rx_work(struct work_struct *work);
static void hci_cmd_work(struct work_struct *work);
@@ -1395,6 +1396,7 @@ static int hci_dev_do_open(struct hci_dev *hdev)
hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
set_bit(HCI_UP, &hdev->flags);
hci_sock_dev_event(hdev, HCI_DEV_UP);
hci_leds_update_powered(hdev, true);
if (!hci_dev_test_flag(hdev, HCI_SETUP) &&
!hci_dev_test_flag(hdev, HCI_CONFIG) &&
!hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
@@ -1532,6 +1534,8 @@ int hci_dev_do_close(struct hci_dev *hdev)
return 0;
}
hci_leds_update_powered(hdev, false);
/* Flush RX and TX works */
flush_work(&hdev->tx_work);
flush_work(&hdev->rx_work);
@@ -2017,6 +2021,7 @@ static void hci_power_on(struct work_struct *work)
if (test_bit(HCI_UP, &hdev->flags) &&
hci_dev_test_flag(hdev, HCI_MGMT) &&
hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
cancel_delayed_work(&hdev->power_off);
hci_req_sync_lock(hdev);
err = __hci_req_hci_power_on(hdev);
hci_req_sync_unlock(hdev);
@@ -3067,6 +3072,8 @@ int hci_register_dev(struct hci_dev *hdev)
if (error < 0)
goto err_wqueue;
hci_leds_init(hdev);
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops,
hdev);

View File

@@ -771,6 +771,11 @@ static u8 update_white_list(struct hci_request *req)
return 0x01;
}
static bool scan_use_rpa(struct hci_dev *hdev)
{
return hci_dev_test_flag(hdev, HCI_PRIVACY);
}
void hci_req_add_le_passive_scan(struct hci_request *req)
{
struct hci_cp_le_set_scan_param param_cp;
@@ -785,7 +790,8 @@ void hci_req_add_le_passive_scan(struct hci_request *req)
* advertising with our address will be correctly reported
* by the controller.
*/
if (hci_update_random_address(req, false, &own_addr_type))
if (hci_update_random_address(req, false, scan_use_rpa(hdev),
&own_addr_type))
return;
/* Adding or removing entries from the white list must
@@ -866,6 +872,11 @@ static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance)
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
flags |= MGMT_ADV_FLAG_CONNECTABLE;
if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE))
flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
else if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
flags |= MGMT_ADV_FLAG_DISCOV;
return flags;
}
@@ -878,6 +889,29 @@ static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance)
return adv_instance->flags;
}
static bool adv_use_rpa(struct hci_dev *hdev, uint32_t flags)
{
/* If privacy is not enabled don't use RPA */
if (!hci_dev_test_flag(hdev, HCI_PRIVACY))
return false;
/* If basic privacy mode is enabled use RPA */
if (!hci_dev_test_flag(hdev, HCI_LIMITED_PRIVACY))
return true;
/* If limited privacy mode is enabled don't use RPA if we're
* both discoverable and bondable.
*/
if ((flags & MGMT_ADV_FLAG_DISCOV) &&
hci_dev_test_flag(hdev, HCI_BONDABLE))
return false;
/* We're neither bondable nor discoverable in the limited
* privacy mode, therefore use RPA.
*/
return true;
}
void __hci_req_enable_advertising(struct hci_request *req)
{
struct hci_dev *hdev = req->hdev;
@@ -911,7 +945,9 @@ void __hci_req_enable_advertising(struct hci_request *req)
* advertising is used. In that case it is fine to use a
* non-resolvable private address.
*/
if (hci_update_random_address(req, !connectable, &own_addr_type) < 0)
if (hci_update_random_address(req, !connectable,
adv_use_rpa(hdev, flags),
&own_addr_type) < 0)
return;
memset(&cp, 0, sizeof(cp));
@@ -1325,7 +1361,7 @@ static void set_random_addr(struct hci_request *req, bdaddr_t *rpa)
}
int hci_update_random_address(struct hci_request *req, bool require_privacy,
u8 *own_addr_type)
bool use_rpa, u8 *own_addr_type)
{
struct hci_dev *hdev = req->hdev;
int err;
@@ -1334,7 +1370,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy,
* current RPA has expired or there is something else than
* the current RPA in use, then generate a new one.
*/
if (hci_dev_test_flag(hdev, HCI_PRIVACY)) {
if (use_rpa) {
int to;
*own_addr_type = ADDR_LE_DEV_RANDOM;
@@ -1596,9 +1632,16 @@ static int discoverable_update(struct hci_request *req, unsigned long opt)
/* Advertising instances don't use the global discoverable setting, so
* only update AD if advertising was enabled using Set Advertising.
*/
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
__hci_req_update_adv_data(req, 0x00);
/* Discoverable mode affects the local advertising
* address in limited privacy mode.
*/
if (hci_dev_test_flag(hdev, HCI_LIMITED_PRIVACY))
__hci_req_enable_advertising(req);
}
hci_dev_unlock(hdev);
return 0;
@@ -1941,7 +1984,8 @@ static int active_scan(struct hci_request *req, unsigned long opt)
* address (when privacy feature has been enabled) or non-resolvable
* private address.
*/
err = hci_update_random_address(req, true, &own_addr_type);
err = hci_update_random_address(req, true, scan_use_rpa(hdev),
&own_addr_type);
if (err < 0)
own_addr_type = ADDR_LE_DEV_PUBLIC;

View File

@@ -89,7 +89,7 @@ static inline void hci_req_update_scan(struct hci_dev *hdev)
void __hci_req_update_scan(struct hci_request *req);
int hci_update_random_address(struct hci_request *req, bool require_privacy,
u8 *own_addr_type);
bool use_rpa, u8 *own_addr_type);
int hci_abort_conn(struct hci_conn *conn, u8 reason);
void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,

74
net/bluetooth/leds.c Normal file
View File

@@ -0,0 +1,74 @@
/*
* Copyright 2015, Heiner Kallweit <hkallweit1@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "leds.h"
struct hci_basic_led_trigger {
struct led_trigger led_trigger;
struct hci_dev *hdev;
};
#define to_hci_basic_led_trigger(arg) container_of(arg, \
struct hci_basic_led_trigger, led_trigger)
void hci_leds_update_powered(struct hci_dev *hdev, bool enabled)
{
if (hdev->power_led)
led_trigger_event(hdev->power_led,
enabled ? LED_FULL : LED_OFF);
}
static void power_activate(struct led_classdev *led_cdev)
{
struct hci_basic_led_trigger *htrig;
bool powered;
htrig = to_hci_basic_led_trigger(led_cdev->trigger);
powered = test_bit(HCI_UP, &htrig->hdev->flags);
led_trigger_event(led_cdev->trigger, powered ? LED_FULL : LED_OFF);
}
static struct led_trigger *led_allocate_basic(struct hci_dev *hdev,
void (*activate)(struct led_classdev *led_cdev),
const char *name)
{
struct hci_basic_led_trigger *htrig;
htrig = devm_kzalloc(&hdev->dev, sizeof(*htrig), GFP_KERNEL);
if (!htrig)
return NULL;
htrig->hdev = hdev;
htrig->led_trigger.activate = activate;
htrig->led_trigger.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
"%s-%s", hdev->name,
name);
if (!htrig->led_trigger.name)
goto err_alloc;
if (devm_led_trigger_register(&hdev->dev, &htrig->led_trigger))
goto err_register;
return &htrig->led_trigger;
err_register:
devm_kfree(&hdev->dev, (void *)htrig->led_trigger.name);
err_alloc:
devm_kfree(&hdev->dev, htrig);
return NULL;
}
void hci_leds_init(struct hci_dev *hdev)
{
/* initialize power_led */
hdev->power_led = led_allocate_basic(hdev, power_activate, "power");
}

16
net/bluetooth/leds.h Normal file
View File

@@ -0,0 +1,16 @@
/*
* Copyright 2015, Heiner Kallweit <hkallweit1@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#if IS_ENABLED(CONFIG_BT_LEDS)
void hci_leds_update_powered(struct hci_dev *hdev, bool enabled);
void hci_leds_init(struct hci_dev *hdev);
#else
static inline void hci_leds_update_powered(struct hci_dev *hdev,
bool enabled) {}
static inline void hci_leds_init(struct hci_dev *hdev) {}
#endif

View File

@@ -38,7 +38,7 @@
#include "mgmt_util.h"
#define MGMT_VERSION 1
#define MGMT_REVISION 11
#define MGMT_REVISION 12
static const u16 mgmt_commands[] = {
MGMT_OP_READ_INDEX_LIST,
@@ -1382,8 +1382,19 @@ static int set_bondable(struct sock *sk, struct hci_dev *hdev, void *data,
if (err < 0)
goto unlock;
if (changed)
if (changed) {
/* In limited privacy mode the change of bondable mode
* may affect the local advertising address.
*/
if (hdev_is_powered(hdev) &&
hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
hci_dev_test_flag(hdev, HCI_DISCOVERABLE) &&
hci_dev_test_flag(hdev, HCI_LIMITED_PRIVACY))
queue_work(hdev->req_workqueue,
&hdev->discoverable_update);
err = new_settings(hdev, sk);
}
unlock:
hci_dev_unlock(hdev);
@@ -4423,7 +4434,7 @@ static int set_privacy(struct sock *sk, struct hci_dev *hdev, void *cp_data,
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
MGMT_STATUS_NOT_SUPPORTED);
if (cp->privacy != 0x00 && cp->privacy != 0x01)
if (cp->privacy != 0x00 && cp->privacy != 0x01 && cp->privacy != 0x02)
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
MGMT_STATUS_INVALID_PARAMS);
@@ -4442,10 +4453,15 @@ static int set_privacy(struct sock *sk, struct hci_dev *hdev, void *cp_data,
changed = !hci_dev_test_and_set_flag(hdev, HCI_PRIVACY);
memcpy(hdev->irk, cp->irk, sizeof(hdev->irk));
hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
if (cp->privacy == 0x02)
hci_dev_set_flag(hdev, HCI_LIMITED_PRIVACY);
else
hci_dev_clear_flag(hdev, HCI_LIMITED_PRIVACY);
} else {
changed = hci_dev_test_and_clear_flag(hdev, HCI_PRIVACY);
memset(hdev->irk, 0, sizeof(hdev->irk));
hci_dev_clear_flag(hdev, HCI_RPA_EXPIRED);
hci_dev_clear_flag(hdev, HCI_LIMITED_PRIVACY);
}
err = send_settings_rsp(sk, MGMT_OP_SET_PRIVACY, hdev);
@@ -5979,6 +5995,10 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
MGMT_STATUS_INVALID_PARAMS);
if (data_len != sizeof(*cp) + cp->adv_data_len + cp->scan_rsp_len)
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
MGMT_STATUS_INVALID_PARAMS);
flags = __le32_to_cpu(cp->flags);
timeout = __le16_to_cpu(cp->timeout);
duration = __le16_to_cpu(cp->duration);

View File

@@ -21,9 +21,10 @@
*/
#include <linux/debugfs.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <crypto/b128ops.h>
#include <crypto/hash.h>
#include <crypto/skcipher.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -87,8 +88,8 @@ struct smp_dev {
u8 min_key_size;
u8 max_key_size;
struct crypto_blkcipher *tfm_aes;
struct crypto_hash *tfm_cmac;
struct crypto_skcipher *tfm_aes;
struct crypto_shash *tfm_cmac;
};
struct smp_chan {
@@ -126,8 +127,8 @@ struct smp_chan {
u8 dhkey[32];
u8 mackey[16];
struct crypto_blkcipher *tfm_aes;
struct crypto_hash *tfm_cmac;
struct crypto_skcipher *tfm_aes;
struct crypto_shash *tfm_cmac;
};
/* These debug key values are defined in the SMP section of the core
@@ -165,12 +166,11 @@ static inline void swap_buf(const u8 *src, u8 *dst, size_t len)
* AES-CMAC, f4, f5, f6, g2 and h6.
*/
static int aes_cmac(struct crypto_hash *tfm, const u8 k[16], const u8 *m,
static int aes_cmac(struct crypto_shash *tfm, const u8 k[16], const u8 *m,
size_t len, u8 mac[16])
{
uint8_t tmp[16], mac_msb[16], msg_msb[CMAC_MSG_MAX];
struct hash_desc desc;
struct scatterlist sg;
SHASH_DESC_ON_STACK(desc, tfm);
int err;
if (len > CMAC_MSG_MAX)
@@ -181,10 +181,8 @@ static int aes_cmac(struct crypto_hash *tfm, const u8 k[16], const u8 *m,
return -EINVAL;
}
desc.tfm = tfm;
desc.flags = 0;
crypto_hash_init(&desc);
desc->tfm = tfm;
desc->flags = 0;
/* Swap key and message from LSB to MSB */
swap_buf(k, tmp, 16);
@@ -193,23 +191,16 @@ static int aes_cmac(struct crypto_hash *tfm, const u8 k[16], const u8 *m,
SMP_DBG("msg (len %zu) %*phN", len, (int) len, m);
SMP_DBG("key %16phN", k);
err = crypto_hash_setkey(tfm, tmp, 16);
err = crypto_shash_setkey(tfm, tmp, 16);
if (err) {
BT_ERR("cipher setkey failed: %d", err);
return err;
}
sg_init_one(&sg, msg_msb, len);
err = crypto_hash_update(&desc, &sg, len);
err = crypto_shash_digest(desc, msg_msb, len, mac_msb);
shash_desc_zero(desc);
if (err) {
BT_ERR("Hash update error %d", err);
return err;
}
err = crypto_hash_final(&desc, mac_msb);
if (err) {
BT_ERR("Hash final error %d", err);
BT_ERR("Hash computation error %d", err);
return err;
}
@@ -220,8 +211,8 @@ static int aes_cmac(struct crypto_hash *tfm, const u8 k[16], const u8 *m,
return 0;
}
static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
const u8 x[16], u8 z, u8 res[16])
static int smp_f4(struct crypto_shash *tfm_cmac, const u8 u[32],
const u8 v[32], const u8 x[16], u8 z, u8 res[16])
{
u8 m[65];
int err;
@@ -243,7 +234,7 @@ static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
return err;
}
static int smp_f5(struct crypto_hash *tfm_cmac, const u8 w[32],
static int smp_f5(struct crypto_shash *tfm_cmac, const u8 w[32],
const u8 n1[16], const u8 n2[16], const u8 a1[7],
const u8 a2[7], u8 mackey[16], u8 ltk[16])
{
@@ -296,7 +287,7 @@ static int smp_f5(struct crypto_hash *tfm_cmac, const u8 w[32],
return 0;
}
static int smp_f6(struct crypto_hash *tfm_cmac, const u8 w[16],
static int smp_f6(struct crypto_shash *tfm_cmac, const u8 w[16],
const u8 n1[16], const u8 n2[16], const u8 r[16],
const u8 io_cap[3], const u8 a1[7], const u8 a2[7],
u8 res[16])
@@ -324,7 +315,7 @@ static int smp_f6(struct crypto_hash *tfm_cmac, const u8 w[16],
return err;
}
static int smp_g2(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
static int smp_g2(struct crypto_shash *tfm_cmac, const u8 u[32], const u8 v[32],
const u8 x[16], const u8 y[16], u32 *val)
{
u8 m[80], tmp[16];
@@ -350,7 +341,7 @@ static int smp_g2(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
return 0;
}
static int smp_h6(struct crypto_hash *tfm_cmac, const u8 w[16],
static int smp_h6(struct crypto_shash *tfm_cmac, const u8 w[16],
const u8 key_id[4], u8 res[16])
{
int err;
@@ -370,9 +361,9 @@ static int smp_h6(struct crypto_hash *tfm_cmac, const u8 w[16],
* s1 and ah.
*/
static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
static int smp_e(struct crypto_skcipher *tfm, const u8 *k, u8 *r)
{
struct blkcipher_desc desc;
SKCIPHER_REQUEST_ON_STACK(req, tfm);
struct scatterlist sg;
uint8_t tmp[16], data[16];
int err;
@@ -384,13 +375,10 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
return -EINVAL;
}
desc.tfm = tfm;
desc.flags = 0;
/* The most significant octet of key corresponds to k[0] */
swap_buf(k, tmp, 16);
err = crypto_blkcipher_setkey(tfm, tmp, 16);
err = crypto_skcipher_setkey(tfm, tmp, 16);
if (err) {
BT_ERR("cipher setkey failed: %d", err);
return err;
@@ -401,7 +389,12 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
sg_init_one(&sg, data, 16);
err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
skcipher_request_set_tfm(req, tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
skcipher_request_set_crypt(req, &sg, &sg, 16, NULL);
err = crypto_skcipher_encrypt(req);
skcipher_request_zero(req);
if (err)
BT_ERR("Encrypt data error %d", err);
@@ -413,7 +406,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
return err;
}
static int smp_c1(struct crypto_blkcipher *tfm_aes, const u8 k[16],
static int smp_c1(struct crypto_skcipher *tfm_aes, const u8 k[16],
const u8 r[16], const u8 preq[7], const u8 pres[7], u8 _iat,
const bdaddr_t *ia, u8 _rat, const bdaddr_t *ra, u8 res[16])
{
@@ -462,7 +455,7 @@ static int smp_c1(struct crypto_blkcipher *tfm_aes, const u8 k[16],
return err;
}
static int smp_s1(struct crypto_blkcipher *tfm_aes, const u8 k[16],
static int smp_s1(struct crypto_skcipher *tfm_aes, const u8 k[16],
const u8 r1[16], const u8 r2[16], u8 _r[16])
{
int err;
@@ -478,7 +471,7 @@ static int smp_s1(struct crypto_blkcipher *tfm_aes, const u8 k[16],
return err;
}
static int smp_ah(struct crypto_blkcipher *tfm, const u8 irk[16],
static int smp_ah(struct crypto_skcipher *tfm, const u8 irk[16],
const u8 r[3], u8 res[3])
{
u8 _res[16];
@@ -766,8 +759,8 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
kzfree(smp->slave_csrk);
kzfree(smp->link_key);
crypto_free_blkcipher(smp->tfm_aes);
crypto_free_hash(smp->tfm_cmac);
crypto_free_skcipher(smp->tfm_aes);
crypto_free_shash(smp->tfm_cmac);
/* Ensure that we don't leave any debug key around if debug key
* support hasn't been explicitly enabled.
@@ -1366,17 +1359,17 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
if (!smp)
return NULL;
smp->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
smp->tfm_aes = crypto_alloc_skcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(smp->tfm_aes)) {
BT_ERR("Unable to create ECB crypto context");
kzfree(smp);
return NULL;
}
smp->tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC);
smp->tfm_cmac = crypto_alloc_shash("cmac(aes)", 0, 0);
if (IS_ERR(smp->tfm_cmac)) {
BT_ERR("Unable to create CMAC crypto context");
crypto_free_blkcipher(smp->tfm_aes);
crypto_free_skcipher(smp->tfm_aes);
kzfree(smp);
return NULL;
}
@@ -3127,8 +3120,8 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
{
struct l2cap_chan *chan;
struct smp_dev *smp;
struct crypto_blkcipher *tfm_aes;
struct crypto_hash *tfm_cmac;
struct crypto_skcipher *tfm_aes;
struct crypto_shash *tfm_cmac;
if (cid == L2CAP_CID_SMP_BREDR) {
smp = NULL;
@@ -3139,17 +3132,17 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
if (!smp)
return ERR_PTR(-ENOMEM);
tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
tfm_aes = crypto_alloc_skcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm_aes)) {
BT_ERR("Unable to create ECB crypto context");
kzfree(smp);
return ERR_CAST(tfm_aes);
}
tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC);
tfm_cmac = crypto_alloc_shash("cmac(aes)", 0, 0);
if (IS_ERR(tfm_cmac)) {
BT_ERR("Unable to create CMAC crypto context");
crypto_free_blkcipher(tfm_aes);
crypto_free_skcipher(tfm_aes);
kzfree(smp);
return ERR_CAST(tfm_cmac);
}
@@ -3163,8 +3156,8 @@ create_chan:
chan = l2cap_chan_create();
if (!chan) {
if (smp) {
crypto_free_blkcipher(smp->tfm_aes);
crypto_free_hash(smp->tfm_cmac);
crypto_free_skcipher(smp->tfm_aes);
crypto_free_shash(smp->tfm_cmac);
kzfree(smp);
}
return ERR_PTR(-ENOMEM);
@@ -3210,10 +3203,8 @@ static void smp_del_chan(struct l2cap_chan *chan)
smp = chan->data;
if (smp) {
chan->data = NULL;
if (smp->tfm_aes)
crypto_free_blkcipher(smp->tfm_aes);
if (smp->tfm_cmac)
crypto_free_hash(smp->tfm_cmac);
crypto_free_skcipher(smp->tfm_aes);
crypto_free_shash(smp->tfm_cmac);
kzfree(smp);
}
@@ -3449,7 +3440,7 @@ void smp_unregister(struct hci_dev *hdev)
#if IS_ENABLED(CONFIG_BT_SELFTEST_SMP)
static int __init test_ah(struct crypto_blkcipher *tfm_aes)
static int __init test_ah(struct crypto_skcipher *tfm_aes)
{
const u8 irk[16] = {
0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
@@ -3469,7 +3460,7 @@ static int __init test_ah(struct crypto_blkcipher *tfm_aes)
return 0;
}
static int __init test_c1(struct crypto_blkcipher *tfm_aes)
static int __init test_c1(struct crypto_skcipher *tfm_aes)
{
const u8 k[16] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -3499,7 +3490,7 @@ static int __init test_c1(struct crypto_blkcipher *tfm_aes)
return 0;
}
static int __init test_s1(struct crypto_blkcipher *tfm_aes)
static int __init test_s1(struct crypto_skcipher *tfm_aes)
{
const u8 k[16] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -3524,7 +3515,7 @@ static int __init test_s1(struct crypto_blkcipher *tfm_aes)
return 0;
}
static int __init test_f4(struct crypto_hash *tfm_cmac)
static int __init test_f4(struct crypto_shash *tfm_cmac)
{
const u8 u[32] = {
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
@@ -3556,7 +3547,7 @@ static int __init test_f4(struct crypto_hash *tfm_cmac)
return 0;
}
static int __init test_f5(struct crypto_hash *tfm_cmac)
static int __init test_f5(struct crypto_shash *tfm_cmac)
{
const u8 w[32] = {
0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86,
@@ -3593,7 +3584,7 @@ static int __init test_f5(struct crypto_hash *tfm_cmac)
return 0;
}
static int __init test_f6(struct crypto_hash *tfm_cmac)
static int __init test_f6(struct crypto_shash *tfm_cmac)
{
const u8 w[16] = {
0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, 0xfd,
@@ -3626,7 +3617,7 @@ static int __init test_f6(struct crypto_hash *tfm_cmac)
return 0;
}
static int __init test_g2(struct crypto_hash *tfm_cmac)
static int __init test_g2(struct crypto_shash *tfm_cmac)
{
const u8 u[32] = {
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
@@ -3658,7 +3649,7 @@ static int __init test_g2(struct crypto_hash *tfm_cmac)
return 0;
}
static int __init test_h6(struct crypto_hash *tfm_cmac)
static int __init test_h6(struct crypto_shash *tfm_cmac)
{
const u8 w[16] = {
0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
@@ -3695,8 +3686,8 @@ static const struct file_operations test_smp_fops = {
.llseek = default_llseek,
};
static int __init run_selftests(struct crypto_blkcipher *tfm_aes,
struct crypto_hash *tfm_cmac)
static int __init run_selftests(struct crypto_skcipher *tfm_aes,
struct crypto_shash *tfm_cmac)
{
ktime_t calltime, delta, rettime;
unsigned long long duration;
@@ -3773,27 +3764,27 @@ done:
int __init bt_selftest_smp(void)
{
struct crypto_blkcipher *tfm_aes;
struct crypto_hash *tfm_cmac;
struct crypto_skcipher *tfm_aes;
struct crypto_shash *tfm_cmac;
int err;
tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
tfm_aes = crypto_alloc_skcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm_aes)) {
BT_ERR("Unable to create ECB crypto context");
return PTR_ERR(tfm_aes);
}
tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC);
tfm_cmac = crypto_alloc_shash("cmac(aes)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm_cmac)) {
BT_ERR("Unable to create CMAC crypto context");
crypto_free_blkcipher(tfm_aes);
crypto_free_skcipher(tfm_aes);
return PTR_ERR(tfm_cmac);
}
err = run_selftests(tfm_aes, tfm_cmac);
crypto_free_hash(tfm_cmac);
crypto_free_blkcipher(tfm_aes);
crypto_free_shash(tfm_cmac);
crypto_free_skcipher(tfm_aes);
return err;
}

View File

@@ -44,7 +44,6 @@ int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb
skb_push(skb, ETH_HLEN);
br_drop_fake_rtable(skb);
skb_sender_cpu_clear(skb);
if (skb->ip_summed == CHECKSUM_PARTIAL &&
(skb->protocol == htons(ETH_P_8021Q) ||

View File

@@ -36,10 +36,10 @@
*/
static int port_cost(struct net_device *dev)
{
struct ethtool_cmd ecmd;
struct ethtool_link_ksettings ecmd;
if (!__ethtool_get_settings(dev, &ecmd)) {
switch (ethtool_cmd_speed(&ecmd)) {
if (!__ethtool_get_link_ksettings(dev, &ecmd)) {
switch (ecmd.base.speed) {
case SPEED_10000:
return 2;
case SPEED_1000:
@@ -223,6 +223,31 @@ static void destroy_nbp_rcu(struct rcu_head *head)
destroy_nbp(p);
}
static unsigned get_max_headroom(struct net_bridge *br)
{
unsigned max_headroom = 0;
struct net_bridge_port *p;
list_for_each_entry(p, &br->port_list, list) {
unsigned dev_headroom = netdev_get_fwd_headroom(p->dev);
if (dev_headroom > max_headroom)
max_headroom = dev_headroom;
}
return max_headroom;
}
static void update_headroom(struct net_bridge *br, int new_hr)
{
struct net_bridge_port *p;
list_for_each_entry(p, &br->port_list, list)
netdev_set_rx_headroom(p->dev, new_hr);
br->dev->needed_headroom = new_hr;
}
/* Delete port(interface) from bridge is done in two steps.
* via RCU. First step, marks device as down. That deletes
* all the timers and stops new packets from flowing through.
@@ -248,6 +273,9 @@ static void del_nbp(struct net_bridge_port *p)
br_ifinfo_notify(RTM_DELLINK, p);
list_del_rcu(&p->list);
if (netdev_get_fwd_headroom(dev) == br->dev->needed_headroom)
update_headroom(br, get_max_headroom(br));
netdev_reset_rx_headroom(dev);
nbp_vlan_flush(p);
br_fdb_delete_by_port(br, p, 0, 1);
@@ -438,6 +466,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
{
struct net_bridge_port *p;
int err = 0;
unsigned br_hr, dev_hr;
bool changed_addr;
/* Don't allow bridging non-ethernet like devices, or DSA-enabled
@@ -505,8 +534,12 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
netdev_update_features(br->dev);
if (br->dev->needed_headroom < dev->needed_headroom)
br->dev->needed_headroom = dev->needed_headroom;
br_hr = br->dev->needed_headroom;
dev_hr = netdev_get_fwd_headroom(dev);
if (br_hr < dev_hr)
update_headroom(br, dev_hr);
else
netdev_set_rx_headroom(dev, br_hr);
if (br_fdb_insert(br, p, dev->dev_addr, 0))
netdev_err(dev, "failed insert local address bridge forwarding table\n");

View File

@@ -222,7 +222,10 @@ static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_bu
/* check if vlan is allowed, to avoid spoofing */
if (p->flags & BR_LEARNING && br_should_learn(p, skb, &vid))
br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, false);
return 0; /* process further */
BR_INPUT_SKB_CB(skb)->brdev = p->br->dev;
br_pass_frame_up(skb);
return 0;
}
/*
@@ -284,14 +287,9 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
}
/* Deliver packet to local host only */
if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
dev_net(skb->dev), NULL, skb, skb->dev, NULL,
br_handle_local_finish)) {
return RX_HANDLER_CONSUMED; /* consumed by filter */
} else {
*pskb = skb;
return RX_HANDLER_PASS; /* continue processing */
}
NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, dev_net(skb->dev),
NULL, skb, skb->dev, NULL, br_handle_local_finish);
return RX_HANDLER_CONSUMED;
}
forward:

View File

@@ -20,7 +20,7 @@ static int br_rports_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
{
struct net_bridge *br = netdev_priv(dev);
struct net_bridge_port *p;
struct nlattr *nest;
struct nlattr *nest, *port_nest;
if (!br->multicast_router || hlist_empty(&br->router_list))
return 0;
@@ -30,8 +30,20 @@ static int br_rports_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
return -EMSGSIZE;
hlist_for_each_entry_rcu(p, &br->router_list, rlist) {
if (p && nla_put_u32(skb, MDBA_ROUTER_PORT, p->dev->ifindex))
if (!p)
continue;
port_nest = nla_nest_start(skb, MDBA_ROUTER_PORT);
if (!port_nest)
goto fail;
if (nla_put_nohdr(skb, sizeof(u32), &p->dev->ifindex) ||
nla_put_u32(skb, MDBA_ROUTER_PATTR_TIMER,
br_timer_value(&p->multicast_router_timer)) ||
nla_put_u8(skb, MDBA_ROUTER_PATTR_TYPE,
p->multicast_router)) {
nla_nest_cancel(skb, port_nest);
goto fail;
}
nla_nest_end(skb, port_nest);
}
nla_nest_end(skb, nest);
@@ -41,6 +53,14 @@ fail:
return -EMSGSIZE;
}
static void __mdb_entry_fill_flags(struct br_mdb_entry *e, unsigned char flags)
{
e->state = flags & MDB_PG_FLAGS_PERMANENT;
e->flags = 0;
if (flags & MDB_PG_FLAGS_OFFLOAD)
e->flags |= MDB_FLAGS_OFFLOAD;
}
static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *dev)
{
@@ -80,26 +100,41 @@ static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
for (pp = &mp->ports;
(p = rcu_dereference(*pp)) != NULL;
pp = &p->next) {
struct nlattr *nest_ent;
struct br_mdb_entry e;
port = p->port;
if (port) {
struct br_mdb_entry e;
memset(&e, 0, sizeof(e));
e.ifindex = port->dev->ifindex;
e.state = p->state;
e.vid = p->addr.vid;
if (p->addr.proto == htons(ETH_P_IP))
e.addr.u.ip4 = p->addr.u.ip4;
if (!port)
continue;
memset(&e, 0, sizeof(e));
e.ifindex = port->dev->ifindex;
e.vid = p->addr.vid;
__mdb_entry_fill_flags(&e, p->flags);
if (p->addr.proto == htons(ETH_P_IP))
e.addr.u.ip4 = p->addr.u.ip4;
#if IS_ENABLED(CONFIG_IPV6)
if (p->addr.proto == htons(ETH_P_IPV6))
e.addr.u.ip6 = p->addr.u.ip6;
if (p->addr.proto == htons(ETH_P_IPV6))
e.addr.u.ip6 = p->addr.u.ip6;
#endif
e.addr.proto = p->addr.proto;
if (nla_put(skb, MDBA_MDB_ENTRY_INFO, sizeof(e), &e)) {
nla_nest_cancel(skb, nest2);
err = -EMSGSIZE;
goto out;
}
e.addr.proto = p->addr.proto;
nest_ent = nla_nest_start(skb,
MDBA_MDB_ENTRY_INFO);
if (!nest_ent) {
nla_nest_cancel(skb, nest2);
err = -EMSGSIZE;
goto out;
}
if (nla_put_nohdr(skb, sizeof(e), &e) ||
nla_put_u32(skb,
MDBA_MDB_EATTR_TIMER,
br_timer_value(&p->timer))) {
nla_nest_cancel(skb, nest_ent);
nla_nest_cancel(skb, nest2);
err = -EMSGSIZE;
goto out;
}
nla_nest_end(skb, nest_ent);
}
nla_nest_end(skb, nest2);
skip:
@@ -209,7 +244,7 @@ static inline size_t rtnl_mdb_nlmsg_size(void)
}
static void __br_mdb_notify(struct net_device *dev, struct br_mdb_entry *entry,
int type)
int type, struct net_bridge_port_group *pg)
{
struct switchdev_obj_port_mdb mdb = {
.obj = {
@@ -232,10 +267,13 @@ static void __br_mdb_notify(struct net_device *dev, struct br_mdb_entry *entry,
#endif
mdb.obj.orig_dev = port_dev;
if (port_dev && type == RTM_NEWMDB)
switchdev_port_obj_add(port_dev, &mdb.obj);
else if (port_dev && type == RTM_DELMDB)
if (port_dev && type == RTM_NEWMDB) {
err = switchdev_port_obj_add(port_dev, &mdb.obj);
if (!err && pg)
pg->flags |= MDB_PG_FLAGS_OFFLOAD;
} else if (port_dev && type == RTM_DELMDB) {
switchdev_port_obj_del(port_dev, &mdb.obj);
}
skb = nlmsg_new(rtnl_mdb_nlmsg_size(), GFP_ATOMIC);
if (!skb)
@@ -253,21 +291,21 @@ errout:
rtnl_set_sk_err(net, RTNLGRP_MDB, err);
}
void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
struct br_ip *group, int type, u8 state)
void br_mdb_notify(struct net_device *dev, struct net_bridge_port_group *pg,
int type)
{
struct br_mdb_entry entry;
memset(&entry, 0, sizeof(entry));
entry.ifindex = port->dev->ifindex;
entry.addr.proto = group->proto;
entry.addr.u.ip4 = group->u.ip4;
entry.ifindex = pg->port->dev->ifindex;
entry.addr.proto = pg->addr.proto;
entry.addr.u.ip4 = pg->addr.u.ip4;
#if IS_ENABLED(CONFIG_IPV6)
entry.addr.u.ip6 = group->u.ip6;
entry.addr.u.ip6 = pg->addr.u.ip6;
#endif
entry.state = state;
entry.vid = group->vid;
__br_mdb_notify(dev, &entry, type);
entry.vid = pg->addr.vid;
__mdb_entry_fill_flags(&entry, pg->flags);
__br_mdb_notify(dev, &entry, type, pg);
}
static int nlmsg_populate_rtr_fill(struct sk_buff *skb,
@@ -412,7 +450,8 @@ static int br_mdb_parse(struct sk_buff *skb, struct nlmsghdr *nlh,
}
static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
struct br_ip *group, unsigned char state)
struct br_ip *group, unsigned char state,
struct net_bridge_port_group **pg)
{
struct net_bridge_mdb_entry *mp;
struct net_bridge_port_group *p;
@@ -443,6 +482,7 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
if (unlikely(!p))
return -ENOMEM;
rcu_assign_pointer(*pp, p);
*pg = p;
if (state == MDB_TEMPORARY)
mod_timer(&p->timer, now + br->multicast_membership_interval);
@@ -450,7 +490,8 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
}
static int __br_mdb_add(struct net *net, struct net_bridge *br,
struct br_mdb_entry *entry)
struct br_mdb_entry *entry,
struct net_bridge_port_group **pg)
{
struct br_ip ip;
struct net_device *dev;
@@ -479,7 +520,7 @@ static int __br_mdb_add(struct net *net, struct net_bridge *br,
#endif
spin_lock_bh(&br->multicast_lock);
ret = br_mdb_add_group(br, p, &ip, entry->state);
ret = br_mdb_add_group(br, p, &ip, entry->state, pg);
spin_unlock_bh(&br->multicast_lock);
return ret;
}
@@ -487,6 +528,7 @@ static int __br_mdb_add(struct net *net, struct net_bridge *br,
static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
{
struct net *net = sock_net(skb->sk);
struct net_bridge_port_group *pg;
struct net_bridge_vlan_group *vg;
struct net_device *dev, *pdev;
struct br_mdb_entry *entry;
@@ -516,15 +558,15 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
if (br_vlan_enabled(br) && vg && entry->vid == 0) {
list_for_each_entry(v, &vg->vlan_list, vlist) {
entry->vid = v->vid;
err = __br_mdb_add(net, br, entry);
err = __br_mdb_add(net, br, entry, &pg);
if (err)
break;
__br_mdb_notify(dev, entry, RTM_NEWMDB);
__br_mdb_notify(dev, entry, RTM_NEWMDB, pg);
}
} else {
err = __br_mdb_add(net, br, entry);
err = __br_mdb_add(net, br, entry, &pg);
if (!err)
__br_mdb_notify(dev, entry, RTM_NEWMDB);
__br_mdb_notify(dev, entry, RTM_NEWMDB, pg);
}
return err;
@@ -568,7 +610,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
if (p->port->state == BR_STATE_DISABLED)
goto unlock;
entry->state = p->state;
__mdb_entry_fill_flags(entry, p->flags);
rcu_assign_pointer(*pp, p->next);
hlist_del_init(&p->mglist);
del_timer(&p->timer);
@@ -620,12 +662,12 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
entry->vid = v->vid;
err = __br_mdb_del(br, entry);
if (!err)
__br_mdb_notify(dev, entry, RTM_DELMDB);
__br_mdb_notify(dev, entry, RTM_DELMDB, NULL);
}
} else {
err = __br_mdb_del(br, entry);
if (!err)
__br_mdb_notify(dev, entry, RTM_DELMDB);
__br_mdb_notify(dev, entry, RTM_DELMDB, NULL);
}
return err;

View File

@@ -283,8 +283,7 @@ static void br_multicast_del_pg(struct net_bridge *br,
rcu_assign_pointer(*pp, p->next);
hlist_del_init(&p->mglist);
del_timer(&p->timer);
br_mdb_notify(br->dev, p->port, &pg->addr, RTM_DELMDB,
p->state);
br_mdb_notify(br->dev, p, RTM_DELMDB);
call_rcu_bh(&p->rcu, br_multicast_free_pg);
if (!mp->ports && !mp->mglist &&
@@ -304,7 +303,7 @@ static void br_multicast_port_group_expired(unsigned long data)
spin_lock(&br->multicast_lock);
if (!netif_running(br->dev) || timer_pending(&pg->timer) ||
hlist_unhashed(&pg->mglist) || pg->state & MDB_PERMANENT)
hlist_unhashed(&pg->mglist) || pg->flags & MDB_PG_FLAGS_PERMANENT)
goto out;
br_multicast_del_pg(br, pg);
@@ -649,7 +648,7 @@ struct net_bridge_port_group *br_multicast_new_port_group(
struct net_bridge_port *port,
struct br_ip *group,
struct net_bridge_port_group __rcu *next,
unsigned char state)
unsigned char flags)
{
struct net_bridge_port_group *p;
@@ -659,7 +658,7 @@ struct net_bridge_port_group *br_multicast_new_port_group(
p->addr = *group;
p->port = port;
p->state = state;
p->flags = flags;
rcu_assign_pointer(p->next, next);
hlist_add_head(&p->mglist, &port->mglist);
setup_timer(&p->timer, br_multicast_port_group_expired,
@@ -702,11 +701,11 @@ static int br_multicast_add_group(struct net_bridge *br,
break;
}
p = br_multicast_new_port_group(port, group, *pp, MDB_TEMPORARY);
p = br_multicast_new_port_group(port, group, *pp, 0);
if (unlikely(!p))
goto err;
rcu_assign_pointer(*pp, p);
br_mdb_notify(br->dev, port, group, RTM_NEWMDB, MDB_TEMPORARY);
br_mdb_notify(br->dev, p, RTM_NEWMDB);
found:
mod_timer(&p->timer, now + br->multicast_membership_interval);
@@ -760,13 +759,17 @@ static void br_multicast_router_expired(unsigned long data)
struct net_bridge *br = port->br;
spin_lock(&br->multicast_lock);
if (port->multicast_router != 1 ||
if (port->multicast_router == MDB_RTR_TYPE_DISABLED ||
port->multicast_router == MDB_RTR_TYPE_PERM ||
timer_pending(&port->multicast_router_timer) ||
hlist_unhashed(&port->rlist))
goto out;
hlist_del_init_rcu(&port->rlist);
br_rtr_notify(br->dev, port, RTM_DELMDB);
/* Don't allow timer refresh if the router expired */
if (port->multicast_router == MDB_RTR_TYPE_TEMP)
port->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
out:
spin_unlock(&br->multicast_lock);
@@ -913,7 +916,7 @@ static void br_ip6_multicast_port_query_expired(unsigned long data)
void br_multicast_add_port(struct net_bridge_port *port)
{
port->multicast_router = 1;
port->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
setup_timer(&port->multicast_router_timer, br_multicast_router_expired,
(unsigned long)port);
@@ -960,7 +963,8 @@ void br_multicast_enable_port(struct net_bridge_port *port)
#if IS_ENABLED(CONFIG_IPV6)
br_multicast_enable(&port->ip6_own_query);
#endif
if (port->multicast_router == 2 && hlist_unhashed(&port->rlist))
if (port->multicast_router == MDB_RTR_TYPE_PERM &&
hlist_unhashed(&port->rlist))
br_multicast_add_router(br, port);
out:
@@ -975,12 +979,15 @@ void br_multicast_disable_port(struct net_bridge_port *port)
spin_lock(&br->multicast_lock);
hlist_for_each_entry_safe(pg, n, &port->mglist, mglist)
if (pg->state == MDB_TEMPORARY)
if (!(pg->flags & MDB_PG_FLAGS_PERMANENT))
br_multicast_del_pg(br, pg);
if (!hlist_unhashed(&port->rlist)) {
hlist_del_init_rcu(&port->rlist);
br_rtr_notify(br->dev, port, RTM_DELMDB);
/* Don't allow timer refresh if disabling */
if (port->multicast_router == MDB_RTR_TYPE_TEMP)
port->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
}
del_timer(&port->multicast_router_timer);
del_timer(&port->ip4_own_query.timer);
@@ -1228,13 +1235,14 @@ static void br_multicast_mark_router(struct net_bridge *br,
unsigned long now = jiffies;
if (!port) {
if (br->multicast_router == 1)
if (br->multicast_router == MDB_RTR_TYPE_TEMP_QUERY)
mod_timer(&br->multicast_router_timer,
now + br->multicast_querier_interval);
return;
}
if (port->multicast_router != 1)
if (port->multicast_router == MDB_RTR_TYPE_DISABLED ||
port->multicast_router == MDB_RTR_TYPE_PERM)
return;
br_multicast_add_router(br, port);
@@ -1453,8 +1461,7 @@ br_multicast_leave_group(struct net_bridge *br,
hlist_del_init(&p->mglist);
del_timer(&p->timer);
call_rcu_bh(&p->rcu, br_multicast_free_pg);
br_mdb_notify(br->dev, port, group, RTM_DELMDB,
p->state);
br_mdb_notify(br->dev, p, RTM_DELMDB);
if (!mp->ports && !mp->mglist &&
netif_running(br->dev))
@@ -1715,7 +1722,7 @@ void br_multicast_init(struct net_bridge *br)
br->hash_elasticity = 4;
br->hash_max = 512;
br->multicast_router = 1;
br->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
br->multicast_querier = 0;
br->multicast_query_use_ifaddr = 0;
br->multicast_last_member_count = 2;
@@ -1825,11 +1832,11 @@ int br_multicast_set_router(struct net_bridge *br, unsigned long val)
spin_lock_bh(&br->multicast_lock);
switch (val) {
case 0:
case 2:
case MDB_RTR_TYPE_DISABLED:
case MDB_RTR_TYPE_PERM:
del_timer(&br->multicast_router_timer);
/* fall through */
case 1:
case MDB_RTR_TYPE_TEMP_QUERY:
br->multicast_router = val;
err = 0;
break;
@@ -1840,37 +1847,53 @@ int br_multicast_set_router(struct net_bridge *br, unsigned long val)
return err;
}
static void __del_port_router(struct net_bridge_port *p)
{
if (hlist_unhashed(&p->rlist))
return;
hlist_del_init_rcu(&p->rlist);
br_rtr_notify(p->br->dev, p, RTM_DELMDB);
}
int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val)
{
struct net_bridge *br = p->br;
unsigned long now = jiffies;
int err = -EINVAL;
spin_lock(&br->multicast_lock);
switch (val) {
case 0:
case 1:
case 2:
p->multicast_router = val;
if (p->multicast_router == val) {
/* Refresh the temp router port timer */
if (p->multicast_router == MDB_RTR_TYPE_TEMP)
mod_timer(&p->multicast_router_timer,
now + br->multicast_querier_interval);
err = 0;
if (val < 2 && !hlist_unhashed(&p->rlist)) {
hlist_del_init_rcu(&p->rlist);
br_rtr_notify(br->dev, p, RTM_DELMDB);
}
if (val == 1)
break;
goto unlock;
}
switch (val) {
case MDB_RTR_TYPE_DISABLED:
p->multicast_router = MDB_RTR_TYPE_DISABLED;
__del_port_router(p);
del_timer(&p->multicast_router_timer);
break;
case MDB_RTR_TYPE_TEMP_QUERY:
p->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
__del_port_router(p);
break;
case MDB_RTR_TYPE_PERM:
p->multicast_router = MDB_RTR_TYPE_PERM;
del_timer(&p->multicast_router_timer);
if (val == 0)
break;
br_multicast_add_router(br, p);
break;
case MDB_RTR_TYPE_TEMP:
p->multicast_router = MDB_RTR_TYPE_TEMP;
br_multicast_mark_router(br, p);
break;
default:
goto unlock;
}
err = 0;
unlock:
spin_unlock(&br->multicast_lock);
return err;

View File

@@ -37,6 +37,7 @@
#include <net/addrconf.h>
#include <net/route.h>
#include <net/netfilter/br_netfilter.h>
#include <net/netns/generic.h>
#include <asm/uaccess.h>
#include "br_private.h"
@@ -44,6 +45,12 @@
#include <linux/sysctl.h>
#endif
static int brnf_net_id __read_mostly;
struct brnf_net {
bool enabled;
};
#ifdef CONFIG_SYSCTL
static struct ctl_table_header *brnf_sysctl_header;
static int brnf_call_iptables __read_mostly = 1;
@@ -938,6 +945,53 @@ static struct nf_hook_ops br_nf_ops[] __read_mostly = {
},
};
static int brnf_device_event(struct notifier_block *unused, unsigned long event,
void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct brnf_net *brnet;
struct net *net;
int ret;
if (event != NETDEV_REGISTER || !(dev->priv_flags & IFF_EBRIDGE))
return NOTIFY_DONE;
ASSERT_RTNL();
net = dev_net(dev);
brnet = net_generic(net, brnf_net_id);
if (brnet->enabled)
return NOTIFY_OK;
ret = nf_register_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops));
if (ret)
return NOTIFY_BAD;
brnet->enabled = true;
return NOTIFY_OK;
}
static void __net_exit brnf_exit_net(struct net *net)
{
struct brnf_net *brnet = net_generic(net, brnf_net_id);
if (!brnet->enabled)
return;
nf_unregister_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops));
brnet->enabled = false;
}
static struct pernet_operations brnf_net_ops __read_mostly = {
.exit = brnf_exit_net,
.id = &brnf_net_id,
.size = sizeof(struct brnf_net),
};
static struct notifier_block brnf_notifier __read_mostly = {
.notifier_call = brnf_device_event,
};
#ifdef CONFIG_SYSCTL
static
int brnf_sysctl_call_tables(struct ctl_table *ctl, int write,
@@ -1003,16 +1057,23 @@ static int __init br_netfilter_init(void)
{
int ret;
ret = nf_register_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
ret = register_pernet_subsys(&brnf_net_ops);
if (ret < 0)
return ret;
ret = register_netdevice_notifier(&brnf_notifier);
if (ret < 0) {
unregister_pernet_subsys(&brnf_net_ops);
return ret;
}
#ifdef CONFIG_SYSCTL
brnf_sysctl_header = register_net_sysctl(&init_net, "net/bridge", brnf_table);
if (brnf_sysctl_header == NULL) {
printk(KERN_WARNING
"br_netfilter: can't register to sysctl.\n");
nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
unregister_netdevice_notifier(&brnf_notifier);
unregister_pernet_subsys(&brnf_net_ops);
return -ENOMEM;
}
#endif
@@ -1024,7 +1085,8 @@ static int __init br_netfilter_init(void)
static void __exit br_netfilter_fini(void)
{
RCU_INIT_POINTER(nf_br_ops, NULL);
nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
unregister_netdevice_notifier(&brnf_notifier);
unregister_pernet_subsys(&brnf_net_ops);
#ifdef CONFIG_SYSCTL
unregister_net_sysctl_table(brnf_sysctl_header);
#endif

View File

@@ -598,7 +598,6 @@ static int br_set_port_state(struct net_bridge_port *p, u8 state)
return -ENETDOWN;
br_set_state(p, state);
br_log_state(p);
br_port_state_selection(p->br);
return 0;
}

View File

@@ -150,6 +150,9 @@ struct net_bridge_fdb_entry
struct rcu_head rcu;
};
#define MDB_PG_FLAGS_PERMANENT BIT(0)
#define MDB_PG_FLAGS_OFFLOAD BIT(1)
struct net_bridge_port_group {
struct net_bridge_port *port;
struct net_bridge_port_group __rcu *next;
@@ -157,7 +160,7 @@ struct net_bridge_port_group {
struct rcu_head rcu;
struct timer_list timer;
struct br_ip addr;
unsigned char state;
unsigned char flags;
};
struct net_bridge_mdb_entry
@@ -554,11 +557,11 @@ void br_multicast_free_pg(struct rcu_head *head);
struct net_bridge_port_group *
br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group,
struct net_bridge_port_group __rcu *next,
unsigned char state);
unsigned char flags);
void br_mdb_init(void);
void br_mdb_uninit(void);
void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
struct br_ip *group, int type, u8 state);
void br_mdb_notify(struct net_device *dev, struct net_bridge_port_group *pg,
int type);
void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port,
int type);
@@ -897,7 +900,6 @@ static inline void br_nf_core_fini(void) {}
#endif
/* br_stp.c */
void br_log_state(const struct net_bridge_port *p);
void br_set_state(struct net_bridge_port *p, unsigned int state);
struct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no);
void br_init_port(struct net_bridge_port *p);

View File

@@ -30,13 +30,6 @@ static const char *const br_port_state_names[] = {
[BR_STATE_BLOCKING] = "blocking",
};
void br_log_state(const struct net_bridge_port *p)
{
br_info(p->br, "port %u(%s) entered %s state\n",
(unsigned int) p->port_no, p->dev->name,
br_port_state_names[p->state]);
}
void br_set_state(struct net_bridge_port *p, unsigned int state)
{
struct switchdev_attr attr = {
@@ -52,6 +45,10 @@ void br_set_state(struct net_bridge_port *p, unsigned int state)
if (err && err != -EOPNOTSUPP)
br_warn(p->br, "error setting offload STP state on port %u(%s)\n",
(unsigned int) p->port_no, p->dev->name);
else
br_info(p->br, "port %u(%s) entered %s state\n",
(unsigned int) p->port_no, p->dev->name,
br_port_state_names[p->state]);
}
/* called under bridge lock */
@@ -126,7 +123,6 @@ static void br_root_port_block(const struct net_bridge *br,
(unsigned int) p->port_no, p->dev->name);
br_set_state(p, BR_STATE_LISTENING);
br_log_state(p);
br_ifinfo_notify(RTM_NEWLINK, p);
if (br->forward_delay > 0)
@@ -407,7 +403,6 @@ static void br_make_blocking(struct net_bridge_port *p)
br_topology_change_detection(p->br);
br_set_state(p, BR_STATE_BLOCKING);
br_log_state(p);
br_ifinfo_notify(RTM_NEWLINK, p);
del_timer(&p->forward_delay_timer);
@@ -431,7 +426,6 @@ static void br_make_forwarding(struct net_bridge_port *p)
else
br_set_state(p, BR_STATE_LEARNING);
br_log_state(p);
br_ifinfo_notify(RTM_NEWLINK, p);
if (br->forward_delay != 0)
@@ -568,6 +562,14 @@ int br_set_max_age(struct net_bridge *br, unsigned long val)
}
/* Set time interval that dynamic forwarding entries live
* For pure software bridge, allow values outside the 802.1
* standard specification for special cases:
* 0 - entry never ages (all permanant)
* 1 - entry disappears (no persistance)
*
* Offloaded switch entries maybe more restrictive
*/
int br_set_ageing_time(struct net_bridge *br, u32 ageing_time)
{
struct switchdev_attr attr = {
@@ -579,9 +581,6 @@ int br_set_ageing_time(struct net_bridge *br, u32 ageing_time)
unsigned long t = clock_t_to_jiffies(ageing_time);
int err;
if (t < BR_MIN_AGEING_TIME || t > BR_MAX_AGEING_TIME)
return -ERANGE;
err = switchdev_port_attr_set(br->dev, &attr);
if (err)
return err;

View File

@@ -102,7 +102,6 @@ void br_stp_enable_port(struct net_bridge_port *p)
{
br_init_port(p);
br_port_state_selection(p->br);
br_log_state(p);
br_ifinfo_notify(RTM_NEWLINK, p);
}
@@ -118,7 +117,6 @@ void br_stp_disable_port(struct net_bridge_port *p)
p->topology_change_ack = 0;
p->config_pending = 0;
br_log_state(p);
br_ifinfo_notify(RTM_NEWLINK, p);
del_timer(&p->message_age_timer);

View File

@@ -98,7 +98,6 @@ static void br_forward_delay_timer_expired(unsigned long arg)
br_topology_change_detection(br);
netif_carrier_on(br->dev);
}
br_log_state(p);
rcu_read_lock();
br_ifinfo_notify(RTM_NEWLINK, p);
rcu_read_unlock();

View File

@@ -955,6 +955,13 @@ err_rhtbl:
*/
int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
{
struct switchdev_obj_port_vlan v = {
.obj.orig_dev = port->dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.flags = flags,
.vid_begin = vid,
.vid_end = vid,
};
struct net_bridge_vlan *vlan;
int ret;
@@ -962,6 +969,10 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
vlan = br_vlan_find(nbp_vlan_group(port), vid);
if (vlan) {
/* Pass the flags to the hardware bridge */
ret = switchdev_port_obj_add(port->dev, &v.obj);
if (ret && ret != -EOPNOTSUPP)
return ret;
__vlan_add_flags(vlan, flags);
return 0;
}

View File

@@ -48,6 +48,7 @@ static void nft_reject_br_send_v4_tcp_reset(struct sk_buff *oldskb,
struct iphdr *niph;
const struct tcphdr *oth;
struct tcphdr _oth;
struct net *net = sock_net(oldskb->sk);
if (!nft_bridge_iphdr_validate(oldskb))
return;
@@ -63,9 +64,9 @@ static void nft_reject_br_send_v4_tcp_reset(struct sk_buff *oldskb,
skb_reserve(nskb, LL_MAX_HEADER);
niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP,
sysctl_ip_default_ttl);
net->ipv4.sysctl_ip_default_ttl);
nf_reject_ip_tcphdr_put(nskb, oldskb, oth);
niph->ttl = sysctl_ip_default_ttl;
niph->ttl = net->ipv4.sysctl_ip_default_ttl;
niph->tot_len = htons(nskb->len);
ip_send_check(niph);
@@ -85,6 +86,7 @@ static void nft_reject_br_send_v4_unreach(struct sk_buff *oldskb,
void *payload;
__wsum csum;
u8 proto;
struct net *net = sock_net(oldskb->sk);
if (oldskb->csum_bad || !nft_bridge_iphdr_validate(oldskb))
return;
@@ -119,7 +121,7 @@ static void nft_reject_br_send_v4_unreach(struct sk_buff *oldskb,
skb_reserve(nskb, LL_MAX_HEADER);
niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_ICMP,
sysctl_ip_default_ttl);
net->ipv4.sysctl_ip_default_ttl);
skb_reset_transport_header(nskb);
icmph = (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr));

View File

@@ -286,7 +286,7 @@ int cfpkt_setlen(struct cfpkt *pkt, u16 len)
else
skb_trim(skb, len);
return cfpkt_getlen(pkt);
return cfpkt_getlen(pkt);
}
/* Need to expand SKB */

View File

@@ -4,7 +4,8 @@
#include <linux/err.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <crypto/hash.h>
#include <crypto/aes.h>
#include <crypto/skcipher.h>
#include <linux/key-type.h>
#include <keys/ceph-type.h>
@@ -79,9 +80,9 @@ int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *inkey)
return 0;
}
static struct crypto_blkcipher *ceph_crypto_alloc_cipher(void)
static struct crypto_skcipher *ceph_crypto_alloc_cipher(void)
{
return crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
return crypto_alloc_skcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
}
static const u8 *aes_iv = (u8 *)CEPH_AES_IV;
@@ -162,11 +163,10 @@ static int ceph_aes_encrypt(const void *key, int key_len,
{
struct scatterlist sg_in[2], prealloc_sg;
struct sg_table sg_out;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 };
struct crypto_skcipher *tfm = ceph_crypto_alloc_cipher();
SKCIPHER_REQUEST_ON_STACK(req, tfm);
int ret;
void *iv;
int ivsize;
char iv[AES_BLOCK_SIZE];
size_t zero_padding = (0x10 - (src_len & 0x0f));
char pad[16];
@@ -184,10 +184,13 @@ static int ceph_aes_encrypt(const void *key, int key_len,
if (ret)
goto out_tfm;
crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
memcpy(iv, aes_iv, ivsize);
crypto_skcipher_setkey((void *)tfm, key, key_len);
memcpy(iv, aes_iv, AES_BLOCK_SIZE);
skcipher_request_set_tfm(req, tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
skcipher_request_set_crypt(req, sg_in, sg_out.sgl,
src_len + zero_padding, iv);
/*
print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1,
@@ -197,8 +200,8 @@ static int ceph_aes_encrypt(const void *key, int key_len,
print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1,
pad, zero_padding, 1);
*/
ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in,
src_len + zero_padding);
ret = crypto_skcipher_encrypt(req);
skcipher_request_zero(req);
if (ret < 0) {
pr_err("ceph_aes_crypt failed %d\n", ret);
goto out_sg;
@@ -211,7 +214,7 @@ static int ceph_aes_encrypt(const void *key, int key_len,
out_sg:
teardown_sgtable(&sg_out);
out_tfm:
crypto_free_blkcipher(tfm);
crypto_free_skcipher(tfm);
return ret;
}
@@ -222,11 +225,10 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
{
struct scatterlist sg_in[3], prealloc_sg;
struct sg_table sg_out;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 };
struct crypto_skcipher *tfm = ceph_crypto_alloc_cipher();
SKCIPHER_REQUEST_ON_STACK(req, tfm);
int ret;
void *iv;
int ivsize;
char iv[AES_BLOCK_SIZE];
size_t zero_padding = (0x10 - ((src1_len + src2_len) & 0x0f));
char pad[16];
@@ -245,10 +247,13 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
if (ret)
goto out_tfm;
crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
memcpy(iv, aes_iv, ivsize);
crypto_skcipher_setkey((void *)tfm, key, key_len);
memcpy(iv, aes_iv, AES_BLOCK_SIZE);
skcipher_request_set_tfm(req, tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
skcipher_request_set_crypt(req, sg_in, sg_out.sgl,
src1_len + src2_len + zero_padding, iv);
/*
print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1,
@@ -260,8 +265,8 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1,
pad, zero_padding, 1);
*/
ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in,
src1_len + src2_len + zero_padding);
ret = crypto_skcipher_encrypt(req);
skcipher_request_zero(req);
if (ret < 0) {
pr_err("ceph_aes_crypt2 failed %d\n", ret);
goto out_sg;
@@ -274,7 +279,7 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
out_sg:
teardown_sgtable(&sg_out);
out_tfm:
crypto_free_blkcipher(tfm);
crypto_free_skcipher(tfm);
return ret;
}
@@ -284,11 +289,10 @@ static int ceph_aes_decrypt(const void *key, int key_len,
{
struct sg_table sg_in;
struct scatterlist sg_out[2], prealloc_sg;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm };
struct crypto_skcipher *tfm = ceph_crypto_alloc_cipher();
SKCIPHER_REQUEST_ON_STACK(req, tfm);
char pad[16];
void *iv;
int ivsize;
char iv[AES_BLOCK_SIZE];
int ret;
int last_byte;
@@ -302,10 +306,13 @@ static int ceph_aes_decrypt(const void *key, int key_len,
if (ret)
goto out_tfm;
crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
memcpy(iv, aes_iv, ivsize);
crypto_skcipher_setkey((void *)tfm, key, key_len);
memcpy(iv, aes_iv, AES_BLOCK_SIZE);
skcipher_request_set_tfm(req, tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
skcipher_request_set_crypt(req, sg_in.sgl, sg_out,
src_len, iv);
/*
print_hex_dump(KERN_ERR, "dec key: ", DUMP_PREFIX_NONE, 16, 1,
@@ -313,7 +320,8 @@ static int ceph_aes_decrypt(const void *key, int key_len,
print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1,
src, src_len, 1);
*/
ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len);
ret = crypto_skcipher_decrypt(req);
skcipher_request_zero(req);
if (ret < 0) {
pr_err("ceph_aes_decrypt failed %d\n", ret);
goto out_sg;
@@ -338,7 +346,7 @@ static int ceph_aes_decrypt(const void *key, int key_len,
out_sg:
teardown_sgtable(&sg_in);
out_tfm:
crypto_free_blkcipher(tfm);
crypto_free_skcipher(tfm);
return ret;
}
@@ -349,11 +357,10 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
{
struct sg_table sg_in;
struct scatterlist sg_out[3], prealloc_sg;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm };
struct crypto_skcipher *tfm = ceph_crypto_alloc_cipher();
SKCIPHER_REQUEST_ON_STACK(req, tfm);
char pad[16];
void *iv;
int ivsize;
char iv[AES_BLOCK_SIZE];
int ret;
int last_byte;
@@ -368,10 +375,13 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
if (ret)
goto out_tfm;
crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
memcpy(iv, aes_iv, ivsize);
crypto_skcipher_setkey((void *)tfm, key, key_len);
memcpy(iv, aes_iv, AES_BLOCK_SIZE);
skcipher_request_set_tfm(req, tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
skcipher_request_set_crypt(req, sg_in.sgl, sg_out,
src_len, iv);
/*
print_hex_dump(KERN_ERR, "dec key: ", DUMP_PREFIX_NONE, 16, 1,
@@ -379,7 +389,8 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1,
src, src_len, 1);
*/
ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len);
ret = crypto_skcipher_decrypt(req);
skcipher_request_zero(req);
if (ret < 0) {
pr_err("ceph_aes_decrypt failed %d\n", ret);
goto out_sg;
@@ -415,7 +426,7 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
out_sg:
teardown_sgtable(&sg_in);
out_tfm:
crypto_free_blkcipher(tfm);
crypto_free_skcipher(tfm);
return ret;
}

View File

@@ -24,3 +24,6 @@ obj-$(CONFIG_NET_PTP_CLASSIFY) += ptp_classifier.o
obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o
obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o
obj-$(CONFIG_LWTUNNEL) += lwtunnel.o
obj-$(CONFIG_DST_CACHE) += dst_cache.o
obj-$(CONFIG_HWBM) += hwbm.o
obj-$(CONFIG_NET_DEVLINK) += devlink.o

View File

@@ -3829,8 +3829,14 @@ static void net_tx_action(struct softirq_action *h)
trace_consume_skb(skb);
else
trace_kfree_skb(skb, net_tx_action);
__kfree_skb(skb);
if (skb->fclone != SKB_FCLONE_UNAVAILABLE)
__kfree_skb(skb);
else
__kfree_skb_defer(skb);
}
__kfree_skb_flush();
}
if (sd->output_queue) {
@@ -4154,7 +4160,10 @@ ncls:
ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
} else {
drop:
atomic_long_inc(&skb->dev->rx_dropped);
if (!deliver_exact)
atomic_long_inc(&skb->dev->rx_dropped);
else
atomic_long_inc(&skb->dev->rx_nohandler);
kfree_skb(skb);
/* Jamal, now you will not able to escape explaining
* me how you were going to use this. :-)
@@ -5152,6 +5161,7 @@ static void net_rx_action(struct softirq_action *h)
}
}
__kfree_skb_flush();
local_irq_disable();
list_splice_tail_init(&sd->poll_list, &list);
@@ -7253,24 +7263,31 @@ void netdev_run_todo(void)
}
}
/* Convert net_device_stats to rtnl_link_stats64. They have the same
* fields in the same order, with only the type differing.
/* Convert net_device_stats to rtnl_link_stats64. rtnl_link_stats64 has
* all the same fields in the same order as net_device_stats, with only
* the type differing, but rtnl_link_stats64 may have additional fields
* at the end for newer counters.
*/
void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64,
const struct net_device_stats *netdev_stats)
{
#if BITS_PER_LONG == 64
BUILD_BUG_ON(sizeof(*stats64) != sizeof(*netdev_stats));
BUILD_BUG_ON(sizeof(*stats64) < sizeof(*netdev_stats));
memcpy(stats64, netdev_stats, sizeof(*stats64));
/* zero out counters that only exist in rtnl_link_stats64 */
memset((char *)stats64 + sizeof(*netdev_stats), 0,
sizeof(*stats64) - sizeof(*netdev_stats));
#else
size_t i, n = sizeof(*stats64) / sizeof(u64);
size_t i, n = sizeof(*netdev_stats) / sizeof(unsigned long);
const unsigned long *src = (const unsigned long *)netdev_stats;
u64 *dst = (u64 *)stats64;
BUILD_BUG_ON(sizeof(*netdev_stats) / sizeof(unsigned long) !=
sizeof(*stats64) / sizeof(u64));
BUILD_BUG_ON(n > sizeof(*stats64) / sizeof(u64));
for (i = 0; i < n; i++)
dst[i] = src[i];
/* zero out counters that only exist in rtnl_link_stats64 */
memset((char *)stats64 + n * sizeof(u64), 0,
sizeof(*stats64) - n * sizeof(u64));
#endif
}
EXPORT_SYMBOL(netdev_stats_to_stats64);
@@ -7300,6 +7317,7 @@ struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev,
}
storage->rx_dropped += atomic_long_read(&dev->rx_dropped);
storage->tx_dropped += atomic_long_read(&dev->tx_dropped);
storage->rx_nohandler += atomic_long_read(&dev->rx_nohandler);
return storage;
}
EXPORT_SYMBOL(dev_get_stats);

738
net/core/devlink.c Normal file
View File

@@ -0,0 +1,738 @@
/*
* net/core/devlink.c - Network physical/parent device Netlink interface
*
* Heavily inspired by net/wireless/
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/gfp.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <rdma/ib_verbs.h>
#include <net/netlink.h>
#include <net/genetlink.h>
#include <net/rtnetlink.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/devlink.h>
static LIST_HEAD(devlink_list);
/* devlink_mutex
*
* An overall lock guarding every operation coming from userspace.
* It also guards devlink devices list and it is taken when
* driver registers/unregisters it.
*/
static DEFINE_MUTEX(devlink_mutex);
/* devlink_port_mutex
*
* Shared lock to guard lists of ports in all devlink devices.
*/
static DEFINE_MUTEX(devlink_port_mutex);
static struct net *devlink_net(const struct devlink *devlink)
{
return read_pnet(&devlink->_net);
}
static void devlink_net_set(struct devlink *devlink, struct net *net)
{
write_pnet(&devlink->_net, net);
}
static struct devlink *devlink_get_from_attrs(struct net *net,
struct nlattr **attrs)
{
struct devlink *devlink;
char *busname;
char *devname;
if (!attrs[DEVLINK_ATTR_BUS_NAME] || !attrs[DEVLINK_ATTR_DEV_NAME])
return ERR_PTR(-EINVAL);
busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]);
devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]);
list_for_each_entry(devlink, &devlink_list, list) {
if (strcmp(devlink->dev->bus->name, busname) == 0 &&
strcmp(dev_name(devlink->dev), devname) == 0 &&
net_eq(devlink_net(devlink), net))
return devlink;
}
return ERR_PTR(-ENODEV);
}
static struct devlink *devlink_get_from_info(struct genl_info *info)
{
return devlink_get_from_attrs(genl_info_net(info), info->attrs);
}
static struct devlink_port *devlink_port_get_by_index(struct devlink *devlink,
int port_index)
{
struct devlink_port *devlink_port;
list_for_each_entry(devlink_port, &devlink->port_list, list) {
if (devlink_port->index == port_index)
return devlink_port;
}
return NULL;
}
static bool devlink_port_index_exists(struct devlink *devlink, int port_index)
{
return devlink_port_get_by_index(devlink, port_index);
}
static struct devlink_port *devlink_port_get_from_attrs(struct devlink *devlink,
struct nlattr **attrs)
{
if (attrs[DEVLINK_ATTR_PORT_INDEX]) {
u32 port_index = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);
struct devlink_port *devlink_port;
devlink_port = devlink_port_get_by_index(devlink, port_index);
if (!devlink_port)
return ERR_PTR(-ENODEV);
return devlink_port;
}
return ERR_PTR(-EINVAL);
}
static struct devlink_port *devlink_port_get_from_info(struct devlink *devlink,
struct genl_info *info)
{
return devlink_port_get_from_attrs(devlink, info->attrs);
}
#define DEVLINK_NL_FLAG_NEED_PORT BIT(0)
static int devlink_nl_pre_doit(const struct genl_ops *ops,
struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink;
mutex_lock(&devlink_mutex);
devlink = devlink_get_from_info(info);
if (IS_ERR(devlink)) {
mutex_unlock(&devlink_mutex);
return PTR_ERR(devlink);
}
info->user_ptr[0] = devlink;
if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) {
struct devlink_port *devlink_port;
mutex_lock(&devlink_port_mutex);
devlink_port = devlink_port_get_from_info(devlink, info);
if (IS_ERR(devlink_port)) {
mutex_unlock(&devlink_port_mutex);
mutex_unlock(&devlink_mutex);
return PTR_ERR(devlink_port);
}
info->user_ptr[1] = devlink_port;
}
return 0;
}
static void devlink_nl_post_doit(const struct genl_ops *ops,
struct sk_buff *skb, struct genl_info *info)
{
if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT)
mutex_unlock(&devlink_port_mutex);
mutex_unlock(&devlink_mutex);
}
static struct genl_family devlink_nl_family = {
.id = GENL_ID_GENERATE,
.name = DEVLINK_GENL_NAME,
.version = DEVLINK_GENL_VERSION,
.maxattr = DEVLINK_ATTR_MAX,
.netnsok = true,
.pre_doit = devlink_nl_pre_doit,
.post_doit = devlink_nl_post_doit,
};
enum devlink_multicast_groups {
DEVLINK_MCGRP_CONFIG,
};
static const struct genl_multicast_group devlink_nl_mcgrps[] = {
[DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME },
};
static int devlink_nl_put_handle(struct sk_buff *msg, struct devlink *devlink)
{
if (nla_put_string(msg, DEVLINK_ATTR_BUS_NAME, devlink->dev->bus->name))
return -EMSGSIZE;
if (nla_put_string(msg, DEVLINK_ATTR_DEV_NAME, dev_name(devlink->dev)))
return -EMSGSIZE;
return 0;
}
static int devlink_nl_fill(struct sk_buff *msg, struct devlink *devlink,
enum devlink_command cmd, u32 portid,
u32 seq, int flags)
{
void *hdr;
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
if (!hdr)
return -EMSGSIZE;
if (devlink_nl_put_handle(msg, devlink))
goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
static void devlink_notify(struct devlink *devlink, enum devlink_command cmd)
{
struct sk_buff *msg;
int err;
WARN_ON(cmd != DEVLINK_CMD_NEW && cmd != DEVLINK_CMD_DEL);
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return;
err = devlink_nl_fill(msg, devlink, cmd, 0, 0, 0);
if (err) {
nlmsg_free(msg);
return;
}
genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
}
static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink,
struct devlink_port *devlink_port,
enum devlink_command cmd, u32 portid,
u32 seq, int flags)
{
void *hdr;
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
if (!hdr)
return -EMSGSIZE;
if (devlink_nl_put_handle(msg, devlink))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, devlink_port->index))
goto nla_put_failure;
if (nla_put_u16(msg, DEVLINK_ATTR_PORT_TYPE, devlink_port->type))
goto nla_put_failure;
if (devlink_port->desired_type != DEVLINK_PORT_TYPE_NOTSET &&
nla_put_u16(msg, DEVLINK_ATTR_PORT_DESIRED_TYPE,
devlink_port->desired_type))
goto nla_put_failure;
if (devlink_port->type == DEVLINK_PORT_TYPE_ETH) {
struct net_device *netdev = devlink_port->type_dev;
if (netdev &&
(nla_put_u32(msg, DEVLINK_ATTR_PORT_NETDEV_IFINDEX,
netdev->ifindex) ||
nla_put_string(msg, DEVLINK_ATTR_PORT_NETDEV_NAME,
netdev->name)))
goto nla_put_failure;
}
if (devlink_port->type == DEVLINK_PORT_TYPE_IB) {
struct ib_device *ibdev = devlink_port->type_dev;
if (ibdev &&
nla_put_string(msg, DEVLINK_ATTR_PORT_IBDEV_NAME,
ibdev->name))
goto nla_put_failure;
}
if (devlink_port->split &&
nla_put_u32(msg, DEVLINK_ATTR_PORT_SPLIT_GROUP,
devlink_port->split_group))
goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
static void devlink_port_notify(struct devlink_port *devlink_port,
enum devlink_command cmd)
{
struct devlink *devlink = devlink_port->devlink;
struct sk_buff *msg;
int err;
if (!devlink_port->registered)
return;
WARN_ON(cmd != DEVLINK_CMD_PORT_NEW && cmd != DEVLINK_CMD_PORT_DEL);
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return;
err = devlink_nl_port_fill(msg, devlink, devlink_port, cmd, 0, 0, 0);
if (err) {
nlmsg_free(msg);
return;
}
genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
}
static int devlink_nl_cmd_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct sk_buff *msg;
int err;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
err = devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW,
info->snd_portid, info->snd_seq, 0);
if (err) {
nlmsg_free(msg);
return err;
}
return genlmsg_reply(msg, info);
}
static int devlink_nl_cmd_get_dumpit(struct sk_buff *msg,
struct netlink_callback *cb)
{
struct devlink *devlink;
int start = cb->args[0];
int idx = 0;
int err;
mutex_lock(&devlink_mutex);
list_for_each_entry(devlink, &devlink_list, list) {
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
continue;
if (idx < start) {
idx++;
continue;
}
err = devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI);
if (err)
goto out;
idx++;
}
out:
mutex_unlock(&devlink_mutex);
cb->args[0] = idx;
return msg->len;
}
static int devlink_nl_cmd_port_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_port *devlink_port = info->user_ptr[1];
struct sk_buff *msg;
int err;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
err = devlink_nl_port_fill(msg, devlink, devlink_port,
DEVLINK_CMD_PORT_NEW,
info->snd_portid, info->snd_seq, 0);
if (err) {
nlmsg_free(msg);
return err;
}
return genlmsg_reply(msg, info);
}
static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg,
struct netlink_callback *cb)
{
struct devlink *devlink;
struct devlink_port *devlink_port;
int start = cb->args[0];
int idx = 0;
int err;
mutex_lock(&devlink_mutex);
mutex_lock(&devlink_port_mutex);
list_for_each_entry(devlink, &devlink_list, list) {
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
continue;
list_for_each_entry(devlink_port, &devlink->port_list, list) {
if (idx < start) {
idx++;
continue;
}
err = devlink_nl_port_fill(msg, devlink, devlink_port,
DEVLINK_CMD_NEW,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
NLM_F_MULTI);
if (err)
goto out;
idx++;
}
}
out:
mutex_unlock(&devlink_port_mutex);
mutex_unlock(&devlink_mutex);
cb->args[0] = idx;
return msg->len;
}
static int devlink_port_type_set(struct devlink *devlink,
struct devlink_port *devlink_port,
enum devlink_port_type port_type)
{
int err;
if (devlink->ops && devlink->ops->port_type_set) {
if (port_type == DEVLINK_PORT_TYPE_NOTSET)
return -EINVAL;
err = devlink->ops->port_type_set(devlink_port, port_type);
if (err)
return err;
devlink_port->desired_type = port_type;
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
return 0;
}
return -EOPNOTSUPP;
}
static int devlink_nl_cmd_port_set_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_port *devlink_port = info->user_ptr[1];
int err;
if (info->attrs[DEVLINK_ATTR_PORT_TYPE]) {
enum devlink_port_type port_type;
port_type = nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_TYPE]);
err = devlink_port_type_set(devlink, devlink_port, port_type);
if (err)
return err;
}
return 0;
}
static int devlink_port_split(struct devlink *devlink,
u32 port_index, u32 count)
{
if (devlink->ops && devlink->ops->port_split)
return devlink->ops->port_split(devlink, port_index, count);
return -EOPNOTSUPP;
}
static int devlink_nl_cmd_port_split_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
u32 port_index;
u32 count;
if (!info->attrs[DEVLINK_ATTR_PORT_INDEX] ||
!info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT])
return -EINVAL;
port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
count = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT]);
return devlink_port_split(devlink, port_index, count);
}
static int devlink_port_unsplit(struct devlink *devlink, u32 port_index)
{
if (devlink->ops && devlink->ops->port_unsplit)
return devlink->ops->port_unsplit(devlink, port_index);
return -EOPNOTSUPP;
}
static int devlink_nl_cmd_port_unsplit_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
u32 port_index;
if (!info->attrs[DEVLINK_ATTR_PORT_INDEX])
return -EINVAL;
port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
return devlink_port_unsplit(devlink, port_index);
}
static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
[DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING },
[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING },
[DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32 },
[DEVLINK_ATTR_PORT_TYPE] = { .type = NLA_U16 },
[DEVLINK_ATTR_PORT_SPLIT_COUNT] = { .type = NLA_U32 },
};
static const struct genl_ops devlink_nl_ops[] = {
{
.cmd = DEVLINK_CMD_GET,
.doit = devlink_nl_cmd_get_doit,
.dumpit = devlink_nl_cmd_get_dumpit,
.policy = devlink_nl_policy,
/* can be retrieved by unprivileged users */
},
{
.cmd = DEVLINK_CMD_PORT_GET,
.doit = devlink_nl_cmd_port_get_doit,
.dumpit = devlink_nl_cmd_port_get_dumpit,
.policy = devlink_nl_policy,
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
/* can be retrieved by unprivileged users */
},
{
.cmd = DEVLINK_CMD_PORT_SET,
.doit = devlink_nl_cmd_port_set_doit,
.policy = devlink_nl_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
},
{
.cmd = DEVLINK_CMD_PORT_SPLIT,
.doit = devlink_nl_cmd_port_split_doit,
.policy = devlink_nl_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = DEVLINK_CMD_PORT_UNSPLIT,
.doit = devlink_nl_cmd_port_unsplit_doit,
.policy = devlink_nl_policy,
.flags = GENL_ADMIN_PERM,
},
};
/**
* devlink_alloc - Allocate new devlink instance resources
*
* @ops: ops
* @priv_size: size of user private data
*
* Allocate new devlink instance resources, including devlink index
* and name.
*/
struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size)
{
struct devlink *devlink;
devlink = kzalloc(sizeof(*devlink) + priv_size, GFP_KERNEL);
if (!devlink)
return NULL;
devlink->ops = ops;
devlink_net_set(devlink, &init_net);
INIT_LIST_HEAD(&devlink->port_list);
return devlink;
}
EXPORT_SYMBOL_GPL(devlink_alloc);
/**
* devlink_register - Register devlink instance
*
* @devlink: devlink
*/
int devlink_register(struct devlink *devlink, struct device *dev)
{
mutex_lock(&devlink_mutex);
devlink->dev = dev;
list_add_tail(&devlink->list, &devlink_list);
devlink_notify(devlink, DEVLINK_CMD_NEW);
mutex_unlock(&devlink_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(devlink_register);
/**
* devlink_unregister - Unregister devlink instance
*
* @devlink: devlink
*/
void devlink_unregister(struct devlink *devlink)
{
mutex_lock(&devlink_mutex);
devlink_notify(devlink, DEVLINK_CMD_DEL);
list_del(&devlink->list);
mutex_unlock(&devlink_mutex);
}
EXPORT_SYMBOL_GPL(devlink_unregister);
/**
* devlink_free - Free devlink instance resources
*
* @devlink: devlink
*/
void devlink_free(struct devlink *devlink)
{
kfree(devlink);
}
EXPORT_SYMBOL_GPL(devlink_free);
/**
* devlink_port_register - Register devlink port
*
* @devlink: devlink
* @devlink_port: devlink port
* @port_index
*
* Register devlink port with provided port index. User can use
* any indexing, even hw-related one. devlink_port structure
* is convenient to be embedded inside user driver private structure.
* Note that the caller should take care of zeroing the devlink_port
* structure.
*/
int devlink_port_register(struct devlink *devlink,
struct devlink_port *devlink_port,
unsigned int port_index)
{
mutex_lock(&devlink_port_mutex);
if (devlink_port_index_exists(devlink, port_index)) {
mutex_unlock(&devlink_port_mutex);
return -EEXIST;
}
devlink_port->devlink = devlink;
devlink_port->index = port_index;
devlink_port->type = DEVLINK_PORT_TYPE_NOTSET;
devlink_port->registered = true;
list_add_tail(&devlink_port->list, &devlink->port_list);
mutex_unlock(&devlink_port_mutex);
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
return 0;
}
EXPORT_SYMBOL_GPL(devlink_port_register);
/**
* devlink_port_unregister - Unregister devlink port
*
* @devlink_port: devlink port
*/
void devlink_port_unregister(struct devlink_port *devlink_port)
{
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL);
mutex_lock(&devlink_port_mutex);
list_del(&devlink_port->list);
mutex_unlock(&devlink_port_mutex);
}
EXPORT_SYMBOL_GPL(devlink_port_unregister);
static void __devlink_port_type_set(struct devlink_port *devlink_port,
enum devlink_port_type type,
void *type_dev)
{
devlink_port->type = type;
devlink_port->type_dev = type_dev;
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
}
/**
* devlink_port_type_eth_set - Set port type to Ethernet
*
* @devlink_port: devlink port
* @netdev: related netdevice
*/
void devlink_port_type_eth_set(struct devlink_port *devlink_port,
struct net_device *netdev)
{
return __devlink_port_type_set(devlink_port,
DEVLINK_PORT_TYPE_ETH, netdev);
}
EXPORT_SYMBOL_GPL(devlink_port_type_eth_set);
/**
* devlink_port_type_ib_set - Set port type to InfiniBand
*
* @devlink_port: devlink port
* @ibdev: related IB device
*/
void devlink_port_type_ib_set(struct devlink_port *devlink_port,
struct ib_device *ibdev)
{
return __devlink_port_type_set(devlink_port,
DEVLINK_PORT_TYPE_IB, ibdev);
}
EXPORT_SYMBOL_GPL(devlink_port_type_ib_set);
/**
* devlink_port_type_clear - Clear port type
*
* @devlink_port: devlink port
*/
void devlink_port_type_clear(struct devlink_port *devlink_port)
{
return __devlink_port_type_set(devlink_port,
DEVLINK_PORT_TYPE_NOTSET, NULL);
}
EXPORT_SYMBOL_GPL(devlink_port_type_clear);
/**
* devlink_port_split_set - Set port is split
*
* @devlink_port: devlink port
* @split_group: split group - identifies group split port is part of
*/
void devlink_port_split_set(struct devlink_port *devlink_port,
u32 split_group)
{
devlink_port->split = true;
devlink_port->split_group = split_group;
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
}
EXPORT_SYMBOL_GPL(devlink_port_split_set);
static int __init devlink_module_init(void)
{
return genl_register_family_with_ops_groups(&devlink_nl_family,
devlink_nl_ops,
devlink_nl_mcgrps);
}
static void __exit devlink_module_exit(void)
{
genl_unregister_family(&devlink_nl_family);
}
module_init(devlink_module_init);
module_exit(devlink_module_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
MODULE_DESCRIPTION("Network physical device Netlink interface");
MODULE_ALIAS_GENL_FAMILY(DEVLINK_GENL_NAME);

View File

@@ -265,7 +265,7 @@ again:
lwtstate_put(dst->lwtstate);
if (dst->flags & DST_METADATA)
kfree(dst);
metadata_dst_free((struct metadata_dst *)dst);
else
kmem_cache_free(dst->ops->kmem_cachep, dst);
@@ -395,6 +395,14 @@ struct metadata_dst *metadata_dst_alloc(u8 optslen, gfp_t flags)
}
EXPORT_SYMBOL_GPL(metadata_dst_alloc);
void metadata_dst_free(struct metadata_dst *md_dst)
{
#ifdef CONFIG_DST_CACHE
dst_cache_destroy(&md_dst->u.tun_info.dst_cache);
#endif
kfree(md_dst);
}
struct metadata_dst __percpu *metadata_dst_alloc_percpu(u8 optslen, gfp_t flags)
{
int cpu;

168
net/core/dst_cache.c Normal file
View File

@@ -0,0 +1,168 @@
/*
* net/core/dst_cache.c - dst entry cache
*
* Copyright (c) 2016 Paolo Abeni <pabeni@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/percpu.h>
#include <net/dst_cache.h>
#include <net/route.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <net/ip6_fib.h>
#endif
#include <uapi/linux/in.h>
struct dst_cache_pcpu {
unsigned long refresh_ts;
struct dst_entry *dst;
u32 cookie;
union {
struct in_addr in_saddr;
struct in6_addr in6_saddr;
};
};
static void dst_cache_per_cpu_dst_set(struct dst_cache_pcpu *dst_cache,
struct dst_entry *dst, u32 cookie)
{
dst_release(dst_cache->dst);
if (dst)
dst_hold(dst);
dst_cache->cookie = cookie;
dst_cache->dst = dst;
}
static struct dst_entry *dst_cache_per_cpu_get(struct dst_cache *dst_cache,
struct dst_cache_pcpu *idst)
{
struct dst_entry *dst;
dst = idst->dst;
if (!dst)
goto fail;
/* the cache already hold a dst reference; it can't go away */
dst_hold(dst);
if (unlikely(!time_after(idst->refresh_ts, dst_cache->reset_ts) ||
(dst->obsolete && !dst->ops->check(dst, idst->cookie)))) {
dst_cache_per_cpu_dst_set(idst, NULL, 0);
dst_release(dst);
goto fail;
}
return dst;
fail:
idst->refresh_ts = jiffies;
return NULL;
}
struct dst_entry *dst_cache_get(struct dst_cache *dst_cache)
{
if (!dst_cache->cache)
return NULL;
return dst_cache_per_cpu_get(dst_cache, this_cpu_ptr(dst_cache->cache));
}
EXPORT_SYMBOL_GPL(dst_cache_get);
struct rtable *dst_cache_get_ip4(struct dst_cache *dst_cache, __be32 *saddr)
{
struct dst_cache_pcpu *idst;
struct dst_entry *dst;
if (!dst_cache->cache)
return NULL;
idst = this_cpu_ptr(dst_cache->cache);
dst = dst_cache_per_cpu_get(dst_cache, idst);
if (!dst)
return NULL;
*saddr = idst->in_saddr.s_addr;
return container_of(dst, struct rtable, dst);
}
EXPORT_SYMBOL_GPL(dst_cache_get_ip4);
void dst_cache_set_ip4(struct dst_cache *dst_cache, struct dst_entry *dst,
__be32 saddr)
{
struct dst_cache_pcpu *idst;
if (!dst_cache->cache)
return;
idst = this_cpu_ptr(dst_cache->cache);
dst_cache_per_cpu_dst_set(idst, dst, 0);
idst->in_saddr.s_addr = saddr;
}
EXPORT_SYMBOL_GPL(dst_cache_set_ip4);
#if IS_ENABLED(CONFIG_IPV6)
void dst_cache_set_ip6(struct dst_cache *dst_cache, struct dst_entry *dst,
const struct in6_addr *addr)
{
struct dst_cache_pcpu *idst;
if (!dst_cache->cache)
return;
idst = this_cpu_ptr(dst_cache->cache);
dst_cache_per_cpu_dst_set(this_cpu_ptr(dst_cache->cache), dst,
rt6_get_cookie((struct rt6_info *)dst));
idst->in6_saddr = *addr;
}
EXPORT_SYMBOL_GPL(dst_cache_set_ip6);
struct dst_entry *dst_cache_get_ip6(struct dst_cache *dst_cache,
struct in6_addr *saddr)
{
struct dst_cache_pcpu *idst;
struct dst_entry *dst;
if (!dst_cache->cache)
return NULL;
idst = this_cpu_ptr(dst_cache->cache);
dst = dst_cache_per_cpu_get(dst_cache, idst);
if (!dst)
return NULL;
*saddr = idst->in6_saddr;
return dst;
}
EXPORT_SYMBOL_GPL(dst_cache_get_ip6);
#endif
int dst_cache_init(struct dst_cache *dst_cache, gfp_t gfp)
{
dst_cache->cache = alloc_percpu_gfp(struct dst_cache_pcpu,
gfp | __GFP_ZERO);
if (!dst_cache->cache)
return -ENOMEM;
dst_cache_reset(dst_cache);
return 0;
}
EXPORT_SYMBOL_GPL(dst_cache_init);
void dst_cache_destroy(struct dst_cache *dst_cache)
{
int i;
if (!dst_cache->cache)
return;
for_each_possible_cpu(i)
dst_release(per_cpu_ptr(dst_cache->cache, i)->dst);
free_percpu(dst_cache->cache);
}
EXPORT_SYMBOL_GPL(dst_cache_destroy);

View File

@@ -98,6 +98,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
[NETIF_F_RXALL_BIT] = "rx-all",
[NETIF_F_HW_L2FW_DOFFLOAD_BIT] = "l2-fwd-offload",
[NETIF_F_BUSY_POLL_BIT] = "busy-poll",
[NETIF_F_HW_TC_BIT] = "hw-tc-offload",
};
static const char
@@ -386,43 +387,461 @@ static int __ethtool_set_flags(struct net_device *dev, u32 data)
return 0;
}
int __ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
static void convert_legacy_u32_to_link_mode(unsigned long *dst, u32 legacy_u32)
{
ASSERT_RTNL();
if (!dev->ethtool_ops->get_settings)
return -EOPNOTSUPP;
memset(cmd, 0, sizeof(struct ethtool_cmd));
cmd->cmd = ETHTOOL_GSET;
return dev->ethtool_ops->get_settings(dev, cmd);
bitmap_zero(dst, __ETHTOOL_LINK_MODE_MASK_NBITS);
dst[0] = legacy_u32;
}
EXPORT_SYMBOL(__ethtool_get_settings);
static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
/* return false if src had higher bits set. lower bits always updated. */
static bool convert_link_mode_to_legacy_u32(u32 *legacy_u32,
const unsigned long *src)
{
bool retval = true;
/* TODO: following test will soon always be true */
if (__ETHTOOL_LINK_MODE_MASK_NBITS > 32) {
__ETHTOOL_DECLARE_LINK_MODE_MASK(ext);
bitmap_zero(ext, __ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_fill(ext, 32);
bitmap_complement(ext, ext, __ETHTOOL_LINK_MODE_MASK_NBITS);
if (bitmap_intersects(ext, src,
__ETHTOOL_LINK_MODE_MASK_NBITS)) {
/* src mask goes beyond bit 31 */
retval = false;
}
}
*legacy_u32 = src[0];
return retval;
}
/* return false if legacy contained non-0 deprecated fields
* transceiver/maxtxpkt/maxrxpkt. rest of ksettings always updated
*/
static bool
convert_legacy_settings_to_link_ksettings(
struct ethtool_link_ksettings *link_ksettings,
const struct ethtool_cmd *legacy_settings)
{
bool retval = true;
memset(link_ksettings, 0, sizeof(*link_ksettings));
/* This is used to tell users that driver is still using these
* deprecated legacy fields, and they should not use
* %ETHTOOL_GLINKSETTINGS/%ETHTOOL_SLINKSETTINGS
*/
if (legacy_settings->transceiver ||
legacy_settings->maxtxpkt ||
legacy_settings->maxrxpkt)
retval = false;
convert_legacy_u32_to_link_mode(
link_ksettings->link_modes.supported,
legacy_settings->supported);
convert_legacy_u32_to_link_mode(
link_ksettings->link_modes.advertising,
legacy_settings->advertising);
convert_legacy_u32_to_link_mode(
link_ksettings->link_modes.lp_advertising,
legacy_settings->lp_advertising);
link_ksettings->base.speed
= ethtool_cmd_speed(legacy_settings);
link_ksettings->base.duplex
= legacy_settings->duplex;
link_ksettings->base.port
= legacy_settings->port;
link_ksettings->base.phy_address
= legacy_settings->phy_address;
link_ksettings->base.autoneg
= legacy_settings->autoneg;
link_ksettings->base.mdio_support
= legacy_settings->mdio_support;
link_ksettings->base.eth_tp_mdix
= legacy_settings->eth_tp_mdix;
link_ksettings->base.eth_tp_mdix_ctrl
= legacy_settings->eth_tp_mdix_ctrl;
return retval;
}
/* return false if ksettings link modes had higher bits
* set. legacy_settings always updated (best effort)
*/
static bool
convert_link_ksettings_to_legacy_settings(
struct ethtool_cmd *legacy_settings,
const struct ethtool_link_ksettings *link_ksettings)
{
bool retval = true;
memset(legacy_settings, 0, sizeof(*legacy_settings));
/* this also clears the deprecated fields in legacy structure:
* __u8 transceiver;
* __u32 maxtxpkt;
* __u32 maxrxpkt;
*/
retval &= convert_link_mode_to_legacy_u32(
&legacy_settings->supported,
link_ksettings->link_modes.supported);
retval &= convert_link_mode_to_legacy_u32(
&legacy_settings->advertising,
link_ksettings->link_modes.advertising);
retval &= convert_link_mode_to_legacy_u32(
&legacy_settings->lp_advertising,
link_ksettings->link_modes.lp_advertising);
ethtool_cmd_speed_set(legacy_settings, link_ksettings->base.speed);
legacy_settings->duplex
= link_ksettings->base.duplex;
legacy_settings->port
= link_ksettings->base.port;
legacy_settings->phy_address
= link_ksettings->base.phy_address;
legacy_settings->autoneg
= link_ksettings->base.autoneg;
legacy_settings->mdio_support
= link_ksettings->base.mdio_support;
legacy_settings->eth_tp_mdix
= link_ksettings->base.eth_tp_mdix;
legacy_settings->eth_tp_mdix_ctrl
= link_ksettings->base.eth_tp_mdix_ctrl;
return retval;
}
/* number of 32-bit words to store the user's link mode bitmaps */
#define __ETHTOOL_LINK_MODE_MASK_NU32 \
DIV_ROUND_UP(__ETHTOOL_LINK_MODE_MASK_NBITS, 32)
/* layout of the struct passed from/to userland */
struct ethtool_link_usettings {
struct ethtool_link_settings base;
struct {
__u32 supported[__ETHTOOL_LINK_MODE_MASK_NU32];
__u32 advertising[__ETHTOOL_LINK_MODE_MASK_NU32];
__u32 lp_advertising[__ETHTOOL_LINK_MODE_MASK_NU32];
} link_modes;
};
/* Internal kernel helper to query a device ethtool_link_settings.
*
* Backward compatibility note: for compatibility with legacy drivers
* that implement only the ethtool_cmd API, this has to work with both
* drivers implementing get_link_ksettings API and drivers
* implementing get_settings API. When drivers implement get_settings
* and report ethtool_cmd deprecated fields
* (transceiver/maxrxpkt/maxtxpkt), these fields are silently ignored
* because the resulting struct ethtool_link_settings does not report them.
*/
int __ethtool_get_link_ksettings(struct net_device *dev,
struct ethtool_link_ksettings *link_ksettings)
{
int err;
struct ethtool_cmd cmd;
err = __ethtool_get_settings(dev, &cmd);
ASSERT_RTNL();
if (dev->ethtool_ops->get_link_ksettings) {
memset(link_ksettings, 0, sizeof(*link_ksettings));
return dev->ethtool_ops->get_link_ksettings(dev,
link_ksettings);
}
/* driver doesn't support %ethtool_link_ksettings API. revert to
* legacy %ethtool_cmd API, unless it's not supported either.
* TODO: remove when ethtool_ops::get_settings disappears internally
*/
if (!dev->ethtool_ops->get_settings)
return -EOPNOTSUPP;
memset(&cmd, 0, sizeof(cmd));
cmd.cmd = ETHTOOL_GSET;
err = dev->ethtool_ops->get_settings(dev, &cmd);
if (err < 0)
return err;
if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
/* we ignore deprecated fields transceiver/maxrxpkt/maxtxpkt
*/
convert_legacy_settings_to_link_ksettings(link_ksettings, &cmd);
return err;
}
EXPORT_SYMBOL(__ethtool_get_link_ksettings);
/* convert ethtool_link_usettings in user space to a kernel internal
* ethtool_link_ksettings. return 0 on success, errno on error.
*/
static int load_link_ksettings_from_user(struct ethtool_link_ksettings *to,
const void __user *from)
{
struct ethtool_link_usettings link_usettings;
if (copy_from_user(&link_usettings, from, sizeof(link_usettings)))
return -EFAULT;
memcpy(&to->base, &link_usettings.base, sizeof(to->base));
bitmap_from_u32array(to->link_modes.supported,
__ETHTOOL_LINK_MODE_MASK_NBITS,
link_usettings.link_modes.supported,
__ETHTOOL_LINK_MODE_MASK_NU32);
bitmap_from_u32array(to->link_modes.advertising,
__ETHTOOL_LINK_MODE_MASK_NBITS,
link_usettings.link_modes.advertising,
__ETHTOOL_LINK_MODE_MASK_NU32);
bitmap_from_u32array(to->link_modes.lp_advertising,
__ETHTOOL_LINK_MODE_MASK_NBITS,
link_usettings.link_modes.lp_advertising,
__ETHTOOL_LINK_MODE_MASK_NU32);
return 0;
}
/* convert a kernel internal ethtool_link_ksettings to
* ethtool_link_usettings in user space. return 0 on success, errno on
* error.
*/
static int
store_link_ksettings_for_user(void __user *to,
const struct ethtool_link_ksettings *from)
{
struct ethtool_link_usettings link_usettings;
memcpy(&link_usettings.base, &from->base, sizeof(link_usettings));
bitmap_to_u32array(link_usettings.link_modes.supported,
__ETHTOOL_LINK_MODE_MASK_NU32,
from->link_modes.supported,
__ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_to_u32array(link_usettings.link_modes.advertising,
__ETHTOOL_LINK_MODE_MASK_NU32,
from->link_modes.advertising,
__ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_to_u32array(link_usettings.link_modes.lp_advertising,
__ETHTOOL_LINK_MODE_MASK_NU32,
from->link_modes.lp_advertising,
__ETHTOOL_LINK_MODE_MASK_NBITS);
if (copy_to_user(to, &link_usettings, sizeof(link_usettings)))
return -EFAULT;
return 0;
}
/* Query device for its ethtool_link_settings.
*
* Backward compatibility note: this function must fail when driver
* does not implement ethtool::get_link_ksettings, even if legacy
* ethtool_ops::get_settings is implemented. This tells new versions
* of ethtool that they should use the legacy API %ETHTOOL_GSET for
* this driver, so that they can correctly access the ethtool_cmd
* deprecated fields (transceiver/maxrxpkt/maxtxpkt), until no driver
* implements ethtool_ops::get_settings anymore.
*/
static int ethtool_get_link_ksettings(struct net_device *dev,
void __user *useraddr)
{
int err = 0;
struct ethtool_link_ksettings link_ksettings;
ASSERT_RTNL();
if (!dev->ethtool_ops->get_link_ksettings)
return -EOPNOTSUPP;
/* handle bitmap nbits handshake */
if (copy_from_user(&link_ksettings.base, useraddr,
sizeof(link_ksettings.base)))
return -EFAULT;
if (__ETHTOOL_LINK_MODE_MASK_NU32
!= link_ksettings.base.link_mode_masks_nwords) {
/* wrong link mode nbits requested */
memset(&link_ksettings, 0, sizeof(link_ksettings));
link_ksettings.base.cmd = ETHTOOL_GLINKSETTINGS;
/* send back number of words required as negative val */
compiletime_assert(__ETHTOOL_LINK_MODE_MASK_NU32 <= S8_MAX,
"need too many bits for link modes!");
link_ksettings.base.link_mode_masks_nwords
= -((s8)__ETHTOOL_LINK_MODE_MASK_NU32);
/* copy the base fields back to user, not the link
* mode bitmaps
*/
if (copy_to_user(useraddr, &link_ksettings.base,
sizeof(link_ksettings.base)))
return -EFAULT;
return 0;
}
/* handshake successful: user/kernel agree on
* link_mode_masks_nwords
*/
memset(&link_ksettings, 0, sizeof(link_ksettings));
err = dev->ethtool_ops->get_link_ksettings(dev, &link_ksettings);
if (err < 0)
return err;
/* make sure we tell the right values to user */
link_ksettings.base.cmd = ETHTOOL_GLINKSETTINGS;
link_ksettings.base.link_mode_masks_nwords
= __ETHTOOL_LINK_MODE_MASK_NU32;
return store_link_ksettings_for_user(useraddr, &link_ksettings);
}
/* Update device ethtool_link_settings.
*
* Backward compatibility note: this function must fail when driver
* does not implement ethtool::set_link_ksettings, even if legacy
* ethtool_ops::set_settings is implemented. This tells new versions
* of ethtool that they should use the legacy API %ETHTOOL_SSET for
* this driver, so that they can correctly update the ethtool_cmd
* deprecated fields (transceiver/maxrxpkt/maxtxpkt), until no driver
* implements ethtool_ops::get_settings anymore.
*/
static int ethtool_set_link_ksettings(struct net_device *dev,
void __user *useraddr)
{
int err;
struct ethtool_link_ksettings link_ksettings;
ASSERT_RTNL();
if (!dev->ethtool_ops->set_link_ksettings)
return -EOPNOTSUPP;
/* make sure nbits field has expected value */
if (copy_from_user(&link_ksettings.base, useraddr,
sizeof(link_ksettings.base)))
return -EFAULT;
if (__ETHTOOL_LINK_MODE_MASK_NU32
!= link_ksettings.base.link_mode_masks_nwords)
return -EINVAL;
/* copy the whole structure, now that we know it has expected
* format
*/
err = load_link_ksettings_from_user(&link_ksettings, useraddr);
if (err)
return err;
/* re-check nwords field, just in case */
if (__ETHTOOL_LINK_MODE_MASK_NU32
!= link_ksettings.base.link_mode_masks_nwords)
return -EINVAL;
return dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
}
static void
warn_incomplete_ethtool_legacy_settings_conversion(const char *details)
{
char name[sizeof(current->comm)];
pr_info_once("warning: `%s' uses legacy ethtool link settings API, %s\n",
get_task_comm(name, current), details);
}
/* Query device for its ethtool_cmd settings.
*
* Backward compatibility note: for compatibility with legacy ethtool,
* this has to work with both drivers implementing get_link_ksettings
* API and drivers implementing get_settings API. When drivers
* implement get_link_ksettings and report higher link mode bits, a
* kernel warning is logged once (with name of 1st driver/device) to
* recommend user to upgrade ethtool, but the command is successful
* (only the lower link mode bits reported back to user).
*/
static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
{
struct ethtool_cmd cmd;
ASSERT_RTNL();
if (dev->ethtool_ops->get_link_ksettings) {
/* First, use link_ksettings API if it is supported */
int err;
struct ethtool_link_ksettings link_ksettings;
memset(&link_ksettings, 0, sizeof(link_ksettings));
err = dev->ethtool_ops->get_link_ksettings(dev,
&link_ksettings);
if (err < 0)
return err;
if (!convert_link_ksettings_to_legacy_settings(&cmd,
&link_ksettings))
warn_incomplete_ethtool_legacy_settings_conversion(
"link modes are only partially reported");
/* send a sensible cmd tag back to user */
cmd.cmd = ETHTOOL_GSET;
} else {
/* driver doesn't support %ethtool_link_ksettings
* API. revert to legacy %ethtool_cmd API, unless it's
* not supported either.
*/
int err;
if (!dev->ethtool_ops->get_settings)
return -EOPNOTSUPP;
memset(&cmd, 0, sizeof(cmd));
cmd.cmd = ETHTOOL_GSET;
err = dev->ethtool_ops->get_settings(dev, &cmd);
if (err < 0)
return err;
}
if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
return -EFAULT;
return 0;
}
/* Update device link settings with given ethtool_cmd.
*
* Backward compatibility note: for compatibility with legacy ethtool,
* this has to work with both drivers implementing set_link_ksettings
* API and drivers implementing set_settings API. When drivers
* implement set_link_ksettings and user's request updates deprecated
* ethtool_cmd fields (transceiver/maxrxpkt/maxtxpkt), a kernel
* warning is logged once (with name of 1st driver/device) to
* recommend user to upgrade ethtool, and the request is rejected.
*/
static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
{
struct ethtool_cmd cmd;
if (!dev->ethtool_ops->set_settings)
return -EOPNOTSUPP;
ASSERT_RTNL();
if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
return -EFAULT;
/* first, try new %ethtool_link_ksettings API. */
if (dev->ethtool_ops->set_link_ksettings) {
struct ethtool_link_ksettings link_ksettings;
if (!convert_legacy_settings_to_link_ksettings(&link_ksettings,
&cmd))
return -EINVAL;
link_ksettings.base.cmd = ETHTOOL_SLINKSETTINGS;
link_ksettings.base.link_mode_masks_nwords
= __ETHTOOL_LINK_MODE_MASK_NU32;
return dev->ethtool_ops->set_link_ksettings(dev,
&link_ksettings);
}
/* legacy %ethtool_cmd API */
/* TODO: return -EOPNOTSUPP when ethtool_ops::get_settings
* disappears internally
*/
if (!dev->ethtool_ops->set_settings)
return -EOPNOTSUPP;
return dev->ethtool_ops->set_settings(dev, &cmd);
}
@@ -632,7 +1051,7 @@ static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr,
return 0;
}
u8 netdev_rss_key[NETDEV_RSS_KEY_LEN];
u8 netdev_rss_key[NETDEV_RSS_KEY_LEN] __read_mostly;
void netdev_rss_key_fill(void *buffer, size_t len)
{
@@ -642,6 +1061,37 @@ void netdev_rss_key_fill(void *buffer, size_t len)
}
EXPORT_SYMBOL(netdev_rss_key_fill);
static int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max)
{
u32 dev_size, current_max = 0;
u32 *indir;
int ret;
if (!dev->ethtool_ops->get_rxfh_indir_size ||
!dev->ethtool_ops->get_rxfh)
return -EOPNOTSUPP;
dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
if (dev_size == 0)
return -EOPNOTSUPP;
indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
if (!indir)
return -ENOMEM;
ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL);
if (ret)
goto out;
while (dev_size--)
current_max = max(current_max, indir[dev_size]);
*max = current_max;
out:
kfree(indir);
return ret;
}
static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
void __user *useraddr)
{
@@ -738,6 +1188,14 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
}
ret = ops->set_rxfh(dev, indir, NULL, ETH_RSS_HASH_NO_CHANGE);
if (ret)
goto out;
/* indicate whether rxfh was set to default */
if (user_size == 0)
dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
else
dev->priv_flags |= IFF_RXFH_CONFIGURED;
out:
kfree(indir);
@@ -897,6 +1355,14 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
}
ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
if (ret)
goto out;
/* indicate whether rxfh was set to default */
if (rxfh.indir_size == 0)
dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
dev->priv_flags |= IFF_RXFH_CONFIGURED;
out:
kfree(rss_config);
@@ -1227,14 +1693,31 @@ static noinline_for_stack int ethtool_get_channels(struct net_device *dev,
static noinline_for_stack int ethtool_set_channels(struct net_device *dev,
void __user *useraddr)
{
struct ethtool_channels channels;
struct ethtool_channels channels, max;
u32 max_rx_in_use = 0;
if (!dev->ethtool_ops->set_channels)
if (!dev->ethtool_ops->set_channels || !dev->ethtool_ops->get_channels)
return -EOPNOTSUPP;
if (copy_from_user(&channels, useraddr, sizeof(channels)))
return -EFAULT;
dev->ethtool_ops->get_channels(dev, &max);
/* ensure new counts are within the maximums */
if ((channels.rx_count > max.max_rx) ||
(channels.tx_count > max.max_tx) ||
(channels.combined_count > max.max_combined) ||
(channels.other_count > max.max_other))
return -EINVAL;
/* ensure the new Rx count fits within the configured Rx flow
* indirection table settings */
if (netif_is_rxfh_configured(dev) &&
!ethtool_get_max_rxfh_channel(dev, &max_rx_in_use) &&
(channels.combined_count + channels.rx_count) <= max_rx_in_use)
return -EINVAL;
return dev->ethtool_ops->set_channels(dev, &channels);
}
@@ -1823,13 +2306,121 @@ out:
return ret;
}
static int ethtool_get_per_queue_coalesce(struct net_device *dev,
void __user *useraddr,
struct ethtool_per_queue_op *per_queue_opt)
{
u32 bit;
int ret;
DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE);
if (!dev->ethtool_ops->get_per_queue_coalesce)
return -EOPNOTSUPP;
useraddr += sizeof(*per_queue_opt);
bitmap_from_u32array(queue_mask,
MAX_NUM_QUEUE,
per_queue_opt->queue_mask,
DIV_ROUND_UP(MAX_NUM_QUEUE, 32));
for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) {
struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE };
ret = dev->ethtool_ops->get_per_queue_coalesce(dev, bit, &coalesce);
if (ret != 0)
return ret;
if (copy_to_user(useraddr, &coalesce, sizeof(coalesce)))
return -EFAULT;
useraddr += sizeof(coalesce);
}
return 0;
}
static int ethtool_set_per_queue_coalesce(struct net_device *dev,
void __user *useraddr,
struct ethtool_per_queue_op *per_queue_opt)
{
u32 bit;
int i, ret = 0;
int n_queue;
struct ethtool_coalesce *backup = NULL, *tmp = NULL;
DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE);
if ((!dev->ethtool_ops->set_per_queue_coalesce) ||
(!dev->ethtool_ops->get_per_queue_coalesce))
return -EOPNOTSUPP;
useraddr += sizeof(*per_queue_opt);
bitmap_from_u32array(queue_mask,
MAX_NUM_QUEUE,
per_queue_opt->queue_mask,
DIV_ROUND_UP(MAX_NUM_QUEUE, 32));
n_queue = bitmap_weight(queue_mask, MAX_NUM_QUEUE);
tmp = backup = kmalloc_array(n_queue, sizeof(*backup), GFP_KERNEL);
if (!backup)
return -ENOMEM;
for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) {
struct ethtool_coalesce coalesce;
ret = dev->ethtool_ops->get_per_queue_coalesce(dev, bit, tmp);
if (ret != 0)
goto roll_back;
tmp++;
if (copy_from_user(&coalesce, useraddr, sizeof(coalesce))) {
ret = -EFAULT;
goto roll_back;
}
ret = dev->ethtool_ops->set_per_queue_coalesce(dev, bit, &coalesce);
if (ret != 0)
goto roll_back;
useraddr += sizeof(coalesce);
}
roll_back:
if (ret != 0) {
tmp = backup;
for_each_set_bit(i, queue_mask, bit) {
dev->ethtool_ops->set_per_queue_coalesce(dev, i, tmp);
tmp++;
}
}
kfree(backup);
return ret;
}
static int ethtool_set_per_queue(struct net_device *dev, void __user *useraddr)
{
struct ethtool_per_queue_op per_queue_opt;
if (copy_from_user(&per_queue_opt, useraddr, sizeof(per_queue_opt)))
return -EFAULT;
switch (per_queue_opt.sub_command) {
case ETHTOOL_GCOALESCE:
return ethtool_get_per_queue_coalesce(dev, useraddr, &per_queue_opt);
case ETHTOOL_SCOALESCE:
return ethtool_set_per_queue_coalesce(dev, useraddr, &per_queue_opt);
default:
return -EOPNOTSUPP;
};
}
/* The main entry point in this file. Called from net/core/dev_ioctl.c */
int dev_ethtool(struct net *net, struct ifreq *ifr)
{
struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
void __user *useraddr = ifr->ifr_data;
u32 ethcmd;
u32 ethcmd, sub_cmd;
int rc;
netdev_features_t old_features;
@@ -1839,8 +2430,14 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
return -EFAULT;
if (ethcmd == ETHTOOL_PERQUEUE) {
if (copy_from_user(&sub_cmd, useraddr + sizeof(ethcmd), sizeof(sub_cmd)))
return -EFAULT;
} else {
sub_cmd = ethcmd;
}
/* Allow some commands to be done by anyone */
switch (ethcmd) {
switch (sub_cmd) {
case ETHTOOL_GSET:
case ETHTOOL_GDRVINFO:
case ETHTOOL_GMSGLVL:
@@ -2070,6 +2667,15 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_GPHYSTATS:
rc = ethtool_get_phy_stats(dev, useraddr);
break;
case ETHTOOL_PERQUEUE:
rc = ethtool_set_per_queue(dev, useraddr);
break;
case ETHTOOL_GLINKSETTINGS:
rc = ethtool_get_link_ksettings(dev, useraddr);
break;
case ETHTOOL_SLINKSETTINGS:
rc = ethtool_set_link_ksettings(dev, useraddr);
break;
default:
rc = -EOPNOTSUPP;
}

View File

@@ -530,12 +530,14 @@ do_pass:
*insn = BPF_MOV64_REG(BPF_REG_A, BPF_REG_TMP);
break;
/* RET_K, RET_A are remaped into 2 insns. */
/* RET_K is remaped into 2 insns. RET_A case doesn't need an
* extra mov as BPF_REG_0 is already mapped into BPF_REG_A.
*/
case BPF_RET | BPF_A:
case BPF_RET | BPF_K:
*insn++ = BPF_MOV32_RAW(BPF_RVAL(fp->code) == BPF_K ?
BPF_K : BPF_X, BPF_REG_0,
BPF_REG_A, fp->k);
if (BPF_RVAL(fp->code) == BPF_K)
*insn++ = BPF_MOV32_RAW(BPF_K, BPF_REG_0,
0, fp->k);
*insn = BPF_EXIT_INSN();
break;
@@ -1181,7 +1183,7 @@ static int __reuseport_attach_prog(struct bpf_prog *prog, struct sock *sk)
if (bpf_prog_size(prog->len) > sysctl_optmem_max)
return -ENOMEM;
if (sk_unhashed(sk)) {
if (sk_unhashed(sk) && sk->sk_reuseport) {
err = reuseport_alloc(sk);
if (err)
return err;
@@ -1333,18 +1335,25 @@ int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk)
return 0;
}
#define BPF_LDST_LEN 16U
struct bpf_scratchpad {
union {
__be32 diff[MAX_BPF_STACK / sizeof(__be32)];
u8 buff[MAX_BPF_STACK];
};
};
static DEFINE_PER_CPU(struct bpf_scratchpad, bpf_sp);
static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
{
struct bpf_scratchpad *sp = this_cpu_ptr(&bpf_sp);
struct sk_buff *skb = (struct sk_buff *) (long) r1;
int offset = (int) r2;
void *from = (void *) (long) r3;
unsigned int len = (unsigned int) r4;
char buf[BPF_LDST_LEN];
void *ptr;
if (unlikely(flags & ~(BPF_F_RECOMPUTE_CSUM)))
if (unlikely(flags & ~(BPF_F_RECOMPUTE_CSUM | BPF_F_INVALIDATE_HASH)))
return -EINVAL;
/* bpf verifier guarantees that:
@@ -1355,14 +1364,12 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
*
* so check for invalid 'offset' and too large 'len'
*/
if (unlikely((u32) offset > 0xffff || len > sizeof(buf)))
if (unlikely((u32) offset > 0xffff || len > sizeof(sp->buff)))
return -EFAULT;
if (unlikely(skb_try_make_writable(skb, offset + len)))
return -EFAULT;
if (unlikely(skb_cloned(skb) &&
!skb_clone_writable(skb, offset + len)))
return -EFAULT;
ptr = skb_header_pointer(skb, offset, len, buf);
ptr = skb_header_pointer(skb, offset, len, sp->buff);
if (unlikely(!ptr))
return -EFAULT;
@@ -1371,17 +1378,19 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
memcpy(ptr, from, len);
if (ptr == buf)
if (ptr == sp->buff)
/* skb_store_bits cannot return -EFAULT here */
skb_store_bits(skb, offset, ptr, len);
if (flags & BPF_F_RECOMPUTE_CSUM)
skb_postpush_rcsum(skb, ptr, len);
if (flags & BPF_F_INVALIDATE_HASH)
skb_clear_hash(skb);
return 0;
}
const struct bpf_func_proto bpf_skb_store_bytes_proto = {
static const struct bpf_func_proto bpf_skb_store_bytes_proto = {
.func = bpf_skb_store_bytes,
.gpl_only = false,
.ret_type = RET_INTEGER,
@@ -1400,7 +1409,7 @@ static u64 bpf_skb_load_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
unsigned int len = (unsigned int) r4;
void *ptr;
if (unlikely((u32) offset > 0xffff || len > BPF_LDST_LEN))
if (unlikely((u32) offset > 0xffff || len > MAX_BPF_STACK))
return -EFAULT;
ptr = skb_header_pointer(skb, offset, len, to);
@@ -1412,7 +1421,7 @@ static u64 bpf_skb_load_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
return 0;
}
const struct bpf_func_proto bpf_skb_load_bytes_proto = {
static const struct bpf_func_proto bpf_skb_load_bytes_proto = {
.func = bpf_skb_load_bytes,
.gpl_only = false,
.ret_type = RET_INTEGER,
@@ -1432,9 +1441,7 @@ static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
return -EINVAL;
if (unlikely((u32) offset > 0xffff))
return -EFAULT;
if (unlikely(skb_cloned(skb) &&
!skb_clone_writable(skb, offset + sizeof(sum))))
if (unlikely(skb_try_make_writable(skb, offset + sizeof(sum))))
return -EFAULT;
ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum);
@@ -1442,6 +1449,12 @@ static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
return -EFAULT;
switch (flags & BPF_F_HDR_FIELD_MASK) {
case 0:
if (unlikely(from != 0))
return -EINVAL;
csum_replace_by_diff(ptr, to);
break;
case 2:
csum_replace2(ptr, from, to);
break;
@@ -1459,7 +1472,7 @@ static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
return 0;
}
const struct bpf_func_proto bpf_l3_csum_replace_proto = {
static const struct bpf_func_proto bpf_l3_csum_replace_proto = {
.func = bpf_l3_csum_replace,
.gpl_only = false,
.ret_type = RET_INTEGER,
@@ -1474,23 +1487,31 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
{
struct sk_buff *skb = (struct sk_buff *) (long) r1;
bool is_pseudo = flags & BPF_F_PSEUDO_HDR;
bool is_mmzero = flags & BPF_F_MARK_MANGLED_0;
int offset = (int) r2;
__sum16 sum, *ptr;
if (unlikely(flags & ~(BPF_F_PSEUDO_HDR | BPF_F_HDR_FIELD_MASK)))
if (unlikely(flags & ~(BPF_F_MARK_MANGLED_0 | BPF_F_PSEUDO_HDR |
BPF_F_HDR_FIELD_MASK)))
return -EINVAL;
if (unlikely((u32) offset > 0xffff))
return -EFAULT;
if (unlikely(skb_cloned(skb) &&
!skb_clone_writable(skb, offset + sizeof(sum))))
if (unlikely(skb_try_make_writable(skb, offset + sizeof(sum))))
return -EFAULT;
ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum);
if (unlikely(!ptr))
return -EFAULT;
if (is_mmzero && !*ptr)
return 0;
switch (flags & BPF_F_HDR_FIELD_MASK) {
case 0:
if (unlikely(from != 0))
return -EINVAL;
inet_proto_csum_replace_by_diff(ptr, skb, to, is_pseudo);
break;
case 2:
inet_proto_csum_replace2(ptr, skb, from, to, is_pseudo);
break;
@@ -1501,6 +1522,8 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
return -EINVAL;
}
if (is_mmzero && !*ptr)
*ptr = CSUM_MANGLED_0;
if (ptr == &sum)
/* skb_store_bits guaranteed to not return -EFAULT here */
skb_store_bits(skb, offset, ptr, sizeof(sum));
@@ -1508,7 +1531,7 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
return 0;
}
const struct bpf_func_proto bpf_l4_csum_replace_proto = {
static const struct bpf_func_proto bpf_l4_csum_replace_proto = {
.func = bpf_l4_csum_replace,
.gpl_only = false,
.ret_type = RET_INTEGER,
@@ -1519,6 +1542,45 @@ const struct bpf_func_proto bpf_l4_csum_replace_proto = {
.arg5_type = ARG_ANYTHING,
};
static u64 bpf_csum_diff(u64 r1, u64 from_size, u64 r3, u64 to_size, u64 seed)
{
struct bpf_scratchpad *sp = this_cpu_ptr(&bpf_sp);
u64 diff_size = from_size + to_size;
__be32 *from = (__be32 *) (long) r1;
__be32 *to = (__be32 *) (long) r3;
int i, j = 0;
/* This is quite flexible, some examples:
*
* from_size == 0, to_size > 0, seed := csum --> pushing data
* from_size > 0, to_size == 0, seed := csum --> pulling data
* from_size > 0, to_size > 0, seed := 0 --> diffing data
*
* Even for diffing, from_size and to_size don't need to be equal.
*/
if (unlikely(((from_size | to_size) & (sizeof(__be32) - 1)) ||
diff_size > sizeof(sp->diff)))
return -EINVAL;
for (i = 0; i < from_size / sizeof(__be32); i++, j++)
sp->diff[j] = ~from[i];
for (i = 0; i < to_size / sizeof(__be32); i++, j++)
sp->diff[j] = to[i];
return csum_partial(sp->diff, diff_size, seed);
}
static const struct bpf_func_proto bpf_csum_diff_proto = {
.func = bpf_csum_diff,
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_STACK,
.arg2_type = ARG_CONST_STACK_SIZE_OR_ZERO,
.arg3_type = ARG_PTR_TO_STACK,
.arg4_type = ARG_CONST_STACK_SIZE_OR_ZERO,
.arg5_type = ARG_ANYTHING,
};
static u64 bpf_clone_redirect(u64 r1, u64 ifindex, u64 flags, u64 r4, u64 r5)
{
struct sk_buff *skb = (struct sk_buff *) (long) r1, *skb2;
@@ -1543,11 +1605,10 @@ static u64 bpf_clone_redirect(u64 r1, u64 ifindex, u64 flags, u64 r4, u64 r5)
}
skb2->dev = dev;
skb_sender_cpu_clear(skb2);
return dev_queue_xmit(skb2);
}
const struct bpf_func_proto bpf_clone_redirect_proto = {
static const struct bpf_func_proto bpf_clone_redirect_proto = {
.func = bpf_clone_redirect,
.gpl_only = false,
.ret_type = RET_INTEGER,
@@ -1596,11 +1657,10 @@ int skb_do_redirect(struct sk_buff *skb)
}
skb->dev = dev;
skb_sender_cpu_clear(skb);
return dev_queue_xmit(skb);
}
const struct bpf_func_proto bpf_redirect_proto = {
static const struct bpf_func_proto bpf_redirect_proto = {
.func = bpf_redirect,
.gpl_only = false,
.ret_type = RET_INTEGER,
@@ -1622,14 +1682,7 @@ static const struct bpf_func_proto bpf_get_cgroup_classid_proto = {
static u64 bpf_get_route_realm(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
{
#ifdef CONFIG_IP_ROUTE_CLASSID
const struct dst_entry *dst;
dst = skb_dst((struct sk_buff *) (unsigned long) r1);
if (dst)
return dst->tclassid;
#endif
return 0;
return dst_tclassid((struct sk_buff *) (unsigned long) r1);
}
static const struct bpf_func_proto bpf_get_route_realm_proto = {
@@ -1682,6 +1735,13 @@ bool bpf_helper_changes_skb_data(void *func)
return true;
if (func == bpf_skb_vlan_pop)
return true;
if (func == bpf_skb_store_bytes)
return true;
if (func == bpf_l3_csum_replace)
return true;
if (func == bpf_l4_csum_replace)
return true;
return false;
}
@@ -1703,12 +1763,15 @@ static u64 bpf_skb_get_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
return -EPROTO;
if (unlikely(size != sizeof(struct bpf_tunnel_key))) {
switch (size) {
case offsetof(struct bpf_tunnel_key, tunnel_label):
goto set_compat;
case offsetof(struct bpf_tunnel_key, remote_ipv6[1]):
/* Fixup deprecated structure layouts here, so we have
* a common path later on.
*/
if (ip_tunnel_info_af(info) != AF_INET)
return -EINVAL;
set_compat:
to = (struct bpf_tunnel_key *)compat;
break;
default:
@@ -1720,11 +1783,13 @@ static u64 bpf_skb_get_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
to->tunnel_tos = info->key.tos;
to->tunnel_ttl = info->key.ttl;
if (flags & BPF_F_TUNINFO_IPV6)
if (flags & BPF_F_TUNINFO_IPV6) {
memcpy(to->remote_ipv6, &info->key.u.ipv6.src,
sizeof(to->remote_ipv6));
else
to->tunnel_label = be32_to_cpu(info->key.label);
} else {
to->remote_ipv4 = be32_to_cpu(info->key.u.ipv4.src);
}
if (unlikely(size != sizeof(struct bpf_tunnel_key)))
memcpy((void *)(long) r2, to, size);
@@ -1732,7 +1797,7 @@ static u64 bpf_skb_get_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
return 0;
}
const struct bpf_func_proto bpf_skb_get_tunnel_key_proto = {
static const struct bpf_func_proto bpf_skb_get_tunnel_key_proto = {
.func = bpf_skb_get_tunnel_key,
.gpl_only = false,
.ret_type = RET_INTEGER,
@@ -1742,6 +1807,32 @@ const struct bpf_func_proto bpf_skb_get_tunnel_key_proto = {
.arg4_type = ARG_ANYTHING,
};
static u64 bpf_skb_get_tunnel_opt(u64 r1, u64 r2, u64 size, u64 r4, u64 r5)
{
struct sk_buff *skb = (struct sk_buff *) (long) r1;
u8 *to = (u8 *) (long) r2;
const struct ip_tunnel_info *info = skb_tunnel_info(skb);
if (unlikely(!info ||
!(info->key.tun_flags & TUNNEL_OPTIONS_PRESENT)))
return -ENOENT;
if (unlikely(size < info->options_len))
return -ENOMEM;
ip_tunnel_info_opts_get(to, info);
return info->options_len;
}
static const struct bpf_func_proto bpf_skb_get_tunnel_opt_proto = {
.func = bpf_skb_get_tunnel_opt,
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_STACK,
.arg3_type = ARG_CONST_STACK_SIZE,
};
static struct metadata_dst __percpu *md_dst;
static u64 bpf_skb_set_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
@@ -1752,10 +1843,12 @@ static u64 bpf_skb_set_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
u8 compat[sizeof(struct bpf_tunnel_key)];
struct ip_tunnel_info *info;
if (unlikely(flags & ~(BPF_F_TUNINFO_IPV6 | BPF_F_ZERO_CSUM_TX)))
if (unlikely(flags & ~(BPF_F_TUNINFO_IPV6 | BPF_F_ZERO_CSUM_TX |
BPF_F_DONT_FRAGMENT)))
return -EINVAL;
if (unlikely(size != sizeof(struct bpf_tunnel_key))) {
switch (size) {
case offsetof(struct bpf_tunnel_key, tunnel_label):
case offsetof(struct bpf_tunnel_key, remote_ipv6[1]):
/* Fixup deprecated structure layouts here, so we have
* a common path later on.
@@ -1768,6 +1861,8 @@ static u64 bpf_skb_set_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
return -EINVAL;
}
}
if (unlikely(!(flags & BPF_F_TUNINFO_IPV6) && from->tunnel_label))
return -EINVAL;
skb_dst_drop(skb);
dst_hold((struct dst_entry *) md);
@@ -1776,7 +1871,10 @@ static u64 bpf_skb_set_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
info = &md->u.tun_info;
info->mode = IP_TUNNEL_INFO_TX;
info->key.tun_flags = TUNNEL_KEY | TUNNEL_CSUM;
info->key.tun_flags = TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_NOCACHE;
if (flags & BPF_F_DONT_FRAGMENT)
info->key.tun_flags |= TUNNEL_DONT_FRAGMENT;
info->key.tun_id = cpu_to_be64(from->tunnel_id);
info->key.tos = from->tunnel_tos;
info->key.ttl = from->tunnel_ttl;
@@ -1785,6 +1883,8 @@ static u64 bpf_skb_set_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
info->mode |= IP_TUNNEL_INFO_IPV6;
memcpy(&info->key.u.ipv6.dst, from->remote_ipv6,
sizeof(from->remote_ipv6));
info->key.label = cpu_to_be32(from->tunnel_label) &
IPV6_FLOWLABEL_MASK;
} else {
info->key.u.ipv4.dst = cpu_to_be32(from->remote_ipv4);
if (flags & BPF_F_ZERO_CSUM_TX)
@@ -1794,7 +1894,7 @@ static u64 bpf_skb_set_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
return 0;
}
const struct bpf_func_proto bpf_skb_set_tunnel_key_proto = {
static const struct bpf_func_proto bpf_skb_set_tunnel_key_proto = {
.func = bpf_skb_set_tunnel_key,
.gpl_only = false,
.ret_type = RET_INTEGER,
@@ -1804,17 +1904,53 @@ const struct bpf_func_proto bpf_skb_set_tunnel_key_proto = {
.arg4_type = ARG_ANYTHING,
};
static const struct bpf_func_proto *bpf_get_skb_set_tunnel_key_proto(void)
static u64 bpf_skb_set_tunnel_opt(u64 r1, u64 r2, u64 size, u64 r4, u64 r5)
{
struct sk_buff *skb = (struct sk_buff *) (long) r1;
u8 *from = (u8 *) (long) r2;
struct ip_tunnel_info *info = skb_tunnel_info(skb);
const struct metadata_dst *md = this_cpu_ptr(md_dst);
if (unlikely(info != &md->u.tun_info || (size & (sizeof(u32) - 1))))
return -EINVAL;
if (unlikely(size > IP_TUNNEL_OPTS_MAX))
return -ENOMEM;
ip_tunnel_info_opts_set(info, from, size);
return 0;
}
static const struct bpf_func_proto bpf_skb_set_tunnel_opt_proto = {
.func = bpf_skb_set_tunnel_opt,
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_STACK,
.arg3_type = ARG_CONST_STACK_SIZE,
};
static const struct bpf_func_proto *
bpf_get_skb_set_tunnel_proto(enum bpf_func_id which)
{
if (!md_dst) {
/* race is not possible, since it's called from
* verifier that is holding verifier mutex
/* Race is not possible, since it's called from verifier
* that is holding verifier mutex.
*/
md_dst = metadata_dst_alloc_percpu(0, GFP_KERNEL);
md_dst = metadata_dst_alloc_percpu(IP_TUNNEL_OPTS_MAX,
GFP_KERNEL);
if (!md_dst)
return NULL;
}
return &bpf_skb_set_tunnel_key_proto;
switch (which) {
case BPF_FUNC_skb_set_tunnel_key:
return &bpf_skb_set_tunnel_key_proto;
case BPF_FUNC_skb_set_tunnel_opt:
return &bpf_skb_set_tunnel_opt_proto;
default:
return NULL;
}
}
static const struct bpf_func_proto *
@@ -1851,6 +1987,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
return &bpf_skb_store_bytes_proto;
case BPF_FUNC_skb_load_bytes:
return &bpf_skb_load_bytes_proto;
case BPF_FUNC_csum_diff:
return &bpf_csum_diff_proto;
case BPF_FUNC_l3_csum_replace:
return &bpf_l3_csum_replace_proto;
case BPF_FUNC_l4_csum_replace:
@@ -1866,7 +2004,11 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
case BPF_FUNC_skb_get_tunnel_key:
return &bpf_skb_get_tunnel_key_proto;
case BPF_FUNC_skb_set_tunnel_key:
return bpf_get_skb_set_tunnel_key_proto();
return bpf_get_skb_set_tunnel_proto(func_id);
case BPF_FUNC_skb_get_tunnel_opt:
return &bpf_skb_get_tunnel_opt_proto;
case BPF_FUNC_skb_set_tunnel_opt:
return bpf_get_skb_set_tunnel_proto(func_id);
case BPF_FUNC_redirect:
return &bpf_redirect_proto;
case BPF_FUNC_get_route_realm:
@@ -1915,16 +2057,14 @@ static bool sk_filter_is_valid_access(int off, int size,
static bool tc_cls_act_is_valid_access(int off, int size,
enum bpf_access_type type)
{
if (off == offsetof(struct __sk_buff, tc_classid))
return type == BPF_WRITE ? true : false;
if (type == BPF_WRITE) {
switch (off) {
case offsetof(struct __sk_buff, mark):
case offsetof(struct __sk_buff, tc_index):
case offsetof(struct __sk_buff, priority):
case offsetof(struct __sk_buff, cb[0]) ...
offsetof(struct __sk_buff, cb[4]):
offsetof(struct __sk_buff, cb[4]):
case offsetof(struct __sk_buff, tc_classid):
break;
default:
return false;
@@ -2041,8 +2181,10 @@ static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
ctx_off -= offsetof(struct __sk_buff, tc_classid);
ctx_off += offsetof(struct sk_buff, cb);
ctx_off += offsetof(struct qdisc_skb_cb, tc_classid);
WARN_ON(type != BPF_WRITE);
*insn++ = BPF_STX_MEM(BPF_H, dst_reg, src_reg, ctx_off);
if (type == BPF_WRITE)
*insn++ = BPF_STX_MEM(BPF_H, dst_reg, src_reg, ctx_off);
else
*insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg, ctx_off);
break;
case offsetof(struct __sk_buff, tc_index):

View File

@@ -19,25 +19,12 @@
#include <net/flow_dissector.h>
#include <scsi/fc/fc_fcoe.h>
static bool dissector_uses_key(const struct flow_dissector *flow_dissector,
enum flow_dissector_key_id key_id)
{
return flow_dissector->used_keys & (1 << key_id);
}
static void dissector_set_key(struct flow_dissector *flow_dissector,
enum flow_dissector_key_id key_id)
{
flow_dissector->used_keys |= (1 << key_id);
}
static void *skb_flow_dissector_target(struct flow_dissector *flow_dissector,
enum flow_dissector_key_id key_id,
void *target_container)
{
return ((char *) target_container) + flow_dissector->offset[key_id];
}
void skb_flow_dissector_init(struct flow_dissector *flow_dissector,
const struct flow_dissector_key *key,
unsigned int key_count)
@@ -178,15 +165,16 @@ ip:
ip_proto = iph->protocol;
if (!dissector_uses_key(flow_dissector,
FLOW_DISSECTOR_KEY_IPV4_ADDRS))
break;
if (dissector_uses_key(flow_dissector,
FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
key_addrs = skb_flow_dissector_target(flow_dissector,
FLOW_DISSECTOR_KEY_IPV4_ADDRS,
target_container);
key_addrs = skb_flow_dissector_target(flow_dissector,
FLOW_DISSECTOR_KEY_IPV4_ADDRS, target_container);
memcpy(&key_addrs->v4addrs, &iph->saddr,
sizeof(key_addrs->v4addrs));
key_control->addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
memcpy(&key_addrs->v4addrs, &iph->saddr,
sizeof(key_addrs->v4addrs));
key_control->addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
}
if (ip_is_fragment(iph)) {
key_control->flags |= FLOW_DIS_IS_FRAGMENT;
@@ -219,13 +207,12 @@ ipv6:
if (dissector_uses_key(flow_dissector,
FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
struct flow_dissector_key_ipv6_addrs *key_ipv6_addrs;
key_addrs = skb_flow_dissector_target(flow_dissector,
FLOW_DISSECTOR_KEY_IPV6_ADDRS,
target_container);
key_ipv6_addrs = skb_flow_dissector_target(flow_dissector,
FLOW_DISSECTOR_KEY_IPV6_ADDRS,
target_container);
memcpy(key_ipv6_addrs, &iph->saddr, sizeof(*key_ipv6_addrs));
memcpy(&key_addrs->v6addrs, &iph->saddr,
sizeof(key_addrs->v6addrs));
key_control->addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
}
@@ -339,8 +326,11 @@ mpls:
}
case htons(ETH_P_FCOE):
key_control->thoff = (u16)(nhoff + FCOE_HEADER_LEN);
/* fall through */
if ((hlen - nhoff) < FCOE_HEADER_LEN)
goto out_bad;
nhoff += FCOE_HEADER_LEN;
goto out_good;
default:
goto out_bad;
}
@@ -447,13 +437,12 @@ ip_proto_again:
key_control->flags |= FLOW_DIS_IS_FRAGMENT;
nhoff += sizeof(_fh);
ip_proto = fh->nexthdr;
if (!(fh->frag_off & htons(IP6_OFFSET))) {
key_control->flags |= FLOW_DIS_FIRST_FRAG;
if (flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG) {
ip_proto = fh->nexthdr;
if (flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG)
goto ip_proto_again;
}
}
goto out_good;
}
@@ -740,6 +729,11 @@ u32 __skb_get_poff(const struct sk_buff *skb, void *data,
{
u32 poff = keys->control.thoff;
/* skip L4 headers for fragments after the first */
if ((keys->control.flags & FLOW_DIS_IS_FRAGMENT) &&
!(keys->control.flags & FLOW_DIS_FIRST_FRAG))
return poff;
switch (keys->basic.ip_proto) {
case IPPROTO_TCP: {
/* access doff as u8 to avoid unaligned access */

87
net/core/hwbm.c Normal file
View File

@@ -0,0 +1,87 @@
/* Support for hardware buffer manager.
*
* Copyright (C) 2016 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/skbuff.h>
#include <net/hwbm.h>
void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf)
{
if (likely(bm_pool->frag_size <= PAGE_SIZE))
skb_free_frag(buf);
else
kfree(buf);
}
EXPORT_SYMBOL_GPL(hwbm_buf_free);
/* Refill processing for HW buffer management */
int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp)
{
int frag_size = bm_pool->frag_size;
void *buf;
if (likely(frag_size <= PAGE_SIZE))
buf = netdev_alloc_frag(frag_size);
else
buf = kmalloc(frag_size, gfp);
if (!buf)
return -ENOMEM;
if (bm_pool->construct)
if (bm_pool->construct(bm_pool, buf)) {
hwbm_buf_free(bm_pool, buf);
return -ENOMEM;
}
return 0;
}
EXPORT_SYMBOL_GPL(hwbm_pool_refill);
int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp)
{
int err, i;
unsigned long flags;
spin_lock_irqsave(&bm_pool->lock, flags);
if (bm_pool->buf_num == bm_pool->size) {
pr_warn("pool already filled\n");
return bm_pool->buf_num;
}
if (buf_num + bm_pool->buf_num > bm_pool->size) {
pr_warn("cannot allocate %d buffers for pool\n",
buf_num);
return 0;
}
if ((buf_num + bm_pool->buf_num) < bm_pool->buf_num) {
pr_warn("Adding %d buffers to the %d current buffers will overflow\n",
buf_num, bm_pool->buf_num);
return 0;
}
for (i = 0; i < buf_num; i++) {
err = hwbm_pool_refill(bm_pool, gfp);
if (err < 0)
break;
}
/* Update BM driver with number of buffers added to pool */
bm_pool->buf_num += i;
pr_debug("hwpm pool: %d of %d buffers added\n", i, buf_num);
spin_unlock_irqrestore(&bm_pool->lock, flags);
return i;
}
EXPORT_SYMBOL_GPL(hwbm_pool_add);

View File

@@ -27,6 +27,31 @@
#include <net/rtnetlink.h>
#include <net/ip6_fib.h>
#ifdef CONFIG_MODULES
static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
{
/* Only lwt encaps implemented without using an interface for
* the encap need to return a string here.
*/
switch (encap_type) {
case LWTUNNEL_ENCAP_MPLS:
return "MPLS";
case LWTUNNEL_ENCAP_ILA:
return "ILA";
case LWTUNNEL_ENCAP_IP6:
case LWTUNNEL_ENCAP_IP:
case LWTUNNEL_ENCAP_NONE:
case __LWTUNNEL_ENCAP_MAX:
/* should not have got here */
WARN_ON(1);
break;
}
return NULL;
}
#endif /* CONFIG_MODULES */
struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
{
struct lwtunnel_state *lws;
@@ -85,6 +110,18 @@ int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
ret = -EOPNOTSUPP;
rcu_read_lock();
ops = rcu_dereference(lwtun_encaps[encap_type]);
#ifdef CONFIG_MODULES
if (!ops) {
const char *encap_type_str = lwtunnel_encap_str(encap_type);
if (encap_type_str) {
rcu_read_unlock();
request_module("rtnl-lwt-%s", encap_type_str);
rcu_read_lock();
ops = rcu_dereference(lwtun_encaps[encap_type]);
}
}
#endif
if (likely(ops && ops->build_state))
ret = ops->build_state(dev, encap, family, cfg, lws);
rcu_read_unlock();

View File

@@ -29,7 +29,6 @@
#ifdef CONFIG_SYSFS
static const char fmt_hex[] = "%#x\n";
static const char fmt_long_hex[] = "%#lx\n";
static const char fmt_dec[] = "%d\n";
static const char fmt_ulong[] = "%lu\n";
static const char fmt_u64[] = "%llu\n";
@@ -199,9 +198,10 @@ static ssize_t speed_show(struct device *dev,
return restart_syscall();
if (netif_running(netdev)) {
struct ethtool_cmd cmd;
if (!__ethtool_get_settings(netdev, &cmd))
ret = sprintf(buf, fmt_dec, ethtool_cmd_speed(&cmd));
struct ethtool_link_ksettings cmd;
if (!__ethtool_get_link_ksettings(netdev, &cmd))
ret = sprintf(buf, fmt_dec, cmd.base.speed);
}
rtnl_unlock();
return ret;
@@ -218,10 +218,12 @@ static ssize_t duplex_show(struct device *dev,
return restart_syscall();
if (netif_running(netdev)) {
struct ethtool_cmd cmd;
if (!__ethtool_get_settings(netdev, &cmd)) {
struct ethtool_link_ksettings cmd;
if (!__ethtool_get_link_ksettings(netdev, &cmd)) {
const char *duplex;
switch (cmd.duplex) {
switch (cmd.base.duplex) {
case DUPLEX_HALF:
duplex = "half";
break;
@@ -574,6 +576,7 @@ NETSTAT_ENTRY(tx_heartbeat_errors);
NETSTAT_ENTRY(tx_window_errors);
NETSTAT_ENTRY(rx_compressed);
NETSTAT_ENTRY(tx_compressed);
NETSTAT_ENTRY(rx_nohandler);
static struct attribute *netstat_attrs[] = {
&dev_attr_rx_packets.attr,
@@ -599,6 +602,7 @@ static struct attribute *netstat_attrs[] = {
&dev_attr_tx_window_errors.attr,
&dev_attr_rx_compressed.attr,
&dev_attr_tx_compressed.attr,
&dev_attr_rx_nohandler.attr,
NULL
};

View File

@@ -9,7 +9,6 @@
* Authors: Thomas Graf <tgraf@suug.ch>
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/cgroup.h>
#include <linux/fdtable.h>

View File

@@ -11,7 +11,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/string.h>

View File

@@ -2856,7 +2856,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
*vlan_encapsulated_proto = htons(ETH_P_IP);
}
skb_set_mac_header(skb, 0);
skb_reset_mac_header(skb);
skb_set_network_header(skb, skb->len);
iph = (struct iphdr *) skb_put(skb, sizeof(struct iphdr));
@@ -2983,7 +2983,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
*vlan_encapsulated_proto = htons(ETH_P_IPV6);
}
skb_set_mac_header(skb, 0);
skb_reset_mac_header(skb);
skb_set_network_header(skb, skb->len);
iph = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));

View File

@@ -804,6 +804,8 @@ static void copy_rtnl_link_stats(struct rtnl_link_stats *a,
a->rx_compressed = b->rx_compressed;
a->tx_compressed = b->tx_compressed;
a->rx_nohandler = b->rx_nohandler;
}
static void copy_rtnl_link_stats64(void *v, const struct rtnl_link_stats64 *b)
@@ -1389,15 +1391,6 @@ static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
[IFLA_VF_TRUST] = { .len = sizeof(struct ifla_vf_trust) },
};
static const struct nla_policy ifla_vf_stats_policy[IFLA_VF_STATS_MAX + 1] = {
[IFLA_VF_STATS_RX_PACKETS] = { .type = NLA_U64 },
[IFLA_VF_STATS_TX_PACKETS] = { .type = NLA_U64 },
[IFLA_VF_STATS_RX_BYTES] = { .type = NLA_U64 },
[IFLA_VF_STATS_TX_BYTES] = { .type = NLA_U64 },
[IFLA_VF_STATS_BROADCAST] = { .type = NLA_U64 },
[IFLA_VF_STATS_MULTICAST] = { .type = NLA_U64 },
};
static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
[IFLA_PORT_VF] = { .type = NLA_U32 },
[IFLA_PORT_PROFILE] = { .type = NLA_STRING,
@@ -1412,6 +1405,58 @@ static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
[IFLA_PORT_RESPONSE] = { .type = NLA_U16, },
};
static const struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla)
{
const struct rtnl_link_ops *ops = NULL;
struct nlattr *linfo[IFLA_INFO_MAX + 1];
if (nla_parse_nested(linfo, IFLA_INFO_MAX, nla, ifla_info_policy) < 0)
return NULL;
if (linfo[IFLA_INFO_KIND]) {
char kind[MODULE_NAME_LEN];
nla_strlcpy(kind, linfo[IFLA_INFO_KIND], sizeof(kind));
ops = rtnl_link_ops_get(kind);
}
return ops;
}
static bool link_master_filtered(struct net_device *dev, int master_idx)
{
struct net_device *master;
if (!master_idx)
return false;
master = netdev_master_upper_dev_get(dev);
if (!master || master->ifindex != master_idx)
return true;
return false;
}
static bool link_kind_filtered(const struct net_device *dev,
const struct rtnl_link_ops *kind_ops)
{
if (kind_ops && dev->rtnl_link_ops != kind_ops)
return true;
return false;
}
static bool link_dump_filtered(struct net_device *dev,
int master_idx,
const struct rtnl_link_ops *kind_ops)
{
if (link_master_filtered(dev, master_idx) ||
link_kind_filtered(dev, kind_ops))
return true;
return false;
}
static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
{
struct net *net = sock_net(skb->sk);
@@ -1421,6 +1466,9 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
struct hlist_head *head;
struct nlattr *tb[IFLA_MAX+1];
u32 ext_filter_mask = 0;
const struct rtnl_link_ops *kind_ops = NULL;
unsigned int flags = NLM_F_MULTI;
int master_idx = 0;
int err;
int hdrlen;
@@ -1443,18 +1491,29 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
if (tb[IFLA_EXT_MASK])
ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
if (tb[IFLA_MASTER])
master_idx = nla_get_u32(tb[IFLA_MASTER]);
if (tb[IFLA_LINKINFO])
kind_ops = linkinfo_to_kind_ops(tb[IFLA_LINKINFO]);
if (master_idx || kind_ops)
flags |= NLM_F_DUMP_FILTERED;
}
for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
idx = 0;
head = &net->dev_index_head[h];
hlist_for_each_entry(dev, head, index_hlist) {
if (link_dump_filtered(dev, master_idx, kind_ops))
continue;
if (idx < s_idx)
goto cont;
err = rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, 0,
NLM_F_MULTI,
flags,
ext_filter_mask);
/* If we ran out of room on the first message,
* we're in trouble

Some files were not shown because too many files have changed in this diff Show More